UNPKG

@gfazioli/mantine-split-pane

Version:

A React component that manages split panes allows users to divide and resize content areas within a layout efficiently.

447 lines (443 loc) 15.8 kB
'use client'; 'use strict'; var React = require('react'); var core = require('@mantine/core'); var Split_context = require('../Split.context.cjs'); var SplitPaneResizer_module = require('./SplitPaneResizer.module.css.cjs'); const varsResolver = core.createVarsResolver( (theme, { size, opacity, radius, color, hoverColor, knobSize, knobOpacity, knobRadius, knobColor, knobHoverColor, withKnob, knobAlwaysOn, spacing, variant, cursorVertical, cursorHorizontal, gradient, hoverGradient }) => { const knobVariant = variant === "dotted" || variant === "dashed"; const forceKnobOpacityValue = withKnob && knobAlwaysOn && !knobVariant ? knobOpacity : "0"; const colors = variantColorResolver({ color, hover: hoverColor, knob: knobColor, hoverKnob: knobHoverColor, theme, gradient, hoverGradient, variant: variant || "filled" }); return { root: { "--split-resizer-size": core.getSize(size, "split-resizer-size"), "--split-resizer-color": colors.color, "--split-resizer-hover-color": colors.hover, "--split-resizer-radius": core.getRadius(radius), "--split-resizer-opacity": opacity !== void 0 ? opacity : "1", "--split-resizer-knob-size": core.getSize(knobSize, "split-resizer-knob-size"), "--split-resizer-knob-opacity": forceKnobOpacityValue, "--split-resizer-knob-hover-opacity": withKnob || knobVariant ? "1" : "0", "--split-resizer-knob-radius": core.getRadius(knobRadius), "--split-resizer-knob-color": colors.knob, "--split-resizer-knob-hover-color": colors.hoverKnob, "--split-resizer-spacing": core.getSize(spacing, "split-resizer-spacing"), "--split-resizer-cursor-vertical": cursorVertical || "col-resize", "--split-resizer-cursor-horizontal": cursorHorizontal || "row-resize" } }; } ); const variantColorResolver = ({ color, hover, knob, hoverKnob, theme, variant, gradient, hoverGradient }) => { const parsedColor = color ? core.parseThemeColor({ color, theme }).value : void 0; const parsedHover = hover ? core.parseThemeColor({ color: hover, theme }).value : void 0; const parsedKnob = knob ? core.parseThemeColor({ color: knob, theme }).value : void 0; const parsedHoverKnob = hoverKnob ? core.parseThemeColor({ color: hoverKnob, theme }).value : void 0; const colors = { color: parsedColor, hover: parsedHover, knob: parsedKnob, hoverKnob: parsedHoverKnob }; if (variant === "gradient") { colors.color = core.getGradient(gradient, theme); colors.hover = core.getGradient(hoverGradient || gradient, theme); } return colors; }; const defaultProps = { orientation: "vertical", opacity: 0.8, size: "sm", radius: "xs", withKnob: false, knobAlwaysOn: true, knobSize: "sm", knobOpacity: 0.5, knobRadius: "sm", knobColor: "white", knobHoverColor: "white", spacing: "xs", step: 8, shiftStep: 64, cursorVertical: "col-resize", cursorHorizontal: "row-resize" }; const SplitPaneResizer = core.factory((_props, _) => { const ctx = Split_context.useSplitContext(); const props = core.useProps("SplitPaneResizer", { ...defaultProps, ...ctx }, _props); const { orientation, opacity, size, radius, withKnob, knobAlwaysOn, knobSize, knobOpacity, knobRadius, knobColor, knobHoverColor, spacing, step, shiftStep, cursorVertical, cursorHorizontal, color, hoverColor, variant, gradient, hoverGradient, onResizeStart, onResizing, onResizeEnd, onDoubleClick, __beforeRef: beforeRef, __afterRef: afterRef, className, style, classNames, styles, unstyled, vars, mod, ...rest } = props; const getStyles = core.useStyles({ name: "SplitPaneResizer", classes: SplitPaneResizer_module, props, className, style, classNames, styles, unstyled, vars, varsResolver }); const containerRef = React.useRef(null); const processVerticalSize = (deltaX = 0) => { const minBeforeWidth = beforeRef.current.getMinWidth(); const maxBeforeWidth = beforeRef.current.getMaxWidth(); const minAfterWidth = afterRef.current.getMinWidth(); const maxAfterWidth = afterRef.current.getMaxWidth(); const beforePane = beforeRef.current.splitPane; const afterPane = afterRef.current.splitPane; let beforeWidth = beforePane.getBoundingClientRect().width; let afterWidth = afterPane.getBoundingClientRect().width; const isBeforeWidthMaxExceeded = maxBeforeWidth && beforeWidth + deltaX > maxBeforeWidth; const isAfterWidthMaxExceeded = maxAfterWidth && afterWidth - deltaX > maxAfterWidth; const isBeforeWidthMinExceeded = minBeforeWidth && beforeWidth + deltaX < minBeforeWidth; const isAfterWidthMinExceeded = minAfterWidth && afterWidth - deltaX < minAfterWidth; const isBeforeWidthNegative = beforeWidth + deltaX < 0; const isAfterWidthNegative = afterWidth - deltaX < 0; function setVerticalSize() { const beforeWidthString = `${beforeWidth}px`; const afterWidthString = `${afterWidth}px`; const beforePaneSizes = { width: beforeWidth, height: beforePane.getBoundingClientRect().height }; const afterPaneSizes = { width: afterWidth, height: afterPane.getBoundingClientRect().height }; beforeRef.current.onResizing?.(beforePaneSizes); afterRef.current.onResizing?.(afterPaneSizes); onResizing?.({ beforePane: beforePaneSizes, afterPane: afterPaneSizes }); beforePane.style.width = beforeWidthString; afterPane.style.width = afterWidthString; } if (!isAfterWidthMaxExceeded && isBeforeWidthMinExceeded) { afterWidth += beforeWidth - minBeforeWidth; beforeWidth = minBeforeWidth; return setVerticalSize(); } if (!isAfterWidthMaxExceeded && isBeforeWidthNegative) { afterWidth += beforeWidth; beforeWidth = 0; return setVerticalSize(); } if (!isAfterWidthMinExceeded && !isAfterWidthNegative && isBeforeWidthMaxExceeded) { afterWidth -= maxBeforeWidth - beforeWidth; beforeWidth = maxBeforeWidth; return setVerticalSize(); } if (!isBeforeWidthMaxExceeded && isAfterWidthMinExceeded) { beforeWidth += afterWidth - minAfterWidth; afterWidth = minAfterWidth; return setVerticalSize(); } if (!isBeforeWidthMaxExceeded && isAfterWidthNegative) { beforeWidth += afterWidth; afterWidth = 0; return setVerticalSize(); } if (!isBeforeWidthMinExceeded && !isBeforeWidthNegative && isAfterWidthMaxExceeded) { beforeWidth -= maxAfterWidth - afterWidth; afterWidth = maxAfterWidth; return setVerticalSize(); } if (isBeforeWidthNegative || isAfterWidthNegative || isBeforeWidthMaxExceeded || isAfterWidthMaxExceeded || isBeforeWidthMinExceeded || isAfterWidthMinExceeded) { return; } beforeWidth += deltaX; afterWidth -= deltaX; setVerticalSize(); }; const processHorizontalSize = (deltaY = 0) => { const minBeforeHeight = beforeRef.current.getMinHeight(); const maxBeforeHeight = beforeRef.current.getMaxHeight(); const minAfterHeight = afterRef.current.getMinHeight(); const maxAfterHeight = afterRef.current.getMaxHeight(); const beforePane = beforeRef.current.splitPane; const afterPane = afterRef.current.splitPane; let beforeHeight = beforePane.getBoundingClientRect().height; let afterHeight = afterPane.getBoundingClientRect().height; const isBeforeHeightMaxExceeded = maxBeforeHeight && beforeHeight + deltaY > maxBeforeHeight; const isAfterHeightMaxExceeded = maxAfterHeight && afterHeight - deltaY > maxAfterHeight; const isBeforeHeightMinExceeded = minBeforeHeight && beforeHeight + deltaY < minBeforeHeight; const isAfterHeightMinExceeded = minAfterHeight && afterHeight - deltaY < minAfterHeight; const isBeforeHeightNegative = beforeHeight + deltaY < 0; const isAfterHeightNegative = afterHeight - deltaY < 0; function setHorizontalSize() { const beforeHeightString = `${beforeHeight}px`; const afterHeightString = `${afterHeight}px`; const beforePaneSizes = { width: beforePane.getBoundingClientRect().width, height: beforeHeight }; const afterPaneSizes = { width: afterPane.getBoundingClientRect().width, height: afterHeight }; onResizing?.({ beforePane: beforePaneSizes, afterPane: afterPaneSizes }); beforeRef.current.onResizing?.(beforePaneSizes); afterRef.current.onResizing?.(afterPaneSizes); beforePane.style.height = beforeHeightString; afterPane.style.height = afterHeightString; } if (!isAfterHeightMaxExceeded && isBeforeHeightMinExceeded) { afterHeight += beforeHeight - minBeforeHeight; beforeHeight = minBeforeHeight; return setHorizontalSize(); } if (!isAfterHeightMaxExceeded && isBeforeHeightNegative) { afterHeight += beforeHeight; beforeHeight = 0; return setHorizontalSize(); } if (!isAfterHeightMinExceeded && !isAfterHeightNegative && isBeforeHeightMaxExceeded) { afterHeight -= maxBeforeHeight - beforeHeight; beforeHeight = maxBeforeHeight; return setHorizontalSize(); } if (!isBeforeHeightMaxExceeded && isAfterHeightMinExceeded) { beforeHeight += afterHeight - minAfterHeight; afterHeight = minAfterHeight; return setHorizontalSize(); } if (!isBeforeHeightMaxExceeded && isAfterHeightNegative) { beforeHeight += afterHeight; afterHeight = 0; return setHorizontalSize(); } if (!isBeforeHeightMinExceeded && !isBeforeHeightNegative && isAfterHeightMaxExceeded) { beforeHeight -= maxAfterHeight - afterHeight; afterHeight = maxAfterHeight; return setHorizontalSize(); } if (isBeforeHeightNegative || isAfterHeightNegative || isBeforeHeightMaxExceeded || isAfterHeightMaxExceeded || isBeforeHeightMinExceeded || isAfterHeightMinExceeded) { return; } beforeHeight += deltaY; afterHeight -= deltaY; setHorizontalSize(); }; const handleStart = (event) => { event.preventDefault(); event.stopPropagation(); document.body.style.userSelect = "none"; document.body.style.webkitUserSelect = "none"; if (event.type === "mousedown") { document.addEventListener("mousemove", handleMove); document.addEventListener("mouseup", handleMouseUp); } if (event.type === "touchstart") { document.addEventListener("touchmove", handleMove); document.addEventListener("touchend", handleTouchEnd); } onResizeStart?.(); beforeRef.current.onResizeStart?.(); afterRef.current.onResizeStart?.(); document.body.style.cursor = orientation === "vertical" ? cursorVertical : cursorHorizontal; }; const handleMove = (event) => { if (!beforeRef.current || !afterRef.current) { throw new Error("beforeRef or afterRef is not defined"); } event.preventDefault(); event.stopPropagation(); const computedStyle = window.getComputedStyle(containerRef.current); if (orientation === "vertical") { const size2 = parseFloat(computedStyle.getPropertyValue("width")); const clientX = "clientX" in event ? event.clientX : event.touches[0].clientX; const deltaX = clientX - containerRef.current.getBoundingClientRect().left - size2 / 2; return processVerticalSize(deltaX); } if (orientation === "horizontal") { const size2 = parseFloat(computedStyle.getPropertyValue("height")); const clientY = "clientY" in event ? event.clientY : event.touches[0].clientY; const deltaY = clientY - containerRef.current.getBoundingClientRect().top - size2 / 2; return processHorizontalSize(deltaY); } }; const handleMouseUp = () => { if (!beforeRef.current || !afterRef.current) { throw new Error("beforeRef or afterRef is not defined"); } document.body.style.userSelect = "initial"; document.body.style.webkitUserSelect = "initial"; document.removeEventListener("mousemove", handleMove); document.removeEventListener("mouseup", handleMouseUp); document.body.style.cursor = "initial"; const beforePane = beforeRef.current.splitPane; const afterPane = afterRef.current.splitPane; const beforePaneSizes = { width: beforePane.getBoundingClientRect().width, height: beforePane.getBoundingClientRect().height }; const afterPaneSizes = { width: afterPane.getBoundingClientRect().width, height: afterPane.getBoundingClientRect().height }; onResizeEnd?.({ beforePane: beforePaneSizes, afterPane: afterPaneSizes }); beforeRef.current.onResizeEnd?.(beforePaneSizes); afterRef.current.onResizeEnd?.(afterPaneSizes); }; const handleTouchEnd = () => { if (!beforeRef.current || !afterRef.current) { throw new Error("beforeRef or afterRef is not defined"); } document.removeEventListener("touchmove", handleMove); document.removeEventListener("touchend", handleTouchEnd); document.body.style.cursor = "initial"; const beforePane = beforeRef.current.splitPane; const afterPane = afterRef.current.splitPane; const beforePaneSizes = { width: beforePane.getBoundingClientRect().width, height: beforePane.getBoundingClientRect().height }; const afterPaneSizes = { width: afterPane.getBoundingClientRect().width, height: afterPane.getBoundingClientRect().height }; onResizeEnd?.({ beforePane: beforePaneSizes, afterPane: afterPaneSizes }); beforeRef.current.onResizeEnd?.(beforePaneSizes); afterRef.current.onResizeEnd?.(afterPaneSizes); }; const handleKeyUp = (event) => { if (containerRef.current !== document.activeElement) { return; } const code = event.nativeEvent.code; const arrowLeftRight = code === "ArrowRight" || code === "ArrowLeft"; const arrowUpDown = code === "ArrowUp" || code === "ArrowDown"; const delta = event.shiftKey ? shiftStep : step; if (orientation === "vertical" && arrowLeftRight) { event.preventDefault(); event.stopPropagation(); const deltaSign = code === "ArrowRight" ? 1 : -1; const deltaX = delta * deltaSign; return processVerticalSize(deltaX); } if (orientation === "horizontal" && arrowUpDown) { event.preventDefault(); event.stopPropagation(); const deltaSign = code === "ArrowDown" ? 1 : -1; const deltaY = delta * deltaSign; return processHorizontalSize(deltaY); } if (code === "Escape") { event.preventDefault(); event.stopPropagation(); containerRef.current.blur(); } }; const handleDoubleClick = (e) => { e.preventDefault(); e.stopPropagation(); beforeRef.current.resetInitialSize(e); afterRef.current.resetInitialSize(e); onDoubleClick?.(e); }; return /* @__PURE__ */ React.createElement( core.UnstyledButton, { ref: containerRef, mod: { orientation }, onMouseDown: handleStart, onKeyDown: handleKeyUp, onTouchStart: handleStart, onDoubleClick: handleDoubleClick, "aria-label": "Resize", ...getStyles("root", { variant: variant || "default" }), ...rest } ); }); SplitPaneResizer.classes = SplitPaneResizer_module; SplitPaneResizer.displayName = "SplitPaneResizer"; exports.SplitPaneResizer = SplitPaneResizer; exports.defaultProps = defaultProps; //# sourceMappingURL=SplitPaneResizer.cjs.map