@fanvue/ui 1.21.0 → 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/dist/charts.d.ts +1 -1
- package/dist/cjs/components/Accordion/AccordionContent.cjs +1 -1
- package/dist/cjs/components/Accordion/AccordionContent.cjs.map +1 -1
- package/dist/cjs/components/Accordion/AccordionItem.cjs +1 -1
- package/dist/cjs/components/Accordion/AccordionItem.cjs.map +1 -1
- package/dist/cjs/components/Accordion/AccordionTrigger.cjs +5 -5
- package/dist/cjs/components/Accordion/AccordionTrigger.cjs.map +1 -1
- package/dist/cjs/components/Alert/Alert.cjs +11 -11
- package/dist/cjs/components/Alert/Alert.cjs.map +1 -1
- package/dist/cjs/components/AudioUpload/AudioUpload.cjs +12 -12
- package/dist/cjs/components/AudioUpload/AudioUpload.cjs.map +1 -1
- package/dist/cjs/components/AudioUpload/AudioWaveform.cjs +1 -1
- package/dist/cjs/components/AudioUpload/AudioWaveform.cjs.map +1 -1
- package/dist/cjs/components/Autocomplete/Autocomplete.cjs +12 -12
- package/dist/cjs/components/Autocomplete/Autocomplete.cjs.map +1 -1
- package/dist/cjs/components/Autocomplete/AutocompleteDropdownContent.cjs +3 -3
- package/dist/cjs/components/Autocomplete/AutocompleteDropdownContent.cjs.map +1 -1
- package/dist/cjs/components/Autocomplete/AutocompleteOptionItem.cjs +3 -3
- package/dist/cjs/components/Autocomplete/AutocompleteOptionItem.cjs.map +1 -1
- package/dist/cjs/components/Autocomplete/AutocompleteTag.cjs +2 -2
- package/dist/cjs/components/Autocomplete/AutocompleteTag.cjs.map +1 -1
- package/dist/cjs/components/Avatar/Avatar.cjs +3 -3
- package/dist/cjs/components/Avatar/Avatar.cjs.map +1 -1
- package/dist/cjs/components/Badge/Badge.cjs +23 -23
- package/dist/cjs/components/Badge/Badge.cjs.map +1 -1
- package/dist/cjs/components/Banner/Banner.cjs +4 -4
- package/dist/cjs/components/Banner/Banner.cjs.map +1 -1
- package/dist/cjs/components/BottomNavigation/BottomNavigation.cjs +1 -1
- package/dist/cjs/components/BottomNavigation/BottomNavigation.cjs.map +1 -1
- package/dist/cjs/components/BottomNavigation/BottomNavigationAction.cjs +2 -2
- package/dist/cjs/components/BottomNavigation/BottomNavigationAction.cjs.map +1 -1
- package/dist/cjs/components/Breadcrumb/Breadcrumb.cjs +3 -3
- package/dist/cjs/components/Breadcrumb/Breadcrumb.cjs.map +1 -1
- package/dist/cjs/components/Button/Button.cjs +10 -10
- package/dist/cjs/components/Button/Button.cjs.map +1 -1
- package/dist/cjs/components/Card/Card.cjs +6 -6
- package/dist/cjs/components/Card/Card.cjs.map +1 -1
- package/dist/cjs/components/Chart/ChartCard.cjs +6 -6
- package/dist/cjs/components/Chart/ChartCard.cjs.map +1 -1
- package/dist/cjs/components/Chart/ChartCenterLabel.cjs +2 -2
- package/dist/cjs/components/Chart/ChartCenterLabel.cjs.map +1 -1
- package/dist/cjs/components/Chart/ChartContainer.cjs +7 -7
- package/dist/cjs/components/Chart/ChartContainer.cjs.map +1 -1
- package/dist/cjs/components/Chart/ChartLegend.cjs +1 -1
- package/dist/cjs/components/Chart/ChartLegend.cjs.map +1 -1
- package/dist/cjs/components/Chart/ChartLoadingOverlay.cjs +1 -1
- package/dist/cjs/components/Chart/ChartLoadingOverlay.cjs.map +1 -1
- package/dist/cjs/components/Chart/ChartPieLegend.cjs +2 -2
- package/dist/cjs/components/Chart/ChartPieLegend.cjs.map +1 -1
- package/dist/cjs/components/Chart/ChartSeriesToggle.cjs +2 -2
- package/dist/cjs/components/Chart/ChartSeriesToggle.cjs.map +1 -1
- package/dist/cjs/components/Chart/ChartTooltip.cjs +4 -4
- package/dist/cjs/components/Chart/ChartTooltip.cjs.map +1 -1
- package/dist/cjs/components/Checkbox/Checkbox.cjs +13 -13
- package/dist/cjs/components/Checkbox/Checkbox.cjs.map +1 -1
- package/dist/cjs/components/Chip/Chip.cjs +7 -7
- package/dist/cjs/components/Chip/Chip.cjs.map +1 -1
- package/dist/cjs/components/Count/Count.cjs +7 -7
- package/dist/cjs/components/Count/Count.cjs.map +1 -1
- package/dist/cjs/components/DatePicker/DatePicker.cjs +14 -14
- package/dist/cjs/components/DatePicker/DatePicker.cjs.map +1 -1
- package/dist/cjs/components/Dialog/Dialog.cjs +6 -6
- package/dist/cjs/components/Dialog/Dialog.cjs.map +1 -1
- package/dist/cjs/components/Divider/Divider.cjs +4 -4
- package/dist/cjs/components/Divider/Divider.cjs.map +1 -1
- package/dist/cjs/components/Drawer/Drawer.cjs +5 -5
- package/dist/cjs/components/Drawer/Drawer.cjs.map +1 -1
- package/dist/cjs/components/DropdownMenu/DropdownMenu.cjs +6 -6
- package/dist/cjs/components/DropdownMenu/DropdownMenu.cjs.map +1 -1
- package/dist/cjs/components/IconButton/IconButton.cjs +10 -10
- package/dist/cjs/components/IconButton/IconButton.cjs.map +1 -1
- package/dist/cjs/components/Icons/LockerOffIcon.cjs +1 -1
- package/dist/cjs/components/Icons/LockerOffIcon.cjs.map +1 -1
- package/dist/cjs/components/Icons/LockerOnIcon.cjs +1 -1
- package/dist/cjs/components/Icons/LockerOnIcon.cjs.map +1 -1
- package/dist/cjs/components/InfoBox/InfoBox.cjs +6 -6
- package/dist/cjs/components/InfoBox/InfoBox.cjs.map +1 -1
- package/dist/cjs/components/Loader/Loader.cjs +1 -1
- package/dist/cjs/components/Loader/Loader.cjs.map +1 -1
- package/dist/cjs/components/Logo/Logo.cjs +13 -13
- package/dist/cjs/components/Logo/Logo.cjs.map +1 -1
- package/dist/cjs/components/MobileStepper/MobileStepper.cjs +2 -2
- package/dist/cjs/components/MobileStepper/MobileStepper.cjs.map +1 -1
- package/dist/cjs/components/OnlineBlinkingIcon/OnlineBlinkingIcon.cjs +2 -2
- package/dist/cjs/components/OnlineBlinkingIcon/OnlineBlinkingIcon.cjs.map +1 -1
- package/dist/cjs/components/Pagination/Pagination.cjs +3 -3
- package/dist/cjs/components/Pagination/Pagination.cjs.map +1 -1
- package/dist/cjs/components/PasswordField/PasswordField.cjs +1 -1
- package/dist/cjs/components/PasswordField/PasswordField.cjs.map +1 -1
- package/dist/cjs/components/Pill/Pill.cjs +10 -10
- package/dist/cjs/components/Pill/Pill.cjs.map +1 -1
- package/dist/cjs/components/ProgressBar/ProgressBar.cjs +13 -13
- package/dist/cjs/components/ProgressBar/ProgressBar.cjs.map +1 -1
- package/dist/cjs/components/Radio/Radio.cjs +4 -4
- package/dist/cjs/components/Radio/Radio.cjs.map +1 -1
- package/dist/cjs/components/Select/Select.cjs +13 -13
- package/dist/cjs/components/Select/Select.cjs.map +1 -1
- package/dist/cjs/components/Skeleton/Skeleton.cjs +2 -2
- package/dist/cjs/components/Skeleton/Skeleton.cjs.map +1 -1
- package/dist/cjs/components/Slider/Slider.cjs +1 -1
- package/dist/cjs/components/Slider/Slider.cjs.map +1 -1
- package/dist/cjs/components/Slider/SliderLayout.cjs +5 -12
- package/dist/cjs/components/Slider/SliderLayout.cjs.map +1 -1
- package/dist/cjs/components/Slider/SliderThumb.cjs +6 -6
- package/dist/cjs/components/Slider/SliderThumb.cjs.map +1 -1
- package/dist/cjs/components/Snackbar/Snackbar.cjs +9 -9
- package/dist/cjs/components/Snackbar/Snackbar.cjs.map +1 -1
- package/dist/cjs/components/Switch/Switch.cjs +3 -3
- package/dist/cjs/components/Switch/Switch.cjs.map +1 -1
- package/dist/cjs/components/SwitchField/SwitchField.cjs +5 -5
- package/dist/cjs/components/SwitchField/SwitchField.cjs.map +1 -1
- package/dist/cjs/components/SwitchToggle/SwitchToggle.cjs +4 -4
- package/dist/cjs/components/SwitchToggle/SwitchToggle.cjs.map +1 -1
- package/dist/cjs/components/Tabs/TabsList.cjs +3 -3
- package/dist/cjs/components/Tabs/TabsList.cjs.map +1 -1
- package/dist/cjs/components/Tabs/TabsTrigger.cjs +8 -8
- package/dist/cjs/components/Tabs/TabsTrigger.cjs.map +1 -1
- package/dist/cjs/components/TextArea/TextArea.cjs +7 -7
- package/dist/cjs/components/TextArea/TextArea.cjs.map +1 -1
- package/dist/cjs/components/TextField/TextField.cjs +11 -11
- package/dist/cjs/components/TextField/TextField.cjs.map +1 -1
- package/dist/cjs/components/Toast/Toast.cjs +7 -7
- package/dist/cjs/components/Toast/Toast.cjs.map +1 -1
- package/dist/cjs/components/Tooltip/Tooltip.cjs +1 -1
- package/dist/cjs/components/Tooltip/Tooltip.cjs.map +1 -1
- package/dist/components/Accordion/AccordionContent.mjs +1 -1
- package/dist/components/Accordion/AccordionContent.mjs.map +1 -1
- package/dist/components/Accordion/AccordionItem.mjs +1 -1
- package/dist/components/Accordion/AccordionItem.mjs.map +1 -1
- package/dist/components/Accordion/AccordionTrigger.mjs +5 -5
- package/dist/components/Accordion/AccordionTrigger.mjs.map +1 -1
- package/dist/components/Alert/Alert.mjs +11 -11
- package/dist/components/Alert/Alert.mjs.map +1 -1
- package/dist/components/AudioUpload/AudioUpload.mjs +12 -12
- package/dist/components/AudioUpload/AudioUpload.mjs.map +1 -1
- package/dist/components/AudioUpload/AudioWaveform.mjs +1 -1
- package/dist/components/AudioUpload/AudioWaveform.mjs.map +1 -1
- package/dist/components/Autocomplete/Autocomplete.mjs +12 -12
- package/dist/components/Autocomplete/Autocomplete.mjs.map +1 -1
- package/dist/components/Autocomplete/AutocompleteDropdownContent.mjs +3 -3
- package/dist/components/Autocomplete/AutocompleteDropdownContent.mjs.map +1 -1
- package/dist/components/Autocomplete/AutocompleteOptionItem.mjs +3 -3
- package/dist/components/Autocomplete/AutocompleteOptionItem.mjs.map +1 -1
- package/dist/components/Autocomplete/AutocompleteTag.mjs +2 -2
- package/dist/components/Autocomplete/AutocompleteTag.mjs.map +1 -1
- package/dist/components/Avatar/Avatar.mjs +3 -3
- package/dist/components/Avatar/Avatar.mjs.map +1 -1
- package/dist/components/Badge/Badge.mjs +23 -23
- package/dist/components/Badge/Badge.mjs.map +1 -1
- package/dist/components/Banner/Banner.mjs +4 -4
- package/dist/components/Banner/Banner.mjs.map +1 -1
- package/dist/components/BottomNavigation/BottomNavigation.mjs +1 -1
- package/dist/components/BottomNavigation/BottomNavigation.mjs.map +1 -1
- package/dist/components/BottomNavigation/BottomNavigationAction.mjs +2 -2
- package/dist/components/BottomNavigation/BottomNavigationAction.mjs.map +1 -1
- package/dist/components/Breadcrumb/Breadcrumb.mjs +3 -3
- package/dist/components/Breadcrumb/Breadcrumb.mjs.map +1 -1
- package/dist/components/Button/Button.mjs +10 -10
- package/dist/components/Button/Button.mjs.map +1 -1
- package/dist/components/Card/Card.mjs +6 -6
- package/dist/components/Card/Card.mjs.map +1 -1
- package/dist/components/Chart/ChartCard.mjs +6 -6
- package/dist/components/Chart/ChartCard.mjs.map +1 -1
- package/dist/components/Chart/ChartCenterLabel.mjs +2 -2
- package/dist/components/Chart/ChartCenterLabel.mjs.map +1 -1
- package/dist/components/Chart/ChartContainer.mjs +7 -7
- package/dist/components/Chart/ChartContainer.mjs.map +1 -1
- package/dist/components/Chart/ChartLegend.mjs +1 -1
- package/dist/components/Chart/ChartLegend.mjs.map +1 -1
- package/dist/components/Chart/ChartLoadingOverlay.mjs +1 -1
- package/dist/components/Chart/ChartLoadingOverlay.mjs.map +1 -1
- package/dist/components/Chart/ChartPieLegend.mjs +2 -2
- package/dist/components/Chart/ChartPieLegend.mjs.map +1 -1
- package/dist/components/Chart/ChartSeriesToggle.mjs +2 -2
- package/dist/components/Chart/ChartSeriesToggle.mjs.map +1 -1
- package/dist/components/Chart/ChartTooltip.mjs +4 -4
- package/dist/components/Chart/ChartTooltip.mjs.map +1 -1
- package/dist/components/Checkbox/Checkbox.mjs +13 -13
- package/dist/components/Checkbox/Checkbox.mjs.map +1 -1
- package/dist/components/Chip/Chip.mjs +7 -7
- package/dist/components/Chip/Chip.mjs.map +1 -1
- package/dist/components/Count/Count.mjs +7 -7
- package/dist/components/Count/Count.mjs.map +1 -1
- package/dist/components/DatePicker/DatePicker.mjs +14 -14
- package/dist/components/DatePicker/DatePicker.mjs.map +1 -1
- package/dist/components/Dialog/Dialog.mjs +6 -6
- package/dist/components/Dialog/Dialog.mjs.map +1 -1
- package/dist/components/Divider/Divider.mjs +4 -4
- package/dist/components/Divider/Divider.mjs.map +1 -1
- package/dist/components/Drawer/Drawer.mjs +5 -5
- package/dist/components/Drawer/Drawer.mjs.map +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.mjs +6 -6
- package/dist/components/DropdownMenu/DropdownMenu.mjs.map +1 -1
- package/dist/components/IconButton/IconButton.mjs +10 -10
- package/dist/components/IconButton/IconButton.mjs.map +1 -1
- package/dist/components/Icons/LockerOffIcon.mjs +1 -1
- package/dist/components/Icons/LockerOffIcon.mjs.map +1 -1
- package/dist/components/Icons/LockerOnIcon.mjs +1 -1
- package/dist/components/Icons/LockerOnIcon.mjs.map +1 -1
- package/dist/components/InfoBox/InfoBox.mjs +6 -6
- package/dist/components/InfoBox/InfoBox.mjs.map +1 -1
- package/dist/components/Loader/Loader.mjs +1 -1
- package/dist/components/Loader/Loader.mjs.map +1 -1
- package/dist/components/Logo/Logo.mjs +13 -13
- package/dist/components/Logo/Logo.mjs.map +1 -1
- package/dist/components/MobileStepper/MobileStepper.mjs +2 -2
- package/dist/components/MobileStepper/MobileStepper.mjs.map +1 -1
- package/dist/components/OnlineBlinkingIcon/OnlineBlinkingIcon.mjs +2 -2
- package/dist/components/OnlineBlinkingIcon/OnlineBlinkingIcon.mjs.map +1 -1
- package/dist/components/Pagination/Pagination.mjs +3 -3
- package/dist/components/Pagination/Pagination.mjs.map +1 -1
- package/dist/components/PasswordField/PasswordField.mjs +1 -1
- package/dist/components/PasswordField/PasswordField.mjs.map +1 -1
- package/dist/components/Pill/Pill.mjs +10 -10
- package/dist/components/Pill/Pill.mjs.map +1 -1
- package/dist/components/ProgressBar/ProgressBar.mjs +13 -13
- package/dist/components/ProgressBar/ProgressBar.mjs.map +1 -1
- package/dist/components/Radio/Radio.mjs +4 -4
- package/dist/components/Radio/Radio.mjs.map +1 -1
- package/dist/components/Select/Select.mjs +13 -13
- package/dist/components/Select/Select.mjs.map +1 -1
- package/dist/components/Skeleton/Skeleton.mjs +2 -2
- package/dist/components/Skeleton/Skeleton.mjs.map +1 -1
- package/dist/components/Slider/Slider.mjs +1 -1
- package/dist/components/Slider/Slider.mjs.map +1 -1
- package/dist/components/Slider/SliderLayout.mjs +5 -12
- package/dist/components/Slider/SliderLayout.mjs.map +1 -1
- package/dist/components/Slider/SliderThumb.mjs +6 -6
- package/dist/components/Slider/SliderThumb.mjs.map +1 -1
- package/dist/components/Snackbar/Snackbar.mjs +9 -9
- package/dist/components/Snackbar/Snackbar.mjs.map +1 -1
- package/dist/components/Switch/Switch.mjs +3 -3
- package/dist/components/Switch/Switch.mjs.map +1 -1
- package/dist/components/SwitchField/SwitchField.mjs +5 -5
- package/dist/components/SwitchField/SwitchField.mjs.map +1 -1
- package/dist/components/SwitchToggle/SwitchToggle.mjs +4 -4
- package/dist/components/SwitchToggle/SwitchToggle.mjs.map +1 -1
- package/dist/components/Tabs/TabsList.mjs +3 -3
- package/dist/components/Tabs/TabsList.mjs.map +1 -1
- package/dist/components/Tabs/TabsTrigger.mjs +8 -8
- package/dist/components/Tabs/TabsTrigger.mjs.map +1 -1
- package/dist/components/TextArea/TextArea.mjs +7 -7
- package/dist/components/TextArea/TextArea.mjs.map +1 -1
- package/dist/components/TextField/TextField.mjs +11 -11
- package/dist/components/TextField/TextField.mjs.map +1 -1
- package/dist/components/Toast/Toast.mjs +7 -7
- package/dist/components/Toast/Toast.mjs.map +1 -1
- package/dist/components/Tooltip/Tooltip.mjs +1 -1
- package/dist/components/Tooltip/Tooltip.mjs.map +1 -1
- package/dist/styles/theme.css +378 -253
- package/package.json +1 -1
|
@@ -51,7 +51,7 @@ const SwitchToggle = React.forwardRef(
|
|
|
51
51
|
ref,
|
|
52
52
|
role: "radiogroup",
|
|
53
53
|
className: cn(
|
|
54
|
-
"relative inline-grid grid-cols-2 rounded-full border border-neutral-200 p-1",
|
|
54
|
+
"relative inline-grid grid-cols-2 rounded-full border border-neutral-alphas-200 p-1",
|
|
55
55
|
disabled && "cursor-not-allowed opacity-50",
|
|
56
56
|
className
|
|
57
57
|
),
|
|
@@ -62,7 +62,7 @@ const SwitchToggle = React.forwardRef(
|
|
|
62
62
|
{
|
|
63
63
|
"aria-hidden": "true",
|
|
64
64
|
className: cn(
|
|
65
|
-
"absolute inset-y-1 left-1 w-[calc(50%-4px)] rounded-full border border-brand-
|
|
65
|
+
"absolute inset-y-1 left-1 w-[calc(50%-4px)] rounded-full border border-brand-primary-default bg-brand-primary-muted",
|
|
66
66
|
"motion-safe:transition-transform motion-safe:duration-200 motion-safe:ease-in-out",
|
|
67
67
|
isSecondSelected && "translate-x-full"
|
|
68
68
|
)
|
|
@@ -86,9 +86,9 @@ const SwitchToggle = React.forwardRef(
|
|
|
86
86
|
onClick: () => handleSelect(option.value),
|
|
87
87
|
onKeyDown: (e) => handleKeyDown(e, index),
|
|
88
88
|
className: cn(
|
|
89
|
-
"relative z-10 inline-flex min-w-0 cursor-pointer items-center justify-center rounded-full border border-transparent text-
|
|
89
|
+
"relative z-10 inline-flex min-w-0 cursor-pointer items-center justify-center rounded-full border border-transparent text-content-primary",
|
|
90
90
|
"focus-visible:shadow-focus-ring focus-visible:outline-none",
|
|
91
|
-
"active:rounded-full active:bg-brand-
|
|
91
|
+
"active:rounded-full active:bg-brand-primary-muted",
|
|
92
92
|
disabled && "pointer-events-none",
|
|
93
93
|
sizeClass
|
|
94
94
|
),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SwitchToggle.mjs","sources":["../../../src/components/SwitchToggle/SwitchToggle.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Height of the switch toggle in pixels. */\nexport type SwitchToggleSize = \"24\" | \"32\" | \"40\";\n\n/** Describes one side of the binary toggle. */\nexport interface SwitchToggleOption {\n /** Display label for the option. */\n label: string;\n /** Value identifier returned via `onChange`. */\n value: string;\n}\n\nexport interface SwitchToggleProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n /** Height of the toggle in pixels. @default \"24\" */\n size?: SwitchToggleSize;\n /** The two options to toggle between (exactly two required). */\n options: [SwitchToggleOption, SwitchToggleOption];\n /** Currently selected value (controlled). */\n value?: string;\n /** Initially selected value (uncontrolled). */\n defaultValue?: string;\n /** Callback fired when the selected value changes. */\n onChange?: (value: string) => void;\n /** Whether the toggle is disabled. @default false */\n disabled?: boolean;\n}\n\nfunction warnMissingAccessibleName(ariaLabel?: string, ariaLabelledBy?: string) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!ariaLabel && !ariaLabelledBy) {\n console.warn(\n \"SwitchToggle: no accessible name provided. Pass an `aria-label` or `aria-labelledby` prop.\",\n );\n }\n }\n}\n\n/**\n * A binary segmented toggle rendered as a `radiogroup`. The active option is\n * highlighted with a sliding pill indicator. Supports both controlled and\n * uncontrolled usage.\n *\n * @example\n * ```tsx\n * <SwitchToggle\n * options={[\n * { label: \"Monthly\", value: \"monthly\" },\n * { label: \"Yearly\", value: \"yearly\" },\n * ]}\n * value={billing}\n * onChange={setBilling}\n * />\n * ```\n */\nexport const SwitchToggle = React.forwardRef<HTMLDivElement, SwitchToggleProps>(\n (\n {\n className,\n size = \"24\",\n options,\n value: controlledValue,\n defaultValue,\n onChange,\n disabled = false,\n ...props\n },\n ref,\n ) => {\n warnMissingAccessibleName(props[\"aria-label\"], props[\"aria-labelledby\"]);\n\n // Tracks selection for uncontrolled usage; ignored when `value` prop is provided\n const [internalValue, setInternalValue] = React.useState(defaultValue ?? options[0].value);\n const isControlled = controlledValue !== undefined;\n const currentValue = isControlled ? controlledValue : internalValue;\n const anySelected = options.some((o) => o.value === currentValue);\n const isSecondSelected = currentValue === options[1].value;\n const buttonRefs = React.useRef<(HTMLButtonElement | null)[]>([]);\n\n const sizeClass =\n size === \"24\"\n ? \"px-2 py-1 typography-semibold-body-sm\"\n : size === \"32\"\n ? \"px-3 py-1.75 typography-semibold-body-md\"\n : \"h-10 px-4 py-2.25 typography-semibold-body-lg\";\n\n const handleSelect = (optionValue: string) => {\n if (disabled || optionValue === currentValue) return;\n if (!isControlled) {\n setInternalValue(optionValue);\n }\n onChange?.(optionValue);\n };\n\n const handleKeyDown = (e: React.KeyboardEvent, index: number) => {\n const nextIndex =\n e.key === \"ArrowRight\" || e.key === \"ArrowDown\"\n ? (index + 1) % options.length\n : e.key === \"ArrowLeft\" || e.key === \"ArrowUp\"\n ? (index - 1 + options.length) % options.length\n : null;\n if (nextIndex === null) return;\n e.preventDefault();\n const nextOption = options[nextIndex] as (typeof options)[number];\n handleSelect(nextOption.value);\n buttonRefs.current[nextIndex]?.focus();\n };\n\n return (\n <div\n ref={ref}\n role=\"radiogroup\"\n className={cn(\n \"relative inline-grid grid-cols-2 rounded-full border border-neutral-200 p-1\",\n disabled && \"cursor-not-allowed opacity-50\",\n className,\n )}\n {...props}\n >\n <span\n aria-hidden=\"true\"\n className={cn(\n \"absolute inset-y-1 left-1 w-[calc(50%-4px)] rounded-full border border-brand-
|
|
1
|
+
{"version":3,"file":"SwitchToggle.mjs","sources":["../../../src/components/SwitchToggle/SwitchToggle.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Height of the switch toggle in pixels. */\nexport type SwitchToggleSize = \"24\" | \"32\" | \"40\";\n\n/** Describes one side of the binary toggle. */\nexport interface SwitchToggleOption {\n /** Display label for the option. */\n label: string;\n /** Value identifier returned via `onChange`. */\n value: string;\n}\n\nexport interface SwitchToggleProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n /** Height of the toggle in pixels. @default \"24\" */\n size?: SwitchToggleSize;\n /** The two options to toggle between (exactly two required). */\n options: [SwitchToggleOption, SwitchToggleOption];\n /** Currently selected value (controlled). */\n value?: string;\n /** Initially selected value (uncontrolled). */\n defaultValue?: string;\n /** Callback fired when the selected value changes. */\n onChange?: (value: string) => void;\n /** Whether the toggle is disabled. @default false */\n disabled?: boolean;\n}\n\nfunction warnMissingAccessibleName(ariaLabel?: string, ariaLabelledBy?: string) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!ariaLabel && !ariaLabelledBy) {\n console.warn(\n \"SwitchToggle: no accessible name provided. Pass an `aria-label` or `aria-labelledby` prop.\",\n );\n }\n }\n}\n\n/**\n * A binary segmented toggle rendered as a `radiogroup`. The active option is\n * highlighted with a sliding pill indicator. Supports both controlled and\n * uncontrolled usage.\n *\n * @example\n * ```tsx\n * <SwitchToggle\n * options={[\n * { label: \"Monthly\", value: \"monthly\" },\n * { label: \"Yearly\", value: \"yearly\" },\n * ]}\n * value={billing}\n * onChange={setBilling}\n * />\n * ```\n */\nexport const SwitchToggle = React.forwardRef<HTMLDivElement, SwitchToggleProps>(\n (\n {\n className,\n size = \"24\",\n options,\n value: controlledValue,\n defaultValue,\n onChange,\n disabled = false,\n ...props\n },\n ref,\n ) => {\n warnMissingAccessibleName(props[\"aria-label\"], props[\"aria-labelledby\"]);\n\n // Tracks selection for uncontrolled usage; ignored when `value` prop is provided\n const [internalValue, setInternalValue] = React.useState(defaultValue ?? options[0].value);\n const isControlled = controlledValue !== undefined;\n const currentValue = isControlled ? controlledValue : internalValue;\n const anySelected = options.some((o) => o.value === currentValue);\n const isSecondSelected = currentValue === options[1].value;\n const buttonRefs = React.useRef<(HTMLButtonElement | null)[]>([]);\n\n const sizeClass =\n size === \"24\"\n ? \"px-2 py-1 typography-semibold-body-sm\"\n : size === \"32\"\n ? \"px-3 py-1.75 typography-semibold-body-md\"\n : \"h-10 px-4 py-2.25 typography-semibold-body-lg\";\n\n const handleSelect = (optionValue: string) => {\n if (disabled || optionValue === currentValue) return;\n if (!isControlled) {\n setInternalValue(optionValue);\n }\n onChange?.(optionValue);\n };\n\n const handleKeyDown = (e: React.KeyboardEvent, index: number) => {\n const nextIndex =\n e.key === \"ArrowRight\" || e.key === \"ArrowDown\"\n ? (index + 1) % options.length\n : e.key === \"ArrowLeft\" || e.key === \"ArrowUp\"\n ? (index - 1 + options.length) % options.length\n : null;\n if (nextIndex === null) return;\n e.preventDefault();\n const nextOption = options[nextIndex] as (typeof options)[number];\n handleSelect(nextOption.value);\n buttonRefs.current[nextIndex]?.focus();\n };\n\n return (\n <div\n ref={ref}\n role=\"radiogroup\"\n className={cn(\n \"relative inline-grid grid-cols-2 rounded-full border border-neutral-alphas-200 p-1\",\n disabled && \"cursor-not-allowed opacity-50\",\n className,\n )}\n {...props}\n >\n <span\n aria-hidden=\"true\"\n className={cn(\n \"absolute inset-y-1 left-1 w-[calc(50%-4px)] rounded-full border border-brand-primary-default bg-brand-primary-muted\",\n \"motion-safe:transition-transform motion-safe:duration-200 motion-safe:ease-in-out\",\n isSecondSelected && \"translate-x-full\",\n )}\n />\n {options.map((option, index) => {\n const isSelected = currentValue === option.value;\n return (\n // biome-ignore lint/a11y/useSemanticElements: native radio inputs only allow Tab-focus on the checked item; buttons with roving tabindex give full keyboard navigation\n <button\n key={option.value}\n ref={(el) => {\n buttonRefs.current[index] = el;\n }}\n type=\"button\"\n role=\"radio\"\n aria-checked={isSelected}\n tabIndex={isSelected || (!anySelected && index === 0) ? 0 : -1}\n disabled={disabled}\n onClick={() => handleSelect(option.value)}\n onKeyDown={(e) => handleKeyDown(e, index)}\n className={cn(\n \"relative z-10 inline-flex min-w-0 cursor-pointer items-center justify-center rounded-full border border-transparent text-content-primary\",\n \"focus-visible:shadow-focus-ring focus-visible:outline-none\",\n \"active:rounded-full active:bg-brand-primary-muted\",\n disabled && \"pointer-events-none\",\n sizeClass,\n )}\n >\n <span className=\"min-w-0 truncate\">{option.label}</span>\n </button>\n );\n })}\n </div>\n );\n },\n);\n\nSwitchToggle.displayName = \"SwitchToggle\";\n"],"names":[],"mappings":";;;;AA6BA,SAAS,0BAA0B,WAAoB,gBAAyB;AAC9E,MAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,QAAI,CAAC,aAAa,CAAC,gBAAgB;AACjC,cAAQ;AAAA,QACN;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AACF;AAmBO,MAAM,eAAe,MAAM;AAAA,EAChC,CACE;AAAA,IACE;AAAA,IACA,OAAO;AAAA,IACP;AAAA,IACA,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,GAAG;AAAA,EAAA,GAEL,QACG;AACH,8BAA0B,MAAM,YAAY,GAAG,MAAM,iBAAiB,CAAC;AAGvE,UAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAS,gBAAgB,QAAQ,CAAC,EAAE,KAAK;AACzF,UAAM,eAAe,oBAAoB;AACzC,UAAM,eAAe,eAAe,kBAAkB;AACtD,UAAM,cAAc,QAAQ,KAAK,CAAC,MAAM,EAAE,UAAU,YAAY;AAChE,UAAM,mBAAmB,iBAAiB,QAAQ,CAAC,EAAE;AACrD,UAAM,aAAa,MAAM,OAAqC,EAAE;AAEhE,UAAM,YACJ,SAAS,OACL,0CACA,SAAS,OACP,6CACA;AAER,UAAM,eAAe,CAAC,gBAAwB;AAC5C,UAAI,YAAY,gBAAgB,aAAc;AAC9C,UAAI,CAAC,cAAc;AACjB,yBAAiB,WAAW;AAAA,MAC9B;AACA,iBAAW,WAAW;AAAA,IACxB;AAEA,UAAM,gBAAgB,CAAC,GAAwB,UAAkB;AAC/D,YAAM,YACJ,EAAE,QAAQ,gBAAgB,EAAE,QAAQ,eAC/B,QAAQ,KAAK,QAAQ,SACtB,EAAE,QAAQ,eAAe,EAAE,QAAQ,aAChC,QAAQ,IAAI,QAAQ,UAAU,QAAQ,SACvC;AACR,UAAI,cAAc,KAAM;AACxB,QAAE,eAAA;AACF,YAAM,aAAa,QAAQ,SAAS;AACpC,mBAAa,WAAW,KAAK;AAC7B,iBAAW,QAAQ,SAAS,GAAG,MAAA;AAAA,IACjC;AAEA,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL,WAAW;AAAA,UACT;AAAA,UACA,YAAY;AAAA,UACZ;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEJ,UAAA;AAAA,UAAA;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,eAAY;AAAA,cACZ,WAAW;AAAA,gBACT;AAAA,gBACA;AAAA,gBACA,oBAAoB;AAAA,cAAA;AAAA,YACtB;AAAA,UAAA;AAAA,UAED,QAAQ,IAAI,CAAC,QAAQ,UAAU;AAC9B,kBAAM,aAAa,iBAAiB,OAAO;AAC3C;AAAA;AAAA,cAEE;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBAEC,KAAK,CAAC,OAAO;AACX,+BAAW,QAAQ,KAAK,IAAI;AAAA,kBAC9B;AAAA,kBACA,MAAK;AAAA,kBACL,MAAK;AAAA,kBACL,gBAAc;AAAA,kBACd,UAAU,cAAe,CAAC,eAAe,UAAU,IAAK,IAAI;AAAA,kBAC5D;AAAA,kBACA,SAAS,MAAM,aAAa,OAAO,KAAK;AAAA,kBACxC,WAAW,CAAC,MAAM,cAAc,GAAG,KAAK;AAAA,kBACxC,WAAW;AAAA,oBACT;AAAA,oBACA;AAAA,oBACA;AAAA,oBACA,YAAY;AAAA,oBACZ;AAAA,kBAAA;AAAA,kBAGF,UAAA,oBAAC,QAAA,EAAK,WAAU,oBAAoB,iBAAO,MAAA,CAAM;AAAA,gBAAA;AAAA,gBAnB5C,OAAO;AAAA,cAAA;AAAA;AAAA,UAsBlB,CAAC;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAGP;AACF;AAEA,aAAa,cAAc;"}
|
|
@@ -59,8 +59,8 @@ const TabsList = React.forwardRef(({ className, children, fullWidth = true, ...p
|
|
|
59
59
|
className: cn(
|
|
60
60
|
"relative",
|
|
61
61
|
fullWidth ? "flex w-full [&>[role=tab]]:flex-1" : "inline-flex",
|
|
62
|
-
"data-[orientation=horizontal]:items-center data-[orientation=horizontal]:shadow-[inset_0_-1px_0_0_var(--color-neutral-200)]",
|
|
63
|
-
"data-[orientation=vertical]:flex-col data-[orientation=vertical]:shadow-[inset_-1px_0_0_0_var(--color-neutral-200)]",
|
|
62
|
+
"data-[orientation=horizontal]:items-center data-[orientation=horizontal]:shadow-[inset_0_-1px_0_0_var(--color-neutral-alphas-200)]",
|
|
63
|
+
"data-[orientation=vertical]:flex-col data-[orientation=vertical]:shadow-[inset_-1px_0_0_0_var(--color-neutral-alphas-200)]",
|
|
64
64
|
className
|
|
65
65
|
),
|
|
66
66
|
...props,
|
|
@@ -71,7 +71,7 @@ const TabsList = React.forwardRef(({ className, children, fullWidth = true, ...p
|
|
|
71
71
|
{
|
|
72
72
|
ref: indicatorRef,
|
|
73
73
|
"aria-hidden": true,
|
|
74
|
-
className: "pointer-events-none absolute rounded-full bg-brand-
|
|
74
|
+
className: "pointer-events-none absolute rounded-full bg-brand-primary-default motion-safe:transition-[transform,width,height] motion-safe:duration-200 motion-safe:ease-in-out",
|
|
75
75
|
style: { opacity: 0 }
|
|
76
76
|
}
|
|
77
77
|
)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TabsList.mjs","sources":["../../../src/components/Tabs/TabsList.tsx"],"sourcesContent":["import * as TabsPrimitive from \"@radix-ui/react-tabs\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Props for the {@link TabsList} component. */\nexport type TabsListProps = React.ComponentPropsWithoutRef<typeof TabsPrimitive.List> & {\n /** When `true` (the default), the tab list spans the full width of its container and each tab grows equally. Set to `false` for inline sizing. */\n fullWidth?: boolean;\n};\n\n/** Container for {@link TabsTrigger} elements. Renders a sliding active-tab indicator that animates between tabs. */\nexport const TabsList = React.forwardRef<\n React.ComponentRef<typeof TabsPrimitive.List>,\n TabsListProps\n>(({ className, children, fullWidth = true, ...props }, ref) => {\n const innerRef = React.useRef<HTMLDivElement>(null);\n const indicatorRef = React.useRef<HTMLSpanElement>(null);\n\n React.useImperativeHandle(ref, () => innerRef.current as HTMLDivElement);\n\n const updateIndicator = React.useCallback(() => {\n const list = innerRef.current;\n const indicator = indicatorRef.current;\n if (!list || !indicator) return;\n\n const activeTab = list.querySelector<HTMLElement>('[data-state=\"active\"]');\n if (!activeTab) {\n indicator.style.opacity = \"0\";\n return;\n }\n\n const isVertical = list.dataset.orientation === \"vertical\";\n\n indicator.style.opacity = \"1\";\n\n if (isVertical) {\n indicator.style.inset = `0 0 auto auto`;\n indicator.style.width = \"4px\";\n indicator.style.height = `${activeTab.offsetHeight}px`;\n indicator.style.transform = `translateY(${activeTab.offsetTop}px)`;\n } else {\n indicator.style.inset = `auto auto 0 0`;\n indicator.style.height = \"4px\";\n indicator.style.width = `${activeTab.offsetWidth}px`;\n indicator.style.transform = `translateX(${activeTab.offsetLeft}px)`;\n }\n }, []);\n\n React.useLayoutEffect(() => {\n const list = innerRef.current;\n const indicator = indicatorRef.current;\n if (!list || !indicator) return;\n\n indicator.style.transitionDuration = \"0s\";\n updateIndicator();\n indicator.getBoundingClientRect();\n indicator.style.transitionDuration = \"\";\n\n const mutationObserver = new MutationObserver(updateIndicator);\n mutationObserver.observe(list, {\n attributes: true,\n attributeFilter: [\"data-state\"],\n childList: true,\n subtree: true,\n });\n\n const resizeObserver = new ResizeObserver(updateIndicator);\n resizeObserver.observe(list);\n\n return () => {\n mutationObserver.disconnect();\n resizeObserver.disconnect();\n };\n }, [updateIndicator]);\n\n return (\n <TabsPrimitive.List\n ref={innerRef}\n className={cn(\n \"relative\",\n fullWidth ? \"flex w-full [&>[role=tab]]:flex-1\" : \"inline-flex\",\n \"data-[orientation=horizontal]:items-center data-[orientation=horizontal]:shadow-[inset_0_-1px_0_0_var(--color-neutral-200)]\",\n \"data-[orientation=vertical]:flex-col data-[orientation=vertical]:shadow-[inset_-1px_0_0_0_var(--color-neutral-200)]\",\n className,\n )}\n {...props}\n >\n {children}\n <span\n ref={indicatorRef}\n aria-hidden\n className=\"pointer-events-none absolute rounded-full bg-brand-
|
|
1
|
+
{"version":3,"file":"TabsList.mjs","sources":["../../../src/components/Tabs/TabsList.tsx"],"sourcesContent":["import * as TabsPrimitive from \"@radix-ui/react-tabs\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Props for the {@link TabsList} component. */\nexport type TabsListProps = React.ComponentPropsWithoutRef<typeof TabsPrimitive.List> & {\n /** When `true` (the default), the tab list spans the full width of its container and each tab grows equally. Set to `false` for inline sizing. */\n fullWidth?: boolean;\n};\n\n/** Container for {@link TabsTrigger} elements. Renders a sliding active-tab indicator that animates between tabs. */\nexport const TabsList = React.forwardRef<\n React.ComponentRef<typeof TabsPrimitive.List>,\n TabsListProps\n>(({ className, children, fullWidth = true, ...props }, ref) => {\n const innerRef = React.useRef<HTMLDivElement>(null);\n const indicatorRef = React.useRef<HTMLSpanElement>(null);\n\n React.useImperativeHandle(ref, () => innerRef.current as HTMLDivElement);\n\n const updateIndicator = React.useCallback(() => {\n const list = innerRef.current;\n const indicator = indicatorRef.current;\n if (!list || !indicator) return;\n\n const activeTab = list.querySelector<HTMLElement>('[data-state=\"active\"]');\n if (!activeTab) {\n indicator.style.opacity = \"0\";\n return;\n }\n\n const isVertical = list.dataset.orientation === \"vertical\";\n\n indicator.style.opacity = \"1\";\n\n if (isVertical) {\n indicator.style.inset = `0 0 auto auto`;\n indicator.style.width = \"4px\";\n indicator.style.height = `${activeTab.offsetHeight}px`;\n indicator.style.transform = `translateY(${activeTab.offsetTop}px)`;\n } else {\n indicator.style.inset = `auto auto 0 0`;\n indicator.style.height = \"4px\";\n indicator.style.width = `${activeTab.offsetWidth}px`;\n indicator.style.transform = `translateX(${activeTab.offsetLeft}px)`;\n }\n }, []);\n\n React.useLayoutEffect(() => {\n const list = innerRef.current;\n const indicator = indicatorRef.current;\n if (!list || !indicator) return;\n\n indicator.style.transitionDuration = \"0s\";\n updateIndicator();\n indicator.getBoundingClientRect();\n indicator.style.transitionDuration = \"\";\n\n const mutationObserver = new MutationObserver(updateIndicator);\n mutationObserver.observe(list, {\n attributes: true,\n attributeFilter: [\"data-state\"],\n childList: true,\n subtree: true,\n });\n\n const resizeObserver = new ResizeObserver(updateIndicator);\n resizeObserver.observe(list);\n\n return () => {\n mutationObserver.disconnect();\n resizeObserver.disconnect();\n };\n }, [updateIndicator]);\n\n return (\n <TabsPrimitive.List\n ref={innerRef}\n className={cn(\n \"relative\",\n fullWidth ? \"flex w-full [&>[role=tab]]:flex-1\" : \"inline-flex\",\n \"data-[orientation=horizontal]:items-center data-[orientation=horizontal]:shadow-[inset_0_-1px_0_0_var(--color-neutral-alphas-200)]\",\n \"data-[orientation=vertical]:flex-col data-[orientation=vertical]:shadow-[inset_-1px_0_0_0_var(--color-neutral-alphas-200)]\",\n className,\n )}\n {...props}\n >\n {children}\n <span\n ref={indicatorRef}\n aria-hidden\n className=\"pointer-events-none absolute rounded-full bg-brand-primary-default motion-safe:transition-[transform,width,height] motion-safe:duration-200 motion-safe:ease-in-out\"\n style={{ opacity: 0 }}\n />\n </TabsPrimitive.List>\n );\n});\n\nTabsList.displayName = \"TabsList\";\n"],"names":[],"mappings":";;;;;AAWO,MAAM,WAAW,MAAM,WAG5B,CAAC,EAAE,WAAW,UAAU,YAAY,MAAM,GAAG,MAAA,GAAS,QAAQ;AAC9D,QAAM,WAAW,MAAM,OAAuB,IAAI;AAClD,QAAM,eAAe,MAAM,OAAwB,IAAI;AAEvD,QAAM,oBAAoB,KAAK,MAAM,SAAS,OAAyB;AAEvE,QAAM,kBAAkB,MAAM,YAAY,MAAM;AAC9C,UAAM,OAAO,SAAS;AACtB,UAAM,YAAY,aAAa;AAC/B,QAAI,CAAC,QAAQ,CAAC,UAAW;AAEzB,UAAM,YAAY,KAAK,cAA2B,uBAAuB;AACzE,QAAI,CAAC,WAAW;AACd,gBAAU,MAAM,UAAU;AAC1B;AAAA,IACF;AAEA,UAAM,aAAa,KAAK,QAAQ,gBAAgB;AAEhD,cAAU,MAAM,UAAU;AAE1B,QAAI,YAAY;AACd,gBAAU,MAAM,QAAQ;AACxB,gBAAU,MAAM,QAAQ;AACxB,gBAAU,MAAM,SAAS,GAAG,UAAU,YAAY;AAClD,gBAAU,MAAM,YAAY,cAAc,UAAU,SAAS;AAAA,IAC/D,OAAO;AACL,gBAAU,MAAM,QAAQ;AACxB,gBAAU,MAAM,SAAS;AACzB,gBAAU,MAAM,QAAQ,GAAG,UAAU,WAAW;AAChD,gBAAU,MAAM,YAAY,cAAc,UAAU,UAAU;AAAA,IAChE;AAAA,EACF,GAAG,CAAA,CAAE;AAEL,QAAM,gBAAgB,MAAM;AAC1B,UAAM,OAAO,SAAS;AACtB,UAAM,YAAY,aAAa;AAC/B,QAAI,CAAC,QAAQ,CAAC,UAAW;AAEzB,cAAU,MAAM,qBAAqB;AACrC,oBAAA;AACA,cAAU,sBAAA;AACV,cAAU,MAAM,qBAAqB;AAErC,UAAM,mBAAmB,IAAI,iBAAiB,eAAe;AAC7D,qBAAiB,QAAQ,MAAM;AAAA,MAC7B,YAAY;AAAA,MACZ,iBAAiB,CAAC,YAAY;AAAA,MAC9B,WAAW;AAAA,MACX,SAAS;AAAA,IAAA,CACV;AAED,UAAM,iBAAiB,IAAI,eAAe,eAAe;AACzD,mBAAe,QAAQ,IAAI;AAE3B,WAAO,MAAM;AACX,uBAAiB,WAAA;AACjB,qBAAe,WAAA;AAAA,IACjB;AAAA,EACF,GAAG,CAAC,eAAe,CAAC;AAEpB,SACE;AAAA,IAAC,cAAc;AAAA,IAAd;AAAA,MACC,KAAK;AAAA,MACL,WAAW;AAAA,QACT;AAAA,QACA,YAAY,sCAAsC;AAAA,QAClD;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,MAED,GAAG;AAAA,MAEH,UAAA;AAAA,QAAA;AAAA,QACD;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,KAAK;AAAA,YACL,eAAW;AAAA,YACX,WAAU;AAAA,YACV,OAAO,EAAE,SAAS,EAAA;AAAA,UAAE;AAAA,QAAA;AAAA,MACtB;AAAA,IAAA;AAAA,EAAA;AAGN,CAAC;AAED,SAAS,cAAc;"}
|
|
@@ -10,18 +10,18 @@ const TabsTrigger = React.forwardRef(({ className, children, ...props }, ref) =>
|
|
|
10
10
|
className: cn(
|
|
11
11
|
"inline-flex min-w-0 items-center justify-center",
|
|
12
12
|
"rounded-xs",
|
|
13
|
-
"typography-semibold-body-lg cursor-pointer text-
|
|
13
|
+
"typography-semibold-body-lg cursor-pointer text-content-primary",
|
|
14
14
|
"motion-safe:transition-colors motion-safe:duration-150 motion-safe:ease-in-out",
|
|
15
15
|
"data-[orientation=horizontal]:px-4 data-[orientation=horizontal]:py-3",
|
|
16
16
|
"data-[orientation=vertical]:justify-start data-[orientation=vertical]:px-4 data-[orientation=vertical]:py-3",
|
|
17
|
-
"data-[state=active]:hover:text-
|
|
18
|
-
"data-[state=inactive]:hover:text-neutral-300",
|
|
19
|
-
"data-[state=active]:active:text-
|
|
20
|
-
"data-[state=inactive]:active:text-neutral-300",
|
|
17
|
+
"data-[state=active]:hover:text-buttons-primary-muted",
|
|
18
|
+
"data-[state=inactive]:hover:text-neutral-alphas-300",
|
|
19
|
+
"data-[state=active]:active:text-buttons-primary-muted",
|
|
20
|
+
"data-[state=inactive]:active:text-neutral-alphas-300",
|
|
21
21
|
"data-disabled:pointer-events-none",
|
|
22
|
-
"data-disabled:data-[state=active]:text-
|
|
23
|
-
"data-disabled:data-[state=inactive]:text-neutral-300",
|
|
24
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-
|
|
22
|
+
"data-disabled:data-[state=active]:text-content-tertiary",
|
|
23
|
+
"data-disabled:data-[state=inactive]:text-neutral-alphas-300",
|
|
24
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-interaction-focus focus-visible:ring-offset-2 focus-visible:ring-offset-bg-primary",
|
|
25
25
|
className
|
|
26
26
|
),
|
|
27
27
|
...props,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TabsTrigger.mjs","sources":["../../../src/components/Tabs/TabsTrigger.tsx"],"sourcesContent":["import * as TabsPrimitive from \"@radix-ui/react-tabs\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Props for the {@link TabsTrigger} button component. */\nexport type TabsTriggerProps = React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>;\n\n/** An interactive tab button that activates its associated {@link TabsContent} panel when clicked. */\nexport const TabsTrigger = React.forwardRef<\n React.ComponentRef<typeof TabsPrimitive.Trigger>,\n TabsTriggerProps\n>(({ className, children, ...props }, ref) => (\n <TabsPrimitive.Trigger\n ref={ref}\n className={cn(\n \"inline-flex min-w-0 items-center justify-center\",\n \"rounded-xs\",\n \"typography-semibold-body-lg cursor-pointer text-
|
|
1
|
+
{"version":3,"file":"TabsTrigger.mjs","sources":["../../../src/components/Tabs/TabsTrigger.tsx"],"sourcesContent":["import * as TabsPrimitive from \"@radix-ui/react-tabs\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Props for the {@link TabsTrigger} button component. */\nexport type TabsTriggerProps = React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>;\n\n/** An interactive tab button that activates its associated {@link TabsContent} panel when clicked. */\nexport const TabsTrigger = React.forwardRef<\n React.ComponentRef<typeof TabsPrimitive.Trigger>,\n TabsTriggerProps\n>(({ className, children, ...props }, ref) => (\n <TabsPrimitive.Trigger\n ref={ref}\n className={cn(\n \"inline-flex min-w-0 items-center justify-center\",\n \"rounded-xs\",\n \"typography-semibold-body-lg cursor-pointer text-content-primary\",\n \"motion-safe:transition-colors motion-safe:duration-150 motion-safe:ease-in-out\",\n \"data-[orientation=horizontal]:px-4 data-[orientation=horizontal]:py-3\",\n \"data-[orientation=vertical]:justify-start data-[orientation=vertical]:px-4 data-[orientation=vertical]:py-3\",\n \"data-[state=active]:hover:text-buttons-primary-muted\",\n \"data-[state=inactive]:hover:text-neutral-alphas-300\",\n \"data-[state=active]:active:text-buttons-primary-muted\",\n \"data-[state=inactive]:active:text-neutral-alphas-300\",\n \"data-disabled:pointer-events-none\",\n \"data-disabled:data-[state=active]:text-content-tertiary\",\n \"data-disabled:data-[state=inactive]:text-neutral-alphas-300\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-interaction-focus focus-visible:ring-offset-2 focus-visible:ring-offset-bg-primary\",\n className,\n )}\n {...props}\n >\n <span className=\"min-w-0 truncate\">{children}</span>\n </TabsPrimitive.Trigger>\n));\n\nTabsTrigger.displayName = \"TabsTrigger\";\n"],"names":[],"mappings":";;;;;AAQO,MAAM,cAAc,MAAM,WAG/B,CAAC,EAAE,WAAW,UAAU,GAAG,SAAS,QACpC;AAAA,EAAC,cAAc;AAAA,EAAd;AAAA,IACC;AAAA,IACA,WAAW;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAED,GAAG;AAAA,IAEJ,UAAA,oBAAC,QAAA,EAAK,WAAU,oBAAoB,SAAA,CAAS;AAAA,EAAA;AAC/C,CACD;AAED,YAAY,cAAc;"}
|
|
@@ -32,16 +32,16 @@ const CLEAR_BUTTON_RIGHT = {
|
|
|
32
32
|
};
|
|
33
33
|
function getContainerClassName(size, error, disabled) {
|
|
34
34
|
return cn(
|
|
35
|
-
"relative rounded-
|
|
36
|
-
error ? "border-error-
|
|
37
|
-
!disabled && !error && "hover:border-neutral-400",
|
|
35
|
+
"relative rounded-sm border bg-neutral-alphas-50 has-focus-visible:outline-none motion-safe:transition-colors",
|
|
36
|
+
error ? "border-error-content" : "border-transparent",
|
|
37
|
+
!disabled && !error && "hover:border-neutral-alphas-400",
|
|
38
38
|
CONTAINER_MIN_HEIGHT[size],
|
|
39
39
|
disabled && "opacity-50"
|
|
40
40
|
);
|
|
41
41
|
}
|
|
42
42
|
function getTextareaClassName(size, hasClearButton, hasMinRows, resizable) {
|
|
43
43
|
return cn(
|
|
44
|
-
"
|
|
44
|
+
"h-full w-full bg-transparent text-content-primary no-underline placeholder:text-content-secondary focus:outline-none disabled:cursor-not-allowed",
|
|
45
45
|
resizable ? "resize-y" : "resize-none",
|
|
46
46
|
!hasMinRows && "min-h-[80px]",
|
|
47
47
|
TEXTAREA_SIZE_CLASSES[size],
|
|
@@ -60,7 +60,7 @@ function TextAreaHelperText({
|
|
|
60
60
|
id,
|
|
61
61
|
className: cn(
|
|
62
62
|
"typography-regular-body-sm px-2 pt-1 pb-0.5",
|
|
63
|
-
error ? "text-error-
|
|
63
|
+
error ? "text-error-content" : "text-content-secondary"
|
|
64
64
|
),
|
|
65
65
|
children
|
|
66
66
|
}
|
|
@@ -160,7 +160,7 @@ const TextArea = React.forwardRef(
|
|
|
160
160
|
"label",
|
|
161
161
|
{
|
|
162
162
|
htmlFor: inputId,
|
|
163
|
-
className: "typography-semibold-body-sm px-1 pt-1 pb-2 text-
|
|
163
|
+
className: "typography-semibold-body-sm px-1 pt-1 pb-2 text-content-primary",
|
|
164
164
|
children: label
|
|
165
165
|
}
|
|
166
166
|
),
|
|
@@ -203,7 +203,7 @@ const TextArea = React.forwardRef(
|
|
|
203
203
|
"pointer-events-none absolute flex size-5 items-center justify-center",
|
|
204
204
|
CLEAR_BUTTON_RIGHT[size]
|
|
205
205
|
),
|
|
206
|
-
children: /* @__PURE__ */ jsx(CheckOutlineIcon, { className: "text-success-
|
|
206
|
+
children: /* @__PURE__ */ jsx(CheckOutlineIcon, { className: "text-success-content" })
|
|
207
207
|
}
|
|
208
208
|
)
|
|
209
209
|
] }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextArea.mjs","sources":["../../../src/components/TextArea/TextArea.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { IconButton } from \"../IconButton/IconButton\";\nimport { CheckOutlineIcon } from \"../Icons/CheckOutlineIcon\";\nimport { CloseIcon } from \"../Icons/CloseIcon\";\n\n/** Text area height in pixels. */\nexport type TextAreaSize = \"48\" | \"40\" | \"32\";\n\nexport interface TextAreaProps\n extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, \"size\"> {\n /** Label text displayed above the textarea. Also used as the accessible name. */\n label?: string;\n /** Helper text displayed below the textarea. Replaced by `errorMessage` when `error` is `true`. */\n helperText?: string;\n /** Minimum height of the text area in pixels. @default \"48\" */\n size?: TextAreaSize;\n /** Whether the text area is in an error state. @default false */\n error?: boolean;\n /** Error message displayed below the textarea. Shown instead of `helperText` when `error` is `true`. */\n errorMessage?: string;\n /** Whether the text area is validated. @default false */\n validated?: boolean;\n /** Whether the text area stretches to fill its container width. @default false */\n fullWidth?: boolean;\n /** Whether to show a clear button when text is present. @default false */\n showClearButton?: boolean;\n /** Callback fired when the clear button is clicked. Note: `onChange` is also called with an empty value when clearing. */\n onClear?: () => void;\n /** Minimum number of rows (lines) for the textarea. */\n minRows?: number;\n /** Maximum number of rows (lines) for the textarea. */\n maxRows?: number;\n /** Whether the textarea can be resized by the user. @default true */\n resizable?: boolean;\n}\n\nconst CONTAINER_MIN_HEIGHT: Record<TextAreaSize, string> = {\n \"48\": \"min-h-12\",\n \"40\": \"min-h-10\",\n \"32\": \"min-h-8\",\n};\n\nconst TEXTAREA_SIZE_CLASSES: Record<TextAreaSize, string> = {\n \"48\": \"py-3 typography-regular-body-lg\",\n \"40\": \"py-2 typography-regular-body-lg\",\n \"32\": \"py-2 typography-regular-body-md\",\n};\n\nconst PADDING_HORIZONTAL: Record<TextAreaSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nconst PADDING_RIGHT_WITH_CLEAR: Record<TextAreaSize, string> = {\n \"48\": \"pr-11\",\n \"40\": \"pr-11\",\n \"32\": \"pr-10\",\n};\n\nconst CLEAR_BUTTON_RIGHT: Record<TextAreaSize, string> = {\n \"48\": \"right-4 top-3\",\n \"40\": \"right-4 top-2\",\n \"32\": \"right-3 top-2\",\n};\n\nfunction getContainerClassName(size: TextAreaSize, error: boolean, disabled?: boolean) {\n return cn(\n \"relative rounded-xl border bg-neutral-100 has-focus-visible:outline-none motion-safe:transition-colors\",\n error ? \"border-error-default\" : \"border-transparent\",\n !disabled && !error && \"hover:border-neutral-400\",\n CONTAINER_MIN_HEIGHT[size],\n disabled && \"opacity-50\",\n );\n}\n\nfunction getTextareaClassName(\n size: TextAreaSize,\n hasClearButton: boolean,\n hasMinRows: boolean,\n resizable: boolean,\n) {\n return cn(\n \"w-full rounded-xl bg-transparent text-foreground-default no-underline placeholder:text-foreground-secondary placeholder:opacity-40 focus:outline-none disabled:cursor-not-allowed\",\n resizable ? \"resize-y\" : \"resize-none\",\n !hasMinRows && \"min-h-[80px]\",\n TEXTAREA_SIZE_CLASSES[size],\n PADDING_HORIZONTAL[size],\n hasClearButton ? PADDING_RIGHT_WITH_CLEAR[size] : \"\",\n );\n}\n\nfunction TextAreaHelperText({\n id,\n error,\n children,\n}: {\n id: string;\n error: boolean;\n children: React.ReactNode;\n}) {\n return (\n <p\n id={id}\n className={cn(\n \"typography-regular-body-sm px-2 pt-1 pb-0.5\",\n error ? \"text-error-default\" : \"text-foreground-secondary\",\n )}\n >\n {children}\n </p>\n );\n}\n\nfunction warnMissingAccessibleName(label?: string, ariaLabel?: string, ariaLabelledBy?: string) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!label && !ariaLabel && !ariaLabelledBy) {\n console.warn(\n \"TextArea: no accessible name provided. Pass a `label`, `aria-label`, or `aria-labelledby` prop.\",\n );\n }\n }\n}\n\nfunction calculateMaxHeight(size: TextAreaSize, maxRows?: number): string | undefined {\n if (!maxRows) return undefined;\n\n // Line height is 24px for body-1 (sizes 48 and 40) and 20px for body-2 (size 32)\n const lineHeight = size === \"32\" ? 20 : 24;\n // py-2 = 8px, py-3 = 12px\n const verticalPadding = size === \"32\" ? 8 : size === \"40\" ? 8 : 12;\n\n return `${lineHeight * maxRows + verticalPadding * 2}px`;\n}\n\nfunction useTextAreaValue(\n value: React.TextareaHTMLAttributes<HTMLTextAreaElement>[\"value\"],\n defaultValue: React.TextareaHTMLAttributes<HTMLTextAreaElement>[\"defaultValue\"],\n showClearButton: boolean,\n onChange?: React.ChangeEventHandler<HTMLTextAreaElement>,\n onClear?: () => void,\n textareaRef?: React.RefObject<HTMLTextAreaElement | null>,\n) {\n const [internalValue, setInternalValue] = React.useState(defaultValue ?? \"\");\n const resolvedValue = value !== undefined ? value : internalValue;\n\n const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setInternalValue(e.target.value);\n onChange?.(e);\n };\n\n const handleClear = () => {\n setInternalValue(\"\");\n\n if (onChange && textareaRef?.current) {\n const syntheticEvent = {\n target: { ...textareaRef.current, value: \"\" },\n currentTarget: { ...textareaRef.current, value: \"\" },\n } as React.ChangeEvent<HTMLTextAreaElement>;\n onChange(syntheticEvent);\n }\n\n onClear?.();\n };\n\n return {\n resolvedValue,\n displayValue: showClearButton ? resolvedValue : value,\n resolvedDefaultValue: showClearButton ? undefined : defaultValue,\n handleChange,\n handleClear,\n };\n}\n\n/**\n * A multi-line text input with optional label, helper/error text, and clear button.\n *\n * Provide at least one of `label`, `aria-label`, or `aria-labelledby` for\n * accessibility — a console warning is emitted in development if none are set.\n *\n * @example\n * ```tsx\n * <TextArea\n * label=\"Description\"\n * placeholder=\"Enter your description...\"\n * showClearButton\n * error={!!descError}\n * errorMessage={descError}\n * />\n * ```\n */\nexport const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(\n (\n {\n label,\n helperText,\n size = \"48\",\n error = false,\n errorMessage,\n validated = false,\n className,\n id,\n disabled,\n fullWidth = false,\n showClearButton = false,\n onClear,\n value,\n defaultValue,\n onChange,\n minRows,\n maxRows,\n resizable = false,\n ...props\n },\n ref,\n ) => {\n const generatedId = React.useId();\n const inputId = id || generatedId;\n const helperTextId = `${inputId}-helper`;\n const bottomText = error && errorMessage ? errorMessage : helperText;\n const maxHeight = calculateMaxHeight(size, maxRows);\n\n const internalRef = React.useRef<HTMLTextAreaElement>(null);\n\n const { resolvedValue, displayValue, resolvedDefaultValue, handleChange, handleClear } =\n useTextAreaValue(value, defaultValue, showClearButton, onChange, onClear, internalRef);\n\n const mergedRef = (node: HTMLTextAreaElement | null) => {\n internalRef.current = node;\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref) {\n (ref as React.MutableRefObject<HTMLTextAreaElement | null>).current = node;\n }\n };\n\n const showClear = showClearButton && resolvedValue !== \"\" && !disabled;\n const showValidated = validated && !showClear;\n const ariaDescribedBy = bottomText ? helperTextId : undefined;\n const textareaStyle = maxHeight ? { maxHeight } : undefined;\n\n warnMissingAccessibleName(label, props[\"aria-label\"], props[\"aria-labelledby\"]);\n\n return (\n <div\n className={cn(\"flex flex-col\", fullWidth && \"w-full\", className)}\n data-disabled={disabled ? \"\" : undefined}\n data-error={error ? \"\" : undefined}\n >\n {label && (\n <label\n htmlFor={inputId}\n className=\"typography-semibold-body-sm px-1 pt-1 pb-2 text-foreground-default\"\n >\n {label}\n </label>\n )}\n\n <div className={getContainerClassName(size, error, disabled)}>\n <textarea\n ref={mergedRef}\n id={inputId}\n disabled={disabled}\n aria-describedby={ariaDescribedBy}\n aria-invalid={error || undefined}\n className={getTextareaClassName(size, showClear, !!minRows, resizable)}\n value={displayValue}\n defaultValue={resolvedDefaultValue}\n onChange={handleChange}\n rows={minRows}\n style={textareaStyle}\n {...props}\n />\n\n {showClear && (\n <IconButton\n variant=\"tertiary\"\n size=\"24\"\n icon={<CloseIcon />}\n aria-label=\"Clear text\"\n onClick={handleClear}\n className={cn(\n \"absolute flex size-5 items-center justify-center self-end\",\n CLEAR_BUTTON_RIGHT[size],\n )}\n />\n )}\n {showValidated && (\n <div\n className={cn(\n \"pointer-events-none absolute flex size-5 items-center justify-center\",\n CLEAR_BUTTON_RIGHT[size],\n )}\n >\n <CheckOutlineIcon className=\"text-success-default\" />\n </div>\n )}\n </div>\n\n {bottomText && (\n <TextAreaHelperText id={helperTextId} error={error}>\n {bottomText}\n </TextAreaHelperText>\n )}\n </div>\n );\n },\n);\n\nTextArea.displayName = \"TextArea\";\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TextArea.mjs","sources":["../../../src/components/TextArea/TextArea.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { IconButton } from \"../IconButton/IconButton\";\nimport { CheckOutlineIcon } from \"../Icons/CheckOutlineIcon\";\nimport { CloseIcon } from \"../Icons/CloseIcon\";\n\n/** Text area height in pixels. */\nexport type TextAreaSize = \"48\" | \"40\" | \"32\";\n\nexport interface TextAreaProps\n extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, \"size\"> {\n /** Label text displayed above the textarea. Also used as the accessible name. */\n label?: string;\n /** Helper text displayed below the textarea. Replaced by `errorMessage` when `error` is `true`. */\n helperText?: string;\n /** Minimum height of the text area in pixels. @default \"48\" */\n size?: TextAreaSize;\n /** Whether the text area is in an error state. @default false */\n error?: boolean;\n /** Error message displayed below the textarea. Shown instead of `helperText` when `error` is `true`. */\n errorMessage?: string;\n /** Whether the text area is validated. @default false */\n validated?: boolean;\n /** Whether the text area stretches to fill its container width. @default false */\n fullWidth?: boolean;\n /** Whether to show a clear button when text is present. @default false */\n showClearButton?: boolean;\n /** Callback fired when the clear button is clicked. Note: `onChange` is also called with an empty value when clearing. */\n onClear?: () => void;\n /** Minimum number of rows (lines) for the textarea. */\n minRows?: number;\n /** Maximum number of rows (lines) for the textarea. */\n maxRows?: number;\n /** Whether the textarea can be resized by the user. @default true */\n resizable?: boolean;\n}\n\nconst CONTAINER_MIN_HEIGHT: Record<TextAreaSize, string> = {\n \"48\": \"min-h-12\",\n \"40\": \"min-h-10\",\n \"32\": \"min-h-8\",\n};\n\nconst TEXTAREA_SIZE_CLASSES: Record<TextAreaSize, string> = {\n \"48\": \"py-3 typography-regular-body-lg\",\n \"40\": \"py-2 typography-regular-body-lg\",\n \"32\": \"py-2 typography-regular-body-md\",\n};\n\nconst PADDING_HORIZONTAL: Record<TextAreaSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nconst PADDING_RIGHT_WITH_CLEAR: Record<TextAreaSize, string> = {\n \"48\": \"pr-11\",\n \"40\": \"pr-11\",\n \"32\": \"pr-10\",\n};\n\nconst CLEAR_BUTTON_RIGHT: Record<TextAreaSize, string> = {\n \"48\": \"right-4 top-3\",\n \"40\": \"right-4 top-2\",\n \"32\": \"right-3 top-2\",\n};\n\nfunction getContainerClassName(size: TextAreaSize, error: boolean, disabled?: boolean) {\n return cn(\n \"relative rounded-sm border bg-neutral-alphas-50 has-focus-visible:outline-none motion-safe:transition-colors\",\n error ? \"border-error-content\" : \"border-transparent\",\n !disabled && !error && \"hover:border-neutral-alphas-400\",\n CONTAINER_MIN_HEIGHT[size],\n disabled && \"opacity-50\",\n );\n}\n\nfunction getTextareaClassName(\n size: TextAreaSize,\n hasClearButton: boolean,\n hasMinRows: boolean,\n resizable: boolean,\n) {\n return cn(\n \"h-full w-full bg-transparent text-content-primary no-underline placeholder:text-content-secondary focus:outline-none disabled:cursor-not-allowed\",\n resizable ? \"resize-y\" : \"resize-none\",\n !hasMinRows && \"min-h-[80px]\",\n TEXTAREA_SIZE_CLASSES[size],\n PADDING_HORIZONTAL[size],\n hasClearButton ? PADDING_RIGHT_WITH_CLEAR[size] : \"\",\n );\n}\n\nfunction TextAreaHelperText({\n id,\n error,\n children,\n}: {\n id: string;\n error: boolean;\n children: React.ReactNode;\n}) {\n return (\n <p\n id={id}\n className={cn(\n \"typography-regular-body-sm px-2 pt-1 pb-0.5\",\n error ? \"text-error-content\" : \"text-content-secondary\",\n )}\n >\n {children}\n </p>\n );\n}\n\nfunction warnMissingAccessibleName(label?: string, ariaLabel?: string, ariaLabelledBy?: string) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!label && !ariaLabel && !ariaLabelledBy) {\n console.warn(\n \"TextArea: no accessible name provided. Pass a `label`, `aria-label`, or `aria-labelledby` prop.\",\n );\n }\n }\n}\n\nfunction calculateMaxHeight(size: TextAreaSize, maxRows?: number): string | undefined {\n if (!maxRows) return undefined;\n\n // Line height is 24px for body-1 (sizes 48 and 40) and 20px for body-2 (size 32)\n const lineHeight = size === \"32\" ? 20 : 24;\n // py-2 = 8px, py-3 = 12px\n const verticalPadding = size === \"32\" ? 8 : size === \"40\" ? 8 : 12;\n\n return `${lineHeight * maxRows + verticalPadding * 2}px`;\n}\n\nfunction useTextAreaValue(\n value: React.TextareaHTMLAttributes<HTMLTextAreaElement>[\"value\"],\n defaultValue: React.TextareaHTMLAttributes<HTMLTextAreaElement>[\"defaultValue\"],\n showClearButton: boolean,\n onChange?: React.ChangeEventHandler<HTMLTextAreaElement>,\n onClear?: () => void,\n textareaRef?: React.RefObject<HTMLTextAreaElement | null>,\n) {\n const [internalValue, setInternalValue] = React.useState(defaultValue ?? \"\");\n const resolvedValue = value !== undefined ? value : internalValue;\n\n const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setInternalValue(e.target.value);\n onChange?.(e);\n };\n\n const handleClear = () => {\n setInternalValue(\"\");\n\n if (onChange && textareaRef?.current) {\n const syntheticEvent = {\n target: { ...textareaRef.current, value: \"\" },\n currentTarget: { ...textareaRef.current, value: \"\" },\n } as React.ChangeEvent<HTMLTextAreaElement>;\n onChange(syntheticEvent);\n }\n\n onClear?.();\n };\n\n return {\n resolvedValue,\n displayValue: showClearButton ? resolvedValue : value,\n resolvedDefaultValue: showClearButton ? undefined : defaultValue,\n handleChange,\n handleClear,\n };\n}\n\n/**\n * A multi-line text input with optional label, helper/error text, and clear button.\n *\n * Provide at least one of `label`, `aria-label`, or `aria-labelledby` for\n * accessibility — a console warning is emitted in development if none are set.\n *\n * @example\n * ```tsx\n * <TextArea\n * label=\"Description\"\n * placeholder=\"Enter your description...\"\n * showClearButton\n * error={!!descError}\n * errorMessage={descError}\n * />\n * ```\n */\nexport const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(\n (\n {\n label,\n helperText,\n size = \"48\",\n error = false,\n errorMessage,\n validated = false,\n className,\n id,\n disabled,\n fullWidth = false,\n showClearButton = false,\n onClear,\n value,\n defaultValue,\n onChange,\n minRows,\n maxRows,\n resizable = false,\n ...props\n },\n ref,\n ) => {\n const generatedId = React.useId();\n const inputId = id || generatedId;\n const helperTextId = `${inputId}-helper`;\n const bottomText = error && errorMessage ? errorMessage : helperText;\n const maxHeight = calculateMaxHeight(size, maxRows);\n\n const internalRef = React.useRef<HTMLTextAreaElement>(null);\n\n const { resolvedValue, displayValue, resolvedDefaultValue, handleChange, handleClear } =\n useTextAreaValue(value, defaultValue, showClearButton, onChange, onClear, internalRef);\n\n const mergedRef = (node: HTMLTextAreaElement | null) => {\n internalRef.current = node;\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref) {\n (ref as React.MutableRefObject<HTMLTextAreaElement | null>).current = node;\n }\n };\n\n const showClear = showClearButton && resolvedValue !== \"\" && !disabled;\n const showValidated = validated && !showClear;\n const ariaDescribedBy = bottomText ? helperTextId : undefined;\n const textareaStyle = maxHeight ? { maxHeight } : undefined;\n\n warnMissingAccessibleName(label, props[\"aria-label\"], props[\"aria-labelledby\"]);\n\n return (\n <div\n className={cn(\"flex flex-col\", fullWidth && \"w-full\", className)}\n data-disabled={disabled ? \"\" : undefined}\n data-error={error ? \"\" : undefined}\n >\n {label && (\n <label\n htmlFor={inputId}\n className=\"typography-semibold-body-sm px-1 pt-1 pb-2 text-content-primary\"\n >\n {label}\n </label>\n )}\n\n <div className={getContainerClassName(size, error, disabled)}>\n <textarea\n ref={mergedRef}\n id={inputId}\n disabled={disabled}\n aria-describedby={ariaDescribedBy}\n aria-invalid={error || undefined}\n className={getTextareaClassName(size, showClear, !!minRows, resizable)}\n value={displayValue}\n defaultValue={resolvedDefaultValue}\n onChange={handleChange}\n rows={minRows}\n style={textareaStyle}\n {...props}\n />\n\n {showClear && (\n <IconButton\n variant=\"tertiary\"\n size=\"24\"\n icon={<CloseIcon />}\n aria-label=\"Clear text\"\n onClick={handleClear}\n className={cn(\n \"absolute flex size-5 items-center justify-center self-end\",\n CLEAR_BUTTON_RIGHT[size],\n )}\n />\n )}\n {showValidated && (\n <div\n className={cn(\n \"pointer-events-none absolute flex size-5 items-center justify-center\",\n CLEAR_BUTTON_RIGHT[size],\n )}\n >\n <CheckOutlineIcon className=\"text-success-content\" />\n </div>\n )}\n </div>\n\n {bottomText && (\n <TextAreaHelperText id={helperTextId} error={error}>\n {bottomText}\n </TextAreaHelperText>\n )}\n </div>\n );\n },\n);\n\nTextArea.displayName = \"TextArea\";\n"],"names":[],"mappings":"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"}
|
|
@@ -19,15 +19,15 @@ const PADDING_HORIZONTAL = {
|
|
|
19
19
|
"32": "px-3"
|
|
20
20
|
};
|
|
21
21
|
const ICON_SPACING = {
|
|
22
|
-
"48": "gap-
|
|
23
|
-
"40": "gap-
|
|
22
|
+
"48": "gap-2",
|
|
23
|
+
"40": "gap-2",
|
|
24
24
|
"32": "gap-2"
|
|
25
25
|
};
|
|
26
26
|
function getContainerClassName(size, error, disabled) {
|
|
27
27
|
return cn(
|
|
28
|
-
"flex items-center rounded-
|
|
29
|
-
error ? "border-error-
|
|
30
|
-
!disabled && !error && "hover:border-neutral-400",
|
|
28
|
+
"flex items-center rounded-sm border bg-neutral-alphas-50 has-focus-visible:outline-none motion-safe:transition-colors",
|
|
29
|
+
error ? "border-error-content" : "border-transparent",
|
|
30
|
+
!disabled && !error && "hover:border-neutral-alphas-400",
|
|
31
31
|
CONTAINER_HEIGHT[size],
|
|
32
32
|
PADDING_HORIZONTAL[size],
|
|
33
33
|
ICON_SPACING[size],
|
|
@@ -36,12 +36,12 @@ function getContainerClassName(size, error, disabled) {
|
|
|
36
36
|
}
|
|
37
37
|
function getInputClassName(size) {
|
|
38
38
|
return cn(
|
|
39
|
-
"h-full min-w-0 flex-1
|
|
39
|
+
"h-full min-w-0 flex-1 bg-transparent text-content-primary no-underline placeholder:text-content-secondary focus:outline-none disabled:cursor-not-allowed",
|
|
40
40
|
INPUT_SIZE_CLASSES[size]
|
|
41
41
|
);
|
|
42
42
|
}
|
|
43
43
|
function TextFieldIcon({ children }) {
|
|
44
|
-
return /* @__PURE__ */ jsx("div", { className: "flex size-
|
|
44
|
+
return /* @__PURE__ */ jsx("div", { className: "flex size-4 shrink-0 items-center justify-center text-content-secondary", children });
|
|
45
45
|
}
|
|
46
46
|
function TextFieldHelperText({
|
|
47
47
|
id,
|
|
@@ -53,8 +53,8 @@ function TextFieldHelperText({
|
|
|
53
53
|
{
|
|
54
54
|
id,
|
|
55
55
|
className: cn(
|
|
56
|
-
"typography-regular-body-sm px-2 pt-
|
|
57
|
-
error ? "text-error-
|
|
56
|
+
"typography-regular-body-sm px-2 pt-2 pb-0.5",
|
|
57
|
+
error ? "text-error-content" : "text-content-secondary"
|
|
58
58
|
),
|
|
59
59
|
children
|
|
60
60
|
}
|
|
@@ -101,7 +101,7 @@ const TextField = React.forwardRef(
|
|
|
101
101
|
"label",
|
|
102
102
|
{
|
|
103
103
|
htmlFor: inputId,
|
|
104
|
-
className: "typography-semibold-body-sm px-1 pt-1 pb-2 text-
|
|
104
|
+
className: "typography-semibold-body-sm px-1 pt-1 pb-2 text-content-primary",
|
|
105
105
|
children: label
|
|
106
106
|
}
|
|
107
107
|
),
|
|
@@ -124,7 +124,7 @@ const TextField = React.forwardRef(
|
|
|
124
124
|
}
|
|
125
125
|
),
|
|
126
126
|
rightIcon && /* @__PURE__ */ jsx(TextFieldIcon, { children: rightIcon }),
|
|
127
|
-
validated && /* @__PURE__ */ jsx(TextFieldIcon, { children: /* @__PURE__ */ jsx(CheckOutlineIcon, { className: "text-success-
|
|
127
|
+
validated && /* @__PURE__ */ jsx(TextFieldIcon, { children: /* @__PURE__ */ jsx(CheckOutlineIcon, { className: "text-success-content" }) })
|
|
128
128
|
] }),
|
|
129
129
|
bottomText && /* @__PURE__ */ jsx(TextFieldHelperText, { id: helperTextId, error, children: bottomText })
|
|
130
130
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextField.mjs","sources":["../../../src/components/TextField/TextField.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { CheckOutlineIcon } from \"@/index\";\nimport { cn } from \"../../utils/cn\";\n\n/** Text field height in pixels. */\nexport type TextFieldSize = \"48\" | \"40\" | \"32\";\n\nexport interface TextFieldProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement>, \"size\" | \"prefix\"> {\n /** Label text displayed above the input. Also used as the accessible name. */\n label?: string;\n /** Helper text displayed below the input. Replaced by `errorMessage` when `error` is `true`. */\n helperText?: string;\n /** Height of the text field in pixels. @default \"48\" */\n size?: TextFieldSize;\n /** Whether the text field is in an error state. @default false */\n error?: boolean;\n /** Error message displayed below the input. Shown instead of `helperText` when `error` is `true`. */\n errorMessage?: string;\n /** Whether the text field is validated. @default false */\n validated?: boolean;\n /** Icon element displayed at the left side of the input. */\n leftIcon?: React.ReactNode;\n /** Icon element displayed at the right side of the input. */\n rightIcon?: React.ReactNode;\n /** Whether the text field stretches to fill its container width. @default false */\n fullWidth?: boolean;\n}\n\nconst CONTAINER_HEIGHT: Record<TextFieldSize, string> = {\n \"48\": \"h-12\",\n \"40\": \"h-10\",\n \"32\": \"h-8\",\n};\n\nconst INPUT_SIZE_CLASSES: Record<TextFieldSize, string> = {\n \"48\": \"py-3 typography-regular-body-lg\",\n \"40\": \"py-2 typography-regular-body-lg\",\n \"32\": \"py-2 typography-regular-body-md\",\n};\n\nconst PADDING_HORIZONTAL: Record<TextFieldSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nconst ICON_SPACING: Record<TextFieldSize, string> = {\n \"48\": \"gap-
|
|
1
|
+
{"version":3,"file":"TextField.mjs","sources":["../../../src/components/TextField/TextField.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { CheckOutlineIcon } from \"@/index\";\nimport { cn } from \"../../utils/cn\";\n\n/** Text field height in pixels. */\nexport type TextFieldSize = \"48\" | \"40\" | \"32\";\n\nexport interface TextFieldProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement>, \"size\" | \"prefix\"> {\n /** Label text displayed above the input. Also used as the accessible name. */\n label?: string;\n /** Helper text displayed below the input. Replaced by `errorMessage` when `error` is `true`. */\n helperText?: string;\n /** Height of the text field in pixels. @default \"48\" */\n size?: TextFieldSize;\n /** Whether the text field is in an error state. @default false */\n error?: boolean;\n /** Error message displayed below the input. Shown instead of `helperText` when `error` is `true`. */\n errorMessage?: string;\n /** Whether the text field is validated. @default false */\n validated?: boolean;\n /** Icon element displayed at the left side of the input. */\n leftIcon?: React.ReactNode;\n /** Icon element displayed at the right side of the input. */\n rightIcon?: React.ReactNode;\n /** Whether the text field stretches to fill its container width. @default false */\n fullWidth?: boolean;\n}\n\nconst CONTAINER_HEIGHT: Record<TextFieldSize, string> = {\n \"48\": \"h-12\",\n \"40\": \"h-10\",\n \"32\": \"h-8\",\n};\n\nconst INPUT_SIZE_CLASSES: Record<TextFieldSize, string> = {\n \"48\": \"py-3 typography-regular-body-lg\",\n \"40\": \"py-2 typography-regular-body-lg\",\n \"32\": \"py-2 typography-regular-body-md\",\n};\n\nconst PADDING_HORIZONTAL: Record<TextFieldSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nconst ICON_SPACING: Record<TextFieldSize, string> = {\n \"48\": \"gap-2\",\n \"40\": \"gap-2\",\n \"32\": \"gap-2\",\n};\n\nfunction getContainerClassName(size: TextFieldSize, error: boolean, disabled?: boolean) {\n return cn(\n \"flex items-center rounded-sm border bg-neutral-alphas-50 has-focus-visible:outline-none motion-safe:transition-colors\",\n error ? \"border-error-content\" : \"border-transparent\",\n !disabled && !error && \"hover:border-neutral-alphas-400\",\n CONTAINER_HEIGHT[size],\n PADDING_HORIZONTAL[size],\n ICON_SPACING[size],\n disabled && \"opacity-50\",\n );\n}\n\nfunction getInputClassName(size: TextFieldSize) {\n return cn(\n \"h-full min-w-0 flex-1 bg-transparent text-content-primary no-underline placeholder:text-content-secondary focus:outline-none disabled:cursor-not-allowed\",\n INPUT_SIZE_CLASSES[size],\n );\n}\n\nfunction TextFieldIcon({ children }: { children: React.ReactNode }) {\n return (\n <div className=\"flex size-4 shrink-0 items-center justify-center text-content-secondary\">\n {children}\n </div>\n );\n}\n\nfunction TextFieldHelperText({\n id,\n error,\n children,\n}: {\n id: string;\n error: boolean;\n children: React.ReactNode;\n}) {\n return (\n <p\n id={id}\n className={cn(\n \"typography-regular-body-sm px-2 pt-2 pb-0.5\",\n error ? \"text-error-content\" : \"text-content-secondary\",\n )}\n >\n {children}\n </p>\n );\n}\n\nfunction warnMissingAccessibleName(label?: string, ariaLabel?: string, ariaLabelledBy?: string) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!label && !ariaLabel && !ariaLabelledBy) {\n console.warn(\n \"TextField: no accessible name provided. Pass a `label`, `aria-label`, or `aria-labelledby` prop.\",\n );\n }\n }\n}\n\n/**\n * A text input field with optional label, helper/error text, and icon slots.\n *\n * Provide at least one of `label`, `aria-label`, or `aria-labelledby` for\n * accessibility — a console warning is emitted in development if none are set.\n *\n * @example\n * ```tsx\n * <TextField\n * label=\"Email\"\n * placeholder=\"you@example.com\"\n * error={!!emailError}\n * errorMessage={emailError}\n * />\n * ```\n */\nexport const TextField = React.forwardRef<HTMLInputElement, TextFieldProps>(\n (\n {\n label,\n helperText,\n size = \"48\",\n error = false,\n errorMessage,\n validated = false,\n leftIcon,\n rightIcon,\n className,\n id,\n disabled,\n fullWidth = false,\n ...props\n },\n ref,\n ) => {\n const generatedId = React.useId();\n const inputId = id || generatedId;\n const helperTextId = `${inputId}-helper`;\n const bottomText = error && errorMessage ? errorMessage : helperText;\n\n warnMissingAccessibleName(label, props[\"aria-label\"], props[\"aria-labelledby\"]);\n\n return (\n <div\n className={cn(\"flex flex-col\", fullWidth && \"w-full\", className)}\n data-disabled={disabled ? \"\" : undefined}\n data-error={error ? \"\" : undefined}\n >\n {label && (\n <label\n htmlFor={inputId}\n className=\"typography-semibold-body-sm px-1 pt-1 pb-2 text-content-primary\"\n >\n {label}\n </label>\n )}\n\n <div className={getContainerClassName(size, error, disabled)}>\n {leftIcon && <TextFieldIcon>{leftIcon}</TextFieldIcon>}\n\n <input\n ref={ref}\n id={inputId}\n disabled={disabled}\n aria-describedby={bottomText ? helperTextId : undefined}\n aria-invalid={error || undefined}\n className={cn(\n getInputClassName(size),\n // Hide native clear button for input[type=\"search\"] in WebKit browsers (Safari/Chrome)\n \"[&[type='search']::-webkit-search-cancel-button]:hidden [&[type='search']::-webkit-search-cancel-button]:appearance-none\",\n )}\n {...props}\n />\n\n {rightIcon && <TextFieldIcon>{rightIcon}</TextFieldIcon>}\n {validated && (\n <TextFieldIcon>\n <CheckOutlineIcon className=\"text-success-content\" />\n </TextFieldIcon>\n )}\n </div>\n\n {bottomText && (\n <TextFieldHelperText id={helperTextId} error={error}>\n {bottomText}\n </TextFieldHelperText>\n )}\n </div>\n );\n },\n);\n\nTextField.displayName = \"TextField\";\n"],"names":[],"mappings":"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"}
|
|
@@ -27,13 +27,13 @@ ToastViewport.displayName = "ToastViewport";
|
|
|
27
27
|
const VariantIcon = ({ variant }) => {
|
|
28
28
|
switch (variant) {
|
|
29
29
|
case "info":
|
|
30
|
-
return /* @__PURE__ */ jsx(InfoIcon, { className: "size-5 text-info-
|
|
30
|
+
return /* @__PURE__ */ jsx(InfoIcon, { className: "size-5 text-info-content" });
|
|
31
31
|
case "warning":
|
|
32
|
-
return /* @__PURE__ */ jsx(WarningIcon, { className: "size-5 text-warning-
|
|
32
|
+
return /* @__PURE__ */ jsx(WarningIcon, { className: "size-5 text-warning-content" });
|
|
33
33
|
case "success":
|
|
34
|
-
return /* @__PURE__ */ jsx(SuccessIcon, { className: "size-5 text-success-
|
|
34
|
+
return /* @__PURE__ */ jsx(SuccessIcon, { className: "size-5 text-success-content" });
|
|
35
35
|
case "error":
|
|
36
|
-
return /* @__PURE__ */ jsx(ErrorIcon, { className: "size-5 text-error-
|
|
36
|
+
return /* @__PURE__ */ jsx(ErrorIcon, { className: "size-5 text-error-content" });
|
|
37
37
|
}
|
|
38
38
|
};
|
|
39
39
|
const Toast = React.forwardRef(
|
|
@@ -59,7 +59,7 @@ const Toast = React.forwardRef(
|
|
|
59
59
|
"data-testid": "toast",
|
|
60
60
|
className: cn(
|
|
61
61
|
// Base styles
|
|
62
|
-
"group pointer-events-auto relative flex w-full flex-col items-start gap-3 overflow-hidden rounded-
|
|
62
|
+
"group pointer-events-auto relative flex w-full flex-col items-start gap-3 overflow-hidden rounded-xs border-none bg-surface-primary-inverted p-4 text-content-primary-inverted shadow-lg transition-all",
|
|
63
63
|
// Dark mode
|
|
64
64
|
"dark:border-opacity-100",
|
|
65
65
|
// Animation
|
|
@@ -79,7 +79,7 @@ const Toast = React.forwardRef(
|
|
|
79
79
|
Button,
|
|
80
80
|
{
|
|
81
81
|
variant: "secondary",
|
|
82
|
-
className: "mt-4 border-
|
|
82
|
+
className: "mt-4 border-content-primary-inverted text-content-primary-inverted",
|
|
83
83
|
size: "32",
|
|
84
84
|
onClick: onActionClick,
|
|
85
85
|
children: actionLabel ?? "Action"
|
|
@@ -92,7 +92,7 @@ const Toast = React.forwardRef(
|
|
|
92
92
|
{
|
|
93
93
|
icon: /* @__PURE__ */ jsx(CloseIcon, {}),
|
|
94
94
|
"aria-label": closeLabel,
|
|
95
|
-
className: "absolute top-2 right-2 text-
|
|
95
|
+
className: "absolute top-2 right-2 text-content-primary-inverted",
|
|
96
96
|
variant: "tertiary",
|
|
97
97
|
size: "24"
|
|
98
98
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Toast.mjs","sources":["../../../src/components/Toast/Toast.tsx"],"sourcesContent":["import * as ToastPrimitive from \"@radix-ui/react-toast\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Avatar } from \"../Avatar/Avatar\";\nimport { Button } from \"../Button/Button\";\nimport { IconButton } from \"../IconButton/IconButton\";\nimport { CloseIcon } from \"../Icons/CloseIcon\";\nimport { ErrorIcon } from \"../Icons/ErrorIcon\";\nimport { InfoIcon } from \"../Icons/InfoIcon\";\nimport { SuccessIcon } from \"../Icons/SuccessIcon\";\nimport { WarningIcon } from \"../Icons/WarningIcon\";\n\n/** Visual/semantic variant of the toast notification. */\nexport type ToastVariant = \"info\" | \"warning\" | \"success\" | \"error\" | \"messageToast\";\n\nexport interface ToastProps\n extends Omit<Omit<React.ComponentPropsWithoutRef<typeof ToastPrimitive.Root>, \"type\">, \"title\"> {\n /** Visual/semantic variant of the toast. @default \"info\" */\n variant?: ToastVariant;\n /** Title text displayed in bold at the top of the toast. */\n title?: string;\n /** Description or body content displayed below the title. */\n description?: React.ReactNode;\n /** Label for the optional action button. @default \"Action\" */\n actionLabel?: string;\n /** Click handler for the action button. When provided, the action button is rendered. */\n onActionClick?: () => void;\n /** Whether to show the close button. @default true */\n showClose?: boolean;\n /** Accessible label for the close button. @default \"Close notification\" */\n closeLabel?: string;\n /** Avatar image URL (used by the `messageToast` variant). */\n avatarSrc?: string;\n /** Alt text for the avatar image. */\n avatarAlt?: string;\n /** Fallback content for the avatar (e.g. initials). */\n avatarFallback?: string;\n}\n\n/** Props for the {@link ToastProvider}. Wraps Radix `Toast.Provider`. */\nexport interface ToastProviderProps extends ToastPrimitive.ToastProviderProps {}\n/** Props for the {@link ToastViewport}. Controls where toasts are rendered on screen. */\nexport interface ToastViewportProps\n extends React.ComponentPropsWithoutRef<typeof ToastPrimitive.Viewport> {}\n\n/** Provides toast context. Wrap your application (or a subtree) with this provider. */\nexport const ToastProvider: React.FC<ToastProviderProps> = ToastPrimitive.Provider;\n\n/** Fixed-position container that renders active toasts. Place once at the root of your app. */\nexport const ToastViewport = React.forwardRef<\n React.ElementRef<typeof ToastPrimitive.Viewport>,\n ToastViewportProps\n>(({ className, ...props }, ref) => (\n <ToastPrimitive.Viewport\n ref={ref}\n className={cn(\n \"pointer-events-none fixed right-0 bottom-0 z-100 flex max-h-screen w-full flex-col-reverse gap-3 p-4 md:max-w-[420px]\",\n className,\n )}\n {...props}\n />\n));\n\nToastViewport.displayName = \"ToastViewport\";\n\nconst VariantIcon = ({ variant }: { variant: ToastVariant }) => {\n switch (variant) {\n case \"info\":\n return <InfoIcon className=\"size-5 text-info-
|
|
1
|
+
{"version":3,"file":"Toast.mjs","sources":["../../../src/components/Toast/Toast.tsx"],"sourcesContent":["import * as ToastPrimitive from \"@radix-ui/react-toast\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Avatar } from \"../Avatar/Avatar\";\nimport { Button } from \"../Button/Button\";\nimport { IconButton } from \"../IconButton/IconButton\";\nimport { CloseIcon } from \"../Icons/CloseIcon\";\nimport { ErrorIcon } from \"../Icons/ErrorIcon\";\nimport { InfoIcon } from \"../Icons/InfoIcon\";\nimport { SuccessIcon } from \"../Icons/SuccessIcon\";\nimport { WarningIcon } from \"../Icons/WarningIcon\";\n\n/** Visual/semantic variant of the toast notification. */\nexport type ToastVariant = \"info\" | \"warning\" | \"success\" | \"error\" | \"messageToast\";\n\nexport interface ToastProps\n extends Omit<Omit<React.ComponentPropsWithoutRef<typeof ToastPrimitive.Root>, \"type\">, \"title\"> {\n /** Visual/semantic variant of the toast. @default \"info\" */\n variant?: ToastVariant;\n /** Title text displayed in bold at the top of the toast. */\n title?: string;\n /** Description or body content displayed below the title. */\n description?: React.ReactNode;\n /** Label for the optional action button. @default \"Action\" */\n actionLabel?: string;\n /** Click handler for the action button. When provided, the action button is rendered. */\n onActionClick?: () => void;\n /** Whether to show the close button. @default true */\n showClose?: boolean;\n /** Accessible label for the close button. @default \"Close notification\" */\n closeLabel?: string;\n /** Avatar image URL (used by the `messageToast` variant). */\n avatarSrc?: string;\n /** Alt text for the avatar image. */\n avatarAlt?: string;\n /** Fallback content for the avatar (e.g. initials). */\n avatarFallback?: string;\n}\n\n/** Props for the {@link ToastProvider}. Wraps Radix `Toast.Provider`. */\nexport interface ToastProviderProps extends ToastPrimitive.ToastProviderProps {}\n/** Props for the {@link ToastViewport}. Controls where toasts are rendered on screen. */\nexport interface ToastViewportProps\n extends React.ComponentPropsWithoutRef<typeof ToastPrimitive.Viewport> {}\n\n/** Provides toast context. Wrap your application (or a subtree) with this provider. */\nexport const ToastProvider: React.FC<ToastProviderProps> = ToastPrimitive.Provider;\n\n/** Fixed-position container that renders active toasts. Place once at the root of your app. */\nexport const ToastViewport = React.forwardRef<\n React.ElementRef<typeof ToastPrimitive.Viewport>,\n ToastViewportProps\n>(({ className, ...props }, ref) => (\n <ToastPrimitive.Viewport\n ref={ref}\n className={cn(\n \"pointer-events-none fixed right-0 bottom-0 z-100 flex max-h-screen w-full flex-col-reverse gap-3 p-4 md:max-w-[420px]\",\n className,\n )}\n {...props}\n />\n));\n\nToastViewport.displayName = \"ToastViewport\";\n\nconst VariantIcon = ({ variant }: { variant: ToastVariant }) => {\n switch (variant) {\n case \"info\":\n return <InfoIcon className=\"size-5 text-info-content\" />;\n case \"warning\":\n return <WarningIcon className=\"size-5 text-warning-content\" />;\n case \"success\":\n return <SuccessIcon className=\"size-5 text-success-content\" />;\n case \"error\":\n return <ErrorIcon className=\"size-5 text-error-content\" />;\n }\n};\n\n/**\n * A dismissible notification that appears temporarily. Supports `info`,\n * `warning`, `success`, `error`, and `messageToast` variants with optional\n * action button, close control, and avatar.\n *\n * Use inside a {@link ToastProvider} with a {@link ToastViewport}.\n *\n * @example\n * ```tsx\n * <Toast variant=\"success\" title=\"Saved\" description=\"Your changes are live.\" />\n * ```\n */\nexport const Toast = React.forwardRef<React.ComponentRef<typeof ToastPrimitive.Root>, ToastProps>(\n (\n {\n className,\n variant = \"info\",\n title,\n description,\n actionLabel,\n onActionClick,\n showClose = true,\n closeLabel = \"Close notification\",\n avatarSrc,\n avatarAlt,\n avatarFallback,\n children,\n ...props\n },\n ref,\n ) => {\n return (\n <ToastPrimitive.Root\n ref={ref}\n data-testid=\"toast\"\n className={cn(\n // Base styles\n \"group pointer-events-auto relative flex w-full flex-col items-start gap-3 overflow-hidden rounded-xs border-none bg-surface-primary-inverted p-4 text-content-primary-inverted shadow-lg transition-all\",\n // Dark mode\n \"dark:border-opacity-100\",\n // Animation\n \"data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-bottom-full data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-(--radix-toast-swipe-end-x) data-[swipe=move]:translate-x-(--radix-toast-swipe-move-x) data-[state=closed]:animate-out data-[state=open]:animate-in data-[swipe=end]:animate-out data-[swipe=move]:transition-none\",\n // Manual CSS overrides\n className,\n )}\n {...props}\n >\n <div className=\"flex w-full items-center gap-3\">\n <div className=\"self-start\">\n {variant === \"messageToast\" ? (\n avatarSrc && <Avatar src={avatarSrc} alt={avatarAlt} fallback={avatarFallback} />\n ) : (\n <VariantIcon variant={variant} />\n )}\n </div>\n <div className=\"flex flex-1 flex-col items-start\">\n {title && (\n <ToastPrimitive.Title className=\"typography-semibold-body-md\">\n {title}\n </ToastPrimitive.Title>\n )}\n {description && (\n <ToastPrimitive.Description className=\"typography-regular-body-md mt-1 opacity-90\">\n {description}\n </ToastPrimitive.Description>\n )}\n {children}\n {onActionClick && (\n <Button\n variant=\"secondary\"\n // These styles are basically inverted from the selected theme\n className=\"mt-4 border-content-primary-inverted text-content-primary-inverted\"\n size=\"32\"\n onClick={onActionClick}\n >\n {actionLabel ?? \"Action\"}\n </Button>\n )}\n </div>\n </div>\n {showClose && (\n <ToastPrimitive.Close asChild>\n <IconButton\n icon={<CloseIcon />}\n aria-label={closeLabel}\n // same as the button above\n className=\"absolute top-2 right-2 text-content-primary-inverted\"\n variant=\"tertiary\"\n size=\"24\"\n />\n </ToastPrimitive.Close>\n )}\n </ToastPrimitive.Root>\n );\n },\n);\n\nToast.displayName = \"Toast\";\n"],"names":[],"mappings":";;;;;;;;;;;;;AA8CO,MAAM,gBAA8C,eAAe;AAGnE,MAAM,gBAAgB,MAAM,WAGjC,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QAC1B;AAAA,EAAC,eAAe;AAAA,EAAf;AAAA,IACC;AAAA,IACA,WAAW;AAAA,MACT;AAAA,MACA;AAAA,IAAA;AAAA,IAED,GAAG;AAAA,EAAA;AACN,CACD;AAED,cAAc,cAAc;AAE5B,MAAM,cAAc,CAAC,EAAE,cAAyC;AAC9D,UAAQ,SAAA;AAAA,IACN,KAAK;AACH,aAAO,oBAAC,UAAA,EAAS,WAAU,2BAAA,CAA2B;AAAA,IACxD,KAAK;AACH,aAAO,oBAAC,aAAA,EAAY,WAAU,8BAAA,CAA8B;AAAA,IAC9D,KAAK;AACH,aAAO,oBAAC,aAAA,EAAY,WAAU,8BAAA,CAA8B;AAAA,IAC9D,KAAK;AACH,aAAO,oBAAC,WAAA,EAAU,WAAU,4BAAA,CAA4B;AAAA,EAAA;AAE9D;AAcO,MAAM,QAAQ,MAAM;AAAA,EACzB,CACE;AAAA,IACE;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ,aAAa;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QACG;AACH,WACE;AAAA,MAAC,eAAe;AAAA,MAAf;AAAA,QACC;AAAA,QACA,eAAY;AAAA,QACZ,WAAW;AAAA;AAAA,UAET;AAAA;AAAA,UAEA;AAAA;AAAA,UAEA;AAAA;AAAA,UAEA;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEJ,UAAA;AAAA,UAAA,qBAAC,OAAA,EAAI,WAAU,kCACb,UAAA;AAAA,YAAA,oBAAC,SAAI,WAAU,cACZ,sBAAY,iBACX,iCAAc,QAAA,EAAO,KAAK,WAAW,KAAK,WAAW,UAAU,eAAA,CAAgB,IAE/E,oBAAC,aAAA,EAAY,SAAkB,EAAA,CAEnC;AAAA,YACA,qBAAC,OAAA,EAAI,WAAU,oCACZ,UAAA;AAAA,cAAA,6BACE,eAAe,OAAf,EAAqB,WAAU,+BAC7B,UAAA,OACH;AAAA,cAED,eACC,oBAAC,eAAe,aAAf,EAA2B,WAAU,8CACnC,UAAA,aACH;AAAA,cAED;AAAA,cACA,iBACC;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,SAAQ;AAAA,kBAER,WAAU;AAAA,kBACV,MAAK;AAAA,kBACL,SAAS;AAAA,kBAER,UAAA,eAAe;AAAA,gBAAA;AAAA,cAAA;AAAA,YAClB,EAAA,CAEJ;AAAA,UAAA,GACF;AAAA,UACC,aACC,oBAAC,eAAe,OAAf,EAAqB,SAAO,MAC3B,UAAA;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,0BAAO,WAAA,EAAU;AAAA,cACjB,cAAY;AAAA,cAEZ,WAAU;AAAA,cACV,SAAQ;AAAA,cACR,MAAK;AAAA,YAAA;AAAA,UAAA,EACP,CACF;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEA,MAAM,cAAc;"}
|
|
@@ -15,7 +15,7 @@ const TooltipContent = React.forwardRef(({ className, sideOffset = 8, style, ...
|
|
|
15
15
|
collisionPadding: 8,
|
|
16
16
|
style: { zIndex: "var(--fanvue-ui-portal-z-index, 50)", ...style },
|
|
17
17
|
className: cn(
|
|
18
|
-
"typography-semibold-body-sm max-w-[320px] rounded-full bg-surface-
|
|
18
|
+
"typography-semibold-body-sm max-w-[320px] rounded-full bg-surface-primary-inverted px-4 py-2 text-content-primary-inverted shadow-[0px_1px_4px_0px_rgba(0,0,0,0.06),0px_1px_3px_0px_rgba(0,0,0,0.05)]",
|
|
19
19
|
className
|
|
20
20
|
),
|
|
21
21
|
align: "center",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tooltip.mjs","sources":["../../../src/components/Tooltip/Tooltip.tsx"],"sourcesContent":["import * as TooltipPrimitive from \"@radix-ui/react-tooltip\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Props for the {@link TooltipProvider}. Wraps Radix `Tooltip.Provider`. */\nexport type TooltipProviderProps = React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Provider>;\n\n/** Provides tooltip delay and skip-delay context. Wrap your app or a subtree. */\nexport const TooltipProvider = TooltipPrimitive.Provider;\n\n/** Props for the {@link Tooltip} root component. */\nexport interface TooltipProps extends React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Root> {\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n defaultOpen?: boolean;\n}\n\n/** Root component that manages open/close state for a single tooltip. */\nexport const Tooltip = TooltipPrimitive.Root;\n\n/** Props for the {@link TooltipTrigger} component. */\nexport type TooltipTriggerProps = React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Trigger>;\n\n/** The element that triggers the tooltip on hover/focus. */\nexport const TooltipTrigger = TooltipPrimitive.Trigger;\n\nexport type TooltipContentProps = React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>;\n\nexport const TooltipContent = React.forwardRef<\n React.ComponentRef<typeof TooltipPrimitive.Content>,\n TooltipContentProps\n>(({ className, sideOffset = 8, style, ...props }, ref) => {\n return (\n <TooltipPrimitive.Portal>\n <TooltipPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n collisionPadding={8}\n style={{ zIndex: \"var(--fanvue-ui-portal-z-index, 50)\", ...style }}\n className={cn(\n \"typography-semibold-body-sm max-w-[320px] rounded-full bg-surface-
|
|
1
|
+
{"version":3,"file":"Tooltip.mjs","sources":["../../../src/components/Tooltip/Tooltip.tsx"],"sourcesContent":["import * as TooltipPrimitive from \"@radix-ui/react-tooltip\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Props for the {@link TooltipProvider}. Wraps Radix `Tooltip.Provider`. */\nexport type TooltipProviderProps = React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Provider>;\n\n/** Provides tooltip delay and skip-delay context. Wrap your app or a subtree. */\nexport const TooltipProvider = TooltipPrimitive.Provider;\n\n/** Props for the {@link Tooltip} root component. */\nexport interface TooltipProps extends React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Root> {\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n defaultOpen?: boolean;\n}\n\n/** Root component that manages open/close state for a single tooltip. */\nexport const Tooltip = TooltipPrimitive.Root;\n\n/** Props for the {@link TooltipTrigger} component. */\nexport type TooltipTriggerProps = React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Trigger>;\n\n/** The element that triggers the tooltip on hover/focus. */\nexport const TooltipTrigger = TooltipPrimitive.Trigger;\n\nexport type TooltipContentProps = React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>;\n\nexport const TooltipContent = React.forwardRef<\n React.ComponentRef<typeof TooltipPrimitive.Content>,\n TooltipContentProps\n>(({ className, sideOffset = 8, style, ...props }, ref) => {\n return (\n <TooltipPrimitive.Portal>\n <TooltipPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n collisionPadding={8}\n style={{ zIndex: \"var(--fanvue-ui-portal-z-index, 50)\", ...style }}\n className={cn(\n \"typography-semibold-body-sm max-w-[320px] rounded-full bg-surface-primary-inverted px-4 py-2 text-content-primary-inverted shadow-[0px_1px_4px_0px_rgba(0,0,0,0.06),0px_1px_3px_0px_rgba(0,0,0,0.05)]\",\n className,\n )}\n align=\"center\"\n {...props}\n />\n </TooltipPrimitive.Portal>\n );\n});\nTooltipContent.displayName = \"TooltipContent\";\n"],"names":[],"mappings":";;;;;AAQO,MAAM,kBAAkB,iBAAiB;AAUzC,MAAM,UAAU,iBAAiB;AAMjC,MAAM,iBAAiB,iBAAiB;AAIxC,MAAM,iBAAiB,MAAM,WAGlC,CAAC,EAAE,WAAW,aAAa,GAAG,OAAO,GAAG,MAAA,GAAS,QAAQ;AACzD,SACE,oBAAC,iBAAiB,QAAjB,EACC,UAAA;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC;AAAA,MACA;AAAA,MACA,kBAAkB;AAAA,MAClB,OAAO,EAAE,QAAQ,uCAAuC,GAAG,MAAA;AAAA,MAC3D,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MAAA;AAAA,MAEF,OAAM;AAAA,MACL,GAAG;AAAA,IAAA;AAAA,EAAA,GAER;AAEJ,CAAC;AACD,eAAe,cAAc;"}
|