react-zeugma 6.3.0 → 6.4.1

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 CHANGED
@@ -40,7 +40,7 @@ npm install react-zeugma
40
40
  Import the core components and configure the layout state inside your React application using the `useZeugma` hook.
41
41
 
42
42
  ```tsx
43
- import { useZeugma, Zeugma, PaneTree, Pane, DragHandle, TreeNode } from 'react-zeugma'
43
+ import { useZeugma, Zeugma, PaneTree, Pane, TreeNode } from 'react-zeugma'
44
44
 
45
45
  const initialLayout: TreeNode = {
46
46
  type: 'split',
@@ -59,32 +59,27 @@ const initialLayout: TreeNode = {
59
59
  function MyPane({ id }: { id: string }) {
60
60
  return (
61
61
  <Pane id={id}>
62
- {({ isDragging, remove }) => (
63
- <div className={`h-full flex flex-col bg-[#18181b] ${isDragging ? 'opacity-30' : ''}`}>
64
- <DragHandle>
65
- <div className="px-3 py-2 bg-[#27272a] border-b border-[#3f3f46] flex items-center justify-between cursor-grab">
66
- <span className="text-xs uppercase text-zinc-300 font-bold">{id}</span>
67
- <button onClick={remove} className="text-zinc-500 hover:text-rose-400 text-xs">
68
- ×
69
- </button>
70
- </div>
71
- </DragHandle>
72
- <div className="flex-1 p-4 text-sm text-zinc-400">Content for {id}</div>
73
- </div>
74
- )}
62
+ <div className="h-full flex flex-col bg-[#18181b]">
63
+ <Pane.DragHandle>
64
+ <div className="px-3 py-2 bg-[#27272a] border-b border-[#3f3f46] flex items-center justify-between cursor-grab">
65
+ <span className="text-xs uppercase text-zinc-300 font-bold">{id}</span>
66
+ </div>
67
+ </Pane.DragHandle>
68
+ <Pane.Content className="flex-1 p-4 text-sm text-zinc-400">
69
+ {(tab) => <div>Content for {tab.id}</div>}
70
+ </Pane.Content>
71
+ </div>
75
72
  </Pane>
76
73
  )
77
74
  }
78
75
 
79
76
  export default function Dashboard() {
80
- const zeugma = useZeugma({ initialLayout })
77
+ const controller = useZeugma({ initialLayout })
81
78
 
82
79
  return (
83
- <Zeugma {...zeugma} renderPane={(id) => <MyPane id={id} />}>
84
- <div className="w-screen h-screen">
85
- <PaneTree />
86
- </div>
87
- </Zeugma>
80
+ <div className="w-screen h-screen">
81
+ <Zeugma controller={controller} renderPane={(id) => <MyPane id={id} />} />
82
+ </div>
88
83
  )
89
84
  }
