@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.
- package/breadcrumb/breadcrumb.js +1 -1
- package/breadcrumb/breadcrumb.js.map +1 -1
- package/breadcrumb/breadcrumb.styles.d.ts +0 -8
- package/breadcrumb/breadcrumb.styles.js +1 -1
- package/breadcrumb/breadcrumb.styles.js.map +1 -1
- package/breadcrumb/breadcrumb.styles_1blzn8n.css +7 -0
- package/breadcrumb/types.d.ts +2 -13
- package/cjs/breadcrumb/breadcrumb.js +1 -1
- package/cjs/breadcrumb/breadcrumb.js.map +1 -1
- package/cjs/breadcrumb/breadcrumb.styles.js +1 -1
- package/cjs/breadcrumb/breadcrumb.styles.js.map +1 -1
- package/cjs/breadcrumb/breadcrumb.styles_1blzn8n.css +7 -0
- package/cjs/drawer/drawer.js +1 -1
- package/cjs/drawer/drawer.js.map +1 -1
- package/cjs/drawer/drawer.styles.js +1 -1
- package/cjs/drawer/drawer.styles.js.map +1 -1
- package/{drawer/drawer.styles_94hx3y.css → cjs/drawer/drawer.styles_a4bwe0.css} +6 -3
- package/cjs/external/dompurify/dist/purify.es.js +4 -2
- package/cjs/external/dompurify/dist/purify.es.js.map +1 -1
- package/cjs/file-upload/file-list/file-list.js +1 -1
- package/cjs/file-upload/file-list/file-list.js.map +1 -1
- package/cjs/file-upload/file-list/file-list.styles.js +1 -1
- package/cjs/file-upload/file-list/file-list.styles.js.map +1 -1
- package/cjs/file-upload/file-list/file-list.styles_1xbypnh.css +2 -0
- package/cjs/file-upload/file-list-item/file-item-actions.js +2 -0
- package/cjs/file-upload/file-list-item/file-item-actions.js.map +1 -0
- package/cjs/file-upload/file-list-item/file-item-actions.styles.js +2 -0
- package/cjs/file-upload/file-list-item/file-item-actions.styles.js.map +1 -0
- package/cjs/file-upload/file-list-item/file-item-actions.styles_1quulgy.css +8 -0
- package/cjs/file-upload/file-list-item/file-item-details.js +2 -0
- package/cjs/file-upload/file-list-item/file-item-details.js.map +1 -0
- package/cjs/file-upload/file-list-item/file-item-details.styles.js +2 -0
- package/cjs/file-upload/file-list-item/file-item-details.styles.js.map +1 -0
- package/cjs/file-upload/file-list-item/file-item-details.styles_1ce9akx.css +7 -0
- package/cjs/file-upload/file-list-item/file-list-item.js +1 -1
- package/cjs/file-upload/file-list-item/file-list-item.js.map +1 -1
- package/cjs/file-upload/file-list-item/file-list-item.styles.js +1 -1
- package/cjs/file-upload/file-list-item/file-list-item.styles.js.map +1 -1
- package/cjs/file-upload/file-list-item/file-list-item.styles_x3a500.css +10 -0
- package/cjs/file-upload/file-list-item/use-truncated-name.js +2 -0
- package/cjs/file-upload/file-list-item/use-truncated-name.js.map +1 -0
- package/cjs/local-nav/local-nav-dropdown/local-nav-dropdown.js +1 -1
- package/cjs/local-nav/local-nav-dropdown/local-nav-dropdown.js.map +1 -1
- package/cjs/local-nav/local-nav-dropdown/local-nav-dropdown.styles.js +1 -1
- package/cjs/local-nav/local-nav-dropdown/local-nav-dropdown.styles.js.map +1 -1
- 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
- package/cjs/local-nav/local-nav-menu/local-nav-menu.js +1 -1
- package/cjs/local-nav/local-nav-menu/local-nav-menu.js.map +1 -1
- package/cjs/local-nav/local-nav-menu/local-nav-menu.styles.js +1 -1
- package/cjs/local-nav/local-nav-menu/local-nav-menu.styles.js.map +1 -1
- package/cjs/local-nav/local-nav-menu/local-nav-menu.styles_67fcyc.css +8 -0
- package/cjs/menu/menu-content.js +1 -1
- package/cjs/menu/menu-content.js.map +1 -1
- package/cjs/menu/menu-content.styles.js +1 -1
- package/cjs/menu/menu-content.styles.js.map +1 -1
- package/cjs/menu/menu-content.styles_1r8oxtk.css +5 -0
- package/cjs/menu/menu-item.styles.js +1 -1
- package/cjs/menu/menu-item.styles.js.map +1 -1
- package/cjs/menu/{menu-item.styles_liyed8.css → menu-item.styles_eut2j9.css} +1 -1
- package/cjs/menu/menu-link.styles.js +1 -1
- package/cjs/menu/menu-link.styles.js.map +1 -1
- package/cjs/menu/menu-link.styles_rng7n7.css +2 -0
- package/cjs/menu/menu-section.js +1 -1
- package/cjs/menu/menu-section.js.map +1 -1
- package/cjs/menu/menu-section.styles.js +1 -1
- package/cjs/menu/menu-section.styles.js.map +1 -1
- package/cjs/menu/menu-section.styles_1n0ppxl.css +5 -0
- package/cjs/modal-v2/slots/slot-styles.js +1 -1
- package/cjs/modal-v2/slots/slot-styles.js.map +1 -1
- package/cjs/modal-v2/slots/{slot-styles_ayb9il.css → slot-styles_ygu6zc.css} +1 -1
- package/cjs/navbar/navbar-items.js +1 -1
- package/cjs/navbar/navbar-items.js.map +1 -1
- package/cjs/notification-banner/notification-banner-hoc.js +1 -1
- package/cjs/notification-banner/notification-banner-hoc.js.map +1 -1
- package/cjs/pagination/pagination.js +1 -1
- package/cjs/pagination/pagination.js.map +1 -1
- package/cjs/shared/dropdown-list/dropdown-label.js +1 -1
- package/cjs/shared/dropdown-list/dropdown-label.js.map +1 -1
- package/cjs/shared/fade-wrapper/fade-wrapper.js +1 -1
- package/cjs/shared/fade-wrapper/fade-wrapper.js.map +1 -1
- package/cjs/theme/tokens/colour.js +1 -1
- package/cjs/theme/tokens/colour.js.map +1 -1
- package/cjs/theme/tokens/radius.js +1 -1
- package/cjs/theme/tokens/radius.js.map +1 -1
- package/codemods/run-codemod.js +8 -2
- package/codemods/run-codemod.js.map +1 -1
- package/codemods/run-codemod.ts +13 -4
- package/drawer/drawer.d.ts +1 -1
- package/drawer/drawer.js +1 -1
- package/drawer/drawer.js.map +1 -1
- package/drawer/drawer.styles.d.ts +3 -0
- package/drawer/drawer.styles.js +1 -1
- package/drawer/drawer.styles.js.map +1 -1
- package/{cjs/drawer/drawer.styles_94hx3y.css → drawer/drawer.styles_a4bwe0.css} +6 -3
- package/drawer/types.d.ts +7 -0
- package/external/dompurify/dist/purify.es.js +4 -2
- package/external/dompurify/dist/purify.es.js.map +1 -1
- package/file-upload/file-list/file-list.js +1 -1
- package/file-upload/file-list/file-list.js.map +1 -1
- package/file-upload/file-list/file-list.styles.d.ts +0 -1
- package/file-upload/file-list/file-list.styles.js +1 -1
- package/file-upload/file-list/file-list.styles.js.map +1 -1
- package/file-upload/file-list/file-list.styles_1xbypnh.css +2 -0
- package/file-upload/file-list-item/file-item-actions.d.ts +30 -0
- package/file-upload/file-list-item/file-item-actions.js +2 -0
- package/file-upload/file-list-item/file-item-actions.js.map +1 -0
- package/file-upload/file-list-item/file-item-actions.styles.d.ts +6 -0
- package/file-upload/file-list-item/file-item-actions.styles.js +2 -0
- package/file-upload/file-list-item/file-item-actions.styles.js.map +1 -0
- package/file-upload/file-list-item/file-item-actions.styles_1quulgy.css +8 -0
- package/file-upload/file-list-item/file-item-details.d.ts +15 -0
- package/file-upload/file-list-item/file-item-details.js +2 -0
- package/file-upload/file-list-item/file-item-details.js.map +1 -0
- package/file-upload/file-list-item/file-item-details.styles.d.ts +7 -0
- package/file-upload/file-list-item/file-item-details.styles.js +2 -0
- package/file-upload/file-list-item/file-item-details.styles.js.map +1 -0
- package/file-upload/file-list-item/file-item-details.styles_1ce9akx.css +7 -0
- package/file-upload/file-list-item/file-list-item.d.ts +1 -7
- package/file-upload/file-list-item/file-list-item.js +1 -1
- package/file-upload/file-list-item/file-list-item.js.map +1 -1
- package/file-upload/file-list-item/file-list-item.styles.d.ts +6 -13
- package/file-upload/file-list-item/file-list-item.styles.js +1 -1
- package/file-upload/file-list-item/file-list-item.styles.js.map +1 -1
- package/file-upload/file-list-item/file-list-item.styles_x3a500.css +10 -0
- package/file-upload/file-list-item/types.d.ts +10 -3
- package/file-upload/file-list-item/use-truncated-name.d.ts +1 -0
- package/file-upload/file-list-item/use-truncated-name.js +2 -0
- package/file-upload/file-list-item/use-truncated-name.js.map +1 -0
- package/input-group/types.d.ts +4 -1
- package/input-select/types.d.ts +1 -0
- package/local-nav/local-nav-dropdown/local-nav-dropdown.js +1 -1
- package/local-nav/local-nav-dropdown/local-nav-dropdown.js.map +1 -1
- package/local-nav/local-nav-dropdown/local-nav-dropdown.styles.d.ts +3 -0
- package/local-nav/local-nav-dropdown/local-nav-dropdown.styles.js +1 -1
- package/local-nav/local-nav-dropdown/local-nav-dropdown.styles.js.map +1 -1
- 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
- package/local-nav/local-nav-menu/local-nav-menu.js +1 -1
- package/local-nav/local-nav-menu/local-nav-menu.js.map +1 -1
- package/local-nav/local-nav-menu/local-nav-menu.styles.d.ts +3 -0
- package/local-nav/local-nav-menu/local-nav-menu.styles.js +1 -1
- package/local-nav/local-nav-menu/local-nav-menu.styles.js.map +1 -1
- package/local-nav/local-nav-menu/local-nav-menu.styles_67fcyc.css +8 -0
- package/local-nav/types.d.ts +17 -0
- package/masked-input/types.d.ts +1 -2
- package/menu/menu-content.js +1 -1
- package/menu/menu-content.js.map +1 -1
- package/menu/menu-content.styles.d.ts +1 -0
- package/menu/menu-content.styles.js +1 -1
- package/menu/menu-content.styles.js.map +1 -1
- package/menu/menu-content.styles_1r8oxtk.css +5 -0
- package/menu/menu-item.styles.js +1 -1
- package/menu/menu-item.styles.js.map +1 -1
- package/menu/{menu-item.styles_liyed8.css → menu-item.styles_eut2j9.css} +1 -1
- package/menu/menu-link.styles.js +1 -1
- package/menu/menu-link.styles.js.map +1 -1
- package/menu/menu-link.styles_rng7n7.css +2 -0
- package/menu/menu-section.d.ts +1 -1
- package/menu/menu-section.js +1 -1
- package/menu/menu-section.js.map +1 -1
- package/menu/menu-section.styles.d.ts +7 -0
- package/menu/menu-section.styles.js +1 -1
- package/menu/menu-section.styles.js.map +1 -1
- package/menu/menu-section.styles_1n0ppxl.css +5 -0
- package/menu/menu.d.ts +1 -1
- package/menu/types.d.ts +10 -2
- package/modal-v2/slots/slot-styles.js +1 -1
- package/modal-v2/slots/slot-styles.js.map +1 -1
- package/modal-v2/slots/{slot-styles_ayb9il.css → slot-styles_ygu6zc.css} +1 -1
- package/navbar/navbar-items.js +1 -1
- package/navbar/navbar-items.js.map +1 -1
- package/navbar/types.d.ts +9 -0
- package/notification-banner/notification-banner-hoc.js +1 -1
- package/notification-banner/notification-banner-hoc.js.map +1 -1
- package/package.json +3 -4
- package/pagination/pagination.js +1 -1
- package/pagination/pagination.js.map +1 -1
- package/pagination/types.d.ts +8 -1
- package/scripts/cli.js +18 -0
- package/select-histogram/types.d.ts +1 -0
- package/shared/dropdown-list/dropdown-label.d.ts +2 -2
- package/shared/dropdown-list/dropdown-label.js +1 -1
- package/shared/dropdown-list/dropdown-label.js.map +1 -1
- package/shared/dropdown-list/types.d.ts +6 -2
- package/shared/fade-wrapper/fade-wrapper.js +1 -1
- package/shared/fade-wrapper/fade-wrapper.js.map +1 -1
- package/shared/fade-wrapper/types.d.ts +1 -0
- package/shared/styles/index.d.ts +5 -0
- package/skills/fds-build/SKILL.md +167 -0
- package/skills/fds-build/catalogue.md +181 -0
- package/skills/fds-build/evals/evals.json +519 -0
- package/skills/fds-build/resources/components/accordion.md +128 -0
- package/skills/fds-build/resources/components/alert.md +54 -0
- package/skills/fds-build/resources/components/animation.md +18 -0
- package/skills/fds-build/resources/components/avatar.md +10 -0
- package/skills/fds-build/resources/components/badge.md +18 -0
- package/skills/fds-build/resources/components/box-container.md +61 -0
- package/skills/fds-build/resources/components/breadcrumb.md +60 -0
- package/skills/fds-build/resources/components/button.md +74 -0
- package/skills/fds-build/resources/components/calendar.md +113 -0
- package/skills/fds-build/resources/components/card.md +43 -0
- package/skills/fds-build/resources/components/checkbox.md +53 -0
- package/skills/fds-build/resources/components/countdown-timer.md +20 -0
- package/skills/fds-build/resources/components/data-table.md +142 -0
- package/skills/fds-build/resources/components/date-navigator.md +25 -0
- package/skills/fds-build/resources/components/divider.md +23 -0
- package/skills/fds-build/resources/components/drawer.md +78 -0
- package/skills/fds-build/resources/components/error-display.md +137 -0
- package/skills/fds-build/resources/components/feedback-rating.md +61 -0
- package/skills/fds-build/resources/components/file-download.md +54 -0
- package/skills/fds-build/resources/components/file-upload.md +105 -0
- package/skills/fds-build/resources/components/filter.md +228 -0
- package/skills/fds-build/resources/components/footer.md +94 -0
- package/skills/fds-build/resources/components/form-date-input.md +85 -0
- package/skills/fds-build/resources/components/form-date-range-input.md +146 -0
- package/skills/fds-build/resources/components/form-e-signature.md +55 -0
- package/skills/fds-build/resources/components/form-input.md +65 -0
- package/skills/fds-build/resources/components/form-masked-input.md +69 -0
- package/skills/fds-build/resources/components/form-multi-select.md +100 -0
- package/skills/fds-build/resources/components/form-nested-multi-select.md +159 -0
- package/skills/fds-build/resources/components/form-nested-select.md +151 -0
- package/skills/fds-build/resources/components/form-otp-verification.md +175 -0
- package/skills/fds-build/resources/components/form-phone-number-input.md +87 -0
- package/skills/fds-build/resources/components/form-predictive-text-input.md +108 -0
- package/skills/fds-build/resources/components/form-select.md +103 -0
- package/skills/fds-build/resources/components/form-slider.md +116 -0
- package/skills/fds-build/resources/components/form-textarea.md +116 -0
- package/skills/fds-build/resources/components/form-time-range-picker.md +98 -0
- package/skills/fds-build/resources/components/form-timepicker.md +77 -0
- package/skills/fds-build/resources/components/form-unit-number-input.md +138 -0
- package/skills/fds-build/resources/components/fullscreen-image-carousel.md +159 -0
- package/skills/fds-build/resources/components/histogram-slider.md +41 -0
- package/skills/fds-build/resources/components/icon.md +167 -0
- package/skills/fds-build/resources/components/image-button.md +12 -0
- package/skills/fds-build/resources/components/input.md +51 -0
- package/skills/fds-build/resources/components/language-switcher.md +15 -0
- package/skills/fds-build/resources/components/layout.md +156 -0
- package/skills/fds-build/resources/components/link-list.md +120 -0
- package/skills/fds-build/resources/components/local-nav.md +119 -0
- package/skills/fds-build/resources/components/masthead.md +14 -0
- package/skills/fds-build/resources/components/menu.md +109 -0
- package/skills/fds-build/resources/components/modal.md +150 -0
- package/skills/fds-build/resources/components/navbar.md +270 -0
- package/skills/fds-build/resources/components/notification-banner.md +106 -0
- package/skills/fds-build/resources/components/pagination.md +104 -0
- package/skills/fds-build/resources/components/pill.md +55 -0
- package/skills/fds-build/resources/components/popover.md +80 -0
- package/skills/fds-build/resources/components/progress-indicator.md +47 -0
- package/skills/fds-build/resources/components/radio-button.md +66 -0
- package/skills/fds-build/resources/components/schedule.md +78 -0
- package/skills/fds-build/resources/components/sidenav.md +183 -0
- package/skills/fds-build/resources/components/singpass-button.md +14 -0
- package/skills/fds-build/resources/components/smart-app-banner.md +28 -0
- package/skills/fds-build/resources/components/tab.md +107 -0
- package/skills/fds-build/resources/components/table.md +65 -0
- package/skills/fds-build/resources/components/tag.md +68 -0
- package/skills/fds-build/resources/components/text-list.md +44 -0
- package/skills/fds-build/resources/components/time-slot-bar-week.md +66 -0
- package/skills/fds-build/resources/components/time-slot-bar.md +57 -0
- package/skills/fds-build/resources/components/time-slot-week-view.md +70 -0
- package/skills/fds-build/resources/components/timeline.md +69 -0
- package/skills/fds-build/resources/components/timetable.md +123 -0
- package/skills/fds-build/resources/components/toast.md +72 -0
- package/skills/fds-build/resources/components/toggle.md +119 -0
- package/skills/fds-build/resources/components/typography.md +86 -0
- package/skills/fds-build/resources/components/uneditable-section.md +84 -0
- package/skills/fds-build/resources/foundations/layout-composition-patterns.md +53 -0
- package/skills/fds-build/resources/foundations/tokens.md +547 -0
- package/skills/fds-build/resources/patterns/states.md +300 -0
- package/skills/fds-build/resources/setup/setup.md +202 -0
- package/skills/fds-build/resources/templates/form.md +427 -0
- package/skills/fds-build/resources/templates/listing.md +435 -0
- package/theme/styles/a11y-playground.css +52 -40
- package/theme/styles/bookingsg.css +52 -40
- package/theme/styles/careercompass.css +52 -40
- package/theme/styles/ccube.css +52 -40
- package/theme/styles/imda.css +52 -40
- package/theme/styles/lifesg.css +52 -40
- package/theme/styles/mylegacy.css +52 -40
- package/theme/styles/oneservice.css +52 -40
- package/theme/styles/pa.css +28 -22
- package/theme/styles/rbs.css +52 -40
- package/theme/styles/sgw-digital-lobby.css +52 -40
- package/theme/styles/smgs.css +52 -40
- package/theme/styles/spf.css +28 -27
- package/theme/styles/sportsg-orange.css +52 -40
- package/theme/styles/supportgowhere.css +52 -40
- package/theme/styles/tote-board.css +52 -40
- package/theme/styles/vica.css +52 -40
- package/theme/styles/websg.css +52 -40
- package/theme/styles/wise.css +52 -40
- package/theme/styles/wogaa.css +52 -40
- package/theme/tokens/colour.d.ts +6 -0
- package/theme/tokens/colour.js +1 -1
- package/theme/tokens/colour.js.map +1 -1
- package/theme/tokens/radius.d.ts +3 -0
- package/theme/tokens/radius.js +1 -1
- package/theme/tokens/radius.js.map +1 -1
- package/breadcrumb/breadcrumb.styles_1nl4aua.css +0 -18
- package/cjs/breadcrumb/breadcrumb.styles_1nl4aua.css +0 -18
- package/cjs/file-upload/file-item-edit.js +0 -2
- package/cjs/file-upload/file-item-edit.js.map +0 -1
- package/cjs/file-upload/file-item-edit.styles.js +0 -2
- package/cjs/file-upload/file-item-edit.styles.js.map +0 -1
- package/cjs/file-upload/file-item-edit.styles_19bvztn.css +0 -11
- package/cjs/file-upload/file-list/file-list.styles_1w7so98.css +0 -3
- package/cjs/file-upload/file-list-item/file-list-item.styles_kgtfke.css +0 -17
- package/cjs/local-nav/local-nav-menu/local-nav-menu.styles_pq19dh.css +0 -5
- package/cjs/menu/menu-content.styles_p7ijll.css +0 -2
- package/cjs/menu/menu-link.styles_hfxoez.css +0 -2
- package/cjs/menu/menu-section.styles_g7my4d.css +0 -3
- package/file-upload/file-item-edit.d.ts +0 -14
- package/file-upload/file-item-edit.js +0 -2
- package/file-upload/file-item-edit.js.map +0 -1
- package/file-upload/file-item-edit.styles.d.ts +0 -9
- package/file-upload/file-item-edit.styles.js +0 -2
- package/file-upload/file-item-edit.styles.js.map +0 -1
- package/file-upload/file-item-edit.styles_19bvztn.css +0 -11
- package/file-upload/file-list/file-list.styles_1w7so98.css +0 -3
- package/file-upload/file-list-item/file-list-item.styles_kgtfke.css +0 -17
- package/local-nav/local-nav-menu/local-nav-menu.styles_pq19dh.css +0 -5
- package/menu/menu-content.styles_p7ijll.css +0 -2
- package/menu/menu-link.styles_hfxoez.css +0 -2
- package/menu/menu-section.styles_g7my4d.css +0 -3
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Form.DateRangeInput
|
|
2
|
+
|
|
3
|
+
Import: `import { Form } from "@lifesg/react-design-system/form"`
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
- Collecting a start and end date from the user (e.g. leave period, booking period, date range filter).
|
|
8
|
+
- Use `variant="week"` when the selection must always be a full calendar week.
|
|
9
|
+
- Use `variant="fixed-range"` when the duration is predetermined (e.g. always 7 days).
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
`onChange` always receives both start and end as `YYYY-MM-DD` strings, even for week and fixed-range variants.
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { useState } from "react";
|
|
17
|
+
import { Form } from "@lifesg/react-design-system/form";
|
|
18
|
+
|
|
19
|
+
const LeavePeriodField = () => {
|
|
20
|
+
const [start, setStart] = useState("");
|
|
21
|
+
const [end, setEnd] = useState("");
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<Form.DateRangeInput
|
|
25
|
+
label="Leave period"
|
|
26
|
+
value={start}
|
|
27
|
+
valueEnd={end}
|
|
28
|
+
minDate="2024-01-01"
|
|
29
|
+
maxDate="2024-12-31"
|
|
30
|
+
onChange={(s, e) => {
|
|
31
|
+
setStart(s);
|
|
32
|
+
setEnd(e);
|
|
33
|
+
}}
|
|
34
|
+
/>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
{
|
|
41
|
+
/* variant="week" — whole-week selection */
|
|
42
|
+
}
|
|
43
|
+
<Form.DateRangeInput
|
|
44
|
+
label="Select week"
|
|
45
|
+
variant="week"
|
|
46
|
+
value={start}
|
|
47
|
+
valueEnd={end}
|
|
48
|
+
onChange={(s, e) => {
|
|
49
|
+
setStart(s);
|
|
50
|
+
setEnd(e);
|
|
51
|
+
}}
|
|
52
|
+
/>;
|
|
53
|
+
|
|
54
|
+
{
|
|
55
|
+
/* variant="fixed-range" — valueEnd derived automatically; only store start */
|
|
56
|
+
}
|
|
57
|
+
<Form.DateRangeInput
|
|
58
|
+
label="14-day quarantine period"
|
|
59
|
+
variant="fixed-range"
|
|
60
|
+
numberOfDays={14}
|
|
61
|
+
value={start}
|
|
62
|
+
valueEnd={end}
|
|
63
|
+
onChange={(s, e) => {
|
|
64
|
+
setStart(s);
|
|
65
|
+
setEnd(e);
|
|
66
|
+
}}
|
|
67
|
+
/>;
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Width: defaults to 4 columns wide at `lg` and above; full-width on mobile. Use `max-width` via a CSS Module class:
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
import styles from "./MyForm.module.css";
|
|
74
|
+
|
|
75
|
+
<Form.DateRangeInput className={styles.dateRangeInput} ... />
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```css
|
|
79
|
+
/* MyForm.module.css */
|
|
80
|
+
:where(body.fds-breakpoint-lg-min) .dateRangeInput {
|
|
81
|
+
max-width: 24rem;
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## YearMonthDisplay
|
|
86
|
+
|
|
87
|
+
| Prop | Type | Description |
|
|
88
|
+
| ------- | -------- | --------------------------------- |
|
|
89
|
+
| `year` | `number` | Currently displayed year (`YYYY`) |
|
|
90
|
+
| `month` | `number` | Currently displayed month (1–12) |
|
|
91
|
+
|
|
92
|
+
## Props
|
|
93
|
+
|
|
94
|
+
| Prop | Type | Default | Description |
|
|
95
|
+
| -------------------------- | ------------------------------------------------ | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
96
|
+
| `className` | `string` | — | — |
|
|
97
|
+
| `id` | `string` | — | — |
|
|
98
|
+
| `hideInputKeyboard` | `boolean` | — | When `true`, suppresses the native on-screen keyboard on mobile devices while still allowing the calendar picker to open. |
|
|
99
|
+
| `value` | `string` | — | The controlled value of the date input in `"YYYY-MM-DD"` or `"YYYY-M-D"` string format. |
|
|
100
|
+
| `valueEnd` | `string` | — | The controlled value of the end date input in `"YYYY-MM-DD"` or `"YYYY-M-D"` string format. |
|
|
101
|
+
| `error` | `boolean` | — | Renders the input in an error state. |
|
|
102
|
+
| `disabled` | `boolean` | — | — |
|
|
103
|
+
| `readOnly` | `boolean` | — | — |
|
|
104
|
+
| `variant` | `Exclude<Variant, "single" \| "multi">` | "range" | Selection model used by the calendar. - `"range"` — user picks a free-form start and end date independently. - `"week"` — selecting any day automatically expands the selection to the full calendar week. - `"fixed-range"` — selecting a start date automatically sets the end date to `numberOfDays - 1` days later. |
|
|
105
|
+
| `numberOfDays` | `number` | 7 | Number of days in the selection window when `variant` is `"fixed-range"`. The end date is calculated as the selected start date plus `numberOfDays - 1` days. |
|
|
106
|
+
| `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. |
|
|
107
|
+
| `zIndex` | `number` | — | The z-index of the calendar dropdown |
|
|
108
|
+
| `dropdownRootNode` | `RefObject<HTMLElement>` | document.body | The root element that contains the dropdown element. |
|
|
109
|
+
| `onChange` | `((startDate: string, endDate: string) => void)` | — | Called when the user completes a valid date range selection. Both dates are provided in `"YYYY-MM-DD"` format. An empty string is passed for either date when the selection is cleared. |
|
|
110
|
+
| `onFocus` | `(() => void)` | — | Called when the input receives focus. |
|
|
111
|
+
| `onBlur` | `(() => void)` | — | Called when focus leaves both the input and the calendar dropdown. |
|
|
112
|
+
| `onYearMonthDisplayChange` | `((value: YearMonthDisplay) => void)` | — | Called when the visible month or year in the calendar changes. |
|
|
113
|
+
| `minDate` | `string` | — | The minimum date that can be selected (inclusive) in 'YYYY-MM-DD' format. |
|
|
114
|
+
| `maxDate` | `string` | — | The maximum date that can be selected (inclusive) in 'YYYY-MM-DD' format. |
|
|
115
|
+
| `disabledDates` | `string[]` | — | Dates to disable in `YYYY-MM-DD` format. Example: `["2023-04-30"]` |
|
|
116
|
+
| `allowDisabledSelection` | `boolean` | — | Specifies if dates normally disabled by `minDate`, `maxDate` and `disabledDates` are still selectable |
|
|
117
|
+
| `showActiveMonthDaysOnly` | `boolean` | — | Specifies if the calendar should display only dates for the selected month |
|
|
118
|
+
| `label` | `FormLabelProps \| string` | — | Label rendered above the input. Accepts a plain string or a `FormLabelProps` object to provide additional configuration. |
|
|
119
|
+
| `errorMessage` | `string \| React.ReactNode` | — | Inline error text rendered below the input. |
|
|
120
|
+
| `layoutType` | `FormElementLayoutType` | — | Controls whether the container uses flex layout or a responsive column grid. |
|
|
121
|
+
| `xxlCols` | `ColSpan` | — | Column span at the `xxl` breakpoint. |
|
|
122
|
+
| `xlCols` | `ColSpan` | — | Column span at the `xl` breakpoint. |
|
|
123
|
+
| `lgCols` | `ColSpan` | — | Column span at the `lg` breakpoint. |
|
|
124
|
+
| `mdCols` | `ColSpan` | — | Column span at the `md` breakpoint. |
|
|
125
|
+
| `smCols` | `ColSpan` | — | Column span at the `sm` breakpoint. |
|
|
126
|
+
| `xsCols` | `ColSpan` | — | Column span at the `xs` breakpoint. |
|
|
127
|
+
| `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. |
|
|
128
|
+
|
|
129
|
+
## Rules
|
|
130
|
+
|
|
131
|
+
- Always supply a `label` so the field is accessible.
|
|
132
|
+
- Pass `value` (start) and `valueEnd` (end) for controlled usage; `onChange` receives both as `YYYY-MM-DD` strings.
|
|
133
|
+
- Use `minDate`/`maxDate` to restrict selection to valid business date ranges.
|
|
134
|
+
- Set `errorMessage` rather than `error` alone so users understand what is wrong.
|
|
135
|
+
- Use `withButton` (default `true`) to require explicit confirmation before the value is committed.
|
|
136
|
+
- Use `readOnly` on review screens where the range should be visible but not editable.
|
|
137
|
+
- Specify `dropdownRootNode` if the component is inside a scrollable container.
|
|
138
|
+
- `valueEnd` is only relevant for `variant="range"` and `variant="fixed-range"`.
|
|
139
|
+
- `numberOfDays` only applies when `variant="fixed-range"`.
|
|
140
|
+
|
|
141
|
+
## Anti-patterns
|
|
142
|
+
|
|
143
|
+
- Do not use for single-date selection — use `Form.DateInput` instead.
|
|
144
|
+
- Do not set `numberOfDays` unless `variant="fixed-range"` is also set.
|
|
145
|
+
- Do not rely on `error` alone without an `errorMessage`.
|
|
146
|
+
- Do not allow end date before start date — validate in `onChange` before updating state.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Form.ESignature
|
|
2
|
+
|
|
3
|
+
Import: `import { Form } from "@lifesg/react-design-system/form"`
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
- Forms that legally require a handwritten signature from the user (e.g. consent forms, declarations, agreements).
|
|
8
|
+
- Use `description` to guide the user on how to sign (e.g. "Sign within the box using your mouse or touchscreen").
|
|
9
|
+
|
|
10
|
+
## Form-wrapper props
|
|
11
|
+
|
|
12
|
+
| Prop | Type | Description |
|
|
13
|
+
| ------------------- | --------------------------- | --------------------------------------- |
|
|
14
|
+
| `label` | `string \| FormLabelProps` | Field label |
|
|
15
|
+
| `errorMessage` | `string \| React.ReactNode` | Error message displayed below the field |
|
|
16
|
+
| `data-error-testid` | `string` | Test identifier for the error message |
|
|
17
|
+
|
|
18
|
+
## Props
|
|
19
|
+
|
|
20
|
+
| Prop | Type | Default | Description |
|
|
21
|
+
| ----------------- | ----------------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
22
|
+
| `className` | `string` | — | — |
|
|
23
|
+
| `id` | `string` | — | — |
|
|
24
|
+
| `disabled` | `boolean` | — | — |
|
|
25
|
+
| `description` | `string` | — | Text rendered below the signature area. |
|
|
26
|
+
| `loadingLabel` | `string` | "Uploading..." | Label displayed alongside the upload progress bar. |
|
|
27
|
+
| `loadingProgress` | `number` | — | When set to a number, replaces the signature area with an upload progress indicator. Remove or set to `undefined` to restore the signature area. |
|
|
28
|
+
| `value` | `string` | — | The current signature as a base64 image dataURL. Pass `undefined` or omit to render the empty "Add signature" state. |
|
|
29
|
+
| `onChange` | `((value: string \| null) => void)` | — | Called when the user saves a signature. |
|
|
30
|
+
| `label` | `FormLabelProps \| string` | — | Label rendered above the input. Accepts a plain string or a `FormLabelProps` object to provide additional configuration. |
|
|
31
|
+
| `errorMessage` | `string \| React.ReactNode` | — | Inline error text rendered below the input. |
|
|
32
|
+
| `layoutType` | `FormElementLayoutType` | — | Controls whether the container uses flex layout or a responsive column grid. |
|
|
33
|
+
| `xxlCols` | `ColSpan` | — | Column span at the `xxl` breakpoint. |
|
|
34
|
+
| `xlCols` | `ColSpan` | — | Column span at the `xl` breakpoint. |
|
|
35
|
+
| `lgCols` | `ColSpan` | — | Column span at the `lg` breakpoint. |
|
|
36
|
+
| `mdCols` | `ColSpan` | — | Column span at the `md` breakpoint. |
|
|
37
|
+
| `smCols` | `ColSpan` | — | Column span at the `sm` breakpoint. |
|
|
38
|
+
| `xsCols` | `ColSpan` | — | Column span at the `xs` breakpoint. |
|
|
39
|
+
| `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. |
|
|
40
|
+
|
|
41
|
+
## Rules
|
|
42
|
+
|
|
43
|
+
- Always supply a `label` so the field is accessible.
|
|
44
|
+
- Pass `value` and `onChange` together for controlled usage; the value is a base64-encoded PNG string.
|
|
45
|
+
- Use `loadingProgress` and `loadingLabel` to indicate server upload progress after the signature is submitted.
|
|
46
|
+
- Set `errorMessage` when signature validation fails (e.g. canvas is empty on submit).
|
|
47
|
+
- Use `disabled` when the signature field should be locked (e.g. after submission or in a locked step).
|
|
48
|
+
- `value` and the `onChange` return value are both base64-encoded PNG strings.
|
|
49
|
+
- `loadingProgress` and `loadingLabel` are used to show upload progress after the signature is captured.
|
|
50
|
+
|
|
51
|
+
## Anti-patterns
|
|
52
|
+
|
|
53
|
+
- Do not use for typed signatures or checkbox confirmations — this component is for handwritten drawing input only.
|
|
54
|
+
- Do not store the raw base64 string in a URL parameter or cookie — it is large; use server-side storage.
|
|
55
|
+
- Do not omit `description` — users need guidance on where and how to draw their signature.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Form.Input
|
|
2
|
+
|
|
3
|
+
Import: `import { Form } from "@lifesg/react-design-system/form"`
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
Use for any single-line text entry inside a form. Always prefer `Form.Input` over the bare `Input` component when a label or error message is required.
|
|
8
|
+
|
|
9
|
+
## Variants
|
|
10
|
+
|
|
11
|
+
| `styleType` | When to use |
|
|
12
|
+
| ---------------------- | ------------------------------------------------------------------------------------------- |
|
|
13
|
+
| `"bordered"` (default) | Standard form fields — provides clear affordance |
|
|
14
|
+
| `"no-border"` | Inline editing or contexts where a surrounding container already provides visual separation |
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
import { Form } from "@lifesg/react-design-system/form";
|
|
20
|
+
|
|
21
|
+
<Form.Input
|
|
22
|
+
label="Full name"
|
|
23
|
+
placeholder="Enter your name"
|
|
24
|
+
value={value}
|
|
25
|
+
onChange={(e) => {}}
|
|
26
|
+
/>;
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Props
|
|
30
|
+
|
|
31
|
+
| Prop | Type | Default | Description |
|
|
32
|
+
| -------------- | --------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
33
|
+
| `spacing` | `number` | — | Inserts a space after every `spacing` characters in the displayed value. Only applies when `type` is `"tel"`. |
|
|
34
|
+
| `allowClear` | `boolean` | false | Shows a clear button when the field has a value and is neither disabled nor read-only. |
|
|
35
|
+
| `onClear` | `() => void` | — | Called when the user activates the clear button. Use this to clear the controlled value. |
|
|
36
|
+
| `styleType` | `InputStyleType` | "bordered" | Visual style variant. |
|
|
37
|
+
| `label` | `FormLabelProps \| string` | — | Label rendered above the input. Accepts a plain string or a `FormLabelProps` object to provide additional configuration. |
|
|
38
|
+
| `errorMessage` | `string \| React.ReactNode` | — | Inline error text rendered below the input. |
|
|
39
|
+
| `layoutType` | `FormElementLayoutType` | — | Controls whether the container uses flex layout or a responsive column grid. |
|
|
40
|
+
| `xxlCols` | `ColSpan` | — | Column span at the `xxl` breakpoint. |
|
|
41
|
+
| `xlCols` | `ColSpan` | — | Column span at the `xl` breakpoint. |
|
|
42
|
+
| `lgCols` | `ColSpan` | — | Column span at the `lg` breakpoint. |
|
|
43
|
+
| `mdCols` | `ColSpan` | — | Column span at the `md` breakpoint. |
|
|
44
|
+
| `smCols` | `ColSpan` | — | Column span at the `sm` breakpoint. |
|
|
45
|
+
| `xsCols` | `ColSpan` | — | Column span at the `xs` breakpoint. |
|
|
46
|
+
| `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. |
|
|
47
|
+
|
|
48
|
+
## Rules
|
|
49
|
+
|
|
50
|
+
- Set `type="email"` for email fields and `type="tel"` for phone fields — triggers the correct mobile keyboard.
|
|
51
|
+
- Use `readOnly` on review or summary screens — not `disabled`. Disabled implies interaction is unavailable; read-only implies the field is intentionally non-editable in context.
|
|
52
|
+
- Use `allowClear` for optional fields and search-like inputs; pair with `onClear` to reset controlled value.
|
|
53
|
+
- Use `spacing` for formatted `tel` inputs (e.g. `spacing={4}` for `9123 4567`) — do not implement custom character splitting.
|
|
54
|
+
- Provide `errorMessage` rather than `error` alone — the string drives both the visual state and the accessible error text.
|
|
55
|
+
- Use `label` as a plain string for simple labels; use `FormLabelProps` (with `subtitle` or `addon`) only when additional context is needed.
|
|
56
|
+
- `error` is redundant when `errorMessage` is provided — `errorMessage` implies the error state automatically.
|
|
57
|
+
- `spacing` only has effect when `type="tel"`.
|
|
58
|
+
- `styleType="no-border"` removes the visible border — use only in contexts with a background that provides visual separation.
|
|
59
|
+
|
|
60
|
+
## Anti-patterns
|
|
61
|
+
|
|
62
|
+
- Do not use `disabled` on review screens — use `readOnly`.
|
|
63
|
+
- Do not set both `error` and `errorMessage` — `errorMessage` is sufficient.
|
|
64
|
+
- Do not use `spacing` with input types other than `"tel"`.
|
|
65
|
+
- Do not use `styleType="no-border"` in a form context without a containing element that visually distinguishes the field.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Form.MaskedInput
|
|
2
|
+
|
|
3
|
+
Import: `import { Form } from "@lifesg/react-design-system/form"`
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
- Displaying sensitive data that should be partially hidden by default with a user-controlled reveal toggle (e.g. NRIC, FIN, passport number, bank account number).
|
|
8
|
+
- Read-only fields that are loaded asynchronously and should show a loading / error state.
|
|
9
|
+
|
|
10
|
+
## Form-wrapper props
|
|
11
|
+
|
|
12
|
+
| Prop | Type | Description |
|
|
13
|
+
| ------------------- | --------------------------- | --------------------------------------- |
|
|
14
|
+
| `label` | `string \| FormLabelProps` | Field label |
|
|
15
|
+
| `errorMessage` | `string \| React.ReactNode` | Error message displayed below the field |
|
|
16
|
+
| `data-error-testid` | `string` | Test identifier for the error message |
|
|
17
|
+
|
|
18
|
+
## Props
|
|
19
|
+
|
|
20
|
+
| Prop | Type | Default | Description |
|
|
21
|
+
| ------------------- | ----------------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
22
|
+
| `value` | `string` | — | — |
|
|
23
|
+
| `iconUnmask` | `JSX.Element` | <EyeIcon /> | Icon rendered inside the toggle button while the value is masked |
|
|
24
|
+
| `iconMask` | `JSX.Element` | <EyeSlashIcon /> | Icon rendered inside the toggle button while the value is unmasked |
|
|
25
|
+
| `iconActiveColor` | `string` | — | CSS color value applied to the mask-toggle icon when it is active |
|
|
26
|
+
| `iconInactiveColor` | `string` | — | CSS color value applied to the mask-toggle icon in its default inactive state. |
|
|
27
|
+
| `disableMask` | `boolean` | false | When `true`, the value is permanently unmasked. |
|
|
28
|
+
| `transformInput` | `"uppercase" \| "lowercase"` | — | Automatically transforms the typed value before it is stored and passed to `onChange`. |
|
|
29
|
+
| `loadState` | `MaskedInputLoadState` | — | Retrieval state for the value. Only applies when `readOnly` is `true`. |
|
|
30
|
+
| `onMask` | `(() => void)` | — | Called when the value transitions from unmasked to masked. |
|
|
31
|
+
| `onUnmask` | `(() => void)` | — | Called when the value transitions from masked to unmasked. |
|
|
32
|
+
| `onTryAgain` | `(() => void)` | — | Called when the user clicks the "Try again?" button shown in the `loadState="fail"` state. Only relevant when `readOnly` is `true`. |
|
|
33
|
+
| `spacing` | `number` | — | Inserts a space after every `spacing` characters in the displayed value. Only applies when `type` is `"tel"`. |
|
|
34
|
+
| `allowClear` | `boolean` | false | Shows a clear button when the field has a value and is neither disabled nor read-only. |
|
|
35
|
+
| `onClear` | `() => void` | — | Called when the user activates the clear button. Use this to clear the controlled value. |
|
|
36
|
+
| `styleType` | `InputStyleType` | "bordered" | Visual style variant. |
|
|
37
|
+
| `maskRange` | `number[]` | — | Index range `[start, end]` of the characters to mask (inclusive, zero-based). Characters outside this range remain visible. |
|
|
38
|
+
| `unmaskRange` | `number[]` | — | Index range `[start, end]` of the characters to keep visible (inclusive, zero-based). Characters outside this range are masked. |
|
|
39
|
+
| `maskRegex` | `RegExp` | — | Regular expression whose matches are replaced with `maskChar`. |
|
|
40
|
+
| `maskTransformer` | `((value: string) => string)` | — | Custom function that receives the raw value and returns the fully masked string. Takes precedence over `maskRegex`, `maskRange`, and `unmaskRange`. |
|
|
41
|
+
| `maskChar` | `string` | "•" | Character used to replace each masked character in the displayed value. |
|
|
42
|
+
| `label` | `FormLabelProps \| string` | — | Label rendered above the input. Accepts a plain string or a `FormLabelProps` object to provide additional configuration. |
|
|
43
|
+
| `errorMessage` | `string \| React.ReactNode` | — | Inline error text rendered below the input. |
|
|
44
|
+
| `layoutType` | `FormElementLayoutType` | — | Controls whether the container uses flex layout or a responsive column grid. |
|
|
45
|
+
| `xxlCols` | `ColSpan` | — | Column span at the `xxl` breakpoint. |
|
|
46
|
+
| `xlCols` | `ColSpan` | — | Column span at the `xl` breakpoint. |
|
|
47
|
+
| `lgCols` | `ColSpan` | — | Column span at the `lg` breakpoint. |
|
|
48
|
+
| `mdCols` | `ColSpan` | — | Column span at the `md` breakpoint. |
|
|
49
|
+
| `smCols` | `ColSpan` | — | Column span at the `sm` breakpoint. |
|
|
50
|
+
| `xsCols` | `ColSpan` | — | Column span at the `xs` breakpoint. |
|
|
51
|
+
| `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. |
|
|
52
|
+
|
|
53
|
+
## Rules
|
|
54
|
+
|
|
55
|
+
- Always supply a `label` so the field is accessible.
|
|
56
|
+
- Choose one masking strategy per instance: `maskRange`, `unmaskRange`, `maskRegex`, or `maskTransformer`.
|
|
57
|
+
- Use `maskRange` for fixed-position sensitive segments (e.g. `[1, 4]` for NRIC middle characters).
|
|
58
|
+
- Use `maskRegex` when the characters to mask follow a pattern rather than a fixed range.
|
|
59
|
+
- Use `transformInput="uppercase"` for identifiers that are conventionally uppercase (e.g. NRIC, passport).
|
|
60
|
+
- Use `loadState` only when `readOnly` is also `true` — for fields that are fetched asynchronously.
|
|
61
|
+
- Provide `onTryAgain` when `loadState="fail"` so users can retry the fetch.
|
|
62
|
+
- `loadState` only takes effect when `readOnly` is also `true`.
|
|
63
|
+
- Provide only one of `maskRange`, `unmaskRange`, `maskRegex`, or `maskTransformer` per instance.
|
|
64
|
+
|
|
65
|
+
## Anti-patterns
|
|
66
|
+
|
|
67
|
+
- Do not use for non-sensitive fields — masking adds friction and should be reserved for data that genuinely requires it.
|
|
68
|
+
- Do not set `loadState` without `readOnly` — it has no effect in editable mode.
|
|
69
|
+
- Do not combine multiple masking strategies (`maskRange` + `maskRegex`) on the same instance.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Form.MultiSelect
|
|
2
|
+
|
|
3
|
+
Import: `import { Form } from "@lifesg/react-design-system/form"`
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
Use when users can select more than one option from a predefined list. For single selections use `Form.Select`. For hierarchical options use `Form.NestedMultiSelect`.
|
|
8
|
+
|
|
9
|
+
## Variants
|
|
10
|
+
|
|
11
|
+
| `variant` | When to use |
|
|
12
|
+
| ----------- | -------------------------------------------------------- |
|
|
13
|
+
| `"default"` | Standard form context |
|
|
14
|
+
| `"small"` | Compact layouts — inline filters, dense data-entry forms |
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
import { Form } from "@lifesg/react-design-system/form";
|
|
20
|
+
|
|
21
|
+
const options = [
|
|
22
|
+
{ value: "doc", label: "Document" },
|
|
23
|
+
{ value: "img", label: "Image" },
|
|
24
|
+
{ value: "pdf", label: "PDF" },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
<Form.MultiSelect
|
|
28
|
+
label="File types"
|
|
29
|
+
options={options}
|
|
30
|
+
selectedOptions={selected}
|
|
31
|
+
valueExtractor={(opt) => opt.value}
|
|
32
|
+
listExtractor={(opt) => opt.label}
|
|
33
|
+
displayValueExtractor={(opt) => opt.label}
|
|
34
|
+
onSelectOptions={(opts) => {}}
|
|
35
|
+
/>;
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Props
|
|
39
|
+
|
|
40
|
+
| Prop | Type | Default | Description |
|
|
41
|
+
| -------------------------- | ------------------------------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
42
|
+
| `valueExtractor` | `((item: T) => V)` | — | Extracts the comparable value from an option item. |
|
|
43
|
+
| `listExtractor` | `((item: T) => string \| ListItemDisplayProps)` | — | Extracts the display text for a list item. |
|
|
44
|
+
| `renderListItem` | `\| ((item: T, args: ListItemRenderArgs) => JSX.Element)` | — | Renders a fully custom element for each list item. |
|
|
45
|
+
| `renderCustomCallToAction` | `\| ((hideOptions: () => void, options: T[]) => JSX.Element)` | — | Renders a custom call-to-action at the bottom of the dropdown list. |
|
|
46
|
+
| `readOnly` | `boolean` | — | — |
|
|
47
|
+
| `selectedOptions` | `T[]` | — | The currently selected options. |
|
|
48
|
+
| `onSelectOptions` | `((options: T[]) => void)` | — | Called when the selection changes. Receives the updated array of selected options after each user interaction. |
|
|
49
|
+
| `onBlur` | `(() => void)` | — | Called when focus leaves both the trigger and dropdown. |
|
|
50
|
+
| `variant` | `DropdownVariantType` | "default" | Controls the height of the trigger and dropdown items. |
|
|
51
|
+
| `alignment` | `DropdownAlignmentType` | — | Aligns the dropdown relative to the trigger element. |
|
|
52
|
+
| `dropdownZIndex` | `number` | — | CSS `z-index` applied to the dropdown overlay. |
|
|
53
|
+
| `maxSelectable` | `number` | — | Caps the number of options that can be simultaneously selected. |
|
|
54
|
+
| `customLabels` | `DropdownCustomLabelProps` | — | Overrides the default labels shown inside the dropdown and on the trigger. |
|
|
55
|
+
| `dropdownWidth` | `string` | — | Custom width for the dropdown. When specified, the dropdown uses this width instead of matching the trigger element width. |
|
|
56
|
+
| `dropdownRootNode` | `RefObject<HTMLElement>` | document.body | The root element that contains the dropdown element. |
|
|
57
|
+
| `options` \* | `T[]` | — | The list of selectable options rendered in the dropdown. |
|
|
58
|
+
| `optionsLoadState` | `ItemsLoadStateType` | "success" | Indicates the load state of options fetched asynchronously. Renders a loading spinner, error retry, or the list depending on the state. |
|
|
59
|
+
| `optionTruncationType` | `TruncateType` | "end" | Controls how long option labels are truncated when they overflow. Specify `"none"` to display option labels in full. |
|
|
60
|
+
| `onShowOptions` | `(() => void)` | — | Called when the dropdown list becomes visible. |
|
|
61
|
+
| `onHideOptions` | `(() => void)` | — | Called when the dropdown list is hidden. |
|
|
62
|
+
| `onRetry` | `(() => void)` | — | Called when the user activates the retry action after a load failure. |
|
|
63
|
+
| `name` | `string` | — | Name attribute forwarded to the underlying button element. |
|
|
64
|
+
| `disabled` | `boolean` | — | — |
|
|
65
|
+
| `enableSearch` | `boolean` | — | Renders a search bar inside the dropdown when `true`. |
|
|
66
|
+
| `hideNoResultsDisplay` | `boolean` | — | Suppresses the built-in "no results" message when `true`. |
|
|
67
|
+
| `noResultsDescription` | `React.ReactNode` | — | — |
|
|
68
|
+
| `searchPlaceholder` | `string` | — | — |
|
|
69
|
+
| `searchFunction` | `((searchValue: string) => T[])` | — | Custom filter function invoked on each keystroke in the search input. |
|
|
70
|
+
| `onSearch` | `(() => void)` | — | Called when the user types in the search input. |
|
|
71
|
+
| `label` | `FormLabelProps \| string` | — | Label rendered above the input. Accepts a plain string or a `FormLabelProps` object to provide additional configuration. |
|
|
72
|
+
| `errorMessage` | `string \| React.ReactNode` | — | Inline error text rendered below the input. |
|
|
73
|
+
| `layoutType` | `FormElementLayoutType` | — | Controls whether the container uses flex layout or a responsive column grid. |
|
|
74
|
+
| `xxlCols` | `ColSpan` | — | Column span at the `xxl` breakpoint. |
|
|
75
|
+
| `xlCols` | `ColSpan` | — | Column span at the `xl` breakpoint. |
|
|
76
|
+
| `lgCols` | `ColSpan` | — | Column span at the `lg` breakpoint. |
|
|
77
|
+
| `mdCols` | `ColSpan` | — | Column span at the `md` breakpoint. |
|
|
78
|
+
| `smCols` | `ColSpan` | — | Column span at the `sm` breakpoint. |
|
|
79
|
+
| `xsCols` | `ColSpan` | — | Column span at the `xs` breakpoint. |
|
|
80
|
+
| `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. |
|
|
81
|
+
|
|
82
|
+
## Rules
|
|
83
|
+
|
|
84
|
+
- Always provide `options` — the component has no meaning without it.
|
|
85
|
+
- Provide `valueExtractor` and `listExtractor` for typed option arrays — treat as required.
|
|
86
|
+
- Always provide `displayValueExtractor` alongside `valueExtractor` when option objects have separate label/value fields. Without it, the component falls back to `valueExtractor`, showing raw values instead of labels in the closed selector.
|
|
87
|
+
- Use `maxSelectable` when there is a business rule limiting how many options can be chosen; the component enforces it automatically.
|
|
88
|
+
- Use `enableSearch` when the list exceeds ~10 items.
|
|
89
|
+
- Provide `customLabels` to localise or contextualise "Select all", "Clear all", and count labels.
|
|
90
|
+
- Use `optionsLoadState` to communicate async loading states.
|
|
91
|
+
- Only override `dropdownZIndex` or `dropdownRootNode` when there are confirmed z-index or scroll conflicts.
|
|
92
|
+
- `onSelectOptions` receives the complete updated selection array — not a delta.
|
|
93
|
+
- Unlike `Form.Select`, there is no `readOnly` prop — use `disabled` for non-editable states.
|
|
94
|
+
|
|
95
|
+
## Anti-patterns
|
|
96
|
+
|
|
97
|
+
- Do not use `Form.MultiSelect` when only one option can be selected — use `Form.Select`.
|
|
98
|
+
- Do not use the deprecated `searchPlaceholder` or `noResultsDescription` props directly — use `customLabels`.
|
|
99
|
+
- Do not set both `error` and `errorMessage`.
|
|
100
|
+
- Do not manage selection capping manually in `onSelectOptions` — use `maxSelectable` instead.
|