Files
model-platform/frontend/app/features/platform/state/uiStore.ts
T
2026-08-07 12:23:50 +08:00

233 lines
6.0 KiB
TypeScript

import { create } from "zustand";
import type {
ScriptItem,
ScriptType,
StableVersion,
Visibility,
} from "../../../services/api";
export type ToastTone = "success" | "error" | "info";
export type ToastState = {
tone: ToastTone;
message: string;
};
export type NewScriptForm = {
name: string;
scriptType: ScriptType;
visibility: Visibility;
parentPath: string;
};
export type ContextMenuState = {
x: number;
y: number;
kind: "root" | "directory" | "file";
path: string;
script?: ScriptItem;
};
export type FolderDialogState = {
open: boolean;
parentPath: string;
name: string;
busy: boolean;
};
export type PublishDialogState = {
target: ScriptItem | null;
releaseNote: string;
visibility: Visibility;
publishing: boolean;
publishedVersion: StableVersion | null;
};
export type UploadState = {
parentPath: string;
uploading: boolean;
};
export const initialCreateForm: NewScriptForm = {
name: "",
scriptType: "notebook",
visibility: "workspace",
parentPath: "",
};
type UiState = {
toast: ToastState | null;
createDialog: {
open: boolean;
creating: boolean;
form: NewScriptForm;
};
folderDialog: FolderDialogState;
contextMenu: ContextMenuState | null;
workspaceMenuOpen: boolean;
upload: UploadState;
publish: PublishDialogState;
// toast
pushToast: (toast: ToastState) => void;
dismissToast: () => void;
// create dialog
openCreateDialog: (parentPath?: string, scriptType?: ScriptType) => void;
closeCreateDialog: () => void;
setCreateForm: (form: NewScriptForm) => void;
setCreating: (creating: boolean) => void;
// folder dialog
openFolderDialog: (parentPath?: string) => void;
closeFolderDialog: () => void;
setFolderName: (name: string) => void;
setFolderBusy: (busy: boolean) => void;
// context menu
showContextMenu: (
event: { clientX: number; clientY: number; preventDefault: () => void; stopPropagation: () => void },
target: Omit<ContextMenuState, "x" | "y">,
) => void;
closeContextMenu: () => void;
// workspace menu (topbar)
setWorkspaceMenuOpen: (open: boolean) => void;
// upload
chooseUpload: (parentPath?: string) => void;
setUploading: (uploading: boolean) => void;
setUploadParentPath: (parentPath: string) => void;
// publish dialog
openPublishDialog: (script: ScriptItem) => void;
closePublishDialog: () => void;
setReleaseNote: (note: string) => void;
setPublishVisibility: (visibility: Visibility) => void;
setPublishing: (publishing: boolean) => void;
setPublishedVersion: (version: StableVersion) => void;
clearPublishedVersion: () => void;
};
export const useUiStore = create<UiState>((set) => ({
toast: null,
createDialog: {
open: false,
creating: false,
form: initialCreateForm,
},
folderDialog: {
open: false,
parentPath: "",
name: "",
busy: false,
},
contextMenu: null,
workspaceMenuOpen: false,
upload: {
parentPath: "",
uploading: false,
},
publish: {
target: null,
releaseNote: "",
visibility: "workspace",
publishing: false,
publishedVersion: null,
},
pushToast: (toast) => set({ toast }),
dismissToast: () => set({ toast: null }),
openCreateDialog: (parentPath = "", scriptType = "notebook") =>
set((state) => ({
contextMenu: null,
createDialog: {
open: true,
creating: false,
form: { ...initialCreateForm, parentPath, scriptType },
},
})),
closeCreateDialog: () =>
set((state) => ({
createDialog: { ...state.createDialog, open: false },
})),
setCreateForm: (form) =>
set((state) => ({ createDialog: { ...state.createDialog, form } })),
setCreating: (creating) =>
set((state) => ({ createDialog: { ...state.createDialog, creating } })),
openFolderDialog: (parentPath = "") =>
set((state) => ({
contextMenu: null,
folderDialog: {
open: true,
parentPath,
name: "",
busy: false,
},
})),
closeFolderDialog: () =>
set((state) => ({
folderDialog: { ...state.folderDialog, open: false },
})),
setFolderName: (name) =>
set((state) => ({ folderDialog: { ...state.folderDialog, name } })),
setFolderBusy: (busy) =>
set((state) => ({ folderDialog: { ...state.folderDialog, busy } })),
showContextMenu: (event, target) => {
event.preventDefault();
event.stopPropagation();
const width = 188;
const height = target.kind === "file" ? 92 : 190;
set({
contextMenu: {
...target,
x: Math.min(event.clientX, window.innerWidth - width - 8),
y: Math.min(event.clientY, window.innerHeight - height - 8),
},
});
},
closeContextMenu: () => set({ contextMenu: null }),
setWorkspaceMenuOpen: (open) => set({ workspaceMenuOpen: open }),
chooseUpload: (parentPath = "") =>
set((state) => ({
contextMenu: null,
upload: { ...state.upload, parentPath },
})),
setUploading: (uploading) =>
set((state) => ({ upload: { ...state.upload, uploading } })),
setUploadParentPath: (parentPath) =>
set((state) => ({ upload: { ...state.upload, parentPath } })),
openPublishDialog: (script) =>
set((state) => ({
publish: {
...state.publish,
target: script,
releaseNote: "",
visibility: script.visibility === "private" ? "private" : "workspace",
},
})),
closePublishDialog: () =>
set((state) => ({ publish: { ...state.publish, target: null } })),
setReleaseNote: (note) =>
set((state) => ({ publish: { ...state.publish, releaseNote: note } })),
setPublishVisibility: (visibility) =>
set((state) => ({ publish: { ...state.publish, visibility } })),
setPublishing: (publishing) =>
set((state) => ({ publish: { ...state.publish, publishing } })),
setPublishedVersion: (version) =>
set((state) => ({
publish: {
...state.publish,
target: null,
publishedVersion: version,
},
})),
clearPublishedVersion: () =>
set((state) => ({ publish: { ...state.publish, publishedVersion: null } })),
}));