@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 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-col",
65
- "overflow-x-hidden",
64
+ "flex-rows",
65
+ "overflow-x-auto",
66
66
  "h-full",
67
- "gap-2",
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", "rounded-md", "hover:bg-slate-200", "transition"], {
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: "text-primary-600 bg-slate-100",
87
- done: "text-success-600",
88
- invalid: "text-warning-600",
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", "hidden", "@3xl:flex"]); //use same container breakpoint as cvaWizardContainer so it collapses at the same time
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("h1", { className: cvaHeader(), children: title }), subTitle ? jsxRuntime.jsx("p", { className: cvaSubtitle(), children: subTitle }) : null] })] }) }));
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-300",
186
- "border-b-2",
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: "primary", children: title }), description ? description : null] }));
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-cols-min-fr",
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
- function hasShouldRenderProperty(obj) {
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
- return (jsxRuntime.jsx("div", { className: cvaFormWizardContainer({ className }), "data-testid": dataTestId, children: jsxRuntime.jsxs("div", { className: cvaFormWizardLayout(), children: [jsxRuntime.jsx(FormWizardSidebar, { children: sharedUtils.objectEntries(steps)
361
- .filter(([_, step]) => { var _a; return (hasShouldRenderProperty(step) ? (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState) : true); })
362
- .map(([key, { title, sidebarDescription, defaultValues }], index) => {
363
- const isPastStep = index < currentVisibleStepIndex;
364
- const isFutureStep = index > currentVisibleStepIndex;
365
- const isCurrentStep = key === currentVisibleStepKey;
366
- const hasDefaultValue = defaultValues !== undefined;
367
- const isSubStep = key.toString().includes("/");
368
- const rootSteps = sharedUtils.objectKeys(steps).filter(k => !k.toString().includes("/"));
369
- const previousStepKey = sharedUtils.objectKeys(steps)[index - 1];
370
- const stepState = () => {
371
- if (isCurrentStep) {
372
- return "active";
373
- }
374
- //If the step has a default value, it is considered valid, so we set it to valid in order to show the green checkmark
375
- if (hasDefaultValue && stepStates[key] === undefined && isEdit) {
376
- setStepStates(prev => ({
377
- ...prev,
378
- [key]: "valid",
379
- }));
380
- }
381
- //If the step has a default value, we return done, so we can navigate to that step
382
- if (hasDefaultValue && isEdit && stepStates[key] === "valid") {
383
- return "done";
384
- }
385
- if (isPastStep) {
386
- return stepStates[key] === "valid" ? "done" : "invalid";
387
- }
388
- if (isFutureStep) {
389
- return sharedUtils.objectKeys(steps)
390
- .filter((stepKey, stepIndex) => {
391
- var _a;
392
- const step = steps[stepKey];
393
- return stepIndex < index && hasShouldRenderProperty(step)
394
- ? (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState)
395
- : true;
396
- })
397
- .every((pastStepKey) => stepStates[pastStepKey] === "valid")
398
- ? "ready"
399
- : "disabled";
400
- }
401
- return "ready";
402
- };
403
- return (jsxRuntime.jsx(FormWizardSidebarStep, { state: stepState(),
404
- stepNumber: isSubStep ? undefined : rootSteps.indexOf(key) + 1,
405
- title,
406
- onNavigate: () => {
407
- previousStepKey &&
408
- setFullFormState(prev => {
409
- var _a;
410
- return ({
411
- ...prev,
412
- [previousStepKey]: (_a = stepForms[previousStepKey]) === null || _a === void 0 ? void 0 : _a.getValues(),
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
- const step = stepEntry[1];
427
- return hasShouldRenderProperty(step) ? (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState) : true;
418
+ return ({
419
+ ...prev,
420
+ [previousStepKey]: (_a = stepForms[previousStepKey]) === null || _a === void 0 ? void 0 : _a.getValues(),
421
+ });
428
422
  });
429
- return currentVisibleStepKey === stepKey ? (jsxRuntime.jsx(FormWizardStepWrapper, { ...props,
430
- basePath,
431
- stepKey: stepKey.toString(), // TODO: .toString() is a workaround
432
- stepForms,
433
- setStepForms,
434
- onCancel,
435
- fullFormState,
436
- setFullFormState,
437
- setStepStates,
438
- fullFormSchema,
439
- lastStepPrimaryActionLabel,
440
- nextStepPath: (_b = (_a = entriesOfRenderableSteps[index + 1]) === null || _a === void 0 ? void 0 : _a[0].toString()) !== null && _b !== void 0 ? _b : null,
441
- previousStepPath: (_d = (_c = entriesOfRenderableSteps[index - 1]) === null || _c === void 0 ? void 0 : _c[0].toString()) !== null && _d !== void 0 ? _d : null }, stepKey.toString())) : null;
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-col",
63
- "overflow-x-hidden",
62
+ "flex-rows",
63
+ "overflow-x-auto",
64
64
  "h-full",
65
- "gap-2",
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", "rounded-md", "hover:bg-slate-200", "transition"], {
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: "text-primary-600 bg-slate-100",
85
- done: "text-success-600",
86
- invalid: "text-warning-600",
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", "hidden", "@3xl:flex"]); //use same container breakpoint as cvaWizardContainer so it collapses at the same time
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("h1", { className: cvaHeader(), children: title }), subTitle ? jsx("p", { className: cvaSubtitle(), children: subTitle }) : null] })] }) }));
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-300",
184
- "border-b-2",
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: "primary", children: title }), description ? description : null] }));
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-cols-min-fr",
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
- function hasShouldRenderProperty(obj) {
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
- return (jsx("div", { className: cvaFormWizardContainer({ className }), "data-testid": dataTestId, children: jsxs("div", { className: cvaFormWizardLayout(), children: [jsx(FormWizardSidebar, { children: objectEntries(steps)
359
- .filter(([_, step]) => { var _a; return (hasShouldRenderProperty(step) ? (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState) : true); })
360
- .map(([key, { title, sidebarDescription, defaultValues }], index) => {
361
- const isPastStep = index < currentVisibleStepIndex;
362
- const isFutureStep = index > currentVisibleStepIndex;
363
- const isCurrentStep = key === currentVisibleStepKey;
364
- const hasDefaultValue = defaultValues !== undefined;
365
- const isSubStep = key.toString().includes("/");
366
- const rootSteps = objectKeys(steps).filter(k => !k.toString().includes("/"));
367
- const previousStepKey = objectKeys(steps)[index - 1];
368
- const stepState = () => {
369
- if (isCurrentStep) {
370
- return "active";
371
- }
372
- //If the step has a default value, it is considered valid, so we set it to valid in order to show the green checkmark
373
- if (hasDefaultValue && stepStates[key] === undefined && isEdit) {
374
- setStepStates(prev => ({
375
- ...prev,
376
- [key]: "valid",
377
- }));
378
- }
379
- //If the step has a default value, we return done, so we can navigate to that step
380
- if (hasDefaultValue && isEdit && stepStates[key] === "valid") {
381
- return "done";
382
- }
383
- if (isPastStep) {
384
- return stepStates[key] === "valid" ? "done" : "invalid";
385
- }
386
- if (isFutureStep) {
387
- return objectKeys(steps)
388
- .filter((stepKey, stepIndex) => {
389
- var _a;
390
- const step = steps[stepKey];
391
- return stepIndex < index && hasShouldRenderProperty(step)
392
- ? (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState)
393
- : true;
394
- })
395
- .every((pastStepKey) => stepStates[pastStepKey] === "valid")
396
- ? "ready"
397
- : "disabled";
398
- }
399
- return "ready";
400
- };
401
- return (jsx(FormWizardSidebarStep, { state: stepState(),
402
- stepNumber: isSubStep ? undefined : rootSteps.indexOf(key) + 1,
403
- title,
404
- onNavigate: () => {
405
- previousStepKey &&
406
- setFullFormState(prev => {
407
- var _a;
408
- return ({
409
- ...prev,
410
- [previousStepKey]: (_a = stepForms[previousStepKey]) === null || _a === void 0 ? void 0 : _a.getValues(),
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
- const step = stepEntry[1];
425
- return hasShouldRenderProperty(step) ? (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState) : true;
416
+ return ({
417
+ ...prev,
418
+ [previousStepKey]: (_a = stepForms[previousStepKey]) === null || _a === void 0 ? void 0 : _a.getValues(),
419
+ });
426
420
  });
427
- return currentVisibleStepKey === stepKey ? (jsx(FormWizardStepWrapper, { ...props,
428
- basePath,
429
- stepKey: stepKey.toString(), // TODO: .toString() is a workaround
430
- stepForms,
431
- setStepForms,
432
- onCancel,
433
- fullFormState,
434
- setFullFormState,
435
- setStepStates,
436
- fullFormSchema,
437
- lastStepPrimaryActionLabel,
438
- nextStepPath: (_b = (_a = entriesOfRenderableSteps[index + 1]) === null || _a === void 0 ? void 0 : _a[0].toString()) !== null && _b !== void 0 ? _b : null,
439
- previousStepPath: (_d = (_c = entriesOfRenderableSteps[index - 1]) === null || _c === void 0 ? void 0 : _c[0].toString()) !== null && _d !== void 0 ? _d : null }, stepKey.toString())) : null;
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-form-wizard",
3
- "version": "0.1.211",
3
+ "version": "0.1.213",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "dependencies": {
@@ -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 | ReactNode;
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 | ReactNode;
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 | ReactNode;
9
+ description: string;
10
10
  lastStepPrimaryActionLabel: string;
11
11
  nextStepPath: string | null;
12
12
  basePath: StringWithStepKey<`${RoutePaths<RegisteredRouter["routeTree"]>}`>;