163 lines
4.4 KiB
TypeScript
163 lines
4.4 KiB
TypeScript
import {
|
|
Component,
|
|
ElementRef,
|
|
HostListener,
|
|
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, Sheet } 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 selectedSheet = signal<Sheet | undefined>(undefined);
|
|
protected selectedFileColumns = signal<Column[]>([]);
|
|
protected sheets = signal<Sheet[]>([]);
|
|
protected selectedTab = signal(0);
|
|
protected dragging = signal(false);
|
|
|
|
private fileInput = viewChild<ElementRef<HTMLInputElement>>('fileSelector');
|
|
|
|
constructor() {
|
|
effect(() => {
|
|
const selectedFile = this.selectedSheet();
|
|
if (selectedFile) {
|
|
this.addSheet(selectedFile);
|
|
}
|
|
});
|
|
|
|
effect(() => {
|
|
if (this.selectedSheet() !== this.sheets()[this.selectedTab()]) {
|
|
if (this.sheets().length > 0) {
|
|
this.selectedSheet.set(this.sheets()[Number(this.selectedTab())]);
|
|
} else {
|
|
this.selectedSheet.set(undefined);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
protected removeTab(index: number) {
|
|
this.sheets.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.selectedSheet.set(this.sheets()[this.selectedTab()]);
|
|
} else if (this.sheets().length > 1) {
|
|
this.selectedTab.update((tab) => tab + 1);
|
|
this.selectedSheet.set(this.sheets()[this.selectedTab()]);
|
|
} else {
|
|
this.selectedSheet.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.addSheet({ file, history: [] });
|
|
}
|
|
}
|
|
}
|
|
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.addSheet({ file, history: [] });
|
|
}
|
|
}
|
|
this.selectedTab.set(this.sheets().length - 1);
|
|
}
|
|
}
|
|
|
|
private addSheet(newSheet: Sheet) {
|
|
if (
|
|
this.sheets().find(
|
|
(sheet) =>
|
|
sheet.file.webkitRelativePath === newSheet.file.webkitRelativePath &&
|
|
sheet.file.name === newSheet.file.name,
|
|
)
|
|
) {
|
|
this.selectedTab.set(
|
|
this.sheets().findIndex(
|
|
(sheet) =>
|
|
sheet.file.webkitRelativePath ===
|
|
newSheet.file.webkitRelativePath &&
|
|
sheet.file.name === newSheet.file.name,
|
|
),
|
|
);
|
|
} else {
|
|
this.sheets.update((tabs) => [...tabs, newSheet]);
|
|
this.selectedTab.set(this.sheets().length - 1);
|
|
}
|
|
}
|
|
@HostListener('window:keydown', [
|
|
'$event.key',
|
|
'$event.ctrlKey',
|
|
'$event.metaKey',
|
|
])
|
|
keydown(key: string, hasCtrl: boolean, hasMeta: boolean) {
|
|
const sheets = this.sheets();
|
|
if (sheets.length > 1 && key === 'z' && (hasCtrl || hasMeta)) {
|
|
this.sheets()[0].history.pop();
|
|
}
|
|
}
|
|
}
|