vue-dockable-desktop 1.9.0 → 1.11.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.
- package/CHANGELOG.md +67 -0
- package/dist/components/VddDesktop.vue.d.ts +10 -3
- package/dist/components/VddLeafGroup.vue.d.ts +4 -4
- package/dist/components/VddWorkspaceGrid.vue.d.ts +10 -3
- package/dist/core/dockRules.d.ts +9 -0
- package/dist/core/panelMenu.d.ts +5 -0
- package/dist/core/registry.d.ts +13 -0
- package/dist/core/workspace.d.ts +57 -2
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +629 -523
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +47 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,73 @@ correspondence lives, alongside the feature-by-feature map in [docs/PARITY.md](d
|
|
|
11
11
|
|
|
12
12
|
## [Unreleased]
|
|
13
13
|
|
|
14
|
+
## [1.11.0] — 2026-10-07
|
|
15
|
+
|
|
16
|
+
**Parity: react-dockable-desktop 7.10.0.** Your own tab content, and an opt-in typed panel registry.
|
|
17
|
+
Both opt-in: without them nothing changes. Same names as rdd 7.10.0.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- **The `#tab-content` slot on `<VddDesktop>`: your own tab content.** It replaces what's inside every
|
|
22
|
+
tab in the grid: the icon, the title and the dirty marker. The slot receives a `TabContentProps`:
|
|
23
|
+
`{ panelId, component, title, icon, dirty, selected, focused }`, with `title` already formatted
|
|
24
|
+
and `icon` the registration's (or `undefined`), so the built-in look is one line to start from.
|
|
25
|
+
Branch on `component` for a per-kind look. The tab itself stays the library's: dragging, keyboard
|
|
26
|
+
navigation, `role="tab"`, its context menu, state attributes and classes, and the close button.
|
|
27
|
+
Floating-window title bars and the taskbar are unchanged. New type: `TabContentProps`.
|
|
28
|
+
- **`definePanels()`: a typed panel registry, opt-in.** Wrap the `panels` map in `definePanels({…})`
|
|
29
|
+
and the workspace `createWorkspace` returns has a typed `openPanel`: only registered names are
|
|
30
|
+
accepted, and `props` is checked against that panel's props (without `panelId`). It returns its
|
|
31
|
+
argument unchanged; the typing is compile-time only. With typed events too, pass both:
|
|
32
|
+
`createWorkspace<typeof panels, AppEvents>({ panels })`. `useWorkspace()` stays untyped. A plain map
|
|
33
|
+
keeps its types exactly. New types: `TypedWorkspace`, `PanelMap`, `PanelPropsOf`.
|
|
34
|
+
|
|
35
|
+
### Docs
|
|
36
|
+
|
|
37
|
+
- The API reference now names `PanelDrop` and `PanelDropTarget` (added in 1.10.0).
|
|
38
|
+
|
|
39
|
+
## [1.10.0] — 2026-10-06
|
|
40
|
+
|
|
41
|
+
**Parity: react-dockable-desktop 7.9.0.** Control over where users can move panels: per-kind rules and
|
|
42
|
+
an app-wide veto, all opt-in. Without them nothing changes, and the app's own calls are never
|
|
43
|
+
restricted. Same names as rdd 7.9.0.
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
|
|
47
|
+
- **`canFloat` and `canDock` per kind**, next to `canDrag`, `canMinimize` and `canClose`.
|
|
48
|
+
`canFloat: false` keeps a panel in the grid: a tab drag can't end in a floating window, and
|
|
49
|
+
"Float Window" and the taskbar's "Maximize" are hidden. `canDock: false` keeps a panel floating:
|
|
50
|
+
dragging it offers no group, tab or edge targets.
|
|
51
|
+
- **`canDrop`, an app-wide veto.** `createWorkspace({ canDrop: ({ panelId, component, to }) => boolean })`
|
|
52
|
+
is asked for every place a dragged panel could go, and for the "Float" and "Maximize" menu items;
|
|
53
|
+
`to` is a `PanelDropTarget` (a group, an edge, or a float). A forbidden target isn't offered, and
|
|
54
|
+
it's asked again at release. Positions and corners are the ones the move applies, mirrored under
|
|
55
|
+
RTL. If it throws, the move is allowed and the error is logged. New types: `PanelDrop`,
|
|
56
|
+
`PanelDropTarget`.
|
|
57
|
+
- Both apply only to what the user does; the app's own calls always work.
|
|
58
|
+
|
|
59
|
+
### Fixed
|
|
60
|
+
|
|
61
|
+
- **A drag built on `startPointerDrag` could outlive its element.** It ended on pointerup,
|
|
62
|
+
pointercancel and window blur, but not when the capturing element was removed mid-drag (an
|
|
63
|
+
overlay widget closed during its own drag), which left its listeners attached and its classes on
|
|
64
|
+
the page. It now also ends on a lost pointer capture, listened for on the document, where browsers
|
|
65
|
+
fire it; `onCancel` runs if given.
|
|
66
|
+
|
|
67
|
+
### Docs
|
|
68
|
+
|
|
69
|
+
- Layout (Chapter 4): **Controlling where users can move panels**. Panels (Chapter 3) and the API
|
|
70
|
+
reference: the new options. `PARITY.md`: the rdd 7.9.0 trace.
|
|
71
|
+
|
|
72
|
+
### Tests
|
|
73
|
+
|
|
74
|
+
- `test/components/releaseB.test.ts` (12 tests): `canFloat` (no float on nothing, no corners, no
|
|
75
|
+
"Float Window" or "Maximize" from a group, the app's `floatPanel` still works), `canDock` (no group
|
|
76
|
+
or edge targets, only corners), `canDrop` (its argument, vetoed zones and edges not offered, RTL
|
|
77
|
+
positions, tab insertion into a vetoed group, rules changed mid-drag for zones and tabs, a throwing
|
|
78
|
+
`canDrop`, no rules meaning every target), and `startPointerDrag` ending on a lost capture. Each
|
|
79
|
+
rule was seen failing with it broken. `api-surface.json` gains the two types.
|
|
80
|
+
|
|
14
81
|
## [1.9.0] — 2026-10-06
|
|
15
82
|
|
|
16
83
|
**Parity: react-dockable-desktop 7.8.0.** More control over how the desktop looks and where panels
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { TaskbarVisibility } from './VddTaskbar.vue';
|
|
2
|
+
import type { TabContentProps } from '../types';
|
|
2
3
|
type __VLS_Props = {
|
|
3
4
|
/** Built-in skin, or a name you scope your own tokens to. @default 'vscode' */
|
|
4
5
|
skin?: string;
|
|
@@ -15,9 +16,15 @@ type __VLS_Props = {
|
|
|
15
16
|
/** Fallback icon for panels that register none. */
|
|
16
17
|
defaultPanelIcon?: unknown;
|
|
17
18
|
};
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
'empty-workspace'?: (
|
|
19
|
+
type __VLS_Slots = {
|
|
20
|
+
/** Your view while no panel is docked (1.9.0). */
|
|
21
|
+
'empty-workspace'?: () => unknown;
|
|
22
|
+
/**
|
|
23
|
+
* Your own content for each tab in the grid (1.11.0): it replaces the built-in icon, title and
|
|
24
|
+
* dirty marker. The tab itself (dragging, keyboard, accessibility, its menu, the close button)
|
|
25
|
+
* stays the library's. Branch on `tab.component` for a per-type look.
|
|
26
|
+
*/
|
|
27
|
+
'tab-content'?: (tab: TabContentProps) => unknown;
|
|
21
28
|
};
|
|
22
29
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
23
30
|
taskbarContextMenu: (panelId: string, event: MouseEvent | PointerEvent) => any;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { LayoutLeafNode } from '../types';
|
|
1
|
+
import type { LayoutLeafNode, TabContentProps } from '../types';
|
|
2
2
|
type __VLS_Props = {
|
|
3
3
|
leaf: LayoutLeafNode;
|
|
4
4
|
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
'
|
|
5
|
+
type __VLS_Slots = {
|
|
6
|
+
'empty-workspace'?: () => unknown;
|
|
7
|
+
'tab-content'?: (tab: TabContentProps) => unknown;
|
|
8
8
|
};
|
|
9
9
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
10
10
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
@@ -1,11 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The recursive grid: a branch renders its children with a draggable divider between each
|
|
3
|
+
* pair; a leaf renders a tab group.
|
|
4
|
+
*
|
|
5
|
+
* `path` is the index route from the root to this node — what `updateSplitSizes` addresses.
|
|
6
|
+
*/
|
|
7
|
+
import type { TabContentProps } from '../types';
|
|
1
8
|
import type { LayoutNode } from '../types';
|
|
2
9
|
type __VLS_Props = {
|
|
3
10
|
node: LayoutNode;
|
|
4
11
|
path: number[];
|
|
5
12
|
};
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
'
|
|
13
|
+
type __VLS_Slots = {
|
|
14
|
+
'empty-workspace'?: () => unknown;
|
|
15
|
+
'tab-content'?: (tab: TabContentProps) => unknown;
|
|
9
16
|
};
|
|
10
17
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
11
18
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Docking rules (1.10.0): what the *user* may do with a panel. The kind's own `canFloat` /
|
|
3
|
+
* `canDock` first, then the workspace's `canDrop`. Never consulted by the app's own calls.
|
|
4
|
+
*/
|
|
5
|
+
import type { PanelDropTarget } from '../types';
|
|
6
|
+
import type { Workspace } from './workspace';
|
|
7
|
+
type RuleHost = Pick<Workspace<never>, 'state' | 'registry' | 'config'>;
|
|
8
|
+
export declare function isDropAllowed(ws: RuleHost, panelId: string, to: PanelDropTarget): boolean;
|
|
9
|
+
export {};
|
package/dist/core/panelMenu.d.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import type { ContextMenuItem } from './contextMenu';
|
|
9
9
|
import type { PanelDefaultOptions } from './registry';
|
|
10
10
|
import type { Label, MessageDescriptor } from '../types';
|
|
11
|
+
import type { Workspace } from './workspace';
|
|
11
12
|
/**
|
|
12
13
|
* Just enough of the workspace to build a menu, so this module does not depend on the store.
|
|
13
14
|
*
|
|
@@ -19,6 +20,10 @@ interface MenuHost {
|
|
|
19
20
|
format: (label: Label | undefined) => string;
|
|
20
21
|
messages: Record<string, MessageDescriptor>;
|
|
21
22
|
panelMenuItems: (id: string) => ContextMenuItem[];
|
|
23
|
+
/** For the docking rules (1.10.0): floating is a move they decide. */
|
|
24
|
+
state: Workspace<never>['state'];
|
|
25
|
+
registry: Workspace<never>['registry'];
|
|
26
|
+
config: Workspace<never>['config'];
|
|
22
27
|
}
|
|
23
28
|
export interface PanelMenuActions {
|
|
24
29
|
float: () => void;
|
package/dist/core/registry.d.ts
CHANGED
|
@@ -23,6 +23,19 @@ export interface PanelDefaultOptions {
|
|
|
23
23
|
canMinimize?: boolean;
|
|
24
24
|
/** Allow dragging the tab, which is also what allows floating by drag. @default true */
|
|
25
25
|
canDrag?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* `false`: the user can't make this panel floating. A tab drag can't end in a floating window
|
|
28
|
+
* (dropped on nothing or on a corner, it stays where it was), and "Float Window" and the taskbar's
|
|
29
|
+
* "Maximize" are hidden. A window the app floated itself can still be moved. The app's own
|
|
30
|
+
* `floatPanel` always works. @default true (1.10.0)
|
|
31
|
+
*/
|
|
32
|
+
canFloat?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* `false`: the user can't dock this panel. Dragging it offers no group, tab or edge targets
|
|
35
|
+
* (corners still pin a floating window), so it stays floating. The app's own docking calls always
|
|
36
|
+
* work; give the kind `initialTarget: 'floating'` so it opens floating. @default true (1.10.0)
|
|
37
|
+
*/
|
|
38
|
+
canDock?: boolean;
|
|
26
39
|
/** Show a letter tile instead of a live thumbnail in the taskbar hover preview. @default false */
|
|
27
40
|
disableLivePreview?: boolean;
|
|
28
41
|
/**
|
package/dist/core/workspace.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { App, Component, ComputedRef, InjectionKey } from 'vue';
|
|
2
|
-
import type { DirtyStateOptions, DropPosition, FloatAnchor, FloatingWindow, Label, LayoutNode, MessageDescriptor, MessageFormatter, PanelInfo, SplitDirection } from '../types';
|
|
1
|
+
import type { AllowedComponentProps, App, Component, ComputedRef, InjectionKey, VNodeProps } from 'vue';
|
|
2
|
+
import type { DirtyStateOptions, DropPosition, FloatAnchor, PanelDrop, FloatingWindow, Label, LayoutNode, MessageDescriptor, MessageFormatter, PanelInfo, SplitDirection } from '../types';
|
|
3
3
|
import { PanelRegistry } from './registry';
|
|
4
4
|
import type { PanelDefaultOptions } from './registry';
|
|
5
5
|
import type { ContextMenuItem, ContextMenuRequest, ShowContextMenuOptions } from './contextMenu';
|
|
@@ -43,6 +43,14 @@ export interface WorkspaceConfig {
|
|
|
43
43
|
* rendered elements, which is the part that can actually break.
|
|
44
44
|
*/
|
|
45
45
|
classes?: HostClasses;
|
|
46
|
+
/**
|
|
47
|
+
* Veto where users can move panels (1.10.0). Called for each place a dragged panel could go, and
|
|
48
|
+
* for the built-in "Float" and "Maximize" menu items: return `false` and that target isn't offered
|
|
49
|
+
* and the move doesn't happen. It runs after the kind's own `canFloat` / `canDock`, and only for
|
|
50
|
+
* what the user does: the app's own calls always work. Keep it fast and pure; it runs while the
|
|
51
|
+
* pointer moves.
|
|
52
|
+
*/
|
|
53
|
+
canDrop?: (drop: PanelDrop) => boolean;
|
|
46
54
|
}
|
|
47
55
|
/** Consumer classes added to the library's chrome. Every field is optional. */
|
|
48
56
|
export interface HostClasses {
|
|
@@ -221,6 +229,52 @@ export interface Workspace<TEvents extends object = Record<string, unknown>> {
|
|
|
221
229
|
}
|
|
222
230
|
/** Injection key for {@link useWorkspace}. */
|
|
223
231
|
export declare const WORKSPACE_KEY: InjectionKey<Workspace<never>>;
|
|
232
|
+
/** The type-only mark {@link definePanels} puts on a map. No such value exists at runtime. */
|
|
233
|
+
declare const panelsBrand: unique symbol;
|
|
234
|
+
/** A panel map marked by {@link definePanels}, so `createWorkspace` types `openPanel` from it (1.11.0). */
|
|
235
|
+
export type PanelMap<TPanels extends Record<string, PanelDefinition> = Record<string, PanelDefinition>> = TPanels & {
|
|
236
|
+
readonly [panelsBrand]: true;
|
|
237
|
+
};
|
|
238
|
+
/** A component's props: from `$props` for a component object, from the first argument for a functional one. */
|
|
239
|
+
type PropsOfComponent<C> = C extends new (...args: never[]) => {
|
|
240
|
+
$props: infer P;
|
|
241
|
+
} ? P : C extends (props: infer P, ...rest: never[]) => unknown ? P : Record<string, unknown>;
|
|
242
|
+
/**
|
|
243
|
+
* The props `openPanel` passes to a registered panel: its component's props, without `panelId`
|
|
244
|
+
* (the library passes it) or Vue's own attributes (`key`, `ref`, `class`, `style`).
|
|
245
|
+
*/
|
|
246
|
+
export type PanelPropsOf<TDefinition> = TDefinition extends {
|
|
247
|
+
component: infer C;
|
|
248
|
+
} ? Omit<PropsOfComponent<C>, 'panelId' | keyof VNodeProps | keyof AllowedComponentProps> : never;
|
|
249
|
+
/**
|
|
250
|
+
* Marks a panel map for typing (1.11.0). Returns it unchanged; a workspace created from it gets a
|
|
251
|
+
* typed `openPanel`: only registered names, and `props` checked against that panel's component.
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* ```ts
|
|
255
|
+
* const panels = definePanels({ map: { component: MapPanel }, chart: { component: ChartPanel } })
|
|
256
|
+
* const workspace = createWorkspace({ panels })
|
|
257
|
+
* workspace.openPanel('c1', 'chart', { props: { series: 3 } }) // checked against ChartPanel's props
|
|
258
|
+
* ```
|
|
259
|
+
*/
|
|
260
|
+
export declare function definePanels<const TPanels extends Record<string, PanelDefinition>>(panels: TPanels): PanelMap<TPanels>;
|
|
261
|
+
/**
|
|
262
|
+
* A workspace created from {@link definePanels}: the same object, with `openPanel` typed from the
|
|
263
|
+
* registry. It goes anywhere a {@link Workspace} goes. `useWorkspace()` stays untyped; keep this
|
|
264
|
+
* one for typed calls.
|
|
265
|
+
*/
|
|
266
|
+
export interface TypedWorkspace<TPanels extends Record<string, PanelDefinition>, TEvents extends object = Record<string, unknown>> extends Workspace<TEvents> {
|
|
267
|
+
/** `openPanel`, typed: a registered name, and that panel's props. */
|
|
268
|
+
openPanel<K extends keyof TPanels & string>(id: string, component: K, options?: OpenPanelOptions<PanelPropsOf<TPanels[K]>>): void;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Create a workspace from a map marked by {@link definePanels} (1.11.0): `openPanel` takes only the
|
|
272
|
+
* registered names, with `props` checked against each panel's component. To name the events as
|
|
273
|
+
* well, pass both type arguments: `createWorkspace<typeof panels, AppEvents>({ panels })`.
|
|
274
|
+
*/
|
|
275
|
+
export declare function createWorkspace<TPanels extends Record<string, PanelDefinition>, TEvents extends object = Record<string, unknown>>(config: WorkspaceConfig & {
|
|
276
|
+
panels: PanelMap<TPanels>;
|
|
277
|
+
}): TypedWorkspace<TPanels, TEvents>;
|
|
224
278
|
/**
|
|
225
279
|
* Create a workspace.
|
|
226
280
|
*
|
|
@@ -228,3 +282,4 @@ export declare const WORKSPACE_KEY: InjectionKey<Workspace<never>>;
|
|
|
228
282
|
* `createPinia()` / `createRouter()` shape a Vue developer already knows.
|
|
229
283
|
*/
|
|
230
284
|
export declare function createWorkspace<TEvents extends object = Record<string, unknown>>(config?: WorkspaceConfig): Workspace<TEvents>;
|
|
285
|
+
export {};
|