@eifi1/ui-kit 0.7.0 → 0.8.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/README.md +32 -17
- package/dist/chart.d.ts +3 -2
- package/dist/components/alert-banner.d.ts +59 -10
- package/dist/components/alert-banner.js +114 -8
- package/dist/components/alert-banner.js.map +1 -1
- package/dist/components/amount-input.d.ts +5 -0
- package/dist/components/button-group.d.ts +26 -0
- package/dist/components/button-group.js +49 -0
- package/dist/components/button-group.js.map +1 -0
- package/dist/components/calculator.d.ts +5 -0
- package/dist/components/chart-zoom.d.ts +63 -14
- package/dist/components/chart-zoom.js +82 -16
- package/dist/components/chart-zoom.js.map +1 -1
- package/dist/components/chip.d.ts +67 -2
- package/dist/components/chip.js +73 -8
- package/dist/components/chip.js.map +1 -1
- package/dist/components/confirm-dialog.d.ts +120 -0
- package/dist/components/confirm-dialog.js +96 -0
- package/dist/components/confirm-dialog.js.map +1 -0
- package/dist/components/copy-button.d.ts +62 -0
- package/dist/components/copy-button.js +95 -0
- package/dist/components/copy-button.js.map +1 -0
- package/dist/components/data-table-filter-popover.d.ts +1 -1
- package/dist/components/data-table-filters.d.ts +1 -1
- package/dist/components/data-table-pagination.d.ts +5 -1
- package/dist/components/data-table-pagination.js +71 -58
- package/dist/components/data-table-pagination.js.map +1 -1
- package/dist/components/data-table-sort.d.ts +28 -5
- package/dist/components/data-table-sort.js +11 -7
- 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 +544 -423
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/date-picker.d.ts +51 -5
- package/dist/components/date-picker.js +152 -40
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/description-list.d.ts +60 -0
- package/dist/components/description-list.js +112 -0
- package/dist/components/description-list.js.map +1 -0
- package/dist/components/disclosure.d.ts +37 -2
- package/dist/components/disclosure.js +14 -5
- package/dist/components/disclosure.js.map +1 -1
- package/dist/components/facing-pair.d.ts +2 -0
- package/dist/components/file-button.d.ts +19 -1
- package/dist/components/file-button.js +8 -1
- package/dist/components/file-button.js.map +1 -1
- package/dist/components/file-dropzone.d.ts +56 -9
- package/dist/components/file-dropzone.js +100 -70
- package/dist/components/file-dropzone.js.map +1 -1
- package/dist/components/floating-panel.d.ts +107 -0
- package/dist/components/floating-panel.js +206 -0
- package/dist/components/floating-panel.js.map +1 -0
- package/dist/components/full-bleed-dialog.d.ts +12 -1
- package/dist/components/full-bleed-dialog.js +26 -5
- package/dist/components/full-bleed-dialog.js.map +1 -1
- package/dist/components/modal.d.ts +6 -2
- package/dist/components/modal.js +2 -1
- package/dist/components/modal.js.map +1 -1
- package/dist/components/number-field.d.ts +5 -0
- package/dist/components/number-input.d.ts +5 -0
- package/dist/components/numpad-sheet.d.ts +5 -0
- package/dist/components/progress-bar.d.ts +54 -0
- package/dist/components/progress-bar.js +94 -0
- package/dist/components/progress-bar.js.map +1 -0
- package/dist/components/scroll-area.d.ts +53 -0
- package/dist/components/scroll-area.js +86 -0
- package/dist/components/scroll-area.js.map +1 -0
- package/dist/components/separator.d.ts +23 -0
- package/dist/components/separator.js +24 -0
- package/dist/components/separator.js.map +1 -0
- package/dist/components/series-chart-ticks.d.ts +34 -1
- package/dist/components/series-chart-ticks.js +83 -1
- package/dist/components/series-chart-ticks.js.map +1 -1
- package/dist/components/series-chart.d.ts +314 -19
- package/dist/components/series-chart.js +510 -120
- package/dist/components/series-chart.js.map +1 -1
- package/dist/components/settings-fields.d.ts +2 -0
- package/dist/components/skeleton.d.ts +36 -0
- package/dist/components/skeleton.js +35 -0
- package/dist/components/skeleton.js.map +1 -0
- package/dist/components/stat-tile.d.ts +60 -6
- package/dist/components/stat-tile.js +60 -26
- package/dist/components/stat-tile.js.map +1 -1
- package/dist/components/table.d.ts +83 -0
- package/dist/components/table.js +166 -0
- package/dist/components/table.js.map +1 -0
- package/dist/components/time-input.d.ts +2 -0
- package/dist/components/toggle-legend.d.ts +3 -2
- package/dist/components/toggle-legend.js +1 -1
- package/dist/components/toggle-legend.js.map +1 -1
- package/dist/components/tooltip.d.ts +10 -4
- package/dist/components/tooltip.js +23 -6
- package/dist/components/tooltip.js.map +1 -1
- package/dist/components/tree-view.d.ts +129 -0
- package/dist/components/tree-view.js +376 -0
- package/dist/components/tree-view.js.map +1 -0
- package/dist/components/treemap.d.ts +9 -2
- package/dist/components/treemap.js +2 -1
- package/dist/components/treemap.js.map +1 -1
- package/dist/components/ui.d.ts +92 -14
- package/dist/components/ui.js +112 -30
- package/dist/components/ui.js.map +1 -1
- package/dist/components/use-table-state.d.ts +1 -1
- package/dist/{data-table-filters-noy0Abvi.d.ts → data-table-filters-CF1PXqjQ.d.ts} +73 -3
- package/dist/data-table.d.ts +2 -2
- package/dist/data-table.js.map +1 -1
- package/dist/hooks/use-copy-to-clipboard.d.ts +28 -0
- package/dist/hooks/use-copy-to-clipboard.js +81 -0
- package/dist/hooks/use-copy-to-clipboard.js.map +1 -0
- package/dist/hooks/use-debounce.d.ts +43 -0
- package/dist/hooks/use-debounce.js +56 -0
- package/dist/hooks/use-debounce.js.map +1 -0
- package/dist/hooks/use-file-drop.d.ts +72 -0
- package/dist/hooks/use-file-drop.js +57 -0
- package/dist/hooks/use-file-drop.js.map +1 -0
- package/dist/i18n/defaults.d.ts +5 -0
- package/dist/i18n/defaults.js +7 -1
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +17 -0
- package/dist/i18n/kit-labels.js +6 -2
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/locales/de-CH-informal.d.ts +43 -0
- package/dist/i18n/locales/de-CH-informal.js +8 -0
- package/dist/i18n/locales/de-CH-informal.js.map +1 -0
- package/dist/i18n/locales/de-CH.d.ts +5 -0
- package/dist/i18n/locales/de-CH.js +1 -14
- package/dist/i18n/locales/de-CH.js.map +1 -1
- package/dist/i18n/locales/de-informal.d.ts +64 -0
- package/dist/i18n/locales/de-informal.js +37 -0
- package/dist/i18n/locales/de-informal.js.map +1 -0
- package/dist/i18n/locales/de.d.ts +5 -0
- package/dist/i18n/locales/de.js +25 -0
- package/dist/i18n/locales/de.js.map +1 -1
- package/dist/i18n/locales/es.d.ts +5 -0
- package/dist/i18n/locales/es.js +25 -0
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts +5 -0
- package/dist/i18n/locales/fr.js +25 -0
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.d.ts +5 -0
- package/dist/i18n/locales/hu.js +25 -0
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.d.ts +5 -0
- package/dist/i18n/locales/it.js +25 -0
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.d.ts +5 -0
- package/dist/i18n/locales/zh.js +25 -0
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/swiss.d.ts +13 -0
- package/dist/i18n/swiss.js +19 -0
- package/dist/i18n/swiss.js.map +1 -0
- package/dist/index.d.ts +26 -11
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/dates.d.ts +37 -1
- package/dist/lib/dates.js +19 -0
- package/dist/lib/dates.js.map +1 -1
- package/dist/rhf/form.d.ts +2 -0
- package/dist/rhf.d.ts +2 -0
- package/dist/search/command-palette.d.ts +15 -1
- package/dist/search/command-palette.js +10 -2
- package/dist/search/command-palette.js.map +1 -1
- package/dist/shell/app-shell.js +2 -2
- package/dist/shell/app-shell.js.map +1 -1
- package/dist/wizard/stepper-nav.d.ts +35 -2
- package/dist/wizard/stepper-nav.js +54 -22
- package/dist/wizard/stepper-nav.js.map +1 -1
- package/dist/wizard/types.d.ts +107 -5
- package/dist/wizard/types.js +2 -1
- package/dist/wizard/types.js.map +1 -1
- package/dist/wizard/use-wizard.js +141 -21
- package/dist/wizard/use-wizard.js.map +1 -1
- package/dist/wizard/wizard-summary.d.ts +10 -2
- package/dist/wizard/wizard-summary.js +3 -1
- package/dist/wizard/wizard-summary.js.map +1 -1
- package/dist/wizard.d.ts +4 -1
- package/dist/wizard.js.map +1 -1
- package/package.json +1 -1
- package/src/components/alert-banner.tsx +211 -20
- package/src/components/button-group.tsx +75 -0
- package/src/components/chart-zoom.tsx +158 -20
- package/src/components/chip.tsx +160 -9
- package/src/components/confirm-dialog.tsx +242 -0
- package/src/components/copy-button.tsx +158 -0
- package/src/components/data-table-pagination.tsx +19 -6
- package/src/components/data-table-sort.ts +49 -10
- package/src/components/data-table.tsx +293 -24
- package/src/components/date-picker.tsx +262 -57
- package/src/components/description-list.tsx +174 -0
- package/src/components/disclosure.tsx +58 -5
- package/src/components/file-button.tsx +22 -2
- package/src/components/file-dropzone.tsx +198 -100
- package/src/components/floating-panel.tsx +341 -0
- package/src/components/full-bleed-dialog.tsx +48 -5
- package/src/components/modal.tsx +9 -2
- package/src/components/progress-bar.tsx +163 -0
- package/src/components/scroll-area.tsx +129 -0
- package/src/components/separator.tsx +37 -0
- package/src/components/series-chart-ticks.ts +135 -0
- package/src/components/series-chart.tsx +934 -67
- package/src/components/skeleton.tsx +65 -0
- package/src/components/stat-tile.tsx +160 -27
- package/src/components/table.tsx +263 -0
- package/src/components/toggle-legend.tsx +4 -3
- package/src/components/tooltip.tsx +40 -12
- package/src/components/tree-view.tsx +589 -0
- package/src/components/treemap.tsx +10 -2
- package/src/components/ui.tsx +268 -42
- package/src/data-table.ts +2 -0
- package/src/hooks/use-copy-to-clipboard.ts +122 -0
- package/src/hooks/use-debounce.ts +105 -0
- package/src/hooks/use-file-drop.ts +123 -0
- package/src/i18n/defaults.ts +6 -0
- package/src/i18n/kit-labels.tsx +19 -0
- package/src/i18n/locales/de-CH-informal.ts +10 -0
- package/src/i18n/locales/de-CH.ts +1 -16
- package/src/i18n/locales/de-informal.ts +61 -0
- package/src/i18n/locales/de.ts +25 -0
- package/src/i18n/locales/es.ts +25 -0
- package/src/i18n/locales/fr.ts +25 -0
- package/src/i18n/locales/hu.ts +25 -0
- package/src/i18n/locales/it.ts +25 -0
- package/src/i18n/locales/zh.ts +25 -0
- package/src/i18n/swiss.ts +25 -0
- package/src/index.ts +29 -0
- package/src/lib/dates.ts +48 -0
- package/src/search/command-palette.tsx +29 -3
- package/src/shell/app-shell.tsx +2 -2
- package/src/wizard/stepper-nav.tsx +102 -28
- package/src/wizard/types.ts +106 -4
- package/src/wizard/use-wizard.ts +211 -31
- package/src/wizard/wizard-summary.tsx +24 -12
- package/src/wizard.ts +3 -2
package/src/wizard/types.ts
CHANGED
|
@@ -24,13 +24,83 @@ export interface WizardStepConfig {
|
|
|
24
24
|
* per-field error messages via `wizard.fieldErrors`.
|
|
25
25
|
*/
|
|
26
26
|
validate?: () => ValidateResult | Promise<ValidateResult>;
|
|
27
|
+
/**
|
|
28
|
+
* The forward button's label on THIS step, in place of `labels.next` — "Analyse",
|
|
29
|
+
* "Import", "Send code". On the committing step (see {@link commits}) it labels
|
|
30
|
+
* Finish instead of `labels.finish`; `labels.submitting` still replaces it while
|
|
31
|
+
* the commit runs. Like `label`, a node the app has already translated.
|
|
32
|
+
*/
|
|
33
|
+
nextLabel?: ReactNode;
|
|
34
|
+
/**
|
|
35
|
+
* This step's forward button COMMITS: it runs `onComplete` (via `finish`) instead
|
|
36
|
+
* of just advancing. Without the flag the last step commits, as it always has.
|
|
37
|
+
*
|
|
38
|
+
* Flag a step before the last and every step after it becomes a post-commit step —
|
|
39
|
+
* an import's "here is what happened, set up recurring rules?" page:
|
|
40
|
+
* - a successful `onComplete` advances onto the next step (a failed one stays, with
|
|
41
|
+
* `wizard.error`, exactly like a last-step Finish);
|
|
42
|
+
* - from there Back, the indicator and `goBack`/`goToStep` cannot cross back over
|
|
43
|
+
* the commit, and Cancel is hidden — there is nothing left to discard;
|
|
44
|
+
* - the last post-commit step's button is Done, which calls `onDone` (and is not
|
|
45
|
+
* shown when there is no `onDone`: the step then carries its own way out).
|
|
46
|
+
*
|
|
47
|
+
* Only the FIRST flagged step counts. Steps after it may still validate, skip and
|
|
48
|
+
* use `nextLabel` like any other.
|
|
49
|
+
*/
|
|
50
|
+
commits?: boolean;
|
|
27
51
|
}
|
|
28
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Where the active step lives. `true` (the default) mirrors it to `?step=N` through
|
|
55
|
+
* react-router, as every wizard before 0.8 did; `{ param }` renames that param (two
|
|
56
|
+
* wizards on one page, or a page that already uses `step`); `false` keeps it in
|
|
57
|
+
* memory only and needs no router at all.
|
|
58
|
+
*
|
|
59
|
+
* Read once, at mount: switching it on a mounted wizard has no effect.
|
|
60
|
+
*/
|
|
61
|
+
export type WizardUrlSync = boolean | { param?: string };
|
|
62
|
+
|
|
29
63
|
export interface UseWizardOptions<TData extends Record<string, unknown>> {
|
|
30
64
|
steps: WizardStepConfig[];
|
|
31
65
|
initialData?: Partial<TData>;
|
|
32
66
|
onComplete?: (data: TData) => void | Promise<void>;
|
|
33
67
|
onCancel?: () => void;
|
|
68
|
+
/**
|
|
69
|
+
* Leave the wizard BACKWARDS: when given, Back is shown on the first step too and
|
|
70
|
+
* calls this — keksdose's import panel, whose first step's Back returns to the
|
|
71
|
+
* dropzone the file came from. No confirm dialog: going back is not discarding.
|
|
72
|
+
*/
|
|
73
|
+
onExit?: () => void;
|
|
74
|
+
/**
|
|
75
|
+
* The Done button on the last step after a committing step (see
|
|
76
|
+
* {@link WizardStepConfig.commits}). Without it no Done button is rendered, and the
|
|
77
|
+
* post-commit step is expected to carry its own way out.
|
|
78
|
+
*/
|
|
79
|
+
onDone?: () => void;
|
|
80
|
+
/**
|
|
81
|
+
* Whether the chrome offers Cancel at all. Default `true`. `false` hides the button
|
|
82
|
+
* (`wizard.canCancel` is false); `wizard.cancel()` still works if the app calls it.
|
|
83
|
+
*/
|
|
84
|
+
cancellable?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Whether Cancel asks first ("Discard this form?"). Default `true`. `false` makes
|
|
87
|
+
* `cancel()` call `onCancel` straight away — for a wizard that has nothing to lose
|
|
88
|
+
* yet, or one whose data is kept elsewhere.
|
|
89
|
+
*/
|
|
90
|
+
confirmCancel?: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Mirror the active step to the URL. Default `true` (`?step=N`, needs a router).
|
|
93
|
+
* See {@link WizardUrlSync}.
|
|
94
|
+
*
|
|
95
|
+
* The param is WRITTEN only, never read back (see the note in `use-wizard.ts`). It
|
|
96
|
+
* is removed again when the wizard is left through the kit: a confirmed Cancel,
|
|
97
|
+
* `onExit` from the first step's Back, and Done — each before the app's callback
|
|
98
|
+
* runs, so an app that navigates away navigates from a clean URL. It is NOT removed
|
|
99
|
+
* after `onComplete` on a last step: an app navigating away there would otherwise
|
|
100
|
+
* be pulled back by the kit's own URL write. An app that stays on the page after a
|
|
101
|
+
* final Finish drops the param itself, or uses `urlSync: false`.
|
|
102
|
+
*/
|
|
103
|
+
urlSync?: WizardUrlSync;
|
|
34
104
|
/**
|
|
35
105
|
* Shown when a step blocks with no per-field detail — an RHF step surfacing
|
|
36
106
|
* its own inline messages, say — so the user learns why the wizard did not
|
|
@@ -55,14 +125,35 @@ export interface UseWizardReturn<TData extends Record<string, unknown>> {
|
|
|
55
125
|
steps: WizardStepConfig[];
|
|
56
126
|
isFirstStep: boolean;
|
|
57
127
|
isLastStep: boolean;
|
|
58
|
-
/** True once every step before the
|
|
128
|
+
/** True once every step before the committing one (the last, unless a step is
|
|
129
|
+
* flagged `commits`) has been completed or skipped. */
|
|
59
130
|
canFinish: boolean;
|
|
131
|
+
/** The step whose forward button commits: the first `commits` step, else the last. */
|
|
132
|
+
commitStepIndex: number;
|
|
133
|
+
/** The active step is {@link commitStepIndex}: the chrome shows Finish, not Next. */
|
|
134
|
+
isCommitStep: boolean;
|
|
135
|
+
/** `onComplete` has resolved. Once the active step is past the committing one, Back
|
|
136
|
+
* cannot cross it and Cancel is hidden. */
|
|
137
|
+
committed: boolean;
|
|
138
|
+
/** Whether Back is offered here: not on the first step unless `onExit` was given,
|
|
139
|
+
* and never back across a commit. (Disabled, not hidden, while `isSubmitting`.) */
|
|
140
|
+
canGoBack: boolean;
|
|
141
|
+
/** Whether Cancel is offered: `cancellable`, and not after a commit. */
|
|
142
|
+
canCancel: boolean;
|
|
143
|
+
/** Whether Done is offered: the last step, past a commit, with an `onDone`. */
|
|
144
|
+
canDone: boolean;
|
|
145
|
+
/** Whether the indicator may jump to `index` — a completed/skipped/active step on
|
|
146
|
+
* the right side of any commit. `goToStep` refuses anything this rejects. */
|
|
147
|
+
canGoToStep: (index: number) => boolean;
|
|
60
148
|
goNext: () => Promise<void>;
|
|
149
|
+
/** One step back — or, on the first step, `onExit`. Refused while submitting. */
|
|
61
150
|
goBack: () => void;
|
|
62
151
|
goToStep: (index: number) => void;
|
|
63
152
|
skip: () => void;
|
|
64
153
|
cancel: () => void;
|
|
65
154
|
finish: () => Promise<void>;
|
|
155
|
+
/** Leave after the post-commit steps: drops the URL param, then calls `onDone`. */
|
|
156
|
+
done: () => void;
|
|
66
157
|
/** Register a validator for the mounted step (used by useRhfWizardStep). */
|
|
67
158
|
registerStepValidate: (
|
|
68
159
|
fn: () => ValidateResult | Promise<ValidateResult>,
|
|
@@ -82,6 +173,9 @@ export interface UseWizardReturn<TData extends Record<string, unknown>> {
|
|
|
82
173
|
|
|
83
174
|
// Loading / error
|
|
84
175
|
isSubmitting: boolean;
|
|
176
|
+
/** A `goNext`/`finish` is running the step's validators (an async server check,
|
|
177
|
+
* say). The chrome shows Next pending — spinner, `aria-busy`, disabled. */
|
|
178
|
+
isValidating: boolean;
|
|
85
179
|
error: string | null;
|
|
86
180
|
clearError: () => void;
|
|
87
181
|
|
|
@@ -102,8 +196,12 @@ export interface SummaryItem {
|
|
|
102
196
|
|
|
103
197
|
export interface SummarySection {
|
|
104
198
|
label: ReactNode;
|
|
105
|
-
/**
|
|
106
|
-
|
|
199
|
+
/**
|
|
200
|
+
* Which step the section's edit button jumps back to. Leave it out for a section
|
|
201
|
+
* that belongs to no step — file-level counts, a total — and the card has no edit
|
|
202
|
+
* button, instead of borrowing one from whichever step it was parked under.
|
|
203
|
+
*/
|
|
204
|
+
stepIndex?: number;
|
|
107
205
|
items: SummaryItem[];
|
|
108
206
|
}
|
|
109
207
|
|
|
@@ -149,12 +247,15 @@ export interface WizardLabels {
|
|
|
149
247
|
/** `wizard.error` when `onComplete` rejects with nothing readable — a thrown
|
|
150
248
|
* non-Error, or an Error whose message is empty. Optional: see above. */
|
|
151
249
|
genericError?: string;
|
|
250
|
+
/** The button on the last step after a committing step (`onDone`). Optional: see
|
|
251
|
+
* `missingRequired`. */
|
|
252
|
+
done?: string;
|
|
152
253
|
}
|
|
153
254
|
|
|
154
255
|
/** Typed with the two optional keys made required, so code reading the defaults
|
|
155
256
|
* directly (the fallback in `useWizard`) needs no `!`. */
|
|
156
257
|
export const DEFAULT_WIZARD_LABELS: WizardLabels &
|
|
157
|
-
Required<Pick<WizardLabels, "missingRequired" | "genericError">> = {
|
|
258
|
+
Required<Pick<WizardLabels, "missingRequired" | "genericError" | "done">> = {
|
|
158
259
|
cancel: "Cancel",
|
|
159
260
|
back: "Back",
|
|
160
261
|
next: "Next",
|
|
@@ -171,6 +272,7 @@ export const DEFAULT_WIZARD_LABELS: WizardLabels &
|
|
|
171
272
|
edit: "Edit",
|
|
172
273
|
missingRequired: "Please fill in all required fields.",
|
|
173
274
|
genericError: "An error occurred",
|
|
275
|
+
done: "Done",
|
|
174
276
|
};
|
|
175
277
|
|
|
176
278
|
export function resolveWizardLabels(labels?: Partial<WizardLabels>): WizardLabels {
|
package/src/wizard/use-wizard.ts
CHANGED
|
@@ -3,10 +3,16 @@
|
|
|
3
3
|
* per-step async validators, collected data, field errors, submit and the
|
|
4
4
|
* cancel-confirm gate.
|
|
5
5
|
*
|
|
6
|
-
* Domain-free,
|
|
7
|
-
* it must be mounted inside a react-router context — the same
|
|
8
|
-
* `DataTable` already carries.
|
|
9
|
-
*
|
|
6
|
+
* Domain-free, and router-free on request. By default it mirrors the active step
|
|
7
|
+
* to `?step=N`, so it must be mounted inside a react-router context — the same
|
|
8
|
+
* requirement `DataTable` already carries. `urlSync: false` keeps the step in
|
|
9
|
+
* memory and needs no router; `urlSync: { param }` renames the param. It only ever
|
|
10
|
+
* WRITES that param (see the note on `initialStep` below for why it deliberately
|
|
11
|
+
* does not read it back), and removes it when the wizard is left through the kit
|
|
12
|
+
* (see `UseWizardOptions.urlSync`).
|
|
13
|
+
*
|
|
14
|
+
* The committing step is the last one unless a step says `commits: true`; the
|
|
15
|
+
* steps after that one are post-commit steps (see `WizardStepConfig.commits`).
|
|
10
16
|
*/
|
|
11
17
|
import { useCallback, useEffect, useReducer, useRef, useState } from "react";
|
|
12
18
|
import { useSearchParams } from "react-router";
|
|
@@ -27,9 +33,26 @@ interface WizardState<TData> {
|
|
|
27
33
|
skippedSteps: Set<number>;
|
|
28
34
|
data: TData;
|
|
29
35
|
isSubmitting: boolean;
|
|
36
|
+
isValidating: boolean;
|
|
30
37
|
error: string | null;
|
|
31
38
|
fieldErrors: FieldErrors;
|
|
32
39
|
showCancelDialog: boolean;
|
|
40
|
+
/** The step whose `onComplete` resolved, or null before any commit. */
|
|
41
|
+
committedStepIndex: number | null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Past the commit: the active step comes after the step that committed. Moves to
|
|
45
|
+
* that step or any before it are refused from here. */
|
|
46
|
+
function isAfterCommit(state: { currentStepIndex: number; committedStepIndex: number | null }) {
|
|
47
|
+
return state.committedStepIndex !== null && state.currentStepIndex > state.committedStepIndex;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Whether `index` is on the far side of a commit the wizard has already moved past. */
|
|
51
|
+
function crossesCommit(
|
|
52
|
+
state: { currentStepIndex: number; committedStepIndex: number | null },
|
|
53
|
+
index: number,
|
|
54
|
+
) {
|
|
55
|
+
return isAfterCommit(state) && index <= (state.committedStepIndex as number);
|
|
33
56
|
}
|
|
34
57
|
|
|
35
58
|
type WizardAction<TData> =
|
|
@@ -41,7 +64,9 @@ type WizardAction<TData> =
|
|
|
41
64
|
| { type: "GO_TO_STEP"; index: number }
|
|
42
65
|
| { type: "SKIP"; from: number }
|
|
43
66
|
| { type: "UPDATE_DATA"; partial: Partial<TData> }
|
|
67
|
+
| { type: "COMMITTED"; from: number }
|
|
44
68
|
| { type: "SET_SUBMITTING"; value: boolean }
|
|
69
|
+
| { type: "SET_VALIDATING"; value: boolean }
|
|
45
70
|
| { type: "SET_ERROR"; error: string | null }
|
|
46
71
|
| { type: "SET_FIELD_ERRORS"; errors: FieldErrors }
|
|
47
72
|
| { type: "SHOW_CANCEL_DIALOG"; show: boolean };
|
|
@@ -61,10 +86,12 @@ function createReducer<TData>(stepCount: number) {
|
|
|
61
86
|
}
|
|
62
87
|
case "GO_BACK": {
|
|
63
88
|
const prevIndex = Math.max(state.currentStepIndex - 1, 0);
|
|
89
|
+
if (crossesCommit(state, prevIndex)) return state;
|
|
64
90
|
return { ...state, currentStepIndex: prevIndex, fieldErrors: {} };
|
|
65
91
|
}
|
|
66
92
|
case "GO_TO_STEP": {
|
|
67
93
|
if (action.index < 0 || action.index >= stepCount) return state;
|
|
94
|
+
if (crossesCommit(state, action.index)) return state;
|
|
68
95
|
if (!state.completedSteps.has(action.index) && action.index > state.currentStepIndex) {
|
|
69
96
|
return state;
|
|
70
97
|
}
|
|
@@ -86,8 +113,27 @@ function createReducer<TData>(stepCount: number) {
|
|
|
86
113
|
}
|
|
87
114
|
return { ...state, data: { ...state.data, ...action.partial }, fieldErrors: nextErrors };
|
|
88
115
|
}
|
|
116
|
+
case "COMMITTED": {
|
|
117
|
+
// Recorded whatever step committed, so `wizard.committed` reads true after any
|
|
118
|
+
// successful Finish. Only a commit with steps after it MOVES: the default
|
|
119
|
+
// last-step commit stays where it is and looks exactly as it did before 0.8.
|
|
120
|
+
const committed = { ...state, committedStepIndex: action.from };
|
|
121
|
+
if (action.from !== state.currentStepIndex || action.from >= stepCount - 1) {
|
|
122
|
+
return committed;
|
|
123
|
+
}
|
|
124
|
+
const completed = new Set(state.completedSteps);
|
|
125
|
+
completed.add(action.from);
|
|
126
|
+
return {
|
|
127
|
+
...committed,
|
|
128
|
+
currentStepIndex: action.from + 1,
|
|
129
|
+
completedSteps: completed,
|
|
130
|
+
fieldErrors: {},
|
|
131
|
+
};
|
|
132
|
+
}
|
|
89
133
|
case "SET_SUBMITTING":
|
|
90
134
|
return { ...state, isSubmitting: action.value };
|
|
135
|
+
case "SET_VALIDATING":
|
|
136
|
+
return { ...state, isValidating: action.value };
|
|
91
137
|
case "SET_ERROR":
|
|
92
138
|
return { ...state, error: action.error };
|
|
93
139
|
case "SET_FIELD_ERRORS":
|
|
@@ -103,8 +149,19 @@ function createReducer<TData>(stepCount: number) {
|
|
|
103
149
|
export function useWizard<TData extends Record<string, unknown>>(
|
|
104
150
|
options: UseWizardOptions<TData>,
|
|
105
151
|
): UseWizardReturn<TData> {
|
|
106
|
-
const {
|
|
107
|
-
|
|
152
|
+
const {
|
|
153
|
+
steps,
|
|
154
|
+
initialData,
|
|
155
|
+
onComplete,
|
|
156
|
+
onCancel,
|
|
157
|
+
onExit,
|
|
158
|
+
onDone,
|
|
159
|
+
cancellable = true,
|
|
160
|
+
confirmCancel: askBeforeCancel = true,
|
|
161
|
+
urlSync = true,
|
|
162
|
+
missingRequiredMessage,
|
|
163
|
+
onValidationFailed,
|
|
164
|
+
} = options;
|
|
108
165
|
const stepsRef = useRef<WizardStepConfig[]>(steps);
|
|
109
166
|
stepsRef.current = steps;
|
|
110
167
|
// A hook, not a component, but it produces two sentences of its own — the
|
|
@@ -138,7 +195,13 @@ export function useWizard<TData extends Record<string, unknown>>(
|
|
|
138
195
|
[],
|
|
139
196
|
);
|
|
140
197
|
|
|
141
|
-
|
|
198
|
+
// Chosen once, at mount: a hook may not be called conditionally, but it may be
|
|
199
|
+
// chosen once and then called unconditionally on every render — which is what
|
|
200
|
+
// makes `urlSync: false` work with no router above the wizard at all.
|
|
201
|
+
const [useStepUrl] = useState(() => (urlSync === false ? useNoStepUrl : useSearchParamStepUrl));
|
|
202
|
+
const [urlParam] = useState(() =>
|
|
203
|
+
typeof urlSync === "object" && urlSync.param ? urlSync.param : "step",
|
|
204
|
+
);
|
|
142
205
|
// The wizard's entered `data` and `completedSteps` are NOT persisted across a
|
|
143
206
|
// reload / deep-link (the reducer re-inits to empty defaults on every mount).
|
|
144
207
|
// Restoring a later step index from `?step=N` would therefore leave the wizard
|
|
@@ -154,9 +217,11 @@ export function useWizard<TData extends Record<string, unknown>>(
|
|
|
154
217
|
skippedSteps: new Set<number>(),
|
|
155
218
|
data: (initialData ?? {}) as TData,
|
|
156
219
|
isSubmitting: false,
|
|
220
|
+
isValidating: false,
|
|
157
221
|
error: null,
|
|
158
222
|
fieldErrors: {},
|
|
159
223
|
showCancelDialog: false,
|
|
224
|
+
committedStepIndex: null,
|
|
160
225
|
});
|
|
161
226
|
|
|
162
227
|
// A mounted step can DISABLE forward navigation (Next/Skip) while its own
|
|
@@ -168,26 +233,36 @@ export function useWizard<TData extends Record<string, unknown>>(
|
|
|
168
233
|
[],
|
|
169
234
|
);
|
|
170
235
|
|
|
171
|
-
// Sync step index to URL
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
return prev;
|
|
176
|
-
}, { replace: true });
|
|
177
|
-
}, [state.currentStepIndex, setUrlParams]);
|
|
236
|
+
// Sync step index to URL (a no-op under `urlSync: false`). `clearStepUrl` drops
|
|
237
|
+
// the param when the wizard is left through the kit, and stops the sync writing
|
|
238
|
+
// it back.
|
|
239
|
+
const clearStepUrl = useStepUrl(urlParam, state.currentStepIndex);
|
|
178
240
|
|
|
179
|
-
|
|
241
|
+
// `steps`, not `stepsRef.current`: during render the two are the same array, and
|
|
242
|
+
// reading a ref in render is what the hooks linter (rightly) objects to.
|
|
243
|
+
const currentStep = steps[state.currentStepIndex];
|
|
180
244
|
const isFirstStep = state.currentStepIndex === 0;
|
|
181
|
-
const isLastStep = state.currentStepIndex ===
|
|
182
|
-
//
|
|
183
|
-
//
|
|
245
|
+
const isLastStep = state.currentStepIndex === steps.length - 1;
|
|
246
|
+
// The step whose forward button commits: the first one flagged `commits`, else the
|
|
247
|
+
// last — which is every wizard written before the flag existed.
|
|
248
|
+
const flagged = steps.findIndex((s) => s.commits);
|
|
249
|
+
const commitStepIndex = flagged === -1 ? steps.length - 1 : flagged;
|
|
250
|
+
const isCommitStep = state.currentStepIndex === commitStepIndex;
|
|
251
|
+
const afterCommit = isAfterCommit(state);
|
|
252
|
+
// Finish is only valid once every step before the committing one has actually
|
|
253
|
+
// been walked through (completed or skipped). Guards against a restored/last-step
|
|
184
254
|
// index submitting a payload built from empty defaults.
|
|
185
|
-
const canFinish =
|
|
255
|
+
const canFinish = steps.every(
|
|
186
256
|
(_, index) =>
|
|
187
|
-
index
|
|
257
|
+
index >= commitStepIndex ||
|
|
188
258
|
state.completedSteps.has(index) ||
|
|
189
259
|
state.skippedSteps.has(index),
|
|
190
260
|
);
|
|
261
|
+
const canGoBack =
|
|
262
|
+
(!isFirstStep || onExit !== undefined) &&
|
|
263
|
+
!crossesCommit(state, state.currentStepIndex - 1);
|
|
264
|
+
const canCancel = cancellable && !afterCommit;
|
|
265
|
+
const canDone = isLastStep && afterCommit && onDone !== undefined;
|
|
191
266
|
|
|
192
267
|
// Runs the active step's config `validate` plus every validator its mounted
|
|
193
268
|
// components registered (RHF steps via useRhfWizardStep), AND-combining the
|
|
@@ -266,23 +341,50 @@ export function useWizard<TData extends Record<string, unknown>>(
|
|
|
266
341
|
if (advancingRef.current) return;
|
|
267
342
|
advancingRef.current = true;
|
|
268
343
|
const from = state.currentStepIndex;
|
|
344
|
+
// Pending state for the chrome (spinner, aria-busy). The ref above is still what
|
|
345
|
+
// refuses the second click: state would only be seen after a re-render.
|
|
346
|
+
dispatch({ type: "SET_VALIDATING", value: true });
|
|
269
347
|
try {
|
|
270
348
|
if (await runStepValidators()) {
|
|
271
349
|
dispatch({ type: "GO_NEXT", from });
|
|
272
350
|
}
|
|
273
351
|
} finally {
|
|
352
|
+
dispatch({ type: "SET_VALIDATING", value: false });
|
|
274
353
|
advancingRef.current = false;
|
|
275
354
|
}
|
|
276
355
|
}, [runStepValidators, state.currentStepIndex]);
|
|
277
356
|
|
|
357
|
+
// Read by `goBack`, which must not move while `onComplete` runs: the commit was
|
|
358
|
+
// decided on the data of the step the user is on, and Back mid-commit would show a
|
|
359
|
+
// step whose edits can no longer reach it. The chrome disables the button too; this
|
|
360
|
+
// covers an app calling `goBack` itself.
|
|
361
|
+
const submittingRef = useRef(false);
|
|
362
|
+
|
|
278
363
|
const goBack = useCallback(() => {
|
|
364
|
+
if (submittingRef.current) return;
|
|
365
|
+
if (state.currentStepIndex === 0 && onExit) {
|
|
366
|
+
clearStepUrl();
|
|
367
|
+
onExit();
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
// On step 0 with no `onExit` this stays the no-op move (clearing field errors) it
|
|
371
|
+
// always was.
|
|
279
372
|
dispatch({ type: "GO_BACK" });
|
|
280
|
-
}, []);
|
|
373
|
+
}, [state.currentStepIndex, onExit, clearStepUrl]);
|
|
281
374
|
|
|
282
375
|
const goToStep = useCallback((index: number) => {
|
|
283
376
|
dispatch({ type: "GO_TO_STEP", index });
|
|
284
377
|
}, []);
|
|
285
378
|
|
|
379
|
+
const canGoToStep = useCallback(
|
|
380
|
+
(index: number) =>
|
|
381
|
+
index >= 0 &&
|
|
382
|
+
index < steps.length &&
|
|
383
|
+
(index <= state.currentStepIndex || state.completedSteps.has(index)) &&
|
|
384
|
+
!crossesCommit(state, index),
|
|
385
|
+
[steps.length, state],
|
|
386
|
+
);
|
|
387
|
+
|
|
286
388
|
const skip = useCallback(() => {
|
|
287
389
|
// Not while Next is validating: the skip would land first and the pending GO_NEXT
|
|
288
390
|
// would then be stale anyway — but the user asked for one move, not a race.
|
|
@@ -290,14 +392,24 @@ export function useWizard<TData extends Record<string, unknown>>(
|
|
|
290
392
|
dispatch({ type: "SKIP", from: state.currentStepIndex });
|
|
291
393
|
}, [state.currentStepIndex]);
|
|
292
394
|
|
|
293
|
-
const cancel = useCallback(() => {
|
|
294
|
-
dispatch({ type: "SHOW_CANCEL_DIALOG", show: true });
|
|
295
|
-
}, []);
|
|
296
|
-
|
|
297
395
|
const confirmCancel = useCallback(() => {
|
|
298
396
|
dispatch({ type: "SHOW_CANCEL_DIALOG", show: false });
|
|
397
|
+
clearStepUrl();
|
|
299
398
|
onCancel?.();
|
|
300
|
-
}, [onCancel]);
|
|
399
|
+
}, [onCancel, clearStepUrl]);
|
|
400
|
+
|
|
401
|
+
const cancel = useCallback(() => {
|
|
402
|
+
if (!askBeforeCancel) {
|
|
403
|
+
confirmCancel();
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
dispatch({ type: "SHOW_CANCEL_DIALOG", show: true });
|
|
407
|
+
}, [askBeforeCancel, confirmCancel]);
|
|
408
|
+
|
|
409
|
+
const done = useCallback(() => {
|
|
410
|
+
clearStepUrl();
|
|
411
|
+
onDone?.();
|
|
412
|
+
}, [onDone, clearStepUrl]);
|
|
301
413
|
|
|
302
414
|
const dismissCancel = useCallback(() => {
|
|
303
415
|
dispatch({ type: "SHOW_CANCEL_DIALOG", show: false });
|
|
@@ -317,29 +429,55 @@ export function useWizard<TData extends Record<string, unknown>>(
|
|
|
317
429
|
|
|
318
430
|
const finish = useCallback(async () => {
|
|
319
431
|
if (!onComplete || !canFinish) return;
|
|
432
|
+
// A commit with steps after it happens once: those steps exist because the
|
|
433
|
+
// data has been written. (A last-step commit keeps its old behaviour — the app
|
|
434
|
+
// may retry Finish after `onComplete` resolved, as it always could.)
|
|
435
|
+
if (commitStepIndex < steps.length - 1 && state.committedStepIndex !== null) return;
|
|
320
436
|
// The same re-entry guard as `goNext`: a double-click on Finish would otherwise
|
|
321
437
|
// validate twice and call `onComplete` twice — `isSubmitting` is only set once
|
|
322
438
|
// validation has passed, too late to stop the second click.
|
|
323
439
|
if (advancingRef.current) return;
|
|
324
440
|
advancingRef.current = true;
|
|
441
|
+
const from = state.currentStepIndex;
|
|
325
442
|
try {
|
|
326
|
-
// Validate the (possibly form-bearing)
|
|
327
|
-
// the Next gate — review-only steps register no validators and pass
|
|
328
|
-
|
|
443
|
+
// Validate the (possibly form-bearing) committing step before submitting,
|
|
444
|
+
// matching the Next gate — review-only steps register no validators and pass
|
|
445
|
+
// through.
|
|
446
|
+
dispatch({ type: "SET_VALIDATING", value: true });
|
|
447
|
+
let valid: boolean;
|
|
448
|
+
try {
|
|
449
|
+
valid = await runStepValidators();
|
|
450
|
+
} finally {
|
|
451
|
+
dispatch({ type: "SET_VALIDATING", value: false });
|
|
452
|
+
}
|
|
453
|
+
if (!valid) return;
|
|
454
|
+
submittingRef.current = true;
|
|
329
455
|
dispatch({ type: "SET_SUBMITTING", value: true });
|
|
330
456
|
dispatch({ type: "SET_ERROR", error: null });
|
|
331
457
|
try {
|
|
332
458
|
await onComplete(state.data);
|
|
459
|
+
dispatch({ type: "COMMITTED", from });
|
|
333
460
|
} catch (err) {
|
|
334
461
|
const message = err instanceof Error && err.message ? err.message : genericError;
|
|
335
462
|
dispatch({ type: "SET_ERROR", error: message });
|
|
336
463
|
} finally {
|
|
464
|
+
submittingRef.current = false;
|
|
337
465
|
dispatch({ type: "SET_SUBMITTING", value: false });
|
|
338
466
|
}
|
|
339
467
|
} finally {
|
|
340
468
|
advancingRef.current = false;
|
|
341
469
|
}
|
|
342
|
-
}, [
|
|
470
|
+
}, [
|
|
471
|
+
onComplete,
|
|
472
|
+
canFinish,
|
|
473
|
+
commitStepIndex,
|
|
474
|
+
steps.length,
|
|
475
|
+
state.committedStepIndex,
|
|
476
|
+
state.currentStepIndex,
|
|
477
|
+
state.data,
|
|
478
|
+
runStepValidators,
|
|
479
|
+
genericError,
|
|
480
|
+
]);
|
|
343
481
|
|
|
344
482
|
const stepStatus = useCallback(
|
|
345
483
|
(index: number): StepStatus => {
|
|
@@ -354,16 +492,24 @@ export function useWizard<TData extends Record<string, unknown>>(
|
|
|
354
492
|
return {
|
|
355
493
|
currentStepIndex: state.currentStepIndex,
|
|
356
494
|
currentStep,
|
|
357
|
-
steps
|
|
495
|
+
steps,
|
|
358
496
|
isFirstStep,
|
|
359
497
|
isLastStep,
|
|
360
498
|
canFinish,
|
|
499
|
+
commitStepIndex,
|
|
500
|
+
isCommitStep,
|
|
501
|
+
committed: state.committedStepIndex !== null,
|
|
502
|
+
canGoBack,
|
|
503
|
+
canCancel,
|
|
504
|
+
canDone,
|
|
505
|
+
canGoToStep,
|
|
361
506
|
goNext,
|
|
362
507
|
goBack,
|
|
363
508
|
goToStep,
|
|
364
509
|
skip,
|
|
365
510
|
cancel,
|
|
366
511
|
finish,
|
|
512
|
+
done,
|
|
367
513
|
registerStepValidate,
|
|
368
514
|
nextBlocked,
|
|
369
515
|
setNextBlocked,
|
|
@@ -372,6 +518,7 @@ export function useWizard<TData extends Record<string, unknown>>(
|
|
|
372
518
|
data: state.data,
|
|
373
519
|
updateData,
|
|
374
520
|
isSubmitting: state.isSubmitting,
|
|
521
|
+
isValidating: state.isValidating,
|
|
375
522
|
error: state.error,
|
|
376
523
|
clearError,
|
|
377
524
|
fieldErrors: state.fieldErrors,
|
|
@@ -381,3 +528,36 @@ export function useWizard<TData extends Record<string, unknown>>(
|
|
|
381
528
|
dismissCancel,
|
|
382
529
|
};
|
|
383
530
|
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* `urlSync: true` / `{ param }`: mirror the active step to `?<param>=N`, and hand back
|
|
534
|
+
* the function that removes it again. Once removed it stays removed — the sync effect
|
|
535
|
+
* re-runs whenever react-router hands out a new setter (every search change), and
|
|
536
|
+
* without the latch it would write the param straight back after the exit cleared it.
|
|
537
|
+
*/
|
|
538
|
+
function useSearchParamStepUrl(param: string, index: number): () => void {
|
|
539
|
+
const [, setUrlParams] = useSearchParams();
|
|
540
|
+
const leftRef = useRef(false);
|
|
541
|
+
useEffect(() => {
|
|
542
|
+
if (leftRef.current) return;
|
|
543
|
+
setUrlParams((prev) => {
|
|
544
|
+
prev.set(param, String(index));
|
|
545
|
+
return prev;
|
|
546
|
+
}, { replace: true });
|
|
547
|
+
}, [param, index, setUrlParams]);
|
|
548
|
+
return useCallback(() => {
|
|
549
|
+
if (leftRef.current) return;
|
|
550
|
+
leftRef.current = true;
|
|
551
|
+
setUrlParams((prev) => {
|
|
552
|
+
prev.delete(param);
|
|
553
|
+
return prev;
|
|
554
|
+
}, { replace: true });
|
|
555
|
+
}, [param, setUrlParams]);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/** `urlSync: false`: the step lives in the reducer only. Touches no router context. */
|
|
559
|
+
function useNoStepUrl(_param: string, _index: number): () => void {
|
|
560
|
+
return noop;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function noop() {}
|
|
@@ -11,14 +11,23 @@ import { useKitLabels } from "../i18n/kit-labels";
|
|
|
11
11
|
* Sections are data, not children, because "jump back to step N" is the whole
|
|
12
12
|
* point of the component and a caller assembling its own cards invariably drops
|
|
13
13
|
* it. Labels and values are `ReactNode`, so a value can be a badge or a link.
|
|
14
|
+
*
|
|
15
|
+
* A section with no `stepIndex` (file-level counts, a total) is a card without an
|
|
16
|
+
* edit button. `disabled` greys every edit button at once — while the commit runs,
|
|
17
|
+
* when jumping back would show a step whose edits can no longer reach the request.
|
|
14
18
|
*/
|
|
15
19
|
export function WizardSummary({
|
|
16
20
|
sections,
|
|
17
21
|
onEditStep,
|
|
22
|
+
disabled = false,
|
|
18
23
|
labels,
|
|
19
24
|
}: {
|
|
20
25
|
sections: SummarySection[];
|
|
21
|
-
|
|
26
|
+
/** Called with the section's `stepIndex`. Optional only for a summary whose
|
|
27
|
+
* sections name no step at all. */
|
|
28
|
+
onEditStep?: (stepIndex: number) => void;
|
|
29
|
+
/** Disable every edit button (e.g. `wizard.isSubmitting`). */
|
|
30
|
+
disabled?: boolean;
|
|
22
31
|
labels?: Partial<WizardLabels>;
|
|
23
32
|
}) {
|
|
24
33
|
const l = useKitLabels("wizard", DEFAULT_WIZARD_LABELS, labels);
|
|
@@ -33,17 +42,20 @@ export function WizardSummary({
|
|
|
33
42
|
the title instead of sitting beside it. */}
|
|
34
43
|
<CardHeader className="pb-2">
|
|
35
44
|
<CardTitle className="text-base">{section.label}</CardTitle>
|
|
36
|
-
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
{section.stepIndex !== undefined && onEditStep && (
|
|
46
|
+
<CardAction>
|
|
47
|
+
<IconButton
|
|
48
|
+
size="sm"
|
|
49
|
+
variant="ghost"
|
|
50
|
+
aria-label={l.edit}
|
|
51
|
+
title={l.edit}
|
|
52
|
+
disabled={disabled}
|
|
53
|
+
onClick={() => onEditStep(section.stepIndex as number)}
|
|
54
|
+
>
|
|
55
|
+
<Pencil />
|
|
56
|
+
</IconButton>
|
|
57
|
+
</CardAction>
|
|
58
|
+
)}
|
|
47
59
|
</CardHeader>
|
|
48
60
|
<CardContent>
|
|
49
61
|
<dl className="grid gap-2 text-sm">
|
package/src/wizard.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// `@eifi1/ui-kit/wizard` — the multi-step engine, its chrome and its review step.
|
|
2
2
|
//
|
|
3
|
-
// A re-slicing of the main barrel, not a new API.
|
|
4
|
-
//
|
|
3
|
+
// A re-slicing of the main barrel, not a new API. By default useWizard syncs the
|
|
4
|
+
// active step to `?step=` via react-router's useSearchParams, so it needs a router;
|
|
5
|
+
// `urlSync: false` keeps the step in memory and needs none.
|
|
5
6
|
//
|
|
6
7
|
// NOT the same thing as `WizardStepper` in components/wizard-stepper, which is a bare
|
|
7
8
|
// two-step indicator with no engine and stays on the main barrel.
|