@lifesg/react-design-system 4.0.0-alpha.7 → 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 (268) 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/button/button.js +1 -1
  5. package/button/button.js.map +1 -1
  6. package/button/button.styles.d.ts +1 -0
  7. package/button/button.styles.js +1 -1
  8. package/button/button.styles.js.map +1 -1
  9. package/button/button.styles_19i4dfq.css +21 -0
  10. package/cjs/accordion/accordion-item.js.map +1 -1
  11. package/cjs/accordion/accordion.js.map +1 -1
  12. package/cjs/alert/alert.js.map +1 -1
  13. package/cjs/button/button.js +1 -1
  14. package/cjs/button/button.js.map +1 -1
  15. package/cjs/button/button.styles.js +1 -1
  16. package/cjs/button/button.styles.js.map +1 -1
  17. package/cjs/button/button.styles_19i4dfq.css +21 -0
  18. package/cjs/countdown-timer/countdown-timer.js.map +1 -1
  19. package/cjs/data-table/data-table.js +1 -1
  20. package/cjs/data-table/data-table.js.map +1 -1
  21. package/cjs/data-table/data-table.styles.js +1 -1
  22. package/cjs/data-table/data-table.styles.js.map +1 -1
  23. package/{data-table/data-table.styles_rxganh.css → cjs/data-table/data-table.styles_1tnlmhe.css} +7 -6
  24. package/cjs/date-range-input/date-range-input.js.map +1 -1
  25. package/cjs/e-signature/e-signature-canvas.js.map +1 -1
  26. package/cjs/error-display/error-display-data.js +1 -1
  27. package/cjs/error-display/error-display-data.js.map +1 -1
  28. package/cjs/error-display/inactivity-timer.js.map +1 -1
  29. package/cjs/external/dompurify/dist/purify.es.js +2 -2
  30. package/cjs/external/dompurify/dist/purify.es.js.map +1 -1
  31. package/cjs/file-download/file-list-card/file-list-card.js.map +1 -1
  32. package/cjs/file-upload/custom-sensors.js.map +1 -1
  33. package/cjs/file-upload/file-list/file-list.js.map +1 -1
  34. package/cjs/filter/addons/filter-item-checkbox-helpers.js.map +1 -1
  35. package/cjs/filter/addons/filter-item-checkbox.js.map +1 -1
  36. package/cjs/filter/addons/filter-item-checkbox.styles.js +1 -1
  37. package/cjs/filter/addons/filter-item-checkbox.styles.js.map +1 -1
  38. package/{filter/addons/filter-item-checkbox.styles_7degw7.css → cjs/filter/addons/filter-item-checkbox.styles_6nbpdl.css} +1 -1
  39. package/cjs/filter/filter-item.js.map +1 -1
  40. package/cjs/filter/filter-item.styles.js +1 -1
  41. package/cjs/filter/filter-item.styles.js.map +1 -1
  42. package/cjs/filter/{filter-item.styles_cphjnj.css → filter-item.styles_1yixp8h.css} +1 -1
  43. package/cjs/filter/filter-modal.styles.js +1 -1
  44. package/cjs/filter/filter-modal.styles.js.map +1 -1
  45. package/{filter/filter-modal.styles_qw3xlg.css → cjs/filter/filter-modal.styles_kuth21.css} +1 -1
  46. package/cjs/filter/filter-sidebar.styles.js +1 -1
  47. package/cjs/filter/filter-sidebar.styles.js.map +1 -1
  48. package/cjs/filter/{filter-sidebar.styles_1otwpjl.css → filter-sidebar.styles_rbfq4f.css} +1 -1
  49. package/cjs/filter/filter.js.map +1 -1
  50. package/cjs/form/form-textarea/form-textarea.js +1 -1
  51. package/cjs/form/form-textarea/form-textarea.js.map +1 -1
  52. package/cjs/fullscreen-image-carousel/stateful-image.js.map +1 -1
  53. package/cjs/histogram-slider/histogram-slider.js.map +1 -1
  54. package/cjs/input-range-slider/input-range-slider.js.map +1 -1
  55. package/cjs/input-slider/input-slider.js.map +1 -1
  56. package/cjs/input-textarea/textarea-counter.js.map +1 -1
  57. package/cjs/input-textarea/textarea.js +1 -1
  58. package/cjs/input-textarea/textarea.js.map +1 -1
  59. package/cjs/input-textarea/textarea.styles.js +1 -1
  60. package/cjs/input-textarea/textarea.styles.js.map +1 -1
  61. package/cjs/input-textarea/{textarea.styles_q77m5x.css → textarea.styles_6we7bs.css} +3 -1
  62. package/cjs/modal/index.js.map +1 -1
  63. package/cjs/modal-v2/modal-v2.js +1 -1
  64. package/cjs/modal-v2/modal-v2.js.map +1 -1
  65. package/cjs/modal-v2/slots/card.js +1 -1
  66. package/cjs/modal-v2/slots/card.js.map +1 -1
  67. package/cjs/modal-v2/slots/header.js +2 -0
  68. package/cjs/modal-v2/slots/header.js.map +1 -0
  69. package/cjs/modal-v2/slots/slot-styles.js +1 -1
  70. package/cjs/modal-v2/slots/slot-styles.js.map +1 -1
  71. package/cjs/modal-v2/slots/{slot-styles_13sd376.css → slot-styles_10ut1v7.css} +5 -1
  72. package/cjs/navbar/navbar-items.js +1 -1
  73. package/cjs/navbar/navbar-items.js.map +1 -1
  74. package/cjs/navbar/navbar-items.styles.js +1 -1
  75. package/cjs/navbar/navbar-items.styles.js.map +1 -1
  76. package/cjs/navbar/{navbar-items.styles_1i3ltot.css → navbar-items.styles_1b1zx.css} +9 -8
  77. package/cjs/navbar/navbar-logo-data.js +1 -1
  78. package/cjs/navbar/navbar-logo-data.js.map +1 -1
  79. package/cjs/navbar/navbar.js.map +1 -1
  80. package/cjs/otp-input/otp-input.js.map +1 -1
  81. package/cjs/overlay/overlay.js.map +1 -1
  82. package/cjs/pagination/pagination.js.map +1 -1
  83. package/cjs/phone-number-input/phone-number-input.js.map +1 -1
  84. package/cjs/predictive-text-input/predictive-text-input.js.map +1 -1
  85. package/cjs/shared/dropdown-list/dropdown-list.js.map +1 -1
  86. package/cjs/shared/internal-calendar/calendar-manager.js.map +1 -1
  87. package/cjs/shared/standalone-date-input/standalone-date-input.js.map +1 -1
  88. package/cjs/singpass-button/singpass-button.styles.js +1 -1
  89. package/cjs/singpass-button/singpass-button.styles.js.map +1 -1
  90. package/cjs/singpass-button/{singpass-button.styles_1miyex3.css → singpass-button.styles_1fj2lru.css} +1 -1
  91. package/cjs/smart-app-banner/smart-app-banner.styles.js +1 -1
  92. package/cjs/smart-app-banner/smart-app-banner.styles.js.map +1 -1
  93. package/cjs/smart-app-banner/{smart-app-banner.styles_15u6yv4.css → smart-app-banner.styles_r6yjq9.css} +1 -1
  94. package/cjs/theme/types.js +1 -1
  95. package/cjs/theme/types.js.map +1 -1
  96. package/cjs/time-slot-bar/time-slot-bar.js.map +1 -1
  97. package/cjs/time-slot-bar-week/time-slot-bar-week-days.js.map +1 -1
  98. package/cjs/timetable/timetable.js.map +1 -1
  99. package/cjs/toast/toast.styles.js +1 -1
  100. package/cjs/toast/toast.styles.js.map +1 -1
  101. package/{toast/toast.styles_1e0l60w.css → cjs/toast/toast.styles_57j3lf.css} +1 -1
  102. package/cjs/toggle/toggle-icon.js +1 -1
  103. package/cjs/toggle/toggle-icon.js.map +1 -1
  104. package/cjs/toggle/toggle-icon.styles.js +1 -1
  105. package/cjs/toggle/toggle-icon.styles.js.map +1 -1
  106. package/cjs/toggle/{toggle-icon.styles_ba71y.css → toggle-icon.styles_o6dxr3.css} +3 -2
  107. package/cjs/toggle/toggle.js +1 -1
  108. package/cjs/toggle/toggle.js.map +1 -1
  109. package/cjs/toggle/toggle.styles.js +1 -1
  110. package/cjs/toggle/toggle.styles.js.map +1 -1
  111. package/cjs/toggle/toggle.styles_11tmfsr.css +41 -0
  112. package/cjs/unit-number/unit-number-input.js.map +1 -1
  113. package/cjs/util/is-styled-components.js.map +1 -1
  114. package/cjs/util/string-helper.js +1 -1
  115. package/cjs/util/string-helper.js.map +1 -1
  116. package/cjs/util/use-event-listener.js.map +1 -1
  117. package/cjs/util/use-state-actions.js.map +1 -1
  118. package/countdown-timer/countdown-timer.js.map +1 -1
  119. package/data-table/data-table.d.ts +1 -1
  120. package/data-table/data-table.js +1 -1
  121. package/data-table/data-table.js.map +1 -1
  122. package/data-table/data-table.styles.d.ts +1 -0
  123. package/data-table/data-table.styles.js +1 -1
  124. package/data-table/data-table.styles.js.map +1 -1
  125. package/{cjs/data-table/data-table.styles_rxganh.css → data-table/data-table.styles_1tnlmhe.css} +7 -6
  126. package/data-table/types.d.ts +4 -2
  127. package/date-range-input/date-range-input.js.map +1 -1
  128. package/docs/component-catalog.json +1 -1
  129. package/e-signature/e-signature-canvas.js.map +1 -1
  130. package/error-display/error-display-data.js +1 -1
  131. package/error-display/error-display-data.js.map +1 -1
  132. package/error-display/inactivity-timer.js.map +1 -1
  133. package/external/dompurify/dist/purify.es.js +2 -2
  134. package/external/dompurify/dist/purify.es.js.map +1 -1
  135. package/file-download/file-list-card/file-list-card.js.map +1 -1
  136. package/file-upload/custom-sensors.d.ts +2 -2
  137. package/file-upload/custom-sensors.js.map +1 -1
  138. package/file-upload/file-list/file-list.js.map +1 -1
  139. package/filter/addons/filter-item-checkbox-helpers.js.map +1 -1
  140. package/filter/addons/filter-item-checkbox.js.map +1 -1
  141. package/filter/addons/filter-item-checkbox.styles.js +1 -1
  142. package/filter/addons/filter-item-checkbox.styles.js.map +1 -1
  143. package/{cjs/filter/addons/filter-item-checkbox.styles_7degw7.css → filter/addons/filter-item-checkbox.styles_6nbpdl.css} +1 -1
  144. package/filter/filter-item.js.map +1 -1
  145. package/filter/filter-item.styles.js +1 -1
  146. package/filter/filter-item.styles.js.map +1 -1
  147. package/filter/{filter-item.styles_cphjnj.css → filter-item.styles_1yixp8h.css} +1 -1
  148. package/filter/filter-modal.styles.js +1 -1
  149. package/filter/filter-modal.styles.js.map +1 -1
  150. package/{cjs/filter/filter-modal.styles_qw3xlg.css → filter/filter-modal.styles_kuth21.css} +1 -1
  151. package/filter/filter-sidebar.styles.js +1 -1
  152. package/filter/filter-sidebar.styles.js.map +1 -1
  153. package/filter/{filter-sidebar.styles_1otwpjl.css → filter-sidebar.styles_rbfq4f.css} +1 -1
  154. package/filter/filter.js.map +1 -1
  155. package/form/form-textarea/form-textarea.js +1 -1
  156. package/form/form-textarea/form-textarea.js.map +1 -1
  157. package/fullscreen-image-carousel/stateful-image.js.map +1 -1
  158. package/fullscreen-image-carousel/types.d.ts +2 -2
  159. package/histogram-slider/histogram-slider.js.map +1 -1
  160. package/input-range-slider/input-range-slider.js.map +1 -1
  161. package/input-slider/input-slider.js.map +1 -1
  162. package/input-textarea/textarea-counter.js.map +1 -1
  163. package/input-textarea/textarea.js +1 -1
  164. package/input-textarea/textarea.js.map +1 -1
  165. package/input-textarea/textarea.styles.d.ts +2 -0
  166. package/input-textarea/textarea.styles.js +1 -1
  167. package/input-textarea/textarea.styles.js.map +1 -1
  168. package/input-textarea/{textarea.styles_q77m5x.css → textarea.styles_6we7bs.css} +3 -1
  169. package/input-textarea/types.d.ts +7 -0
  170. package/modal/index.d.ts +4 -0
  171. package/modal/index.js.map +1 -1
  172. package/modal/types.d.ts +2 -0
  173. package/modal-v2/modal-v2.d.ts +6 -0
  174. package/modal-v2/modal-v2.js +1 -1
  175. package/modal-v2/modal-v2.js.map +1 -1
  176. package/modal-v2/slots/card.js +1 -1
  177. package/modal-v2/slots/card.js.map +1 -1
  178. package/modal-v2/slots/header.d.ts +5 -0
  179. package/modal-v2/slots/header.js +2 -0
  180. package/modal-v2/slots/header.js.map +1 -0
  181. package/modal-v2/slots/index.d.ts +1 -0
  182. package/modal-v2/slots/slot-styles.d.ts +4 -0
  183. package/modal-v2/slots/slot-styles.js +1 -1
  184. package/modal-v2/slots/slot-styles.js.map +1 -1
  185. package/modal-v2/slots/{slot-styles_13sd376.css → slot-styles_10ut1v7.css} +5 -1
  186. package/modal-v2/types.d.ts +21 -0
  187. package/navbar/navbar-items.js +1 -1
  188. package/navbar/navbar-items.js.map +1 -1
  189. package/navbar/navbar-items.styles.d.ts +2 -1
  190. package/navbar/navbar-items.styles.js +1 -1
  191. package/navbar/navbar-items.styles.js.map +1 -1
  192. package/navbar/{navbar-items.styles_1i3ltot.css → navbar-items.styles_1b1zx.css} +9 -8
  193. package/navbar/navbar-logo-data.js +1 -1
  194. package/navbar/navbar-logo-data.js.map +1 -1
  195. package/navbar/navbar.js.map +1 -1
  196. package/otp-input/otp-input.js.map +1 -1
  197. package/overlay/overlay.js.map +1 -1
  198. package/package.json +1 -1
  199. package/pagination/pagination.js.map +1 -1
  200. package/phone-number-input/phone-number-input.js.map +1 -1
  201. package/predictive-text-input/predictive-text-input.js.map +1 -1
  202. package/shared/dropdown-list/dropdown-list.js.map +1 -1
  203. package/shared/internal-calendar/calendar-manager.js.map +1 -1
  204. package/shared/standalone-date-input/standalone-date-input.js.map +1 -1
  205. package/singpass-button/singpass-button.styles.js +1 -1
  206. package/singpass-button/singpass-button.styles.js.map +1 -1
  207. package/singpass-button/{singpass-button.styles_1miyex3.css → singpass-button.styles_1fj2lru.css} +1 -1
  208. package/smart-app-banner/smart-app-banner.styles.js +1 -1
  209. package/smart-app-banner/smart-app-banner.styles.js.map +1 -1
  210. package/smart-app-banner/{smart-app-banner.styles_15u6yv4.css → smart-app-banner.styles_r6yjq9.css} +1 -1
  211. package/theme/styles/a11y-playground.css +358 -87
  212. package/theme/styles/bookingsg.css +391 -85
  213. package/theme/styles/careercompass.css +396 -90
  214. package/theme/styles/ccube.css +402 -45
  215. package/theme/styles/imda.css +348 -85
  216. package/theme/styles/lifesg.css +377 -87
  217. package/theme/styles/mylegacy.css +382 -85
  218. package/theme/styles/oneservice.css +349 -86
  219. package/theme/styles/pa.css +172 -104
  220. package/theme/styles/rbs.css +403 -46
  221. package/theme/styles/sgw-digital-lobby.css +352 -87
  222. package/theme/styles/smgs.css +401 -95
  223. package/theme/styles/spf.css +208 -99
  224. package/theme/styles/supportgowhere.css +387 -90
  225. package/theme/styles/tote-board.css +816 -0
  226. package/theme/types.d.ts +1 -1
  227. package/theme/types.js +1 -1
  228. package/theme/types.js.map +1 -1
  229. package/time-slot-bar/time-slot-bar.js.map +1 -1
  230. package/time-slot-bar-week/time-slot-bar-week-days.js.map +1 -1
  231. package/timetable/timetable.js.map +1 -1
  232. package/toast/toast.styles.js +1 -1
  233. package/toast/toast.styles.js.map +1 -1
  234. package/{cjs/toast/toast.styles_1e0l60w.css → toast/toast.styles_57j3lf.css} +1 -1
  235. package/toggle/toggle-icon.d.ts +2 -1
  236. package/toggle/toggle-icon.js +1 -1
  237. package/toggle/toggle-icon.js.map +1 -1
  238. package/toggle/toggle-icon.styles.d.ts +1 -0
  239. package/toggle/toggle-icon.styles.js +1 -1
  240. package/toggle/toggle-icon.styles.js.map +1 -1
  241. package/toggle/{toggle-icon.styles_ba71y.css → toggle-icon.styles_o6dxr3.css} +3 -2
  242. package/toggle/toggle.js +1 -1
  243. package/toggle/toggle.js.map +1 -1
  244. package/toggle/toggle.styles.d.ts +18 -15
  245. package/toggle/toggle.styles.js +1 -1
  246. package/toggle/toggle.styles.js.map +1 -1
  247. package/toggle/toggle.styles_11tmfsr.css +41 -0
  248. package/tools/storybook-argtypes/services/arg-types-row-builder.d.ts +6 -0
  249. package/tools/storybook-argtypes/types.d.ts +13 -1
  250. package/unit-number/unit-number-input.js.map +1 -1
  251. package/util/is-styled-components.js.map +1 -1
  252. package/util/string-helper.d.ts +2 -2
  253. package/util/string-helper.js +1 -1
  254. package/util/string-helper.js.map +1 -1
  255. package/util/use-event-listener.js.map +1 -1
  256. package/util/use-state-actions.js.map +1 -1
  257. package/button/button.styles_1pxy0co.css +0 -20
  258. package/cjs/button/button.styles_1pxy0co.css +0 -20
  259. package/cjs/form/form-textarea/form-textarea.styles.js +0 -2
  260. package/cjs/form/form-textarea/form-textarea.styles.js.map +0 -1
  261. package/cjs/form/form-textarea/form-textarea.styles_1kneyif.css +0 -3
  262. package/cjs/toggle/toggle.styles_1t5qrye.css +0 -38
  263. package/form/form-textarea/form-textarea.styles.d.ts +0 -3
  264. package/form/form-textarea/form-textarea.styles.js +0 -2
  265. package/form/form-textarea/form-textarea.styles.js.map +0 -1
  266. package/form/form-textarea/form-textarea.styles_1kneyif.css +0 -3
  267. package/theme/styles/default.css +0 -158
  268. package/toggle/toggle.styles_1t5qrye.css +0 -38
package/theme/types.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { ValueOf } from "../util/utility-types";
2
2
  import type { Border, Breakpoint, ComponentToken, FontSpec, Motion, PrimitiveColours, Radius, SemanticColours, Shadow, Spacing } from "./tokens";
3
3
  /** All available theme names. */
4
- export declare const THEME_TYPES: readonly ["a11y-playground", "bookingsg", "careercompass", "ccube", "imda", "lifesg", "mylegacy", "oneservice", "pa", "rbs", "sgw-digital-lobby", "smgs", "spf", "supportgowhere"];
4
+ export declare const THEME_TYPES: readonly ["a11y-playground", "bookingsg", "careercompass", "ccube", "imda", "lifesg", "mylegacy", "oneservice", "pa", "rbs", "sgw-digital-lobby", "smgs", "spf", "supportgowhere", "tote-board"];
5
5
  /** Union of all supported theme names. */
6
6
  export type ThemeType = (typeof THEME_TYPES)[number];
