@lifesg/react-design-system 4.0.0-alpha.8 → 4.0.0-alpha.9

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 (199) hide show
  1. package/accordion/accordion-item.js.map +1 -1
  2. package/accordion/accordion.js.map +1 -1
  3. package/alert/alert.js.map +1 -1
  4. package/cjs/accordion/accordion-item.js.map +1 -1
  5. package/cjs/accordion/accordion.js.map +1 -1
  6. package/cjs/alert/alert.js.map +1 -1
  7. package/cjs/countdown-timer/countdown-timer.js.map +1 -1
  8. package/cjs/data-table/data-table.js +1 -1
  9. package/cjs/data-table/data-table.js.map +1 -1
  10. package/cjs/date-range-input/date-range-input.js.map +1 -1
  11. package/cjs/e-signature/e-signature-canvas.js.map +1 -1
  12. package/cjs/error-display/inactivity-timer.js.map +1 -1
  13. package/cjs/external/dompurify/dist/purify.es.js +2 -2
  14. package/cjs/external/dompurify/dist/purify.es.js.map +1 -1
  15. package/cjs/file-download/file-list-card/file-list-card.js.map +1 -1
  16. package/cjs/file-upload/custom-sensors.js.map +1 -1
  17. package/cjs/file-upload/file-list/file-list.js.map +1 -1
  18. package/cjs/filter/addons/filter-item-checkbox-helpers.js.map +1 -1
  19. package/cjs/filter/addons/filter-item-checkbox.js.map +1 -1
  20. package/cjs/filter/filter-item.js.map +1 -1
  21. package/cjs/filter/filter.js.map +1 -1
  22. package/cjs/form/form-textarea/form-textarea.js +1 -1
  23. package/cjs/form/form-textarea/form-textarea.js.map +1 -1
  24. package/cjs/fullscreen-image-carousel/stateful-image.js.map +1 -1
  25. package/cjs/histogram-slider/histogram-slider.js.map +1 -1
  26. package/cjs/input-range-slider/input-range-slider.js.map +1 -1
  27. package/cjs/input-slider/input-slider.js.map +1 -1
  28. package/cjs/input-textarea/textarea-counter.js.map +1 -1
  29. package/cjs/input-textarea/textarea.js +1 -1
  30. package/cjs/input-textarea/textarea.js.map +1 -1
  31. package/cjs/input-textarea/textarea.styles.js +1 -1
  32. package/cjs/input-textarea/textarea.styles.js.map +1 -1
  33. package/{input-textarea/textarea.styles_q77m5x.css → cjs/input-textarea/textarea.styles_6we7bs.css} +3 -1
  34. package/cjs/modal/index.js.map +1 -1
  35. package/cjs/modal-v2/modal-v2.js +1 -1
  36. package/cjs/modal-v2/modal-v2.js.map +1 -1
  37. package/cjs/modal-v2/slots/card.js +1 -1
  38. package/cjs/modal-v2/slots/card.js.map +1 -1
  39. package/cjs/modal-v2/slots/header.js +2 -0
  40. package/cjs/modal-v2/slots/header.js.map +1 -0
  41. package/cjs/modal-v2/slots/slot-styles.js +1 -1
  42. package/cjs/modal-v2/slots/slot-styles.js.map +1 -1
  43. package/cjs/modal-v2/slots/{slot-styles_13sd376.css → slot-styles_10ut1v7.css} +5 -1
  44. package/cjs/navbar/navbar-items.js +1 -1
  45. package/cjs/navbar/navbar-items.js.map +1 -1
  46. package/cjs/navbar/navbar-items.styles.js +1 -1
  47. package/cjs/navbar/navbar-items.styles.js.map +1 -1
  48. package/cjs/navbar/{navbar-items.styles_1i3ltot.css → navbar-items.styles_1b1zx.css} +9 -8
  49. package/cjs/navbar/navbar-logo-data.js +1 -1
  50. package/cjs/navbar/navbar-logo-data.js.map +1 -1
  51. package/cjs/navbar/navbar.js.map +1 -1
  52. package/cjs/otp-input/otp-input.js.map +1 -1
  53. package/cjs/overlay/overlay.js.map +1 -1
  54. package/cjs/pagination/pagination.js.map +1 -1
  55. package/cjs/phone-number-input/phone-number-input.js.map +1 -1
  56. package/cjs/predictive-text-input/predictive-text-input.js.map +1 -1
  57. package/cjs/shared/dropdown-list/dropdown-list.js.map +1 -1
  58. package/cjs/shared/internal-calendar/calendar-manager.js.map +1 -1
  59. package/cjs/shared/standalone-date-input/standalone-date-input.js.map +1 -1
  60. package/cjs/theme/types.js +1 -1
  61. package/cjs/theme/types.js.map +1 -1
  62. package/cjs/time-slot-bar/time-slot-bar.js.map +1 -1
  63. package/cjs/time-slot-bar-week/time-slot-bar-week-days.js.map +1 -1
  64. package/cjs/timetable/timetable.js.map +1 -1
  65. package/cjs/toggle/toggle-icon.js +1 -1
  66. package/cjs/toggle/toggle-icon.js.map +1 -1
  67. package/cjs/toggle/toggle-icon.styles.js +1 -1
  68. package/cjs/toggle/toggle-icon.styles.js.map +1 -1
  69. package/cjs/toggle/{toggle-icon.styles_ba71y.css → toggle-icon.styles_o6dxr3.css} +3 -2
  70. package/cjs/toggle/toggle.js +1 -1
  71. package/cjs/toggle/toggle.js.map +1 -1
  72. package/cjs/toggle/toggle.styles.js +1 -1
  73. package/cjs/toggle/toggle.styles.js.map +1 -1
  74. package/cjs/toggle/toggle.styles_11tmfsr.css +41 -0
  75. package/cjs/unit-number/unit-number-input.js.map +1 -1
  76. package/cjs/util/is-styled-components.js.map +1 -1
  77. package/cjs/util/string-helper.js +1 -1
  78. package/cjs/util/string-helper.js.map +1 -1
  79. package/cjs/util/use-event-listener.js.map +1 -1
  80. package/cjs/util/use-state-actions.js.map +1 -1
  81. package/countdown-timer/countdown-timer.js.map +1 -1
  82. package/data-table/data-table.js +1 -1
  83. package/data-table/data-table.js.map +1 -1
  84. package/data-table/types.d.ts +2 -2
  85. package/date-range-input/date-range-input.js.map +1 -1
  86. package/docs/component-catalog.json +1 -1
  87. package/e-signature/e-signature-canvas.js.map +1 -1
  88. package/error-display/inactivity-timer.js.map +1 -1
  89. package/external/dompurify/dist/purify.es.js +2 -2
  90. package/external/dompurify/dist/purify.es.js.map +1 -1
  91. package/file-download/file-list-card/file-list-card.js.map +1 -1
  92. package/file-upload/custom-sensors.d.ts +2 -2
  93. package/file-upload/custom-sensors.js.map +1 -1
  94. package/file-upload/file-list/file-list.js.map +1 -1
  95. package/filter/addons/filter-item-checkbox-helpers.js.map +1 -1
  96. package/filter/addons/filter-item-checkbox.js.map +1 -1
  97. package/filter/filter-item.js.map +1 -1
  98. package/filter/filter.js.map +1 -1
  99. package/form/form-textarea/form-textarea.js +1 -1
  100. package/form/form-textarea/form-textarea.js.map +1 -1
  101. package/fullscreen-image-carousel/stateful-image.js.map +1 -1
  102. package/fullscreen-image-carousel/types.d.ts +2 -2
  103. package/histogram-slider/histogram-slider.js.map +1 -1
  104. package/input-range-slider/input-range-slider.js.map +1 -1
  105. package/input-slider/input-slider.js.map +1 -1
  106. package/input-textarea/textarea-counter.js.map +1 -1
  107. package/input-textarea/textarea.js +1 -1
  108. package/input-textarea/textarea.js.map +1 -1
  109. package/input-textarea/textarea.styles.d.ts +2 -0
  110. package/input-textarea/textarea.styles.js +1 -1
  111. package/input-textarea/textarea.styles.js.map +1 -1
  112. package/{cjs/input-textarea/textarea.styles_q77m5x.css → input-textarea/textarea.styles_6we7bs.css} +3 -1
  113. package/input-textarea/types.d.ts +7 -0
  114. package/modal/index.d.ts +4 -0
  115. package/modal/index.js.map +1 -1
  116. package/modal/types.d.ts +2 -0
  117. package/modal-v2/modal-v2.d.ts +6 -0
  118. package/modal-v2/modal-v2.js +1 -1
  119. package/modal-v2/modal-v2.js.map +1 -1
  120. package/modal-v2/slots/card.js +1 -1
  121. package/modal-v2/slots/card.js.map +1 -1
  122. package/modal-v2/slots/header.d.ts +5 -0
  123. package/modal-v2/slots/header.js +2 -0
  124. package/modal-v2/slots/header.js.map +1 -0
  125. package/modal-v2/slots/index.d.ts +1 -0
  126. package/modal-v2/slots/slot-styles.d.ts +4 -0
  127. package/modal-v2/slots/slot-styles.js +1 -1
  128. package/modal-v2/slots/slot-styles.js.map +1 -1
  129. package/modal-v2/slots/{slot-styles_13sd376.css → slot-styles_10ut1v7.css} +5 -1
  130. package/modal-v2/types.d.ts +21 -0
  131. package/navbar/navbar-items.js +1 -1
  132. package/navbar/navbar-items.js.map +1 -1
  133. package/navbar/navbar-items.styles.d.ts +2 -1
  134. package/navbar/navbar-items.styles.js +1 -1
  135. package/navbar/navbar-items.styles.js.map +1 -1
  136. package/navbar/{navbar-items.styles_1i3ltot.css → navbar-items.styles_1b1zx.css} +9 -8
  137. package/navbar/navbar-logo-data.js +1 -1
  138. package/navbar/navbar-logo-data.js.map +1 -1
  139. package/navbar/navbar.js.map +1 -1
  140. package/otp-input/otp-input.js.map +1 -1
  141. package/overlay/overlay.js.map +1 -1
  142. package/package.json +1 -1
  143. package/pagination/pagination.js.map +1 -1
  144. package/phone-number-input/phone-number-input.js.map +1 -1
  145. package/predictive-text-input/predictive-text-input.js.map +1 -1
  146. package/shared/dropdown-list/dropdown-list.js.map +1 -1
  147. package/shared/internal-calendar/calendar-manager.js.map +1 -1
  148. package/shared/standalone-date-input/standalone-date-input.js.map +1 -1
  149. package/theme/styles/a11y-playground.css +358 -87
  150. package/theme/styles/bookingsg.css +391 -85
  151. package/theme/styles/careercompass.css +396 -90
  152. package/theme/styles/ccube.css +402 -45
  153. package/theme/styles/imda.css +348 -85
  154. package/theme/styles/lifesg.css +377 -87
  155. package/theme/styles/mylegacy.css +382 -85
  156. package/theme/styles/oneservice.css +349 -86
  157. package/theme/styles/pa.css +172 -63
  158. package/theme/styles/rbs.css +403 -46
  159. package/theme/styles/sgw-digital-lobby.css +352 -87
  160. package/theme/styles/smgs.css +401 -95
  161. package/theme/styles/spf.css +208 -99
  162. package/theme/styles/supportgowhere.css +387 -90
  163. package/theme/styles/tote-board.css +816 -0
  164. package/theme/types.d.ts +1 -1
  165. package/theme/types.js +1 -1
  166. package/theme/types.js.map +1 -1
  167. package/time-slot-bar/time-slot-bar.js.map +1 -1
  168. package/time-slot-bar-week/time-slot-bar-week-days.js.map +1 -1
  169. package/timetable/timetable.js.map +1 -1
  170. package/toggle/toggle-icon.d.ts +2 -1
  171. package/toggle/toggle-icon.js +1 -1
  172. package/toggle/toggle-icon.js.map +1 -1
  173. package/toggle/toggle-icon.styles.d.ts +1 -0
  174. package/toggle/toggle-icon.styles.js +1 -1
  175. package/toggle/toggle-icon.styles.js.map +1 -1
  176. package/toggle/{toggle-icon.styles_ba71y.css → toggle-icon.styles_o6dxr3.css} +3 -2
  177. package/toggle/toggle.js +1 -1
  178. package/toggle/toggle.js.map +1 -1
  179. package/toggle/toggle.styles.d.ts +18 -15
  180. package/toggle/toggle.styles.js +1 -1
  181. package/toggle/toggle.styles.js.map +1 -1
  182. package/toggle/toggle.styles_11tmfsr.css +41 -0
  183. package/unit-number/unit-number-input.js.map +1 -1
  184. package/util/is-styled-components.js.map +1 -1
  185. package/util/string-helper.d.ts +2 -2
  186. package/util/string-helper.js +1 -1
  187. package/util/string-helper.js.map +1 -1
  188. package/util/use-event-listener.js.map +1 -1
  189. package/util/use-state-actions.js.map +1 -1
  190. package/cjs/form/form-textarea/form-textarea.styles.js +0 -2
  191. package/cjs/form/form-textarea/form-textarea.styles.js.map +0 -1
  192. package/cjs/form/form-textarea/form-textarea.styles_1kneyif.css +0 -3
  193. package/cjs/toggle/toggle.styles_1t5qrye.css +0 -38
  194. package/form/form-textarea/form-textarea.styles.d.ts +0 -3
  195. package/form/form-textarea/form-textarea.styles.js +0 -2
  196. package/form/form-textarea/form-textarea.styles.js.map +0 -1
  197. package/form/form-textarea/form-textarea.styles_1kneyif.css +0 -3
  198. package/theme/styles/default.css +0 -158
  199. package/toggle/toggle.styles_1t5qrye.css +0 -38
