@dashfoo/core 0.5.2 → 1.0.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/dist/index.d.ts +159 -67
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +178 -80
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,31 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { assign, emit, setup } from "xstate";
|
|
3
3
|
|
|
4
|
+
//#region src/lib/clamp-selected.ts
|
|
5
|
+
/**
|
|
6
|
+
* Lives in `lib/` rather than beside `normalize`, because both the parse
|
|
7
|
+
* boundary (`tabsetNodeSchema`) and the tree walker that `normalize` uses need
|
|
8
|
+
* it, and importing it from either one would close a module cycle.
|
|
9
|
+
*/
|
|
10
|
+
const clampSelected = (length, selected) => {
|
|
11
|
+
if (length === 0) return 0;
|
|
12
|
+
const base = Number.isFinite(selected) ? Math.trunc(selected) : 0;
|
|
13
|
+
if (base < 0) return 0;
|
|
14
|
+
if (base > length - 1) return length - 1;
|
|
15
|
+
return base;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
//#endregion
|
|
4
19
|
//#region src/model/schema.ts
|
|
20
|
+
/**
|
|
21
|
+
* Weights are relative shares, and every reader that rendered one already
|
|
22
|
+
* treated a missing weight as a single share (`row-view` summed `weight ?? 1`),
|
|
23
|
+
* so filling it in at the boundary changes nothing on screen. The one place that
|
|
24
|
+
* read a missing weight as a percentage was `placeBesideTarget`, which is why
|
|
25
|
+
* docking beside an unweighted target halved it to `50` and left its unweighted
|
|
26
|
+
* siblings rendering as a sliver of the row.
|
|
27
|
+
*/
|
|
28
|
+
const DEFAULT_WEIGHT = 1;
|
|
5
29
|
const jsonValueSchema = z.lazy(() => z.union([
|
|
6
30
|
z.string(),
|
|
7
31
|
z.number(),
|
|
@@ -44,7 +68,14 @@ const tabNodeSchema = z.object({
|
|
|
44
68
|
name: z.string(),
|
|
45
69
|
type: z.literal("tab")
|
|
46
70
|
});
|
|
47
|
-
|
|
71
|
+
/**
|
|
72
|
+
* The pre-transform object, kept separate so `node-attrs` can `.pick()` mutable
|
|
73
|
+
* keys off it. Picking from the transformed schema is impossible (a pipe has no
|
|
74
|
+
* `.pick`), and picking `weight` with its default attached would inject that
|
|
75
|
+
* default: `.partial()` over a defaulted field still fills it in, which would
|
|
76
|
+
* rewrite a tabset's weight on every unrelated attribute update.
|
|
77
|
+
*/
|
|
78
|
+
const tabsetNodeObjectSchema = z.object({
|
|
48
79
|
children: z.array(tabNodeSchema),
|
|
49
80
|
enableClose: z.boolean().optional(),
|
|
50
81
|
enableMaximize: z.boolean().optional(),
|
|
@@ -54,8 +85,12 @@ const tabsetNodeSchema = z.object({
|
|
|
54
85
|
name: z.string().optional(),
|
|
55
86
|
selected: z.number().int(),
|
|
56
87
|
type: z.literal("tabset"),
|
|
57
|
-
weight: z.number().
|
|
88
|
+
weight: z.number().default(1)
|
|
58
89
|
});
|
|
90
|
+
const tabsetNodeSchema = tabsetNodeObjectSchema.transform((tabset) => ({
|
|
91
|
+
...tabset,
|
|
92
|
+
selected: clampSelected(tabset.children.length, tabset.selected)
|
|
93
|
+
}));
|
|
59
94
|
const rowNodeSchema = z.lazy(() => z.object({
|
|
60
95
|
children: z.array(z.union([rowNodeSchema, tabsetNodeSchema])),
|
|
61
96
|
id: z.string(),
|
|
@@ -64,7 +99,7 @@ const rowNodeSchema = z.lazy(() => z.object({
|
|
|
64
99
|
orientation: orientationSchema,
|
|
65
100
|
snap: snapSchema.optional(),
|
|
66
101
|
type: z.literal("row"),
|
|
67
|
-
weight: z.number().
|
|
102
|
+
weight: z.number().default(1)
|
|
68
103
|
}));
|
|
69
104
|
const geometrySchema = z.object({
|
|
70
105
|
height: z.number(),
|
|
@@ -95,7 +130,7 @@ const globalAttributesSchema = z.object({
|
|
|
95
130
|
});
|
|
96
131
|
const dashfooSchema = z.object({
|
|
97
132
|
activeTabsetId: z.string().optional(),
|
|
98
|
-
floats: z.array(floatNodeSchema).
|
|
133
|
+
floats: z.array(floatNodeSchema).default(() => []),
|
|
99
134
|
global: globalAttributesSchema,
|
|
100
135
|
layout: rowNodeSchema,
|
|
101
136
|
maximizedTabsetId: z.string().optional(),
|
|
@@ -103,7 +138,7 @@ const dashfooSchema = z.object({
|
|
|
103
138
|
});
|
|
104
139
|
|
|
105
140
|
//#endregion
|
|
106
|
-
//#region src/state/
|
|
141
|
+
//#region src/state/node-attrs.ts
|
|
107
142
|
const mutableTabAttrsSchema = tabNodeSchema.pick({
|
|
108
143
|
config: true,
|
|
109
144
|
enableClose: true,
|
|
@@ -111,14 +146,18 @@ const mutableTabAttrsSchema = tabNodeSchema.pick({
|
|
|
111
146
|
enableRename: true,
|
|
112
147
|
name: true
|
|
113
148
|
}).partial();
|
|
114
|
-
|
|
149
|
+
/**
|
|
150
|
+
* `weight` is re-declared instead of picked: `.partial()` over the schema's
|
|
151
|
+
* defaulted `weight` still injects the default, so `{ name: "x" }` would arrive
|
|
152
|
+
* at the reducer carrying a weight and silently resize the tabset.
|
|
153
|
+
*/
|
|
154
|
+
const mutableTabsetAttrsSchema = tabsetNodeObjectSchema.pick({
|
|
115
155
|
enableClose: true,
|
|
116
156
|
enableMaximize: true,
|
|
117
157
|
max: true,
|
|
118
158
|
min: true,
|
|
119
|
-
selected: true
|
|
120
|
-
|
|
121
|
-
}).partial();
|
|
159
|
+
selected: true
|
|
160
|
+
}).extend({ weight: z.number() }).partial();
|
|
122
161
|
const mutableRowAttrsSchema = z.object({
|
|
123
162
|
max: dimensionSchema.optional(),
|
|
124
163
|
min: dimensionSchema.optional(),
|
|
@@ -126,11 +165,19 @@ const mutableRowAttrsSchema = z.object({
|
|
|
126
165
|
snap: snapSchema.optional(),
|
|
127
166
|
weight: z.number()
|
|
128
167
|
}).partial();
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
168
|
+
/**
|
|
169
|
+
* One all-optional object carrying every mutable key of every node kind, not a
|
|
170
|
+
* union of the three: a union whose members are all fully optional matches on
|
|
171
|
+
* its first member and strips the keys the others own, which erased every
|
|
172
|
+
* tabset and row attribute at the parse boundary. The four keys that two kinds
|
|
173
|
+
* share (`enableClose`, `max`, `min`, `weight`) declare the same type on both
|
|
174
|
+
* sides, so merging widens nothing. Which keys are legal for a given node is
|
|
175
|
+
* enforced by the reducer against that node's own schema.
|
|
176
|
+
*/
|
|
177
|
+
const mutableNodeAttrsSchema = mutableTabAttrsSchema.extend(mutableTabsetAttrsSchema.shape).extend(mutableRowAttrsSchema.shape);
|
|
178
|
+
|
|
179
|
+
//#endregion
|
|
180
|
+
//#region src/state/actions.ts
|
|
134
181
|
const dockLocationSchema = z.enum([
|
|
135
182
|
"center",
|
|
136
183
|
"split-bottom",
|
|
@@ -243,8 +290,8 @@ const createTabId = () => createNodeId("tab");
|
|
|
243
290
|
|
|
244
291
|
//#endregion
|
|
245
292
|
//#region src/model/tree.ts
|
|
246
|
-
const collectRoots = (model) => [model.layout, ...
|
|
247
|
-
const findFloat = (model, floatId) =>
|
|
293
|
+
const collectRoots = (model) => [model.layout, ...model.floats.map((float) => float.layout)];
|
|
294
|
+
const findFloat = (model, floatId) => model.floats.find((float) => float.id === floatId);
|
|
248
295
|
/**
|
|
249
296
|
* Depth-first pre-order over one root: the row itself, then each child in
|
|
250
297
|
* order, descending into nested rows and into a tabset's tabs. Returning a
|
|
@@ -344,7 +391,7 @@ const collectIdsInRow = (row, acc) => {
|
|
|
344
391
|
};
|
|
345
392
|
const findDuplicateIds = (model) => {
|
|
346
393
|
const ids = [];
|
|
347
|
-
for (const float of model.floats
|
|
394
|
+
for (const float of model.floats) ids.push(float.id);
|
|
348
395
|
for (const root of collectRoots(model)) collectIdsInRow(root, ids);
|
|
349
396
|
const seen = /* @__PURE__ */ new Set();
|
|
350
397
|
const duplicates = /* @__PURE__ */ new Set();
|
|
@@ -377,7 +424,8 @@ const tabset = (children, options = {}) => {
|
|
|
377
424
|
...max === void 0 ? {} : { max: toDimension(max) },
|
|
378
425
|
...min === void 0 ? {} : { min: toDimension(min) },
|
|
379
426
|
id: options.id ?? createNodeId("tabset"),
|
|
380
|
-
selected: options.selected ?? 0
|
|
427
|
+
selected: clampSelected(children.length, options.selected ?? 0),
|
|
428
|
+
weight: options.weight ?? 1
|
|
381
429
|
};
|
|
382
430
|
};
|
|
383
431
|
const row = (children, options = {}) => {
|
|
@@ -389,7 +437,8 @@ const row = (children, options = {}) => {
|
|
|
389
437
|
...max === void 0 ? {} : { max: toDimension(max) },
|
|
390
438
|
...min === void 0 ? {} : { min: toDimension(min) },
|
|
391
439
|
id: options.id ?? createNodeId("row"),
|
|
392
|
-
orientation: options.orientation ?? "row"
|
|
440
|
+
orientation: options.orientation ?? "row",
|
|
441
|
+
weight: options.weight ?? 1
|
|
393
442
|
};
|
|
394
443
|
};
|
|
395
444
|
const floatNode = (layout, geometry, options = {}) => ({
|
|
@@ -404,6 +453,7 @@ const model = (layout, options = {}) => {
|
|
|
404
453
|
layout,
|
|
405
454
|
version: 1,
|
|
406
455
|
...options,
|
|
456
|
+
floats: options.floats ?? [],
|
|
407
457
|
global: options.global ?? {}
|
|
408
458
|
};
|
|
409
459
|
const duplicates = findDuplicateIds(built);
|
|
@@ -411,6 +461,12 @@ const model = (layout, options = {}) => {
|
|
|
411
461
|
return built;
|
|
412
462
|
};
|
|
413
463
|
|
|
464
|
+
//#endregion
|
|
465
|
+
//#region src/lib/assert-never.ts
|
|
466
|
+
const assertNever = (value) => {
|
|
467
|
+
throw new Error(`Unhandled variant: ${JSON.stringify(value)}`);
|
|
468
|
+
};
|
|
469
|
+
|
|
414
470
|
//#endregion
|
|
415
471
|
//#region src/geometry/geometry.ts
|
|
416
472
|
const DEFAULT_BAND_FRACTION = .22;
|
|
@@ -431,6 +487,23 @@ const closestEdge = (d) => {
|
|
|
431
487
|
if (min === d.top) return "top";
|
|
432
488
|
return "bottom";
|
|
433
489
|
};
|
|
490
|
+
const dockLocationFor = (target) => {
|
|
491
|
+
switch (target.kind) {
|
|
492
|
+
case "split": return `split-${target.edge}`;
|
|
493
|
+
case "tab": return "center";
|
|
494
|
+
default: return assertNever(target);
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
const splitEdge = (location) => {
|
|
498
|
+
switch (location) {
|
|
499
|
+
case "center": return;
|
|
500
|
+
case "split-bottom": return "bottom";
|
|
501
|
+
case "split-left": return "left";
|
|
502
|
+
case "split-right": return "right";
|
|
503
|
+
case "split-top": return "top";
|
|
504
|
+
default: return assertNever(location);
|
|
505
|
+
}
|
|
506
|
+
};
|
|
434
507
|
const resolveDockTarget = (pointer, rect, opts) => {
|
|
435
508
|
const band = opts?.bandFraction ?? DEFAULT_BAND_FRACTION;
|
|
436
509
|
if (rect.width <= 0 || rect.height <= 0) return { kind: "tab" };
|
|
@@ -564,7 +637,7 @@ const zoneRect = (rect, location) => {
|
|
|
564
637
|
y: rect.y
|
|
565
638
|
};
|
|
566
639
|
case "center": return rect;
|
|
567
|
-
default: return
|
|
640
|
+
default: return assertNever(location);
|
|
568
641
|
}
|
|
569
642
|
};
|
|
570
643
|
|
|
@@ -626,20 +699,21 @@ const snapSizes = (sizes, boundaryIndex, config) => {
|
|
|
626
699
|
snapped: true
|
|
627
700
|
};
|
|
628
701
|
};
|
|
702
|
+
const decideSnap = (sizes, boundaryIndex, config) => {
|
|
703
|
+
if (boundaryIndex === null || config === null || !snapEnabled(config)) return { kind: "inactive" };
|
|
704
|
+
const { sizes: snappedSizes, snapped } = snapSizes(sizes, boundaryIndex, config);
|
|
705
|
+
return snapped ? {
|
|
706
|
+
kind: "engage",
|
|
707
|
+
sizes: snappedSizes
|
|
708
|
+
} : { kind: "clear" };
|
|
709
|
+
};
|
|
710
|
+
const settleSnap = (sizes, boundaryIndex, config) => {
|
|
711
|
+
const decision = decideSnap(sizes, boundaryIndex, config);
|
|
712
|
+
return decision.kind === "engage" ? decision.sizes : sizes;
|
|
713
|
+
};
|
|
629
714
|
|
|
630
715
|
//#endregion
|
|
631
716
|
//#region src/model/invariants.ts
|
|
632
|
-
const clampSelected = (length, selected) => {
|
|
633
|
-
if (length === 0) return 0;
|
|
634
|
-
const base = Number.isFinite(selected) ? Math.trunc(selected) : 0;
|
|
635
|
-
if (base < 0) return 0;
|
|
636
|
-
if (base > length - 1) return length - 1;
|
|
637
|
-
return base;
|
|
638
|
-
};
|
|
639
|
-
const normalizeTabset = (tabset) => ({
|
|
640
|
-
...tabset,
|
|
641
|
-
selected: clampSelected(tabset.children.length, tabset.selected)
|
|
642
|
-
});
|
|
643
717
|
/**
|
|
644
718
|
* A row that collapses to a single child hands its slot in the parent to
|
|
645
719
|
* that child, so the slot's sizing constraints have to travel with it.
|
|
@@ -651,7 +725,7 @@ const normalizeTabset = (tabset) => ({
|
|
|
651
725
|
const inheritSlot = (row, survivor) => {
|
|
652
726
|
const sized = {
|
|
653
727
|
...survivor,
|
|
654
|
-
|
|
728
|
+
weight: row.weight,
|
|
655
729
|
...row.min === void 0 ? {} : { min: row.min },
|
|
656
730
|
...row.max === void 0 ? {} : { max: row.max }
|
|
657
731
|
};
|
|
@@ -665,7 +739,7 @@ const normalizeRowChildren = (children) => {
|
|
|
665
739
|
const out = [];
|
|
666
740
|
for (const child of children) {
|
|
667
741
|
if (child.type === "tabset") {
|
|
668
|
-
if (child.children.length > 0) out.push(
|
|
742
|
+
if (child.children.length > 0) out.push(child);
|
|
669
743
|
continue;
|
|
670
744
|
}
|
|
671
745
|
const grandchildren = normalizeRowChildren(child.children);
|
|
@@ -700,22 +774,22 @@ const normalizeLayout = (root) => {
|
|
|
700
774
|
};
|
|
701
775
|
const normalize = (model) => {
|
|
702
776
|
const layout = normalizeLayout(model.layout);
|
|
703
|
-
const
|
|
704
|
-
for (const float of model.floats
|
|
777
|
+
const floats = [];
|
|
778
|
+
for (const float of model.floats) {
|
|
705
779
|
const floatLayout = normalizeLayout(float.layout);
|
|
706
|
-
if (rowContainsTabset(floatLayout))
|
|
780
|
+
if (rowContainsTabset(floatLayout)) floats.push({
|
|
707
781
|
...float,
|
|
708
782
|
layout: floatLayout
|
|
709
783
|
});
|
|
710
784
|
}
|
|
711
|
-
const floats = healedFloats.length > 0 ? healedFloats : void 0;
|
|
712
785
|
const withRoots = {
|
|
713
786
|
...model,
|
|
714
787
|
floats,
|
|
715
788
|
layout
|
|
716
789
|
};
|
|
717
|
-
const
|
|
718
|
-
const
|
|
790
|
+
const tabsets = collectTabsets(withRoots);
|
|
791
|
+
const tabsetIds = new Set(tabsets.map((tabset) => tabset.id));
|
|
792
|
+
const firstTabsetId = tabsets[0]?.id;
|
|
719
793
|
const activeTabsetId = model.activeTabsetId !== void 0 && tabsetIds.has(model.activeTabsetId) ? model.activeTabsetId : firstTabsetId;
|
|
720
794
|
const maximizedTabsetId = model.maximizedTabsetId !== void 0 && tabsetIds.has(model.maximizedTabsetId) ? model.maximizedTabsetId : void 0;
|
|
721
795
|
return {
|
|
@@ -734,30 +808,33 @@ const removeTabsetReturning = (row, tabsetId) => {
|
|
|
734
808
|
return removed?.type === "tabset" ? removed : void 0;
|
|
735
809
|
};
|
|
736
810
|
const removeTabset = (row, tabsetId) => removeTabsetReturning(row, tabsetId) !== void 0;
|
|
737
|
-
const
|
|
738
|
-
const
|
|
811
|
+
const splitPlacement = (location) => {
|
|
812
|
+
const edge = splitEdge(location);
|
|
813
|
+
return {
|
|
814
|
+
before: edge === "left" || edge === "top",
|
|
815
|
+
orientation: edge === "left" || edge === "right" ? "row" : "column"
|
|
816
|
+
};
|
|
817
|
+
};
|
|
739
818
|
const placeBesideTarget = (draft, placed, targetId, location) => {
|
|
740
819
|
const targetTabset = findTabset(draft, targetId);
|
|
741
820
|
const found = findTabsetParent(findRootContaining(draft, targetId) ?? draft.layout, targetId);
|
|
742
821
|
if (!targetTabset || !found) return;
|
|
743
|
-
const orientation =
|
|
744
|
-
const before = splitsBefore(location);
|
|
822
|
+
const { before, orientation } = splitPlacement(location);
|
|
745
823
|
if (found.parent.orientation === orientation) {
|
|
746
|
-
const
|
|
747
|
-
targetTabset.weight =
|
|
748
|
-
placed.weight =
|
|
824
|
+
const half = targetTabset.weight / 2;
|
|
825
|
+
targetTabset.weight = half;
|
|
826
|
+
placed.weight = half;
|
|
749
827
|
found.parent.children.splice(before ? found.index : found.index + 1, 0, placed);
|
|
750
828
|
} else {
|
|
751
|
-
const targetWeight = targetTabset.weight;
|
|
752
|
-
targetTabset.weight = 50;
|
|
753
|
-
placed.weight = 50;
|
|
754
829
|
const newRow = {
|
|
755
830
|
children: before ? [placed, targetTabset] : [targetTabset, placed],
|
|
756
831
|
id: createNodeId("row"),
|
|
757
832
|
orientation,
|
|
758
|
-
type: "row"
|
|
833
|
+
type: "row",
|
|
834
|
+
weight: targetTabset.weight
|
|
759
835
|
};
|
|
760
|
-
|
|
836
|
+
targetTabset.weight = 50;
|
|
837
|
+
placed.weight = 50;
|
|
761
838
|
found.parent.children.splice(found.index, 1, newRow);
|
|
762
839
|
}
|
|
763
840
|
draft.activeTabsetId = placed.id;
|
|
@@ -767,7 +844,7 @@ const insertTab = (draft, tab, target) => {
|
|
|
767
844
|
const targetTabset = findTabset(draft, targetId);
|
|
768
845
|
if (!targetTabset) return;
|
|
769
846
|
if (location === "center") {
|
|
770
|
-
const at = index ?? targetTabset.children.length;
|
|
847
|
+
const at = Math.min(Math.max(index ?? targetTabset.children.length, 0), targetTabset.children.length);
|
|
771
848
|
targetTabset.children.splice(at, 0, tab);
|
|
772
849
|
targetTabset.selected = at;
|
|
773
850
|
return;
|
|
@@ -777,10 +854,20 @@ const insertTab = (draft, tab, target) => {
|
|
|
777
854
|
id: createNodeId("tabset"),
|
|
778
855
|
selected: 0,
|
|
779
856
|
type: "tabset",
|
|
780
|
-
weight:
|
|
857
|
+
weight: 1
|
|
781
858
|
};
|
|
782
859
|
placeBesideTarget(draft, newTabset, targetId, location);
|
|
783
860
|
};
|
|
861
|
+
/**
|
|
862
|
+
* Removing a tab can leave `selected` past the end of the strip, so every
|
|
863
|
+
* detach re-clamps its container rather than leaving the whole tree to be
|
|
864
|
+
* re-walked afterwards.
|
|
865
|
+
*/
|
|
866
|
+
const detachTab = (container, index) => {
|
|
867
|
+
const removed = container.children.splice(index, 1).at(0);
|
|
868
|
+
container.selected = clampSelected(container.children.length, container.selected);
|
|
869
|
+
return removed;
|
|
870
|
+
};
|
|
784
871
|
const mergeTabsInto = (target, tabs, selectedOffset) => {
|
|
785
872
|
const mergeStart = target.children.length;
|
|
786
873
|
target.children.push(...tabs);
|
|
@@ -791,17 +878,20 @@ const wrapTabInLayout = (tab) => ({
|
|
|
791
878
|
children: [tab],
|
|
792
879
|
id: createNodeId("tabset"),
|
|
793
880
|
selected: 0,
|
|
794
|
-
type: "tabset"
|
|
881
|
+
type: "tabset",
|
|
882
|
+
weight: 1
|
|
795
883
|
}],
|
|
796
884
|
id: createNodeId("row"),
|
|
797
885
|
orientation: "row",
|
|
798
|
-
type: "row"
|
|
886
|
+
type: "row",
|
|
887
|
+
weight: 1
|
|
799
888
|
});
|
|
800
889
|
const wrapTabsetInLayout = (tabset) => ({
|
|
801
890
|
children: [tabset],
|
|
802
891
|
id: createNodeId("row"),
|
|
803
892
|
orientation: "row",
|
|
804
|
-
type: "row"
|
|
893
|
+
type: "row",
|
|
894
|
+
weight: 1
|
|
805
895
|
});
|
|
806
896
|
|
|
807
897
|
//#endregion
|
|
@@ -824,10 +914,10 @@ const pushFloat = (draft, layout, geometry, floatId) => {
|
|
|
824
914
|
geometry: geometry ?? DEFAULT_FLOAT_GEOMETRY,
|
|
825
915
|
id: floatId ?? createNodeId("float"),
|
|
826
916
|
layout,
|
|
827
|
-
name: uniqueFloatName("Panel", draft.floats
|
|
917
|
+
name: uniqueFloatName("Panel", draft.floats),
|
|
828
918
|
type: "float"
|
|
829
919
|
};
|
|
830
|
-
draft.floats = [...draft.floats
|
|
920
|
+
draft.floats = [...draft.floats, float];
|
|
831
921
|
const tabsets = [];
|
|
832
922
|
collectTabsetsInRow(layout, tabsets);
|
|
833
923
|
const first = tabsets.at(0);
|
|
@@ -853,11 +943,10 @@ const resolveMainTarget = (draft, targetId) => {
|
|
|
853
943
|
return mainTabsets[0];
|
|
854
944
|
};
|
|
855
945
|
const dockFloat = (draft, floatId, targetId, location) => {
|
|
856
|
-
const floats = draft.floats
|
|
946
|
+
const floats = draft.floats;
|
|
857
947
|
const index = floats.findIndex((float) => float.id === floatId);
|
|
858
948
|
if (index === -1) return;
|
|
859
949
|
const float = floats.splice(index, 1).at(0);
|
|
860
|
-
draft.floats = floats;
|
|
861
950
|
if (!float) return;
|
|
862
951
|
const tabsets = [];
|
|
863
952
|
collectTabsetsInRow(float.layout, tabsets);
|
|
@@ -877,20 +966,30 @@ const dockFloat = (draft, floatId, targetId, location) => {
|
|
|
877
966
|
return;
|
|
878
967
|
}
|
|
879
968
|
const where = location ?? "split-right";
|
|
880
|
-
|
|
969
|
+
placeBesideTarget(draft, tabsets.length === 1 && leadTabset ? leadTabset : {
|
|
881
970
|
children: tabs,
|
|
882
971
|
id: createNodeId("tabset"),
|
|
883
972
|
selected: selectedOffset,
|
|
884
|
-
type: "tabset"
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
placeBesideTarget(draft, placed, target.id, where);
|
|
973
|
+
type: "tabset",
|
|
974
|
+
weight: 1
|
|
975
|
+
}, target.id, where);
|
|
888
976
|
};
|
|
889
977
|
|
|
890
978
|
//#endregion
|
|
891
979
|
//#region src/state/reducer.ts
|
|
892
|
-
const
|
|
893
|
-
|
|
980
|
+
const parseNodeAttrs = (node, attrs) => {
|
|
981
|
+
switch (node.type) {
|
|
982
|
+
case "row": return mutableRowAttrsSchema.parse(attrs);
|
|
983
|
+
case "tab": return mutableTabAttrsSchema.parse(attrs);
|
|
984
|
+
case "tabset": {
|
|
985
|
+
const parsed = mutableTabsetAttrsSchema.parse(attrs);
|
|
986
|
+
return parsed.selected === void 0 ? parsed : {
|
|
987
|
+
...parsed,
|
|
988
|
+
selected: clampSelected(node.children.length, parsed.selected)
|
|
989
|
+
};
|
|
990
|
+
}
|
|
991
|
+
default: return assertNever(node);
|
|
992
|
+
}
|
|
894
993
|
};
|
|
895
994
|
const applyAction = (draft, action) => {
|
|
896
995
|
switch (action.type) {
|
|
@@ -914,7 +1013,7 @@ const applyAction = (draft, action) => {
|
|
|
914
1013
|
}
|
|
915
1014
|
case "deleteTab": {
|
|
916
1015
|
const location = findTab(draft, action.tabId);
|
|
917
|
-
if (location) location.container
|
|
1016
|
+
if (location) detachTab(location.container, location.index);
|
|
918
1017
|
return;
|
|
919
1018
|
}
|
|
920
1019
|
case "deleteTabset": {
|
|
@@ -928,7 +1027,7 @@ const applyAction = (draft, action) => {
|
|
|
928
1027
|
case "floatTab": {
|
|
929
1028
|
const location = findTab(draft, action.tabId);
|
|
930
1029
|
if (!location) return;
|
|
931
|
-
const removed = location.container
|
|
1030
|
+
const removed = detachTab(location.container, location.index);
|
|
932
1031
|
if (removed) pushFloat(draft, wrapTabInLayout(removed), action.geometry, action.floatId);
|
|
933
1032
|
return;
|
|
934
1033
|
}
|
|
@@ -943,7 +1042,7 @@ const applyAction = (draft, action) => {
|
|
|
943
1042
|
case "moveNode": {
|
|
944
1043
|
const source = findTab(draft, action.sourceId);
|
|
945
1044
|
if (!source) return;
|
|
946
|
-
const removed = source.container
|
|
1045
|
+
const removed = detachTab(source.container, source.index);
|
|
947
1046
|
if (!removed) return;
|
|
948
1047
|
insertTab(draft, removed, {
|
|
949
1048
|
id: action.targetId,
|
|
@@ -965,17 +1064,15 @@ const applyAction = (draft, action) => {
|
|
|
965
1064
|
}
|
|
966
1065
|
return;
|
|
967
1066
|
}
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
if (detached) placeBesideTarget(draft, detached, action.targetId, action.location);
|
|
972
|
-
}
|
|
1067
|
+
const sourceRoot = findRootContaining(draft, action.sourceId);
|
|
1068
|
+
const detached = sourceRoot ? removeTabsetReturning(sourceRoot, action.sourceId) : void 0;
|
|
1069
|
+
if (detached) placeBesideTarget(draft, detached, action.targetId, action.location);
|
|
973
1070
|
return;
|
|
974
1071
|
}
|
|
975
1072
|
case "renameFloat": {
|
|
976
1073
|
const float = findFloat(draft, action.floatId);
|
|
977
1074
|
if (float) {
|
|
978
|
-
const others =
|
|
1075
|
+
const others = draft.floats.filter((other) => other.id !== action.floatId);
|
|
979
1076
|
float.name = uniqueFloatName(action.name, others);
|
|
980
1077
|
}
|
|
981
1078
|
return;
|
|
@@ -987,7 +1084,7 @@ const applyAction = (draft, action) => {
|
|
|
987
1084
|
}
|
|
988
1085
|
case "selectTab": {
|
|
989
1086
|
const tabset = findTabset(draft, action.tabsetId);
|
|
990
|
-
if (tabset) tabset.selected = action.index;
|
|
1087
|
+
if (tabset) tabset.selected = clampSelected(tabset.children.length, action.index);
|
|
991
1088
|
return;
|
|
992
1089
|
}
|
|
993
1090
|
case "setActiveTabset":
|
|
@@ -1006,7 +1103,7 @@ const applyAction = (draft, action) => {
|
|
|
1006
1103
|
return;
|
|
1007
1104
|
case "updateNodeAttributes": {
|
|
1008
1105
|
const node = findAttributedNode(draft, action.nodeId);
|
|
1009
|
-
if (node) Object.assign(node, action.attrs);
|
|
1106
|
+
if (node) Object.assign(node, parseNodeAttrs(node, action.attrs));
|
|
1010
1107
|
return;
|
|
1011
1108
|
}
|
|
1012
1109
|
default: assertNever(action);
|
|
@@ -1164,12 +1261,13 @@ const stackModel = (model, orientation = "column") => {
|
|
|
1164
1261
|
children,
|
|
1165
1262
|
id: model.layout.id,
|
|
1166
1263
|
orientation,
|
|
1167
|
-
type: "row"
|
|
1264
|
+
type: "row",
|
|
1265
|
+
weight: model.layout.weight
|
|
1168
1266
|
},
|
|
1169
1267
|
maximizedTabsetId: void 0
|
|
1170
1268
|
});
|
|
1171
1269
|
};
|
|
1172
1270
|
|
|
1173
1271
|
//#endregion
|
|
1174
|
-
export { DEFAULT_SNAP_THRESHOLD, HISTORY_LIMIT, actionSchema, canRedo, canUndo, collectRoots, collectTabsets, collectTabsetsInRow, createHistory, createNodeId, createTabId, dashfooMachine, dashfooSchema, dimensionSchema, dispatch, dockLocationSchema, dockZonePolygons, dragDockMachine, edgeSchema, findAttributedNode, findDuplicateIds, findFloat, findRootContaining, findRow, findTab, findTabset, findTabsetParent, floatNode, floatNodeSchema, fromJSON, geometrySchema, getFirstTabset, globalAttributesSchema, jsonValueSchema, model, mutableNodeAttrsSchema, normalize, orientationSchema, parseModel, redo, reducer, resolveDockTarget, resolveSnapTargets, row, rowNodeSchema, snapEnabled, snapSchema, snapSizes, stackModel, tab, tabNodeSchema, tabset, tabsetNodeSchema, toJSON, undo, unitSchema, zoneRect };
|
|
1272
|
+
export { DEFAULT_SNAP_THRESHOLD, DEFAULT_WEIGHT, HISTORY_LIMIT, actionSchema, canRedo, canUndo, collectRoots, collectTabsets, collectTabsetsInRow, createHistory, createNodeId, createTabId, dashfooMachine, dashfooSchema, decideSnap, dimensionSchema, dispatch, dockLocationFor, dockLocationSchema, dockZonePolygons, dragDockMachine, edgeSchema, findAttributedNode, findDuplicateIds, findFloat, findRootContaining, findRow, findTab, findTabset, findTabsetParent, floatNode, floatNodeSchema, fromJSON, geometrySchema, getFirstTabset, globalAttributesSchema, jsonValueSchema, model, mutableNodeAttrsSchema, normalize, orientationSchema, parseModel, redo, reducer, resolveDockTarget, resolveSnapTargets, row, rowNodeSchema, settleSnap, snapEnabled, snapSchema, snapSizes, splitEdge, stackModel, tab, tabNodeSchema, tabset, tabsetNodeObjectSchema, tabsetNodeSchema, toJSON, undo, unitSchema, zoneRect };
|
|
1175
1273
|
//# sourceMappingURL=index.js.map
|