feat(s3-columns): load full prefix path on initial load and add folder upload support
This commit is contained in:
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@git.zone/tsview',
|
||||
version: '1.8.1',
|
||||
version: '1.9.0',
|
||||
description: 'A CLI tool for viewing S3 and MongoDB data with a web UI'
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user