@trackunit/react-form-wizard 0.1.211 → 0.1.213
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/index.cjs.js +142 -109
- package/index.esm.js +142 -109
- package/package.json +1 -1
- package/src/FormWizard/useFormWizard.d.ts +1 -2
- package/src/FormWizardHeader/FormWizardHeader.d.ts +1 -2
- package/src/FormWizardSidebarStep/FormWizardSidebarStep.variants.d.ts +2 -0
- package/src/FormWizardStepWrapper/FormWizardStepWrapper.d.ts +1 -1
package/index.cjs.js
CHANGED
|
@@ -61,14 +61,13 @@ const setupLibraryTranslations = () => {
|
|
|
61
61
|
const cvaFillSection = cssClassVarianceUtilities.cvaMerge(["flex-grow", "p-0"]);
|
|
62
62
|
const cvaSidebar = cssClassVarianceUtilities.cvaMerge([
|
|
63
63
|
"flex",
|
|
64
|
-
"flex-
|
|
65
|
-
"overflow-x-
|
|
64
|
+
"flex-rows",
|
|
65
|
+
"overflow-x-auto",
|
|
66
66
|
"h-full",
|
|
67
|
-
"
|
|
68
|
-
"p-responsive-space",
|
|
69
|
-
"bg-slate-50",
|
|
67
|
+
"bg-white",
|
|
70
68
|
"border-r",
|
|
71
69
|
"border-slate-200",
|
|
70
|
+
"@3xl:flex-col",
|
|
72
71
|
]);
|
|
73
72
|
|
|
74
73
|
/**
|
|
@@ -79,13 +78,13 @@ function FormWizardSidebar({ dataTestId, children, className }) {
|
|
|
79
78
|
return (jsxRuntime.jsxs("aside", { className: cvaSidebar({ className }), "data-testid": dataTestId, children: [children, jsxRuntime.jsx("div", { className: cvaFillSection() })] }));
|
|
80
79
|
}
|
|
81
80
|
|
|
82
|
-
const cvaFormWizardSidebarStep = cssClassVarianceUtilities.cvaMerge(["p-2", "
|
|
81
|
+
const cvaFormWizardSidebarStep = cssClassVarianceUtilities.cvaMerge(["p-2", "hover:bg-slate-200", "transition", "border-b", "border-slate-200", "shrink-0"], {
|
|
83
82
|
variants: {
|
|
84
83
|
state: {
|
|
85
84
|
ready: "",
|
|
86
|
-
active: "
|
|
87
|
-
done: "
|
|
88
|
-
invalid: "
|
|
85
|
+
active: "@3xl:bg-slate-100",
|
|
86
|
+
done: "",
|
|
87
|
+
invalid: "",
|
|
89
88
|
disabled: "pointer-events-none cursor-not-allowed opacity-50 hover:bg-transparent",
|
|
90
89
|
},
|
|
91
90
|
isSubStep: {
|
|
@@ -98,8 +97,15 @@ const cvaFormWizardSidebarStep = cssClassVarianceUtilities.cvaMerge(["p-2", "rou
|
|
|
98
97
|
isSubStep: false,
|
|
99
98
|
},
|
|
100
99
|
});
|
|
101
|
-
const cvaSectionHeaderContainer = cssClassVarianceUtilities.cvaMerge(["flex", "justify-between", "gap-2"], {
|
|
100
|
+
const cvaSectionHeaderContainer = cssClassVarianceUtilities.cvaMerge(["flex", "justify-between", "gap-2", "p-2", "rounded"], {
|
|
102
101
|
variants: {
|
|
102
|
+
state: {
|
|
103
|
+
ready: "",
|
|
104
|
+
active: "bg-slate-100",
|
|
105
|
+
done: "",
|
|
106
|
+
invalid: "",
|
|
107
|
+
disabled: "",
|
|
108
|
+
},
|
|
103
109
|
cursor: {
|
|
104
110
|
withCursor: "hover:cursor-pointer",
|
|
105
111
|
default: "",
|
|
@@ -110,7 +116,8 @@ const cvaSectionHeaderContainer = cssClassVarianceUtilities.cvaMerge(["flex", "j
|
|
|
110
116
|
},
|
|
111
117
|
});
|
|
112
118
|
const cvaHeader = cssClassVarianceUtilities.cvaMerge(["text-md"]);
|
|
113
|
-
const cvaSubtitle = cssClassVarianceUtilities.cvaMerge(["text-xs", "text-gray-500"]);
|
|
119
|
+
const cvaSubtitle = cssClassVarianceUtilities.cvaMerge(["text-xs", "text-gray-500", "hidden", "@3xl:block"]);
|
|
120
|
+
const cvaStepNumberContainer = cssClassVarianceUtilities.cvaMerge(["self-center"]);
|
|
114
121
|
const cvaStepNumber = cssClassVarianceUtilities.cvaMerge(["flex", "justify-center", "text-xs", "items-center", "m-0.5", "w-[20px]", "h-[20px]", "bg-gray-100", "rounded-full"], {
|
|
115
122
|
variants: {
|
|
116
123
|
state: {
|
|
@@ -136,7 +143,7 @@ const cvaStepNumber = cssClassVarianceUtilities.cvaMerge(["flex", "justify-cente
|
|
|
136
143
|
state: "ready",
|
|
137
144
|
},
|
|
138
145
|
});
|
|
139
|
-
const cvaTitleAndDescription = cssClassVarianceUtilities.cvaMerge(["justify-center", "flex-1", "flex-col", "
|
|
146
|
+
const cvaTitleAndDescription = cssClassVarianceUtilities.cvaMerge(["justify-center", "flex-1", "flex-col", "@3xl:flex"]); //use same container breakpoint as cvaWizardContainer so it collapses at the same time
|
|
140
147
|
|
|
141
148
|
const getStateIcon = (state) => {
|
|
142
149
|
switch (state) {
|
|
@@ -154,7 +161,7 @@ const getStateIcon = (state) => {
|
|
|
154
161
|
*/
|
|
155
162
|
const FormWizardSidebarStep = ({ stepNumber, state, basePath, title, subTitle, path, dataTestId, onNavigate, isSubStep, }) => {
|
|
156
163
|
const stateIcon = getStateIcon(state);
|
|
157
|
-
return (jsxRuntime.jsx(reactRouter.Link, { className: cvaFormWizardSidebarStep({ state, isSubStep }), "data-testid": dataTestId !== null && dataTestId !== void 0 ? dataTestId : `form-wizard-sidebar-step-${path}`, onClick: () => onNavigate(), params: prev => ({ ...prev, stepPath: path }), search: prev => ({ ...prev }), to: basePath, children: jsxRuntime.jsxs("div", { className: cvaSectionHeaderContainer(), children: [jsxRuntime.jsx("div", { children: stateIcon !== null && stateIcon !== void 0 ? stateIcon : jsxRuntime.jsx("span", { className: cvaStepNumber({ state, isSubStep }), children: stepNumber }) }), jsxRuntime.jsxs("div", { className: cvaTitleAndDescription(), children: [jsxRuntime.jsx("
|
|
164
|
+
return (jsxRuntime.jsx(reactRouter.Link, { className: cvaFormWizardSidebarStep({ state, isSubStep }), "data-testid": dataTestId !== null && dataTestId !== void 0 ? dataTestId : `form-wizard-sidebar-step-${path}`, onClick: () => onNavigate(), params: prev => ({ ...prev, stepPath: path }), search: prev => ({ ...prev }), to: basePath, children: jsxRuntime.jsxs("div", { className: cvaSectionHeaderContainer({ state }), children: [jsxRuntime.jsx("div", { className: cvaStepNumberContainer(), children: stateIcon !== null && stateIcon !== void 0 ? stateIcon : (jsxRuntime.jsx("span", { className: cvaStepNumber({ state, isSubStep }), "data-testid": isSubStep ? `form-wizard-sidebar-substep` : `form-wizard-sidebar-step-number-${stepNumber}`, children: stepNumber })) }), jsxRuntime.jsxs("div", { className: cvaTitleAndDescription(), children: [jsxRuntime.jsx("h3", { className: cvaHeader(), children: title }), subTitle ? jsxRuntime.jsx("p", { className: cvaSubtitle(), children: subTitle }) : null] })] }) }));
|
|
158
165
|
};
|
|
159
166
|
|
|
160
167
|
const cvaFooterContainer = cssClassVarianceUtilities.cvaMerge([
|
|
@@ -166,6 +173,7 @@ const cvaFooterContainer = cssClassVarianceUtilities.cvaMerge([
|
|
|
166
173
|
"p-responsive-space",
|
|
167
174
|
"border-t",
|
|
168
175
|
"gap-responsive-space",
|
|
176
|
+
"bg-slate-50",
|
|
169
177
|
]);
|
|
170
178
|
const cvaNavigationContainer = cssClassVarianceUtilities.cvaMerge(["flex", "gap-responsive-space"]);
|
|
171
179
|
|
|
@@ -182,8 +190,8 @@ const cvaFormWizardHeader = cssClassVarianceUtilities.cvaMerge([
|
|
|
182
190
|
"flex",
|
|
183
191
|
"flex-col",
|
|
184
192
|
"p-responsive-space",
|
|
185
|
-
"border-b-slate-
|
|
186
|
-
"border-b
|
|
193
|
+
"border-b-slate-200",
|
|
194
|
+
"border-b",
|
|
187
195
|
]);
|
|
188
196
|
|
|
189
197
|
/**
|
|
@@ -191,10 +199,10 @@ const cvaFormWizardHeader = cssClassVarianceUtilities.cvaMerge([
|
|
|
191
199
|
* In most cases you should not be using this component directly, but rather use the FormWizard component.
|
|
192
200
|
*/
|
|
193
201
|
const FormWizardHeader = ({ title, description, className, dataTestId }) => {
|
|
194
|
-
return (jsxRuntime.jsxs("div", { className: cvaFormWizardHeader({ className }), "data-testid": dataTestId, children: [jsxRuntime.jsx(reactComponents.Heading, { variant: "
|
|
202
|
+
return (jsxRuntime.jsxs("div", { className: cvaFormWizardHeader({ className }), "data-testid": dataTestId, children: [jsxRuntime.jsx(reactComponents.Heading, { variant: "secondary", children: title }), description ? (jsxRuntime.jsx(reactComponents.Heading, { subtle: true, variant: "subtitle", children: description })) : null] }));
|
|
195
203
|
};
|
|
196
204
|
|
|
197
|
-
const cvaFormWizardComponentContainer = cssClassVarianceUtilities.cvaMerge(["p-responsive-space", "overflow-y-auto"]);
|
|
205
|
+
const cvaFormWizardComponentContainer = cssClassVarianceUtilities.cvaMerge(["p-responsive-space", "overflow-y-auto", "bg-slate-50"]);
|
|
198
206
|
const cvaFormWizardComponentStepForm = cssClassVarianceUtilities.cvaMerge(["grid-rows-min-fr-min", "grid", "h-full", "overflow-hidden"]);
|
|
199
207
|
|
|
200
208
|
/**
|
|
@@ -310,7 +318,8 @@ const cvaFormWizardContainer = cssClassVarianceUtilities.cvaMerge(["h-full"]);
|
|
|
310
318
|
const cvaFormWizardLayout = cssClassVarianceUtilities.cvaMerge([
|
|
311
319
|
"grid",
|
|
312
320
|
"h-full",
|
|
313
|
-
"grid-
|
|
321
|
+
"grid-rows-min-fr",
|
|
322
|
+
"@3xl:grid-rows-1",
|
|
314
323
|
"@3xl:grid-cols-[240px_1fr]", //use same breakpoint for wizardSidebarSection so it collapses at the same time
|
|
315
324
|
"@4xl:grid-cols-[320px_1fr]",
|
|
316
325
|
]);
|
|
@@ -339,107 +348,131 @@ const FormWizard = ({ lastStepPrimaryActionLabel, isEdit, steps, fullFormSchema,
|
|
|
339
348
|
if (fullFormSchema.shape[keyToCheck] === undefined) {
|
|
340
349
|
navigate({ to: basePath, params: { stepPath: firstSchemaKey } });
|
|
341
350
|
}
|
|
342
|
-
const currentVisibleStepKey = subStepPath ? `${stepPath}/${subStepPath}` : stepPath;
|
|
343
|
-
const currentVisibleStepIndex = sharedUtils.objectKeys(steps)
|
|
344
|
-
.filter(key => {
|
|
345
|
-
var _a;
|
|
346
|
-
const step = steps[key];
|
|
347
|
-
return hasShouldRenderProperty(step) ? (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState) : true;
|
|
348
|
-
})
|
|
349
|
-
.findIndex(key => key === currentVisibleStepKey);
|
|
350
351
|
/**
|
|
351
352
|
* Asserts that step has a shouldRender.
|
|
352
353
|
* Is a workaround because I can't for the life of me get it to look up the step by a correctly typed
|
|
353
354
|
* template literal key of TComponentStepDefinitions and infer that it has a shouldRender automatically.👎
|
|
354
355
|
*/
|
|
355
|
-
|
|
356
|
+
const hasShouldRenderProperty = react.useCallback((obj) => {
|
|
356
357
|
// TODO: remove this check if we can figure out a way to make TypeScript infer the type correctly
|
|
357
358
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, local-rules/no-typescript-assertion
|
|
358
359
|
return typeof obj.shouldRender === "function";
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
basePath,
|
|
417
|
-
path: String(key),
|
|
418
|
-
subTitle: sidebarDescription,
|
|
419
|
-
isSubStep }, String(key)));
|
|
420
|
-
}) }), jsxRuntime.jsx("div", { className: cvaStepContainer(), children: sharedUtils.objectEntries(steps)
|
|
421
|
-
.filter(([_, step]) => { var _a; return (hasShouldRenderProperty(step) ? (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState) : true); })
|
|
422
|
-
.map(([stepKey, props], index) => {
|
|
423
|
-
var _a, _b, _c, _d;
|
|
424
|
-
const entriesOfRenderableSteps = sharedUtils.objectEntries(steps).filter(stepEntry => {
|
|
360
|
+
}, []);
|
|
361
|
+
const currentVisibleStepKey = subStepPath ? `${stepPath}/${subStepPath}` : stepPath;
|
|
362
|
+
const currentVisibleStepIndex = react.useMemo(() => sharedUtils.objectKeys(steps)
|
|
363
|
+
.filter(key => {
|
|
364
|
+
var _a;
|
|
365
|
+
const step = steps[key];
|
|
366
|
+
return hasShouldRenderProperty(step) ? (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState) : true;
|
|
367
|
+
})
|
|
368
|
+
.findIndex(key => key === currentVisibleStepKey), [currentVisibleStepKey, fullFormState, hasShouldRenderProperty, steps]);
|
|
369
|
+
const sidebarSteps = react.useMemo(() => {
|
|
370
|
+
return sharedUtils.objectEntries(steps)
|
|
371
|
+
.filter(([_, step]) => { var _a; return (hasShouldRenderProperty(step) ? (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState) : true); })
|
|
372
|
+
.map(([key, { title, sidebarDescription, defaultValues }], index) => {
|
|
373
|
+
const isPastStep = index < currentVisibleStepIndex;
|
|
374
|
+
const isFutureStep = index > currentVisibleStepIndex;
|
|
375
|
+
const isCurrentStep = key === currentVisibleStepKey;
|
|
376
|
+
const hasDefaultValue = defaultValues !== undefined;
|
|
377
|
+
const isSubStep = key.toString().includes("/");
|
|
378
|
+
const rootSteps = sharedUtils.objectKeys(steps).filter(k => !k.toString().includes("/"));
|
|
379
|
+
const previousStepKey = sharedUtils.objectKeys(steps)[index - 1];
|
|
380
|
+
const stepState = () => {
|
|
381
|
+
if (isCurrentStep) {
|
|
382
|
+
return "active";
|
|
383
|
+
}
|
|
384
|
+
//If the step has a default value, it is considered valid, so we set it to valid in order to show the green checkmark
|
|
385
|
+
if (hasDefaultValue && stepStates[key] === undefined && isEdit) {
|
|
386
|
+
setStepStates(prev => ({
|
|
387
|
+
...prev,
|
|
388
|
+
[key]: "valid",
|
|
389
|
+
}));
|
|
390
|
+
}
|
|
391
|
+
//If the step has a default value, we return done, so we can navigate to that step
|
|
392
|
+
if (hasDefaultValue && isEdit && stepStates[key] === "valid") {
|
|
393
|
+
return "done";
|
|
394
|
+
}
|
|
395
|
+
if (isPastStep) {
|
|
396
|
+
return stepStates[key] === "valid" ? "done" : "invalid";
|
|
397
|
+
}
|
|
398
|
+
if (isFutureStep) {
|
|
399
|
+
return sharedUtils.objectKeys(steps)
|
|
400
|
+
.filter((stepKey, stepIndex) => {
|
|
401
|
+
var _a;
|
|
402
|
+
const step = steps[stepKey];
|
|
403
|
+
return stepIndex < index && hasShouldRenderProperty(step) ? (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState) : true;
|
|
404
|
+
})
|
|
405
|
+
.every((pastStepKey) => stepStates[pastStepKey] === "valid")
|
|
406
|
+
? "ready"
|
|
407
|
+
: "disabled";
|
|
408
|
+
}
|
|
409
|
+
return "ready";
|
|
410
|
+
};
|
|
411
|
+
return (jsxRuntime.jsx(FormWizardSidebarStep, { state: stepState(),
|
|
412
|
+
stepNumber: isSubStep ? undefined : rootSteps.indexOf(key) + 1,
|
|
413
|
+
title,
|
|
414
|
+
onNavigate: () => {
|
|
415
|
+
previousStepKey &&
|
|
416
|
+
setFullFormState(prev => {
|
|
425
417
|
var _a;
|
|
426
|
-
|
|
427
|
-
|
|
418
|
+
return ({
|
|
419
|
+
...prev,
|
|
420
|
+
[previousStepKey]: (_a = stepForms[previousStepKey]) === null || _a === void 0 ? void 0 : _a.getValues(),
|
|
421
|
+
});
|
|
428
422
|
});
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
423
|
+
},
|
|
424
|
+
basePath,
|
|
425
|
+
path: String(key),
|
|
426
|
+
subTitle: sidebarDescription,
|
|
427
|
+
isSubStep }, String(key)));
|
|
428
|
+
});
|
|
429
|
+
}, [
|
|
430
|
+
basePath,
|
|
431
|
+
currentVisibleStepIndex,
|
|
432
|
+
currentVisibleStepKey,
|
|
433
|
+
fullFormState,
|
|
434
|
+
hasShouldRenderProperty,
|
|
435
|
+
isEdit,
|
|
436
|
+
stepForms,
|
|
437
|
+
stepStates,
|
|
438
|
+
steps,
|
|
439
|
+
]);
|
|
440
|
+
const stepWrapper = react.useMemo(() => {
|
|
441
|
+
return sharedUtils.objectEntries(steps)
|
|
442
|
+
.filter(([_, step]) => { var _a; return (hasShouldRenderProperty(step) ? (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState) : true); })
|
|
443
|
+
.map(([stepKey, props], index) => {
|
|
444
|
+
var _a, _b, _c, _d;
|
|
445
|
+
const entriesOfRenderableSteps = sharedUtils.objectEntries(steps).filter(stepEntry => {
|
|
446
|
+
var _a;
|
|
447
|
+
const step = stepEntry[1];
|
|
448
|
+
return hasShouldRenderProperty(step) ? (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState) : true;
|
|
449
|
+
});
|
|
450
|
+
return currentVisibleStepKey === stepKey ? (jsxRuntime.jsx(FormWizardStepWrapper, { ...props,
|
|
451
|
+
basePath,
|
|
452
|
+
stepKey: stepKey.toString(), // TODO: .toString() is a workaround
|
|
453
|
+
stepForms,
|
|
454
|
+
setStepForms,
|
|
455
|
+
onCancel,
|
|
456
|
+
fullFormState,
|
|
457
|
+
setFullFormState,
|
|
458
|
+
setStepStates,
|
|
459
|
+
fullFormSchema,
|
|
460
|
+
lastStepPrimaryActionLabel,
|
|
461
|
+
nextStepPath: (_b = (_a = entriesOfRenderableSteps[index + 1]) === null || _a === void 0 ? void 0 : _a[0].toString()) !== null && _b !== void 0 ? _b : null,
|
|
462
|
+
previousStepPath: (_d = (_c = entriesOfRenderableSteps[index - 1]) === null || _c === void 0 ? void 0 : _c[0].toString()) !== null && _d !== void 0 ? _d : null }, stepKey.toString())) : null;
|
|
463
|
+
});
|
|
464
|
+
}, [
|
|
465
|
+
basePath,
|
|
466
|
+
currentVisibleStepKey,
|
|
467
|
+
fullFormSchema,
|
|
468
|
+
fullFormState,
|
|
469
|
+
hasShouldRenderProperty,
|
|
470
|
+
lastStepPrimaryActionLabel,
|
|
471
|
+
onCancel,
|
|
472
|
+
stepForms,
|
|
473
|
+
steps,
|
|
474
|
+
]);
|
|
475
|
+
return (jsxRuntime.jsx("div", { className: cvaFormWizardContainer({ className }), "data-testid": dataTestId, children: jsxRuntime.jsxs("div", { className: cvaFormWizardLayout(), children: [jsxRuntime.jsx(FormWizardSidebar, { children: sidebarSteps }), jsxRuntime.jsx("div", { className: cvaStepContainer(), children: stepWrapper })] }) }));
|
|
443
476
|
};
|
|
444
477
|
|
|
445
478
|
/**
|
package/index.esm.js
CHANGED
|
@@ -59,14 +59,13 @@ const setupLibraryTranslations = () => {
|
|
|
59
59
|
const cvaFillSection = cvaMerge(["flex-grow", "p-0"]);
|
|
60
60
|
const cvaSidebar = cvaMerge([
|
|
61
61
|
"flex",
|
|
62
|
-
"flex-
|
|
63
|
-
"overflow-x-
|
|
62
|
+
"flex-rows",
|
|
63
|
+
"overflow-x-auto",
|
|
64
64
|
"h-full",
|
|
65
|
-
"
|
|
66
|
-
"p-responsive-space",
|
|
67
|
-
"bg-slate-50",
|
|
65
|
+
"bg-white",
|
|
68
66
|
"border-r",
|
|
69
67
|
"border-slate-200",
|
|
68
|
+
"@3xl:flex-col",
|
|
70
69
|
]);
|
|
71
70
|
|
|
72
71
|
/**
|
|
@@ -77,13 +76,13 @@ function FormWizardSidebar({ dataTestId, children, className }) {
|
|
|
77
76
|
return (jsxs("aside", { className: cvaSidebar({ className }), "data-testid": dataTestId, children: [children, jsx("div", { className: cvaFillSection() })] }));
|
|
78
77
|
}
|
|
79
78
|
|
|
80
|
-
const cvaFormWizardSidebarStep = cvaMerge(["p-2", "
|
|
79
|
+
const cvaFormWizardSidebarStep = cvaMerge(["p-2", "hover:bg-slate-200", "transition", "border-b", "border-slate-200", "shrink-0"], {
|
|
81
80
|
variants: {
|
|
82
81
|
state: {
|
|
83
82
|
ready: "",
|
|
84
|
-
active: "
|
|
85
|
-
done: "
|
|
86
|
-
invalid: "
|
|
83
|
+
active: "@3xl:bg-slate-100",
|
|
84
|
+
done: "",
|
|
85
|
+
invalid: "",
|
|
87
86
|
disabled: "pointer-events-none cursor-not-allowed opacity-50 hover:bg-transparent",
|
|
88
87
|
},
|
|
89
88
|
isSubStep: {
|
|
@@ -96,8 +95,15 @@ const cvaFormWizardSidebarStep = cvaMerge(["p-2", "rounded-md", "hover:bg-slate-
|
|
|
96
95
|
isSubStep: false,
|
|
97
96
|
},
|
|
98
97
|
});
|
|
99
|
-
const cvaSectionHeaderContainer = cvaMerge(["flex", "justify-between", "gap-2"], {
|
|
98
|
+
const cvaSectionHeaderContainer = cvaMerge(["flex", "justify-between", "gap-2", "p-2", "rounded"], {
|
|
100
99
|
variants: {
|
|
100
|
+
state: {
|
|
101
|
+
ready: "",
|
|
102
|
+
active: "bg-slate-100",
|
|
103
|
+
done: "",
|
|
104
|
+
invalid: "",
|
|
105
|
+
disabled: "",
|
|
106
|
+
},
|
|
101
107
|
cursor: {
|
|
102
108
|
withCursor: "hover:cursor-pointer",
|
|
103
109
|
default: "",
|
|
@@ -108,7 +114,8 @@ const cvaSectionHeaderContainer = cvaMerge(["flex", "justify-between", "gap-2"],
|
|
|
108
114
|
},
|
|
109
115
|
});
|
|
110
116
|
const cvaHeader = cvaMerge(["text-md"]);
|
|
111
|
-
const cvaSubtitle = cvaMerge(["text-xs", "text-gray-500"]);
|
|
117
|
+
const cvaSubtitle = cvaMerge(["text-xs", "text-gray-500", "hidden", "@3xl:block"]);
|
|
118
|
+
const cvaStepNumberContainer = cvaMerge(["self-center"]);
|
|
112
119
|
const cvaStepNumber = cvaMerge(["flex", "justify-center", "text-xs", "items-center", "m-0.5", "w-[20px]", "h-[20px]", "bg-gray-100", "rounded-full"], {
|
|
113
120
|
variants: {
|
|
114
121
|
state: {
|
|
@@ -134,7 +141,7 @@ const cvaStepNumber = cvaMerge(["flex", "justify-center", "text-xs", "items-cent
|
|
|
134
141
|
state: "ready",
|
|
135
142
|
},
|
|
136
143
|
});
|
|
137
|
-
const cvaTitleAndDescription = cvaMerge(["justify-center", "flex-1", "flex-col", "
|
|
144
|
+
const cvaTitleAndDescription = cvaMerge(["justify-center", "flex-1", "flex-col", "@3xl:flex"]); //use same container breakpoint as cvaWizardContainer so it collapses at the same time
|
|
138
145
|
|
|
139
146
|
const getStateIcon = (state) => {
|
|
140
147
|
switch (state) {
|
|
@@ -152,7 +159,7 @@ const getStateIcon = (state) => {
|
|
|
152
159
|
*/
|
|
153
160
|
const FormWizardSidebarStep = ({ stepNumber, state, basePath, title, subTitle, path, dataTestId, onNavigate, isSubStep, }) => {
|
|
154
161
|
const stateIcon = getStateIcon(state);
|
|
155
|
-
return (jsx(Link, { className: cvaFormWizardSidebarStep({ state, isSubStep }), "data-testid": dataTestId !== null && dataTestId !== void 0 ? dataTestId : `form-wizard-sidebar-step-${path}`, onClick: () => onNavigate(), params: prev => ({ ...prev, stepPath: path }), search: prev => ({ ...prev }), to: basePath, children: jsxs("div", { className: cvaSectionHeaderContainer(), children: [jsx("div", { children: stateIcon !== null && stateIcon !== void 0 ? stateIcon : jsx("span", { className: cvaStepNumber({ state, isSubStep }), children: stepNumber }) }), jsxs("div", { className: cvaTitleAndDescription(), children: [jsx("
|
|
162
|
+
return (jsx(Link, { className: cvaFormWizardSidebarStep({ state, isSubStep }), "data-testid": dataTestId !== null && dataTestId !== void 0 ? dataTestId : `form-wizard-sidebar-step-${path}`, onClick: () => onNavigate(), params: prev => ({ ...prev, stepPath: path }), search: prev => ({ ...prev }), to: basePath, children: jsxs("div", { className: cvaSectionHeaderContainer({ state }), children: [jsx("div", { className: cvaStepNumberContainer(), children: stateIcon !== null && stateIcon !== void 0 ? stateIcon : (jsx("span", { className: cvaStepNumber({ state, isSubStep }), "data-testid": isSubStep ? `form-wizard-sidebar-substep` : `form-wizard-sidebar-step-number-${stepNumber}`, children: stepNumber })) }), jsxs("div", { className: cvaTitleAndDescription(), children: [jsx("h3", { className: cvaHeader(), children: title }), subTitle ? jsx("p", { className: cvaSubtitle(), children: subTitle }) : null] })] }) }));
|
|
156
163
|
};
|
|
157
164
|
|
|
158
165
|
const cvaFooterContainer = cvaMerge([
|
|
@@ -164,6 +171,7 @@ const cvaFooterContainer = cvaMerge([
|
|
|
164
171
|
"p-responsive-space",
|
|
165
172
|
"border-t",
|
|
166
173
|
"gap-responsive-space",
|
|
174
|
+
"bg-slate-50",
|
|
167
175
|
]);
|
|
168
176
|
const cvaNavigationContainer = cvaMerge(["flex", "gap-responsive-space"]);
|
|
169
177
|
|
|
@@ -180,8 +188,8 @@ const cvaFormWizardHeader = cvaMerge([
|
|
|
180
188
|
"flex",
|
|
181
189
|
"flex-col",
|
|
182
190
|
"p-responsive-space",
|
|
183
|
-
"border-b-slate-
|
|
184
|
-
"border-b
|
|
191
|
+
"border-b-slate-200",
|
|
192
|
+
"border-b",
|
|
185
193
|
]);
|
|
186
194
|
|
|
187
195
|
/**
|
|
@@ -189,10 +197,10 @@ const cvaFormWizardHeader = cvaMerge([
|
|
|
189
197
|
* In most cases you should not be using this component directly, but rather use the FormWizard component.
|
|
190
198
|
*/
|
|
191
199
|
const FormWizardHeader = ({ title, description, className, dataTestId }) => {
|
|
192
|
-
return (jsxs("div", { className: cvaFormWizardHeader({ className }), "data-testid": dataTestId, children: [jsx(Heading, { variant: "
|
|
200
|
+
return (jsxs("div", { className: cvaFormWizardHeader({ className }), "data-testid": dataTestId, children: [jsx(Heading, { variant: "secondary", children: title }), description ? (jsx(Heading, { subtle: true, variant: "subtitle", children: description })) : null] }));
|
|
193
201
|
};
|
|
194
202
|
|
|
195
|
-
const cvaFormWizardComponentContainer = cvaMerge(["p-responsive-space", "overflow-y-auto"]);
|
|
203
|
+
const cvaFormWizardComponentContainer = cvaMerge(["p-responsive-space", "overflow-y-auto", "bg-slate-50"]);
|
|
196
204
|
const cvaFormWizardComponentStepForm = cvaMerge(["grid-rows-min-fr-min", "grid", "h-full", "overflow-hidden"]);
|
|
197
205
|
|
|
198
206
|
/**
|
|
@@ -308,7 +316,8 @@ const cvaFormWizardContainer = cvaMerge(["h-full"]);
|
|
|
308
316
|
const cvaFormWizardLayout = cvaMerge([
|
|
309
317
|
"grid",
|
|
310
318
|
"h-full",
|
|
311
|
-
"grid-
|
|
319
|
+
"grid-rows-min-fr",
|
|
320
|
+
"@3xl:grid-rows-1",
|
|
312
321
|
"@3xl:grid-cols-[240px_1fr]", //use same breakpoint for wizardSidebarSection so it collapses at the same time
|
|
313
322
|
"@4xl:grid-cols-[320px_1fr]",
|
|
314
323
|
]);
|
|
@@ -337,107 +346,131 @@ const FormWizard = ({ lastStepPrimaryActionLabel, isEdit, steps, fullFormSchema,
|
|
|
337
346
|
if (fullFormSchema.shape[keyToCheck] === undefined) {
|
|
338
347
|
navigate({ to: basePath, params: { stepPath: firstSchemaKey } });
|
|
339
348
|
}
|
|
340
|
-
const currentVisibleStepKey = subStepPath ? `${stepPath}/${subStepPath}` : stepPath;
|
|
341
|
-
const currentVisibleStepIndex = objectKeys(steps)
|
|
342
|
-
.filter(key => {
|
|
343
|
-
var _a;
|
|
344
|
-
const step = steps[key];
|
|
345
|
-
return hasShouldRenderProperty(step) ? (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState) : true;
|
|
346
|
-
})
|
|
347
|
-
.findIndex(key => key === currentVisibleStepKey);
|
|
348
349
|
/**
|
|
349
350
|
* Asserts that step has a shouldRender.
|
|
350
351
|
* Is a workaround because I can't for the life of me get it to look up the step by a correctly typed
|
|
351
352
|
* template literal key of TComponentStepDefinitions and infer that it has a shouldRender automatically.👎
|
|
352
353
|
*/
|
|
353
|
-
|
|
354
|
+
const hasShouldRenderProperty = useCallback((obj) => {
|
|
354
355
|
// TODO: remove this check if we can figure out a way to make TypeScript infer the type correctly
|
|
355
356
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, local-rules/no-typescript-assertion
|
|
356
357
|
return typeof obj.shouldRender === "function";
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
basePath,
|
|
415
|
-
path: String(key),
|
|
416
|
-
subTitle: sidebarDescription,
|
|
417
|
-
isSubStep }, String(key)));
|
|
418
|
-
}) }), jsx("div", { className: cvaStepContainer(), children: objectEntries(steps)
|
|
419
|
-
.filter(([_, step]) => { var _a; return (hasShouldRenderProperty(step) ? (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState) : true); })
|
|
420
|
-
.map(([stepKey, props], index) => {
|
|
421
|
-
var _a, _b, _c, _d;
|
|
422
|
-
const entriesOfRenderableSteps = objectEntries(steps).filter(stepEntry => {
|
|
358
|
+
}, []);
|
|
359
|
+
const currentVisibleStepKey = subStepPath ? `${stepPath}/${subStepPath}` : stepPath;
|
|
360
|
+
const currentVisibleStepIndex = useMemo(() => objectKeys(steps)
|
|
361
|
+
.filter(key => {
|
|
362
|
+
var _a;
|
|
363
|
+
const step = steps[key];
|
|
364
|
+
return hasShouldRenderProperty(step) ? (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState) : true;
|
|
365
|
+
})
|
|
366
|
+
.findIndex(key => key === currentVisibleStepKey), [currentVisibleStepKey, fullFormState, hasShouldRenderProperty, steps]);
|
|
367
|
+
const sidebarSteps = useMemo(() => {
|
|
368
|
+
return objectEntries(steps)
|
|
369
|
+
.filter(([_, step]) => { var _a; return (hasShouldRenderProperty(step) ? (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState) : true); })
|
|
370
|
+
.map(([key, { title, sidebarDescription, defaultValues }], index) => {
|
|
371
|
+
const isPastStep = index < currentVisibleStepIndex;
|
|
372
|
+
const isFutureStep = index > currentVisibleStepIndex;
|
|
373
|
+
const isCurrentStep = key === currentVisibleStepKey;
|
|
374
|
+
const hasDefaultValue = defaultValues !== undefined;
|
|
375
|
+
const isSubStep = key.toString().includes("/");
|
|
376
|
+
const rootSteps = objectKeys(steps).filter(k => !k.toString().includes("/"));
|
|
377
|
+
const previousStepKey = objectKeys(steps)[index - 1];
|
|
378
|
+
const stepState = () => {
|
|
379
|
+
if (isCurrentStep) {
|
|
380
|
+
return "active";
|
|
381
|
+
}
|
|
382
|
+
//If the step has a default value, it is considered valid, so we set it to valid in order to show the green checkmark
|
|
383
|
+
if (hasDefaultValue && stepStates[key] === undefined && isEdit) {
|
|
384
|
+
setStepStates(prev => ({
|
|
385
|
+
...prev,
|
|
386
|
+
[key]: "valid",
|
|
387
|
+
}));
|
|
388
|
+
}
|
|
389
|
+
//If the step has a default value, we return done, so we can navigate to that step
|
|
390
|
+
if (hasDefaultValue && isEdit && stepStates[key] === "valid") {
|
|
391
|
+
return "done";
|
|
392
|
+
}
|
|
393
|
+
if (isPastStep) {
|
|
394
|
+
return stepStates[key] === "valid" ? "done" : "invalid";
|
|
395
|
+
}
|
|
396
|
+
if (isFutureStep) {
|
|
397
|
+
return objectKeys(steps)
|
|
398
|
+
.filter((stepKey, stepIndex) => {
|
|
399
|
+
var _a;
|
|
400
|
+
const step = steps[stepKey];
|
|
401
|
+
return stepIndex < index && hasShouldRenderProperty(step) ? (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState) : true;
|
|
402
|
+
})
|
|
403
|
+
.every((pastStepKey) => stepStates[pastStepKey] === "valid")
|
|
404
|
+
? "ready"
|
|
405
|
+
: "disabled";
|
|
406
|
+
}
|
|
407
|
+
return "ready";
|
|
408
|
+
};
|
|
409
|
+
return (jsx(FormWizardSidebarStep, { state: stepState(),
|
|
410
|
+
stepNumber: isSubStep ? undefined : rootSteps.indexOf(key) + 1,
|
|
411
|
+
title,
|
|
412
|
+
onNavigate: () => {
|
|
413
|
+
previousStepKey &&
|
|
414
|
+
setFullFormState(prev => {
|
|
423
415
|
var _a;
|
|
424
|
-
|
|
425
|
-
|
|
416
|
+
return ({
|
|
417
|
+
...prev,
|
|
418
|
+
[previousStepKey]: (_a = stepForms[previousStepKey]) === null || _a === void 0 ? void 0 : _a.getValues(),
|
|
419
|
+
});
|
|
426
420
|
});
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
421
|
+
},
|
|
422
|
+
basePath,
|
|
423
|
+
path: String(key),
|
|
424
|
+
subTitle: sidebarDescription,
|
|
425
|
+
isSubStep }, String(key)));
|
|
426
|
+
});
|
|
427
|
+
}, [
|
|
428
|
+
basePath,
|
|
429
|
+
currentVisibleStepIndex,
|
|
430
|
+
currentVisibleStepKey,
|
|
431
|
+
fullFormState,
|
|
432
|
+
hasShouldRenderProperty,
|
|
433
|
+
isEdit,
|
|
434
|
+
stepForms,
|
|
435
|
+
stepStates,
|
|
436
|
+
steps,
|
|
437
|
+
]);
|
|
438
|
+
const stepWrapper = useMemo(() => {
|
|
439
|
+
return objectEntries(steps)
|
|
440
|
+
.filter(([_, step]) => { var _a; return (hasShouldRenderProperty(step) ? (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState) : true); })
|
|
441
|
+
.map(([stepKey, props], index) => {
|
|
442
|
+
var _a, _b, _c, _d;
|
|
443
|
+
const entriesOfRenderableSteps = objectEntries(steps).filter(stepEntry => {
|
|
444
|
+
var _a;
|
|
445
|
+
const step = stepEntry[1];
|
|
446
|
+
return hasShouldRenderProperty(step) ? (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState) : true;
|
|
447
|
+
});
|
|
448
|
+
return currentVisibleStepKey === stepKey ? (jsx(FormWizardStepWrapper, { ...props,
|
|
449
|
+
basePath,
|
|
450
|
+
stepKey: stepKey.toString(), // TODO: .toString() is a workaround
|
|
451
|
+
stepForms,
|
|
452
|
+
setStepForms,
|
|
453
|
+
onCancel,
|
|
454
|
+
fullFormState,
|
|
455
|
+
setFullFormState,
|
|
456
|
+
setStepStates,
|
|
457
|
+
fullFormSchema,
|
|
458
|
+
lastStepPrimaryActionLabel,
|
|
459
|
+
nextStepPath: (_b = (_a = entriesOfRenderableSteps[index + 1]) === null || _a === void 0 ? void 0 : _a[0].toString()) !== null && _b !== void 0 ? _b : null,
|
|
460
|
+
previousStepPath: (_d = (_c = entriesOfRenderableSteps[index - 1]) === null || _c === void 0 ? void 0 : _c[0].toString()) !== null && _d !== void 0 ? _d : null }, stepKey.toString())) : null;
|
|
461
|
+
});
|
|
462
|
+
}, [
|
|
463
|
+
basePath,
|
|
464
|
+
currentVisibleStepKey,
|
|
465
|
+
fullFormSchema,
|
|
466
|
+
fullFormState,
|
|
467
|
+
hasShouldRenderProperty,
|
|
468
|
+
lastStepPrimaryActionLabel,
|
|
469
|
+
onCancel,
|
|
470
|
+
stepForms,
|
|
471
|
+
steps,
|
|
472
|
+
]);
|
|
473
|
+
return (jsx("div", { className: cvaFormWizardContainer({ className }), "data-testid": dataTestId, children: jsxs("div", { className: cvaFormWizardLayout(), children: [jsx(FormWizardSidebar, { children: sidebarSteps }), jsx("div", { className: cvaStepContainer(), children: stepWrapper })] }) }));
|
|
441
474
|
};
|
|
442
475
|
|
|
443
476
|
/**
|
package/package.json
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { RegisteredRouter, RouteIds } from "@tanstack/react-router";
|
|
2
2
|
import { CommonProps } from "@trackunit/react-components";
|
|
3
3
|
import { MappedOmit } from "@trackunit/shared-utils";
|
|
4
|
-
import { ReactNode } from "react";
|
|
5
4
|
import { UseFormReturn } from "react-hook-form";
|
|
6
5
|
import { AnyZodObject, SomeZodObject, z } from "zod";
|
|
7
6
|
export type StepState = "active" | "done" | "invalid" | "disabled" | "ready";
|
|
@@ -17,7 +16,7 @@ export type StepComponentPropsInner<TStepSchema extends TFullSchemaValue[keyof T
|
|
|
17
16
|
};
|
|
18
17
|
export interface SimpleStepProps {
|
|
19
18
|
title: string;
|
|
20
|
-
description: string
|
|
19
|
+
description: string;
|
|
21
20
|
sidebarDescription?: string;
|
|
22
21
|
containerClassName?: string;
|
|
23
22
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { CommonProps } from "@trackunit/react-components";
|
|
2
|
-
import { ReactNode } from "react";
|
|
3
2
|
interface FormWizardHeaderProps extends CommonProps {
|
|
4
3
|
title: string;
|
|
5
|
-
description?: string
|
|
4
|
+
description?: string;
|
|
6
5
|
}
|
|
7
6
|
/**
|
|
8
7
|
* The header component for the FormWizard.
|
|
@@ -3,10 +3,12 @@ export declare const cvaFormWizardSidebarStep: (props?: ({
|
|
|
3
3
|
isSubStep?: boolean | null | undefined;
|
|
4
4
|
} & import("class-variance-authority/dist/types").ClassProp) | undefined) => string;
|
|
5
5
|
export declare const cvaSectionHeaderContainer: (props?: ({
|
|
6
|
+
state?: "ready" | "active" | "done" | "invalid" | "disabled" | null | undefined;
|
|
6
7
|
cursor?: "default" | "withCursor" | null | undefined;
|
|
7
8
|
} & import("class-variance-authority/dist/types").ClassProp) | undefined) => string;
|
|
8
9
|
export declare const cvaHeader: (props?: import("class-variance-authority/dist/types").ClassProp | undefined) => string;
|
|
9
10
|
export declare const cvaSubtitle: (props?: import("class-variance-authority/dist/types").ClassProp | undefined) => string;
|
|
11
|
+
export declare const cvaStepNumberContainer: (props?: import("class-variance-authority/dist/types").ClassProp | undefined) => string;
|
|
10
12
|
export declare const cvaStepNumber: (props?: ({
|
|
11
13
|
state?: "ready" | "active" | "done" | "invalid" | "disabled" | null | undefined;
|
|
12
14
|
isSubStep?: boolean | null | undefined;
|
|
@@ -6,7 +6,7 @@ import { FormWizardStepFormReturnMap } from "../FormWizard/FormWizard";
|
|
|
6
6
|
import { SimpleStepProps, StepComponentPropsInner, StepDefinitions, StringWithStepKey } from "../FormWizard/useFormWizard";
|
|
7
7
|
export type StepFormValidity = "valid" | "invalid";
|
|
8
8
|
export interface FormWizardStepWrapperProps<TStepKey extends keyof z.TypeOf<TFullSchema>, TFullSchema extends AnyZodObject> extends SimpleStepProps {
|
|
9
|
-
description: string
|
|
9
|
+
description: string;
|
|
10
10
|
lastStepPrimaryActionLabel: string;
|
|
11
11
|
nextStepPath: string | null;
|
|
12
12
|
basePath: StringWithStepKey<`${RoutePaths<RegisteredRouter["routeTree"]>}`>;
|