@@ -1 +1 @@
1
- {"version":3,"file":"histogram-slider.js","sources":["../../src/histogram-slider/histogram-slider.tsx"],"sourcesContent":["import { announce, clearAnnouncer } from \"@react-aria/live-announcer\";\nimport clsx from \"clsx\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\n\nimport { InputRangeSlider } from \"../input-range-slider\";\nimport { concatIds } from \"../shared/accessibility\";\nimport { useApplyStyle } from \"../theme\";\nimport { Typography } from \"../typography\";\nimport { useId } from \"../util\";\nimport * as styles from \"./histogram-slider.styles\";\nimport type { HistogramBinProps, HistogramSliderProps } from \"./types\";\n\nconst ANNOUNCEMENT_DEBOUNCE_MS = 500;\n\n// @catalog\n/**\n * A histogram with an overlaid two-thumb range slider for numeric filtering.\n *\n * Use `HistogramSlider` to let users narrow a dataset by selecting a numeric\n * range while seeing how values are distributed across bins.\n */\nexport const HistogramSlider = ({\n bins = [],\n interval,\n disabled,\n readOnly,\n value,\n showRangeLabels,\n rangeLabelPrefix,\n rangeLabelSuffix,\n ariaLabels,\n \"aria-labelledby\": ariaLabelledBy,\n \"aria-describedby\": ariaDescribedBy,\n onChange,\n onChangeEnd,\n renderEmptyView,\n renderRangeLabel,\n className,\n ...otherProps\n}: HistogramSliderProps) => {\n // =========================================================================\n // CONST, STATE, REF\n // =========================================================================\n const counts = bins.map((item) => item.count);\n const maxCount = Math.max(...counts);\n\n const values = bins.map((item) => item.minValue);\n const maxValue = Math.max(...values);\n const minValue = Math.min(...values);\n\n const [selection, setSelection] = useState<[number, number]>(\n initSelection()\n );\n const internalId = useId();\n const announcementTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(\n null\n );\n const rangeLabelId = `${internalId}-range-label`;\n const sliderDescribedBy = concatIds(\n ariaDescribedBy,\n showRangeLabels ? rangeLabelId : undefined\n );\n\n const items = useMemo(() => {\n const sorted = [...bins].sort((a, b) => a.minValue - b.minValue);\n const numCols = (maxValue - minValue) / interval + 1;\n\n const items: typeof sorted = [];\n for (let i = 0; i < numCols; i++) {\n const bin = sorted.find(\n (b) => b.minValue === minValue + interval * i\n );\n if (bin) {\n items.push(bin);\n } else {\n items.push({ count: 0, minValue: minValue + interval * i });\n }\n }\n return items;\n }, [bins, interval]);\n\n // =========================================================================\n // EFFECTS\n // =========================================================================\n useEffect(() => {\n if (value !== selection) {\n setSelection(initSelection());\n }\n }, [value]);\n\n useEffect(() => {\n return () => {\n clearPendingAnnouncement();\n };\n }, []);\n\n // =========================================================================\n // EVENT HANDLERS\n // =========================================================================\n const handleChange = (values: number[]) => {\n const [val1, val2] = values;\n const newSelection: [number, number] = [val1, val2];\n setSelection(newSelection);\n scheduleRangePercentageAnnouncement(newSelection);\n onChange?.(newSelection);\n };\n\n const handleChangeEnd = (values: number[]) => {\n const [val1, val2] = values;\n const newSelection: [number, number] = [val1, val2];\n setSelection(newSelection);\n scheduleRangePercentageAnnouncement(newSelection);\n onChangeEnd?.(newSelection);\n };\n\n // =========================================================================\n // HELPER FUNCTIONS\n // =========================================================================\n function initSelection(): [number, number] {\n return value ?? [minValue, minValue + interval];\n }\n\n function clearPendingAnnouncement() {\n if (announcementTimeoutRef.current) {\n clearTimeout(announcementTimeoutRef.current);\n announcementTimeoutRef.current = null;\n }\n }\n\n function getRangePercentageDescription(currentSelection: [number, number]) {\n const totalCount = items.reduce((sum, item) => sum + item.count, 0);\n\n if (totalCount === 0) {\n return \"0% of results available in the range you specified.\";\n }\n\n const selectedCount = items.reduce((sum, item) => {\n const isSelected =\n item.minValue >= currentSelection[0] &&\n item.minValue < currentSelection[1];\n\n return isSelected ? sum + item.count : sum;\n }, 0);\n\n const percentage = Math.round((selectedCount / totalCount) * 100);\n\n return `${percentage}% of results available in the range you specified.`;\n }\n\n function scheduleRangePercentageAnnouncement(\n currentSelection: [number, number]\n ) {\n if (disabled || readOnly) {\n return;\n }\n\n const announcement = getRangePercentageDescription(currentSelection);\n\n clearPendingAnnouncement();\n\n announcementTimeoutRef.current = setTimeout(() => {\n clearAnnouncer(\"polite\");\n announce(announcement, \"polite\");\n }, ANNOUNCEMENT_DEBOUNCE_MS);\n }\n\n // =========================================================================\n // RENDER FUNCTIONS\n // =========================================================================\n const renderRangeItem = (value: number) => {\n if (renderRangeLabel) {\n return renderRangeLabel(value);\n }\n return (\n <Typography.BodyBL>\n {rangeLabelPrefix}\n {value}\n {rangeLabelSuffix}\n </Typography.BodyBL>\n );\n };\n\n const renderHistogramSlider = () => {\n const selectionDescription = getRangePercentageDescription(selection);\n\n return (\n <>\n <div className={styles.histogram}>\n {items.map((item, i) => (\n <HistogramBar\n key={i}\n item={item}\n maxCount={maxCount}\n isSelected={\n item.minValue >= selection[0] &&\n item.minValue < selection[1]\n }\n isDisabled={disabled || readOnly}\n />\n ))}\n </div>\n <InputRangeSlider\n min={minValue}\n max={maxValue + interval}\n step={interval}\n minRange={interval}\n numOfThumbs={2}\n value={selection}\n disabled={disabled}\n readOnly={readOnly}\n ariaLabels={ariaLabels}\n ariaDescriptions={[\n selectionDescription,\n selectionDescription,\n ]}\n aria-describedby={sliderDescribedBy}\n aria-labelledby={ariaLabelledBy}\n onChange={handleChange}\n onChangeEnd={handleChangeEnd}\n className={styles.slider}\n />\n </>\n );\n };\n\n return (\n <div role=\"group\" {...otherProps} className={className}>\n {showRangeLabels && (\n <div id={rangeLabelId} className={styles.label}>\n {renderRangeItem(selection[0])}\n <div className={styles.separator}>-</div>\n {renderRangeItem(selection[1])}\n </div>\n )}\n {items.every((item) => item.count === 0) && renderEmptyView\n ? renderEmptyView()\n : renderHistogramSlider()}\n </div>\n );\n};\n\n// =============================================================================\n// SUB COMPONENT\n// =============================================================================\ninterface HistogramBarProps {\n item: HistogramBinProps;\n maxCount: number;\n isSelected: boolean;\n isDisabled: boolean | undefined;\n}\n\nconst HistogramBar = ({\n item,\n maxCount,\n isSelected,\n isDisabled,\n}: HistogramBarProps) => {\n const ratio = item.count / maxCount;\n const barRef = useRef<HTMLDivElement>(null);\n\n useApplyStyle(barRef, {\n height: ratio ? `calc(calc(100% - 0.25rem) * ${ratio} + 0.25rem)` : \"0\",\n });\n\n return (\n <div\n data-testid=\"histogram-bar\"\n className={clsx(\n styles.bar,\n isDisabled && isSelected && styles.barSelectedDisabled,\n isDisabled && styles.barDisabled,\n isSelected && styles.barSelected\n )}\n ref={barRef}\n />\n );\n};\n"],"names":["HistogramSlider","_a","bins","interval","disabled","readOnly","value","showRangeLabels","rangeLabelPrefix","rangeLabelSuffix","ariaLabels","ariaLabelledBy","ariaDescribedBy","onChange","onChangeEnd","renderEmptyView","renderRangeLabel","className","otherProps","__rest","counts","map","item","count","maxCount","Math","max","values","minValue","maxValue","min","selection","setSelection","useState","initSelection","internalId","useId","announcementTimeoutRef","useRef","rangeLabelId","sliderDescribedBy","concatIds","undefined","items","useMemo","sorted","sort","a","b","numCols","i","bin","find","push","useEffect","clearPendingAnnouncement","handleChange","val1","val2","newSelection","scheduleRangePercentageAnnouncement","handleChangeEnd","current","clearTimeout","getRangePercentageDescription","currentSelection","totalCount","reduce","sum","selectedCount","round","announcement","setTimeout","clearAnnouncer","announce","renderRangeItem","_jsxs","Typography","BodyBL","children","Object","assign","role","id","styles.label","_jsx","styles.separator","every","selectionDescription","_Fragment","styles.histogram","HistogramBar","isSelected","isDisabled","InputRangeSlider","step","minRange","numOfThumbs","ariaDescriptions","styles.slider","renderHistogramSlider","ratio","barRef","useApplyStyle","height","clsx","styles.bar","styles.barSelectedDisabled","styles.barDisabled","styles.barSelected","ref"],"mappings":"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"}
1
+ {"version":3,"file":"histogram-slider.js","sources":["../../src/histogram-slider/histogram-slider.tsx"],"sourcesContent":["import { announce, clearAnnouncer } from \"@react-aria/live-announcer\";\nimport clsx from \"clsx\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\n\nimport { InputRangeSlider } from \"../input-range-slider\";\nimport { concatIds } from \"../shared/accessibility\";\nimport { useApplyStyle } from \"../theme\";\nimport { Typography } from \"../typography\";\nimport { useId } from \"../util\";\nimport * as styles from \"./histogram-slider.styles\";\nimport type { HistogramBinProps, HistogramSliderProps } from \"./types\";\n\nconst ANNOUNCEMENT_DEBOUNCE_MS = 500;\n\n// @catalog\n/**\n * A histogram with an overlaid two-thumb range slider for numeric filtering.\n *\n * Use `HistogramSlider` to let users narrow a dataset by selecting a numeric\n * range while seeing how values are distributed across bins.\n */\nexport const HistogramSlider = ({\n bins = [],\n interval,\n disabled,\n readOnly,\n value,\n showRangeLabels,\n rangeLabelPrefix,\n rangeLabelSuffix,\n ariaLabels,\n \"aria-labelledby\": ariaLabelledBy,\n \"aria-describedby\": ariaDescribedBy,\n onChange,\n onChangeEnd,\n renderEmptyView,\n renderRangeLabel,\n className,\n ...otherProps\n}: HistogramSliderProps) => {\n // =========================================================================\n // CONST, STATE, REF\n // =========================================================================\n const counts = bins.map((item) => item.count);\n const maxCount = Math.max(...counts);\n\n const values = bins.map((item) => item.minValue);\n const maxValue = Math.max(...values);\n const minValue = Math.min(...values);\n\n const [selection, setSelection] = useState<[number, number]>(\n initSelection()\n );\n const internalId = useId();\n const announcementTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(\n null\n );\n const rangeLabelId = `${internalId}-range-label`;\n const sliderDescribedBy = concatIds(\n ariaDescribedBy,\n showRangeLabels ? rangeLabelId : undefined\n );\n\n const items = useMemo(() => {\n const sorted = [...bins].sort((a, b) => a.minValue - b.minValue);\n const numCols = (maxValue - minValue) / interval + 1;\n\n const items: typeof sorted = [];\n for (let i = 0; i < numCols; i++) {\n const bin = sorted.find(\n (b) => b.minValue === minValue + interval * i\n );\n if (bin) {\n items.push(bin);\n } else {\n items.push({ count: 0, minValue: minValue + interval * i });\n }\n }\n return items;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [bins, interval]);\n\n // =========================================================================\n // EFFECTS\n // =========================================================================\n useEffect(() => {\n if (value !== selection) {\n setSelection(initSelection());\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [value]);\n\n useEffect(() => {\n return () => {\n clearPendingAnnouncement();\n };\n }, []);\n\n // =========================================================================\n // EVENT HANDLERS\n // =========================================================================\n const handleChange = (values: number[]) => {\n const [val1, val2] = values;\n const newSelection: [number, number] = [val1, val2];\n setSelection(newSelection);\n scheduleRangePercentageAnnouncement(newSelection);\n onChange?.(newSelection);\n };\n\n const handleChangeEnd = (values: number[]) => {\n const [val1, val2] = values;\n const newSelection: [number, number] = [val1, val2];\n setSelection(newSelection);\n scheduleRangePercentageAnnouncement(newSelection);\n onChangeEnd?.(newSelection);\n };\n\n // =========================================================================\n // HELPER FUNCTIONS\n // =========================================================================\n function initSelection(): [number, number] {\n return value ?? [minValue, minValue + interval];\n }\n\n function clearPendingAnnouncement() {\n if (announcementTimeoutRef.current) {\n clearTimeout(announcementTimeoutRef.current);\n announcementTimeoutRef.current = null;\n }\n }\n\n function getRangePercentageDescription(currentSelection: [number, number]) {\n const totalCount = items.reduce((sum, item) => sum + item.count, 0);\n\n if (totalCount === 0) {\n return \"0% of results available in the range you specified.\";\n }\n\n const selectedCount = items.reduce((sum, item) => {\n const isSelected =\n item.minValue >= currentSelection[0] &&\n item.minValue < currentSelection[1];\n\n return isSelected ? sum + item.count : sum;\n }, 0);\n\n const percentage = Math.round((selectedCount / totalCount) * 100);\n\n return `${percentage}% of results available in the range you specified.`;\n }\n\n function scheduleRangePercentageAnnouncement(\n currentSelection: [number, number]\n ) {\n if (disabled || readOnly) {\n return;\n }\n\n const announcement = getRangePercentageDescription(currentSelection);\n\n clearPendingAnnouncement();\n\n announcementTimeoutRef.current = setTimeout(() => {\n clearAnnouncer(\"polite\");\n announce(announcement, \"polite\");\n }, ANNOUNCEMENT_DEBOUNCE_MS);\n }\n\n // =========================================================================\n // RENDER FUNCTIONS\n // =========================================================================\n const renderRangeItem = (value: number) => {\n if (renderRangeLabel) {\n return renderRangeLabel(value);\n }\n return (\n <Typography.BodyBL>\n {rangeLabelPrefix}\n {value}\n {rangeLabelSuffix}\n </Typography.BodyBL>\n );\n };\n\n const renderHistogramSlider = () => {\n const selectionDescription = getRangePercentageDescription(selection);\n\n return (\n <>\n <div className={styles.histogram}>\n {items.map((item, i) => (\n <HistogramBar\n key={i}\n item={item}\n maxCount={maxCount}\n isSelected={\n item.minValue >= selection[0] &&\n item.minValue < selection[1]\n }\n isDisabled={disabled || readOnly}\n />\n ))}\n </div>\n <InputRangeSlider\n min={minValue}\n max={maxValue + interval}\n step={interval}\n minRange={interval}\n numOfThumbs={2}\n value={selection}\n disabled={disabled}\n readOnly={readOnly}\n ariaLabels={ariaLabels}\n ariaDescriptions={[\n selectionDescription,\n selectionDescription,\n ]}\n aria-describedby={sliderDescribedBy}\n aria-labelledby={ariaLabelledBy}\n onChange={handleChange}\n onChangeEnd={handleChangeEnd}\n className={styles.slider}\n />\n </>\n );\n };\n\n return (\n <div role=\"group\" {...otherProps} className={className}>\n {showRangeLabels && (\n <div id={rangeLabelId} className={styles.label}>\n {renderRangeItem(selection[0])}\n <div className={styles.separator}>-</div>\n {renderRangeItem(selection[1])}\n </div>\n )}\n {items.every((item) => item.count === 0) && renderEmptyView\n ? renderEmptyView()\n : renderHistogramSlider()}\n </div>\n );\n};\n\n// =============================================================================\n// SUB COMPONENT\n// =============================================================================\ninterface HistogramBarProps {\n item: HistogramBinProps;\n maxCount: number;\n isSelected: boolean;\n isDisabled: boolean | undefined;\n}\n\nconst HistogramBar = ({\n item,\n maxCount,\n isSelected,\n isDisabled,\n}: HistogramBarProps) => {\n const ratio = item.count / maxCount;\n const barRef = useRef<HTMLDivElement>(null);\n\n useApplyStyle(barRef, {\n height: ratio ? `calc(calc(100% - 0.25rem) * ${ratio} + 0.25rem)` : \"0\",\n });\n\n return (\n <div\n data-testid=\"histogram-bar\"\n className={clsx(\n styles.bar,\n isDisabled && isSelected && styles.barSelectedDisabled,\n isDisabled && styles.barDisabled,\n isSelected && styles.barSelected\n )}\n ref={barRef}\n />\n );\n};\n"],"names":["HistogramSlider","_a","bins","interval","disabled","readOnly","value","showRangeLabels","rangeLabelPrefix","rangeLabelSuffix","ariaLabels","ariaLabelledBy","ariaDescribedBy","onChange","onChangeEnd","renderEmptyView","renderRangeLabel","className","otherProps","__rest","counts","map","item","count","maxCount","Math","max","values","minValue","maxValue","min","selection","setSelection","useState","initSelection","internalId","useId","announcementTimeoutRef","useRef","rangeLabelId","sliderDescribedBy","concatIds","undefined","items","useMemo","sorted","sort","a","b","numCols","i","bin","find","push","useEffect","clearPendingAnnouncement","handleChange","val1","val2","newSelection","scheduleRangePercentageAnnouncement","handleChangeEnd","current","clearTimeout","getRangePercentageDescription","currentSelection","totalCount","reduce","sum","selectedCount","round","announcement","setTimeout","clearAnnouncer","announce","renderRangeItem","_jsxs","Typography","BodyBL","children","Object","assign","role","id","styles.label","_jsx","styles.separator","every","selectionDescription","_Fragment","styles.histogram","HistogramBar","isSelected","isDisabled","InputRangeSlider","step","minRange","numOfThumbs","ariaDescriptions","styles.slider","renderHistogramSlider","ratio","barRef","useApplyStyle","height","clsx","styles.bar","styles.barSelectedDisabled","styles.barDisabled","styles.barSelected","ref"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"input-range-slider.js","sources":["../../src/input-range-slider/input-range-slider.tsx"],"sourcesContent":["import { announce, clearAnnouncer } from \"@react-aria/live-announcer\";\nimport clsx from \"clsx\";\nimport type React from \"react\";\nimport { useEffect, useState } from \"react\";\nimport ReactSlider from \"react-slider\";\n\nimport { concatIds, VisuallyHidden } from \"../shared/accessibility\";\nimport { Colour } from \"../theme\";\nimport { Typography } from \"../typography\";\nimport { useId } from \"../util\";\nimport * as styles from \"./input-range-slider.styles\";\nimport { Thumb, Track } from \"./slider-components\";\nimport type { InputRangeSliderProps } from \"./types\";\n\n// @catalog\n/**\n * A draggable track slider for selecting one or more numeric values within a\n * bounded range.\n *\n * Use `InputRangeSlider` when the selection does not need to be precise;\n * the user picks a value or a range by dragging thumb controls along a track.\n */\nexport const InputRangeSlider = ({\n id,\n value,\n min = 0,\n max = 100,\n step = 1,\n minRange = 0,\n numOfThumbs = 2,\n colors,\n disabled,\n readOnly,\n showSliderLabels,\n sliderLabelPrefix,\n sliderLabelSuffix,\n showIndicatorLabel,\n indicatorLabelPrefix,\n indicatorLabelSuffix,\n ariaLabels,\n ariaDescriptions,\n \"aria-invalid\": ariaInvalid,\n \"aria-labelledby\": ariaLabelledBy,\n \"aria-describedby\": ariaDescribedBy,\n renderSliderLabel,\n onChange,\n onChangeEnd,\n className,\n ...otherProps\n}: InputRangeSliderProps) => {\n // =========================================================================\n // CONST, STATE, REF\n // =========================================================================\n const [selection, setSelection] = useState<number[]>(initialiseSelection());\n const [focusedThumbIndex, setFocusedThumbIndex] = useState<number | null>(\n null\n );\n const internalId = useId();\n const trackColors = getTrackColors();\n const indicatorTextId = `${internalId}-indicator`;\n const instructionTextId = `${internalId}-instruction`;\n const resolvedAriaLabels = getResolvedAriaLabels();\n\n // =========================================================================\n // EFFECTS\n // =========================================================================\n useEffect(() => {\n if (value !== selection) {\n setSelection(initialiseSelection());\n }\n }, [value]);\n\n // =========================================================================\n // EVENT HANDLERS\n // =========================================================================\n const handleChange = (value: number | readonly number[], index: number) => {\n if (readOnly || disabled) {\n return;\n }\n\n if (typeof value === \"number\") {\n const nextValue = [value];\n setSelection(nextValue);\n onChange?.(nextValue);\n return;\n }\n\n if (index > -1 && selection[index] === value[index]) {\n // skip unnecessary update when dragging the start thumb across the end thumb\n return;\n }\n\n const nextValue = [...value];\n setSelection(nextValue);\n onChange?.(nextValue);\n };\n\n const handleChangeEnd = (value: number | readonly number[]) => {\n if (readOnly || disabled) {\n return;\n }\n\n if (typeof value === \"number\") {\n const val = [value];\n setSelection(val);\n onChangeEnd?.(val);\n return;\n }\n\n const newSelection = [...value];\n setSelection(newSelection);\n onChangeEnd?.(newSelection);\n };\n\n const handleThumbKeyDown = (\n event: React.KeyboardEvent<HTMLDivElement>,\n index: number\n ) => {\n if (disabled || readOnly) {\n return;\n }\n\n const message = getBlockedMovementMessage(event.key, index);\n\n if (!message) {\n return;\n }\n\n clearAnnouncer(\"assertive\");\n announce(message, \"assertive\");\n };\n\n const handleNativeRangeChange = (\n event: React.ChangeEvent<HTMLInputElement>,\n index: number\n ) => {\n if (disabled || readOnly) {\n return;\n }\n\n const nextRawValue = Number(event.currentTarget.value);\n\n const nextSelection = [...selection];\n nextSelection[index] = clampValueForThumb(\n nextRawValue,\n index,\n selection\n );\n\n setSelection(nextSelection);\n onChange?.(nextSelection);\n onChangeEnd?.(nextSelection);\n };\n\n // =========================================================================\n // HELPER FUNCTIONS\n // =========================================================================\n function initialiseSelection() {\n if (value && value.length === numOfThumbs) {\n return value;\n }\n\n const values: number[] = [];\n for (let i = 0; i < numOfThumbs; i++) {\n values.push(min + step * i);\n }\n return values;\n }\n\n function getResolvedAriaLabels() {\n return Array.from({ length: numOfThumbs }, (_, index) => {\n return ariaLabels?.[index] || getDefaultAriaLabel(index);\n });\n }\n\n function getDefaultAriaLabel(index: number) {\n if (numOfThumbs === 1) {\n return \"\";\n }\n\n if (index === 0) {\n return \"Minimum value slider\";\n }\n\n if (index === numOfThumbs - 1) {\n return \"Maximum value slider\";\n }\n\n return \"Indeterminate value slider\";\n }\n\n function getThumbDescriptionText(index: number) {\n return ariaDescriptions?.[index];\n }\n\n function getTrackColors() {\n const defaultColors = getDefaultColors();\n return new Array(numOfThumbs + 1).fill(0).map((_, i) => {\n return colors?.[i] || defaultColors[i];\n });\n }\n\n function getDefaultColors() {\n const inactiveColor =\n disabled || readOnly\n ? Colour[\"border-disabled\"]\n : Colour[\"border-strong\"];\n const activeColor =\n disabled || readOnly\n ? Colour[\"border-selected-disabled\"]\n : Colour[\"border-selected\"];\n\n if (numOfThumbs === 1) {\n return [activeColor, inactiveColor];\n }\n\n const defaultColors: typeof colors = [inactiveColor];\n\n for (let index = 0; index < numOfThumbs - 1; index++) {\n defaultColors.push(activeColor);\n }\n\n defaultColors.push(inactiveColor);\n return defaultColors;\n }\n\n function getValueText(currentValue: number) {\n return `${sliderLabelPrefix || \"\"}${currentValue}${\n sliderLabelSuffix || \"\"\n }`;\n }\n\n function getThumbDescriptionIds(thumbDescriptionId?: string) {\n return concatIds(\n ariaDescribedBy,\n showIndicatorLabel ? indicatorTextId : undefined,\n disabled || readOnly ? undefined : instructionTextId,\n thumbDescriptionId\n );\n }\n\n function isIncreaseKey(key: string) {\n return [\"ArrowRight\", \"ArrowUp\", \"PageUp\", \"End\"].includes(key);\n }\n\n function isDecreaseKey(key: string) {\n return [\"ArrowLeft\", \"ArrowDown\", \"PageDown\", \"Home\"].includes(key);\n }\n\n function getSliderTypeText(label?: string) {\n if (!label) {\n return \"slider\";\n }\n\n return label.toLowerCase();\n }\n\n function getSliderTargetText(label?: string) {\n if (!label) {\n return \"slider\";\n }\n\n return label.replace(/ slider$/i, \"\").toLowerCase();\n }\n\n function getBlockedMovementMessage(key: string, index: number) {\n if (numOfThumbs < 2) {\n return \"\";\n }\n\n const currentValue = selection[index];\n const previousValue = index > 0 ? selection[index - 1] : undefined;\n const nextValue =\n index < selection.length - 1 ? selection[index + 1] : undefined;\n\n const currentLabel = resolvedAriaLabels[index];\n const previousLabel =\n index > 0 ? resolvedAriaLabels[index - 1] : undefined;\n const nextLabel =\n index < resolvedAriaLabels.length - 1\n ? resolvedAriaLabels[index + 1]\n : undefined;\n\n if (isIncreaseKey(key)) {\n if (\n nextValue !== undefined &&\n currentValue >= nextValue - minRange\n ) {\n return `The ${getSliderTypeText(\n currentLabel\n )} has reached its limit. Increase the ${getSliderTargetText(\n nextLabel\n )} to set a higher ${getSliderTargetText(currentLabel)}.`;\n }\n }\n\n if (isDecreaseKey(key)) {\n if (\n previousValue !== undefined &&\n currentValue <= previousValue + minRange\n ) {\n return `The ${getSliderTypeText(\n currentLabel\n )} has reached its limit. Decrease the ${getSliderTargetText(\n previousLabel\n )} to set a lower ${getSliderTargetText(currentLabel)}.`;\n }\n }\n\n return \"\";\n }\n\n function getThumbMin(index: number, values = selection) {\n if (index === 0) {\n return min;\n }\n\n return values[index - 1] + minRange;\n }\n\n function getThumbMax(index: number, values = selection) {\n if (index === values.length - 1) {\n return max;\n }\n\n return values[index + 1] - minRange;\n }\n\n function clampValueForThumb(\n nextValue: number,\n index: number,\n values: number[]\n ) {\n const minAllowed = getThumbMin(index, values);\n const maxAllowed = getThumbMax(index, values);\n\n return Math.min(maxAllowed, Math.max(minAllowed, nextValue));\n }\n\n // =========================================================================\n // RENDER FUNCTIONS\n // =========================================================================\n const formatLabel = (value: number) => {\n if (renderSliderLabel) {\n return renderSliderLabel(value);\n }\n\n return (\n <Typography.BodyBL className={styles.labelText}>\n {sliderLabelPrefix}\n {value}\n {sliderLabelSuffix}\n </Typography.BodyBL>\n );\n };\n\n const formatIndicationLabel = () => {\n let formattedSelection = \"\";\n\n if (selection.length === 1) {\n formattedSelection = `${selection[0]}`;\n } else if (selection.length === 2) {\n formattedSelection = `${selection[0]} - ${selection[1]}`;\n } else if (selection.length > 2) {\n const minValue = Math.min(...selection);\n const maxValue = Math.max(...selection);\n formattedSelection = `${minValue} - ${maxValue}`;\n }\n\n return (\n <Typography.BodyBL className={styles.labelText}>\n {indicatorLabelPrefix}\n {formattedSelection}\n {indicatorLabelSuffix}\n </Typography.BodyBL>\n );\n };\n\n return (\n <div\n {...otherProps}\n id={id}\n role=\"group\"\n aria-labelledby={ariaLabelledBy}\n aria-disabled={disabled}\n className={clsx(styles.wrapper, className)}\n >\n {!disabled && !readOnly && (\n <VisuallyHidden id={instructionTextId}>\n Use left and right arrow keys to adjust the slider.\n </VisuallyHidden>\n )}\n\n {showIndicatorLabel && (\n <div\n id={indicatorTextId}\n className={styles.indicatorLabelContainer}\n >\n {formatIndicationLabel()}\n </div>\n )}\n\n {selection.map((thumbValue, index) => {\n const thumbLabelTextId = `${internalId}-thumb-label-${index}`;\n const thumbDescriptionText = getThumbDescriptionText(index);\n const thumbDescriptionTextId = thumbDescriptionText\n ? `${internalId}-thumb-description-${index}`\n : undefined;\n\n return (\n <VisuallyHidden key={`native-slider-${index}`}>\n {thumbDescriptionTextId && (\n <span id={thumbDescriptionTextId}>\n {thumbDescriptionText}\n </span>\n )}\n <span id={thumbLabelTextId}>\n {resolvedAriaLabels[index]}\n </span>\n\n <input\n data-testid={`slider-input-${index}`}\n type=\"range\"\n min={getThumbMin(index)}\n max={getThumbMax(index)}\n step={step}\n value={thumbValue}\n aria-disabled={disabled || undefined}\n readOnly={readOnly}\n aria-labelledby={concatIds(\n ariaLabelledBy,\n thumbLabelTextId\n )}\n aria-describedby={getThumbDescriptionIds(\n thumbDescriptionTextId\n )}\n aria-valuetext={getValueText(thumbValue)}\n aria-invalid={ariaInvalid}\n onFocus={() => setFocusedThumbIndex(index)}\n onBlur={() => setFocusedThumbIndex(null)}\n onChange={(event) =>\n handleNativeRangeChange(event, index)\n }\n onKeyDown={(event) => {\n handleThumbKeyDown(event, index);\n }}\n />\n </VisuallyHidden>\n );\n })}\n\n {/* Native range inputs provide the accessible interaction model.\n The visible react-slider is presentation-only. */}\n <ReactSlider\n step={step}\n min={min}\n max={max}\n value={selection}\n disabled={disabled || readOnly}\n onChange={handleChange}\n onAfterChange={handleChangeEnd}\n minDistance={minRange}\n aria-hidden\n className={styles.slider}\n renderThumb={(thumbProps, state) => {\n return (\n <Thumb\n data-testid={`slider-thumb-${state.index}`}\n {...thumbProps}\n key={thumbProps.key}\n tabIndex={-1}\n aria-hidden\n focused={focusedThumbIndex === state.index}\n disabled={disabled}\n readOnly={readOnly}\n />\n );\n }}\n renderTrack={(trackProps, state) => {\n return (\n <Track\n data-testid={`slider-track-${state.index}`}\n {...trackProps}\n key={trackProps.key}\n color={trackColors[state.index]}\n />\n );\n }}\n />\n\n {showSliderLabels && (\n <div className={styles.labelContainer}>\n <div>{formatLabel(min)}</div>\n <div>{formatLabel(max)}</div>\n </div>\n )}\n </div>\n );\n};\n"],"names":["InputRangeSlider","_a","id","value","min","max","step","minRange","numOfThumbs","colors","disabled","readOnly","showSliderLabels","sliderLabelPrefix","sliderLabelSuffix","showIndicatorLabel","indicatorLabelPrefix","indicatorLabelSuffix","ariaLabels","ariaDescriptions","ariaInvalid","ariaLabelledBy","ariaDescribedBy","renderSliderLabel","onChange","onChangeEnd","className","otherProps","__rest","selection","setSelection","useState","initialiseSelection","focusedThumbIndex","setFocusedThumbIndex","internalId","useId","trackColors","defaultColors","inactiveColor","Colour","activeColor","index","push","getDefaultColors","Array","fill","map","_","i","getTrackColors","indicatorTextId","instructionTextId","resolvedAriaLabels","from","length","getDefaultAriaLabel","useEffect","handleThumbKeyDown","event","message","key","currentValue","previousValue","undefined","nextValue","currentLabel","previousLabel","nextLabel","includes","isIncreaseKey","getSliderTypeText","getSliderTargetText","isDecreaseKey","getBlockedMovementMessage","clearAnnouncer","announce","handleNativeRangeChange","nextRawValue","Number","currentTarget","nextSelection","values","minAllowed","getThumbMin","maxAllowed","getThumbMax","Math","clampValueForThumb","label","toLowerCase","replace","formatLabel","_jsxs","Typography","BodyBL","styles.labelText","children","Object","assign","role","clsx","styles.wrapper","_jsx","VisuallyHidden","styles.indicatorLabelContainer","formattedSelection","formatIndicationLabel","thumbValue","thumbLabelTextId","thumbDescriptionText","getThumbDescriptionText","thumbDescriptionTextId","type","concatIds","thumbDescriptionId","onFocus","onBlur","onKeyDown","ReactSlider","onAfterChange","val","newSelection","minDistance","styles.slider","renderThumb","thumbProps","state","_createElement","Thumb","tabIndex","focused","renderTrack","trackProps","Track","color","styles.labelContainer"],"mappings":"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"}
1
+ {"version":3,"file":"input-range-slider.js","sources":["../../src/input-range-slider/input-range-slider.tsx"],"sourcesContent":["import { announce, clearAnnouncer } from \"@react-aria/live-announcer\";\nimport clsx from \"clsx\";\nimport type React from \"react\";\nimport { useEffect, useState } from \"react\";\nimport ReactSlider from \"react-slider\";\n\nimport { concatIds, VisuallyHidden } from \"../shared/accessibility\";\nimport { Colour } from \"../theme\";\nimport { Typography } from \"../typography\";\nimport { useId } from \"../util\";\nimport * as styles from \"./input-range-slider.styles\";\nimport { Thumb, Track } from \"./slider-components\";\nimport type { InputRangeSliderProps } from \"./types\";\n\n// @catalog\n/**\n * A draggable track slider for selecting one or more numeric values within a\n * bounded range.\n *\n * Use `InputRangeSlider` when the selection does not need to be precise;\n * the user picks a value or a range by dragging thumb controls along a track.\n */\nexport const InputRangeSlider = ({\n id,\n value,\n min = 0,\n max = 100,\n step = 1,\n minRange = 0,\n numOfThumbs = 2,\n colors,\n disabled,\n readOnly,\n showSliderLabels,\n sliderLabelPrefix,\n sliderLabelSuffix,\n showIndicatorLabel,\n indicatorLabelPrefix,\n indicatorLabelSuffix,\n ariaLabels,\n ariaDescriptions,\n \"aria-invalid\": ariaInvalid,\n \"aria-labelledby\": ariaLabelledBy,\n \"aria-describedby\": ariaDescribedBy,\n renderSliderLabel,\n onChange,\n onChangeEnd,\n className,\n ...otherProps\n}: InputRangeSliderProps) => {\n // =========================================================================\n // CONST, STATE, REF\n // =========================================================================\n const [selection, setSelection] = useState<number[]>(initialiseSelection());\n const [focusedThumbIndex, setFocusedThumbIndex] = useState<number | null>(\n null\n );\n const internalId = useId();\n const trackColors = getTrackColors();\n const indicatorTextId = `${internalId}-indicator`;\n const instructionTextId = `${internalId}-instruction`;\n const resolvedAriaLabels = getResolvedAriaLabels();\n\n // =========================================================================\n // EFFECTS\n // =========================================================================\n useEffect(() => {\n if (value !== selection) {\n setSelection(initialiseSelection());\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [value]);\n\n // =========================================================================\n // EVENT HANDLERS\n // =========================================================================\n const handleChange = (value: number | readonly number[], index: number) => {\n if (readOnly || disabled) {\n return;\n }\n\n if (typeof value === \"number\") {\n const nextValue = [value];\n setSelection(nextValue);\n onChange?.(nextValue);\n return;\n }\n\n if (index > -1 && selection[index] === value[index]) {\n // skip unnecessary update when dragging the start thumb across the end thumb\n return;\n }\n\n const nextValue = [...value];\n setSelection(nextValue);\n onChange?.(nextValue);\n };\n\n const handleChangeEnd = (value: number | readonly number[]) => {\n if (readOnly || disabled) {\n return;\n }\n\n if (typeof value === \"number\") {\n const val = [value];\n setSelection(val);\n onChangeEnd?.(val);\n return;\n }\n\n const newSelection = [...value];\n setSelection(newSelection);\n onChangeEnd?.(newSelection);\n };\n\n const handleThumbKeyDown = (\n event: React.KeyboardEvent<HTMLDivElement>,\n index: number\n ) => {\n if (disabled || readOnly) {\n return;\n }\n\n const message = getBlockedMovementMessage(event.key, index);\n\n if (!message) {\n return;\n }\n\n clearAnnouncer(\"assertive\");\n announce(message, \"assertive\");\n };\n\n const handleNativeRangeChange = (\n event: React.ChangeEvent<HTMLInputElement>,\n index: number\n ) => {\n if (disabled || readOnly) {\n return;\n }\n\n const nextRawValue = Number(event.currentTarget.value);\n\n const nextSelection = [...selection];\n nextSelection[index] = clampValueForThumb(\n nextRawValue,\n index,\n selection\n );\n\n setSelection(nextSelection);\n onChange?.(nextSelection);\n onChangeEnd?.(nextSelection);\n };\n\n // =========================================================================\n // HELPER FUNCTIONS\n // =========================================================================\n function initialiseSelection() {\n if (value && value.length === numOfThumbs) {\n return value;\n }\n\n const values: number[] = [];\n for (let i = 0; i < numOfThumbs; i++) {\n values.push(min + step * i);\n }\n return values;\n }\n\n function getResolvedAriaLabels() {\n return Array.from({ length: numOfThumbs }, (_, index) => {\n return ariaLabels?.[index] || getDefaultAriaLabel(index);\n });\n }\n\n function getDefaultAriaLabel(index: number) {\n if (numOfThumbs === 1) {\n return \"\";\n }\n\n if (index === 0) {\n return \"Minimum value slider\";\n }\n\n if (index === numOfThumbs - 1) {\n return \"Maximum value slider\";\n }\n\n return \"Indeterminate value slider\";\n }\n\n function getThumbDescriptionText(index: number) {\n return ariaDescriptions?.[index];\n }\n\n function getTrackColors() {\n const defaultColors = getDefaultColors();\n return new Array(numOfThumbs + 1).fill(0).map((_, i) => {\n return colors?.[i] || defaultColors[i];\n });\n }\n\n function getDefaultColors() {\n const inactiveColor =\n disabled || readOnly\n ? Colour[\"border-disabled\"]\n : Colour[\"border-strong\"];\n const activeColor =\n disabled || readOnly\n ? Colour[\"border-selected-disabled\"]\n : Colour[\"border-selected\"];\n\n if (numOfThumbs === 1) {\n return [activeColor, inactiveColor];\n }\n\n const defaultColors: typeof colors = [inactiveColor];\n\n for (let index = 0; index < numOfThumbs - 1; index++) {\n defaultColors.push(activeColor);\n }\n\n defaultColors.push(inactiveColor);\n return defaultColors;\n }\n\n function getValueText(currentValue: number) {\n return `${sliderLabelPrefix || \"\"}${currentValue}${\n sliderLabelSuffix || \"\"\n }`;\n }\n\n function getThumbDescriptionIds(thumbDescriptionId?: string) {\n return concatIds(\n ariaDescribedBy,\n showIndicatorLabel ? indicatorTextId : undefined,\n disabled || readOnly ? undefined : instructionTextId,\n thumbDescriptionId\n );\n }\n\n function isIncreaseKey(key: string) {\n return [\"ArrowRight\", \"ArrowUp\", \"PageUp\", \"End\"].includes(key);\n }\n\n function isDecreaseKey(key: string) {\n return [\"ArrowLeft\", \"ArrowDown\", \"PageDown\", \"Home\"].includes(key);\n }\n\n function getSliderTypeText(label?: string) {\n if (!label) {\n return \"slider\";\n }\n\n return label.toLowerCase();\n }\n\n function getSliderTargetText(label?: string) {\n if (!label) {\n return \"slider\";\n }\n\n return label.replace(/ slider$/i, \"\").toLowerCase();\n }\n\n function getBlockedMovementMessage(key: string, index: number) {\n if (numOfThumbs < 2) {\n return \"\";\n }\n\n const currentValue = selection[index];\n const previousValue = index > 0 ? selection[index - 1] : undefined;\n const nextValue =\n index < selection.length - 1 ? selection[index + 1] : undefined;\n\n const currentLabel = resolvedAriaLabels[index];\n const previousLabel =\n index > 0 ? resolvedAriaLabels[index - 1] : undefined;\n const nextLabel =\n index < resolvedAriaLabels.length - 1\n ? resolvedAriaLabels[index + 1]\n : undefined;\n\n if (isIncreaseKey(key)) {\n if (\n nextValue !== undefined &&\n currentValue >= nextValue - minRange\n ) {\n return `The ${getSliderTypeText(\n currentLabel\n )} has reached its limit. Increase the ${getSliderTargetText(\n nextLabel\n )} to set a higher ${getSliderTargetText(currentLabel)}.`;\n }\n }\n\n if (isDecreaseKey(key)) {\n if (\n previousValue !== undefined &&\n currentValue <= previousValue + minRange\n ) {\n return `The ${getSliderTypeText(\n currentLabel\n )} has reached its limit. Decrease the ${getSliderTargetText(\n previousLabel\n )} to set a lower ${getSliderTargetText(currentLabel)}.`;\n }\n }\n\n return \"\";\n }\n\n function getThumbMin(index: number, values = selection) {\n if (index === 0) {\n return min;\n }\n\n return values[index - 1] + minRange;\n }\n\n function getThumbMax(index: number, values = selection) {\n if (index === values.length - 1) {\n return max;\n }\n\n return values[index + 1] - minRange;\n }\n\n function clampValueForThumb(\n nextValue: number,\n index: number,\n values: number[]\n ) {\n const minAllowed = getThumbMin(index, values);\n const maxAllowed = getThumbMax(index, values);\n\n return Math.min(maxAllowed, Math.max(minAllowed, nextValue));\n }\n\n // =========================================================================\n // RENDER FUNCTIONS\n // =========================================================================\n const formatLabel = (value: number) => {\n if (renderSliderLabel) {\n return renderSliderLabel(value);\n }\n\n return (\n <Typography.BodyBL className={styles.labelText}>\n {sliderLabelPrefix}\n {value}\n {sliderLabelSuffix}\n </Typography.BodyBL>\n );\n };\n\n const formatIndicationLabel = () => {\n let formattedSelection = \"\";\n\n if (selection.length === 1) {\n formattedSelection = `${selection[0]}`;\n } else if (selection.length === 2) {\n formattedSelection = `${selection[0]} - ${selection[1]}`;\n } else if (selection.length > 2) {\n const minValue = Math.min(...selection);\n const maxValue = Math.max(...selection);\n formattedSelection = `${minValue} - ${maxValue}`;\n }\n\n return (\n <Typography.BodyBL className={styles.labelText}>\n {indicatorLabelPrefix}\n {formattedSelection}\n {indicatorLabelSuffix}\n </Typography.BodyBL>\n );\n };\n\n return (\n <div\n {...otherProps}\n id={id}\n role=\"group\"\n aria-labelledby={ariaLabelledBy}\n aria-disabled={disabled}\n className={clsx(styles.wrapper, className)}\n >\n {!disabled && !readOnly && (\n <VisuallyHidden id={instructionTextId}>\n Use left and right arrow keys to adjust the slider.\n </VisuallyHidden>\n )}\n\n {showIndicatorLabel && (\n <div\n id={indicatorTextId}\n className={styles.indicatorLabelContainer}\n >\n {formatIndicationLabel()}\n </div>\n )}\n\n {selection.map((thumbValue, index) => {\n const thumbLabelTextId = `${internalId}-thumb-label-${index}`;\n const thumbDescriptionText = getThumbDescriptionText(index);\n const thumbDescriptionTextId = thumbDescriptionText\n ? `${internalId}-thumb-description-${index}`\n : undefined;\n\n return (\n <VisuallyHidden key={`native-slider-${index}`}>\n {thumbDescriptionTextId && (\n <span id={thumbDescriptionTextId}>\n {thumbDescriptionText}\n </span>\n )}\n <span id={thumbLabelTextId}>\n {resolvedAriaLabels[index]}\n </span>\n\n <input\n data-testid={`slider-input-${index}`}\n type=\"range\"\n min={getThumbMin(index)}\n max={getThumbMax(index)}\n step={step}\n value={thumbValue}\n aria-disabled={disabled || undefined}\n readOnly={readOnly}\n aria-labelledby={concatIds(\n ariaLabelledBy,\n thumbLabelTextId\n )}\n aria-describedby={getThumbDescriptionIds(\n thumbDescriptionTextId\n )}\n aria-valuetext={getValueText(thumbValue)}\n aria-invalid={ariaInvalid}\n onFocus={() => setFocusedThumbIndex(index)}\n onBlur={() => setFocusedThumbIndex(null)}\n onChange={(event) =>\n handleNativeRangeChange(event, index)\n }\n onKeyDown={(event) => {\n handleThumbKeyDown(event, index);\n }}\n />\n </VisuallyHidden>\n );\n })}\n\n {/* Native range inputs provide the accessible interaction model.\n The visible react-slider is presentation-only. */}\n <ReactSlider\n step={step}\n min={min}\n max={max}\n value={selection}\n disabled={disabled || readOnly}\n onChange={handleChange}\n onAfterChange={handleChangeEnd}\n minDistance={minRange}\n aria-hidden\n className={styles.slider}\n renderThumb={(thumbProps, state) => {\n return (\n <Thumb\n data-testid={`slider-thumb-${state.index}`}\n {...thumbProps}\n key={thumbProps.key}\n tabIndex={-1}\n aria-hidden\n focused={focusedThumbIndex === state.index}\n disabled={disabled}\n readOnly={readOnly}\n />\n );\n }}\n renderTrack={(trackProps, state) => {\n return (\n <Track\n data-testid={`slider-track-${state.index}`}\n {...trackProps}\n key={trackProps.key}\n color={trackColors[state.index]}\n />\n );\n }}\n />\n\n {showSliderLabels && (\n <div className={styles.labelContainer}>\n <div>{formatLabel(min)}</div>\n <div>{formatLabel(max)}</div>\n </div>\n )}\n </div>\n );\n};\n"],"names":["InputRangeSlider","_a","id","value","min","max","step","minRange","numOfThumbs","colors","disabled","readOnly","showSliderLabels","sliderLabelPrefix","sliderLabelSuffix","showIndicatorLabel","indicatorLabelPrefix","indicatorLabelSuffix","ariaLabels","ariaDescriptions","ariaInvalid","ariaLabelledBy","ariaDescribedBy","renderSliderLabel","onChange","onChangeEnd","className","otherProps","__rest","selection","setSelection","useState","initialiseSelection","focusedThumbIndex","setFocusedThumbIndex","internalId","useId","trackColors","defaultColors","inactiveColor","Colour","activeColor","index","push","getDefaultColors","Array","fill","map","_","i","getTrackColors","indicatorTextId","instructionTextId","resolvedAriaLabels","from","length","getDefaultAriaLabel","useEffect","handleThumbKeyDown","event","message","key","currentValue","previousValue","undefined","nextValue","currentLabel","previousLabel","nextLabel","includes","isIncreaseKey","getSliderTypeText","getSliderTargetText","isDecreaseKey","getBlockedMovementMessage","clearAnnouncer","announce","handleNativeRangeChange","nextRawValue","Number","currentTarget","nextSelection","values","minAllowed","getThumbMin","maxAllowed","getThumbMax","Math","clampValueForThumb","label","toLowerCase","replace","formatLabel","_jsxs","Typography","BodyBL","styles.labelText","children","Object","assign","role","clsx","styles.wrapper","_jsx","VisuallyHidden","styles.indicatorLabelContainer","formattedSelection","formatIndicationLabel","thumbValue","thumbLabelTextId","thumbDescriptionText","getThumbDescriptionText","thumbDescriptionTextId","type","concatIds","thumbDescriptionId","onFocus","onBlur","onKeyDown","ReactSlider","onAfterChange","val","newSelection","minDistance","styles.slider","renderThumb","thumbProps","state","_createElement","Thumb","tabIndex","focused","renderTrack","trackProps","Track","color","styles.labelContainer"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"input-slider.js","sources":["../../src/input-slider/input-slider.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\nimport { InputRangeSlider } from \"../input-range-slider\";\nimport type { InputSliderProps } from \"./types\";\n\n// @catalog\n/**\n * A single-thumb slider for selecting one numeric value within a range.\n *\n * Use `InputSlider` when a user needs to pick a single point on a continuous\n * scale without a precise value. For selecting a range between two values, use `InputRangeSlider` instead.\n */\nexport const InputSlider = ({\n value,\n ariaLabel,\n onChange,\n onChangeEnd,\n ...otherProps\n}: InputSliderProps) => {\n // =========================================================================\n // CONST, STATE, REF\n // =========================================================================\n const [selection, setSelection] = useState<number[]>(initialiseSelection());\n\n // =========================================================================\n // EFFECTS\n // =========================================================================\n useEffect(() => {\n if (value !== selection[0]) {\n setSelection(initialiseSelection());\n }\n }, [value]);\n\n // =========================================================================\n // EVENT HANDLERS\n // =========================================================================\n const handleChange = (value: number[]) => {\n const [newVal] = value;\n setSelection([newVal]);\n onChange?.(newVal);\n };\n\n const handleChangeEnd = (value: number[]) => {\n const [newVal] = value;\n setSelection([newVal]);\n onChangeEnd?.(newVal);\n };\n\n // =========================================================================\n // HELPER FUNCTIONS\n // =========================================================================\n function initialiseSelection() {\n return value != undefined ? [value] : [0];\n }\n\n return (\n <InputRangeSlider\n {...otherProps}\n value={selection}\n numOfThumbs={1}\n onChange={handleChange}\n onChangeEnd={handleChangeEnd}\n ariaLabels={ariaLabel ? [ariaLabel] : undefined}\n />\n );\n};\n"],"names":["InputSlider","_a","value","ariaLabel","onChange","onChangeEnd","otherProps","__rest","selection","setSelection","useState","initialiseSelection","useEffect","undefined","_jsx","InputRangeSlider","Object","assign","numOfThumbs","newVal","ariaLabels"],"mappings":"sNAYO,MAAMA,EAAeC,IAAA,IAAAC,MACxBA,EAAKC,UACLA,EAASC,SACTA,EAAQC,YACRA,GAAWJ,EACRK,EAAUC,EAAAN,EALW,CAAA,QAAA,YAAA,WAAA,gBAUxB,MAAOO,EAAWC,GAAgBC,EAAmBC,KAKrDC,GAAU,KACFV,IAAUM,EAAU,IACpBC,EAAaE,IACjB,GACD,CAACT,IAoBJ,SAASS,IACL,OAAgBE,MAATX,EAAqB,CAACA,GAAS,CAAC,EAC3C,CAEA,OACIY,EAACC,EAAgBC,OAAAC,OAAA,CAAA,EACTX,GACJJ,MAAOM,EACPU,YAAa,EACbd,SAxBcF,IAClB,MAAOiB,GAAUjB,EACjBO,EAAa,CAACU,IACdf,SAAAA,EAAWe,EAAO,EAsBdd,YAnBiBH,IACrB,MAAOiB,GAAUjB,EACjBO,EAAa,CAACU,IACdd,SAAAA,EAAcc,EAAO,EAiBjBC,WAAYjB,EAAY,CAACA,QAAaU,IACxC"}
1
+ {"version":3,"file":"input-slider.js","sources":["../../src/input-slider/input-slider.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\nimport { InputRangeSlider } from \"../input-range-slider\";\nimport type { InputSliderProps } from \"./types\";\n\n// @catalog\n/**\n * A single-thumb slider for selecting one numeric value within a range.\n *\n * Use `InputSlider` when a user needs to pick a single point on a continuous\n * scale without a precise value. For selecting a range between two values, use `InputRangeSlider` instead.\n */\nexport const InputSlider = ({\n value,\n ariaLabel,\n onChange,\n onChangeEnd,\n ...otherProps\n}: InputSliderProps) => {\n // =========================================================================\n // CONST, STATE, REF\n // =========================================================================\n const [selection, setSelection] = useState<number[]>(initialiseSelection());\n\n // =========================================================================\n // EFFECTS\n // =========================================================================\n useEffect(() => {\n if (value !== selection[0]) {\n setSelection(initialiseSelection());\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [value]);\n\n // =========================================================================\n // EVENT HANDLERS\n // =========================================================================\n const handleChange = (value: number[]) => {\n const [newVal] = value;\n setSelection([newVal]);\n onChange?.(newVal);\n };\n\n const handleChangeEnd = (value: number[]) => {\n const [newVal] = value;\n setSelection([newVal]);\n onChangeEnd?.(newVal);\n };\n\n // =========================================================================\n // HELPER FUNCTIONS\n // =========================================================================\n function initialiseSelection() {\n return value != undefined ? [value] : [0];\n }\n\n return (\n <InputRangeSlider\n {...otherProps}\n value={selection}\n numOfThumbs={1}\n onChange={handleChange}\n onChangeEnd={handleChangeEnd}\n ariaLabels={ariaLabel ? [ariaLabel] : undefined}\n />\n );\n};\n"],"names":["InputSlider","_a","value","ariaLabel","onChange","onChangeEnd","otherProps","__rest","selection","setSelection","useState","initialiseSelection","useEffect","undefined","_jsx","InputRangeSlider","Object","assign","numOfThumbs","newVal","ariaLabels"],"mappings":"sNAYO,MAAMA,EAAeC,IAAA,IAAAC,MACxBA,EAAKC,UACLA,EAASC,SACTA,EAAQC,YACRA,GAAWJ,EACRK,EAAUC,EAAAN,EALW,CAAA,QAAA,YAAA,WAAA,gBAUxB,MAAOO,EAAWC,GAAgBC,EAAmBC,KAKrDC,GAAU,KACFV,IAAUM,EAAU,IACpBC,EAAaE,IACjB,GAED,CAACT,IAoBJ,SAASS,IACL,OAAgBE,MAATX,EAAqB,CAACA,GAAS,CAAC,EAC3C,CAEA,OACIY,EAACC,EAAgBC,OAAAC,OAAA,CAAA,EACTX,GACJJ,MAAOM,EACPU,YAAa,EACbd,SAxBcF,IAClB,MAAOiB,GAAUjB,EACjBO,EAAa,CAACU,IACdf,SAAAA,EAAWe,EAAO,EAsBdd,YAnBiBH,IACrB,MAAOiB,GAAUjB,EACjBO,EAAa,CAACU,IACdd,SAAAA,EAAcc,EAAO,EAiBjBC,WAAYjB,EAAY,CAACA,QAAaU,IACxC"}
@@ -1 +1 @@
1
- {"version":3,"file":"textarea-counter.js","sources":["../../src/input-textarea/textarea-counter.tsx"],"sourcesContent":["import React, { useEffect, useState } from \"react\";\n\nimport * as styles from \"./textarea-counter.styles\";\n\ninterface Props {\n value: string | number | readonly string[] | undefined;\n maxLength: number;\n renderCustomCounter?: (\n maxLength: number,\n currentValueLength: number\n ) => JSX.Element;\n}\n\nexport const TextareaCounter = ({\n value,\n maxLength,\n renderCustomCounter,\n}: Props): JSX.Element => {\n // =============================================================================\n // CONST, STATE, REF\n // =============================================================================\n const [counterLabel, setCounterLabel] = useState<\n string | number | readonly string[] | JSX.Element\n >(\"\");\n\n // =============================================================================\n // EFFECTS\n // =============================================================================\n useEffect(() => {\n setCounterLabel(getCounterLabel(`${value || \"\"}`));\n }, [value, maxLength]);\n\n // =============================================================================\n // HELPER FUNCTIONS\n // =============================================================================\n const getCounterLabel = (value: string | number | readonly string[]) => {\n if (renderCustomCounter) {\n return renderCustomCounter(maxLength, value.toString().length);\n } else {\n const remainingLength = maxLength - value.toString().length;\n if (remainingLength === 1) {\n return `${remainingLength} character left`;\n } else {\n return `${remainingLength.toLocaleString()} characters left`;\n }\n }\n };\n\n // =============================================================================\n // RENDER FUNCTIONS\n // =============================================================================\n return (\n <>\n {React.isValidElement(counterLabel) ? (\n counterLabel\n ) : (\n <p className={styles.counterLabel} data-testid=\"counter-label\">\n {counterLabel}\n </p>\n )}\n </>\n );\n};\n"],"names":["TextareaCounter","value","maxLength","renderCustomCounter","counterLabel","setCounterLabel","useState","useEffect","getCounterLabel","toString","length","remainingLength","toLocaleString","_jsx","_Fragment","children","React","isValidElement","className","styles.counterLabel"],"mappings":"qKAaO,MAAMA,EAAkB,EAC3BC,QACAC,YACAC,0BAKA,MAAOC,EAAcC,GAAmBC,EAEtC,IAKFC,GAAU,KACNF,EAAgBG,EAAgB,GAAGP,GAAS,MAAM,GACnD,CAACA,EAAOC,IAKX,MAAMM,EAAmBP,IACrB,GAAIE,EACA,OAAOA,EAAoBD,EAAWD,EAAMQ,WAAWC,QACpD,CACH,MAAMC,EAAkBT,EAAYD,EAAMQ,WAAWC,OACrD,OAAwB,IAApBC,EACO,GAAGA,mBAEH,GAAGA,EAAgBC,kCAElC,GAMJ,OACIC,EAAAC,EAAA,CAAAC,SACKC,EAAMC,eAAeb,GAAa,EAG/BS,EAAA,IAAA,CAAGK,UAAWC,EAAmB,cAAc,gBAAeJ,SACzDX,KAGV"}
1
+ {"version":3,"file":"textarea-counter.js","sources":["../../src/input-textarea/textarea-counter.tsx"],"sourcesContent":["import React, { useEffect, useState } from \"react\";\n\nimport * as styles from \"./textarea-counter.styles\";\n\ninterface Props {\n value: string | number | readonly string[] | undefined;\n maxLength: number;\n renderCustomCounter?: (\n maxLength: number,\n currentValueLength: number\n ) => JSX.Element;\n}\n\nexport const TextareaCounter = ({\n value,\n maxLength,\n renderCustomCounter,\n}: Props): JSX.Element => {\n // =============================================================================\n // CONST, STATE, REF\n // =============================================================================\n const [counterLabel, setCounterLabel] = useState<\n string | number | readonly string[] | JSX.Element\n >(\"\");\n\n // =============================================================================\n // EFFECTS\n // =============================================================================\n useEffect(() => {\n setCounterLabel(getCounterLabel(`${value || \"\"}`));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [value, maxLength]);\n\n // =============================================================================\n // HELPER FUNCTIONS\n // =============================================================================\n const getCounterLabel = (value: string | number | readonly string[]) => {\n if (renderCustomCounter) {\n return renderCustomCounter(maxLength, value.toString().length);\n } else {\n const remainingLength = maxLength - value.toString().length;\n if (remainingLength === 1) {\n return `${remainingLength} character left`;\n } else {\n return `${remainingLength.toLocaleString()} characters left`;\n }\n }\n };\n\n // =============================================================================\n // RENDER FUNCTIONS\n // =============================================================================\n return (\n <>\n {React.isValidElement(counterLabel) ? (\n counterLabel\n ) : (\n <p className={styles.counterLabel} data-testid=\"counter-label\">\n {counterLabel}\n </p>\n )}\n </>\n );\n};\n"],"names":["TextareaCounter","value","maxLength","renderCustomCounter","counterLabel","setCounterLabel","useState","useEffect","getCounterLabel","toString","length","remainingLength","toLocaleString","_jsx","_Fragment","children","React","isValidElement","className","styles.counterLabel"],"mappings":"qKAaO,MAAMA,EAAkB,EAC3BC,QACAC,YACAC,0BAKA,MAAOC,EAAcC,GAAmBC,EAEtC,IAKFC,GAAU,KACNF,EAAgBG,EAAgB,GAAGP,GAAS,MAAM,GAEnD,CAACA,EAAOC,IAKX,MAAMM,EAAmBP,IACrB,GAAIE,EACA,OAAOA,EAAoBD,EAAWD,EAAMQ,WAAWC,QACpD,CACH,MAAMC,EAAkBT,EAAYD,EAAMQ,WAAWC,OACrD,OAAwB,IAApBC,EACO,GAAGA,mBAEH,GAAGA,EAAgBC,kCAElC,GAMJ,OACIC,EAAAC,EAAA,CAAAC,SACKC,EAAMC,eAAeb,GAAa,EAG/BS,EAAA,IAAA,CAAGK,UAAWC,EAAmB,cAAc,gBAAeJ,SACzDX,KAGV"}
@@ -1,2 +1,2 @@
1
- import{__rest as e}from"../_virtual/_tslib.js";import{jsx as t,jsxs as r}from"react/jsx-runtime";import a from"clsx";import n,{useState as s,useEffect as l}from"react";import{element as o,elementReadOnly as i,elementDisabled as u,elementError as g,wrapper as m}from"./textarea.styles.js";import{TextareaCounter as c}from"./textarea-counter.js";const f=n.forwardRef(((r,n)=>{var{className:m,disabled:c,error:f,maxLength:h,onChange:d,prefix:v,readOnly:x,rows:p=5,transformValue:b,value:C}=r,j=e(r,["className","disabled","error","maxLength","onChange","prefix","readOnly","rows","transformValue","value"]);const[w,O]=s(C);l((()=>{O(C)}),[C]);return t("textarea",Object.assign({ref:n,disabled:c,value:v?v+(null!=w?w:""):w,onChange:e=>{let t=e.target.value;if(v){t.startsWith(v)||(t=v+t.trimStart()),t.length<v.length&&(t=v);if((e.target.selectionStart||0)<v.length)return void e.preventDefault();const r=t.slice(v.length),a=b?b(r):r;if(O(a),e.target.value=v+a,d){const t=Object.assign(Object.assign({},e),{target:Object.assign(Object.assign({},e.target),{value:a})});d(t)}}else{const r=b?b(null!=t?t:""):t;O(r),e.target.value=r,d&&d(e)}},onKeyDown:e=>{if(!v)return;const{selectionStart:t}=e.currentTarget;"Backspace"===e.key&&t<=v.length&&e.preventDefault(),"ArrowLeft"===e.key&&t<=v.length&&e.preventDefault(),"Home"===e.key&&(e.preventDefault(),requestAnimationFrame((()=>{e.currentTarget.setSelectionRange(v.length,v.length)})))},readOnly:x,className:a(o,x&&i||c&&u||f&&g,m),rows:p,maxLength:v&&h?v.length+h:h},j))})),h=n.forwardRef(((a,n)=>{var{value:o,disabled:i,rows:u=5,onChange:g,transformValue:h,prefix:d,maxLength:v,renderCustomCounter:x}=a,p=e(a,["value","disabled","rows","onChange","transformValue","prefix","maxLength","renderCustomCounter"]);const[b,C]=s(o);l((()=>{C(o)}),[o]);return r("div",{className:m,children:[t(f,Object.assign({ref:n,disabled:i,value:b,rows:u||5,onChange:e=>{const t=e.target.value;C(t),g&&g(e)},prefix:d,transformValue:h,maxLength:v},p)),v&&t(c,{value:b,maxLength:v,renderCustomCounter:x})]})}));export{h as Textarea,f as TextareaBase};
1
+ import{__rest as e}from"../_virtual/_tslib.js";import{jsx as r,jsxs as t}from"react/jsx-runtime";import a from"clsx";import n,{useState as s,useEffect as o}from"react";import{FormErrorMessage as l}from"../form/form-label/form-label.js";import{element as i,elementReadOnly as m,elementDisabled as g,elementError as u,wrapper as c,labelContainer as f,errorMessage as d}from"./textarea.styles.js";import{TextareaCounter as h}from"./textarea-counter.js";const v=n.forwardRef(((t,n)=>{var{className:l,disabled:c,error:f,maxLength:d,onChange:h,prefix:v,readOnly:p,rows:x=5,transformValue:b,value:C}=t,j=e(t,["className","disabled","error","maxLength","onChange","prefix","readOnly","rows","transformValue","value"]);const[w,O]=s(C);o((()=>{O(C)}),[C]);return r("textarea",Object.assign({ref:n,disabled:c,value:v?v+(null!=w?w:""):w,onChange:e=>{let r=e.target.value;if(v){r.startsWith(v)||(r=v+r.trimStart()),r.length<v.length&&(r=v);if((e.target.selectionStart||0)<v.length)return void e.preventDefault();const t=r.slice(v.length),a=b?b(t):t;if(O(a),e.target.value=v+a,h){const r=Object.assign(Object.assign({},e),{target:Object.assign(Object.assign({},e.target),{value:a})});h(r)}}else{const t=b?b(null!=r?r:""):r;O(t),e.target.value=t,h&&h(e)}},onKeyDown:e=>{if(!v)return;const{selectionStart:r}=e.currentTarget;"Backspace"===e.key&&r<=v.length&&e.preventDefault(),"ArrowLeft"===e.key&&r<=v.length&&e.preventDefault(),"Home"===e.key&&(e.preventDefault(),requestAnimationFrame((()=>{e.currentTarget.setSelectionRange(v.length,v.length)})))},readOnly:p,className:a(i,p&&m||c&&g||f&&u,l),rows:x,maxLength:v&&d?v.length+d:d},j))})),p=n.forwardRef(((a,n)=>{var{value:i,disabled:m,rows:g=5,onChange:u,transformValue:p,prefix:x,maxLength:b,renderCustomCounter:C,errorMessage:j,"data-error-testid":w,error:O}=a,y=e(a,["value","disabled","rows","onChange","transformValue","prefix","maxLength","renderCustomCounter","errorMessage","data-error-testid","error"]);const[L,N]=s(i);o((()=>{N(i)}),[i]);return t("div",{className:c,children:[r(v,Object.assign({ref:n,disabled:m,value:L,rows:g||5,onChange:e=>{const r=e.target.value;N(r),u&&u(e)},prefix:x,transformValue:p,maxLength:b,error:!!j||O},y)),j||b?t("div",{className:f,children:[j&&r(l,{className:d,"data-testid":w,children:j}),b&&r(h,{value:L,maxLength:b,renderCustomCounter:C})]}):null]})}));export{p as Textarea,v as TextareaBase};
2
2
  //# sourceMappingURL=textarea.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"textarea.js","sources":["../../src/input-textarea/textarea.tsx"],"sourcesContent":["import clsx from \"clsx\";\nimport React, { useEffect, useState } from \"react\";\n\nimport * as styles from \"./textarea.styles\";\nimport { TextareaCounter } from \"./textarea-counter\";\nimport type { TextareaProps, TextareaRef } from \"./types\";\n\n// =============================================================================\n// BASE COMPONENT\n// =============================================================================\nconst TextareaBaseComponent = (\n {\n className,\n disabled,\n error,\n maxLength,\n onChange,\n prefix,\n readOnly,\n rows = 5,\n transformValue,\n value,\n ...otherProps\n }: TextareaProps,\n ref: TextareaRef\n) => {\n const [stateValue, setStateValue] = useState(value);\n\n useEffect(() => {\n setStateValue(value);\n }, [value]);\n\n const handleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {\n let newValue = event.target.value;\n if (prefix) {\n // Ensure prefix is always present\n if (!newValue.startsWith(prefix)) {\n newValue = prefix + newValue.trimStart();\n }\n\n // Prevent deleting the prefix\n if (newValue.length < prefix.length) {\n newValue = prefix;\n }\n\n const cursorPosition = event.target.selectionStart || 0;\n\n // Ensure backspace does not delete the prefix\n if (cursorPosition < prefix.length) {\n event.preventDefault();\n return;\n }\n\n // Extract user input\n const userInput = newValue.slice(prefix.length);\n\n // Transform the input if needed\n const transformedValue = transformValue\n ? transformValue(userInput)\n : userInput;\n\n // Update state and input field\n setStateValue(transformedValue);\n event.target.value = prefix + transformedValue;\n\n // Pass only the user input (without prefix) to `onChange`\n if (onChange) {\n const syntheticEvent = {\n ...event,\n target: { ...event.target, value: transformedValue },\n };\n onChange(\n syntheticEvent as React.ChangeEvent<HTMLTextAreaElement>\n );\n }\n } else {\n const transformedValue = transformValue\n ? transformValue(newValue ?? \"\")\n : newValue;\n\n setStateValue(transformedValue);\n event.target.value = transformedValue;\n if (onChange) onChange(event);\n }\n };\n\n const getDisplayValue = () => {\n return prefix ? prefix + (stateValue ?? \"\") : stateValue;\n };\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (!prefix) return;\n\n const { selectionStart } = event.currentTarget;\n\n // Prevent backspace in prefix\n if (event.key === \"Backspace\" && selectionStart <= prefix.length) {\n event.preventDefault();\n }\n\n // Prevent left arrow from moving into prefix\n if (event.key === \"ArrowLeft\" && selectionStart <= prefix.length) {\n event.preventDefault();\n }\n\n // Prevent \"Home\" key from moving cursor to beginning\n if (event.key === \"Home\") {\n event.preventDefault();\n requestAnimationFrame(() => {\n event.currentTarget.setSelectionRange(\n prefix.length,\n prefix.length\n );\n });\n }\n };\n\n return (\n <textarea\n ref={ref}\n disabled={disabled}\n value={getDisplayValue()}\n onChange={handleChange}\n onKeyDown={handleKeyDown}\n readOnly={readOnly}\n className={clsx(\n styles.element,\n (readOnly && styles.elementReadOnly) ||\n (disabled && styles.elementDisabled) ||\n (error && styles.elementError),\n className\n )}\n rows={rows}\n maxLength={\n prefix && maxLength ? prefix.length + maxLength : maxLength\n }\n {...otherProps}\n />\n );\n};\n\nexport const TextareaBase = React.forwardRef(TextareaBaseComponent);\n\n// =============================================================================\n// MODULE COMPONENT (comes with the counter)\n// =============================================================================\n\nconst TextareaComponent = (\n {\n value,\n disabled,\n rows = 5,\n onChange,\n transformValue,\n prefix,\n maxLength,\n renderCustomCounter,\n ...otherProps\n }: TextareaProps,\n ref: TextareaRef\n) => {\n // -------------------------------------------------------------------------\n // CONST, STATE, REF\n // -------------------------------------------------------------------------\n const [stateValue, setStateValue] = useState<\n string | number | readonly string[] | undefined\n >(value);\n\n // -------------------------------------------------------------------------\n // EFFECTS\n // -------------------------------------------------------------------------\n useEffect(() => {\n setStateValue(value);\n }, [value]);\n\n // -------------------------------------------------------------------------\n // EVENT HANDLER\n // -------------------------------------------------------------------------\n const handleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {\n const newValue = event.target.value;\n setStateValue(newValue);\n if (onChange) onChange(event);\n };\n\n // -------------------------------------------------------------------------\n // RENDER FUNCTIONS\n // -------------------------------------------------------------------------\n return (\n <div className={styles.wrapper}>\n <TextareaBase\n ref={ref}\n disabled={disabled}\n value={stateValue}\n rows={rows || 5}\n onChange={handleChange}\n prefix={prefix}\n transformValue={transformValue}\n maxLength={maxLength}\n {...otherProps}\n />\n {maxLength && (\n <TextareaCounter\n value={stateValue}\n maxLength={maxLength}\n renderCustomCounter={renderCustomCounter}\n />\n )}\n </div>\n );\n};\n\n// @catalog\n/**\n * A multi-line text input with an optional character counter and prefix.\n */\nexport const Textarea = React.forwardRef(TextareaComponent);\n"],"names":["TextareaBase","React","forwardRef","_a","ref","className","disabled","error","maxLength","onChange","prefix","readOnly","rows","transformValue","value","otherProps","__rest","stateValue","setStateValue","useState","useEffect","_jsx","Object","assign","event","newValue","target","startsWith","trimStart","length","selectionStart","preventDefault","userInput","slice","transformedValue","syntheticEvent","onKeyDown","currentTarget","key","requestAnimationFrame","setSelectionRange","clsx","styles.element","styles.elementReadOnly","styles.elementDisabled","styles.elementError","Textarea","renderCustomCounter","_jsxs","styles.wrapper","children","TextareaCounter"],"mappings":"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"}
1
+ {"version":3,"file":"textarea.js","sources":["../../src/input-textarea/textarea.tsx"],"sourcesContent":["import clsx from \"clsx\";\nimport React, { useEffect, useState } from \"react\";\n\nimport { FormErrorMessage } from \"../form/form-label\";\nimport * as styles from \"./textarea.styles\";\nimport { TextareaCounter } from \"./textarea-counter\";\nimport type { TextareaProps, TextareaRef } from \"./types\";\n\n// =============================================================================\n// BASE COMPONENT\n// =============================================================================\nconst TextareaBaseComponent = (\n {\n className,\n disabled,\n error,\n maxLength,\n onChange,\n prefix,\n readOnly,\n rows = 5,\n transformValue,\n value,\n ...otherProps\n }: TextareaProps,\n ref: TextareaRef\n) => {\n const [stateValue, setStateValue] = useState(value);\n\n useEffect(() => {\n setStateValue(value);\n }, [value]);\n\n const handleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {\n let newValue = event.target.value;\n if (prefix) {\n // Ensure prefix is always present\n if (!newValue.startsWith(prefix)) {\n newValue = prefix + newValue.trimStart();\n }\n\n // Prevent deleting the prefix\n if (newValue.length < prefix.length) {\n newValue = prefix;\n }\n\n const cursorPosition = event.target.selectionStart || 0;\n\n // Ensure backspace does not delete the prefix\n if (cursorPosition < prefix.length) {\n event.preventDefault();\n return;\n }\n\n // Extract user input\n const userInput = newValue.slice(prefix.length);\n\n // Transform the input if needed\n const transformedValue = transformValue\n ? transformValue(userInput)\n : userInput;\n\n // Update state and input field\n setStateValue(transformedValue);\n event.target.value = prefix + transformedValue;\n\n // Pass only the user input (without prefix) to `onChange`\n if (onChange) {\n const syntheticEvent = {\n ...event,\n target: { ...event.target, value: transformedValue },\n };\n onChange(\n syntheticEvent as React.ChangeEvent<HTMLTextAreaElement>\n );\n }\n } else {\n const transformedValue = transformValue\n ? transformValue(newValue ?? \"\")\n : newValue;\n\n setStateValue(transformedValue);\n event.target.value = transformedValue;\n if (onChange) onChange(event);\n }\n };\n\n const getDisplayValue = () => {\n return prefix ? prefix + (stateValue ?? \"\") : stateValue;\n };\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (!prefix) return;\n\n const { selectionStart } = event.currentTarget;\n\n // Prevent backspace in prefix\n if (event.key === \"Backspace\" && selectionStart <= prefix.length) {\n event.preventDefault();\n }\n\n // Prevent left arrow from moving into prefix\n if (event.key === \"ArrowLeft\" && selectionStart <= prefix.length) {\n event.preventDefault();\n }\n\n // Prevent \"Home\" key from moving cursor to beginning\n if (event.key === \"Home\") {\n event.preventDefault();\n requestAnimationFrame(() => {\n event.currentTarget.setSelectionRange(\n prefix.length,\n prefix.length\n );\n });\n }\n };\n\n return (\n <textarea\n ref={ref}\n disabled={disabled}\n value={getDisplayValue()}\n onChange={handleChange}\n onKeyDown={handleKeyDown}\n readOnly={readOnly}\n className={clsx(\n styles.element,\n (readOnly && styles.elementReadOnly) ||\n (disabled && styles.elementDisabled) ||\n (error && styles.elementError),\n className\n )}\n rows={rows}\n maxLength={\n prefix && maxLength ? prefix.length + maxLength : maxLength\n }\n {...otherProps}\n />\n );\n};\n\nexport const TextareaBase = React.forwardRef(TextareaBaseComponent);\n\n// =============================================================================\n// MODULE COMPONENT (comes with the counter)\n// =============================================================================\n\nconst TextareaComponent = (\n {\n value,\n disabled,\n rows = 5,\n onChange,\n transformValue,\n prefix,\n maxLength,\n renderCustomCounter,\n errorMessage,\n \"data-error-testid\": errorTestId,\n error,\n ...otherProps\n }: TextareaProps,\n ref: TextareaRef\n) => {\n // -------------------------------------------------------------------------\n // CONST, STATE, REF\n // -------------------------------------------------------------------------\n const [stateValue, setStateValue] = useState<\n string | number | readonly string[] | undefined\n >(value);\n\n // -------------------------------------------------------------------------\n // EFFECTS\n // -------------------------------------------------------------------------\n useEffect(() => {\n setStateValue(value);\n }, [value]);\n\n // -------------------------------------------------------------------------\n // EVENT HANDLER\n // -------------------------------------------------------------------------\n const handleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {\n const newValue = event.target.value;\n setStateValue(newValue);\n if (onChange) onChange(event);\n };\n\n // -------------------------------------------------------------------------\n // RENDER FUNCTIONS\n // -------------------------------------------------------------------------\n const renderBottomLabels = () => {\n if (!errorMessage && !maxLength) return null;\n return (\n <div className={styles.labelContainer}>\n {errorMessage && (\n <FormErrorMessage\n className={styles.errorMessage}\n data-testid={errorTestId}\n >\n {errorMessage}\n </FormErrorMessage>\n )}\n {maxLength && (\n <TextareaCounter\n value={stateValue}\n maxLength={maxLength}\n renderCustomCounter={renderCustomCounter}\n />\n )}\n </div>\n );\n };\n\n return (\n <div className={styles.wrapper}>\n <TextareaBase\n ref={ref}\n disabled={disabled}\n value={stateValue}\n rows={rows || 5}\n onChange={handleChange}\n prefix={prefix}\n transformValue={transformValue}\n maxLength={maxLength}\n error={!!errorMessage || error}\n {...otherProps}\n />\n {renderBottomLabels()}\n </div>\n );\n};\n\n// @catalog\n/**\n * A multi-line text input with an optional character counter and prefix.\n */\nexport const Textarea = React.forwardRef(TextareaComponent);\n"],"names":["TextareaBase","React","forwardRef","_a","ref","className","disabled","error","maxLength","onChange","prefix","readOnly","rows","transformValue","value","otherProps","__rest","stateValue","setStateValue","useState","useEffect","_jsx","Object","assign","event","newValue","target","startsWith","trimStart","length","selectionStart","preventDefault","userInput","slice","transformedValue","syntheticEvent","onKeyDown","currentTarget","key","requestAnimationFrame","setSelectionRange","clsx","styles.element","styles.elementReadOnly","styles.elementDisabled","styles.elementError","Textarea","renderCustomCounter","errorMessage","errorTestId","_jsxs","styles.wrapper","children","styles.labelContainer","FormErrorMessage","styles.errorMessage","TextareaCounter"],"mappings":"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"}
@@ -3,3 +3,5 @@ export declare const element: import("@linaria/core").LinariaClassName;
3
3
  export declare const elementReadOnly: import("@linaria/core").LinariaClassName;
4
4
  export declare const elementDisabled: import("@linaria/core").LinariaClassName;
5
5
  export declare const elementError: import("@linaria/core").LinariaClassName;
6
+ export declare const labelContainer: import("@linaria/core").LinariaClassName;
7
+ export declare const errorMessage: import("@linaria/core").LinariaClassName;
@@ -1,2 +1,2 @@
1
- import"./textarea.styles_q77m5x.css";const t="w1xqauhq",e="e10c6rk9",a="e1kovz0t",r="e9uwuyt",s="etmlvbn";export{e as element,r as elementDisabled,s as elementError,a as elementReadOnly,t as wrapper};
1
+ import"./textarea.styles_6we7bs.css";const e="w1xqauhq",t="e10c6rk9",s="e1kovz0t",a="e9uwuyt",r="etmlvbn",w="l1npkv4w",n="e1mvywhi";export{t as element,a as elementDisabled,r as elementError,s as elementReadOnly,n as errorMessage,w as labelContainer,e as wrapper};
2
2
  //# sourceMappingURL=textarea.styles.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"textarea.styles.js","sources":["../../src/input-textarea/textarea.styles.ts"],"sourcesContent":["import { css } from \"@linaria/core\";\n\nimport { Border, Colour, Font, Radius, Spacing } from \"../theme\";\n\nexport const wrapper = css`\n display: flex;\n flex-direction: column;\n gap: ${Spacing[\"spacing-8\"]};\n`;\n\nexport const element = css`\n border: ${Border[\"width-010\"]} ${Border[\"solid\"]} ${Colour[\"border\"]};\n border-radius: ${Radius[\"sm\"]};\n background: ${Colour.bg};\n outline: none;\n overflow: auto;\n\n display: block;\n padding: ${Spacing[\"spacing-12\"]} ${Spacing[\"spacing-16\"]};\n width: 100%;\n\n ${Font[\"body-baseline-regular\"]}\n color: ${Colour[\"text\"]};\n\n &:focus,\n &:active {\n outline-offset: -1px;\n outline: ${Border[\"width-020\"]} ${Border[\"solid\"]}\n ${Colour[\"border-focus\"]};\n }\n\n &::placeholder,\n &::-webkit-input-placeholder {\n color: ${Colour[\"text-subtler\"]};\n }\n\n &::-webkit-scrollbar {\n width: 14px;\n }\n\n &::-webkit-scrollbar-track {\n background: transparent;\n }\n\n &::-webkit-scrollbar-thumb {\n background: ${Colour[\"bg-inverse-subtlest\"]};\n border: 5px solid transparent;\n border-radius: ${Radius[\"full\"]};\n background-clip: padding-box;\n }\n`;\n\nexport const elementReadOnly = css`\n border-color: transparent;\n background: transparent !important;\n padding: ${Spacing[\"spacing-12\"]} 0;\n\n &:focus,\n &:active {\n outline-color: ${Colour[\"border-focus\"]};\n }\n`;\n\nexport const elementDisabled = css`\n background: ${Colour[\"bg-disabled\"]};\n cursor: not-allowed;\n\n &:focus,\n &:active {\n outline-color: ${Colour[\"border-disabled\"]};\n }\n`;\n\nexport const elementError = css`\n border-color: ${Colour[\"border-error\"]};\n\n &:focus,\n &:active {\n outline-color: ${Colour[\"border-error-focus\"]};\n }\n`;\n"],"names":["wrapper","element","elementReadOnly","elementDisabled","elementError"],"mappings":"mEAIO,MAAMA,EAAO,WAMPC,EAAO,WA0CPC,EAAe,WAWfC,EAAe,UAUfC,EAAY"}
1
+ {"version":3,"file":"textarea.styles.js","sources":["../../src/input-textarea/textarea.styles.ts"],"sourcesContent":["import { css } from \"@linaria/core\";\n\nimport { Border, Colour, Font, Radius, Spacing } from \"../theme\";\n\nexport const wrapper = css`\n display: flex;\n flex-direction: column;\n gap: ${Spacing[\"spacing-8\"]};\n`;\n\nexport const element = css`\n border: ${Border[\"width-010\"]} ${Border[\"solid\"]} ${Colour[\"border\"]};\n border-radius: ${Radius[\"sm\"]};\n background: ${Colour.bg};\n outline: none;\n overflow: auto;\n\n display: block;\n padding: ${Spacing[\"spacing-12\"]} ${Spacing[\"spacing-16\"]};\n width: 100%;\n\n ${Font[\"body-baseline-regular\"]}\n color: ${Colour[\"text\"]};\n\n &:focus,\n &:active {\n outline-offset: -1px;\n outline: ${Border[\"width-020\"]} ${Border[\"solid\"]}\n ${Colour[\"border-focus\"]};\n }\n\n &::placeholder,\n &::-webkit-input-placeholder {\n color: ${Colour[\"text-subtler\"]};\n }\n\n &::-webkit-scrollbar {\n width: 14px;\n }\n\n &::-webkit-scrollbar-track {\n background: transparent;\n }\n\n &::-webkit-scrollbar-thumb {\n background: ${Colour[\"bg-inverse-subtlest\"]};\n border: 5px solid transparent;\n border-radius: ${Radius[\"full\"]};\n background-clip: padding-box;\n }\n`;\n\nexport const elementReadOnly = css`\n border-color: transparent;\n background: transparent !important;\n padding: ${Spacing[\"spacing-12\"]} 0;\n\n &:focus,\n &:active {\n outline-color: ${Colour[\"border-focus\"]};\n }\n`;\n\nexport const elementDisabled = css`\n background: ${Colour[\"bg-disabled\"]};\n cursor: not-allowed;\n\n &:focus,\n &:active {\n outline-color: ${Colour[\"border-disabled\"]};\n }\n`;\n\nexport const elementError = css`\n border-color: ${Colour[\"border-error\"]};\n\n &:focus,\n &:active {\n outline-color: ${Colour[\"border-error-focus\"]};\n }\n`;\n\nexport const labelContainer = css`\n display: flex;\n justify-content: flex-end;\n gap: ${Spacing[\"spacing-12\"]};\n`;\n\nexport const errorMessage = css`\n flex: 1;\n margin-top: 0;\n align-items: flex-start;\n`;\n"],"names":["wrapper","element","elementReadOnly","elementDisabled","elementError","labelContainer","errorMessage"],"mappings":"mEAIO,MAAMA,EAAO,WAMPC,EAAO,WA0CPC,EAAe,WAWfC,EAAe,UAUfC,EAAY,UASZC,EAAc,WAMdC,EAAY"}
@@ -2,4 +2,6 @@
2
2
  .e10c6rk9{border:var(--fds-border-width-010) var(--fds-border-style-solid) var(--fds-colour-border);border-radius:var(--fds-radius-sm);background:var(--fds-colour-bg);outline:none;overflow:auto;display:block;padding:var(--fds-spacing-12) var(--fds-spacing-16);width:100%;font-family:var(--fds-font-family-body);font-variant:var(--fds-font-variant);font-size:var(--fds-font-body-size-baseline);font-weight:var(--fds-font-weight-regular);line-height:var(--fds-font-body-lh-baseline);letter-spacing:var(--fds-font-body-ls-baseline);color:var(--fds-colour-text);}.e10c6rk9:focus,.e10c6rk9:active{outline-offset:-1px;outline:var(--fds-border-width-020) var(--fds-border-style-solid) var(--fds-colour-border-focus);}.e10c6rk9::-webkit-input-placeholder{color:var(--fds-colour-text-subtler);}.e10c6rk9::-webkit-scrollbar{width:14px;}.e10c6rk9::-webkit-scrollbar-track{background:transparent;}.e10c6rk9::-webkit-scrollbar-thumb{background:var(--fds-colour-bg-inverse-subtlest);border:5px solid transparent;border-radius:var(--fds-radius-full);-webkit-background-clip:padding-box;background-clip:padding-box;}.e10c6rk9::-webkit-input-placeholder{color:var(--fds-colour-text-subtler);}.e10c6rk9::-moz-placeholder{color:var(--fds-colour-text-subtler);}.e10c6rk9:-ms-input-placeholder{color:var(--fds-colour-text-subtler);}.e10c6rk9::placeholder{color:var(--fds-colour-text-subtler);}
3
3
  .e1kovz0t{border-color:transparent;background:transparent!important;padding:var(--fds-spacing-12) 0;}.e1kovz0t:focus,.e1kovz0t:active{outline-color:var(--fds-colour-border-focus);}
4
4
  .e9uwuyt{background:var(--fds-colour-bg-disabled);cursor:not-allowed;}.e9uwuyt:focus,.e9uwuyt:active{outline-color:var(--fds-colour-border-disabled);}
5
- .etmlvbn{border-color:var(--fds-colour-border-error);}.etmlvbn:focus,.etmlvbn:active{outline-color:var(--fds-colour-border-error-focus);}}
5
+ .etmlvbn{border-color:var(--fds-colour-border-error);}.etmlvbn:focus,.etmlvbn:active{outline-color:var(--fds-colour-border-error-focus);}
6
+ .l1npkv4w{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-ms-flex-pack:end;-webkit-justify-content:flex-end;justify-content:flex-end;gap:var(--fds-spacing-12);}
7
+ .e1mvywhi{-webkit-flex:1;-ms-flex:1;flex:1;margin-top:0;-webkit-align-items:flex-start;-webkit-box-align:flex-start;-ms-flex-align:flex-start;align-items:flex-start;}}
@@ -6,6 +6,13 @@ export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextArea
6
6
  /** Renders the input in an error state. */
