Files
ingey-eager/src/app/app.component.ts
vato007 4def7b8daf
All checks were successful
build / build (push) Successful in 1m31s
Add basic column selection
2025-07-08 19:10:31 +09:30

150 lines
4.0 KiB
TypeScript

import {
Component,
ElementRef,
effect,
signal,
viewChild,
} from '@angular/core';
import { ButtonModule } from 'primeng/button';
import { SplitterModule } from 'primeng/splitter';
import { TabsModule } from 'primeng/tabs';
import { ToolbarModule } from 'primeng/toolbar';
import { IconField } from 'primeng/iconfield';
import { InputIcon } from 'primeng/inputicon';
import { SplitButtonModule } from 'primeng/splitbutton';
import { InputTextModule } from 'primeng/inputtext';
import { FileTreeComponent } from './file-tree/file-tree.component';
import { FileViewerComponent } from './file-viewer/file-viewer.component';
import { ColumnEditorComponent } from './column-editor/column-editor.component';
import { Column } from './duckdb.service';
@Component({
selector: 'app-root',
imports: [
ButtonModule,
SplitterModule,
TabsModule,
ToolbarModule,
IconField,
InputIcon,
InputTextModule,
SplitButtonModule,
FileTreeComponent,
FileViewerComponent,
ColumnEditorComponent,
],
templateUrl: './app.component.html',
styleUrl: './app.component.scss',
})
export class AppComponent {
protected selectedFile = signal<File | undefined>(undefined);
protected selectedFileColumns = signal<Column[]>([]);
protected tabs = signal<File[]>([]);
protected selectedTab = signal(0);
protected dragging = signal(false);
private fileInput = viewChild<ElementRef<HTMLInputElement>>('fileSelector');
constructor() {
effect(() => {
const selectedFile = this.selectedFile();
if (selectedFile) {
this.addFile(selectedFile);
}
});
effect(() => {
if (this.selectedFile() !== this.tabs()[this.selectedTab()]) {
if (this.tabs().length > 0) {
this.selectedFile.set(this.tabs()[Number(this.selectedTab())]);
} else {
this.selectedFile.set(undefined);
}
}
});
}
protected removeTab(index: number) {
this.tabs.update((tabs) => {
const copy = tabs.slice();
copy.splice(index, 1);
return copy;
});
if (this.selectedTab() === index) {
if (this.selectedTab() > 0) {
this.selectedTab.update((tab) => tab - 1);
this.selectedFile.set(this.tabs()[this.selectedTab()]);
} else if (this.tabs().length > 1) {
this.selectedTab.update((tab) => tab + 1);
this.selectedFile.set(this.tabs()[this.selectedTab()]);
} else {
this.selectedFile.set(undefined);
}
}
}
protected fileDropped(event: DragEvent) {
event.preventDefault();
const files = event.dataTransfer?.files;
if (files) {
for (const file of files) {
if (file.type === 'text/csv') {
this.addFile(file);
}
}
}
this.dragging.set(false);
}
protected dragEnter(event: DragEvent) {
event.preventDefault();
const numItems = event.dataTransfer?.items.length;
if (numItems) {
for (let i = 0; i < numItems; i++) {
if (event.dataTransfer?.items[i].type === 'text/csv') {
this.dragging.set(true);
return;
}
}
}
}
protected dragLeave(event: DragEvent) {
event.preventDefault();
this.dragging.set(false);
}
protected fileChange(event: Event) {
const files = this.fileInput()?.nativeElement.files;
if (files) {
for (const file of files) {
if (file.type === 'text/csv') {
this.addFile(file);
}
}
this.selectedTab.set(this.tabs().length - 1);
}
}
private addFile(file: File) {
if (
this.tabs().find(
(tab) =>
tab.webkitRelativePath === file.webkitRelativePath &&
tab.name === file.name,
)
) {
this.selectedTab.set(
this.tabs().findIndex(
(tab) =>
tab.webkitRelativePath === file.webkitRelativePath &&
tab.name === file.name,
),
);
} else {
this.tabs.update((tabs) => [...tabs, file]);
this.selectedTab.set(this.tabs().length - 1);
}
}
}