7
7
  /**
package/theme/types.js CHANGED
@@ -1,2 +1,2 @@
1
- const s=["a11y-playground","bookingsg","careercompass","ccube","imda","lifesg","mylegacy","oneservice","pa","rbs","sgw-digital-lobby","smgs","spf","supportgowhere"];export{s as THEME_TYPES};
1
+ const e=["a11y-playground","bookingsg","careercompass","ccube","imda","lifesg","mylegacy","oneservice","pa","rbs","sgw-digital-lobby","smgs","spf","supportgowhere","tote-board"];export{e as THEME_TYPES};
2
2
  //# sourceMappingURL=types.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sources":["../../src/theme/types.ts"],"sourcesContent":["import type { ValueOf } from \"../util/utility-types\";\nimport type {\n Border,\n Breakpoint,\n ComponentToken,\n FontSpec,\n Motion,\n PrimitiveColours,\n Radius,\n SemanticColours,\n Shadow,\n Spacing,\n} from \"./tokens\";\n\n/** All available theme names. */\nexport const THEME_TYPES = [\n \"a11y-playground\",\n \"bookingsg\",\n \"careercompass\",\n \"ccube\",\n \"imda\",\n \"lifesg\",\n \"mylegacy\",\n \"oneservice\",\n \"pa\",\n \"rbs\",\n \"sgw-digital-lobby\",\n \"smgs\",\n \"spf\",\n \"supportgowhere\",\n] as const;\n\n/** Union of all supported theme names. */\nexport type ThemeType = (typeof THEME_TYPES)[number];\n/**\n * Colour mode accepted by `ThemeProvider`.\n * — `\"auto\"` follows OS preference.\n * - `\"light\"` light mode.\n * - `\"dark\"` dark mode.\n */\nexport type ThemeMode = \"light\" | \"dark\" | \"auto\";\n\n/** Resolved colour mode. */\nexport type ResolvedThemeMode = \"light\" | \"dark\";\n\n/** Available typography size presets. */\nexport type FontSize =\n | \"heading-xxl\"\n | \"heading-xl\"\n | \"heading-lg\"\n | \"heading-md\"\n | \"heading-sm\"\n | \"heading-xs\"\n | \"body-baseline\"\n | \"body-md\"\n | \"body-sm\"\n | \"body-xs\"\n | \"form-label\"\n | \"form-description\";\n\n/** Available typography weight presets. */\nexport type FontWeight = \"light\" | \"regular\" | \"semibold\" | \"bold\";\n\n/** Accepted breakpoint CSS variable strings. */\nexport type BreakpointCSSVariableString = ValueOf<typeof Breakpoint>;\n/** Accepted border CSS variable strings. */\nexport type BorderCSSVariableString = ValueOf<typeof Border>;\n/** Accepted component-scoped CSS variable strings. */\nexport type ComponentTokenCSSVariableString =\n | ValueOf<typeof ComponentToken.Animation>\n | ValueOf<typeof ComponentToken.Button>\n | ValueOf<typeof ComponentToken.Footer>\n | ValueOf<typeof ComponentToken.Navbar>;\n/** Accepted font CSS variable strings. */\nexport type FontSpecCSSVariableString = ValueOf<typeof FontSpec>;\n/** Accepted motion CSS variable strings. */\nexport type MotionCSSVariableString = ValueOf<typeof Motion>;\n/** Accepted primitive colour CSS variable strings. */\nexport type PrimitiveColourCSSVariableString = ValueOf<typeof PrimitiveColours>;\n/** Accepted radius CSS variable strings. */\nexport type RadiusCSSVariableString = ValueOf<typeof Radius>;\n/** Accepted semantic colour CSS variable strings. */\nexport type SemanticColourCSSVariableString = ValueOf<typeof SemanticColours>;\n/** Accepted colour CSS variable strings. */\nexport type ColourCSSVariableString =\n | PrimitiveColourCSSVariableString\n | SemanticColourCSSVariableString;\n/** Accepted shadow CSS variable strings. */\nexport type ShadowCSSVariableString = ValueOf<typeof Shadow>;\n/** Accepted spacing and layout CSS variable strings. */\nexport type SpacingCSSVariableString = ValueOf<typeof Spacing>;\n\n/** Union of all design-token CSS variable string literals. */\nexport type CSSVariableString =\n | BreakpointCSSVariableString\n | BorderCSSVariableString\n | ColourCSSVariableString\n | ComponentTokenCSSVariableString\n | FontSpecCSSVariableString\n | MotionCSSVariableString\n | RadiusCSSVariableString\n | ShadowCSSVariableString\n | SpacingCSSVariableString;\n"],"names":["THEME_TYPES"],"mappings":"AAeO,MAAMA,EAAc,CACvB,kBACA,YACA,gBACA,QACA,OACA,SACA,WACA,aACA,KACA,MACA,oBACA,OACA,MACA"}
