@eifi1/ui-kit 0.4.1 → 0.5.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/LICENSE +32 -0
- package/README.md +498 -57
- package/dist/chart.d.ts +10 -0
- package/dist/chart.js +10 -0
- package/dist/chart.js.map +1 -0
- package/dist/components/account-settings.d.ts +11 -0
- package/dist/components/account-settings.js +19 -12
- package/dist/components/account-settings.js.map +1 -1
- package/dist/components/alert-banner.d.ts +10 -6
- package/dist/components/alert-banner.js +20 -16
- package/dist/components/alert-banner.js.map +1 -1
- package/dist/components/amount-input.d.ts +30 -4
- package/dist/components/amount-input.js +36 -14
- package/dist/components/amount-input.js.map +1 -1
- package/dist/components/calculator.d.ts +24 -7
- package/dist/components/calculator.js +37 -20
- package/dist/components/calculator.js.map +1 -1
- package/dist/components/chart-zoom.d.ts +156 -0
- package/dist/components/chart-zoom.js +262 -0
- package/dist/components/chart-zoom.js.map +1 -0
- package/dist/components/chart.d.ts +28 -3
- package/dist/components/chart.js +71 -32
- package/dist/components/chart.js.map +1 -1
- package/dist/components/checkbox.d.ts +51 -0
- package/dist/components/checkbox.js +123 -0
- package/dist/components/checkbox.js.map +1 -0
- package/dist/components/chip.d.ts +128 -0
- package/dist/components/chip.js +357 -0
- package/dist/components/chip.js.map +1 -0
- package/dist/components/choice-parts.d.ts +24 -0
- package/dist/components/choice-parts.js +18 -0
- package/dist/components/choice-parts.js.map +1 -0
- package/dist/components/combobox-core.d.ts +52 -9
- package/dist/components/combobox-core.js +89 -19
- package/dist/components/combobox-core.js.map +1 -1
- package/dist/components/combobox.d.ts +43 -33
- package/dist/components/combobox.js +409 -340
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/currency-select.d.ts +48 -8
- package/dist/components/currency-select.js +107 -65
- package/dist/components/currency-select.js.map +1 -1
- package/dist/components/data-table-filter-popover.d.ts +15 -3
- package/dist/components/data-table-filter-popover.js +23 -17
- package/dist/components/data-table-filter-popover.js.map +1 -1
- package/dist/components/data-table-filters.d.ts +1 -1
- package/dist/components/data-table-filters.js +3 -2
- package/dist/components/data-table-filters.js.map +1 -1
- package/dist/components/data-table-labels.d.ts +53 -2
- package/dist/components/data-table-labels.js +31 -2
- package/dist/components/data-table-labels.js.map +1 -1
- package/dist/components/data-table-pagination.d.ts +3 -1
- package/dist/components/data-table-pagination.js +16 -10
- package/dist/components/data-table-pagination.js.map +1 -1
- package/dist/components/data-table-sort.js +1 -0
- package/dist/components/data-table-sort.js.map +1 -1
- package/dist/components/data-table.d.ts +1 -1
- package/dist/components/data-table.js +201 -85
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/date-picker.d.ts +25 -9
- package/dist/components/date-picker.js +232 -94
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/dropdown.d.ts +74 -19
- package/dist/components/dropdown.js +72 -22
- package/dist/components/dropdown.js.map +1 -1
- package/dist/components/entity-combobox.d.ts +5 -4
- package/dist/components/entity-combobox.js +107 -76
- package/dist/components/entity-combobox.js.map +1 -1
- package/dist/components/facing-pair.d.ts +36 -0
- package/dist/components/facing-pair.js +40 -0
- package/dist/components/facing-pair.js.map +1 -0
- package/dist/components/field-sync.d.ts +164 -0
- package/dist/components/field-sync.js +264 -0
- package/dist/components/field-sync.js.map +1 -0
- package/dist/components/file-dropzone.d.ts +21 -7
- package/dist/components/file-dropzone.js +28 -7
- package/dist/components/file-dropzone.js.map +1 -1
- package/dist/components/full-bleed-dialog.d.ts +37 -26
- package/dist/components/full-bleed-dialog.js +20 -5
- package/dist/components/full-bleed-dialog.js.map +1 -1
- package/dist/components/grouped-picker.d.ts +21 -10
- package/dist/components/grouped-picker.js +66 -52
- package/dist/components/grouped-picker.js.map +1 -1
- package/dist/components/hover-menu.d.ts +20 -4
- package/dist/components/hover-menu.js +15 -4
- package/dist/components/hover-menu.js.map +1 -1
- package/dist/components/mini-calendar.d.ts +102 -14
- package/dist/components/mini-calendar.js +256 -70
- package/dist/components/mini-calendar.js.map +1 -1
- package/dist/components/modal.d.ts +9 -3
- package/dist/components/modal.js +34 -45
- package/dist/components/modal.js.map +1 -1
- package/dist/components/month-picker.d.ts +79 -0
- package/dist/components/month-picker.js +342 -0
- package/dist/components/month-picker.js.map +1 -0
- package/dist/components/multi-entity-combobox.d.ts +7 -6
- package/dist/components/multi-entity-combobox.js +112 -72
- package/dist/components/multi-entity-combobox.js.map +1 -1
- package/dist/components/multi-select.d.ts +13 -11
- package/dist/components/multi-select.js +161 -84
- package/dist/components/multi-select.js.map +1 -1
- package/dist/components/number-field.d.ts +92 -0
- package/dist/components/number-field.js +111 -0
- package/dist/components/number-field.js.map +1 -0
- package/dist/components/number-input.d.ts +21 -2
- package/dist/components/number-input.js +2 -1
- package/dist/components/number-input.js.map +1 -1
- package/dist/components/numpad-sheet.d.ts +25 -4
- package/dist/components/numpad-sheet.js +30 -13
- package/dist/components/numpad-sheet.js.map +1 -1
- package/dist/components/page-contents.d.ts +90 -0
- package/dist/components/page-contents.js +170 -0
- package/dist/components/page-contents.js.map +1 -0
- package/dist/components/password-strength.d.ts +106 -0
- package/dist/components/password-strength.js +132 -0
- package/dist/components/password-strength.js.map +1 -0
- package/dist/components/picker-sheet.d.ts +23 -15
- package/dist/components/picker-sheet.js +34 -14
- package/dist/components/picker-sheet.js.map +1 -1
- package/dist/components/popover.d.ts +62 -4
- package/dist/components/popover.js +52 -2
- package/dist/components/popover.js.map +1 -1
- package/dist/components/search-field.d.ts +25 -7
- package/dist/components/search-field.js +7 -5
- package/dist/components/search-field.js.map +1 -1
- package/dist/components/series-chart-labels.d.ts +14 -0
- package/dist/components/series-chart-labels.js +11 -0
- package/dist/components/series-chart-labels.js.map +1 -0
- package/dist/components/series-chart.d.ts +207 -0
- package/dist/components/series-chart.js +223 -0
- package/dist/components/series-chart.js.map +1 -0
- package/dist/components/settings-fields.d.ts +14 -4
- package/dist/components/settings-fields.js +19 -8
- package/dist/components/settings-fields.js.map +1 -1
- package/dist/components/signature-pad.d.ts +119 -0
- package/dist/components/signature-pad.js +405 -0
- package/dist/components/signature-pad.js.map +1 -0
- package/dist/components/slider.d.ts +80 -0
- package/dist/components/slider.js +155 -0
- package/dist/components/slider.js.map +1 -0
- package/dist/components/sparkline.d.ts +93 -0
- package/dist/components/sparkline.js +205 -0
- package/dist/components/sparkline.js.map +1 -0
- package/dist/components/stat-tile.d.ts +151 -0
- package/dist/components/stat-tile.js +236 -0
- package/dist/components/stat-tile.js.map +1 -0
- package/dist/components/swipeable-row.d.ts +25 -2
- package/dist/components/swipeable-row.js +31 -0
- package/dist/components/swipeable-row.js.map +1 -1
- package/dist/components/switch.d.ts +60 -0
- package/dist/components/switch.js +81 -0
- package/dist/components/switch.js.map +1 -0
- package/dist/components/time-input.d.ts +58 -0
- package/dist/components/time-input.js +50 -0
- package/dist/components/time-input.js.map +1 -0
- package/dist/components/toggle-group.d.ts +18 -3
- package/dist/components/toggle-group.js +10 -6
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/components/toggle-legend.d.ts +115 -0
- package/dist/components/toggle-legend.js +114 -0
- package/dist/components/toggle-legend.js.map +1 -0
- package/dist/components/tooltip.d.ts +40 -11
- package/dist/components/tooltip.js +77 -20
- package/dist/components/tooltip.js.map +1 -1
- package/dist/components/treemap.d.ts +132 -0
- package/dist/components/treemap.js +179 -0
- package/dist/components/treemap.js.map +1 -0
- package/dist/components/ui.d.ts +159 -46
- package/dist/components/ui.js +220 -115
- package/dist/components/ui.js.map +1 -1
- package/dist/components/use-mobile-reveal.js +1 -0
- package/dist/components/use-mobile-reveal.js.map +1 -1
- package/dist/components/use-table-state.d.ts +1 -1
- package/dist/components/use-table-state.js +38 -17
- package/dist/components/use-table-state.js.map +1 -1
- package/dist/components/user-avatar.d.ts +5 -3
- package/dist/components/user-avatar.js +4 -2
- package/dist/components/user-avatar.js.map +1 -1
- package/dist/components/wizard-stepper.js +3 -2
- package/dist/components/wizard-stepper.js.map +1 -1
- package/dist/{data-table-filters-aXyf0Xub.d.ts → data-table-filters-DM1auUfH.d.ts} +27 -5
- package/dist/data-table.d.ts +7 -0
- package/dist/data-table.js +12 -0
- package/dist/data-table.js.map +1 -0
- package/dist/feedback/feedback-attachment.js +12 -11
- package/dist/feedback/feedback-attachment.js.map +1 -1
- package/dist/feedback/feedback-dialog.js +2 -1
- package/dist/feedback/feedback-dialog.js.map +1 -1
- package/dist/feedback/feedback-inbox.d.ts +33 -7
- package/dist/feedback/feedback-inbox.js +49 -37
- package/dist/feedback/feedback-inbox.js.map +1 -1
- package/dist/feedback.d.ts +5 -0
- package/dist/feedback.js +5 -0
- package/dist/feedback.js.map +1 -0
- package/dist/hooks/use-anchored-panel.js +1 -0
- package/dist/hooks/use-anchored-panel.js.map +1 -1
- package/dist/hooks/use-anchored-rect.js +1 -0
- package/dist/hooks/use-anchored-rect.js.map +1 -1
- package/dist/hooks/use-announce.d.ts +46 -0
- package/dist/hooks/use-announce.js +37 -0
- package/dist/hooks/use-announce.js.map +1 -0
- package/dist/hooks/use-body-scroll-lock.js +1 -0
- package/dist/hooks/use-body-scroll-lock.js.map +1 -1
- package/dist/hooks/use-close-transition.js +1 -0
- package/dist/hooks/use-close-transition.js.map +1 -1
- package/dist/hooks/use-dismiss.js +1 -0
- package/dist/hooks/use-dismiss.js.map +1 -1
- package/dist/hooks/use-focus-trap.d.ts +16 -0
- package/dist/hooks/use-focus-trap.js +67 -0
- package/dist/hooks/use-focus-trap.js.map +1 -0
- package/dist/hooks/use-media-query.js +1 -0
- package/dist/hooks/use-media-query.js.map +1 -1
- package/dist/hooks/use-overlay-history.js +27 -1
- package/dist/hooks/use-overlay-history.js.map +1 -1
- package/dist/hooks/use-row-swipe.js +1 -0
- package/dist/hooks/use-row-swipe.js.map +1 -1
- package/dist/i18n/defaults.d.ts +31 -0
- package/dist/i18n/defaults.js +63 -0
- package/dist/i18n/defaults.js.map +1 -0
- package/dist/i18n/kit-labels.d.ts +250 -0
- package/dist/i18n/kit-labels.js +184 -0
- package/dist/i18n/kit-labels.js.map +1 -0
- package/dist/index.d.ts +47 -22
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/calc.js +1 -0
- package/dist/lib/calc.js.map +1 -1
- package/dist/lib/cn.js +1 -0
- package/dist/lib/cn.js.map +1 -1
- package/dist/lib/dates.js +1 -0
- package/dist/lib/dates.js.map +1 -1
- package/dist/lib/logger.js +1 -0
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/safe-storage.js +1 -0
- package/dist/lib/safe-storage.js.map +1 -1
- package/dist/search/command-palette.d.ts +11 -1
- package/dist/search/command-palette.js +21 -15
- package/dist/search/command-palette.js.map +1 -1
- package/dist/search.d.ts +2 -0
- package/dist/search.js +3 -0
- package/dist/search.js.map +1 -0
- package/dist/shell/app-shell.d.ts +33 -4
- package/dist/shell/app-shell.js +275 -78
- package/dist/shell/app-shell.js.map +1 -1
- package/dist/shell/option-switcher-menu.js +3 -2
- package/dist/shell/option-switcher-menu.js.map +1 -1
- package/dist/shell/role-switcher.d.ts +6 -0
- package/dist/shell/role-switcher.js +7 -4
- package/dist/shell/role-switcher.js.map +1 -1
- package/dist/shell/top-bar.d.ts +15 -7
- package/dist/shell/top-bar.js +3 -5
- package/dist/shell/top-bar.js.map +1 -1
- package/dist/shell/topbar-action-menu.js +3 -2
- package/dist/shell/topbar-action-menu.js.map +1 -1
- package/dist/shell/topbar-controls.d.ts +5 -2
- package/dist/shell/topbar-controls.js +20 -15
- package/dist/shell/topbar-controls.js.map +1 -1
- package/dist/shell.d.ts +13 -0
- package/dist/shell.js +8 -0
- package/dist/shell.js.map +1 -0
- package/dist/theme/chart-palette.d.ts +19 -12
- package/dist/theme/chart-palette.js +24 -30
- package/dist/theme/chart-palette.js.map +1 -1
- package/dist/theme/color.d.ts +78 -0
- package/dist/theme/color.js +148 -0
- package/dist/theme/color.js.map +1 -0
- package/dist/theme/palette-derive.d.ts +140 -0
- package/dist/theme/palette-derive.js +376 -0
- package/dist/theme/palette-derive.js.map +1 -0
- package/dist/theme/palette-presets.d.ts +14 -1
- package/dist/theme/palette-presets.js +231 -3
- package/dist/theme/palette-presets.js.map +1 -1
- package/dist/theme/palette-store.js +1 -0
- package/dist/theme/palette-store.js.map +1 -1
- package/dist/theme/theme-store.js +1 -0
- package/dist/theme/theme-store.js.map +1 -1
- package/dist/tour/tour.d.ts +2 -1
- package/dist/tour/tour.js +15 -9
- package/dist/tour/tour.js.map +1 -1
- package/dist/tour.d.ts +2 -0
- package/dist/tour.js +3 -0
- package/dist/tour.js.map +1 -0
- package/dist/wizard/stepper-nav.js +10 -8
- package/dist/wizard/stepper-nav.js.map +1 -1
- package/dist/wizard/types.d.ts +18 -2
- package/dist/wizard/types.js +4 -1
- package/dist/wizard/types.js.map +1 -1
- package/dist/wizard/use-wizard.js +12 -4
- package/dist/wizard/use-wizard.js.map +1 -1
- package/dist/wizard/validation.js +1 -0
- package/dist/wizard/validation.js.map +1 -1
- package/dist/wizard/wizard-context.js +1 -0
- package/dist/wizard/wizard-context.js.map +1 -1
- package/dist/wizard/wizard-step.js +1 -0
- package/dist/wizard/wizard-step.js.map +1 -1
- package/dist/wizard/wizard-summary.js +5 -3
- package/dist/wizard/wizard-summary.js.map +1 -1
- package/dist/wizard.d.ts +8 -0
- package/dist/wizard.js +9 -0
- package/dist/wizard.js.map +1 -0
- package/package.json +66 -13
- package/src/chart.ts +22 -0
- package/src/components/account-settings.tsx +275 -0
- package/src/components/alert-banner.tsx +75 -0
- package/src/components/amount-input.tsx +471 -0
- package/src/components/calculator.tsx +252 -0
- package/src/components/chart-zoom.tsx +499 -0
- package/src/components/chart.tsx +363 -0
- package/src/components/checkbox.tsx +222 -0
- package/src/components/chip.tsx +570 -0
- package/src/components/choice-parts.ts +33 -0
- package/src/components/combobox-core.tsx +540 -0
- package/src/components/combobox.tsx +1026 -0
- package/src/components/currency-select.tsx +269 -0
- package/src/components/data-table-filter-popover.tsx +252 -0
- package/src/components/data-table-filters.ts +176 -0
- package/src/components/data-table-labels.ts +190 -0
- package/src/components/data-table-pagination.tsx +126 -0
- package/src/components/data-table-sort.ts +70 -0
- package/src/components/data-table.tsx +1740 -0
- package/src/components/date-picker.tsx +631 -0
- package/src/components/dropdown.tsx +341 -0
- package/src/components/entity-combobox.tsx +215 -0
- package/src/components/facing-pair.ts +86 -0
- package/src/components/field-sync.tsx +472 -0
- package/src/components/file-dropzone.tsx +181 -0
- package/src/components/full-bleed-dialog.tsx +179 -0
- package/src/components/grouped-picker.tsx +157 -0
- package/src/components/hover-menu.tsx +183 -0
- package/src/components/mini-calendar.tsx +554 -0
- package/src/components/modal.tsx +287 -0
- package/src/components/month-picker.tsx +549 -0
- package/src/components/multi-entity-combobox.tsx +226 -0
- package/src/components/multi-select.tsx +303 -0
- package/src/components/number-field.tsx +225 -0
- package/src/components/number-input.tsx +211 -0
- package/src/components/numpad-sheet.tsx +264 -0
- package/src/components/page-contents.tsx +289 -0
- package/src/components/password-strength.tsx +258 -0
- package/src/components/picker-sheet.tsx +251 -0
- package/src/components/popover.tsx +195 -0
- package/src/components/search-field.tsx +146 -0
- package/src/components/series-chart-labels.ts +26 -0
- package/src/components/series-chart.tsx +481 -0
- package/src/components/settings-fields.tsx +68 -0
- package/src/components/signature-pad.tsx +630 -0
- package/src/components/slider.tsx +313 -0
- package/src/components/sparkline.tsx +329 -0
- package/src/components/stat-tile.tsx +448 -0
- package/src/components/swipeable-row.tsx +271 -0
- package/src/components/switch.tsx +170 -0
- package/src/components/time-input.tsx +107 -0
- package/src/components/toggle-group.tsx +153 -0
- package/src/components/toggle-legend.tsx +229 -0
- package/src/components/tooltip.tsx +477 -0
- package/src/components/treemap.tsx +390 -0
- package/src/components/ui.tsx +1284 -0
- package/src/components/use-mobile-reveal.ts +54 -0
- package/src/components/use-table-state.ts +295 -0
- package/src/components/user-avatar.tsx +61 -0
- package/src/components/wizard-stepper.tsx +37 -0
- package/src/data-table.ts +19 -0
- package/src/feedback/feedback-attachment.tsx +246 -0
- package/src/feedback/feedback-dialog.tsx +209 -0
- package/src/feedback/feedback-inbox.tsx +615 -0
- package/src/feedback.ts +8 -0
- package/src/hooks/use-anchored-panel.ts +169 -0
- package/src/hooks/use-anchored-rect.ts +78 -0
- package/src/hooks/use-announce.ts +85 -0
- package/src/hooks/use-body-scroll-lock.ts +94 -0
- package/src/hooks/use-close-transition.ts +99 -0
- package/src/hooks/use-dismiss.ts +77 -0
- package/src/hooks/use-focus-trap.ts +160 -0
- package/src/hooks/use-media-query.ts +23 -0
- package/src/hooks/use-overlay-history.ts +345 -0
- package/src/hooks/use-row-swipe.ts +212 -0
- package/src/i18n/defaults.ts +68 -0
- package/src/i18n/kit-labels.tsx +447 -0
- package/src/index.ts +169 -0
- package/src/lib/calc.ts +309 -0
- package/src/lib/cn.ts +5 -0
- package/src/lib/dates.ts +187 -0
- package/src/lib/logger.ts +73 -0
- package/src/lib/safe-storage.ts +37 -0
- package/src/search/command-palette.tsx +342 -0
- package/src/search.ts +3 -0
- package/src/shell/app-shell.tsx +772 -0
- package/src/shell/option-switcher-menu.tsx +72 -0
- package/src/shell/role-switcher.tsx +46 -0
- package/src/shell/top-bar.tsx +39 -0
- package/src/shell/topbar-action-menu.tsx +91 -0
- package/src/shell/topbar-controls.tsx +229 -0
- package/src/shell.ts +10 -0
- package/src/theme/chart-palette.ts +145 -0
- package/src/theme/color.ts +266 -0
- package/src/theme/palette-derive.ts +681 -0
- package/src/theme/palette-presets.ts +760 -0
- package/src/theme/palette-store.ts +94 -0
- package/src/theme/theme-store.ts +145 -0
- package/src/tour/tour.tsx +586 -0
- package/src/tour.ts +3 -0
- package/src/vite-env.d.ts +26 -0
- package/src/wizard/stepper-nav.tsx +221 -0
- package/src/wizard/types.ts +178 -0
- package/src/wizard/use-wizard.ts +345 -0
- package/src/wizard/validation.ts +37 -0
- package/src/wizard/wizard-context.tsx +29 -0
- package/src/wizard/wizard-step.tsx +14 -0
- package/src/wizard/wizard-summary.tsx +69 -0
- package/src/wizard.ts +14 -0
- package/tokens.css +222 -20
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { useId, useMemo } from "react";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
import { Check } from "lucide-react";
|
|
4
|
+
import { cn } from "../lib/cn";
|
|
5
|
+
import { Button } from "../components/ui";
|
|
6
|
+
import { Modal } from "../components/modal";
|
|
7
|
+
import { alertFrameClass } from "../components/alert-banner";
|
|
8
|
+
import { WizardContextProvider } from "./wizard-context";
|
|
9
|
+
import { DEFAULT_WIZARD_LABELS } from "./types";
|
|
10
|
+
import { useKitLabels } from "../i18n/kit-labels";
|
|
11
|
+
import type { UseWizardReturn, WizardLabels } from "./types";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The chrome around a wizard: the step indicator, the step content, and the
|
|
15
|
+
* Cancel / Back / Skip / Next / Finish bar — plus the cancel-confirmation
|
|
16
|
+
* dialog, because "are you sure you want to lose this" is part of the wizard's
|
|
17
|
+
* contract rather than something each app should re-decide.
|
|
18
|
+
*
|
|
19
|
+
* It is a component and not a set of parts for one reason: which button shows,
|
|
20
|
+
* and whether it is enabled, is a function of `wizard` state that was getting
|
|
21
|
+
* re-derived (and re-derived slightly differently) at every call site. Finish is
|
|
22
|
+
* gated on `canFinish`, Next and Skip on `nextBlocked`, and Skip only appears on
|
|
23
|
+
* an `optional` step that is not the last — that policy lives here now.
|
|
24
|
+
*
|
|
25
|
+
* The `<StepperNav>` is also what publishes {@link WizardContextProvider}, which
|
|
26
|
+
* is how a mounted step registers its own validator. A step's
|
|
27
|
+
* `useRhfWizardStep` / `useWizardStepValidate` therefore only works inside one.
|
|
28
|
+
*/
|
|
29
|
+
export function StepperNav<TData extends Record<string, unknown>>({
|
|
30
|
+
wizard,
|
|
31
|
+
children,
|
|
32
|
+
title,
|
|
33
|
+
description,
|
|
34
|
+
className,
|
|
35
|
+
labels,
|
|
36
|
+
}: {
|
|
37
|
+
wizard: UseWizardReturn<TData>;
|
|
38
|
+
children: ReactNode;
|
|
39
|
+
title?: ReactNode;
|
|
40
|
+
description?: ReactNode;
|
|
41
|
+
className?: string;
|
|
42
|
+
labels?: Partial<WizardLabels>;
|
|
43
|
+
}) {
|
|
44
|
+
const l = useKitLabels("wizard", DEFAULT_WIZARD_LABELS, labels);
|
|
45
|
+
const cancelTitleId = useId();
|
|
46
|
+
|
|
47
|
+
const contextValue = useMemo(
|
|
48
|
+
() => ({
|
|
49
|
+
registerStepValidate: wizard.registerStepValidate,
|
|
50
|
+
setNextBlocked: wizard.setNextBlocked,
|
|
51
|
+
}),
|
|
52
|
+
[wizard.registerStepValidate, wizard.setNextBlocked],
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
// Finish is only valid once every step before the last has been completed or
|
|
56
|
+
// skipped (derived once in useWizard). Prevents a restored last-step index
|
|
57
|
+
// (empty data after a reload) from firing an empty submit.
|
|
58
|
+
const canFinish = wizard.canFinish;
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<div className={cn("flex flex-col gap-6", className)}>
|
|
62
|
+
{(title || description) && (
|
|
63
|
+
<div>
|
|
64
|
+
{title && (
|
|
65
|
+
<h1 className="text-2xl font-bold tracking-tight text-[var(--text-primary)]">
|
|
66
|
+
{title}
|
|
67
|
+
</h1>
|
|
68
|
+
)}
|
|
69
|
+
{description && (
|
|
70
|
+
<p className="mt-1 text-sm text-[var(--text-muted)]">{description}</p>
|
|
71
|
+
)}
|
|
72
|
+
</div>
|
|
73
|
+
)}
|
|
74
|
+
|
|
75
|
+
{/* Step indicator */}
|
|
76
|
+
<nav aria-label={l.steps}>
|
|
77
|
+
<ol className="flex w-full items-center">
|
|
78
|
+
{wizard.steps.map((step, index) => {
|
|
79
|
+
const status = wizard.stepStatus(index);
|
|
80
|
+
const isLast = index === wizard.steps.length - 1;
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<li key={step.id} className={cn("flex items-center", !isLast && "flex-1")}>
|
|
84
|
+
<button
|
|
85
|
+
type="button"
|
|
86
|
+
onClick={() => wizard.goToStep(index)}
|
|
87
|
+
disabled={status === "upcoming"}
|
|
88
|
+
aria-current={status === "active" ? "step" : undefined}
|
|
89
|
+
className={cn(
|
|
90
|
+
"group flex flex-col items-center gap-1.5",
|
|
91
|
+
status === "upcoming" ? "cursor-default" : "cursor-pointer",
|
|
92
|
+
)}
|
|
93
|
+
>
|
|
94
|
+
{/* Circle */}
|
|
95
|
+
<span
|
|
96
|
+
className={cn(
|
|
97
|
+
"flex size-8 shrink-0 items-center justify-center rounded-full border-2 text-xs font-semibold transition-colors",
|
|
98
|
+
status === "completed" &&
|
|
99
|
+
"border-[var(--brand)] bg-[var(--brand)] text-[var(--brand-contrast)]",
|
|
100
|
+
status === "active" &&
|
|
101
|
+
"border-[var(--brand)] bg-[var(--bg-surface)] text-[var(--brand)]",
|
|
102
|
+
status === "upcoming" &&
|
|
103
|
+
"border-[var(--border)] bg-[var(--bg-surface)] text-[var(--text-placeholder)]",
|
|
104
|
+
status === "skipped" &&
|
|
105
|
+
"border-[var(--border)] bg-[var(--bg-surface-2)] text-[var(--text-muted)]",
|
|
106
|
+
)}
|
|
107
|
+
>
|
|
108
|
+
{status === "completed" ? <Check className="size-4" /> : index + 1}
|
|
109
|
+
</span>
|
|
110
|
+
{/* Label — hidden on mobile, where the line below carries it */}
|
|
111
|
+
<span
|
|
112
|
+
className={cn(
|
|
113
|
+
"hidden text-xs font-medium md:block",
|
|
114
|
+
status === "active" || status === "completed"
|
|
115
|
+
? "text-[var(--text-primary)]"
|
|
116
|
+
: "text-[var(--text-muted)]",
|
|
117
|
+
)}
|
|
118
|
+
>
|
|
119
|
+
{step.label}
|
|
120
|
+
</span>
|
|
121
|
+
</button>
|
|
122
|
+
|
|
123
|
+
{/* Connector line */}
|
|
124
|
+
{!isLast && (
|
|
125
|
+
<div
|
|
126
|
+
className={cn(
|
|
127
|
+
"mx-2 h-0.5 flex-1",
|
|
128
|
+
status === "completed" ? "bg-[var(--brand)]" : "bg-[var(--border)]",
|
|
129
|
+
)}
|
|
130
|
+
/>
|
|
131
|
+
)}
|
|
132
|
+
</li>
|
|
133
|
+
);
|
|
134
|
+
})}
|
|
135
|
+
</ol>
|
|
136
|
+
{/* Mobile: the labels above are hidden, so name the current step here */}
|
|
137
|
+
<p className="mt-2 text-center text-sm font-medium text-[var(--text-primary)] md:hidden">
|
|
138
|
+
{l.step(wizard.currentStepIndex + 1, wizard.steps.length)}
|
|
139
|
+
{" — "}
|
|
140
|
+
{wizard.currentStep.label}
|
|
141
|
+
</p>
|
|
142
|
+
</nav>
|
|
143
|
+
|
|
144
|
+
{/* Step content */}
|
|
145
|
+
<WizardContextProvider value={contextValue}>
|
|
146
|
+
<div className="min-h-[300px]">{children}</div>
|
|
147
|
+
</WizardContextProvider>
|
|
148
|
+
|
|
149
|
+
{/* Submit error */}
|
|
150
|
+
{wizard.error && (
|
|
151
|
+
<div
|
|
152
|
+
role="alert"
|
|
153
|
+
className={cn(
|
|
154
|
+
"text-sm text-[var(--danger)]",
|
|
155
|
+
alertFrameClass("danger"),
|
|
156
|
+
)}
|
|
157
|
+
>
|
|
158
|
+
{wizard.error}
|
|
159
|
+
</div>
|
|
160
|
+
)}
|
|
161
|
+
|
|
162
|
+
{/* Navigation bar */}
|
|
163
|
+
<div className="flex items-center justify-between border-t border-[var(--border)] pt-4">
|
|
164
|
+
<Button variant="secondary" onClick={wizard.cancel}>
|
|
165
|
+
{l.cancel}
|
|
166
|
+
</Button>
|
|
167
|
+
<div className="flex items-center gap-2">
|
|
168
|
+
{!wizard.isFirstStep && (
|
|
169
|
+
<Button variant="secondary" onClick={wizard.goBack}>
|
|
170
|
+
{l.back}
|
|
171
|
+
</Button>
|
|
172
|
+
)}
|
|
173
|
+
{wizard.currentStep.optional && !wizard.isLastStep && (
|
|
174
|
+
<Button variant="ghost" onClick={wizard.skip} disabled={wizard.nextBlocked}>
|
|
175
|
+
{l.skip}
|
|
176
|
+
</Button>
|
|
177
|
+
)}
|
|
178
|
+
{wizard.isLastStep ? (
|
|
179
|
+
<Button
|
|
180
|
+
variant="brand"
|
|
181
|
+
data-tour="wizard-finish"
|
|
182
|
+
onClick={wizard.finish}
|
|
183
|
+
disabled={wizard.isSubmitting || !canFinish}
|
|
184
|
+
>
|
|
185
|
+
{wizard.isSubmitting ? l.submitting : l.finish}
|
|
186
|
+
</Button>
|
|
187
|
+
) : (
|
|
188
|
+
<Button
|
|
189
|
+
variant="brand"
|
|
190
|
+
data-tour="wizard-next"
|
|
191
|
+
onClick={wizard.goNext}
|
|
192
|
+
disabled={wizard.nextBlocked}
|
|
193
|
+
>
|
|
194
|
+
{l.next}
|
|
195
|
+
</Button>
|
|
196
|
+
)}
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
{/* Cancel confirmation */}
|
|
201
|
+
{wizard.showCancelDialog && (
|
|
202
|
+
<Modal onClose={wizard.dismissCancel} labelledBy={cancelTitleId} className="space-y-4">
|
|
203
|
+
<div className="space-y-1.5">
|
|
204
|
+
<h2 id={cancelTitleId} className="text-lg font-semibold text-[var(--text-primary)]">
|
|
205
|
+
{l.cancelTitle}
|
|
206
|
+
</h2>
|
|
207
|
+
<p className="text-sm text-[var(--text-muted)]">{l.confirmCancel}</p>
|
|
208
|
+
</div>
|
|
209
|
+
<div className="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
|
|
210
|
+
<Button variant="secondary" onClick={wizard.dismissCancel}>
|
|
211
|
+
{l.cancelDismissLabel}
|
|
212
|
+
</Button>
|
|
213
|
+
<Button variant="danger" onClick={wizard.confirmCancel}>
|
|
214
|
+
{l.cancelConfirmLabel}
|
|
215
|
+
</Button>
|
|
216
|
+
</div>
|
|
217
|
+
</Modal>
|
|
218
|
+
)}
|
|
219
|
+
</div>
|
|
220
|
+
);
|
|
221
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
export type FieldErrors = Record<string, string>;
|
|
4
|
+
|
|
5
|
+
export type ValidateResult = boolean | { ok: boolean; errors?: FieldErrors };
|
|
6
|
+
|
|
7
|
+
export interface WizardStepConfig {
|
|
8
|
+
/** Unique step identifier. */
|
|
9
|
+
id: string;
|
|
10
|
+
/**
|
|
11
|
+
* What the step is called in the indicator.
|
|
12
|
+
*
|
|
13
|
+
* A `ReactNode`, not a translation key: the engine never resolves strings.
|
|
14
|
+
* An app with i18n maps its own keys at the `useWizard` call — the same split
|
|
15
|
+
* `TourStep` and `DataTableLabels` already use.
|
|
16
|
+
*/
|
|
17
|
+
label: ReactNode;
|
|
18
|
+
/** Whether the step can be skipped. */
|
|
19
|
+
optional?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Validation gate before advancing.
|
|
22
|
+
* - Return `true` / `false` to just block.
|
|
23
|
+
* - Return `{ ok: false, errors: { fieldName: "message" } }` to also surface
|
|
24
|
+
* per-field error messages via `wizard.fieldErrors`.
|
|
25
|
+
*/
|
|
26
|
+
validate?: () => ValidateResult | Promise<ValidateResult>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface UseWizardOptions<TData extends Record<string, unknown>> {
|
|
30
|
+
steps: WizardStepConfig[];
|
|
31
|
+
initialData?: Partial<TData>;
|
|
32
|
+
onComplete?: (data: TData) => void | Promise<void>;
|
|
33
|
+
onCancel?: () => void;
|
|
34
|
+
/**
|
|
35
|
+
* Shown when a step blocks with no per-field detail — an RHF step surfacing
|
|
36
|
+
* its own inline messages, say — so the user learns why the wizard did not
|
|
37
|
+
* advance. Falls back to `wizard.missingRequired` from the {@link UiKitProvider},
|
|
38
|
+
* then to English.
|
|
39
|
+
*/
|
|
40
|
+
missingRequiredMessage?: string;
|
|
41
|
+
/**
|
|
42
|
+
* How to surface {@link missingRequiredMessage}. Defaults to a `sonner` toast
|
|
43
|
+
* (an optional peer, imported only when this actually fires). Pass your own to
|
|
44
|
+
* route it somewhere else, or to a no-op to silence it.
|
|
45
|
+
*/
|
|
46
|
+
onValidationFailed?: (message: string) => void;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export type StepStatus = "upcoming" | "active" | "completed" | "skipped";
|
|
50
|
+
|
|
51
|
+
export interface UseWizardReturn<TData extends Record<string, unknown>> {
|
|
52
|
+
// Navigation
|
|
53
|
+
currentStepIndex: number;
|
|
54
|
+
currentStep: WizardStepConfig;
|
|
55
|
+
steps: WizardStepConfig[];
|
|
56
|
+
isFirstStep: boolean;
|
|
57
|
+
isLastStep: boolean;
|
|
58
|
+
/** True once every step before the last has been completed or skipped. */
|
|
59
|
+
canFinish: boolean;
|
|
60
|
+
goNext: () => Promise<void>;
|
|
61
|
+
goBack: () => void;
|
|
62
|
+
goToStep: (index: number) => void;
|
|
63
|
+
skip: () => void;
|
|
64
|
+
cancel: () => void;
|
|
65
|
+
finish: () => Promise<void>;
|
|
66
|
+
/** Register a validator for the mounted step (used by useRhfWizardStep). */
|
|
67
|
+
registerStepValidate: (
|
|
68
|
+
fn: () => ValidateResult | Promise<ValidateResult>,
|
|
69
|
+
) => () => void;
|
|
70
|
+
/** True when the mounted step has blocked forward navigation (Next/Skip disabled). */
|
|
71
|
+
nextBlocked: boolean;
|
|
72
|
+
/** Set by the active step (via useWizardNextGate) to disable/enable Next+Skip. */
|
|
73
|
+
setNextBlocked: (blocked: boolean) => void;
|
|
74
|
+
|
|
75
|
+
// Step state
|
|
76
|
+
stepStatus: (index: number) => StepStatus;
|
|
77
|
+
completedSteps: Set<number>;
|
|
78
|
+
|
|
79
|
+
// Data
|
|
80
|
+
data: TData;
|
|
81
|
+
updateData: (partial: Partial<TData>) => void;
|
|
82
|
+
|
|
83
|
+
// Loading / error
|
|
84
|
+
isSubmitting: boolean;
|
|
85
|
+
error: string | null;
|
|
86
|
+
clearError: () => void;
|
|
87
|
+
|
|
88
|
+
// Per-field validation errors (populated by the last failed goNext)
|
|
89
|
+
fieldErrors: FieldErrors;
|
|
90
|
+
clearFieldErrors: () => void;
|
|
91
|
+
|
|
92
|
+
// Cancel dialog
|
|
93
|
+
showCancelDialog: boolean;
|
|
94
|
+
confirmCancel: () => void;
|
|
95
|
+
dismissCancel: () => void;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface SummaryItem {
|
|
99
|
+
label: ReactNode;
|
|
100
|
+
value: ReactNode;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface SummarySection {
|
|
104
|
+
label: ReactNode;
|
|
105
|
+
/** Which step the section's edit button jumps back to. */
|
|
106
|
+
stepIndex: number;
|
|
107
|
+
items: SummaryItem[];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Every string the wizard chrome shows. All optional with English defaults, so a
|
|
112
|
+
* monolingual app can mount `StepperNav` bare; a translated app passes the whole
|
|
113
|
+
* object once (typically from a thin app-side wrapper).
|
|
114
|
+
*/
|
|
115
|
+
export interface WizardLabels {
|
|
116
|
+
cancel: string;
|
|
117
|
+
back: string;
|
|
118
|
+
next: string;
|
|
119
|
+
skip: string;
|
|
120
|
+
finish: string;
|
|
121
|
+
/** Shown on Finish while `isSubmitting`. */
|
|
122
|
+
submitting: string;
|
|
123
|
+
/** `aria-label` for the step indicator's `<nav>`. */
|
|
124
|
+
steps: string;
|
|
125
|
+
/** The mobile "Step 2 of 5" line. */
|
|
126
|
+
step: (current: number, total: number) => string;
|
|
127
|
+
/** Heading of the cancel-confirmation dialog. */
|
|
128
|
+
cancelTitle: string;
|
|
129
|
+
/** Body of the cancel-confirmation dialog. */
|
|
130
|
+
confirmCancel: string;
|
|
131
|
+
/** Confirm button in the cancel dialog. */
|
|
132
|
+
cancelConfirmLabel: string;
|
|
133
|
+
/** Dismiss button in the cancel dialog ("keep editing"). */
|
|
134
|
+
cancelDismissLabel: string;
|
|
135
|
+
/** Heading above a {@link WizardSummary}. */
|
|
136
|
+
reviewTitle: string;
|
|
137
|
+
/** `aria-label` on a summary section's edit button. */
|
|
138
|
+
edit: string;
|
|
139
|
+
/**
|
|
140
|
+
* Toasted (or handed to `onValidationFailed`) when a step blocks Next without
|
|
141
|
+
* saying which field is at fault. `useWizard`'s `missingRequiredMessage` wins.
|
|
142
|
+
*
|
|
143
|
+
* This and `genericError` are OPTIONAL, unlike every key above, for the reason
|
|
144
|
+
* `TwoFactorSettingLabels.qrAlt` is: apps annotate a full translation as
|
|
145
|
+
* `const DE: WizardLabels = {…}`, and a new required key would be a compile error
|
|
146
|
+
* in each of them on the next update. The defaults below always carry both.
|
|
147
|
+
*/
|
|
148
|
+
missingRequired?: string;
|
|
149
|
+
/** `wizard.error` when `onComplete` rejects with nothing readable — a thrown
|
|
150
|
+
* non-Error, or an Error whose message is empty. Optional: see above. */
|
|
151
|
+
genericError?: string;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Typed with the two optional keys made required, so code reading the defaults
|
|
155
|
+
* directly (the fallback in `useWizard`) needs no `!`. */
|
|
156
|
+
export const DEFAULT_WIZARD_LABELS: WizardLabels &
|
|
157
|
+
Required<Pick<WizardLabels, "missingRequired" | "genericError">> = {
|
|
158
|
+
cancel: "Cancel",
|
|
159
|
+
back: "Back",
|
|
160
|
+
next: "Next",
|
|
161
|
+
skip: "Skip",
|
|
162
|
+
finish: "Finish",
|
|
163
|
+
submitting: "Creating…",
|
|
164
|
+
steps: "Steps",
|
|
165
|
+
step: (current, total) => `Step ${current} of ${total}`,
|
|
166
|
+
cancelTitle: "Discard this form?",
|
|
167
|
+
confirmCancel: "Your entries will be lost.",
|
|
168
|
+
cancelConfirmLabel: "Discard",
|
|
169
|
+
cancelDismissLabel: "Keep editing",
|
|
170
|
+
reviewTitle: "Review",
|
|
171
|
+
edit: "Edit",
|
|
172
|
+
missingRequired: "Please fill in all required fields.",
|
|
173
|
+
genericError: "An error occurred",
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export function resolveWizardLabels(labels?: Partial<WizardLabels>): WizardLabels {
|
|
177
|
+
return labels ? { ...DEFAULT_WIZARD_LABELS, ...labels } : DEFAULT_WIZARD_LABELS;
|
|
178
|
+
}
|