@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/README.md +313 -0
- package/dist/SmartFormProvider-B-BTl4wO.d.cts +94 -0
- package/dist/SmartFormProvider-B-BTl4wO.d.ts +94 -0
- package/dist/SmartTags-HmvmCJPT.d.cts +111 -0
- package/dist/SmartTags-HmvmCJPT.d.ts +111 -0
- package/dist/chunk-6WAEAWTD.js +949 -0
- package/dist/chunk-6WAEAWTD.js.map +1 -0
- package/dist/chunk-IG4XDQMV.js +604 -0
- package/dist/chunk-IG4XDQMV.js.map +1 -0
- package/dist/chunk-Y6NGPMDH.cjs +982 -0
- package/dist/chunk-Y6NGPMDH.cjs.map +1 -0
- package/dist/chunk-YV7RVYMD.cjs +639 -0
- package/dist/chunk-YV7RVYMD.cjs.map +1 -0
- package/dist/fields.cjs +1445 -0
- package/dist/fields.cjs.map +1 -0
- package/dist/fields.d.cts +119 -0
- package/dist/fields.d.ts +119 -0
- package/dist/fields.js +1384 -0
- package/dist/fields.js.map +1 -0
- package/dist/index.cjs +906 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +165 -0
- package/dist/index.d.ts +165 -0
- package/dist/index.js +821 -0
- package/dist/index.js.map +1 -0
- package/dist/opinionated.cjs +408 -0
- package/dist/opinionated.cjs.map +1 -0
- package/dist/opinionated.d.cts +274 -0
- package/dist/opinionated.d.ts +274 -0
- package/dist/opinionated.js +382 -0
- package/dist/opinionated.js.map +1 -0
- package/dist/style.css +114 -0
- package/package.json +90 -0
|
@@ -0,0 +1,604 @@
|
|
|
1
|
+
import { createContext, useState, useRef, useCallback, useContext, useEffect } from 'react';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
4
|
+
import { clsx } from 'clsx';
|
|
5
|
+
import { twMerge } from 'tailwind-merge';
|
|
6
|
+
import * as LabelPrimitive from '@radix-ui/react-label';
|
|
7
|
+
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
8
|
+
import { InfoIcon, EyeOffIcon, EyeIcon } from 'lucide-react';
|
|
9
|
+
|
|
10
|
+
var FormContext = createContext(null);
|
|
11
|
+
var SmartFormProvider = ({
|
|
12
|
+
children,
|
|
13
|
+
config,
|
|
14
|
+
initialData = {}
|
|
15
|
+
}) => {
|
|
16
|
+
const getStorageKey = () => {
|
|
17
|
+
return config.storageKey || "smart-form-data";
|
|
18
|
+
};
|
|
19
|
+
const loadFromStorage = () => {
|
|
20
|
+
if (!config.enableLocalStorage) return initialData;
|
|
21
|
+
try {
|
|
22
|
+
const stored = localStorage.getItem(getStorageKey());
|
|
23
|
+
if (stored) {
|
|
24
|
+
const parsedStored = JSON.parse(stored);
|
|
25
|
+
const merged = { ...initialData };
|
|
26
|
+
Object.keys(parsedStored).forEach((key) => {
|
|
27
|
+
const storedValue = parsedStored[key];
|
|
28
|
+
if (storedValue !== null && storedValue !== void 0 && storedValue !== "") {
|
|
29
|
+
merged[key] = storedValue;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
return merged;
|
|
33
|
+
}
|
|
34
|
+
return initialData;
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.warn("Failed to load form data from localStorage:", error);
|
|
37
|
+
return initialData;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const saveToStorage = (data) => {
|
|
41
|
+
if (!config.enableLocalStorage) return;
|
|
42
|
+
try {
|
|
43
|
+
localStorage.setItem(getStorageKey(), JSON.stringify(data));
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.warn("Failed to save form data to localStorage:", error);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
const clearStorage = () => {
|
|
49
|
+
if (!config.enableLocalStorage) return;
|
|
50
|
+
try {
|
|
51
|
+
localStorage.removeItem(getStorageKey());
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.warn("Failed to clear form data from localStorage:", error);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
const [formData, setFormData] = useState(loadFromStorage);
|
|
57
|
+
const [errors, setErrors] = useState({});
|
|
58
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
59
|
+
const [isDraftSaving, setIsDraftSaving] = useState(false);
|
|
60
|
+
const [validationRegistry, setValidationRegistry] = useState({});
|
|
61
|
+
const fieldRefs = useRef({});
|
|
62
|
+
const submitHooksRef = useRef(/* @__PURE__ */ new Map());
|
|
63
|
+
const updateField = useCallback((field, value) => {
|
|
64
|
+
setFormData((prev) => {
|
|
65
|
+
const newData = {
|
|
66
|
+
...prev,
|
|
67
|
+
[field]: value
|
|
68
|
+
};
|
|
69
|
+
setTimeout(() => saveToStorage(newData), 0);
|
|
70
|
+
return newData;
|
|
71
|
+
});
|
|
72
|
+
if (errors[field]) {
|
|
73
|
+
setErrors((prev) => {
|
|
74
|
+
const newErrors = { ...prev };
|
|
75
|
+
delete newErrors[field];
|
|
76
|
+
return newErrors;
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}, [errors, config.enableLocalStorage, saveToStorage]);
|
|
80
|
+
const validateField = useCallback((field, value) => {
|
|
81
|
+
const validation = validationRegistry[field];
|
|
82
|
+
if (!validation) return true;
|
|
83
|
+
try {
|
|
84
|
+
validation.parse(value);
|
|
85
|
+
return true;
|
|
86
|
+
} catch (error) {
|
|
87
|
+
if (error instanceof z.ZodError) {
|
|
88
|
+
setErrors((prev) => ({
|
|
89
|
+
...prev,
|
|
90
|
+
[field]: error.issues[0]?.message || `Invalid ${field}`
|
|
91
|
+
}));
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
}, [validationRegistry]);
|
|
97
|
+
const registerValidation = useCallback((field, validation) => {
|
|
98
|
+
setValidationRegistry((prev) => ({
|
|
99
|
+
...prev,
|
|
100
|
+
[field]: validation
|
|
101
|
+
}));
|
|
102
|
+
}, []);
|
|
103
|
+
const validateAllFields = useCallback(() => {
|
|
104
|
+
const allErrors = {};
|
|
105
|
+
let isValid = true;
|
|
106
|
+
for (const [field, validation] of Object.entries(validationRegistry)) {
|
|
107
|
+
if (validation && typeof validation.parse === "function") {
|
|
108
|
+
try {
|
|
109
|
+
validation.parse(formData[field]);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
if (error instanceof z.ZodError) {
|
|
112
|
+
allErrors[field] = error.issues[0]?.message || `Invalid ${field}`;
|
|
113
|
+
isValid = false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
setErrors(allErrors);
|
|
119
|
+
return isValid;
|
|
120
|
+
}, [formData, validationRegistry]);
|
|
121
|
+
const validateFields = useCallback((fields) => {
|
|
122
|
+
const allErrors = {};
|
|
123
|
+
let isValid = true;
|
|
124
|
+
for (const field of fields) {
|
|
125
|
+
const validation = validationRegistry[field];
|
|
126
|
+
if (validation) {
|
|
127
|
+
try {
|
|
128
|
+
validation.parse(formData[field]);
|
|
129
|
+
} catch (error) {
|
|
130
|
+
if (error instanceof z.ZodError) {
|
|
131
|
+
allErrors[field] = error.issues[0]?.message || `Invalid ${field}`;
|
|
132
|
+
isValid = false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
setErrors((prev) => ({
|
|
138
|
+
...prev,
|
|
139
|
+
...allErrors
|
|
140
|
+
}));
|
|
141
|
+
return isValid;
|
|
142
|
+
}, [formData, validationRegistry]);
|
|
143
|
+
const submitForm = useCallback(async () => {
|
|
144
|
+
if (!validateAllFields()) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (!config.api) {
|
|
148
|
+
console.warn("No API endpoint provided");
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (config.logFormData) {
|
|
152
|
+
console.log("\u{1F4E4} Submitting form data:", formData);
|
|
153
|
+
console.log("\u{1F3AF} API endpoint:", config.api);
|
|
154
|
+
}
|
|
155
|
+
const toJsonSafe = (val) => {
|
|
156
|
+
if (val instanceof File) return val.name;
|
|
157
|
+
if (val instanceof Date) return val.toISOString();
|
|
158
|
+
if (Array.isArray(val)) {
|
|
159
|
+
return val.map((item) => item instanceof File ? item.name : toJsonSafe(item));
|
|
160
|
+
}
|
|
161
|
+
if (val && typeof val === "object") {
|
|
162
|
+
const out = {};
|
|
163
|
+
for (const [k, v] of Object.entries(val)) out[k] = toJsonSafe(v);
|
|
164
|
+
return out;
|
|
165
|
+
}
|
|
166
|
+
return val;
|
|
167
|
+
};
|
|
168
|
+
let jsonPayload = toJsonSafe(formData);
|
|
169
|
+
if (config.transformData) {
|
|
170
|
+
jsonPayload = config.transformData(jsonPayload);
|
|
171
|
+
}
|
|
172
|
+
setIsLoading(true);
|
|
173
|
+
try {
|
|
174
|
+
const headers = {
|
|
175
|
+
"Content-Type": "application/json"
|
|
176
|
+
};
|
|
177
|
+
if (config.authentication?.enable) {
|
|
178
|
+
const { accessTokenKey = "accessToken" } = config.authentication;
|
|
179
|
+
const token = localStorage.getItem(accessTokenKey);
|
|
180
|
+
if (token) {
|
|
181
|
+
headers.Authorization = `Bearer ${token}`;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const fetchOptions = {
|
|
185
|
+
method: config.method || "POST",
|
|
186
|
+
headers,
|
|
187
|
+
body: JSON.stringify(jsonPayload)
|
|
188
|
+
};
|
|
189
|
+
if (config.authentication?.enable) {
|
|
190
|
+
fetchOptions.credentials = "include";
|
|
191
|
+
}
|
|
192
|
+
let response = await fetch(config.api, fetchOptions);
|
|
193
|
+
if (response.status === 403 && config.authentication?.enable && config.authentication.refreshTokenEndpoint) {
|
|
194
|
+
const newAccessToken = await refreshAccessToken(config.authentication);
|
|
195
|
+
if (newAccessToken) {
|
|
196
|
+
headers.Authorization = `Bearer ${newAccessToken}`;
|
|
197
|
+
response = await fetch(config.api, {
|
|
198
|
+
...fetchOptions,
|
|
199
|
+
headers
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (!response.ok) {
|
|
204
|
+
const errorResponse = await response.json().catch(() => ({ message: "Request failed" }));
|
|
205
|
+
console.log("Error response:", errorResponse);
|
|
206
|
+
throw new Error(JSON.stringify(errorResponse));
|
|
207
|
+
}
|
|
208
|
+
const result = await response.json();
|
|
209
|
+
if (config.onSuccess) {
|
|
210
|
+
config.onSuccess(result);
|
|
211
|
+
}
|
|
212
|
+
for (const hook of submitHooksRef.current.values()) {
|
|
213
|
+
try {
|
|
214
|
+
await hook();
|
|
215
|
+
} catch (hookError) {
|
|
216
|
+
console.error("Submit hook error:", hookError);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
} catch (error) {
|
|
220
|
+
if (config.onError) {
|
|
221
|
+
let errorData;
|
|
222
|
+
try {
|
|
223
|
+
errorData = JSON.parse(error.message);
|
|
224
|
+
} catch {
|
|
225
|
+
let userFriendlyMessage = "An error occurred";
|
|
226
|
+
if (error.message === "Failed to fetch" || error.message?.includes("Failed to fetch")) {
|
|
227
|
+
userFriendlyMessage = "This service is temporarily unavailable. Please try again after some time.";
|
|
228
|
+
} else if (error.message?.includes("Network Error")) {
|
|
229
|
+
userFriendlyMessage = "Network error occurred. Please check your connection and try again.";
|
|
230
|
+
} else if (error.message?.includes("timeout")) {
|
|
231
|
+
userFriendlyMessage = "Request timed out. Please try again.";
|
|
232
|
+
} else if (error.message?.includes("CORS")) {
|
|
233
|
+
userFriendlyMessage = "Connection error. Please contact support if this persists.";
|
|
234
|
+
} else if (error.message && error.message !== "An error occurred") {
|
|
235
|
+
userFriendlyMessage = error.message;
|
|
236
|
+
}
|
|
237
|
+
errorData = {
|
|
238
|
+
message: userFriendlyMessage,
|
|
239
|
+
details: []
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
config.onError(errorData);
|
|
243
|
+
}
|
|
244
|
+
console.error("Form submission error:", error);
|
|
245
|
+
} finally {
|
|
246
|
+
setIsLoading(false);
|
|
247
|
+
}
|
|
248
|
+
}, [formData, config, validateAllFields]);
|
|
249
|
+
const validateFormAndGetResult = useCallback(() => {
|
|
250
|
+
return validateAllFields();
|
|
251
|
+
}, [validateAllFields]);
|
|
252
|
+
const refreshAccessToken = useCallback(async (authConfig) => {
|
|
253
|
+
const { refreshTokenEndpoint, accessTokenKey = "accessToken", refreshTokenKey = "refreshToken" } = authConfig;
|
|
254
|
+
if (!refreshTokenEndpoint) {
|
|
255
|
+
throw new Error("Refresh token endpoint not provided");
|
|
256
|
+
}
|
|
257
|
+
try {
|
|
258
|
+
const refreshToken = localStorage.getItem(refreshTokenKey);
|
|
259
|
+
if (!refreshToken) {
|
|
260
|
+
throw new Error("No refresh token available");
|
|
261
|
+
}
|
|
262
|
+
const response = await fetch(refreshTokenEndpoint, {
|
|
263
|
+
method: "POST",
|
|
264
|
+
headers: {
|
|
265
|
+
"Content-Type": "application/json"
|
|
266
|
+
},
|
|
267
|
+
body: JSON.stringify({ refreshToken })
|
|
268
|
+
});
|
|
269
|
+
if (!response.ok) {
|
|
270
|
+
throw new Error(`Token refresh failed: ${response.status}`);
|
|
271
|
+
}
|
|
272
|
+
const result = await response.json();
|
|
273
|
+
const newAccessToken = result.data?.accessToken || result.accessToken;
|
|
274
|
+
if (newAccessToken) {
|
|
275
|
+
localStorage.setItem(accessTokenKey, newAccessToken);
|
|
276
|
+
return newAccessToken;
|
|
277
|
+
}
|
|
278
|
+
throw new Error("No access token in refresh response");
|
|
279
|
+
} catch (error) {
|
|
280
|
+
console.error("Token refresh failed:", error);
|
|
281
|
+
localStorage.removeItem(accessTokenKey);
|
|
282
|
+
localStorage.removeItem(refreshTokenKey);
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
}, []);
|
|
286
|
+
const saveDraft = useCallback(async () => {
|
|
287
|
+
if (!config.saveDraftApi) {
|
|
288
|
+
console.warn("No save draft API endpoint provided");
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
if (config.logFormData) {
|
|
292
|
+
console.log("\u{1F4BE} Saving draft data:", formData);
|
|
293
|
+
console.log("\u{1F3AF} Draft API endpoint:", config.saveDraftApi);
|
|
294
|
+
}
|
|
295
|
+
setIsDraftSaving(true);
|
|
296
|
+
try {
|
|
297
|
+
const headers = {
|
|
298
|
+
"Content-Type": "application/json"
|
|
299
|
+
};
|
|
300
|
+
if (config.authentication?.enable) {
|
|
301
|
+
const { accessTokenKey = "accessToken" } = config.authentication;
|
|
302
|
+
const token = localStorage.getItem(accessTokenKey);
|
|
303
|
+
if (token) {
|
|
304
|
+
headers.Authorization = `Bearer ${token}`;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
const fetchOptions = {
|
|
308
|
+
method: "POST",
|
|
309
|
+
headers,
|
|
310
|
+
body: JSON.stringify(formData)
|
|
311
|
+
};
|
|
312
|
+
if (config.authentication?.enable) {
|
|
313
|
+
fetchOptions.credentials = "include";
|
|
314
|
+
}
|
|
315
|
+
let response = await fetch(config.saveDraftApi, fetchOptions);
|
|
316
|
+
if (response.status === 403 && config.authentication?.enable && config.authentication.refreshTokenEndpoint) {
|
|
317
|
+
const newAccessToken = await refreshAccessToken(config.authentication);
|
|
318
|
+
if (newAccessToken) {
|
|
319
|
+
headers.Authorization = `Bearer ${newAccessToken}`;
|
|
320
|
+
response = await fetch(config.saveDraftApi, {
|
|
321
|
+
...fetchOptions,
|
|
322
|
+
headers
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
if (!response.ok) {
|
|
327
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
328
|
+
}
|
|
329
|
+
const result = await response.json();
|
|
330
|
+
console.log("Draft saved successfully:", result);
|
|
331
|
+
} catch (error) {
|
|
332
|
+
console.error("Draft save error:", error);
|
|
333
|
+
} finally {
|
|
334
|
+
setIsDraftSaving(false);
|
|
335
|
+
}
|
|
336
|
+
}, [formData, config]);
|
|
337
|
+
const resetForm = useCallback(() => {
|
|
338
|
+
setFormData(initialData);
|
|
339
|
+
setErrors({});
|
|
340
|
+
clearStorage();
|
|
341
|
+
}, [initialData, config.enableLocalStorage, clearStorage]);
|
|
342
|
+
const contextValue = {
|
|
343
|
+
formData,
|
|
344
|
+
errors,
|
|
345
|
+
isLoading,
|
|
346
|
+
isDraftSaving,
|
|
347
|
+
updateField,
|
|
348
|
+
validateField,
|
|
349
|
+
validateFields,
|
|
350
|
+
submitForm,
|
|
351
|
+
validateFormAndGetResult,
|
|
352
|
+
saveDraft,
|
|
353
|
+
resetForm,
|
|
354
|
+
fieldRefs,
|
|
355
|
+
registerValidation,
|
|
356
|
+
validationRegistry,
|
|
357
|
+
config,
|
|
358
|
+
setErrors,
|
|
359
|
+
registerSubmitHook: (key, hook) => {
|
|
360
|
+
submitHooksRef.current.set(key, hook);
|
|
361
|
+
},
|
|
362
|
+
unregisterSubmitHook: (key) => {
|
|
363
|
+
submitHooksRef.current.delete(key);
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
return /* @__PURE__ */ jsx(FormContext.Provider, { value: contextValue, children });
|
|
367
|
+
};
|
|
368
|
+
var useSmartForm = () => {
|
|
369
|
+
const context = useContext(FormContext);
|
|
370
|
+
if (!context) {
|
|
371
|
+
throw new Error("useSmartForm must be used within a SmartFormProvider");
|
|
372
|
+
}
|
|
373
|
+
return context;
|
|
374
|
+
};
|
|
375
|
+
var useFormField = (field) => {
|
|
376
|
+
const { formData, errors, updateField, fieldRefs, registerValidation } = useSmartForm();
|
|
377
|
+
return {
|
|
378
|
+
value: formData[field],
|
|
379
|
+
error: errors[field],
|
|
380
|
+
onChange: (value) => updateField(field, value),
|
|
381
|
+
fieldRef: (el) => fieldRefs.current[field] = el,
|
|
382
|
+
registerValidation
|
|
383
|
+
};
|
|
384
|
+
};
|
|
385
|
+
var FieldDetectionContext = createContext(null);
|
|
386
|
+
var useFieldDetection = () => {
|
|
387
|
+
const context = useContext(FieldDetectionContext);
|
|
388
|
+
return context;
|
|
389
|
+
};
|
|
390
|
+
function cn(...inputs) {
|
|
391
|
+
return twMerge(clsx(inputs));
|
|
392
|
+
}
|
|
393
|
+
function Input({ className, type, ...props }) {
|
|
394
|
+
return /* @__PURE__ */ jsx(
|
|
395
|
+
"input",
|
|
396
|
+
{
|
|
397
|
+
type,
|
|
398
|
+
"data-slot": "input",
|
|
399
|
+
className: cn(
|
|
400
|
+
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
401
|
+
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
402
|
+
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
403
|
+
className
|
|
404
|
+
),
|
|
405
|
+
...props
|
|
406
|
+
}
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
function Textarea({ className, ...props }) {
|
|
410
|
+
return /* @__PURE__ */ jsx(
|
|
411
|
+
"textarea",
|
|
412
|
+
{
|
|
413
|
+
"data-slot": "textarea",
|
|
414
|
+
className: cn(
|
|
415
|
+
"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
416
|
+
className
|
|
417
|
+
),
|
|
418
|
+
...props
|
|
419
|
+
}
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
function Label({
|
|
423
|
+
className,
|
|
424
|
+
...props
|
|
425
|
+
}) {
|
|
426
|
+
return /* @__PURE__ */ jsx(
|
|
427
|
+
LabelPrimitive.Root,
|
|
428
|
+
{
|
|
429
|
+
"data-slot": "label",
|
|
430
|
+
className: cn(
|
|
431
|
+
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
|
432
|
+
className
|
|
433
|
+
),
|
|
434
|
+
...props
|
|
435
|
+
}
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
function TooltipProvider({
|
|
439
|
+
delayDuration = 0,
|
|
440
|
+
...props
|
|
441
|
+
}) {
|
|
442
|
+
return /* @__PURE__ */ jsx(
|
|
443
|
+
TooltipPrimitive.Provider,
|
|
444
|
+
{
|
|
445
|
+
"data-slot": "tooltip-provider",
|
|
446
|
+
delayDuration,
|
|
447
|
+
...props
|
|
448
|
+
}
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
function Tooltip({
|
|
452
|
+
...props
|
|
453
|
+
}) {
|
|
454
|
+
return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsx(TooltipPrimitive.Root, { "data-slot": "tooltip", ...props }) });
|
|
455
|
+
}
|
|
456
|
+
function TooltipTrigger({
|
|
457
|
+
...props
|
|
458
|
+
}) {
|
|
459
|
+
return /* @__PURE__ */ jsx(TooltipPrimitive.Trigger, { "data-slot": "tooltip-trigger", ...props });
|
|
460
|
+
}
|
|
461
|
+
function TooltipContent({
|
|
462
|
+
className,
|
|
463
|
+
sideOffset = 0,
|
|
464
|
+
children,
|
|
465
|
+
...props
|
|
466
|
+
}) {
|
|
467
|
+
return /* @__PURE__ */ jsx(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
|
|
468
|
+
TooltipPrimitive.Content,
|
|
469
|
+
{
|
|
470
|
+
"data-slot": "tooltip-content",
|
|
471
|
+
sideOffset,
|
|
472
|
+
className: cn(
|
|
473
|
+
"bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
|
|
474
|
+
className
|
|
475
|
+
),
|
|
476
|
+
...props,
|
|
477
|
+
children: [
|
|
478
|
+
children,
|
|
479
|
+
/* @__PURE__ */ jsx(TooltipPrimitive.Arrow, { className: "bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" })
|
|
480
|
+
]
|
|
481
|
+
}
|
|
482
|
+
) });
|
|
483
|
+
}
|
|
484
|
+
var SmartInput = ({
|
|
485
|
+
field,
|
|
486
|
+
label,
|
|
487
|
+
type = "text",
|
|
488
|
+
placeholder,
|
|
489
|
+
validation,
|
|
490
|
+
className = "",
|
|
491
|
+
required = false,
|
|
492
|
+
defaultValue,
|
|
493
|
+
info,
|
|
494
|
+
subLabel
|
|
495
|
+
}) => {
|
|
496
|
+
const { value, error, onChange, fieldRef, registerValidation } = useFormField(field);
|
|
497
|
+
const fieldDetection = useFieldDetection();
|
|
498
|
+
const hasRegistered = useRef(false);
|
|
499
|
+
const hasSetDefault = useRef(false);
|
|
500
|
+
const [showPassword, setShowPassword] = useState(false);
|
|
501
|
+
const togglePasswordVisibility = () => {
|
|
502
|
+
setShowPassword(!showPassword);
|
|
503
|
+
};
|
|
504
|
+
useEffect(() => {
|
|
505
|
+
if (validation && !hasRegistered.current) {
|
|
506
|
+
hasRegistered.current = true;
|
|
507
|
+
registerValidation(field, validation);
|
|
508
|
+
}
|
|
509
|
+
}, [validation, field, registerValidation]);
|
|
510
|
+
useEffect(() => {
|
|
511
|
+
if (fieldDetection?.registerField) {
|
|
512
|
+
fieldDetection.registerField(field);
|
|
513
|
+
}
|
|
514
|
+
}, [field, fieldDetection]);
|
|
515
|
+
useEffect(() => {
|
|
516
|
+
if (defaultValue !== void 0 && !hasSetDefault.current && (value === void 0 || value === null || value === "")) {
|
|
517
|
+
onChange(defaultValue);
|
|
518
|
+
hasSetDefault.current = true;
|
|
519
|
+
}
|
|
520
|
+
}, [defaultValue, value, onChange]);
|
|
521
|
+
const getPlaceholder = () => {
|
|
522
|
+
if (placeholder) return placeholder;
|
|
523
|
+
if (!label) return `Enter ${field}`;
|
|
524
|
+
if (type === "email") return `Enter ${label.toLowerCase()}`;
|
|
525
|
+
if (type === "tel") return `Enter ${label.toLowerCase()}`;
|
|
526
|
+
return `Enter ${label.toLowerCase()}`;
|
|
527
|
+
};
|
|
528
|
+
const renderInput = () => {
|
|
529
|
+
switch (type) {
|
|
530
|
+
case "textarea":
|
|
531
|
+
return /* @__PURE__ */ jsx(
|
|
532
|
+
Textarea,
|
|
533
|
+
{
|
|
534
|
+
ref: fieldRef,
|
|
535
|
+
value: value || "",
|
|
536
|
+
onChange: (e) => onChange(e.target.value),
|
|
537
|
+
className: `w-full ${error ? "border-destructive" : ""} ${className}`,
|
|
538
|
+
placeholder: getPlaceholder(),
|
|
539
|
+
rows: 4,
|
|
540
|
+
"data-field": field
|
|
541
|
+
}
|
|
542
|
+
);
|
|
543
|
+
case "password":
|
|
544
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
545
|
+
/* @__PURE__ */ jsx(
|
|
546
|
+
Input,
|
|
547
|
+
{
|
|
548
|
+
ref: fieldRef,
|
|
549
|
+
type: showPassword ? "text" : "password",
|
|
550
|
+
value: value || "",
|
|
551
|
+
onChange: (e) => onChange(e.target.value),
|
|
552
|
+
className: `w-full pr-10 ${error ? "border-destructive" : ""} ${className}`,
|
|
553
|
+
placeholder: getPlaceholder(),
|
|
554
|
+
"data-field": field
|
|
555
|
+
}
|
|
556
|
+
),
|
|
557
|
+
/* @__PURE__ */ jsx(
|
|
558
|
+
"button",
|
|
559
|
+
{
|
|
560
|
+
type: "button",
|
|
561
|
+
onClick: togglePasswordVisibility,
|
|
562
|
+
className: "absolute right-3 top-1/2 -translate-y-1/2 p-1 hover:bg-gray-100 rounded transition-colors",
|
|
563
|
+
children: showPassword ? /* @__PURE__ */ jsx(EyeOffIcon, { className: "h-4 w-4 text-muted-foreground" }) : /* @__PURE__ */ jsx(EyeIcon, { className: "h-4 w-4 text-muted-foreground" })
|
|
564
|
+
}
|
|
565
|
+
)
|
|
566
|
+
] });
|
|
567
|
+
default:
|
|
568
|
+
return /* @__PURE__ */ jsx(
|
|
569
|
+
Input,
|
|
570
|
+
{
|
|
571
|
+
ref: fieldRef,
|
|
572
|
+
type,
|
|
573
|
+
value: value || "",
|
|
574
|
+
onChange: (e) => onChange(e.target.value),
|
|
575
|
+
className: `w-full ${error ? "border-destructive" : ""} ${className}`,
|
|
576
|
+
placeholder: getPlaceholder(),
|
|
577
|
+
"data-field": field
|
|
578
|
+
}
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
return /* @__PURE__ */ jsxs("div", { className: `flex-1 min-w-0 ${className}`, children: [
|
|
583
|
+
label && /* @__PURE__ */ jsxs("div", { className: "mb-1", children: [
|
|
584
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
585
|
+
/* @__PURE__ */ jsxs(Label, { className: "text-sm font-medium text-foreground", children: [
|
|
586
|
+
label,
|
|
587
|
+
" ",
|
|
588
|
+
required && /* @__PURE__ */ jsx("span", { className: "text-destructive", children: "*" })
|
|
589
|
+
] }),
|
|
590
|
+
info && /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
591
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(InfoIcon, { className: "h-4 w-4 text-muted-foreground cursor-pointer mr-2" }) }),
|
|
592
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { className: "max-w-xs", children: info }) })
|
|
593
|
+
] }) })
|
|
594
|
+
] }),
|
|
595
|
+
subLabel && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-1", children: subLabel })
|
|
596
|
+
] }),
|
|
597
|
+
renderInput(),
|
|
598
|
+
error && /* @__PURE__ */ jsx("p", { className: "text-destructive text-sm mt-1", children: error })
|
|
599
|
+
] });
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
export { FieldDetectionContext, Input, Label, SmartFormProvider, SmartInput, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, cn, useFieldDetection, useFormField, useSmartForm };
|
|
603
|
+
//# sourceMappingURL=chunk-IG4XDQMV.js.map
|
|
604
|
+
//# sourceMappingURL=chunk-IG4XDQMV.js.map
|