Feat: Add workspace context actions

This commit is contained in:
MangoPig
2026-06-18 11:16:54 +01:00
parent dea9e7e6ff
commit eeba19bbb6
10 changed files with 1464 additions and 119 deletions

View File

@@ -82,6 +82,12 @@ export type SidebarItem = {
meta?: string;
};
export type WorkspaceStaticKind = "workspace" | "home" | "settings";
export type WorkspaceStaticItem = SidebarItem & {
contextKind: WorkspaceStaticKind;
};
export type WorkspaceTreeNode = {
id: string;
label: string;
@@ -111,6 +117,69 @@ export type MobileBottomNavItem = {
active?: boolean;
};
export type WorkspaceContextMenuTarget = {
id: string;
label: string;
kind: WorkspaceStaticKind | WorkspaceTreeNode["kind"];
};
export type WorkspaceContextMenuAction = {
id: string;
label: string;
tone?: "default" | "danger";
shortcut?: WorkspaceContextMenuShortcut;
children?: readonly WorkspaceContextMenuAction[];
};
export type WorkspaceContextMenuShortcutModifier = "meta" | "alt" | "shift";
export type WorkspaceContextMenuShortcutKey = "b" | "c" | "d" | "delete" | "enter" | "f" | "m" | "r";
export type WorkspaceContextMenuShortcut = {
modifiers?: readonly WorkspaceContextMenuShortcutModifier[];
key: WorkspaceContextMenuShortcutKey;
};
export type WorkspaceContextMenuSection = {
id: string;
label?: string;
items: readonly WorkspaceContextMenuAction[];
};
export const getWorkspaceContextMenuEyebrow = (target: WorkspaceContextMenuTarget): string => {
switch (target.kind) {
case "workspace":
case "home":
return "Workspace";
case "settings":
return "Configuration";
case "folder":
return "Folder";
case "board":
return "Board";
case "doc":
return "Doc";
}
};
export const createWorkspaceSurfaceTarget = (workspace: ActiveProject): WorkspaceContextMenuTarget => ({
id: `workspace-${workspace.id}`,
label: workspace.name,
kind: "workspace",
});
export const createWorkspaceStaticTarget = (item: WorkspaceStaticItem): WorkspaceContextMenuTarget => ({
id: item.id,
label: item.label,
kind: item.contextKind,
});
export const createWorkspaceTreeTarget = (node: WorkspaceTreeNode): WorkspaceContextMenuTarget => ({
id: node.id,
label: node.label,
kind: node.kind,
});
export type NotificationItem = {
id: string;
title: string;
@@ -190,9 +259,9 @@ export const departmentItems: readonly DepartmentItem[] = [
// Sidebar and topbar scaffold data
// These static entries stay pinned in both desktop and mobile workspace navigation.
export const workspaceStaticItems: readonly SidebarItem[] = [
{ id: "home", label: "Home", icon: Home, active: true },
{ id: "workspace-settings", label: "Settings", icon: Settings },
export const workspaceStaticItems: readonly WorkspaceStaticItem[] = [
{ id: "home", label: "Home", icon: Home, active: true, contextKind: "home" },
{ id: "workspace-settings", label: "Settings", icon: Settings, contextKind: "settings" },
] as const;
// Freeform workspace tree scaffold: folders, boards, and docs are first-class siblings.
@@ -240,6 +309,129 @@ export const mobileBottomNavItems: readonly MobileBottomNavItem[] = [
{ id: "browse", label: "Browse", icon: Folder },
] as const;
// Initial context-menu IA scaffold. Behavior wiring can evolve later, but the
// target kinds and action grouping should stay shared across workspace surfaces.
export const getWorkspaceContextMenuSections = (
target: WorkspaceContextMenuTarget,
): readonly WorkspaceContextMenuSection[] => {
const createActions = [
{ id: "new-folder", label: "New folder", shortcut: { modifiers: ["alt"], key: "f" } },
{ id: "new-board", label: "New board", shortcut: { modifiers: ["alt"], key: "b" } },
{ id: "new-doc", label: "New doc", shortcut: { modifiers: ["alt"], key: "d" } },
] as const;
const createSubmenuAction = {
id: "create",
label: "Create",
children: createActions,
} as const;
switch (target.kind) {
case "workspace":
return [
{
id: "create",
label: undefined,
items: [createSubmenuAction],
},
{
id: "workspace",
label: undefined,
items: [
{ id: "rename-workspace", label: "Rename workspace", shortcut: { key: "enter" } },
{ id: "copy-workspace-link", label: "Copy link", shortcut: { modifiers: ["meta"], key: "c" } },
],
},
] as const;
case "home":
return [
{
id: "create",
label: undefined,
items: [createSubmenuAction],
},
{
id: "workspace",
label: undefined,
items: [{ id: "open-home", label: "Open home", shortcut: { key: "enter" } }],
},
] as const;
case "settings":
return [
{
id: "settings",
label: undefined,
items: [
{ id: "open-settings", label: "Open settings", shortcut: { key: "enter" } },
{ id: "copy-settings-link", label: "Copy link", shortcut: { modifiers: ["meta"], key: "c" } },
],
},
] as const;
case "folder":
return [
{
id: "open",
items: [
{ id: "open-folder", label: "Open folder", shortcut: { key: "enter" } },
{ id: "rename-folder", label: "Rename", shortcut: { modifiers: ["meta"], key: "r" } },
],
},
{
id: "create",
label: undefined,
items: [createSubmenuAction],
},
{
id: "organize",
label: undefined,
items: [
{ id: "duplicate-folder", label: "Duplicate", shortcut: { modifiers: ["meta"], key: "d" } },
{ id: "move-folder", label: "Move", shortcut: { modifiers: ["meta"], key: "m" } },
{ id: "delete-folder", label: "Delete", shortcut: { modifiers: ["meta"], key: "delete" }, tone: "danger" },
],
},
] as const;
case "board":
return [
{
id: "board",
items: [
{ id: "open-board", label: "Open board", shortcut: { key: "enter" } },
{ id: "rename-board", label: "Rename", shortcut: { modifiers: ["meta"], key: "r" } },
],
},
{
id: "organize",
label: undefined,
items: [
{ id: "duplicate-board", label: "Duplicate", shortcut: { modifiers: ["meta"], key: "d" } },
{ id: "move-board", label: "Move", shortcut: { modifiers: ["meta"], key: "m" } },
{ id: "delete-board", label: "Delete", shortcut: { modifiers: ["meta"], key: "delete" }, tone: "danger" },
],
},
] as const;
case "doc":
return [
{
id: "doc",
items: [
{ id: "open-doc", label: "Open doc", shortcut: { key: "enter" } },
{ id: "rename-doc", label: "Rename", shortcut: { modifiers: ["meta"], key: "r" } },
],
},
{
id: "organize",
label: undefined,
items: [
{ id: "duplicate-doc", label: "Duplicate", shortcut: { modifiers: ["meta"], key: "d" } },
{ id: "move-doc", label: "Move", shortcut: { modifiers: ["meta"], key: "m" } },
{ id: "delete-doc", label: "Delete", shortcut: { modifiers: ["meta"], key: "delete" }, tone: "danger" },
],
},
] as const;
}
};
export const topBarActions: readonly TopBarAction[] = [
{ id: "search", label: "Search", icon: Search },
] as const;