You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
web-file-system/src/frontend/widgets/UiContextMenuController.ts

173 lines
4.5 KiB

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<ContextMenuState>({
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<HTMLElement>("[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));
}
}