@eifi1/ui-kit 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +219 -0
- package/dist/components/account-settings.d.ts +71 -0
- package/dist/components/account-settings.js +159 -0
- package/dist/components/account-settings.js.map +1 -0
- package/dist/components/alert-banner.d.ts +20 -0
- package/dist/components/alert-banner.js +39 -0
- package/dist/components/alert-banner.js.map +1 -0
- package/dist/components/amount-input.d.ts +117 -0
- package/dist/components/amount-input.js +243 -0
- package/dist/components/amount-input.js.map +1 -0
- package/dist/components/calculator.d.ts +42 -0
- package/dist/components/calculator.js +161 -0
- package/dist/components/calculator.js.map +1 -0
- package/dist/components/chart.d.ts +58 -0
- package/dist/components/chart.js +158 -0
- package/dist/components/chart.js.map +1 -0
- package/dist/components/combobox-core.d.ts +77 -0
- package/dist/components/combobox-core.js +263 -0
- package/dist/components/combobox-core.js.map +1 -0
- package/dist/components/combobox.d.ts +141 -0
- package/dist/components/combobox.js +550 -0
- package/dist/components/combobox.js.map +1 -0
- package/dist/components/currency-select.d.ts +37 -0
- package/dist/components/currency-select.js +131 -0
- package/dist/components/currency-select.js.map +1 -0
- package/dist/components/data-table-filter-popover.d.ts +28 -0
- package/dist/components/data-table-filter-popover.js +201 -0
- package/dist/components/data-table-filter-popover.js.map +1 -0
- package/dist/components/data-table-filters.d.ts +5 -0
- package/dist/components/data-table-filters.js +118 -0
- package/dist/components/data-table-filters.js.map +1 -0
- package/dist/components/data-table-labels.d.ts +33 -0
- package/dist/components/data-table-labels.js +53 -0
- package/dist/components/data-table-labels.js.map +1 -0
- package/dist/components/data-table-pagination.d.ts +18 -0
- package/dist/components/data-table-pagination.js +92 -0
- package/dist/components/data-table-pagination.js.map +1 -0
- package/dist/components/data-table-sort.d.ts +23 -0
- package/dist/components/data-table-sort.js +55 -0
- package/dist/components/data-table-sort.js.map +1 -0
- package/dist/components/data-table.d.ts +5 -0
- package/dist/components/data-table.js +926 -0
- package/dist/components/data-table.js.map +1 -0
- package/dist/components/date-picker.d.ts +85 -0
- package/dist/components/date-picker.js +253 -0
- package/dist/components/date-picker.js.map +1 -0
- package/dist/components/dropdown.d.ts +98 -0
- package/dist/components/dropdown.js +122 -0
- package/dist/components/dropdown.js.map +1 -0
- package/dist/components/entity-combobox.d.ts +49 -0
- package/dist/components/entity-combobox.js +115 -0
- package/dist/components/entity-combobox.js.map +1 -0
- package/dist/components/file-dropzone.d.ts +18 -0
- package/dist/components/file-dropzone.js +101 -0
- package/dist/components/file-dropzone.js.map +1 -0
- package/dist/components/full-bleed-dialog.d.ts +57 -0
- package/dist/components/full-bleed-dialog.js +68 -0
- package/dist/components/full-bleed-dialog.js.map +1 -0
- package/dist/components/grouped-picker.d.ts +37 -0
- package/dist/components/grouped-picker.js +87 -0
- package/dist/components/grouped-picker.js.map +1 -0
- package/dist/components/hover-menu.d.ts +16 -0
- package/dist/components/hover-menu.js +113 -0
- package/dist/components/hover-menu.js.map +1 -0
- package/dist/components/mini-calendar.d.ts +33 -0
- package/dist/components/mini-calendar.js +133 -0
- package/dist/components/mini-calendar.js.map +1 -0
- package/dist/components/modal.d.ts +76 -0
- package/dist/components/modal.js +156 -0
- package/dist/components/modal.js.map +1 -0
- package/dist/components/multi-entity-combobox.d.ts +47 -0
- package/dist/components/multi-entity-combobox.js +116 -0
- package/dist/components/multi-entity-combobox.js.map +1 -0
- package/dist/components/multi-select.d.ts +39 -0
- package/dist/components/multi-select.js +132 -0
- package/dist/components/multi-select.js.map +1 -0
- package/dist/components/number-input.d.ts +74 -0
- package/dist/components/number-input.js +114 -0
- package/dist/components/number-input.js.map +1 -0
- package/dist/components/numpad-sheet.d.ts +45 -0
- package/dist/components/numpad-sheet.js +102 -0
- package/dist/components/numpad-sheet.js.map +1 -0
- package/dist/components/picker-sheet.d.ts +44 -0
- package/dist/components/picker-sheet.js +78 -0
- package/dist/components/picker-sheet.js.map +1 -0
- package/dist/components/popover.d.ts +20 -0
- package/dist/components/popover.js +46 -0
- package/dist/components/popover.js.map +1 -0
- package/dist/components/search-field.d.ts +52 -0
- package/dist/components/search-field.js +64 -0
- package/dist/components/search-field.js.map +1 -0
- package/dist/components/settings-fields.d.ts +39 -0
- package/dist/components/settings-fields.js +17 -0
- package/dist/components/settings-fields.js.map +1 -0
- package/dist/components/swipeable-row.d.ts +60 -0
- package/dist/components/swipeable-row.js +115 -0
- package/dist/components/swipeable-row.js.map +1 -0
- package/dist/components/toggle-group.d.ts +34 -0
- package/dist/components/toggle-group.js +90 -0
- package/dist/components/toggle-group.js.map +1 -0
- package/dist/components/tooltip.d.ts +86 -0
- package/dist/components/tooltip.js +171 -0
- package/dist/components/tooltip.js.map +1 -0
- package/dist/components/ui.d.ts +239 -0
- package/dist/components/ui.js +512 -0
- package/dist/components/ui.js.map +1 -0
- package/dist/components/use-mobile-reveal.d.ts +31 -0
- package/dist/components/use-mobile-reveal.js +33 -0
- package/dist/components/use-mobile-reveal.js.map +1 -0
- package/dist/components/use-table-state.d.ts +54 -0
- package/dist/components/use-table-state.js +153 -0
- package/dist/components/use-table-state.js.map +1 -0
- package/dist/components/user-avatar.d.ts +23 -0
- package/dist/components/user-avatar.js +33 -0
- package/dist/components/user-avatar.js.map +1 -0
- package/dist/components/wizard-stepper.d.ts +14 -0
- package/dist/components/wizard-stepper.js +27 -0
- package/dist/components/wizard-stepper.js.map +1 -0
- package/dist/data-table-filters-aXyf0Xub.d.ts +261 -0
- package/dist/feedback/feedback-attachment.d.ts +63 -0
- package/dist/feedback/feedback-attachment.js +160 -0
- package/dist/feedback/feedback-attachment.js.map +1 -0
- package/dist/feedback/feedback-dialog.d.ts +72 -0
- package/dist/feedback/feedback-dialog.js +109 -0
- package/dist/feedback/feedback-dialog.js.map +1 -0
- package/dist/feedback/feedback-inbox.d.ts +226 -0
- package/dist/feedback/feedback-inbox.js +315 -0
- package/dist/feedback/feedback-inbox.js.map +1 -0
- package/dist/hooks/use-anchored-panel.d.ts +80 -0
- package/dist/hooks/use-anchored-panel.js +71 -0
- package/dist/hooks/use-anchored-panel.js.map +1 -0
- package/dist/hooks/use-anchored-rect.d.ts +24 -0
- package/dist/hooks/use-anchored-rect.js +40 -0
- package/dist/hooks/use-anchored-rect.js.map +1 -0
- package/dist/hooks/use-body-scroll-lock.d.ts +9 -0
- package/dist/hooks/use-body-scroll-lock.js +41 -0
- package/dist/hooks/use-body-scroll-lock.js.map +1 -0
- package/dist/hooks/use-close-transition.d.ts +41 -0
- package/dist/hooks/use-close-transition.js +36 -0
- package/dist/hooks/use-close-transition.js.map +1 -0
- package/dist/hooks/use-dismiss.d.ts +34 -0
- package/dist/hooks/use-dismiss.js +41 -0
- package/dist/hooks/use-dismiss.js.map +1 -0
- package/dist/hooks/use-media-query.d.ts +8 -0
- package/dist/hooks/use-media-query.js +18 -0
- package/dist/hooks/use-media-query.js.map +1 -0
- package/dist/hooks/use-overlay-history.d.ts +10 -0
- package/dist/hooks/use-overlay-history.js +99 -0
- package/dist/hooks/use-overlay-history.js.map +1 -0
- package/dist/hooks/use-row-swipe.d.ts +30 -0
- package/dist/hooks/use-row-swipe.js +128 -0
- package/dist/hooks/use-row-swipe.js.map +1 -0
- package/dist/index.d.ts +75 -0
- package/dist/index.js +90 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/calc.d.ts +89 -0
- package/dist/lib/calc.js +180 -0
- package/dist/lib/calc.js.map +1 -0
- package/dist/lib/cn.d.ts +6 -0
- package/dist/lib/cn.js +7 -0
- package/dist/lib/cn.js.map +1 -0
- package/dist/lib/dates.d.ts +84 -0
- package/dist/lib/dates.js +108 -0
- package/dist/lib/dates.js.map +1 -0
- package/dist/lib/logger.d.ts +17 -0
- package/dist/lib/logger.js +30 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/safe-storage.d.ts +23 -0
- package/dist/lib/safe-storage.js +20 -0
- package/dist/lib/safe-storage.js.map +1 -0
- package/dist/search/command-palette.d.ts +71 -0
- package/dist/search/command-palette.js +198 -0
- package/dist/search/command-palette.js.map +1 -0
- package/dist/shell/app-shell.d.ts +64 -0
- package/dist/shell/app-shell.js +237 -0
- package/dist/shell/app-shell.js.map +1 -0
- package/dist/shell/option-switcher-menu.d.ts +24 -0
- package/dist/shell/option-switcher-menu.js +44 -0
- package/dist/shell/option-switcher-menu.js.map +1 -0
- package/dist/shell/role-switcher.d.ts +19 -0
- package/dist/shell/role-switcher.js +28 -0
- package/dist/shell/role-switcher.js.map +1 -0
- package/dist/shell/top-bar.d.ts +15 -0
- package/dist/shell/top-bar.js +29 -0
- package/dist/shell/top-bar.js.map +1 -0
- package/dist/shell/topbar-action-menu.d.ts +44 -0
- package/dist/shell/topbar-action-menu.js +49 -0
- package/dist/shell/topbar-action-menu.js.map +1 -0
- package/dist/shell/topbar-controls.d.ts +54 -0
- package/dist/shell/topbar-controls.js +152 -0
- package/dist/shell/topbar-controls.js.map +1 -0
- package/dist/theme/chart-palette.d.ts +60 -0
- package/dist/theme/chart-palette.js +79 -0
- package/dist/theme/chart-palette.js.map +1 -0
- package/dist/theme/palette-presets.d.ts +39 -0
- package/dist/theme/palette-presets.js +440 -0
- package/dist/theme/palette-presets.js.map +1 -0
- package/dist/theme/palette-store.d.ts +45 -0
- package/dist/theme/palette-store.js +52 -0
- package/dist/theme/palette-store.js.map +1 -0
- package/dist/theme/theme-store.d.ts +57 -0
- package/dist/theme/theme-store.js +96 -0
- package/dist/theme/theme-store.js.map +1 -0
- package/dist/tour/tour.d.ts +104 -0
- package/dist/tour/tour.js +351 -0
- package/dist/tour/tour.js.map +1 -0
- package/dist/wizard/stepper-nav.d.ts +30 -0
- package/dist/wizard/stepper-nav.js +146 -0
- package/dist/wizard/stepper-nav.js.map +1 -0
- package/dist/wizard/types.d.ts +124 -0
- package/dist/wizard/types.js +24 -0
- package/dist/wizard/types.js.map +1 -0
- package/dist/wizard/use-wizard.d.ts +6 -0
- package/dist/wizard/use-wizard.js +222 -0
- package/dist/wizard/use-wizard.js.map +1 -0
- package/dist/wizard/validation.d.ts +23 -0
- package/dist/wizard/validation.js +13 -0
- package/dist/wizard/validation.js.map +1 -0
- package/dist/wizard/wizard-context.d.ts +19 -0
- package/dist/wizard/wizard-context.js +15 -0
- package/dist/wizard/wizard-context.js.map +1 -0
- package/dist/wizard/wizard-step.d.ts +11 -0
- package/dist/wizard/wizard-step.js +12 -0
- package/dist/wizard/wizard-step.js.map +1 -0
- package/dist/wizard/wizard-summary.d.ts +18 -0
- package/dist/wizard/wizard-summary.js +41 -0
- package/dist/wizard/wizard-summary.js.map +1 -0
- package/package.json +87 -0
- package/tokens.css +254 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { useCallback, useEffect, useReducer, useRef, useState } from "react";
|
|
2
|
+
import { useSearchParams } from "react-router";
|
|
3
|
+
function createReducer(stepCount) {
|
|
4
|
+
return function reducer(state, action) {
|
|
5
|
+
switch (action.type) {
|
|
6
|
+
case "GO_NEXT": {
|
|
7
|
+
const completed = new Set(state.completedSteps);
|
|
8
|
+
completed.add(state.currentStepIndex);
|
|
9
|
+
const nextIndex = Math.min(state.currentStepIndex + 1, stepCount - 1);
|
|
10
|
+
return { ...state, currentStepIndex: nextIndex, completedSteps: completed, fieldErrors: {} };
|
|
11
|
+
}
|
|
12
|
+
case "GO_BACK": {
|
|
13
|
+
const prevIndex = Math.max(state.currentStepIndex - 1, 0);
|
|
14
|
+
return { ...state, currentStepIndex: prevIndex, fieldErrors: {} };
|
|
15
|
+
}
|
|
16
|
+
case "GO_TO_STEP": {
|
|
17
|
+
if (action.index < 0 || action.index >= stepCount) return state;
|
|
18
|
+
if (!state.completedSteps.has(action.index) && action.index > state.currentStepIndex) {
|
|
19
|
+
return state;
|
|
20
|
+
}
|
|
21
|
+
return { ...state, currentStepIndex: action.index };
|
|
22
|
+
}
|
|
23
|
+
case "SKIP": {
|
|
24
|
+
const skipped = new Set(state.skippedSteps);
|
|
25
|
+
skipped.add(state.currentStepIndex);
|
|
26
|
+
const nextIndex = Math.min(state.currentStepIndex + 1, stepCount - 1);
|
|
27
|
+
return { ...state, currentStepIndex: nextIndex, skippedSteps: skipped };
|
|
28
|
+
}
|
|
29
|
+
case "UPDATE_DATA": {
|
|
30
|
+
const nextErrors = { ...state.fieldErrors };
|
|
31
|
+
for (const key of Object.keys(action.partial)) {
|
|
32
|
+
delete nextErrors[key];
|
|
33
|
+
}
|
|
34
|
+
return { ...state, data: { ...state.data, ...action.partial }, fieldErrors: nextErrors };
|
|
35
|
+
}
|
|
36
|
+
case "SET_SUBMITTING":
|
|
37
|
+
return { ...state, isSubmitting: action.value };
|
|
38
|
+
case "SET_ERROR":
|
|
39
|
+
return { ...state, error: action.error };
|
|
40
|
+
case "SET_FIELD_ERRORS":
|
|
41
|
+
return { ...state, fieldErrors: action.errors };
|
|
42
|
+
case "SHOW_CANCEL_DIALOG":
|
|
43
|
+
return { ...state, showCancelDialog: action.show };
|
|
44
|
+
default:
|
|
45
|
+
return state;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function useWizard(options) {
|
|
50
|
+
const { steps, initialData, onComplete, onCancel, missingRequiredMessage, onValidationFailed } = options;
|
|
51
|
+
const stepsRef = useRef(steps);
|
|
52
|
+
stepsRef.current = steps;
|
|
53
|
+
const validatorsRef = useRef(
|
|
54
|
+
/* @__PURE__ */ new Map()
|
|
55
|
+
);
|
|
56
|
+
const registerStepValidate = useCallback(
|
|
57
|
+
(fn) => {
|
|
58
|
+
const token = /* @__PURE__ */ Symbol();
|
|
59
|
+
validatorsRef.current.set(token, fn);
|
|
60
|
+
return () => {
|
|
61
|
+
validatorsRef.current.delete(token);
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
[]
|
|
65
|
+
);
|
|
66
|
+
const [, setUrlParams] = useSearchParams();
|
|
67
|
+
const initialStep = 0;
|
|
68
|
+
const reducer = createReducer(steps.length);
|
|
69
|
+
const [state, dispatch] = useReducer(reducer, {
|
|
70
|
+
currentStepIndex: initialStep,
|
|
71
|
+
completedSteps: /* @__PURE__ */ new Set(),
|
|
72
|
+
skippedSteps: /* @__PURE__ */ new Set(),
|
|
73
|
+
data: initialData ?? {},
|
|
74
|
+
isSubmitting: false,
|
|
75
|
+
error: null,
|
|
76
|
+
fieldErrors: {},
|
|
77
|
+
showCancelDialog: false
|
|
78
|
+
});
|
|
79
|
+
const [nextBlocked, setNextBlockedState] = useState(false);
|
|
80
|
+
const setNextBlocked = useCallback(
|
|
81
|
+
(blocked) => setNextBlockedState(blocked),
|
|
82
|
+
[]
|
|
83
|
+
);
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
setUrlParams((prev) => {
|
|
86
|
+
prev.set("step", String(state.currentStepIndex));
|
|
87
|
+
return prev;
|
|
88
|
+
}, { replace: true });
|
|
89
|
+
}, [state.currentStepIndex, setUrlParams]);
|
|
90
|
+
const currentStep = stepsRef.current[state.currentStepIndex];
|
|
91
|
+
const isFirstStep = state.currentStepIndex === 0;
|
|
92
|
+
const isLastStep = state.currentStepIndex === stepsRef.current.length - 1;
|
|
93
|
+
const canFinish = stepsRef.current.every(
|
|
94
|
+
(_, index) => index === stepsRef.current.length - 1 || state.completedSteps.has(index) || state.skippedSteps.has(index)
|
|
95
|
+
);
|
|
96
|
+
const runStepValidators = useCallback(async () => {
|
|
97
|
+
const step = stepsRef.current[state.currentStepIndex];
|
|
98
|
+
const validators = [...validatorsRef.current.values()];
|
|
99
|
+
if (step.validate) validators.push(step.validate);
|
|
100
|
+
if (validators.length === 0) return true;
|
|
101
|
+
let ok = true;
|
|
102
|
+
let errors = {};
|
|
103
|
+
try {
|
|
104
|
+
for (const validate of validators) {
|
|
105
|
+
const result = await validate();
|
|
106
|
+
const vOk = typeof result === "boolean" ? result : result.ok;
|
|
107
|
+
if (!vOk) {
|
|
108
|
+
ok = false;
|
|
109
|
+
if (typeof result !== "boolean" && result.errors) {
|
|
110
|
+
errors = { ...errors, ...result.errors };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
} catch (err) {
|
|
115
|
+
console.error("[wizard] a step validator threw", err);
|
|
116
|
+
ok = false;
|
|
117
|
+
}
|
|
118
|
+
if (!ok) {
|
|
119
|
+
dispatch({ type: "SET_FIELD_ERRORS", errors });
|
|
120
|
+
if (Object.keys(errors).length === 0) {
|
|
121
|
+
const message = missingRequiredMessage ?? "Please fill in all required fields.";
|
|
122
|
+
if (onValidationFailed) {
|
|
123
|
+
onValidationFailed(message);
|
|
124
|
+
} else {
|
|
125
|
+
const { toast } = await import("sonner");
|
|
126
|
+
toast.error(message);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
return true;
|
|
132
|
+
}, [state.currentStepIndex, missingRequiredMessage, onValidationFailed]);
|
|
133
|
+
const goNext = useCallback(async () => {
|
|
134
|
+
if (await runStepValidators()) {
|
|
135
|
+
dispatch({ type: "GO_NEXT" });
|
|
136
|
+
}
|
|
137
|
+
}, [runStepValidators]);
|
|
138
|
+
const goBack = useCallback(() => {
|
|
139
|
+
dispatch({ type: "GO_BACK" });
|
|
140
|
+
}, []);
|
|
141
|
+
const goToStep = useCallback((index) => {
|
|
142
|
+
dispatch({ type: "GO_TO_STEP", index });
|
|
143
|
+
}, []);
|
|
144
|
+
const skip = useCallback(() => {
|
|
145
|
+
dispatch({ type: "SKIP" });
|
|
146
|
+
}, []);
|
|
147
|
+
const cancel = useCallback(() => {
|
|
148
|
+
dispatch({ type: "SHOW_CANCEL_DIALOG", show: true });
|
|
149
|
+
}, []);
|
|
150
|
+
const confirmCancel = useCallback(() => {
|
|
151
|
+
dispatch({ type: "SHOW_CANCEL_DIALOG", show: false });
|
|
152
|
+
onCancel?.();
|
|
153
|
+
}, [onCancel]);
|
|
154
|
+
const dismissCancel = useCallback(() => {
|
|
155
|
+
dispatch({ type: "SHOW_CANCEL_DIALOG", show: false });
|
|
156
|
+
}, []);
|
|
157
|
+
const updateData = useCallback((partial) => {
|
|
158
|
+
dispatch({ type: "UPDATE_DATA", partial });
|
|
159
|
+
}, []);
|
|
160
|
+
const clearError = useCallback(() => {
|
|
161
|
+
dispatch({ type: "SET_ERROR", error: null });
|
|
162
|
+
}, []);
|
|
163
|
+
const clearFieldErrors = useCallback(() => {
|
|
164
|
+
dispatch({ type: "SET_FIELD_ERRORS", errors: {} });
|
|
165
|
+
}, []);
|
|
166
|
+
const finish = useCallback(async () => {
|
|
167
|
+
if (!onComplete || !canFinish) return;
|
|
168
|
+
if (!await runStepValidators()) return;
|
|
169
|
+
dispatch({ type: "SET_SUBMITTING", value: true });
|
|
170
|
+
dispatch({ type: "SET_ERROR", error: null });
|
|
171
|
+
try {
|
|
172
|
+
await onComplete(state.data);
|
|
173
|
+
} catch (err) {
|
|
174
|
+
const message = err instanceof Error ? err.message : "An error occurred";
|
|
175
|
+
dispatch({ type: "SET_ERROR", error: message });
|
|
176
|
+
} finally {
|
|
177
|
+
dispatch({ type: "SET_SUBMITTING", value: false });
|
|
178
|
+
}
|
|
179
|
+
}, [onComplete, canFinish, state.data, runStepValidators]);
|
|
180
|
+
const stepStatus = useCallback(
|
|
181
|
+
(index) => {
|
|
182
|
+
if (index === state.currentStepIndex) return "active";
|
|
183
|
+
if (state.skippedSteps.has(index)) return "skipped";
|
|
184
|
+
if (state.completedSteps.has(index)) return "completed";
|
|
185
|
+
return "upcoming";
|
|
186
|
+
},
|
|
187
|
+
[state.currentStepIndex, state.completedSteps, state.skippedSteps]
|
|
188
|
+
);
|
|
189
|
+
return {
|
|
190
|
+
currentStepIndex: state.currentStepIndex,
|
|
191
|
+
currentStep,
|
|
192
|
+
steps: stepsRef.current,
|
|
193
|
+
isFirstStep,
|
|
194
|
+
isLastStep,
|
|
195
|
+
canFinish,
|
|
196
|
+
goNext,
|
|
197
|
+
goBack,
|
|
198
|
+
goToStep,
|
|
199
|
+
skip,
|
|
200
|
+
cancel,
|
|
201
|
+
finish,
|
|
202
|
+
registerStepValidate,
|
|
203
|
+
nextBlocked,
|
|
204
|
+
setNextBlocked,
|
|
205
|
+
stepStatus,
|
|
206
|
+
completedSteps: state.completedSteps,
|
|
207
|
+
data: state.data,
|
|
208
|
+
updateData,
|
|
209
|
+
isSubmitting: state.isSubmitting,
|
|
210
|
+
error: state.error,
|
|
211
|
+
clearError,
|
|
212
|
+
fieldErrors: state.fieldErrors,
|
|
213
|
+
clearFieldErrors,
|
|
214
|
+
showCancelDialog: state.showCancelDialog,
|
|
215
|
+
confirmCancel,
|
|
216
|
+
dismissCancel
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
export {
|
|
220
|
+
useWizard
|
|
221
|
+
};
|
|
222
|
+
//# sourceMappingURL=use-wizard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/wizard/use-wizard.ts"],"sourcesContent":["/**\n * The multi-step wizard state machine: step index, completed/skipped sets,\n * per-step async validators, collected data, field errors, submit and the\n * cancel-confirm gate.\n *\n * Domain-free, but NOT router-free: it mirrors the active step to `?step=N`, so\n * it must be mounted inside a react-router context — the same requirement\n * `DataTable` already carries. It only ever WRITES that param (see the note on\n * `initialStep` below for why it deliberately does not read it back).\n */\nimport { useCallback, useEffect, useReducer, useRef, useState } from \"react\";\nimport { useSearchParams } from \"react-router\";\nimport type {\n FieldErrors,\n StepStatus,\n UseWizardOptions,\n UseWizardReturn,\n ValidateResult,\n WizardStepConfig,\n} from \"./types\";\n\ninterface WizardState<TData> {\n currentStepIndex: number;\n completedSteps: Set<number>;\n skippedSteps: Set<number>;\n data: TData;\n isSubmitting: boolean;\n error: string | null;\n fieldErrors: FieldErrors;\n showCancelDialog: boolean;\n}\n\ntype WizardAction<TData> =\n | { type: \"GO_NEXT\" }\n | { type: \"GO_BACK\" }\n | { type: \"GO_TO_STEP\"; index: number }\n | { type: \"SKIP\" }\n | { type: \"UPDATE_DATA\"; partial: Partial<TData> }\n | { type: \"SET_SUBMITTING\"; value: boolean }\n | { type: \"SET_ERROR\"; error: string | null }\n | { type: \"SET_FIELD_ERRORS\"; errors: FieldErrors }\n | { type: \"SHOW_CANCEL_DIALOG\"; show: boolean };\n\nfunction createReducer<TData>(stepCount: number) {\n return function reducer(\n state: WizardState<TData>,\n action: WizardAction<TData>,\n ): WizardState<TData> {\n switch (action.type) {\n case \"GO_NEXT\": {\n const completed = new Set(state.completedSteps);\n completed.add(state.currentStepIndex);\n const nextIndex = Math.min(state.currentStepIndex + 1, stepCount - 1);\n return { ...state, currentStepIndex: nextIndex, completedSteps: completed, fieldErrors: {} };\n }\n case \"GO_BACK\": {\n const prevIndex = Math.max(state.currentStepIndex - 1, 0);\n return { ...state, currentStepIndex: prevIndex, fieldErrors: {} };\n }\n case \"GO_TO_STEP\": {\n if (action.index < 0 || action.index >= stepCount) return state;\n if (!state.completedSteps.has(action.index) && action.index > state.currentStepIndex) {\n return state;\n }\n return { ...state, currentStepIndex: action.index };\n }\n case \"SKIP\": {\n const skipped = new Set(state.skippedSteps);\n skipped.add(state.currentStepIndex);\n const nextIndex = Math.min(state.currentStepIndex + 1, stepCount - 1);\n return { ...state, currentStepIndex: nextIndex, skippedSteps: skipped };\n }\n case \"UPDATE_DATA\": {\n // Clear error for any field that is now being edited (so typing makes\n // the red message disappear).\n const nextErrors = { ...state.fieldErrors };\n for (const key of Object.keys(action.partial)) {\n delete nextErrors[key];\n }\n return { ...state, data: { ...state.data, ...action.partial }, fieldErrors: nextErrors };\n }\n case \"SET_SUBMITTING\":\n return { ...state, isSubmitting: action.value };\n case \"SET_ERROR\":\n return { ...state, error: action.error };\n case \"SET_FIELD_ERRORS\":\n return { ...state, fieldErrors: action.errors };\n case \"SHOW_CANCEL_DIALOG\":\n return { ...state, showCancelDialog: action.show };\n default:\n return state;\n }\n };\n}\n\nexport function useWizard<TData extends Record<string, unknown>>(\n options: UseWizardOptions<TData>,\n): UseWizardReturn<TData> {\n const { steps, initialData, onComplete, onCancel, missingRequiredMessage, onValidationFailed } =\n options;\n const stepsRef = useRef<WizardStepConfig[]>(steps);\n stepsRef.current = steps;\n\n // Validators registered by the CURRENT step's mounted components (RHF steps\n // register via `useRhfWizardStep` through the wizard context). Only the active\n // step's validators are present — each unregisters on unmount. `goNext` and\n // `finish` run them (via `runStepValidators`) alongside the step config's own\n // `validate`.\n const validatorsRef = useRef(\n new Map<symbol, () => ValidateResult | Promise<ValidateResult>>(),\n );\n const registerStepValidate = useCallback(\n (fn: () => ValidateResult | Promise<ValidateResult>) => {\n const token = Symbol();\n validatorsRef.current.set(token, fn);\n return () => {\n validatorsRef.current.delete(token);\n };\n },\n [],\n );\n\n const [, setUrlParams] = useSearchParams();\n // The wizard's entered `data` and `completedSteps` are NOT persisted across a\n // reload / deep-link (the reducer re-inits to empty defaults on every mount).\n // Restoring a later step index from `?step=N` would therefore leave the wizard\n // on (or past) the last step with an EMPTY data set, letting Finish submit an\n // empty payload. Always start at step 0 so a reload re-runs the wizard from a\n // valid state instead of losing the entered data silently.\n const initialStep = 0;\n\n const reducer = createReducer<TData>(steps.length);\n const [state, dispatch] = useReducer(reducer, {\n currentStepIndex: initialStep,\n completedSteps: new Set<number>(),\n skippedSteps: new Set<number>(),\n data: (initialData ?? {}) as TData,\n isSubmitting: false,\n error: null,\n fieldErrors: {},\n showCancelDialog: false,\n });\n\n // A mounted step can DISABLE forward navigation (Next/Skip) while its own\n // state is invalid (e.g. tenant shares != 100%). Defaults to false, so\n // wizards that never call setNextBlocked behave exactly as before.\n const [nextBlocked, setNextBlockedState] = useState(false);\n const setNextBlocked = useCallback(\n (blocked: boolean) => setNextBlockedState(blocked),\n [],\n );\n\n // Sync step index to URL\n useEffect(() => {\n setUrlParams((prev) => {\n prev.set(\"step\", String(state.currentStepIndex));\n return prev;\n }, { replace: true });\n }, [state.currentStepIndex, setUrlParams]);\n\n const currentStep = stepsRef.current[state.currentStepIndex];\n const isFirstStep = state.currentStepIndex === 0;\n const isLastStep = state.currentStepIndex === stepsRef.current.length - 1;\n // Finish is only valid once every step before the last has actually been\n // walked through (completed or skipped). Guards against a restored/last-step\n // index submitting a payload built from empty defaults.\n const canFinish = stepsRef.current.every(\n (_, index) =>\n index === stepsRef.current.length - 1 ||\n state.completedSteps.has(index) ||\n state.skippedSteps.has(index),\n );\n\n // Runs the active step's config `validate` plus every validator its mounted\n // components registered (RHF steps via useRhfWizardStep), AND-combining the\n // results and merging any per-field errors. Returns `true` when the step\n // passes (or registered no validators). Shared by `goNext` and `finish` so a\n // form-bearing last step is validated (and its values flushed) on Finish too.\n const runStepValidators = useCallback(async (): Promise<boolean> => {\n const step = stepsRef.current[state.currentStepIndex];\n const validators = [...validatorsRef.current.values()];\n if (step.validate) validators.push(step.validate);\n if (validators.length === 0) return true;\n let ok = true;\n let errors: FieldErrors = {};\n try {\n for (const validate of validators) {\n const result = await validate();\n const vOk = typeof result === \"boolean\" ? result : result.ok;\n if (!vOk) {\n ok = false;\n if (typeof result !== \"boolean\" && result.errors) {\n errors = { ...errors, ...result.errors };\n }\n }\n }\n } catch (err) {\n // FALL THROUGH, do not return. A bare `return false` here blocked Next while\n // skipping the `if (!ok)` block below — the only thing that shows a message,\n // and whose own comment is \"say something anyway, so the user is not left with\n // a Next button that silently does nothing\". So a validator that threw (an\n // async one whose request rejected, a null dereference in a field rule)\n // produced exactly the outcome that block was written to prevent: no field\n // errors, no toast, `onValidationFailed` never called, nothing in the console.\n //\n // The step still must not advance; `ok = false` is what says so. Whatever\n // partial `errors` the loop had collected before the throw are kept — they are\n // real, and an empty set is what routes this to the generic message.\n console.error(\"[wizard] a step validator threw\", err);\n ok = false;\n }\n if (!ok) {\n dispatch({ type: \"SET_FIELD_ERRORS\", errors });\n if (Object.keys(errors).length === 0) {\n // No per-field detail (e.g. an RHF step surfacing its own inline\n // message) — say something anyway, so the user is not left with a Next\n // button that silently does nothing.\n const message = missingRequiredMessage ?? \"Please fill in all required fields.\";\n if (onValidationFailed) {\n onValidationFailed(message);\n } else {\n // sonner is an optional peer: imported here, on the failure path only,\n // so an app that never trips this never has to install it.\n const { toast } = await import(\"sonner\");\n toast.error(message);\n }\n }\n return false;\n }\n return true;\n }, [state.currentStepIndex, missingRequiredMessage, onValidationFailed]);\n\n const goNext = useCallback(async () => {\n if (await runStepValidators()) {\n dispatch({ type: \"GO_NEXT\" });\n }\n }, [runStepValidators]);\n\n const goBack = useCallback(() => {\n dispatch({ type: \"GO_BACK\" });\n }, []);\n\n const goToStep = useCallback((index: number) => {\n dispatch({ type: \"GO_TO_STEP\", index });\n }, []);\n\n const skip = useCallback(() => {\n dispatch({ type: \"SKIP\" });\n }, []);\n\n const cancel = useCallback(() => {\n dispatch({ type: \"SHOW_CANCEL_DIALOG\", show: true });\n }, []);\n\n const confirmCancel = useCallback(() => {\n dispatch({ type: \"SHOW_CANCEL_DIALOG\", show: false });\n onCancel?.();\n }, [onCancel]);\n\n const dismissCancel = useCallback(() => {\n dispatch({ type: \"SHOW_CANCEL_DIALOG\", show: false });\n }, []);\n\n const updateData = useCallback((partial: Partial<TData>) => {\n dispatch({ type: \"UPDATE_DATA\", partial });\n }, []);\n\n const clearError = useCallback(() => {\n dispatch({ type: \"SET_ERROR\", error: null });\n }, []);\n\n const clearFieldErrors = useCallback(() => {\n dispatch({ type: \"SET_FIELD_ERRORS\", errors: {} });\n }, []);\n\n const finish = useCallback(async () => {\n if (!onComplete || !canFinish) return;\n // Validate the (possibly form-bearing) last step before submitting, matching\n // the Next gate — review-only steps register no validators and pass through.\n if (!(await runStepValidators())) return;\n dispatch({ type: \"SET_SUBMITTING\", value: true });\n dispatch({ type: \"SET_ERROR\", error: null });\n try {\n await onComplete(state.data);\n } catch (err) {\n const message = err instanceof Error ? err.message : \"An error occurred\";\n dispatch({ type: \"SET_ERROR\", error: message });\n } finally {\n dispatch({ type: \"SET_SUBMITTING\", value: false });\n }\n }, [onComplete, canFinish, state.data, runStepValidators]);\n\n const stepStatus = useCallback(\n (index: number): StepStatus => {\n if (index === state.currentStepIndex) return \"active\";\n if (state.skippedSteps.has(index)) return \"skipped\";\n if (state.completedSteps.has(index)) return \"completed\";\n return \"upcoming\";\n },\n [state.currentStepIndex, state.completedSteps, state.skippedSteps],\n );\n\n return {\n currentStepIndex: state.currentStepIndex,\n currentStep,\n steps: stepsRef.current,\n isFirstStep,\n isLastStep,\n canFinish,\n goNext,\n goBack,\n goToStep,\n skip,\n cancel,\n finish,\n registerStepValidate,\n nextBlocked,\n setNextBlocked,\n stepStatus,\n completedSteps: state.completedSteps,\n data: state.data,\n updateData,\n isSubmitting: state.isSubmitting,\n error: state.error,\n clearError,\n fieldErrors: state.fieldErrors,\n clearFieldErrors,\n showCancelDialog: state.showCancelDialog,\n confirmCancel,\n dismissCancel,\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ValidateResult } from './types.js';
|
|
2
|
+
import 'react';
|
|
3
|
+
|
|
4
|
+
interface RequiredFieldSpec<T> {
|
|
5
|
+
key: keyof T;
|
|
6
|
+
/**
|
|
7
|
+
* Custom predicate (e.g. numeric `> 0` or range checks). Defaults to a
|
|
8
|
+
* "present" check: a non-empty trimmed string, or a non-null id/enum.
|
|
9
|
+
*/
|
|
10
|
+
valid?: (value: unknown, data: T) => boolean;
|
|
11
|
+
/** Per-field message override (defaults to the shared `defaultMessage`). */
|
|
12
|
+
message?: string;
|
|
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
|
+
declare function requiredFieldsValidator<T extends Record<string, unknown>>(data: T, fields: RequiredFieldSpec<T>[], defaultMessage: string): ValidateResult;
|
|
22
|
+
|
|
23
|
+
export { type RequiredFieldSpec, requiredFieldsValidator };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
function requiredFieldsValidator(data, fields, defaultMessage) {
|
|
2
|
+
const errors = {};
|
|
3
|
+
for (const f of fields) {
|
|
4
|
+
const v = data[f.key];
|
|
5
|
+
const ok = f.valid ? f.valid(v, data) : typeof v === "string" ? v.trim() !== "" : v != null;
|
|
6
|
+
if (!ok) errors[String(f.key)] = f.message ?? defaultMessage;
|
|
7
|
+
}
|
|
8
|
+
return { ok: Object.keys(errors).length === 0, errors };
|
|
9
|
+
}
|
|
10
|
+
export {
|
|
11
|
+
requiredFieldsValidator
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/wizard/validation.ts"],"sourcesContent":["import type { ValidateResult, FieldErrors } from \"./types\";\n\nexport interface RequiredFieldSpec<T> {\n key: keyof T;\n /**\n * Custom predicate (e.g. numeric `> 0` or range checks). Defaults to a\n * \"present\" check: a non-empty trimmed string, or a non-null id/enum.\n */\n valid?: (value: unknown, data: T) => boolean;\n /** Per-field message override (defaults to the shared `defaultMessage`). */\n message?: string;\n}\n\n/**\n * Folds the copy-pasted `const errors = {}; if (!data.x) errors.x = required; …`\n * step-validate block reimplemented across the single-file entity wizards.\n * Pure-required fields need only `{ key }`; numeric constraints (amount `> 0`,\n * rate in range) pass a `valid` predicate. The message is caller-supplied so\n * each page keeps its own namespace (e.g. `t(\"common:validation.required\")`).\n */\nexport function requiredFieldsValidator<T extends Record<string, unknown>>(\n data: T,\n fields: RequiredFieldSpec<T>[],\n defaultMessage: string,\n): ValidateResult {\n const errors: FieldErrors = {};\n for (const f of fields) {\n const v = data[f.key];\n const ok = f.valid\n ? f.valid(v, data)\n : typeof v === \"string\"\n ? v.trim() !== \"\"\n : v != null;\n if (!ok) errors[String(f.key)] = f.message ?? defaultMessage;\n }\n return { ok: Object.keys(errors).length === 0, errors };\n}\n"],"mappings":"AAoBO,SAAS,wBACd,MACA,QACA,gBACgB;AAChB,QAAM,SAAsB,CAAC;AAC7B,aAAW,KAAK,QAAQ;AACtB,UAAM,IAAI,KAAK,EAAE,GAAG;AACpB,UAAM,KAAK,EAAE,QACT,EAAE,MAAM,GAAG,IAAI,IACf,OAAO,MAAM,WACX,EAAE,KAAK,MAAM,KACb,KAAK;AACX,QAAI,CAAC,GAAI,QAAO,OAAO,EAAE,GAAG,CAAC,IAAI,EAAE,WAAW;AAAA,EAChD;AACA,SAAO,EAAE,IAAI,OAAO,KAAK,MAAM,EAAE,WAAW,GAAG,OAAO;AACxD;","names":[]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ValidateResult } from './types.js';
|
|
3
|
+
|
|
4
|
+
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: (fn: () => ValidateResult | Promise<ValidateResult>) => () => void;
|
|
13
|
+
/** Lets the mounted step disable Next/Skip (e.g. invalid tenant shares). */
|
|
14
|
+
setNextBlocked: (blocked: boolean) => void;
|
|
15
|
+
}
|
|
16
|
+
declare const WizardContextProvider: react.Provider<WizardContextValue | null>;
|
|
17
|
+
declare function useWizardContext(): WizardContextValue;
|
|
18
|
+
|
|
19
|
+
export { WizardContextProvider, type WizardContextValue, useWizardContext };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
const WizardContext = createContext(null);
|
|
3
|
+
const WizardContextProvider = WizardContext.Provider;
|
|
4
|
+
function useWizardContext() {
|
|
5
|
+
const ctx = useContext(WizardContext);
|
|
6
|
+
if (!ctx) {
|
|
7
|
+
throw new Error("useWizardContext must be used within a <StepperNav>");
|
|
8
|
+
}
|
|
9
|
+
return ctx;
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
WizardContextProvider,
|
|
13
|
+
useWizardContext
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=wizard-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/wizard/wizard-context.tsx"],"sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { ValidateResult } from \"./types\";\n\nexport interface WizardContextValue {\n /**\n * Register a validator for the currently-mounted step. Returns an unregister\n * fn (call it in a useEffect cleanup). `goNext` runs every registered\n * validator for the active step (plus the step config's own `validate`),\n * AND-combining the results — this replaces the per-page `useRef` +\n * `validate: () => ref.current?.()` bridge each RHF step used to wire.\n */\n registerStepValidate: (\n fn: () => ValidateResult | Promise<ValidateResult>,\n ) => () => void;\n /** Lets the mounted step disable Next/Skip (e.g. invalid tenant shares). */\n setNextBlocked: (blocked: boolean) => void;\n}\n\nconst WizardContext = createContext<WizardContextValue | null>(null);\n\nexport const WizardContextProvider = WizardContext.Provider;\n\nexport function useWizardContext(): WizardContextValue {\n const ctx = useContext(WizardContext);\n if (!ctx) {\n throw new Error(\"useWizardContext must be used within a <StepperNav>\");\n }\n return ctx;\n}\n"],"mappings":"AAAA,SAAS,eAAe,kBAAkB;AAkB1C,MAAM,gBAAgB,cAAyC,IAAI;AAE5D,MAAM,wBAAwB,cAAc;AAE5C,SAAS,mBAAuC;AACrD,QAAM,MAAM,WAAW,aAAa;AACpC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,qDAAqD;AAAA,EACvE;AACA,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
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
|
+
declare function WizardStep({ children, className, }: {
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
className?: string;
|
|
9
|
+
}): react.JSX.Element;
|
|
10
|
+
|
|
11
|
+
export { WizardStep };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/cn.js";
|
|
3
|
+
function WizardStep({
|
|
4
|
+
children,
|
|
5
|
+
className
|
|
6
|
+
}) {
|
|
7
|
+
return /* @__PURE__ */ jsx("div", { className: cn("space-y-6", className), children });
|
|
8
|
+
}
|
|
9
|
+
export {
|
|
10
|
+
WizardStep
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=wizard-step.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/wizard/wizard-step.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"../lib/cn\";\n\n/** The vertical rhythm one wizard step's content sits on. A component rather\n * than a class string so every step in every app spaces its fields the same. */\nexport function WizardStep({\n children,\n className,\n}: {\n children: ReactNode;\n className?: string;\n}) {\n return <div className={cn(\"space-y-6\", className)}>{children}</div>;\n}\n"],"mappings":"AAYS;AAXT,SAAS,UAAU;AAIZ,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AACF,GAGG;AACD,SAAO,oBAAC,SAAI,WAAW,GAAG,aAAa,SAAS,GAAI,UAAS;AAC/D;","names":[]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { SummarySection, WizardLabels } from './types.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The review step: one card per section, each row a label/value pair, each card
|
|
6
|
+
* carrying an edit button back to the step it came from.
|
|
7
|
+
*
|
|
8
|
+
* Sections are data, not children, because "jump back to step N" is the whole
|
|
9
|
+
* point of the component and a caller assembling its own cards invariably drops
|
|
10
|
+
* it. Labels and values are `ReactNode`, so a value can be a badge or a link.
|
|
11
|
+
*/
|
|
12
|
+
declare function WizardSummary({ sections, onEditStep, labels, }: {
|
|
13
|
+
sections: SummarySection[];
|
|
14
|
+
onEditStep: (stepIndex: number) => void;
|
|
15
|
+
labels?: Partial<WizardLabels>;
|
|
16
|
+
}): react.JSX.Element;
|
|
17
|
+
|
|
18
|
+
export { WizardSummary };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Pencil } from "lucide-react";
|
|
3
|
+
import { Card, CardAction, CardContent, CardHeader, CardTitle, IconButton } from "../components/ui.js";
|
|
4
|
+
import { resolveWizardLabels } from "./types.js";
|
|
5
|
+
function WizardSummary({
|
|
6
|
+
sections,
|
|
7
|
+
onEditStep,
|
|
8
|
+
labels
|
|
9
|
+
}) {
|
|
10
|
+
const l = resolveWizardLabels(labels);
|
|
11
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
12
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-[var(--text-primary)]", children: l.reviewTitle }),
|
|
13
|
+
sections.map((section, sIdx) => /* @__PURE__ */ jsxs(Card, { children: [
|
|
14
|
+
/* @__PURE__ */ jsxs(CardHeader, { className: "pb-2", children: [
|
|
15
|
+
/* @__PURE__ */ jsx(CardTitle, { className: "text-base", children: section.label }),
|
|
16
|
+
/* @__PURE__ */ jsx(CardAction, { children: /* @__PURE__ */ jsx(
|
|
17
|
+
IconButton,
|
|
18
|
+
{
|
|
19
|
+
size: "sm",
|
|
20
|
+
variant: "ghost",
|
|
21
|
+
"aria-label": l.edit,
|
|
22
|
+
title: l.edit,
|
|
23
|
+
onClick: () => onEditStep(section.stepIndex),
|
|
24
|
+
children: /* @__PURE__ */ jsx(Pencil, {})
|
|
25
|
+
}
|
|
26
|
+
) })
|
|
27
|
+
] }),
|
|
28
|
+
/* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx("dl", { className: "grid gap-2 text-sm", children: section.items.map((item, iIdx) => /* @__PURE__ */ jsxs("div", { children: [
|
|
29
|
+
iIdx > 0 && /* @__PURE__ */ jsx("div", { className: "mb-2 border-t border-[var(--border)]", role: "presentation" }),
|
|
30
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4", children: [
|
|
31
|
+
/* @__PURE__ */ jsx("dt", { className: "text-slate-500 dark:text-slate-400", children: item.label }),
|
|
32
|
+
/* @__PURE__ */ jsx("dd", { className: "text-right font-medium text-[var(--text-primary)]", children: item.value })
|
|
33
|
+
] })
|
|
34
|
+
] }, iIdx)) }) })
|
|
35
|
+
] }, sIdx))
|
|
36
|
+
] });
|
|
37
|
+
}
|
|
38
|
+
export {
|
|
39
|
+
WizardSummary
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=wizard-summary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/wizard/wizard-summary.tsx"],"sourcesContent":["import { Pencil } from \"lucide-react\";\nimport { Card, CardAction, CardContent, CardHeader, CardTitle, IconButton } from \"../components/ui\";\nimport type { SummarySection, WizardLabels } from \"./types\";\nimport { resolveWizardLabels } from \"./types\";\n\n/**\n * The review step: one card per section, each row a label/value pair, each card\n * carrying an edit button back to the step it came from.\n *\n * Sections are data, not children, because \"jump back to step N\" is the whole\n * point of the component and a caller assembling its own cards invariably drops\n * it. Labels and values are `ReactNode`, so a value can be a badge or a link.\n */\nexport function WizardSummary({\n sections,\n onEditStep,\n labels,\n}: {\n sections: SummarySection[];\n onEditStep: (stepIndex: number) => void;\n labels?: Partial<WizardLabels>;\n}) {\n const l = resolveWizardLabels(labels);\n\n return (\n <div className=\"space-y-4\">\n <h2 className=\"text-lg font-semibold text-[var(--text-primary)]\">{l.reviewTitle}</h2>\n {sections.map((section, sIdx) => (\n <Card key={sIdx}>\n {/* CardHeader is a grid, and CardAction is the top-right column it\n reserves — the edit button has to go through it or it stacks under\n the title instead of sitting beside it. */}\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-base\">{section.label}</CardTitle>\n <CardAction>\n <IconButton\n size=\"sm\"\n variant=\"ghost\"\n aria-label={l.edit}\n title={l.edit}\n onClick={() => onEditStep(section.stepIndex)}\n >\n <Pencil />\n </IconButton>\n </CardAction>\n </CardHeader>\n <CardContent>\n <dl className=\"grid gap-2 text-sm\">\n {section.items.map((item, iIdx) => (\n <div key={iIdx}>\n {iIdx > 0 && (\n <div className=\"mb-2 border-t border-[var(--border)]\" role=\"presentation\" />\n )}\n <div className=\"flex items-start justify-between gap-4\">\n <dt className=\"text-slate-500 dark:text-slate-400\">{item.label}</dt>\n <dd className=\"text-right font-medium text-[var(--text-primary)]\">\n {item.value}\n </dd>\n </div>\n </div>\n ))}\n </dl>\n </CardContent>\n </Card>\n ))}\n </div>\n );\n}\n"],"mappings":"AA0BM,cAMI,YANJ;AA1BN,SAAS,cAAc;AACvB,SAAS,MAAM,YAAY,aAAa,YAAY,WAAW,kBAAkB;AAEjF,SAAS,2BAA2B;AAU7B,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,IAAI,oBAAoB,MAAM;AAEpC,SACE,qBAAC,SAAI,WAAU,aACb;AAAA,wBAAC,QAAG,WAAU,oDAAoD,YAAE,aAAY;AAAA,IAC/E,SAAS,IAAI,CAAC,SAAS,SACtB,qBAAC,QAIC;AAAA,2BAAC,cAAW,WAAU,QACpB;AAAA,4BAAC,aAAU,WAAU,aAAa,kBAAQ,OAAM;AAAA,QAChD,oBAAC,cACC;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAQ;AAAA,YACR,cAAY,EAAE;AAAA,YACd,OAAO,EAAE;AAAA,YACT,SAAS,MAAM,WAAW,QAAQ,SAAS;AAAA,YAE3C,8BAAC,UAAO;AAAA;AAAA,QACV,GACF;AAAA,SACF;AAAA,MACA,oBAAC,eACC,8BAAC,QAAG,WAAU,sBACX,kBAAQ,MAAM,IAAI,CAAC,MAAM,SACxB,qBAAC,SACE;AAAA,eAAO,KACN,oBAAC,SAAI,WAAU,wCAAuC,MAAK,gBAAe;AAAA,QAE5E,qBAAC,SAAI,WAAU,0CACb;AAAA,8BAAC,QAAG,WAAU,sCAAsC,eAAK,OAAM;AAAA,UAC/D,oBAAC,QAAG,WAAU,qDACX,eAAK,OACR;AAAA,WACF;AAAA,WATQ,IAUV,CACD,GACH,GACF;AAAA,SAlCS,IAmCX,CACD;AAAA,KACH;AAEJ;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@eifi1/ui-kit",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "App-agnostic shared design system: domain-free React components, theme and tokens.",
|
|
6
|
+
"_sideEffects": "The barrel (src/index.ts) re-exports EVERY component, including components/chart, which statically imports recharts. Without this field a bundler must assume each of those modules has side effects, so it cannot drop the unused ones: importing a Button from '@eifi1/ui-kit' dragged all 433KB of recharts into the consumer's ENTRY chunk, and Keksdose was preloading it on every page load, login included. Nothing in src/ imports for side effects (no bare imports, no CSS imports from JS, no module-level global writes) — the token stylesheet is its own './tokens.css' export — so this is safe. CSS is still listed so any future stylesheet survives.",
|
|
7
|
+
"sideEffects": [
|
|
8
|
+
"**/*.css"
|
|
9
|
+
],
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./dates": {
|
|
16
|
+
"types": "./dist/lib/dates.d.ts",
|
|
17
|
+
"import": "./dist/lib/dates.js"
|
|
18
|
+
},
|
|
19
|
+
"./tokens.css": "./tokens.css",
|
|
20
|
+
"./package.json": "./package.json"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"tokens.css"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"typecheck": "tsc --noEmit",
|
|
28
|
+
"test": "vitest run",
|
|
29
|
+
"test:watch": "vitest",
|
|
30
|
+
"build": "tsup && node scripts/fix-esm-extensions.mjs",
|
|
31
|
+
"prepublishOnly": "npm run build"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"react": ">=19",
|
|
35
|
+
"react-dom": ">=19",
|
|
36
|
+
"react-router": "^8.3.0",
|
|
37
|
+
"recharts": ">=3",
|
|
38
|
+
"sonner": ">=2"
|
|
39
|
+
},
|
|
40
|
+
"peerDependenciesMeta": {
|
|
41
|
+
"react-router": {
|
|
42
|
+
"optional": true
|
|
43
|
+
},
|
|
44
|
+
"recharts": {
|
|
45
|
+
"optional": true
|
|
46
|
+
},
|
|
47
|
+
"sonner": {
|
|
48
|
+
"optional": true
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"clsx": "^2.1.1",
|
|
53
|
+
"flag-icons": "^7.5.0",
|
|
54
|
+
"lucide-react": "^0.511.0",
|
|
55
|
+
"tailwind-merge": "^3.3.0",
|
|
56
|
+
"zustand": "^5.0.5"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
60
|
+
"@testing-library/react": "^16.3.0",
|
|
61
|
+
"@types/react": "^19.2.14",
|
|
62
|
+
"@types/react-dom": "^19.2.3",
|
|
63
|
+
"@vitejs/plugin-react": "^4.5.2",
|
|
64
|
+
"jsdom": "^30.0.1",
|
|
65
|
+
"react-router": "^8.3.0",
|
|
66
|
+
"recharts": "^3.8.1",
|
|
67
|
+
"sonner": "^2.0.7",
|
|
68
|
+
"tsup": "^8.5.0",
|
|
69
|
+
"typescript": "~5.9.3",
|
|
70
|
+
"vitest": "^4.1.11"
|
|
71
|
+
},
|
|
72
|
+
"license": "MIT",
|
|
73
|
+
"repository": {
|
|
74
|
+
"type": "git",
|
|
75
|
+
"url": "git+https://github.com/Eifi1/ui-kit.git"
|
|
76
|
+
},
|
|
77
|
+
"homepage": "https://github.com/Eifi1/ui-kit#readme",
|
|
78
|
+
"bugs": {
|
|
79
|
+
"url": "https://github.com/Eifi1/ui-kit/issues"
|
|
80
|
+
},
|
|
81
|
+
"publishConfig": {
|
|
82
|
+
"access": "public"
|
|
83
|
+
},
|
|
84
|
+
"main": "./dist/index.js",
|
|
85
|
+
"module": "./dist/index.js",
|
|
86
|
+
"types": "./dist/index.d.ts"
|
|
87
|
+
}
|