@dashfoo/react 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -36
- package/dist/index.d.ts +33 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ paint.
|
|
|
15
15
|
It builds on three engines:
|
|
16
16
|
|
|
17
17
|
- **[react-resizable-panels](https://github.com/bvaughn/react-resizable-panels)** for splitter resize (the resize adapter is the only file that imports it).
|
|
18
|
-
- **[@dnd-kit/dom](https://github.com/clauderic/dnd-kit) 0.
|
|
18
|
+
- **[@dnd-kit/dom](https://github.com/clauderic/dnd-kit) 0.5** — the framework-agnostic core (no React bindings) — for drag (the drag adapter is the only file that touches it; pointer-only).
|
|
19
19
|
- **[@dashfoo/core](https://www.npmjs.com/package/@dashfoo/core)** for the document: a zod schema, a pure reducer, and the XState machines that drive state and the drag lifecycle.
|
|
20
20
|
|
|
21
21
|
## Install
|
|
@@ -59,24 +59,29 @@ 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
|
|
63
|
-
| ------------------------- |
|
|
64
|
-
| `model` | `Dashfoo`
|
|
65
|
-
| `defaultModel` | `Dashfoo`
|
|
66
|
-
| `onModelChange` | `(model: Dashfoo, action?: Action) => void`
|
|
67
|
-
| `components` | `Record<string, ComponentType<{ node: TabNode }>>`
|
|
68
|
-
| `factory` | `(tab: TabNode) => ReactNode`
|
|
69
|
-
| `persist` | `string \| { key; storage?; debounceMs? }`
|
|
70
|
-
| `onAction` | `(action: Action) => Action \| null`
|
|
71
|
-
| `onActiveTabsetChange` | `(id: string \| undefined) => void`
|
|
72
|
-
| `onMaximizedTabsetChange` | `(id: string \| undefined) => void`
|
|
73
|
-
| `renderTabLabel` | `(tab: TabNode) => ReactNode`
|
|
74
|
-
| `renderTabsetToolbar` | `(tabset: TabsetNode) => ReactNode`
|
|
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. |
|
|
83
|
+
| `responsive` | `{ maxWidth: number; orientation? }` | — | At/below `maxWidth` (the layout's own container width) render a stacked column and lock drag + resize. View-only projection — no remount, state preserved. See below. |
|
|
84
|
+
| `snap` | `{ step?: number; divisions?: number \| "panels"; threshold?: number }` | — | Magnetic splitter snapping onto a grid — the union of `step` (multiples of a fixed percent) and `divisions` (even splits: `100/d`, or `"panels"` to divide by the row's panel count, so a 3-panel row snaps to thirds). Within `threshold` percent (default `4`) it snaps and commits on release as one undo step. Off when omitted; replaces `global.snap`; a row overrides via its own `snap` (`{}` disables). Locked off in compact mode. |
|
|
80
85
|
|
|
81
86
|
You must pass either `model` or `defaultModel`. Passing neither throws.
|
|
82
87
|
|
|
@@ -175,19 +180,22 @@ with the right role and ARIA wiring, ready for you to style.
|
|
|
175
180
|
|
|
176
181
|
### Per-node enable flags
|
|
177
182
|
|
|
178
|
-
The
|
|
183
|
+
The top-level chrome props are layout-wide gates. Individual nodes can opt out
|
|
179
184
|
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
|
-
| `
|
|
185
|
+
explicitly `false`). A control shows only when the `editable` umbrella, the
|
|
186
|
+
chrome prop, the model global, and the node's flag all allow it (`maximizable`
|
|
187
|
+
ignores `editable` — maximize is view state, not a structural edit).
|
|
188
|
+
|
|
189
|
+
| Field | On node | Disables |
|
|
190
|
+
| ------------------- | ------- | ------------------------------------------------ |
|
|
191
|
+
| `enableClose` | tab | the close control for that tab |
|
|
192
|
+
| `enableClose` | tabset | closing for every tab in the tabset |
|
|
193
|
+
| `enableRename` | tab | double-click rename for that tab |
|
|
194
|
+
| `enableDrag` | tab | dragging that tab |
|
|
195
|
+
| `enableMaximize` | tabset | the maximize control for that tabset |
|
|
196
|
+
| `tabEnableDrag` | global | tree-wide default behind `tab.enableDrag` |
|
|
197
|
+
| `enableSplitDock` | global | splitting a tabset on drop (drops stack instead) |
|
|
198
|
+
| `enableSplitResize` | global | dragging the splitters (gutters keep their size) |
|
|
191
199
|
|
|
192
200
|
```ts
|
|
193
201
|
{ id: "logs", type: "tab", name: "Logs", component: "logs", enableClose: false }
|
|
@@ -244,6 +252,31 @@ keyboard access.
|
|
|
244
252
|
| `label` | `string` | `""` | The drag preview chip text. |
|
|
245
253
|
| `disabled` | `boolean` | `false` | Unregisters the source while true. |
|
|
246
254
|
|
|
255
|
+
## Responsive layouts
|
|
256
|
+
|
|
257
|
+
Panels are weight-based, so the tree already scales with its container. To
|
|
258
|
+
_restructure_ on small screens, give `DashfooLayout` a `responsive` prop:
|
|
259
|
+
|
|
260
|
+
```tsx
|
|
261
|
+
<DashfooLayout defaultModel={model} factory={renderPanel} responsive={{ maxWidth: 720 }} />
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
At or below `maxWidth` — measured on the layout's own container, not the viewport
|
|
265
|
+
— the layout renders as a single stacked column and **locks every structural
|
|
266
|
+
interaction** (tab/tabset drag, split resize), leaving tap-to-switch and
|
|
267
|
+
maximize. This mirrors VS Code: docking is a desktop interaction, so narrow
|
|
268
|
+
screens get a read-only, navigable view instead of unusable hit targets.
|
|
269
|
+
|
|
270
|
+
The stacked view is a **derived projection** — the model in the store is never
|
|
271
|
+
mutated, so the layout is never remounted and widening past `maxWidth` restores
|
|
272
|
+
the desktop arrangement exactly. Undo history, persistence, selection, and
|
|
273
|
+
mounted panels all survive the breakpoint cross. The theme grows tab hit targets
|
|
274
|
+
to 44px under `@media (pointer: coarse)` so tap-to-switch stays usable.
|
|
275
|
+
|
|
276
|
+
Hand-built layouts (the `Layout.*` primitives) get the same behavior with
|
|
277
|
+
`useContainerWidth` + `stackModel`; for _distinct_ per-breakpoint models, use
|
|
278
|
+
`useResponsiveModel`. Both feed reactive props with no `key`/remount.
|
|
279
|
+
|
|
247
280
|
## Persistence
|
|
248
281
|
|
|
249
282
|
The `persist` prop saves an uncontrolled layout and restores it on load. It loads
|
|
@@ -279,7 +312,7 @@ in `onModelChange`.
|
|
|
279
312
|
|
|
280
313
|
The lower-level `usePersistence` load/save primitive (that `persist` is built on)
|
|
281
314
|
is also exported, for hosts that drive the store directly. See the
|
|
282
|
-
[persistence guide](https://docs.dashfoo.
|
|
315
|
+
[persistence guide](https://docs.dashfoo.com/persistence) for the storage seam,
|
|
283
316
|
validation pipeline, and SSR notes.
|
|
284
317
|
|
|
285
318
|
### Options
|
|
@@ -345,7 +378,7 @@ you.
|
|
|
345
378
|
| `tabset-grip` | `button` | Drags the whole tabset. `aria-label="Move tabset"`; shown when `draggableTabsets` is on and the tabset is not maximized. |
|
|
346
379
|
| `tabset-maximize` | `button` | Maximize/restore toggle. `aria-pressed` reflects state. |
|
|
347
380
|
| `tabcontent` | `div` | `role="tabpanel"`, the active tab's content (or empty when none). |
|
|
348
|
-
| `dock-indicator` | `div` | The drag preview overlay (
|
|
381
|
+
| `dock-indicator` | `div` | The drag preview overlay (insertion line or zone pane). `pointer-events: none`. |
|
|
349
382
|
| `drag-preview` | `div` | The chip that follows the pointer during a drag, showing the dragged label. |
|
|
350
383
|
| `separator` | rrp `Separator` | rrp emits `data-separator` with `aria-orientation`; style splitters here. |
|
|
351
384
|
|
|
@@ -366,7 +399,7 @@ to theme the drag preview without touching layout.
|
|
|
366
399
|
| `--dashfoo-dock-border` | indicator border color | `oklch(0.708 0 0 / 0.75)` |
|
|
367
400
|
| `--dashfoo-dock-border-width` | indicator border width | `1px` |
|
|
368
401
|
| `--dashfoo-dock-radius` | indicator corner radius | `6px` |
|
|
369
|
-
| `--dashfoo-dock-
|
|
402
|
+
| `--dashfoo-dock-line-radius` | insertion-line radius | `2px` |
|
|
370
403
|
| `--dashfoo-dock-transition` | indicator move animation | `left 60ms, top 60ms, width 60ms, height 60ms` |
|
|
371
404
|
|
|
372
405
|
Set `--dashfoo-dock-transition: none` to disable the indicator animation.
|
|
@@ -405,6 +438,16 @@ const { index, tab } = useTab(); // which tab a part belongs to; throws outside
|
|
|
405
438
|
tabset being dragged) or `null` — including outside a drag layer — for
|
|
406
439
|
drag-aware styling in custom parts.
|
|
407
440
|
|
|
441
|
+
`useDropIntent()` returns the live drop intent (`{ targetId, location, index? }`
|
|
442
|
+
— where the drag would land if dropped right now) or `null` when nothing is
|
|
443
|
+
dragging or the pointer is over no valid target (empty space, a non-editable
|
|
444
|
+
layout, or a no-op drop the library suppresses). Together with
|
|
445
|
+
`dockZonePolygons` from `@dashfoo/core` it supports consumer-rendered drop
|
|
446
|
+
indicators and drop-zone visualizations alongside the built-in indicator. Both
|
|
447
|
+
hooks work anywhere under a layout; under a `DashfooDragProvider` they work
|
|
448
|
+
anywhere under the provider (the provider hosts the drag store, so widget lists
|
|
449
|
+
and overlays outside the layout observe the same drag).
|
|
450
|
+
|
|
408
451
|
## Build your own layout
|
|
409
452
|
|
|
410
453
|
`DashfooLayout` is a thin assembly of exported primitives — you can compose the
|
|
@@ -525,9 +568,10 @@ type TabsetState;
|
|
|
525
568
|
useTab; // { tab, index } identity (throws outside Tabset.Tab)
|
|
526
569
|
type TabContextValue;
|
|
527
570
|
useDragSubject; // the live drag subject, or null
|
|
571
|
+
useDropIntent; // where the drag would land if dropped right now, or null
|
|
528
572
|
|
|
529
573
|
// External drag sources
|
|
530
|
-
DashfooDragProvider; // shares one drag manager between a layout and outside sources
|
|
574
|
+
DashfooDragProvider; // shares one drag manager + drag store between a layout and outside sources
|
|
531
575
|
useExternalTabSource;
|
|
532
576
|
type ExternalTabSourceOptions;
|
|
533
577
|
|
|
@@ -540,7 +584,8 @@ type Persistence;
|
|
|
540
584
|
type PersistConfig;
|
|
541
585
|
|
|
542
586
|
// Responsive
|
|
543
|
-
|
|
587
|
+
useContainerWidth; // [ref, width] via ResizeObserver — the hand-built building block
|
|
588
|
+
useResponsiveModel; // per-breakpoint model + lock flags (no remount)
|
|
544
589
|
matchBreakpoint; // does a breakpoint apply at a given width
|
|
545
590
|
activeBreakpoint; // first matching breakpoint, else the last (the catch-all)
|
|
546
591
|
type Breakpoint;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Action, Dashfoo, DockLocation, DragSubject, RowNode, TabNode, TabsetNode } from "@dashfoo/core";
|
|
2
|
-
import { ComponentProps, ComponentType, ReactNode } from "react";
|
|
1
|
+
import { Action, Dashfoo, DockLocation, DragSubject, DropIntent, Orientation, RowNode, SnapConfig, TabNode, TabsetNode } from "@dashfoo/core";
|
|
2
|
+
import { ComponentProps, ComponentType, ReactNode, Ref } from "react";
|
|
3
3
|
import { DragDropManager } from "@dnd-kit/dom";
|
|
4
4
|
//#region src/hooks/persistence.d.ts
|
|
5
5
|
type StorageAdapter = {
|
|
@@ -52,7 +52,9 @@ type DashfooLayoutProps = {
|
|
|
52
52
|
closableTabs?: boolean;
|
|
53
53
|
components?: Record<string, TabComponent>;
|
|
54
54
|
defaultModel?: Dashfoo;
|
|
55
|
+
draggableTabs?: boolean;
|
|
55
56
|
draggableTabsets?: boolean;
|
|
57
|
+
editable?: boolean;
|
|
56
58
|
factory?: (tab: TabNode) => ReactNode;
|
|
57
59
|
keepMounted?: boolean;
|
|
58
60
|
maximizable?: boolean;
|
|
@@ -65,6 +67,12 @@ type DashfooLayoutProps = {
|
|
|
65
67
|
renamableTabs?: boolean;
|
|
66
68
|
renderTabLabel?: (tab: TabNode) => ReactNode;
|
|
67
69
|
renderTabsetToolbar?: (tabset: TabsetNode) => ReactNode;
|
|
70
|
+
resizableSplits?: boolean;
|
|
71
|
+
responsive?: {
|
|
72
|
+
maxWidth: number;
|
|
73
|
+
orientation?: Orientation;
|
|
74
|
+
};
|
|
75
|
+
snap?: SnapConfig;
|
|
68
76
|
};
|
|
69
77
|
declare const DashfooLayout: import("react").ForwardRefExoticComponent<DashfooLayoutProps & import("react").RefAttributes<DashfooHandle>>;
|
|
70
78
|
//#endregion
|
|
@@ -89,6 +97,7 @@ declare const useExternalTabSource: ({
|
|
|
89
97
|
ref: (element: Element | null) => void;
|
|
90
98
|
};
|
|
91
99
|
declare const useDragSubject: () => DragSubject | null;
|
|
100
|
+
declare const useDropIntent: () => DropIntent | null;
|
|
92
101
|
//#endregion
|
|
93
102
|
//#region src/components/drag-adapter.d.ts
|
|
94
103
|
type DragProviderProps = {
|
|
@@ -102,7 +111,9 @@ type LayoutRootProps = Omit<ComponentProps<"div">, "children"> & {
|
|
|
102
111
|
children: ReactNode;
|
|
103
112
|
closableTabs?: boolean;
|
|
104
113
|
dispatch: (action: Action) => void;
|
|
114
|
+
draggableTabs?: boolean;
|
|
105
115
|
draggableTabsets?: boolean;
|
|
116
|
+
editable?: boolean;
|
|
106
117
|
keepMounted?: boolean;
|
|
107
118
|
maximizable?: boolean;
|
|
108
119
|
model: Dashfoo;
|
|
@@ -110,6 +121,9 @@ type LayoutRootProps = Omit<ComponentProps<"div">, "children"> & {
|
|
|
110
121
|
renderTab: (tab: TabNode) => ReactNode;
|
|
111
122
|
renderTabLabel?: (tab: TabNode) => ReactNode;
|
|
112
123
|
renderTabsetToolbar?: (tabset: TabsetNode) => ReactNode;
|
|
124
|
+
resizableSplits?: boolean;
|
|
125
|
+
rootRef?: Ref<HTMLDivElement>;
|
|
126
|
+
snap?: SnapConfig;
|
|
113
127
|
};
|
|
114
128
|
//#endregion
|
|
115
129
|
//#region src/components/row-view.d.ts
|
|
@@ -134,7 +148,9 @@ declare const Layout: {
|
|
|
134
148
|
children,
|
|
135
149
|
closableTabs,
|
|
136
150
|
dispatch,
|
|
151
|
+
draggableTabs,
|
|
137
152
|
draggableTabsets,
|
|
153
|
+
editable,
|
|
138
154
|
keepMounted,
|
|
139
155
|
maximizable,
|
|
140
156
|
model,
|
|
@@ -142,6 +158,9 @@ declare const Layout: {
|
|
|
142
158
|
renderTab,
|
|
143
159
|
renderTabLabel,
|
|
144
160
|
renderTabsetToolbar,
|
|
161
|
+
resizableSplits,
|
|
162
|
+
rootRef,
|
|
163
|
+
snap,
|
|
145
164
|
style,
|
|
146
165
|
...props
|
|
147
166
|
}: LayoutRootProps) => import("react").ReactNode;
|
|
@@ -304,7 +323,9 @@ declare const useTab: () => TabContextValue;
|
|
|
304
323
|
type LayoutState = {
|
|
305
324
|
closableTabs: boolean;
|
|
306
325
|
dispatch: (action: Action) => void;
|
|
326
|
+
draggableTabs: boolean;
|
|
307
327
|
draggableTabsets: boolean;
|
|
328
|
+
editable: boolean;
|
|
308
329
|
keepMounted: boolean;
|
|
309
330
|
maximizable: boolean;
|
|
310
331
|
maximizedTabsetId: string | undefined;
|
|
@@ -312,6 +333,8 @@ type LayoutState = {
|
|
|
312
333
|
renderTab: (tab: TabNode) => ReactNode;
|
|
313
334
|
renderTabLabel: ((tab: TabNode) => ReactNode) | undefined;
|
|
314
335
|
renderTabsetToolbar: ((tabset: TabsetNode) => ReactNode) | undefined;
|
|
336
|
+
resizableSplits: boolean;
|
|
337
|
+
snap: SnapConfig | null;
|
|
315
338
|
splitDock: boolean;
|
|
316
339
|
tabLocation: "bottom" | "top";
|
|
317
340
|
tabsetMinSize: number;
|
|
@@ -321,6 +344,7 @@ declare const useLayout: <T>(selector: (state: LayoutState) => T) => T;
|
|
|
321
344
|
//#endregion
|
|
322
345
|
//#region src/hooks/responsive.d.ts
|
|
323
346
|
type Breakpoint = {
|
|
347
|
+
compact?: boolean;
|
|
324
348
|
id: string;
|
|
325
349
|
model: Dashfoo;
|
|
326
350
|
query?: {
|
|
@@ -335,11 +359,15 @@ type UseResponsiveModelOptions = {
|
|
|
335
359
|
type ResponsiveModel = {
|
|
336
360
|
breakpoint: string;
|
|
337
361
|
containerRef: (element: HTMLElement | null) => void;
|
|
338
|
-
|
|
339
|
-
|
|
362
|
+
draggableTabs: boolean;
|
|
363
|
+
draggableTabsets: boolean;
|
|
364
|
+
isCompact: boolean;
|
|
365
|
+
model: Dashfoo;
|
|
366
|
+
resizableSplits: boolean;
|
|
340
367
|
};
|
|
341
368
|
declare const matchBreakpoint: (breakpoint: Breakpoint, width: number) => boolean;
|
|
342
369
|
declare const activeBreakpoint: (breakpoints: Array<Breakpoint>, width: number) => Breakpoint;
|
|
370
|
+
declare const useContainerWidth: () => [(element: HTMLElement | null) => void, number];
|
|
343
371
|
declare const useResponsiveModel: ({
|
|
344
372
|
breakpoints
|
|
345
373
|
}: UseResponsiveModelOptions) => ResponsiveModel;
|
|
@@ -364,5 +392,5 @@ type UseDashfooStoreOptions = {
|
|
|
364
392
|
};
|
|
365
393
|
declare const useDashfooStore: (options: UseDashfooStoreOptions) => DashfooStore;
|
|
366
394
|
//#endregion
|
|
367
|
-
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, useExternalTabSource, useLayout, usePersistence, useResponsiveModel, useTab, useTabset };
|
|
395
|
+
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, useContainerWidth, useDashfooStore, useDragSubject, useDropIntent, useExternalTabSource, useLayout, usePersistence, useResponsiveModel, useTab, useTabset };
|
|
368
396
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/hooks/persistence.ts","../src/components/dashfoo-layout.tsx","../src/components/drag-root.tsx","../src/hooks/drag-hooks.tsx","../src/components/drag-adapter.tsx","../src/components/layout-root.tsx","../src/components/row-view.tsx","../src/components/tabset/tabset-view.tsx","../src/components/layout.tsx","../src/components/panel.tsx","../src/components/tabset/tabset-tab-controls.tsx","../src/components/tabset/tabset-content.tsx","../src/components/tabset/tabset-toolbar.tsx","../src/components/tabset/tabset-root.tsx","../src/components/tabset/tabset-tab.tsx","../src/components/tabset/tabset-tablist.tsx","../src/components/tabset/tabset.ts","../src/components/tab-overflow.tsx","../src/components/tabset/tabset-store.ts","../src/hooks/layout-store.ts","../src/hooks/responsive.ts","../src/hooks/store.ts"],"mappings":";;;;KAQK,cAAA;EACH,OAAA,GAAU,GAAA;EACV,UAAA,GAAa,GAAA;EACb,OAAA,GAAU,GAAA,UAAa,KAAA;AAAA;AAAA,cAGnB,oBAAA,QAA2B,cAWhC;AAAA,cAIK,mBAAA,EAAqB,cAiC1B;AAAA,KAII,aAAA;EACH,UAAA;EACA,GAAA;EACA,OAAA,EAAS,cAAc;AAAA;AAAA,KAGpB,WAAA;EACH,KAAA;EAEA,YAAA,EAAc,OAAA;EACd,IAAA,GAAO,KAAA,EAAO,OAAO;AAAA;AAAA,cASjB,cAAA,GACJ,MAAA,EAAQ,aAAA,SACR,YAAA,EAAc,OAAA,iBACb,WAAA;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/hooks/persistence.ts","../src/components/dashfoo-layout.tsx","../src/components/drag-root.tsx","../src/hooks/drag-hooks.tsx","../src/components/drag-adapter.tsx","../src/components/layout-root.tsx","../src/components/row-view.tsx","../src/components/tabset/tabset-view.tsx","../src/components/layout.tsx","../src/components/panel.tsx","../src/components/tabset/tabset-tab-controls.tsx","../src/components/tabset/tabset-content.tsx","../src/components/tabset/tabset-toolbar.tsx","../src/components/tabset/tabset-root.tsx","../src/components/tabset/tabset-tab.tsx","../src/components/tabset/tabset-tablist.tsx","../src/components/tabset/tabset.ts","../src/components/tab-overflow.tsx","../src/components/tabset/tabset-store.ts","../src/hooks/layout-store.ts","../src/hooks/responsive.ts","../src/hooks/store.ts"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
"use client";import{canRedo as e,canUndo as t,dashfooMachine as n,dragDockMachine as r,findTabset as i,fromJSON as a,normalize as o,reducer as s,resolveDockTarget as c,
|
|
1
|
+
"use client";import{canRedo as e,canUndo as t,dashfooMachine as n,dragDockMachine as r,findTabset as i,fromJSON as a,normalize as o,reducer as s,resolveDockTarget as c,snapEnabled as l,snapSizes as u,stackModel as d,tabNodeSchema as f,toJSON as p,zoneRect as m}from"@dashfoo/core";import{Fragment as h,createContext as g,createElement as _,forwardRef as v,useCallback as y,useContext as b,useEffect as x,useId as S,useImperativeHandle as C,useInsertionEffect as w,useLayoutEffect as T,useMemo as E,useRef as D,useState as O}from"react";import{useActorRef as k,useSelector as A}from"@xstate/react";import{Accessibility as j,DragDropManager as ee,Draggable as te,Feedback as ne,PointerSensor as M}from"@dnd-kit/dom";import{createStore as N,useStore as P}from"zustand";import{Fragment as F,jsx as I,jsxs as L}from"react/jsx-runtime";import{Group as re,Panel as ie,Separator as ae}from"react-resizable-panels";const oe=()=>{let e=new Map;return{getItem:t=>e.get(t)??null,removeItem:t=>{e.delete(t)},setItem:(t,n)=>{e.set(t,n)}}},R={getItem:e=>{if(typeof window>`u`)return null;try{return window.localStorage.getItem(e)}catch{return null}},removeItem:e=>{if(!(typeof window>`u`))try{window.localStorage.removeItem(e)}catch{}},setItem:(e,t)=>{if(!(typeof window>`u`))try{window.localStorage.setItem(e,t)}catch(e){console.warn(`[dashfoo] failed to persist layout`,e)}}},se=(e,t)=>{let n=D(e);x(()=>{n.current=e});let[r]=O(()=>{if(e===null||t===void 0)return t;let n=e.storage.getItem(e.key);if(n===null)return t;try{return a(n)}catch{return t}});x(()=>{let e=n.current;if(e===null)return;let t=e.storage.getItem(e.key);if(t!==null)try{a(t)}catch(t){console.warn(`[dashfoo] discarding unreadable persisted layout`,t),e.storage.removeItem(e.key)}},[]);let i=D(e?.key);x(()=>{e!==null&&e.key!==i.current&&(console.warn(`[dashfoo] persist key changed for a mounted layout; the displayed layout still reflects the previous key — remount (key={persistKey}) to load the new key`),i.current=e.key)},[e]);let o=D(null),s=D(null),c=y(()=>{o.current!==null&&(clearTimeout(o.current),o.current=null),s.current!==null&&(s.current.storage.setItem(s.current.key,s.current.value),s.current=null)},[]),l=y(e=>{let t=n.current;t!==null&&(s.current={key:t.key,storage:t.storage,value:p(e)},o.current!==null&&clearTimeout(o.current),o.current=setTimeout(c,t.debounceMs))},[c]);x(()=>{let e=()=>{c()},t=()=>{document.visibilityState===`hidden`&&c()};return window.addEventListener(`pagehide`,e),document.addEventListener(`visibilitychange`,t),()=>{window.removeEventListener(`pagehide`,e),document.removeEventListener(`visibilitychange`,t),c()}},[c]);let u=y(()=>{o.current!==null&&(clearTimeout(o.current),o.current=null),s.current=null;let e=n.current;e!==null&&e.storage.removeItem(e.key)},[]);return E(()=>({clear:u,initialModel:r,save:l}),[u,r,l])},ce=()=>typeof window<`u`&&typeof window.matchMedia==`function`,le=(e,t)=>{let{query:n}=e;return n?`maxWidth`in n?t<=n.maxWidth:ce()&&window.matchMedia(n.media).matches:!0},ue=(e,t)=>{let n=e.at(-1);if(!n)throw Error(`useResponsiveModel requires at least one breakpoint.`);return e.find(e=>le(e,t))??n},z=()=>{let[e,t]=O(1/0),n=D(null),r=y(e=>{if(n.current?.disconnect(),!e)return;let r=new ResizeObserver(e=>{let n=e[0];n&&t(n.contentRect.width)});r.observe(e),n.current=r},[]);return x(()=>()=>{n.current?.disconnect()},[]),[r,e]},de=({breakpoints:e})=>{let[t,n]=z(),[,r]=O(0);x(()=>{if(!ce())return;let t=e.flatMap(e=>e.query&&`media`in e.query?[window.matchMedia(e.query.media)]:[]);if(t.length===0)return;let n=()=>{r(e=>e+1)};for(let e of t)e.addEventListener(`change`,n);return()=>{for(let e of t)e.removeEventListener(`change`,n)}},[e]);let i=ue(e,n),a=i.compact===!0;return{breakpoint:i.id,containerRef:t,draggableTabs:!a,draggableTabsets:!a,isCompact:a,model:i.model,resizableSplits:!a}},fe=r=>{let{defaultModel:i,model:a,onAction:c,onActiveTabsetChange:l,onMaximizedTabsetChange:u,onModelChange:d}=r,f=a??i;if(f===void 0)throw Error("useDashfooStore requires either a `model` or a `defaultModel`.");let p=k(n,{input:{model:o(f)}}),m=A(p,e=>e.context.history);x(()=>{a!==void 0&&p.send({model:o(a),type:`SET_MODEL`})},[p,a]);let h=a??m.present,g=y((e,t,n)=>{Object.is(e,t)||(d?.(t,n),t.activeTabsetId!==e.activeTabsetId&&l?.(t.activeTabsetId),t.maximizedTabsetId!==e.maximizedTabsetId&&u?.(t.maximizedTabsetId))},[d,l,u]),_=y(e=>{let t=c?c(e):e;if(!t)return;let n=(()=>{if(a===void 0){let e=p.getSnapshot().context.history.present;return p.send({action:t,type:`DISPATCH`}),{after:p.getSnapshot().context.history.present,before:e}}return{after:s(a,t),before:o(a)}})();g(n.before,n.after,t)},[p,a,g,c]),v=y(()=>{if(a!==void 0)return;let e=p.getSnapshot().context.history.present;p.send({type:`UNDO`});let t=p.getSnapshot().context.history.present;g(e,t)},[p,a,g]),b=y(()=>{if(a!==void 0)return;let e=p.getSnapshot().context.history.present;p.send({type:`REDO`});let t=p.getSnapshot().context.history.present;g(e,t)},[p,a,g]),S=y(e=>{p.send({model:o(e),type:`SET_MODEL`})},[p]);return E(()=>({canRedo:()=>e(p.getSnapshot().context.history),canUndo:()=>t(p.getSnapshot().context.history),dispatch:_,model:h,redo:b,setModel:S,undo:v}),[p,_,h,b,S,v])},pe=(e,t)=>{let{target:n}=e;if(!(n instanceof Element))return!1;let r=n.closest(`
|
|
2
2
|
input:not([disabled]),
|
|
3
3
|
select:not([disabled]),
|
|
4
4
|
textarea:not([disabled]),
|
|
5
5
|
button:not([disabled]),
|
|
6
6
|
a[href],
|
|
7
7
|
[contenteditable]:not([contenteditable="false"])
|
|
8
|
-
`);return r!==null&&r!==t.element},ue=()=>new k({plugins:e=>e.filter(e=>e!==O&&e!==ee),sensors:()=>[te.configure({preventActivation:le})]}),I=p(null),L=p(null),de=()=>j(()=>({subject:null})),fe=de(),pe=p(null),me=()=>{let e=w(null),t=w(null);return{draggableRef:t,elementRef:e,ref:g(n=>{e.current=n,t.current&&(t.current.element=n??void 0)},[])}},R=(e,t,n)=>{let r=_(I),i=_(L),a=r?.manager??i,{draggableRef:o,elementRef:s,ref:c}=me(),{data:l,id:u}=e;return v(()=>{if(!a||t)return;let e=new A({data:{...l,label:``},id:u},a);return e.element=s.current??void 0,o.current=e,()=>{e.destroy(),o.current=null}},[l,t,o,s,u,a]),v(()=>{let e=o.current;e&&(e.data={...l,label:n})},[l,o,n]),{ref:c}},he=(e,t=!1,n=``)=>R({data:C(()=>({type:`tab`}),[]),id:e},t,n),ge=(e,t=!1,n=``)=>R({data:C(()=>({tabsetId:e,type:`tabset`}),[e]),id:`grip-${e}`},t,n),_e=e=>{let t=_(I);return{ref:g(n=>{t?.registerTabset(e,n)},[t,e])}},ve=({createTab:e,disabled:t=!1,label:n=``})=>{let r=y(),i=w(e);return v(()=>{i.current=e}),R({data:C(()=>({createTab:()=>i.current(),type:`external`}),[]),id:`external-${r}`},t,n)},z=()=>M(_(pe)??fe,e=>e.subject),ye=e=>j(()=>e),B=p(null),V=e=>{let t=_(B);if(t===null)throw Error(`dashfoo components must be rendered inside <DashfooLayout> or <Layout.Root>.`);return M(t,e)},H=(e,t)=>e.x>=t.x&&e.x<=t.x+t.width&&e.y>=t.y&&e.y<=t.y+t.height,be=(e,t)=>{let n=e.findIndex(e=>t<e.x+e.width/2);return n===-1?e.length:n},xe=(e,t,n,r)=>{let i=t[n],a=t.at(-1),o=i??a,s=i?.x??(a?a.x+a.width:e.x),c=Math.min(r.width,e.width),l=o?.height??Math.min(r.height,e.height);return{height:l,width:c,x:Math.max(e.x,Math.min(s,e.x+e.width-c)),y:o?.y??e.y+e.height-l}},Se=(e,t,n)=>!(e===`grip-${t}`||n.length===1&&n[0]===e),Ce=new Set,U=(e,t)=>{Ce.has(e)||(Ce.add(e),console.warn(`[dashfoo] ${t}`))},we={boxSizing:`border-box`,pointerEvents:`none`,position:`fixed`,transition:`var(--dashfoo-dock-transition, left 60ms, top 60ms, width 60ms, height 60ms)`,zIndex:9999},Te=e=>({...we,background:`var(--dashfoo-dock-fill, oklch(0.556 0 0 / 0.18))`,border:`var(--dashfoo-dock-border-width, 1px) solid var(--dashfoo-dock-border, oklch(0.708 0 0 / 0.75))`,borderRadius:`var(--dashfoo-dock-radius, 6px)`,height:e.height,left:e.x,top:e.y,width:e.width}),Ee=e=>({...Te(e),borderRadius:`var(--dashfoo-dock-tab-radius, var(--dashfoo-dock-radius, 6px))`}),De=(e,t)=>[...e.querySelectorAll(`[data-dashfoo="tab-item"]`)].flatMap(e=>e.querySelector(`[data-dashfoo="tab"]`)?.dataset.tabId===t?[]:[e.getBoundingClientRect()]),Oe={height:32,width:96},ke=(e,t)=>e||(t.length>0?{height:t.reduce((e,t)=>e+t.height,0)/t.length,width:t.reduce((e,t)=>e+t.width,0)/t.length}:Oe),Ae=({actorRef:e,getSourceSize:t,getTabsetElement:n})=>{let r=D(e,e=>e.context.intent),i=D(e,e=>e.context.subject?.id);if(!r)return null;let a=n(r.targetId);if(!a)return null;if(r.location===`center`){let e=a.querySelector(`[data-dashfoo="tabstrip"]`);if(e){let n=De(e,i);return N(`div`,{"data-dashfoo":`dock-indicator`,style:Ee(xe(e.getBoundingClientRect(),n,r.index??0,ke(t(),n)))})}}return N(`div`,{"data-dashfoo":`dock-indicator`,style:Te(d(a.getBoundingClientRect(),r.location))})},je={left:0,position:`fixed`,top:0,zIndex:9999},Me=({overlayRef:e,preview:t})=>t?N(`div`,{"data-dashfoo":`drag-preview`,ref:e,style:{...je,transform:`translate(${t.x}px, ${t.y}px)`},children:t.label}):null,Ne=(e,t)=>[...e.querySelectorAll(`[data-dashfoo="tab"]`)].flatMap(e=>e.dataset.tabId===t?[]:[e.getBoundingClientRect()]),Pe=(e,t,n,r)=>{let i=t.querySelector(`[data-dashfoo="tabstrip"]`);if(i&&H(n,i.getBoundingClientRect()))return{index:be(Ne(i,r),n.x),location:`center`,targetId:e};let a=c(n,t.getBoundingClientRect()),o=a.kind===`tab`?`center`:`split-${a.edge}`;return o===`center`&&i?{index:Ne(i,r).length,location:o,targetId:e}:{location:o,targetId:e}},W={x:12,y:8},Fe=e=>{let t=e?.data?.label;return typeof t==`string`?t:``},Ie=e=>typeof e==`function`,Le=e=>{if(!e)return;let t=(e.closest(`[data-dashfoo="tab-item"]`)??e).getBoundingClientRect();return t.width>0&&t.height>0?{height:t.height,width:t.width}:void 0},Re=e=>{let t=e.data;if(t?.type===`tabset`)return{id:String(t.tabsetId),kind:`tabset`};if(t?.type===`external`){if(!Ie(t.createTab))return console.warn(`[dashfoo] external drag source is missing its createTab function`),null;let n;try{n=t.createTab()}catch(e){return console.warn(`[dashfoo] external drag source createTab threw`,e),null}let r=l.safeParse(n);return r.success?{id:String(e.id),kind:`external`,tab:r.data}:(console.warn(`[dashfoo] external drag source returned an invalid tab`,r.error),null)}return{id:String(e.id),kind:`tab`}},ze=({children:e,onCommit:t,splitDock:n})=>{let i=E(r),a=_(B),o=w(new Map),s=w(null),c=w(void 0),[l,u]=T(null),d=_(L),[f]=T(()=>d?null:ue());x(()=>()=>f?.destroy(),[f]);let p=d??f;if(p===null)throw Error(`[dashfoo] DashfooDragProvider unmounted while its layout is still mounted; keep the provider above the layout or remount the layout`);let[m]=T(()=>de());v(()=>{let e=i.subscribe(e=>{let t=e.context.subject;m.getState().subject!==t&&m.setState({subject:t})});return()=>{e.unsubscribe()}},[i,m]),v(()=>{let e=i.on(`COMMIT`,e=>{let n=t??a?.getState().dispatch;if(!n){U(`drag-layer-no-commit`,`Layout.DragLayer has neither an onCommit prop nor a <Layout.Root> above it; drops are ignored`);return}n(e.action)});return()=>{e.unsubscribe()}},[i,a,t]);let h=g((e,t)=>{t?o.current.set(e,t):o.current.delete(e)},[]),y=g(e=>o.current.get(e),[]),b=g(()=>c.current,[]),S=g(e=>{for(let[t,n]of o.current)if(H(e,n.getBoundingClientRect()))return{element:n,id:t}},[]),D=g((e,t,r,i)=>{if(!Se(i,e,[...t.querySelectorAll(`[data-dashfoo="tab"]`)].map(e=>e.dataset.tabId??``)))return null;let o=Pe(e,t,r,i);return!(n??a?.getState().splitDock??!0)&&o.location.startsWith(`split-`)?{location:`center`,targetId:e}:o},[a,n]),O=g(e=>{let t=s.current;t&&(t.style.transform=`translate(${e.x+W.x}px, ${e.y+W.y}px)`)},[]),k=g(e=>{s.current=e},[]);v(()=>{let e=e=>{let t=e.operation.source;if(!t)return;let n=Re(t);if(!n)return;let r=e.operation.position.current;c.current=Le(t.element),u({label:Fe(t),x:r.x+W.x,y:r.y+W.y}),i.send({subject:n,type:`START`})},t=e=>{let t=e.operation,n=t.position.current;O(n);let r=t.source?String(t.source.id):void 0,a=S(n);i.send({intent:a?D(a.id,a.element,n,r):null,type:`OVER`})},n=e=>{if(u(null),c.current=void 0,e.canceled){i.send({type:`CANCEL`});return}let t=e.operation,n=t.position.current,r=t.source?String(t.source.id):void 0,a=S(n);a&&i.send({intent:D(a.id,a.element,n,r),type:`OVER`}),i.send({type:`DROP`})},r=p.monitor.addEventListener(`dragstart`,e),a=p.monitor.addEventListener(`dragmove`,t),o=p.monitor.addEventListener(`dragend`,n);return()=>{r(),a(),o()}},[i,p,O,D,S]);let A=C(()=>({manager:p,registerTabset:h}),[p,h]);return N(I.Provider,{value:A,children:P(pe.Provider,{value:m,children:[e,N(Ae,{actorRef:i,getSourceSize:b,getTabsetElement:y}),N(Me,{overlayRef:k,preview:l})]})})},Be={display:`flex`,height:`100%`,width:`100%`},Ve=({children:e,closableTabs:t=!0,dispatch:n,draggableTabsets:r=!0,keepMounted:i=!1,maximizable:a=!0,model:o,renamableTabs:s=!0,renderTab:c,renderTabLabel:l,renderTabsetToolbar:u,style:d,...f})=>{let p=o.global,m={closableTabs:t&&p.tabEnableClose!==!1,dispatch:n,draggableTabsets:r,keepMounted:i,maximizable:a&&p.tabSetEnableMaximize!==!1,maximizedTabsetId:o.maximizedTabsetId,renamableTabs:s&&p.tabEnableRename!==!1,renderTab:c,renderTabLabel:l,renderTabsetToolbar:u,splitDock:p.enableSplitDock!==!1,tabLocation:p.tabLocation??`top`,tabsetMinSize:p.tabSetMinSize??320,tabStripEnabled:p.tabSetEnableTabStrip!==!1},[h]=T(()=>ye(m));S(()=>{h.setState(m)});let g=p.splitterSize===void 0?{...Be,...d}:{...Be,"--dashfoo-splitter-size":`${p.splitterSize}px`,...d};return N(B.Provider,{value:h,children:N(`div`,{...f,"data-dashfoo":`layout`,style:g,children:e})})},G=(e,t)=>`dashfoo-tab-${e}-${t}`,K=e=>`dashfoo-panel-${e}`,He=(e,t,n)=>e?.querySelector(`#${CSS.escape(G(t,n))}`)??null,Ue=e=>j((t,n)=>({...e,cancelRename:()=>t({editingTabId:null}),closeTab:e=>{t({restoreFocus:!0}),n().dispatch({tabId:e,type:`deleteTab`})},commitRename:(e,r)=>{t({editingTabId:null});let{node:i,renameTab:a}=n(),o=r.trim(),s=i.children.find(t=>t.id===e)?.name;o&&o!==s&&a(e,o)},editingTabId:null,overflowItems:[],registerRenameInput:()=>(t(e=>({renameInputCount:e.renameInputCount+1})),()=>t(e=>({renameInputCount:e.renameInputCount-1}))),registerTablist:e=>{if(e){let r=n().tablistElement;r&&r!==e&&U(`duplicate-tablist-${n().node.id}`,`more than one <Tabset.Tablist> under the same <Tabset.Root>; the last one wins for keyboard focus and overflow`),t({tablistElement:e});return}t({tablistElement:null})},renameInputCount:0,renameTab:(e,t)=>n().dispatch({name:t,tabId:e,type:`renameTab`}),restoreFocus:!1,selectOverflowTab:e=>{let{dispatch:t,node:r,tablistElement:i}=n(),a=r.children.findIndex(t=>t.id===e);a!==-1&&(t({index:a,tabsetId:r.id,type:`selectTab`}),He(i,r.id,e)?.scrollIntoView({block:`nearest`,inline:`nearest`}))},selectTab:(e,t)=>{let{dispatch:r,node:i,tablistElement:a}=n();if(r({index:e,tabsetId:i.id,type:`selectTab`}),t?.focus){let t=i.children[e]?.id;t&&He(a,i.id,t)?.focus()}},startEditing:e=>{let{node:r,renameInputCount:i,tabsRenamable:a}=n(),o=r.children.find(t=>t.id===e);if(!(!a||o?.enableRename===!1)){if(i===0){U(`missing-rename-input-${r.id}`,`startEditing needs a <Tabset.RenameInput> in the composition; ignoring`);return}t({editingTabId:e})}},tablistElement:null,toggleMaximize:()=>{let{dispatch:e,isMaximized:t,node:r}=n();e({tabsetId:t?null:r.id,type:`setMaximizedTabset`})}})),q=p(null),J=e=>{let t=_(q);if(t===null)throw Error(`dashfoo Tabset parts must be rendered inside <Tabset.Root>.`);return M(t,e)},We=p(null),Y=()=>{let e=_(We);if(e===null)throw Error(`dashfoo tab parts must be rendered inside <Tabset.Tab>.`);return e},Ge={flex:1,minHeight:0,overflow:`auto`},Ke=({render:e,tab:t})=>{let n=V(e=>e.renderTab);return e?e(t):n(t)},qe=({children:e,style:t,...n})=>{let r=J(e=>e.node),i=J(e=>e.visualSelected),a=V(e=>e.keepMounted),o={...Ge,...t};if(a&&r.children.length>0)return r.children.map((t,a)=>m(`div`,{...n,"aria-labelledby":G(r.id,t.id),"data-dashfoo":`tabcontent`,hidden:a!==i||void 0,id:a===i?K(r.id):void 0,key:t.id,role:a===i?`tabpanel`:void 0,style:o,tabIndex:a===i?0:void 0},N(Ke,{render:e,tab:t})));let s=r.children[i];return s?N(`div`,{...n,"aria-labelledby":G(r.id,s.id),"data-dashfoo":`tabcontent`,id:K(r.id),role:`tabpanel`,style:o,tabIndex:0,children:N(Ke,{render:e,tab:s})}):N(`div`,{...n,"data-dashfoo":`tabcontent`,style:o})},Je=()=>P(`svg`,{"aria-hidden":`true`,fill:`currentColor`,height:`10`,viewBox:`0 0 10 10`,width:`10`,children:[N(`circle`,{cx:`2`,cy:`5`,r:`1`}),N(`circle`,{cx:`5`,cy:`5`,r:`1`}),N(`circle`,{cx:`8`,cy:`5`,r:`1`})]}),Ye={position:`absolute`,right:0,top:`100%`,zIndex:10},Xe=({items:e,onSelect:t})=>{let[n,r]=T(!1),[i,a]=T(0),o=w(null),s=w(null),c=w([]);v(()=>{if(!n)return;let e=e=>{e.target instanceof Node&&!o.current?.contains(e.target)&&r(!1)};return document.addEventListener(`pointerdown`,e),()=>{document.removeEventListener(`pointerdown`,e)}},[n]),v(()=>{n&&c.current[0]?.focus()},[n]);let l=()=>{r(!1),s.current?.focus()},u=e=>{a(e),c.current[e]?.focus()},d=()=>{a(0),r(e=>!e)},f=e=>{t(e),l()};return P(`div`,{"data-dashfoo":`tab-overflow-root`,ref:o,style:{position:`relative`},children:[N(`button`,{"aria-expanded":n,"aria-haspopup":`menu`,"aria-label":`More tabs`,"data-dashfoo":`tab-overflow`,onClick:d,ref:s,type:`button`,children:N(Je,{})}),n?N(`div`,{"data-dashfoo":`tab-overflow-menu`,onBlur:e=>{e.relatedTarget instanceof Node&&o.current?.contains(e.relatedTarget)||r(!1)},onKeyDown:t=>{let n=e.length-1;switch(t.key){case`ArrowDown`:t.preventDefault(),u(i>=n?0:i+1);break;case`ArrowUp`:t.preventDefault(),u(i<=0?n:i-1);break;case`Home`:t.preventDefault(),u(0);break;case`End`:t.preventDefault(),u(n);break;case`Escape`:t.preventDefault(),l();break;default:break}},role:`menu`,style:Ye,tabIndex:-1,children:e.map((e,t)=>N(`button`,{"data-dashfoo":`tab-overflow-item`,onClick:()=>f(e.id),ref:e=>{c.current[t]=e},role:`menuitem`,tabIndex:t===i?0:-1,type:`button`,children:e.name},e.id))}):null]})},Ze=()=>{let e=J(e=>e.overflowItems),t=J(e=>e.selectOverflowTab);return e.length===0?null:N(Xe,{items:e,onSelect:t})},Qe=e=>{let t=e.getBoundingClientRect();return[...e.querySelectorAll(`[data-dashfoo="tab"]`)].flatMap(e=>{let n=e.getBoundingClientRect();return(n.right>t.right+1||n.left<t.left-1)&&e.dataset.tabId?[e.dataset.tabId]:[]})},$e=(e,t)=>{let[n,r]=T([]);return v(()=>{let t=()=>{r(t=>{let n=e?Qe(e):[];return n.length===0&&t.length===0?t:n})};if(t(),!e)return;let n=new ResizeObserver(t);return n.observe(e),e.addEventListener(`scroll`,t,{passive:!0}),()=>{n.disconnect(),e.removeEventListener(`scroll`,t)}},[e,t]),n},et=(e,t)=>t+1<e?t+1:t-1,tt={display:`flex`,flexDirection:`column`,height:`100%`,minHeight:0,minWidth:0,width:`100%`},nt=({children:e,node:t,ref:n,style:r,...i})=>{let a=V(e=>e.closableTabs),o=V(e=>e.dispatch),s=V(e=>e.maximizable),c=V(e=>e.maximizedTabsetId),l=V(e=>e.renamableTabs),u=V(e=>e.tabLocation),{ref:d}=_e(t.id),f=z(),p=w(null),m=c===t.id,h=s&&t.enableMaximize!==!1,g=a&&t.enableClose!==!1,_=l,y=f?.kind===`tab`?t.children.findIndex(e=>e.id===f.id):-1,b=y!==-1&&y===t.selected?et(t.children.length,y):t.selected,x=t.children[b],E=f?.kind===`tabset`&&f.id===t.id,D={activeTab:x,dispatch:o,isMaximized:m,node:t,showMaximize:h,tabsClosable:g,tabsRenamable:_,visualSelected:b},[O]=T(()=>Ue(D));S(()=>{O.setState(D)});let k=$e(M(O,e=>e.tablistElement),t.children.map(e=>`${e.id}:${e.name}`).join(`|`)),A=C(()=>{let e=new Map(t.children.map(e=>[e.id,e.name]));return k.map(t=>({id:t,name:e.get(t)??t}))},[k,t.children]);return S(()=>{O.setState({overflowItems:A})},[A,O]),v(()=>{if(!O.getState().restoreFocus)return;O.setState({restoreFocus:!1});let e=t.children[t.selected]?.id;e?p.current?.querySelector(`#${CSS.escape(G(t.id,e))}`)?.focus():p.current?.focus()},[t.children,t.selected,t.id,O]),N(q.Provider,{value:O,children:N(`div`,{...i,"data-dashfoo":`tabset`,"data-dragging-source":E||void 0,"data-tab-location":u,ref:e=>{d(e),p.current=e,typeof n==`function`?n(e):n&&(n.current=e)},style:{...tt,...r},tabIndex:-1,children:e})})},rt=(e,t)=>{typeof e==`function`?e(t):e&&(e.current=t)},X=(...e)=>t=>{for(let n of e)rt(n,t)},it=({children:e,ref:t,tab:n,...r})=>{let i=_(q),a=J(e=>e.node),o=J(e=>e.editingTabId===n.id),s=z(),c=s?.kind===`tab`&&s.id===n.id,l=w(null),u=w(!1),d=a.children.findIndex(e=>e.id===n.id);v(()=>{i?.getState().node.children.some(e=>e.id===n.id)===!1&&U(`orphan-tab-${n.id}`,`<Tabset.Tab> for "${n.id}" is not a child of its Tabset.Root's node; it renders but can never be selected`)},[d===-1,i,n.id]),v(()=>{u.current&&!o&&l.current?.querySelector(`[data-dashfoo="tab"]`)?.focus(),u.current=o},[o]);let f=C(()=>({index:d,tab:n}),[d,n]),p=C(()=>X(l,t),[t]);return N(We.Provider,{value:f,children:N(`span`,{...r,"data-dashfoo":`tab-item`,"data-dragging":c||void 0,ref:p,children:e})})},at=({children:e,onClick:t,onDoubleClick:n,ref:r,...i})=>{let{index:a,tab:o}=Y(),s=J(e=>e.node),c=J(e=>e.editingTabId===o.id),l=J(e=>e.selectTab),u=J(e=>e.startEditing),d=J(e=>e.tabsRenamable),f=J(e=>e.visualSelected),p=V(e=>e.renderTabLabel),{ref:m}=he(o.id,o.enableDrag===!1,o.name),h=C(()=>X(m,r),[m,r]);if(c)return null;let g=a===f,_=d&&o.enableRename!==!1,v=e=>{t?.(e),l(a)},y=e=>{n?.(e),_&&u(o.id)};return N(`button`,{"aria-label":p?o.name:void 0,...i,"aria-controls":K(s.id),"aria-selected":g,"data-dashfoo":`tab`,"data-tab-id":o.id,id:G(s.id,o.id),onClick:v,onDoubleClick:y,ref:h,role:`tab`,tabIndex:g?0:-1,type:`button`,children:e??(p?p(o):o.name)})},ot={pointerEvents:`none`},Z=({children:e})=>N(`svg`,{"aria-hidden":`true`,fill:`none`,height:`12`,stroke:`currentColor`,strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:`2`,style:ot,viewBox:`0 0 24 24`,width:`12`,children:e}),st=()=>P(Z,{children:[N(`path`,{d:`M18 6 6 18`}),N(`path`,{d:`m6 6 12 12`})]}),ct=()=>P(Z,{children:[N(`circle`,{cx:`12`,cy:`5`,r:`1`}),N(`circle`,{cx:`19`,cy:`5`,r:`1`}),N(`circle`,{cx:`5`,cy:`5`,r:`1`}),N(`circle`,{cx:`12`,cy:`12`,r:`1`}),N(`circle`,{cx:`19`,cy:`12`,r:`1`}),N(`circle`,{cx:`5`,cy:`12`,r:`1`}),N(`circle`,{cx:`12`,cy:`19`,r:`1`}),N(`circle`,{cx:`19`,cy:`19`,r:`1`}),N(`circle`,{cx:`5`,cy:`19`,r:`1`})]}),lt=({maximized:e})=>e?P(Z,{children:[N(`path`,{d:`m14 10 7-7`}),N(`path`,{d:`M20 10h-6V4`}),N(`path`,{d:`m3 21 7-7`}),N(`path`,{d:`M4 14h6v6`})]}):P(Z,{children:[N(`path`,{d:`M15 3h6v6`}),N(`path`,{d:`m21 3-7 7`}),N(`path`,{d:`m3 21 7-7`}),N(`path`,{d:`M9 21H3v-6`})]}),ut=({defaultValue:e,onBlur:t,onKeyDown:n,ref:r,...i})=>{let{tab:a}=Y(),o=J(e=>e.cancelRename),s=J(e=>e.commitRename),c=w(null),l=w(!1);v(()=>{c.current?.focus(),c.current?.select()},[]);let u=e=>{n?.(e),e.key===`Enter`?(l.current=!0,s(a.id,e.currentTarget.value)):e.key===`Escape`&&(l.current=!0,o())},d=e=>{t?.(e),!l.current&&(l.current=!0,s(a.id,e.currentTarget.value))};return N(`input`,{"aria-label":`Rename ${a.name}`,...i,"data-dashfoo":`tab-rename`,defaultValue:e??a.name,onBlur:d,onKeyDown:u,ref:e=>{c.current=e,typeof r==`function`?r(e):r&&(r.current=e)},type:`text`})},dt=e=>{let{tab:t}=Y(),n=J(e=>e.editingTabId===t.id),r=J(e=>e.registerRenameInput);return v(()=>r(),[r]),n?N(ut,{...e}):null},ft=({children:e,onClick:t,...n})=>{let{index:r,tab:i}=Y(),a=J(e=>e.closeTab),o=J(e=>e.editingTabId===i.id),s=J(e=>e.tabsClosable),c=J(e=>e.visualSelected);if(!(s&&i.enableClose!==!1)||o)return null;let l=e=>{t?.(e),e.stopPropagation(),a(i.id)};return N(`button`,{"aria-label":`Close ${i.name}`,...n,"data-dashfoo":`tab-close`,onClick:l,tabIndex:r===c?0:-1,type:`button`,children:e??N(st,{})})},pt={display:`flex`,flexShrink:0},mt={display:`flex`,minWidth:0,overflowX:`auto`},ht=({style:e,...t})=>N(`div`,{...t,"data-dashfoo":`tabstrip`,style:{...pt,...e}}),gt=({onKeyDown:e,ref:t,style:n,...r})=>{let i=J(e=>e.node),a=J(e=>e.registerTablist),o=J(e=>e.selectTab),s=t=>{if(e?.(t),t.defaultPrevented||!(t.target instanceof HTMLElement)||!t.target.closest(`[role="tab"]`))return;let n=i.children.length;if(n===0)return;let r={ArrowLeft:(i.selected-1+n)%n,ArrowRight:(i.selected+1)%n,End:n-1,Home:0}[t.key];r!==void 0&&(t.preventDefault(),o(r,{focus:!0}))},c=C(()=>X(a,t),[a,t]);return N(`div`,{"aria-label":i.name??`Tabs`,...r,"aria-orientation":`horizontal`,"data-dashfoo":`tablist`,onKeyDown:s,ref:c,role:`tablist`,style:{...mt,...n},tabIndex:-1})},_t={alignItems:`center`,display:`flex`,flexShrink:0,marginInlineStart:`auto`},vt=({style:e,...t})=>N(`div`,{...t,"data-dashfoo":`tabset-toolbar`,style:{..._t,...e}}),yt=({children:e,ref:t,...n})=>{let r=J(e=>e.activeTab?.name??``),i=J(e=>e.isMaximized),a=J(e=>e.node),o=!V(e=>e.draggableTabsets)||i,{ref:s}=ge(a.id,o,r),c=C(()=>X(s,t),[s,t]);return o?null:N(`button`,{"aria-label":`Move tabset`,...n,"data-dashfoo":`tabset-grip`,ref:c,type:`button`,children:e??N(ct,{})})},Q=({children:e,onClick:t,...n})=>{let r=J(e=>e.isMaximized),i=J(e=>e.showMaximize),a=J(e=>e.toggleMaximize);if(!i)return null;let o=e=>{t?.(e),a()};return N(`button`,{"aria-label":r?`Restore`:`Maximize`,...n,"aria-pressed":r,"data-dashfoo":`tabset-maximize`,onClick:o,type:`button`,children:e??N(lt,{maximized:r})})},bt=()=>{let e=V(e=>e.draggableTabsets),t=V(e=>e.renderTabsetToolbar),n=V(e=>e.tabLocation),r=V(e=>e.tabStripEnabled),i=J(e=>e.isMaximized),a=J(e=>e.node),o=J(e=>e.overflowItems.length),s=J(e=>e.showMaximize)||t!==void 0||o>0||e&&!i,c=r?P(ht,{children:[N(gt,{children:a.children.map(e=>P(it,{tab:e,children:[N(at,{}),N(dt,{}),N(ft,{})]},e.id))}),s?P(vt,{children:[N(Ze,{}),N(yt,{}),t?.(a),N(Q,{})]}):null]}):null,l=N(qe,{});return n===`bottom`?P(ne,{children:[l,c]}):P(ne,{children:[c,l]})},xt=({node:e})=>N(nt,{node:e,children:N(bt,{})}),St=e=>`${e.value}${e.unit}`,Ct=e=>e?.unit===`px`?e.value:void 0,wt={display:`flex`,flex:1,minHeight:0,minWidth:0},Tt=.01,Et=(e,t)=>e.reduce((e,n)=>(e[n.id]=(n.weight??1)/t*100,e),{}),Dt=(e,t)=>{let n=Object.keys(e),r=Object.keys(t);return n.length===r.length?r.every(n=>Math.abs((e[n]??NaN)-t[n])<=Tt):!1},Ot=(e,t,n)=>{let r=Ct(e.min);if(r!==void 0)return r;if(e.min!==void 0)return;if(e.type===`tabset`)return n;let i=[];for(let r of e.children){let e=Ot(r,t,n);if(e===void 0)return;i.push(e)}if(i.length!==0)return e.orientation===t?i.reduce((e,t)=>e+t,0):Math.max(...i)},kt=({node:e,renderTabset:t})=>{let n=V(e=>e.dispatch),r=V(e=>e.tabsetMinSize),i=e.orientation===`row`?`horizontal`:`vertical`,a=e.children.reduce((e,t)=>e+(t.weight??1),0),o=C(()=>Et(e.children,a),[e.children,a]),s=w(!1),c=w(null),l=w(!1);return S(()=>{let e=c.current;if(!e||Dt(e.getLayout(),o))return;l.current=!0,e.setLayout(o);let t=requestAnimationFrame(()=>{l.current=!1});return()=>{cancelAnimationFrame(t),l.current=!1}},[o]),N(re,{"data-dashfoo":`row`,groupRef:c,onLayoutChanged:t=>{if(!s.current){s.current=!0;return}if(l.current)return;let r=e.children.map(e=>t[e.id]??e.weight??1);r.some((t,n)=>Math.abs(t-(e.children[n]?.weight??1))>Tt)&&n({rowId:e.id,type:`adjustSplit`,weights:r})},orientation:i,style:wt,children:e.children.map((n,i)=>{let o=(n.weight??1)/a*100,s=n.min?St(n.min):void 0;if(s===void 0){let t=Ot(n,e.orientation,r);s=t===void 0?void 0:`${t}px`}let c=n.max?St(n.max):void 0;return P(f,{children:[i>0?N(ae,{"data-dashfoo":`splitter`}):null,N(ie,{defaultSize:`${o}%`,id:n.id,maxSize:c,minSize:s,children:n.type===`row`?N(kt,{node:n,renderTabset:t}):t?.(n)??N(xt,{node:n})})]},n.id)})},e.children.map(e=>e.id).join(`|`))},$={DragLayer:ze,Root:Ve,Rows:kt,Tabset:xt},At=(e,t)=>e===void 0||!t?null:typeof e==`string`?{debounceMs:300,key:e,storage:F}:{debounceMs:e.debounceMs??300,key:e.key,storage:e.storage??F},jt=new Set,Mt=h((e,t)=>{let{closableTabs:n=!0,components:r,defaultModel:a,draggableTabsets:o=!0,factory:s,keepMounted:c=!1,maximizable:l=!0,model:u,onAction:d,onActiveTabsetChange:f,onMaximizedTabsetChange:p,onModelChange:m,persist:h,renamableTabs:_=!0,renderTabLabel:v,renderTabsetToolbar:y}=e,x=se(C(()=>At(h,a!==void 0),[h,a]),a),S=g((e,t)=>{x.save(e),m?.(e,t)},[m,x]),w=ce({defaultModel:x.initialModel??a,model:u,onAction:d,onActiveTabsetChange:f,onMaximizedTabsetChange:p,onModelChange:S});b(t,()=>({addTab:(e,t)=>w.dispatch({index:t.index,location:t.location??`center`,tab:e,targetId:t.targetId,type:`addNode`}),canRedo:w.canRedo,canUndo:w.canUndo,closeTab:e=>w.dispatch({tabId:e,type:`deleteTab`}),dispatch:w.dispatch,getModel:()=>w.model,maximizeTabset:e=>w.dispatch({tabsetId:e,type:`setMaximizedTabset`}),redo:w.redo,renameTab:(e,t)=>w.dispatch({name:t,tabId:e,type:`renameTab`}),resetLayout:()=>{x.clear(),a!==void 0&&w.setModel(a)},selectTab:(e,t)=>w.dispatch({index:t,tabsetId:e,type:`selectTab`}),undo:w.undo}),[a,x,w]);let T=g(e=>{if(s)return s(e);let t=r?.[e.component];return t?N(t,{node:e}):(r&&!jt.has(e.component)&&(jt.add(e.component),console.warn(`[dashfoo] no component registered for "${e.component}"`)),null)},[r,s]),E=w.model.maximizedTabsetId?i(w.model,w.model.maximizedTabsetId):void 0;return N($.Root,{closableTabs:n,dispatch:w.dispatch,draggableTabsets:o,keepMounted:c,maximizable:l,model:w.model,renamableTabs:_,renderTab:T,renderTabLabel:v,renderTabsetToolbar:y,children:N($.DragLayer,{children:E?N($.Tabset,{node:E}):N($.Rows,{node:w.model.layout})})})});Mt.displayName=`DashfooLayout`;const Nt=({children:e})=>{let[t]=T(ue);return x(()=>()=>t.destroy(),[t]),N(L.Provider,{value:t,children:e})},Pt={Badge:e=>N(`span`,{...e,"data-dashfoo":`panel-badge`}),Body:e=>N(`div`,{...e,"data-dashfoo":`panel-body`}),Header:e=>N(`div`,{...e,"data-dashfoo":`panel-header`}),Icon:e=>N(`span`,{...e,"data-dashfoo":`panel-icon`}),Root:e=>N(`div`,{...e,"data-dashfoo":`panel`}),Title:e=>N(`span`,{...e,"data-dashfoo":`panel-title`})},Ft={CloseButton:ft,Content:qe,Grip:yt,MaximizeButton:Q,OverflowMenu:Ze,RenameInput:dt,Root:nt,Tab:it,Tablist:gt,TabStrip:ht,Toolbar:vt,Trigger:at},It=()=>typeof window<`u`&&typeof window.matchMedia==`function`,Lt=(e,t)=>{let{query:n}=e;return n?`maxWidth`in n?t<=n.maxWidth:It()&&window.matchMedia(n.media).matches:!0},Rt=(e,t)=>{let n=e.at(-1);if(!n)throw Error(`useResponsiveModel requires at least one breakpoint.`);return e.find(e=>Lt(e,t))??n},zt=({breakpoints:e})=>{let[t,n]=T(1/0),[,r]=T(0),i=w(null),a=g(e=>{if(i.current?.disconnect(),!e)return;let t=new ResizeObserver(e=>{let t=e[0];t&&n(t.contentRect.width)});t.observe(e),i.current=t},[]);v(()=>()=>{i.current?.disconnect()},[]),v(()=>{if(!It())return;let t=e.flatMap(e=>e.query&&`media`in e.query?[window.matchMedia(e.query.media)]:[]);if(t.length===0)return;let n=()=>{r(e=>e+1)};for(let e of t)e.addEventListener(`change`,n);return()=>{for(let e of t)e.removeEventListener(`change`,n)}},[e]);let o=Rt(e,t);return{breakpoint:o.id,containerRef:a,defaultModel:o.model,key:o.id}};export{Nt as DashfooDragProvider,Mt as DashfooLayout,$ as Layout,Pt as Panel,Ft as Tabset,Rt as activeBreakpoint,F as localStorageAdapter,Lt as matchBreakpoint,oe as memoryStorageAdapter,ce as useDashfooStore,z as useDragSubject,ve as useExternalTabSource,V as useLayout,se as usePersistence,zt as useResponsiveModel,Y as useTab,J as useTabset};
|
|
8
|
+
`);return r!==null&&r!==t.element},me=()=>new ee({plugins:e=>e.filter(e=>e!==j&&e!==ne),sensors:()=>[M.configure({preventActivation:pe})]}),B=g(null),V=g(null),H=()=>N(()=>({intent:null,intentOwner:null,subject:null})),he=(e,t)=>e===t||e!==null&&t!==null&&e.index===t.index&&e.location===t.location&&e.targetId===t.targetId,ge=(e,t)=>e===t||e!==null&&t!==null&&e.id===t.id&&e.kind===t.kind,_e=H(),U=g(null),ve=()=>{let e=D(null),t=D(null);return{draggableRef:t,elementRef:e,ref:y(n=>{e.current=n,t.current&&(t.current.element=n??void 0)},[])}},W=(e,t,n)=>{let r=b(B),i=b(V),a=r?.manager??i,{draggableRef:o,elementRef:s,ref:c}=ve(),{data:l,id:u}=e;return x(()=>{if(!a||t)return;let e=new te({data:{...l,label:``},id:u},a);return e.element=s.current??void 0,o.current=e,()=>{e.destroy(),o.current=null}},[l,t,o,s,u,a]),x(()=>{let e=o.current;e&&(e.data={...l,label:n})},[l,o,n]),{ref:c}},ye=(e,t=!1,n=``)=>W({data:E(()=>({type:`tab`}),[]),id:e},t,n),be=(e,t=!1,n=``)=>W({data:E(()=>({tabsetId:e,type:`tabset`}),[e]),id:`grip-${e}`},t,n),xe=e=>{let t=b(B);return{ref:y(n=>{t?.registerTabset(e,n)},[t,e])}},Se=({createTab:e,disabled:t=!1,label:n=``})=>{let r=S(),i=D(e);return x(()=>{i.current=e}),W({data:E(()=>({createTab:()=>i.current(),type:`external`}),[]),id:`external-${r}`},t,n)},Ce=()=>P(b(U)??_e,e=>e.subject),we=()=>P(b(U)??_e,e=>e.intent),Te=e=>N(()=>e),Ee=g(null),G=e=>{let t=b(Ee);if(t===null)throw Error(`dashfoo components must be rendered inside <DashfooLayout> or <Layout.Root>.`);return P(t,e)},De=(e,t)=>e.x>=t.x&&e.x<=t.x+t.width&&e.y>=t.y&&e.y<=t.y+t.height,Oe=(e,t)=>{let n=e.findIndex(e=>t<e.x+e.width/2);return n===-1?e.length:n},ke=(e,t,n)=>{let r=t[n],i=t.at(-1),a=(r?.x??(i?i.x+i.width:e.x))-4/2;return{height:e.height,width:4,x:Math.max(e.x,Math.min(a,e.x+e.width-4)),y:e.y}},Ae=(e,t,n)=>!(e===`grip-${t}`||n.length===1&&n[0]===e),je=new Set,K=(e,t)=>{je.has(e)||(je.add(e),console.warn(`[dashfoo] ${t}`))},Me={boxSizing:`border-box`,pointerEvents:`none`,position:`fixed`,transition:`var(--dashfoo-dock-transition, left 60ms, top 60ms, width 60ms, height 60ms)`,zIndex:9999},Ne=e=>({...Me,background:`var(--dashfoo-dock-fill, oklch(0.556 0 0 / 0.18))`,border:`var(--dashfoo-dock-border-width, 1px) solid var(--dashfoo-dock-border, oklch(0.708 0 0 / 0.75))`,borderRadius:`var(--dashfoo-dock-radius, 6px)`,height:e.height,left:e.x,top:e.y,width:e.width}),Pe=e=>({...Ne(e),borderRadius:`var(--dashfoo-dock-line-radius, 2px)`}),Fe=(e,t)=>[...e.querySelectorAll(`[data-dashfoo="tab-item"]`)].flatMap(e=>e.querySelector(`[data-dashfoo="tab"]`)?.dataset.tabId===t?[]:[e.getBoundingClientRect()]),Ie=({actorRef:e,getTabsetElement:t})=>{let n=A(e,e=>e.context.intent),r=A(e,e=>e.context.subject?.id);if(!n)return null;let i=t(n.targetId);if(!i)return null;if(n.location===`center`){let e=i.querySelector(`[data-dashfoo="tabstrip"]`);if(e)return I(`div`,{"data-dashfoo":`dock-indicator`,style:Pe(ke(e.getBoundingClientRect(),Fe(e,r),n.index??0))})}return I(`div`,{"data-dashfoo":`dock-indicator`,style:Ne(m(i.getBoundingClientRect(),n.location))})},Le={left:0,position:`fixed`,top:0,zIndex:9999},Re=({overlayRef:e,preview:t})=>t?I(`div`,{"data-dashfoo":`drag-preview`,ref:e,style:{...Le,transform:`translate(${t.x}px, ${t.y}px)`},children:t.label}):null,ze=(e,t)=>[...e.querySelectorAll(`[data-dashfoo="tab"]`)].flatMap(e=>e.dataset.tabId===t?[]:[e.getBoundingClientRect()]),Be=(e,t,n,r)=>{let i=t.querySelector(`[data-dashfoo="tabstrip"]`);if(i&&De(n,i.getBoundingClientRect()))return{index:Oe(ze(i,r),n.x),location:`center`,targetId:e};let a=c(n,t.getBoundingClientRect()),o=a.kind===`tab`?`center`:`split-${a.edge}`;return o===`center`&&i?{index:ze(i,r).length,location:o,targetId:e}:{location:o,targetId:e}},q={x:12,y:8},Ve=e=>{let t=e?.data?.label;return typeof t==`string`?t:``},He=e=>typeof e==`function`,Ue=e=>{let t=e.data;if(t?.type===`tabset`)return{id:String(t.tabsetId),kind:`tabset`};if(t?.type===`external`){if(!He(t.createTab))return console.warn(`[dashfoo] external drag source is missing its createTab function`),null;let n;try{n=t.createTab()}catch(e){return console.warn(`[dashfoo] external drag source createTab threw`,e),null}let r=f.safeParse(n);return r.success?{id:String(e.id),kind:`external`,tab:r.data}:(console.warn(`[dashfoo] external drag source returned an invalid tab`,r.error),null)}return{id:String(e.id),kind:`tab`}},We=({children:e,onCommit:t,splitDock:n})=>{let i=k(r),a=b(Ee),[o]=O(()=>new Map),s=D(null),[c,l]=O(null),u=b(V),[d]=O(()=>u?null:me());w(()=>()=>d?.destroy(),[d]);let f=u??d;if(f===null)throw Error(`[dashfoo] DashfooDragProvider unmounted while its layout is still mounted; keep the provider above the layout or remount the layout`);let p=b(U),[m]=O(()=>p??H()),h=S();x(()=>{let e=i.subscribe(e=>{let{intent:t,subject:n}=e.context,r=m.getState();t===null?r.intent!==null&&r.intentOwner===h&&m.setState({intent:null,intentOwner:null}):(!he(r.intent,t)||r.intentOwner!==h)&&m.setState({intent:t,intentOwner:h}),ge(m.getState().subject,n)||m.setState({subject:n})});return()=>{e.unsubscribe()}},[i,h,m]);let g=D(t),_=D(n);x(()=>{g.current=t,_.current=n}),x(()=>{let e=i.on(`COMMIT`,e=>{if(a?.getState().editable===!1)return;let t=g.current??a?.getState().dispatch;if(!t){K(`drag-layer-no-commit`,`Layout.DragLayer has neither an onCommit prop nor a <Layout.Root> above it; drops are ignored`);return}t(e.action)});return()=>{e.unsubscribe()}},[i,a]);let v=y((e,t)=>{t?o.set(e,t):o.delete(e)},[o]),C=y(e=>o.get(e),[o]),T=y(e=>{s.current=e},[]);x(()=>{let e=e=>{for(let[t,n]of o)if(De(e,n.getBoundingClientRect()))return{element:n,id:t}},t=(e,t,n,r)=>{if(a?.getState().editable===!1||!Ae(r,e,[...t.querySelectorAll(`[data-dashfoo="tab"]`)].map(e=>e.dataset.tabId??``)))return null;let i=Be(e,t,n,r);return!(_.current??a?.getState().splitDock??!0)&&i.location.startsWith(`split-`)?{location:`center`,targetId:e}:i},n=e=>{let t=s.current;t&&(t.style.transform=`translate(${e.x+q.x}px, ${e.y+q.y}px)`)},r=e=>{let t=e.operation.source;if(!t)return;let n=Ue(t);if(!n)return;let r=e.operation.position.current;l({label:Ve(t),x:r.x+q.x,y:r.y+q.y}),i.send({subject:n,type:`START`})},c=r=>{let a=r.operation,o=a.position.current;n(o);let s=a.source?String(a.source.id):void 0,c=e(o);i.send({intent:c?t(c.id,c.element,o,s):null,type:`OVER`})},u=n=>{if(l(null),n.canceled){i.send({type:`CANCEL`});return}let r=n.operation,a=r.position.current,o=r.source?String(r.source.id):void 0,s=e(a);s&&i.send({intent:t(s.id,s.element,a,o),type:`OVER`}),i.send({type:`DROP`})},d=f.monitor.addEventListener(`dragstart`,r),p=f.monitor.addEventListener(`dragmove`,c),m=f.monitor.addEventListener(`dragend`,u);return()=>{d(),p(),m()}},[i,a,f,o]);let A=E(()=>({manager:f,registerTabset:v}),[f,v]);return I(B.Provider,{value:A,children:L(U.Provider,{value:m,children:[e,I(Ie,{actorRef:i,getTabsetElement:C}),I(Re,{overlayRef:T,preview:c})]})})},Ge={display:`flex`,height:`100%`,width:`100%`},Ke=({children:e,closableTabs:t=!0,dispatch:n,draggableTabs:r=!0,draggableTabsets:i=!0,editable:a=!0,keepMounted:o=!1,maximizable:s=!0,model:c,renamableTabs:l=!0,renderTab:u,renderTabLabel:d,renderTabsetToolbar:f,resizableSplits:p=!0,rootRef:m,snap:h,style:g,..._})=>{let v=c.global,y={closableTabs:a&&t&&v.tabEnableClose!==!1,dispatch:n,draggableTabs:a&&r&&v.tabEnableDrag!==!1,draggableTabsets:a&&i,editable:a,keepMounted:o,maximizable:s&&v.tabSetEnableMaximize!==!1,maximizedTabsetId:c.maximizedTabsetId,renamableTabs:a&&l&&v.tabEnableRename!==!1,renderTab:u,renderTabLabel:d,renderTabsetToolbar:f,resizableSplits:a&&p&&v.enableSplitResize!==!1,snap:h??v.snap??null,splitDock:v.enableSplitDock!==!1,tabLocation:v.tabLocation??`top`,tabsetMinSize:v.tabSetMinSize??320,tabStripEnabled:v.tabSetEnableTabStrip!==!1},[b]=O(()=>Te(y));T(()=>{b.setState(y)});let x=v.splitterSize===void 0?{...Ge,...g}:{...Ge,"--dashfoo-splitter-size":`${v.splitterSize}px`,...g};return I(Ee.Provider,{value:b,children:I(`div`,{..._,"data-dashfoo":`layout`,ref:m,style:x,children:e})})},J=(e,t)=>`dashfoo-tab-${e}-${t}`,qe=e=>`dashfoo-panel-${e}`,Je=(e,t,n)=>e?.querySelector(`#${CSS.escape(J(t,n))}`)??null,Ye=e=>N((t,n)=>({...e,cancelRename:()=>t({editingTabId:null}),closeTab:e=>{t({restoreFocus:!0}),n().dispatch({tabId:e,type:`deleteTab`})},commitRename:(e,r)=>{t({editingTabId:null});let{node:i,renameTab:a}=n(),o=r.trim(),s=i.children.find(t=>t.id===e)?.name;o&&o!==s&&a(e,o)},editingTabId:null,overflowItems:[],registerRenameInput:()=>(t(e=>({renameInputCount:e.renameInputCount+1})),()=>t(e=>({renameInputCount:e.renameInputCount-1}))),registerTablist:e=>{if(e){let r=n().tablistElement;r&&r!==e&&K(`duplicate-tablist-${n().node.id}`,`more than one <Tabset.Tablist> under the same <Tabset.Root>; the last one wins for keyboard focus and overflow`),t({tablistElement:e});return}t({tablistElement:null})},renameInputCount:0,renameTab:(e,t)=>n().dispatch({name:t,tabId:e,type:`renameTab`}),restoreFocus:!1,selectOverflowTab:e=>{let{dispatch:t,node:r,tablistElement:i}=n(),a=r.children.findIndex(t=>t.id===e);a!==-1&&(t({index:a,tabsetId:r.id,type:`selectTab`}),Je(i,r.id,e)?.scrollIntoView({block:`nearest`,inline:`nearest`}))},selectTab:(e,t)=>{let{dispatch:r,node:i,tablistElement:a}=n();if(r({index:e,tabsetId:i.id,type:`selectTab`}),t?.focus){let t=i.children[e]?.id;t&&Je(a,i.id,t)?.focus()}},startEditing:e=>{let{node:r,renameInputCount:i,tabsRenamable:a}=n(),o=r.children.find(t=>t.id===e);if(!(!a||o?.enableRename===!1)){if(i===0){K(`missing-rename-input-${r.id}`,`startEditing needs a <Tabset.RenameInput> in the composition; ignoring`);return}t({editingTabId:e})}},tablistElement:null,toggleMaximize:()=>{let{dispatch:e,isMaximized:t,node:r}=n();e({tabsetId:t?null:r.id,type:`setMaximizedTabset`})}})),Xe=g(null),Y=e=>{let t=b(Xe);if(t===null)throw Error(`dashfoo Tabset parts must be rendered inside <Tabset.Root>.`);return P(t,e)},Ze=g(null),X=()=>{let e=b(Ze);if(e===null)throw Error(`dashfoo tab parts must be rendered inside <Tabset.Tab>.`);return e},Qe={flex:1,minHeight:0,overflow:`auto`},$e=({render:e,tab:t})=>{let n=G(e=>e.renderTab);return e?e(t):n(t)},et=({children:e,style:t,...n})=>{let r=Y(e=>e.node),i=Y(e=>e.visualSelected),a=G(e=>e.keepMounted),o={...Qe,...t};if(a&&r.children.length>0)return r.children.map((t,a)=>_(`div`,{...n,"aria-labelledby":J(r.id,t.id),"data-dashfoo":`tabcontent`,hidden:a!==i||void 0,id:a===i?qe(r.id):void 0,key:t.id,role:a===i?`tabpanel`:void 0,style:o,tabIndex:a===i?0:void 0},I($e,{render:e,tab:t})));let s=r.children[i];return s?I(`div`,{...n,"aria-labelledby":J(r.id,s.id),"data-dashfoo":`tabcontent`,id:qe(r.id),role:`tabpanel`,style:o,tabIndex:0,children:I($e,{render:e,tab:s})}):I(`div`,{...n,"data-dashfoo":`tabcontent`,style:o})},tt=()=>L(`svg`,{"aria-hidden":`true`,fill:`currentColor`,height:`10`,viewBox:`0 0 10 10`,width:`10`,children:[I(`circle`,{cx:`2`,cy:`5`,r:`1`}),I(`circle`,{cx:`5`,cy:`5`,r:`1`}),I(`circle`,{cx:`8`,cy:`5`,r:`1`})]}),nt={position:`absolute`,right:0,top:`100%`,zIndex:10},rt=({items:e,onSelect:t})=>{let[n,r]=O(!1),[i,a]=O(0),o=D(null),s=D(null),c=D([]);x(()=>{if(!n)return;let e=e=>{e.target instanceof Node&&!o.current?.contains(e.target)&&r(!1)};return document.addEventListener(`pointerdown`,e),()=>{document.removeEventListener(`pointerdown`,e)}},[n]),x(()=>{n&&c.current[0]?.focus()},[n]);let l=()=>{r(!1),s.current?.focus()},u=e=>{a(e),c.current[e]?.focus()},d=()=>{a(0),r(e=>!e)},f=e=>{t(e),l()};return L(`div`,{"data-dashfoo":`tab-overflow-root`,ref:o,style:{position:`relative`},children:[I(`button`,{"aria-expanded":n,"aria-haspopup":`menu`,"aria-label":`More tabs`,"data-dashfoo":`tab-overflow`,onClick:d,ref:s,type:`button`,children:I(tt,{})}),n?I(`div`,{"data-dashfoo":`tab-overflow-menu`,onBlur:e=>{e.relatedTarget instanceof Node&&o.current?.contains(e.relatedTarget)||r(!1)},onKeyDown:t=>{let n=e.length-1;switch(t.key){case`ArrowDown`:t.preventDefault(),u(i>=n?0:i+1);break;case`ArrowUp`:t.preventDefault(),u(i<=0?n:i-1);break;case`Home`:t.preventDefault(),u(0);break;case`End`:t.preventDefault(),u(n);break;case`Escape`:t.preventDefault(),l();break;default:break}},role:`menu`,style:nt,tabIndex:-1,children:e.map((e,t)=>I(`button`,{"data-dashfoo":`tab-overflow-item`,onClick:()=>f(e.id),ref:e=>{c.current[t]=e},role:`menuitem`,tabIndex:t===i?0:-1,type:`button`,children:e.name},e.id))}):null]})},it=()=>{let e=Y(e=>e.overflowItems),t=Y(e=>e.selectOverflowTab);return e.length===0?null:I(rt,{items:e,onSelect:t})},at=e=>{let t=e.getBoundingClientRect();return[...e.querySelectorAll(`[data-dashfoo="tab"]`)].flatMap(e=>{let n=e.getBoundingClientRect();return(n.right>t.right+1||n.left<t.left-1)&&e.dataset.tabId?[e.dataset.tabId]:[]})},ot=(e,t)=>{let[n,r]=O([]);return x(()=>{let t=()=>{r(t=>{let n=e?at(e):[];return n.length===0&&t.length===0?t:n})};if(t(),!e)return;let n=new ResizeObserver(t);return n.observe(e),e.addEventListener(`scroll`,t,{passive:!0}),()=>{n.disconnect(),e.removeEventListener(`scroll`,t)}},[e,t]),n},st=(e,t)=>t+1<e?t+1:t-1,ct={display:`flex`,flexDirection:`column`,height:`100%`,minHeight:0,minWidth:0,width:`100%`},lt=({children:e,node:t,ref:n,style:r,...i})=>{let a=G(e=>e.closableTabs),o=G(e=>e.dispatch),s=G(e=>e.maximizable),c=G(e=>e.maximizedTabsetId),l=G(e=>e.renamableTabs),u=G(e=>e.tabLocation),{ref:d}=xe(t.id),f=Ce(),p=D(null),m=c===t.id,h=s&&t.enableMaximize!==!1,g=a&&t.enableClose!==!1,_=l,v=f?.kind===`tab`?t.children.findIndex(e=>e.id===f.id):-1,y=v!==-1&&v===t.selected?st(t.children.length,v):t.selected,b=t.children[y],S=f?.kind===`tabset`&&f.id===t.id,C={activeTab:b,dispatch:o,isMaximized:m,node:t,showMaximize:h,tabsClosable:g,tabsRenamable:_,visualSelected:y},[w]=O(()=>Ye(C));T(()=>{w.setState(C)});let k=ot(P(w,e=>e.tablistElement),t.children.map(e=>`${e.id}:${e.name}`).join(`|`)),A=E(()=>{let e=new Map(t.children.map(e=>[e.id,e.name]));return k.map(t=>({id:t,name:e.get(t)??t}))},[k,t.children]);return T(()=>{w.setState({overflowItems:A})},[A,w]),x(()=>{if(!w.getState().restoreFocus)return;w.setState({restoreFocus:!1});let e=t.children[t.selected]?.id;e?p.current?.querySelector(`#${CSS.escape(J(t.id,e))}`)?.focus():p.current?.focus()},[t.children,t.selected,t.id,w]),I(Xe.Provider,{value:w,children:I(`div`,{...i,"data-dashfoo":`tabset`,"data-dragging-source":S||void 0,"data-tab-location":u,ref:e=>{d(e),p.current=e,typeof n==`function`?n(e):n&&(n.current=e)},style:{...ct,...r},tabIndex:-1,children:e})})},ut=(e,t)=>{typeof e==`function`?e(t):e&&(e.current=t)},Z=(...e)=>t=>{for(let n of e)ut(n,t)},dt=({children:e,ref:t,tab:n,...r})=>{let i=b(Xe),a=Y(e=>e.node),o=Y(e=>e.editingTabId===n.id),s=Ce(),c=s?.kind===`tab`&&s.id===n.id,l=D(null),u=D(!1),d=a.children.findIndex(e=>e.id===n.id);x(()=>{i?.getState().node.children.some(e=>e.id===n.id)===!1&&K(`orphan-tab-${n.id}`,`<Tabset.Tab> for "${n.id}" is not a child of its Tabset.Root's node; it renders but can never be selected`)},[d===-1,i,n.id]),x(()=>{u.current&&!o&&l.current?.querySelector(`[data-dashfoo="tab"]`)?.focus(),u.current=o},[o]);let f=E(()=>({index:d,tab:n}),[d,n]),p=E(()=>Z(l,t),[t]);return I(Ze.Provider,{value:f,children:I(`span`,{...r,"data-dashfoo":`tab-item`,"data-dragging":c||void 0,ref:p,children:e})})},ft=({children:e,onClick:t,onDoubleClick:n,ref:r,...i})=>{let{index:a,tab:o}=X(),s=Y(e=>e.node),c=Y(e=>e.editingTabId===o.id),l=Y(e=>e.selectTab),u=Y(e=>e.startEditing),d=Y(e=>e.tabsRenamable),f=Y(e=>e.visualSelected),p=G(e=>e.draggableTabs),m=G(e=>e.renderTabLabel),{ref:h}=ye(o.id,!p||o.enableDrag===!1,o.name),g=E(()=>Z(h,r),[h,r]);if(c)return null;let _=a===f,v=d&&o.enableRename!==!1,y=e=>{t?.(e),l(a)},b=e=>{n?.(e),v&&u(o.id)};return I(`button`,{"aria-label":m?o.name:void 0,...i,"aria-controls":qe(s.id),"aria-selected":_,"data-dashfoo":`tab`,"data-tab-id":o.id,id:J(s.id,o.id),onClick:y,onDoubleClick:b,ref:g,role:`tab`,tabIndex:_?0:-1,type:`button`,children:e??(m?m(o):o.name)})},pt={pointerEvents:`none`},Q=({children:e})=>I(`svg`,{"aria-hidden":`true`,fill:`none`,height:`12`,stroke:`currentColor`,strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:`2`,style:pt,viewBox:`0 0 24 24`,width:`12`,children:e}),mt=()=>L(Q,{children:[I(`path`,{d:`M18 6 6 18`}),I(`path`,{d:`m6 6 12 12`})]}),ht=()=>L(Q,{children:[I(`circle`,{cx:`12`,cy:`5`,r:`1`}),I(`circle`,{cx:`19`,cy:`5`,r:`1`}),I(`circle`,{cx:`5`,cy:`5`,r:`1`}),I(`circle`,{cx:`12`,cy:`12`,r:`1`}),I(`circle`,{cx:`19`,cy:`12`,r:`1`}),I(`circle`,{cx:`5`,cy:`12`,r:`1`}),I(`circle`,{cx:`12`,cy:`19`,r:`1`}),I(`circle`,{cx:`19`,cy:`19`,r:`1`}),I(`circle`,{cx:`5`,cy:`19`,r:`1`})]}),gt=({maximized:e})=>e?L(Q,{children:[I(`path`,{d:`m14 10 7-7`}),I(`path`,{d:`M20 10h-6V4`}),I(`path`,{d:`m3 21 7-7`}),I(`path`,{d:`M4 14h6v6`})]}):L(Q,{children:[I(`path`,{d:`M15 3h6v6`}),I(`path`,{d:`m21 3-7 7`}),I(`path`,{d:`m3 21 7-7`}),I(`path`,{d:`M9 21H3v-6`})]}),_t=({defaultValue:e,onBlur:t,onKeyDown:n,ref:r,...i})=>{let{tab:a}=X(),o=Y(e=>e.cancelRename),s=Y(e=>e.commitRename),c=D(null),l=D(!1),u=E(()=>Z(c,r),[r]);x(()=>{c.current?.focus(),c.current?.select()},[]);let d=e=>{n?.(e),e.key===`Enter`?(l.current=!0,s(a.id,e.currentTarget.value)):e.key===`Escape`&&(l.current=!0,o())},f=e=>{t?.(e),!l.current&&(l.current=!0,s(a.id,e.currentTarget.value))};return I(`input`,{"aria-label":`Rename ${a.name}`,...i,"data-dashfoo":`tab-rename`,defaultValue:e??a.name,onBlur:f,onKeyDown:d,ref:u,type:`text`})},vt=e=>{let{tab:t}=X(),n=Y(e=>e.editingTabId===t.id),r=Y(e=>e.registerRenameInput);return x(()=>r(),[r]),n?I(_t,{...e}):null},yt=({children:e,onClick:t,...n})=>{let{index:r,tab:i}=X(),a=Y(e=>e.closeTab),o=Y(e=>e.editingTabId===i.id),s=Y(e=>e.tabsClosable),c=Y(e=>e.visualSelected);if(!(s&&i.enableClose!==!1)||o)return null;let l=e=>{t?.(e),e.stopPropagation(),a(i.id)};return I(`button`,{"aria-label":`Close ${i.name}`,...n,"data-dashfoo":`tab-close`,onClick:l,tabIndex:r===c?0:-1,type:`button`,children:e??I(mt,{})})},bt={display:`flex`,flexShrink:0},xt={display:`flex`,minWidth:0,overflowX:`auto`},St=({style:e,...t})=>I(`div`,{...t,"data-dashfoo":`tabstrip`,style:{...bt,...e}}),Ct=({onKeyDown:e,ref:t,style:n,...r})=>{let i=Y(e=>e.node),a=Y(e=>e.registerTablist),o=Y(e=>e.selectTab),s=t=>{if(e?.(t),t.defaultPrevented||!(t.target instanceof HTMLElement)||!t.target.closest(`[role="tab"]`))return;let n=i.children.length;if(n===0)return;let r={ArrowLeft:(i.selected-1+n)%n,ArrowRight:(i.selected+1)%n,End:n-1,Home:0}[t.key];r!==void 0&&(t.preventDefault(),o(r,{focus:!0}))},c=E(()=>Z(a,t),[a,t]);return I(`div`,{"aria-label":i.name??`Tabs`,...r,"aria-orientation":`horizontal`,"data-dashfoo":`tablist`,onKeyDown:s,ref:c,role:`tablist`,style:{...xt,...n},tabIndex:-1})},wt={alignItems:`center`,display:`flex`,flexShrink:0,marginInlineStart:`auto`},Tt=({style:e,...t})=>I(`div`,{...t,"data-dashfoo":`tabset-toolbar`,style:{...wt,...e}}),Et=({children:e,ref:t,...n})=>{let r=Y(e=>e.activeTab?.name??``),i=Y(e=>e.isMaximized),a=Y(e=>e.node),o=!G(e=>e.draggableTabsets)||i,{ref:s}=be(a.id,o,r),c=E(()=>Z(s,t),[s,t]);return o?null:I(`button`,{"aria-label":`Move tabset`,...n,"data-dashfoo":`tabset-grip`,ref:c,type:`button`,children:e??I(ht,{})})},Dt=({children:e,onClick:t,...n})=>{let r=Y(e=>e.isMaximized),i=Y(e=>e.showMaximize),a=Y(e=>e.toggleMaximize);if(!i)return null;let o=e=>{t?.(e),a()};return I(`button`,{"aria-label":r?`Restore`:`Maximize`,...n,"aria-pressed":r,"data-dashfoo":`tabset-maximize`,onClick:o,type:`button`,children:e??I(gt,{maximized:r})})},Ot=I(et,{}),kt=()=>{let e=G(e=>e.draggableTabsets),t=G(e=>e.renderTabsetToolbar),n=G(e=>e.tabLocation),r=G(e=>e.tabStripEnabled),i=Y(e=>e.isMaximized),a=Y(e=>e.node),o=Y(e=>e.overflowItems.length),s=Y(e=>e.showMaximize)||t!==void 0||o>0||e&&!i,c=r?L(St,{children:[I(Ct,{children:a.children.map(e=>L(dt,{tab:e,children:[I(ft,{}),I(vt,{}),I(yt,{})]},e.id))}),s?L(Tt,{children:[I(it,{}),I(Et,{}),t?.(a),I(Dt,{})]}):null]}):null;return n===`bottom`?L(F,{children:[Ot,c]}):L(F,{children:[c,Ot]})},At=({node:e})=>I(lt,{node:e,children:I(kt,{})}),jt=e=>`${e.value}${e.unit}`,Mt=e=>e?.unit===`px`?e.value:void 0,Nt={display:`flex`,flex:1,minHeight:0,minWidth:0},Pt=.01,Ft=(e,t)=>e.reduce((e,n)=>(e[n.id]=(n.weight??1)/t*100,e),{}),It=(e,t)=>t.map(t=>e[t.id]??0),Lt=(e,t)=>t.reduce((t,n,r)=>(t[n.id]=e[r]??0,t),{}),Rt=(e,t)=>{let n=Object.keys(e),r=Object.keys(t);return n.length===r.length?r.every(n=>Math.abs((e[n]??NaN)-t[n])<=Pt):!1},zt=(e,t,n)=>{let r=Mt(e.min);if(r!==void 0)return r;if(e.min!==void 0)return;if(e.type===`tabset`)return n;let i=[];for(let r of e.children){let e=zt(r,t,n);if(e===void 0)return;i.push(e)}if(i.length!==0)return e.orientation===t?i.reduce((e,t)=>e+t,0):Math.max(...i)},Bt=({node:e,renderTabset:t})=>{let n=G(e=>e.dispatch),r=G(e=>e.resizableSplits),i=G(e=>e.tabsetMinSize),a=G(e=>e.snap),o=e.snap??a,s=l(o),c=e.orientation===`row`?`horizontal`:`vertical`,d=e.children.reduce((e,t)=>e+(t.weight??1),0),f=E(()=>Ft(e.children,d),[e.children,d]),p=D(!1),m=D(null),g=D(null),_=D(!1),v=D(null),y=D(!1),b=D(null),[x,S]=O(null),C=e=>{e!==b.current&&(b.current=e,S(e))},w=e=>{let t=g.current;t&&(e?t.dataset.dashfooSnapping=`true`:delete t.dataset.dashfooSnapping)};return T(()=>{let e=m.current;if(!e)return;let t=e.getLayout();if(!(Object.keys(t).length===Object.keys(f).length&&Object.keys(f).every(e=>e in t))||Rt(t,f))return;_.current=!0,e.setLayout(f);let n=requestAnimationFrame(()=>{_.current=!1});return()=>{cancelAnimationFrame(n),_.current=!1}},[f]),I(re,{"data-dashfoo":`row`,disabled:!r,elementRef:g,groupRef:m,onLayoutChange:s?t=>{if(y.current||_.current||!r||o===null||!s)return;let n=v.current;if(n===null)return;let{sizes:i,snapped:a}=u(It(t,e.children),n,o);if(C(a?n:null),!a){w(!1);return}w(!0);let c=m.current,l=Lt(i,e.children);!c||Rt(t,l)||(y.current=!0,c.setLayout(l),y.current=!1)}:void 0,onLayoutChanged:t=>{if(!p.current){p.current=!0;return}if(_.current)return;let i=v.current;if(v.current=null,C(null),w(!1),!r)return;let a=e.children.map(e=>t[e.id]??e.weight??1);i!==null&&o!==null&&s&&(a=u(a,i,o).sizes),a.some((t,n)=>Math.abs(t-(e.children[n]?.weight??1))>Pt)&&n({rowId:e.id,type:`adjustSplit`,weights:a})},orientation:c,style:Nt,children:e.children.map((n,a)=>{let o=(n.weight??1)/d*100,c=n.min?jt(n.min):void 0;if(c===void 0){let t=zt(n,e.orientation,i);c=t===void 0?void 0:`${t}px`}let l=n.max?jt(n.max):void 0;return L(h,{children:[a>0?I(ae,{"data-dashfoo":`splitter`,"data-dashfoo-snapped":x===a-1?`true`:void 0,disabled:!r,onPointerDown:s&&r?()=>{v.current=a-1}:void 0}):null,I(ie,{defaultSize:`${o}%`,id:n.id,maxSize:l,minSize:c,children:n.type===`row`?I(Bt,{node:n,renderTabset:t}):t?.(n)??I(At,{node:n})})]},n.id)})},e.id)},$={DragLayer:We,Root:Ke,Rows:Bt,Tabset:At},Vt=(e,t)=>e===void 0||!t?null:typeof e==`string`?{debounceMs:300,key:e,storage:R}:{debounceMs:e.debounceMs??300,key:e.key,storage:e.storage??R},Ht=new Set,Ut=v((e,t)=>{let{closableTabs:n=!0,components:r,defaultModel:a,draggableTabs:o=!0,draggableTabsets:s=!0,editable:c=!0,factory:l,keepMounted:u=!1,maximizable:f=!0,model:p,onAction:m,onActiveTabsetChange:h,onMaximizedTabsetChange:g,onModelChange:_,persist:v,renamableTabs:b=!0,renderTabLabel:x,renderTabsetToolbar:S,resizableSplits:w=!0,responsive:T,snap:O}=e,k=se(E(()=>Vt(v,a!==void 0),[v,a]),a),A=y((e,t)=>{k.save(e),_?.(e,t)},[_,k]),j=fe({defaultModel:k.initialModel??a,model:p,onAction:m,onActiveTabsetChange:h,onMaximizedTabsetChange:g,onModelChange:A});C(t,()=>({addTab:(e,t)=>j.dispatch({index:t.index,location:t.location??`center`,tab:e,targetId:t.targetId,type:`addNode`}),canRedo:j.canRedo,canUndo:j.canUndo,closeTab:e=>j.dispatch({tabId:e,type:`deleteTab`}),dispatch:j.dispatch,getModel:()=>j.model,maximizeTabset:e=>j.dispatch({tabsetId:e,type:`setMaximizedTabset`}),redo:j.redo,renameTab:(e,t)=>j.dispatch({name:t,tabId:e,type:`renameTab`}),resetLayout:()=>{k.clear(),a!==void 0&&j.setModel(a)},selectTab:(e,t)=>j.dispatch({index:t,tabsetId:e,type:`selectTab`}),undo:j.undo}),[a,k,j]);let ee=y(e=>{if(l)return l(e);let t=r?.[e.component];return t?I(t,{node:e}):(r&&!Ht.has(e.component)&&(Ht.add(e.component),console.warn(`[dashfoo] no component registered for "${e.component}"`)),null)},[r,l]),[te,ne]=z(),M=T!==void 0&&ne<=T.maxWidth,N=E(()=>M?d(j.model,T?.orientation):j.model,[M,T?.orientation,j.model]),P=D(M);P.current=M;let F=y(e=>{P.current&&e.type===`adjustSplit`||j.dispatch(e)},[j]),L=N.maximizedTabsetId?i(N,N.maximizedTabsetId):void 0;return I($.Root,{closableTabs:n,dispatch:F,draggableTabs:M?!1:o,draggableTabsets:M?!1:s,editable:c,keepMounted:u,maximizable:f,model:N,renamableTabs:b,renderTab:ee,renderTabLabel:x,renderTabsetToolbar:S,resizableSplits:M?!1:w,rootRef:te,snap:O,children:I($.DragLayer,{children:L?I($.Tabset,{node:L}):I($.Rows,{node:N.layout})})})});Ut.displayName=`DashfooLayout`;const Wt=({children:e})=>{let[t]=O(me);w(()=>()=>t.destroy(),[t]);let[n]=O(H);return I(V.Provider,{value:t,children:I(U.Provider,{value:n,children:e})})},Gt={Badge:e=>I(`span`,{...e,"data-dashfoo":`panel-badge`}),Body:e=>I(`div`,{...e,"data-dashfoo":`panel-body`}),Header:e=>I(`div`,{...e,"data-dashfoo":`panel-header`}),Icon:e=>I(`span`,{...e,"data-dashfoo":`panel-icon`}),Root:e=>I(`div`,{...e,"data-dashfoo":`panel`}),Title:e=>I(`span`,{...e,"data-dashfoo":`panel-title`})},Kt={CloseButton:yt,Content:et,Grip:Et,MaximizeButton:Dt,OverflowMenu:it,RenameInput:vt,Root:lt,Tab:dt,Tablist:Ct,TabStrip:St,Toolbar:Tt,Trigger:ft};export{Wt as DashfooDragProvider,Ut as DashfooLayout,$ as Layout,Gt as Panel,Kt as Tabset,ue as activeBreakpoint,R as localStorageAdapter,le as matchBreakpoint,oe as memoryStorageAdapter,z as useContainerWidth,fe as useDashfooStore,Ce as useDragSubject,we as useDropIntent,Se as useExternalTabSource,G as useLayout,se as usePersistence,de as useResponsiveModel,X as useTab,Y as useTabset};
|
|
9
9
|
//# sourceMappingURL=index.js.map
|