shadcn-glass-ui 1.0.0 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +123 -0
- package/README.md +128 -138
- package/dist/components.cjs +4 -4
- package/dist/components.js +1 -1
- package/dist/hooks.cjs +2 -2
- package/dist/index.cjs +5 -5
- package/dist/index.js +1 -1
- package/dist/r/ai-card-glass.json +24 -0
- package/dist/r/alert-glass.json +42 -0
- package/dist/r/avatar-glass.json +43 -0
- package/dist/r/badge-glass.json +41 -0
- package/dist/r/base-progress-glass.json +23 -0
- package/dist/r/button-glass.json +45 -0
- package/dist/r/career-stats-glass.json +24 -0
- package/dist/r/career-stats-header-glass.json +24 -0
- package/dist/r/checkbox-glass.json +42 -0
- package/dist/r/circular-metric-glass.json +23 -0
- package/dist/r/circular-progress-glass.json +40 -0
- package/dist/r/combobox-glass.json +41 -0
- package/dist/r/contribution-metrics-glass.json +23 -0
- package/dist/r/dropdown-glass.json +43 -0
- package/dist/r/expandable-header-glass.json +23 -0
- package/dist/r/flag-alert-glass.json +23 -0
- package/dist/r/flags-section-glass.json +24 -0
- package/dist/r/form-field-wrapper.json +23 -0
- package/dist/r/glass-card.json +42 -0
- package/dist/r/header-branding-glass.json +24 -0
- package/dist/r/header-nav-glass.json +25 -0
- package/dist/r/icon-button-glass.json +25 -0
- package/dist/r/input-glass.json +43 -0
- package/dist/r/interactive-card.json +24 -0
- package/dist/r/language-bar-glass.json +23 -0
- package/dist/r/metric-card-glass.json +24 -0
- package/dist/r/metrics-grid-glass.json +23 -0
- package/dist/r/modal-glass.json +43 -0
- package/dist/r/notification-glass.json +44 -0
- package/dist/r/popover-glass.json +40 -0
- package/dist/r/profile-avatar-glass.json +23 -0
- package/dist/r/profile-header-glass.json +24 -0
- package/dist/r/progress-glass.json +25 -0
- package/dist/r/projects-list-glass.json +24 -0
- package/dist/r/rainbow-progress-glass.json +23 -0
- package/dist/r/registry.json +337 -0
- package/dist/r/repository-card-glass.json +23 -0
- package/dist/r/repository-header-glass.json +24 -0
- package/dist/r/repository-metadata-glass.json +23 -0
- package/dist/r/search-box-glass.json +23 -0
- package/dist/r/segmented-control-glass.json +23 -0
- package/dist/r/skeleton-glass.json +41 -0
- package/dist/r/slider-glass.json +42 -0
- package/dist/r/sort-dropdown-glass.json +25 -0
- package/dist/r/stat-item-glass.json +25 -0
- package/dist/r/status-indicator-glass.json +23 -0
- package/dist/r/tabs-glass.json +38 -0
- package/dist/r/theme-toggle-glass.json +25 -0
- package/dist/r/toggle-glass.json +42 -0
- package/dist/r/tooltip-glass.json +42 -0
- package/dist/r/touch-target.json +23 -0
- package/dist/r/trust-score-card-glass.json +24 -0
- package/dist/r/trust-score-display-glass.json +24 -0
- package/dist/r/user-info-glass.json +24 -0
- package/dist/r/user-stats-line-glass.json +24 -0
- package/dist/r/year-card-glass.json +25 -0
- package/dist/shadcn-glass-ui.css +1 -1
- package/dist/{theme-context-DrLak65e.cjs → theme-context-e3yxC7A6.cjs} +2 -2
- package/dist/{theme-context-DrLak65e.cjs.map → theme-context-e3yxC7A6.cjs.map} +1 -1
- package/dist/themes.cjs +1 -1
- package/dist/{trust-score-card-glass-DqaCKo1w.cjs → trust-score-card-glass-CZeCRkHL.cjs} +19 -17
- package/dist/trust-score-card-glass-CZeCRkHL.cjs.map +1 -0
- package/dist/{trust-score-card-glass-tJnNNzeS.js → trust-score-card-glass-DWrcNoI2.js} +16 -14
- package/dist/trust-score-card-glass-DWrcNoI2.js.map +1 -0
- package/dist/{use-focus-6xqfE5s6.cjs → use-focus-CUkhhBRX.cjs} +2 -2
- package/dist/{use-focus-6xqfE5s6.cjs.map → use-focus-CUkhhBRX.cjs.map} +1 -1
- package/dist/{use-wallpaper-tint-D1f3UGGs.cjs → use-wallpaper-tint-b9KAZtoy.cjs} +2 -2
- package/dist/{use-wallpaper-tint-D1f3UGGs.cjs.map → use-wallpaper-tint-b9KAZtoy.cjs.map} +1 -1
- package/dist/{utils-BNzkwPwE.cjs → utils-Ba5INf7M.cjs} +2 -2
- package/dist/{utils-BNzkwPwE.cjs.map → utils-Ba5INf7M.cjs.map} +1 -1
- package/dist/utils.cjs +1 -1
- package/package.json +5 -2
- package/dist/trust-score-card-glass-DqaCKo1w.cjs.map +0 -1
- package/dist/trust-score-card-glass-tJnNNzeS.js.map +0 -1
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "dropdown-glass",
|
|
4
|
+
"type": "registry:ui",
|
|
5
|
+
"title": "Dropdown Glass",
|
|
6
|
+
"description": "Glass-themed dropdown menu based on Radix UI with:",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"@radix-ui/react-dropdown-menu",
|
|
9
|
+
"lucide-react",
|
|
10
|
+
"react"
|
|
11
|
+
],
|
|
12
|
+
"registryDependencies": [
|
|
13
|
+
"cn",
|
|
14
|
+
"primitives",
|
|
15
|
+
"variants"
|
|
16
|
+
],
|
|
17
|
+
"files": [
|
|
18
|
+
{
|
|
19
|
+
"path": "components/glass/ui/dropdown-glass.tsx",
|
|
20
|
+
"type": "registry:component",
|
|
21
|
+
"content": "/**\n * DropdownGlass Component\n *\n * Glass-themed dropdown menu based on Radix UI with:\n * - Theme-aware styling (glass/light/aurora)\n * - Smooth animations\n * - Proper positioning and accessibility\n * - Optional item icons and dividers\n *\n * @example\n * Simple API (recommended for basic dropdowns):\n * ```tsx\n * import { DropdownGlass } from '@/components/glass/ui/dropdown-glass';\n * import { MoreVertical, Edit, Trash } from 'lucide-react';\n *\n * <DropdownGlass\n * trigger={\n * <button>\n * <MoreVertical />\n * </button>\n * }\n * items={[\n * { label: 'Edit', icon: Edit, onClick: () => handleEdit() },\n * { divider: true },\n * { label: 'Delete', icon: Trash, onClick: () => handleDelete(), danger: true }\n * ]}\n * />\n * ```\n *\n * @example\n * Advanced: Using Radix UI primitives directly (for complex dropdowns):\n * ```tsx\n * import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';\n * import { getDropdownContentStyles, dropdownContentClasses } from '@/lib/variants/dropdown-content-styles';\n *\n * <DropdownMenuPrimitive.Root>\n * <DropdownMenuPrimitive.Trigger asChild>\n * <button>Open Menu</button>\n * </DropdownMenuPrimitive.Trigger>\n *\n * <DropdownMenuPrimitive.Portal>\n * <DropdownMenuPrimitive.Content\n * className={dropdownContentClasses}\n * style={getDropdownContentStyles()}\n * align=\"start\"\n * sideOffset={8}\n * >\n * <DropdownMenuPrimitive.Label className=\"px-3 py-1.5 text-xs font-medium\">\n * Actions\n * </DropdownMenuPrimitive.Label>\n *\n * <DropdownMenuPrimitive.Item\n * className=\"px-3 py-2 cursor-pointer hover:bg-[var(--dropdown-item-hover)]\"\n * onSelect={() => handleAction()}\n * >\n * Action Item\n * </DropdownMenuPrimitive.Item>\n *\n * <DropdownMenuPrimitive.Separator className=\"h-px my-1 bg-[var(--dropdown-border)]\" />\n *\n * <DropdownMenuPrimitive.CheckboxItem\n * checked={isChecked}\n * onCheckedChange={setIsChecked}\n * >\n * <DropdownMenuPrimitive.ItemIndicator>\n * <Check className=\"w-4 h-4\" />\n * </DropdownMenuPrimitive.ItemIndicator>\n * Checkbox Item\n * </DropdownMenuPrimitive.CheckboxItem>\n *\n * <DropdownMenuPrimitive.Sub>\n * <DropdownMenuPrimitive.SubTrigger>\n * More Options\n * </DropdownMenuPrimitive.SubTrigger>\n * <DropdownMenuPrimitive.SubContent>\n * <DropdownMenuPrimitive.Item>Sub Item 1</DropdownMenuPrimitive.Item>\n * <DropdownMenuPrimitive.Item>Sub Item 2</DropdownMenuPrimitive.Item>\n * </DropdownMenuPrimitive.SubContent>\n * </DropdownMenuPrimitive.Sub>\n * </DropdownMenuPrimitive.Content>\n * </DropdownMenuPrimitive.Portal>\n * </DropdownMenuPrimitive.Root>\n * ```\n *\n * @see {@link https://www.radix-ui.com/primitives/docs/components/dropdown-menu Radix UI Dropdown Menu Documentation}\n *\n * Available Radix primitives:\n * - `DropdownMenuPrimitive.Root` - Root component\n * - `DropdownMenuPrimitive.Trigger` - Trigger button (use `asChild` for custom triggers)\n * - `DropdownMenuPrimitive.Content` - Dropdown content container\n * - `DropdownMenuPrimitive.Item` - Menu item\n * - `DropdownMenuPrimitive.CheckboxItem` - Checkbox menu item\n * - `DropdownMenuPrimitive.RadioGroup` + `RadioItem` - Radio group\n * - `DropdownMenuPrimitive.Label` - Section label\n * - `DropdownMenuPrimitive.Separator` - Visual separator\n * - `DropdownMenuPrimitive.Sub` + `SubTrigger` + `SubContent` - Nested menus\n * - `DropdownMenuPrimitive.Portal` - Portal for dropdown content\n *\n * Use `getDropdownContentStyles()` and `dropdownContentClasses` from\n * `@/lib/variants/dropdown-content-styles` for consistent glass styling.\n */\n\nimport * as React from 'react';\nimport * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';\nimport type { LucideIcon } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport { ICON_SIZES } from '@/components/glass/primitives';\nimport { getDropdownContentStyles, dropdownContentClasses } from '@/lib/variants/dropdown-content-styles';\nimport '@/glass-theme.css';\n\n// ========================================\n// TYPES\n// ========================================\n\nexport interface DropdownItem {\n readonly label?: string;\n readonly icon?: LucideIcon;\n readonly onClick?: () => void;\n readonly danger?: boolean;\n readonly divider?: boolean;\n}\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\nexport interface DropdownGlassProps {\n readonly trigger: React.ReactNode;\n readonly items: readonly DropdownItem[];\n readonly align?: 'left' | 'right';\n readonly className?: string;\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\nexport const DropdownGlass = React.forwardRef<\n HTMLDivElement,\n DropdownGlassProps\n>(({ trigger, items, align = 'left', className }, ref) => {\n return (\n <div ref={ref} className={cn('relative inline-block', className)}>\n <DropdownMenuPrimitive.Root>\n <DropdownMenuPrimitive.Trigger asChild>\n {trigger}\n </DropdownMenuPrimitive.Trigger>\n\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n align={align === 'left' ? 'start' : 'end'}\n sideOffset={8}\n className={dropdownContentClasses}\n style={getDropdownContentStyles()}\n role=\"menu\"\n aria-orientation=\"vertical\"\n >\n {items.map((item, idx) =>\n item.divider ? (\n <DropdownMenuPrimitive.Separator\n key={`divider-${idx}`}\n className=\"my-2 mx-3 h-px\"\n style={{\n borderTop: '1px solid var(--dropdown-divider)',\n }}\n role=\"separator\"\n />\n ) : (\n <DropdownMenuPrimitive.Item\n key={`item-${idx}`}\n onClick={item.onClick}\n className={cn(\n 'group w-full px-3 py-2 md:px-4 md:py-2.5 text-xs md:text-sm text-left flex items-center gap-2 md:gap-3',\n 'cursor-default select-none',\n 'transition-colors duration-200 ease-out',\n 'focus-visible:outline-none focus-visible:shadow-(--focus-glow)',\n 'data-[highlighted]:bg-[var(--dropdown-item-hover)]',\n item.danger\n ? 'text-[var(--alert-danger-text)] data-[highlighted]:text-[var(--alert-danger-text)]'\n : 'text-[var(--dropdown-item-text)]'\n )}\n role=\"menuitem\"\n >\n {item.icon && (\n <item.icon\n className={cn(\n ICON_SIZES.md,\n 'transition-colors duration-200 ease-out shrink-0',\n item.danger\n ? 'text-[var(--alert-danger-text)]'\n : 'text-[var(--dropdown-icon)] group-data-[highlighted]:text-[var(--dropdown-icon-hover)]'\n )}\n />\n )}\n <span className=\"font-medium\">{item.label}</span>\n </DropdownMenuPrimitive.Item>\n )\n )}\n </DropdownMenuPrimitive.Content>\n </DropdownMenuPrimitive.Portal>\n </DropdownMenuPrimitive.Root>\n </div>\n );\n});\n\nDropdownGlass.displayName = 'DropdownGlass';\n"
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"categories": [
|
|
25
|
+
"ui"
|
|
26
|
+
],
|
|
27
|
+
"cssVars": {
|
|
28
|
+
"light": {
|
|
29
|
+
"--glass-bg": "rgba(255, 255, 255, 0.1)",
|
|
30
|
+
"--glass-border": "rgba(255, 255, 255, 0.2)",
|
|
31
|
+
"--blur-sm": "8px",
|
|
32
|
+
"--blur-md": "16px",
|
|
33
|
+
"--blur-lg": "24px"
|
|
34
|
+
},
|
|
35
|
+
"dark": {
|
|
36
|
+
"--glass-bg": "rgba(255, 255, 255, 0.05)",
|
|
37
|
+
"--glass-border": "rgba(255, 255, 255, 0.1)",
|
|
38
|
+
"--blur-sm": "8px",
|
|
39
|
+
"--blur-md": "16px",
|
|
40
|
+
"--blur-lg": "24px"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "expandable-header-glass",
|
|
4
|
+
"type": "registry:component",
|
|
5
|
+
"title": "Expandable Header Glass",
|
|
6
|
+
"description": "Expandable Header Glass component with glass effects",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"lucide-react"
|
|
9
|
+
],
|
|
10
|
+
"registryDependencies": [
|
|
11
|
+
"cn"
|
|
12
|
+
],
|
|
13
|
+
"files": [
|
|
14
|
+
{
|
|
15
|
+
"path": "components/glass/atomic/expandable-header-glass.tsx",
|
|
16
|
+
"type": "registry:component",
|
|
17
|
+
"content": "// ========================================\n// EXPANDABLE HEADER GLASS - ATOMIC COMPONENT\n// Collapsible section header with icon and chevron\n// Level 2: Atomic (extracted from FlagsSectionGlass)\n// ========================================\n\nimport {\n forwardRef,\n type ButtonHTMLAttributes,\n type ReactNode,\n type CSSProperties,\n} from 'react';\nimport { ChevronUp, ChevronDown, type LucideIcon } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport '@/glass-theme.css';\n\nexport interface ExpandableHeaderGlassProps\n extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'title'> {\n /** Header title */\n readonly title: ReactNode;\n /** Leading icon component */\n readonly icon?: LucideIcon;\n /** Icon color (CSS variable or color value) */\n readonly iconColor?: string;\n /** Expanded state */\n readonly expanded: boolean;\n /** Toggle callback */\n readonly onToggle?: () => void;\n}\n\nexport const ExpandableHeaderGlass = forwardRef<\n HTMLButtonElement,\n ExpandableHeaderGlassProps\n>(\n (\n {\n title,\n icon: Icon,\n iconColor = 'var(--text-accent)',\n expanded,\n onToggle,\n className,\n ...props\n },\n ref\n ) => {\n const textStyles: CSSProperties = {\n color: 'var(--text-primary)',\n };\n\n const chevronStyles: CSSProperties = {\n color: 'var(--text-muted)',\n };\n\n const iconStyles: CSSProperties = {\n color: iconColor,\n };\n\n return (\n <button\n ref={ref}\n type=\"button\"\n onClick={onToggle}\n aria-expanded={expanded}\n className={cn(\n 'w-full p-4 flex items-center justify-between rounded-2xl transition-colors hover:bg-white/5',\n className\n )}\n style={textStyles}\n {...props}\n >\n <div className=\"flex items-center gap-2\">\n {Icon && <Icon className=\"w-5 h-5\" style={iconStyles} />}\n <span className=\"font-medium\">{title}</span>\n </div>\n {expanded ? (\n <ChevronUp className=\"w-5 h-5\" style={chevronStyles} />\n ) : (\n <ChevronDown className=\"w-5 h-5\" style={chevronStyles} />\n )}\n </button>\n );\n }\n);\n\nExpandableHeaderGlass.displayName = 'ExpandableHeaderGlass';\n"
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"categories": [
|
|
21
|
+
"atomic"
|
|
22
|
+
]
|
|
23
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "flag-alert-glass",
|
|
4
|
+
"type": "registry:component",
|
|
5
|
+
"title": "Flag Alert Glass",
|
|
6
|
+
"description": "Flag Alert Glass component with glass effects",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"react"
|
|
9
|
+
],
|
|
10
|
+
"registryDependencies": [
|
|
11
|
+
"cn"
|
|
12
|
+
],
|
|
13
|
+
"files": [
|
|
14
|
+
{
|
|
15
|
+
"path": "components/glass/specialized/flag-alert-glass.tsx",
|
|
16
|
+
"type": "registry:component",
|
|
17
|
+
"content": "// ========================================\n// FLAG ALERT GLASS COMPONENT\n// Individual warning/danger flag alert\n// ========================================\n\nimport { forwardRef, useState, type CSSProperties } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { StatusIndicatorGlass, type StatusType } from \"./status-indicator-glass\";\nimport \"@/glass-theme.css\";\n\nexport type FlagType = \"warning\" | \"danger\";\n\nexport interface FlagAlertGlassProps extends React.HTMLAttributes<HTMLDivElement> {\n readonly type?: FlagType;\n readonly title: string;\n readonly description?: string;\n}\n\n// CSS variable maps for flag types\nconst flagVarMap: Record<FlagType, { bg: string; border: string; text: string; statusType: StatusType }> = {\n danger: {\n bg: \"var(--alert-danger-bg)\",\n border: \"var(--alert-danger-border)\",\n text: \"var(--alert-danger-text)\",\n statusType: \"red\",\n },\n warning: {\n bg: \"var(--alert-warning-bg)\",\n border: \"var(--alert-warning-border)\",\n text: \"var(--alert-warning-text)\",\n statusType: \"yellow\",\n },\n};\n\nexport const FlagAlertGlass = forwardRef<HTMLDivElement, FlagAlertGlassProps>(\n ({ type = \"warning\", title, description, className, ...props }, ref) => {\n const [isHovered, setIsHovered] = useState(false);\n const config = flagVarMap[type];\n\n const alertStyles: CSSProperties = {\n background: config.bg,\n borderColor: config.border,\n transform: isHovered ? \"translateX(4px)\" : \"translateX(0)\",\n };\n\n return (\n <div\n ref={ref}\n className={cn(\n \"p-2.5 md:p-3 rounded-xl border transition-all duration-300\",\n className\n )}\n style={alertStyles}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n role=\"alert\"\n {...props}\n >\n <div\n className=\"flex items-center gap-1.5 md:gap-2 font-medium text-xs md:text-sm\"\n style={{ color: config.text }}\n >\n <StatusIndicatorGlass type={config.statusType} />\n {title}\n </div>\n {description && (\n <p\n className=\"text-[10px] md:text-xs mt-0.5 md:mt-1 ml-4 md:ml-5\"\n style={{ color: \"var(--text-muted)\" }}\n >\n {description}\n </p>\n )}\n </div>\n );\n }\n);\n\nFlagAlertGlass.displayName = \"FlagAlertGlass\";\n"
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"categories": [
|
|
21
|
+
"specialized"
|
|
22
|
+
]
|
|
23
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "flags-section-glass",
|
|
4
|
+
"type": "registry:block",
|
|
5
|
+
"title": "Flags Section Glass",
|
|
6
|
+
"description": "Flags Section Glass component with glass effects",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"lucide-react",
|
|
9
|
+
"react"
|
|
10
|
+
],
|
|
11
|
+
"registryDependencies": [
|
|
12
|
+
"cn"
|
|
13
|
+
],
|
|
14
|
+
"files": [
|
|
15
|
+
{
|
|
16
|
+
"path": "components/glass/sections/flags-section-glass.tsx",
|
|
17
|
+
"type": "registry:component",
|
|
18
|
+
"content": "// ========================================\n// FLAGS SECTION GLASS COMPONENT\n// Expandable flags/warnings section\n// ========================================\n\nimport { forwardRef } from \"react\";\nimport { AlertTriangle, ChevronUp, ChevronDown } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport { GlassCard } from \"../ui/glass-card\";\nimport { FlagAlertGlass, type FlagType } from \"../specialized/flag-alert-glass\";\nimport \"@/glass-theme.css\";\n\nexport interface FlagData {\n readonly type: FlagType;\n readonly title: string;\n readonly description?: string;\n}\n\nexport interface FlagsSectionGlassProps extends React.HTMLAttributes<HTMLDivElement> {\n readonly flags?: readonly FlagData[];\n readonly expanded?: boolean;\n readonly onToggle?: () => void;\n}\n\nexport const FlagsSectionGlass = forwardRef<HTMLDivElement, FlagsSectionGlassProps>(\n ({ flags = [], expanded = false, onToggle, className, ...props }, ref) => {\n return (\n <GlassCard\n ref={ref}\n className={cn(className)}\n intensity=\"medium\"\n hover={false}\n {...props}\n >\n <button\n onClick={onToggle}\n className=\"w-full p-3 md:p-4 flex items-center justify-between rounded-2xl\"\n style={{ color: \"var(--text-primary)\" }}\n type=\"button\"\n aria-expanded={expanded}\n >\n <div className=\"flex items-center gap-1.5 md:gap-2\">\n <AlertTriangle className=\"w-4 h-4 md:w-5 md:h-5\" style={{ color: \"var(--status-away)\" }} />\n <span className=\"font-medium text-sm md:text-base\">{flags.length} flags detected</span>\n </div>\n {expanded ? (\n <ChevronUp className=\"w-4 h-4 md:w-5 md:h-5\" style={{ color: \"var(--text-muted)\" }} />\n ) : (\n <ChevronDown className=\"w-4 h-4 md:w-5 md:h-5\" style={{ color: \"var(--text-muted)\" }} />\n )}\n </button>\n {expanded && (\n <div className=\"px-3 pb-3 md:px-4 md:pb-4 space-y-1.5 md:space-y-2\">\n {flags.map((flag, i) => (\n <FlagAlertGlass\n key={`flag-${i}`}\n type={flag.type}\n title={flag.title}\n description={flag.description}\n />\n ))}\n </div>\n )}\n </GlassCard>\n );\n }\n);\n\nFlagsSectionGlass.displayName = \"FlagsSectionGlass\";\n"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"categories": [
|
|
22
|
+
"sections"
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "form-field-wrapper",
|
|
4
|
+
"type": "registry:lib",
|
|
5
|
+
"title": "Form Field Wrapper",
|
|
6
|
+
"description": "Unified wrapper for form controls with label, validation states, and messages.\n * Eliminates code duplication across InputGlass, SliderGlass, ComboBoxGlass, etc.\n *\n * Handles:",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"react"
|
|
9
|
+
],
|
|
10
|
+
"registryDependencies": [
|
|
11
|
+
"cn"
|
|
12
|
+
],
|
|
13
|
+
"files": [
|
|
14
|
+
{
|
|
15
|
+
"path": "components/glass/primitives/form-field-wrapper.tsx",
|
|
16
|
+
"type": "registry:lib",
|
|
17
|
+
"content": "/**\n * FormFieldWrapper Component\n *\n * Unified wrapper for form controls with label, validation states, and messages.\n * Eliminates code duplication across InputGlass, SliderGlass, ComboBoxGlass, etc.\n *\n * Handles:\n * - Label with optional required indicator\n * - Error messages (highest priority, red)\n * - Success messages (green, shown if no error)\n * - Consistent spacing and typography\n */\n\nimport { forwardRef, type ReactNode, type HTMLAttributes } from 'react';\nimport { cn } from '@/lib/utils';\n\n/**\n * Props for the FormFieldWrapper component\n */\nexport interface FormFieldWrapperProps extends HTMLAttributes<HTMLDivElement> {\n /**\n * Label text displayed above the field\n */\n label?: string;\n\n /**\n * Error message - takes priority over success\n * Displays in red below the field\n */\n error?: string;\n\n /**\n * Success message - displays in green if no error\n * Displays below the field\n */\n success?: string;\n\n /**\n * ID to link label with input via htmlFor\n * Should match the input's id attribute\n */\n htmlFor?: string;\n\n /**\n * Shows red asterisk (*) next to label\n * @default false\n */\n required?: boolean;\n\n /**\n * The form control element(s) to wrap\n */\n children: ReactNode;\n}\n\n/**\n * FormFieldWrapper component\n *\n * Provides consistent structure for form fields with labels and validation messages.\n * Used by InputGlass, SliderGlass, and other form components.\n *\n * @example\n * ```tsx\n * // Basic usage\n * <FormFieldWrapper label=\"Email\" htmlFor=\"email-input\">\n * <input id=\"email-input\" type=\"email\" />\n * </FormFieldWrapper>\n *\n * // With validation\n * <FormFieldWrapper\n * label=\"Username\"\n * error=\"Username is required\"\n * required\n * htmlFor=\"username\"\n * >\n * <input id=\"username\" />\n * </FormFieldWrapper>\n *\n * // Success state\n * <FormFieldWrapper\n * label=\"Password\"\n * success=\"Strong password\"\n * htmlFor=\"password\"\n * >\n * <input id=\"password\" type=\"password\" />\n * </FormFieldWrapper>\n * ```\n */\nexport const FormFieldWrapper = forwardRef<HTMLDivElement, FormFieldWrapperProps>(\n (\n {\n label,\n error,\n success,\n htmlFor,\n required,\n className,\n children,\n ...props\n },\n ref\n ) => {\n return (\n <div\n ref={ref}\n className={cn('flex flex-col gap-1 md:gap-1.5', className)}\n {...props}\n >\n {label && (\n <label\n htmlFor={htmlFor}\n className=\"text-xs md:text-sm font-medium\"\n style={{ color: 'var(--text-secondary)' }}\n >\n {label}\n {required && (\n <span className=\"text-[var(--alert-danger-text)] ml-1\" aria-label=\"required\">\n *\n </span>\n )}\n </label>\n )}\n\n {children}\n\n {error && (\n <p\n className=\"text-xs\"\n style={{ color: 'var(--alert-danger-text)' }}\n role=\"alert\"\n aria-live=\"polite\"\n >\n {error}\n </p>\n )}\n\n {success && !error && (\n <p\n className=\"text-xs\"\n style={{ color: 'var(--alert-success-text)' }}\n aria-live=\"polite\"\n >\n {success}\n </p>\n )}\n </div>\n );\n }\n);\n\nFormFieldWrapper.displayName = 'FormFieldWrapper';\n"
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"categories": [
|
|
21
|
+
"primitives"
|
|
22
|
+
]
|
|
23
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "glass-card",
|
|
4
|
+
"type": "registry:ui",
|
|
5
|
+
"title": "Glass Card",
|
|
6
|
+
"description": "Glass-themed container with:",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"@radix-ui/react-slot",
|
|
9
|
+
"class-variance-authority"
|
|
10
|
+
],
|
|
11
|
+
"registryDependencies": [
|
|
12
|
+
"cn",
|
|
13
|
+
"use-hover",
|
|
14
|
+
"variants"
|
|
15
|
+
],
|
|
16
|
+
"files": [
|
|
17
|
+
{
|
|
18
|
+
"path": "components/glass/ui/glass-card.tsx",
|
|
19
|
+
"type": "registry:component",
|
|
20
|
+
"content": "/**\n * GlassCard Component\n *\n * Glass-themed container with:\n * - Theme-aware styling (glass/light/aurora)\n * - Configurable blur intensity\n * - Optional glow effects\n * - Hover animations\n */\n\nimport {\n forwardRef,\n type ReactNode,\n type CSSProperties,\n} from 'react';\nimport { Slot } from '@radix-ui/react-slot';\nimport { type VariantProps } from 'class-variance-authority';\nimport { cn } from '@/lib/utils';\nimport { useHover } from '@/lib/hooks/use-hover';\nimport { cardIntensity } from '@/lib/variants/glass-card-variants';\nimport '@/glass-theme.css';\n\nimport type { GlowType, IntensityType, PaddingType } from '@/lib/variants/glass-card-variants';\n\n// ========================================\n// BLUR MAP\n// ========================================\n// Per UI_DESIGN.md design tokens:\n// - subtle: 8px (--glass-blur-sm) - light glass effect\n// - medium: 16px (--glass-blur-md) - standard cards (was 12px - breaking change)\n// - strong: 24px (--glass-blur-lg) - featured cards (was 16px - breaking change)\n\nconst blurMap: Record<IntensityType, string> = {\n subtle: 'var(--blur-sm)', // 8px\n medium: 'var(--blur-md)', // 16px (BREAKING: was 12px)\n strong: 'var(--blur-lg)', // 24px (BREAKING: was 16px)\n};\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\n/**\n * Props for the GlassCard component\n *\n * @example\n * ```tsx\n * // Basic card\n * <GlassCard intensity=\"medium\">Content</GlassCard>\n *\n * // As a clickable link\n * <GlassCard asChild intensity=\"medium\">\n * <a href=\"/details\">View Details</a>\n * </GlassCard>\n * ```\n */\nexport interface GlassCardProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, 'style'>,\n VariantProps<typeof cardIntensity> {\n /**\n * Render as child element instead of div (polymorphic rendering).\n * Useful for making cards clickable links or custom interactive elements.\n * @default false\n * @example\n * ```tsx\n * <GlassCard asChild>\n * <a href=\"/article\">Article Content</a>\n * </GlassCard>\n * ```\n */\n readonly asChild?: boolean;\n\n readonly children: ReactNode;\n readonly glow?: GlowType;\n readonly padding?: PaddingType;\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\n// CSS variable maps for intensity\nconst bgVarMap: Record<IntensityType, string> = {\n subtle: 'var(--card-subtle-bg)',\n medium: 'var(--card-medium-bg)',\n strong: 'var(--card-strong-bg)',\n};\n\nconst borderVarMap: Record<IntensityType, string> = {\n subtle: 'var(--card-subtle-border)',\n medium: 'var(--card-medium-border)',\n strong: 'var(--card-strong-border)',\n};\n\nconst glowVarMap: Record<string, string> = {\n blue: 'var(--glow-blue)',\n violet: 'var(--glow-violet)',\n purple: 'var(--glow-violet)',\n cyan: 'var(--glow-cyan)',\n};\n\nexport const GlassCard = forwardRef<HTMLDivElement, GlassCardProps>(\n (\n {\n asChild = false,\n children,\n className,\n intensity = 'medium',\n glow = null,\n hover = true,\n padding = 'default',\n ...props\n },\n ref\n ) => {\n const { isHovered, hoverProps } = useHover();\n const intensityVal = intensity ?? 'medium';\n\n const cardStyles: CSSProperties = {\n background: isHovered && hover ? 'var(--card-hover-bg)' : bgVarMap[intensityVal],\n borderColor: isHovered && hover ? 'var(--card-hover-border)' : borderVarMap[intensityVal],\n backdropFilter: `blur(${blurMap[intensityVal]})`,\n WebkitBackdropFilter: `blur(${blurMap[intensityVal]})`,\n boxShadow: glow\n ? glowVarMap[glow]\n : isHovered && hover\n ? 'var(--card-hover-glow)'\n : 'var(--glow-neutral)',\n };\n\n // Polymorphic component - render as Slot when asChild is true\n const Comp = asChild ? Slot : 'div';\n\n return (\n <Comp\n ref={ref}\n className={cn(cardIntensity({ intensity, hover, padding }), className)}\n style={cardStyles}\n onMouseEnter={hoverProps.onMouseEnter}\n onMouseLeave={hoverProps.onMouseLeave}\n {...props}\n >\n {children}\n </Comp>\n );\n }\n);\n\nGlassCard.displayName = 'GlassCard';\n"
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
"categories": [
|
|
24
|
+
"ui"
|
|
25
|
+
],
|
|
26
|
+
"cssVars": {
|
|
27
|
+
"light": {
|
|
28
|
+
"--glass-bg": "rgba(255, 255, 255, 0.1)",
|
|
29
|
+
"--glass-border": "rgba(255, 255, 255, 0.2)",
|
|
30
|
+
"--blur-sm": "8px",
|
|
31
|
+
"--blur-md": "16px",
|
|
32
|
+
"--blur-lg": "24px"
|
|
33
|
+
},
|
|
34
|
+
"dark": {
|
|
35
|
+
"--glass-bg": "rgba(255, 255, 255, 0.05)",
|
|
36
|
+
"--glass-border": "rgba(255, 255, 255, 0.1)",
|
|
37
|
+
"--blur-sm": "8px",
|
|
38
|
+
"--blur-md": "16px",
|
|
39
|
+
"--blur-lg": "24px"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "header-branding-glass",
|
|
4
|
+
"type": "registry:block",
|
|
5
|
+
"title": "Header Branding Glass",
|
|
6
|
+
"description": "Header Branding Glass component with glass effects",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"lucide-react",
|
|
9
|
+
"react"
|
|
10
|
+
],
|
|
11
|
+
"registryDependencies": [
|
|
12
|
+
"cn"
|
|
13
|
+
],
|
|
14
|
+
"files": [
|
|
15
|
+
{
|
|
16
|
+
"path": "components/glass/sections/header-branding-glass.tsx",
|
|
17
|
+
"type": "registry:component",
|
|
18
|
+
"content": "// ========================================\n// HEADER BRANDING GLASS - SECTION COMPONENT\n// Header branding with logo and subtitle\n// Level 4: Section (extracted from HeaderNavGlass)\n// ========================================\n\nimport { forwardRef, type HTMLAttributes, type CSSProperties } from 'react';\nimport { type LucideIcon } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport { IconButtonGlass } from '../atomic/icon-button-glass';\nimport '@/glass-theme.css';\n\nexport interface HeaderBrandingGlassProps extends HTMLAttributes<HTMLDivElement> {\n /** Logo icon component */\n readonly logoIcon: LucideIcon;\n /** Main title */\n readonly title: string;\n /** Subtitle (hidden on mobile) */\n readonly subtitle?: string;\n /** Logo click handler */\n readonly onLogoClick?: () => void;\n /** Logo aria label */\n readonly logoAriaLabel?: string;\n}\n\nexport const HeaderBrandingGlass = forwardRef<HTMLDivElement, HeaderBrandingGlassProps>(\n (\n {\n logoIcon,\n title,\n subtitle,\n onLogoClick,\n logoAriaLabel = 'Home',\n className,\n ...props\n },\n ref\n ) => {\n const titleStyles: CSSProperties = {\n color: 'var(--text-primary)',\n };\n\n return (\n <div\n ref={ref}\n className={cn('flex items-center gap-4', className)}\n {...props}\n >\n <IconButtonGlass\n icon={logoIcon}\n aria-label={logoAriaLabel}\n onClick={onLogoClick}\n variant=\"gradient\"\n size=\"md\"\n />\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:gap-2\">\n <span className=\"font-semibold text-base md:text-lg\" style={titleStyles}>\n {title}\n </span>\n {subtitle && (\n <span\n className=\"hidden md:inline text-sm\"\n style={{ color: 'var(--text-secondary)' }}\n >\n · {subtitle}\n </span>\n )}\n </div>\n </div>\n );\n }\n);\n\nHeaderBrandingGlass.displayName = 'HeaderBrandingGlass';\n"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"categories": [
|
|
22
|
+
"sections"
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "header-nav-glass",
|
|
4
|
+
"type": "registry:block",
|
|
5
|
+
"title": "Header Nav Glass",
|
|
6
|
+
"description": "Header Nav Glass component with glass effects",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"lucide-react",
|
|
9
|
+
"react"
|
|
10
|
+
],
|
|
11
|
+
"registryDependencies": [
|
|
12
|
+
"cn",
|
|
13
|
+
"theme-provider"
|
|
14
|
+
],
|
|
15
|
+
"files": [
|
|
16
|
+
{
|
|
17
|
+
"path": "components/glass/sections/header-nav-glass.tsx",
|
|
18
|
+
"type": "registry:component",
|
|
19
|
+
"content": "// ========================================\n// HEADER NAV GLASS COMPONENT\n// Navigation header with search and theme toggle\n// ========================================\n\nimport { forwardRef, type CSSProperties } from \"react\";\nimport { Github, Sun, Moon, Palette } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport { useTheme, type ThemeName } from \"@/lib/theme-context\";\nimport { ButtonGlass } from \"../ui/button-glass\";\nimport { SearchBoxGlass } from \"../atomic/search-box-glass\";\nimport \"@/glass-theme.css\";\n\nconst themes: ThemeName[] = [\"light\", \"aurora\", \"glass\"];\n\nconst themeConfig: Record<ThemeName, { label: string; icon: typeof Sun }> = {\n light: { label: \"Light\", icon: Sun },\n aurora: { label: \"Aurora\", icon: Moon },\n glass: { label: \"Glass\", icon: Palette },\n};\n\nexport interface HeaderNavGlassProps extends React.HTMLAttributes<HTMLElement> {\n readonly username?: string;\n readonly onSearch?: (value: string) => void;\n readonly onThemeToggle?: () => void;\n}\n\nexport const HeaderNavGlass = forwardRef<HTMLElement, HeaderNavGlassProps>(\n ({ username = \"Yhooi2\", onSearch, onThemeToggle, className, ...props }, ref) => {\n const { theme, cycleTheme } = useTheme();\n\n const nextTheme = themes[(themes.indexOf(theme) + 1) % themes.length];\n const NextIcon = themeConfig[nextTheme].icon;\n\n const headerStyles: CSSProperties = {\n background: \"var(--header-bg)\",\n borderColor: \"var(--header-border)\",\n backdropFilter: \"blur(var(--blur-md))\",\n WebkitBackdropFilter: \"blur(var(--blur-md))\",\n };\n\n const iconBtnStyles: CSSProperties = {\n background: \"linear-gradient(135deg, var(--icon-btn-from), var(--icon-btn-to))\",\n boxShadow: \"var(--icon-btn-shadow)\",\n };\n\n const themeBtnStyles: CSSProperties = {\n background: \"var(--card-subtle-bg)\",\n border: \"1px solid var(--card-subtle-border)\",\n };\n\n return (\n <header\n ref={ref}\n className={cn(\"border rounded-xl py-2 px-3 md:py-3 md:px-4 transition-all duration-300\", className)}\n style={headerStyles}\n {...props}\n >\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2 md:gap-4\">\n <button\n className=\"w-8 h-8 md:w-10 md:h-10 rounded-xl flex items-center justify-center transition-all duration-300 hover:scale-105\"\n style={iconBtnStyles}\n type=\"button\"\n aria-label=\"GitHub\"\n >\n <Github className=\"w-4 h-4 md:w-5 md:h-5\" style={{ color: \"var(--icon-btn-text)\" }} />\n </button>\n <span className=\"font-semibold text-base md:text-lg\" style={{ color: \"var(--text-primary)\" }}>\n User Analytics\n </span>\n <SearchBoxGlass\n className=\"ml-2 md:ml-4\"\n defaultValue={username}\n onSubmit={onSearch}\n inputWidth=\"w-28 sm:w-36 md:w-48\"\n placeholder=\"Search username...\"\n />\n </div>\n <div className=\"flex items-center gap-2 md:gap-3\">\n <button\n onClick={onThemeToggle ?? cycleTheme}\n className=\"p-2 md:p-2.5 rounded-xl transition-all duration-300 hover:scale-105\"\n style={themeBtnStyles}\n type=\"button\"\n aria-label={`Switch to ${themeConfig[nextTheme].label} theme`}\n >\n <NextIcon className=\"w-4 h-4 md:w-5 md:h-5\" style={{ color: \"var(--text-secondary)\" }} />\n </button>\n <ButtonGlass variant=\"secondary\" icon={Github} className=\"hidden md:inline-flex\">\n Sign in with GitHub\n </ButtonGlass>\n </div>\n </div>\n </header>\n );\n }\n);\n\nHeaderNavGlass.displayName = \"HeaderNavGlass\";\n"
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"categories": [
|
|
23
|
+
"sections"
|
|
24
|
+
]
|
|
25
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "icon-button-glass",
|
|
4
|
+
"type": "registry:component",
|
|
5
|
+
"title": "Icon Button Glass",
|
|
6
|
+
"description": "Icon Button Glass component with glass effects",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"class-variance-authority",
|
|
9
|
+
"lucide-react",
|
|
10
|
+
"react"
|
|
11
|
+
],
|
|
12
|
+
"registryDependencies": [
|
|
13
|
+
"cn"
|
|
14
|
+
],
|
|
15
|
+
"files": [
|
|
16
|
+
{
|
|
17
|
+
"path": "components/glass/atomic/icon-button-glass.tsx",
|
|
18
|
+
"type": "registry:component",
|
|
19
|
+
"content": "// ========================================\n// ICON BUTTON GLASS - ATOMIC COMPONENT\n// Glassmorphism icon button with responsive touch targets\n// Level 2: Atomic (extracted from HeaderNavGlass)\n// ========================================\n\nimport { forwardRef, type ButtonHTMLAttributes, type CSSProperties } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { type LucideIcon } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport '@/glass-theme.css';\n\nconst iconButtonVariants = cva(\n 'rounded-xl flex items-center justify-center transition-all duration-300 hover:scale-105 focus:outline-none focus:ring-2 focus:ring-offset-2',\n {\n variants: {\n size: {\n sm: 'w-8 h-8',\n md: 'w-10 h-10',\n lg: 'w-12 h-12',\n // Touch target: 44px minimum for mobile (WCAG 2.1 AA)\n touch: 'w-11 h-11 md:w-10 md:h-10',\n },\n variant: {\n gradient: '',\n subtle: '',\n ghost: 'bg-transparent hover:bg-white/10',\n },\n },\n defaultVariants: {\n size: 'md',\n variant: 'gradient',\n },\n }\n);\n\nexport interface IconButtonGlassProps\n extends ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof iconButtonVariants> {\n /** Lucide icon component */\n readonly icon: LucideIcon;\n /** Icon size in pixels (default: 20) */\n readonly iconSize?: number;\n /** Accessible label for screen readers */\n readonly 'aria-label': string;\n}\n\nexport const IconButtonGlass = forwardRef<HTMLButtonElement, IconButtonGlassProps>(\n (\n {\n icon: Icon,\n iconSize = 20,\n size,\n variant,\n className,\n 'aria-label': ariaLabel,\n ...props\n },\n ref\n ) => {\n const gradientStyles: CSSProperties | undefined =\n variant === 'gradient'\n ? {\n background:\n 'linear-gradient(135deg, var(--icon-btn-from), var(--icon-btn-to))',\n boxShadow: 'var(--icon-btn-shadow)',\n }\n : undefined;\n\n const subtleStyles: CSSProperties | undefined =\n variant === 'subtle'\n ? {\n background: 'var(--card-subtle-bg)',\n border: '1px solid var(--card-subtle-border)',\n }\n : undefined;\n\n const iconStyles: CSSProperties = {\n color: 'var(--icon-btn-text)',\n };\n\n return (\n <button\n ref={ref}\n type=\"button\"\n aria-label={ariaLabel}\n className={cn(iconButtonVariants({ size, variant }), className)}\n style={gradientStyles ?? subtleStyles}\n {...props}\n >\n <Icon size={iconSize} style={iconStyles} />\n </button>\n );\n }\n);\n\nIconButtonGlass.displayName = 'IconButtonGlass';\n"
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"categories": [
|
|
23
|
+
"atomic"
|
|
24
|
+
]
|
|
25
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "input-glass",
|
|
4
|
+
"type": "registry:ui",
|
|
5
|
+
"title": "Input Glass",
|
|
6
|
+
"description": "Glass-themed input with:",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"class-variance-authority",
|
|
9
|
+
"lucide-react"
|
|
10
|
+
],
|
|
11
|
+
"registryDependencies": [
|
|
12
|
+
"cn",
|
|
13
|
+
"primitives",
|
|
14
|
+
"use-focus",
|
|
15
|
+
"variants"
|
|
16
|
+
],
|
|
17
|
+
"files": [
|
|
18
|
+
{
|
|
19
|
+
"path": "components/glass/ui/input-glass.tsx",
|
|
20
|
+
"type": "registry:component",
|
|
21
|
+
"content": "/**\n * InputGlass Component\n *\n * Glass-themed input with:\n * - Theme-aware styling via CSS variables (glass/light/aurora)\n * - Focus glow effects\n * - Error/success states\n * - Icon support (left/right position)\n * - Backdrop blur effect\n */\n\nimport {\n forwardRef,\n useCallback,\n type InputHTMLAttributes,\n type CSSProperties,\n type FocusEvent,\n} from 'react';\nimport { type VariantProps } from 'class-variance-authority';\nimport { type LucideIcon } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport { useFocus } from '@/lib/hooks/use-focus';\nimport { inputVariants } from '@/lib/variants/input-glass-variants';\nimport { ICON_SIZES, FormFieldWrapper } from '@/components/glass/primitives';\nimport '@/glass-theme.css';\n\n// ========================================\n// CSS VARIABLE HELPERS\n// ========================================\n\nconst getInputStyles = (\n isFocused: boolean,\n error?: string,\n success?: string\n): CSSProperties => {\n // Determine border color based on state\n let borderColor = 'var(--input-border)';\n if (error) {\n borderColor = 'var(--alert-danger-text)';\n } else if (success) {\n borderColor = 'var(--alert-success-text)';\n } else if (isFocused) {\n borderColor = 'var(--input-focus-border)';\n }\n\n return {\n background: 'var(--input-bg)',\n border: `1px solid ${borderColor}`,\n color: 'var(--input-text)',\n boxShadow: isFocused ? 'var(--focus-glow)' : 'none',\n };\n};\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\n/**\n * Props for the InputGlass component\n *\n * A glass-themed input field with labels, validation states, and icon support.\n * Features focus glow effects and theme-aware styling.\n *\n * @example\n * ```tsx\n * // Basic input with label\n * <InputGlass label=\"Email\" placeholder=\"you@example.com\" />\n *\n * // With validation states\n * <InputGlass label=\"Username\" error=\"Username is required\" />\n * <InputGlass label=\"Password\" success=\"Strong password\" type=\"password\" />\n *\n * // With icon\n * <InputGlass icon={Search} placeholder=\"Search...\" />\n * <InputGlass icon={Mail} iconPosition=\"right\" />\n * ```\n */\nexport interface InputGlassProps\n extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'>,\n VariantProps<typeof inputVariants> {\n /**\n * Label text displayed above the input field\n */\n readonly label?: string;\n\n /**\n * Error message to display below the input (red styling)\n */\n readonly error?: string;\n\n /**\n * Success message to display below the input (green styling)\n */\n readonly success?: string;\n\n /**\n * Icon component from lucide-react to display\n * @example icon={Search}\n */\n readonly icon?: LucideIcon;\n\n /**\n * Position of the icon relative to input text\n * @default \"left\"\n */\n readonly iconPosition?: 'left' | 'right';\n\n /**\n * @deprecated Use `size` prop instead. Will be removed in v4.0\n * @default \"md\"\n */\n readonly inputSize?: 'sm' | 'md' | 'lg';\n\n // Note: size prop comes from VariantProps<typeof inputVariants>\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\nexport const InputGlass = forwardRef<HTMLInputElement, InputGlassProps>(\n (\n {\n className,\n size,\n inputSize,\n label,\n error,\n success,\n icon: Icon,\n iconPosition = 'left',\n disabled,\n onFocus,\n onBlur,\n ...props\n },\n ref\n ) => {\n // Determine size value with fallback to deprecated inputSize prop\n const sizeValue = size ?? inputSize ?? 'md';\n\n // Deprecation warning in development mode\n if (process.env.NODE_ENV !== 'production' && inputSize !== undefined) {\n console.warn(\n '[InputGlass] The `inputSize` prop is deprecated and will be removed in v4.0. Use `size` instead.'\n );\n }\n\n const { isFocused, focusProps } = useFocus();\n\n // Wrap focus handlers to call both internal and external callbacks\n const handleFocus = useCallback(\n (e: FocusEvent<HTMLInputElement>) => {\n focusProps.onFocus(e);\n onFocus?.(e);\n },\n [focusProps, onFocus]\n );\n\n const handleBlur = useCallback(\n (e: FocusEvent<HTMLInputElement>) => {\n focusProps.onBlur(e);\n onBlur?.(e);\n },\n [focusProps, onBlur]\n );\n\n const hasIcon = Boolean(Icon);\n const paddingLeft = hasIcon && iconPosition === 'left' ? 'pl-10' : '';\n const paddingRight = hasIcon && iconPosition === 'right' ? 'pr-10' : '';\n\n return (\n <FormFieldWrapper\n label={label}\n error={error}\n success={success}\n htmlFor={props.id}\n className={className}\n >\n <div className=\"relative\">\n {Icon && iconPosition === 'left' && (\n <Icon\n className={cn(\n 'absolute left-2.5 md:left-3 top-1/2 -translate-y-1/2 transition-colors duration-300 z-10',\n ICON_SIZES.md\n )}\n style={{\n color: isFocused ? 'var(--text-accent)' : 'var(--text-muted)',\n }}\n />\n )}\n <input\n ref={ref}\n className={cn(\n inputVariants({ size: sizeValue }),\n paddingLeft,\n paddingRight\n )}\n style={getInputStyles(isFocused, error, success)}\n disabled={disabled}\n onFocus={handleFocus}\n onBlur={handleBlur}\n {...props}\n />\n {Icon && iconPosition === 'right' && (\n <Icon\n className={cn(\n 'absolute right-2.5 md:right-3 top-1/2 -translate-y-1/2 transition-colors duration-300 z-10',\n ICON_SIZES.md\n )}\n style={{\n color: isFocused ? 'var(--text-accent)' : 'var(--text-muted)',\n }}\n />\n )}\n </div>\n </FormFieldWrapper>\n );\n }\n);\n\nInputGlass.displayName = 'InputGlass';\n"
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"categories": [
|
|
25
|
+
"ui"
|
|
26
|
+
],
|
|
27
|
+
"cssVars": {
|
|
28
|
+
"light": {
|
|
29
|
+
"--glass-bg": "rgba(255, 255, 255, 0.1)",
|
|
30
|
+
"--glass-border": "rgba(255, 255, 255, 0.2)",
|
|
31
|
+
"--blur-sm": "8px",
|
|
32
|
+
"--blur-md": "16px",
|
|
33
|
+
"--blur-lg": "24px"
|
|
34
|
+
},
|
|
35
|
+
"dark": {
|
|
36
|
+
"--glass-bg": "rgba(255, 255, 255, 0.05)",
|
|
37
|
+
"--glass-border": "rgba(255, 255, 255, 0.1)",
|
|
38
|
+
"--blur-sm": "8px",
|
|
39
|
+
"--blur-md": "16px",
|
|
40
|
+
"--blur-lg": "24px"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "interactive-card",
|
|
4
|
+
"type": "registry:lib",
|
|
5
|
+
"title": "Interactive Card",
|
|
6
|
+
"description": "Unified wrapper for card components with hover animations and glass effects.\n * Eliminates hover state duplication in MetricCardGlass, YearCardGlass,\n * AICardGlass, RepositoryCardGlass, and other card components.\n *\n * Features:",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"react"
|
|
9
|
+
],
|
|
10
|
+
"registryDependencies": [
|
|
11
|
+
"cn",
|
|
12
|
+
"use-hover"
|
|
13
|
+
],
|
|
14
|
+
"files": [
|
|
15
|
+
{
|
|
16
|
+
"path": "components/glass/primitives/interactive-card.tsx",
|
|
17
|
+
"type": "registry:lib",
|
|
18
|
+
"content": "/**\n * InteractiveCard Component\n *\n * Unified wrapper for card components with hover animations and glass effects.\n * Eliminates hover state duplication in MetricCardGlass, YearCardGlass,\n * AICardGlass, RepositoryCardGlass, and other card components.\n *\n * Features:\n * - Hover lift animation (translateY -2px)\n * - Optional glow effects\n * - Glass surface with backdrop blur\n * - Configurable backgrounds and borders\n */\n\nimport { forwardRef, type HTMLAttributes, type CSSProperties } from 'react';\nimport { cn } from '@/lib/utils';\nimport { useHover } from '@/lib/hooks/use-hover';\n\n/**\n * Props for the InteractiveCard component\n */\nexport interface InteractiveCardProps extends HTMLAttributes<HTMLDivElement> {\n /**\n * Enable hover lift effect (translateY -2px)\n * @default true\n */\n hoverLift?: boolean;\n\n /**\n * CSS variable for hover glow effect\n * @example 'var(--glow-primary)'\n */\n hoverGlow?: string;\n\n /**\n * CSS variable for hover background\n * @example 'var(--card-hover-bg)'\n */\n hoverBg?: string;\n\n /**\n * CSS variable for base background\n * @default 'var(--card-bg)'\n */\n baseBg?: string;\n\n /**\n * CSS variable for border color\n * @default 'var(--card-border)'\n */\n borderColor?: string;\n\n /**\n * CSS variable for hover border color\n */\n hoverBorderColor?: string;\n\n /**\n * Blur level for glass effect\n * @default 'sm'\n */\n blur?: 'sm' | 'md' | 'lg' | 'xl';\n\n /**\n * Disable all hover interactions\n * @default false\n */\n disabled?: boolean;\n\n /**\n * Border radius class\n * @default 'rounded-2xl'\n */\n rounded?: 'rounded-xl' | 'rounded-2xl' | 'rounded-3xl';\n\n /**\n * Transition speed\n * @default 'var(--transition-slow)'\n */\n transition?: string;\n}\n\n/**\n * InteractiveCard component\n *\n * Provides consistent hover animations and glass effects for card components.\n * Replaces ~80 lines of duplicated hover state management across 4+ components.\n *\n * @example\n * ```tsx\n * // Basic usage\n * <InteractiveCard>\n * <h3>Card Title</h3>\n * <p>Card content</p>\n * </InteractiveCard>\n *\n * // With hover effects\n * <InteractiveCard\n * hoverLift\n * hoverGlow=\"var(--glow-primary)\"\n * hoverBg=\"var(--card-hover-bg)\"\n * hoverBorderColor=\"var(--card-hover-border)\"\n * >\n * <MetricContent />\n * </InteractiveCard>\n *\n * // Custom blur and rounding\n * <InteractiveCard\n * blur=\"md\"\n * rounded=\"rounded-3xl\"\n * baseBg=\"var(--metric-emerald-bg)\"\n * >\n * <StatusCard />\n * </InteractiveCard>\n * ```\n */\nexport const InteractiveCard = forwardRef<HTMLDivElement, InteractiveCardProps>(\n (\n {\n hoverLift = true,\n hoverGlow,\n hoverBg,\n baseBg = 'var(--card-bg)',\n borderColor = 'var(--card-border)',\n hoverBorderColor,\n blur = 'sm',\n disabled = false,\n rounded = 'rounded-2xl',\n transition = 'var(--transition-slow)',\n className,\n style,\n children,\n ...props\n },\n ref\n ) => {\n const { isHovered, hoverProps } = useHover({ includeFocus: !disabled });\n\n const cardStyles: CSSProperties = {\n // Background\n background: isHovered && hoverBg ? hoverBg : baseBg,\n\n // Border\n border: `1px solid ${\n isHovered && hoverBorderColor ? hoverBorderColor : borderColor\n }`,\n\n // Glassmorphism\n backdropFilter: `blur(var(--blur-${blur}))`,\n WebkitBackdropFilter: `blur(var(--blur-${blur}))`,\n\n // Hover transform\n transform: hoverLift && isHovered && !disabled ? 'translateY(-2px)' : 'translateY(0)',\n\n // Glow effect\n boxShadow: isHovered && hoverGlow && !disabled ? hoverGlow : 'none',\n\n // Transition\n transition: `all ${transition}`,\n\n // User styles override\n ...style,\n };\n\n return (\n <div\n ref={ref}\n className={cn(rounded, className)}\n style={cardStyles}\n {...(disabled ? {} : hoverProps)}\n {...props}\n >\n {children}\n </div>\n );\n }\n);\n\nInteractiveCard.displayName = 'InteractiveCard';\n"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"categories": [
|
|
22
|
+
"primitives"
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "language-bar-glass",
|
|
4
|
+
"type": "registry:component",
|
|
5
|
+
"title": "Language Bar Glass",
|
|
6
|
+
"description": "Language Bar Glass component with glass effects",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"react"
|
|
9
|
+
],
|
|
10
|
+
"registryDependencies": [
|
|
11
|
+
"cn"
|
|
12
|
+
],
|
|
13
|
+
"files": [
|
|
14
|
+
{
|
|
15
|
+
"path": "components/glass/specialized/language-bar-glass.tsx",
|
|
16
|
+
"type": "registry:component",
|
|
17
|
+
"content": "// ========================================\n// LANGUAGE BAR GLASS COMPONENT\n// Language/skill proficiency bar with legend\n// ========================================\n\nimport { forwardRef, useState, type CSSProperties } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport \"@/glass-theme.css\";\n\nexport interface LanguageData {\n readonly name: string;\n readonly percent: number;\n readonly color?: string;\n}\n\nexport interface LanguageBarGlassProps extends React.HTMLAttributes<HTMLDivElement> {\n readonly languages: readonly LanguageData[];\n readonly showLegend?: boolean;\n}\n\nconst defaultLangColors: Record<string, string> = {\n TypeScript: \"bg-blue-500\",\n JavaScript: \"bg-yellow-400\",\n Python: \"bg-emerald-500\",\n HTML: \"bg-orange-500\",\n CSS: \"bg-purple-500\",\n Java: \"bg-red-500\",\n Go: \"bg-cyan-500\",\n Rust: \"bg-orange-600\",\n Ruby: \"bg-red-600\",\n PHP: \"bg-indigo-500\",\n};\n\nexport const LanguageBarGlass = forwardRef<HTMLDivElement, LanguageBarGlassProps>(\n ({ languages, showLegend = true, className, ...props }, ref) => {\n const [hoveredLang, setHoveredLang] = useState<number | null>(null);\n\n const barStyles: CSSProperties = {\n boxShadow: \"var(--rainbow-glow)\",\n };\n\n return (\n <div ref={ref} className={cn(\"w-full\", className)} {...props}>\n {/* Progress bar */}\n <div\n className=\"flex h-2 md:h-2.5 rounded-full overflow-hidden\"\n style={barStyles}\n role=\"group\"\n aria-label=\"Language distribution\"\n >\n {languages.map((lang, i) => {\n const colorClass = lang.color ?? defaultLangColors[lang.name] ?? \"bg-slate-400\";\n const segmentStyles: CSSProperties = {\n width: `${lang.percent}%`,\n opacity: hoveredLang !== null && hoveredLang !== i ? 0.5 : 1,\n transition: \"all 0.3s\",\n };\n\n return (\n <div\n key={`bar-${lang.name}-${i}`}\n className={cn(colorClass)}\n style={segmentStyles}\n role=\"progressbar\"\n aria-label={`${lang.name}: ${lang.percent}%`}\n aria-valuenow={lang.percent}\n aria-valuemin={0}\n aria-valuemax={100}\n onMouseEnter={() => setHoveredLang(i)}\n onMouseLeave={() => setHoveredLang(null)}\n />\n );\n })}\n </div>\n\n {/* Legend */}\n {showLegend && (\n <div\n className=\"flex items-center gap-3 md:gap-4 mt-1.5 md:mt-2 text-[10px] md:text-xs flex-wrap\"\n style={{ color: \"var(--text-secondary)\" }}\n >\n {languages.map((lang, i) => {\n const colorClass = lang.color ?? defaultLangColors[lang.name] ?? \"bg-slate-400\";\n\n return (\n <span\n key={`legend-${lang.name}-${i}`}\n className=\"flex items-center gap-1 md:gap-1.5 cursor-pointer\"\n onMouseEnter={() => setHoveredLang(i)}\n onMouseLeave={() => setHoveredLang(null)}\n >\n <span className={cn(\"w-2 h-2 md:w-2.5 md:h-2.5 rounded-full\", colorClass)} />\n {lang.name} {lang.percent}%\n </span>\n );\n })}\n </div>\n )}\n </div>\n );\n }\n);\n\nLanguageBarGlass.displayName = \"LanguageBarGlass\";\n"
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"categories": [
|
|
21
|
+
"specialized"
|
|
22
|
+
]
|
|
23
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "metric-card-glass",
|
|
4
|
+
"type": "registry:block",
|
|
5
|
+
"title": "Metric Card Glass",
|
|
6
|
+
"description": "Metric Card Glass component with glass effects",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"react"
|
|
9
|
+
],
|
|
10
|
+
"registryDependencies": [
|
|
11
|
+
"cn",
|
|
12
|
+
"variants"
|
|
13
|
+
],
|
|
14
|
+
"files": [
|
|
15
|
+
{
|
|
16
|
+
"path": "components/glass/composite/metric-card-glass.tsx",
|
|
17
|
+
"type": "registry:component",
|
|
18
|
+
"content": "// ========================================\n// METRIC CARD GLASS COMPONENT\n// Metric display card with progress\n// ========================================\n\nimport { forwardRef, type CSSProperties } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { ProgressGlass } from \"../specialized/progress-glass\";\nimport { InteractiveCard } from \"../primitives\";\nimport \"@/glass-theme.css\";\n\nimport type { ProgressGradient } from \"@/lib/variants/progress-glass-variants\";\n\nexport type MetricColor = \"emerald\" | \"amber\" | \"blue\" | \"red\";\n\nexport interface MetricCardGlassProps extends React.HTMLAttributes<HTMLDivElement> {\n readonly label: string;\n readonly value: number;\n readonly color?: MetricColor;\n}\n\n// Map MetricColor to ProgressGradient\nconst colorToGradient: Record<MetricColor, ProgressGradient> = {\n emerald: \"emerald\",\n amber: \"amber\",\n blue: \"blue\",\n red: \"rose\",\n};\n\n// CSS variable maps for metric colors\nconst metricVarMap: Record<MetricColor, { bg: string; text: string; border: string; glow: string }> = {\n emerald: {\n bg: \"var(--metric-emerald-bg)\",\n text: \"var(--metric-emerald-text)\",\n border: \"var(--metric-emerald-border)\",\n glow: \"var(--metric-emerald-glow)\",\n },\n amber: {\n bg: \"var(--metric-amber-bg)\",\n text: \"var(--metric-amber-text)\",\n border: \"var(--metric-amber-border)\",\n glow: \"var(--metric-amber-glow)\",\n },\n blue: {\n bg: \"var(--metric-blue-bg)\",\n text: \"var(--metric-blue-text)\",\n border: \"var(--metric-blue-border)\",\n glow: \"var(--metric-blue-glow)\",\n },\n red: {\n bg: \"var(--metric-red-bg)\",\n text: \"var(--metric-red-text)\",\n border: \"var(--metric-red-border)\",\n glow: \"var(--metric-red-glow)\",\n },\n};\n\nexport const MetricCardGlass = forwardRef<HTMLDivElement, MetricCardGlassProps>(\n ({ label, value, color = \"blue\", className, ...props }, ref) => {\n const colorVars = metricVarMap[color];\n\n const valueStyles: CSSProperties = {\n color: colorVars.text,\n textShadow: colorVars.glow,\n };\n\n return (\n <InteractiveCard\n ref={ref}\n baseBg={colorVars.bg}\n borderColor={colorVars.border}\n hoverGlow={colorVars.glow}\n hoverLift\n blur=\"sm\"\n rounded=\"rounded-xl\"\n className={cn(\"p-3 md:p-4\", className)}\n {...props}\n >\n <div className=\"flex flex-col items-center mb-2 md:mb-3 gap-1\">\n <span className=\"font-bold text-sm sm:text-base md:text-lg whitespace-nowrap\" style={valueStyles}>\n {value}%\n </span>\n <span\n className=\"text-[10px] sm:text-xs md:text-sm font-medium truncate\"\n style={{ color: \"var(--text-secondary)\" }}\n >\n {label}\n </span>\n </div>\n <ProgressGlass\n value={value}\n gradient={colorToGradient[color]}\n size=\"sm\"\n />\n </InteractiveCard>\n );\n }\n);\n\nMetricCardGlass.displayName = \"MetricCardGlass\";\n"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"categories": [
|
|
22
|
+
"composite"
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "metrics-grid-glass",
|
|
4
|
+
"type": "registry:block",
|
|
5
|
+
"title": "Metrics Grid Glass",
|
|
6
|
+
"description": "Metrics Grid Glass component with glass effects",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"react"
|
|
9
|
+
],
|
|
10
|
+
"registryDependencies": [
|
|
11
|
+
"cn"
|
|
12
|
+
],
|
|
13
|
+
"files": [
|
|
14
|
+
{
|
|
15
|
+
"path": "components/glass/composite/metrics-grid-glass.tsx",
|
|
16
|
+
"type": "registry:component",
|
|
17
|
+
"content": "// ========================================\n// METRICS GRID GLASS - COMPOSITE COMPONENT\n// Responsive grid of metric cards\n// Level 3: Composite (extracted from TrustScoreCardGlass)\n// ========================================\n\nimport { forwardRef, type HTMLAttributes } from 'react';\nimport { cn } from '@/lib/utils';\nimport { MetricCardGlass, type MetricColor } from './metric-card-glass';\nimport '@/glass-theme.css';\n\nexport interface MetricData {\n readonly label: string;\n readonly value: number;\n readonly color: MetricColor;\n}\n\nexport interface MetricsGridGlassProps extends HTMLAttributes<HTMLDivElement> {\n /** Array of metrics to display */\n readonly metrics: readonly MetricData[];\n /** Number of columns on desktop (1-6) */\n readonly columns?: 1 | 2 | 3 | 4 | 5 | 6;\n /** Gap size */\n readonly gap?: 'sm' | 'md' | 'lg';\n}\n\nexport const MetricsGridGlass = forwardRef<HTMLDivElement, MetricsGridGlassProps>(\n ({ metrics, columns = 4, gap = 'md', className, ...props }, ref) => {\n const gapClasses = {\n sm: 'gap-2',\n md: 'gap-4',\n lg: 'gap-6',\n };\n\n const columnClasses = {\n 1: 'grid-cols-1',\n 2: 'grid-cols-1 sm:grid-cols-2',\n 3: 'grid-cols-1 sm:grid-cols-2 md:grid-cols-3',\n 4: 'grid-cols-2 sm:grid-cols-3 md:grid-cols-4',\n 5: 'grid-cols-2 sm:grid-cols-3 md:grid-cols-5',\n 6: 'grid-cols-2 sm:grid-cols-3 md:grid-cols-6',\n };\n\n if (metrics.length === 0) {\n return null;\n }\n\n return (\n <div\n ref={ref}\n className={cn('grid', columnClasses[columns], gapClasses[gap], className)}\n {...props}\n >\n {metrics.map((metric) => (\n <MetricCardGlass\n key={metric.label}\n label={metric.label}\n value={metric.value}\n color={metric.color}\n />\n ))}\n </div>\n );\n }\n);\n\nMetricsGridGlass.displayName = 'MetricsGridGlass';\n"
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"categories": [
|
|
21
|
+
"composite"
|
|
22
|
+
]
|
|
23
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "modal-glass",
|
|
4
|
+
"type": "registry:ui",
|
|
5
|
+
"title": "Modal Glass",
|
|
6
|
+
"description": "ModalGlass Component (Compound API only)",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"lucide-react"
|
|
9
|
+
],
|
|
10
|
+
"registryDependencies": [
|
|
11
|
+
"cn",
|
|
12
|
+
"primitives",
|
|
13
|
+
"use-focus",
|
|
14
|
+
"use-hover",
|
|
15
|
+
"variants"
|
|
16
|
+
],
|
|
17
|
+
"files": [
|
|
18
|
+
{
|
|
19
|
+
"path": "components/glass/ui/modal-glass.tsx",
|
|
20
|
+
"type": "registry:component",
|
|
21
|
+
"content": "/* eslint-disable react-refresh/only-export-components */\n/**\n * ModalGlass Component (Compound API only)\n *\n * Glass-themed modal with:\n * - Theme-aware styling (glass/light/aurora)\n * - Smooth open/close animations\n * - Escape key to close\n * - Click outside to close\n * - Body scroll lock\n * - Size variants\n * - Compound component API for advanced composition\n *\n * @example\n * ```tsx\n * <ModalGlass.Root open={open} onOpenChange={setOpen}>\n * <ModalGlass.Overlay />\n * <ModalGlass.Content>\n * <ModalGlass.Header>\n * <ModalGlass.Title>Confirm</ModalGlass.Title>\n * <ModalGlass.Description>Are you sure?</ModalGlass.Description>\n * <ModalGlass.Close />\n * </ModalGlass.Header>\n * <ModalGlass.Body>\n * <p>Body content</p>\n * </ModalGlass.Body>\n * <ModalGlass.Footer>\n * <ButtonGlass onClick={() => setOpen(false)}>Cancel</ButtonGlass>\n * </ModalGlass.Footer>\n * </ModalGlass.Content>\n * </ModalGlass.Root>\n * ```\n *\n * @since v1.0.0 - Legacy API removed (isOpen/onClose/title props)\n */\n\nimport {\n useState,\n useEffect,\n useCallback,\n useMemo,\n forwardRef,\n createContext,\n useContext,\n type CSSProperties,\n type FC,\n type ReactNode,\n} from 'react';\nimport { X } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport { useHover } from '@/lib/hooks/use-hover';\nimport { useFocus } from '@/lib/hooks/use-focus';\nimport { modalSizes, type ModalSize } from '@/lib/variants/modal-glass-variants';\nimport { ICON_SIZES } from '@/components/glass/primitives';\nimport '@/glass-theme.css';\n\n// ========================================\n// TYPES & CONSTANTS\n// ========================================\n\nconst MODAL_ANIMATION_DURATION = 200;\n\n// ========================================\n// HELPERS\n// ========================================\n\nconst lockBodyScroll = (): void => {\n if (typeof document === 'undefined') return;\n document.body.style.overflow = 'hidden';\n};\n\nconst unlockBodyScroll = (): void => {\n if (typeof document === 'undefined') return;\n document.body.style.overflow = '';\n};\n\nconst delay = (ms: number): Promise<void> => {\n return new Promise((resolve) => setTimeout(resolve, ms));\n};\n\n// ========================================\n// CONTEXT FOR COMPOUND COMPONENTS\n// ========================================\n\ninterface ModalContextValue {\n isOpen: boolean;\n onClose: () => void;\n size: ModalSize;\n isClosing: boolean;\n}\n\nconst ModalContext = createContext<ModalContextValue | null>(null);\n\nconst useModalContext = () => {\n const context = useContext(ModalContext);\n if (!context) {\n throw new Error('Modal compound components must be used within ModalGlass.Root');\n }\n return context;\n};\n\n// ========================================\n// COMPOUND COMPONENT: ROOT\n// ========================================\n\ninterface ModalRootProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Open state */\n open: boolean;\n /** Callback when open state changes */\n onOpenChange?: (open: boolean) => void;\n /** Size variant */\n size?: ModalSize;\n /** Child components */\n children: ReactNode;\n}\n\nconst ModalRoot: FC<ModalRootProps> = ({\n open,\n onOpenChange,\n size = 'md',\n children,\n ...props\n}) => {\n const [isClosing, setIsClosing] = useState(false);\n\n const handleClose = useCallback(async () => {\n setIsClosing(true);\n await delay(MODAL_ANIMATION_DURATION);\n setIsClosing(false);\n onOpenChange?.(false);\n }, [onOpenChange]);\n\n useEffect(() => {\n if (open) {\n lockBodyScroll();\n } else {\n unlockBodyScroll();\n }\n return () => {\n unlockBodyScroll();\n };\n }, [open]);\n\n useEffect(() => {\n if (!open) return;\n\n const handleEscape = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') {\n handleClose();\n }\n };\n\n document.addEventListener('keydown', handleEscape);\n return () => {\n document.removeEventListener('keydown', handleEscape);\n };\n }, [open, handleClose]);\n\n if (!open) return null;\n\n return (\n <ModalContext.Provider value={{ isOpen: open, onClose: handleClose, size, isClosing }}>\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center p-2 sm:p-4\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"modal-title\"\n {...props}\n >\n {children}\n </div>\n </ModalContext.Provider>\n );\n};\n\n// ========================================\n// COMPOUND COMPONENT: OVERLAY\n// ========================================\n\ninterface ModalOverlayProps {\n className?: string;\n}\n\nconst ModalOverlay: FC<ModalOverlayProps> = ({ className }) => {\n const { onClose, isClosing } = useModalContext();\n\n const overlayStyles: CSSProperties = useMemo(\n () => ({\n background: 'var(--modal-overlay)',\n backdropFilter: 'blur(var(--blur-sm))',\n WebkitBackdropFilter: 'blur(var(--blur-sm))',\n opacity: isClosing ? 0 : 1,\n transition: 'all 0.3s',\n }),\n [isClosing]\n );\n\n return (\n <div\n className={cn('absolute inset-0 transition-all duration-300', className)}\n style={overlayStyles}\n onClick={onClose}\n aria-hidden=\"true\"\n />\n );\n};\n\n// ========================================\n// COMPOUND COMPONENT: CONTENT\n// ========================================\n\ninterface ModalContentProps {\n children: ReactNode;\n className?: string;\n}\n\nconst ModalContent = forwardRef<HTMLDivElement, ModalContentProps>(\n ({ children, className }, ref) => {\n const { size, isClosing } = useModalContext();\n\n const modalStyles: CSSProperties = useMemo(\n () => ({\n background: 'var(--modal-bg)',\n border: '1px solid var(--modal-border)',\n boxShadow: 'var(--modal-glow)',\n backdropFilter: 'blur(var(--blur-lg))',\n WebkitBackdropFilter: 'blur(var(--blur-lg))',\n transform: isClosing ? 'scale(0.95) translateY(10px)' : 'scale(1) translateY(0)',\n opacity: isClosing ? 0 : 1,\n animation: !isClosing ? 'modalFadeIn 0.3s ease-out' : 'none',\n }),\n [isClosing]\n );\n\n return (\n <div ref={ref} className={cn(modalSizes({ size }), className)} style={modalStyles}>\n {/* Glow effect */}\n <div\n className=\"absolute inset-0 rounded-3xl pointer-events-none\"\n style={{\n background: 'var(--modal-glow-effect)',\n }}\n />\n {children}\n </div>\n );\n }\n);\n\nModalContent.displayName = 'ModalContent';\n\n// ========================================\n// COMPOUND COMPONENT: HEADER\n// ========================================\n\ninterface ModalHeaderProps {\n children: ReactNode;\n className?: string;\n}\n\nconst ModalHeader: FC<ModalHeaderProps> = ({ children, className }) => {\n return (\n <div className={cn('relative flex items-start justify-between mb-4 md:mb-5', className)}>\n {children}\n </div>\n );\n};\n\n// ========================================\n// COMPOUND COMPONENT: BODY\n// ========================================\n\ninterface ModalBodyProps {\n children: ReactNode;\n className?: string;\n}\n\nconst ModalBody: FC<ModalBodyProps> = ({ children, className }) => {\n return (\n <div className={cn('relative', className)} style={{ color: 'var(--text-secondary)' }}>\n {children}\n </div>\n );\n};\n\n// ========================================\n// COMPOUND COMPONENT: FOOTER\n// ========================================\n\ninterface ModalFooterProps {\n children: ReactNode;\n className?: string;\n}\n\nconst ModalFooter: FC<ModalFooterProps> = ({ children, className }) => {\n return (\n <div className={cn('relative flex gap-3 mt-4 md:mt-5 justify-end', className)}>\n {children}\n </div>\n );\n};\n\n// ========================================\n// COMPOUND COMPONENT: TITLE\n// ========================================\n\ninterface ModalTitleProps {\n children: ReactNode;\n className?: string;\n}\n\nconst ModalTitle: FC<ModalTitleProps> = ({ children, className }) => {\n return (\n <h3\n id=\"modal-title\"\n className={cn('text-lg md:text-xl font-semibold', className)}\n style={{ color: 'var(--text-primary)' }}\n >\n {children}\n </h3>\n );\n};\n\n// ========================================\n// COMPOUND COMPONENT: DESCRIPTION\n// ========================================\n\ninterface ModalDescriptionProps {\n children: ReactNode;\n className?: string;\n}\n\nconst ModalDescription: FC<ModalDescriptionProps> = ({ children, className }) => {\n return (\n <p\n id=\"modal-description\"\n className={cn('text-sm md:text-base mt-1', className)}\n style={{ color: 'var(--text-muted)' }}\n >\n {children}\n </p>\n );\n};\n\n// ========================================\n// COMPOUND COMPONENT: CLOSE\n// ========================================\n\ninterface ModalCloseProps {\n className?: string;\n}\n\nconst ModalClose: FC<ModalCloseProps> = ({ className }) => {\n const { onClose } = useModalContext();\n const { isHovered, hoverProps } = useHover();\n const { isFocusVisible, focusProps } = useFocus({ focusVisible: true });\n\n const closeButtonStyles: CSSProperties = useMemo(\n () => ({\n background: 'var(--modal-close-btn-bg)',\n border: 'var(--modal-close-btn-border)',\n color: 'var(--text-muted)',\n boxShadow: isFocusVisible\n ? 'var(--focus-glow)'\n : isHovered\n ? 'var(--modal-close-btn-hover-glow)'\n : 'none',\n outline: 'none',\n }),\n [isHovered, isFocusVisible]\n );\n\n return (\n <button\n onClick={onClose}\n onMouseEnter={hoverProps.onMouseEnter}\n onMouseLeave={hoverProps.onMouseLeave}\n onFocus={focusProps.onFocus}\n onBlur={focusProps.onBlur}\n className={cn(\n 'p-1.5 md:p-2 rounded-xl transition-all duration-300',\n className\n )}\n style={closeButtonStyles}\n type=\"button\"\n aria-label=\"Close modal\"\n >\n <X className={ICON_SIZES.md} />\n </button>\n );\n};\n\n// ========================================\n// EXPORT COMPOUND COMPONENT (v1.0.0+)\n// ========================================\n\n/**\n * ModalGlass - Compound Component API\n *\n * @example\n * ```tsx\n * <ModalGlass.Root open={open} onOpenChange={setOpen}>\n * <ModalGlass.Overlay />\n * <ModalGlass.Content>\n * <ModalGlass.Header>\n * <ModalGlass.Title>Confirm</ModalGlass.Title>\n * <ModalGlass.Description>Are you sure?</ModalGlass.Description>\n * <ModalGlass.Close />\n * </ModalGlass.Header>\n * <ModalGlass.Body>\n * <p>Body content</p>\n * </ModalGlass.Body>\n * <ModalGlass.Footer>\n * <ButtonGlass>Cancel</ButtonGlass>\n * </ModalGlass.Footer>\n * </ModalGlass.Content>\n * </ModalGlass.Root>\n * ```\n */\nexport const ModalGlass = {\n Root: ModalRoot,\n Overlay: ModalOverlay,\n Content: ModalContent,\n Header: ModalHeader,\n Body: ModalBody,\n Footer: ModalFooter,\n Title: ModalTitle,\n Description: ModalDescription,\n Close: ModalClose,\n};\n"
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"categories": [
|
|
25
|
+
"ui"
|
|
26
|
+
],
|
|
27
|
+
"cssVars": {
|
|
28
|
+
"light": {
|
|
29
|
+
"--glass-bg": "rgba(255, 255, 255, 0.1)",
|
|
30
|
+
"--glass-border": "rgba(255, 255, 255, 0.2)",
|
|
31
|
+
"--blur-sm": "8px",
|
|
32
|
+
"--blur-md": "16px",
|
|
33
|
+
"--blur-lg": "24px"
|
|
34
|
+
},
|
|
35
|
+
"dark": {
|
|
36
|
+
"--glass-bg": "rgba(255, 255, 255, 0.05)",
|
|
37
|
+
"--glass-border": "rgba(255, 255, 255, 0.1)",
|
|
38
|
+
"--blur-sm": "8px",
|
|
39
|
+
"--blur-md": "16px",
|
|
40
|
+
"--blur-lg": "24px"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|