@window-splitter/state 0.4.0 → 0.4.2

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.
@@ -1,6 +1,86 @@
1
1
  // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
3
  exports[`utils > prepareSnapshot 1`] = `
4
+ {
5
+ "children": {},
6
+ "context": {
7
+ "dragOvershoot": "0",
8
+ "groupId": "group",
9
+ "items": [
10
+ {
11
+ "collapseIsControlled": false,
12
+ "collapsedSize": {
13
+ "type": "pixel",
14
+ "value": "0",
15
+ },
16
+ "currentValue": {
17
+ "type": "pixel",
18
+ "value": "-1",
19
+ },
20
+ "id": "panel-1",
21
+ "lastKnownSize": {
22
+ "height": 200,
23
+ "width": 190,
24
+ },
25
+ "max": "1fr",
26
+ "min": {
27
+ "type": "pixel",
28
+ "value": "100",
29
+ },
30
+ "onResize": {},
31
+ "type": "panel",
32
+ },
33
+ {
34
+ "id": "resizer-1",
35
+ "size": {
36
+ "type": "pixel",
37
+ "value": "10",
38
+ },
39
+ "type": "handle",
40
+ },
41
+ {
42
+ "collapseIsControlled": false,
43
+ "collapsed": false,
44
+ "collapsedSize": {
45
+ "type": "pixel",
46
+ "value": "50",
47
+ },
48
+ "collapsible": true,
49
+ "currentValue": {
50
+ "type": "pixel",
51
+ "value": "-1",
52
+ },
53
+ "id": "panel-2",
54
+ "lastKnownSize": {
55
+ "height": 200,
56
+ "width": 300,
57
+ },
58
+ "max": {
59
+ "type": "pixel",
60
+ "value": "300",
61
+ },
62
+ "min": {
63
+ "type": "pixel",
64
+ "value": "0",
65
+ },
66
+ "onResize": {},
67
+ "type": "panel",
68
+ },
69
+ ],
70
+ "orientation": "horizontal",
71
+ "size": {
72
+ "height": 200,
73
+ "width": 500,
74
+ },
75
+ },
76
+ "historyValue": {},
77
+ "status": "active",
78
+ "tags": [],
79
+ "value": "idle",
80
+ }
81
+ `;
82
+
83
+ exports[`utils > prepareSnapshot 2`] = `
4
84
  {
5
85
  "children": {},
6
86
  "context": {
@@ -15,7 +95,7 @@ exports[`utils > prepareSnapshot 1`] = `
15
95
  },
16
96
  "currentValue": {
17
97
  "type": "percent",
18
- "value": "0.38775510204081632653",
98
+ "value": "0.59183673469387755102",
19
99
  },
20
100
  "id": "panel-1",
21
101
  "max": "1fr",
