@fanvue/ui 1.1.1 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/README.md +5 -1
  2. package/dist/cjs/components/Alert/Alert.cjs +16 -6
  3. package/dist/cjs/components/Alert/Alert.cjs.map +1 -1
  4. package/dist/cjs/components/Avatar/Avatar.cjs.map +1 -1
  5. package/dist/cjs/components/Badge/Badge.cjs.map +1 -1
  6. package/dist/cjs/components/Button/Button.cjs +11 -11
  7. package/dist/cjs/components/Button/Button.cjs.map +1 -1
  8. package/dist/cjs/components/Checkbox/Checkbox.cjs +7 -1
  9. package/dist/cjs/components/Checkbox/Checkbox.cjs.map +1 -1
  10. package/dist/cjs/components/Chip/Chip.cjs +3 -3
  11. package/dist/cjs/components/Chip/Chip.cjs.map +1 -1
  12. package/dist/cjs/components/Count/Count.cjs.map +1 -1
  13. package/dist/cjs/components/DatePicker/DatePicker.cjs +30 -7
  14. package/dist/cjs/components/DatePicker/DatePicker.cjs.map +1 -1
  15. package/dist/cjs/components/Divider/Divider.cjs.map +1 -1
  16. package/dist/cjs/components/IconButton/IconButton.cjs +23 -19
  17. package/dist/cjs/components/IconButton/IconButton.cjs.map +1 -1
  18. package/dist/cjs/components/Icons/ArrowRightIcon.cjs.map +1 -1
  19. package/dist/cjs/components/Icons/ArrowUpRightIcon.cjs.map +1 -1
  20. package/dist/cjs/components/Icons/CheckCircleIcon.cjs.map +1 -1
  21. package/dist/cjs/components/Icons/ChevronLeftIcon.cjs.map +1 -1
  22. package/dist/cjs/components/Icons/ChevronRightIcon.cjs.map +1 -1
  23. package/dist/cjs/components/Icons/CloseIcon.cjs.map +1 -1
  24. package/dist/cjs/components/Icons/CrossIcon.cjs.map +1 -1
  25. package/dist/cjs/components/Icons/CrownIcon.cjs.map +1 -1
  26. package/dist/cjs/components/Icons/ErrorCircleIcon.cjs.map +1 -1
  27. package/dist/cjs/components/Icons/ErrorIcon.cjs.map +1 -1
  28. package/dist/cjs/components/Icons/EyeIcon.cjs +63 -0
  29. package/dist/cjs/components/Icons/EyeIcon.cjs.map +1 -0
  30. package/dist/cjs/components/Icons/FireIcon.cjs.map +1 -1
  31. package/dist/cjs/components/Icons/HomeIcon.cjs.map +1 -1
  32. package/dist/cjs/components/Icons/InfoCircleIcon.cjs.map +1 -1
  33. package/dist/cjs/components/Icons/InfoIcon.cjs.map +1 -1
  34. package/dist/cjs/components/Icons/MicrophoneIcon.cjs.map +1 -1
  35. package/dist/cjs/components/Icons/PlusIcon.cjs.map +1 -1
  36. package/dist/cjs/components/Icons/SpinnerIcon.cjs.map +1 -1
  37. package/dist/cjs/components/Icons/StopIcon.cjs.map +1 -1
  38. package/dist/cjs/components/Icons/SuccessIcon.cjs.map +1 -1
  39. package/dist/cjs/components/Icons/WarningIcon.cjs.map +1 -1
  40. package/dist/cjs/components/Icons/WarningTriangleIcon.cjs.map +1 -1
  41. package/dist/cjs/components/Logo/Logo.cjs +11 -11
  42. package/dist/cjs/components/Logo/Logo.cjs.map +1 -1
  43. package/dist/cjs/components/Pagination/Pagination.cjs +12 -6
  44. package/dist/cjs/components/Pagination/Pagination.cjs.map +1 -1
  45. package/dist/cjs/components/Pill/Pill.cjs.map +1 -1
  46. package/dist/cjs/components/ProgressBar/ProgressBar.cjs.map +1 -1
  47. package/dist/cjs/components/Radio/Radio.cjs +1 -1
  48. package/dist/cjs/components/Radio/Radio.cjs.map +1 -1
  49. package/dist/cjs/components/RadioGroup/RadioGroup.cjs.map +1 -1
  50. package/dist/cjs/components/Slider/Slider.cjs.map +1 -1
  51. package/dist/cjs/components/Slider/SliderThumb.cjs +1 -0
  52. package/dist/cjs/components/Slider/SliderThumb.cjs.map +1 -1
  53. package/dist/cjs/components/Snackbar/Snackbar.cjs +12 -5
  54. package/dist/cjs/components/Snackbar/Snackbar.cjs.map +1 -1
  55. package/dist/cjs/components/Switch/Switch.cjs +1 -0
  56. package/dist/cjs/components/Switch/Switch.cjs.map +1 -1
  57. package/dist/cjs/components/SwitchField/SwitchField.cjs +6 -4
  58. package/dist/cjs/components/SwitchField/SwitchField.cjs.map +1 -1
  59. package/dist/cjs/components/SwitchToggle/SwitchToggle.cjs +48 -31
  60. package/dist/cjs/components/SwitchToggle/SwitchToggle.cjs.map +1 -1
  61. package/dist/cjs/components/Tabs/Tabs.cjs.map +1 -1
  62. package/dist/cjs/components/Tabs/TabsContent.cjs.map +1 -1
  63. package/dist/cjs/components/Tabs/TabsList.cjs.map +1 -1
  64. package/dist/cjs/components/Tabs/TabsTrigger.cjs +3 -0
  65. package/dist/cjs/components/Tabs/TabsTrigger.cjs.map +1 -1
  66. package/dist/cjs/components/TextField/TextField.cjs +164 -0
  67. package/dist/cjs/components/TextField/TextField.cjs.map +1 -0
  68. package/dist/cjs/components/Toast/Toast.cjs +4 -3
  69. package/dist/cjs/components/Toast/Toast.cjs.map +1 -1
  70. package/dist/cjs/components/Tooltip/Tooltip.cjs +50 -0
  71. package/dist/cjs/components/Tooltip/Tooltip.cjs.map +1 -0
  72. package/dist/cjs/index.cjs +9 -0
  73. package/dist/cjs/index.cjs.map +1 -1
  74. package/dist/components/Alert/Alert.mjs +16 -6
  75. package/dist/components/Alert/Alert.mjs.map +1 -1
  76. package/dist/components/Avatar/Avatar.mjs.map +1 -1
  77. package/dist/components/Badge/Badge.mjs.map +1 -1
  78. package/dist/components/Button/Button.mjs +11 -11
  79. package/dist/components/Button/Button.mjs.map +1 -1
  80. package/dist/components/Checkbox/Checkbox.mjs +7 -1
  81. package/dist/components/Checkbox/Checkbox.mjs.map +1 -1
  82. package/dist/components/Chip/Chip.mjs +3 -3
  83. package/dist/components/Chip/Chip.mjs.map +1 -1
  84. package/dist/components/Count/Count.mjs.map +1 -1
  85. package/dist/components/DatePicker/DatePicker.mjs +30 -7
  86. package/dist/components/DatePicker/DatePicker.mjs.map +1 -1
  87. package/dist/components/Divider/Divider.mjs.map +1 -1
  88. package/dist/components/IconButton/IconButton.mjs +23 -19
  89. package/dist/components/IconButton/IconButton.mjs.map +1 -1
  90. package/dist/components/Icons/ArrowRightIcon.mjs.map +1 -1
  91. package/dist/components/Icons/ArrowUpRightIcon.mjs.map +1 -1
  92. package/dist/components/Icons/CheckCircleIcon.mjs.map +1 -1
  93. package/dist/components/Icons/ChevronLeftIcon.mjs.map +1 -1
  94. package/dist/components/Icons/ChevronRightIcon.mjs.map +1 -1
  95. package/dist/components/Icons/CloseIcon.mjs.map +1 -1
  96. package/dist/components/Icons/CrossIcon.mjs.map +1 -1
  97. package/dist/components/Icons/CrownIcon.mjs.map +1 -1
  98. package/dist/components/Icons/ErrorCircleIcon.mjs.map +1 -1
  99. package/dist/components/Icons/ErrorIcon.mjs.map +1 -1
  100. package/dist/components/Icons/EyeIcon.mjs +46 -0
  101. package/dist/components/Icons/EyeIcon.mjs.map +1 -0
  102. package/dist/components/Icons/FireIcon.mjs.map +1 -1
  103. package/dist/components/Icons/HomeIcon.mjs.map +1 -1
  104. package/dist/components/Icons/InfoCircleIcon.mjs.map +1 -1
  105. package/dist/components/Icons/InfoIcon.mjs.map +1 -1
  106. package/dist/components/Icons/MicrophoneIcon.mjs.map +1 -1
  107. package/dist/components/Icons/PlusIcon.mjs.map +1 -1
  108. package/dist/components/Icons/SpinnerIcon.mjs.map +1 -1
  109. package/dist/components/Icons/StopIcon.mjs.map +1 -1
  110. package/dist/components/Icons/SuccessIcon.mjs.map +1 -1
  111. package/dist/components/Icons/WarningIcon.mjs.map +1 -1
  112. package/dist/components/Icons/WarningTriangleIcon.mjs.map +1 -1
  113. package/dist/components/Logo/Logo.mjs +11 -11
  114. package/dist/components/Logo/Logo.mjs.map +1 -1
  115. package/dist/components/Pagination/Pagination.mjs +12 -6
  116. package/dist/components/Pagination/Pagination.mjs.map +1 -1
  117. package/dist/components/Pill/Pill.mjs.map +1 -1
  118. package/dist/components/ProgressBar/ProgressBar.mjs.map +1 -1
  119. package/dist/components/Radio/Radio.mjs +1 -1
  120. package/dist/components/Radio/Radio.mjs.map +1 -1
  121. package/dist/components/RadioGroup/RadioGroup.mjs.map +1 -1
  122. package/dist/components/Slider/Slider.mjs.map +1 -1
  123. package/dist/components/Slider/SliderThumb.mjs +1 -0
  124. package/dist/components/Slider/SliderThumb.mjs.map +1 -1
  125. package/dist/components/Snackbar/Snackbar.mjs +12 -5
  126. package/dist/components/Snackbar/Snackbar.mjs.map +1 -1
  127. package/dist/components/Switch/Switch.mjs +1 -0
  128. package/dist/components/Switch/Switch.mjs.map +1 -1
  129. package/dist/components/SwitchField/SwitchField.mjs +6 -4
  130. package/dist/components/SwitchField/SwitchField.mjs.map +1 -1
  131. package/dist/components/SwitchToggle/SwitchToggle.mjs +48 -31
  132. package/dist/components/SwitchToggle/SwitchToggle.mjs.map +1 -1
  133. package/dist/components/Tabs/Tabs.mjs.map +1 -1
  134. package/dist/components/Tabs/TabsContent.mjs.map +1 -1
  135. package/dist/components/Tabs/TabsList.mjs.map +1 -1
  136. package/dist/components/Tabs/TabsTrigger.mjs +3 -0
  137. package/dist/components/Tabs/TabsTrigger.mjs.map +1 -1
  138. package/dist/components/TextField/TextField.mjs +147 -0
  139. package/dist/components/TextField/TextField.mjs.map +1 -0
  140. package/dist/components/Toast/Toast.mjs +4 -3
  141. package/dist/components/Toast/Toast.mjs.map +1 -1
  142. package/dist/components/Tooltip/Tooltip.mjs +32 -0
  143. package/dist/components/Tooltip/Tooltip.mjs.map +1 -0
  144. package/dist/index.d.ts +569 -133
  145. package/dist/index.mjs +9 -0
  146. package/dist/index.mjs.map +1 -1
  147. package/dist/styles/theme.css +6 -0
  148. package/package.json +4 -3