90
85
  ```
@@ -93,127 +88,137 @@ export default function Dashboard() {
93
88
 
94
89
  ## API Reference
95
90
 
96
- ### `<Zeugma>`
91
+ ### `<ZeugmaProvider>`
92
+
93
+ The context provider that sets up the drag-and-drop state machine, monitors active drags, and registers layout change notifications. This is a headless component that does not render any visual grid container. Wrap your custom dashboard structure (including sidebars, toolbars, and the `<Zeugma>` grid layout) with `<ZeugmaProvider>` to allow descendant components to consume the layout context.
94
+
95
+ It accepts a required `controller` and `children` props:
97
96
 
98
- The context provider that sets up the drag-and-drop state machine, monitors active drags, and registers layout change notifications. It extends `ZeugmaController` directly; you typically spread the controller object returned by `useZeugma` onto it.
97
+ | Prop | Type | Required | Description |
98
+ | :----------- | :----------------- | :------- | :--------------------------------------------------------------------- |
99
+ | `controller` | `ZeugmaController` | Yes | The Zeugma controller object returned by `useZeugma(options)`. |
100
+ | `children` | `React.ReactNode` | Yes | Sibling or descendant components that will have access to the context. |
99
101
 
100
- | Prop | Type | Required | Description |
101
- | -------------------- | -------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------- |
102
- | `...controllerProps` | `ZeugmaController` | Yes | All properties returned by `useZeugma(options)`. Usually passed by spreading the controller object (e.g., `{...zeugma}`). |
103
- | `renderPane` | `(paneId: string) => ReactNode` | Yes | Renderer function lookup that returns a `<Pane>` structure. |
104
- | `classNames` | `ZeugmaClassNames` | No | Custom classes for overriding pane, resizer, and drop preview overlays. |
105
- | `renderDragOverlay` | `(activeId: string, type: 'pane' \| 'tab') => ReactNode` | No | Renders a custom cursor-following drag preview overlay. |
106
- | `renderWidget` | `(tabId: string) => ReactNode` | No | Render function mapping tab IDs to React elements. Used to render tab widgets inside portals. |
102
+ Additionally, `<ZeugmaProvider>` accepts all configuration settings and lifecycle callbacks listed for `<Zeugma>` below (such as `classNames`, `resizerSize`, `renderPane`, `onDragStart`, etc.).
103
+
104
+ ### `<Zeugma>`
105
+
106
+ The main visual dashboard grid renderer.
107
+
108
+ - **Standalone Mode**: If used standalone, `<Zeugma>` acts as the context provider itself. You must pass both `controller` and `renderPane` props to it.
109
+ - **Provider-Wrapped Mode**: If nested within a `<ZeugmaProvider>`, you do not need to pass any props to `<Zeugma>` (it will automatically read the state, configuration, and callbacks directly from the parent context).
110
+
111
+ | Prop | Type | Required | Description |
112
+ | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | --------------------------------------------------------------------------------------------- |
113
+ | `controller` | `ZeugmaController` | Standalone only | The Zeugma controller object returned by `useZeugma(options)`. |
114
+ | `renderPane` | `(paneId: string) => ReactNode` | Standalone only | Renderer function lookup that returns a `<Pane>` structure. |
115
+ | `classNames` | `ZeugmaClassNames` | No | Custom classes for overriding pane, resizer, and drop preview overlays. |
116
+ | `renderDragOverlay` | `(active: DragOverlayActiveItem) => ReactNode` | No | Renders a custom cursor-following drag preview overlay. |
117
+ | `resizerSize` | `number` | No | Thickness of the split resizer bars in pixels. Defaults to `4`. |
118
+ | `dragActivationDistance` | `number` | No | Minimum pointer drag distance (in pixels) required to activate dragging (defaults to `8`). |
119
+ | `snapThreshold` | `number` | No | Threshold in pixels to snap layout resizers to adjacent edges (defaults to `8`). |
120
+ | `minSplitPercentage` | `number` | No | Minimum resizing limit percentage (defaults to `5`). |
121
+ | `maxSplitPercentage` | `number` | No | Maximum resizing limit percentage (defaults to `95`). |
122
+ | `enableDragToDismiss` | `boolean` | No | If true, enables the drag-out-to-dismiss gesture to close widgets (defaults to `false`). |
123
+ | `dismissThreshold` | `number` | No | Distance in pixels outside container bounds required to trigger dismissal (defaults to `60`). |
124
+ | `onRemove` | `(paneId: string) => void` | No | Callback triggered when a pane is removed. |
125
+ | `onDragStart` | `(activeId: string) => void` | No | Callback triggered when dragging starts. |
126
+ | `onDragEnd` | `(activeId: string, overId: string \| null, dropAction: { type: 'split' \| 'move'; direction?: SplitDirection; position?: string } \| null) => void` | No | Callback triggered when dragging ends. |
127
+ | `onResizeStart` | `(currentNode: SplitNode) => void` | No | Callback triggered when resizing starts. |
128
+ | `onResize` | `(currentNode: SplitNode, percentage: number) => void` | No | Callback triggered during resizing. |
129
+ | `onResizeEnd` | `(currentNode: SplitNode, percentage: number) => void` | No | Callback triggered when resizing ends. |
130
+ | `onDismissIntentChange` | `(paneId: string \| null) => void` | No | Callback triggered when drag-out intent changes. |
107
131
 
108
132
  ### `useZeugma(options)`
109
133
 
110
- A custom state hook that initializes and manages the recursive layout tree and handles drag-and-drop actions.
111
-
112
- | Option | Type | Default | Description |
113
- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------- |
114
- | `initialLayout` | `TreeNode \| null` | — | Initial layout tree structure for uncontrolled mode. Only used on initial mount. |
115
- | `layout` | `TreeNode \| null` | — | Controlled layout tree structure. If provided, the hook runs in controlled mode and synchronizes with it. |
116
- | `fullscreenPaneId` | `string \| null` | — | Controlled fullscreen pane ID. Pass `null` for no fullscreen pane. |
117
- | `onFullscreenChange` | `(paneId: string \| null) => void` | — | Callback triggered when a pane is toggled to/from fullscreen mode. |
118
- | `locked` | `boolean` | `false` | If true, layout resizes and drags are disabled. |
119
- | `dragActivationDistance` | `number` | `8` | Minimum pointer drag distance (in pixels) required to activate dragging. |
120
- | `snapThreshold` | `number` | `8` | Threshold in pixels to snap layout resizers to adjacent edges. |
121
- | `minSplitPercentage` | `number` | `5` | Minimum resizing limit percentage. |
122
- | `maxSplitPercentage` | `number` | `95` | Maximum resizing limit percentage. |
123
- | `enableDragToDismiss` | `boolean` | `false` | If true, enables the drag-out-to-dismiss gesture to close widgets. |
124
- | `dismissThreshold` | `number` | `60` | Distance in pixels outside container bounds required to trigger dismissal. |
125
- | `onRemove` | `(paneId: string) => void` | — | Callback triggered when a pane is removed. |
126
- | `onDragStart` | `(activeId: string) => void` | — | Callback triggered when dragging starts. |
127
- | `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. |
128
- | `onResizeStart` | `(currentNode: SplitNode) => void` | — | Callback triggered when resizing starts. |
129
- | `onResize` | `(currentNode: SplitNode, percentage: number) => void` | — | Callback triggered during resizing. |
130
- | `onResizeEnd` | `(currentNode: SplitNode, percentage: number) => void` | — | Callback triggered when resizing ends. |
131
- | `onDismissIntentChange` | `(paneId: string \| null) => void` | — | Callback triggered when drag-out intent changes. |
134
+ A custom state hook that initializes and manages the layout tree, locked state, and fullscreen mode.
135
+
136
+ | Option | Type | Default | Description |
137
+ | -------------------- | --------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------- |
138
+ | `initialLayout` | `TreeNode \| null` | — | Initial layout tree structure for uncontrolled mode. Only used on initial mount. |
139
+ | `layout` | `TreeNode \| null` | — | Controlled layout tree structure. If provided, the hook runs in controlled mode and synchronizes with it. |
140
+ | `onChange` | `(newLayout: TreeNode \| null) => void` | — | Callback triggered when the layout changes. |
141
+ | `fullscreenPaneId` | `string \| null` | — | Controlled fullscreen pane ID. Pass `null` for no fullscreen pane. |
142
+ | `onFullscreenChange` | `(paneId: string \| null) => void` | | Callback triggered when a pane is toggled to/from fullscreen mode. |
143
+ | `locked` | `boolean` | `false` | If true, layout resizes and drags are disabled. |
132
144
 
