@fanvue/ui 2.21.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/Accordion/AccordionContent.cjs +1 -1
- package/dist/cjs/components/Accordion/AccordionContent.cjs.map +1 -1
- package/dist/cjs/components/Accordion/AccordionTrigger.cjs +2 -2
- package/dist/cjs/components/Accordion/AccordionTrigger.cjs.map +1 -1
- package/dist/cjs/components/Alert/Alert.cjs +2 -2
- package/dist/cjs/components/Alert/Alert.cjs.map +1 -1
- package/dist/cjs/components/AudioUpload/AudioUpload.cjs +6 -6
- package/dist/cjs/components/AudioUpload/AudioUpload.cjs.map +1 -1
- package/dist/cjs/components/Autocomplete/Autocomplete.cjs +6 -6
- package/dist/cjs/components/Autocomplete/Autocomplete.cjs.map +1 -1
- package/dist/cjs/components/Autocomplete/AutocompleteDropdownContent.cjs +4 -4
- package/dist/cjs/components/Autocomplete/AutocompleteDropdownContent.cjs.map +1 -1
- package/dist/cjs/components/Autocomplete/AutocompleteOptionItem.cjs +1 -1
- package/dist/cjs/components/Autocomplete/AutocompleteOptionItem.cjs.map +1 -1
- package/dist/cjs/components/Autocomplete/AutocompleteTag.cjs +1 -1
- package/dist/cjs/components/Autocomplete/AutocompleteTag.cjs.map +1 -1
- package/dist/cjs/components/Badge/Badge.cjs +35 -21
- package/dist/cjs/components/Badge/Badge.cjs.map +1 -1
- package/dist/cjs/components/Banner/Banner.cjs +11 -11
- package/dist/cjs/components/Banner/Banner.cjs.map +1 -1
- package/dist/cjs/components/BottomNavigation/BottomNavigation.cjs +2 -2
- 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 +2 -2
- package/dist/cjs/components/Breadcrumb/Breadcrumb.cjs.map +1 -1
- package/dist/cjs/components/Button/Button.cjs +9 -9
- package/dist/cjs/components/Button/Button.cjs.map +1 -1
- package/dist/cjs/components/Card/Card.cjs +2 -2
- package/dist/cjs/components/Card/Card.cjs.map +1 -1
- package/dist/cjs/components/Chart/ChartCard.cjs +4 -4
- package/dist/cjs/components/Chart/ChartCard.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 +1 -1
- package/dist/cjs/components/Chart/ChartSeriesToggle.cjs.map +1 -1
- package/dist/cjs/components/ChatInput/ChatInput.cjs +4 -4
- package/dist/cjs/components/ChatInput/ChatInput.cjs.map +1 -1
- package/dist/cjs/components/Checkbox/Checkbox.cjs +3 -3
- 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/CreatorCard/CreatorCard.cjs +4 -4
- package/dist/cjs/components/CreatorCard/CreatorCard.cjs.map +1 -1
- package/dist/cjs/components/CreatorCover/CreatorCover.cjs +5 -5
- package/dist/cjs/components/CreatorCover/CreatorCover.cjs.map +1 -1
- package/dist/cjs/components/CreatorTile/CreatorTile.cjs +2 -2
- package/dist/cjs/components/CreatorTile/CreatorTile.cjs.map +1 -1
- package/dist/cjs/components/DatePicker/DatePicker.cjs +5 -5
- package/dist/cjs/components/DatePicker/DatePicker.cjs.map +1 -1
- package/dist/cjs/components/Dialog/Dialog.cjs +4 -4
- package/dist/cjs/components/Dialog/Dialog.cjs.map +1 -1
- package/dist/cjs/components/Divider/Divider.cjs +1 -1
- package/dist/cjs/components/Divider/Divider.cjs.map +1 -1
- package/dist/cjs/components/Drawer/Drawer.cjs +3 -3
- package/dist/cjs/components/Drawer/Drawer.cjs.map +1 -1
- package/dist/cjs/components/DropdownMenu/DropdownMenu.cjs +13 -13
- package/dist/cjs/components/DropdownMenu/DropdownMenu.cjs.map +1 -1
- package/dist/cjs/components/EmptyState/EmptyState.cjs +6 -6
- package/dist/cjs/components/EmptyState/EmptyState.cjs.map +1 -1
- package/dist/cjs/components/IconButton/IconButton.cjs +6 -6
- package/dist/cjs/components/IconButton/IconButton.cjs.map +1 -1
- package/dist/cjs/components/InfoBox/InfoBox.cjs +4 -4
- package/dist/cjs/components/InfoBox/InfoBox.cjs.map +1 -1
- package/dist/cjs/components/InlineEdit/InlineEdit.cjs +1 -1
- package/dist/cjs/components/InlineEdit/InlineEdit.cjs.map +1 -1
- package/dist/cjs/components/Logo/Logo.cjs +2 -2
- package/dist/cjs/components/Logo/Logo.cjs.map +1 -1
- package/dist/cjs/components/MobileStepper/MobileStepper.cjs +1 -1
- package/dist/cjs/components/MobileStepper/MobileStepper.cjs.map +1 -1
- package/dist/cjs/components/Pagination/Pagination.cjs +1 -1
- package/dist/cjs/components/Pagination/Pagination.cjs.map +1 -1
- package/dist/cjs/components/Pill/Pill.cjs +5 -5
- package/dist/cjs/components/Pill/Pill.cjs.map +1 -1
- package/dist/cjs/components/ProgressBar/ProgressBar.cjs +5 -5
- package/dist/cjs/components/ProgressBar/ProgressBar.cjs.map +1 -1
- package/dist/cjs/components/Radio/Radio.cjs +3 -3
- package/dist/cjs/components/Radio/Radio.cjs.map +1 -1
- package/dist/cjs/components/Select/Select.cjs +11 -8
- package/dist/cjs/components/Select/Select.cjs.map +1 -1
- package/dist/cjs/components/Skeleton/Skeleton.cjs +1 -1
- package/dist/cjs/components/Skeleton/Skeleton.cjs.map +1 -1
- package/dist/cjs/components/Slider/SliderLayout.cjs +12 -5
- package/dist/cjs/components/Slider/SliderLayout.cjs.map +1 -1
- package/dist/cjs/components/Slider/SliderThumb.cjs +3 -3
- package/dist/cjs/components/Slider/SliderThumb.cjs.map +1 -1
- package/dist/cjs/components/Snackbar/Snackbar.cjs +6 -6
- package/dist/cjs/components/Snackbar/Snackbar.cjs.map +1 -1
- package/dist/cjs/components/Stepper/StepperStep.cjs +9 -9
- package/dist/cjs/components/Stepper/StepperStep.cjs.map +1 -1
- package/dist/cjs/components/Switch/Switch.cjs +1 -1
- package/dist/cjs/components/Switch/Switch.cjs.map +1 -1
- package/dist/cjs/components/SwitchField/SwitchField.cjs +2 -2
- package/dist/cjs/components/SwitchField/SwitchField.cjs.map +1 -1
- package/dist/cjs/components/SwitchToggle/SwitchToggle.cjs +1 -1
- package/dist/cjs/components/SwitchToggle/SwitchToggle.cjs.map +1 -1
- package/dist/cjs/components/Table/Table.cjs +7 -7
- package/dist/cjs/components/Table/Table.cjs.map +1 -1
- package/dist/cjs/components/Table/TablePagination.cjs +2 -2
- package/dist/cjs/components/Table/TablePagination.cjs.map +1 -1
- package/dist/cjs/components/Tabs/TabsTrigger.cjs +2 -2
- package/dist/cjs/components/Tabs/TabsTrigger.cjs.map +1 -1
- package/dist/cjs/components/TextArea/TextArea.cjs +5 -5
- package/dist/cjs/components/TextArea/TextArea.cjs.map +1 -1
- package/dist/cjs/components/TextField/TextField.cjs +5 -5
- package/dist/cjs/components/TextField/TextField.cjs.map +1 -1
- package/dist/cjs/components/Toast/Toast.cjs +2 -2
- 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/AccordionTrigger.mjs +2 -2
- package/dist/components/Accordion/AccordionTrigger.mjs.map +1 -1
- package/dist/components/Alert/Alert.mjs +2 -2
- package/dist/components/Alert/Alert.mjs.map +1 -1
- package/dist/components/AudioUpload/AudioUpload.mjs +6 -6
- package/dist/components/AudioUpload/AudioUpload.mjs.map +1 -1
- package/dist/components/Autocomplete/Autocomplete.mjs +6 -6
- package/dist/components/Autocomplete/Autocomplete.mjs.map +1 -1
- package/dist/components/Autocomplete/AutocompleteDropdownContent.mjs +4 -4
- package/dist/components/Autocomplete/AutocompleteDropdownContent.mjs.map +1 -1
- package/dist/components/Autocomplete/AutocompleteOptionItem.mjs +1 -1
- package/dist/components/Autocomplete/AutocompleteOptionItem.mjs.map +1 -1
- package/dist/components/Autocomplete/AutocompleteTag.mjs +1 -1
- package/dist/components/Autocomplete/AutocompleteTag.mjs.map +1 -1
- package/dist/components/Badge/Badge.mjs +35 -21
- package/dist/components/Badge/Badge.mjs.map +1 -1
- package/dist/components/Banner/Banner.mjs +11 -11
- package/dist/components/Banner/Banner.mjs.map +1 -1
- package/dist/components/BottomNavigation/BottomNavigation.mjs +2 -2
- 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 +2 -2
- package/dist/components/Breadcrumb/Breadcrumb.mjs.map +1 -1
- package/dist/components/Button/Button.mjs +9 -9
- package/dist/components/Button/Button.mjs.map +1 -1
- package/dist/components/Card/Card.mjs +2 -2
- package/dist/components/Card/Card.mjs.map +1 -1
- package/dist/components/Chart/ChartCard.mjs +4 -4
- package/dist/components/Chart/ChartCard.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 +1 -1
- package/dist/components/Chart/ChartSeriesToggle.mjs.map +1 -1
- package/dist/components/ChatInput/ChatInput.mjs +4 -4
- package/dist/components/ChatInput/ChatInput.mjs.map +1 -1
- package/dist/components/Checkbox/Checkbox.mjs +3 -3
- 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/CreatorCard/CreatorCard.mjs +4 -4
- package/dist/components/CreatorCard/CreatorCard.mjs.map +1 -1
- package/dist/components/CreatorCover/CreatorCover.mjs +5 -5
- package/dist/components/CreatorCover/CreatorCover.mjs.map +1 -1
- package/dist/components/CreatorTile/CreatorTile.mjs +2 -2
- package/dist/components/CreatorTile/CreatorTile.mjs.map +1 -1
- package/dist/components/DatePicker/DatePicker.mjs +5 -5
- package/dist/components/DatePicker/DatePicker.mjs.map +1 -1
- package/dist/components/Dialog/Dialog.mjs +4 -4
- package/dist/components/Dialog/Dialog.mjs.map +1 -1
- package/dist/components/Divider/Divider.mjs +1 -1
- package/dist/components/Divider/Divider.mjs.map +1 -1
- package/dist/components/Drawer/Drawer.mjs +3 -3
- package/dist/components/Drawer/Drawer.mjs.map +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.mjs +13 -13
- package/dist/components/DropdownMenu/DropdownMenu.mjs.map +1 -1
- package/dist/components/EmptyState/EmptyState.mjs +6 -6
- package/dist/components/EmptyState/EmptyState.mjs.map +1 -1
- package/dist/components/IconButton/IconButton.mjs +6 -6
- package/dist/components/IconButton/IconButton.mjs.map +1 -1
- package/dist/components/InfoBox/InfoBox.mjs +4 -4
- package/dist/components/InfoBox/InfoBox.mjs.map +1 -1
- package/dist/components/InlineEdit/InlineEdit.mjs +1 -1
- package/dist/components/InlineEdit/InlineEdit.mjs.map +1 -1
- package/dist/components/Logo/Logo.mjs +2 -2
- package/dist/components/Logo/Logo.mjs.map +1 -1
- package/dist/components/MobileStepper/MobileStepper.mjs +1 -1
- package/dist/components/MobileStepper/MobileStepper.mjs.map +1 -1
- package/dist/components/Pagination/Pagination.mjs +1 -1
- package/dist/components/Pagination/Pagination.mjs.map +1 -1
- package/dist/components/Pill/Pill.mjs +5 -5
- package/dist/components/Pill/Pill.mjs.map +1 -1
- package/dist/components/ProgressBar/ProgressBar.mjs +5 -5
- package/dist/components/ProgressBar/ProgressBar.mjs.map +1 -1
- package/dist/components/Radio/Radio.mjs +3 -3
- package/dist/components/Radio/Radio.mjs.map +1 -1
- package/dist/components/Select/Select.mjs +11 -8
- package/dist/components/Select/Select.mjs.map +1 -1
- package/dist/components/Skeleton/Skeleton.mjs +1 -1
- package/dist/components/Skeleton/Skeleton.mjs.map +1 -1
- package/dist/components/Slider/SliderLayout.mjs +12 -5
- package/dist/components/Slider/SliderLayout.mjs.map +1 -1
- package/dist/components/Slider/SliderThumb.mjs +3 -3
- package/dist/components/Slider/SliderThumb.mjs.map +1 -1
- package/dist/components/Snackbar/Snackbar.mjs +6 -6
- package/dist/components/Snackbar/Snackbar.mjs.map +1 -1
- package/dist/components/Stepper/StepperStep.mjs +9 -9
- package/dist/components/Stepper/StepperStep.mjs.map +1 -1
- package/dist/components/Switch/Switch.mjs +1 -1
- package/dist/components/Switch/Switch.mjs.map +1 -1
- package/dist/components/SwitchField/SwitchField.mjs +2 -2
- package/dist/components/SwitchField/SwitchField.mjs.map +1 -1
- package/dist/components/SwitchToggle/SwitchToggle.mjs +1 -1
- package/dist/components/SwitchToggle/SwitchToggle.mjs.map +1 -1
- package/dist/components/Table/Table.mjs +7 -7
- package/dist/components/Table/Table.mjs.map +1 -1
- package/dist/components/Table/TablePagination.mjs +2 -2
- package/dist/components/Table/TablePagination.mjs.map +1 -1
- package/dist/components/Tabs/TabsTrigger.mjs +2 -2
- package/dist/components/Tabs/TabsTrigger.mjs.map +1 -1
- package/dist/components/TextArea/TextArea.mjs +5 -5
- package/dist/components/TextArea/TextArea.mjs.map +1 -1
- package/dist/components/TextField/TextField.mjs +5 -5
- package/dist/components/TextField/TextField.mjs.map +1 -1
- package/dist/components/Toast/Toast.mjs +2 -2
- 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/base.css +2 -2
- package/dist/styles/theme.css +626 -195
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EmptyState.mjs","sources":["../../../src/components/EmptyState/EmptyState.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Button } from \"../Button/Button\";\n\nexport type EmptyStateVariant = \"default\" | \"centered\";\n\nexport type EmptyStateTitleSize = \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\";\n\nconst titleSizeClass: Record<EmptyStateTitleSize, string> = {\n xs: \"typography-
|
|
1
|
+
{"version":3,"file":"EmptyState.mjs","sources":["../../../src/components/EmptyState/EmptyState.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Button } from \"../Button/Button\";\n\nexport type EmptyStateVariant = \"default\" | \"centered\";\n\nexport type EmptyStateTitleSize = \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\";\n\nconst titleSizeClass: Record<EmptyStateTitleSize, string> = {\n xs: \"typography-header-heading-xs\",\n sm: \"typography-header-heading-sm\",\n md: \"typography-header-heading-md\",\n lg: \"typography-header-heading-lg\",\n xl: \"typography-header-heading-xl\",\n};\n\nexport type EmptyStateMediaSize = \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\";\n\nconst mediaSizeClass: Record<EmptyStateMediaSize, string> = {\n xs: \"h-[80px]\",\n sm: \"h-[160px]\",\n md: \"h-[200px]\",\n lg: \"h-[280px]\",\n xl: \"h-[360px]\",\n};\n\n/** Slot that can be plain copy (styled by `EmptyState`) or custom markup. */\nexport type EmptyStateSlot = string | React.ReactNode;\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\nfunction hasSlotContent(value: EmptyStateSlot | undefined): boolean {\n if (value === undefined || value === null || value === false) {\n return false;\n }\n if (typeof value === \"string\") {\n return value.length > 0;\n }\n return true;\n}\n\nexport interface EmptyStateProps extends Omit<React.HTMLAttributes<HTMLElement>, \"title\"> {\n /**\n * Matches Figma property `Property 1` (`Default` / `centered`).\n * @default \"default\"\n */\n variant?: EmptyStateVariant;\n /** Main heading. Strings use library heading styles; pass a node for full control. */\n title?: EmptyStateSlot;\n /**\n * Size of the title heading — mapped internally to bold heading typography tokens.\n * @default \"lg\"\n */\n titleSize?: EmptyStateTitleSize;\n /** Supporting body copy. Strings use library body styles; pass a node for rich text. */\n description?: EmptyStateSlot;\n /**\n * Top visual / illustration slot.\n * A string is treated as an image URL (`<img src={…}>`); pass a node for custom layout.\n */\n media?: EmptyStateSlot;\n /**\n * Height of the media container.\n * @default \"lg\"\n */\n mediaSize?: EmptyStateMediaSize;\n /**\n * Primary call to action.\n * A string renders a brand `Button` with that label; pass a node for links, loading, etc.\n */\n primaryAction?: EmptyStateSlot;\n /**\n * Secondary action below the primary.\n * A string renders a secondary `Button` with that label; pass a node when you need more control.\n */\n secondaryAction?: EmptyStateSlot;\n}\n\nexport const EmptyState = React.forwardRef<HTMLElement, EmptyStateProps>(\n (\n {\n className,\n variant = \"default\",\n title,\n titleSize = \"lg\",\n description,\n media,\n mediaSize = \"lg\",\n primaryAction,\n secondaryAction,\n ...props\n },\n ref,\n ) => {\n const isCentered = variant === \"centered\";\n const titleId = React.useId();\n const regionLabelledBy = hasSlotContent(title) ? titleId : undefined;\n\n const renderedPrimary =\n primaryAction === undefined ||\n primaryAction === null ||\n primaryAction === false ||\n primaryAction === \"\" ? null : isNonEmptyString(primaryAction) ? (\n <Button variant=\"brand\" fullWidth>\n {primaryAction}\n </Button>\n ) : (\n primaryAction\n );\n\n const renderedSecondary =\n secondaryAction === undefined ||\n secondaryAction === null ||\n secondaryAction === false ||\n secondaryAction === \"\" ? null : isNonEmptyString(secondaryAction) ? (\n <Button variant=\"secondary\" fullWidth>\n {secondaryAction}\n </Button>\n ) : (\n secondaryAction\n );\n\n const renderedTitle =\n title === undefined ||\n title === null ||\n title === false ||\n title === \"\" ? null : isNonEmptyString(title) ? (\n <h2 id={titleId} className={cn(\"m-0 text-content-primary\", titleSizeClass[titleSize])}>\n {title}\n </h2>\n ) : (\n <div\n id={titleId}\n className={cn(\"text-content-primary min-w-0 w-full\", titleSizeClass[titleSize])}\n >\n {title}\n </div>\n );\n\n const renderedDescription =\n description === undefined ||\n description === null ||\n description === false ||\n description === \"\" ? null : isNonEmptyString(description) ? (\n <p className=\"m-0 typography-body-default-16px-regular text-content-secondary\">\n {description}\n </p>\n ) : (\n <div className=\"typography-body-default-16px-regular text-content-secondary min-w-0 w-full\">\n {description}\n </div>\n );\n\n const renderedMedia =\n media === undefined ||\n media === null ||\n media === false ||\n media === \"\" ? null : isNonEmptyString(media) ? (\n <img\n src={media}\n alt=\"\"\n decoding=\"async\"\n className=\"block h-full w-full object-contain object-center\"\n />\n ) : (\n media\n );\n\n const showMedia = renderedMedia !== null;\n const showActions = renderedPrimary !== null || renderedSecondary !== null;\n\n return (\n <section\n ref={ref}\n aria-labelledby={regionLabelledBy}\n data-testid=\"empty-state\"\n className={cn(\n \"flex w-full max-w-[375px] flex-col gap-6\",\n isCentered ? \"items-center text-center\" : \"items-start text-left\",\n className,\n )}\n {...props}\n >\n {showMedia && (\n <div className={cn(\"w-full overflow-hidden rounded-md\", mediaSizeClass[mediaSize])}>\n {renderedMedia}\n </div>\n )}\n\n <div\n className={cn(\"flex w-full flex-col gap-6\", isCentered ? \"items-center\" : \"items-start\")}\n >\n <div\n className={cn(\n \"flex w-full flex-col gap-4\",\n isCentered ? \"items-center\" : \"items-start\",\n )}\n >\n {renderedTitle}\n {renderedDescription}\n </div>\n\n {showActions ? (\n <div className={cn(\"flex flex-col gap-4\", isCentered ? \"items-center\" : \"items-start\")}>\n {renderedPrimary}\n {renderedSecondary}\n </div>\n ) : null}\n </div>\n </section>\n );\n },\n);\n\nEmptyState.displayName = \"EmptyState\";\n"],"names":[],"mappings":"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"}
|
|
@@ -4,16 +4,16 @@ import * as React from "react";
|
|
|
4
4
|
import { cn } from "../../utils/cn.mjs";
|
|
5
5
|
import { Count } from "../Count/Count.mjs";
|
|
6
6
|
const iconButtonVariants = {
|
|
7
|
-
primary: "bg-buttons-primary text-content-primary-inverted hover:bg-buttons-primary-hover not-disabled:active:bg-buttons-primary-hover disabled:opacity-50 focus-visible:shadow-focus-ring",
|
|
7
|
+
primary: "bg-buttons-primary-default text-content-primary-inverted hover:bg-buttons-primary-hover not-disabled:active:bg-buttons-primary-hover disabled:opacity-50 focus-visible:shadow-focus-ring",
|
|
8
8
|
secondary: "bg-neutral-alphas-50 text-icons-primary hover:bg-brand-primary-muted not-disabled:active:bg-brand-primary-muted disabled:opacity-50 focus-visible:shadow-focus-ring",
|
|
9
9
|
tertiary: "bg-transparent text-content-primary hover:bg-brand-primary-muted not-disabled:active:bg-brand-primary-muted disabled:opacity-50 focus-visible:shadow-focus-ring",
|
|
10
|
-
brand: "bg-content-
|
|
11
|
-
contrast: "bg-transparent text-content-
|
|
12
|
-
messaging: "bg-content-
|
|
10
|
+
brand: "bg-content-always-black text-brand-primary-default hover:bg-brand-primary-default hover:text-content-always-black not-disabled:active:bg-brand-primary-default not-disabled:active:text-content-always-black disabled:opacity-50 focus-visible:shadow-focus-ring",
|
|
11
|
+
contrast: "bg-transparent text-content-always-white hover:bg-brand-primary-muted not-disabled:active:bg-brand-primary-muted disabled:opacity-50 focus-visible:shadow-focus-ring",
|
|
12
|
+
messaging: "bg-content-always-black text-brand-primary-default hover:bg-brand-primary-default hover:text-content-always-black not-disabled:active:bg-brand-primary-default not-disabled:active:text-content-always-black disabled:opacity-50 focus-visible:shadow-focus-ring",
|
|
13
13
|
navTray: "bg-transparent text-content-primary hover:bg-brand-primary-muted not-disabled:active:bg-brand-primary-muted disabled:opacity-50 focus-visible:shadow-focus-ring",
|
|
14
14
|
tertiaryDestructive: "bg-transparent text-error-content hover:bg-brand-primary-muted not-disabled:active:bg-brand-primary-muted disabled:opacity-50 focus-visible:shadow-focus-ring",
|
|
15
|
-
stop: "bg-buttons-primary text-content-primary-inverted hover:bg-buttons-brand hover:text-content-
|
|
16
|
-
microphone: "bg-buttons-primary text-content-primary-inverted hover:bg-buttons-brand hover:text-content-
|
|
15
|
+
stop: "bg-buttons-primary-default text-content-primary-inverted hover:bg-buttons-brand-default hover:text-content-always-black not-disabled:active:bg-buttons-brand-default not-disabled:active:text-content-always-black disabled:opacity-50 focus-visible:shadow-focus-ring",
|
|
16
|
+
microphone: "bg-buttons-primary-default text-content-primary-inverted hover:bg-buttons-brand-default hover:text-content-always-black not-disabled:active:bg-buttons-brand-default not-disabled:active:text-content-always-black disabled:opacity-50 focus-visible:shadow-focus-ring"
|
|
17
17
|
};
|
|
18
18
|
const iconSizeVariants = {
|
|
19
19
|
24: "[&>svg]:size-4",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IconButton.mjs","sources":["../../../src/components/IconButton/IconButton.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Count, type CountSize } from \"../Count/Count\";\n\nconst iconButtonVariants = {\n primary:\n \"bg-buttons-primary text-content-primary-inverted hover:bg-buttons-primary-hover not-disabled:active:bg-buttons-primary-hover disabled:opacity-50 focus-visible:shadow-focus-ring\",\n secondary:\n \"bg-neutral-alphas-50 text-icons-primary hover:bg-brand-primary-muted not-disabled:active:bg-brand-primary-muted disabled:opacity-50 focus-visible:shadow-focus-ring\",\n tertiary:\n \"bg-transparent text-content-primary hover:bg-brand-primary-muted not-disabled:active:bg-brand-primary-muted disabled:opacity-50 focus-visible:shadow-focus-ring\",\n brand:\n \"bg-content-
|
|
1
|
+
{"version":3,"file":"IconButton.mjs","sources":["../../../src/components/IconButton/IconButton.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Count, type CountSize } from \"../Count/Count\";\n\nconst iconButtonVariants = {\n primary:\n \"bg-buttons-primary-default text-content-primary-inverted hover:bg-buttons-primary-hover not-disabled:active:bg-buttons-primary-hover disabled:opacity-50 focus-visible:shadow-focus-ring\",\n secondary:\n \"bg-neutral-alphas-50 text-icons-primary hover:bg-brand-primary-muted not-disabled:active:bg-brand-primary-muted disabled:opacity-50 focus-visible:shadow-focus-ring\",\n tertiary:\n \"bg-transparent text-content-primary hover:bg-brand-primary-muted not-disabled:active:bg-brand-primary-muted disabled:opacity-50 focus-visible:shadow-focus-ring\",\n brand:\n \"bg-content-always-black text-brand-primary-default hover:bg-brand-primary-default hover:text-content-always-black not-disabled:active:bg-brand-primary-default not-disabled:active:text-content-always-black disabled:opacity-50 focus-visible:shadow-focus-ring\",\n contrast:\n \"bg-transparent text-content-always-white hover:bg-brand-primary-muted not-disabled:active:bg-brand-primary-muted disabled:opacity-50 focus-visible:shadow-focus-ring\",\n messaging:\n \"bg-content-always-black text-brand-primary-default hover:bg-brand-primary-default hover:text-content-always-black not-disabled:active:bg-brand-primary-default not-disabled:active:text-content-always-black disabled:opacity-50 focus-visible:shadow-focus-ring\",\n navTray:\n \"bg-transparent text-content-primary hover:bg-brand-primary-muted not-disabled:active:bg-brand-primary-muted disabled:opacity-50 focus-visible:shadow-focus-ring\",\n tertiaryDestructive:\n \"bg-transparent text-error-content hover:bg-brand-primary-muted not-disabled:active:bg-brand-primary-muted disabled:opacity-50 focus-visible:shadow-focus-ring\",\n stop: \"bg-buttons-primary-default text-content-primary-inverted hover:bg-buttons-brand-default hover:text-content-always-black not-disabled:active:bg-buttons-brand-default not-disabled:active:text-content-always-black disabled:opacity-50 focus-visible:shadow-focus-ring\",\n microphone:\n \"bg-buttons-primary-default text-content-primary-inverted hover:bg-buttons-brand-default hover:text-content-always-black not-disabled:active:bg-buttons-brand-default not-disabled:active:text-content-always-black disabled:opacity-50 focus-visible:shadow-focus-ring\",\n};\n\nconst iconSizeVariants = {\n 24: \"[&>svg]:size-4\",\n 32: \"[&>svg]:size-5\",\n 40: \"[&>svg]:size-6\",\n 52: \"[&>svg]:size-7\",\n 72: \"[&>svg]:size-8\",\n} as const;\n\nconst sizeVariants = {\n 24: \"size-6 p-1\",\n 32: \"size-8 p-1.5\",\n 40: \"size-10 p-[10px]\",\n 52: \"size-[52px] p-2\",\n 72: \"size-[72px] p-4\",\n} as const;\n\nconst countSizeMap: Record<string, CountSize> = {\n 24: \"16\",\n 32: \"24\",\n 40: \"32\",\n 52: \"32\",\n 72: \"32\",\n};\n\n/** Visual style variant of the icon button. */\nexport type IconButtonVariant =\n | \"primary\"\n | \"secondary\"\n | \"tertiary\"\n | \"brand\"\n | \"contrast\"\n | \"messaging\"\n | \"navTray\"\n | \"tertiaryDestructive\"\n | \"stop\"\n | \"microphone\";\n\n/** Icon button size in pixels. */\nexport type IconButtonSize = \"24\" | \"32\" | \"40\" | \"52\" | \"72\";\n\nexport interface IconButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Visual style variant of the icon button. @default \"primary\" */\n variant?: IconButtonVariant;\n /** Size of the button in pixels. @default \"40\" */\n size?: IconButtonSize;\n /** Icon element to render inside the button. */\n icon: React.ReactNode;\n /** When provided, displays a {@link Count} badge at the top-right corner (tertiary & navTray variants only). */\n counterValue?: number;\n}\n\n/**\n * A circular button containing only an icon. Use when an action can be\n * represented by an icon alone (e.g. close, send, mic). Pair with an\n * `aria-label` for accessibility.\n *\n * @example\n * ```tsx\n * <IconButton icon={<CloseIcon />} aria-label=\"Close\" variant=\"tertiary\" />\n * ```\n */\nexport const IconButton = React.forwardRef<HTMLButtonElement, IconButtonProps>(\n (\n { className, variant = \"primary\", size = \"40\", icon, counterValue, disabled = false, ...props },\n ref,\n ) => {\n if (process.env.NODE_ENV !== \"production\") {\n if (!props[\"aria-label\"] && !props[\"aria-labelledby\"] && !props.title) {\n console.warn(\n \"IconButton: No accessible name provided. Add an `aria-label`, `aria-labelledby`, or `title` prop so screen readers can announce this button.\",\n );\n }\n }\n\n return (\n <button\n ref={ref}\n type=\"button\"\n data-testid=\"icon-button\"\n disabled={disabled}\n className={cn(\n // Base styles\n \"relative inline-flex shrink-0 items-center justify-center focus-visible:outline-none\",\n \"cursor-pointer rounded-full transition-all duration-150 ease-in-out disabled:cursor-default\",\n // Size variants\n sizeVariants[size],\n // Variant styles\n iconButtonVariants[variant],\n // Manual CSS overrides\n className,\n )}\n {...props}\n >\n <span\n className={cn(\"flex shrink-0 items-center justify-center\", iconSizeVariants[size])}\n aria-hidden=\"true\"\n >\n {icon}\n </span>\n\n {counterValue !== undefined && (\n <Count\n value={counterValue}\n variant=\"alert\"\n size={countSizeMap[size]}\n className=\"absolute -top-0.5 -right-0.5\"\n />\n )}\n </button>\n );\n },\n);\n\nIconButton.displayName = \"IconButton\";\n"],"names":[],"mappings":";;;;;AAIA,MAAM,qBAAqB;AAAA,EACzB,SACE;AAAA,EACF,WACE;AAAA,EACF,UACE;AAAA,EACF,OACE;AAAA,EACF,UACE;AAAA,EACF,WACE;AAAA,EACF,SACE;AAAA,EACF,qBACE;AAAA,EACF,MAAM;AAAA,EACN,YACE;AACJ;AAEA,MAAM,mBAAmB;AAAA,EACvB,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAEA,MAAM,eAAe;AAAA,EACnB,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAEA,MAAM,eAA0C;AAAA,EAC9C,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAuCO,MAAM,aAAa,MAAM;AAAA,EAC9B,CACE,EAAE,WAAW,UAAU,WAAW,OAAO,MAAM,MAAM,cAAc,WAAW,OAAO,GAAG,MAAA,GACxF,QACG;AACH,QAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,UAAI,CAAC,MAAM,YAAY,KAAK,CAAC,MAAM,iBAAiB,KAAK,CAAC,MAAM,OAAO;AACrE,gBAAQ;AAAA,UACN;AAAA,QAAA;AAAA,MAEJ;AAAA,IACF;AAEA,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL,eAAY;AAAA,QACZ;AAAA,QACA,WAAW;AAAA;AAAA,UAET;AAAA,UACA;AAAA;AAAA,UAEA,aAAa,IAAI;AAAA;AAAA,UAEjB,mBAAmB,OAAO;AAAA;AAAA,UAE1B;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEJ,UAAA;AAAA,UAAA;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAW,GAAG,6CAA6C,iBAAiB,IAAI,CAAC;AAAA,cACjF,eAAY;AAAA,cAEX,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,UAGF,iBAAiB,UAChB;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,OAAO;AAAA,cACP,SAAQ;AAAA,cACR,MAAM,aAAa,IAAI;AAAA,cACvB,WAAU;AAAA,YAAA;AAAA,UAAA;AAAA,QACZ;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEA,WAAW,cAAc;"}
|
|
@@ -10,7 +10,7 @@ const InfoBox = PopoverPrimitive.Root;
|
|
|
10
10
|
const InfoBoxTrigger = React.forwardRef((props, ref) => /* @__PURE__ */ jsx(PopoverPrimitive.Trigger, { ref, ...useSuppressClickAfterDrag(props) }));
|
|
11
11
|
InfoBoxTrigger.displayName = "InfoBoxTrigger";
|
|
12
12
|
const ACTION_CLASSES = {
|
|
13
|
-
brand: "hover:bg-brand-primary-default/80 hover:text-content-
|
|
13
|
+
brand: "hover:bg-brand-primary-default/80 hover:text-content-always-black",
|
|
14
14
|
tertiary: "text-content-primary-inverted hover:text-content-primary-inverted hover:bg-content-primary-inverted/10"
|
|
15
15
|
};
|
|
16
16
|
const ActionButton = ({
|
|
@@ -44,7 +44,7 @@ const InfoBoxContent = React.forwardRef(
|
|
|
44
44
|
collisionPadding,
|
|
45
45
|
style: { zIndex: "var(--fanvue-ui-portal-z-index, 50)", ...style },
|
|
46
46
|
className: cn(
|
|
47
|
-
"typography-
|
|
47
|
+
"typography-body-small-14px-regular max-w-[280px] overflow-hidden rounded-md border border-white/20 bg-surface-primary-inverted p-4 text-content-primary-inverted shadow-[0px_2px_4px_0px_rgba(17,24,39,0.08)]",
|
|
48
48
|
className
|
|
49
49
|
),
|
|
50
50
|
align: "center",
|
|
@@ -63,13 +63,13 @@ const InfoBoxContent = React.forwardRef(
|
|
|
63
63
|
"p",
|
|
64
64
|
{
|
|
65
65
|
id: headingId,
|
|
66
|
-
className: "typography-
|
|
66
|
+
className: "typography-body-default-16px-semibold min-w-0 flex-1 text-content-primary-inverted",
|
|
67
67
|
children: heading
|
|
68
68
|
}
|
|
69
69
|
),
|
|
70
70
|
pill && /* @__PURE__ */ jsx("div", { className: "shrink-0", children: pill })
|
|
71
71
|
] }),
|
|
72
|
-
children && /* @__PURE__ */ jsx("div", { className: "typography-
|
|
72
|
+
children && /* @__PURE__ */ jsx("div", { className: "typography-body-small-14px-regular text-content-primary-inverted", children }),
|
|
73
73
|
hasActions && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
74
74
|
primaryAction && /* @__PURE__ */ jsx(ActionButton, { action: primaryAction, variant: "brand" }),
|
|
75
75
|
secondaryAction && /* @__PURE__ */ jsx(ActionButton, { action: secondaryAction, variant: "tertiary" })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InfoBox.mjs","sources":["../../../src/components/InfoBox/InfoBox.tsx"],"sourcesContent":["import * as PopoverPrimitive from \"@radix-ui/react-popover\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { FLOATING_CONTENT_COLLISION_PADDING } from \"../../utils/floatingContentCollisionPadding\";\nimport { useSuppressClickAfterDrag } from \"../../utils/useSuppressClickAfterDrag\";\nimport { Button } from \"../Button/Button\";\n\n/** Props for the {@link InfoBox} root component. */\nexport interface InfoBoxProps extends React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Root> {\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n defaultOpen?: boolean;\n}\n\n/** Root component that manages open/close state for an info box. */\nexport const InfoBox = PopoverPrimitive.Root;\n\n/** Props for the {@link InfoBoxTrigger} component. */\nexport type InfoBoxTriggerProps = React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Trigger>;\n\n/**\n * The element that triggers the info box on click.\n *\n * On touch / pen, a press-and-release that crosses a small movement threshold\n * is treated as a drag and the resulting synthetic click is suppressed —\n * defends against Android Chrome opening the popover on a scroll-drag-end.\n */\nexport const InfoBoxTrigger = React.forwardRef<\n React.ComponentRef<typeof PopoverPrimitive.Trigger>,\n InfoBoxTriggerProps\n>((props, ref) => <PopoverPrimitive.Trigger ref={ref} {...useSuppressClickAfterDrag(props)} />);\nInfoBoxTrigger.displayName = \"InfoBoxTrigger\";\n\n/** Action button with a label and click handler. */\ninterface InfoBoxButtonAction {\n label: string;\n onClick?: () => void;\n}\n\n/** Custom element rendered in place of the default action button. */\ninterface InfoBoxElementAction {\n element: React.ReactNode;\n}\n\n/** Action configuration for {@link InfoBoxContent}. */\nexport type InfoBoxAction = InfoBoxButtonAction | InfoBoxElementAction;\n\nexport interface InfoBoxContentProps\n extends React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> {\n /** Whether to show the directional arrow pointer. @default true */\n showArrow?: boolean;\n /** Heading text rendered at the top of the info box. */\n heading?: React.ReactNode;\n /** Icon element displayed to the left of the heading. */\n icon?: React.ReactNode;\n /** Pill or badge element displayed to the right of the heading. */\n pill?: React.ReactNode;\n /** Primary action button (brand green). */\n primaryAction?: InfoBoxAction;\n /** Secondary action button (ghost). */\n secondaryAction?: InfoBoxAction;\n}\n\nconst ACTION_CLASSES: Record<\"brand\" | \"tertiary\", string> = {\n brand: \"hover:bg-brand-primary-default/80 hover:text-content-
|
|
1
|
+
{"version":3,"file":"InfoBox.mjs","sources":["../../../src/components/InfoBox/InfoBox.tsx"],"sourcesContent":["import * as PopoverPrimitive from \"@radix-ui/react-popover\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { FLOATING_CONTENT_COLLISION_PADDING } from \"../../utils/floatingContentCollisionPadding\";\nimport { useSuppressClickAfterDrag } from \"../../utils/useSuppressClickAfterDrag\";\nimport { Button } from \"../Button/Button\";\n\n/** Props for the {@link InfoBox} root component. */\nexport interface InfoBoxProps extends React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Root> {\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n defaultOpen?: boolean;\n}\n\n/** Root component that manages open/close state for an info box. */\nexport const InfoBox = PopoverPrimitive.Root;\n\n/** Props for the {@link InfoBoxTrigger} component. */\nexport type InfoBoxTriggerProps = React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Trigger>;\n\n/**\n * The element that triggers the info box on click.\n *\n * On touch / pen, a press-and-release that crosses a small movement threshold\n * is treated as a drag and the resulting synthetic click is suppressed —\n * defends against Android Chrome opening the popover on a scroll-drag-end.\n */\nexport const InfoBoxTrigger = React.forwardRef<\n React.ComponentRef<typeof PopoverPrimitive.Trigger>,\n InfoBoxTriggerProps\n>((props, ref) => <PopoverPrimitive.Trigger ref={ref} {...useSuppressClickAfterDrag(props)} />);\nInfoBoxTrigger.displayName = \"InfoBoxTrigger\";\n\n/** Action button with a label and click handler. */\ninterface InfoBoxButtonAction {\n label: string;\n onClick?: () => void;\n}\n\n/** Custom element rendered in place of the default action button. */\ninterface InfoBoxElementAction {\n element: React.ReactNode;\n}\n\n/** Action configuration for {@link InfoBoxContent}. */\nexport type InfoBoxAction = InfoBoxButtonAction | InfoBoxElementAction;\n\nexport interface InfoBoxContentProps\n extends React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> {\n /** Whether to show the directional arrow pointer. @default true */\n showArrow?: boolean;\n /** Heading text rendered at the top of the info box. */\n heading?: React.ReactNode;\n /** Icon element displayed to the left of the heading. */\n icon?: React.ReactNode;\n /** Pill or badge element displayed to the right of the heading. */\n pill?: React.ReactNode;\n /** Primary action button (brand green). */\n primaryAction?: InfoBoxAction;\n /** Secondary action button (ghost). */\n secondaryAction?: InfoBoxAction;\n}\n\nconst ACTION_CLASSES: Record<\"brand\" | \"tertiary\", string> = {\n brand: \"hover:bg-brand-primary-default/80 hover:text-content-always-black\",\n tertiary:\n \"text-content-primary-inverted hover:text-content-primary-inverted hover:bg-content-primary-inverted/10\",\n};\n\nconst ActionButton = ({\n action,\n variant,\n}: {\n action: InfoBoxAction;\n variant: \"brand\" | \"tertiary\";\n}) =>\n \"element\" in action ? (\n action.element\n ) : (\n <Button variant={variant} onClick={action.onClick} className={ACTION_CLASSES[variant]}>\n {action.label}\n </Button>\n );\n\nexport const InfoBoxContent = React.forwardRef<\n React.ComponentRef<typeof PopoverPrimitive.Content>,\n InfoBoxContentProps\n>(\n (\n {\n className,\n showArrow = true,\n sideOffset = 8,\n heading,\n icon,\n pill,\n primaryAction,\n secondaryAction,\n children,\n style,\n onOpenAutoFocus,\n collisionPadding = FLOATING_CONTENT_COLLISION_PADDING,\n ...props\n },\n ref,\n ) => {\n const hasHeader = icon !== undefined || heading !== undefined || pill !== undefined;\n const hasActions = primaryAction !== undefined || secondaryAction !== undefined;\n const headingId = React.useId();\n\n return (\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n collisionPadding={collisionPadding}\n style={{ zIndex: \"var(--fanvue-ui-portal-z-index, 50)\", ...style }}\n className={cn(\n \"typography-body-small-14px-regular max-w-[280px] overflow-hidden rounded-md border border-white/20 bg-surface-primary-inverted p-4 text-content-primary-inverted shadow-[0px_2px_4px_0px_rgba(17,24,39,0.08)]\",\n className,\n )}\n align=\"center\"\n aria-labelledby={heading ? headingId : undefined}\n arrowPadding={12}\n onOpenAutoFocus={(e) => {\n // Prevent auto-focus stealing when opening — content is supplementary, not a dialog.\n e.preventDefault();\n onOpenAutoFocus?.(e);\n }}\n {...props}\n >\n <div className=\"flex flex-col gap-3\">\n {hasHeader && (\n <div className=\"flex items-center gap-3\">\n {icon && <div className=\"size-5 shrink-0\">{icon}</div>}\n {heading && (\n <p\n id={headingId}\n className=\"typography-body-default-16px-semibold min-w-0 flex-1 text-content-primary-inverted\"\n >\n {heading}\n </p>\n )}\n {pill && <div className=\"shrink-0\">{pill}</div>}\n </div>\n )}\n {children && (\n <div className=\"typography-body-small-14px-regular text-content-primary-inverted\">\n {children}\n </div>\n )}\n {hasActions && (\n <div className=\"flex items-center gap-1\">\n {primaryAction && <ActionButton action={primaryAction} variant=\"brand\" />}\n {secondaryAction && <ActionButton action={secondaryAction} variant=\"tertiary\" />}\n </div>\n )}\n </div>\n {showArrow && (\n <PopoverPrimitive.Arrow\n className={\n \"-translate-y-px! fill-surface-primary-inverted stroke-2 stroke-surface-primary-inverted\"\n }\n width={12}\n height={6}\n />\n )}\n </PopoverPrimitive.Content>\n </PopoverPrimitive.Portal>\n );\n },\n);\nInfoBoxContent.displayName = \"InfoBoxContent\";\n"],"names":[],"mappings":";;;;;;;;AAeO,MAAM,UAAU,iBAAiB;AAYjC,MAAM,iBAAiB,MAAM,WAGlC,CAAC,OAAO,QAAQ,oBAAC,iBAAiB,SAAjB,EAAyB,KAAW,GAAG,0BAA0B,KAAK,GAAG,CAAE;AAC9F,eAAe,cAAc;AAgC7B,MAAM,iBAAuD;AAAA,EAC3D,OAAO;AAAA,EACP,UACE;AACJ;AAEA,MAAM,eAAe,CAAC;AAAA,EACpB;AAAA,EACA;AACF,MAIE,aAAa,SACX,OAAO,8BAEN,QAAA,EAAO,SAAkB,SAAS,OAAO,SAAS,WAAW,eAAe,OAAO,GACjF,iBAAO,OACV;AAGG,MAAM,iBAAiB,MAAM;AAAA,EAIlC,CACE;AAAA,IACE;AAAA,IACA,YAAY;AAAA,IACZ,aAAa;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,mBAAmB;AAAA,IACnB,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,YAAY,SAAS,UAAa,YAAY,UAAa,SAAS;AAC1E,UAAM,aAAa,kBAAkB,UAAa,oBAAoB;AACtE,UAAM,YAAY,MAAM,MAAA;AAExB,WACE,oBAAC,iBAAiB,QAAjB,EACC,UAAA;AAAA,MAAC,iBAAiB;AAAA,MAAjB;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA,OAAO,EAAE,QAAQ,uCAAuC,GAAG,MAAA;AAAA,QAC3D,WAAW;AAAA,UACT;AAAA,UACA;AAAA,QAAA;AAAA,QAEF,OAAM;AAAA,QACN,mBAAiB,UAAU,YAAY;AAAA,QACvC,cAAc;AAAA,QACd,iBAAiB,CAAC,MAAM;AAEtB,YAAE,eAAA;AACF,4BAAkB,CAAC;AAAA,QACrB;AAAA,QACC,GAAG;AAAA,QAEJ,UAAA;AAAA,UAAA,qBAAC,OAAA,EAAI,WAAU,uBACZ,UAAA;AAAA,YAAA,aACC,qBAAC,OAAA,EAAI,WAAU,2BACZ,UAAA;AAAA,cAAA,QAAQ,oBAAC,OAAA,EAAI,WAAU,mBAAmB,UAAA,MAAK;AAAA,cAC/C,WACC;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,IAAI;AAAA,kBACJ,WAAU;AAAA,kBAET,UAAA;AAAA,gBAAA;AAAA,cAAA;AAAA,cAGJ,QAAQ,oBAAC,OAAA,EAAI,WAAU,YAAY,UAAA,KAAA,CAAK;AAAA,YAAA,GAC3C;AAAA,YAED,YACC,oBAAC,OAAA,EAAI,WAAU,oEACZ,UACH;AAAA,YAED,cACC,qBAAC,OAAA,EAAI,WAAU,2BACZ,UAAA;AAAA,cAAA,iBAAiB,oBAAC,cAAA,EAAa,QAAQ,eAAe,SAAQ,SAAQ;AAAA,cACtE,mBAAmB,oBAAC,cAAA,EAAa,QAAQ,iBAAiB,SAAQ,WAAA,CAAW;AAAA,YAAA,EAAA,CAChF;AAAA,UAAA,GAEJ;AAAA,UACC,aACC;AAAA,YAAC,iBAAiB;AAAA,YAAjB;AAAA,cACC,WACE;AAAA,cAEF,OAAO;AAAA,cACP,QAAQ;AAAA,YAAA;AAAA,UAAA;AAAA,QACV;AAAA,MAAA;AAAA,IAAA,GAGN;AAAA,EAEJ;AACF;AACA,eAAe,cAAc;"}
|
|
@@ -101,7 +101,7 @@ const InlineEdit = React.forwardRef(
|
|
|
101
101
|
{
|
|
102
102
|
"aria-hidden": "true",
|
|
103
103
|
className: cn(
|
|
104
|
-
"typography-
|
|
104
|
+
"typography-description-12px-semibold invisible block whitespace-pre border border-transparent px-3",
|
|
105
105
|
size === "32" ? "h-8" : "h-10",
|
|
106
106
|
showLeftIcon && "pl-9"
|
|
107
107
|
),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InlineEdit.mjs","sources":["../../../src/components/InlineEdit/InlineEdit.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Chip } from \"../Chip/Chip\";\n\n/** Height of the inline edit field in pixels. */\nexport type InlineEditSize = \"32\" | \"40\";\n\nexport interface InlineEditProps\n extends Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n \"value\" | \"defaultValue\" | \"size\" | \"onSubmit\"\n > {\n /** Current value displayed in the chip and used as the starting draft when editing. */\n value: string;\n /** Called with the trimmed draft when the user commits an edit (Enter or blur). */\n onSubmit: (value: string) => void;\n /** Called when the user cancels an edit with Escape. */\n onCancel?: () => void;\n /** Height of the field in pixels. @default \"40\" */\n size?: InlineEditSize;\n /** Whether the field is disabled — prevents entering edit mode. @default false */\n disabled?: boolean;\n /** Accessible label for the edit input. @default \"Edit\" */\n editLabel?: string;\n /** Icon rendered before the value in display mode. Hidden when editing. */\n leftIcon?: React.ReactNode;\n /**\n * Validator for the trimmed draft on commit. Returning `false` reverts to\n * the previous value without calling `onSubmit`. @default rejects empty drafts\n */\n validate?: (draft: string) => boolean;\n /** Additional class name applied to the root element. */\n className?: string;\n}\n\n/**\n * A chip-styled inline edit field. Renders as a dashed-border button that\n * swaps to a text input on click, allowing the value to be edited in place.\n *\n * Enter and blur commit the draft via `onSubmit`. Escape reverts to `value`\n * and calls `onCancel`. Drafts that fail `validate` are reverted.\n *\n * The forwarded ref points at the underlying `<input>` and is only populated\n * while the field is in edit mode — it resolves to `null` in display mode.\n *\n * Consumers may pass `onChange`, `onBlur`, and `onKeyDown` to participate in\n * input events. The component's own handlers run after the consumer's, and\n * keyboard handling is skipped if the consumer calls `event.preventDefault()`.\n *\n * @example\n * ```tsx\n * const [name, setName] = useState(\"New folder\");\n * <InlineEdit value={name} onSubmit={setName} />\n * ```\n */\nexport const InlineEdit = React.forwardRef<HTMLInputElement, InlineEditProps>(\n (\n {\n value,\n onSubmit,\n onCancel,\n size = \"40\",\n disabled = false,\n editLabel = \"Edit\",\n leftIcon,\n validate,\n className,\n placeholder,\n onChange,\n onBlur,\n onKeyDown,\n ...inputProps\n },\n ref,\n ) => {\n const [isEditing, setIsEditing] = React.useState(false);\n const [draft, setDraft] = React.useState(value);\n const inputRef = React.useRef<HTMLInputElement | null>(null);\n\n const setInputRef = React.useCallback(\n (node: HTMLInputElement | null) => {\n inputRef.current = node;\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref) {\n (ref as React.RefObject<HTMLInputElement | null>).current = node;\n }\n },\n [ref],\n );\n\n React.useEffect(() => {\n if (!isEditing) {\n setDraft(value);\n }\n }, [value, isEditing]);\n\n React.useEffect(() => {\n if (!isEditing) return;\n const input = inputRef.current;\n if (!input) return;\n input.focus();\n input.select();\n }, [isEditing]);\n\n const enterEditMode = () => {\n if (disabled) return;\n setDraft(value);\n setIsEditing(true);\n };\n\n const commit = () => {\n const trimmed = draft.trim();\n const isValid = validate ? validate(trimmed) : trimmed.length > 0;\n if (!isValid) {\n setDraft(value);\n setIsEditing(false);\n return;\n }\n if (trimmed !== value) {\n onSubmit(trimmed);\n }\n setIsEditing(false);\n };\n\n const cancel = () => {\n setDraft(value);\n setIsEditing(false);\n onCancel?.();\n };\n\n const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n onChange?.(event);\n setDraft(event.target.value);\n };\n\n const handleBlur = (event: React.FocusEvent<HTMLInputElement>) => {\n onBlur?.(event);\n commit();\n };\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {\n onKeyDown?.(event);\n if (event.defaultPrevented) return;\n if (event.key === \"Enter\") {\n event.preventDefault();\n commit();\n } else if (event.key === \"Escape\") {\n event.preventDefault();\n cancel();\n }\n };\n\n const showLeftIcon = Boolean(leftIcon) && !isEditing;\n const sizerText = (isEditing ? draft : value) || placeholder || \" \";\n\n return (\n <span\n data-testid=\"inline-edit\"\n className={cn(\"relative inline-block align-middle\", className)}\n >\n <span\n aria-hidden=\"true\"\n className={cn(\n \"typography-
|
|
1
|
+
{"version":3,"file":"InlineEdit.mjs","sources":["../../../src/components/InlineEdit/InlineEdit.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Chip } from \"../Chip/Chip\";\n\n/** Height of the inline edit field in pixels. */\nexport type InlineEditSize = \"32\" | \"40\";\n\nexport interface InlineEditProps\n extends Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n \"value\" | \"defaultValue\" | \"size\" | \"onSubmit\"\n > {\n /** Current value displayed in the chip and used as the starting draft when editing. */\n value: string;\n /** Called with the trimmed draft when the user commits an edit (Enter or blur). */\n onSubmit: (value: string) => void;\n /** Called when the user cancels an edit with Escape. */\n onCancel?: () => void;\n /** Height of the field in pixels. @default \"40\" */\n size?: InlineEditSize;\n /** Whether the field is disabled — prevents entering edit mode. @default false */\n disabled?: boolean;\n /** Accessible label for the edit input. @default \"Edit\" */\n editLabel?: string;\n /** Icon rendered before the value in display mode. Hidden when editing. */\n leftIcon?: React.ReactNode;\n /**\n * Validator for the trimmed draft on commit. Returning `false` reverts to\n * the previous value without calling `onSubmit`. @default rejects empty drafts\n */\n validate?: (draft: string) => boolean;\n /** Additional class name applied to the root element. */\n className?: string;\n}\n\n/**\n * A chip-styled inline edit field. Renders as a dashed-border button that\n * swaps to a text input on click, allowing the value to be edited in place.\n *\n * Enter and blur commit the draft via `onSubmit`. Escape reverts to `value`\n * and calls `onCancel`. Drafts that fail `validate` are reverted.\n *\n * The forwarded ref points at the underlying `<input>` and is only populated\n * while the field is in edit mode — it resolves to `null` in display mode.\n *\n * Consumers may pass `onChange`, `onBlur`, and `onKeyDown` to participate in\n * input events. The component's own handlers run after the consumer's, and\n * keyboard handling is skipped if the consumer calls `event.preventDefault()`.\n *\n * @example\n * ```tsx\n * const [name, setName] = useState(\"New folder\");\n * <InlineEdit value={name} onSubmit={setName} />\n * ```\n */\nexport const InlineEdit = React.forwardRef<HTMLInputElement, InlineEditProps>(\n (\n {\n value,\n onSubmit,\n onCancel,\n size = \"40\",\n disabled = false,\n editLabel = \"Edit\",\n leftIcon,\n validate,\n className,\n placeholder,\n onChange,\n onBlur,\n onKeyDown,\n ...inputProps\n },\n ref,\n ) => {\n const [isEditing, setIsEditing] = React.useState(false);\n const [draft, setDraft] = React.useState(value);\n const inputRef = React.useRef<HTMLInputElement | null>(null);\n\n const setInputRef = React.useCallback(\n (node: HTMLInputElement | null) => {\n inputRef.current = node;\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref) {\n (ref as React.RefObject<HTMLInputElement | null>).current = node;\n }\n },\n [ref],\n );\n\n React.useEffect(() => {\n if (!isEditing) {\n setDraft(value);\n }\n }, [value, isEditing]);\n\n React.useEffect(() => {\n if (!isEditing) return;\n const input = inputRef.current;\n if (!input) return;\n input.focus();\n input.select();\n }, [isEditing]);\n\n const enterEditMode = () => {\n if (disabled) return;\n setDraft(value);\n setIsEditing(true);\n };\n\n const commit = () => {\n const trimmed = draft.trim();\n const isValid = validate ? validate(trimmed) : trimmed.length > 0;\n if (!isValid) {\n setDraft(value);\n setIsEditing(false);\n return;\n }\n if (trimmed !== value) {\n onSubmit(trimmed);\n }\n setIsEditing(false);\n };\n\n const cancel = () => {\n setDraft(value);\n setIsEditing(false);\n onCancel?.();\n };\n\n const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n onChange?.(event);\n setDraft(event.target.value);\n };\n\n const handleBlur = (event: React.FocusEvent<HTMLInputElement>) => {\n onBlur?.(event);\n commit();\n };\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {\n onKeyDown?.(event);\n if (event.defaultPrevented) return;\n if (event.key === \"Enter\") {\n event.preventDefault();\n commit();\n } else if (event.key === \"Escape\") {\n event.preventDefault();\n cancel();\n }\n };\n\n const showLeftIcon = Boolean(leftIcon) && !isEditing;\n const sizerText = (isEditing ? draft : value) || placeholder || \" \";\n\n return (\n <span\n data-testid=\"inline-edit\"\n className={cn(\"relative inline-block align-middle\", className)}\n >\n <span\n aria-hidden=\"true\"\n className={cn(\n \"typography-description-12px-semibold invisible block whitespace-pre border border-transparent px-3\",\n size === \"32\" ? \"h-8\" : \"h-10\",\n showLeftIcon && \"pl-9\",\n )}\n >\n {sizerText}\n </span>\n {isEditing ? (\n <Chip\n asChild\n dotted\n variant=\"square\"\n size={size}\n className=\"absolute inset-0 block h-auto w-full focus-within:shadow-focus-ring\"\n >\n <span>\n <input\n ref={setInputRef}\n type=\"text\"\n value={draft}\n placeholder={placeholder}\n onChange={handleChange}\n onKeyDown={handleKeyDown}\n onBlur={handleBlur}\n aria-label={editLabel}\n data-testid=\"inline-edit-input\"\n className=\"block h-full w-full bg-transparent px-3 outline-none\"\n {...inputProps}\n />\n </span>\n </Chip>\n ) : (\n <Chip\n dotted\n variant=\"square\"\n size={size}\n disabled={disabled}\n leftIcon={leftIcon}\n onClick={enterEditMode}\n data-testid=\"inline-edit-trigger\"\n className=\"absolute inset-0 h-auto w-full cursor-text\"\n >\n {value}\n </Chip>\n )}\n </span>\n );\n },\n);\n\nInlineEdit.displayName = \"InlineEdit\";\n"],"names":[],"mappings":"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"}
|
|
@@ -23,14 +23,14 @@ const getLogoColors = (color, variant) => {
|
|
|
23
23
|
return {
|
|
24
24
|
icon: variant === "icon" ? "var(--primitives-color-gray-white)" : "var(--color-brand-primary-default)",
|
|
25
25
|
iconInner: "var(--primitives-color-gray-black)",
|
|
26
|
-
textClass: "text-content-
|
|
26
|
+
textClass: "text-content-always-white"
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
29
|
if (color === "blackAlways") {
|
|
30
30
|
return {
|
|
31
31
|
icon: variant === "icon" ? "var(--primitives-color-gray-black)" : "var(--color-brand-primary-default)",
|
|
32
32
|
iconInner: variant === "icon" ? "var(--primitives-color-gray-white)" : "var(--primitives-color-gray-black)",
|
|
33
|
-
textClass: "text-content-
|
|
33
|
+
textClass: "text-content-always-black"
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Logo.mjs","sources":["../../../src/components/Logo/Logo.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\nconst getLogoColors = (color: LogoColor, variant: LogoVariant) => {\n if (color === \"fullColour\") {\n return {\n icon: \"var(--color-brand-primary-default)\",\n iconInner: \"var(--primitives-color-gray-black)\",\n textClass: \"\", // Uses parent's text-content-primary\n };\n }\n\n if (color === \"decolour\") {\n return {\n iconClass: \"fill-[#151515] dark:fill-[#ffffff]\",\n iconInnerClass: \"fill-[#ffffff] dark:fill-[#151515]\",\n textClass: \"\", // Uses parent's text-content-primary\n };\n }\n\n if (color === \"whiteAlways\") {\n return {\n icon:\n variant === \"icon\"\n ? \"var(--primitives-color-gray-white)\"\n : \"var(--color-brand-primary-default)\",\n iconInner: \"var(--primitives-color-gray-black)\",\n textClass: \"text-content-on-brand-inverted\",\n };\n }\n\n if (color === \"blackAlways\") {\n return {\n icon:\n variant === \"icon\"\n ? \"var(--primitives-color-gray-black)\"\n : \"var(--color-brand-primary-default)\",\n iconInner:\n variant === \"icon\"\n ? \"var(--primitives-color-gray-white)\"\n : \"var(--primitives-color-gray-black)\",\n textClass: \"text-content-on-brand\",\n };\n }\n\n return {\n icon: \"var(--color-brand-primary-default)\",\n iconInner: \"var(--primitives-color-gray-black)\",\n textClass: \"\", // Default to adaptive color\n };\n};\n\n/** Layout variant of the logo. */\nexport type LogoVariant = \"full\" | \"icon\" | \"wordmark\" | \"portrait\";\n/** Colour scheme of the logo. */\nexport type LogoColor = \"fullColour\" | \"decolour\" | \"whiteAlways\" | \"blackAlways\";\n/** Height of the logo in pixels. Both icon and wordmark scale proportionally. */\nexport type LogoSize = \"16\" | \"20\" | \"24\" | \"32\" | \"40\" | \"48\" | \"64\";\n\nconst sizeClasses: Record<LogoSize, string> = {\n \"16\": \"h-4\",\n \"20\": \"h-5\",\n \"24\": \"h-6\",\n \"32\": \"h-8\",\n \"40\": \"h-10\",\n \"48\": \"h-12\",\n \"64\": \"h-16\",\n};\n\nexport interface LogoProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Layout variant of the logo. @default \"full\" */\n variant?: LogoVariant;\n /** Colour scheme of the logo. @default \"fullColour\" */\n color?: LogoColor;\n /** Height of the logo in pixels. @default \"32\" (or \"40\" when `variant=\"icon\"`) */\n size?: LogoSize;\n /**\n * Accessible label for the logo. Required when `type` is `\"icon\"` and\n * the logo is used inside interactive contexts (links, buttons).\n *\n * @example \"Fanvue home\"\n */\n \"aria-label\"?: string;\n}\n\nconst WordmarkSVG = ({ className }: { className?: string }) => {\n return (\n <svg\n viewBox=\"0 0 128 30\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n className={className}\n data-testid=\"logo-wordmark\"\n >\n <path\n d=\"M89.0679 20.1823C89.0679 23.4373 90.1256 25.553 93.0961 25.553C95.9847 25.553 98.1815 23.0304 98.1815 17.7818V8.01701H102.902V29.0523H98.2629V25.3495C97.1238 27.75 95.2114 29.6218 91.9566 29.6218C86.464 29.6218 84.3888 26.0006 84.3888 21.1589V8.01701H89.0679V20.1823ZM116.586 7.44485C123.787 7.44485 126.717 12.9782 126.757 18.9592C126.757 19.1627 126.757 19.4883 126.716 19.8544H110.523C110.889 23.5569 113.249 25.8353 116.586 25.8353C118.986 25.8353 121.02 24.8995 121.752 22.8245H126.432C125.211 27.0966 121.59 29.6192 116.586 29.6192C110.279 29.6192 106.007 25.1028 106.007 18.4707C106.007 12.0829 110.483 7.44485 116.586 7.44485ZM29.0135 7.40527C35.971 7.40527 37.8834 11.5958 37.8834 16.112V24.2089C37.8834 25.7957 37.965 27.8301 38.2091 29.0508H33.408C33.3266 28.237 33.2858 27.4232 33.2858 26.5688V25.5922H33.2451C32.5534 27.301 30.7633 29.5795 26.5726 29.5796C21.8122 29.5796 19.1673 26.6501 19.1673 23.3137C19.1674 17.4955 26.2876 17.0073 29.3391 16.5191C32.0245 16.1122 33.2451 15.5831 33.2451 13.7929C33.2451 12.1248 31.6581 11.067 29.0949 11.067C26.8165 11.067 25.1484 12.3691 24.6601 14.4441H20.1846C20.7135 11.1078 23.5208 7.40535 29.0135 7.40527ZM66.6676 8.01701C68.4577 13.5504 70.2072 18.8399 71.9568 24.3326H71.9973C73.5435 19.2874 75.4559 13.5911 77.2055 8.01701H82.2099C79.606 15.0559 77.0835 22.0134 74.5202 29.0523H69.312L61.6223 8.01701H66.6676ZM18.3094 4.15021H4.92328V12.2878H17.2107V16.3973H4.92328V29.0508H0V0H18.3094V4.15021ZM52.6473 7.44485C58.099 7.44493 60.2147 11.066 60.2147 15.9077V29.0497H55.536V16.8839C55.536 13.629 54.437 11.5133 51.5078 11.5133C48.5783 11.5133 46.4216 14.036 46.4216 19.2845V29.0497H41.7024V8.01436H46.3406V11.7168C47.4392 9.31627 49.3921 7.44485 52.6473 7.44485ZM33.3265 17.0886C32.879 18.2685 31.7802 19.2856 28.1997 19.9773C25.3111 20.5062 23.8464 21.4015 23.8464 23.1509C23.8464 24.8191 25.2704 26.04 27.7523 26.04C30.5597 26.04 33.3265 24.2902 33.3265 19.2857V17.0886ZM116.586 11.1066C113.249 11.1066 111.011 13.263 110.564 16.5179H122.119C121.834 13.5071 120.085 11.1066 116.586 11.1066Z\"\n fill=\"currentColor\"\n />\n </svg>\n );\n};\n\n/**\n * The Fanvue brand logo. Supports full (icon + wordmark), icon-only, wordmark-only,\n * and portrait (stacked) layouts with multiple colour schemes.\n *\n * @example\n * ```tsx\n * <Logo type=\"full\" color=\"fullColour\" />\n * ```\n */\nexport const Logo = React.forwardRef<HTMLDivElement, LogoProps>(\n ({ className, variant = \"full\", color = \"fullColour\", size, ...props }, ref) => {\n const colors = getLogoColors(color, variant);\n const showIcon = variant === \"full\" || variant === \"icon\" || variant === \"portrait\";\n const showWordmark = variant === \"full\" || variant === \"wordmark\" || variant === \"portrait\";\n const sizeClass = sizeClasses[size ?? (variant === \"icon\" ? \"40\" : \"32\")];\n\n // When aria-label is provided, add role=\"img\" for proper accessibility\n const ariaProps = props[\"aria-label\"] ? { role: \"img\" as const } : {};\n\n return (\n <div\n ref={ref}\n data-testid=\"logo\"\n className={cn(\n \"inline-flex items-center text-content-primary\",\n variant === \"portrait\" ? \"flex-col gap-2\" : \"flex-row\",\n variant === \"full\" && \"gap-2\",\n className,\n )}\n {...ariaProps}\n {...props}\n >\n {showIcon && (\n <svg\n viewBox=\"0 0 39 39\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={cn(\"w-auto shrink-0\", sizeClass)}\n aria-hidden=\"true\"\n data-testid=\"logo-icon\"\n >\n <path\n d=\"M0 11.2339C0 5.02957 5.02957 0 11.2339 0H27.7661C33.9704 0 39 5.02957 39 11.2339V27.7661C39 33.9704 33.9704 39 27.7661 39H11.2339C5.02957 39 0 33.9704 0 27.7661V11.2339Z\"\n {...(color === \"decolour\" ? { className: colors.iconClass } : { fill: colors.icon })}\n />\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M12.277 30.5825C11.4418 30.5825 11.0355 29.8659 11.2059 29.1153C11.4275 28.0916 12.5838 25.0548 11.7145 23.6899C10.4361 21.6938 7.25562 21.9838 6.5397 20.9602C6.02371 20.2089 6.48355 19.478 7.19738 19.0493C8.79967 18.0257 11.902 18.3157 14.9191 16.3025C16.5895 15.2106 18.1237 12.9927 18.993 11.662C20.2203 9.78527 20.7487 9.39287 23.3226 9.39287H32.3376C33.7574 9.39287 34.202 11.8036 31.8852 12.0686C31.2886 12.1368 29.6977 12.3757 27.4306 12.6487C25.2658 12.9216 20.4589 13.5728 22.351 16.6608C23.7658 18.2816 26.7488 18.0769 27.4306 19.0493C27.9238 19.7225 27.4875 20.4384 26.9505 20.7824C25.3311 21.8061 21.8737 21.6938 18.8566 23.6899C16.8111 25.0548 15.1478 28.0916 14.4659 29.1153C13.9716 29.8659 13.1293 30.5825 12.294 30.5825H12.277Z\"\n {...(color === \"decolour\"\n ? { className: colors.iconInnerClass }\n : { fill: colors.iconInner })}\n />\n </svg>\n )}\n {showWordmark && <WordmarkSVG className={cn(\"w-auto\", sizeClass, colors.textClass)} />}\n </div>\n );\n },\n);\n\nLogo.displayName = \"Logo\";\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Logo.mjs","sources":["../../../src/components/Logo/Logo.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\nconst getLogoColors = (color: LogoColor, variant: LogoVariant) => {\n if (color === \"fullColour\") {\n return {\n icon: \"var(--color-brand-primary-default)\",\n iconInner: \"var(--primitives-color-gray-black)\",\n textClass: \"\", // Uses parent's text-content-primary\n };\n }\n\n if (color === \"decolour\") {\n return {\n iconClass: \"fill-[#151515] dark:fill-[#ffffff]\",\n iconInnerClass: \"fill-[#ffffff] dark:fill-[#151515]\",\n textClass: \"\", // Uses parent's text-content-primary\n };\n }\n\n if (color === \"whiteAlways\") {\n return {\n icon:\n variant === \"icon\"\n ? \"var(--primitives-color-gray-white)\"\n : \"var(--color-brand-primary-default)\",\n iconInner: \"var(--primitives-color-gray-black)\",\n textClass: \"text-content-always-white\",\n };\n }\n\n if (color === \"blackAlways\") {\n return {\n icon:\n variant === \"icon\"\n ? \"var(--primitives-color-gray-black)\"\n : \"var(--color-brand-primary-default)\",\n iconInner:\n variant === \"icon\"\n ? \"var(--primitives-color-gray-white)\"\n : \"var(--primitives-color-gray-black)\",\n textClass: \"text-content-always-black\",\n };\n }\n\n return {\n icon: \"var(--color-brand-primary-default)\",\n iconInner: \"var(--primitives-color-gray-black)\",\n textClass: \"\", // Default to adaptive color\n };\n};\n\n/** Layout variant of the logo. */\nexport type LogoVariant = \"full\" | \"icon\" | \"wordmark\" | \"portrait\";\n/** Colour scheme of the logo. */\nexport type LogoColor = \"fullColour\" | \"decolour\" | \"whiteAlways\" | \"blackAlways\";\n/** Height of the logo in pixels. Both icon and wordmark scale proportionally. */\nexport type LogoSize = \"16\" | \"20\" | \"24\" | \"32\" | \"40\" | \"48\" | \"64\";\n\nconst sizeClasses: Record<LogoSize, string> = {\n \"16\": \"h-4\",\n \"20\": \"h-5\",\n \"24\": \"h-6\",\n \"32\": \"h-8\",\n \"40\": \"h-10\",\n \"48\": \"h-12\",\n \"64\": \"h-16\",\n};\n\nexport interface LogoProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Layout variant of the logo. @default \"full\" */\n variant?: LogoVariant;\n /** Colour scheme of the logo. @default \"fullColour\" */\n color?: LogoColor;\n /** Height of the logo in pixels. @default \"32\" (or \"40\" when `variant=\"icon\"`) */\n size?: LogoSize;\n /**\n * Accessible label for the logo. Required when `type` is `\"icon\"` and\n * the logo is used inside interactive contexts (links, buttons).\n *\n * @example \"Fanvue home\"\n */\n \"aria-label\"?: string;\n}\n\nconst WordmarkSVG = ({ className }: { className?: string }) => {\n return (\n <svg\n viewBox=\"0 0 128 30\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n className={className}\n data-testid=\"logo-wordmark\"\n >\n <path\n d=\"M89.0679 20.1823C89.0679 23.4373 90.1256 25.553 93.0961 25.553C95.9847 25.553 98.1815 23.0304 98.1815 17.7818V8.01701H102.902V29.0523H98.2629V25.3495C97.1238 27.75 95.2114 29.6218 91.9566 29.6218C86.464 29.6218 84.3888 26.0006 84.3888 21.1589V8.01701H89.0679V20.1823ZM116.586 7.44485C123.787 7.44485 126.717 12.9782 126.757 18.9592C126.757 19.1627 126.757 19.4883 126.716 19.8544H110.523C110.889 23.5569 113.249 25.8353 116.586 25.8353C118.986 25.8353 121.02 24.8995 121.752 22.8245H126.432C125.211 27.0966 121.59 29.6192 116.586 29.6192C110.279 29.6192 106.007 25.1028 106.007 18.4707C106.007 12.0829 110.483 7.44485 116.586 7.44485ZM29.0135 7.40527C35.971 7.40527 37.8834 11.5958 37.8834 16.112V24.2089C37.8834 25.7957 37.965 27.8301 38.2091 29.0508H33.408C33.3266 28.237 33.2858 27.4232 33.2858 26.5688V25.5922H33.2451C32.5534 27.301 30.7633 29.5795 26.5726 29.5796C21.8122 29.5796 19.1673 26.6501 19.1673 23.3137C19.1674 17.4955 26.2876 17.0073 29.3391 16.5191C32.0245 16.1122 33.2451 15.5831 33.2451 13.7929C33.2451 12.1248 31.6581 11.067 29.0949 11.067C26.8165 11.067 25.1484 12.3691 24.6601 14.4441H20.1846C20.7135 11.1078 23.5208 7.40535 29.0135 7.40527ZM66.6676 8.01701C68.4577 13.5504 70.2072 18.8399 71.9568 24.3326H71.9973C73.5435 19.2874 75.4559 13.5911 77.2055 8.01701H82.2099C79.606 15.0559 77.0835 22.0134 74.5202 29.0523H69.312L61.6223 8.01701H66.6676ZM18.3094 4.15021H4.92328V12.2878H17.2107V16.3973H4.92328V29.0508H0V0H18.3094V4.15021ZM52.6473 7.44485C58.099 7.44493 60.2147 11.066 60.2147 15.9077V29.0497H55.536V16.8839C55.536 13.629 54.437 11.5133 51.5078 11.5133C48.5783 11.5133 46.4216 14.036 46.4216 19.2845V29.0497H41.7024V8.01436H46.3406V11.7168C47.4392 9.31627 49.3921 7.44485 52.6473 7.44485ZM33.3265 17.0886C32.879 18.2685 31.7802 19.2856 28.1997 19.9773C25.3111 20.5062 23.8464 21.4015 23.8464 23.1509C23.8464 24.8191 25.2704 26.04 27.7523 26.04C30.5597 26.04 33.3265 24.2902 33.3265 19.2857V17.0886ZM116.586 11.1066C113.249 11.1066 111.011 13.263 110.564 16.5179H122.119C121.834 13.5071 120.085 11.1066 116.586 11.1066Z\"\n fill=\"currentColor\"\n />\n </svg>\n );\n};\n\n/**\n * The Fanvue brand logo. Supports full (icon + wordmark), icon-only, wordmark-only,\n * and portrait (stacked) layouts with multiple colour schemes.\n *\n * @example\n * ```tsx\n * <Logo type=\"full\" color=\"fullColour\" />\n * ```\n */\nexport const Logo = React.forwardRef<HTMLDivElement, LogoProps>(\n ({ className, variant = \"full\", color = \"fullColour\", size, ...props }, ref) => {\n const colors = getLogoColors(color, variant);\n const showIcon = variant === \"full\" || variant === \"icon\" || variant === \"portrait\";\n const showWordmark = variant === \"full\" || variant === \"wordmark\" || variant === \"portrait\";\n const sizeClass = sizeClasses[size ?? (variant === \"icon\" ? \"40\" : \"32\")];\n\n // When aria-label is provided, add role=\"img\" for proper accessibility\n const ariaProps = props[\"aria-label\"] ? { role: \"img\" as const } : {};\n\n return (\n <div\n ref={ref}\n data-testid=\"logo\"\n className={cn(\n \"inline-flex items-center text-content-primary\",\n variant === \"portrait\" ? \"flex-col gap-2\" : \"flex-row\",\n variant === \"full\" && \"gap-2\",\n className,\n )}\n {...ariaProps}\n {...props}\n >\n {showIcon && (\n <svg\n viewBox=\"0 0 39 39\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={cn(\"w-auto shrink-0\", sizeClass)}\n aria-hidden=\"true\"\n data-testid=\"logo-icon\"\n >\n <path\n d=\"M0 11.2339C0 5.02957 5.02957 0 11.2339 0H27.7661C33.9704 0 39 5.02957 39 11.2339V27.7661C39 33.9704 33.9704 39 27.7661 39H11.2339C5.02957 39 0 33.9704 0 27.7661V11.2339Z\"\n {...(color === \"decolour\" ? { className: colors.iconClass } : { fill: colors.icon })}\n />\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M12.277 30.5825C11.4418 30.5825 11.0355 29.8659 11.2059 29.1153C11.4275 28.0916 12.5838 25.0548 11.7145 23.6899C10.4361 21.6938 7.25562 21.9838 6.5397 20.9602C6.02371 20.2089 6.48355 19.478 7.19738 19.0493C8.79967 18.0257 11.902 18.3157 14.9191 16.3025C16.5895 15.2106 18.1237 12.9927 18.993 11.662C20.2203 9.78527 20.7487 9.39287 23.3226 9.39287H32.3376C33.7574 9.39287 34.202 11.8036 31.8852 12.0686C31.2886 12.1368 29.6977 12.3757 27.4306 12.6487C25.2658 12.9216 20.4589 13.5728 22.351 16.6608C23.7658 18.2816 26.7488 18.0769 27.4306 19.0493C27.9238 19.7225 27.4875 20.4384 26.9505 20.7824C25.3311 21.8061 21.8737 21.6938 18.8566 23.6899C16.8111 25.0548 15.1478 28.0916 14.4659 29.1153C13.9716 29.8659 13.1293 30.5825 12.294 30.5825H12.277Z\"\n {...(color === \"decolour\"\n ? { className: colors.iconInnerClass }\n : { fill: colors.iconInner })}\n />\n </svg>\n )}\n {showWordmark && <WordmarkSVG className={cn(\"w-auto\", sizeClass, colors.textClass)} />}\n </div>\n );\n },\n);\n\nLogo.displayName = \"Logo\";\n"],"names":[],"mappings":"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"}
|
|
@@ -81,7 +81,7 @@ const MobileStepper = React.forwardRef(
|
|
|
81
81
|
"output",
|
|
82
82
|
{
|
|
83
83
|
"aria-live": "polite",
|
|
84
|
-
className: "typography-
|
|
84
|
+
className: "typography-description-12px-regular truncate text-content-secondary",
|
|
85
85
|
children: formatText(clampedStep + 1, safeSteps)
|
|
86
86
|
}
|
|
87
87
|
)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MobileStepper.mjs","sources":["../../../src/components/MobileStepper/MobileStepper.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { ProgressBar } from \"../ProgressBar/ProgressBar\";\n\n/** Display variant for the step indicator. */\nexport type MobileStepperVariant = \"dots\" | \"progress\" | \"text\";\n\n/** Positioning mode — `\"static\"` flows normally, `\"bottom\"` fixes to the viewport bottom. */\nexport type MobileStepperPosition = \"static\" | \"bottom\";\n\nexport interface MobileStepperProps extends React.HTMLAttributes<HTMLElement> {\n /** Total number of steps. */\n steps: number;\n /** Zero-indexed active step, clamped to `0` – `steps - 1`. */\n activeStep: number;\n /** Step indicator style. @default \"dots\" */\n variant?: MobileStepperVariant;\n /** Content rendered on the left (typically a \"Back\" button). */\n backButton?: React.ReactNode;\n /** Content rendered on the right (typically a \"Next\" button). */\n nextButton?: React.ReactNode;\n /** Positioning mode. @default \"static\" */\n position?: MobileStepperPosition;\n /** Accessible label for the stepper region. @default \"Progress\" */\n ariaLabel?: string;\n /** Accessible label for the step progress indicator (dots/progress bar). @default \"Step progress\" */\n stepProgressLabel?: string;\n /**\n * Formatter for the `aria-valuetext` on dots and progress variants.\n * Receives `(activeStep, totalSteps)` where `activeStep` is 1-indexed.\n * @default (active, total) => \\`Step ${active} of ${total}\\`\n */\n formatStepLabel?: (activeStep: number, totalSteps: number) => string;\n /**\n * Custom formatter for the text variant.\n * Receives `(activeStep, steps)` where `activeStep` is 1-indexed for display.\n * Only used when `variant` is `\"text\"`.\n * @default (active, total) => \\`${active} / ${total}\\`\n */\n formatText?: (activeStep: number, totalSteps: number) => string;\n}\n\nfunction defaultFormatText(activeStep: number, totalSteps: number): string {\n return `${activeStep} / ${totalSteps}`;\n}\n\nfunction defaultFormatStepLabel(activeStep: number, totalSteps: number): string {\n return `Step ${activeStep} of ${totalSteps}`;\n}\n\n/**\n * A compact, mobile-friendly stepper that shows progress through a sequence of\n * steps. Supports three indicator variants: dots, a progress bar, or text.\n *\n * @example\n * ```tsx\n * <MobileStepper\n * steps={6}\n * activeStep={2}\n * variant=\"dots\"\n * backButton={<Button size=\"sm\" onClick={handleBack}>Back</Button>}\n * nextButton={<Button size=\"sm\" onClick={handleNext}>Next</Button>}\n * />\n * ```\n */\nexport const MobileStepper = React.forwardRef<HTMLElement, MobileStepperProps>(\n (\n {\n steps,\n activeStep,\n variant = \"dots\",\n backButton,\n nextButton,\n position = \"static\",\n ariaLabel = \"Progress\",\n stepProgressLabel = \"Step progress\",\n formatStepLabel = defaultFormatStepLabel,\n formatText = defaultFormatText,\n className,\n ...props\n },\n ref,\n ) => {\n const safeSteps = Math.max(1, steps);\n const clampedStep = Math.min(safeSteps - 1, Math.max(0, activeStep));\n const progressValue = safeSteps > 1 ? (clampedStep / (safeSteps - 1)) * 100 : 100;\n const stepText = formatStepLabel(clampedStep + 1, safeSteps);\n\n const hasButtons = backButton != null || nextButton != null;\n\n const sharedClassName = cn(\n \"flex w-full items-center justify-between gap-2 bg-background-surface px-2 py-2\",\n position === \"bottom\" && \"fixed inset-x-0 bottom-0 z-50 pb-[env(safe-area-inset-bottom)]\",\n className,\n );\n\n const content = (\n <>\n {backButton != null && <div className=\"flex shrink-0\">{backButton}</div>}\n\n <div className=\"flex min-w-0 flex-1 items-center justify-center overflow-hidden\">\n {variant === \"dots\" && (\n <output aria-live=\"polite\" className=\"flex items-center\">\n <div\n role=\"progressbar\"\n aria-label={stepProgressLabel}\n aria-valuenow={clampedStep + 1}\n aria-valuemin={1}\n aria-valuemax={safeSteps}\n aria-valuetext={stepText}\n className=\"flex flex-wrap items-center gap-1\"\n >\n {Array.from({ length: safeSteps }, (_, i) => {\n const state =\n i < clampedStep ? \"completed\" : i === clampedStep ? \"active\" : \"incomplete\";\n return (\n <span\n // biome-ignore lint/suspicious/noArrayIndexKey: static dot list never reorders\n key={`step-${i}`}\n data-state={state}\n className={cn(\n \"block size-2 shrink-0 rounded-full motion-safe:transition-colors motion-safe:duration-150\",\n state === \"active\" ? \"bg-content-primary\" : \"bg-neutral-alphas-200\",\n )}\n aria-hidden=\"true\"\n />\n );\n })}\n </div>\n <span className=\"sr-only\">{stepText}</span>\n </output>\n )}\n\n {variant === \"progress\" && (\n <output aria-live=\"polite\" className=\"w-full max-w-md px-2\">\n <ProgressBar\n value={progressValue}\n size=\"small\"\n variant=\"generic\"\n ariaLabel={stepProgressLabel}\n ariaValueText={stepText}\n />\n </output>\n )}\n\n {variant === \"text\" && (\n <output\n aria-live=\"polite\"\n className=\"typography-
|
|
1
|
+
{"version":3,"file":"MobileStepper.mjs","sources":["../../../src/components/MobileStepper/MobileStepper.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { ProgressBar } from \"../ProgressBar/ProgressBar\";\n\n/** Display variant for the step indicator. */\nexport type MobileStepperVariant = \"dots\" | \"progress\" | \"text\";\n\n/** Positioning mode — `\"static\"` flows normally, `\"bottom\"` fixes to the viewport bottom. */\nexport type MobileStepperPosition = \"static\" | \"bottom\";\n\nexport interface MobileStepperProps extends React.HTMLAttributes<HTMLElement> {\n /** Total number of steps. */\n steps: number;\n /** Zero-indexed active step, clamped to `0` – `steps - 1`. */\n activeStep: number;\n /** Step indicator style. @default \"dots\" */\n variant?: MobileStepperVariant;\n /** Content rendered on the left (typically a \"Back\" button). */\n backButton?: React.ReactNode;\n /** Content rendered on the right (typically a \"Next\" button). */\n nextButton?: React.ReactNode;\n /** Positioning mode. @default \"static\" */\n position?: MobileStepperPosition;\n /** Accessible label for the stepper region. @default \"Progress\" */\n ariaLabel?: string;\n /** Accessible label for the step progress indicator (dots/progress bar). @default \"Step progress\" */\n stepProgressLabel?: string;\n /**\n * Formatter for the `aria-valuetext` on dots and progress variants.\n * Receives `(activeStep, totalSteps)` where `activeStep` is 1-indexed.\n * @default (active, total) => \\`Step ${active} of ${total}\\`\n */\n formatStepLabel?: (activeStep: number, totalSteps: number) => string;\n /**\n * Custom formatter for the text variant.\n * Receives `(activeStep, steps)` where `activeStep` is 1-indexed for display.\n * Only used when `variant` is `\"text\"`.\n * @default (active, total) => \\`${active} / ${total}\\`\n */\n formatText?: (activeStep: number, totalSteps: number) => string;\n}\n\nfunction defaultFormatText(activeStep: number, totalSteps: number): string {\n return `${activeStep} / ${totalSteps}`;\n}\n\nfunction defaultFormatStepLabel(activeStep: number, totalSteps: number): string {\n return `Step ${activeStep} of ${totalSteps}`;\n}\n\n/**\n * A compact, mobile-friendly stepper that shows progress through a sequence of\n * steps. Supports three indicator variants: dots, a progress bar, or text.\n *\n * @example\n * ```tsx\n * <MobileStepper\n * steps={6}\n * activeStep={2}\n * variant=\"dots\"\n * backButton={<Button size=\"sm\" onClick={handleBack}>Back</Button>}\n * nextButton={<Button size=\"sm\" onClick={handleNext}>Next</Button>}\n * />\n * ```\n */\nexport const MobileStepper = React.forwardRef<HTMLElement, MobileStepperProps>(\n (\n {\n steps,\n activeStep,\n variant = \"dots\",\n backButton,\n nextButton,\n position = \"static\",\n ariaLabel = \"Progress\",\n stepProgressLabel = \"Step progress\",\n formatStepLabel = defaultFormatStepLabel,\n formatText = defaultFormatText,\n className,\n ...props\n },\n ref,\n ) => {\n const safeSteps = Math.max(1, steps);\n const clampedStep = Math.min(safeSteps - 1, Math.max(0, activeStep));\n const progressValue = safeSteps > 1 ? (clampedStep / (safeSteps - 1)) * 100 : 100;\n const stepText = formatStepLabel(clampedStep + 1, safeSteps);\n\n const hasButtons = backButton != null || nextButton != null;\n\n const sharedClassName = cn(\n \"flex w-full items-center justify-between gap-2 bg-background-surface px-2 py-2\",\n position === \"bottom\" && \"fixed inset-x-0 bottom-0 z-50 pb-[env(safe-area-inset-bottom)]\",\n className,\n );\n\n const content = (\n <>\n {backButton != null && <div className=\"flex shrink-0\">{backButton}</div>}\n\n <div className=\"flex min-w-0 flex-1 items-center justify-center overflow-hidden\">\n {variant === \"dots\" && (\n <output aria-live=\"polite\" className=\"flex items-center\">\n <div\n role=\"progressbar\"\n aria-label={stepProgressLabel}\n aria-valuenow={clampedStep + 1}\n aria-valuemin={1}\n aria-valuemax={safeSteps}\n aria-valuetext={stepText}\n className=\"flex flex-wrap items-center gap-1\"\n >\n {Array.from({ length: safeSteps }, (_, i) => {\n const state =\n i < clampedStep ? \"completed\" : i === clampedStep ? \"active\" : \"incomplete\";\n return (\n <span\n // biome-ignore lint/suspicious/noArrayIndexKey: static dot list never reorders\n key={`step-${i}`}\n data-state={state}\n className={cn(\n \"block size-2 shrink-0 rounded-full motion-safe:transition-colors motion-safe:duration-150\",\n state === \"active\" ? \"bg-content-primary\" : \"bg-neutral-alphas-200\",\n )}\n aria-hidden=\"true\"\n />\n );\n })}\n </div>\n <span className=\"sr-only\">{stepText}</span>\n </output>\n )}\n\n {variant === \"progress\" && (\n <output aria-live=\"polite\" className=\"w-full max-w-md px-2\">\n <ProgressBar\n value={progressValue}\n size=\"small\"\n variant=\"generic\"\n ariaLabel={stepProgressLabel}\n ariaValueText={stepText}\n />\n </output>\n )}\n\n {variant === \"text\" && (\n <output\n aria-live=\"polite\"\n className=\"typography-description-12px-regular truncate text-content-secondary\"\n >\n {formatText(clampedStep + 1, safeSteps)}\n </output>\n )}\n </div>\n\n {nextButton != null && <div className=\"flex shrink-0\">{nextButton}</div>}\n </>\n );\n\n if (hasButtons) {\n return (\n <nav\n ref={ref as React.Ref<HTMLElement>}\n aria-label={ariaLabel}\n className={sharedClassName}\n {...props}\n >\n {content}\n </nav>\n );\n }\n\n return (\n <fieldset\n ref={ref as React.Ref<HTMLFieldSetElement>}\n aria-label={ariaLabel}\n className={cn(sharedClassName, \"m-0 border-0 p-0\")}\n {...props}\n >\n {content}\n </fieldset>\n );\n },\n);\n\nMobileStepper.displayName = \"MobileStepper\";\n"],"names":[],"mappings":"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"}
|
|
@@ -83,7 +83,7 @@ const Pagination = React.forwardRef(
|
|
|
83
83
|
onClick: () => onPageChange?.(page),
|
|
84
84
|
className: cn(
|
|
85
85
|
"flex size-4 cursor-pointer items-center justify-center rounded-full text-xs focus-visible:shadow-focus-ring focus-visible:outline-none motion-safe:transition-colors motion-safe:duration-150",
|
|
86
|
-
page === currentPage ? "bg-buttons-primary text-content-primary-inverted" : "bg-neutral-alphas-50 text-content-primary hover:bg-neutral-alphas-100 active:bg-neutral-alphas-100"
|
|
86
|
+
page === currentPage ? "bg-buttons-primary-default text-content-primary-inverted" : "bg-neutral-alphas-50 text-content-primary hover:bg-neutral-alphas-100 active:bg-neutral-alphas-100"
|
|
87
87
|
),
|
|
88
88
|
children: page
|
|
89
89
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Pagination.mjs","sources":["../../../src/components/Pagination/Pagination.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { IconButton } from \"../IconButton/IconButton\";\nimport { ChevronLeftIcon } from \"../Icons/ChevronLeftIcon\";\nimport { ChevronRightIcon } from \"../Icons/ChevronRightIcon\";\n\n/** Pagination display style — numbered buttons or minimal dots. */\nexport type PaginationVariant = \"default\" | \"dots\";\n\nexport interface PaginationProps extends Omit<React.HTMLAttributes<HTMLElement>, \"onChange\"> {\n /** Display style — numbered page buttons or minimal dots. @default \"default\" */\n variant?: PaginationVariant;\n /** Total number of pages. */\n totalPages: number;\n /** Current active page (1-indexed). */\n currentPage: number;\n /** Callback fired when the active page changes. Receives the new 1-indexed page number. */\n onPageChange?: (page: number) => void;\n /** Accessible label for the `<nav>` landmark. @default \"Pagination\" */\n ariaLabel?: string;\n /** Accessible label for the previous-page button. @default \"Previous page\" */\n previousLabel?: string;\n /** Accessible label for the next-page button. @default \"Next page\" */\n nextLabel?: string;\n /** Function that returns an accessible label for each page button. @default (page) => \\`Page ${page}\\` */\n getPageLabel?: (page: number) => string;\n}\n\ntype PageItem = number | \"ellipsis-start\" | \"ellipsis-end\";\n\nfunction getVisiblePages(currentPage: number, totalPages: number): PageItem[] {\n if (totalPages <= 7) {\n return Array.from({ length: totalPages }, (_, i) => i + 1);\n }\n\n const pages: PageItem[] = [1];\n\n if (currentPage <= 4) {\n pages.push(2, 3, 4, 5, \"ellipsis-end\");\n } else if (currentPage >= totalPages - 3) {\n pages.push(\"ellipsis-start\", totalPages - 4, totalPages - 3, totalPages - 2, totalPages - 1);\n } else {\n pages.push(\"ellipsis-start\", currentPage - 1, currentPage, currentPage + 1, \"ellipsis-end\");\n }\n\n pages.push(totalPages);\n return pages;\n}\n\n/**\n * Page navigation control with previous/next buttons and numbered page\n * indicators. Supports a numbered-buttons layout (`\"default\"`) and a compact\n * dots layout (`\"dots\"`).\n *\n * @example\n * ```tsx\n * <Pagination totalPages={10} currentPage={page} onPageChange={setPage} />\n * ```\n */\nexport const Pagination = React.forwardRef<HTMLElement, PaginationProps>(\n (\n {\n variant = \"default\",\n totalPages,\n currentPage,\n onPageChange,\n ariaLabel = \"Pagination\",\n previousLabel = \"Previous page\",\n nextLabel = \"Next page\",\n getPageLabel = (page: number) => `Page ${page}`,\n className,\n ...props\n },\n ref,\n ) => {\n const isFirstPage = currentPage <= 1;\n const isLastPage = currentPage >= totalPages;\n\n const handlePrevious = () => {\n if (!isFirstPage) onPageChange?.(currentPage - 1);\n };\n\n const handleNext = () => {\n if (!isLastPage) onPageChange?.(currentPage + 1);\n };\n\n return (\n <nav\n ref={ref}\n aria-label={ariaLabel}\n className={cn(\n \"inline-flex items-center pb-4\",\n variant === \"default\" && \"gap-3\",\n variant === \"dots\" && \"gap-4\",\n className,\n )}\n {...props}\n >\n <IconButton\n variant=\"tertiary\"\n size=\"32\"\n icon={<ChevronLeftIcon />}\n aria-label={previousLabel}\n disabled={isFirstPage}\n onClick={handlePrevious}\n />\n\n {variant === \"default\" && (\n <div className=\"flex items-center gap-3\">\n {getVisiblePages(currentPage, totalPages).map((page) =>\n typeof page === \"string\" ? (\n <span\n key={page}\n className=\"flex size-4 items-center justify-center text-content-secondary text-xs\"\n aria-hidden=\"true\"\n >\n …\n </span>\n ) : (\n <button\n key={page}\n type=\"button\"\n aria-label={getPageLabel(page)}\n aria-current={page === currentPage ? \"page\" : undefined}\n onClick={() => onPageChange?.(page)}\n className={cn(\n \"flex size-4 cursor-pointer items-center justify-center rounded-full text-xs focus-visible:shadow-focus-ring focus-visible:outline-none motion-safe:transition-colors motion-safe:duration-150\",\n page === currentPage\n ? \"bg-buttons-primary text-content-primary-inverted\"\n : \"bg-neutral-alphas-50 text-content-primary hover:bg-neutral-alphas-100 active:bg-neutral-alphas-100\",\n )}\n >\n {page}\n </button>\n ),\n )}\n </div>\n )}\n\n {variant === \"dots\" && (\n <div className=\"flex items-center\">\n {Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => (\n <button\n key={page}\n type=\"button\"\n aria-label={getPageLabel(page)}\n aria-current={page === currentPage ? \"page\" : undefined}\n onClick={() => onPageChange?.(page)}\n className=\"flex size-6 cursor-pointer items-center justify-center rounded-full focus-visible:shadow-focus-ring focus-visible:outline-none\"\n >\n <span\n className={cn(\n \"block rounded-full motion-safe:transition-all motion-safe:duration-150\",\n page === currentPage\n ? \"size-2 bg-neutral-alphas-400\"\n : \"size-1.5 bg-neutral-alphas-200 hover:bg-neutral-alphas-300 active:bg-neutral-alphas-300\",\n )}\n />\n </button>\n ))}\n </div>\n )}\n\n <IconButton\n variant=\"tertiary\"\n size=\"32\"\n icon={<ChevronRightIcon />}\n aria-label={nextLabel}\n disabled={isLastPage}\n onClick={handleNext}\n />\n </nav>\n );\n },\n);\n\nPagination.displayName = \"Pagination\";\n"],"names":[],"mappings":";;;;;;;AA8BA,SAAS,gBAAgB,aAAqB,YAAgC;AAC5E,MAAI,cAAc,GAAG;AACnB,WAAO,MAAM,KAAK,EAAE,QAAQ,cAAc,CAAC,GAAG,MAAM,IAAI,CAAC;AAAA,EAC3D;AAEA,QAAM,QAAoB,CAAC,CAAC;AAE5B,MAAI,eAAe,GAAG;AACpB,UAAM,KAAK,GAAG,GAAG,GAAG,GAAG,cAAc;AAAA,EACvC,WAAW,eAAe,aAAa,GAAG;AACxC,UAAM,KAAK,kBAAkB,aAAa,GAAG,aAAa,GAAG,aAAa,GAAG,aAAa,CAAC;AAAA,EAC7F,OAAO;AACL,UAAM,KAAK,kBAAkB,cAAc,GAAG,aAAa,cAAc,GAAG,cAAc;AAAA,EAC5F;AAEA,QAAM,KAAK,UAAU;AACrB,SAAO;AACT;AAYO,MAAM,aAAa,MAAM;AAAA,EAC9B,CACE;AAAA,IACE,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,eAAe,CAAC,SAAiB,QAAQ,IAAI;AAAA,IAC7C;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,cAAc,eAAe;AACnC,UAAM,aAAa,eAAe;AAElC,UAAM,iBAAiB,MAAM;AAC3B,UAAI,CAAC,YAAa,gBAAe,cAAc,CAAC;AAAA,IAClD;AAEA,UAAM,aAAa,MAAM;AACvB,UAAI,CAAC,WAAY,gBAAe,cAAc,CAAC;AAAA,IACjD;AAEA,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,cAAY;AAAA,QACZ,WAAW;AAAA,UACT;AAAA,UACA,YAAY,aAAa;AAAA,UACzB,YAAY,UAAU;AAAA,UACtB;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEJ,UAAA;AAAA,UAAA;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,SAAQ;AAAA,cACR,MAAK;AAAA,cACL,0BAAO,iBAAA,EAAgB;AAAA,cACvB,cAAY;AAAA,cACZ,UAAU;AAAA,cACV,SAAS;AAAA,YAAA;AAAA,UAAA;AAAA,UAGV,YAAY,aACX,oBAAC,OAAA,EAAI,WAAU,2BACZ,UAAA,gBAAgB,aAAa,UAAU,EAAE;AAAA,YAAI,CAAC,SAC7C,OAAO,SAAS,WACd;AAAA,cAAC;AAAA,cAAA;AAAA,gBAEC,WAAU;AAAA,gBACV,eAAY;AAAA,gBACb,UAAA;AAAA,cAAA;AAAA,cAHM;AAAA,YAAA,IAOP;AAAA,cAAC;AAAA,cAAA;AAAA,gBAEC,MAAK;AAAA,gBACL,cAAY,aAAa,IAAI;AAAA,gBAC7B,gBAAc,SAAS,cAAc,SAAS;AAAA,gBAC9C,SAAS,MAAM,eAAe,IAAI;AAAA,gBAClC,WAAW;AAAA,kBACT;AAAA,kBACA,SAAS,cACL,
|
|
1
|
+
{"version":3,"file":"Pagination.mjs","sources":["../../../src/components/Pagination/Pagination.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { IconButton } from \"../IconButton/IconButton\";\nimport { ChevronLeftIcon } from \"../Icons/ChevronLeftIcon\";\nimport { ChevronRightIcon } from \"../Icons/ChevronRightIcon\";\n\n/** Pagination display style — numbered buttons or minimal dots. */\nexport type PaginationVariant = \"default\" | \"dots\";\n\nexport interface PaginationProps extends Omit<React.HTMLAttributes<HTMLElement>, \"onChange\"> {\n /** Display style — numbered page buttons or minimal dots. @default \"default\" */\n variant?: PaginationVariant;\n /** Total number of pages. */\n totalPages: number;\n /** Current active page (1-indexed). */\n currentPage: number;\n /** Callback fired when the active page changes. Receives the new 1-indexed page number. */\n onPageChange?: (page: number) => void;\n /** Accessible label for the `<nav>` landmark. @default \"Pagination\" */\n ariaLabel?: string;\n /** Accessible label for the previous-page button. @default \"Previous page\" */\n previousLabel?: string;\n /** Accessible label for the next-page button. @default \"Next page\" */\n nextLabel?: string;\n /** Function that returns an accessible label for each page button. @default (page) => \\`Page ${page}\\` */\n getPageLabel?: (page: number) => string;\n}\n\ntype PageItem = number | \"ellipsis-start\" | \"ellipsis-end\";\n\nfunction getVisiblePages(currentPage: number, totalPages: number): PageItem[] {\n if (totalPages <= 7) {\n return Array.from({ length: totalPages }, (_, i) => i + 1);\n }\n\n const pages: PageItem[] = [1];\n\n if (currentPage <= 4) {\n pages.push(2, 3, 4, 5, \"ellipsis-end\");\n } else if (currentPage >= totalPages - 3) {\n pages.push(\"ellipsis-start\", totalPages - 4, totalPages - 3, totalPages - 2, totalPages - 1);\n } else {\n pages.push(\"ellipsis-start\", currentPage - 1, currentPage, currentPage + 1, \"ellipsis-end\");\n }\n\n pages.push(totalPages);\n return pages;\n}\n\n/**\n * Page navigation control with previous/next buttons and numbered page\n * indicators. Supports a numbered-buttons layout (`\"default\"`) and a compact\n * dots layout (`\"dots\"`).\n *\n * @example\n * ```tsx\n * <Pagination totalPages={10} currentPage={page} onPageChange={setPage} />\n * ```\n */\nexport const Pagination = React.forwardRef<HTMLElement, PaginationProps>(\n (\n {\n variant = \"default\",\n totalPages,\n currentPage,\n onPageChange,\n ariaLabel = \"Pagination\",\n previousLabel = \"Previous page\",\n nextLabel = \"Next page\",\n getPageLabel = (page: number) => `Page ${page}`,\n className,\n ...props\n },\n ref,\n ) => {\n const isFirstPage = currentPage <= 1;\n const isLastPage = currentPage >= totalPages;\n\n const handlePrevious = () => {\n if (!isFirstPage) onPageChange?.(currentPage - 1);\n };\n\n const handleNext = () => {\n if (!isLastPage) onPageChange?.(currentPage + 1);\n };\n\n return (\n <nav\n ref={ref}\n aria-label={ariaLabel}\n className={cn(\n \"inline-flex items-center pb-4\",\n variant === \"default\" && \"gap-3\",\n variant === \"dots\" && \"gap-4\",\n className,\n )}\n {...props}\n >\n <IconButton\n variant=\"tertiary\"\n size=\"32\"\n icon={<ChevronLeftIcon />}\n aria-label={previousLabel}\n disabled={isFirstPage}\n onClick={handlePrevious}\n />\n\n {variant === \"default\" && (\n <div className=\"flex items-center gap-3\">\n {getVisiblePages(currentPage, totalPages).map((page) =>\n typeof page === \"string\" ? (\n <span\n key={page}\n className=\"flex size-4 items-center justify-center text-content-secondary text-xs\"\n aria-hidden=\"true\"\n >\n …\n </span>\n ) : (\n <button\n key={page}\n type=\"button\"\n aria-label={getPageLabel(page)}\n aria-current={page === currentPage ? \"page\" : undefined}\n onClick={() => onPageChange?.(page)}\n className={cn(\n \"flex size-4 cursor-pointer items-center justify-center rounded-full text-xs focus-visible:shadow-focus-ring focus-visible:outline-none motion-safe:transition-colors motion-safe:duration-150\",\n page === currentPage\n ? \"bg-buttons-primary-default text-content-primary-inverted\"\n : \"bg-neutral-alphas-50 text-content-primary hover:bg-neutral-alphas-100 active:bg-neutral-alphas-100\",\n )}\n >\n {page}\n </button>\n ),\n )}\n </div>\n )}\n\n {variant === \"dots\" && (\n <div className=\"flex items-center\">\n {Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => (\n <button\n key={page}\n type=\"button\"\n aria-label={getPageLabel(page)}\n aria-current={page === currentPage ? \"page\" : undefined}\n onClick={() => onPageChange?.(page)}\n className=\"flex size-6 cursor-pointer items-center justify-center rounded-full focus-visible:shadow-focus-ring focus-visible:outline-none\"\n >\n <span\n className={cn(\n \"block rounded-full motion-safe:transition-all motion-safe:duration-150\",\n page === currentPage\n ? \"size-2 bg-neutral-alphas-400\"\n : \"size-1.5 bg-neutral-alphas-200 hover:bg-neutral-alphas-300 active:bg-neutral-alphas-300\",\n )}\n />\n </button>\n ))}\n </div>\n )}\n\n <IconButton\n variant=\"tertiary\"\n size=\"32\"\n icon={<ChevronRightIcon />}\n aria-label={nextLabel}\n disabled={isLastPage}\n onClick={handleNext}\n />\n </nav>\n );\n },\n);\n\nPagination.displayName = \"Pagination\";\n"],"names":[],"mappings":";;;;;;;AA8BA,SAAS,gBAAgB,aAAqB,YAAgC;AAC5E,MAAI,cAAc,GAAG;AACnB,WAAO,MAAM,KAAK,EAAE,QAAQ,cAAc,CAAC,GAAG,MAAM,IAAI,CAAC;AAAA,EAC3D;AAEA,QAAM,QAAoB,CAAC,CAAC;AAE5B,MAAI,eAAe,GAAG;AACpB,UAAM,KAAK,GAAG,GAAG,GAAG,GAAG,cAAc;AAAA,EACvC,WAAW,eAAe,aAAa,GAAG;AACxC,UAAM,KAAK,kBAAkB,aAAa,GAAG,aAAa,GAAG,aAAa,GAAG,aAAa,CAAC;AAAA,EAC7F,OAAO;AACL,UAAM,KAAK,kBAAkB,cAAc,GAAG,aAAa,cAAc,GAAG,cAAc;AAAA,EAC5F;AAEA,QAAM,KAAK,UAAU;AACrB,SAAO;AACT;AAYO,MAAM,aAAa,MAAM;AAAA,EAC9B,CACE;AAAA,IACE,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,eAAe,CAAC,SAAiB,QAAQ,IAAI;AAAA,IAC7C;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,cAAc,eAAe;AACnC,UAAM,aAAa,eAAe;AAElC,UAAM,iBAAiB,MAAM;AAC3B,UAAI,CAAC,YAAa,gBAAe,cAAc,CAAC;AAAA,IAClD;AAEA,UAAM,aAAa,MAAM;AACvB,UAAI,CAAC,WAAY,gBAAe,cAAc,CAAC;AAAA,IACjD;AAEA,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,cAAY;AAAA,QACZ,WAAW;AAAA,UACT;AAAA,UACA,YAAY,aAAa;AAAA,UACzB,YAAY,UAAU;AAAA,UACtB;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEJ,UAAA;AAAA,UAAA;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,SAAQ;AAAA,cACR,MAAK;AAAA,cACL,0BAAO,iBAAA,EAAgB;AAAA,cACvB,cAAY;AAAA,cACZ,UAAU;AAAA,cACV,SAAS;AAAA,YAAA;AAAA,UAAA;AAAA,UAGV,YAAY,aACX,oBAAC,OAAA,EAAI,WAAU,2BACZ,UAAA,gBAAgB,aAAa,UAAU,EAAE;AAAA,YAAI,CAAC,SAC7C,OAAO,SAAS,WACd;AAAA,cAAC;AAAA,cAAA;AAAA,gBAEC,WAAU;AAAA,gBACV,eAAY;AAAA,gBACb,UAAA;AAAA,cAAA;AAAA,cAHM;AAAA,YAAA,IAOP;AAAA,cAAC;AAAA,cAAA;AAAA,gBAEC,MAAK;AAAA,gBACL,cAAY,aAAa,IAAI;AAAA,gBAC7B,gBAAc,SAAS,cAAc,SAAS;AAAA,gBAC9C,SAAS,MAAM,eAAe,IAAI;AAAA,gBAClC,WAAW;AAAA,kBACT;AAAA,kBACA,SAAS,cACL,6DACA;AAAA,gBAAA;AAAA,gBAGL,UAAA;AAAA,cAAA;AAAA,cAZI;AAAA,YAAA;AAAA,UAaP,GAGN;AAAA,UAGD,YAAY,UACX,oBAAC,OAAA,EAAI,WAAU,qBACZ,UAAA,MAAM,KAAK,EAAE,QAAQ,cAAc,CAAC,GAAG,MAAM,IAAI,CAAC,EAAE,IAAI,CAAC,SACxD;AAAA,YAAC;AAAA,YAAA;AAAA,cAEC,MAAK;AAAA,cACL,cAAY,aAAa,IAAI;AAAA,cAC7B,gBAAc,SAAS,cAAc,SAAS;AAAA,cAC9C,SAAS,MAAM,eAAe,IAAI;AAAA,cAClC,WAAU;AAAA,cAEV,UAAA;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,WAAW;AAAA,oBACT;AAAA,oBACA,SAAS,cACL,iCACA;AAAA,kBAAA;AAAA,gBACN;AAAA,cAAA;AAAA,YACF;AAAA,YAdK;AAAA,UAAA,CAgBR,GACH;AAAA,UAGF;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,SAAQ;AAAA,cACR,MAAK;AAAA,cACL,0BAAO,kBAAA,EAAiB;AAAA,cACxB,cAAY;AAAA,cACZ,UAAU;AAAA,cACV,SAAS;AAAA,YAAA;AAAA,UAAA;AAAA,QACX;AAAA,MAAA;AAAA,IAAA;AAAA,EAGN;AACF;AAEA,WAAW,cAAc;"}
|
|
@@ -10,10 +10,10 @@ const pillVariants = {
|
|
|
10
10
|
blue: "bg-info-surface text-info-content",
|
|
11
11
|
gold: "bg-warning-surface text-warning-content",
|
|
12
12
|
pinkLight: "bg-brand-secondary-muted text-content-primary",
|
|
13
|
-
base: "bg-
|
|
14
|
-
brand: "bg-brand-primary-default text-content-
|
|
15
|
-
brandLight: "bg-brand-primary-muted text-content-
|
|
16
|
-
beta: "bg-brand-secondary-default text-content-
|
|
13
|
+
base: "bg-surface-primary-inverted text-content-primary-inverted",
|
|
14
|
+
brand: "bg-brand-primary-default text-content-always-black",
|
|
15
|
+
brandLight: "bg-brand-primary-muted text-content-primary",
|
|
16
|
+
beta: "bg-brand-secondary-default text-content-always-black",
|
|
17
17
|
error: "bg-error-content text-error-surface"
|
|
18
18
|
}
|
|
19
19
|
};
|
|
@@ -38,7 +38,7 @@ const Pill = React.forwardRef(
|
|
|
38
38
|
// Base styles
|
|
39
39
|
"inline-flex min-w-0 items-center justify-center gap-2 rounded-full px-3 py-1",
|
|
40
40
|
// Typography
|
|
41
|
-
"typography-
|
|
41
|
+
"typography-description-12px-semibold",
|
|
42
42
|
// Variant styles
|
|
43
43
|
pillVariants.variant[variant],
|
|
44
44
|
// Interactive
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Pill.mjs","sources":["../../../src/components/Pill/Pill.tsx"],"sourcesContent":["import { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\nconst pillVariants = {\n variant: {\n green: \"bg-success-surface text-success-content\",\n grey: \"bg-neutral-alphas-50 text-content-secondary\",\n blue: \"bg-info-surface text-info-content\",\n gold: \"bg-warning-surface text-warning-content\",\n pinkLight: \"bg-brand-secondary-muted text-content-primary\",\n base: \"bg-
|
|
1
|
+
{"version":3,"file":"Pill.mjs","sources":["../../../src/components/Pill/Pill.tsx"],"sourcesContent":["import { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\nconst pillVariants = {\n variant: {\n green: \"bg-success-surface text-success-content\",\n grey: \"bg-neutral-alphas-50 text-content-secondary\",\n blue: \"bg-info-surface text-info-content\",\n gold: \"bg-warning-surface text-warning-content\",\n pinkLight: \"bg-brand-secondary-muted text-content-primary\",\n base: \"bg-surface-primary-inverted text-content-primary-inverted\",\n brand: \"bg-brand-primary-default text-content-always-black\",\n brandLight: \"bg-brand-primary-muted text-content-primary\",\n beta: \"bg-brand-secondary-default text-content-always-black\",\n error: \"bg-error-content text-error-surface\",\n },\n} as const;\n\n/** Colour variant of the pill. */\nexport type PillVariant =\n | \"green\"\n | \"grey\"\n | \"blue\"\n | \"gold\"\n | \"pinkLight\"\n | \"base\"\n | \"brand\"\n | \"brandLight\"\n | \"beta\"\n | \"error\";\n\nexport interface PillProps extends React.HTMLAttributes<HTMLSpanElement> {\n /** Colour variant of the pill. @default \"green\" */\n variant?: PillVariant;\n /** Icon element displayed before the label. */\n leftIcon?: React.ReactNode;\n /** Icon element displayed after the label. */\n rightIcon?: React.ReactNode;\n /** Merge props onto a child element instead of rendering a `<span>`. @default false */\n asChild?: boolean;\n}\n\n/**\n * A small rounded label for categorisation, status, or tagging.\n *\n * @example\n * ```tsx\n * <Pill variant=\"brand\">New</Pill>\n * ```\n */\nexport const Pill = React.forwardRef<HTMLSpanElement, PillProps>(\n (\n {\n className,\n variant = \"green\",\n leftIcon,\n rightIcon,\n asChild = false,\n onClick,\n children,\n ...props\n },\n ref,\n ) => {\n const Comp = asChild ? Slot : \"span\";\n\n return (\n <Comp\n ref={ref}\n data-testid=\"pill\"\n className={cn(\n // Base styles\n \"inline-flex min-w-0 items-center justify-center gap-2 rounded-full px-3 py-1\",\n // Typography\n \"typography-description-12px-semibold\",\n // Variant styles\n pillVariants.variant[variant],\n // Interactive\n onClick && \"cursor-pointer\",\n // Manual CSS overrides\n className,\n )}\n onClick={onClick}\n {...props}\n >\n {leftIcon && (\n <span className=\"flex [&>svg]:size-3\" aria-hidden=\"true\">\n {leftIcon}\n </span>\n )}\n {asChild ? (\n <Slottable>{children}</Slottable>\n ) : (\n <span className=\"min-w-0 truncate\">{children}</span>\n )}\n {rightIcon && (\n <span className=\"flex [&>svg]:size-3\" aria-hidden=\"true\">\n {rightIcon}\n </span>\n )}\n </Comp>\n );\n },\n);\n\nPill.displayName = \"Pill\";\n"],"names":[],"mappings":";;;;;AAIA,MAAM,eAAe;AAAA,EACnB,SAAS;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,WAAW;AAAA,IACX,MAAM;AAAA,IACN,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,OAAO;AAAA,EAAA;AAEX;AAkCO,MAAM,OAAO,MAAM;AAAA,EACxB,CACE;AAAA,IACE;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,OAAO,UAAU,OAAO;AAE9B,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,eAAY;AAAA,QACZ,WAAW;AAAA;AAAA,UAET;AAAA;AAAA,UAEA;AAAA;AAAA,UAEA,aAAa,QAAQ,OAAO;AAAA;AAAA,UAE5B,WAAW;AAAA;AAAA,UAEX;AAAA,QAAA;AAAA,QAEF;AAAA,QACC,GAAG;AAAA,QAEH,UAAA;AAAA,UAAA,gCACE,QAAA,EAAK,WAAU,uBAAsB,eAAY,QAC/C,UAAA,UACH;AAAA,UAED,8BACE,WAAA,EAAW,SAAA,CAAS,IAErB,oBAAC,QAAA,EAAK,WAAU,oBAAoB,SAAA,CAAS;AAAA,UAE9C,aACC,oBAAC,QAAA,EAAK,WAAU,uBAAsB,eAAY,QAC/C,UAAA,UAAA,CACH;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEA,KAAK,cAAc;"}
|
|
@@ -50,13 +50,13 @@ const ProgressBar = React.forwardRef(
|
|
|
50
50
|
const showFooter = leftIcon != null || helperLeft != null || helperRight != null;
|
|
51
51
|
return /* @__PURE__ */ jsxs("div", { ref, className: cn("flex w-full flex-col", GAP[size], className), ...props, children: [
|
|
52
52
|
showHeader && /* @__PURE__ */ jsxs("div", { className: "flex w-full items-end justify-between", children: [
|
|
53
|
-
title != null && /* @__PURE__ */ jsx("p", { className: "typography-
|
|
53
|
+
title != null && /* @__PURE__ */ jsx("p", { className: "typography-description-12px-semibold text-content-primary", children: title }),
|
|
54
54
|
showCompletion && /* @__PURE__ */ jsxs(
|
|
55
55
|
"span",
|
|
56
56
|
{
|
|
57
57
|
className: cn(
|
|
58
58
|
textColor,
|
|
59
|
-
isSmall ? "typography-
|
|
59
|
+
isSmall ? "typography-header-heading-sm" : "typography-header-heading-xl"
|
|
60
60
|
),
|
|
61
61
|
children: [
|
|
62
62
|
Math.round(clampedValue),
|
|
@@ -64,7 +64,7 @@ const ProgressBar = React.forwardRef(
|
|
|
64
64
|
]
|
|
65
65
|
}
|
|
66
66
|
),
|
|
67
|
-
stepsLabel != null && /* @__PURE__ */ jsx("span", { className: "typography-
|
|
67
|
+
stepsLabel != null && /* @__PURE__ */ jsx("span", { className: "typography-description-12px-regular text-content-primary", children: stepsLabel })
|
|
68
68
|
] }),
|
|
69
69
|
/* @__PURE__ */ jsx(
|
|
70
70
|
"div",
|
|
@@ -91,9 +91,9 @@ const ProgressBar = React.forwardRef(
|
|
|
91
91
|
showFooter && /* @__PURE__ */ jsxs("div", { className: "flex w-full items-center justify-between", children: [
|
|
92
92
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
93
93
|
leftIcon != null && /* @__PURE__ */ jsx("span", { className: "flex size-5 items-center justify-center", "aria-hidden": "true", children: leftIcon }),
|
|
94
|
-
helperLeft != null && /* @__PURE__ */ jsx("span", { className: "typography-
|
|
94
|
+
helperLeft != null && /* @__PURE__ */ jsx("span", { className: "typography-description-12px-regular text-content-primary", children: helperLeft })
|
|
95
95
|
] }),
|
|
96
|
-
helperRight != null && /* @__PURE__ */ jsx("span", { className: "typography-
|
|
96
|
+
helperRight != null && /* @__PURE__ */ jsx("span", { className: "typography-description-12px-regular text-content-primary", children: helperRight })
|
|
97
97
|
] })
|
|
98
98
|
] });
|
|
99
99
|
}
|