@@ -1 +1 @@
1
- {"version":3,"file":"Slider.mjs","sources":["../../../src/components/Slider/Slider.tsx"],"sourcesContent":["import * as SliderPrimitive from \"@radix-ui/react-slider\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { SliderLayout } from \"./SliderLayout\";\nimport { SliderThumb } from \"./SliderThumb\";\n\nexport type SliderLabelPosition = \"top\" | \"left\";\n\nexport interface SliderProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>,\n \"asChild\" | \"children\"\n > {\n /** Label text displayed alongside the slider */\n label?: string;\n /** Position of the label relative to the slider track */\n labelPosition?: SliderLabelPosition;\n /** Text shown at the minimum end of the track */\n minLabel?: string;\n /** Text shown at the maximum end of the track */\n maxLabel?: string;\n /** Whether to show a tooltip with the current value above the thumb */\n showTooltip?: boolean;\n /** Override the displayed tooltip value (e.g. for formatting) */\n formatTooltip?: (value: number) => string;\n}\n\nexport const Slider = React.forwardRef<\n React.ComponentRef<typeof SliderPrimitive.Root>,\n SliderProps\n>(\n (\n {\n className,\n label,\n labelPosition = \"top\",\n minLabel,\n maxLabel,\n showTooltip = false,\n formatTooltip,\n disabled,\n value,\n defaultValue,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n ...props\n },\n ref,\n ) => {\n const labelId = React.useId();\n const thumbCount = value?.length ?? defaultValue?.length ?? 1;\n const resolvedLabelledBy = ariaLabelledBy ?? (label ? labelId : undefined);\n const resolvedAriaLabel = !resolvedLabelledBy ? ariaLabel : undefined;\n\n const sliderTrack = (\n <SliderPrimitive.Root\n ref={ref}\n disabled={disabled}\n value={value}\n defaultValue={defaultValue}\n aria-label={resolvedAriaLabel}\n aria-labelledby={resolvedLabelledBy}\n className={cn(\n \"group/slider relative flex w-full touch-none select-none items-center\",\n disabled && \"pointer-events-none opacity-50\",\n className,\n )}\n {...props}\n >\n <SliderPrimitive.Track className=\"relative h-3 w-full overflow-hidden rounded-full border border-neutral-100 bg-neutral-100\">\n <SliderPrimitive.Range className=\"absolute h-full rounded-full bg-brand-green-500\" />\n </SliderPrimitive.Track>\n\n {Array.from({ length: thumbCount }, (_, i) => (\n <SliderThumb\n // biome-ignore lint/suspicious/noArrayIndexKey: thumbs are fixed-count and never reorder\n key={i}\n showTooltip={showTooltip}\n formatTooltip={formatTooltip}\n index={i}\n aria-label={resolvedAriaLabel}\n aria-labelledby={resolvedLabelledBy}\n />\n ))}\n </SliderPrimitive.Root>\n );\n\n const hasLayout = Boolean(label || minLabel || maxLabel);\n\n if (!hasLayout) return sliderTrack;\n\n return (\n <SliderLayout\n label={label}\n labelId={labelId}\n labelPosition={labelPosition}\n minLabel={minLabel}\n maxLabel={maxLabel}\n >\n {sliderTrack}\n </SliderLayout>\n );\n },\n);\n\nSlider.displayName = \"Slider\";\n"],"names":[],"mappings":";;;;;;;AA2BO,MAAM,SAAS,MAAM;AAAA,EAI1B,CACE;AAAA,IACE;AAAA,IACA;AAAA,IACA,gBAAgB;AAAA,IAChB;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd,mBAAmB;AAAA,IACnB,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,UAAU,MAAM,MAAA;AACtB,UAAM,aAAa,OAAO,UAAU,cAAc,UAAU;AAC5D,UAAM,qBAAqB,mBAAmB,QAAQ,UAAU;AAChE,UAAM,oBAAoB,CAAC,qBAAqB,YAAY;AAE5D,UAAM,cACJ;AAAA,MAAC,gBAAgB;AAAA,MAAhB;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,cAAY;AAAA,QACZ,mBAAiB;AAAA,QACjB,WAAW;AAAA,UACT;AAAA,UACA,YAAY;AAAA,UACZ;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEJ,UAAA;AAAA,UAAA,oBAAC,gBAAgB,OAAhB,EAAsB,WAAU,6FAC/B,UAAA,oBAAC,gBAAgB,OAAhB,EAAsB,WAAU,kDAAA,CAAkD,EAAA,CACrF;AAAA,UAEC,MAAM,KAAK,EAAE,QAAQ,cAAc,CAAC,GAAG,MACtC;AAAA,YAAC;AAAA,YAAA;AAAA,cAGC;AAAA,cACA;AAAA,cACA,OAAO;AAAA,cACP,cAAY;AAAA,cACZ,mBAAiB;AAAA,YAAA;AAAA,YALZ;AAAA,UAAA,CAOR;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAIL,UAAM,YAAY,QAAQ,SAAS,YAAY,QAAQ;AAEvD,QAAI,CAAC,UAAW,QAAO;AAEvB,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QAEC,UAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAGP;AACF;AAEA,OAAO,cAAc;"}
1
+ {"version":3,"file":"Slider.mjs","sources":["../../../src/components/Slider/Slider.tsx"],"sourcesContent":["import * as SliderPrimitive from \"@radix-ui/react-slider\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { SliderLayout } from \"./SliderLayout\";\nimport { SliderThumb } from \"./SliderThumb\";\n\n/** Position of the slider label relative to the track. */\nexport type SliderLabelPosition = \"top\" | \"left\";\n\nexport interface SliderProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>,\n \"asChild\" | \"children\"\n > {\n /** Label text displayed alongside the slider. */\n label?: string;\n /** Position of the label relative to the slider track. @default \"top\" */\n labelPosition?: SliderLabelPosition;\n /** Text shown at the minimum end of the track. */\n minLabel?: string;\n /** Text shown at the maximum end of the track. */\n maxLabel?: string;\n /** Whether to show a tooltip with the current value above the thumb. @default false */\n showTooltip?: boolean;\n /** Custom formatter for the tooltip value (e.g. to add units or format numbers). */\n formatTooltip?: (value: number) => string;\n}\n\n/**\n * A range input for selecting one or more numeric values along a track.\n * Supports single and multi-thumb modes, optional labels, and a value tooltip.\n *\n * Built on Radix UI `Slider`.\n *\n * @example\n * ```tsx\n * <Slider\n * label=\"Volume\"\n * min={0}\n * max={100}\n * defaultValue={[50]}\n * showTooltip\n * />\n * ```\n */\nexport const Slider = React.forwardRef<\n React.ComponentRef<typeof SliderPrimitive.Root>,\n SliderProps\n>(\n (\n {\n className,\n label,\n labelPosition = \"top\",\n minLabel,\n maxLabel,\n showTooltip = false,\n formatTooltip,\n disabled,\n value,\n defaultValue,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n ...props\n },\n ref,\n ) => {\n const labelId = React.useId();\n const thumbCount = value?.length ?? defaultValue?.length ?? 1;\n const resolvedLabelledBy = ariaLabelledBy ?? (label ? labelId : undefined);\n const resolvedAriaLabel = !resolvedLabelledBy ? ariaLabel : undefined;\n\n const sliderTrack = (\n <SliderPrimitive.Root\n ref={ref}\n disabled={disabled}\n value={value}\n defaultValue={defaultValue}\n aria-label={resolvedAriaLabel}\n aria-labelledby={resolvedLabelledBy}\n className={cn(\n \"group/slider relative flex w-full touch-none select-none items-center\",\n disabled && \"pointer-events-none opacity-50\",\n className,\n )}\n {...props}\n >\n <SliderPrimitive.Track className=\"relative h-3 w-full overflow-hidden rounded-full border border-neutral-100 bg-neutral-100\">\n <SliderPrimitive.Range className=\"absolute h-full rounded-full bg-brand-green-500\" />\n </SliderPrimitive.Track>\n\n {Array.from({ length: thumbCount }, (_, i) => (\n <SliderThumb\n // biome-ignore lint/suspicious/noArrayIndexKey: thumbs are fixed-count and never reorder\n key={i}\n showTooltip={showTooltip}\n formatTooltip={formatTooltip}\n index={i}\n aria-label={resolvedAriaLabel}\n aria-labelledby={resolvedLabelledBy}\n />\n ))}\n </SliderPrimitive.Root>\n );\n\n const hasLayout = Boolean(label || minLabel || maxLabel);\n\n if (!hasLayout) return sliderTrack;\n\n return (\n <SliderLayout\n label={label}\n labelId={labelId}\n labelPosition={labelPosition}\n minLabel={minLabel}\n maxLabel={maxLabel}\n >\n {sliderTrack}\n </SliderLayout>\n );\n },\n);\n\nSlider.displayName = \"Slider\";\n"],"names":[],"mappings":";;;;;;;AA6CO,MAAM,SAAS,MAAM;AAAA,EAI1B,CACE;AAAA,IACE;AAAA,IACA;AAAA,IACA,gBAAgB;AAAA,IAChB;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd,mBAAmB;AAAA,IACnB,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,UAAU,MAAM,MAAA;AACtB,UAAM,aAAa,OAAO,UAAU,cAAc,UAAU;AAC5D,UAAM,qBAAqB,mBAAmB,QAAQ,UAAU;AAChE,UAAM,oBAAoB,CAAC,qBAAqB,YAAY;AAE5D,UAAM,cACJ;AAAA,MAAC,gBAAgB;AAAA,MAAhB;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,cAAY;AAAA,QACZ,mBAAiB;AAAA,QACjB,WAAW;AAAA,UACT;AAAA,UACA,YAAY;AAAA,UACZ;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEJ,UAAA;AAAA,UAAA,oBAAC,gBAAgB,OAAhB,EAAsB,WAAU,6FAC/B,UAAA,oBAAC,gBAAgB,OAAhB,EAAsB,WAAU,kDAAA,CAAkD,EAAA,CACrF;AAAA,UAEC,MAAM,KAAK,EAAE,QAAQ,cAAc,CAAC,GAAG,MACtC;AAAA,YAAC;AAAA,YAAA;AAAA,cAGC;AAAA,cACA;AAAA,cACA,OAAO;AAAA,cACP,cAAY;AAAA,cACZ,mBAAiB;AAAA,YAAA;AAAA,YALZ;AAAA,UAAA,CAOR;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAIL,UAAM,YAAY,QAAQ,SAAS,YAAY,QAAQ;AAEvD,QAAI,CAAC,UAAW,QAAO;AAEvB,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QAEC,UAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAGP;AACF;AAEA,OAAO,cAAc;"}
@@ -39,6 +39,7 @@ function SliderThumb({
39
39
  "flex size-6 items-center justify-center rounded-full border border-neutral-100 bg-background-inverse-solid shadow-sm",
40
40
  "transition-shadow duration-150",
41
41
  "hover:ring-2 hover:ring-brand-green-500",
42
+ "not-data-disabled:active:ring-2 not-data-disabled:active:ring-brand-green-500",
42
43
  "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",
43
44
  "data-disabled:cursor-not-allowed"
44
45
  ),
@@ -1 +1 @@
1
- {"version":3,"file":"SliderThumb.mjs","sources":["../../../src/components/Slider/SliderThumb.tsx"],"sourcesContent":["import * as SliderPrimitive from \"@radix-ui/react-slider\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\ninterface SliderThumbProps {\n showTooltip: boolean;\n formatTooltip?: (value: number) => string;\n index: number;\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n}\n\nexport function SliderThumb({\n showTooltip,\n formatTooltip,\n index,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n}: SliderThumbProps) {\n const thumbRef = React.useCallback(\n (el: HTMLSpanElement | null) => {\n if (!el || !showTooltip) return;\n syncTooltipText(el, formatTooltip);\n },\n [showTooltip, formatTooltip],\n );\n\n return (\n <SliderPrimitive.Thumb\n ref={thumbRef}\n data-index={index}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n onPointerDown={(e) => {\n if (showTooltip) syncTooltipText(e.currentTarget, formatTooltip);\n }}\n onPointerMove={(e) => {\n if (showTooltip) syncTooltipText(e.currentTarget, formatTooltip);\n }}\n onKeyDown={(e) => {\n if (showTooltip) {\n requestAnimationFrame(() => syncTooltipText(e.currentTarget, formatTooltip));\n }\n }}\n className={cn(\n \"flex size-6 items-center justify-center rounded-full border border-neutral-100 bg-background-inverse-solid shadow-sm\",\n \"transition-shadow duration-150\",\n \"hover:ring-2 hover:ring-brand-green-500\",\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 \"data-disabled:cursor-not-allowed\",\n )}\n >\n <span className=\"block size-3 rounded-full bg-brand-green-500 shadow-[inset_0px_1px_2px_0px_rgba(0,0,0,0.1)]\" />\n\n {showTooltip && (\n <span\n role=\"tooltip\"\n data-slider-tooltip\n className=\"typography-caption-semibold pointer-events-none absolute bottom-full mb-2 rounded-3xl bg-background-solid px-2 py-1 text-background-inverse-solid shadow-sm\"\n />\n )}\n </SliderPrimitive.Thumb>\n );\n}\n\nfunction syncTooltipText(thumb: HTMLElement, formatTooltip?: (value: number) => string) {\n const raw = thumb.getAttribute(\"aria-valuenow\");\n const tooltip = thumb.querySelector<HTMLSpanElement>(\"[data-slider-tooltip]\");\n if (raw == null || !tooltip) return;\n const num = Number(raw);\n tooltip.textContent = formatTooltip ? formatTooltip(num) : String(num);\n}\n"],"names":[],"mappings":";;;;;AAYO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,mBAAmB;AACrB,GAAqB;AACnB,QAAM,WAAW,MAAM;AAAA,IACrB,CAAC,OAA+B;AAC9B,UAAI,CAAC,MAAM,CAAC,YAAa;AACzB,sBAAgB,IAAI,aAAa;AAAA,IACnC;AAAA,IACA,CAAC,aAAa,aAAa;AAAA,EAAA;AAG7B,SACE;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC,KAAK;AAAA,MACL,cAAY;AAAA,MACZ,cAAY;AAAA,MACZ,mBAAiB;AAAA,MACjB,eAAe,CAAC,MAAM;AACpB,YAAI,YAAa,iBAAgB,EAAE,eAAe,aAAa;AAAA,MACjE;AAAA,MACA,eAAe,CAAC,MAAM;AACpB,YAAI,YAAa,iBAAgB,EAAE,eAAe,aAAa;AAAA,MACjE;AAAA,MACA,WAAW,CAAC,MAAM;AAChB,YAAI,aAAa;AACf,gCAAsB,MAAM,gBAAgB,EAAE,eAAe,aAAa,CAAC;AAAA,QAC7E;AAAA,MACF;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,MAGF,UAAA;AAAA,QAAA,oBAAC,QAAA,EAAK,WAAU,8FAAA,CAA8F;AAAA,QAE7G,eACC;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,uBAAmB;AAAA,YACnB,WAAU;AAAA,UAAA;AAAA,QAAA;AAAA,MACZ;AAAA,IAAA;AAAA,EAAA;AAIR;AAEA,SAAS,gBAAgB,OAAoB,eAA2C;AACtF,QAAM,MAAM,MAAM,aAAa,eAAe;AAC9C,QAAM,UAAU,MAAM,cAA+B,uBAAuB;AAC5E,MAAI,OAAO,QAAQ,CAAC,QAAS;AAC7B,QAAM,MAAM,OAAO,GAAG;AACtB,UAAQ,cAAc,gBAAgB,cAAc,GAAG,IAAI,OAAO,GAAG;AACvE;"}
1
+ {"version":3,"file":"SliderThumb.mjs","sources":["../../../src/components/Slider/SliderThumb.tsx"],"sourcesContent":["import * as SliderPrimitive from \"@radix-ui/react-slider\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\ninterface SliderThumbProps {\n showTooltip: boolean;\n formatTooltip?: (value: number) => string;\n index: number;\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n}\n\nexport function SliderThumb({\n showTooltip,\n formatTooltip,\n index,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n}: SliderThumbProps) {\n const thumbRef = React.useCallback(\n (el: HTMLSpanElement | null) => {\n if (!el || !showTooltip) return;\n syncTooltipText(el, formatTooltip);\n },\n [showTooltip, formatTooltip],\n );\n\n return (\n <SliderPrimitive.Thumb\n ref={thumbRef}\n data-index={index}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n onPointerDown={(e) => {\n if (showTooltip) syncTooltipText(e.currentTarget, formatTooltip);\n }}\n onPointerMove={(e) => {\n if (showTooltip) syncTooltipText(e.currentTarget, formatTooltip);\n }}\n onKeyDown={(e) => {\n if (showTooltip) {\n requestAnimationFrame(() => syncTooltipText(e.currentTarget, formatTooltip));\n }\n }}\n className={cn(\n \"flex size-6 items-center justify-center rounded-full border border-neutral-100 bg-background-inverse-solid shadow-sm\",\n \"transition-shadow duration-150\",\n \"hover:ring-2 hover:ring-brand-green-500\",\n \"not-data-disabled:active:ring-2 not-data-disabled:active:ring-brand-green-500\",\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 \"data-disabled:cursor-not-allowed\",\n )}\n >\n <span className=\"block size-3 rounded-full bg-brand-green-500 shadow-[inset_0px_1px_2px_0px_rgba(0,0,0,0.1)]\" />\n\n {showTooltip && (\n <span\n role=\"tooltip\"\n data-slider-tooltip\n className=\"typography-caption-semibold pointer-events-none absolute bottom-full mb-2 rounded-3xl bg-background-solid px-2 py-1 text-background-inverse-solid shadow-sm\"\n />\n )}\n </SliderPrimitive.Thumb>\n );\n}\n\nfunction syncTooltipText(thumb: HTMLElement, formatTooltip?: (value: number) => string) {\n const raw = thumb.getAttribute(\"aria-valuenow\");\n const tooltip = thumb.querySelector<HTMLSpanElement>(\"[data-slider-tooltip]\");\n if (raw == null || !tooltip) return;\n const num = Number(raw);\n tooltip.textContent = formatTooltip ? formatTooltip(num) : String(num);\n}\n"],"names":[],"mappings":";;;;;AAYO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,mBAAmB;AACrB,GAAqB;AACnB,QAAM,WAAW,MAAM;AAAA,IACrB,CAAC,OAA+B;AAC9B,UAAI,CAAC,MAAM,CAAC,YAAa;AACzB,sBAAgB,IAAI,aAAa;AAAA,IACnC;AAAA,IACA,CAAC,aAAa,aAAa;AAAA,EAAA;AAG7B,SACE;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC,KAAK;AAAA,MACL,cAAY;AAAA,MACZ,cAAY;AAAA,MACZ,mBAAiB;AAAA,MACjB,eAAe,CAAC,MAAM;AACpB,YAAI,YAAa,iBAAgB,EAAE,eAAe,aAAa;AAAA,MACjE;AAAA,MACA,eAAe,CAAC,MAAM;AACpB,YAAI,YAAa,iBAAgB,EAAE,eAAe,aAAa;AAAA,MACjE;AAAA,MACA,WAAW,CAAC,MAAM;AAChB,YAAI,aAAa;AACf,gCAAsB,MAAM,gBAAgB,EAAE,eAAe,aAAa,CAAC;AAAA,QAC7E;AAAA,MACF;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,MAGF,UAAA;AAAA,QAAA,oBAAC,QAAA,EAAK,WAAU,8FAAA,CAA8F;AAAA,QAE7G,eACC;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,uBAAmB;AAAA,YACnB,WAAU;AAAA,UAAA;AAAA,QAAA;AAAA,MACZ;AAAA,IAAA;AAAA,EAAA;AAIR;AAEA,SAAS,gBAAgB,OAAoB,eAA2C;AACtF,QAAM,MAAM,MAAM,aAAa,eAAe;AAC9C,QAAM,UAAU,MAAM,cAA+B,uBAAuB;AAC5E,MAAI,OAAO,QAAQ,CAAC,QAAS;AAC7B,QAAM,MAAM,OAAO,GAAG;AACtB,UAAQ,cAAc,gBAAgB,cAAc,GAAG,IAAI,OAAO,GAAG;AACvE;"}
@@ -3,17 +3,22 @@ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { cn } from "../../utils/cn.mjs";
5
5
  import { Button } from "../Button/Button.mjs";
6
+ import { IconButton } from "../IconButton/IconButton.mjs";
6
7
  import { CrossIcon } from "../Icons/CrossIcon.mjs";
7
- function CloseButton({ onClose, className }) {
8
+ function CloseButton({
9
+ onClose,
10
+ className,
11
+ closeLabel = "Close snackbar"
12
+ }) {
8
13
  return /* @__PURE__ */ jsx(
9
- Button,
14
+ IconButton,
10
15
  {
11
16
  variant: "tertiary",
12
17
  size: "24",
13
18
  onClick: onClose,
14
- className: cn("h-auto shrink-0", className),
15
- "aria-label": "Close snackbar",
16
- children: /* @__PURE__ */ jsx(CrossIcon, {})
19
+ icon: /* @__PURE__ */ jsx(CrossIcon, {}),
20
+ className: cn("shrink-0", className),
21
+ "aria-label": closeLabel
17
22
  }
18
23
  );
19
24
  }
@@ -118,6 +123,7 @@ const Snackbar = React.forwardRef(
118
123
  secondarySlot,
119
124
  closable = false,
120
125
  onClose,
126
+ closeLabel,
121
127
  children,
122
128
  ...props
123
129
  }, ref) => {
@@ -182,6 +188,7 @@ const Snackbar = React.forwardRef(
182
188
  CloseButton,
183
189
  {
184
190
  onClose,
191
+ closeLabel,
185
192
  className: variant === "welcome" ? "absolute top-2 right-2" : void 0
186
193
  }
187
194
  )
@@ -1 +1 @@
1
- {"version":3,"file":"Snackbar.mjs","sources":["../../../src/components/Snackbar/Snackbar.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 SnackbarVariant = \"default\" | \"vipEarn\" | \"welcome\";\n\nexport interface SnackbarProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"title\"> {\n /** Snackbar layout variant */\n variant?: SnackbarVariant;\n /** Left icon element */\n icon?: React.ReactNode;\n /** Title text */\n title?: React.ReactNode;\n /** Description text */\n description?: React.ReactNode;\n /** Whether to show the actions section */\n showActions?: boolean;\n /** Primary CTA label (renders a default Button) */\n primaryLabel?: string;\n /** Primary CTA click handler (used with primaryLabel) */\n primaryOnClick?: () => void;\n /** Custom element to render as primary CTA (overrides primaryLabel/primaryOnClick) */\n primarySlot?: React.ReactNode;\n /** Secondary CTA label (renders a default Button) */\n secondaryLabel?: string;\n /** Secondary CTA click handler (used with secondaryLabel) */\n secondaryOnClick?: () => void;\n /** Custom element to render as secondary CTA (overrides secondaryLabel/secondaryOnClick) */\n secondarySlot?: React.ReactNode;\n /** Show close button */\n closable?: boolean;\n /** Close button click handler */\n onClose?: () => void;\n}\n\nfunction CloseButton({ onClose, className }: { onClose?: () => void; className?: string }) {\n return (\n <Button\n variant=\"tertiary\"\n size=\"24\"\n onClick={onClose}\n className={cn(\"h-auto shrink-0\", className)}\n aria-label=\"Close snackbar\"\n >\n <CrossIcon />\n </Button>\n );\n}\n\n/**\n * Resolves a CTA slot. If a custom slot is provided it takes precedence,\n * otherwise a default Button is rendered from label + onClick.\n */\nfunction resolveCtaSlot(\n slot: React.ReactNode | undefined,\n label: string | undefined,\n onClick: (() => void) | undefined,\n buttonProps: React.ComponentProps<typeof Button>,\n): React.ReactNode | null {\n if (slot) return slot;\n if (label) {\n return (\n <Button onClick={onClick} {...buttonProps}>\n {label}\n </Button>\n );\n }\n return null;\n}\n\nfunction VipEarnContent({\n icon,\n title,\n description,\n showActions,\n primarySlot,\n primaryLabel,\n primaryOnClick,\n}: Pick<\n SnackbarProps,\n | \"icon\"\n | \"title\"\n | \"description\"\n | \"showActions\"\n | \"primarySlot\"\n | \"primaryLabel\"\n | \"primaryOnClick\"\n>) {\n const primary = resolveCtaSlot(primarySlot, primaryLabel, primaryOnClick, {\n variant: \"text\",\n size: \"24\",\n });\n\n return (\n <>\n {icon && (\n <span className=\"flex shrink-0\" aria-hidden=\"true\">\n {icon}\n </span>\n )}\n <div className=\"flex min-w-0 flex-1 flex-col gap-4\">\n <div className=\"flex flex-col\">\n {title && <p className=\"typography-body-1-semibold text-body-100 leading-6\">{title}</p>}\n {description && <p className=\"typography-body-2-regular text-body-200\">{description}</p>}\n </div>\n {showActions && primary && <div className=\"self-start\">{primary}</div>}\n </div>\n </>\n );\n}\n\nfunction WelcomeContent({\n title,\n description,\n showActions,\n primarySlot,\n primaryLabel,\n primaryOnClick,\n secondarySlot,\n secondaryLabel,\n secondaryOnClick,\n}: Pick<\n SnackbarProps,\n | \"title\"\n | \"description\"\n | \"showActions\"\n | \"primarySlot\"\n | \"primaryLabel\"\n | \"primaryOnClick\"\n | \"secondarySlot\"\n | \"secondaryLabel\"\n | \"secondaryOnClick\"\n>) {\n const primary = resolveCtaSlot(primarySlot, primaryLabel, primaryOnClick, {\n variant: \"primary\",\n });\n const secondary = resolveCtaSlot(secondarySlot, secondaryLabel, secondaryOnClick, {\n variant: \"secondary\",\n });\n\n return (\n <>\n <div className=\"flex flex-col items-center gap-2 px-8 text-center text-body-100\">\n {title && <p className=\"typography-heading-4 text-body-100\">{title}</p>}\n {description && <p className=\"typography-body-2-regular text-body-200\">{description}</p>}\n </div>\n {showActions && (primary || secondary) && (\n <div className=\"flex w-full flex-col gap-4 px-8 sm:flex-row sm:*:flex-1\">\n {secondary}\n {primary}\n </div>\n )}\n </>\n );\n}\n\nfunction DefaultContent({\n children,\n showActions,\n primarySlot,\n primaryLabel,\n primaryOnClick,\n secondarySlot,\n secondaryLabel,\n secondaryOnClick,\n}: Pick<\n SnackbarProps,\n | \"children\"\n | \"showActions\"\n | \"primarySlot\"\n | \"primaryLabel\"\n | \"primaryOnClick\"\n | \"secondarySlot\"\n | \"secondaryLabel\"\n | \"secondaryOnClick\"\n>) {\n const primary = resolveCtaSlot(primarySlot, primaryLabel, primaryOnClick, {\n variant: \"primary\",\n size: \"40\",\n });\n const secondary = resolveCtaSlot(secondarySlot, secondaryLabel, secondaryOnClick, {\n variant: \"tertiary\",\n size: \"40\",\n });\n\n return (\n <>\n <div className=\"typography-body-1-medium flex min-w-0 flex-1 items-center self-stretch text-body-100\">\n {children}\n </div>\n {showActions && (primary || secondary) && (\n <div className=\"flex shrink-0 items-start gap-2\">\n {primary}\n {secondary}\n </div>\n )}\n </>\n );\n}\n\nexport const Snackbar = React.forwardRef<HTMLDivElement, SnackbarProps>(\n (\n {\n className,\n variant = \"default\",\n icon,\n title,\n description,\n showActions = true,\n primaryLabel,\n primaryOnClick,\n primarySlot,\n secondaryLabel,\n secondaryOnClick,\n secondarySlot,\n closable = false,\n onClose,\n children,\n ...props\n },\n ref,\n ) => {\n return (\n /* biome-ignore lint/a11y/useSemanticElements: output cannot contain div children; browsers would break the wrapper. */\n <div\n ref={ref}\n role=\"status\"\n data-testid=\"snackbar\"\n className={cn(\n \"flex gap-4 rounded-2xl\",\n (variant === \"default\" || variant === \"vipEarn\") &&\n \"border border-neutral-50 bg-background-200 p-4 backdrop-blur-md\",\n variant === \"default\" && \"flex-wrap items-start\",\n variant === \"vipEarn\" && \"items-start\",\n variant === \"welcome\" &&\n \"relative flex-col items-center bg-background-inverse-solid py-6\",\n className,\n )}\n {...props}\n >\n {variant === \"vipEarn\" && (\n <VipEarnContent\n icon={icon}\n title={title}\n description={description}\n showActions={showActions}\n primarySlot={primarySlot}\n primaryLabel={primaryLabel}\n primaryOnClick={primaryOnClick}\n />\n )}\n {variant === \"welcome\" && (\n <WelcomeContent\n title={title}\n description={description}\n showActions={showActions}\n primarySlot={primarySlot}\n primaryLabel={primaryLabel}\n primaryOnClick={primaryOnClick}\n secondarySlot={secondarySlot}\n secondaryLabel={secondaryLabel}\n secondaryOnClick={secondaryOnClick}\n />\n )}\n {variant === \"default\" && (\n <DefaultContent\n showActions={showActions}\n primarySlot={primarySlot}\n primaryLabel={primaryLabel}\n primaryOnClick={primaryOnClick}\n secondarySlot={secondarySlot}\n secondaryLabel={secondaryLabel}\n secondaryOnClick={secondaryOnClick}\n >\n {children}\n </DefaultContent>\n )}\n {closable && (\n <CloseButton\n onClose={onClose}\n className={variant === \"welcome\" ? \"absolute top-2 right-2\" : undefined}\n />\n )}\n </div>\n );\n },\n);\n\nSnackbar.displayName = \"Snackbar\";\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Snackbar.mjs","sources":["../../../src/components/Snackbar/Snackbar.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Button } from \"../Button/Button\";\nimport { IconButton } from \"../IconButton/IconButton\";\nimport { CrossIcon } from \"../Icons/CrossIcon\";\n\n/** Layout variant of the snackbar. */\nexport type SnackbarVariant = \"default\" | \"vipEarn\" | \"welcome\";\n\nexport interface SnackbarProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"title\"> {\n /** Layout variant of the snackbar. @default \"default\" */\n variant?: SnackbarVariant;\n /** Icon element displayed at the leading edge (used by the `vipEarn` variant). */\n icon?: React.ReactNode;\n /** Title content. */\n title?: React.ReactNode;\n /** Descriptive body text. */\n description?: React.ReactNode;\n /** Whether to show primary/secondary action buttons. @default true */\n showActions?: boolean;\n /** Primary CTA label — renders a default {@link Button}. */\n primaryLabel?: string;\n /** Click handler for the primary CTA (used together with `primaryLabel`). */\n primaryOnClick?: () => void;\n /** Custom element rendered as the primary CTA. Overrides `primaryLabel` / `primaryOnClick`. */\n primarySlot?: React.ReactNode;\n /** Secondary CTA label — renders a default {@link Button}. */\n secondaryLabel?: string;\n /** Click handler for the secondary CTA (used together with `secondaryLabel`). */\n secondaryOnClick?: () => void;\n /** Custom element rendered as the secondary CTA. Overrides `secondaryLabel` / `secondaryOnClick`. */\n secondarySlot?: React.ReactNode;\n /** Whether to show the close button. @default false */\n closable?: boolean;\n /** Callback fired when the close button is clicked. */\n onClose?: () => void;\n /** Accessible label for the close button. @default \"Close snackbar\" */\n closeLabel?: string;\n}\n\nfunction CloseButton({\n onClose,\n className,\n closeLabel = \"Close snackbar\",\n}: {\n onClose?: () => void;\n className?: string;\n closeLabel?: string;\n}) {\n return (\n <IconButton\n variant=\"tertiary\"\n size=\"24\"\n onClick={onClose}\n icon={<CrossIcon />}\n className={cn(\"shrink-0\", className)}\n aria-label={closeLabel}\n />\n );\n}\n\n/**\n * Resolves a CTA slot. If a custom slot is provided it takes precedence,\n * otherwise a default Button is rendered from label + onClick.\n */\nfunction resolveCtaSlot(\n slot: React.ReactNode | undefined,\n label: string | undefined,\n onClick: (() => void) | undefined,\n buttonProps: React.ComponentProps<typeof Button>,\n): React.ReactNode | null {\n if (slot) return slot;\n if (label) {\n return (\n <Button onClick={onClick} {...buttonProps}>\n {label}\n </Button>\n );\n }\n return null;\n}\n\nfunction VipEarnContent({\n icon,\n title,\n description,\n showActions,\n primarySlot,\n primaryLabel,\n primaryOnClick,\n}: Pick<\n SnackbarProps,\n | \"icon\"\n | \"title\"\n | \"description\"\n | \"showActions\"\n | \"primarySlot\"\n | \"primaryLabel\"\n | \"primaryOnClick\"\n>) {\n const primary = resolveCtaSlot(primarySlot, primaryLabel, primaryOnClick, {\n variant: \"text\",\n size: \"24\",\n });\n\n return (\n <>\n {icon && (\n <span className=\"flex shrink-0\" aria-hidden=\"true\">\n {icon}\n </span>\n )}\n <div className=\"flex min-w-0 flex-1 flex-col gap-4\">\n <div className=\"flex flex-col\">\n {title && <p className=\"typography-body-1-semibold text-body-100 leading-6\">{title}</p>}\n {description && <p className=\"typography-body-2-regular text-body-200\">{description}</p>}\n </div>\n {showActions && primary && <div className=\"self-start\">{primary}</div>}\n </div>\n </>\n );\n}\n\nfunction WelcomeContent({\n title,\n description,\n showActions,\n primarySlot,\n primaryLabel,\n primaryOnClick,\n secondarySlot,\n secondaryLabel,\n secondaryOnClick,\n}: Pick<\n SnackbarProps,\n | \"title\"\n | \"description\"\n | \"showActions\"\n | \"primarySlot\"\n | \"primaryLabel\"\n | \"primaryOnClick\"\n | \"secondarySlot\"\n | \"secondaryLabel\"\n | \"secondaryOnClick\"\n>) {\n const primary = resolveCtaSlot(primarySlot, primaryLabel, primaryOnClick, {\n variant: \"primary\",\n });\n const secondary = resolveCtaSlot(secondarySlot, secondaryLabel, secondaryOnClick, {\n variant: \"secondary\",\n });\n\n return (\n <>\n <div className=\"flex flex-col items-center gap-2 px-8 text-center text-body-100\">\n {title && <p className=\"typography-heading-4 text-body-100\">{title}</p>}\n {description && <p className=\"typography-body-2-regular text-body-200\">{description}</p>}\n </div>\n {showActions && (primary || secondary) && (\n <div className=\"flex w-full flex-col gap-4 px-8 sm:flex-row sm:*:flex-1\">\n {secondary}\n {primary}\n </div>\n )}\n </>\n );\n}\n\nfunction DefaultContent({\n children,\n showActions,\n primarySlot,\n primaryLabel,\n primaryOnClick,\n secondarySlot,\n secondaryLabel,\n secondaryOnClick,\n}: Pick<\n SnackbarProps,\n | \"children\"\n | \"showActions\"\n | \"primarySlot\"\n | \"primaryLabel\"\n | \"primaryOnClick\"\n | \"secondarySlot\"\n | \"secondaryLabel\"\n | \"secondaryOnClick\"\n>) {\n const primary = resolveCtaSlot(primarySlot, primaryLabel, primaryOnClick, {\n variant: \"primary\",\n size: \"40\",\n });\n const secondary = resolveCtaSlot(secondarySlot, secondaryLabel, secondaryOnClick, {\n variant: \"tertiary\",\n size: \"40\",\n });\n\n return (\n <>\n <div className=\"typography-body-1-medium flex min-w-0 flex-1 items-center self-stretch text-body-100\">\n {children}\n </div>\n {showActions && (primary || secondary) && (\n <div className=\"flex shrink-0 items-start gap-2\">\n {primary}\n {secondary}\n </div>\n )}\n </>\n );\n}\n\n/**\n * A prominent inline message with optional title, description, action buttons,\n * and close control. Supports three layout variants: `default`, `vipEarn`, and\n * `welcome`.\n *\n * @example\n * ```tsx\n * <Snackbar\n * variant=\"default\"\n * primaryLabel=\"Undo\"\n * primaryOnClick={undo}\n * closable\n * onClose={dismiss}\n * >\n * Item deleted\n * </Snackbar>\n * ```\n */\nexport const Snackbar = React.forwardRef<HTMLDivElement, SnackbarProps>(\n (\n {\n className,\n variant = \"default\",\n icon,\n title,\n description,\n showActions = true,\n primaryLabel,\n primaryOnClick,\n primarySlot,\n secondaryLabel,\n secondaryOnClick,\n secondarySlot,\n closable = false,\n onClose,\n closeLabel,\n children,\n ...props\n },\n ref,\n ) => {\n return (\n /* biome-ignore lint/a11y/useSemanticElements: output cannot contain div children; browsers would break the wrapper. */\n <div\n ref={ref}\n role=\"status\"\n data-testid=\"snackbar\"\n className={cn(\n \"flex gap-4 rounded-2xl\",\n (variant === \"default\" || variant === \"vipEarn\") &&\n \"border border-neutral-50 bg-background-200 p-4 backdrop-blur-md\",\n variant === \"default\" && \"flex-wrap items-start\",\n variant === \"vipEarn\" && \"items-start\",\n variant === \"welcome\" &&\n \"relative flex-col items-center bg-background-inverse-solid py-6\",\n className,\n )}\n {...props}\n >\n {variant === \"vipEarn\" && (\n <VipEarnContent\n icon={icon}\n title={title}\n description={description}\n showActions={showActions}\n primarySlot={primarySlot}\n primaryLabel={primaryLabel}\n primaryOnClick={primaryOnClick}\n />\n )}\n {variant === \"welcome\" && (\n <WelcomeContent\n title={title}\n description={description}\n showActions={showActions}\n primarySlot={primarySlot}\n primaryLabel={primaryLabel}\n primaryOnClick={primaryOnClick}\n secondarySlot={secondarySlot}\n secondaryLabel={secondaryLabel}\n secondaryOnClick={secondaryOnClick}\n />\n )}\n {variant === \"default\" && (\n <DefaultContent\n showActions={showActions}\n primarySlot={primarySlot}\n primaryLabel={primaryLabel}\n primaryOnClick={primaryOnClick}\n secondarySlot={secondarySlot}\n secondaryLabel={secondaryLabel}\n secondaryOnClick={secondaryOnClick}\n >\n {children}\n </DefaultContent>\n )}\n {closable && (\n <CloseButton\n onClose={onClose}\n closeLabel={closeLabel}\n className={variant === \"welcome\" ? \"absolute top-2 right-2\" : undefined}\n />\n )}\n </div>\n );\n },\n);\n\nSnackbar.displayName = \"Snackbar\";\n"],"names":[],"mappings":"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"}
@@ -14,6 +14,7 @@ const Switch = React.forwardRef(({ className, size = "default", ...props }, ref)
14
14
  "focus-visible:shadow-focus-ring focus-visible:outline-none",
15
15
  "data-[state=checked]:border-neutral-200 data-[state=checked]:bg-brand-green-500",
16
16
  "data-[state=unchecked]:bg-neutral-400",
17
+ "not-disabled:active:opacity-80",
17
18
  "disabled:cursor-not-allowed disabled:opacity-50",
18
19
  size === "default" && "h-6 w-10.5",
19
20
  size === "small" && "h-5 w-9",
@@ -1 +1 @@
1
- {"version":3,"file":"Switch.mjs","sources":["../../../src/components/Switch/Switch.tsx"],"sourcesContent":["import * as SwitchPrimitive from \"@radix-ui/react-switch\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\nexport type SwitchSize = \"default\" | \"small\";\n\nexport interface SwitchProps\n extends Omit<React.ComponentPropsWithoutRef<typeof SwitchPrimitive.Root>, \"asChild\"> {\n /** Size variant of the switch */\n size?: SwitchSize;\n}\n\nexport const Switch = React.forwardRef<\n React.ComponentRef<typeof SwitchPrimitive.Root>,\n SwitchProps\n>(({ className, size = \"default\", ...props }, ref) => {\n const thumbSizeClass =\n size === \"default\"\n ? \"size-4.5 translate-x-0.75 data-[state=checked]:translate-x-4.75\"\n : \"size-4 translate-x-0.5 data-[state=checked]:translate-x-4.25\";\n\n return (\n <SwitchPrimitive.Root\n ref={ref}\n className={cn(\n \"inline-flex shrink-0 cursor-pointer items-center rounded-full border border-transparent transition-colors duration-150\",\n \"focus-visible:shadow-focus-ring focus-visible:outline-none\",\n \"data-[state=checked]:border-neutral-200 data-[state=checked]:bg-brand-green-500\",\n \"data-[state=unchecked]:bg-neutral-400\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n size === \"default\" && \"h-6 w-10.5\",\n size === \"small\" && \"h-5 w-9\",\n className,\n )}\n {...props}\n >\n <SwitchPrimitive.Thumb\n className={cn(\n \"pointer-events-none rounded-full bg-body-white-solid-constant shadow-sm transition-transform duration-150 dark:bg-body-black-solid-constant\",\n thumbSizeClass,\n )}\n />\n </SwitchPrimitive.Root>\n );\n});\n\nSwitch.displayName = \"Switch\";\n"],"names":[],"mappings":";;;;;AAYO,MAAM,SAAS,MAAM,WAG1B,CAAC,EAAE,WAAW,OAAO,WAAW,GAAG,MAAA,GAAS,QAAQ;AACpD,QAAM,iBACJ,SAAS,YACL,oEACA;AAEN,SACE;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,SAAS,aAAa;AAAA,QACtB,SAAS,WAAW;AAAA,QACpB;AAAA,MAAA;AAAA,MAED,GAAG;AAAA,MAEJ,UAAA;AAAA,QAAC,gBAAgB;AAAA,QAAhB;AAAA,UACC,WAAW;AAAA,YACT;AAAA,YACA;AAAA,UAAA;AAAA,QACF;AAAA,MAAA;AAAA,IACF;AAAA,EAAA;AAGN,CAAC;AAED,OAAO,cAAc;"}
1
+ {"version":3,"file":"Switch.mjs","sources":["../../../src/components/Switch/Switch.tsx"],"sourcesContent":["import * as SwitchPrimitive from \"@radix-ui/react-switch\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Size variant of the switch toggle. */\nexport type SwitchSize = \"default\" | \"small\";\n\nexport interface SwitchProps\n extends Omit<React.ComponentPropsWithoutRef<typeof SwitchPrimitive.Root>, \"asChild\"> {\n /** Size variant of the switch. @default \"default\" */\n size?: SwitchSize;\n}\n\n/**\n * A toggle switch for boolean on/off states. Built on Radix UI `Switch`.\n *\n * For a labelled switch with helper text, see {@link SwitchField}.\n *\n * @example\n * ```tsx\n * <Switch checked={on} onCheckedChange={setOn} />\n * ```\n */\nexport const Switch = React.forwardRef<\n React.ComponentRef<typeof SwitchPrimitive.Root>,\n SwitchProps\n>(({ className, size = \"default\", ...props }, ref) => {\n const thumbSizeClass =\n size === \"default\"\n ? \"size-4.5 translate-x-0.75 data-[state=checked]:translate-x-4.75\"\n : \"size-4 translate-x-0.5 data-[state=checked]:translate-x-4.25\";\n\n return (\n <SwitchPrimitive.Root\n ref={ref}\n className={cn(\n \"inline-flex shrink-0 cursor-pointer items-center rounded-full border border-transparent transition-colors duration-150\",\n \"focus-visible:shadow-focus-ring focus-visible:outline-none\",\n \"data-[state=checked]:border-neutral-200 data-[state=checked]:bg-brand-green-500\",\n \"data-[state=unchecked]:bg-neutral-400\",\n \"not-disabled:active:opacity-80\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n size === \"default\" && \"h-6 w-10.5\",\n size === \"small\" && \"h-5 w-9\",\n className,\n )}\n {...props}\n >\n <SwitchPrimitive.Thumb\n className={cn(\n \"pointer-events-none rounded-full bg-body-white-solid-constant shadow-sm transition-transform duration-150 dark:bg-body-black-solid-constant\",\n thumbSizeClass,\n )}\n />\n </SwitchPrimitive.Root>\n );\n});\n\nSwitch.displayName = \"Switch\";\n"],"names":[],"mappings":";;;;;AAuBO,MAAM,SAAS,MAAM,WAG1B,CAAC,EAAE,WAAW,OAAO,WAAW,GAAG,MAAA,GAAS,QAAQ;AACpD,QAAM,iBACJ,SAAS,YACL,oEACA;AAEN,SACE;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,SAAS,aAAa;AAAA,QACtB,SAAS,WAAW;AAAA,QACpB;AAAA,MAAA;AAAA,MAED,GAAG;AAAA,MAEJ,UAAA;AAAA,QAAC,gBAAgB;AAAA,QAAhB;AAAA,UACC,WAAW;AAAA,YACT;AAAA,YACA;AAAA,UAAA;AAAA,QACF;AAAA,MAAA;AAAA,IACF;AAAA,EAAA;AAGN,CAAC;AAED,OAAO,cAAc;"}
@@ -4,6 +4,7 @@ import * as React from "react";
4
4
  import { cn } from "../../utils/cn.mjs";
5
5
  import { InfoCircleIcon } from "../Icons/InfoCircleIcon.mjs";
6
6
  import { Switch } from "../Switch/Switch.mjs";
7
+ import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from "../Tooltip/Tooltip.mjs";
7
8
  const SwitchField = React.forwardRef(
8
9
  ({
9
10
  className,
@@ -12,6 +13,7 @@ const SwitchField = React.forwardRef(
12
13
  label,
13
14
  helperText,
14
15
  infoText,
16
+ infoLabel = "More information",
15
17
  disabled,
16
18
  id: propId,
17
19
  ...props
@@ -51,10 +53,10 @@ const SwitchField = React.forwardRef(
51
53
  children: label
52
54
  }
53
55
  ),
54
- infoText && /* @__PURE__ */ jsxs("span", { className: "group relative flex shrink-0 pt-0.5", children: [
55
- /* @__PURE__ */ jsx(InfoCircleIcon, { className: "size-5 text-body-200" }),
56
- /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute top-full left-1/2 mt-1 -translate-x-1/2 rounded-lg bg-neutral-400 px-3 py-1.5 text-body-white-solid-constant text-xs leading-none opacity-0 transition-opacity group-hover:opacity-100", children: infoText })
57
- ] })
56
+ infoText && /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
57
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx("button", { type: "button", "aria-label": infoLabel, className: "flex shrink-0 pt-0.5", children: /* @__PURE__ */ jsx(InfoCircleIcon, { "aria-hidden": "true", className: "size-5 text-body-200" }) }) }),
58
+ /* @__PURE__ */ jsx(TooltipContent, { side: "bottom", children: infoText })
59
+ ] }) })
58
60
  ] }),