133
145
  ### `useZeugmaContext()`
134
146
 
135
147
  A custom React context hook that returns the unified layout controller properties and state actions. Must be used within a `<Zeugma>` provider component.
136
148
 
137
- Provides direct access to the current layout state (e.g., `layout`, `layoutBeforeDrag`, `locked`), state setters (e.g., `setLocked`), queries (e.g., `findTabById`, `findPaneContainingTab`, `findPaneById`), and mutation actions (e.g., `addPane`, `removePane`, `updateTabMetadata`, `removeTab`, `selectTab`, etc.).
149
+ Provides direct access to the current layout state (e.g., `layout`, `locked`), state setters (e.g., `setLocked`), queries (e.g., `findTabById`, `findPaneContainingTab`, `findPaneById`), and mutation actions (e.g., `addTab`, `removePane`, `selectTab`, etc.).
138
150
 
139
151
  ```ts
140
- const { layout, layoutBeforeDrag, locked, findTabById, setLocked } = useZeugmaContext()
152
+ const { layout, locked, findTabById, setLocked, removePane } = useZeugmaContext()
141
153
  ```
142
154
 
143
155
  ### `<PaneTree>`
144
156
 
145
- Recursively renders the split nodes and pane nodes. Must be placed inside `<Zeugma>`.
146
-
147
- | Prop | Type | Required | Description |
148
- | ------------- | ------------------ | -------- | ------------------------------------------------------------------- |
149
- | `tree` | `TreeNode \| null` | No | Custom subtree to render. Defaults to the provider's root `layout`. |
150
- | `resizerSize` | `number` | No | Thickness of the split resizer bars in pixels. Defaults to `4`. |
157
+ An internal component that recursively renders the split nodes and pane nodes. It is automatically managed and rendered internally by `<Zeugma>`, so consumers do not need to import or render it manually. Configuration options like `resizerSize` and `snapThreshold` are passed directly as props to `<Zeugma>` instead.
151
158
 
