@zag-js/splitter 1.4.1 → 1.5.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/dist/index.js CHANGED
@@ -21,17 +21,17 @@ var getCursor = (state, x) => {
21
21
  if (state.isAtMax) cursor = x ? "w-resize" : "n-resize";
22
22
  return cursor;
23
23
  };
24
- var getPanelStyle = (panels, id) => {
25
- const flexGrow = panels.find((panel) => panel.id === id)?.size ?? "0";
26
- return { flexBasis: 0, flexGrow, flexShrink: 1, overflow: "hidden" };
24
+ var getResizeTriggerEls = (ctx) => {
25
+ return domQuery.queryAll(getRootEl(ctx), `[role=separator][data-ownedby='${CSS.escape(getRootId(ctx))}']`);
27
26
  };
28
- var setupGlobalCursor = (ctx, state, x) => {
27
+ var setupGlobalCursor = (ctx, state, x, nonce) => {
29
28
  const styleEl = ctx.getById(getGlobalCursorId(ctx));
30
29
  const textContent = `* { cursor: ${getCursor(state, x)} !important; }`;
31
30
  if (styleEl) {
32
31
  styleEl.textContent = textContent;
33
32
  } else {
34
33
  const style = ctx.getDoc().createElement("style");
34
+ if (nonce) style.nonce = nonce;
35
35
  style.id = getGlobalCursorId(ctx);
36
36
  style.textContent = textContent;
37
37
  ctx.getDoc().head.appendChild(style);
@@ -41,170 +41,255 @@ var removeGlobalCursor = (ctx) => {
41
41
  const styleEl = ctx.getById(getGlobalCursorId(ctx));
42
42
  styleEl?.remove();
43
43
  };
44
-
45
- // src/splitter.utils.ts
46
- function validateSize(key, size) {
47
- if (Math.floor(size) > 100) {
48
- throw new Error(`Total ${key} of panels cannot be greater than 100`);
49
- }
50
- }
51
- function getNormalizedPanels(sizes) {
52
- let numOfPanelsWithoutSize = 0;
53
- let totalSize = 0;
44
+ function calculateAriaValues({
45
+ size,
46
+ panels,
47
+ pivotIndices
48
+ }) {
49
+ let currentMinSize = 0;
50
+ let currentMaxSize = 100;
54
51
  let totalMinSize = 0;
55
- const panels = sizes.map((panel) => {
56
- const minSize = panel.minSize ?? 0;
57
- const maxSize = panel.maxSize ?? 100;
58
- totalMinSize += minSize;
59
- if (panel.size == null) {
60
- numOfPanelsWithoutSize++;
52
+ let totalMaxSize = 0;
53
+ const firstIndex = pivotIndices[0];
54
+ utils.ensure(firstIndex, "No pivot index found");
55
+ panels.forEach((panel, index) => {
56
+ const { maxSize = 100, minSize = 0 } = panel;
57
+ if (index === firstIndex) {
58
+ currentMinSize = minSize;
59
+ currentMaxSize = maxSize;
61
60
  } else {
62
- totalSize += panel.size;
61
+ totalMinSize += minSize;
62
+ totalMaxSize += maxSize;
63
63
  }
64
- return {
65
- ...panel,
66
- minSize,
67
- maxSize
68
- };
69
64
  });
70
- validateSize("minSize", totalMinSize);
71
- validateSize("size", totalSize);
72
- let end = 0;
73
- let remainingSize = 0;
74
- const result = panels.map((panel) => {
75
- let start = end;
76
- if (panel.size != null) {
77
- end += panel.size;
78
- remainingSize = panel.size - panel.minSize;
79
- return {
80
- ...panel,
81
- start,
82
- end,
83
- remainingSize
84
- };
85
- }
86
- const size = (100 - totalSize) / numOfPanelsWithoutSize;
87
- end += size;
88
- remainingSize = size - panel.minSize;
89
- return { ...panel, size, start, end, remainingSize };
90
- });
91
- return result;
65
+ const valueMax = Math.min(currentMaxSize, 100 - totalMinSize);
66
+ const valueMin = Math.max(currentMinSize, 100 - totalMaxSize);
67
+ const valueNow = size[firstIndex];
68
+ return {
69
+ valueMax,
70
+ valueMin,
71
+ valueNow
72
+ };
92
73
  }
93
- function getHandlePanels(panels, id) {
94
- const [beforeId, afterId] = id?.split(":") ?? [];
95
- if (!beforeId || !afterId) return;
74
+ function getAriaValue(size, panels, handleId) {
75
+ const [beforeId, afterId] = handleId.split(":");
96
76
  const beforeIndex = panels.findIndex((panel) => panel.id === beforeId);
97
77
  const afterIndex = panels.findIndex((panel) => panel.id === afterId);
98
- if (beforeIndex === -1 || afterIndex === -1) return;
99
- const before = panels[beforeIndex];
100
- const after = panels[afterIndex];
78
+ const { valueMax, valueMin, valueNow } = calculateAriaValues({
79
+ size,
80
+ panels,
81
+ pivotIndices: [beforeIndex, afterIndex]
82
+ });
101
83
  return {
102
- before: {
103
- ...before,
104
- index: beforeIndex
105
- },
106
- after: {
107
- ...after,
108
- index: afterIndex
109
- }
84
+ beforeId,
85
+ afterId,
86
+ valueMax: Math.round(valueMax),
87
+ valueMin: Math.round(valueMin),
88
+ valueNow: valueNow != null ? Math.round(valueNow) : void 0
110
89
  };
111
90
  }
112
- function getHandleBounds(panels, id) {
113
- const handlePanels = getHandlePanels(panels, id);
114
- if (!handlePanels) return;
115
- const { before, after } = handlePanels;
116
- return {
117
- min: Math.max(before.start + before.minSize, after.end - after.maxSize),
118
- max: Math.min(after.end - after.minSize, before.maxSize + before.start)
119
- };
91
+
92
+ // src/utils/fuzzy.ts
93
+ var PRECISION = 10;
94
+ function fuzzyCompareNumbers(actual, expected, fractionDigits = PRECISION) {
95
+ if (actual.toFixed(fractionDigits) === expected.toFixed(fractionDigits)) {
96
+ return 0;
97
+ } else {
98
+ return actual > expected ? 1 : -1;
99
+ }
120
100
  }
121
- function getPanelBounds(panels, id) {
122
- const bounds = getHandleBounds(panels, id);
123
- const handlePanels = getHandlePanels(panels, id);
124
- if (!bounds || !handlePanels) return;
125
- const { before, after } = handlePanels;
126
- const beforeMin = Math.abs(before.start - bounds.min);
127
- const afterMin = after.size + (before.size - beforeMin);
128
- const beforeMax = Math.abs(before.start - bounds.max);
129
- const afterMax = after.size - (beforeMax - before.size);
130
- return {
131
- before: {
132
- index: before.index,
133
- min: beforeMin,
134
- max: beforeMax,
135
- isAtMin: beforeMin === before.size,
136
- isAtMax: beforeMax === before.size,
137
- up(step) {
138
- return Math.min(before.size + step, beforeMax);
139
- },
140
- down(step) {
141
- return Math.max(before.size - step, beforeMin);
142
- }
143
- },
144
- after: {
145
- index: after.index,
146
- min: afterMin,
147
- max: afterMax,
148
- isAtMin: afterMin === after.size,
149
- isAtMax: afterMax === after.size,
150
- up(step) {
151
- return Math.min(after.size + step, afterMin);
152
- },
153
- down(step) {
154
- return Math.max(after.size - step, afterMax);
155
- }
101
+ function fuzzyNumbersEqual(actual, expected, fractionDigits = PRECISION) {
102
+ if (actual == null || expected == null) return false;
103
+ return fuzzyCompareNumbers(actual, expected, fractionDigits) === 0;
104
+ }
105
+ function fuzzySizeEqual(actual, expected, fractionDigits) {
106
+ if (actual.length !== expected.length) {
107
+ return false;
108
+ }
109
+ for (let index = 0; index < actual.length; index++) {
110
+ const actualSize = actual[index];
111
+ const expectedSize = expected[index];
112
+ if (!fuzzyNumbersEqual(actualSize, expectedSize, fractionDigits)) {
113
+ return false;
156
114
  }
115
+ }
116
+ return true;
117
+ }
118
+ function getPanelById(panels, id) {
119
+ const panel = panels.find((panel2) => panel2.id === id);
120
+ utils.ensure(panel, `Panel data not found for id "${id}"`);
121
+ return panel;
122
+ }
123
+ function findPanelDataIndex(panels, panel) {
124
+ return panels.findIndex((prevPanel) => prevPanel === panel || prevPanel.id === panel.id);
125
+ }
126
+ function findPanelIndex(panels, id) {
127
+ return panels.findIndex((panel) => panel.id === id);
128
+ }
129
+ function panelDataHelper(panels, panel, sizes) {
130
+ const index = findPanelIndex(panels, panel.id);
131
+ const pivotIndices = index === panels.length - 1 ? [index - 1, index] : [index, index + 1];
132
+ const panelSize = sizes[index];
133
+ return { ...panel, panelSize, pivotIndices };
134
+ }
135
+ function sortPanels(panels) {
136
+ return panels.sort((panelA, panelB) => {
137
+ const orderA = panelA.order;
138
+ const orderB = panelB.order;
139
+ if (orderA == null && orderB == null) {
140
+ return 0;
141
+ } else if (orderA == null) {
142
+ return -1;
143
+ } else if (orderB == null) {
144
+ return 1;
145
+ } else {
146
+ return orderA - orderB;
147
+ }
148
+ });
149
+ }
150
+ function getPanelLayout(panels) {
151
+ return panels.map((panel) => panel.id).sort().join(":");
152
+ }
153
+ function serializePanels(panels) {
154
+ const keys = panels.map((panel) => panel.id);
155
+ const sortedKeys = keys.sort();
156
+ const serialized = sortedKeys.map((key) => {
157
+ const panel = panels.find((panel2) => panel2.id === key);
158
+ return JSON.stringify(panel);
159
+ });
160
+ return serialized.join(",");
161
+ }
162
+ function getPanelFlexBoxStyle({
163
+ defaultSize,
164
+ dragState,
165
+ sizes,
166
+ panels,
167
+ panelIndex,
168
+ precision = 3
169
+ }) {
170
+ const size = sizes[panelIndex];
171
+ let flexGrow;
172
+ if (size == null) {
173
+ flexGrow = defaultSize != void 0 ? defaultSize.toPrecision(precision) : "1";
174
+ } else if (panels.length === 1) {
175
+ flexGrow = "1";
176
+ } else {
177
+ flexGrow = size.toPrecision(precision);
178
+ }
179
+ return {
180
+ flexBasis: 0,
181
+ flexGrow,
182
+ flexShrink: 1,
183
+ // Without this, Panel sizes may be unintentionally overridden by their content
184
+ overflow: "hidden",
185
+ // Disable pointer events inside of a panel during resize
186
+ // This avoid edge cases like nested iframes
187
+ pointerEvents: dragState !== null ? "none" : void 0
157
188
  };
158
189
  }
159
- function clamp(value, min, max) {
160
- return Math.min(Math.max(value, min), max);
190
+ function getUnsafeDefaultSize({ panels, size: sizes }) {
191
+ const finalSizes = Array(panels.length);
192
+ let numPanelsWithSizes = 0;
193
+ let remainingSize = 100;
194
+ for (let index = 0; index < panels.length; index++) {
195
+ const panel = panels[index];
196
+ utils.ensure(panel, `Panel data not found for index ${index}`);
197
+ const defaultSize = sizes[index];
198
+ if (defaultSize != null) {
199
+ numPanelsWithSizes++;
200
+ finalSizes[index] = defaultSize;
201
+ remainingSize -= defaultSize;
202
+ }
203
+ }
204
+ for (let index = 0; index < panels.length; index++) {
205
+ const panel = panels[index];
206
+ utils.ensure(panel, `Panel data not found for index ${index}`);
207
+ const defaultSize = sizes[index];
208
+ if (defaultSize != null) {
209
+ continue;
210
+ }
211
+ const numRemainingPanels = panels.length - numPanelsWithSizes;
212
+ const size = remainingSize / numRemainingPanels;
213
+ numPanelsWithSizes++;
214
+ finalSizes[index] = size;
215
+ remainingSize -= size;
216
+ }
217
+ return finalSizes;
161
218
  }
162
219
 
163
220
  // src/splitter.connect.ts
164
221
  function connect(service, normalize) {
165
222
  const { state, send, prop, computed, context, scope } = service;
166
- const horizontal = computed("isHorizontal");
167
- const focused = state.hasTag("focus");
223
+ const horizontal = computed("horizontal");
168
224
  const dragging = state.matches("dragging");
169
- const panels = computed("panels");
170
- const activeResizeId = context.get("activeResizeId");
171
- function getResizeTriggerState(props2) {
172
- const { id, disabled } = props2;
173
- const ids = id.split(":");
174
- const panelIds = ids.map((id2) => getPanelId(scope, id2));
175
- const panels2 = getHandleBounds(computed("panels"), id);
176
- return {
177
- disabled: !!disabled,
178
- focused: activeResizeId === id && focused,
179
- panelIds,
180
- min: panels2?.min,
181
- max: panels2?.max,
182
- value: 0
183
- };
184
- }
225
+ const getPanelStyle = (id) => {
226
+ const panels = prop("panels");
227
+ const panelIndex = panels.findIndex((panel) => panel.id === id);
228
+ const defaultSize = context.initial("size")[panelIndex];
229
+ const dragState = context.get("dragState");
230
+ return getPanelFlexBoxStyle({
231
+ defaultSize,
232
+ dragState,
233
+ sizes: context.get("size"),
234
+ panels,
235
+ panelIndex
236
+ });
237
+ };
185
238
  return {
186
- focused,
187
239
  dragging,
188
- getResizeTriggerState,
189
- bounds: getHandleBounds(computed("panels"), activeResizeId),
190
- setToMinSize(id) {
191
- const panel = panels.find((panel2) => panel2.id === id);
192
- send({ type: "SIZE.SET", id, size: panel?.minSize, src: "setToMinSize" });
240
+ getItems() {
241
+ return prop("panels").flatMap((panel, index, arr) => {
242
+ const nextPanel = arr[index + 1];
243
+ if (panel && nextPanel) {
244
+ return [
245
+ { type: "panel", id: panel.id },
246
+ { type: "handle", id: `${panel.id}:${nextPanel.id}` }
247
+ ];
248
+ }
249
+ return [{ type: "panel", id: panel.id }];
250
+ });
251
+ },
252
+ getSizes() {
253
+ return context.get("size");
193
254
  },
194
- setToMaxSize(id) {
195
- const panel = panels.find((panel2) => panel2.id === id);
196
- send({ type: "SIZE.SET", id, size: panel?.maxSize, src: "setToMaxSize" });
255
+ setSizes(size) {
256
+ send({ type: "SIZE.SET", size });
197
257
  },
198
- setSize(id, size) {
199
- send({ type: "SIZE.SET", id, size });
258
+ collapsePanel(id) {
259
+ send({ type: "PANEL.COLLAPSE", id });
200
260
  },
201
- setSizes(sizes) {
202
- send({ type: "SIZES.SET", sizes });
261
+ expandPanel(id, minSize) {
262
+ send({ type: "PANEL.EXPAND", id, minSize });
203
263
  },
204
- getSize(id) {
205
- const panel = panels.find((panel2) => panel2.id === id);
206
- utils.ensure(panel, `Panel with id ${id} not found`);
207
- return panel.size;
264
+ resizePanel(id, unsafePanelSize) {
265
+ send({ type: "PANEL.RESIZE", id, size: unsafePanelSize });
266
+ },
267
+ getPanelSize(id) {
268
+ const panels = prop("panels");
269
+ const size = context.get("size");
270
+ const panelData = getPanelById(panels, id);
271
+ const { panelSize } = panelDataHelper(panels, panelData, size);
272
+ utils.ensure(panelSize, `Panel size not found for panel "${panelData.id}"`);
273
+ return panelSize;
274
+ },
275
+ isPanelCollapsed(id) {
276
+ const panels = prop("panels");
277
+ const size = context.get("size");
278
+ const panelData = getPanelById(panels, id);
279
+ const { collapsedSize = 0, collapsible, panelSize } = panelDataHelper(panels, panelData, size);
280
+ utils.ensure(panelSize, `Panel size not found for panel "${panelData.id}"`);
281
+ return collapsible === true && fuzzyNumbersEqual(panelSize, collapsedSize);
282
+ },
283
+ isPanelExpanded(id) {
284
+ const panels = prop("panels");
285
+ const size = context.get("size");
286
+ const panelData = getPanelById(panels, id);
287
+ const { collapsedSize = 0, collapsible, panelSize } = panelDataHelper(panels, panelData, size);
288
+ utils.ensure(panelSize, `Panel size not found for panel "${panelData.id}"`);
289
+ return !collapsible || fuzzyCompareNumbers(panelSize, collapsedSize) > 0;
290
+ },
291
+ getLayout() {
292
+ return getPanelLayout(prop("panels"));
208
293
  },
209
294
  getRootProps() {
210
295
  return normalize.element({
@@ -227,44 +312,51 @@ function connect(service, normalize) {
227
312
  ...parts.panel.attrs,
228
313
  "data-orientation": prop("orientation"),
229
314
  dir: prop("dir"),
315
+ "data-id": id,
316
+ "data-index": findPanelIndex(prop("panels"), id),
230
317
  id: getPanelId(scope, id),
231
318
  "data-ownedby": getRootId(scope),
232
- style: getPanelStyle(panels, id)
319
+ style: getPanelStyle(id)
233
320
  });
234
321
  },
235
322
  getResizeTriggerProps(props2) {
236
- const { id, disabled, step = 1 } = props2;
237
- const triggerState = getResizeTriggerState(props2);
323
+ const { id, disabled } = props2;
324
+ const aria = getAriaValue(context.get("size"), prop("panels"), id);
325
+ const dragging2 = context.get("dragState")?.resizeTriggerId === id;
326
+ const focused = dragging2 || context.get("keyboardState")?.resizeTriggerId === id;
238
327
  return normalize.element({
239
328
  ...parts.resizeTrigger.attrs,
240
329
  dir: prop("dir"),
241
330
  id: getResizeTriggerId(scope, id),
242
331
  role: "separator",
332
+ "data-id": id,
243
333
  "data-ownedby": getRootId(scope),
244
334
  tabIndex: disabled ? void 0 : 0,
245
- "aria-valuenow": triggerState.value,
246
- "aria-valuemin": triggerState.min,
247
- "aria-valuemax": triggerState.max,
335
+ "aria-valuenow": aria.valueNow,
336
+ "aria-valuemin": aria.valueMin,
337
+ "aria-valuemax": aria.valueMax,
248
338
  "data-orientation": prop("orientation"),
249
339
  "aria-orientation": prop("orientation"),
250
- "aria-controls": triggerState.panelIds.join(" "),
251
- "data-focus": domQuery.dataAttr(triggerState.focused),
340
+ "aria-controls": `${getPanelId(scope, aria.beforeId)} ${getPanelId(scope, aria.afterId)}`,
341
+ "data-focus": domQuery.dataAttr(focused),
252
342
  "data-disabled": domQuery.dataAttr(disabled),
253
343
  style: {
254
344
  touchAction: "none",
255
345
  userSelect: "none",
256
346
  WebkitUserSelect: "none",
257
347
  flex: "0 0 auto",
258
- pointerEvents: dragging && !triggerState.focused ? "none" : void 0,
348
+ pointerEvents: dragging2 && !focused ? "none" : void 0,
259
349
  cursor: horizontal ? "col-resize" : "row-resize",
260
350
  [horizontal ? "minHeight" : "minWidth"]: "0"
261
351
  },
262
352
  onPointerDown(event) {
353
+ if (event.button !== 0) return;
263
354
  if (disabled) {
264
355
  event.preventDefault();
265
356
  return;
266
357
  }
267
- send({ type: "POINTER_DOWN", id });
358
+ const point = domQuery.getEventPoint(event);
359
+ send({ type: "POINTER_DOWN", id, point });
268
360
  event.currentTarget.setPointerCapture(event.pointerId);
269
361
  event.preventDefault();
270
362
  event.stopPropagation();
@@ -284,40 +376,49 @@ function connect(service, normalize) {
284
376
  send({ type: "POINTER_LEAVE", id });
285
377
  },
286
378
  onBlur() {
379
+ if (disabled) return;
287
380
  send({ type: "BLUR" });
288
381
  },
289
382
  onFocus() {
290
- send({ type: "FOCUS", id });
291
- },
292
- onDoubleClick() {
293
383
  if (disabled) return;
294
- send({ type: "DOUBLE_CLICK", id });
384
+ send({ type: "FOCUS", id });
295
385
  },
296
386
  onKeyDown(event) {
297
387
  if (event.defaultPrevented) return;
298
388
  if (disabled) return;
299
- const moveStep = domQuery.getEventStep(event) * step;
389
+ const keyboardResizeBy = prop("keyboardResizeBy");
390
+ let delta = 0;
391
+ if (event.shiftKey) {
392
+ delta = 10;
393
+ } else if (keyboardResizeBy != null) {
394
+ delta = keyboardResizeBy;
395
+ } else {
396
+ delta = 1;
397
+ }
300
398
  const keyMap = {
301
399
  Enter() {
302
- send({ type: "ENTER" });
400
+ send({ type: "ENTER", id });
303
401
  },
304
402
  ArrowUp() {
305
- send({ type: "ARROW_UP", step: moveStep });
403
+ send({ type: "KEYBOARD_MOVE", id, delta: horizontal ? 0 : delta });
306
404
  },
307
405
  ArrowDown() {
308
- send({ type: "ARROW_DOWN", step: moveStep });
406
+ send({ type: "KEYBOARD_MOVE", id, delta: horizontal ? 0 : delta });
309
407
  },
310
408
  ArrowLeft() {
311
- send({ type: "ARROW_LEFT", step: moveStep });
409
+ send({ type: "KEYBOARD_MOVE", id, delta: horizontal ? -delta : 0 });
312
410
  },
313
411
  ArrowRight() {
314
- send({ type: "ARROW_RIGHT", step: moveStep });
412
+ send({ type: "KEYBOARD_MOVE", id, delta: horizontal ? delta : 0 });
315
413
  },
316
414
  Home() {
317
- send({ type: "HOME" });
415
+ send({ type: "KEYBOARD_MOVE", id, delta: -100 });
318
416
  },
319
417
  End() {
320
- send({ type: "END" });
418
+ send({ type: "KEYBOARD_MOVE", id, delta: 100 });
419
+ },
420
+ F6() {
421
+ send({ type: "FOCUS.CYCLE", id, shiftKey: event.shiftKey });
321
422
  }
322
423
  };
323
424
  const key = domQuery.getEventKey(event, {
@@ -334,81 +435,295 @@ function connect(service, normalize) {
334
435
  }
335
436
  };
336
437
  }
438
+ function resizePanel({ panels, index, size }) {
439
+ const panel = panels[index];
440
+ utils.ensure(panel, `Panel data not found for index ${index}`);
441
+ let { collapsedSize = 0, collapsible, maxSize = 100, minSize = 0 } = panel;
442
+ if (fuzzyCompareNumbers(size, minSize) < 0) {
443
+ if (collapsible) {
444
+ const halfwayPoint = (collapsedSize + minSize) / 2;
445
+ if (fuzzyCompareNumbers(size, halfwayPoint) < 0) {
446
+ size = collapsedSize;
447
+ } else {
448
+ size = minSize;
449
+ }
450
+ } else {
451
+ size = minSize;
452
+ }
453
+ }
454
+ size = Math.min(maxSize, size);
455
+ size = parseFloat(size.toFixed(PRECISION));
456
+ return size;
457
+ }
458
+
459
+ // src/utils/resize-by-delta.ts
460
+ function resizeByDelta(props2) {
461
+ let { delta, initialSize, panels, pivotIndices, prevSize, trigger } = props2;
462
+ if (fuzzyNumbersEqual(delta, 0)) {
463
+ return initialSize;
464
+ }
465
+ const nextSize = [...initialSize];
466
+ const [firstPivotIndex, secondPivotIndex] = pivotIndices;
467
+ utils.ensure(firstPivotIndex, "Invalid first pivot index");
468
+ utils.ensure(secondPivotIndex, "Invalid second pivot index");
469
+ let deltaApplied = 0;
470
+ {
471
+ if (trigger === "keyboard") {
472
+ {
473
+ const index = delta < 0 ? secondPivotIndex : firstPivotIndex;
474
+ const panel = panels[index];
475
+ utils.ensure(panel, `Panel data not found for index ${index}`);
476
+ const { collapsedSize = 0, collapsible, minSize = 0 } = panel;
477
+ if (collapsible) {
478
+ const prevSize2 = initialSize[index];
479
+ utils.ensure(prevSize2, `Previous size not found for panel index ${index}`);
480
+ if (fuzzyNumbersEqual(prevSize2, collapsedSize)) {
481
+ const localDelta = minSize - prevSize2;
482
+ if (fuzzyCompareNumbers(localDelta, Math.abs(delta)) > 0) {
483
+ delta = delta < 0 ? 0 - localDelta : localDelta;
484
+ }
485
+ }
486
+ }
487
+ }
488
+ {
489
+ const index = delta < 0 ? firstPivotIndex : secondPivotIndex;
490
+ const panel = panels[index];
491
+ utils.ensure(panel, `No panel data found for index ${index}`);
492
+ const { collapsedSize = 0, collapsible, minSize = 0 } = panel;
493
+ if (collapsible) {
494
+ const prevSize2 = initialSize[index];
495
+ utils.ensure(prevSize2, `Previous size not found for panel index ${index}`);
496
+ if (fuzzyNumbersEqual(prevSize2, minSize)) {
497
+ const localDelta = prevSize2 - collapsedSize;
498
+ if (fuzzyCompareNumbers(localDelta, Math.abs(delta)) > 0) {
499
+ delta = delta < 0 ? 0 - localDelta : localDelta;
500
+ }
501
+ }
502
+ }
503
+ }
504
+ }
505
+ }
506
+ {
507
+ const increment = delta < 0 ? 1 : -1;
508
+ let index = delta < 0 ? secondPivotIndex : firstPivotIndex;
509
+ let maxAvailableDelta = 0;
510
+ while (true) {
511
+ const prevSize2 = initialSize[index];
512
+ utils.ensure(prevSize2, `Previous size not found for panel index ${index}`);
513
+ const maxSafeSize = resizePanel({
514
+ panels,
515
+ index,
516
+ size: 100
517
+ });
518
+ const delta2 = maxSafeSize - prevSize2;
519
+ maxAvailableDelta += delta2;
520
+ index += increment;
521
+ if (index < 0 || index >= panels.length) {
522
+ break;
523
+ }
524
+ }
525
+ const minAbsDelta = Math.min(Math.abs(delta), Math.abs(maxAvailableDelta));
526
+ delta = delta < 0 ? 0 - minAbsDelta : minAbsDelta;
527
+ }
528
+ {
529
+ const pivotIndex = delta < 0 ? firstPivotIndex : secondPivotIndex;
530
+ let index = pivotIndex;
531
+ while (index >= 0 && index < panels.length) {
532
+ const deltaRemaining = Math.abs(delta) - Math.abs(deltaApplied);
533
+ const prevSize2 = initialSize[index];
534
+ utils.ensure(prevSize2, `Previous size not found for panel index ${index}`);
535
+ const unsafeSize = prevSize2 - deltaRemaining;
536
+ const safeSize = resizePanel({ panels, index, size: unsafeSize });
537
+ if (!fuzzyNumbersEqual(prevSize2, safeSize)) {
538
+ deltaApplied += prevSize2 - safeSize;
539
+ nextSize[index] = safeSize;
540
+ if (deltaApplied.toPrecision(3).localeCompare(Math.abs(delta).toPrecision(3), void 0, {
541
+ numeric: true
542
+ }) >= 0) {
543
+ break;
544
+ }
545
+ }
546
+ if (delta < 0) {
547
+ index--;
548
+ } else {
549
+ index++;
550
+ }
551
+ }
552
+ }
553
+ if (fuzzySizeEqual(prevSize, nextSize)) {
554
+ return prevSize;
555
+ }
556
+ {
557
+ const pivotIndex = delta < 0 ? secondPivotIndex : firstPivotIndex;
558
+ const prevSize2 = initialSize[pivotIndex];
559
+ utils.ensure(prevSize2, `Previous size not found for panel index ${pivotIndex}`);
560
+ const unsafeSize = prevSize2 + deltaApplied;
561
+ const safeSize = resizePanel({ panels, index: pivotIndex, size: unsafeSize });
562
+ nextSize[pivotIndex] = safeSize;
563
+ if (!fuzzyNumbersEqual(safeSize, unsafeSize)) {
564
+ let deltaRemaining = unsafeSize - safeSize;
565
+ const pivotIndex2 = delta < 0 ? secondPivotIndex : firstPivotIndex;
566
+ let index = pivotIndex2;
567
+ while (index >= 0 && index < panels.length) {
568
+ const prevSize3 = nextSize[index];
569
+ utils.ensure(prevSize3, `Previous size not found for panel index ${index}`);
570
+ const unsafeSize2 = prevSize3 + deltaRemaining;
571
+ const safeSize2 = resizePanel({ panels, index, size: unsafeSize2 });
572
+ if (!fuzzyNumbersEqual(prevSize3, safeSize2)) {
573
+ deltaRemaining -= safeSize2 - prevSize3;
574
+ nextSize[index] = safeSize2;
575
+ }
576
+ if (fuzzyNumbersEqual(deltaRemaining, 0)) {
577
+ break;
578
+ }
579
+ if (delta > 0) {
580
+ index--;
581
+ } else {
582
+ index++;
583
+ }
584
+ }
585
+ }
586
+ }
587
+ const totalSize = nextSize.reduce((total, size) => size + total, 0);
588
+ if (!fuzzyNumbersEqual(totalSize, 100)) {
589
+ return prevSize;
590
+ }
591
+ return nextSize;
592
+ }
593
+ function validateSizes({ size: prevSize, panels }) {
594
+ const nextSize = [...prevSize];
595
+ const nextSizeTotalSize = nextSize.reduce((accumulated, current) => accumulated + current, 0);
596
+ if (nextSize.length !== panels.length) {
597
+ throw Error(`Invalid ${panels.length} panel size: ${nextSize.map((size) => `${size}%`).join(", ")}`);
598
+ } else if (!fuzzyNumbersEqual(nextSizeTotalSize, 100) && nextSize.length > 0) {
599
+ for (let index = 0; index < panels.length; index++) {
600
+ const unsafeSize = nextSize[index];
601
+ utils.ensure(unsafeSize, `No size data found for index ${index}`);
602
+ const safeSize = 100 / nextSizeTotalSize * unsafeSize;
603
+ nextSize[index] = safeSize;
604
+ }
605
+ }
606
+ let remainingSize = 0;
607
+ for (let index = 0; index < panels.length; index++) {
608
+ const unsafeSize = nextSize[index];
609
+ utils.ensure(unsafeSize, `No size data found for index ${index}`);
610
+ const safeSize = resizePanel({ panels, index, size: unsafeSize });
611
+ if (unsafeSize != safeSize) {
612
+ remainingSize += unsafeSize - safeSize;
613
+ nextSize[index] = safeSize;
614
+ }
615
+ }
616
+ if (!fuzzyNumbersEqual(remainingSize, 0)) {
617
+ for (let index = 0; index < panels.length; index++) {
618
+ const prevSize2 = nextSize[index];
619
+ utils.ensure(prevSize2, `No size data found for index ${index}`);
620
+ const unsafeSize = prevSize2 + remainingSize;
621
+ const safeSize = resizePanel({ panels, index, size: unsafeSize });
622
+ if (prevSize2 !== safeSize) {
623
+ remainingSize -= safeSize - prevSize2;
624
+ nextSize[index] = safeSize;
625
+ if (fuzzyNumbersEqual(remainingSize, 0)) {
626
+ break;
627
+ }
628
+ }
629
+ }
630
+ }
631
+ return nextSize;
632
+ }
633
+
634
+ // src/splitter.machine.ts
337
635
  var machine = core.createMachine({
338
636
  props({ props: props2 }) {
637
+ utils.ensureProps(props2, ["panels"]);
339
638
  return {
340
639
  orientation: "horizontal",
341
640
  defaultSize: [],
342
- ...props2
641
+ dir: "ltr",
642
+ ...props2,
643
+ panels: sortPanels(props2.panels)
343
644
  };
344
645
  },
345
646
  initialState() {
346
647
  return "idle";
347
648
  },
348
- context({ prop, bindable, getContext }) {
649
+ context({ prop, bindable, getContext, getRefs }) {
349
650
  return {
350
- activeResizeId: bindable(() => ({
351
- defaultValue: null
352
- })),
353
651
  size: bindable(() => ({
354
- defaultValue: prop("defaultSize"),
355
652
  value: prop("size"),
356
- hash(a) {
357
- return a.map((panel) => `${panel.id}:${panel.size}`).join(",");
653
+ defaultValue: prop("defaultSize"),
654
+ isEqual(a, b) {
655
+ return b != null && fuzzySizeEqual(a, b);
358
656
  },
359
657
  onChange(value) {
360
- const context = getContext();
361
- prop("onSizeChange")?.({
658
+ const ctx = getContext();
659
+ const refs = getRefs();
660
+ const sizesBeforeCollapse = refs.get("panelSizeBeforeCollapse");
661
+ const expandToSizes = Object.fromEntries(sizesBeforeCollapse.entries());
662
+ const resizeTriggerId = ctx.get("dragState")?.resizeTriggerId ?? null;
663
+ const layout = getPanelLayout(prop("panels"));
664
+ prop("onResize")?.({
362
665
  size: value,
363
- activeHandleId: context.get("activeResizeId")
666
+ layout,
667
+ resizeTriggerId,
668
+ expandToSizes
364
669
  });
365
670
  }
366
671
  })),
367
- activeResizeState: bindable(() => ({
368
- defaultValue: { isAtMin: false, isAtMax: false }
672
+ dragState: bindable(() => ({
673
+ defaultValue: null
674
+ })),
675
+ keyboardState: bindable(() => ({
676
+ defaultValue: null
369
677
  }))
370
678
  };
371
679
  },
372
- refs({ context }) {
680
+ watch({ track, action, prop }) {
681
+ track([() => serializePanels(prop("panels"))], () => {
682
+ action(["syncSize"]);
683
+ });
684
+ },
685
+ refs() {
373
686
  return {
374
- previousPanels: getNormalizedPanels(context.get("size"))
687
+ panelSizeBeforeCollapse: /* @__PURE__ */ new Map(),
688
+ prevDelta: 0,
689
+ panelIdToLastNotifiedSizeMap: /* @__PURE__ */ new Map()
375
690
  };
376
691
  },
377
- watch({ track, action, context }) {
378
- track([() => context.hash("size")], () => {
379
- action(["setActiveResizeState"]);
380
- });
381
- },
382
692
  computed: {
383
- isHorizontal: ({ prop }) => prop("orientation") === "horizontal",
384
- panels: ({ context }) => getNormalizedPanels(context.get("size"))
693
+ horizontal({ prop }) {
694
+ return prop("orientation") === "horizontal";
695
+ }
385
696
  },
386
697
  on: {
387
698
  "SIZE.SET": {
388
- actions: ["setPanelSize"]
699
+ actions: ["setSize"]
700
+ },
701
+ "PANEL.COLLAPSE": {
702
+ actions: ["collapsePanel"]
703
+ },
704
+ "PANEL.EXPAND": {
705
+ actions: ["expandPanel"]
389
706
  },
390
- "SIZES.SET": {
391
- actions: ["setPanelSizes"]
707
+ "PANEL.RESIZE": {
708
+ actions: ["resizePanel"]
392
709
  }
393
710
  },
711
+ entry: ["syncSize"],
394
712
  states: {
395
713
  idle: {
396
- entry: ["clearActiveHandleId"],
714
+ entry: ["clearDraggingState", "clearKeyboardState"],
397
715
  on: {
398
716
  POINTER_OVER: {
399
717
  target: "hover:temp",
400
- actions: ["setActiveHandleId"]
718
+ actions: ["setKeyboardState"]
401
719
  },
402
720
  FOCUS: {
403
721
  target: "focused",
404
- actions: ["setActiveHandleId"]
722
+ actions: ["setKeyboardState"]
405
723
  },
406
724
  POINTER_DOWN: {
407
725
  target: "dragging",
408
- actions: ["setActiveHandleId"]
409
- },
410
- DOUBLE_CLICK: {
411
- actions: ["resetStartPanel", "setPreviousPanels"]
726
+ actions: ["setDraggingState"]
412
727
  }
413
728
  }
414
729
  },
@@ -420,7 +735,7 @@ var machine = core.createMachine({
420
735
  },
421
736
  POINTER_DOWN: {
422
737
  target: "dragging",
423
- actions: ["setActiveHandleId"]
738
+ actions: ["setDraggingState"]
424
739
  },
425
740
  POINTER_LEAVE: {
426
741
  target: "idle"
@@ -431,7 +746,8 @@ var machine = core.createMachine({
431
746
  tags: ["focus"],
432
747
  on: {
433
748
  POINTER_DOWN: {
434
- target: "dragging"
749
+ target: "dragging",
750
+ actions: ["setDraggingState"]
435
751
  },
436
752
  POINTER_LEAVE: {
437
753
  target: "idle"
@@ -444,52 +760,32 @@ var machine = core.createMachine({
444
760
  BLUR: {
445
761
  target: "idle"
446
762
  },
763
+ ENTER: {
764
+ actions: ["collapseOrExpandPanel"]
765
+ },
447
766
  POINTER_DOWN: {
448
767
  target: "dragging",
449
- actions: ["setActiveHandleId"]
450
- },
451
- ARROW_LEFT: {
452
- guard: "isHorizontal",
453
- actions: ["shrinkStartPanel", "setPreviousPanels"]
454
- },
455
- ARROW_RIGHT: {
456
- guard: "isHorizontal",
457
- actions: ["expandStartPanel", "setPreviousPanels"]
768
+ actions: ["setDraggingState"]
458
769
  },
459
- ARROW_UP: {
460
- guard: "isVertical",
461
- actions: ["shrinkStartPanel", "setPreviousPanels"]
770
+ KEYBOARD_MOVE: {
771
+ actions: ["setKeyboardValue"]
462
772
  },
463
- ARROW_DOWN: {
464
- guard: "isVertical",
465
- actions: ["expandStartPanel", "setPreviousPanels"]
466
- },
467
- ENTER: [
468
- {
469
- guard: "isStartPanelAtMax",
470
- actions: ["setStartPanelToMin", "setPreviousPanels"]
471
- },
472
- { actions: ["setStartPanelToMax", "setPreviousPanels"] }
473
- ],
474
- HOME: {
475
- actions: ["setStartPanelToMin", "setPreviousPanels"]
476
- },
477
- END: {
478
- actions: ["setStartPanelToMax", "setPreviousPanels"]
773
+ "FOCUS.CYCLE": {
774
+ actions: ["focusNextResizeTrigger"]
479
775
  }
480
776
  }
481
777
  },
482
778
  dragging: {
483
779
  tags: ["focus"],
484
- entry: ["focusResizeHandle"],
485
780
  effects: ["trackPointerMove"],
781
+ entry: ["invokeOnResizeStart"],
486
782
  on: {
487
783
  POINTER_MOVE: {
488
- actions: ["setPointerValue", "setGlobalCursor", "invokeOnResize"]
784
+ actions: ["setPointerValue", "setGlobalCursor"]
489
785
  },
490
786
  POINTER_UP: {
491
- target: "focused",
492
- actions: ["setPreviousPanels", "clearGlobalCursor", "blurResizeHandle", "invokeOnResizeEnd"]
787
+ target: "idle",
788
+ actions: ["invokeOnResizeEnd", "clearGlobalCursor"]
493
789
  }
494
790
  }
495
791
  }
@@ -513,182 +809,315 @@ var machine = core.createMachine({
513
809
  });
514
810
  }
515
811
  },
516
- guards: {
517
- isStartPanelAtMin: ({ context }) => context.get("activeResizeState").isAtMin,
518
- isStartPanelAtMax: ({ context }) => context.get("activeResizeState").isAtMax,
519
- isHorizontal: ({ prop }) => prop("orientation") === "horizontal",
520
- isVertical: ({ prop }) => prop("orientation") !== "horizontal"
521
- },
522
812
  actions: {
523
- setGlobalCursor({ context, scope, computed }) {
524
- const activeState = context.get("activeResizeState");
525
- const isHorizontal = computed("isHorizontal");
526
- setupGlobalCursor(scope, activeState, isHorizontal);
527
- },
528
- clearGlobalCursor({ scope }) {
529
- removeGlobalCursor(scope);
813
+ setSize(params) {
814
+ const { context, event, prop } = params;
815
+ const unsafeSize = event.size;
816
+ const prevSize = context.get("size");
817
+ const panels = prop("panels");
818
+ const safeSize = validateSizes({
819
+ size: unsafeSize,
820
+ panels
821
+ });
822
+ if (!utils.isEqual(prevSize, safeSize)) {
823
+ setSize(params, safeSize);
824
+ }
530
825
  },
531
- invokeOnResize({ context, prop }) {
532
- prop("onSizeChange")?.({
533
- size: Array.from(context.get("size")),
534
- activeHandleId: context.get("activeResizeId")
826
+ syncSize({ context, prop }) {
827
+ const panels = prop("panels");
828
+ let prevSize = context.get("size");
829
+ let unsafeSize = null;
830
+ if (prevSize.length === 0) {
831
+ unsafeSize = getUnsafeDefaultSize({
832
+ panels,
833
+ size: context.initial("size")
834
+ });
835
+ }
836
+ const nextSize = validateSizes({
837
+ size: unsafeSize ?? prevSize,
838
+ panels
535
839
  });
840
+ if (!utils.isEqual(prevSize, nextSize)) {
841
+ context.set("size", nextSize);
842
+ }
536
843
  },
537
- invokeOnResizeEnd({ context, prop }) {
538
- prop("onSizeChangeEnd")?.({
539
- size: Array.from(context.get("size")),
540
- activeHandleId: context.get("activeResizeId")
844
+ setDraggingState({ context, event, prop, scope }) {
845
+ const orientation = prop("orientation");
846
+ const size = context.get("size");
847
+ const resizeTriggerId = event.id;
848
+ const panelGroupEl = getRootEl(scope);
849
+ if (!panelGroupEl) return;
850
+ const handleElement = getResizeTriggerEl(scope, resizeTriggerId);
851
+ utils.ensure(handleElement, `Drag handle element not found for id "${resizeTriggerId}"`);
852
+ const initialCursorPosition = orientation === "horizontal" ? event.point.x : event.point.y;
853
+ context.set("dragState", {
854
+ resizeTriggerId: event.id,
855
+ resizeTriggerRect: handleElement.getBoundingClientRect(),
856
+ initialCursorPosition,
857
+ initialSize: size
541
858
  });
542
859
  },
543
- setActiveHandleId({ context, event }) {
544
- context.set("activeResizeId", event.id);
860
+ clearDraggingState({ context }) {
861
+ context.set("dragState", null);
545
862
  },
546
- clearActiveHandleId({ context }) {
547
- context.set("activeResizeId", null);
863
+ setKeyboardState({ context, event }) {
864
+ context.set("keyboardState", {
865
+ resizeTriggerId: event.id
866
+ });
548
867
  },
549
- setPanelSize({ context, event }) {
550
- const { id, size } = event;
551
- context.set(
552
- "size",
553
- (prev) => prev.map((panel) => {
554
- const panelSize = clamp(size, panel.minSize ?? 0, panel.maxSize ?? 100);
555
- return panel.id === id ? { ...panel, size: panelSize } : panel;
556
- })
557
- );
868
+ clearKeyboardState({ context }) {
869
+ context.set("keyboardState", null);
558
870
  },
559
- setPanelSizes({ context, event }) {
560
- context.set("size", event.sizes);
871
+ collapsePanel(params) {
872
+ const { context, prop, event, refs } = params;
873
+ const prevSize = context.get("size");
874
+ const panels = prop("panels");
875
+ const panel = panels.find((panel2) => panel2.id === event.id);
876
+ utils.ensure(panel, `Panel data not found for id "${event.id}"`);
877
+ if (panel.collapsible) {
878
+ const { collapsedSize = 0, panelSize, pivotIndices } = panelDataHelper(panels, panel, prevSize);
879
+ utils.ensure(panelSize, `Panel size not found for panel "${panel.id}"`);
880
+ if (!fuzzyNumbersEqual(panelSize, collapsedSize)) {
881
+ refs.get("panelSizeBeforeCollapse").set(panel.id, panelSize);
882
+ const isLastPanel = findPanelDataIndex(panels, panel) === panels.length - 1;
883
+ const delta = isLastPanel ? panelSize - collapsedSize : collapsedSize - panelSize;
884
+ const nextSize = resizeByDelta({
885
+ delta,
886
+ initialSize: prevSize,
887
+ panels,
888
+ pivotIndices,
889
+ prevSize,
890
+ trigger: "imperative-api"
891
+ });
892
+ if (!utils.isEqual(prevSize, nextSize)) {
893
+ setSize(params, nextSize);
894
+ }
895
+ }
896
+ }
561
897
  },
562
- setStartPanelToMin({ context, computed }) {
563
- const bounds = getPanelBounds(computed("panels"), context.get("activeResizeId"));
564
- if (!bounds) return;
565
- const { before, after } = bounds;
566
- context.set("size", (prev) => {
567
- const next = prev.slice();
568
- next[before.index].size = before.min;
569
- next[after.index].size = after.min;
570
- return next;
571
- });
898
+ expandPanel(params) {
899
+ const { context, prop, event, refs } = params;
900
+ const panels = prop("panels");
901
+ const prevSize = context.get("size");
902
+ const panel = panels.find((panel2) => panel2.id === event.id);
903
+ utils.ensure(panel, `Panel data not found for id "${event.id}"`);
904
+ if (panel.collapsible) {
905
+ const {
906
+ collapsedSize = 0,
907
+ panelSize = 0,
908
+ minSize: minSizeFromProps = 0,
909
+ pivotIndices
910
+ } = panelDataHelper(panels, panel, prevSize);
911
+ const minSize = event.minSize ?? minSizeFromProps;
912
+ if (fuzzyNumbersEqual(panelSize, collapsedSize)) {
913
+ const prevPanelSize = refs.get("panelSizeBeforeCollapse").get(panel.id);
914
+ const baseSize = prevPanelSize != null && prevPanelSize >= minSize ? prevPanelSize : minSize;
915
+ const isLastPanel = findPanelDataIndex(panels, panel) === panels.length - 1;
916
+ const delta = isLastPanel ? panelSize - baseSize : baseSize - panelSize;
917
+ const nextSize = resizeByDelta({
918
+ delta,
919
+ initialSize: prevSize,
920
+ panels,
921
+ pivotIndices,
922
+ prevSize,
923
+ trigger: "imperative-api"
924
+ });
925
+ if (!utils.isEqual(prevSize, nextSize)) {
926
+ setSize(params, nextSize);
927
+ }
928
+ }
929
+ }
572
930
  },
573
- setStartPanelToMax({ context, computed }) {
574
- const bounds = getPanelBounds(computed("panels"), context.get("activeResizeId"));
575
- if (!bounds) return;
576
- const { before, after } = bounds;
577
- context.set("size", (prev) => {
578
- const next = prev.slice();
579
- next[before.index].size = before.max;
580
- next[after.index].size = after.max;
581
- return next;
931
+ resizePanel(params) {
932
+ const { context, prop, event } = params;
933
+ const prevSize = context.get("size");
934
+ const panels = prop("panels");
935
+ const panel = getPanelById(panels, event.id);
936
+ const unsafePanelSize = event.size;
937
+ const { panelSize, pivotIndices } = panelDataHelper(panels, panel, prevSize);
938
+ utils.ensure(panelSize, `Panel size not found for panel "${panel.id}"`);
939
+ const isLastPanel = findPanelDataIndex(panels, panel) === panels.length - 1;
940
+ const delta = isLastPanel ? panelSize - unsafePanelSize : unsafePanelSize - panelSize;
941
+ const nextSize = resizeByDelta({
942
+ delta,
943
+ initialSize: prevSize,
944
+ panels,
945
+ pivotIndices,
946
+ prevSize,
947
+ trigger: "imperative-api"
582
948
  });
949
+ if (!utils.isEqual(prevSize, nextSize)) {
950
+ setSize(params, nextSize);
951
+ }
583
952
  },
584
- expandStartPanel({ context, event, computed }) {
585
- const bounds = getPanelBounds(computed("panels"), context.get("activeResizeId"));
586
- if (!bounds) return;
587
- const { before, after } = bounds;
588
- context.set("size", (prev) => {
589
- const next = prev.slice();
590
- next[before.index].size = before.up(event.step);
591
- next[after.index].size = after.down(event.step);
592
- return next;
953
+ setPointerValue(params) {
954
+ const { context, event, prop, scope } = params;
955
+ const dragState = context.get("dragState");
956
+ if (!dragState) return;
957
+ const { resizeTriggerId, initialSize, initialCursorPosition } = dragState;
958
+ const panels = prop("panels");
959
+ const panelGroupElement = getRootEl(scope);
960
+ utils.ensure(panelGroupElement, `Panel group element not found`);
961
+ const pivotIndices = resizeTriggerId.split(":").map((id) => panels.findIndex((panel) => panel.id === id));
962
+ const horizontal = prop("orientation") === "horizontal";
963
+ const cursorPosition = horizontal ? event.point.x : event.point.y;
964
+ const groupRect = panelGroupElement.getBoundingClientRect();
965
+ const groupSizeInPixels = horizontal ? groupRect.width : groupRect.height;
966
+ const offsetPixels = cursorPosition - initialCursorPosition;
967
+ const offsetPercentage = offsetPixels / groupSizeInPixels * 100;
968
+ const prevSize = context.get("size");
969
+ const nextSize = resizeByDelta({
970
+ delta: offsetPercentage,
971
+ initialSize: initialSize ?? prevSize,
972
+ panels,
973
+ pivotIndices,
974
+ prevSize,
975
+ trigger: "mouse-or-touch"
593
976
  });
977
+ if (!utils.isEqual(prevSize, nextSize)) {
978
+ setSize(params, nextSize);
979
+ }
594
980
  },
595
- shrinkStartPanel({ context, event, computed }) {
596
- const bounds = getPanelBounds(computed("panels"), context.get("activeResizeId"));
597
- if (!bounds) return;
598
- const { before, after } = bounds;
599
- context.set("size", (prev) => {
600
- const next = prev.slice();
601
- next[before.index].size = before.down(event.step);
602
- next[after.index].size = after.up(event.step);
603
- return next;
981
+ setKeyboardValue(params) {
982
+ const { context, event, prop } = params;
983
+ const panelDataArray = prop("panels");
984
+ const resizeTriggerId = event.id;
985
+ const delta = event.delta;
986
+ const pivotIndices = resizeTriggerId.split(":").map((id) => panelDataArray.findIndex((panelData) => panelData.id === id));
987
+ const prevSize = context.get("size");
988
+ const nextSize = resizeByDelta({
989
+ delta,
990
+ initialSize: prevSize,
991
+ panels: panelDataArray,
992
+ pivotIndices,
993
+ prevSize,
994
+ trigger: "keyboard"
604
995
  });
996
+ if (!utils.isEqual(prevSize, nextSize)) {
997
+ setSize(params, nextSize);
998
+ }
605
999
  },
606
- resetStartPanel({ context, computed }) {
607
- const bounds = getPanelBounds(computed("panels"), context.get("activeResizeId"));
608
- if (!bounds) return;
609
- const { before, after } = bounds;
610
- const initialSize = context.initial("size");
611
- context.set("size", (prev) => {
612
- const next = prev.slice();
613
- next[before.index].size = initialSize[before.index].size;
614
- next[after.index].size = initialSize[after.index].size;
615
- return next;
1000
+ invokeOnResizeEnd({ context, prop }) {
1001
+ queueMicrotask(() => {
1002
+ const dragState = context.get("dragState");
1003
+ prop("onResizeEnd")?.({
1004
+ size: context.get("size"),
1005
+ resizeTriggerId: dragState?.resizeTriggerId ?? null
1006
+ });
616
1007
  });
617
1008
  },
618
- focusResizeHandle({ scope, context }) {
619
- domQuery.raf(() => {
620
- const activeId = context.get("activeResizeId");
621
- if (!activeId) return;
622
- getResizeTriggerEl(scope, activeId)?.focus({ preventScroll: true });
1009
+ invokeOnResizeStart({ prop }) {
1010
+ queueMicrotask(() => {
1011
+ prop("onResizeStart")?.();
623
1012
  });
624
1013
  },
625
- blurResizeHandle({ scope, context }) {
626
- domQuery.raf(() => {
627
- const activeId = context.get("activeResizeId");
628
- if (!activeId) return;
629
- getResizeTriggerEl(scope, activeId)?.blur();
630
- });
1014
+ collapseOrExpandPanel(params) {
1015
+ const { context, prop } = params;
1016
+ const panelDataArray = prop("panels");
1017
+ const sizes = context.get("size");
1018
+ const resizeTriggerId = context.get("keyboardState")?.resizeTriggerId;
1019
+ const [idBefore, idAfter] = resizeTriggerId?.split(":") ?? [];
1020
+ const index = panelDataArray.findIndex((panelData2) => panelData2.id === idBefore);
1021
+ if (index === -1) return;
1022
+ const panelData = panelDataArray[index];
1023
+ utils.ensure(panelData, `No panel data found for index ${index}`);
1024
+ const size = sizes[index];
1025
+ const { collapsedSize = 0, collapsible, minSize = 0 } = panelData;
1026
+ if (size != null && collapsible) {
1027
+ const pivotIndices = [idBefore, idAfter].map(
1028
+ (id) => panelDataArray.findIndex((panelData2) => panelData2.id === id)
1029
+ );
1030
+ const nextSize = resizeByDelta({
1031
+ delta: fuzzyNumbersEqual(size, collapsedSize) ? minSize - collapsedSize : collapsedSize - size,
1032
+ initialSize: context.initial("size"),
1033
+ panels: panelDataArray,
1034
+ pivotIndices,
1035
+ prevSize: sizes,
1036
+ trigger: "keyboard"
1037
+ });
1038
+ if (!utils.isEqual(sizes, nextSize)) {
1039
+ setSize(params, nextSize);
1040
+ }
1041
+ }
631
1042
  },
632
- setPreviousPanels({ refs, computed }) {
633
- refs.set("previousPanels", computed("panels").slice());
1043
+ setGlobalCursor({ context, scope, prop }) {
1044
+ const dragState = context.get("dragState");
1045
+ if (!dragState) return;
1046
+ const panels = prop("panels");
1047
+ const horizontal = prop("orientation") === "horizontal";
1048
+ const [idBefore] = dragState.resizeTriggerId.split(":");
1049
+ const indexBefore = panels.findIndex((panel2) => panel2.id === idBefore);
1050
+ const panel = panels[indexBefore];
1051
+ const size = context.get("size");
1052
+ const aria = getAriaValue(size, panels, dragState.resizeTriggerId);
1053
+ const isAtMin = fuzzyNumbersEqual(aria.valueNow, aria.valueMin) || fuzzyNumbersEqual(aria.valueNow, panel.collapsedSize);
1054
+ const isAtMax = fuzzyNumbersEqual(aria.valueNow, aria.valueMax);
1055
+ const cursorState = { isAtMin, isAtMax };
1056
+ setupGlobalCursor(scope, cursorState, horizontal, prop("nonce"));
634
1057
  },
635
- setActiveResizeState({ context, computed }) {
636
- const panels = getPanelBounds(computed("panels"), context.get("activeResizeId"));
637
- if (!panels) return;
638
- const { before } = panels;
639
- context.set("activeResizeState", {
640
- isAtMin: before.isAtMin,
641
- isAtMax: before.isAtMax
642
- });
1058
+ clearGlobalCursor({ scope }) {
1059
+ removeGlobalCursor(scope);
643
1060
  },
644
- setPointerValue({ context, event, prop, scope, computed }) {
645
- const panels = getHandlePanels(computed("panels"), context.get("activeResizeId"));
646
- const bounds = getHandleBounds(computed("panels"), context.get("activeResizeId"));
647
- if (!panels || !bounds) return;
648
- const rootEl = getRootEl(scope);
649
- if (!rootEl) return;
650
- const relativePoint = domQuery.getRelativePoint(event.point, rootEl);
651
- const percentValue = relativePoint.getPercentValue({
652
- dir: prop("dir"),
653
- orientation: prop("orientation")
654
- });
655
- let pointValue = percentValue * 100;
656
- context.set("activeResizeState", {
657
- isAtMin: pointValue < bounds.min,
658
- isAtMax: pointValue > bounds.max
659
- });
660
- pointValue = clamp(pointValue, bounds.min, bounds.max);
661
- const { before, after } = panels;
662
- const offset = pointValue - before.end;
663
- context.set("size", (prev) => {
664
- const next = prev.slice();
665
- next[before.index].size = before.size + offset;
666
- next[after.index].size = after.size - offset;
667
- return next;
668
- });
1061
+ focusNextResizeTrigger({ event, scope }) {
1062
+ const resizeTriggers = getResizeTriggerEls(scope);
1063
+ const index = resizeTriggers.findIndex((el) => el.dataset.id === event.id);
1064
+ const handleEl = event.shiftKey ? utils.prev(resizeTriggers, index) : utils.next(resizeTriggers, index);
1065
+ handleEl?.focus();
669
1066
  }
670
1067
  }
671
1068
  }
672
1069
  });
1070
+ function setSize(params, sizes) {
1071
+ const { refs, prop, context } = params;
1072
+ const panelsArray = prop("panels");
1073
+ const onCollapse = prop("onCollapse");
1074
+ const onExpand = prop("onExpand");
1075
+ const panelIdToLastNotifiedSizeMap = refs.get("panelIdToLastNotifiedSizeMap");
1076
+ context.set("size", sizes);
1077
+ sizes.forEach((size, index) => {
1078
+ const panelData = panelsArray[index];
1079
+ utils.ensure(panelData, `Panel data not found for index ${index}`);
1080
+ const { collapsedSize = 0, collapsible, id: panelId } = panelData;
1081
+ const lastNotifiedSize = panelIdToLastNotifiedSizeMap.get(panelId);
1082
+ if (lastNotifiedSize == null || size !== lastNotifiedSize) {
1083
+ panelIdToLastNotifiedSizeMap.set(panelId, size);
1084
+ if (collapsible && (onCollapse || onExpand)) {
1085
+ if ((lastNotifiedSize == null || fuzzyNumbersEqual(lastNotifiedSize, collapsedSize)) && !fuzzyNumbersEqual(size, collapsedSize)) {
1086
+ onExpand?.({ panelId, size });
1087
+ }
1088
+ if (onCollapse && (lastNotifiedSize == null || !fuzzyNumbersEqual(lastNotifiedSize, collapsedSize)) && fuzzyNumbersEqual(size, collapsedSize)) {
1089
+ onCollapse?.({ panelId, size });
1090
+ }
1091
+ }
1092
+ }
1093
+ });
1094
+ }
673
1095
  var props = types.createProps()([
674
1096
  "dir",
675
1097
  "getRootNode",
676
1098
  "id",
677
1099
  "ids",
678
- "onSizeChange",
679
- "onSizeChangeEnd",
1100
+ "onResize",
1101
+ "onResizeStart",
1102
+ "onResizeEnd",
1103
+ "onCollapse",
1104
+ "onExpand",
680
1105
  "orientation",
681
1106
  "size",
682
- "defaultSize"
1107
+ "defaultSize",
1108
+ "panels",
1109
+ "keyboardResizeBy",
1110
+ "nonce"
683
1111
  ]);
684
1112
  var splitProps = utils.createSplitProps(props);
685
- var panelProps = types.createProps()(["id", "snapSize"]);
1113
+ var panelProps = types.createProps()(["id"]);
686
1114
  var splitPanelProps = utils.createSplitProps(panelProps);
687
- var resizeTriggerProps = types.createProps()(["disabled", "id", "step"]);
1115
+ var resizeTriggerProps = types.createProps()(["disabled", "id"]);
688
1116
  var splitResizeTriggerProps = utils.createSplitProps(resizeTriggerProps);
689
1117
 
690
1118
  exports.anatomy = anatomy;
691
1119
  exports.connect = connect;
1120
+ exports.layout = getPanelLayout;
692
1121
  exports.machine = machine;
693
1122
  exports.panelProps = panelProps;
694
1123
  exports.props = props;