@fanvue/ui 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +5 -1
  2. package/dist/cjs/components/Alert/Alert.cjs +19 -3
  3. package/dist/cjs/components/Alert/Alert.cjs.map +1 -1
  4. package/dist/cjs/components/Button/Button.cjs +2 -2
  5. package/dist/cjs/components/Button/Button.cjs.map +1 -1
  6. package/dist/cjs/components/Checkbox/Checkbox.cjs +7 -0
  7. package/dist/cjs/components/Checkbox/Checkbox.cjs.map +1 -1
  8. package/dist/cjs/components/Chip/Chip.cjs +1 -1
  9. package/dist/cjs/components/Chip/Chip.cjs.map +1 -1
  10. package/dist/cjs/components/DatePicker/DatePicker.cjs +1 -1
  11. package/dist/cjs/components/DatePicker/DatePicker.cjs.map +1 -1
  12. package/dist/cjs/components/IconButton/IconButton.cjs +23 -19
  13. package/dist/cjs/components/IconButton/IconButton.cjs.map +1 -1
  14. package/dist/cjs/components/Icons/EyeIcon.cjs +63 -0
  15. package/dist/cjs/components/Icons/EyeIcon.cjs.map +1 -0
  16. package/dist/cjs/components/Pagination/Pagination.cjs +11 -5
  17. package/dist/cjs/components/Pagination/Pagination.cjs.map +1 -1
  18. package/dist/cjs/components/Snackbar/Snackbar.cjs +12 -5
  19. package/dist/cjs/components/Snackbar/Snackbar.cjs.map +1 -1
  20. package/dist/cjs/components/SwitchField/SwitchField.cjs +6 -4
  21. package/dist/cjs/components/SwitchField/SwitchField.cjs.map +1 -1
  22. package/dist/cjs/components/Tabs/TabsTrigger.cjs +1 -0
  23. package/dist/cjs/components/Tabs/TabsTrigger.cjs.map +1 -1
  24. package/dist/cjs/components/TextField/TextField.cjs +164 -0
  25. package/dist/cjs/components/TextField/TextField.cjs.map +1 -0
  26. package/dist/cjs/components/Toast/Toast.cjs +4 -3
  27. package/dist/cjs/components/Toast/Toast.cjs.map +1 -1
  28. package/dist/cjs/components/Tooltip/Tooltip.cjs +50 -0
  29. package/dist/cjs/components/Tooltip/Tooltip.cjs.map +1 -0
  30. package/dist/cjs/index.cjs +9 -0
  31. package/dist/cjs/index.cjs.map +1 -1
  32. package/dist/components/Alert/Alert.mjs +19 -3
  33. package/dist/components/Alert/Alert.mjs.map +1 -1
  34. package/dist/components/Button/Button.mjs +2 -2
  35. package/dist/components/Button/Button.mjs.map +1 -1
  36. package/dist/components/Checkbox/Checkbox.mjs +7 -0
  37. package/dist/components/Checkbox/Checkbox.mjs.map +1 -1
  38. package/dist/components/Chip/Chip.mjs +1 -1
  39. package/dist/components/Chip/Chip.mjs.map +1 -1
  40. package/dist/components/DatePicker/DatePicker.mjs +1 -1
  41. package/dist/components/DatePicker/DatePicker.mjs.map +1 -1
  42. package/dist/components/IconButton/IconButton.mjs +23 -19
  43. package/dist/components/IconButton/IconButton.mjs.map +1 -1
  44. package/dist/components/Icons/EyeIcon.mjs +46 -0
  45. package/dist/components/Icons/EyeIcon.mjs.map +1 -0
  46. package/dist/components/Pagination/Pagination.mjs +11 -5
  47. package/dist/components/Pagination/Pagination.mjs.map +1 -1
  48. package/dist/components/Snackbar/Snackbar.mjs +12 -5
  49. package/dist/components/Snackbar/Snackbar.mjs.map +1 -1
  50. package/dist/components/SwitchField/SwitchField.mjs +6 -4
  51. package/dist/components/SwitchField/SwitchField.mjs.map +1 -1
  52. package/dist/components/Tabs/TabsTrigger.mjs +1 -0
  53. package/dist/components/Tabs/TabsTrigger.mjs.map +1 -1
  54. package/dist/components/TextField/TextField.mjs +147 -0
  55. package/dist/components/TextField/TextField.mjs.map +1 -0
  56. package/dist/components/Toast/Toast.mjs +4 -3
  57. package/dist/components/Toast/Toast.mjs.map +1 -1
  58. package/dist/components/Tooltip/Tooltip.mjs +32 -0
  59. package/dist/components/Tooltip/Tooltip.mjs.map +1 -0
  60. package/dist/index.d.ts +57 -0
  61. package/dist/index.mjs +9 -0
  62. package/dist/index.mjs.map +1 -1
  63. package/dist/styles/theme.css +21 -21
  64. package/package.json +3 -2