152
159
  ### `<Pane id>`
153
160
 
154
- Wraps the individual pane components inside the renderer. Utilizes a render prop passing active layout attributes.
155
-
156
- | Prop | Type | Required | Description |
157
- | ---------- | --------------------------------------- | -------- | ---------------------------------------------------------------------- |
158
- | `id` | `string` | Yes | The unique ID corresponding to a `PaneNode`'s `paneId`. |
159
- | `children` | `(props: PaneRenderProps) => ReactNode` | Yes | Render prop function. |
160
- | `style` | `React.CSSProperties` | No | Optional inline CSS styles applied to the pane outer container. |
161
- | `locked` | `boolean` | No | Optional override to lock this specific pane (disables drag and drop). |
162
-
163
- #### Render Props: `PaneRenderProps`
164
-
165
- | Parameter | Type | Description |
166
- | ------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
167
- | `isDragging` | `boolean` | `true` if the pane is actively being dragged. |
168
- | `isFullscreen` | `boolean` | `true` if the pane currently occupies the fullscreen view. |
169
- | `toggleFullscreen` | `() => void` | Toggles the pane to and from fullscreen/zoomed mode. |
170
- | `remove` | `() => void` | Removes this pane (and its active tab) from the layout tree. |
171
- | `metadata` | `Record<string, unknown> \| undefined` | The metadata values associated with the active tab. |
172
- | `updateMetadata` | `(updater: (current: Record<string, unknown> \| undefined) => Record<string, unknown> \| undefined) => void` | Updates metadata of the active tab via an updater function. |
173
- | `locked` | `boolean` | `true` if this specific pane or the dashboard globally is locked. |
174
- | `tabs` | `string[]` | The array of tab IDs in this pane. |
175
- | `activeTabId` | `string` | The currently active tab ID. |
176
- | `selectTab` | `(tabId: string) => void` | Selects a specific tab to make it active. |
177
- | `removeTab` | `(tabId: string) => void` | Removes/closes a specific tab. |
178
- | `tabsMetadata` | `Record<string, Record<string, unknown>> \| undefined` | Metadata values associated with all tabs in this pane. |
179
- | `updateTabMetadata` | `(tabId: string, updater: (current: Record<string, unknown> \| undefined) => Record<string, unknown> \| undefined) => void` | Updates the metadata of a specific tab. |
180
- | `renderActiveTab` | `() => ReactNode` | Renders the portal placeholder for the currently active tab in the pane. |
161
+ Wraps the individual pane components inside the renderer. It acts as a context provider and container. Any pane state or handlers should be accessed via `usePaneContext()` or compound sub-components.
181
162
 
182
- ### `<Tabs>`
163
+ | Prop | Type | Required | Description |
164
+ | ---------- | --------------------- | -------- | ----------------------------------------------------------------------- |
165
+ | `id` | `string` | Yes | The unique ID corresponding to a `PaneNode`'s `paneId`. |
166
+ | `children` | `React.ReactNode` | Yes | Children components inside the pane (e.g. tabs, drag handles, content). |
167
+ | `style` | `React.CSSProperties` | No | Optional inline CSS styles applied to the pane outer container. |
168
+ | `locked` | `boolean` | No | Optional override to lock this specific pane (disables drag and drop). |
183
169
 
