{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "search-box-glass", "type": "registry:component", "title": "Search Box Glass", "description": "Search input with glassmorphism styling and integrated submit button.\n * Provides a cohesive search experience with visual feedback and keyboard support.\n *\n * ## Features", "dependencies": [ "lucide-react", "react", "shadcn-glass-ui" ], "registryDependencies": [ "cn" ], "files": [ { "path": "components/glass/atomic/search-box-glass.tsx", "type": "registry:component", "content": "/**\n * SearchBoxGlass Component\n *\n * Search input with glassmorphism styling and integrated submit button.\n * Provides a cohesive search experience with visual feedback and keyboard support.\n *\n * ## Features\n * - Search input with glass theme styling\n * - Built-in search icon (magnifying glass) on left side\n * - Submit button with Search icon and optional text label\n * - Compact variant (icon-only button) for mobile layouts\n * - Focus glow effect for visual feedback\n * - Enter key submission for keyboard users\n * - Controlled or uncontrolled component modes\n * - Customizable input width via Tailwind classes\n * - Responsive design (adapts button label on small screens)\n * - Accessible with proper ARIA labels\n *\n * ## CSS Variables\n * Uses InputGlass CSS variables:\n * - `--search-bg` - Input background color\n * - `--search-border` - Input border color\n * - `--search-btn-bg` - Submit button background\n * - `--search-btn-text` - Submit button text color\n * - `--text-primary` - Input text color\n * - `--focus-glow` - Focus ring glow effect\n *\n * @example Basic search box\n * ```tsx\n * import { SearchBoxGlass } from 'shadcn-glass-ui'\n *\n * function SearchBar() {\n * const handleSearch = (query: string) => {\n * console.log('Searching for:', query)\n * }\n *\n * return (\n * \n * )\n * }\n * ```\n *\n * @example Controlled search box\n * ```tsx\n * function ControlledSearch() {\n * const [query, setQuery] = useState('')\n *\n * return (\n * console.log('Submitted:', q)}\n * />\n * )\n * }\n * ```\n *\n * @example Compact variant for mobile\n * ```tsx\n * \n * ```\n *\n * @example Custom width\n * ```tsx\n * \n * ```\n *\n * @accessibility\n * - Input has aria-label matching placeholder text\n * - Submit button has aria-label \"Search\"\n * - Keyboard accessible (Tab to navigate, Enter to submit)\n * - Focus ring visible via --focus-glow CSS variable\n * - Search icon provides visual affordance\n *\n * @since v1.0.0\n */\n\nimport { forwardRef, useState, type InputHTMLAttributes, type CSSProperties } from 'react';\nimport { Search } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport '@/glass-theme.css';\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\n/**\n * Props for SearchBoxGlass component.\n *\n * Extends standard input attributes with search-specific props.\n * Omits `onSubmit` and `onChange` from HTMLInputElement to provide custom signatures.\n *\n * @example\n * ```tsx\n * const props: SearchBoxGlassProps = {\n * placeholder: 'Search users...',\n * defaultValue: 'john',\n * onSubmit: (query) => searchUsers(query),\n * variant: 'compact',\n * };\n * ```\n */\nexport interface SearchBoxGlassProps extends Omit<\n InputHTMLAttributes,\n 'onSubmit' | 'onChange'\n> {\n /**\n * Callback when search is submitted.\n *\n * Triggered by Enter key press or submit button click.\n * Receives current input value as parameter.\n *\n * @example\n * ```tsx\n * {\n * console.log('Searching:', query)\n * fetchResults(query)\n * }}\n * />\n * ```\n */\n readonly onSubmit?: (value: string) => void;\n\n /**\n * Initial search value (uncontrolled).\n *\n * Use for uncontrolled component with default value.\n * For controlled component, use `value` + `onChange` instead.\n *\n * @default \"\"\n * @example\n * ```tsx\n * \n * ```\n */\n readonly defaultValue?: string;\n\n /**\n * Controlled value.\n *\n * Use with `onChange` for controlled component mode.\n * Component is controlled if this prop is provided.\n *\n * @example\n * ```tsx\n * const [query, setQuery] = useState('')\n *\n * \n * ```\n */\n readonly value?: string;\n\n /**\n * Controlled change handler.\n *\n * Receives new input value (not event) on every change.\n * Use with `value` prop for controlled component mode.\n *\n * @example\n * ```tsx\n * setQuery(newQuery.toLowerCase())}\n * />\n * ```\n */\n readonly onChange?: (value: string) => void;\n\n /**\n * Visual variant of the search button.\n *\n * - `default`: Shows \"Search\" text label (hidden on mobile)\n * - `compact`: Icon-only button on all screen sizes\n *\n * @default \"default\"\n * @example\n * ```tsx\n * // Full button with label (responsive)\n * \n *\n * // Icon-only button (compact)\n * \n * ```\n */\n readonly variant?: 'default' | 'compact';\n\n /**\n * Input width class (Tailwind CSS).\n *\n * Controls the width of the search input field.\n * Use responsive classes for adaptive sizing.\n *\n * @default \"w-32 sm:w-40 md:w-48\"\n * @example\n * ```tsx\n * // Small search box\n * \n *\n * // Large responsive search box\n * \n * ```\n */\n readonly inputWidth?: string;\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\nexport const SearchBoxGlass = forwardRef(\n (\n {\n onSubmit,\n defaultValue = '',\n value: controlledValue,\n onChange: controlledOnChange,\n variant = 'default',\n inputWidth = 'w-32 sm:w-40 md:w-48',\n placeholder = 'Search username...',\n className,\n ...props\n },\n ref\n ) => {\n const [internalValue, setInternalValue] = useState(defaultValue);\n const [isFocused, setIsFocused] = useState(false);\n\n const isControlled = controlledValue !== undefined;\n const value = isControlled ? controlledValue : internalValue;\n\n const handleChange = (e: React.ChangeEvent) => {\n const newValue = e.target.value;\n if (isControlled) {\n controlledOnChange?.(newValue);\n } else {\n setInternalValue(newValue);\n }\n };\n\n const handleSubmit = () => {\n onSubmit?.(value);\n };\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter') {\n handleSubmit();\n }\n };\n\n const containerStyles: CSSProperties = {\n boxShadow: isFocused ? 'var(--focus-glow)' : 'none',\n };\n\n const inputStyles: CSSProperties = {\n background: 'var(--search-bg)',\n color: 'var(--text-primary)',\n border: '1px solid var(--search-border)',\n borderRight: 'none',\n borderTopLeftRadius: '0.75rem',\n borderBottomLeftRadius: '0.75rem',\n };\n\n const buttonStyles: CSSProperties = {\n background: 'var(--search-btn-bg)',\n color: 'var(--search-btn-text)',\n borderTopRightRadius: '0.75rem',\n borderBottomRightRadius: '0.75rem',\n };\n\n return (\n \n setIsFocused(true)}\n onBlur={() => setIsFocused(false)}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n aria-label={placeholder}\n className={cn('px-4 py-2 text-sm outline-none transition-all', inputWidth)}\n style={inputStyles}\n {...props}\n />\n \n \n {variant === 'default' && Search}\n \n \n );\n }\n);\n\nSearchBoxGlass.displayName = 'SearchBoxGlass';\n" } ], "categories": [ "atomic" ] }