7
7
  error?: boolean | undefined;
8
8
  "data-testid"?: string | undefined;
9
+ /**
10
+ * Inline error text rendered below the textarea.
11
+ * Also sets the input into an error state.
12
+ */
13
+ errorMessage?: string | React.ReactNode | undefined;
14
+ /** Test id for the error message element. */
15
+ "data-error-testid"?: string | undefined;
9
16
  /**
10
17
  * Transforms user input on every change event.
11
18
  */
package/modal/index.d.ts CHANGED
@@ -6,11 +6,15 @@
6
6
  *
7
7
  * Sub-components:
8
8
  * - `Modal.Box` — a styled content box with an optional close button.
9
+ *
10
+ * @deprecated Use `ModalV2` instead, which provides a more composable API, improved scroll behaviour, and proper accessibility.
9
11
  */
10
12
  export declare const Modal: (({ id, show, animationFrom, children, enableOverlayClick, rootComponentId, zIndex, onOverlayClick, dismissKeyboardOnShow, className, ...otherProps }: import("./types").ModalProps) => JSX.Element) & {
11
13
  /**
12
14
  * A styled content container for use inside `Modal`. Renders an optional
13
15
  * close button and stops click events from propagating to the overlay.
16
+ *
17
+ * @deprecated Use `ModalV2.Card` instead.
14
18
  */
15
19
  Box: (props: import("./types").ModalBoxProps & import("react").RefAttributes<HTMLDivElement>) => React.ReactElement | null;
16
20
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/modal/index.ts"],"sourcesContent":["import { Modal as Base } from \"./modal\";\nimport { ModalBox as Box } from \"./modal-box\";\n\n// @catalog\n/**\n * A full-screen overlay modal with a slide animation.\n *\n * Use `Modal` to present content that requires the user's immediate attention\n * or a focused interaction. Use `ModalV2` for built-in scroll handling and improved accessibility.\n *\n * Sub-components:\n * - `Modal.Box` — a styled content box with an optional close button.\n */\nexport const Modal = Object.assign(Base, {\n /**\n * A styled content container for use inside `Modal`. Renders an optional\n * close button and stops click events from propagating to the overlay.\n */\n Box,\n});\n\nexport * from \"./types\";\n"],"names":["Modal","Object","assign","Base","Box"],"mappings":"mFAaaA,EAAQC,OAAOC,OAAOC,EAAM,KAKrCC"}
1
+ {"version":3,"file":"index.js","sources":["../../src/modal/index.ts"],"sourcesContent":["import { Modal as Base } from \"./modal\";\nimport { ModalBox as Box } from \"./modal-box\";\n\n// @catalog\n/**\n * A full-screen overlay modal with a slide animation.\n *\n * Use `Modal` to present content that requires the user's immediate attention\n * or a focused interaction. Use `ModalV2` for built-in scroll handling and improved accessibility.\n *\n * Sub-components:\n * - `Modal.Box` — a styled content box with an optional close button.\n *\n * @deprecated Use `ModalV2` instead, which provides a more composable API, improved scroll behaviour, and proper accessibility.\n */\nexport const Modal = Object.assign(Base, {\n /**\n * A styled content container for use inside `Modal`. Renders an optional\n * close button and stops click events from propagating to the overlay.\n *\n * @deprecated Use `ModalV2.Card` instead.\n */\n Box,\n});\n\nexport * from \"./types\";\n"],"names":["Modal","Object","assign","Base","Box"],"mappings":"mFAeaA,EAAQC,OAAOC,OAAOC,EAAM,KAOrCC"}
package/modal/types.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { ModalAnimationDirection } from "../modal-v2/types";
2
2
  /**
3
3
  * Props for the `Modal` component.
4
+ * @deprecated Use `ModalV2` instead, which provides improved scroll behaviour and accessibility.
4
5
  */
