vue-dockable-desktop 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/LICENSE +21 -0
  3. package/README.md +169 -0
  4. package/dist/components/VddConfirm.vue.d.ts +19 -0
  5. package/dist/components/VddContextMenu.vue.d.ts +35 -0
  6. package/dist/components/VddDesktop.vue.d.ts +28 -0
  7. package/dist/components/VddDragGhost.vue.d.ts +3 -0
  8. package/dist/components/VddDropZones.vue.d.ts +6 -0
  9. package/dist/components/VddEdgeZones.vue.d.ts +3 -0
  10. package/dist/components/VddFloatingWidget.vue.d.ts +51 -0
  11. package/dist/components/VddFloatingWindow.vue.d.ts +12 -0
  12. package/dist/components/VddLeafGroup.vue.d.ts +7 -0
  13. package/dist/components/VddModalHost.vue.d.ts +9 -0
  14. package/dist/components/VddModals.vue.d.ts +3 -0
  15. package/dist/components/VddOverlayFrame.vue.d.ts +28 -0
  16. package/dist/components/VddPanelMount.vue.d.ts +8 -0
  17. package/dist/components/VddPanelOverlay.vue.d.ts +13 -0
  18. package/dist/components/VddPanelSlot.vue.d.ts +9 -0
  19. package/dist/components/VddPanelToolbar.vue.d.ts +26 -0
  20. package/dist/components/VddSecondarySidebar.vue.d.ts +16 -0
  21. package/dist/components/VddSidePanelHost.vue.d.ts +10 -0
  22. package/dist/components/VddSidePanels.vue.d.ts +12 -0
  23. package/dist/components/VddSidebar.vue.d.ts +54 -0
  24. package/dist/components/VddSidebarDrawer.vue.d.ts +51 -0
  25. package/dist/components/VddSidebarRail.vue.d.ts +22 -0
  26. package/dist/components/VddSidebarTabScope.vue.d.ts +22 -0
  27. package/dist/components/VddTaskbar.vue.d.ts +17 -0
  28. package/dist/components/VddTaskbarPreview.vue.d.ts +24 -0
  29. package/dist/components/VddToastIcon.vue.d.ts +8 -0
  30. package/dist/components/VddToastItem.vue.d.ts +18 -0
  31. package/dist/components/VddToasts.vue.d.ts +36 -0
  32. package/dist/components/VddToolbar.vue.d.ts +20 -0
  33. package/dist/components/VddToolbarButton.vue.d.ts +25 -0
  34. package/dist/components/VddToolbarCenter.vue.d.ts +13 -0
  35. package/dist/components/VddToolbarGroupButton.vue.d.ts +8 -0
  36. package/dist/components/VddToolbarItem.vue.d.ts +13 -0
  37. package/dist/components/VddToolbarSearch.vue.d.ts +27 -0
  38. package/dist/components/VddToolbarSeparator.vue.d.ts +3 -0
  39. package/dist/components/VddToolbarSpacer.vue.d.ts +3 -0
  40. package/dist/components/VddToolbarToggle.vue.d.ts +32 -0
  41. package/dist/components/VddWorkspaceGrid.vue.d.ts +8 -0
  42. package/dist/composables/useColorScheme.d.ts +19 -0
  43. package/dist/composables/useContextMenu.d.ts +29 -0
  44. package/dist/composables/useContributions.d.ts +48 -0
  45. package/dist/composables/useDragDock.d.ts +59 -0
  46. package/dist/composables/useOverlayHost.d.ts +30 -0
  47. package/dist/composables/useOverlays.d.ts +51 -0
  48. package/dist/composables/usePanel.d.ts +82 -0
  49. package/dist/composables/usePanelDom.d.ts +7 -0
  50. package/dist/composables/usePanelOverlay.d.ts +35 -0
  51. package/dist/composables/useSidebar.d.ts +15 -0
  52. package/dist/composables/useToolbar.d.ts +9 -0
  53. package/dist/composables/useWorkspace.d.ts +18 -0
  54. package/dist/core/anchorGeometry.d.ts +39 -0
  55. package/dist/core/contextMenu.d.ts +74 -0
  56. package/dist/core/contributions.d.ts +56 -0
  57. package/dist/core/dragResize.d.ts +67 -0
  58. package/dist/core/eventBus.d.ts +73 -0
  59. package/dist/core/layoutTree.d.ts +96 -0
  60. package/dist/core/messages.d.ts +117 -0
  61. package/dist/core/overlayState.d.ts +64 -0
  62. package/dist/core/overlays.d.ts +114 -0
  63. package/dist/core/panelDom.d.ts +79 -0
  64. package/dist/core/panelMenu.d.ts +45 -0
  65. package/dist/core/panelOverlay.d.ts +121 -0
  66. package/dist/core/registry.d.ts +61 -0
  67. package/dist/core/serializable.d.ts +26 -0
  68. package/dist/core/serialize.d.ts +32 -0
  69. package/dist/core/sidebarTypes.d.ts +102 -0
  70. package/dist/core/stretch.d.ts +47 -0
  71. package/dist/core/toast.d.ts +474 -0
  72. package/dist/core/toolbarState.d.ts +13 -0
  73. package/dist/core/toolbarTypes.d.ts +99 -0
  74. package/dist/core/workspace.d.ts +222 -0
  75. package/dist/index.cjs +5 -0
  76. package/dist/index.cjs.map +1 -0
  77. package/dist/index.d.ts +64 -0
  78. package/dist/index.js +4851 -0
  79. package/dist/index.js.map +1 -0
  80. package/dist/styles.css +3944 -0
  81. package/dist/types.d.ts +145 -0
  82. package/package.json +102 -0
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Toolbar item shapes.
3
+ *
4
+ * Kept as data, with rdd's field names, so a React app's `items` array transfers verbatim —
5
+ * a toolbar is configuration, not markup.
6
+ */
7
+ import type { Component } from 'vue';
8
+ /** A one-shot button. */
9
+ export interface ToolbarActionItem {
10
+ type: 'action';
11
+ id: string;
12
+ label: string;
13
+ icon: Component;
14
+ onClick: () => void;
15
+ disabled?: boolean;
16
+ }
17
+ /** One of a set, of which exactly one is active. */
18
+ export interface ToolbarRadioItem {
19
+ type: 'radio';
20
+ id: string;
21
+ /** Items sharing a group are mutually exclusive. */
22
+ group: string;
23
+ label: string;
24
+ icon: Component;
25
+ /** Shown in a group flyout; reserved for a richer tooltip. */
26
+ shortcut?: string;
27
+ onActivate?: (id: string) => void;
28
+ disabled?: boolean;
29
+ }
30
+ /**
31
+ * An independent on/off modifier — snap-to-grid, say.
32
+ *
33
+ * Uncontrolled by default, with the state kept on the workspace and keyed by `id`. Supply
34
+ * `active` and the caller becomes the source of truth, which is what lets two instances of
35
+ * the same panel type report independent state instead of colliding on a shared id.
36
+ */
37
+ export interface ToolbarToggleItem {
38
+ type: 'toggle';
39
+ id: string;
40
+ label: string;
41
+ icon: Component;
42
+ shortcut?: string;
43
+ /** Controlled state. Supplying it — **including as `false`** — makes the caller the owner. */
44
+ active?: boolean;
45
+ onToggle?: (active: boolean) => void;
46
+ disabled?: boolean;
47
+ }
48
+ /** One entry inside a group's flyout. */
49
+ export interface ToolbarGroupSubItem {
50
+ id: string;
51
+ label: string;
52
+ icon: Component;
53
+ /** Displayed in the flyout. */
54
+ shortcut?: string;
55
+ disabled?: boolean;
56
+ onActivate?: (id: string) => void;
57
+ }
58
+ export type ToolbarGroupEntry = ToolbarGroupSubItem | {
59
+ type: 'separator';
60
+ };
61
+ /**
62
+ * A collapsed family of tools: one button that opens a flyout, with radio semantics inside.
63
+ *
64
+ * The button's icon becomes the active sub-tool's, so the strip shows what is selected
65
+ * without being expanded.
66
+ */
67
+ export interface ToolbarGroupItem {
68
+ type: 'group';
69
+ /** Both the button id and the radio group key. */
70
+ id: string;
71
+ /** Tooltip when nothing is selected. */
72
+ label: string;
73
+ /** Icon when nothing is selected. */
74
+ defaultIcon: Component;
75
+ items: ToolbarGroupEntry[];
76
+ disabled?: boolean;
77
+ /** Controlled active sub-item. Supplying it — **including as `null`** — makes the caller the owner. */
78
+ activeItemId?: string | null;
79
+ onActiveItemChange?: (id: string) => void;
80
+ }
81
+ export interface ToolbarSeparator {
82
+ type: 'separator';
83
+ }
84
+ export type ToolbarItem = ToolbarActionItem | ToolbarRadioItem | ToolbarToggleItem | ToolbarGroupItem | ToolbarSeparator;
85
+ export declare const isSubItem: (entry: ToolbarGroupEntry) => entry is ToolbarGroupSubItem;
86
+ /**
87
+ * Where a group's flyout opens: away from the strip, mirrored for reading direction.
88
+ *
89
+ * Pure, so the placement is testable without layout.
90
+ */
91
+ export declare function flyoutPlacement(rect: {
92
+ left: number;
93
+ right: number;
94
+ top: number;
95
+ bottom: number;
96
+ }, position: 'left' | 'right' | 'top' | 'bottom', viewport: {
97
+ width: number;
98
+ height: number;
99
+ }, isRtl?: boolean, gap?: number): Record<string, number>;
@@ -0,0 +1,222 @@
1
+ import type { App, Component, ComputedRef, InjectionKey } from 'vue';
2
+ import type { DirtyStateOptions, DropPosition, FloatAnchor, FloatingWindow, Label, LayoutNode, MessageDescriptor, MessageFormatter, PanelInfo, SplitDirection } from '../types';
3
+ import { PanelRegistry } from './registry';
4
+ import type { PanelDefaultOptions } from './registry';
5
+ import type { ContextMenuItem, ShowContextMenuOptions } from './contextMenu';
6
+ import type { BuiltInEvents } from './eventBus';
7
+ import { defaultMessages } from './messages';
8
+ import type { ToolbarState } from './toolbarState';
9
+ import type { Overlays } from './overlays';
10
+ import type { Contributions, PanelContribution } from './contributions';
11
+ /** One entry in the panel catalogue passed to {@link createWorkspace}. */
12
+ export interface PanelDefinition {
13
+ component: Component;
14
+ defaultOptions?: PanelDefaultOptions;
15
+ }
16
+ /** Configuration for {@link createWorkspace}. */
17
+ export interface WorkspaceConfig {
18
+ /** The panel catalogue: keys used by `openPanel` and in saved layouts. */
19
+ panels?: Record<string, PanelDefinition>;
20
+ /** A layout from a previous `saveLayout()`. `null`/omitted starts from an empty workspace. */
21
+ initialState?: string | null;
22
+ /** Reading direction. `'rtl'` mirrors drop zones, tab order and corner anchors. @default 'ltr' */
23
+ dir?: 'ltr' | 'rtl';
24
+ /** Resolves the library's own message descriptors. The whole i18n surface. */
25
+ formatMessage?: MessageFormatter;
26
+ /** Override any subset of the built-in UI strings. */
27
+ messages?: Partial<Record<keyof typeof defaultMessages, MessageDescriptor>>;
28
+ /** Fraction taken by a panel dropped on a leaf's edge. Clamped to 0.1–0.9. @default 0.5 */
29
+ defaultSplitRatio?: number;
30
+ /** Fraction taken by a panel dropped on a workspace edge. Clamped to 0.1–0.9. @default 0.2 */
31
+ defaultEdgeSplitRatio?: number;
32
+ /** Base z-index for floating windows and all library chrome, mirrored as `--vdd-z-base`. @default 1000 */
33
+ zIndexBase?: number;
34
+ /**
35
+ * Your own classes, added to the library's own chrome.
36
+ *
37
+ * For attaching a styling framework's utilities — Tailwind, MUI, Bootstrap — to elements
38
+ * you otherwise cannot reach, since they are inside the library's markup. The library's
39
+ * own `vdd-` classes stay, so this adds rather than replaces.
40
+ *
41
+ * rdd exposed the same thing through a `useStyleClasses()` hook whose only test was that
42
+ * the hook returned the config; here the gate and a test assert the classes reach the
43
+ * rendered elements, which is the part that can actually break.
44
+ */
45
+ classes?: HostClasses;
46
+ }
47
+ /** Consumer classes added to the library's chrome. Every field is optional. */
48
+ export interface HostClasses {
49
+ /** The modal's window box. */
50
+ modal?: string;
51
+ /** The modal's scrolling body. */
52
+ modalBody?: string;
53
+ /** A side panel's window box. */
54
+ sidePanel?: string;
55
+ /** A side panel's scrolling body. */
56
+ sidePanelBody?: string;
57
+ /** A floating window's outer box. */
58
+ window?: string;
59
+ /** A floating window's body, which holds the panel. */
60
+ windowBody?: string;
61
+ }
62
+ /** Options for {@link Workspace.openPanel}. */
63
+ export interface OpenPanelOptions<P extends object = Record<string, unknown>> {
64
+ /** Override the tab/window title. */
65
+ title?: Label;
66
+ /** Where it goes. @default the registry's `initialTarget`, else `'docked'` */
67
+ initialTarget?: 'floating' | 'docked' | 'tabbed';
68
+ /** Corner to pin to, for a floating panel. */
69
+ anchor?: FloatAnchor | null;
70
+ /** Make it the active panel. @default true */
71
+ focus?: boolean;
72
+ /** Per-instance data handed to the component. Whether it survives `saveLayout()` is a
73
+ * runtime fact — see {@link PanelInfo.serializable}. */
74
+ props?: P;
75
+ /** If another open panel of the same `component` has this key, focus that one instead. */
76
+ dedupeKey?: string;
77
+ }
78
+ /** The live workspace state. */
79
+ export interface WorkspaceState {
80
+ gridRoot: LayoutNode;
81
+ floating: FloatingWindow[];
82
+ minimized: {
83
+ id: string;
84
+ title: Label;
85
+ component: string;
86
+ }[];
87
+ panels: Record<string, PanelInfo>;
88
+ /** The panel the user is looking at. Always visible, never minimised. */
89
+ activePanelId: string | null;
90
+ /** The panel being dragged, or `null`. */
91
+ draggedPanelId: string | null;
92
+ dir: 'ltr' | 'rtl';
93
+ isRtl: boolean;
94
+ splitRatio: number;
95
+ edgeSplitRatio: number;
96
+ }
97
+ /** A workspace instance: reactive state, actions, registry, and a Vue plugin. */
98
+ export interface Workspace<TEvents extends Record<string, unknown> = Record<string, unknown>> {
99
+ /** The whole state, read-only. Reactive — use it in templates and `computed`. */
100
+ readonly state: Readonly<WorkspaceState>;
101
+ /** The panel catalogue. */
102
+ readonly registry: PanelRegistry;
103
+ readonly gridRoot: ComputedRef<LayoutNode>;
104
+ readonly floating: ComputedRef<FloatingWindow[]>;
105
+ readonly minimized: ComputedRef<WorkspaceState['minimized']>;
106
+ readonly panels: ComputedRef<Record<string, PanelInfo>>;
107
+ readonly activePanelId: ComputedRef<string | null>;
108
+ readonly draggedPanelId: ComputedRef<string | null>;
109
+ readonly dir: ComputedRef<'ltr' | 'rtl'>;
110
+ readonly isRtl: ComputedRef<boolean>;
111
+ openPanel<P extends object = Record<string, unknown>>(id: string, component: string, options?: OpenPanelOptions<P>): void;
112
+ closePanel(id: string): void;
113
+ requestClosePanel(id: string, options?: {
114
+ force?: boolean;
115
+ onConfirm?: (opts?: DirtyStateOptions) => Promise<boolean>;
116
+ }): Promise<void>;
117
+ minimizePanel(id: string): void;
118
+ restorePanel(id: string, options?: {
119
+ focus?: boolean;
120
+ }): void;
121
+ floatPanel(id: string, rect?: {
122
+ x: number;
123
+ y: number;
124
+ width: number;
125
+ height: number;
126
+ }, anchor?: FloatAnchor | null): void;
127
+ dockPanel(id: string, targetLeafId?: string): void;
128
+ dockPanelToGroup(id: string, targetLeafId: string, position: DropPosition): void;
129
+ dockPanelToWorkspaceEdge(id: string, position: SplitDirection): void;
130
+ /** Toggle a panel between filling the workspace and its previous size. A minimised panel
131
+ * is restored first — rdd's equivalent silently did nothing (divergence D1). */
132
+ maximizePanel(id: string): void;
133
+ focusPanel(id: string): void;
134
+ movePanelOrder(panelId: string, targetLeafId: string, targetIndex: number): void;
135
+ closeLeafGroup(leafId: string): void;
136
+ updateSplitSizes(path: number[], sizes: number[]): void;
137
+ updateFloatingPosition(id: string, updates: Partial<Pick<FloatingWindow, 'x' | 'y' | 'width' | 'height' | 'anchor'>>): void;
138
+ setPanelDirty(id: string, dirty: boolean, options?: DirtyStateOptions): void;
139
+ updatePanelTitle(id: string, title: Label): void;
140
+ setDirection(dir: 'ltr' | 'rtl'): void;
141
+ /** @internal drives drag visuals */
142
+ setDraggedPanelId(id: string | null): void;
143
+ isOpen(id: string): boolean;
144
+ getOpenPanelIds(): string[];
145
+ findPanelId(component: string, dedupeKey: string): string | null;
146
+ saveLayout(): string;
147
+ loadLayout(json: string): boolean;
148
+ publish<K extends keyof (TEvents & BuiltInEvents) & string>(event: K, data: (TEvents & BuiltInEvents)[K]): void;
149
+ subscribe<K extends keyof (TEvents & BuiltInEvents) & string>(event: K, cb: (data: (TEvents & BuiltInEvents)[K]) => void): () => void;
150
+ registerCloseGuard(id: string, guard: () => boolean | Promise<boolean>): () => void;
151
+ registerStateProvider(id: string, provider: () => unknown): () => void;
152
+ /**
153
+ * Contribute extra items to a panel's own context menu. The getter is read each time the
154
+ * menu opens, so state-driven changes (enabled, checked, present at all) just work.
155
+ */
156
+ registerPanelMenu(id: string, getItems: () => ContextMenuItem[]): () => void;
157
+ /** The items a panel has contributed, or an empty list. */
158
+ panelMenuItems(id: string): ContextMenuItem[];
159
+ /**
160
+ * Open a context menu.
161
+ *
162
+ * Callable from anywhere, including outside the component tree — the request is state, and
163
+ * `<VddContextMenu>` renders whatever is pending. rdd needed a provider, an adapter ref and
164
+ * a registration handshake to make this reachable from components on either side of the
165
+ * workspace in the tree.
166
+ */
167
+ showContextMenu(options: ShowContextMenuOptions): void;
168
+ /** Dismiss the open menu, if any. */
169
+ closeContextMenu(): void;
170
+ /** The pending menu request, or `null`. @internal */
171
+ readonly contextMenu: ComputedRef<{
172
+ x: number;
173
+ y: number;
174
+ items: ContextMenuItem[];
175
+ } | null>;
176
+ /**
177
+ * Toolbar selection state, for items that are not controlled by the caller.
178
+ *
179
+ * On the workspace rather than behind its own provider, so `useToolbar()` works anywhere —
180
+ * including from a service. rdd needed a `<ToolbarProvider>` in the tree for this.
181
+ */
182
+ readonly toolbar: ToolbarState;
183
+ /**
184
+ * Toolbar items and sidebar sections panels publish while they are active.
185
+ *
186
+ * Read through {@link Workspace.activeContribution}, which is driven by `activePanelId` —
187
+ * and therefore by the invariant that `activePanelId` never names a panel the user cannot
188
+ * see. rdd's could (divergence D2), which made the shell's toolbar show controls belonging
189
+ * to a panel hidden behind another tab.
190
+ */
191
+ readonly contributions: Contributions;
192
+ /** What the active panel has published, or `null`. Reactive. */
193
+ readonly activeContribution: ComputedRef<PanelContribution | null>;
194
+ /**
195
+ * Side panels and the modal stack.
196
+ *
197
+ * Here for the same reason as `toolbar`: a modal is very often opened from something that
198
+ * is not a component — a save handler, a router guard, an error interceptor. rdd needed a
199
+ * `<PanelProvider>` in the tree, so those callers had to be handed an action object.
200
+ */
201
+ readonly overlays: Overlays;
202
+ /** Your own classes for the library's chrome, with every field present. */
203
+ readonly classes: Required<HostClasses>;
204
+ /** Resolve a label through the configured formatter. */
205
+ format(label: Label | undefined): string;
206
+ /** The merged message table. */
207
+ readonly messages: Record<keyof typeof defaultMessages, MessageDescriptor>;
208
+ readonly config: Readonly<Required<Pick<WorkspaceConfig, 'zIndexBase'>> & WorkspaceConfig>;
209
+ /** Vue plugin entry point: `app.use(workspace)`. */
210
+ install(app: App): void;
211
+ /** Drop every listener and guard. Only needed when workspaces are created dynamically. */
212
+ dispose(): void;
213
+ }
214
+ /** Injection key for {@link useWorkspace}. */
215
+ export declare const WORKSPACE_KEY: InjectionKey<Workspace<never>>;
216
+ /**
217
+ * Create a workspace.
218
+ *
219
+ * The returned object is both the imperative API and a Vue plugin, matching the
220
+ * `createPinia()` / `createRouter()` shape a Vue developer already knows.
221
+ */
222
+ export declare function createWorkspace<TEvents extends Record<string, unknown> = Record<string, unknown>>(config?: WorkspaceConfig): Workspace<TEvents>;