this.closeRenameDialog()}>
e.stopPropagation()}>
${title}
Location: ${this.bucketName}/${getParentPrefix(this.renameSource.key)}
${this.renameError ? html`
${this.renameError}
` : ''}
{
this.renameName = (e.target as HTMLInputElement).value;
this.renameError = null;
}}
@keydown=${(e: KeyboardEvent) => {
if (e.key === 'Enter') this.executeRename();
if (e.key === 'Escape') this.closeRenameDialog();
}}
/>
`;
}
private renderMoveDialog() {
if (!this.showMoveDialog || !this.moveSource) return '';
const sourceName = getFileName(this.moveSource.key);
return html`
this.closeMovePickerDialog()}>
e.stopPropagation()}>
Move "${sourceName}" to...
this.navigateMovePicker('')}>
${this.bucketName}
${getPathSegments(this.movePickerCurrentPrefix).map(seg => html`
/
this.navigateMovePicker(seg)}>
${getFileName(seg)}
`)}
${this.movePickerLoading ? html`
Loading...
` : ''}
${!this.movePickerLoading && this.movePickerPrefixes.filter(p => p !== this.movePickerSource!.key).length === 0 ? html`
No subfolders
` : ''}
${this.movePickerPrefixes
.filter(p => p !== this.movePickerSource!.key)
.map(prefix => html`
this.navigateMovePicker(prefix)}
@dblclick=${() => this.selectMoveDestination(prefix)}>
${getFileName(prefix)}
`)}
`;
}
private async handleCreate() {
if (!this.createDialogName.trim() || !this.dataProvider) return;
const name = this.createDialogName.trim();
let path: string;
if (this.createDialogType === 'folder') {
path = this.createDialogPrefix + name + '/.keep';
} else {
path = this.createDialogPrefix + name;
}
const ext = name.split('.').pop()?.toLowerCase() || '';
const contentType = this.createDialogType === 'file' ? getContentType(ext) : 'application/octet-stream';
const content = this.createDialogType === 'file' ? getDefaultContent(ext) : '';
const success = await this.dataProvider.putObject(
this.bucketName,
path,
btoa(content),
contentType
);
if (success) {
this.showCreateDialog = false;
await this.refreshColumnByPrefix(this.createDialogPrefix);
}
}
private renderCreateDialog() {
if (!this.showCreateDialog) return '';
const isFolder = this.createDialogType === 'folder';
const title = isFolder ? 'Create New Folder' : 'Create New File';
const placeholder = isFolder ? 'folder-name or path/to/folder' : 'filename.txt or path/to/file.txt';
return html`
this.showCreateDialog = false}>
e.stopPropagation()}>
${title}
Location: ${this.bucketName}/${this.createDialogPrefix}
this.createDialogName = (e.target as HTMLInputElement).value}
@keydown=${(e: KeyboardEvent) => e.key === 'Enter' && this.handleCreate()}
/>
Use "/" to create nested ${isFolder ? 'folders' : 'path'} (e.g., ${isFolder ? 'parent/child' : 'folder/file.txt'})
`;
}
render() {
if (this.loading && this.columns.length === 0) {
return html`Loading...
`;
}
return html`
${this.columns.map((column, index) => this.renderColumnWrapper(column, index))}
${this.renderCreateDialog()}
${this.renderMoveDialog()}
${this.renderMovePickerDialog()}
${this.renderRenameDialog()}
`;
}
private renderColumnWrapper(column: IColumn, index: number) {
return html`
${this.renderColumn(column, index)}
this.startResize(e, index)}
>
`;
}
private renderColumn(column: IColumn, index: number) {
const headerName = column.prefix
? getFileName(column.prefix)
: this.bucketName;
return html`
this.handleColumnDragEnter(e, index)}
@dragover=${(e: DragEvent) => this.handleColumnDragOver(e)}
@dragleave=${(e: DragEvent) => this.handleColumnDragLeave(e, index)}
@drop=${(e: DragEvent) => this.handleColumnDrop(e, index)}
>
${headerName}
this.handleEmptySpaceContextMenu(e, index)}>
${column.prefixes.length === 0 && column.objects.length === 0
? html`
Empty folder
`
: ''}
${column.prefixes.map(
(prefix) => html`
this.selectFolder(index, prefix)}
@contextmenu=${(e: MouseEvent) => this.handleFolderContextMenu(e, index, prefix)}
@dragstart=${(e: DragEvent) => this.handleItemDragStart(e, prefix, true)}
@dragend=${(e: DragEvent) => this.handleItemDragEnd(e)}
@dragenter=${(e: DragEvent) => this.handleFolderDragEnter(e, prefix)}
@dragover=${(e: DragEvent) => this.handleFolderDragOver(e)}
@dragleave=${(e: DragEvent) => this.handleFolderDragLeave(e, prefix)}
>
${getFileName(prefix)}
`
)}
${column.objects.map(
(obj) => html`
this.selectFile(index, obj.key)}
@contextmenu=${(e: MouseEvent) => this.handleFileContextMenu(e, index, obj.key)}
@dragstart=${(e: DragEvent) => this.handleItemDragStart(e, obj.key, false)}
@dragend=${(e: DragEvent) => this.handleItemDragEnd(e)}
>
${getFileName(obj.key)}
`
)}
${this.dragOverColumnIndex === index ? html`
${this.draggedItem
? (this.dragOverFolderPrefix
? `Move to ${getFileName(this.dragOverFolderPrefix)}`
: 'Move here')
: (this.dragOverFolderPrefix
? `Drop to upload into ${getFileName(this.dragOverFolderPrefix)}`
: 'Drop to upload here')}
` : ''}
${this.uploading ? html`
Uploading...
${this.uploadProgress ? html`
${this.uploadProgress.current} / ${this.uploadProgress.total} files
` : ''}
` : ''}
`;
}
}