@lifesg/react-design-system 4.0.0-alpha.12 → 4.0.0-alpha.14

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 (323) hide show
  1. package/breadcrumb/breadcrumb.js +1 -1
  2. package/breadcrumb/breadcrumb.js.map +1 -1
  3. package/breadcrumb/breadcrumb.styles.d.ts +0 -8
  4. package/breadcrumb/breadcrumb.styles.js +1 -1
  5. package/breadcrumb/breadcrumb.styles.js.map +1 -1
  6. package/breadcrumb/breadcrumb.styles_1blzn8n.css +7 -0
  7. package/breadcrumb/types.d.ts +2 -13
  8. package/cjs/breadcrumb/breadcrumb.js +1 -1
  9. package/cjs/breadcrumb/breadcrumb.js.map +1 -1
  10. package/cjs/breadcrumb/breadcrumb.styles.js +1 -1
  11. package/cjs/breadcrumb/breadcrumb.styles.js.map +1 -1
  12. package/cjs/breadcrumb/breadcrumb.styles_1blzn8n.css +7 -0
  13. package/cjs/drawer/drawer.js +1 -1
  14. package/cjs/drawer/drawer.js.map +1 -1
  15. package/cjs/drawer/drawer.styles.js +1 -1
  16. package/cjs/drawer/drawer.styles.js.map +1 -1
  17. package/{drawer/drawer.styles_94hx3y.css → cjs/drawer/drawer.styles_a4bwe0.css} +6 -3
  18. package/cjs/external/dompurify/dist/purify.es.js +4 -2
  19. package/cjs/external/dompurify/dist/purify.es.js.map +1 -1
  20. package/cjs/file-upload/file-list/file-list.js +1 -1
  21. package/cjs/file-upload/file-list/file-list.js.map +1 -1
  22. package/cjs/file-upload/file-list/file-list.styles.js +1 -1
  23. package/cjs/file-upload/file-list/file-list.styles.js.map +1 -1
  24. package/cjs/file-upload/file-list/file-list.styles_1xbypnh.css +2 -0
  25. package/cjs/file-upload/file-list-item/file-item-actions.js +2 -0
  26. package/cjs/file-upload/file-list-item/file-item-actions.js.map +1 -0
  27. package/cjs/file-upload/file-list-item/file-item-actions.styles.js +2 -0
  28. package/cjs/file-upload/file-list-item/file-item-actions.styles.js.map +1 -0
  29. package/cjs/file-upload/file-list-item/file-item-actions.styles_1quulgy.css +8 -0
  30. package/cjs/file-upload/file-list-item/file-item-details.js +2 -0
  31. package/cjs/file-upload/file-list-item/file-item-details.js.map +1 -0
  32. package/cjs/file-upload/file-list-item/file-item-details.styles.js +2 -0
  33. package/cjs/file-upload/file-list-item/file-item-details.styles.js.map +1 -0
  34. package/cjs/file-upload/file-list-item/file-item-details.styles_1ce9akx.css +7 -0
  35. package/cjs/file-upload/file-list-item/file-list-item.js +1 -1
  36. package/cjs/file-upload/file-list-item/file-list-item.js.map +1 -1
  37. package/cjs/file-upload/file-list-item/file-list-item.styles.js +1 -1
  38. package/cjs/file-upload/file-list-item/file-list-item.styles.js.map +1 -1
  39. package/cjs/file-upload/file-list-item/file-list-item.styles_x3a500.css +10 -0
  40. package/cjs/file-upload/file-list-item/use-truncated-name.js +2 -0
  41. package/cjs/file-upload/file-list-item/use-truncated-name.js.map +1 -0
  42. package/cjs/local-nav/local-nav-dropdown/local-nav-dropdown.js +1 -1
  43. package/cjs/local-nav/local-nav-dropdown/local-nav-dropdown.js.map +1 -1
  44. package/cjs/local-nav/local-nav-dropdown/local-nav-dropdown.styles.js +1 -1
  45. package/cjs/local-nav/local-nav-dropdown/local-nav-dropdown.styles.js.map +1 -1
  46. package/{local-nav/local-nav-dropdown/local-nav-dropdown.styles_uhd4xl.css → cjs/local-nav/local-nav-dropdown/local-nav-dropdown.styles_kpel1m.css} +9 -6
  47. package/cjs/local-nav/local-nav-menu/local-nav-menu.js +1 -1
  48. package/cjs/local-nav/local-nav-menu/local-nav-menu.js.map +1 -1
  49. package/cjs/local-nav/local-nav-menu/local-nav-menu.styles.js +1 -1
  50. package/cjs/local-nav/local-nav-menu/local-nav-menu.styles.js.map +1 -1
  51. package/cjs/local-nav/local-nav-menu/local-nav-menu.styles_67fcyc.css +8 -0
  52. package/cjs/menu/menu-content.js +1 -1
  53. package/cjs/menu/menu-content.js.map +1 -1
  54. package/cjs/menu/menu-content.styles.js +1 -1
  55. package/cjs/menu/menu-content.styles.js.map +1 -1
  56. package/cjs/menu/menu-content.styles_1r8oxtk.css +5 -0
  57. package/cjs/menu/menu-item.styles.js +1 -1
  58. package/cjs/menu/menu-item.styles.js.map +1 -1
  59. package/cjs/menu/{menu-item.styles_liyed8.css → menu-item.styles_eut2j9.css} +1 -1
  60. package/cjs/menu/menu-link.styles.js +1 -1
  61. package/cjs/menu/menu-link.styles.js.map +1 -1
  62. package/cjs/menu/menu-link.styles_rng7n7.css +2 -0
  63. package/cjs/menu/menu-section.js +1 -1
  64. package/cjs/menu/menu-section.js.map +1 -1
  65. package/cjs/menu/menu-section.styles.js +1 -1
  66. package/cjs/menu/menu-section.styles.js.map +1 -1
  67. package/cjs/menu/menu-section.styles_1n0ppxl.css +5 -0
  68. package/cjs/modal-v2/slots/slot-styles.js +1 -1
  69. package/cjs/modal-v2/slots/slot-styles.js.map +1 -1
  70. package/cjs/modal-v2/slots/{slot-styles_ayb9il.css → slot-styles_ygu6zc.css} +1 -1
  71. package/cjs/navbar/navbar-items.js +1 -1
  72. package/cjs/navbar/navbar-items.js.map +1 -1
  73. package/cjs/notification-banner/notification-banner-hoc.js +1 -1
  74. package/cjs/notification-banner/notification-banner-hoc.js.map +1 -1
  75. package/cjs/pagination/pagination.js +1 -1
  76. package/cjs/pagination/pagination.js.map +1 -1
  77. package/cjs/shared/dropdown-list/dropdown-label.js +1 -1
  78. package/cjs/shared/dropdown-list/dropdown-label.js.map +1 -1
  79. package/cjs/shared/fade-wrapper/fade-wrapper.js +1 -1
  80. package/cjs/shared/fade-wrapper/fade-wrapper.js.map +1 -1
  81. package/cjs/theme/tokens/colour.js +1 -1
  82. package/cjs/theme/tokens/colour.js.map +1 -1
  83. package/cjs/theme/tokens/radius.js +1 -1
  84. package/cjs/theme/tokens/radius.js.map +1 -1
  85. package/codemods/run-codemod.js +8 -2
  86. package/codemods/run-codemod.js.map +1 -1
  87. package/codemods/run-codemod.ts +13 -4
  88. package/drawer/drawer.d.ts +1 -1
  89. package/drawer/drawer.js +1 -1
  90. package/drawer/drawer.js.map +1 -1
  91. package/drawer/drawer.styles.d.ts +3 -0
  92. package/drawer/drawer.styles.js +1 -1
  93. package/drawer/drawer.styles.js.map +1 -1
  94. package/{cjs/drawer/drawer.styles_94hx3y.css → drawer/drawer.styles_a4bwe0.css} +6 -3
  95. package/drawer/types.d.ts +7 -0
  96. package/external/dompurify/dist/purify.es.js +4 -2
  97. package/external/dompurify/dist/purify.es.js.map +1 -1
  98. package/file-upload/file-list/file-list.js +1 -1
  99. package/file-upload/file-list/file-list.js.map +1 -1
  100. package/file-upload/file-list/file-list.styles.d.ts +0 -1
  101. package/file-upload/file-list/file-list.styles.js +1 -1
  102. package/file-upload/file-list/file-list.styles.js.map +1 -1
  103. package/file-upload/file-list/file-list.styles_1xbypnh.css +2 -0
  104. package/file-upload/file-list-item/file-item-actions.d.ts +30 -0
  105. package/file-upload/file-list-item/file-item-actions.js +2 -0
  106. package/file-upload/file-list-item/file-item-actions.js.map +1 -0
  107. package/file-upload/file-list-item/file-item-actions.styles.d.ts +6 -0
  108. package/file-upload/file-list-item/file-item-actions.styles.js +2 -0
  109. package/file-upload/file-list-item/file-item-actions.styles.js.map +1 -0
  110. package/file-upload/file-list-item/file-item-actions.styles_1quulgy.css +8 -0
  111. package/file-upload/file-list-item/file-item-details.d.ts +15 -0
  112. package/file-upload/file-list-item/file-item-details.js +2 -0
  113. package/file-upload/file-list-item/file-item-details.js.map +1 -0
  114. package/file-upload/file-list-item/file-item-details.styles.d.ts +7 -0
  115. package/file-upload/file-list-item/file-item-details.styles.js +2 -0
  116. package/file-upload/file-list-item/file-item-details.styles.js.map +1 -0
  117. package/file-upload/file-list-item/file-item-details.styles_1ce9akx.css +7 -0
  118. package/file-upload/file-list-item/file-list-item.d.ts +1 -7
  119. package/file-upload/file-list-item/file-list-item.js +1 -1
  120. package/file-upload/file-list-item/file-list-item.js.map +1 -1
  121. package/file-upload/file-list-item/file-list-item.styles.d.ts +6 -13
  122. package/file-upload/file-list-item/file-list-item.styles.js +1 -1
  123. package/file-upload/file-list-item/file-list-item.styles.js.map +1 -1
  124. package/file-upload/file-list-item/file-list-item.styles_x3a500.css +10 -0
  125. package/file-upload/file-list-item/types.d.ts +10 -3
  126. package/file-upload/file-list-item/use-truncated-name.d.ts +1 -0
  127. package/file-upload/file-list-item/use-truncated-name.js +2 -0
  128. package/file-upload/file-list-item/use-truncated-name.js.map +1 -0
  129. package/input-group/types.d.ts +4 -1
  130. package/input-select/types.d.ts +1 -0
  131. package/local-nav/local-nav-dropdown/local-nav-dropdown.js +1 -1
  132. package/local-nav/local-nav-dropdown/local-nav-dropdown.js.map +1 -1
  133. package/local-nav/local-nav-dropdown/local-nav-dropdown.styles.d.ts +3 -0
  134. package/local-nav/local-nav-dropdown/local-nav-dropdown.styles.js +1 -1
  135. package/local-nav/local-nav-dropdown/local-nav-dropdown.styles.js.map +1 -1
  136. package/{cjs/local-nav/local-nav-dropdown/local-nav-dropdown.styles_uhd4xl.css → local-nav/local-nav-dropdown/local-nav-dropdown.styles_kpel1m.css} +9 -6
  137. package/local-nav/local-nav-menu/local-nav-menu.js +1 -1
  138. package/local-nav/local-nav-menu/local-nav-menu.js.map +1 -1
  139. package/local-nav/local-nav-menu/local-nav-menu.styles.d.ts +3 -0
  140. package/local-nav/local-nav-menu/local-nav-menu.styles.js +1 -1
  141. package/local-nav/local-nav-menu/local-nav-menu.styles.js.map +1 -1
  142. package/local-nav/local-nav-menu/local-nav-menu.styles_67fcyc.css +8 -0
  143. package/local-nav/types.d.ts +17 -0
  144. package/masked-input/types.d.ts +1 -2
  145. package/menu/menu-content.js +1 -1
  146. package/menu/menu-content.js.map +1 -1
  147. package/menu/menu-content.styles.d.ts +1 -0
  148. package/menu/menu-content.styles.js +1 -1
  149. package/menu/menu-content.styles.js.map +1 -1
  150. package/menu/menu-content.styles_1r8oxtk.css +5 -0
  151. package/menu/menu-item.styles.js +1 -1
  152. package/menu/menu-item.styles.js.map +1 -1
  153. package/menu/{menu-item.styles_liyed8.css → menu-item.styles_eut2j9.css} +1 -1
  154. package/menu/menu-link.styles.js +1 -1
  155. package/menu/menu-link.styles.js.map +1 -1
  156. package/menu/menu-link.styles_rng7n7.css +2 -0
  157. package/menu/menu-section.d.ts +1 -1
  158. package/menu/menu-section.js +1 -1
  159. package/menu/menu-section.js.map +1 -1
  160. package/menu/menu-section.styles.d.ts +7 -0
  161. package/menu/menu-section.styles.js +1 -1
  162. package/menu/menu-section.styles.js.map +1 -1
  163. package/menu/menu-section.styles_1n0ppxl.css +5 -0
  164. package/menu/menu.d.ts +1 -1
  165. package/menu/types.d.ts +10 -2
  166. package/modal-v2/slots/slot-styles.js +1 -1
  167. package/modal-v2/slots/slot-styles.js.map +1 -1
  168. package/modal-v2/slots/{slot-styles_ayb9il.css → slot-styles_ygu6zc.css} +1 -1
  169. package/navbar/navbar-items.js +1 -1
  170. package/navbar/navbar-items.js.map +1 -1
  171. package/navbar/types.d.ts +9 -0
  172. package/notification-banner/notification-banner-hoc.js +1 -1
  173. package/notification-banner/notification-banner-hoc.js.map +1 -1
  174. package/package.json +3 -4
  175. package/pagination/pagination.js +1 -1
  176. package/pagination/pagination.js.map +1 -1
  177. package/pagination/types.d.ts +8 -1
  178. package/scripts/cli.js +18 -0
  179. package/select-histogram/types.d.ts +1 -0
  180. package/shared/dropdown-list/dropdown-label.d.ts +2 -2
  181. package/shared/dropdown-list/dropdown-label.js +1 -1
  182. package/shared/dropdown-list/dropdown-label.js.map +1 -1
  183. package/shared/dropdown-list/types.d.ts +6 -2
  184. package/shared/fade-wrapper/fade-wrapper.js +1 -1
  185. package/shared/fade-wrapper/fade-wrapper.js.map +1 -1
  186. package/shared/fade-wrapper/types.d.ts +1 -0
  187. package/shared/styles/index.d.ts +5 -0
  188. package/skills/fds-build/SKILL.md +167 -0
  189. package/skills/fds-build/catalogue.md +181 -0
  190. package/skills/fds-build/evals/evals.json +519 -0
  191. package/skills/fds-build/resources/components/accordion.md +128 -0
  192. package/skills/fds-build/resources/components/alert.md +54 -0
  193. package/skills/fds-build/resources/components/animation.md +18 -0
  194. package/skills/fds-build/resources/components/avatar.md +10 -0
  195. package/skills/fds-build/resources/components/badge.md +18 -0
  196. package/skills/fds-build/resources/components/box-container.md +61 -0
  197. package/skills/fds-build/resources/components/breadcrumb.md +60 -0
  198. package/skills/fds-build/resources/components/button.md +74 -0
  199. package/skills/fds-build/resources/components/calendar.md +113 -0
  200. package/skills/fds-build/resources/components/card.md +43 -0
  201. package/skills/fds-build/resources/components/checkbox.md +53 -0
  202. package/skills/fds-build/resources/components/countdown-timer.md +20 -0
  203. package/skills/fds-build/resources/components/data-table.md +142 -0
  204. package/skills/fds-build/resources/components/date-navigator.md +25 -0
  205. package/skills/fds-build/resources/components/divider.md +23 -0
  206. package/skills/fds-build/resources/components/drawer.md +78 -0
  207. package/skills/fds-build/resources/components/error-display.md +137 -0
  208. package/skills/fds-build/resources/components/feedback-rating.md +61 -0
  209. package/skills/fds-build/resources/components/file-download.md +54 -0
  210. package/skills/fds-build/resources/components/file-upload.md +105 -0
  211. package/skills/fds-build/resources/components/filter.md +228 -0
  212. package/skills/fds-build/resources/components/footer.md +94 -0
  213. package/skills/fds-build/resources/components/form-date-input.md +85 -0
  214. package/skills/fds-build/resources/components/form-date-range-input.md +146 -0
  215. package/skills/fds-build/resources/components/form-e-signature.md +55 -0
  216. package/skills/fds-build/resources/components/form-input.md +65 -0
  217. package/skills/fds-build/resources/components/form-masked-input.md +69 -0
  218. package/skills/fds-build/resources/components/form-multi-select.md +100 -0
  219. package/skills/fds-build/resources/components/form-nested-multi-select.md +159 -0
  220. package/skills/fds-build/resources/components/form-nested-select.md +151 -0
  221. package/skills/fds-build/resources/components/form-otp-verification.md +175 -0
  222. package/skills/fds-build/resources/components/form-phone-number-input.md +87 -0
  223. package/skills/fds-build/resources/components/form-predictive-text-input.md +108 -0
  224. package/skills/fds-build/resources/components/form-select.md +103 -0
  225. package/skills/fds-build/resources/components/form-slider.md +116 -0
  226. package/skills/fds-build/resources/components/form-textarea.md +116 -0
  227. package/skills/fds-build/resources/components/form-time-range-picker.md +98 -0
  228. package/skills/fds-build/resources/components/form-timepicker.md +77 -0
  229. package/skills/fds-build/resources/components/form-unit-number-input.md +138 -0
  230. package/skills/fds-build/resources/components/fullscreen-image-carousel.md +159 -0
  231. package/skills/fds-build/resources/components/histogram-slider.md +41 -0
  232. package/skills/fds-build/resources/components/icon.md +167 -0
  233. package/skills/fds-build/resources/components/image-button.md +12 -0
  234. package/skills/fds-build/resources/components/input.md +51 -0
  235. package/skills/fds-build/resources/components/language-switcher.md +15 -0
  236. package/skills/fds-build/resources/components/layout.md +156 -0
  237. package/skills/fds-build/resources/components/link-list.md +120 -0
  238. package/skills/fds-build/resources/components/local-nav.md +119 -0
  239. package/skills/fds-build/resources/components/masthead.md +14 -0
  240. package/skills/fds-build/resources/components/menu.md +109 -0
  241. package/skills/fds-build/resources/components/modal.md +150 -0
  242. package/skills/fds-build/resources/components/navbar.md +270 -0
  243. package/skills/fds-build/resources/components/notification-banner.md +106 -0
  244. package/skills/fds-build/resources/components/pagination.md +104 -0
  245. package/skills/fds-build/resources/components/pill.md +55 -0
  246. package/skills/fds-build/resources/components/popover.md +80 -0
  247. package/skills/fds-build/resources/components/progress-indicator.md +47 -0
  248. package/skills/fds-build/resources/components/radio-button.md +66 -0
  249. package/skills/fds-build/resources/components/schedule.md +78 -0
  250. package/skills/fds-build/resources/components/sidenav.md +183 -0
  251. package/skills/fds-build/resources/components/singpass-button.md +14 -0
  252. package/skills/fds-build/resources/components/smart-app-banner.md +28 -0
  253. package/skills/fds-build/resources/components/tab.md +107 -0
  254. package/skills/fds-build/resources/components/table.md +65 -0
  255. package/skills/fds-build/resources/components/tag.md +68 -0
  256. package/skills/fds-build/resources/components/text-list.md +44 -0
  257. package/skills/fds-build/resources/components/time-slot-bar-week.md +66 -0
  258. package/skills/fds-build/resources/components/time-slot-bar.md +57 -0
  259. package/skills/fds-build/resources/components/time-slot-week-view.md +70 -0
  260. package/skills/fds-build/resources/components/timeline.md +69 -0
  261. package/skills/fds-build/resources/components/timetable.md +123 -0
  262. package/skills/fds-build/resources/components/toast.md +72 -0
  263. package/skills/fds-build/resources/components/toggle.md +119 -0
  264. package/skills/fds-build/resources/components/typography.md +86 -0
  265. package/skills/fds-build/resources/components/uneditable-section.md +84 -0
  266. package/skills/fds-build/resources/foundations/layout-composition-patterns.md +53 -0
  267. package/skills/fds-build/resources/foundations/tokens.md +547 -0
  268. package/skills/fds-build/resources/patterns/states.md +300 -0
  269. package/skills/fds-build/resources/setup/setup.md +202 -0
  270. package/skills/fds-build/resources/templates/form.md +427 -0
  271. package/skills/fds-build/resources/templates/listing.md +435 -0
  272. package/theme/styles/a11y-playground.css +52 -40
  273. package/theme/styles/bookingsg.css +52 -40
  274. package/theme/styles/careercompass.css +52 -40
  275. package/theme/styles/ccube.css +52 -40
  276. package/theme/styles/imda.css +52 -40
  277. package/theme/styles/lifesg.css +52 -40
  278. package/theme/styles/mylegacy.css +52 -40
  279. package/theme/styles/oneservice.css +52 -40
  280. package/theme/styles/pa.css +28 -22
  281. package/theme/styles/rbs.css +52 -40
  282. package/theme/styles/sgw-digital-lobby.css +52 -40
  283. package/theme/styles/smgs.css +52 -40
  284. package/theme/styles/spf.css +28 -27
  285. package/theme/styles/sportsg-orange.css +52 -40
  286. package/theme/styles/supportgowhere.css +52 -40
  287. package/theme/styles/tote-board.css +52 -40
  288. package/theme/styles/vica.css +52 -40
  289. package/theme/styles/websg.css +52 -40
  290. package/theme/styles/wise.css +52 -40
  291. package/theme/styles/wogaa.css +52 -40
  292. package/theme/tokens/colour.d.ts +6 -0
  293. package/theme/tokens/colour.js +1 -1
  294. package/theme/tokens/colour.js.map +1 -1
  295. package/theme/tokens/radius.d.ts +3 -0
  296. package/theme/tokens/radius.js +1 -1
  297. package/theme/tokens/radius.js.map +1 -1
  298. package/breadcrumb/breadcrumb.styles_1nl4aua.css +0 -18
  299. package/cjs/breadcrumb/breadcrumb.styles_1nl4aua.css +0 -18
  300. package/cjs/file-upload/file-item-edit.js +0 -2
  301. package/cjs/file-upload/file-item-edit.js.map +0 -1
  302. package/cjs/file-upload/file-item-edit.styles.js +0 -2
  303. package/cjs/file-upload/file-item-edit.styles.js.map +0 -1
  304. package/cjs/file-upload/file-item-edit.styles_19bvztn.css +0 -11
  305. package/cjs/file-upload/file-list/file-list.styles_1w7so98.css +0 -3
  306. package/cjs/file-upload/file-list-item/file-list-item.styles_kgtfke.css +0 -17
  307. package/cjs/local-nav/local-nav-menu/local-nav-menu.styles_pq19dh.css +0 -5
  308. package/cjs/menu/menu-content.styles_p7ijll.css +0 -2
  309. package/cjs/menu/menu-link.styles_hfxoez.css +0 -2
  310. package/cjs/menu/menu-section.styles_g7my4d.css +0 -3
  311. package/file-upload/file-item-edit.d.ts +0 -14
  312. package/file-upload/file-item-edit.js +0 -2
  313. package/file-upload/file-item-edit.js.map +0 -1
  314. package/file-upload/file-item-edit.styles.d.ts +0 -9
  315. package/file-upload/file-item-edit.styles.js +0 -2
  316. package/file-upload/file-item-edit.styles.js.map +0 -1
  317. package/file-upload/file-item-edit.styles_19bvztn.css +0 -11
  318. package/file-upload/file-list/file-list.styles_1w7so98.css +0 -3
  319. package/file-upload/file-list-item/file-list-item.styles_kgtfke.css +0 -17
  320. package/local-nav/local-nav-menu/local-nav-menu.styles_pq19dh.css +0 -5
  321. package/menu/menu-content.styles_p7ijll.css +0 -2
  322. package/menu/menu-link.styles_hfxoez.css +0 -2
  323. package/menu/menu-section.styles_g7my4d.css +0 -3
