@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,51 @@
|
|
|
1
|
+
# Input
|
|
2
|
+
|
|
3
|
+
Import: `import { Input } from "@lifesg/react-design-system/input"`
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
Use `Input` for standalone inputs that do not need a label or error message — search bars, inline editing, filter fields, or composition contexts where the form layout is provided externally. For all labelled form fields with validation messages, use `Form.Input` instead.
|
|
8
|
+
|
|
9
|
+
## Variants
|
|
10
|
+
|
|
11
|
+
| `styleType` | When to use |
|
|
12
|
+
| ---------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
13
|
+
| `"bordered"` (default) | Standard standalone input — provides clear affordance |
|
|
14
|
+
| `"no-border"` | Inline editing or search bars embedded in headers/toolbars where a border would conflict with the surrounding UI |
|
|
15
|
+
|
|
16
|
+
## Props — `InputProps`
|
|
17
|
+
|
|
18
|
+
| Prop | Type | Default | Description |
|
|
19
|
+
| ------------ | ---------------- | ---------- | ------------------------------------------------------------------------------------------------------------- |
|
|
20
|
+
| `spacing` | `number` | — | Inserts a space after every `spacing` characters in the displayed value. Only applies when `type` is `"tel"`. |
|
|
21
|
+
| `error` | `boolean` | — | Marks the input wrapper in an error state. |
|
|
22
|
+
| `allowClear` | `boolean` | false | Shows a clear button when the field has a value and is neither disabled nor read-only. |
|
|
23
|
+
| `onClear` | `() => void` | — | Called when the user activates the clear button. Use this to clear the controlled value. |
|
|
24
|
+
| `styleType` | `InputStyleType` | "bordered" | Visual style variant. |
|
|
25
|
+
|
|
26
|
+
## Props — `InputPartialProps`
|
|
27
|
+
|
|
28
|
+
| Prop | Type | Default | Description |
|
|
29
|
+
| ------------ | ---------------- | ---------- | ------------------------------------------------------------------------------------------------------------- |
|
|
30
|
+
| `spacing` | `number` | — | Inserts a space after every `spacing` characters in the displayed value. Only applies when `type` is `"tel"`. |
|
|
31
|
+
| `allowClear` | `boolean` | false | Shows a clear button when the field has a value and is neither disabled nor read-only. |
|
|
32
|
+
| `onClear` | `() => void` | — | Called when the user activates the clear button. Use this to clear the controlled value. |
|
|
33
|
+
| `styleType` | `InputStyleType` | "bordered" | Visual style variant. |
|
|
34
|
+
|
|
35
|
+
## Rules
|
|
36
|
+
|
|
37
|
+
- Use `type="email"` for email fields and `type="tel"` for phone fields — triggers the correct mobile keyboard.
|
|
38
|
+
- Use `spacing` for formatted `tel` inputs (e.g. `spacing={4}` for `9123 4567`) — do not implement custom character splitting.
|
|
39
|
+
- Use `allowClear` for search inputs and optional fields; pair with `onClear` to reset the controlled value.
|
|
40
|
+
- Set `error={true}` to communicate an invalid state visually — provide a separate error message element in the surrounding layout.
|
|
41
|
+
- Use `styleType="no-border"` only when the surrounding container or background provides sufficient visual separation.
|
|
42
|
+
- `Input` is the bare input field with no label or error message wrapper. Use `Form.Input` when a label or validation message is needed.
|
|
43
|
+
- `spacing` only has effect when `type="tel"`.
|
|
44
|
+
- `styleType="no-border"` removes the visible border — use only when the surrounding context provides sufficient visual distinction.
|
|
45
|
+
- `error` drives error-state styling only; there is no built-in error message — pair with external error text if needed.
|
|
46
|
+
|
|
47
|
+
## Anti-patterns
|
|
48
|
+
|
|
49
|
+
- Do not use `Input` inside a form that needs labels or validation messages — use `Form.Input`.
|
|
50
|
+
- Do not use `spacing` with input types other than `"tel"`.
|
|
51
|
+
- Do not rely on `Input` alone to surface error messages — it has no built-in error text slot.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# LanguageSwitcher
|
|
2
|
+
|
|
3
|
+
Import: `import { LanguageSwitcher } from "@lifesg/react-design-system/language-switcher"`
|
|
4
|
+
|
|
5
|
+
## Props — `LanguageSwitcherProps`
|
|
6
|
+
|
|
7
|
+
| Prop | Type | Default | Description |
|
|
8
|
+
| ------------------ | -------------------------------------------- | ---------- | -------------------------------------------------------------------------- |
|
|
9
|
+
| `variant` | `LanguageSwitcherVariant` | "dropdown" | Presentation mode. |
|
|
10
|
+
| `selectedLanguage` | `LanguageSwitcherCode` | "en" | The currently active language code. |
|
|
11
|
+
| `onSelectLanguage` | `((language: LanguageSwitcherCode) => void)` | — | Called when the user selects a language different from `selectedLanguage`. |
|
|
12
|
+
|
|
13
|
+
## Rules
|
|
14
|
+
|
|
15
|
+
- `LanguageSwitcherCode` is the union of supported language codes: `"en" | "zh" | "ms" | "ta"`.
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Layout
|
|
2
|
+
|
|
3
|
+
Import: `import { Layout } from "@lifesg/react-design-system/layout"`
|
|
4
|
+
|
|
5
|
+
## Composition
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Layout.Container type="grid" ← responsive 12-col grid (desktop) / 8-col (mobile)
|
|
9
|
+
├── Layout.ColDiv xsCols lgCols ← column span — always set xsCols
|
|
10
|
+
├── Layout.ColDiv xsCols lgCols
|
|
11
|
+
└── Layout.ColDiv xsCols lgCols
|
|
12
|
+
|
|
13
|
+
Layout.Container type="flex" ← horizontal flexbox row
|
|
14
|
+
└── {children}
|
|
15
|
+
|
|
16
|
+
Layout.Container type="flex-column" ← vertical flexbox column
|
|
17
|
+
└── {children}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`Layout.Section` and `Layout.Content` share the same props as `Layout.Container` and can be used in place of it for semantic HTML structure. Never nest `Layout.Container` inside another `Layout.Container`. Always set `xsCols` on every `Layout.ColDiv` — never set only `lgCols`.
|
|
21
|
+
|
|
22
|
+
- `Layout.ColDiv` — responsive grid column div
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import { Layout } from "@lifesg/react-design-system/layout";
|
|
28
|
+
|
|
29
|
+
<Layout.Container type="grid">
|
|
30
|
+
<Layout.ColDiv xsCols={8} lgCols={6}>
|
|
31
|
+
Content
|
|
32
|
+
</Layout.ColDiv>
|
|
33
|
+
</Layout.Container>;
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
| `type` | Use case |
|
|
37
|
+
| --------------- | -------------------------------- |
|
|
38
|
+
| `"grid"` | Responsive column grid (default) |
|
|
39
|
+
| `"flex"` | Horizontal flexbox layout |
|
|
40
|
+
| `"flex-column"` | Vertical flexbox layout |
|
|
41
|
+
|
|
42
|
+
## Breakpoints
|
|
43
|
+
|
|
44
|
+
| Breakpoint | Min width | Columns | Gutter | Device |
|
|
45
|
+
| ---------- | --------- | ------- | ------ | --------------------- |
|
|
46
|
+
| `xxs` | 0px | 8 | 16px | Smallest mobile |
|
|
47
|
+
| `xs` | 321px | 8 | 16px | Small mobile |
|
|
48
|
+
| `sm` | 376px | 8 | 16px | Mobile |
|
|
49
|
+
| `md` | 481px | 8 | 16px | Large mobile / tablet |
|
|
50
|
+
| `lg` | 769px | 12 | 32px | Desktop |
|
|
51
|
+
| `xl` | 1201px | 12 | 32px | Large desktop |
|
|
52
|
+
| `xxl` | 1441px | 12 | 32px | Extra-large |
|
|
53
|
+
|
|
54
|
+
### Common column widths
|
|
55
|
+
|
|
56
|
+
| Use case | `xsCols` | `lgCols` | `xlCols` |
|
|
57
|
+
| ------------------------------------ | -------- | -------- | -------- |
|
|
58
|
+
| Full width | 8 | 12 | — |
|
|
59
|
+
| Full width (form progress indicator) | 12 | 12 | — |
|
|
60
|
+
| Narrow content (forms) | 12 | 9 | 8 |
|
|
61
|
+
| Half-width (2-up cards) | 8 | 6 | — |
|
|
62
|
+
| Third-width (3-up cards) | 8 | 4 | — |
|
|
63
|
+
| Quarter-width (4-up metrics) | 8 | 6 | 3 |
|
|
64
|
+
| Sidebar | 8 | 3 | — |
|
|
65
|
+
| Main + sidebar main | 8 | 9 | — |
|
|
66
|
+
|
|
67
|
+
## Responsive styles
|
|
68
|
+
|
|
69
|
+
Use CSS Module classes for responsive styles in v4. The `MediaQuery` breakpoint body classes replace `@media` queries in CSS Modules.
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
import styles from "./MyComponent.module.css";
|
|
73
|
+
|
|
74
|
+
<div className={styles.responsiveDiv}>...</div>;
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```css
|
|
78
|
+
/* MyComponent.module.css */
|
|
79
|
+
.responsiveDiv {
|
|
80
|
+
padding: var(--fds-spacing-16);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
:global(body.fds-breakpoint-lg-min) .responsiveDiv {
|
|
84
|
+
padding: var(--fds-spacing-32);
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
| CSS Module selector | Query type | When it applies |
|
|
89
|
+
| ------------------------------------------ | ------------------- | -------------------------------- |
|
|
90
|
+
| `:global(body.fds-breakpoint-md-min) .cls` | `min-width: 481px` | md and up (tablet+) |
|
|
91
|
+
| `:global(body.fds-breakpoint-lg-min) .cls` | `min-width: 769px` | lg and up (desktop+) |
|
|
92
|
+
| `:global(body.fds-breakpoint-md-max) .cls` | `max-width: 768px` | md and below (mobile only) |
|
|
93
|
+
| `:global(body.fds-breakpoint-lg-max) .cls` | `max-width: 1200px` | lg and below (non-large-desktop) |
|
|
94
|
+
|
|
95
|
+
Design mobile-first — define base styles for mobile, then override with `MinWidth` for larger screens.
|
|
96
|
+
|
|
97
|
+
## Props — `ContainerProps`
|
|
98
|
+
|
|
99
|
+
| Prop | Type | Default | Description |
|
|
100
|
+
| ------------- | ----------------- | ------- | --------------------------------------------------------- |
|
|
101
|
+
| `type` | `ContainerType` | "flex" | Display mode applied to the container. |
|
|
102
|
+
| `children` \* | `React.ReactNode` | — | — |
|
|
103
|
+
| `stretch` | `boolean` | false | Stretches the element to fill its container's full width. |
|
|
104
|
+
|
|
105
|
+
## Props — `SectionProps`
|
|
106
|
+
|
|
107
|
+
| Prop | Type | Default | Description |
|
|
108
|
+
| ------------- | ----------------- | ------- | --------------------------------------------------------- |
|
|
109
|
+
| `children` \* | `React.ReactNode` | — | — |
|
|
110
|
+
| `stretch` | `boolean` | false | Stretches the element to fill its container's full width. |
|
|
111
|
+
|
|
112
|
+
## Props — `ContentProps`
|
|
113
|
+
|
|
114
|
+
| Prop | Type | Default | Description |
|
|
115
|
+
| ------------- | ----------------- | ------- | --------------------------------------------------------- |
|
|
116
|
+
| `type` | `ContainerType` | "flex" | Display mode applied to the container. |
|
|
117
|
+
| `children` \* | `React.ReactNode` | — | — |
|
|
118
|
+
| `stretch` | `boolean` | false | Stretches the element to fill its container's full width. |
|
|
119
|
+
|
|
120
|
+
## Props — `ColProps`
|
|
121
|
+
|
|
122
|
+
| Prop | Type | Default | Description |
|
|
123
|
+
| --------- | --------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
124
|
+
| `xxlCols` | `ColSpan` | — | Column span at the `xxl` breakpoint. |
|
|
125
|
+
| `xlCols` | `ColSpan` | — | Column span at the `xl` breakpoint. |
|
|
126
|
+
| `lgCols` | `ColSpan` | — | Column span at the `lg` breakpoint. |
|
|
127
|
+
| `mdCols` | `ColSpan` | — | Column span at the `md` breakpoint. |
|
|
128
|
+
| `smCols` | `ColSpan` | — | Column span at the `sm` breakpoint. |
|
|
129
|
+
| `xsCols` | `ColSpan` | — | Column span at the `xs` breakpoint. |
|
|
130
|
+
| `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. |
|
|
131
|
+
|
|
132
|
+
## Props — `ColDivProps`
|
|
133
|
+
|
|
134
|
+
| Prop | Type | Default | Description |
|
|
135
|
+
| --------- | --------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
136
|
+
| `xxlCols` | `ColSpan` | — | Column span at the `xxl` breakpoint. |
|
|
137
|
+
| `xlCols` | `ColSpan` | — | Column span at the `xl` breakpoint. |
|
|
138
|
+
| `lgCols` | `ColSpan` | — | Column span at the `lg` breakpoint. |
|
|
139
|
+
| `mdCols` | `ColSpan` | — | Column span at the `md` breakpoint. |
|
|
140
|
+
| `smCols` | `ColSpan` | — | Column span at the `sm` breakpoint. |
|
|
141
|
+
| `xsCols` | `ColSpan` | — | Column span at the `xs` breakpoint. |
|
|
142
|
+
| `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. |
|
|
143
|
+
|
|
144
|
+
## Rules
|
|
145
|
+
|
|
146
|
+
- Use CSS Modules for component-level styles — do not use plain inline styles for layout concerns.
|
|
147
|
+
- Use `:where(body.fds-breakpoint-{bp}-min)` selectors for responsive styles — never hardcoded pixel breakpoints.
|
|
148
|
+
- Use CSS custom properties for spacing (e.g. `var(--fds-spacing-16)`) — never hardcode pixel values.
|
|
149
|
+
|
|
150
|
+
Grid rules:
|
|
151
|
+
|
|
152
|
+
- Always use `type="grid"` on Container for responsive column layouts.
|
|
153
|
+
- Always specify `xsCols` on ColDiv — mobile-first; never only `lgCols`.
|
|
154
|
+
- Max `xsCols` is 8 (mobile/tablet), max `lgCols` is 12 (desktop).
|
|
155
|
+
- Never nest `Layout.Container` inside another `Layout.Container`.
|
|
156
|
+
- A column span applies to all larger viewports until overridden.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# LinkList
|
|
2
|
+
|
|
3
|
+
Import: `import { LinkList } from "@lifesg/react-design-system/link-list"`
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
Use to present a set of related links — related articles, resources, or quick-access shortcuts. Use `style="small"` for compact contexts such as sidebars or footnotes.
|
|
8
|
+
|
|
9
|
+
## Sub-components
|
|
10
|
+
|
|
11
|
+
### LinkListItemProps
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Default | Description |
|
|
14
|
+
| ---------------------- | ------------------------------------------------------ | ------- | -------------------------------------------------- |
|
|
15
|
+
| `title` | `string` | — | Link label — required |
|
|
16
|
+
| `description` | `string` | — | First-level supporting text below the title |
|
|
17
|
+
| `secondaryDescription` | `string` | — | Second-level supporting text below the description |
|
|
18
|
+
| `options` | `T` | — | Custom properties to pass to the item |
|
|
19
|
+
| `onClick` | `(event: React.MouseEvent<HTMLAnchorElement>) => void` | — | Per-item click handler — overrides `onItemClick` |
|
|
20
|
+
| `data-testid` | `string` | — | Test identifier for the item |
|
|
21
|
+
|
|
22
|
+
Inherits `HTMLAnchorElement` attributes (e.g. `href`, `target`).
|
|
23
|
+
|
|
24
|
+
### Custom labels
|
|
25
|
+
|
|
26
|
+
| Prop | Type | Default | Description |
|
|
27
|
+
| ---------- | -------- | ------------- | ------------------------------ |
|
|
28
|
+
| `viewMore` | `string` | `"View more"` | Label for the show-more button |
|
|
29
|
+
| `viewLess` | `string` | `"View less"` | Label for the show-less button |
|
|
30
|
+
|
|
31
|
+
## Variants
|
|
32
|
+
|
|
33
|
+
| `style` | Usage |
|
|
34
|
+
| ----------- | ------------------------------------------------------ |
|
|
35
|
+
| `"default"` | Standard size — use in main content areas |
|
|
36
|
+
| `"small"` | Compact — use in sidebars, footnotes, or dense layouts |
|
|
37
|
+
|
|
38
|
+
| `loadMode` | Usage |
|
|
39
|
+
| ---------- | ------------------------------------------------------------- |
|
|
40
|
+
| `"eager"` | All items loaded upfront; use `maxShown` to collapse the list |
|
|
41
|
+
| `"lazy"` | Items fetched on demand; requires `loadMore` and `onLoadMore` |
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
import { LinkList } from "@lifesg/react-design-system/link-list";
|
|
47
|
+
|
|
48
|
+
<LinkList
|
|
49
|
+
items={[
|
|
50
|
+
{
|
|
51
|
+
title: "Apply for a grant",
|
|
52
|
+
description: "Find out if you qualify.",
|
|
53
|
+
href: "/grants/apply",
|
|
54
|
+
},
|
|
55
|
+
{ title: "Check application status", href: "/grants/status" },
|
|
56
|
+
{ title: "Download forms", href: "/grants/forms" },
|
|
57
|
+
]}
|
|
58
|
+
maxShown={2}
|
|
59
|
+
/>;
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Props — `LinkListItemProps`
|
|
63
|
+
|
|
64
|
+
| Prop | Type | Default | Description |
|
|
65
|
+
| ---------------------- | ----------------------------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------- |
|
|
66
|
+
| `title` \* | `string \| JSX.Element` | — | Primary label displayed as the link text. |
|
|
67
|
+
| `description` | `string \| JSX.Element` | — | Secondary text shown below the title. |
|
|
68
|
+
| `secondaryDescription` | `JSX.Element` | — | Additional content rendered below the description. Accepts a JSX element for rich secondary content. |
|
|
69
|
+
| `options` | `T` | — | Arbitrary custom data passed through to the `onItemClick` callback. |
|
|
70
|
+
| `onClick` | `\| ((event: React.MouseEvent<HTMLAnchorElement>) => void)` | — | Called when an item is clicked. This overrides the `onItemClick` event handler. |
|
|
71
|
+
|
|
72
|
+
## Props — `LinkListBaseProps`
|
|
73
|
+
|
|
74
|
+
| Prop | Type | Default | Description |
|
|
75
|
+
| -------------- | ----------------------------------------------------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------- |
|
|
76
|
+
| `items` \* | `LinkListItemProps<T>[]` | — | The list of link items to render. |
|
|
77
|
+
| `style` | `LinkListStyle` | "default" | Visual size variant applied to all items. |
|
|
78
|
+
| `className` | `string` | — | — |
|
|
79
|
+
| `onItemClick` | `\| (( item: LinkListItemProps<T>, event: React.MouseEvent<HTMLAnchorElement> ) => void)` | — | Component-level click handler called for every item click, unless the item defines its own `onClick`. |
|
|
80
|
+
| `customLabels` | `\| { viewMore?: string; viewLess?: string; }` | — | Overrides the default toggle button labels. |
|
|
81
|
+
|
|
82
|
+
## Props — `LinkListEagerProps`
|
|
83
|
+
|
|
84
|
+
| Prop | Type | Default | Description |
|
|
85
|
+
| ---------- | --------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
86
|
+
| `loadMode` | `"eager"` | — | When `"eager"`, all items are rendered immediately. |
|
|
87
|
+
| `maxShown` | `number` | — | Maximum number of items shown before the list is truncated. Items beyond this count are hidden until the user activates the toggle. When omitted, all items are shown without truncation. |
|
|
88
|
+
|
|
89
|
+
## Props — `LinkListLazyProps`
|
|
90
|
+
|
|
91
|
+
| Prop | Type | Default | Description |
|
|
92
|
+
| --------------- | ----------------------------- | ------- | ----------------------------------------------------------------------------------------- |
|
|
93
|
+
| `loadMode` \* | `"lazy"` | — | When `"lazy"`, items are loaded on demand. |
|
|
94
|
+
| `loadMore` \* | `boolean` | — | Whether more items are available to load. When `false`, the "View more" button is hidden. |
|
|
95
|
+
| `onLoadMore` \* | `() => void \| Promise<void>` | — | Called when the user activates the "View more" button. |
|
|
96
|
+
|
|
97
|
+
## Props — `LinkListProps`
|
|
98
|
+
|
|
99
|
+
| Prop | Type | Default | Description |
|
|
100
|
+
| -------------- | ----------------------------------------------------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------- |
|
|
101
|
+
| `items` \* | `LinkListItemProps<T>[]` | — | The list of link items to render. |
|
|
102
|
+
| `style` | `LinkListStyle` | "default" | Visual size variant applied to all items. |
|
|
103
|
+
| `className` | `string` | — | — |
|
|
104
|
+
| `onItemClick` | `\| (( item: LinkListItemProps<T>, event: React.MouseEvent<HTMLAnchorElement> ) => void)` | — | Component-level click handler called for every item click, unless the item defines its own `onClick`. |
|
|
105
|
+
| `customLabels` | `\| { viewMore?: string; viewLess?: string; }` | — | Overrides the default toggle button labels. |
|
|
106
|
+
| `loadMode` | `"eager"` | — | When `"eager"`, all items are rendered immediately. |
|
|
107
|
+
|
|
108
|
+
## Rules
|
|
109
|
+
|
|
110
|
+
- Use `maxShown` when the list has many items to avoid overwhelming the user on first load (eager mode).
|
|
111
|
+
- In `"lazy"` mode, both `loadMore` and `onLoadMore` are required.
|
|
112
|
+
- `onItemClick` applies to all items; per-item `onClick` in `LinkListItemProps` overrides it for that item.
|
|
113
|
+
- Use per-item `onClick` when individual items need different handling; use `onItemClick` for a uniform handler across all items.
|
|
114
|
+
- Use `description` to give users enough context to choose the right link without clicking.
|
|
115
|
+
|
|
116
|
+
## Anti-patterns
|
|
117
|
+
|
|
118
|
+
- Using lazy mode without providing `onLoadMore` — the "View more" button will be non-functional.
|
|
119
|
+
- Omitting `description` when link titles are ambiguous.
|
|
120
|
+
- Using LinkList for non-link content — use a list or table instead.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# LocalNav
|
|
2
|
+
|
|
3
|
+
Import: `import { LocalNavMenu, LocalNavDropdown } from "@lifesg/react-design-system/local-nav"`
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
Use to let users navigate between sections on the same page (in-page navigation). Two components exist for different breakpoints — use both together for full responsive coverage.
|
|
8
|
+
|
|
9
|
+
## Sub-components
|
|
10
|
+
|
|
11
|
+
`LocalNavMenu`, `LocalNavDropdown`
|
|
12
|
+
|
|
13
|
+
## Variants
|
|
14
|
+
|
|
15
|
+
| Component | Viewport | Behaviour |
|
|
16
|
+
| ------------------ | ----------------- | ---------------------------------------------------------------------- |
|
|
17
|
+
| `LocalNavMenu` | Desktop | Vertical link list fixed to the left of page content, sticky on scroll |
|
|
18
|
+
| `LocalNavDropdown` | Tablet and mobile | Renders inline, then sticks to top of viewport after scroll |
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
import {
|
|
24
|
+
LocalNavMenu,
|
|
25
|
+
LocalNavDropdown,
|
|
26
|
+
} from "@lifesg/react-design-system/local-nav";
|
|
27
|
+
|
|
28
|
+
const navItems = [
|
|
29
|
+
{ title: "Overview", id: "overview" },
|
|
30
|
+
{ title: "Eligibility", id: "eligibility" },
|
|
31
|
+
{ title: "How to apply", id: "how-to-apply" },
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
{
|
|
35
|
+
/* Desktop */
|
|
36
|
+
}
|
|
37
|
+
<LocalNavMenu
|
|
38
|
+
items={navItems}
|
|
39
|
+
selectedItemIndex={activeIndex}
|
|
40
|
+
onNavItemSelect={(e, item, index) => {}}
|
|
41
|
+
/>;
|
|
42
|
+
|
|
43
|
+
{
|
|
44
|
+
/* Mobile / tablet */
|
|
45
|
+
}
|
|
46
|
+
<LocalNavDropdown
|
|
47
|
+
items={navItems}
|
|
48
|
+
selectedItemIndex={activeIndex}
|
|
49
|
+
defaultLabel="Jump to section"
|
|
50
|
+
onNavItemSelect={(e, item, index) => {}}
|
|
51
|
+
/>;
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Props — `TitleAddonProps`
|
|
55
|
+
|
|
56
|
+
| Prop | Type | Default | Description |
|
|
57
|
+
| ------- | ------------- | ------- | ----------------------------------------------------------------------- |
|
|
58
|
+
| `left` | `JSX.Element` | — | Element rendered immediately before the title. |
|
|
59
|
+
| `right` | `JSX.Element` | — | Element rendered after the title, flushed to the far right of the item. |
|
|
60
|
+
|
|
61
|
+
## Props — `LocalNavItemProps`
|
|
62
|
+
|
|
63
|
+
| Prop | Type | Default | Description |
|
|
64
|
+
| ------------ | --------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
65
|
+
| `title` \* | `string \| React.ReactNode` | — | — |
|
|
66
|
+
| `id` | `string` | — | — |
|
|
67
|
+
| `titleAddon` | `TitleAddonProps` | — | Optional addon element rendered alongside the item title. Ignored when a custom `renderItem` is provided, since the caller then controls the item's layout. |
|
|
68
|
+
|
|
69
|
+
## Props — `LocalNavMenuItemRenderProps`
|
|
70
|
+
|
|
71
|
+
| Prop | Type | Default | Description |
|
|
72
|
+
| ------------- | --------- | ------- | ------------------------------------------------------------ |
|
|
73
|
+
| `selected` \* | `boolean` | — | Whether this item is the currently selected navigation item. |
|
|
74
|
+
|
|
75
|
+
## Props — `LocalNavMenuProps`
|
|
76
|
+
|
|
77
|
+
| Prop | Type | Default | Description |
|
|
78
|
+
| -------------------- | -------------------------------------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------ |
|
|
79
|
+
| `renderItem` | `\| (( item: LocalNavItemProps, renderProps: LocalNavMenuItemRenderProps ) => React.ReactNode)` | — | Custom renderer for each navigation item. |
|
|
80
|
+
| `className` | `string` | — | — |
|
|
81
|
+
| `id` | `string` | — | — |
|
|
82
|
+
| `onNavItemSelect` \* | `( e: React.MouseEvent<HTMLElement> \| React.KeyboardEvent<HTMLElement>, item: LocalNavItemProps, index: number ) => void` | — | Called when a navigation item is activated. |
|
|
83
|
+
| `items` \* | `LocalNavItemProps[]` | — | The list of navigation items to render. |
|
|
84
|
+
| `selectedItemIndex` | `number` | — | Zero-based index of the currently selected item. |
|
|
85
|
+
|
|
86
|
+
## Props — `LocalNavDropdownItemRenderProps`
|
|
87
|
+
|
|
88
|
+
| Prop | Type | Default | Description |
|
|
89
|
+
| ------------- | --------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
90
|
+
| `selected` \* | `boolean` | — | Whether this item is the currently selected navigation item. |
|
|
91
|
+
| `stickied` \* | `boolean` | — | Whether the dropdown is currently in its stickied (fixed) state. Use this to conditionally style items differently when the nav is pinned. |
|
|
92
|
+
|
|
93
|
+
## Props — `LocalNavDropdownProps`
|
|
94
|
+
|
|
95
|
+
| Prop | Type | Default | Description |
|
|
96
|
+
| -------------------- | -------------------------------------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
97
|
+
| `defaultLabel` \* | `string \| React.ReactNode` | — | Label shown in the collapsed trigger when no item is selected or when the dropdown is not stickied. Once the dropdown is stickied and an item is selected, the trigger displays the selected item's title instead. |
|
|
98
|
+
| `stickyOffset` | `number` | 0 | Distance in pixels from the top of the viewport while the dropdown is stickied. |
|
|
99
|
+
| `renderItem` | `\| (( item: LocalNavItemProps, renderProps: LocalNavDropdownItemRenderProps ) => React.ReactNode)` | — | Custom renderer for each item in the dropdown list. |
|
|
100
|
+
| `className` | `string` | — | — |
|
|
101
|
+
| `id` | `string` | — | — |
|
|
102
|
+
| `onNavItemSelect` \* | `( e: React.MouseEvent<HTMLElement> \| React.KeyboardEvent<HTMLElement>, item: LocalNavItemProps, index: number ) => void` | — | Called when a navigation item is activated. |
|
|
103
|
+
| `items` \* | `LocalNavItemProps[]` | — | The list of navigation items to render. |
|
|
104
|
+
| `selectedItemIndex` | `number` | — | Zero-based index of the currently selected item. |
|
|
105
|
+
|
|
106
|
+
## Rules
|
|
107
|
+
|
|
108
|
+
- Use `LocalNavMenu` on desktop and `LocalNavDropdown` on tablet/mobile — render both and show/hide with CSS breakpoints.
|
|
109
|
+
- Always implement scroll-spy: update `selectedItemIndex` as the user scrolls to keep the active item in sync with the visible section. This is not automatic.
|
|
110
|
+
- Place `LocalNavMenu` on the left side of the page content area — not above it.
|
|
111
|
+
- Set `stickyOffset` on `LocalNavDropdown` equal to the height of the fixed Navbar so the dropdown does not overlap it when sticky.
|
|
112
|
+
- Keep item labels short — long labels cause overflow.
|
|
113
|
+
- `defaultLabel` is required on `LocalNavDropdown` — it is the placeholder shown before any item is selected.
|
|
114
|
+
|
|
115
|
+
## Anti-patterns
|
|
116
|
+
|
|
117
|
+
- Do not use `LocalNavMenu` on tablet or mobile — use `LocalNavDropdown` instead.
|
|
118
|
+
- Do not omit scroll-spy logic — without it, `selectedItemIndex` will not reflect the current section.
|
|
119
|
+
- Do not place `LocalNavMenu` above the content — it must be a left sidebar.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Masthead
|
|
2
|
+
|
|
3
|
+
Import: `import { Masthead } from "@lifesg/react-design-system/masthead"`
|
|
4
|
+
|
|
5
|
+
## Props
|
|
6
|
+
|
|
7
|
+
| Prop | Type | Default | Description |
|
|
8
|
+
| --------- | --------- | ------- | ----------------------------------------------------------------------------------------------------------------- |
|
|
9
|
+
| `stretch` | `boolean` | false | Allows the masthead to span the full width of its container rather than constraining to the default layout width. |
|
|
10
|
+
|
|
11
|
+
## Rules
|
|
12
|
+
|
|
13
|
+
- No mandatory props.
|
|
14
|
+
- `Masthead` is typically included via the `Navbar` `masthead` prop — use this standalone component only when rendering the masthead outside a Navbar context.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Menu
|
|
2
|
+
|
|
3
|
+
Import: `import { Menu } from "@lifesg/react-design-system/menu"`
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
Use when a trigger element (button, avatar, nav link) should open a floating dropdown with a list of actions or links.
|
|
8
|
+
|
|
9
|
+
Common use cases:
|
|
10
|
+
|
|
11
|
+
- Avatar / user account menu in authenticated dashboards
|
|
12
|
+
- Navbar link dropdown for sub-navigation
|
|
13
|
+
|
|
14
|
+
## Sub-components
|
|
15
|
+
|
|
16
|
+
`Menu`, `Menu.Content`, `Menu.Section`, `Menu.Item`, `Menu.Link`
|
|
17
|
+
|
|
18
|
+
## Variants
|
|
19
|
+
|
|
20
|
+
| Use case | Structure |
|
|
21
|
+
| ----------------- | --------------------------------------------------------------------------------------------------------------------------- |
|
|
22
|
+
| User account menu | First section: name + email as `Menu.Item`. Subsequent sections: `Menu.Link` for profile/settings, `Menu.Item` for sign out |
|
|
23
|
+
| Nav link dropdown | Single `Menu.Section` with `Menu.Link` items |
|
|
24
|
+
| Action menu | `Menu.Section` with `Menu.Item` elements and optional `label` |
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import { Menu } from "@lifesg/react-design-system/menu";
|
|
30
|
+
import { Button } from "@lifesg/react-design-system/button";
|
|
31
|
+
|
|
32
|
+
<Menu
|
|
33
|
+
menuContent={
|
|
34
|
+
<Menu.Content>
|
|
35
|
+
<Menu.Section showDivider={false}>
|
|
36
|
+
<Menu.Link href="/profile">Profile</Menu.Link>
|
|
37
|
+
<Menu.Link href="/settings">Settings</Menu.Link>
|
|
38
|
+
</Menu.Section>
|
|
39
|
+
<Menu.Section>
|
|
40
|
+
<Menu.Item label="Sign out" onClick={() => {}} />
|
|
41
|
+
</Menu.Section>
|
|
42
|
+
</Menu.Content>
|
|
43
|
+
}
|
|
44
|
+
>
|
|
45
|
+
<Button styleType="secondary">Account</Button>
|
|
46
|
+
</Menu>;
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Props — `MenuProps`
|
|
50
|
+
|
|
51
|
+
| Prop | Type | Default | Description |
|
|
52
|
+
| ------------------ | -------------------------------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
53
|
+
| `menuContent` \* | `FunctionComponentElement<MenuContentProps>` | — | The `Menu.Content` element to display inside the popover panel. |
|
|
54
|
+
| `position` | `PopoverPosition` | "bottom-start" | Preferred placement of the popover relative to the trigger. |
|
|
55
|
+
| `children` \* | `React.ReactNode` | — | The trigger element rendered inside a focusable wrapper div. |
|
|
56
|
+
| `trigger` | `PopoverTriggerType` | "click" | Interaction that opens the popover. |
|
|
57
|
+
| `id` | `string` | — | — |
|
|
58
|
+
| `zIndex` | `number` | — | Overrides the stacking context z-index for the floating popover layer. Falls back to the inherited floating context z-index when omitted. |
|
|
59
|
+
| `className` | `string` | — | — |
|
|
60
|
+
| `rootNode` | `RefObject<HTMLElement>` | document.body | The root element that contains the popover element. |
|
|
61
|
+
| `customOffset` | `number` | 16 | Distance in pixels between the trigger element and the popover panel. |
|
|
62
|
+
| `delay` | `\| { open?: number; close?: number }` | — | Open and close delays in milliseconds. Only applies when `trigger` is `"hover"`. |
|
|
63
|
+
| `enableFlip` | `boolean` | true | Allows the popover to flip to the opposite side when the preferred position has insufficient viewport space. |
|
|
64
|
+
| `enableResize` | `boolean` | false | Resizes the popover to fit the remaining vertical space in the viewport. |
|
|
65
|
+
| `overflow` | `PopoverOverflowType` | "auto" | CSS `overflow` value forwarded to the popover card when `enableResize` is active. |
|
|
66
|
+
| `popoverAriaLabel` | `string` | "More information" | Accessible label for the popover dialog element. |
|
|
67
|
+
| `triggerOnFocus` | `boolean` | false | Also opens the popover when the trigger element receives focus. |
|
|
68
|
+
| `isModal` | `boolean` | true | When `true`, focus is trapped inside the popover until dismissed. When `false`, the popover closes automatically once the user tabs away from both the trigger and the popover. |
|
|
69
|
+
| `onPopoverAppear` | `(() => void)` | — | Called when the popover becomes visible. |
|
|
70
|
+
| `onPopoverDismiss` | `(() => void)` | — | Called when the popover is dismissed. |
|
|
71
|
+
|
|
72
|
+
## Props — `MenuContentProps`
|
|
73
|
+
|
|
74
|
+
| Prop | Type | Default | Description |
|
|
75
|
+
| ------------- | -------------------------------------------------------------------- | ------- | ---------------------------------------------------------------- |
|
|
76
|
+
| `children` \* | `ReactElement<MenuSectionProps> \| ReactElement<MenuSectionProps>[]` | — | One or more `Menu.Section` elements that make up the panel body. |
|
|
77
|
+
| `overflow` | `PopoverOverflowType` | — | CSS `overflow` value applied to the popover card body. |
|
|
78
|
+
| `maxHeight` | `number` | — | Maximum height of the popover card in pixels. |
|
|
79
|
+
|
|
80
|
+
## Props — `MenuSectionProps`
|
|
81
|
+
|
|
82
|
+
| Prop | Type | Default | Description |
|
|
83
|
+
| ------------- | -------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
84
|
+
| `children` \* | `MenuSectionItem \| MenuSectionItem[]` | — | One or more `Menu.Item` or `Menu.Link` elements within this section. |
|
|
85
|
+
| `showDivider` | `boolean` | true | Whether to render a top divider separating this section from the one above. |
|
|
86
|
+
| `columns` | `number` | — | Renders section children in a grid with the given number of columns on desktop. Items flow vertically (top-to-bottom) within each column before wrapping to the next. Has no effect on mobile viewports, which always render as a single column. |
|
|
87
|
+
| `label` | `string` | — | Accessible group label rendered above the section items. |
|
|
88
|
+
|
|
89
|
+
## Props — `MenuItemProps`
|
|
90
|
+
|
|
91
|
+
| Prop | Type | Default | Description |
|
|
92
|
+
| ---------- | -------- | ------- | -------------------------------------------------- |
|
|
93
|
+
| `label` | `string` | — | Primary text label. |
|
|
94
|
+
| `subLabel` | `string` | — | Secondary descriptive text rendered below `label`. |
|
|
95
|
+
|
|
96
|
+
## Rules
|
|
97
|
+
|
|
98
|
+
- `Menu` wraps a trigger element as its child — the trigger can be any element (e.g. `Avatar`, `Button`, `NavbarLink`).
|
|
99
|
+
- Pass `Menu.Content` as the `menuContent` prop — not as a child of `Menu`.
|
|
100
|
+
- Use `Menu.Link` for navigation (href-based); use `Menu.Item` for in-page actions (onClick-based).
|
|
101
|
+
- Use `Menu.Section` with `showDivider` to visually separate unrelated groups of actions.
|
|
102
|
+
- For user account menus, always show the user's name and email as the first section.
|
|
103
|
+
- Use `position` to control which side of the trigger the menu appears on; default `"bottom-start"` suits most cases.
|
|
104
|
+
|
|
105
|
+
## Anti-patterns
|
|
106
|
+
|
|
107
|
+
- Do not pass `Menu.Content` as a child of `Menu` — it must be passed as `menuContent`.
|
|
108
|
+
- Do not mix navigation links and action items in the same `Menu.Section` without a divider.
|
|
109
|
+
- Do not use `Menu.Item` for links that navigate to a new page — use `Menu.Link` instead.
|