UNPKG

aura-glass

Version:

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

160 lines (157 loc) 5.88 kB
'use client'; import { jsxs, jsx } from 'react/jsx-runtime'; import { forwardRef, useRef, useEffect } from 'react'; import { cn } from '../../lib/utilsComprehensive.js'; import { AlertCircle } from 'lucide-react'; const GlassTextarea = /*#__PURE__*/forwardRef(({ variant = "default", size = "md", state = "default", fullWidth = false, label, helperText, errorText, autoResize = false, minRows = 3, maxRows = 10, showCharCount = false, maxLength, loading = false, icon, className, id, rows = minRows, value, onChange, ...props }, ref) => { const textareaRef = useRef(null); const combinedRef = ref || textareaRef; const textareaId = id || `textarea-${Math.random().toString(36).substr(2, 9)}`; const currentLength = typeof value === "string" ? value?.length || 0 : 0; const sizeConfig = { sm: { text: "glass-text-sm", padding: "glass-px-3 glass-py-2", minHeight: "min-h-[80px]" }, md: { text: "glass-text-sm", padding: "glass-px-4 glass-py-3", minHeight: "min-h-[100px]" }, lg: { text: "glass-text-base", padding: "glass-px-4 glass-py-4", minHeight: "min-h-[120px]" } }; const variantConfig = { default: { base: "bg-background/50 border border-border/20", focus: "focus:border-primary focus:ring-1 focus:ring-primary" }, filled: { base: "bg-muted/50 border border-transparent", focus: "focus:bg-background/50 focus:border-primary" }, outlined: { base: "bg-transparent border-2 border-border/20", focus: "focus:border-primary" }, minimal: { base: "bg-transparent border-0 border-b border-border/20", focus: "focus:border-primary" } }; const stateConfig = { default: "", error: "border-red-400 focus:border-red-400 focus:ring-red-400", warning: "border-amber-400 focus:border-amber-400 focus:ring-amber-400", success: "border-green-400 focus:border-green-400 focus:ring-green-400" }; const config = sizeConfig?.[size]; const variantStyles = variantConfig?.[variant]; const stateStyles = stateConfig?.[state]; const hasError = state === "error" || !!errorText; // Auto-resize functionality useEffect(() => { if (autoResize && combinedRef && "current" in combinedRef && combinedRef.current) { const textarea = combinedRef.current; const lineHeight = parseInt(getComputedStyle(textarea).lineHeight); const paddingTop = parseInt(getComputedStyle(textarea).paddingTop); const paddingBottom = parseInt(getComputedStyle(textarea).paddingBottom); textarea.style.height = "auto"; const scrollHeight = textarea.scrollHeight; const minHeight = lineHeight * minRows + paddingTop + paddingBottom; const maxHeight = lineHeight * maxRows + paddingTop + paddingBottom; textarea.style.height = `${Math.min(Math.max(scrollHeight, minHeight), maxHeight)}px`; } }, [value, autoResize, minRows, maxRows, combinedRef]); const handleChange = event => { onChange?.(event); }; return jsxs("div", { "data-glass-component": true, className: cn("glass-textarea-wrapper", fullWidth && "w-full"), children: [label && jsxs("label", { htmlFor: textareaId, className: cn("block font-medium text-foreground glass-mb-2", config.text, props?.disabled && "opacity-50"), children: [label, props?.required && jsx("span", { className: 'text-primary glass-ml-1', "aria-label": "required", children: "*" })] }), jsxs("div", { className: 'relative', children: [jsx("textarea", { ref: combinedRef, id: textareaId, rows: autoResize ? undefined : rows, value: value, onChange: handleChange, maxLength: maxLength, "aria-describedby": helperText ? `${textareaId}-helper` : errorText ? `${textareaId}-error` : undefined, "aria-invalid": hasError, className: cn("glass-textarea", "w-full resize-none glass-radius-lg glass-backdrop-blur-md transition-all duration-200 glass-pulse-ring", "placeholder:glass-text-secondary", "focus:outline-none focus:ring-offset-2 focus:ring-offset-background", // Size config.text, config.padding, !autoResize && config.minHeight, // Variant variantStyles.base, variantStyles.focus, // State stateStyles, // Disabled props?.disabled && "opacity-50 cursor-not-allowed", // Loading loading && "animate-pulse cursor-wait", // Full width fullWidth && "w-full", className), ...props }), icon && jsx("div", { className: cn("absolute top-3 right-3 glass-text-secondary", props?.disabled && "opacity-50"), children: icon }), hasError && jsx("div", { className: 'absolute top-3 right-3 text-primary', children: jsx(AlertCircle, { className: 'h-5 w-5' }) })] }), showCharCount && maxLength && jsxs("div", { className: cn("flex justify-end glass-mt-1 glass-text-xs", currentLength > maxLength * 0.9 ? "text-amber-400" : "glass-text-secondary", currentLength >= maxLength && "text-red-400"), children: [currentLength, "/", maxLength] }), helperText && !hasError && jsx("p", { id: `${textareaId}-helper`, className: cn("glass-mt-2 glass-text-xs glass-text-secondary", props?.disabled && "opacity-50"), children: helperText }), errorText && jsxs("p", { id: `${textareaId}-error`, className: 'glass-mt-2 glass-text-xs text-primary glass-flex glass-items-center glass-gap-1', children: [jsx(AlertCircle, { className: 'h-3 w-3' }), errorText] })] }); }); GlassTextarea.displayName = "GlassTextarea"; export { GlassTextarea, GlassTextarea as default }; //# sourceMappingURL=GlassTextarea.js.map