@mortenbrudvik/waveui 0.8.0 → 0.10.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 (206) hide show
  1. package/README.md +634 -167
  2. package/dist/components/data-display/InfoButton.cjs +2 -0
  3. package/dist/components/data-display/InfoButton.cjs.map +1 -0
  4. package/dist/components/data-display/InfoButton.mjs +173 -0
  5. package/dist/components/data-display/InfoButton.mjs.map +1 -0
  6. package/dist/components/data-display/InfoLabel.cjs +1 -1
  7. package/dist/components/data-display/InfoLabel.cjs.map +1 -1
  8. package/dist/components/data-display/InfoLabel.mjs +39 -96
  9. package/dist/components/data-display/InfoLabel.mjs.map +1 -1
  10. package/dist/components/input/Calendar.cjs +2 -0
  11. package/dist/components/input/Calendar.cjs.map +1 -0
  12. package/dist/components/input/Calendar.mjs +352 -0
  13. package/dist/components/input/Calendar.mjs.map +1 -0
  14. package/dist/components/input/Calendar.views.cjs +2 -0
  15. package/dist/components/input/Calendar.views.cjs.map +1 -0
  16. package/dist/components/input/Calendar.views.mjs +194 -0
  17. package/dist/components/input/Calendar.views.mjs.map +1 -0
  18. package/dist/components/input/Checkbox.cjs +1 -1
  19. package/dist/components/input/Checkbox.cjs.map +1 -1
  20. package/dist/components/input/Checkbox.mjs +91 -75
  21. package/dist/components/input/Checkbox.mjs.map +1 -1
  22. package/dist/components/input/ColorArea.cjs +1 -1
  23. package/dist/components/input/ColorArea.cjs.map +1 -1
  24. package/dist/components/input/ColorArea.mjs +18 -18
  25. package/dist/components/input/ColorArea.mjs.map +1 -1
  26. package/dist/components/input/ColorPicker.parts.cjs +1 -1
  27. package/dist/components/input/ColorPicker.parts.cjs.map +1 -1
  28. package/dist/components/input/ColorPicker.parts.mjs +1 -1
  29. package/dist/components/input/ColorPicker.parts.mjs.map +1 -1
  30. package/dist/components/input/Combobox.cjs +1 -1
  31. package/dist/components/input/Combobox.cjs.map +1 -1
  32. package/dist/components/input/Combobox.expand.cjs +1 -1
  33. package/dist/components/input/Combobox.expand.cjs.map +1 -1
  34. package/dist/components/input/Combobox.expand.mjs +20 -20
  35. package/dist/components/input/Combobox.expand.mjs.map +1 -1
  36. package/dist/components/input/Combobox.mjs +219 -138
  37. package/dist/components/input/Combobox.mjs.map +1 -1
  38. package/dist/components/input/Combobox.text.cjs +2 -0
  39. package/dist/components/input/Combobox.text.cjs.map +1 -0
  40. package/dist/components/input/Combobox.text.mjs +24 -0
  41. package/dist/components/input/Combobox.text.mjs.map +1 -0
  42. package/dist/components/input/DatePicker.cjs +1 -1
  43. package/dist/components/input/DatePicker.cjs.map +1 -1
  44. package/dist/components/input/DatePicker.mjs +205 -286
  45. package/dist/components/input/DatePicker.mjs.map +1 -1
  46. package/dist/components/input/Dropdown.cjs +1 -1
  47. package/dist/components/input/Dropdown.cjs.map +1 -1
  48. package/dist/components/input/Dropdown.mjs +137 -105
  49. package/dist/components/input/Dropdown.mjs.map +1 -1
  50. package/dist/components/input/Field.cjs +1 -1
  51. package/dist/components/input/Field.cjs.map +1 -1
  52. package/dist/components/input/Field.mjs +99 -66
  53. package/dist/components/input/Field.mjs.map +1 -1
  54. package/dist/components/input/Input.cjs +1 -1
  55. package/dist/components/input/Input.cjs.map +1 -1
  56. package/dist/components/input/Input.mjs +79 -55
  57. package/dist/components/input/Input.mjs.map +1 -1
  58. package/dist/components/input/Label.cjs +1 -1
  59. package/dist/components/input/Label.cjs.map +1 -1
  60. package/dist/components/input/Label.mjs +19 -13
  61. package/dist/components/input/Label.mjs.map +1 -1
  62. package/dist/components/input/Listbox.cjs +2 -0
  63. package/dist/components/input/Listbox.cjs.map +1 -0
  64. package/dist/components/input/Listbox.mjs +134 -0
  65. package/dist/components/input/Listbox.mjs.map +1 -0
  66. package/dist/components/input/Option.cjs +1 -1
  67. package/dist/components/input/Option.cjs.map +1 -1
  68. package/dist/components/input/Option.mjs +158 -97
  69. package/dist/components/input/Option.mjs.map +1 -1
  70. package/dist/components/input/Rating.cjs +1 -1
  71. package/dist/components/input/Rating.cjs.map +1 -1
  72. package/dist/components/input/Rating.item.cjs +2 -0
  73. package/dist/components/input/Rating.item.cjs.map +1 -0
  74. package/dist/components/input/Rating.item.mjs +255 -0
  75. package/dist/components/input/Rating.item.mjs.map +1 -0
  76. package/dist/components/input/Rating.mjs +150 -151
  77. package/dist/components/input/Rating.mjs.map +1 -1
  78. package/dist/components/input/SearchBox.cjs +1 -1
  79. package/dist/components/input/SearchBox.cjs.map +1 -1
  80. package/dist/components/input/SearchBox.mjs +146 -115
  81. package/dist/components/input/SearchBox.mjs.map +1 -1
  82. package/dist/components/input/Select.cjs +1 -1
  83. package/dist/components/input/Select.cjs.map +1 -1
  84. package/dist/components/input/Select.mjs +34 -25
  85. package/dist/components/input/Select.mjs.map +1 -1
  86. package/dist/components/input/Slider.cjs +1 -1
  87. package/dist/components/input/Slider.cjs.map +1 -1
  88. package/dist/components/input/Slider.mjs +77 -54
  89. package/dist/components/input/Slider.mjs.map +1 -1
  90. package/dist/components/input/SpinButton.cjs +1 -1
  91. package/dist/components/input/SpinButton.cjs.map +1 -1
  92. package/dist/components/input/SpinButton.mjs +198 -96
  93. package/dist/components/input/SpinButton.mjs.map +1 -1
  94. package/dist/components/input/SwatchPicker.cjs +1 -1
  95. package/dist/components/input/SwatchPicker.cjs.map +1 -1
  96. package/dist/components/input/SwatchPicker.context.cjs +2 -0
  97. package/dist/components/input/SwatchPicker.context.cjs.map +1 -0
  98. package/dist/components/input/SwatchPicker.context.mjs +35 -0
  99. package/dist/components/input/SwatchPicker.context.mjs.map +1 -0
  100. package/dist/components/input/SwatchPicker.mjs +131 -103
  101. package/dist/components/input/SwatchPicker.mjs.map +1 -1
  102. package/dist/components/input/SwatchPicker.swatches.cjs +2 -0
  103. package/dist/components/input/SwatchPicker.swatches.cjs.map +1 -0
  104. package/dist/components/input/SwatchPicker.swatches.mjs +207 -0
  105. package/dist/components/input/SwatchPicker.swatches.mjs.map +1 -0
  106. package/dist/components/input/Switch.cjs +1 -1
  107. package/dist/components/input/Switch.cjs.map +1 -1
  108. package/dist/components/input/Switch.mjs +71 -55
  109. package/dist/components/input/Switch.mjs.map +1 -1
  110. package/dist/components/input/TagPicker.cjs +1 -1
  111. package/dist/components/input/TagPicker.cjs.map +1 -1
  112. package/dist/components/input/TagPicker.mjs +154 -123
  113. package/dist/components/input/TagPicker.mjs.map +1 -1
  114. package/dist/components/input/Textarea.cjs +1 -1
  115. package/dist/components/input/Textarea.cjs.map +1 -1
  116. package/dist/components/input/Textarea.mjs +28 -21
  117. package/dist/components/input/Textarea.mjs.map +1 -1
  118. package/dist/components/input/TimePicker.cjs +1 -1
  119. package/dist/components/input/TimePicker.cjs.map +1 -1
  120. package/dist/components/input/TimePicker.mjs +204 -199
  121. package/dist/components/input/TimePicker.mjs.map +1 -1
  122. package/dist/components/input/dateUtils.cjs +1 -1
  123. package/dist/components/input/dateUtils.cjs.map +1 -1
  124. package/dist/components/input/dateUtils.mjs +114 -96
  125. package/dist/components/input/dateUtils.mjs.map +1 -1
  126. package/dist/components/input/fieldLabel.cjs +2 -0
  127. package/dist/components/input/fieldLabel.cjs.map +1 -0
  128. package/dist/components/input/fieldLabel.mjs +17 -0
  129. package/dist/components/input/fieldLabel.mjs.map +1 -0
  130. package/dist/components/input/inputLook.cjs +2 -0
  131. package/dist/components/input/inputLook.cjs.map +1 -0
  132. package/dist/components/input/inputLook.mjs +28 -0
  133. package/dist/components/input/inputLook.mjs.map +1 -0
  134. package/dist/components/input/pickerLabels.cjs +2 -0
  135. package/dist/components/input/pickerLabels.cjs.map +1 -0
  136. package/dist/components/input/pickerLabels.mjs +16 -0
  137. package/dist/components/input/pickerLabels.mjs.map +1 -0
  138. package/dist/components/input/pickerStyles.cjs +1 -1
  139. package/dist/components/input/pickerStyles.cjs.map +1 -1
  140. package/dist/components/input/pickerStyles.mjs +42 -4
  141. package/dist/components/input/pickerStyles.mjs.map +1 -1
  142. package/dist/components/input/pickerValues.cjs +2 -0
  143. package/dist/components/input/pickerValues.cjs.map +1 -0
  144. package/dist/components/input/pickerValues.mjs +24 -0
  145. package/dist/components/input/pickerValues.mjs.map +1 -0
  146. package/dist/components/navigation/Menu.popover.cjs +1 -1
  147. package/dist/components/navigation/Menu.popover.cjs.map +1 -1
  148. package/dist/components/navigation/Menu.popover.mjs +10 -10
  149. package/dist/components/navigation/Menu.root.cjs +1 -1
  150. package/dist/components/navigation/Menu.root.cjs.map +1 -1
  151. package/dist/components/navigation/Menu.root.mjs +10 -10
  152. package/dist/components/overlays/Popover.cjs +1 -1
  153. package/dist/components/overlays/Popover.cjs.map +1 -1
  154. package/dist/components/overlays/Popover.mjs +203 -208
  155. package/dist/components/overlays/Popover.mjs.map +1 -1
  156. package/dist/components/overlays/Popover.shared.cjs +1 -1
  157. package/dist/components/overlays/Popover.shared.cjs.map +1 -1
  158. package/dist/components/overlays/Popover.shared.mjs +82 -77
  159. package/dist/components/overlays/Popover.shared.mjs.map +1 -1
  160. package/dist/components/provider/WaveProvider.cjs +1 -1
  161. package/dist/components/provider/WaveProvider.cjs.map +1 -1
  162. package/dist/components/provider/WaveProvider.mjs +25 -19
  163. package/dist/components/provider/WaveProvider.mjs.map +1 -1
  164. package/dist/components/table/useGridNavigation.cjs +1 -1
  165. package/dist/components/table/useGridNavigation.cjs.map +1 -1
  166. package/dist/components/table/useGridNavigation.mjs +67 -70
  167. package/dist/components/table/useGridNavigation.mjs.map +1 -1
  168. package/dist/hooks/useActiveDescendant.cjs +2 -0
  169. package/dist/hooks/useActiveDescendant.cjs.map +1 -0
  170. package/dist/hooks/useActiveDescendant.mjs +56 -0
  171. package/dist/hooks/useActiveDescendant.mjs.map +1 -0
  172. package/dist/hooks/useFieldControl.cjs.map +1 -1
  173. package/dist/hooks/useFieldControl.mjs.map +1 -1
  174. package/dist/hooks/useListbox.cjs +1 -1
  175. package/dist/hooks/useListbox.cjs.map +1 -1
  176. package/dist/hooks/useListbox.mjs +250 -210
  177. package/dist/hooks/useListbox.mjs.map +1 -1
  178. package/dist/hooks/useRovingGrid.cjs +2 -0
  179. package/dist/hooks/useRovingGrid.cjs.map +1 -0
  180. package/dist/hooks/useRovingGrid.mjs +69 -0
  181. package/dist/hooks/useRovingGrid.mjs.map +1 -0
  182. package/dist/index.cjs +1 -1
  183. package/dist/index.d.cts +2901 -203
  184. package/dist/index.d.ts +2901 -203
  185. package/dist/index.mjs +107 -99
  186. package/dist/lib/classNames.cjs +1 -1
  187. package/dist/lib/classNames.cjs.map +1 -1
  188. package/dist/lib/classNames.mjs +111 -67
  189. package/dist/lib/classNames.mjs.map +1 -1
  190. package/dist/lib/date.cjs +2 -0
  191. package/dist/lib/date.cjs.map +1 -0
  192. package/dist/lib/date.mjs +44 -0
  193. package/dist/lib/date.mjs.map +1 -0
  194. package/dist/lib/focus.cjs +1 -1
  195. package/dist/lib/focus.cjs.map +1 -1
  196. package/dist/lib/focus.mjs +5 -2
  197. package/dist/lib/focus.mjs.map +1 -1
  198. package/dist/lib/layers.cjs.map +1 -1
  199. package/dist/lib/layers.mjs.map +1 -1
  200. package/dist/lib/styles.cjs +1 -1
  201. package/dist/lib/styles.cjs.map +1 -1
  202. package/dist/lib/styles.mjs +21 -4
  203. package/dist/lib/styles.mjs.map +1 -1
  204. package/dist/styles.css +1 -1
  205. package/package.json +1 -1
  206. package/src/styles/tokens.css +13 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SwatchPicker.swatches.cjs","names":["CheckIcon","cn","unwrapButtonGlyph","useSwatchPickerContext","motionSafeTransition","focusRing","composeEventHandlers","swatchPickerClassNames","forcedColors","slotRendersContent","renderSlot","colorSwatchClassNames","getCheckColors","imageSwatchClassNames","useId","AddIcon","emptySwatchClassNames","require_runtime","require_cn","require_dev","require_slot","require_composeEventHandlers","require_classNames","require_useId","require_styles","require_Button_slots","require_icons","require_SwatchPicker_context","require_colorUtils","react","react_jsx_runtime","sizeMap","glyphSizeMap","shapeMap","SwatchCheck","colors","SwatchSlash","warnUnnamedSwatch","kind","value","fallback","useSwatchIcon","icon","useSwatchIconWarning","button","glyph","useSwatchContext","context","size","shape","outlineOffset","spacing","SwatchGridCell","selected","swatchBaseClasses","SwatchRadio","sizeProp","shapeProp","ariaLabel","ariaLabelledBy","rest","selectedValue","isSelected","isTab","focusMode","isGrid","layout","register","named","fallbackKind","defaultName","getTabIndex","onClick","select","rootClassName","paintClassName","keepsColors","className","style","paint","checkColors","iconClassName","disabled","ColorSwatch","color","borderColor","ImageSwatch","src","EmptySwatch","cellValue","content","control"],"sources":["../../../src/components/input/SwatchPicker.swatches.tsx"],"sourcesContent":["import * as React from 'react';\nimport {\n colorSwatchClassNames,\n emptySwatchClassNames,\n imageSwatchClassNames,\n swatchPickerClassNames,\n} from '../../lib/classNames';\nimport { cn } from '../../lib/cn';\nimport { composeEventHandlers } from '../../lib/composeEventHandlers';\nimport { warnOnce } from '../../lib/dev';\nimport { AddIcon, CheckIcon } from '../../lib/icons';\nimport { renderSlot, slotRendersContent } from '../../lib/slot';\nimport type { Slot } from '../../lib/slot';\nimport { focusRing, forcedColors, motionSafeTransition } from '../../lib/styles';\nimport type { Shape } from '../../lib/types';\nimport { useId } from '../../hooks/useId';\nimport { unwrapButtonGlyph, type UnwrappedButton } from '../button/Button.slots';\nimport { useSwatchPickerContext, type SwatchPickerSize } from './SwatchPicker.context';\nimport { getCheckColors, type CheckColors } from './colorUtils';\n\n/*\n * The swatch kinds a `SwatchPicker` renders `items` through and a consumer can render as children\n * (D18, D21): `ColorSwatch`, `ImageSwatch` and `EmptySwatch`. Every swatch reads\n * `SwatchPickerContext` and puts its `ref`, `className`, `style`, name and rest props on its own\n * `<button>` (an explicit exception to C-ROUTING: there is no separate root, so a wrapping\n * Tooltip's ARIA and handlers reach the focused element without extra wiring).\n */\n\nconst sizeMap: Record<SwatchPickerSize, string> = {\n 'extra-small': 'w-5 h-5',\n small: 'w-6 h-6',\n medium: 'w-8 h-8',\n large: 'w-10 h-10',\n};\n\nconst glyphSizeMap: Record<SwatchPickerSize, number> = {\n 'extra-small': 10,\n small: 12,\n medium: 16,\n large: 20,\n};\n\nconst shapeMap: Record<Shape, string> = {\n circular: 'rounded-full',\n square: 'rounded-none',\n rounded: 'rounded',\n};\n\n/**\n * The check glyph of the selected swatch: black or white by the swatch's luminance, drawn over a\n * halo in the other color so it stays visible on any swatch (C-TOKENS exception for\n * user-supplied colors, `input-pickers#16`). Without `colors` (an image has no single luminance)\n * it is the theme's foreground glyph over a background halo.\n */\nfunction SwatchCheck({ colors, size }: { colors: CheckColors | null; size: number }) {\n return (\n <span aria-hidden=\"true\" className=\"pointer-events-none grid\">\n <CheckIcon\n size={size}\n strokeWidth={4}\n className={cn('col-start-1 row-start-1', !colors && 'text-background')}\n style={colors ? { color: colors.halo } : undefined}\n />\n <CheckIcon\n size={size}\n strokeWidth={2}\n className={cn('col-start-1 row-start-1', !colors && 'text-foreground')}\n style={colors ? { color: colors.glyph } : undefined}\n />\n </span>\n );\n}\n\n/**\n * The diagonal slash of a disabled swatch, drawn like the check glyph (a halo line under a glyph\n * line), so the state does not rely on the swatch color or its opacity.\n */\nfunction SwatchSlash({ colors }: { colors: CheckColors | null }) {\n return (\n <svg\n data-wave-swatch-slash=\"\"\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n className=\"pointer-events-none absolute inset-0 h-full w-full\"\n >\n <line\n x1=\"5\"\n y1=\"19\"\n x2=\"19\"\n y2=\"5\"\n stroke=\"currentColor\"\n strokeLinecap=\"round\"\n strokeWidth={4}\n className={colors ? undefined : 'text-background'}\n style={colors ? { color: colors.halo } : undefined}\n />\n <line\n x1=\"5\"\n y1=\"19\"\n x2=\"19\"\n y2=\"5\"\n stroke=\"currentColor\"\n strokeLinecap=\"round\"\n strokeWidth={2}\n className={colors ? undefined : 'text-foreground'}\n style={colors ? { color: colors.glyph } : undefined}\n />\n </svg>\n );\n}\n\n/** Warns once per swatch kind (not per value: `<Kind>:unnamed`, e.g. `ColorSwatch:unnamed`). */\nfunction warnUnnamedSwatch(kind: string, value: string, fallback: string): void {\n warnOnce(\n `${kind}:unnamed`,\n `${kind}: swatch \"${value}\" has no accessible name, so it is announced by its ${fallback}. ` +\n 'Pass `aria-label`, or wrap it in a Tooltip with `relationship=\"label\"`.',\n );\n}\n\n/**\n * A swatch's `icon` as a glyph inside its wired button (C-SLOTS): a `<button>` or `Button` is\n * unwrapped (its children become the glyph) with a one-time warning (`<Kind>:icon-button`).\n */\nfunction useSwatchIcon(kind: string, icon: Slot<'span'> | undefined): Slot<'span'> | undefined {\n const { glyph, button } = unwrapButtonGlyph(icon);\n useSwatchIconWarning(kind, button);\n return glyph;\n}\n\n/** The one-time `<Kind>:icon-button` warning for an unwrapped button. */\nfunction useSwatchIconWarning(kind: string, button: UnwrappedButton | null): void {\n React.useEffect(() => {\n if (button) {\n warnOnce(\n `${kind}:icon-button`,\n `${kind}: \\`icon\\` received ${button}; its children render as the glyph of the swatch ` +\n 'and its props were dropped (buttons cannot be nested). Pass icon content instead, ' +\n 'e.g. `icon={<MyIcon />}`.',\n );\n }\n }, [button, kind]);\n}\n\n/** The picker's context with this swatch's own `size` and `shape` applied over the picker's. */\nfunction useSwatchContext(kind: string, size?: SwatchPickerSize, shape?: Shape) {\n const context = useSwatchPickerContext(kind);\n return { ...context, size: size ?? context.size, shape: shape ?? context.shape };\n}\n\n/**\n * Focus outline offset: 4px, clear of a selected swatch's ring band (ring and offset, 4px); 2px at\n * `spacing=\"small\"` so it does not cover the neighbour (D22), where it lies over that band.\n */\nfunction outlineOffset(spacing: 'small' | 'medium'): string {\n return spacing === 'small' ? 'focus-visible:outline-offset-2' : 'focus-visible:outline-offset-4';\n}\n\n/**\n * The cell around a swatch's button in the grid layout: it carries only the role and, for a swatch\n * with a value, `aria-selected` (a `gridcell` role on the button would replace its button role,\n * which `aria-pressed` needs). Every consumer prop stays on the button.\n */\nfunction SwatchGridCell({ selected, children }: { selected?: boolean; children: React.ReactNode }) {\n return (\n <div role=\"gridcell\" aria-selected={selected} className=\"flex shrink-0\">\n {children}\n </div>\n );\n}\n\n/** The classes every swatch kind shares: size, shape, box, motion, focus ring and disabled look. */\nfunction swatchBaseClasses(\n size: SwatchPickerSize,\n shape: Shape,\n spacing: 'small' | 'medium',\n): string {\n return cn(\n sizeMap[size],\n shapeMap[shape],\n // The padding is set here (C-NATIVE).\n 'relative flex shrink-0 items-center justify-center border-2 p-0',\n motionSafeTransition,\n focusRing,\n outlineOffset(spacing),\n 'disabled:cursor-not-allowed disabled:opacity-50',\n );\n}\n\n/** Properties for the ColorSwatch component. */\nexport interface ColorSwatchProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n 'value' | 'color'\n> {\n /** Unique identifier for the swatch (the value the picker selects and a form submits). */\n value: string;\n /** CSS color value (e.g., hex, rgb) used as the swatch background (a runtime user color). */\n color: string;\n /**\n * Decorative glyph (`aria-hidden`) shown while the swatch is not selected; the check replaces it\n * while selected. A `<button>` or `Button` passed here is not nested: its children become the\n * glyph and a one-time development warning is logged.\n */\n icon?: Slot<'span'>;\n /**\n * CSS color of the swatch's border, for a color close to the page background (set by hand, as\n * in Fluent). In forced colors the system border color replaces it.\n */\n borderColor?: string;\n /**\n * Size of the swatch (20, 24, 32 or 40px).\n * @default the picker's\n */\n size?: SwatchPickerSize;\n /**\n * Shape of the swatch.\n * @default the picker's\n */\n shape?: Shape;\n /**\n * Ref to the swatch button: `role=\"radio\"` in the row layout, inside its cell in the grid\n * layout, a toggle button in tab mode.\n */\n ref?: React.Ref<HTMLButtonElement>;\n}\n\n/** What `ColorSwatch` and `ImageSwatch` do differently. */\ninterface SwatchRadioProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n 'value' | 'color'\n> {\n kind: 'ColorSwatch' | 'ImageSwatch';\n /** The kind's stable root class, named by the module that sets its `displayName` (P3-00). */\n rootClassName: string;\n /** The kind's stable `icon` part class (ColorSwatch only). */\n iconClassName?: string;\n value: string;\n /** What names the swatch when it has no name: 'color value' or 'value' (the warning's text). */\n fallbackKind: string;\n /** The accessible name of an unnamed swatch. */\n defaultName: string;\n /** The luminance-picked glyph colors; `null`: the theme's foreground over a background halo. */\n checkColors: CheckColors | null;\n /** The swatch's own background, border color and image (they win over a consumer `style`). */\n paint: Record<string, string>;\n /** Classes of the image swatch's covering background. */\n paintClassName?: string;\n /** A color swatch keeps its colors in forced colors (the color is the content). */\n keepsColors: boolean;\n icon?: Slot<'span'>;\n size?: SwatchPickerSize;\n shape?: Shape;\n ref?: React.Ref<HTMLButtonElement>;\n}\n\n/** The radio button shared by `ColorSwatch` and `ImageSwatch`. */\nconst SwatchRadio = ({\n kind,\n rootClassName,\n iconClassName,\n value,\n fallbackKind,\n defaultName,\n checkColors,\n paint,\n paintClassName,\n keepsColors,\n icon,\n size: sizeProp,\n shape: shapeProp,\n className,\n style,\n onClick,\n disabled,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n ref,\n ...rest\n}: SwatchRadioProps) => {\n const {\n value: selectedValue,\n select,\n size,\n shape,\n layout,\n focusMode,\n spacing,\n getTabIndex,\n register,\n } = useSwatchContext(kind, sizeProp, shapeProp);\n const isSelected = selectedValue === value;\n const isTab = focusMode === 'tab';\n const isGrid = layout === 'grid' && !isTab;\n const glyph = useSwatchIcon(kind, icon);\n\n React.useLayoutEffect(() => register(value), [register, value]);\n\n const named = ariaLabel !== undefined || ariaLabelledBy !== undefined;\n React.useEffect(() => {\n if (!named) warnUnnamedSwatch(kind, value, fallbackKind);\n }, [named, kind, value, fallbackKind]);\n\n const button = (\n <button\n type=\"button\"\n // The grid layout: a plain button inside its gridcell, pressed while selected (D20); tab\n // mode (D19): a toggle button of a group, `aria-pressed` always present.\n role={isGrid || isTab ? undefined : 'radio'}\n {...rest}\n ref={ref}\n disabled={disabled}\n aria-label={ariaLabel ?? (ariaLabelledBy ? undefined : defaultName)}\n aria-labelledby={ariaLabelledBy}\n aria-checked={isGrid || isTab ? undefined : isSelected}\n aria-pressed={isTab ? isSelected : isGrid ? isSelected || undefined : undefined}\n data-roving-value={value}\n data-selected={isSelected ? '' : undefined}\n tabIndex={getTabIndex(value)}\n onClick={composeEventHandlers(onClick, () => select(value))}\n className={cn(\n rootClassName,\n // Every swatch in a picker carries the picker's part class, children included (0.8).\n swatchPickerClassNames.swatch,\n swatchBaseClasses(size, shape, spacing),\n // A `borderColor` arrives as a variable, so the forced-colors edge below still wins.\n 'border-[color:var(--wave-swatch-border,transparent)]',\n paintClassName,\n // User colors are the content: keep them (and the check glyph) in forced colors, with a\n // Highlight outline, which an opted-out element would otherwise draw in author colors.\n keepsColors &&\n 'forced-colors:forced-color-adjust-none forced-colors:focus-visible:outline-[Highlight]',\n forcedColors.border,\n // An image stays in forced colors, so its selection needs a sign of its own.\n isSelected && !keepsColors && 'forced-colors:border-[Highlight]',\n isSelected\n ? cn(\n 'ring-2 ring-foreground ring-offset-2 ring-offset-background',\n keepsColors && 'forced-colors:ring-[Highlight]',\n )\n : 'not-disabled:not-aria-disabled:hover:border-stroke-hover',\n className,\n )}\n style={{ ...style, ...paint }}\n >\n {isSelected ? (\n <SwatchCheck colors={checkColors} size={glyphSizeMap[size]} />\n ) : (\n glyph != null &&\n slotRendersContent(glyph) &&\n renderSlot(glyph, 'span', cn(iconClassName, 'pointer-events-none inline-flex'), {\n 'aria-hidden': true,\n style: checkColors ? { color: checkColors.glyph } : undefined,\n })\n )}\n {disabled && <SwatchSlash colors={checkColors} />}\n </button>\n );\n return isGrid ? <SwatchGridCell selected={isSelected}>{button}</SwatchGridCell> : button;\n};\nSwatchRadio.displayName = 'SwatchRadio';\n\n// D18: swatches read SwatchPickerContext; their own ref/className/style/rest go straight to the\n// button (an explicit exception to C-ROUTING's root/control split).\n/**\n * One color swatch of a `SwatchPicker`, read from `items` or rendered directly as a child.\n * Outside a `SwatchPicker` it throws in development and renders inertly in production.\n *\n * **Naming**: give it `aria-label`, or wrap it in a `Tooltip` with `relationship=\"label\"` (its\n * `aria-labelledby` reaches this element, since every rest prop lands on the swatch's own\n * button). Without either it is named by its `color` value and a development warning is logged.\n *\n * **Disabled**: `disabled` is native (out of the arrow keys and the tab order, never selected);\n * the swatch is dimmed and crossed by a diagonal slash, so the state does not rely on its color.\n *\n * @example\n * <SwatchPicker aria-label=\"Accent color\">\n * <ColorSwatch value=\"red\" color=\"#d13438\" aria-label=\"Red\" />\n * </SwatchPicker>\n */\nexport const ColorSwatch = ({ color, borderColor, ...rest }: ColorSwatchProps) => {\n return (\n <SwatchRadio\n {...rest}\n kind=\"ColorSwatch\"\n rootClassName={colorSwatchClassNames.root}\n iconClassName={colorSwatchClassNames.icon}\n fallbackKind=\"color value\"\n defaultName={color}\n checkColors={getCheckColors(color)}\n keepsColors\n paint={{\n backgroundColor: color,\n ...(borderColor !== undefined && { '--wave-swatch-border': borderColor }),\n }}\n />\n );\n};\nColorSwatch.displayName = 'ColorSwatch';\n\n/** Properties for the ImageSwatch component. */\nexport interface ImageSwatchProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n 'value'\n> {\n /** Unique identifier for the swatch (the value the picker selects and a form submits). */\n value: string;\n /** URL of the image (drawn as a covering background image). */\n src: string;\n /**\n * Size of the swatch (20, 24, 32 or 40px).\n * @default the picker's\n */\n size?: SwatchPickerSize;\n /**\n * Shape of the swatch.\n * @default the picker's\n */\n shape?: Shape;\n /**\n * Ref to the swatch button: `role=\"radio\"` in the row layout, inside its cell in the grid\n * layout, a toggle button in tab mode.\n */\n ref?: React.Ref<HTMLButtonElement>;\n}\n\n/**\n * One image swatch of a `SwatchPicker`: a pattern or texture drawn as a covering background\n * image. Outside a `SwatchPicker` it throws in development and renders inertly in production.\n *\n * **Naming**: give it `aria-label`, or wrap it in a `Tooltip` with `relationship=\"label\"`.\n * Without either it is named by its `value` and a development warning is logged.\n *\n * **Selected and disabled**: an image has no single luminance, so the check and the disabled slash\n * are the theme's foreground glyph over a background halo. Unlike `ColorSwatch` it does not opt\n * out of forced colors (a background image stays).\n *\n * @example\n * <SwatchPicker aria-label=\"Pattern\">\n * <ImageSwatch value=\"dots\" src=\"/dots.png\" aria-label=\"Dots\" />\n * </SwatchPicker>\n */\nexport const ImageSwatch = ({ src, ...rest }: ImageSwatchProps) => {\n return (\n <SwatchRadio\n {...rest}\n kind=\"ImageSwatch\"\n rootClassName={imageSwatchClassNames.root}\n fallbackKind=\"value\"\n defaultName={rest.value}\n checkColors={null}\n keepsColors={false}\n paintClassName=\"bg-cover bg-center bg-no-repeat\"\n paint={{ backgroundImage: `url(${JSON.stringify(src)})` }}\n />\n );\n};\nImageSwatch.displayName = 'ImageSwatch';\n\n/** Properties for the EmptySwatch component. */\nexport interface EmptySwatchProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /**\n * Accessible name of the button.\n * @default 'Add color'\n */\n 'aria-label'?: string;\n /**\n * Decorative glyph (`aria-hidden`). `null` and `undefined` keep the plus glyph; `false` or\n * anything that renders nothing shows none. A `<button>` or `Button` passed here is not nested:\n * its children become the glyph and a one-time development warning is logged.\n * @default a plus glyph\n */\n icon?: Slot<'span'>;\n /**\n * Size of the swatch (20, 24, 32 or 40px).\n * @default the picker's\n */\n size?: SwatchPickerSize;\n /**\n * Shape of the swatch.\n * @default the picker's\n */\n shape?: Shape;\n /** Ref to the button. */\n ref?: React.Ref<HTMLButtonElement>;\n}\n\n/**\n * An \"add\" button inside a `SwatchPicker` (for example to open a color dialog): it has no value\n * and is never selected. In the row layout it is a plain button with its own tab stop, outside\n * the radio group's arrow order, and arrow keys pressed on it do nothing. In the grid layout it\n * is a cell like the others, in the arrow order (without `aria-selected`). Outside a\n * `SwatchPicker` it throws in development and renders inertly in production.\n *\n * @example\n * <SwatchPicker aria-label=\"Accent color\" items={items}>\n * <EmptySwatch onClick={openColorDialog} />\n * </SwatchPicker>\n */\nexport const EmptySwatch = ({\n icon,\n size: sizeProp,\n shape: shapeProp,\n className,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n ref,\n ...rest\n}: EmptySwatchProps) => {\n const { size, shape, layout, focusMode, spacing, getTabIndex } = useSwatchContext(\n 'EmptySwatch',\n sizeProp,\n shapeProp,\n );\n // Tab mode has no grid cells: the button is a tab stop of its own, like in the row layout.\n const isGrid = layout === 'grid' && focusMode !== 'tab';\n // The grid's roving keys address cells by value: an EmptySwatch, which has none, gets its own\n // (never selected, never submitted).\n const cellValue = useId('wave-empty-swatch');\n const { glyph, button } = unwrapButtonGlyph(icon);\n useSwatchIconWarning('EmptySwatch', button);\n // The optional-indicator rule: `null`/`undefined` keep the plus, anything that renders nothing\n // hides it; an unwrapped button whose children render nothing keeps the plus too.\n const content =\n icon == null || (button !== null && !slotRendersContent(glyph)) ? (\n <AddIcon size={glyphSizeMap[size]} />\n ) : (\n glyph\n );\n\n const control = (\n <button\n type=\"button\"\n {...rest}\n ref={ref}\n aria-label={ariaLabel ?? (ariaLabelledBy ? undefined : 'Add color')}\n aria-labelledby={ariaLabelledBy}\n // The row layout's root skips its arrow handling for events from here.\n data-wave-empty-swatch=\"\"\n {...(isGrid && { 'data-roving-value': cellValue, tabIndex: getTabIndex(cellValue) })}\n className={cn(\n emptySwatchClassNames.root,\n swatchPickerClassNames.swatch,\n swatchBaseClasses(size, shape, spacing),\n 'border-dashed border-stroke-accessible bg-transparent text-foreground',\n forcedColors.control,\n 'not-disabled:not-aria-disabled:hover:bg-subtle-hover',\n className,\n )}\n >\n {slotRendersContent(content) &&\n renderSlot(\n content,\n 'span',\n cn(emptySwatchClassNames.icon, 'pointer-events-none inline-flex'),\n {\n 'aria-hidden': true,\n },\n )}\n </button>\n );\n return isGrid ? <SwatchGridCell>{control}</SwatchGridCell> : control;\n};\nEmptySwatch.displayName = 'EmptySwatch';\n"],"mappings":"uhBA4BA,IAAM+B,EAA4C,CAChD,cAAe,UACf,MAAO,UACP,OAAQ,UACR,MAAO,WACT,EAEMC,EAAiD,CACrD,cAAe,GACf,MAAO,GACP,OAAQ,GACR,MAAO,EACT,EAEMC,EAAkC,CACtC,SAAU,eACV,OAAQ,eACR,QAAS,SACX,EAQA,SAASC,EAAY,CAAE,SAAQ,QAAsD,CACnF,OACE,EAAAJ,EAAA,KAAA,CAAC,OAAD,CAAM,cAAY,OAAO,UAAU,2BAAnC,SAAA,EACE,EAAAA,EAAA,IAAA,CAAC9B,EAAAA,UAAD,CACQ,OACN,YAAa,EACb,UAAWC,EAAAA,GAAG,0BAA2B,CAACkC,GAAU,iBAAiB,EACrE,MAAOA,EAAS,CAAE,MAAOA,EAAO,IAAK,EAAI,IAAA,EAC1C,CAAA,GACD,EAAAL,EAAA,IAAA,CAAC9B,EAAAA,UAAD,CACQ,OACN,YAAa,EACb,UAAWC,EAAAA,GAAG,0BAA2B,CAACkC,GAAU,iBAAiB,EACrE,MAAOA,EAAS,CAAE,MAAOA,EAAO,KAAM,EAAI,IAAA,EAC3C,CAAA,CACG,GAEV,CAMA,SAASC,EAAY,CAAE,UAA0C,CAC/D,OACE,EAAAN,EAAA,KAAA,CAAC,MAAD,CACE,yBAAuB,GACvB,cAAY,OACZ,QAAQ,YACR,KAAK,OACL,UAAU,qDALZ,SAAA,EAOE,EAAAA,EAAA,IAAA,CAAC,OAAD,CACE,GAAG,IACH,GAAG,KACH,GAAG,KACH,GAAG,IACH,OAAO,eACP,cAAc,QACd,YAAa,EACb,UAAWK,EAAS,IAAA,GAAY,kBAChC,MAAOA,EAAS,CAAE,MAAOA,EAAO,IAAK,EAAI,IAAA,EAC1C,CAAA,GACD,EAAAL,EAAA,IAAA,CAAC,OAAD,CACE,GAAG,IACH,GAAG,KACH,GAAG,KACH,GAAG,IACH,OAAO,eACP,cAAc,QACd,YAAa,EACb,UAAWK,EAAS,IAAA,GAAY,kBAChC,MAAOA,EAAS,CAAE,MAAOA,EAAO,KAAM,EAAI,IAAA,EAC3C,CAAA,CACE,GAET,CAGA,SAASE,EAAkB,EAAc,EAAe,EAAwB,CAC9ElB,EAAA,SACE,GAAGmB,EAAK,UACR,GAAGA,EAAK,YAAYC,EAAM,sDAAsDC,EAAS,8EAE3F,CACF,CAMA,SAASC,EAAc,EAAc,EAA0D,CAC7F,GAAM,CAAE,QAAO,UAAWvC,EAAAA,kBAAkBwC,CAAI,EAEhD,OADAC,EAAqBL,EAAMM,CAAM,EAC1BC,CACT,CAGA,SAASF,EAAqB,EAAc,EAAsC,CAChFd,EAAM,cAAgB,CAChBe,GACFzB,EAAA,SACE,GAAGmB,EAAK,cACR,GAAGA,EAAK,sBAAsBM,EAAO,+JAGvC,CAEJ,EAAG,CAACA,EAAQN,CAAI,CAAC,CACnB,CAGA,SAASQ,EAAiB,EAAc,EAAyB,EAAe,CAC9E,IAAMC,EAAU5C,EAAAA,uBAAuBmC,CAAI,EAC3C,MAAO,CAAE,GAAGS,EAAS,KAAMC,GAAQD,EAAQ,KAAM,MAAOE,GAASF,EAAQ,KAAM,CACjF,CAMA,SAASG,EAAc,EAAqC,CAC1D,OAAOC,IAAY,QAAU,iCAAmC,gCAClE,CAOA,SAASC,EAAe,CAAE,WAAU,YAA+D,CACjG,OACE,EAAAtB,EAAA,IAAA,CAAC,MAAD,CAAK,KAAK,WAAW,gBAAeuB,EAAU,UAAU,gBACrD,UACE,CAAA,CAET,CAGA,SAASC,EACP,EACA,EACA,EACQ,CACR,OAAOrD,EAAAA,GACL8B,EAAQiB,GACRf,EAASgB,GAET,kEACA7C,EAAAA,qBACAC,EAAAA,UACA6C,EAAcC,CAAO,EACrB,iDACF,CACF,CAqEA,IAAMI,GAAe,CACnB,OACA,gBACA,gBACA,QACA,eACA,cACA,cACA,QACA,iBACA,cACA,OACA,KAAMC,EACN,MAAOC,EACP,YACA,QACA,UACA,WACA,aAAcC,EACd,kBAAmBC,EACnB,MACA,GAAGC,KACmB,CACtB,GAAM,CACJ,MAAOC,EACP,SACA,OACA,QACA,SACA,YACA,UACA,cACA,YACEf,EAAiBR,EAAMkB,EAAUC,CAAS,EACxCK,EAAaD,IAAkBtB,EAC/BwB,EAAQC,IAAc,MACtBC,EAASC,IAAW,QAAU,CAACH,EAC/BlB,EAAQJ,EAAcH,EAAMI,CAAI,EAEtCb,EAAM,oBAAsBsC,EAAS5B,CAAK,EAAG,CAAC4B,EAAU5B,CAAK,CAAC,EAE9D,IAAM6B,EAAQV,IAAc,IAAA,IAAaC,IAAmB,IAAA,GAC5D9B,EAAM,cAAgB,CACfuC,GAAO/B,EAAkBC,EAAMC,EAAO8B,CAAY,CACzD,EAAG,CAACD,EAAO9B,EAAMC,EAAO8B,CAAY,CAAC,EAErC,IAAMzB,GACJ,EAAAd,EAAA,KAAA,CAAC,SAAD,CACE,KAAK,SAGL,KAAMmC,GAAUF,EAAQ,IAAA,GAAY,QACpC,GAAIH,EACC,MACK,WACV,aAAYF,IAAcC,EAAiB,IAAA,GAAYW,GACvD,kBAAiBX,EACjB,eAAcM,GAAUF,EAAQ,IAAA,GAAYD,EAC5C,eAAcC,EAAQD,EAAaG,GAASH,GAA0B,IAAA,GACtE,oBAAmBvB,EACnB,gBAAeuB,EAAa,GAAK,IAAA,GACjC,SAAUS,EAAYhC,CAAK,EAC3B,QAASjC,EAAAA,qBAAqBkE,MAAeC,EAAOlC,CAAK,CAAC,EAC1D,UAAWtC,EAAAA,GACTyE,EAEAnE,EAAAA,uBAAuB,OACvB+C,EAAkBN,EAAMC,EAAOE,CAAO,EAEtC,uDACAwB,EAGAC,GACE,yFACFpE,EAAAA,aAAa,OAEbsD,GAAc,CAACc,GAAe,mCAC9Bd,EACI7D,EAAAA,GACE,8DACA2E,GAAe,gCACjB,EACA,2DACJC,CACF,EACA,MAAO,CAAE,GAAGC,EAAO,GAAGC,CAAM,EAvC9B,SAAA,CAyCGjB,GACC,EAAAhC,EAAA,IAAA,CAACI,EAAD,CAAa,OAAQ8C,EAAa,KAAMhD,EAAagB,EAAQ,CAAA,EAE7DH,GAAS,MACTpC,EAAAA,mBAAmBoC,CAAK,GACxBnC,EAAAA,WAAWmC,EAAO,OAAQ5C,EAAAA,GAAGgF,EAAe,iCAAiC,EAAG,CAC9E,cAAe,GACf,MAAOD,EAAc,CAAE,MAAOA,EAAY,KAAM,EAAI,IAAA,EACtD,CAAC,EAEFE,IAAY,EAAApD,EAAA,IAAA,CAACM,EAAD,CAAa,OAAQ4C,CAAc,CAAA,CAC1C,IAEV,OAAOf,GAAS,EAAAnC,EAAA,IAAA,CAACsB,EAAD,CAAgB,SAAUU,EAAa,SAAAlB,CAAuB,CAAA,EAAIA,CACpF,EACA,EAAY,YAAc,cAoB1B,IAAauC,GAAe,CAAE,QAAO,cAAa,GAAGvB,MAEjD,EAAA9B,EAAA,IAAA,CAACyB,EAAD,CACE,GAAIK,EACJ,KAAK,cACL,cAAejD,EAAAA,sBAAsB,KACrC,cAAeA,EAAAA,sBAAsB,KACrC,aAAa,cACb,YAAayE,EACb,YAAaxE,EAAAA,eAAewE,CAAK,EACjC,YAAA,GACA,MAAO,CACL,gBAAiBA,EACjB,GAAIC,IAAgB,IAAA,IAAa,CAAE,uBAAwBA,CAAY,CACzE,CACD,CAAA,EAGL,EAAY,YAAc,cA4C1B,IAAaC,GAAe,CAAE,MAAK,GAAG1B,MAElC,EAAA9B,EAAA,IAAA,CAACyB,EAAD,CACE,GAAIK,EACJ,KAAK,cACL,cAAe/C,EAAAA,sBAAsB,KACrC,aAAa,QACb,YAAa+C,EAAK,MAClB,YAAa,KACb,YAAa,GACb,eAAe,kCACf,MAAO,CAAE,gBAAiB,OAAO,KAAK,UAAU2B,CAAG,EAAE,EAAG,CACzD,CAAA,EAGL,EAAY,YAAc,cA0C1B,IAAaC,GAAe,CAC1B,OACA,KAAMhC,EACN,MAAOC,EACP,YACA,aAAcC,EACd,kBAAmBC,EACnB,MACA,GAAGC,KACmB,CACtB,GAAM,CAAE,OAAM,QAAO,SAAQ,YAAW,UAAS,eAAgBd,EAC/D,cACAU,EACAC,CACF,EAEMQ,EAASC,IAAW,QAAUF,IAAc,MAG5CyB,EAAY3E,EAAAA,MAAM,mBAAmB,EACrC,CAAE,QAAO,UAAWZ,EAAAA,kBAAkBwC,CAAI,EAChDC,EAAqB,cAAeC,CAAM,EAG1C,IAAM8C,EACJhD,GAAQ,MAASE,IAAW,MAAQ,CAACnC,EAAAA,mBAAmBoC,CAAK,GAC3D,EAAAf,EAAA,IAAA,CAACf,EAAAA,QAAD,CAAS,KAAMiB,EAAagB,EAAQ,CAAA,EAEpCH,EAGE8C,GACJ,EAAA7D,EAAA,IAAA,CAAC,SAAD,CACE,KAAK,SACL,GAAI8B,EACC,MACL,aAAYF,IAAcC,EAAiB,IAAA,GAAY,aACvD,kBAAiBA,EAEjB,yBAAuB,GACvB,GAAKM,GAAU,CAAE,oBAAqBwB,EAAW,SAAUlB,EAAYkB,CAAS,CAAE,EAClF,UAAWxF,EAAAA,GACTe,EAAAA,sBAAsB,KACtBT,EAAAA,uBAAuB,OACvB+C,EAAkBN,EAAMC,EAAOE,CAAO,EACtC,wEACA3C,EAAAA,aAAa,QACb,uDACAqE,CACF,EAEC,SAAApE,EAAAA,mBAAmBiF,CAAO,GACzBhF,EAAAA,WACEgF,EACA,OACAzF,EAAAA,GAAGe,EAAAA,sBAAsB,KAAM,iCAAiC,EAChE,CACE,cAAe,EACjB,CACF,CACI,CAAA,EAEV,OAAOiD,GAAS,EAAAnC,EAAA,IAAA,CAACsB,EAAD,CAAA,SAAiBuC,CAAwB,CAAA,EAAIA,CAC/D,EACA,EAAY,YAAc"}
