react-zeugma 6.0.4 → 6.1.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 +365 -81
- package/dist/{types-Kf7gkm94.d.cts → dom-DrgwRi7G.d.cts} +10 -1
- package/dist/{types-Kf7gkm94.d.ts → dom-DrgwRi7G.d.ts} +10 -1
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -28
- package/dist/index.d.ts +3 -28
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/utils.cjs +1 -1
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +2 -1
- package/dist/utils.d.ts +2 -1
- package/dist/utils.js +1 -1
- package/dist/utils.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -113,24 +113,26 @@ The context provider that sets up the drag-and-drop state machine, monitors acti
|
|
|
113
113
|
|
|
114
114
|
A custom state hook that initializes and manages the recursive layout tree and handles drag-and-drop actions.
|
|
115
115
|
|
|
116
|
-
| Option | Type
|
|
117
|
-
| ------------------------ |
|
|
118
|
-
| `initialLayout` | `TreeNode \| null`
|
|
119
|
-
| `layout` | `TreeNode \| null`
|
|
120
|
-
| `
|
|
121
|
-
| `
|
|
122
|
-
| `
|
|
123
|
-
| `
|
|
124
|
-
| `
|
|
125
|
-
| `
|
|
126
|
-
| `
|
|
127
|
-
| `
|
|
128
|
-
| `
|
|
129
|
-
| `
|
|
130
|
-
| `
|
|
131
|
-
| `
|
|
132
|
-
| `
|
|
133
|
-
| `
|
|
116
|
+
| Option | Type | Default | Description |
|
|
117
|
+
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------- |
|
|
118
|
+
| `initialLayout` | `TreeNode \| null` | — | Initial layout tree structure for uncontrolled mode. Only used on initial mount. |
|
|
119
|
+
| `layout` | `TreeNode \| null` | — | Controlled layout tree structure. If provided, the hook runs in controlled mode and synchronizes with it. |
|
|
120
|
+
| `fullscreenPaneId` | `string \| null` | — | Controlled fullscreen pane ID. Pass `null` for no fullscreen pane. |
|
|
121
|
+
| `onFullscreenChange` | `(paneId: string \| null) => void` | — | Callback triggered when a pane is toggled to/from fullscreen mode. |
|
|
122
|
+
| `locked` | `boolean` | `false` | If true, layout resizes and drags are disabled. |
|
|
123
|
+
| `dragActivationDistance` | `number` | `8` | Minimum pointer drag distance (in pixels) required to activate dragging. |
|
|
124
|
+
| `snapThreshold` | `number` | `8` | Threshold in pixels to snap layout resizers to adjacent edges. |
|
|
125
|
+
| `minSplitPercentage` | `number` | `5` | Minimum resizing limit percentage. |
|
|
126
|
+
| `maxSplitPercentage` | `number` | `95` | Maximum resizing limit percentage. |
|
|
127
|
+
| `enableDragToDismiss` | `boolean` | `false` | If true, enables the drag-out-to-dismiss gesture to close widgets. |
|
|
128
|
+
| `dismissThreshold` | `number` | `60` | Distance in pixels outside container bounds required to trigger dismissal. |
|
|
129
|
+
| `onRemove` | `(paneId: string) => void` | — | Callback triggered when a pane is removed. |
|
|
130
|
+
| `onDragStart` | `(activeId: string) => void` | — | Callback triggered when dragging starts. |
|
|
131
|
+
| `onDragEnd` | `(activeId: string, overId: string \| null, dropAction: { type: 'split' \| 'move'; direction?: SplitDirection; position?: string } \| null) => void` | — | Callback triggered when dragging ends, with drop target and action details. |
|
|
132
|
+
| `onResizeStart` | `(currentNode: SplitNode) => void` | — | Callback triggered when resizing starts. |
|
|
133
|
+
| `onResize` | `(currentNode: SplitNode, percentage: number) => void` | — | Callback triggered during resizing. |
|
|
134
|
+
| `onResizeEnd` | `(currentNode: SplitNode, percentage: number) => void` | — | Callback triggered when resizing ends. |
|
|
135
|
+
| `onDismissIntentChange` | `(paneId: string \| null) => void` | — | Callback triggered when drag-out intent changes. |
|
|
134
136
|
|
|
135
137
|
### `useZeugmaContext()`
|
|
136
138
|
|
|
@@ -155,36 +157,67 @@ Recursively renders the split nodes and pane nodes. Must be placed inside `<Zeug
|
|
|
155
157
|
|
|
156
158
|
Wraps the individual pane components inside the renderer. Utilizes a render prop passing active layout attributes.
|
|
157
159
|
|
|
158
|
-
| Prop | Type | Required | Description
|
|
159
|
-
| ---------- | --------------------------------------- | -------- |
|
|
160
|
-
| `id` | `string` | Yes | The unique ID corresponding to a `PaneNode`'s `paneId`.
|
|
161
|
-
| `children` | `(props: PaneRenderProps) => ReactNode` | Yes | Render prop function.
|
|
160
|
+
| Prop | Type | Required | Description |
|
|
161
|
+
| ---------- | --------------------------------------- | -------- | ---------------------------------------------------------------------- |
|
|
162
|
+
| `id` | `string` | Yes | The unique ID corresponding to a `PaneNode`'s `paneId`. |
|
|
163
|
+
| `children` | `(props: PaneRenderProps) => ReactNode` | Yes | Render prop function. |
|
|
164
|
+
| `style` | `React.CSSProperties` | No | Optional inline CSS styles applied to the pane outer container. |
|
|
165
|
+
| `locked` | `boolean` | No | Optional override to lock this specific pane (disables drag and drop). |
|
|
162
166
|
|
|
163
167
|
#### Render Props: `PaneRenderProps`
|
|
164
168
|
|
|
165
|
-
| Parameter
|
|
166
|
-
|
|
|
167
|
-
| `isDragging`
|
|
168
|
-
| `isFullscreen`
|
|
169
|
-
| `toggleFullscreen`
|
|
170
|
-
| `remove`
|
|
171
|
-
| `
|
|
169
|
+
| Parameter | Type | Description |
|
|
170
|
+
| ------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
|
|
171
|
+
| `isDragging` | `boolean` | `true` if the pane is actively being dragged. |
|
|
172
|
+
| `isFullscreen` | `boolean` | `true` if the pane currently occupies the fullscreen view. |
|
|
173
|
+
| `toggleFullscreen` | `() => void` | Toggles the pane to and from fullscreen/zoomed mode. |
|
|
174
|
+
| `remove` | `() => void` | Removes this pane (and its active tab) from the layout tree. |
|
|
175
|
+
| `metadata` | `Record<string, unknown> \| undefined` | The metadata values associated with the active tab. |
|
|
176
|
+
| `updateMetadata` | `(updater: (current: Record<string, unknown> \| undefined) => Record<string, unknown> \| undefined) => void` | Updates metadata of the active tab via an updater function. |
|
|
177
|
+
| `locked` | `boolean` | `true` if this specific pane or the dashboard globally is locked. |
|
|
178
|
+
| `tabs` | `string[]` | The array of tab IDs in this pane. |
|
|
179
|
+
| `activeTabId` | `string` | The currently active tab ID. |
|
|
180
|
+
| `selectTab` | `(tabId: string) => void` | Selects a specific tab to make it active. |
|
|
181
|
+
| `removeTab` | `(tabId: string) => void` | Removes/closes a specific tab. |
|
|
182
|
+
| `tabsMetadata` | `Record<string, Record<string, unknown>> \| undefined` | Metadata values associated with all tabs in this pane. |
|
|
183
|
+
| `updateTabMetadata` | `(tabId: string, updater: (current: Record<string, unknown> \| undefined) => Record<string, unknown> \| undefined) => void` | Updates the metadata of a specific tab. |
|
|
184
|
+
| `renderActiveTab` | `() => ReactNode` | Renders the portal placeholder for the currently active tab in the pane. |
|
|
172
185
|
|
|
173
186
|
### `<Tabs>`
|
|
174
187
|
|
|
175
188
|
A helper component that renders a list of tab items for a pane, wrapping the internal drag-and-drop tab logic.
|
|
176
189
|
|
|
177
|
-
| Prop | Type
|
|
178
|
-
| :------------- |
|
|
179
|
-
| `tabs` | `string[]`
|
|
180
|
-
| `activeTabId` | `string`
|
|
181
|
-
| `locked` | `boolean`
|
|
182
|
-
| `tabsMetadata` | `Record<string, Record<string, any>>`
|
|
183
|
-
| `selectTab` | `(id: string) => void`
|
|
184
|
-
| `removeTab` | `(id: string) => void`
|
|
185
|
-
| `classNames` | `{ container?: string; tab?: string \| ((tabId: string) => string) }`
|
|
186
|
-
| `styles` | `{ container?: CSSProperties; tab?: CSSProperties \| ((tabId: string) => CSSProperties) }`
|
|
187
|
-
| `renderTab` | `(props: { tabId: string; activeTabId: string; isDragging: boolean; isOver: boolean; metadata?: Record<string,
|
|
190
|
+
| Prop | Type | Required | Description |
|
|
191
|
+
| :------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------- | :------------------------------------------------------------------- |
|
|
192
|
+
| `tabs` | `string[]` | Yes | The list of tab IDs in this pane. |
|
|
193
|
+
| `activeTabId` | `string` | Yes | The currently active tab ID. |
|
|
194
|
+
| `locked` | `boolean` | No | Whether dragging/reordering tabs is disabled (defaults to `false`). |
|
|
195
|
+
| `tabsMetadata` | `Record<string, Record<string, any>>` | No | Metadata associated with each tab. |
|
|
196
|
+
| `selectTab` | `(id: string) => void` | Yes | Callback when a tab is selected. |
|
|
197
|
+
| `removeTab` | `(id: string) => void` | Yes | Callback when a tab is closed/removed. |
|
|
198
|
+
| `classNames` | `{ container?: string; tab?: string \| ((tabId: string) => string) }` | No | Custom class names for the container and individual tab items. |
|
|
199
|
+
| `styles` | `{ container?: CSSProperties; tab?: CSSProperties \| ((tabId: string) => CSSProperties) }` | No | Custom inline CSS styles for the container and individual tab items. |
|
|
200
|
+
| `renderTab` | `(props: { tabId: string; activeTabId: string; isDragging: boolean; isOver: boolean; metadata?: Record<string, unknown>; selectTab: (id: string) => void; removeTab: (id: string) => void }) => ReactNode` | Yes | Render prop function called for each tab item. |
|
|
201
|
+
|
|
202
|
+
### `useTabContext()`
|
|
203
|
+
|
|
204
|
+
A context hook that provides per-tab state from within a `<Tab>` component rendered inside `<Tabs>`. Must be used within a rendered tab child.
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
const { tabId, isActive, isDragging, isOver, metadata, locked, selectTab, removeTab } =
|
|
208
|
+
useTabContext()
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
| Property | Type | Description |
|
|
212
|
+
| ------------ | -------------------------------------- | -------------------------------------------------- |
|
|
213
|
+
| `tabId` | `string` | The ID of the tab this context belongs to. |
|
|
214
|
+
| `isActive` | `boolean` | Whether this tab is the currently active tab. |
|
|
215
|
+
| `isDragging` | `boolean` | Whether this tab is currently being dragged. |
|
|
216
|
+
| `isOver` | `boolean` | Whether a dragged item is currently over this tab. |
|
|
217
|
+
| `metadata` | `Record<string, unknown> \| undefined` | Custom metadata associated with this tab. |
|
|
218
|
+
| `locked` | `boolean` | Whether this tab (or the dashboard) is locked. |
|
|
219
|
+
| `selectTab` | `() => void` | Selects this tab (no argument needed). |
|
|
220
|
+
| `removeTab` | `() => void` | Removes/closes this tab (no argument needed). |
|
|
188
221
|
|
|
189
222
|
### `<DragHandle>`
|
|
190
223
|
|
|
@@ -196,39 +229,57 @@ Defines the interactive drag region inside a `<Pane>`. **Must be placed inside a
|
|
|
196
229
|
| `className` | `string` | No | Custom CSS class for the drag handle wrapper. |
|
|
197
230
|
| `style` | `React.CSSProperties` | No | Inline styles for the drag handle wrapper. |
|
|
198
231
|
|
|
199
|
-
|
|
232
|
+
## Tree Utilities
|
|
200
233
|
|
|
201
|
-
|
|
234
|
+
Import these serializable tree utility functions from `react-zeugma/utils` for programmatically mutating or querying layout schemas.
|
|
202
235
|
|
|
203
|
-
|
|
204
|
-
| :----------------- | :------------------------- | :------- | :--------------- | :--------------------------------------------------------------- |
|
|
205
|
-
| `height` | `number` | No | `400` | Controlled height in pixels (or initial height if uncontrolled). |
|
|
206
|
-
| `onHeightChange` | `(height: number) => void` | No | — | Callback function triggered during or after dragging to resize. |
|
|
207
|
-
| `minHeight` | `number` | No | `100` | Minimum allowed height in pixels. |
|
|
208
|
-
| `maxHeight` | `number` | No | `Infinity` | Maximum allowed height in pixels. |
|
|
209
|
-
| `persist` | `boolean` | No | — | Whether to persist height changes in localStorage. |
|
|
210
|
-
| `localStorageKey` | `string` | No | `'default-pane'` | Custom localStorage key name (prefixed by `zeugma-height:`). |
|
|
211
|
-
| `resizerHeight` | `number` | No | `6` | Height of the resizer drag handle in pixels. |
|
|
212
|
-
| `className` | `string` | No | — | Custom CSS class applied to the outer container. |
|
|
213
|
-
| `resizerClassName` | `string` | No | — | Custom CSS class applied to the drag handle. |
|
|
236
|
+
#### `generateUniqueId(): string`
|
|
214
237
|
|
|
215
|
-
|
|
238
|
+
Generates a unique pane ID string (e.g., `'pane-abc123xyz'`). Useful when creating new pane nodes programmatically.
|
|
216
239
|
|
|
217
|
-
|
|
240
|
+
#### `removePane(tree: TreeNode | null, paneId: string): TreeNode | null`
|
|
218
241
|
|
|
219
|
-
|
|
242
|
+
Recursively scans the layout tree, removes the targeted pane node, and collapses redundant split boundaries.
|
|
220
243
|
|
|
221
|
-
#### `
|
|
244
|
+
#### `removeTab(tree: TreeNode | null, tabId: string): TreeNode | null`
|
|
222
245
|
|
|
223
|
-
|
|
246
|
+
Removes a single tab from its parent pane. If the pane has no remaining tabs after removal, the pane itself is collapsed out of the tree.
|
|
247
|
+
|
|
248
|
+
#### `addPane(tree: TreeNode | null, paneToAdd: string, metadata?: Record<string, unknown>): TreeNode`
|
|
249
|
+
|
|
250
|
+
Recursively matches the bottommost/rightmost pane leaf in the tree, splits it, and inserts the target `paneToAdd`. Optionally sets initial metadata for the new pane's tab.
|
|
224
251
|
|
|
225
|
-
#### `
|
|
252
|
+
#### `addTab(tree: TreeNode | null, targetPaneId: string, tabId: string, metadata?: Record<string, unknown>): TreeNode | null`
|
|
226
253
|
|
|
227
|
-
|
|
254
|
+
Appends a tab directly into a specific target pane node by its ID. Sets the new tab as the active tab. Does nothing if `targetPaneId` is not found.
|
|
228
255
|
|
|
229
256
|
#### `splitPane(tree, targetId, direction, splitType, paneToAdd)`
|
|
230
257
|
|
|
231
|
-
Splits the targeted `targetId` pane inside the tree with `direction` (_row_ / _column_) and type (_left_, _right_, _top_, _bottom_) to insert `paneToAdd`.
|
|
258
|
+
Splits the targeted `targetId` pane inside the tree with `direction` (_row_ / _column_) and type (_left_, _right_, _top_, _bottom_) to insert `paneToAdd`. `paneToAdd` may be a tab ID string or a full `PaneNode` object.
|
|
259
|
+
|
|
260
|
+
#### `updateSplitPercentage(tree: TreeNode | null, target: SplitNode, newPercentage: number): TreeNode | null`
|
|
261
|
+
|
|
262
|
+
Finds the target `SplitNode` by reference in the tree and updates its `splitPercentage`.
|
|
263
|
+
|
|
264
|
+
#### `selectTab(tree: TreeNode | null, paneId: string, tabId: string): TreeNode | null`
|
|
265
|
+
|
|
266
|
+
Activates the specified `tabId` within the `paneId` pane node. Returns the tree unchanged if the tab is already active or the pane is not found.
|
|
267
|
+
|
|
268
|
+
#### `mergeTab(tree: TreeNode | null, draggedTabId: string, targetPaneId: string): TreeNode | null`
|
|
269
|
+
|
|
270
|
+
Moves `draggedTabId` from its current pane into `targetPaneId`, preserving tab metadata. Collapses the source pane if it becomes empty. Sets the moved tab as the active tab in the target pane.
|
|
271
|
+
|
|
272
|
+
#### `moveTab(tree: TreeNode | null, draggedTabId: string, targetTabId: string, position?: 'before' | 'after'): TreeNode | null`
|
|
273
|
+
|
|
274
|
+
Reorders `draggedTabId` adjacent to `targetTabId` within the same pane (or moves it cross-pane). `position` defaults to `'before'`.
|
|
275
|
+
|
|
276
|
+
#### `updateTabMetadata(tree: TreeNode | null, tabId: string, updater: (current: Record<string, unknown> | undefined) => Record<string, unknown> | undefined): TreeNode | null`
|
|
277
|
+
|
|
278
|
+
Updates the metadata of a specific tab using an updater function. Returning `undefined` from the updater removes the entry.
|
|
279
|
+
|
|
280
|
+
#### `updatePaneLock(tree: TreeNode | null, paneId: string, locked: boolean): TreeNode | null`
|
|
281
|
+
|
|
282
|
+
Sets the `locked` flag on the specified pane node. When `locked` is `false`, the field is removed from the node entirely to keep the tree clean.
|
|
232
283
|
|
|
233
284
|
#### `findPaneById(tree: TreeNode | null, paneId: string): PaneNode | null`
|
|
234
285
|
|
|
@@ -242,10 +293,81 @@ Recursively searches the layout tree and returns the `PaneNode` containing the s
|
|
|
242
293
|
|
|
243
294
|
Searches the layout tree for the given `tabId` and returns computed details (parent `paneId`, `isActive`, `index`, and custom `metadata`).
|
|
244
295
|
|
|
296
|
+
#### `computeLayout(node: TreeNode | null, left?, top?, width?, height?): { panes: ComputedPane[]; splitters: ComputedSplitter[] }`
|
|
297
|
+
|
|
298
|
+
Recursively computes the absolute position and dimensions (as percentages relative to the container) for every pane and splitter in the tree. Useful for building custom render layers or analytics on top of the layout engine.
|
|
299
|
+
|
|
245
300
|
#### `calculateTabDropIndex(tabs: string[], activeType: string | null, overTabId: string | null, overTabPosition: 'before' | 'after' | null): number`
|
|
246
301
|
|
|
247
302
|
Calculates the target insertion index for a dragged tab within a list of tabs. Returns `-1` if the drop target is not in the list.
|
|
248
303
|
|
|
304
|
+
#### `getOrCreateHiddenContainer(id: string): HTMLElement`
|
|
305
|
+
|
|
306
|
+
Retrieves or initializes a hidden container `div` appended to `document.body` for sheltering portal elements while they are inactive or popped out.
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## Popping Out Tabs (Open in a New Window)
|
|
311
|
+
|
|
312
|
+
`react-zeugma` includes built-in support for popping out tab panels into separate browser windows (popouts) with **zero React component unmounting or state loss**.
|
|
313
|
+
|
|
314
|
+
### How it Works
|
|
315
|
+
|
|
316
|
+
1. **State-Driven**: The popped-out state is controlled via the tab's metadata under the `openInNewWindow` boolean key.
|
|
317
|
+
2. **Stable Portal target**: The React portal container reference is kept stable, and `document.adoptNode` is used to migrate the DOM container directly between parent and popup documents, keeping internal component state (like inputs, terminal histories, or connections) fully intact.
|
|
318
|
+
3. **Style Syncing**: Active stylesheets and link tags are automatically copied from the parent workspace into the popup window on creation.
|
|
319
|
+
4. **Auto-Restoration**: Close actions on the popup window automatically reset the metadata state to `openInNewWindow: false` and restore the tab wrapper back to the main document layout.
|
|
320
|
+
|
|
321
|
+
### Integration Recipe
|
|
322
|
+
|
|
323
|
+
To enable popping out tabs in your dashboard:
|
|
324
|
+
|
|
325
|
+
#### 1. Add a Popout Toggle Button
|
|
326
|
+
|
|
327
|
+
Inside your pane header or controls, add a button to toggle the popout state of the active tab:
|
|
328
|
+
|
|
329
|
+
```tsx
|
|
330
|
+
<button
|
|
331
|
+
onClick={() => {
|
|
332
|
+
const activeTabId = paneProps.activeTabId
|
|
333
|
+
const isPopout = !!paneProps.tabsMetadata?.[activeTabId]?.openInNewWindow
|
|
334
|
+
|
|
335
|
+
paneProps.updateTabMetadata(activeTabId, (current) => ({
|
|
336
|
+
...current,
|
|
337
|
+
openInNewWindow: !isPopout,
|
|
338
|
+
}))
|
|
339
|
+
}}
|
|
340
|
+
>
|
|
341
|
+
{paneProps.tabsMetadata?.[paneProps.activeTabId]?.openInNewWindow ? 'Restore' : 'Popout'}
|
|
342
|
+
</button>
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
#### 2. Display a Placeholder in the Workspace Layout
|
|
346
|
+
|
|
347
|
+
When a tab is open in a separate window, render a custom placeholder inside the main workspace pane instead of rendering the tab's active content:
|
|
348
|
+
|
|
349
|
+
```tsx
|
|
350
|
+
<div className="pane-body">
|
|
351
|
+
{paneProps.tabsMetadata?.[paneProps.activeTabId]?.openInNewWindow ? (
|
|
352
|
+
<div className="popout-placeholder">
|
|
353
|
+
<p>This tab is open in a separate window.</p>
|
|
354
|
+
<button
|
|
355
|
+
onClick={() => {
|
|
356
|
+
paneProps.updateTabMetadata(paneProps.activeTabId, (current) => ({
|
|
357
|
+
...current,
|
|
358
|
+
openInNewWindow: false,
|
|
359
|
+
}))
|
|
360
|
+
}}
|
|
361
|
+
>
|
|
362
|
+
Restore Tab
|
|
363
|
+
</button>
|
|
364
|
+
</div>
|
|
365
|
+
) : (
|
|
366
|
+
paneProps.renderActiveTab()
|
|
367
|
+
)}
|
|
368
|
+
</div>
|
|
369
|
+
```
|
|
370
|
+
|
|
249
371
|
---
|
|
250
372
|
|
|
251
373
|
## Custom Styling
|
|
@@ -261,17 +383,38 @@ Use custom CSS or styling rules to style resizers, dragging states, drop preview
|
|
|
261
383
|
'bg-transparent hover:bg-indigo-500/50 active:bg-indigo-500 transition-colors duration-150',
|
|
262
384
|
// split previews
|
|
263
385
|
dropPreview: 'bg-indigo-500/10 border-2 border-dashed border-indigo-500/50 backdrop-blur-xs',
|
|
386
|
+
// tab separator line
|
|
387
|
+
tabSeparator: 'w-px h-4 bg-zinc-700',
|
|
264
388
|
}}
|
|
265
389
|
>
|
|
266
390
|
<PaneTree />
|
|
267
391
|
</Zeugma>
|
|
268
392
|
```
|
|
269
393
|
|
|
394
|
+
### `ZeugmaClassNames` reference
|
|
395
|
+
|
|
396
|
+
| Key | Applied to |
|
|
397
|
+
| ------------------------ | --------------------------------------------------------------------------------- |
|
|
398
|
+
| `dashboard` | Root dashboard container. |
|
|
399
|
+
| `dashboardDismissActive` | Root container when a drag-out dismiss is active. |
|
|
400
|
+
| `dashboardLocked` | Root container when the dashboard is globally locked. |
|
|
401
|
+
| `pane` | Outer wrapper `<div>` of each `<Pane>`. |
|
|
402
|
+
| `paneLocked` | Pane container when locked. |
|
|
403
|
+
| `dropPreview` | Drop zone preview box when hovering over a split-edge drop zone. |
|
|
404
|
+
| `dragOverlay` | Cursor-following drag preview portal wrapper. |
|
|
405
|
+
| `resizer` | Drag-to-resize split bar handles. |
|
|
406
|
+
| `dismissPreview` | Background dismiss zone indicator during a drag-out dismiss gesture. |
|
|
407
|
+
| `lockedPreview` | Drop zone indicator when hovering over a locked pane. |
|
|
408
|
+
| `tabDropPreview` | Placeholder line element rendered at the target insertion point during tab drags. |
|
|
409
|
+
| `tabSeparator` | Separator line rendered between non-active adjacent tabs in `<Tabs>`. |
|
|
410
|
+
|
|
270
411
|
---
|
|
271
412
|
|
|
272
413
|
## Types Reference
|
|
273
414
|
|
|
274
|
-
Full TypeScript type definitions
|
|
415
|
+
Full TypeScript type definitions exported from `react-zeugma`.
|
|
416
|
+
|
|
417
|
+
### Layout Tree
|
|
275
418
|
|
|
276
419
|
```ts
|
|
277
420
|
export type SplitDirection = 'row' | 'column'
|
|
@@ -281,7 +424,7 @@ export interface SplitNode {
|
|
|
281
424
|
direction: SplitDirection
|
|
282
425
|
first: TreeNode
|
|
283
426
|
second: TreeNode
|
|
284
|
-
splitPercentage: number
|
|
427
|
+
splitPercentage: number // 0–100
|
|
285
428
|
}
|
|
286
429
|
|
|
287
430
|
export interface PaneNode {
|
|
@@ -304,6 +447,164 @@ export interface TabDetails {
|
|
|
304
447
|
}
|
|
305
448
|
```
|
|
306
449
|
|
|
450
|
+
### Component Props
|
|
451
|
+
|
|
452
|
+
````ts
|
|
453
|
+
export interface PaneProps {
|
|
454
|
+
id: string
|
|
455
|
+
children: (props: PaneRenderProps) => React.ReactNode
|
|
456
|
+
style?: React.CSSProperties
|
|
457
|
+
locked?: boolean
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
export interface DragHandleProps {
|
|
461
|
+
children?: React.ReactNode
|
|
462
|
+
className?: string
|
|
463
|
+
style?: React.CSSProperties
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
export interface TabsProps {
|
|
467
|
+
tabs: string[]
|
|
468
|
+
activeTabId: string
|
|
469
|
+
locked?: boolean
|
|
470
|
+
tabsMetadata?: Record<string, Record<string, unknown>>
|
|
471
|
+
selectTab: (id: string) => void
|
|
472
|
+
removeTab: (id: string) => void
|
|
473
|
+
renderTab: (props: {
|
|
474
|
+
tabId: string
|
|
475
|
+
activeTabId: string
|
|
476
|
+
isDragging: boolean
|
|
477
|
+
isOver: boolean
|
|
478
|
+
metadata?: Record<string, unknown>
|
|
479
|
+
selectTab: (id: string) => void
|
|
480
|
+
removeTab: (id: string) => void
|
|
481
|
+
}) => React.ReactNode
|
|
482
|
+
classNames?: {
|
|
483
|
+
container?: string
|
|
484
|
+
tab?: string | ((tabId: string) => string)
|
|
485
|
+
}
|
|
486
|
+
styles?: {
|
|
487
|
+
container?: React.CSSProperties
|
|
488
|
+
tab?: React.CSSProperties | ((tabId: string) => React.CSSProperties)
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
### Render Props
|
|
493
|
+
|
|
494
|
+
```ts
|
|
495
|
+
export interface PaneRenderProps {
|
|
496
|
+
isDragging: boolean
|
|
497
|
+
isFullscreen: boolean
|
|
498
|
+
toggleFullscreen: () => void
|
|
499
|
+
remove: () => void
|
|
500
|
+
metadata: Record<string, unknown> | undefined
|
|
501
|
+
updateMetadata: (
|
|
502
|
+
updater: (current: Record<string, unknown> | undefined) => Record<string, unknown> | undefined,
|
|
503
|
+
) => void
|
|
504
|
+
locked: boolean
|
|
505
|
+
tabs: string[]
|
|
506
|
+
activeTabId: string
|
|
507
|
+
selectTab: (tabId: string) => void
|
|
508
|
+
removeTab: (tabId: string) => void
|
|
509
|
+
tabsMetadata: Record<string, Record<string, unknown>> | undefined
|
|
510
|
+
updateTabMetadata: (
|
|
511
|
+
tabId: string,
|
|
512
|
+
updater: (current: Record<string, unknown> | undefined) => Record<string, unknown> | undefined,
|
|
513
|
+
) => void
|
|
514
|
+
renderActiveTab: () => ReactNode
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
export interface TabContextValue {
|
|
518
|
+
tabId: string
|
|
519
|
+
isActive: boolean
|
|
520
|
+
isDragging: boolean
|
|
521
|
+
isOver: boolean
|
|
522
|
+
metadata?: Record<string, unknown>
|
|
523
|
+
locked: boolean
|
|
524
|
+
selectTab: () => void
|
|
525
|
+
removeTab: () => void
|
|
526
|
+
}
|
|
527
|
+
````
|
|
528
|
+
|
|
529
|
+
### Controller & Context
|
|
530
|
+
|
|
531
|
+
```ts
|
|
532
|
+
export interface UseZeugmaOptions { /* see useZeugma() section above */ }
|
|
533
|
+
|
|
534
|
+
export interface ZeugmaController {
|
|
535
|
+
// State
|
|
536
|
+
layout: TreeNode | null
|
|
537
|
+
setLayout: Dispatch<SetStateAction<TreeNode | null>>
|
|
538
|
+
fullscreenPaneId: string | null
|
|
539
|
+
setFullscreenPaneId: (paneId: string | null) => void
|
|
540
|
+
locked: boolean
|
|
541
|
+
setLocked: Dispatch<SetStateAction<boolean>>
|
|
542
|
+
|
|
543
|
+
// DnD state
|
|
544
|
+
activeId: string | null
|
|
545
|
+
activeType: 'pane' | 'tab' | null
|
|
546
|
+
dismissIntentId: string | null
|
|
547
|
+
|
|
548
|
+
// Config
|
|
549
|
+
dragActivationDistance: number
|
|
550
|
+
snapThreshold: number
|
|
551
|
+
minSplitPercentage: number
|
|
552
|
+
maxSplitPercentage: number
|
|
553
|
+
enableDragToDismiss: boolean
|
|
554
|
+
dismissThreshold: number
|
|
555
|
+
|
|
556
|
+
// Public actions
|
|
557
|
+
removePane: (paneId: string) => void
|
|
558
|
+
addPane: (paneId: string, metadata?: Record<string, unknown>) => void
|
|
559
|
+
addTab: (paneId: string, tabId: string, metadata?: Record<string, unknown>) => void
|
|
560
|
+
updateTabMetadata: (tabId: string, updater: ...) => void
|
|
561
|
+
updatePaneLock: (paneId: string, locked: boolean) => void
|
|
562
|
+
selectTab: (paneId: string, tabId: string) => void
|
|
563
|
+
mergeTab: (draggedTabId: string, targetPaneId: string) => void
|
|
564
|
+
removeTab: (tabId: string) => void
|
|
565
|
+
splitPane: (targetId: string, direction: SplitDirection, splitType: 'left' | 'right' | 'top' | 'bottom', paneToAdd: string) => void
|
|
566
|
+
updateSplitPercentage: (currentNode: SplitNode, percentage: number) => void
|
|
567
|
+
moveTab: (draggedTabId: string, targetTabId: string, position?: 'before' | 'after') => void
|
|
568
|
+
|
|
569
|
+
// Public queries
|
|
570
|
+
findPaneById: (paneId: string) => PaneNode | null
|
|
571
|
+
findPaneContainingTab: (tabId: string) => PaneNode | null
|
|
572
|
+
findTabById: (tabId: string) => TabDetails | null
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// ZeugmaContextValue is the combined state + actions interface
|
|
576
|
+
// exposed by useZeugmaContext(). It includes all of ZeugmaController
|
|
577
|
+
// plus the renderPane and classNames configuration.
|
|
578
|
+
export interface ZeugmaContextValue extends ZeugmaStateValue, ZeugmaActionsValue {}
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
### Computed Layout Types (from `react-zeugma/utils`)
|
|
582
|
+
|
|
583
|
+
```ts
|
|
584
|
+
export interface ComputedPane {
|
|
585
|
+
paneId: string
|
|
586
|
+
left: number // percentage
|
|
587
|
+
top: number // percentage
|
|
588
|
+
width: number // percentage
|
|
589
|
+
height: number // percentage
|
|
590
|
+
node: PaneNode
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
export interface ComputedSplitter {
|
|
594
|
+
id: string
|
|
595
|
+
currentNode: SplitNode
|
|
596
|
+
direction: SplitDirection
|
|
597
|
+
left: number
|
|
598
|
+
top: number
|
|
599
|
+
width: number
|
|
600
|
+
height: number
|
|
601
|
+
parentLeft: number
|
|
602
|
+
parentTop: number
|
|
603
|
+
parentWidth: number
|
|
604
|
+
parentHeight: number
|
|
605
|
+
}
|
|
606
|
+
```
|
|
607
|
+
|
|
307
608
|
---
|
|
308
609
|
|
|
309
610
|
## SKILL.md
|
|
@@ -481,24 +782,6 @@ Defines the interactive drag region inside a `<Pane>`. **Must be placed inside a
|
|
|
481
782
|
- `className?: string`
|
|
482
783
|
- `style?: React.CSSProperties`
|
|
483
784
|
|
|
484
|
-
---
|
|
485
|
-
|
|
486
|
-
### `<ResizableContainer>`
|
|
487
|
-
|
|
488
|
-
A vertical-resize container wrapper that wraps any node (typically `<PaneTree />` or a dashboard component) and allows the user to resize its height by dragging a handle at the bottom edge. Includes smooth scroll parent propagation and drag-to-scroll infinite scrolling behavior.
|
|
489
|
-
|
|
490
|
-
#### Props
|
|
491
|
-
|
|
492
|
-
- `height?: number` — Controlled height in pixels (or initial height if uncontrolled). Defaults to `400`.
|
|
493
|
-
- `onHeightChange?: (height: number) => void` — Callback function triggered during or after dragging to resize.
|
|
494
|
-
- `minHeight?: number` — Minimum allowed height in pixels (defaults to `100`).
|
|
495
|
-
- `maxHeight?: number` — Maximum allowed height in pixels (defaults to `Infinity`).
|
|
496
|
-
- `persist?: boolean` — Whether to persist height changes in localStorage.
|
|
497
|
-
- `localStorageKey?: string` — Custom localStorage key name (defaults to `'default-pane'`).
|
|
498
|
-
- `resizerHeight?: number` — Height of the resizer drag handle in pixels (defaults to `6`).
|
|
499
|
-
- `className?: string` — Custom CSS class applied to the outer container.
|
|
500
|
-
- `resizerClassName?: string` — Custom CSS class applied to the drag handle.
|
|
501
|
-
|
|
502
785
|
## 3. Programmatic State Utilities
|
|
503
786
|
|
|
504
787
|
Import these helpers from `react-zeugma/utils` to manipulate or query the tree layout programmatically in your state handlers:
|
|
@@ -591,6 +874,7 @@ interface ZeugmaClassNames {
|
|
|
591
874
|
dismissPreview?: string // Applied to the background dismiss zone indicator during a drag-out dismiss gesture
|
|
592
875
|
lockedPreview?: string // Applied to drop zone indicator when hovering over a locked pane
|
|
593
876
|
tabDropPreview?: string // Applied to the drop placeholder line element during tab drags
|
|
877
|
+
tabSeparator?: string // Applied to the separator line between non-active adjacent tabs
|
|
594
878
|
}
|
|
595
879
|
```
|
|
596
880
|
|
|
@@ -263,4 +263,13 @@ interface PortalRegistryValue {
|
|
|
263
263
|
registerPortalTarget: (tabId: string, el: HTMLDivElement | null) => void;
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
-
|
|
266
|
+
/**
|
|
267
|
+
* Utility to copy stylesheets from source document to destination document.
|
|
268
|
+
*/
|
|
269
|
+
declare function copyStyles(srcDoc: Document, destDoc: Document): void;
|
|
270
|
+
/**
|
|
271
|
+
* Utility to get or create a hidden container element on the document body.
|
|
272
|
+
*/
|
|
273
|
+
declare function getOrCreateHiddenContainer(id: string): HTMLElement;
|
|
274
|
+
|
|
275
|
+
export { type PortalRegistryValue as P, type SplitDirection as S, type TreeNode as T, type UseZeugmaOptions as U, type ZeugmaActionsValue as Z, type ZeugmaDragStateValue as a, type ZeugmaStateValue as b, type ZeugmaController as c, type ZeugmaContextValue as d, type ZeugmaProps as e, type SplitNode as f, type PaneNode as g, type TabDetails as h, type ZeugmaClassNames as i, copyStyles as j, getOrCreateHiddenContainer as k };
|
|
@@ -263,4 +263,13 @@ interface PortalRegistryValue {
|
|
|
263
263
|
registerPortalTarget: (tabId: string, el: HTMLDivElement | null) => void;
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
-
|
|
266
|
+
/**
|
|
267
|
+
* Utility to copy stylesheets from source document to destination document.
|
|
268
|
+
*/
|
|
269
|
+
declare function copyStyles(srcDoc: Document, destDoc: Document): void;
|
|
270
|
+
/**
|
|
271
|
+
* Utility to get or create a hidden container element on the document body.
|
|
272
|
+
*/
|
|
273
|
+
declare function getOrCreateHiddenContainer(id: string): HTMLElement;
|
|
274
|
+
|
|
275
|
+
export { type PortalRegistryValue as P, type SplitDirection as S, type TreeNode as T, type UseZeugmaOptions as U, type ZeugmaActionsValue as Z, type ZeugmaDragStateValue as a, type ZeugmaStateValue as b, type ZeugmaController as c, type ZeugmaContextValue as d, type ZeugmaProps as e, type SplitNode as f, type PaneNode as g, type TabDetails as h, type ZeugmaClassNames as i, copyStyles as j, getOrCreateHiddenContainer as k };
|