@window-splitter/state 1.0.2 → 1.1.1--canary.9d2f98a.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
@@ -257,6 +257,7 @@ interface ApplyDeltaEvent {
257
257
  type: "applyDelta";
258
258
  delta: number;
259
259
  handleId: string;
260
+ panelId: string;
260
261
  }
261
262
 
262
263
  interface SetOrientationEvent {
@@ -278,6 +279,7 @@ interface CollapsePanelEvent extends ControlledCollapseToggle {
278
279
  type: "collapsePanel";
279
280
  /** The panel to collapse */
280
281
  panelId: string;
282
+ resolve?: () => void;
281
283
  }
282
284
 
283
285
  interface ExpandPanelEvent extends ControlledCollapseToggle {
@@ -285,6 +287,7 @@ interface ExpandPanelEvent extends ControlledCollapseToggle {
285
287
  type: "expandPanel";
286
288
  /** The panel to expand */
287
289
  panelId: string;
290
+ resolve?: () => void;
288
291
  }
289
292
 
290
293
  interface SetPanelPixelSizeEvent {
@@ -317,6 +320,11 @@ export interface GroupMachineContextValue {
317
320
  * How to save the persisted state
318
321
  */
319
322
  autosaveStrategy?: "localStorage" | "cookie";
323
+ /**
324
+ * The amount to move the drag handle when shift is held down.
325
+ * @default 15px
326
+ */
327
+ shiftAmount?: number;
320
328
  }
321
329
 
322
330
  interface LockGroupEvent {
@@ -735,6 +743,25 @@ function getHandleForPanelId(
735
743
  ) {
736
744
  const panelIndex = context.items.findIndex((item) => item.id === panelId);
737
745
 
746
+ // When in controlled collapse mode we defer to the controlled state to know if we need to update.
747
+ // The confirmation comes along as an expand/collapse event.
748
+ // The default behavior is to find the closes handle and use that to collapse, if we're dragging some other
749
+ // handle though we want to use that instead. This will results in a smoother visual experience.
750
+ if (context.activeDragHandleId) {
751
+ const handleIndex = getPanelHandleIndex(
752
+ context,
753
+ context.activeDragHandleId
754
+ );
755
+ const item = context.items[handleIndex];
756
+
757
+ if (item && isPanelHandle(item)) {
758
+ return {
759
+ item,
760
+ direction: handleIndex > panelIndex ? (1 as const) : (-1 as const),
761
+ };
762
+ }
763
+ }
764
+
738
765
  invariant(panelIndex !== -1, `Expected panel before: ${panelId}`);
739
766
 
740
767
  let item = context.items[panelIndex + 1];
@@ -752,6 +779,37 @@ function getHandleForPanelId(
752
779
  throw new Error(`Cant find handle for panel: ${panelId}`);
753
780
  }
754
781
 
782
+ function getHandleForPanelIdWithAvailableSpace(
783
+ context: GroupMachineContextValue,
784
+ panelId: string
785
+ ) {
786
+ const panelIndex = context.items.findIndex((item) => item.id === panelId);
787
+
788
+ invariant(panelIndex !== -1, `Expected panel before: ${panelId}`);
789
+
790
+ let item = context.items[panelIndex + 1];
791
+
792
+ if (
793
+ item &&
794
+ isPanelHandle(item) &&
795
+ findPanelWithSpace(context, context.items, panelIndex + 1, 1, "add")
796
+ ) {
797
+ return { item, direction: 1 as const };
798
+ }
799
+
800
+ item = context.items[panelIndex - 1];
801
+
802
+ if (
803
+ item &&
804
+ isPanelHandle(item) &&
805
+ findPanelWithSpace(context, context.items, panelIndex - 1, -1, "add")
806
+ ) {
807
+ return { item, direction: -1 as const };
808
+ }
809
+
810
+ return getHandleForPanelId(context, panelId);
811
+ }
812
+
755
813
  /** Given the specified order props and default order of the items, order the items */
756
814
  function sortWithOrder(items: Array<Item>) {
757
815
  const defaultPlacement: Record<string, number> = {};
@@ -799,8 +857,8 @@ function panelHasSpace(
799
857
  `panelHasSpace only works with number values: ${item.id} ${item.currentValue}`
800
858
  );
801
859
 
802
- if (item.collapsible && !item.collapsed) {
803
- return true;
860
+ if (item.collapsible) {
861
+ return !item.collapsed;
804
862
  }
805
863
 
806
864
  if (adjustment === "add") {
@@ -1098,6 +1156,7 @@ function updateLayout(
1098
1156
  | (DragHandleEvent & {
1099
1157
  controlled?: boolean;
1100
1158
  disregardCollapseBuffer?: never;
1159
+ panelId?: string;
1101
1160
  isVirtual?: boolean;
1102
1161
  })
1103
1162
  | {
@@ -1105,6 +1164,7 @@ function updateLayout(
1105
1164
  value: MoveMoveEvent;
1106
1165
  handleId: string;
1107
1166
  controlled?: boolean;
1167
+ panelId: string;
1108
1168
  disregardCollapseBuffer?: boolean;
1109
1169
  isVirtual?: boolean;
1110
1170
  }
@@ -1119,7 +1179,7 @@ function updateLayout(
1119
1179
  : dragEvent.value.deltaY;
1120
1180
 
1121
1181
  if (dragEvent.value.shiftKey) {
1122
- moveAmount *= 15;
1182
+ moveAmount *= context.shiftAmount ?? 15;
1123
1183
  }
1124
1184
 
1125
1185
  if (moveAmount === 0) {
@@ -1147,7 +1207,26 @@ function updateLayout(
1147
1207
 
1148
1208
  invariant(isPanelData(panelBefore), `Expected panel before: ${handle.id}`);
1149
1209
 
1150
- const panelAfter = newItems[handleIndex - moveDirection];
1210
+ /** The panel that gets bigger */
1211
+ const panelAfter =
1212
+ dragEvent.type === "collapsePanel"
1213
+ ? // If we have the panel id just use that
1214
+ newItems.find(
1215
+ (item) =>
1216
+ item.id === dragEvent.panelId &&
1217
+ panelBefore.id !== dragEvent.panelId
1218
+ ) ||
1219
+ // otherwise fallback to the next panel with space
1220
+ findPanelWithSpace(
1221
+ context,
1222
+ newItems,
1223
+ handleIndex - moveDirection,
1224
+ moveDirection * -1,
1225
+ "add",
1226
+ dragEvent.disregardCollapseBuffer
1227
+ ) ||
1228
+ newItems[handleIndex - moveDirection]
1229
+ : newItems[handleIndex - moveDirection];
1151
1230
 
1152
1231
  invariant(
1153
1232
  panelAfter && isPanelData(panelAfter),
@@ -1165,7 +1244,7 @@ function updateLayout(
1165
1244
  const newDragOvershoot = context.dragOvershoot.add(moveAmount);
1166
1245
 
1167
1246
  // Don't let the panel expand until the threshold is reached
1168
- if (!dragEvent.disregardCollapseBuffer) {
1247
+ if (!dragEvent.disregardCollapseBuffer && !dragEvent.controlled) {
1169
1248
  const isInLeftBuffer = newDragOvershoot.lt(0) && moveDirection > 0;
1170
1249
  const isInLeftOvershoot = newDragOvershoot.gt(0) && moveDirection > 0;
1171
1250
  const isInRightBuffer = newDragOvershoot.gt(0) && moveDirection < 0;
@@ -1199,6 +1278,7 @@ function updateLayout(
1199
1278
  // Don't let the panel collapse until the threshold is reached
1200
1279
  if (
1201
1280
  !dragEvent.disregardCollapseBuffer &&
1281
+ !dragEvent.controlled &&
1202
1282
  panelBefore.collapsible &&
1203
1283
  panelBefore.currentValue.value.eq(
1204
1284
  getUnitPixelValue(context, panelBefore.min)
@@ -1282,7 +1362,10 @@ function updateLayout(
1282
1362
  .minus(Math.abs(moveAmount))
1283
1363
  );
1284
1364
 
1285
- if (panelBeforeNewValue.lt(panelBefore.min.value)) {
1365
+ if (
1366
+ panelBeforeNewValue.lt(panelBefore.min.value) &&
1367
+ !panelBefore.collapsible
1368
+ ) {
1286
1369
  // TODO this should probably distribute the space between the panels?
1287
1370
  return { dragOvershoot: newDragOvershoot };
1288
1371
  }
@@ -1323,7 +1406,7 @@ function updateLayout(
1323
1406
  panelBefore.collapsed = true;
1324
1407
  panelBeforeNewValue = getUnitPixelValue(context, panelBefore.collapsedSize);
1325
1408
  // Add the extra space created to the before panel
1326
- panelAfterNewValue = panelAfterNewValue.add(
1409
+ panelAfterNewValue = panelAfter.currentValue.value.add(
1327
1410
  panelBeforePreviousValue.minus(panelBeforeNewValue)
1328
1411
  );
1329
1412
 
@@ -1427,6 +1510,7 @@ function iterativelyUpdateLayout({
1427
1510
  controlled,
1428
1511
  disregardCollapseBuffer,
1429
1512
  isVirtual,
1513
+ panelId,
1430
1514
  }: {
1431
1515
  context: GroupMachineContextValue;
1432
1516
  handleId: string;
@@ -1439,6 +1523,7 @@ function iterativelyUpdateLayout({
1439
1523
  * no on* callbacks will be called
1440
1524
  */
1441
1525
  isVirtual?: boolean;
1526
+ panelId: string;
1442
1527
  }) {
1443
1528
  let newContext: Partial<GroupMachineContextValue> = context;
1444
1529
 
@@ -1454,6 +1539,7 @@ function iterativelyUpdateLayout({
1454
1539
  controlled,
1455
1540
  disregardCollapseBuffer,
1456
1541
  isVirtual,
1542
+ panelId,
1457
1543
  value: dragHandlePayload({
1458
1544
  delta: direction,
1459
1545
  orientation: context.orientation,
@@ -1560,6 +1646,7 @@ function handleOverflow(context: GroupMachineContextValue) {
1560
1646
  handleId: handleId.item.id,
1561
1647
  delta: sizeChange,
1562
1648
  direction: (handleId.direction * -1) as -1 | 1,
1649
+ panelId: collapsiblePanel.id,
1563
1650
  context: {
1564
1651
  ...newContext,
1565
1652
  // act like its the old size so the space is distributed correctly
@@ -1595,6 +1682,7 @@ function setCookie(name: string, jsonData: unknown) {
1595
1682
  interface AnimationActorOutput {
1596
1683
  panelId: string;
1597
1684
  action: "expand" | "collapse";
1685
+ resolve?: () => void;
1598
1686
  }
1599
1687
 
1600
1688
  function getDeltaForEvent(
@@ -1620,11 +1708,20 @@ function animationActor(
1620
1708
  abortController: AbortController
1621
1709
  ) {
1622
1710
  const panel = getPanelWithId(context, event.panelId);
1623
- const handle = getHandleForPanelId(context, event.panelId);
1624
-
1711
+ const handle = getHandleForPanelIdWithAvailableSpace(context, event.panelId);
1625
1712
  let direction = new Big(handle.direction);
1626
1713
  const fullDelta = getDeltaForEvent(context, event);
1627
1714
 
1715
+ const contextCopy = { ...context, items: prepareItems(context) };
1716
+ const finalLayout = iterativelyUpdateLayout({
1717
+ context: contextCopy,
1718
+ panelId: event.panelId,
1719
+ handleId: handle.item.id,
1720
+ delta: fullDelta,
1721
+ direction: handle.direction,
1722
+ isVirtual: true,
1723
+ });
1724
+
1628
1725
  return new Promise<AnimationActorOutput | undefined>((resolve, reject) => {
1629
1726
  abortController.signal.addEventListener("abort", () => {
1630
1727
  reject(new Error("Operation was canceled"));
@@ -1652,6 +1749,7 @@ function animationActor(
1652
1749
  type: "applyDelta",
1653
1750
  handleId: handle.item.id,
1654
1751
  delta: delta.toNumber(),
1752
+ panelId: panel.id,
1655
1753
  });
1656
1754
 
1657
1755
  appliedDelta = appliedDelta.add(
@@ -1665,8 +1763,21 @@ function animationActor(
1665
1763
  );
1666
1764
 
1667
1765
  if (e.eq(1)) {
1766
+ // If we're controlled expanding to a value that breaks the layout we might get negative values
1767
+ // In this case we need to use a valid layout like `iterativelyUpdateLayout`'s return value
1768
+ // Ideally we figure out some way to not do this but disregardCollapseBuffer is needed for animations
1769
+ if (
1770
+ context.items.find(
1771
+ (i) =>
1772
+ isPanelData(i) &&
1773
+ i.currentValue.value.round(undefined, Big.roundHalfEven).lt(0)
1774
+ )
1775
+ ) {
1776
+ assign(context, finalLayout);
1777
+ }
1778
+
1668
1779
  const action = event.type === "expandPanel" ? "expand" : "collapse";
1669
- resolve({ panelId: panel.id, action });
1780
+ resolve({ panelId: panel.id, action, resolve: event.resolve });
1670
1781
  return false;
1671
1782
  }
1672
1783
 
@@ -1689,6 +1800,7 @@ export interface GroupMachineInput {
1689
1800
  groupId: string;
1690
1801
  items?: Item[];
1691
1802
  autosaveStrategy?: "localStorage" | "cookie";
1803
+ shiftAmount?: number;
1692
1804
  }
1693
1805
 
1694
1806
  export type State = "idle" | "dragging" | "togglingCollapse";
@@ -1712,6 +1824,7 @@ export function groupMachine(
1712
1824
  dragOvershoot: new Big(0),
1713
1825
  groupId: input.groupId || `group-${groupId++}`,
1714
1826
  autosaveStrategy: input.autosaveStrategy,
1827
+ shiftAmount: input.shiftAmount ?? 15,
1715
1828
  };
1716
1829
 
1717
1830
  const actions = {
@@ -1799,6 +1912,7 @@ export function groupMachine(
1799
1912
  : handle.direction;
1800
1913
 
1801
1914
  const newContext = updateLayout(context, {
1915
+ panelId: event.panelId,
1802
1916
  handleId: handle.item.id,
1803
1917
  type: "dragHandle",
1804
1918
  controlled: event.controlled,
@@ -1843,15 +1957,20 @@ export function groupMachine(
1843
1957
  },
1844
1958
  cannotExpandPanel: (event: GroupMachineEvent) => {
1845
1959
  isEvent(event, ["expandPanel"]);
1846
- const delta = getDeltaForEvent(context, event);
1847
- const handle = getHandleForPanelId(context, event.panelId);
1848
1960
  const pixelItems = prepareItems(context);
1849
-
1850
1961
  let interimContext = { ...context, items: pixelItems };
1962
+
1963
+ const delta = getDeltaForEvent(context, event);
1964
+ const handle = getHandleForPanelIdWithAvailableSpace(
1965
+ interimContext,
1966
+ event.panelId
1967
+ );
1968
+
1851
1969
  interimContext = {
1852
1970
  ...interimContext,
1853
1971
  ...iterativelyUpdateLayout({
1854
1972
  context: interimContext,
1973
+ panelId: event.panelId,
1855
1974
  handleId: handle.item.id,
1856
1975
  controlled: event.controlled,
1857
1976
  delta: delta,
@@ -1900,6 +2019,8 @@ export function groupMachine(
1900
2019
  }
1901
2020
 
1902
2021
  state.current = to;
2022
+
2023
+ onUpdate?.(context);
1903
2024
  }
1904
2025
 
1905
2026
  function send(event: GroupMachineEvent) {
@@ -2110,11 +2231,13 @@ export function groupMachine(
2110
2231
  const delta = isBigger
2111
2232
  ? newSize.minus(current)
2112
2233
  : current.minus(newSize);
2234
+ panel.sizeBeforeCollapse = newSize.toNumber();
2113
2235
 
2114
2236
  Object.assign(
2115
2237
  context,
2116
2238
  iterativelyUpdateLayout({
2117
2239
  context,
2240
+ panelId: event.panelId,
2118
2241
  direction: (handle.direction * (isBigger ? 1 : -1)) as -1 | 1,
2119
2242
  handleId: handle.item.id,
2120
2243
  delta,
@@ -2139,6 +2262,9 @@ export function groupMachine(
2139
2262
  (output) => {
2140
2263
  actions.onAnimationEnd(output);
2141
2264
  transition("idle");
2265
+ if (event.resolve) {
2266
+ requestAnimationFrame(event.resolve);
2267
+ }
2142
2268
  }
2143
2269
  );
2144
2270
  }
@@ -2159,6 +2285,9 @@ export function groupMachine(
2159
2285
  (output) => {
2160
2286
  actions.onAnimationEnd(output);
2161
2287
  transition("idle");
2288
+ if (event.resolve) {
2289
+ requestAnimationFrame(event.resolve);
2290
+ }
2162
2291
  }
2163
2292
  );
2164
2293
  }
@@ -2191,6 +2320,7 @@ export function groupMachine(
2191
2320
  assign(
2192
2321
  context,
2193
2322
  updateLayout(context, {
2323
+ panelId: event.panelId,
2194
2324
  handleId: event.handleId,
2195
2325
  type: "collapsePanel",
2196
2326
  disregardCollapseBuffer: true,
@@ -485,6 +485,283 @@ describe("constraints", () => {
485
485
  );
486
486
  });
487
487
 
488
+ test("sibling collapsible panels work", async () => {
489
+ const onCollapseChange = (isCollapsed: boolean) => {
490
+ if (isCollapsed) {
491
+ actor.send({
492
+ type: "collapsePanel",
493
+ panelId: "panel-1",
494
+ controlled: true,
495
+ });
496
+ } else {
497
+ actor.send({
498
+ type: "expandPanel",
499
+ panelId: "panel-1",
500
+ controlled: true,
501
+ });
502
+ }
503
+ };
504
+
505
+ const actor = createActor({
506
+ groupId: "group",
507
+ items: [
508
+ initializePanel({
509
+ id: "panel-1",
510
+ default: "200px",
511
+ min: "100px",
512
+ collapsible: true,
513
+ collapsed: false,
514
+ onCollapseChange: { current: onCollapseChange },
515
+ }),
516
+ initializePanelHandleData({ id: "resizer-1", size: "10px" }),
517
+ initializePanel({
518
+ id: "panel-2",
519
+ default: "200px",
520
+ min: "100px",
521
+ collapsible: true,
522
+ }),
523
+ initializePanelHandleData({ id: "resizer-2", size: "10px" }),
524
+ initializePanel({
525
+ id: "panel-3",
526
+ min: "200px",
527
+ collapsible: true,
528
+ collapsedSize: "60px",
529
+ }),
530
+ ],
531
+ });
532
+
533
+ initializeSizes(actor, { width: 800, height: 200 });
534
+
535
+ capturePixelValues(actor, () => {
536
+ // First collapse both panels
537
+ expect(buildTemplate(actor.value)).toMatchInlineSnapshot(
538
+ `"200px 10px 200px 10px 380px"`
539
+ );
540
+ dragHandle(actor, { id: "resizer-2", delta: -410 });
541
+ expect(buildTemplate(actor.value)).toMatchInlineSnapshot(
542
+ `"0px 10px 0px 10px 780px"`
543
+ );
544
+ });
545
+
546
+ actor.send({
547
+ type: "expandPanel",
548
+ panelId: "panel-1",
549
+ controlled: true,
550
+ });
551
+ await waitForIdle(actor);
552
+
553
+ capturePixelValues(actor, () => {
554
+ expect(buildTemplate(actor.value)).toMatchInlineSnapshot(
555
+ `"100px 10px 0px 10px 680px"`
556
+ );
557
+ });
558
+ });
559
+
560
+ test("controlled collapse complex drags", async () => {
561
+ const onCollapseChange = (panelId: string) => (isCollapsed: boolean) => {
562
+ if (isCollapsed) {
563
+ actor.send({
564
+ type: "collapsePanel",
565
+ panelId,
566
+ controlled: true,
567
+ });
568
+ } else {
569
+ actor.send({ type: "expandPanel", panelId, controlled: true });
570
+ }
571
+ };
572
+
573
+ const onChange1 = onCollapseChange("panel-1");
574
+ const onChange2 = onCollapseChange("panel-2");
575
+ const onChange3 = onCollapseChange("panel-3");
576
+
577
+ const actor = createActor({
578
+ groupId: "group",
579
+ items: [
580
+ initializePanel({
581
+ id: "panel-1",
582
+ default: "200px",
583
+ min: "100px",
584
+ collapsible: true,
585
+ collapsed: false,
586
+ onCollapseChange: { current: onChange1 },
587
+ }),
588
+ initializePanelHandleData({ id: "resizer-1", size: "10px" }),
589
+ initializePanel({
590
+ id: "panel-2",
591
+ default: "200px",
592
+ min: "100px",
593
+ collapsible: true,
594
+ onCollapseChange: { current: onChange2 },
595
+ }),
596
+ initializePanelHandleData({ id: "resizer-2", size: "10px" }),
597
+ initializePanel({
598
+ id: "panel-3",
599
+ min: "100px",
600
+ collapsible: true,
601
+ onCollapseChange: { current: onChange3 },
602
+ }),
603
+ ],
604
+ });
605
+
606
+ initializeSizes(actor, { width: 800, height: 200 });
607
+
608
+ // Collapse middle panel from the left
609
+ capturePixelValues(actor, () => {
610
+ expect(buildTemplate(actor.value)).toMatchInlineSnapshot(
611
+ `"200px 10px 200px 10px 380px"`
612
+ );
613
+ dragHandle(actor, { id: "resizer-1", delta: 200 });
614
+ expect(buildTemplate(actor.value)).toMatchInlineSnapshot(
615
+ `"450px 10px 0px 10px 330px"`
616
+ );
617
+ dragHandle(actor, { id: "resizer-1", delta: -200 });
618
+ expect(buildTemplate(actor.value)).toMatchInlineSnapshot(
619
+ `"249px 10px 201px 10px 330px"`
620
+ );
621
+ });
622
+
623
+ // Collapse middle panel from the right
624
+ capturePixelValues(actor, () => {
625
+ dragHandle(actor, { id: "resizer-2", delta: -200 });
626
+ expect(buildTemplate(actor.value)).toMatchInlineSnapshot(
627
+ `"200px 10px 0px 10px 580px"`
628
+ );
629
+ dragHandle(actor, { id: "resizer-2", delta: 200 });
630
+ expect(buildTemplate(actor.value)).toMatchInlineSnapshot(
631
+ `"200px 10px 201px 10px 379px"`
632
+ );
633
+ });
634
+
635
+ // Collapse the left panel
636
+ capturePixelValues(actor, () => {
637
+ dragHandle(actor, { id: "resizer-1", delta: -200 });
638
+ expect(buildTemplate(actor.value)).toMatchInlineSnapshot(
639
+ `"0px 10px 401px 10px 379px"`
640
+ );
641
+ dragHandle(actor, { id: "resizer-1", delta: 200 });
642
+ expect(buildTemplate(actor.value)).toMatchInlineSnapshot(
643
+ `"150px 10px 251px 10px 379px"`
644
+ );
645
+ });
646
+
647
+ // Collapse the right panel
648
+ capturePixelValues(actor, () => {
649
+ dragHandle(actor, { id: "resizer-2", delta: 600 });
650
+ expect(buildTemplate(actor.value)).toMatchInlineSnapshot(
651
+ `"150px 10px 630px 10px 0px"`
652
+ );
653
+ dragHandle(actor, { id: "resizer-2", delta: -600 });
654
+ expect(buildTemplate(actor.value)).toMatchInlineSnapshot(
655
+ `"150px 10px 300px 10px 330px"`
656
+ );
657
+ });
658
+
659
+ // Sweep the left handle to the right
660
+ capturePixelValues(actor, () => {
661
+ dragHandle(actor, { id: "resizer-1", delta: 1000 });
662
+ expect(buildTemplate(actor.value)).toMatchInlineSnapshot(
663
+ `"780px 10px 0px 10px 0px"`
664
+ );
665
+ });
666
+
667
+ // Sweep the right handle to the left
668
+ capturePixelValues(actor, () => {
669
+ dragHandle(actor, { id: "resizer-2", delta: -1000 });
670
+ expect(buildTemplate(actor.value)).toMatchInlineSnapshot(
671
+ `"0px 10px 0px 10px 780px"`
672
+ );
673
+ });
674
+ });
675
+
676
+ test("controlled collapse events", async () => {
677
+ const onCollapseChange = (panelId: string) => (isCollapsed: boolean) => {
678
+ if (isCollapsed) {
679
+ actor.send({
680
+ type: "collapsePanel",
681
+ panelId,
682
+ controlled: true,
683
+ });
684
+ } else {
685
+ actor.send({ type: "expandPanel", panelId, controlled: true });
686
+ }
687
+ };
688
+
689
+ const onChange1 = onCollapseChange("panel-1");
690
+ const onChange2 = onCollapseChange("panel-2");
691
+ const onChange3 = onCollapseChange("panel-3");
692
+
693
+ const actor = createActor({
694
+ groupId: "group",
695
+ items: [
696
+ initializePanel({
697
+ id: "panel-1",
698
+ default: "200px",
699
+ min: "100px",
700
+ collapsible: true,
701
+ collapsed: false,
702
+ onCollapseChange: { current: onChange1 },
703
+ }),
704
+ initializePanelHandleData({ id: "resizer-1", size: "10px" }),
705
+ initializePanel({
706
+ id: "panel-2",
707
+ default: "200px",
708
+ min: "100px",
709
+ collapsible: true,
710
+ collapsed: false,
711
+ onCollapseChange: { current: onChange2 },
712
+ }),
713
+ initializePanelHandleData({ id: "resizer-2", size: "10px" }),
714
+ initializePanel({
715
+ id: "panel-3",
716
+ min: "100px",
717
+ collapsible: true,
718
+ collapsed: false,
719
+ onCollapseChange: { current: onChange3 },
720
+ }),
721
+ ],
722
+ });
723
+
724
+ initializeSizes(actor, { width: 800, height: 200 });
725
+
726
+ capturePixelValues(actor, () => {
727
+ expect(buildTemplate(actor.value)).toMatchInlineSnapshot(
728
+ `"200px 10px 200px 10px 380px"`
729
+ );
730
+ });
731
+
732
+ actor.send({ type: "collapsePanel", panelId: "panel-2", controlled: true });
733
+ await waitForIdle(actor);
734
+ capturePixelValues(actor, () => {
735
+ expect(buildTemplate(actor.value)).toMatchInlineSnapshot(
736
+ `"200px 10px 0px 10px 580px"`
737
+ );
738
+ });
739
+
740
+ actor.send({ type: "collapsePanel", panelId: "panel-3", controlled: true });
741
+ await waitForIdle(actor);
742
+ capturePixelValues(actor, () => {
743
+ expect(buildTemplate(actor.value)).toMatchInlineSnapshot(
744
+ `"780px 10px 0px 10px 0px"`
745
+ );
746
+ });
747
+
748
+ actor.send({ type: "expandPanel", panelId: "panel-2", controlled: true });
749
+ await waitForIdle(actor);
750
+ capturePixelValues(actor, () => {
751
+ expect(buildTemplate(actor.value)).toMatchInlineSnapshot(
752
+ `"580px 10px 200px 10px 0px"`
753
+ );
754
+ });
755
+
756
+ actor.send({ type: "expandPanel", panelId: "panel-3", controlled: true });
757
+ await waitForIdle(actor);
758
+ capturePixelValues(actor, () => {
759
+ expect(buildTemplate(actor.value)).toMatchInlineSnapshot(
760
+ `"100px 10px 0px 10px 680px"`
761
+ );
762
+ });
763
+ });
764
+
488
765
  test("panel can have a min", () => {
489
766
  const actor = createActor({ groupId: "group" });
490
767