@eifi1/ui-kit 0.4.0 → 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 +91 -20
- 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 +96 -20
- package/dist/components/dropdown.js +77 -33
- 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 +38 -6
- package/dist/components/file-dropzone.js +34 -9
- 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 +28 -2
- package/dist/components/number-input.js +12 -2
- package/dist/components/number-input.js.map +1 -1
- package/dist/components/numpad-sheet.d.ts +32 -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 +15 -4
- package/dist/components/user-avatar.js +5 -4
- 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 +27 -5
- package/dist/theme/palette-presets.js +231 -33
- 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 +67 -14
- 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,345 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The multi-step wizard state machine: step index, completed/skipped sets,
|
|
3
|
+
* per-step async validators, collected data, field errors, submit and the
|
|
4
|
+
* cancel-confirm gate.
|
|
5
|
+
*
|
|
6
|
+
* Domain-free, but NOT router-free: it mirrors the active step to `?step=N`, so
|
|
7
|
+
* it must be mounted inside a react-router context — the same requirement
|
|
8
|
+
* `DataTable` already carries. It only ever WRITES that param (see the note on
|
|
9
|
+
* `initialStep` below for why it deliberately does not read it back).
|
|
10
|
+
*/
|
|
11
|
+
import { useCallback, useEffect, useReducer, useRef, useState } from "react";
|
|
12
|
+
import { useSearchParams } from "react-router";
|
|
13
|
+
import { useKitLabels } from "../i18n/kit-labels";
|
|
14
|
+
import { DEFAULT_WIZARD_LABELS } from "./types";
|
|
15
|
+
import type {
|
|
16
|
+
FieldErrors,
|
|
17
|
+
StepStatus,
|
|
18
|
+
UseWizardOptions,
|
|
19
|
+
UseWizardReturn,
|
|
20
|
+
ValidateResult,
|
|
21
|
+
WizardStepConfig,
|
|
22
|
+
} from "./types";
|
|
23
|
+
|
|
24
|
+
interface WizardState<TData> {
|
|
25
|
+
currentStepIndex: number;
|
|
26
|
+
completedSteps: Set<number>;
|
|
27
|
+
skippedSteps: Set<number>;
|
|
28
|
+
data: TData;
|
|
29
|
+
isSubmitting: boolean;
|
|
30
|
+
error: string | null;
|
|
31
|
+
fieldErrors: FieldErrors;
|
|
32
|
+
showCancelDialog: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type WizardAction<TData> =
|
|
36
|
+
| { type: "GO_NEXT" }
|
|
37
|
+
| { type: "GO_BACK" }
|
|
38
|
+
| { type: "GO_TO_STEP"; index: number }
|
|
39
|
+
| { type: "SKIP" }
|
|
40
|
+
| { type: "UPDATE_DATA"; partial: Partial<TData> }
|
|
41
|
+
| { type: "SET_SUBMITTING"; value: boolean }
|
|
42
|
+
| { type: "SET_ERROR"; error: string | null }
|
|
43
|
+
| { type: "SET_FIELD_ERRORS"; errors: FieldErrors }
|
|
44
|
+
| { type: "SHOW_CANCEL_DIALOG"; show: boolean };
|
|
45
|
+
|
|
46
|
+
function createReducer<TData>(stepCount: number) {
|
|
47
|
+
return function reducer(
|
|
48
|
+
state: WizardState<TData>,
|
|
49
|
+
action: WizardAction<TData>,
|
|
50
|
+
): WizardState<TData> {
|
|
51
|
+
switch (action.type) {
|
|
52
|
+
case "GO_NEXT": {
|
|
53
|
+
const completed = new Set(state.completedSteps);
|
|
54
|
+
completed.add(state.currentStepIndex);
|
|
55
|
+
const nextIndex = Math.min(state.currentStepIndex + 1, stepCount - 1);
|
|
56
|
+
return { ...state, currentStepIndex: nextIndex, completedSteps: completed, fieldErrors: {} };
|
|
57
|
+
}
|
|
58
|
+
case "GO_BACK": {
|
|
59
|
+
const prevIndex = Math.max(state.currentStepIndex - 1, 0);
|
|
60
|
+
return { ...state, currentStepIndex: prevIndex, fieldErrors: {} };
|
|
61
|
+
}
|
|
62
|
+
case "GO_TO_STEP": {
|
|
63
|
+
if (action.index < 0 || action.index >= stepCount) return state;
|
|
64
|
+
if (!state.completedSteps.has(action.index) && action.index > state.currentStepIndex) {
|
|
65
|
+
return state;
|
|
66
|
+
}
|
|
67
|
+
return { ...state, currentStepIndex: action.index };
|
|
68
|
+
}
|
|
69
|
+
case "SKIP": {
|
|
70
|
+
const skipped = new Set(state.skippedSteps);
|
|
71
|
+
skipped.add(state.currentStepIndex);
|
|
72
|
+
const nextIndex = Math.min(state.currentStepIndex + 1, stepCount - 1);
|
|
73
|
+
return { ...state, currentStepIndex: nextIndex, skippedSteps: skipped };
|
|
74
|
+
}
|
|
75
|
+
case "UPDATE_DATA": {
|
|
76
|
+
// Clear error for any field that is now being edited (so typing makes
|
|
77
|
+
// the red message disappear).
|
|
78
|
+
const nextErrors = { ...state.fieldErrors };
|
|
79
|
+
for (const key of Object.keys(action.partial)) {
|
|
80
|
+
delete nextErrors[key];
|
|
81
|
+
}
|
|
82
|
+
return { ...state, data: { ...state.data, ...action.partial }, fieldErrors: nextErrors };
|
|
83
|
+
}
|
|
84
|
+
case "SET_SUBMITTING":
|
|
85
|
+
return { ...state, isSubmitting: action.value };
|
|
86
|
+
case "SET_ERROR":
|
|
87
|
+
return { ...state, error: action.error };
|
|
88
|
+
case "SET_FIELD_ERRORS":
|
|
89
|
+
return { ...state, fieldErrors: action.errors };
|
|
90
|
+
case "SHOW_CANCEL_DIALOG":
|
|
91
|
+
return { ...state, showCancelDialog: action.show };
|
|
92
|
+
default:
|
|
93
|
+
return state;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function useWizard<TData extends Record<string, unknown>>(
|
|
99
|
+
options: UseWizardOptions<TData>,
|
|
100
|
+
): UseWizardReturn<TData> {
|
|
101
|
+
const { steps, initialData, onComplete, onCancel, missingRequiredMessage, onValidationFailed } =
|
|
102
|
+
options;
|
|
103
|
+
const stepsRef = useRef<WizardStepConfig[]>(steps);
|
|
104
|
+
stepsRef.current = steps;
|
|
105
|
+
// A hook, not a component, but it produces two sentences of its own — the
|
|
106
|
+
// "fill in the required fields" toast and the fallback submit error — and both
|
|
107
|
+
// reach the user. So it reads the provider like any component does; only the two
|
|
108
|
+
// strings are depended on below, so the callbacks stay stable across renders.
|
|
109
|
+
// The `??` is for the type only: both keys are optional on `WizardLabels` (see
|
|
110
|
+
// there), and the merge never replaces a default with `undefined`.
|
|
111
|
+
const wizardLabels = useKitLabels("wizard", DEFAULT_WIZARD_LABELS, {
|
|
112
|
+
missingRequired: missingRequiredMessage,
|
|
113
|
+
});
|
|
114
|
+
const missingRequired = wizardLabels.missingRequired ?? DEFAULT_WIZARD_LABELS.missingRequired;
|
|
115
|
+
const genericError = wizardLabels.genericError ?? DEFAULT_WIZARD_LABELS.genericError;
|
|
116
|
+
|
|
117
|
+
// Validators registered by the CURRENT step's mounted components (RHF steps
|
|
118
|
+
// register via `useRhfWizardStep` through the wizard context). Only the active
|
|
119
|
+
// step's validators are present — each unregisters on unmount. `goNext` and
|
|
120
|
+
// `finish` run them (via `runStepValidators`) alongside the step config's own
|
|
121
|
+
// `validate`.
|
|
122
|
+
const validatorsRef = useRef(
|
|
123
|
+
new Map<symbol, () => ValidateResult | Promise<ValidateResult>>(),
|
|
124
|
+
);
|
|
125
|
+
const registerStepValidate = useCallback(
|
|
126
|
+
(fn: () => ValidateResult | Promise<ValidateResult>) => {
|
|
127
|
+
const token = Symbol();
|
|
128
|
+
validatorsRef.current.set(token, fn);
|
|
129
|
+
return () => {
|
|
130
|
+
validatorsRef.current.delete(token);
|
|
131
|
+
};
|
|
132
|
+
},
|
|
133
|
+
[],
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
const [, setUrlParams] = useSearchParams();
|
|
137
|
+
// The wizard's entered `data` and `completedSteps` are NOT persisted across a
|
|
138
|
+
// reload / deep-link (the reducer re-inits to empty defaults on every mount).
|
|
139
|
+
// Restoring a later step index from `?step=N` would therefore leave the wizard
|
|
140
|
+
// on (or past) the last step with an EMPTY data set, letting Finish submit an
|
|
141
|
+
// empty payload. Always start at step 0 so a reload re-runs the wizard from a
|
|
142
|
+
// valid state instead of losing the entered data silently.
|
|
143
|
+
const initialStep = 0;
|
|
144
|
+
|
|
145
|
+
const reducer = createReducer<TData>(steps.length);
|
|
146
|
+
const [state, dispatch] = useReducer(reducer, {
|
|
147
|
+
currentStepIndex: initialStep,
|
|
148
|
+
completedSteps: new Set<number>(),
|
|
149
|
+
skippedSteps: new Set<number>(),
|
|
150
|
+
data: (initialData ?? {}) as TData,
|
|
151
|
+
isSubmitting: false,
|
|
152
|
+
error: null,
|
|
153
|
+
fieldErrors: {},
|
|
154
|
+
showCancelDialog: false,
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// A mounted step can DISABLE forward navigation (Next/Skip) while its own
|
|
158
|
+
// state is invalid (e.g. tenant shares != 100%). Defaults to false, so
|
|
159
|
+
// wizards that never call setNextBlocked behave exactly as before.
|
|
160
|
+
const [nextBlocked, setNextBlockedState] = useState(false);
|
|
161
|
+
const setNextBlocked = useCallback(
|
|
162
|
+
(blocked: boolean) => setNextBlockedState(blocked),
|
|
163
|
+
[],
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
// Sync step index to URL
|
|
167
|
+
useEffect(() => {
|
|
168
|
+
setUrlParams((prev) => {
|
|
169
|
+
prev.set("step", String(state.currentStepIndex));
|
|
170
|
+
return prev;
|
|
171
|
+
}, { replace: true });
|
|
172
|
+
}, [state.currentStepIndex, setUrlParams]);
|
|
173
|
+
|
|
174
|
+
const currentStep = stepsRef.current[state.currentStepIndex];
|
|
175
|
+
const isFirstStep = state.currentStepIndex === 0;
|
|
176
|
+
const isLastStep = state.currentStepIndex === stepsRef.current.length - 1;
|
|
177
|
+
// Finish is only valid once every step before the last has actually been
|
|
178
|
+
// walked through (completed or skipped). Guards against a restored/last-step
|
|
179
|
+
// index submitting a payload built from empty defaults.
|
|
180
|
+
const canFinish = stepsRef.current.every(
|
|
181
|
+
(_, index) =>
|
|
182
|
+
index === stepsRef.current.length - 1 ||
|
|
183
|
+
state.completedSteps.has(index) ||
|
|
184
|
+
state.skippedSteps.has(index),
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
// Runs the active step's config `validate` plus every validator its mounted
|
|
188
|
+
// components registered (RHF steps via useRhfWizardStep), AND-combining the
|
|
189
|
+
// results and merging any per-field errors. Returns `true` when the step
|
|
190
|
+
// passes (or registered no validators). Shared by `goNext` and `finish` so a
|
|
191
|
+
// form-bearing last step is validated (and its values flushed) on Finish too.
|
|
192
|
+
const runStepValidators = useCallback(async (): Promise<boolean> => {
|
|
193
|
+
const step = stepsRef.current[state.currentStepIndex];
|
|
194
|
+
const validators = [...validatorsRef.current.values()];
|
|
195
|
+
if (step.validate) validators.push(step.validate);
|
|
196
|
+
if (validators.length === 0) return true;
|
|
197
|
+
let ok = true;
|
|
198
|
+
let errors: FieldErrors = {};
|
|
199
|
+
try {
|
|
200
|
+
for (const validate of validators) {
|
|
201
|
+
const result = await validate();
|
|
202
|
+
const vOk = typeof result === "boolean" ? result : result.ok;
|
|
203
|
+
if (!vOk) {
|
|
204
|
+
ok = false;
|
|
205
|
+
if (typeof result !== "boolean" && result.errors) {
|
|
206
|
+
errors = { ...errors, ...result.errors };
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
} catch (err) {
|
|
211
|
+
// FALL THROUGH, do not return. A bare `return false` here blocked Next while
|
|
212
|
+
// skipping the `if (!ok)` block below — the only thing that shows a message,
|
|
213
|
+
// and whose own comment is "say something anyway, so the user is not left with
|
|
214
|
+
// a Next button that silently does nothing". So a validator that threw (an
|
|
215
|
+
// async one whose request rejected, a null dereference in a field rule)
|
|
216
|
+
// produced exactly the outcome that block was written to prevent: no field
|
|
217
|
+
// errors, no toast, `onValidationFailed` never called, nothing in the console.
|
|
218
|
+
//
|
|
219
|
+
// The step still must not advance; `ok = false` is what says so. Whatever
|
|
220
|
+
// partial `errors` the loop had collected before the throw are kept — they are
|
|
221
|
+
// real, and an empty set is what routes this to the generic message.
|
|
222
|
+
console.error("[wizard] a step validator threw", err);
|
|
223
|
+
ok = false;
|
|
224
|
+
}
|
|
225
|
+
if (!ok) {
|
|
226
|
+
dispatch({ type: "SET_FIELD_ERRORS", errors });
|
|
227
|
+
if (Object.keys(errors).length === 0) {
|
|
228
|
+
// No per-field detail (e.g. an RHF step surfacing its own inline
|
|
229
|
+
// message) — say something anyway, so the user is not left with a Next
|
|
230
|
+
// button that silently does nothing.
|
|
231
|
+
const message = missingRequired;
|
|
232
|
+
if (onValidationFailed) {
|
|
233
|
+
onValidationFailed(message);
|
|
234
|
+
} else {
|
|
235
|
+
// sonner is an optional peer: imported here, on the failure path only,
|
|
236
|
+
// so an app that never trips this never has to install it.
|
|
237
|
+
const { toast } = await import("sonner");
|
|
238
|
+
toast.error(message);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
return true;
|
|
244
|
+
}, [state.currentStepIndex, missingRequired, onValidationFailed]);
|
|
245
|
+
|
|
246
|
+
const goNext = useCallback(async () => {
|
|
247
|
+
if (await runStepValidators()) {
|
|
248
|
+
dispatch({ type: "GO_NEXT" });
|
|
249
|
+
}
|
|
250
|
+
}, [runStepValidators]);
|
|
251
|
+
|
|
252
|
+
const goBack = useCallback(() => {
|
|
253
|
+
dispatch({ type: "GO_BACK" });
|
|
254
|
+
}, []);
|
|
255
|
+
|
|
256
|
+
const goToStep = useCallback((index: number) => {
|
|
257
|
+
dispatch({ type: "GO_TO_STEP", index });
|
|
258
|
+
}, []);
|
|
259
|
+
|
|
260
|
+
const skip = useCallback(() => {
|
|
261
|
+
dispatch({ type: "SKIP" });
|
|
262
|
+
}, []);
|
|
263
|
+
|
|
264
|
+
const cancel = useCallback(() => {
|
|
265
|
+
dispatch({ type: "SHOW_CANCEL_DIALOG", show: true });
|
|
266
|
+
}, []);
|
|
267
|
+
|
|
268
|
+
const confirmCancel = useCallback(() => {
|
|
269
|
+
dispatch({ type: "SHOW_CANCEL_DIALOG", show: false });
|
|
270
|
+
onCancel?.();
|
|
271
|
+
}, [onCancel]);
|
|
272
|
+
|
|
273
|
+
const dismissCancel = useCallback(() => {
|
|
274
|
+
dispatch({ type: "SHOW_CANCEL_DIALOG", show: false });
|
|
275
|
+
}, []);
|
|
276
|
+
|
|
277
|
+
const updateData = useCallback((partial: Partial<TData>) => {
|
|
278
|
+
dispatch({ type: "UPDATE_DATA", partial });
|
|
279
|
+
}, []);
|
|
280
|
+
|
|
281
|
+
const clearError = useCallback(() => {
|
|
282
|
+
dispatch({ type: "SET_ERROR", error: null });
|
|
283
|
+
}, []);
|
|
284
|
+
|
|
285
|
+
const clearFieldErrors = useCallback(() => {
|
|
286
|
+
dispatch({ type: "SET_FIELD_ERRORS", errors: {} });
|
|
287
|
+
}, []);
|
|
288
|
+
|
|
289
|
+
const finish = useCallback(async () => {
|
|
290
|
+
if (!onComplete || !canFinish) return;
|
|
291
|
+
// Validate the (possibly form-bearing) last step before submitting, matching
|
|
292
|
+
// the Next gate — review-only steps register no validators and pass through.
|
|
293
|
+
if (!(await runStepValidators())) return;
|
|
294
|
+
dispatch({ type: "SET_SUBMITTING", value: true });
|
|
295
|
+
dispatch({ type: "SET_ERROR", error: null });
|
|
296
|
+
try {
|
|
297
|
+
await onComplete(state.data);
|
|
298
|
+
} catch (err) {
|
|
299
|
+
const message = err instanceof Error && err.message ? err.message : genericError;
|
|
300
|
+
dispatch({ type: "SET_ERROR", error: message });
|
|
301
|
+
} finally {
|
|
302
|
+
dispatch({ type: "SET_SUBMITTING", value: false });
|
|
303
|
+
}
|
|
304
|
+
}, [onComplete, canFinish, state.data, runStepValidators, genericError]);
|
|
305
|
+
|
|
306
|
+
const stepStatus = useCallback(
|
|
307
|
+
(index: number): StepStatus => {
|
|
308
|
+
if (index === state.currentStepIndex) return "active";
|
|
309
|
+
if (state.skippedSteps.has(index)) return "skipped";
|
|
310
|
+
if (state.completedSteps.has(index)) return "completed";
|
|
311
|
+
return "upcoming";
|
|
312
|
+
},
|
|
313
|
+
[state.currentStepIndex, state.completedSteps, state.skippedSteps],
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
return {
|
|
317
|
+
currentStepIndex: state.currentStepIndex,
|
|
318
|
+
currentStep,
|
|
319
|
+
steps: stepsRef.current,
|
|
320
|
+
isFirstStep,
|
|
321
|
+
isLastStep,
|
|
322
|
+
canFinish,
|
|
323
|
+
goNext,
|
|
324
|
+
goBack,
|
|
325
|
+
goToStep,
|
|
326
|
+
skip,
|
|
327
|
+
cancel,
|
|
328
|
+
finish,
|
|
329
|
+
registerStepValidate,
|
|
330
|
+
nextBlocked,
|
|
331
|
+
setNextBlocked,
|
|
332
|
+
stepStatus,
|
|
333
|
+
completedSteps: state.completedSteps,
|
|
334
|
+
data: state.data,
|
|
335
|
+
updateData,
|
|
336
|
+
isSubmitting: state.isSubmitting,
|
|
337
|
+
error: state.error,
|
|
338
|
+
clearError,
|
|
339
|
+
fieldErrors: state.fieldErrors,
|
|
340
|
+
clearFieldErrors,
|
|
341
|
+
showCancelDialog: state.showCancelDialog,
|
|
342
|
+
confirmCancel,
|
|
343
|
+
dismissCancel,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ValidateResult, FieldErrors } from "./types";
|
|
2
|
+
|
|
3
|
+
export interface RequiredFieldSpec<T> {
|
|
4
|
+
key: keyof T;
|
|
5
|
+
/**
|
|
6
|
+
* Custom predicate (e.g. numeric `> 0` or range checks). Defaults to a
|
|
7
|
+
* "present" check: a non-empty trimmed string, or a non-null id/enum.
|
|
8
|
+
*/
|
|
9
|
+
valid?: (value: unknown, data: T) => boolean;
|
|
10
|
+
/** Per-field message override (defaults to the shared `defaultMessage`). */
|
|
11
|
+
message?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Folds the copy-pasted `const errors = {}; if (!data.x) errors.x = required; …`
|
|
16
|
+
* step-validate block reimplemented across the single-file entity wizards.
|
|
17
|
+
* Pure-required fields need only `{ key }`; numeric constraints (amount `> 0`,
|
|
18
|
+
* rate in range) pass a `valid` predicate. The message is caller-supplied so
|
|
19
|
+
* each page keeps its own namespace (e.g. `t("common:validation.required")`).
|
|
20
|
+
*/
|
|
21
|
+
export function requiredFieldsValidator<T extends Record<string, unknown>>(
|
|
22
|
+
data: T,
|
|
23
|
+
fields: RequiredFieldSpec<T>[],
|
|
24
|
+
defaultMessage: string,
|
|
25
|
+
): ValidateResult {
|
|
26
|
+
const errors: FieldErrors = {};
|
|
27
|
+
for (const f of fields) {
|
|
28
|
+
const v = data[f.key];
|
|
29
|
+
const ok = f.valid
|
|
30
|
+
? f.valid(v, data)
|
|
31
|
+
: typeof v === "string"
|
|
32
|
+
? v.trim() !== ""
|
|
33
|
+
: v != null;
|
|
34
|
+
if (!ok) errors[String(f.key)] = f.message ?? defaultMessage;
|
|
35
|
+
}
|
|
36
|
+
return { ok: Object.keys(errors).length === 0, errors };
|
|
37
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
import type { ValidateResult } from "./types";
|
|
3
|
+
|
|
4
|
+
export interface WizardContextValue {
|
|
5
|
+
/**
|
|
6
|
+
* Register a validator for the currently-mounted step. Returns an unregister
|
|
7
|
+
* fn (call it in a useEffect cleanup). `goNext` runs every registered
|
|
8
|
+
* validator for the active step (plus the step config's own `validate`),
|
|
9
|
+
* AND-combining the results — this replaces the per-page `useRef` +
|
|
10
|
+
* `validate: () => ref.current?.()` bridge each RHF step used to wire.
|
|
11
|
+
*/
|
|
12
|
+
registerStepValidate: (
|
|
13
|
+
fn: () => ValidateResult | Promise<ValidateResult>,
|
|
14
|
+
) => () => void;
|
|
15
|
+
/** Lets the mounted step disable Next/Skip (e.g. invalid tenant shares). */
|
|
16
|
+
setNextBlocked: (blocked: boolean) => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const WizardContext = createContext<WizardContextValue | null>(null);
|
|
20
|
+
|
|
21
|
+
export const WizardContextProvider = WizardContext.Provider;
|
|
22
|
+
|
|
23
|
+
export function useWizardContext(): WizardContextValue {
|
|
24
|
+
const ctx = useContext(WizardContext);
|
|
25
|
+
if (!ctx) {
|
|
26
|
+
throw new Error("useWizardContext must be used within a <StepperNav>");
|
|
27
|
+
}
|
|
28
|
+
return ctx;
|
|
29
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { cn } from "../lib/cn";
|
|
3
|
+
|
|
4
|
+
/** The vertical rhythm one wizard step's content sits on. A component rather
|
|
5
|
+
* than a class string so every step in every app spaces its fields the same. */
|
|
6
|
+
export function WizardStep({
|
|
7
|
+
children,
|
|
8
|
+
className,
|
|
9
|
+
}: {
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
className?: string;
|
|
12
|
+
}) {
|
|
13
|
+
return <div className={cn("space-y-6", className)}>{children}</div>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Pencil } from "lucide-react";
|
|
2
|
+
import { Card, CardAction, CardContent, CardHeader, CardTitle, IconButton } from "../components/ui";
|
|
3
|
+
import type { SummarySection, WizardLabels } from "./types";
|
|
4
|
+
import { DEFAULT_WIZARD_LABELS } from "./types";
|
|
5
|
+
import { useKitLabels } from "../i18n/kit-labels";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The review step: one card per section, each row a label/value pair, each card
|
|
9
|
+
* carrying an edit button back to the step it came from.
|
|
10
|
+
*
|
|
11
|
+
* Sections are data, not children, because "jump back to step N" is the whole
|
|
12
|
+
* point of the component and a caller assembling its own cards invariably drops
|
|
13
|
+
* it. Labels and values are `ReactNode`, so a value can be a badge or a link.
|
|
14
|
+
*/
|
|
15
|
+
export function WizardSummary({
|
|
16
|
+
sections,
|
|
17
|
+
onEditStep,
|
|
18
|
+
labels,
|
|
19
|
+
}: {
|
|
20
|
+
sections: SummarySection[];
|
|
21
|
+
onEditStep: (stepIndex: number) => void;
|
|
22
|
+
labels?: Partial<WizardLabels>;
|
|
23
|
+
}) {
|
|
24
|
+
const l = useKitLabels("wizard", DEFAULT_WIZARD_LABELS, labels);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div className="space-y-4">
|
|
28
|
+
<h2 className="text-lg font-semibold text-[var(--text-primary)]">{l.reviewTitle}</h2>
|
|
29
|
+
{sections.map((section, sIdx) => (
|
|
30
|
+
<Card key={sIdx}>
|
|
31
|
+
{/* CardHeader is a grid, and CardAction is the top-right column it
|
|
32
|
+
reserves — the edit button has to go through it or it stacks under
|
|
33
|
+
the title instead of sitting beside it. */}
|
|
34
|
+
<CardHeader className="pb-2">
|
|
35
|
+
<CardTitle className="text-base">{section.label}</CardTitle>
|
|
36
|
+
<CardAction>
|
|
37
|
+
<IconButton
|
|
38
|
+
size="sm"
|
|
39
|
+
variant="ghost"
|
|
40
|
+
aria-label={l.edit}
|
|
41
|
+
title={l.edit}
|
|
42
|
+
onClick={() => onEditStep(section.stepIndex)}
|
|
43
|
+
>
|
|
44
|
+
<Pencil />
|
|
45
|
+
</IconButton>
|
|
46
|
+
</CardAction>
|
|
47
|
+
</CardHeader>
|
|
48
|
+
<CardContent>
|
|
49
|
+
<dl className="grid gap-2 text-sm">
|
|
50
|
+
{section.items.map((item, iIdx) => (
|
|
51
|
+
<div key={iIdx}>
|
|
52
|
+
{iIdx > 0 && (
|
|
53
|
+
<div className="mb-2 border-t border-[var(--border)]" role="presentation" />
|
|
54
|
+
)}
|
|
55
|
+
<div className="flex items-start justify-between gap-4">
|
|
56
|
+
<dt className="text-[var(--text-muted)]">{item.label}</dt>
|
|
57
|
+
<dd className="text-right font-medium text-[var(--text-primary)]">
|
|
58
|
+
{item.value}
|
|
59
|
+
</dd>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
))}
|
|
63
|
+
</dl>
|
|
64
|
+
</CardContent>
|
|
65
|
+
</Card>
|
|
66
|
+
))}
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
}
|
package/src/wizard.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// `@eifi1/ui-kit/wizard` — the multi-step engine, its chrome and its review step.
|
|
2
|
+
//
|
|
3
|
+
// A re-slicing of the main barrel, not a new API. This entry point requires
|
|
4
|
+
// `react-router`: useWizard syncs the active step to `?step=` via useSearchParams.
|
|
5
|
+
//
|
|
6
|
+
// NOT the same thing as `WizardStepper` in components/wizard-stepper, which is a bare
|
|
7
|
+
// two-step indicator with no engine and stays on the main barrel.
|
|
8
|
+
export * from "./wizard/types";
|
|
9
|
+
export * from "./wizard/use-wizard";
|
|
10
|
+
export * from "./wizard/wizard-context";
|
|
11
|
+
export * from "./wizard/validation";
|
|
12
|
+
export * from "./wizard/wizard-step";
|
|
13
|
+
export * from "./wizard/wizard-summary";
|
|
14
|
+
export * from "./wizard/stepper-nav";
|