@@ -0,0 +1,207 @@
1
+ "use client";
2
+ import { cn as e } from "../../lib/cn.mjs";
3
+ import { warnOnce as t } from "../../lib/dev.mjs";
4
+ import { renderSlot as n, slotRendersContent as r } from "../../lib/slot.mjs";
5
+ import { composeEventHandlers as i } from "../../lib/composeEventHandlers.mjs";
6
+ import { colorSwatchClassNames as a, emptySwatchClassNames as o, imageSwatchClassNames as s, swatchPickerClassNames as c } from "../../lib/classNames.mjs";
7
+ import { useId as l } from "../../hooks/useId.mjs";
8
+ import { focusRing as u, forcedColors as d, motionSafeTransition as f } from "../../lib/styles.mjs";
9
+ import { unwrapButtonGlyph as p } from "../button/Button.slots.mjs";
10
+ import { AddIcon as m, CheckIcon as h } from "../../lib/icons.mjs";
11
+ import { useSwatchPickerContext as g } from "./SwatchPicker.context.mjs";
12
+ import { getCheckColors as _ } from "./colorUtils.mjs";
13
+ import * as v from "react";
14
+ import { jsx as y, jsxs as b } from "react/jsx-runtime";
15
+ //#region src/components/input/SwatchPicker.swatches.tsx
16
+ var x = {
17
+ "extra-small": "w-5 h-5",
18
+ small: "w-6 h-6",
19
+ medium: "w-8 h-8",
20
+ large: "w-10 h-10"
21
+ }, S = {
22
+ "extra-small": 10,
23
+ small: 12,
24
+ medium: 16,
25
+ large: 20
26
+ }, C = {
27
+ circular: "rounded-full",
28
+ square: "rounded-none",
29
+ rounded: "rounded"
30
+ };
31
+ function w({ colors: t, size: n }) {
32
+ return /* @__PURE__ */ b("span", {
33
+ "aria-hidden": "true",
34
+ className: "pointer-events-none grid",
35
+ children: [/* @__PURE__ */ y(h, {
36
+ size: n,
37
+ strokeWidth: 4,
38
+ className: e("col-start-1 row-start-1", !t && "text-background"),
39
+ style: t ? { color: t.halo } : void 0
40
+ }), /* @__PURE__ */ y(h, {
41
+ size: n,
42
+ strokeWidth: 2,
43
+ className: e("col-start-1 row-start-1", !t && "text-foreground"),
44
+ style: t ? { color: t.glyph } : void 0
45
+ })]
46
+ });
47
+ }
48
+ function T({ colors: e }) {
49
+ return /* @__PURE__ */ b("svg", {
50
+ "data-wave-swatch-slash": "",
51
+ "aria-hidden": "true",
52
+ viewBox: "0 0 24 24",
53
+ fill: "none",
54
+ className: "pointer-events-none absolute inset-0 h-full w-full",
55
+ children: [/* @__PURE__ */ y("line", {
56
+ x1: "5",
57
+ y1: "19",
58
+ x2: "19",
59
+ y2: "5",
60
+ stroke: "currentColor",
61
+ strokeLinecap: "round",
62
+ strokeWidth: 4,
63
+ className: e ? void 0 : "text-background",
64
+ style: e ? { color: e.halo } : void 0
65
+ }), /* @__PURE__ */ y("line", {
66
+ x1: "5",
67
+ y1: "19",
68
+ x2: "19",
69
+ y2: "5",
70
+ stroke: "currentColor",
71
+ strokeLinecap: "round",
72
+ strokeWidth: 2,
73
+ className: e ? void 0 : "text-foreground",
74
+ style: e ? { color: e.glyph } : void 0
75
+ })]
76
+ });
77
+ }
78
+ function E(e, n, r) {
79
+ t(`${e}:unnamed`, `${e}: swatch "${n}" has no accessible name, so it is announced by its ${r}. Pass \`aria-label\`, or wrap it in a Tooltip with \`relationship="label"\`.`);
80
+ }
81
+ function D(e, t) {
82
+ let { glyph: n, button: r } = p(t);
83
+ return O(e, r), n;
84
+ }
85
+ function O(e, n) {
86
+ v.useEffect(() => {
87
+ n && t(`${e}:icon-button`, `${e}: \`icon\` received ${n}; its children render as the glyph of the swatch and its props were dropped (buttons cannot be nested). Pass icon content instead, e.g. \`icon={<MyIcon />}\`.`);
88
+ }, [n, e]);
89
+ }
90
+ function k(e, t, n) {
91
+ let r = g(e);
92
+ return {
93
+ ...r,
94
+ size: t ?? r.size,
95
+ shape: n ?? r.shape
96
+ };
97
+ }
98
+ function A(e) {
99
+ return e === "small" ? "focus-visible:outline-offset-2" : "focus-visible:outline-offset-4";
100
+ }
101
+ function j({ selected: e, children: t }) {
102
+ return /* @__PURE__ */ y("div", {
103
+ role: "gridcell",
104
+ "aria-selected": e,
105
+ className: "flex shrink-0",
106
+ children: t
107
+ });
108
+ }
109
+ function M(t, n, r) {
110
+ return e(x[t], C[n], "relative flex shrink-0 items-center justify-center border-2 p-0", f, u, A(r), "disabled:cursor-not-allowed disabled:opacity-50");
111
+ }
112
+ var N = ({ kind: t, rootClassName: a, iconClassName: o, value: s, fallbackKind: l, defaultName: u, checkColors: f, paint: p, paintClassName: m, keepsColors: h, icon: g, size: _, shape: x, className: C, style: O, onClick: A, disabled: N, "aria-label": P, "aria-labelledby": F, ref: I, ...L }) => {
113
+ let { value: R, select: z, size: B, shape: V, layout: H, focusMode: U, spacing: W, getTabIndex: G, register: K } = k(t, _, x), q = R === s, J = U === "tab", Y = H === "grid" && !J, X = D(t, g);
114
+ v.useLayoutEffect(() => K(s), [K, s]);
115
+ let Z = P !== void 0 || F !== void 0;
116
+ v.useEffect(() => {
117
+ Z || E(t, s, l);
118
+ }, [
119
+ Z,
120
+ t,
121
+ s,
122
+ l
123
+ ]);
124
+ let Q = /* @__PURE__ */ b("button", {
125
+ type: "button",
126
+ role: Y || J ? void 0 : "radio",
127
+ ...L,
128
+ ref: I,
129
+ disabled: N,
130
+ "aria-label": P ?? (F ? void 0 : u),
131
+ "aria-labelledby": F,
132
+ "aria-checked": Y || J ? void 0 : q,
133
+ "aria-pressed": J ? q : Y && q || void 0,
134
+ "data-roving-value": s,
135
+ "data-selected": q ? "" : void 0,
136
+ tabIndex: G(s),
137
+ onClick: i(A, () => z(s)),
138
+ className: e(a, c.swatch, M(B, V, W), "border-[color:var(--wave-swatch-border,transparent)]", m, h && "forced-colors:forced-color-adjust-none forced-colors:focus-visible:outline-[Highlight]", d.border, q && !h && "forced-colors:border-[Highlight]", q ? e("ring-2 ring-foreground ring-offset-2 ring-offset-background", h && "forced-colors:ring-[Highlight]") : "not-disabled:not-aria-disabled:hover:border-stroke-hover", C),
139
+ style: {
140
+ ...O,
141
+ ...p
142
+ },
143
+ children: [q ? /* @__PURE__ */ y(w, {
144
+ colors: f,
145
+ size: S[B]
146
+ }) : X != null && r(X) && n(X, "span", e(o, "pointer-events-none inline-flex"), {
147
+ "aria-hidden": !0,
148
+ style: f ? { color: f.glyph } : void 0
149
+ }), N && /* @__PURE__ */ y(T, { colors: f })]
150
+ });
151
+ return Y ? /* @__PURE__ */ y(j, {
152
+ selected: q,
153
+ children: Q
154
+ }) : Q;
155
+ };
156
+ N.displayName = "SwatchRadio";
157
+ var P = ({ color: e, borderColor: t, ...n }) => /* @__PURE__ */ y(N, {
158
+ ...n,
159
+ kind: "ColorSwatch",
160
+ rootClassName: a.root,
161
+ iconClassName: a.icon,
162
+ fallbackKind: "color value",
163
+ defaultName: e,
164
+ checkColors: _(e),
165
+ keepsColors: !0,
166
+ paint: {
167
+ backgroundColor: e,
168
+ ...t !== void 0 && { "--wave-swatch-border": t }
169
+ }
170
+ });
171
+ P.displayName = "ColorSwatch";
172
+ var F = ({ src: e, ...t }) => /* @__PURE__ */ y(N, {
173
+ ...t,
174
+ kind: "ImageSwatch",
175
+ rootClassName: s.root,
176
+ fallbackKind: "value",
177
+ defaultName: t.value,
178
+ checkColors: null,
179
+ keepsColors: !1,
180
+ paintClassName: "bg-cover bg-center bg-no-repeat",
181
+ paint: { backgroundImage: `url(${JSON.stringify(e)})` }
182
+ });
183
+ F.displayName = "ImageSwatch";
184
+ var I = ({ icon: t, size: i, shape: a, className: s, "aria-label": u, "aria-labelledby": f, ref: h, ...g }) => {
185
+ let { size: _, shape: v, layout: b, focusMode: x, spacing: C, getTabIndex: w } = k("EmptySwatch", i, a), T = b === "grid" && x !== "tab", E = l("wave-empty-swatch"), { glyph: D, button: A } = p(t);
186
+ O("EmptySwatch", A);
187
+ let N = t == null || A !== null && !r(D) ? /* @__PURE__ */ y(m, { size: S[_] }) : D, P = /* @__PURE__ */ y("button", {
188
+ type: "button",
189
+ ...g,
190
+ ref: h,
191
+ "aria-label": u ?? (f ? void 0 : "Add color"),
192
+ "aria-labelledby": f,
193
+ "data-wave-empty-swatch": "",
194
+ ...T && {
195
+ "data-roving-value": E,
196
+ tabIndex: w(E)
197
+ },
198
+ className: e(o.root, c.swatch, M(_, v, C), "border-dashed border-stroke-accessible bg-transparent text-foreground", d.control, "not-disabled:not-aria-disabled:hover:bg-subtle-hover", s),
199
+ children: r(N) && n(N, "span", e(o.icon, "pointer-events-none inline-flex"), { "aria-hidden": !0 })
200
+ });
201
+ return T ? /* @__PURE__ */ y(j, { children: P }) : P;
202
+ };
203
+ I.displayName = "EmptySwatch";
204
+ //#endregion
205
+ export { P as ColorSwatch, I as EmptySwatch, F as ImageSwatch };
206
+
207
+ //# sourceMappingURL=SwatchPicker.swatches.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SwatchPicker.swatches.mjs","names":["useId","useId$1","React","sizeMap","glyphSizeMap","shapeMap","SwatchCheck","CheckIcon","cn","colors","SwatchSlash","warnUnnamedSwatch","kind","value","fallback","useSwatchIcon","unwrapButtonGlyph","icon","useSwatchIconWarning","button","glyph","warnOnce","useSwatchContext","context","useSwatchPickerContext","size","shape","outlineOffset","spacing","SwatchGridCell","selected","swatchBaseClasses","motionSafeTransition","focusRing","SwatchRadio","sizeProp","shapeProp","ariaLabel","ariaLabelledBy","rest","selectedValue","isSelected","isTab","focusMode","isGrid","layout","register","named","fallbackKind","defaultName","getTabIndex","composeEventHandlers","onClick","select","rootClassName","swatchPickerClassNames","paintClassName","keepsColors","forcedColors","className","style","paint","checkColors","slotRendersContent","renderSlot","iconClassName","disabled","ColorSwatch","colorSwatchClassNames","color","getCheckColors","borderColor","ImageSwatch","imageSwatchClassNames","src","EmptySwatch","cellValue","content","AddIcon","control","emptySwatchClassNames"],"sources":["../../../src/components/input/SwatchPicker.swatches.tsx"],"sourcesContent":["import * as React from 'react';\nimport {\n colorSwatchClassNames,\n emptySwatchClassNames,\n imageSwatchClassNames,\n swatchPickerClassNames,\n} from '../../lib/classNames';\nimport { cn } from '../../lib/cn';\nimport { composeEventHandlers } from '../../lib/composeEventHandlers';\nimport { warnOnce } from '../../lib/dev';\nimport { AddIcon, CheckIcon } from '../../lib/icons';\nimport { renderSlot, slotRendersContent } from '../../lib/slot';\nimport type { Slot } from '../../lib/slot';\nimport { focusRing, forcedColors, motionSafeTransition } from '../../lib/styles';\nimport type { Shape } from '../../lib/types';\nimport { useId } from '../../hooks/useId';\nimport { unwrapButtonGlyph, type UnwrappedButton } from '../button/Button.slots';\nimport { useSwatchPickerContext, type SwatchPickerSize } from './SwatchPicker.context';\nimport { getCheckColors, type CheckColors } from './colorUtils';\n\n/*\n * The swatch kinds a `SwatchPicker` renders `items` through and a consumer can render as children\n * (D18, D21): `ColorSwatch`, `ImageSwatch` and `EmptySwatch`. Every swatch reads\n * `SwatchPickerContext` and puts its `ref`, `className`, `style`, name and rest props on its own\n * `<button>` (an explicit exception to C-ROUTING: there is no separate root, so a wrapping\n * Tooltip's ARIA and handlers reach the focused element without extra wiring).\n */\n\nconst sizeMap: Record<SwatchPickerSize, string> = {\n 'extra-small': 'w-5 h-5',\n small: 'w-6 h-6',\n medium: 'w-8 h-8',\n large: 'w-10 h-10',\n};\n\nconst glyphSizeMap: Record<SwatchPickerSize, number> = {\n 'extra-small': 10,\n small: 12,\n medium: 16,\n large: 20,\n};\n\nconst shapeMap: Record<Shape, string> = {\n circular: 'rounded-full',\n square: 'rounded-none',\n rounded: 'rounded',\n};\n\n/**\n * The check glyph of the selected swatch: black or white by the swatch's luminance, drawn over a\n * halo in the other color so it stays visible on any swatch (C-TOKENS exception for\n * user-supplied colors, `input-pickers#16`). Without `colors` (an image has no single luminance)\n * it is the theme's foreground glyph over a background halo.\n */\nfunction SwatchCheck({ colors, size }: { colors: CheckColors | null; size: number }) {\n return (\n <span aria-hidden=\"true\" className=\"pointer-events-none grid\">\n <CheckIcon\n size={size}\n strokeWidth={4}\n className={cn('col-start-1 row-start-1', !colors && 'text-background')}\n style={colors ? { color: colors.halo } : undefined}\n />\n <CheckIcon\n size={size}\n strokeWidth={2}\n className={cn('col-start-1 row-start-1', !colors && 'text-foreground')}\n style={colors ? { color: colors.glyph } : undefined}\n />\n </span>\n );\n}\n\n/**\n * The diagonal slash of a disabled swatch, drawn like the check glyph (a halo line under a glyph\n * line), so the state does not rely on the swatch color or its opacity.\n */\nfunction SwatchSlash({ colors }: { colors: CheckColors | null }) {\n return (\n <svg\n data-wave-swatch-slash=\"\"\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n className=\"pointer-events-none absolute inset-0 h-full w-full\"\n >\n <line\n x1=\"5\"\n y1=\"19\"\n x2=\"19\"\n y2=\"5\"\n stroke=\"currentColor\"\n strokeLinecap=\"round\"\n strokeWidth={4}\n className={colors ? undefined : 'text-background'}\n style={colors ? { color: colors.halo } : undefined}\n />\n <line\n x1=\"5\"\n y1=\"19\"\n x2=\"19\"\n y2=\"5\"\n stroke=\"currentColor\"\n strokeLinecap=\"round\"\n strokeWidth={2}\n className={colors ? undefined : 'text-foreground'}\n style={colors ? { color: colors.glyph } : undefined}\n />\n </svg>\n );\n}\n\n/** Warns once per swatch kind (not per value: `<Kind>:unnamed`, e.g. `ColorSwatch:unnamed`). */\nfunction warnUnnamedSwatch(kind: string, value: string, fallback: string): void {\n warnOnce(\n `${kind}:unnamed`,\n `${kind}: swatch \"${value}\" has no accessible name, so it is announced by its ${fallback}. ` +\n 'Pass `aria-label`, or wrap it in a Tooltip with `relationship=\"label\"`.',\n );\n}\n\n/**\n * A swatch's `icon` as a glyph inside its wired button (C-SLOTS): a `<button>` or `Button` is\n * unwrapped (its children become the glyph) with a one-time warning (`<Kind>:icon-button`).\n */\nfunction useSwatchIcon(kind: string, icon: Slot<'span'> | undefined): Slot<'span'> | undefined {\n const { glyph, button } = unwrapButtonGlyph(icon);\n useSwatchIconWarning(kind, button);\n return glyph;\n}\n\n/** The one-time `<Kind>:icon-button` warning for an unwrapped button. */\nfunction useSwatchIconWarning(kind: string, button: UnwrappedButton | null): void {\n React.useEffect(() => {\n if (button) {\n warnOnce(\n `${kind}:icon-button`,\n `${kind}: \\`icon\\` received ${button}; its children render as the glyph of the swatch ` +\n 'and its props were dropped (buttons cannot be nested). Pass icon content instead, ' +\n 'e.g. `icon={<MyIcon />}`.',\n );\n }\n }, [button, kind]);\n}\n\n/** The picker's context with this swatch's own `size` and `shape` applied over the picker's. */\nfunction useSwatchContext(kind: string, size?: SwatchPickerSize, shape?: Shape) {\n const context = useSwatchPickerContext(kind);\n return { ...context, size: size ?? context.size, shape: shape ?? context.shape };\n}\n\n/**\n * Focus outline offset: 4px, clear of a selected swatch's ring band (ring and offset, 4px); 2px at\n * `spacing=\"small\"` so it does not cover the neighbour (D22), where it lies over that band.\n */\nfunction outlineOffset(spacing: 'small' | 'medium'): string {\n return spacing === 'small' ? 'focus-visible:outline-offset-2' : 'focus-visible:outline-offset-4';\n}\n\n/**\n * The cell around a swatch's button in the grid layout: it carries only the role and, for a swatch\n * with a value, `aria-selected` (a `gridcell` role on the button would replace its button role,\n * which `aria-pressed` needs). Every consumer prop stays on the button.\n */\nfunction SwatchGridCell({ selected, children }: { selected?: boolean; children: React.ReactNode }) {\n return (\n <div role=\"gridcell\" aria-selected={selected} className=\"flex shrink-0\">\n {children}\n </div>\n );\n}\n\n/** The classes every swatch kind shares: size, shape, box, motion, focus ring and disabled look. */\nfunction swatchBaseClasses(\n size: SwatchPickerSize,\n shape: Shape,\n spacing: 'small' | 'medium',\n): string {\n return cn(\n sizeMap[size],\n shapeMap[shape],\n // The padding is set here (C-NATIVE).\n 'relative flex shrink-0 items-center justify-center border-2 p-0',\n motionSafeTransition,\n focusRing,\n outlineOffset(spacing),\n 'disabled:cursor-not-allowed disabled:opacity-50',\n );\n}\n\n/** Properties for the ColorSwatch component. */\nexport interface ColorSwatchProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n 'value' | 'color'\n> {\n /** Unique identifier for the swatch (the value the picker selects and a form submits). */\n value: string;\n /** CSS color value (e.g., hex, rgb) used as the swatch background (a runtime user color). */\n color: string;\n /**\n * Decorative glyph (`aria-hidden`) shown while the swatch is not selected; the check replaces it\n * while selected. A `<button>` or `Button` passed here is not nested: its children become the\n * glyph and a one-time development warning is logged.\n */\n icon?: Slot<'span'>;\n /**\n * CSS color of the swatch's border, for a color close to the page background (set by hand, as\n * in Fluent). In forced colors the system border color replaces it.\n */\n borderColor?: string;\n /**\n * Size of the swatch (20, 24, 32 or 40px).\n * @default the picker's\n */\n size?: SwatchPickerSize;\n /**\n * Shape of the swatch.\n * @default the picker's\n */\n shape?: Shape;\n /**\n * Ref to the swatch button: `role=\"radio\"` in the row layout, inside its cell in the grid\n * layout, a toggle button in tab mode.\n */\n ref?: React.Ref<HTMLButtonElement>;\n}\n\n/** What `ColorSwatch` and `ImageSwatch` do differently. */\ninterface SwatchRadioProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n 'value' | 'color'\n> {\n kind: 'ColorSwatch' | 'ImageSwatch';\n /** The kind's stable root class, named by the module that sets its `displayName` (P3-00). */\n rootClassName: string;\n /** The kind's stable `icon` part class (ColorSwatch only). */\n iconClassName?: string;\n value: string;\n /** What names the swatch when it has no name: 'color value' or 'value' (the warning's text). */\n fallbackKind: string;\n /** The accessible name of an unnamed swatch. */\n defaultName: string;\n /** The luminance-picked glyph colors; `null`: the theme's foreground over a background halo. */\n checkColors: CheckColors | null;\n /** The swatch's own background, border color and image (they win over a consumer `style`). */\n paint: Record<string, string>;\n /** Classes of the image swatch's covering background. */\n paintClassName?: string;\n /** A color swatch keeps its colors in forced colors (the color is the content). */\n keepsColors: boolean;\n icon?: Slot<'span'>;\n size?: SwatchPickerSize;\n shape?: Shape;\n ref?: React.Ref<HTMLButtonElement>;\n}\n\n/** The radio button shared by `ColorSwatch` and `ImageSwatch`. */\nconst SwatchRadio = ({\n kind,\n rootClassName,\n iconClassName,\n value,\n fallbackKind,\n defaultName,\n checkColors,\n paint,\n paintClassName,\n keepsColors,\n icon,\n size: sizeProp,\n shape: shapeProp,\n className,\n style,\n onClick,\n disabled,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n ref,\n ...rest\n}: SwatchRadioProps) => {\n const {\n value: selectedValue,\n select,\n size,\n shape,\n layout,\n focusMode,\n spacing,\n getTabIndex,\n register,\n } = useSwatchContext(kind, sizeProp, shapeProp);\n const isSelected = selectedValue === value;\n const isTab = focusMode === 'tab';\n const isGrid = layout === 'grid' && !isTab;\n const glyph = useSwatchIcon(kind, icon);\n\n React.useLayoutEffect(() => register(value), [register, value]);\n\n const named = ariaLabel !== undefined || ariaLabelledBy !== undefined;\n React.useEffect(() => {\n if (!named) warnUnnamedSwatch(kind, value, fallbackKind);\n }, [named, kind, value, fallbackKind]);\n\n const button = (\n <button\n type=\"button\"\n // The grid layout: a plain button inside its gridcell, pressed while selected (D20); tab\n // mode (D19): a toggle button of a group, `aria-pressed` always present.\n role={isGrid || isTab ? undefined : 'radio'}\n {...rest}\n ref={ref}\n disabled={disabled}\n aria-label={ariaLabel ?? (ariaLabelledBy ? undefined : defaultName)}\n aria-labelledby={ariaLabelledBy}\n aria-checked={isGrid || isTab ? undefined : isSelected}\n aria-pressed={isTab ? isSelected : isGrid ? isSelected || undefined : undefined}\n data-roving-value={value}\n data-selected={isSelected ? '' : undefined}\n tabIndex={getTabIndex(value)}\n onClick={composeEventHandlers(onClick, () => select(value))}\n className={cn(\n rootClassName,\n // Every swatch in a picker carries the picker's part class, children included (0.8).\n swatchPickerClassNames.swatch,\n swatchBaseClasses(size, shape, spacing),\n // A `borderColor` arrives as a variable, so the forced-colors edge below still wins.\n 'border-[color:var(--wave-swatch-border,transparent)]',\n paintClassName,\n // User colors are the content: keep them (and the check glyph) in forced colors, with a\n // Highlight outline, which an opted-out element would otherwise draw in author colors.\n keepsColors &&\n 'forced-colors:forced-color-adjust-none forced-colors:focus-visible:outline-[Highlight]',\n forcedColors.border,\n // An image stays in forced colors, so its selection needs a sign of its own.\n isSelected && !keepsColors && 'forced-colors:border-[Highlight]',\n isSelected\n ? cn(\n 'ring-2 ring-foreground ring-offset-2 ring-offset-background',\n keepsColors && 'forced-colors:ring-[Highlight]',\n )\n : 'not-disabled:not-aria-disabled:hover:border-stroke-hover',\n className,\n )}\n style={{ ...style, ...paint }}\n >\n {isSelected ? (\n <SwatchCheck colors={checkColors} size={glyphSizeMap[size]} />\n ) : (\n glyph != null &&\n slotRendersContent(glyph) &&\n renderSlot(glyph, 'span', cn(iconClassName, 'pointer-events-none inline-flex'), {\n 'aria-hidden': true,\n style: checkColors ? { color: checkColors.glyph } : undefined,\n })\n )}\n {disabled && <SwatchSlash colors={checkColors} />}\n </button>\n );\n return isGrid ? <SwatchGridCell selected={isSelected}>{button}</SwatchGridCell> : button;\n};\nSwatchRadio.displayName = 'SwatchRadio';\n\n// D18: swatches read SwatchPickerContext; their own ref/className/style/rest go straight to the\n// button (an explicit exception to C-ROUTING's root/control split).\n/**\n * One color swatch of a `SwatchPicker`, read from `items` or rendered directly as a child.\n * Outside a `SwatchPicker` it throws in development and renders inertly in production.\n *\n * **Naming**: give it `aria-label`, or wrap it in a `Tooltip` with `relationship=\"label\"` (its\n * `aria-labelledby` reaches this element, since every rest prop lands on the swatch's own\n * button). Without either it is named by its `color` value and a development warning is logged.\n *\n * **Disabled**: `disabled` is native (out of the arrow keys and the tab order, never selected);\n * the swatch is dimmed and crossed by a diagonal slash, so the state does not rely on its color.\n *\n * @example\n * <SwatchPicker aria-label=\"Accent color\">\n * <ColorSwatch value=\"red\" color=\"#d13438\" aria-label=\"Red\" />\n * </SwatchPicker>\n */\nexport const ColorSwatch = ({ color, borderColor, ...rest }: ColorSwatchProps) => {\n return (\n <SwatchRadio\n {...rest}\n kind=\"ColorSwatch\"\n rootClassName={colorSwatchClassNames.root}\n iconClassName={colorSwatchClassNames.icon}\n fallbackKind=\"color value\"\n defaultName={color}\n checkColors={getCheckColors(color)}\n keepsColors\n paint={{\n backgroundColor: color,\n ...(borderColor !== undefined && { '--wave-swatch-border': borderColor }),\n }}\n />\n );\n};\nColorSwatch.displayName = 'ColorSwatch';\n\n/** Properties for the ImageSwatch component. */\nexport interface ImageSwatchProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n 'value'\n> {\n /** Unique identifier for the swatch (the value the picker selects and a form submits). */\n value: string;\n /** URL of the image (drawn as a covering background image). */\n src: string;\n /**\n * Size of the swatch (20, 24, 32 or 40px).\n * @default the picker's\n */\n size?: SwatchPickerSize;\n /**\n * Shape of the swatch.\n * @default the picker's\n */\n shape?: Shape;\n /**\n * Ref to the swatch button: `role=\"radio\"` in the row layout, inside its cell in the grid\n * layout, a toggle button in tab mode.\n */\n ref?: React.Ref<HTMLButtonElement>;\n}\n\n/**\n * One image swatch of a `SwatchPicker`: a pattern or texture drawn as a covering background\n * image. Outside a `SwatchPicker` it throws in development and renders inertly in production.\n *\n * **Naming**: give it `aria-label`, or wrap it in a `Tooltip` with `relationship=\"label\"`.\n * Without either it is named by its `value` and a development warning is logged.\n *\n * **Selected and disabled**: an image has no single luminance, so the check and the disabled slash\n * are the theme's foreground glyph over a background halo. Unlike `ColorSwatch` it does not opt\n * out of forced colors (a background image stays).\n *\n * @example\n * <SwatchPicker aria-label=\"Pattern\">\n * <ImageSwatch value=\"dots\" src=\"/dots.png\" aria-label=\"Dots\" />\n * </SwatchPicker>\n */\nexport const ImageSwatch = ({ src, ...rest }: ImageSwatchProps) => {\n return (\n <SwatchRadio\n {...rest}\n kind=\"ImageSwatch\"\n rootClassName={imageSwatchClassNames.root}\n fallbackKind=\"value\"\n defaultName={rest.value}\n checkColors={null}\n keepsColors={false}\n paintClassName=\"bg-cover bg-center bg-no-repeat\"\n paint={{ backgroundImage: `url(${JSON.stringify(src)})` }}\n />\n );\n};\nImageSwatch.displayName = 'ImageSwatch';\n\n/** Properties for the EmptySwatch component. */\nexport interface EmptySwatchProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /**\n * Accessible name of the button.\n * @default 'Add color'\n */\n 'aria-label'?: string;\n /**\n * Decorative glyph (`aria-hidden`). `null` and `undefined` keep the plus glyph; `false` or\n * anything that renders nothing shows none. A `<button>` or `Button` passed here is not nested:\n * its children become the glyph and a one-time development warning is logged.\n * @default a plus glyph\n */\n icon?: Slot<'span'>;\n /**\n * Size of the swatch (20, 24, 32 or 40px).\n * @default the picker's\n */\n size?: SwatchPickerSize;\n /**\n * Shape of the swatch.\n * @default the picker's\n */\n shape?: Shape;\n /** Ref to the button. */\n ref?: React.Ref<HTMLButtonElement>;\n}\n\n/**\n * An \"add\" button inside a `SwatchPicker` (for example to open a color dialog): it has no value\n * and is never selected. In the row layout it is a plain button with its own tab stop, outside\n * the radio group's arrow order, and arrow keys pressed on it do nothing. In the grid layout it\n * is a cell like the others, in the arrow order (without `aria-selected`). Outside a\n * `SwatchPicker` it throws in development and renders inertly in production.\n *\n * @example\n * <SwatchPicker aria-label=\"Accent color\" items={items}>\n * <EmptySwatch onClick={openColorDialog} />\n * </SwatchPicker>\n */\nexport const EmptySwatch = ({\n icon,\n size: sizeProp,\n shape: shapeProp,\n className,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n ref,\n ...rest\n}: EmptySwatchProps) => {\n const { size, shape, layout, focusMode, spacing, getTabIndex } = useSwatchContext(\n 'EmptySwatch',\n sizeProp,\n shapeProp,\n );\n // Tab mode has no grid cells: the button is a tab stop of its own, like in the row layout.\n const isGrid = layout === 'grid' && focusMode !== 'tab';\n // The grid's roving keys address cells by value: an EmptySwatch, which has none, gets its own\n // (never selected, never submitted).\n const cellValue = useId('wave-empty-swatch');\n const { glyph, button } = unwrapButtonGlyph(icon);\n useSwatchIconWarning('EmptySwatch', button);\n // The optional-indicator rule: `null`/`undefined` keep the plus, anything that renders nothing\n // hides it; an unwrapped button whose children render nothing keeps the plus too.\n const content =\n icon == null || (button !== null && !slotRendersContent(glyph)) ? (\n <AddIcon size={glyphSizeMap[size]} />\n ) : (\n glyph\n );\n\n const control = (\n <button\n type=\"button\"\n {...rest}\n ref={ref}\n aria-label={ariaLabel ?? (ariaLabelledBy ? undefined : 'Add color')}\n aria-labelledby={ariaLabelledBy}\n // The row layout's root skips its arrow handling for events from here.\n data-wave-empty-swatch=\"\"\n {...(isGrid && { 'data-roving-value': cellValue, tabIndex: getTabIndex(cellValue) })}\n className={cn(\n emptySwatchClassNames.root,\n swatchPickerClassNames.swatch,\n swatchBaseClasses(size, shape, spacing),\n 'border-dashed border-stroke-accessible bg-transparent text-foreground',\n forcedColors.control,\n 'not-disabled:not-aria-disabled:hover:bg-subtle-hover',\n className,\n )}\n >\n {slotRendersContent(content) &&\n renderSlot(\n content,\n 'span',\n cn(emptySwatchClassNames.icon, 'pointer-events-none inline-flex'),\n {\n 'aria-hidden': true,\n },\n )}\n </button>\n );\n return isGrid ? <SwatchGridCell>{control}</SwatchGridCell> : control;\n};\nEmptySwatch.displayName = 'EmptySwatch';\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../lib/cn.cjs"),n=require("../../lib/dev.cjs"),r=require("../../lib/slot.cjs"),ee=require("../../lib/composeEventHandlers.cjs"),i=require("../../lib/classNames.cjs"),te=require("../../hooks/useControllable.cjs"),a=require("../../hooks/useId.cjs"),o=require("../../hooks/useMergedRefs.cjs"),s=require("../../lib/aria.cjs"),c=require("../../hooks/useFieldControl.cjs"),l=require("../../lib/styles.cjs"),ne=require("../../hooks/useFormReset.cjs"),re=require("../internal/HiddenInput.cjs");let u=require("react");u=e.__toESM(u,1);let d=require("react/jsx-runtime");var f=`forced-colors:border-[Highlight] forced-colors:bg-[Highlight]`,p=`forced-colors:border-[GrayText] forced-colors:bg-[Canvas]`,m=({checked:e,defaultChecked:m=!1,onCheckedChange:h,onChange:g,disabled:_=!1,disabledFocusable:v=!1,label:y,labelPosition:b=`after`,name:x,value:S,required:C,form:w,id:ie,"aria-label":ae,"aria-labelledby":oe,"aria-describedby":T,"aria-invalid":E,"aria-required":D,"aria-errormessage":O,"aria-details":k,"aria-disabled":A,autoFocus:j,tabIndex:M,onClick:N,onFocus:P,onBlur:F,onKeyDown:I,onKeyUp:L,controlRef:R,className:z,children:se,ref:B,...V})=>{g!==void 0&&n.warnDeprecated(`Switch`,`onChange`,`onCheckedChange`);let H=r.slotRendersContent(se);u.useEffect(()=>{H&&n.warnOnce(`Switch:children`,"Switch: children are not rendered. Pass the label in `label`.")},[H]);let[U,W]=te.useControllable(e,m,e=>{h?.(e),g?.(e)}),G=a.useId(`switch`),K=a.useId(`switch-label`),q=c.useFieldContext(),J=c.useFieldControl({id:ie,"aria-label":ae,"aria-labelledby":oe,"aria-describedby":T,"aria-invalid":E,"aria-required":D??C}),ce=C??q?.required??!1,Y=r.slotRendersContent(y),le=Y&&J[`aria-label`]===void 0?s.joinIds(J[`aria-labelledby`],q&&J.id===q.controlId?q.labelId:void 0,K):J[`aria-labelledby`],X=u.useRef(null),ue=o.useMergedRefs(X,R);ne.useFormReset(X,()=>W(m),w);let Z=Y?(0,d.jsx)(`span`,{id:K,className:t.cn(i.switchClassNames.label,`text-body-1 text-foreground`),children:r.materialiseSlotContent(y)}):null,Q=b===`before`||b===`above`,$=_||v;return(0,d.jsxs)(`label`,{ref:B,"data-label-position":b,className:t.cn(i.switchClassNames.root,`relative inline-flex items-start gap-2 select-none`,b===`above`&&`flex-col gap-1`,$?`cursor-not-allowed opacity-50 has-focus-visible:opacity-100`:`cursor-pointer`,z),...V,children:[Q&&Z,(0,d.jsx)(`button`,{id:G,type:`button`,...J,"aria-labelledby":le,"aria-errormessage":O,"aria-details":k,"aria-disabled":A,autoFocus:j,tabIndex:M,onFocus:P,onBlur:F,onKeyDown:I,onKeyUp:L,ref:ue,role:`switch`,"aria-checked":U,disabled:_&&!v,...s.focusableDisabledProps(v,{reachable:!0}),onClick:v?e=>{e.preventDefault(),e.stopPropagation()}:ee.composeEventHandlers(N,()=>W(e=>!e)),className:t.cn(i.switchClassNames.track,`relative inline-flex h-[20px] w-[40px] shrink-0 items-center rounded-full border p-0 transition-colors duration-200 motion-reduce:transition-none`,l.focusRing,U?t.cn(`border-primary bg-primary`,$?p:f):t.cn(`border-stroke-accessible bg-transparent`,$?l.forcedColors.disabled:l.forcedColors.control)),children:(0,d.jsx)(`span`,{"aria-hidden":`true`,className:t.cn(i.switchClassNames.thumb,`block h-[14px] w-[14px] rounded-full transition-transform duration-200 motion-reduce:transition-none forced-colors:forced-color-adjust-none`,U?`translate-x-[22px] wave-rtl:-translate-x-[22px] bg-primary-foreground`:`translate-x-[2px] wave-rtl:-translate-x-[2px] bg-stroke-accessible`,$?`forced-colors:bg-[GrayText]`:U?`forced-colors:bg-[HighlightText]`:`forced-colors:bg-[ButtonText]`)})}),!Q&&Z,(0,d.jsx)(re.HiddenInput,{type:`checkbox`,name:x,form:w,disabled:$,value:S,checked:U,required:ce,onInvalid:()=>X.current?.focus()})]})};m.displayName=`Switch`,exports.Switch=m;
1
+ "use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../lib/cn.cjs"),n=require("../../lib/dev.cjs"),r=require("../../lib/slot.cjs"),ee=require("../../lib/composeEventHandlers.cjs"),i=require("../../lib/classNames.cjs"),a=require("../../hooks/useControllable.cjs"),o=require("../../hooks/useId.cjs"),s=require("../../hooks/useMergedRefs.cjs"),c=require("../../lib/aria.cjs"),l=require("../../hooks/useFieldControl.cjs"),u=require("../../lib/styles.cjs"),te=require("../../hooks/useFormReset.cjs"),ne=require("../internal/HiddenInput.cjs");let d=require("react");d=e.__toESM(d,1);let f=require("react/jsx-runtime");var re=`forced-colors:border-[Highlight] forced-colors:bg-[Highlight]`,ie=`forced-colors:border-[GrayText] forced-colors:bg-[Canvas]`,p={small:{track:`h-[16px] w-[32px] before:absolute before:inset-x-0 before:-inset-y-[5px]`,thumb:`h-[10px] w-[10px]`,on:`translate-x-[18px] wave-rtl:-translate-x-[18px]`,off:`translate-x-[2px] wave-rtl:-translate-x-[2px]`,offset:`mt-0.5`},medium:{track:`h-[20px] w-[40px]`,thumb:`h-[14px] w-[14px]`,on:`translate-x-[22px] wave-rtl:-translate-x-[22px]`,off:`translate-x-[2px] wave-rtl:-translate-x-[2px]`,offset:``}},m=({checked:e,defaultChecked:m=!1,onCheckedChange:h,onChange:g,disabled:_=!1,disabledFocusable:v=!1,label:y,labelPosition:b=`after`,size:x=`medium`,name:S,value:C,required:w,form:T,id:ae,"aria-label":oe,"aria-labelledby":se,"aria-describedby":ce,"aria-invalid":le,"aria-required":E,"aria-errormessage":D,"aria-details":O,"aria-disabled":k,autoFocus:A,tabIndex:j,onClick:M,onFocus:N,onBlur:P,onKeyDown:F,onKeyUp:I,controlRef:L,className:R,children:z,ref:B,...ue})=>{g!==void 0&&n.warnDeprecated(`Switch`,`onChange`,`onCheckedChange`);let V=r.slotRendersContent(z);d.useEffect(()=>{V&&n.warnOnce(`Switch:children`,"Switch: children are not rendered. Pass the label in `label`.")},[V]);let[H,U]=a.useControllable(e,m,e=>{h?.(e),g?.(e)}),de=o.useId(`switch`),W=o.useId(`switch-label`),G=l.useFieldContext(),K=l.useFieldControl({id:ae,"aria-label":oe,"aria-labelledby":se,"aria-describedby":ce,"aria-invalid":le,"aria-required":E??w}),fe=w??G?.required??!1,q=r.slotRendersContent(y),pe=q&&K[`aria-label`]===void 0?c.joinIds(K[`aria-labelledby`],G&&K.id===G.controlId?G.labelId:void 0,W):K[`aria-labelledby`],J=d.useRef(null),me=s.useMergedRefs(J,L);te.useFormReset(J,()=>U(m),T);let Y=Object.hasOwn(p,x)?x:`medium`,X=p[Y],Z=q?(0,f.jsx)(`span`,{id:W,className:t.cn(i.switchClassNames.label,`text-body-1 text-foreground`),children:r.materialiseSlotContent(y)}):null,Q=b===`before`||b===`above`,$=_||v;return(0,f.jsxs)(`label`,{ref:B,"data-label-position":b,"data-size":Y,className:t.cn(i.switchClassNames.root,`relative inline-flex items-start gap-2 select-none`,b===`above`&&`flex-col gap-1`,$?`cursor-not-allowed opacity-50 has-focus-visible:opacity-100`:`cursor-pointer`,R),...ue,children:[Q&&Z,(0,f.jsx)(`button`,{id:de,type:`button`,...K,"aria-labelledby":pe,"aria-errormessage":D,"aria-details":O,"aria-disabled":k,autoFocus:A,tabIndex:j,onFocus:N,onBlur:P,onKeyDown:F,onKeyUp:I,ref:me,role:`switch`,"aria-checked":H,disabled:_&&!v,...c.focusableDisabledProps(v,{reachable:!0}),onClick:v?e=>{e.preventDefault(),e.stopPropagation()}:ee.composeEventHandlers(M,()=>U(e=>!e)),className:t.cn(i.switchClassNames.track,`relative inline-flex shrink-0 items-center rounded-full border p-0 transition-colors duration-200 motion-reduce:transition-none`,X.track,q&&b!==`above`&&X.offset,u.focusRing,H?t.cn(`border-primary bg-primary`,$?ie:re):t.cn(`border-stroke-accessible bg-transparent`,$?u.forcedColors.disabled:u.forcedColors.control)),children:(0,f.jsx)(`span`,{"aria-hidden":`true`,className:t.cn(i.switchClassNames.thumb,`block rounded-full transition-transform duration-200 motion-reduce:transition-none forced-colors:forced-color-adjust-none`,X.thumb,H?t.cn(X.on,`bg-primary-foreground`):t.cn(X.off,`bg-stroke-accessible`),$?`forced-colors:bg-[GrayText]`:H?`forced-colors:bg-[HighlightText]`:`forced-colors:bg-[ButtonText]`)})}),!Q&&Z,(0,f.jsx)(ne.HiddenInput,{type:`checkbox`,name:S,form:T,disabled:$,value:C,checked:H,required:fe,onInvalid:()=>J.current?.focus()})]})};m.displayName=`Switch`,exports.Switch=m;
2
2
  //# sourceMappingURL=Switch.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Switch.cjs","names":["slotRendersContent","useControllable","useId","useFieldContext","useFieldControl","joinIds","React","useMergedRefs","cn","switchClassNames","materialiseSlotContent","focusableDisabledProps","composeEventHandlers","focusRing","forcedColors","HiddenInput","require_runtime","require_cn","require_dev","require_slot","require_composeEventHandlers","require_classNames","require_useControllable","require_useId","require_useMergedRefs","require_aria","require_useFieldControl","require_styles","require_useFormReset","require_HiddenInput","react","react_jsx_runtime","onTrack","disabledOnTrack","Switch","checkedProp","ariaLabel","ariaLabelledBy","ariaDescribedBy","ariaInvalid","ariaRequired","ariaErrorMessage","ariaDetails","ariaDisabled","rest","onChange","hasChildren","children","checked","setChecked","defaultChecked","onCheckedChange","next","generatedId","labelTextId","field","fieldProps","required","isRequired","hasLabel","label","labelledBy","buttonRef","mergedControlRef","controlRef","form","labelText","labelFirst","labelPosition","unavailable","disabled","disabledFocusable","className","event","onClick","prev"],"sources":["../../../src/components/input/Switch.tsx"],"sourcesContent":["import * as React from 'react';\nimport { focusableDisabledProps, joinIds } from '../../lib/aria';\nimport { switchClassNames } from '../../lib/classNames';\nimport { cn } from '../../lib/cn';\nimport { composeEventHandlers } from '../../lib/composeEventHandlers';\nimport { warnDeprecated, warnOnce } from '../../lib/dev';\nimport { materialiseSlotContent, slotRendersContent } from '../../lib/slot';\nimport { focusRing, forcedColors } from '../../lib/styles';\nimport type { LabelPosition } from '../../lib/types';\nimport { useControllable } from '../../hooks/useControllable';\nimport { useFieldContext, useFieldControl } from '../../hooks/useFieldControl';\nimport { useFormReset } from '../../hooks/useFormReset';\nimport { useId } from '../../hooks/useId';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { HiddenInput } from '../internal/HiddenInput';\n\n/*\n * Forced colors: the focusable track keeps system-color utilities, so the browser still forces its\n * focus outline; only the thumb (the leaf indicator) opts out of forced colors. A disabled switch\n * draws a GrayText thumb and border on Canvas, without the Highlight fill.\n */\nconst onTrack = 'forced-colors:border-[Highlight] forced-colors:bg-[Highlight]';\nconst disabledOnTrack = 'forced-colors:border-[GrayText] forced-colors:bg-[Canvas]';\n\n/** Handlers that run on the `role=\"switch\"` button instead of the root label (C-ROUTING). */\ntype SwitchControlHandlers = 'onClick' | 'onFocus' | 'onBlur' | 'onKeyDown' | 'onKeyUp';\n\n/** Where a Switch renders its label: after the track (default), before it or above it. */\nexport type SwitchLabelPosition = Extract<LabelPosition, 'before' | 'after' | 'above'>;\n\n/** Properties for the Switch component. */\nexport interface SwitchProps extends Omit<\n React.HTMLAttributes<HTMLLabelElement>,\n 'onChange' | 'defaultChecked' | SwitchControlHandlers\n> {\n /** Controlled checked (on/off) state. */\n checked?: boolean;\n /** Initial checked state for uncontrolled usage (also the value a form reset restores).\n * @default false\n */\n defaultChecked?: boolean;\n /** Called with the new checked state when the switch is toggled (not for no-op updates). */\n onCheckedChange?: (checked: boolean) => void;\n /**\n * Called with the new checked state when the switch is toggled.\n * @deprecated Use `onCheckedChange`.\n */\n onChange?: (checked: boolean) => void;\n /** Whether the switch is disabled and non-interactive. */\n disabled?: boolean;\n /**\n * Marks the switch unavailable but keeps it focusable and in the tab order: for a disabled\n * switch that needs a Tooltip, or one in a toolbar. Renders `aria-disabled=\"true\"`,\n * `data-disabled` and `data-disabled-focusable` on the switch instead of the native `disabled`\n * attribute. The switch cannot be toggled and is not submitted with its form; your `onClick` is\n * not called, and a click on the switch (or Space and Enter) does not reach ancestor click\n * handlers, as for a natively disabled control. Wins over `disabled` when both are set. In a\n * roving container (a `Toolbar`) it stays in the arrow-key order.\n * @default false\n */\n disabledFocusable?: boolean;\n /**\n * Label next to the control (any phrasing content, links included, but no other form\n * controls). It names the control through `aria-labelledby`, after a consumer or Field\n * `aria-labelledby`; a consumer `aria-label` names it instead. Clicking its text toggles the\n * control; clicking a link inside it follows the link. `children` are not rendered: pass the\n * label here.\n */\n label?: React.ReactNode;\n /**\n * Where the label renders: after the control (default), before it, or above it. The DOM order\n * follows the visual order.\n * @default 'after'\n */\n labelPosition?: SwitchLabelPosition;\n /**\n * Form field name. With a name, the switch takes part in native form submission: it submits\n * `name=value` while on.\n */\n name?: string;\n /** Value submitted while on.\n * @default 'on'\n */\n value?: string;\n /** The form must not be submitted while the switch is off (native validation). */\n required?: boolean;\n /** Id of the form the switch belongs to, when it is rendered outside that form. */\n form?: string;\n /** Click handler of the switch control (the `role=\"switch\"` button). */\n onClick?: React.MouseEventHandler<HTMLButtonElement>;\n /** Focus handler of the switch control. */\n onFocus?: React.FocusEventHandler<HTMLButtonElement>;\n /** Blur handler of the switch control. */\n onBlur?: React.FocusEventHandler<HTMLButtonElement>;\n /** Keydown handler of the switch control. */\n onKeyDown?: React.KeyboardEventHandler<HTMLButtonElement>;\n /** Keyup handler of the switch control. */\n onKeyUp?: React.KeyboardEventHandler<HTMLButtonElement>;\n /** Ref to the focusable `role=\"switch\"` button. */\n controlRef?: React.Ref<HTMLButtonElement>;\n /** Ref to the root `<label>` element. */\n ref?: React.Ref<HTMLLabelElement>;\n}\n\n/**\n * An on/off toggle drawn as a `role=\"switch\"` button inside a `<label>`.\n *\n * - The root `<label>` receives `ref`, `className`, `style` and `data-*`; the control receives\n * `id`, `aria-label`/`-labelledby`/`-describedby`/`-invalid`/`-required`/`-errormessage`/\n * `-details`/`-disabled`, `tabIndex`, `autoFocus` and the click, focus and key handlers\n * (`controlRef` exposes it). A consumer `onClick` runs once per activation, before the toggle;\n * `preventDefault()` in it cancels the toggle.\n * - Inside a `Field` it is named by the Field label (followed by its own `label` text) and described\n * by the Field message and hint.\n * - With `name` (or `required`) it takes part in native forms like `<input type=\"checkbox\">`, and\n * a form reset restores `defaultChecked`.\n * - The thumb position mirrors when the switch itself is right-to-left (its nearest `dir`), so a\n * switch in an LTR subtree of an RTL page keeps the LTR layout.\n * - `label` takes rich content (a link) and renders after the switch, or before or above it with\n * `labelPosition` (the root carries `data-label-position`). Beside the label, the switch lines\n * up with the first line of a label that wraps or has a second line. `children` are not\n * rendered (a development warning says so).\n */\nexport const Switch = ({\n checked: checkedProp,\n defaultChecked = false,\n onCheckedChange,\n onChange,\n disabled = false,\n disabledFocusable = false,\n label,\n labelPosition = 'after',\n name,\n value,\n required,\n form,\n id,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n 'aria-invalid': ariaInvalid,\n 'aria-required': ariaRequired,\n 'aria-errormessage': ariaErrorMessage,\n 'aria-details': ariaDetails,\n 'aria-disabled': ariaDisabled,\n autoFocus,\n tabIndex,\n onClick,\n onFocus,\n onBlur,\n onKeyDown,\n onKeyUp,\n controlRef,\n className,\n children,\n ref,\n ...rest\n}: SwitchProps) => {\n if (onChange !== undefined) warnDeprecated('Switch', 'onChange', 'onCheckedChange');\n const hasChildren = slotRendersContent(children);\n React.useEffect(() => {\n if (hasChildren) {\n warnOnce('Switch:children', 'Switch: children are not rendered. Pass the label in `label`.');\n }\n }, [hasChildren]);\n const [checked, setChecked] = useControllable(checkedProp, defaultChecked, (next: boolean) => {\n onCheckedChange?.(next);\n onChange?.(next);\n });\n\n const generatedId = useId('switch');\n const labelTextId = useId('switch-label');\n const field = useFieldContext();\n const fieldProps = useFieldControl({\n id,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n 'aria-invalid': ariaInvalid,\n // An explicit `required={false}` wins over a required Field, so aria-required always matches\n // the native validation below (`isRequired`).\n 'aria-required': ariaRequired ?? required,\n });\n const isRequired = required ?? field?.required ?? false;\n // `0` is a label; `null`, `false`, `''` and empty collections are not (C-SLOTS).\n const hasLabel = slotRendersContent(label);\n // The label text names the control through aria-labelledby, disabled or not: axe exempts the\n // dimmed text of a disabled control only when the control references it this way (its <label>\n // exemption covers native inputs only). A consumer aria-label still names the control alone.\n // The ids already there (the consumer's, the Field label from useFieldControl) come first, then\n // the Field label whose <label htmlFor> targets this control (aria-labelledby would hide it), so\n // the name keeps every label.\n const labelledBy =\n hasLabel && fieldProps['aria-label'] === undefined\n ? joinIds(\n fieldProps['aria-labelledby'],\n field && fieldProps.id === field.controlId ? field.labelId : undefined,\n labelTextId,\n )\n : fieldProps['aria-labelledby'];\n\n const buttonRef = React.useRef<HTMLButtonElement>(null);\n const mergedControlRef = useMergedRefs(buttonRef, controlRef);\n useFormReset(buttonRef, () => setChecked(defaultChecked), form);\n\n const labelText = hasLabel ? (\n <span id={labelTextId} className={cn(switchClassNames.label, 'text-body-1 text-foreground')}>\n {materialiseSlotContent(label)}\n </span>\n ) : null;\n const labelFirst = labelPosition === 'before' || labelPosition === 'above';\n // Focusable-disabled wins over `disabled`: the switch stays focusable but looks and acts disabled.\n const unavailable = disabled || disabledFocusable;\n\n return (\n <label\n ref={ref}\n data-label-position={labelPosition}\n className={cn(\n switchClassNames.root,\n // items-start: the 20px track lines up with the 20px first line of a label that wraps or\n // has a second line, not with its middle.\n 'relative inline-flex items-start gap-2 select-none',\n labelPosition === 'above' && 'flex-col gap-1',\n // The dimmed look lifts while a focus ring shows inside the root (the control's, under\n // disabledFocusable, or a link's in the label): opacity would dim the ring below 3:1.\n unavailable\n ? 'cursor-not-allowed opacity-50 has-focus-visible:opacity-100'\n : 'cursor-pointer',\n className,\n )}\n {...rest}\n >\n {labelFirst && labelText}\n <button\n id={generatedId}\n type=\"button\"\n {...fieldProps}\n aria-labelledby={labelledBy}\n aria-errormessage={ariaErrorMessage}\n aria-details={ariaDetails}\n aria-disabled={ariaDisabled}\n autoFocus={autoFocus}\n tabIndex={tabIndex}\n onFocus={onFocus}\n onBlur={onBlur}\n onKeyDown={onKeyDown}\n onKeyUp={onKeyUp}\n ref={mergedControlRef}\n role=\"switch\"\n aria-checked={checked}\n disabled={disabled && !disabledFocusable}\n {...focusableDisabledProps(disabledFocusable, { reachable: true })}\n onClick={\n disabledFocusable\n ? // Clicks, Space and Enter (which click a button) neither toggle nor reach `onClick`,\n // and, as a natively disabled control dispatches no click, no ancestor sees them.\n (event: React.MouseEvent<HTMLButtonElement>) => {\n event.preventDefault();\n event.stopPropagation();\n }\n : composeEventHandlers(onClick, () => setChecked((prev) => !prev))\n }\n className={cn(\n switchClassNames.track,\n // The whole control is sized in px, like the Checkbox box and the radio circle: the thumb\n // and its offsets are px, so a track in rem would stop fitting them at any root font size\n // other than 16px. The 40x20 track (1px border) leaves a 2px inset around the 14px thumb.\n // p-0 is set here, not left to the native reset, which any app button style overrides\n // (C-NATIVE): the thumb offsets assume no padding.\n 'relative inline-flex h-[20px] w-[40px] shrink-0 items-center rounded-full border p-0 transition-colors duration-200 motion-reduce:transition-none',\n focusRing,\n checked\n ? cn('border-primary bg-primary', unavailable ? disabledOnTrack : onTrack)\n : cn(\n 'border-stroke-accessible bg-transparent',\n unavailable ? forcedColors.disabled : forcedColors.control,\n ),\n )}\n >\n <span\n aria-hidden=\"true\"\n className={cn(\n switchClassNames.thumb,\n 'block h-[14px] w-[14px] rounded-full transition-transform duration-200 motion-reduce:transition-none forced-colors:forced-color-adjust-none',\n checked\n ? 'translate-x-[22px] wave-rtl:-translate-x-[22px] bg-primary-foreground'\n : 'translate-x-[2px] wave-rtl:-translate-x-[2px] bg-stroke-accessible',\n unavailable\n ? 'forced-colors:bg-[GrayText]'\n : checked\n ? 'forced-colors:bg-[HighlightText]'\n : 'forced-colors:bg-[ButtonText]',\n )}\n />\n </button>\n {!labelFirst && labelText}\n <HiddenInput\n type=\"checkbox\"\n name={name}\n form={form}\n disabled={unavailable}\n value={value}\n checked={checked}\n required={isRequired}\n onInvalid={() => buttonRef.current?.focus()}\n />\n </label>\n );\n};\n\nSwitch.displayName = 'Switch';\n"],"mappings":"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"}
