@grampro/kit 0.0.0-stage → 2.4.0
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/.gbs/skills/gbs-components/SKILL.md +190 -0
- package/.gbs/skills/gbs-components/references/accessibility.md +102 -0
- package/.gbs/skills/gbs-components/references/data-display.md +185 -0
- package/.gbs/skills/gbs-components/references/data-grid.md +116 -0
- package/.gbs/skills/gbs-components/references/forms.md +202 -0
- package/.gbs/skills/gbs-components/references/install.md +211 -0
- package/.gbs/skills/gbs-components/references/overlays.md +206 -0
- package/.gbs/skills/gbs-components/references/pickers.md +83 -0
- package/.gbs/skills/gbs-components/references/styling.md +246 -0
- package/.gbs/skills/gbs-components/references/toaster.md +57 -0
- package/CHANGELOG.md +139 -0
- package/LICENSE +15 -0
- package/README.md +193 -2
- package/index.cjs +1370 -0
- package/package.json +74 -4
- package/schema/passport-v1.schema.json +204 -0
- package/source/components/accordion/README.md +45 -0
- package/source/components/accordion/core/index.ts +2 -0
- package/source/components/accordion/core/open.ts +56 -0
- package/source/components/accordion/core/types.ts +17 -0
- package/source/components/accordion/index.ts +8 -0
- package/source/components/accordion/passport.json +259 -0
- package/source/components/accordion/react/Accordion.tsx +133 -0
- package/source/components/accordion/react/AccordionItem.tsx +120 -0
- package/source/components/accordion/react/context.ts +20 -0
- package/source/components/accordion/react/props.ts +13 -0
- package/source/components/accordion/styles.css +207 -0
- package/source/components/alert/README.md +39 -0
- package/source/components/alert/core/index.ts +2 -0
- package/source/components/alert/core/live.ts +25 -0
- package/source/components/alert/core/types.ts +7 -0
- package/source/components/alert/index.ts +5 -0
- package/source/components/alert/passport.json +250 -0
- package/source/components/alert/react/Alert.tsx +113 -0
- package/source/components/alert/react/icons.tsx +21 -0
- package/source/components/alert/react/locale.ts +5 -0
- package/source/components/alert/react/props.ts +11 -0
- package/source/components/alert/styles.css +154 -0
- package/source/components/avatar/core/identity.ts +103 -0
- package/source/components/avatar/core/index.ts +2 -0
- package/source/components/avatar/core/types.ts +12 -0
- package/source/components/avatar/index.ts +7 -0
- package/source/components/avatar/passport.json +294 -0
- package/source/components/avatar/react/Avatar.tsx +118 -0
- package/source/components/avatar/react/AvatarGroup.tsx +74 -0
- package/source/components/avatar/react/locale.ts +11 -0
- package/source/components/avatar/react/props.ts +6 -0
- package/source/components/avatar/styles.css +225 -0
- package/source/components/badge/README.md +37 -0
- package/source/components/badge/core/count.ts +37 -0
- package/source/components/badge/core/index.ts +2 -0
- package/source/components/badge/core/types.ts +11 -0
- package/source/components/badge/index.ts +7 -0
- package/source/components/badge/passport.json +332 -0
- package/source/components/badge/react/Badge.tsx +89 -0
- package/source/components/badge/react/Tag.tsx +82 -0
- package/source/components/badge/react/locale.ts +5 -0
- package/source/components/badge/react/props.ts +6 -0
- package/source/components/badge/styles.css +203 -0
- package/source/components/breadcrumb/README.md +34 -0
- package/source/components/breadcrumb/core/index.ts +4 -0
- package/source/components/breadcrumb/core/trail.ts +42 -0
- package/source/components/breadcrumb/core/types.ts +20 -0
- package/source/components/breadcrumb/index.ts +5 -0
- package/source/components/breadcrumb/passport.json +243 -0
- package/source/components/breadcrumb/react/Breadcrumb.tsx +177 -0
- package/source/components/breadcrumb/react/locale.ts +6 -0
- package/source/components/breadcrumb/react/props.ts +14 -0
- package/source/components/breadcrumb/styles.css +138 -0
- package/source/components/button/README.md +32 -0
- package/source/components/button/core/index.ts +3 -0
- package/source/components/button/core/state.ts +16 -0
- package/source/components/button/core/types.ts +28 -0
- package/source/components/button/index.ts +5 -0
- package/source/components/button/passport.json +402 -0
- package/source/components/button/passport.manual.json +31 -0
- package/source/components/button/react/Button.tsx +173 -0
- package/source/components/button/react/locale.ts +5 -0
- package/source/components/button/react/props.ts +23 -0
- package/source/components/button/styles.css +232 -0
- package/source/components/card/README.md +60 -0
- package/source/components/card/core/index.ts +4 -0
- package/source/components/card/core/trend.ts +32 -0
- package/source/components/card/core/types.ts +5 -0
- package/source/components/card/index.ts +5 -0
- package/source/components/card/passport.json +337 -0
- package/source/components/card/react/Card.tsx +133 -0
- package/source/components/card/react/Stat.tsx +127 -0
- package/source/components/card/react/props.ts +4 -0
- package/source/components/card/styles.css +230 -0
- package/source/components/checkbox/README.md +36 -0
- package/source/components/checkbox/core/group.ts +30 -0
- package/source/components/checkbox/core/index.ts +3 -0
- package/source/components/checkbox/core/types.ts +17 -0
- package/source/components/checkbox/index.ts +9 -0
- package/source/components/checkbox/passport.json +456 -0
- package/source/components/checkbox/react/Checkbox.tsx +147 -0
- package/source/components/checkbox/react/CheckboxGroup.tsx +149 -0
- package/source/components/checkbox/react/context.ts +16 -0
- package/source/components/checkbox/react/locale.ts +5 -0
- package/source/components/checkbox/react/props.ts +6 -0
- package/source/components/checkbox/styles.css +211 -0
- package/source/components/combobox/README.md +170 -0
- package/source/components/combobox/core/filter.ts +155 -0
- package/source/components/combobox/core/index.ts +16 -0
- package/source/components/combobox/core/types.ts +48 -0
- package/source/components/combobox/core/virtual.ts +67 -0
- package/source/components/combobox/index.ts +13 -0
- package/source/components/combobox/passport.json +456 -0
- package/source/components/combobox/react/Listbox.tsx +310 -0
- package/source/components/combobox/react/MultiSelect.tsx +265 -0
- package/source/components/combobox/react/Popover.tsx +38 -0
- package/source/components/combobox/react/Select.tsx +200 -0
- package/source/components/combobox/react/icons.tsx +11 -0
- package/source/components/combobox/react/locale.ts +16 -0
- package/source/components/combobox/react/props.ts +88 -0
- package/source/components/combobox/react/useCombobox.ts +409 -0
- package/source/components/combobox/styles.css +419 -0
- package/source/components/data-grid/README.md +233 -0
- package/source/components/data-grid/agent/coerce.ts +368 -0
- package/source/components/data-grid/agent/contract.ts +410 -0
- package/source/components/data-grid/agent/dataset.ts +92 -0
- package/source/components/data-grid/agent/engine.ts +470 -0
- package/source/components/data-grid/agent/executors.ts +155 -0
- package/source/components/data-grid/agent/index.ts +79 -0
- package/source/components/data-grid/agent/intent.ts +324 -0
- package/source/components/data-grid/agent/operations.ts +335 -0
- package/source/components/data-grid/agent/validate.ts +630 -0
- package/source/components/data-grid/agent/webmcp.ts +107 -0
- package/source/components/data-grid/core/columnHelper.ts +21 -0
- package/source/components/data-grid/core/columns.ts +288 -0
- package/source/components/data-grid/core/filtering.ts +237 -0
- package/source/components/data-grid/core/grid.ts +638 -0
- package/source/components/data-grid/core/index.ts +14 -0
- package/source/components/data-grid/core/rows.ts +64 -0
- package/source/components/data-grid/core/sorting.ts +133 -0
- package/source/components/data-grid/core/state.ts +82 -0
- package/source/components/data-grid/core/store.ts +52 -0
- package/source/components/data-grid/core/types.ts +302 -0
- package/source/components/data-grid/core/values.ts +98 -0
- package/source/components/data-grid/core/virtual.ts +85 -0
- package/source/components/data-grid/export/csv.ts +32 -0
- package/source/components/data-grid/export/download.ts +12 -0
- package/source/components/data-grid/export/pdf-font.ts +139 -0
- package/source/components/data-grid/export/pdf-writer.ts +113 -0
- package/source/components/data-grid/export/pdf.ts +379 -0
- package/source/components/data-grid/export/table.ts +73 -0
- package/source/components/data-grid/export/xlsx.ts +145 -0
- package/source/components/data-grid/export/zip.ts +87 -0
- package/source/components/data-grid/index.ts +14 -0
- package/source/components/data-grid/passport.json +1051 -0
- package/source/components/data-grid/passport.manual.json +255 -0
- package/source/components/data-grid/react/AskGrid.tsx +164 -0
- package/source/components/data-grid/react/Cell.tsx +282 -0
- package/source/components/data-grid/react/ColumnMenu.tsx +135 -0
- package/source/components/data-grid/react/DataGrid.tsx +335 -0
- package/source/components/data-grid/react/FilterForm.tsx +142 -0
- package/source/components/data-grid/react/HeaderRow.tsx +332 -0
- package/source/components/data-grid/react/Pagination.tsx +107 -0
- package/source/components/data-grid/react/Popover.tsx +35 -0
- package/source/components/data-grid/react/Row.tsx +85 -0
- package/source/components/data-grid/react/Toolbar.tsx +267 -0
- package/source/components/data-grid/react/Viewport.tsx +167 -0
- package/source/components/data-grid/react/context.ts +57 -0
- package/source/components/data-grid/react/hooks.ts +83 -0
- package/source/components/data-grid/react/icons.tsx +45 -0
- package/source/components/data-grid/react/keyboard.ts +110 -0
- package/source/components/data-grid/react/locale.ts +130 -0
- package/source/components/data-grid/styles.css +874 -0
- package/source/components/date-picker/README.md +141 -0
- package/source/components/date-picker/core/calendar.ts +266 -0
- package/source/components/date-picker/core/format.ts +170 -0
- package/source/components/date-picker/core/index.ts +41 -0
- package/source/components/date-picker/core/today.ts +72 -0
- package/source/components/date-picker/core/types.ts +68 -0
- package/source/components/date-picker/index.ts +16 -0
- package/source/components/date-picker/passport.json +407 -0
- package/source/components/date-picker/react/Calendar.tsx +160 -0
- package/source/components/date-picker/react/CalendarPanel.tsx +170 -0
- package/source/components/date-picker/react/DatePicker.tsx +249 -0
- package/source/components/date-picker/react/DateRangePicker.tsx +291 -0
- package/source/components/date-picker/react/MonthYearPanels.tsx +123 -0
- package/source/components/date-picker/react/Popover.tsx +45 -0
- package/source/components/date-picker/react/icons.tsx +15 -0
- package/source/components/date-picker/react/locale.ts +23 -0
- package/source/components/date-picker/react/props.ts +76 -0
- package/source/components/date-picker/react/useDatePicker.ts +557 -0
- package/source/components/date-picker/styles.css +445 -0
- package/source/components/dialog/README.md +39 -0
- package/source/components/dialog/core/api.ts +34 -0
- package/source/components/dialog/core/dialog.ts +11 -0
- package/source/components/dialog/core/index.ts +7 -0
- package/source/components/dialog/core/store.ts +81 -0
- package/source/components/dialog/core/types.ts +59 -0
- package/source/components/dialog/index.ts +7 -0
- package/source/components/dialog/passport.json +344 -0
- package/source/components/dialog/react/Dialog.tsx +279 -0
- package/source/components/dialog/react/DialogHost.tsx +45 -0
- package/source/components/dialog/react/icons.tsx +20 -0
- package/source/components/dialog/react/locale.ts +8 -0
- package/source/components/dialog/react/props.ts +13 -0
- package/source/components/dialog/styles.css +280 -0
- package/source/components/file-uploader/README.md +99 -0
- package/source/components/file-uploader/core/format.ts +79 -0
- package/source/components/file-uploader/core/index.ts +26 -0
- package/source/components/file-uploader/core/store.ts +432 -0
- package/source/components/file-uploader/core/transport.ts +154 -0
- package/source/components/file-uploader/core/types.ts +126 -0
- package/source/components/file-uploader/core/validate.ts +83 -0
- package/source/components/file-uploader/index.ts +7 -0
- package/source/components/file-uploader/passport.json +518 -0
- package/source/components/file-uploader/react/FileRow.tsx +268 -0
- package/source/components/file-uploader/react/FileUploader.tsx +511 -0
- package/source/components/file-uploader/react/icons.tsx +60 -0
- package/source/components/file-uploader/react/locale.ts +33 -0
- package/source/components/file-uploader/react/props.ts +31 -0
- package/source/components/file-uploader/react/useFileUploader.ts +32 -0
- package/source/components/file-uploader/styles.css +394 -0
- package/source/components/input/README.md +44 -0
- package/source/components/input/core/count.ts +5 -0
- package/source/components/input/core/index.ts +11 -0
- package/source/components/input/core/otp.ts +64 -0
- package/source/components/input/core/types.ts +14 -0
- package/source/components/input/index.ts +8 -0
- package/source/components/input/passport.json +536 -0
- package/source/components/input/react/Input.tsx +219 -0
- package/source/components/input/react/OtpInput.tsx +256 -0
- package/source/components/input/react/dom.ts +10 -0
- package/source/components/input/react/icons.tsx +17 -0
- package/source/components/input/react/locale.ts +9 -0
- package/source/components/input/react/props.ts +6 -0
- package/source/components/input/styles.css +295 -0
- package/source/components/menu/README.md +68 -0
- package/source/components/menu/core/index.ts +11 -0
- package/source/components/menu/core/navigation.ts +84 -0
- package/source/components/menu/core/types.ts +32 -0
- package/source/components/menu/index.ts +19 -0
- package/source/components/menu/passport.json +322 -0
- package/source/components/menu/react/Menu.tsx +371 -0
- package/source/components/menu/react/context.ts +31 -0
- package/source/components/menu/react/items.tsx +268 -0
- package/source/components/menu/react/locale.ts +6 -0
- package/source/components/menu/react/props.ts +4 -0
- package/source/components/menu/react/useMenuKeys.ts +124 -0
- package/source/components/menu/styles.css +224 -0
- package/source/components/modal/README.md +51 -0
- package/source/components/modal/core/dismiss.ts +29 -0
- package/source/components/modal/core/index.ts +3 -0
- package/source/components/modal/core/types.ts +27 -0
- package/source/components/modal/index.ts +5 -0
- package/source/components/modal/passport.json +289 -0
- package/source/components/modal/react/Modal.tsx +242 -0
- package/source/components/modal/react/icons.tsx +1 -0
- package/source/components/modal/react/locale.ts +5 -0
- package/source/components/modal/react/props.ts +17 -0
- package/source/components/modal/styles.css +241 -0
- package/source/components/number-input/README.md +53 -0
- package/source/components/number-input/core/format.ts +37 -0
- package/source/components/number-input/core/index.ts +4 -0
- package/source/components/number-input/core/parse.ts +118 -0
- package/source/components/number-input/core/step.ts +73 -0
- package/source/components/number-input/core/types.ts +32 -0
- package/source/components/number-input/index.ts +5 -0
- package/source/components/number-input/passport.json +541 -0
- package/source/components/number-input/react/NumberInput.tsx +465 -0
- package/source/components/number-input/react/icons.tsx +17 -0
- package/source/components/number-input/react/locale.ts +7 -0
- package/source/components/number-input/react/props.ts +11 -0
- package/source/components/number-input/styles.css +230 -0
- package/source/components/popover/README.md +44 -0
- package/source/components/popover/core/index.ts +2 -0
- package/source/components/popover/core/types.ts +12 -0
- package/source/components/popover/index.ts +4 -0
- package/source/components/popover/passport.json +238 -0
- package/source/components/popover/react/Popover.tsx +202 -0
- package/source/components/popover/react/locale.ts +5 -0
- package/source/components/popover/react/props.ts +4 -0
- package/source/components/popover/styles.css +148 -0
- package/source/components/progress/README.md +38 -0
- package/source/components/progress/core/index.ts +2 -0
- package/source/components/progress/core/progress.ts +40 -0
- package/source/components/progress/core/types.ts +23 -0
- package/source/components/progress/index.ts +7 -0
- package/source/components/progress/passport.json +270 -0
- package/source/components/progress/react/CircularProgress.tsx +106 -0
- package/source/components/progress/react/Progress.tsx +113 -0
- package/source/components/progress/react/locale.ts +7 -0
- package/source/components/progress/react/props.ts +6 -0
- package/source/components/progress/styles.css +200 -0
- package/source/components/radio-group/README.md +58 -0
- package/source/components/radio-group/core/group.ts +36 -0
- package/source/components/radio-group/core/index.ts +2 -0
- package/source/components/radio-group/core/types.ts +20 -0
- package/source/components/radio-group/index.ts +9 -0
- package/source/components/radio-group/passport.json +477 -0
- package/source/components/radio-group/react/Radio.tsx +144 -0
- package/source/components/radio-group/react/RadioGroup.tsx +179 -0
- package/source/components/radio-group/react/context.ts +18 -0
- package/source/components/radio-group/react/locale.ts +5 -0
- package/source/components/radio-group/react/props.ts +12 -0
- package/source/components/radio-group/styles.css +269 -0
- package/source/components/shared/README.md +54 -0
- package/source/components/shared/core/agent/adapter.ts +65 -0
- package/source/components/shared/core/agent/history.ts +120 -0
- package/source/components/shared/core/agent/index.ts +46 -0
- package/source/components/shared/core/agent/numbers.ts +217 -0
- package/source/components/shared/core/agent/schema.ts +180 -0
- package/source/components/shared/core/agent/types.ts +169 -0
- package/source/components/shared/core/agent/webmcp.ts +328 -0
- package/source/components/shared/core/cx.ts +9 -0
- package/source/components/shared/core/field.ts +35 -0
- package/source/components/shared/core/position.ts +124 -0
- package/source/components/shared/core/text.ts +14 -0
- package/source/components/shared/index.ts +17 -0
- package/source/components/shared/react/GramproAIProvider.tsx +50 -0
- package/source/components/shared/react/Popover.tsx +189 -0
- package/source/components/shared/react/icons.tsx +74 -0
- package/source/components/shared/react/useAskAgent.ts +217 -0
- package/source/components/shared/react/useControllableState.ts +42 -0
- package/source/components/shared/styles.css +79 -0
- package/source/components/shared/version.json +4 -0
- package/source/components/shared/version.ts +6 -0
- package/source/components/skeleton/README.md +58 -0
- package/source/components/skeleton/core/index.ts +3 -0
- package/source/components/skeleton/core/lines.ts +25 -0
- package/source/components/skeleton/core/types.ts +5 -0
- package/source/components/skeleton/index.ts +5 -0
- package/source/components/skeleton/passport.json +251 -0
- package/source/components/skeleton/react/Empty.tsx +51 -0
- package/source/components/skeleton/react/Skeleton.tsx +92 -0
- package/source/components/skeleton/react/props.ts +4 -0
- package/source/components/skeleton/styles.css +185 -0
- package/source/components/spinner/README.md +29 -0
- package/source/components/spinner/core/index.ts +3 -0
- package/source/components/spinner/core/types.ts +28 -0
- package/source/components/spinner/core/visibility.ts +34 -0
- package/source/components/spinner/index.ts +6 -0
- package/source/components/spinner/passport.json +245 -0
- package/source/components/spinner/react/Spinner.tsx +96 -0
- package/source/components/spinner/react/locale.ts +5 -0
- package/source/components/spinner/react/props.ts +4 -0
- package/source/components/spinner/react/useDelayedLoading.ts +39 -0
- package/source/components/spinner/styles.css +173 -0
- package/source/components/switch/README.md +46 -0
- package/source/components/switch/core/index.ts +2 -0
- package/source/components/switch/core/toggle.ts +54 -0
- package/source/components/switch/core/types.ts +9 -0
- package/source/components/switch/index.ts +5 -0
- package/source/components/switch/passport.json +421 -0
- package/source/components/switch/react/Switch.tsx +219 -0
- package/source/components/switch/react/locale.ts +5 -0
- package/source/components/switch/react/props.ts +12 -0
- package/source/components/switch/styles.css +227 -0
- package/source/components/tabs/README.md +32 -0
- package/source/components/tabs/core/index.ts +3 -0
- package/source/components/tabs/core/navigation.ts +54 -0
- package/source/components/tabs/core/types.ts +16 -0
- package/source/components/tabs/index.ts +5 -0
- package/source/components/tabs/passport.json +315 -0
- package/source/components/tabs/react/Tabs.tsx +306 -0
- package/source/components/tabs/react/context.ts +21 -0
- package/source/components/tabs/styles.css +263 -0
- package/source/components/textarea/README.md +39 -0
- package/source/components/textarea/core/count.ts +5 -0
- package/source/components/textarea/core/index.ts +4 -0
- package/source/components/textarea/core/size.ts +23 -0
- package/source/components/textarea/core/types.ts +17 -0
- package/source/components/textarea/index.ts +5 -0
- package/source/components/textarea/passport.json +382 -0
- package/source/components/textarea/react/Textarea.tsx +208 -0
- package/source/components/textarea/react/locale.ts +5 -0
- package/source/components/textarea/react/props.ts +4 -0
- package/source/components/textarea/styles.css +158 -0
- package/source/components/toaster/README.md +128 -0
- package/source/components/toaster/core/api.ts +87 -0
- package/source/components/toaster/core/index.ts +14 -0
- package/source/components/toaster/core/store.ts +211 -0
- package/source/components/toaster/core/toast.ts +12 -0
- package/source/components/toaster/core/types.ts +78 -0
- package/source/components/toaster/index.ts +6 -0
- package/source/components/toaster/passport.json +221 -0
- package/source/components/toaster/react/ToastItem.tsx +164 -0
- package/source/components/toaster/react/Toaster.tsx +187 -0
- package/source/components/toaster/react/icons.tsx +23 -0
- package/source/components/toaster/react/locale.ts +6 -0
- package/source/components/toaster/react/props.ts +15 -0
- package/source/components/toaster/react/useToasts.ts +11 -0
- package/source/components/toaster/styles.css +282 -0
- package/source/components/tooltip/README.md +43 -0
- package/source/components/tooltip/core/index.ts +4 -0
- package/source/components/tooltip/core/types.ts +4 -0
- package/source/components/tooltip/core/visibility.ts +34 -0
- package/source/components/tooltip/index.ts +3 -0
- package/source/components/tooltip/passport.json +170 -0
- package/source/components/tooltip/react/Tooltip.tsx +196 -0
- package/source/components/tooltip/react/props.ts +4 -0
- package/source/components/tooltip/styles.css +71 -0
- package/source/fallback/index.tsx +11 -0
- package/source/globalStyle.ts +74 -0
- package/source/hooks/SelectionHooks/type.ts +4 -0
- package/source/hooks/SelectionHooks/useClickOutside.ts +22 -0
- package/source/hooks/SelectionHooks/useMultiSelectState.ts +64 -0
- package/source/hooks/SelectionHooks/useSelectData.ts +58 -0
- package/source/hooks/SelectionHooks/useSelectState.ts +36 -0
- package/source/icon/Icon.tsx +102 -0
- package/source/icon/iconPaths.ts +206 -0
- package/source/legacy-components/bargraph/index.tsx +95 -0
- package/source/legacy-components/bargraph/type.ts +12 -0
- package/source/legacy-components/breadcrumb/index.tsx +66 -0
- package/source/legacy-components/breadcrumb/types.ts +10 -0
- package/source/legacy-components/button/index.tsx +42 -0
- package/source/legacy-components/card/index.tsx +31 -0
- package/source/legacy-components/checkbox/index.tsx +25 -0
- package/source/legacy-components/contextmenu/ContextMenu.tsx +87 -0
- package/source/legacy-components/contextmenu/ContextMenuItem.tsx +30 -0
- package/source/legacy-components/contextmenu/index.ts +2 -0
- package/source/legacy-components/contextmenu/types.ts +19 -0
- package/source/legacy-components/darkmode/index.tsx +55 -0
- package/source/legacy-components/datagrid/context/GridContext.tsx +215 -0
- package/source/legacy-components/datagrid/createTemplate.tsx +82 -0
- package/source/legacy-components/datagrid/hooks/index.ts +6 -0
- package/source/legacy-components/datagrid/hooks/useColumns.ts +33 -0
- package/source/legacy-components/datagrid/hooks/useDataSource.ts +54 -0
- package/source/legacy-components/datagrid/hooks/useFiltering.ts +169 -0
- package/source/legacy-components/datagrid/hooks/usePagination.ts +112 -0
- package/source/legacy-components/datagrid/hooks/useRowSelection.ts +48 -0
- package/source/legacy-components/datagrid/hooks/useSearch.ts +90 -0
- package/source/legacy-components/datagrid/index.tsx +28 -0
- package/source/legacy-components/datagrid/layout/FilterPopup.tsx +92 -0
- package/source/legacy-components/datagrid/layout/GridBody.tsx +184 -0
- package/source/legacy-components/datagrid/layout/GridPagination.tsx +119 -0
- package/source/legacy-components/datagrid/layout/GridToolbar.tsx +109 -0
- package/source/legacy-components/datagrid/layout/index.tsx +68 -0
- package/source/legacy-components/datagrid/type.ts +198 -0
- package/source/legacy-components/datepicker/index.tsx +365 -0
- package/source/legacy-components/datepicker/types.ts +12 -0
- package/source/legacy-components/dialog/index.tsx +44 -0
- package/source/legacy-components/dialog/types.ts +12 -0
- package/source/legacy-components/formrenderer/componentHandles/CheckBoxHandles.tsx +80 -0
- package/source/legacy-components/formrenderer/componentHandles/DatePickerHandles.tsx +93 -0
- package/source/legacy-components/formrenderer/componentHandles/InputHandles.tsx +106 -0
- package/source/legacy-components/formrenderer/componentHandles/MultiHandles.tsx +93 -0
- package/source/legacy-components/formrenderer/componentHandles/SelectHandles.tsx +95 -0
- package/source/legacy-components/formrenderer/helperFunctions.ts +72 -0
- package/source/legacy-components/formrenderer/index.tsx +93 -0
- package/source/legacy-components/formrenderer/types.ts +127 -0
- package/source/legacy-components/input/index.tsx +142 -0
- package/source/legacy-components/input/types.ts +10 -0
- package/source/legacy-components/materialInput/index.tsx +138 -0
- package/source/legacy-components/materialInput/types.ts +16 -0
- package/source/legacy-components/modal/index.tsx +152 -0
- package/source/legacy-components/modal/types.ts +19 -0
- package/source/legacy-components/multiselect/PortalDropDown.tsx +44 -0
- package/source/legacy-components/multiselect/index.tsx +269 -0
- package/source/legacy-components/multiselect/types.ts +26 -0
- package/source/legacy-components/navbar/AuthSection.tsx +98 -0
- package/source/legacy-components/navbar/MobileToggle.tsx +31 -0
- package/source/legacy-components/navbar/NavItemComponent.tsx +119 -0
- package/source/legacy-components/navbar/SearchBar.tsx +55 -0
- package/source/legacy-components/navbar/helperFunctions.ts +27 -0
- package/source/legacy-components/navbar/index.tsx +156 -0
- package/source/legacy-components/navbar/types.ts +69 -0
- package/source/legacy-components/select/PortalDropDown.tsx +59 -0
- package/source/legacy-components/select/index.tsx +249 -0
- package/source/legacy-components/select/types.ts +29 -0
- package/source/legacy-components/sidebar/components/MenuItem.tsx +101 -0
- package/source/legacy-components/sidebar/components/MenuSection.tsx +34 -0
- package/source/legacy-components/sidebar/components/SidebarFooter.tsx +50 -0
- package/source/legacy-components/sidebar/components/SidebarLogo.tsx +24 -0
- package/source/legacy-components/sidebar/components/SidebarMenu.tsx +29 -0
- package/source/legacy-components/sidebar/components/UserProfile.tsx +31 -0
- package/source/legacy-components/sidebar/hooks/useSidebarState.ts +41 -0
- package/source/legacy-components/sidebar/index.tsx +77 -0
- package/source/legacy-components/sidebar/type.ts +42 -0
- package/source/legacy-components/spinner/Stroke.tsx +89 -0
- package/source/legacy-components/spinner/index.tsx +54 -0
- package/source/legacy-components/spinner/types.ts +9 -0
- package/source/legacy-components/tabs/index.tsx +71 -0
- package/source/legacy-components/tabs/types.ts +16 -0
- package/source/legacy-components/textarea/index.tsx +123 -0
- package/source/legacy-components/textarea/types.ts +15 -0
- package/source/legacy-components/toast/ToastComponent.tsx +101 -0
- package/source/legacy-components/toast/ToastIcon.tsx +61 -0
- package/source/legacy-components/toast/index.tsx +42 -0
- package/source/legacy-components/toast/toastStore.ts +42 -0
- package/source/legacy-components/toast/types.ts +18 -0
- package/source/legacy-components/toast/useToast.ts +12 -0
- package/source/legacy-components/uploader/ProgressAnimation.tsx +50 -0
- package/source/legacy-components/uploader/UploadedFilePreview.tsx +130 -0
- package/source/legacy-components/uploader/index.tsx +311 -0
- package/source/legacy-components/uploader/types.ts +30 -0
- package/source/legacy-components/uploader/uploaderIcon.tsx +72 -0
- package/source/legacy-components/uploader/uploaderService.ts +134 -0
- package/source/legacy-components/useUploader/useUploader.tsx +310 -0
- package/source/legacy-components/usepaginateddata/usePaginatedData.ts +174 -0
- package/source/theme.ts +40 -0
- package/source/utils.ts +10 -0
- package/tools/env.cjs +61 -0
- package/tools/passport/cli.cjs +79 -0
- package/tools/passport/extract.cjs +493 -0
- package/tools/passport/index.cjs +185 -0
- package/tools/passport/merge.cjs +131 -0
- package/tools/passport/policy.cjs +65 -0
- package/tools/passport/validate.cjs +277 -0
- package/tools/ts-require.cjs +79 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Textarea styles.
|
|
3
|
+
*
|
|
4
|
+
* Tokens read the shared --gbs-* variables when they are set on an ancestor
|
|
5
|
+
* (set them on :root to theme every component at once) and fall back to the
|
|
6
|
+
* same default palette otherwise. Rules are unlayered: they beat a framework
|
|
7
|
+
* reset on specificity, and lose to anything you pass through `className`.
|
|
8
|
+
* With Tailwind v4, import this file after the framework and inside its
|
|
9
|
+
* components layer - see the README.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
.ta-root {
|
|
13
|
+
--ta-fg: var(--gbs-fg, var(--dg-fg, light-dark(#18181b, #f4f4f5)));
|
|
14
|
+
--ta-muted: var(--gbs-muted, var(--dg-muted, light-dark(#71717a, #a1a1aa)));
|
|
15
|
+
--ta-border: var(--gbs-border, var(--dg-border, light-dark(#e4e4e7, #27272a)));
|
|
16
|
+
--ta-input-bg: var(--gbs-input-bg, var(--dg-input-bg, light-dark(#ffffff, #121216)));
|
|
17
|
+
--ta-readonly-bg: var(--gbs-readonly-bg, light-dark(#fafafa, #0e0e12));
|
|
18
|
+
--ta-focus: var(--gbs-focus, var(--dg-focus, light-dark(#2563eb, #60a5fa)));
|
|
19
|
+
--ta-danger: var(--gbs-danger, var(--dg-danger, light-dark(#dc2626, #f87171)));
|
|
20
|
+
--ta-radius: var(--gbs-radius, var(--dg-radius, 8px));
|
|
21
|
+
--ta-font-size: var(--gbs-font-size, var(--dg-font-size, 13px));
|
|
22
|
+
--ta-px: 10px;
|
|
23
|
+
--ta-py: 8px;
|
|
24
|
+
|
|
25
|
+
color-scheme: inherit;
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-direction: column;
|
|
28
|
+
gap: 4px;
|
|
29
|
+
width: 100%;
|
|
30
|
+
min-width: 0;
|
|
31
|
+
font-size: var(--ta-font-size);
|
|
32
|
+
line-height: 1.4;
|
|
33
|
+
color: var(--ta-fg);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
:where(.dark, [data-theme="dark"]) .ta-root {
|
|
37
|
+
color-scheme: dark;
|
|
38
|
+
}
|
|
39
|
+
:where(.light, [data-theme="light"]) .ta-root {
|
|
40
|
+
color-scheme: light;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
:where(.ta-root) *,
|
|
44
|
+
:where(.ta-root) *::before,
|
|
45
|
+
:where(.ta-root) *::after {
|
|
46
|
+
box-sizing: border-box;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.ta-root[data-size="sm"] {
|
|
50
|
+
--ta-font-size: 12px;
|
|
51
|
+
--ta-px: 8px;
|
|
52
|
+
--ta-py: 6px;
|
|
53
|
+
}
|
|
54
|
+
.ta-root[data-size="lg"] {
|
|
55
|
+
--ta-font-size: 14px;
|
|
56
|
+
--ta-px: 12px;
|
|
57
|
+
--ta-py: 10px;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.ta-label {
|
|
61
|
+
font-weight: 500;
|
|
62
|
+
}
|
|
63
|
+
.ta-label[data-required]::after {
|
|
64
|
+
content: " *";
|
|
65
|
+
color: var(--ta-danger);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.ta-input {
|
|
69
|
+
display: block;
|
|
70
|
+
width: 100%;
|
|
71
|
+
min-height: calc(1lh + 2 * var(--ta-py) + 2px);
|
|
72
|
+
margin: 0;
|
|
73
|
+
padding: var(--ta-py) var(--ta-px);
|
|
74
|
+
border: 1px solid var(--ta-border);
|
|
75
|
+
border-radius: var(--ta-radius);
|
|
76
|
+
background: var(--ta-input-bg);
|
|
77
|
+
color: inherit;
|
|
78
|
+
font: inherit;
|
|
79
|
+
line-height: 1.5;
|
|
80
|
+
}
|
|
81
|
+
.ta-input::placeholder {
|
|
82
|
+
color: var(--ta-muted);
|
|
83
|
+
}
|
|
84
|
+
.ta-input:hover:not(:disabled) {
|
|
85
|
+
border-color: color-mix(in oklab, var(--ta-border), var(--ta-fg) 25%);
|
|
86
|
+
}
|
|
87
|
+
.ta-input:focus-visible {
|
|
88
|
+
outline: 2px solid var(--ta-focus);
|
|
89
|
+
outline-offset: -1px;
|
|
90
|
+
}
|
|
91
|
+
.ta-input[data-invalid] {
|
|
92
|
+
border-color: var(--ta-danger);
|
|
93
|
+
outline-color: var(--ta-danger);
|
|
94
|
+
}
|
|
95
|
+
.ta-input:read-only {
|
|
96
|
+
background: var(--ta-readonly-bg);
|
|
97
|
+
}
|
|
98
|
+
.ta-input:disabled {
|
|
99
|
+
opacity: 0.55;
|
|
100
|
+
cursor: not-allowed;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.ta-input[data-resize="none"] {
|
|
104
|
+
resize: none;
|
|
105
|
+
}
|
|
106
|
+
.ta-input[data-resize="vertical"] {
|
|
107
|
+
resize: vertical;
|
|
108
|
+
}
|
|
109
|
+
.ta-input[data-resize="horizontal"] {
|
|
110
|
+
resize: horizontal;
|
|
111
|
+
}
|
|
112
|
+
.ta-input[data-resize="both"] {
|
|
113
|
+
resize: both;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/*
|
|
117
|
+
* Auto-resize in CSS where supported. The row limits come from the
|
|
118
|
+
* component; `--ta-max-rows: none` makes max-height invalid, which means no limit.
|
|
119
|
+
*/
|
|
120
|
+
@supports (field-sizing: content) {
|
|
121
|
+
.ta-input[data-autoresize] {
|
|
122
|
+
field-sizing: content;
|
|
123
|
+
min-height: calc(var(--ta-min-rows, 3) * 1lh + 2 * var(--ta-py) + 2px);
|
|
124
|
+
max-height: calc(var(--ta-max-rows) * 1lh + 2 * var(--ta-py) + 2px);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.ta-footer {
|
|
129
|
+
display: flex;
|
|
130
|
+
align-items: baseline;
|
|
131
|
+
gap: 8px;
|
|
132
|
+
}
|
|
133
|
+
.ta-description {
|
|
134
|
+
flex: 1 1 auto;
|
|
135
|
+
min-width: 0;
|
|
136
|
+
color: var(--ta-muted);
|
|
137
|
+
font-size: 0.92em;
|
|
138
|
+
}
|
|
139
|
+
.ta-count {
|
|
140
|
+
flex: none;
|
|
141
|
+
margin-inline-start: auto;
|
|
142
|
+
color: var(--ta-muted);
|
|
143
|
+
font-size: 0.92em;
|
|
144
|
+
font-variant-numeric: tabular-nums;
|
|
145
|
+
}
|
|
146
|
+
.ta-count[data-over] {
|
|
147
|
+
color: var(--ta-danger);
|
|
148
|
+
}
|
|
149
|
+
.ta-error {
|
|
150
|
+
color: var(--ta-danger);
|
|
151
|
+
font-size: 0.92em;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
@media (forced-colors: active) {
|
|
155
|
+
.ta-input:focus-visible {
|
|
156
|
+
outline-color: Highlight;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Toaster
|
|
2
|
+
|
|
3
|
+
Notifications for any React 19 app — Vite, Next.js, Remix or plain React — styled
|
|
4
|
+
to match the DataGrid, Combobox and DatePicker. No runtime dependencies besides
|
|
5
|
+
React.
|
|
6
|
+
|
|
7
|
+
- **`<Toaster />`** — mount once; renders the toasts.
|
|
8
|
+
- **`toast()`** — call from anywhere: components, event handlers, data layers,
|
|
9
|
+
even code outside React. No hook, context or provider.
|
|
10
|
+
|
|
11
|
+
## Setup
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { toast, Toaster } from "@/components/toaster";
|
|
15
|
+
import "@/components/toaster/styles.css";
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
// Once, near the root of the app (e.g. app/layout.tsx or App.tsx)
|
|
20
|
+
<Toaster />
|
|
21
|
+
|
|
22
|
+
// Anywhere
|
|
23
|
+
toast.success("Settings saved");
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Import from one path everywhere. `toast()` and `<Toaster />` meet through a
|
|
27
|
+
shared store, and a bundler that sees two import paths may create two stores.
|
|
28
|
+
|
|
29
|
+
## Showing toasts
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
toast("Event created", { description: "Monday, 10:00" });
|
|
33
|
+
toast.success("Saved");
|
|
34
|
+
toast.error("Upload failed", { action: { label: "Retry", onClick: retry } });
|
|
35
|
+
toast.warning("Storage almost full");
|
|
36
|
+
toast.info("New version available");
|
|
37
|
+
const id = toast.loading("Uploading…"); // stays until updated
|
|
38
|
+
|
|
39
|
+
toast.update(id, { type: "success", title: "Uploaded" });
|
|
40
|
+
toast.dismiss(id); // or toast.dismiss() for all
|
|
41
|
+
|
|
42
|
+
toast.promise(saveUser(data), {
|
|
43
|
+
loading: "Saving…",
|
|
44
|
+
success: (user) => `${user.name} saved`,
|
|
45
|
+
error: (error) => `Could not save: ${(error as Error).message}`,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
toast.custom(({ dismiss }) => <MyCard onClose={dismiss} />);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Every call returns the toast's id. Passing an `id` that is already on screen
|
|
52
|
+
updates that toast instead of adding another.
|
|
53
|
+
|
|
54
|
+
## Toast options
|
|
55
|
+
|
|
56
|
+
| Option | Type | Default | Description |
|
|
57
|
+
| --- | --- | --- | --- |
|
|
58
|
+
| `id` | `string` | generated | Reuse to update a toast in place. |
|
|
59
|
+
| `type` | `"default"` \| `"success"` \| `"error"` \| `"warning"` \| `"info"` \| `"loading"` | `"default"` | Icon, color bar and announcement. |
|
|
60
|
+
| `description` | `ReactNode` | — | Second line. |
|
|
61
|
+
| `duration` | `number` | `5000` (loading: stays) | Milliseconds. `0` or `Infinity` stays until dismissed. |
|
|
62
|
+
| `dismissible` | `boolean` | `true` | Close button, Escape and swipe. |
|
|
63
|
+
| `action` / `cancel` | `{ label, onClick(event) }` | — | Buttons. The toast closes after a click unless `event.preventDefault()` is called. |
|
|
64
|
+
| `icon` | `ReactNode` | the type's icon | `null` hides it. |
|
|
65
|
+
| `className` | `string` | — | Class for this toast. |
|
|
66
|
+
| `render` | `({ id, dismiss }) => ReactNode` | — | Custom body (`toast.custom` sets it). |
|
|
67
|
+
| `onDismiss` / `onAutoClose` | `(toast) => void` | — | Closed by someone, or because time ran out. |
|
|
68
|
+
|
|
69
|
+
## Toaster props
|
|
70
|
+
|
|
71
|
+
| Prop | Type | Default | Description |
|
|
72
|
+
| --- | --- | --- | --- |
|
|
73
|
+
| `position` | `"top-left"` \| `"top-center"` \| `"top-right"` \| `"bottom-left"` \| `"bottom-center"` \| `"bottom-right"` | `"top-right"` | Where the stack sits. Full width on narrow screens. |
|
|
74
|
+
| `limit` | `number` | `3` | Toasts on screen at once. The rest wait, with their timers held. |
|
|
75
|
+
| `duration` | `number` | `5000` | Default auto-close delay. |
|
|
76
|
+
| `closeButton` | `boolean` | `true` | Close button on dismissible toasts. |
|
|
77
|
+
| `hotkey` | `string[]` | `["altKey", "KeyT"]` | Moves focus to the notifications. `[]` turns it off. |
|
|
78
|
+
| `icons` | `Partial<Record<ToastType, ReactNode>>` | — | Replace icons per type; `null` hides one. |
|
|
79
|
+
| `store` | `ToastStore` | the `toast()` store | For a separate notification area. |
|
|
80
|
+
| `dir` | `"ltr"` \| `"rtl"` \| `"auto"` | inherited | Text direction. |
|
|
81
|
+
| `className`, `classNames`, `style` | — | — | Slots: `region`, `list`, `toast`, `icon`, `content`, `title`, `description`, `actions`, `action`, `cancel`, `close`. |
|
|
82
|
+
| `localeText` | `Partial<ToasterLocaleText>` | English | `regionLabel(hotkey)`, `close`. |
|
|
83
|
+
|
|
84
|
+
## Behavior
|
|
85
|
+
|
|
86
|
+
- Timers pause while the pointer is over the toasts, while focus is inside them,
|
|
87
|
+
and while the browser tab is hidden.
|
|
88
|
+
- Toasts beyond `limit` wait with their full time and appear as others close.
|
|
89
|
+
- Toasts render in a portal on `<body>`, so no parent's overflow, transform or
|
|
90
|
+
z-index can hide them.
|
|
91
|
+
|
|
92
|
+
## Keyboard and accessibility
|
|
93
|
+
|
|
94
|
+
| Keys | Action |
|
|
95
|
+
| --- | --- |
|
|
96
|
+
| **Alt + T** | Move focus to the notifications (configurable with `hotkey`). |
|
|
97
|
+
| **Tab** | Move between toasts and their buttons. |
|
|
98
|
+
| **Escape** | Dismiss the focused toast. |
|
|
99
|
+
| swipe sideways | Dismiss (touch, pen or mouse). |
|
|
100
|
+
|
|
101
|
+
The list is a polite live region, always in the page, so new toasts are read out
|
|
102
|
+
without interrupting. Error toasts use `role="alert"` and interrupt. The region is
|
|
103
|
+
a labelled landmark ("Notifications (Alt+T)").
|
|
104
|
+
|
|
105
|
+
## Theming
|
|
106
|
+
|
|
107
|
+
Override `--ts-*` variables (they default to the grid's `--dg-*`). Toasts render
|
|
108
|
+
on `<body>`, so set shared `--dg-*` values on `:root`:
|
|
109
|
+
|
|
110
|
+
```css
|
|
111
|
+
.ts-region { --ts-width: 420px; --ts-success: #059669; --ts-radius: 12px; }
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
State attributes: `data-position` on the region; `data-type`, `data-state`
|
|
115
|
+
(`open` / `closing`), `data-custom`, `data-swiping` on toasts.
|
|
116
|
+
|
|
117
|
+
## Headless use
|
|
118
|
+
|
|
119
|
+
`useToasts(store?)` returns the live snapshot (`toasts`, `paused`, `limit`) for a
|
|
120
|
+
custom UI, and `visibleToasts(snapshot)` picks the ones to show.
|
|
121
|
+
`createToastStore()` and `createToastApi(store)` build separate instances. All of
|
|
122
|
+
`core` runs without React.
|
|
123
|
+
|
|
124
|
+
## Known limits
|
|
125
|
+
|
|
126
|
+
- One stack per `<Toaster />`; toasts don't collapse into an expandable pile.
|
|
127
|
+
- `toast()` does nothing during a server render or in server code. Call it in
|
|
128
|
+
the browser, e.g. after a Server Action resolves.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { ToastPatch, ToastStore } from "./store";
|
|
3
|
+
import type { ToastOptions, ToastRenderProps, ToastType } from "./types";
|
|
4
|
+
|
|
5
|
+
type TypedOptions = Omit<ToastOptions, "type">;
|
|
6
|
+
type Message<T> = ReactNode | ((value: T) => ReactNode);
|
|
7
|
+
|
|
8
|
+
export interface PromiseMessages<T> {
|
|
9
|
+
loading: ReactNode;
|
|
10
|
+
/** Omit to close the toast quietly on success. */
|
|
11
|
+
success?: Message<T>;
|
|
12
|
+
/** Omit to close the toast quietly on failure. */
|
|
13
|
+
error?: Message<unknown>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ToastApi {
|
|
17
|
+
(title: ReactNode, options?: ToastOptions): string;
|
|
18
|
+
success(title: ReactNode, options?: TypedOptions): string;
|
|
19
|
+
error(title: ReactNode, options?: TypedOptions): string;
|
|
20
|
+
warning(title: ReactNode, options?: TypedOptions): string;
|
|
21
|
+
info(title: ReactNode, options?: TypedOptions): string;
|
|
22
|
+
/** Stays until updated or dismissed. */
|
|
23
|
+
loading(title: ReactNode, options?: TypedOptions): string;
|
|
24
|
+
custom(
|
|
25
|
+
render: (props: ToastRenderProps) => ReactNode,
|
|
26
|
+
options?: Omit<TypedOptions, "render">,
|
|
27
|
+
): string;
|
|
28
|
+
/** Shows a loading toast, then turns it into a success or error. Returns the same promise. */
|
|
29
|
+
promise<T>(
|
|
30
|
+
input: Promise<T> | (() => Promise<T>),
|
|
31
|
+
messages: PromiseMessages<T>,
|
|
32
|
+
options?: TypedOptions,
|
|
33
|
+
): Promise<T>;
|
|
34
|
+
update(id: string, patch: ToastPatch): void;
|
|
35
|
+
dismiss(id?: string): void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The `toast()` function for a store. `canShow` lets the default instance ignore
|
|
40
|
+
* calls made where nobody can see them, such as during a server render.
|
|
41
|
+
*/
|
|
42
|
+
export function createToastApi(store: ToastStore, canShow: () => boolean = () => true): ToastApi {
|
|
43
|
+
const show = (title: ReactNode, options?: ToastOptions) =>
|
|
44
|
+
canShow() ? store.show(title, options) : (options?.id ?? "");
|
|
45
|
+
|
|
46
|
+
const typed = (type: ToastType) => (title: ReactNode, options?: TypedOptions) =>
|
|
47
|
+
show(title, { ...options, type });
|
|
48
|
+
|
|
49
|
+
function promise<T>(
|
|
50
|
+
input: Promise<T> | (() => Promise<T>),
|
|
51
|
+
messages: PromiseMessages<T>,
|
|
52
|
+
options?: TypedOptions,
|
|
53
|
+
): Promise<T> {
|
|
54
|
+
const pending = typeof input === "function" ? input() : input;
|
|
55
|
+
if (!canShow()) return pending;
|
|
56
|
+
|
|
57
|
+
const id = store.show(messages.loading, { ...options, type: "loading" });
|
|
58
|
+
|
|
59
|
+
function settle<V>(type: "success" | "error", message: Message<V> | undefined, value: V) {
|
|
60
|
+
if (message === undefined) store.dismiss(id);
|
|
61
|
+
else store.update(id, { type, title: typeof message === "function" ? message(value) : message });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Handling the rejection here also stops it being reported as unhandled when
|
|
65
|
+
// the caller doesn't await the returned promise.
|
|
66
|
+
pending.then(
|
|
67
|
+
(value) => settle("success", messages.success, value),
|
|
68
|
+
(error: unknown) => settle("error", messages.error, error),
|
|
69
|
+
);
|
|
70
|
+
return pending;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return Object.assign(show, {
|
|
74
|
+
success: typed("success"),
|
|
75
|
+
error: typed("error"),
|
|
76
|
+
warning: typed("warning"),
|
|
77
|
+
info: typed("info"),
|
|
78
|
+
loading: typed("loading"),
|
|
79
|
+
custom: (
|
|
80
|
+
render: (props: ToastRenderProps) => ReactNode,
|
|
81
|
+
options?: Omit<TypedOptions, "render">,
|
|
82
|
+
) => show(null, { ...options, render }),
|
|
83
|
+
promise,
|
|
84
|
+
update: (id: string, patch: ToastPatch) => store.update(id, patch),
|
|
85
|
+
dismiss: (id?: string) => store.dismiss(id),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Framework-free: the store, the timers and the `toast()` API run without
|
|
2
|
+
// React, so they can be called from a data layer and tested on their own.
|
|
3
|
+
export { createToastApi } from "./api";
|
|
4
|
+
export type { PromiseMessages, ToastApi } from "./api";
|
|
5
|
+
export {
|
|
6
|
+
createToastStore,
|
|
7
|
+
DEFAULT_DURATION,
|
|
8
|
+
DEFAULT_LIMIT,
|
|
9
|
+
EXIT_DURATION,
|
|
10
|
+
visibleToasts,
|
|
11
|
+
} from "./store";
|
|
12
|
+
export type { ToastPatch, ToastStore, ToastStoreConfig } from "./store";
|
|
13
|
+
export { toast, toastStore } from "./toast";
|
|
14
|
+
export type * from "./types";
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { ToastOptions, ToastRecord, ToastSnapshot } from "./types";
|
|
3
|
+
|
|
4
|
+
export const DEFAULT_DURATION = 5000;
|
|
5
|
+
export const DEFAULT_LIMIT = 3;
|
|
6
|
+
/** Matches the CSS exit transition, so a toast fades out before it leaves the DOM. */
|
|
7
|
+
export const EXIT_DURATION = 200;
|
|
8
|
+
|
|
9
|
+
export interface ToastStoreConfig {
|
|
10
|
+
/** Default auto-close delay in milliseconds. */
|
|
11
|
+
duration?: number;
|
|
12
|
+
/** Open toasts on screen at once; the rest wait with their timers held. */
|
|
13
|
+
limit?: number;
|
|
14
|
+
/** How long a closed toast stays for its exit animation. */
|
|
15
|
+
exitDuration?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type ToastPatch = Omit<ToastOptions, "id"> & { title?: ReactNode };
|
|
19
|
+
|
|
20
|
+
export interface ToastStore {
|
|
21
|
+
subscribe(listener: () => void): () => void;
|
|
22
|
+
getSnapshot(): ToastSnapshot;
|
|
23
|
+
getServerSnapshot(): ToastSnapshot;
|
|
24
|
+
/** Adds a toast, or updates the one with the same `id`. Returns the id. */
|
|
25
|
+
show(title: ReactNode, options?: ToastOptions): string;
|
|
26
|
+
/** Changes an open toast. A new `type` without a `duration` gets that type's default. */
|
|
27
|
+
update(id: string, patch: ToastPatch): void;
|
|
28
|
+
/** Closes one toast, or every toast when no id is given. */
|
|
29
|
+
dismiss(id?: string): void;
|
|
30
|
+
pause(): void;
|
|
31
|
+
resume(): void;
|
|
32
|
+
configure(config: ToastStoreConfig): void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface Countdown {
|
|
36
|
+
remaining: number;
|
|
37
|
+
startedAt: number;
|
|
38
|
+
handle: ReturnType<typeof setTimeout> | null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const EMPTY: ToastSnapshot = { toasts: [], paused: false, limit: DEFAULT_LIMIT };
|
|
42
|
+
|
|
43
|
+
/** What a Toaster renders: every closing toast, plus the newest `limit` open ones. */
|
|
44
|
+
export function visibleToasts(snapshot: ToastSnapshot): ToastRecord[] {
|
|
45
|
+
let open = 0;
|
|
46
|
+
return snapshot.toasts.filter((toast) => toast.state !== "open" || open++ < snapshot.limit);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function definedOnly<T extends object>(value: T): Partial<T> {
|
|
50
|
+
return Object.fromEntries(
|
|
51
|
+
Object.entries(value).filter(([, entry]) => entry !== undefined),
|
|
52
|
+
) as Partial<T>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Toast state and timers, with no React. Snapshots are immutable, so the store
|
|
57
|
+
* plugs straight into `useSyncExternalStore`. Only toasts on screen count down:
|
|
58
|
+
* one waiting beyond `limit` keeps its full time until it gets a slot.
|
|
59
|
+
*/
|
|
60
|
+
export function createToastStore(config: ToastStoreConfig = {}): ToastStore {
|
|
61
|
+
let settings: Required<ToastStoreConfig> = {
|
|
62
|
+
duration: DEFAULT_DURATION,
|
|
63
|
+
limit: DEFAULT_LIMIT,
|
|
64
|
+
exitDuration: EXIT_DURATION,
|
|
65
|
+
...definedOnly(config),
|
|
66
|
+
};
|
|
67
|
+
let snapshot: ToastSnapshot = { ...EMPTY, limit: settings.limit };
|
|
68
|
+
let counter = 0;
|
|
69
|
+
const listeners = new Set<() => void>();
|
|
70
|
+
const countdowns = new Map<string, Countdown>();
|
|
71
|
+
const removals = new Map<string, ReturnType<typeof setTimeout>>();
|
|
72
|
+
|
|
73
|
+
function stop(id: string) {
|
|
74
|
+
const countdown = countdowns.get(id);
|
|
75
|
+
if (!countdown?.handle) return;
|
|
76
|
+
clearTimeout(countdown.handle);
|
|
77
|
+
countdown.handle = null;
|
|
78
|
+
countdown.remaining = Math.max(0, countdown.remaining - (Date.now() - countdown.startedAt));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function syncTimers() {
|
|
82
|
+
const visible = new Set(visibleToasts(snapshot));
|
|
83
|
+
for (const toast of snapshot.toasts) {
|
|
84
|
+
const countdown = countdowns.get(toast.id);
|
|
85
|
+
if (!countdown || toast.state !== "open") continue;
|
|
86
|
+
const run =
|
|
87
|
+
visible.has(toast) && !snapshot.paused && Number.isFinite(countdown.remaining);
|
|
88
|
+
if (run && !countdown.handle) {
|
|
89
|
+
countdown.startedAt = Date.now();
|
|
90
|
+
countdown.handle = setTimeout(() => close(toast.id, true), countdown.remaining);
|
|
91
|
+
} else if (!run && countdown.handle) {
|
|
92
|
+
stop(toast.id);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function commit(
|
|
98
|
+
toasts: readonly ToastRecord[],
|
|
99
|
+
changes: Partial<Omit<ToastSnapshot, "toasts">> = {},
|
|
100
|
+
) {
|
|
101
|
+
snapshot = { ...snapshot, ...changes, toasts };
|
|
102
|
+
syncTimers();
|
|
103
|
+
for (const listener of listeners) listener();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function close(id: string, auto: boolean) {
|
|
107
|
+
const toast = snapshot.toasts.find((item) => item.id === id && item.state === "open");
|
|
108
|
+
if (!toast) return;
|
|
109
|
+
stop(id);
|
|
110
|
+
countdowns.delete(id);
|
|
111
|
+
|
|
112
|
+
const closing: ToastRecord = { ...toast, state: "closing" };
|
|
113
|
+
commit(snapshot.toasts.map((item) => (item === toast ? closing : item)));
|
|
114
|
+
if (auto) closing.onAutoClose?.(closing);
|
|
115
|
+
else closing.onDismiss?.(closing);
|
|
116
|
+
|
|
117
|
+
removals.set(
|
|
118
|
+
id,
|
|
119
|
+
setTimeout(() => {
|
|
120
|
+
removals.delete(id);
|
|
121
|
+
commit(snapshot.toasts.filter((item) => item !== closing));
|
|
122
|
+
}, settings.exitDuration),
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function show(title: ReactNode, options: ToastOptions = {}): string {
|
|
127
|
+
const id = options.id ?? `toast-${++counter}`;
|
|
128
|
+
const type = options.type ?? "default";
|
|
129
|
+
const existing = snapshot.toasts.find((item) => item.id === id);
|
|
130
|
+
const reused = existing?.state === "open" ? existing : undefined;
|
|
131
|
+
const requested = options.duration ?? (type === "loading" ? Infinity : settings.duration);
|
|
132
|
+
|
|
133
|
+
const record: ToastRecord = {
|
|
134
|
+
...options,
|
|
135
|
+
id,
|
|
136
|
+
type,
|
|
137
|
+
title,
|
|
138
|
+
// 0, negative and NaN all mean "stay until dismissed".
|
|
139
|
+
duration: requested > 0 ? requested : Infinity,
|
|
140
|
+
dismissible: options.dismissible ?? true,
|
|
141
|
+
state: "open",
|
|
142
|
+
createdAt: reused?.createdAt ?? Date.now(),
|
|
143
|
+
version: (existing?.version ?? 0) + 1,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
// Showing an id that is fading out brings it back instead of stacking a copy.
|
|
147
|
+
const removal = removals.get(id);
|
|
148
|
+
if (removal !== undefined) {
|
|
149
|
+
clearTimeout(removal);
|
|
150
|
+
removals.delete(id);
|
|
151
|
+
}
|
|
152
|
+
stop(id);
|
|
153
|
+
countdowns.set(id, { remaining: record.duration, startedAt: 0, handle: null });
|
|
154
|
+
|
|
155
|
+
commit(
|
|
156
|
+
reused
|
|
157
|
+
? snapshot.toasts.map((item) => (item === reused ? record : item))
|
|
158
|
+
: [record, ...snapshot.toasts.filter((item) => item.id !== id)],
|
|
159
|
+
);
|
|
160
|
+
return id;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function update(id: string, patch: ToastPatch) {
|
|
164
|
+
const existing = snapshot.toasts.find((item) => item.id === id && item.state === "open");
|
|
165
|
+
if (!existing) return;
|
|
166
|
+
const { title = existing.title, ...changes } = patch;
|
|
167
|
+
const typeChanged = changes.type !== undefined && changes.type !== existing.type;
|
|
168
|
+
show(title, {
|
|
169
|
+
...existing,
|
|
170
|
+
...changes,
|
|
171
|
+
id,
|
|
172
|
+
duration: changes.duration ?? (typeChanged ? undefined : existing.duration),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function dismiss(id?: string) {
|
|
177
|
+
if (id !== undefined) {
|
|
178
|
+
close(id, false);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
for (const toast of snapshot.toasts) {
|
|
182
|
+
if (toast.state === "open") close(toast.id, false);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function setPaused(paused: boolean) {
|
|
187
|
+
if (snapshot.paused !== paused) commit(snapshot.toasts, { paused });
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function configure(next: ToastStoreConfig) {
|
|
191
|
+
settings = { ...settings, ...definedOnly(next) };
|
|
192
|
+
if (settings.limit !== snapshot.limit) commit(snapshot.toasts, { limit: settings.limit });
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
subscribe(listener) {
|
|
197
|
+
listeners.add(listener);
|
|
198
|
+
return () => {
|
|
199
|
+
listeners.delete(listener);
|
|
200
|
+
};
|
|
201
|
+
},
|
|
202
|
+
getSnapshot: () => snapshot,
|
|
203
|
+
getServerSnapshot: () => EMPTY,
|
|
204
|
+
show,
|
|
205
|
+
update,
|
|
206
|
+
dismiss,
|
|
207
|
+
pause: () => setPaused(true),
|
|
208
|
+
resume: () => setPaused(false),
|
|
209
|
+
configure,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createToastApi } from "./api";
|
|
2
|
+
import { createToastStore } from "./store";
|
|
3
|
+
|
|
4
|
+
/** The store behind `toast()`, and behind any `<Toaster />` without a `store` prop. */
|
|
5
|
+
export const toastStore = createToastStore();
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Shows a notification from anywhere: components, event handlers, data layers,
|
|
9
|
+
* code outside React. On a server nobody can see a toast, so calls do nothing
|
|
10
|
+
* there — which also keeps one request's toasts from leaking into another's.
|
|
11
|
+
*/
|
|
12
|
+
export const toast = createToastApi(toastStore, () => typeof document !== "undefined");
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
export type ToastType = "default" | "success" | "error" | "warning" | "info" | "loading";
|
|
4
|
+
|
|
5
|
+
export type ToastPosition =
|
|
6
|
+
| "top-left"
|
|
7
|
+
| "top-center"
|
|
8
|
+
| "top-right"
|
|
9
|
+
| "bottom-left"
|
|
10
|
+
| "bottom-center"
|
|
11
|
+
| "bottom-right";
|
|
12
|
+
|
|
13
|
+
/** The click event handed to an action. Call `preventDefault()` to keep the toast open. */
|
|
14
|
+
export interface ToastActionEvent {
|
|
15
|
+
preventDefault(): void;
|
|
16
|
+
readonly defaultPrevented: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ToastAction {
|
|
20
|
+
label: ReactNode;
|
|
21
|
+
onClick(event: ToastActionEvent): void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ToastRenderProps {
|
|
25
|
+
id: string;
|
|
26
|
+
dismiss(): void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ToastOptions {
|
|
30
|
+
/** Showing a toast with an id that is already on screen updates it in place. */
|
|
31
|
+
id?: string;
|
|
32
|
+
type?: ToastType;
|
|
33
|
+
description?: ReactNode;
|
|
34
|
+
/** Milliseconds before it closes. `0` or `Infinity` keeps it until dismissed. */
|
|
35
|
+
duration?: number;
|
|
36
|
+
/** Close button, Escape and swipe. Default true. */
|
|
37
|
+
dismissible?: boolean;
|
|
38
|
+
action?: ToastAction;
|
|
39
|
+
cancel?: ToastAction;
|
|
40
|
+
/** Replaces the type's icon; `null` hides it. */
|
|
41
|
+
icon?: ReactNode;
|
|
42
|
+
className?: string;
|
|
43
|
+
/** Renders the whole toast body yourself. */
|
|
44
|
+
render?(props: ToastRenderProps): ReactNode;
|
|
45
|
+
/** Closed by the user or by code. */
|
|
46
|
+
onDismiss?(toast: ToastRecord): void;
|
|
47
|
+
/** Closed because its time ran out. */
|
|
48
|
+
onAutoClose?(toast: ToastRecord): void;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface ToastRecord
|
|
52
|
+
extends Omit<ToastOptions, "id" | "type" | "duration" | "dismissible"> {
|
|
53
|
+
id: string;
|
|
54
|
+
type: ToastType;
|
|
55
|
+
title: ReactNode;
|
|
56
|
+
/** Always a positive number; `Infinity` when it stays until dismissed. */
|
|
57
|
+
duration: number;
|
|
58
|
+
dismissible: boolean;
|
|
59
|
+
/** `closing` while the exit animation plays, just before removal. */
|
|
60
|
+
state: "open" | "closing";
|
|
61
|
+
createdAt: number;
|
|
62
|
+
/** Goes up each time the same id is shown or updated. */
|
|
63
|
+
version: number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface ToastSnapshot {
|
|
67
|
+
/** Newest first, including toasts waiting beyond `limit`. */
|
|
68
|
+
toasts: readonly ToastRecord[];
|
|
69
|
+
/** Timers are held, e.g. while the pointer is over the toasts. */
|
|
70
|
+
paused: boolean;
|
|
71
|
+
/** How many open toasts are on screen at once. */
|
|
72
|
+
limit: number;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface ToasterLocaleText {
|
|
76
|
+
regionLabel(hotkey: string): string;
|
|
77
|
+
close: string;
|
|
78
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { Toaster } from "./react/Toaster";
|
|
2
|
+
export type { ToasterProps } from "./react/Toaster";
|
|
3
|
+
export { useToasts } from "./react/useToasts";
|
|
4
|
+
export { defaultToasterText } from "./react/locale";
|
|
5
|
+
export type { ToasterSlot } from "./react/props";
|
|
6
|
+
export * from "./core";
|