@window-splitter/state 0.3.2 → 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/src/index.ts CHANGED
@@ -65,6 +65,13 @@ export interface Constraints<T extends ParsedUnit | Unit = ParsedUnit> {
65
65
  defaultCollapsed?: boolean;
66
66
  /** The size of the panel once collapsed */
67
67
  collapsedSize?: T;
68
+ /**
69
+ * By default the layout will be stored in percentage values while at rest.
70
+ * This makes scaling the layout easier when the container is resized.
71
+ * However you might have a panel you want to stay at a static size when
72
+ * the container is resized.
73
+ */
74
+ isStaticAtRest?: boolean;
68
75
  }
69
76
 
70
77
  interface Order {
@@ -228,6 +235,7 @@ interface SetSizeEvent {
228
235
  /** Set the size of the whole group */
229
236
  type: "setSize";
230
237
  size: Rect;
238
+ handleOverflow?: boolean;
231
239
  }
232
240
 
233
241
  interface SetActualItemsSizeEvent {
@@ -406,23 +414,24 @@ type OnResizeSize = {
406
414
 
407
415
  export type OnResizeCallback = (size: OnResizeSize) => void;
408
416
 
409
- interface InitializePanelOptions {
417
+ type InitializePanelOptions = {
410
418
  min?: Unit;
411
419
  max?: Unit;
412
420
  default?: Unit;
413
- collapsible?: boolean;
414
- collapsed?: boolean;
415
421
  collapsedSize?: Unit;
416
- onCollapseChange?: {
417
- current: ((isCollapsed: boolean) => void) | null | undefined;
418
- };
419
- onResize?: {
420
- current: OnResizeCallback | null | undefined;
421
- };
422
- collapseAnimation?: PanelData["collapseAnimation"];
423
- defaultCollapsed?: boolean;
424
422
  id?: string;
425
- }
423
+ } & Partial<
424
+ Pick<
425
+ PanelData,
426
+ | "isStaticAtRest"
427
+ | "collapseAnimation"
428
+ | "defaultCollapsed"
429
+ | "onResize"
430
+ | "collapsible"
431
+ | "collapsed"
432
+ | "onCollapseChange"
433
+ >
434
+ >;
426
435
 
427
436
  type InitializePanelOptionsWithId = InitializePanelOptions & { id: string };
428
437
 
@@ -468,6 +477,7 @@ export function initializePanel(
468
477
  id: item.id,
469
478
  collapseAnimation: item.collapseAnimation,
470
479
  default: item.default ? parseUnit(item.default) : undefined,
480
+ isStaticAtRest: item.isStaticAtRest,
471
481
  } satisfies Omit<PanelData, "id" | "currentValue"> & { id?: string };
472
482
 
473
483
  return { ...data, currentValue: makePixelUnit(-1) } satisfies Omit<
@@ -686,10 +696,7 @@ function panelHasSpace(
686
696
  );
687
697
  }
688
698
 
689
- return (
690
- item.currentValue.value.gt(getUnitPixelValue(context, item.min)) &&
691
- item.currentValue.value.lte(getUnitPixelValue(context, item.max))
692
- );
699
+ return item.currentValue.value.gt(getUnitPixelValue(context, item.min));
693
700
  }
694
701
 
695
702
  /** Search in a `direction` for a panel that still has space to expand. */
@@ -726,11 +733,9 @@ function getStaticWidth(context: GroupMachineContextValue) {
726
733
  for (const item of context.items) {
727
734
  if (isPanelHandle(item)) {
728
735
  width = width.add(item.size.value);
729
- } else if (
730
- isPanelData(item) &&
731
- item.collapsed &&
732
- item.currentValue.type === "pixel"
733
- ) {
736
+ } else if (item.collapsed && item.currentValue.type === "pixel") {
737
+ width = width.add(item.currentValue.value);
738
+ } else if (item.isStaticAtRest) {
734
739
  width = width.add(item.currentValue.value);
735
740
  }
736
741
  }
@@ -805,6 +810,11 @@ export function buildTemplate(context: GroupMachineContextValue) {
805
810
  item.currentValue.type === "pixel" &&
806
811
  item.currentValue.value.toNumber() !== -1
807
812
  ) {
813
+ if (item.isStaticAtRest) {
814
+ const max = item.max === "1fr" ? "100%" : formatUnit(item.max);
815
+ return `clamp(${min}, ${formatUnit(item.currentValue)}, ${max})`;
816
+ }
817
+
808
818
  return formatUnit(item.currentValue);
809
819
  } else if (item.currentValue.type === "percent") {
810
820
  const max = item.max === "1fr" ? "100%" : formatUnit(item.max);
@@ -919,18 +929,18 @@ function createUnrestrainedPanel(_: GroupMachineContextValue, data: PanelData) {
919
929
  */
920
930
 
921
931
  /** Converts the items to pixels */
922
- export function prepareItems(context: GroupMachineContextValue) {
932
+ export function prepareItems(context: GroupMachineContextValue): Item[] {
923
933
  const staticWidth = getStaticWidth(context);
924
934
  const newItems = [];
925
935
 
926
936
  for (const item of context.items) {
927
937
  if (!item || !isPanelData(item)) {
928
- newItems.push(item);
938
+ newItems.push({ ...item });
929
939
  continue;
930
940
  }
931
941
 
932
942
  if (item.currentValue.type === "pixel") {
933
- newItems.push(item);
943
+ newItems.push({ ...item });
934
944
  continue;
935
945
  }
936
946
 
@@ -1031,6 +1041,10 @@ function updateLayout(
1031
1041
 
1032
1042
  const isInDragBugger =
1033
1043
  newDragOvershoot.abs().lt(COLLAPSE_THRESHOLD) &&
1044
+ // Let the panel expand at it's min size
1045
+ !panelAfter.currentValue.value
1046
+ .add(newDragOvershoot.abs())
1047
+ .gte(panelAfter.min.value) &&
1034
1048
  panelAfter.collapsible &&
1035
1049
  panelAfter.collapsed &&
1036
1050
  (isInLeftOvershoot || isInRightOvershoot);
@@ -1048,9 +1062,11 @@ function updateLayout(
1048
1062
 
1049
1063
  // Don't let the panel collapse until the threshold is reached
1050
1064
  if (
1065
+ !dragEvent.disregardCollapseBuffer &&
1051
1066
  panelBefore.collapsible &&
1052
- panelBefore.currentValue.value ===
1067
+ panelBefore.currentValue.value.eq(
1053
1068
  getUnitPixelValue(context, panelBefore.min)
1069
+ )
1054
1070
  ) {
1055
1071
  const potentialNewValue = panelBefore.currentValue.value.sub(
1056
1072
  newDragOvershoot.abs()
@@ -1116,8 +1132,6 @@ function updateLayout(
1116
1132
  .add(Math.abs(moveAmount))
1117
1133
  );
1118
1134
 
1119
- panelAfter.collapsed = false;
1120
-
1121
1135
  if (extra.gt(0)) {
1122
1136
  panelAfterNewValue = panelAfterNewValue.add(extra);
1123
1137
  }
@@ -1131,6 +1145,13 @@ function updateLayout(
1131
1145
  .minus(Math.abs(moveAmount))
1132
1146
  );
1133
1147
 
1148
+ if (panelBeforeNewValue.lt(panelBefore.min.value)) {
1149
+ // TODO this should probably distribute the space between the panels?
1150
+ return { dragOvershoot: newDragOvershoot };
1151
+ }
1152
+
1153
+ panelAfter.collapsed = false;
1154
+
1134
1155
  if (
1135
1156
  panelAfter.onCollapseChange?.current &&
1136
1157
  !panelAfter.collapseIsControlled &&
@@ -1140,9 +1161,9 @@ function updateLayout(
1140
1161
  }
1141
1162
  }
1142
1163
 
1143
- const panelBeforeIsAboutToCollapse =
1144
- panelBefore.currentValue.value ===
1145
- getUnitPixelValue(context, panelBefore.min);
1164
+ const panelBeforeIsAboutToCollapse = panelBefore.currentValue.value.eq(
1165
+ getUnitPixelValue(context, panelBefore.min)
1166
+ );
1146
1167
 
1147
1168
  // If the panel was expanded and now is at it's min size, collapse it
1148
1169
  if (
@@ -1187,8 +1208,11 @@ function updateLayout(
1187
1208
  );
1188
1209
 
1189
1210
  if (!leftoverSpace.eq(0)) {
1190
- panelBefore.currentValue.value =
1191
- panelBefore.currentValue.value.add(leftoverSpace);
1211
+ panelBefore.currentValue.value = clampUnit(
1212
+ context,
1213
+ panelBefore,
1214
+ panelBefore.currentValue.value.add(leftoverSpace)
1215
+ );
1192
1216
  }
1193
1217
 
1194
1218
  return { items: newItems };
@@ -1215,7 +1239,7 @@ function commitLayout(context: GroupMachineContextValue) {
1215
1239
  const staticWidth = getStaticWidth({ ...context, items: newItems });
1216
1240
 
1217
1241
  newItems.forEach((item, index) => {
1218
- if (item.type !== "panel" || item.collapsed) {
1242
+ if (item.type !== "panel" || item.collapsed || item.isStaticAtRest) {
1219
1243
  return;
1220
1244
  }
1221
1245
 
@@ -1334,6 +1358,81 @@ function applyDeltaInBothDirections(
1334
1358
  }
1335
1359
  }
1336
1360
 
1361
+ /**
1362
+ * A layout might overflow at small screen sizes.
1363
+ * This function tries to fix that by:
1364
+ *
1365
+ * 1. It will try to collapse a panel if it can.
1366
+ */
1367
+ function handleOverflow(context: GroupMachineContextValue) {
1368
+ // If we haven't measured yet we can't do anything
1369
+ if (
1370
+ context.items.some((i) => isPanelData(i) && i.currentValue.value.eq(-1))
1371
+ ) {
1372
+ return context;
1373
+ }
1374
+
1375
+ const groupSize = new Big(getGroupSize(context));
1376
+ const nonStaticWidth = groupSize.sub(getStaticWidth(context).toNumber());
1377
+ const pixelItems = context.items.map((i) => {
1378
+ if (isPanelHandle(i)) return i.size.value;
1379
+ if (i.collapsed) return getUnitPixelValue(context, i.currentValue);
1380
+
1381
+ const pixel =
1382
+ (i.currentValue.type === "pixel" && i.currentValue.value) ||
1383
+ i.currentValue.value.mul(nonStaticWidth);
1384
+
1385
+ return clampUnit(context, i, pixel);
1386
+ });
1387
+ const totalSize = pixelItems.reduce((acc, i) => acc.add(i), new Big(0));
1388
+ const overflow = totalSize.abs().sub(groupSize);
1389
+
1390
+ if (overflow.eq(0) || groupSize.eq(0)) {
1391
+ return context;
1392
+ }
1393
+
1394
+ let newContext = { ...context, items: prepareItems(context) };
1395
+
1396
+ const collapsiblePanel = newContext.items.find((i): i is PanelData =>
1397
+ Boolean(isPanelData(i) && i.collapsible)
1398
+ );
1399
+
1400
+ if (collapsiblePanel) {
1401
+ const collapsiblePanelIndex = newContext.items.findIndex(
1402
+ (i) => i.id === collapsiblePanel.id
1403
+ );
1404
+ const handleId = getHandleForPanelId(newContext, collapsiblePanel.id);
1405
+ const sizeChange = collapsiblePanel.currentValue.value.sub(
1406
+ getUnitPixelValue(newContext, collapsiblePanel.collapsedSize)
1407
+ );
1408
+
1409
+ // Try to collapse the panel
1410
+ newContext = {
1411
+ ...newContext,
1412
+ ...iterativelyUpdateLayout({
1413
+ handleId: handleId.item.id,
1414
+ delta: sizeChange,
1415
+ direction: (handleId.direction * -1) as -1 | 1,
1416
+ context: {
1417
+ ...newContext,
1418
+ // act like its the old size so the space is distributed correctly
1419
+ size: { width: totalSize.toNumber(), height: totalSize.toNumber() },
1420
+ },
1421
+ }),
1422
+ };
1423
+
1424
+ // Then remove all the overflow
1425
+ applyDeltaInBothDirections(
1426
+ newContext,
1427
+ newContext.items,
1428
+ collapsiblePanelIndex,
1429
+ overflow.neg()
1430
+ );
1431
+ }
1432
+
1433
+ return { ...newContext, items: commitLayout(newContext) };
1434
+ }
1435
+
1337
1436
  // #endregion
1338
1437
 
1339
1438
  // #region Machine
@@ -1349,6 +1448,22 @@ interface AnimationActorOutput {
1349
1448
  action: "expand" | "collapse";
1350
1449
  }
1351
1450
 
1451
+ function getDeltaForEvent(
1452
+ context: GroupMachineContextValue,
1453
+ event: CollapsePanelEvent | ExpandPanelEvent
1454
+ ) {
1455
+ const panel = getPanelWithId(context, event.panelId);
1456
+
1457
+ if (event.type === "expandPanel") {
1458
+ return new Big(
1459
+ panel.sizeBeforeCollapse ?? getUnitPixelValue(context, panel.min)
1460
+ ).minus(panel.currentValue.value);
1461
+ }
1462
+
1463
+ const collapsedSize = getUnitPixelValue(context, panel.collapsedSize);
1464
+ return panel.currentValue.value.minus(collapsedSize);
1465
+ }
1466
+
1352
1467
  const animationActor = fromPromise<
1353
1468
  AnimationActorOutput | undefined,
1354
1469
  AnimationActorInput
@@ -1359,18 +1474,11 @@ const animationActor = fromPromise<
1359
1474
  const handle = getHandleForPanelId(context, event.panelId);
1360
1475
 
1361
1476
  let direction = new Big(handle.direction);
1362
- let fullDelta = new Big(0);
1363
-
1364
- if (event.type === "expandPanel") {
1365
- fullDelta = new Big(
1366
- panel.sizeBeforeCollapse ?? getUnitPixelValue(context, panel.min)
1367
- ).minus(panel.currentValue.value);
1368
- } else {
1369
- const collapsedSize = getUnitPixelValue(context, panel.collapsedSize);
1477
+ const fullDelta = getDeltaForEvent(context, event);
1370
1478
 
1479
+ if (event.type === "collapsePanel") {
1371
1480
  panel.sizeBeforeCollapse = panel.currentValue.value.toNumber();
1372
1481
  direction = direction.mul(new Big(-1));
1373
- fullDelta = panel.currentValue.value.minus(collapsedSize);
1374
1482
  }
1375
1483
 
1376
1484
  const fps = 60;
@@ -1457,6 +1565,8 @@ export const groupMachine = createMachine(
1457
1565
  { target: "togglingCollapse" },
1458
1566
  ],
1459
1567
  expandPanel: [
1568
+ // This will match if we can't expand and the expansion won't happen
1569
+ { guard: "cannotExpandPanel" },
1460
1570
  {
1461
1571
  actions: "notifyCollapseToggle",
1462
1572
  guard: "shouldNotifyCollapseToggle",
@@ -1530,6 +1640,38 @@ export const groupMachine = createMachine(
1530
1640
  const panel = getPanelWithId(context, event.panelId);
1531
1641
  return panel.collapseIsControlled === true;
1532
1642
  },
1643
+ cannotExpandPanel: ({ context, event }) => {
1644
+ isEvent(event, ["expandPanel"]);
1645
+ const delta = getDeltaForEvent(context, event);
1646
+ const handle = getHandleForPanelId(context, event.panelId);
1647
+ const pixelItems = prepareItems(context);
1648
+
1649
+ let interimContext = { ...context, items: pixelItems };
1650
+ interimContext = {
1651
+ ...interimContext,
1652
+ ...iterativelyUpdateLayout({
1653
+ context: interimContext,
1654
+ handleId: handle.item.id,
1655
+ controlled: event.controlled,
1656
+ delta: delta,
1657
+ direction: handle.direction,
1658
+ }),
1659
+ };
1660
+ const updatedPanel = interimContext.items.find(
1661
+ (i) => i.id === event.panelId
1662
+ );
1663
+ const totalSize = interimContext.items.reduce(
1664
+ (acc, i) =>
1665
+ acc.add(isPanelData(i) ? i.currentValue.value : i.size.value),
1666
+ new Big(0)
1667
+ );
1668
+ const didExpand =
1669
+ updatedPanel &&
1670
+ isPanelData(updatedPanel) &&
1671
+ !updatedPanel.currentValue.value.eq(updatedPanel.collapsedSize.value);
1672
+
1673
+ return totalSize.gt(getGroupSize(context)) || !didExpand;
1674
+ },
1533
1675
  },
1534
1676
  actors: {
1535
1677
  animation: animationActor,
@@ -1575,11 +1717,14 @@ export const groupMachine = createMachine(
1575
1717
  return context.items;
1576
1718
  },
1577
1719
  }),
1578
- updateSize: assign({
1579
- size: ({ event }) => {
1580
- isEvent(event, ["setSize"]);
1581
- return event.size;
1582
- },
1720
+ updateSize: enqueueActions(({ context, event, enqueue }) => {
1721
+ isEvent(event, ["setSize"]);
1722
+
1723
+ if (event.handleOverflow) {
1724
+ enqueue.assign(handleOverflow({ ...context, size: event.size }));
1725
+ } else {
1726
+ enqueue.assign({ size: event.size });
1727
+ }
1583
1728
  }),
1584
1729
  recordActualItemSize: assign({
1585
1730
  items: ({ context, event }) => {
@@ -1763,10 +1908,14 @@ export const groupMachine = createMachine(
1763
1908
  item,
1764
1909
  getUnitPixelValue(context, item.currentValue)
1765
1910
  );
1911
+ const groupSize = getGroupSize(context);
1766
1912
 
1767
1913
  item.onResize?.current?.({
1768
1914
  pixel: pixel.toNumber(),
1769
- percentage: pixel.div(getGroupSize(context)).toNumber(),
1915
+ percentage:
1916
+ groupSize > 0
1917
+ ? pixel.div(getGroupSize(context)).toNumber()
1918
+ : -1,
1770
1919
  });
1771
1920
  }
1772
1921
  }
@@ -1301,6 +1301,189 @@ describe("collapsible panel", () => {
1301
1301
  expect(getTemplate(actor)).toMatchInlineSnapshot(`"245px 10px 245px"`);
1302
1302
  });
1303
1303
  });
1304
+
1305
+ describe("constraints", () => {
1306
+ test("on render", async () => {
1307
+ const actor = createActor(groupMachine, {
1308
+ input: { groupId: "group" },
1309
+ }).start();
1310
+
1311
+ sendAll(actor, [
1312
+ {
1313
+ type: "registerPanel",
1314
+ data: initializePanel({ id: "panel-1", min: "200px" }),
1315
+ },
1316
+ {
1317
+ type: "registerPanelHandle",
1318
+ data: { id: "resizer-1", size: "10px" },
1319
+ },
1320
+ {
1321
+ type: "registerPanel",
1322
+ data: initializePanel({
1323
+ id: "panel-2",
1324
+ collapsible: true,
1325
+ min: "300px",
1326
+ collapsedSize: "60px",
1327
+ }),
1328
+ },
1329
+ ]);
1330
+
1331
+ expect(getTemplate(actor)).toBe(
1332
+ "minmax(200px, 1fr) 10px minmax(300px, 1fr)"
1333
+ );
1334
+ initializeSizes(actor, { width: 400, height: 200 });
1335
+ actor.send({
1336
+ type: "setSize",
1337
+ size: { width: 400, height: 200 },
1338
+ handleOverflow: true,
1339
+ });
1340
+ // There wasn't enough space to render the panel so the last one is collapsed
1341
+ expect(getTemplate(actor)).toBe(
1342
+ "minmax(200px, min(calc(1 * (100% - 70px)), 100%)) 10px 60px"
1343
+ );
1344
+ });
1345
+
1346
+ test("on resize", async () => {
1347
+ const actor = createActor(groupMachine, {
1348
+ input: { groupId: "group" },
1349
+ }).start();
1350
+
1351
+ sendAll(actor, [
1352
+ {
1353
+ type: "registerPanel",
1354
+ data: initializePanel({ id: "panel-1", min: "200px" }),
1355
+ },
1356
+ {
1357
+ type: "registerPanelHandle",
1358
+ data: { id: "resizer-1", size: "10px" },
1359
+ },
1360
+ {
1361
+ type: "registerPanel",
1362
+ data: initializePanel({
1363
+ id: "panel-2",
1364
+ collapsible: true,
1365
+ min: "300px",
1366
+ collapsedSize: "60px",
1367
+ }),
1368
+ },
1369
+ ]);
1370
+
1371
+ expect(getTemplate(actor)).toBe(
1372
+ "minmax(200px, 1fr) 10px minmax(300px, 1fr)"
1373
+ );
1374
+ initializeSizes(actor, { width: 500, height: 200 });
1375
+ expect(getTemplate(actor)).toBe(
1376
+ "minmax(200px, min(calc(0.40816326530612244898 * (100% - 10px)), 100%)) 10px minmax(300px, min(calc(0.61224489795918367347 * (100% - 10px)), 100%))"
1377
+ );
1378
+
1379
+ actor.send({
1380
+ type: "setSize",
1381
+ size: { width: 400, height: 200 },
1382
+ handleOverflow: true,
1383
+ });
1384
+ // There wasn't enough space to render the panel so the last one is collapsed
1385
+ expect(getTemplate(actor)).toBe(
1386
+ "minmax(200px, min(calc(1 * (100% - 70px)), 100%)) 10px 60px"
1387
+ );
1388
+ });
1389
+
1390
+ test("cannot expand panel via event", async () => {
1391
+ const actor = createActor(groupMachine, {
1392
+ input: { groupId: "group" },
1393
+ }).start();
1394
+
1395
+ sendAll(actor, [
1396
+ {
1397
+ type: "registerPanel",
1398
+ data: initializePanel({ id: "panel-1", min: "200px" }),
1399
+ },
1400
+ {
1401
+ type: "registerPanelHandle",
1402
+ data: { id: "resizer-1", size: "10px" },
1403
+ },
1404
+ {
1405
+ type: "registerPanel",
1406
+ data: initializePanel({
1407
+ id: "panel-2",
1408
+ collapsible: true,
1409
+ min: "300px",
1410
+ collapsedSize: "60px",
1411
+ }),
1412
+ },
1413
+ ]);
1414
+
1415
+ expect(getTemplate(actor)).toBe(
1416
+ "minmax(200px, 1fr) 10px minmax(300px, 1fr)"
1417
+ );
1418
+ initializeSizes(actor, { width: 400, height: 200 });
1419
+ actor.send({
1420
+ type: "setSize",
1421
+ size: { width: 400, height: 200 },
1422
+ handleOverflow: true,
1423
+ });
1424
+ // There wasn't enough space to render the panel so the last one is collapsed
1425
+ expect(getTemplate(actor)).toBe(
1426
+ "minmax(200px, min(calc(1 * (100% - 70px)), 100%)) 10px 60px"
1427
+ );
1428
+
1429
+ actor.send({
1430
+ type: "expandPanel",
1431
+ panelId: "panel-2",
1432
+ });
1433
+ await waitForIdle(actor);
1434
+
1435
+ // There wasn't enough space to render the panel so the last one is collapsed
1436
+ expect(getTemplate(actor)).toBe(
1437
+ "minmax(200px, min(calc(1 * (100% - 70px)), 100%)) 10px 60px"
1438
+ );
1439
+ });
1440
+
1441
+ test("cannot expand panel via drag", async () => {
1442
+ const actor = createActor(groupMachine, {
1443
+ input: { groupId: "group" },
1444
+ }).start();
1445
+
1446
+ sendAll(actor, [
1447
+ {
1448
+ type: "registerPanel",
1449
+ data: initializePanel({ id: "panel-1", min: "200px" }),
1450
+ },
1451
+ {
1452
+ type: "registerPanelHandle",
1453
+ data: { id: "resizer-1", size: "10px" },
1454
+ },
1455
+ {
1456
+ type: "registerPanel",
1457
+ data: initializePanel({
1458
+ id: "panel-2",
1459
+ collapsible: true,
1460
+ min: "300px",
1461
+ collapsedSize: "60px",
1462
+ }),
1463
+ },
1464
+ ]);
1465
+
1466
+ expect(getTemplate(actor)).toBe(
1467
+ "minmax(200px, 1fr) 10px minmax(300px, 1fr)"
1468
+ );
1469
+ initializeSizes(actor, { width: 400, height: 200 });
1470
+ actor.send({
1471
+ type: "setSize",
1472
+ size: { width: 400, height: 200 },
1473
+ handleOverflow: true,
1474
+ });
1475
+ // There wasn't enough space to render the panel so the last one is collapsed
1476
+ expect(getTemplate(actor)).toBe(
1477
+ "minmax(200px, min(calc(1 * (100% - 70px)), 100%)) 10px 60px"
1478
+ );
1479
+
1480
+ // Drag the resizer to the right
1481
+ capturePixelValues(actor, () => {
1482
+ dragHandle(actor, { id: "resizer-1", delta: -400 });
1483
+ expect(getTemplate(actor)).toBe("330px 10px 60px");
1484
+ });
1485
+ });
1486
+ });
1304
1487
  });
1305
1488
 
1306
1489
  describe("conditional panel", () => {
@@ -1749,3 +1932,39 @@ describe("utils", async () => {
1749
1932
  expect(newSnapshot).toMatchSnapshot();
1750
1933
  });
1751
1934
  });
1935
+
1936
+ describe("static at rest", () => {
1937
+ test("a panel can be pixels when resting", async () => {
1938
+ const actor = createActor(groupMachine, {
1939
+ input: {
1940
+ groupId: "group",
1941
+ initialItems: [
1942
+ initializePanel({
1943
+ id: "panel-1",
1944
+ min: "20px",
1945
+ max: "200px",
1946
+ isStaticAtRest: true,
1947
+ }),
1948
+ initializePanelHandleData({ id: "resizer-1", size: "10px" }),
1949
+ initializePanel({ id: "panel-2", min: "50px" }),
1950
+ initializePanelHandleData({ id: "resizer-2", size: "10px" }),
1951
+ initializePanel({ id: "panel-3", min: "50px" }),
1952
+ ],
1953
+ },
1954
+ }).start();
1955
+
1956
+ initializeSizes(actor, { width: 500, height: 200 });
1957
+
1958
+ expect(getTemplate(actor)).toMatchInlineSnapshot(
1959
+ `"clamp(20px, 200px, 200px) 10px minmax(50px, min(calc(0.5 * (100% - 220px)), 100%)) 10px minmax(50px, min(calc(0.5 * (100% - 220px)), 100%))"`
1960
+ );
1961
+
1962
+ capturePixelValues(actor, () => {
1963
+ dragHandle(actor, { id: "resizer-1", delta: -10 });
1964
+ });
1965
+
1966
+ expect(getTemplate(actor)).toMatchInlineSnapshot(
1967
+ `"clamp(20px, 190px, 200px) 10px minmax(50px, min(calc(0.51724137931034482759 * (100% - 210px)), 100%)) 10px minmax(50px, min(calc(0.48275862068965517241 * (100% - 210px)), 100%))"`
1968
+ );
1969
+ });
1970
+ });