184
- A helper component that renders a list of tab items for a pane, wrapping the internal drag-and-drop tab logic.
170
+ #### Compound Sub-components
185
171
 
186
- | Prop | Type | Required | Description |
187
- | :------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------- | :------------------------------------------------------------------- |
188
- | `tabs` | `string[]` | Yes | The list of tab IDs in this pane. |
189
- | `activeTabId` | `string` | Yes | The currently active tab ID. |
190
- | `locked` | `boolean` | No | Whether dragging/reordering tabs is disabled (defaults to `false`). |
191
- | `tabsMetadata` | `Record<string, Record<string, any>>` | No | Metadata associated with each tab. |
192
- | `selectTab` | `(id: string) => void` | Yes | Callback when a tab is selected. |
193
- | `removeTab` | `(id: string) => void` | Yes | Callback when a tab is closed/removed. |
194
- | `classNames` | `{ container?: string; tab?: string \| ((tabId: string) => string) }` | No | Custom class names for the container and individual tab items. |
195
- | `styles` | `{ container?: CSSProperties; tab?: CSSProperties \| ((tabId: string) => CSSProperties) }` | No | Custom inline CSS styles for the container and individual tab items. |
196
- | `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. |
172
+ - **`<Pane.Content>`**: Renders the portal target for the active tab content.
173
+ - `children` can be a callback function `(tab: TabDetails) => React.ReactNode` or static `React.ReactNode`.
174
+ - Accepts `className` and `style` props.
175
+ - **`<Pane.DragHandle>`**: Defines the interactive drag region.
176
+ - **`<Pane.Tabs>`**: Renders the list of tab items for the pane.
177
+ - **`<Pane.Tab>`**: Renders an individual tab item.
178
+ - **`<Pane.Controls>`**: A headless wrapper for pane control buttons (fullscreen, close, etc.).
197
179
 
198
- ### `useTabContext()`
180
+ #### Hook: `usePaneContext()`
199
181
 
200
- A context hook that provides per-tab state from within a `<Tab>` component rendered inside `<Tabs>`. Must be used within a rendered tab child.
182
+ Provides direct access to the pane's state and action handlers from within any child component of `<Pane>`. Returns `PaneContextValue` which extends `PaneRenderProps`:
201
183
 
202
184
  ```ts
