@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,123 @@
|
|
|
1
|
+
# Timetable
|
|
2
|
+
|
|
3
|
+
Import: `import { Timetable } from "@lifesg/react-design-system/timetable"`
|
|
4
|
+
|
|
5
|
+
## TimeTableRowData
|
|
6
|
+
|
|
7
|
+
| Prop | Type | Default | Description |
|
|
8
|
+
| ----------------------- | ---------------------------------------------------------- | --------- | --------------------------------------------------------- |
|
|
9
|
+
| `name` \* | `string` | — | Row header name |
|
|
10
|
+
| `rowCells` \* | `TimeTableRowCellData[]` | — | Array of row cells; sorted by start time automatically |
|
|
11
|
+
| `id` | `string` | — | Row identifier; passed to `onRowNameClick` |
|
|
12
|
+
| `subtitle` | `string \| JSX.Element` | — | Row header subtitle |
|
|
13
|
+
| `rowHeaderPopover` | `TimeTablePopoverProps` | — | Custom popover for the row header |
|
|
14
|
+
| `outOfRangeCellPopover` | `TimeTablePopoverProps` | — | Custom popover for auto-filled blocked cells |
|
|
15
|
+
| `rowMinTime` | `string` | `minTime` | Row start time; blocked cells are auto-filled before this |
|
|
16
|
+
| `rowMaxTime` | `string` | `maxTime` | Row end time; blocked cells are auto-filled after this |
|
|
17
|
+
| `onRowNameClick` | `(rowData: TimeTableRowData, e: React.MouseEvent) => void` | — | Called when the row header name is clicked |
|
|
18
|
+
|
|
19
|
+
## TimeTableRowCellData
|
|
20
|
+
|
|
21
|
+
| Prop | Type | Default | Description |
|
|
22
|
+
| --------------------- | --------------------------------------------------------------- | ------- | ------------------------------------------------------------------ |
|
|
23
|
+
| `startTime` \* | `string` | — | Cell start time in `HH:mm` (duration must be a multiple of 15 min) |
|
|
24
|
+
| `endTime` \* | `string` | — | Cell end time in `HH:mm` (duration must be a multiple of 15 min) |
|
|
25
|
+
| `id` | `string` | — | Cell identifier; passed to `onClick` |
|
|
26
|
+
| `status` | `"filled" \| "blocked" \| "default" \| "pending" \| "disabled"` | — | Cell status, determines rendering style |
|
|
27
|
+
| `title` | `string` | — | Title shown for this cell |
|
|
28
|
+
| `subtitle` | `string` | — | Subtitle shown for this cell |
|
|
29
|
+
| `customPopover` | `TimeTablePopoverProps` | — | Custom popover for the cell |
|
|
30
|
+
| `cellStyleAttributes` | `TimeTableCellStyleAttributes` | — | Custom style attributes; falls back to default row bar colors |
|
|
31
|
+
| `onClick` | `(data: TimeTableRowCellData, e: React.MouseEvent) => void` | — | Called when the cell is clicked |
|
|
32
|
+
|
|
33
|
+
## TimeTableCellStyleAttributes
|
|
34
|
+
|
|
35
|
+
| Prop | Type | Description |
|
|
36
|
+
| ------------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------- |
|
|
37
|
+
| `backgroundColor` | `string \| (props: ThemeStyleProps) => string` | Main background color |
|
|
38
|
+
| `altBackgroundColor` | `string \| (props: ThemeStyleProps) => string` | Alternate background color for striped displays |
|
|
39
|
+
| `hoverBackgroundColor` | `string \| (props: ThemeStyleProps) => string` | Background on hover |
|
|
40
|
+
| `altHoverBackgroundColor` | `string \| (props: ThemeStyleProps) => string` | Alternate background on hover |
|
|
41
|
+
| `styleType` | `"default" \| "solid" \| "stripes"` | Display style; defaults to cell type (blocked/pending = striped, others = solid) |
|
|
42
|
+
|
|
43
|
+
## Props — `TimeTableCellStyleAttributes`
|
|
44
|
+
|
|
45
|
+
| Prop | Type | Default | Description |
|
|
46
|
+
| ------------------------- | ------------------------ | --------- | -------------------------------------------------------------------------- |
|
|
47
|
+
| `backgroundColor` | `string` | — | Main background color for the cell. |
|
|
48
|
+
| `altBackgroundColor` | `string` | — | Alternate background color used for the second stripe in striped patterns. |
|
|
49
|
+
| `hoverBackgroundColor` | `string` | — | Background color applied on hover. |
|
|
50
|
+
| `altHoverBackgroundColor` | `string` | — | Alternate background color applied on hover for striped patterns. |
|
|
51
|
+
| `styleType` | `TimeTableCellStyleType` | "default" | Cell background pattern. |
|
|
52
|
+
|
|
53
|
+
## Props — `TimeTableRef`
|
|
54
|
+
|
|
55
|
+
| Prop | Type | Default | Description |
|
|
56
|
+
| ---------------- | ------------ | ------- | --------------------------------------------------------------------------------------------- |
|
|
57
|
+
| `resetScroll` \* | `() => void` | — | Scrolls the timetable back to `initialScrollTime`. No-op when `initialScrollTime` is not set. |
|
|
58
|
+
|
|
59
|
+
## Props — `TimeTableRowData`
|
|
60
|
+
|
|
61
|
+
| Prop | Type | Default | Description |
|
|
62
|
+
| ----------------------- | --------------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------- |
|
|
63
|
+
| `id` | `string` | — | Unique identifier for the row. |
|
|
64
|
+
| `name` \* | `string` | — | Display name shown in the row header. |
|
|
65
|
+
| `rowCells` \* | `TimeTableRowCellData[]` | — | Cells occupying time slots within this row. |
|
|
66
|
+
| `subtitle` | `string \| JSX.Element` | — | Secondary text or element rendered beneath the row name in the header. |
|
|
67
|
+
| `rowHeaderPopover` | `TimeTablePopoverProps` | — | Popover attached to the row header label. |
|
|
68
|
+
| `outOfRangeCellPopover` | `TimeTablePopoverProps` | — | Popover shown when the user interacts with cells outside the active time range. |
|
|
69
|
+
| `rowMinTime` | `string` | — | Earliest time this row is active, in `HH:mm` format. Time slots before this value are rendered as out-of-range. |
|
|
70
|
+
| `rowMaxTime` | `string` | — | Latest time this row is active, in `HH:mm` format. Time slots after this value are rendered as out-of-range. |
|
|
71
|
+
| `onRowNameClick` | `\| ((rowData: TimeTableRowData, e: React.MouseEvent) => void)` | — | Called when the row header name is clicked. |
|
|
72
|
+
|
|
73
|
+
## Props — `TimeTablePopoverProps`
|
|
74
|
+
|
|
75
|
+
| Prop | Type | Default | Description |
|
|
76
|
+
| ------------ | -------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------- |
|
|
77
|
+
| `trigger` \* | `PopoverTriggerType` | — | Interaction that opens the popover |
|
|
78
|
+
| `content` \* | `string \| JSX.Element \| (() => React.ReactNode)` | — | Content rendered inside the popover. |
|
|
79
|
+
| `delay` | `\| { open?: number; close?: number }` | — | Open and close delays in milliseconds. Useful for hover popovers to avoid flickering on quick mouse movements. |
|
|
80
|
+
| `offset` | `number` | — | Distance in pixels between the popover and its trigger element. |
|
|
81
|
+
|
|
82
|
+
## Props — `TimeTableProps`
|
|
83
|
+
|
|
84
|
+
| Prop | Type | Default | Description |
|
|
85
|
+
| ------------------------ | --------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
86
|
+
| `id` | `string` | — | — |
|
|
87
|
+
| `className` | `string` | — | — |
|
|
88
|
+
| `date` \* | `string` | — | The date currently displayed. |
|
|
89
|
+
| `emptyContentMessage` | `string` | — | Text shown in the empty state when `rowData` is empty or `totalRecords` is `0`. |
|
|
90
|
+
| `rowData` \* | `TimeTableRowData[]` | — | Rows to render in the timetable grid. |
|
|
91
|
+
| `loading` | `boolean` | — | When `true`, replaces row content with a loading spinner. |
|
|
92
|
+
| `minTime` | `string` | "00:00" | Earliest hour column displayed, in `HH:mm` format. Rounded to the nearest full hour internally. |
|
|
93
|
+
| `maxTime` | `string` | "23:00" | Latest hour column displayed, in `HH:mm` format. Rounded up to the nearest full hour internally. |
|
|
94
|
+
| `initialScrollTime` | `string` | — | Time to scroll to on mount in `HH:mm` format (e.g. `"09:00"`). |
|
|
95
|
+
| `minDate` | `string` | — | Earliest date the user can navigate to in the date picker. |
|
|
96
|
+
| `maxDate` | `string` | — | Latest date the user can navigate to in the date picker |
|
|
97
|
+
| `totalRecords` | `number` | — | Total number of records across all pages. When `0`, the empty state is shown regardless of `rowData`. |
|
|
98
|
+
| `showCurrentDateAsToday` | `boolean` | — | When `true`, the date header displays `"Today"` instead of the formatted date string when `date` matches the current calendar day. |
|
|
99
|
+
| `showDateAsShortForm` | `boolean` | — | When `true`, the date header uses an abbreviated date format. |
|
|
100
|
+
| `roundInitialScrollTime` | `boolean` | true | When `true`, `initialScrollTime` is snapped to the nearest time interval (e.g. `"06:30"` snaps to `"06:00"` with a 60-minute interval). |
|
|
101
|
+
| `onRefresh` | `(() => void)` | — | Called when the user clicks the refresh button in the header. |
|
|
102
|
+
| `onPage` | `(() => void)` | — | Called when the timetable needs to load the next page of rows. |
|
|
103
|
+
| `onPreviousDayClick` \* | `(currentDate: string) => void` | — | Called when the user navigates to the previous day. |
|
|
104
|
+
| `onNextDayClick` \* | `(currentDate: string) => void` | — | Called when the user navigates to the next day. |
|
|
105
|
+
| `onCalendarDateSelect` | `((currentDate: string) => void)` | — | Called when the user selects a date from the calendar picker. |
|
|
106
|
+
|
|
107
|
+
## Props — `TimeTableRowCellData`
|
|
108
|
+
|
|
109
|
+
| Prop | Type | Default | Description |
|
|
110
|
+
| --------------------- | ---------------------------------------------------------------- | ------- | ------------------------------------------------------- |
|
|
111
|
+
| `id` | `string` | — | Unique identifier for the cell. |
|
|
112
|
+
| `startTime` \* | `string` | — | Start time of the cell in `HH:mm` format. |
|
|
113
|
+
| `endTime` \* | `string` | — | End time of the cell in `HH:mm` format. |
|
|
114
|
+
| `status` \* | `TimeTableCellType` | — | Visual and interaction state of the cell. |
|
|
115
|
+
| `title` | `string` | — | Primary label rendered inside the cell. |
|
|
116
|
+
| `subtitle` | `string` | — | Secondary label rendered below `title` inside the cell. |
|
|
117
|
+
| `customPopover` | `TimeTablePopoverProps` | — | Popover shown when the user interacts with this cell. |
|
|
118
|
+
| `cellStyleAttributes` | `TimeTableCellStyleAttributes` | — | Color and pattern overrides for this cell. |
|
|
119
|
+
| `onClick` | `\| ((data: TimeTableRowCellData, e: React.MouseEvent) => void)` | — | Called when the user clicks this cell. |
|
|
120
|
+
|
|
121
|
+
## Rules
|
|
122
|
+
|
|
123
|
+
- Do not combine `onClick` on a cell with a `trigger="click"` popover on the same cell — they conflict.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Toast
|
|
2
|
+
|
|
3
|
+
Import: `import { Toast } from "@lifesg/react-design-system/toast"`
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
Use for brief, non-blocking feedback on system or user actions — "Changes saved", "Upload complete", "Request failed". Does not replace persistent callouts — use Alert for section-specific notices.
|
|
8
|
+
|
|
9
|
+
## Sub-components
|
|
10
|
+
|
|
11
|
+
### ActionButtonProps
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Default | Description |
|
|
14
|
+
| --------- | ------------ | ------- | --------------------------------------------------- |
|
|
15
|
+
| `label` | `string` | — | Label of the action button — required |
|
|
16
|
+
| `onClick` | `() => void` | — | Called when the action button is clicked — required |
|
|
17
|
+
|
|
18
|
+
## Variants
|
|
19
|
+
|
|
20
|
+
| `type` | When to use |
|
|
21
|
+
| ----------- | ----------------------------- |
|
|
22
|
+
| `"success"` | Action completed successfully |
|
|
23
|
+
| `"error"` | Action failed or system error |
|
|
24
|
+
| `"warning"` | Completed with caution |
|
|
25
|
+
| `"info"` | Neutral status update |
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import { Toast } from "@lifesg/react-design-system/toast";
|
|
31
|
+
|
|
32
|
+
<Toast
|
|
33
|
+
type="success"
|
|
34
|
+
label="Changes saved successfully."
|
|
35
|
+
autoDismiss
|
|
36
|
+
onDismiss={() => {}}
|
|
37
|
+
/>;
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Props — `ToastActionButtonProps`
|
|
41
|
+
|
|
42
|
+
| Prop | Type | Default | Description |
|
|
43
|
+
| ------------ | ------------ | ------- | ---------------------------------------- |
|
|
44
|
+
| `label` \* | `string` | — | The label of the action button |
|
|
45
|
+
| `onClick` \* | `() => void` | — | Called when the action button is clicked |
|
|
46
|
+
|
|
47
|
+
## Props — `ToastProps`
|
|
48
|
+
|
|
49
|
+
| Prop | Type | Default | Description |
|
|
50
|
+
| ----------------- | --------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
|
51
|
+
| `type` \* | `ToastType` | "success" | Visual variant controlling the icon and color treatment. |
|
|
52
|
+
| `label` \* | `string \| React.ReactNode` | — | Main text content of the toast. When `title` is also provided, rendered as a supporting description using a smaller typography style. |
|
|
53
|
+
| `title` | `string \| React.ReactNode` | — | Optional heading displayed above `label`. |
|
|
54
|
+
| `autoDismiss` | `boolean` | — | When `true`, the toast dismisses itself automatically after `autoDismissTime` milliseconds. |
|
|
55
|
+
| `autoDismissTime` | `number` | 4000 | Duration in milliseconds before the toast auto-dismisses. Only takes effect when `autoDismiss` is `true`. |
|
|
56
|
+
| `onDismiss` | `(() => void)` | — | Called after the dismissal animation completes. |
|
|
57
|
+
| `fixed` | `boolean` | true | Pins the toast in a fixed position at the top of the viewport. |
|
|
58
|
+
| `actionButton` | `ToastActionButtonProps` | — | Optional action button rendered alongside the toast content. |
|
|
59
|
+
|
|
60
|
+
## Rules
|
|
61
|
+
|
|
62
|
+
- `autoDismissTime` only takes effect when `autoDismiss` is `true`.
|
|
63
|
+
- Use `autoDismiss` for non-critical updates — keep visible for errors that require user action.
|
|
64
|
+
- Use `actionButton` for recoverable errors (retry, undo).
|
|
65
|
+
- Use `title` + `label` together when the toast message has a heading and a description.
|
|
66
|
+
- Do not use Toast for persistent notices — use Alert or NotificationBanner instead.
|
|
67
|
+
|
|
68
|
+
## Anti-patterns
|
|
69
|
+
|
|
70
|
+
- Using Toast for errors that require user action without providing an `actionButton`.
|
|
71
|
+
- Setting `autoDismiss` on error toasts — errors should remain visible until the user dismisses them.
|
|
72
|
+
- Replacing Alert with Toast for section-specific inline messages.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Toggle
|
|
2
|
+
|
|
3
|
+
Import: `import { Toggle } from "@lifesg/react-design-system/toggle"`
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
- Form inputs for boolean choices, single/multi-option selection, and yes/no questions.
|
|
8
|
+
- Preferred over bare `Checkbox` and `RadioButton` when building form inputs that need a card-style visible label container.
|
|
9
|
+
|
|
10
|
+
## Variants
|
|
11
|
+
|
|
12
|
+
| `type` | When to use |
|
|
13
|
+
| ------------ | ------------------------------------------------------------ |
|
|
14
|
+
| `"checkbox"` | Multiple options can be selected or deselected independently |
|
|
15
|
+
| `"radio"` | Exactly one option must be selected — cannot be deselected |
|
|
16
|
+
| `"yes"` | Binary yes answer — use with a paired `type="no"` Toggle |
|
|
17
|
+
| `"no"` | Binary no answer — use with a paired `type="yes"` Toggle |
|
|
18
|
+
|
|
19
|
+
| `styleType` | When to use |
|
|
20
|
+
| ------------- | ------------------------------------------------------------ |
|
|
21
|
+
| `"default"` | Standard bordered card |
|
|
22
|
+
| `"no-border"` | Borderless — for contexts where the border is visually noisy |
|
|
23
|
+
|
|
24
|
+
| `type` | Behaviour |
|
|
25
|
+
| ------------ | ------------------------------------ |
|
|
26
|
+
| `"checkbox"` | Allows deselection — standard toggle |
|
|
27
|
+
| `"radio"` | No deselection — radio-style |
|
|
28
|
+
| `"yes"` | No deselection — tick indicator |
|
|
29
|
+
| `"no"` | No deselection — cross indicator |
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { Toggle } from "@lifesg/react-design-system/toggle";
|
|
35
|
+
import { Form } from "@lifesg/react-design-system/form";
|
|
36
|
+
|
|
37
|
+
{/* Checkbox group */}
|
|
38
|
+
<Form.Label>Select your preferences</Form.Label>
|
|
39
|
+
<Toggle type="checkbox" indicator checked={emailChecked} onChange={(e) => {}}>
|
|
40
|
+
Email notifications
|
|
41
|
+
</Toggle>
|
|
42
|
+
<Toggle type="checkbox" indicator checked={smsChecked} onChange={(e) => {}}>
|
|
43
|
+
SMS notifications
|
|
44
|
+
</Toggle>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Props — `ToggleProps`
|
|
48
|
+
|
|
49
|
+
| Prop | Type | Default | Description |
|
|
50
|
+
| ----------------------- | ------------------------------------------------------------ | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
51
|
+
| `type` | `ToggleType` | "checkbox" | Determines selection behavior and the indicator icon style. |
|
|
52
|
+
| `indicator` | `boolean` | — | Whether to render the selection indicator icon inside the toggle. The icon shape depends on `type`: - `"checkbox"` renders a tick box. - `"radio"` renders a filled circle. - `"yes"` renders a tick. - `"no"` renders a cross. |
|
|
53
|
+
| `checked` | `boolean` | — | Controlled checked state of the toggle. |
|
|
54
|
+
| `styleType` | `ToggleStyleType` | "default" | Visual presentation of the toggle container. |
|
|
55
|
+
| `children` \* | `React.ReactNode` | — | Label content for the toggle. |
|
|
56
|
+
| `childrenMaxLines` | `\| { mobile?: number; desktop?: number; tablet?: number; }` | — | Maximum number of visible label lines at each breakpoint. Lines beyond the limit are truncated with an ellipsis. Omit a breakpoint key to leave that viewport unconstrained. |
|
|
57
|
+
| `subLabel` | `(() => JSX.Element) \| JSX.Element \| string` | — | Secondary content rendered below the main label. |
|
|
58
|
+
| `disabled` | `boolean` | — | — |
|
|
59
|
+
| `error` | `boolean` | — | Renders the input in an error state. |
|
|
60
|
+
| `className` | `string` | — | — |
|
|
61
|
+
| `name` | `string` | — | — |
|
|
62
|
+
| `id` | `string` | — | — |
|
|
63
|
+
| `onChange` | `\| ((event: React.ChangeEvent<HTMLInputElement>) => void)` | — | Called when the toggle selection state changes. When provided, the caller is responsible for reflecting the new value back via `checked`. |
|
|
64
|
+
| `compositeSection` | `ToggleCompositeSectionProps` | — | Optional collapsible body section rendered below the toggle label. The section automatically expands when the toggle becomes selected. |
|
|
65
|
+
| `removable` | `boolean` | — | Renders a "Remove" button alongside the label. Clicking the button calls `onRemove` when the toggle is not disabled. |
|
|
66
|
+
| `onRemove` | `(() => void)` | — | Called when the remove button is clicked and the toggle is not disabled. |
|
|
67
|
+
| `useContentWidth` | `boolean` | — | Constrains the toggle width to its content rather than filling the container. |
|
|
68
|
+
| `focusableWhenDisabled` | `boolean` | — | Keeps the toggle keyboard-focusable when `disabled` is `true`. When set alongside `disabled`, the native `disabled` attribute is omitted in favor of `aria-disabled="true"` and `tabIndex={0}`, keeping the element keyboard-reachable. `onChange` is still suppressed. |
|
|
69
|
+
|
|
70
|
+
## Props — `ToggleCompositeSectionProps`
|
|
71
|
+
|
|
72
|
+
| Prop | Type | Default | Description |
|
|
73
|
+
| ----------------- | ------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
74
|
+
| `children` \* | `React.ReactNode` | — | Content rendered inside the composite section body. |
|
|
75
|
+
| `collapsible` | `boolean` | true | Whether the section can be collapsed and expanded by the user. |
|
|
76
|
+
| `initialExpanded` | `boolean` | false | Whether the composite section starts in the expanded state. When the toggle becomes selected, the section automatically expands regardless of this value. Only applies when `collapsible` is `true`. |
|
|
77
|
+
| `errors` | `string[] \| JSX.Element` | — | Errors shown in an alert banner when the section is collapsed. Pass a `string[]` to render a titled error list, or a `JSX.Element` for a custom error presentation. |
|
|
78
|
+
|
|
79
|
+
## Rules
|
|
80
|
+
|
|
81
|
+
- `children` (main label) is mandatory.
|
|
82
|
+
- `indicator` is recommended when using `type="yes"` or `type="no"` to show the tick/cross icon.
|
|
83
|
+
- Always pass `indicator` for `type="checkbox"`, `type="radio"`, `type="yes"`, and `type="no"` — it shows the relevant icon (tick, dot, tick, cross) inside the toggle.
|
|
84
|
+
- Use `type="radio"` when the option cannot be deselected (e.g. single-select from a list).
|
|
85
|
+
- Use `compositeSection` to reveal dependent fields when a Toggle is selected — do not conditionally render fields outside the Toggle.
|
|
86
|
+
- Use `subLabel` for secondary supporting text — do not embed long descriptions in `children`.
|
|
87
|
+
- Use `childrenMaxLines` to constrain label height in dense list layouts.
|
|
88
|
+
- Place a `Form.Label` above each toggle group to provide the question or field label.
|
|
89
|
+
|
|
90
|
+
### Spacing
|
|
91
|
+
|
|
92
|
+
| Layout | Gap |
|
|
93
|
+
| ------------------------- | ------------------------------------ |
|
|
94
|
+
| Stacked (checkbox, radio) | `spacing-8` between each Toggle |
|
|
95
|
+
| Side by side (yes / no) | `spacing-16` between the two Toggles |
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
{/* Stacked — checkbox or radio */}
|
|
99
|
+
<Form.Label>Declaration</Form.Label>
|
|
100
|
+
<ToggleWrapper> {/* margin-bottom: spacing-32 — matches Form field built-in */}
|
|
101
|
+
<Toggle indicator checked={a} onChange={...}>Option A</Toggle>
|
|
102
|
+
<div style={{ marginBottom: Spacing["spacing-8"] }} />
|
|
103
|
+
<Toggle indicator checked={b} onChange={...}>Option B</Toggle>
|
|
104
|
+
</ToggleWrapper>
|
|
105
|
+
|
|
106
|
+
{/* Side by side — yes / no */}
|
|
107
|
+
<Form.Label>Do you have a disability?</Form.Label>
|
|
108
|
+
<YesNoGroup> {/* display: flex; gap: spacing-16 */}
|
|
109
|
+
<Toggle type="yes" indicator checked={val === "yes"} onChange={...}>Yes</Toggle>
|
|
110
|
+
<Toggle type="no" indicator checked={val === "no"} onChange={...}>No</Toggle>
|
|
111
|
+
</YesNoGroup>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Anti-patterns
|
|
115
|
+
|
|
116
|
+
- Do not use bare `Checkbox` or `RadioButton` in forms when a labelled card container is needed — use `Toggle` instead.
|
|
117
|
+
- Do not conditionally render dependent fields outside the Toggle — use `compositeSection`.
|
|
118
|
+
- Do not use `type="yes"` or `type="no"` without pairing both options together in the UI.
|
|
119
|
+
- Do not omit `indicator` for any Toggle type — it is required for all variants.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Typography
|
|
2
|
+
|
|
3
|
+
Import: `import { Typography } from "@lifesg/react-design-system/typography"`
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
Use Typography components to render text content — headings, paragraphs, and links. Do not use for parent containers that have text children; use Font tokens instead (see `foundations/typography.md`).
|
|
8
|
+
|
|
9
|
+
## Sub-components
|
|
10
|
+
|
|
11
|
+
**Headings:** `Typography.HeadingXXL`, `Typography.HeadingXL`, `Typography.HeadingLG`, `Typography.HeadingMD`, `Typography.HeadingSM`, `Typography.HeadingXS`
|
|
12
|
+
|
|
13
|
+
**Body:** `Typography.BodyBL`, `Typography.BodyMD`, `Typography.BodySM`, `Typography.BodyXS`
|
|
14
|
+
|
|
15
|
+
**Links:** `Typography.LinkBL`, `Typography.LinkMD`, `Typography.LinkSM`, `Typography.LinkXS`
|
|
16
|
+
|
|
17
|
+
## Variants
|
|
18
|
+
|
|
19
|
+
| Component | Default element | Usage |
|
|
20
|
+
| ----------------- | --------------- | -------------------------------------- |
|
|
21
|
+
| `HeadingXXL` | `h1` | Hero/page-level title |
|
|
22
|
+
| `HeadingXL` | `h2` | Page title or primary section heading |
|
|
23
|
+
| `HeadingLG` | `h3` | Section heading |
|
|
24
|
+
| `HeadingMD` | `h4` | Subsection heading |
|
|
25
|
+
| `HeadingSM` | — | Card heading, secondary heading |
|
|
26
|
+
| `HeadingXS` | — | Small heading |
|
|
27
|
+
| `BodyBL` | — | Default paragraph text |
|
|
28
|
+
| `BodyMD` | — | Medium body text |
|
|
29
|
+
| `BodySM` | — | Helper text |
|
|
30
|
+
| `BodyXS` | — | Metadata, timestamps |
|
|
31
|
+
| `LinkBL/MD/SM/XS` | `<a>` | Inline links, sized to match body text |
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { Typography } from "@lifesg/react-design-system/typography";
|
|
37
|
+
|
|
38
|
+
<>
|
|
39
|
+
<Typography.HeadingXL>Page title</Typography.HeadingXL>
|
|
40
|
+
<Typography.BodyBL paragraph>
|
|
41
|
+
Supporting body text for the section. Use{" "}
|
|
42
|
+
<Typography.LinkMD href="/learn-more">
|
|
43
|
+
link components
|
|
44
|
+
</Typography.LinkMD>{" "}
|
|
45
|
+
for inline anchors.
|
|
46
|
+
</Typography.BodyBL>
|
|
47
|
+
</>;
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Props — `TypographyLinkProps`
|
|
51
|
+
|
|
52
|
+
| Prop | Type | Default | Description |
|
|
53
|
+
| ---------------- | -------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------- |
|
|
54
|
+
| `weight` | `TypographyWeight` | "regular" | Font weight of the link text. |
|
|
55
|
+
| `external` | `boolean` | — | Appends an external-link icon after the link text to signal that the destination is outside the current site. |
|
|
56
|
+
| `underlineStyle` | `TypographyUnderlineStyle` | "underline" | Text-decoration style for the link. |
|
|
57
|
+
|
|
58
|
+
## Props — `TypographyProps`
|
|
59
|
+
|
|
60
|
+
| Prop | Type | Default | Description |
|
|
61
|
+
| ----------- | ------------------ | --------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
62
|
+
| `weight` | `TypographyWeight` | "regular" | Font weight of the rendered text. |
|
|
63
|
+
| `inline` | `boolean` | — | Renders the element as an inline `span`. When `true`, the `as` prop is ignored and `maxLines` clamping is disabled. |
|
|
64
|
+
| `paragraph` | `boolean` | — | Adds a bottom margin to separate the element from following content. |
|
|
65
|
+
| `maxLines` | `number` | — | Clamps visible text to the given number of lines, truncating the remainder. |
|
|
66
|
+
| `as` | `T` | — | Overrides the rendered HTML element, e.g. `"h2"`. |
|
|
67
|
+
|
|
68
|
+
## Rules
|
|
69
|
+
|
|
70
|
+
- Default HTML elements: `HeadingXXL` → `h1`, `HeadingXL` → `h2`, `HeadingLG` → `h3`, `HeadingMD` → `h4`.
|
|
71
|
+
- Text color is not set by the component — apply via `styled()` using Colour tokens.
|
|
72
|
+
- Do not pass `color` as a prop — `TypographyProps` extends `HTMLAttributes` and forwards the value to the DOM, triggering a React warning.
|
|
73
|
+
- Do not use Typography for parent containers — use Font tokens instead.
|
|
74
|
+
- Heading components render default HTML elements — use the `as` prop when visual size and reading order don't match.
|
|
75
|
+
- Heading levels must be sequential (h1 → h2 → h3) — never skip levels.
|
|
76
|
+
- Apply text color via `styled()` using Colour tokens — never pass `color` as a prop.
|
|
77
|
+
- Use the `weight` prop to adjust emphasis, not opacity.
|
|
78
|
+
- Links are underlined by default — use `underlineStyle="none"` only when context makes the link obvious.
|
|
79
|
+
- Always use the `external` prop for links that navigate outside the domain.
|
|
80
|
+
|
|
81
|
+
## Anti-patterns
|
|
82
|
+
|
|
83
|
+
- Passing `color` directly as a prop — it forwards a Colour object to the DOM and triggers a React warning.
|
|
84
|
+
- Skipping heading levels for visual sizing — use the `as` prop instead.
|
|
85
|
+
- Using `<h1>` raw HTML inside a Typography component instead of `as="h1"`.
|
|
86
|
+
- Using Typography on a container element to inherit font styles — use Font tokens.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# UneditableSection
|
|
2
|
+
|
|
3
|
+
Import: `import { UneditableSection } from "@lifesg/react-design-system/uneditable-section"`
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
- Displaying read-only label/value pairs on summary or review screens.
|
|
8
|
+
- Showing retrieved or pre-filled data the user cannot edit.
|
|
9
|
+
|
|
10
|
+
## Sub-components
|
|
11
|
+
|
|
12
|
+
- `UneditableSection.ItemSection` — wraps a group of items (extends `HTMLUListElement`)
|
|
13
|
+
- `UneditableSection.Item` — individual label/value item
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { UneditableSection } from "@lifesg/react-design-system/uneditable-section";
|
|
19
|
+
|
|
20
|
+
<UneditableSection
|
|
21
|
+
title="Personal details"
|
|
22
|
+
items={[
|
|
23
|
+
{ label: "Full name", value: "Jane Tan", displayWidth: "half" },
|
|
24
|
+
{ label: "Date of birth", value: "1 Jan 1990", displayWidth: "half" },
|
|
25
|
+
{ label: "Address", value: "123 Example Road, Singapore 123456" },
|
|
26
|
+
]}
|
|
27
|
+
/>;
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Props — `UneditableSectionItemProps`
|
|
31
|
+
|
|
32
|
+
| Prop | Type | Default | Description |
|
|
33
|
+
| ------------------- | --------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
34
|
+
| `id` | `string` | — | — |
|
|
35
|
+
| `label` \* | `string` | — | Field label shown above the item value. |
|
|
36
|
+
| `value` \* | `string \| React.ReactNode` | — | The display value for the field. |
|
|
37
|
+
| `displayWidth` | `UneditableSectionItemDisplayWidth` | "full" | Grid column span for the item within the section layout. |
|
|
38
|
+
| `maskState` | `UneditableSectionItemMaskState` | — | Controls whether the value is masked or unmasked. When undefined, no mask or unmask controls are rendered. |
|
|
39
|
+
| `maskLoadingState` | `UneditableSectionItemMaskLoadingState` | — | Async loading state during a mask or unmask action. When undefined, the resolved value is rendered directly. |
|
|
40
|
+
| `disableMaskUnmask` | `boolean` | — | Prevents the user from toggling the mask or unmask control. |
|
|
41
|
+
| `alert` | `AlertProps` | — | Alert rendered below the item value. |
|
|
42
|
+
| `maskRange` | `number[]` | — | Index range `[start, end]` of the characters to mask (inclusive, zero-based). Characters outside this range remain visible. |
|
|
43
|
+
| `unmaskRange` | `number[]` | — | Index range `[start, end]` of the characters to keep visible (inclusive, zero-based). Characters outside this range are masked. |
|
|
44
|
+
| `maskRegex` | `RegExp` | — | Regular expression whose matches are replaced with `maskChar`. |
|
|
45
|
+
| `maskTransformer` | `((value: string) => string)` | — | Custom function that receives the raw value and returns the fully masked string. Takes precedence over `maskRegex`, `maskRange`, and `unmaskRange`. |
|
|
46
|
+
| `maskChar` | `string` | "•" | Character used to replace each masked character in the displayed value. |
|
|
47
|
+
|
|
48
|
+
## Props — `UneditableSectionItemSectionProps`
|
|
49
|
+
|
|
50
|
+
| Prop | Type | Default | Description |
|
|
51
|
+
| --------- | --------- | ------- | -------------------------------------------------------------- |
|
|
52
|
+
| `stretch` | `boolean` | — | Stretches every child item to fill the full width of the grid. |
|
|
53
|
+
|
|
54
|
+
## Props — `UneditableSectionProps`
|
|
55
|
+
|
|
56
|
+
| Prop | Type | Default | Description |
|
|
57
|
+
| --------------- | ---------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------ |
|
|
58
|
+
| `items` | `UneditableSectionItemProps[]` | — | List of labeled read-only field items to render in a responsive grid. |
|
|
59
|
+
| `title` | `string` | — | Optional heading rendered above the items grid. |
|
|
60
|
+
| `description` | `string` | — | Optional description rendered below `title` and above the items grid. |
|
|
61
|
+
| `topSection` | `JSX.Element` | — | Custom content rendered above the items grid, below `description`. |
|
|
62
|
+
| `bottomSection` | `JSX.Element` | — | Custom content rendered below the items grid. |
|
|
63
|
+
| `children` | `JSX.Element \| JSX.Element[]` | — | Fully custom section body. |
|
|
64
|
+
| `className` | `string` | — | — |
|
|
65
|
+
| `id` | `string` | — | — |
|
|
66
|
+
| `background` | `boolean` | true | Applies a grey background to the section container. Set to `false` for a transparent background. |
|
|
67
|
+
| `stretch` | `boolean` | — | Stretches every item to fill the full column width of the grid. |
|
|
68
|
+
| `fullWidth` | `boolean` | — | When `true`, allows the section to span the full available width of its parent. |
|
|
69
|
+
| `onMask` | `((item: UneditableSectionItemProps) => void)` | — | Called when a mask control is activated on an item. |
|
|
70
|
+
| `onUnmask` | `((item: UneditableSectionItemProps) => void)` | — | Called when an unmask control is activated on an item. |
|
|
71
|
+
| `onTryAgain` | `((item: UneditableSectionItemProps) => void)` | — | Called when the retry button is clicked on an item in error state. |
|
|
72
|
+
|
|
73
|
+
## Rules
|
|
74
|
+
|
|
75
|
+
- Always supply `items` with a `label` and `value` for each field.
|
|
76
|
+
- Use `displayWidth: "half"` for short values (names, dates, codes) to show two per row; omit for long values.
|
|
77
|
+
- Show `"—"` for any unanswered or empty field rather than leaving `value` blank.
|
|
78
|
+
- Set `background={false}` when rendering inside a `BoxContainer` — the container already provides the visual boundary; the default background creates an unwanted nested box appearance.
|
|
79
|
+
- Set `fullWidth` when rendering inside a container such as `BoxContainer` to remove the default `Layout.Content` spacing and allow content to fill the full width.
|
|
80
|
+
|
|
81
|
+
## Anti-patterns
|
|
82
|
+
|
|
83
|
+
- Do not use `UneditableSection` for editable fields — use `Form.*` components with `readOnly` instead.
|
|
84
|
+
- Do not nest `UneditableSection` inside another `UneditableSection`.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Layout & Composition Patterns
|
|
2
|
+
|
|
3
|
+
Cross-cutting rules for assembling FDS components into polished layouts. Apply alongside per-page template files.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Vertical Spacing Rhythm
|
|
8
|
+
|
|
9
|
+
| Between… | Token |
|
|
10
|
+
| ------------------------------------------ | ------------ |
|
|
11
|
+
| Page title and first content section | `spacing-32` |
|
|
12
|
+
| Major page sections (cards → table) | `spacing-48` |
|
|
13
|
+
| Cards in a grid row (gap) | `spacing-24` |
|
|
14
|
+
| Heading and its body text within a section | `spacing-8` |
|
|
15
|
+
| Form fields within a group | `spacing-24` |
|
|
16
|
+
| Form field groups (personal → address) | `spacing-32` |
|
|
17
|
+
| Button row and the content above it | `spacing-32` |
|
|
18
|
+
| Last content section and page bottom | `spacing-64` |
|
|
19
|
+
|
|
20
|
+
Apply spacing tokens via inline `style` props:
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
<section style={{ marginBottom: Spacing["spacing-48"] }}>...</section>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Visual Hierarchy
|
|
29
|
+
|
|
30
|
+
Every page must use at least three distinct text levels.
|
|
31
|
+
|
|
32
|
+
| Level | Component | Colour token | Use for |
|
|
33
|
+
| ------- | ---------------------- | -------------- | --------------------------------------- |
|
|
34
|
+
| Primary | `Typography.HeadingXL` | `text` | Page title — one per page |
|
|
35
|
+
| Section | `Typography.HeadingMD` | `text` | Section headings within the page |
|
|
36
|
+
| Card | `Typography.HeadingSM` | `text` | Card titles, panel headers |
|
|
37
|
+
| Body | `Typography.BodyMD` | `text` | Default body text, descriptions |
|
|
38
|
+
| Support | `Typography.BodySM` | `text-subtle` | Secondary info, timestamps, helper text |
|
|
39
|
+
| Caption | `Typography.BodyXS` | `text-subtler` | Footnotes, disclaimers, metadata |
|
|
40
|
+
|
|
41
|
+
Page title pattern: `Typography.HeadingXL` as `h1`, `Typography.BodyMD` with `style={{ color: Colour["text-subtle"] }}` directly below as subtitle.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Anti-patterns
|
|
46
|
+
|
|
47
|
+
| Anti-pattern | Fix |
|
|
48
|
+
| --------------------------------------- | ----------------------------------------------------------------- |
|
|
49
|
+
| All text the same size and colour | Use at least 3 Typography sizes; `text-subtle` for secondary text |
|
|
50
|
+
| No spacing between page sections | `spacing-48` between major sections |
|
|
51
|
+
| Summary metric cards stacked vertically | Use a responsive grid: 3–4 across on desktop, stacked on mobile |
|
|
52
|
+
| Buttons touching content above | `spacing-32` above button row |
|
|
53
|
+
| Missing page-bottom spacing | `spacing-64` padding-bottom on page wrapper |
|