@gfazioli/mantine-split-pane
Version:
A Mantine 9 React component for resizable split pane layouts with 7 resizer variants, context-based prop inheritance, responsive orientation, and dynamic pane generation.
108 lines (106 loc) • 3.6 kB
JavaScript
'use client';
function clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
}
const DEFAULT_SNAP_TOLERANCE = 10;
const PERCENTAGE_PATTERN = /^(\d+(?:\.\d+)?)%$/;
function isValidSnapPoint(value) {
if (typeof value === "number") {
return Number.isFinite(value) && value >= 0;
}
if (typeof value === "string") {
const match = value.trim().match(PERCENTAGE_PATTERN);
return match !== null && Number.isFinite(parseFloat(match[1]));
}
return false;
}
function resolveSnapPoint(point, totalSize) {
if (typeof point === "number") {
return point;
}
const match = point.trim().match(PERCENTAGE_PATTERN);
if (!match) {
return Number.NaN;
}
return parseFloat(match[1]) / 100 * totalSize;
}
function normalizeSnapPoints({
snapPoints,
snapTolerance
}) {
const normalizedTolerance = Math.max(0, snapTolerance ?? DEFAULT_SNAP_TOLERANCE);
const raw = Array.isArray(snapPoints) ? snapPoints : [];
const valid = raw.filter(isValidSnapPoint);
const seen = /* @__PURE__ */ new Set();
const dedup = [];
for (const point of valid) {
const key = typeof point === "string" ? point.trim() : String(point);
if (!seen.has(key)) {
seen.add(key);
dedup.push(typeof point === "string" ? point.trim() : point);
}
}
return {
snapPoints: dedup,
snapTolerance: normalizedTolerance
};
}
function findNearestSnap(value, points, tolerance) {
let snappedValue = value;
let snappedPoint = null;
let shortestDistance = Infinity;
for (const point of points) {
const distance = Math.abs(value - point);
if (distance <= tolerance && (distance < shortestDistance || distance === shortestDistance && point < snappedValue)) {
snappedValue = point;
snappedPoint = point;
shortestDistance = distance;
}
}
return { value: snappedValue, point: snappedPoint };
}
function calculateSnappedPaneSizes({
beforeSize,
afterSize,
delta,
minBeforeSize,
maxBeforeSize,
minAfterSize,
maxAfterSize,
snapPoints,
snapTolerance,
snapFrom = "before"
}) {
const totalSize = beforeSize + afterSize;
const safeMinBeforeSize = Math.max(0, minBeforeSize ?? 0);
const safeMaxBeforeSize = maxBeforeSize ?? totalSize;
const safeMinAfterSize = Math.max(0, minAfterSize ?? 0);
const safeMaxAfterSize = maxAfterSize ?? totalSize;
const minAllowedBeforeSize = Math.max(safeMinBeforeSize, totalSize - safeMaxAfterSize, 0);
const maxAllowedBeforeSize = Math.min(safeMaxBeforeSize, totalSize - safeMinAfterSize, totalSize);
if (minAllowedBeforeSize > maxAllowedBeforeSize) {
return { beforeSize, afterSize, snappedPoint: null };
}
let nextBeforeSize = clamp(beforeSize + delta, minAllowedBeforeSize, maxAllowedBeforeSize);
let snappedPoint = null;
if (snapPoints.length > 0) {
const resolved = snapPoints.map((point) => {
const asBefore = resolveSnapPoint(point, totalSize);
return snapFrom === "after" ? totalSize - asBefore : asBefore;
}).filter(
(point) => Number.isFinite(point) && point >= minAllowedBeforeSize && point <= maxAllowedBeforeSize
);
const match = findNearestSnap(nextBeforeSize, resolved, snapTolerance);
nextBeforeSize = match.value;
if (match.point !== null) {
snappedPoint = snapFrom === "after" ? totalSize - match.point : match.point;
}
}
return {
beforeSize: nextBeforeSize,
afterSize: totalSize - nextBeforeSize,
snappedPoint
};
}
export { DEFAULT_SNAP_TOLERANCE, calculateSnappedPaneSizes, isValidSnapPoint, normalizeSnapPoints, resolveSnapPoint };
//# sourceMappingURL=snap.mjs.map