@mantine/hooks
Version:
A collection of 50+ hooks for state and UI management
636 lines (635 loc) • 27.3 kB
JavaScript
"use client";
const require_use_uncontrolled = require("../use-uncontrolled/use-uncontrolled.cjs");
let react = require("react");
//#region packages/@mantine/hooks/src/use-splitter/use-splitter.ts
const PX_RE = /^(-?[\d.]+)px$/;
const REM_RE = /^(-?[\d.]+)rem$/;
const PERCENT_RE = /^(-?[\d.]+)%$/;
function isFixedSize(size) {
return typeof size === "string" && (PX_RE.test(size) || REM_RE.test(size));
}
function sizeMagnitude(size) {
return typeof size === "number" ? size : parseFloat(size);
}
function detectPixelMode(options) {
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);
}
function getRootFontSize() {
if (typeof window === "undefined") return 16;
const fontSize = parseFloat(getComputedStyle(document.documentElement).fontSize);
return Number.isFinite(fontSize) && fontSize > 0 ? fontSize : 16;
}
function resolveSize(size, pixelMode, containerPx, rootFontSize) {
if (!pixelMode) return sizeMagnitude(size);
if (typeof size === "number") return size / 100 * containerPx;
const percent = PERCENT_RE.exec(size);
if (percent) return parseFloat(percent[1]) / 100 * containerPx;
const rem = REM_RE.exec(size);
if (rem) return parseFloat(rem[1]) * rootFontSize;
const px = PX_RE.exec(size);
if (px) return parseFloat(px[1]);
return 0;
}
/** Down-scaling factor applied to fixed panes when their combined pixel size overflows the
* container, so they shrink to fit (matching `resolveWorkingSizes`). Returns `1` when nothing
* overflows or the layout is not in pixel mode. Encoders divide by this to invert the scaling and
* persist the original absolute sizes instead of the shrunk-to-fit ones. */
function getFixedScale(sizes, pixelMode, containerPx, rootFontSize) {
if (!pixelMode) return 1;
let fixedTotal = 0;
sizes.forEach((size) => {
if (isFixedSize(size)) fixedTotal += resolveSize(size, true, containerPx, rootFontSize);
});
return fixedTotal > containerPx && fixedTotal > 0 ? containerPx / fixedTotal : 1;
}
/** Resolves all sizes to pixels at once. Fixed panes get their absolute pixel size, flexible panes
* share the leftover space by their weight ratio – matching how the layout is rendered with
* `flex-grow`, so drag math operates on the same pixel sizes the user sees. */
function resolveWorkingSizes(sizes, pixelMode, containerPx, rootFontSize) {
if (!pixelMode) return sizes.map((size) => sizeMagnitude(size));
let fixedTotal = 0;
let flexibleWeight = 0;
sizes.forEach((size) => {
if (isFixedSize(size)) fixedTotal += resolveSize(size, true, containerPx, rootFontSize);
else flexibleWeight += sizeMagnitude(size);
});
const leftover = Math.max(0, containerPx - fixedTotal);
const fixedScale = getFixedScale(sizes, pixelMode, containerPx, rootFontSize);
return sizes.map((size) => {
if (isFixedSize(size)) return resolveSize(size, true, containerPx, rootFontSize) * fixedScale;
return flexibleWeight > 0 ? sizeMagnitude(size) / flexibleWeight * leftover : 0;
});
}
function encodeSize(value, original, pixelMode, containerPx, rootFontSize, fixedScale = 1) {
if (!pixelMode) return typeof original === "string" && PERCENT_RE.test(original) ? `${value}%` : value;
if (typeof original === "number") return containerPx > 0 ? value / containerPx * 100 : original;
if (PERCENT_RE.test(original)) return `${containerPx > 0 ? value / containerPx * 100 : parseFloat(original)}%`;
const absolute = fixedScale > 0 ? value / fixedScale : value;
if (REM_RE.test(original)) return `${rootFontSize > 0 ? absolute / rootFontSize : 0}rem`;
return `${absolute}px`;
}
/** Encodes working pixel sizes back to raw sizes after a resize, keeping the unit each pane was
* declared in. Panes whose working size did not change keep their original raw value. When fixed
* panes overflow the container they render down-scaled, so their working sizes are scaled back up to
* absolute sizes (preserving their declared sizes). If the resize instead hands space to a flexible
* pane the overflow clears and the layout leaves the down-scaled regime: every pane is then encoded
* from its current working size – including untouched fixed panes (so they do not jump back to their
* over-sized value) and untouched flexible panes (so a pane that was squeezed to `0` does not keep a
* stale weight and steal the freed space on the next render). */
function encodeWorkingSizes(nextWorking, baseWorking, baseRaw, pixelMode, containerPx, rootFontSize) {
const fixedScale = getFixedScale(baseRaw, pixelMode, containerPx, rootFontSize);
let fixedWorkingSum = 0;
nextWorking.forEach((value, i) => {
if (isFixedSize(baseRaw[i])) fixedWorkingSum += value;
});
const overflowCleared = fixedScale < 1 && fixedWorkingSum < containerPx - 1e-6;
const encodeScale = overflowCleared ? 1 : fixedScale;
return nextWorking.map((value, i) => overflowCleared || Math.abs(value - baseWorking[i]) > 1e-6 ? encodeSize(value, baseRaw[i], pixelMode, containerPx, rootFontSize, encodeScale) : baseRaw[i]);
}
function resolvePanel(panel, pixelMode, containerPx, rootFontSize) {
return {
defaultSize: resolveSize(panel.defaultSize, pixelMode, containerPx, rootFontSize),
min: panel.min != null ? resolveSize(panel.min, pixelMode, containerPx, rootFontSize) : 0,
max: panel.max != null ? resolveSize(panel.max, pixelMode, containerPx, rootFontSize) : pixelMode ? containerPx : 100,
collapseThreshold: panel.collapseThreshold != null ? resolveSize(panel.collapseThreshold, pixelMode, containerPx, rootFontSize) : void 0,
collapsible: panel.collapsible
};
}
function resolveStep(step, pixelMode, containerPx, rootFontSize) {
return resolveSize(step, pixelMode, containerPx, rootFontSize);
}
function clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
}
function getMin(panel) {
return panel.min ?? 0;
}
function getMax(panel) {
return panel.max ?? Infinity;
}
function getCollapseThreshold(panel) {
return panel.collapseThreshold ?? getMin(panel);
}
function createInitialInternalState() {
return {
isDragging: false,
handleIndex: -1,
startPointer: 0,
containerSize: 0,
rootFontSize: 16,
pixelMode: false,
startSizes: [],
startRaw: [],
preCollapseSizes: []
};
}
function checkCollapse(sizes, panels, handleIndex, delta) {
const beforeIdx = handleIndex;
const afterIdx = handleIndex + 1;
const beforePanel = panels[beforeIdx];
const afterPanel = panels[afterIdx];
const rawBefore = sizes[beforeIdx] + delta;
const rawAfter = sizes[afterIdx] - delta;
if (beforePanel.collapsible && rawBefore < getCollapseThreshold(beforePanel) && rawBefore < sizes[beforeIdx]) {
const result = [...sizes];
result[afterIdx] += result[beforeIdx];
result[beforeIdx] = 0;
return result;
}
if (afterPanel.collapsible && rawAfter < getCollapseThreshold(afterPanel) && rawAfter < sizes[afterIdx]) {
const result = [...sizes];
result[beforeIdx] += result[afterIdx];
result[afterIdx] = 0;
return result;
}
return null;
}
function applyAdjacentOnly(sizes, panels, handleIndex, delta) {
const result = [...sizes];
const beforeIdx = handleIndex;
const afterIdx = handleIndex + 1;
const total = result[beforeIdx] + result[afterIdx];
const effectiveBeforeMax = Math.min(getMax(panels[beforeIdx]), total - getMin(panels[afterIdx]));
const effectiveBeforeMin = Math.max(getMin(panels[beforeIdx]), total - getMax(panels[afterIdx]));
const newBefore = clamp(result[beforeIdx] + delta, effectiveBeforeMin, effectiveBeforeMax);
result[beforeIdx] = newBefore;
result[afterIdx] = total - newBefore;
return result;
}
function redistributeNearest(sizes, panels, handleIndex, delta) {
const result = [...sizes];
if (delta > 0) {
const growIdx = handleIndex;
const maxGrow = getMax(panels[growIdx]) - result[growIdx];
const wantedGrow = Math.min(delta, maxGrow);
let taken = 0;
for (let i = handleIndex + 1; i < result.length && taken < wantedGrow; i += 1) {
const canGive = result[i] - getMin(panels[i]);
const take = Math.min(canGive, wantedGrow - taken);
result[i] -= take;
taken += take;
}
result[growIdx] += taken;
} else if (delta < 0) {
const growIdx = handleIndex + 1;
const maxGrow = getMax(panels[growIdx]) - result[growIdx];
const wantedGrow = Math.min(Math.abs(delta), maxGrow);
let taken = 0;
for (let i = handleIndex; i >= 0 && taken < wantedGrow; i -= 1) {
const canGive = result[i] - getMin(panels[i]);
const take = Math.min(canGive, wantedGrow - taken);
result[i] -= take;
taken += take;
}
result[growIdx] += taken;
}
return result;
}
function redistributeEqual(sizes, panels, handleIndex, delta) {
const result = [...sizes];
if (delta > 0) {
const growIdx = handleIndex;
const maxGrow = getMax(panels[growIdx]) - result[growIdx];
const wantedGrow = Math.min(delta, maxGrow);
const donors = [];
for (let i = handleIndex + 1; i < result.length; i += 1) if (result[i] > getMin(panels[i])) donors.push(i);
let remaining = wantedGrow;
while (remaining > .001 && donors.length > 0) {
const perDonor = remaining / donors.length;
const exhausted = [];
for (let d = 0; d < donors.length; d += 1) {
const idx = donors[d];
const canGive = result[idx] - getMin(panels[idx]);
const take = Math.min(canGive, perDonor);
result[idx] -= take;
remaining -= take;
if (canGive <= perDonor + .001) exhausted.push(d);
}
for (let i = exhausted.length - 1; i >= 0; i -= 1) donors.splice(exhausted[i], 1);
if (exhausted.length === 0) break;
}
result[growIdx] += wantedGrow - remaining;
} else if (delta < 0) {
const growIdx = handleIndex + 1;
const maxGrow = getMax(panels[growIdx]) - result[growIdx];
const wantedGrow = Math.min(Math.abs(delta), maxGrow);
const donors = [];
for (let i = handleIndex; i >= 0; i -= 1) if (result[i] > getMin(panels[i])) donors.push(i);
let remaining = wantedGrow;
while (remaining > .001 && donors.length > 0) {
const perDonor = remaining / donors.length;
const exhausted = [];
for (let d = 0; d < donors.length; d += 1) {
const idx = donors[d];
const canGive = result[idx] - getMin(panels[idx]);
const take = Math.min(canGive, perDonor);
result[idx] -= take;
remaining -= take;
if (canGive <= perDonor + .001) exhausted.push(d);
}
for (let i = exhausted.length - 1; i >= 0; i -= 1) donors.splice(exhausted[i], 1);
if (exhausted.length === 0) break;
}
result[growIdx] += wantedGrow - remaining;
}
return result;
}
function applyConstraints(sizes, panels, handleIndex, delta, redistribute) {
if (typeof redistribute === "function") return redistribute({
sizes: [...sizes],
panels,
handleIndex,
delta
});
if (redistribute === "nearest" || redistribute === "equal") {
const result = (redistribute === "nearest" ? redistributeNearest : redistributeEqual)(sizes, panels, handleIndex, delta);
const beforeIdx = handleIndex;
const afterIdx = handleIndex + 1;
const beforePanel = panels[beforeIdx];
const afterPanel = panels[afterIdx];
if (beforePanel.collapsible && result[beforeIdx] < getCollapseThreshold(beforePanel) && result[beforeIdx] < sizes[beforeIdx]) {
const freed = result[beforeIdx];
result[afterIdx] += freed;
result[beforeIdx] = 0;
} else if (afterPanel.collapsible && result[afterIdx] < getCollapseThreshold(afterPanel) && result[afterIdx] < sizes[afterIdx]) {
const freed = result[afterIdx];
result[beforeIdx] += freed;
result[afterIdx] = 0;
}
return result;
}
const collapsed = checkCollapse(sizes, panels, handleIndex, delta);
if (collapsed) return collapsed;
return applyAdjacentOnly(sizes, panels, handleIndex, delta);
}
function useSplitter(options) {
const { panels, orientation = "horizontal", sizes: controlledSizes, onSizeChange, onCollapseChange, redistribute, step = 1, shiftStep = 10, dir = "ltr", resetOnDoubleClick = true, enabled = true } = options;
const pixelMode = detectPixelMode(options);
const defaultSizes = panels.map((panel) => panel.defaultSize);
const [currentSizes, setCurrentSizes] = require_use_uncontrolled.useUncontrolled({
value: controlledSizes,
defaultValue: defaultSizes,
finalValue: defaultSizes,
onChange: onSizeChange
});
const [activeHandle, setActiveHandle] = (0, react.useState)(-1);
const [containerSize, setContainerSize] = (0, react.useState)(0);
const optionsRef = (0, react.useRef)(options);
optionsRef.current = options;
const internalStateRef = (0, react.useRef)(createInitialInternalState());
const containerRef = (0, react.useRef)(null);
const containerSizeRef = (0, react.useRef)(0);
const rootFontSizeRef = (0, react.useRef)(16);
const documentControllerRef = (0, react.useRef)(null);
const frameRef = (0, react.useRef)(0);
const currentSizesRef = (0, react.useRef)(currentSizes);
currentSizesRef.current = currentSizes;
const preCollapseSizesRef = (0, react.useRef)(defaultSizes);
const collapsed = currentSizes.map((size) => sizeMagnitude(size) === 0);
const measureContainer = (0, react.useCallback)(() => {
const node = containerRef.current;
if (!node) return 0;
const rect = node.getBoundingClientRect();
return (optionsRef.current.orientation ?? "horizontal") === "horizontal" ? rect.width : rect.height;
}, []);
const updateSizes = (0, react.useCallback)((newSizes) => {
currentSizesRef.current = newSizes;
setCurrentSizes(newSizes);
}, [setCurrentSizes]);
const collapsePanel = (0, react.useCallback)((panelIndex) => {
if (!panels[panelIndex]?.collapsible) return;
const raw = currentSizesRef.current;
if (sizeMagnitude(raw[panelIndex]) === 0) return;
const container = pixelMode ? containerSizeRef.current || measureContainer() : 0;
const rootFontSize = rootFontSizeRef.current;
const working = resolveWorkingSizes(raw, pixelMode, container, rootFontSize);
preCollapseSizesRef.current = [...raw];
const freedSize = working[panelIndex];
working[panelIndex] = 0;
const neighbor = panelIndex === 0 ? 1 : panelIndex - 1;
working[neighbor] += freedSize;
updateSizes(working.map((value, i) => encodeSize(value, raw[i], pixelMode, container, rootFontSize)));
onCollapseChange?.(panelIndex, true);
}, [
panels,
pixelMode,
measureContainer,
updateSizes,
onCollapseChange
]);
const expandPanel = (0, react.useCallback)((panelIndex) => {
if (!panels[panelIndex]?.collapsible) return;
const raw = currentSizesRef.current;
if (sizeMagnitude(raw[panelIndex]) !== 0) return;
const container = pixelMode ? containerSizeRef.current || measureContainer() : 0;
const rootFontSize = rootFontSizeRef.current;
const working = resolveWorkingSizes(raw, pixelMode, container, rootFontSize);
const preCollapse = preCollapseSizesRef.current;
const restoreSize = resolveSize(preCollapse[panelIndex] != null && sizeMagnitude(preCollapse[panelIndex]) !== 0 ? preCollapse[panelIndex] : panels[panelIndex].defaultSize, pixelMode, container, rootFontSize);
const neighbor = panelIndex === 0 ? 1 : panelIndex - 1;
const neighborMin = panels[neighbor].min != null ? resolveSize(panels[neighbor].min, pixelMode, container, rootFontSize) : 0;
const available = Math.max(0, working[neighbor] - neighborMin);
const actualRestore = Math.min(restoreSize, available);
if (actualRestore <= 0) return;
working[panelIndex] = actualRestore;
working[neighbor] -= actualRestore;
updateSizes(working.map((value, i) => encodeSize(value, raw[i], pixelMode, container, rootFontSize)));
onCollapseChange?.(panelIndex, false);
}, [
panels,
pixelMode,
measureContainer,
updateSizes,
onCollapseChange
]);
const toggleCollapsePanel = (0, react.useCallback)((panelIndex) => {
if (sizeMagnitude(currentSizesRef.current[panelIndex]) === 0) expandPanel(panelIndex);
else collapsePanel(panelIndex);
}, [collapsePanel, expandPanel]);
const emitCollapseTransitions = (0, react.useCallback)((prev, next, indices, preCollapseSnapshot) => {
const onChange = optionsRef.current.onCollapseChange;
for (const idx of indices) {
const wasCollapsed = sizeMagnitude(prev[idx]) === 0;
const nowCollapsed = next[idx] === 0;
if (!wasCollapsed && nowCollapsed) {
preCollapseSizesRef.current = [...preCollapseSnapshot];
onChange?.(idx, true);
} else if (wasCollapsed && !nowCollapsed) onChange?.(idx, false);
}
}, []);
const reset = (0, react.useCallback)((handleIndex) => {
const raw = currentSizesRef.current;
const beforeIdx = handleIndex;
const afterIdx = handleIndex + 1;
if (beforeIdx < 0 || afterIdx >= raw.length) return;
const container = pixelMode ? containerSizeRef.current || measureContainer() : 0;
const rootFontSize = rootFontSizeRef.current;
const working = resolveWorkingSizes(raw, pixelMode, container, rootFontSize);
const resolvedPanels = optionsRef.current.panels.map((panel) => resolvePanel(panel, pixelMode, container, rootFontSize));
const total = working[beforeIdx] + working[afterIdx];
const defBefore = resolvedPanels[beforeIdx].defaultSize;
const defTotal = defBefore + resolvedPanels[afterIdx].defaultSize;
const next = applyAdjacentOnly(working, resolvedPanels, beforeIdx, (defTotal === 0 ? total / 2 : total * (defBefore / defTotal)) - working[beforeIdx]);
emitCollapseTransitions(raw, next, [beforeIdx, afterIdx], raw);
updateSizes(encodeWorkingSizes(next, working, raw, pixelMode, container, rootFontSize));
}, [
emitCollapseTransitions,
updateSizes,
pixelMode,
measureContainer
]);
const containerRefCallback = (0, react.useCallback)((node) => {
containerRef.current = node;
}, []);
(0, react.useEffect)(() => {
if (!pixelMode || typeof ResizeObserver === "undefined") return;
const node = containerRef.current;
if (!node) return;
let frame = 0;
const update = () => {
const rect = node.getBoundingClientRect();
const size = (optionsRef.current.orientation ?? "horizontal") === "horizontal" ? rect.width : rect.height;
rootFontSizeRef.current = getRootFontSize();
containerSizeRef.current = size;
setContainerSize((prev) => prev !== size ? size : prev);
};
const observer = new ResizeObserver(() => {
cancelAnimationFrame(frame);
frame = requestAnimationFrame(update);
});
observer.observe(node);
update();
return () => {
cancelAnimationFrame(frame);
observer.disconnect();
};
}, [pixelMode, orientation]);
const handleRefCallbacks = (0, react.useRef)(/* @__PURE__ */ new Map());
const handleElementControllers = (0, react.useRef)(/* @__PURE__ */ new Map());
const getHandleRefCallback = (0, react.useCallback)((handleIndex) => {
if (handleRefCallbacks.current.has(handleIndex)) return handleRefCallbacks.current.get(handleIndex);
const callback = (node) => {
const existingController = handleElementControllers.current.get(handleIndex);
if (existingController) {
existingController.abort();
handleElementControllers.current.delete(handleIndex);
}
if (!node) return;
const elementController = new AbortController();
handleElementControllers.current.set(handleIndex, elementController);
const onPointerDown = (event) => {
if (optionsRef.current.enabled === false) return;
if (event.button !== 0) return;
const container = containerRef.current;
if (!container) return;
const opts = optionsRef.current;
const isHorizontal = (opts.orientation ?? "horizontal") === "horizontal";
const rect = container.getBoundingClientRect();
const containerSizePx = isHorizontal ? rect.width : rect.height;
const pointerPos = isHorizontal ? event.clientX : event.clientY;
const isPixelMode = detectPixelMode(opts);
const rootFontSize = getRootFontSize();
const s = internalStateRef.current;
s.isDragging = true;
s.handleIndex = handleIndex;
s.startPointer = pointerPos;
s.containerSize = containerSizePx;
s.rootFontSize = rootFontSize;
s.pixelMode = isPixelMode;
s.startRaw = [...currentSizesRef.current];
s.startSizes = resolveWorkingSizes(s.startRaw, isPixelMode, containerSizePx, rootFontSize);
s.preCollapseSizes = [...preCollapseSizesRef.current];
setActiveHandle(handleIndex);
document.body.style.userSelect = "none";
document.body.style.webkitUserSelect = "none";
document.body.style.cursor = isHorizontal ? "col-resize" : "row-resize";
opts.onResizeStart?.(handleIndex);
documentControllerRef.current?.abort();
documentControllerRef.current = new AbortController();
const sig = documentControllerRef.current.signal;
document.addEventListener("pointermove", onPointerMove, { signal: sig });
document.addEventListener("pointerup", onPointerUp, { signal: sig });
document.addEventListener("pointercancel", onPointerUp, { signal: sig });
};
const flushResize = (pointerEvent) => {
const s = internalStateRef.current;
if (!s.containerSize) return;
const opts = optionsRef.current;
const isHorizontal = (opts.orientation ?? "horizontal") === "horizontal";
const isRtl = isHorizontal && opts.dir === "rtl";
const pointerPos = isHorizontal ? pointerEvent.clientX : pointerEvent.clientY;
const pixelDelta = (isRtl ? -1 : 1) * (pointerPos - s.startPointer);
const delta = s.pixelMode ? pixelDelta : pixelDelta / s.containerSize * 100;
const resolvedPanels = opts.panels.map((panel) => resolvePanel(panel, s.pixelMode, s.containerSize, s.rootFontSize));
const newSizes = applyConstraints(s.startSizes, resolvedPanels, s.handleIndex, delta, opts.redistribute);
const prevSizes = currentSizesRef.current;
emitCollapseTransitions(prevSizes, newSizes, [s.handleIndex, s.handleIndex + 1], s.startRaw);
const encoded = encodeWorkingSizes(newSizes, s.startSizes, s.startRaw, s.pixelMode, s.containerSize, s.rootFontSize);
currentSizesRef.current = encoded;
setCurrentSizes(encoded);
};
const onPointerMove = (event) => {
if (!internalStateRef.current.isDragging) return;
cancelAnimationFrame(frameRef.current);
frameRef.current = requestAnimationFrame(() => {
flushResize(event);
});
};
const onPointerUp = (event) => {
const s = internalStateRef.current;
if (!s.isDragging) return;
cancelAnimationFrame(frameRef.current);
flushResize(event);
s.isDragging = false;
const finishedHandle = s.handleIndex;
s.handleIndex = -1;
setActiveHandle(-1);
document.body.style.userSelect = "";
document.body.style.webkitUserSelect = "";
document.body.style.cursor = "";
documentControllerRef.current?.abort();
documentControllerRef.current = null;
optionsRef.current.onResizeEnd?.(finishedHandle, [...currentSizesRef.current]);
};
node.addEventListener("pointerdown", onPointerDown, { signal: elementController.signal });
};
handleRefCallbacks.current.set(handleIndex, callback);
return callback;
}, [setCurrentSizes]);
const getHandleProps = (0, react.useCallback)((input) => {
const { index } = input;
const orient = orientation;
const rootFontSize = rootFontSizeRef.current;
const working = resolveWorkingSizes(currentSizes, pixelMode, containerSize, rootFontSize);
const resolvedPanels = panels.map((panel) => resolvePanel(panel, pixelMode, containerSize, rootFontSize));
const beforeSize = working[index] ?? 0;
const beforePanel = resolvedPanels[index];
return {
ref: getHandleRefCallback(index),
role: "separator",
"aria-orientation": orient,
"aria-valuenow": Math.round(beforeSize),
"aria-valuemin": Math.round(getMin(beforePanel)),
"aria-valuemax": Math.round(getMax(beforePanel)),
tabIndex: 0,
onKeyDown: (event) => {
if (!enabled) return;
const isHorizontal = orient === "horizontal";
const isRtl = dir === "rtl";
const container = pixelMode ? containerSizeRef.current || measureContainer() : 0;
const liveRootFontSize = rootFontSizeRef.current;
const liveWorking = resolveWorkingSizes(currentSizes, pixelMode, container, liveRootFontSize);
const livePanels = panels.map((panel) => resolvePanel(panel, pixelMode, container, liveRootFontSize));
const liveBeforePanel = livePanels[index];
const liveAfterPanel = livePanels[index + 1];
let delta = 0;
const currentStep = resolveStep(event.shiftKey ? shiftStep : step, pixelMode, container, liveRootFontSize);
switch (event.key) {
case "ArrowLeft":
if (!isHorizontal) return;
delta = isRtl ? currentStep : -currentStep;
break;
case "ArrowRight":
if (!isHorizontal) return;
delta = isRtl ? -currentStep : currentStep;
break;
case "ArrowUp":
if (isHorizontal) return;
delta = -currentStep;
break;
case "ArrowDown":
if (isHorizontal) return;
delta = currentStep;
break;
case "Home":
delta = -(liveWorking[index] - getMin(liveBeforePanel));
break;
case "End":
delta = getMax(liveBeforePanel) - liveWorking[index];
break;
case "Enter": {
const beforeCollapsible = liveBeforePanel?.collapsible;
const afterCollapsible = liveAfterPanel?.collapsible;
if (beforeCollapsible && liveWorking[index] <= liveWorking[index + 1]) {
toggleCollapsePanel(index);
event.preventDefault();
return;
}
if (afterCollapsible) {
toggleCollapsePanel(index + 1);
event.preventDefault();
return;
}
if (beforeCollapsible) {
toggleCollapsePanel(index);
event.preventDefault();
return;
}
return;
}
default: return;
}
event.preventDefault();
if (delta !== 0) {
const newSizes = applyConstraints(liveWorking, livePanels, index, delta, redistribute);
emitCollapseTransitions(currentSizes, newSizes, [index, index + 1], currentSizes);
updateSizes(encodeWorkingSizes(newSizes, liveWorking, currentSizes, pixelMode, container, liveRootFontSize));
}
},
onDoubleClick: () => {
if (!enabled || !resetOnDoubleClick) return;
reset(index);
},
"data-active": activeHandle === index || void 0,
"data-orientation": orient
};
}, [
orientation,
currentSizes,
panels,
pixelMode,
containerSize,
enabled,
dir,
step,
shiftStep,
resetOnDoubleClick,
activeHandle,
redistribute,
measureContainer,
getHandleRefCallback,
toggleCollapsePanel,
updateSizes,
emitCollapseTransitions,
reset
]);
(0, react.useEffect)(() => () => {
documentControllerRef.current?.abort();
documentControllerRef.current = null;
handleElementControllers.current.forEach((controller) => controller.abort());
handleElementControllers.current.clear();
cancelAnimationFrame(frameRef.current);
if (internalStateRef.current.isDragging) {
internalStateRef.current.isDragging = false;
document.body.style.userSelect = "";
document.body.style.webkitUserSelect = "";
document.body.style.cursor = "";
}
}, []);
return {
ref: containerRefCallback,
sizes: currentSizes,
pixelMode,
collapsed,
activeHandle,
getHandleProps,
setSizes: updateSizes,
collapse: collapsePanel,
expand: expandPanel,
toggleCollapse: toggleCollapsePanel,
reset
};
}
//#endregion
exports.useSplitter = useSplitter;
//# sourceMappingURL=use-splitter.cjs.map