@zag-js/splitter 1.39.1 → 1.41.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.
Files changed (37) hide show
  1. package/dist/index.d.mts +1 -1
  2. package/dist/index.d.ts +1 -1
  3. package/dist/splitter.connect.js +61 -29
  4. package/dist/splitter.connect.mjs +61 -29
  5. package/dist/splitter.dom.d.mts +4 -3
  6. package/dist/splitter.dom.d.ts +4 -3
  7. package/dist/splitter.dom.js +31 -2
  8. package/dist/splitter.dom.mjs +31 -3
  9. package/dist/splitter.machine.js +168 -59
  10. package/dist/splitter.machine.mjs +169 -61
  11. package/dist/splitter.types.d.mts +27 -8
  12. package/dist/splitter.types.d.ts +27 -8
  13. package/dist/utils/aria.d.mts +9 -3
  14. package/dist/utils/aria.d.ts +9 -3
  15. package/dist/utils/aria.js +9 -0
  16. package/dist/utils/aria.mjs +9 -0
  17. package/dist/utils/panel.d.mts +13 -12
  18. package/dist/utils/panel.d.ts +13 -12
  19. package/dist/utils/panel.js +41 -7
  20. package/dist/utils/panel.mjs +41 -7
  21. package/dist/utils/preserve-fixed-panel-sizes.d.mts +18 -0
  22. package/dist/utils/preserve-fixed-panel-sizes.d.ts +18 -0
  23. package/dist/utils/preserve-fixed-panel-sizes.js +91 -0
  24. package/dist/utils/preserve-fixed-panel-sizes.mjs +68 -0
  25. package/dist/utils/registry.js +1 -1
  26. package/dist/utils/registry.mjs +2 -2
  27. package/dist/utils/resize-by-delta.d.mts +2 -2
  28. package/dist/utils/resize-by-delta.d.ts +2 -2
  29. package/dist/utils/resize-panel.d.mts +2 -2
  30. package/dist/utils/resize-panel.d.ts +2 -2
  31. package/dist/utils/size.d.mts +17 -0
  32. package/dist/utils/size.d.ts +17 -0
  33. package/dist/utils/size.js +138 -0
  34. package/dist/utils/size.mjs +111 -0
  35. package/dist/utils/validate-sizes.d.mts +2 -2
  36. package/dist/utils/validate-sizes.d.ts +2 -2
  37. package/package.json +6 -6
@@ -2,7 +2,7 @@ import "./chunk-QZ7TP4HQ.mjs";
2
2
 
3
3
  // src/splitter.machine.ts
4
4
  import { createMachine } from "@zag-js/core";
5
- import { observeChildren, trackPointerMove } from "@zag-js/dom-query";
5
+ import { observeChildren, resizeObserverBorderBox, trackPointerMove } from "@zag-js/dom-query";
6
6
  import { ensure, ensureProps, isEqual, next, prev, setRafTimeout } from "@zag-js/utils";
7
7
  import * as dom from "./splitter.dom.mjs";
8
8
  import { getAriaValue } from "./utils/aria.mjs";
@@ -11,12 +11,13 @@ import {
11
11
  findPanelDataIndex,
12
12
  getPanelById,
13
13
  getPanelLayout,
14
- getUnsafeDefaultSize,
15
14
  panelDataHelper,
16
15
  serializePanels,
17
16
  sortPanels
18
17
  } from "./utils/panel.mjs";
18
+ import { preserveFixedPanelSizes } from "./utils/preserve-fixed-panel-sizes.mjs";
19
19
  import { resizeByDelta } from "./utils/resize-by-delta.mjs";
20
+ import { getGroupSize, normalizePanels, resolvePanelSizes } from "./utils/size.mjs";
20
21
  import { validateSizes } from "./utils/validate-sizes.mjs";