1
+ {"version":3,"file":"types.js","sources":["../../src/theme/types.ts"],"sourcesContent":["import type { ValueOf } from \"../util/utility-types\";\nimport type {\n Border,\n Breakpoint,\n ComponentToken,\n FontSpec,\n Motion,\n PrimitiveColours,\n Radius,\n SemanticColours,\n Shadow,\n Spacing,\n} from \"./tokens\";\n\n/** All available theme names. */\nexport const THEME_TYPES = [\n \"a11y-playground\",\n \"bookingsg\",\n \"careercompass\",\n \"ccube\",\n \"imda\",\n \"lifesg\",\n \"mylegacy\",\n \"oneservice\",\n \"pa\",\n \"rbs\",\n \"sgw-digital-lobby\",\n \"smgs\",\n \"spf\",\n \"supportgowhere\",\n \"tote-board\",\n] as const;\n\n/** Union of all supported theme names. */\nexport type ThemeType = (typeof THEME_TYPES)[number];\n/**\n * Colour mode accepted by `ThemeProvider`.\n * — `\"auto\"` follows OS preference.\n * - `\"light\"` light mode.\n * - `\"dark\"` dark mode.\n */\nexport type ThemeMode = \"light\" | \"dark\" | \"auto\";\n\n/** Resolved colour mode. */\nexport type ResolvedThemeMode = \"light\" | \"dark\";\n\n/** Available typography size presets. */\nexport type FontSize =\n | \"heading-xxl\"\n | \"heading-xl\"\n | \"heading-lg\"\n | \"heading-md\"\n | \"heading-sm\"\n | \"heading-xs\"\n | \"body-baseline\"\n | \"body-md\"\n | \"body-sm\"\n | \"body-xs\"\n | \"form-label\"\n | \"form-description\";\n\n/** Available typography weight presets. */\nexport type FontWeight = \"light\" | \"regular\" | \"semibold\" | \"bold\";\n\n/** Accepted breakpoint CSS variable strings. */\nexport type BreakpointCSSVariableString = ValueOf<typeof Breakpoint>;\n/** Accepted border CSS variable strings. */\nexport type BorderCSSVariableString = ValueOf<typeof Border>;\n/** Accepted component-scoped CSS variable strings. */\nexport type ComponentTokenCSSVariableString =\n | ValueOf<typeof ComponentToken.Animation>\n | ValueOf<typeof ComponentToken.Button>\n | ValueOf<typeof ComponentToken.Footer>\n | ValueOf<typeof ComponentToken.Navbar>;\n/** Accepted font CSS variable strings. */\nexport type FontSpecCSSVariableString = ValueOf<typeof FontSpec>;\n/** Accepted motion CSS variable strings. */\nexport type MotionCSSVariableString = ValueOf<typeof Motion>;\n/** Accepted primitive colour CSS variable strings. */\nexport type PrimitiveColourCSSVariableString = ValueOf<typeof PrimitiveColours>;\n/** Accepted radius CSS variable strings. */\nexport type RadiusCSSVariableString = ValueOf<typeof Radius>;\n/** Accepted semantic colour CSS variable strings. */\nexport type SemanticColourCSSVariableString = ValueOf<typeof SemanticColours>;\n/** Accepted colour CSS variable strings. */\nexport type ColourCSSVariableString =\n | PrimitiveColourCSSVariableString\n | SemanticColourCSSVariableString;\n/** Accepted shadow CSS variable strings. */\nexport type ShadowCSSVariableString = ValueOf<typeof Shadow>;\n/** Accepted spacing and layout CSS variable strings. */\nexport type SpacingCSSVariableString = ValueOf<typeof Spacing>;\n\n/** Union of all design-token CSS variable string literals. */\nexport type CSSVariableString =\n | BreakpointCSSVariableString\n | BorderCSSVariableString\n | ColourCSSVariableString\n | ComponentTokenCSSVariableString\n | FontSpecCSSVariableString\n | MotionCSSVariableString\n | RadiusCSSVariableString\n | ShadowCSSVariableString\n | SpacingCSSVariableString;\n"],"names":["THEME_TYPES"],"mappings":"AAeO,MAAMA,EAAc,CACvB,kBACA,YACA,gBACA,QACA,OACA,SACA,WACA,aACA,KACA,MACA,oBACA,OACA,MACA,iBACA"}
@@ -1 +1 @@
1
- {"version":3,"file":"time-slot-bar.js","sources":["../../src/time-slot-bar/time-slot-bar.tsx"],"sourcesContent":["import { ChevronLeftIcon } from \"@lifesg/react-icons/chevron-left\";\nimport { ChevronRightIcon } from \"@lifesg/react-icons/chevron-right\";\nimport clsx from \"clsx\";\nimport dayjs, { type Dayjs } from \"dayjs\";\nimport {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { useResizeDetector } from \"react-resize-detector\";\n\nimport { VisuallyHidden } from \"../shared/accessibility\";\nimport { ClickableIcon } from \"../shared/clickable-icon\";\nimport { TimeSlot } from \"../shared/time-slot\";\nimport { Typography } from \"../typography\";\nimport { TimeHelper } from \"../util/time-helper\";\nimport { getCellWidth, TimeSlotBarHelper } from \"./helper\";\nimport type { Direction } from \"./internal-types\";\nimport * as styles from \"./time-slot-bar.styles\";\nimport TimeSlotDivider from \"./time-slot-divider\";\nimport TimeSlotItem from \"./time-slot-item\";\nimport type {\n TimeSlot as TTimeSlot,\n TimeSlotBarProps,\n TimeSlotBarRef,\n TimeSlotBarVariant,\n} from \"./types\";\n\nconst CELL_DURATION = 30; // In minutes\n\nconst Component = (props: TimeSlotBarProps, ref: React.Ref<TimeSlotBarRef>) => {\n // =============================================================================\n // CONST, STATE, REF\n // =============================================================================\n const {\n \"data-testid\": testId,\n className,\n variant = \"default\",\n startTime,\n endTime,\n slots,\n onSlotClick,\n onClick,\n styleAttributes,\n initialScrollTime,\n roundInitialScrollTime = true,\n } = props;\n const barRef = useRef<HTMLDivElement>(null);\n const [scrollPosition, setScrollPosition] = useState<number>(0);\n const [clientWidth, setClientWidth] = useState<number>(0);\n const cellWidth = getCellWidth(variant);\n\n const adjustedStartTime = TimeSlotBarHelper.adjustTimeForMarker(\n startTime,\n \"start\"\n );\n const adjustedEndTime = TimeSlotBarHelper.adjustTimeForMarker(\n endTime,\n \"end\"\n );\n\n const SCROLL_INCREMENT = cellWidth * 2.5; // In px. Each scroll increment corresponds to 75mins\n\n // Expose imperative handle\n useImperativeHandle(ref, () => ({\n resetScroll,\n }));\n\n const { summary: slotsSummary, computedSlots: allSlots } = useMemo(() => {\n return TimeSlotBarHelper.processSlots(\n { start: adjustedStartTime, end: adjustedEndTime },\n slots,\n variant\n );\n }, [adjustedStartTime, adjustedEndTime, slots, variant]);\n\n // =============================================================================\n // EFFECTS\n // =============================================================================\n\n useEffect(() => {\n const container = barRef.current;\n if (container) {\n container.addEventListener(\"scroll\", handleScroll);\n // Initialize clientWidth on mount\n setClientWidth(container.clientWidth);\n }\n\n return () => {\n if (container) {\n container.removeEventListener(\"scroll\", handleScroll);\n }\n };\n }, []);\n\n // Initial scroll on mount and when dependencies change\n useEffect(() => {\n resetScroll();\n }, [initialScrollTime, cellWidth, startTime, endTime]);\n\n // =============================================================================\n // EVENT HANDLERS\n // =============================================================================\n\n const handleScroll = () => {\n if (barRef.current) {\n setScrollPosition(barRef.current.scrollLeft);\n }\n };\n\n const handleArrowButtonClick = (direction: Direction) => {\n if (barRef.current) {\n barRef.current.scrollBy({\n left:\n direction === \"left\" ? -SCROLL_INCREMENT : SCROLL_INCREMENT,\n behavior: \"smooth\",\n });\n }\n };\n\n const handleResize = () => {\n if (barRef.current) {\n setClientWidth(barRef.current.clientWidth);\n }\n };\n\n useResizeDetector({\n onResize: handleResize,\n targetRef: barRef,\n refreshMode: \"debounce\",\n refreshRate: 50,\n });\n\n // ===========================================================================\n // HELPER FUNCTIONS\n // ===========================================================================\n /**\n * NOTE: Special condition\n * Show ellipsis only if the slotWidth is smaller or equal to\n * 15min interval\n */\n const showFullEllipsis = (slotWidth: number) => {\n return slotWidth <= cellWidth / 2;\n };\n\n const getDataTestId = (subStr: string) => {\n if (!testId) return undefined;\n return `${testId}-${subStr}`;\n };\n\n /**\n * Function to determine if a long time marker should be displayed\n * For minified variant the long time marker is displayed every alternate hour\n */\n const shouldDisplayLongTimeMarker = (\n currentTime: Dayjs,\n isStartHourEven: boolean, // To determine if the first hour is even or odd for minified\n variant: TimeSlotBarVariant\n ) => {\n const isHour = currentTime.minute() === 0;\n\n if (variant === \"default\") {\n return isHour;\n }\n\n const isAlternateHour =\n isHour &&\n (isStartHourEven\n ? currentTime.hour() % 2 === 0\n : currentTime.hour() % 2 === 1);\n\n return isAlternateHour;\n };\n\n // Function to reset scroll to initialScrollTime\n const resetScroll = () => {\n if (initialScrollTime && barRef.current && cellWidth > 0) {\n const scrollPosition = TimeHelper.calculateScrollPosition({\n scrollTime: initialScrollTime,\n minTime: adjustedStartTime,\n maxTime: adjustedEndTime,\n interval: CELL_DURATION,\n intervalWidth: cellWidth,\n options: {\n roundToInterval: roundInitialScrollTime,\n },\n });\n\n if (scrollPosition !== null) {\n barRef.current.scrollLeft = scrollPosition;\n }\n }\n };\n\n const isVariant = (v: TimeSlotBarVariant) => {\n if (variant === v) return true;\n return false;\n };\n\n // =============================================================================\n // RENDER FUNCTIONS\n // =============================================================================\n\n // Render time markers\n const renderTimeMarkers = () => {\n const timeMarkers = [];\n const startTimeFormatted = dayjs(adjustedStartTime, \"HH:mm\");\n const endTimeFormatted = dayjs(adjustedEndTime, \"HH:mm\");\n\n const isStartHourEven = startTimeFormatted.hour() % 2 === 0;\n\n for (\n let currentTime = startTimeFormatted;\n currentTime.isBefore(endTimeFormatted);\n currentTime = currentTime.add(CELL_DURATION, \"minute\")\n ) {\n const displayLongTimeMarker = shouldDisplayLongTimeMarker(\n currentTime,\n isStartHourEven,\n variant\n );\n\n timeMarkers.push(\n <div\n data-testid={`${currentTime.format(\"HH:mm\")}-marker`}\n key={currentTime.format(\"HH:mm\")}\n className={clsx(\n styles.timeMarker,\n variant === \"default\"\n ? styles.timeMarkerDefault\n : styles.timeMarkerMinified,\n displayLongTimeMarker && styles.timeMarkerLong\n )}\n >\n {displayLongTimeMarker && (\n <Typography.BodyXS\n className={styles.timeLabel}\n weight=\"semibold\"\n >\n {TimeSlotBarHelper.formatHourlyDisplay(\n currentTime.format(\"HH:mm\")\n )}\n </Typography.BodyXS>\n )}\n </div>\n );\n }\n return timeMarkers;\n };\n\n // Render default time slot (aka background)\n const renderDefaultTimeSlots = () => {\n if (!styleAttributes) return;\n\n const {\n backgroundColor,\n backgroundColor2,\n hoverBackgroundColor,\n hoverBackgroundColor2,\n styleType = \"default\",\n } = styleAttributes;\n\n const slotWidth = TimeSlotBarHelper.calculateWidth(\n adjustedStartTime,\n adjustedEndTime,\n cellWidth\n );\n\n const isClickable = !!onClick && isVariant(\"default\");\n\n return (\n <>\n <TimeSlotDivider variant={variant} />\n <TimeSlotItem\n key={\"default-timeslot\"}\n variant={variant}\n dataTestId={getDataTestId(\"default-timeslot\")}\n slotWidth={slotWidth}\n slotOffset={0}\n onClick={isClickable ? onClick : undefined}\n >\n <TimeSlot\n className={styles.timeSlotItemContent}\n styleType={styleType}\n bgColor={backgroundColor}\n bgColor2={backgroundColor2}\n hoverBgColor={hoverBackgroundColor}\n hoverBgColor2={hoverBackgroundColor2}\n clickable={isClickable}\n />\n </TimeSlotItem>\n </>\n );\n };\n\n // Render time slots\n const renderTimeSlots = () => {\n return allSlots.map((slot) => {\n const {\n id,\n startTime: slotStartTime,\n endTime: slotEndTime,\n label,\n clickable = true,\n styleAttributes,\n ariaLabel,\n } = slot;\n\n const {\n color,\n styleType = \"default\",\n backgroundColor,\n backgroundColor2,\n hoverBackgroundColor,\n hoverBackgroundColor2,\n } = styleAttributes;\n\n const slotWidth = TimeSlotBarHelper.calculateWidth(\n slotStartTime,\n slotEndTime,\n cellWidth\n );\n const slotOffset = TimeSlotBarHelper.calculateWidth(\n adjustedStartTime,\n slotStartTime,\n cellWidth\n );\n\n const isClickable = clickable && variant === \"default\";\n const showLabel = isVariant(\"default\") && label;\n\n const handleSlotClick =\n (clickedSlot: TTimeSlot & { ariaLabel?: string }) =>\n (evt: React.MouseEvent<HTMLElement>) => {\n evt.stopPropagation();\n\n const { ariaLabel: _, ...slot } = clickedSlot;\n if (isClickable) onSlotClick(slot);\n };\n\n return (\n <div key={id} role=\"gridcell\">\n <TimeSlotDivider\n variant={variant}\n left={slotOffset}\n dataTestId=\"start-border\"\n />\n <TimeSlotItem\n variant={variant}\n dataTestId={getDataTestId(`${id}-timeslot`)}\n slotWidth={slotWidth}\n slotOffset={slotOffset}\n color={color}\n onClick={handleSlotClick(slot)}\n >\n <TimeSlot\n className={styles.timeSlotItemContent}\n styleType={styleType}\n bgColor={backgroundColor}\n bgColor2={backgroundColor2}\n hoverBgColor={hoverBackgroundColor}\n hoverBgColor2={hoverBackgroundColor2}\n clickable={isClickable}\n >\n {!!ariaLabel && (\n <VisuallyHidden>\n <button\n type=\"button\" // overrides default type=\"submit\" when used with <form>\n aria-disabled={!isClickable}\n aria-label={ariaLabel}\n onClick={handleSlotClick(slot)} // use `button` element native keyboard activation handling\n />\n </VisuallyHidden>\n )}\n\n {showLabel && (\n <Typography.BodyXS\n className={styles.cellText}\n weight={\"semibold\"}\n aria-hidden={!ariaLabel}\n >\n {showFullEllipsis(slotWidth)\n ? \"...\"\n : label}\n </Typography.BodyXS>\n )}\n </TimeSlot>\n </TimeSlotItem>\n {endTime !== slotEndTime && (\n <TimeSlotDivider\n variant={variant}\n dataTestId=\"end-border\"\n left={slotOffset + slotWidth}\n />\n )}\n </div>\n );\n });\n };\n\n const renderArrowButton = (direction: Direction) => {\n const isLeft = direction === \"left\";\n const totalScrollableWidth = TimeSlotBarHelper.calculateWidth(\n adjustedStartTime,\n adjustedEndTime,\n cellWidth\n );\n const hasContentOnLeft = scrollPosition > 0;\n const hasContentOnRight =\n scrollPosition + clientWidth < totalScrollableWidth;\n const shouldShowArrow = isLeft ? hasContentOnLeft : hasContentOnRight;\n\n if (!shouldShowArrow) return null;\n\n const directionClass = isLeft\n ? styles.arrowButtonLeft\n : styles.arrowButtonRight;\n const variantClass =\n variant === \"default\"\n ? styles.arrowButtonDefault\n : styles.arrowButtonMinified;\n const arrowIcon = isLeft ? (\n <ChevronLeftIcon className={styles.arrowIcon} />\n ) : (\n <ChevronRightIcon className={styles.arrowIcon} />\n );\n\n return (\n <ClickableIcon\n data-testid={getDataTestId(`arrow-${direction}`)}\n aria-hidden\n tabIndex={-1}\n className={clsx(\n styles.arrowButton,\n directionClass,\n variantClass\n )}\n focusHighlight={false}\n focusOutline=\"none\"\n onClick={() => {\n handleArrowButtonClick(direction);\n }}\n >\n {arrowIcon}\n </ClickableIcon>\n );\n };\n\n return (\n <div className={clsx(styles.container, className)}>\n <div\n data-testid={testId}\n ref={barRef}\n className={clsx(\n styles.timeSlotBarContainer,\n variant === \"default\"\n ? styles.containerDefault\n : styles.containerMinified\n )}\n >\n <div\n className={styles.timeMarkerWrapper}\n data-testid={getDataTestId(\"time-marker-wrapper\")}\n data-id=\"marker-wrapper\"\n >\n {renderTimeMarkers()}\n </div>\n <div role=\"grid\" aria-label={slotsSummary} tabIndex={0}>\n <div\n className={styles.timeSlotWrapper}\n data-testid={getDataTestId(\"time-slot-wrapper\")}\n data-id=\"slot-wrapper\"\n role=\"row\"\n >\n {renderDefaultTimeSlots()}\n {renderTimeSlots()}\n </div>\n </div>\n </div>\n {renderArrowButton(\"left\")}\n {renderArrowButton(\"right\")}\n </div>\n );\n};\n\n// @catalog\n/**\n * A horizontally scrollable timeline bar that visualises time slots across a\n * defined time range.\n *\n * Use `TimeSlotBar` to display availability, bookings, or schedule occupancy\n * as coloured cells on a shared time axis.\n */\nexport const TimeSlotBar = forwardRef<TimeSlotBarRef, TimeSlotBarProps>(\n Component\n);\n"],"names":["TimeSlotBar","forwardRef","props","ref","testId","className","variant","startTime","endTime","slots","onSlotClick","onClick","styleAttributes","initialScrollTime","roundInitialScrollTime","barRef","useRef","scrollPosition","setScrollPosition","useState","clientWidth","setClientWidth","cellWidth","getCellWidth","adjustedStartTime","TimeSlotBarHelper","adjustTimeForMarker","adjustedEndTime","SCROLL_INCREMENT","useImperativeHandle","resetScroll","summary","slotsSummary","computedSlots","allSlots","useMemo","processSlots","start","end","useEffect","container","current","addEventListener","handleScroll","removeEventListener","scrollLeft","useResizeDetector","onResize","targetRef","refreshMode","refreshRate","showFullEllipsis","slotWidth","getDataTestId","subStr","shouldDisplayLongTimeMarker","currentTime","isStartHourEven","isHour","minute","hour","TimeHelper","calculateScrollPosition","scrollTime","minTime","maxTime","interval","intervalWidth","options","roundToInterval","isVariant","v","renderArrowButton","direction","isLeft","totalScrollableWidth","calculateWidth","directionClass","styles.arrowButtonLeft","styles.arrowButtonRight","variantClass","styles.arrowButtonDefault","styles.arrowButtonMinified","arrowIcon","_jsx","ChevronLeftIcon","ChevronRightIcon","styles.arrowIcon","ClickableIcon","tabIndex","clsx","styles.arrowButton","focusHighlight","focusOutline","scrollBy","left","behavior","handleArrowButtonClick","children","_jsxs","styles.container","styles.timeSlotBarContainer","styles.containerDefault","styles.containerMinified","styles.timeMarkerWrapper","timeMarkers","startTimeFormatted","dayjs","endTimeFormatted","isBefore","add","displayLongTimeMarker","push","format","styles.timeMarker","styles.timeMarkerDefault","styles.timeMarkerMinified","styles.timeMarkerLong","Typography","BodyXS","styles.timeLabel","weight","formatHourlyDisplay","renderTimeMarkers","role","styles.timeSlotWrapper","backgroundColor","backgroundColor2","hoverBackgroundColor","hoverBackgroundColor2","styleType","isClickable","_Fragment","TimeSlotDivider","TimeSlotItem","dataTestId","slotOffset","undefined","TimeSlot","styles.timeSlotItemContent","bgColor","bgColor2","hoverBgColor","hoverBgColor2","clickable","renderDefaultTimeSlots","map","slot","id","slotStartTime","slotEndTime","label","ariaLabel","color","showLabel","handleSlotClick","clickedSlot","evt","stopPropagation","_","__rest","VisuallyHidden","type","styles.cellText"],"mappings":"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"}
1
+ {"version":3,"file":"time-slot-bar.js","sources":["../../src/time-slot-bar/time-slot-bar.tsx"],"sourcesContent":["import { ChevronLeftIcon } from \"@lifesg/react-icons/chevron-left\";\nimport { ChevronRightIcon } from \"@lifesg/react-icons/chevron-right\";\nimport clsx from \"clsx\";\nimport dayjs, { type Dayjs } from \"dayjs\";\nimport {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { useResizeDetector } from \"react-resize-detector\";\n\nimport { VisuallyHidden } from \"../shared/accessibility\";\nimport { ClickableIcon } from \"../shared/clickable-icon\";\nimport { TimeSlot } from \"../shared/time-slot\";\nimport { Typography } from \"../typography\";\nimport { TimeHelper } from \"../util/time-helper\";\nimport { getCellWidth, TimeSlotBarHelper } from \"./helper\";\nimport type { Direction } from \"./internal-types\";\nimport * as styles from \"./time-slot-bar.styles\";\nimport TimeSlotDivider from \"./time-slot-divider\";\nimport TimeSlotItem from \"./time-slot-item\";\nimport type {\n TimeSlot as TTimeSlot,\n TimeSlotBarProps,\n TimeSlotBarRef,\n TimeSlotBarVariant,\n} from \"./types\";\n\nconst CELL_DURATION = 30; // In minutes\n\nconst Component = (props: TimeSlotBarProps, ref: React.Ref<TimeSlotBarRef>) => {\n // =============================================================================\n // CONST, STATE, REF\n // =============================================================================\n const {\n \"data-testid\": testId,\n className,\n variant = \"default\",\n startTime,\n endTime,\n slots,\n onSlotClick,\n onClick,\n styleAttributes,\n initialScrollTime,\n roundInitialScrollTime = true,\n } = props;\n const barRef = useRef<HTMLDivElement>(null);\n const [scrollPosition, setScrollPosition] = useState<number>(0);\n const [clientWidth, setClientWidth] = useState<number>(0);\n const cellWidth = getCellWidth(variant);\n\n const adjustedStartTime = TimeSlotBarHelper.adjustTimeForMarker(\n startTime,\n \"start\"\n );\n const adjustedEndTime = TimeSlotBarHelper.adjustTimeForMarker(\n endTime,\n \"end\"\n );\n\n const SCROLL_INCREMENT = cellWidth * 2.5; // In px. Each scroll increment corresponds to 75mins\n\n // Expose imperative handle\n useImperativeHandle(ref, () => ({\n resetScroll,\n }));\n\n const { summary: slotsSummary, computedSlots: allSlots } = useMemo(() => {\n return TimeSlotBarHelper.processSlots(\n { start: adjustedStartTime, end: adjustedEndTime },\n slots,\n variant\n );\n }, [adjustedStartTime, adjustedEndTime, slots, variant]);\n\n // =============================================================================\n // EFFECTS\n // =============================================================================\n\n useEffect(() => {\n const container = barRef.current;\n if (container) {\n container.addEventListener(\"scroll\", handleScroll);\n // Initialize clientWidth on mount\n setClientWidth(container.clientWidth);\n }\n\n return () => {\n if (container) {\n container.removeEventListener(\"scroll\", handleScroll);\n }\n };\n }, []);\n\n // Initial scroll on mount and when dependencies change\n useEffect(() => {\n resetScroll();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [initialScrollTime, cellWidth, startTime, endTime]);\n\n // =============================================================================\n // EVENT HANDLERS\n // =============================================================================\n\n const handleScroll = () => {\n if (barRef.current) {\n setScrollPosition(barRef.current.scrollLeft);\n }\n };\n\n const handleArrowButtonClick = (direction: Direction) => {\n if (barRef.current) {\n barRef.current.scrollBy({\n left:\n direction === \"left\" ? -SCROLL_INCREMENT : SCROLL_INCREMENT,\n behavior: \"smooth\",\n });\n }\n };\n\n const handleResize = () => {\n if (barRef.current) {\n setClientWidth(barRef.current.clientWidth);\n }\n };\n\n useResizeDetector({\n onResize: handleResize,\n targetRef: barRef,\n refreshMode: \"debounce\",\n refreshRate: 50,\n });\n\n // ===========================================================================\n // HELPER FUNCTIONS\n // ===========================================================================\n /**\n * NOTE: Special condition\n * Show ellipsis only if the slotWidth is smaller or equal to\n * 15min interval\n */\n const showFullEllipsis = (slotWidth: number) => {\n return slotWidth <= cellWidth / 2;\n };\n\n const getDataTestId = (subStr: string) => {\n if (!testId) return undefined;\n return `${testId}-${subStr}`;\n };\n\n /**\n * Function to determine if a long time marker should be displayed\n * For minified variant the long time marker is displayed every alternate hour\n */\n const shouldDisplayLongTimeMarker = (\n currentTime: Dayjs,\n isStartHourEven: boolean, // To determine if the first hour is even or odd for minified\n variant: TimeSlotBarVariant\n ) => {\n const isHour = currentTime.minute() === 0;\n\n if (variant === \"default\") {\n return isHour;\n }\n\n const isAlternateHour =\n isHour &&\n (isStartHourEven\n ? currentTime.hour() % 2 === 0\n : currentTime.hour() % 2 === 1);\n\n return isAlternateHour;\n };\n\n // Function to reset scroll to initialScrollTime\n const resetScroll = () => {\n if (initialScrollTime && barRef.current && cellWidth > 0) {\n const scrollPosition = TimeHelper.calculateScrollPosition({\n scrollTime: initialScrollTime,\n minTime: adjustedStartTime,\n maxTime: adjustedEndTime,\n interval: CELL_DURATION,\n intervalWidth: cellWidth,\n options: {\n roundToInterval: roundInitialScrollTime,\n },\n });\n\n if (scrollPosition !== null) {\n barRef.current.scrollLeft = scrollPosition;\n }\n }\n };\n\n const isVariant = (v: TimeSlotBarVariant) => {\n if (variant === v) return true;\n return false;\n };\n\n // =============================================================================\n // RENDER FUNCTIONS\n // =============================================================================\n\n // Render time markers\n const renderTimeMarkers = () => {\n const timeMarkers = [];\n const startTimeFormatted = dayjs(adjustedStartTime, \"HH:mm\");\n const endTimeFormatted = dayjs(adjustedEndTime, \"HH:mm\");\n\n const isStartHourEven = startTimeFormatted.hour() % 2 === 0;\n\n for (\n let currentTime = startTimeFormatted;\n currentTime.isBefore(endTimeFormatted);\n currentTime = currentTime.add(CELL_DURATION, \"minute\")\n ) {\n const displayLongTimeMarker = shouldDisplayLongTimeMarker(\n currentTime,\n isStartHourEven,\n variant\n );\n\n timeMarkers.push(\n <div\n data-testid={`${currentTime.format(\"HH:mm\")}-marker`}\n key={currentTime.format(\"HH:mm\")}\n className={clsx(\n styles.timeMarker,\n variant === \"default\"\n ? styles.timeMarkerDefault\n : styles.timeMarkerMinified,\n displayLongTimeMarker && styles.timeMarkerLong\n )}\n >\n {displayLongTimeMarker && (\n <Typography.BodyXS\n className={styles.timeLabel}\n weight=\"semibold\"\n >\n {TimeSlotBarHelper.formatHourlyDisplay(\n currentTime.format(\"HH:mm\")\n )}\n </Typography.BodyXS>\n )}\n </div>\n );\n }\n return timeMarkers;\n };\n\n // Render default time slot (aka background)\n const renderDefaultTimeSlots = () => {\n if (!styleAttributes) return;\n\n const {\n backgroundColor,\n backgroundColor2,\n hoverBackgroundColor,\n hoverBackgroundColor2,\n styleType = \"default\",\n } = styleAttributes;\n\n const slotWidth = TimeSlotBarHelper.calculateWidth(\n adjustedStartTime,\n adjustedEndTime,\n cellWidth\n );\n\n const isClickable = !!onClick && isVariant(\"default\");\n\n return (\n <>\n <TimeSlotDivider variant={variant} />\n <TimeSlotItem\n key={\"default-timeslot\"}\n variant={variant}\n dataTestId={getDataTestId(\"default-timeslot\")}\n slotWidth={slotWidth}\n slotOffset={0}\n onClick={isClickable ? onClick : undefined}\n >\n <TimeSlot\n className={styles.timeSlotItemContent}\n styleType={styleType}\n bgColor={backgroundColor}\n bgColor2={backgroundColor2}\n hoverBgColor={hoverBackgroundColor}\n hoverBgColor2={hoverBackgroundColor2}\n clickable={isClickable}\n />\n </TimeSlotItem>\n </>\n );\n };\n\n // Render time slots\n const renderTimeSlots = () => {\n return allSlots.map((slot) => {\n const {\n id,\n startTime: slotStartTime,\n endTime: slotEndTime,\n label,\n clickable = true,\n styleAttributes,\n ariaLabel,\n } = slot;\n\n const {\n color,\n styleType = \"default\",\n backgroundColor,\n backgroundColor2,\n hoverBackgroundColor,\n hoverBackgroundColor2,\n } = styleAttributes;\n\n const slotWidth = TimeSlotBarHelper.calculateWidth(\n slotStartTime,\n slotEndTime,\n cellWidth\n );\n const slotOffset = TimeSlotBarHelper.calculateWidth(\n adjustedStartTime,\n slotStartTime,\n cellWidth\n );\n\n const isClickable = clickable && variant === \"default\";\n const showLabel = isVariant(\"default\") && label;\n\n const handleSlotClick =\n (clickedSlot: TTimeSlot & { ariaLabel?: string }) =>\n (evt: React.MouseEvent<HTMLElement>) => {\n evt.stopPropagation();\n\n const { ariaLabel: _, ...slot } = clickedSlot;\n if (isClickable) onSlotClick(slot);\n };\n\n return (\n <div key={id} role=\"gridcell\">\n <TimeSlotDivider\n variant={variant}\n left={slotOffset}\n dataTestId=\"start-border\"\n />\n <TimeSlotItem\n variant={variant}\n dataTestId={getDataTestId(`${id}-timeslot`)}\n slotWidth={slotWidth}\n slotOffset={slotOffset}\n color={color}\n onClick={handleSlotClick(slot)}\n >\n <TimeSlot\n className={styles.timeSlotItemContent}\n styleType={styleType}\n bgColor={backgroundColor}\n bgColor2={backgroundColor2}\n hoverBgColor={hoverBackgroundColor}\n hoverBgColor2={hoverBackgroundColor2}\n clickable={isClickable}\n >\n {!!ariaLabel && (\n <VisuallyHidden>\n <button\n type=\"button\" // overrides default type=\"submit\" when used with <form>\n aria-disabled={!isClickable}\n aria-label={ariaLabel}\n onClick={handleSlotClick(slot)} // use `button` element native keyboard activation handling\n />\n </VisuallyHidden>\n )}\n\n {showLabel && (\n <Typography.BodyXS\n className={styles.cellText}\n weight={\"semibold\"}\n aria-hidden={!ariaLabel}\n >\n {showFullEllipsis(slotWidth)\n ? \"...\"\n : label}\n </Typography.BodyXS>\n )}\n </TimeSlot>\n </TimeSlotItem>\n {endTime !== slotEndTime && (\n <TimeSlotDivider\n variant={variant}\n dataTestId=\"end-border\"\n left={slotOffset + slotWidth}\n />\n )}\n </div>\n );\n });\n };\n\n const renderArrowButton = (direction: Direction) => {\n const isLeft = direction === \"left\";\n const totalScrollableWidth = TimeSlotBarHelper.calculateWidth(\n adjustedStartTime,\n adjustedEndTime,\n cellWidth\n );\n const hasContentOnLeft = scrollPosition > 0;\n const hasContentOnRight =\n scrollPosition + clientWidth < totalScrollableWidth;\n const shouldShowArrow = isLeft ? hasContentOnLeft : hasContentOnRight;\n\n if (!shouldShowArrow) return null;\n\n const directionClass = isLeft\n ? styles.arrowButtonLeft\n : styles.arrowButtonRight;\n const variantClass =\n variant === \"default\"\n ? styles.arrowButtonDefault\n : styles.arrowButtonMinified;\n const arrowIcon = isLeft ? (\n <ChevronLeftIcon className={styles.arrowIcon} />\n ) : (\n <ChevronRightIcon className={styles.arrowIcon} />\n );\n\n return (\n <ClickableIcon\n data-testid={getDataTestId(`arrow-${direction}`)}\n aria-hidden\n tabIndex={-1}\n className={clsx(\n styles.arrowButton,\n directionClass,\n variantClass\n )}\n focusHighlight={false}\n focusOutline=\"none\"\n onClick={() => {\n handleArrowButtonClick(direction);\n }}\n >\n {arrowIcon}\n </ClickableIcon>\n );\n };\n\n return (\n <div className={clsx(styles.container, className)}>\n <div\n data-testid={testId}\n ref={barRef}\n className={clsx(\n styles.timeSlotBarContainer,\n variant === \"default\"\n ? styles.containerDefault\n : styles.containerMinified\n )}\n >\n <div\n className={styles.timeMarkerWrapper}\n data-testid={getDataTestId(\"time-marker-wrapper\")}\n data-id=\"marker-wrapper\"\n >\n {renderTimeMarkers()}\n </div>\n <div role=\"grid\" aria-label={slotsSummary} tabIndex={0}>\n <div\n className={styles.timeSlotWrapper}\n data-testid={getDataTestId(\"time-slot-wrapper\")}\n data-id=\"slot-wrapper\"\n role=\"row\"\n >\n {renderDefaultTimeSlots()}\n {renderTimeSlots()}\n </div>\n </div>\n </div>\n {renderArrowButton(\"left\")}\n {renderArrowButton(\"right\")}\n </div>\n );\n};\n\n// @catalog\n/**\n * A horizontally scrollable timeline bar that visualises time slots across a\n * defined time range.\n *\n * Use `TimeSlotBar` to display availability, bookings, or schedule occupancy\n * as coloured cells on a shared time axis.\n */\nexport const TimeSlotBar = forwardRef<TimeSlotBarRef, TimeSlotBarProps>(\n Component\n);\n"],"names":["TimeSlotBar","forwardRef","props","ref","testId","className","variant","startTime","endTime","slots","onSlotClick","onClick","styleAttributes","initialScrollTime","roundInitialScrollTime","barRef","useRef","scrollPosition","setScrollPosition","useState","clientWidth","setClientWidth","cellWidth","getCellWidth","adjustedStartTime","TimeSlotBarHelper","adjustTimeForMarker","adjustedEndTime","SCROLL_INCREMENT","useImperativeHandle","resetScroll","summary","slotsSummary","computedSlots","allSlots","useMemo","processSlots","start","end","useEffect","container","current","addEventListener","handleScroll","removeEventListener","scrollLeft","useResizeDetector","onResize","targetRef","refreshMode","refreshRate","showFullEllipsis","slotWidth","getDataTestId","subStr","shouldDisplayLongTimeMarker","currentTime","isStartHourEven","isHour","minute","hour","TimeHelper","calculateScrollPosition","scrollTime","minTime","maxTime","interval","intervalWidth","options","roundToInterval","isVariant","v","renderArrowButton","direction","isLeft","totalScrollableWidth","calculateWidth","directionClass","styles.arrowButtonLeft","styles.arrowButtonRight","variantClass","styles.arrowButtonDefault","styles.arrowButtonMinified","arrowIcon","_jsx","ChevronLeftIcon","ChevronRightIcon","styles.arrowIcon","ClickableIcon","tabIndex","clsx","styles.arrowButton","focusHighlight","focusOutline","scrollBy","left","behavior","handleArrowButtonClick","children","_jsxs","styles.container","styles.timeSlotBarContainer","styles.containerDefault","styles.containerMinified","styles.timeMarkerWrapper","timeMarkers","startTimeFormatted","dayjs","endTimeFormatted","isBefore","add","displayLongTimeMarker","push","format","styles.timeMarker","styles.timeMarkerDefault","styles.timeMarkerMinified","styles.timeMarkerLong","Typography","BodyXS","styles.timeLabel","weight","formatHourlyDisplay","renderTimeMarkers","role","styles.timeSlotWrapper","backgroundColor","backgroundColor2","hoverBackgroundColor","hoverBackgroundColor2","styleType","isClickable","_Fragment","TimeSlotDivider","TimeSlotItem","dataTestId","slotOffset","undefined","TimeSlot","styles.timeSlotItemContent","bgColor","bgColor2","hoverBgColor","hoverBgColor2","clickable","renderDefaultTimeSlots","map","slot","id","slotStartTime","slotEndTime","label","ariaLabel","color","showLabel","handleSlotClick","clickedSlot","evt","stopPropagation","_","__rest","VisuallyHidden","type","styles.cellText"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"time-slot-bar-week-days.js","sources":["../../src/time-slot-bar-week/time-slot-bar-week-days.tsx"],"sourcesContent":["import { ChevronUpIcon } from \"@lifesg/react-icons\";\nimport { animated, useSpring } from \"@react-spring/web\";\nimport clsx from \"clsx\";\nimport type { Dayjs } from \"dayjs\";\nimport dayjs from \"dayjs\";\nimport isEmpty from \"lodash/isEmpty\";\nimport maxBy from \"lodash/maxBy\";\nimport minBy from \"lodash/minBy\";\nimport { useMemo, useRef, useState } from \"react\";\nimport { useResizeDetector } from \"react-resize-detector\";\n\nimport { Button } from \"../button\";\nimport type { InternalCalendarProps } from \"../shared/internal-calendar\";\nimport type { CellStyleProps } from \"../shared/internal-calendar/day-cell\";\nimport { DayCell } from \"../shared/internal-calendar/day-cell\";\nimport { Colour, formatUnitValue, useApplyStyle } from \"../theme\";\nimport type { TimeSlot } from \"../time-slot-bar/types\";\nimport { Typography } from \"../typography\";\nimport { DateHelper, useIsMounted } from \"../util\";\nimport { CalendarHelper } from \"../util/calendar-helper\";\nimport { StringHelper } from \"../util/string-helper\";\nimport { TimeHelper } from \"../util/time-helper\";\nimport { ROW_HEIGHT, SLOT_GAP } from \"./consts\";\nimport * as styles from \"./time-slot-bar-week-days.styles\";\nimport type { FocusableSlotMeta, TimeSlotCell } from \"./time-slot-cell\";\nimport { WeekTimeSlotCell } from \"./time-slot-cell\";\nimport type { TimeSlotCellsVariant } from \"./types\";\n\ninterface TimeSlotWeekDaysProps\n extends Pick<\n InternalCalendarProps,\n \"disabledDates\" | \"minDate\" | \"maxDate\"\n > {\n selectedDate: string | undefined;\n calendarDate: Dayjs;\n onSelect: (value: Dayjs) => void;\n slots: { [date: string]: TimeSlot[] };\n startTime?: string | undefined;\n endTime?: string | undefined;\n maxVisibleCellHeight?: number | undefined;\n interval?: number | undefined;\n variant?: TimeSlotCellsVariant | undefined;\n enableSelection?: boolean | undefined;\n onSlotClick?: ((date: string, timeSlot: TimeSlot) => void) | undefined;\n}\n\nexport const TimeSlotBarWeekDays = ({\n calendarDate,\n disabledDates,\n selectedDate,\n onSelect,\n minDate,\n maxDate,\n startTime,\n endTime,\n maxVisibleCellHeight,\n slots: daySlots,\n interval = 30,\n variant = \"flexible\",\n enableSelection = true,\n onSlotClick,\n}: TimeSlotWeekDaysProps) => {\n // =============================================================================\n // CONST, STATE, REF\n // =============================================================================\n const dateFormat = \"YYYY-MM-DD\";\n const [expandAll, setExpandAll] = useState<boolean>(false);\n const [hoverDay, setHoverDay] = useState<Dayjs>();\n const isMounted = useIsMounted();\n const slotButtonRefs = useRef<Record<string, HTMLButtonElement | null>>({});\n const timeColumnRef = useRef<HTMLDivElement>(null);\n const currentCalendarWeek = useMemo((): Dayjs[] => {\n return CalendarHelper.generateDaysForCurrentWeek(calendarDate);\n }, [calendarDate]);\n\n const flattenedSlots = Object.values(daySlots ?? {}).flat();\n const minStartTime =\n startTime ?? minBy(flattenedSlots, \"startTime\")?.startTime ?? \"00:00\";\n const maxEndTime =\n endTime ?? maxBy(flattenedSlots, \"endTime\")?.endTime ?? \"24:00\";\n const numberOfCells = Math.ceil(\n DateHelper.getTimeDiffInMinutes(minStartTime, maxEndTime) / interval\n );\n\n // React spring animation configuration\n const { height: actualHeight = 0, ref: cellsRef } = useResizeDetector();\n const hasCollapsedContent =\n !!maxVisibleCellHeight && actualHeight > maxVisibleCellHeight;\n const visibleRowCount =\n maxVisibleCellHeight !== undefined\n ? Math.max(\n 1,\n Math.floor((maxVisibleCellHeight + SLOT_GAP) / ROW_HEIGHT)\n )\n : 0;\n const collapsedHeight =\n visibleRowCount > 0\n ? visibleRowCount * ROW_HEIGHT - SLOT_GAP\n : maxVisibleCellHeight;\n const height = maxVisibleCellHeight\n ? !hasCollapsedContent || expandAll\n ? actualHeight\n : collapsedHeight\n : actualHeight;\n const expandableStyles = useSpring({ height });\n\n const generatedDaySlots = useMemo((): {\n [date: string]: TimeSlotCell[];\n } => {\n if (daySlots) {\n const transformedDaySlots: Record<string, TimeSlotCell[]> = {};\n Object.entries(daySlots).forEach(([key, slots]) => {\n const cellsArray = initializeAndFillSlots(slots);\n transformedDaySlots[key] = populateEmptyCells(cellsArray);\n });\n return transformedDaySlots;\n }\n return {};\n }, [daySlots]);\n\n const focusableSlotsByDate = useMemo(() => {\n return currentCalendarWeek.reduce<Record<string, FocusableSlotMeta[]>>(\n (result, day) => {\n const formattedDate = day.format(dateFormat);\n\n result[formattedDate] = getCellsForDate(formattedDate)\n .filter(\n (\n slot\n ): slot is TimeSlotCell & {\n isActualSlot: true;\n rowIndex: number;\n } => !!slot.isActualSlot && slot.rowIndex !== undefined\n )\n .map((slot) => ({\n key: `${formattedDate}-${slot.id}`,\n date: formattedDate,\n rowIndex: slot.rowIndex,\n }))\n .sort((a, b) => a.rowIndex - b.rowIndex);\n\n return result;\n },\n {}\n );\n }, [currentCalendarWeek, generatedDaySlots]);\n\n useApplyStyle(timeColumnRef, {\n [styles.tokens.timeColumn.height]: formatUnitValue(height, \"px\"),\n });\n\n // =============================================================================\n // EVENT HANDLERS\n // =============================================================================\n const handleDayClick = (value: Dayjs, isDisabled: boolean) => {\n if (isDisabled || !enableSelection) return;\n\n onSelect(value);\n };\n\n const handleDayHover = (value: Dayjs) => {\n setHoverDay(value);\n };\n\n const handleDayHoverClear = () => {\n setHoverDay(undefined);\n };\n\n const handleSlotClick = (date: string, slot: TimeSlot) => {\n onSlotClick?.(date, slot);\n };\n\n const handleSlotButtonClick =\n (date: string, slot: TimeSlot) =>\n (event: React.MouseEvent<HTMLButtonElement>) => {\n event.stopPropagation();\n\n handleSlotClick(date, slot);\n };\n\n const handleExpandCollapseClick = (event: React.MouseEvent) => {\n event.preventDefault();\n setExpandAll((prevExpandValue) => !prevExpandValue);\n };\n\n const handleSlotKeyDown = (\n event: React.KeyboardEvent<HTMLButtonElement>,\n currentSlot: FocusableSlotMeta\n ) => {\n const sameColumnSlots = focusableSlotsByDate[currentSlot.date] ?? [];\n\n const focusSlot = (slot?: FocusableSlotMeta) => {\n if (!slot) return;\n slotButtonRefs.current[slot.key]?.focus();\n };\n\n switch (event.key) {\n case \"ArrowRight\":\n case \"ArrowDown\": {\n event.preventDefault();\n const currentIndex = sameColumnSlots.findIndex(\n (slot) => slot.key === currentSlot.key\n );\n focusSlot(sameColumnSlots[currentIndex + 1]);\n break;\n }\n case \"ArrowLeft\":\n case \"ArrowUp\": {\n event.preventDefault();\n const currentIndex = sameColumnSlots.findIndex(\n (slot) => slot.key === currentSlot.key\n );\n focusSlot(sameColumnSlots[currentIndex - 1]);\n break;\n }\n case \"Home\":\n event.preventDefault();\n focusSlot(sameColumnSlots[0]);\n break;\n case \"End\":\n event.preventDefault();\n focusSlot(sameColumnSlots[sameColumnSlots.length - 1]);\n break;\n case \"PageUp\":\n event.preventDefault();\n focusSlot(sameColumnSlots[0]);\n break;\n case \"PageDown\": {\n event.preventDefault();\n if (hasCollapsedContent && !expandAll) {\n setExpandAll(true);\n const lastVisibleSlot = [...sameColumnSlots]\n .reverse()\n .find((slot) => slot.rowIndex < visibleRowCount);\n focusSlot(lastVisibleSlot ?? sameColumnSlots[0]);\n break;\n }\n\n focusSlot(sameColumnSlots[sameColumnSlots.length - 1]);\n break;\n }\n default:\n break;\n }\n };\n\n // =============================================================================\n // HELPER FUNCTIONS\n // =============================================================================\n const isDisabled = (day: Dayjs): boolean => {\n const isWithinRange = CalendarHelper.isWithinRange(\n day,\n minDate ? dayjs(minDate) : undefined,\n maxDate ? dayjs(maxDate) : undefined\n );\n\n const isDisabledDate =\n (disabledDates && disabledDates.includes(day.format(dateFormat))) ??\n false;\n\n return !isWithinRange || isDisabledDate;\n };\n\n const generateStyleProps = (day: Dayjs) => {\n const dateStartWithYear = day.format(dateFormat);\n const disabled = isDisabled(day);\n const dayCellStyleProps: CellStyleProps = {};\n const isHoverEnabled = enableSelection && !disabled;\n\n if (disabled) {\n dayCellStyleProps.disabled = true;\n }\n\n dayCellStyleProps.interactive = enableSelection\n ? isHoverEnabled\n : false;\n\n const isHover =\n isHoverEnabled && hoverDay && day.isSame(hoverDay, \"day\");\n const isSelected = [selectedDate].includes(dateStartWithYear);\n\n if (isSelected && isHover) {\n dayCellStyleProps.labelType = \"selected-hover\";\n dayCellStyleProps.circleLeft = \"selected-hover-outline\";\n dayCellStyleProps.circleRight = \"selected-hover-outline\";\n } else if (isSelected) {\n dayCellStyleProps.labelType = \"selected\";\n dayCellStyleProps.circleLeft = \"selected-outline\";\n dayCellStyleProps.circleRight = \"selected-outline\";\n } else if (isHover) {\n dayCellStyleProps.labelType = \"hover\";\n dayCellStyleProps.circleLeft = \"hover-subtle\";\n dayCellStyleProps.circleRight = \"hover-subtle\";\n }\n\n return dayCellStyleProps;\n };\n\n function generateFallbackCell(\n prefix: string | number,\n cellLength = 1\n ): TimeSlotCell {\n return {\n id: `${prefix.toString()}-${new Date().getTime()}`,\n startTime: \"\",\n endTime: \"\",\n clickable: false,\n isActualSlot: false,\n styleAttributes: {\n backgroundColor: Colour[\"bg-stronger\"],\n },\n cellLength,\n };\n }\n\n function getSlotAriaLabel(date: string, slot: TimeSlot) {\n const { startTime: slotStartTime, endTime: slotEndTime } = slot;\n\n return StringHelper.joinNonEmptyStrings([\n dayjs(date).format(\"D MMMM YYYY dddd\"),\n slotStartTime && slotEndTime\n ? TimeHelper.formatTimeRange(slotStartTime, slotEndTime)\n : undefined,\n slot.label,\n slot.clickable ?? true ? \"Available\" : \"Unavailable\",\n ]);\n }\n\n function initializeAndFillSlots(slots: TimeSlot[]): TimeSlotCell[] {\n const cellsArray = Array(numberOfCells).fill({});\n\n slots.forEach((slot) => {\n const startIndex = Math.max(\n 0,\n DateHelper.getTimeDiffInMinutes(minStartTime, slot.startTime) /\n interval\n );\n const endIndex = Math.min(\n numberOfCells,\n DateHelper.getTimeDiffInMinutes(minStartTime, slot.endTime) /\n interval\n );\n const cellLength = Math.ceil(endIndex) - Math.floor(startIndex);\n\n switch (variant) {\n case \"fixed\":\n // Keep fixed slots as 1 long cell\n cellsArray[Math.floor(startIndex)] = {\n ...slot,\n isActualSlot: true,\n rowIndex: Math.floor(startIndex),\n cellLength: endIndex - startIndex,\n };\n break;\n case \"flexible\":\n // Split long slot into multiple cells of length 1\n for (let i = 0; i < cellLength; i++) {\n const startTime = DateHelper.addMinutesToTime(\n minStartTime,\n Math.floor(startIndex + i) * interval\n );\n const endTime = DateHelper.addMinutesToTime(\n startTime,\n interval\n );\n\n let halfFill: \"default\" | \"top\" | \"bottom\";\n\n if (!isEmpty(cellsArray[Math.floor(startIndex + i)])) {\n // Previous cell extends into current cell, fill entire cell\n halfFill = \"default\";\n } else if (i === 0 && startIndex % 1 !== 0) {\n halfFill = \"top\";\n } else if (i === cellLength - 1 && endIndex % 1 !== 0) {\n halfFill = \"bottom\";\n } else {\n halfFill = \"default\";\n }\n\n cellsArray[Math.floor(startIndex + i)] = {\n ...slot,\n id: `${slot.id}-${i}`,\n startTime,\n endTime,\n isActualSlot: true,\n rowIndex: Math.floor(startIndex + i),\n cellLength: 1,\n halfFill,\n };\n }\n break;\n }\n });\n return cellsArray;\n }\n\n function populateEmptyCells(cellsArray: any[]): TimeSlotCell[] {\n let i = 0;\n switch (variant) {\n case \"fixed\":\n // Truncate consecutive empty cells into 1 [{},{},{...},{},{}] -> [{},{...},{}]\n cellsArray = cellsArray.reduce(\n (result, obj) => {\n const lastObj =\n result.length > 0\n ? result[result.length - 1]\n : result[0];\n if (isEmpty(obj) && isEmpty(lastObj)) {\n return result;\n }\n return [...result, obj];\n },\n [{}]\n );\n\n // Fill in empty cells with timings & corresponding cell lengths\n while (i < cellsArray.length) {\n if (isEmpty(cellsArray[i])) {\n const startTime =\n cellsArray[i - 1]?.endTime ?? minStartTime;\n const endTime =\n cellsArray[i + 1]?.startTime ?? maxEndTime;\n if (startTime !== endTime) {\n const cellLength =\n DateHelper.getTimeDiffInMinutes(\n startTime,\n endTime\n ) / interval;\n cellsArray[i] = {\n ...generateFallbackCell(i),\n startTime,\n endTime,\n cellLength,\n };\n }\n }\n i++;\n }\n break;\n case \"flexible\":\n // Fill in individual empty cell with interval timings\n while (i < cellsArray.length) {\n if (isEmpty(cellsArray[i])) {\n cellsArray[i] = generateFallbackCell(i);\n i++;\n } else {\n i += cellsArray[i].cellLength;\n }\n }\n break;\n }\n return cellsArray.filter(\n (slot) => !isEmpty(slot) && slot.cellLength > 0\n );\n }\n\n function getCellsForDate(formattedDate: string) {\n return (\n generatedDaySlots[formattedDate] ??\n Array(variant === \"flexible\" ? numberOfCells : 1)\n .fill(undefined)\n .map((_, index) =>\n generateFallbackCell(\n index,\n variant === \"fixed\" ? numberOfCells : undefined\n )\n )\n );\n }\n\n // =============================================================================\n // RENDER FUNCTIONS\n // =============================================================================\n const renderWeek = () => {\n return (\n <div className={styles.headerCellWeekColumn} role=\"row\">\n {currentCalendarWeek.map((day, index) => (\n <div\n key={`week-day-${index}`}\n className={styles.headerCellWeek}\n role=\"gridcell\"\n >\n <Typography.BodyXS\n weight={\"semibold\"}\n className={clsx(\n isDisabled(day) && styles.cellWeekTextDisabled\n )}\n >\n {dayjs(day).format(\"ddd\")}\n </Typography.BodyXS>\n </div>\n ))}\n </div>\n );\n };\n\n const renderHeader = () => {\n return (\n <div\n className={styles.headerCellWeekColumn}\n role=\"row\"\n onBlur={handleDayHoverClear}\n >\n {currentCalendarWeek.map((day, dayIndex) => {\n const dayCellStyleProps = generateStyleProps(day);\n\n return (\n <DayCell\n key={`day-${dayIndex}`}\n date={day}\n calendarDate={dayjs(selectedDate)}\n role=\"columnheader\"\n tabIndex={dayCellStyleProps.interactive ? 0 : -1}\n onSelect={() => {\n handleDayClick(\n day,\n !dayCellStyleProps.interactive\n );\n }}\n onHover={handleDayHover}\n onHoverEnd={handleDayHoverClear}\n onFocus={handleDayHover}\n {...dayCellStyleProps}\n ></DayCell>\n );\n })}\n </div>\n );\n };\n\n const renderTimeColumn = () => {\n let hasSetFirstAfternoonHour = false;\n\n const formatTime = (index: number) => {\n const parsedTime = dayjs(minStartTime, \"HH:mm\").add(\n index * 4 * interval,\n \"minutes\"\n );\n const hour = parsedTime.format(\"h\");\n const minutes = parsedTime.format(\"mm\");\n const amPm = parsedTime.format(\"a\");\n\n let formattedTime = `${hour}${\n minutes !== \"00\" ? ` ${minutes}` : \"\"\n }`;\n\n if (\n !hasSetFirstAfternoonHour &&\n amPm === \"pm\" &&\n minutes === \"00\"\n ) {\n formattedTime += ` ${amPm}`;\n hasSetFirstAfternoonHour = true;\n }\n\n return formattedTime;\n };\n\n return (\n <div className={styles.timeColumn} ref={timeColumnRef} aria-hidden>\n {Array(Math.ceil(numberOfCells / 4))\n .fill(undefined)\n .map((_, index) => (\n <div\n key={`time-${index}`}\n className={styles.timeColumnWrapper}\n >\n <div className={styles.timeColumnText}>\n {formatTime(index)}\n </div>\n </div>\n ))}\n </div>\n );\n };\n\n const renderSlotCell = (formattedDate: string, slot: TimeSlotCell) => {\n return (\n <WeekTimeSlotCell\n key={slot.id}\n formattedDate={formattedDate}\n slot={slot}\n variant={variant}\n hasCollapsedContent={hasCollapsedContent}\n expandAll={expandAll}\n visibleRowCount={visibleRowCount}\n onSlotClick={handleSlotClick}\n getSlotAriaLabel={getSlotAriaLabel}\n onSlotKeyDown={handleSlotKeyDown}\n onSlotButtonClick={handleSlotButtonClick}\n slotButtonRefs={slotButtonRefs}\n />\n );\n };\n\n const renderTimeSlotBarCells = () => {\n return (\n <animated.div\n className={styles.expandable}\n style={isMounted ? expandableStyles : undefined}\n role=\"row\"\n >\n <div\n className={styles.columnWeekCell}\n ref={cellsRef}\n key={`week-cell-${calendarDate.format(dateFormat)}`}\n >\n {currentCalendarWeek.map((day, dayIndex) => {\n const formattedDate = day.format(dateFormat);\n const cellsArray = getCellsForDate(formattedDate);\n return (\n <div\n key={`wrapper-${dayIndex}`}\n className={styles.timeSlotWrapper}\n role=\"gridcell\"\n >\n {cellsArray.map((slot) =>\n renderSlotCell(formattedDate, slot)\n )}\n </div>\n );\n })}\n </div>\n </animated.div>\n );\n };\n\n const renderCollapseExpandAll = () => {\n if (!maxVisibleCellHeight || !cellsRef.current) return;\n return (\n <div className={styles.collapseExpandAllWrapper}>\n <Button\n data-testid=\"time-bar-expand-collapse-button\"\n styleType=\"light\"\n sizeType=\"small\"\n aria-expanded={expandAll}\n onClick={handleExpandCollapseClick}\n className={styles.collapseExpandAllButton}\n >\n <span className={styles.collapseExpandAllButtonContent}>\n <ChevronUpIcon\n className={clsx(\n styles.chevronIcon,\n expandAll && styles.chevronIconExpanded\n )}\n />\n {`${expandAll ? \"Hide\" : \"Show\"} later times`}\n </span>\n </Button>\n </div>\n );\n };\n\n return (\n <div className={styles.wrapper}>\n <div className={styles.gridWrapper} role=\"grid\">\n {renderHeader()}\n {renderWeek()}\n {renderTimeColumn()}\n {renderTimeSlotBarCells()}\n </div>\n {renderCollapseExpandAll()}\n </div>\n );\n};\n"],"names":["TimeSlotBarWeekDays","calendarDate","disabledDates","selectedDate","onSelect","minDate","maxDate","startTime","endTime","maxVisibleCellHeight","slots","daySlots","interval","variant","enableSelection","onSlotClick","dateFormat","expandAll","setExpandAll","useState","hoverDay","setHoverDay","isMounted","useIsMounted","slotButtonRefs","useRef","timeColumnRef","currentCalendarWeek","useMemo","CalendarHelper","generateDaysForCurrentWeek","flattenedSlots","Object","values","flat","minStartTime","_b","_a","minBy","maxEndTime","_d","_c","maxBy","numberOfCells","Math","ceil","DateHelper","getTimeDiffInMinutes","height","actualHeight","ref","cellsRef","useResizeDetector","hasCollapsedContent","visibleRowCount","undefined","max","floor","SLOT_GAP","ROW_HEIGHT","expandableStyles","useSpring","generatedDaySlots","transformedDaySlots","entries","forEach","key","cellsArray","Array","fill","slot","startIndex","endIndex","min","cellLength","assign","isActualSlot","rowIndex","i","addMinutesToTime","halfFill","isEmpty","id","initializeAndFillSlots","reduce","result","obj","lastObj","length","generateFallbackCell","filter","populateEmptyCells","focusableSlotsByDate","day","formattedDate","format","getCellsForDate","map","date","sort","a","b","useApplyStyle","styles.tokens","timeColumn","formatUnitValue","handleDayHover","value","handleDayHoverClear","handleSlotClick","handleSlotButtonClick","event","stopPropagation","handleExpandCollapseClick","preventDefault","prevExpandValue","handleSlotKeyDown","currentSlot","sameColumnSlots","focusSlot","current","focus","currentIndex","findIndex","lastVisibleSlot","reverse","find","isDisabled","isWithinRange","dayjs","isDisabledDate","includes","prefix","toString","Date","getTime","clickable","styleAttributes","backgroundColor","Colour","getSlotAriaLabel","slotStartTime","slotEndTime","StringHelper","joinNonEmptyStrings","TimeHelper","formatTimeRange","label","_","index","_jsxs","className","styles.wrapper","children","styles.gridWrapper","role","_jsx","styles.headerCellWeekColumn","onBlur","dayIndex","dayCellStyleProps","dateStartWithYear","disabled","isHoverEnabled","interactive","isHover","isSame","isSelected","labelType","circleLeft","circleRight","generateStyleProps","DayCell","tabIndex","handleDayClick","onHover","onHoverEnd","onFocus","styles.headerCellWeek","Typography","BodyXS","weight","clsx","styles.cellWeekTextDisabled","hasSetFirstAfternoonHour","formatTime","parsedTime","add","hour","minutes","amPm","formattedTime","styles.timeColumn","styles.timeColumnWrapper","styles.timeColumnText","renderTimeColumn","animated","div","styles.expandable","style","styles.columnWeekCell","styles.timeSlotWrapper","WeekTimeSlotCell","onSlotKeyDown","onSlotButtonClick","renderSlotCell","styles.collapseExpandAllWrapper","Button","styleType","sizeType","onClick","styles.collapseExpandAllButton","styles.collapseExpandAllButtonContent","ChevronUpIcon","styles.chevronIcon","styles.chevronIconExpanded","renderCollapseExpandAll"],"mappings":"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"}
1
+ {"version":3,"file":"time-slot-bar-week-days.js","sources":["../../src/time-slot-bar-week/time-slot-bar-week-days.tsx"],"sourcesContent":["import { ChevronUpIcon } from \"@lifesg/react-icons\";\nimport { animated, useSpring } from \"@react-spring/web\";\nimport clsx from \"clsx\";\nimport type { Dayjs } from \"dayjs\";\nimport dayjs from \"dayjs\";\nimport isEmpty from \"lodash/isEmpty\";\nimport maxBy from \"lodash/maxBy\";\nimport minBy from \"lodash/minBy\";\nimport { useMemo, useRef, useState } from \"react\";\nimport { useResizeDetector } from \"react-resize-detector\";\n\nimport { Button } from \"../button\";\nimport type { InternalCalendarProps } from \"../shared/internal-calendar\";\nimport type { CellStyleProps } from \"../shared/internal-calendar/day-cell\";\nimport { DayCell } from \"../shared/internal-calendar/day-cell\";\nimport { Colour, formatUnitValue, useApplyStyle } from \"../theme\";\nimport type { TimeSlot } from \"../time-slot-bar/types\";\nimport { Typography } from \"../typography\";\nimport { DateHelper, useIsMounted } from \"../util\";\nimport { CalendarHelper } from \"../util/calendar-helper\";\nimport { StringHelper } from \"../util/string-helper\";\nimport { TimeHelper } from \"../util/time-helper\";\nimport { ROW_HEIGHT, SLOT_GAP } from \"./consts\";\nimport * as styles from \"./time-slot-bar-week-days.styles\";\nimport type { FocusableSlotMeta, TimeSlotCell } from \"./time-slot-cell\";\nimport { WeekTimeSlotCell } from \"./time-slot-cell\";\nimport type { TimeSlotCellsVariant } from \"./types\";\n\ninterface TimeSlotWeekDaysProps\n extends Pick<\n InternalCalendarProps,\n \"disabledDates\" | \"minDate\" | \"maxDate\"\n > {\n selectedDate: string | undefined;\n calendarDate: Dayjs;\n onSelect: (value: Dayjs) => void;\n slots: { [date: string]: TimeSlot[] };\n startTime?: string | undefined;\n endTime?: string | undefined;\n maxVisibleCellHeight?: number | undefined;\n interval?: number | undefined;\n variant?: TimeSlotCellsVariant | undefined;\n enableSelection?: boolean | undefined;\n onSlotClick?: ((date: string, timeSlot: TimeSlot) => void) | undefined;\n}\n\nexport const TimeSlotBarWeekDays = ({\n calendarDate,\n disabledDates,\n selectedDate,\n onSelect,\n minDate,\n maxDate,\n startTime,\n endTime,\n maxVisibleCellHeight,\n slots: daySlots,\n interval = 30,\n variant = \"flexible\",\n enableSelection = true,\n onSlotClick,\n}: TimeSlotWeekDaysProps) => {\n // =============================================================================\n // CONST, STATE, REF\n // =============================================================================\n const dateFormat = \"YYYY-MM-DD\";\n const [expandAll, setExpandAll] = useState<boolean>(false);\n const [hoverDay, setHoverDay] = useState<Dayjs>();\n const isMounted = useIsMounted();\n const slotButtonRefs = useRef<Record<string, HTMLButtonElement | null>>({});\n const timeColumnRef = useRef<HTMLDivElement>(null);\n const currentCalendarWeek = useMemo((): Dayjs[] => {\n return CalendarHelper.generateDaysForCurrentWeek(calendarDate);\n }, [calendarDate]);\n\n const flattenedSlots = Object.values(daySlots ?? {}).flat();\n const minStartTime =\n startTime ?? minBy(flattenedSlots, \"startTime\")?.startTime ?? \"00:00\";\n const maxEndTime =\n endTime ?? maxBy(flattenedSlots, \"endTime\")?.endTime ?? \"24:00\";\n const numberOfCells = Math.ceil(\n DateHelper.getTimeDiffInMinutes(minStartTime, maxEndTime) / interval\n );\n\n // React spring animation configuration\n const { height: actualHeight = 0, ref: cellsRef } = useResizeDetector();\n const hasCollapsedContent =\n !!maxVisibleCellHeight && actualHeight > maxVisibleCellHeight;\n const visibleRowCount =\n maxVisibleCellHeight !== undefined\n ? Math.max(\n 1,\n Math.floor((maxVisibleCellHeight + SLOT_GAP) / ROW_HEIGHT)\n )\n : 0;\n const collapsedHeight =\n visibleRowCount > 0\n ? visibleRowCount * ROW_HEIGHT - SLOT_GAP\n : maxVisibleCellHeight;\n const height = maxVisibleCellHeight\n ? !hasCollapsedContent || expandAll\n ? actualHeight\n : collapsedHeight\n : actualHeight;\n const expandableStyles = useSpring({ height });\n\n const generatedDaySlots = useMemo((): {\n [date: string]: TimeSlotCell[];\n } => {\n if (daySlots) {\n const transformedDaySlots: Record<string, TimeSlotCell[]> = {};\n Object.entries(daySlots).forEach(([key, slots]) => {\n const cellsArray = initializeAndFillSlots(slots);\n transformedDaySlots[key] = populateEmptyCells(cellsArray);\n });\n return transformedDaySlots;\n }\n return {};\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [daySlots]);\n\n const focusableSlotsByDate = useMemo(() => {\n return currentCalendarWeek.reduce<Record<string, FocusableSlotMeta[]>>(\n (result, day) => {\n const formattedDate = day.format(dateFormat);\n\n result[formattedDate] = getCellsForDate(formattedDate)\n .filter(\n (\n slot\n ): slot is TimeSlotCell & {\n isActualSlot: true;\n rowIndex: number;\n } => !!slot.isActualSlot && slot.rowIndex !== undefined\n )\n .map((slot) => ({\n key: `${formattedDate}-${slot.id}`,\n date: formattedDate,\n rowIndex: slot.rowIndex,\n }))\n .sort((a, b) => a.rowIndex - b.rowIndex);\n\n return result;\n },\n {}\n );\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [currentCalendarWeek, generatedDaySlots]);\n\n useApplyStyle(timeColumnRef, {\n [styles.tokens.timeColumn.height]: formatUnitValue(height, \"px\"),\n });\n\n // =============================================================================\n // EVENT HANDLERS\n // =============================================================================\n const handleDayClick = (value: Dayjs, isDisabled: boolean) => {\n if (isDisabled || !enableSelection) return;\n\n onSelect(value);\n };\n\n const handleDayHover = (value: Dayjs) => {\n setHoverDay(value);\n };\n\n const handleDayHoverClear = () => {\n setHoverDay(undefined);\n };\n\n const handleSlotClick = (date: string, slot: TimeSlot) => {\n onSlotClick?.(date, slot);\n };\n\n const handleSlotButtonClick =\n (date: string, slot: TimeSlot) =>\n (event: React.MouseEvent<HTMLButtonElement>) => {\n event.stopPropagation();\n\n handleSlotClick(date, slot);\n };\n\n const handleExpandCollapseClick = (event: React.MouseEvent) => {\n event.preventDefault();\n setExpandAll((prevExpandValue) => !prevExpandValue);\n };\n\n const handleSlotKeyDown = (\n event: React.KeyboardEvent<HTMLButtonElement>,\n currentSlot: FocusableSlotMeta\n ) => {\n const sameColumnSlots = focusableSlotsByDate[currentSlot.date] ?? [];\n\n const focusSlot = (slot?: FocusableSlotMeta) => {\n if (!slot) return;\n slotButtonRefs.current[slot.key]?.focus();\n };\n\n switch (event.key) {\n case \"ArrowRight\":\n case \"ArrowDown\": {\n event.preventDefault();\n const currentIndex = sameColumnSlots.findIndex(\n (slot) => slot.key === currentSlot.key\n );\n focusSlot(sameColumnSlots[currentIndex + 1]);\n break;\n }\n case \"ArrowLeft\":\n case \"ArrowUp\": {\n event.preventDefault();\n const currentIndex = sameColumnSlots.findIndex(\n (slot) => slot.key === currentSlot.key\n );\n focusSlot(sameColumnSlots[currentIndex - 1]);\n break;\n }\n case \"Home\":\n event.preventDefault();\n focusSlot(sameColumnSlots[0]);\n break;\n case \"End\":\n event.preventDefault();\n focusSlot(sameColumnSlots[sameColumnSlots.length - 1]);\n break;\n case \"PageUp\":\n event.preventDefault();\n focusSlot(sameColumnSlots[0]);\n break;\n case \"PageDown\": {\n event.preventDefault();\n if (hasCollapsedContent && !expandAll) {\n setExpandAll(true);\n const lastVisibleSlot = [...sameColumnSlots]\n .reverse()\n .find((slot) => slot.rowIndex < visibleRowCount);\n focusSlot(lastVisibleSlot ?? sameColumnSlots[0]);\n break;\n }\n\n focusSlot(sameColumnSlots[sameColumnSlots.length - 1]);\n break;\n }\n default:\n break;\n }\n };\n\n // =============================================================================\n // HELPER FUNCTIONS\n // =============================================================================\n const isDisabled = (day: Dayjs): boolean => {\n const isWithinRange = CalendarHelper.isWithinRange(\n day,\n minDate ? dayjs(minDate) : undefined,\n maxDate ? dayjs(maxDate) : undefined\n );\n\n const isDisabledDate =\n (disabledDates && disabledDates.includes(day.format(dateFormat))) ??\n false;\n\n return !isWithinRange || isDisabledDate;\n };\n\n const generateStyleProps = (day: Dayjs) => {\n const dateStartWithYear = day.format(dateFormat);\n const disabled = isDisabled(day);\n const dayCellStyleProps: CellStyleProps = {};\n const isHoverEnabled = enableSelection && !disabled;\n\n if (disabled) {\n dayCellStyleProps.disabled = true;\n }\n\n dayCellStyleProps.interactive = enableSelection\n ? isHoverEnabled\n : false;\n\n const isHover =\n isHoverEnabled && hoverDay && day.isSame(hoverDay, \"day\");\n const isSelected = [selectedDate].includes(dateStartWithYear);\n\n if (isSelected && isHover) {\n dayCellStyleProps.labelType = \"selected-hover\";\n dayCellStyleProps.circleLeft = \"selected-hover-outline\";\n dayCellStyleProps.circleRight = \"selected-hover-outline\";\n } else if (isSelected) {\n dayCellStyleProps.labelType = \"selected\";\n dayCellStyleProps.circleLeft = \"selected-outline\";\n dayCellStyleProps.circleRight = \"selected-outline\";\n } else if (isHover) {\n dayCellStyleProps.labelType = \"hover\";\n dayCellStyleProps.circleLeft = \"hover-subtle\";\n dayCellStyleProps.circleRight = \"hover-subtle\";\n }\n\n return dayCellStyleProps;\n };\n\n function generateFallbackCell(\n prefix: string | number,\n cellLength = 1\n ): TimeSlotCell {\n return {\n id: `${prefix.toString()}-${new Date().getTime()}`,\n startTime: \"\",\n endTime: \"\",\n clickable: false,\n isActualSlot: false,\n styleAttributes: {\n backgroundColor: Colour[\"bg-stronger\"],\n },\n cellLength,\n };\n }\n\n function getSlotAriaLabel(date: string, slot: TimeSlot) {\n const { startTime: slotStartTime, endTime: slotEndTime } = slot;\n\n return StringHelper.joinNonEmptyStrings([\n dayjs(date).format(\"D MMMM YYYY dddd\"),\n slotStartTime && slotEndTime\n ? TimeHelper.formatTimeRange(slotStartTime, slotEndTime)\n : undefined,\n slot.label,\n slot.clickable ?? true ? \"Available\" : \"Unavailable\",\n ]);\n }\n\n function initializeAndFillSlots(slots: TimeSlot[]): TimeSlotCell[] {\n const cellsArray = Array(numberOfCells).fill({});\n\n slots.forEach((slot) => {\n const startIndex = Math.max(\n 0,\n DateHelper.getTimeDiffInMinutes(minStartTime, slot.startTime) /\n interval\n );\n const endIndex = Math.min(\n numberOfCells,\n DateHelper.getTimeDiffInMinutes(minStartTime, slot.endTime) /\n interval\n );\n const cellLength = Math.ceil(endIndex) - Math.floor(startIndex);\n\n switch (variant) {\n case \"fixed\":\n // Keep fixed slots as 1 long cell\n cellsArray[Math.floor(startIndex)] = {\n ...slot,\n isActualSlot: true,\n rowIndex: Math.floor(startIndex),\n cellLength: endIndex - startIndex,\n };\n break;\n case \"flexible\":\n // Split long slot into multiple cells of length 1\n for (let i = 0; i < cellLength; i++) {\n const startTime = DateHelper.addMinutesToTime(\n minStartTime,\n Math.floor(startIndex + i) * interval\n );\n const endTime = DateHelper.addMinutesToTime(\n startTime,\n interval\n );\n\n let halfFill: \"default\" | \"top\" | \"bottom\";\n\n if (!isEmpty(cellsArray[Math.floor(startIndex + i)])) {\n // Previous cell extends into current cell, fill entire cell\n halfFill = \"default\";\n } else if (i === 0 && startIndex % 1 !== 0) {\n halfFill = \"top\";\n } else if (i === cellLength - 1 && endIndex % 1 !== 0) {\n halfFill = \"bottom\";\n } else {\n halfFill = \"default\";\n }\n\n cellsArray[Math.floor(startIndex + i)] = {\n ...slot,\n id: `${slot.id}-${i}`,\n startTime,\n endTime,\n isActualSlot: true,\n rowIndex: Math.floor(startIndex + i),\n cellLength: 1,\n halfFill,\n };\n }\n break;\n }\n });\n return cellsArray;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function populateEmptyCells(cellsArray: any[]): TimeSlotCell[] {\n let i = 0;\n switch (variant) {\n case \"fixed\":\n // Truncate consecutive empty cells into 1 [{},{},{...},{},{}] -> [{},{...},{}]\n cellsArray = cellsArray.reduce(\n (result, obj) => {\n const lastObj =\n result.length > 0\n ? result[result.length - 1]\n : result[0];\n if (isEmpty(obj) && isEmpty(lastObj)) {\n return result;\n }\n return [...result, obj];\n },\n [{}]\n );\n\n // Fill in empty cells with timings & corresponding cell lengths\n while (i < cellsArray.length) {\n if (isEmpty(cellsArray[i])) {\n const startTime =\n cellsArray[i - 1]?.endTime ?? minStartTime;\n const endTime =\n cellsArray[i + 1]?.startTime ?? maxEndTime;\n if (startTime !== endTime) {\n const cellLength =\n DateHelper.getTimeDiffInMinutes(\n startTime,\n endTime\n ) / interval;\n cellsArray[i] = {\n ...generateFallbackCell(i),\n startTime,\n endTime,\n cellLength,\n };\n }\n }\n i++;\n }\n break;\n case \"flexible\":\n // Fill in individual empty cell with interval timings\n while (i < cellsArray.length) {\n if (isEmpty(cellsArray[i])) {\n cellsArray[i] = generateFallbackCell(i);\n i++;\n } else {\n i += cellsArray[i].cellLength;\n }\n }\n break;\n }\n return cellsArray.filter(\n (slot) => !isEmpty(slot) && slot.cellLength > 0\n );\n }\n\n function getCellsForDate(formattedDate: string) {\n return (\n generatedDaySlots[formattedDate] ??\n Array(variant === \"flexible\" ? numberOfCells : 1)\n .fill(undefined)\n .map((_, index) =>\n generateFallbackCell(\n index,\n variant === \"fixed\" ? numberOfCells : undefined\n )\n )\n );\n }\n\n // =============================================================================\n // RENDER FUNCTIONS\n // =============================================================================\n const renderWeek = () => {\n return (\n <div className={styles.headerCellWeekColumn} role=\"row\">\n {currentCalendarWeek.map((day, index) => (\n <div\n key={`week-day-${index}`}\n className={styles.headerCellWeek}\n role=\"gridcell\"\n >\n <Typography.BodyXS\n weight={\"semibold\"}\n className={clsx(\n isDisabled(day) && styles.cellWeekTextDisabled\n )}\n >\n {dayjs(day).format(\"ddd\")}\n </Typography.BodyXS>\n </div>\n ))}\n </div>\n );\n };\n\n const renderHeader = () => {\n return (\n <div\n className={styles.headerCellWeekColumn}\n role=\"row\"\n onBlur={handleDayHoverClear}\n >\n {currentCalendarWeek.map((day, dayIndex) => {\n const dayCellStyleProps = generateStyleProps(day);\n\n return (\n <DayCell\n key={`day-${dayIndex}`}\n date={day}\n calendarDate={dayjs(selectedDate)}\n role=\"columnheader\"\n tabIndex={dayCellStyleProps.interactive ? 0 : -1}\n onSelect={() => {\n handleDayClick(\n day,\n !dayCellStyleProps.interactive\n );\n }}\n onHover={handleDayHover}\n onHoverEnd={handleDayHoverClear}\n onFocus={handleDayHover}\n {...dayCellStyleProps}\n ></DayCell>\n );\n })}\n </div>\n );\n };\n\n const renderTimeColumn = () => {\n let hasSetFirstAfternoonHour = false;\n\n const formatTime = (index: number) => {\n const parsedTime = dayjs(minStartTime, \"HH:mm\").add(\n index * 4 * interval,\n \"minutes\"\n );\n const hour = parsedTime.format(\"h\");\n const minutes = parsedTime.format(\"mm\");\n const amPm = parsedTime.format(\"a\");\n\n let formattedTime = `${hour}${\n minutes !== \"00\" ? ` ${minutes}` : \"\"\n }`;\n\n if (\n !hasSetFirstAfternoonHour &&\n amPm === \"pm\" &&\n minutes === \"00\"\n ) {\n formattedTime += ` ${amPm}`;\n hasSetFirstAfternoonHour = true;\n }\n\n return formattedTime;\n };\n\n return (\n <div className={styles.timeColumn} ref={timeColumnRef} aria-hidden>\n {Array(Math.ceil(numberOfCells / 4))\n .fill(undefined)\n .map((_, index) => (\n <div\n key={`time-${index}`}\n className={styles.timeColumnWrapper}\n >\n <div className={styles.timeColumnText}>\n {formatTime(index)}\n </div>\n </div>\n ))}\n </div>\n );\n };\n\n const renderSlotCell = (formattedDate: string, slot: TimeSlotCell) => {\n return (\n <WeekTimeSlotCell\n key={slot.id}\n formattedDate={formattedDate}\n slot={slot}\n variant={variant}\n hasCollapsedContent={hasCollapsedContent}\n expandAll={expandAll}\n visibleRowCount={visibleRowCount}\n onSlotClick={handleSlotClick}\n getSlotAriaLabel={getSlotAriaLabel}\n onSlotKeyDown={handleSlotKeyDown}\n onSlotButtonClick={handleSlotButtonClick}\n slotButtonRefs={slotButtonRefs}\n />\n );\n };\n\n const renderTimeSlotBarCells = () => {\n return (\n <animated.div\n className={styles.expandable}\n style={isMounted ? expandableStyles : undefined}\n role=\"row\"\n >\n <div\n className={styles.columnWeekCell}\n ref={cellsRef}\n key={`week-cell-${calendarDate.format(dateFormat)}`}\n >\n {currentCalendarWeek.map((day, dayIndex) => {\n const formattedDate = day.format(dateFormat);\n const cellsArray = getCellsForDate(formattedDate);\n return (\n <div\n key={`wrapper-${dayIndex}`}\n className={styles.timeSlotWrapper}\n role=\"gridcell\"\n >\n {cellsArray.map((slot) =>\n renderSlotCell(formattedDate, slot)\n )}\n </div>\n );\n })}\n </div>\n </animated.div>\n );\n };\n\n const renderCollapseExpandAll = () => {\n if (!maxVisibleCellHeight || !cellsRef.current) return;\n return (\n <div className={styles.collapseExpandAllWrapper}>\n <Button\n data-testid=\"time-bar-expand-collapse-button\"\n styleType=\"light\"\n sizeType=\"small\"\n aria-expanded={expandAll}\n onClick={handleExpandCollapseClick}\n className={styles.collapseExpandAllButton}\n >\n <span className={styles.collapseExpandAllButtonContent}>\n <ChevronUpIcon\n className={clsx(\n styles.chevronIcon,\n expandAll && styles.chevronIconExpanded\n )}\n />\n {`${expandAll ? \"Hide\" : \"Show\"} later times`}\n </span>\n </Button>\n </div>\n );\n };\n\n return (\n <div className={styles.wrapper}>\n <div className={styles.gridWrapper} role=\"grid\">\n {renderHeader()}\n {renderWeek()}\n {renderTimeColumn()}\n {renderTimeSlotBarCells()}\n </div>\n {renderCollapseExpandAll()}\n </div>\n );\n};\n"],"names":["TimeSlotBarWeekDays","calendarDate","disabledDates","selectedDate","onSelect","minDate","maxDate","startTime","endTime","maxVisibleCellHeight","slots","daySlots","interval","variant","enableSelection","onSlotClick","dateFormat","expandAll","setExpandAll","useState","hoverDay","setHoverDay","isMounted","useIsMounted","slotButtonRefs","useRef","timeColumnRef","currentCalendarWeek","useMemo","CalendarHelper","generateDaysForCurrentWeek","flattenedSlots","Object","values","flat","minStartTime","_b","_a","minBy","maxEndTime","_d","_c","maxBy","numberOfCells","Math","ceil","DateHelper","getTimeDiffInMinutes","height","actualHeight","ref","cellsRef","useResizeDetector","hasCollapsedContent","visibleRowCount","undefined","max","floor","SLOT_GAP","ROW_HEIGHT","expandableStyles","useSpring","generatedDaySlots","transformedDaySlots","entries","forEach","key","cellsArray","Array","fill","slot","startIndex","endIndex","min","cellLength","assign","isActualSlot","rowIndex","i","addMinutesToTime","halfFill","isEmpty","id","initializeAndFillSlots","reduce","result","obj","lastObj","length","generateFallbackCell","filter","populateEmptyCells","focusableSlotsByDate","day","formattedDate","format","getCellsForDate","map","date","sort","a","b","useApplyStyle","styles.tokens","timeColumn","formatUnitValue","handleDayHover","value","handleDayHoverClear","handleSlotClick","handleSlotButtonClick","event","stopPropagation","handleExpandCollapseClick","preventDefault","prevExpandValue","handleSlotKeyDown","currentSlot","sameColumnSlots","focusSlot","current","focus","currentIndex","findIndex","lastVisibleSlot","reverse","find","isDisabled","isWithinRange","dayjs","isDisabledDate","includes","prefix","toString","Date","getTime","clickable","styleAttributes","backgroundColor","Colour","getSlotAriaLabel","slotStartTime","slotEndTime","StringHelper","joinNonEmptyStrings","TimeHelper","formatTimeRange","label","_","index","_jsxs","className","styles.wrapper","children","styles.gridWrapper","role","_jsx","styles.headerCellWeekColumn","onBlur","dayIndex","dayCellStyleProps","dateStartWithYear","disabled","isHoverEnabled","interactive","isHover","isSame","isSelected","labelType","circleLeft","circleRight","generateStyleProps","DayCell","tabIndex","handleDayClick","onHover","onHoverEnd","onFocus","styles.headerCellWeek","Typography","BodyXS","weight","clsx","styles.cellWeekTextDisabled","hasSetFirstAfternoonHour","formatTime","parsedTime","add","hour","minutes","amPm","formattedTime","styles.timeColumn","styles.timeColumnWrapper","styles.timeColumnText","renderTimeColumn","animated","div","styles.expandable","style","styles.columnWeekCell","styles.timeSlotWrapper","WeekTimeSlotCell","onSlotKeyDown","onSlotButtonClick","renderSlotCell","styles.collapseExpandAllWrapper","Button","styleType","sizeType","onClick","styles.collapseExpandAllButton","styles.collapseExpandAllButtonContent","ChevronUpIcon","styles.chevronIcon","styles.chevronIconExpanded","renderCollapseExpandAll"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"timetable.js","sources":["../../src/timetable/timetable.tsx"],"sourcesContent":["import clsx from \"clsx\";\nimport dayjs from \"dayjs\";\nimport { isEmpty, throttle } from \"lodash\";\nimport {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from \"react\";\nimport { useResizeDetector } from \"react-resize-detector\";\n\nimport { LoadingDotsSpinner } from \"../animations\";\nimport { ErrorDisplay } from \"../error-display\";\nimport type { PopoverTriggerProps } from \"../popover\";\nimport { PopoverTrigger } from \"../popover\";\nimport { VisuallyHidden } from \"../shared/accessibility\";\nimport { useApplyStyle } from \"../theme\";\nimport { Typography } from \"../typography\";\nimport { TimeHelper } from \"../util/time-helper\";\nimport {\n MIN_INTERVAL_WIDTH,\n ROW_BAR_COLOR_ALTERNATE_COLOR,\n ROW_BAR_COLOR_SEQUENCE,\n ROW_HEADER_WIDTH,\n ROW_INTERVAL,\n} from \"./const\";\nimport * as styles from \"./timetable.styles\";\nimport { TimeTableHeader } from \"./timetable-header/timetable-header\";\nimport { RowBar } from \"./timetable-row/row-bar\";\nimport type { TimeTableProps, TimeTableRef, TimeTableRowData } from \"./types\";\n\nconst Component = (props: TimeTableProps, ref: React.Ref<TimeTableRef>) => {\n // =============================================================================\n // CONST, STATE, REF\n // =============================================================================\n const {\n date,\n emptyContentMessage,\n rowData,\n loading,\n minTime = \"00:00\",\n maxTime = \"23:00\",\n initialScrollTime,\n minDate,\n maxDate,\n totalRecords,\n showCurrentDateAsToday,\n showDateAsShortForm,\n onPage,\n onRefresh,\n onNextDayClick,\n onPreviousDayClick,\n onCalendarDateSelect,\n roundInitialScrollTime = true,\n ...otherProps\n } = props;\n const testId = otherProps[\"data-testid\"] || \"timetable\";\n const timetableMinTime = TimeHelper.roundToNearestInterval(minTime, 60);\n const timetableMaxTime = TimeHelper.roundToNearestInterval(\n maxTime,\n 60,\n true\n );\n const hourlyIntervals = TimeHelper.generateHourlyIntervals(\n timetableMinTime,\n timetableMaxTime\n );\n const numberOfIntervals = Math.ceil(\n (hourlyIntervals.length * 60) / ROW_INTERVAL\n );\n const formattedDate = dayjs(date).format(\"D MMMM YYYY\");\n const timetableAriaLabel = `Timetable of resource availability, for ${formattedDate}, from ${dayjs(\n timetableMinTime,\n \"HH:mm\"\n ).format(\"ha\")} to ${dayjs(timetableMaxTime, \"HH:mm\").format(\"ha\")}`;\n const ariaRowCount = (totalRecords || rowData.length) + 1;\n const ariaColCount = numberOfIntervals + 1;\n\n const isEmptyContent = totalRecords === 0 || isEmpty(rowData);\n const allRecordsLoaded = isEmptyContent || rowData.length === totalRecords;\n const hasLazyLoad = !allRecordsLoaded && !!onPage;\n const tableContainerRef = useRef<HTMLDivElement>(null);\n const contentContainerRef = useRef<HTMLDivElement>(null);\n const columnHeaderRowRef = useRef<HTMLDivElement>(null);\n const loadingWrapperRef = useRef<HTMLDivElement>(null);\n const [isScrolledX, setIsScrolledX] = useState<boolean>(false);\n const [isScrolledY, setIsScrolledY] = useState<boolean>(false);\n const [intervalWidth, setIntervalWidth] = useState<number>(0);\n const [loadMore, setLoadMore] = useState<boolean>(false);\n\n // Expose imperative handle\n useImperativeHandle(ref, () => ({\n resetScroll,\n }));\n\n useApplyStyle(columnHeaderRowRef, {\n [styles.tokens.columnHeaderRow.columnWidth]: `${intervalWidth * 4}px`,\n });\n\n useApplyStyle(loadingWrapperRef, {\n [styles.tokens.loadingWrapper.cellWidth]: `${intervalWidth * 4}px`,\n });\n\n // =============================================================================\n // EFFECTS\n // =============================================================================\n useEffect(() => {\n const handleScroll = throttle(() => {\n if (tableContainerRef.current) {\n setIsScrolledX(tableContainerRef.current.scrollLeft > 0);\n setIsScrolledY(tableContainerRef.current.scrollTop > 0);\n }\n\n if (loadMore || !tableContainerRef.current) return;\n\n const { scrollTop, clientHeight, scrollHeight } =\n tableContainerRef.current;\n const scrollBottomTolerance = 1;\n const isEndReached =\n Math.ceil(scrollTop + clientHeight) >=\n scrollHeight - scrollBottomTolerance;\n const shouldLoadMore =\n isEndReached && !allRecordsLoaded && onPage && !loading;\n\n if (shouldLoadMore) {\n setLoadMore(true);\n onPage();\n }\n }, 100);\n\n const tableContainer = tableContainerRef.current;\n\n if (tableContainer) {\n tableContainer.addEventListener(\"scroll\", handleScroll);\n }\n\n return () => {\n if (tableContainer) {\n tableContainer.removeEventListener(\"scroll\", handleScroll);\n }\n };\n }, [allRecordsLoaded, loadMore, loading, onPage]);\n\n useEffect(() => {\n setLoadMore(false);\n }, [rowData]);\n\n // Initial scroll on mount and when dependencies change\n useEffect(() => {\n resetScroll();\n }, [initialScrollTime, intervalWidth, timetableMinTime, timetableMaxTime]);\n\n // =============================================================================\n // EVENT HANDLERS\n // =============================================================================\n /**\n * Handles when the TimeTable component is resized.\n *\n * @remarks\n * When the component is resized, we recalculate the width of each interval\n * in the Timetable. We do this by dividing the width of the container by\n * the number of intervals per row bar. We also make sure that the width\n * of each interval is at least 21px, otherwise it becomes too small.\n */\n const handleResize = () => {\n if (tableContainerRef.current) {\n const numberOfIntervalsPerRowBar = Math.ceil(\n (hourlyIntervals.length * 60) / ROW_INTERVAL\n );\n const tableContainerWidth =\n tableContainerRef.current.clientWidth - ROW_HEADER_WIDTH;\n const calcWidth = tableContainerWidth / numberOfIntervalsPerRowBar;\n const width =\n calcWidth > MIN_INTERVAL_WIDTH ? calcWidth : MIN_INTERVAL_WIDTH;\n setIntervalWidth(width);\n }\n };\n\n useResizeDetector({\n onResize: handleResize,\n targetRef: tableContainerRef,\n refreshMode: \"debounce\",\n refreshRate: 50,\n });\n\n const handleRowNameClick = (\n data: TimeTableRowData,\n event: React.MouseEvent\n ) => {\n if (data.onRowNameClick) {\n data.onRowNameClick(data, event);\n }\n };\n\n // ===========================================================================\n // HELPER FUNCTIONS\n // ===========================================================================\n /**\n * Using closures to more efficiently loop through and wrap the color sequencing instead of modding large numbers repeatedly\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures#closure\n */\n const getColorFromSequence = (() => {\n let colorIndex = 0;\n const colorSequenceLength = ROW_BAR_COLOR_SEQUENCE.length;\n\n return () => {\n const mainColor = ROW_BAR_COLOR_SEQUENCE[colorIndex];\n const alternateColor = ROW_BAR_COLOR_ALTERNATE_COLOR[colorIndex];\n colorIndex = (colorIndex + 1) % colorSequenceLength;\n return { mainColor, alternateColor };\n };\n })();\n\n const buildPopoverTrigger = (\n data: TimeTableRowData,\n child: JSX.Element\n ) => {\n if (!data.rowHeaderPopover) {\n return child;\n }\n\n const popoverTriggerProps: PopoverTriggerProps = {\n position: \"bottom-start\",\n customOffset: data.rowHeaderPopover.offset,\n children: child,\n trigger: data.rowHeaderPopover.trigger,\n delay: data.rowHeaderPopover.delay,\n popoverContent: data.rowHeaderPopover.content,\n };\n\n return (\n <PopoverTrigger\n {...popoverTriggerProps}\n className={styles.popoverTrigger}\n key={`${data.id}-popover-trigger`}\n zIndex={2}\n >\n {child}\n </PopoverTrigger>\n );\n };\n\n // Function to reset scroll to initialScrollTime\n const resetScroll = () => {\n if (\n initialScrollTime &&\n tableContainerRef.current &&\n intervalWidth > 0\n ) {\n const scrollPosition = TimeHelper.calculateScrollPosition({\n scrollTime: initialScrollTime,\n minTime: timetableMinTime,\n maxTime: timetableMaxTime,\n interval: ROW_INTERVAL,\n intervalWidth,\n options: {\n roundToInterval: roundInitialScrollTime,\n },\n });\n\n if (scrollPosition !== null) {\n tableContainerRef.current.scrollLeft = scrollPosition;\n }\n }\n };\n\n // =============================================================================\n // RENDER FUNCTIONS\n // =============================================================================\n const renderRowHeader = (data: TimeTableRowData) => {\n const rowHeaderContent = (\n <>\n <Typography.BodyMD\n className={clsx(\n styles.clickableRowHeaderTitle,\n !!data.onRowNameClick &&\n styles.clickableRowHeaderTitleClickable\n )}\n onClick={(event: React.MouseEvent) =>\n handleRowNameClick(data, event)\n }\n weight=\"semibold\"\n data-testid={`${data.id}-row-header-title`}\n >\n {data.name}\n </Typography.BodyMD>\n\n <Typography.BodyXS\n weight=\"bold\"\n className={clsx(\n styles.rowHeaderSubtitle,\n !data.subtitle && styles.rowHeaderSubtitleHidden\n )}\n data-testid={`${data.id}-row-header-subtitle`}\n >\n {data.subtitle}\n </Typography.BodyXS>\n </>\n );\n\n return (\n <div\n className={clsx(\n styles.rowHeader,\n isScrolledX && styles.rowHeaderScrolled\n )}\n key={`${data.id}-row-header`}\n role=\"rowheader\"\n aria-colindex={1}\n >\n {buildPopoverTrigger(data, rowHeaderContent)}\n </div>\n );\n };\n\n const renderColumnHeaders = () => {\n // Don't render first column if there are no rows\n\n return hourlyIntervals.map((columnHeader: string) => (\n <div\n className={styles.columnHeader}\n key={`${columnHeader}-column-key`}\n data-testid={`${columnHeader}-column`}\n >\n <Typography.BodySM\n className={styles.columnHeaderTitle}\n weight={\"semibold\"}\n >\n {columnHeader}\n </Typography.BodySM>\n </div>\n ));\n };\n\n const renderRows = () => {\n if (loading) {\n return (\n <LoadingDotsSpinner\n data-testid=\"timetable-loading-spinner\"\n className={clsx(\n styles.loadingSpinner,\n isEmptyContent\n ? styles.loadingSpinnerEmptyContent\n : styles.loadingSpinnerHasContent\n )}\n />\n );\n }\n\n return (\n <div\n className={styles.timeTableBody}\n data-testid=\"content-container-id\"\n ref={contentContainerRef}\n role=\"rowgroup\"\n >\n {rowData.map((data, index) => {\n return (\n <div\n className={styles.timeTableRow}\n key={`${index}-timetable-row`}\n role=\"row\"\n aria-rowindex={index + 2}\n aria-label={data.name}\n >\n {renderRowHeader(data)}\n <RowBar\n key={`${index}-row-bar`}\n timetableMinTime={timetableMinTime}\n timetableMaxTime={timetableMaxTime}\n rowBarColor={getColorFromSequence()}\n intervalWidth={intervalWidth}\n containerRef={contentContainerRef}\n {...data}\n />\n </div>\n );\n })}\n {renderLazyLoadRow()}\n </div>\n );\n };\n\n const renderLazyLoadRow = () => {\n if (loading || !loadMore) return;\n return (\n <div className={styles.timeTableRow}>\n <div\n className={clsx(\n styles.rowHeader,\n isScrolledX && styles.rowHeaderScrolled\n )}\n >\n <div className={styles.loadingBar} />\n </div>\n <div\n ref={loadingWrapperRef}\n className={styles.loadingWrapper}\n data-testid=\"lazy-loader\"\n role=\"presentation\"\n >\n {hourlyIntervals.map((_, index) => (\n <div\n className={styles.loadingCell}\n key={`lazy-load-cell-${index}`}\n >\n <div className={styles.loadingBar} />\n </div>\n ))}\n </div>\n </div>\n );\n };\n\n if (isEmptyContent) {\n return (\n <div {...otherProps} data-testid={testId}>\n <TimeTableHeader\n selectedDate={date}\n loading={loading || loadMore}\n tableContainerRef={tableContainerRef}\n minDate={minDate}\n maxDate={maxDate}\n totalRecords={totalRecords}\n showCurrentDateAsToday={showCurrentDateAsToday}\n showDateAsShortForm={showDateAsShortForm}\n onPreviousDayClick={onPreviousDayClick}\n onNextDayClick={onNextDayClick}\n onRefresh={onRefresh}\n onCalendarDateSelect={onCalendarDateSelect}\n />\n <div\n className={clsx(\n styles.emptyTableContainer,\n \"empty-container\"\n )}\n tabIndex={0}\n aria-live=\"polite\"\n aria-atomic\n aria-busy={loading}\n >\n {!loading ? (\n <ErrorDisplay\n className={styles.noResultsFound}\n type=\"no-item-found\"\n description={emptyContentMessage}\n />\n ) : (\n <>\n <VisuallyHidden>{`Loading ${timetableAriaLabel}`}</VisuallyHidden>\n <LoadingDotsSpinner\n data-testid=\"timetable-loading-spinner\"\n className={clsx(\n styles.loadingSpinner,\n isEmptyContent\n ? styles.loadingSpinnerEmptyContent\n : styles.loadingSpinnerHasContent\n )}\n />\n </>\n )}\n </div>\n </div>\n );\n }\n\n return (\n <div {...otherProps} data-testid={testId}>\n <TimeTableHeader\n data-id=\"timetable-date-navigator\"\n selectedDate={date}\n loading={loading || loadMore}\n tableContainerRef={tableContainerRef}\n minDate={minDate}\n maxDate={maxDate}\n totalRecords={totalRecords}\n showCurrentDateAsToday={showCurrentDateAsToday}\n showDateAsShortForm={showDateAsShortForm}\n onPreviousDayClick={onPreviousDayClick}\n onNextDayClick={onNextDayClick}\n onRefresh={onRefresh}\n onCalendarDateSelect={onCalendarDateSelect}\n />\n <div\n data-id=\"timetable-container\"\n data-testid=\"timetable-container\"\n ref={tableContainerRef}\n className={clsx(\n styles.timeTableContainer,\n hasLazyLoad && styles.timeTableContainerNotAllLoaded,\n loading && styles.timeTableContainerLoading\n )}\n role=\"grid\"\n aria-label={timetableAriaLabel}\n aria-rowcount={ariaRowCount}\n aria-colcount={ariaColCount}\n aria-busy={loadMore}\n tabIndex={0}\n >\n <div\n className={styles.timeTableHeaderRow}\n role=\"row\"\n aria-rowindex={1}\n data-testid={\"row-header-column-id\"}\n >\n <div\n className={clsx(\n styles.rowColumnHeader,\n (isScrolledX || isScrolledY) &&\n styles.rowColumnHeaderScrolled,\n isScrolledX && styles.rowColumnHeaderScrolledX,\n isScrolledY && styles.rowColumnHeaderScrolledY\n )}\n role=\"columnheader\"\n aria-colindex={1}\n >\n <VisuallyHidden>Resource</VisuallyHidden>\n </div>\n <div\n ref={columnHeaderRowRef}\n className={clsx(\n styles.columnHeaderRow,\n isScrolledY && styles.columnHeaderRowScrolled\n )}\n role=\"presentation\"\n >\n {renderColumnHeaders()}\n </div>\n </div>\n {renderRows()}\n </div>\n </div>\n );\n};\n\n// @catalog\n/**\n * A scrollable grid that visualises time slot availability across a day.\n *\n * Use `TimeTable` to display one day's schedule for multiple resources side by\n * side.\n */\nexport const TimeTable = forwardRef<TimeTableRef, TimeTableProps>(Component);\n"],"names":["TimeTable","forwardRef","props","ref","date","emptyContentMessage","rowData","loading","minTime","maxTime","initialScrollTime","minDate","maxDate","totalRecords","showCurrentDateAsToday","showDateAsShortForm","onPage","onRefresh","onNextDayClick","onPreviousDayClick","onCalendarDateSelect","roundInitialScrollTime","otherProps","testId","timetableMinTime","TimeHelper","roundToNearestInterval","timetableMaxTime","hourlyIntervals","generateHourlyIntervals","numberOfIntervals","Math","ceil","length","ROW_INTERVAL","timetableAriaLabel","dayjs","format","ariaRowCount","ariaColCount","isEmptyContent","isEmpty","allRecordsLoaded","hasLazyLoad","tableContainerRef","useRef","contentContainerRef","columnHeaderRowRef","loadingWrapperRef","isScrolledX","setIsScrolledX","useState","isScrolledY","setIsScrolledY","intervalWidth","setIntervalWidth","loadMore","setLoadMore","useImperativeHandle","resetScroll","useApplyStyle","styles.tokens","columnHeaderRow","columnWidth","loadingWrapper","cellWidth","useEffect","handleScroll","throttle","current","scrollLeft","scrollTop","clientHeight","scrollHeight","tableContainer","addEventListener","removeEventListener","useResizeDetector","onResize","numberOfIntervalsPerRowBar","calcWidth","clientWidth","ROW_HEADER_WIDTH","MIN_INTERVAL_WIDTH","targetRef","refreshMode","refreshRate","getColorFromSequence","colorIndex","colorSequenceLength","ROW_BAR_COLOR_SEQUENCE","mainColor","alternateColor","ROW_BAR_COLOR_ALTERNATE_COLOR","buildPopoverTrigger","data","child","rowHeaderPopover","popoverTriggerProps","position","customOffset","offset","children","trigger","delay","popoverContent","content","_createElement","PopoverTrigger","Object","assign","className","styles.popoverTrigger","key","id","zIndex","scrollPosition","calculateScrollPosition","scrollTime","interval","options","roundToInterval","renderRowHeader","rowHeaderContent","_jsxs","_Fragment","_jsx","Typography","BodyMD","clsx","styles.clickableRowHeaderTitle","onRowNameClick","styles.clickableRowHeaderTitleClickable","onClick","event","handleRowNameClick","weight","name","BodyXS","styles.rowHeaderSubtitle","subtitle","styles.rowHeaderSubtitleHidden","styles.rowHeader","styles.rowHeaderScrolled","role","renderLazyLoadRow","styles.timeTableRow","styles.loadingBar","styles.loadingWrapper","map","_","index","styles.loadingCell","TimeTableHeader","selectedDate","styles.emptyTableContainer","tabIndex","VisuallyHidden","LoadingDotsSpinner","styles.loadingSpinner","styles.loadingSpinnerEmptyContent","styles.loadingSpinnerHasContent","ErrorDisplay","styles.noResultsFound","type","description","styles.timeTableContainer","styles.timeTableContainerNotAllLoaded","styles.timeTableContainerLoading","styles.timeTableHeaderRow","styles.rowColumnHeader","styles.rowColumnHeaderScrolled","styles.rowColumnHeaderScrolledX","styles.rowColumnHeaderScrolledY","styles.columnHeaderRow","styles.columnHeaderRowScrolled","columnHeader","styles.columnHeader","BodySM","styles.columnHeaderTitle","styles.timeTableBody","RowBar","rowBarColor","containerRef"],"mappings":"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"}
1
+ {"version":3,"file":"timetable.js","sources":["../../src/timetable/timetable.tsx"],"sourcesContent":["import clsx from \"clsx\";\nimport dayjs from \"dayjs\";\nimport { isEmpty, throttle } from \"lodash\";\nimport {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from \"react\";\nimport { useResizeDetector } from \"react-resize-detector\";\n\nimport { LoadingDotsSpinner } from \"../animations\";\nimport { ErrorDisplay } from \"../error-display\";\nimport type { PopoverTriggerProps } from \"../popover\";\nimport { PopoverTrigger } from \"../popover\";\nimport { VisuallyHidden } from \"../shared/accessibility\";\nimport { useApplyStyle } from \"../theme\";\nimport { Typography } from \"../typography\";\nimport { TimeHelper } from \"../util/time-helper\";\nimport {\n MIN_INTERVAL_WIDTH,\n ROW_BAR_COLOR_ALTERNATE_COLOR,\n ROW_BAR_COLOR_SEQUENCE,\n ROW_HEADER_WIDTH,\n ROW_INTERVAL,\n} from \"./const\";\nimport * as styles from \"./timetable.styles\";\nimport { TimeTableHeader } from \"./timetable-header/timetable-header\";\nimport { RowBar } from \"./timetable-row/row-bar\";\nimport type { TimeTableProps, TimeTableRef, TimeTableRowData } from \"./types\";\n\nconst Component = (props: TimeTableProps, ref: React.Ref<TimeTableRef>) => {\n // =============================================================================\n // CONST, STATE, REF\n // =============================================================================\n const {\n date,\n emptyContentMessage,\n rowData,\n loading,\n minTime = \"00:00\",\n maxTime = \"23:00\",\n initialScrollTime,\n minDate,\n maxDate,\n totalRecords,\n showCurrentDateAsToday,\n showDateAsShortForm,\n onPage,\n onRefresh,\n onNextDayClick,\n onPreviousDayClick,\n onCalendarDateSelect,\n roundInitialScrollTime = true,\n ...otherProps\n } = props;\n const testId = otherProps[\"data-testid\"] || \"timetable\";\n const timetableMinTime = TimeHelper.roundToNearestInterval(minTime, 60);\n const timetableMaxTime = TimeHelper.roundToNearestInterval(\n maxTime,\n 60,\n true\n );\n const hourlyIntervals = TimeHelper.generateHourlyIntervals(\n timetableMinTime,\n timetableMaxTime\n );\n const numberOfIntervals = Math.ceil(\n (hourlyIntervals.length * 60) / ROW_INTERVAL\n );\n const formattedDate = dayjs(date).format(\"D MMMM YYYY\");\n const timetableAriaLabel = `Timetable of resource availability, for ${formattedDate}, from ${dayjs(\n timetableMinTime,\n \"HH:mm\"\n ).format(\"ha\")} to ${dayjs(timetableMaxTime, \"HH:mm\").format(\"ha\")}`;\n const ariaRowCount = (totalRecords || rowData.length) + 1;\n const ariaColCount = numberOfIntervals + 1;\n\n const isEmptyContent = totalRecords === 0 || isEmpty(rowData);\n const allRecordsLoaded = isEmptyContent || rowData.length === totalRecords;\n const hasLazyLoad = !allRecordsLoaded && !!onPage;\n const tableContainerRef = useRef<HTMLDivElement>(null);\n const contentContainerRef = useRef<HTMLDivElement>(null);\n const columnHeaderRowRef = useRef<HTMLDivElement>(null);\n const loadingWrapperRef = useRef<HTMLDivElement>(null);\n const [isScrolledX, setIsScrolledX] = useState<boolean>(false);\n const [isScrolledY, setIsScrolledY] = useState<boolean>(false);\n const [intervalWidth, setIntervalWidth] = useState<number>(0);\n const [loadMore, setLoadMore] = useState<boolean>(false);\n\n // Expose imperative handle\n useImperativeHandle(ref, () => ({\n resetScroll,\n }));\n\n useApplyStyle(columnHeaderRowRef, {\n [styles.tokens.columnHeaderRow.columnWidth]: `${intervalWidth * 4}px`,\n });\n\n useApplyStyle(loadingWrapperRef, {\n [styles.tokens.loadingWrapper.cellWidth]: `${intervalWidth * 4}px`,\n });\n\n // =============================================================================\n // EFFECTS\n // =============================================================================\n useEffect(() => {\n const handleScroll = throttle(() => {\n if (tableContainerRef.current) {\n setIsScrolledX(tableContainerRef.current.scrollLeft > 0);\n setIsScrolledY(tableContainerRef.current.scrollTop > 0);\n }\n\n if (loadMore || !tableContainerRef.current) return;\n\n const { scrollTop, clientHeight, scrollHeight } =\n tableContainerRef.current;\n const scrollBottomTolerance = 1;\n const isEndReached =\n Math.ceil(scrollTop + clientHeight) >=\n scrollHeight - scrollBottomTolerance;\n const shouldLoadMore =\n isEndReached && !allRecordsLoaded && onPage && !loading;\n\n if (shouldLoadMore) {\n setLoadMore(true);\n onPage();\n }\n }, 100);\n\n const tableContainer = tableContainerRef.current;\n\n if (tableContainer) {\n tableContainer.addEventListener(\"scroll\", handleScroll);\n }\n\n return () => {\n if (tableContainer) {\n tableContainer.removeEventListener(\"scroll\", handleScroll);\n }\n };\n }, [allRecordsLoaded, loadMore, loading, onPage]);\n\n useEffect(() => {\n setLoadMore(false);\n }, [rowData]);\n\n // Initial scroll on mount and when dependencies change\n useEffect(() => {\n resetScroll();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [initialScrollTime, intervalWidth, timetableMinTime, timetableMaxTime]);\n\n // =============================================================================\n // EVENT HANDLERS\n // =============================================================================\n /**\n * Handles when the TimeTable component is resized.\n *\n * @remarks\n * When the component is resized, we recalculate the width of each interval\n * in the Timetable. We do this by dividing the width of the container by\n * the number of intervals per row bar. We also make sure that the width\n * of each interval is at least 21px, otherwise it becomes too small.\n */\n const handleResize = () => {\n if (tableContainerRef.current) {\n const numberOfIntervalsPerRowBar = Math.ceil(\n (hourlyIntervals.length * 60) / ROW_INTERVAL\n );\n const tableContainerWidth =\n tableContainerRef.current.clientWidth - ROW_HEADER_WIDTH;\n const calcWidth = tableContainerWidth / numberOfIntervalsPerRowBar;\n const width =\n calcWidth > MIN_INTERVAL_WIDTH ? calcWidth : MIN_INTERVAL_WIDTH;\n setIntervalWidth(width);\n }\n };\n\n useResizeDetector({\n onResize: handleResize,\n targetRef: tableContainerRef,\n refreshMode: \"debounce\",\n refreshRate: 50,\n });\n\n const handleRowNameClick = (\n data: TimeTableRowData,\n event: React.MouseEvent\n ) => {\n if (data.onRowNameClick) {\n data.onRowNameClick(data, event);\n }\n };\n\n // ===========================================================================\n // HELPER FUNCTIONS\n // ===========================================================================\n /**\n * Using closures to more efficiently loop through and wrap the color sequencing instead of modding large numbers repeatedly\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures#closure\n */\n const getColorFromSequence = (() => {\n let colorIndex = 0;\n const colorSequenceLength = ROW_BAR_COLOR_SEQUENCE.length;\n\n return () => {\n const mainColor = ROW_BAR_COLOR_SEQUENCE[colorIndex];\n const alternateColor = ROW_BAR_COLOR_ALTERNATE_COLOR[colorIndex];\n colorIndex = (colorIndex + 1) % colorSequenceLength;\n return { mainColor, alternateColor };\n };\n })();\n\n const buildPopoverTrigger = (\n data: TimeTableRowData,\n child: JSX.Element\n ) => {\n if (!data.rowHeaderPopover) {\n return child;\n }\n\n const popoverTriggerProps: PopoverTriggerProps = {\n position: \"bottom-start\",\n customOffset: data.rowHeaderPopover.offset,\n children: child,\n trigger: data.rowHeaderPopover.trigger,\n delay: data.rowHeaderPopover.delay,\n popoverContent: data.rowHeaderPopover.content,\n };\n\n return (\n <PopoverTrigger\n {...popoverTriggerProps}\n className={styles.popoverTrigger}\n key={`${data.id}-popover-trigger`}\n zIndex={2}\n >\n {child}\n </PopoverTrigger>\n );\n };\n\n // Function to reset scroll to initialScrollTime\n const resetScroll = () => {\n if (\n initialScrollTime &&\n tableContainerRef.current &&\n intervalWidth > 0\n ) {\n const scrollPosition = TimeHelper.calculateScrollPosition({\n scrollTime: initialScrollTime,\n minTime: timetableMinTime,\n maxTime: timetableMaxTime,\n interval: ROW_INTERVAL,\n intervalWidth,\n options: {\n roundToInterval: roundInitialScrollTime,\n },\n });\n\n if (scrollPosition !== null) {\n tableContainerRef.current.scrollLeft = scrollPosition;\n }\n }\n };\n\n // =============================================================================\n // RENDER FUNCTIONS\n // =============================================================================\n const renderRowHeader = (data: TimeTableRowData) => {\n const rowHeaderContent = (\n <>\n <Typography.BodyMD\n className={clsx(\n styles.clickableRowHeaderTitle,\n !!data.onRowNameClick &&\n styles.clickableRowHeaderTitleClickable\n )}\n onClick={(event: React.MouseEvent) =>\n handleRowNameClick(data, event)\n }\n weight=\"semibold\"\n data-testid={`${data.id}-row-header-title`}\n >\n {data.name}\n </Typography.BodyMD>\n\n <Typography.BodyXS\n weight=\"bold\"\n className={clsx(\n styles.rowHeaderSubtitle,\n !data.subtitle && styles.rowHeaderSubtitleHidden\n )}\n data-testid={`${data.id}-row-header-subtitle`}\n >\n {data.subtitle}\n </Typography.BodyXS>\n </>\n );\n\n return (\n <div\n className={clsx(\n styles.rowHeader,\n isScrolledX && styles.rowHeaderScrolled\n )}\n key={`${data.id}-row-header`}\n role=\"rowheader\"\n aria-colindex={1}\n >\n {buildPopoverTrigger(data, rowHeaderContent)}\n </div>\n );\n };\n\n const renderColumnHeaders = () => {\n // Don't render first column if there are no rows\n\n return hourlyIntervals.map((columnHeader: string) => (\n <div\n className={styles.columnHeader}\n key={`${columnHeader}-column-key`}\n data-testid={`${columnHeader}-column`}\n >\n <Typography.BodySM\n className={styles.columnHeaderTitle}\n weight={\"semibold\"}\n >\n {columnHeader}\n </Typography.BodySM>\n </div>\n ));\n };\n\n const renderRows = () => {\n if (loading) {\n return (\n <LoadingDotsSpinner\n data-testid=\"timetable-loading-spinner\"\n className={clsx(\n styles.loadingSpinner,\n isEmptyContent\n ? styles.loadingSpinnerEmptyContent\n : styles.loadingSpinnerHasContent\n )}\n />\n );\n }\n\n return (\n <div\n className={styles.timeTableBody}\n data-testid=\"content-container-id\"\n ref={contentContainerRef}\n role=\"rowgroup\"\n >\n {rowData.map((data, index) => {\n return (\n <div\n className={styles.timeTableRow}\n key={`${index}-timetable-row`}\n role=\"row\"\n aria-rowindex={index + 2}\n aria-label={data.name}\n >\n {renderRowHeader(data)}\n <RowBar\n key={`${index}-row-bar`}\n timetableMinTime={timetableMinTime}\n timetableMaxTime={timetableMaxTime}\n rowBarColor={getColorFromSequence()}\n intervalWidth={intervalWidth}\n containerRef={contentContainerRef}\n {...data}\n />\n </div>\n );\n })}\n {renderLazyLoadRow()}\n </div>\n );\n };\n\n const renderLazyLoadRow = () => {\n if (loading || !loadMore) return;\n return (\n <div className={styles.timeTableRow}>\n <div\n className={clsx(\n styles.rowHeader,\n isScrolledX && styles.rowHeaderScrolled\n )}\n >\n <div className={styles.loadingBar} />\n </div>\n <div\n ref={loadingWrapperRef}\n className={styles.loadingWrapper}\n data-testid=\"lazy-loader\"\n role=\"presentation\"\n >\n {hourlyIntervals.map((_, index) => (\n <div\n className={styles.loadingCell}\n key={`lazy-load-cell-${index}`}\n >\n <div className={styles.loadingBar} />\n </div>\n ))}\n </div>\n </div>\n );\n };\n\n if (isEmptyContent) {\n return (\n <div {...otherProps} data-testid={testId}>\n <TimeTableHeader\n selectedDate={date}\n loading={loading || loadMore}\n tableContainerRef={tableContainerRef}\n minDate={minDate}\n maxDate={maxDate}\n totalRecords={totalRecords}\n showCurrentDateAsToday={showCurrentDateAsToday}\n showDateAsShortForm={showDateAsShortForm}\n onPreviousDayClick={onPreviousDayClick}\n onNextDayClick={onNextDayClick}\n onRefresh={onRefresh}\n onCalendarDateSelect={onCalendarDateSelect}\n />\n <div\n className={clsx(\n styles.emptyTableContainer,\n \"empty-container\"\n )}\n tabIndex={0}\n aria-live=\"polite\"\n aria-atomic\n aria-busy={loading}\n >\n {!loading ? (\n <ErrorDisplay\n className={styles.noResultsFound}\n type=\"no-item-found\"\n description={emptyContentMessage}\n />\n ) : (\n <>\n <VisuallyHidden>{`Loading ${timetableAriaLabel}`}</VisuallyHidden>\n <LoadingDotsSpinner\n data-testid=\"timetable-loading-spinner\"\n className={clsx(\n styles.loadingSpinner,\n isEmptyContent\n ? styles.loadingSpinnerEmptyContent\n : styles.loadingSpinnerHasContent\n )}\n />\n </>\n )}\n </div>\n </div>\n );\n }\n\n return (\n <div {...otherProps} data-testid={testId}>\n <TimeTableHeader\n data-id=\"timetable-date-navigator\"\n selectedDate={date}\n loading={loading || loadMore}\n tableContainerRef={tableContainerRef}\n minDate={minDate}\n maxDate={maxDate}\n totalRecords={totalRecords}\n showCurrentDateAsToday={showCurrentDateAsToday}\n showDateAsShortForm={showDateAsShortForm}\n onPreviousDayClick={onPreviousDayClick}\n onNextDayClick={onNextDayClick}\n onRefresh={onRefresh}\n onCalendarDateSelect={onCalendarDateSelect}\n />\n <div\n data-id=\"timetable-container\"\n data-testid=\"timetable-container\"\n ref={tableContainerRef}\n className={clsx(\n styles.timeTableContainer,\n hasLazyLoad && styles.timeTableContainerNotAllLoaded,\n loading && styles.timeTableContainerLoading\n )}\n role=\"grid\"\n aria-label={timetableAriaLabel}\n aria-rowcount={ariaRowCount}\n aria-colcount={ariaColCount}\n aria-busy={loadMore}\n tabIndex={0}\n >\n <div\n className={styles.timeTableHeaderRow}\n role=\"row\"\n aria-rowindex={1}\n data-testid={\"row-header-column-id\"}\n >\n <div\n className={clsx(\n styles.rowColumnHeader,\n (isScrolledX || isScrolledY) &&\n styles.rowColumnHeaderScrolled,\n isScrolledX && styles.rowColumnHeaderScrolledX,\n isScrolledY && styles.rowColumnHeaderScrolledY\n )}\n role=\"columnheader\"\n aria-colindex={1}\n >\n <VisuallyHidden>Resource</VisuallyHidden>\n </div>\n <div\n ref={columnHeaderRowRef}\n className={clsx(\n styles.columnHeaderRow,\n isScrolledY && styles.columnHeaderRowScrolled\n )}\n role=\"presentation\"\n >\n {renderColumnHeaders()}\n </div>\n </div>\n {renderRows()}\n </div>\n </div>\n );\n};\n\n// @catalog\n/**\n * A scrollable grid that visualises time slot availability across a day.\n *\n * Use `TimeTable` to display one day's schedule for multiple resources side by\n * side.\n */\nexport const TimeTable = forwardRef<TimeTableRef, TimeTableProps>(Component);\n"],"names":["TimeTable","forwardRef","props","ref","date","emptyContentMessage","rowData","loading","minTime","maxTime","initialScrollTime","minDate","maxDate","totalRecords","showCurrentDateAsToday","showDateAsShortForm","onPage","onRefresh","onNextDayClick","onPreviousDayClick","onCalendarDateSelect","roundInitialScrollTime","otherProps","testId","timetableMinTime","TimeHelper","roundToNearestInterval","timetableMaxTime","hourlyIntervals","generateHourlyIntervals","numberOfIntervals","Math","ceil","length","ROW_INTERVAL","timetableAriaLabel","dayjs","format","ariaRowCount","ariaColCount","isEmptyContent","isEmpty","allRecordsLoaded","hasLazyLoad","tableContainerRef","useRef","contentContainerRef","columnHeaderRowRef","loadingWrapperRef","isScrolledX","setIsScrolledX","useState","isScrolledY","setIsScrolledY","intervalWidth","setIntervalWidth","loadMore","setLoadMore","useImperativeHandle","resetScroll","useApplyStyle","styles.tokens","columnHeaderRow","columnWidth","loadingWrapper","cellWidth","useEffect","handleScroll","throttle","current","scrollLeft","scrollTop","clientHeight","scrollHeight","tableContainer","addEventListener","removeEventListener","useResizeDetector","onResize","numberOfIntervalsPerRowBar","calcWidth","clientWidth","ROW_HEADER_WIDTH","MIN_INTERVAL_WIDTH","targetRef","refreshMode","refreshRate","getColorFromSequence","colorIndex","colorSequenceLength","ROW_BAR_COLOR_SEQUENCE","mainColor","alternateColor","ROW_BAR_COLOR_ALTERNATE_COLOR","buildPopoverTrigger","data","child","rowHeaderPopover","popoverTriggerProps","position","customOffset","offset","children","trigger","delay","popoverContent","content","_createElement","PopoverTrigger","Object","assign","className","styles.popoverTrigger","key","id","zIndex","scrollPosition","calculateScrollPosition","scrollTime","interval","options","roundToInterval","renderRowHeader","rowHeaderContent","_jsxs","_Fragment","_jsx","Typography","BodyMD","clsx","styles.clickableRowHeaderTitle","onRowNameClick","styles.clickableRowHeaderTitleClickable","onClick","event","handleRowNameClick","weight","name","BodyXS","styles.rowHeaderSubtitle","subtitle","styles.rowHeaderSubtitleHidden","styles.rowHeader","styles.rowHeaderScrolled","role","renderLazyLoadRow","styles.timeTableRow","styles.loadingBar","styles.loadingWrapper","map","_","index","styles.loadingCell","TimeTableHeader","selectedDate","styles.emptyTableContainer","tabIndex","VisuallyHidden","LoadingDotsSpinner","styles.loadingSpinner","styles.loadingSpinnerEmptyContent","styles.loadingSpinnerHasContent","ErrorDisplay","styles.noResultsFound","type","description","styles.timeTableContainer","styles.timeTableContainerNotAllLoaded","styles.timeTableContainerLoading","styles.timeTableHeaderRow","styles.rowColumnHeader","styles.rowColumnHeaderScrolled","styles.rowColumnHeaderScrolledX","styles.rowColumnHeaderScrolledY","styles.columnHeaderRow","styles.columnHeaderRowScrolled","columnHeader","styles.columnHeader","BodySM","styles.columnHeaderTitle","styles.timeTableBody","RowBar","rowBarColor","containerRef"],"mappings":"ggFAgCA,MAggBaA,GAAYC,GAhgBP,CAACC,EAAuBC,MAItC,MAAMC,KACFA,GAAIC,oBACJA,GAAmBC,QACnBA,GAAOC,QACPA,GAAOC,QACPA,GAAU,QAAOC,QACjBA,GAAU,QAAOC,kBACjBA,GAAiBC,QACjBA,GAAOC,QACPA,GAAOC,aACPA,GAAYC,uBACZA,GAAsBC,oBACtBA,GAAmBC,OACnBA,GAAMC,UACNA,GAASC,eACTA,GAAcC,mBACdA,GAAkBC,qBAClBA,GAAoBC,uBACpBA,IAAyB,GAEzBnB,EADGoB,KACHpB,EApBE,CAAA,OAAA,sBAAA,UAAA,UAAA,UAAA,UAAA,oBAAA,UAAA,UAAA,eAAA,yBAAA,sBAAA,SAAA,YAAA,iBAAA,qBAAA,uBAAA,2BAqBAqB,GAASD,GAAW,gBAAkB,YACtCE,GAAmBC,EAAWC,uBAAuBlB,GAAS,IAC9DmB,GAAmBF,EAAWC,uBAChCjB,GACA,IACA,GAEEmB,GAAkBH,EAAWI,wBAC/BL,GACAG,IAEEG,GAAoBC,KAAKC,KACD,GAAzBJ,GAAgBK,OAAeC,GAG9BC,GAAqB,2CADLC,EAAMhC,IAAMiC,OAAO,wBACoDD,EACzFZ,GACA,SACFa,OAAO,YAAYD,EAAMT,GAAkB,SAASU,OAAO,QACvDC,IAAgBzB,IAAgBP,GAAQ2B,QAAU,EAClDM,GAAeT,GAAoB,EAEnCU,GAAkC,IAAjB3B,IAAsB4B,EAAAA,QAAQnC,IAC/CoC,GAAmBF,IAAkBlC,GAAQ2B,SAAWpB,GACxD8B,IAAeD,MAAsB1B,GACrC4B,GAAoBC,EAAuB,MAC3CC,GAAsBD,EAAuB,MAC7CE,GAAqBF,EAAuB,MAC5CG,GAAoBH,EAAuB,OAC1CI,GAAaC,IAAkBC,GAAkB,IACjDC,GAAaC,IAAkBF,GAAkB,IACjDG,GAAeC,IAAoBJ,EAAiB,IACpDK,GAAUC,IAAeN,GAAkB,GAGlDO,EAAoBvD,IAAK,KAAA,CACrBwD,mBAGJC,EAAcb,GAAoB,CAC9B,CAACc,EAAcC,gBAAgBC,aAAiC,EAAhBT,GAAH,OAGjDM,EAAcZ,GAAmB,CAC7B,CAACa,EAAcG,eAAeC,WAA+B,EAAhBX,GAAH,OAM9CY,GAAU,KACN,MAAMC,EAAeC,EAAAA,UAAS,KAM1B,GALIxB,GAAkByB,UAClBnB,GAAeN,GAAkByB,QAAQC,WAAa,GACtDjB,GAAeT,GAAkByB,QAAQE,UAAY,IAGrDf,KAAaZ,GAAkByB,QAAS,OAE5C,MAAME,UAAEA,EAASC,aAAEA,EAAYC,aAAEA,GAC7B7B,GAAkByB,QAGlBtC,KAAKC,KAAKuC,EAAYC,IACtBC,EAH0B,IAKT/B,IAAoB1B,KAAWT,KAGhDkD,IAAY,GACZzC,KACJ,GACD,KAEG0D,EAAiB9B,GAAkByB,QAMzC,OAJIK,GACAA,EAAeC,iBAAiB,SAAUR,GAGvC,KACCO,GACAA,EAAeE,oBAAoB,SAAUT,EACjD,CACH,GACF,CAACzB,GAAkBc,GAAUjD,GAASS,KAEzCkD,GAAU,KACNT,IAAY,EAAM,GACnB,CAACnD,KAGJ4D,GAAU,KACNP,IAAa,GAEd,CAACjD,GAAmB4C,GAAe9B,GAAkBG,KA4BxDkD,EAAkB,CACdC,SAfiB,KACjB,GAAIlC,GAAkByB,QAAS,CAC3B,MAAMU,EAA6BhD,KAAKC,KACV,GAAzBJ,GAAgBK,OAAeC,GAI9B8C,GADFpC,GAAkByB,QAAQY,YAAcC,GACJH,EAGxCxB,GADIyB,EAAYG,EAAqBH,EAAYG,EAErD,GAKAC,UAAWxC,GACXyC,YAAa,WACbC,YAAa,KAGjB,MAgBMC,GAAuB,MACzB,IAAIC,EAAa,EACjB,MAAMC,EAAsBC,EAAuBzD,OAEnD,MAAO,KACH,MAAM0D,EAAYD,EAAuBF,GACnCI,EAAiBC,EAA8BL,GAErD,OADAA,GAAcA,EAAa,GAAKC,EACzB,CAAEE,YAAWC,iBAAgB,CAE3C,EAV4B,GAYvBE,GAAsB,CACxBC,EACAC,KAEA,IAAKD,EAAKE,iBACN,OAAOD,EAGX,MAAME,EAA2C,CAC7CC,SAAU,eACVC,aAAcL,EAAKE,iBAAiBI,OACpCC,SAAUN,EACVO,QAASR,EAAKE,iBAAiBM,QAC/BC,MAAOT,EAAKE,iBAAiBO,MAC7BC,eAAgBV,EAAKE,iBAAiBS,SAG1C,OACIC,EAACC,EAAcC,OAAAC,OAAA,CAAA,EACPZ,EAAmB,CACvBa,UAAWC,GACXC,IAAK,GAAGlB,EAAKmB,qBACbC,OAAQ,IAEPnB,EACY,EAKnBrC,GAAc,KAChB,GACIjD,IACAkC,GAAkByB,SAClBf,GAAgB,EAClB,CACE,MAAM8D,EAAiB3F,EAAW4F,wBAAwB,CACtDC,WAAY5G,GACZF,QAASgB,GACTf,QAASkB,GACT4F,SAAUrF,EACVoB,iBACAkE,QAAS,CACLC,gBAAiBpG,MAIF,OAAnB+F,IACAxE,GAAkByB,QAAQC,WAAa8C,EAE/C,GAMEM,GAAmB3B,IACrB,MAAM4B,EACFC,EAAAC,EAAA,CAAAvB,SAAA,CACIwB,EAACC,EAAWC,OAAM,CACdjB,UAAWkB,EACPC,IACEnC,EAAKoC,gBACHC,GAERC,QAAUC,GA7FC,EACvBvC,EACAuC,KAEIvC,EAAKoC,gBACLpC,EAAKoC,eAAepC,EAAMuC,EAC9B,EAwFgBC,CAAmBxC,EAAMuC,GAE7BE,OAAO,WAAU,cACJ,GAAGzC,EAAKmB,sBAAqBZ,SAEzCP,EAAK0C,OAGVX,EAACC,EAAWW,OAAM,CACdF,OAAO,OACPzB,UAAWkB,EACPU,GACC5C,EAAK6C,UAAYC,GACrB,cACY,GAAG9C,EAAKmB,yBAAwBZ,SAE5CP,EAAK6C,cAKlB,OACId,EAAA,MAAA,CACIf,UAAWkB,EACPa,EACA7F,IAAe8F,GAGnBC,KAAK,4BACU,EAAC1C,SAEfR,GAAoBC,EAAM4B,IAJtB,GAAG5B,EAAKmB,gBAKX,EAwER+B,GAAoB,KACtB,IAAI1I,IAAYiD,GAChB,OACIoE,EAAA,MAAA,CAAKb,UAAWmC,EAAmB5C,SAAA,CAC/BwB,EAAA,MAAA,CACIf,UAAWkB,EACPa,EACA7F,IAAe8F,GAClBzC,SAEDwB,EAAA,MAAA,CAAKf,UAAWoC,MAEpBrB,EAAA,MAAA,CACI3H,IAAK6C,GACL+D,UAAWqC,EAAqB,cACpB,cACZJ,KAAK,eAAc1C,SAElB1E,GAAgByH,KAAI,CAACC,EAAGC,IACrBzB,SACIf,UAAWyC,GAAkBlD,SAG7BwB,EAAA,MAAA,CAAKf,UAAWoC,KAFX,kBAAkBI,WAMjC,EAId,OAEQ3B,EAAA,MAFJpF,GAEIqE,OAAAC,OAAA,CAAA,EAASxF,GAAU,CAAA,cAAeC,GAAM+E,SAAA,CACpCwB,EAAC2B,GAAe,CACZC,aAActJ,GACdG,QAASA,IAAWiD,GACpBZ,kBAAmBA,GACnBjC,QAASA,GACTC,QAASA,GACTC,aAAcA,GACdC,uBAAwBA,GACxBC,oBAAqBA,GACrBI,mBAAoBA,GACpBD,eAAgBA,GAChBD,UAAWA,GACXG,qBAAsBA,KAE1B0G,EAAA,MAAA,CACIf,UAAWkB,EACP0B,EACA,mBAEJC,SAAU,EAAC,YACD,SAAQ,eAAA,EAAA,YAEPrJ,GAAO+F,SAEhB/F,GAOEqH,EAAAC,EAAA,CAAAvB,SAAA,CACIwB,EAAC+B,EAAc,CAAAvD,SAAE,WAAWnE,OAC5B2F,EAACgC,EAAkB,CAAA,cACH,4BACZ/C,UAAWkB,EACP8B,EACAvH,GACMwH,EACAC,QAdlBnC,EAACoC,EAAY,CACTnD,UAAWoD,EACXC,KAAK,gBACLC,YAAahK,UAsBjCwG,OAAAC,OAAA,CAAA,EAASxF,GAAU,CAAA,cAAeC,GAAM+E,SAAA,CACpCwB,EAAC2B,GAAe,CAAA,UACJ,2BACRC,aAActJ,GACdG,QAASA,IAAWiD,GACpBZ,kBAAmBA,GACnBjC,QAASA,GACTC,QAASA,GACTC,aAAcA,GACdC,uBAAwBA,GACxBC,oBAAqBA,GACrBI,mBAAoBA,GACpBD,eAAgBA,GAChBD,UAAWA,GACXG,qBAAsBA,KAE1BwG,EAAA,MAAA,CAAA,UACY,sBAAqB,cACjB,sBACZzH,IAAKyC,GACLmE,UAAWkB,EACPqC,EACA3H,IAAe4H,EACfhK,IAAWiK,GAEfxB,KAAK,OAAM,aACC7G,GAAkB,gBACfG,GAAY,gBACZC,GAAY,YAChBiB,GACXoG,SAAU,EAACtD,SAAA,CAEXsB,EAAA,MAAA,CACIb,UAAW0D,EACXzB,KAAK,MAAK,gBACK,EAAC,cACH,uBAAsB1C,SAAA,CAEnCwB,EAAA,MAAA,CACIf,UAAWkB,EACPyC,GACCzH,IAAeG,KACZuH,EACJ1H,IAAe2H,EACfxH,IAAeyH,GAEnB7B,KAAK,eAAc,gBACJ,EAAC1C,SAEhBwB,EAAC+B,EAAc,CAAAvD,SAAA,eAEnBwB,EAAA,MAAA,CACI3H,IAAK4C,GACLgE,UAAWkB,EACP6C,EACA1H,IAAe2H,GAEnB/B,KAAK,eAAc1C,SA9M5B1E,GAAgByH,KAAK2B,GACxBlD,SACIf,UAAWkE,gBAEE,GAAGD,WAAqB1E,SAErCwB,EAACC,EAAWmD,QACRnE,UAAWoE,EACX3C,OAAQ,WAAUlC,SAEjB0E,KAPA,GAAGA,uBAcZzK,GAEIuH,EAACgC,EAAkB,CAAA,cACH,4BACZ/C,UAAWkB,EACP8B,EACAvH,GACMwH,EACAC,KAOlBrC,EAAA,MAAA,CACIb,UAAWqE,EAAoB,cACnB,uBACZjL,IAAK2C,GACLkG,KAAK,WAAU1C,SAAA,CAEdhG,GAAQ+I,KAAI,CAACtD,EAAMwD,IAEZ3B,EAAA,MAAA,CACIb,UAAWmC,EAEXF,KAAK,MAAK,gBACKO,EAAQ,EAAC,aACZxD,EAAK0C,KAAInC,SAAA,CAEpBoB,GAAgB3B,GACjB+B,EAACuD,GAAMxE,OAAAC,OAAA,CAEHtF,iBAAkBA,GAClBG,iBAAkBA,GAClB2J,YAAa/F,KACbjC,cAAeA,GACfiI,aAAczI,IACViD,GANC,GAAGwD,eAPP,GAAGA,qBAkBnBN,cAyJH"}
@@ -1,2 +1,2 @@
1
- import"./toast.styles_1e0l60w.css";const t="wqf508q",s="c1ll5mdk",e="t1mt05g8",l="t1j1n9on",o="t1see4ey",m="dmmbr7j",d="a10z10pl",_="d1jd4l4i";export{d as actionButton,s as contentWrapper,m as description,_ as dismissButton,l as textContainer,e as textIconWrapper,o as title,t as wrapper};
1
+ import"./toast.styles_57j3lf.css";const t="wqf508q",s="c1ll5mdk",l="t1mt05g8",o="t1j1n9on",e="t1see4ey",m="dmmbr7j",d="a10z10pl",j="d1jd4l4i";export{d as actionButton,s as contentWrapper,m as description,j as dismissButton,o as textContainer,l as textIconWrapper,e as title,t as wrapper};
2
2
  //# sourceMappingURL=toast.styles.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"toast.styles.js","sources":["../../src/toast/toast.styles.ts"],"sourcesContent":["import { css } from \"@linaria/core\";\n\nimport { Colour, Font, MediaQuery, Radius } from \"../theme\";\n\nexport const wrapper = css`\n display: flex;\n flex-direction: row;\n position: relative;\n margin: 0;\n top: 0;\n right: 0;\n padding: 1rem;\n border-radius: ${Radius[\"md\"]};\n z-index: 10;\n align-items: center;\n gap: 2rem;\n\n ${MediaQuery.MaxWidth.lg} {\n left: 0;\n }\n\n &[data-fixed=\"true\"] {\n position: fixed;\n margin: 1rem;\n }\n\n &[data-type=\"success\"] {\n background: ${Colour[\"bg-success\"]};\n border: 1px solid ${Colour[\"border-success\"]};\n color: ${Colour[\"text-success\"]};\n }\n\n &[data-type=\"warning\"] {\n background: ${Colour[\"bg-warning\"]};\n border: 1px solid ${Colour[\"border-warning\"]};\n color: ${Colour[\"text-warning\"]};\n }\n\n &[data-type=\"error\"] {\n background: ${Colour[\"bg-error\"]};\n border: 1px solid ${Colour[\"border-error\"]};\n color: ${Colour[\"text-error\"]};\n }\n\n &[data-type=\"info\"] {\n background: ${Colour[\"bg-info\"]};\n border: 1px solid ${Colour[\"border-info\"]};\n color: ${Colour[\"text-info\"]};\n }\n`;\n\nexport const contentWrapper = css`\n display: flex;\n align-items: center;\n gap: 2rem;\n flex: 1;\n justify-content: space-between;\n\n ${MediaQuery.MaxWidth.sm} {\n display: flex;\n align-items: flex-start;\n flex-direction: column;\n gap: 0.75rem;\n }\n`;\n\nexport const textIconWrapper = css`\n display: flex;\n flex-direction: row;\n align-items: flex-start;\n\n & > svg {\n flex-shrink: 0;\n width: 1.5rem;\n height: 1.5rem;\n margin-top: 0.0625rem;\n margin-right: 0.5rem;\n }\n\n &[data-type=\"success\"] > svg {\n color: ${Colour[\"icon-success\"]};\n }\n\n &[data-type=\"warning\"] > svg {\n color: ${Colour[\"icon-warning\"]};\n }\n\n &[data-type=\"error\"] > svg {\n color: ${Colour[\"icon-error\"]};\n }\n\n &[data-type=\"info\"] > svg {\n color: ${Colour[\"icon-info\"]};\n }\n`;\n\nexport const textContainer = css`\n display: flex;\n flex-direction: column;\n`;\n\nexport const title = css`\n display: flex;\n ${Font[\"body-baseline-semibold\"]}\n color: inherit;\n`;\n\nexport const description = css`\n color: inherit;\n`;\n\nexport const actionButton = css`\n align-self: center;\n margin-left: auto;\n height: auto;\n width: fit-content;\n flex-shrink: 0;\n\n ${MediaQuery.MaxWidth.sm} {\n align-self: flex-start;\n margin-left: 2rem;\n }\n`;\n\nexport const dismissButton = css`\n padding: 0.75rem;\n margin: -0.75rem;\n align-self: stretch;\n color: inherit;\n\n &:hover {\n background: transparent;\n }\n\n svg {\n width: 1.5rem;\n height: 1.5rem;\n color: inherit;\n }\n\n ${MediaQuery.MaxWidth.sm} {\n align-self: center;\n }\n`;\n"],"names":["wrapper","contentWrapper","textIconWrapper","textContainer","title","description","actionButton","dismissButton"],"mappings":"wDAIO,MAAMA,EAAO,UA+CPC,EAAc,WAedC,EAAe,WA8BfC,EAAa,WAKbC,EAAK,WAMLC,EAAW,UAIXC,EAAY,WAaZC,EAAa"}
1
+ {"version":3,"file":"toast.styles.js","sources":["../../src/toast/toast.styles.ts"],"sourcesContent":["import { css } from \"@linaria/core\";\n\nimport { Colour, Font, MediaQuery, Radius } from \"../theme\";\n\nexport const wrapper = css`\n display: flex;\n flex-direction: row;\n position: relative;\n margin: 0;\n top: 0;\n right: 0;\n padding: 1rem;\n border-radius: ${Radius[\"md\"]};\n z-index: 10;\n align-items: center;\n gap: 2rem;\n\n ${MediaQuery.MaxWidth.lg} {\n left: 0;\n }\n\n &[data-fixed=\"true\"] {\n position: fixed;\n margin: 1rem;\n }\n\n &[data-type=\"success\"] {\n background: ${Colour[\"bg-success\"]};\n border: 1px solid ${Colour[\"border-success\"]};\n color: ${Colour[\"text-success\"]};\n }\n\n &[data-type=\"warning\"] {\n background: ${Colour[\"bg-warning\"]};\n border: 1px solid ${Colour[\"border-warning\"]};\n color: ${Colour[\"text-warning\"]};\n }\n\n &[data-type=\"error\"] {\n background: ${Colour[\"bg-error\"]};\n border: 1px solid ${Colour[\"border-error\"]};\n color: ${Colour[\"text-error\"]};\n }\n\n &[data-type=\"info\"] {\n background: ${Colour[\"bg-info\"]};\n border: 1px solid ${Colour[\"border-info\"]};\n color: ${Colour[\"text-info\"]};\n }\n`;\n\nexport const contentWrapper = css`\n display: flex;\n align-items: center;\n gap: 2rem;\n flex: 1;\n justify-content: space-between;\n\n ${MediaQuery.MaxWidth.sm} {\n display: flex;\n align-items: flex-start;\n flex-direction: column;\n gap: 0.75rem;\n }\n`;\n\nexport const textIconWrapper = css`\n display: flex;\n flex-direction: row;\n align-items: flex-start;\n\n & > svg {\n flex-shrink: 0;\n width: 1.5rem;\n height: 1.5rem;\n margin-top: 0.0625rem;\n margin-right: 0.5rem;\n }\n\n &[data-type=\"success\"] > svg {\n color: ${Colour[\"icon-success\"]};\n }\n\n &[data-type=\"warning\"] > svg {\n color: ${Colour[\"icon-warning\"]};\n }\n\n &[data-type=\"error\"] > svg {\n color: ${Colour[\"icon-error\"]};\n }\n\n &[data-type=\"info\"] > svg {\n color: ${Colour[\"icon-info\"]};\n }\n`;\n\nexport const textContainer = css`\n display: flex;\n flex-direction: column;\n`;\n\nexport const title = css`\n display: flex;\n ${Font[\"body-baseline-semibold\"]}\n color: inherit;\n`;\n\nexport const description = css`\n color: inherit;\n`;\n\nexport const actionButton = css`\n align-self: center;\n margin-left: auto;\n width: fit-content;\n flex-shrink: 0;\n\n ${MediaQuery.MaxWidth.sm} {\n align-self: flex-start;\n margin-left: 2rem;\n }\n`;\n\nexport const dismissButton = css`\n padding: 0.75rem;\n margin: -0.75rem;\n align-self: stretch;\n color: inherit;\n\n &:hover {\n background: transparent;\n }\n\n svg {\n width: 1.5rem;\n height: 1.5rem;\n color: inherit;\n }\n\n ${MediaQuery.MaxWidth.sm} {\n align-self: center;\n }\n`;\n"],"names":["wrapper","contentWrapper","textIconWrapper","textContainer","title","description","actionButton","dismissButton"],"mappings":"uDAIO,MAAMA,EAAO,UA+CPC,EAAc,WAedC,EAAe,WA8BfC,EAAa,WAKbC,EAAK,WAMLC,EAAW,UAIXC,EAAY,WAYZC,EAAa"}
@@ -4,5 +4,5 @@
4
4
  .t1j1n9on{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;}
5
5
  .t1see4ey{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;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-semibold);line-height:var(--fds-font-body-lh-baseline);letter-spacing:var(--fds-font-body-ls-baseline);color:inherit;}
6
6
  .dmmbr7j{color:inherit;}
7
- .a10z10pl{-webkit-align-self:center;-ms-flex-item-align:center;-ms-grid-row-align:center;align-self:center;margin-left:auto;height:auto;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;}:where(body.fds-breakpoint-sm-max) .a10z10pl{-webkit-align-self:flex-start;-ms-flex-item-align:start;align-self:flex-start;margin-left:2rem;}
7
+ .a10z10pl{-webkit-align-self:center;-ms-flex-item-align:center;-ms-grid-row-align:center;align-self:center;margin-left:auto;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;}:where(body.fds-breakpoint-sm-max) .a10z10pl{-webkit-align-self:flex-start;-ms-flex-item-align:start;align-self:flex-start;margin-left:2rem;}
8
8
  .d1jd4l4i{padding:0.75rem;margin:-0.75rem;-webkit-align-self:stretch;-ms-flex-item-align:stretch;-ms-grid-row-align:stretch;align-self:stretch;color:inherit;}.d1jd4l4i:hover{background:transparent;}.d1jd4l4i svg{width:1.5rem;height:1.5rem;color:inherit;}:where(body.fds-breakpoint-sm-max) .d1jd4l4i{-webkit-align-self:center;-ms-flex-item-align:center;-ms-grid-row-align:center;align-self:center;}}
