|
|
|
|
@@ -81,6 +81,7 @@ export class TsviewS3Columns extends DeesElement {
|
|
|
|
|
private dragCounters: Map<number, number> = new Map();
|
|
|
|
|
private folderHoverTimer: ReturnType<typeof setTimeout> | null = null;
|
|
|
|
|
private fileInputElement: HTMLInputElement | null = null;
|
|
|
|
|
private folderInputElement: HTMLInputElement | null = null;
|
|
|
|
|
|
|
|
|
|
public static styles = [
|
|
|
|
|
cssManager.defaultStyles,
|
|
|
|
|
@@ -380,10 +381,11 @@ export class TsviewS3Columns extends DeesElement {
|
|
|
|
|
this.clearFolderHover();
|
|
|
|
|
this.dragCounters.clear();
|
|
|
|
|
if (this.fileInputElement) { this.fileInputElement.remove(); this.fileInputElement = null; }
|
|
|
|
|
if (this.folderInputElement) { this.folderInputElement.remove(); this.folderInputElement = null; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
updated(changedProperties: Map<string, unknown>) {
|
|
|
|
|
if (changedProperties.has('bucketName')) {
|
|
|
|
|
if (changedProperties.has('bucketName') || changedProperties.has('currentPrefix')) {
|
|
|
|
|
this.loadInitialColumn();
|
|
|
|
|
} else if (changedProperties.has('refreshKey')) {
|
|
|
|
|
this.refreshAllColumns();
|
|
|
|
|
@@ -393,16 +395,37 @@ export class TsviewS3Columns extends DeesElement {
|
|
|
|
|
private async loadInitialColumn() {
|
|
|
|
|
this.loading = true;
|
|
|
|
|
try {
|
|
|
|
|
const result = await apiService.listObjects(this.bucketName, this.currentPrefix, '/');
|
|
|
|
|
this.columns = [
|
|
|
|
|
{
|
|
|
|
|
prefix: this.currentPrefix,
|
|
|
|
|
// Parse the path segments from currentPrefix
|
|
|
|
|
// e.g., "folder1/folder2/folder3/" → ["folder1/", "folder1/folder2/", "folder1/folder2/folder3/"]
|
|
|
|
|
const pathSegments = this.getPathSegments(this.currentPrefix);
|
|
|
|
|
|
|
|
|
|
// Build all prefixes we need to load (including root)
|
|
|
|
|
const prefixesToLoad = ['', ...pathSegments];
|
|
|
|
|
|
|
|
|
|
// Load all columns in parallel
|
|
|
|
|
const columnResults = await Promise.all(
|
|
|
|
|
prefixesToLoad.map(prefix =>
|
|
|
|
|
apiService.listObjects(this.bucketName, prefix, '/')
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Build columns array with proper selections
|
|
|
|
|
this.columns = columnResults.map((result, index) => {
|
|
|
|
|
const prefix = prefixesToLoad[index];
|
|
|
|
|
// The selected item is the next prefix in the path (if any)
|
|
|
|
|
const selectedItem = index < pathSegments.length ? pathSegments[index] : null;
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
prefix,
|
|
|
|
|
objects: result.objects,
|
|
|
|
|
prefixes: result.prefixes,
|
|
|
|
|
selectedItem: null,
|
|
|
|
|
selectedItem,
|
|
|
|
|
width: this.DEFAULT_COLUMN_WIDTH,
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Auto-scroll to show the rightmost column
|
|
|
|
|
this.updateComplete.then(() => this.scrollToEnd());
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error('Error loading objects:', err);
|
|
|
|
|
this.columns = [];
|
|
|
|
|
@@ -410,6 +433,22 @@ export class TsviewS3Columns extends DeesElement {
|
|
|
|
|
this.loading = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Helper to parse prefix into cumulative path segments
|
|
|
|
|
private getPathSegments(prefix: string): string[] {
|
|
|
|
|
if (!prefix) return [];
|
|
|
|
|
|
|
|
|
|
const parts = prefix.split('/').filter(p => p); // Remove empty strings
|
|
|
|
|
const segments: string[] = [];
|
|
|
|
|
let cumulative = '';
|
|
|
|
|
|
|
|
|
|
for (const part of parts) {
|
|
|
|
|
cumulative += part + '/';
|
|
|
|
|
segments.push(cumulative);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return segments;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async refreshAllColumns() {
|
|
|
|
|
const updatedColumns = await Promise.all(
|
|
|
|
|
this.columns.map(async (col) => {
|
|
|
|
|
@@ -566,9 +605,14 @@ export class TsviewS3Columns extends DeesElement {
|
|
|
|
|
action: async () => this.openCreateDialog('file', prefix),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'Upload...',
|
|
|
|
|
iconName: 'lucide:upload',
|
|
|
|
|
action: async () => this.triggerFileUpload(prefix),
|
|
|
|
|
name: 'Upload Files...',
|
|
|
|
|
iconName: 'lucide:file',
|
|
|
|
|
action: async () => this.triggerFileUpload(prefix, 'files'),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'Upload Folder...',
|
|
|
|
|
iconName: 'lucide:folderUp',
|
|
|
|
|
action: async () => this.triggerFileUpload(prefix, 'folder'),
|
|
|
|
|
},
|
|
|
|
|
{ divider: true },
|
|
|
|
|
{
|
|
|
|
|
@@ -654,9 +698,14 @@ export class TsviewS3Columns extends DeesElement {
|
|
|
|
|
action: async () => this.openCreateDialog('file', prefix),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'Upload...',
|
|
|
|
|
iconName: 'lucide:upload',
|
|
|
|
|
action: async () => this.triggerFileUpload(prefix),
|
|
|
|
|
name: 'Upload Files...',
|
|
|
|
|
iconName: 'lucide:file',
|
|
|
|
|
action: async () => this.triggerFileUpload(prefix, 'files'),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'Upload Folder...',
|
|
|
|
|
iconName: 'lucide:folderUp',
|
|
|
|
|
action: async () => this.triggerFileUpload(prefix, 'folder'),
|
|
|
|
|
},
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
@@ -697,20 +746,27 @@ export class TsviewS3Columns extends DeesElement {
|
|
|
|
|
|
|
|
|
|
// --- File upload helpers ---
|
|
|
|
|
|
|
|
|
|
private ensureFileInput(): HTMLInputElement {
|
|
|
|
|
private ensureFileInputs(): void {
|
|
|
|
|
if (!this.fileInputElement) {
|
|
|
|
|
this.fileInputElement = document.createElement('input');
|
|
|
|
|
this.fileInputElement.type = 'file';
|
|
|
|
|
this.fileInputElement.multiple = true;
|
|
|
|
|
(this.fileInputElement as any).webkitdirectory = true; // Enable folder selection
|
|
|
|
|
this.fileInputElement.style.display = 'none';
|
|
|
|
|
this.shadowRoot!.appendChild(this.fileInputElement);
|
|
|
|
|
}
|
|
|
|
|
return this.fileInputElement;
|
|
|
|
|
if (!this.folderInputElement) {
|
|
|
|
|
this.folderInputElement = document.createElement('input');
|
|
|
|
|
this.folderInputElement.type = 'file';
|
|
|
|
|
this.folderInputElement.multiple = true;
|
|
|
|
|
(this.folderInputElement as any).webkitdirectory = true;
|
|
|
|
|
this.folderInputElement.style.display = 'none';
|
|
|
|
|
this.shadowRoot!.appendChild(this.folderInputElement);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private triggerFileUpload(targetPrefix: string) {
|
|
|
|
|
const input = this.ensureFileInput();
|
|
|
|
|
private triggerFileUpload(targetPrefix: string, type: 'files' | 'folder') {
|
|
|
|
|
this.ensureFileInputs();
|
|
|
|
|
const input = type === 'folder' ? this.folderInputElement! : this.fileInputElement!;
|
|
|
|
|
input.value = '';
|
|
|
|
|
const handler = async () => {
|
|
|
|
|
input.removeEventListener('change', handler);
|
|
|
|
|
|