@@ -0,0 +1,105 @@
1
+ # FileUpload
2
+
3
+ Import: `import { FileUpload } from "@lifesg/react-design-system/file-upload"`
4
+
5
+ ## When to use
6
+
7
+ - Forms that require users to attach one or more files (e.g. supporting documents, ID scans, images).
8
+ - Use `sortable` when file order matters for downstream processing.
9
+ - Use `editableFileItems` when image files need alt-text or descriptions.
10
+ - Use `readOnly` on review or summary screens to display uploaded files without allowing changes.
11
+
12
+ ## Usage
13
+
14
+ ```tsx
15
+ import { FileUpload } from "@lifesg/react-design-system/file-upload";
16
+
17
+ <FileUpload
18
+ title="Upload supporting documents"
19
+ description="PDF, JPG, or PNG. Max 5 MB each."
20
+ accept=".pdf,.jpg,.png"
21
+ fileItems={fileItems}
22
+ onChange={(files) => {}}
23
+ onDelete={(item) => {}}
24
+ />;
25
+ ```
26
+
27
+ ## Custom labels
28
+
29
+ | Prop | Type | Default | Description |
30
+ | -------------------------------- | -------- | ---------------- | ------------------------------------------------- |
31
+ | `customLabels.uploadButtonLabel` | `string` | `"Upload files"` | Label of the button that triggers the file dialog |
32
+
33
+ ## Props — `FileItemProps`
34
+
35
+ | Prop | Type | Default | Description |
36
+ | ----------------------- | --------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
37
+ | `id` \* | `string` | — | Unique identifier used to target delete, edit, and sort operations. |
38
+ | `name` \* | `string` | — | The display name of the file |
39
+ | `type` \* | `string` | — | The MIME type of the file (e.g. `"application/pdf"`). |
40
+ | `size` \* | `number` | — | The size of the file in bytes. Displayed as formatted text when provided. |
41
+ | `description` | `string` | — | The alt text or description of the file |
42
+ | `progress` | `number` | — | Upload progress expressed as a fraction between 0 and 1. When provided, a progress indicator is rendered on the file item. |
43
+ | `errorMessage` | `string \| React.ReactNode` | — | Error message shown on the file item to indicate an upload failure. Accepts a plain string or a React node for richer error display. |
44
+ | `thumbnailImageDataUrl` | `string` | — | URL or data URL for a thumbnail image shown alongside the file entry. When not provided, a PDF icon is displayed for PDF files only; other file types show no default icon. |
45
+ | `truncateText` | `boolean` | true | Truncates long file names with an ellipsis when `true`. |
46
+
47
+ ## Props — `FileInputProps`
48
+
49
+ | Prop | Type | Default | Description |
50
+ | ----------- | ------------------------------------ | ------- | ---------------------------------------------------------------- |
51
+ | `accept` | `string` | — | Defines the file types that is accepted |
52
+ | `capture` | `boolean \| "user" \| "environment"` | — | Specifies which camera to use for capture of image or video data |
53
+ | `multiple` | `boolean` | — | Specifies if user is allowed to select more than one file. |
54
+ | `disabled` | `boolean` | — | — |
55
+ | `id` | `string` | — | — |
56
+ | `className` | `string` | — | — |
57
+ | `name` | `string` | — | — |
58
+
59
+ ## Props — `FileUploadProps`
60
+
61
+ | Prop | Type | Default | Description |
62
+ | -------------------------- | ---------------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
63
+ | `title` | `string \| JSX.Element` | — | Heading rendered at the top of the component. Accepts a plain string or a JSX element for rich formatting. |
64
+ | `description` | `string \| JSX.Element` | — | Supporting text rendered below `title`. Accepts a plain string or a JSX element for rich formatting. |
65
+ | `styleType` | `FileUploadStyle` | "bordered" | Visual style variant for the component. |
66
+ | `maxFiles` | `number` | — | Maximum number of files the component will accept. Once `fileItems.length` reaches this value, uploading is disabled until an item is removed. |
67
+ | `warning` | `string \| JSX.Element` | — | Warning message rendered inside the component. Accepts a plain string or a JSX element for rich formatting. |
68
+ | `fileItems` | `FileItemProps[]` | — | Controlled list of file items displayed in the file list. Update this array in response to `onChange`, `onDelete`, `onEdit`, and `onSort` to reflect the current upload state. |
69
+ | `editableFileItems` | `boolean` | false | When `true`, image file items render an editable description field. Editing triggers `onEdit` with the updated `FileItemProps`. |
70
+ | `fileDescriptionMaxLength` | `number` | — | Character limit for the description input on editable image file items. Only applies when `editableFileItems` is `true`. |
71
+ | `descriptionRequired` | `boolean` | true | When `false`, the save button on an editable file item is enabled even when the description field is empty. |
72
+ | `descriptionLabel` | `FormLabelProps` | — | Overrides the default label and subtext for the description input field on editable file items. |
73
+ | `sortable` | `boolean` | false | When `true`, file items can be reordered via drag-and-drop. The reordered list is emitted via `onSort`. |
74
+ | `errorMessage` | `string \| JSX.Element` | — | General error message rendered below the file list. Accepts a plain string or a JSX element for rich formatting. |
75
+ | `readOnly` | `boolean` | — | When `true`, the upload button and drop zone are hidden and file items are rendered without delete or edit controls. |
76
+ | `customLabels` | `{ uploadButtonLabel?: string }` | — | Overrides default label text for elements within the component. |
77
+ | `onChange` | `((files: File[]) => void)` | — | Called with the newly uploaded files. Receives only the files that fit within the remaining `maxFiles` slots. |
78
+ | `onEdit` | `((fileItem: FileItemProps) => void)` | — | Called when the user saves an edited description on a file item. |
79
+ | `onDelete` | `((fileItem: FileItemProps) => void)` | — | Called when the user clicks the delete button on a file item. |
80
+ | `onSort` | `((fileItems: FileItemProps[]) => void)` | — | Called after the user reorders file items via drag-and-drop. Only fired when `sortable` is `true`. |
81
+ | `accept` | `string` | — | Defines the file types that is accepted |
82
+ | `capture` | `boolean \| "user" \| "environment"` | — | Specifies which camera to use for capture of image or video data |
83
+ | `multiple` | `boolean` | — | Specifies if user is allowed to select more than one file. |
84
+ | `disabled` | `boolean` | — | — |
85
+ | `id` | `string` | — | — |
86
+ | `className` | `string` | — | — |
87
+ | `name` | `string` | — | — |
88
+
89
+ ## Rules
90
+
91
+ - Always manage `fileItems` state in the parent — the component does not store files internally.
92
+ - Always handle `onChange` to convert incoming `File[]` into `FileItemProps[]` and add them to state.
93
+ - Always handle `onDelete` to remove the corresponding item from state.
94
+ - Use `maxFiles` to enforce the maximum number of attachments allowed by the backend.
95
+ - Use `progress` on individual `FileItemProps` to show per-file upload progress.
96
+ - Use `errorMessage` on individual `FileItemProps` to surface per-file upload errors.
97
+ - Use the component-level `errorMessage` for overall validation errors (e.g. "At least one file is required").
98
+ - Use `accept` (inherited from `<input type="file">`) to restrict file types to what the backend accepts.
99
+
100
+ ## Anti-patterns
101
+
102
+ - Do not use without wiring `onChange` and `onDelete` — the list will not update.
103
+ - Do not store files in component state inside the same component that renders `FileUpload` — hoist state to the form level.
104
+ - Do not skip `id`, `name`, `type`, and `size` in `FileItemProps` — they are required fields.
105
+ - Do not use `editableFileItems` for non-image files — description editing only applies to images.
@@ -0,0 +1,228 @@
1
+ # Filter
2
+
3
+ Import: `import { Filter } from "@lifesg/react-design-system/filter"`
4
+
5
+ ## When to use
6
+
7
+ Use on listing pages to narrow a result set. `Filter` automatically switches between a desktop sidebar and a mobile modal at the `lg-max` breakpoint.
8
+
9
+ ## Composition
10
+
11
+ ```
12
+ Filter ← recommended: auto-switches sidebar ↔ modal at lg breakpoint
13
+ ├── Filter.Item title="Sort by" ← section header + collapsible wrapper
14
+ │ └── {RadioButton group} ← sort options use RadioButton, not Filter.Checkbox
15
+ ├── Filter.Item title="Category"
16
+ │ └── Filter.Checkbox ← multi-select filter options
17
+ └── Filter.Item title="..."
18
+ └── Filter.Checkbox
19
+ ```
20
+
21
+ Manual variants (bypass auto-responsive):
22
+
23
+ ```
24
+ Filter.Sidebar ← desktop sidebar only
25
+ Filter.Modal ← mobile modal only
26
+ Filter.Page ← full-page filter (mobile-only flows)
27
+ ```
28
+
29
+ `Filter.Item` is the only valid direct child of `Filter`, `Filter.Sidebar`, or `Filter.Modal`. Use `RadioButton` for sort (single-select); use `Filter.Checkbox` for multi-select filters.
30
+
31
+ ## Variants
32
+
33
+ | Variant | When to use |
34
+ | ------------------------ | --------------------------------------------------------------- |
35
+ | `<Filter>` (recommended) | Auto-responsive — desktop sidebar + mobile modal |
36
+ | `<Filter.Sidebar>` | Desktop sidebar only — when bypassing auto-responsive behaviour |
37
+ | `<Filter.Modal>` | Mobile modal only — when bypassing auto-responsive behaviour |
38
+
39
+ ## Usage
40
+
41
+ ### Auto-responsive Filter (recommended)
42
+
43
+ `Filter` renders a sidebar on desktop and a modal on mobile automatically. Pass children as a function to receive `mode` (`"desktop" | "mobile"`) when you need to render differently per viewport.
44
+
45
+ ```tsx
46
+ import { useState } from "react";
47
+ import { Filter } from "@lifesg/react-design-system/filter";
48
+ import { RadioButton } from "@lifesg/react-design-system/radio-button";
49
+
50
+ const ListingFilter = () => {
51
+ const [category, setCategory] = useState("all");
52
+ const [activeCount, setActiveCount] = useState(0);
53
+
54
+ const handleClear = () => {
55
+ setCategory("all");
56
+ setActiveCount(0);
57
+ };
58
+
59
+ return (
60
+ <Filter
61
+ count={activeCount}
62
+ onClear={handleClear}
63
+ onDone={() => {
64
+ /* commit draft state */
65
+ }}
66
+ onDismiss={() => {
67
+ /* discard draft state */
68
+ }}
69
+ >
70
+ {(mode) => (
71
+ <>
72
+ <Filter.Item title="Category">
73
+ <RadioButton
74
+ name="category"
75
+ value="all"
76
+ checked={category === "all"}
77
+ onChange={() => setCategory("all")}
78
+ >
79
+ All
80
+ </RadioButton>
81
+ <RadioButton
82
+ name="category"
83
+ value="news"
84
+ checked={category === "news"}
85
+ onChange={() => setCategory("news")}
86
+ >
87
+ News
88
+ </RadioButton>
89
+ </Filter.Item>
90
+ <Filter.Item title="Sort by" collapsible={false}>
91
+ <RadioButton name="sort" value="newest" defaultChecked>
92
+ Newest first
93
+ </RadioButton>
94
+ <RadioButton name="sort" value="oldest">
95
+ Oldest first
96
+ </RadioButton>
97
+ </Filter.Item>
98
+ </>
99
+ )}
100
+ </Filter>
101
+ );
102
+ };
103
+ ```
104
+
105
+ Key points:
106
+
107
+ - `onDone` and `onDismiss` are mobile-only callbacks; they are ignored on desktop.
108
+ - `collapsible={false}` on sort sections keeps them always expanded.
109
+ - `count` drives the active-filter badge in the header.
110
+
111
+ ## Props — `FilterBaseProps`
112
+
113
+ | Prop | Type | Default | Description |
114
+ | ------------------------- | ------------------------------------------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
115
+ | `id` | `string` | — | — |
116
+ | `className` | `string` | — | — |
117
+ | `style` | `CSSProperties` | — | — |
118
+ | `count` | `number` | — | Number of active filter selections displayed as a badge on the header and toggle button. |
119
+ | `clearButtonDisabled` | `boolean` | false | When `true`, the clear button is rendered in a disabled state. |
120
+ | `onClear` | `(() => void)` | — | Called when the clear button is pressed. |
121
+ | `children` | `ReactNode \| ((mode: Mode) => ReactNode)` | — | Content rendered inside the filter panel. Pass a render function to receive the current `Mode` and conditionally render different content for desktop (`"default"`) and mobile (`"mobile"`). |
122
+ | `headerTitle` | `string` | — | — |
123
+ | `toggleFilterButtonLabel` | `string` | — | — |
124
+ | `doneButtonLabel` | `string` | — | — |
125
+
126
+ ## Props — `FilterSidebarProps`
127
+
128
+ | Prop | Type | Default | Description |
129
+ | ------------------------- | ------------------------------------------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
130
+ | `customLabels` | `FilterSidebarCustomLabelProps` | — | Overrides default labels used by the sidebar variant. |
131
+ | `id` | `string` | — | — |
132
+ | `className` | `string` | — | — |
133
+ | `style` | `CSSProperties` | — | — |
134
+ | `count` | `number` | — | Number of active filter selections displayed as a badge on the header and toggle button. |
135
+ | `clearButtonDisabled` | `boolean` | false | When `true`, the clear button is rendered in a disabled state. |
136
+ | `onClear` | `(() => void)` | — | Called when the clear button is pressed. |
137
+ | `children` | `ReactNode \| ((mode: Mode) => ReactNode)` | — | Content rendered inside the filter panel. Pass a render function to receive the current `Mode` and conditionally render different content for desktop (`"default"`) and mobile (`"mobile"`). |
138
+ | `headerTitle` | `string` | — | — |
139
+ | `toggleFilterButtonLabel` | `string` | — | — |
140
+ | `doneButtonLabel` | `string` | — | — |
141
+
142
+ ## Props — `FilterModalProps`
143
+
144
+ | Prop | Type | Default | Description |
145
+ | ------------------------- | ------------------------------------------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
146
+ | `onDismiss` | `(() => void)` | — | Called when the dismiss (close) button is pressed in the modal overlay. |
147
+ | `onDone` | `(() => void)` | — | Called when the done button is pressed in the modal overlay. |
148
+ | `onModalOpen` | `() => void` | — | Called when the modal overlay is opened by the toggle button. |
149
+ | `toggleFilterButtonStyle` | `ButtonStyleType` | "light" | Visual style of the toggle button that opens the modal. |
150
+ | `customLabels` | `FilterModalCustomLabelProps` | — | Overrides default labels used by the modal variant. |
151
+ | `insets` | `Insets` | — | Safe-area insets applied to the modal header and footer, useful for notch or home-indicator offsets on mobile devices. |
152
+ | `id` | `string` | — | — |
153
+ | `className` | `string` | — | — |
154
+ | `style` | `CSSProperties` | — | — |
155
+ | `count` | `number` | — | Number of active filter selections displayed as a badge on the header and toggle button. |
156
+ | `clearButtonDisabled` | `boolean` | false | When `true`, the clear button is rendered in a disabled state. |
157
+ | `onClear` | `(() => void)` | — | Called when the clear button is pressed. |
158
+ | `children` | `ReactNode \| ((mode: Mode) => ReactNode)` | — | Content rendered inside the filter panel. Pass a render function to receive the current `Mode` and conditionally render different content for desktop (`"default"`) and mobile (`"mobile"`). |
159
+ | `headerTitle` | `string` | — | — |
160
+ | `toggleFilterButtonLabel` | `string` | — | — |
161
+ | `doneButtonLabel` | `string` | — | — |
162
+
163
+ ## Props — `FilterProps`
164
+
165
+ | Prop | Type | Default | Description |
166
+ | ------------------------- | ----------------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
167
+ | `customLabels` | `\| FilterModalCustomLabelProps \| FilterSidebarCustomLabelProps` | — | Overrides default labels for the active variant. Only the labels relevant to the current mode (sidebar or modal) are used. |
168
+ | `id` | `string` | — | — |
169
+ | `className` | `string` | — | — |
170
+ | `style` | `CSSProperties` | — | — |
171
+ | `count` | `number` | — | Number of active filter selections displayed as a badge on the header and toggle button. |
172
+ | `clearButtonDisabled` | `boolean` | false | When `true`, the clear button is rendered in a disabled state. |
173
+ | `onClear` | `(() => void)` | — | Called when the clear button is pressed. |
174
+ | `children` | `ReactNode \| ((mode: Mode) => ReactNode)` | — | Content rendered inside the filter panel. Pass a render function to receive the current `Mode` and conditionally render different content for desktop (`"default"`) and mobile (`"mobile"`). |
175
+ | `headerTitle` | `string` | — | — |
176
+ | `toggleFilterButtonLabel` | `string` | — | — |
177
+ | `doneButtonLabel` | `string` | — | — |
178
+ | `onDismiss` | `(() => void)` | — | Called when the dismiss (close) button is pressed in the modal overlay. |
179
+ | `onDone` | `(() => void)` | — | Called when the done button is pressed in the modal overlay. |
180
+ | `onModalOpen` | `() => void` | — | Called when the modal overlay is opened by the toggle button. |
181
+ | `toggleFilterButtonStyle` | `ButtonStyleType` | "light" | Visual style of the toggle button that opens the modal. |
182
+ | `insets` | `Insets` | — | Safe-area insets applied to the modal header and footer, useful for notch or home-indicator offsets on mobile devices. |
183
+
184
+ ## Props — `FilterItemProps`
185
+
186
+ | Prop | Type | Default | Description |
187
+ | ------------------- | ---------------------------------------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
188
+ | `children` \* | `\| React.ReactNode \| ((mode: Mode, state: { minimised: boolean }) => React.ReactNode)` | — | Content rendered inside the filter item. Pass a render function to receive the current `Mode` and minimised state, enabling content to adapt between desktop and mobile contexts. |
189
+ | `showDivider` | `boolean` | true | Whether a divider line is shown above the item in desktop mode. |
190
+ | `showMobileDivider` | `boolean` | true | Whether a divider line is shown above the item in mobile mode. |
191
+ | `title` | `string` | — | Label rendered in the item header. |
192
+ | `addon` | `FormLabelAddonProps` | — | Optional popover addon displayed inline with the title. |
193
+ | `className` | `string` | — | — |
194
+ | `id` | `string` | — | — |
195
+ | `collapsible` | `boolean` | true | Whether the item can be expanded or collapsed in desktop mode. Items are always expanded in mobile mode regardless of this setting. |
196
+ | `initialExpanded` | `boolean` | false | Initial expanded state for the uncontrolled component. Has no effect when `expanded` is provided. |
197
+ | `expanded` | `boolean` | — | Controls the expanded state. When provided, the component is controlled and `onExpandChange` must be used to update the value. |
198
+ | `onExpandChange` | `((expanded: boolean) => void)` | — | Called when the user toggles the expand/collapse state. |
199
+ | `minimisable` | `boolean` | false | Whether the item content can be minimised to show a truncated height with a "View more" button. |
200
+ | `minimisedHeight` | `number` | — | Height in pixels to clip the content when minimised. Defaults to 50% of content height, capped at 216 px. |
201
+ | `style` | `React.CSSProperties` | — | — |
202
+
203
+ ## Props — `FilterPageProps`
204
+
205
+ | Prop | Type | Default | Description |
206
+ | ------------- | ----------------- | ------- | ------------------------------------------------- |
207
+ | `children` \* | `React.ReactNode` | — | — |
208
+ | `onDismiss` | `(() => void)` | — | Called when the back (dismiss) button is pressed. |
209
+ | `onDone` | `(() => void)` | — | Called when the done button is pressed. |
210
+
211
+ ## Rules
212
+
213
+ - `Filter` is the recommended entry point — it automatically renders `Filter.Sidebar` on desktop and `Filter.Modal` on mobile.
214
+ - Always provide `onClear` to reset all filter state in a single handler.
215
+ - Set `collapsible={false}` on sort sections — sort options should always be visible.
216
+ - Pass children as a function `(mode) => ReactNode` to render different content on desktop vs mobile.
217
+ - `Filter.Modal` has a built-in Done button — do not add an external Apply button; use `onDone` to react to it.
218
+ - Add a keyword search input (`Form.Input`) above the `Filter` component — search is not built into `Filter`.
219
+ - Use `count` to show the number of active filters in the header.
220
+ - Use `Filter.Sidebar` or `Filter.Modal` directly only when overriding the responsive behaviour.
221
+ - `Filter.Page` is used inside `Filter.Modal` only, for drill-down sub-pages.
222
+
223
+ ## Anti-patterns
224
+
225
+ - Do not pass `viewport`, `showSearch`, or `searchText` — these props do not exist.
226
+ - Do not use `headerTitle`, `toggleFilterButtonLabel`, or `doneButtonLabel` directly on `Filter` — these are deprecated. Use `customLabels` instead.
227
+ - Do not add an external Apply button when using `Filter.Modal` — the built-in Done button handles it.
228
+ - Do not use `Filter.Sidebar` and `Filter.Modal` separately unless you need to override responsive behaviour — use `Filter` instead.
@@ -0,0 +1,94 @@
1
+ # Footer
2
+
3
+ Import: `import { Footer } from "@lifesg/react-design-system/footer"`
4
+
5
+ ## When to use
6
+
7
+ Place at the bottom of every page. Required on all pages.
8
+
9
+ ## Variants
10
+
11
+ | Variant | When to use |
12
+ | ----------------------- | ---------------------------------------------------------------- |
13
+ | `<Footer />` (no props) | Standard pages — disclaimer links and copyright are theme-driven |
14
+ | `links` | Pages with navigational footer links (max 2 columns) |
15
+ | `copyrightInfo` | Custom copyright text required |
16
+ | `layout="stretch"` | Full-width layout — pair with `Navbar layout="stretch"` |
17
+
18
+ ## Usage
19
+
20
+ ```tsx
21
+ import { Footer } from "@lifesg/react-design-system/footer";
22
+
23
+ {
24
+ /* Standard footer — no props required */
25
+ }
26
+ <Footer />;
27
+
28
+ {
29
+ /* Footer with navigation links */
30
+ }
31
+ <Footer
32
+ links={[
33
+ [
34
+ { children: "About us", href: "/about" },
35
+ { children: "Contact", href: "/contact" },
36
+ ],
37
+ ]}
38
+ lastUpdated={new Date("2025-01-01")}
39
+ />;
40
+ ```
41
+
42
+ ## Props — `DisclaimerLinks`
43
+
44
+ | Prop | Type | Default | Description |
45
+ | --------------------- | ------------------------- | ------- | ----------- |
46
+ | `privacy` | `DisclaimerTextLinkProps` | — | — |
47
+ | `termsOfUse` | `DisclaimerTextLinkProps` | — | — |
48
+ | `reportVulnerability` | `DisclaimerTextLinkProps` | — | — |
49
+
50
+ ## Props — `FooterLinkProps`
51
+
52
+ | Prop | Type | Default | Description |
53
+ | ---------------- | -------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------- |
54
+ | `data-options` | `T` | — | Any custom attributes you would like to pass to the link |
55
+ | `weight` | `TypographyWeight` | "regular" | Font weight of the link text. |
56
+ | `external` | `boolean` | — | Appends an external-link icon after the link text to signal that the destination is outside the current site. |
57
+ | `underlineStyle` | `TypographyUnderlineStyle` | "underline" | Text-decoration style for the link. |
58
+
59
+ ## Props — `FooterProps`
60
+
61
+ | Prop | Type | Default | Description |
62
+ | ------------------- | -------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
63
+ | `links` | `FooterLinkProps<T>[][]` | — | The footer links. Multi array to present in different columns |
64
+ | `showDownloadAddon` | `boolean` | — | Indicates if the download app icons are to be present |
65
+ | `showResourceAddon` | `boolean` | — | Indicates if additional app resources should be displayed. When both `showDownloadAddon` and `showResourceAddon` are `true`, `showDownloadAddon` takes priority and the resource add-on is not rendered. |
66
+ | `children` | `JSX.Element \| JSX.Element[]` | — | Custom component. This overrides the logo, links and download section |
67
+ | `disclaimerLinks` | `DisclaimerLinks` | — | Override attributes for the built-in disclaimer links. Each supplied key is merged with the theme defaults. Omitted keys fall back to the default URLs for the active theme. |
68
+ | `copyrightInfo` | `string` | — | A custom copyright text |
69
+ | `logoSrc` | `string` | — | Custom logo source |
70
+ | `logoAlt` | `string` | — | Custom logo alt text |
71
+ | `lastUpdated` | `Date` | — | Date displayed in the copyright line as "Last updated D MMMM YYYY". |
72
+ | `onFooterLinkClick` | `((link: FooterLinkProps<T>) => void)` | — | Called when a footer navigation link is clicked. Receives the full `FooterLinkProps` for the clicked link. If the link defines its own `onClick` handler, that handler takes priority and `onFooterLinkClick` is not called. |
73
+ | `layout` | `"default" \| "stretch"` | "default" | Controls whether the footer content is constrained to a maximum width or stretches to fill the full viewport width. |
74
+ | `id` | `string` | — | — |
75
+ | `className` | `string` | — | — |
76
+ | `hideLogo` | `boolean` | — | Suppresses the logo even when a `logoSrc` or a theme-based logo URL is available. |
77
+
78
+ ## Rules
79
+
80
+ - Use `<Footer />` with no props for standard pages — do not add unnecessary props.
81
+ - `layout` must match the Navbar `layout` prop; do not mix `"default"` and `"stretch"`.
82
+ - Wrap the app in a flex column layout so the footer stays pinned to the bottom on short pages:
83
+ - Outer wrapper: `display: flex; flex-direction: column; min-height: 100vh`
84
+ - Page content area: `flex: 1`
85
+ - Disclaimer link text cannot be customised — only `href` and event handlers can be overridden via `disclaimerLinks`.
86
+ - `copyrightInfo` overrides `lastUpdated` — do not set both.
87
+ - When `showDownloadAddon` and `showResourceAddon` are both `true`, download takes precedence.
88
+ - `links` is a 2D array: each inner array is one column of links.
89
+
90
+ ## Anti-patterns
91
+
92
+ - Do not omit the flex layout wrapper — the footer will not stick to the bottom on short pages.
93
+ - Do not pass `children` and `links` together — `children` replaces the top section entirely.
94
+ - Do not customise disclaimer link labels — they are legally fixed strings.
@@ -0,0 +1,85 @@
1
+ # Form.DateInput
2
+
3
+ Import: `import { Form } from "@lifesg/react-design-system/form"`
4
+
5
+ ## When to use
6
+
7
+ - Collecting a single date from the user (e.g. date of birth, appointment date, submission deadline).
8
+ - Any form field that requires calendar-based date selection.
9
+
10
+ ## Usage
11
+
12
+ ```tsx
13
+ import styles from "./MyForm.module.css";
14
+
15
+ <Form.DateInput className={styles.dateInput} ... />
16
+ ```
17
+
18
+ ```css
19
+ /* MyForm.module.css */
20
+ :where(body.fds-breakpoint-lg-min) .dateInput {
21
+ max-width: 24rem;
22
+ }
23
+ ```
24
+
25
+ Defaults to 4 columns wide at `lg` and above; full-width on mobile. Use `max-width` via a CSS Module class — `max-width` constrains the component without conflicting with its internal `width: 100%`.
26
+
27
+ ## YearMonthDisplay
28
+
29
+ | Prop | Type | Description |
30
+ | ------- | -------- | --------------------------------- |
31
+ | `year` | `number` | Currently displayed year (`YYYY`) |
32
+ | `month` | `number` | Currently displayed month (1–12) |
33
+
34
+ ## Props
35
+
36
+ | Prop | Type | Default | Description |
37
+ | -------------------------- | ------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
38
+ | `className` | `string` | — | — |
39
+ | `id` | `string` | — | — |
40
+ | `dropdownRootNode` | `RefObject<HTMLElement>` | document.body | The root element that contains the dropdown element. |
41
+ | `readOnly` | `boolean` | — | — |
42
+ | `hideInputKeyboard` | `boolean` | — | When `true`, suppresses the native on-screen keyboard on mobile devices while still allowing the calendar picker to open. |
43
+ | `value` | `string` | — | The controlled value of the date input in `"YYYY-MM-DD"` or `"YYYY-M-D"` string format. |
44
+ | `error` | `boolean` | — | Renders the input in an error state. |
45
+ | `disabled` | `boolean` | — | — |
46
+ | `withButton` | `boolean` | true | Whether to show "Done" and "Cancel" action buttons inside the calendar dropdown. When `false`, a selection immediately commits and closes the calendar. |
47
+ | `zIndex` | `number` | — | The z-index of the calendar dropdown. |
48
+ | `onChange` | `((value: string) => void)` | — | Called when the user commits a date selection. Receives the selected date as a `"YYYY-MM-DD"` string, or an empty string when the selection is cleared. |
49
+ | `onFocus` | `(() => void)` | — | Called when the input receives focus. |
50
+ | `onBlur` | `(() => void)` | — | Called when focus leaves both the input and the calendar dropdown. |
51
+ | `onYearMonthDisplayChange` | `((value: YearMonthDisplay) => void)` | — | Called when the visible month or year in the calendar changes. |
52
+ | `minDate` | `string` | — | The minimum date that can be selected (inclusive) in 'YYYY-MM-DD' format. |
53
+ | `maxDate` | `string` | — | The maximum date that can be selected (inclusive) in 'YYYY-MM-DD' format. |
54
+ | `disabledDates` | `string[]` | — | Dates to disable in `YYYY-MM-DD` format. Example: `["2023-04-30"]` |
55
+ | `allowDisabledSelection` | `boolean` | — | Specifies if dates normally disabled by `minDate`, `maxDate` and `disabledDates` are still selectable |
56
+ | `showActiveMonthDaysOnly` | `boolean` | — | Specifies if the calendar should display only dates for the selected month |
57
+ | `label` | `FormLabelProps \| string` | — | Label rendered above the input. Accepts a plain string or a `FormLabelProps` object to provide additional configuration. |
58
+ | `errorMessage` | `string \| React.ReactNode` | — | Inline error text rendered below the input. |
59
+ | `layoutType` | `FormElementLayoutType` | — | Controls whether the container uses flex layout or a responsive column grid. |
60
+ | `xxlCols` | `ColSpan` | — | Column span at the `xxl` breakpoint. |
61
+ | `xlCols` | `ColSpan` | — | Column span at the `xl` breakpoint. |
62
+ | `lgCols` | `ColSpan` | — | Column span at the `lg` breakpoint. |
63
+ | `mdCols` | `ColSpan` | — | Column span at the `md` breakpoint. |
64
+ | `smCols` | `ColSpan` | — | Column span at the `sm` breakpoint. |
65
+ | `xsCols` | `ColSpan` | — | Column span at the `xs` breakpoint. |
66
+ | `xxsCols` | `ColSpan` | — | Column span at the `xxs` (smallest) breakpoint. Acts as the base value. All larger breakpoints fall back to this when their own prop is not set. |
67
+
68
+ ## Rules
69
+
70
+ - Always supply a `label` so the field is accessible.
71
+ - Pass `value` and `onChange` together for controlled usage; `onChange` receives a `YYYY-MM-DD` string.
72
+ - Use `minDate`/`maxDate` to constrain selectable dates to valid business ranges.
73
+ - Use `disabledDates` for known blackout dates (e.g. public holidays).
74
+ - Set `errorMessage` rather than `error` alone so users understand what is wrong.
75
+ - Use `withButton` (default `true`) to require explicit confirmation before the value is committed.
76
+ - Use `readOnly` on review or summary screens where the value should be visible but not editable.
77
+ - Specify `dropdownRootNode` if the component is inside a scrollable container to prevent the calendar from clipping.
78
+ - Date strings use `YYYY-MM-DD` format throughout (value, minDate, maxDate, disabledDates, onChange return).
79
+
80
+ ## Anti-patterns
81
+
82
+ - Do not use a plain text input for date collection — the picker enforces valid date formats automatically.
83
+ - Do not set `allowDisabledSelection` unless there is a specific UX reason to allow out-of-range dates.
84
+ - Do not rely on `error` prop alone without an `errorMessage`; users need descriptive feedback.
85
+ - Do not use for date-range selection — use `Form.DateRangeInput` instead.