203
- const { tabId, isActive, isDragging, isOver, metadata, locked, selectTab, removeTab } =
204
- useTabContext()
185
+ const { id, isDragging, isFullscreen, toggleFullscreen, remove, tabs, activeTabId } =
186
+ usePaneContext()
205
187
  ```
206
188
 
207
- | Property | Type | Description |
208
- | ------------ | -------------------------------------- | -------------------------------------------------- |
209
- | `tabId` | `string` | The ID of the tab this context belongs to. |
210
- | `isActive` | `boolean` | Whether this tab is the currently active tab. |
211
- | `isDragging` | `boolean` | Whether this tab is currently being dragged. |
212
- | `isOver` | `boolean` | Whether a dragged item is currently over this tab. |
213
- | `metadata` | `Record<string, unknown> \| undefined` | Custom metadata associated with this tab. |
214
- | `locked` | `boolean` | Whether this tab (or the dashboard) is locked. |
215
- | `selectTab` | `() => void` | Selects this tab (no argument needed). |
216
- | `removeTab` | `() => void` | Removes/closes this tab (no argument needed). |
189
+ #### Pane Context Value: `PaneRenderProps`
190
+
191
+ | Parameter | Type | Description |
192
+ | ------------------- | --------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
193
+ | `isDragging` | `boolean` | `true` if the pane is actively being dragged. |
194
+ | `isFullscreen` | `boolean` | `true` if the pane currently occupies the fullscreen view. |
195
+ | `toggleFullscreen` | `() => void` | Toggles the pane to and from fullscreen/zoomed mode. |
196
+ | `remove` | `() => void` | Removes this pane (and its active tab) from the layout tree. |
197
+ | `metadata` | `Record<string, unknown> \| undefined` | The metadata values associated with the active tab. |
198
+ | `updateMetadata` | `(updater: (current: Record<string, unknown> \| undefined) => Record<string, unknown> \| undefined) => void` | Updates metadata of the active tab via an updater function. |
199
+ | `locked` | `boolean` | `true` if this specific pane or the dashboard globally is locked. |
200
+ | `tabs` | `string[]` | The array of tab IDs in this pane. |
201
+ | `activeTabId` | `string` | The currently active tab ID. |
202
+ | `selectTab` | `(tabId: string) => void` | Selects a specific tab to make it active. |
203
+ | `removeTab` | `(tabId: string) => void` | Removes/closes a specific tab. |
204
+ | `tabsMetadata` | `Record<string, Record<string, unknown>> \| undefined` | Metadata values associated with all tabs in this pane. |
205
+ | `updateTabMetadata` | `(tabId: string, updater: (current: Record<string, unknown> \| undefined) => Record<string, unknown> \| undefined) => void` | Updates the metadata of a specific tab. |
206
+
207
+ ### `<Tabs>`
208
+
209
+ A helper component that renders a list of tab items for a pane, wrapping the internal drag-and-drop tab logic.
210
+
211
+ | Prop | Type | Required | Description |
212
+ | :------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------- | :------------------------------------------------------------------- |
213
+ | `tabs` | `string[]` | Yes | The list of tab IDs in this pane. |
214
+ | `activeTabId` | `string` | Yes | The currently active tab ID. |
215
+ | `locked` | `boolean` | No | Whether dragging/reordering tabs is disabled (defaults to `false`). |
216
+ | `tabsMetadata` | `Record<string, Record<string, any>>` | No | Metadata associated with each tab. |
217
+ | `selectTab` | `(id: string) => void` | Yes | Callback when a tab is selected. |
218
+ | `removeTab` | `(id: string) => void` | Yes | Callback when a tab is closed/removed. |
219
+ | `classNames` | `{ container?: string; tab?: string \| ((tabId: string) => string) }` | No | Custom class names for the container and individual tab items. |
220
+ | `styles` | `{ container?: CSSProperties; tab?: CSSProperties \| ((tabId: string) => CSSProperties) }` | No | Custom inline CSS styles for the container and individual tab items. |
221
+ | `renderTab` | `(props: { tabId: string; activeTabId: string; isDragging: boolean; isOver: boolean; metadata?: Record<string, unknown>; onSelect: () => void; onRemove: () => void }) => ReactNode` | Yes | Render prop function called for each tab item. |
217
222
 
218
223
  ### `<DragHandle>`
219
224
 
@@ -378,10 +383,10 @@ export interface TabDetails {
378
383
 
379
384
  ### Component Props
380
385
 
381
- ````ts
386
+ ```ts
382
387
  export interface PaneProps {
383
388
  id: string
384
- children: (props: PaneRenderProps) => React.ReactNode
389
+ children: React.ReactNode
385
390
  style?: React.CSSProperties
386
391
  locked?: boolean
387
392
  }
@@ -405,8 +410,8 @@ export interface TabsProps {
405
410
  isDragging: boolean
406
411
  isOver: boolean
407
412
  metadata?: Record<string, unknown>
408
- selectTab: (id: string) => void
409
- removeTab: (id: string) => void
413
+ onSelect: () => void
414
+ onRemove: () => void
410
415
  }) => React.ReactNode
411
416
  classNames?: {
412
417
  container?: string
@@ -417,6 +422,7 @@ export interface TabsProps {
417
422
  tab?: React.CSSProperties | ((tabId: string) => React.CSSProperties)
418
423
  }
419
424
  }
425
+ ```
420
426
 
421
427
  ### Render Props
422
428
 
@@ -440,71 +446,57 @@ export interface PaneRenderProps {
440
446
  tabId: string,
441
447
  updater: (current: Record<string, unknown> | undefined) => Record<string, unknown> | undefined,
442
448
  ) => void
443
- renderActiveTab: () => ReactNode
444
449
  }
445
450
 
446
- export interface TabContextValue {
447
- tabId: string
448
- isActive: boolean
449
- isDragging: boolean
450
- isOver: boolean
451
- metadata?: Record<string, unknown>
452
- locked: boolean
453
- selectTab: () => void
454
- removeTab: () => void
455
451
  }
456
- ````
452
+ ```
457
453
 
