@dashfoo/react 0.1.0 → 0.3.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 +218 -51
- package/dist/index.d.ts +251 -22
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -59,24 +59,27 @@ a sized parent and add your CSS (see the [attribute reference](#data-dashfoo-att
|
|
|
59
59
|
|
|
60
60
|
## `DashfooLayout` props
|
|
61
61
|
|
|
62
|
-
| Prop | Type | Default | Description
|
|
63
|
-
| ------------------------- | -------------------------------------------------- | ------- |
|
|
64
|
-
| `model` | `Dashfoo` | — | Controlled document. When set, the prop is the source of truth.
|
|
65
|
-
| `defaultModel` | `Dashfoo` | — | Uncontrolled initial document. The component owns it from there.
|
|
66
|
-
| `onModelChange` | `(model: Dashfoo, action?: Action) => void` | — | Called after every change with the next model and the action that caused it.
|
|
67
|
-
| `components` | `Record<string, ComponentType<{ node: TabNode }>>` | — | Registry mapping `tab.component` keys to components.
|
|
68
|
-
| `factory` | `(tab: TabNode) => ReactNode` | — | Render override. When provided, it resolves every tab and `components` is ignored.
|
|
69
|
-
| `persist` | `string \| { key; storage?; debounceMs? }` | — | Auto-save the model (uncontrolled mode only). A bare string is a localStorage key.
|
|
70
|
-
| `onAction` | `(action: Action) => Action \| null` | — | Intercept each action before it commits — return it, a replacement, or `null` to veto.
|
|
71
|
-
| `onActiveTabsetChange` | `(id: string \| undefined) => void` | — | Fires when the active tabset changes.
|
|
72
|
-
| `onMaximizedTabsetChange` | `(id: string \| undefined) => void` | — | Fires when a tabset is maximized or restored.
|
|
73
|
-
| `renderTabLabel` | `(tab: TabNode) => ReactNode` | — | Override the tab label (the accessible name stays `tab.name`).
|
|
74
|
-
| `renderTabsetToolbar` | `(tabset: TabsetNode) => ReactNode` | — | Inject custom controls into a tabset's toolbar.
|
|
75
|
-
| `
|
|
76
|
-
| `
|
|
77
|
-
| `
|
|
78
|
-
| `
|
|
79
|
-
| `
|
|
62
|
+
| Prop | Type | Default | Description |
|
|
63
|
+
| ------------------------- | -------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
|
64
|
+
| `model` | `Dashfoo` | — | Controlled document. When set, the prop is the source of truth. |
|
|
65
|
+
| `defaultModel` | `Dashfoo` | — | Uncontrolled initial document. The component owns it from there. |
|
|
66
|
+
| `onModelChange` | `(model: Dashfoo, action?: Action) => void` | — | Called after every change with the next model and the action that caused it. |
|
|
67
|
+
| `components` | `Record<string, ComponentType<{ node: TabNode }>>` | — | Registry mapping `tab.component` keys to components. |
|
|
68
|
+
| `factory` | `(tab: TabNode) => ReactNode` | — | Render override. When provided, it resolves every tab and `components` is ignored. |
|
|
69
|
+
| `persist` | `string \| { key; storage?; debounceMs? }` | — | Auto-save the model (uncontrolled mode only). A bare string is a localStorage key. |
|
|
70
|
+
| `onAction` | `(action: Action) => Action \| null` | — | Intercept each action before it commits — return it, a replacement, or `null` to veto. |
|
|
71
|
+
| `onActiveTabsetChange` | `(id: string \| undefined) => void` | — | Fires when the active tabset changes. |
|
|
72
|
+
| `onMaximizedTabsetChange` | `(id: string \| undefined) => void` | — | Fires when a tabset is maximized or restored. |
|
|
73
|
+
| `renderTabLabel` | `(tab: TabNode) => ReactNode` | — | Override the tab label (the accessible name stays `tab.name`). |
|
|
74
|
+
| `renderTabsetToolbar` | `(tabset: TabsetNode) => ReactNode` | — | Inject custom controls into a tabset's toolbar. |
|
|
75
|
+
| `editable` | `boolean` | `true` | `false` renders a static layout: no drag, close, rename, or splitter resize. Tab selection, maximize, and the ref API keep working. |
|
|
76
|
+
| `closableTabs` | `boolean` | `true` | Show the per-tab close control. |
|
|
77
|
+
| `renamableTabs` | `boolean` | `true` | Allow double-click inline rename. |
|
|
78
|
+
| `maximizable` | `boolean` | `true` | Show the tabset maximize/restore control. |
|
|
79
|
+
| `draggableTabs` | `boolean` | `true` | Allow individual tabs to be dragged between tabsets. |
|
|
80
|
+
| `draggableTabsets` | `boolean` | `true` | Show the grip that drags a whole tabset. |
|
|
81
|
+
| `resizableSplits` | `boolean` | `true` | Allow dragging the splitters; `false` disables them in place (gutters keep their size). |
|
|
82
|
+
| `keepMounted` | `boolean` | `false` | Keep inactive tab panels mounted (hidden) so their state survives a tab switch. |
|
|
80
83
|
|
|
81
84
|
You must pass either `model` or `defaultModel`. Passing neither throws.
|
|
82
85
|
|
|
@@ -175,19 +178,22 @@ with the right role and ARIA wiring, ready for you to style.
|
|
|
175
178
|
|
|
176
179
|
### Per-node enable flags
|
|
177
180
|
|
|
178
|
-
The
|
|
181
|
+
The top-level chrome props are layout-wide gates. Individual nodes can opt out
|
|
179
182
|
through optional boolean fields in the model (a flag defaults to enabled unless
|
|
180
|
-
explicitly `false`). A control shows only when
|
|
181
|
-
node's flag allow it
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
|
185
|
-
|
|
|
186
|
-
| `enableClose`
|
|
187
|
-
| `
|
|
188
|
-
| `
|
|
189
|
-
| `
|
|
190
|
-
| `
|
|
183
|
+
explicitly `false`). A control shows only when the `editable` umbrella, the
|
|
184
|
+
chrome prop, the model global, and the node's flag all allow it (`maximizable`
|
|
185
|
+
ignores `editable` — maximize is view state, not a structural edit).
|
|
186
|
+
|
|
187
|
+
| Field | On node | Disables |
|
|
188
|
+
| ------------------- | ------- | ------------------------------------------------ |
|
|
189
|
+
| `enableClose` | tab | the close control for that tab |
|
|
190
|
+
| `enableClose` | tabset | closing for every tab in the tabset |
|
|
191
|
+
| `enableRename` | tab | double-click rename for that tab |
|
|
192
|
+
| `enableDrag` | tab | dragging that tab |
|
|
193
|
+
| `enableMaximize` | tabset | the maximize control for that tabset |
|
|
194
|
+
| `tabEnableDrag` | global | tree-wide default behind `tab.enableDrag` |
|
|
195
|
+
| `enableSplitDock` | global | splitting a tabset on drop (drops stack instead) |
|
|
196
|
+
| `enableSplitResize` | global | dragging the splitters (gutters keep their size) |
|
|
191
197
|
|
|
192
198
|
```ts
|
|
193
199
|
{ id: "logs", type: "tab", name: "Logs", component: "logs", enableClose: false }
|
|
@@ -200,6 +206,50 @@ Rows and tabsets can carry `min` and `max` dimensions from `@dashfoo/core`.
|
|
|
200
206
|
tabset has no node-level `min`, it uses `global.tabSetMinSize`; when that global
|
|
201
207
|
is omitted, the React adapter falls back to `320px`.
|
|
202
208
|
|
|
209
|
+
## External drag sources
|
|
210
|
+
|
|
211
|
+
Tabs normally move within a layout. To drag new content in from outside it — a
|
|
212
|
+
widget list, a palette, a marketplace — wrap both sides in `DashfooDragProvider`
|
|
213
|
+
and register each source with `useExternalTabSource`. Dropping a source on the
|
|
214
|
+
layout inserts the tab it creates (stacking on a strip or body, splitting on an
|
|
215
|
+
edge), exactly like an internal tab drop. A drop outside any tabset is a no-op.
|
|
216
|
+
|
|
217
|
+
```tsx
|
|
218
|
+
import { createTabId, tab } from "@dashfoo/core";
|
|
219
|
+
import { DashfooDragProvider, DashfooLayout, useExternalTabSource } from "@dashfoo/react";
|
|
220
|
+
|
|
221
|
+
const WidgetCard = ({ component, name }: { component: string; name: string }) => {
|
|
222
|
+
const { ref } = useExternalTabSource({
|
|
223
|
+
createTab: () => tab(component, name, { id: createTabId() }),
|
|
224
|
+
label: name,
|
|
225
|
+
});
|
|
226
|
+
return <div ref={ref}>{name}</div>;
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const App = () => (
|
|
230
|
+
<DashfooDragProvider>
|
|
231
|
+
<WidgetCard component="metrics" name="Metrics" />
|
|
232
|
+
<DashfooLayout defaultModel={model} components={registry} />
|
|
233
|
+
</DashfooDragProvider>
|
|
234
|
+
);
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
`createTab` runs at drag start and must return a fresh `TabNode` with a unique
|
|
238
|
+
id per call (`createNodeId` / `createTabId` from `@dashfoo/core` mint one). The
|
|
239
|
+
returned node is validated against the model schema; an invalid tab warns and
|
|
240
|
+
cancels the drag. `DashfooDragProvider` is optional everywhere else — a
|
|
241
|
+
standalone layout needs no provider. Pointer drag is the only drag input, so
|
|
242
|
+
offer a click-to-add path (e.g. `ref.addTab(...)`) alongside the drag for
|
|
243
|
+
keyboard access.
|
|
244
|
+
|
|
245
|
+
`useExternalTabSource` options:
|
|
246
|
+
|
|
247
|
+
| Option | Type | Default | Description |
|
|
248
|
+
| ----------- | --------------- | ------------ | ------------------------------------------ |
|
|
249
|
+
| `createTab` | `() => TabNode` | — (required) | Builds the tab to insert; called per drag. |
|
|
250
|
+
| `label` | `string` | `""` | The drag preview chip text. |
|
|
251
|
+
| `disabled` | `boolean` | `false` | Unregisters the source while true. |
|
|
252
|
+
|
|
203
253
|
## Persistence
|
|
204
254
|
|
|
205
255
|
The `persist` prop saves an uncontrolled layout and restores it on load. It loads
|
|
@@ -228,8 +278,10 @@ const Layout = () => {
|
|
|
228
278
|
|
|
229
279
|
`persist` accepts a bare localStorage key or `{ key, storage?, debounceMs? }` for
|
|
230
280
|
a custom store (sessionStorage, in-memory, your own). A pending save flushes on
|
|
231
|
-
unmount
|
|
232
|
-
|
|
281
|
+
unmount and on page hide (reload, tab close, navigation, the page going to the
|
|
282
|
+
background), so the last change is never lost — even inside the debounce window.
|
|
283
|
+
It applies to uncontrolled mode only; in controlled mode, save the model yourself
|
|
284
|
+
in `onModelChange`.
|
|
233
285
|
|
|
234
286
|
The lower-level `usePersistence` load/save primitive (that `persist` is built on)
|
|
235
287
|
is also exported, for hosts that drive the store directly. See the
|
|
@@ -299,7 +351,7 @@ you.
|
|
|
299
351
|
| `tabset-grip` | `button` | Drags the whole tabset. `aria-label="Move tabset"`; shown when `draggableTabsets` is on and the tabset is not maximized. |
|
|
300
352
|
| `tabset-maximize` | `button` | Maximize/restore toggle. `aria-pressed` reflects state. |
|
|
301
353
|
| `tabcontent` | `div` | `role="tabpanel"`, the active tab's content (or empty when none). |
|
|
302
|
-
| `dock-indicator` | `div` | The drag preview overlay (insertion line or zone). `pointer-events: none`.
|
|
354
|
+
| `dock-indicator` | `div` | The drag preview overlay (insertion line or zone pane). `pointer-events: none`. |
|
|
303
355
|
| `drag-preview` | `div` | The chip that follows the pointer during a drag, showing the dragged label. |
|
|
304
356
|
| `separator` | rrp `Separator` | rrp emits `data-separator` with `aria-orientation`; style splitters here. |
|
|
305
357
|
|
|
@@ -314,15 +366,14 @@ The `[data-dashfoo="dock-indicator"]` overlay positions itself inline, but every
|
|
|
314
366
|
visual property reads from a CSS variable with a neutral fallback. Override them
|
|
315
367
|
to theme the drag preview without touching layout.
|
|
316
368
|
|
|
317
|
-
| Variable | Used for
|
|
318
|
-
| ----------------------------- |
|
|
319
|
-
| `--dashfoo-dock-fill` |
|
|
320
|
-
| `--dashfoo-dock-border` |
|
|
321
|
-
| `--dashfoo-dock-border-width` |
|
|
322
|
-
| `--dashfoo-dock-radius` |
|
|
323
|
-
| `--dashfoo-dock-line`
|
|
324
|
-
| `--dashfoo-dock-
|
|
325
|
-
| `--dashfoo-dock-transition` | indicator move animation | `left 60ms, top 60ms, width 60ms, height 60ms` |
|
|
369
|
+
| Variable | Used for | Fallback |
|
|
370
|
+
| ----------------------------- | ------------------------ | ---------------------------------------------- |
|
|
371
|
+
| `--dashfoo-dock-fill` | indicator fill | `oklch(0.556 0 0 / 0.18)` |
|
|
372
|
+
| `--dashfoo-dock-border` | indicator border color | `oklch(0.708 0 0 / 0.75)` |
|
|
373
|
+
| `--dashfoo-dock-border-width` | indicator border width | `1px` |
|
|
374
|
+
| `--dashfoo-dock-radius` | indicator corner radius | `6px` |
|
|
375
|
+
| `--dashfoo-dock-line-radius` | insertion-line radius | `2px` |
|
|
376
|
+
| `--dashfoo-dock-transition` | indicator move animation | `left 60ms, top 60ms, width 60ms, height 60ms` |
|
|
326
377
|
|
|
327
378
|
Set `--dashfoo-dock-transition: none` to disable the indicator animation.
|
|
328
379
|
|
|
@@ -330,7 +381,6 @@ Set `--dashfoo-dock-transition: none` to disable the indicator animation.
|
|
|
330
381
|
:root {
|
|
331
382
|
--dashfoo-dock-fill: rgba(255, 255, 255, 0.1);
|
|
332
383
|
--dashfoo-dock-border: rgba(255, 255, 255, 0.4);
|
|
333
|
-
--dashfoo-dock-line: rgba(255, 255, 255, 0.85);
|
|
334
384
|
}
|
|
335
385
|
```
|
|
336
386
|
|
|
@@ -348,8 +398,106 @@ It binds an XState actor to React, normalizes the incoming model, and exposes
|
|
|
348
398
|
`dispatch` plus undo/redo. Uncontrolled mode gives full history; controlled mode
|
|
349
399
|
routes changes through `onModelChange` and keeps the actor in sync.
|
|
350
400
|
|
|
351
|
-
|
|
352
|
-
|
|
401
|
+
Three selector hooks read the scoped stores the primitives coordinate through.
|
|
402
|
+
Each takes a selector and re-renders only when the selected slice changes:
|
|
403
|
+
|
|
404
|
+
```ts
|
|
405
|
+
const dispatch = useLayout((state) => state.dispatch); // layout-wide config + dispatch; throws outside <DashfooLayout>/<Layout.Root>
|
|
406
|
+
const node = useTabset((state) => state.node); // per-tabset state; throws outside <Tabset.Root>
|
|
407
|
+
const { index, tab } = useTab(); // which tab a part belongs to; throws outside <Tabset.Tab>
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
`useDragSubject()` returns the live drag subject (`{ kind, id }` for a tab or
|
|
411
|
+
tabset being dragged) or `null` — including outside a drag layer — for
|
|
412
|
+
drag-aware styling in custom parts.
|
|
413
|
+
|
|
414
|
+
`useDropIntent()` returns the live drop intent (`{ targetId, location, index? }`
|
|
415
|
+
— where the drag would land if dropped right now) or `null` when nothing is
|
|
416
|
+
dragging or the pointer is over no valid target (empty space, a non-editable
|
|
417
|
+
layout, or a no-op drop the library suppresses). Together with
|
|
418
|
+
`dockZonePolygons` from `@dashfoo/core` it supports consumer-rendered drop
|
|
419
|
+
indicators and drop-zone visualizations alongside the built-in indicator. Both
|
|
420
|
+
hooks work anywhere under a layout; under a `DashfooDragProvider` they work
|
|
421
|
+
anywhere under the provider (the provider hosts the drag store, so widget lists
|
|
422
|
+
and overlays outside the layout observe the same drag).
|
|
423
|
+
|
|
424
|
+
## Build your own layout
|
|
425
|
+
|
|
426
|
+
`DashfooLayout` is a thin assembly of exported primitives — you can compose the
|
|
427
|
+
same parts yourself when you need custom chrome. Two compound namespaces, same
|
|
428
|
+
pattern as `Panel`:
|
|
429
|
+
|
|
430
|
+
| Part | Element | Role |
|
|
431
|
+
| ----------------------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
|
432
|
+
| `Layout.Root` | `div[data-dashfoo="layout"]` | Creates the layout store. Takes `model`, `dispatch`, `renderTab`, and the chrome flags `DashfooLayout` accepts. |
|
|
433
|
+
| `Layout.DragLayer` | none (overlays) | Opts the tree into drag-dock. Omit it and everything else still works, just without dragging. |
|
|
434
|
+
| `Layout.Rows` | rrp split tree | Renders a `RowNode` recursively. `renderTabset` swaps in a custom tabset composition at every leaf. |
|
|
435
|
+
| `Layout.Tabset` | — | The stock tabset composition, for leaves that don't need custom chrome. |
|
|
436
|
+
| `Tabset.Root` | `div[data-dashfoo="tabset"]` | Creates the per-tabset store; owns drop registration, overflow measurement, and focus restore after close. |
|
|
437
|
+
| `Tabset.TabStrip` | `div[data-dashfoo="tabstrip"]` | The strip row (drag hit-testing targets this attribute). |
|
|
438
|
+
| `Tabset.Tablist` | `div[data-dashfoo="tablist"]` | `role="tablist"` + roving-tabindex arrow/Home/End navigation. |
|
|
439
|
+
| `Tabset.Tab` | `span[data-dashfoo="tab-item"]` | Per-tab wrapper; provides identity to the parts inside. |
|
|
440
|
+
| `Tabset.Trigger` | `button[data-dashfoo="tab"]` | The tab button: select on click, rename on double-click, draggable. Children override the label. |
|
|
441
|
+
| `Tabset.RenameInput` | `input[data-dashfoo="tab-rename"]` | Inline rename editor; renders only while its tab is being renamed. |
|
|
442
|
+
| `Tabset.CloseButton` | `button[data-dashfoo="tab-close"]` | Closes the tab with focus restore; hides when the tab isn't closable. |
|
|
443
|
+
| `Tabset.Content` | `div[data-dashfoo="tabcontent"]` | The `role="tabpanel"` pane(s); honors `keepMounted`. Children render-prop overrides `renderTab`. |
|
|
444
|
+
| `Tabset.Toolbar` | `div[data-dashfoo="tabset-toolbar"]` | Trailing toolbar container. |
|
|
445
|
+
| `Tabset.OverflowMenu` | menu button | Lists clipped tabs; hides when nothing overflows. |
|
|
446
|
+
| `Tabset.Grip` | `button[data-dashfoo="tabset-grip"]` | Drag handle for the whole tabset; hides when tabset dragging is off. |
|
|
447
|
+
| `Tabset.MaximizeButton` | `button[data-dashfoo="tabset-maximize"]` | Maximize/restore toggle; hides when maximize is off. |
|
|
448
|
+
|
|
449
|
+
All parts spread native props (`className`, `style`, handlers) like `Panel`;
|
|
450
|
+
the structural attributes (`role`, ids, `data-dashfoo`) are applied after the
|
|
451
|
+
spread because drag hit-testing and overflow measurement query them.
|
|
452
|
+
|
|
453
|
+
```tsx
|
|
454
|
+
import { Layout, Tabset, useDashfooStore, useTab } from "@dashfoo/react";
|
|
455
|
+
|
|
456
|
+
const MyTabset = ({ node }: { node: TabsetNode }) => (
|
|
457
|
+
<Tabset.Root node={node}>
|
|
458
|
+
<Tabset.TabStrip>
|
|
459
|
+
<Tabset.Tablist>
|
|
460
|
+
{node.children.map((tab) => (
|
|
461
|
+
<Tabset.Tab key={tab.id} tab={tab}>
|
|
462
|
+
<Tabset.Trigger>
|
|
463
|
+
<MyLabel /> {/* reads useTab()/useTabset() */}
|
|
464
|
+
</Tabset.Trigger>
|
|
465
|
+
<Tabset.RenameInput />
|
|
466
|
+
<Tabset.CloseButton />
|
|
467
|
+
</Tabset.Tab>
|
|
468
|
+
))}
|
|
469
|
+
</Tabset.Tablist>
|
|
470
|
+
<Tabset.Toolbar>
|
|
471
|
+
<Tabset.OverflowMenu />
|
|
472
|
+
<Tabset.MaximizeButton />
|
|
473
|
+
<Tabset.Grip />
|
|
474
|
+
</Tabset.Toolbar>
|
|
475
|
+
</Tabset.TabStrip>
|
|
476
|
+
<Tabset.Content />
|
|
477
|
+
</Tabset.Root>
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
const MyLayout = () => {
|
|
481
|
+
const store = useDashfooStore({ defaultModel });
|
|
482
|
+
return (
|
|
483
|
+
<Layout.Root dispatch={store.dispatch} model={store.model} renderTab={renderTab}>
|
|
484
|
+
<Layout.DragLayer>
|
|
485
|
+
<Layout.Rows node={store.model.layout} renderTabset={(node) => <MyTabset node={node} />} />
|
|
486
|
+
</Layout.DragLayer>
|
|
487
|
+
</Layout.Root>
|
|
488
|
+
);
|
|
489
|
+
};
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
Maximize is the host's concern in a hand-built layout: when
|
|
493
|
+
`model.maximizedTabsetId` is set, render that tabset alone (via `findTabset`
|
|
494
|
+
from `@dashfoo/core`) instead of `Layout.Rows` — that's all `DashfooLayout`
|
|
495
|
+
does. The demo's "Raw primitives" page (`apps/demo-vite/src/pages/raw.tsx`) is
|
|
496
|
+
a complete working reference.
|
|
497
|
+
|
|
498
|
+
Misuse is never silent: parts outside their provider throw, and soft mistakes
|
|
499
|
+
(two `Tablist`s, a `Tab` whose node isn't in the tabset, renaming without a
|
|
500
|
+
`RenameInput`) warn with `[dashfoo]` and degrade gracefully.
|
|
353
501
|
|
|
354
502
|
## Exports
|
|
355
503
|
|
|
@@ -371,15 +519,34 @@ type PanelIconProps;
|
|
|
371
519
|
type PanelRootProps;
|
|
372
520
|
type PanelTitleProps;
|
|
373
521
|
|
|
374
|
-
//
|
|
522
|
+
// Layout primitives (build your own layout)
|
|
523
|
+
Layout; // Layout.Root / Layout.DragLayer / Layout.Rows / Layout.Tabset
|
|
524
|
+
type LayoutRootProps;
|
|
525
|
+
type LayoutDragLayerProps;
|
|
526
|
+
type LayoutRowsProps;
|
|
527
|
+
type LayoutTabsetProps;
|
|
528
|
+
|
|
529
|
+
// Tabset primitives
|
|
530
|
+
Tabset; // Tabset.Root / .TabStrip / .Tablist / .Tab / .Trigger / .RenameInput / .CloseButton / .Content / .Toolbar / .OverflowMenu / .Grip / .MaximizeButton
|
|
531
|
+
type TabsetRootProps; // …and a Props type per part
|
|
532
|
+
|
|
533
|
+
// Store + selector hooks
|
|
375
534
|
useDashfooStore;
|
|
376
535
|
type DashfooStore;
|
|
377
536
|
type UseDashfooStoreOptions;
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
537
|
+
useLayout; // select from the layout store (throws outside Layout.Root/DashfooLayout)
|
|
538
|
+
type LayoutState;
|
|
539
|
+
useTabset; // select from the enclosing tabset store (throws outside Tabset.Root)
|
|
540
|
+
type TabsetState;
|
|
541
|
+
useTab; // { tab, index } identity (throws outside Tabset.Tab)
|
|
542
|
+
type TabContextValue;
|
|
543
|
+
useDragSubject; // the live drag subject, or null
|
|
544
|
+
useDropIntent; // where the drag would land if dropped right now, or null
|
|
545
|
+
|
|
546
|
+
// External drag sources
|
|
547
|
+
DashfooDragProvider; // shares one drag manager + drag store between a layout and outside sources
|
|
548
|
+
useExternalTabSource;
|
|
549
|
+
type ExternalTabSourceOptions;
|
|
383
550
|
|
|
384
551
|
// Persistence
|
|
385
552
|
usePersistence; // load/save primitive (the `persist` prop builds on this)
|
package/dist/index.d.ts
CHANGED
|
@@ -1,25 +1,6 @@
|
|
|
1
|
+
import { Action, Dashfoo, DockLocation, DragSubject, DropIntent, RowNode, TabNode, TabsetNode } from "@dashfoo/core";
|
|
1
2
|
import { ComponentProps, ComponentType, ReactNode } from "react";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
//#region src/hooks/context.d.ts
|
|
5
|
-
type DashfooContextValue = {
|
|
6
|
-
closableTabs: boolean;
|
|
7
|
-
dispatch: (action: Action) => void;
|
|
8
|
-
draggableTabsets: boolean;
|
|
9
|
-
keepMounted: boolean;
|
|
10
|
-
maximizable: boolean;
|
|
11
|
-
maximizedTabsetId?: string;
|
|
12
|
-
renamableTabs: boolean;
|
|
13
|
-
renderTab: (tab: TabNode) => ReactNode;
|
|
14
|
-
renderTabLabel?: (tab: TabNode) => ReactNode;
|
|
15
|
-
renderTabsetToolbar?: (tabset: TabsetNode) => ReactNode;
|
|
16
|
-
tabLocation: "bottom" | "top";
|
|
17
|
-
tabsetMinSize: number;
|
|
18
|
-
tabStripEnabled: boolean;
|
|
19
|
-
};
|
|
20
|
-
declare const DashfooContext: import("react").Context<DashfooContextValue | null>;
|
|
21
|
-
declare const useDashfooContext: () => DashfooContextValue;
|
|
22
|
-
//#endregion
|
|
3
|
+
import { DragDropManager } from "@dnd-kit/dom";
|
|
23
4
|
//#region src/hooks/persistence.d.ts
|
|
24
5
|
type StorageAdapter = {
|
|
25
6
|
getItem: (key: string) => string | null;
|
|
@@ -71,7 +52,9 @@ type DashfooLayoutProps = {
|
|
|
71
52
|
closableTabs?: boolean;
|
|
72
53
|
components?: Record<string, TabComponent>;
|
|
73
54
|
defaultModel?: Dashfoo;
|
|
55
|
+
draggableTabs?: boolean;
|
|
74
56
|
draggableTabsets?: boolean;
|
|
57
|
+
editable?: boolean;
|
|
75
58
|
factory?: (tab: TabNode) => ReactNode;
|
|
76
59
|
keepMounted?: boolean;
|
|
77
60
|
maximizable?: boolean;
|
|
@@ -84,9 +67,103 @@ type DashfooLayoutProps = {
|
|
|
84
67
|
renamableTabs?: boolean;
|
|
85
68
|
renderTabLabel?: (tab: TabNode) => ReactNode;
|
|
86
69
|
renderTabsetToolbar?: (tabset: TabsetNode) => ReactNode;
|
|
70
|
+
resizableSplits?: boolean;
|
|
87
71
|
};
|
|
88
72
|
declare const DashfooLayout: import("react").ForwardRefExoticComponent<DashfooLayoutProps & import("react").RefAttributes<DashfooHandle>>;
|
|
89
73
|
//#endregion
|
|
74
|
+
//#region src/components/drag-root.d.ts
|
|
75
|
+
declare const DashfooDragProvider: ({
|
|
76
|
+
children
|
|
77
|
+
}: {
|
|
78
|
+
children: ReactNode;
|
|
79
|
+
}) => ReactNode;
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region src/hooks/drag-hooks.d.ts
|
|
82
|
+
type ExternalTabSourceOptions = {
|
|
83
|
+
createTab: () => TabNode;
|
|
84
|
+
disabled?: boolean;
|
|
85
|
+
label?: string;
|
|
86
|
+
};
|
|
87
|
+
declare const useExternalTabSource: ({
|
|
88
|
+
createTab,
|
|
89
|
+
disabled,
|
|
90
|
+
label
|
|
91
|
+
}: ExternalTabSourceOptions) => {
|
|
92
|
+
ref: (element: Element | null) => void;
|
|
93
|
+
};
|
|
94
|
+
declare const useDragSubject: () => DragSubject | null;
|
|
95
|
+
declare const useDropIntent: () => DropIntent | null;
|
|
96
|
+
//#endregion
|
|
97
|
+
//#region src/components/drag-adapter.d.ts
|
|
98
|
+
type DragProviderProps = {
|
|
99
|
+
children: ReactNode;
|
|
100
|
+
onCommit?: (action: Action) => void;
|
|
101
|
+
splitDock?: boolean;
|
|
102
|
+
};
|
|
103
|
+
//#endregion
|
|
104
|
+
//#region src/components/layout-root.d.ts
|
|
105
|
+
type LayoutRootProps = Omit<ComponentProps<"div">, "children"> & {
|
|
106
|
+
children: ReactNode;
|
|
107
|
+
closableTabs?: boolean;
|
|
108
|
+
dispatch: (action: Action) => void;
|
|
109
|
+
draggableTabs?: boolean;
|
|
110
|
+
draggableTabsets?: boolean;
|
|
111
|
+
editable?: boolean;
|
|
112
|
+
keepMounted?: boolean;
|
|
113
|
+
maximizable?: boolean;
|
|
114
|
+
model: Dashfoo;
|
|
115
|
+
renamableTabs?: boolean;
|
|
116
|
+
renderTab: (tab: TabNode) => ReactNode;
|
|
117
|
+
renderTabLabel?: (tab: TabNode) => ReactNode;
|
|
118
|
+
renderTabsetToolbar?: (tabset: TabsetNode) => ReactNode;
|
|
119
|
+
resizableSplits?: boolean;
|
|
120
|
+
};
|
|
121
|
+
//#endregion
|
|
122
|
+
//#region src/components/row-view.d.ts
|
|
123
|
+
type RowViewProps = {
|
|
124
|
+
node: RowNode;
|
|
125
|
+
renderTabset?: (node: TabsetNode) => ReactNode;
|
|
126
|
+
};
|
|
127
|
+
//#endregion
|
|
128
|
+
//#region src/components/tabset/tabset-view.d.ts
|
|
129
|
+
type TabsetViewProps = {
|
|
130
|
+
node: TabsetNode;
|
|
131
|
+
};
|
|
132
|
+
//#endregion
|
|
133
|
+
//#region src/components/layout.d.ts
|
|
134
|
+
declare const Layout: {
|
|
135
|
+
readonly DragLayer: ({
|
|
136
|
+
children,
|
|
137
|
+
onCommit,
|
|
138
|
+
splitDock
|
|
139
|
+
}: DragProviderProps) => import("react").ReactNode;
|
|
140
|
+
readonly Root: ({
|
|
141
|
+
children,
|
|
142
|
+
closableTabs,
|
|
143
|
+
dispatch,
|
|
144
|
+
draggableTabs,
|
|
145
|
+
draggableTabsets,
|
|
146
|
+
editable,
|
|
147
|
+
keepMounted,
|
|
148
|
+
maximizable,
|
|
149
|
+
model,
|
|
150
|
+
renamableTabs,
|
|
151
|
+
renderTab,
|
|
152
|
+
renderTabLabel,
|
|
153
|
+
renderTabsetToolbar,
|
|
154
|
+
resizableSplits,
|
|
155
|
+
style,
|
|
156
|
+
...props
|
|
157
|
+
}: LayoutRootProps) => import("react").ReactNode;
|
|
158
|
+
readonly Rows: ({
|
|
159
|
+
node,
|
|
160
|
+
renderTabset
|
|
161
|
+
}: RowViewProps) => import("react").ReactNode;
|
|
162
|
+
readonly Tabset: ({
|
|
163
|
+
node
|
|
164
|
+
}: TabsetViewProps) => import("react").ReactNode;
|
|
165
|
+
};
|
|
166
|
+
//#endregion
|
|
90
167
|
//#region src/components/panel.d.ts
|
|
91
168
|
type PanelRootProps = ComponentProps<"div">;
|
|
92
169
|
type PanelHeaderProps = ComponentProps<"div">;
|
|
@@ -103,6 +180,158 @@ declare const Panel: {
|
|
|
103
180
|
readonly Title: (props: PanelTitleProps) => ReactNode;
|
|
104
181
|
};
|
|
105
182
|
//#endregion
|
|
183
|
+
//#region src/components/tabset/tabset-tab-controls.d.ts
|
|
184
|
+
type TabsetRenameInputProps = ComponentProps<"input">;
|
|
185
|
+
type TabsetCloseButtonProps = ComponentProps<"button">;
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region src/components/tabset/tabset-content.d.ts
|
|
188
|
+
type TabsetContentProps = Omit<ComponentProps<"div">, "children"> & {
|
|
189
|
+
children?: (tab: TabNode) => ReactNode;
|
|
190
|
+
};
|
|
191
|
+
//#endregion
|
|
192
|
+
//#region src/components/tabset/tabset-toolbar.d.ts
|
|
193
|
+
type TabsetToolbarProps = ComponentProps<"div">;
|
|
194
|
+
type TabsetGripProps = ComponentProps<"button">;
|
|
195
|
+
type TabsetMaximizeButtonProps = ComponentProps<"button">;
|
|
196
|
+
//#endregion
|
|
197
|
+
//#region src/components/tabset/tabset-root.d.ts
|
|
198
|
+
type TabsetRootProps = ComponentProps<"div"> & {
|
|
199
|
+
node: TabsetNode;
|
|
200
|
+
};
|
|
201
|
+
//#endregion
|
|
202
|
+
//#region src/components/tabset/tabset-tab.d.ts
|
|
203
|
+
type TabsetTabProps = ComponentProps<"span"> & {
|
|
204
|
+
tab: TabNode;
|
|
205
|
+
};
|
|
206
|
+
type TabsetTriggerProps = ComponentProps<"button">;
|
|
207
|
+
//#endregion
|
|
208
|
+
//#region src/components/tabset/tabset-tablist.d.ts
|
|
209
|
+
type TabsetTabStripProps = ComponentProps<"div">;
|
|
210
|
+
type TabsetTablistProps = ComponentProps<"div">;
|
|
211
|
+
//#endregion
|
|
212
|
+
//#region src/components/tabset/tabset.d.ts
|
|
213
|
+
declare const Tabset: {
|
|
214
|
+
readonly CloseButton: ({
|
|
215
|
+
children,
|
|
216
|
+
onClick,
|
|
217
|
+
...props
|
|
218
|
+
}: TabsetCloseButtonProps) => import("react").ReactNode;
|
|
219
|
+
readonly Content: ({
|
|
220
|
+
children,
|
|
221
|
+
style,
|
|
222
|
+
...props
|
|
223
|
+
}: TabsetContentProps) => import("react").ReactNode;
|
|
224
|
+
readonly Grip: ({
|
|
225
|
+
children,
|
|
226
|
+
ref: userRef,
|
|
227
|
+
...props
|
|
228
|
+
}: TabsetGripProps) => import("react").ReactNode;
|
|
229
|
+
readonly MaximizeButton: ({
|
|
230
|
+
children,
|
|
231
|
+
onClick,
|
|
232
|
+
...props
|
|
233
|
+
}: TabsetMaximizeButtonProps) => import("react").ReactNode;
|
|
234
|
+
readonly OverflowMenu: () => import("react").ReactNode;
|
|
235
|
+
readonly RenameInput: (props: TabsetRenameInputProps) => import("react").ReactNode;
|
|
236
|
+
readonly Root: ({
|
|
237
|
+
children,
|
|
238
|
+
node,
|
|
239
|
+
ref: userRef,
|
|
240
|
+
style,
|
|
241
|
+
...props
|
|
242
|
+
}: TabsetRootProps) => import("react").ReactNode;
|
|
243
|
+
readonly Tab: ({
|
|
244
|
+
children,
|
|
245
|
+
ref: userRef,
|
|
246
|
+
tab,
|
|
247
|
+
...props
|
|
248
|
+
}: TabsetTabProps) => import("react").ReactNode;
|
|
249
|
+
readonly Tablist: ({
|
|
250
|
+
onKeyDown,
|
|
251
|
+
ref: userRef,
|
|
252
|
+
style,
|
|
253
|
+
...props
|
|
254
|
+
}: TabsetTablistProps) => import("react").ReactNode;
|
|
255
|
+
readonly TabStrip: ({
|
|
256
|
+
style,
|
|
257
|
+
...props
|
|
258
|
+
}: TabsetTabStripProps) => import("react").ReactNode;
|
|
259
|
+
readonly Toolbar: ({
|
|
260
|
+
style,
|
|
261
|
+
...props
|
|
262
|
+
}: TabsetToolbarProps) => import("react").ReactNode;
|
|
263
|
+
readonly Trigger: ({
|
|
264
|
+
children,
|
|
265
|
+
onClick,
|
|
266
|
+
onDoubleClick,
|
|
267
|
+
ref: userRef,
|
|
268
|
+
...props
|
|
269
|
+
}: TabsetTriggerProps) => import("react").ReactNode;
|
|
270
|
+
};
|
|
271
|
+
//#endregion
|
|
272
|
+
//#region src/components/tab-overflow.d.ts
|
|
273
|
+
type OverflowItem = {
|
|
274
|
+
id: string;
|
|
275
|
+
name: string;
|
|
276
|
+
};
|
|
277
|
+
//#endregion
|
|
278
|
+
//#region src/components/tabset/tabset-store.d.ts
|
|
279
|
+
type TabsetState = {
|
|
280
|
+
activeTab: TabNode | undefined;
|
|
281
|
+
cancelRename: () => void;
|
|
282
|
+
closeTab: (tabId: string) => void;
|
|
283
|
+
commitRename: (tabId: string, value: string) => void;
|
|
284
|
+
dispatch: (action: Action) => void;
|
|
285
|
+
editingTabId: string | null;
|
|
286
|
+
isMaximized: boolean;
|
|
287
|
+
node: TabsetNode;
|
|
288
|
+
overflowItems: Array<OverflowItem>;
|
|
289
|
+
registerRenameInput: () => () => void;
|
|
290
|
+
registerTablist: (element: HTMLDivElement | null) => void;
|
|
291
|
+
renameInputCount: number;
|
|
292
|
+
renameTab: (tabId: string, name: string) => void;
|
|
293
|
+
restoreFocus: boolean;
|
|
294
|
+
selectOverflowTab: (tabId: string) => void;
|
|
295
|
+
selectTab: (index: number, options?: {
|
|
296
|
+
focus?: boolean;
|
|
297
|
+
}) => void;
|
|
298
|
+
showMaximize: boolean;
|
|
299
|
+
startEditing: (tabId: string) => void;
|
|
300
|
+
tablistElement: HTMLDivElement | null;
|
|
301
|
+
tabsClosable: boolean;
|
|
302
|
+
tabsRenamable: boolean;
|
|
303
|
+
toggleMaximize: () => void;
|
|
304
|
+
visualSelected: number;
|
|
305
|
+
};
|
|
306
|
+
declare const useTabset: <T>(selector: (state: TabsetState) => T) => T;
|
|
307
|
+
type TabContextValue = {
|
|
308
|
+
index: number;
|
|
309
|
+
tab: TabNode;
|
|
310
|
+
};
|
|
311
|
+
declare const useTab: () => TabContextValue;
|
|
312
|
+
//#endregion
|
|
313
|
+
//#region src/hooks/layout-store.d.ts
|
|
314
|
+
type LayoutState = {
|
|
315
|
+
closableTabs: boolean;
|
|
316
|
+
dispatch: (action: Action) => void;
|
|
317
|
+
draggableTabs: boolean;
|
|
318
|
+
draggableTabsets: boolean;
|
|
319
|
+
editable: boolean;
|
|
320
|
+
keepMounted: boolean;
|
|
321
|
+
maximizable: boolean;
|
|
322
|
+
maximizedTabsetId: string | undefined;
|
|
323
|
+
renamableTabs: boolean;
|
|
324
|
+
renderTab: (tab: TabNode) => ReactNode;
|
|
325
|
+
renderTabLabel: ((tab: TabNode) => ReactNode) | undefined;
|
|
326
|
+
renderTabsetToolbar: ((tabset: TabsetNode) => ReactNode) | undefined;
|
|
327
|
+
resizableSplits: boolean;
|
|
328
|
+
splitDock: boolean;
|
|
329
|
+
tabLocation: "bottom" | "top";
|
|
330
|
+
tabsetMinSize: number;
|
|
331
|
+
tabStripEnabled: boolean;
|
|
332
|
+
};
|
|
333
|
+
declare const useLayout: <T>(selector: (state: LayoutState) => T) => T;
|
|
334
|
+
//#endregion
|
|
106
335
|
//#region src/hooks/responsive.d.ts
|
|
107
336
|
type Breakpoint = {
|
|
108
337
|
id: string;
|
|
@@ -148,5 +377,5 @@ type UseDashfooStoreOptions = {
|
|
|
148
377
|
};
|
|
149
378
|
declare const useDashfooStore: (options: UseDashfooStoreOptions) => DashfooStore;
|
|
150
379
|
//#endregion
|
|
151
|
-
export { type Breakpoint,
|
|
380
|
+
export { type Breakpoint, DashfooDragProvider, type DashfooHandle, DashfooLayout, type DashfooLayoutProps, type DashfooStore, type ExternalTabSourceOptions, Layout, type DragProviderProps as LayoutDragLayerProps, type LayoutRootProps, type RowViewProps as LayoutRowsProps, type LayoutState, type TabsetViewProps as LayoutTabsetProps, Panel, type PanelBadgeProps, type PanelBodyProps, type PanelHeaderProps, type PanelIconProps, type PanelRootProps, type PanelTitleProps, type PersistConfig, type Persistence, type ResponsiveModel, type StorageAdapter, type TabComponent, type TabContextValue, Tabset, type TabsetCloseButtonProps, type TabsetContentProps, type TabsetGripProps, type TabsetMaximizeButtonProps, type TabsetRenameInputProps, type TabsetRootProps, type TabsetState, type TabsetTabProps, type TabsetTabStripProps, type TabsetTablistProps, type TabsetToolbarProps, type TabsetTriggerProps, type UseDashfooStoreOptions, type UseResponsiveModelOptions, activeBreakpoint, localStorageAdapter, matchBreakpoint, memoryStorageAdapter, useDashfooStore, useDragSubject, useDropIntent, useExternalTabSource, useLayout, usePersistence, useResponsiveModel, useTab, useTabset };
|
|
152
381
|
//# sourceMappingURL=index.d.ts.map
|