21
22
  var machine = createMachine({
22
23
  props({ props }) {
@@ -34,15 +35,18 @@ var machine = createMachine({
34
35
  },
35
36
  context({ prop, bindable, getContext, getRefs }) {
36
37
  return {
38
+ panels: bindable(() => ({
39
+ defaultValue: normalizePanels(prop("panels"), null, prop("orientation"))
40
+ })),
37
41
  size: bindable(() => ({
38
- value: prop("size"),
39
- defaultValue: prop("defaultSize"),
42
+ defaultValue: [],
40
43
  isEqual(a, b) {
41
44
  return b != null && fuzzySizeEqual(a, b);
42
45
  },
43
46
  onChange(value) {
44
47
  const ctx = getContext();
45
48
  const refs = getRefs();
49
+ if (refs.get("suppressOnResize")) return;
46
50
  const sizesBeforeCollapse = refs.get("panelSizeBeforeCollapse");
47
51
  const expandToSizes = Object.fromEntries(sizesBeforeCollapse.entries());
48
52
  const resizeTriggerId = ctx.get("dragState")?.resizeTriggerId ?? null;
@@ -64,15 +68,27 @@ var machine = createMachine({
64
68
  };
65
69
  },
66
70
  watch({ track, action, prop }) {
67
- track([() => serializePanels(prop("panels"))], () => {
68
- action(["syncSize"]);
69
- });
71
+ track(
72
+ [
73
+ () => serializePanels(prop("panels")),
74
+ () => JSON.stringify(prop("size") ?? []),
75
+ () => JSON.stringify(prop("defaultSize") ?? [])
76
+ ],
77
+ () => {
78
+ action(["syncSize"]);
79
+ }
80
+ );
70
81
  },
71
82
  refs() {
72
83
  return {
73
84
  panelSizeBeforeCollapse: /* @__PURE__ */ new Map(),
74
85
  prevDelta: 0,
75
- panelIdToLastNotifiedSizeMap: /* @__PURE__ */ new Map()
86
+ panelIdToLastNotifiedSizeMap: /* @__PURE__ */ new Map(),
87
+ initialSize: null,
88
+ prevInitialLayout: null,
89
+ prevGroupSize: null,
90
+ lastRequestedSize: null,
91
+ suppressOnResize: false
76
92
  };
77
93
  },
78
94
  computed: {
@@ -84,6 +100,9 @@ var machine = createMachine({
84
100
  "SIZE.SET": {
85
101
  actions: ["setSize"]
86
102
  },
103
+ "SIZE.RESET": {
104
+ actions: ["resetSize"]
105
+ },
87
106
  "PANEL.COLLAPSE": {
88
107
  actions: ["collapsePanel"]
89
108
  },
@@ -92,11 +111,14 @@ var machine = createMachine({
92
111
  },
93
112
  "PANEL.RESIZE": {
94
113
  actions: ["resizePanel"]
114
+ },
115
+ "ROOT.RESIZE": {
116
+ actions: ["syncSize"]
95
117
  }
96
118
  },
97
119
  entry: ["syncSize"],
98
120
  exit: ["clearGlobalCursor"],
99
- effects: ["trackResizeHandles"],
121
+ effects: ["trackResizeHandles", "trackRootResize"],
100
122
  states: {
101
123
  idle: {
102
124
  entry: ["clearDraggingState", "clearKeyboardState"],
@@ -171,14 +193,27 @@ var machine = createMachine({
171
193
  POINTER_MOVE: {
172
194
  actions: ["setPointerValue", "setGlobalCursor"]
173
195
  },
174
- POINTER_UP: {
175
- target: "idle",
176
- actions: ["invokeOnResizeEnd", "clearGlobalCursor"]
177
- }
196
+ POINTER_UP: [
197
+ {
198
+ guard: "isResizeTriggerFocused",
199
+ target: "focused",
200
+ actions: ["invokeOnResizeEnd", "setKeyboardState", "clearDraggingState", "clearGlobalCursor"]
201
+ },
202
+ {
203
+ target: "idle",
204
+ actions: ["invokeOnResizeEnd", "clearGlobalCursor"]
205
+ }
206
+ ]
178
207
  }
179
208
  }
180
209
  },
181
210
  implementations: {
211
+ guards: {
212
+ isResizeTriggerFocused({ context, scope }) {
213
+ const dragState = context.get("dragState");
214
+ return scope.isActiveElement(dom.getResizeTriggerEl(scope, dragState?.resizeTriggerId));
215
+ }
216
+ },
182
217
  effects: {
183
218
  trackResizeHandles: ({ prop, scope, send }) => {
184
219
  const registry = prop("registry");
@@ -211,6 +246,13 @@ var machine = createMachine({
211
246
  observeCleanup?.();
212
247
  };
213
248
  },
249
+ trackRootResize: ({ scope, send }) => {
250
+ const rootEl = dom.getRootEl(scope);
251
+ if (!rootEl) return;
252
+ return resizeObserverBorderBox.observe(rootEl, () => {
253
+ send({ type: "ROOT.RESIZE" });
254
+ });
255
+ },
214
256
  waitForHoverDelay: ({ send }) => {
215
257
  return setRafTimeout(() => {
216
258
  send({ type: "HOVER_DELAY" });
@@ -230,40 +272,85 @@ var machine = createMachine({
230
272
  },
231
273
  actions: {
232
274
  setSize(params) {
233
- const { context, event, prop } = params;
275
+ const { context, event, prop, scope } = params;
234
276
  const unsafeSize = event.size;
235
277
  const prevSize = context.get("size");
236
- const panels = prop("panels");
278
+ const panels = context.get("panels");
237
279
  const safeSize = validateSizes({
238
- size: unsafeSize,
280
+ size: resolvePanelSizes({
281
+ sizes: unsafeSize,
282
+ panels: prop("panels"),
283
+ rootEl: dom.getRootEl(scope),
284
+ orientation: prop("orientation")
285
+ }),
239
286
  panels
240
287
  });
241
288
  if (!isEqual(prevSize, safeSize)) {
242
289
  setSize(params, safeSize);
243
290
  }
244
291
  },
245
- syncSize({ context, prop }) {
246
- const panels = prop("panels");
247
- let prevSize = context.get("size");
248
- let unsafeSize = null;
249
- if (prevSize.length === 0) {
250
- unsafeSize = getUnsafeDefaultSize({
251
- panels,
252
- size: context.initial("size")
253
- });
254
- }
255
- const nextSize = validateSizes({
256
- size: unsafeSize ?? prevSize,
292
+ resetSize(params) {
293
+ const { refs, context, prop, scope } = params;
294
+ const initialSize = refs.get("initialSize");
295
+ const nextSize = initialSize ?? validateSizes({
296
+ size: resolvePanelSizes({
297
+ sizes: prop("size") ?? prop("defaultSize"),
298
+ panels: prop("panels"),
299
+ rootEl: dom.getRootEl(scope),
300
+ orientation: prop("orientation")
301
+ }),
302
+ panels: context.get("panels")
303
+ });
304
+ setSize(params, nextSize);
305
+ },
306
+ syncSize(params) {
307
+ const { context, scope, prop, refs } = params;
308
+ const rootEl = dom.getRootEl(scope);
309
+ if (!rootEl) return;
310
+ const orientation = prop("orientation");
311
+ const nextGroupSize = getGroupSize(rootEl, orientation);
312
+ if (nextGroupSize <= 0) return;
313
+ const panels = normalizePanels(prop("panels"), rootEl, prop("orientation"));
314
+ context.set("panels", panels);
315
+ const sizeSpec = prop("size") ?? prop("defaultSize");
316
+ const initialLayout = `${getPanelLayout(prop("panels"))}:${JSON.stringify(prop("size") ?? [])}:${JSON.stringify(prop("defaultSize") ?? [])}`;
317
+ const prevGroupSize = refs.get("prevGroupSize");
318
+ const currentSize = context.get("size");
319
+ const nextResolvedSize = resolvePanelSizes({
320
+ sizes: sizeSpec,
321
+ panels: prop("panels"),
322
+ rootEl,
323
+ orientation
324
+ });
325
+ const canPreserveLayout = prevGroupSize != null && prevGroupSize !== nextGroupSize && currentSize.length === panels.length;
326
+ const nextSize = canPreserveLayout ? preserveFixedPanelSizes({
327
+ panels,
328
+ prevLayout: currentSize,
329
+ prevGroupSize,
330
+ nextGroupSize
331
+ }) : nextResolvedSize;
332
+ const safeSize = validateSizes({
333
+ size: nextSize,
257
334
  panels
258
335
  });
259
- if (!isEqual(prevSize, nextSize)) {
260
- context.set("size", nextSize);
336
+ if (refs.get("prevInitialLayout") !== initialLayout) {
337
+ refs.set("initialSize", safeSize);
338
+ refs.set("prevInitialLayout", initialLayout);
339
+ }
340
+ const prevSize = context.get("size");
341
+ if (!isEqual(prevSize, safeSize)) {
342
+ refs.set("suppressOnResize", prop("size") != null || prevSize.length === 0);
343
+ context.set("size", safeSize);
344
+ refs.set("suppressOnResize", false);
261
345
  }
346
+ refs.set("prevGroupSize", nextGroupSize);
262
347
  },
263
348
  setDraggingState({ context, event, prop, scope }) {
264
349
  const orientation = prop("orientation");
265
350
  const size = context.get("size");
266
351
  const resizeTriggerId = event.id;
352
+ const resolvedResizeTriggerId = dom.resolveResizeTriggerId(scope, resizeTriggerId);
353
+ if (!resolvedResizeTriggerId) return;
267
354
  const panelGroupEl = dom.getRootEl(scope);
268
355
  if (!panelGroupEl) return;
269
356
  const handleElement = dom.getResizeTriggerEl(scope, resizeTriggerId);
@@ -271,6 +358,7 @@ var machine = createMachine({
271
358
  const initialCursorPosition = orientation === "horizontal" ? event.point.x : event.point.y;
272
359
  context.set("dragState", {
273
360
  resizeTriggerId: event.id,
361
+ resolvedResizeTriggerId,
274
362
  resizeTriggerRect: handleElement.getBoundingClientRect(),
275
363
  initialCursorPosition,
276
364
  initialSize: size
@@ -279,23 +367,26 @@ var machine = createMachine({
279
367
  clearDraggingState({ context }) {
280
368
  context.set("dragState", null);
281
369
  },
282
- setKeyboardState({ context, event }) {
370
+ setKeyboardState({ context, event, scope }) {
371
+ const id = event.id ?? context.get("dragState")?.resizeTriggerId;
372
+ if (id == null) return;
283
373
  context.set("keyboardState", {
284
- resizeTriggerId: event.id
374
+ resizeTriggerId: id,
375
+ resolvedResizeTriggerId: dom.resolveResizeTriggerId(scope, id)
285
376
  });
286
377
  },
287
378
  clearKeyboardState({ context }) {
288
379
  context.set("keyboardState", null);
289
380
  },
290
381
  collapsePanel(params) {
291
- const { context, prop, event, refs } = params;
382
+ const { context, event, refs } = params;
292
383
  const prevSize = context.get("size");
293
- const panels = prop("panels");
384
+ const panels = context.get("panels");
294
385
  const panel = panels.find((panel2) => panel2.id === event.id);
295
386
  ensure(panel, () => `Panel data not found for id "${event.id}"`);
296
387
  if (panel.collapsible) {
297
388
  const { collapsedSize = 0, panelSize, pivotIndices } = panelDataHelper(panels, panel, prevSize);
298
- ensure(panelSize, () => `Panel size not found for panel "${panel.id}"`);
389
+ ensure(panelSize != null, () => `Panel size not found for panel "${panel.id}"`);
299
390
  if (!fuzzyNumbersEqual(panelSize, collapsedSize)) {
300
391
  refs.get("panelSizeBeforeCollapse").set(panel.id, panelSize);
301
392
  const isLastPanel = findPanelDataIndex(panels, panel) === panels.length - 1;
@@ -315,8 +406,8 @@ var machine = createMachine({
315
406
  }
316
407
  },
317
408
  expandPanel(params) {
318
- const { context, prop, event, refs } = params;
319
- const panels = prop("panels");
409
+ const { context, event, refs } = params;
410
+ const panels = context.get("panels");
320
411
  const prevSize = context.get("size");
321
412
  const panel = panels.find((panel2) => panel2.id === event.id);
322
413
  ensure(panel, () => `Panel data not found for id "${event.id}"`);
@@ -348,13 +439,13 @@ var machine = createMachine({
348
439
  }
349
440
  },
350
441
  resizePanel(params) {
351
- const { context, prop, event } = params;
442
+ const { context, event } = params;
352
443
  const prevSize = context.get("size");
353
- const panels = prop("panels");
444
+ const panels = context.get("panels");
354
445
  const panel = getPanelById(panels, event.id);
355
446
  const unsafePanelSize = event.size;
356
447
  const { panelSize, pivotIndices } = panelDataHelper(panels, panel, prevSize);
357
- ensure(panelSize, () => `Panel size not found for panel "${panel.id}"`);
448
+ ensure(panelSize != null, () => `Panel size not found for panel "${panel.id}"`);
358
449
  const isLastPanel = findPanelDataIndex(panels, panel) === panels.length - 1;
359
450
  const delta = isLastPanel ? panelSize - unsafePanelSize : unsafePanelSize - panelSize;
360
451
  const nextSize = resizeByDelta({
@@ -373,11 +464,11 @@ var machine = createMachine({
373
464
  const { context, event, prop, scope } = params;
374
465
  const dragState = context.get("dragState");
375
466
  if (!dragState) return;
376
- const { resizeTriggerId, initialSize, initialCursorPosition } = dragState;
377
- const panels = prop("panels");
467
+ const { resolvedResizeTriggerId, initialSize, initialCursorPosition } = dragState;
468
+ const panels = context.get("panels");
378
469
  const panelGroupElement = dom.getRootEl(scope);
379
470
  ensure(panelGroupElement, () => `Panel group element not found`);
380
- const pivotIndices = resizeTriggerId.split(":").map((id) => panels.findIndex((panel) => panel.id === id));
471
+ const pivotIndices = resolvedResizeTriggerId.split(":").map((id) => panels.findIndex((panel) => panel.id === id));
381
472
  const horizontal = prop("orientation") === "horizontal";
382
473
  const cursorPosition = horizontal ? event.point.x : event.point.y;
383
474
  const groupRect = panelGroupElement.getBoundingClientRect();
@@ -398,9 +489,10 @@ var machine = createMachine({
398
489
  }
399
490
  },
400
491
  setKeyboardValue(params) {
401
- const { context, event, prop } = params;
402
- const panelDataArray = prop("panels");
403
- const resizeTriggerId = event.id;
492
+ const { context, event } = params;
493
+ const panelDataArray = context.get("panels");
494
+ const resizeTriggerId = dom.resolveResizeTriggerId(params.scope, event.id);
495
+ if (!resizeTriggerId) return;
404
496
  const delta = event.delta;
405
497
  const pivotIndices = resizeTriggerId.split(":").map((id) => panelDataArray.findIndex((panelData) => panelData.id === id));
406
498
  const prevSize = context.get("size");
@@ -416,11 +508,11 @@ var machine = createMachine({
416
508
  setSize(params, nextSize);
417
509
  }
418
510
  },
419
- invokeOnResizeEnd({ context, prop }) {
511
+ invokeOnResizeEnd({ context, prop, refs }) {
420
512
  queueMicrotask(() => {
421
513
  const dragState = context.get("dragState");
422
514
  prop("onResizeEnd")?.({
423
- size: context.get("size"),
515
+ size: refs.get("lastRequestedSize") ?? context.get("size"),
424
516
  resizeTriggerId: dragState?.resizeTriggerId ?? null
425
517
  });
426
518
  });
@@ -431,10 +523,10 @@ var machine = createMachine({
431
523
  });
432
524
  },
433
525
  collapseOrExpandPanel(params) {
434
- const { context, prop } = params;
435
- const panelDataArray = prop("panels");
526
+ const { context, refs } = params;
527
+ const panelDataArray = context.get("panels");
436
528
  const sizes = context.get("size");
437
- const resizeTriggerId = context.get("keyboardState")?.resizeTriggerId;
529
+ const resizeTriggerId = context.get("keyboardState")?.resolvedResizeTriggerId;
438
530
  const [idBefore, idAfter] = resizeTriggerId?.split(":") ?? [];
439
531
  const index = panelDataArray.findIndex((panelData2) => panelData2.id === idBefore);
440
532
  if (index === -1) return;
@@ -448,7 +540,7 @@ var machine = createMachine({
448
540
  );
449
541
  const nextSize = resizeByDelta({
450
542
  delta: fuzzyNumbersEqual(size, collapsedSize) ? minSize - collapsedSize : collapsedSize - size,
451
- initialSize: context.initial("size"),
543
+ initialSize: refs.get("initialSize") ?? sizes,
452
544
  panels: panelDataArray,
453
545
  pivotIndices,
454
546
  prevSize: sizes,
@@ -459,18 +551,19 @@ var machine = createMachine({
459
551
  }
460
552
  }
461
553
  },
462
- setGlobalCursor({ context, scope, prop }) {
554
+ setGlobalCursor(params) {
555
+ const { context, scope, prop } = params;
463
556
  const registry = prop("registry");
464
557
  if (registry) return;
465
558
  const dragState = context.get("dragState");
466
559
  if (!dragState) return;
467
- const panels = prop("panels");
560
+ const panels = context.get("panels");
468
561
  const horizontal = prop("orientation") === "horizontal";
469
- const [idBefore] = dragState.resizeTriggerId.split(":");
562
+ const [idBefore] = dragState.resolvedResizeTriggerId.split(":");
470
563
  const indexBefore = panels.findIndex((panel2) => panel2.id === idBefore);
471
564
  const panel = panels[indexBefore];
472
565
  const size = context.get("size");
473
- const aria = getAriaValue(size, panels, dragState.resizeTriggerId);
566
+ const aria = getAriaValue(size, panels, dragState.resolvedResizeTriggerId);
474
567
  const isAtMin = fuzzyNumbersEqual(aria.valueNow, aria.valueMin) || fuzzyNumbersEqual(aria.valueNow, panel.collapsedSize);
475
568
  const isAtMax = fuzzyNumbersEqual(aria.valueNow, aria.valueMax);
476
569
  const cursorState = { isAtMin, isAtMax };
@@ -490,21 +583,36 @@ var machine = createMachine({
490
583
  });
491
584
  function setSize(params, sizes) {
492
585
  const { refs, prop, context } = params;
493
- const panelsArray = prop("panels");
586
+ const panelsArray = context.get("panels");
494
587
  const onCollapse = prop("onCollapse");
495
588
  const onExpand = prop("onExpand");
589
+ const onResize = prop("onResize");
496
590
  const onResizeStart = prop("onResizeStart");
497
591
  const onResizeEnd = prop("onResizeEnd");
498
592
  const panelIdToLastNotifiedSizeMap = refs.get("panelIdToLastNotifiedSizeMap");
499
593
  const dragState = context.get("dragState");
500
594
  const keyboardState = context.get("keyboardState");
501
595
  const isProgrammatic = dragState === null && keyboardState === null;
596
+ refs.set("lastRequestedSize", sizes);
502
597
  if (isProgrammatic && onResizeStart) {
503
598
  queueMicrotask(() => {
504
599
  onResizeStart();
505
600
  });
506
601
  }
507
- context.set("size", sizes);
602
+ if (prop("size") == null) {
603
+ context.set("size", sizes);
604
+ } else if (onResize) {
605
+ const sizesBeforeCollapse = refs.get("panelSizeBeforeCollapse");
606
+ const expandToSizes = Object.fromEntries(sizesBeforeCollapse.entries());
607
+ const resizeTriggerId = dragState?.resizeTriggerId ?? null;
608
+ const layout = getPanelLayout(prop("panels"));
609
+ onResize({
610
+ size: sizes,
611
+ layout,
612
+ resizeTriggerId,
613
+ expandToSizes
614
+ });
615
+ }
508
616
  sizes.forEach((size, index) => {
509
617
  const panelData = panelsArray[index];
510
618
  ensure(panelData, () => `Panel data not found for index ${index}`);
@@ -512,11 +620,11 @@ function setSize(params, sizes) {
512
620
  const lastNotifiedSize = panelIdToLastNotifiedSizeMap.get(panelId);
513
621
  if (lastNotifiedSize == null || size !== lastNotifiedSize) {
514
622
  panelIdToLastNotifiedSizeMap.set(panelId, size);
515
- if (collapsible && (onCollapse || onExpand)) {
516
- if ((lastNotifiedSize == null || fuzzyNumbersEqual(lastNotifiedSize, collapsedSize)) && !fuzzyNumbersEqual(size, collapsedSize)) {
623
+ if (collapsible && lastNotifiedSize != null && (onCollapse || onExpand)) {
624
+ if (fuzzyNumbersEqual(lastNotifiedSize, collapsedSize) && !fuzzyNumbersEqual(size, collapsedSize)) {
517
625
  onExpand?.({ panelId, size });
518
626
  }
519
- if (onCollapse && (lastNotifiedSize == null || !fuzzyNumbersEqual(lastNotifiedSize, collapsedSize)) && fuzzyNumbersEqual(size, collapsedSize)) {
627
+ if (!fuzzyNumbersEqual(lastNotifiedSize, collapsedSize) && fuzzyNumbersEqual(size, collapsedSize)) {
520
628
  onCollapse?.({ panelId, size });
521
629
  }
522
630
  }
@@ -4,7 +4,9 @@ import { SplitterRegistry } from './utils/registry.mjs';
4
4
 
5
5
  type ResizeEvent = PointerEvent | KeyboardEvent;
6
6
  type PanelId = string;
7
- type ResizeTriggerId = `${PanelId}:${PanelId}`;
7
+ type ResizeTriggerId = `${PanelId}:${PanelId}` | `${PanelId}:` | `:${PanelId}`;
8
+ type PanelSize = number | string;
9
+ type PanelResizeBehavior = "preserve-relative-size" | "preserve-pixel-size";
8
10
  interface PanelData {
9
11
  /**
10
12
  * The id of the panel.
@@ -17,20 +19,29 @@ interface PanelData {
17
19
  /**
18
20
  * The minimum size of the panel.
19
21
  */
20
- minSize?: number | undefined;
22
+ minSize?: PanelSize | undefined;
21
23
  /**
22
24
  * The maximum size of the panel.
23
25
  */
24
- maxSize?: number | undefined;
26
+ maxSize?: PanelSize | undefined;
25
27
  /**
26
28
  * Whether the panel is collapsible.
27
29
  */
28
30
  collapsible?: boolean | undefined;
31
+ /**
32
+ * How the panel should behave when the parent group is resized.
33
+ */
34
+ resizeBehavior?: PanelResizeBehavior | undefined;
29
35
  /**
30
36
  * The size of the panel when collapsed.
31
37
  */
32
- collapsedSize?: number | undefined;
38
+ collapsedSize?: PanelSize | undefined;
33
39
  }
40
+ type NormalizedPanelData = Omit<PanelData, "minSize" | "maxSize" | "collapsedSize"> & {
41
+ minSize?: number | undefined;
42
+ maxSize?: number | undefined;
43
+ collapsedSize?: number | undefined;
44
+ };
34
45
  interface CursorState {
35
46
  isAtMin: boolean;
36
47
  isAtMax: boolean;
@@ -64,12 +75,12 @@ interface SplitterProps extends DirectionProperty, CommonProperties {
64
75
  /**
65
76
  * The controlled size data of the panels
66
77
  */
67
- size?: number[] | undefined;
78
+ size?: PanelSize[] | undefined;
68
79
  /**
69
80
  * The initial size of the panels when rendered.
70
81
  * Use when you don't need to control the size of the panels.
71
82
  */
72
- defaultSize?: number[] | undefined;
83
+ defaultSize?: PanelSize[] | undefined;
73
84
  /**
74
85
  * The size constraints of the panels.
75
86
  */
@@ -115,22 +126,30 @@ interface SplitterProps extends DirectionProperty, CommonProperties {
115
126
  type PropsWithDefault = "orientation" | "panels";
116
127
  interface DragState {
117
128
  resizeTriggerId: string;
129
+ resolvedResizeTriggerId: `${PanelId}:${PanelId}`;
118
130
  resizeTriggerRect: DOMRect;
119
131
  initialCursorPosition: number;
120
132
  initialSize: number[];
121
133
  }
122
134
  interface KeyboardState {
123
135
  resizeTriggerId: string;
136
+ resolvedResizeTriggerId: `${PanelId}:${PanelId}` | null;
124
137
  }
125
138
  interface Context {
126
139
  dragState: DragState | null;
127
140
  keyboardState: KeyboardState | null;
128
141
  size: number[];
142
+ panels: NormalizedPanelData[];
129
143
  }
130
144
  interface Refs {
131
145
  panelSizeBeforeCollapse: Map<string, number>;
132
146
  panelIdToLastNotifiedSizeMap: Map<string, number>;
133
147
  prevDelta: number;
148
+ initialSize: number[] | null;
149
+ prevInitialLayout: string | null;
150
+ prevGroupSize: number | null;
151
+ lastRequestedSize: number[] | null;
152
+ suppressOnResize: boolean;
134
153
  }
135
154
  interface SplitterSchema {
136
155
  state: "idle" | "hover:temp" | "hover" | "dragging" | "focused";
@@ -185,7 +204,7 @@ interface SplitterApi<T extends PropTypes = PropTypes> {
185
204
  /**
186
205
  * Sets the sizes of the panels.
187
206
  */
188
- setSizes: (size: number[]) => void;
207
+ setSizes: (size: PanelSize[]) => void;
189
208
  /**
190
209
  * Returns the items of the splitter.
191
210
  */
@@ -240,4 +259,4 @@ interface SplitterApi<T extends PropTypes = PropTypes> {
240
259
  getResizeTriggerIndicator: (props: ResizeTriggerProps) => T["element"];
241
260
  }
242
261
 
243
- export type { CursorState, DragState, ElementIds, ExpandCollapseDetails, KeyboardState, PanelData, PanelId, PanelItem, PanelProps, ResizeDetails, ResizeEndDetails, ResizeEvent, ResizeTriggerId, ResizeTriggerItem, ResizeTriggerProps, ResizeTriggerState, SplitterApi, SplitterItem, SplitterMachine, SplitterProps, SplitterSchema, SplitterService };
262
+ export type { CursorState, DragState, ElementIds, ExpandCollapseDetails, KeyboardState, NormalizedPanelData, PanelData, PanelId, PanelItem, PanelProps, PanelResizeBehavior, PanelSize, ResizeDetails, ResizeEndDetails, ResizeEvent, ResizeTriggerId, ResizeTriggerItem, ResizeTriggerProps, ResizeTriggerState, SplitterApi, SplitterItem, SplitterMachine, SplitterProps, SplitterSchema, SplitterService };
@@ -4,7 +4,9 @@ import { SplitterRegistry } from './utils/registry.js';
4
4
 
5
5
  type ResizeEvent = PointerEvent | KeyboardEvent;
6
6
  type PanelId = string;
7
- type ResizeTriggerId = `${PanelId}:${PanelId}`;
7
+ type ResizeTriggerId = `${PanelId}:${PanelId}` | `${PanelId}:` | `:${PanelId}`;
8
+ type PanelSize = number | string;
9
+ type PanelResizeBehavior = "preserve-relative-size" | "preserve-pixel-size";
8
10
  interface PanelData {
9
11
  /**
10
12
  * The id of the panel.
@@ -17,20 +19,29 @@ interface PanelData {
17
19
  /**
18
20
  * The minimum size of the panel.
19
21
  */
20
- minSize?: number | undefined;
22
+ minSize?: PanelSize | undefined;
21
23
  /**
22
24
  * The maximum size of the panel.
23
25
  */
24
- maxSize?: number | undefined;
26
+ maxSize?: PanelSize | undefined;
25
27
  /**
26
28
  * Whether the panel is collapsible.
27
29
  */
28
30
  collapsible?: boolean | undefined;
31
+ /**
32
+ * How the panel should behave when the parent group is resized.
33
+ */
34
+ resizeBehavior?: PanelResizeBehavior | undefined;
29
35
  /**
30
36
  * The size of the panel when collapsed.
31
37
  */
32
- collapsedSize?: number | undefined;
38
+ collapsedSize?: PanelSize | undefined;
33
39
  }
40
+ type NormalizedPanelData = Omit<PanelData, "minSize" | "maxSize" | "collapsedSize"> & {
41
+ minSize?: number | undefined;
42
+ maxSize?: number | undefined;
43
+ collapsedSize?: number | undefined;
44
+ };
34
45
  interface CursorState {
35
46
  isAtMin: boolean;
36
47
  isAtMax: boolean;
@@ -64,12 +75,12 @@ interface SplitterProps extends DirectionProperty, CommonProperties {
64
75
  /**
65
76
  * The controlled size data of the panels
66
77
  */
67
- size?: number[] | undefined;
78
+ size?: PanelSize[] | undefined;
68
79
  /**
69
80
  * The initial size of the panels when rendered.
70
81
  * Use when you don't need to control the size of the panels.
71
82
  */
72
- defaultSize?: number[] | undefined;
83
+ defaultSize?: PanelSize[] | undefined;
73
84
  /**
74
85
  * The size constraints of the panels.
75
86
  */
@@ -115,22 +126,30 @@ interface SplitterProps extends DirectionProperty, CommonProperties {
115
126
  type PropsWithDefault = "orientation" | "panels";
116
127
  interface DragState {
117
128
  resizeTriggerId: string;
129
+ resolvedResizeTriggerId: `${PanelId}:${PanelId}`;
118
130
  resizeTriggerRect: DOMRect;
119
131
  initialCursorPosition: number;
120
132
  initialSize: number[];
121
133
  }
122
134
  interface KeyboardState {
123
135
  resizeTriggerId: string;
136
+ resolvedResizeTriggerId: `${PanelId}:${PanelId}` | null;
124
137
  }
125
138
  interface Context {
126
139
  dragState: DragState | null;
127
140
  keyboardState: KeyboardState | null;
128
141
  size: number[];
142
+ panels: NormalizedPanelData[];
129
143
  }
130
144
  interface Refs {
131
145
  panelSizeBeforeCollapse: Map<string, number>;
132
146
  panelIdToLastNotifiedSizeMap: Map<string, number>;
133
147
  prevDelta: number;
148
+ initialSize: number[] | null;
149
+ prevInitialLayout: string | null;
150
+ prevGroupSize: number | null;
151
+ lastRequestedSize: number[] | null;
152
+ suppressOnResize: boolean;
134
153
  }
135
154
  interface SplitterSchema {
136
155
  state: "idle" | "hover:temp" | "hover" | "dragging" | "focused";
@@ -185,7 +204,7 @@ interface SplitterApi<T extends PropTypes = PropTypes> {
185
204
  /**
186
205
  * Sets the sizes of the panels.
187
206
  */
188
- setSizes: (size: number[]) => void;
207
+ setSizes: (size: PanelSize[]) => void;
189
208
  /**
190
209
  * Returns the items of the splitter.
191
210
  */
@@ -240,4 +259,4 @@ interface SplitterApi<T extends PropTypes = PropTypes> {
240
259
  getResizeTriggerIndicator: (props: ResizeTriggerProps) => T["element"];
241
260
  }
242
261
 
243
- export type { CursorState, DragState, ElementIds, ExpandCollapseDetails, KeyboardState, PanelData, PanelId, PanelItem, PanelProps, ResizeDetails, ResizeEndDetails, ResizeEvent, ResizeTriggerId, ResizeTriggerItem, ResizeTriggerProps, ResizeTriggerState, SplitterApi, SplitterItem, SplitterMachine, SplitterProps, SplitterSchema, SplitterService };
262
+ export type { CursorState, DragState, ElementIds, ExpandCollapseDetails, KeyboardState, NormalizedPanelData, PanelData, PanelId, PanelItem, PanelProps, PanelResizeBehavior, PanelSize, ResizeDetails, ResizeEndDetails, ResizeEvent, ResizeTriggerId, ResizeTriggerItem, ResizeTriggerProps, ResizeTriggerState, SplitterApi, SplitterItem, SplitterMachine, SplitterProps, SplitterSchema, SplitterService };