@corvu-next/resizable 0.1.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.jsx ADDED
@@ -0,0 +1,1706 @@
1
+ // src/context.ts
2
+ import { createContext, useContext } from "solid-js";
3
+ import {
4
+ createKeyedContext,
5
+ useKeyedContext
6
+ } from "@corvu-next/utils/create/keyedContext";
7
+ var ResizableContext = createContext(null);
8
+ var createResizableContext = (contextId) => {
9
+ if (contextId === void 0) return ResizableContext;
10
+ const context = createKeyedContext(
11
+ `resizable-${contextId}`
12
+ );
13
+ return context;
14
+ };
15
+ var useResizableContext = (contextId) => {
16
+ if (contextId === void 0) {
17
+ const context2 = useContext(ResizableContext);
18
+ if (!context2) {
19
+ throw new Error(
20
+ "[corvu]: Resizable context not found. Make sure to wrap Resizable components in <Resizable.Root>"
21
+ );
22
+ }
23
+ return context2;
24
+ }
25
+ const context = useKeyedContext(
26
+ `resizable-${contextId}`
27
+ );
28
+ if (!context) {
29
+ throw new Error(
30
+ `[corvu]: Resizable context with id "${contextId}" not found. Make sure to wrap Resizable components in <Resizable.Root contextId="${contextId}">`
31
+ );
32
+ }
33
+ return context;
34
+ };
35
+ var InternalResizableContext = createContext(null);
36
+ var createInternalResizableContext = (contextId) => {
37
+ if (contextId === void 0) return InternalResizableContext;
38
+ const context = createKeyedContext(
39
+ `resizable-internal-${contextId}`
40
+ );
41
+ return context;
42
+ };
43
+ var useInternalResizableContext = (contextId) => {
44
+ if (contextId === void 0) {
45
+ const context2 = useContext(InternalResizableContext);
46
+ if (!context2) {
47
+ throw new Error(
48
+ "[corvu]: Resizable context not found. Make sure to wrap Resizable components in <Resizable.Root>"
49
+ );
50
+ }
51
+ return context2;
52
+ }
53
+ const context = useKeyedContext(
54
+ `resizable-internal-${contextId}`
55
+ );
56
+ if (!context) {
57
+ throw new Error(
58
+ `[corvu]: Resizable context with id "${contextId}" not found. Make sure to wrap Resizable components in <Resizable.Root contextId="${contextId}">`
59
+ );
60
+ }
61
+ return context;
62
+ };
63
+
64
+ // src/Handle.tsx
65
+ import {
66
+ callEventHandler,
67
+ combineStyle
68
+ } from "@corvu-next/utils/dom";
69
+ import {
70
+ createEffect,
71
+ createMemo,
72
+ createSignal,
73
+ merge,
74
+ omit,
75
+ Show
76
+ } from "solid-js";
77
+ import { Dynamic } from "@corvu-next/utils/dynamic";
78
+
79
+ // src/lib/utils.ts
80
+ var resolveSize = (size, rootSize) => {
81
+ if (typeof size === "number") {
82
+ return size;
83
+ }
84
+ if (!size.endsWith("px")) {
85
+ throw new Error(
86
+ `[corvu] Sizes must be a number or a string ending with 'px'. Got ${size}`
87
+ );
88
+ }
89
+ return fixToPrecision(parseFloat(size) / rootSize);
90
+ };
91
+ var splitPanels = (props) => {
92
+ const precedingPanels = props.panels.filter(
93
+ (panel) => !!(props.focusedElement.compareDocumentPosition(panel.data.element) & Node.DOCUMENT_POSITION_PRECEDING)
94
+ );
95
+ const followingPanels = props.panels.filter(
96
+ (panel) => !!(props.focusedElement.compareDocumentPosition(panel.data.element) & Node.DOCUMENT_POSITION_FOLLOWING)
97
+ );
98
+ return [precedingPanels, followingPanels];
99
+ };
100
+ var PRECISION = 6;
101
+ var fixToPrecision = (value) => parseFloat(value.toFixed(PRECISION));
102
+
103
+ // src/lib/cursor.ts
104
+ var globalCursorStyle = null;
105
+ var cursorStyleElement = null;
106
+ var globalResizeConstraints = 0;
107
+ var constraintToCursorMap = {
108
+ 1: "e-resize",
109
+ 2: "w-resize",
110
+ 3: "ew-resize",
111
+ 4: "s-resize",
112
+ 8: "n-resize",
113
+ 12: "ns-resize",
114
+ 5: "se-resize",
115
+ 9: "ne-resize",
116
+ 6: "sw-resize",
117
+ 10: "nw-resize"
118
+ };
119
+ var cachedCursorStyle = null;
120
+ var updateCursorStyle = () => {
121
+ if (!globalCursorStyle) {
122
+ if (cursorStyleElement) {
123
+ cachedCursorStyle = null;
124
+ cursorStyleElement.remove();
125
+ cursorStyleElement = null;
126
+ }
127
+ return;
128
+ }
129
+ let cursorStyle = constraintToCursorMap[globalResizeConstraints] ?? null;
130
+ if (cursorStyle === null) {
131
+ switch (globalCursorStyle) {
132
+ case "horizontal":
133
+ cursorStyle = "col-resize";
134
+ break;
135
+ case "vertical":
136
+ cursorStyle = "row-resize";
137
+ break;
138
+ case "both":
139
+ cursorStyle = "move";
140
+ break;
141
+ }
142
+ }
143
+ if (cursorStyle === cachedCursorStyle) return;
144
+ cachedCursorStyle = cursorStyle;
145
+ if (!cursorStyleElement) {
146
+ cursorStyleElement = document.createElement("style");
147
+ document.head.appendChild(cursorStyleElement);
148
+ }
149
+ cursorStyleElement.innerHTML = `*{cursor: ${cursorStyle}!important;}`;
150
+ };
151
+ var reportResizeConstraints = (orientation, constraints) => {
152
+ switch (orientation) {
153
+ case "horizontal":
154
+ if (constraints === 1) {
155
+ globalResizeConstraints |= 1;
156
+ globalResizeConstraints &= ~2;
157
+ } else if (constraints === 2) {
158
+ globalResizeConstraints |= 2;
159
+ globalResizeConstraints &= ~1;
160
+ } else if (constraints === 3) {
161
+ globalResizeConstraints |= 3;
162
+ } else {
163
+ globalResizeConstraints &= ~3;
164
+ }
165
+ break;
166
+ case "vertical":
167
+ if (constraints === 1) {
168
+ globalResizeConstraints |= 4;
169
+ globalResizeConstraints &= ~8;
170
+ } else if (constraints === 2) {
171
+ globalResizeConstraints |= 8;
172
+ globalResizeConstraints &= ~4;
173
+ } else if (constraints === 3) {
174
+ globalResizeConstraints |= 12;
175
+ } else {
176
+ globalResizeConstraints &= ~12;
177
+ }
178
+ break;
179
+ }
180
+ updateCursorStyle();
181
+ };
182
+ var resetResizeConstraints = () => {
183
+ globalResizeConstraints = 0;
184
+ updateCursorStyle();
185
+ };
186
+ var setGlobalCursorStyle = (cursorStyle) => {
187
+ globalCursorStyle = cursorStyle;
188
+ updateCursorStyle();
189
+ };
190
+ var handleResizeConstraints = (props) => {
191
+ if (fixToPrecision(props.distributablePercentage) !== fixToPrecision(props.desiredPercentage)) {
192
+ let constraints = null;
193
+ if (props.betweenCollapse === true) {
194
+ constraints = 3;
195
+ } else if (props.desiredPercentage < props.distributablePercentage && props.revertConstraints !== true || props.desiredPercentage > props.distributablePercentage && props.revertConstraints === true) {
196
+ constraints = 1;
197
+ } else {
198
+ constraints = 2;
199
+ }
200
+ reportResizeConstraints(props.orientation, constraints);
201
+ } else {
202
+ reportResizeConstraints(props.orientation, 0);
203
+ }
204
+ };
205
+
206
+ // src/lib/handleManager.ts
207
+ import { some } from "@corvu-next/utils/reactivity";
208
+ var handles = [];
209
+ var dragStartPos = null;
210
+ var globalHovered = null;
211
+ var INTERSECTION_TOLERANCE = 1;
212
+ var equalsWithTolerance = (a, b) => Math.abs(a - b) <= INTERSECTION_TOLERANCE;
213
+ var registerHandle = (handle) => {
214
+ handles.push(handle);
215
+ for (const handle2 of handles) {
216
+ for (const compareHandle of handles) {
217
+ if (handle2.orientation === compareHandle.orientation || handle2.element === compareHandle.element) {
218
+ continue;
219
+ }
220
+ const handleRect = handle2.element.getBoundingClientRect();
221
+ const compareHandleRect = compareHandle.element.getBoundingClientRect();
222
+ if (handle2.orientation === "horizontal") {
223
+ if (handleRect.left > compareHandleRect.right || handleRect.right < compareHandleRect.left) {
224
+ continue;
225
+ }
226
+ }
227
+ if (handle2.orientation === "vertical") {
228
+ if (handleRect.top > compareHandleRect.bottom || handleRect.bottom < compareHandleRect.top) {
229
+ continue;
230
+ }
231
+ }
232
+ const isStartIntersection = handle2.orientation === "horizontal" ? equalsWithTolerance(handleRect.top, compareHandleRect.bottom) : equalsWithTolerance(handleRect.left, compareHandleRect.right);
233
+ const isEndIntersection = handle2.orientation === "horizontal" ? equalsWithTolerance(handleRect.bottom, compareHandleRect.top) : equalsWithTolerance(handleRect.right, compareHandleRect.left);
234
+ if (isStartIntersection) {
235
+ handle2.startIntersection.setHandle(compareHandle);
236
+ }
237
+ if (isEndIntersection) {
238
+ handle2.endIntersection.setHandle(compareHandle);
239
+ }
240
+ }
241
+ }
242
+ return {
243
+ onDragStart: (event, target) => onDragStart(handle, event, target),
244
+ onHoveredChange: (state) => {
245
+ globalHovered = state;
246
+ const dragging = !!dragStartPos;
247
+ let cursorStyle = null;
248
+ switch (state) {
249
+ case "handle": {
250
+ const startHandle = handle.startIntersection.handle();
251
+ const endHandle = handle.endIntersection.handle();
252
+ if (!dragging) {
253
+ handle.setActive(true);
254
+ startHandle?.setActive(false);
255
+ endHandle?.setActive(false);
256
+ }
257
+ startHandle?.setHoveredAsIntersection(false);
258
+ endHandle?.setHoveredAsIntersection(false);
259
+ cursorStyle = handle.orientation;
260
+ break;
261
+ }
262
+ case "startIntersection": {
263
+ const startHandle = handle.startIntersection.handle();
264
+ if (!dragging) {
265
+ startHandle?.setActive(true);
266
+ }
267
+ startHandle?.setHoveredAsIntersection(true);
268
+ cursorStyle = "both";
269
+ break;
270
+ }
271
+ case "endIntersection": {
272
+ const endHandle = handle.endIntersection.handle();
273
+ if (!dragging) {
274
+ endHandle?.setActive(true);
275
+ }
276
+ endHandle?.setHoveredAsIntersection(true);
277
+ cursorStyle = "both";
278
+ break;
279
+ }
280
+ case null: {
281
+ const startHandle = handle.startIntersection.handle();
282
+ const endHandle = handle.endIntersection.handle();
283
+ if (!dragging && !handle.focused()) {
284
+ handle.setActive(false);
285
+ startHandle?.setActive(false);
286
+ endHandle?.setActive(false);
287
+ }
288
+ startHandle?.setHoveredAsIntersection(false);
289
+ endHandle?.setHoveredAsIntersection(false);
290
+ cursorStyle = null;
291
+ break;
292
+ }
293
+ }
294
+ if (!dragging && handle.handleCursorStyle()) {
295
+ setGlobalCursorStyle(cursorStyle);
296
+ }
297
+ }
298
+ };
299
+ };
300
+ var unregisterHandle = (handle) => {
301
+ handles.splice(handles.indexOf(handle), 1);
302
+ };
303
+ var onDragStart = (handle, event, target) => {
304
+ dragStartPos = { x: event.clientX, y: event.clientY };
305
+ handle.setDragging(true);
306
+ if (target === "startIntersection") {
307
+ handle.startIntersection.handle()?.setDragging(true);
308
+ }
309
+ if (target === "endIntersection") {
310
+ handle.endIntersection.handle()?.setDragging(true);
311
+ }
312
+ window.addEventListener("pointermove", onPointerMove);
313
+ window.addEventListener("touchmove", onTouchMove);
314
+ window.addEventListener("pointerup", onDragEnd);
315
+ window.addEventListener("touchend", onDragEnd);
316
+ window.addEventListener("contextmenu", onDragEnd);
317
+ };
318
+ var onPointerMove = (event) => onMove(event.clientX, event.clientY, event.altKey);
319
+ var onTouchMove = (event) => {
320
+ if (!event.touches[0]) return;
321
+ onMove(event.touches[0].clientX, event.touches[0].clientY, event.altKey);
322
+ };
323
+ var altKeyCache = false;
324
+ var onMove = (x, y, altKey) => {
325
+ if (!dragStartPos) return;
326
+ if (handles.some((handle) => handle.dragging() && handle.altKey === "only")) {
327
+ altKey = true;
328
+ }
329
+ if (handles.some((handle) => handle.dragging() && handle.altKey === false)) {
330
+ altKey = false;
331
+ }
332
+ if (altKeyCache !== altKey) {
333
+ dragStartPos = { x, y };
334
+ altKeyCache = altKey;
335
+ }
336
+ for (const handle of handles) {
337
+ if (!handle.dragging()) continue;
338
+ handle.onDrag(
339
+ handle.orientation === "horizontal" ? x - dragStartPos.x : y - dragStartPos.y,
340
+ altKey
341
+ );
342
+ }
343
+ };
344
+ var onDragEnd = (event) => {
345
+ for (const handle of handles) {
346
+ if (!handle.dragging()) {
347
+ if (some(handle.hovered, handle.focused, handle.hoveredAsIntersection)) {
348
+ handle.setActive(true);
349
+ if (handle.handleCursorStyle()) {
350
+ setGlobalCursorStyle(handle.orientation);
351
+ }
352
+ }
353
+ } else {
354
+ handle.setDragging(false);
355
+ handle.onDragEnd(event);
356
+ if (!handle.hovered() && !handle.hoveredAsIntersection()) {
357
+ handle.setActive(false);
358
+ }
359
+ if (!globalHovered && handle.handleCursorStyle()) {
360
+ setGlobalCursorStyle(null);
361
+ }
362
+ }
363
+ }
364
+ resetResizeConstraints();
365
+ dragStartPos = null;
366
+ window.removeEventListener("pointermove", onPointerMove);
367
+ window.removeEventListener("touchmove", onTouchMove);
368
+ window.removeEventListener("pointerup", onDragEnd);
369
+ window.removeEventListener("touchend", onDragEnd);
370
+ window.removeEventListener("contextmenu", onDragEnd);
371
+ };
372
+
373
+ // src/Handle.tsx
374
+ import { dataIf } from "@corvu-next/utils";
375
+ import { mergeRefs } from "@corvu-next/utils/reactivity";
376
+ var ResizableHandle = (props) => {
377
+ const defaultedProps = merge(
378
+ {
379
+ startIntersection: true,
380
+ endIntersection: true,
381
+ altKey: true
382
+ },
383
+ props
384
+ );
385
+ const localProps = defaultedProps;
386
+ const otherProps = omit(
387
+ defaultedProps,
388
+ "startIntersection",
389
+ "endIntersection",
390
+ "altKey",
391
+ "onHandleDragStart",
392
+ "onHandleDrag",
393
+ "onHandleDragEnd",
394
+ "contextId",
395
+ "ref",
396
+ "style",
397
+ "disabled",
398
+ "children",
399
+ "onMouseEnter",
400
+ "onMouseLeave",
401
+ "onKeyDown",
402
+ "onKeyUp",
403
+ "onFocus",
404
+ "onBlur",
405
+ "onPointerDown"
406
+ );
407
+ const [ref, setRef] = createSignal(null);
408
+ const [hoveredAsIntersection, setHoveredAsIntersection] = createSignal(false);
409
+ const [hovered, setHovered] = createSignal(null);
410
+ const [focused, setFocused] = createSignal(false);
411
+ const [active, setActive] = createSignal(false);
412
+ const [dragging, setDragging] = createSignal(false);
413
+ const [startIntersectionHandle, setStartIntersectionHandle] = createSignal(null);
414
+ const [endIntersectionHandle, setEndIntersectionHandle] = createSignal(null);
415
+ const context = createMemo(
416
+ () => useInternalResizableContext(localProps.contextId)
417
+ );
418
+ const ariaInformation = createMemo(() => {
419
+ const handle = ref();
420
+ if (!handle) {
421
+ return void 0;
422
+ }
423
+ const panels = context().panels();
424
+ const [precidingPanels, followingPanels] = splitPanels({
425
+ panels,
426
+ focusedElement: handle
427
+ });
428
+ const ariaControls = precidingPanels[precidingPanels.length - 1]?.data.id;
429
+ const ariaValueMax = followingPanels.reduce(
430
+ (acc, panel) => acc - resolveSize(panel.data.minSize, context().rootSize()),
431
+ 1
432
+ );
433
+ const ariaValueMin = precidingPanels.reduce(
434
+ (acc, panel) => acc + resolveSize(panel.data.minSize, context().rootSize()),
435
+ 0
436
+ );
437
+ const ariaValueNow = precidingPanels.reduce(
438
+ (acc, panel) => acc + resolveSize(panel.size(), context().rootSize()),
439
+ 0
440
+ );
441
+ return {
442
+ ariaControls,
443
+ ariaValueMax: fixToPrecision(ariaValueMax),
444
+ ariaValueMin: fixToPrecision(ariaValueMin),
445
+ ariaValueNow: fixToPrecision(ariaValueNow)
446
+ };
447
+ });
448
+ let globalHandleCallbacks = null;
449
+ createEffect((_prev) => {
450
+ if (localProps.disabled === true) return void 0;
451
+ const element = ref();
452
+ if (!element) return void 0;
453
+ const globalHandle = {
454
+ element,
455
+ orientation: context().orientation(),
456
+ handleCursorStyle: context().handleCursorStyle,
457
+ altKey: localProps.altKey,
458
+ startIntersection: {
459
+ handle: startIntersectionHandle,
460
+ setHandle: (handle) => {
461
+ if (localProps.startIntersection !== true) return;
462
+ setStartIntersectionHandle(handle);
463
+ }
464
+ },
465
+ endIntersection: {
466
+ handle: endIntersectionHandle,
467
+ setHandle: (handle) => {
468
+ if (localProps.endIntersection !== true) return;
469
+ setEndIntersectionHandle(handle);
470
+ }
471
+ },
472
+ hovered,
473
+ focused,
474
+ hoveredAsIntersection,
475
+ setHoveredAsIntersection,
476
+ active,
477
+ setActive,
478
+ dragging,
479
+ setDragging,
480
+ onDrag: (delta, altKey) => {
481
+ if (localProps.onHandleDrag !== void 0) {
482
+ const dragEvent = new CustomEvent("drag", {
483
+ cancelable: true
484
+ });
485
+ localProps.onHandleDrag(dragEvent);
486
+ if (dragEvent.defaultPrevented) return;
487
+ }
488
+ context().onDrag(element, delta, altKey);
489
+ },
490
+ onDragEnd: (event) => {
491
+ localProps.onHandleDragEnd?.(event);
492
+ context().onDragEnd();
493
+ }
494
+ };
495
+ globalHandleCallbacks = registerHandle(globalHandle);
496
+ return globalHandle;
497
+ }, (prevHandle) => {
498
+ if (prevHandle) {
499
+ unregisterHandle(prevHandle);
500
+ globalHandleCallbacks = null;
501
+ }
502
+ });
503
+ createEffect(() => {
504
+ const state = hovered();
505
+ return state;
506
+ }, (state) => {
507
+ globalHandleCallbacks?.onHoveredChange(state);
508
+ });
509
+ const onMouseEnter = (e) => {
510
+ if (callEventHandler(localProps.onMouseEnter, e) || localProps.disabled === true)
511
+ return;
512
+ setHovered("handle");
513
+ };
514
+ const onMouseLeave = (e) => {
515
+ if (callEventHandler(localProps.onMouseLeave, e)) return;
516
+ setHovered(null);
517
+ };
518
+ const onKeyDown = (e) => {
519
+ if (callEventHandler(localProps.onKeyDown, e) || dragging()) return;
520
+ const element = ref();
521
+ if (!element) return;
522
+ const altKey = localProps.altKey === "only" || localProps.altKey !== false && e.altKey;
523
+ context().onKeyDown(element, e, altKey);
524
+ };
525
+ const onKeyUp = (e) => {
526
+ if (callEventHandler(localProps.onKeyUp, e) || e.key !== "Tab") return;
527
+ setFocused(true);
528
+ };
529
+ const onFocus = (e) => {
530
+ if (callEventHandler(localProps.onFocus, e) || hovered()) return;
531
+ setFocused(true);
532
+ setActive(true);
533
+ };
534
+ const onBlur = (e) => {
535
+ if (callEventHandler(localProps.onBlur, e)) return;
536
+ setFocused(false);
537
+ if (hovered()) return;
538
+ setActive(false);
539
+ };
540
+ const onPointerDown = (e) => {
541
+ if (callEventHandler(localProps.onPointerDown, e)) return;
542
+ if (callEventHandler(localProps.onHandleDragStart, e)) return;
543
+ const targetElement = e.target;
544
+ targetElement.setPointerCapture(e.pointerId);
545
+ let target = "handle";
546
+ if (targetElement.hasAttribute(
547
+ "data-corvu-resizable-handle-start-intersection"
548
+ )) {
549
+ target = "startIntersection";
550
+ }
551
+ if (targetElement.hasAttribute("data-corvu-resizable-handle-end-intersection")) {
552
+ target = "endIntersection";
553
+ }
554
+ globalHandleCallbacks?.onDragStart(e, target);
555
+ };
556
+ return <Dynamic
557
+ as="button"
558
+ ref={mergeRefs(setRef, localProps.ref)}
559
+ style={combineStyle(
560
+ {
561
+ position: "relative",
562
+ cursor: context().handleCursorStyle() ? "inherit" : void 0,
563
+ "touch-action": "none",
564
+ "flex-shrink": 0
565
+ },
566
+ localProps.style
567
+ )}
568
+ disabled={localProps.disabled}
569
+ onBlur={onBlur}
570
+ onFocus={onFocus}
571
+ onKeyDown={onKeyDown}
572
+ onKeyUp={onKeyUp}
573
+ onMouseEnter={onMouseEnter}
574
+ onMouseLeave={onMouseLeave}
575
+ onPointerDown={onPointerDown}
576
+ role="separator"
577
+ aria-controls={ariaInformation()?.ariaControls}
578
+ aria-orientation={context().orientation()}
579
+ aria-valuemax={ariaInformation()?.ariaValueMax}
580
+ aria-valuemin={ariaInformation()?.ariaValueMin}
581
+ aria-valuenow={ariaInformation()?.ariaValueNow}
582
+ data-active={dataIf(active())}
583
+ data-dragging={dataIf(dragging())}
584
+ data-orientation={context().orientation()}
585
+ data-corvu-resizable-handle=""
586
+ {...otherProps}
587
+ >
588
+ <Show when={startIntersectionHandle()}>
589
+ <div
590
+ data-corvu-resizable-handle-start-intersection
591
+ onMouseEnter={() => setHovered("startIntersection")}
592
+ onMouseLeave={(e) => {
593
+ if (ref()?.contains(e.relatedTarget) === true) {
594
+ setHovered("handle");
595
+ } else {
596
+ setHovered(null);
597
+ }
598
+ }}
599
+ style={{
600
+ position: "absolute",
601
+ "aspect-ratio": "1 / 1",
602
+ top: 0,
603
+ left: 0,
604
+ height: context().orientation() === "horizontal" ? void 0 : "100%",
605
+ width: context().orientation() === "horizontal" ? "100%" : void 0,
606
+ transform: context().orientation() === "horizontal" ? "translate3d(0, -100%, 0)" : "translate3d(-100%, 0, 0)",
607
+ "z-index": 1
608
+ }}
609
+ />
610
+ </Show>
611
+ {localProps.children}
612
+ <Show when={endIntersectionHandle()}>
613
+ <div
614
+ data-corvu-resizable-handle-end-intersection
615
+ onMouseEnter={() => setHovered("endIntersection")}
616
+ onMouseLeave={(e) => {
617
+ if (ref()?.contains(e.relatedTarget) === true) {
618
+ setHovered("handle");
619
+ } else {
620
+ setHovered(null);
621
+ }
622
+ }}
623
+ style={{
624
+ position: "absolute",
625
+ "aspect-ratio": "1 / 1",
626
+ bottom: 0,
627
+ right: 0,
628
+ height: context().orientation() === "horizontal" ? void 0 : "100%",
629
+ width: context().orientation() === "horizontal" ? "100%" : void 0,
630
+ transform: context().orientation() === "horizontal" ? "translate3d(0, 100%, 0)" : "translate3d(100%, 0, 0)",
631
+ "z-index": 1
632
+ }}
633
+ />
634
+ </Show>
635
+ </Dynamic>;
636
+ };
637
+ var Handle_default = ResizableHandle;
638
+
639
+ // src/Panel.tsx
640
+ import { combineStyle as combineStyle2 } from "@corvu-next/utils/dom";
641
+ import {
642
+ createEffect as createEffect2,
643
+ createMemo as createMemo2,
644
+ createSignal as createSignal2,
645
+ createUniqueId,
646
+ merge as merge2,
647
+ omit as omit2,
648
+ untrack
649
+ } from "solid-js";
650
+ import { dataIf as dataIf2, isFunction } from "@corvu-next/utils";
651
+ import { Dynamic as Dynamic2 } from "@corvu-next/utils/dynamic";
652
+ import createOnce from "@corvu-next/utils/create/once";
653
+
654
+ // src/panelContext.ts
655
+ import { createContext as createContext2, useContext as useContext2 } from "solid-js";
656
+ import {
657
+ createKeyedContext as createKeyedContext2,
658
+ useKeyedContext as useKeyedContext2
659
+ } from "@corvu-next/utils/create/keyedContext";
660
+ var ResizablePanelContext = createContext2(null);
661
+ var createResizablePanelContext = (contextId) => {
662
+ if (contextId === void 0) return ResizablePanelContext;
663
+ const context = createKeyedContext2(
664
+ `resizable-panel-${contextId}`
665
+ );
666
+ return context;
667
+ };
668
+ var useResizablePanelContext = (contextId) => {
669
+ if (contextId === void 0) {
670
+ const context2 = useContext2(ResizablePanelContext);
671
+ if (!context2) {
672
+ throw new Error(
673
+ "[corvu]: Resizable panel context not found. Make sure to call usePanelContext under <Resizable.Panel>"
674
+ );
675
+ }
676
+ return context2;
677
+ }
678
+ const context = useKeyedContext2(
679
+ `resizable-panel-${contextId}`
680
+ );
681
+ if (!context) {
682
+ throw new Error(
683
+ `[corvu]: Resizable context with id "${contextId}" not found. Make sure to call usePanelContext under <Resizable.Panel contextId="${contextId}">`
684
+ );
685
+ }
686
+ return context;
687
+ };
688
+
689
+ // src/Panel.tsx
690
+ import { mergeRefs as mergeRefs2 } from "@corvu-next/utils/reactivity";
691
+ var ResizablePanel = (props) => {
692
+ const defaultedProps = merge2(
693
+ {
694
+ initialSize: null,
695
+ minSize: 0,
696
+ maxSize: 1,
697
+ collapsible: false,
698
+ collapsedSize: 0,
699
+ collapseThreshold: 0.05,
700
+ panelId: createUniqueId()
701
+ },
702
+ props
703
+ );
704
+ const localProps = defaultedProps;
705
+ const otherProps = omit2(
706
+ defaultedProps,
707
+ "initialSize",
708
+ "minSize",
709
+ "maxSize",
710
+ "collapsible",
711
+ "collapsedSize",
712
+ "collapseThreshold",
713
+ "onResize",
714
+ "onCollapse",
715
+ "onExpand",
716
+ "contextId",
717
+ "panelId",
718
+ "ref",
719
+ "style",
720
+ "children"
721
+ );
722
+ const [ref, setRef] = createSignal2(null);
723
+ const context = createMemo2(
724
+ () => useInternalResizableContext(localProps.contextId)
725
+ );
726
+ const [panelInstance, setPanelInstance] = createSignal2(
727
+ null
728
+ );
729
+ createEffect2((prev) => {
730
+ const element = ref();
731
+ if (!element) return void 0;
732
+ const _context = context();
733
+ const instance = untrack(() => {
734
+ return _context.registerPanel({
735
+ id: localProps.panelId,
736
+ element,
737
+ initialSize: localProps.initialSize,
738
+ minSize: localProps.minSize,
739
+ maxSize: localProps.maxSize,
740
+ collapsible: localProps.collapsible,
741
+ collapsedSize: localProps.collapsedSize,
742
+ collapseThreshold: localProps.collapseThreshold,
743
+ onResize: localProps.onResize
744
+ });
745
+ });
746
+ setPanelInstance(instance);
747
+ return { instance, contextRef: context };
748
+ }, (_prev) => {
749
+ if (_prev) {
750
+ _prev.contextRef().unregisterPanel(_prev.instance.data.id);
751
+ }
752
+ });
753
+ const panelSize = () => {
754
+ const instance = panelInstance();
755
+ if (!instance) {
756
+ if (typeof localProps.initialSize === "number") {
757
+ return localProps.initialSize;
758
+ }
759
+ return 1;
760
+ }
761
+ return instance.size();
762
+ };
763
+ const collapsed = createMemo2((prev) => {
764
+ const instance = panelInstance();
765
+ if (localProps.collapsible !== true) {
766
+ return false;
767
+ }
768
+ const collapsed2 = instance ? instance.size() === resolveSize(localProps.collapsedSize, context().rootSize()) : false;
769
+ if (instance && prev !== collapsed2) {
770
+ if (collapsed2 && localProps.onCollapse !== void 0) {
771
+ localProps.onCollapse(instance.size());
772
+ } else if (!collapsed2 && localProps.onExpand !== void 0) {
773
+ localProps.onExpand(instance.size());
774
+ }
775
+ }
776
+ return collapsed2;
777
+ });
778
+ const resize2 = (size, strategy) => {
779
+ const instance = panelInstance();
780
+ if (!instance) {
781
+ return;
782
+ }
783
+ instance.resize(size, strategy ?? "both");
784
+ };
785
+ const collapse = (strategy) => {
786
+ const instance = panelInstance();
787
+ if (!instance) {
788
+ return;
789
+ }
790
+ instance.collapse(strategy ?? "both");
791
+ };
792
+ const expand = (strategy) => {
793
+ const instance = panelInstance();
794
+ if (!instance) {
795
+ return;
796
+ }
797
+ instance.expand(strategy ?? "both");
798
+ };
799
+ const childrenProps = {
800
+ get size() {
801
+ return panelSize();
802
+ },
803
+ get minSize() {
804
+ return localProps.minSize;
805
+ },
806
+ get maxSize() {
807
+ return localProps.maxSize;
808
+ },
809
+ get collapsible() {
810
+ return localProps.collapsible;
811
+ },
812
+ get collapsedSize() {
813
+ return localProps.collapsedSize;
814
+ },
815
+ get collapseThreshold() {
816
+ return localProps.collapseThreshold;
817
+ },
818
+ get collapsed() {
819
+ return collapsed();
820
+ },
821
+ resize: resize2,
822
+ collapse,
823
+ expand,
824
+ get panelId() {
825
+ return localProps.panelId;
826
+ }
827
+ };
828
+ const memoizedChildren = createOnce(() => localProps.children);
829
+ const resolveChildren = () => {
830
+ const children = memoizedChildren()();
831
+ if (isFunction(children)) {
832
+ return children(childrenProps);
833
+ }
834
+ return children;
835
+ };
836
+ const memoizedResizablePanel = createMemo2(() => {
837
+ const ResizablePanelContext2 = createResizablePanelContext(
838
+ localProps.contextId
839
+ );
840
+ return <ResizablePanelContext2
841
+ value={{
842
+ size: panelSize,
843
+ minSize: () => localProps.minSize,
844
+ maxSize: () => localProps.maxSize,
845
+ collapsible: () => localProps.collapsible,
846
+ collapsedSize: () => localProps.collapsedSize,
847
+ collapseThreshold: () => localProps.collapseThreshold,
848
+ collapsed,
849
+ resize: resize2,
850
+ collapse,
851
+ expand,
852
+ panelId: () => localProps.panelId
853
+ }}
854
+ >
855
+ <Dynamic2
856
+ as="div"
857
+ ref={mergeRefs2(setRef, localProps.ref)}
858
+ style={combineStyle2(
859
+ {
860
+ "flex-basis": panelSize() * 100 + "%"
861
+ },
862
+ localProps.style
863
+ )}
864
+ id={localProps.panelId}
865
+ data-collapsed={dataIf2(collapsed())}
866
+ data-expanded={dataIf2(
867
+ localProps.collapsible === true && !collapsed()
868
+ )}
869
+ data-orientation={context().orientation()}
870
+ data-corvu-resizable-panel=""
871
+ {...otherProps}
872
+ >
873
+ {untrack(() => resolveChildren())}
874
+ </Dynamic2>
875
+ </ResizablePanelContext2>;
876
+ });
877
+ return memoizedResizablePanel;
878
+ };
879
+ var Panel_default = ResizablePanel;
880
+
881
+ // src/Root.tsx
882
+ import {
883
+ combineStyle as combineStyle3,
884
+ sortByDocumentPosition
885
+ } from "@corvu-next/utils/dom";
886
+ import {
887
+ createEffect as createEffect3,
888
+ createMemo as createMemo3,
889
+ createSignal as createSignal3,
890
+ merge as merge3,
891
+ omit as omit3,
892
+ untrack as untrack2
893
+ } from "solid-js";
894
+
895
+ // src/lib/resize.ts
896
+ import "solid-js";
897
+ var getDistributablePercentage = (props) => {
898
+ let distributablePercentage = props.desiredPercentage >= 0 ? Infinity : -Infinity;
899
+ let newSizes = props.initialSizes;
900
+ for (const resizeAction of props.resizeActions) {
901
+ const desiredPercentage = resizeAction.negate !== true ? props.desiredPercentage : -props.desiredPercentage;
902
+ let [
903
+ distributedPercentagePreceding,
904
+ // eslint-disable-next-line prefer-const
905
+ distributedSizesPreceding,
906
+ // eslint-disable-next-line prefer-const
907
+ collapsedPreceding
908
+ ] = distributePercentage({
909
+ desiredPercentage,
910
+ side: "preceding",
911
+ panels: resizeAction.precedingPanels,
912
+ initialSizes: newSizes,
913
+ initialSizesStartIndex: 0,
914
+ collapsible: props.collapsible,
915
+ rootSize: props.resizableData.rootSize
916
+ });
917
+ let [
918
+ distributedPercentageFollowing,
919
+ // eslint-disable-next-line prefer-const
920
+ distributedSizesFollowing,
921
+ // eslint-disable-next-line prefer-const
922
+ collapsedFollowing
923
+ ] = distributePercentage({
924
+ desiredPercentage,
925
+ side: "following",
926
+ panels: resizeAction.followingPanels,
927
+ initialSizes: newSizes,
928
+ initialSizesStartIndex: resizeAction.precedingPanels.length,
929
+ collapsible: props.collapsible,
930
+ rootSize: props.resizableData.rootSize
931
+ });
932
+ if (resizeAction.negate === true) {
933
+ distributedPercentagePreceding = -distributedPercentagePreceding;
934
+ distributedPercentageFollowing = -distributedPercentageFollowing;
935
+ }
936
+ if (collapsedPreceding) {
937
+ distributedPercentageFollowing = distributedPercentagePreceding;
938
+ }
939
+ if (collapsedFollowing) {
940
+ distributedPercentagePreceding = distributedPercentageFollowing;
941
+ }
942
+ if (props.desiredPercentage >= 0) {
943
+ distributablePercentage = Math.min(
944
+ distributablePercentage,
945
+ Math.min(
946
+ distributedPercentagePreceding,
947
+ distributedPercentageFollowing
948
+ )
949
+ );
950
+ } else {
951
+ distributablePercentage = Math.max(
952
+ distributablePercentage,
953
+ Math.max(
954
+ distributedPercentagePreceding,
955
+ distributedPercentageFollowing
956
+ )
957
+ );
958
+ }
959
+ newSizes = [...distributedSizesPreceding, ...distributedSizesFollowing];
960
+ }
961
+ return distributablePercentage;
962
+ };
963
+ var distributePercentage = (props) => {
964
+ props.desiredPercentage = fixToPrecision(props.desiredPercentage);
965
+ const resizeDirection = getResizeDirection({
966
+ side: props.side,
967
+ desiredPercentage: props.desiredPercentage
968
+ });
969
+ let distributedPercentage = 0;
970
+ const distributedSizes = props.initialSizes.slice(
971
+ props.initialSizesStartIndex,
972
+ props.initialSizesStartIndex + props.panels.length
973
+ );
974
+ for (let i = props.side === "preceding" ? props.panels.length - 1 : 0; props.side === "preceding" ? i >= 0 : i < props.panels.length; props.side === "preceding" ? i-- : i++) {
975
+ const panel2 = props.panels[i];
976
+ const panelSize2 = props.initialSizes[i + props.initialSizesStartIndex];
977
+ const collapsedSize2 = resolveSize(
978
+ panel2.data.collapsedSize ?? 0,
979
+ props.rootSize
980
+ );
981
+ if (panel2.data.collapsible && panelSize2 === collapsedSize2) continue;
982
+ const availablePercentage2 = fixToPrecision(
983
+ props.desiredPercentage - distributedPercentage
984
+ );
985
+ if (availablePercentage2 === 0) break;
986
+ switch (resizeDirection) {
987
+ case "precedingDecreasing": {
988
+ const minSize2 = resolveSize(panel2.data.minSize, props.rootSize);
989
+ distributedSizes[i] = Math.max(minSize2, panelSize2 + availablePercentage2);
990
+ distributedPercentage += distributedSizes[i] - panelSize2;
991
+ break;
992
+ }
993
+ case "followingDecreasing": {
994
+ const minSize2 = resolveSize(panel2.data.minSize, props.rootSize);
995
+ distributedSizes[i] = Math.max(minSize2, panelSize2 - availablePercentage2);
996
+ distributedPercentage -= distributedSizes[i] - panelSize2;
997
+ break;
998
+ }
999
+ case "precedingIncreasing": {
1000
+ const maxSize = resolveSize(panel2.data.maxSize, props.rootSize);
1001
+ distributedSizes[i] = Math.min(maxSize, panelSize2 + availablePercentage2);
1002
+ distributedPercentage += distributedSizes[i] - panelSize2;
1003
+ break;
1004
+ }
1005
+ case "followingIncreasing": {
1006
+ const maxSize = resolveSize(panel2.data.maxSize, props.rootSize);
1007
+ distributedSizes[i] = Math.min(maxSize, panelSize2 - availablePercentage2);
1008
+ distributedPercentage -= distributedSizes[i] - panelSize2;
1009
+ break;
1010
+ }
1011
+ }
1012
+ }
1013
+ distributedPercentage = fixToPrecision(distributedPercentage);
1014
+ if (!props.collapsible || distributedPercentage === props.desiredPercentage) {
1015
+ return [distributedPercentage, distributedSizes, false];
1016
+ }
1017
+ const panelIndex = props.side === "preceding" ? props.panels.length - 1 : 0;
1018
+ const panel = props.panels[panelIndex];
1019
+ if (!panel.data.collapsible) {
1020
+ return [distributedPercentage, distributedSizes, false];
1021
+ }
1022
+ const availablePercentage = fixToPrecision(
1023
+ props.desiredPercentage - distributedPercentage
1024
+ );
1025
+ let collapsed = false;
1026
+ const panelSize = props.initialSizes[panelIndex + props.initialSizesStartIndex];
1027
+ const minSize = resolveSize(panel.data.minSize, props.rootSize);
1028
+ const collapsedSize = resolveSize(
1029
+ panel.data.collapsedSize ?? 0,
1030
+ props.rootSize
1031
+ );
1032
+ const collapseThreshold = Math.min(
1033
+ resolveSize(panel.data.collapseThreshold ?? 0, props.rootSize),
1034
+ minSize - collapsedSize
1035
+ );
1036
+ const isCollapsed = panelSize === collapsedSize;
1037
+ if (resizeDirection === "precedingDecreasing" && !isCollapsed && Math.abs(availablePercentage) >= collapseThreshold) {
1038
+ distributedPercentage -= distributedSizes[panelIndex] - panelSize;
1039
+ distributedSizes[panelIndex] = collapsedSize;
1040
+ distributedPercentage += distributedSizes[panelIndex] - panelSize;
1041
+ collapsed = true;
1042
+ } else if (resizeDirection === "precedingIncreasing" && isCollapsed && Math.abs(availablePercentage) >= collapseThreshold) {
1043
+ const minSize2 = resolveSize(panel.data.minSize, props.rootSize);
1044
+ distributedSizes[panelIndex] = minSize2;
1045
+ if (Math.abs(availablePercentage) >= minSize2 - collapsedSize) {
1046
+ const maxSize = resolveSize(panel.data.maxSize, props.rootSize);
1047
+ distributedSizes[panelIndex] = Math.min(
1048
+ maxSize,
1049
+ panelSize + availablePercentage
1050
+ );
1051
+ } else {
1052
+ collapsed = true;
1053
+ }
1054
+ distributedPercentage += distributedSizes[panelIndex] - panelSize;
1055
+ } else if (resizeDirection === "followingDecreasing" && !isCollapsed && Math.abs(availablePercentage) >= collapseThreshold) {
1056
+ distributedPercentage += distributedSizes[panelIndex] - panelSize;
1057
+ distributedSizes[panelIndex] = collapsedSize;
1058
+ distributedPercentage -= distributedSizes[panelIndex] - panelSize;
1059
+ collapsed = true;
1060
+ } else if (resizeDirection === "followingIncreasing" && isCollapsed && Math.abs(availablePercentage) >= collapseThreshold) {
1061
+ const minSize2 = resolveSize(panel.data.minSize, props.rootSize);
1062
+ distributedSizes[panelIndex] = minSize2;
1063
+ if (Math.abs(availablePercentage) >= minSize2 - collapsedSize) {
1064
+ const maxSize = resolveSize(panel.data.maxSize, props.rootSize);
1065
+ distributedSizes[panelIndex] = Math.min(
1066
+ maxSize,
1067
+ panelSize - availablePercentage
1068
+ );
1069
+ } else {
1070
+ collapsed = true;
1071
+ }
1072
+ distributedPercentage -= distributedSizes[panelIndex] - panelSize;
1073
+ }
1074
+ return [distributedPercentage, distributedSizes, collapsed];
1075
+ };
1076
+ var getResizeDirection = (props) => {
1077
+ switch (props.side) {
1078
+ case "preceding":
1079
+ return props.desiredPercentage >= 0 ? "precedingIncreasing" : "precedingDecreasing";
1080
+ case "following":
1081
+ return props.desiredPercentage >= 0 ? "followingDecreasing" : "followingIncreasing";
1082
+ }
1083
+ };
1084
+ var resize = (props) => {
1085
+ let newSizes = props.initialSizes;
1086
+ for (const resizeAction of props.resizeActions) {
1087
+ const [, distributedSizesPreceding] = distributePercentage({
1088
+ desiredPercentage: resizeAction.deltaPercentage,
1089
+ side: "preceding",
1090
+ panels: resizeAction.precedingPanels,
1091
+ initialSizes: newSizes,
1092
+ initialSizesStartIndex: 0,
1093
+ collapsible: props.collapsible,
1094
+ rootSize: props.resizableData.rootSize
1095
+ });
1096
+ const [, distributedSizesFollowing] = distributePercentage({
1097
+ desiredPercentage: resizeAction.deltaPercentage,
1098
+ side: "following",
1099
+ panels: resizeAction.followingPanels,
1100
+ initialSizes: newSizes,
1101
+ initialSizesStartIndex: resizeAction.precedingPanels.length,
1102
+ collapsible: props.collapsible,
1103
+ rootSize: props.resizableData.rootSize
1104
+ });
1105
+ newSizes = [...distributedSizesPreceding, ...distributedSizesFollowing];
1106
+ }
1107
+ newSizes = newSizes.map(fixToPrecision);
1108
+ const totalSize = newSizes.reduce((totalSize2, size) => totalSize2 + size, 0);
1109
+ if (totalSize !== 1) {
1110
+ const offset = totalSize - 1;
1111
+ const offsetPerPanel = offset / newSizes.length;
1112
+ newSizes = newSizes.map((size) => size - offsetPerPanel);
1113
+ }
1114
+ props.resizableData.setSizes(newSizes.map(fixToPrecision));
1115
+ };
1116
+ var resizePanel = (props) => {
1117
+ let [precedingPanels, followingPanels] = splitPanels({
1118
+ panels: props.panels,
1119
+ focusedElement: props.panel.data.element
1120
+ });
1121
+ const panelIndex = props.panels.indexOf(props.panel);
1122
+ if (panelIndex === 0) {
1123
+ props.strategy = "following";
1124
+ } else if (panelIndex === props.panels.length - 1) {
1125
+ props.strategy = "preceding";
1126
+ }
1127
+ if (props.strategy === "both") {
1128
+ const precedingPanelsIncluding = [...precedingPanels, props.panel];
1129
+ const followingPanelsIncluding = [props.panel, ...followingPanels];
1130
+ const distributablePercentage = getDistributablePercentage({
1131
+ desiredPercentage: props.deltaPercentage / 2,
1132
+ initialSizes: props.initialSizes,
1133
+ collapsible: true,
1134
+ resizeActions: [
1135
+ {
1136
+ precedingPanels: precedingPanelsIncluding,
1137
+ followingPanels
1138
+ },
1139
+ {
1140
+ precedingPanels,
1141
+ followingPanels: followingPanelsIncluding,
1142
+ negate: true
1143
+ }
1144
+ ],
1145
+ resizableData: {
1146
+ rootSize: props.resizableData.rootSize
1147
+ }
1148
+ });
1149
+ resize({
1150
+ initialSizes: props.initialSizes,
1151
+ collapsible: true,
1152
+ resizeActions: [
1153
+ {
1154
+ precedingPanels: precedingPanelsIncluding,
1155
+ followingPanels,
1156
+ deltaPercentage: distributablePercentage
1157
+ },
1158
+ {
1159
+ precedingPanels,
1160
+ followingPanels: followingPanelsIncluding,
1161
+ deltaPercentage: -distributablePercentage
1162
+ }
1163
+ ],
1164
+ resizableData: props.resizableData
1165
+ });
1166
+ } else {
1167
+ precedingPanels = props.strategy === "preceding" ? precedingPanels : [...precedingPanels, props.panel];
1168
+ followingPanels = props.strategy === "following" ? followingPanels : [props.panel, ...followingPanels];
1169
+ if (props.strategy === "preceding") {
1170
+ props.deltaPercentage = -props.deltaPercentage;
1171
+ }
1172
+ const distributablePercentage = getDistributablePercentage({
1173
+ desiredPercentage: props.deltaPercentage,
1174
+ initialSizes: props.initialSizes,
1175
+ collapsible: props.collapsible,
1176
+ resizeActions: [
1177
+ {
1178
+ precedingPanels,
1179
+ followingPanels
1180
+ }
1181
+ ],
1182
+ resizableData: {
1183
+ rootSize: props.resizableData.rootSize
1184
+ }
1185
+ });
1186
+ resize({
1187
+ initialSizes: props.initialSizes,
1188
+ collapsible: true,
1189
+ resizeActions: [
1190
+ {
1191
+ precedingPanels,
1192
+ followingPanels,
1193
+ deltaPercentage: distributablePercentage
1194
+ }
1195
+ ],
1196
+ resizableData: props.resizableData
1197
+ });
1198
+ }
1199
+ };
1200
+ var deltaResize = (props) => {
1201
+ if (props.altKey && props.panels.length > 2) {
1202
+ let panelIndex = props.panels.filter(
1203
+ (panel2) => !!(props.handle.compareDocumentPosition(panel2.data.element) & Node.DOCUMENT_POSITION_PRECEDING)
1204
+ ).length - 1;
1205
+ const isPrecedingHandle = panelIndex === 0;
1206
+ if (isPrecedingHandle) {
1207
+ panelIndex++;
1208
+ props.deltaPercentage = -props.deltaPercentage;
1209
+ }
1210
+ const panel = props.panels[panelIndex];
1211
+ const panelSize = props.initialSizes[panelIndex];
1212
+ const minDelta = resolveSize(panel.data.minSize, props.resizableData.rootSize) - panelSize;
1213
+ const maxDelta = resolveSize(panel.data.maxSize, props.resizableData.rootSize) - panelSize;
1214
+ const cappedDeltaPercentage = Math.max(minDelta, Math.min(props.deltaPercentage * 2, maxDelta)) / 2;
1215
+ const [precedingPanels, followingPanels] = splitPanels({
1216
+ panels: props.panels,
1217
+ focusedElement: panel.data.element
1218
+ });
1219
+ const precedingPanelsIncluding = [...precedingPanels, panel];
1220
+ const followingPanelsIncluding = [panel, ...followingPanels];
1221
+ const distributablePercentage = getDistributablePercentage({
1222
+ desiredPercentage: cappedDeltaPercentage,
1223
+ initialSizes: props.initialSizes,
1224
+ collapsible: false,
1225
+ resizeActions: [
1226
+ {
1227
+ precedingPanels: precedingPanelsIncluding,
1228
+ followingPanels
1229
+ },
1230
+ {
1231
+ precedingPanels,
1232
+ followingPanels: followingPanelsIncluding,
1233
+ negate: true
1234
+ }
1235
+ ],
1236
+ resizableData: {
1237
+ rootSize: props.resizableData.rootSize
1238
+ }
1239
+ });
1240
+ if (props.resizableData.handleCursorStyle === true) {
1241
+ handleResizeConstraints({
1242
+ orientation: props.resizableData.orientation,
1243
+ desiredPercentage: props.deltaPercentage,
1244
+ distributablePercentage,
1245
+ revertConstraints: isPrecedingHandle
1246
+ });
1247
+ }
1248
+ resize({
1249
+ initialSizes: props.initialSizes,
1250
+ collapsible: false,
1251
+ resizeActions: [
1252
+ {
1253
+ precedingPanels: precedingPanelsIncluding,
1254
+ followingPanels,
1255
+ deltaPercentage: distributablePercentage
1256
+ },
1257
+ {
1258
+ precedingPanels,
1259
+ followingPanels: followingPanelsIncluding,
1260
+ deltaPercentage: -distributablePercentage
1261
+ }
1262
+ ],
1263
+ resizableData: props.resizableData
1264
+ });
1265
+ } else {
1266
+ const [precedingPanels, followingPanels] = splitPanels({
1267
+ panels: props.panels,
1268
+ focusedElement: props.handle
1269
+ });
1270
+ const distributablePercentage = getDistributablePercentage({
1271
+ desiredPercentage: props.deltaPercentage,
1272
+ initialSizes: props.initialSizes,
1273
+ collapsible: true,
1274
+ resizeActions: [
1275
+ {
1276
+ precedingPanels,
1277
+ followingPanels
1278
+ }
1279
+ ],
1280
+ resizableData: {
1281
+ rootSize: props.resizableData.rootSize
1282
+ }
1283
+ });
1284
+ resize({
1285
+ initialSizes: props.initialSizes,
1286
+ collapsible: true,
1287
+ resizeActions: [
1288
+ {
1289
+ precedingPanels,
1290
+ followingPanels,
1291
+ deltaPercentage: distributablePercentage
1292
+ }
1293
+ ],
1294
+ resizableData: props.resizableData
1295
+ });
1296
+ if (props.resizableData.handleCursorStyle) {
1297
+ const fixedDesiredPercentage = fixToPrecision(props.deltaPercentage);
1298
+ const fixedDistributablePercentage = fixToPrecision(
1299
+ distributablePercentage
1300
+ );
1301
+ let betweenCollapse = false;
1302
+ const precedingPanel = precedingPanels[precedingPanels.length - 1];
1303
+ if (precedingPanel.data.collapsible) {
1304
+ const precedingCollapsedSize = resolveSize(
1305
+ precedingPanel.data.collapsedSize ?? 0,
1306
+ props.resizableData.rootSize
1307
+ );
1308
+ if (precedingPanel.size() === precedingCollapsedSize && fixedDesiredPercentage > fixedDistributablePercentage || precedingPanel.size() !== precedingCollapsedSize && fixedDesiredPercentage < fixedDistributablePercentage) {
1309
+ betweenCollapse = true;
1310
+ }
1311
+ }
1312
+ const followingPanel = followingPanels[0];
1313
+ if (followingPanel.data.collapsible) {
1314
+ const followingCollapsedSize = resolveSize(
1315
+ followingPanel.data.collapsedSize ?? 0,
1316
+ props.resizableData.rootSize
1317
+ );
1318
+ if (followingPanel.size() === followingCollapsedSize && fixedDesiredPercentage < fixedDistributablePercentage || followingPanel.size() !== followingCollapsedSize && fixedDesiredPercentage > fixedDistributablePercentage) {
1319
+ betweenCollapse = true;
1320
+ }
1321
+ }
1322
+ handleResizeConstraints({
1323
+ orientation: props.resizableData.orientation,
1324
+ desiredPercentage: props.deltaPercentage,
1325
+ distributablePercentage,
1326
+ betweenCollapse
1327
+ });
1328
+ }
1329
+ }
1330
+ };
1331
+
1332
+ // src/Root.tsx
1333
+ import { Dynamic as Dynamic3 } from "@corvu-next/utils/dynamic";
1334
+ import { isFunction as isFunction2 } from "@corvu-next/utils";
1335
+ import createControllableSignal from "@corvu-next/utils/create/controllableSignal";
1336
+ import createOnce2 from "@corvu-next/utils/create/once";
1337
+ import createSize from "@corvu-next/utils/create/size";
1338
+ import { mergeRefs as mergeRefs3 } from "@corvu-next/utils/reactivity";
1339
+ var ResizableRoot = (props) => {
1340
+ const defaultedProps = merge3(
1341
+ {
1342
+ orientation: "horizontal",
1343
+ initialSizes: [],
1344
+ keyboardDelta: 0.1,
1345
+ handleCursorStyle: true
1346
+ },
1347
+ props
1348
+ );
1349
+ const localProps = defaultedProps;
1350
+ const otherProps = omit3(
1351
+ defaultedProps,
1352
+ "orientation",
1353
+ "sizes",
1354
+ "onSizesChange",
1355
+ "initialSizes",
1356
+ "keyboardDelta",
1357
+ "handleCursorStyle",
1358
+ "contextId",
1359
+ "ref",
1360
+ "style",
1361
+ "children"
1362
+ );
1363
+ const [sizes, setSizes] = createControllableSignal({
1364
+ value: () => localProps.sizes,
1365
+ initialValue: [],
1366
+ onChange: localProps.onSizesChange
1367
+ });
1368
+ const [ref, setRef] = createSignal3(null);
1369
+ const rootSize = createSize({
1370
+ element: ref,
1371
+ dimension: () => localProps.orientation === "horizontal" ? "width" : "height"
1372
+ });
1373
+ const [panels, setPanels] = createSignal3([]);
1374
+ const sizesToIds = [];
1375
+ const registerPanel = (panelData) => {
1376
+ const _panels = panels();
1377
+ const panelIndex = _panels.filter(
1378
+ (panel2) => !!(panelData.element.compareDocumentPosition(panel2.data.element) & Node.DOCUMENT_POSITION_PRECEDING)
1379
+ ).length;
1380
+ const idExists = sizesToIds[panelIndex] === void 0 || sizesToIds[panelIndex] === panelData.id;
1381
+ const sizeExists = sizes()[panelIndex] !== void 0;
1382
+ let panelSize = null;
1383
+ if (panelData.initialSize !== null) {
1384
+ panelSize = resolveSize(panelData.initialSize, rootSize());
1385
+ } else if (localProps.initialSizes[panelIndex] !== void 0 && idExists) {
1386
+ panelSize = resolveSize(localProps.initialSizes[panelIndex], rootSize());
1387
+ }
1388
+ panelSize = panelSize ?? 0.5;
1389
+ setSizes((sizes2) => {
1390
+ let newSizes = [...sizes2];
1391
+ const previousTotalSize = newSizes.reduce(
1392
+ (totalSize, size) => totalSize + size,
1393
+ 0
1394
+ );
1395
+ if ((idExists && !sizeExists || !idExists) && previousTotalSize === 1) {
1396
+ const offsetPerPanel = panelSize / newSizes.length;
1397
+ newSizes = newSizes.map((size) => size - offsetPerPanel);
1398
+ }
1399
+ if (idExists) {
1400
+ if (!sizeExists) {
1401
+ newSizes[panelIndex] = panelSize;
1402
+ }
1403
+ sizesToIds[panelIndex] = panelData.id;
1404
+ } else {
1405
+ newSizes.splice(panelIndex, 0, panelSize);
1406
+ sizesToIds.splice(panelIndex, 0, panelData.id);
1407
+ }
1408
+ return newSizes;
1409
+ });
1410
+ const panelSizeMemo = createMemo3(() => {
1411
+ const index = sizesToIds.indexOf(panelData.id);
1412
+ return sizes()[index];
1413
+ });
1414
+ createEffect3(() => panelData.onResize?.(panelSizeMemo()));
1415
+ const panel = {
1416
+ data: panelData,
1417
+ size: panelSizeMemo,
1418
+ resize: (size, strategy) => resize2(sizesToIds.indexOf(panelData.id), size, strategy),
1419
+ collapse: (strategy) => collapse(sizesToIds.indexOf(panelData.id), strategy),
1420
+ expand: (strategy) => expand(sizesToIds.indexOf(panelData.id), strategy)
1421
+ };
1422
+ setPanels((panels2) => {
1423
+ const newPanels = [...panels2];
1424
+ newPanels.push(panel);
1425
+ newPanels.sort(
1426
+ (a, b) => sortByDocumentPosition(a.data.element, b.data.element)
1427
+ );
1428
+ return newPanels;
1429
+ });
1430
+ return panel;
1431
+ };
1432
+ const unregisterPanel = (id) => {
1433
+ setPanels((panels2) => panels2.filter((panel) => panel.data.id !== id));
1434
+ const panelSizeIndex = sizesToIds.indexOf(id);
1435
+ sizesToIds.splice(panelSizeIndex, 1);
1436
+ setSizes((sizes2) => {
1437
+ let newSizes = [...sizes2];
1438
+ newSizes.splice(panelSizeIndex, 1);
1439
+ const totalSize = newSizes.reduce(
1440
+ (totalSize2, size) => totalSize2 + size,
1441
+ 0
1442
+ );
1443
+ const offset = totalSize - 1;
1444
+ const offsetPerPanel = offset / newSizes.length;
1445
+ newSizes = newSizes.map((size) => size + offsetPerPanel);
1446
+ return newSizes;
1447
+ });
1448
+ };
1449
+ createEffect3(() => {
1450
+ if (localProps.onSizesChange !== void 0) {
1451
+ localProps.onSizesChange(sizes());
1452
+ }
1453
+ });
1454
+ const resize2 = (panelIndex, size, strategy) => {
1455
+ untrack2(() => {
1456
+ const panel = panels()[panelIndex];
1457
+ if (!panel) return;
1458
+ const minSize = resolveSize(panel.data.minSize, rootSize());
1459
+ const maxSize = resolveSize(panel.data.maxSize, rootSize());
1460
+ const newSize = resolveSize(size, rootSize());
1461
+ const allowedSize = Math.max(minSize, Math.min(newSize, maxSize));
1462
+ const deltaPercentage = allowedSize - sizes()[panelIndex];
1463
+ resizePanel({
1464
+ deltaPercentage,
1465
+ strategy: strategy ?? "both",
1466
+ panel,
1467
+ panels: panels(),
1468
+ initialSizes: panels().map((panel2) => panel2.size()),
1469
+ collapsible: false,
1470
+ resizableData: {
1471
+ rootSize: rootSize(),
1472
+ orientation: localProps.orientation,
1473
+ setSizes
1474
+ }
1475
+ });
1476
+ });
1477
+ };
1478
+ const collapse = (panelIndex, strategy) => {
1479
+ untrack2(() => {
1480
+ const panel = panels()[panelIndex];
1481
+ if (!panel) return;
1482
+ const panelSize = sizes()[panelIndex];
1483
+ const collapsedSize = resolveSize(
1484
+ panel.data.collapsedSize ?? 0,
1485
+ rootSize()
1486
+ );
1487
+ if (!panel.data.collapsible || panelSize === collapsedSize) return;
1488
+ const deltaPercentage = collapsedSize - panelSize;
1489
+ resizePanel({
1490
+ deltaPercentage,
1491
+ strategy: strategy ?? "both",
1492
+ panel,
1493
+ panels: panels(),
1494
+ initialSizes: panels().map((panel2) => panel2.size()),
1495
+ collapsible: true,
1496
+ resizableData: {
1497
+ rootSize: rootSize(),
1498
+ orientation: localProps.orientation,
1499
+ setSizes
1500
+ }
1501
+ });
1502
+ });
1503
+ };
1504
+ const expand = (panelIndex, strategy) => {
1505
+ untrack2(() => {
1506
+ const panel = panels()[panelIndex];
1507
+ if (!panel) return;
1508
+ const panelSize = sizes()[panelIndex];
1509
+ const collapsedSize = resolveSize(
1510
+ panel.data.collapsedSize ?? 0,
1511
+ rootSize()
1512
+ );
1513
+ if (!panel.data.collapsible || panelSize !== collapsedSize) return;
1514
+ const minSize = resolveSize(panel.data.minSize, rootSize());
1515
+ const deltaPercentage = minSize - panelSize;
1516
+ resizePanel({
1517
+ deltaPercentage,
1518
+ strategy: strategy ?? "both",
1519
+ panel,
1520
+ panels: panels(),
1521
+ initialSizes: panels().map((panel2) => panel2.size()),
1522
+ collapsible: true,
1523
+ resizableData: {
1524
+ rootSize: rootSize(),
1525
+ orientation: localProps.orientation,
1526
+ setSizes
1527
+ }
1528
+ });
1529
+ });
1530
+ };
1531
+ let initialSizes = null;
1532
+ let altKeyCache2 = false;
1533
+ const onDrag = (handle, delta, altKey) => {
1534
+ if (initialSizes === null || altKeyCache2 !== altKey) {
1535
+ initialSizes = panels().map((panel) => panel.size());
1536
+ altKeyCache2 = altKey;
1537
+ }
1538
+ deltaResize({
1539
+ deltaPercentage: delta / rootSize(),
1540
+ altKey,
1541
+ handle,
1542
+ panels: panels(),
1543
+ initialSizes,
1544
+ resizableData: {
1545
+ rootSize: rootSize(),
1546
+ handleCursorStyle: localProps.handleCursorStyle,
1547
+ orientation: localProps.orientation,
1548
+ setSizes
1549
+ }
1550
+ });
1551
+ };
1552
+ const onKeyDown = (handle, event, altKey) => {
1553
+ if (event.key === "Enter") {
1554
+ const [precedingPanels, followingPanels] = splitPanels({
1555
+ panels: panels(),
1556
+ focusedElement: handle
1557
+ });
1558
+ let collapsiblePanel = precedingPanels[precedingPanels.length - 1];
1559
+ if (!collapsiblePanel || !collapsiblePanel.data.collapsible) {
1560
+ collapsiblePanel = followingPanels[0];
1561
+ if (!collapsiblePanel || !collapsiblePanel.data.collapsible) return;
1562
+ }
1563
+ const size = collapsiblePanel.size();
1564
+ const collapsedSize = resolveSize(
1565
+ collapsiblePanel.data.collapsedSize ?? 0,
1566
+ rootSize()
1567
+ );
1568
+ if (size === collapsedSize) {
1569
+ collapsiblePanel.expand("following");
1570
+ } else {
1571
+ collapsiblePanel.collapse("following");
1572
+ }
1573
+ return;
1574
+ }
1575
+ let deltaPercentage = null;
1576
+ if (localProps.orientation === "horizontal" && event.key === "ArrowLeft" || localProps.orientation === "vertical" && event.key === "ArrowUp" || event.key === "Home") {
1577
+ if (event.shiftKey || event.key === "Home") {
1578
+ deltaPercentage = -1;
1579
+ } else {
1580
+ deltaPercentage = -resolveSize(localProps.keyboardDelta, rootSize());
1581
+ }
1582
+ } else if (localProps.orientation === "horizontal" && event.key === "ArrowRight" || localProps.orientation === "vertical" && event.key === "ArrowDown" || event.key === "End") {
1583
+ if (event.shiftKey || event.key === "End") {
1584
+ deltaPercentage = 1;
1585
+ } else {
1586
+ deltaPercentage = resolveSize(localProps.keyboardDelta, rootSize());
1587
+ }
1588
+ }
1589
+ if (deltaPercentage === null) return;
1590
+ event.preventDefault();
1591
+ const initialSizes2 = panels().map((panel) => panel.size());
1592
+ deltaResize({
1593
+ deltaPercentage,
1594
+ altKey,
1595
+ handle,
1596
+ panels: panels(),
1597
+ initialSizes: initialSizes2,
1598
+ resizableData: {
1599
+ rootSize: rootSize(),
1600
+ handleCursorStyle: false,
1601
+ orientation: localProps.orientation,
1602
+ setSizes
1603
+ }
1604
+ });
1605
+ };
1606
+ const childrenProps = {
1607
+ get sizes() {
1608
+ return sizes();
1609
+ },
1610
+ setSizes,
1611
+ get orientation() {
1612
+ return localProps.orientation;
1613
+ },
1614
+ get keyboardDelta() {
1615
+ return localProps.keyboardDelta;
1616
+ },
1617
+ get handleCursorStyle() {
1618
+ return localProps.handleCursorStyle;
1619
+ },
1620
+ resize: resize2,
1621
+ collapse,
1622
+ expand
1623
+ };
1624
+ const memoizedChildren = createOnce2(() => localProps.children);
1625
+ const resolveChildren = () => {
1626
+ const children = memoizedChildren()();
1627
+ if (isFunction2(children)) {
1628
+ return children(childrenProps);
1629
+ }
1630
+ return children;
1631
+ };
1632
+ const memoizedResizableRoot = createMemo3(() => {
1633
+ const ResizableContext2 = createResizableContext(localProps.contextId);
1634
+ const InternalResizableContext2 = createInternalResizableContext(
1635
+ localProps.contextId
1636
+ );
1637
+ return <ResizableContext2
1638
+ value={{
1639
+ sizes,
1640
+ setSizes,
1641
+ orientation: () => localProps.orientation,
1642
+ keyboardDelta: () => localProps.keyboardDelta,
1643
+ handleCursorStyle: () => localProps.handleCursorStyle,
1644
+ resize: resize2,
1645
+ collapse,
1646
+ expand
1647
+ }}
1648
+ >
1649
+ <InternalResizableContext2
1650
+ value={{
1651
+ sizes,
1652
+ setSizes,
1653
+ orientation: () => localProps.orientation,
1654
+ keyboardDelta: () => localProps.keyboardDelta,
1655
+ handleCursorStyle: () => localProps.handleCursorStyle,
1656
+ rootSize,
1657
+ panels,
1658
+ registerPanel,
1659
+ unregisterPanel,
1660
+ onDrag,
1661
+ onDragEnd: () => initialSizes = null,
1662
+ onKeyDown,
1663
+ resize: resize2,
1664
+ collapse,
1665
+ expand
1666
+ }}
1667
+ >
1668
+ <Dynamic3
1669
+ as="div"
1670
+ ref={mergeRefs3(setRef, localProps.ref)}
1671
+ style={combineStyle3(
1672
+ {
1673
+ display: "flex",
1674
+ "flex-direction": localProps.orientation === "horizontal" ? "row" : "column"
1675
+ },
1676
+ localProps.style
1677
+ )}
1678
+ data-orientation={localProps.orientation}
1679
+ data-corvu-resizable-root=""
1680
+ {...otherProps}
1681
+ >
1682
+ {untrack2(() => resolveChildren())}
1683
+ </Dynamic3>
1684
+ </InternalResizableContext2>
1685
+ </ResizableContext2>;
1686
+ });
1687
+ return memoizedResizableRoot;
1688
+ };
1689
+ var Root_default = ResizableRoot;
1690
+
1691
+ // src/index.ts
1692
+ var Resizable = Object.assign(Root_default, {
1693
+ Panel: Panel_default,
1694
+ Handle: Handle_default,
1695
+ useContext: useResizableContext,
1696
+ usePanelContext: useResizablePanelContext
1697
+ });
1698
+ var index_default = Resizable;
1699
+ export {
1700
+ Handle_default as Handle,
1701
+ Panel_default as Panel,
1702
+ Root_default as Root,
1703
+ index_default as default,
1704
+ useResizableContext as useContext,
1705
+ useResizablePanelContext as usePanelContext
1706
+ };