react-dockable-desktop 1.3.0 → 2.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.
package/dist/index.d.cts CHANGED
@@ -90,7 +90,7 @@ declare const PanelRegistry: PanelRegistryClass;
90
90
  * define in their IntlProvider messages table. The `defaultMessage` is used
91
91
  * as a fallback when no external formatter is provided.
92
92
  *
93
- * Pass a partial or full override to <WindowManagerProvider predefinedMessages={…} />
93
+ * Pass a partial or full override to `<WindowManagerProvider predefinedMessages={…} />`
94
94
  * to customise labels without replacing the whole table.
95
95
  */
96
96
  declare const defaultPredefinedMessages: {
@@ -358,73 +358,221 @@ interface WindowState {
358
358
  isRtl: boolean;
359
359
  }
360
360
  /**
361
- * Interface mapping layout actions, event bus handles, and serialization methods.
361
+ * All layout mutation methods, event bus handles, and serialization methods
362
+ * exposed by the `WindowManagerProvider`.
363
+ *
364
+ * Obtain this object via {@link useWindowManagerActions} inside a component,
365
+ * or via {@link WorkspaceClient} methods from outside the React tree.
366
+ *
367
+ * @group Hooks
368
+ * @example
369
+ * ```tsx
370
+ * function MyToolbar() {
371
+ * const actions = useWindowManagerActions();
372
+ * return <button onClick={() => actions.openPanel('map-1', 'map')}>Open Map</button>;
373
+ * }
374
+ * ```
362
375
  */
363
376
  interface WindowActions {
364
- /** Instantiates a registered panel into the workspace. */
377
+ /**
378
+ * Opens a registered panel into the workspace.
379
+ * If the panel ID is already open, the panel is focused instead of duplicated.
380
+ * @param id - Unique instance identifier for this panel.
381
+ * @param component - Component key registered in the panel catalog.
382
+ * @param options - Optional display and placement overrides.
383
+ * @example
384
+ * ```ts
385
+ * actions.openPanel('map-1', 'map', { title: 'Satellite View', initialTarget: 'floating' });
386
+ * ```
387
+ */
365
388
  openPanel: (id: string, component: string, options?: {
366
389
  title?: string | ContextMenuPredefinedMessage;
367
390
  initialTarget?: 'floating' | 'docked' | 'tabbed';
368
391
  stickyRight?: boolean;
369
392
  stickyBottom?: boolean;
370
393
  }) => void;
371
- /** Directly closes a panel by ID, bypassing close confirmation dialogs. */
394
+ /**
395
+ * Closes a panel immediately, bypassing dirty-state close guards.
396
+ * For guarded close, use {@link requestClosePanel}.
397
+ * @param id - Panel instance ID.
398
+ */
372
399
  closePanel: (id: string) => void;
373
- /** Minimizes a panel to the bottom taskbar, saving its current layout positioning. */
400
+ /**
401
+ * Minimizes a panel to the bottom taskbar dock, preserving its layout position.
402
+ * @param id - Panel instance ID.
403
+ */
374
404
  minimizePanel: (id: string) => void;
375
- /** Restores a minimized panel back to its previous position in the grid or as a float. */
405
+ /**
406
+ * Restores a minimized panel back to its last docked or floating position.
407
+ * @param id - Panel instance ID.
408
+ */
376
409
  restorePanel: (id: string) => void;
377
- /** Detaches a docked panel, turning it into a floating resizable window. */
410
+ /**
411
+ * Detaches a docked panel, converting it to a resizable floating window.
412
+ * @param id - Panel instance ID.
413
+ * @param rect - Optional initial position and size for the floating window.
414
+ */
378
415
  floatPanel: (id: string, rect?: {
379
416
  x: number;
380
417
  y: number;
381
418
  width: number;
382
419
  height: number;
383
420
  }) => void;
384
- /** Returns a floating window back to a docked grid tab group. */
421
+ /**
422
+ * Returns a floating window to a docked grid tab group.
423
+ * @param id - Panel instance ID.
424
+ * @param targetLeafId - Target leaf group ID. Defaults to the panel's last leaf.
425
+ */
385
426
  dockPanel: (id: string, targetLeafId?: string) => void;
386
- /** Maximizes a floating window to cover the entire layout screen boundaries. */
427
+ /**
428
+ * Maximizes a floating window to cover the entire workspace viewport.
429
+ * @param id - Panel instance ID.
430
+ */
387
431
  maximizePanel: (id: string) => void;
388
- /** Resizes flex dimensions of children inside split layout rows/columns. */
432
+ /**
433
+ * Resizes the flex split proportions of a branch node's children.
434
+ * @param path - Index path from root to the branch node.
435
+ * @param sizes - New proportional sizes (must sum to 1.0).
436
+ */
389
437
  updateSplitSizes: (path: number[], sizes: number[]) => void;
390
- /** Updates bounds or positioning attributes on a floating window. */
438
+ /**
439
+ * Updates the position or size of a floating window.
440
+ * @param id - Panel instance ID.
441
+ * @param updates - Partial update to `x`, `y`, `width`, `height`, `stickyRight`, or `stickyBottom`.
442
+ */
391
443
  updateFloatingPosition: (id: string, updates: Partial<Pick<FloatingWindow, 'x' | 'y' | 'width' | 'height' | 'stickyRight' | 'stickyBottom'>>) => void;
392
- /** Pushes a floating window z-index layer to render on top of others. */
393
- bringToFront: (id: string) => void;
394
- /** Serializes the active grid node structures and panel targets to JSON. */
444
+ /**
445
+ * Activates the given panel regardless of its current state.
446
+ * - Floating panel: raises z-index so the window appears on top of others.
447
+ * - Docked panel: selects the tab within its leaf group.
448
+ * @param id - Panel instance ID.
449
+ * @example
450
+ * ```ts
451
+ * // Ensure a panel is visible before updating its content:
452
+ * if (actions.isOpen('map-1')) actions.focusPanel('map-1');
453
+ * ```
454
+ */
455
+ focusPanel: (id: string) => void;
456
+ /**
457
+ * Returns `true` if a panel with the given ID is currently open (docked, floating, or minimized).
458
+ * Uses a synchronous `stateRef` read — safe to call outside of render.
459
+ * @param id - Panel instance ID.
460
+ * @returns `true` if the panel is open.
461
+ * @example
462
+ * ```ts
463
+ * if (!actions.isOpen('map-1')) {
464
+ * actions.openPanel('map-1', 'map');
465
+ * } else {
466
+ * actions.focusPanel('map-1');
467
+ * }
468
+ * ```
469
+ */
470
+ isOpen: (id: string) => boolean;
471
+ /**
472
+ * Returns the IDs of all currently open panels (docked, floating, and minimized).
473
+ * Uses a synchronous `stateRef` read — safe to call outside of render.
474
+ * @returns Array of panel instance IDs.
475
+ */
476
+ getOpenPanelIds: () => string[];
477
+ /**
478
+ * Serializes the entire workspace state to a JSON string.
479
+ * Includes grid layout, floating window positions, minimized panels, and panel metadata.
480
+ * @returns JSON string suitable for storage and later restoration via {@link loadLayout}.
481
+ * @example
482
+ * ```ts
483
+ * localStorage.setItem('layout', actions.saveLayout());
484
+ * ```
485
+ */
395
486
  saveLayout: () => string;
396
- /** Rebuilds the grid layouts and floating window placements from a JSON string. */
487
+ /**
488
+ * Restores a previously serialized workspace from a JSON string.
489
+ * Replaces the entire current layout — all panels not in the snapshot are closed.
490
+ * @param layoutJson - JSON string produced by {@link saveLayout}.
491
+ */
397
492
  loadLayout: (layoutJson: string) => void;
398
- /** Publishes an event to the global inter-panel message bus. */
493
+ /**
494
+ * Publishes an event to the inter-panel pub/sub event bus.
495
+ * @param event - Event name string.
496
+ * @param data - Arbitrary payload passed to all subscribers.
497
+ */
399
498
  publish: (event: string, data: any) => void;
400
- /** Subscribes callback listeners to inter-panel event messages. */
499
+ /**
500
+ * Subscribes a callback to the inter-panel pub/sub event bus.
501
+ * @param event - Event name string.
502
+ * @param callback - Function called with the event payload.
503
+ * @returns Unsubscribe function — call it to remove the listener.
504
+ * @example
505
+ * ```ts
506
+ * useEffect(() => actions.subscribe('map:zoom', ({ level }) => setZoom(level)), []);
507
+ * ```
508
+ */
401
509
  subscribe: (event: string, callback: (data: any) => void) => () => void;
402
- /** Stores reference to the active tab ID being dragged. */
510
+ /** @internal Stores reference to the active tab ID being dragged. */
403
511
  setDraggedPanelId: (id: string | null) => void;
404
- /** Splits an existing tab group to dock a dragged panel next to it. */
512
+ /**
513
+ * Splits an existing leaf group and docks a panel to the given side.
514
+ * @param id - Panel instance ID to dock.
515
+ * @param targetLeafId - Leaf group ID to split.
516
+ * @param position - Which side of the target to split and dock into.
517
+ */
405
518
  dockPanelToGroup: (id: string, targetLeafId: string, position: 'left' | 'right' | 'top' | 'bottom' | 'center') => void;
406
- /** Reorders tab indices inside a docked leaf group. */
519
+ /**
520
+ * Reorders a panel's tab index within a docked leaf group.
521
+ * @param panelId - Panel instance ID to move.
522
+ * @param targetLeafId - Destination leaf group ID.
523
+ * @param targetIndex - New tab index within the target group.
524
+ */
407
525
  movePanelOrder: (panelId: string, targetLeafId: string, targetIndex: number) => void;
408
- /** Closes an empty split group. */
526
+ /**
527
+ * Closes an empty leaf group (removes it from the grid tree).
528
+ * @param leafId - Leaf node ID to remove.
529
+ */
409
530
  closeLeafGroup: (leafId: string) => void;
410
- /** Binds a close intercept confirmation guard. */
531
+ /**
532
+ * Registers a close guard that can intercept and cancel panel close requests.
533
+ * @param id - Panel instance ID to guard.
534
+ * @param guard - Function returning `true` (allow close) or `false` / `Promise<false>` (block).
535
+ */
411
536
  registerCloseGuard: (id: string, guard: () => boolean | Promise<boolean>) => void;
412
- /** Removes close confirmation guards. */
537
+ /**
538
+ * Removes a previously registered close guard.
539
+ * @param id - Panel instance ID.
540
+ */
413
541
  unregisterCloseGuard: (id: string) => void;
414
- /** Set panel dirty state flag. */
542
+ /**
543
+ * Marks a panel as dirty (has unsaved changes). Dirty panels show a visual indicator
544
+ * and the built-in close guard prompts the user before closing.
545
+ * @param id - Panel instance ID.
546
+ * @param dirty - `true` to mark dirty, `false` to clear.
547
+ * @param options - Custom confirmation dialog options.
548
+ */
415
549
  setPanelDirty: (id: string, dirty: boolean, options?: DirtyStateOptions) => void;
416
- /** Change title header. */
550
+ /**
551
+ * Updates the display title of an open panel.
552
+ * @param id - Panel instance ID.
553
+ * @param title - New title string or localizable message descriptor.
554
+ */
417
555
  updatePanelTitle: (id: string, title: string | ContextMenuPredefinedMessage) => void;
418
- /** Intercepts close panel requests, prompting warning dialogs if dirty. */
556
+ /**
557
+ * Closes a panel, first running any registered close guards.
558
+ * If the panel is dirty, shows the built-in unsaved-changes confirmation dialog.
559
+ * @param id - Panel instance ID.
560
+ * @param options - `force: true` bypasses guards; `onConfirm` provides a custom dialog.
561
+ */
419
562
  requestClosePanel: (id: string, options?: {
420
563
  force?: boolean;
421
564
  onConfirm?: (opts?: DirtyStateOptions) => Promise<boolean>;
422
565
  }) => Promise<void>;
423
- /** Docks a panel directly to workspace edges. */
566
+ /**
567
+ * Docks a floating panel to a workspace edge, creating a full-width or full-height column/row.
568
+ * @param id - Panel instance ID.
569
+ * @param position - Edge to dock to.
570
+ */
424
571
  dockPanelToWorkspaceEdge: (id: string, position: 'left' | 'right' | 'top' | 'bottom') => void;
425
- /** Update active focused tab reference. */
426
- setActivePanel: (id: string | null) => void;
427
- /** Explicitly set or override layout direction */
572
+ /**
573
+ * Overrides the workspace layout direction.
574
+ * @param dir - `'ltr'` or `'rtl'`.
575
+ */
428
576
  setDirection: (dir: 'ltr' | 'rtl') => void;
429
577
  }
430
578
  /** Represents custom CSS classes injected into layout parts. */
@@ -438,6 +586,23 @@ interface StyleClasses {
438
586
  }
439
587
  /** Custom hook to read configured style class contexts. */
440
588
  declare const useStyleClasses: () => StyleClasses;
589
+ /**
590
+ * React hook to read the scoped {@link PanelRegistryClass} for the current provider.
591
+ * When the provider was created with a {@link WorkspaceClient}, this returns the client's
592
+ * private registry. Otherwise it returns the global `PanelRegistry` singleton.
593
+ *
594
+ * @group Hooks
595
+ * @returns The panel registry instance in scope.
596
+ * @example
597
+ * ```tsx
598
+ * function MyComponent() {
599
+ * const registry = useRegistry();
600
+ * const entry = registry.get('map');
601
+ * return entry ? <entry.Component panelId="preview" /> : null;
602
+ * }
603
+ * ```
604
+ */
605
+ declare const useRegistry: () => PanelRegistryClass;
441
606
  interface WindowManagerProviderProps {
442
607
  children: React__default.ReactNode;
443
608
  /** WorkspaceClient instance created outside the React tree. When provided, its registry
@@ -455,13 +620,40 @@ interface WindowManagerProviderProps {
455
620
  }
456
621
  declare const WindowManagerProvider: React__default.FC<WindowManagerProviderProps>;
457
622
  /**
458
- * React hook to retrieve the active Window Manager layout state.
623
+ * React hook to subscribe to the live {@link WindowState} inside a component.
624
+ * The component re-renders whenever the state changes.
625
+ *
626
+ * For imperative reads without a subscription, use {@link WorkspaceClient} methods
627
+ * like `isOpen()` and `getOpenPanelIds()` instead.
628
+ *
629
+ * @group Hooks
630
+ * @returns The current workspace state tree.
459
631
  * @throws Error if used outside of a {@link WindowManagerProvider}.
632
+ * @example
633
+ * ```tsx
634
+ * function PanelList() {
635
+ * const { panels } = useWindowManagerState();
636
+ * return <ul>{Object.keys(panels).map(id => <li key={id}>{id}</li>)}</ul>;
637
+ * }
638
+ * ```
460
639
  */
461
640
  declare const useWindowManagerState: () => WindowState;
462
641
  /**
463
- * React hook to retrieve layouts mutation actions (dock, float, minimize, save/load).
642
+ * React hook to retrieve all layout mutation actions.
643
+ * Returns the public {@link WindowActions} interface.
644
+ *
645
+ * @group Hooks
646
+ * @returns The full set of workspace mutation methods.
464
647
  * @throws Error if used outside of a {@link WindowManagerProvider}.
648
+ * @example
649
+ * ```tsx
650
+ * function Toolbar() {
651
+ * const actions = useWindowManagerActions();
652
+ * return (
653
+ * <button onClick={() => actions.openPanel('map-1', 'map')}>Open Map</button>
654
+ * );
655
+ * }
656
+ * ```
465
657
  */
466
658
  declare const useWindowManagerActions: () => WindowActions;
467
659
  /**
@@ -511,11 +703,20 @@ interface WorkspaceClientConfig {
511
703
  /**
512
704
  * WorkspaceClient is the central configuration and imperative API object for
513
705
  * react-dockable-desktop. Create one instance outside the React tree and pass
514
- * it to <WindowManagerProvider client={client}>.
706
+ * it to `<WindowManagerProvider client={client}>`.
515
707
  *
516
708
  * Pattern: TanStack QueryClient / Redux store — configuration and imperative
517
709
  * access live on the client; rendering is delegated to the thin React provider.
518
710
  *
711
+ * @remarks
712
+ * Calls made before the provider mounts are queued and replayed automatically
713
+ * in order once `_connect()` fires. If the client is never connected to a
714
+ * provider (e.g. `client={workspace}` was forgotten), a console warning is
715
+ * emitted in development after 1 second.
716
+ *
717
+ * `subscribe()` and `saveLayout()` return values immediately and cannot be
718
+ * queued — they return safe defaults (`() => {}` and `''`) when disconnected.
719
+ *
519
720
  * @example
520
721
  * const workspace = new WorkspaceClient({
521
722
  * panels: {
@@ -531,7 +732,8 @@ interface WorkspaceClientConfig {
531
732
  *
532
733
  * // Imperative access from anywhere:
533
734
  * workspace.saveLayout();
534
- * workspace.openPanel('map', 'map');
735
+ * workspace.openPanel('map-1', 'map');
736
+ * workspace.focusPanel('map-1');
535
737
  */
536
738
  declare class WorkspaceClient {
537
739
  /** Scoped panel registry — fully independent from the global singleton. */
@@ -541,6 +743,10 @@ declare class WorkspaceClient {
541
743
  /** Non-rendering configuration forwarded to the provider. */
542
744
  readonly config: Pick<WorkspaceClientConfig, 'formatMessage' | 'predefinedMessages' | 'dir'>;
543
745
  private _actions;
746
+ /** Calls queued before _connect() fires — replayed in order on first connect. */
747
+ private _pendingCalls;
748
+ /** DEV-only timer that warns if _connect() is never called within 1 second. */
749
+ private _disconnectedWarnTimer;
544
750
  constructor(config?: WorkspaceClientConfig);
545
751
  /** @internal Called by WindowManagerProvider after mount. */
546
752
  _connect(actions: WindowActions): void;
@@ -548,8 +754,11 @@ declare class WorkspaceClient {
548
754
  _disconnect(): void;
549
755
  /** True while the provider is mounted and React state is accessible. */
550
756
  get isConnected(): boolean;
551
- saveLayout(): string;
552
- loadLayout(json: string): void;
757
+ /**
758
+ * Dispatches a void action immediately if connected, or queues it for replay.
759
+ * In development, warns if the client is still not connected after 1 second.
760
+ */
761
+ private _dispatch;
553
762
  openPanel(...args: Parameters<WindowActions['openPanel']>): void;
554
763
  closePanel(id: string): void;
555
764
  minimizePanel(id: string): void;
@@ -557,7 +766,18 @@ declare class WorkspaceClient {
557
766
  floatPanel(...args: Parameters<WindowActions['floatPanel']>): void;
558
767
  dockPanel(...args: Parameters<WindowActions['dockPanel']>): void;
559
768
  maximizePanel(id: string): void;
560
- bringToFront(id: string): void;
769
+ /**
770
+ * Activates the given panel regardless of its current state.
771
+ * For floating panels: raises z-index so the window appears on top.
772
+ * For docked panels: selects the tab within its leaf group.
773
+ */
774
+ focusPanel(id: string): void;
775
+ /** Returns `true` if a panel with this ID is currently open. */
776
+ isOpen(id: string): boolean;
777
+ /** Returns the IDs of all currently open panels. */
778
+ getOpenPanelIds(): string[];
779
+ saveLayout(): string;
780
+ loadLayout(json: string): void;
561
781
  setDirection(dir: 'ltr' | 'rtl'): void;
562
782
  publish(event: string, data: unknown): void;
563
783
  subscribe(event: string, callback: (data: unknown) => void): () => void;
@@ -834,7 +1054,7 @@ interface SidebarProps {
834
1054
  children?: React__default.ReactNode;
835
1055
  }
836
1056
  /**
837
- * Imperative handle exposed by <Sidebar ref={...}> via forwardRef.
1057
+ * Imperative handle exposed by `<Sidebar ref={...}>` via forwardRef.
838
1058
  * Allows external components (outside the sidebar tree) to control
839
1059
  * which tab is open without prop drilling.
840
1060
  */
@@ -852,4 +1072,4 @@ interface SidebarHandle {
852
1072
  */
853
1073
  declare const Sidebar: React__default.ForwardRefExoticComponent<SidebarProps & React__default.RefAttributes<SidebarHandle>>;
854
1074
 
855
- export { type CloseOptions, ConfirmationForm, type ConfirmationFormProps, type ContextMenuPredefinedMessage, type FloatingWindow, FormContainerContext, type FormContainerContract, FormContainerProvider, type LayoutGridNode, type LayoutLeafNode, type LayoutNode, LeftPanelRenderer, type MessageFormatter, type ModalOptions, ModalStackRenderer, type PanelActions, type PanelDefinition, type PanelInfo, type PanelInstance, type PanelInstanceId, PanelProvider, PanelRegistry, type PanelRegistryEntry, type PanelState, type PanelTitle, type PredefinedMessageKey, RightPanelRenderer, type SidePanelOptions, SidePanelRenderer, type SidePanelRendererProps, Sidebar, type SidebarHandle, type SidebarProps, type SidebarTab, type SplitOrientation, type StyleClasses, type WindowActions, WindowManager, WindowManagerProvider, type WindowState, WorkspaceClient, type WorkspaceClientConfig, defaultPredefinedMessages, formatLabel, useFormContainer, useFormatMessage, usePanelActions, usePanelContext, usePanelState, usePredefinedMessages, useStyleClasses, useWindowManagerActions, useWindowManagerState };
1075
+ export { type CloseOptions, ConfirmationForm, type ConfirmationFormProps, type ContextMenuPredefinedMessage, type FloatingWindow, FormContainerContext, type FormContainerContract, FormContainerProvider, type LayoutGridNode, type LayoutLeafNode, type LayoutNode, LeftPanelRenderer, type MessageFormatter, type ModalOptions, ModalStackRenderer, type PanelActions, type PanelDefinition, type PanelInfo, type PanelInstance, type PanelInstanceId, PanelProvider, PanelRegistry, PanelRegistryClass, type PanelRegistryEntry, type PanelState, type PanelTitle, type PredefinedMessageKey, RightPanelRenderer, type SidePanelOptions, SidePanelRenderer, type SidePanelRendererProps, Sidebar, type SidebarHandle, type SidebarProps, type SidebarTab, type SplitOrientation, type StyleClasses, type WindowActions, WindowManager, WindowManagerProvider, type WindowState, WorkspaceClient, type WorkspaceClientConfig, defaultPredefinedMessages, formatLabel, useFormContainer, useFormatMessage, usePanelActions, usePanelContext, usePanelState, usePredefinedMessages, useRegistry, useStyleClasses, useWindowManagerActions, useWindowManagerState };