@datum-cloud/datum-ui 0.6.0-alpha.b817c77 → 0.6.0-alpha.b8a44ac

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.
Files changed (126) hide show
  1. package/README.md +3 -0
  2. package/dist/combobox/index.mjs +2 -0
  3. package/dist/combobox-cKTFK4uN.mjs +96 -0
  4. package/dist/components/features/combobox/combobox.d.ts +27 -0
  5. package/dist/components/features/combobox/combobox.d.ts.map +1 -0
  6. package/dist/components/features/combobox/index.d.ts +3 -0
  7. package/dist/components/features/combobox/index.d.ts.map +1 -0
  8. package/dist/components/features/combobox/types.d.ts +78 -0
  9. package/dist/components/features/combobox/types.d.ts.map +1 -0
  10. package/dist/components/features/date-time-picker/date-time-picker.d.ts +9 -0
  11. package/dist/components/features/date-time-picker/date-time-picker.d.ts.map +1 -0
  12. package/dist/components/features/date-time-picker/index.d.ts +3 -0
  13. package/dist/components/features/date-time-picker/index.d.ts.map +1 -0
  14. package/dist/components/features/date-time-picker/types.d.ts +53 -0
  15. package/dist/components/features/date-time-picker/types.d.ts.map +1 -0
  16. package/dist/components/features/date-time-picker/utils/format.d.ts +13 -0
  17. package/dist/components/features/date-time-picker/utils/format.d.ts.map +1 -0
  18. package/dist/components/features/date-time-picker/utils/index.d.ts +3 -0
  19. package/dist/components/features/date-time-picker/utils/index.d.ts.map +1 -0
  20. package/dist/components/features/date-time-picker/utils/timezone.d.ts +23 -0
  21. package/dist/components/features/date-time-picker/utils/timezone.d.ts.map +1 -0
  22. package/dist/components/features/form/adapter-types.d.ts +20 -0
  23. package/dist/components/features/form/adapter-types.d.ts.map +1 -1
  24. package/dist/components/features/form/adapters/conform/conform-adapter.d.ts.map +1 -1
  25. package/dist/components/features/form/adapters/rhf/rhf-adapter.d.ts.map +1 -1
  26. package/dist/components/features/form/components/form-autosearch.d.ts +25 -0
  27. package/dist/components/features/form/components/form-autosearch.d.ts.map +1 -0
  28. package/dist/components/features/form/components/form-combobox.d.ts +76 -0
  29. package/dist/components/features/form/components/form-combobox.d.ts.map +1 -0
  30. package/dist/components/features/form/components/form-copy-box.d.ts +3 -0
  31. package/dist/components/features/form/components/form-copy-box.d.ts.map +1 -1
  32. package/dist/components/features/form/components/form-custom.d.ts.map +1 -1
  33. package/dist/components/features/form/components/form-date-picker.d.ts +38 -0
  34. package/dist/components/features/form/components/form-date-picker.d.ts.map +1 -0
  35. package/dist/components/features/form/components/form-date-time-picker.d.ts +37 -0
  36. package/dist/components/features/form/components/form-date-time-picker.d.ts.map +1 -0
  37. package/dist/components/features/form/components/form-dialog.d.ts.map +1 -1
  38. package/dist/components/features/form/components/form-radio-group.d.ts.map +1 -1
  39. package/dist/components/features/form/components/form-root.d.ts.map +1 -1
  40. package/dist/components/features/form/components/form-time-picker.d.ts +21 -0
  41. package/dist/components/features/form/components/form-time-picker.d.ts.map +1 -0
  42. package/dist/components/features/form/components/form-transfer.d.ts +37 -0
  43. package/dist/components/features/form/components/form-transfer.d.ts.map +1 -0
  44. package/dist/components/features/form/components/index.d.ts +7 -1
  45. package/dist/components/features/form/components/index.d.ts.map +1 -1
  46. package/dist/components/features/form/components/stepper/form-stepper.d.ts.map +1 -1
  47. package/dist/components/features/form/hooks/index.d.ts +1 -1
  48. package/dist/components/features/form/hooks/index.d.ts.map +1 -1
  49. package/dist/components/features/form/hooks/use-form-state.d.ts +36 -0
  50. package/dist/components/features/form/hooks/use-form-state.d.ts.map +1 -0
  51. package/dist/components/features/form/index.d.ts +39 -21
  52. package/dist/components/features/form/index.d.ts.map +1 -1
  53. package/dist/components/features/form/stepper/index.d.ts +17 -0
  54. package/dist/components/features/form/stepper/index.d.ts.map +1 -0
  55. package/dist/components/features/form/types/index.d.ts +36 -0
  56. package/dist/components/features/form/types/index.d.ts.map +1 -1
  57. package/dist/components/features/form/utils/get-field-constraints.d.ts +23 -1
  58. package/dist/components/features/form/utils/get-field-constraints.d.ts.map +1 -1
  59. package/dist/components/features/form/utils/get-schema-defaults.d.ts +24 -0
  60. package/dist/components/features/form/utils/get-schema-defaults.d.ts.map +1 -0
  61. package/dist/components/features/form/utils/zod-helpers.d.ts +12 -0
  62. package/dist/components/features/form/utils/zod-helpers.d.ts.map +1 -0
  63. package/dist/components/features/time-picker/index.d.ts +3 -0
  64. package/dist/components/features/time-picker/index.d.ts.map +1 -0
  65. package/dist/components/features/time-picker/time-picker.d.ts +22 -0
  66. package/dist/components/features/time-picker/time-picker.d.ts.map +1 -0
  67. package/dist/components/features/time-picker/types.d.ts +31 -0
  68. package/dist/components/features/time-picker/types.d.ts.map +1 -0
  69. package/dist/components/features/transfer/components/index.d.ts +9 -0
  70. package/dist/components/features/transfer/components/index.d.ts.map +1 -0
  71. package/dist/components/features/transfer/components/transfer-group.d.ts +7 -0
  72. package/dist/components/features/transfer/components/transfer-group.d.ts.map +1 -0
  73. package/dist/components/features/transfer/components/transfer-item.d.ts +10 -0
  74. package/dist/components/features/transfer/components/transfer-item.d.ts.map +1 -0
  75. package/dist/components/features/transfer/components/transfer-panel.d.ts +18 -0
  76. package/dist/components/features/transfer/components/transfer-panel.d.ts.map +1 -0
  77. package/dist/components/features/transfer/components/transfer-search.d.ts +9 -0
  78. package/dist/components/features/transfer/components/transfer-search.d.ts.map +1 -0
  79. package/dist/components/features/transfer/hooks/use-transfer-dnd.d.ts +26 -0
  80. package/dist/components/features/transfer/hooks/use-transfer-dnd.d.ts.map +1 -0
  81. package/dist/components/features/transfer/hooks/use-transfer-state.d.ts +20 -0
  82. package/dist/components/features/transfer/hooks/use-transfer-state.d.ts.map +1 -0
  83. package/dist/components/features/transfer/index.d.ts +3 -0
  84. package/dist/components/features/transfer/index.d.ts.map +1 -0
  85. package/dist/components/features/transfer/transfer.d.ts +6 -0
  86. package/dist/components/features/transfer/transfer.d.ts.map +1 -0
  87. package/dist/components/features/transfer/types.d.ts +69 -0
  88. package/dist/components/features/transfer/types.d.ts.map +1 -0
  89. package/dist/date-picker/index.mjs +1 -1
  90. package/dist/date-time-picker/index.mjs +2 -0
  91. package/dist/date-time-picker-Dy2jrJoN.mjs +175 -0
  92. package/dist/form/adapters/conform/index.mjs +102 -12
  93. package/dist/form/adapters/rhf/index.mjs +112 -26
  94. package/dist/form/index.mjs +3 -3
  95. package/dist/form/stepper/index.mjs +541 -0
  96. package/dist/form-context-Ccxm-wqL.mjs +17 -0
  97. package/dist/{form-BE1xBne4.mjs → form-mlNLKaB5.mjs} +350 -592
  98. package/dist/{get-field-constraints-BPMW8VvY.mjs → get-field-constraints-BicgDkfH.mjs} +19 -16
  99. package/dist/grid/index.mjs +1 -1
  100. package/dist/hooks/index.mjs +2 -2
  101. package/dist/index.mjs +14 -14
  102. package/dist/input-number/index.mjs +1 -1
  103. package/dist/map/index.mjs +1 -1
  104. package/dist/{map-Cw7u8r6E.mjs → map-CWIQ-eql.mjs} +1 -1
  105. package/dist/more-actions/index.mjs +1 -1
  106. package/dist/page-title/index.mjs +1 -1
  107. package/dist/stepper/index.mjs +1 -320
  108. package/dist/stepper-DvIOp0hh.mjs +321 -0
  109. package/dist/tag-input/index.mjs +1 -1
  110. package/dist/task-queue/index.mjs +1 -1
  111. package/dist/time-picker/index.mjs +2 -0
  112. package/dist/time-picker-BoF7pZZ2.mjs +43 -0
  113. package/dist/transfer/index.mjs +2 -0
  114. package/dist/transfer-B2n8pgEQ.mjs +260 -0
  115. package/package.json +37 -1
  116. /package/dist/{adapter-context-B7L2ucTr.mjs → adapter-context-rWveHhDd.mjs} +0 -0
  117. /package/dist/{col-YBbQ5wlb.mjs → col-1T0Q3SlH.mjs} +0 -0
  118. /package/dist/{hooks-DYjN7lvC.mjs → hooks-D8r2M2U6.mjs} +0 -0
  119. /package/dist/{input-number-DEjXG2I6.mjs → input-number-a7uydAsw.mjs} +0 -0
  120. /package/dist/{map-leaflet-imports-D6nTEOIh.mjs → map-leaflet-imports-CRSKA79m.mjs} +0 -0
  121. /package/dist/{more-actions-BNQ2yfWZ.mjs → more-actions-ILnEZq_E.mjs} +0 -0
  122. /package/dist/{page-title-CNiRNZ7p.mjs → page-title-ChsnpBiH.mjs} +0 -0
  123. /package/dist/{tag-input-BKed-cul.mjs → tag-input-T9cUX9-G.mjs} +0 -0
  124. /package/dist/{task-queue-dropdown-Di_Wjspz.mjs → task-queue-dropdown-Wcbj-f0V.mjs} +0 -0
  125. /package/dist/{to-api-format-Cq4prffn.mjs → to-api-format-Bh3c01gr.mjs} +0 -0
  126. /package/dist/{use-copy-to-clipboard-BGdTmkFV.mjs → use-copy-to-clipboard-uNeeVHC4.mjs} +0 -0