@@ -44,7 +124,7 @@ exports[`utils > prepareSnapshot 1`] = `
44
124
  "collapsible": true,
45
125
  "currentValue": {
46
126
  "type": "percent",
47
- "value": "0.61224489795918367347",
127
+ "value": "0.40816326530612244898",
48
128
  },
49
129
  "id": "panel-2",
50
130
  "max": {
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import Cookies from "universal-cookie";
1
2
  import { raf } from "@react-spring/rafz";
2
3
  import {
3
4
  createMachine,
@@ -115,6 +116,7 @@ export interface PanelData
115
116
  collapseAnimation?:
116
117
  | CollapseAnimation
117
118
  | { duration: number; easing: CollapseAnimation | ((t: number) => number) };
119
+ lastKnownSize?: Rect;
118
120
  }
119
121
 
120
122
  function getCollapseAnimation(panel: PanelData) {
@@ -174,6 +176,12 @@ interface RegisterPanelEvent {
174
176
  data: Omit<PanelData, "type" | "currentValue" | "defaultCollapsed">;
175
177
  }
176
178
 
179
+ interface RebindPanelCallbacksEvent {
180
+ /** Rebind the panel callbacks */
181
+ type: "rebindPanelCallbacks";
182
+ data: Pick<PanelData, "id" | "onCollapseChange" | "onResize">;
183
+ }
184
+
177
185
  interface RegisterDynamicPanelEvent extends Omit<RegisterPanelEvent, "type"> {
178
186
  /** Register a new panel with the state machine */
179
187
  type: "registerDynamicPanel";
@@ -235,7 +243,6 @@ interface SetSizeEvent {
235
243
  /** Set the size of the whole group */
236
244
  type: "setSize";
237
245
  size: Rect;
238
- handleOverflow?: boolean;
239
246
  }
240
247
 
241
248
  interface SetActualItemsSizeEvent {
@@ -302,8 +309,17 @@ export interface GroupMachineContextValue {
302
309
  /** How much the drag has overshot the handle */
303
310
  dragOvershoot: Big.Big;
304
311
  groupId: string;
312
+ /**
313
+ * How to save the persisted state
314
+ */
315
+ autosaveStrategy?: "localStorage" | "cookie";
305
316
  }
306
317
 
318
+ export type GroupMachineSnapshot = Snapshot<unknown> & {
319
+ context: GroupMachineContextValue;
320
+ value: string;
321
+ };
322
+
307
323
  export type GroupMachineEvent =
308
324
  | RegisterPanelEvent
309
325
  | RegisterDynamicPanelEvent
@@ -319,8 +335,8 @@ export type GroupMachineEvent =
319
335
  | ExpandPanelEvent
320
336
  | SetPanelPixelSizeEvent
321
337
  | ApplyDeltaEvent
322
- | SetActualItemsSizeEvent;
323
-
338
+ | SetActualItemsSizeEvent
339
+ | RebindPanelCallbacksEvent;
324
340
  type EventForType<T extends GroupMachineEvent["type"]> = Extract<
325
341
  GroupMachineEvent,
326
342
  { type: T }
@@ -352,10 +368,8 @@ export function getCursor(
352
368
  }
353
369
  }
354
370
 
355
- export function prepareSnapshot(snapshot: Snapshot<unknown>) {
356
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
357
- const snapshotContext = (snapshot as any)
358
- .context as unknown as GroupMachineContextValue;
371
+ export function prepareSnapshot(snapshot: GroupMachineSnapshot) {
372
+ const snapshotContext = snapshot.context;
359
373
 
360
374
  snapshotContext.dragOvershoot = new Big(snapshotContext.dragOvershoot);
361
375
 
@@ -800,6 +814,7 @@ export function getPanelPercentageSize(
800
814
  /** Build the grid template from the item values. */
801
815
  export function buildTemplate(context: GroupMachineContextValue) {
802
816
  const staticWidth = getStaticWidth(context);
817
+ let hasSeenFillPanel = false;
803
818
 
804
819
  return context.items
805
820
  .map((item) => {
@@ -817,6 +832,15 @@ export function buildTemplate(context: GroupMachineContextValue) {
817
832
 
818
833
  return formatUnit(item.currentValue);
819
834
  } else if (item.currentValue.type === "percent") {
835
+ if (
836
+ !hasSeenFillPanel &&
837
+ (item.max === "1fr" ||
838
+ (item.max.type === "percent" && item.max.value.eq(100)))
839
+ ) {
840
+ hasSeenFillPanel = true;
841
+ return `minmax(${min}, 1fr)`;
842
+ }
843
+
820
844
  const max = item.max === "1fr" ? "100%" : formatUnit(item.max);
821
845
  return `minmax(${min}, min(calc(${item.currentValue.value} * (100% - ${staticWidth}px)), ${max}))`;
822
846
  } else if (item.collapsible && item.collapsed) {
@@ -939,6 +963,16 @@ export function prepareItems(context: GroupMachineContextValue): Item[] {
939
963
  continue;
940
964
  }
941
965
 
966
+ if (item.lastKnownSize) {
967
+ const lastSize = makePixelUnit(
968
+ context.orientation === "horizontal"
969
+ ? item.lastKnownSize.width
970
+ : item.lastKnownSize.height
971
+ );
972
+ newItems.push({ ...item, currentValue: lastSize });
973
+ continue;
974
+ }
975
+
942
976
  if (item.currentValue.type === "pixel") {
943
977
  newItems.push({ ...item });
944
978
  continue;
@@ -1433,6 +1467,10 @@ function handleOverflow(context: GroupMachineContextValue) {
1433
1467
  return { ...newContext, items: commitLayout(newContext) };
1434
1468
  }
1435
1469
 
1470
+ function clearLastKnownSize(items: Item[]) {
1471
+ return items.map((i) => ({ ...i, lastKnownSize: undefined }));
1472
+ }
1473
+
1436
1474
  // #endregion
1437
1475
 
1438
1476
  // #region Machine
@@ -1534,6 +1572,7 @@ export const groupMachine = createMachine(
1534
1572
  orientation?: Orientation;
1535
1573
  groupId: string;
1536
1574
  initialItems?: Item[];
1575
+ autosaveStrategy?: "localStorage" | "cookie";
1537
1576
  },
1538
1577
  },
1539
1578
  context: ({ input }) => ({
@@ -1542,15 +1581,17 @@ export const groupMachine = createMachine(
1542
1581
  orientation: input.orientation || "horizontal",
1543
1582
  dragOvershoot: new Big(0),
1544
1583
  groupId: input.groupId,
1584
+ autosaveStrategy: input.autosaveStrategy,
1545
1585
  }),
1546
1586
  states: {
1547
1587
  idle: {
1588
+ entry: ["onAutosave"],
1548
1589
  on: {
1549
- setActualItemsSize: { actions: ["recordActualItemSize", "onResize"] },
1550
1590
  dragHandleStart: { target: "dragging" },
1551
1591
  setPanelPixelSize: {
1552
1592
  actions: [
1553
1593
  "prepare",
1594
+ "onClearLastKnownSize",
1554
1595
  "onSetPanelSize",
1555
1596
  "commit",
1556
1597
  "onResize",
@@ -1578,7 +1619,9 @@ export const groupMachine = createMachine(
1578
1619
  dragging: {
1579
1620
  entry: ["prepare"],
1580
1621
  on: {
1581
- dragHandle: { actions: ["onDragHandle", "onResize"] },
1622
+ dragHandle: {
1623
+ actions: ["onClearLastKnownSize", "onDragHandle", "onResize"],
1624
+ },
1582
1625
  dragHandleEnd: { target: "idle" },
1583
1626
  collapsePanel: {
1584
1627
  guard: "shouldCollapseToggle",
@@ -1589,16 +1632,16 @@ export const groupMachine = createMachine(
1589
1632
  actions: "runCollapseToggle",
1590
1633
  },
1591
1634
  },
1592
- exit: ["commit", "onAutosave"],
1635
+ exit: ["commit"],
1593
1636
  },
1594
1637
  togglingCollapse: {
1595
- entry: ["prepare"],
1638
+ entry: ["prepare", "onClearLastKnownSize"],
1596
1639
  invoke: {
1597
1640
  src: "animation",
1598
1641
  input: (i) => ({ ...i, send: i.self.send }),
1599
1642
  onDone: {
1600
1643
  target: "idle",
1601
- actions: ["onToggleCollapseComplete", "commit", "onAutosave"],
1644
+ actions: ["onToggleCollapseComplete", "commit"],
1602
1645
  },
1603
1646
  },
1604
1647
  on: {
@@ -1607,25 +1650,44 @@ export const groupMachine = createMachine(
1607
1650
  },
1608
1651
  },
1609
1652
  on: {
1653
+ setActualItemsSize: { actions: ["recordActualItemSize", "onResize"] },
1610
1654
  registerPanel: { actions: ["assignPanelData"] },
1655
+ rebindPanelCallbacks: { actions: ["rebindPanelCallbacks"] },
1611
1656
  registerDynamicPanel: {
1612
1657
  actions: [
1613
1658
  "prepare",
1614
1659
  "onRegisterDynamicPanel",
1660
+ "onClearLastKnownSize",
1615
1661
  "commit",
1616
1662
  "onResize",
1617
1663
  "onAutosave",
1618
1664
  ],
1619
1665
  },
1620
1666
  unregisterPanel: {
1621
- actions: ["prepare", "removeItem", "commit", "onResize", "onAutosave"],
1667
+ actions: [
1668
+ "prepare",
1669
+ "removeItem",
1670
+ "onClearLastKnownSize",
1671
+ "commit",
1672
+ "onResize",
1673
+ "onAutosave",
1674
+ ],
1622
1675
  },
1623
1676
  registerPanelHandle: { actions: ["assignPanelHandleData"] },
1624
1677
  unregisterPanelHandle: {
1625
- actions: ["prepare", "removeItem", "commit", "onResize", "onAutosave"],
1678
+ actions: [
1679
+ "prepare",
1680
+ "removeItem",
1681
+ "onClearLastKnownSize",
1682
+ "commit",
1683
+ "onResize",
1684
+ "onAutosave",
1685
+ ],
1626
1686
  },
1627
1687
  setSize: { actions: ["updateSize", "onResize"] },
1628
- setOrientation: { actions: ["updateOrientation", "onResize"] },
1688
+ setOrientation: {
1689
+ actions: ["updateOrientation", "onClearLastKnownSize", "onResize"],
1690
+ },
1629
1691
  },
1630
1692
  },
1631
1693
  {
@@ -1677,6 +1739,26 @@ export const groupMachine = createMachine(
1677
1739
  animation: animationActor,
1678
1740
  },
1679
1741
  actions: {
1742
+ onAutosave: ({ context, self }) => {
1743
+ if (!context.autosaveStrategy || typeof window === "undefined") {
1744
+ return;
1745
+ }
1746
+
1747
+ const snapshot = self.getPersistedSnapshot() as GroupMachineSnapshot;
1748
+ snapshot.context.items = clearLastKnownSize(context.items);
1749
+ snapshot.value = "idle";
1750
+ const data = JSON.stringify(snapshot);
1751
+
1752
+ if (context.autosaveStrategy === "localStorage") {
1753
+ localStorage.setItem(context.groupId, data);
1754
+ } else {
1755
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1756
+ const ActualClass = (Cookies as any).default || Cookies;
1757
+ const cookies = new ActualClass(null, { path: "/" });
1758
+
1759
+ cookies.set(context.groupId, data, { path: "/", maxAge: 31536000 });
1760
+ }
1761
+ },
1680
1762
  notifyCollapseToggle: ({ context, event }) => {
1681
1763
  isEvent(event, ["collapsePanel", "expandPanel"]);
1682
1764
  const panel = getPanelWithId(context, event.panelId);
@@ -1703,8 +1785,7 @@ export const groupMachine = createMachine(
1703
1785
  }),
1704
1786
  onToggleCollapseComplete: assign({
1705
1787
  items: ({ context, event: e }) => {
1706
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1707
- const output = (e as any).output as AnimationActorOutput;
1788
+ const { output } = e as unknown as { output: AnimationActorOutput };
1708
1789
  invariant(output, "Expected output from animation actor");
1709
1790
 
1710
1791
  const panel = getPanelWithId(context, output.panelId);
@@ -1717,34 +1798,50 @@ export const groupMachine = createMachine(
1717
1798
  return context.items;
1718
1799
  },
1719
1800
  }),
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
- }
1801
+ updateSize: assign({
1802
+ size: ({ event }) => {
1803
+ isEvent(event, ["setSize"]);
1804
+ return event.size;
1805
+ },
1728
1806
  }),
1729
1807
  recordActualItemSize: assign({
1730
1808
  items: ({ context, event }) => {
1731
1809
  isEvent(event, ["setActualItemsSize"]);
1732
1810
 
1733
- const orientation = context.orientation;
1811
+ const withLastKnownSize = context.items.map((i) => {
1812
+ if (!isPanelData(i)) return i;
1813
+ const lastKnownSize = event.childrenSizes[i.id] || i.lastKnownSize;
1814
+ return { ...i, lastKnownSize };
1815
+ });
1816
+
1817
+ let totalSize = 0;
1818
+
1819
+ for (const item of withLastKnownSize) {
1820
+ if (isPanelData(item)) {
1821
+ const size =
1822
+ item.lastKnownSize?.[
1823
+ context.orientation === "horizontal" ? "width" : "height"
1824
+ ];
1734
1825
 
1735
- for (const [id, size] of Object.entries(event.childrenSizes)) {
1736
- const item = context.items.find((i) => i.id === id);
1826
+ // If any size is 0 don't handle overflow
1827
+ if (!size) {
1828
+ return withLastKnownSize;
1829
+ }
1737
1830
 
1738
- if (!isPanelData(item)) {
1739
- continue;
1831
+ totalSize += size;
1832
+ } else {
1833
+ totalSize += item.size.value.toNumber();
1740
1834
  }
1835
+ }
1741
1836
 
1742
- item.currentValue = makePixelUnit(
1743
- orientation === "horizontal" ? size.width : size.height
1744
- );
1837
+ if (totalSize > getGroupSize(context)) {
1838
+ return handleOverflow({
1839
+ ...context,
1840
+ items: prepareItems({ ...context, items: withLastKnownSize }),
1841
+ }).items;
1745
1842
  }
1746
1843
 
1747
- return commitLayout(context);
1844
+ return withLastKnownSize;
1748
1845
  },
1749
1846
  }),
1750
1847
  updateOrientation: assign({
@@ -1753,6 +1850,9 @@ export const groupMachine = createMachine(
1753
1850
  return event.orientation;
1754
1851
  },
1755
1852
  }),
1853
+ onClearLastKnownSize: assign({
1854
+ items: ({ context }) => clearLastKnownSize(context.items),
1855
+ }),
1756
1856
  assignPanelData: assign({
1757
1857
  items: ({ context, event }) => {
1758
1858
  isEvent(event, ["registerPanel"]);
@@ -1764,6 +1864,20 @@ export const groupMachine = createMachine(
1764
1864
  });
1765
1865
  },
1766
1866
  }),
1867
+ rebindPanelCallbacks: assign({
1868
+ items: ({ context, event }) => {
1869
+ isEvent(event, ["rebindPanelCallbacks"]);
1870
+
1871
+ for (const item of context.items) {
1872
+ if (isPanelData(item) && item.id === event.data.id) {
1873
+ item.onCollapseChange = event.data.onCollapseChange;
1874
+ item.onResize = event.data.onResize;
1875
+ }
1876
+ }
1877
+
1878
+ return context.items;
1879
+ },
1880
+ }),
1767
1881
  onRegisterDynamicPanel: assign({
1768
1882
  items: ({ context, event }) => {
1769
1883
  isEvent(event, ["registerDynamicPanel"]);
@@ -1903,11 +2017,17 @@ export const groupMachine = createMachine(
1903
2017
  onResize: ({ context }) => {
1904
2018
  for (const item of context.items) {
1905
2019
  if (isPanelData(item)) {
1906
- const pixel = clampUnit(
1907
- context,
1908
- item,
1909
- getUnitPixelValue(context, item.currentValue)
1910
- );
2020
+ const pixel = item.lastKnownSize
2021
+ ? new Big(
2022
+ context.orientation === "horizontal"
2023
+ ? item.lastKnownSize.width
2024
+ : item.lastKnownSize.height
2025
+ )
2026
+ : clampUnit(
2027
+ context,
2028
+ item,
2029
+ getUnitPixelValue(context, item.currentValue)
2030
+ );
1911
2031
  const groupSize = getGroupSize(context);
1912
2032
 
1913
2033
  item.onResize?.current?.({