aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
325 lines (322 loc) • 11.6 kB
JavaScript
'use client';
import { jsxs, jsx } from 'react/jsx-runtime';
import { cn } from '../../lib/utilsComprehensive.js';
import React, { forwardRef, useState } 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';
import { LiquidGlassMaterial } from '../../primitives/LiquidGlassMaterial.js';
import { useA11yId, announceToScreenReader, createFormFieldA11y } from '../../utils/a11y.js';
import { GlassButton } from '../button/GlassButton.js';
export { GlassTextarea as GlassTextArea } from './GlassTextarea.js';
/**
* GlassInput component
* A glassmorphism input field with various states and configurations
*/
const GlassInput = /*#__PURE__*/forwardRef(({
variant = "default",
size = "md",
state = "default",
fullWidth = false,
leftIcon,
rightIcon,
helperText,
errorText,
loading = false,
clearable = false,
material = "glass",
materialProps,
onClear,
className,
disabled,
label,
"aria-labelledby": ariaLabelledBy,
"aria-describedby": ariaDescribedBy,
"aria-required": ariaRequired,
"aria-invalid": ariaInvalid,
"aria-errormessage": ariaErrorMessage,
required,
...props
}, ref) => {
const [isFocused, setIsFocused] = useState(false);
// Generate unique IDs for accessibility
const inputId = useA11yId("glass-input");
const helperTextId = helperText || errorText ? useA11yId("glass-input-helper") : undefined;
const errorId = errorText ? useA11yId("glass-input-error") : undefined;
const labelId = label ? useA11yId("glass-input-label") : undefined;
const currentState = errorText ? "error" : state;
const displayHelperText = errorText || helperText;
const isInvalid = currentState === "error" || ariaInvalid === true;
const isRequired = required || ariaRequired === true;
// Create accessibility attributes
const a11yProps = createFormFieldA11y({
id: inputId,
label: !ariaLabelledBy && !labelId ? label : undefined,
description: displayHelperText,
error: errorText,
required: isRequired,
invalid: isInvalid,
disabled: disabled || loading,
labelId: ariaLabelledBy || labelId,
descriptionId: helperTextId,
errorId: errorId || ariaErrorMessage
});
// Announce error state changes
React.useEffect(() => {
if (errorText) {
announceToScreenReader(`Input error: ${errorText}`, "assertive");
}
}, [errorText]);
const sizeClasses = {
sm: "h-8 glass-px-3 glass-text-sm",
md: "h-10 glass-px-4 glass-text-sm",
lg: "h-12 px-5 glass-text-base"
};
const variantClasses = {
default: "bg-background/50 border border-border/30",
filled: "bg-muted/50 border border-transparent",
outlined: "bg-transparent border-2 border-border",
minimal: "bg-transparent border-0 border-b border-border"
};
const stateClasses = {
default: "border-border/30 focus:border-primary/50",
error: "border-destructive/50 focus:border-destructive",
warning: "border-warning/50 focus:border-warning",
success: "border-success/50 focus:border-success"
};
const iconSize = {
sm: "w-4 h-4",
md: "w-4 h-4",
lg: "w-5 h-5"
};
const handleClear = () => {
if (onClear) {
onClear();
} else if (props?.onChange) {
const event = {
target: {
value: ""
}
};
props?.onChange(event);
}
};
return jsxs("div", {
"data-glass-component": true,
className: cn("relative", {
"w-full": fullWidth
}),
children: [label && jsx("label", {
id: labelId,
htmlFor: inputId,
className: cn("block glass-text-sm font-medium text-foreground glass-mb-2", isRequired && 'after:content-["*"] after:glass-ml-1 after:text-destructive'),
children: label
}), material === "liquid" ? jsxs(LiquidGlassMaterial, {
ior: materialProps?.ior || (isFocused ? 1.46 : 1.43),
thickness: materialProps?.thickness || (size === "sm" ? 5 : size === "md" ? 7 : 9),
tint: materialProps?.tint || (currentState === "error" ? {
r: 220,
g: 38,
b: 38,
a: 0.08
} : currentState === "warning" ? {
r: 217,
g: 119,
b: 6,
a: 0.08
} : currentState === "success" ? {
r: 34,
g: 197,
b: 94,
a: 0.08
} : {
r: 0,
g: 0,
b: 0,
a: 0.04
}),
variant: materialProps?.variant || "clear",
quality: materialProps?.quality || "high",
environmentAdaptation: true,
motionResponsive: false,
interactive: true,
className: cn("relative flex items-center transition-all duration-200 liquid-glass-input-surface", sizeClasses?.[size], variantClasses?.[variant], stateClasses?.[currentState], {
"opacity-50": disabled,
"ring-2 ring-primary/20": isFocused && currentState === "default",
"ring-2 ring-destructive/20": isFocused && currentState === "error",
"ring-2 ring-warning/20": isFocused && currentState === "warning",
"ring-2 ring-success/20": isFocused && currentState === "success"
}, className),
style: {
"--liquid-glass-input-density": isFocused ? "0.95" : "0.92",
"--liquid-glass-focus-refraction": "1.15"
},
"data-liquid-glass-input": "true",
"data-input-state": currentState,
"data-input-focused": isFocused,
"aria-busy": loading || undefined,
contentProps: {
"data-liquid-glass-input": "true",
"data-input-state": currentState,
"data-input-focused": isFocused ? "true" : "false"
},
children: [leftIcon && jsx("div", {
className: cn("flex items-center justify-center mr-3 glass-text-secondary", iconSize?.[size]),
children: leftIcon
}), jsx("input", {
ref: ref,
...a11yProps,
className: cn("flex-1 bg-transparent border-0 outline-none glass-pulse-ring", "placeholder:glass-text-secondary", "text-foreground", "disabled:cursor-not-allowed", {
"pr-8": clearable || rightIcon || loading
}),
disabled: disabled || loading,
onFocus: e => {
setIsFocused(true);
props?.onFocus?.(e);
},
onBlur: e => {
setIsFocused(false);
props?.onBlur?.(e);
},
...(() => {
const {
variant,
size,
state,
fullWidth,
leftIcon,
rightIcon,
clearable,
loading: _,
loadingText: __,
material: ___,
materialProps: ____,
label: _____,
helperText: ______,
errorText: _______,
successText: ________,
warningText: _________,
description: __________,
...validProps
} = props;
return validProps;
})()
}), loading && jsx(GlassButton, {
variant: "ghost",
size: "sm",
iconOnly: true,
disabled: true,
"aria-label": "Loading",
className: cn("flex items-center justify-center glass-text-secondary", iconSize?.[size]),
children: jsx("div", {
className: 'animate-spin',
children: "\u27F3"
})
}), clearable && props.value && !loading && jsx(GlassButton, {
variant: "ghost",
size: "sm",
iconOnly: true,
onClick: () => {
if (ref && "current" in ref && ref.current) {
ref.current.value = "";
ref.current.focus();
props?.onChange?.({
target: ref.current
});
}
},
"aria-label": `Clear ${label || "input"}`,
className: cn("flex items-center justify-center glass-text-secondary hover:text-foreground transition-colors", iconSize?.[size]),
children: "\u00D7"
}), rightIcon && !loading && jsx("div", {
className: cn("flex items-center justify-center glass-text-secondary", iconSize?.[size]),
children: rightIcon
})]
}) : jsxs(OptimizedGlassCore, {
elevation: isFocused ? "level2" : "level1",
intensity: "medium",
depth: 2,
tint: "neutral",
border: "subtle",
animation: "none",
performanceMode: "medium",
liftOnHover: true,
press: true,
className: cn("relative flex items-center transition-all duration-200 rounded-2xl", sizeClasses?.[size], variantClasses?.[variant], stateClasses?.[currentState], {
"opacity-50": disabled,
"ring-2 ring-primary/20": isFocused && currentState === "default",
"ring-2 ring-destructive/20": isFocused && currentState === "error",
"ring-2 ring-warning/20": isFocused && currentState === "warning",
"ring-2 ring-success/20": isFocused && currentState === "success"
}, className),
"aria-busy": loading || undefined,
children: [leftIcon && jsx("div", {
className: cn("flex items-center justify-center mr-3 glass-text-secondary", iconSize?.[size]),
children: leftIcon
}), jsx("input", {
ref: ref,
...a11yProps,
className: cn("flex-1 bg-transparent border-0 outline-none glass-pulse-ring", "placeholder:glass-text-secondary", "text-foreground", "disabled:cursor-not-allowed", {
"pr-8": clearable || rightIcon || loading
}),
disabled: disabled || loading,
onFocus: e => {
setIsFocused(true);
props?.onFocus?.(e);
},
onBlur: e => {
setIsFocused(false);
props?.onBlur?.(e);
},
...(() => {
const {
variant,
size,
state,
fullWidth,
leftIcon,
rightIcon,
helperText,
errorText,
loading,
clearable,
onClear,
className,
label,
...validProps
} = props;
return validProps;
})()
}), jsxs("div", {
className: "glass-flex glass-items-center glass-gap-1",
children: [loading && jsx("div", {
className: cn("animate-spin glass-radius-full border-2 border-current border-t-transparent", iconSize?.[size])
}), clearable && props?.value && !loading && jsx(GlassButton, {
type: "button",
onClick: handleClear,
variant: "ghost",
size: "sm",
iconOnly: true,
"aria-label": `Clear ${label || "input"}`,
className: cn("flex items-center justify-center glass-text-secondary hover:text-foreground transition-colors", iconSize?.[size]),
children: "\u00D7"
}), rightIcon && !loading && jsx("div", {
className: cn("flex items-center justify-center glass-text-secondary", iconSize?.[size]),
children: rightIcon
})]
})]
}), displayHelperText && jsx("p", {
id: errorText ? errorId : helperTextId,
className: cn("glass-mt-1 glass-text-xs", currentState === "error" ? "text-destructive" : currentState === "warning" ? "text-warning" : currentState === "success" ? "text-success" : "glass-text-secondary"),
role: errorText ? "alert" : undefined,
"aria-live": errorText ? "polite" : undefined,
children: displayHelperText
})]
});
});
GlassInput.displayName = "GlassInput";
export { GlassInput };
//# sourceMappingURL=GlassInput.js.map