@ttoss/react-wizard 0.4.8 → 0.4.10

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/dist/index.cjs ADDED
@@ -0,0 +1,511 @@
1
+ /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
+ Object.defineProperty(exports, Symbol.toStringTag, {
3
+ value: 'Module'
4
+ });
5
+ //#region \0rolldown/runtime.js
6
+ var __create = Object.create;
7
+ var __defProp = Object.defineProperty;
8
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
9
+ var __getOwnPropNames = Object.getOwnPropertyNames;
10
+ var __getProtoOf = Object.getPrototypeOf;
11
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
15
+ key = keys[i];
16
+ if (!__hasOwnProp.call(to, key) && key !== except) {
17
+ __defProp(to, key, {
18
+ get: (k => from[k]).bind(null, key),
19
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
20
+ });
21
+ }
22
+ }
23
+ }
24
+ return to;
25
+ };
26
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
27
+ value: mod,
28
+ enumerable: true
29
+ }) : target, mod));
30
+
31
+ //#endregion
32
+ let _ttoss_react_i18n = require("@ttoss/react-i18n");
33
+ let _ttoss_ui = require("@ttoss/ui");
34
+ let react = require("react");
35
+ let react$1 = __toESM(react, 1);
36
+ react = __toESM(react);
37
+ let _chakra_ui_react = require("@chakra-ui/react");
38
+ let react_jsx_runtime = require("react/jsx-runtime");
39
+
40
+ //#region src/Wizard.styles.ts
41
+ const gradientFlow = (0, _ttoss_ui.keyframes)({
42
+ "0%": {
43
+ backgroundPosition: "0% 50%"
44
+ },
45
+ "50%": {
46
+ backgroundPosition: "100% 50%"
47
+ },
48
+ "100%": {
49
+ backgroundPosition: "0% 50%"
50
+ }
51
+ });
52
+ const getAccentGradientBackground = t => {
53
+ const theme = t;
54
+ const start = theme.colors?.action?.background?.accent?.default || theme.colors?.input?.background?.accent?.default;
55
+ if (!start) return void 0;
56
+ return `linear-gradient(270deg, ${start}, ${theme.colors?.action?.background?.accent?.active || theme.colors?.input?.background?.accent?.active || start}, ${start})`;
57
+ };
58
+ const getPrimaryGradientBackground = t => {
59
+ const theme = t;
60
+ const start = theme.colors?.action?.background?.primary?.default;
61
+ if (!start) return void 0;
62
+ return `linear-gradient(270deg, ${start}, ${theme.colors?.action?.background?.secondary?.default || start}, ${start})`;
63
+ };
64
+ const getVariantStyles = (variantType = "spotlight-accent") => {
65
+ const variants = {
66
+ "spotlight-accent": {
67
+ accentColor: "action.background.accent.default",
68
+ accentTextColor: "action.text.accent.default",
69
+ borderColor: "display.border.muted.default",
70
+ primaryButtonVariant: "accent",
71
+ gradientBackground: getAccentGradientBackground
72
+ },
73
+ "spotlight-primary": {
74
+ accentColor: "action.background.primary.default",
75
+ accentTextColor: "display.text.accent.default",
76
+ borderColor: "display.border.muted.default",
77
+ primaryButtonVariant: "primary",
78
+ gradientBackground: getPrimaryGradientBackground
79
+ },
80
+ primary: {
81
+ accentColor: "action.background.primary.default",
82
+ accentTextColor: "action.text.primary.default",
83
+ borderColor: "display.border.muted.default",
84
+ primaryButtonVariant: "primary"
85
+ },
86
+ secondary: {
87
+ accentColor: "action.background.secondary.default",
88
+ accentTextColor: "action.text.primary.default",
89
+ borderColor: "display.border.muted.default",
90
+ primaryButtonVariant: "secondary"
91
+ },
92
+ accent: {
93
+ accentColor: "action.background.accent.default",
94
+ accentTextColor: "action.text.accent.default",
95
+ borderColor: "display.border.muted.default",
96
+ primaryButtonVariant: "accent"
97
+ }
98
+ };
99
+ return variants[variantType] ?? variants["spotlight-accent"];
100
+ };
101
+ const getWizardShellSx = (variant = "spotlight-accent") => {
102
+ return {
103
+ width: "100%",
104
+ minHeight: "300px",
105
+ border: "1px solid",
106
+ borderColor: getVariantStyles(variant).borderColor,
107
+ borderRadius: "8px",
108
+ overflow: "hidden"
109
+ };
110
+ };
111
+ const getWizardStepListSx = ({
112
+ layout,
113
+ variant = "spotlight-accent"
114
+ }) => {
115
+ const isHorizontal = layout === "top" || layout === "bottom";
116
+ const isSpotlight = variant.startsWith("spotlight-");
117
+ const variantStyles = getVariantStyles(variant);
118
+ return {
119
+ position: "relative",
120
+ padding: "6",
121
+ backgroundColor: variantStyles.accentColor,
122
+ ...(isSpotlight ? {
123
+ background: variantStyles.gradientBackground,
124
+ backgroundSize: "400% 400%",
125
+ animation: `${gradientFlow} 6s ease infinite`
126
+ } : {}),
127
+ ...(isHorizontal ? {
128
+ width: "100%"
129
+ } : {
130
+ minWidth: "200px"
131
+ })
132
+ };
133
+ };
134
+ const getWizardStepIndicatorSx = ({
135
+ status,
136
+ variant = "spotlight-accent",
137
+ isClickable
138
+ }) => {
139
+ const variantStyles = getVariantStyles(variant);
140
+ const isCompleted = status === "completed";
141
+ const isUpcoming = status === "upcoming";
142
+ return {
143
+ borderColor: variantStyles.accentTextColor,
144
+ backgroundColor: isCompleted ? variantStyles.accentTextColor : "transparent",
145
+ color: isCompleted ? variantStyles.accentColor : variantStyles.accentTextColor,
146
+ opacity: isUpcoming ? .4 : 1,
147
+ transition: "all 0.2s ease",
148
+ ...(isClickable ? {
149
+ _hover: {
150
+ opacity: 1
151
+ }
152
+ } : {})
153
+ };
154
+ };
155
+ const getWizardStepSeparatorSx = ({
156
+ isCompleted,
157
+ variant = "spotlight-accent"
158
+ }) => {
159
+ return {
160
+ backgroundColor: getVariantStyles(variant).accentTextColor,
161
+ opacity: isCompleted ? 1 : .4
162
+ };
163
+ };
164
+ const getWizardStepTitleSx = ({
165
+ status,
166
+ variant = "spotlight-accent"
167
+ }) => {
168
+ return {
169
+ color: getVariantStyles(variant).accentTextColor,
170
+ textAlign: "center",
171
+ fontWeight: status === "active" ? "bold" : "normal",
172
+ opacity: status === "upcoming" ? .4 : 1
173
+ };
174
+ };
175
+ const getWizardStepDescriptionSx = ({
176
+ status,
177
+ variant = "spotlight-accent"
178
+ }) => {
179
+ return {
180
+ color: getVariantStyles(variant).accentTextColor,
181
+ textAlign: "center",
182
+ opacity: status === "upcoming" ? .4 : 1
183
+ };
184
+ };
185
+ const WizardStepDescriptionFlexSx = {
186
+ flexDirection: "row",
187
+ alignItems: "center",
188
+ justifyContent: "center"
189
+ };
190
+ const WizardStepTextWrapperSx = {
191
+ textAlign: "center"
192
+ };
193
+ const getWizardPrimaryButtonVariant = (variant = "spotlight-accent") => {
194
+ return getVariantStyles(variant).primaryButtonVariant;
195
+ };
196
+
197
+ //#endregion
198
+ //#region src/WizardContext.ts
199
+ const WizardContext = react$1.createContext(null);
200
+ /**
201
+ * Hook to access the wizard context from within step content.
202
+ */
203
+ const useWizard = () => {
204
+ const context = react$1.useContext(WizardContext);
205
+ if (!context) throw new Error("useWizard must be used within a Wizard component.");
206
+ return context;
207
+ };
208
+
209
+ //#endregion
210
+ //#region src/WizardStepList.tsx
211
+ const WizardStepList = ({
212
+ steps,
213
+ currentStep,
214
+ layout,
215
+ variant,
216
+ allowStepClick,
217
+ getStepStatus,
218
+ onStepClick
219
+ }) => {
220
+ const isHorizontal = layout === "top" || layout === "bottom";
221
+ const orientation = isHorizontal ? "horizontal" : "vertical";
222
+ return /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Flex, {
223
+ role: "navigation",
224
+ "aria-label": "Wizard steps",
225
+ "data-variant": variant,
226
+ sx: getWizardStepListSx({
227
+ layout,
228
+ variant
229
+ }),
230
+ children: /* @__PURE__ */(0, react_jsx_runtime.jsx)(_chakra_ui_react.Steps.Root, {
231
+ step: currentStep,
232
+ count: steps.length,
233
+ orientation,
234
+ children: /* @__PURE__ */(0, react_jsx_runtime.jsx)(_chakra_ui_react.Steps.List, {
235
+ children: steps.map((step, index) => {
236
+ const status = getStepStatus({
237
+ stepIndex: index
238
+ });
239
+ const isClickable = allowStepClick && status === "completed" && index !== currentStep;
240
+ return /* @__PURE__ */(0, react_jsx_runtime.jsxs)(_chakra_ui_react.Steps.Item, {
241
+ index,
242
+ children: [/* @__PURE__ */(0, react_jsx_runtime.jsxs)(_ttoss_ui.Flex, {
243
+ sx: {
244
+ flexDirection: isHorizontal ? "column" : "row",
245
+ alignItems: "center",
246
+ gap: "2"
247
+ },
248
+ children: [/* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Box, {
249
+ role: "button",
250
+ tabIndex: isClickable ? 0 : -1,
251
+ onClick: () => {
252
+ if (isClickable) onStepClick({
253
+ stepIndex: index
254
+ });
255
+ },
256
+ onKeyDown: e => {
257
+ if (isClickable && (e.key === "Enter" || e.key === " ")) {
258
+ e.preventDefault();
259
+ onStepClick({
260
+ stepIndex: index
261
+ });
262
+ }
263
+ },
264
+ "aria-current": status === "active" ? "step" : void 0,
265
+ sx: {
266
+ cursor: isClickable ? "pointer" : "default"
267
+ },
268
+ children: /* @__PURE__ */(0, react_jsx_runtime.jsx)(_chakra_ui_react.Steps.Indicator, {
269
+ css: getWizardStepIndicatorSx({
270
+ status,
271
+ variant,
272
+ isClickable
273
+ }),
274
+ children: /* @__PURE__ */(0, react_jsx_runtime.jsx)(_chakra_ui_react.Steps.Status, {
275
+ complete: "✓",
276
+ incomplete: /* @__PURE__ */(0, react_jsx_runtime.jsx)(_chakra_ui_react.Steps.Number, {})
277
+ })
278
+ })
279
+ }), (step.title || step.description) && /* @__PURE__ */(0, react_jsx_runtime.jsxs)(_ttoss_ui.Box, {
280
+ sx: WizardStepTextWrapperSx,
281
+ children: [step.title && /* @__PURE__ */(0, react_jsx_runtime.jsx)(_chakra_ui_react.Steps.Title, {
282
+ css: getWizardStepTitleSx({
283
+ status,
284
+ variant
285
+ }),
286
+ children: step.title
287
+ }), step.description && /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Flex, {
288
+ sx: WizardStepDescriptionFlexSx,
289
+ children: /* @__PURE__ */(0, react_jsx_runtime.jsx)(_chakra_ui_react.Steps.Description, {
290
+ css: getWizardStepDescriptionSx({
291
+ status,
292
+ variant
293
+ }),
294
+ children: step.description
295
+ })
296
+ })]
297
+ })]
298
+ }), index < steps.length - 1 && /* @__PURE__ */(0, react_jsx_runtime.jsx)(_chakra_ui_react.Steps.Separator, {
299
+ css: getWizardStepSeparatorSx({
300
+ isCompleted: index < currentStep,
301
+ variant
302
+ })
303
+ })]
304
+ }, index);
305
+ })
306
+ })
307
+ })
308
+ });
309
+ };
310
+
311
+ //#endregion
312
+ //#region src/Wizard.tsx
313
+ const messages = (0, _ttoss_react_i18n.defineMessages)({
314
+ previous: {
315
+ defaultMessage: "Previous",
316
+ description: "Label for the previous step button in the wizard."
317
+ },
318
+ next: {
319
+ defaultMessage: "Next",
320
+ description: "Label for the next step button in the wizard."
321
+ },
322
+ finish: {
323
+ defaultMessage: "Finish",
324
+ description: "Label for the finish button on the last wizard step."
325
+ },
326
+ cancel: {
327
+ defaultMessage: "Cancel",
328
+ description: "Label for the cancel button in the wizard."
329
+ }
330
+ });
331
+ const getFlexDirection = layout => {
332
+ switch (layout) {
333
+ case "top":
334
+ return "column";
335
+ case "bottom":
336
+ return "column-reverse";
337
+ case "left":
338
+ return "row";
339
+ case "right":
340
+ return "row-reverse";
341
+ }
342
+ };
343
+ /**
344
+ * Renders a multi-step wizard with step navigation, localized action labels,
345
+ * and support for completion, cancellation, and step changes.
346
+ *
347
+ * @param props - Wizard configuration and step content.
348
+ * @param props.steps - The ordered steps displayed in the wizard.
349
+ * @param props.onComplete - Called when the user finishes the last step.
350
+ * @param props.onCancel - Called when the user cancels the wizard.
351
+ * @param props.onStepChange - Called when the current step changes.
352
+ */
353
+ const Wizard = ({
354
+ steps,
355
+ layout = "top",
356
+ variant = "spotlight-accent",
357
+ onComplete,
358
+ onCancel,
359
+ onStepChange,
360
+ initialStep = 0,
361
+ allowStepClick = true,
362
+ labels
363
+ }) => {
364
+ const {
365
+ intl
366
+ } = (0, _ttoss_react_i18n.useI18n)();
367
+ const [currentStep, setCurrentStep] = react.useState(initialStep);
368
+ const stepValidationRef = react.useRef(null);
369
+ const totalSteps = steps.length;
370
+ const isFirstStep = currentStep === 0;
371
+ const isLastStep = currentStep === totalSteps - 1;
372
+ const buttonLabels = react.useMemo(() => {
373
+ return {
374
+ previous: labels?.previous ?? intl.formatMessage(messages.previous),
375
+ next: labels?.next ?? intl.formatMessage(messages.next),
376
+ finish: labels?.finish ?? intl.formatMessage(messages.finish),
377
+ cancel: labels?.cancel ?? intl.formatMessage(messages.cancel)
378
+ };
379
+ }, [intl, labels]);
380
+ const getStepStatus = react.useCallback(({
381
+ stepIndex
382
+ }) => {
383
+ if (stepIndex < currentStep) return "completed";
384
+ if (stepIndex === currentStep) return "active";
385
+ return "upcoming";
386
+ }, [currentStep]);
387
+ const goToNext = react.useCallback(async () => {
388
+ const step = steps[currentStep];
389
+ if (stepValidationRef.current) {
390
+ if (!(await stepValidationRef.current())) return;
391
+ }
392
+ if (step.onNext) {
393
+ if (!(await step.onNext())) return;
394
+ }
395
+ if (isLastStep) onComplete?.();else {
396
+ const nextStep = currentStep + 1;
397
+ stepValidationRef.current = null;
398
+ setCurrentStep(nextStep);
399
+ onStepChange?.({
400
+ stepIndex: nextStep
401
+ });
402
+ }
403
+ }, [currentStep, steps, isLastStep, onComplete, onStepChange]);
404
+ const goToPrevious = react.useCallback(() => {
405
+ if (!isFirstStep) {
406
+ const prevStep = currentStep - 1;
407
+ stepValidationRef.current = null;
408
+ setCurrentStep(prevStep);
409
+ onStepChange?.({
410
+ stepIndex: prevStep
411
+ });
412
+ }
413
+ }, [currentStep, isFirstStep, onStepChange]);
414
+ const goToStep = react.useCallback(({
415
+ stepIndex
416
+ }) => {
417
+ if (stepIndex >= 0 && stepIndex < totalSteps && stepIndex <= currentStep) {
418
+ stepValidationRef.current = null;
419
+ setCurrentStep(stepIndex);
420
+ onStepChange?.({
421
+ stepIndex
422
+ });
423
+ }
424
+ }, [currentStep, totalSteps, onStepChange]);
425
+ const setStepValidation = react.useCallback(validate => {
426
+ stepValidationRef.current = validate;
427
+ }, []);
428
+ const contextValue = react.useMemo(() => {
429
+ return {
430
+ currentStep,
431
+ totalSteps,
432
+ goToNext,
433
+ goToPrevious,
434
+ goToStep,
435
+ isFirstStep,
436
+ isLastStep,
437
+ getStepStatus,
438
+ setStepValidation
439
+ };
440
+ }, [currentStep, totalSteps, goToNext, goToPrevious, goToStep, isFirstStep, isLastStep, getStepStatus, setStepValidation]);
441
+ return /* @__PURE__ */(0, react_jsx_runtime.jsx)(WizardContext.Provider, {
442
+ value: contextValue,
443
+ children: /* @__PURE__ */(0, react_jsx_runtime.jsxs)(_ttoss_ui.Flex, {
444
+ "data-variant": variant,
445
+ sx: {
446
+ flexDirection: getFlexDirection(layout),
447
+ ...getWizardShellSx(variant)
448
+ },
449
+ children: [/* @__PURE__ */(0, react_jsx_runtime.jsx)(WizardStepList, {
450
+ steps,
451
+ currentStep,
452
+ layout,
453
+ variant,
454
+ allowStepClick,
455
+ getStepStatus,
456
+ onStepClick: goToStep
457
+ }), /* @__PURE__ */(0, react_jsx_runtime.jsxs)(_ttoss_ui.Flex, {
458
+ sx: {
459
+ flexDirection: "column",
460
+ flex: 1,
461
+ padding: "6"
462
+ },
463
+ children: [/* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Box, {
464
+ sx: {
465
+ flex: 1,
466
+ marginBottom: "4"
467
+ },
468
+ children: steps[currentStep].content
469
+ }), /* @__PURE__ */(0, react_jsx_runtime.jsxs)(_ttoss_ui.Flex, {
470
+ sx: {
471
+ justifyContent: "space-between",
472
+ alignItems: "center",
473
+ gap: "3"
474
+ },
475
+ children: [/* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Flex, {
476
+ sx: {
477
+ gap: "3"
478
+ },
479
+ children: onCancel && /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Button, {
480
+ variant: "secondary",
481
+ onClick: onCancel,
482
+ "aria-label": buttonLabels.cancel,
483
+ children: buttonLabels.cancel
484
+ })
485
+ }), /* @__PURE__ */(0, react_jsx_runtime.jsxs)(_ttoss_ui.Flex, {
486
+ sx: {
487
+ gap: "3"
488
+ },
489
+ children: [/* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Button, {
490
+ variant: "secondary",
491
+ onClick: goToPrevious,
492
+ disabled: isFirstStep,
493
+ "aria-label": buttonLabels.previous,
494
+ children: buttonLabels.previous
495
+ }), /* @__PURE__ */(0, react_jsx_runtime.jsx)(_ttoss_ui.Button, {
496
+ variant: getWizardPrimaryButtonVariant(variant),
497
+ onClick: goToNext,
498
+ "aria-label": isLastStep ? buttonLabels.finish : buttonLabels.next,
499
+ children: isLastStep ? buttonLabels.finish : buttonLabels.next
500
+ })]
501
+ })]
502
+ })]
503
+ })]
504
+ })
505
+ });
506
+ };
507
+ Wizard.displayName = "Wizard";
508
+
509
+ //#endregion
510
+ exports.Wizard = Wizard;
511
+ exports.useWizard = useWizard;