react-zeugma 6.3.0 → 6.4.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.
@@ -1,4 +1,4 @@
1
- import { Dispatch, SetStateAction, RefObject, ReactNode } from 'react';
1
+ import { ReactNode, RefObject, Dispatch, SetStateAction } from 'react';
2
2
 
3
3
  type SplitDirection = 'row' | 'column';
4
4
  interface SplitNode {
@@ -16,14 +16,7 @@ interface PaneNode {
16
16
  locked?: boolean;
17
17
  tabsMetadata?: Record<string, Record<string, unknown>>;
18
18
  }
19
- interface WidgetNode {
20
- type: 'widget';
21
- id: string;
22
- locked?: boolean;
23
- metadata?: Record<string, unknown>;
24
- }
25
- type LeafNode = PaneNode | WidgetNode;
26
- type TreeNode = SplitNode | LeafNode;
19
+ type TreeNode = SplitNode | PaneNode;
27
20
  interface TabDetails {
28
21
  id: string;
29
22
  paneId: string;
@@ -44,86 +37,26 @@ interface UseZeugmaOptions {
44
37
  onFullscreenChange?: (paneId: string | null) => void;
45
38
  /** Whether the layout is locked. When locked, resizing, dragging, and dropping are disabled. */
46
39
  locked?: boolean;
47
- /** Minimum pixel distance that a user must drag a pane handle before dragging triggers. Defaults to 8. */
48
- dragActivationDistance?: number;
49
- /** Threshold value in pixels for snapping layout resizing handles to adjacent edges. Defaults to 8. */
50
- snapThreshold?: number;
51
- /** Minimum split percentage allowed when resizing split panes. Defaults to 5. */
52
- minSplitPercentage?: number;
53
- /** Maximum split percentage allowed when resizing split panes. Defaults to 95. */
54
- maxSplitPercentage?: number;
55
- /** Whether dragging a pane far enough outside the container triggers a drag-out/dismiss action. Defaults to false. */
56
- enableDragToDismiss?: boolean;
57
- /** The threshold in pixels beyond the container boundaries required to activate the drag-out/dismiss action. Defaults to 60. */
58
- dismissThreshold?: number;
59
- /** Callback triggered when a pane is removed from the dashboard layout tree. */
60
- onRemove?: (paneId: string) => void;
61
- /** Callback triggered when dragging starts for a pane. */
62
- onDragStart?: (activeId: string) => void;
63
- /** Callback triggered when dragging ends, providing details on target pane and drop action (split or move). */
64
- onDragEnd?: (activeId: string, overId: string | null, dropAction: {
65
- type: 'split' | 'move';
66
- direction?: SplitDirection;
67
- position?: 'top' | 'bottom' | 'left' | 'right' | 'center';
68
- } | null) => void;
69
- /** Callback triggered when the user starts dragging a resizing handle between split panes. */
70
- onResizeStart?: (currentNode: SplitNode) => void;
71
- /** Callback triggered continuously while the user is dragging a resizing handle. Passes the new split percentage. */
72
- onResize?: (currentNode: SplitNode, percentage: number) => void;
73
- /** Callback triggered when the user stops dragging a resizing handle. Passes the final split percentage. */
74
- onResizeEnd?: (currentNode: SplitNode, percentage: number) => void;
75
- /** Callback triggered when the drag-out/dismiss intent changes. */
76
- onDismissIntentChange?: (paneId: string | null) => void;
77
40
  }
78
- interface ZeugmaController {
41
+ interface ZeugmaState {
79
42
  /** The current active layout tree structure, or null if empty. */
80
43
  layout: TreeNode | null;
81
- /** Updates the layout tree. Also resets transient states like fullscreenPaneId. */
82
- setLayout: Dispatch<SetStateAction<TreeNode | null>>;
83
- /** @internal Raw layout setter used by DnD internals — does NOT reset transient states. */
84
- _internalSetLayout: Dispatch<SetStateAction<TreeNode | null>>;
85
44
  /** The ID of the pane currently zoomed to fullscreen, or null. */
86
45
  fullscreenPaneId: string | null;
87
- /** Programmatically sets the fullscreen pane ID. */
88
- setFullscreenPaneId: (paneId: string | null) => void;
89
46
  /** Whether the layout is globally locked. */
90
47
  locked: boolean;
48
+ }
49
+ interface ZeugmaStateSetters {
50
+ /** Updates the layout tree. Also resets transient states like fullscreenPaneId. */
51
+ setLayout: Dispatch<SetStateAction<TreeNode | null>>;
52
+ /** Programmatically sets the fullscreen pane ID. */
53
+ setFullscreenPaneId: (paneId: string | null) => void;
91
54
  /** Programmatically updates the global locked status. */
92
55
  setLocked: Dispatch<SetStateAction<boolean>>;
93
- /** The ID of the active dragged item (pane or tab). */
94
- activeId: string | null;
95
- /** The type of the active dragged item ('pane' | 'tab'). */
96
- activeType: 'pane' | 'tab' | null;
97
- /** The ID of the item with active dismiss intent, or null. */
98
- dismissIntentId: string | null;
99
- setActiveId: Dispatch<SetStateAction<string | null>>;
100
- setActiveType: Dispatch<SetStateAction<'pane' | 'tab' | null>>;
101
- setDismissIntentId: Dispatch<SetStateAction<string | null>>;
102
- containerRef: RefObject<HTMLElement | null>;
103
- setContainerRef: (element: HTMLElement | null) => void;
104
- layoutBeforeDrag: TreeNode | null;
105
- setLayoutBeforeDrag: Dispatch<SetStateAction<TreeNode | null>>;
106
- dragActivationDistance: number;
107
- snapThreshold: number;
108
- minSplitPercentage: number;
109
- maxSplitPercentage: number;
110
- enableDragToDismiss: boolean;
111
- dismissThreshold: number;
112
- onRemove?: (paneId: string) => void;
113
- onDragStart?: (activeId: string) => void;
114
- onDragEnd?: (activeId: string, overId: string | null, dropAction: {
115
- type: 'split' | 'move';
116
- direction?: SplitDirection;
117
- position?: 'top' | 'bottom' | 'left' | 'right' | 'center';
118
- } | null) => void;
119
- onResizeStart?: (currentNode: SplitNode) => void;
120
- onResize?: (currentNode: SplitNode, percentage: number) => void;
121
- onResizeEnd?: (currentNode: SplitNode, percentage: number) => void;
122
- onDismissIntentChange?: (paneId: string | null) => void;
56
+ }
57
+ interface ZeugmaActions {
123
58
  /** Removes the specified pane or widget from the layout tree and collapses its parent split. */
124
59
  removePane: (paneId: string) => void;
125
- /** Appends/inserts a widget at the bottom-rightmost leaf of the layout tree. */
126
- addWidget: (widgetId: string, metadata?: Record<string, unknown>) => void;
127
60
  /** Appends a tab into a target pane node, or splits/creates a new pane if no target pane ID is provided. */
128
61
  addTab: (tabId: string, targetPaneId?: string, metadata?: Record<string, unknown>) => void;
129
62
  /** Stable callback to update metadata for a specific tab or widget. */
@@ -142,12 +75,34 @@ interface ZeugmaController {
142
75
  updateSplitPercentage: (currentNode: SplitNode, percentage: number) => void;
143
76
  /** Moves/reorders a tab relative to another target tab. */
144
77
  moveTab: (draggedTabId: string, targetTabId: string, position?: 'before' | 'after') => void;
78
+ }
79
+ interface ZeugmaQueries {
145
80
  /** Find a PaneNode or WidgetNode by its ID in the layout tree. */
146
- findPaneById: (paneId: string) => LeafNode | null;
81
+ findPaneById: (paneId: string) => PaneNode | null;
147
82
  /** Find the PaneNode containing the given tab ID in the layout tree. */
148
83
  findPaneContainingTab: (tabId: string) => PaneNode | null;
149
84
  /** Find the details of a tab by its ID in the layout tree. */
150
85
  findTabById: (tabId: string) => TabDetails | null;
86
+ /** Get the metadata for a specific tab by its ID. */
87
+ getTabMetadata: (tabId: string) => Record<string, unknown> | undefined;
88
+ /** Get the metadata of the active tab in a specific pane by the pane ID. */
89
+ getActiveTabMetadata: (paneId: string) => Record<string, unknown> | undefined;
90
+ }
91
+ interface ZeugmaController extends ZeugmaState, ZeugmaStateSetters, ZeugmaActions, ZeugmaQueries {
92
+ }
93
+ /** @internal Internal controller containing transient drag-and-drop orchestration state. */
94
+ interface ZeugmaControllerInternal extends ZeugmaController {
95
+ _internalSetLayout: Dispatch<SetStateAction<TreeNode | null>>;
96
+ activeId: string | null;
97
+ setActiveId: Dispatch<SetStateAction<string | null>>;
98
+ activeType: 'pane' | 'tab' | null;
99
+ setActiveType: Dispatch<SetStateAction<'pane' | 'tab' | null>>;
100
+ dismissIntentId: string | null;
101
+ setDismissIntentId: Dispatch<SetStateAction<string | null>>;
102
+ containerRef: RefObject<HTMLElement | null>;
103
+ setContainerRef: (element: HTMLElement | null) => void;
104
+ layoutBeforeDrag: TreeNode | null;
105
+ setLayoutBeforeDrag: Dispatch<SetStateAction<TreeNode | null>>;
151
106
  }
152
107
  interface ZeugmaClassNames {
153
108
  /** CSS class applied to the root dashboard container. */
@@ -172,48 +127,93 @@ interface ZeugmaClassNames {
172
127
  dashboardLocked?: string;
173
128
  /** CSS class applied to drop zone indicator when hovering over a locked pane. */
174
129
  lockedPreview?: string;
175
- /** CSS class applied to tab drop preview splitter line. */
130
+ /** CSS class applied to drop zone indicator when hovering over a locked pane. */
176
131
  tabDropPreview?: string;
177
132
  /** CSS class applied to the separator line between tabs. */
178
133
  tabSeparator?: string;
134
+ /** CSS class applied to the wrapper element for a tab's contents. */
135
+ tabContentWrapper?: string;
136
+ /** CSS class applied to the tabs container div. */
137
+ tabsContainer?: string;
138
+ /** CSS class applied to individual tabs (as static string or dynamic callback). */
139
+ tab?: string | ((tabId: string) => string);
140
+ /** CSS class applied to the pane inner container (containing header and content). */
141
+ paneContainer?: string;
142
+ /** CSS class applied to the pane header bar. */
143
+ paneHeader?: string;
144
+ /** CSS class applied to the pane controls wrapper. */
145
+ paneControls?: string;
146
+ /** CSS class applied to the pane buttons (maximize/close). */
147
+ paneButton?: string;
148
+ /** CSS class applied to the default tab close button. */
149
+ tabCloseButton?: string;
150
+ /** CSS class applied to the drag handle. */
151
+ dragHandle?: string;
179
152
  }
180
- interface ZeugmaProps extends ZeugmaController {
181
- /** Render function mapping unique pane IDs to React elements. Usually renders a <Pane> wrapper. */
182
- renderPane: (paneId: string) => ReactNode;
153
+ interface DragOverlayActiveItem {
154
+ type: 'tab' | 'pane';
155
+ id: string;
156
+ isDismissing: boolean;
157
+ }
158
+ interface ZeugmaProps {
159
+ /** The Zeugma Controller returned by useZeugma() */
160
+ controller: ZeugmaController;
183
161
  /** Custom overlay renderer function used to customize the cursor-following drag preview for an active pane or tab. */
184
- renderDragOverlay?: (activeId: string, type: 'pane' | 'tab') => ReactNode;
162
+ renderDragOverlay?: (active: DragOverlayActiveItem) => ReactNode;
185
163
  /** Optional CSS class name mapping overrides for custom styles of components like panes, drop previews, overlays, etc. */
186
164
  classNames?: ZeugmaClassNames;
187
- /** Render function mapping tab IDs to React elements. Used to render tab widgets inside portals. */
188
- renderWidget?: (tabId: string) => ReactNode;
189
- /** Child nodes nested inside the Zeugma context, usually containing a <PaneTree> or similar layout viewer. */
190
- children: ReactNode;
165
+ /** Render function mapping unique pane nodes to React elements. Usually renders a <Pane> wrapper. */
166
+ renderPane: (paneId: string) => ReactNode;
167
+ /** Size/thickness of the split handle resizer bars in pixels. */
168
+ resizerSize?: number;
169
+ /** Minimum pixel distance that a user must drag a pane handle before dragging triggers. Defaults to 8. */
170
+ dragActivationDistance?: number;
171
+ /** Threshold value in pixels for snapping layout resizing handles to adjacent edges. Defaults to 8. */
172
+ snapThreshold?: number;
173
+ /** Minimum split percentage allowed when resizing split panes. Defaults to 5. */
174
+ minSplitPercentage?: number;
175
+ /** Maximum split percentage allowed when resizing split panes. Defaults to 95. */
176
+ maxSplitPercentage?: number;
177
+ /** Whether dragging a pane far enough outside the container triggers a drag-out/dismiss action. Defaults to false. */
178
+ enableDragToDismiss?: boolean;
179
+ /** The threshold in pixels beyond the container boundaries required to activate the drag-out/dismiss action. Defaults to 60. */
180
+ dismissThreshold?: number;
181
+ /** Callback triggered when a pane is removed from the dashboard layout tree. */
182
+ onRemove?: (paneId: string) => void;
183
+ /** Callback triggered when dragging starts for a pane. */
184
+ onDragStart?: (activeId: string) => void;
185
+ /** Callback triggered when dragging ends, providing details on target pane and drop action (split or move). */
186
+ onDragEnd?: (activeId: string, overId: string | null, dropAction: {
187
+ type: 'split' | 'move';
188
+ direction?: SplitDirection;
189
+ position?: 'top' | 'bottom' | 'left' | 'right' | 'center';
190
+ } | null) => void;
191
+ /** Callback triggered when the user starts dragging a resizing handle between split panes. */
192
+ onResizeStart?: (currentNode: SplitNode) => void;
193
+ /** Callback triggered continuously while the user is dragging a resizing handle. Passes the new split percentage. */
194
+ onResize?: (currentNode: SplitNode, percentage: number) => void;
195
+ /** Callback triggered when the user stops dragging a resizing handle. Passes the final split percentage. */
196
+ onResizeEnd?: (currentNode: SplitNode, percentage: number) => void;
197
+ /** Callback triggered when the drag-out/dismiss intent changes. */
198
+ onDismissIntentChange?: (paneId: string | null) => void;
191
199
  }
192
200
  /**
193
201
  * State context — holds reactive values that change during runtime.
194
202
  * All consumers of this context will re-render when any of these values change.
195
203
  */
196
- interface ZeugmaStateValue {
197
- /** The current active layout tree structure, or null if empty. */
198
- layout: TreeNode | null;
204
+ /**
205
+ * State context holds reactive values that change during runtime.
206
+ * All consumers of this context will re-render when any of these values change.
207
+ */
208
+ interface ZeugmaStateValue extends ZeugmaState, ZeugmaQueries {
199
209
  /** Callback to update the layout tree. */
200
210
  setLayout: Dispatch<SetStateAction<TreeNode | null>>;
201
- /** Renders the inner content of a pane given its unique ID. */
202
- renderPane: (paneId: string) => ReactNode;
203
- /** The ID of the pane currently zoomed to fullscreen, or null. */
204
- fullscreenPaneId: string | null;
205
- /** Normalized or overridden CSS classes for custom layout styling. */
206
- classNames: ZeugmaClassNames;
207
- /** Whether the layout is globally locked. */
208
- locked: boolean;
209
211
  /** Programmatically updates the global locked status. */
210
212
  setLocked: Dispatch<SetStateAction<boolean>>;
211
- /** Find a PaneNode or WidgetNode by its ID in the layout tree. */
212
- findPaneById: (paneId: string) => LeafNode | null;
213
- /** Find the PaneNode containing the given tab ID in the layout tree. */
214
- findPaneContainingTab: (tabId: string) => PaneNode | null;
215
- /** Find the details of a tab by its ID in the layout tree. */
216
- findTabById: (tabId: string) => TabDetails | null;
213
+ /** Normalized or overridden CSS classes for custom layout styling. */
214
+ classNames: ZeugmaClassNames;
215
+ /** Render function mapping unique pane nodes to React elements. */
216
+ renderPane: (paneId: string) => ReactNode;
217
217
  /** The ID of the active dragged item (pane or tab). */
218
218
  activeId: string | null;
219
219
  /** The type of the active dragged item ('pane' | 'tab'). */
@@ -240,38 +240,16 @@ interface ZeugmaDragStateValue {
240
240
  * Actions context — holds stable dispatch functions with permanent identity.
241
241
  * Consumers of only this context will never re-render from layout/drag state changes.
242
242
  */
243
- interface ZeugmaActionsValue {
244
- /** Removes the specified pane or widget from the layout tree and collapses its parent split. */
245
- removePane: (paneId: string) => void;
246
- /** Appends/inserts a widget at the bottom-rightmost leaf of the layout tree. */
247
- addWidget: (widgetId: string, metadata?: Record<string, unknown>) => void;
248
- /** Appends a tab into a target pane node, or splits/creates a new pane if no target pane ID is provided. */
249
- addTab: (tabId: string, targetPaneId?: string, metadata?: Record<string, unknown>) => void;
250
- /** Stable callback to update metadata for a specific tab or widget. */
251
- updateMetadata: (id: string, updater: (current: Record<string, unknown> | undefined) => Record<string, unknown> | undefined) => void;
252
- /** Stable callback to update the locked status of a specific pane in the layout tree. */
253
- updatePaneLock: (paneId: string, locked: boolean) => void;
254
- /** Stable callback to activate a tab within a pane. */
255
- selectTab: (paneId: string, tabId: string) => void;
256
- /** Stable callback to merge a dragged tab/pane into another pane's tab list. */
257
- mergeTab: (draggedTabId: string, targetPaneId: string) => void;
258
- /** Stable callback to remove/close a specific tab from the layout. */
259
- removeTab: (tabId: string) => void;
243
+ interface ZeugmaActionsValue extends ZeugmaActions {
260
244
  /** Programmatically sets the fullscreen pane ID. */
261
245
  setFullscreenPaneId: (paneId: string | null) => void;
262
246
  /** Programmatically updates the global locked status. */
263
247
  setLocked: Dispatch<SetStateAction<boolean>>;
264
- /** Splits an existing pane and adds a new one. */
265
- splitPane: (targetId: string, direction: SplitDirection, splitType: 'left' | 'right' | 'top' | 'bottom', paneToAdd: string) => void;
266
- /** Updates the split percentage of a split node. */
267
- updateSplitPercentage: (currentNode: SplitNode, percentage: number) => void;
268
- /** Moves/reorders a tab relative to another target tab. */
269
- moveTab: (draggedTabId: string, targetTabId: string, position?: 'before' | 'after') => void;
270
- }
271
- interface ZeugmaContextValue extends ZeugmaStateValue, ZeugmaActionsValue {
272
248
  }
273
249
  interface PortalRegistryValue {
274
250
  registerPortalTarget: (tabId: string, el: HTMLDivElement | null) => void;
251
+ registerRenderCallback: (tabId: string, render: (tab: TabDetails) => ReactNode) => void;
252
+ renderCallbacksRef: RefObject<Record<string, (tab: TabDetails) => ReactNode>>;
275
253
  }
276
254
 
277
- export type { LeafNode as L, PortalRegistryValue as P, SplitDirection as S, TreeNode as T, UseZeugmaOptions as U, WidgetNode as W, ZeugmaActionsValue as Z, ZeugmaDragStateValue as a, ZeugmaStateValue as b, ZeugmaController as c, ZeugmaContextValue as d, ZeugmaProps as e, SplitNode as f, PaneNode as g, TabDetails as h, ZeugmaClassNames as i };
255
+ export type { DragOverlayActiveItem as D, PortalRegistryValue as P, SplitDirection as S, TreeNode as T, UseZeugmaOptions as U, ZeugmaActionsValue as Z, ZeugmaDragStateValue as a, ZeugmaStateValue as b, ZeugmaController as c, ZeugmaProps as d, SplitNode as e, TabDetails as f, PaneNode as g, ZeugmaActions as h, ZeugmaClassNames as i, ZeugmaControllerInternal as j, ZeugmaQueries as k, ZeugmaState as l, ZeugmaStateSetters as m };
@@ -1,4 +1,4 @@
1
- import { Dispatch, SetStateAction, RefObject, ReactNode } from 'react';
1
+ import { ReactNode, RefObject, Dispatch, SetStateAction } from 'react';
2
2
 
3
3
  type SplitDirection = 'row' | 'column';
4
4
  interface SplitNode {
@@ -16,14 +16,7 @@ interface PaneNode {
16
16
  locked?: boolean;
17
17
  tabsMetadata?: Record<string, Record<string, unknown>>;
18
18
  }
19
- interface WidgetNode {
20
- type: 'widget';
21
- id: string;
22
- locked?: boolean;
23
- metadata?: Record<string, unknown>;
24
- }
25
- type LeafNode = PaneNode | WidgetNode;
26
- type TreeNode = SplitNode | LeafNode;
19
+ type TreeNode = SplitNode | PaneNode;
27
20
  interface TabDetails {
28
21
  id: string;
29
22
  paneId: string;
@@ -44,86 +37,26 @@ interface UseZeugmaOptions {
44
37
  onFullscreenChange?: (paneId: string | null) => void;
45
38
  /** Whether the layout is locked. When locked, resizing, dragging, and dropping are disabled. */
46
39
  locked?: boolean;
47
- /** Minimum pixel distance that a user must drag a pane handle before dragging triggers. Defaults to 8. */
48
- dragActivationDistance?: number;
49
- /** Threshold value in pixels for snapping layout resizing handles to adjacent edges. Defaults to 8. */
50
- snapThreshold?: number;
51
- /** Minimum split percentage allowed when resizing split panes. Defaults to 5. */
52
- minSplitPercentage?: number;
53
- /** Maximum split percentage allowed when resizing split panes. Defaults to 95. */
54
- maxSplitPercentage?: number;
55
- /** Whether dragging a pane far enough outside the container triggers a drag-out/dismiss action. Defaults to false. */
56
- enableDragToDismiss?: boolean;
57
- /** The threshold in pixels beyond the container boundaries required to activate the drag-out/dismiss action. Defaults to 60. */
58
- dismissThreshold?: number;
59
- /** Callback triggered when a pane is removed from the dashboard layout tree. */
60
- onRemove?: (paneId: string) => void;
61
- /** Callback triggered when dragging starts for a pane. */
62
- onDragStart?: (activeId: string) => void;
63
- /** Callback triggered when dragging ends, providing details on target pane and drop action (split or move). */
64
- onDragEnd?: (activeId: string, overId: string | null, dropAction: {
65
- type: 'split' | 'move';
66
- direction?: SplitDirection;
67
- position?: 'top' | 'bottom' | 'left' | 'right' | 'center';
68
- } | null) => void;
69
- /** Callback triggered when the user starts dragging a resizing handle between split panes. */
70
- onResizeStart?: (currentNode: SplitNode) => void;
71
- /** Callback triggered continuously while the user is dragging a resizing handle. Passes the new split percentage. */
72
- onResize?: (currentNode: SplitNode, percentage: number) => void;
73
- /** Callback triggered when the user stops dragging a resizing handle. Passes the final split percentage. */
74
- onResizeEnd?: (currentNode: SplitNode, percentage: number) => void;
75
- /** Callback triggered when the drag-out/dismiss intent changes. */
76
- onDismissIntentChange?: (paneId: string | null) => void;
77
40
  }
78
- interface ZeugmaController {
41
+ interface ZeugmaState {
79
42
  /** The current active layout tree structure, or null if empty. */
80
43
  layout: TreeNode | null;
81
- /** Updates the layout tree. Also resets transient states like fullscreenPaneId. */
82
- setLayout: Dispatch<SetStateAction<TreeNode | null>>;
83
- /** @internal Raw layout setter used by DnD internals — does NOT reset transient states. */
84
- _internalSetLayout: Dispatch<SetStateAction<TreeNode | null>>;
85
44
  /** The ID of the pane currently zoomed to fullscreen, or null. */
86
45
  fullscreenPaneId: string | null;
87
- /** Programmatically sets the fullscreen pane ID. */
88
- setFullscreenPaneId: (paneId: string | null) => void;
89
46
  /** Whether the layout is globally locked. */
90
47
  locked: boolean;
48
+ }
49
+ interface ZeugmaStateSetters {
50
+ /** Updates the layout tree. Also resets transient states like fullscreenPaneId. */
51
+ setLayout: Dispatch<SetStateAction<TreeNode | null>>;
52
+ /** Programmatically sets the fullscreen pane ID. */
53
+ setFullscreenPaneId: (paneId: string | null) => void;
91
54
  /** Programmatically updates the global locked status. */
92
55
  setLocked: Dispatch<SetStateAction<boolean>>;
93
- /** The ID of the active dragged item (pane or tab). */
94
- activeId: string | null;
95
- /** The type of the active dragged item ('pane' | 'tab'). */
96
- activeType: 'pane' | 'tab' | null;
97
- /** The ID of the item with active dismiss intent, or null. */
98
- dismissIntentId: string | null;
99
- setActiveId: Dispatch<SetStateAction<string | null>>;
100
- setActiveType: Dispatch<SetStateAction<'pane' | 'tab' | null>>;
101
- setDismissIntentId: Dispatch<SetStateAction<string | null>>;
102
- containerRef: RefObject<HTMLElement | null>;
103
- setContainerRef: (element: HTMLElement | null) => void;
104
- layoutBeforeDrag: TreeNode | null;
105
- setLayoutBeforeDrag: Dispatch<SetStateAction<TreeNode | null>>;
106
- dragActivationDistance: number;
107
- snapThreshold: number;
108
- minSplitPercentage: number;
109
- maxSplitPercentage: number;
110
- enableDragToDismiss: boolean;
111
- dismissThreshold: number;
112
- onRemove?: (paneId: string) => void;
113
- onDragStart?: (activeId: string) => void;
114
- onDragEnd?: (activeId: string, overId: string | null, dropAction: {
115
- type: 'split' | 'move';
116
- direction?: SplitDirection;
117
- position?: 'top' | 'bottom' | 'left' | 'right' | 'center';
118
- } | null) => void;
119
- onResizeStart?: (currentNode: SplitNode) => void;
120
- onResize?: (currentNode: SplitNode, percentage: number) => void;
121
- onResizeEnd?: (currentNode: SplitNode, percentage: number) => void;
122
- onDismissIntentChange?: (paneId: string | null) => void;
56
+ }
57
+ interface ZeugmaActions {
123
58
  /** Removes the specified pane or widget from the layout tree and collapses its parent split. */
124
59
  removePane: (paneId: string) => void;
125
- /** Appends/inserts a widget at the bottom-rightmost leaf of the layout tree. */
126
- addWidget: (widgetId: string, metadata?: Record<string, unknown>) => void;
127
60
  /** Appends a tab into a target pane node, or splits/creates a new pane if no target pane ID is provided. */
128
61
  addTab: (tabId: string, targetPaneId?: string, metadata?: Record<string, unknown>) => void;
129
62
  /** Stable callback to update metadata for a specific tab or widget. */
@@ -142,12 +75,34 @@ interface ZeugmaController {
142
75
  updateSplitPercentage: (currentNode: SplitNode, percentage: number) => void;
143
76
  /** Moves/reorders a tab relative to another target tab. */
144
77
  moveTab: (draggedTabId: string, targetTabId: string, position?: 'before' | 'after') => void;
78
+ }
79
+ interface ZeugmaQueries {
145
80
  /** Find a PaneNode or WidgetNode by its ID in the layout tree. */
146
- findPaneById: (paneId: string) => LeafNode | null;
81
+ findPaneById: (paneId: string) => PaneNode | null;
147
82
  /** Find the PaneNode containing the given tab ID in the layout tree. */
148
83
  findPaneContainingTab: (tabId: string) => PaneNode | null;
149
84
  /** Find the details of a tab by its ID in the layout tree. */
150
85
  findTabById: (tabId: string) => TabDetails | null;
86
+ /** Get the metadata for a specific tab by its ID. */
87
+ getTabMetadata: (tabId: string) => Record<string, unknown> | undefined;
88
+ /** Get the metadata of the active tab in a specific pane by the pane ID. */
89
+ getActiveTabMetadata: (paneId: string) => Record<string, unknown> | undefined;
90
+ }
91
+ interface ZeugmaController extends ZeugmaState, ZeugmaStateSetters, ZeugmaActions, ZeugmaQueries {
92
+ }
93
+ /** @internal Internal controller containing transient drag-and-drop orchestration state. */
94
+ interface ZeugmaControllerInternal extends ZeugmaController {
95
+ _internalSetLayout: Dispatch<SetStateAction<TreeNode | null>>;
96
+ activeId: string | null;
97
+ setActiveId: Dispatch<SetStateAction<string | null>>;
98
+ activeType: 'pane' | 'tab' | null;
99
+ setActiveType: Dispatch<SetStateAction<'pane' | 'tab' | null>>;
100
+ dismissIntentId: string | null;
101
+ setDismissIntentId: Dispatch<SetStateAction<string | null>>;
102
+ containerRef: RefObject<HTMLElement | null>;
103
+ setContainerRef: (element: HTMLElement | null) => void;
104
+ layoutBeforeDrag: TreeNode | null;
105
+ setLayoutBeforeDrag: Dispatch<SetStateAction<TreeNode | null>>;
151
106
  }
152
107
  interface ZeugmaClassNames {
153
108
  /** CSS class applied to the root dashboard container. */
@@ -172,48 +127,93 @@ interface ZeugmaClassNames {
172
127
  dashboardLocked?: string;
173
128
  /** CSS class applied to drop zone indicator when hovering over a locked pane. */
174
129
  lockedPreview?: string;
175
- /** CSS class applied to tab drop preview splitter line. */
130
+ /** CSS class applied to drop zone indicator when hovering over a locked pane. */
176
131
  tabDropPreview?: string;
177
132
  /** CSS class applied to the separator line between tabs. */
178
133
  tabSeparator?: string;
134
+ /** CSS class applied to the wrapper element for a tab's contents. */
135
+ tabContentWrapper?: string;
136
+ /** CSS class applied to the tabs container div. */
137
+ tabsContainer?: string;
138
+ /** CSS class applied to individual tabs (as static string or dynamic callback). */
139
+ tab?: string | ((tabId: string) => string);
140
+ /** CSS class applied to the pane inner container (containing header and content). */
141
+ paneContainer?: string;
142
+ /** CSS class applied to the pane header bar. */
143
+ paneHeader?: string;
144
+ /** CSS class applied to the pane controls wrapper. */
145
+ paneControls?: string;
146
+ /** CSS class applied to the pane buttons (maximize/close). */
147
+ paneButton?: string;
148
+ /** CSS class applied to the default tab close button. */
149
+ tabCloseButton?: string;
150
+ /** CSS class applied to the drag handle. */
151
+ dragHandle?: string;
179
152
  }
180
- interface ZeugmaProps extends ZeugmaController {
181
- /** Render function mapping unique pane IDs to React elements. Usually renders a <Pane> wrapper. */
182
- renderPane: (paneId: string) => ReactNode;
153
+ interface DragOverlayActiveItem {
154
+ type: 'tab' | 'pane';
155
+ id: string;
156
+ isDismissing: boolean;
157
+ }
158
+ interface ZeugmaProps {
159
+ /** The Zeugma Controller returned by useZeugma() */
160
+ controller: ZeugmaController;
183
161
  /** Custom overlay renderer function used to customize the cursor-following drag preview for an active pane or tab. */
184
- renderDragOverlay?: (activeId: string, type: 'pane' | 'tab') => ReactNode;
162
+ renderDragOverlay?: (active: DragOverlayActiveItem) => ReactNode;
185
163
  /** Optional CSS class name mapping overrides for custom styles of components like panes, drop previews, overlays, etc. */
186
164
  classNames?: ZeugmaClassNames;
187
- /** Render function mapping tab IDs to React elements. Used to render tab widgets inside portals. */
188
- renderWidget?: (tabId: string) => ReactNode;
189
- /** Child nodes nested inside the Zeugma context, usually containing a <PaneTree> or similar layout viewer. */
190
- children: ReactNode;
165
+ /** Render function mapping unique pane nodes to React elements. Usually renders a <Pane> wrapper. */
166
+ renderPane: (paneId: string) => ReactNode;
167
+ /** Size/thickness of the split handle resizer bars in pixels. */
168
+ resizerSize?: number;
169
+ /** Minimum pixel distance that a user must drag a pane handle before dragging triggers. Defaults to 8. */
170
+ dragActivationDistance?: number;
171
+ /** Threshold value in pixels for snapping layout resizing handles to adjacent edges. Defaults to 8. */
172
+ snapThreshold?: number;
173
+ /** Minimum split percentage allowed when resizing split panes. Defaults to 5. */
174
+ minSplitPercentage?: number;
175
+ /** Maximum split percentage allowed when resizing split panes. Defaults to 95. */
176
+ maxSplitPercentage?: number;
177
+ /** Whether dragging a pane far enough outside the container triggers a drag-out/dismiss action. Defaults to false. */
178
+ enableDragToDismiss?: boolean;
179
+ /** The threshold in pixels beyond the container boundaries required to activate the drag-out/dismiss action. Defaults to 60. */
180
+ dismissThreshold?: number;
181
+ /** Callback triggered when a pane is removed from the dashboard layout tree. */
182
+ onRemove?: (paneId: string) => void;
183
+ /** Callback triggered when dragging starts for a pane. */
184
+ onDragStart?: (activeId: string) => void;
185
+ /** Callback triggered when dragging ends, providing details on target pane and drop action (split or move). */
186
+ onDragEnd?: (activeId: string, overId: string | null, dropAction: {
187
+ type: 'split' | 'move';
188
+ direction?: SplitDirection;
189
+ position?: 'top' | 'bottom' | 'left' | 'right' | 'center';
190
+ } | null) => void;
191
+ /** Callback triggered when the user starts dragging a resizing handle between split panes. */
192
+ onResizeStart?: (currentNode: SplitNode) => void;
193
+ /** Callback triggered continuously while the user is dragging a resizing handle. Passes the new split percentage. */
194
+ onResize?: (currentNode: SplitNode, percentage: number) => void;
195
+ /** Callback triggered when the user stops dragging a resizing handle. Passes the final split percentage. */
196
+ onResizeEnd?: (currentNode: SplitNode, percentage: number) => void;
197
+ /** Callback triggered when the drag-out/dismiss intent changes. */
198
+ onDismissIntentChange?: (paneId: string | null) => void;
191
199
  }
192
200
  /**
193
201
  * State context — holds reactive values that change during runtime.
194
202
  * All consumers of this context will re-render when any of these values change.
195
203
  */
196
- interface ZeugmaStateValue {
197
- /** The current active layout tree structure, or null if empty. */
198
- layout: TreeNode | null;
204
+ /**
205
+ * State context holds reactive values that change during runtime.
206
+ * All consumers of this context will re-render when any of these values change.
207
+ */
208
+ interface ZeugmaStateValue extends ZeugmaState, ZeugmaQueries {
199
209
  /** Callback to update the layout tree. */
200
210
  setLayout: Dispatch<SetStateAction<TreeNode | null>>;
201
- /** Renders the inner content of a pane given its unique ID. */
202
- renderPane: (paneId: string) => ReactNode;
203
- /** The ID of the pane currently zoomed to fullscreen, or null. */
204
- fullscreenPaneId: string | null;
205
- /** Normalized or overridden CSS classes for custom layout styling. */
206
- classNames: ZeugmaClassNames;
207
- /** Whether the layout is globally locked. */
208
- locked: boolean;
209
211
  /** Programmatically updates the global locked status. */
210
212
  setLocked: Dispatch<SetStateAction<boolean>>;
211
- /** Find a PaneNode or WidgetNode by its ID in the layout tree. */
212
- findPaneById: (paneId: string) => LeafNode | null;
213
- /** Find the PaneNode containing the given tab ID in the layout tree. */
214
- findPaneContainingTab: (tabId: string) => PaneNode | null;
215
- /** Find the details of a tab by its ID in the layout tree. */
216
- findTabById: (tabId: string) => TabDetails | null;
213
+ /** Normalized or overridden CSS classes for custom layout styling. */
214
+ classNames: ZeugmaClassNames;
215
+ /** Render function mapping unique pane nodes to React elements. */
216
+ renderPane: (paneId: string) => ReactNode;
217
217
  /** The ID of the active dragged item (pane or tab). */
218
218
  activeId: string | null;
219
219
  /** The type of the active dragged item ('pane' | 'tab'). */
@@ -240,38 +240,16 @@ interface ZeugmaDragStateValue {
240
240
  * Actions context — holds stable dispatch functions with permanent identity.
241
241
  * Consumers of only this context will never re-render from layout/drag state changes.
242
242
  */
243
- interface ZeugmaActionsValue {
244
- /** Removes the specified pane or widget from the layout tree and collapses its parent split. */
245
- removePane: (paneId: string) => void;
246
- /** Appends/inserts a widget at the bottom-rightmost leaf of the layout tree. */
247
- addWidget: (widgetId: string, metadata?: Record<string, unknown>) => void;
248
- /** Appends a tab into a target pane node, or splits/creates a new pane if no target pane ID is provided. */
249
- addTab: (tabId: string, targetPaneId?: string, metadata?: Record<string, unknown>) => void;
250
- /** Stable callback to update metadata for a specific tab or widget. */
251
- updateMetadata: (id: string, updater: (current: Record<string, unknown> | undefined) => Record<string, unknown> | undefined) => void;
252
- /** Stable callback to update the locked status of a specific pane in the layout tree. */
253
- updatePaneLock: (paneId: string, locked: boolean) => void;
254
- /** Stable callback to activate a tab within a pane. */
255
- selectTab: (paneId: string, tabId: string) => void;
256
- /** Stable callback to merge a dragged tab/pane into another pane's tab list. */
257
- mergeTab: (draggedTabId: string, targetPaneId: string) => void;
258
- /** Stable callback to remove/close a specific tab from the layout. */
259
- removeTab: (tabId: string) => void;
243
+ interface ZeugmaActionsValue extends ZeugmaActions {
260
244
  /** Programmatically sets the fullscreen pane ID. */
261
245
  setFullscreenPaneId: (paneId: string | null) => void;
262
246
  /** Programmatically updates the global locked status. */
263
247
  setLocked: Dispatch<SetStateAction<boolean>>;
264
- /** Splits an existing pane and adds a new one. */
265
- splitPane: (targetId: string, direction: SplitDirection, splitType: 'left' | 'right' | 'top' | 'bottom', paneToAdd: string) => void;
266
- /** Updates the split percentage of a split node. */
267
- updateSplitPercentage: (currentNode: SplitNode, percentage: number) => void;
268
- /** Moves/reorders a tab relative to another target tab. */
269
- moveTab: (draggedTabId: string, targetTabId: string, position?: 'before' | 'after') => void;
270
- }
271
- interface ZeugmaContextValue extends ZeugmaStateValue, ZeugmaActionsValue {
272
248
  }
273
249
  interface PortalRegistryValue {
274
250
  registerPortalTarget: (tabId: string, el: HTMLDivElement | null) => void;
251
+ registerRenderCallback: (tabId: string, render: (tab: TabDetails) => ReactNode) => void;
252
+ renderCallbacksRef: RefObject<Record<string, (tab: TabDetails) => ReactNode>>;
275
253
  }
276
254
 
277
- export type { LeafNode as L, PortalRegistryValue as P, SplitDirection as S, TreeNode as T, UseZeugmaOptions as U, WidgetNode as W, ZeugmaActionsValue as Z, ZeugmaDragStateValue as a, ZeugmaStateValue as b, ZeugmaController as c, ZeugmaContextValue as d, ZeugmaProps as e, SplitNode as f, PaneNode as g, TabDetails as h, ZeugmaClassNames as i };
255
+ export type { DragOverlayActiveItem as D, PortalRegistryValue as P, SplitDirection as S, TreeNode as T, UseZeugmaOptions as U, ZeugmaActionsValue as Z, ZeugmaDragStateValue as a, ZeugmaStateValue as b, ZeugmaController as c, ZeugmaProps as d, SplitNode as e, TabDetails as f, PaneNode as g, ZeugmaActions as h, ZeugmaClassNames as i, ZeugmaControllerInternal as j, ZeugmaQueries as k, ZeugmaState as l, ZeugmaStateSetters as m };