UNPKG

aura-glass

Version:

A comprehensive glassmorphism design system for React applications with 142+ production-ready components

192 lines (189 loc) 6.21 kB
'use client'; import { jsx, jsxs } from 'react/jsx-runtime'; import { cn } from '../../lib/utilsComprehensive.js'; import { forwardRef, useState, useCallback } from 'react'; import '../../primitives/GlassCore.js'; import '../../primitives/glass/GlassAdvanced.js'; import { OptimizedGlassCore } from '../../primitives/OptimizedGlassCore.js'; import '../../primitives/glass/OptimizedGlassAdvanced.js'; import '../../primitives/MotionNative.js'; import '../../primitives/motion/MotionFramer.js'; const StarIcon = ({ filled, halfFilled }) => jsxs("svg", { viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: "glass-w-full glass-h-full", children: [jsx("defs", { children: halfFilled && jsxs("linearGradient", { id: "halfFill", children: [jsx("stop", { offset: "50%", stopColor: "currentColor", stopOpacity: "1" }), jsx("stop", { offset: "50%", stopColor: "currentColor", stopOpacity: "0.2" })] }) }), jsx("path", { d: "M12 2L15.09 8.26L22 9.27L17 14.14L18.18 21.02L12 17.77L5.82 21.02L7 14.14L2 9.27L8.91 8.26L12 2Z", fill: halfFilled ? "url(#halfFill)" : filled ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", opacity: filled || halfFilled ? 1 : 0.3 })] }); const GlassRating = /*#__PURE__*/forwardRef(({ value = 0, max = 5, size = "md", readOnly = false, disabled = false, allowHalf = false, showValue = false, icon, emptyIcon, variant = "primary", onChange, onHoverChange, elevation = "level1", glass = true, labels, className, ...props }, ref) => { const [hoverValue, setHoverValue] = useState(null); const sizeClasses = { sm: "w-4 h-4", md: "w-6 h-6", lg: "w-8 h-8", xl: "w-10 h-10" }; const variantClasses = { primary: "text-blue-500", secondary: "text-purple-500", success: "text-green-500", warning: "text-yellow-500", danger: "text-red-500" }; const textSizeClasses = { sm: "glass-text-sm", md: "glass-text-base", lg: "glass-text-lg", xl: "glass-text-xl" }; const handleClick = useCallback((index, isHalf = false) => { if (readOnly || disabled) return; const newValue = isHalf ? index + 0.5 : index + 1; onChange?.(newValue); }, [readOnly, disabled, onChange]); const handleMouseMove = useCallback((event, index) => { if (readOnly || disabled || !allowHalf) return; const { left, width } = event.currentTarget.getBoundingClientRect(); const percent = (event.clientX - left) / width; const newHoverValue = percent < 0.5 ? index + 0.5 : index + 1; if (newHoverValue !== hoverValue) { setHoverValue(newHoverValue); onHoverChange?.(newHoverValue); } }, [readOnly, disabled, allowHalf, hoverValue, onHoverChange]); const handleMouseEnter = useCallback(index => { if (readOnly || disabled) return; const newValue = index + 1; setHoverValue(newValue); onHoverChange?.(newValue); }, [readOnly, disabled, onHoverChange]); const handleMouseLeave = useCallback(() => { if (readOnly || disabled) return; setHoverValue(null); onHoverChange?.(null); }, [readOnly, disabled, onHoverChange]); const handleKeyDown = useCallback((event, index) => { if (readOnly || disabled) return; switch (event.key) { case "ArrowLeft": case "ArrowDown": event.preventDefault(); if (index > 0) { onChange?.(index); } break; case "ArrowRight": case "ArrowUp": event.preventDefault(); if (index < max - 1) { onChange?.(index + 2); } break; case "Enter": case " ": event.preventDefault(); onChange?.(index + 1); break; } }, [readOnly, disabled, max, onChange]); const displayValue = hoverValue ?? value; const stars = Array.from({ length: max }, (_, index) => { const isFilled = index < Math.floor(displayValue); const isHalfFilled = allowHalf && index === Math.floor(displayValue) && displayValue % 1 !== 0; const label = labels?.[index] || `${index + 1} star${index !== 0 ? "s" : ""}`; return jsx("button", { "data-glass-component": true, type: "button", onClick: () => handleClick(index), onMouseMove: e => allowHalf && handleMouseMove(e, index), onMouseEnter: () => handleMouseEnter(index), onKeyDown: e => handleKeyDown(e, index), disabled: disabled || readOnly, "aria-label": label, "aria-pressed": index < value, className: cn(sizeClasses[size], variantClasses[variant], "transition-all duration-200 ease-out", !readOnly && !disabled && "cursor-pointer hover:scale-110 active:scale-95", readOnly && "cursor-default", disabled && "opacity-50 cursor-not-allowed", "focus:outline-none glass-focus glass-touch-target glass-contrast-guard"), children: icon && isFilled ? icon : emptyIcon && !isFilled && !isHalfFilled ? emptyIcon : jsx(StarIcon, { filled: isFilled, halfFilled: isHalfFilled }) }, index); }); const content = jsxs("div", { className: cn("flex items-center gap-1", disabled && "pointer-events-none"), onMouseLeave: handleMouseLeave, role: "radiogroup", "aria-label": "Rating", "aria-required": !readOnly, "aria-readonly": readOnly, children: [stars, showValue && jsxs("span", { className: cn("glass-text-secondary ml-2", textSizeClasses[size]), "aria-live": "polite", children: [displayValue.toFixed(allowHalf && displayValue % 1 !== 0 ? 1 : 0), " ", "/ ", max] })] }); if (!glass) { return jsx("div", { ref: ref, className: cn("inline-flex", className), ...props, children: content }); } return jsx(OptimizedGlassCore, { ref: ref, elevation: elevation, className: cn("inline-flex glass-p-2 glass-radius-lg", className), ...props, children: content }); }); GlassRating.displayName = "GlassRating"; export { GlassRating, GlassRating as default }; //# sourceMappingURL=GlassRating.js.map