Add file/folder selection to browsers, fix numeric sorting

This commit is contained in:
2024-07-14 14:08:32 +09:30
parent f79df6ff27
commit e4e980bc70
3 changed files with 82 additions and 12 deletions

View File

@@ -2,11 +2,13 @@ import { FlatTreeControl } from '@angular/cdk/tree';
import { CommonModule } from '@angular/common';
import {
Component,
ElementRef,
OnDestroy,
OnInit,
computed,
output,
signal,
viewChild,
} from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
@@ -24,7 +26,7 @@ export interface FileOrFolder {
isDirectory: boolean;
name: string;
children?: FileOrFolder[];
path: string;
path?: string;
}
interface FileNode {
@@ -43,6 +45,9 @@ interface FileNode {
export class FileTreeComponent implements OnInit, OnDestroy {
fileSelected = output<FileOrFolder>();
protected fileSelector =
viewChild<ElementRef<HTMLInputElement>>('fileSelector');
// File tree
protected hasChild = (_: number, node: FileNode) => node.expandable;
@@ -77,6 +82,7 @@ export class FileTreeComponent implements OnInit, OnDestroy {
// Folder selection
protected selectedDirectory = signal<string | null>(null);
protected files = signal<FileOrFolder[]>([]);
protected isFileSelected = signal<boolean>(false);
protected workspaceName = computed(() => {
const directory = this.selectedDirectory();
if (directory) {
@@ -104,6 +110,15 @@ export class FileTreeComponent implements OnInit, OnDestroy {
}
async selectDirectory() {
try {
await this.selectDirectoryNative();
} catch (err) {
// Tauri failed, try using browser to select files
this.fileSelector()?.nativeElement.click();
}
}
private async selectDirectoryNative() {
const selectedDirectory = await open({
directory: true,
multiple: false,
@@ -118,26 +133,69 @@ export class FileTreeComponent implements OnInit, OnDestroy {
const entries = await readDir(selectedDirectory, {
recursive: true,
});
const splitNumbers = /(\d)+|(\D)+/;
this.files.set(
entries
.sort(this.sortFiles(splitNumbers))
.map((entry) => this.mapEntry(entry, splitNumbers))
entries.sort(this.sortFiles).map((entry) => this.mapEntry(entry))
);
this.isFileSelected.set(true);
}
}
private mapEntry(entry: FileEntry, splitNumbers: RegExp): FileOrFolder {
protected selectFilesBrowser() {
const files = this.fileSelector()?.nativeElement.files;
if (files && files.length > 0) {
const mappedFiles: FileOrFolder[] = [];
for (let i = 0; i < files.length; i++) {
const file = files[i];
if (file.webkitRelativePath?.includes('/')) {
// Got a file in a folder, so put it into the appropriate folder in the tree
const splitFilePath = file.webkitRelativePath.split('/');
let currentChildren: FileOrFolder[] | undefined = mappedFiles;
for (let j = 0; j < splitFilePath.length - 1; j++) {
const relativePath = splitFilePath[j];
let matchingChild: FileOrFolder | undefined = currentChildren?.find(
(mappedFile) => mappedFile.name === relativePath
);
if (!matchingChild) {
matchingChild = {
isDirectory: true,
name: relativePath,
children: [],
};
currentChildren?.push(matchingChild);
}
currentChildren = matchingChild.children;
}
currentChildren?.push({ isDirectory: false, name: file.name });
} else {
mappedFiles.push({ isDirectory: false, name: file.name });
}
}
this.recursiveSort(mappedFiles);
this.files.set(mappedFiles);
this.isFileSelected.set(true);
}
}
private mapEntry(entry: FileEntry): FileOrFolder {
return {
isDirectory: entry.children != null,
name: entry.name || '',
children: entry.children
?.sort(this.sortFiles(splitNumbers))
.map((entry) => this.mapEntry(entry, splitNumbers)),
?.sort(this.sortFiles)
.map((entry) => this.mapEntry(entry)),
path: entry.path,
};
}
private sortFiles = (splitNumbers: RegExp) => (a: FileEntry, b: FileEntry) =>
a.name?.localeCompare(b.name ?? '') ?? 0;
private sortFiles = (a: { name?: string }, b: { name?: string }) =>
new Intl.Collator(undefined, { numeric: true }).compare(a.name!, b.name!);
private recursiveSort(files: FileOrFolder[]) {
for (const file of files) {
if (file.children) {
this.recursiveSort(file.children);
}
}
files.sort(this.sortFiles);
}
}