shadcn-glass-ui 1.0.11 → 2.0.0
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 +213 -0
- package/README.md +174 -17
- package/context7.json +58 -0
- package/dist/cli/index.cjs +470 -0
- package/dist/cli/index.cjs.map +1 -0
- package/dist/cli/index.d.ts +18 -0
- package/dist/cli/index.js +469 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/components.cjs +4 -4
- package/dist/components.d.ts +153 -22
- package/dist/components.js +2 -2
- package/dist/hooks.cjs +2 -2
- package/dist/index.cjs +872 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +0 -2390
- package/dist/index.js +844 -3
- package/dist/index.js.map +1 -1
- package/dist/r/ai-card-glass.json +5 -12
- package/dist/r/avatar-glass.json +5 -15
- package/dist/r/circular-metric-glass.json +5 -11
- package/dist/r/circular-progress-glass.json +5 -12
- package/dist/r/combobox-glass.json +5 -13
- package/dist/r/flag-alert-glass.json +5 -11
- package/dist/r/flags-section-glass.json +5 -12
- package/dist/r/insight-card-glass.json +17 -0
- package/dist/r/interactive-card.json +5 -12
- package/dist/r/language-bar-glass.json +5 -11
- package/dist/r/metric-card-glass.json +6 -13
- package/dist/r/metrics-grid-glass.json +5 -11
- package/dist/r/modal-glass.json +5 -15
- package/dist/r/progress-glass.json +5 -13
- package/dist/r/registry.json +20 -2
- package/dist/r/segmented-control-glass.json +5 -11
- package/dist/r/sparkline-glass.json +17 -0
- package/dist/r/stepper-glass.json +33 -0
- package/dist/r/tabs-glass.json +4 -9
- package/dist/r/trust-score-card-glass.json +5 -12
- package/dist/r/year-card-glass.json +5 -13
- package/dist/shadcn-glass-ui.css +1 -1
- package/dist/{theme-context-XtasSxRT.cjs → theme-context-CVW50BKW.cjs} +2 -2
- package/dist/{theme-context-XtasSxRT.cjs.map → theme-context-CVW50BKW.cjs.map} +1 -1
- package/dist/themes.cjs +1 -1
- package/dist/trust-score-card-glass-BcZbul0P.js +26895 -0
- package/dist/trust-score-card-glass-BcZbul0P.js.map +1 -0
- package/dist/trust-score-card-glass-r3qM09Jp.cjs +27689 -0
- package/dist/trust-score-card-glass-r3qM09Jp.cjs.map +1 -0
- package/dist/{use-focus-BbpE2qGq.cjs → use-focus-ZE8ozmZE.cjs} +2 -2
- package/dist/{use-focus-BbpE2qGq.cjs.map → use-focus-ZE8ozmZE.cjs.map} +1 -1
- package/dist/{use-wallpaper-tint-CIqtoETa.cjs → use-wallpaper-tint-BuS80tbN.cjs} +2 -2
- package/dist/{use-wallpaper-tint-CIqtoETa.cjs.map → use-wallpaper-tint-BuS80tbN.cjs.map} +1 -1
- package/dist/{utils-CGCOTvxT.js → utils-CcyeqpKQ.js} +1 -1
- package/dist/{utils-CGCOTvxT.js.map → utils-CcyeqpKQ.js.map} +1 -1
- package/dist/{utils-CriE74ig.cjs → utils-DLXayspX.cjs} +2 -2
- package/dist/{utils-CriE74ig.cjs.map → utils-DLXayspX.cjs.map} +1 -1
- package/dist/utils.cjs +1 -1
- package/dist/utils.js +1 -1
- package/docs/AI_IMPROVEMENTS_COMPLETE.md +574 -0
- package/docs/AI_USAGE.md +1328 -0
- package/docs/API_PATTERNS_COMPARISON.md +418 -0
- package/docs/BEST_PRACTICES.md +800 -0
- package/docs/COMPLIANCE_CHECKLIST.md +307 -0
- package/docs/COMPLIANCE_TESTING.md +436 -0
- package/docs/COMPONENTS_CATALOG.md +1076 -0
- package/docs/CSS_VARIABLES_AUDIT.md +306 -0
- package/docs/CSS_VARIABLES_REFACTORING_PLAN.md +330 -0
- package/docs/EXPORTS_MAP.json +875 -0
- package/docs/EXPORTS_STRUCTURE.md +729 -0
- package/docs/GETTING_STARTED.md +655 -0
- package/docs/METRIC_CARD_API_REDESIGN.md +354 -0
- package/docs/PRIMITIVE_MAPPING.md +404 -0
- package/docs/PUBLISHING.md +593 -0
- package/docs/REGISTRY_SUMMARY.md +96 -0
- package/docs/REGISTRY_USAGE.md +339 -0
- package/docs/SECURITY.md +117 -0
- package/docs/THEME_CREATION_GUIDE.md +455 -0
- package/docs/TOKEN_ARCHITECTURE.md +365 -0
- package/docs/announcements/v1.0.5-devto.md +363 -0
- package/docs/announcements/v1.0.5-reddit.md +115 -0
- package/docs/announcements/v1.0.5-twitter.md +70 -0
- package/docs/api/README.md +725 -0
- package/docs/api/functions/ThemeProvider.md +21 -0
- package/docs/api/functions/cn.md +27 -0
- package/docs/api/functions/getNextTheme.md +21 -0
- package/docs/api/functions/getThemeConfig.md +21 -0
- package/docs/api/functions/useFocus.md +53 -0
- package/docs/api/functions/useHover.md +47 -0
- package/docs/api/functions/useResponsive.md +31 -0
- package/docs/api/functions/useTheme.md +15 -0
- package/docs/api/functions/useWallpaperTint.md +36 -0
- package/docs/api/globals.md +139 -0
- package/docs/api/interfaces/AlertGlassProps.md +131 -0
- package/docs/api/interfaces/AvatarGlassProps.md +114 -0
- package/docs/api/interfaces/BadgeGlassProps.md +118 -0
- package/docs/api/interfaces/ButtonGlassProps.md +179 -0
- package/docs/api/interfaces/CheckboxGlassProps.md +125 -0
- package/docs/api/interfaces/DropdownGlassProps.md +123 -0
- package/docs/api/interfaces/DropdownItem.md +53 -0
- package/docs/api/interfaces/GlassCardProps.md +151 -0
- package/docs/api/interfaces/InputGlassProps.md +169 -0
- package/docs/api/interfaces/NotificationGlassProps.md +67 -0
- package/docs/api/interfaces/ProgressGlassProps.md +49 -0
- package/docs/api/interfaces/SkeletonGlassProps.md +41 -0
- package/docs/api/interfaces/SliderGlassProps.md +107 -0
- package/docs/api/interfaces/TabItem.md +25 -0
- package/docs/api/interfaces/ThemeConfig.md +25 -0
- package/docs/api/interfaces/ThemeContextValue.md +47 -0
- package/docs/api/interfaces/ToggleGlassProps.md +59 -0
- package/docs/api/interfaces/TooltipGlassProps.md +119 -0
- package/docs/api/type-aliases/AlertType.md +11 -0
- package/docs/api/type-aliases/AlertVariant.md +11 -0
- package/docs/api/type-aliases/AvatarSize.md +11 -0
- package/docs/api/type-aliases/AvatarStatus.md +13 -0
- package/docs/api/type-aliases/BadgeSize.md +11 -0
- package/docs/api/type-aliases/BadgeVariant.md +11 -0
- package/docs/api/type-aliases/ButtonGlassSize.md +11 -0
- package/docs/api/type-aliases/ButtonGlassVariant.md +11 -0
- package/docs/api/type-aliases/DropdownAlign.md +11 -0
- package/docs/api/type-aliases/GlowType.md +11 -0
- package/docs/api/type-aliases/InputGlassSize.md +11 -0
- package/docs/api/type-aliases/IntensityType.md +11 -0
- package/docs/api/type-aliases/ModalSize.md +11 -0
- package/docs/api/type-aliases/NotificationType.md +11 -0
- package/docs/api/type-aliases/PaddingType.md +11 -0
- package/docs/api/type-aliases/ProgressGradient.md +11 -0
- package/docs/api/type-aliases/ProgressSize.md +11 -0
- package/docs/api/type-aliases/SkeletonVariant.md +11 -0
- package/docs/api/type-aliases/Theme.md +11 -0
- package/docs/api/type-aliases/ToggleGlassSize.md +11 -0
- package/docs/api/type-aliases/TooltipPosition.md +11 -0
- package/docs/api/variables/AICardGlass.md +11 -0
- package/docs/api/variables/AlertGlass.md +11 -0
- package/docs/api/variables/AvatarGlass.md +11 -0
- package/docs/api/variables/BadgeGlass.md +11 -0
- package/docs/api/variables/BaseProgressGlass.md +11 -0
- package/docs/api/variables/ButtonGlass.md +11 -0
- package/docs/api/variables/CareerStatsGlass.md +11 -0
- package/docs/api/variables/CareerStatsHeaderGlass.md +11 -0
- package/docs/api/variables/CheckboxGlass.md +11 -0
- package/docs/api/variables/CircularMetricGlass.md +22 -0
- package/docs/api/variables/CircularProgressGlass.md +11 -0
- package/docs/api/variables/ComboBoxGlass.md +27 -0
- package/docs/api/variables/ContributionMetricsGlass.md +11 -0
- package/docs/api/variables/DropdownGlass.md +11 -0
- package/docs/api/variables/ExpandableHeaderGlass.md +11 -0
- package/docs/api/variables/FlagAlertGlass.md +11 -0
- package/docs/api/variables/FlagsSectionGlass.md +11 -0
- package/docs/api/variables/FormFieldWrapper.md +44 -0
- package/docs/api/variables/GlassCard.md +11 -0
- package/docs/api/variables/HeaderBrandingGlass.md +11 -0
- package/docs/api/variables/HeaderNavGlass.md +11 -0
- package/docs/api/variables/IconButtonGlass.md +11 -0
- package/docs/api/variables/InputGlass.md +11 -0
- package/docs/api/variables/InteractiveCard.md +45 -0
- package/docs/api/variables/LanguageBarGlass.md +11 -0
- package/docs/api/variables/MetricCardGlass.md +11 -0
- package/docs/api/variables/MetricsGridGlass.md +11 -0
- package/docs/api/variables/ModalGlass.md +72 -0
- package/docs/api/variables/NotificationGlass.md +11 -0
- package/docs/api/variables/PopoverGlass.md +11 -0
- package/docs/api/variables/ProfileAvatarGlass.md +11 -0
- package/docs/api/variables/ProfileHeaderGlass.md +11 -0
- package/docs/api/variables/ProgressGlass.md +11 -0
- package/docs/api/variables/ProjectsListGlass.md +11 -0
- package/docs/api/variables/RainbowProgressGlass.md +11 -0
- package/docs/api/variables/RepositoryCardGlass.md +11 -0
- package/docs/api/variables/RepositoryHeaderGlass.md +11 -0
- package/docs/api/variables/RepositoryMetadataGlass.md +11 -0
- package/docs/api/variables/SearchBoxGlass.md +11 -0
- package/docs/api/variables/SegmentedControlGlass.md +11 -0
- package/docs/api/variables/SkeletonGlass.md +11 -0
- package/docs/api/variables/SliderGlass.md +11 -0
- package/docs/api/variables/SortDropdownGlass.md +11 -0
- package/docs/api/variables/StatItemGlass.md +11 -0
- package/docs/api/variables/StatusIndicatorGlass.md +11 -0
- package/docs/api/variables/THEMES.md +11 -0
- package/docs/api/variables/THEME_CONFIG.md +11 -0
- package/docs/api/variables/TabsGlass.md +52 -0
- package/docs/api/variables/ThemeToggleGlass.md +11 -0
- package/docs/api/variables/ToggleGlass.md +11 -0
- package/docs/api/variables/TooltipGlass.md +11 -0
- package/docs/api/variables/TouchTarget.md +35 -0
- package/docs/api/variables/TrustScoreCardGlass.md +11 -0
- package/docs/api/variables/TrustScoreDisplayGlass.md +11 -0
- package/docs/api/variables/UserInfoGlass.md +11 -0
- package/docs/api/variables/UserStatsLineGlass.md +11 -0
- package/docs/api/variables/YearCardGlass.md +11 -0
- package/docs/api/variables/alertVariants.md +21 -0
- package/docs/api/variables/avatarSizes.md +21 -0
- package/docs/api/variables/badgeVariants.md +21 -0
- package/docs/api/variables/buttonGlassVariants.md +21 -0
- package/docs/api/variables/cardIntensity.md +21 -0
- package/docs/api/variables/dropdownAlign.md +21 -0
- package/docs/api/variables/inputVariants.md +21 -0
- package/docs/api/variables/modalSizes.md +21 -0
- package/docs/api/variables/notificationVariants.md +21 -0
- package/docs/api/variables/progressSizes.md +21 -0
- package/docs/api/variables/shadcnAlertVariants.md +21 -0
- package/docs/api/variables/shadcnBadgeVariants.md +21 -0
- package/docs/api/variables/shadcnButtonVariants.md +21 -0
- package/docs/api/variables/skeletonVariants.md +21 -0
- package/docs/api/variables/statusSizes.md +21 -0
- package/docs/api/variables/toggleSizes.md +21 -0
- package/docs/api/variables/tooltipPositions.md +21 -0
- package/docs/design-system/UI_DESIGN.md +628 -0
- package/docs/migration/CSS_VARIABLES_MIGRATION_2.0.md +325 -0
- package/docs/migration/modal-glass-compound-api.md +458 -0
- package/docs/migration/select-to-combobox.md +386 -0
- package/docs/migration/tabs-glass-compound-api.md +579 -0
- package/docs/technical/DEPENDENCIES.md +291 -0
- package/docs/visual-testing-guide.md +324 -0
- package/package.json +16 -3
- package/dist/trust-score-card-glass-CNcQveNY.cjs +0 -8231
- package/dist/trust-score-card-glass-CNcQveNY.cjs.map +0 -1
- package/dist/trust-score-card-glass-CowcDyxH.js +0 -7795
- package/dist/trust-score-card-glass-CowcDyxH.js.map +0 -1
package/dist/r/tabs-glass.json
CHANGED
|
@@ -5,20 +5,15 @@
|
|
|
5
5
|
"title": "Tabs Glass",
|
|
6
6
|
"description": "TabsGlass Component (Compound API only)",
|
|
7
7
|
"dependencies": [],
|
|
8
|
-
"registryDependencies": [
|
|
9
|
-
"cn",
|
|
10
|
-
"use-focus"
|
|
11
|
-
],
|
|
8
|
+
"registryDependencies": ["cn", "use-focus"],
|
|
12
9
|
"files": [
|
|
13
10
|
{
|
|
14
11
|
"path": "components/glass/ui/tabs-glass.tsx",
|
|
15
12
|
"type": "registry:component",
|
|
16
|
-
"content": "/* eslint-disable react-refresh/only-export-components */\n/**\n * TabsGlass Component (Compound API only)\n *\n * Glass-themed tab navigation with:\n * - Theme-aware styling (glass/light/aurora)\n * - Active tab indicator\n * - Smooth transitions\n * - Compound component API for advanced composition\n *\n * @example\n * ```tsx\n * <TabsGlass.Root value={activeTab} onValueChange={setActiveTab}>\n * <TabsGlass.List>\n * <TabsGlass.Trigger value=\"overview\">Overview</TabsGlass.Trigger>\n * <TabsGlass.Trigger value=\"analytics\">Analytics</TabsGlass.Trigger>\n * <TabsGlass.Trigger value=\"settings\">Settings</TabsGlass.Trigger>\n * </TabsGlass.List>\n * <TabsGlass.Content value=\"overview\">\n * Overview content\n * </TabsGlass.Content>\n * <TabsGlass.Content value=\"analytics\">\n * Analytics content\n * </TabsGlass.Content>\n * <TabsGlass.Content value=\"settings\">\n * Settings content\n * </TabsGlass.Content>\n * </TabsGlass.Root>\n * ```\n *\n * @since v1.0.0 - Legacy API removed (tabs/activeTab/onChange props)\n */\n\nimport {\n forwardRef,\n createContext,\n useContext,\n type CSSProperties,\n type FC,\n type ReactNode,\n} from 'react';\nimport { cn } from '@/lib/utils';\nimport { useFocus } from '@/lib/hooks/use-focus';\nimport '@/glass-theme.css';\n\n// ========================================\n// TYPES\n// ========================================\n\nexport interface TabItem {\n readonly id: string;\n readonly label: string;\n}\n\n// ========================================\n// CONTEXT FOR COMPOUND COMPONENTS\n// ========================================\n\ninterface TabsContextValue {\n value: string;\n onValueChange?: (value: string) => void;\n}\n\nconst TabsContext = createContext<TabsContextValue | null>(null);\n\nconst useTabsContext = () => {\n const context = useContext(TabsContext);\n if (!context) {\n throw new Error('Tabs compound components must be used within TabsGlass.Root');\n }\n return context;\n};\n\n// ========================================\n// COMPOUND COMPONENT: ROOT\n// ========================================\n\n/**\n * Props for TabsGlass.Root component\n *\n * Root component that manages tab state and provides context for child components.\n * Features accessible keyboard navigation and ARIA attributes.\n *\n * @accessibility\n * - **Keyboard Navigation:** Arrow keys navigate between tabs, Tab moves to tab panel content (WCAG 2.1.1)\n * - **Focus Management:** Visible focus ring on active tab using `--focus-glow` CSS variable (WCAG 2.4.7)\n * - **Screen Readers:** Uses `role=\"tablist\"`, `role=\"tab\"`, `role=\"tabpanel\"` for proper tab semantics (WCAG 4.1.3)\n * - **ARIA Attributes:** Tabs marked with `aria-selected`, panels with `aria-hidden` for state announcement\n * - **Active State:** Visual indicator (underline) plus color change for multi-modal feedback\n * - **Touch Targets:** Tab triggers meet minimum 44x44px touch target (WCAG 2.5.5)\n * - **Color Contrast:** Active and inactive tab text meet WCAG AA contrast ratio 4.5:1\n * - **Motion:** Transitions and indicator animations respect `prefers-reduced-motion` settings\n *\n * @example\n * ```tsx\n * // Basic tabs\n * <TabsGlass.Root value={activeTab} onValueChange={setActiveTab}>\n * <TabsGlass.List>\n * <TabsGlass.Trigger value=\"overview\">Overview</TabsGlass.Trigger>\n * <TabsGlass.Trigger value=\"analytics\">Analytics</TabsGlass.Trigger>\n * <TabsGlass.Trigger value=\"settings\">Settings</TabsGlass.Trigger>\n * </TabsGlass.List>\n * <TabsGlass.Content value=\"overview\">\n * <h2>Overview</h2>\n * <p>Overview content here</p>\n * </TabsGlass.Content>\n * <TabsGlass.Content value=\"analytics\">\n * <h2>Analytics</h2>\n * <p>Analytics content here</p>\n * </TabsGlass.Content>\n * <TabsGlass.Content value=\"settings\">\n * <h2>Settings</h2>\n * <p>Settings content here</p>\n * </TabsGlass.Content>\n * </TabsGlass.Root>\n *\n * // Tabs with icons (ensure accessible labels)\n * <TabsGlass.Root value={activeTab} onValueChange={setActiveTab}>\n * <TabsGlass.List>\n * <TabsGlass.Trigger value=\"home\" aria-label=\"Home dashboard\">\n * <Home className=\"w-4 h-4\" />\n * </TabsGlass.Trigger>\n * <TabsGlass.Trigger value=\"search\" aria-label=\"Search\">\n * <Search className=\"w-4 h-4\" />\n * </TabsGlass.Trigger>\n * </TabsGlass.List>\n * <TabsGlass.Content value=\"home\">Dashboard content</TabsGlass.Content>\n * <TabsGlass.Content value=\"search\">Search content</TabsGlass.Content>\n * </TabsGlass.Root>\n *\n * // Disabled tab (announced to screen readers)\n * <TabsGlass.Root value={activeTab} onValueChange={setActiveTab}>\n * <TabsGlass.List>\n * <TabsGlass.Trigger value=\"tab1\">Available Tab</TabsGlass.Trigger>\n * <TabsGlass.Trigger value=\"tab2\" disabled>\n * Locked Tab\n * </TabsGlass.Trigger>\n * </TabsGlass.List>\n * <TabsGlass.Content value=\"tab1\">Content 1</TabsGlass.Content>\n * </TabsGlass.Root>\n *\n * // Form tabs with proper focus management\n * <TabsGlass.Root value={currentStep} onValueChange={setCurrentStep}>\n * <TabsGlass.List aria-label=\"Registration steps\">\n * <TabsGlass.Trigger value=\"account\">Account Info</TabsGlass.Trigger>\n * <TabsGlass.Trigger value=\"profile\">Profile Details</TabsGlass.Trigger>\n * <TabsGlass.Trigger value=\"confirm\">Confirmation</TabsGlass.Trigger>\n * </TabsGlass.List>\n * <TabsGlass.Content value=\"account\">\n * <InputGlass label=\"Email\" type=\"email\" />\n * </TabsGlass.Content>\n * <TabsGlass.Content value=\"profile\">\n * <InputGlass label=\"Name\" />\n * </TabsGlass.Content>\n * <TabsGlass.Content value=\"confirm\">\n * <p>Review your information</p>\n * </TabsGlass.Content>\n * </TabsGlass.Root>\n * ```\n */\ninterface TabsRootProps {\n /** Current active tab value */\n value: string;\n /** Callback when tab value changes */\n onValueChange?: (value: string) => void;\n /** Child components */\n children: ReactNode;\n /** Optional className for container */\n className?: string;\n}\n\nconst TabsRoot: FC<TabsRootProps> = ({ value, onValueChange, children, className }) => {\n return (\n <TabsContext.Provider value={{ value, onValueChange }}>\n <div className={cn('tabs-glass-root', className)}>{children}</div>\n </TabsContext.Provider>\n );\n};\n\n// ========================================\n// COMPOUND COMPONENT: LIST\n// ========================================\n\ninterface TabsListProps extends React.HTMLAttributes<HTMLDivElement> {\n children: ReactNode;\n className?: string;\n}\n\nconst TabsList = forwardRef<HTMLDivElement, TabsListProps>(\n ({ children, className, ...props }, ref) => {\n const containerStyles: CSSProperties = {\n background: 'var(--tab-container-bg)',\n border: '1px solid var(--tab-container-border)',\n };\n\n return (\n <div\n ref={ref}\n className={cn('inline-flex gap-0.5 md:gap-1 p-0.5 md:p-1 rounded-xl', className)}\n style={containerStyles}\n role=\"tablist\"\n {...props}\n >\n {children}\n </div>\n );\n }\n);\n\nTabsList.displayName = 'TabsList';\n\n// ========================================\n// COMPOUND COMPONENT: TRIGGER\n// ========================================\n\ninterface TabsTriggerProps {\n /** Value of this tab */\n value: string;\n /** Tab label/content */\n children: ReactNode;\n /** Optional className */\n className?: string;\n /** Disabled state */\n disabled?: boolean;\n}\n\nconst TabsTrigger = forwardRef<HTMLButtonElement, TabsTriggerProps>(\n ({ value, children, className, disabled }, ref) => {\n const { value: activeValue, onValueChange } = useTabsContext();\n const { isFocusVisible, focusProps } = useFocus({ focusVisible: true });\n const isActive = activeValue === value;\n\n const tabStyles: CSSProperties = {\n background: isActive ? 'var(--tab-active-bg)' : 'var(--tab-bg)',\n color: isActive ? 'var(--tab-active-text)' : 'var(--text-secondary)',\n boxShadow: isFocusVisible && !disabled ? 'var(--focus-glow)' : 'none',\n };\n\n return (\n <button\n ref={ref}\n type=\"button\"\n role=\"tab\"\n aria-selected={isActive}\n disabled={disabled}\n className={cn(\n 'relative px-2.5 py-1.5 md:px-4 md:py-2 rounded-lg text-xs md:text-sm font-medium transition-[background-color,color,opacity] duration-300',\n disabled && 'opacity-50 cursor-not-allowed',\n className\n )}\n style={tabStyles}\n onClick={() => !disabled && onValueChange?.(value)}\n onFocus={focusProps.onFocus}\n onBlur={focusProps.onBlur}\n >\n {children}\n {isActive && (\n <div\n className=\"absolute bottom-0 left-1/2 -translate-x-1/2 w-6 md:w-8 h-0.5 rounded-full\"\n style={{ background: 'var(--tab-indicator)' }}\n />\n )}\n </button>\n );\n }\n);\n\nTabsTrigger.displayName = 'TabsTrigger';\n\n// ========================================\n// COMPOUND COMPONENT: CONTENT\n// ========================================\n\ninterface TabsContentProps {\n /** Value of the tab this content belongs to */\n value: string;\n /** Content to display when tab is active */\n children: ReactNode;\n /** Optional className */\n className?: string;\n}\n\nconst TabsContent: FC<TabsContentProps> = ({ value, children, className }) => {\n const { value: activeValue } = useTabsContext();\n const isActive = activeValue === value;\n\n if (!isActive) return null;\n\n return (\n <div\n role=\"tabpanel\"\n aria-hidden={!isActive}\n className={cn('animate-in fade-in-0 duration-200', className)}\n >\n {children}\n </div>\n );\n};\n\n// ========================================\n// EXPORT COMPOUND COMPONENT (v1.0.0+)\n// ========================================\n\n/**\n * TabsGlass - Compound Component API\n *\n * @example\n * ```tsx\n * <TabsGlass.Root value={activeTab} onValueChange={setActiveTab}>\n * <TabsGlass.List>\n * <TabsGlass.Trigger value=\"tab1\">Overview</TabsGlass.Trigger>\n * <TabsGlass.Trigger value=\"tab2\">Analytics</TabsGlass.Trigger>\n * </TabsGlass.List>\n * <TabsGlass.Content value=\"tab1\">\n * <p>Overview content</p>\n * </TabsGlass.Content>\n * <TabsGlass.Content value=\"tab2\">\n * <p>Analytics content</p>\n * </TabsGlass.Content>\n * </TabsGlass.Root>\n * ```\n *\n * @since v1.0.0 - Legacy API removed (tabs/activeTab/onChange props)\n */\nexport const TabsGlass = {\n Root: TabsRoot,\n List: TabsList,\n Trigger: TabsTrigger,\n Content: TabsContent,\n};\n"
|
|
13
|
+
"content": "/* eslint-disable react-refresh/only-export-components */\n/**\n * TabsGlass Component (Compound API only)\n *\n * Glass-themed tab navigation with:\n * - Theme-aware styling (glass/light/aurora)\n * - Active tab indicator\n * - Smooth transitions\n * - Compound component API for advanced composition\n *\n * @example\n * ```tsx\n * <TabsGlass.Root value={activeTab} onValueChange={setActiveTab}>\n * <TabsGlass.List>\n * <TabsGlass.Trigger value=\"overview\">Overview</TabsGlass.Trigger>\n * <TabsGlass.Trigger value=\"analytics\">Analytics</TabsGlass.Trigger>\n * <TabsGlass.Trigger value=\"settings\">Settings</TabsGlass.Trigger>\n * </TabsGlass.List>\n * <TabsGlass.Content value=\"overview\">\n * Overview content\n * </TabsGlass.Content>\n * <TabsGlass.Content value=\"analytics\">\n * Analytics content\n * </TabsGlass.Content>\n * <TabsGlass.Content value=\"settings\">\n * Settings content\n * </TabsGlass.Content>\n * </TabsGlass.Root>\n * ```\n *\n * @since v1.0.0 - Legacy API removed (tabs/activeTab/onChange props)\n */\n\nimport {\n forwardRef,\n createContext,\n useContext,\n type CSSProperties,\n type FC,\n type ReactNode,\n} from 'react';\nimport { cn } from '@/lib/utils';\nimport { useFocus } from '@/lib/hooks/use-focus';\nimport '@/glass-theme.css';\n\n// ========================================\n// TYPES\n// ========================================\n\nexport interface TabItem {\n readonly id: string;\n readonly label: string;\n}\n\n// ========================================\n// CONTEXT FOR COMPOUND COMPONENTS\n// ========================================\n\ninterface TabsContextValue {\n value: string;\n onValueChange?: (value: string) => void;\n}\n\nconst TabsContext = createContext<TabsContextValue | null>(null);\n\nconst useTabsContext = () => {\n const context = useContext(TabsContext);\n if (!context) {\n throw new Error('Tabs compound components must be used within TabsGlass.Root');\n }\n return context;\n};\n\n// ========================================\n// COMPOUND COMPONENT: ROOT\n// ========================================\n\n/**\n * Props for TabsGlass.Root component\n *\n * Root component that manages tab state and provides context for child components.\n * Features accessible keyboard navigation and ARIA attributes.\n *\n * @accessibility\n * - **Keyboard Navigation:** Arrow keys navigate between tabs, Tab moves to tab panel content (WCAG 2.1.1)\n * - **Focus Management:** Visible focus ring on active tab using `--focus-glow` CSS variable (WCAG 2.4.7)\n * - **Screen Readers:** Uses `role=\"tablist\"`, `role=\"tab\"`, `role=\"tabpanel\"` for proper tab semantics (WCAG 4.1.3)\n * - **ARIA Attributes:** Tabs marked with `aria-selected`, panels with `aria-hidden` for state announcement\n * - **Active State:** Visual indicator (underline) plus color change for multi-modal feedback\n * - **Touch Targets:** Tab triggers meet minimum 44x44px touch target (WCAG 2.5.5)\n * - **Color Contrast:** Active and inactive tab text meet WCAG AA contrast ratio 4.5:1\n * - **Motion:** Transitions and indicator animations respect `prefers-reduced-motion` settings\n *\n * @example\n * ```tsx\n * // Basic tabs\n * <TabsGlass.Root value={activeTab} onValueChange={setActiveTab}>\n * <TabsGlass.List>\n * <TabsGlass.Trigger value=\"overview\">Overview</TabsGlass.Trigger>\n * <TabsGlass.Trigger value=\"analytics\">Analytics</TabsGlass.Trigger>\n * <TabsGlass.Trigger value=\"settings\">Settings</TabsGlass.Trigger>\n * </TabsGlass.List>\n * <TabsGlass.Content value=\"overview\">\n * <h2>Overview</h2>\n * <p>Overview content here</p>\n * </TabsGlass.Content>\n * <TabsGlass.Content value=\"analytics\">\n * <h2>Analytics</h2>\n * <p>Analytics content here</p>\n * </TabsGlass.Content>\n * <TabsGlass.Content value=\"settings\">\n * <h2>Settings</h2>\n * <p>Settings content here</p>\n * </TabsGlass.Content>\n * </TabsGlass.Root>\n *\n * // Tabs with icons (ensure accessible labels)\n * <TabsGlass.Root value={activeTab} onValueChange={setActiveTab}>\n * <TabsGlass.List>\n * <TabsGlass.Trigger value=\"home\" aria-label=\"Home dashboard\">\n * <Home className=\"w-4 h-4\" />\n * </TabsGlass.Trigger>\n * <TabsGlass.Trigger value=\"search\" aria-label=\"Search\">\n * <Search className=\"w-4 h-4\" />\n * </TabsGlass.Trigger>\n * </TabsGlass.List>\n * <TabsGlass.Content value=\"home\">Dashboard content</TabsGlass.Content>\n * <TabsGlass.Content value=\"search\">Search content</TabsGlass.Content>\n * </TabsGlass.Root>\n *\n * // Disabled tab (announced to screen readers)\n * <TabsGlass.Root value={activeTab} onValueChange={setActiveTab}>\n * <TabsGlass.List>\n * <TabsGlass.Trigger value=\"tab1\">Available Tab</TabsGlass.Trigger>\n * <TabsGlass.Trigger value=\"tab2\" disabled>\n * Locked Tab\n * </TabsGlass.Trigger>\n * </TabsGlass.List>\n * <TabsGlass.Content value=\"tab1\">Content 1</TabsGlass.Content>\n * </TabsGlass.Root>\n *\n * // Form tabs with proper focus management\n * <TabsGlass.Root value={currentStep} onValueChange={setCurrentStep}>\n * <TabsGlass.List aria-label=\"Registration steps\">\n * <TabsGlass.Trigger value=\"account\">Account Info</TabsGlass.Trigger>\n * <TabsGlass.Trigger value=\"profile\">Profile Details</TabsGlass.Trigger>\n * <TabsGlass.Trigger value=\"confirm\">Confirmation</TabsGlass.Trigger>\n * </TabsGlass.List>\n * <TabsGlass.Content value=\"account\">\n * <InputGlass label=\"Email\" type=\"email\" />\n * </TabsGlass.Content>\n * <TabsGlass.Content value=\"profile\">\n * <InputGlass label=\"Name\" />\n * </TabsGlass.Content>\n * <TabsGlass.Content value=\"confirm\">\n * <p>Review your information</p>\n * </TabsGlass.Content>\n * </TabsGlass.Root>\n * ```\n */\ninterface TabsRootProps {\n /** Current active tab value */\n value: string;\n /** Callback when tab value changes */\n onValueChange?: (value: string) => void;\n /** Child components */\n children: ReactNode;\n /** Optional className for container */\n className?: string;\n}\n\nconst TabsRoot: FC<TabsRootProps> = ({ value, onValueChange, children, className }) => {\n return (\n <TabsContext.Provider value={{ value, onValueChange }}>\n <div className={cn('tabs-glass-root', className)}>{children}</div>\n </TabsContext.Provider>\n );\n};\n\n// ========================================\n// COMPOUND COMPONENT: LIST\n// ========================================\n\ninterface TabsListProps extends React.HTMLAttributes<HTMLDivElement> {\n children: ReactNode;\n className?: string;\n}\n\nconst TabsList = forwardRef<HTMLDivElement, TabsListProps>(\n ({ children, className, ...props }, ref) => {\n const containerStyles: CSSProperties = {\n background: 'var(--tab-container-bg)',\n border: '1px solid var(--tab-container-border)',\n };\n\n return (\n <div\n ref={ref}\n className={cn('inline-flex gap-0.5 md:gap-1 p-0.5 md:p-1 rounded-xl', className)}\n style={containerStyles}\n role=\"tablist\"\n {...props}\n >\n {children}\n </div>\n );\n }\n);\n\nTabsList.displayName = 'TabsList';\n\n// ========================================\n// COMPOUND COMPONENT: TRIGGER\n// ========================================\n\ninterface TabsTriggerProps {\n /** Value of this tab */\n value: string;\n /** Tab label/content */\n children: ReactNode;\n /** Optional className */\n className?: string;\n /** Disabled state */\n disabled?: boolean;\n}\n\nconst TabsTrigger = forwardRef<HTMLButtonElement, TabsTriggerProps>(\n ({ value, children, className, disabled }, ref) => {\n const { value: activeValue, onValueChange } = useTabsContext();\n const { isFocusVisible, focusProps } = useFocus({ focusVisible: true });\n const isActive = activeValue === value;\n\n const tabStyles: CSSProperties = {\n background: isActive ? 'var(--tab-active-bg)' : 'var(--tab-bg)',\n color: isActive ? 'var(--tab-active-text)' : 'var(--text-secondary)',\n boxShadow: isFocusVisible && !disabled ? 'var(--focus-glow)' : 'none',\n };\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLButtonElement>) => {\n if (disabled) return;\n\n const tablist = e.currentTarget.closest('[role=\"tablist\"]');\n if (!tablist) return;\n\n const tabs = Array.from(\n tablist.querySelectorAll('[role=\"tab\"]:not([disabled])')\n ) as HTMLButtonElement[];\n const currentIndex = tabs.indexOf(e.currentTarget);\n\n let nextIndex = currentIndex;\n\n switch (e.key) {\n case 'ArrowRight':\n e.preventDefault();\n nextIndex = currentIndex + 1 >= tabs.length ? 0 : currentIndex + 1;\n break;\n case 'ArrowLeft':\n e.preventDefault();\n nextIndex = currentIndex - 1 < 0 ? tabs.length - 1 : currentIndex - 1;\n break;\n case 'Home':\n e.preventDefault();\n nextIndex = 0;\n break;\n case 'End':\n e.preventDefault();\n nextIndex = tabs.length - 1;\n break;\n default:\n return;\n }\n\n const nextTab = tabs[nextIndex];\n if (nextTab) {\n nextTab.focus();\n // Get the value from the button's data attribute or find it in context\n const nextValue = nextTab.getAttribute('data-value');\n if (nextValue && onValueChange) {\n onValueChange(nextValue);\n }\n }\n };\n\n return (\n <button\n ref={ref}\n type=\"button\"\n role=\"tab\"\n aria-selected={isActive}\n disabled={disabled}\n data-value={value}\n className={cn(\n 'relative px-2.5 py-1.5 md:px-4 md:py-2 rounded-lg text-xs md:text-sm font-medium transition-[background-color,color,opacity] duration-300',\n disabled && 'opacity-50 cursor-not-allowed',\n className\n )}\n style={tabStyles}\n onClick={() => !disabled && onValueChange?.(value)}\n onKeyDown={handleKeyDown}\n onFocus={focusProps.onFocus}\n onBlur={focusProps.onBlur}\n >\n {children}\n {isActive && (\n <div\n className=\"absolute bottom-0 left-1/2 -translate-x-1/2 w-6 md:w-8 h-0.5 rounded-full\"\n style={{ background: 'var(--tab-indicator)' }}\n />\n )}\n </button>\n );\n }\n);\n\nTabsTrigger.displayName = 'TabsTrigger';\n\n// ========================================\n// COMPOUND COMPONENT: CONTENT\n// ========================================\n\ninterface TabsContentProps {\n /** Value of the tab this content belongs to */\n value: string;\n /** Content to display when tab is active */\n children: ReactNode;\n /** Optional className */\n className?: string;\n}\n\nconst TabsContent: FC<TabsContentProps> = ({ value, children, className }) => {\n const { value: activeValue } = useTabsContext();\n const isActive = activeValue === value;\n\n if (!isActive) return null;\n\n return (\n <div\n role=\"tabpanel\"\n aria-hidden={!isActive}\n className={cn('animate-in fade-in-0 duration-200', className)}\n >\n {children}\n </div>\n );\n};\n\n// ========================================\n// EXPORT COMPOUND COMPONENT (v1.0.0+)\n// ========================================\n\n/**\n * TabsGlass - Compound Component API\n *\n * @example\n * ```tsx\n * <TabsGlass.Root value={activeTab} onValueChange={setActiveTab}>\n * <TabsGlass.List>\n * <TabsGlass.Trigger value=\"tab1\">Overview</TabsGlass.Trigger>\n * <TabsGlass.Trigger value=\"tab2\">Analytics</TabsGlass.Trigger>\n * </TabsGlass.List>\n * <TabsGlass.Content value=\"tab1\">\n * <p>Overview content</p>\n * </TabsGlass.Content>\n * <TabsGlass.Content value=\"tab2\">\n * <p>Analytics content</p>\n * </TabsGlass.Content>\n * </TabsGlass.Root>\n * ```\n *\n * @since v1.0.0 - Legacy API removed (tabs/activeTab/onChange props)\n */\nexport const TabsGlass = {\n Root: TabsRoot,\n List: TabsList,\n Trigger: TabsTrigger,\n Content: TabsContent,\n};\n"
|
|
17
14
|
}
|
|
18
15
|
],
|
|
19
|
-
"categories": [
|
|
20
|
-
"ui"
|
|
21
|
-
],
|
|
16
|
+
"categories": ["ui"],
|
|
22
17
|
"cssVars": {
|
|
23
18
|
"light": {
|
|
24
19
|
"--glass-bg": "rgba(255, 255, 255, 0.1)",
|
|
@@ -35,4 +30,4 @@
|
|
|
35
30
|
"--blur-lg": "24px"
|
|
36
31
|
}
|
|
37
32
|
}
|
|
38
|
-
}
|
|
33
|
+
}
|
|
@@ -4,21 +4,14 @@
|
|
|
4
4
|
"type": "registry:block",
|
|
5
5
|
"title": "Trust Score Card Glass",
|
|
6
6
|
"description": "Trust Score Card Glass component with glass effects",
|
|
7
|
-
"dependencies": [
|
|
8
|
-
|
|
9
|
-
"react"
|
|
10
|
-
],
|
|
11
|
-
"registryDependencies": [
|
|
12
|
-
"cn"
|
|
13
|
-
],
|
|
7
|
+
"dependencies": ["lucide-react", "react"],
|
|
8
|
+
"registryDependencies": ["cn"],
|
|
14
9
|
"files": [
|
|
15
10
|
{
|
|
16
11
|
"path": "components/glass/sections/trust-score-card-glass.tsx",
|
|
17
12
|
"type": "registry:component",
|
|
18
|
-
"content": "// ========================================\n// TRUST SCORE CARD GLASS COMPONENT\n// Overall trust score display with metrics\n// ========================================\n\nimport { forwardRef } from
|
|
13
|
+
"content": "// ========================================\n// TRUST SCORE CARD GLASS COMPONENT\n// Overall trust score display with metrics\n// ========================================\n\nimport { forwardRef } from 'react';\nimport { Target } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport { GlassCard } from '../ui/glass-card';\nimport { RainbowProgressGlass } from '../specialized/rainbow-progress-glass';\nimport { MetricCardGlass, type MetricVariant } from '../composite/metric-card-glass';\nimport '@/glass-theme.css';\n\nexport interface MetricData {\n readonly title: string;\n readonly value: string | number;\n readonly variant: MetricVariant;\n}\n\nexport interface TrustScoreCardGlassProps extends React.HTMLAttributes<HTMLDivElement> {\n readonly score?: number;\n readonly metrics?: readonly MetricData[];\n}\n\nexport const TrustScoreCardGlass = forwardRef<HTMLDivElement, TrustScoreCardGlassProps>(\n ({ score = 72, metrics = [], className, ...props }, ref) => {\n return (\n <GlassCard\n ref={ref}\n className={cn('p-4 md:p-5', className)}\n intensity=\"strong\"\n glow=\"cyan\"\n hover={false}\n {...props}\n >\n <div className=\"flex items-center justify-between mb-3 md:mb-4\">\n <h2\n className=\"font-semibold flex items-center gap-1.5 md:gap-2 text-base md:text-lg\"\n style={{ color: 'var(--text-primary)' }}\n >\n <Target className=\"w-4 h-4 md:w-5 md:h-5\" style={{ color: 'var(--text-accent)' }} />\n Overall Trust Score\n </h2>\n <div className=\"flex items-center gap-1.5 md:gap-2 animate-[score-pulse_2s_ease-in-out_infinite]\">\n <span className=\"text-3xl md:text-4xl font-bold bg-linear-to-r from-amber-400 via-emerald-400 to-cyan-400 bg-clip-text text-transparent\">\n {score}\n </span>\n <span className=\"text-lg md:text-xl\" style={{ color: 'var(--text-muted)' }}>\n / 100\n </span>\n </div>\n </div>\n <RainbowProgressGlass value={score} size=\"lg\" showGlow />\n {metrics.length > 0 && (\n <div className=\"grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-3 md:gap-4 mt-4 md:mt-5\">\n {metrics.map((m) => (\n <MetricCardGlass key={m.title} title={m.title} value={m.value} variant={m.variant} />\n ))}\n </div>\n )}\n </GlassCard>\n );\n }\n);\n\nTrustScoreCardGlass.displayName = 'TrustScoreCardGlass';\n"
|
|
19
14
|
}
|
|
20
15
|
],
|
|
21
|
-
"categories": [
|
|
22
|
-
|
|
23
|
-
]
|
|
24
|
-
}
|
|
16
|
+
"categories": ["sections"]
|
|
17
|
+
}
|
|
@@ -4,22 +4,14 @@
|
|
|
4
4
|
"type": "registry:block",
|
|
5
5
|
"title": "Year Card Glass",
|
|
6
6
|
"description": "Year Card Glass component with glass effects",
|
|
7
|
-
"dependencies": [
|
|
8
|
-
|
|
9
|
-
"react"
|
|
10
|
-
],
|
|
11
|
-
"registryDependencies": [
|
|
12
|
-
"cn",
|
|
13
|
-
"variants"
|
|
14
|
-
],
|
|
7
|
+
"dependencies": ["lucide-react", "react"],
|
|
8
|
+
"registryDependencies": ["cn", "variants"],
|
|
15
9
|
"files": [
|
|
16
10
|
{
|
|
17
11
|
"path": "components/glass/composite/year-card-glass.tsx",
|
|
18
12
|
"type": "registry:component",
|
|
19
|
-
"content": "// ========================================\n// YEAR CARD GLASS COMPONENT\n// Year card for career timeline\n// ========================================\n\nimport { forwardRef, type CSSProperties } from
|
|
13
|
+
"content": "// ========================================\n// YEAR CARD GLASS COMPONENT\n// Year card for career timeline / analytics\n// Domain-specific composite component following shadcn/ui patterns\n// ========================================\n\nimport { forwardRef, type CSSProperties, type ReactNode } from 'react';\nimport { ChevronDown, ChevronUp } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport { BadgeGlass } from '../ui/badge-glass';\nimport { ProgressGlass } from '../specialized/progress-glass';\nimport { SparklineGlass } from '../specialized/sparkline-glass';\nimport { ButtonGlass } from '../ui/button-glass';\nimport { InteractiveCard } from '../primitives';\nimport { InsightCardGlass } from '../atomic/insight-card-glass';\nimport type { ProgressGradient } from '@/lib/variants/progress-glass-variants';\nimport '@/glass-theme.css';\n\n// ========================================\n// TYPES\n// ========================================\n\nexport interface YearCardGlassInsight {\n readonly variant?: 'default' | 'tip' | 'highlight' | 'warning' | 'stat' | 'growth' | 'decline';\n readonly emoji?: string;\n readonly text: string;\n readonly detail?: string;\n}\n\nexport interface YearCardGlassStat {\n readonly label: string;\n readonly value: string | number;\n readonly icon?: ReactNode;\n}\n\nexport interface YearCardGlassProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Year to display (string or number) */\n readonly year: string | number;\n /** Emoji for the badge */\n readonly emoji: string;\n /** Label for the badge (e.g., \"Peak Year\") */\n readonly label: string;\n /** Primary metric value (e.g., \"629 commits\") */\n readonly commits: string;\n /** Progress percentage (0-100) */\n readonly progress: number;\n /** Whether the card is expanded to show details */\n readonly isExpanded?: boolean;\n /** Progress bar gradient color */\n readonly gradient?: ProgressGradient;\n /** Number of PRs (shown in expanded view) */\n readonly prs?: number;\n /** Number of repos (shown in expanded view) */\n readonly repos?: number;\n /** Callback when \"Show repos\" button is clicked */\n readonly onShowYear?: () => void;\n /** Monthly activity data for sparkline */\n readonly sparklineData?: readonly number[];\n /** Labels for sparkline (e.g., month names) */\n readonly sparklineLabels?: readonly string[];\n /** Insights to display in expanded view */\n readonly insights?: readonly YearCardGlassInsight[];\n /** Custom stats for expanded view (replaces default commits/prs/repos grid) */\n readonly stats?: readonly YearCardGlassStat[];\n /** Custom action button text */\n readonly actionLabel?: string;\n /** Show sparkline in collapsed view */\n readonly showSparklineCollapsed?: boolean;\n /** Custom value formatter for commits display */\n readonly valueFormatter?: (commits: string) => string;\n /** Additional content for expanded section */\n readonly children?: ReactNode;\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\nexport const YearCardGlass = forwardRef<HTMLDivElement, YearCardGlassProps>(\n (\n {\n year,\n emoji,\n label,\n commits,\n progress,\n isExpanded = false,\n gradient = 'blue',\n prs = 0,\n repos = 0,\n onShowYear,\n sparklineData,\n sparklineLabels,\n insights,\n stats,\n actionLabel,\n showSparklineCollapsed = true,\n valueFormatter,\n children,\n className,\n onClick,\n ...props\n },\n ref\n ) => {\n const expandedStyles: CSSProperties = {\n background: 'var(--expanded-bg)',\n borderColor: 'var(--expanded-border)',\n };\n\n const metricCardStyles: CSSProperties = {\n background: 'var(--card-bg)',\n borderColor: 'var(--card-border)',\n };\n\n // Format commits display\n const displayCommits = valueFormatter ? valueFormatter(commits) : commits;\n\n // Default stats if none provided\n const displayStats = stats || [\n { label: 'Commits', value: commits },\n { label: 'PRs', value: prs },\n { label: 'Repos', value: repos },\n ];\n\n // Default action label\n const buttonLabel = actionLabel || `Show repos from ${year}`;\n\n return (\n <InteractiveCard\n ref={ref}\n baseBg=\"var(--year-card-bg)\"\n hoverBg=\"var(--card-hover-bg)\"\n borderColor=\"var(--year-card-border)\"\n hoverGlow=\"var(--year-card-hover-glow)\"\n hoverLift\n rounded=\"rounded-xl\"\n className={cn('w-full max-w-2xl p-2.5 md:p-3 cursor-pointer', className)}\n onClick={onClick}\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onClick?.(e as unknown as React.MouseEvent<HTMLDivElement>);\n }\n }}\n aria-expanded={isExpanded}\n aria-label={`${year} year: ${label}, ${commits} commits. ${isExpanded ? 'Collapse' : 'Expand'} details`}\n {...props}\n >\n {/* Header */}\n <div className=\"flex items-center justify-between mb-1.5 md:mb-2\">\n <div className=\"flex items-center gap-1.5 md:gap-2\">\n <span\n className=\"font-semibold text-sm md:text-base\"\n style={{ color: 'var(--text-primary)' }}\n >\n {year}\n </span>\n <BadgeGlass>\n {emoji} {label}\n </BadgeGlass>\n </div>\n <span\n className=\"text-xs md:text-sm flex items-center gap-0.5 md:gap-1\"\n style={{ color: 'var(--text-secondary)' }}\n >\n {displayCommits}\n {isExpanded ? (\n <ChevronUp className=\"w-3.5 h-3.5 md:w-4 md:h-4\" aria-hidden=\"true\" />\n ) : (\n <ChevronDown className=\"w-3.5 h-3.5 md:w-4 md:h-4\" aria-hidden=\"true\" />\n )}\n </span>\n </div>\n\n {/* Progress + Sparkline (collapsed view) */}\n <div className=\"flex items-center gap-2\">\n <div className=\"flex-1\">\n <ProgressGlass value={progress} gradient={gradient} size=\"sm\" />\n </div>\n {showSparklineCollapsed && sparklineData && sparklineData.length > 0 && (\n <SparklineGlass\n data={sparklineData}\n labels={sparklineLabels}\n height=\"sm\"\n gap=\"sm\"\n className=\"w-16 md:w-20\"\n aria-label={`Activity trend for ${year}`}\n />\n )}\n </div>\n\n {/* Expanded Section */}\n {isExpanded && (\n <div className=\"mt-3 pt-3 border-t space-y-3\" style={expandedStyles}>\n {/* Stats Grid */}\n <div\n className=\"grid gap-2 md:gap-3\"\n style={{ gridTemplateColumns: `repeat(${Math.min(displayStats.length, 4)}, 1fr)` }}\n >\n {displayStats.map((stat, index) => (\n <div\n key={index}\n className=\"p-2 md:p-2.5 rounded-lg border text-center\"\n style={metricCardStyles}\n >\n {stat.icon && (\n <div className=\"mb-1 flex justify-center text-[var(--text-muted)]\">\n {stat.icon}\n </div>\n )}\n <div\n className=\"text-base md:text-xl font-bold\"\n style={{ color: 'var(--text-primary)' }}\n >\n {stat.value}\n </div>\n <div className=\"text-(length:--font-size-2xs) md:text-xs text-(--text-muted)\">\n {stat.label}\n </div>\n </div>\n ))}\n </div>\n\n {/* Expanded Sparkline with labels (if not shown in collapsed) */}\n {!showSparklineCollapsed && sparklineData && sparklineData.length > 0 && (\n <SparklineGlass\n data={sparklineData}\n labels={sparklineLabels}\n showLabels={!!sparklineLabels}\n height=\"md\"\n gap=\"sm\"\n highlightMax\n className=\"w-full\"\n aria-label={`Monthly activity for ${year}`}\n />\n )}\n\n {/* Insights */}\n {insights && insights.length > 0 && (\n <div className=\"space-y-2\">\n {insights.map((insight, index) => (\n <InsightCardGlass\n key={index}\n variant={insight.variant}\n emoji={insight.emoji}\n text={insight.text}\n detail={insight.detail}\n inline={false}\n />\n ))}\n </div>\n )}\n\n {/* Custom children content */}\n {children}\n\n {/* Show Year Button */}\n {onShowYear && (\n <ButtonGlass\n variant=\"primary\"\n size=\"sm\"\n onClick={(e) => {\n e.stopPropagation();\n onShowYear();\n }}\n className=\"w-full\"\n >\n {buttonLabel}\n </ButtonGlass>\n )}\n </div>\n )}\n </InteractiveCard>\n );\n }\n);\n\nYearCardGlass.displayName = 'YearCardGlass';\n"
|
|
20
14
|
}
|
|
21
15
|
],
|
|
22
|
-
"categories": [
|
|
23
|
-
|
|
24
|
-
]
|
|
25
|
-
}
|
|
16
|
+
"categories": ["composite"]
|
|
17
|
+
}
|