vue-dockable-desktop 1.10.0 → 1.11.1

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 CHANGED
@@ -11,6 +11,55 @@ correspondence lives, alongside the feature-by-feature map in [docs/PARITY.md](d
11
11
 
12
12
  ## [Unreleased]
13
13
 
14
+ ## [1.11.1] — 2026-11-11
15
+
16
+ **Parity: react-dockable-desktop 7.10.0** (unchanged). A panel's tab icon is centred on its title.
17
+
18
+ ### Fixed
19
+
20
+ - **The tab icon sat about 1px below the title's middle,** for every icon (a component, a bare
21
+ `<svg>`, an emoji) and in every skin. It was an inline box on the title's text line, and
22
+ `vertical-align: middle` centres on the lowercase letters, not on the capitals and digits. The
23
+ tab's icon and title are now one flex row (`.vdd-workspace-tab-title`, as rdd's since 7.1.2) and
24
+ the icon is centred on the title. Long titles still end in "…": the title span
25
+ (`.vdd-workspace-tab-label`) now carries the ellipsis itself, which a flex row needs.
26
+ - **Apps that worked around it** (for example `.vdd-workspace-tab-icon { vertical-align: -0.2em }`)
27
+ should remove the override, or the icon now sits too high.
28
+ - The `#tab-content` slot's content is laid out as in 1.11.0: its wrapper stays a plain block, so
29
+ the spaces between your own elements are kept. Floating-window title bars were already centred.
30
+
31
+ ### Internal
32
+
33
+ - M17's browser gate gains a "tab icon" step: in all seven skins, LTR and RTL, the icon's centre is
34
+ within 0.5px of the title's middle (it was +1.04px), and a squeezed tab cuts a long title with an
35
+ ellipsis. It fails with either new rule removed. `releaseC.test.ts` asserts the classes, with and
36
+ without the slot (+2 tests).
37
+
38
+ ## [1.11.0] — 2026-10-07
39
+
40
+ **Parity: react-dockable-desktop 7.10.0.** Your own tab content, and an opt-in typed panel registry.
41
+ Both opt-in: without them nothing changes. Same names as rdd 7.10.0.
42
+
43
+ ### Added
44
+
45
+ - **The `#tab-content` slot on `<VddDesktop>`: your own tab content.** It replaces what's inside every
46
+ tab in the grid: the icon, the title and the dirty marker. The slot receives a `TabContentProps`:
47
+ `{ panelId, component, title, icon, dirty, selected, focused }`, with `title` already formatted
48
+ and `icon` the registration's (or `undefined`), so the built-in look is one line to start from.
49
+ Branch on `component` for a per-kind look. The tab itself stays the library's: dragging, keyboard
50
+ navigation, `role="tab"`, its context menu, state attributes and classes, and the close button.
51
+ Floating-window title bars and the taskbar are unchanged. New type: `TabContentProps`.
52
+ - **`definePanels()`: a typed panel registry, opt-in.** Wrap the `panels` map in `definePanels({…})`
53
+ and the workspace `createWorkspace` returns has a typed `openPanel`: only registered names are
54
+ accepted, and `props` is checked against that panel's props (without `panelId`). It returns its
55
+ argument unchanged; the typing is compile-time only. With typed events too, pass both:
56
+ `createWorkspace<typeof panels, AppEvents>({ panels })`. `useWorkspace()` stays untyped. A plain map
57
+ keeps its types exactly. New types: `TypedWorkspace`, `PanelMap`, `PanelPropsOf`.
58
+
59
+ ### Docs
60
+
61
+ - The API reference now names `PanelDrop` and `PanelDropTarget` (added in 1.10.0).
62
+
14
63
  ## [1.10.0] — 2026-10-06
15
64
 
16
65
  **Parity: react-dockable-desktop 7.9.0.** Control over where users can move panels: per-kind rules and
@@ -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
- declare var __VLS_13: {};
19
- type __VLS_Slots = {} & {
20
- 'empty-workspace'?: (props: typeof __VLS_13) => any;
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
- declare var __VLS_16: {};
6
- type __VLS_Slots = {} & {
7
- 'empty-workspace'?: (props: typeof __VLS_16) => any;
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
- declare var __VLS_9: {};
7
- type __VLS_Slots = {} & {
8
- 'empty-workspace'?: (props: typeof __VLS_9) => any;
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>;
@@ -1,4 +1,4 @@
1
- import type { App, Component, ComputedRef, InjectionKey } from 'vue';
1
+ import type { AllowedComponentProps, App, Component, ComputedRef, InjectionKey, VNodeProps } from 'vue';
2
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';
@@ -229,6 +229,52 @@ export interface Workspace<TEvents extends object = Record<string, unknown>> {
229
229
  }
230
230
  /** Injection key for {@link useWorkspace}. */
231
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>;
232
278
  /**
233
279
  * Create a workspace.
234
280
  *
@@ -236,3 +282,4 @@ export declare const WORKSPACE_KEY: InjectionKey<Workspace<never>>;
236
282
  * `createPinia()` / `createRouter()` shape a Vue developer already knows.
237
283
  */
238
284
  export declare function createWorkspace<TEvents extends object = Record<string, unknown>>(config?: WorkspaceConfig): Workspace<TEvents>;
285
+ export {};