@window-splitter/state 0.3.1 → 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/.turbo/turbo-build.log +4 -5
- package/.turbo/turbo-lint.log +1 -1
- package/CHANGELOG.md +44 -0
- package/coverage/coverage-final.json +1 -1
- package/coverage/coverage-summary.json +2 -2
- package/coverage/index.html +21 -21
- package/coverage/index.ts.html +1367 -893
- package/dist/commonjs/index.d.ts +11 -13
- package/dist/commonjs/index.d.ts.map +1 -1
- package/dist/commonjs/index.js +149 -37
- package/dist/commonjs/index.js.map +1 -1
- package/dist/esm/index.d.ts +11 -13
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +149 -37
- package/dist/esm/index.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +229 -60
- package/src/machine.test.ts +249 -2
package/dist/esm/index.js
CHANGED
|
@@ -138,6 +138,7 @@ export function initializePanel(item) {
|
|
|
138
138
|
id: item.id,
|
|
139
139
|
collapseAnimation: item.collapseAnimation,
|
|
140
140
|
default: item.default ? parseUnit(item.default) : undefined,
|
|
141
|
+
isStaticAtRest: item.isStaticAtRest,
|
|
141
142
|
};
|
|
142
143
|
return { ...data, currentValue: makePixelUnit(-1) };
|
|
143
144
|
}
|
|
@@ -159,7 +160,7 @@ export function parseUnit(unit) {
|
|
|
159
160
|
return makePixelUnit(parseFloat(unit));
|
|
160
161
|
}
|
|
161
162
|
if (unit.endsWith("%")) {
|
|
162
|
-
return makePercentUnit(parseFloat(unit));
|
|
163
|
+
return makePercentUnit(parseFloat(unit) / 100);
|
|
163
164
|
}
|
|
164
165
|
throw new Error(`Invalid unit: ${unit}`);
|
|
165
166
|
}
|
|
@@ -180,7 +181,7 @@ function getUnitPixelValue(context, unit) {
|
|
|
180
181
|
const parsed = unit === "1fr" ? parseUnit(unit) : unit;
|
|
181
182
|
return parsed.type === "pixel"
|
|
182
183
|
? parsed.value
|
|
183
|
-
:
|
|
184
|
+
: parsed.value.mul(getGroupSize(context));
|
|
184
185
|
}
|
|
185
186
|
/** Clamp a new `currentValue` given the panel's constraints. */
|
|
186
187
|
function clampUnit(context, item, value) {
|
|
@@ -280,8 +281,7 @@ function panelHasSpace(context, item, adjustment) {
|
|
|
280
281
|
return (item.currentValue.value.gte(getUnitPixelValue(context, item.min)) &&
|
|
281
282
|
item.currentValue.value.lt(getUnitPixelValue(context, item.max)));
|
|
282
283
|
}
|
|
283
|
-
return
|
|
284
|
-
item.currentValue.value.lte(getUnitPixelValue(context, item.max)));
|
|
284
|
+
return item.currentValue.value.gt(getUnitPixelValue(context, item.min));
|
|
285
285
|
}
|
|
286
286
|
/** Search in a `direction` for a panel that still has space to expand. */
|
|
287
287
|
function findPanelWithSpace(context, items, start, direction, adjustment, disregardCollapseBuffer) {
|
|
@@ -305,9 +305,10 @@ function getStaticWidth(context) {
|
|
|
305
305
|
if (isPanelHandle(item)) {
|
|
306
306
|
width = width.add(item.size.value);
|
|
307
307
|
}
|
|
308
|
-
else if (
|
|
309
|
-
item.
|
|
310
|
-
|
|
308
|
+
else if (item.collapsed && item.currentValue.type === "pixel") {
|
|
309
|
+
width = width.add(item.currentValue.value);
|
|
310
|
+
}
|
|
311
|
+
else if (item.isStaticAtRest) {
|
|
311
312
|
width = width.add(item.currentValue.value);
|
|
312
313
|
}
|
|
313
314
|
}
|
|
@@ -317,7 +318,7 @@ function formatUnit(unit) {
|
|
|
317
318
|
if (unit.type === "pixel") {
|
|
318
319
|
return `${unit.value.toNumber()}px`;
|
|
319
320
|
}
|
|
320
|
-
return `${unit.value.toNumber()}%`;
|
|
321
|
+
return `${unit.value.mul(100).toNumber()}%`;
|
|
321
322
|
}
|
|
322
323
|
export function getPanelGroupPixelSizes(context) {
|
|
323
324
|
return prepareItems(context).map((i) => isPanelData(i)
|
|
@@ -354,6 +355,10 @@ export function buildTemplate(context) {
|
|
|
354
355
|
const min = formatUnit(item.min);
|
|
355
356
|
if (item.currentValue.type === "pixel" &&
|
|
356
357
|
item.currentValue.value.toNumber() !== -1) {
|
|
358
|
+
if (item.isStaticAtRest) {
|
|
359
|
+
const max = item.max === "1fr" ? "100%" : formatUnit(item.max);
|
|
360
|
+
return `clamp(${min}, ${formatUnit(item.currentValue)}, ${max})`;
|
|
361
|
+
}
|
|
357
362
|
return formatUnit(item.currentValue);
|
|
358
363
|
}
|
|
359
364
|
else if (item.currentValue.type === "percent") {
|
|
@@ -364,10 +369,24 @@ export function buildTemplate(context) {
|
|
|
364
369
|
return formatUnit(item.collapsedSize);
|
|
365
370
|
}
|
|
366
371
|
else if (item.default) {
|
|
367
|
-
|
|
372
|
+
const siblingHasFill = context.items.some((i) => isPanelData(i) &&
|
|
373
|
+
i.id !== item.id &&
|
|
374
|
+
!i.collapsed &&
|
|
375
|
+
(i.max === "1fr" ||
|
|
376
|
+
(i.max.type === "percent" && i.max.value.eq(100))));
|
|
377
|
+
// If a sibling has a fill, this item doesn't need to expand
|
|
378
|
+
// So we can just use the default value
|
|
379
|
+
if (siblingHasFill) {
|
|
380
|
+
return formatUnit(item.default);
|
|
381
|
+
}
|
|
382
|
+
// Use 1fr so that panel fills ths space if needed
|
|
383
|
+
const max = item.max === "1fr" ? "1fr" : formatUnit(item.max);
|
|
384
|
+
return `minmax(${formatUnit(item.default)}, ${max})`;
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
const max = item.max === "1fr" ? "1fr" : formatUnit(item.max);
|
|
388
|
+
return `minmax(${min}, ${max})`;
|
|
368
389
|
}
|
|
369
|
-
const max = item.max === "1fr" ? "1fr" : formatUnit(item.max);
|
|
370
|
-
return `minmax(${min}, ${max})`;
|
|
371
390
|
}
|
|
372
391
|
return formatUnit(item.size);
|
|
373
392
|
})
|
|
@@ -445,11 +464,11 @@ export function prepareItems(context) {
|
|
|
445
464
|
const newItems = [];
|
|
446
465
|
for (const item of context.items) {
|
|
447
466
|
if (!item || !isPanelData(item)) {
|
|
448
|
-
newItems.push(item);
|
|
467
|
+
newItems.push({ ...item });
|
|
449
468
|
continue;
|
|
450
469
|
}
|
|
451
470
|
if (item.currentValue.type === "pixel") {
|
|
452
|
-
newItems.push(item);
|
|
471
|
+
newItems.push({ ...item });
|
|
453
472
|
continue;
|
|
454
473
|
}
|
|
455
474
|
const pixel = new Big(getGroupSize(context))
|
|
@@ -503,6 +522,10 @@ function updateLayout(context, dragEvent) {
|
|
|
503
522
|
const potentialNewValue = panelAfter.currentValue.value.add(new Big(newDragOvershoot).mul(isInRightBuffer ? moveDirection : 1));
|
|
504
523
|
const min = getUnitPixelValue(context, panelAfter.min);
|
|
505
524
|
const isInDragBugger = newDragOvershoot.abs().lt(COLLAPSE_THRESHOLD) &&
|
|
525
|
+
// Let the panel expand at it's min size
|
|
526
|
+
!panelAfter.currentValue.value
|
|
527
|
+
.add(newDragOvershoot.abs())
|
|
528
|
+
.gte(panelAfter.min.value) &&
|
|
506
529
|
panelAfter.collapsible &&
|
|
507
530
|
panelAfter.collapsed &&
|
|
508
531
|
(isInLeftOvershoot || isInRightOvershoot);
|
|
@@ -515,9 +538,9 @@ function updateLayout(context, dragEvent) {
|
|
|
515
538
|
}
|
|
516
539
|
}
|
|
517
540
|
// Don't let the panel collapse until the threshold is reached
|
|
518
|
-
if (
|
|
519
|
-
panelBefore.
|
|
520
|
-
|
|
541
|
+
if (!dragEvent.disregardCollapseBuffer &&
|
|
542
|
+
panelBefore.collapsible &&
|
|
543
|
+
panelBefore.currentValue.value.eq(getUnitPixelValue(context, panelBefore.min))) {
|
|
521
544
|
const potentialNewValue = panelBefore.currentValue.value.sub(newDragOvershoot.abs());
|
|
522
545
|
if (newDragOvershoot.abs().lt(COLLAPSE_THRESHOLD) &&
|
|
523
546
|
potentialNewValue.gt(getUnitPixelValue(context, panelBefore.collapsedSize))) {
|
|
@@ -564,7 +587,6 @@ function updateLayout(context, dragEvent) {
|
|
|
564
587
|
.sub(panelAfterNewValue
|
|
565
588
|
// Then re-add the move amount
|
|
566
589
|
.add(Math.abs(moveAmount)));
|
|
567
|
-
panelAfter.collapsed = false;
|
|
568
590
|
if (extra.gt(0)) {
|
|
569
591
|
panelAfterNewValue = panelAfterNewValue.add(extra);
|
|
570
592
|
}
|
|
@@ -574,14 +596,18 @@ function updateLayout(context, dragEvent) {
|
|
|
574
596
|
.minus(panelAfterPreviousValue)
|
|
575
597
|
// And then re-apply the movement value
|
|
576
598
|
.minus(Math.abs(moveAmount)));
|
|
599
|
+
if (panelBeforeNewValue.lt(panelBefore.min.value)) {
|
|
600
|
+
// TODO this should probably distribute the space between the panels?
|
|
601
|
+
return { dragOvershoot: newDragOvershoot };
|
|
602
|
+
}
|
|
603
|
+
panelAfter.collapsed = false;
|
|
577
604
|
if (panelAfter.onCollapseChange?.current &&
|
|
578
605
|
!panelAfter.collapseIsControlled &&
|
|
579
606
|
!dragEvent.controlled) {
|
|
580
607
|
panelAfter.onCollapseChange.current(false);
|
|
581
608
|
}
|
|
582
609
|
}
|
|
583
|
-
const panelBeforeIsAboutToCollapse = panelBefore.currentValue.value
|
|
584
|
-
getUnitPixelValue(context, panelBefore.min);
|
|
610
|
+
const panelBeforeIsAboutToCollapse = panelBefore.currentValue.value.eq(getUnitPixelValue(context, panelBefore.min));
|
|
585
611
|
// If the panel was expanded and now is at it's min size, collapse it
|
|
586
612
|
if (!dragEvent.disregardCollapseBuffer &&
|
|
587
613
|
panelBefore.collapsible &&
|
|
@@ -607,8 +633,7 @@ function updateLayout(context, dragEvent) {
|
|
|
607
633
|
panelAfter.currentValue = { type: "pixel", value: panelAfterNewValue };
|
|
608
634
|
const leftoverSpace = new Big(getGroupSize(context)).minus(newItems.reduce((acc, b) => acc.add(isPanelData(b) ? b.currentValue.value : b.size.value), new Big(0)));
|
|
609
635
|
if (!leftoverSpace.eq(0)) {
|
|
610
|
-
panelBefore.currentValue.value =
|
|
611
|
-
panelBefore.currentValue.value.add(leftoverSpace);
|
|
636
|
+
panelBefore.currentValue.value = clampUnit(context, panelBefore, panelBefore.currentValue.value.add(leftoverSpace));
|
|
612
637
|
}
|
|
613
638
|
return { items: newItems };
|
|
614
639
|
}
|
|
@@ -629,7 +654,7 @@ function commitLayout(context) {
|
|
|
629
654
|
});
|
|
630
655
|
const staticWidth = getStaticWidth({ ...context, items: newItems });
|
|
631
656
|
newItems.forEach((item, index) => {
|
|
632
|
-
if (item.type !== "panel" || item.collapsed) {
|
|
657
|
+
if (item.type !== "panel" || item.collapsed || item.isStaticAtRest) {
|
|
633
658
|
return;
|
|
634
659
|
}
|
|
635
660
|
newItems[index] = {
|
|
@@ -699,19 +724,74 @@ function applyDeltaInBothDirections(context, newItems, itemIndex, delta) {
|
|
|
699
724
|
direction = direction === 1 ? -1 : 1;
|
|
700
725
|
}
|
|
701
726
|
}
|
|
727
|
+
/**
|
|
728
|
+
* A layout might overflow at small screen sizes.
|
|
729
|
+
* This function tries to fix that by:
|
|
730
|
+
*
|
|
731
|
+
* 1. It will try to collapse a panel if it can.
|
|
732
|
+
*/
|
|
733
|
+
function handleOverflow(context) {
|
|
734
|
+
// If we haven't measured yet we can't do anything
|
|
735
|
+
if (context.items.some((i) => isPanelData(i) && i.currentValue.value.eq(-1))) {
|
|
736
|
+
return context;
|
|
737
|
+
}
|
|
738
|
+
const groupSize = new Big(getGroupSize(context));
|
|
739
|
+
const nonStaticWidth = groupSize.sub(getStaticWidth(context).toNumber());
|
|
740
|
+
const pixelItems = context.items.map((i) => {
|
|
741
|
+
if (isPanelHandle(i))
|
|
742
|
+
return i.size.value;
|
|
743
|
+
if (i.collapsed)
|
|
744
|
+
return getUnitPixelValue(context, i.currentValue);
|
|
745
|
+
const pixel = (i.currentValue.type === "pixel" && i.currentValue.value) ||
|
|
746
|
+
i.currentValue.value.mul(nonStaticWidth);
|
|
747
|
+
return clampUnit(context, i, pixel);
|
|
748
|
+
});
|
|
749
|
+
const totalSize = pixelItems.reduce((acc, i) => acc.add(i), new Big(0));
|
|
750
|
+
const overflow = totalSize.abs().sub(groupSize);
|
|
751
|
+
if (overflow.eq(0) || groupSize.eq(0)) {
|
|
752
|
+
return context;
|
|
753
|
+
}
|
|
754
|
+
let newContext = { ...context, items: prepareItems(context) };
|
|
755
|
+
const collapsiblePanel = newContext.items.find((i) => Boolean(isPanelData(i) && i.collapsible));
|
|
756
|
+
if (collapsiblePanel) {
|
|
757
|
+
const collapsiblePanelIndex = newContext.items.findIndex((i) => i.id === collapsiblePanel.id);
|
|
758
|
+
const handleId = getHandleForPanelId(newContext, collapsiblePanel.id);
|
|
759
|
+
const sizeChange = collapsiblePanel.currentValue.value.sub(getUnitPixelValue(newContext, collapsiblePanel.collapsedSize));
|
|
760
|
+
// Try to collapse the panel
|
|
761
|
+
newContext = {
|
|
762
|
+
...newContext,
|
|
763
|
+
...iterativelyUpdateLayout({
|
|
764
|
+
handleId: handleId.item.id,
|
|
765
|
+
delta: sizeChange,
|
|
766
|
+
direction: (handleId.direction * -1),
|
|
767
|
+
context: {
|
|
768
|
+
...newContext,
|
|
769
|
+
// act like its the old size so the space is distributed correctly
|
|
770
|
+
size: { width: totalSize.toNumber(), height: totalSize.toNumber() },
|
|
771
|
+
},
|
|
772
|
+
}),
|
|
773
|
+
};
|
|
774
|
+
// Then remove all the overflow
|
|
775
|
+
applyDeltaInBothDirections(newContext, newContext.items, collapsiblePanelIndex, overflow.neg());
|
|
776
|
+
}
|
|
777
|
+
return { ...newContext, items: commitLayout(newContext) };
|
|
778
|
+
}
|
|
779
|
+
function getDeltaForEvent(context, event) {
|
|
780
|
+
const panel = getPanelWithId(context, event.panelId);
|
|
781
|
+
if (event.type === "expandPanel") {
|
|
782
|
+
return new Big(panel.sizeBeforeCollapse ?? getUnitPixelValue(context, panel.min)).minus(panel.currentValue.value);
|
|
783
|
+
}
|
|
784
|
+
const collapsedSize = getUnitPixelValue(context, panel.collapsedSize);
|
|
785
|
+
return panel.currentValue.value.minus(collapsedSize);
|
|
786
|
+
}
|
|
702
787
|
const animationActor = fromPromise(({ input: { send, context, event } }) => new Promise((resolve) => {
|
|
703
788
|
const panel = getPanelWithId(context, event.panelId);
|
|
704
789
|
const handle = getHandleForPanelId(context, event.panelId);
|
|
705
790
|
let direction = new Big(handle.direction);
|
|
706
|
-
|
|
707
|
-
if (event.type === "
|
|
708
|
-
fullDelta = new Big(panel.sizeBeforeCollapse ?? getUnitPixelValue(context, panel.min)).minus(panel.currentValue.value);
|
|
709
|
-
}
|
|
710
|
-
else {
|
|
711
|
-
const collapsedSize = getUnitPixelValue(context, panel.collapsedSize);
|
|
791
|
+
const fullDelta = getDeltaForEvent(context, event);
|
|
792
|
+
if (event.type === "collapsePanel") {
|
|
712
793
|
panel.sizeBeforeCollapse = panel.currentValue.value.toNumber();
|
|
713
794
|
direction = direction.mul(new Big(-1));
|
|
714
|
-
fullDelta = panel.currentValue.value.minus(collapsedSize);
|
|
715
795
|
}
|
|
716
796
|
const fps = 60;
|
|
717
797
|
const { duration, ease } = getCollapseAnimation(panel);
|
|
@@ -759,7 +839,7 @@ export const groupMachine = createMachine({
|
|
|
759
839
|
states: {
|
|
760
840
|
idle: {
|
|
761
841
|
on: {
|
|
762
|
-
setActualItemsSize: { actions: ["recordActualItemSize"] },
|
|
842
|
+
setActualItemsSize: { actions: ["recordActualItemSize", "onResize"] },
|
|
763
843
|
dragHandleStart: { target: "dragging" },
|
|
764
844
|
setPanelPixelSize: {
|
|
765
845
|
actions: [
|
|
@@ -778,6 +858,8 @@ export const groupMachine = createMachine({
|
|
|
778
858
|
{ target: "togglingCollapse" },
|
|
779
859
|
],
|
|
780
860
|
expandPanel: [
|
|
861
|
+
// This will match if we can't expand and the expansion won't happen
|
|
862
|
+
{ guard: "cannotExpandPanel" },
|
|
781
863
|
{
|
|
782
864
|
actions: "notifyCollapseToggle",
|
|
783
865
|
guard: "shouldNotifyCollapseToggle",
|
|
@@ -850,6 +932,29 @@ export const groupMachine = createMachine({
|
|
|
850
932
|
const panel = getPanelWithId(context, event.panelId);
|
|
851
933
|
return panel.collapseIsControlled === true;
|
|
852
934
|
},
|
|
935
|
+
cannotExpandPanel: ({ context, event }) => {
|
|
936
|
+
isEvent(event, ["expandPanel"]);
|
|
937
|
+
const delta = getDeltaForEvent(context, event);
|
|
938
|
+
const handle = getHandleForPanelId(context, event.panelId);
|
|
939
|
+
const pixelItems = prepareItems(context);
|
|
940
|
+
let interimContext = { ...context, items: pixelItems };
|
|
941
|
+
interimContext = {
|
|
942
|
+
...interimContext,
|
|
943
|
+
...iterativelyUpdateLayout({
|
|
944
|
+
context: interimContext,
|
|
945
|
+
handleId: handle.item.id,
|
|
946
|
+
controlled: event.controlled,
|
|
947
|
+
delta: delta,
|
|
948
|
+
direction: handle.direction,
|
|
949
|
+
}),
|
|
950
|
+
};
|
|
951
|
+
const updatedPanel = interimContext.items.find((i) => i.id === event.panelId);
|
|
952
|
+
const totalSize = interimContext.items.reduce((acc, i) => acc.add(isPanelData(i) ? i.currentValue.value : i.size.value), new Big(0));
|
|
953
|
+
const didExpand = updatedPanel &&
|
|
954
|
+
isPanelData(updatedPanel) &&
|
|
955
|
+
!updatedPanel.currentValue.value.eq(updatedPanel.collapsedSize.value);
|
|
956
|
+
return totalSize.gt(getGroupSize(context)) || !didExpand;
|
|
957
|
+
},
|
|
853
958
|
},
|
|
854
959
|
actors: {
|
|
855
960
|
animation: animationActor,
|
|
@@ -889,11 +994,14 @@ export const groupMachine = createMachine({
|
|
|
889
994
|
return context.items;
|
|
890
995
|
},
|
|
891
996
|
}),
|
|
892
|
-
updateSize:
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
}
|
|
997
|
+
updateSize: enqueueActions(({ context, event, enqueue }) => {
|
|
998
|
+
isEvent(event, ["setSize"]);
|
|
999
|
+
if (event.handleOverflow) {
|
|
1000
|
+
enqueue.assign(handleOverflow({ ...context, size: event.size }));
|
|
1001
|
+
}
|
|
1002
|
+
else {
|
|
1003
|
+
enqueue.assign({ size: event.size });
|
|
1004
|
+
}
|
|
897
1005
|
}),
|
|
898
1006
|
recordActualItemSize: assign({
|
|
899
1007
|
items: ({ context, event }) => {
|
|
@@ -1034,9 +1142,13 @@ export const groupMachine = createMachine({
|
|
|
1034
1142
|
onResize: ({ context }) => {
|
|
1035
1143
|
for (const item of context.items) {
|
|
1036
1144
|
if (isPanelData(item)) {
|
|
1145
|
+
const pixel = clampUnit(context, item, getUnitPixelValue(context, item.currentValue));
|
|
1146
|
+
const groupSize = getGroupSize(context);
|
|
1037
1147
|
item.onResize?.current?.({
|
|
1038
|
-
pixel:
|
|
1039
|
-
percentage:
|
|
1148
|
+
pixel: pixel.toNumber(),
|
|
1149
|
+
percentage: groupSize > 0
|
|
1150
|
+
? pixel.div(getGroupSize(context)).toNumber()
|
|
1151
|
+
: -1,
|
|
1040
1152
|
});
|
|
1041
1153
|
}
|
|
1042
1154
|
}
|