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