aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
139 lines (136 loc) • 4.8 kB
JavaScript
'use client';
import { jsxs, jsx } from 'react/jsx-runtime';
import React, { forwardRef } from 'react';
import { cn } from '../../lib/utilsComprehensive.js';
import { useA11yId } from '../../utils/a11y.js';
import { useMotionPreferenceContext } from '../../contexts/MotionPreferenceContext.js';
/**
* GlassSplitPane component
* Resizable split pane with glassmorphism styling
*/
const GlassSplitPane = /*#__PURE__*/forwardRef(({
// TODO: Integrate ContrastGuard for any section titles, labels, and helper text for WCAG AA compliance
direction = "horizontal",
initial = 50,
min = 20,
max = 80,
left,
right,
respectMotionPreference = true,
"aria-label": ariaLabel = "Split pane",
onSplitChange,
className,
...props
}, ref) => {
const [pct, setPct] = React.useState(initial);
const dragging = React.useRef(false);
const splitPaneId = useA11yId();
const separatorId = useA11yId();
const splitPaneLabelId = useA11yId("split-pane-label");
const leftPaneLabelId = useA11yId("split-pane-left");
const rightPaneLabelId = useA11yId("split-pane-right");
const {
prefersReducedMotion
} = useMotionPreferenceContext();
const shouldRespectMotion = respectMotionPreference && !prefersReducedMotion;
const onDown = () => dragging.current = true;
const onUp = () => dragging.current = false;
const onMove = React.useCallback(e => {
if (!dragging.current) return;
let newPct;
if (direction === "horizontal") {
newPct = e.clientX / window.innerWidth * 100;
} else {
newPct = e.clientY / window.innerHeight * 100;
}
const clampedPct = Math.max(min, Math.min(max, newPct));
setPct(clampedPct);
onSplitChange?.(clampedPct);
}, [direction, min, max, onSplitChange]);
React.useEffect(() => {
window.addEventListener("mousemove", onMove);
window.addEventListener("mouseup", onUp);
return () => {
window.removeEventListener("mousemove", onMove);
window.removeEventListener("mouseup", onUp);
};
}, [onMove]);
// Keyboard support for separator
const handleKeyDown = e => {
const step = 5; // 5% steps
let newPct = pct;
switch (e.key) {
case "ArrowLeft":
case "ArrowUp":
newPct = Math.max(min, pct - step);
break;
case "ArrowRight":
case "ArrowDown":
newPct = Math.min(max, pct + step);
break;
case "Home":
newPct = min;
break;
case "End":
newPct = max;
break;
default:
return;
}
e.preventDefault();
setPct(newPct);
onSplitChange?.(newPct);
};
return jsxs("div", {
ref: ref,
id: splitPaneId,
className: cn("relative w-full h-full", direction === "horizontal" ? "grid grid-cols-[var(--a)_12px_1fr]" : "grid grid-rows-[var(--a)_12px_1fr]",
// Motion preferences
shouldRespectMotion && "motion-safe:transition-all motion-reduce:transition-none", className),
style: {
// @ts-ignore custom var
["--a"]: `${pct}%`
},
role: "group",
"aria-labelledby": splitPaneLabelId,
...props,
children: [jsx("span", {
id: splitPaneLabelId,
className: "sr-only",
children: ariaLabel
}), jsxs("div", {
className: 'glass-min-w-0 glass-min-h-0 overflow-auto',
role: "region",
"aria-labelledby": leftPaneLabelId,
children: [jsx("span", {
id: leftPaneLabelId,
className: "sr-only",
children: direction === "horizontal" ? "Left pane" : "Top pane"
}), left]
}), jsx("div", {
id: separatorId,
role: "separator",
tabIndex: 0,
"aria-orientation": direction === "horizontal" ? "vertical" : "horizontal",
"aria-valuenow": pct,
"aria-valuemin": min,
"aria-valuemax": max,
"aria-label": `Resize ${direction === "horizontal" ? "vertical" : "horizontal"} split. Currently at ${Math.round(pct)}%`,
onMouseDown: onDown,
onKeyDown: handleKeyDown,
className: cn("select-none glass-radius-full focus:outline-none focus:ring-2 focus:ring-primary/50", direction === "horizontal" ? "w-3 h-full cursor-col-resize" : "h-3 w-full cursor-row-resize", "bg-white/10", shouldRespectMotion ? "hover:bg-white/20 transition-colors duration-200" : "hover:bg-white/20", dragging.current && "bg-white/30")
}), jsxs("div", {
className: 'glass-min-w-0 glass-min-h-0 overflow-auto',
role: "region",
"aria-labelledby": rightPaneLabelId,
children: [jsx("span", {
id: rightPaneLabelId,
className: "sr-only",
children: direction === "horizontal" ? "Right pane" : "Bottom pane"
}), right]
})]
});
});
GlassSplitPane.displayName = "GlassSplitPane";
export { GlassSplitPane, GlassSplitPane as default };
//# sourceMappingURL=GlassSplitPane.js.map