@algodomain/smart-forms 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,906 @@
1
+ 'use strict';
2
+
3
+ var chunkY6NGPMDH_cjs = require('./chunk-Y6NGPMDH.cjs');
4
+ var chunkYV7RVYMD_cjs = require('./chunk-YV7RVYMD.cjs');
5
+ var React3 = require('react');
6
+ var reactToastify = require('react-toastify');
7
+ var jsxRuntime = require('react/jsx-runtime');
8
+ var lucideReact = require('lucide-react');
9
+ var zod = require('zod');
10
+ var TabsPrimitive = require('@radix-ui/react-tabs');
11
+
12
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
13
+
14
+ function _interopNamespace(e) {
15
+ if (e && e.__esModule) return e;
16
+ var n = Object.create(null);
17
+ if (e) {
18
+ Object.keys(e).forEach(function (k) {
19
+ if (k !== 'default') {
20
+ var d = Object.getOwnPropertyDescriptor(e, k);
21
+ Object.defineProperty(n, k, d.get ? d : {
22
+ enumerable: true,
23
+ get: function () { return e[k]; }
24
+ });
25
+ }
26
+ });
27
+ }
28
+ n.default = e;
29
+ return Object.freeze(n);
30
+ }
31
+
32
+ var React3__default = /*#__PURE__*/_interopDefault(React3);
33
+ var TabsPrimitive__namespace = /*#__PURE__*/_interopNamespace(TabsPrimitive);
34
+
35
+ var ToastContainerWrapper = () => /* @__PURE__ */ jsxRuntime.jsx(
36
+ reactToastify.ToastContainer,
37
+ {
38
+ position: "top-center",
39
+ autoClose: 2e3,
40
+ newestOnTop: false,
41
+ closeOnClick: true,
42
+ rtl: false,
43
+ pauseOnFocusLoss: true,
44
+ draggable: true,
45
+ pauseOnHover: true,
46
+ theme: "light",
47
+ transition: reactToastify.Bounce
48
+ }
49
+ );
50
+ var FormHeader = ({ title, subTitle, logo }) => {
51
+ if (!title && !subTitle && !logo) return null;
52
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-4 border-b border-gray-200 pb-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center space-x-4", children: [
53
+ logo && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0", children: logo }),
54
+ (title || subTitle) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: logo ? "text-left" : "text-center", children: [
55
+ title && /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-bold text-gray-800", children: title }),
56
+ subTitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm", children: subTitle })
57
+ ] })
58
+ ] }) });
59
+ };
60
+ var Section = ({ text }) => {
61
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative my-6", children: [
62
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full border-t border-gray-300" }) }),
63
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative flex justify-center text-sm", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bg-white px-2 text-gray-500", children: text }) })
64
+ ] });
65
+ };
66
+ var Footer = ({ children, className = "" }) => {
67
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `mt-4 pt-4 border-t border-gray-200 ${className}`, children });
68
+ };
69
+ var BaseSmartForm = ({
70
+ children,
71
+ api,
72
+ method = "POST",
73
+ submitButtonText = "Submit",
74
+ submitButtonIcon,
75
+ allowSaveDraft = false,
76
+ saveDraftApi,
77
+ enableLocalStorage = false,
78
+ storageKey,
79
+ logFormData = false,
80
+ onSuccess,
81
+ onError,
82
+ transformData,
83
+ className = "max-w-2xl mx-auto p-6 bg-white",
84
+ title,
85
+ subTitle,
86
+ logo,
87
+ footer,
88
+ initialData = {},
89
+ showReset = false,
90
+ showProgressBar = true,
91
+ showTabNumbers = true,
92
+ authentication
93
+ }) => {
94
+ const config = {
95
+ api,
96
+ method,
97
+ submitButtonText,
98
+ submitButtonIcon,
99
+ allowSaveDraft,
100
+ saveDraftApi,
101
+ enableLocalStorage,
102
+ storageKey,
103
+ logFormData,
104
+ onSuccess,
105
+ onError,
106
+ transformData,
107
+ className,
108
+ title,
109
+ subTitle,
110
+ logo,
111
+ footer,
112
+ showReset,
113
+ showProgressBar,
114
+ showTabNumbers,
115
+ authentication
116
+ };
117
+ return /* @__PURE__ */ jsxRuntime.jsx(chunkYV7RVYMD_cjs.SmartFormProvider, { config, initialData, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className, children: [
118
+ /* @__PURE__ */ jsxRuntime.jsx(ToastContainerWrapper, {}),
119
+ /* @__PURE__ */ jsxRuntime.jsx(FormHeader, { title, subTitle, logo }),
120
+ children,
121
+ footer && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 pt-4 border-t border-gray-200", children: footer })
122
+ ] }) });
123
+ };
124
+ var LoadingSpinner = ({ className = "h-4 w-4" }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: `animate-spin rounded-full border-b-2 border-current ${className}` });
125
+ var SubmitButton = ({ onClick, disabled, children, className }) => /* @__PURE__ */ jsxRuntime.jsx(chunkY6NGPMDH_cjs.Button, { onClick, disabled, className, children: disabled ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
126
+ /* @__PURE__ */ jsxRuntime.jsx(LoadingSpinner, {}),
127
+ "Submitting..."
128
+ ] }) : children });
129
+ var DraftSaveButton = ({ onClick, disabled }) => /* @__PURE__ */ jsxRuntime.jsx(chunkY6NGPMDH_cjs.Button, { onClick, disabled, variant: "secondary", children: disabled ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
130
+ /* @__PURE__ */ jsxRuntime.jsx(LoadingSpinner, {}),
131
+ "Saving..."
132
+ ] }) : "Save Draft" });
133
+ var ResetButton = ({ onClick }) => /* @__PURE__ */ jsxRuntime.jsx(chunkY6NGPMDH_cjs.Button, { onClick, variant: "secondary", children: "Reset Form" });
134
+ var NavigationButtons = ({
135
+ onPrevious,
136
+ onNext,
137
+ onSubmit,
138
+ onSaveDraft,
139
+ onReset,
140
+ isLoading,
141
+ isDraftSaving,
142
+ config,
143
+ isFirstTab = false,
144
+ isLastTab = false
145
+ }) => {
146
+ const hasResetButton = !!onReset;
147
+ const hasDraftButton = config.allowSaveDraft && !!onSaveDraft;
148
+ const hasSubmitOrNextButton = isLastTab || !!onNext;
149
+ const rightSectionButtons = [hasResetButton, hasDraftButton, hasSubmitOrNextButton].filter(Boolean).length;
150
+ const hasPreviousButton = !!onPrevious;
151
+ const isSingleButton = rightSectionButtons === 1 && !hasPreviousButton;
152
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center mt-8 pt-6 border-t border-gray-200", children: [
153
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-4", children: onPrevious && /* @__PURE__ */ jsxRuntime.jsxs(
154
+ chunkY6NGPMDH_cjs.Button,
155
+ {
156
+ onClick: onPrevious,
157
+ disabled: isFirstTab,
158
+ variant: "secondary",
159
+ children: [
160
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "h-4 w-4" }),
161
+ "Previous"
162
+ ]
163
+ }
164
+ ) }),
165
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: isSingleButton ? "w-full" : "flex gap-4", children: [
166
+ onReset && /* @__PURE__ */ jsxRuntime.jsx(ResetButton, { onClick: onReset }),
167
+ config.allowSaveDraft && onSaveDraft && /* @__PURE__ */ jsxRuntime.jsx(DraftSaveButton, { onClick: onSaveDraft, disabled: isDraftSaving }),
168
+ isLastTab ? /* @__PURE__ */ jsxRuntime.jsxs(
169
+ SubmitButton,
170
+ {
171
+ onClick: onSubmit,
172
+ disabled: isLoading,
173
+ className: isSingleButton ? "w-full" : "",
174
+ children: [
175
+ config.submitButtonIcon,
176
+ config.submitButtonText || "Submit"
177
+ ]
178
+ }
179
+ ) : onNext && /* @__PURE__ */ jsxRuntime.jsxs(
180
+ chunkY6NGPMDH_cjs.Button,
181
+ {
182
+ onClick: onNext,
183
+ className: isSingleButton ? "w-full" : "",
184
+ children: [
185
+ "Next",
186
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-4 w-4" })
187
+ ]
188
+ }
189
+ )
190
+ ] })
191
+ ] });
192
+ };
193
+ var SimpleFormButtons = ({
194
+ onSubmit,
195
+ onSaveDraft,
196
+ onReset,
197
+ isLoading,
198
+ isDraftSaving,
199
+ config
200
+ }) => {
201
+ const hasResetButton = !!onReset;
202
+ const hasDraftButton = config.allowSaveDraft && !!onSaveDraft;
203
+ const hasSubmitButton = true;
204
+ const totalButtons = [hasResetButton, hasDraftButton, hasSubmitButton].filter(Boolean).length;
205
+ const isSingleButton = totalButtons === 1;
206
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end mt-8 pt-6 border-t border-gray-200", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: isSingleButton ? "w-full" : "flex gap-4", children: [
207
+ onReset && /* @__PURE__ */ jsxRuntime.jsx(ResetButton, { onClick: onReset }),
208
+ config.allowSaveDraft && onSaveDraft && /* @__PURE__ */ jsxRuntime.jsx(DraftSaveButton, { onClick: onSaveDraft, disabled: isDraftSaving }),
209
+ /* @__PURE__ */ jsxRuntime.jsxs(
210
+ SubmitButton,
211
+ {
212
+ onClick: onSubmit,
213
+ disabled: isLoading,
214
+ className: isSingleButton ? "w-full" : "",
215
+ children: [
216
+ config.submitButtonIcon,
217
+ config.submitButtonText || "Submit"
218
+ ]
219
+ }
220
+ )
221
+ ] }) });
222
+ };
223
+ var SubmitButton2 = () => {
224
+ const { isLoading, isDraftSaving, submitForm, saveDraft, resetForm, config } = chunkYV7RVYMD_cjs.useSmartForm();
225
+ const shouldShowReset = config.showReset || config.enableLocalStorage;
226
+ return /* @__PURE__ */ jsxRuntime.jsx(
227
+ SimpleFormButtons,
228
+ {
229
+ onSubmit: submitForm,
230
+ onSaveDraft: config.allowSaveDraft ? saveDraft : void 0,
231
+ onReset: shouldShowReset ? resetForm : void 0,
232
+ isLoading,
233
+ isDraftSaving,
234
+ config
235
+ }
236
+ );
237
+ };
238
+ var SmartForm = (props) => {
239
+ const { children, ...otherProps } = props;
240
+ const childArray = React3__default.default.Children.toArray(children);
241
+ const footerChildren = [];
242
+ const regularChildren = [];
243
+ childArray.forEach((child) => {
244
+ if (React3.isValidElement(child) && child.type === Footer) {
245
+ footerChildren.push(child);
246
+ } else {
247
+ regularChildren.push(child);
248
+ }
249
+ });
250
+ return /* @__PURE__ */ jsxRuntime.jsxs(BaseSmartForm, { ...otherProps, children: [
251
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-6", children: regularChildren }),
252
+ /* @__PURE__ */ jsxRuntime.jsx(SubmitButton2, {}),
253
+ footerChildren.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: footerChildren })
254
+ ] });
255
+ };
256
+ function Tabs({
257
+ className,
258
+ ...props
259
+ }) {
260
+ return /* @__PURE__ */ jsxRuntime.jsx(
261
+ TabsPrimitive__namespace.Root,
262
+ {
263
+ "data-slot": "tabs",
264
+ className: chunkYV7RVYMD_cjs.cn("flex flex-col gap-2", className),
265
+ ...props
266
+ }
267
+ );
268
+ }
269
+ function TabsList({
270
+ className,
271
+ ...props
272
+ }) {
273
+ return /* @__PURE__ */ jsxRuntime.jsx(
274
+ TabsPrimitive__namespace.List,
275
+ {
276
+ "data-slot": "tabs-list",
277
+ className: chunkYV7RVYMD_cjs.cn(
278
+ "bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]",
279
+ className
280
+ ),
281
+ ...props
282
+ }
283
+ );
284
+ }
285
+ function TabsTrigger({
286
+ className,
287
+ ...props
288
+ }) {
289
+ return /* @__PURE__ */ jsxRuntime.jsx(
290
+ TabsPrimitive__namespace.Trigger,
291
+ {
292
+ "data-slot": "tabs-trigger",
293
+ className: chunkYV7RVYMD_cjs.cn(
294
+ "data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
295
+ className
296
+ ),
297
+ ...props
298
+ }
299
+ );
300
+ }
301
+ function TabsContent({
302
+ className,
303
+ ...props
304
+ }) {
305
+ return /* @__PURE__ */ jsxRuntime.jsx(
306
+ TabsPrimitive__namespace.Content,
307
+ {
308
+ "data-slot": "tabs-content",
309
+ className: chunkYV7RVYMD_cjs.cn("flex-1 outline-none", className),
310
+ ...props
311
+ }
312
+ );
313
+ }
314
+ var TabIndexContext = React3__default.default.createContext(null);
315
+ var useTabIndex = () => {
316
+ const context = React3.useContext(TabIndexContext);
317
+ return context;
318
+ };
319
+ var TabIndexProvider = ({
320
+ children,
321
+ tabIndex
322
+ }) => {
323
+ return /* @__PURE__ */ jsxRuntime.jsx(TabIndexContext.Provider, { value: { tabIndex }, children });
324
+ };
325
+ var ExternalFormContext = React3.createContext(null);
326
+ var Tab = ({ children }) => {
327
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
328
+ };
329
+ var NavigationButtonsWrapper = ({
330
+ activeTab,
331
+ totalTabs,
332
+ onPrevious,
333
+ onNext,
334
+ onSubmit,
335
+ onSaveDraft,
336
+ onReset,
337
+ isLoading,
338
+ isDraftSaving,
339
+ config
340
+ }) => {
341
+ const isLastTab = activeTab === totalTabs - 1;
342
+ const isFirstTab = activeTab === 0;
343
+ return /* @__PURE__ */ jsxRuntime.jsx(
344
+ NavigationButtons,
345
+ {
346
+ onPrevious,
347
+ onNext,
348
+ onSubmit,
349
+ onSaveDraft,
350
+ onReset,
351
+ isLoading,
352
+ isDraftSaving,
353
+ config,
354
+ isFirstTab,
355
+ isLastTab
356
+ }
357
+ );
358
+ };
359
+ var MultiTabSmartForm = ({
360
+ children,
361
+ showProgressBar = true,
362
+ showTabNumbers = true,
363
+ ...baseProps
364
+ }) => {
365
+ const [activeTab, setActiveTab] = React3.useState(0);
366
+ const [tabFields, setTabFields] = React3.useState([]);
367
+ const [completedTabs, setCompletedTabs] = React3.useState(/* @__PURE__ */ new Set());
368
+ const [validationErrorTabs, setValidationErrorTabs] = React3.useState(/* @__PURE__ */ new Set());
369
+ const [externalTabFields, setExternalTabFields] = React3.useState({});
370
+ const { footerChildren, regularChildren } = React3.useMemo(() => {
371
+ const childArray = React3__default.default.Children.toArray(children);
372
+ const footerChildren2 = [];
373
+ const regularChildren2 = [];
374
+ childArray.forEach((child) => {
375
+ if (React3.isValidElement(child) && child.type === Footer) {
376
+ footerChildren2.push(child);
377
+ } else {
378
+ regularChildren2.push(child);
379
+ }
380
+ });
381
+ return { footerChildren: footerChildren2, regularChildren: regularChildren2 };
382
+ }, [children]);
383
+ const tabs = [];
384
+ React3__default.default.Children.forEach(regularChildren, (child) => {
385
+ if (React3__default.default.isValidElement(child) && child.type === Tab) {
386
+ const props = child.props;
387
+ if (props.title) {
388
+ tabs.push(props.title);
389
+ }
390
+ }
391
+ });
392
+ const findSmartInputs = React3.useCallback((children2) => {
393
+ const fields = [];
394
+ React3__default.default.Children.forEach(children2, (child) => {
395
+ if (React3__default.default.isValidElement(child)) {
396
+ const fieldProps = child.props;
397
+ if (fieldProps && typeof fieldProps === "object" && "field" in fieldProps && fieldProps.field) {
398
+ fields.push(fieldProps.field);
399
+ } else if (child.props && typeof child.props === "object" && "children" in child.props) {
400
+ const nestedFields = findSmartInputs(child.props.children);
401
+ fields.push(...nestedFields);
402
+ }
403
+ }
404
+ });
405
+ return fields;
406
+ }, []);
407
+ const registerTabFields = React3.useCallback((tabIndex, fields) => {
408
+ setExternalTabFields((prev) => ({
409
+ ...prev,
410
+ [tabIndex]: fields
411
+ }));
412
+ }, []);
413
+ React3__default.default.useEffect(() => {
414
+ const fields = [];
415
+ React3__default.default.Children.forEach(regularChildren, (child) => {
416
+ if (React3__default.default.isValidElement(child) && child.type === Tab) {
417
+ const tabFieldsInTab = findSmartInputs(child.props.children);
418
+ fields.push(tabFieldsInTab);
419
+ }
420
+ });
421
+ setTabFields(fields);
422
+ }, [regularChildren, findSmartInputs]);
423
+ const getCombinedTabFields = React3.useCallback((tabIndex) => {
424
+ const internalFields = tabFields[tabIndex] || [];
425
+ const externalFields = externalTabFields[tabIndex] || [];
426
+ return [...internalFields, ...externalFields];
427
+ }, [tabFields, externalTabFields]);
428
+ const config = {
429
+ ...baseProps,
430
+ showProgressBar,
431
+ showTabNumbers
432
+ };
433
+ const handleTabChange = React3.useCallback((index) => {
434
+ setActiveTab(index);
435
+ }, []);
436
+ const handlePrevious = React3.useCallback(() => {
437
+ if (activeTab > 0) {
438
+ setActiveTab(activeTab - 1);
439
+ }
440
+ }, [activeTab]);
441
+ const handleNext = React3.useCallback(() => {
442
+ if (activeTab < tabs.length - 1) {
443
+ setActiveTab(activeTab + 1);
444
+ }
445
+ }, [activeTab, tabs.length]);
446
+ const externalFormContextValue = React3.useMemo(() => ({
447
+ registerTabFields,
448
+ currentTabIndex: activeTab
449
+ }), [registerTabFields, activeTab]);
450
+ return /* @__PURE__ */ jsxRuntime.jsx(BaseSmartForm, { ...baseProps, children: /* @__PURE__ */ jsxRuntime.jsx(ExternalFormContext.Provider, { value: externalFormContextValue, children: /* @__PURE__ */ jsxRuntime.jsx(
451
+ MultiTabFormContent,
452
+ {
453
+ activeTab,
454
+ tabs,
455
+ getCombinedTabFields,
456
+ config,
457
+ onTabChange: handleTabChange,
458
+ onPrevious: handlePrevious,
459
+ onNext: handleNext,
460
+ completedTabs,
461
+ setCompletedTabs,
462
+ validationErrorTabs,
463
+ setValidationErrorTabs,
464
+ footerChildren,
465
+ children: regularChildren
466
+ }
467
+ ) }) });
468
+ };
469
+ var MultiTabFormContent = ({
470
+ activeTab,
471
+ tabs,
472
+ getCombinedTabFields,
473
+ config,
474
+ onTabChange,
475
+ onPrevious,
476
+ onNext,
477
+ completedTabs,
478
+ setCompletedTabs,
479
+ validationErrorTabs,
480
+ setValidationErrorTabs,
481
+ footerChildren,
482
+ children
483
+ }) => {
484
+ const { isLoading, isDraftSaving, submitForm, saveDraft, resetForm, validateFields, formData, validationRegistry, setErrors } = chunkYV7RVYMD_cjs.useSmartForm();
485
+ const [maxContentHeight, setMaxContentHeight] = React3.useState(0);
486
+ const contentRefs = React3.useRef([]);
487
+ const debounce = (func, wait) => {
488
+ let timeout;
489
+ return function executedFunction(...args) {
490
+ const later = () => {
491
+ clearTimeout(timeout);
492
+ func(...args);
493
+ };
494
+ clearTimeout(timeout);
495
+ timeout = setTimeout(later, wait);
496
+ };
497
+ };
498
+ React3__default.default.useEffect(() => {
499
+ const calculateMaxHeight = () => {
500
+ let maxHeight = 0;
501
+ contentRefs.current.forEach((ref) => {
502
+ if (ref) {
503
+ const height = ref.scrollHeight;
504
+ maxHeight = Math.max(maxHeight, height);
505
+ }
506
+ });
507
+ if (maxHeight > 0) {
508
+ setMaxContentHeight(maxHeight);
509
+ }
510
+ };
511
+ const timeoutId = setTimeout(calculateMaxHeight, 100);
512
+ window.addEventListener("resize", calculateMaxHeight);
513
+ return () => {
514
+ clearTimeout(timeoutId);
515
+ window.removeEventListener("resize", calculateMaxHeight);
516
+ };
517
+ }, [children, activeTab]);
518
+ React3__default.default.useEffect(() => {
519
+ const checkTabCompletion = debounce(() => {
520
+ tabs.forEach((_, tabIndex) => {
521
+ const tabFieldsInTab = getCombinedTabFields(tabIndex);
522
+ if (tabFieldsInTab.length === 0) {
523
+ setCompletedTabs((prev) => {
524
+ const newSet = new Set(prev);
525
+ newSet.delete(tabIndex);
526
+ return newSet;
527
+ });
528
+ return;
529
+ }
530
+ let allFieldsValid = true;
531
+ let hasAnyValue = false;
532
+ for (const field of tabFieldsInTab) {
533
+ const validation = validationRegistry[field];
534
+ const fieldValue = formData[field];
535
+ if (fieldValue !== void 0 && fieldValue !== null && fieldValue !== "") {
536
+ hasAnyValue = true;
537
+ }
538
+ if (validation) {
539
+ try {
540
+ validation.parse(fieldValue);
541
+ } catch (error) {
542
+ if (error instanceof zod.z.ZodError) {
543
+ allFieldsValid = false;
544
+ break;
545
+ }
546
+ }
547
+ }
548
+ }
549
+ if (hasAnyValue && allFieldsValid) {
550
+ setCompletedTabs((prev) => /* @__PURE__ */ new Set([...prev, tabIndex]));
551
+ setValidationErrorTabs((prev) => {
552
+ const newSet = new Set(prev);
553
+ newSet.delete(tabIndex);
554
+ return newSet;
555
+ });
556
+ } else {
557
+ setCompletedTabs((prev) => {
558
+ const newSet = new Set(prev);
559
+ newSet.delete(tabIndex);
560
+ return newSet;
561
+ });
562
+ }
563
+ });
564
+ }, 300);
565
+ checkTabCompletion();
566
+ }, [formData, validationRegistry, getCombinedTabFields, tabs, setCompletedTabs, setValidationErrorTabs]);
567
+ const handleTabChangeWithErrorCheck = React3.useCallback((index) => {
568
+ onTabChange(index);
569
+ }, [onTabChange]);
570
+ const handleNextWithValidation = React3.useCallback(() => {
571
+ const currentTabFields = getCombinedTabFields(activeTab);
572
+ if (currentTabFields.length > 0) {
573
+ const allErrors = {};
574
+ let isValid = true;
575
+ for (const field of currentTabFields) {
576
+ const validation = validationRegistry[field];
577
+ if (validation) {
578
+ try {
579
+ validation.parse(formData[field]);
580
+ } catch (error) {
581
+ if (error instanceof zod.z.ZodError) {
582
+ allErrors[field] = error.issues[0]?.message || `Invalid ${field}`;
583
+ isValid = false;
584
+ }
585
+ }
586
+ }
587
+ }
588
+ if (isValid) {
589
+ setCompletedTabs((prev) => /* @__PURE__ */ new Set([...prev, activeTab]));
590
+ setValidationErrorTabs((prev) => {
591
+ const newSet = new Set(prev);
592
+ newSet.delete(activeTab);
593
+ return newSet;
594
+ });
595
+ onNext();
596
+ } else {
597
+ setCompletedTabs((prev) => {
598
+ const newSet = new Set(prev);
599
+ newSet.delete(activeTab);
600
+ return newSet;
601
+ });
602
+ setValidationErrorTabs((prev) => /* @__PURE__ */ new Set([...prev, activeTab]));
603
+ validateFields(currentTabFields);
604
+ const firstErrorField = currentTabFields.find((field) => allErrors[field]);
605
+ if (firstErrorField) {
606
+ setTimeout(() => {
607
+ const element = document.querySelector(`[data-field="${firstErrorField}"]`);
608
+ if (element) {
609
+ element.scrollIntoView({ behavior: "smooth", block: "center" });
610
+ element.focus();
611
+ }
612
+ }, 50);
613
+ }
614
+ }
615
+ } else {
616
+ onNext();
617
+ }
618
+ }, [activeTab, getCombinedTabFields, validateFields, onNext, setCompletedTabs, setValidationErrorTabs, validationRegistry, formData]);
619
+ const handleSubmitWithValidation = React3.useCallback(async () => {
620
+ const allErrors = {};
621
+ let isValid = true;
622
+ let firstErrorTabIndex = -1;
623
+ for (const [field, validation] of Object.entries(validationRegistry)) {
624
+ if (validation && typeof validation.parse === "function") {
625
+ try {
626
+ validation.parse(formData[field]);
627
+ } catch (error) {
628
+ if (error instanceof zod.z.ZodError) {
629
+ allErrors[field] = error.issues[0]?.message || `Invalid ${field}`;
630
+ isValid = false;
631
+ }
632
+ }
633
+ }
634
+ }
635
+ setErrors(allErrors);
636
+ if (isValid) {
637
+ await submitForm();
638
+ } else {
639
+ setValidationErrorTabs(/* @__PURE__ */ new Set());
640
+ for (let tabIndex = 0; tabIndex < tabs.length; tabIndex++) {
641
+ const tabFieldsInTab = getCombinedTabFields(tabIndex);
642
+ const hasErrorsInTab = tabFieldsInTab.some((field) => allErrors[field]);
643
+ if (hasErrorsInTab) {
644
+ setValidationErrorTabs((prev) => /* @__PURE__ */ new Set([...prev, tabIndex]));
645
+ if (firstErrorTabIndex === -1) {
646
+ firstErrorTabIndex = tabIndex;
647
+ }
648
+ }
649
+ }
650
+ if (firstErrorTabIndex !== -1) {
651
+ onTabChange(firstErrorTabIndex);
652
+ setTimeout(() => {
653
+ const tabFieldsInTab = getCombinedTabFields(firstErrorTabIndex);
654
+ const firstErrorField = tabFieldsInTab.find((field) => allErrors[field]);
655
+ if (firstErrorField) {
656
+ const element = document.querySelector(`[data-field="${firstErrorField}"]`);
657
+ if (element) {
658
+ element.scrollIntoView({ behavior: "smooth", block: "center" });
659
+ element.focus();
660
+ }
661
+ }
662
+ }, 100);
663
+ }
664
+ }
665
+ }, [submitForm, getCombinedTabFields, onTabChange, tabs.length, validationRegistry, formData, setErrors, setValidationErrorTabs]);
666
+ const activeTabValue = tabs[activeTab] || tabs[0] || "";
667
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
668
+ /* @__PURE__ */ jsxRuntime.jsxs(Tabs, { value: activeTabValue, onValueChange: (value) => {
669
+ const newIndex = tabs.indexOf(value);
670
+ if (newIndex !== -1) {
671
+ handleTabChangeWithErrorCheck(newIndex);
672
+ }
673
+ }, className: "w-full", children: [
674
+ /* @__PURE__ */ jsxRuntime.jsx(TabsList, { className: `grid w-full mb-8`, style: { gridTemplateColumns: `repeat(${tabs.length}, 1fr)` }, children: tabs.map((tab, index) => /* @__PURE__ */ jsxRuntime.jsxs(TabsTrigger, { value: tab, className: "flex items-center gap-2", children: [
675
+ config.showTabNumbers && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
676
+ index + 1,
677
+ "."
678
+ ] }),
679
+ tab,
680
+ completedTabs.has(index) && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-4 w-4 text-green-600" }),
681
+ validationErrorTabs.has(index) && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-4 w-4 text-red-600" })
682
+ ] }, tab)) }),
683
+ React3__default.default.Children.map(children, (child, index) => {
684
+ if (React3__default.default.isValidElement(child) && child.type === Tab) {
685
+ const tabProps = child.props;
686
+ return /* @__PURE__ */ jsxRuntime.jsx(TabsContent, { value: tabProps.title, children: /* @__PURE__ */ jsxRuntime.jsx(TabIndexProvider, { tabIndex: index, children: /* @__PURE__ */ jsxRuntime.jsx(
687
+ "div",
688
+ {
689
+ ref: (el) => {
690
+ contentRefs.current[index] = el;
691
+ },
692
+ className: "space-y-6",
693
+ style: { minHeight: maxContentHeight > 0 ? `${maxContentHeight}px` : void 0 },
694
+ children: tabProps.children
695
+ }
696
+ ) }) }, index);
697
+ }
698
+ return null;
699
+ }),
700
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", left: "-9999px", top: "-9999px", visibility: "hidden" }, children: React3__default.default.Children.map(children, (child, index) => {
701
+ if (React3__default.default.isValidElement(child) && child.type === Tab) {
702
+ const tabProps = child.props;
703
+ return /* @__PURE__ */ jsxRuntime.jsx(TabIndexProvider, { tabIndex: index, children: /* @__PURE__ */ jsxRuntime.jsx(
704
+ "div",
705
+ {
706
+ ref: (el) => {
707
+ contentRefs.current[index] = el;
708
+ },
709
+ className: "space-y-6",
710
+ children: tabProps.children
711
+ }
712
+ ) }, `hidden-${index}`);
713
+ }
714
+ return null;
715
+ }) })
716
+ ] }),
717
+ /* @__PURE__ */ jsxRuntime.jsx(
718
+ NavigationButtonsWrapper,
719
+ {
720
+ activeTab,
721
+ totalTabs: tabs.length,
722
+ onPrevious,
723
+ onNext: handleNextWithValidation,
724
+ onSubmit: handleSubmitWithValidation,
725
+ onSaveDraft: config.allowSaveDraft ? saveDraft : void 0,
726
+ onReset: config.showReset || config.enableLocalStorage ? resetForm : void 0,
727
+ isLoading,
728
+ isDraftSaving,
729
+ config
730
+ }
731
+ ),
732
+ footerChildren.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: footerChildren }),
733
+ config.showProgressBar && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6", children: [
734
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between text-sm text-muted-foreground mb-2", children: [
735
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Progress" }),
736
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
737
+ Math.round((activeTab + 1) / tabs.length * 100),
738
+ "%"
739
+ ] })
740
+ ] }),
741
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full bg-muted rounded-full h-2", children: /* @__PURE__ */ jsxRuntime.jsx(
742
+ "div",
743
+ {
744
+ className: "bg-primary h-2 rounded-full transition-all duration-300",
745
+ style: { width: `${(activeTab + 1) / tabs.length * 100}%` }
746
+ }
747
+ ) })
748
+ ] })
749
+ ] });
750
+ };
751
+
752
+ // src/hooks/useFormWrapper.ts
753
+ var useFormWrapper = () => {
754
+ return chunkYV7RVYMD_cjs.useSmartForm();
755
+ };
756
+ var useExternalFormRegistration = () => {
757
+ const context = React3.useContext(ExternalFormContext);
758
+ if (!context) {
759
+ throw new Error("useExternalFormRegistration must be used within a MultiTabSmartForm");
760
+ }
761
+ return context;
762
+ };
763
+ var useExternalFormFields = (fields, tabIndex) => {
764
+ const { registerTabFields } = useExternalFormRegistration();
765
+ const stableFields = React3.useMemo(() => fields, [fields.join(",")]);
766
+ React3.useEffect(() => {
767
+ if (stableFields.length > 0) {
768
+ registerTabFields(tabIndex, stableFields);
769
+ }
770
+ }, [stableFields, tabIndex, registerTabFields]);
771
+ };
772
+ var ExternalFieldProvider = ({
773
+ children,
774
+ registerField
775
+ }) => {
776
+ return /* @__PURE__ */ jsxRuntime.jsx(chunkYV7RVYMD_cjs.FieldDetectionContext.Provider, { value: { registerField }, children });
777
+ };
778
+
779
+ // src/useAutoDetectFields.tsx
780
+ var useAutoDetectFields = (tabIndex) => {
781
+ const { registerTabFields } = useExternalFormRegistration();
782
+ const hasRegistered = React3.useRef(false);
783
+ const detectedFields = React3.useRef(/* @__PURE__ */ new Set());
784
+ const registerField = React3.useCallback((fieldName) => {
785
+ detectedFields.current.add(fieldName);
786
+ if (!hasRegistered.current) {
787
+ const fieldsArray = Array.from(detectedFields.current);
788
+ registerTabFields(tabIndex, fieldsArray);
789
+ hasRegistered.current = true;
790
+ }
791
+ }, [tabIndex, registerTabFields]);
792
+ return { registerField, ExternalFieldProvider };
793
+ };
794
+ var useExternalTab = () => {
795
+ const { registerTabFields } = useExternalFormRegistration();
796
+ const tabIndexContext = useTabIndex();
797
+ const detectedFields = React3.useRef(/* @__PURE__ */ new Set());
798
+ const registrationTimeout = React3.useRef(null);
799
+ const hasRegistered = React3.useRef(false);
800
+ const tabIndex = tabIndexContext?.tabIndex ?? 0;
801
+ const registerField = React3.useCallback((fieldName) => {
802
+ detectedFields.current.add(fieldName);
803
+ if (registrationTimeout.current) {
804
+ clearTimeout(registrationTimeout.current);
805
+ }
806
+ registrationTimeout.current = setTimeout(() => {
807
+ if (!hasRegistered.current) {
808
+ const fieldsArray = Array.from(detectedFields.current);
809
+ if (fieldsArray.length > 0) {
810
+ registerTabFields(tabIndex, fieldsArray);
811
+ hasRegistered.current = true;
812
+ }
813
+ }
814
+ }, 200);
815
+ }, [tabIndex, registerTabFields]);
816
+ React3.useEffect(() => {
817
+ const forceRegistrationTimeout = setTimeout(() => {
818
+ if (!hasRegistered.current) {
819
+ const fieldsArray = Array.from(detectedFields.current);
820
+ if (fieldsArray.length > 0) {
821
+ registerTabFields(tabIndex, fieldsArray);
822
+ hasRegistered.current = true;
823
+ }
824
+ }
825
+ }, 1e3);
826
+ return () => {
827
+ clearTimeout(forceRegistrationTimeout);
828
+ if (registrationTimeout.current) {
829
+ clearTimeout(registrationTimeout.current);
830
+ }
831
+ };
832
+ }, [tabIndex, registerTabFields]);
833
+ return { registerField, ExternalFieldProvider };
834
+ };
835
+ var FormFieldGroup = ({
836
+ children,
837
+ className = ""
838
+ }) => {
839
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex flex-wrap gap-2 md:gap-4 mb-4 ${className}`, children });
840
+ };
841
+
842
+ Object.defineProperty(exports, "SmartCheckbox", {
843
+ enumerable: true,
844
+ get: function () { return chunkY6NGPMDH_cjs.SmartCheckbox; }
845
+ });
846
+ Object.defineProperty(exports, "SmartDatePicker", {
847
+ enumerable: true,
848
+ get: function () { return chunkY6NGPMDH_cjs.SmartDatePicker; }
849
+ });
850
+ Object.defineProperty(exports, "SmartRadioGroup", {
851
+ enumerable: true,
852
+ get: function () { return chunkY6NGPMDH_cjs.SmartRadioGroup; }
853
+ });
854
+ Object.defineProperty(exports, "SmartSelect", {
855
+ enumerable: true,
856
+ get: function () { return chunkY6NGPMDH_cjs.SmartSelect; }
857
+ });
858
+ Object.defineProperty(exports, "SmartTags", {
859
+ enumerable: true,
860
+ get: function () { return chunkY6NGPMDH_cjs.SmartTags; }
861
+ });
862
+ Object.defineProperty(exports, "SmartFormProvider", {
863
+ enumerable: true,
864
+ get: function () { return chunkYV7RVYMD_cjs.SmartFormProvider; }
865
+ });
866
+ Object.defineProperty(exports, "SmartInput", {
867
+ enumerable: true,
868
+ get: function () { return chunkYV7RVYMD_cjs.SmartInput; }
869
+ });
870
+ Object.defineProperty(exports, "useFieldDetection", {
871
+ enumerable: true,
872
+ get: function () { return chunkYV7RVYMD_cjs.useFieldDetection; }
873
+ });
874
+ Object.defineProperty(exports, "useFormField", {
875
+ enumerable: true,
876
+ get: function () { return chunkYV7RVYMD_cjs.useFormField; }
877
+ });
878
+ Object.defineProperty(exports, "useSmartForm", {
879
+ enumerable: true,
880
+ get: function () { return chunkYV7RVYMD_cjs.useSmartForm; }
881
+ });
882
+ exports.BaseSmartForm = BaseSmartForm;
883
+ exports.DraftSaveButton = DraftSaveButton;
884
+ exports.ExternalFieldProvider = ExternalFieldProvider;
885
+ exports.Footer = Footer;
886
+ exports.FormFieldGroup = FormFieldGroup;
887
+ exports.FormHeader = FormHeader;
888
+ exports.LoadingSpinner = LoadingSpinner;
889
+ exports.MultiTabSmartForm = MultiTabSmartForm;
890
+ exports.NavigationButtons = NavigationButtons;
891
+ exports.ResetButton = ResetButton;
892
+ exports.Section = Section;
893
+ exports.SimpleFormButtons = SimpleFormButtons;
894
+ exports.SmartForm = SmartForm;
895
+ exports.SubmitButton = SubmitButton;
896
+ exports.Tab = Tab;
897
+ exports.TabIndexProvider = TabIndexProvider;
898
+ exports.ToastContainerWrapper = ToastContainerWrapper;
899
+ exports.useAutoDetectFields = useAutoDetectFields;
900
+ exports.useExternalFormFields = useExternalFormFields;
901
+ exports.useExternalFormRegistration = useExternalFormRegistration;
902
+ exports.useExternalTab = useExternalTab;
903
+ exports.useFormWrapper = useFormWrapper;
904
+ exports.useTabIndex = useTabIndex;
905
+ //# sourceMappingURL=index.cjs.map
906
+ //# sourceMappingURL=index.cjs.map