@window-splitter/state 0.2.4 → 0.3.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
@@ -23,12 +23,12 @@ export type PercentUnit = `${number}%`;
23
23
  export type Unit = PixelUnit | PercentUnit;
24
24
  type Orientation = "horizontal" | "vertical";
25
25
 
26
- interface ParsedPercentUnit {
26
+ export interface ParsedPercentUnit {
27
27
  type: "percent";
28
28
  value: Big.Big;
29
29
  }
30
30
 
31
- interface ParsedPixelUnit {
31
+ export interface ParsedPixelUnit {
32
32
  type: "pixel";
33
33
  value: Big.Big;
34
34
  }
@@ -88,6 +88,10 @@ export interface PanelData
88
88
  onCollapseChange?: {
89
89
  current: ((isCollapsed: boolean) => void) | null | undefined;
90
90
  };
91
+ /** A ref to the latest "onResize" function provided by the user */
92
+ onResize?: {
93
+ current: OnResizeCallback | null | undefined;
94
+ };
91
95
  /**
92
96
  * The current value for the item in the grid
93
97
  */
@@ -113,8 +117,8 @@ function getCollapseAnimation(panel: PanelData) {
113
117
  if (panel.collapseAnimation) {
114
118
  if (typeof panel.collapseAnimation === "string") {
115
119
  easeFn = collapseAnimations[panel.collapseAnimation];
116
- } else if ("duration" in panel.collapseAnimation) {
117
- duration = panel.collapseAnimation.duration ?? duration;
120
+ } else {
121
+ duration = panel.collapseAnimation.duration;
118
122
  easeFn =
119
123
  typeof panel.collapseAnimation.easing === "function"
120
124
  ? panel.collapseAnimation.easing
@@ -178,7 +182,7 @@ export type InitializePanelHandleData = Omit<
178
182
  PanelHandleData,
179
183
  "type" | "size"
180
184
  > & {
181
- size: PixelUnit | ParsedPixelUnit;
185
+ size: PixelUnit;
182
186
  };
183
187
 
184
188
  interface RegisterPanelHandleEvent {
@@ -289,8 +293,6 @@ export interface GroupMachineContextValue {
289
293
  orientation: Orientation;
290
294
  /** How much the drag has overshot the handle */
291
295
  dragOvershoot: Big.Big;
292
- /** An id to use for autosaving the layout */
293
- autosaveId?: string;
294
296
  groupId: string;
295
297
  }
296
298
 
@@ -320,6 +322,28 @@ type EventForType<T extends GroupMachineEvent["type"]> = Extract<
320
322
 
321
323
  // #region Helpers
322
324
 
325
+ export function getCursor(
326
+ context: Pick<GroupMachineContextValue, "dragOvershoot" | "orientation">
327
+ ) {
328
+ if (context.orientation === "horizontal") {
329
+ if (context.dragOvershoot.gt(0)) {
330
+ return "w-resize";
331
+ } else if (context.dragOvershoot.lt(0)) {
332
+ return "e-resize";
333
+ } else {
334
+ return "ew-resize";
335
+ }
336
+ } else {
337
+ if (context.dragOvershoot.gt(0)) {
338
+ return "n-resize";
339
+ } else if (context.dragOvershoot.lt(0)) {
340
+ return "s-resize";
341
+ } else {
342
+ return "ns-resize";
343
+ }
344
+ }
345
+ }
346
+
323
347
  export function prepareSnapshot(snapshot: Snapshot<unknown>) {
324
348
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
325
349
  const snapshotContext = (snapshot as any)
@@ -330,14 +354,8 @@ export function prepareSnapshot(snapshot: Snapshot<unknown>) {
330
354
  for (const item of snapshotContext.items) {
331
355
  if (isPanelData(item)) {
332
356
  item.currentValue.value = new Big(item.currentValue.value);
333
-
334
- if (item.collapsedSize) {
335
- item.collapsedSize.value = new Big(item.collapsedSize.value);
336
- }
337
-
338
- if (item.min) {
339
- item.min.value = new Big(item.min.value);
340
- }
357
+ item.collapsedSize.value = new Big(item.collapsedSize.value);
358
+ item.min.value = new Big(item.min.value);
341
359
 
342
360
  if (item.max && item.max !== "1fr") {
343
361
  item.max.value = new Big(item.max.value);
@@ -381,6 +399,13 @@ export function isPanelHandle(value: unknown): value is PanelHandleData {
381
399
  );
382
400
  }
383
401
 
402
+ type OnResizeSize = {
403
+ pixel: number;
404
+ percentage: number;
405
+ };
406
+
407
+ export type OnResizeCallback = (size: OnResizeSize) => void;
408
+
384
409
  interface InitializePanelOptions {
385
410
  min?: Unit;
386
411
  max?: Unit;
@@ -391,6 +416,9 @@ interface InitializePanelOptions {
391
416
  onCollapseChange?: {
392
417
  current: ((isCollapsed: boolean) => void) | null | undefined;
393
418
  };
419
+ onResize?: {
420
+ current: OnResizeCallback | null | undefined;
421
+ };
394
422
  collapseAnimation?: PanelData["collapseAnimation"];
395
423
  defaultCollapsed?: boolean;
396
424
  id?: string;
@@ -405,6 +433,25 @@ export function initializePanel(
405
433
  export function initializePanel(
406
434
  item: InitializePanelOptions | InitializePanelOptionsWithId
407
435
  ): PanelData | Omit<PanelData, "id"> {
436
+ const onResize = () => {
437
+ let lastCall: OnResizeSize | null = null;
438
+
439
+ // Memo-ize so we only call the callback once per size
440
+ return ((size) => {
441
+ if (
442
+ !lastCall ||
443
+ (lastCall.pixel === size.pixel &&
444
+ lastCall.percentage === size.percentage)
445
+ ) {
446
+ lastCall = size;
447
+ return;
448
+ }
449
+
450
+ lastCall = size;
451
+ item.onResize?.current?.(size);
452
+ }) satisfies OnResizeCallback;
453
+ };
454
+
408
455
  const data = {
409
456
  type: "panel" as const,
410
457
  min: parseUnit(item.min || "0px"),
@@ -415,6 +462,7 @@ export function initializePanel(
415
462
  collapsible: item.collapsible,
416
463
  collapsedSize: parseUnit(item.collapsedSize ?? "0px"),
417
464
  onCollapseChange: item.onCollapseChange,
465
+ onResize: { current: onResize() },
418
466
  collapseIsControlled: typeof item.collapsed !== "undefined",
419
467
  sizeBeforeCollapse: undefined,
420
468
  id: item.id,
@@ -432,7 +480,10 @@ export function initializePanelHandleData(item: InitializePanelHandleData) {
432
480
  return {
433
481
  type: "handle" as const,
434
482
  ...item,
435
- size: typeof item.size === "string" ? parseUnit(item.size) : item.size,
483
+ size:
484
+ typeof item.size === "string"
485
+ ? (parseUnit(item.size) as ParsedPixelUnit)
486
+ : item.size,
436
487
  };
437
488
  }
438
489
 
@@ -469,7 +520,7 @@ export function getGroupSize(context: GroupMachineContextValue) {
469
520
  }
470
521
 
471
522
  /** Get the size of a panel in pixels */
472
- export function getUnitPixelValue(
523
+ function getUnitPixelValue(
473
524
  context: GroupMachineContextValue,
474
525
  unit: ParsedUnit | "1fr"
475
526
  ) {
@@ -695,6 +746,52 @@ function formatUnit(unit: ParsedUnit): Unit {
695
746
  return `${unit.value.toNumber()}%`;
696
747
  }
697
748
 
749
+ export function getPanelGroupPixelSizes(context: GroupMachineContextValue) {
750
+ return prepareItems(context).map((i) =>
751
+ isPanelData(i)
752
+ ? i.currentValue.value.toNumber()
753
+ : getUnitPixelValue(context, i.size).toNumber()
754
+ );
755
+ }
756
+
757
+ export function getPanelPixelSize(
758
+ context: GroupMachineContextValue,
759
+ panelId: string
760
+ ) {
761
+ const p = getPanelWithId(
762
+ { ...context, items: prepareItems(context) },
763
+ panelId
764
+ );
765
+
766
+ return p.currentValue.value.toNumber();
767
+ }
768
+
769
+ export function getPanelGroupPercentageSizes(
770
+ context: GroupMachineContextValue
771
+ ) {
772
+ const clamped = commitLayout({
773
+ ...context,
774
+ items: prepareItems(context),
775
+ });
776
+
777
+ return clamped.map((i) => {
778
+ if (isPanelHandle(i)) {
779
+ return getUnitPercentageValue(getGroupSize(context), i.size);
780
+ }
781
+
782
+ return getUnitPercentageValue(getGroupSize(context), i.currentValue);
783
+ });
784
+ }
785
+
786
+ export function getPanelPercentageSize(
787
+ context: GroupMachineContextValue,
788
+ panelId: string
789
+ ) {
790
+ const items = prepareItems(context);
791
+ const p = getPanelWithId({ ...context, items }, panelId);
792
+ return getUnitPercentageValue(getGroupSize(context), p.currentValue);
793
+ }
794
+
698
795
  /** Build the grid template from the item values. */
699
796
  export function buildTemplate(context: GroupMachineContextValue) {
700
797
  const staticWidth = getStaticWidth(context);
@@ -708,10 +805,6 @@ export function buildTemplate(context: GroupMachineContextValue) {
708
805
  item.currentValue.type === "pixel" &&
709
806
  item.currentValue.value.toNumber() !== -1
710
807
  ) {
711
- if (item.currentValue.value.toNumber() < 0) {
712
- return "0px";
713
- }
714
-
715
808
  return formatUnit(item.currentValue);
716
809
  } else if (item.currentValue.type === "percent") {
717
810
  const max = item.max === "1fr" ? "100%" : formatUnit(item.max);
@@ -1071,7 +1164,7 @@ function updateLayout(
1071
1164
  )
1072
1165
  );
1073
1166
 
1074
- if (leftoverSpace.gt(0)) {
1167
+ if (!leftoverSpace.eq(0)) {
1075
1168
  panelBefore.currentValue.value =
1076
1169
  panelBefore.currentValue.value.add(leftoverSpace);
1077
1170
  }
@@ -1080,7 +1173,7 @@ function updateLayout(
1080
1173
  }
1081
1174
 
1082
1175
  /** Converts the items to percentages */
1083
- export function commitLayout(context: GroupMachineContextValue) {
1176
+ function commitLayout(context: GroupMachineContextValue) {
1084
1177
  const newItems = [...context.items];
1085
1178
 
1086
1179
  // First set all the static width
@@ -1120,11 +1213,11 @@ export function commitLayout(context: GroupMachineContextValue) {
1120
1213
 
1121
1214
  export function dragHandlePayload({
1122
1215
  delta,
1123
- orientation,
1216
+ orientation = "horizontal",
1124
1217
  shiftKey = false,
1125
1218
  }: {
1126
1219
  delta: number;
1127
- orientation: Orientation;
1220
+ orientation?: Orientation;
1128
1221
  shiftKey?: boolean;
1129
1222
  }) {
1130
1223
  return {
@@ -1308,7 +1401,6 @@ export const groupMachine = createMachine(
1308
1401
  context: {} as GroupMachineContextValue,
1309
1402
  events: {} as GroupMachineEvent,
1310
1403
  input: {} as {
1311
- autosaveId?: string;
1312
1404
  orientation?: Orientation;
1313
1405
  groupId: string;
1314
1406
  initialItems?: Item[];
@@ -1319,7 +1411,6 @@ export const groupMachine = createMachine(
1319
1411
  items: input.initialItems || [],
1320
1412
  orientation: input.orientation || "horizontal",
1321
1413
  dragOvershoot: new Big(0),
1322
- autosaveId: input.autosaveId,
1323
1414
  groupId: input.groupId,
1324
1415
  }),
1325
1416
  states: {
@@ -1328,7 +1419,13 @@ export const groupMachine = createMachine(
1328
1419
  setActualItemsSize: { actions: ["recordActualItemSize"] },
1329
1420
  dragHandleStart: { target: "dragging" },
1330
1421
  setPanelPixelSize: {
1331
- actions: ["prepare", "onSetPanelSize", "commit"],
1422
+ actions: [
1423
+ "prepare",
1424
+ "onSetPanelSize",
1425
+ "commit",
1426
+ "onResize",
1427
+ "onAutosave",
1428
+ ],
1332
1429
  },
1333
1430
  collapsePanel: [
1334
1431
  {
@@ -1349,7 +1446,7 @@ export const groupMachine = createMachine(
1349
1446
  dragging: {
1350
1447
  entry: ["prepare"],
1351
1448
  on: {
1352
- dragHandle: { actions: ["onDragHandle"] },
1449
+ dragHandle: { actions: ["onDragHandle", "onResize"] },
1353
1450
  dragHandleEnd: { target: "idle" },
1354
1451
  collapsePanel: {
1355
1452
  guard: "shouldCollapseToggle",
@@ -1373,24 +1470,30 @@ export const groupMachine = createMachine(
1373
1470
  },
1374
1471
  },
1375
1472
  on: {
1376
- applyDelta: { actions: ["onApplyDelta"] },
1473
+ applyDelta: { actions: ["onApplyDelta", "onResize"] },
1377
1474
  },
1378
1475
  },
1379
1476
  },
1380
1477
  on: {
1381
1478
  registerPanel: { actions: ["assignPanelData"] },
1382
1479
  registerDynamicPanel: {
1383
- actions: ["prepare", "onRegisterDynamicPanel", "commit", "onAutosave"],
1480
+ actions: [
1481
+ "prepare",
1482
+ "onRegisterDynamicPanel",
1483
+ "commit",
1484
+ "onResize",
1485
+ "onAutosave",
1486
+ ],
1384
1487
  },
1385
1488
  unregisterPanel: {
1386
- actions: ["prepare", "removeItem", "commit", "onAutosave"],
1489
+ actions: ["prepare", "removeItem", "commit", "onResize", "onAutosave"],
1387
1490
  },
1388
1491
  registerPanelHandle: { actions: ["assignPanelHandleData"] },
1389
1492
  unregisterPanelHandle: {
1390
- actions: ["prepare", "removeItem", "commit", "onAutosave"],
1493
+ actions: ["prepare", "removeItem", "commit", "onResize", "onAutosave"],
1391
1494
  },
1392
- setSize: { actions: ["updateSize"] },
1393
- setOrientation: { actions: ["updateOrientation"] },
1495
+ setSize: { actions: ["updateSize", "onResize"] },
1496
+ setOrientation: { actions: ["updateOrientation", "onResize"] },
1394
1497
  },
1395
1498
  },
1396
1499
  {
@@ -1630,6 +1733,19 @@ export const groupMachine = createMachine(
1630
1733
  })
1631
1734
  );
1632
1735
  }),
1736
+ onResize: ({ context }) => {
1737
+ for (const item of context.items) {
1738
+ if (isPanelData(item)) {
1739
+ item.onResize?.current?.({
1740
+ pixel: getUnitPixelValue(context, item.currentValue).toNumber(),
1741
+ percentage: getUnitPercentageValue(
1742
+ getGroupSize(context),
1743
+ item.currentValue
1744
+ ),
1745
+ });
1746
+ }
1747
+ }
1748
+ },
1633
1749
  },
1634
1750
  }
1635
1751
  );