59
61
  helperText && /* @__PURE__ */ jsx(
60
62
  "span",
@@ -1 +1 @@
1
- {"version":3,"file":"SwitchField.mjs","sources":["../../../src/components/SwitchField/SwitchField.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { InfoCircleIcon } from \"../Icons/InfoCircleIcon\";\nimport { Switch, type SwitchSize } from \"../Switch/Switch\";\n\nexport type SwitchFieldOrientation = \"right\" | \"left\";\n\nexport interface SwitchFieldProps\n extends Omit<React.ComponentPropsWithoutRef<typeof Switch>, \"size\" | \"className\"> {\n /** Position of the switch relative to the label */\n orientation?: SwitchFieldOrientation;\n /** Size variant of the switch and text */\n size?: SwitchSize;\n /** Label text displayed next to the switch */\n label?: string;\n /** Helper text displayed below the label */\n helperText?: string;\n /** Info tooltip text displayed on hover of the info icon */\n infoText?: string;\n /** Additional className for the wrapper */\n className?: string;\n}\n\nexport const SwitchField = React.forwardRef<React.ComponentRef<typeof Switch>, SwitchFieldProps>(\n (\n {\n className,\n orientation = \"right\",\n size = \"default\",\n label,\n helperText,\n infoText,\n disabled,\n id: propId,\n ...props\n },\n ref,\n ) => {\n const generatedId = React.useId();\n const id = propId ?? generatedId;\n const helperTextId = helperText ? `${id}-helper` : undefined;\n\n const switchElement = (\n <Switch\n ref={ref}\n id={id}\n size={size}\n disabled={disabled}\n aria-describedby={helperTextId}\n {...props}\n />\n );\n\n const labelContent = (label || helperText) && (\n <div\n className={cn(\n \"flex min-w-0 flex-1 flex-col items-start\",\n size === \"default\" ? \"gap-1\" : \"gap-0.5\",\n )}\n >\n {label && (\n <div className=\"flex items-center gap-1\">\n <label\n htmlFor={id}\n className={cn(\n \"cursor-pointer select-none text-body-100\",\n disabled && \"cursor-not-allowed text-disabled-100\",\n size === \"default\" ? \"typography-body-1-semibold\" : \"typography-body-2-semibold\",\n )}\n >\n {label}\n </label>\n {infoText && (\n <span className=\"group relative flex shrink-0 pt-0.5\">\n <InfoCircleIcon className=\"size-5 text-body-200\" />\n <span className=\"pointer-events-none absolute top-full left-1/2 mt-1 -translate-x-1/2 rounded-lg bg-neutral-400 px-3 py-1.5 text-body-white-solid-constant text-xs leading-none opacity-0 transition-opacity group-hover:opacity-100\">\n {/*!TODO Replace with Tooltip once built\n https://linear.app/fanvue/issue/ENG-7226/component-library-tooltip-component */}\n {infoText}\n </span>\n </span>\n )}\n </div>\n )}\n {helperText && (\n <span\n id={helperTextId}\n className={cn(\n \"text-body-200\", // !TODO https://linear.app/fanvue/issue/ENG-7301/swap-out-typography-tailwind-utility-classes\n disabled && \"text-disabled-100\",\n size === \"default\" ? \"typography-body-2-regular\" : \"typography-caption-regular\",\n )}\n >\n {helperText}\n </span>\n )}\n </div>\n );\n\n return (\n <div className={cn(\"flex items-start gap-2\", disabled && \"cursor-not-allowed\", className)}>\n {orientation === \"left\" && switchElement}\n {labelContent}\n {orientation === \"right\" && switchElement}\n </div>\n );\n },\n);\n\nSwitchField.displayName = \"SwitchField\";\n"],"names":[],"mappings":";;;;;;AAuBO,MAAM,cAAc,MAAM;AAAA,EAC/B,CACE;AAAA,IACE;AAAA,IACA,cAAc;AAAA,IACd,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,IAAI;AAAA,IACJ,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,cAAc,MAAM,MAAA;AAC1B,UAAM,KAAK,UAAU;AACrB,UAAM,eAAe,aAAa,GAAG,EAAE,YAAY;AAEnD,UAAM,gBACJ;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,oBAAkB;AAAA,QACjB,GAAG;AAAA,MAAA;AAAA,IAAA;AAIR,UAAM,gBAAgB,SAAS,eAC7B;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAW;AAAA,UACT;AAAA,UACA,SAAS,YAAY,UAAU;AAAA,QAAA;AAAA,QAGhC,UAAA;AAAA,UAAA,SACC,qBAAC,OAAA,EAAI,WAAU,2BACb,UAAA;AAAA,YAAA;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,SAAS;AAAA,gBACT,WAAW;AAAA,kBACT;AAAA,kBACA,YAAY;AAAA,kBACZ,SAAS,YAAY,+BAA+B;AAAA,gBAAA;AAAA,gBAGrD,UAAA;AAAA,cAAA;AAAA,YAAA;AAAA,YAEF,YACC,qBAAC,QAAA,EAAK,WAAU,uCACd,UAAA;AAAA,cAAA,oBAAC,gBAAA,EAAe,WAAU,uBAAA,CAAuB;AAAA,cACjD,oBAAC,QAAA,EAAK,WAAU,uNAGb,UAAA,SAAA,CACH;AAAA,YAAA,EAAA,CACF;AAAA,UAAA,GAEJ;AAAA,UAED,cACC;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,IAAI;AAAA,cACJ,WAAW;AAAA,gBACT;AAAA;AAAA,gBACA,YAAY;AAAA,gBACZ,SAAS,YAAY,8BAA8B;AAAA,cAAA;AAAA,cAGpD,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,QACH;AAAA,MAAA;AAAA,IAAA;AAKN,WACE,qBAAC,SAAI,WAAW,GAAG,0BAA0B,YAAY,sBAAsB,SAAS,GACrF,UAAA;AAAA,MAAA,gBAAgB,UAAU;AAAA,MAC1B;AAAA,MACA,gBAAgB,WAAW;AAAA,IAAA,GAC9B;AAAA,EAEJ;AACF;AAEA,YAAY,cAAc;"}
1
+ {"version":3,"file":"SwitchField.mjs","sources":["../../../src/components/SwitchField/SwitchField.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { InfoCircleIcon } from \"../Icons/InfoCircleIcon\";\nimport { Switch, type SwitchSize } from \"../Switch/Switch\";\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from \"../Tooltip/Tooltip\";\n\n/** Side on which the switch toggle is positioned relative to the label. */\nexport type SwitchFieldOrientation = \"right\" | \"left\";\n\nexport interface SwitchFieldProps\n extends Omit<React.ComponentPropsWithoutRef<typeof Switch>, \"size\" | \"className\"> {\n /** Side on which the switch is placed relative to the label. @default \"right\" */\n orientation?: SwitchFieldOrientation;\n /** Size variant of the switch and accompanying text. @default \"default\" */\n size?: SwitchSize;\n /** Label text displayed next to the switch. */\n label?: string;\n /** Descriptive text displayed below the label. */\n helperText?: string;\n /** Tooltip text shown when hovering the info icon next to the label. */\n infoText?: string;\n /** Accessible label for the info tooltip trigger button. Override for i18n.\n * @default \"More information\"\n */\n infoLabel?: string;\n /** Additional CSS class name for the outer wrapper. */\n className?: string;\n}\n\n/**\n * A labelled switch field with optional helper text and info tooltip. Composes\n * the {@link Switch} component with a `<label>` and description.\n *\n * @example\n * ```tsx\n * <SwitchField\n * label=\"Notifications\"\n * helperText=\"Receive push notifications\"\n * checked={enabled}\n * onCheckedChange={setEnabled}\n * />\n * ```\n */\nexport const SwitchField = React.forwardRef<React.ComponentRef<typeof Switch>, SwitchFieldProps>(\n (\n {\n className,\n orientation = \"right\",\n size = \"default\",\n label,\n helperText,\n infoText,\n infoLabel = \"More information\",\n disabled,\n id: propId,\n ...props\n },\n ref,\n ) => {\n const generatedId = React.useId();\n const id = propId ?? generatedId;\n const helperTextId = helperText ? `${id}-helper` : undefined;\n\n const switchElement = (\n <Switch\n ref={ref}\n id={id}\n size={size}\n disabled={disabled}\n aria-describedby={helperTextId}\n {...props}\n />\n );\n\n const labelContent = (label || helperText) && (\n <div\n className={cn(\n \"flex min-w-0 flex-1 flex-col items-start\",\n size === \"default\" ? \"gap-1\" : \"gap-0.5\",\n )}\n >\n {label && (\n <div className=\"flex items-center gap-1\">\n <label\n htmlFor={id}\n className={cn(\n \"cursor-pointer select-none text-body-100\",\n disabled && \"cursor-not-allowed text-disabled-100\",\n size === \"default\" ? \"typography-body-1-semibold\" : \"typography-body-2-semibold\",\n )}\n >\n {label}\n </label>\n {infoText && (\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <button type=\"button\" aria-label={infoLabel} className=\"flex shrink-0 pt-0.5\">\n <InfoCircleIcon aria-hidden=\"true\" className=\"size-5 text-body-200\" />\n </button>\n </TooltipTrigger>\n <TooltipContent side=\"bottom\">{infoText}</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n )}\n </div>\n )}\n {helperText && (\n <span\n id={helperTextId}\n className={cn(\n \"text-body-200\", // !TODO https://linear.app/fanvue/issue/ENG-7301/swap-out-typography-tailwind-utility-classes\n disabled && \"text-disabled-100\",\n size === \"default\" ? \"typography-body-2-regular\" : \"typography-caption-regular\",\n )}\n >\n {helperText}\n </span>\n )}\n </div>\n );\n\n return (\n <div className={cn(\"flex items-start gap-2\", disabled && \"cursor-not-allowed\", className)}>\n {orientation === \"left\" && switchElement}\n {labelContent}\n {orientation === \"right\" && switchElement}\n </div>\n );\n },\n);\n\nSwitchField.displayName = \"SwitchField\";\n"],"names":[],"mappings":";;;;;;;AA2CO,MAAM,cAAc,MAAM;AAAA,EAC/B,CACE;AAAA,IACE;AAAA,IACA,cAAc;AAAA,IACd,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ;AAAA,IACA,IAAI;AAAA,IACJ,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,cAAc,MAAM,MAAA;AAC1B,UAAM,KAAK,UAAU;AACrB,UAAM,eAAe,aAAa,GAAG,EAAE,YAAY;AAEnD,UAAM,gBACJ;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,oBAAkB;AAAA,QACjB,GAAG;AAAA,MAAA;AAAA,IAAA;AAIR,UAAM,gBAAgB,SAAS,eAC7B;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAW;AAAA,UACT;AAAA,UACA,SAAS,YAAY,UAAU;AAAA,QAAA;AAAA,QAGhC,UAAA;AAAA,UAAA,SACC,qBAAC,OAAA,EAAI,WAAU,2BACb,UAAA;AAAA,YAAA;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,SAAS;AAAA,gBACT,WAAW;AAAA,kBACT;AAAA,kBACA,YAAY;AAAA,kBACZ,SAAS,YAAY,+BAA+B;AAAA,gBAAA;AAAA,gBAGrD,UAAA;AAAA,cAAA;AAAA,YAAA;AAAA,YAEF,YACC,oBAAC,iBAAA,EACC,UAAA,qBAAC,SAAA,EACC,UAAA;AAAA,cAAA,oBAAC,kBAAe,SAAO,MACrB,8BAAC,UAAA,EAAO,MAAK,UAAS,cAAY,WAAW,WAAU,wBACrD,8BAAC,gBAAA,EAAe,eAAY,QAAO,WAAU,wBAAuB,GACtE,EAAA,CACF;AAAA,cACA,oBAAC,gBAAA,EAAe,MAAK,UAAU,UAAA,SAAA,CAAS;AAAA,YAAA,EAAA,CAC1C,EAAA,CACF;AAAA,UAAA,GAEJ;AAAA,UAED,cACC;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,IAAI;AAAA,cACJ,WAAW;AAAA,gBACT;AAAA;AAAA,gBACA,YAAY;AAAA,gBACZ,SAAS,YAAY,8BAA8B;AAAA,cAAA;AAAA,cAGpD,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,QACH;AAAA,MAAA;AAAA,IAAA;AAKN,WACE,qBAAC,SAAI,WAAW,GAAG,0BAA0B,YAAY,sBAAsB,SAAS,GACrF,UAAA;AAAA,MAAA,gBAAgB,UAAU;AAAA,MAC1B;AAAA,MACA,gBAAgB,WAAW;AAAA,IAAA,GAC9B;AAAA,EAEJ;AACF;AAEA,YAAY,cAAc;"}
@@ -2,6 +2,15 @@
2
2
  import { jsxs, jsx } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { cn } from "../../utils/cn.mjs";
5
+ function warnMissingAccessibleName(ariaLabel, ariaLabelledBy) {
6
+ if (process.env.NODE_ENV !== "production") {
7
+ if (!ariaLabel && !ariaLabelledBy) {
8
+ console.warn(
9
+ "SwitchToggle: no accessible name provided. Pass an `aria-label` or `aria-labelledby` prop."
10
+ );
11
+ }
12
+ }
13
+ }
5
14
  const SwitchToggle = React.forwardRef(
6
15
  ({
7
16
  className,
@@ -13,19 +22,29 @@ const SwitchToggle = React.forwardRef(
13
22
  disabled = false,
14
23
  ...props
15
24
  }, ref) => {
16
- const groupName = React.useId();
25
+ warnMissingAccessibleName(props["aria-label"], props["aria-labelledby"]);
17
26
  const [internalValue, setInternalValue] = React.useState(defaultValue ?? options[0].value);
18
27
  const isControlled = controlledValue !== void 0;
19
28
  const currentValue = isControlled ? controlledValue : internalValue;
29
+ const anySelected = options.some((o) => o.value === currentValue);
20
30
  const isSecondSelected = currentValue === options[1].value;
31
+ const buttonRefs = React.useRef([]);
21
32
  const sizeClass = size === "24" ? "px-2 py-1 typography-caption-semibold" : size === "32" ? "px-3 py-1.75 typography-body-2-semibold" : "h-10 px-4 py-2.25 typography-button-small";
22
33
  const handleSelect = (optionValue) => {
23
- if (disabled) return;
34
+ if (disabled || optionValue === currentValue) return;
24
35
  if (!isControlled) {
25
36
  setInternalValue(optionValue);
26
37
  }
27
38
  onChange?.(optionValue);
28
39
  };
40
+ const handleKeyDown = (e, index) => {
41
+ const nextIndex = e.key === "ArrowRight" || e.key === "ArrowDown" ? (index + 1) % options.length : e.key === "ArrowLeft" || e.key === "ArrowUp" ? (index - 1 + options.length) % options.length : null;
42
+ if (nextIndex === null) return;
43
+ e.preventDefault();
44
+ const nextOption = options[nextIndex];
45
+ handleSelect(nextOption.value);
46
+ buttonRefs.current[nextIndex]?.focus();
47
+ };
29
48
  return /* @__PURE__ */ jsxs(
30
49
  "div",
31
50
  {
@@ -49,36 +68,34 @@ const SwitchToggle = React.forwardRef(
49
68
  )
50
69
  }
51
70
  ),
52
- options.map((option) => {
53
- const optionId = `${groupName}-${option.value}`;
54
- return /* @__PURE__ */ jsxs(
55
- "label",
56
- {
57
- htmlFor: optionId,
58
- className: cn(
59
- "relative z-10 inline-flex shrink-0 cursor-pointer items-center justify-center rounded-full border border-transparent text-body-100",
60
- "has-focus-visible:shadow-focus-ring has-focus-visible:outline-none",
61
- disabled && "pointer-events-none",
62
- sizeClass
63
- ),
64
- children: [
65
- /* @__PURE__ */ jsx(
66
- "input",
67
- {
68
- id: optionId,
69
- type: "radio",
70
- name: groupName,
71
- value: option.value,
72
- checked: currentValue === option.value,
73
- disabled,
74
- onChange: () => handleSelect(option.value),
75
- className: "sr-only"
76
- }
71
+ options.map((option, index) => {
72
+ const isSelected = currentValue === option.value;
73
+ return (
74
+ // biome-ignore lint/a11y/useSemanticElements: native radio inputs only allow Tab-focus on the checked item; buttons with roving tabindex give full keyboard navigation
75
+ /* @__PURE__ */ jsx(
76
+ "button",
77
+ {
78
+ ref: (el) => {
79
+ buttonRefs.current[index] = el;
80
+ },
81
+ type: "button",
82
+ role: "radio",
83
+ "aria-checked": isSelected,
84
+ tabIndex: isSelected || !anySelected && index === 0 ? 0 : -1,
85
+ disabled,
86
+ onClick: () => handleSelect(option.value),
87
+ onKeyDown: (e) => handleKeyDown(e, index),
88
+ className: cn(
89
+ "relative z-10 inline-flex shrink-0 cursor-pointer items-center justify-center rounded-full border border-transparent text-body-100",
90
+ "focus-visible:shadow-focus-ring focus-visible:outline-none",
91
+ "active:rounded-full active:bg-brand-green-50",
92
+ disabled && "pointer-events-none",
93
+ sizeClass
77
94
  ),
78
- option.label
79
- ]
80
- },
81
- option.value
95
+ children: option.label
96
+ },
97
+ option.value
98
+ )
82
99
  );
83
100
  })
84
101
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"SwitchToggle.mjs","sources":["../../../src/components/SwitchToggle/SwitchToggle.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\nexport type SwitchToggleSize = \"24\" | \"32\" | \"40\";\n\nexport interface SwitchToggleOption {\n /** Display label for the option */\n label: string;\n /** Value identifier for the option */\n value: string;\n}\n\nexport interface SwitchToggleProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n /** Size variant */\n size?: SwitchToggleSize;\n /** The two options to toggle between */\n options: [SwitchToggleOption, SwitchToggleOption];\n /** Currently selected value */\n value?: string;\n /** Default selected value (uncontrolled) */\n defaultValue?: string;\n /** Callback when the selected value changes */\n onChange?: (value: string) => void;\n /** Whether the toggle is disabled */\n disabled?: boolean;\n}\n\nexport const SwitchToggle = React.forwardRef<HTMLDivElement, SwitchToggleProps>(\n (\n {\n className,\n size = \"24\",\n options,\n value: controlledValue,\n defaultValue,\n onChange,\n disabled = false,\n ...props\n },\n ref,\n ) => {\n const groupName = React.useId();\n // Tracks selection for uncontrolled usage; ignored when `value` prop is provided\n const [internalValue, setInternalValue] = React.useState(defaultValue ?? options[0].value);\n const isControlled = controlledValue !== undefined;\n const currentValue = isControlled ? controlledValue : internalValue;\n const isSecondSelected = currentValue === options[1].value;\n\n const sizeClass =\n size === \"24\"\n ? \"px-2 py-1 typography-caption-semibold\"\n : size === \"32\"\n ? \"px-3 py-1.75 typography-body-2-semibold\"\n : \"h-10 px-4 py-2.25 typography-button-small\";\n\n const handleSelect = (optionValue: string) => {\n if (disabled) return;\n if (!isControlled) {\n setInternalValue(optionValue);\n }\n onChange?.(optionValue);\n };\n\n return (\n <div\n ref={ref}\n role=\"radiogroup\"\n className={cn(\n \"relative inline-grid grid-cols-2 rounded-full border border-neutral-200 p-1\",\n disabled && \"cursor-not-allowed opacity-50\",\n className,\n )}\n {...props}\n >\n <span\n aria-hidden=\"true\"\n className={cn(\n \"absolute inset-y-1 left-1 w-[calc(50%-4px)] rounded-full border border-brand-green-500 bg-brand-green-50\",\n \"motion-safe:transition-transform motion-safe:duration-200 motion-safe:ease-in-out\",\n isSecondSelected && \"translate-x-full\",\n )}\n />\n {options.map((option) => {\n const optionId = `${groupName}-${option.value}`;\n return (\n <label\n key={option.value}\n htmlFor={optionId}\n className={cn(\n \"relative z-10 inline-flex shrink-0 cursor-pointer items-center justify-center rounded-full border border-transparent text-body-100\",\n \"has-focus-visible:shadow-focus-ring has-focus-visible:outline-none\",\n disabled && \"pointer-events-none\",\n sizeClass,\n )}\n >\n <input\n id={optionId}\n type=\"radio\"\n name={groupName}\n value={option.value}\n checked={currentValue === option.value}\n disabled={disabled}\n onChange={() => handleSelect(option.value)}\n className=\"sr-only\"\n />\n {option.label}\n </label>\n );\n })}\n </div>\n );\n },\n);\n\nSwitchToggle.displayName = \"SwitchToggle\";\n"],"names":[],"mappings":";;;;AA2BO,MAAM,eAAe,MAAM;AAAA,EAChC,CACE;AAAA,IACE;AAAA,IACA,OAAO;AAAA,IACP;AAAA,IACA,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,YAAY,MAAM,MAAA;AAExB,UAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAS,gBAAgB,QAAQ,CAAC,EAAE,KAAK;AACzF,UAAM,eAAe,oBAAoB;AACzC,UAAM,eAAe,eAAe,kBAAkB;AACtD,UAAM,mBAAmB,iBAAiB,QAAQ,CAAC,EAAE;AAErD,UAAM,YACJ,SAAS,OACL,0CACA,SAAS,OACP,4CACA;AAER,UAAM,eAAe,CAAC,gBAAwB;AAC5C,UAAI,SAAU;AACd,UAAI,CAAC,cAAc;AACjB,yBAAiB,WAAW;AAAA,MAC9B;AACA,iBAAW,WAAW;AAAA,IACxB;AAEA,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL,WAAW;AAAA,UACT;AAAA,UACA,YAAY;AAAA,UACZ;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEJ,UAAA;AAAA,UAAA;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,eAAY;AAAA,cACZ,WAAW;AAAA,gBACT;AAAA,gBACA;AAAA,gBACA,oBAAoB;AAAA,cAAA;AAAA,YACtB;AAAA,UAAA;AAAA,UAED,QAAQ,IAAI,CAAC,WAAW;AACvB,kBAAM,WAAW,GAAG,SAAS,IAAI,OAAO,KAAK;AAC7C,mBACE;AAAA,cAAC;AAAA,cAAA;AAAA,gBAEC,SAAS;AAAA,gBACT,WAAW;AAAA,kBACT;AAAA,kBACA;AAAA,kBACA,YAAY;AAAA,kBACZ;AAAA,gBAAA;AAAA,gBAGF,UAAA;AAAA,kBAAA;AAAA,oBAAC;AAAA,oBAAA;AAAA,sBACC,IAAI;AAAA,sBACJ,MAAK;AAAA,sBACL,MAAM;AAAA,sBACN,OAAO,OAAO;AAAA,sBACd,SAAS,iBAAiB,OAAO;AAAA,sBACjC;AAAA,sBACA,UAAU,MAAM,aAAa,OAAO,KAAK;AAAA,sBACzC,WAAU;AAAA,oBAAA;AAAA,kBAAA;AAAA,kBAEX,OAAO;AAAA,gBAAA;AAAA,cAAA;AAAA,cAnBH,OAAO;AAAA,YAAA;AAAA,UAsBlB,CAAC;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAGP;AACF;AAEA,aAAa,cAAc;"}
1
+ {"version":3,"file":"SwitchToggle.mjs","sources":["../../../src/components/SwitchToggle/SwitchToggle.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Height of the switch toggle in pixels. */\nexport type SwitchToggleSize = \"24\" | \"32\" | \"40\";\n\n/** Describes one side of the binary toggle. */\nexport interface SwitchToggleOption {\n /** Display label for the option. */\n label: string;\n /** Value identifier returned via `onChange`. */\n value: string;\n}\n\nexport interface SwitchToggleProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n /** Height of the toggle in pixels. @default \"24\" */\n size?: SwitchToggleSize;\n /** The two options to toggle between (exactly two required). */\n options: [SwitchToggleOption, SwitchToggleOption];\n /** Currently selected value (controlled). */\n value?: string;\n /** Initially selected value (uncontrolled). */\n defaultValue?: string;\n /** Callback fired when the selected value changes. */\n onChange?: (value: string) => void;\n /** Whether the toggle is disabled. @default false */\n disabled?: boolean;\n}\n\nfunction warnMissingAccessibleName(ariaLabel?: string, ariaLabelledBy?: string) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!ariaLabel && !ariaLabelledBy) {\n console.warn(\n \"SwitchToggle: no accessible name provided. Pass an `aria-label` or `aria-labelledby` prop.\",\n );\n }\n }\n}\n\n/**\n * A binary segmented toggle rendered as a `radiogroup`. The active option is\n * highlighted with a sliding pill indicator. Supports both controlled and\n * uncontrolled usage.\n *\n * @example\n * ```tsx\n * <SwitchToggle\n * options={[\n * { label: \"Monthly\", value: \"monthly\" },\n * { label: \"Yearly\", value: \"yearly\" },\n * ]}\n * value={billing}\n * onChange={setBilling}\n * />\n * ```\n */\nexport const SwitchToggle = React.forwardRef<HTMLDivElement, SwitchToggleProps>(\n (\n {\n className,\n size = \"24\",\n options,\n value: controlledValue,\n defaultValue,\n onChange,\n disabled = false,\n ...props\n },\n ref,\n ) => {\n warnMissingAccessibleName(props[\"aria-label\"], props[\"aria-labelledby\"]);\n\n // Tracks selection for uncontrolled usage; ignored when `value` prop is provided\n const [internalValue, setInternalValue] = React.useState(defaultValue ?? options[0].value);\n const isControlled = controlledValue !== undefined;\n const currentValue = isControlled ? controlledValue : internalValue;\n const anySelected = options.some((o) => o.value === currentValue);\n const isSecondSelected = currentValue === options[1].value;\n const buttonRefs = React.useRef<(HTMLButtonElement | null)[]>([]);\n\n const sizeClass =\n size === \"24\"\n ? \"px-2 py-1 typography-caption-semibold\"\n : size === \"32\"\n ? \"px-3 py-1.75 typography-body-2-semibold\"\n : \"h-10 px-4 py-2.25 typography-button-small\";\n\n const handleSelect = (optionValue: string) => {\n if (disabled || optionValue === currentValue) return;\n if (!isControlled) {\n setInternalValue(optionValue);\n }\n onChange?.(optionValue);\n };\n\n const handleKeyDown = (e: React.KeyboardEvent, index: number) => {\n const nextIndex =\n e.key === \"ArrowRight\" || e.key === \"ArrowDown\"\n ? (index + 1) % options.length\n : e.key === \"ArrowLeft\" || e.key === \"ArrowUp\"\n ? (index - 1 + options.length) % options.length\n : null;\n if (nextIndex === null) return;\n e.preventDefault();\n const nextOption = options[nextIndex] as (typeof options)[number];\n handleSelect(nextOption.value);\n buttonRefs.current[nextIndex]?.focus();\n };\n\n return (\n <div\n ref={ref}\n role=\"radiogroup\"\n className={cn(\n \"relative inline-grid grid-cols-2 rounded-full border border-neutral-200 p-1\",\n disabled && \"cursor-not-allowed opacity-50\",\n className,\n )}\n {...props}\n >\n <span\n aria-hidden=\"true\"\n className={cn(\n \"absolute inset-y-1 left-1 w-[calc(50%-4px)] rounded-full border border-brand-green-500 bg-brand-green-50\",\n \"motion-safe:transition-transform motion-safe:duration-200 motion-safe:ease-in-out\",\n isSecondSelected && \"translate-x-full\",\n )}\n />\n {options.map((option, index) => {\n const isSelected = currentValue === option.value;\n return (\n // biome-ignore lint/a11y/useSemanticElements: native radio inputs only allow Tab-focus on the checked item; buttons with roving tabindex give full keyboard navigation\n <button\n key={option.value}\n ref={(el) => {\n buttonRefs.current[index] = el;\n }}\n type=\"button\"\n role=\"radio\"\n aria-checked={isSelected}\n tabIndex={isSelected || (!anySelected && index === 0) ? 0 : -1}\n disabled={disabled}\n onClick={() => handleSelect(option.value)}\n onKeyDown={(e) => handleKeyDown(e, index)}\n className={cn(\n \"relative z-10 inline-flex shrink-0 cursor-pointer items-center justify-center rounded-full border border-transparent text-body-100\",\n \"focus-visible:shadow-focus-ring focus-visible:outline-none\",\n \"active:rounded-full active:bg-brand-green-50\",\n disabled && \"pointer-events-none\",\n sizeClass,\n )}\n >\n {option.label}\n </button>\n );\n })}\n </div>\n );\n },\n);\n\nSwitchToggle.displayName = \"SwitchToggle\";\n"],"names":[],"mappings":";;;;AA6BA,SAAS,0BAA0B,WAAoB,gBAAyB;AAC9E,MAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,QAAI,CAAC,aAAa,CAAC,gBAAgB;AACjC,cAAQ;AAAA,QACN;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AACF;AAmBO,MAAM,eAAe,MAAM;AAAA,EAChC,CACE;AAAA,IACE;AAAA,IACA,OAAO;AAAA,IACP;AAAA,IACA,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,GAAG;AAAA,EAAA,GAEL,QACG;AACH,8BAA0B,MAAM,YAAY,GAAG,MAAM,iBAAiB,CAAC;AAGvE,UAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAS,gBAAgB,QAAQ,CAAC,EAAE,KAAK;AACzF,UAAM,eAAe,oBAAoB;AACzC,UAAM,eAAe,eAAe,kBAAkB;AACtD,UAAM,cAAc,QAAQ,KAAK,CAAC,MAAM,EAAE,UAAU,YAAY;AAChE,UAAM,mBAAmB,iBAAiB,QAAQ,CAAC,EAAE;AACrD,UAAM,aAAa,MAAM,OAAqC,EAAE;AAEhE,UAAM,YACJ,SAAS,OACL,0CACA,SAAS,OACP,4CACA;AAER,UAAM,eAAe,CAAC,gBAAwB;AAC5C,UAAI,YAAY,gBAAgB,aAAc;AAC9C,UAAI,CAAC,cAAc;AACjB,yBAAiB,WAAW;AAAA,MAC9B;AACA,iBAAW,WAAW;AAAA,IACxB;AAEA,UAAM,gBAAgB,CAAC,GAAwB,UAAkB;AAC/D,YAAM,YACJ,EAAE,QAAQ,gBAAgB,EAAE,QAAQ,eAC/B,QAAQ,KAAK,QAAQ,SACtB,EAAE,QAAQ,eAAe,EAAE,QAAQ,aAChC,QAAQ,IAAI,QAAQ,UAAU,QAAQ,SACvC;AACR,UAAI,cAAc,KAAM;AACxB,QAAE,eAAA;AACF,YAAM,aAAa,QAAQ,SAAS;AACpC,mBAAa,WAAW,KAAK;AAC7B,iBAAW,QAAQ,SAAS,GAAG,MAAA;AAAA,IACjC;AAEA,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL,WAAW;AAAA,UACT;AAAA,UACA,YAAY;AAAA,UACZ;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEJ,UAAA;AAAA,UAAA;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,eAAY;AAAA,cACZ,WAAW;AAAA,gBACT;AAAA,gBACA;AAAA,gBACA,oBAAoB;AAAA,cAAA;AAAA,YACtB;AAAA,UAAA;AAAA,UAED,QAAQ,IAAI,CAAC,QAAQ,UAAU;AAC9B,kBAAM,aAAa,iBAAiB,OAAO;AAC3C;AAAA;AAAA,cAEE;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBAEC,KAAK,CAAC,OAAO;AACX,+BAAW,QAAQ,KAAK,IAAI;AAAA,kBAC9B;AAAA,kBACA,MAAK;AAAA,kBACL,MAAK;AAAA,kBACL,gBAAc;AAAA,kBACd,UAAU,cAAe,CAAC,eAAe,UAAU,IAAK,IAAI;AAAA,kBAC5D;AAAA,kBACA,SAAS,MAAM,aAAa,OAAO,KAAK;AAAA,kBACxC,WAAW,CAAC,MAAM,cAAc,GAAG,KAAK;AAAA,kBACxC,WAAW;AAAA,oBACT;AAAA,oBACA;AAAA,oBACA;AAAA,oBACA,YAAY;AAAA,oBACZ;AAAA,kBAAA;AAAA,kBAGD,UAAA,OAAO;AAAA,gBAAA;AAAA,gBAnBH,OAAO;AAAA,cAAA;AAAA;AAAA,UAsBlB,CAAC;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAGP;AACF;AAEA,aAAa,cAAc;"}
@@ -1 +1 @@
1
- {"version":3,"file":"Tabs.mjs","sources":["../../../src/components/Tabs/Tabs.tsx"],"sourcesContent":["import * as TabsPrimitive from \"@radix-ui/react-tabs\";\nimport type * as React from \"react\";\n\nexport type TabsProps = React.ComponentPropsWithoutRef<typeof TabsPrimitive.Root>;\n\nexport const Tabs = TabsPrimitive.Root;\n"],"names":[],"mappings":";;AAKO,MAAM,OAAO,cAAc;"}
1
+ {"version":3,"file":"Tabs.mjs","sources":["../../../src/components/Tabs/Tabs.tsx"],"sourcesContent":["import * as TabsPrimitive from \"@radix-ui/react-tabs\";\nimport type * as React from \"react\";\n\n/** Props for the {@link Tabs} root component. Extends Radix `Tabs.Root` props. */\nexport type TabsProps = React.ComponentPropsWithoutRef<typeof TabsPrimitive.Root>;\n\n/**\n * Root container for a tabbed interface. Manages the active tab state and\n * coordinates {@link TabsList}, {@link TabsTrigger}, and {@link TabsContent}.\n *\n * Built on Radix UI `Tabs`.\n *\n * @example\n * ```tsx\n * <Tabs defaultValue=\"tab1\">\n * <TabsList>\n * <TabsTrigger value=\"tab1\">Tab 1</TabsTrigger>\n * <TabsTrigger value=\"tab2\">Tab 2</TabsTrigger>\n * </TabsList>\n * <TabsContent value=\"tab1\">Content 1</TabsContent>\n * <TabsContent value=\"tab2\">Content 2</TabsContent>\n * </Tabs>\n * ```\n */\nexport const Tabs = TabsPrimitive.Root;\n"],"names":[],"mappings":";;AAwBO,MAAM,OAAO,cAAc;"}
@@ -1 +1 @@
1
- {"version":3,"file":"TabsContent.mjs","sources":["../../../src/components/Tabs/TabsContent.tsx"],"sourcesContent":["import * as TabsPrimitive from \"@radix-ui/react-tabs\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\nexport type TabsContentProps = React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>;\n\nexport const TabsContent = React.forwardRef<\n React.ComponentRef<typeof TabsPrimitive.Content>,\n TabsContentProps\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.Content\n ref={ref}\n className={cn(\"focus-visible:outline-none\", className)}\n {...props}\n />\n));\n\nTabsContent.displayName = \"TabsContent\";\n"],"names":[],"mappings":";;;;;AAMO,MAAM,cAAc,MAAM,WAG/B,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QAC1B;AAAA,EAAC,cAAc;AAAA,EAAd;AAAA,IACC;AAAA,IACA,WAAW,GAAG,8BAA8B,SAAS;AAAA,IACpD,GAAG;AAAA,EAAA;AACN,CACD;AAED,YAAY,cAAc;"}
1
+ {"version":3,"file":"TabsContent.mjs","sources":["../../../src/components/Tabs/TabsContent.tsx"],"sourcesContent":["import * as TabsPrimitive from \"@radix-ui/react-tabs\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Props for the {@link TabsContent} panel component. */\nexport type TabsContentProps = React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>;\n\n/** Renders the content panel for a given tab `value`. Only visible when its value matches the active tab. */\nexport const TabsContent = React.forwardRef<\n React.ComponentRef<typeof TabsPrimitive.Content>,\n TabsContentProps\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.Content\n ref={ref}\n className={cn(\"focus-visible:outline-none\", className)}\n {...props}\n />\n));\n\nTabsContent.displayName = \"TabsContent\";\n"],"names":[],"mappings":";;;;;AAQO,MAAM,cAAc,MAAM,WAG/B,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QAC1B;AAAA,EAAC,cAAc;AAAA,EAAd;AAAA,IACC;AAAA,IACA,WAAW,GAAG,8BAA8B,SAAS;AAAA,IACpD,GAAG;AAAA,EAAA;AACN,CACD;AAED,YAAY,cAAc;"}
@@ -1 +1 @@
1
- {"version":3,"file":"TabsList.mjs","sources":["../../../src/components/Tabs/TabsList.tsx"],"sourcesContent":["import * as TabsPrimitive from \"@radix-ui/react-tabs\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\nexport type TabsListProps = React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>;\n\nexport const TabsList = React.forwardRef<\n React.ComponentRef<typeof TabsPrimitive.List>,\n TabsListProps\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.List\n ref={ref}\n className={cn(\n \"inline-flex\", // !TODO setup shadows tokens https://linear.app/fanvue/issue/ENG-7368/setup-shadow-tokens\n \"data-[orientation=horizontal]:items-center data-[orientation=horizontal]:shadow-[inset_0_-1px_0_0_var(--color-neutral-200)]\",\n \"data-[orientation=vertical]:flex-col data-[orientation=vertical]:shadow-[inset_-1px_0_0_0_var(--color-neutral-200)]\",\n className,\n )}\n {...props}\n />\n));\n\nTabsList.displayName = \"TabsList\";\n"],"names":[],"mappings":";;;;;AAMO,MAAM,WAAW,MAAM,WAG5B,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QAC1B;AAAA,EAAC,cAAc;AAAA,EAAd;AAAA,IACC;AAAA,IACA,WAAW;AAAA,MACT;AAAA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAED,GAAG;AAAA,EAAA;AACN,CACD;AAED,SAAS,cAAc;"}
1
+ {"version":3,"file":"TabsList.mjs","sources":["../../../src/components/Tabs/TabsList.tsx"],"sourcesContent":["import * as TabsPrimitive from \"@radix-ui/react-tabs\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Props for the {@link TabsList} component. */\nexport type TabsListProps = React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>;\n\n/** Container for {@link TabsTrigger} elements. Supports both horizontal and vertical orientations. */\nexport const TabsList = React.forwardRef<\n React.ComponentRef<typeof TabsPrimitive.List>,\n TabsListProps\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.List\n ref={ref}\n className={cn(\n \"inline-flex\", // !TODO setup shadows tokens https://linear.app/fanvue/issue/ENG-7368/setup-shadow-tokens\n \"data-[orientation=horizontal]:items-center data-[orientation=horizontal]:shadow-[inset_0_-1px_0_0_var(--color-neutral-200)]\",\n \"data-[orientation=vertical]:flex-col data-[orientation=vertical]:shadow-[inset_-1px_0_0_0_var(--color-neutral-200)]\",\n className,\n )}\n {...props}\n />\n));\n\nTabsList.displayName = \"TabsList\";\n"],"names":[],"mappings":";;;;;AAQO,MAAM,WAAW,MAAM,WAG5B,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QAC1B;AAAA,EAAC,cAAc;AAAA,EAAd;AAAA,IACC;AAAA,IACA,WAAW;AAAA,MACT;AAAA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAED,GAAG;AAAA,EAAA;AACN,CACD;AAED,SAAS,cAAc;"}
@@ -11,11 +11,14 @@ const TabsTrigger = React.forwardRef(({ className, ...props }, ref) => /* @__PUR
11
11
  "inline-flex items-center justify-center",
12
12
  "rounded-xs border-transparent",
13
13
  "typography-body-1-semibold cursor-pointer text-body-100",
14
+ "motion-safe:transition-[color,border-color] motion-safe:duration-150 motion-safe:ease-in-out",
14
15
  "data-[orientation=horizontal]:border-b-4 data-[orientation=horizontal]:px-3 data-[orientation=horizontal]:pb-4",
15
16
  "data-[orientation=vertical]:justify-start data-[orientation=vertical]:border-r-4 data-[orientation=vertical]:px-4 data-[orientation=vertical]:py-3",
16
17
  "data-[state=active]:border-brand-green-500",
17
18
  "data-[state=active]:hover:text-hover-100",
18
19
  "data-[state=inactive]:hover:text-hover-200",
20
+ "data-[state=active]:active:text-hover-100",
21
+ "data-[state=inactive]:active:text-hover-200",
19
22
  "data-disabled:pointer-events-none",
20
23
  "data-disabled:data-[state=active]:text-disabled-100",
21
24
  "data-disabled:data-[state=inactive]:text-disabled-400",
@@ -1 +1 @@
1
- {"version":3,"file":"TabsTrigger.mjs","sources":["../../../src/components/Tabs/TabsTrigger.tsx"],"sourcesContent":["import * as TabsPrimitive from \"@radix-ui/react-tabs\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\nexport type TabsTriggerProps = React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>;\n\nexport const TabsTrigger = React.forwardRef<\n React.ComponentRef<typeof TabsPrimitive.Trigger>,\n TabsTriggerProps\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.Trigger\n ref={ref}\n className={cn(\n \"inline-flex items-center justify-center\",\n \"rounded-xs border-transparent\",\n \"typography-body-1-semibold cursor-pointer text-body-100\",\n \"data-[orientation=horizontal]:border-b-4 data-[orientation=horizontal]:px-3 data-[orientation=horizontal]:pb-4\",\n \"data-[orientation=vertical]:justify-start data-[orientation=vertical]:border-r-4 data-[orientation=vertical]:px-4 data-[orientation=vertical]:py-3\",\n \"data-[state=active]:border-brand-green-500\",\n \"data-[state=active]:hover:text-hover-100\",\n \"data-[state=inactive]:hover:text-hover-200\",\n \"data-disabled:pointer-events-none\",\n \"data-disabled:data-[state=active]:text-disabled-100\",\n \"data-disabled:data-[state=inactive]:text-disabled-400\",\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 className,\n )}\n {...props}\n />\n));\n\nTabsTrigger.displayName = \"TabsTrigger\";\n"],"names":[],"mappings":";;;;;AAMO,MAAM,cAAc,MAAM,WAG/B,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QAC1B;AAAA,EAAC,cAAc;AAAA,EAAd;AAAA,IACC;AAAA,IACA,WAAW;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAED,GAAG;AAAA,EAAA;AACN,CACD;AAED,YAAY,cAAc;"}
1
+ {"version":3,"file":"TabsTrigger.mjs","sources":["../../../src/components/Tabs/TabsTrigger.tsx"],"sourcesContent":["import * as TabsPrimitive from \"@radix-ui/react-tabs\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Props for the {@link TabsTrigger} button component. */\nexport type TabsTriggerProps = React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>;\n\n/** An interactive tab button that activates its associated {@link TabsContent} panel when clicked. */\nexport const TabsTrigger = React.forwardRef<\n React.ComponentRef<typeof TabsPrimitive.Trigger>,\n TabsTriggerProps\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.Trigger\n ref={ref}\n className={cn(\n \"inline-flex items-center justify-center\",\n \"rounded-xs border-transparent\",\n \"typography-body-1-semibold cursor-pointer text-body-100\",\n \"motion-safe:transition-[color,border-color] motion-safe:duration-150 motion-safe:ease-in-out\",\n \"data-[orientation=horizontal]:border-b-4 data-[orientation=horizontal]:px-3 data-[orientation=horizontal]:pb-4\",\n \"data-[orientation=vertical]:justify-start data-[orientation=vertical]:border-r-4 data-[orientation=vertical]:px-4 data-[orientation=vertical]:py-3\",\n \"data-[state=active]:border-brand-green-500\",\n \"data-[state=active]:hover:text-hover-100\",\n \"data-[state=inactive]:hover:text-hover-200\",\n \"data-[state=active]:active:text-hover-100\",\n \"data-[state=inactive]:active:text-hover-200\",\n \"data-disabled:pointer-events-none\",\n \"data-disabled:data-[state=active]:text-disabled-100\",\n \"data-disabled:data-[state=inactive]:text-disabled-400\",\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 className,\n )}\n {...props}\n />\n));\n\nTabsTrigger.displayName = \"TabsTrigger\";\n"],"names":[],"mappings":";;;;;AAQO,MAAM,cAAc,MAAM,WAG/B,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QAC1B;AAAA,EAAC,cAAc;AAAA,EAAd;AAAA,IACC;AAAA,IACA,WAAW;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAED,GAAG;AAAA,EAAA;AACN,CACD;AAED,YAAY,cAAc;"}
@@ -0,0 +1,147 @@
1
+ "use client";
2
+ import { jsxs, jsx } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { cn } from "../../utils/cn.mjs";
5
+ const CONTAINER_HEIGHT = {
6
+ "48": "h-12",
7
+ "40": "h-10",
8
+ "32": "h-8"
9
+ };
10
+ const INPUT_SIZE_CLASSES = {
11
+ "48": "py-3 typography-body-1-regular",
12
+ "40": "py-2 typography-body-1-regular",
13
+ "32": "py-2 typography-body-2-regular"
14
+ };
15
+ const PADDING_LEFT = {
16
+ "48": ["pl-4", "pl-11"],
17
+ "40": ["pl-4", "pl-11"],
18
+ "32": ["pl-3", "pl-10"]
19
+ };
20
+ const PADDING_RIGHT = {
21
+ "48": ["pr-4", "pr-11"],
22
+ "40": ["pr-4", "pr-11"],
23
+ "32": ["pr-3", "pr-10"]
24
+ };
25
+ const ICON_LEFT = {
26
+ "48": "left-4",
27
+ "40": "left-4",
28
+ "32": "left-3"
29
+ };
30
+ const ICON_RIGHT = {
31
+ "48": "right-4",
32
+ "40": "right-4",
33
+ "32": "right-3"
34
+ };
35
+ function getContainerClassName(size, error, disabled) {
36
+ return cn(
37
+ "relative rounded-xl border bg-neutral-100 has-focus-visible:shadow-focus-ring has-focus-visible:outline-none motion-safe:transition-colors",
38
+ error ? "border-error-500" : "border-transparent",
39
+ !disabled && !error && "hover:border-neutral-400",
40
+ CONTAINER_HEIGHT[size],
41
+ disabled && "opacity-50"
42
+ );
43
+ }
44
+ function getInputClassName(size, hasLeftIcon, hasRightIcon) {
45
+ return cn(
46
+ "h-full w-full rounded-xl bg-transparent text-body-100 no-underline placeholder:text-body-200 placeholder:opacity-40 focus:outline-none disabled:cursor-not-allowed",
47
+ INPUT_SIZE_CLASSES[size],
48
+ PADDING_LEFT[size][hasLeftIcon ? 1 : 0],
49
+ PADDING_RIGHT[size][hasRightIcon ? 1 : 0]
50
+ );
51
+ }
52
+ const ICON_BASE = "pointer-events-none absolute top-1/2 flex size-5 -translate-y-1/2 items-center justify-center text-body-200";
53
+ function TextFieldIcon({
54
+ children,
55
+ size,
56
+ side
57
+ }) {
58
+ return /* @__PURE__ */ jsx("div", { className: cn(ICON_BASE, side === "left" ? ICON_LEFT[size] : ICON_RIGHT[size]), children });
59
+ }
60
+ function TextFieldHelperText({
61
+ id,
62
+ error,
63
+ children
64
+ }) {
65
+ return /* @__PURE__ */ jsx(
66
+ "p",
67
+ {
68
+ id,
69
+ className: cn(
70
+ "typography-caption-regular px-2 pt-1 pb-0.5",
71
+ error ? "text-error-500" : "text-body-200"
72
+ ),
73
+ children
74
+ }
75
+ );
76
+ }
77
+ function warnMissingAccessibleName(label, ariaLabel, ariaLabelledBy) {
78
+ if (process.env.NODE_ENV !== "production") {
79
+ if (!label && !ariaLabel && !ariaLabelledBy) {
80
+ console.warn(
81
+ "TextField: no accessible name provided. Pass a `label`, `aria-label`, or `aria-labelledby` prop."
82
+ );
83
+ }
84
+ }
85
+ }
86
+ const TextField = React.forwardRef(
87
+ ({
88
+ label,
89
+ helperText,
90
+ size = "48",
91
+ error = false,
92
+ errorMessage,
93
+ leftIcon,
94
+ rightIcon,
95
+ className,
96
+ id,
97
+ disabled,
98
+ fullWidth = false,
99
+ ...props
100
+ }, ref) => {
101
+ const generatedId = React.useId();
102
+ const inputId = id || generatedId;
103
+ const helperTextId = `${inputId}-helper`;
104
+ const bottomText = error && errorMessage ? errorMessage : helperText;
105
+ warnMissingAccessibleName(label, props["aria-label"], props["aria-labelledby"]);
106
+ return /* @__PURE__ */ jsxs(
107
+ "div",
108
+ {
109
+ className: cn("flex flex-col", fullWidth && "w-full", className),
110
+ "data-disabled": disabled ? "" : void 0,
111
+ "data-error": error ? "" : void 0,
112
+ children: [
113
+ label && /* @__PURE__ */ jsx(
114
+ "label",
115
+ {
116
+ htmlFor: inputId,
117
+ className: "typography-caption-semibold px-1 pt-1 pb-2 text-body-100",
118
+ children: label
119
+ }
120
+ ),
121
+ /* @__PURE__ */ jsxs("div", { className: getContainerClassName(size, error, disabled), children: [
122
+ leftIcon && /* @__PURE__ */ jsx(TextFieldIcon, { size, side: "left", children: leftIcon }),
123
+ /* @__PURE__ */ jsx(
124
+ "input",
125
+ {
126
+ ref,
127
+ id: inputId,
128
+ disabled,
129
+ "aria-describedby": bottomText ? helperTextId : void 0,
130
+ "aria-invalid": error || void 0,
131
+ className: getInputClassName(size, !!leftIcon, !!rightIcon),
132
+ ...props
133
+ }
134
+ ),
135
+ rightIcon && /* @__PURE__ */ jsx(TextFieldIcon, { size, side: "right", children: rightIcon })
136
+ ] }),
137
+ bottomText && /* @__PURE__ */ jsx(TextFieldHelperText, { id: helperTextId, error, children: bottomText })
138
+ ]
139
+ }
140
+ );
141
+ }
142
+ );
143
+ TextField.displayName = "TextField";
144
+ export {
145
+ TextField
146
+ };
147
+ //# sourceMappingURL=TextField.mjs.map