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.
- package/README.md +133 -157
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +69 -52
- package/dist/index.d.ts +69 -52
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/{types-Diujkc-H.d.cts → types-B9LrYapR.d.cts} +109 -131
- package/dist/{types-Diujkc-H.d.ts → types-B9LrYapR.d.ts} +109 -131
- package/dist/utils.cjs +1 -1
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +17 -10
- package/dist/utils.d.ts +17 -10
- package/dist/utils.js +1 -1
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
94
|
-
|
|
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) =>
|
|
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
|
|
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
|
|
181
|
-
|
|
182
|
-
|
|
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?: (
|
|
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
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
|
|
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
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
-
/**
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
|
|
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 {
|
|
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 {
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
94
|
-
|
|
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) =>
|
|
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
|
|
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
|
|
181
|
-
|
|
182
|
-
|
|
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?: (
|
|
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
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
|
|
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
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
-
/**
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
|
|
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 {
|
|
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 };
|