Files
clickhouse-agx/src/lib/FileDropEventManager.ts
2025-02-25 13:50:47 +01:00

78 lines
1.7 KiB
TypeScript

import { webview } from '@tauri-apps/api';
import type { Webview } from '@tauri-apps/api/webview';
import { readTextFile } from '@tauri-apps/plugin-fs';
import { on } from 'svelte/events';
type FileInfo = {
path: string;
content: () => Promise<string>;
type: string;
};
type Listener = (file: FileInfo) => any;
type NativeEvent = Parameters<Parameters<Webview['onDragDropEvent']>[0]>[0];
export class FileDropEventManager {
#listeners = new Set<Listener>();
#unlisten?: () => void;
constructor() {
if (PLATFORM === 'WEB') this.#unlisten = on(window, 'drop', this.#web_handler.bind(this));
else
webview
.getCurrentWebview()
.onDragDropEvent(this.#native_handler.bind(this))
.then((un) => (this.#unlisten = un));
}
async #web_handler(event: DragEvent) {
const files = Array.from(event.dataTransfer?.files ?? []);
if (!files.length) return;
event.preventDefault();
for (const file of files) {
this.#emit({
path: file.name,
content: () => file.text(),
type: getFileTypeFromPath(file.name)
});
}
}
async #native_handler({ payload }: NativeEvent) {
if (payload.type !== 'drop') return;
const paths = payload.paths;
if (!paths.length) return;
for (const path of paths) {
this.#emit({ path, content: () => readTextFile(path), type: getFileTypeFromPath(path) });
}
}
#emit(file: FileInfo) {
if (!this.#listeners.size) return;
queueMicrotask(() => {
for (const fn of this.#listeners) fn(file);
});
}
on(e: 'drop', handler: Listener) {
this.#listeners.add(handler);
return () => {
this.#listeners.delete(handler);
};
}
unlisten() {
this.#listeners.clear();
this.#unlisten?.();
}
}
function getFileTypeFromPath(path: string) {
return path.split('.').pop()?.toLowerCase() ?? '';
}