import {copyText} from '../../shared' import {computed, nextTick, reactive} from "vue"; import {deleteFile, store} from "../store"; import {dispatch} from "../worker.ts"; import {showDirectoryNaming} from "./UiDirectoryNamingController"; import {toast} from "./UiToastController"; export type ContextmenuTarget = | "directory" // 作用在侧边栏文件夹按钮上 | "file" // 作用在右侧文件图标上 | "filename" // 作用在右侧文件标题图标上 | "sidebar" // 作用在侧边栏上 | "content"; // 作用在右侧文件容器上 export interface ContextMenuOptions { x: number; y: number; directoryId?: number; fileId?: number; target?: ContextmenuTarget; } interface ContextMenuState extends ContextMenuOptions { visible: boolean; } export const controller = reactive({ x: 0, y: 0, directoryId: 0, fileId: undefined, visible: false, }); export const file = computed((): CloudFile | undefined => { return controller.fileId ? store.files[controller.directoryId ?? 0]?.find(f => f.id === controller.fileId) : undefined }) export const directory = computed(() => { const dirId = controller.directoryId return dirId == 0 ? store.rootDirectory : dirId != null ? store.directories.get(dirId) : undefined }) export function isTarget(...targets: ContextmenuTarget[]): boolean { return controller.target != null && targets.includes(controller.target); } export function showContextMenu(opts: ContextMenuOptions): void { if (opts.target && opts.directoryId != null) { controller.visible = true; void nextTick(() => { controller.x = opts.x; controller.y = opts.y; controller.fileId = opts.fileId; controller.directoryId = opts.directoryId; controller.target = opts.target; }); } else { hideContextMenu(); } } export function hideContextMenu() { if (controller.visible) { controller.visible = false; void nextTick(() => { if (!controller.visible) { controller.directoryId = undefined; controller.fileId = undefined; controller.target = undefined; } }); } } export function toggleContextMenu(evt: MouseEvent): void { const elm = evt.target as HTMLElement; if (elm.closest('[data-role="contextmenu"]')) { return; } const dataset = elm.closest("[data-contextmenu]")?.dataset; if (dataset && dataset.target != null && dataset.directoryId != null) { showContextMenu({ x: evt.clientX, y: evt.clientY, fileId: dataset.fileId ? Number(dataset.fileId) : undefined, directoryId: Number(dataset.directoryId), target: dataset.target as ContextmenuTarget, }); } else { hideContextMenu(); } } export function handleNewDir(child?: boolean) { showDirectoryNaming({ action: "create", pid: child ? controller.directoryId : undefined, }); } // 重命名文件或文件夹 export function renameDir() { const {directoryId} = controller if (isTarget("directory") && directoryId) { showDirectoryNaming({ id: directoryId, action: "rename", }); } } export function copyDirName() { const text = directory.value?.title text?.length && copyText(text) } export function copyFileName() { const text = file.value?.name text?.length && copyText(text) } export function copyFilePath() { const text = file.value?.object.path text?.length && copyText(text) } export function removeDir() { const {directoryId} = controller if (isTarget("directory") && directoryId) { dispatch("dir", "delete", directoryId) .then(() => store.directories.delete(directoryId)) .then(() => toast("success", "删除成功")) .catch((error) => toast("error", error)); } } export function removeFile() { const f = file.value; if (isTarget("file", "filename") && f) { dispatch("file", "delete", f.id) .then(() => deleteFile(f)) .then(() => toast("success", "删除成功")) .catch((error) => toast("error", error)); } } export function canShare() { return typeof navigator.canShare === 'function' && navigator.canShare({ title: "MDN", text: "Learn web development on MDN!", url: "https://developer.mozilla.org", }) } export function share() { if (file.value) { navigator .share({ title: file.value!.name, text: file.value!.object.path, url: file.value!.object.path, }) .then(() => toast("success", "分享成功")) .catch((error) => toast("error", error)); } }