5
6
  export interface ModalProps extends React.HTMLAttributes<HTMLDivElement> {
6
7
  /**
@@ -52,6 +53,7 @@ export interface ModalProps extends React.HTMLAttributes<HTMLDivElement> {
52
53
  }
53
54
  /**
54
55
  * Props for the `Modal.Box` sub-component.
56
+ * @deprecated Use `ModalV2.Card` instead.
55
57
  */
56
58
  export interface ModalBoxProps extends React.HTMLAttributes<HTMLDivElement> {
57
59
  children: React.ReactNode;
@@ -11,6 +11,7 @@ import type { ModalV2Props } from "./types";
11
11
  * - `ModalV2.CloseButton` — renders an accessible close button that invokes the modal's `onClose` callback.
12
12
  * - `ModalV2.Content` — container for the body content of the modal card.
13
13
  * - `ModalV2.Footer` — footer area for primary and secondary action buttons.
14
+ * - `ModalV2.Header` — header bar with a close button and optional title. Use instead of `CloseButton` when a heading is needed.
14
15
  */
15
16
  export declare const ModalV2: (({ id, show, onClose, animationFrom, children, enableOverlayClick, rootComponentId, zIndex, onOverlayClick, dismissKeyboardOnShow, "data-testid": testId, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, disableInitialFocus, className, ...otherProps }: ModalV2Props) => JSX.Element) & {
16
17
  /** Wraps modal content and arranges `Content`, `Footer`, and `CloseButton` slots in the correct layout. */
@@ -30,4 +31,9 @@ export declare const ModalV2: (({ id, show, onClose, animationFrom, children, en
30
31
  ({ "data-testid": testId, primaryButton, secondaryButton, className, ...otherProps }: import("./types").ModalFooterProps): import("react/jsx-runtime").JSX.Element;
31
32
  displayName: string;
32
33
  };
34
+ /** Header bar with a close button and optional title. Use instead of `CloseButton` when a heading is needed. */
35
+ Header: {
36
+ ({ "data-testid": testId, title, closeButtonPosition, className, ...otherProps }: import("./types").ModalHeaderProps): import("react/jsx-runtime").JSX.Element;
37
+ displayName: string;
38
+ };
33
39
  };
@@ -1,2 +1,2 @@
1
- import{__rest as e}from"../_virtual/_tslib.js";import{jsx as o}from"react/jsx-runtime";import{useFloating as t,useTransitionStatus as r,useDismiss as i,useInteractions as a,FloatingFocusManager as l}from"@floating-ui/react";import s from"clsx";import n,{useRef as m}from"react";import{Overlay as d}from"../overlay/overlay.js";import{useViewport as c}from"../shared/hooks/useViewport.js";import"../theme/theme-provider/index.js";import"../theme/theme-provider/context.js";import"../theme/tokens/border.js";import"../theme/tokens/colour.js";import"../theme/tokens/font.js";import"../theme/tokens/media-query.js";import{useApplyStyle as p}from"../theme/utils/use-apply-styles.js";import"../theme/utils/use-media-query.js";import"../util/calendar-helper.js";import"../external/dayjs/dayjs.min.js";import"../util/simple-id-generator.js";import"../util/string-helper.js";import"@react-aria/live-announcer";import{useEvent as u}from"../util/use-event.js";import{ModalContext as b}from"./modal-context.js";import{tokens as h,scrollContainer as j,modalContainer as v,container as f}from"./modal-v2.styles.js";import{Card as y}from"./slots/card.js";import{CloseButton as C}from"./slots/close-button.js";import{Content as g}from"./slots/content.js";import{Footer as x}from"./slots/footer.js";const O=Object.assign((y=>{var{id:C,show:g,onClose:x,animationFrom:O="bottom",children:k,enableOverlayClick:F=!0,rootComponentId:I,zIndex:w,onOverlayClick:N,dismissKeyboardOnShow:z=!0,"data-testid":P="modal","aria-label":S,"aria-labelledby":q,"aria-describedby":B,disableInitialFocus:E=!1,className:H}=y,K=e(y,["id","show","onClose","animationFrom","children","enableOverlayClick","rootComponentId","zIndex","onOverlayClick","dismissKeyboardOnShow","data-testid","aria-label","aria-labelledby","aria-describedby","disableInitialFocus","className"]);const $=u((()=>{var e,o;z&&(null===(o=null===(e=document.activeElement)||void 0===e?void 0:e.blur)||void 0===o||o.call(e))})),{verticalHeight:_}=c({enabled:g,onBeforeStart:$}),M=m(null),R=m(null),V=k&&n.cloneElement(k,{ref:M});p(R,{[h.container.verticalHeight]:null==_?null:`${_}px`});const{refs:A,context:D}=t({open:g,onOpenChange:e=>{e||null==x||x()}}),{isMounted:G,status:J}=r(D,{duration:300}),L=i(D,{outsidePress:!1,enabled:!!x}),{getFloatingProps:Q}=a([L]);return o(d,{"data-testid":`${P}-overlay`,show:g,enableOverlayClick:F,onOverlayClick:N,id:C,rootId:I,containerRef:M,zIndex:w,children:o("div",Object.assign({ref:R,"data-testid":P,"data-status":J,"data-animation-from":O},K,{className:s(f,H),children:o(b.Provider,{value:{onClose:x},children:G&&o(l,{context:D,initialFocus:E?-1:A.floating,children:o("div",{className:j,children:o("div",Object.assign({ref:A.setFloating},Q(),{role:"dialog","aria-label":S,"aria-labelledby":q,"aria-describedby":B,className:v,children:V}))})})})}))})}),{Card:y,CloseButton:C,Content:g,Footer:x});export{O as ModalV2};
1
+ import{__rest as e}from"../_virtual/_tslib.js";import{jsx as o}from"react/jsx-runtime";import{useFloating as t,useTransitionStatus as r,useDismiss as i,useInteractions as a,FloatingFocusManager as l}from"@floating-ui/react";import s from"clsx";import n,{useRef as m}from"react";import{Overlay as d}from"../overlay/overlay.js";import{useViewport as c}from"../shared/hooks/useViewport.js";import"../theme/theme-provider/index.js";import"../theme/theme-provider/context.js";import"../theme/tokens/border.js";import"../theme/tokens/colour.js";import"../theme/tokens/font.js";import"../theme/tokens/media-query.js";import{useApplyStyle as p}from"../theme/utils/use-apply-styles.js";import"../theme/utils/use-media-query.js";import"../util/calendar-helper.js";import"../external/dayjs/dayjs.min.js";import"../util/simple-id-generator.js";import"../util/string-helper.js";import"@react-aria/live-announcer";import{useEvent as u}from"../util/use-event.js";import{ModalContext as b}from"./modal-context.js";import{tokens as h,scrollContainer as j,modalContainer as v,container as f}from"./modal-v2.styles.js";import{Card as y}from"./slots/card.js";import{CloseButton as C}from"./slots/close-button.js";import{Content as g}from"./slots/content.js";import{Footer as x}from"./slots/footer.js";import{Header as O}from"./slots/header.js";const k=Object.assign((y=>{var{id:C,show:g,onClose:x,animationFrom:O="bottom",children:k,enableOverlayClick:F=!0,rootComponentId:I,zIndex:w,onOverlayClick:N,dismissKeyboardOnShow:z=!0,"data-testid":H="modal","aria-label":P,"aria-labelledby":S,"aria-describedby":q,disableInitialFocus:B=!1,className:E}=y,K=e(y,["id","show","onClose","animationFrom","children","enableOverlayClick","rootComponentId","zIndex","onOverlayClick","dismissKeyboardOnShow","data-testid","aria-label","aria-labelledby","aria-describedby","disableInitialFocus","className"]);const $=u((()=>{var e,o;z&&(null===(o=null===(e=document.activeElement)||void 0===e?void 0:e.blur)||void 0===o||o.call(e))})),{verticalHeight:_}=c({enabled:g,onBeforeStart:$}),M=m(null),R=m(null),V=k&&n.cloneElement(k,{ref:M});p(R,{[h.container.verticalHeight]:null==_?null:`${_}px`});const{refs:A,context:D}=t({open:g,onOpenChange:e=>{e||null==x||x()}}),{isMounted:G,status:J}=r(D,{duration:300}),L=i(D,{outsidePress:!1,enabled:!!x}),{getFloatingProps:Q}=a([L]);return o(d,{"data-testid":`${H}-overlay`,show:g,enableOverlayClick:F,onOverlayClick:N,id:C,rootId:I,containerRef:M,zIndex:w,children:o("div",Object.assign({ref:R,"data-testid":H,"data-status":J,"data-animation-from":O},K,{className:s(f,E),children:o(b.Provider,{value:{onClose:x},children:G&&o(l,{context:D,initialFocus:B?-1:A.floating,children:o("div",{className:j,children:o("div",Object.assign({ref:A.setFloating},Q(),{role:"dialog","aria-label":P,"aria-labelledby":S,"aria-describedby":q,className:v,children:V}))})})})}))})}),{Card:y,CloseButton:C,Content:g,Footer:x,Header:O});export{k as ModalV2};
2
2
  //# sourceMappingURL=modal-v2.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"modal-v2.js","sources":["../../src/modal-v2/modal-v2.tsx"],"sourcesContent":["import {\n FloatingFocusManager,\n useDismiss,\n useFloating,\n useInteractions,\n useTransitionStatus,\n} from \"@floating-ui/react\";\nimport clsx from \"clsx\";\nimport React, { useRef } from \"react\";\n\nimport { Overlay } from \"../overlay/overlay\";\nimport { useViewport } from \"../shared/hooks\";\nimport { useApplyStyle } from \"../theme\";\nimport { useEvent } from \"../util\";\nimport { ModalContext } from \"./modal-context\";\nimport * as styles from \"./modal-v2.styles\";\nimport { Card, CloseButton, Content, Footer } from \"./slots\";\nimport type { ModalV2Props } from \"./types\";\n\nconst Base = ({\n id,\n show,\n onClose,\n animationFrom = \"bottom\",\n children,\n enableOverlayClick = true,\n rootComponentId,\n zIndex,\n onOverlayClick,\n dismissKeyboardOnShow = true,\n \"data-testid\": testId = \"modal\",\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n \"aria-describedby\": ariaDescribedBy,\n disableInitialFocus = false,\n className,\n ...otherProps\n}: ModalV2Props): JSX.Element => {\n // =========================================================================\n // CONST, STATE, REF\n // =========================================================================\n const dismissKeyboard = useEvent(() => {\n if (dismissKeyboardOnShow) {\n (document.activeElement as HTMLElement)?.blur?.();\n }\n });\n const { verticalHeight } = useViewport({\n enabled: show,\n onBeforeStart: dismissKeyboard,\n });\n const childRef = useRef<HTMLDivElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const childWithRef =\n children && React.cloneElement(children, { ref: childRef });\n\n useApplyStyle(containerRef, {\n [styles.tokens.container.verticalHeight]:\n verticalHeight == null ? null : `${verticalHeight}px`,\n });\n\n // =========================================================================\n // FLOATING UI CONFIG\n // =========================================================================\n const { refs, context } = useFloating({\n open: show,\n onOpenChange: (isOpen) => {\n if (!isOpen) {\n onClose?.();\n }\n },\n });\n const { isMounted, status } = useTransitionStatus(context, {\n duration: 300,\n });\n const dismiss = useDismiss(context, {\n /* handled by overlayclick */\n outsidePress: false,\n enabled: !!onClose,\n });\n const { getFloatingProps } = useInteractions([dismiss]);\n\n // =========================================================================\n // RENDER FUNCTIONS\n // =========================================================================\n return (\n <Overlay\n data-testid={`${testId}-overlay`}\n show={show}\n enableOverlayClick={enableOverlayClick}\n onOverlayClick={onOverlayClick}\n id={id}\n rootId={rootComponentId}\n containerRef={childRef}\n zIndex={zIndex}\n >\n <div\n ref={containerRef}\n data-testid={testId}\n data-status={status}\n data-animation-from={animationFrom}\n {...otherProps}\n className={clsx(styles.container, className)}\n >\n <ModalContext.Provider value={{ onClose }}>\n {isMounted && (\n <FloatingFocusManager\n context={context}\n initialFocus={\n disableInitialFocus ? -1 : refs.floating\n }\n >\n <div className={styles.scrollContainer}>\n <div\n ref={refs.setFloating}\n {...getFloatingProps()}\n role=\"dialog\"\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n className={styles.modalContainer}\n >\n {childWithRef}\n </div>\n </div>\n </FloatingFocusManager>\n )}\n </ModalContext.Provider>\n </div>\n </Overlay>\n );\n};\n\n// @catalog\n/**\n * A composable modal dialog with slot-based content layout.\n *\n * Use `ModalV2` when you need a controlled overlay dialog with structured\n * content slots.\n *\n * Sub-components:\n * - `ModalV2.Card` — wraps modal content and arranges `Content`, `Footer`, and `CloseButton` slots in the correct layout.\n * - `ModalV2.CloseButton` — renders an accessible close button that invokes the modal's `onClose` callback.\n * - `ModalV2.Content` — container for the body content of the modal card.\n * - `ModalV2.Footer` — footer area for primary and secondary action buttons.\n */\nexport const ModalV2 = Object.assign(Base, {\n /** Wraps modal content and arranges `Content`, `Footer`, and `CloseButton` slots in the correct layout. */\n Card,\n /** Renders an accessible close button that invokes the modal's `onClose` callback. */\n CloseButton,\n /** Container for the body content of the modal card. */\n Content,\n /** Footer area for primary and secondary action buttons. */\n Footer,\n});\n"],"names":["ModalV2","Object","assign","_a","id","show","onClose","animationFrom","children","enableOverlayClick","rootComponentId","zIndex","onOverlayClick","dismissKeyboardOnShow","testId","ariaLabel","ariaLabelledBy","ariaDescribedBy","disableInitialFocus","className","otherProps","__rest","dismissKeyboard","useEvent","_b","document","activeElement","blur","verticalHeight","useViewport","enabled","onBeforeStart","childRef","useRef","containerRef","childWithRef","React","cloneElement","ref","useApplyStyle","styles.tokens","container","refs","context","useFloating","open","onOpenChange","isOpen","isMounted","status","useTransitionStatus","duration","dismiss","useDismiss","outsidePress","getFloatingProps","useInteractions","_jsx","Overlay","rootId","clsx","styles.container","ModalContext","Provider","value","FloatingFocusManager","initialFocus","floating","styles.scrollContainer","setFloating","role","styles.modalContainer","Card","CloseButton","Content","Footer"],"mappings":"iwCAmBA,MA8HaA,EAAUC,OAAOC,QA9HhBC,QAAAC,GACVA,EAAEC,KACFA,EAAIC,QACJA,EAAOC,cACPA,EAAgB,SAAQC,SACxBA,EAAQC,mBACRA,GAAqB,EAAIC,gBACzBA,EAAeC,OACfA,EAAMC,eACNA,EAAcC,sBACdA,GAAwB,EACxB,cAAeC,EAAS,QACxB,aAAcC,EACd,kBAAmBC,EACnB,mBAAoBC,EAAeC,oBACnCA,GAAsB,EAAKC,UAC3BA,GAAShB,EACNiB,EAAUC,EAAAlB,EAjBH,CAAA,KAAA,OAAA,UAAA,gBAAA,WAAA,qBAAA,kBAAA,SAAA,iBAAA,wBAAA,cAAA,aAAA,kBAAA,mBAAA,sBAAA,cAsBV,MAAMmB,EAAkBC,GAAS,aACzBV,YACAW,EAAuC,UAAtCC,SAASC,qBAA6B,IAAAvB,OAAA,EAAAA,EAAEwB,6BAC7C,KAEEC,eAAEA,GAAmBC,EAAY,CACnCC,QAASzB,EACT0B,cAAeT,IAEbU,EAAWC,EAAuB,MAClCC,EAAeD,EAAuB,MACtCE,EACF3B,GAAY4B,EAAMC,aAAa7B,EAAU,CAAE8B,IAAKN,IAEpDO,EAAcL,EAAc,CACxB,CAACM,EAAcC,UAAUb,gBACH,MAAlBA,EAAyB,KAAO,GAAGA,QAM3C,MAAMc,KAAEA,EAAIC,QAAEA,GAAYC,EAAY,CAClCC,KAAMxC,EACNyC,aAAeC,IACNA,GACDzC,SAAAA,GACJ,KAGF0C,UAAEA,EAASC,OAAEA,GAAWC,EAAoBP,EAAS,CACvDQ,SAAU,MAERC,EAAUC,EAAWV,EAAS,CAEhCW,cAAc,EACdxB,UAAWxB,KAETiD,iBAAEA,GAAqBC,EAAgB,CAACJ,IAK9C,OACIK,EAACC,EAAO,CAAA,cACS,GAAG5C,YAChBT,KAAMA,EACNI,mBAAoBA,EACpBG,eAAgBA,EAChBR,GAAIA,EACJuD,OAAQjD,EACRwB,aAAcF,EACdrB,OAAQA,EAAMH,SAEdiD,EAAA,MAAAxD,OAAAC,OAAA,CACIoC,IAAKJ,EAAY,cACJpB,EAAM,cACNmC,EAAM,sBACE1C,GACjBa,GACJD,UAAWyC,EAAKC,EAAkB1C,GAAUX,SAE5CiD,EAACK,EAAaC,SAAQ,CAACC,MAAO,CAAE1D,WAASE,SACpCwC,GACGS,EAACQ,EAAoB,CACjBtB,QAASA,EACTuB,aACIhD,GAAsB,EAAKwB,EAAKyB,SAAQ3D,SAG5CiD,EAAA,MAAA,CAAKtC,UAAWiD,EAAsB5D,SAClCiD,EAAA,MAAAxD,OAAAC,OAAA,CACIoC,IAAKI,EAAK2B,aACNd,IAAkB,CACtBe,KAAK,SAAQ,aACDvD,EAAS,kBACJC,EAAc,mBACbC,EAClBE,UAAWoD,WAEVpC,eAOnB,GAiByB,CAEvCqC,OAEAC,cAEAC,UAEAC"}
1
+ {"version":3,"file":"modal-v2.js","sources":["../../src/modal-v2/modal-v2.tsx"],"sourcesContent":["import {\n FloatingFocusManager,\n useDismiss,\n useFloating,\n useInteractions,\n useTransitionStatus,\n} from \"@floating-ui/react\";\nimport clsx from \"clsx\";\nimport React, { useRef } from \"react\";\n\nimport { Overlay } from \"../overlay/overlay\";\nimport { useViewport } from \"../shared/hooks\";\nimport { useApplyStyle } from \"../theme\";\nimport { useEvent } from \"../util\";\nimport { ModalContext } from \"./modal-context\";\nimport * as styles from \"./modal-v2.styles\";\nimport { Card, CloseButton, Content, Footer, Header } from \"./slots\";\nimport type { ModalV2Props } from \"./types\";\n\nconst Base = ({\n id,\n show,\n onClose,\n animationFrom = \"bottom\",\n children,\n enableOverlayClick = true,\n rootComponentId,\n zIndex,\n onOverlayClick,\n dismissKeyboardOnShow = true,\n \"data-testid\": testId = \"modal\",\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n \"aria-describedby\": ariaDescribedBy,\n disableInitialFocus = false,\n className,\n ...otherProps\n}: ModalV2Props): JSX.Element => {\n // =========================================================================\n // CONST, STATE, REF\n // =========================================================================\n const dismissKeyboard = useEvent(() => {\n if (dismissKeyboardOnShow) {\n (document.activeElement as HTMLElement)?.blur?.();\n }\n });\n const { verticalHeight } = useViewport({\n enabled: show,\n onBeforeStart: dismissKeyboard,\n });\n const childRef = useRef<HTMLDivElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const childWithRef =\n children && React.cloneElement(children, { ref: childRef });\n\n useApplyStyle(containerRef, {\n [styles.tokens.container.verticalHeight]:\n verticalHeight == null ? null : `${verticalHeight}px`,\n });\n\n // =========================================================================\n // FLOATING UI CONFIG\n // =========================================================================\n const { refs, context } = useFloating({\n open: show,\n onOpenChange: (isOpen) => {\n if (!isOpen) {\n onClose?.();\n }\n },\n });\n const { isMounted, status } = useTransitionStatus(context, {\n duration: 300,\n });\n const dismiss = useDismiss(context, {\n /* handled by overlayclick */\n outsidePress: false,\n enabled: !!onClose,\n });\n const { getFloatingProps } = useInteractions([dismiss]);\n\n // =========================================================================\n // RENDER FUNCTIONS\n // =========================================================================\n return (\n <Overlay\n data-testid={`${testId}-overlay`}\n show={show}\n enableOverlayClick={enableOverlayClick}\n onOverlayClick={onOverlayClick}\n id={id}\n rootId={rootComponentId}\n containerRef={childRef}\n zIndex={zIndex}\n >\n <div\n ref={containerRef}\n data-testid={testId}\n data-status={status}\n data-animation-from={animationFrom}\n {...otherProps}\n className={clsx(styles.container, className)}\n >\n <ModalContext.Provider value={{ onClose }}>\n {isMounted && (\n <FloatingFocusManager\n context={context}\n initialFocus={\n disableInitialFocus ? -1 : refs.floating\n }\n >\n <div className={styles.scrollContainer}>\n <div\n ref={refs.setFloating}\n {...getFloatingProps()}\n role=\"dialog\"\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n className={styles.modalContainer}\n >\n {childWithRef}\n </div>\n </div>\n </FloatingFocusManager>\n )}\n </ModalContext.Provider>\n </div>\n </Overlay>\n );\n};\n\n// @catalog\n/**\n * A composable modal dialog with slot-based content layout.\n *\n * Use `ModalV2` when you need a controlled overlay dialog with structured\n * content slots.\n *\n * Sub-components:\n * - `ModalV2.Card` — wraps modal content and arranges `Content`, `Footer`, and `CloseButton` slots in the correct layout.\n * - `ModalV2.CloseButton` — renders an accessible close button that invokes the modal's `onClose` callback.\n * - `ModalV2.Content` — container for the body content of the modal card.\n * - `ModalV2.Footer` — footer area for primary and secondary action buttons.\n * - `ModalV2.Header` — header bar with a close button and optional title. Use instead of `CloseButton` when a heading is needed.\n */\nexport const ModalV2 = Object.assign(Base, {\n /** Wraps modal content and arranges `Content`, `Footer`, and `CloseButton` slots in the correct layout. */\n Card,\n /** Renders an accessible close button that invokes the modal's `onClose` callback. */\n CloseButton,\n /** Container for the body content of the modal card. */\n Content,\n /** Footer area for primary and secondary action buttons. */\n Footer,\n /** Header bar with a close button and optional title. Use instead of `CloseButton` when a heading is needed. */\n Header,\n});\n"],"names":["ModalV2","Object","assign","_a","id","show","onClose","animationFrom","children","enableOverlayClick","rootComponentId","zIndex","onOverlayClick","dismissKeyboardOnShow","testId","ariaLabel","ariaLabelledBy","ariaDescribedBy","disableInitialFocus","className","otherProps","__rest","dismissKeyboard","useEvent","_b","document","activeElement","blur","verticalHeight","useViewport","enabled","onBeforeStart","childRef","useRef","containerRef","childWithRef","React","cloneElement","ref","useApplyStyle","styles.tokens","container","refs","context","useFloating","open","onOpenChange","isOpen","isMounted","status","useTransitionStatus","duration","dismiss","useDismiss","outsidePress","getFloatingProps","useInteractions","_jsx","Overlay","rootId","clsx","styles.container","ModalContext","Provider","value","FloatingFocusManager","initialFocus","floating","styles.scrollContainer","setFloating","role","styles.modalContainer","Card","CloseButton","Content","Footer","Header"],"mappings":"4yCAmBA,MA+HaA,EAAUC,OAAOC,QA/HhBC,QAAAC,GACVA,EAAEC,KACFA,EAAIC,QACJA,EAAOC,cACPA,EAAgB,SAAQC,SACxBA,EAAQC,mBACRA,GAAqB,EAAIC,gBACzBA,EAAeC,OACfA,EAAMC,eACNA,EAAcC,sBACdA,GAAwB,EACxB,cAAeC,EAAS,QACxB,aAAcC,EACd,kBAAmBC,EACnB,mBAAoBC,EAAeC,oBACnCA,GAAsB,EAAKC,UAC3BA,GAAShB,EACNiB,EAAUC,EAAAlB,EAjBH,CAAA,KAAA,OAAA,UAAA,gBAAA,WAAA,qBAAA,kBAAA,SAAA,iBAAA,wBAAA,cAAA,aAAA,kBAAA,mBAAA,sBAAA,cAsBV,MAAMmB,EAAkBC,GAAS,aACzBV,YACAW,EAAuC,UAAtCC,SAASC,qBAA6B,IAAAvB,OAAA,EAAAA,EAAEwB,6BAC7C,KAEEC,eAAEA,GAAmBC,EAAY,CACnCC,QAASzB,EACT0B,cAAeT,IAEbU,EAAWC,EAAuB,MAClCC,EAAeD,EAAuB,MACtCE,EACF3B,GAAY4B,EAAMC,aAAa7B,EAAU,CAAE8B,IAAKN,IAEpDO,EAAcL,EAAc,CACxB,CAACM,EAAcC,UAAUb,gBACH,MAAlBA,EAAyB,KAAO,GAAGA,QAM3C,MAAMc,KAAEA,EAAIC,QAAEA,GAAYC,EAAY,CAClCC,KAAMxC,EACNyC,aAAeC,IACNA,GACDzC,SAAAA,GACJ,KAGF0C,UAAEA,EAASC,OAAEA,GAAWC,EAAoBP,EAAS,CACvDQ,SAAU,MAERC,EAAUC,EAAWV,EAAS,CAEhCW,cAAc,EACdxB,UAAWxB,KAETiD,iBAAEA,GAAqBC,EAAgB,CAACJ,IAK9C,OACIK,EAACC,EAAO,CAAA,cACS,GAAG5C,YAChBT,KAAMA,EACNI,mBAAoBA,EACpBG,eAAgBA,EAChBR,GAAIA,EACJuD,OAAQjD,EACRwB,aAAcF,EACdrB,OAAQA,EAAMH,SAEdiD,EAAA,MAAAxD,OAAAC,OAAA,CACIoC,IAAKJ,EAAY,cACJpB,EAAM,cACNmC,EAAM,sBACE1C,GACjBa,GACJD,UAAWyC,EAAKC,EAAkB1C,GAAUX,SAE5CiD,EAACK,EAAaC,SAAQ,CAACC,MAAO,CAAE1D,WAASE,SACpCwC,GACGS,EAACQ,EAAoB,CACjBtB,QAASA,EACTuB,aACIhD,GAAsB,EAAKwB,EAAKyB,SAAQ3D,SAG5CiD,EAAA,MAAA,CAAKtC,UAAWiD,EAAsB5D,SAClCiD,EAAA,MAAAxD,OAAAC,OAAA,CACIoC,IAAKI,EAAK2B,aACNd,IAAkB,CACtBe,KAAK,SAAQ,aACDvD,EAAS,kBACJC,EAAc,mBACbC,EAClBE,UAAWoD,WAEVpC,eAOnB,GAkByB,CAEvCqC,OAEAC,cAEAC,UAEAC,SAEAC"}
@@ -1,2 +1,2 @@
1
- import{__rest as t}from"../../_virtual/_tslib.js";import{jsxs as r}from"react/jsx-runtime";import e from"clsx";import i from"react";import"../../util/calendar-helper.js";import"../../external/dayjs/dayjs.min.js";import{mergeRefs as o}from"../../util/merge-refs.js";import"../../util/simple-id-generator.js";import"../../util/string-helper.js";import"@react-aria/live-announcer";import s from"../../util/is-styled-components.js";import{CloseButton as a}from"./close-button.js";import{Content as m}from"./content.js";import{Footer as l}from"./footer.js";import{slotSpacer as n,modalCard as d}from"./slot-styles.js";const p=i.forwardRef((function(p,c){var{id:f,"data-testid":j="modal-card",children:u,className:y,elementRef:h}=p,g=t(p,["id","data-testid","children","className","elementRef"]);const v=t=>i.Children.toArray(u).find((r=>((t,r)=>s(t.type)?t.type.target===r:t.type===r)(r,t))),N=v(a),b=v(m),x=v(l),C=!!N;return r("div",Object.assign({ref:o(c,h),id:f,"data-testid":j},g,{onClick:t=>{t.stopPropagation()},className:e(d,y),children:[r("div",{"data-has-close-button":C?"true":"false",className:e(n),children:[b,x]}),C&&N]}))}));p.displayName="ModalV2.Card";export{p as Card};
1
+ import{__rest as t}from"../../_virtual/_tslib.js";import{jsxs as r}from"react/jsx-runtime";import e from"clsx";import o from"react";import"../../util/calendar-helper.js";import"../../external/dayjs/dayjs.min.js";import{mergeRefs as i}from"../../util/merge-refs.js";import"../../util/simple-id-generator.js";import"../../util/string-helper.js";import"@react-aria/live-announcer";import s from"../../util/is-styled-components.js";import{CloseButton as a}from"./close-button.js";import{Content as m}from"./content.js";import{Footer as l}from"./footer.js";import{Header as n}from"./header.js";import{slotSpacer as d,modalCard as p}from"./slot-styles.js";const c=o.forwardRef((function(c,f){var{id:j,"data-testid":u="modal-card",children:h,className:y,elementRef:g}=c,v=t(c,["id","data-testid","children","className","elementRef"]);const N=t=>o.Children.toArray(h).find((r=>((t,r)=>s(t.type)?t.type.target===r:t.type===r)(r,t))),b=N(a),x=N(m),C=N(l),R=N(n),_=!!b||!!R;return r("div",Object.assign({ref:i(f,g),id:j,"data-testid":u},v,{onClick:t=>{t.stopPropagation()},className:e(p,y),children:[r("div",{"data-has-close-button":!!_,className:e(d),children:[x,C]}),_&&(R||b)]}))}));c.displayName="ModalV2.Card";export{c as Card};
2
2
  //# sourceMappingURL=card.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"card.js","sources":["../../../src/modal-v2/slots/card.tsx"],"sourcesContent":["import clsx from \"clsx\";\nimport type { NamedExoticComponent } from \"react\";\nimport React from \"react\";\n\nimport { mergeRefs } from \"../../util\";\nimport isStyledComponent from \"../../util/is-styled-components\";\nimport type { ModalCardProps } from \"../types\";\nimport { CloseButton } from \"./close-button\";\nimport { Content } from \"./content\";\nimport { Footer } from \"./footer\";\nimport * as styles from \"./slot-styles\";\n\nfunction CardInner(\n {\n id,\n \"data-testid\": testId = \"modal-card\",\n children,\n className,\n elementRef,\n ...otherProps\n }: ModalCardProps,\n ref: React.ForwardedRef<HTMLDivElement>\n) {\n // =============================================================================\n // EVENT HANDLERS\n // =============================================================================\n const handleOnClick = (event: React.MouseEvent) => {\n event.stopPropagation();\n };\n\n // =========================================================================\n // HELPERS\n // =========================================================================\n const isComponentType = (child: React.ReactPortal, type: any) =>\n isStyledComponent(child.type)\n ? (child.type as unknown as { target: any }).target === type\n : child.type === type;\n\n const findComponentOfType = (type: any) => {\n return React.Children.toArray(children).find((child) =>\n isComponentType(child as React.ReactPortal, type)\n );\n };\n\n // =============================================================================\n // RENDER FUNCTIONS\n // =============================================================================\n const CloseButtonSlot = findComponentOfType(CloseButton);\n const ContentSlot = findComponentOfType(Content);\n const FooterSlot = findComponentOfType(Footer);\n\n const hasCloseButton = !!CloseButtonSlot;\n\n return (\n <div\n ref={mergeRefs(ref, elementRef)}\n id={id}\n data-testid={testId}\n {...otherProps}\n onClick={handleOnClick}\n className={clsx(styles.modalCard, className)}\n >\n <div\n data-has-close-button={hasCloseButton ? \"true\" : \"false\"}\n className={clsx(styles.slotSpacer)}\n >\n {ContentSlot}\n {FooterSlot}\n </div>\n {hasCloseButton && CloseButtonSlot}\n </div>\n );\n}\n\nexport const Card = React.forwardRef<HTMLDivElement, ModalCardProps>(CardInner);\n\n(Card as NamedExoticComponent).displayName = \"ModalV2.Card\";\n"],"names":["Card","React","forwardRef","_a","ref","id","testId","children","className","elementRef","otherProps","__rest","findComponentOfType","type","Children","toArray","find","child","isStyledComponent","target","isComponentType","CloseButtonSlot","CloseButton","ContentSlot","Content","FooterSlot","Footer","hasCloseButton","_jsxs","Object","assign","mergeRefs","onClick","event","stopPropagation","clsx","styles.modalCard","styles.slotSpacer","displayName"],"mappings":"qmBA0EO,MAAMA,EAAOC,EAAMC,YA9D1B,SACIC,EAQAC,GARA,IAAAC,GACIA,EACA,cAAeC,EAAS,aAAYC,SACpCA,EAAQC,UACRA,EAASC,WACTA,GAAUN,EACPO,EAAUC,EAAAR,EANjB,0DAaA,MAYMS,EAAuBC,GAClBZ,EAAMa,SAASC,QAAQR,GAAUS,MAAMC,GAN1B,EAACA,EAA0BJ,IAC/CK,EAAkBD,EAAMJ,MACjBI,EAAMJ,KAAoCM,SAAWN,EACtDI,EAAMJ,OAASA,EAIjBO,CAAgBH,EAA4BJ,KAO9CQ,EAAkBT,EAAoBU,GACtCC,EAAcX,EAAoBY,GAClCC,EAAab,EAAoBc,GAEjCC,IAAmBN,EAEzB,OACIO,EAAA,MAAAC,OAAAC,OAAA,CACI1B,IAAK2B,EAAU3B,EAAKK,GACpBJ,GAAIA,EAAE,cACOC,GACTI,EAAU,CACdsB,QAjCeC,IACnBA,EAAMC,iBAAiB,EAiCnB1B,UAAW2B,EAAKC,EAAkB5B,GAAUD,SAAA,CAE5CqB,EAAA,MAAA,CAAA,wBAC2BD,EAAiB,OAAS,QACjDnB,UAAW2B,EAAKE,GAAkB9B,SAAA,CAEjCgB,EACAE,KAEJE,GAAkBN,KAG/B,IAICrB,EAA8BsC,YAAc"}
1
+ {"version":3,"file":"card.js","sources":["../../../src/modal-v2/slots/card.tsx"],"sourcesContent":["import clsx from \"clsx\";\nimport type { NamedExoticComponent } from \"react\";\nimport React from \"react\";\n\nimport { mergeRefs } from \"../../util\";\nimport isStyledComponent from \"../../util/is-styled-components\";\nimport type { ModalCardProps } from \"../types\";\nimport { CloseButton } from \"./close-button\";\nimport { Content } from \"./content\";\nimport { Footer } from \"./footer\";\nimport { Header } from \"./header\";\nimport * as styles from \"./slot-styles\";\n\nfunction CardInner(\n {\n id,\n \"data-testid\": testId = \"modal-card\",\n children,\n className,\n elementRef,\n ...otherProps\n }: ModalCardProps,\n ref: React.ForwardedRef<HTMLDivElement>\n) {\n // =============================================================================\n // EVENT HANDLERS\n // =============================================================================\n const handleOnClick = (event: React.MouseEvent) => {\n event.stopPropagation();\n };\n\n // =========================================================================\n // HELPERS\n // =========================================================================\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const isComponentType = (child: React.ReactPortal, type: any) =>\n isStyledComponent(child.type)\n ? // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (child.type as unknown as { target: any }).target === type\n : child.type === type;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const findComponentOfType = (type: any) => {\n return React.Children.toArray(children).find((child) =>\n isComponentType(child as React.ReactPortal, type)\n );\n };\n\n // =============================================================================\n // RENDER FUNCTIONS\n // =============================================================================\n const CloseButtonSlot = findComponentOfType(CloseButton);\n const ContentSlot = findComponentOfType(Content);\n const FooterSlot = findComponentOfType(Footer);\n const HeaderSlot = findComponentOfType(Header);\n\n const hasHeader = !!CloseButtonSlot || !!HeaderSlot;\n\n return (\n <div\n ref={mergeRefs(ref, elementRef)}\n id={id}\n data-testid={testId}\n {...otherProps}\n onClick={handleOnClick}\n className={clsx(styles.modalCard, className)}\n >\n <div\n data-has-close-button={!!hasHeader}\n className={clsx(styles.slotSpacer)}\n >\n {ContentSlot}\n {FooterSlot}\n </div>\n {hasHeader && (HeaderSlot || CloseButtonSlot)}\n </div>\n );\n}\n\nexport const Card = React.forwardRef<HTMLDivElement, ModalCardProps>(CardInner);\n\n(Card as NamedExoticComponent).displayName = \"ModalV2.Card\";\n"],"names":["Card","React","forwardRef","_a","ref","id","testId","children","className","elementRef","otherProps","__rest","findComponentOfType","type","Children","toArray","find","child","isStyledComponent","target","isComponentType","CloseButtonSlot","CloseButton","ContentSlot","Content","FooterSlot","Footer","HeaderSlot","Header","hasHeader","_jsxs","Object","assign","mergeRefs","onClick","event","stopPropagation","clsx","styles.modalCard","styles.slotSpacer","displayName"],"mappings":"0oBA+EO,MAAMA,EAAOC,EAAMC,YAlE1B,SACIC,EAQAC,GARA,IAAAC,GACIA,EACA,cAAeC,EAAS,aAAYC,SACpCA,EAAQC,UACRA,EAASC,WACTA,GAAUN,EACPO,EAAUC,EAAAR,EANjB,0DAaA,MAeMS,EAAuBC,GAClBZ,EAAMa,SAASC,QAAQR,GAAUS,MAAMC,GAR1B,EAACA,EAA0BJ,IAC/CK,EAAkBD,EAAMJ,MAEjBI,EAAMJ,KAAoCM,SAAWN,EACtDI,EAAMJ,OAASA,EAKjBO,CAAgBH,EAA4BJ,KAO9CQ,EAAkBT,EAAoBU,GACtCC,EAAcX,EAAoBY,GAClCC,EAAab,EAAoBc,GACjCC,EAAaf,EAAoBgB,GAEjCC,IAAcR,KAAqBM,EAEzC,OACIG,EAAA,MAAAC,OAAAC,OAAA,CACI5B,IAAK6B,EAAU7B,EAAKK,GACpBJ,GAAIA,EAAE,cACOC,GACTI,EAAU,CACdwB,QArCeC,IACnBA,EAAMC,iBAAiB,EAqCnB5B,UAAW6B,EAAKC,EAAkB9B,GAAUD,SAAA,CAE5CuB,EAAA,MAAA,CAAA,0BAC6BD,EACzBrB,UAAW6B,EAAKE,GAAkBhC,SAAA,CAEjCgB,EACAE,KAEJI,IAAcF,GAAcN,MAGzC,IAICrB,EAA8BwC,YAAc"}
@@ -0,0 +1,5 @@
1
+ import type { ModalHeaderProps } from "../types";
2
+ export declare const Header: {
3
+ ({ "data-testid": testId, title, closeButtonPosition, className, ...otherProps }: ModalHeaderProps): import("react/jsx-runtime").JSX.Element;
4
+ displayName: string;
5
+ };
@@ -0,0 +1,2 @@
1
+ import{__rest as t}from"../../_virtual/_tslib.js";import{jsxs as o,jsx as s}from"react/jsx-runtime";import{CrossIcon as a}from"@lifesg/react-icons/cross";import i from"clsx";import{useContext as e}from"react";import{ClickableIcon as r}from"../../shared/clickable-icon/clickable-icon.js";import{ModalContext as l}from"../modal-context.js";import{headerContainer as c,headerCloseButton as m,headerTitle as n,headerSpacer as d}from"./slot-styles.js";const p=p=>{var{"data-testid":f="modal-header",title:u,closeButtonPosition:h="right",className:b}=p,N=t(p,["data-testid","title","closeButtonPosition","className"]);const{onClose:j}=e(l);return o("div",Object.assign({"data-testid":f,"data-close-button-position":h,className:i(c,b)},N,{children:[s(r,{onClick:j,"data-testid":"close-button",focusHighlight:!1,focusOutline:"browser","aria-label":"Close button",className:m,children:s(a,{"aria-hidden":!0})}),u&&s("span",{className:n,children:u}),u&&s("div",{className:d,"aria-hidden":!0})]}))};p.displayName="ModalV2.Header";export{p as Header};
2
+ //# sourceMappingURL=header.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"header.js","sources":["../../../src/modal-v2/slots/header.tsx"],"sourcesContent":["import { CrossIcon } from \"@lifesg/react-icons/cross\";\nimport clsx from \"clsx\";\nimport { useContext } from \"react\";\n\nimport { ClickableIcon } from \"../../shared/clickable-icon\";\nimport { ModalContext } from \"../modal-context\";\nimport type { ModalHeaderProps } from \"../types\";\nimport * as styles from \"./slot-styles\";\n\nexport const Header = ({\n \"data-testid\": testId = \"modal-header\",\n title,\n closeButtonPosition = \"right\",\n className,\n ...otherProps\n}: ModalHeaderProps) => {\n const { onClose } = useContext(ModalContext);\n\n return (\n <div\n data-testid={testId}\n data-close-button-position={closeButtonPosition}\n className={clsx(styles.headerContainer, className)}\n {...otherProps}\n >\n <ClickableIcon\n onClick={onClose}\n data-testid=\"close-button\"\n focusHighlight={false}\n focusOutline=\"browser\"\n aria-label=\"Close button\"\n className={styles.headerCloseButton}\n >\n <CrossIcon aria-hidden />\n </ClickableIcon>\n {title && <span className={styles.headerTitle}>{title}</span>}\n {title && <div className={styles.headerSpacer} aria-hidden />}\n </div>\n );\n};\n\nHeader.displayName = \"ModalV2.Header\";\n"],"names":["Header","_a","testId","title","closeButtonPosition","className","otherProps","__rest","onClose","useContext","ModalContext","_jsxs","Object","assign","clsx","styles.headerContainer","children","_jsx","ClickableIcon","onClick","focusHighlight","focusOutline","styles.headerCloseButton","CrossIcon","styles.headerTitle","styles.headerSpacer","displayName"],"mappings":"+bASO,MAAMA,EAAUC,IAAA,IACnB,cAAeC,EAAS,eAAcC,MACtCA,EAAKC,oBACLA,EAAsB,QAAOC,UAC7BA,GAASJ,EACNK,EAAUC,EAAAN,EALM,2DAOnB,MAAMO,QAAEA,GAAYC,EAAWC,GAE/B,OACIC,EAAA,MAAAC,OAAAC,OAAA,CAAA,cACiBX,EAAM,6BACSE,EAC5BC,UAAWS,EAAKC,EAAwBV,IACpCC,EAAU,CAAAU,SAAA,CAEdC,EAACC,EAAa,CACVC,QAASX,EAAO,cACJ,eACZY,gBAAgB,EAChBC,aAAa,UAAS,aACX,eACXhB,UAAWiB,EAAwBN,SAEnCC,EAACM,EAAS,CAAA,eAAA,MAEbpB,GAASc,EAAA,OAAA,CAAMZ,UAAWmB,EAAkBR,SAAGb,IAC/CA,GAASc,EAAA,MAAA,CAAKZ,UAAWoB,EAAmB,eAAA,OAC3C,EAIdzB,EAAO0B,YAAc"}
@@ -2,3 +2,4 @@ export { Card } from "./card";
2
2
  export { CloseButton } from "./close-button";
3
3
  export { Content } from "./content";
4
4
  export { Footer } from "./footer";
5
+ export { Header } from "./header";