shadcn-glass-ui 2.3.0 → 2.3.1
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 +26 -0
- package/dist/cli/index.cjs +1 -1
- package/dist/components/glass/index.d.ts +7 -5
- package/dist/components/glass/index.d.ts.map +1 -1
- package/dist/components/glass/specialized/flag-alert-glass.d.ts.map +1 -1
- package/dist/components/glass/specialized/progress-glass.d.ts.map +1 -1
- package/dist/components/glass/ui/avatar-glass.d.ts.map +1 -1
- package/dist/components/glass/ui/checkbox-glass.d.ts +21 -46
- package/dist/components/glass/ui/checkbox-glass.d.ts.map +1 -1
- package/dist/components/glass/ui/circular-progress-glass.d.ts.map +1 -1
- package/dist/components/glass/ui/dropdown-menu-glass.d.ts +11 -5
- package/dist/components/glass/ui/dropdown-menu-glass.d.ts.map +1 -1
- package/dist/components/glass/ui/glass-card.d.ts.map +1 -1
- package/dist/components/glass/ui/index.d.ts +7 -1
- package/dist/components/glass/ui/index.d.ts.map +1 -1
- package/dist/components/glass/ui/notification-glass.d.ts.map +1 -1
- package/dist/components/glass/ui/popover-glass.d.ts +6 -3
- package/dist/components/glass/ui/popover-glass.d.ts.map +1 -1
- package/dist/components/glass/ui/skeleton-glass.d.ts.map +1 -1
- package/dist/components/glass/ui/slider-glass.d.ts.map +1 -1
- package/dist/components/glass/ui/stepper-glass.d.ts.map +1 -1
- package/dist/components/glass/ui/tabs-glass.d.ts +93 -142
- package/dist/components/glass/ui/tabs-glass.d.ts.map +1 -1
- package/dist/components/glass/ui/toggle-glass.d.ts.map +1 -1
- package/dist/components.cjs +4 -4
- package/dist/components.mjs +1 -1
- package/dist/hooks.cjs +2 -2
- package/dist/index.cjs +12 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +8 -1
- package/dist/index.mjs.map +1 -1
- package/dist/lib/variants/skeleton-glass-variants.d.ts +1 -1
- package/dist/r/avatar-glass.json +1 -1
- package/dist/r/checkbox-glass.json +4 -5
- package/dist/r/circular-progress-glass.json +1 -1
- package/dist/r/dropdown-glass.json +1 -1
- package/dist/r/dropdown-menu-glass.json +1 -1
- package/dist/r/flag-alert-glass.json +1 -1
- package/dist/r/glass-card.json +3 -2
- package/dist/r/notification-glass.json +1 -1
- package/dist/r/popover-glass.json +1 -1
- package/dist/r/progress-glass.json +1 -1
- package/dist/r/registry.json +2 -2
- package/dist/r/skeleton-glass.json +1 -1
- package/dist/r/slider-glass.json +1 -1
- package/dist/r/stepper-glass.json +1 -1
- package/dist/r/tabs-glass.json +5 -4
- package/dist/r/toggle-glass.json +1 -1
- package/dist/shadcn-glass-ui.css +1 -1
- package/dist/{theme-context-DmTETrFi.cjs → theme-context-7NcW0KZL.cjs} +2 -2
- package/dist/{theme-context-DmTETrFi.cjs.map → theme-context-7NcW0KZL.cjs.map} +1 -1
- package/dist/themes.cjs +1 -1
- package/dist/{trust-score-card-glass-EfMB5l5J.mjs → trust-score-card-glass-BGqBcdyJ.mjs} +120 -177
- package/dist/trust-score-card-glass-BGqBcdyJ.mjs.map +1 -0
- package/dist/{trust-score-card-glass-3VBi9soW.cjs → trust-score-card-glass-DtgFygh5.cjs} +124 -179
- package/dist/trust-score-card-glass-DtgFygh5.cjs.map +1 -0
- package/dist/{use-focus-CswOSq71.cjs → use-focus-BFBcpBh1.cjs} +2 -2
- package/dist/{use-focus-CswOSq71.cjs.map → use-focus-BFBcpBh1.cjs.map} +1 -1
- package/dist/{use-wallpaper-tint-WtRWtupA.cjs → use-wallpaper-tint-DTTStm5f.cjs} +2 -2
- package/dist/{use-wallpaper-tint-WtRWtupA.cjs.map → use-wallpaper-tint-DTTStm5f.cjs.map} +1 -1
- package/dist/{utils-DX6rdBol.cjs → utils-CiuCe_Aq.cjs} +2 -2
- package/dist/{utils-DX6rdBol.cjs.map → utils-CiuCe_Aq.cjs.map} +1 -1
- package/dist/utils.cjs +1 -1
- package/package.json +5 -1
- package/dist/trust-score-card-glass-3VBi9soW.cjs.map +0 -1
- package/dist/trust-score-card-glass-EfMB5l5J.mjs.map +0 -1
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export type SkeletonVariant = 'text' | 'title' | 'avatar' | 'thumbnail' | 'card';
|
|
6
6
|
export declare const skeletonVariants: (props?: ({
|
|
7
|
-
variant?: "title" | "text" | "
|
|
7
|
+
variant?: "title" | "text" | "avatar" | "card" | "thumbnail" | null | undefined;
|
|
8
8
|
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
9
9
|
//# sourceMappingURL=skeleton-glass-variants.d.ts.map
|
package/dist/r/avatar-glass.json
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
{
|
|
18
18
|
"path": "components/glass/ui/avatar-glass.tsx",
|
|
19
19
|
"type": "registry:component",
|
|
20
|
-
"content": "/**\n * AvatarGlass Component\n *\n * Glass-themed avatar with:\n * - Theme-aware styling (glass/light/aurora)\n * - Glow effect on hover\n * - Optional glow-pulse animation\n * - Status indicator with glow\n * - Size variants\n * - Built on Radix UI primitives\n *\n * @example Compound API (recommended)\n * ```tsx\n * <AvatarGlass size=\"default\">\n * <AvatarGlassImage src=\"/avatar.jpg\" alt=\"User\" />\n * <AvatarGlassFallback>JD</AvatarGlassFallback>\n * </AvatarGlass>\n * ```\n *\n * @example With status indicator\n * ```tsx\n * <AvatarGlass size=\"lg\" status=\"online\">\n * <AvatarGlassImage src=\"/avatar.jpg\" alt=\"User\" />\n * <AvatarGlassFallback>JD</AvatarGlassFallback>\n * </AvatarGlass>\n * ```\n *\n * @example With glow animation\n * ```tsx\n * <AvatarGlass size=\"xl\" glowing>\n * <AvatarGlassImage src=\"/avatar.jpg\" alt=\"User\" />\n * <AvatarGlassFallback>JD</AvatarGlassFallback>\n * </AvatarGlass>\n * ```\n */\n\n'use client';\n\nimport * as React from 'react';\nimport * as AvatarPrimitive from '@radix-ui/react-avatar';\nimport { cn } from '@/lib/utils';\nimport { useHover } from '@/lib/hooks/use-hover';\nimport { avatarSizes, statusSizes } from '@/lib/variants/avatar-glass-variants';\nimport '@/glass-theme.css';\n\n// ========================================\n// TYPES\n// ========================================\n\nexport type AvatarStatus = 'online' | 'offline' | 'busy' | 'away';\nexport type AvatarSize = 'sm' | 'md' | 'lg' | 'xl';\n\n// ========================================\n// HELPERS\n// ========================================\n\nconst getStatusVars = (statusType: AvatarStatus): { bg: string; glow: string } => {\n const statusVars: Record<AvatarStatus, { bg: string; glow: string }> = {\n online: { bg: 'var(--status-online)', glow: 'var(--status-online-glow)' },\n offline: { bg: 'var(--status-offline)', glow: 'none' },\n busy: { bg: 'var(--status-busy)', glow: 'var(--status-busy-glow)' },\n away: { bg: 'var(--status-away)', glow: 'var(--status-away-glow)' },\n };\n return statusVars[statusType];\n};\n\n// ========================================\n// CONTEXT\n// ========================================\n\ninterface AvatarGlassContextValue {\n size: AvatarSize;\n status?: AvatarStatus;\n glowing?: boolean;\n}\n\nconst AvatarGlassContext = React.createContext<AvatarGlassContextValue>({\n size: 'md',\n});\n\n// ========================================\n// COMPOUND COMPONENT: ROOT\n// ========================================\n\ninterface AvatarGlassRootProps extends React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root> {\n size?: AvatarSize;\n status?: AvatarStatus;\n glowing?: boolean;\n}\n\nconst AvatarGlassRoot = React.forwardRef<\n React.ElementRef<typeof AvatarPrimitive.Root>,\n AvatarGlassRootProps\n>(({ className, size = 'md', status, glowing = false, children, ...props }, ref) => {\n const { isHovered, hoverProps } = useHover();\n\n const avatarStyles: React.CSSProperties = {\n background: 'var(--avatar-bg)',\n border: '3px solid var(--avatar-border)',\n boxShadow: isHovered ? 'var(--avatar-hover-glow)' : 'var(--avatar-shadow)',\n color: 'var(--text-inverse)',\n };\n\n return (\n <AvatarGlassContext.Provider value={{ size, status, glowing }}>\n <div\n className={cn('relative inline-flex', className)}\n onMouseEnter={hoverProps.onMouseEnter}\n onMouseLeave={hoverProps.onMouseLeave}\n >\n <AvatarPrimitive.Root\n ref={ref}\n className={cn(\n avatarSizes({ size }),\n glowing && 'animate-[glow-pulse_2s_ease-in-out_infinite]'\n )}\n style={avatarStyles}\n {...props}\n >\n {children}\n </AvatarPrimitive.Root>\n\n {/* Status indicator */}\n {status && (\n <span\n className={cn(statusSizes({ size }))}\n style={{\n background: getStatusVars(status).bg,\n boxShadow: getStatusVars(status).glow,\n }}\n role=\"status\"\n aria-label={`Status: ${status}`}\n />\n )}\n </div>\n </AvatarGlassContext.Provider>\n );\n});\n\nAvatarGlassRoot.displayName = 'AvatarGlass';\n\n// ========================================\n// COMPOUND COMPONENT: IMAGE\n// ========================================\n\ntype AvatarGlassImageProps = React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>;\n\nconst AvatarGlassImage = React.forwardRef<\n React.ElementRef<typeof AvatarPrimitive.Image>,\n AvatarGlassImageProps\n>(({ className, ...props }, ref) => {\n return (\n <AvatarPrimitive.Image\n ref={ref}\n className={cn('aspect-square h-full w-full object-cover', className)}\n {...props}\n />\n );\n});\n\nAvatarGlassImage.displayName = 'AvatarGlassImage';\n\n// ========================================\n// COMPOUND COMPONENT: FALLBACK\n// ========================================\n\ntype AvatarGlassFallbackProps = React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>;\n\nconst AvatarGlassFallback = React.forwardRef<\n React.ElementRef<typeof AvatarPrimitive.Fallback>,\n AvatarGlassFallbackProps\n>(({ className, ...props }, ref) => {\n return (\n <AvatarPrimitive.Fallback\n ref={ref}\n className={cn(\n 'flex h-full w-full items-center justify-center font-semibold uppercase',\n className\n )}\n {...props}\n />\n );\n});\n\nAvatarGlassFallback.displayName = 'AvatarGlassFallback';\n\n// ========================================\n// HELPER FUNCTION (for simple use cases)\n// ========================================\n\nconst getInitials = (name: string): string => {\n if (!name || name.trim().length === 0) return '?';\n return name\n .split(' ')\n .map((part) => part[0])\n .join('')\n .toUpperCase()\n .slice(0, 2);\n};\n\n// ========================================\n// SIMPLE WRAPPER (backward compatibility)\n// ========================================\n\ninterface AvatarGlassSimpleProps {\n name: string;\n size?: AvatarSize;\n status?: AvatarStatus;\n glowing?: boolean;\n className?: string;\n}\n\nconst AvatarGlassSimple: React.FC<AvatarGlassSimpleProps> = ({\n name,\n size = 'md',\n status,\n glowing,\n className,\n}) => {\n return (\n <AvatarGlassRoot size={size} status={status} glowing={glowing} className={className}>\n <AvatarGlassFallback>{getInitials(name)}</AvatarGlassFallback>\n </AvatarGlassRoot>\n );\n};\n\n// ========================================\n// EXPORTS\n// ========================================\n\n// Compound API (shadcn/ui pattern)\nexport const AvatarGlass = AvatarGlassRoot;\nexport { AvatarGlassImage, AvatarGlassFallback };\n\n// Simple wrapper (backward compatibility)\nexport { AvatarGlassSimple };\n"
|
|
20
|
+
"content": "/**\n * AvatarGlass Component\n *\n * Glass-themed avatar with:\n * - Theme-aware styling (glass/light/aurora)\n * - Glow effect on hover\n * - Optional glow-pulse animation\n * - Status indicator with glow\n * - Size variants\n * - Built on Radix UI primitives\n *\n * @example Compound API (recommended)\n * ```tsx\n * <AvatarGlass size=\"default\">\n * <AvatarGlassImage src=\"/avatar.jpg\" alt=\"User\" />\n * <AvatarGlassFallback>JD</AvatarGlassFallback>\n * </AvatarGlass>\n * ```\n *\n * @example With status indicator\n * ```tsx\n * <AvatarGlass size=\"lg\" status=\"online\">\n * <AvatarGlassImage src=\"/avatar.jpg\" alt=\"User\" />\n * <AvatarGlassFallback>JD</AvatarGlassFallback>\n * </AvatarGlass>\n * ```\n *\n * @example With glow animation\n * ```tsx\n * <AvatarGlass size=\"xl\" glowing>\n * <AvatarGlassImage src=\"/avatar.jpg\" alt=\"User\" />\n * <AvatarGlassFallback>JD</AvatarGlassFallback>\n * </AvatarGlass>\n * ```\n */\n\n'use client';\n\nimport * as React from 'react';\nimport * as AvatarPrimitive from '@radix-ui/react-avatar';\nimport { cn } from '@/lib/utils';\nimport { useHover } from '@/lib/hooks/use-hover';\nimport { avatarSizes, statusSizes } from '@/lib/variants/avatar-glass-variants';\nimport '@/glass-theme.css';\n\n// ========================================\n// TYPES\n// ========================================\n\nexport type AvatarStatus = 'online' | 'offline' | 'busy' | 'away';\nexport type AvatarSize = 'sm' | 'md' | 'lg' | 'xl';\n\n// ========================================\n// HELPERS\n// ========================================\n\nconst getStatusVars = (statusType: AvatarStatus): { bg: string; glow: string } => {\n const statusVars: Record<AvatarStatus, { bg: string; glow: string }> = {\n online: { bg: 'var(--status-online)', glow: 'var(--status-online-glow)' },\n offline: { bg: 'var(--status-offline)', glow: 'none' },\n busy: { bg: 'var(--status-busy)', glow: 'var(--status-busy-glow)' },\n away: { bg: 'var(--status-away)', glow: 'var(--status-away-glow)' },\n };\n return statusVars[statusType];\n};\n\n// ========================================\n// CONTEXT\n// ========================================\n\ninterface AvatarGlassContextValue {\n size: AvatarSize;\n status?: AvatarStatus;\n glowing?: boolean;\n}\n\nconst AvatarGlassContext = React.createContext<AvatarGlassContextValue>({\n size: 'md',\n});\n\n// ========================================\n// COMPOUND COMPONENT: ROOT\n// ========================================\n\ninterface AvatarGlassRootProps extends React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root> {\n size?: AvatarSize;\n status?: AvatarStatus;\n glowing?: boolean;\n}\n\nconst AvatarGlassRoot = React.forwardRef<\n React.ElementRef<typeof AvatarPrimitive.Root>,\n AvatarGlassRootProps\n>(({ className, size = 'md', status, glowing = false, children, ...props }, ref) => {\n const { isHovered, hoverProps } = useHover();\n\n const avatarStyles: React.CSSProperties = {\n background: 'var(--avatar-bg)',\n border: '3px solid var(--avatar-border)',\n boxShadow: isHovered ? 'var(--avatar-hover-glow)' : 'var(--avatar-shadow)',\n color: 'var(--text-inverse)',\n };\n\n return (\n <AvatarGlassContext.Provider value={{ size, status, glowing }}>\n <div\n data-slot=\"avatar\"\n className={cn('relative inline-flex', className)}\n onMouseEnter={hoverProps.onMouseEnter}\n onMouseLeave={hoverProps.onMouseLeave}\n >\n <AvatarPrimitive.Root\n ref={ref}\n className={cn(\n avatarSizes({ size }),\n glowing && 'animate-[glow-pulse_2s_ease-in-out_infinite]'\n )}\n style={avatarStyles}\n {...props}\n >\n {children}\n </AvatarPrimitive.Root>\n\n {/* Status indicator */}\n {status && (\n <span\n data-slot=\"avatar-status\"\n className={cn(statusSizes({ size }))}\n style={{\n background: getStatusVars(status).bg,\n boxShadow: getStatusVars(status).glow,\n }}\n role=\"status\"\n aria-label={`Status: ${status}`}\n />\n )}\n </div>\n </AvatarGlassContext.Provider>\n );\n});\n\nAvatarGlassRoot.displayName = 'AvatarGlass';\n\n// ========================================\n// COMPOUND COMPONENT: IMAGE\n// ========================================\n\ntype AvatarGlassImageProps = React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>;\n\nconst AvatarGlassImage = React.forwardRef<\n React.ElementRef<typeof AvatarPrimitive.Image>,\n AvatarGlassImageProps\n>(({ className, ...props }, ref) => {\n return (\n <AvatarPrimitive.Image\n ref={ref}\n data-slot=\"avatar-image\"\n className={cn('aspect-square h-full w-full object-cover', className)}\n {...props}\n />\n );\n});\n\nAvatarGlassImage.displayName = 'AvatarGlassImage';\n\n// ========================================\n// COMPOUND COMPONENT: FALLBACK\n// ========================================\n\ntype AvatarGlassFallbackProps = React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>;\n\nconst AvatarGlassFallback = React.forwardRef<\n React.ElementRef<typeof AvatarPrimitive.Fallback>,\n AvatarGlassFallbackProps\n>(({ className, ...props }, ref) => {\n return (\n <AvatarPrimitive.Fallback\n ref={ref}\n data-slot=\"avatar-fallback\"\n className={cn(\n 'flex h-full w-full items-center justify-center font-semibold uppercase',\n className\n )}\n {...props}\n />\n );\n});\n\nAvatarGlassFallback.displayName = 'AvatarGlassFallback';\n\n// ========================================\n// HELPER FUNCTION (for simple use cases)\n// ========================================\n\nconst getInitials = (name: string): string => {\n if (!name || name.trim().length === 0) return '?';\n return name\n .split(' ')\n .map((part) => part[0])\n .join('')\n .toUpperCase()\n .slice(0, 2);\n};\n\n// ========================================\n// SIMPLE WRAPPER (backward compatibility)\n// ========================================\n\ninterface AvatarGlassSimpleProps {\n name: string;\n size?: AvatarSize;\n status?: AvatarStatus;\n glowing?: boolean;\n className?: string;\n}\n\nconst AvatarGlassSimple: React.FC<AvatarGlassSimpleProps> = ({\n name,\n size = 'md',\n status,\n glowing,\n className,\n}) => {\n return (\n <AvatarGlassRoot size={size} status={status} glowing={glowing} className={className}>\n <AvatarGlassFallback>{getInitials(name)}</AvatarGlassFallback>\n </AvatarGlassRoot>\n );\n};\n\n// ========================================\n// EXPORTS\n// ========================================\n\n// Compound API (shadcn/ui pattern)\nexport const AvatarGlass = AvatarGlassRoot;\nexport { AvatarGlassImage, AvatarGlassFallback };\n\n// Simple wrapper (backward compatibility)\nexport { AvatarGlassSimple };\n"
|
|
21
21
|
}
|
|
22
22
|
],
|
|
23
23
|
"categories": [
|
|
@@ -3,22 +3,21 @@
|
|
|
3
3
|
"name": "checkbox-glass",
|
|
4
4
|
"type": "registry:ui",
|
|
5
5
|
"title": "Checkbox Glass",
|
|
6
|
-
"description": "Glass-themed checkbox with:",
|
|
6
|
+
"description": "Glass-themed checkbox built on Radix UI primitives with:",
|
|
7
7
|
"dependencies": [
|
|
8
|
+
"@radix-ui/react-checkbox",
|
|
8
9
|
"lucide-react",
|
|
9
10
|
"react",
|
|
10
11
|
"shadcn-glass-ui"
|
|
11
12
|
],
|
|
12
13
|
"registryDependencies": [
|
|
13
|
-
"cn"
|
|
14
|
-
"use-focus",
|
|
15
|
-
"use-hover"
|
|
14
|
+
"cn"
|
|
16
15
|
],
|
|
17
16
|
"files": [
|
|
18
17
|
{
|
|
19
18
|
"path": "components/glass/ui/checkbox-glass.tsx",
|
|
20
19
|
"type": "registry:component",
|
|
21
|
-
"content": "/**\n * CheckboxGlass Component\n *\n * Glass-themed checkbox with:\n * - Theme-aware styling (glass/light/aurora)\n * - Glow effect on hover\n * - Optional label\n */\n\nimport * as React from 'react';\nimport
|
|
20
|
+
"content": "/**\n * CheckboxGlass Component\n *\n * Glass-themed checkbox built on Radix UI primitives with:\n * - 100% shadcn/ui type compatibility\n * - Theme-aware styling (glass/light/aurora)\n * - Glow effect on hover/focus\n * - Optional label\n *\n * @since v2.2.6 - Migrated to Radix UI primitives for full type compatibility\n */\n\nimport * as React from 'react';\nimport * as CheckboxPrimitive from '@radix-ui/react-checkbox';\nimport { Check, Minus } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport '@/glass-theme.css';\n\n// ========================================\n// TYPES\n// ========================================\n\n/**\n * Checked state type compatible with shadcn/ui (Radix UI)\n */\nexport type CheckedState = boolean | 'indeterminate';\n\n/**\n * Props for the CheckboxGlass component\n *\n * Extends Radix UI Checkbox.Root props for 100% shadcn/ui compatibility.\n * All Radix props are supported including: checked, defaultChecked, onCheckedChange,\n * disabled, required, name, value, etc.\n *\n * **Type Compatibility (v2.3.1+):**\n * - Extends `React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>`\n * - No more `as unknown as` type assertions needed\n * - Full IntelliSense for all Radix props\n *\n * @accessibility\n * - **Keyboard Navigation:** Space to toggle (WCAG 2.1.1)\n * - **Focus Management:** Visible focus ring using `--focus-glow` CSS variable (WCAG 2.4.7)\n * - **Screen Readers:** Radix UI handles ARIA attributes automatically\n * - **Touch Targets:** 44x44px minimum touch area per Apple HIG (WCAG 2.5.5)\n * - **Color Contrast:** Check mark and backgrounds meet WCAG AA contrast ratio 4.5:1\n *\n * @example\n * ```tsx\n * // shadcn/ui compatible API\n * <CheckboxGlass\n * checked={isChecked}\n * onCheckedChange={(checked) => setIsChecked(checked === true)}\n * />\n *\n * // Indeterminate state (tri-state checkbox)\n * <CheckboxGlass\n * checked={allSelected ? true : someSelected ? 'indeterminate' : false}\n * onCheckedChange={(checked) => {\n * if (checked === true) selectAll();\n * else deselectAll();\n * }}\n * label=\"Select all\"\n * />\n *\n * // Legacy API (still supported)\n * <CheckboxGlass checked={agreed} onChange={setAgreed} label=\"I agree to terms\" />\n * ```\n *\n * @since v2.3.0 - Added shadcn/ui compatible `onCheckedChange` and `indeterminate` support\n * @since v2.2.6 - Migrated to Radix UI primitives for full type compatibility\n */\nexport interface CheckboxGlassProps extends Omit<\n React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>,\n 'onChange'\n> {\n /**\n * @deprecated Use `onCheckedChange` instead. Will be removed in v4.0.\n * Legacy callback for backwards compatibility.\n */\n readonly onChange?: (checked: boolean) => void;\n /** Optional label text (Glass UI extension) */\n readonly label?: string;\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\nexport const CheckboxGlass = React.forwardRef<\n React.ElementRef<typeof CheckboxPrimitive.Root>,\n CheckboxGlassProps\n>(({ className, label, onChange, onCheckedChange, disabled, checked, ...props }, ref) => {\n // Wrapper for legacy onChange callback\n const handleCheckedChange = React.useCallback(\n (newChecked: CheckedState) => {\n onCheckedChange?.(newChecked);\n // Legacy support (deprecated)\n if (onChange && typeof newChecked === 'boolean') {\n onChange(newChecked);\n }\n },\n [onCheckedChange, onChange]\n );\n\n // Determine visual states\n const isIndeterminate = checked === 'indeterminate';\n const isChecked = checked === true;\n const showIndicator = isChecked || isIndeterminate;\n\n // Inline styles for CSS variables (matches original implementation)\n const checkboxStyles: React.CSSProperties = {\n background: showIndicator ? 'var(--checkbox-checked-bg)' : 'var(--checkbox-bg)',\n borderColor: showIndicator ? 'var(--checkbox-checked-bg)' : 'var(--checkbox-border)',\n };\n\n return (\n <label\n className={cn(\n 'inline-flex items-center gap-2 md:gap-2.5',\n disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer',\n className\n )}\n >\n {/* Touch area wrapper - 44px minimum for Apple HIG compliance */}\n <span className=\"inline-flex items-center justify-center min-w-11 min-h-11\">\n <CheckboxPrimitive.Root\n ref={ref}\n data-slot=\"checkbox\"\n checked={checked}\n onCheckedChange={handleCheckedChange}\n disabled={disabled}\n className={cn(\n 'peer relative w-6 h-6 md:w-5 md:h-5 shrink-0',\n 'rounded-md border-2 transition-all duration-300',\n 'flex items-center justify-center',\n // Focus state\n 'focus-visible:outline-none focus-visible:shadow-(--focus-glow)',\n // Hover state\n 'hover:shadow-(--checkbox-glow)',\n // Disabled state\n 'disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:shadow-none'\n )}\n style={checkboxStyles}\n {...props}\n >\n <CheckboxPrimitive.Indicator\n data-slot=\"checkbox-indicator\"\n className=\"flex items-center justify-center text-current\"\n >\n {isIndeterminate ? (\n <Minus\n className=\"w-3.5 h-3.5 md:w-3 md:h-3\"\n style={{ color: 'var(--text-inverse)' }}\n />\n ) : (\n <Check\n className=\"w-3.5 h-3.5 md:w-3 md:h-3\"\n style={{ color: 'var(--text-inverse)' }}\n />\n )}\n </CheckboxPrimitive.Indicator>\n </CheckboxPrimitive.Root>\n </span>\n {label && (\n <span className=\"text-xs md:text-sm\" style={{ color: 'var(--text-secondary)' }}>\n {label}\n </span>\n )}\n </label>\n );\n});\n\nCheckboxGlass.displayName = 'CheckboxGlass';\n\n/**\n * Checkbox - shadcn/ui compatible alias for CheckboxGlass\n *\n * @example\n * ```tsx\n * import { Checkbox } from 'shadcn-glass-ui'\n *\n * <Checkbox\n * checked={isChecked}\n * onCheckedChange={(checked) => setIsChecked(checked === true)}\n * />\n * ```\n *\n * @since v2.3.0\n */\nexport const Checkbox = CheckboxGlass;\n"
|
|
22
21
|
}
|
|
23
22
|
],
|
|
24
23
|
"categories": [
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
{
|
|
16
16
|
"path": "components/glass/ui/circular-progress-glass.tsx",
|
|
17
17
|
"type": "registry:component",
|
|
18
|
-
"content": "/**\n * CircularProgressGlass Component\n *\n * SVG-based circular progress indicator with:\n * - Determinate and indeterminate variants\n * - Configurable size and thickness\n * - Glow effect with SVG filters\n * - Theme-aware styling\n * - Optional label in center\n * - Gradient colors support\n */\n\nimport { forwardRef, useMemo, useId } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '@/lib/utils';\nimport '@/glass-theme.css';\n\n// ========================================\n// VARIANTS\n// ========================================\n\nconst circularProgressVariants = cva('relative inline-flex items-center justify-center p-4', {\n variants: {\n size: {\n sm: 'w-20 h-20',\n md: 'w-28 h-28',\n lg: 'w-36 h-36',\n xl: 'w-44 h-44',\n },\n },\n defaultVariants: {\n size: 'md',\n },\n});\n\n// ========================================\n// TYPES\n// ========================================\n\nexport type CircularProgressGradient = 'violet' | 'blue' | 'cyan' | 'amber' | 'emerald' | 'rose';\n\nexport interface CircularProgressGlassProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'children'>,\n VariantProps<typeof circularProgressVariants> {\n /** Progress value (0-100) for determinate variant */\n readonly value?: number;\n /** Variant type */\n readonly variant?: 'determinate' | 'indeterminate';\n /** Stroke width in pixels */\n readonly thickness?: number;\n /** Background track width in pixels */\n readonly trackWidth?: number;\n /** Progress color gradient */\n readonly color?: CircularProgressGradient;\n /** Track color (background circle) */\n readonly trackColor?: string;\n /** Show percentage label in center */\n readonly showLabel?: boolean;\n /** Custom label text (overrides percentage) */\n readonly label?: string;\n /** Custom color for the center label text */\n readonly labelColor?: string;\n /** Show glow effect */\n readonly showGlow?: boolean;\n /** Glow intensity */\n readonly glowIntensity?: 'low' | 'medium' | 'high';\n /** Stroke linecap style */\n readonly strokeLinecap?: 'round' | 'butt' | 'square';\n /** Animation duration in seconds */\n readonly animationDuration?: number;\n}\n\n// ========================================\n// HELPERS\n// ========================================\n\nconst getGradientColors = (gradient: CircularProgressGradient) => {\n const gradients: Record<CircularProgressGradient, { from: string; to: string; glowVar: string }> =\n {\n violet: { from: '#8b5cf6', to: '#a855f7', glowVar: '--progress-glow-violet' },\n blue: { from: '#3b82f6', to: '#60a5fa', glowVar: '--progress-glow-blue' },\n cyan: { from: '#06b6d4', to: '#22d3ee', glowVar: '--progress-glow-cyan' },\n amber: { from: '#f59e0b', to: '#fbbf24', glowVar: '--progress-glow-amber' },\n emerald: { from: '#10b981', to: '#34d399', glowVar: '--progress-glow-emerald' },\n rose: { from: '#f43f5e', to: '#fb7185', glowVar: '--progress-glow-rose' },\n };\n return gradients[gradient];\n};\n\nconst getGlowStdDeviation = (intensity: 'low' | 'medium' | 'high'): number => {\n const intensities = { low: 2, medium: 4, high: 6 };\n return intensities[intensity];\n};\n\n// ========================================\n// COMPONENT\n// ========================================\n\nexport const CircularProgressGlass = forwardRef<HTMLDivElement, CircularProgressGlassProps>(\n (\n {\n className,\n size = 'md',\n value = 0,\n variant = 'determinate',\n thickness = 8,\n trackWidth = 8,\n color = 'violet',\n trackColor = 'oklch(100% 0 0 / 0.1)',\n showLabel = true,\n label,\n labelColor,\n showGlow = true,\n glowIntensity = 'medium',\n strokeLinecap = 'round',\n animationDuration = 1,\n ...props\n },\n ref\n ) => {\n const clampedValue = Math.min(100, Math.max(0, value));\n const gradientColors = getGradientColors(color);\n\n // SVG dimensions\n const sizeMap = { sm: 64, md: 96, lg: 128, xl: 160 };\n const svgSize = sizeMap[size || 'md'];\n const radius = (svgSize - Math.max(thickness, trackWidth)) / 2;\n const circumference = 2 * Math.PI * radius;\n const center = svgSize / 2;\n\n // Calculate stroke dash offset for determinate progress\n const dashOffset = useMemo(() => {\n if (variant === 'indeterminate') return circumference * 0.75;\n return circumference * ((100 - clampedValue) / 100);\n }, [variant, clampedValue, circumference]);\n\n // Generate unique IDs for SVG elements (using useId for stable IDs)\n const uniqueId = useId();\n const gradientId = `circular-gradient-${uniqueId}`;\n const glowId = `circular-glow-${uniqueId}`;\n\n return (\n <div
|
|
18
|
+
"content": "/**\n * CircularProgressGlass Component\n *\n * SVG-based circular progress indicator with:\n * - Determinate and indeterminate variants\n * - Configurable size and thickness\n * - Glow effect with SVG filters\n * - Theme-aware styling\n * - Optional label in center\n * - Gradient colors support\n */\n\nimport { forwardRef, useMemo, useId } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '@/lib/utils';\nimport '@/glass-theme.css';\n\n// ========================================\n// VARIANTS\n// ========================================\n\nconst circularProgressVariants = cva('relative inline-flex items-center justify-center p-4', {\n variants: {\n size: {\n sm: 'w-20 h-20',\n md: 'w-28 h-28',\n lg: 'w-36 h-36',\n xl: 'w-44 h-44',\n },\n },\n defaultVariants: {\n size: 'md',\n },\n});\n\n// ========================================\n// TYPES\n// ========================================\n\nexport type CircularProgressGradient = 'violet' | 'blue' | 'cyan' | 'amber' | 'emerald' | 'rose';\n\nexport interface CircularProgressGlassProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'children'>,\n VariantProps<typeof circularProgressVariants> {\n /** Progress value (0-100) for determinate variant */\n readonly value?: number;\n /** Variant type */\n readonly variant?: 'determinate' | 'indeterminate';\n /** Stroke width in pixels */\n readonly thickness?: number;\n /** Background track width in pixels */\n readonly trackWidth?: number;\n /** Progress color gradient */\n readonly color?: CircularProgressGradient;\n /** Track color (background circle) */\n readonly trackColor?: string;\n /** Show percentage label in center */\n readonly showLabel?: boolean;\n /** Custom label text (overrides percentage) */\n readonly label?: string;\n /** Custom color for the center label text */\n readonly labelColor?: string;\n /** Show glow effect */\n readonly showGlow?: boolean;\n /** Glow intensity */\n readonly glowIntensity?: 'low' | 'medium' | 'high';\n /** Stroke linecap style */\n readonly strokeLinecap?: 'round' | 'butt' | 'square';\n /** Animation duration in seconds */\n readonly animationDuration?: number;\n}\n\n// ========================================\n// HELPERS\n// ========================================\n\nconst getGradientColors = (gradient: CircularProgressGradient) => {\n const gradients: Record<CircularProgressGradient, { from: string; to: string; glowVar: string }> =\n {\n violet: { from: '#8b5cf6', to: '#a855f7', glowVar: '--progress-glow-violet' },\n blue: { from: '#3b82f6', to: '#60a5fa', glowVar: '--progress-glow-blue' },\n cyan: { from: '#06b6d4', to: '#22d3ee', glowVar: '--progress-glow-cyan' },\n amber: { from: '#f59e0b', to: '#fbbf24', glowVar: '--progress-glow-amber' },\n emerald: { from: '#10b981', to: '#34d399', glowVar: '--progress-glow-emerald' },\n rose: { from: '#f43f5e', to: '#fb7185', glowVar: '--progress-glow-rose' },\n };\n return gradients[gradient];\n};\n\nconst getGlowStdDeviation = (intensity: 'low' | 'medium' | 'high'): number => {\n const intensities = { low: 2, medium: 4, high: 6 };\n return intensities[intensity];\n};\n\n// ========================================\n// COMPONENT\n// ========================================\n\nexport const CircularProgressGlass = forwardRef<HTMLDivElement, CircularProgressGlassProps>(\n (\n {\n className,\n size = 'md',\n value = 0,\n variant = 'determinate',\n thickness = 8,\n trackWidth = 8,\n color = 'violet',\n trackColor = 'oklch(100% 0 0 / 0.1)',\n showLabel = true,\n label,\n labelColor,\n showGlow = true,\n glowIntensity = 'medium',\n strokeLinecap = 'round',\n animationDuration = 1,\n ...props\n },\n ref\n ) => {\n const clampedValue = Math.min(100, Math.max(0, value));\n const gradientColors = getGradientColors(color);\n\n // SVG dimensions\n const sizeMap = { sm: 64, md: 96, lg: 128, xl: 160 };\n const svgSize = sizeMap[size || 'md'];\n const radius = (svgSize - Math.max(thickness, trackWidth)) / 2;\n const circumference = 2 * Math.PI * radius;\n const center = svgSize / 2;\n\n // Calculate stroke dash offset for determinate progress\n const dashOffset = useMemo(() => {\n if (variant === 'indeterminate') return circumference * 0.75;\n return circumference * ((100 - clampedValue) / 100);\n }, [variant, clampedValue, circumference]);\n\n // Generate unique IDs for SVG elements (using useId for stable IDs)\n const uniqueId = useId();\n const gradientId = `circular-gradient-${uniqueId}`;\n const glowId = `circular-glow-${uniqueId}`;\n\n return (\n <div\n ref={ref}\n data-slot=\"circular-progress\"\n className={cn(circularProgressVariants({ size }), className)}\n {...props}\n >\n <svg\n width={svgSize}\n height={svgSize}\n className=\"transform -rotate-90 overflow-visible\"\n aria-hidden=\"true\"\n style={{ overflow: 'visible' }}\n >\n <defs>\n {/* Gradient definition */}\n <linearGradient id={gradientId} x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n <stop offset=\"0%\" stopColor={gradientColors.from} />\n <stop offset=\"100%\" stopColor={gradientColors.to} />\n </linearGradient>\n\n {/* Glow filter */}\n {showGlow && (\n <filter id={glowId}>\n <feGaussianBlur\n stdDeviation={getGlowStdDeviation(glowIntensity)}\n result=\"coloredBlur\"\n />\n <feMerge>\n <feMergeNode in=\"coloredBlur\" />\n <feMergeNode in=\"SourceGraphic\" />\n </feMerge>\n </filter>\n )}\n </defs>\n\n {/* Background track */}\n <circle\n cx={center}\n cy={center}\n r={radius}\n fill=\"none\"\n stroke={trackColor}\n strokeWidth={trackWidth}\n />\n\n {/* Progress circle */}\n <circle\n cx={center}\n cy={center}\n r={radius}\n fill=\"none\"\n stroke={`url(#${gradientId})`}\n strokeWidth={thickness}\n strokeLinecap={strokeLinecap}\n strokeDasharray={circumference}\n strokeDashoffset={dashOffset}\n filter={showGlow ? `url(#${glowId})` : undefined}\n className={cn(\n 'transition-all',\n variant === 'indeterminate' && 'animate-circular-progress-spin'\n )}\n style={{\n transitionDuration: `${animationDuration}s`,\n transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)',\n }}\n />\n </svg>\n\n {/* Center label */}\n {showLabel && (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <span\n className=\"text-sm font-semibold tabular-nums\"\n style={{ color: labelColor || gradientColors.to }}\n >\n {label || (variant === 'determinate' ? `${clampedValue}%` : '')}\n </span>\n </div>\n )}\n\n {/* Accessibility */}\n <div\n role=\"progressbar\"\n aria-valuenow={variant === 'determinate' ? clampedValue : undefined}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-label={\n label || (variant === 'determinate' ? `Progress: ${clampedValue}%` : 'Loading progress')\n }\n aria-valuetext={label || (variant === 'determinate' ? `${clampedValue}%` : 'Loading...')}\n className=\"sr-only\"\n >\n {label || (variant === 'determinate' ? `${clampedValue}%` : 'Loading...')}\n </div>\n </div>\n );\n }\n);\n\nCircularProgressGlass.displayName = 'CircularProgressGlass';\n"
|
|
19
19
|
}
|
|
20
20
|
],
|
|
21
21
|
"categories": [
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
{
|
|
17
17
|
"path": "components/glass/ui/dropdown-glass.tsx",
|
|
18
18
|
"type": "registry:component",
|
|
19
|
-
"content": "/**\n * DropdownGlass Component\n *\n * Glass-themed dropdown menu with two APIs:\n *\n * 1. **Simple API** (items prop) - Quick setup for basic menus\n * 2. **Compound API** (DropdownMenuGlass.*) - Full shadcn/ui pattern for complex menus\n *\n * @example Simple API (recommended for basic dropdowns)\n * ```tsx\n * import { DropdownGlass } from '@/components/glass/ui/dropdown-glass';\n *\n * <DropdownGlass\n * trigger={<button><MoreVertical /></button>}\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 Compound API (for complex dropdowns)\n * ```tsx\n * import {\n * DropdownMenuGlass,\n * DropdownMenuGlassTrigger,\n * DropdownMenuGlassContent,\n * DropdownMenuGlassItem,\n * DropdownMenuGlassSeparator,\n * } from '@/components/glass/ui/dropdown-menu-glass';\n *\n * <DropdownMenuGlass>\n * <DropdownMenuGlassTrigger asChild>\n * <Button>Open Menu</Button>\n * </DropdownMenuGlassTrigger>\n * <DropdownMenuGlassContent>\n * <DropdownMenuGlassItem>Edit</DropdownMenuGlassItem>\n * <DropdownMenuGlassSeparator />\n * <DropdownMenuGlassItem variant=\"destructive\">Delete</DropdownMenuGlassItem>\n * </DropdownMenuGlassContent>\n * </DropdownMenuGlass>\n * ```\n *\n * @see ./dropdown-menu-glass.tsx for compound component exports\n */\n\n'use client';\n\nimport * as React from 'react';\nimport type { LucideIcon } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport { ICON_SIZES } from '@/components/glass/primitives';\nimport {\n DropdownMenuGlass,\n DropdownMenuGlassTrigger,\n DropdownMenuGlassContent,\n DropdownMenuGlassItem,\n DropdownMenuGlassSeparator,\n} from './dropdown-menu-glass';\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\n/**\n * Props for the DropdownGlass component (Simple API)\n *\n * @accessibility\n * - **Keyboard Navigation:** Arrow keys navigate, Enter/Space activates, Escape closes\n * - **Focus Management:** Focus trapped within menu when open\n * - **Screen Readers:** Uses role=\"menu\" and role=\"menuitem\"\n * - **Touch Targets:** All items meet minimum 44x44px\n */\nexport interface DropdownGlassProps {\n /** Trigger element (button, etc.) */\n readonly trigger: React.ReactNode;\n /** Menu items array */\n readonly items: readonly DropdownItem[];\n /** Dropdown alignment */\n readonly align?: 'left' | 'right';\n /** Additional className */\n readonly className?: string;\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\n/**\n * DropdownGlass - Simple API wrapper\n *\n * For complex dropdowns with checkboxes, radio groups, sub-menus, etc.,\n * use the compound components from dropdown-menu-glass.tsx directly.\n */\nexport const DropdownGlass = React.forwardRef<HTMLDivElement, DropdownGlassProps>(\n ({ trigger, items, align = 'left', className }, ref) => {\n return (\n <div ref={ref} className={cn('relative inline-block', className)}>\n <DropdownMenuGlass>\n <DropdownMenuGlassTrigger asChild>{trigger}</DropdownMenuGlassTrigger>\n\n <DropdownMenuGlassContent align={align === 'left' ? 'start' : 'end'}>\n {items.map((item, idx) =>\n item.divider ? (\n <DropdownMenuGlassSeparator key={`divider-${idx}`} />\n ) : (\n <DropdownMenuGlassItem\n key={`item-${idx}`}\n variant={item.danger ? 'destructive' : 'default'}\n onSelect={item.onClick}\n >\n {item.icon && (\n <item.icon\n className={cn(\n ICON_SIZES.md,\n 'shrink-0',\n item.danger\n ? 'text-(--alert-danger-text)'\n : 'text-(--dropdown-icon) group-data-highlighted:text-(--dropdown-icon-hover)'\n )}\n />\n )}\n <span className=\"font-medium\">{item.label}</span>\n </DropdownMenuGlassItem>\n )\n )}\n </DropdownMenuGlassContent>\n </DropdownMenuGlass>\n </div>\n );\n }\n);\n\nDropdownGlass.displayName = 'DropdownGlass';\n\n// ========================================\n// RE-EXPORTS (for convenience)\n// ========================================\n\nexport {\n DropdownMenuGlass,\n DropdownMenuGlassTrigger,\n DropdownMenuGlassContent,\n DropdownMenuGlassItem,\n DropdownMenuGlassSeparator,\n} from './dropdown-menu-glass';\n"
|
|
19
|
+
"content": "/**\n * DropdownGlass Component\n *\n * Glass-themed dropdown menu with two APIs:\n *\n * 1. **Simple API** (items prop) - Quick setup for basic menus\n * 2. **Compound API** (DropdownMenuGlass.*) - Full shadcn/ui pattern for complex menus\n *\n * @example Simple API (recommended for basic dropdowns)\n * ```tsx\n * import { DropdownGlass } from '@/components/glass/ui/dropdown-glass';\n *\n * <DropdownGlass\n * trigger={<button><MoreVertical /></button>}\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 Compound API (for complex dropdowns)\n * ```tsx\n * import {\n * DropdownMenuGlass,\n * DropdownMenuGlassTrigger,\n * DropdownMenuGlassContent,\n * DropdownMenuGlassItem,\n * DropdownMenuGlassSeparator,\n * } from '@/components/glass/ui/dropdown-menu-glass';\n *\n * <DropdownMenuGlass>\n * <DropdownMenuGlassTrigger asChild>\n * <Button>Open Menu</Button>\n * </DropdownMenuGlassTrigger>\n * <DropdownMenuGlassContent>\n * <DropdownMenuGlassItem>Edit</DropdownMenuGlassItem>\n * <DropdownMenuGlassSeparator />\n * <DropdownMenuGlassItem variant=\"destructive\">Delete</DropdownMenuGlassItem>\n * </DropdownMenuGlassContent>\n * </DropdownMenuGlass>\n * ```\n *\n * @see ./dropdown-menu-glass.tsx for compound component exports\n */\n\n'use client';\n\nimport * as React from 'react';\nimport type { LucideIcon } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport { ICON_SIZES } from '@/components/glass/primitives';\nimport {\n DropdownMenuGlass,\n DropdownMenuGlassTrigger,\n DropdownMenuGlassContent,\n DropdownMenuGlassItem,\n DropdownMenuGlassSeparator,\n} from './dropdown-menu-glass';\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\n/**\n * Props for the DropdownGlass component (Simple API)\n *\n * @accessibility\n * - **Keyboard Navigation:** Arrow keys navigate, Enter/Space activates, Escape closes\n * - **Focus Management:** Focus trapped within menu when open\n * - **Screen Readers:** Uses role=\"menu\" and role=\"menuitem\"\n * - **Touch Targets:** All items meet minimum 44x44px\n */\nexport interface DropdownGlassProps {\n /** Trigger element (button, etc.) */\n readonly trigger: React.ReactNode;\n /** Menu items array */\n readonly items: readonly DropdownItem[];\n /** Dropdown alignment */\n readonly align?: 'left' | 'right';\n /** Additional className */\n readonly className?: string;\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\n/**\n * DropdownGlass - Simple API wrapper\n *\n * For complex dropdowns with checkboxes, radio groups, sub-menus, etc.,\n * use the compound components from dropdown-menu-glass.tsx directly.\n */\nexport const DropdownGlass = React.forwardRef<HTMLDivElement, DropdownGlassProps>(\n ({ trigger, items, align = 'left', className }, ref) => {\n return (\n <div ref={ref} data-slot=\"dropdown\" className={cn('relative inline-block', className)}>\n <DropdownMenuGlass>\n <DropdownMenuGlassTrigger asChild>{trigger}</DropdownMenuGlassTrigger>\n\n <DropdownMenuGlassContent align={align === 'left' ? 'start' : 'end'}>\n {items.map((item, idx) =>\n item.divider ? (\n <DropdownMenuGlassSeparator key={`divider-${idx}`} />\n ) : (\n <DropdownMenuGlassItem\n key={`item-${idx}`}\n variant={item.danger ? 'destructive' : 'default'}\n onSelect={item.onClick}\n >\n {item.icon && (\n <item.icon\n className={cn(\n ICON_SIZES.md,\n 'shrink-0',\n item.danger\n ? 'text-(--alert-danger-text)'\n : 'text-(--dropdown-icon) group-data-highlighted:text-(--dropdown-icon-hover)'\n )}\n />\n )}\n <span className=\"font-medium\">{item.label}</span>\n </DropdownMenuGlassItem>\n )\n )}\n </DropdownMenuGlassContent>\n </DropdownMenuGlass>\n </div>\n );\n }\n);\n\nDropdownGlass.displayName = 'DropdownGlass';\n\n// ========================================\n// RE-EXPORTS (for convenience)\n// ========================================\n\nexport {\n DropdownMenuGlass,\n DropdownMenuGlassTrigger,\n DropdownMenuGlassContent,\n DropdownMenuGlassItem,\n DropdownMenuGlassSeparator,\n} from './dropdown-menu-glass';\n"
|
|
20
20
|
}
|
|
21
21
|
],
|
|
22
22
|
"categories": [
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
{
|
|
18
18
|
"path": "components/glass/ui/dropdown-menu-glass.tsx",
|
|
19
19
|
"type": "registry:component",
|
|
20
|
-
"content": "/**\n * DropdownMenuGlass - Compound Component\n *\n * Glass-themed dropdown menu following shadcn/ui compound component pattern.\n * Built on Radix UI primitives with unified glass styling.\n *\n * @example Basic usage\n * ```tsx\n * <DropdownMenuGlass>\n * <DropdownMenuGlassTrigger asChild>\n * <Button>Open Menu</Button>\n * </DropdownMenuGlassTrigger>\n * <DropdownMenuGlassContent>\n * <DropdownMenuGlassItem onSelect={() => console.log('Edit')}>\n * <Edit className=\"mr-2 h-4 w-4\" />\n * Edit\n * </DropdownMenuGlassItem>\n * <DropdownMenuGlassSeparator />\n * <DropdownMenuGlassItem variant=\"destructive\">\n * <Trash className=\"mr-2 h-4 w-4\" />\n * Delete\n * </DropdownMenuGlassItem>\n * </DropdownMenuGlassContent>\n * </DropdownMenuGlass>\n * ```\n *\n * @example With labels and groups\n * ```tsx\n * <DropdownMenuGlass>\n * <DropdownMenuGlassTrigger asChild>\n * <Button variant=\"outline\">Options</Button>\n * </DropdownMenuGlassTrigger>\n * <DropdownMenuGlassContent>\n * <DropdownMenuGlassLabel>Actions</DropdownMenuGlassLabel>\n * <DropdownMenuGlassGroup>\n * <DropdownMenuGlassItem>Copy</DropdownMenuGlassItem>\n * <DropdownMenuGlassItem>Paste</DropdownMenuGlassItem>\n * </DropdownMenuGlassGroup>\n * <DropdownMenuGlassSeparator />\n * <DropdownMenuGlassLabel>Danger Zone</DropdownMenuGlassLabel>\n * <DropdownMenuGlassItem variant=\"destructive\">Delete</DropdownMenuGlassItem>\n * </DropdownMenuGlassContent>\n * </DropdownMenuGlass>\n * ```\n *\n * @see https://www.radix-ui.com/primitives/docs/components/dropdown-menu\n */\n\n'use client';\n\nimport * as React from 'react';\nimport * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';\nimport { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport {\n getDropdownContentStyles,\n dropdownContentClasses,\n getDropdownItemClasses,\n dropdownSeparatorClasses,\n dropdownLabelClasses,\n} from '@/lib/variants/dropdown-content-styles';\nimport '@/glass-theme.css';\n\n// ========================================\n// ROOT\n// ========================================\n\nconst DropdownMenuGlass = DropdownMenuPrimitive.Root;\n\n// ========================================\n// TRIGGER\n// ========================================\n\nconst DropdownMenuGlassTrigger = DropdownMenuPrimitive.Trigger;\n\n// ========================================\n// GROUP\n// ========================================\n\nconst DropdownMenuGlassGroup = DropdownMenuPrimitive.Group;\n\n// ========================================\n// PORTAL\n// ========================================\n\nconst DropdownMenuGlassPortal = DropdownMenuPrimitive.Portal;\n\n// ========================================\n// SUB\n// ========================================\n\nconst DropdownMenuGlassSub = DropdownMenuPrimitive.Sub;\n\n// ========================================\n// RADIO GROUP\n// ========================================\n\nconst DropdownMenuGlassRadioGroup = DropdownMenuPrimitive.RadioGroup;\n\n// ========================================\n// SUB TRIGGER\n// ========================================\n\nconst DropdownMenuGlassSubTrigger = React.forwardRef<\n React.ComponentRef<typeof DropdownMenuPrimitive.SubTrigger>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {\n inset?: boolean;\n }\n>(({ className, inset, children, ...props }, ref) => (\n <DropdownMenuPrimitive.SubTrigger\n ref={ref}\n className={cn(\n getDropdownItemClasses(),\n 'data-[state=open]:bg-[var(--dropdown-item-hover)]',\n inset && 'pl-8',\n className\n )}\n {...props}\n >\n {children}\n <ChevronRightIcon className=\"ml-auto h-4 w-4\" />\n </DropdownMenuPrimitive.SubTrigger>\n));\nDropdownMenuGlassSubTrigger.displayName = 'DropdownMenuGlassSubTrigger';\n\n// ========================================\n// SUB CONTENT\n// ========================================\n\nconst DropdownMenuGlassSubContent = React.forwardRef<\n React.ComponentRef<typeof DropdownMenuPrimitive.SubContent>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.SubContent\n ref={ref}\n className={cn(dropdownContentClasses, 'p-1.5', className)}\n style={getDropdownContentStyles()}\n {...props}\n />\n));\nDropdownMenuGlassSubContent.displayName = 'DropdownMenuGlassSubContent';\n\n// ========================================\n// CONTENT\n// ========================================\n\nconst DropdownMenuGlassContent = React.forwardRef<\n React.ComponentRef<typeof DropdownMenuPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>\n>(({ className, sideOffset = 8, ...props }, ref) => (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n className={cn(dropdownContentClasses, 'p-1.5', className)}\n style={getDropdownContentStyles()}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n));\nDropdownMenuGlassContent.displayName = 'DropdownMenuGlassContent';\n\n// ========================================\n// ITEM\n// ========================================\n\nexport interface DropdownMenuGlassItemProps extends React.ComponentPropsWithoutRef<\n typeof DropdownMenuPrimitive.Item\n> {\n inset?: boolean;\n variant?: 'default' | 'destructive';\n}\n\nconst DropdownMenuGlassItem = React.forwardRef<\n React.ComponentRef<typeof DropdownMenuPrimitive.Item>,\n DropdownMenuGlassItemProps\n>(({ className, inset, variant = 'default', ...props }, ref) => (\n <DropdownMenuPrimitive.Item\n ref={ref}\n className={cn(\n getDropdownItemClasses({ danger: variant === 'destructive' }),\n inset && 'pl-8',\n className\n )}\n {...props}\n />\n));\nDropdownMenuGlassItem.displayName = 'DropdownMenuGlassItem';\n\n// ========================================\n// CHECKBOX ITEM\n// ========================================\n\nconst DropdownMenuGlassCheckboxItem = React.forwardRef<\n React.ComponentRef<typeof DropdownMenuPrimitive.CheckboxItem>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>\n>(({ className, children, checked, ...props }, ref) => (\n <DropdownMenuPrimitive.CheckboxItem\n ref={ref}\n className={cn(getDropdownItemClasses(), 'pl-8 pr-2', className)}\n checked={checked}\n {...props}\n >\n <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <CheckIcon className=\"h-4 w-4\" />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.CheckboxItem>\n));\nDropdownMenuGlassCheckboxItem.displayName = 'DropdownMenuGlassCheckboxItem';\n\n// ========================================\n// RADIO ITEM\n// ========================================\n\nconst DropdownMenuGlassRadioItem = React.forwardRef<\n React.ComponentRef<typeof DropdownMenuPrimitive.RadioItem>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>\n>(({ className, children, ...props }, ref) => (\n <DropdownMenuPrimitive.RadioItem\n ref={ref}\n className={cn(getDropdownItemClasses(), 'pl-8 pr-2', className)}\n {...props}\n >\n <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <CircleIcon className=\"h-2 w-2 fill-current\" />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.RadioItem>\n));\nDropdownMenuGlassRadioItem.displayName = 'DropdownMenuGlassRadioItem';\n\n// ========================================\n// LABEL\n// ========================================\n\nconst DropdownMenuGlassLabel = React.forwardRef<\n React.ComponentRef<typeof DropdownMenuPrimitive.Label>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {\n inset?: boolean;\n }\n>(({ className, inset, ...props }, ref) => (\n <DropdownMenuPrimitive.Label\n ref={ref}\n className={cn(dropdownLabelClasses, inset && 'pl-8', className)}\n {...props}\n />\n));\nDropdownMenuGlassLabel.displayName = 'DropdownMenuGlassLabel';\n\n// ========================================\n// SEPARATOR\n// ========================================\n\nconst DropdownMenuGlassSeparator = React.forwardRef<\n React.ComponentRef<typeof DropdownMenuPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Separator\n ref={ref}\n className={cn(dropdownSeparatorClasses, '-mx-1 my-1', className)}\n {...props}\n />\n));\nDropdownMenuGlassSeparator.displayName = 'DropdownMenuGlassSeparator';\n\n// ========================================\n// SHORTCUT\n// ========================================\n\nconst DropdownMenuGlassShortcut = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLSpanElement>) => {\n return (\n <span\n className={cn('ml-auto text-xs tracking-widest text-(--text-muted)', className)}\n {...props}\n />\n );\n};\nDropdownMenuGlassShortcut.displayName = 'DropdownMenuGlassShortcut';\n\n// ========================================\n// EXPORTS\n// ========================================\n\nexport {\n DropdownMenuGlass,\n DropdownMenuGlassTrigger,\n DropdownMenuGlassContent,\n DropdownMenuGlassItem,\n DropdownMenuGlassCheckboxItem,\n DropdownMenuGlassRadioItem,\n DropdownMenuGlassLabel,\n DropdownMenuGlassSeparator,\n DropdownMenuGlassShortcut,\n DropdownMenuGlassGroup,\n DropdownMenuGlassPortal,\n DropdownMenuGlassSub,\n DropdownMenuGlassSubContent,\n DropdownMenuGlassSubTrigger,\n DropdownMenuGlassRadioGroup,\n};\n"
|
|
20
|
+
"content": "/**\n * DropdownMenuGlass - Compound Component\n *\n * Glass-themed dropdown menu following shadcn/ui compound component pattern.\n * Built on Radix UI primitives with unified glass styling.\n *\n * @example Basic usage\n * ```tsx\n * <DropdownMenuGlass>\n * <DropdownMenuGlassTrigger asChild>\n * <Button>Open Menu</Button>\n * </DropdownMenuGlassTrigger>\n * <DropdownMenuGlassContent>\n * <DropdownMenuGlassItem onSelect={() => console.log('Edit')}>\n * <Edit className=\"mr-2 h-4 w-4\" />\n * Edit\n * </DropdownMenuGlassItem>\n * <DropdownMenuGlassSeparator />\n * <DropdownMenuGlassItem variant=\"destructive\">\n * <Trash className=\"mr-2 h-4 w-4\" />\n * Delete\n * </DropdownMenuGlassItem>\n * </DropdownMenuGlassContent>\n * </DropdownMenuGlass>\n * ```\n *\n * @example With labels and groups\n * ```tsx\n * <DropdownMenuGlass>\n * <DropdownMenuGlassTrigger asChild>\n * <Button variant=\"outline\">Options</Button>\n * </DropdownMenuGlassTrigger>\n * <DropdownMenuGlassContent>\n * <DropdownMenuGlassLabel>Actions</DropdownMenuGlassLabel>\n * <DropdownMenuGlassGroup>\n * <DropdownMenuGlassItem>Copy</DropdownMenuGlassItem>\n * <DropdownMenuGlassItem>Paste</DropdownMenuGlassItem>\n * </DropdownMenuGlassGroup>\n * <DropdownMenuGlassSeparator />\n * <DropdownMenuGlassLabel>Danger Zone</DropdownMenuGlassLabel>\n * <DropdownMenuGlassItem variant=\"destructive\">Delete</DropdownMenuGlassItem>\n * </DropdownMenuGlassContent>\n * </DropdownMenuGlass>\n * ```\n *\n * @see https://www.radix-ui.com/primitives/docs/components/dropdown-menu\n */\n\n'use client';\n\nimport * as React from 'react';\nimport * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';\nimport { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport {\n getDropdownContentStyles,\n dropdownContentClasses,\n getDropdownItemClasses,\n dropdownSeparatorClasses,\n dropdownLabelClasses,\n} from '@/lib/variants/dropdown-content-styles';\nimport '@/glass-theme.css';\n\n// ========================================\n// ROOT\n// ========================================\n\n// Note: DropdownMenuPrimitive.Root is a context provider and doesn't render DOM,\n// so data-slot is applied to Content instead. This matches shadcn/ui pattern.\nconst DropdownMenuGlassRoot = (\n props: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Root>\n) => <DropdownMenuPrimitive.Root {...props} />;\nDropdownMenuGlassRoot.displayName = 'DropdownMenuGlass';\n\nconst DropdownMenuGlass = DropdownMenuGlassRoot;\n\n// ========================================\n// TRIGGER\n// ========================================\n\nconst DropdownMenuGlassTrigger = React.forwardRef<\n React.ComponentRef<typeof DropdownMenuPrimitive.Trigger>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Trigger>\n>((props, ref) => (\n <DropdownMenuPrimitive.Trigger data-slot=\"dropdown-menu-trigger\" ref={ref} {...props} />\n));\nDropdownMenuGlassTrigger.displayName = 'DropdownMenuGlassTrigger';\n\n// ========================================\n// GROUP\n// ========================================\n\nconst DropdownMenuGlassGroup = React.forwardRef<\n React.ComponentRef<typeof DropdownMenuPrimitive.Group>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Group>\n>((props, ref) => (\n <DropdownMenuPrimitive.Group data-slot=\"dropdown-menu-group\" ref={ref} {...props} />\n));\nDropdownMenuGlassGroup.displayName = 'DropdownMenuGlassGroup';\n\n// ========================================\n// PORTAL\n// ========================================\n\nconst DropdownMenuGlassPortal = DropdownMenuPrimitive.Portal;\n\n// ========================================\n// SUB\n// ========================================\n\n// Note: DropdownMenuPrimitive.Sub is a context provider and doesn't render DOM,\n// so data-slot is applied to SubContent instead. This matches shadcn/ui pattern.\nconst DropdownMenuGlassSub = (\n props: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Sub>\n) => <DropdownMenuPrimitive.Sub {...props} />;\nDropdownMenuGlassSub.displayName = 'DropdownMenuGlassSub';\n\n// ========================================\n// RADIO GROUP\n// ========================================\n\nconst DropdownMenuGlassRadioGroup = React.forwardRef<\n React.ComponentRef<typeof DropdownMenuPrimitive.RadioGroup>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioGroup>\n>((props, ref) => (\n <DropdownMenuPrimitive.RadioGroup data-slot=\"dropdown-menu-radio-group\" ref={ref} {...props} />\n));\nDropdownMenuGlassRadioGroup.displayName = 'DropdownMenuGlassRadioGroup';\n\n// ========================================\n// SUB TRIGGER\n// ========================================\n\nconst DropdownMenuGlassSubTrigger = React.forwardRef<\n React.ComponentRef<typeof DropdownMenuPrimitive.SubTrigger>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {\n inset?: boolean;\n }\n>(({ className, inset, children, ...props }, ref) => (\n <DropdownMenuPrimitive.SubTrigger\n ref={ref}\n data-slot=\"dropdown-menu-sub-trigger\"\n className={cn(\n getDropdownItemClasses(),\n 'data-[state=open]:bg-[var(--dropdown-item-hover)]',\n inset && 'pl-8',\n className\n )}\n {...props}\n >\n {children}\n <ChevronRightIcon className=\"ml-auto h-4 w-4\" />\n </DropdownMenuPrimitive.SubTrigger>\n));\nDropdownMenuGlassSubTrigger.displayName = 'DropdownMenuGlassSubTrigger';\n\n// ========================================\n// SUB CONTENT\n// ========================================\n\nconst DropdownMenuGlassSubContent = React.forwardRef<\n React.ComponentRef<typeof DropdownMenuPrimitive.SubContent>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.SubContent\n ref={ref}\n data-slot=\"dropdown-menu-sub-content\"\n className={cn(dropdownContentClasses, 'p-1.5', className)}\n style={getDropdownContentStyles()}\n {...props}\n />\n));\nDropdownMenuGlassSubContent.displayName = 'DropdownMenuGlassSubContent';\n\n// ========================================\n// CONTENT\n// ========================================\n\nconst DropdownMenuGlassContent = React.forwardRef<\n React.ComponentRef<typeof DropdownMenuPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>\n>(({ className, sideOffset = 8, ...props }, ref) => (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n ref={ref}\n data-slot=\"dropdown-menu-content\"\n sideOffset={sideOffset}\n className={cn(dropdownContentClasses, 'p-1.5', className)}\n style={getDropdownContentStyles()}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n));\nDropdownMenuGlassContent.displayName = 'DropdownMenuGlassContent';\n\n// ========================================\n// ITEM\n// ========================================\n\nexport interface DropdownMenuGlassItemProps extends React.ComponentPropsWithoutRef<\n typeof DropdownMenuPrimitive.Item\n> {\n inset?: boolean;\n variant?: 'default' | 'destructive';\n}\n\nconst DropdownMenuGlassItem = React.forwardRef<\n React.ComponentRef<typeof DropdownMenuPrimitive.Item>,\n DropdownMenuGlassItemProps\n>(({ className, inset, variant = 'default', ...props }, ref) => (\n <DropdownMenuPrimitive.Item\n ref={ref}\n data-slot=\"dropdown-menu-item\"\n className={cn(\n getDropdownItemClasses({ danger: variant === 'destructive' }),\n inset && 'pl-8',\n className\n )}\n {...props}\n />\n));\nDropdownMenuGlassItem.displayName = 'DropdownMenuGlassItem';\n\n// ========================================\n// CHECKBOX ITEM\n// ========================================\n\nconst DropdownMenuGlassCheckboxItem = React.forwardRef<\n React.ComponentRef<typeof DropdownMenuPrimitive.CheckboxItem>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>\n>(({ className, children, checked, ...props }, ref) => (\n <DropdownMenuPrimitive.CheckboxItem\n ref={ref}\n data-slot=\"dropdown-menu-checkbox-item\"\n className={cn(getDropdownItemClasses(), 'pl-8 pr-2', className)}\n checked={checked}\n {...props}\n >\n <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <CheckIcon className=\"h-4 w-4\" />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.CheckboxItem>\n));\nDropdownMenuGlassCheckboxItem.displayName = 'DropdownMenuGlassCheckboxItem';\n\n// ========================================\n// RADIO ITEM\n// ========================================\n\nconst DropdownMenuGlassRadioItem = React.forwardRef<\n React.ComponentRef<typeof DropdownMenuPrimitive.RadioItem>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>\n>(({ className, children, ...props }, ref) => (\n <DropdownMenuPrimitive.RadioItem\n ref={ref}\n data-slot=\"dropdown-menu-radio-item\"\n className={cn(getDropdownItemClasses(), 'pl-8 pr-2', className)}\n {...props}\n >\n <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <CircleIcon className=\"h-2 w-2 fill-current\" />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.RadioItem>\n));\nDropdownMenuGlassRadioItem.displayName = 'DropdownMenuGlassRadioItem';\n\n// ========================================\n// LABEL\n// ========================================\n\nconst DropdownMenuGlassLabel = React.forwardRef<\n React.ComponentRef<typeof DropdownMenuPrimitive.Label>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {\n inset?: boolean;\n }\n>(({ className, inset, ...props }, ref) => (\n <DropdownMenuPrimitive.Label\n ref={ref}\n data-slot=\"dropdown-menu-label\"\n className={cn(dropdownLabelClasses, inset && 'pl-8', className)}\n {...props}\n />\n));\nDropdownMenuGlassLabel.displayName = 'DropdownMenuGlassLabel';\n\n// ========================================\n// SEPARATOR\n// ========================================\n\nconst DropdownMenuGlassSeparator = React.forwardRef<\n React.ComponentRef<typeof DropdownMenuPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Separator\n ref={ref}\n data-slot=\"dropdown-menu-separator\"\n className={cn(dropdownSeparatorClasses, '-mx-1 my-1', className)}\n {...props}\n />\n));\nDropdownMenuGlassSeparator.displayName = 'DropdownMenuGlassSeparator';\n\n// ========================================\n// SHORTCUT\n// ========================================\n\nconst DropdownMenuGlassShortcut = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLSpanElement>) => {\n return (\n <span\n data-slot=\"dropdown-menu-shortcut\"\n className={cn('ml-auto text-xs tracking-widest text-(--text-muted)', className)}\n {...props}\n />\n );\n};\nDropdownMenuGlassShortcut.displayName = 'DropdownMenuGlassShortcut';\n\n// ========================================\n// EXPORTS\n// ========================================\n\nexport {\n DropdownMenuGlass,\n DropdownMenuGlassTrigger,\n DropdownMenuGlassContent,\n DropdownMenuGlassItem,\n DropdownMenuGlassCheckboxItem,\n DropdownMenuGlassRadioItem,\n DropdownMenuGlassLabel,\n DropdownMenuGlassSeparator,\n DropdownMenuGlassShortcut,\n DropdownMenuGlassGroup,\n DropdownMenuGlassPortal,\n DropdownMenuGlassSub,\n DropdownMenuGlassSubContent,\n DropdownMenuGlassSubTrigger,\n DropdownMenuGlassRadioGroup,\n};\n"
|
|
21
21
|
}
|
|
22
22
|
],
|
|
23
23
|
"categories": [
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
{
|
|
15
15
|
"path": "components/glass/specialized/flag-alert-glass.tsx",
|
|
16
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<\n FlagType,\n { bg: string; border: string; text: string; statusType: StatusType }\n> = {\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('p-2.5 md:p-3 rounded-xl border transition-all duration-300'
|
|
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<\n FlagType,\n { bg: string; border: string; text: string; statusType: StatusType }\n> = {\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 max-w-md',\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 className=\"text-(length:--font-size-2xs) md:text-xs mt-0.5 md:mt-1 ml-4 md:ml-5 text-(--text-muted)\">\n {description}\n </p>\n )}\n </div>\n );\n }\n);\n\nFlagAlertGlass.displayName = 'FlagAlertGlass';\n"
|
|
18
18
|
}
|
|
19
19
|
],
|
|
20
20
|
"categories": [
|
package/dist/r/glass-card.json
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
"description": "Glass-themed container with:",
|
|
7
7
|
"dependencies": [
|
|
8
8
|
"@radix-ui/react-slot",
|
|
9
|
-
"class-variance-authority"
|
|
9
|
+
"class-variance-authority",
|
|
10
|
+
"react"
|
|
10
11
|
],
|
|
11
12
|
"registryDependencies": [
|
|
12
13
|
"cn",
|
|
@@ -17,7 +18,7 @@
|
|
|
17
18
|
{
|
|
18
19
|
"path": "components/glass/ui/glass-card.tsx",
|
|
19
20
|
"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 {
|
|
21
|
+
"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 { forwardRef, type ReactNode, type CSSProperties } 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 * A glass-themed container with configurable blur, glow effects, and hover animations.\n * Features polymorphic rendering via `asChild` for semantic HTML flexibility.\n *\n * @accessibility\n * - **Keyboard Navigation:** When used with `asChild` as a link/button, inherits native keyboard support (Enter/Space activation)\n * - **Focus Management:** Focus ring applied to child element when using `asChild` pattern with interactive elements\n * - **Screen Readers:** Semantic HTML preserved via `asChild` - use appropriate elements (`<a>`, `<button>`, `<article>`)\n * - **Hover State:** Hover effects are purely visual and do not affect functionality (progressive enhancement)\n * - **Touch Targets:** When interactive, ensure child element meets minimum 44x44px touch target (WCAG 2.5.5)\n * - **Color Contrast:** Card border and background meet WCAG AA contrast requirements, content contrast is consumer's responsibility\n * - **Motion:** Hover scale animation respects `prefers-reduced-motion` settings via CSS transitions\n *\n * @example\n * ```tsx\n * // Basic card\n * <GlassCard intensity=\"medium\">Content</GlassCard>\n *\n * // As a clickable link with accessible name\n * <GlassCard asChild intensity=\"medium\">\n * <a href=\"/details\" aria-label=\"View product details\">\n * <h3>Product Title</h3>\n * <p>Description</p>\n * </a>\n * </GlassCard>\n *\n * // Different intensity levels\n * <GlassCard intensity=\"subtle\">Subtle blur</GlassCard>\n * <GlassCard intensity=\"medium\">Standard blur</GlassCard>\n * <GlassCard intensity=\"strong\">Heavy blur</GlassCard>\n *\n * // With glow effects\n * <GlassCard glow=\"blue\">Blue glow card</GlassCard>\n * <GlassCard glow=\"violet\">Violet glow card</GlassCard>\n * <GlassCard glow=\"cyan\">Cyan glow card</GlassCard>\n *\n * // As a button (interactive) with role\n * <GlassCard asChild hover intensity=\"medium\">\n * <button onClick={handleClick} aria-label=\"Open settings\">\n * <Settings className=\"w-6 h-6\" />\n * <span>Settings</span>\n * </button>\n * </GlassCard>\n *\n * // Article card with semantic HTML\n * <GlassCard asChild intensity=\"medium\" padding=\"lg\">\n * <article>\n * <header><h2>Article Title</h2></header>\n * <p>Article content...</p>\n * <footer>Published: Jan 1, 2025</footer>\n * </article>\n * </GlassCard>\n * ```\n */\nexport interface GlassCardProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, 'style'>, 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 data-slot=\"card\"\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
|
}
|
|
22
23
|
],
|
|
23
24
|
"categories": [
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
{
|
|
20
20
|
"path": "components/glass/ui/notification-glass.tsx",
|
|
21
21
|
"type": "registry:component",
|
|
22
|
-
"content": "/**\n * NotificationGlass Component\n *\n * Glass-themed toast notification with:\n * - Theme-aware styling (glass/light/aurora)\n * - Type variants (info/success/warning/error)\n * - Glow effect on hover\n * - Dismissible\n */\n\nimport { forwardRef, type CSSProperties } from 'react';\nimport { type VariantProps } from 'class-variance-authority';\nimport { Info, CheckCircle, AlertTriangle, AlertCircle, X } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport { useHover } from '@/lib/hooks/use-hover';\nimport { notificationVariants } from '@/lib/variants/notification-glass-variants';\nimport { ICON_SIZES } from '@/components/glass/primitives';\nimport '@/glass-theme.css';\n\nimport type { NotificationType } from '@/lib/variants/notification-glass-variants';\n\n// ========================================\n// CONSTANTS\n// ========================================\n\nconst NOTIFICATION_ICONS = {\n info: Info,\n success: CheckCircle,\n warning: AlertTriangle,\n error: AlertCircle,\n} as const;\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\nexport interface NotificationGlassProps\n extends
|
|
22
|
+
"content": "/**\n * NotificationGlass Component\n *\n * Glass-themed toast notification with:\n * - Theme-aware styling (glass/light/aurora)\n * - Type variants (info/success/warning/error)\n * - Glow effect on hover\n * - Dismissible\n */\n\nimport { forwardRef, type CSSProperties } from 'react';\nimport { type VariantProps } from 'class-variance-authority';\nimport { Info, CheckCircle, AlertTriangle, AlertCircle, X } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport { useHover } from '@/lib/hooks/use-hover';\nimport { notificationVariants } from '@/lib/variants/notification-glass-variants';\nimport { ICON_SIZES } from '@/components/glass/primitives';\nimport '@/glass-theme.css';\n\nimport type { NotificationType } from '@/lib/variants/notification-glass-variants';\n\n// ========================================\n// CONSTANTS\n// ========================================\n\nconst NOTIFICATION_ICONS = {\n info: Info,\n success: CheckCircle,\n warning: AlertTriangle,\n error: AlertCircle,\n} as const;\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\nexport interface NotificationGlassProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'title' | 'style'>,\n VariantProps<typeof notificationVariants> {\n readonly title: string;\n readonly message: string;\n /** Notification variant (shadcn/ui compatible) */\n readonly variant?: 'default' | 'destructive' | 'success' | 'warning';\n /** @deprecated Use variant prop instead. Will be removed in next major version. */\n readonly type?: NotificationType;\n readonly onClose: () => void;\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\n// Type-specific CSS variable mapping\nconst getTypeVars = (\n notifType: NotificationType\n): { color: string; glow: string; iconBg: string } => {\n const configs: Record<NotificationType, { color: string; glow: string; iconBg: string }> = {\n info: {\n color: 'var(--notification-info-color)',\n glow: 'var(--notification-info-glow)',\n iconBg: 'var(--notification-info-icon-bg)',\n },\n success: {\n color: 'var(--notification-success-color)',\n glow: 'var(--notification-success-glow)',\n iconBg: 'var(--notification-success-icon-bg)',\n },\n warning: {\n color: 'var(--notification-warning-color)',\n glow: 'var(--notification-warning-glow)',\n iconBg: 'var(--notification-warning-icon-bg)',\n },\n error: {\n color: 'var(--notification-error-color)',\n glow: 'var(--notification-error-glow)',\n iconBg: 'var(--notification-error-icon-bg)',\n },\n };\n return configs[notifType];\n};\n\nexport const NotificationGlass = forwardRef<HTMLDivElement, NotificationGlassProps>(\n ({ variant: variantProp, type: typeProp, title, message, onClose, className, ...props }, ref) => {\n // Backward compatibility: support deprecated 'type' prop\n const variant = variantProp ?? typeProp ?? 'default';\n\n // Show deprecation warning in development\n if (process.env.NODE_ENV === 'development' && typeProp) {\n console.warn(\n 'NotificationGlass: The \"type\" prop is deprecated. Use \"variant\" instead. Example: <NotificationGlass variant=\"destructive\" />'\n );\n }\n\n // Map variant to internal notification type\n const variantToType: Record<string, NotificationType> = {\n default: 'info',\n destructive: 'error',\n success: 'success',\n warning: 'warning',\n // Backward compatibility aliases\n info: 'info',\n error: 'error',\n };\n\n const effectiveType: NotificationType = variantToType[variant] || 'info';\n\n const { isHovered, hoverProps } = useHover();\n const Icon = NOTIFICATION_ICONS[effectiveType];\n const config = getTypeVars(effectiveType);\n\n const containerStyles: CSSProperties = {\n background: 'var(--notification-bg)',\n border: '1px solid var(--notification-border)',\n boxShadow: isHovered ? config.glow : 'var(--notification-shadow)',\n transform: isHovered ? 'translateY(-2px)' : 'translateY(0)',\n };\n\n const iconContainerStyles: CSSProperties = {\n background: config.iconBg,\n boxShadow: isHovered ? config.glow : 'none',\n };\n\n return (\n <div\n ref={ref}\n data-slot=\"notification\"\n className={cn(notificationVariants({ type: effectiveType }), className)}\n style={containerStyles}\n role=\"alert\"\n aria-live=\"polite\"\n onMouseEnter={hoverProps.onMouseEnter}\n onMouseLeave={hoverProps.onMouseLeave}\n {...props}\n >\n {/* Icon with glow */}\n <div\n className=\"w-8 h-8 md:w-10 md:h-10 rounded-xl flex items-center justify-center shrink-0\"\n style={iconContainerStyles}\n >\n <Icon className=\"w-4 h-4 md:w-5 md:h-5\" style={{ color: config.color }} />\n </div>\n\n {/* Content */}\n <div className=\"flex-1 min-w-0\">\n <p\n className=\"font-semibold text-xs md:text-sm mb-0.5 md:mb-1\"\n style={{ color: 'var(--text-primary)' }}\n >\n {title}\n </p>\n <p className=\"text-xs md:text-sm\" style={{ color: 'var(--text-secondary)' }}>\n {message}\n </p>\n </div>\n\n {/* Close button */}\n <button\n onClick={onClose}\n className=\"p-1 md:p-1.5 rounded-lg shrink-0\"\n style={{ color: 'var(--text-muted)' }}\n type=\"button\"\n aria-label=\"Close notification\"\n >\n <X className={ICON_SIZES.md} />\n </button>\n </div>\n );\n }\n);\n\nNotificationGlass.displayName = 'NotificationGlass';\n"
|
|
23
23
|
}
|
|
24
24
|
],
|
|
25
25
|
"categories": [
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
{
|
|
16
16
|
"path": "components/glass/ui/popover-glass.tsx",
|
|
17
17
|
"type": "registry:component",
|
|
18
|
-
"content": "/**\n * PopoverGlass Component\n *\n * Floating glass-themed container for tooltips, dropdowns, and overlays with:\n * - Theme-aware styling (glass/light/aurora)\n * - Smooth animations with fade-in effect\n * - Arrow pointer with glass styling\n * - ESC key and click-outside to close\n * - Focus trap for accessibility\n * - All position/alignment options (top/right/bottom/left × start/center/end)\n *\n * @example Compound API (recommended)\n * ```tsx\n * <PopoverGlass>\n * <PopoverGlassTrigger asChild>\n * <ButtonGlass>Open</ButtonGlass>\n * </PopoverGlassTrigger>\n * <PopoverGlassContent side=\"top\">\n * <div className=\"p-4\">\n * <h3 style={{ color: 'var(--text-primary)' }}>Title</h3>\n * <p style={{ color: 'var(--text-secondary)' }}>Content</p>\n * </div>\n * </PopoverGlassContent>\n * </PopoverGlass>\n * ```\n *\n * @example Legacy API (backward compatible)\n * ```tsx\n * <PopoverGlassLegacy\n * trigger={<ButtonGlass>Open</ButtonGlass>}\n * side=\"top\"\n * >\n * <div className=\"p-4\">Content</div>\n * </PopoverGlassLegacy>\n * ```\n */\n\n'use client';\n\nimport * as React from 'react';\nimport * as PopoverPrimitive from '@radix-ui/react-popover';\nimport { cn } from '@/lib/utils';\nimport '@/glass-theme.css';\n\n// ========================================\n// COMPOUND COMPONENT: ROOT\n// ========================================\n\nconst PopoverGlassRoot = PopoverPrimitive.Root;\n\n// ========================================\n// COMPOUND COMPONENT: TRIGGER\n// ========================================\n\nconst PopoverGlassTrigger = PopoverPrimitive.Trigger;\n\n// ========================================\n// COMPOUND COMPONENT: ANCHOR\n// ========================================\n\nconst PopoverGlassAnchor = PopoverPrimitive.Anchor;\n\n// ========================================\n// COMPOUND COMPONENT: CONTENT\n// ========================================\n\ninterface PopoverGlassContentProps extends React.ComponentPropsWithoutRef<\n typeof PopoverPrimitive.Content\n> {\n /** Whether to show the arrow pointer */\n showArrow?: boolean;\n}\n\nconst PopoverGlassContent = React.forwardRef<\n React.ElementRef<typeof PopoverPrimitive.Content>,\n PopoverGlassContentProps\n>(({ className, align = 'center', sideOffset = 8, showArrow = true, children, ...props }, ref) => {\n // Popover content styles with CSS variables\n const popoverStyles: React.CSSProperties = {\n background: 'var(--popover-bg)',\n border: '1px solid var(--popover-border)',\n boxShadow: 'var(--popover-shadow)',\n backdropFilter: 'blur(var(--blur-md))',\n WebkitBackdropFilter: 'blur(var(--blur-md))',\n };\n\n // Arrow styles\n const arrowStyles: React.CSSProperties = {\n fill: 'var(--popover-arrow-bg)',\n };\n\n return (\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n ref={ref}\n align={align}\n sideOffset={sideOffset}\n className={cn(\n 'z-50003 rounded-2xl',\n 'animate-in fade-in-0 zoom-in-95 duration-200',\n 'data-[side=bottom]:slide-in-from-top-2',\n 'data-[side=top]:slide-in-from-bottom-2',\n 'data-[side=right]:slide-in-from-left-2',\n 'data-[side=left]:slide-in-from-right-2',\n 'outline-none',\n className\n )}\n style={popoverStyles}\n {...props}\n >\n {children}\n\n {showArrow && (\n <PopoverPrimitive.Arrow\n className=\"fill-current\"\n style={arrowStyles}\n width={16}\n height={8}\n />\n )}\n </PopoverPrimitive.Content>\n </PopoverPrimitive.Portal>\n );\n});\n\nPopoverGlassContent.displayName = 'PopoverGlassContent';\n\n// ========================================\n// LEGACY API (backward compatible)\n// ========================================\n\nexport interface PopoverGlassLegacyProps {\n /** The trigger element that opens the popover */\n readonly trigger: React.ReactNode;\n /** The content to display inside the popover */\n readonly children: React.ReactNode;\n /** The preferred side of the trigger to render against */\n readonly side?: 'top' | 'right' | 'bottom' | 'left';\n /** The preferred alignment against the trigger */\n readonly align?: 'start' | 'center' | 'end';\n /** The distance in pixels from the trigger */\n readonly sideOffset?: number;\n /** Controlled open state */\n readonly open?: boolean;\n /** Callback when open state changes */\n readonly onOpenChange?: (open: boolean) => void;\n /** Whether to show the arrow pointer */\n readonly showArrow?: boolean;\n /** Additional class name for the content wrapper */\n readonly className?: string;\n}\n\nconst PopoverGlassLegacy = React.forwardRef<HTMLDivElement, PopoverGlassLegacyProps>(\n (\n {\n trigger,\n children,\n side = 'bottom',\n align = 'center',\n sideOffset = 8,\n open,\n onOpenChange,\n showArrow = true,\n className,\n },\n ref\n ) => {\n return (\n <PopoverGlassRoot open={open} onOpenChange={onOpenChange}>\n <PopoverGlassTrigger asChild>{trigger}</PopoverGlassTrigger>\n <PopoverGlassContent\n ref={ref}\n side={side}\n align={align}\n sideOffset={sideOffset}\n showArrow={showArrow}\n className={className}\n >\n {children}\n </PopoverGlassContent>\n </PopoverGlassRoot>\n );\n }\n);\n\nPopoverGlassLegacy.displayName = 'PopoverGlassLegacy';\n\n// ========================================\n// EXPORTS\n// ========================================\n\n// Compound API (shadcn/ui pattern)\nexport const PopoverGlass = PopoverGlassRoot;\nexport { PopoverGlassTrigger, PopoverGlassContent, PopoverGlassAnchor };\n\n// Legacy API (backward compatible)\nexport { PopoverGlassLegacy };\n\n// For backward compatibility, also export as default\nexport { PopoverGlassLegacy as default };\n"
|
|
18
|
+
"content": "/**\n * PopoverGlass Component\n *\n * Floating glass-themed container for tooltips, dropdowns, and overlays with:\n * - Theme-aware styling (glass/light/aurora)\n * - Smooth animations with fade-in effect\n * - Arrow pointer with glass styling\n * - ESC key and click-outside to close\n * - Focus trap for accessibility\n * - All position/alignment options (top/right/bottom/left × start/center/end)\n *\n * @example Compound API (recommended)\n * ```tsx\n * <PopoverGlass>\n * <PopoverGlassTrigger asChild>\n * <ButtonGlass>Open</ButtonGlass>\n * </PopoverGlassTrigger>\n * <PopoverGlassContent side=\"top\">\n * <div className=\"p-4\">\n * <h3 style={{ color: 'var(--text-primary)' }}>Title</h3>\n * <p style={{ color: 'var(--text-secondary)' }}>Content</p>\n * </div>\n * </PopoverGlassContent>\n * </PopoverGlass>\n * ```\n *\n * @example Legacy API (backward compatible)\n * ```tsx\n * <PopoverGlassLegacy\n * trigger={<ButtonGlass>Open</ButtonGlass>}\n * side=\"top\"\n * >\n * <div className=\"p-4\">Content</div>\n * </PopoverGlassLegacy>\n * ```\n */\n\n'use client';\n\nimport * as React from 'react';\nimport * as PopoverPrimitive from '@radix-ui/react-popover';\nimport { cn } from '@/lib/utils';\nimport '@/glass-theme.css';\n\n// ========================================\n// COMPOUND COMPONENT: ROOT\n// ========================================\n\n// Note: PopoverPrimitive.Root is a context provider and doesn't render DOM,\n// so data-slot is applied to Content instead. This matches shadcn/ui pattern.\nconst PopoverGlassRoot = (props: React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Root>) => (\n <PopoverPrimitive.Root {...props} />\n);\nPopoverGlassRoot.displayName = 'PopoverGlass';\n\n// ========================================\n// COMPOUND COMPONENT: TRIGGER\n// ========================================\n\nconst PopoverGlassTrigger = React.forwardRef<\n React.ComponentRef<typeof PopoverPrimitive.Trigger>,\n React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Trigger>\n>((props, ref) => <PopoverPrimitive.Trigger data-slot=\"popover-trigger\" ref={ref} {...props} />);\nPopoverGlassTrigger.displayName = 'PopoverGlassTrigger';\n\n// ========================================\n// COMPOUND COMPONENT: ANCHOR\n// ========================================\n\nconst PopoverGlassAnchor = React.forwardRef<\n React.ComponentRef<typeof PopoverPrimitive.Anchor>,\n React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Anchor>\n>((props, ref) => <PopoverPrimitive.Anchor data-slot=\"popover-anchor\" ref={ref} {...props} />);\nPopoverGlassAnchor.displayName = 'PopoverGlassAnchor';\n\n// ========================================\n// COMPOUND COMPONENT: CONTENT\n// ========================================\n\ninterface PopoverGlassContentProps extends React.ComponentPropsWithoutRef<\n typeof PopoverPrimitive.Content\n> {\n /** Whether to show the arrow pointer */\n showArrow?: boolean;\n}\n\nconst PopoverGlassContent = React.forwardRef<\n React.ElementRef<typeof PopoverPrimitive.Content>,\n PopoverGlassContentProps\n>(({ className, align = 'center', sideOffset = 8, showArrow = true, children, ...props }, ref) => {\n // Popover content styles with CSS variables\n const popoverStyles: React.CSSProperties = {\n background: 'var(--popover-bg)',\n border: '1px solid var(--popover-border)',\n boxShadow: 'var(--popover-shadow)',\n backdropFilter: 'blur(var(--blur-md))',\n WebkitBackdropFilter: 'blur(var(--blur-md))',\n };\n\n // Arrow styles\n const arrowStyles: React.CSSProperties = {\n fill: 'var(--popover-arrow-bg)',\n };\n\n return (\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n ref={ref}\n data-slot=\"popover-content\"\n align={align}\n sideOffset={sideOffset}\n className={cn(\n 'z-50003 rounded-2xl',\n 'animate-in fade-in-0 zoom-in-95 duration-200',\n 'data-[side=bottom]:slide-in-from-top-2',\n 'data-[side=top]:slide-in-from-bottom-2',\n 'data-[side=right]:slide-in-from-left-2',\n 'data-[side=left]:slide-in-from-right-2',\n 'outline-none',\n className\n )}\n style={popoverStyles}\n {...props}\n >\n {children}\n\n {showArrow && (\n <PopoverPrimitive.Arrow\n className=\"fill-current\"\n style={arrowStyles}\n width={16}\n height={8}\n />\n )}\n </PopoverPrimitive.Content>\n </PopoverPrimitive.Portal>\n );\n});\n\nPopoverGlassContent.displayName = 'PopoverGlassContent';\n\n// ========================================\n// LEGACY API (backward compatible)\n// ========================================\n\nexport interface PopoverGlassLegacyProps {\n /** The trigger element that opens the popover */\n readonly trigger: React.ReactNode;\n /** The content to display inside the popover */\n readonly children: React.ReactNode;\n /** The preferred side of the trigger to render against */\n readonly side?: 'top' | 'right' | 'bottom' | 'left';\n /** The preferred alignment against the trigger */\n readonly align?: 'start' | 'center' | 'end';\n /** The distance in pixels from the trigger */\n readonly sideOffset?: number;\n /** Controlled open state */\n readonly open?: boolean;\n /** Callback when open state changes */\n readonly onOpenChange?: (open: boolean) => void;\n /** Whether to show the arrow pointer */\n readonly showArrow?: boolean;\n /** Additional class name for the content wrapper */\n readonly className?: string;\n}\n\nconst PopoverGlassLegacy = React.forwardRef<HTMLDivElement, PopoverGlassLegacyProps>(\n (\n {\n trigger,\n children,\n side = 'bottom',\n align = 'center',\n sideOffset = 8,\n open,\n onOpenChange,\n showArrow = true,\n className,\n },\n ref\n ) => {\n return (\n <PopoverGlassRoot open={open} onOpenChange={onOpenChange}>\n <PopoverGlassTrigger asChild>{trigger}</PopoverGlassTrigger>\n <PopoverGlassContent\n ref={ref}\n side={side}\n align={align}\n sideOffset={sideOffset}\n showArrow={showArrow}\n className={className}\n >\n {children}\n </PopoverGlassContent>\n </PopoverGlassRoot>\n );\n }\n);\n\nPopoverGlassLegacy.displayName = 'PopoverGlassLegacy';\n\n// ========================================\n// EXPORTS\n// ========================================\n\n// Compound API (shadcn/ui pattern)\nexport const PopoverGlass = PopoverGlassRoot;\nexport { PopoverGlassTrigger, PopoverGlassContent, PopoverGlassAnchor };\n\n// Legacy API (backward compatible)\nexport { PopoverGlassLegacy };\n\n// For backward compatibility, also export as default\nexport { PopoverGlassLegacy as default };\n"
|
|
19
19
|
}
|
|
20
20
|
],
|
|
21
21
|
"categories": [
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
{
|
|
17
17
|
"path": "components/glass/specialized/progress-glass.tsx",
|
|
18
18
|
"type": "registry:component",
|
|
19
|
-
"content": "/**\n * ProgressGlass Component\n *\n * Glass-themed progress bar with:\n * - Theme-aware styling (glass/light/aurora)\n * - Gradient fill with glow\n * - Size variants\n * - Optional label\n */\n\nimport { forwardRef, type CSSProperties } from 'react';\nimport { type VariantProps } from 'class-variance-authority';\nimport { cn } from '@/lib/utils';\nimport { progressSizes, type ProgressGradient } from '@/lib/variants/progress-glass-variants';\nimport '@/glass-theme.css';\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\nexport interface ProgressGlassProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, 'style'>, VariantProps<typeof progressSizes> {\n readonly value: number;\n readonly gradient?: ProgressGradient;\n readonly showLabel?: boolean;\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\n// Map gradient to existing metric color variables\nconst getGradientColor = (\n gradient: ProgressGradient = 'violet'\n): { colorVar: string; glowVar: string } => {\n const gradients: Record<ProgressGradient, { colorVar: string; glowVar: string }> = {\n violet: { colorVar: '--metric-default-text', glowVar: '--progress-glow-violet' }, // Uses blue metric color\n blue: { colorVar: '--metric-default-text', glowVar: '--progress-glow-blue' },\n cyan: { colorVar: '--metric-secondary-text', glowVar: '--progress-glow-cyan' },\n amber: { colorVar: '--metric-warning-text', glowVar: '--progress-glow-amber' },\n emerald: { colorVar: '--metric-success-text', glowVar: '--progress-glow-emerald' },\n rose: { colorVar: '--metric-destructive-text', glowVar: '--progress-glow-rose' },\n };\n return gradients[gradient] || gradients.violet;\n};\n\nexport const ProgressGlass = forwardRef<HTMLDivElement, ProgressGlassProps>(\n ({ className, size = 'md', value = 0, gradient = 'violet', showLabel, ...props }, ref) => {\n const clampedValue = Math.min(100, Math.max(0, value));\n const { colorVar, glowVar } = getGradientColor(gradient);\n\n const trackStyles: CSSProperties = {\n background: 'var(--progress-bg)',\n };\n\n const fillStyles: CSSProperties = {\n width: `${clampedValue}%`,\n background: `var(${colorVar})`,\n boxShadow: `var(${glowVar})`,\n };\n\n return (\n <div ref={ref} className={cn('w-full', className)} {...props}>\n {showLabel && (\n <div className=\"flex justify-between mb-1 md:mb-1.5\">\n <span className=\"text-(length:--font-size-2xs) md:text-xs text-(--text-muted)\">\n Progress\n </span>\n <span className=\"text-(length:--font-size-2xs) md:text-xs font-medium text-(--text-secondary)\">\n {clampedValue}%\n </span>\n </div>\n )}\n <div className={cn(progressSizes({ size }))} style={trackStyles}>\n <div\n className=\"h-full rounded-full transition-all duration-700 ease-out\"\n style={fillStyles}\n role=\"progressbar\"\n aria-valuenow={clampedValue}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-label={`Progress: ${clampedValue}%`}\n />\n </div>\n </div>\n );\n }\n);\n\nProgressGlass.displayName = 'ProgressGlass';\n"
|
|
19
|
+
"content": "/**\n * ProgressGlass Component\n *\n * Glass-themed progress bar with:\n * - Theme-aware styling (glass/light/aurora)\n * - Gradient fill with glow\n * - Size variants\n * - Optional label\n */\n\nimport { forwardRef, type CSSProperties } from 'react';\nimport { type VariantProps } from 'class-variance-authority';\nimport { cn } from '@/lib/utils';\nimport { progressSizes, type ProgressGradient } from '@/lib/variants/progress-glass-variants';\nimport '@/glass-theme.css';\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\nexport interface ProgressGlassProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, 'style'>, VariantProps<typeof progressSizes> {\n readonly value: number;\n readonly gradient?: ProgressGradient;\n readonly showLabel?: boolean;\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\n// Map gradient to existing metric color variables\nconst getGradientColor = (\n gradient: ProgressGradient = 'violet'\n): { colorVar: string; glowVar: string } => {\n const gradients: Record<ProgressGradient, { colorVar: string; glowVar: string }> = {\n violet: { colorVar: '--metric-default-text', glowVar: '--progress-glow-violet' }, // Uses blue metric color\n blue: { colorVar: '--metric-default-text', glowVar: '--progress-glow-blue' },\n cyan: { colorVar: '--metric-secondary-text', glowVar: '--progress-glow-cyan' },\n amber: { colorVar: '--metric-warning-text', glowVar: '--progress-glow-amber' },\n emerald: { colorVar: '--metric-success-text', glowVar: '--progress-glow-emerald' },\n rose: { colorVar: '--metric-destructive-text', glowVar: '--progress-glow-rose' },\n };\n return gradients[gradient] || gradients.violet;\n};\n\nexport const ProgressGlass = forwardRef<HTMLDivElement, ProgressGlassProps>(\n ({ className, size = 'md', value = 0, gradient = 'violet', showLabel, ...props }, ref) => {\n const clampedValue = Math.min(100, Math.max(0, value));\n const { colorVar, glowVar } = getGradientColor(gradient);\n\n const trackStyles: CSSProperties = {\n background: 'var(--progress-bg)',\n };\n\n const fillStyles: CSSProperties = {\n width: `${clampedValue}%`,\n background: `var(${colorVar})`,\n boxShadow: `var(${glowVar})`,\n };\n\n return (\n <div ref={ref} data-slot=\"progress\" className={cn('w-full', className)} {...props}>\n {showLabel && (\n <div className=\"flex justify-between mb-1 md:mb-1.5\">\n <span className=\"text-(length:--font-size-2xs) md:text-xs text-(--text-muted)\">\n Progress\n </span>\n <span className=\"text-(length:--font-size-2xs) md:text-xs font-medium text-(--text-secondary)\">\n {clampedValue}%\n </span>\n </div>\n )}\n <div className={cn(progressSizes({ size }))} style={trackStyles}>\n <div\n data-slot=\"progress-indicator\"\n className=\"h-full rounded-full transition-all duration-700 ease-out\"\n style={fillStyles}\n role=\"progressbar\"\n aria-valuenow={clampedValue}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-label={`Progress: ${clampedValue}%`}\n />\n </div>\n </div>\n );\n }\n);\n\nProgressGlass.displayName = 'ProgressGlass';\n"
|
|
20
20
|
}
|
|
21
21
|
],
|
|
22
22
|
"categories": [
|
package/dist/r/registry.json
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"name": "tabs-glass",
|
|
20
20
|
"type": "registry:ui",
|
|
21
21
|
"title": "Tabs Glass",
|
|
22
|
-
"description": "TabsGlass Component (
|
|
22
|
+
"description": "TabsGlass Component (Radix UI based)"
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
25
|
"name": "stepper-glass",
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
"name": "checkbox-glass",
|
|
98
98
|
"type": "registry:ui",
|
|
99
99
|
"title": "Checkbox Glass",
|
|
100
|
-
"description": "Glass-themed checkbox with:"
|
|
100
|
+
"description": "Glass-themed checkbox built on Radix UI primitives with:"
|
|
101
101
|
},
|
|
102
102
|
{
|
|
103
103
|
"name": "card-glass",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
{
|
|
17
17
|
"path": "components/glass/ui/skeleton-glass.tsx",
|
|
18
18
|
"type": "registry:component",
|
|
19
|
-
"content": "/**\n * SkeletonGlass Component\n *\n * Glass-themed loading skeleton with:\n * - Theme-aware styling (glass/light/aurora)\n * - Shimmer animation\n * - Variant presets (text, title, avatar, thumbnail, card)\n */\n\nimport { forwardRef, type CSSProperties } from 'react';\nimport { type VariantProps } from 'class-variance-authority';\nimport { cn } from '@/lib/utils';\nimport { skeletonVariants } from '@/lib/variants/skeleton-glass-variants';\nimport '@/glass-theme.css';\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\nexport interface SkeletonGlassProps\n extends React.HTMLAttributes<HTMLDivElement
|
|
19
|
+
"content": "/**\n * SkeletonGlass Component\n *\n * Glass-themed loading skeleton with:\n * - Theme-aware styling (glass/light/aurora)\n * - Shimmer animation\n * - Variant presets (text, title, avatar, thumbnail, card)\n */\n\nimport { forwardRef, type CSSProperties } from 'react';\nimport { type VariantProps } from 'class-variance-authority';\nimport { cn } from '@/lib/utils';\nimport { skeletonVariants } from '@/lib/variants/skeleton-glass-variants';\nimport '@/glass-theme.css';\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\nexport interface SkeletonGlassProps\n extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof skeletonVariants> {\n readonly width?: string | number;\n readonly height?: string | number;\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\nexport const SkeletonGlass = forwardRef<HTMLDivElement, SkeletonGlassProps>(\n ({ className, variant = 'text', width, height, style, ...props }, ref) => {\n const skeletonStyles: CSSProperties = {\n width,\n height,\n background:\n 'linear-gradient(90deg, var(--skeleton-bg) 25%, var(--skeleton-shine) 50%, var(--skeleton-bg) 75%)',\n backgroundSize: '200% 100%',\n animation: 'skeleton-loading 1.5s infinite',\n ...style,\n };\n\n return (\n <div\n ref={ref}\n data-slot=\"skeleton\"\n className={cn(skeletonVariants({ variant }), className)}\n style={skeletonStyles}\n aria-hidden=\"true\"\n {...props}\n />\n );\n }\n);\n\nSkeletonGlass.displayName = 'SkeletonGlass';\n"
|
|
20
20
|
}
|
|
21
21
|
],
|
|
22
22
|
"categories": [
|
package/dist/r/slider-glass.json
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
{
|
|
17
17
|
"path": "components/glass/ui/slider-glass.tsx",
|
|
18
18
|
"type": "registry:component",
|
|
19
|
-
"content": "/**\n * SliderGlass Component\n *\n * Glass-themed range slider built on Radix UI with shadcn/ui compatible API:\n * - Theme-aware styling (glass/light/aurora)\n * - Glow effect on hover/drag\n * - Gradient fill (glass theme)\n * - Range slider support (multiple thumbs)\n * - Optional label and value display\n *\n * **shadcn/ui compatible props:**\n * - `value` / `defaultValue` - Array of values (supports range)\n * - `onValueChange` - Callback when values change\n * - `onValueCommit` - Callback when interaction ends\n *\n * @example\n * ```tsx\n * // Single value (controlled)\n * <SliderGlass value={[50]} onValueChange={setValue} />\n *\n * // Range slider\n * <SliderGlass defaultValue={[25, 75]} />\n *\n * // With label and value display\n * <SliderGlass value={[50]} onValueChange={setValue} label=\"Volume\" showValue />\n * ```\n */\n\nimport { forwardRef, type CSSProperties, type ComponentPropsWithoutRef } from 'react';\nimport * as SliderPrimitive from '@radix-ui/react-slider';\nimport { cn } from '@/lib/utils';\nimport { FormFieldWrapper } from '@/components/glass/primitives';\nimport '@/glass-theme.css';\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\nexport interface SliderGlassProps extends Omit<\n ComponentPropsWithoutRef<typeof SliderPrimitive.Root>,\n 'value' | 'defaultValue' | 'onValueChange'\n> {\n /**\n * Controlled value (shadcn/ui compatible - array for range support)\n */\n readonly value?: number[];\n /**\n * Default value for uncontrolled usage (array for range support)\n */\n readonly defaultValue?: number[];\n /**\n * Callback when value changes (shadcn/ui compatible)\n */\n readonly onValueChange?: (value: number[]) => void;\n /**\n * Callback when interaction ends (mouse up / touch end)\n */\n readonly onValueCommit?: (value: number[]) => void;\n /**\n * Show current value(s) next to label\n */\n readonly showValue?: boolean;\n /**\n * Optional label text\n */\n readonly label?: string;\n /**\n * Error message to display\n */\n readonly error?: string;\n /**\n * Success message to display\n */\n readonly success?: string;\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\nexport const SliderGlass = forwardRef<\n React.ComponentRef<typeof SliderPrimitive.Root>,\n SliderGlassProps\n>(\n (\n {\n className,\n value,\n defaultValue,\n onValueChange,\n onValueCommit,\n min = 0,\n max = 100,\n step = 1,\n showValue,\n label,\n error,\n success,\n disabled,\n orientation = 'horizontal',\n ...props\n },\n ref\n ) => {\n // Determine current values for rendering thumbs and display\n const currentValue = value ?? defaultValue ?? [min];\n\n // Format value display\n const formatValueDisplay = (values: number[]) => {\n if (values.length === 1) {\n return `${values[0]}`;\n }\n return `${values[0]} - ${values[values.length - 1]}`;\n };\n\n // Track styles via CSS variables\n const trackStyles: CSSProperties = {\n background: 'var(--slider-track)',\n };\n\n // Range (fill) styles via CSS variables\n const rangeStyles: CSSProperties = {\n background: 'var(--slider-fill)',\n };\n\n // Thumb styles via CSS variables\n const thumbStyles: CSSProperties = {\n background: 'var(--slider-thumb)',\n border: '2px solid var(--slider-thumb-border)',\n };\n\n // Custom label with value display - only used when showValue is true\n const customLabel =\n (label && showValue) || (!label && showValue) ? (\n <div className=\"flex justify-between mb-1.5 md:mb-2\">\n {label && (\n <label\n className=\"text-xs md:text-sm font-medium\"\n style={{ color: 'var(--text-secondary)' }}\n >\n {label}\n </label>\n )}\n <span\n className=\"text-xs md:text-sm font-medium tabular-nums\"\n style={{ color: 'var(--text-secondary)' }}\n >\n {formatValueDisplay(currentValue)}\n </span>\n </div>\n ) : undefined;\n\n return (\n <FormFieldWrapper\n label={showValue ? undefined : label}\n error={error}\n success={success}\n className={cn('w-full', className)}\n >\n {customLabel}\n <SliderPrimitive.Root\n ref={ref}\n value={value}\n defaultValue={defaultValue}\n onValueChange={onValueChange}\n onValueCommit={onValueCommit}\n min={min}\n max={max}\n step={step}\n disabled={disabled}\n orientation={orientation}\n className={cn(\n 'relative flex touch-none select-none',\n orientation === 'horizontal'\n ? 'w-full h-8 md:h-6 items-center'\n : 'flex-col h-full w-8 md:w-6 justify-center',\n disabled && 'opacity-50 cursor-not-allowed',\n 'group'\n )}\n {...props}\n >\n <SliderPrimitive.Track\n className={cn(\n 'relative grow rounded-full',\n orientation === 'horizontal' ? 'h-2.5 md:h-2 w-full' : 'w-2.5 md:w-2 h-full'\n )}\n style={trackStyles}\n >\n <SliderPrimitive.Range\n className={cn(\n 'absolute rounded-full transition-shadow duration-150',\n orientation === 'horizontal' ? 'h-full' : 'w-full',\n 'group-hover:shadow-(--slider-fill-glow)',\n 'group-active:shadow-(--slider-fill-glow)'\n )}\n style={rangeStyles}\n />\n </SliderPrimitive.Track>\n {currentValue.map((_, index) => (\n <SliderPrimitive.Thumb\n key={index}\n className={cn(\n 'block rounded-full shadow-md transition-all duration-150',\n 'w-6 h-6 md:w-5 md:h-5',\n 'hover:scale-105',\n 'focus-visible:outline-none focus-visible:shadow-(--focus-glow)',\n 'active:scale-110',\n disabled && 'pointer-events-none'\n )}\n style={thumbStyles}\n aria-label={\n label\n ? currentValue.length > 1\n ? `${label} thumb ${index + 1}`\n : label\n : `Slider thumb ${index + 1}`\n }\n />\n ))}\n </SliderPrimitive.Root>\n </FormFieldWrapper>\n );\n }\n);\n\nSliderGlass.displayName = 'SliderGlass';\n"
|
|
19
|
+
"content": "/**\n * SliderGlass Component\n *\n * Glass-themed range slider built on Radix UI with shadcn/ui compatible API:\n * - Theme-aware styling (glass/light/aurora)\n * - Glow effect on hover/drag\n * - Gradient fill (glass theme)\n * - Range slider support (multiple thumbs)\n * - Optional label and value display\n *\n * **shadcn/ui compatible props:**\n * - `value` / `defaultValue` - Array of values (supports range)\n * - `onValueChange` - Callback when values change\n * - `onValueCommit` - Callback when interaction ends\n *\n * @example\n * ```tsx\n * // Single value (controlled)\n * <SliderGlass value={[50]} onValueChange={setValue} />\n *\n * // Range slider\n * <SliderGlass defaultValue={[25, 75]} />\n *\n * // With label and value display\n * <SliderGlass value={[50]} onValueChange={setValue} label=\"Volume\" showValue />\n * ```\n */\n\nimport { forwardRef, type CSSProperties, type ComponentPropsWithoutRef } from 'react';\nimport * as SliderPrimitive from '@radix-ui/react-slider';\nimport { cn } from '@/lib/utils';\nimport { FormFieldWrapper } from '@/components/glass/primitives';\nimport '@/glass-theme.css';\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\nexport interface SliderGlassProps extends Omit<\n ComponentPropsWithoutRef<typeof SliderPrimitive.Root>,\n 'value' | 'defaultValue' | 'onValueChange'\n> {\n /**\n * Controlled value (shadcn/ui compatible - array for range support)\n */\n readonly value?: number[];\n /**\n * Default value for uncontrolled usage (array for range support)\n */\n readonly defaultValue?: number[];\n /**\n * Callback when value changes (shadcn/ui compatible)\n */\n readonly onValueChange?: (value: number[]) => void;\n /**\n * Callback when interaction ends (mouse up / touch end)\n */\n readonly onValueCommit?: (value: number[]) => void;\n /**\n * Show current value(s) next to label\n */\n readonly showValue?: boolean;\n /**\n * Optional label text\n */\n readonly label?: string;\n /**\n * Error message to display\n */\n readonly error?: string;\n /**\n * Success message to display\n */\n readonly success?: string;\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\nexport const SliderGlass = forwardRef<\n React.ComponentRef<typeof SliderPrimitive.Root>,\n SliderGlassProps\n>(\n (\n {\n className,\n value,\n defaultValue,\n onValueChange,\n onValueCommit,\n min = 0,\n max = 100,\n step = 1,\n showValue,\n label,\n error,\n success,\n disabled,\n orientation = 'horizontal',\n ...props\n },\n ref\n ) => {\n // Determine current values for rendering thumbs and display\n const currentValue = value ?? defaultValue ?? [min];\n\n // Format value display\n const formatValueDisplay = (values: number[]) => {\n if (values.length === 1) {\n return `${values[0]}`;\n }\n return `${values[0]} - ${values[values.length - 1]}`;\n };\n\n // Track styles via CSS variables\n const trackStyles: CSSProperties = {\n background: 'var(--slider-track)',\n };\n\n // Range (fill) styles via CSS variables\n const rangeStyles: CSSProperties = {\n background: 'var(--slider-fill)',\n };\n\n // Thumb styles via CSS variables\n const thumbStyles: CSSProperties = {\n background: 'var(--slider-thumb)',\n border: '2px solid var(--slider-thumb-border)',\n };\n\n // Custom label with value display - only used when showValue is true\n const customLabel =\n (label && showValue) || (!label && showValue) ? (\n <div className=\"flex justify-between mb-1.5 md:mb-2\">\n {label && (\n <label\n className=\"text-xs md:text-sm font-medium\"\n style={{ color: 'var(--text-secondary)' }}\n >\n {label}\n </label>\n )}\n <span\n className=\"text-xs md:text-sm font-medium tabular-nums\"\n style={{ color: 'var(--text-secondary)' }}\n >\n {formatValueDisplay(currentValue)}\n </span>\n </div>\n ) : undefined;\n\n return (\n <FormFieldWrapper\n label={showValue ? undefined : label}\n error={error}\n success={success}\n className={cn('w-full', className)}\n >\n {customLabel}\n <SliderPrimitive.Root\n ref={ref}\n data-slot=\"slider\"\n value={value}\n defaultValue={defaultValue}\n onValueChange={onValueChange}\n onValueCommit={onValueCommit}\n min={min}\n max={max}\n step={step}\n disabled={disabled}\n orientation={orientation}\n className={cn(\n 'relative flex touch-none select-none',\n orientation === 'horizontal'\n ? 'w-full h-8 md:h-6 items-center'\n : 'flex-col h-full w-8 md:w-6 justify-center',\n disabled && 'opacity-50 cursor-not-allowed',\n 'group'\n )}\n {...props}\n >\n <SliderPrimitive.Track\n data-slot=\"slider-track\"\n className={cn(\n 'relative grow rounded-full',\n orientation === 'horizontal' ? 'h-2.5 md:h-2 w-full' : 'w-2.5 md:w-2 h-full'\n )}\n style={trackStyles}\n >\n <SliderPrimitive.Range\n data-slot=\"slider-range\"\n className={cn(\n 'absolute rounded-full transition-shadow duration-150',\n orientation === 'horizontal' ? 'h-full' : 'w-full',\n 'group-hover:shadow-(--slider-fill-glow)',\n 'group-active:shadow-(--slider-fill-glow)'\n )}\n style={rangeStyles}\n />\n </SliderPrimitive.Track>\n {currentValue.map((_, index) => (\n <SliderPrimitive.Thumb\n key={index}\n data-slot=\"slider-thumb\"\n className={cn(\n 'block rounded-full shadow-md transition-all duration-150',\n 'w-6 h-6 md:w-5 md:h-5',\n 'hover:scale-105',\n 'focus-visible:outline-none focus-visible:shadow-(--focus-glow)',\n 'active:scale-110',\n disabled && 'pointer-events-none'\n )}\n style={thumbStyles}\n aria-label={\n label\n ? currentValue.length > 1\n ? `${label} thumb ${index + 1}`\n : label\n : `Slider thumb ${index + 1}`\n }\n />\n ))}\n </SliderPrimitive.Root>\n </FormFieldWrapper>\n );\n }\n);\n\nSliderGlass.displayName = 'SliderGlass';\n"
|
|
20
20
|
}
|
|
21
21
|
],
|
|
22
22
|
"categories": [
|