1
+ {"version":3,"file":"Switch.cjs","names":["slotRendersContent","useControllable","useId","useFieldContext","useFieldControl","joinIds","React","useMergedRefs","cn","switchClassNames","materialiseSlotContent","focusableDisabledProps","composeEventHandlers","focusRing","forcedColors","HiddenInput","require_runtime","require_cn","require_dev","require_slot","require_composeEventHandlers","require_classNames","require_useControllable","require_useId","require_useMergedRefs","require_aria","require_useFieldControl","require_styles","require_useFormReset","require_HiddenInput","react","react_jsx_runtime","onTrack","disabledOnTrack","SWITCH_SIZE","Switch","checkedProp","ariaLabel","ariaLabelledBy","ariaDescribedBy","ariaInvalid","ariaRequired","ariaErrorMessage","ariaDetails","ariaDisabled","rest","onChange","hasChildren","children","checked","setChecked","defaultChecked","onCheckedChange","next","generatedId","labelTextId","field","fieldProps","required","isRequired","hasLabel","label","labelledBy","buttonRef","mergedControlRef","controlRef","form","resolvedSize","size","sizeMetrics","labelText","labelFirst","labelPosition","unavailable","disabled","disabledFocusable","className","event","onClick","prev"],"sources":["../../../src/components/input/Switch.tsx"],"sourcesContent":["import * as React from 'react';\nimport { focusableDisabledProps, joinIds } from '../../lib/aria';\nimport { switchClassNames } from '../../lib/classNames';\nimport { cn } from '../../lib/cn';\nimport { composeEventHandlers } from '../../lib/composeEventHandlers';\nimport { warnDeprecated, warnOnce } from '../../lib/dev';\nimport { materialiseSlotContent, slotRendersContent } from '../../lib/slot';\nimport { focusRing, forcedColors } from '../../lib/styles';\nimport type { LabelPosition, Size } from '../../lib/types';\nimport { useControllable } from '../../hooks/useControllable';\nimport { useFieldContext, useFieldControl } from '../../hooks/useFieldControl';\nimport { useFormReset } from '../../hooks/useFormReset';\nimport { useId } from '../../hooks/useId';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { HiddenInput } from '../internal/HiddenInput';\n\n/*\n * Forced colors: the focusable track keeps system-color utilities, so the browser still forces its\n * focus outline; only the thumb (the leaf indicator) opts out of forced colors. A disabled switch\n * draws a GrayText thumb and border on Canvas, without the Highlight fill.\n */\nconst onTrack = 'forced-colors:border-[Highlight] forced-colors:bg-[Highlight]';\nconst disabledOnTrack = 'forced-colors:border-[GrayText] forced-colors:bg-[Canvas]';\n\n/** Handlers that run on the `role=\"switch\"` button instead of the root label (C-ROUTING). */\ntype SwitchControlHandlers = 'onClick' | 'onFocus' | 'onBlur' | 'onKeyDown' | 'onKeyUp';\n\n/** Where a Switch renders its label: after the track (default), before it or above it. */\nexport type SwitchLabelPosition = Extract<LabelPosition, 'before' | 'after' | 'above'>;\n\n/** Size of the track: a 32x16 track with a 10px thumb, or the 0.7 40x20 track with a 14px thumb. */\nexport type SwitchSize = Extract<Size, 'small' | 'medium'>;\n\n/** Per-size track, thumb and label-offset classes. */\nconst SWITCH_SIZE: Readonly<\n Record<SwitchSize, { track: string; thumb: string; on: string; off: string; offset: string }>\n> = {\n small: {\n // The target layer is placed against the padding box inside the 1px border: 14 + 2 × 5 = 24px.\n track: 'h-[16px] w-[32px] before:absolute before:inset-x-0 before:-inset-y-[5px]',\n thumb: 'h-[10px] w-[10px]',\n on: 'translate-x-[18px] wave-rtl:-translate-x-[18px]',\n off: 'translate-x-[2px] wave-rtl:-translate-x-[2px]',\n offset: 'mt-0.5',\n },\n medium: {\n track: 'h-[20px] w-[40px]',\n thumb: 'h-[14px] w-[14px]',\n on: 'translate-x-[22px] wave-rtl:-translate-x-[22px]',\n off: 'translate-x-[2px] wave-rtl:-translate-x-[2px]',\n offset: '',\n },\n};\n\n/** Properties for the Switch component. */\nexport interface SwitchProps extends Omit<\n React.HTMLAttributes<HTMLLabelElement>,\n 'onChange' | 'defaultChecked' | SwitchControlHandlers\n> {\n /** Controlled checked (on/off) state. */\n checked?: boolean;\n /** Initial checked state for uncontrolled usage (also the value a form reset restores).\n * @default false\n */\n defaultChecked?: boolean;\n /** Called with the new checked state when the switch is toggled (not for no-op updates). */\n onCheckedChange?: (checked: boolean) => void;\n /**\n * Called with the new checked state when the switch is toggled.\n * @deprecated Use `onCheckedChange`.\n */\n onChange?: (checked: boolean) => void;\n /** Whether the switch is disabled and non-interactive. */\n disabled?: boolean;\n /**\n * Marks the switch unavailable but keeps it focusable and in the tab order: for a disabled\n * switch that needs a Tooltip, or one in a toolbar. Renders `aria-disabled=\"true\"`,\n * `data-disabled` and `data-disabled-focusable` on the switch instead of the native `disabled`\n * attribute. The switch cannot be toggled and is not submitted with its form; your `onClick` is\n * not called, and a click on the switch (or Space and Enter) does not reach ancestor click\n * handlers, as for a natively disabled control. Wins over `disabled` when both are set. In a\n * roving container (a `Toolbar`) it stays in the arrow-key order.\n * @default false\n */\n disabledFocusable?: boolean;\n /**\n * Label next to the control (any phrasing content, links included, but no other form\n * controls). It names the control through `aria-labelledby`, after a consumer or Field\n * `aria-labelledby`; a consumer `aria-label` names it instead. Clicking its text toggles the\n * control; clicking a link inside it follows the link. `children` are not rendered: pass the\n * label here.\n */\n label?: React.ReactNode;\n /**\n * Where the label renders: after the control (default), before it, or above it. The DOM order\n * follows the visual order.\n * @default 'after'\n */\n labelPosition?: SwitchLabelPosition;\n /**\n * Size of the track: the 0.7 40x20 track with a 14px thumb, or a small 32x16 track with a 10px\n * thumb and a 24px-tall pointer target. Unlike the text controls and pickers, Switch does not\n * read a surrounding Field's size or a WaveProvider's input defaults: this prop is the only\n * source of its size.\n * @default 'medium'\n */\n size?: SwitchSize;\n /**\n * Form field name. With a name, the switch takes part in native form submission: it submits\n * `name=value` while on.\n */\n name?: string;\n /** Value submitted while on.\n * @default 'on'\n */\n value?: string;\n /** The form must not be submitted while the switch is off (native validation). */\n required?: boolean;\n /** Id of the form the switch belongs to, when it is rendered outside that form. */\n form?: string;\n /** Click handler of the switch control (the `role=\"switch\"` button). */\n onClick?: React.MouseEventHandler<HTMLButtonElement>;\n /** Focus handler of the switch control. */\n onFocus?: React.FocusEventHandler<HTMLButtonElement>;\n /** Blur handler of the switch control. */\n onBlur?: React.FocusEventHandler<HTMLButtonElement>;\n /** Keydown handler of the switch control. */\n onKeyDown?: React.KeyboardEventHandler<HTMLButtonElement>;\n /** Keyup handler of the switch control. */\n onKeyUp?: React.KeyboardEventHandler<HTMLButtonElement>;\n /** Ref to the focusable `role=\"switch\"` button. */\n controlRef?: React.Ref<HTMLButtonElement>;\n /** Ref to the root `<label>` element. */\n ref?: React.Ref<HTMLLabelElement>;\n}\n\n/**\n * An on/off toggle drawn as a `role=\"switch\"` button inside a `<label>`.\n *\n * - The root `<label>` receives `ref`, `className`, `style` and `data-*`; the control receives\n * `id`, `aria-label`/`-labelledby`/`-describedby`/`-invalid`/`-required`/`-errormessage`/\n * `-details`/`-disabled`, `tabIndex`, `autoFocus` and the click, focus and key handlers\n * (`controlRef` exposes it). A consumer `onClick` runs once per activation, before the toggle;\n * `preventDefault()` in it cancels the toggle.\n * - Inside a `Field` it is named by the Field label (followed by its own `label` text) and described\n * by the Field message and hint.\n * - With `name` (or `required`) it takes part in native forms like `<input type=\"checkbox\">`, and\n * a form reset restores `defaultChecked`.\n * - The thumb position mirrors when the switch itself is right-to-left (its nearest `dir`), so a\n * switch in an LTR subtree of an RTL page keeps the LTR layout.\n * - `label` takes rich content (a link) and renders after the switch, or before or above it with\n * `labelPosition` (the root carries `data-label-position`). Beside the label, the switch lines\n * up with the first line of a label that wraps or has a second line. `children` are not\n * rendered (a development warning says so).\n * - `size` (`small`, `medium`) sets the track and thumb dimensions; the root carries `data-size`.\n * Switch does not read a surrounding `Field`'s size or a `WaveProvider`'s input defaults.\n */\nexport const Switch = ({\n checked: checkedProp,\n defaultChecked = false,\n onCheckedChange,\n onChange,\n disabled = false,\n disabledFocusable = false,\n label,\n labelPosition = 'after',\n size = 'medium',\n name,\n value,\n required,\n form,\n id,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n 'aria-invalid': ariaInvalid,\n 'aria-required': ariaRequired,\n 'aria-errormessage': ariaErrorMessage,\n 'aria-details': ariaDetails,\n 'aria-disabled': ariaDisabled,\n autoFocus,\n tabIndex,\n onClick,\n onFocus,\n onBlur,\n onKeyDown,\n onKeyUp,\n controlRef,\n className,\n children,\n ref,\n ...rest\n}: SwitchProps) => {\n if (onChange !== undefined) warnDeprecated('Switch', 'onChange', 'onCheckedChange');\n const hasChildren = slotRendersContent(children);\n React.useEffect(() => {\n if (hasChildren) {\n warnOnce('Switch:children', 'Switch: children are not rendered. Pass the label in `label`.');\n }\n }, [hasChildren]);\n const [checked, setChecked] = useControllable(checkedProp, defaultChecked, (next: boolean) => {\n onCheckedChange?.(next);\n onChange?.(next);\n });\n\n const generatedId = useId('switch');\n const labelTextId = useId('switch-label');\n const field = useFieldContext();\n const fieldProps = useFieldControl({\n id,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n 'aria-invalid': ariaInvalid,\n // An explicit `required={false}` wins over a required Field, so aria-required always matches\n // the native validation below (`isRequired`).\n 'aria-required': ariaRequired ?? required,\n });\n const isRequired = required ?? field?.required ?? false;\n // `0` is a label; `null`, `false`, `''` and empty collections are not (C-SLOTS).\n const hasLabel = slotRendersContent(label);\n // The label text names the control through aria-labelledby, disabled or not: axe exempts the\n // dimmed text of a disabled control only when the control references it this way (its <label>\n // exemption covers native inputs only). A consumer aria-label still names the control alone.\n // The ids already there (the consumer's, the Field label from useFieldControl) come first, then\n // the Field label whose <label htmlFor> targets this control (aria-labelledby would hide it), so\n // the name keeps every label.\n const labelledBy =\n hasLabel && fieldProps['aria-label'] === undefined\n ? joinIds(\n fieldProps['aria-labelledby'],\n field && fieldProps.id === field.controlId ? field.labelId : undefined,\n labelTextId,\n )\n : fieldProps['aria-labelledby'];\n\n const buttonRef = React.useRef<HTMLButtonElement>(null);\n const mergedControlRef = useMergedRefs(buttonRef, controlRef);\n useFormReset(buttonRef, () => setChecked(defaultChecked), form);\n\n // A size outside the union (from untyped code) renders, and is reported in the data attribute,\n // as the default: TypeScript rejects one, but the value still reaches this render.\n const resolvedSize: SwitchSize = Object.hasOwn(SWITCH_SIZE, size) ? size : 'medium';\n const sizeMetrics = SWITCH_SIZE[resolvedSize];\n const labelText = hasLabel ? (\n <span id={labelTextId} className={cn(switchClassNames.label, 'text-body-1 text-foreground')}>\n {materialiseSlotContent(label)}\n </span>\n ) : null;\n const labelFirst = labelPosition === 'before' || labelPosition === 'above';\n // Focusable-disabled wins over `disabled`: the switch stays focusable but looks and acts disabled.\n const unavailable = disabled || disabledFocusable;\n\n return (\n <label\n ref={ref}\n data-label-position={labelPosition}\n data-size={resolvedSize}\n className={cn(\n switchClassNames.root,\n // items-start: the track lines up with the top of the label's first line, not its\n // middle, when the label wraps or has a second line; medium's 20px track matches that\n // 20px line exactly, small's 16px track gets an offset below to stay centred on it.\n 'relative inline-flex items-start gap-2 select-none',\n labelPosition === 'above' && 'flex-col gap-1',\n // The dimmed look lifts while a focus ring shows inside the root (the control's, under\n // disabledFocusable, or a link's in the label): opacity would dim the ring below 3:1.\n unavailable\n ? 'cursor-not-allowed opacity-50 has-focus-visible:opacity-100'\n : 'cursor-pointer',\n className,\n )}\n {...rest}\n >\n {labelFirst && labelText}\n <button\n id={generatedId}\n type=\"button\"\n {...fieldProps}\n aria-labelledby={labelledBy}\n aria-errormessage={ariaErrorMessage}\n aria-details={ariaDetails}\n aria-disabled={ariaDisabled}\n autoFocus={autoFocus}\n tabIndex={tabIndex}\n onFocus={onFocus}\n onBlur={onBlur}\n onKeyDown={onKeyDown}\n onKeyUp={onKeyUp}\n ref={mergedControlRef}\n role=\"switch\"\n aria-checked={checked}\n disabled={disabled && !disabledFocusable}\n {...focusableDisabledProps(disabledFocusable, { reachable: true })}\n onClick={\n disabledFocusable\n ? // Clicks, Space and Enter (which click a button) neither toggle nor reach `onClick`,\n // and, as a natively disabled control dispatches no click, no ancestor sees them.\n (event: React.MouseEvent<HTMLButtonElement>) => {\n event.preventDefault();\n event.stopPropagation();\n }\n : composeEventHandlers(onClick, () => setChecked((prev) => !prev))\n }\n className={cn(\n switchClassNames.track,\n // The whole control is sized in px, like the Checkbox box and the radio circle: the\n // thumb and its offsets are px, so a track in rem would stop fitting them at any root\n // font size other than 16px. Medium keeps the 0.7 look, a 40x20 track around a 14px\n // thumb, a 2px gap on every side. Small is a 32x16 track around a 10px thumb, the same\n // 2px gap, and a transparent strip behind the track that grows the pointer target to\n // 24px tall. No padding is assumed here, unlike the native reset that any app button\n // style overrides (C-NATIVE): the thumb offsets rely on it staying at zero.\n 'relative inline-flex shrink-0 items-center rounded-full border p-0 transition-colors duration-200 motion-reduce:transition-none',\n sizeMetrics.track,\n // The offset centres the track on the label line beside it; above the label, the\n // column gap alone separates them.\n hasLabel && labelPosition !== 'above' && sizeMetrics.offset,\n focusRing,\n checked\n ? cn('border-primary bg-primary', unavailable ? disabledOnTrack : onTrack)\n : cn(\n 'border-stroke-accessible bg-transparent',\n unavailable ? forcedColors.disabled : forcedColors.control,\n ),\n )}\n >\n <span\n aria-hidden=\"true\"\n className={cn(\n switchClassNames.thumb,\n 'block rounded-full transition-transform duration-200 motion-reduce:transition-none forced-colors:forced-color-adjust-none',\n sizeMetrics.thumb,\n checked\n ? cn(sizeMetrics.on, 'bg-primary-foreground')\n : cn(sizeMetrics.off, 'bg-stroke-accessible'),\n unavailable\n ? 'forced-colors:bg-[GrayText]'\n : checked\n ? 'forced-colors:bg-[HighlightText]'\n : 'forced-colors:bg-[ButtonText]',\n )}\n />\n </button>\n {!labelFirst && labelText}\n <HiddenInput\n type=\"checkbox\"\n name={name}\n form={form}\n disabled={unavailable}\n value={value}\n checked={checked}\n required={isRequired}\n onInvalid={() => buttonRef.current?.focus()}\n />\n </label>\n );\n};\n\nSwitch.displayName = 'Switch';\n"],"mappings":"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"}