458
454
  ### Controller & Context
459
455
 
460
456
  ```ts
461
- export interface UseZeugmaOptions { /* see useZeugma() section above */ }
462
-
463
- export interface ZeugmaController {
464
- // State
457
+ export interface ZeugmaState {
465
458
  layout: TreeNode | null
466
- setLayout: Dispatch<SetStateAction<TreeNode | null>>
467
459
  fullscreenPaneId: string | null
468
- setFullscreenPaneId: (paneId: string | null) => void
469
460
  locked: boolean
470
- setLocked: Dispatch<SetStateAction<boolean>>
471
-
472
- // DnD state
473
- activeId: string | null
474
- activeType: 'pane' | 'tab' | null
475
- dismissIntentId: string | null
461
+ }
476
462
 
477
- // Config
478
- dragActivationDistance: number
479
- snapThreshold: number
480
- minSplitPercentage: number
481
- maxSplitPercentage: number
482
- enableDragToDismiss: boolean
483
- dismissThreshold: number
463
+ export interface ZeugmaStateSetters {
464
+ setLayout: Dispatch<SetStateAction<TreeNode | null>>
465
+ setFullscreenPaneId: (paneId: string | null) => void
466
+ setLocked: Dispatch<SetStateAction<boolean>>
467
+ }
484
468
 
485
- // Public actions
469
+ export interface ZeugmaActions {
486
470
  removePane: (paneId: string) => void
487
- addPane: (paneId: string, metadata?: Record<string, unknown>) => void
488
- addTab: (paneId: string, tabId: string, metadata?: Record<string, unknown>) => void
489
- updateTabMetadata: (tabId: string, updater: ...) => void
471
+ addTab: (tabId: string, targetPaneId?: string, metadata?: Record<string, unknown>) => void
472
+ updateMetadata: (
473
+ id: string,
474
+ updater: (current: Record<string, unknown> | undefined) => Record<string, unknown> | undefined,
475
+ ) => void
490
476
  updatePaneLock: (paneId: string, locked: boolean) => void
491
477
  selectTab: (paneId: string, tabId: string) => void
492
478
  mergeTab: (draggedTabId: string, targetPaneId: string) => void
493
479
  removeTab: (tabId: string) => void
494
- splitPane: (targetId: string, direction: SplitDirection, splitType: 'left' | 'right' | 'top' | 'bottom', paneToAdd: string) => void
480
+ splitPane: (
481
+ targetId: string,
482
+ direction: SplitDirection,
483
+ splitType: 'left' | 'right' | 'top' | 'bottom',
484
+ paneToAdd: string,
485
+ ) => void
495
486
  updateSplitPercentage: (currentNode: SplitNode, percentage: number) => void
496
487
  moveTab: (draggedTabId: string, targetTabId: string, position?: 'before' | 'after') => void
488
+ }
497
489
 
498
- // Public queries
490
+ export interface ZeugmaQueries {
499
491
  findPaneById: (paneId: string) => PaneNode | null
500
492
  findPaneContainingTab: (tabId: string) => PaneNode | null
501
493
  findTabById: (tabId: string) => TabDetails | null
494
+ getTabMetadata: (tabId: string) => Record<string, unknown> | undefined
495
+ getActiveTabMetadata: (paneId: string) => Record<string, unknown> | undefined
502
496
  }
503
497
 
504
- // ZeugmaContextValue is the combined state + actions interface
505
- // exposed by useZeugmaContext(). It includes all of ZeugmaController
506
- // plus the renderPane and classNames configuration.
507
- export interface ZeugmaContextValue extends ZeugmaStateValue, ZeugmaActionsValue {}
498
+ export interface ZeugmaController
499
+ extends ZeugmaState, ZeugmaStateSetters, ZeugmaActions, ZeugmaQueries {}
508
500
  ```
509
501
 
510
502
  ### Computed Layout Types (from `react-zeugma/utils`)