@@ -0,0 +1,541 @@
1
+ import { t as cn } from "../../cn-D2KYQ917.mjs";
2
+ import { t as Button } from "../../button-BllvE9Lm.mjs";
3
+ import { n as useFormContext, t as FormProvider } from "../../form-context-Ccxm-wqL.mjs";
4
+ import { n as useAdapter } from "../../adapter-context-rWveHhDd.mjs";
5
+ import { t as defineStepper } from "../../stepper-DvIOp0hh.mjs";
6
+ import { CheckIcon } from "lucide-react";
7
+ import * as React$1 from "react";
8
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
9
+ import { z } from "zod";
10
+ //#region src/components/features/form/components/stepper/form-stepper.tsx
11
+ const FormStepperContext = React$1.createContext(null);
12
+ function useFormStepperContext() {
13
+ const context = React$1.use(FormStepperContext);
14
+ if (!context) throw new Error("useFormStepperContext must be used within a Form.Stepper component");
15
+ return context;
16
+ }
17
+ /**
18
+ * Recursively unwrap ZodIntersection (from .and()) to extract the base ZodObject.
19
+ *
20
+ * Zod v4 schema types use `def.type` as a string discriminant:
21
+ * - "intersection" (from .and()): merge left + right base objects
22
+ * - "object": return directly
23
+ *
24
+ * Note: In Zod v4, .superRefine() and .refine() return `this` (no wrapper),
25
+ * so only ZodIntersection needs unwrapping.
26
+ */
27
+ function getBaseObject(schema) {
28
+ if (schema.def.type === "intersection") {
29
+ const intersectionDef = schema.def;
30
+ const left = getBaseObject(intersectionDef.left);
31
+ const right = getBaseObject(intersectionDef.right);
32
+ return left.merge(right);
33
+ }
34
+ if (schema.def.type !== "object") return z.object({});
35
+ return schema;
36
+ }
37
+ /**
38
+ * Merge multiple zod schemas into one ZodObject for HTML constraint generation.
39
+ * Handles ZodIntersection (.and()) by unwrapping to base ZodObject shapes.
40
+ * Per-step validation still uses the original schemas with all refinements intact.
41
+ */
42
+ function mergeSchemas(steps) {
43
+ if (steps.length === 0) throw new Error("Form.Stepper requires at least one step");
44
+ return steps.reduce((acc, step, index) => {
45
+ const base = getBaseObject(step.schema);
46
+ if (index === 0) return base;
47
+ return acc.merge(base);
48
+ }, {});
49
+ }
50
+ /**
51
+ * Convert StepConfig[] to Stepperize step format
52
+ */
53
+ function toStepperizeSteps(steps) {
54
+ return steps.map((step) => ({
55
+ id: step.id,
56
+ label: step.label,
57
+ description: step.description
58
+ }));
59
+ }
60
+ /**
61
+ * Form.Stepper - Multi-step form container
62
+ *
63
+ * Uses Stepperize internally for step navigation and a single Conform form
64
+ * instance for all steps. Schemas are auto-merged for unified validation.
65
+ *
66
+ * @example
67
+ * ```tsx
68
+ * const steps = [
69
+ * { id: 'account', label: 'Account', schema: accountSchema },
70
+ * { id: 'profile', label: 'Profile', schema: profileSchema },
71
+ * ];
72
+ *
73
+ * <Form.Stepper steps={steps} onComplete={handleComplete}>
74
+ * <Form.StepperNavigation />
75
+ *
76
+ * <Form.Step id="account">
77
+ * <Form.Field name="email" label="Email" required>
78
+ * <Form.Input type="email" />
79
+ * </Form.Field>
80
+ * </Form.Step>
81
+ *
82
+ * <Form.Step id="profile">
83
+ * <Form.Field name="name" label="Full Name" required>
84
+ * <Form.Input />
85
+ * </Form.Field>
86
+ * </Form.Step>
87
+ *
88
+ * <Form.StepperControls />
89
+ * </Form.Stepper>
90
+ * ```
91
+ */
92
+ function FormStepper({ steps, children, onComplete, onStepChange, initialStep, className, defaultValues, id, formComponent }) {
93
+ const stepperDef = React$1.useMemo(() => {
94
+ return defineStepper(...toStepperizeSteps(steps));
95
+ }, [steps]);
96
+ const initialStepIndex = React$1.useMemo(() => {
97
+ if (!initialStep) return void 0;
98
+ const index = steps.findIndex((s) => s.id === initialStep);
99
+ return index >= 0 ? steps[index].id : void 0;
100
+ }, [initialStep, steps]);
101
+ const { Stepper } = stepperDef;
102
+ const providerProps = initialStepIndex ? { initialStep: initialStepIndex } : {};
103
+ return /* @__PURE__ */ jsx(Stepper.Provider, {
104
+ ...providerProps,
105
+ children: /* @__PURE__ */ jsx(FormStepperContent, {
106
+ steps,
107
+ stepperDef,
108
+ onComplete,
109
+ onStepChange,
110
+ className,
111
+ defaultValues,
112
+ id,
113
+ formComponent,
114
+ children
115
+ })
116
+ });
117
+ }
118
+ FormStepper.displayName = "Form.Stepper";
119
+ function FormStepperContent({ steps, stepperDef, children, onComplete, onStepChange, className, defaultValues, id, formComponent }) {
120
+ const { useStepper } = stepperDef;
121
+ const stepper = useStepper();
122
+ return /* @__PURE__ */ jsx(StepForm, {
123
+ steps,
124
+ stepper,
125
+ currentStepConfig: React$1.useMemo(() => steps.find((s) => s.id === stepper.state.current.data.id) ?? steps[0], [steps, stepper.state.current.data.id]),
126
+ combinedSchema: React$1.useMemo(() => mergeSchemas(steps), [steps]),
127
+ storedValues: React$1.useMemo(() => {
128
+ const allMetadata = steps.reduce((acc, step) => ({
129
+ ...acc,
130
+ ...stepper.metadata.get(step.id) || {}
131
+ }), {});
132
+ return {
133
+ ...defaultValues,
134
+ ...allMetadata
135
+ };
136
+ }, [
137
+ steps,
138
+ stepper,
139
+ defaultValues,
140
+ stepper.state.current.data.id
141
+ ]),
142
+ onComplete,
143
+ onStepChange,
144
+ className,
145
+ id,
146
+ formComponent,
147
+ children
148
+ }, stepper.state.current.data.id);
149
+ }
150
+ function StepForm({ steps, stepper, currentStepConfig, combinedSchema: _combinedSchema, storedValues, children, onComplete, onStepChange, className, id, formComponent: FormComp = "form" }) {
151
+ const adapter = useAdapter();
152
+ const [isSubmitting, setIsSubmitting] = React$1.useState(false);
153
+ const formRef = React$1.useRef(null);
154
+ const currentIndex = stepper.lookup.getIndex(stepper.state.current.data.id);
155
+ const handleStepSubmit = React$1.useCallback(async (data) => {
156
+ stepper.metadata.set(stepper.state.current.data.id, data);
157
+ if (stepper.state.isLast) {
158
+ setIsSubmitting(true);
159
+ try {
160
+ await onComplete({
161
+ ...steps.reduce((acc, step) => ({
162
+ ...acc,
163
+ ...stepper.metadata.get(step.id) || {}
164
+ }), {}),
165
+ ...data
166
+ });
167
+ } catch {} finally {
168
+ setIsSubmitting(false);
169
+ }
170
+ } else {
171
+ const nextStepId = stepper.lookup.getNext(stepper.state.current.data.id)?.id;
172
+ if (nextStepId) {
173
+ stepper.navigation.goTo(nextStepId);
174
+ onStepChange?.(nextStepId, "next");
175
+ }
176
+ }
177
+ }, [
178
+ stepper,
179
+ steps,
180
+ onComplete,
181
+ onStepChange
182
+ ]);
183
+ const instance = adapter.useCreateForm({
184
+ schema: currentStepConfig.schema,
185
+ defaultValues: storedValues,
186
+ mode: "onSubmit",
187
+ id: `${id ?? "stepper"}-${currentStepConfig.id}`,
188
+ onSubmit: handleStepSubmit,
189
+ formRef
190
+ });
191
+ const next = React$1.useCallback(() => {
192
+ formRef.current?.requestSubmit();
193
+ }, []);
194
+ const prev = React$1.useCallback(() => {
195
+ const currentValues = instance.getValues();
196
+ if (Object.keys(currentValues).length > 0) stepper.metadata.set(stepper.state.current.data.id, currentValues);
197
+ const prevStepId = stepper.lookup.getPrev(stepper.state.current.data.id)?.id;
198
+ if (prevStepId) {
199
+ stepper.navigation.goTo(prevStepId);
200
+ onStepChange?.(prevStepId, "prev");
201
+ }
202
+ }, [
203
+ instance,
204
+ stepper,
205
+ onStepChange
206
+ ]);
207
+ const goTo = React$1.useCallback((stepId) => {
208
+ if (stepper.lookup.getIndex(stepId) < currentIndex) {
209
+ const currentValues = instance.getValues();
210
+ if (Object.keys(currentValues).length > 0) stepper.metadata.set(stepper.state.current.data.id, currentValues);
211
+ stepper.navigation.goTo(stepId);
212
+ onStepChange?.(stepId, "prev");
213
+ }
214
+ }, [
215
+ instance,
216
+ stepper,
217
+ currentIndex,
218
+ onStepChange
219
+ ]);
220
+ const getStepData = React$1.useCallback((stepId) => stepper.metadata.get(stepId), [stepper]);
221
+ const getAllStepData = React$1.useCallback(() => {
222
+ return steps.reduce((acc, step) => ({
223
+ ...acc,
224
+ ...stepper.metadata.get(step.id) || {}
225
+ }), {});
226
+ }, [steps, stepper]);
227
+ const stepperContextValue = React$1.useMemo(() => ({
228
+ steps,
229
+ current: currentStepConfig,
230
+ currentIndex,
231
+ next,
232
+ prev,
233
+ goTo,
234
+ isFirst: stepper.state.isFirst,
235
+ isLast: stepper.state.isLast,
236
+ getStepData,
237
+ getAllStepData,
238
+ utils: { getIndex: (stepId) => stepper.lookup.getIndex(stepId) }
239
+ }), [
240
+ steps,
241
+ currentStepConfig,
242
+ currentIndex,
243
+ stepper,
244
+ next,
245
+ prev,
246
+ goTo,
247
+ getStepData,
248
+ getAllStepData
249
+ ]);
250
+ const contextValue = React$1.useMemo(() => ({
251
+ form: instance,
252
+ fields: instance.fields,
253
+ isSubmitting,
254
+ isDirty: instance.formState.isDirty,
255
+ isValid: instance.formState.isValid,
256
+ isSubmitted: instance.formState.isSubmitted,
257
+ submitCount: instance.formState.submitCount,
258
+ dirtyFields: instance.formState.dirtyFields,
259
+ touchedFields: instance.formState.touchedFields,
260
+ submit: () => formRef.current?.requestSubmit(),
261
+ reset: () => instance.reset(),
262
+ formId: instance.id
263
+ }), [
264
+ instance,
265
+ isSubmitting,
266
+ instance.formState
267
+ ]);
268
+ const renderProps = {
269
+ steps,
270
+ current: currentStepConfig,
271
+ currentIndex,
272
+ next,
273
+ prev,
274
+ goTo,
275
+ isFirst: stepper.state.isFirst,
276
+ isLast: stepper.state.isLast,
277
+ getStepData,
278
+ getAllStepData
279
+ };
280
+ const resolvedChildren = typeof children === "function" ? children(renderProps) : children;
281
+ return /* @__PURE__ */ jsx(FormStepperContext, {
282
+ value: stepperContextValue,
283
+ children: /* @__PURE__ */ jsx(FormProvider, {
284
+ value: contextValue,
285
+ children: /* @__PURE__ */ jsx(adapter.FormProvider, {
286
+ instance,
287
+ children: /* @__PURE__ */ jsx(FormComp, {
288
+ ref: formRef,
289
+ ...instance.formProps,
290
+ className: cn("space-y-6", className),
291
+ autoComplete: "off",
292
+ noValidate: true,
293
+ onSubmit: (e) => {
294
+ e.stopPropagation();
295
+ const adapterSubmit = instance.formProps.onSubmit;
296
+ adapterSubmit?.(e);
297
+ },
298
+ children: resolvedChildren
299
+ })
300
+ })
301
+ })
302
+ });
303
+ }
304
+ //#endregion
305
+ //#region src/components/features/form/components/stepper/form-step.tsx
306
+ /**
307
+ * Form.Step - Individual step content container
308
+ *
309
+ * Only renders its children when the step is active.
310
+ * Works with the single-form architecture - fields remain registered
311
+ * even when unmounted, preserving their values.
312
+ *
313
+ * @example
314
+ * ```tsx
315
+ * <Form.Step id="account">
316
+ * <Form.Field name="email" label="Email" required>
317
+ * <Form.Input type="email" />
318
+ * </Form.Field>
319
+ * </Form.Step>
320
+ * ```
321
+ */
322
+ function FormStep({ id, children }) {
323
+ const { current } = useFormStepperContext();
324
+ if (current.id !== id) return null;
325
+ return /* @__PURE__ */ jsx(Fragment$1, { children });
326
+ }
327
+ FormStep.displayName = "Form.Step";
328
+ //#endregion
329
+ //#region src/components/features/form/components/stepper/stepper-controls.tsx
330
+ /**
331
+ * Form.StepperControls - Navigation buttons (Previous/Next/Submit)
332
+ *
333
+ * Provides Previous and Next/Submit buttons for navigating between steps.
334
+ * The Next button triggers form validation before advancing.
335
+ * The Previous button navigates back without validation.
336
+ *
337
+ * @example
338
+ * ```tsx
339
+ * <Form.StepperControls
340
+ * prevLabel={(isFirst) => isFirst ? 'Cancel' : 'Previous'}
341
+ * nextLabel={(isLast) => isLast ? 'Submit' : 'Next'}
342
+ * loadingText="Creating..."
343
+ * onCancel={() => setOpen(false)}
344
+ * />
345
+ * ```
346
+ *
347
+ * @example With external loading state
348
+ * ```tsx
349
+ * <Form.StepperControls
350
+ * loading={fetcher.state === 'submitting'}
351
+ * disabled={!isValid}
352
+ * loadingText="Saving..."
353
+ * />
354
+ * ```
355
+ */
356
+ function StepperControls({ prevLabel = "Previous", nextLabel = (isLast) => isLast ? "Submit" : "Next", loadingText = "Submitting...", showPrev = true, loading, disabled, onPrev, onCancel, className }) {
357
+ const { prev, isFirst, isLast } = useFormStepperContext();
358
+ const { isSubmitting: formIsSubmitting } = useFormContext();
359
+ const isLoading = loading ?? formIsSubmitting;
360
+ const isDisabled = disabled ?? false;
361
+ const getPrevLabel = () => {
362
+ if (typeof prevLabel === "function") return prevLabel(isFirst);
363
+ return prevLabel;
364
+ };
365
+ const getNextLabel = () => {
366
+ if (typeof nextLabel === "function") return nextLabel(isLast);
367
+ return nextLabel;
368
+ };
369
+ const handlePrev = () => {
370
+ if (isFirst && onCancel) onCancel();
371
+ else {
372
+ onPrev?.();
373
+ prev();
374
+ }
375
+ };
376
+ return /* @__PURE__ */ jsxs("div", {
377
+ className: cn("flex items-center justify-between gap-3", className),
378
+ children: [/* @__PURE__ */ jsx("div", { children: showPrev && /* @__PURE__ */ jsx(Button, {
379
+ htmlType: "button",
380
+ type: "quaternary",
381
+ theme: "outline",
382
+ size: "small",
383
+ onClick: handlePrev,
384
+ disabled: isLoading || isDisabled,
385
+ children: getPrevLabel()
386
+ }) }), /* @__PURE__ */ jsx(Button, {
387
+ htmlType: "submit",
388
+ type: "primary",
389
+ size: "small",
390
+ loading: isLoading,
391
+ disabled: isLoading || isDisabled,
392
+ children: isLoading && isLast ? loadingText : getNextLabel()
393
+ })]
394
+ });
395
+ }
396
+ StepperControls.displayName = "Form.StepperControls";
397
+ //#endregion
398
+ //#region src/components/features/form/components/stepper/stepper-navigation.tsx
399
+ /**
400
+ * Form.StepperNavigation - Step indicators/progress
401
+ *
402
+ * Displays visual step indicators showing current progress through the form.
403
+ * Supports horizontal and vertical variants with optional label orientation.
404
+ *
405
+ * @example
406
+ * ```tsx
407
+ * <Form.StepperNavigation variant="horizontal" labelOrientation="vertical" />
408
+ * ```
409
+ */
410
+ function StepperNavigation({ variant = "horizontal", labelOrientation = "vertical", className }) {
411
+ const { steps, currentIndex } = useFormStepperContext();
412
+ if (variant === "horizontal" && labelOrientation === "vertical") return /* @__PURE__ */ jsx("nav", {
413
+ "aria-label": "Form steps",
414
+ className: cn("flex flex-row items-start justify-between", className),
415
+ children: steps.map((step, index) => {
416
+ const isActive = index === currentIndex;
417
+ const isCompleted = index < currentIndex;
418
+ return /* @__PURE__ */ jsxs("div", {
419
+ className: "relative flex flex-1 flex-col items-center",
420
+ children: [
421
+ !(index === steps.length - 1) && /* @__PURE__ */ jsx("div", { className: "bg-stepper-line absolute top-4 right-[calc(-50%+20px)] left-[calc(50%+20px)] h-0.5" }),
422
+ /* @__PURE__ */ jsx("div", {
423
+ className: cn("relative z-10 flex h-8 w-8 items-center justify-center rounded-full border bg-transparent text-sm font-medium transition-colors", isActive && "border-primary bg-primary text-primary-foreground", isCompleted && "border-tertiary-foreground bg-tertiary-foreground text-tertiary", !isActive && !isCompleted && "border-stepper-label text-stepper-label"),
424
+ "aria-current": isActive ? "step" : void 0,
425
+ children: isCompleted ? /* @__PURE__ */ jsx(CheckIcon, { className: "text-tertiary h-4 w-4" }) : index + 1
426
+ }),
427
+ /* @__PURE__ */ jsxs("div", {
428
+ className: "mt-1",
429
+ children: [/* @__PURE__ */ jsx("span", {
430
+ className: cn("text-xs font-medium", isActive && "text-foreground", isCompleted && "text-stepper-label", !isActive && !isCompleted && "text-stepper-label"),
431
+ children: step.label
432
+ }), step.description && /* @__PURE__ */ jsx("p", {
433
+ className: "text-muted-foreground mt-0.5 text-xs",
434
+ children: step.description
435
+ })]
436
+ })
437
+ ]
438
+ }, step.id);
439
+ })
440
+ });
441
+ if (variant === "horizontal") return /* @__PURE__ */ jsx("nav", {
442
+ "aria-label": "Form steps",
443
+ className: cn("flex flex-row items-center", className),
444
+ children: steps.map((step, index) => {
445
+ const isActive = index === currentIndex;
446
+ const isCompleted = index < currentIndex;
447
+ const isLast = index === steps.length - 1;
448
+ return /* @__PURE__ */ jsxs(React$1.Fragment, { children: [/* @__PURE__ */ jsxs("div", {
449
+ className: "flex items-center",
450
+ children: [/* @__PURE__ */ jsx("div", {
451
+ className: cn("flex h-8 w-8 items-center justify-center rounded-full border text-sm font-medium transition-colors", isActive && "border-primary bg-primary text-primary-foreground", isCompleted && "border-tertiary-foreground bg-tertiary-foreground text-tertiary", !isActive && !isCompleted && "border-stepper-label text-stepper-label"),
452
+ "aria-current": isActive ? "step" : void 0,
453
+ children: isCompleted ? /* @__PURE__ */ jsx(CheckIcon, { className: "text-tertiary size-4" }) : index + 1
454
+ }), /* @__PURE__ */ jsx("div", {
455
+ className: "ml-2",
456
+ children: /* @__PURE__ */ jsx("span", {
457
+ className: cn("text-sm font-medium", isActive && "text-foreground", isCompleted && "text-stepper-label", !isActive && !isCompleted && "text-stepper-label"),
458
+ children: step.label
459
+ })
460
+ })]
461
+ }), !isLast && /* @__PURE__ */ jsx("div", { className: "bg-stepper-line mx-4 h-0.5 min-w-8 flex-1" })] }, step.id);
462
+ })
463
+ });
464
+ return /* @__PURE__ */ jsx("nav", {
465
+ "aria-label": "Form steps",
466
+ className: cn("flex flex-col", className),
467
+ children: steps.map((step, index) => {
468
+ const isActive = index === currentIndex;
469
+ const isCompleted = index < currentIndex;
470
+ const isLast = index === steps.length - 1;
471
+ return /* @__PURE__ */ jsxs("div", {
472
+ className: "flex flex-row",
473
+ children: [/* @__PURE__ */ jsxs("div", {
474
+ className: "flex flex-col items-center",
475
+ children: [/* @__PURE__ */ jsx("div", {
476
+ className: cn("flex h-8 w-8 items-center justify-center rounded-full border text-sm font-medium transition-colors", isActive && "border-primary bg-primary text-primary-foreground", isCompleted && "border-tertiary-foreground bg-tertiary-foreground text-tertiary", !isActive && !isCompleted && "border-stepper-label text-stepper-label"),
477
+ "aria-current": isActive ? "step" : void 0,
478
+ children: isCompleted ? /* @__PURE__ */ jsx(CheckIcon, { className: "text-tertiary size-4" }) : index + 1
479
+ }), !isLast && /* @__PURE__ */ jsx("div", { className: "bg-stepper-line my-1 min-h-8 w-0.5 flex-1" })]
480
+ }), /* @__PURE__ */ jsxs("div", {
481
+ className: "ml-3 pb-8",
482
+ children: [/* @__PURE__ */ jsx("span", {
483
+ className: cn("text-sm font-medium", isActive && "text-foreground", isCompleted && "text-stepper-label", !isActive && !isCompleted && "text-stepper-label"),
484
+ children: step.label
485
+ }), step.description && /* @__PURE__ */ jsx("p", {
486
+ className: "text-muted-foreground mt-0.5 text-xs",
487
+ children: step.description
488
+ })]
489
+ })]
490
+ }, step.id);
491
+ })
492
+ });
493
+ }
494
+ StepperNavigation.displayName = "Form.StepperNavigation";
495
+ //#endregion
496
+ //#region src/components/features/form/hooks/use-stepper.ts
497
+ /**
498
+ * Hook to access the stepper context
499
+ * Must be used within a Form.Stepper component
500
+ *
501
+ * @example
502
+ * ```tsx
503
+ * function StepContent() {
504
+ * const {
505
+ * current,
506
+ * currentIndex,
507
+ * steps,
508
+ * next,
509
+ * prev,
510
+ * goTo,
511
+ * isFirst,
512
+ * isLast,
513
+ * } = useStepper();
514
+ *
515
+ * return (
516
+ * <div>
517
+ * <h2>Step {currentIndex + 1}: {current.label}</h2>
518
+ * <button onClick={prev} disabled={isFirst}>Previous</button>
519
+ * <button onClick={next} disabled={isLast}>Next</button>
520
+ * </div>
521
+ * );
522
+ * }
523
+ * ```
524
+ */
525
+ function useStepper() {
526
+ const context = useFormStepperContext();
527
+ return {
528
+ steps: context.steps,
529
+ current: context.current,
530
+ currentIndex: context.currentIndex,
531
+ next: context.next,
532
+ prev: context.prev,
533
+ goTo: context.goTo,
534
+ isFirst: context.isFirst,
535
+ isLast: context.isLast,
536
+ getStepData: context.getStepData,
537
+ getAllStepData: context.getAllStepData
538
+ };
539
+ }
540
+ //#endregion
541
+ export { FormStep, FormStepper, StepperControls, StepperNavigation, useStepper };
@@ -0,0 +1,17 @@
1
+ import * as React$1 from "react";
2
+ import { jsx } from "react/jsx-runtime";
3
+ //#region src/components/features/form/context/form-context.tsx
4
+ const FormContext = React$1.createContext(null);
5
+ function FormProvider({ children, value }) {
6
+ return /* @__PURE__ */ jsx(FormContext, {
7
+ value,
8
+ children
9
+ });
10
+ }
11
+ function useFormContext() {
12
+ const context = React$1.use(FormContext);
13
+ if (!context) throw new Error("useFormContext must be used within a Form.Root component");
14
+ return context;
15
+ }
16
+ //#endregion
17
+ export { useFormContext as n, FormProvider as t };