@mantine/hooks 9.3.2 → 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 +226 -55
- 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 +226 -55
- 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 +48 -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
|
}
|
|
@@ -165,7 +264,8 @@ function applyConstraints(sizes, panels, handleIndex, delta, redistribute) {
|
|
|
165
264
|
}
|
|
166
265
|
function useSplitter(options) {
|
|
167
266
|
const { panels, orientation = "horizontal", sizes: controlledSizes, onSizeChange, onCollapseChange, redistribute, step = 1, shiftStep = 10, dir = "ltr", resetOnDoubleClick = true, enabled = true } = options;
|
|
168
|
-
const
|
|
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,64 +273,83 @@ 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]);
|
|
230
349
|
const emitCollapseTransitions = (0, react.useCallback)((prev, next, indices, preCollapseSnapshot) => {
|
|
231
350
|
const onChange = optionsRef.current.onCollapseChange;
|
|
232
351
|
for (const idx of indices) {
|
|
233
|
-
const wasCollapsed = prev[idx] === 0;
|
|
352
|
+
const wasCollapsed = sizeMagnitude(prev[idx]) === 0;
|
|
234
353
|
const nowCollapsed = next[idx] === 0;
|
|
235
354
|
if (!wasCollapsed && nowCollapsed) {
|
|
236
355
|
preCollapseSizesRef.current = [...preCollapseSnapshot];
|
|
@@ -239,21 +358,52 @@ function useSplitter(options) {
|
|
|
239
358
|
}
|
|
240
359
|
}, []);
|
|
241
360
|
const reset = (0, react.useCallback)((handleIndex) => {
|
|
242
|
-
const
|
|
243
|
-
const currentPanels = optionsRef.current.panels;
|
|
361
|
+
const raw = currentSizesRef.current;
|
|
244
362
|
const beforeIdx = handleIndex;
|
|
245
363
|
const afterIdx = handleIndex + 1;
|
|
246
|
-
if (beforeIdx < 0 || afterIdx >=
|
|
247
|
-
const
|
|
248
|
-
const
|
|
249
|
-
const
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
+
]);
|
|
254
381
|
const containerRefCallback = (0, react.useCallback)((node) => {
|
|
255
382
|
containerRef.current = node;
|
|
256
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]);
|
|
257
407
|
const handleRefCallbacks = (0, react.useRef)(/* @__PURE__ */ new Map());
|
|
258
408
|
const handleElementControllers = (0, react.useRef)(/* @__PURE__ */ new Map());
|
|
259
409
|
const getHandleRefCallback = (0, react.useCallback)((handleIndex) => {
|
|
@@ -272,22 +422,28 @@ function useSplitter(options) {
|
|
|
272
422
|
if (event.button !== 0) return;
|
|
273
423
|
const container = containerRef.current;
|
|
274
424
|
if (!container) return;
|
|
425
|
+
const opts = optionsRef.current;
|
|
426
|
+
const isHorizontal = (opts.orientation ?? "horizontal") === "horizontal";
|
|
275
427
|
const rect = container.getBoundingClientRect();
|
|
276
|
-
const
|
|
277
|
-
const containerSize = isHorizontal ? rect.width : rect.height;
|
|
428
|
+
const containerSizePx = isHorizontal ? rect.width : rect.height;
|
|
278
429
|
const pointerPos = isHorizontal ? event.clientX : event.clientY;
|
|
430
|
+
const isPixelMode = detectPixelMode(opts);
|
|
431
|
+
const rootFontSize = getRootFontSize();
|
|
279
432
|
const s = internalStateRef.current;
|
|
280
433
|
s.isDragging = true;
|
|
281
434
|
s.handleIndex = handleIndex;
|
|
282
435
|
s.startPointer = pointerPos;
|
|
283
|
-
s.containerSize =
|
|
284
|
-
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);
|
|
285
441
|
s.preCollapseSizes = [...preCollapseSizesRef.current];
|
|
286
442
|
setActiveHandle(handleIndex);
|
|
287
443
|
document.body.style.userSelect = "none";
|
|
288
444
|
document.body.style.webkitUserSelect = "none";
|
|
289
445
|
document.body.style.cursor = isHorizontal ? "col-resize" : "row-resize";
|
|
290
|
-
|
|
446
|
+
opts.onResizeStart?.(handleIndex);
|
|
291
447
|
documentControllerRef.current?.abort();
|
|
292
448
|
documentControllerRef.current = new AbortController();
|
|
293
449
|
const sig = documentControllerRef.current.signal;
|
|
@@ -298,16 +454,19 @@ function useSplitter(options) {
|
|
|
298
454
|
const flushResize = (pointerEvent) => {
|
|
299
455
|
const s = internalStateRef.current;
|
|
300
456
|
if (!s.containerSize) return;
|
|
301
|
-
const isHorizontal = (optionsRef.current.orientation ?? "horizontal") === "horizontal";
|
|
302
|
-
const isRtl = isHorizontal && optionsRef.current.dir === "rtl";
|
|
303
|
-
const pixelDelta = (isHorizontal ? pointerEvent.clientX : pointerEvent.clientY) - s.startPointer;
|
|
304
|
-
const percentDelta = (isRtl ? -pixelDelta : pixelDelta) / s.containerSize * 100;
|
|
305
457
|
const opts = optionsRef.current;
|
|
306
|
-
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);
|
|
307
465
|
const prevSizes = currentSizesRef.current;
|
|
308
|
-
emitCollapseTransitions(prevSizes, newSizes, [s.handleIndex, s.handleIndex + 1], s.
|
|
309
|
-
|
|
310
|
-
|
|
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);
|
|
311
470
|
};
|
|
312
471
|
const onPointerMove = (event) => {
|
|
313
472
|
if (!internalStateRef.current.isDragging) return;
|
|
@@ -340,9 +499,11 @@ function useSplitter(options) {
|
|
|
340
499
|
const getHandleProps = (0, react.useCallback)((input) => {
|
|
341
500
|
const { index } = input;
|
|
342
501
|
const orient = orientation;
|
|
343
|
-
const
|
|
344
|
-
const
|
|
345
|
-
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];
|
|
346
507
|
return {
|
|
347
508
|
ref: getHandleRefCallback(index),
|
|
348
509
|
role: "separator",
|
|
@@ -355,8 +516,14 @@ function useSplitter(options) {
|
|
|
355
516
|
if (!enabled) return;
|
|
356
517
|
const isHorizontal = orient === "horizontal";
|
|
357
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];
|
|
358
525
|
let delta = 0;
|
|
359
|
-
const currentStep = event.shiftKey ? shiftStep : step;
|
|
526
|
+
const currentStep = resolveStep(event.shiftKey ? shiftStep : step, pixelMode, container, liveRootFontSize);
|
|
360
527
|
switch (event.key) {
|
|
361
528
|
case "ArrowLeft":
|
|
362
529
|
if (!isHorizontal) return;
|
|
@@ -375,15 +542,15 @@ function useSplitter(options) {
|
|
|
375
542
|
delta = currentStep;
|
|
376
543
|
break;
|
|
377
544
|
case "Home":
|
|
378
|
-
delta = -(
|
|
545
|
+
delta = -(liveWorking[index] - getMin(liveBeforePanel));
|
|
379
546
|
break;
|
|
380
547
|
case "End":
|
|
381
|
-
delta = getMax(
|
|
548
|
+
delta = getMax(liveBeforePanel) - liveWorking[index];
|
|
382
549
|
break;
|
|
383
550
|
case "Enter": {
|
|
384
|
-
const beforeCollapsible =
|
|
385
|
-
const afterCollapsible =
|
|
386
|
-
if (beforeCollapsible &&
|
|
551
|
+
const beforeCollapsible = liveBeforePanel?.collapsible;
|
|
552
|
+
const afterCollapsible = liveAfterPanel?.collapsible;
|
|
553
|
+
if (beforeCollapsible && liveWorking[index] <= liveWorking[index + 1]) {
|
|
387
554
|
toggleCollapsePanel(index);
|
|
388
555
|
event.preventDefault();
|
|
389
556
|
return;
|
|
@@ -404,9 +571,9 @@ function useSplitter(options) {
|
|
|
404
571
|
}
|
|
405
572
|
event.preventDefault();
|
|
406
573
|
if (delta !== 0) {
|
|
407
|
-
const newSizes = applyConstraints(
|
|
574
|
+
const newSizes = applyConstraints(liveWorking, livePanels, index, delta, redistribute);
|
|
408
575
|
emitCollapseTransitions(currentSizes, newSizes, [index, index + 1], currentSizes);
|
|
409
|
-
updateSizes(newSizes);
|
|
576
|
+
updateSizes(encodeWorkingSizes(newSizes, liveWorking, currentSizes, pixelMode, container, liveRootFontSize));
|
|
410
577
|
}
|
|
411
578
|
},
|
|
412
579
|
onDoubleClick: () => {
|
|
@@ -420,6 +587,8 @@ function useSplitter(options) {
|
|
|
420
587
|
orientation,
|
|
421
588
|
currentSizes,
|
|
422
589
|
panels,
|
|
590
|
+
pixelMode,
|
|
591
|
+
containerSize,
|
|
423
592
|
enabled,
|
|
424
593
|
dir,
|
|
425
594
|
step,
|
|
@@ -427,6 +596,7 @@ function useSplitter(options) {
|
|
|
427
596
|
resetOnDoubleClick,
|
|
428
597
|
activeHandle,
|
|
429
598
|
redistribute,
|
|
599
|
+
measureContainer,
|
|
430
600
|
getHandleRefCallback,
|
|
431
601
|
toggleCollapsePanel,
|
|
432
602
|
updateSizes,
|
|
@@ -449,6 +619,7 @@ function useSplitter(options) {
|
|
|
449
619
|
return {
|
|
450
620
|
ref: containerRefCallback,
|
|
451
621
|
sizes: currentSizes,
|
|
622
|
+
pixelMode,
|
|
452
623
|
collapsed,
|
|
453
624
|
activeHandle,
|
|
454
625
|
getHandleProps,
|