package/README.md CHANGED
@@ -5,9 +5,13 @@ React component library built with Tailwind CSS for the Fanvue ecosystem.
5
5
  [![CI](https://github.com/fanvue/fanv-ui/actions/workflows/ci.yml/badge.svg)](https://github.com/fanvue/fanv-ui/actions/workflows/ci.yml)
6
6
  [![Chromatic](https://github.com/fanvue/fanv-ui/actions/workflows/chromatic.yml/badge.svg)](https://github.com/fanvue/fanv-ui/actions/workflows/chromatic.yml)
7
7
  [![npm version](https://img.shields.io/npm/v/@fanvue/ui.svg)](https://www.npmjs.com/package/@fanvue/ui)
8
+ [![npm downloads](https://img.shields.io/npm/dm/@fanvue/ui.svg)](https://www.npmjs.com/package/@fanvue/ui)
9
+ [![bundle size](https://img.shields.io/bundlephobia/minzip/@fanvue/ui)](https://bundlephobia.com/package/@fanvue/ui)
8
10
  [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
9
11
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue.svg)](https://www.typescriptlang.org/)
10
- [![Storybook](https://img.shields.io/badge/Storybook-ff4785.svg?logo=storybook&logoColor=white)](https://github.com/fanvue/fanv-ui)
12
+ [![Storybook](https://img.shields.io/badge/Storybook-ff4785.svg?logo=storybook&logoColor=white)](https://697a1b6dd4dad73ee9c0e5f5-qhmsdilfrq.chromatic.com/)
13
+ [![Showcase](https://img.shields.io/badge/Showcase-GitHub%20Pages-brightgreen.svg?logo=github)](https://fanvue.github.io/fanv-ui/)
14
+ [![GitHub](https://img.shields.io/github/stars/fanvue/fanv-ui?style=social)](https://github.com/fanvue/fanv-ui)
11
15
 
12
16
  ## Features
13
17
 
@@ -23,8 +23,24 @@ function _interopNamespaceDefault(e) {
23
23
  return Object.freeze(n);
24
24
  }
25
25
  const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
26
+ const CLOSE_BUTTON_CLASSES = {
27
+ info: "hover:bg-info-500/10 text-info-500 motion-safe:transition-colors motion-safe:duration-150",
28
+ success: "hover:bg-success-500/10 text-success-500 motion-safe:transition-colors motion-safe:duration-150",
29
+ warning: "hover:bg-warning-500/10 text-warning-500 motion-safe:transition-colors motion-safe:duration-150",
30
+ error: "hover:bg-error-500/10 text-error-500 motion-safe:transition-colors motion-safe:duration-150"
31
+ };
26
32
  const Alert = React__namespace.forwardRef(
27
- ({ className, variant = "info", title, icon, closable = false, onClose, children, ...props }, ref) => {
33
+ ({
34
+ className,
35
+ variant = "info",
36
+ title,
37
+ icon,
38
+ closable = false,
39
+ onClose,
40
+ closeLabel = "Close alert",
41
+ children,
42
+ ...props
43
+ }, ref) => {
28
44
  return /* @__PURE__ */ jsxRuntime.jsxs(
29
45
  "div",
30
46
  {
@@ -57,8 +73,8 @@ const Alert = React__namespace.forwardRef(
57
73
  variant: "tertiary",
58
74
  size: "24",
59
75
  onClick: onClose,
60
- className: "self-start",
61
- "aria-label": "Close alert",
76
+ className: cn.cn("self-start", CLOSE_BUTTON_CLASSES[variant]),
77
+ "aria-label": closeLabel,
62
78
  children: /* @__PURE__ */ jsxRuntime.jsx(CrossIcon.CrossIcon, {})
63
79
  }
64
80
  )
@@ -1 +1 @@
1
- {"version":3,"file":"Alert.cjs","sources":["../../../../src/components/Alert/Alert.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Button } from \"../Button/Button\";\nimport { CrossIcon } from \"../Icons/CrossIcon\";\n\nexport type AlertVariant = \"info\" | \"success\" | \"warning\" | \"error\";\n\nexport interface AlertProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Visual style variant of the alert (matches Figma \"Variant\" property) */\n variant?: AlertVariant;\n /** Optional title text (bold) */\n title?: string;\n /** Left icon element */\n icon?: React.ReactNode;\n /** Show close button */\n closable?: boolean;\n /** Callback when close button is clicked */\n onClose?: () => void;\n}\n\nexport const Alert = React.forwardRef<HTMLDivElement, AlertProps>(\n (\n { className, variant = \"info\", title, icon, closable = false, onClose, children, ...props },\n ref,\n ) => {\n return (\n <div\n ref={ref}\n role=\"alert\"\n data-testid=\"alert\"\n className={cn(\n \"grid gap-x-3 rounded-lg p-4 text-sm leading-[18px]\",\n icon && closable && \"grid-cols-[auto_1fr_auto]\",\n icon && !closable && \"grid-cols-[auto_1fr]\",\n !icon && closable && \"grid-cols-[1fr_auto]\",\n !icon && !closable && \"grid-cols-[1fr]\",\n title && children ? \"items-start\" : \"items-center\",\n variant === \"info\" && \"bg-info-50 text-info-500\",\n variant === \"success\" && \"bg-success-50 text-success-500\",\n variant === \"warning\" && \"bg-warning-50 text-warning-500\",\n variant === \"error\" && \"bg-error-50 text-error-500\",\n className,\n )}\n {...props}\n >\n {icon && (\n <span className=\"flex shrink-0 items-start\" aria-hidden=\"true\">\n {icon}\n </span>\n )}\n\n <div className=\"flex min-w-0 flex-col gap-2\">\n {title && <div className=\"typography-body-2-semibold text-body-100\">{title}</div>}\n <div className=\"typography-body-2-regular text-body-200\">{children}</div>\n </div>\n\n {closable && (\n <Button\n variant=\"tertiary\"\n size=\"24\"\n onClick={onClose}\n className=\"self-start\"\n aria-label=\"Close alert\"\n >\n <CrossIcon />\n </Button>\n )}\n </div>\n );\n },\n);\n\nAlert.displayName = \"Alert\";\n"],"names":["React","jsxs","cn","jsx","Button","CrossIcon"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAoBO,MAAM,QAAQA,iBAAM;AAAA,EACzB,CACE,EAAE,WAAW,UAAU,QAAQ,OAAO,MAAM,WAAW,OAAO,SAAS,UAAU,GAAG,MAAA,GACpF,QACG;AACH,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL,eAAY;AAAA,QACZ,WAAWC,GAAAA;AAAAA,UACT;AAAA,UACA,QAAQ,YAAY;AAAA,UACpB,QAAQ,CAAC,YAAY;AAAA,UACrB,CAAC,QAAQ,YAAY;AAAA,UACrB,CAAC,QAAQ,CAAC,YAAY;AAAA,UACtB,SAAS,WAAW,gBAAgB;AAAA,UACpC,YAAY,UAAU;AAAA,UACtB,YAAY,aAAa;AAAA,UACzB,YAAY,aAAa;AAAA,UACzB,YAAY,WAAW;AAAA,UACvB;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEH,UAAA;AAAA,UAAA,uCACE,QAAA,EAAK,WAAU,6BAA4B,eAAY,QACrD,UAAA,MACH;AAAA,UAGFD,2BAAAA,KAAC,OAAA,EAAI,WAAU,+BACZ,UAAA;AAAA,YAAA,SAASE,2BAAAA,IAAC,OAAA,EAAI,WAAU,4CAA4C,UAAA,OAAM;AAAA,YAC3EA,2BAAAA,IAAC,OAAA,EAAI,WAAU,2CAA2C,SAAA,CAAS;AAAA,UAAA,GACrE;AAAA,UAEC,YACCA,2BAAAA;AAAAA,YAACC,OAAAA;AAAAA,YAAA;AAAA,cACC,SAAQ;AAAA,cACR,MAAK;AAAA,cACL,SAAS;AAAA,cACT,WAAU;AAAA,cACV,cAAW;AAAA,cAEX,yCAACC,UAAAA,WAAA,CAAA,CAAU;AAAA,YAAA;AAAA,UAAA;AAAA,QACb;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEA,MAAM,cAAc;;"}
1
+ {"version":3,"file":"Alert.cjs","sources":["../../../../src/components/Alert/Alert.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Button } from \"../Button/Button\";\nimport { CrossIcon } from \"../Icons/CrossIcon\";\n\nexport type AlertVariant = \"info\" | \"success\" | \"warning\" | \"error\";\n\nexport interface AlertProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Visual style variant of the alert (matches Figma \"Variant\" property) */\n variant?: AlertVariant;\n /** Optional title text (bold) */\n title?: string;\n /** Left icon element */\n icon?: React.ReactNode;\n /** Show close button */\n closable?: boolean;\n /** Callback when close button is clicked */\n onClose?: () => void;\n /** Accessible label for the close button. @default \"Close alert\" */\n closeLabel?: string;\n}\n\nconst CLOSE_BUTTON_CLASSES: Record<AlertVariant, string> = {\n info: \"hover:bg-info-500/10 text-info-500 motion-safe:transition-colors motion-safe:duration-150\",\n success:\n \"hover:bg-success-500/10 text-success-500 motion-safe:transition-colors motion-safe:duration-150\",\n warning:\n \"hover:bg-warning-500/10 text-warning-500 motion-safe:transition-colors motion-safe:duration-150\",\n error:\n \"hover:bg-error-500/10 text-error-500 motion-safe:transition-colors motion-safe:duration-150\",\n};\n\nexport const Alert = React.forwardRef<HTMLDivElement, AlertProps>(\n (\n {\n className,\n variant = \"info\",\n title,\n icon,\n closable = false,\n onClose,\n closeLabel = \"Close alert\",\n children,\n ...props\n },\n ref,\n ) => {\n return (\n <div\n ref={ref}\n role=\"alert\"\n data-testid=\"alert\"\n className={cn(\n \"grid gap-x-3 rounded-lg p-4 text-sm leading-[18px]\",\n icon && closable && \"grid-cols-[auto_1fr_auto]\",\n icon && !closable && \"grid-cols-[auto_1fr]\",\n !icon && closable && \"grid-cols-[1fr_auto]\",\n !icon && !closable && \"grid-cols-[1fr]\",\n title && children ? \"items-start\" : \"items-center\",\n variant === \"info\" && \"bg-info-50 text-info-500\",\n variant === \"success\" && \"bg-success-50 text-success-500\",\n variant === \"warning\" && \"bg-warning-50 text-warning-500\",\n variant === \"error\" && \"bg-error-50 text-error-500\",\n className,\n )}\n {...props}\n >\n {icon && (\n <span className=\"flex shrink-0 items-start\" aria-hidden=\"true\">\n {icon}\n </span>\n )}\n\n <div className=\"flex min-w-0 flex-col gap-2\">\n {title && <div className=\"typography-body-2-semibold text-body-100\">{title}</div>}\n <div className=\"typography-body-2-regular text-body-200\">{children}</div>\n </div>\n\n {closable && (\n <Button\n variant=\"tertiary\"\n size=\"24\"\n onClick={onClose}\n className={cn(\"self-start\", CLOSE_BUTTON_CLASSES[variant])}\n aria-label={closeLabel}\n >\n <CrossIcon />\n </Button>\n )}\n </div>\n );\n },\n);\n\nAlert.displayName = \"Alert\";\n"],"names":["React","jsxs","cn","jsx","Button","CrossIcon"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAsBA,MAAM,uBAAqD;AAAA,EACzD,MAAM;AAAA,EACN,SACE;AAAA,EACF,SACE;AAAA,EACF,OACE;AACJ;AAEO,MAAM,QAAQA,iBAAM;AAAA,EACzB,CACE;AAAA,IACE;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACA,aAAa;AAAA,IACb;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QACG;AACH,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL,eAAY;AAAA,QACZ,WAAWC,GAAAA;AAAAA,UACT;AAAA,UACA,QAAQ,YAAY;AAAA,UACpB,QAAQ,CAAC,YAAY;AAAA,UACrB,CAAC,QAAQ,YAAY;AAAA,UACrB,CAAC,QAAQ,CAAC,YAAY;AAAA,UACtB,SAAS,WAAW,gBAAgB;AAAA,UACpC,YAAY,UAAU;AAAA,UACtB,YAAY,aAAa;AAAA,UACzB,YAAY,aAAa;AAAA,UACzB,YAAY,WAAW;AAAA,UACvB;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEH,UAAA;AAAA,UAAA,uCACE,QAAA,EAAK,WAAU,6BAA4B,eAAY,QACrD,UAAA,MACH;AAAA,UAGFD,2BAAAA,KAAC,OAAA,EAAI,WAAU,+BACZ,UAAA;AAAA,YAAA,SAASE,2BAAAA,IAAC,OAAA,EAAI,WAAU,4CAA4C,UAAA,OAAM;AAAA,YAC3EA,2BAAAA,IAAC,OAAA,EAAI,WAAU,2CAA2C,SAAA,CAAS;AAAA,UAAA,GACrE;AAAA,UAEC,YACCA,2BAAAA;AAAAA,YAACC,OAAAA;AAAAA,YAAA;AAAA,cACC,SAAQ;AAAA,cACR,MAAK;AAAA,cACL,SAAS;AAAA,cACT,WAAWF,GAAAA,GAAG,cAAc,qBAAqB,OAAO,CAAC;AAAA,cACzD,cAAY;AAAA,cAEZ,yCAACG,UAAAA,WAAA,CAAA,CAAU;AAAA,YAAA;AAAA,UAAA;AAAA,QACb;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEA,MAAM,cAAc;;"}
@@ -126,7 +126,7 @@ const Button = React__namespace.forwardRef(
126
126
  const Comp = asChild ? reactSlot.Slot : "button";
127
127
  const isDisabled = disabled || loading;
128
128
  const iconSizeClass = ICON_SIZE_CLASS[size];
129
- const buttonSpecificProps = !asChild ? { "data-testid": "button", disabled: isDisabled } : isDisabled ? { "aria-disabled": true } : {};
129
+ const buttonSpecificProps = !asChild ? { type: "button", "data-testid": "button", disabled: isDisabled } : isDisabled ? { "aria-disabled": true } : {};
130
130
  const loadingLabelProps = loading && asChild ? { "aria-label": getTextContent(children) } : {};
131
131
  const content = renderContent({
132
132
  loading,
@@ -150,7 +150,7 @@ const Button = React__namespace.forwardRef(
150
150
  // Base styles
151
151
  "inline-flex cursor-pointer items-center justify-center gap-2 rounded-full transition-colors",
152
152
  // Focus ring
153
- "focus:shadow-focus focus-visible:outline-none",
153
+ "focus-visible:shadow-focus-ring focus-visible:outline-none",
154
154
  // Disabled state
155
155
  "disabled:pointer-events-none disabled:opacity-50",
156
156
  "aria-disabled:pointer-events-none aria-disabled:opacity-50",
@@ -1 +1 @@
1
- {"version":3,"file":"Button.cjs","sources":["../../../../src/components/Button/Button.tsx"],"sourcesContent":["import { Slot } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { SpinnerIcon } from \"../Icons/SpinnerIcon\";\n\nexport type ButtonVariant =\n | \"primary\"\n | \"secondary\"\n | \"tertiary\"\n | \"link\"\n | \"brand\"\n | \"destructive\"\n | \"white\"\n | \"tertiaryDestructive\"\n | \"text\";\n\nexport type ButtonSize = \"48\" | \"40\" | \"32\" | \"24\";\n\nexport interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Visual style variant of the button */\n variant?: ButtonVariant;\n /** Size of the button in pixels */\n size?: ButtonSize;\n /** Left icon element */\n leftIcon?: React.ReactNode;\n /** Right icon element */\n rightIcon?: React.ReactNode;\n /** Show loading spinner */\n loading?: boolean;\n /** Render as a different element using Radix Slot */\n asChild?: boolean;\n /** Old price shown with strikethrough before the current price */\n discount?: string;\n /** Current price shown inside the button after the label/icons */\n price?: string;\n}\n\nconst SIZE_CLASSES: Record<ButtonSize, string> = {\n \"48\": \"h-12 px-4 py-3 typography-button-large\",\n \"40\": \"h-10 px-4 py-2 typography-button-small\",\n \"32\": \"h-8 px-3 py-2 typography-body-2-semibold\",\n \"24\": \"h-6 px-0 py-1 typography-body-2-semibold\",\n};\n\nconst ICON_SIZE_CLASS: Record<ButtonSize, string> = {\n \"48\": \"size-5\",\n \"40\": \"size-5\",\n \"32\": \"size-4\",\n \"24\": \"size-3.5\",\n};\n\nconst VARIANT_CLASSES: Record<ButtonVariant, string> = {\n primary:\n \"bg-neutral-400 text-body-300 hover:bg-brand-green-500 hover:text-body-black-solid-constant\",\n secondary:\n \"border-body-100 border border-1 border-body-100 bg-transparent text-body-100 hover:bg-brand-green-50\",\n tertiary: \"bg-transparent text-body-100 hover:bg-brand-green-50\",\n link: \"bg-transparent text-body-100 underline decoration-solid hover:bg-brand-green-50\",\n brand: \"bg-brand-green-500 text-body-black-solid-constant hover:bg-brand-pink-500\",\n destructive:\n \"bg-error-500 text-body-white-solid-constant hover:bg-background-solid dark:hover:bg-background-white-solid-constant dark:hover:text-error-500\",\n white:\n \"bg-background-white-solid-constant text-body-black-solid-constant hover:bg-brand-green-500\",\n tertiaryDestructive: \"bg-transparent text-error-500 hover:bg-error-50\",\n text: \"bg-transparent text-body-100 hover:underline\",\n};\n\n/** Recursively extract text content from React nodes for accessible labels */\nfunction getTextContent(node: React.ReactNode): string | undefined {\n if (typeof node === \"string\") return node;\n if (typeof node === \"number\") return String(node);\n if (React.isValidElement(node)) {\n return getTextContent((node.props as { children?: React.ReactNode }).children);\n }\n if (Array.isArray(node)) {\n const text = node.map(getTextContent).filter(Boolean).join(\"\");\n return text || undefined;\n }\n return undefined;\n}\n\nconst LoadingSpinner = ({ size }: { size: ButtonSize }) => {\n return (\n <span className=\"animate-spin\" aria-hidden=\"true\">\n <SpinnerIcon className={ICON_SIZE_CLASS[size]}>\n <title>Loading</title>\n </SpinnerIcon>\n </span>\n );\n};\n\nfunction renderContent({\n loading,\n asChild,\n children,\n size,\n leftIcon,\n rightIcon,\n iconSizeClass,\n discount,\n price,\n}: {\n loading: boolean;\n asChild: boolean;\n children: React.ReactNode;\n size: ButtonSize;\n leftIcon: React.ReactNode;\n rightIcon: React.ReactNode;\n iconSizeClass: string;\n discount?: string;\n price?: string;\n}) {\n if (loading) {\n // When asChild, clone the child element with spinner content instead of\n // wrapping in sr-only span (which would nest interactive elements)\n if (asChild && React.isValidElement(children)) {\n return React.cloneElement(\n children as React.ReactElement<{ children?: React.ReactNode }>,\n undefined,\n <LoadingSpinner size={size} />,\n );\n }\n return (\n <>\n <LoadingSpinner size={size} />\n <span className=\"sr-only\">{children}</span>\n </>\n );\n }\n\n if (asChild) return children;\n\n return (\n <>\n {leftIcon && (\n <span\n className={cn(\"flex shrink-0 items-center justify-center\", iconSizeClass)}\n aria-hidden=\"true\"\n >\n {leftIcon}\n </span>\n )}\n {children}\n {rightIcon && (\n <span\n className={cn(\"flex shrink-0 items-center justify-center\", iconSizeClass)}\n aria-hidden=\"true\"\n >\n {rightIcon}\n </span>\n )}\n {discount != null && (\n <span className=\"typography-body-1-regular line-through\" aria-hidden=\"true\">\n {discount}\n </span>\n )}\n {price != null && <span aria-hidden=\"true\">{price}</span>}\n </>\n );\n}\n\nexport const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n className,\n variant = \"primary\",\n size = \"48\",\n leftIcon,\n rightIcon,\n loading = false,\n asChild = false,\n disabled,\n children,\n discount,\n price,\n ...props\n },\n ref,\n ) => {\n const Comp = asChild ? Slot : \"button\";\n const isDisabled = disabled || loading;\n const iconSizeClass = ICON_SIZE_CLASS[size];\n\n const buttonSpecificProps = !asChild\n ? { \"data-testid\": \"button\", disabled: isDisabled }\n : isDisabled\n ? { \"aria-disabled\": true }\n : {};\n\n // When asChild + loading, extract text from children for aria-label since we\n // can't wrap element children in an sr-only span (creates invalid nested markup)\n const loadingLabelProps = loading && asChild ? { \"aria-label\": getTextContent(children) } : {};\n\n const content = renderContent({\n loading,\n asChild,\n children,\n size,\n leftIcon,\n rightIcon,\n iconSizeClass,\n discount,\n price,\n });\n\n return (\n <Comp\n ref={ref}\n {...buttonSpecificProps}\n aria-busy={loading}\n {...loadingLabelProps}\n className={cn(\n // Base styles\n \"inline-flex cursor-pointer items-center justify-center gap-2 rounded-full transition-colors\",\n // Focus ring\n \"focus:shadow-focus focus-visible:outline-none\",\n // Disabled state\n \"disabled:pointer-events-none disabled:opacity-50\",\n \"aria-disabled:pointer-events-none aria-disabled:opacity-50\",\n // Size styles\n SIZE_CLASSES[size],\n // Variant styles\n VARIANT_CLASSES[variant],\n // Manual CSS overrides\n className,\n )}\n {...props}\n >\n {content}\n </Comp>\n );\n },\n);\n\nButton.displayName = \"Button\";\n"],"names":["React","jsx","SpinnerIcon","jsxs","Fragment","cn","Slot"],"mappings":"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"}
1
+ {"version":3,"file":"Button.cjs","sources":["../../../../src/components/Button/Button.tsx"],"sourcesContent":["import { Slot } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { SpinnerIcon } from \"../Icons/SpinnerIcon\";\n\nexport type ButtonVariant =\n | \"primary\"\n | \"secondary\"\n | \"tertiary\"\n | \"link\"\n | \"brand\"\n | \"destructive\"\n | \"white\"\n | \"tertiaryDestructive\"\n | \"text\";\n\nexport type ButtonSize = \"48\" | \"40\" | \"32\" | \"24\";\n\nexport interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Visual style variant of the button */\n variant?: ButtonVariant;\n /** Size of the button in pixels */\n size?: ButtonSize;\n /** Left icon element */\n leftIcon?: React.ReactNode;\n /** Right icon element */\n rightIcon?: React.ReactNode;\n /** Show loading spinner */\n loading?: boolean;\n /** Render as a different element using Radix Slot */\n asChild?: boolean;\n /** Old price shown with strikethrough before the current price */\n discount?: string;\n /** Current price shown inside the button after the label/icons */\n price?: string;\n}\n\nconst SIZE_CLASSES: Record<ButtonSize, string> = {\n \"48\": \"h-12 px-4 py-3 typography-button-large\",\n \"40\": \"h-10 px-4 py-2 typography-button-small\",\n \"32\": \"h-8 px-3 py-2 typography-body-2-semibold\",\n \"24\": \"h-6 px-0 py-1 typography-body-2-semibold\",\n};\n\nconst ICON_SIZE_CLASS: Record<ButtonSize, string> = {\n \"48\": \"size-5\",\n \"40\": \"size-5\",\n \"32\": \"size-4\",\n \"24\": \"size-3.5\",\n};\n\nconst VARIANT_CLASSES: Record<ButtonVariant, string> = {\n primary:\n \"bg-neutral-400 text-body-300 hover:bg-brand-green-500 hover:text-body-black-solid-constant\",\n secondary:\n \"border-body-100 border border-1 border-body-100 bg-transparent text-body-100 hover:bg-brand-green-50\",\n tertiary: \"bg-transparent text-body-100 hover:bg-brand-green-50\",\n link: \"bg-transparent text-body-100 underline decoration-solid hover:bg-brand-green-50\",\n brand: \"bg-brand-green-500 text-body-black-solid-constant hover:bg-brand-pink-500\",\n destructive:\n \"bg-error-500 text-body-white-solid-constant hover:bg-background-solid dark:hover:bg-background-white-solid-constant dark:hover:text-error-500\",\n white:\n \"bg-background-white-solid-constant text-body-black-solid-constant hover:bg-brand-green-500\",\n tertiaryDestructive: \"bg-transparent text-error-500 hover:bg-error-50\",\n text: \"bg-transparent text-body-100 hover:underline\",\n};\n\n/** Recursively extract text content from React nodes for accessible labels */\nfunction getTextContent(node: React.ReactNode): string | undefined {\n if (typeof node === \"string\") return node;\n if (typeof node === \"number\") return String(node);\n if (React.isValidElement(node)) {\n return getTextContent((node.props as { children?: React.ReactNode }).children);\n }\n if (Array.isArray(node)) {\n const text = node.map(getTextContent).filter(Boolean).join(\"\");\n return text || undefined;\n }\n return undefined;\n}\n\nconst LoadingSpinner = ({ size }: { size: ButtonSize }) => {\n return (\n <span className=\"animate-spin\" aria-hidden=\"true\">\n <SpinnerIcon className={ICON_SIZE_CLASS[size]}>\n <title>Loading</title>\n </SpinnerIcon>\n </span>\n );\n};\n\nfunction renderContent({\n loading,\n asChild,\n children,\n size,\n leftIcon,\n rightIcon,\n iconSizeClass,\n discount,\n price,\n}: {\n loading: boolean;\n asChild: boolean;\n children: React.ReactNode;\n size: ButtonSize;\n leftIcon: React.ReactNode;\n rightIcon: React.ReactNode;\n iconSizeClass: string;\n discount?: string;\n price?: string;\n}) {\n if (loading) {\n // When asChild, clone the child element with spinner content instead of\n // wrapping in sr-only span (which would nest interactive elements)\n if (asChild && React.isValidElement(children)) {\n return React.cloneElement(\n children as React.ReactElement<{ children?: React.ReactNode }>,\n undefined,\n <LoadingSpinner size={size} />,\n );\n }\n return (\n <>\n <LoadingSpinner size={size} />\n <span className=\"sr-only\">{children}</span>\n </>\n );\n }\n\n if (asChild) return children;\n\n return (\n <>\n {leftIcon && (\n <span\n className={cn(\"flex shrink-0 items-center justify-center\", iconSizeClass)}\n aria-hidden=\"true\"\n >\n {leftIcon}\n </span>\n )}\n {children}\n {rightIcon && (\n <span\n className={cn(\"flex shrink-0 items-center justify-center\", iconSizeClass)}\n aria-hidden=\"true\"\n >\n {rightIcon}\n </span>\n )}\n {discount != null && (\n <span className=\"typography-body-1-regular line-through\" aria-hidden=\"true\">\n {discount}\n </span>\n )}\n {price != null && <span aria-hidden=\"true\">{price}</span>}\n </>\n );\n}\n\nexport const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n className,\n variant = \"primary\",\n size = \"48\",\n leftIcon,\n rightIcon,\n loading = false,\n asChild = false,\n disabled,\n children,\n discount,\n price,\n ...props\n },\n ref,\n ) => {\n const Comp = asChild ? Slot : \"button\";\n const isDisabled = disabled || loading;\n const iconSizeClass = ICON_SIZE_CLASS[size];\n\n const buttonSpecificProps = !asChild\n ? { type: \"button\" as const, \"data-testid\": \"button\", disabled: isDisabled }\n : isDisabled\n ? { \"aria-disabled\": true }\n : {};\n\n // When asChild + loading, extract text from children for aria-label since we\n // can't wrap element children in an sr-only span (creates invalid nested markup)\n const loadingLabelProps = loading && asChild ? { \"aria-label\": getTextContent(children) } : {};\n\n const content = renderContent({\n loading,\n asChild,\n children,\n size,\n leftIcon,\n rightIcon,\n iconSizeClass,\n discount,\n price,\n });\n\n return (\n <Comp\n ref={ref}\n {...buttonSpecificProps}\n aria-busy={loading}\n {...loadingLabelProps}\n className={cn(\n // Base styles\n \"inline-flex cursor-pointer items-center justify-center gap-2 rounded-full transition-colors\",\n // Focus ring\n \"focus-visible:shadow-focus-ring focus-visible:outline-none\",\n // Disabled state\n \"disabled:pointer-events-none disabled:opacity-50\",\n \"aria-disabled:pointer-events-none aria-disabled:opacity-50\",\n // Size styles\n SIZE_CLASSES[size],\n // Variant styles\n VARIANT_CLASSES[variant],\n // Manual CSS overrides\n className,\n )}\n {...props}\n >\n {content}\n </Comp>\n );\n },\n);\n\nButton.displayName = \"Button\";\n"],"names":["React","jsx","SpinnerIcon","jsxs","Fragment","cn","Slot"],"mappings":"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"}
@@ -30,6 +30,13 @@ const Checkbox = React__namespace.forwardRef(
30
30
  const id = React__namespace.useId();
31
31
  const helperTextId = helperText ? `${id}-helper` : void 0;
32
32
  const hasLabel = Boolean(label || helperText);
33
+ if (process.env.NODE_ENV !== "production") {
34
+ if (!label && !props["aria-label"] && !props["aria-labelledby"]) {
35
+ console.warn(
36
+ "Checkbox: No accessible name provided. Add a `label`, `aria-label`, or `aria-labelledby` prop so screen readers can announce this checkbox."
37
+ );
38
+ }
39
+ }
33
40
  const inputRef = React__namespace.useRef(null);
34
41
  React__namespace.useImperativeHandle(ref, () => inputRef.current);
35
42
  const handleCheckedChange = (value) => {
@@ -1 +1 @@
1
- {"version":3,"file":"Checkbox.cjs","sources":["../../../../src/components/Checkbox/Checkbox.tsx"],"sourcesContent":["import * as CheckboxPrimitive from \"@radix-ui/react-checkbox\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { CheckIcon } from \"../Icons/CheckIcon\";\nimport { MinusIcon } from \"../Icons/MinusIcon\";\n\nexport type CheckboxSize = \"default\" | \"small\";\n\nexport interface CheckboxProps\n extends Omit<React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>, \"asChild\"> {\n /** Size variant for label and helper text */\n size?: CheckboxSize;\n /** Label text displayed next to the checkbox */\n label?: string;\n /** Helper text displayed below the label */\n helperText?: string;\n}\n\n/**\n * The ref type is intentionally `HTMLInputElement` (not `HTMLButtonElement`) for form library\n * compatibility. Libraries like react-hook-form call `register()` which expects an `HTMLInputElement`\n * ref. A hidden `<input>` is synced to the Radix checkbox state via `useImperativeHandle`.\n */\nexport const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(\n ({ className, size = \"default\", label, helperText, disabled, name, ...props }, ref) => {\n const id = React.useId();\n const helperTextId = helperText ? `${id}-helper` : undefined;\n const hasLabel = Boolean(label || helperText);\n\n // Hidden input for form library compatibility (e.g. react-hook-form register)\n const inputRef = React.useRef<HTMLInputElement>(null);\n React.useImperativeHandle(ref, () => inputRef.current as HTMLInputElement);\n\n const handleCheckedChange = (value: boolean | \"indeterminate\") => {\n const checked = value === true;\n if (inputRef.current) {\n inputRef.current.checked = checked;\n inputRef.current.dispatchEvent(new Event(\"change\", { bubbles: true }));\n }\n props.onCheckedChange?.(value);\n };\n\n const checkboxElement = (\n <span\n className={cn(\n \"relative inline-flex size-5 shrink-0\",\n // Alignment when used with label\n label && (helperText ? \"mt-1\" : \"mt-0.5\"),\n )}\n >\n <input\n ref={inputRef}\n type=\"checkbox\"\n name={name}\n disabled={disabled}\n aria-hidden\n tabIndex={-1}\n onChange={() => {}}\n className=\"pointer-events-none absolute size-px overflow-hidden opacity-0\"\n style={{ clip: \"rect(0,0,0,0)\" }}\n />\n <CheckboxPrimitive.Root\n id={id}\n disabled={disabled}\n aria-describedby={helperTextId}\n data-testid=\"checkbox\"\n {...props}\n onCheckedChange={handleCheckedChange}\n className={cn(\n // Base styles\n \"flex size-5 items-center justify-center rounded border-2\",\n \"transition-[border-color,background-color,color,box-shadow] duration-150\",\n // Default state\n \"border-body-100 bg-transparent text-transparent\",\n // Checked state\n \"data-[state=checked]:border-body-100 data-[state=checked]:bg-body-100 data-[state=checked]:text-body-300\",\n // Indeterminate state\n \"data-[state=indeterminate]:border-body-100 data-[state=indeterminate]:bg-body-100 data-[state=indeterminate]:text-body-300\",\n // Hover state\n \"hover:ring-2 hover:ring-brand-green-500 group-hover:ring-2 group-hover:ring-brand-green-500\",\n // Focus state\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-purple-500 focus-visible:ring-offset-2 focus-visible:ring-offset-background-inverse-solid\",\n // Disabled state\n \"disabled:cursor-not-allowed disabled:border-disabled-400 disabled:ring-0 disabled:group-hover:ring-0\",\n \"disabled:data-[state=checked]:border-disabled-400 disabled:data-[state=checked]:bg-disabled-400 disabled:data-[state=checked]:text-disabled-100\",\n !hasLabel && className,\n )}\n >\n <CheckboxPrimitive.Indicator\n forceMount\n className={cn(\n \"flex size-3 items-center justify-center text-body-300\",\n \"data-[state=unchecked]:invisible\",\n )}\n >\n {props.checked === \"indeterminate\" ? <MinusIcon /> : <CheckIcon />}\n </CheckboxPrimitive.Indicator>\n </CheckboxPrimitive.Root>\n </span>\n );\n\n if (!hasLabel) {\n return checkboxElement;\n }\n\n return (\n <div\n className={cn(\n \"inline-flex flex-col gap-0.5\",\n disabled && \"is-disabled cursor-not-allowed\",\n className,\n )}\n >\n <div className=\"group inline-flex items-start gap-2\">\n {checkboxElement}\n {label && (\n <label\n htmlFor={id}\n className={cn(\n \"cursor-pointer select-none text-body-100\",\n \"group-has-disabled:cursor-not-allowed group-has-disabled:text-disabled-100\",\n size === \"small\" ? \"typography-body-2-semibold\" : \"typography-body-1-semibold\",\n )}\n >\n {label}\n </label>\n )}\n </div>\n {helperText && (\n <span\n id={helperTextId}\n className={cn(\n \"ml-7 text-body-200\",\n \"in-[.is-disabled]:cursor-not-allowed in-[.is-disabled]:text-disabled-100\",\n size === \"small\" ? \"typography-caption-regular\" : \"typography-body-2-regular\",\n )}\n >\n {helperText}\n </span>\n )}\n </div>\n );\n },\n);\n\nCheckbox.displayName = \"Checkbox\";\n"],"names":["React","jsxs","cn","jsx","CheckboxPrimitive","MinusIcon","CheckIcon"],"mappings":"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"}
1
+ {"version":3,"file":"Checkbox.cjs","sources":["../../../../src/components/Checkbox/Checkbox.tsx"],"sourcesContent":["import * as CheckboxPrimitive from \"@radix-ui/react-checkbox\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { CheckIcon } from \"../Icons/CheckIcon\";\nimport { MinusIcon } from \"../Icons/MinusIcon\";\n\nexport type CheckboxSize = \"default\" | \"small\";\n\nexport interface CheckboxProps\n extends Omit<React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>, \"asChild\"> {\n /** Size variant for label and helper text */\n size?: CheckboxSize;\n /** Label text displayed next to the checkbox */\n label?: string;\n /** Helper text displayed below the label */\n helperText?: string;\n}\n\n/**\n * The ref type is intentionally `HTMLInputElement` (not `HTMLButtonElement`) for form library\n * compatibility. Libraries like react-hook-form call `register()` which expects an `HTMLInputElement`\n * ref. A hidden `<input>` is synced to the Radix checkbox state via `useImperativeHandle`.\n */\nexport const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(\n ({ className, size = \"default\", label, helperText, disabled, name, ...props }, ref) => {\n const id = React.useId();\n const helperTextId = helperText ? `${id}-helper` : undefined;\n const hasLabel = Boolean(label || helperText);\n\n if (process.env.NODE_ENV !== \"production\") {\n if (!label && !props[\"aria-label\"] && !props[\"aria-labelledby\"]) {\n console.warn(\n \"Checkbox: No accessible name provided. Add a `label`, `aria-label`, or `aria-labelledby` prop so screen readers can announce this checkbox.\",\n );\n }\n }\n\n // Hidden input for form library compatibility (e.g. react-hook-form register)\n const inputRef = React.useRef<HTMLInputElement>(null);\n React.useImperativeHandle(ref, () => inputRef.current as HTMLInputElement);\n\n const handleCheckedChange = (value: boolean | \"indeterminate\") => {\n const checked = value === true;\n if (inputRef.current) {\n inputRef.current.checked = checked;\n inputRef.current.dispatchEvent(new Event(\"change\", { bubbles: true }));\n }\n props.onCheckedChange?.(value);\n };\n\n const checkboxElement = (\n <span\n className={cn(\n \"relative inline-flex size-5 shrink-0\",\n // Alignment when used with label\n label && (helperText ? \"mt-1\" : \"mt-0.5\"),\n )}\n >\n <input\n ref={inputRef}\n type=\"checkbox\"\n name={name}\n disabled={disabled}\n aria-hidden\n tabIndex={-1}\n onChange={() => {}}\n className=\"pointer-events-none absolute size-px overflow-hidden opacity-0\"\n style={{ clip: \"rect(0,0,0,0)\" }}\n />\n <CheckboxPrimitive.Root\n id={id}\n disabled={disabled}\n aria-describedby={helperTextId}\n data-testid=\"checkbox\"\n {...props}\n onCheckedChange={handleCheckedChange}\n className={cn(\n // Base styles\n \"flex size-5 items-center justify-center rounded border-2\",\n \"transition-[border-color,background-color,color,box-shadow] duration-150\",\n // Default state\n \"border-body-100 bg-transparent text-transparent\",\n // Checked state\n \"data-[state=checked]:border-body-100 data-[state=checked]:bg-body-100 data-[state=checked]:text-body-300\",\n // Indeterminate state\n \"data-[state=indeterminate]:border-body-100 data-[state=indeterminate]:bg-body-100 data-[state=indeterminate]:text-body-300\",\n // Hover state\n \"hover:ring-2 hover:ring-brand-green-500 group-hover:ring-2 group-hover:ring-brand-green-500\",\n // Focus state\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-purple-500 focus-visible:ring-offset-2 focus-visible:ring-offset-background-inverse-solid\",\n // Disabled state\n \"disabled:cursor-not-allowed disabled:border-disabled-400 disabled:ring-0 disabled:group-hover:ring-0\",\n \"disabled:data-[state=checked]:border-disabled-400 disabled:data-[state=checked]:bg-disabled-400 disabled:data-[state=checked]:text-disabled-100\",\n !hasLabel && className,\n )}\n >\n <CheckboxPrimitive.Indicator\n forceMount\n className={cn(\n \"flex size-3 items-center justify-center text-body-300\",\n \"data-[state=unchecked]:invisible\",\n )}\n >\n {props.checked === \"indeterminate\" ? <MinusIcon /> : <CheckIcon />}\n </CheckboxPrimitive.Indicator>\n </CheckboxPrimitive.Root>\n </span>\n );\n\n if (!hasLabel) {\n return checkboxElement;\n }\n\n return (\n <div\n className={cn(\n \"inline-flex flex-col gap-0.5\",\n disabled && \"is-disabled cursor-not-allowed\",\n className,\n )}\n >\n <div className=\"group inline-flex items-start gap-2\">\n {checkboxElement}\n {label && (\n <label\n htmlFor={id}\n className={cn(\n \"cursor-pointer select-none text-body-100\",\n \"group-has-disabled:cursor-not-allowed group-has-disabled:text-disabled-100\",\n size === \"small\" ? \"typography-body-2-semibold\" : \"typography-body-1-semibold\",\n )}\n >\n {label}\n </label>\n )}\n </div>\n {helperText && (\n <span\n id={helperTextId}\n className={cn(\n \"ml-7 text-body-200\",\n \"in-[.is-disabled]:cursor-not-allowed in-[.is-disabled]:text-disabled-100\",\n size === \"small\" ? \"typography-caption-regular\" : \"typography-body-2-regular\",\n )}\n >\n {helperText}\n </span>\n )}\n </div>\n );\n },\n);\n\nCheckbox.displayName = \"Checkbox\";\n"],"names":["React","jsxs","cn","jsx","CheckboxPrimitive","MinusIcon","CheckIcon"],"mappings":"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"}
@@ -47,7 +47,7 @@ const Chip = React__namespace.forwardRef(
47
47
  ref,
48
48
  "data-testid": "chip",
49
49
  className: cn.cn(
50
- "typography-caption-semibold relative inline-flex items-center gap-2 px-3 transition-colors",
50
+ "typography-caption-semibold relative inline-flex items-center gap-2 px-3 motion-safe:transition-colors motion-safe:duration-150",
51
51
  // Shape
52
52
  variant === "square" ? "rounded-lg" : "rounded-full",
53
53
  // Size
@@ -1 +1 @@
1
- {"version":3,"file":"Chip.cjs","sources":["../../../../src/components/Chip/Chip.tsx"],"sourcesContent":["import { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\nexport type ChipVariant = \"rounded\" | \"square\" | \"dark\";\nexport type ChipSize = \"32\" | \"40\";\n\nexport interface ChipProps extends React.HTMLAttributes<HTMLElement> {\n /** Visual shape variant of the chip */\n variant?: ChipVariant;\n /** Size of the chip */\n size?: ChipSize;\n /** Whether the chip is in a selected state */\n selected?: boolean;\n /** Whether the chip is disabled */\n disabled?: boolean;\n /** Show left status dot */\n leftDot?: boolean;\n /** Left icon element */\n leftIcon?: React.ReactNode;\n /** Right icon element */\n rightIcon?: React.ReactNode;\n /** Notification badge content (e.g., \"99+\"). Passed as a string for i18n support. */\n notificationLabel?: string;\n /** Click handler — when provided, the chip renders as a `<button>` for accessibility */\n onClick?: React.MouseEventHandler<HTMLElement>;\n /** Render as a different element using Radix Slot */\n asChild?: boolean;\n}\n\nexport const Chip = React.forwardRef<HTMLButtonElement, ChipProps>(\n (\n {\n className,\n variant = \"rounded\",\n size = \"32\",\n selected = false,\n disabled = false,\n leftDot = false,\n leftIcon,\n rightIcon,\n notificationLabel,\n onClick,\n asChild = false,\n children,\n ...props\n },\n ref,\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Variant-heavy UI component\n ) => {\n const isInteractive = !!onClick && !asChild;\n const Comp = asChild ? Slot : isInteractive ? \"button\" : \"span\";\n const isDark = variant === \"dark\";\n\n return (\n <Comp\n ref={ref}\n data-testid=\"chip\"\n className={cn(\n \"typography-caption-semibold relative inline-flex items-center gap-2 px-3 transition-colors\",\n // Shape\n variant === \"square\" ? \"rounded-lg\" : \"rounded-full\",\n // Size\n size === \"32\" && \"h-8 py-1\",\n size === \"40\" && \"h-10 py-2.5\",\n // Variant colors\n isDark && \"bg-background-800 text-body-white-solid-constant\",\n !isDark && selected && \"bg-brand-green-50 text-neutral-400\",\n !isDark && !selected && \"bg-neutral-100 text-neutral-400\",\n // Hover\n isInteractive &&\n !disabled &&\n !isDark &&\n selected &&\n \"hover:bg-brand-green-500 hover:text-body-black-solid-constant\",\n isInteractive && !disabled && !isDark && !selected && \"hover:bg-hover-400\",\n // Focus\n \"focus-visible:shadow-focus-ring focus-visible:outline-none\",\n // Disabled\n disabled && isDark && \"pointer-events-none opacity-50\",\n disabled && !isDark && \"pointer-events-none text-neutral-300\",\n className,\n )}\n {...(isInteractive && {\n type: \"button\" as const,\n disabled,\n \"aria-pressed\": selected,\n onClick,\n })}\n {...(!isInteractive && disabled && { \"aria-disabled\": true })}\n {...(selected && { \"data-selected\": \"\" })}\n {...props}\n >\n {leftDot && <span className=\"size-2 shrink-0 rounded-full bg-current\" aria-hidden=\"true\" />}\n {leftIcon && (\n <span className=\"flex size-5 shrink-0 items-center justify-center\" aria-hidden=\"true\">\n {leftIcon}\n </span>\n )}\n <Slottable>{children}</Slottable>\n {rightIcon && (\n <span className=\"flex size-5 shrink-0 items-center justify-center\" aria-hidden=\"true\">\n {rightIcon}\n </span>\n )}\n {notificationLabel && (\n <span className=\"typography-caption-semibold absolute -top-1 -right-1 flex h-4 min-w-4 items-center justify-center rounded-full bg-body-100 px-1 text-body-300\">\n {notificationLabel}\n </span>\n )}\n </Comp>\n );\n },\n);\n\nChip.displayName = \"Chip\";\n"],"names":["React","Slot","jsxs","cn","jsx","Slottable"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AA8BO,MAAM,OAAOA,iBAAM;AAAA,EACxB,CACE;AAAA,IACE;AAAA,IACA,UAAU;AAAA,IACV,OAAO;AAAA,IACP,WAAW;AAAA,IACX,WAAW;AAAA,IACX,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QAEG;AACH,UAAM,gBAAgB,CAAC,CAAC,WAAW,CAAC;AACpC,UAAM,OAAO,UAAUC,UAAAA,OAAO,gBAAgB,WAAW;AACzD,UAAM,SAAS,YAAY;AAE3B,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,eAAY;AAAA,QACZ,WAAWC,GAAAA;AAAAA,UACT;AAAA;AAAA,UAEA,YAAY,WAAW,eAAe;AAAA;AAAA,UAEtC,SAAS,QAAQ;AAAA,UACjB,SAAS,QAAQ;AAAA;AAAA,UAEjB,UAAU;AAAA,UACV,CAAC,UAAU,YAAY;AAAA,UACvB,CAAC,UAAU,CAAC,YAAY;AAAA;AAAA,UAExB,iBACE,CAAC,YACD,CAAC,UACD,YACA;AAAA,UACF,iBAAiB,CAAC,YAAY,CAAC,UAAU,CAAC,YAAY;AAAA;AAAA,UAEtD;AAAA;AAAA,UAEA,YAAY,UAAU;AAAA,UACtB,YAAY,CAAC,UAAU;AAAA,UACvB;AAAA,QAAA;AAAA,QAED,GAAI,iBAAiB;AAAA,UACpB,MAAM;AAAA,UACN;AAAA,UACA,gBAAgB;AAAA,UAChB;AAAA,QAAA;AAAA,QAED,GAAI,CAAC,iBAAiB,YAAY,EAAE,iBAAiB,KAAA;AAAA,QACrD,GAAI,YAAY,EAAE,iBAAiB,GAAA;AAAA,QACnC,GAAG;AAAA,QAEH,UAAA;AAAA,UAAA,WAAWC,2BAAAA,IAAC,QAAA,EAAK,WAAU,2CAA0C,eAAY,QAAO;AAAA,UACxF,YACCA,2BAAAA,IAAC,QAAA,EAAK,WAAU,oDAAmD,eAAY,QAC5E,UAAA,UACH;AAAA,UAEFA,+BAACC,UAAAA,aAAW,UAAS;AAAA,UACpB,aACCD,2BAAAA,IAAC,QAAA,EAAK,WAAU,oDAAmD,eAAY,QAC5E,UAAA,WACH;AAAA,UAED,qBACCA,2BAAAA,IAAC,QAAA,EAAK,WAAU,iJACb,UAAA,kBAAA,CACH;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEA,KAAK,cAAc;;"}
1
+ {"version":3,"file":"Chip.cjs","sources":["../../../../src/components/Chip/Chip.tsx"],"sourcesContent":["import { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\nexport type ChipVariant = \"rounded\" | \"square\" | \"dark\";\nexport type ChipSize = \"32\" | \"40\";\n\nexport interface ChipProps extends React.HTMLAttributes<HTMLElement> {\n /** Visual shape variant of the chip */\n variant?: ChipVariant;\n /** Size of the chip */\n size?: ChipSize;\n /** Whether the chip is in a selected state */\n selected?: boolean;\n /** Whether the chip is disabled */\n disabled?: boolean;\n /** Show left status dot */\n leftDot?: boolean;\n /** Left icon element */\n leftIcon?: React.ReactNode;\n /** Right icon element */\n rightIcon?: React.ReactNode;\n /** Notification badge content (e.g., \"99+\"). Passed as a string for i18n support. */\n notificationLabel?: string;\n /** Click handler — when provided, the chip renders as a `<button>` for accessibility */\n onClick?: React.MouseEventHandler<HTMLElement>;\n /** Render as a different element using Radix Slot */\n asChild?: boolean;\n}\n\nexport const Chip = React.forwardRef<HTMLButtonElement, ChipProps>(\n (\n {\n className,\n variant = \"rounded\",\n size = \"32\",\n selected = false,\n disabled = false,\n leftDot = false,\n leftIcon,\n rightIcon,\n notificationLabel,\n onClick,\n asChild = false,\n children,\n ...props\n },\n ref,\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Variant-heavy UI component\n ) => {\n const isInteractive = !!onClick && !asChild;\n const Comp = asChild ? Slot : isInteractive ? \"button\" : \"span\";\n const isDark = variant === \"dark\";\n\n return (\n <Comp\n ref={ref}\n data-testid=\"chip\"\n className={cn(\n \"typography-caption-semibold relative inline-flex items-center gap-2 px-3 motion-safe:transition-colors motion-safe:duration-150\",\n // Shape\n variant === \"square\" ? \"rounded-lg\" : \"rounded-full\",\n // Size\n size === \"32\" && \"h-8 py-1\",\n size === \"40\" && \"h-10 py-2.5\",\n // Variant colors\n isDark && \"bg-background-800 text-body-white-solid-constant\",\n !isDark && selected && \"bg-brand-green-50 text-neutral-400\",\n !isDark && !selected && \"bg-neutral-100 text-neutral-400\",\n // Hover\n isInteractive &&\n !disabled &&\n !isDark &&\n selected &&\n \"hover:bg-brand-green-500 hover:text-body-black-solid-constant\",\n isInteractive && !disabled && !isDark && !selected && \"hover:bg-hover-400\",\n // Focus\n \"focus-visible:shadow-focus-ring focus-visible:outline-none\",\n // Disabled\n disabled && isDark && \"pointer-events-none opacity-50\",\n disabled && !isDark && \"pointer-events-none text-neutral-300\",\n className,\n )}\n {...(isInteractive && {\n type: \"button\" as const,\n disabled,\n \"aria-pressed\": selected,\n onClick,\n })}\n {...(!isInteractive && disabled && { \"aria-disabled\": true })}\n {...(selected && { \"data-selected\": \"\" })}\n {...props}\n >\n {leftDot && <span className=\"size-2 shrink-0 rounded-full bg-current\" aria-hidden=\"true\" />}\n {leftIcon && (\n <span className=\"flex size-5 shrink-0 items-center justify-center\" aria-hidden=\"true\">\n {leftIcon}\n </span>\n )}\n <Slottable>{children}</Slottable>\n {rightIcon && (\n <span className=\"flex size-5 shrink-0 items-center justify-center\" aria-hidden=\"true\">\n {rightIcon}\n </span>\n )}\n {notificationLabel && (\n <span className=\"typography-caption-semibold absolute -top-1 -right-1 flex h-4 min-w-4 items-center justify-center rounded-full bg-body-100 px-1 text-body-300\">\n {notificationLabel}\n </span>\n )}\n </Comp>\n );\n },\n);\n\nChip.displayName = \"Chip\";\n"],"names":["React","Slot","jsxs","cn","jsx","Slottable"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AA8BO,MAAM,OAAOA,iBAAM;AAAA,EACxB,CACE;AAAA,IACE;AAAA,IACA,UAAU;AAAA,IACV,OAAO;AAAA,IACP,WAAW;AAAA,IACX,WAAW;AAAA,IACX,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QAEG;AACH,UAAM,gBAAgB,CAAC,CAAC,WAAW,CAAC;AACpC,UAAM,OAAO,UAAUC,UAAAA,OAAO,gBAAgB,WAAW;AACzD,UAAM,SAAS,YAAY;AAE3B,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,eAAY;AAAA,QACZ,WAAWC,GAAAA;AAAAA,UACT;AAAA;AAAA,UAEA,YAAY,WAAW,eAAe;AAAA;AAAA,UAEtC,SAAS,QAAQ;AAAA,UACjB,SAAS,QAAQ;AAAA;AAAA,UAEjB,UAAU;AAAA,UACV,CAAC,UAAU,YAAY;AAAA,UACvB,CAAC,UAAU,CAAC,YAAY;AAAA;AAAA,UAExB,iBACE,CAAC,YACD,CAAC,UACD,YACA;AAAA,UACF,iBAAiB,CAAC,YAAY,CAAC,UAAU,CAAC,YAAY;AAAA;AAAA,UAEtD;AAAA;AAAA,UAEA,YAAY,UAAU;AAAA,UACtB,YAAY,CAAC,UAAU;AAAA,UACvB;AAAA,QAAA;AAAA,QAED,GAAI,iBAAiB;AAAA,UACpB,MAAM;AAAA,UACN;AAAA,UACA,gBAAgB;AAAA,UAChB;AAAA,QAAA;AAAA,QAED,GAAI,CAAC,iBAAiB,YAAY,EAAE,iBAAiB,KAAA;AAAA,QACrD,GAAI,YAAY,EAAE,iBAAiB,GAAA;AAAA,QACnC,GAAG;AAAA,QAEH,UAAA;AAAA,UAAA,WAAWC,2BAAAA,IAAC,QAAA,EAAK,WAAU,2CAA0C,eAAY,QAAO;AAAA,UACxF,YACCA,2BAAAA,IAAC,QAAA,EAAK,WAAU,oDAAmD,eAAY,QAC5E,UAAA,UACH;AAAA,UAEFA,+BAACC,UAAAA,aAAW,UAAS;AAAA,UACpB,aACCD,2BAAAA,IAAC,QAAA,EAAK,WAAU,oDAAmD,eAAY,QAC5E,UAAA,WACH;AAAA,UAED,qBACCA,2BAAAA,IAAC,QAAA,EAAK,WAAU,iJACb,UAAA,kBAAA,CACH;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEA,KAAK,cAAc;;"}
@@ -38,7 +38,7 @@ function DayButton({ day, modifiers, className, ...buttonProps }) {
38
38
  "relative z-10 inline-flex size-10 cursor-pointer items-center justify-center rounded-lg",
39
39
  "typography-body-2-regular",
40
40
  "transition-colors hover:bg-brand-green-50",
41
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-purple-500",
41
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-purple-500 focus-visible:outline-offset-[-2px]",
42
42
  "disabled:cursor-not-allowed disabled:opacity-50",
43
43
  modifiers.today && !modifiers.selected && "border border-brand-green-500",
44
44
  modifiers.selected && !modifiers.range_middle ? "bg-brand-green-500 text-body-black-solid-constant hover:bg-brand-green-500" : "text-body-100",
@@ -1 +1 @@
1
- {"version":3,"file":"DatePicker.cjs","sources":["../../../../src/components/DatePicker/DatePicker.tsx"],"sourcesContent":["import { forwardRef, useEffect, useRef } from \"react\";\nimport {\n type ChevronProps,\n type DateRange,\n type DayButtonProps,\n DayPicker,\n type DayPickerProps,\n type DayProps,\n type MonthGridProps,\n type WeekdayProps,\n type WeekdaysProps,\n type WeekProps,\n type WeeksProps,\n} from \"react-day-picker\";\nimport { cn } from \"../../utils/cn\";\nimport type { OmitDistributed } from \"../../utils/types\";\nimport { Button } from \"../Button/Button\";\nimport { ChevronLeftIcon } from \"../Icons/ChevronLeftIcon\";\nimport { ChevronRightIcon } from \"../Icons/ChevronRightIcon\";\n\nexport type { DateRange }; // Needed by consumers when passing props\n\nexport type DatePickerType = \"single\" | \"double\";\n\nexport interface DatePickerOwnProps {\n /** Display one month or two side-by-side. @default \"single\" */\n type?: DatePickerType;\n /** Called when the Apply button is clicked. */\n onApply?: () => void;\n /** Called when the Cancel button is clicked. */\n onCancel?: () => void;\n /** Label for the cancel button. @default \"Cancel\" */\n cancelLabel?: string;\n /** Label for the apply button. @default \"Apply\" */\n applyLabel?: string;\n /** Whether to render cancel / apply footer buttons. @default true */\n showFooter?: boolean;\n /** Additional className for the outer container. */\n className?: string;\n}\n\nfunction Day({ day, modifiers, className, ...divProps }: DayProps) {\n const { range_start, range_end } = modifiers;\n const isSingleDayRange = range_start && range_end;\n\n return (\n <div\n className={cn(\n className,\n (range_start || range_end) && !isSingleDayRange && \"from-50% from-transparent to-50%\",\n range_start && !isSingleDayRange && \"bg-linear-to-r to-brand-green-50\",\n range_end && !isSingleDayRange && \"bg-linear-to-l to-brand-green-50\",\n )}\n {...divProps}\n />\n );\n}\n\nfunction DayButton({ day, modifiers, className, ...buttonProps }: DayButtonProps) {\n const ref = useRef<HTMLButtonElement>(null);\n\n useEffect(() => {\n if (modifiers.focused) ref.current?.focus();\n }, [modifiers.focused]);\n\n return (\n <button\n ref={ref}\n type=\"button\"\n className={cn(\n \"relative z-10 inline-flex size-10 cursor-pointer items-center justify-center rounded-lg\",\n \"typography-body-2-regular\",\n \"transition-colors hover:bg-brand-green-50\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-purple-500\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n modifiers.today && !modifiers.selected && \"border border-brand-green-500\",\n modifiers.selected && !modifiers.range_middle\n ? \"bg-brand-green-500 text-body-black-solid-constant hover:bg-brand-green-500\"\n : \"text-body-100\",\n modifiers.range_middle && \"rounded-none bg-transparent\",\n modifiers.outside && \"pointer-events-none opacity-50\",\n )}\n {...buttonProps}\n />\n );\n}\n\nexport type DatePickerProps = DatePickerOwnProps &\n OmitDistributed<DayPickerProps, \"numberOfMonths\">;\n\nexport const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>(\n (\n {\n type = \"single\",\n onApply,\n onCancel,\n cancelLabel = \"Cancel\",\n applyLabel = \"Apply\",\n showFooter = true,\n className,\n formatters,\n ...dayPickerProps\n },\n ref,\n ) => {\n const numberOfMonths = type === \"double\" ? 2 : 1;\n const isMulti = numberOfMonths > 1;\n\n return (\n <div\n ref={ref}\n className={cn(\n \"inline-flex flex-col rounded-2xl border border-neutral-200 bg-background-inverse-solid shadow-[0px_6px_12px_0px_rgba(0,0,0,0.1)] backdrop-blur-sm\",\n className,\n )}\n >\n <DayPicker\n showOutsideDays\n numberOfMonths={numberOfMonths}\n formatters={{\n formatCaption: (date: Date) =>\n date.toLocaleDateString(\"en-US\", { month: \"short\", year: \"numeric\" }),\n ...formatters,\n }}\n classNames={{\n root: \"w-full\",\n months: \"relative flex\",\n month: \"flex flex-1 flex-col\",\n month_caption: cn(\"flex items-center py-4\", isMulti ? \"justify-center px-2\" : \"px-5\"),\n caption_label: \"typography-body-1-semibold text-body-100\",\n nav: cn(\n \"absolute top-4 z-20 flex\",\n isMulti ? \"pointer-events-none inset-x-3 justify-between\" : \"right-3 gap-1\",\n ),\n button_previous:\n \"pointer-events-auto inline-flex size-8 cursor-pointer items-center justify-center rounded-full text-body-100 transition-colors hover:bg-brand-green-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-purple-500 disabled:cursor-not-allowed disabled:opacity-50\", // !TODO https://linear.app/fanvue/issue/ENG-7301/swap-out-typography-tailwind-utility-classes\n button_next:\n \"pointer-events-auto inline-flex size-8 cursor-pointer items-center justify-center rounded-full text-body-100 transition-colors hover:bg-brand-green-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-purple-500 disabled:cursor-not-allowed disabled:opacity-50\", // !TODO https://linear.app/fanvue/issue/ENG-7301/swap-out-typography-tailwind-utility-classes\n month_grid: cn(\"mb-4\", isMulti ? \"mx-2\" : \"mx-4\"),\n weekdays: \"flex\",\n weekday:\n \"flex h-[30px] w-10 flex-1 items-center justify-center typography-body-2-regular text-body-200\",\n week: \"flex overflow-hidden rounded-lg\",\n day: \"relative flex w-10 flex-1 items-center justify-center\",\n range_middle: \"bg-brand-green-50\",\n hidden: \"hidden\",\n }}\n components={{\n /**\n * !NOTE: We're unable to use semantic elements for the grid due to rdp, as such we've disabled the a11y lint rules for these elements in biome.json.\n */\n Chevron: ({ orientation }: ChevronProps) =>\n orientation === \"left\" ? <ChevronLeftIcon /> : <ChevronRightIcon />,\n MonthGrid: (props: MonthGridProps) => <div role=\"grid\" {...props} />,\n Weekdays: (props: WeekdaysProps) => <div role=\"row\" {...props} />,\n Weekday: (props: WeekdayProps) => <div role=\"columnheader\" {...props} />,\n Weeks: (props: WeeksProps) => <div role=\"rowgroup\" {...props} />,\n Week: ({ week, ...props }: WeekProps) => <div role=\"row\" {...props} />,\n Day,\n DayButton,\n }}\n {...dayPickerProps}\n />\n\n {showFooter && (\n <div className=\"flex gap-4 px-5 pb-4\">\n <Button variant=\"secondary\" size=\"40\" className=\"flex-1\" onClick={onCancel}>\n {cancelLabel}\n </Button>\n <Button variant=\"primary\" size=\"40\" className=\"flex-1\" onClick={onApply}>\n {applyLabel}\n </Button>\n </div>\n )}\n </div>\n );\n },\n);\n\nDatePicker.displayName = \"DatePicker\";\n"],"names":["jsx","cn","useRef","useEffect","forwardRef","jsxs","DayPicker","ChevronLeftIcon","ChevronRightIcon","Button"],"mappings":"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"}
1
+ {"version":3,"file":"DatePicker.cjs","sources":["../../../../src/components/DatePicker/DatePicker.tsx"],"sourcesContent":["import { forwardRef, useEffect, useRef } from \"react\";\nimport {\n type ChevronProps,\n type DateRange,\n type DayButtonProps,\n DayPicker,\n type DayPickerProps,\n type DayProps,\n type MonthGridProps,\n type WeekdayProps,\n type WeekdaysProps,\n type WeekProps,\n type WeeksProps,\n} from \"react-day-picker\";\nimport { cn } from \"../../utils/cn\";\nimport type { OmitDistributed } from \"../../utils/types\";\nimport { Button } from \"../Button/Button\";\nimport { ChevronLeftIcon } from \"../Icons/ChevronLeftIcon\";\nimport { ChevronRightIcon } from \"../Icons/ChevronRightIcon\";\n\nexport type { DateRange }; // Needed by consumers when passing props\n\nexport type DatePickerType = \"single\" | \"double\";\n\nexport interface DatePickerOwnProps {\n /** Display one month or two side-by-side. @default \"single\" */\n type?: DatePickerType;\n /** Called when the Apply button is clicked. */\n onApply?: () => void;\n /** Called when the Cancel button is clicked. */\n onCancel?: () => void;\n /** Label for the cancel button. @default \"Cancel\" */\n cancelLabel?: string;\n /** Label for the apply button. @default \"Apply\" */\n applyLabel?: string;\n /** Whether to render cancel / apply footer buttons. @default true */\n showFooter?: boolean;\n /** Additional className for the outer container. */\n className?: string;\n}\n\nfunction Day({ day, modifiers, className, ...divProps }: DayProps) {\n const { range_start, range_end } = modifiers;\n const isSingleDayRange = range_start && range_end;\n\n return (\n <div\n className={cn(\n className,\n (range_start || range_end) && !isSingleDayRange && \"from-50% from-transparent to-50%\",\n range_start && !isSingleDayRange && \"bg-linear-to-r to-brand-green-50\",\n range_end && !isSingleDayRange && \"bg-linear-to-l to-brand-green-50\",\n )}\n {...divProps}\n />\n );\n}\n\nfunction DayButton({ day, modifiers, className, ...buttonProps }: DayButtonProps) {\n const ref = useRef<HTMLButtonElement>(null);\n\n useEffect(() => {\n if (modifiers.focused) ref.current?.focus();\n }, [modifiers.focused]);\n\n return (\n <button\n ref={ref}\n type=\"button\"\n className={cn(\n \"relative z-10 inline-flex size-10 cursor-pointer items-center justify-center rounded-lg\",\n \"typography-body-2-regular\",\n \"transition-colors hover:bg-brand-green-50\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-purple-500 focus-visible:outline-offset-[-2px]\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n modifiers.today && !modifiers.selected && \"border border-brand-green-500\",\n modifiers.selected && !modifiers.range_middle\n ? \"bg-brand-green-500 text-body-black-solid-constant hover:bg-brand-green-500\"\n : \"text-body-100\",\n modifiers.range_middle && \"rounded-none bg-transparent\",\n modifiers.outside && \"pointer-events-none opacity-50\",\n )}\n {...buttonProps}\n />\n );\n}\n\nexport type DatePickerProps = DatePickerOwnProps &\n OmitDistributed<DayPickerProps, \"numberOfMonths\">;\n\nexport const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>(\n (\n {\n type = \"single\",\n onApply,\n onCancel,\n cancelLabel = \"Cancel\",\n applyLabel = \"Apply\",\n showFooter = true,\n className,\n formatters,\n ...dayPickerProps\n },\n ref,\n ) => {\n const numberOfMonths = type === \"double\" ? 2 : 1;\n const isMulti = numberOfMonths > 1;\n\n return (\n <div\n ref={ref}\n className={cn(\n \"inline-flex flex-col rounded-2xl border border-neutral-200 bg-background-inverse-solid shadow-[0px_6px_12px_0px_rgba(0,0,0,0.1)] backdrop-blur-sm\",\n className,\n )}\n >\n <DayPicker\n showOutsideDays\n numberOfMonths={numberOfMonths}\n formatters={{\n formatCaption: (date: Date) =>\n date.toLocaleDateString(\"en-US\", { month: \"short\", year: \"numeric\" }),\n ...formatters,\n }}\n classNames={{\n root: \"w-full\",\n months: \"relative flex\",\n month: \"flex flex-1 flex-col\",\n month_caption: cn(\"flex items-center py-4\", isMulti ? \"justify-center px-2\" : \"px-5\"),\n caption_label: \"typography-body-1-semibold text-body-100\",\n nav: cn(\n \"absolute top-4 z-20 flex\",\n isMulti ? \"pointer-events-none inset-x-3 justify-between\" : \"right-3 gap-1\",\n ),\n button_previous:\n \"pointer-events-auto inline-flex size-8 cursor-pointer items-center justify-center rounded-full text-body-100 transition-colors hover:bg-brand-green-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-purple-500 disabled:cursor-not-allowed disabled:opacity-50\", // !TODO https://linear.app/fanvue/issue/ENG-7301/swap-out-typography-tailwind-utility-classes\n button_next:\n \"pointer-events-auto inline-flex size-8 cursor-pointer items-center justify-center rounded-full text-body-100 transition-colors hover:bg-brand-green-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-purple-500 disabled:cursor-not-allowed disabled:opacity-50\", // !TODO https://linear.app/fanvue/issue/ENG-7301/swap-out-typography-tailwind-utility-classes\n month_grid: cn(\"mb-4\", isMulti ? \"mx-2\" : \"mx-4\"),\n weekdays: \"flex\",\n weekday:\n \"flex h-[30px] w-10 flex-1 items-center justify-center typography-body-2-regular text-body-200\",\n week: \"flex overflow-hidden rounded-lg\",\n day: \"relative flex w-10 flex-1 items-center justify-center\",\n range_middle: \"bg-brand-green-50\",\n hidden: \"hidden\",\n }}\n components={{\n /**\n * !NOTE: We're unable to use semantic elements for the grid due to rdp, as such we've disabled the a11y lint rules for these elements in biome.json.\n */\n Chevron: ({ orientation }: ChevronProps) =>\n orientation === \"left\" ? <ChevronLeftIcon /> : <ChevronRightIcon />,\n MonthGrid: (props: MonthGridProps) => <div role=\"grid\" {...props} />,\n Weekdays: (props: WeekdaysProps) => <div role=\"row\" {...props} />,\n Weekday: (props: WeekdayProps) => <div role=\"columnheader\" {...props} />,\n Weeks: (props: WeeksProps) => <div role=\"rowgroup\" {...props} />,\n Week: ({ week, ...props }: WeekProps) => <div role=\"row\" {...props} />,\n Day,\n DayButton,\n }}\n {...dayPickerProps}\n />\n\n {showFooter && (\n <div className=\"flex gap-4 px-5 pb-4\">\n <Button variant=\"secondary\" size=\"40\" className=\"flex-1\" onClick={onCancel}>\n {cancelLabel}\n </Button>\n <Button variant=\"primary\" size=\"40\" className=\"flex-1\" onClick={onApply}>\n {applyLabel}\n </Button>\n </div>\n )}\n </div>\n );\n },\n);\n\nDatePicker.displayName = \"DatePicker\";\n"],"names":["jsx","cn","useRef","useEffect","forwardRef","jsxs","DayPicker","ChevronLeftIcon","ChevronRightIcon","Button"],"mappings":"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"}
@@ -23,23 +23,23 @@ function _interopNamespaceDefault(e) {
23
23
  }
24
24
  const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
25
25
  const iconButtonVariants = {
26
- primary: "bg-neutral-400 text-body-300 hover:bg-hover-100 disabled:opacity-50 focus:shadow-focus-ring",
27
- secondary: `bg-neutral-100 text-neutral-400 hover:bg-neutral-200 disabled:opacity-50 focus:focus:shadow-focus-ring`,
28
- tertiary: `bg-transparent text-neutral-400 hover:bg-hover-300 disabled:opacity-50 focus:shadow-focus-ring active:bg-brand-green-50`,
29
- brand: `bg-body-black-solid-constant text-brand-green-500 hover:bg-brand-green-500 hover:text-body-black-solid-constant disabled:opacity-50 focus:shadow-focus-ring`,
30
- contrast: `bg-transparent text-body-white-solid-constant disabled:opacity-50 focus:shadow-focus-ring`,
31
- messaging: `bg-body-black-solid-constant text-brand-green-500 hover:bg-brand-green-500 hover:text-body-black-solid-constant disabled:opacity-50 focus:shadow-focus-ring`,
32
- navTray: `bg-transparent text-neutral-400 disabled:opacity-50 focus:shadow-focus-ring active:bg-brand-green-50`,
33
- tertiaryDestructive: `bg-transparent text-error-500 hover:bg-hover-300 disabled:opacity-50 focus:shadow-focus-ring`,
34
- stop: `bg-neutral-400 text-body-300 hover:bg-brand-green-500 hover:text-body-black-solid-constant`,
35
- microphone: `bg-neutral-400 text-body-300 hover:bg-brand-green-500 hover:text-body-black-solid-constant`
26
+ primary: "bg-neutral-400 text-body-300 hover:bg-hover-100 disabled:opacity-50 focus-visible:shadow-focus-ring",
27
+ secondary: "bg-neutral-100 text-neutral-400 hover:bg-neutral-200 disabled:opacity-50 focus-visible:shadow-focus-ring",
28
+ tertiary: "bg-transparent text-neutral-400 hover:bg-hover-300 disabled:opacity-50 focus-visible:shadow-focus-ring active:bg-brand-green-50",
29
+ brand: "bg-body-black-solid-constant text-brand-green-500 hover:bg-brand-green-500 hover:text-body-black-solid-constant disabled:opacity-50 focus-visible:shadow-focus-ring",
30
+ contrast: "bg-transparent text-body-white-solid-constant disabled:opacity-50 focus-visible:shadow-focus-ring",
31
+ messaging: "bg-body-black-solid-constant text-brand-green-500 hover:bg-brand-green-500 hover:text-body-black-solid-constant disabled:opacity-50 focus-visible:shadow-focus-ring",
32
+ navTray: "bg-transparent text-neutral-400 disabled:opacity-50 focus-visible:shadow-focus-ring active:bg-brand-green-50",
33
+ tertiaryDestructive: "bg-transparent text-error-500 hover:bg-hover-300 disabled:opacity-50 focus-visible:shadow-focus-ring",
34
+ stop: "bg-neutral-400 text-body-300 hover:bg-brand-green-500 hover:text-body-black-solid-constant disabled:opacity-50 focus-visible:shadow-focus-ring",
35
+ microphone: "bg-neutral-400 text-body-300 hover:bg-brand-green-500 hover:text-body-black-solid-constant disabled:opacity-50 focus-visible:shadow-focus-ring"
36
36
  };
37
37
  const iconSizeVariants = {
38
- 24: "size-4",
39
- 32: "size-5",
40
- 40: "size-6",
41
- 52: "size-7",
42
- 72: "size-8"
38
+ 24: "[&>svg]:size-4",
39
+ 32: "[&>svg]:size-5",
40
+ 40: "[&>svg]:size-6",
41
+ 52: "[&>svg]:size-7",
42
+ 72: "[&>svg]:size-8"
43
43
  };
44
44
  const sizeVariants = {
45
45
  24: "p-1",
@@ -50,6 +50,13 @@ const sizeVariants = {
50
50
  };
51
51
  const IconButton = React__namespace.forwardRef(
52
52
  ({ className, variant = "primary", size = "40", icon, counterValue, disabled = false, ...props }, ref) => {
53
+ if (process.env.NODE_ENV !== "production") {
54
+ if (!props["aria-label"] && !props["aria-labelledby"] && !props.title) {
55
+ console.warn(
56
+ "IconButton: No accessible name provided. Add an `aria-label`, `aria-labelledby`, or `title` prop so screen readers can announce this button."
57
+ );
58
+ }
59
+ }
53
60
  return /* @__PURE__ */ jsxRuntime.jsxs(
54
61
  "button",
55
62
  {
@@ -73,10 +80,7 @@ const IconButton = React__namespace.forwardRef(
73
80
  /* @__PURE__ */ jsxRuntime.jsx(
74
81
  "span",
75
82
  {
76
- className: cn.cn(
77
- "flex shrink-0 items-center justify-center overflow-clip",
78
- iconSizeVariants[size]
79
- ),
83
+ className: cn.cn("flex shrink-0 items-center justify-center", iconSizeVariants[size]),
80
84
  "aria-hidden": "true",
81
85
  children: icon
82
86
  }
@@ -1 +1 @@
1
- {"version":3,"file":"IconButton.cjs","sources":["../../../../src/components/IconButton/IconButton.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Count } from \"../Count/Count\";\n\nconst iconButtonVariants = {\n primary:\n \"bg-neutral-400 text-body-300 hover:bg-hover-100 disabled:opacity-50 focus:shadow-focus-ring\",\n secondary: `bg-neutral-100 text-neutral-400 hover:bg-neutral-200 disabled:opacity-50 focus:focus:shadow-focus-ring`,\n tertiary: `bg-transparent text-neutral-400 hover:bg-hover-300 disabled:opacity-50 focus:shadow-focus-ring active:bg-brand-green-50`,\n brand: `bg-body-black-solid-constant text-brand-green-500 hover:bg-brand-green-500 hover:text-body-black-solid-constant disabled:opacity-50 focus:shadow-focus-ring`,\n contrast: `bg-transparent text-body-white-solid-constant disabled:opacity-50 focus:shadow-focus-ring`,\n messaging: `bg-body-black-solid-constant text-brand-green-500 hover:bg-brand-green-500 hover:text-body-black-solid-constant disabled:opacity-50 focus:shadow-focus-ring`,\n navTray: `bg-transparent text-neutral-400 disabled:opacity-50 focus:shadow-focus-ring active:bg-brand-green-50`,\n tertiaryDestructive: `bg-transparent text-error-500 hover:bg-hover-300 disabled:opacity-50 focus:shadow-focus-ring`,\n stop: `bg-neutral-400 text-body-300 hover:bg-brand-green-500 hover:text-body-black-solid-constant`,\n microphone: `bg-neutral-400 text-body-300 hover:bg-brand-green-500 hover:text-body-black-solid-constant`,\n};\n\nconst iconSizeVariants = {\n 24: \"size-4\",\n 32: \"size-5\",\n 40: \"size-6\",\n 52: \"size-7\",\n 72: \"size-8\",\n} as const;\n\nconst sizeVariants = {\n 24: \"p-1\",\n 32: \"p-1.5\",\n 40: \"p-[10px]\",\n 52: \"p-2\",\n 72: \"p-4\",\n} as const;\n\nexport type IconButtonVariant =\n | \"primary\"\n | \"secondary\"\n | \"tertiary\"\n | \"brand\"\n | \"contrast\"\n | \"messaging\"\n | \"navTray\"\n | \"tertiaryDestructive\"\n | \"stop\"\n | \"microphone\";\n\nexport type IconButtonSize = \"24\" | \"32\" | \"40\" | \"52\" | \"72\";\n\nexport interface IconButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Visual style variant of the icon button */\n variant?: IconButtonVariant;\n /** Size of the button */\n size?: IconButtonSize;\n /** Icon element to display */\n icon: React.ReactNode;\n /** Counter value to display */\n counterValue?: number;\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 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(\n \"flex shrink-0 items-center justify-center overflow-clip\",\n iconSizeVariants[size],\n )}\n aria-hidden=\"true\"\n >\n {icon}\n </span>\n\n {counterValue !== undefined && (variant === \"tertiary\" || variant === \"navTray\") && (\n <div className=\"absolute top-0 right-0\">\n <Count value={counterValue} />\n </div>\n )}\n </button>\n );\n },\n);\n\nIconButton.displayName = \"IconButton\";\n"],"names":["React","jsxs","cn","jsx","Count"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAIA,MAAM,qBAAqB;AAAA,EACzB,SACE;AAAA,EACF,WAAW;AAAA,EACX,UAAU;AAAA,EACV,OAAO;AAAA,EACP,UAAU;AAAA,EACV,WAAW;AAAA,EACX,SAAS;AAAA,EACT,qBAAqB;AAAA,EACrB,MAAM;AAAA,EACN,YAAY;AACd;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;AA2BO,MAAM,aAAaA,iBAAM;AAAA,EAC9B,CACE,EAAE,WAAW,UAAU,WAAW,OAAO,MAAM,MAAM,cAAc,WAAW,OAAO,GAAG,MAAA,GACxF,QACG;AACH,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL,eAAY;AAAA,QACZ;AAAA,QACA,WAAWC,GAAAA;AAAAA;AAAAA,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,UAAAC,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAWD,GAAAA;AAAAA,gBACT;AAAA,gBACA,iBAAiB,IAAI;AAAA,cAAA;AAAA,cAEvB,eAAY;AAAA,cAEX,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,UAGF,iBAAiB,WAAc,YAAY,cAAc,YAAY,cACpEC,2BAAAA,IAAC,OAAA,EAAI,WAAU,0BACb,UAAAA,2BAAAA,IAACC,MAAAA,OAAA,EAAM,OAAO,cAAc,EAAA,CAC9B;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEA,WAAW,cAAc;;"}
1
+ {"version":3,"file":"IconButton.cjs","sources":["../../../../src/components/IconButton/IconButton.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Count } from \"../Count/Count\";\n\nconst iconButtonVariants = {\n primary:\n \"bg-neutral-400 text-body-300 hover:bg-hover-100 disabled:opacity-50 focus-visible:shadow-focus-ring\",\n secondary:\n \"bg-neutral-100 text-neutral-400 hover:bg-neutral-200 disabled:opacity-50 focus-visible:shadow-focus-ring\",\n tertiary:\n \"bg-transparent text-neutral-400 hover:bg-hover-300 disabled:opacity-50 focus-visible:shadow-focus-ring active:bg-brand-green-50\",\n brand:\n \"bg-body-black-solid-constant text-brand-green-500 hover:bg-brand-green-500 hover:text-body-black-solid-constant disabled:opacity-50 focus-visible:shadow-focus-ring\",\n contrast:\n \"bg-transparent text-body-white-solid-constant disabled:opacity-50 focus-visible:shadow-focus-ring\",\n messaging:\n \"bg-body-black-solid-constant text-brand-green-500 hover:bg-brand-green-500 hover:text-body-black-solid-constant disabled:opacity-50 focus-visible:shadow-focus-ring\",\n navTray:\n \"bg-transparent text-neutral-400 disabled:opacity-50 focus-visible:shadow-focus-ring active:bg-brand-green-50\",\n tertiaryDestructive:\n \"bg-transparent text-error-500 hover:bg-hover-300 disabled:opacity-50 focus-visible:shadow-focus-ring\",\n stop: \"bg-neutral-400 text-body-300 hover:bg-brand-green-500 hover:text-body-black-solid-constant disabled:opacity-50 focus-visible:shadow-focus-ring\",\n microphone:\n \"bg-neutral-400 text-body-300 hover:bg-brand-green-500 hover:text-body-black-solid-constant 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: \"p-1\",\n 32: \"p-1.5\",\n 40: \"p-[10px]\",\n 52: \"p-2\",\n 72: \"p-4\",\n} as const;\n\nexport type IconButtonVariant =\n | \"primary\"\n | \"secondary\"\n | \"tertiary\"\n | \"brand\"\n | \"contrast\"\n | \"messaging\"\n | \"navTray\"\n | \"tertiaryDestructive\"\n | \"stop\"\n | \"microphone\";\n\nexport type IconButtonSize = \"24\" | \"32\" | \"40\" | \"52\" | \"72\";\n\nexport interface IconButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Visual style variant of the icon button */\n variant?: IconButtonVariant;\n /** Size of the button */\n size?: IconButtonSize;\n /** Icon element to display */\n icon: React.ReactNode;\n /** Counter value to display */\n counterValue?: number;\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 && (variant === \"tertiary\" || variant === \"navTray\") && (\n <div className=\"absolute top-0 right-0\">\n <Count value={counterValue} />\n </div>\n )}\n </button>\n );\n },\n);\n\nIconButton.displayName = \"IconButton\";\n"],"names":["React","jsxs","cn","jsx","Count"],"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;AA2BO,MAAM,aAAaA,iBAAM;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,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL,eAAY;AAAA,QACZ;AAAA,QACA,WAAWC,GAAAA;AAAAA;AAAAA,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,UAAAC,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAWD,GAAAA,GAAG,6CAA6C,iBAAiB,IAAI,CAAC;AAAA,cACjF,eAAY;AAAA,cAEX,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,UAGF,iBAAiB,WAAc,YAAY,cAAc,YAAY,cACpEC,2BAAAA,IAAC,OAAA,EAAI,WAAU,0BACb,UAAAA,2BAAAA,IAACC,MAAAA,OAAA,EAAM,OAAO,cAAc,EAAA,CAC9B;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEA,WAAW,cAAc;;"}
@@ -0,0 +1,63 @@
1
+ "use client";
2
+ "use strict";
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
+ const jsxRuntime = require("react/jsx-runtime");
5
+ const React = require("react");
6
+ const cn = require("../../utils/cn.cjs");
7
+ function _interopNamespaceDefault(e) {
8
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
9
+ if (e) {
10
+ for (const k in e) {
11
+ if (k !== "default") {
12
+ const d = Object.getOwnPropertyDescriptor(e, k);
13
+ Object.defineProperty(n, k, d.get ? d : {
14
+ enumerable: true,
15
+ get: () => e[k]
16
+ });
17
+ }
18
+ }
19
+ }
20
+ n.default = e;
21
+ return Object.freeze(n);
22
+ }
23
+ const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
24
+ const EyeIcon = React__namespace.forwardRef(
25
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
26
+ "svg",
27
+ {
28
+ ref,
29
+ viewBox: "0 0 20 20",
30
+ fill: "none",
31
+ "aria-hidden": "true",
32
+ className: cn.cn("size-5", className),
33
+ ...props,
34
+ children: [
35
+ /* @__PURE__ */ jsxRuntime.jsx(
36
+ "path",
37
+ {
38
+ d: "M1 10s3-7 9-7 9 7 9 7-3 7-9 7-9-7-9-7Z",
39
+ stroke: "currentColor",
40
+ strokeWidth: "2",
41
+ strokeLinecap: "round",
42
+ strokeLinejoin: "round"
43
+ }
44
+ ),
45
+ /* @__PURE__ */ jsxRuntime.jsx(
46
+ "circle",
47
+ {
48
+ cx: "10",
49
+ cy: "10",
50
+ r: "3",
51
+ stroke: "currentColor",
52
+ strokeWidth: "2",
53
+ strokeLinecap: "round",
54
+ strokeLinejoin: "round"
55
+ }
56
+ )
57
+ ]
58
+ }
59
+ )
60
+ );
61
+ EyeIcon.displayName = "EyeIcon";
62
+ exports.EyeIcon = EyeIcon;
63
+ //# sourceMappingURL=EyeIcon.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EyeIcon.cjs","sources":["../../../../src/components/Icons/EyeIcon.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport type { IconProps } from \"./types\";\n\nexport const EyeIcon = React.forwardRef<SVGSVGElement, IconProps>(\n ({ className, ...props }, ref) => (\n <svg\n ref={ref}\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n aria-hidden=\"true\"\n className={cn(\"size-5\", className)}\n {...props}\n >\n <path\n d=\"M1 10s3-7 9-7 9 7 9 7-3 7-9 7-9-7-9-7Z\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n <circle\n cx=\"10\"\n cy=\"10\"\n r=\"3\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n ),\n);\n\nEyeIcon.displayName = \"EyeIcon\";\n"],"names":["React","jsxs","cn","jsx"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAIO,MAAM,UAAUA,iBAAM;AAAA,EAC3B,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QACxBC,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,eAAY;AAAA,MACZ,WAAWC,GAAAA,GAAG,UAAU,SAAS;AAAA,MAChC,GAAG;AAAA,MAEJ,UAAA;AAAA,QAAAC,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,GAAE;AAAA,YACF,QAAO;AAAA,YACP,aAAY;AAAA,YACZ,eAAc;AAAA,YACd,gBAAe;AAAA,UAAA;AAAA,QAAA;AAAA,QAEjBA,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,IAAG;AAAA,YACH,IAAG;AAAA,YACH,GAAE;AAAA,YACF,QAAO;AAAA,YACP,aAAY;AAAA,YACZ,eAAc;AAAA,YACd,gBAAe;AAAA,UAAA;AAAA,QAAA;AAAA,MACjB;AAAA,IAAA;AAAA,EAAA;AAGN;AAEA,QAAQ,cAAc;;"}
@@ -101,7 +101,7 @@ const Pagination = React__namespace.forwardRef(
101
101
  "aria-current": page === currentPage ? "page" : void 0,
102
102
  onClick: () => onPageChange?.(page),
103
103
  className: cn.cn(
104
- "flex size-4 cursor-pointer items-center justify-center rounded-full text-xs transition-colors",
104
+ "flex size-4 cursor-pointer items-center justify-center rounded-full text-xs motion-safe:transition-colors motion-safe:duration-150 focus-visible:shadow-focus-ring focus-visible:outline-none",
105
105
  page === currentPage ? "bg-neutral-400 text-body-300" : "bg-neutral-100 text-body-100 hover:bg-neutral-200"
106
106
  ),
107
107
  children: page
@@ -109,16 +109,22 @@ const Pagination = React__namespace.forwardRef(
109
109
  page
110
110
  )
111
111
  ) }),
112
- variant === "dots" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => /* @__PURE__ */ jsxRuntime.jsx(
112
+ variant === "dots" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center", children: Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => /* @__PURE__ */ jsxRuntime.jsx(
113
113
  "button",
114
114
  {
115
115
  type: "button",
116
116
  "aria-label": getPageLabel(page),
117
117
  "aria-current": page === currentPage ? "page" : void 0,
118
118
  onClick: () => onPageChange?.(page),
119
- className: cn.cn(
120
- "cursor-pointer rounded-full transition-all",
121
- page === currentPage ? "size-2 bg-neutral-400" : "size-1.5 bg-neutral-200 hover:bg-neutral-250"
119
+ className: "flex size-6 cursor-pointer items-center justify-center rounded-full focus-visible:shadow-focus-ring focus-visible:outline-none",
120
+ children: /* @__PURE__ */ jsxRuntime.jsx(
121
+ "span",
122
+ {
123
+ className: cn.cn(
124
+ "block rounded-full motion-safe:transition-all motion-safe:duration-150",
125
+ page === currentPage ? "size-2 bg-neutral-400" : "size-1.5 bg-neutral-200 hover:bg-neutral-250"
126
+ )
127
+ }
122
128
  )
123
129
  },
124
130
  page
@@ -1 +1 @@
1
- {"version":3,"file":"Pagination.cjs","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\nexport type PaginationVariant = \"default\" | \"dots\";\n\nexport interface PaginationProps extends Omit<React.HTMLAttributes<HTMLElement>, \"onChange\"> {\n /** Visual style variant */\n variant?: PaginationVariant;\n /** Total number of pages */\n totalPages: number;\n /** Current active page (1-indexed) */\n currentPage: number;\n /** Called when the page changes */\n onPageChange?: (page: number) => void;\n /** Label for the nav landmark. @default \"Pagination\" */\n ariaLabel?: string;\n /** Label for the previous button. @default \"Previous page\" */\n previousLabel?: string;\n /** Label for the next button. @default \"Next page\" */\n nextLabel?: string;\n /** Generates the aria-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\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\",\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-body-200 text-xs\"\n aria-hidden=\"true\"\n >\n &hellip;\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 transition-colors\",\n page === currentPage\n ? \"bg-neutral-400 text-body-300\"\n : \"bg-neutral-100 text-body-100 hover:bg-neutral-200\",\n )}\n >\n {page}\n </button>\n ),\n )}\n </div>\n )}\n\n {variant === \"dots\" && (\n <div className=\"flex items-center gap-2\">\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={cn(\n \"cursor-pointer rounded-full transition-all\",\n page === currentPage\n ? \"size-2 bg-neutral-400\"\n : \"size-1.5 bg-neutral-200 hover:bg-neutral-250\",\n )}\n />\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":["React","jsxs","cn","jsx","IconButton","ChevronLeftIcon","ChevronRightIcon"],"mappings":"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"}
1
+ {"version":3,"file":"Pagination.cjs","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\nexport type PaginationVariant = \"default\" | \"dots\";\n\nexport interface PaginationProps extends Omit<React.HTMLAttributes<HTMLElement>, \"onChange\"> {\n /** Visual style variant */\n variant?: PaginationVariant;\n /** Total number of pages */\n totalPages: number;\n /** Current active page (1-indexed) */\n currentPage: number;\n /** Called when the page changes */\n onPageChange?: (page: number) => void;\n /** Label for the nav landmark. @default \"Pagination\" */\n ariaLabel?: string;\n /** Label for the previous button. @default \"Previous page\" */\n previousLabel?: string;\n /** Label for the next button. @default \"Next page\" */\n nextLabel?: string;\n /** Generates the aria-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\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\",\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-body-200 text-xs\"\n aria-hidden=\"true\"\n >\n &hellip;\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 motion-safe:transition-colors motion-safe:duration-150 focus-visible:shadow-focus-ring focus-visible:outline-none\",\n page === currentPage\n ? \"bg-neutral-400 text-body-300\"\n : \"bg-neutral-100 text-body-100 hover:bg-neutral-200\",\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-400\"\n : \"size-1.5 bg-neutral-200 hover:bg-neutral-250\",\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":["React","jsxs","cn","jsx","IconButton","ChevronLeftIcon","ChevronRightIcon"],"mappings":"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"}