@@ -3,6 +3,7 @@ export interface ToggleIconProps {
3
3
  type: ToggleIconType;
4
4
  active?: boolean | undefined;
5
5
  disabled?: boolean | undefined;
6
+ error?: boolean | undefined;
6
7
  className?: string | undefined;
7
8
  }
8
- export declare const ToggleIcon: ({ type, active, disabled, className, }: ToggleIconProps) => import("react/jsx-runtime").JSX.Element;
9
+ export declare const ToggleIcon: ({ type, active, disabled, className, error, }: ToggleIconProps) => import("react/jsx-runtime").JSX.Element;
@@ -1,2 +1,2 @@
1
- import{jsx as r}from"react/jsx-runtime";import{CircleIcon as e}from"@lifesg/react-icons/circle";import{CircleDotIcon as i}from"@lifesg/react-icons/circle-dot";import{CrossIcon as c}from"@lifesg/react-icons/cross";import{SquareIcon as o}from"@lifesg/react-icons/square";import{SquareTickFillIcon as s}from"@lifesg/react-icons/square-tick-fill";import{TickIcon as t}from"@lifesg/react-icons/tick";import a from"clsx";import{wrapperBase as l,wrapperActiveDisabled as m,wrapperDisabled as f,wrapperActive as n}from"./toggle-icon.styles.js";const p=({type:p,active:d=!1,disabled:g,className:k})=>{let b;switch(p){case"checkbox":b=r(d?s:o,{});break;case"radio":b=r(d?i:e,{});break;case"tick":b=r(t,{});break;case"cross":b=r(c,{});break;default:b=null}return r("div",{className:a(l,d&&g?m:g?f:d?n:void 0,k),"aria-hidden":!0,children:b})};export{p as ToggleIcon};
1
+ import{jsx as r}from"react/jsx-runtime";import{CircleIcon as e}from"@lifesg/react-icons/circle";import{CircleDotIcon as i}from"@lifesg/react-icons/circle-dot";import{CrossIcon as c}from"@lifesg/react-icons/cross";import{SquareIcon as o}from"@lifesg/react-icons/square";import{SquareTickFillIcon as s}from"@lifesg/react-icons/square-tick-fill";import{TickIcon as t}from"@lifesg/react-icons/tick";import a from"clsx";import{wrapperBase as l,wrapperActiveDisabled as m,wrapperError as f,wrapperActive as n,wrapperDisabled as p}from"./toggle-icon.styles.js";const d=({type:d,active:g=!1,disabled:k,className:b,error:u})=>{let h;switch(d){case"checkbox":h=r(g?s:o,{});break;case"radio":h=r(g?i:e,{});break;case"tick":h=r(t,{});break;case"cross":h=r(c,{});break;default:h=null}return r("div",{className:a(l,g?k?m:u?f:n:k?p:void 0,b),"aria-hidden":!0,children:h})};export{d as ToggleIcon};
2
2
  //# sourceMappingURL=toggle-icon.js.map