@mantine/hooks 9.3.1 → 9.4.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/cjs/use-click-outside/use-click-outside.cjs.map +1 -1
- package/cjs/use-clipboard/use-clipboard.cjs.map +1 -1
- package/cjs/use-collapse/use-collapse.cjs.map +1 -1
- package/cjs/use-collapse/use-horizontal-collapse.cjs.map +1 -1
- package/cjs/use-counter/use-counter.cjs.map +1 -1
- package/cjs/use-debounced-callback/use-debounced-callback.cjs.map +1 -1
- package/cjs/use-debounced-state/use-debounced-state.cjs.map +1 -1
- package/cjs/use-debounced-value/use-debounced-value.cjs.map +1 -1
- package/cjs/use-did-update/use-did-update.cjs.map +1 -1
- package/cjs/use-disclosure/use-disclosure.cjs.map +1 -1
- package/cjs/use-document-title/use-document-title.cjs.map +1 -1
- package/cjs/use-document-visibility/use-document-visibility.cjs.map +1 -1
- package/cjs/use-drag/use-drag.cjs.map +1 -1
- package/cjs/use-event-listener/use-event-listener.cjs.map +1 -1
- package/cjs/use-eye-dropper/use-eye-dropper.cjs.map +1 -1
- package/cjs/use-favicon/use-favicon.cjs.map +1 -1
- package/cjs/use-fetch/use-fetch.cjs.map +1 -1
- package/cjs/use-file-dialog/use-file-dialog.cjs.map +1 -1
- package/cjs/use-floating-window/use-floating-window.cjs.map +1 -1
- package/cjs/use-focus-return/use-focus-return.cjs.map +1 -1
- package/cjs/use-focus-trap/scope-tab.cjs.map +1 -1
- package/cjs/use-focus-trap/tabbable.cjs.map +1 -1
- package/cjs/use-focus-trap/use-focus-trap.cjs.map +1 -1
- package/cjs/use-focus-within/use-focus-within.cjs.map +1 -1
- package/cjs/use-force-update/use-force-update.cjs.map +1 -1
- package/cjs/use-fullscreen/use-fullscreen.cjs.map +1 -1
- package/cjs/use-hash/use-hash.cjs.map +1 -1
- package/cjs/use-headroom/use-headroom.cjs.map +1 -1
- package/cjs/use-hotkeys/parse-hotkey.cjs.map +1 -1
- package/cjs/use-hotkeys/use-hotkeys.cjs.map +1 -1
- package/cjs/use-hover/use-hover.cjs.map +1 -1
- package/cjs/use-id/use-id.cjs.map +1 -1
- package/cjs/use-idle/use-idle.cjs.map +1 -1
- package/cjs/use-in-viewport/use-in-viewport.cjs.map +1 -1
- package/cjs/use-input-state/use-input-state.cjs.map +1 -1
- package/cjs/use-intersection/use-intersection.cjs.map +1 -1
- package/cjs/use-interval/use-interval.cjs.map +1 -1
- package/cjs/use-is-first-render/use-is-first-render.cjs.map +1 -1
- package/cjs/use-list-state/use-list-state.cjs.map +1 -1
- package/cjs/use-local-storage/create-storage.cjs.map +1 -1
- package/cjs/use-local-storage/use-local-storage.cjs.map +1 -1
- package/cjs/use-logger/use-logger.cjs.map +1 -1
- package/cjs/use-long-press/use-long-press.cjs.map +1 -1
- package/cjs/use-map/use-map.cjs.map +1 -1
- package/cjs/use-mask/use-mask.cjs.map +1 -1
- package/cjs/use-media-query/use-media-query.cjs.map +1 -1
- package/cjs/use-merged-ref/use-merged-ref.cjs.map +1 -1
- package/cjs/use-mounted/use-mounted.cjs.map +1 -1
- package/cjs/use-mouse/use-mouse.cjs.map +1 -1
- package/cjs/use-move/use-move.cjs.map +1 -1
- package/cjs/use-mutation-observer/use-mutation-observer.cjs.map +1 -1
- package/cjs/use-network/use-network.cjs.map +1 -1
- package/cjs/use-orientation/use-orientation.cjs.map +1 -1
- package/cjs/use-os/use-os.cjs.map +1 -1
- package/cjs/use-page-leave/use-page-leave.cjs.map +1 -1
- package/cjs/use-pagination/use-pagination.cjs.map +1 -1
- package/cjs/use-previous/use-previous.cjs.map +1 -1
- package/cjs/use-queue/use-queue.cjs.map +1 -1
- package/cjs/use-radial-move/use-radial-move.cjs.map +1 -1
- package/cjs/use-resize-observer/use-resize-observer.cjs.map +1 -1
- package/cjs/use-roving-index/use-roving-index.cjs.map +1 -1
- package/cjs/use-scroll-direction/use-scroll-direction.cjs.map +1 -1
- package/cjs/use-scroll-into-view/use-scroll-into-view.cjs.map +1 -1
- package/cjs/use-scroll-spy/use-scroll-spy.cjs.map +1 -1
- package/cjs/use-scroller/use-scroller.cjs.map +1 -1
- package/cjs/use-selection/use-selection.cjs.map +1 -1
- package/cjs/use-session-storage/use-session-storage.cjs.map +1 -1
- package/cjs/use-set/use-set.cjs.map +1 -1
- package/cjs/use-set-state/use-set-state.cjs.map +1 -1
- package/cjs/use-shallow-effect/use-shallow-effect.cjs.map +1 -1
- package/cjs/use-splitter/use-splitter.cjs +250 -70
- package/cjs/use-splitter/use-splitter.cjs.map +1 -1
- package/cjs/use-state-history/use-state-history.cjs.map +1 -1
- package/cjs/use-text-selection/use-text-selection.cjs.map +1 -1
- package/cjs/use-throttled-callback/use-throttled-callback.cjs.map +1 -1
- package/cjs/use-throttled-state/use-throttled-state.cjs.map +1 -1
- package/cjs/use-throttled-value/use-throttled-value.cjs.map +1 -1
- package/cjs/use-timeout/use-timeout.cjs.map +1 -1
- package/cjs/use-toggle/use-toggle.cjs.map +1 -1
- package/cjs/use-uncontrolled/use-uncontrolled.cjs.map +1 -1
- package/cjs/use-validated-state/use-validated-state.cjs.map +1 -1
- package/cjs/use-viewport-size/use-viewport-size.cjs.map +1 -1
- package/cjs/use-window-event/use-window-event.cjs.map +1 -1
- package/cjs/use-window-scroll/use-window-scroll.cjs.map +1 -1
- package/cjs/utils/lower-first/lower-first.cjs.map +1 -1
- package/cjs/utils/random-id/random-id.cjs.map +1 -1
- package/cjs/utils/shallow-equal/shallow-equal.cjs.map +1 -1
- package/cjs/utils/upper-first/upper-first.cjs.map +1 -1
- package/cjs/utils/use-callback-ref/use-callback-ref.cjs.map +1 -1
- package/esm/use-clipboard/use-clipboard.mjs.map +1 -1
- package/esm/use-document-title/use-document-title.mjs.map +1 -1
- package/esm/use-eye-dropper/use-eye-dropper.mjs.map +1 -1
- package/esm/use-favicon/use-favicon.mjs.map +1 -1
- package/esm/use-fetch/use-fetch.mjs.map +1 -1
- package/esm/use-floating-window/use-floating-window.mjs.map +1 -1
- package/esm/use-focus-trap/scope-tab.mjs.map +1 -1
- package/esm/use-focus-trap/tabbable.mjs.map +1 -1
- package/esm/use-hotkeys/parse-hotkey.mjs.map +1 -1
- package/esm/use-hotkeys/use-hotkeys.mjs.map +1 -1
- package/esm/use-id/use-id.mjs.map +1 -1
- package/esm/use-local-storage/create-storage.mjs.map +1 -1
- package/esm/use-local-storage/use-local-storage.mjs.map +1 -1
- package/esm/use-mask/use-mask.mjs.map +1 -1
- package/esm/use-media-query/use-media-query.mjs.map +1 -1
- package/esm/use-move/use-move.mjs.map +1 -1
- package/esm/use-radial-move/use-radial-move.mjs.map +1 -1
- package/esm/use-scroll-into-view/use-scroll-into-view.mjs.map +1 -1
- package/esm/use-scroll-spy/use-scroll-spy.mjs.map +1 -1
- package/esm/use-scroller/use-scroller.mjs.map +1 -1
- package/esm/use-session-storage/use-session-storage.mjs.map +1 -1
- package/esm/use-set/use-set.mjs.map +1 -1
- package/esm/use-splitter/use-splitter.mjs +250 -70
- package/esm/use-splitter/use-splitter.mjs.map +1 -1
- package/esm/use-toggle/use-toggle.mjs.map +1 -1
- package/esm/utils/lower-first/lower-first.mjs.map +1 -1
- package/esm/utils/random-id/random-id.mjs.map +1 -1
- package/esm/utils/shallow-equal/shallow-equal.mjs.map +1 -1
- package/esm/utils/upper-first/upper-first.mjs.map +1 -1
- package/lib/index.d.mts +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/use-splitter/use-splitter.d.ts +54 -22
- package/package.json +1 -1
|
@@ -2,6 +2,102 @@
|
|
|
2
2
|
const require_use_uncontrolled = require("../use-uncontrolled/use-uncontrolled.cjs");
|
|
3
3
|
let react = require("react");
|
|
4
4
|
//#region packages/@mantine/hooks/src/use-splitter/use-splitter.ts
|
|
5
|
+
const PX_RE = /^(-?[\d.]+)px$/;
|
|
6
|
+
const REM_RE = /^(-?[\d.]+)rem$/;
|
|
7
|
+
const PERCENT_RE = /^(-?[\d.]+)%$/;
|
|
8
|
+
function isFixedSize(size) {
|
|
9
|
+
return typeof size === "string" && (PX_RE.test(size) || REM_RE.test(size));
|
|
10
|
+
}
|
|
11
|
+
function sizeMagnitude(size) {
|
|
12
|
+
return typeof size === "number" ? size : parseFloat(size);
|
|
13
|
+
}
|
|
14
|
+
function detectPixelMode(options) {
|
|
15
|
+
return options.panels.some((panel) => isFixedSize(panel.defaultSize) || isFixedSize(panel.min) || isFixedSize(panel.max) || isFixedSize(panel.collapseThreshold)) || isFixedSize(options.step) || isFixedSize(options.shiftStep) || (options.sizes?.some(isFixedSize) ?? false);
|
|
16
|
+
}
|
|
17
|
+
function getRootFontSize() {
|
|
18
|
+
if (typeof window === "undefined") return 16;
|
|
19
|
+
const fontSize = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
|
20
|
+
return Number.isFinite(fontSize) && fontSize > 0 ? fontSize : 16;
|
|
21
|
+
}
|
|
22
|
+
function resolveSize(size, pixelMode, containerPx, rootFontSize) {
|
|
23
|
+
if (!pixelMode) return sizeMagnitude(size);
|
|
24
|
+
if (typeof size === "number") return size / 100 * containerPx;
|
|
25
|
+
const percent = PERCENT_RE.exec(size);
|
|
26
|
+
if (percent) return parseFloat(percent[1]) / 100 * containerPx;
|
|
27
|
+
const rem = REM_RE.exec(size);
|
|
28
|
+
if (rem) return parseFloat(rem[1]) * rootFontSize;
|
|
29
|
+
const px = PX_RE.exec(size);
|
|
30
|
+
if (px) return parseFloat(px[1]);
|
|
31
|
+
return 0;
|
|
32
|
+
}
|
|
33
|
+
/** Down-scaling factor applied to fixed panes when their combined pixel size overflows the
|
|
34
|
+
* container, so they shrink to fit (matching `resolveWorkingSizes`). Returns `1` when nothing
|
|
35
|
+
* overflows or the layout is not in pixel mode. Encoders divide by this to invert the scaling and
|
|
36
|
+
* persist the original absolute sizes instead of the shrunk-to-fit ones. */
|
|
37
|
+
function getFixedScale(sizes, pixelMode, containerPx, rootFontSize) {
|
|
38
|
+
if (!pixelMode) return 1;
|
|
39
|
+
let fixedTotal = 0;
|
|
40
|
+
sizes.forEach((size) => {
|
|
41
|
+
if (isFixedSize(size)) fixedTotal += resolveSize(size, true, containerPx, rootFontSize);
|
|
42
|
+
});
|
|
43
|
+
return fixedTotal > containerPx && fixedTotal > 0 ? containerPx / fixedTotal : 1;
|
|
44
|
+
}
|
|
45
|
+
/** Resolves all sizes to pixels at once. Fixed panes get their absolute pixel size, flexible panes
|
|
46
|
+
* share the leftover space by their weight ratio – matching how the layout is rendered with
|
|
47
|
+
* `flex-grow`, so drag math operates on the same pixel sizes the user sees. */
|
|
48
|
+
function resolveWorkingSizes(sizes, pixelMode, containerPx, rootFontSize) {
|
|
49
|
+
if (!pixelMode) return sizes.map((size) => sizeMagnitude(size));
|
|
50
|
+
let fixedTotal = 0;
|
|
51
|
+
let flexibleWeight = 0;
|
|
52
|
+
sizes.forEach((size) => {
|
|
53
|
+
if (isFixedSize(size)) fixedTotal += resolveSize(size, true, containerPx, rootFontSize);
|
|
54
|
+
else flexibleWeight += sizeMagnitude(size);
|
|
55
|
+
});
|
|
56
|
+
const leftover = Math.max(0, containerPx - fixedTotal);
|
|
57
|
+
const fixedScale = getFixedScale(sizes, pixelMode, containerPx, rootFontSize);
|
|
58
|
+
return sizes.map((size) => {
|
|
59
|
+
if (isFixedSize(size)) return resolveSize(size, true, containerPx, rootFontSize) * fixedScale;
|
|
60
|
+
return flexibleWeight > 0 ? sizeMagnitude(size) / flexibleWeight * leftover : 0;
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function encodeSize(value, original, pixelMode, containerPx, rootFontSize, fixedScale = 1) {
|
|
64
|
+
if (!pixelMode) return typeof original === "string" && PERCENT_RE.test(original) ? `${value}%` : value;
|
|
65
|
+
if (typeof original === "number") return containerPx > 0 ? value / containerPx * 100 : original;
|
|
66
|
+
if (PERCENT_RE.test(original)) return `${containerPx > 0 ? value / containerPx * 100 : parseFloat(original)}%`;
|
|
67
|
+
const absolute = fixedScale > 0 ? value / fixedScale : value;
|
|
68
|
+
if (REM_RE.test(original)) return `${rootFontSize > 0 ? absolute / rootFontSize : 0}rem`;
|
|
69
|
+
return `${absolute}px`;
|
|
70
|
+
}
|
|
71
|
+
/** Encodes working pixel sizes back to raw sizes after a resize, keeping the unit each pane was
|
|
72
|
+
* declared in. Panes whose working size did not change keep their original raw value. When fixed
|
|
73
|
+
* panes overflow the container they render down-scaled, so their working sizes are scaled back up to
|
|
74
|
+
* absolute sizes (preserving their declared sizes). If the resize instead hands space to a flexible
|
|
75
|
+
* pane the overflow clears and the layout leaves the down-scaled regime: every pane is then encoded
|
|
76
|
+
* from its current working size – including untouched fixed panes (so they do not jump back to their
|
|
77
|
+
* over-sized value) and untouched flexible panes (so a pane that was squeezed to `0` does not keep a
|
|
78
|
+
* stale weight and steal the freed space on the next render). */
|
|
79
|
+
function encodeWorkingSizes(nextWorking, baseWorking, baseRaw, pixelMode, containerPx, rootFontSize) {
|
|
80
|
+
const fixedScale = getFixedScale(baseRaw, pixelMode, containerPx, rootFontSize);
|
|
81
|
+
let fixedWorkingSum = 0;
|
|
82
|
+
nextWorking.forEach((value, i) => {
|
|
83
|
+
if (isFixedSize(baseRaw[i])) fixedWorkingSum += value;
|
|
84
|
+
});
|
|
85
|
+
const overflowCleared = fixedScale < 1 && fixedWorkingSum < containerPx - 1e-6;
|
|
86
|
+
const encodeScale = overflowCleared ? 1 : fixedScale;
|
|
87
|
+
return nextWorking.map((value, i) => overflowCleared || Math.abs(value - baseWorking[i]) > 1e-6 ? encodeSize(value, baseRaw[i], pixelMode, containerPx, rootFontSize, encodeScale) : baseRaw[i]);
|
|
88
|
+
}
|
|
89
|
+
function resolvePanel(panel, pixelMode, containerPx, rootFontSize) {
|
|
90
|
+
return {
|
|
91
|
+
defaultSize: resolveSize(panel.defaultSize, pixelMode, containerPx, rootFontSize),
|
|
92
|
+
min: panel.min != null ? resolveSize(panel.min, pixelMode, containerPx, rootFontSize) : 0,
|
|
93
|
+
max: panel.max != null ? resolveSize(panel.max, pixelMode, containerPx, rootFontSize) : pixelMode ? containerPx : 100,
|
|
94
|
+
collapseThreshold: panel.collapseThreshold != null ? resolveSize(panel.collapseThreshold, pixelMode, containerPx, rootFontSize) : void 0,
|
|
95
|
+
collapsible: panel.collapsible
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function resolveStep(step, pixelMode, containerPx, rootFontSize) {
|
|
99
|
+
return resolveSize(step, pixelMode, containerPx, rootFontSize);
|
|
100
|
+
}
|
|
5
101
|
function clamp(value, min, max) {
|
|
6
102
|
return Math.min(Math.max(value, min), max);
|
|
7
103
|
}
|
|
@@ -9,7 +105,7 @@ function getMin(panel) {
|
|
|
9
105
|
return panel.min ?? 0;
|
|
10
106
|
}
|
|
11
107
|
function getMax(panel) {
|
|
12
|
-
return panel.max ??
|
|
108
|
+
return panel.max ?? Infinity;
|
|
13
109
|
}
|
|
14
110
|
function getCollapseThreshold(panel) {
|
|
15
111
|
return panel.collapseThreshold ?? getMin(panel);
|
|
@@ -20,7 +116,10 @@ function createInitialInternalState() {
|
|
|
20
116
|
handleIndex: -1,
|
|
21
117
|
startPointer: 0,
|
|
22
118
|
containerSize: 0,
|
|
119
|
+
rootFontSize: 16,
|
|
120
|
+
pixelMode: false,
|
|
23
121
|
startSizes: [],
|
|
122
|
+
startRaw: [],
|
|
24
123
|
preCollapseSizes: []
|
|
25
124
|
};
|
|
26
125
|
}
|
|
@@ -164,8 +263,9 @@ function applyConstraints(sizes, panels, handleIndex, delta, redistribute) {
|
|
|
164
263
|
return applyAdjacentOnly(sizes, panels, handleIndex, delta);
|
|
165
264
|
}
|
|
166
265
|
function useSplitter(options) {
|
|
167
|
-
const { panels, orientation = "horizontal", sizes: controlledSizes, onSizeChange, onCollapseChange, redistribute, step = 1, shiftStep = 10, dir = "ltr", enabled = true } = options;
|
|
168
|
-
const
|
|
266
|
+
const { panels, orientation = "horizontal", sizes: controlledSizes, onSizeChange, onCollapseChange, redistribute, step = 1, shiftStep = 10, dir = "ltr", resetOnDoubleClick = true, enabled = true } = options;
|
|
267
|
+
const pixelMode = detectPixelMode(options);
|
|
268
|
+
const defaultSizes = panels.map((panel) => panel.defaultSize);
|
|
169
269
|
const [currentSizes, setCurrentSizes] = require_use_uncontrolled.useUncontrolled({
|
|
170
270
|
value: controlledSizes,
|
|
171
271
|
defaultValue: defaultSizes,
|
|
@@ -173,63 +273,137 @@ function useSplitter(options) {
|
|
|
173
273
|
onChange: onSizeChange
|
|
174
274
|
});
|
|
175
275
|
const [activeHandle, setActiveHandle] = (0, react.useState)(-1);
|
|
276
|
+
const [containerSize, setContainerSize] = (0, react.useState)(0);
|
|
176
277
|
const optionsRef = (0, react.useRef)(options);
|
|
177
278
|
optionsRef.current = options;
|
|
178
279
|
const internalStateRef = (0, react.useRef)(createInitialInternalState());
|
|
179
280
|
const containerRef = (0, react.useRef)(null);
|
|
281
|
+
const containerSizeRef = (0, react.useRef)(0);
|
|
282
|
+
const rootFontSizeRef = (0, react.useRef)(16);
|
|
180
283
|
const documentControllerRef = (0, react.useRef)(null);
|
|
181
284
|
const frameRef = (0, react.useRef)(0);
|
|
182
285
|
const currentSizesRef = (0, react.useRef)(currentSizes);
|
|
183
286
|
currentSizesRef.current = currentSizes;
|
|
184
287
|
const preCollapseSizesRef = (0, react.useRef)(defaultSizes);
|
|
185
|
-
const collapsed = currentSizes.map((size) => size === 0);
|
|
288
|
+
const collapsed = currentSizes.map((size) => sizeMagnitude(size) === 0);
|
|
289
|
+
const measureContainer = (0, react.useCallback)(() => {
|
|
290
|
+
const node = containerRef.current;
|
|
291
|
+
if (!node) return 0;
|
|
292
|
+
const rect = node.getBoundingClientRect();
|
|
293
|
+
return (optionsRef.current.orientation ?? "horizontal") === "horizontal" ? rect.width : rect.height;
|
|
294
|
+
}, []);
|
|
186
295
|
const updateSizes = (0, react.useCallback)((newSizes) => {
|
|
187
296
|
currentSizesRef.current = newSizes;
|
|
188
297
|
setCurrentSizes(newSizes);
|
|
189
298
|
}, [setCurrentSizes]);
|
|
190
299
|
const collapsePanel = (0, react.useCallback)((panelIndex) => {
|
|
191
300
|
if (!panels[panelIndex]?.collapsible) return;
|
|
192
|
-
const
|
|
193
|
-
if (
|
|
194
|
-
|
|
195
|
-
const
|
|
196
|
-
const
|
|
197
|
-
|
|
301
|
+
const raw = currentSizesRef.current;
|
|
302
|
+
if (sizeMagnitude(raw[panelIndex]) === 0) return;
|
|
303
|
+
const container = pixelMode ? containerSizeRef.current || measureContainer() : 0;
|
|
304
|
+
const rootFontSize = rootFontSizeRef.current;
|
|
305
|
+
const working = resolveWorkingSizes(raw, pixelMode, container, rootFontSize);
|
|
306
|
+
preCollapseSizesRef.current = [...raw];
|
|
307
|
+
const freedSize = working[panelIndex];
|
|
308
|
+
working[panelIndex] = 0;
|
|
198
309
|
const neighbor = panelIndex === 0 ? 1 : panelIndex - 1;
|
|
199
|
-
|
|
200
|
-
updateSizes(
|
|
310
|
+
working[neighbor] += freedSize;
|
|
311
|
+
updateSizes(working.map((value, i) => encodeSize(value, raw[i], pixelMode, container, rootFontSize)));
|
|
201
312
|
onCollapseChange?.(panelIndex, true);
|
|
202
313
|
}, [
|
|
203
314
|
panels,
|
|
315
|
+
pixelMode,
|
|
316
|
+
measureContainer,
|
|
204
317
|
updateSizes,
|
|
205
318
|
onCollapseChange
|
|
206
319
|
]);
|
|
207
320
|
const expandPanel = (0, react.useCallback)((panelIndex) => {
|
|
208
321
|
if (!panels[panelIndex]?.collapsible) return;
|
|
209
|
-
const
|
|
210
|
-
if (
|
|
211
|
-
const
|
|
212
|
-
const
|
|
322
|
+
const raw = currentSizesRef.current;
|
|
323
|
+
if (sizeMagnitude(raw[panelIndex]) !== 0) return;
|
|
324
|
+
const container = pixelMode ? containerSizeRef.current || measureContainer() : 0;
|
|
325
|
+
const rootFontSize = rootFontSizeRef.current;
|
|
326
|
+
const working = resolveWorkingSizes(raw, pixelMode, container, rootFontSize);
|
|
327
|
+
const preCollapse = preCollapseSizesRef.current;
|
|
328
|
+
const restoreSize = resolveSize(preCollapse[panelIndex] != null && sizeMagnitude(preCollapse[panelIndex]) !== 0 ? preCollapse[panelIndex] : panels[panelIndex].defaultSize, pixelMode, container, rootFontSize);
|
|
213
329
|
const neighbor = panelIndex === 0 ? 1 : panelIndex - 1;
|
|
214
|
-
const
|
|
330
|
+
const neighborMin = panels[neighbor].min != null ? resolveSize(panels[neighbor].min, pixelMode, container, rootFontSize) : 0;
|
|
331
|
+
const available = Math.max(0, working[neighbor] - neighborMin);
|
|
215
332
|
const actualRestore = Math.min(restoreSize, available);
|
|
216
333
|
if (actualRestore <= 0) return;
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
updateSizes(
|
|
334
|
+
working[panelIndex] = actualRestore;
|
|
335
|
+
working[neighbor] -= actualRestore;
|
|
336
|
+
updateSizes(working.map((value, i) => encodeSize(value, raw[i], pixelMode, container, rootFontSize)));
|
|
220
337
|
onCollapseChange?.(panelIndex, false);
|
|
221
338
|
}, [
|
|
222
339
|
panels,
|
|
340
|
+
pixelMode,
|
|
341
|
+
measureContainer,
|
|
223
342
|
updateSizes,
|
|
224
343
|
onCollapseChange
|
|
225
344
|
]);
|
|
226
345
|
const toggleCollapsePanel = (0, react.useCallback)((panelIndex) => {
|
|
227
|
-
if (currentSizesRef.current[panelIndex] === 0) expandPanel(panelIndex);
|
|
346
|
+
if (sizeMagnitude(currentSizesRef.current[panelIndex]) === 0) expandPanel(panelIndex);
|
|
228
347
|
else collapsePanel(panelIndex);
|
|
229
348
|
}, [collapsePanel, expandPanel]);
|
|
349
|
+
const emitCollapseTransitions = (0, react.useCallback)((prev, next, indices, preCollapseSnapshot) => {
|
|
350
|
+
const onChange = optionsRef.current.onCollapseChange;
|
|
351
|
+
for (const idx of indices) {
|
|
352
|
+
const wasCollapsed = sizeMagnitude(prev[idx]) === 0;
|
|
353
|
+
const nowCollapsed = next[idx] === 0;
|
|
354
|
+
if (!wasCollapsed && nowCollapsed) {
|
|
355
|
+
preCollapseSizesRef.current = [...preCollapseSnapshot];
|
|
356
|
+
onChange?.(idx, true);
|
|
357
|
+
} else if (wasCollapsed && !nowCollapsed) onChange?.(idx, false);
|
|
358
|
+
}
|
|
359
|
+
}, []);
|
|
360
|
+
const reset = (0, react.useCallback)((handleIndex) => {
|
|
361
|
+
const raw = currentSizesRef.current;
|
|
362
|
+
const beforeIdx = handleIndex;
|
|
363
|
+
const afterIdx = handleIndex + 1;
|
|
364
|
+
if (beforeIdx < 0 || afterIdx >= raw.length) return;
|
|
365
|
+
const container = pixelMode ? containerSizeRef.current || measureContainer() : 0;
|
|
366
|
+
const rootFontSize = rootFontSizeRef.current;
|
|
367
|
+
const working = resolveWorkingSizes(raw, pixelMode, container, rootFontSize);
|
|
368
|
+
const resolvedPanels = optionsRef.current.panels.map((panel) => resolvePanel(panel, pixelMode, container, rootFontSize));
|
|
369
|
+
const total = working[beforeIdx] + working[afterIdx];
|
|
370
|
+
const defBefore = resolvedPanels[beforeIdx].defaultSize;
|
|
371
|
+
const defTotal = defBefore + resolvedPanels[afterIdx].defaultSize;
|
|
372
|
+
const next = applyAdjacentOnly(working, resolvedPanels, beforeIdx, (defTotal === 0 ? total / 2 : total * (defBefore / defTotal)) - working[beforeIdx]);
|
|
373
|
+
emitCollapseTransitions(raw, next, [beforeIdx, afterIdx], raw);
|
|
374
|
+
updateSizes(encodeWorkingSizes(next, working, raw, pixelMode, container, rootFontSize));
|
|
375
|
+
}, [
|
|
376
|
+
emitCollapseTransitions,
|
|
377
|
+
updateSizes,
|
|
378
|
+
pixelMode,
|
|
379
|
+
measureContainer
|
|
380
|
+
]);
|
|
230
381
|
const containerRefCallback = (0, react.useCallback)((node) => {
|
|
231
382
|
containerRef.current = node;
|
|
232
383
|
}, []);
|
|
384
|
+
(0, react.useEffect)(() => {
|
|
385
|
+
if (!pixelMode || typeof ResizeObserver === "undefined") return;
|
|
386
|
+
const node = containerRef.current;
|
|
387
|
+
if (!node) return;
|
|
388
|
+
let frame = 0;
|
|
389
|
+
const update = () => {
|
|
390
|
+
const rect = node.getBoundingClientRect();
|
|
391
|
+
const size = (optionsRef.current.orientation ?? "horizontal") === "horizontal" ? rect.width : rect.height;
|
|
392
|
+
rootFontSizeRef.current = getRootFontSize();
|
|
393
|
+
containerSizeRef.current = size;
|
|
394
|
+
setContainerSize((prev) => prev !== size ? size : prev);
|
|
395
|
+
};
|
|
396
|
+
const observer = new ResizeObserver(() => {
|
|
397
|
+
cancelAnimationFrame(frame);
|
|
398
|
+
frame = requestAnimationFrame(update);
|
|
399
|
+
});
|
|
400
|
+
observer.observe(node);
|
|
401
|
+
update();
|
|
402
|
+
return () => {
|
|
403
|
+
cancelAnimationFrame(frame);
|
|
404
|
+
observer.disconnect();
|
|
405
|
+
};
|
|
406
|
+
}, [pixelMode, orientation]);
|
|
233
407
|
const handleRefCallbacks = (0, react.useRef)(/* @__PURE__ */ new Map());
|
|
234
408
|
const handleElementControllers = (0, react.useRef)(/* @__PURE__ */ new Map());
|
|
235
409
|
const getHandleRefCallback = (0, react.useCallback)((handleIndex) => {
|
|
@@ -248,22 +422,28 @@ function useSplitter(options) {
|
|
|
248
422
|
if (event.button !== 0) return;
|
|
249
423
|
const container = containerRef.current;
|
|
250
424
|
if (!container) return;
|
|
425
|
+
const opts = optionsRef.current;
|
|
426
|
+
const isHorizontal = (opts.orientation ?? "horizontal") === "horizontal";
|
|
251
427
|
const rect = container.getBoundingClientRect();
|
|
252
|
-
const
|
|
253
|
-
const containerSize = isHorizontal ? rect.width : rect.height;
|
|
428
|
+
const containerSizePx = isHorizontal ? rect.width : rect.height;
|
|
254
429
|
const pointerPos = isHorizontal ? event.clientX : event.clientY;
|
|
430
|
+
const isPixelMode = detectPixelMode(opts);
|
|
431
|
+
const rootFontSize = getRootFontSize();
|
|
255
432
|
const s = internalStateRef.current;
|
|
256
433
|
s.isDragging = true;
|
|
257
434
|
s.handleIndex = handleIndex;
|
|
258
435
|
s.startPointer = pointerPos;
|
|
259
|
-
s.containerSize =
|
|
260
|
-
s.
|
|
436
|
+
s.containerSize = containerSizePx;
|
|
437
|
+
s.rootFontSize = rootFontSize;
|
|
438
|
+
s.pixelMode = isPixelMode;
|
|
439
|
+
s.startRaw = [...currentSizesRef.current];
|
|
440
|
+
s.startSizes = resolveWorkingSizes(s.startRaw, isPixelMode, containerSizePx, rootFontSize);
|
|
261
441
|
s.preCollapseSizes = [...preCollapseSizesRef.current];
|
|
262
442
|
setActiveHandle(handleIndex);
|
|
263
443
|
document.body.style.userSelect = "none";
|
|
264
444
|
document.body.style.webkitUserSelect = "none";
|
|
265
445
|
document.body.style.cursor = isHorizontal ? "col-resize" : "row-resize";
|
|
266
|
-
|
|
446
|
+
opts.onResizeStart?.(handleIndex);
|
|
267
447
|
documentControllerRef.current?.abort();
|
|
268
448
|
documentControllerRef.current = new AbortController();
|
|
269
449
|
const sig = documentControllerRef.current.signal;
|
|
@@ -274,27 +454,19 @@ function useSplitter(options) {
|
|
|
274
454
|
const flushResize = (pointerEvent) => {
|
|
275
455
|
const s = internalStateRef.current;
|
|
276
456
|
if (!s.containerSize) return;
|
|
277
|
-
const isHorizontal = (optionsRef.current.orientation ?? "horizontal") === "horizontal";
|
|
278
|
-
const isRtl = isHorizontal && optionsRef.current.dir === "rtl";
|
|
279
|
-
const pixelDelta = (isHorizontal ? pointerEvent.clientX : pointerEvent.clientY) - s.startPointer;
|
|
280
|
-
const percentDelta = (isRtl ? -pixelDelta : pixelDelta) / s.containerSize * 100;
|
|
281
457
|
const opts = optionsRef.current;
|
|
282
|
-
const
|
|
458
|
+
const isHorizontal = (opts.orientation ?? "horizontal") === "horizontal";
|
|
459
|
+
const isRtl = isHorizontal && opts.dir === "rtl";
|
|
460
|
+
const pointerPos = isHorizontal ? pointerEvent.clientX : pointerEvent.clientY;
|
|
461
|
+
const pixelDelta = (isRtl ? -1 : 1) * (pointerPos - s.startPointer);
|
|
462
|
+
const delta = s.pixelMode ? pixelDelta : pixelDelta / s.containerSize * 100;
|
|
463
|
+
const resolvedPanels = opts.panels.map((panel) => resolvePanel(panel, s.pixelMode, s.containerSize, s.rootFontSize));
|
|
464
|
+
const newSizes = applyConstraints(s.startSizes, resolvedPanels, s.handleIndex, delta, opts.redistribute);
|
|
283
465
|
const prevSizes = currentSizesRef.current;
|
|
284
|
-
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
if (!beforeWasCollapsed && beforeNowCollapsed) {
|
|
289
|
-
preCollapseSizesRef.current = [...s.startSizes];
|
|
290
|
-
opts.onCollapseChange?.(s.handleIndex, true);
|
|
291
|
-
} else if (beforeWasCollapsed && !beforeNowCollapsed) opts.onCollapseChange?.(s.handleIndex, false);
|
|
292
|
-
if (!afterWasCollapsed && afterNowCollapsed) {
|
|
293
|
-
preCollapseSizesRef.current = [...s.startSizes];
|
|
294
|
-
opts.onCollapseChange?.(s.handleIndex + 1, true);
|
|
295
|
-
} else if (afterWasCollapsed && !afterNowCollapsed) opts.onCollapseChange?.(s.handleIndex + 1, false);
|
|
296
|
-
currentSizesRef.current = newSizes;
|
|
297
|
-
setCurrentSizes(newSizes);
|
|
466
|
+
emitCollapseTransitions(prevSizes, newSizes, [s.handleIndex, s.handleIndex + 1], s.startRaw);
|
|
467
|
+
const encoded = encodeWorkingSizes(newSizes, s.startSizes, s.startRaw, s.pixelMode, s.containerSize, s.rootFontSize);
|
|
468
|
+
currentSizesRef.current = encoded;
|
|
469
|
+
setCurrentSizes(encoded);
|
|
298
470
|
};
|
|
299
471
|
const onPointerMove = (event) => {
|
|
300
472
|
if (!internalStateRef.current.isDragging) return;
|
|
@@ -327,9 +499,11 @@ function useSplitter(options) {
|
|
|
327
499
|
const getHandleProps = (0, react.useCallback)((input) => {
|
|
328
500
|
const { index } = input;
|
|
329
501
|
const orient = orientation;
|
|
330
|
-
const
|
|
331
|
-
const
|
|
332
|
-
const
|
|
502
|
+
const rootFontSize = rootFontSizeRef.current;
|
|
503
|
+
const working = resolveWorkingSizes(currentSizes, pixelMode, containerSize, rootFontSize);
|
|
504
|
+
const resolvedPanels = panels.map((panel) => resolvePanel(panel, pixelMode, containerSize, rootFontSize));
|
|
505
|
+
const beforeSize = working[index] ?? 0;
|
|
506
|
+
const beforePanel = resolvedPanels[index];
|
|
333
507
|
return {
|
|
334
508
|
ref: getHandleRefCallback(index),
|
|
335
509
|
role: "separator",
|
|
@@ -342,8 +516,14 @@ function useSplitter(options) {
|
|
|
342
516
|
if (!enabled) return;
|
|
343
517
|
const isHorizontal = orient === "horizontal";
|
|
344
518
|
const isRtl = dir === "rtl";
|
|
519
|
+
const container = pixelMode ? containerSizeRef.current || measureContainer() : 0;
|
|
520
|
+
const liveRootFontSize = rootFontSizeRef.current;
|
|
521
|
+
const liveWorking = resolveWorkingSizes(currentSizes, pixelMode, container, liveRootFontSize);
|
|
522
|
+
const livePanels = panels.map((panel) => resolvePanel(panel, pixelMode, container, liveRootFontSize));
|
|
523
|
+
const liveBeforePanel = livePanels[index];
|
|
524
|
+
const liveAfterPanel = livePanels[index + 1];
|
|
345
525
|
let delta = 0;
|
|
346
|
-
const currentStep = event.shiftKey ? shiftStep : step;
|
|
526
|
+
const currentStep = resolveStep(event.shiftKey ? shiftStep : step, pixelMode, container, liveRootFontSize);
|
|
347
527
|
switch (event.key) {
|
|
348
528
|
case "ArrowLeft":
|
|
349
529
|
if (!isHorizontal) return;
|
|
@@ -362,15 +542,15 @@ function useSplitter(options) {
|
|
|
362
542
|
delta = currentStep;
|
|
363
543
|
break;
|
|
364
544
|
case "Home":
|
|
365
|
-
delta = -(
|
|
545
|
+
delta = -(liveWorking[index] - getMin(liveBeforePanel));
|
|
366
546
|
break;
|
|
367
547
|
case "End":
|
|
368
|
-
delta = getMax(
|
|
548
|
+
delta = getMax(liveBeforePanel) - liveWorking[index];
|
|
369
549
|
break;
|
|
370
550
|
case "Enter": {
|
|
371
|
-
const beforeCollapsible =
|
|
372
|
-
const afterCollapsible =
|
|
373
|
-
if (beforeCollapsible &&
|
|
551
|
+
const beforeCollapsible = liveBeforePanel?.collapsible;
|
|
552
|
+
const afterCollapsible = liveAfterPanel?.collapsible;
|
|
553
|
+
if (beforeCollapsible && liveWorking[index] <= liveWorking[index + 1]) {
|
|
374
554
|
toggleCollapsePanel(index);
|
|
375
555
|
event.preventDefault();
|
|
376
556
|
return;
|
|
@@ -391,22 +571,15 @@ function useSplitter(options) {
|
|
|
391
571
|
}
|
|
392
572
|
event.preventDefault();
|
|
393
573
|
if (delta !== 0) {
|
|
394
|
-
const newSizes = applyConstraints(
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
const beforeNow = newSizes[index] === 0;
|
|
398
|
-
const afterNow = newSizes[index + 1] === 0;
|
|
399
|
-
if (!beforeWas && beforeNow) {
|
|
400
|
-
preCollapseSizesRef.current = [...currentSizes];
|
|
401
|
-
onCollapseChange?.(index, true);
|
|
402
|
-
} else if (beforeWas && !beforeNow) onCollapseChange?.(index, false);
|
|
403
|
-
if (!afterWas && afterNow) {
|
|
404
|
-
preCollapseSizesRef.current = [...currentSizes];
|
|
405
|
-
onCollapseChange?.(index + 1, true);
|
|
406
|
-
} else if (afterWas && !afterNow) onCollapseChange?.(index + 1, false);
|
|
407
|
-
updateSizes(newSizes);
|
|
574
|
+
const newSizes = applyConstraints(liveWorking, livePanels, index, delta, redistribute);
|
|
575
|
+
emitCollapseTransitions(currentSizes, newSizes, [index, index + 1], currentSizes);
|
|
576
|
+
updateSizes(encodeWorkingSizes(newSizes, liveWorking, currentSizes, pixelMode, container, liveRootFontSize));
|
|
408
577
|
}
|
|
409
578
|
},
|
|
579
|
+
onDoubleClick: () => {
|
|
580
|
+
if (!enabled || !resetOnDoubleClick) return;
|
|
581
|
+
reset(index);
|
|
582
|
+
},
|
|
410
583
|
"data-active": activeHandle === index || void 0,
|
|
411
584
|
"data-orientation": orient
|
|
412
585
|
};
|
|
@@ -414,16 +587,21 @@ function useSplitter(options) {
|
|
|
414
587
|
orientation,
|
|
415
588
|
currentSizes,
|
|
416
589
|
panels,
|
|
590
|
+
pixelMode,
|
|
591
|
+
containerSize,
|
|
417
592
|
enabled,
|
|
418
593
|
dir,
|
|
419
594
|
step,
|
|
420
595
|
shiftStep,
|
|
596
|
+
resetOnDoubleClick,
|
|
421
597
|
activeHandle,
|
|
422
598
|
redistribute,
|
|
599
|
+
measureContainer,
|
|
423
600
|
getHandleRefCallback,
|
|
424
601
|
toggleCollapsePanel,
|
|
425
602
|
updateSizes,
|
|
426
|
-
|
|
603
|
+
emitCollapseTransitions,
|
|
604
|
+
reset
|
|
427
605
|
]);
|
|
428
606
|
(0, react.useEffect)(() => () => {
|
|
429
607
|
documentControllerRef.current?.abort();
|
|
@@ -441,13 +619,15 @@ function useSplitter(options) {
|
|
|
441
619
|
return {
|
|
442
620
|
ref: containerRefCallback,
|
|
443
621
|
sizes: currentSizes,
|
|
622
|
+
pixelMode,
|
|
444
623
|
collapsed,
|
|
445
624
|
activeHandle,
|
|
446
625
|
getHandleProps,
|
|
447
626
|
setSizes: updateSizes,
|
|
448
627
|
collapse: collapsePanel,
|
|
449
628
|
expand: expandPanel,
|
|
450
|
-
toggleCollapse: toggleCollapsePanel
|
|
629
|
+
toggleCollapse: toggleCollapsePanel,
|
|
630
|
+
reset
|
|
451
631
|
};
|
|
452
632
|
}
|
|
453
633
|
//#endregion
|