@fabio.caffarello/react-design-system 3.13.0 → 4.1.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/dist/granular/ui/components/Autocomplete/Autocomplete.js +116 -88
- package/dist/granular/ui/components/Autocomplete/Autocomplete.js.map +1 -1
- package/dist/granular/ui/components/Autocomplete/AutocompleteList.js +57 -47
- package/dist/granular/ui/components/Autocomplete/AutocompleteList.js.map +1 -1
- package/dist/granular/ui/components/Autocomplete/AutocompleteOption.js +21 -20
- package/dist/granular/ui/components/Autocomplete/AutocompleteOption.js.map +1 -1
- package/dist/granular/ui/components/Breadcrumb/Breadcrumb.js.map +1 -1
- package/dist/granular/ui/components/ColorPicker/ColorPicker.js.map +1 -1
- package/dist/granular/ui/components/CommandPalette/CommandPalette.js +187 -149
- package/dist/granular/ui/components/CommandPalette/CommandPalette.js.map +1 -1
- package/dist/granular/ui/components/DataGrid/DataGrid.js +92 -92
- package/dist/granular/ui/components/DataGrid/DataGrid.js.map +1 -1
- package/dist/granular/ui/components/DatePicker/DatePickerCalendar.js +154 -139
- package/dist/granular/ui/components/DatePicker/DatePickerCalendar.js.map +1 -1
- package/dist/granular/ui/components/Dialog/AlertDialog.js +73 -40
- package/dist/granular/ui/components/Dialog/AlertDialog.js.map +1 -1
- package/dist/granular/ui/components/Dialog/DialogContent.js +54 -48
- package/dist/granular/ui/components/Dialog/DialogContent.js.map +1 -1
- package/dist/granular/ui/components/Dialog/DialogDescription.js +31 -31
- package/dist/granular/ui/components/Dialog/DialogDescription.js.map +1 -1
- package/dist/granular/ui/components/Dialog/DialogTitle.js +30 -30
- package/dist/granular/ui/components/Dialog/DialogTitle.js.map +1 -1
- package/dist/granular/ui/components/Drawer/Drawer.js.map +1 -1
- package/dist/granular/ui/components/Dropdown/Dropdown.js.map +1 -1
- package/dist/granular/ui/components/EmptyState/EmptyState.js.map +1 -1
- package/dist/granular/ui/components/FileUpload/FileUpload.js.map +1 -1
- package/dist/granular/ui/components/Form/Form.js +38 -37
- package/dist/granular/ui/components/Form/Form.js.map +1 -1
- package/dist/granular/ui/components/Form/FormField.js +28 -26
- package/dist/granular/ui/components/Form/FormField.js.map +1 -1
- package/dist/granular/ui/components/Header/Header.js.map +1 -1
- package/dist/granular/ui/components/Header/components/HeaderActions.js.map +1 -1
- package/dist/granular/ui/components/Header/components/HeaderHamburger.js.map +1 -1
- package/dist/granular/ui/components/Header/components/HeaderLogo.js.map +1 -1
- package/dist/granular/ui/components/Header/components/HeaderMobileMenu.js.map +1 -1
- package/dist/granular/ui/components/Header/components/HeaderNavigation.js.map +1 -1
- package/dist/granular/ui/components/Header/contexts/HeaderContext.js.map +1 -1
- package/dist/granular/ui/components/Menu/Menu.js.map +1 -1
- package/dist/granular/ui/components/Modal/Modal.js +98 -86
- package/dist/granular/ui/components/Modal/Modal.js.map +1 -1
- package/dist/granular/ui/components/MultiSelect/MultiSelect.js +122 -106
- package/dist/granular/ui/components/MultiSelect/MultiSelect.js.map +1 -1
- package/dist/granular/ui/components/Navigation/Navigation.js.map +1 -1
- package/dist/granular/ui/components/PageHeader/PageHeader.js.map +1 -1
- package/dist/granular/ui/components/Pagination/Pagination.js.map +1 -1
- package/dist/granular/ui/components/Popover/Popover.js.map +1 -1
- package/dist/granular/ui/components/Rating/Rating.js.map +1 -1
- package/dist/granular/ui/components/SearchInput/SearchInput.js.map +1 -1
- package/dist/granular/ui/components/SideNavbar/components/Navbar/NavbarGroup.js +82 -64
- package/dist/granular/ui/components/SideNavbar/components/Navbar/NavbarGroup.js.map +1 -1
- package/dist/granular/ui/components/SideNavbar/components/Navbar/NavbarItem.js +30 -29
- package/dist/granular/ui/components/SideNavbar/components/Navbar/NavbarItem.js.map +1 -1
- package/dist/granular/ui/components/SideNavbar/components/SideNavbarResizeHandle.js +37 -35
- package/dist/granular/ui/components/SideNavbar/components/SideNavbarResizeHandle.js.map +1 -1
- package/dist/granular/ui/components/SideNavbar/providers/SideNavbarStateProvider.js +57 -57
- package/dist/granular/ui/components/SideNavbar/providers/SideNavbarStateProvider.js.map +1 -1
- package/dist/granular/ui/components/Stepper/Stepper.js +102 -94
- package/dist/granular/ui/components/Stepper/Stepper.js.map +1 -1
- package/dist/granular/ui/components/Table/Table.js +41 -35
- package/dist/granular/ui/components/Table/Table.js.map +1 -1
- package/dist/granular/ui/components/Table/TableActions/TableActions.js.map +1 -1
- package/dist/granular/ui/components/Table/TableFilters/TableFilters.js +49 -46
- package/dist/granular/ui/components/Table/TableFilters/TableFilters.js.map +1 -1
- package/dist/granular/ui/components/Table/TablePagination/TablePagination.js.map +1 -1
- package/dist/granular/ui/components/Table/TableProvider.js +82 -80
- package/dist/granular/ui/components/Table/TableProvider.js.map +1 -1
- package/dist/granular/ui/components/Table/TableRow.js +57 -53
- package/dist/granular/ui/components/Table/TableRow.js.map +1 -1
- package/dist/granular/ui/components/Table/useColumnResizing.js +53 -53
- package/dist/granular/ui/components/Table/useColumnResizing.js.map +1 -1
- package/dist/granular/ui/components/TimePicker/TimePicker.js +149 -103
- package/dist/granular/ui/components/TimePicker/TimePicker.js.map +1 -1
- package/dist/granular/ui/components/Timeline/Timeline.js.map +1 -1
- package/dist/granular/ui/hooks/useFocusRestore.js +14 -15
- package/dist/granular/ui/hooks/useFocusRestore.js.map +1 -1
- package/dist/granular/ui/primitives/Badge/Badge.js.map +1 -1
- package/dist/granular/ui/primitives/Button/Button.js +86 -104
- package/dist/granular/ui/primitives/Button/Button.js.map +1 -1
- package/dist/granular/ui/primitives/Checkbox/Checkbox.js.map +1 -1
- package/dist/granular/ui/primitives/Chip/Chip.js +91 -71
- package/dist/granular/ui/primitives/Chip/Chip.js.map +1 -1
- package/dist/granular/ui/primitives/ErrorMessage/ErrorMessage.js.map +1 -1
- package/dist/granular/ui/primitives/Input/Input.js.map +1 -1
- package/dist/granular/ui/primitives/Label/Label.js.map +1 -1
- package/dist/granular/ui/primitives/NavLink/NavLink.js.map +1 -1
- package/dist/granular/ui/primitives/Radio/Radio.js.map +1 -1
- package/dist/granular/ui/primitives/Select/Select.js.map +1 -1
- package/dist/granular/ui/primitives/Separator/Separator.js.map +1 -1
- package/dist/granular/ui/primitives/Skeleton/Skeleton.js.map +1 -1
- package/dist/granular/ui/primitives/Slider/Slider.js.map +1 -1
- package/dist/granular/ui/primitives/Spinner/Spinner.js.map +1 -1
- package/dist/granular/ui/primitives/Switch/Switch.js.map +1 -1
- package/dist/granular/ui/primitives/Tooltip/Tooltip.js.map +1 -1
- package/dist/granular/ui/providers/DialogContext.js.map +1 -1
- package/dist/granular/ui/providers/DialogProvider.js +24 -20
- package/dist/granular/ui/providers/DialogProvider.js.map +1 -1
- package/dist/index.cjs +144 -144
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +5896 -5609
- package/dist/index.js.map +1 -1
- package/dist/react-design-system.css +1 -1
- package/dist/server/index.cjs +13 -13
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.js +1050 -789
- package/dist/server/index.js.map +1 -1
- package/dist/ui/components/Autocomplete/Autocomplete.d.ts +21 -0
- package/dist/ui/components/Autocomplete/AutocompleteList.d.ts +4 -0
- package/dist/ui/components/Autocomplete/AutocompleteOption.d.ts +8 -0
- package/dist/ui/components/Breadcrumb/Breadcrumb.d.ts +0 -1
- package/dist/ui/components/ColorPicker/ColorPicker.d.ts +0 -1
- package/dist/ui/components/CommandPalette/CommandPalette.d.ts +0 -1
- package/dist/ui/components/DataGrid/DataGrid.d.ts +0 -1
- package/dist/ui/components/Dialog/DialogContent.d.ts +20 -1
- package/dist/ui/components/Drawer/Drawer.d.ts +0 -1
- package/dist/ui/components/Dropdown/Dropdown.d.ts +0 -1
- package/dist/ui/components/EmptyState/EmptyState.d.ts +0 -1
- package/dist/ui/components/FileUpload/FileUpload.d.ts +0 -1
- package/dist/ui/components/Form/FormField.d.ts +7 -0
- package/dist/ui/components/Header/Header.d.ts +1 -1
- package/dist/ui/components/Header/components/HeaderActions.d.ts +1 -1
- package/dist/ui/components/Header/components/HeaderHamburger.d.ts +1 -1
- package/dist/ui/components/Header/components/HeaderLogo.d.ts +1 -1
- package/dist/ui/components/Header/components/HeaderMobileMenu.d.ts +1 -1
- package/dist/ui/components/Header/components/HeaderNavigation.d.ts +1 -1
- package/dist/ui/components/Header/contexts/HeaderContext.d.ts +1 -1
- package/dist/ui/components/Menu/Menu.d.ts +0 -1
- package/dist/ui/components/Modal/Modal.d.ts +1 -2
- package/dist/ui/components/Navigation/Navigation.d.ts +1 -1
- package/dist/ui/components/PageHeader/PageHeader.d.ts +1 -1
- package/dist/ui/components/Pagination/Pagination.d.ts +0 -1
- package/dist/ui/components/Popover/Popover.d.ts +0 -1
- package/dist/ui/components/Rating/Rating.d.ts +0 -1
- package/dist/ui/components/SearchInput/SearchInput.d.ts +0 -1
- package/dist/ui/components/Stepper/Stepper.d.ts +0 -1
- package/dist/ui/components/Table/TableActions/TableActions.d.ts +0 -1
- package/dist/ui/components/Table/TableFilters/TableFilters.d.ts +0 -1
- package/dist/ui/components/Table/TablePagination/TablePagination.d.ts +0 -1
- package/dist/ui/components/TimePicker/TimePicker.d.ts +0 -1
- package/dist/ui/components/Timeline/Timeline.d.ts +0 -1
- package/dist/ui/primitives/Checkbox/Checkbox.d.ts +0 -1
- package/dist/ui/primitives/Chip/Chip.d.ts +21 -0
- package/dist/ui/primitives/ErrorMessage/ErrorMessage.d.ts +0 -1
- package/dist/ui/primitives/Input/Input.d.ts +0 -1
- package/dist/ui/primitives/Label/Label.d.ts +0 -1
- package/dist/ui/primitives/NavLink/NavLink.d.ts +1 -1
- package/dist/ui/primitives/Radio/Radio.d.ts +0 -1
- package/dist/ui/primitives/Select/Select.d.ts +0 -1
- package/dist/ui/primitives/Skeleton/Skeleton.d.ts +0 -1
- package/dist/ui/primitives/Slider/Slider.d.ts +0 -1
- package/dist/ui/primitives/Switch/Switch.d.ts +0 -1
- package/dist/ui/primitives/Tooltip/Tooltip.d.ts +0 -1
- package/dist/ui/providers/DialogContext.d.ts +8 -0
- package/dist/ui/server.d.ts +2 -0
- package/package.json +7 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Timeline.js","sources":["../../../../../src/ui/components/Timeline/Timeline.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { CheckCircle2 } from \"lucide-react\";\nimport { getSpacingClass } from \"../../tokens/spacing\";\nimport { getRadiusClass } from \"../../tokens/radius\";\n\nexport type TimelineOrientation = \"horizontal\" | \"vertical\";\n\nexport interface TimelineItem {\n id: string;\n title: string;\n description?: string;\n content?: ReactNode;\n timestamp?: string;\n icon?: ReactNode;\n status?: \"default\" | \"active\" | \"completed\" | \"error\";\n}\n\nexport interface TimelineProps {\n items: TimelineItem[];\n orientation?: TimelineOrientation;\n className?: string;\n}\n\n/**\n * Timeline Component\n *\n * A timeline component for displaying events in chronological order.\n * Supports horizontal and vertical orientations.\n * Follows Atomic Design principles as an Organism component.\n *\n * @example\n * ```tsx\n * <Timeline\n * items={[\n * { id: '1', title: 'Event 1', description: 'Description 1', timestamp: '2024-01-01' },\n * { id: '2', title: 'Event 2', description: 'Description 2', timestamp: '2024-01-02' },\n * ]}\n * />\n * ```\n */\nexport default function Timeline({\n items,\n orientation = \"vertical\",\n className = \"\",\n}: TimelineProps) {\n if (orientation === \"horizontal\") {\n return (\n <div className={`flex items-start ${className}`}>\n {items.map((item, index) => {\n const status =\n item.status ||\n (index === 0\n ? \"active\"\n : index < items.findIndex((i) => i.status === \"active\")\n ? \"completed\"\n : \"default\");\n const isLast = index === items.length - 1;\n\n return (\n <div key={item.id} className=\"flex items-start flex-1\">\n <div className=\"flex flex-col items-center flex-1\">\n {/* Icon/Indicator */}\n <div\n // data-marker=\"pending\" — see .claude/rules/colors.md\n // \"fg-quaternary: AA-by-construction exception\".\n {...(status === \"default\"\n ? { \"data-marker\": \"pending\" }\n : {})}\n className={`\n flex\n items-center\n justify-center\n w-10\n h-10\n ${getRadiusClass(\"full\")}\n border-2\n ${\n status === \"completed\"\n ? \"bg-success border-success text-fg-inverse\"\n : status === \"active\"\n ? \"bg-surface-brand-strong border-line-brand text-fg-inverse\"\n : status === \"error\"\n ? \"bg-error border-error text-fg-inverse\"\n : \"bg-surface-base border-line-emphasis text-fg-quaternary\"\n }\n `}\n >\n {item.icon ||\n (status === \"completed\" ? (\n <CheckCircle2 className=\"h-4 w-4\" />\n ) : (\n index + 1\n ))}\n </div>\n\n {/* Connector Line */}\n {!isLast && (\n <div\n className={`\n w-full\n h-0.5\n ${getSpacingClass(\"sm\", \"mt\")}\n ${status === \"completed\" ? \"bg-success\" : \"bg-line-emphasis\"}\n `}\n />\n )}\n\n {/* Content */}\n <div\n className={`${getSpacingClass(\"base\", \"mt\")} text-center ${getSpacingClass(\"base\", \"px\")}`}\n >\n {item.timestamp && (\n <p\n className={`text-xs text-fg-tertiary ${getSpacingClass(\"xs\", \"mb\")}`}\n >\n {item.timestamp}\n </p>\n )}\n <h3 className=\"text-sm font-semibold text-fg-primary\">\n {item.title}\n </h3>\n {item.description && (\n <p\n className={`text-xs text-fg-secondary ${getSpacingClass(\"xs\", \"mt\")}`}\n >\n {item.description}\n </p>\n )}\n {item.content && (\n <div className={getSpacingClass(\"sm\", \"mt\")}>\n {item.content}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n })}\n </div>\n );\n }\n\n // Vertical orientation\n return (\n <div className={`${getSpacingClass(\"none\", \"space-y\")} ${className}`}>\n {items.map((item, index) => {\n const status =\n item.status ||\n (index === 0\n ? \"active\"\n : index < items.findIndex((i) => i.status === \"active\")\n ? \"completed\"\n : \"default\");\n const isLast = index === items.length - 1;\n\n return (\n <div\n key={item.id}\n className={`flex items-start ${getSpacingClass(\"base\", \"gap\")}`}\n >\n {/* Timeline Line & Icon */}\n <div className=\"flex flex-col items-center\">\n <div\n // data-marker=\"pending\" — see .claude/rules/colors.md\n // \"fg-quaternary: AA-by-construction exception\".\n {...(status === \"default\" ? { \"data-marker\": \"pending\" } : {})}\n className={`\n flex\n items-center\n justify-center\n w-10\n h-10\n ${getRadiusClass(\"full\")}\n border-2\n ${\n status === \"completed\"\n ? \"bg-success border-success text-fg-inverse\"\n : status === \"active\"\n ? \"bg-surface-brand-strong border-line-brand text-fg-inverse\"\n : status === \"error\"\n ? \"bg-error border-error text-fg-inverse\"\n : \"bg-surface-base border-line-emphasis text-fg-quaternary\"\n }\n `}\n >\n {item.icon ||\n (status === \"completed\" ? (\n <CheckCircle2 className=\"h-4 w-4\" />\n ) : (\n index + 1\n ))}\n </div>\n {!isLast && (\n <div\n className={`\n w-0.5\n flex-1\n min-h-16\n ${getSpacingClass(\"sm\", \"mt\")}\n ${status === \"completed\" ? \"bg-success\" : \"bg-line-emphasis\"}\n `}\n />\n )}\n </div>\n\n {/* Content */}\n <div className={`flex-1 ${getSpacingClass(\"xl\", \"pb\")}`}>\n {item.timestamp && (\n <p\n className={`text-xs text-fg-tertiary ${getSpacingClass(\"xs\", \"mb\")}`}\n >\n {item.timestamp}\n </p>\n )}\n <h3\n className={`\n text-base\n font-semibold\n ${status === \"active\" ? \"text-fg-brand-emphasis\" : \"text-fg-primary\"}\n `}\n >\n {item.title}\n </h3>\n {item.description && (\n <p\n className={`text-sm text-fg-secondary ${getSpacingClass(\"xs\", \"mt\")}`}\n >\n {item.description}\n </p>\n )}\n {item.content && (\n <div className={getSpacingClass(\"md\", \"mt\")}>\n {item.content}\n </div>\n )}\n </div>\n </div>\n );\n })}\n </div>\n );\n}\n"],"names":["Timeline","items","orientation","className","jsx","item","index","status","i","isLast","jsxs","__spreadProps","__spreadValues","getRadiusClass","CheckCircle2","getSpacingClass"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Timeline.js","sources":["../../../../../src/ui/components/Timeline/Timeline.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { CheckCircle2 } from \"lucide-react\";\nimport { getSpacingClass } from \"../../tokens/spacing\";\nimport { getRadiusClass } from \"../../tokens/radius\";\n\nexport type TimelineOrientation = \"horizontal\" | \"vertical\";\n\nexport interface TimelineItem {\n id: string;\n title: string;\n description?: string;\n content?: ReactNode;\n timestamp?: string;\n icon?: ReactNode;\n status?: \"default\" | \"active\" | \"completed\" | \"error\";\n}\n\nexport interface TimelineProps {\n items: TimelineItem[];\n orientation?: TimelineOrientation;\n className?: string;\n}\n\n/**\n * Timeline Component\n *\n * A timeline component for displaying events in chronological order.\n * Supports horizontal and vertical orientations.\n *\n * @example\n * ```tsx\n * <Timeline\n * items={[\n * { id: '1', title: 'Event 1', description: 'Description 1', timestamp: '2024-01-01' },\n * { id: '2', title: 'Event 2', description: 'Description 2', timestamp: '2024-01-02' },\n * ]}\n * />\n * ```\n */\nexport default function Timeline({\n items,\n orientation = \"vertical\",\n className = \"\",\n}: TimelineProps) {\n if (orientation === \"horizontal\") {\n return (\n <div className={`flex items-start ${className}`}>\n {items.map((item, index) => {\n const status =\n item.status ||\n (index === 0\n ? \"active\"\n : index < items.findIndex((i) => i.status === \"active\")\n ? \"completed\"\n : \"default\");\n const isLast = index === items.length - 1;\n\n return (\n <div key={item.id} className=\"flex items-start flex-1\">\n <div className=\"flex flex-col items-center flex-1\">\n {/* Icon/Indicator */}\n <div\n // data-marker=\"pending\" — see .claude/rules/colors.md\n // \"fg-quaternary: AA-by-construction exception\".\n {...(status === \"default\"\n ? { \"data-marker\": \"pending\" }\n : {})}\n className={`\n flex\n items-center\n justify-center\n w-10\n h-10\n ${getRadiusClass(\"full\")}\n border-2\n ${\n status === \"completed\"\n ? \"bg-success border-success text-fg-inverse\"\n : status === \"active\"\n ? \"bg-surface-brand-strong border-line-brand text-fg-inverse\"\n : status === \"error\"\n ? \"bg-error border-error text-fg-inverse\"\n : \"bg-surface-base border-line-emphasis text-fg-quaternary\"\n }\n `}\n >\n {item.icon ||\n (status === \"completed\" ? (\n <CheckCircle2 className=\"h-4 w-4\" />\n ) : (\n index + 1\n ))}\n </div>\n\n {/* Connector Line */}\n {!isLast && (\n <div\n className={`\n w-full\n h-0.5\n ${getSpacingClass(\"sm\", \"mt\")}\n ${status === \"completed\" ? \"bg-success\" : \"bg-line-emphasis\"}\n `}\n />\n )}\n\n {/* Content */}\n <div\n className={`${getSpacingClass(\"base\", \"mt\")} text-center ${getSpacingClass(\"base\", \"px\")}`}\n >\n {item.timestamp && (\n <p\n className={`text-xs text-fg-tertiary ${getSpacingClass(\"xs\", \"mb\")}`}\n >\n {item.timestamp}\n </p>\n )}\n <h3 className=\"text-sm font-semibold text-fg-primary\">\n {item.title}\n </h3>\n {item.description && (\n <p\n className={`text-xs text-fg-secondary ${getSpacingClass(\"xs\", \"mt\")}`}\n >\n {item.description}\n </p>\n )}\n {item.content && (\n <div className={getSpacingClass(\"sm\", \"mt\")}>\n {item.content}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n })}\n </div>\n );\n }\n\n // Vertical orientation\n return (\n <div className={`${getSpacingClass(\"none\", \"space-y\")} ${className}`}>\n {items.map((item, index) => {\n const status =\n item.status ||\n (index === 0\n ? \"active\"\n : index < items.findIndex((i) => i.status === \"active\")\n ? \"completed\"\n : \"default\");\n const isLast = index === items.length - 1;\n\n return (\n <div\n key={item.id}\n className={`flex items-start ${getSpacingClass(\"base\", \"gap\")}`}\n >\n {/* Timeline Line & Icon */}\n <div className=\"flex flex-col items-center\">\n <div\n // data-marker=\"pending\" — see .claude/rules/colors.md\n // \"fg-quaternary: AA-by-construction exception\".\n {...(status === \"default\" ? { \"data-marker\": \"pending\" } : {})}\n className={`\n flex\n items-center\n justify-center\n w-10\n h-10\n ${getRadiusClass(\"full\")}\n border-2\n ${\n status === \"completed\"\n ? \"bg-success border-success text-fg-inverse\"\n : status === \"active\"\n ? \"bg-surface-brand-strong border-line-brand text-fg-inverse\"\n : status === \"error\"\n ? \"bg-error border-error text-fg-inverse\"\n : \"bg-surface-base border-line-emphasis text-fg-quaternary\"\n }\n `}\n >\n {item.icon ||\n (status === \"completed\" ? (\n <CheckCircle2 className=\"h-4 w-4\" />\n ) : (\n index + 1\n ))}\n </div>\n {!isLast && (\n <div\n className={`\n w-0.5\n flex-1\n min-h-16\n ${getSpacingClass(\"sm\", \"mt\")}\n ${status === \"completed\" ? \"bg-success\" : \"bg-line-emphasis\"}\n `}\n />\n )}\n </div>\n\n {/* Content */}\n <div className={`flex-1 ${getSpacingClass(\"xl\", \"pb\")}`}>\n {item.timestamp && (\n <p\n className={`text-xs text-fg-tertiary ${getSpacingClass(\"xs\", \"mb\")}`}\n >\n {item.timestamp}\n </p>\n )}\n <h3\n className={`\n text-base\n font-semibold\n ${status === \"active\" ? \"text-fg-brand-emphasis\" : \"text-fg-primary\"}\n `}\n >\n {item.title}\n </h3>\n {item.description && (\n <p\n className={`text-sm text-fg-secondary ${getSpacingClass(\"xs\", \"mt\")}`}\n >\n {item.description}\n </p>\n )}\n {item.content && (\n <div className={getSpacingClass(\"md\", \"mt\")}>\n {item.content}\n </div>\n )}\n </div>\n </div>\n );\n })}\n </div>\n );\n}\n"],"names":["Timeline","items","orientation","className","jsx","item","index","status","i","isLast","jsxs","__spreadProps","__spreadValues","getRadiusClass","CheckCircle2","getSpacingClass"],"mappings":"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"}
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { useRef as
|
|
3
|
-
function
|
|
4
|
-
const u =
|
|
5
|
-
|
|
6
|
-
var
|
|
7
|
-
if (
|
|
8
|
-
u.current = (
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}, [t]);
|
|
2
|
+
import { useRef as c, useEffect as l } from "react";
|
|
3
|
+
function s(r) {
|
|
4
|
+
const u = c(null), e = c(null);
|
|
5
|
+
l(() => {
|
|
6
|
+
var n;
|
|
7
|
+
if (r)
|
|
8
|
+
return e.current !== null && (clearTimeout(e.current), e.current = null), u.current = (n = document.activeElement) != null ? n : null, () => {
|
|
9
|
+
const t = u.current;
|
|
10
|
+
e.current = setTimeout(() => {
|
|
11
|
+
t == null || t.focus(), e.current = null;
|
|
12
|
+
}, 0);
|
|
13
|
+
};
|
|
14
|
+
}, [r]);
|
|
16
15
|
}
|
|
17
16
|
export {
|
|
18
|
-
|
|
17
|
+
s as useFocusRestore
|
|
19
18
|
};
|
|
20
19
|
//# sourceMappingURL=useFocusRestore.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFocusRestore.js","sources":["../../../../src/ui/hooks/useFocusRestore.ts"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef } from \"react\";\n\n/**\n * Snapshot focus when an overlay opens; restore on close.\n *\n * When `isOpen` flips from false to true, snapshots\n * `document.activeElement` at that moment. When `isOpen` flips back to\n * false, restores focus to the snapshotted element in a `setTimeout(0)`\n * — matching the timing the existing DialogProvider proved out\n * (Dialog.test.tsx:221 depends on this exact shape).\n *\n * Trigger-ref-free by design: the snapshot mechanism captures whatever\n * had focus at open time, so overlays can be opened by any composition\n * (button click, programmatic call from a notification, etc.) without\n * carrying a ref to a single triggering element.\n *\n * Scope is restore only. Focus trapping (Tab cycling within an open\n * modal container) is the separate concern of `useFocusTrap`. The two\n * hooks compose: modal surfaces (Dialog, Drawer) use both; non-modal\n * surfaces (Popover) use only restore.\n *\n * @example\n * ```tsx\n * function MyOverlay({ isOpen }: { isOpen: boolean }) {\n * useFocusRestore(isOpen);\n * // ...\n * }\n * ```\n */\nexport function useFocusRestore(isOpen: boolean): void {\n const previousActiveElement = useRef<HTMLElement | null>(null);\n\n useEffect(() => {\n if (isOpen) {\n previousActiveElement.current =\n
|
|
1
|
+
{"version":3,"file":"useFocusRestore.js","sources":["../../../../src/ui/hooks/useFocusRestore.ts"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef } from \"react\";\n\n/**\n * Snapshot focus when an overlay opens; restore on close.\n *\n * When `isOpen` flips from false to true, snapshots\n * `document.activeElement` at that moment. When `isOpen` flips back to\n * false, restores focus to the snapshotted element in a `setTimeout(0)`\n * — matching the timing the existing DialogProvider proved out\n * (Dialog.test.tsx:221 depends on this exact shape).\n *\n * Trigger-ref-free by design: the snapshot mechanism captures whatever\n * had focus at open time, so overlays can be opened by any composition\n * (button click, programmatic call from a notification, etc.) without\n * carrying a ref to a single triggering element.\n *\n * Scope is restore only. Focus trapping (Tab cycling within an open\n * modal container) is the separate concern of `useFocusTrap`. The two\n * hooks compose: modal surfaces (Dialog, Drawer) use both; non-modal\n * surfaces (Popover) use only restore.\n *\n * @example\n * ```tsx\n * function MyOverlay({ isOpen }: { isOpen: boolean }) {\n * useFocusRestore(isOpen);\n * // ...\n * }\n * ```\n */\nexport function useFocusRestore(isOpen: boolean): void {\n const previousActiveElement = useRef<HTMLElement | null>(null);\n const restoreTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n if (!isOpen) return;\n\n // Rising edge: cancel any pending restore (rapid close→open) and\n // snapshot whatever had focus before the overlay opened. The snapshot\n // is taken before useAutoFocus (called after this hook) moves focus\n // inside the overlay.\n if (restoreTimer.current !== null) {\n clearTimeout(restoreTimer.current);\n restoreTimer.current = null;\n }\n previousActiveElement.current =\n (document.activeElement as HTMLElement | null) ?? null;\n\n // Restore from the effect CLEANUP — not from an `isOpen === false`\n // branch. The cleanup fires on BOTH the falling edge (isOpen → false\n // while mounted) AND on unmount-while-open. The latter is the common\n // `{isOpen && <Overlay />}` pattern: closing unmounts the subtree, so\n // an isOpen=false render never happens and a falling-edge-only restore\n // would leave focus stranded on <body> (WCAG 2.4.3).\n return () => {\n const previous = previousActiveElement.current;\n // setTimeout(0) defers the restore until after React has committed\n // the close and removed the overlay's DOM, so focus lands on the\n // pre-open element rather than a now-disposed node inside the overlay.\n restoreTimer.current = setTimeout(() => {\n previous?.focus();\n restoreTimer.current = null;\n }, 0);\n };\n }, [isOpen]);\n}\n"],"names":["useFocusRestore","isOpen","previousActiveElement","useRef","restoreTimer","useEffect","_a","previous"],"mappings":";;AA+BO,SAASA,EAAgBC,GAAuB;AACrD,QAAMC,IAAwBC,EAA2B,IAAI,GACvDC,IAAeD,EAA6C,IAAI;AAEtE,EAAAE,EAAU,MAAM;;AACd,QAAKJ;AAML,aAAIG,EAAa,YAAY,SAC3B,aAAaA,EAAa,OAAO,GACjCA,EAAa,UAAU,OAEzBF,EAAsB,WACnBI,IAAA,SAAS,kBAAT,OAAAA,IAAiD,MAQ7C,MAAM;AACX,cAAMC,IAAWL,EAAsB;AAIvC,QAAAE,EAAa,UAAU,WAAW,MAAM;AACtC,UAAAG,KAAA,QAAAA,EAAU,SACVH,EAAa,UAAU;AAAA,QACzB,GAAG,CAAC;AAAA,MACN;AAAA,EACF,GAAG,CAACH,CAAM,CAAC;AACb;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Badge.js","sources":["../../../../../src/ui/primitives/Badge/Badge.tsx"],"sourcesContent":["import { memo, forwardRef } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { getRadiusClass } from \"../../tokens/radius\";\nimport { getSpacingClass } from \"../../tokens/spacing\";\nimport {\n getTypographySize,\n getTypographyWeight,\n} from \"../../tokens/typography\";\nimport { cn, cva } from \"../../utils\";\n\nexport type BadgeVariant =\n | \"success\"\n | \"warning\"\n | \"error\"\n | \"info\"\n | \"neutral\"\n | \"primary\"\n | \"secondary\";\nexport type BadgeSize = \"sm\" | \"md\" | \"lg\";\nexport type BadgeStyle = \"solid\" | \"outline\";\n\nexport interface BadgeProps extends Omit<\n HTMLAttributes<HTMLSpanElement>,\n \"style\"\n> {\n variant?: BadgeVariant;\n size?: BadgeSize;\n style?: BadgeStyle;\n children: ReactNode;\n \"aria-label\"?: string;\n}\n\n/**\n * Badge Component\n *\n * A versatile badge component for displaying status, priority, and other labels.\n * Follows Atomic Design principles as an Atom component.\n * Uses tokens for consistent theming.\n *\n * @example\n * ```tsx\n * <Badge variant=\"success\">Active</Badge>\n * <Badge variant=\"error\" size=\"lg\">Critical</Badge>\n * <Badge variant=\"info\" style=\"outline\">New</Badge>\n * ```\n */\n// Badge variants using CVA\nconst badgeVariants = cva(\n // Base classes\n cn(\n \"inline-flex\",\n \"items-center\",\n \"justify-center\",\n getTypographyWeight(\"label\"),\n getRadiusClass(\"md\"),\n \"border\",\n ),\n {\n variants: {\n variant: {\n success: \"\",\n warning: \"\",\n error: \"\",\n info: \"\",\n neutral: \"\",\n primary: \"\",\n secondary: \"\",\n },\n size: {\n sm: cn(\n getSpacingClass(\"1.5\", \"px\"),\n getSpacingClass(\"0.5\", \"py\"),\n getTypographySize(\"caption\"),\n ),\n md: cn(\n getSpacingClass(\"sm\", \"px\"),\n getSpacingClass(\"xs\", \"py\"),\n getTypographySize(\"caption\"),\n ),\n lg: cn(\n getSpacingClass(\"sm\", \"px\"),\n getSpacingClass(\"xs\", \"py\"),\n getTypographySize(\"bodySmall\"),\n ),\n },\n style: {\n solid: \"\",\n outline: \"\",\n },\n },\n compoundVariants: [\n // Solid style variants\n {\n variant: \"success\",\n style: \"solid\",\n class: cn(\"bg-success-bg\", \"text-success-dark\", \"border-success\"),\n },\n {\n variant: \"warning\",\n style: \"solid\",\n class: cn(\"bg-warning-bg\", \"text-warning-dark\", \"border-warning\"),\n },\n {\n variant: \"error\",\n style: \"solid\",\n class: cn(\"bg-error-bg\", \"text-error-dark\", \"border-error\"),\n },\n {\n variant: \"info\",\n style: \"solid\",\n class: cn(\"bg-info-bg\", \"text-info-dark\", \"border-info\"),\n },\n {\n variant: \"neutral\",\n style: \"solid\",\n class: cn(\"bg-surface-muted\", \"text-fg-primary\", \"border-line-default\"),\n },\n {\n variant: \"primary\",\n style: \"solid\",\n class: cn(\n \"bg-surface-brand-subtle\",\n \"text-fg-brand-emphasis\",\n \"border-line-brand\",\n ),\n },\n {\n variant: \"secondary\",\n style: \"solid\",\n // bg-pink-300: secondary solid badge — no semantic equivalent\n // (would shift 2 shades to bg-surface-secondary). Kept literal until\n // secondary brand surface palette expands beyond DEFAULT.\n class: cn(\n \"bg-pink-300\",\n \"text-fg-brand-secondary-emphasis\",\n \"border-line-secondary\",\n ),\n },\n // Outline style variants\n {\n variant: \"success\",\n style: \"outline\",\n class: cn(\"bg-transparent\", \"border-success\", \"text-fg-success\"),\n },\n {\n variant: \"warning\",\n style: \"outline\",\n class: cn(\"bg-transparent\", \"border-warning\", \"text-fg-warning\"),\n },\n {\n variant: \"error\",\n style: \"outline\",\n class: cn(\"bg-transparent\", \"border-error\", \"text-fg-error\"),\n },\n {\n variant: \"info\",\n style: \"outline\",\n class: cn(\"bg-transparent\", \"border-info\", \"text-fg-info\"),\n },\n {\n variant: \"neutral\",\n style: \"outline\",\n class: cn(\"bg-transparent\", \"border-line-default\", \"text-fg-secondary\"),\n },\n {\n variant: \"primary\",\n style: \"outline\",\n class: cn(\"bg-transparent\", \"border-line-brand\", \"text-fg-brand\"),\n },\n {\n variant: \"secondary\",\n style: \"outline\",\n class: cn(\n \"bg-transparent\",\n \"border-line-secondary\",\n \"text-fg-brand-secondary\",\n ),\n },\n ],\n defaultVariants: {\n variant: \"neutral\",\n size: \"md\",\n style: \"solid\",\n },\n },\n);\n\nconst Badge = memo(\n forwardRef<HTMLSpanElement, BadgeProps>(function Badge(\n {\n variant = \"neutral\",\n size = \"md\",\n style = \"solid\",\n className = \"\",\n children,\n \"aria-label\": ariaLabel,\n ...props\n },\n ref,\n ) {\n const classes = cn(badgeVariants({ variant, size, style }), className);\n\n // Best-effort accessible name resolution: explicit aria-label wins;\n // string children become the label; single-wrapped string children\n // (e.g. <Badge><span>Active</span></Badge>) are unwrapped one level.\n // Otherwise undefined and the consumer is responsible for naming\n // the badge externally.\n let accessibleLabel: string | undefined;\n if (ariaLabel) {\n accessibleLabel = ariaLabel;\n } else if (typeof children === \"string\") {\n accessibleLabel = children;\n } else if (\n typeof children === \"object\" &&\n children !== null &&\n \"props\" in children\n ) {\n const childProps = (children as { props?: { children?: unknown } }).props;\n if (childProps?.children && typeof childProps.children === \"string\") {\n accessibleLabel = childProps.children;\n }\n }\n\n return (\n <span\n ref={ref}\n role=\"status\"\n aria-label={accessibleLabel}\n className={classes}\n {...props}\n >\n {children}\n </span>\n );\n }),\n);\n\nBadge.displayName = \"Badge\";\n\nexport default Badge;\n"],"names":["badgeVariants","cva","cn","getTypographyWeight","getRadiusClass","getSpacingClass","getTypographySize","Badge","memo","forwardRef","_a","ref","_b","variant","size","style","className","children","ariaLabel","props","__objRest","classes","accessibleLabel","childProps","jsx","__spreadProps","__spreadValues"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Badge.js","sources":["../../../../../src/ui/primitives/Badge/Badge.tsx"],"sourcesContent":["import { memo, forwardRef } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { getRadiusClass } from \"../../tokens/radius\";\nimport { getSpacingClass } from \"../../tokens/spacing\";\nimport {\n getTypographySize,\n getTypographyWeight,\n} from \"../../tokens/typography\";\nimport { cn, cva } from \"../../utils\";\n\nexport type BadgeVariant =\n | \"success\"\n | \"warning\"\n | \"error\"\n | \"info\"\n | \"neutral\"\n | \"primary\"\n | \"secondary\";\nexport type BadgeSize = \"sm\" | \"md\" | \"lg\";\nexport type BadgeStyle = \"solid\" | \"outline\";\n\nexport interface BadgeProps extends Omit<\n HTMLAttributes<HTMLSpanElement>,\n \"style\"\n> {\n variant?: BadgeVariant;\n size?: BadgeSize;\n style?: BadgeStyle;\n children: ReactNode;\n \"aria-label\"?: string;\n}\n\n/**\n * Badge Component\n *\n * A versatile badge component for displaying status, priority, and other labels.\n * Uses tokens for consistent theming.\n *\n * @example\n * ```tsx\n * <Badge variant=\"success\">Active</Badge>\n * <Badge variant=\"error\" size=\"lg\">Critical</Badge>\n * <Badge variant=\"info\" style=\"outline\">New</Badge>\n * ```\n */\n// Badge variants using CVA\nconst badgeVariants = cva(\n // Base classes\n cn(\n \"inline-flex\",\n \"items-center\",\n \"justify-center\",\n getTypographyWeight(\"label\"),\n getRadiusClass(\"md\"),\n \"border\",\n ),\n {\n variants: {\n variant: {\n success: \"\",\n warning: \"\",\n error: \"\",\n info: \"\",\n neutral: \"\",\n primary: \"\",\n secondary: \"\",\n },\n size: {\n sm: cn(\n getSpacingClass(\"1.5\", \"px\"),\n getSpacingClass(\"0.5\", \"py\"),\n getTypographySize(\"caption\"),\n ),\n md: cn(\n getSpacingClass(\"sm\", \"px\"),\n getSpacingClass(\"xs\", \"py\"),\n getTypographySize(\"caption\"),\n ),\n lg: cn(\n getSpacingClass(\"sm\", \"px\"),\n getSpacingClass(\"xs\", \"py\"),\n getTypographySize(\"bodySmall\"),\n ),\n },\n style: {\n solid: \"\",\n outline: \"\",\n },\n },\n compoundVariants: [\n // Solid style variants\n {\n variant: \"success\",\n style: \"solid\",\n class: cn(\"bg-success-bg\", \"text-success-dark\", \"border-success\"),\n },\n {\n variant: \"warning\",\n style: \"solid\",\n class: cn(\"bg-warning-bg\", \"text-warning-dark\", \"border-warning\"),\n },\n {\n variant: \"error\",\n style: \"solid\",\n class: cn(\"bg-error-bg\", \"text-error-dark\", \"border-error\"),\n },\n {\n variant: \"info\",\n style: \"solid\",\n class: cn(\"bg-info-bg\", \"text-info-dark\", \"border-info\"),\n },\n {\n variant: \"neutral\",\n style: \"solid\",\n class: cn(\"bg-surface-muted\", \"text-fg-primary\", \"border-line-default\"),\n },\n {\n variant: \"primary\",\n style: \"solid\",\n class: cn(\n \"bg-surface-brand-subtle\",\n \"text-fg-brand-emphasis\",\n \"border-line-brand\",\n ),\n },\n {\n variant: \"secondary\",\n style: \"solid\",\n // bg-pink-300: secondary solid badge — no semantic equivalent\n // (would shift 2 shades to bg-surface-secondary). Kept literal until\n // secondary brand surface palette expands beyond DEFAULT.\n class: cn(\n \"bg-pink-300\",\n \"text-fg-brand-secondary-emphasis\",\n \"border-line-secondary\",\n ),\n },\n // Outline style variants\n {\n variant: \"success\",\n style: \"outline\",\n class: cn(\"bg-transparent\", \"border-success\", \"text-fg-success\"),\n },\n {\n variant: \"warning\",\n style: \"outline\",\n class: cn(\"bg-transparent\", \"border-warning\", \"text-fg-warning\"),\n },\n {\n variant: \"error\",\n style: \"outline\",\n class: cn(\"bg-transparent\", \"border-error\", \"text-fg-error\"),\n },\n {\n variant: \"info\",\n style: \"outline\",\n class: cn(\"bg-transparent\", \"border-info\", \"text-fg-info\"),\n },\n {\n variant: \"neutral\",\n style: \"outline\",\n class: cn(\"bg-transparent\", \"border-line-default\", \"text-fg-secondary\"),\n },\n {\n variant: \"primary\",\n style: \"outline\",\n class: cn(\"bg-transparent\", \"border-line-brand\", \"text-fg-brand\"),\n },\n {\n variant: \"secondary\",\n style: \"outline\",\n class: cn(\n \"bg-transparent\",\n \"border-line-secondary\",\n \"text-fg-brand-secondary\",\n ),\n },\n ],\n defaultVariants: {\n variant: \"neutral\",\n size: \"md\",\n style: \"solid\",\n },\n },\n);\n\nconst Badge = memo(\n forwardRef<HTMLSpanElement, BadgeProps>(function Badge(\n {\n variant = \"neutral\",\n size = \"md\",\n style = \"solid\",\n className = \"\",\n children,\n \"aria-label\": ariaLabel,\n ...props\n },\n ref,\n ) {\n const classes = cn(badgeVariants({ variant, size, style }), className);\n\n // Best-effort accessible name resolution: explicit aria-label wins;\n // string children become the label; single-wrapped string children\n // (e.g. <Badge><span>Active</span></Badge>) are unwrapped one level.\n // Otherwise undefined and the consumer is responsible for naming\n // the badge externally.\n let accessibleLabel: string | undefined;\n if (ariaLabel) {\n accessibleLabel = ariaLabel;\n } else if (typeof children === \"string\") {\n accessibleLabel = children;\n } else if (\n typeof children === \"object\" &&\n children !== null &&\n \"props\" in children\n ) {\n const childProps = (children as { props?: { children?: unknown } }).props;\n if (childProps?.children && typeof childProps.children === \"string\") {\n accessibleLabel = childProps.children;\n }\n }\n\n return (\n <span\n ref={ref}\n role=\"status\"\n aria-label={accessibleLabel}\n className={classes}\n {...props}\n >\n {children}\n </span>\n );\n }),\n);\n\nBadge.displayName = \"Badge\";\n\nexport default Badge;\n"],"names":["badgeVariants","cva","cn","getTypographyWeight","getRadiusClass","getSpacingClass","getTypographySize","Badge","memo","forwardRef","_a","ref","_b","variant","size","style","className","children","ariaLabel","props","__objRest","classes","accessibleLabel","childProps","jsx","__spreadProps","__spreadValues"],"mappings":"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"}
|
|
@@ -1,43 +1,43 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
var
|
|
2
|
+
var T = Object.defineProperty, W = Object.defineProperties;
|
|
3
|
+
var _ = Object.getOwnPropertyDescriptors;
|
|
4
|
+
var u = Object.getOwnPropertySymbols;
|
|
5
|
+
var k = Object.prototype.hasOwnProperty, j = Object.prototype.propertyIsEnumerable;
|
|
6
|
+
var B = (r, n, o) => n in r ? T(r, n, { enumerable: !0, configurable: !0, writable: !0, value: o }) : r[n] = o, m = (r, n) => {
|
|
7
7
|
for (var o in n || (n = {}))
|
|
8
|
-
|
|
9
|
-
if (
|
|
10
|
-
for (var o of
|
|
11
|
-
|
|
8
|
+
k.call(n, o) && B(r, o, n[o]);
|
|
9
|
+
if (u)
|
|
10
|
+
for (var o of u(n))
|
|
11
|
+
j.call(n, o) && B(r, o, n[o]);
|
|
12
12
|
return r;
|
|
13
|
-
},
|
|
14
|
-
var
|
|
13
|
+
}, g = (r, n) => W(r, _(n));
|
|
14
|
+
var C = (r, n) => {
|
|
15
15
|
var o = {};
|
|
16
|
-
for (var
|
|
17
|
-
|
|
18
|
-
if (r != null &&
|
|
19
|
-
for (var
|
|
20
|
-
n.indexOf(
|
|
16
|
+
for (var t in r)
|
|
17
|
+
k.call(r, t) && n.indexOf(t) < 0 && (o[t] = r[t]);
|
|
18
|
+
if (r != null && u)
|
|
19
|
+
for (var t of u(r))
|
|
20
|
+
n.indexOf(t) < 0 && j.call(r, t) && (o[t] = r[t]);
|
|
21
21
|
return o;
|
|
22
22
|
};
|
|
23
|
-
import {
|
|
24
|
-
import { memo as
|
|
25
|
-
import { Slot as
|
|
26
|
-
import { getRadiusClass as
|
|
23
|
+
import { jsxs as b, jsx as i, Fragment as D } from "react/jsx-runtime";
|
|
24
|
+
import { memo as q, forwardRef as G } from "react";
|
|
25
|
+
import { Slot as H, Slottable as J } from "@radix-ui/react-slot";
|
|
26
|
+
import { getRadiusClass as K } from "../../tokens/radius.js";
|
|
27
27
|
import { getSpacingClass as e } from "../../tokens/spacing.js";
|
|
28
|
-
import { getTypographySize as
|
|
29
|
-
import
|
|
30
|
-
import { cn as
|
|
31
|
-
import { cva as
|
|
32
|
-
const
|
|
28
|
+
import { getTypographySize as v, getTypographyClasses as M } from "../../tokens/typography.js";
|
|
29
|
+
import Q from "../Spinner/Spinner.js";
|
|
30
|
+
import { cn as s } from "../../utils/cn.js";
|
|
31
|
+
import { cva as U } from "../../utils/cva.js";
|
|
32
|
+
const X = U(
|
|
33
33
|
// Base classes
|
|
34
|
-
|
|
34
|
+
s(
|
|
35
35
|
"inline-flex",
|
|
36
36
|
"items-center",
|
|
37
37
|
"justify-center",
|
|
38
|
-
|
|
38
|
+
M("button").split(" ")[2] || "font-medium",
|
|
39
39
|
// Extract font-medium
|
|
40
|
-
|
|
40
|
+
K("md"),
|
|
41
41
|
"transition-colors",
|
|
42
42
|
"focus:outline-none",
|
|
43
43
|
"focus:ring-2",
|
|
@@ -48,25 +48,25 @@ const I = L(
|
|
|
48
48
|
{
|
|
49
49
|
variants: {
|
|
50
50
|
variant: {
|
|
51
|
-
primary:
|
|
51
|
+
primary: s(
|
|
52
52
|
"bg-surface-brand-strong",
|
|
53
53
|
"text-fg-inverse",
|
|
54
54
|
"hover:opacity-90",
|
|
55
55
|
"focus:ring-line-brand"
|
|
56
56
|
),
|
|
57
|
-
secondary:
|
|
57
|
+
secondary: s(
|
|
58
58
|
"bg-surface-secondary",
|
|
59
59
|
"text-fg-inverse",
|
|
60
60
|
"hover:opacity-90",
|
|
61
61
|
"focus:ring-line-secondary"
|
|
62
62
|
),
|
|
63
|
-
error:
|
|
63
|
+
error: s(
|
|
64
64
|
"bg-error",
|
|
65
65
|
"text-fg-inverse",
|
|
66
66
|
"hover:opacity-90",
|
|
67
67
|
"focus:ring-error"
|
|
68
68
|
),
|
|
69
|
-
outline:
|
|
69
|
+
outline: s(
|
|
70
70
|
"border-2",
|
|
71
71
|
"border-line-default",
|
|
72
72
|
"bg-transparent",
|
|
@@ -74,13 +74,13 @@ const I = L(
|
|
|
74
74
|
"hover:bg-surface-hover",
|
|
75
75
|
"focus:ring-line-focus"
|
|
76
76
|
),
|
|
77
|
-
ghost:
|
|
77
|
+
ghost: s(
|
|
78
78
|
"bg-transparent",
|
|
79
79
|
"text-fg-primary",
|
|
80
80
|
"hover:bg-surface-hover",
|
|
81
81
|
"focus:ring-line-focus"
|
|
82
82
|
),
|
|
83
|
-
iconOnly:
|
|
83
|
+
iconOnly: s(
|
|
84
84
|
"bg-transparent",
|
|
85
85
|
"text-fg-primary",
|
|
86
86
|
"hover:bg-surface-hover",
|
|
@@ -114,7 +114,7 @@ const I = L(
|
|
|
114
114
|
// a disabled link should still receive the same visual
|
|
115
115
|
// treatment as a disabled chrome variant — the opacity and
|
|
116
116
|
// cursor signal the disabled state.
|
|
117
|
-
link:
|
|
117
|
+
link: s(
|
|
118
118
|
"bg-transparent",
|
|
119
119
|
"text-fg-brand",
|
|
120
120
|
"underline-offset-4",
|
|
@@ -123,22 +123,22 @@ const I = L(
|
|
|
123
123
|
)
|
|
124
124
|
},
|
|
125
125
|
size: {
|
|
126
|
-
sm:
|
|
126
|
+
sm: s(
|
|
127
127
|
e("md", "px"),
|
|
128
128
|
e("1.5", "py"),
|
|
129
|
-
|
|
129
|
+
v("bodySmall"),
|
|
130
130
|
e("1.5", "gap")
|
|
131
131
|
),
|
|
132
|
-
md:
|
|
132
|
+
md: s(
|
|
133
133
|
e("base", "px"),
|
|
134
134
|
e("sm", "py"),
|
|
135
|
-
|
|
135
|
+
v("body"),
|
|
136
136
|
e("sm", "gap")
|
|
137
137
|
),
|
|
138
|
-
lg:
|
|
138
|
+
lg: s(
|
|
139
139
|
e("lg", "px"),
|
|
140
140
|
e("md", "py"),
|
|
141
|
-
|
|
141
|
+
v("bodyLarge"),
|
|
142
142
|
e("2.5", "gap")
|
|
143
143
|
)
|
|
144
144
|
}
|
|
@@ -148,17 +148,17 @@ const I = L(
|
|
|
148
148
|
{
|
|
149
149
|
variant: "iconOnly",
|
|
150
150
|
size: "sm",
|
|
151
|
-
class:
|
|
151
|
+
class: s("h-8", "w-8", e("none", "p"))
|
|
152
152
|
},
|
|
153
153
|
{
|
|
154
154
|
variant: "iconOnly",
|
|
155
155
|
size: "md",
|
|
156
|
-
class:
|
|
156
|
+
class: s("h-10", "w-10", e("none", "p"))
|
|
157
157
|
},
|
|
158
158
|
{
|
|
159
159
|
variant: "iconOnly",
|
|
160
160
|
size: "lg",
|
|
161
|
-
class:
|
|
161
|
+
class: s("h-12", "w-12", e("none", "p"))
|
|
162
162
|
},
|
|
163
163
|
// Link variant zeroes the size's padding via compoundVariants so
|
|
164
164
|
// the override runs AFTER the size block's `px-N`/`py-N` and
|
|
@@ -169,17 +169,17 @@ const I = L(
|
|
|
169
169
|
{
|
|
170
170
|
variant: "link",
|
|
171
171
|
size: "sm",
|
|
172
|
-
class:
|
|
172
|
+
class: s(e("none", "px"), e("none", "py"))
|
|
173
173
|
},
|
|
174
174
|
{
|
|
175
175
|
variant: "link",
|
|
176
176
|
size: "md",
|
|
177
|
-
class:
|
|
177
|
+
class: s(e("none", "px"), e("none", "py"))
|
|
178
178
|
},
|
|
179
179
|
{
|
|
180
180
|
variant: "link",
|
|
181
181
|
size: "lg",
|
|
182
|
-
class:
|
|
182
|
+
class: s(e("none", "px"), e("none", "py"))
|
|
183
183
|
}
|
|
184
184
|
],
|
|
185
185
|
defaultVariants: {
|
|
@@ -200,24 +200,24 @@ function d({
|
|
|
200
200
|
}
|
|
201
201
|
) : null;
|
|
202
202
|
}
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
var
|
|
203
|
+
const Y = q(
|
|
204
|
+
G(function(Z, z) {
|
|
205
|
+
var O = Z, {
|
|
206
206
|
variant: n = "primary",
|
|
207
207
|
size: o = "md",
|
|
208
|
-
isLoading:
|
|
209
|
-
loadingText:
|
|
210
|
-
loadingIcon:
|
|
211
|
-
leftIcon:
|
|
208
|
+
isLoading: t = !1,
|
|
209
|
+
loadingText: y,
|
|
210
|
+
loadingIcon: E,
|
|
211
|
+
leftIcon: p,
|
|
212
212
|
rightIcon: c,
|
|
213
|
-
fullWidth:
|
|
213
|
+
fullWidth: R = !1,
|
|
214
214
|
asChild: f = !1,
|
|
215
|
-
as:
|
|
216
|
-
className:
|
|
217
|
-
disabled:
|
|
218
|
-
children:
|
|
219
|
-
"aria-label":
|
|
220
|
-
} =
|
|
215
|
+
as: a,
|
|
216
|
+
className: $ = "",
|
|
217
|
+
disabled: h = !1,
|
|
218
|
+
children: l,
|
|
219
|
+
"aria-label": x
|
|
220
|
+
} = O, w = C(O, [
|
|
221
221
|
"variant",
|
|
222
222
|
"size",
|
|
223
223
|
"isLoading",
|
|
@@ -233,56 +233,38 @@ const nn = K(
|
|
|
233
233
|
"children",
|
|
234
234
|
"aria-label"
|
|
235
235
|
]);
|
|
236
|
-
typeof process != "undefined" && process.env.NODE_ENV !== "production" && f &&
|
|
236
|
+
typeof process != "undefined" && process.env.NODE_ENV !== "production" && f && a !== void 0 && a !== "button" && console.warn(
|
|
237
237
|
"[Button] `as` is ignored when `asChild` is true; the child element is used as the root. Drop one of the two props to silence this warning."
|
|
238
238
|
);
|
|
239
|
-
const
|
|
240
|
-
(
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
() => j && !b && !a ? "Button" : b,
|
|
254
|
-
[j, b, a]
|
|
255
|
-
), C = m(() => n === "error" ? "primary" : n === "primary" || n === "secondary" ? "neutral" : "primary", [n]), D = m(
|
|
256
|
-
() => o === "sm" ? "sm" : o === "lg" ? "lg" : "md",
|
|
257
|
-
[o]
|
|
258
|
-
), q = m(
|
|
259
|
-
() => w || /* @__PURE__ */ i(Z, { size: D, variant: C }),
|
|
260
|
-
[w, D, C]
|
|
261
|
-
), E = !f && (p === void 0 || p === "button") && !N.type ? "button" : void 0, R = u(u({
|
|
262
|
-
className: T,
|
|
263
|
-
disabled: S || s,
|
|
264
|
-
"aria-busy": s,
|
|
265
|
-
"aria-label": _,
|
|
266
|
-
"aria-disabled": S || s
|
|
267
|
-
}, E ? { type: E } : {}), N);
|
|
268
|
-
return f ? /* @__PURE__ */ h(k, v(u({ ref: B }, R), { children: [
|
|
269
|
-
l && /* @__PURE__ */ i(d, { position: "left", children: l }),
|
|
270
|
-
/* @__PURE__ */ i(W, { children: a }),
|
|
239
|
+
const S = f ? H : a != null ? a : "button", A = s(
|
|
240
|
+
X({ variant: n, size: o }),
|
|
241
|
+
R && "w-full",
|
|
242
|
+
$
|
|
243
|
+
), F = (n === "iconOnly" || !l && (p || c)) && !x && !l ? "Button" : x, P = E || /* @__PURE__ */ i(Q, { size: o === "sm" ? "sm" : o === "lg" ? "lg" : "md", variant: n === "error" ? "primary" : n === "primary" || n === "secondary" ? "neutral" : "primary" }), N = !f && (a === void 0 || a === "button") && !w.type ? "button" : void 0, V = m(m({
|
|
244
|
+
className: A,
|
|
245
|
+
disabled: h || t,
|
|
246
|
+
"aria-busy": t,
|
|
247
|
+
"aria-label": F,
|
|
248
|
+
"aria-disabled": h || t
|
|
249
|
+
}, N ? { type: N } : {}), w);
|
|
250
|
+
return f ? /* @__PURE__ */ b(S, g(m({ ref: z }, V), { children: [
|
|
251
|
+
p && /* @__PURE__ */ i(d, { position: "left", children: p }),
|
|
252
|
+
/* @__PURE__ */ i(J, { children: l }),
|
|
271
253
|
c && /* @__PURE__ */ i(d, { position: "right", children: c })
|
|
272
|
-
] })) : /* @__PURE__ */ i(
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
!
|
|
276
|
-
] }) : /* @__PURE__ */
|
|
277
|
-
|
|
278
|
-
|
|
254
|
+
] })) : /* @__PURE__ */ i(S, g(m({ ref: z }, V), { children: t ? /* @__PURE__ */ b(D, { children: [
|
|
255
|
+
P,
|
|
256
|
+
y && /* @__PURE__ */ i("span", { className: e("sm", "ml"), children: y }),
|
|
257
|
+
!y && l && /* @__PURE__ */ i("span", { className: `${e("sm", "ml")} opacity-0`, children: l })
|
|
258
|
+
] }) : /* @__PURE__ */ b(D, { children: [
|
|
259
|
+
p && /* @__PURE__ */ i(d, { position: "left", children: p }),
|
|
260
|
+
l,
|
|
279
261
|
c && /* @__PURE__ */ i(d, { position: "right", children: c })
|
|
280
262
|
] }) }));
|
|
281
263
|
})
|
|
282
264
|
);
|
|
283
|
-
|
|
265
|
+
Y.displayName = "Button";
|
|
284
266
|
export {
|
|
285
|
-
|
|
286
|
-
|
|
267
|
+
Y as Button,
|
|
268
|
+
Y as default
|
|
287
269
|
};
|
|
288
270
|
//# sourceMappingURL=Button.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.js","sources":["../../../../../src/ui/primitives/Button/Button.tsx"],"sourcesContent":["import { forwardRef, memo, useMemo } from \"react\";\nimport type { ButtonHTMLAttributes, ReactNode, ElementType } from \"react\";\nimport { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport { getRadiusClass } from \"../../tokens/radius\";\n\n// Ambient declaration so the dev-only warn below typechecks without\n// pulling @types/node into the app tsconfig. At runtime the consumer's\n// bundler (webpack/turbopack/etc.) replaces `process.env.NODE_ENV` with\n// a literal; the `typeof process` guard keeps the branch safe in\n// browser/edge runtimes where `process` doesn't exist.\ndeclare const process: { env: { NODE_ENV?: string } };\nimport { getSpacingClass } from \"../../tokens/spacing\";\nimport {\n getTypographyClasses,\n getTypographySize,\n} from \"../../tokens/typography\";\nimport { cn, cva } from \"../../utils\";\nimport Spinner from \"../Spinner/Spinner\";\n\nexport type ButtonVariant =\n | \"primary\"\n | \"secondary\"\n | \"error\"\n | \"outline\"\n | \"ghost\"\n | \"iconOnly\"\n | \"link\";\nexport type ButtonSize = \"sm\" | \"md\" | \"lg\";\n\nexport interface ButtonProps extends Omit<\n ButtonHTMLAttributes<HTMLButtonElement>,\n \"as\"\n> {\n variant?: ButtonVariant;\n size?: ButtonSize;\n isLoading?: boolean;\n loadingText?: string;\n loadingIcon?: ReactNode;\n leftIcon?: ReactNode;\n rightIcon?: ReactNode;\n fullWidth?: boolean;\n as?: ElementType;\n href?: string;\n target?: string;\n /**\n * When true, render via Radix `Slot`: classes, ARIA, ref and remaining\n * props are projected onto the single child element instead of an\n * intrinsic `<button>`. The child keeps its own element type and props\n * intact — including framework-native props like `<Link href prefetch>`.\n *\n * `asChild` takes precedence over `as`: if both are supplied, `as` is\n * ignored and a dev-only warning is logged.\n *\n * @example\n * ```tsx\n * <Button asChild variant=\"primary\">\n * <Link href=\"/profile\" prefetch>Open profile</Link>\n * </Button>\n * // → <a class=\"…button classes\" href=\"/profile\" data-prefetch>Open profile</a>\n * ```\n */\n asChild?: boolean;\n}\n\n/**\n * Button Variants using CVA\n * Type-safe variant system for Button component\n */\nconst buttonVariants = cva(\n // Base classes\n cn(\n \"inline-flex\",\n \"items-center\",\n \"justify-center\",\n getTypographyClasses(\"button\").split(\" \")[2] || \"font-medium\", // Extract font-medium\n getRadiusClass(\"md\"),\n \"transition-colors\",\n \"focus:outline-none\",\n \"focus:ring-2\",\n \"focus:ring-offset-2\",\n \"disabled:opacity-50\",\n \"disabled:cursor-not-allowed\",\n ),\n {\n variants: {\n variant: {\n primary: cn(\n \"bg-surface-brand-strong\",\n \"text-fg-inverse\",\n \"hover:opacity-90\",\n \"focus:ring-line-brand\",\n ),\n secondary: cn(\n \"bg-surface-secondary\",\n \"text-fg-inverse\",\n \"hover:opacity-90\",\n \"focus:ring-line-secondary\",\n ),\n error: cn(\n \"bg-error\",\n \"text-fg-inverse\",\n \"hover:opacity-90\",\n \"focus:ring-error\",\n ),\n outline: cn(\n \"border-2\",\n \"border-line-default\",\n \"bg-transparent\",\n \"text-fg-primary\",\n \"hover:bg-surface-hover\",\n \"focus:ring-line-focus\",\n ),\n ghost: cn(\n \"bg-transparent\",\n \"text-fg-primary\",\n \"hover:bg-surface-hover\",\n \"focus:ring-line-focus\",\n ),\n iconOnly: cn(\n \"bg-transparent\",\n \"text-fg-primary\",\n \"hover:bg-surface-hover\",\n \"focus:ring-line-focus\",\n getSpacingClass(\"none\", \"p\"),\n ),\n // Textual call-to-action — brand-coloured text, underline on\n // hover, no chrome (no surface, no border). Padding is zeroed\n // out per size in compoundVariants so the bounding box hugs\n // the text (height intrinsic). Issue #156.\n //\n // Focus ring: `focus:ring-line-focus` keeps the same focus\n // family the other no-chrome variants use (ghost, outline,\n // iconOnly). The base's `focus:ring-2` + `focus:ring-offset-2`\n // produce a 2px ring 2px away from the text bounding box; the\n // 2px offset is rendered in surface-base so the ring contrasts\n // against surface (≥3:1 for WCAG 2.4.11, verified) rather than\n // colliding with the brand-coloured text. The element keeps\n // `getRadiusClass(\"md\")` from the base, which is invisible\n // without chrome but rounds the focus ring corners.\n //\n // hover:underline pairs with `underline-offset-4` so the\n // underline that appears on hover sits clear of the descenders\n // (WCAG-aligned with link best practice). At rest the link\n // carries no underline — the brand colour alone signals\n // affordance, matching the shadcn convention this variant is\n // modelled on (see issue body).\n //\n // disabled: inherits opacity-50 + cursor-not-allowed from the\n // base. We do NOT special-case `disabled:no-underline` because\n // a disabled link should still receive the same visual\n // treatment as a disabled chrome variant — the opacity and\n // cursor signal the disabled state.\n link: cn(\n \"bg-transparent\",\n \"text-fg-brand\",\n \"underline-offset-4\",\n \"hover:underline\",\n \"focus:ring-line-focus\",\n ),\n },\n size: {\n sm: cn(\n getSpacingClass(\"md\", \"px\"),\n getSpacingClass(\"1.5\", \"py\"),\n getTypographySize(\"bodySmall\"),\n getSpacingClass(\"1.5\", \"gap\"),\n ),\n md: cn(\n getSpacingClass(\"base\", \"px\"),\n getSpacingClass(\"sm\", \"py\"),\n getTypographySize(\"body\"),\n getSpacingClass(\"sm\", \"gap\"),\n ),\n lg: cn(\n getSpacingClass(\"lg\", \"px\"),\n getSpacingClass(\"md\", \"py\"),\n getTypographySize(\"bodyLarge\"),\n getSpacingClass(\"2.5\", \"gap\"),\n ),\n },\n },\n compoundVariants: [\n // IconOnly variant has different sizing\n {\n variant: \"iconOnly\",\n size: \"sm\",\n class: cn(\"h-8\", \"w-8\", getSpacingClass(\"none\", \"p\")),\n },\n {\n variant: \"iconOnly\",\n size: \"md\",\n class: cn(\"h-10\", \"w-10\", getSpacingClass(\"none\", \"p\")),\n },\n {\n variant: \"iconOnly\",\n size: \"lg\",\n class: cn(\"h-12\", \"w-12\", getSpacingClass(\"none\", \"p\")),\n },\n // Link variant zeroes the size's padding via compoundVariants so\n // the override runs AFTER the size block's `px-N`/`py-N` and\n // twMerge picks the zero value (`cn` joins variant before\n // compound). The size's typography and gap survive — the link's\n // text scales with size and icons still get gap-N when present.\n // Issue #156.\n {\n variant: \"link\",\n size: \"sm\",\n class: cn(getSpacingClass(\"none\", \"px\"), getSpacingClass(\"none\", \"py\")),\n },\n {\n variant: \"link\",\n size: \"md\",\n class: cn(getSpacingClass(\"none\", \"px\"), getSpacingClass(\"none\", \"py\")),\n },\n {\n variant: \"link\",\n size: \"lg\",\n class: cn(getSpacingClass(\"none\", \"px\"), getSpacingClass(\"none\", \"py\")),\n },\n ],\n defaultVariants: {\n variant: \"primary\",\n size: \"md\",\n },\n },\n);\n\n/**\n * Icon Wrapper Component\n * Handles icon spacing and alignment consistently\n */\nfunction IconWrapper({\n children,\n position,\n}: {\n children: ReactNode;\n position: \"left\" | \"right\";\n}) {\n if (!children) return null;\n\n return (\n <span\n className={`inline-flex items-center ${position === \"left\" ? getSpacingClass(\"none\", \"mr\") : getSpacingClass(\"none\", \"ml\")}`}\n >\n {children}\n </span>\n );\n}\n\n/**\n * Button Component\n *\n * A styled button with variants, sizes, and loading states.\n *\n * Polymorphism — two APIs:\n * - `as` (legacy): swap the root element type. `<Button as={Link} href=\"…\">`.\n * - `asChild` (recommended): project Button's styling onto the single\n * child element. Idiomatic Radix Slot pattern, preserves the child's\n * own props and TS type (e.g. `<Link href prefetch>`). When `asChild`\n * is true, `as` is ignored.\n *\n * @example\n * ```tsx\n * // Basic usage\n * <Button variant=\"primary\" size=\"md\" onClick={handleClick}>Click me</Button>\n *\n * // With icons\n * <Button leftIcon={<Icon />} rightIcon={<Icon />}>Action</Button>\n *\n * // Loading state\n * <Button isLoading loadingText=\"Saving...\">Save</Button>\n *\n * // Polymorphic via asChild (preserves Next Link's TS type and native props)\n * <Button asChild variant=\"primary\">\n * <Link href=\"/page\" prefetch>Open</Link>\n * </Button>\n *\n * // Polymorphic via legacy `as`\n * <Button as=\"a\" href=\"/page\">Navigate</Button>\n * ```\n */\nconst Button = memo(\n forwardRef<HTMLButtonElement, ButtonProps>(function Button(\n {\n variant = \"primary\",\n size = \"md\",\n isLoading = false,\n loadingText,\n loadingIcon,\n leftIcon,\n rightIcon,\n fullWidth = false,\n asChild = false,\n as,\n className = \"\",\n disabled = false,\n children,\n \"aria-label\": ariaLabel,\n ...props\n },\n ref,\n ) {\n // asChild wins over `as`. Warn in dev so the precedence is observable\n // instead of being a silent override. Production builds strip this branch.\n if (\n typeof process !== \"undefined\" &&\n process.env.NODE_ENV !== \"production\" &&\n asChild &&\n as !== undefined &&\n as !== \"button\"\n ) {\n console.warn(\n \"[Button] `as` is ignored when `asChild` is true; the child element is used as the root. Drop one of the two props to silence this warning.\",\n );\n }\n\n const Component: ElementType = asChild ? Slot : (as ?? \"button\");\n\n // Memoize classes computation\n const classes = useMemo(\n () =>\n cn(\n buttonVariants({\n variant,\n size,\n }),\n fullWidth && \"w-full\",\n className,\n ),\n [variant, size, fullWidth, className],\n );\n\n // Memoize icon-only check\n const isIconOnly = useMemo(\n () => variant === \"iconOnly\" || (!children && (leftIcon || rightIcon)),\n [variant, children, leftIcon, rightIcon],\n );\n\n // Memoize aria label\n const finalAriaLabel = useMemo(\n () =>\n isIconOnly && !ariaLabel && !children\n ? \"Button\" // Fallback, but should be provided\n : ariaLabel,\n [isIconOnly, ariaLabel, children],\n );\n\n // Memoize spinner variant computation\n const spinnerVariant = useMemo((): \"primary\" | \"secondary\" | \"neutral\" => {\n if (variant === \"error\") return \"primary\"; // Red buttons use primary spinner (white)\n if (variant === \"primary\" || variant === \"secondary\") return \"neutral\"; // Colored buttons use neutral spinner\n return \"primary\"; // Default\n }, [variant]);\n\n // Memoize spinner size\n const spinnerSize = useMemo(\n () => (size === \"sm\" ? \"sm\" : size === \"lg\" ? \"lg\" : \"md\"),\n [size],\n );\n\n // Memoize loading icon\n const displayLoadingIcon = useMemo(\n () =>\n loadingIcon || <Spinner size={spinnerSize} variant={spinnerVariant} />,\n [loadingIcon, spinnerSize, spinnerVariant],\n );\n\n // Build button props (spread props at the end to allow overrides).\n // `type=\"button\"` default applies only when rendering an intrinsic\n // <button>. asChild and `as` (custom) projections leave it off so we\n // don't paint a meaningless `type` attribute onto <a> / <Link>.\n const defaultType =\n !asChild && (as === undefined || as === \"button\") && !props.type\n ? \"button\"\n : undefined;\n const buttonProps = {\n className: classes,\n disabled: disabled || isLoading,\n \"aria-busy\": isLoading,\n \"aria-label\": finalAriaLabel,\n \"aria-disabled\": disabled || isLoading,\n ...(defaultType ? { type: defaultType } : {}),\n ...props,\n };\n\n // asChild path: render Slot with the icons and Slottable as direct\n // sibling children (NOT wrapped in a React.Fragment). Slot's\n // children-processing inspects each direct child for Slottable; if\n // the children are wrapped in a Fragment, Slot's SlotClone merges\n // the projected props into the Fragment itself (a silent no-op for\n // className / aria / ref), and nothing reaches the consumer's\n // element. Hence the two distinct branches below: same content\n // shape, different host element, different child layout.\n if (asChild) {\n return (\n <Component ref={ref} {...buttonProps}>\n {leftIcon && <IconWrapper position=\"left\">{leftIcon}</IconWrapper>}\n <Slottable>{children}</Slottable>\n {rightIcon && <IconWrapper position=\"right\">{rightIcon}</IconWrapper>}\n </Component>\n );\n }\n\n return (\n <Component ref={ref} {...buttonProps}>\n {isLoading ? (\n <>\n {displayLoadingIcon}\n {loadingText && (\n <span className={getSpacingClass(\"sm\", \"ml\")}>{loadingText}</span>\n )}\n {!loadingText && children && (\n <span className={`${getSpacingClass(\"sm\", \"ml\")} opacity-0`}>\n {children}\n </span>\n )}\n </>\n ) : (\n <>\n {leftIcon && <IconWrapper position=\"left\">{leftIcon}</IconWrapper>}\n {children}\n {rightIcon && (\n <IconWrapper position=\"right\">{rightIcon}</IconWrapper>\n )}\n </>\n )}\n </Component>\n );\n }),\n);\n\nButton.displayName = \"Button\";\n\nexport default Button;\nexport { Button };\n"],"names":["buttonVariants","cva","cn","getTypographyClasses","getRadiusClass","getSpacingClass","getTypographySize","IconWrapper","children","position","jsx","Button","memo","forwardRef","_a","ref","_b","variant","size","isLoading","loadingText","loadingIcon","leftIcon","rightIcon","fullWidth","asChild","as","className","disabled","ariaLabel","props","__objRest","Component","Slot","classes","useMemo","isIconOnly","finalAriaLabel","spinnerVariant","spinnerSize","displayLoadingIcon","Spinner","defaultType","buttonProps","__spreadValues","jsxs","__spreadProps","Slottable","Fragment"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Button.js","sources":["../../../../../src/ui/primitives/Button/Button.tsx"],"sourcesContent":["import { forwardRef, memo } from \"react\";\nimport type { ButtonHTMLAttributes, ReactNode, ElementType } from \"react\";\nimport { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport { getRadiusClass } from \"../../tokens/radius\";\n\n// Ambient declaration so the dev-only warn below typechecks without\n// pulling @types/node into the app tsconfig. At runtime the consumer's\n// bundler (webpack/turbopack/etc.) replaces `process.env.NODE_ENV` with\n// a literal; the `typeof process` guard keeps the branch safe in\n// browser/edge runtimes where `process` doesn't exist.\ndeclare const process: { env: { NODE_ENV?: string } };\nimport { getSpacingClass } from \"../../tokens/spacing\";\nimport {\n getTypographyClasses,\n getTypographySize,\n} from \"../../tokens/typography\";\nimport { cn, cva } from \"../../utils\";\nimport Spinner from \"../Spinner/Spinner\";\n\nexport type ButtonVariant =\n | \"primary\"\n | \"secondary\"\n | \"error\"\n | \"outline\"\n | \"ghost\"\n | \"iconOnly\"\n | \"link\";\nexport type ButtonSize = \"sm\" | \"md\" | \"lg\";\n\nexport interface ButtonProps extends Omit<\n ButtonHTMLAttributes<HTMLButtonElement>,\n \"as\"\n> {\n variant?: ButtonVariant;\n size?: ButtonSize;\n isLoading?: boolean;\n loadingText?: string;\n loadingIcon?: ReactNode;\n leftIcon?: ReactNode;\n rightIcon?: ReactNode;\n fullWidth?: boolean;\n as?: ElementType;\n href?: string;\n target?: string;\n /**\n * When true, render via Radix `Slot`: classes, ARIA, ref and remaining\n * props are projected onto the single child element instead of an\n * intrinsic `<button>`. The child keeps its own element type and props\n * intact — including framework-native props like `<Link href prefetch>`.\n *\n * `asChild` takes precedence over `as`: if both are supplied, `as` is\n * ignored and a dev-only warning is logged.\n *\n * @example\n * ```tsx\n * <Button asChild variant=\"primary\">\n * <Link href=\"/profile\" prefetch>Open profile</Link>\n * </Button>\n * // → <a class=\"…button classes\" href=\"/profile\" data-prefetch>Open profile</a>\n * ```\n */\n asChild?: boolean;\n}\n\n/**\n * Button Variants using CVA\n * Type-safe variant system for Button component\n */\nconst buttonVariants = cva(\n // Base classes\n cn(\n \"inline-flex\",\n \"items-center\",\n \"justify-center\",\n getTypographyClasses(\"button\").split(\" \")[2] || \"font-medium\", // Extract font-medium\n getRadiusClass(\"md\"),\n \"transition-colors\",\n \"focus:outline-none\",\n \"focus:ring-2\",\n \"focus:ring-offset-2\",\n \"disabled:opacity-50\",\n \"disabled:cursor-not-allowed\",\n ),\n {\n variants: {\n variant: {\n primary: cn(\n \"bg-surface-brand-strong\",\n \"text-fg-inverse\",\n \"hover:opacity-90\",\n \"focus:ring-line-brand\",\n ),\n secondary: cn(\n \"bg-surface-secondary\",\n \"text-fg-inverse\",\n \"hover:opacity-90\",\n \"focus:ring-line-secondary\",\n ),\n error: cn(\n \"bg-error\",\n \"text-fg-inverse\",\n \"hover:opacity-90\",\n \"focus:ring-error\",\n ),\n outline: cn(\n \"border-2\",\n \"border-line-default\",\n \"bg-transparent\",\n \"text-fg-primary\",\n \"hover:bg-surface-hover\",\n \"focus:ring-line-focus\",\n ),\n ghost: cn(\n \"bg-transparent\",\n \"text-fg-primary\",\n \"hover:bg-surface-hover\",\n \"focus:ring-line-focus\",\n ),\n iconOnly: cn(\n \"bg-transparent\",\n \"text-fg-primary\",\n \"hover:bg-surface-hover\",\n \"focus:ring-line-focus\",\n getSpacingClass(\"none\", \"p\"),\n ),\n // Textual call-to-action — brand-coloured text, underline on\n // hover, no chrome (no surface, no border). Padding is zeroed\n // out per size in compoundVariants so the bounding box hugs\n // the text (height intrinsic). Issue #156.\n //\n // Focus ring: `focus:ring-line-focus` keeps the same focus\n // family the other no-chrome variants use (ghost, outline,\n // iconOnly). The base's `focus:ring-2` + `focus:ring-offset-2`\n // produce a 2px ring 2px away from the text bounding box; the\n // 2px offset is rendered in surface-base so the ring contrasts\n // against surface (≥3:1 for WCAG 2.4.11, verified) rather than\n // colliding with the brand-coloured text. The element keeps\n // `getRadiusClass(\"md\")` from the base, which is invisible\n // without chrome but rounds the focus ring corners.\n //\n // hover:underline pairs with `underline-offset-4` so the\n // underline that appears on hover sits clear of the descenders\n // (WCAG-aligned with link best practice). At rest the link\n // carries no underline — the brand colour alone signals\n // affordance, matching the shadcn convention this variant is\n // modelled on (see issue body).\n //\n // disabled: inherits opacity-50 + cursor-not-allowed from the\n // base. We do NOT special-case `disabled:no-underline` because\n // a disabled link should still receive the same visual\n // treatment as a disabled chrome variant — the opacity and\n // cursor signal the disabled state.\n link: cn(\n \"bg-transparent\",\n \"text-fg-brand\",\n \"underline-offset-4\",\n \"hover:underline\",\n \"focus:ring-line-focus\",\n ),\n },\n size: {\n sm: cn(\n getSpacingClass(\"md\", \"px\"),\n getSpacingClass(\"1.5\", \"py\"),\n getTypographySize(\"bodySmall\"),\n getSpacingClass(\"1.5\", \"gap\"),\n ),\n md: cn(\n getSpacingClass(\"base\", \"px\"),\n getSpacingClass(\"sm\", \"py\"),\n getTypographySize(\"body\"),\n getSpacingClass(\"sm\", \"gap\"),\n ),\n lg: cn(\n getSpacingClass(\"lg\", \"px\"),\n getSpacingClass(\"md\", \"py\"),\n getTypographySize(\"bodyLarge\"),\n getSpacingClass(\"2.5\", \"gap\"),\n ),\n },\n },\n compoundVariants: [\n // IconOnly variant has different sizing\n {\n variant: \"iconOnly\",\n size: \"sm\",\n class: cn(\"h-8\", \"w-8\", getSpacingClass(\"none\", \"p\")),\n },\n {\n variant: \"iconOnly\",\n size: \"md\",\n class: cn(\"h-10\", \"w-10\", getSpacingClass(\"none\", \"p\")),\n },\n {\n variant: \"iconOnly\",\n size: \"lg\",\n class: cn(\"h-12\", \"w-12\", getSpacingClass(\"none\", \"p\")),\n },\n // Link variant zeroes the size's padding via compoundVariants so\n // the override runs AFTER the size block's `px-N`/`py-N` and\n // twMerge picks the zero value (`cn` joins variant before\n // compound). The size's typography and gap survive — the link's\n // text scales with size and icons still get gap-N when present.\n // Issue #156.\n {\n variant: \"link\",\n size: \"sm\",\n class: cn(getSpacingClass(\"none\", \"px\"), getSpacingClass(\"none\", \"py\")),\n },\n {\n variant: \"link\",\n size: \"md\",\n class: cn(getSpacingClass(\"none\", \"px\"), getSpacingClass(\"none\", \"py\")),\n },\n {\n variant: \"link\",\n size: \"lg\",\n class: cn(getSpacingClass(\"none\", \"px\"), getSpacingClass(\"none\", \"py\")),\n },\n ],\n defaultVariants: {\n variant: \"primary\",\n size: \"md\",\n },\n },\n);\n\n/**\n * Icon Wrapper Component\n * Handles icon spacing and alignment consistently\n */\nfunction IconWrapper({\n children,\n position,\n}: {\n children: ReactNode;\n position: \"left\" | \"right\";\n}) {\n if (!children) return null;\n\n return (\n <span\n className={`inline-flex items-center ${position === \"left\" ? getSpacingClass(\"none\", \"mr\") : getSpacingClass(\"none\", \"ml\")}`}\n >\n {children}\n </span>\n );\n}\n\n/**\n * Button Component\n *\n * A styled button with variants, sizes, and loading states.\n *\n * Polymorphism — two APIs:\n * - `as` (legacy): swap the root element type. `<Button as={Link} href=\"…\">`.\n * - `asChild` (recommended): project Button's styling onto the single\n * child element. Idiomatic Radix Slot pattern, preserves the child's\n * own props and TS type (e.g. `<Link href prefetch>`). When `asChild`\n * is true, `as` is ignored.\n *\n * @example\n * ```tsx\n * // Basic usage\n * <Button variant=\"primary\" size=\"md\" onClick={handleClick}>Click me</Button>\n *\n * // With icons\n * <Button leftIcon={<Icon />} rightIcon={<Icon />}>Action</Button>\n *\n * // Loading state\n * <Button isLoading loadingText=\"Saving...\">Save</Button>\n *\n * // Polymorphic via asChild (preserves Next Link's TS type and native props)\n * <Button asChild variant=\"primary\">\n * <Link href=\"/page\" prefetch>Open</Link>\n * </Button>\n *\n * // Polymorphic via legacy `as`\n * <Button as=\"a\" href=\"/page\">Navigate</Button>\n * ```\n */\nconst Button = memo(\n forwardRef<HTMLButtonElement, ButtonProps>(function Button(\n {\n variant = \"primary\",\n size = \"md\",\n isLoading = false,\n loadingText,\n loadingIcon,\n leftIcon,\n rightIcon,\n fullWidth = false,\n asChild = false,\n as,\n className = \"\",\n disabled = false,\n children,\n \"aria-label\": ariaLabel,\n ...props\n },\n ref,\n ) {\n // asChild wins over `as`. Warn in dev so the precedence is observable\n // instead of being a silent override. Production builds strip this branch.\n if (\n typeof process !== \"undefined\" &&\n process.env.NODE_ENV !== \"production\" &&\n asChild &&\n as !== undefined &&\n as !== \"button\"\n ) {\n console.warn(\n \"[Button] `as` is ignored when `asChild` is true; the child element is used as the root. Drop one of the two props to silence this warning.\",\n );\n }\n\n const Component: ElementType = asChild ? Slot : (as ?? \"button\");\n\n // These were `useMemo` originally — all decorative (class-string\n // concat, small boolean/string/JSX picks), none gating render-driving\n // state. Inlined so Button carries NO React client API and qualifies\n // for the `./server` entry (issue #224), the same de-memoization\n // precedent as Badge/Label/Card in #155. `React.memo` on the\n // component is preserved and still gates re-renders at the\n // consumer-prop boundary; the inlined work is nanoseconds.\n const classes = cn(\n buttonVariants({ variant, size }),\n fullWidth && \"w-full\",\n className,\n );\n\n const isIconOnly =\n variant === \"iconOnly\" || (!children && (leftIcon || rightIcon));\n\n const finalAriaLabel =\n isIconOnly && !ariaLabel && !children\n ? \"Button\" // Fallback, but should be provided\n : ariaLabel;\n\n const spinnerVariant: \"primary\" | \"secondary\" | \"neutral\" =\n variant === \"error\"\n ? \"primary\" // Red buttons use primary spinner (white)\n : variant === \"primary\" || variant === \"secondary\"\n ? \"neutral\" // Colored buttons use neutral spinner\n : \"primary\";\n\n const spinnerSize = size === \"sm\" ? \"sm\" : size === \"lg\" ? \"lg\" : \"md\";\n\n const displayLoadingIcon = loadingIcon || (\n <Spinner size={spinnerSize} variant={spinnerVariant} />\n );\n\n // Build button props (spread props at the end to allow overrides).\n // `type=\"button\"` default applies only when rendering an intrinsic\n // <button>. asChild and `as` (custom) projections leave it off so we\n // don't paint a meaningless `type` attribute onto <a> / <Link>.\n const defaultType =\n !asChild && (as === undefined || as === \"button\") && !props.type\n ? \"button\"\n : undefined;\n const buttonProps = {\n className: classes,\n disabled: disabled || isLoading,\n \"aria-busy\": isLoading,\n \"aria-label\": finalAriaLabel,\n \"aria-disabled\": disabled || isLoading,\n ...(defaultType ? { type: defaultType } : {}),\n ...props,\n };\n\n // asChild path: render Slot with the icons and Slottable as direct\n // sibling children (NOT wrapped in a React.Fragment). Slot's\n // children-processing inspects each direct child for Slottable; if\n // the children are wrapped in a Fragment, Slot's SlotClone merges\n // the projected props into the Fragment itself (a silent no-op for\n // className / aria / ref), and nothing reaches the consumer's\n // element. Hence the two distinct branches below: same content\n // shape, different host element, different child layout.\n if (asChild) {\n return (\n <Component ref={ref} {...buttonProps}>\n {leftIcon && <IconWrapper position=\"left\">{leftIcon}</IconWrapper>}\n <Slottable>{children}</Slottable>\n {rightIcon && <IconWrapper position=\"right\">{rightIcon}</IconWrapper>}\n </Component>\n );\n }\n\n return (\n <Component ref={ref} {...buttonProps}>\n {isLoading ? (\n <>\n {displayLoadingIcon}\n {loadingText && (\n <span className={getSpacingClass(\"sm\", \"ml\")}>{loadingText}</span>\n )}\n {!loadingText && children && (\n <span className={`${getSpacingClass(\"sm\", \"ml\")} opacity-0`}>\n {children}\n </span>\n )}\n </>\n ) : (\n <>\n {leftIcon && <IconWrapper position=\"left\">{leftIcon}</IconWrapper>}\n {children}\n {rightIcon && (\n <IconWrapper position=\"right\">{rightIcon}</IconWrapper>\n )}\n </>\n )}\n </Component>\n );\n }),\n);\n\nButton.displayName = \"Button\";\n\nexport default Button;\nexport { Button };\n"],"names":["buttonVariants","cva","cn","getTypographyClasses","getRadiusClass","getSpacingClass","getTypographySize","IconWrapper","children","position","jsx","Button","memo","forwardRef","_a","ref","_b","variant","size","isLoading","loadingText","loadingIcon","leftIcon","rightIcon","fullWidth","asChild","as","className","disabled","ariaLabel","props","__objRest","Component","Slot","classes","finalAriaLabel","displayLoadingIcon","Spinner","defaultType","buttonProps","__spreadValues","jsxs","__spreadProps","Slottable","Fragment"],"mappings":"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"}
|