@rxdrag/website-lib-react 0.0.3 → 0.0.6

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 (97) hide show
  1. package/dist/ReactModalTrigger-9207e763.js +26 -0
  2. package/dist/ReactModalTrigger-9207e763.js.map +1 -0
  3. package/dist/components/RichTextOutline/parseOutline.d.ts +5 -0
  4. package/dist/components/all.d.ts +0 -21
  5. package/dist/components/index.d.ts +0 -5
  6. package/dist/forms.d.ts +1 -0
  7. package/dist/forms.mjs +1647 -0
  8. package/dist/forms.mjs.map +1 -0
  9. package/dist/index.mjs +9 -3918
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/jsx-runtime-c02cc059.js +325 -0
  12. package/dist/jsx-runtime-c02cc059.js.map +1 -0
  13. package/dist/media.d.ts +1 -0
  14. package/dist/media.mjs +613 -0
  15. package/dist/media.mjs.map +1 -0
  16. package/dist/richtext.d.ts +1 -0
  17. package/dist/richtext.mjs +191 -0
  18. package/dist/richtext.mjs.map +1 -0
  19. package/dist/ui.d.ts +10 -0
  20. package/dist/ui.mjs +687 -0
  21. package/dist/ui.mjs.map +1 -0
  22. package/dist/video.d.ts +2 -0
  23. package/dist/video.mjs +426 -0
  24. package/dist/video.mjs.map +1 -0
  25. package/forms.ts +1 -0
  26. package/index.ts +1 -0
  27. package/media.ts +1 -0
  28. package/package.json +41 -6
  29. package/richtext.ts +1 -0
  30. package/src/components/Analytics/eventHandlers.ts +173 -0
  31. package/src/components/Analytics/index.tsx +21 -0
  32. package/src/components/Analytics/singleton.ts +214 -0
  33. package/src/components/Analytics/tracking.ts +221 -0
  34. package/src/components/Analytics/types.ts +60 -0
  35. package/src/components/Analytics/utils.ts +95 -0
  36. package/src/components/AttachmentIcon/index.tsx +53 -0
  37. package/src/components/BackgroundHlsVideoPlayer.tsx +97 -0
  38. package/src/components/BackgroundVideoPlayer.tsx +32 -0
  39. package/src/components/Bulletin.tsx +30 -0
  40. package/src/components/ContactForm/ContactForm.tsx +290 -0
  41. package/src/components/ContactForm/FileUpload2.tsx +423 -0
  42. package/src/components/ContactForm/Input.tsx +48 -0
  43. package/src/components/ContactForm/Input2.tsx +59 -0
  44. package/src/components/ContactForm/Submit.tsx +48 -0
  45. package/src/components/ContactForm/TelInput.tsx +215 -0
  46. package/src/components/ContactForm/TelInput2.tsx +213 -0
  47. package/src/components/ContactForm/Textarea.tsx +48 -0
  48. package/src/components/ContactForm/Textarea2.tsx +89 -0
  49. package/src/components/ContactForm/countryDialCodes.ts +243 -0
  50. package/src/components/ContactForm/factory.tsx +60 -0
  51. package/src/components/ContactForm/funcs.ts +64 -0
  52. package/src/components/ContactForm/hooks/useInlineLabelPadding.ts +43 -0
  53. package/src/components/ContactForm/hooks/useTelControl.ts +81 -0
  54. package/src/components/ContactForm/index.ts +7 -0
  55. package/src/components/ContactForm/types.ts +68 -0
  56. package/src/components/Icon/index.tsx +20 -0
  57. package/src/components/Medias/MainMedia.tsx +257 -0
  58. package/src/components/Medias/Thumbnail.tsx +62 -0
  59. package/src/components/Medias/VideoPlayer.tsx +114 -0
  60. package/src/components/Medias/index.tsx +271 -0
  61. package/src/components/ProductCard/ProductCard.tsx +24 -0
  62. package/src/components/ProductCard/ProductCta/index.tsx +28 -0
  63. package/src/components/ProductCard/ProductCta/style.css +4 -0
  64. package/src/components/ProductCard/ProductDescription/index.tsx +13 -0
  65. package/src/components/ProductCard/ProductDescription/style.css +6 -0
  66. package/src/components/ProductCard/ProductMedia/index.tsx +35 -0
  67. package/src/components/ProductCard/ProductMedia/style.css +6 -0
  68. package/src/components/ProductCard/ProductTitle/index.tsx +7 -0
  69. package/src/components/ProductCard/ProductTitle/style.css +4 -0
  70. package/src/components/ProductCard/ProductView.tsx +36 -0
  71. package/src/components/ProductCard/index.ts +5 -0
  72. package/src/components/ProductCard/useQueryProduct.ts +32 -0
  73. package/src/components/ReactModalTrigger.tsx +28 -0
  74. package/src/components/ReactVideoPlayer.tsx +332 -0
  75. package/src/components/RichTextOutline/index.tsx +74 -0
  76. package/src/components/RichTextOutline/parseOutline.ts +63 -0
  77. package/src/components/RichTextOutline/useAcitviedHeading.ts +142 -0
  78. package/src/components/RichTextOutline/useAnchorScroll.ts +24 -0
  79. package/src/components/Scroller.tsx +39 -0
  80. package/src/components/SearchInput.tsx +21 -0
  81. package/src/components/Share/index.tsx +86 -0
  82. package/src/components/Share/socials.tsx +80 -0
  83. package/src/components/Share//350/265/204/346/226/231.md +7 -0
  84. package/src/components/ToTop.tsx +72 -0
  85. package/src/components/VideoPlayIcon.tsx +43 -0
  86. package/src/components/all.ts +25 -0
  87. package/src/components/index.ts +12 -0
  88. package/src/forms.ts +1 -0
  89. package/src/index.ts +1 -0
  90. package/src/media.ts +1 -0
  91. package/src/richtext.ts +1 -0
  92. package/src/types/view-model.ts +37 -0
  93. package/src/ui.ts +10 -0
  94. package/src/video.ts +2 -0
  95. package/ui.ts +1 -0
  96. package/video.ts +1 -0
  97. package/dist/style.css +0 -17
package/dist/forms.mjs ADDED
@@ -0,0 +1,1647 @@
1
+ import { j as jsxRuntimeExports } from "./jsx-runtime-c02cc059.js";
2
+ import { useRef, useState, useEffect, forwardRef, useMemo } from "react";
3
+ import clsx from "clsx";
4
+ const Submit = (props) => {
5
+ const {
6
+ title = "Send Message",
7
+ className,
8
+ spinner,
9
+ submitting,
10
+ disabled,
11
+ onClick,
12
+ rawHtml,
13
+ needClasses,
14
+ ...rest
15
+ } = props;
16
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
17
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
18
+ "button",
19
+ {
20
+ type: "button",
21
+ className,
22
+ disabled: disabled ?? submitting,
23
+ onClick,
24
+ ...rest,
25
+ children: [
26
+ submitting && spinner,
27
+ rawHtml ? /* @__PURE__ */ jsxRuntimeExports.jsx(
28
+ "div",
29
+ {
30
+ style: { display: "contents" },
31
+ dangerouslySetInnerHTML: { __html: rawHtml }
32
+ }
33
+ ) : title
34
+ ]
35
+ }
36
+ ),
37
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "fixed -z-10 top-0 left-0 w-0 h-0 overflow-hidden opacity-0 pointer-events-none", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: needClasses }) })
38
+ ] });
39
+ };
40
+ const encrypt = (value, formSalt) => {
41
+ const timestamp = Math.floor(Date.now() / (60 * 1e3));
42
+ const dataToEncrypt = `${formSalt}:${timestamp}:${value}`;
43
+ let hash = 0;
44
+ for (let i = 0; i < dataToEncrypt.length; i++) {
45
+ const char = dataToEncrypt.charCodeAt(i);
46
+ hash = (hash << 5) - hash + char;
47
+ hash = hash & hash;
48
+ }
49
+ return `${hash.toString(16)}_${timestamp}`;
50
+ };
51
+ const verifyEncryption = (formSalt, encryptedValue = "", originalValue = "", maxAgeMinutes = 30) => {
52
+ const parts = encryptedValue.split("_");
53
+ if (parts.length !== 2) {
54
+ return false;
55
+ }
56
+ const [hashString, timestampString] = parts;
57
+ const timestamp = parseInt(timestampString, 10);
58
+ const currentTimestamp = Math.floor(Date.now() / (60 * 1e3));
59
+ if (isNaN(timestamp) || currentTimestamp - timestamp > maxAgeMinutes) {
60
+ return false;
61
+ }
62
+ const dataToEncrypt = `${formSalt}:${timestamp}:${originalValue}`;
63
+ let hash = 0;
64
+ for (let i = 0; i < dataToEncrypt.length; i++) {
65
+ const char = dataToEncrypt.charCodeAt(i);
66
+ hash = (hash << 5) - hash + char;
67
+ hash = hash & hash;
68
+ }
69
+ return hashString === hash.toString(16);
70
+ };
71
+ function useInlineLabelPadding(extraSpace = 16) {
72
+ const labelRef = useRef(null);
73
+ const [paddingLeft, setPaddingLeft] = useState("3.5rem");
74
+ useEffect(() => {
75
+ const labelEl = labelRef.current;
76
+ if (!labelEl)
77
+ return;
78
+ let rafId = 0;
79
+ const measure = () => {
80
+ const width = labelEl.getBoundingClientRect().width;
81
+ setPaddingLeft(`${width + extraSpace}px`);
82
+ };
83
+ measure();
84
+ rafId = requestAnimationFrame(measure);
85
+ if (typeof ResizeObserver === "undefined") {
86
+ return () => {
87
+ cancelAnimationFrame(rafId);
88
+ };
89
+ }
90
+ const ro = new ResizeObserver(() => {
91
+ measure();
92
+ });
93
+ ro.observe(labelEl);
94
+ return () => {
95
+ cancelAnimationFrame(rafId);
96
+ ro.disconnect();
97
+ };
98
+ }, [extraSpace]);
99
+ return { labelRef, paddingLeft };
100
+ }
101
+ const generateEncryption = (formSalt, value) => {
102
+ const timestamp = Math.floor(Date.now() / (60 * 1e3));
103
+ const dataToEncrypt = `${formSalt}:${timestamp}:${value}`;
104
+ let hash = 0;
105
+ for (let i = 0; i < dataToEncrypt.length; i++) {
106
+ const char = dataToEncrypt.charCodeAt(i);
107
+ hash = (hash << 5) - hash + char;
108
+ hash = hash & hash;
109
+ }
110
+ return `${hash.toString(16)}_${timestamp}`;
111
+ };
112
+ const FileUpload2 = forwardRef(
113
+ (props, ref) => {
114
+ const {
115
+ label,
116
+ name,
117
+ required,
118
+ className,
119
+ labelClassName,
120
+ inputClassName,
121
+ accept = "*/*",
122
+ onChange,
123
+ onUploadStateChange,
124
+ error,
125
+ placeholder = "Click to select file",
126
+ uploadingText = "Uploading...",
127
+ selectedText = "Selected: {name}",
128
+ formSalt = "default-salt",
129
+ maxSize = 5 * 1024 * 1024,
130
+ ...rest
131
+ } = props;
132
+ const [uploadedFile, setUploadedFile] = useState(null);
133
+ const [isUploading, setIsUploading] = useState(false);
134
+ const [uploadError, setUploadError] = useState("");
135
+ const fileInputRef = useRef(null);
136
+ const { labelRef, paddingLeft } = useInlineLabelPadding(16);
137
+ const abortControllerRef = useRef(null);
138
+ const uploadFile = async (file, signal) => {
139
+ var _a;
140
+ if (file.size > maxSize) {
141
+ throw new Error(
142
+ `File ${file.name} exceeds maximum size limit of ${maxSize / 1024 / 1024}MB`
143
+ );
144
+ }
145
+ const honeypot = "";
146
+ const encryptedField = generateEncryption(formSalt, honeypot);
147
+ const credentialsResponse = await fetch("/api/get-upload-credentials", {
148
+ method: "POST",
149
+ headers: {
150
+ "Content-Type": "application/json"
151
+ },
152
+ body: JSON.stringify({
153
+ fileName: file.name,
154
+ fileSize: file.size,
155
+ fileType: file.type,
156
+ mediaType: "document",
157
+ // Request document type credentials (usually R2)
158
+ encryptedField,
159
+ honeypot
160
+ }),
161
+ signal
162
+ });
163
+ if (!credentialsResponse.ok) {
164
+ const errorData = await credentialsResponse.json();
165
+ throw new Error(
166
+ errorData.message || "Failed to get upload credentials"
167
+ );
168
+ }
169
+ const credentialsData = await credentialsResponse.json();
170
+ if (!credentialsData.success) {
171
+ throw new Error(
172
+ credentialsData.message || "Failed to get upload credentials"
173
+ );
174
+ }
175
+ const { credentials, mediaType } = credentialsData;
176
+ const uploadUrl = credentials == null ? void 0 : credentials.uploadUrl;
177
+ const platform = credentials == null ? void 0 : credentials.platform;
178
+ if (!uploadUrl) {
179
+ throw new Error("Failed to get upload URL");
180
+ }
181
+ let uploadResponse;
182
+ if (platform === "cloudflare_images") {
183
+ const formData = new FormData();
184
+ formData.append("file", file);
185
+ uploadResponse = await fetch(uploadUrl, {
186
+ method: "POST",
187
+ body: formData,
188
+ signal
189
+ });
190
+ } else {
191
+ uploadResponse = await fetch(uploadUrl, {
192
+ method: "PUT",
193
+ headers: {
194
+ "Content-Type": file.type
195
+ },
196
+ body: file,
197
+ signal
198
+ });
199
+ }
200
+ if (!uploadResponse.ok) {
201
+ throw new Error(
202
+ `File upload failed: ${uploadResponse.status} ${uploadResponse.statusText}`
203
+ );
204
+ }
205
+ let resourceId;
206
+ let publicUrl;
207
+ if (platform === "cloudflare_images") {
208
+ const uploadResult = await uploadResponse.json();
209
+ resourceId = ((_a = uploadResult == null ? void 0 : uploadResult.result) == null ? void 0 : _a.id) || uploadUrl;
210
+ const accountHash = uploadUrl.split("/")[3];
211
+ publicUrl = `https://imagedelivery.net/${accountHash}/${resourceId}/public`;
212
+ } else if (platform === "cloudflare_r2") {
213
+ resourceId = uploadUrl.split("/").pop() || uploadUrl;
214
+ publicUrl = uploadUrl;
215
+ } else {
216
+ resourceId = uploadUrl;
217
+ publicUrl = uploadUrl;
218
+ }
219
+ const mediaPayload = {
220
+ name: file.name,
221
+ size: file.size,
222
+ mimeType: file.type,
223
+ mediaType,
224
+ resourceUrl: resourceId,
225
+ storageType: platform || "cloudflare_images",
226
+ encryptedField,
227
+ honeypot
228
+ };
229
+ const createMediaResponse = await fetch("/api/create-media", {
230
+ method: "POST",
231
+ headers: {
232
+ "Content-Type": "application/json"
233
+ },
234
+ body: JSON.stringify(mediaPayload),
235
+ signal
236
+ });
237
+ if (!createMediaResponse.ok) {
238
+ const errorData = await createMediaResponse.json();
239
+ throw new Error(errorData.message || "Failed to create Media object");
240
+ }
241
+ const mediaData = await createMediaResponse.json();
242
+ if (!mediaData.success) {
243
+ throw new Error(mediaData.message || "Failed to create Media object");
244
+ }
245
+ return {
246
+ id: mediaData.mediaId,
247
+ name: file.name,
248
+ size: file.size,
249
+ url: publicUrl
250
+ // Use public URL instead of resource ID
251
+ };
252
+ };
253
+ const handleFileChange = async (e) => {
254
+ const files = e.target.files;
255
+ if (!files || files.length === 0)
256
+ return;
257
+ const file = files[0];
258
+ const abortController = new AbortController();
259
+ abortControllerRef.current = abortController;
260
+ setIsUploading(true);
261
+ onUploadStateChange == null ? void 0 : onUploadStateChange(true);
262
+ setUploadError("");
263
+ try {
264
+ if (file.size > maxSize) {
265
+ throw new Error(
266
+ `File ${file.name} exceeds ${maxSize / 1024 / 1024}MB limit`
267
+ );
268
+ }
269
+ const uploadedFileData = await uploadFile(file, abortController.signal);
270
+ setUploadedFile(uploadedFileData);
271
+ onChange == null ? void 0 : onChange(uploadedFileData);
272
+ } catch (error2) {
273
+ if (error2 instanceof Error && error2.name === "AbortError") {
274
+ console.log("User cancelled upload");
275
+ } else {
276
+ console.error("File upload failed:", error2);
277
+ setUploadError(
278
+ error2 instanceof Error ? error2.message : "File upload failed"
279
+ );
280
+ }
281
+ } finally {
282
+ setIsUploading(false);
283
+ onUploadStateChange == null ? void 0 : onUploadStateChange(false);
284
+ abortControllerRef.current = null;
285
+ if (fileInputRef.current) {
286
+ fileInputRef.current.value = "";
287
+ }
288
+ }
289
+ };
290
+ const handleClick = () => {
291
+ var _a;
292
+ if (isUploading || uploadedFile)
293
+ return;
294
+ (_a = fileInputRef.current) == null ? void 0 : _a.click();
295
+ };
296
+ const handleRemoveFile = (e) => {
297
+ e.stopPropagation();
298
+ setUploadedFile(null);
299
+ onChange == null ? void 0 : onChange(null);
300
+ setUploadError("");
301
+ };
302
+ const handleCancelUpload = (e) => {
303
+ e.stopPropagation();
304
+ if (abortControllerRef.current) {
305
+ abortControllerRef.current.abort();
306
+ }
307
+ };
308
+ const getDisplayText = () => {
309
+ if (isUploading)
310
+ return uploadingText;
311
+ if (uploadedFile) {
312
+ return selectedText.replace("{name}", uploadedFile.name);
313
+ }
314
+ return placeholder;
315
+ };
316
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
317
+ "div",
318
+ {
319
+ ref,
320
+ className: clsx("relative w-full", className),
321
+ ...rest,
322
+ children: [
323
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
324
+ "label",
325
+ {
326
+ ref: labelRef,
327
+ htmlFor: name,
328
+ className: clsx(
329
+ "absolute left-3 top-1/2 -translate-y-1/2 mt-1 opacity-70 text-sm pointer-events-none whitespace-nowrap z-10",
330
+ labelClassName
331
+ ),
332
+ children: label
333
+ }
334
+ ),
335
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
336
+ "input",
337
+ {
338
+ ref: fileInputRef,
339
+ type: "file",
340
+ id: name,
341
+ name,
342
+ required,
343
+ accept,
344
+ onChange: handleFileChange,
345
+ disabled: !!uploadedFile,
346
+ className: "hidden"
347
+ }
348
+ ),
349
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
350
+ "div",
351
+ {
352
+ onClick: handleClick,
353
+ className: clsx(
354
+ "w-full pr-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-300 bg-transparent transition-colors duration-200 flex items-center justify-start min-h-[2.5rem]",
355
+ isUploading || uploadedFile ? "cursor-not-allowed opacity-75" : "cursor-pointer hover:bg-white",
356
+ inputClassName
357
+ ),
358
+ style: { paddingLeft },
359
+ children: [
360
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
361
+ "span",
362
+ {
363
+ className: clsx(
364
+ "pl-6 text-sm font-medium flex-1 mr-4",
365
+ uploadedFile ? "text-gray-900" : "text-gray-600"
366
+ ),
367
+ children: getDisplayText()
368
+ }
369
+ ),
370
+ isUploading ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
371
+ "button",
372
+ {
373
+ type: "button",
374
+ onClick: handleCancelUpload,
375
+ className: "ml-2 p-1 hover:bg-gray-100 rounded-full transition-colors flex-shrink-0 flex items-center gap-1",
376
+ title: "Cancel upload",
377
+ children: [
378
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "animate-spin rounded-full h-4 w-4 border-t-2 border-b-2 border-primary-500" }),
379
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
380
+ "svg",
381
+ {
382
+ className: "w-3 h-3 text-gray-500",
383
+ fill: "none",
384
+ stroke: "currentColor",
385
+ viewBox: "0 0 24 24",
386
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
387
+ "path",
388
+ {
389
+ strokeLinecap: "round",
390
+ strokeLinejoin: "round",
391
+ strokeWidth: 2,
392
+ d: "M6 18L18 6M6 6l12 12"
393
+ }
394
+ )
395
+ }
396
+ )
397
+ ]
398
+ }
399
+ ) : uploadedFile ? /* @__PURE__ */ jsxRuntimeExports.jsx(
400
+ "button",
401
+ {
402
+ type: "button",
403
+ onClick: handleRemoveFile,
404
+ className: "ml-2 p-1 hover:bg-red-100 rounded-full transition-colors flex-shrink-0",
405
+ title: "Delete file",
406
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
407
+ "svg",
408
+ {
409
+ className: "w-4 h-4 text-red-500",
410
+ fill: "none",
411
+ stroke: "currentColor",
412
+ viewBox: "0 0 24 24",
413
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
414
+ "path",
415
+ {
416
+ strokeLinecap: "round",
417
+ strokeLinejoin: "round",
418
+ strokeWidth: 2,
419
+ d: "M6 18L18 6M6 6l12 12"
420
+ }
421
+ )
422
+ }
423
+ )
424
+ }
425
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsx(
426
+ "svg",
427
+ {
428
+ className: "w-5 h-5 text-gray-400 flex-shrink-0",
429
+ fill: "none",
430
+ stroke: "currentColor",
431
+ viewBox: "0 0 24 24",
432
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
433
+ "path",
434
+ {
435
+ strokeLinecap: "round",
436
+ strokeLinejoin: "round",
437
+ strokeWidth: 2,
438
+ d: "M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
439
+ }
440
+ )
441
+ }
442
+ )
443
+ ]
444
+ }
445
+ ),
446
+ (error || uploadError) && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-red-500 mt-1 text-sm", children: error || uploadError })
447
+ ]
448
+ }
449
+ );
450
+ }
451
+ );
452
+ const Input = forwardRef((props, ref) => {
453
+ const {
454
+ label,
455
+ name,
456
+ required,
457
+ requiredClassName,
458
+ labelClassName,
459
+ inputClassName,
460
+ type,
461
+ autoFocus,
462
+ value,
463
+ onChange,
464
+ error,
465
+ ...rest
466
+ } = props;
467
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { ...rest, children: [
468
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { htmlFor: name, className: labelClassName, children: [
469
+ label,
470
+ required ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: requiredClassName, children: "*" }) : ""
471
+ ] }),
472
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
473
+ "input",
474
+ {
475
+ ref,
476
+ type: type || "text",
477
+ id: name,
478
+ name,
479
+ required,
480
+ className: inputClassName,
481
+ autoComplete: name,
482
+ autoFocus,
483
+ value,
484
+ onChange
485
+ }
486
+ ),
487
+ error && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-red-500 mt-1", children: error })
488
+ ] });
489
+ });
490
+ const Input2 = forwardRef((props, ref) => {
491
+ const {
492
+ label,
493
+ name,
494
+ required,
495
+ requiredClassName,
496
+ className,
497
+ labelClassName,
498
+ inputClassName,
499
+ type = "text",
500
+ autoFocus,
501
+ value,
502
+ placeholder,
503
+ onChange,
504
+ error,
505
+ ...rest
506
+ } = props;
507
+ const { labelRef, paddingLeft } = useInlineLabelPadding(16);
508
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: clsx("relative w-full", className), ...rest, children: [
509
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
510
+ "label",
511
+ {
512
+ ref: labelRef,
513
+ htmlFor: name,
514
+ className: clsx(
515
+ "absolute left-3 top-1/2 -translate-y-1/2 mt-1 opacity-70 text-sm pointer-events-none whitespace-nowrap",
516
+ labelClassName
517
+ ),
518
+ children: [
519
+ label,
520
+ required ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: requiredClassName, children: "*" }) : ""
521
+ ]
522
+ }
523
+ ),
524
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
525
+ "input",
526
+ {
527
+ ref,
528
+ type,
529
+ id: name,
530
+ name,
531
+ required,
532
+ placeholder,
533
+ className: clsx(
534
+ "w-full pr-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-300 bg-transparent focus:bg-white transition-colors duration-200",
535
+ inputClassName
536
+ ),
537
+ style: { paddingLeft },
538
+ autoComplete: name,
539
+ autoFocus,
540
+ value,
541
+ onChange
542
+ }
543
+ ),
544
+ error && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-red-500 mt-1 text-sm", children: error })
545
+ ] });
546
+ });
547
+ const Textarea = forwardRef(
548
+ (props, ref) => {
549
+ const {
550
+ label,
551
+ name,
552
+ required,
553
+ requiredClassName,
554
+ labelClassName,
555
+ inputClassName,
556
+ rows = 8,
557
+ value,
558
+ onChange,
559
+ error,
560
+ ...rest
561
+ } = props;
562
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { ...rest, children: [
563
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { htmlFor: name, className: labelClassName, children: [
564
+ label,
565
+ required ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: requiredClassName, children: "*" }) : ""
566
+ ] }),
567
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
568
+ "textarea",
569
+ {
570
+ ref,
571
+ id: name,
572
+ name,
573
+ required,
574
+ className: inputClassName,
575
+ rows,
576
+ value,
577
+ onChange,
578
+ autoComplete: name
579
+ }
580
+ ),
581
+ error && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-red-500 mt-1", children: error })
582
+ ] });
583
+ }
584
+ );
585
+ const Textarea2 = forwardRef(
586
+ (props, ref) => {
587
+ const {
588
+ label,
589
+ name,
590
+ required,
591
+ requiredClassName,
592
+ labelClassName,
593
+ inputClassName,
594
+ className,
595
+ rows = 6,
596
+ value,
597
+ onChange,
598
+ error,
599
+ ...rest
600
+ } = props;
601
+ const [paddingTop, setPaddingTop] = useState("2.5rem");
602
+ const labelRef = useRef(null);
603
+ useEffect(() => {
604
+ const labelEl = labelRef.current;
605
+ if (!labelEl)
606
+ return;
607
+ let rafId = 0;
608
+ const measure = () => {
609
+ const height = labelEl.getBoundingClientRect().height;
610
+ setPaddingTop(`${height + 12}px`);
611
+ };
612
+ measure();
613
+ rafId = requestAnimationFrame(measure);
614
+ if (typeof ResizeObserver === "undefined") {
615
+ return () => {
616
+ cancelAnimationFrame(rafId);
617
+ };
618
+ }
619
+ const ro = new ResizeObserver(() => {
620
+ measure();
621
+ });
622
+ ro.observe(labelEl);
623
+ return () => {
624
+ cancelAnimationFrame(rafId);
625
+ ro.disconnect();
626
+ };
627
+ }, [label]);
628
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className, ...rest, children: [
629
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative w-full", children: [
630
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
631
+ "label",
632
+ {
633
+ ref: labelRef,
634
+ htmlFor: name,
635
+ className: `absolute left-3 top-2 opacity-70 text-sm pointer-events-none ${labelClassName || ""}`,
636
+ children: [
637
+ label,
638
+ required ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: requiredClassName, children: "*" }) : ""
639
+ ]
640
+ }
641
+ ),
642
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
643
+ "textarea",
644
+ {
645
+ ref,
646
+ id: name,
647
+ name,
648
+ required,
649
+ rows,
650
+ value,
651
+ onChange,
652
+ autoComplete: name,
653
+ style: { paddingTop },
654
+ className: `w-full px-3 pb-2 border border-gray-300 rounded-md shadow-sm focus:outline-none
655
+ focus:ring-2 focus:ring-primary-300 focus:border-transparent resize-y bg-transparent focus:bg-white transition-colors duration-200 ${inputClassName || ""}`
656
+ }
657
+ )
658
+ ] }),
659
+ error && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-red-500 mt-1 text-sm", children: error })
660
+ ] });
661
+ }
662
+ );
663
+ Textarea2.displayName = "Textarea2";
664
+ const countries = [
665
+ { code: "AF", name: "Afghanistan", region: "Asia", dialCode: "+93" },
666
+ { code: "AL", name: "Albania", region: "Europe", dialCode: "+355" },
667
+ { code: "DZ", name: "Algeria", region: "Africa", dialCode: "+213" },
668
+ { code: "AS", name: "American Samoa", region: "Oceania", dialCode: "+1" },
669
+ { code: "AD", name: "Andorra", region: "Europe", dialCode: "+376" },
670
+ { code: "AO", name: "Angola", region: "Africa", dialCode: "+244" },
671
+ { code: "AI", name: "Anguilla", region: "North America", dialCode: "+1" },
672
+ { code: "AG", name: "Antigua and Barbuda", region: "North America", dialCode: "+1" },
673
+ { code: "AR", name: "Argentina", region: "South America", dialCode: "+54" },
674
+ { code: "AM", name: "Armenia", region: "Asia", dialCode: "+374" },
675
+ { code: "AW", name: "Aruba", region: "North America", dialCode: "+297" },
676
+ { code: "AU", name: "Australia", region: "Oceania", dialCode: "+61" },
677
+ { code: "AT", name: "Austria", region: "Europe", dialCode: "+43" },
678
+ { code: "AZ", name: "Azerbaijan", region: "Asia", dialCode: "+994" },
679
+ { code: "BS", name: "Bahamas", region: "North America", dialCode: "+1" },
680
+ { code: "BH", name: "Bahrain", region: "Asia", dialCode: "+973" },
681
+ { code: "BD", name: "Bangladesh", region: "Asia", dialCode: "+880" },
682
+ { code: "BB", name: "Barbados", region: "North America", dialCode: "+1" },
683
+ { code: "BY", name: "Belarus", region: "Europe", dialCode: "+375" },
684
+ { code: "BE", name: "Belgium", region: "Europe", dialCode: "+32" },
685
+ { code: "BZ", name: "Belize", region: "North America", dialCode: "+501" },
686
+ { code: "BJ", name: "Benin", region: "Africa", dialCode: "+229" },
687
+ { code: "BM", name: "Bermuda", region: "North America", dialCode: "+1" },
688
+ { code: "BT", name: "Bhutan", region: "Asia", dialCode: "+975" },
689
+ { code: "BO", name: "Bolivia", region: "South America", dialCode: "+591" },
690
+ { code: "BA", name: "Bosnia and Herzegovina", region: "Europe", dialCode: "+387" },
691
+ { code: "BW", name: "Botswana", region: "Africa", dialCode: "+267" },
692
+ { code: "BR", name: "Brazil", region: "South America", dialCode: "+55" },
693
+ { code: "VG", name: "British Virgin Islands", region: "North America", dialCode: "+1" },
694
+ { code: "BN", name: "Brunei", region: "Asia", dialCode: "+673" },
695
+ { code: "BG", name: "Bulgaria", region: "Europe", dialCode: "+359" },
696
+ { code: "BF", name: "Burkina Faso", region: "Africa", dialCode: "+226" },
697
+ { code: "BI", name: "Burundi", region: "Africa", dialCode: "+257" },
698
+ { code: "KH", name: "Cambodia", region: "Asia", dialCode: "+855" },
699
+ { code: "CM", name: "Cameroon", region: "Africa", dialCode: "+237" },
700
+ { code: "CA", name: "Canada", region: "North America", dialCode: "+1" },
701
+ { code: "CV", name: "Cape Verde", region: "Africa", dialCode: "+238" },
702
+ { code: "KY", name: "Cayman Islands", region: "North America", dialCode: "+1" },
703
+ { code: "CF", name: "Central African Republic", region: "Africa", dialCode: "+236" },
704
+ { code: "TD", name: "Chad", region: "Africa", dialCode: "+235" },
705
+ { code: "CL", name: "Chile", region: "South America", dialCode: "+56" },
706
+ { code: "CN", name: "China", region: "Asia", dialCode: "+86" },
707
+ { code: "HK", name: "Hong Kong, China", region: "Asia", dialCode: "+852" },
708
+ { code: "MO", name: "Macao, China", region: "Asia", dialCode: "+853" },
709
+ { code: "TW", name: "Taiwan, China", region: "Asia", dialCode: "+886" },
710
+ { code: "CO", name: "Colombia", region: "South America", dialCode: "+57" },
711
+ { code: "KM", name: "Comoros", region: "Africa", dialCode: "+269" },
712
+ { code: "CG", name: "Congo", region: "Africa", dialCode: "+242" },
713
+ { code: "CD", name: "Congo (DRC)", region: "Africa", dialCode: "+243" },
714
+ { code: "CR", name: "Costa Rica", region: "North America", dialCode: "+506" },
715
+ { code: "CI", name: "Côte d’Ivoire", region: "Africa", dialCode: "+225" },
716
+ { code: "HR", name: "Croatia", region: "Europe", dialCode: "+385" },
717
+ { code: "CU", name: "Cuba", region: "North America", dialCode: "+53" },
718
+ { code: "CY", name: "Cyprus", region: "Europe", dialCode: "+357" },
719
+ { code: "CZ", name: "Czechia", region: "Europe", dialCode: "+420" },
720
+ { code: "DK", name: "Denmark", region: "Europe", dialCode: "+45" },
721
+ { code: "DJ", name: "Djibouti", region: "Africa", dialCode: "+253" },
722
+ { code: "DM", name: "Dominica", region: "North America", dialCode: "+1" },
723
+ { code: "DO", name: "Dominican Republic", region: "North America", dialCode: "+1" },
724
+ { code: "EC", name: "Ecuador", region: "South America", dialCode: "+593" },
725
+ { code: "EG", name: "Egypt", region: "Africa", dialCode: "+20" },
726
+ { code: "SV", name: "El Salvador", region: "North America", dialCode: "+503" },
727
+ { code: "GQ", name: "Equatorial Guinea", region: "Africa", dialCode: "+240" },
728
+ { code: "ER", name: "Eritrea", region: "Africa", dialCode: "+291" },
729
+ { code: "EE", name: "Estonia", region: "Europe", dialCode: "+372" },
730
+ { code: "SZ", name: "Eswatini", region: "Africa", dialCode: "+268" },
731
+ { code: "ET", name: "Ethiopia", region: "Africa", dialCode: "+251" },
732
+ { code: "FO", name: "Faroe Islands", region: "Europe", dialCode: "+298" },
733
+ { code: "FJ", name: "Fiji", region: "Oceania", dialCode: "+679" },
734
+ { code: "FI", name: "Finland", region: "Europe", dialCode: "+358" },
735
+ { code: "FR", name: "France", region: "Europe", dialCode: "+33" },
736
+ { code: "GF", name: "French Guiana", region: "South America", dialCode: "+594" },
737
+ { code: "PF", name: "French Polynesia", region: "Oceania", dialCode: "+689" },
738
+ { code: "GA", name: "Gabon", region: "Africa", dialCode: "+241" },
739
+ { code: "GM", name: "Gambia", region: "Africa", dialCode: "+220" },
740
+ { code: "GE", name: "Georgia", region: "Asia", dialCode: "+995" },
741
+ { code: "DE", name: "Germany", region: "Europe", dialCode: "+49" },
742
+ { code: "GH", name: "Ghana", region: "Africa", dialCode: "+233" },
743
+ { code: "GI", name: "Gibraltar", region: "Europe", dialCode: "+350" },
744
+ { code: "GR", name: "Greece", region: "Europe", dialCode: "+30" },
745
+ { code: "GL", name: "Greenland", region: "North America", dialCode: "+299" },
746
+ { code: "GD", name: "Grenada", region: "North America", dialCode: "+1" },
747
+ { code: "GP", name: "Guadeloupe", region: "North America", dialCode: "+590" },
748
+ { code: "GU", name: "Guam", region: "Oceania", dialCode: "+1" },
749
+ { code: "GT", name: "Guatemala", region: "North America", dialCode: "+502" },
750
+ { code: "GG", name: "Guernsey", region: "Europe", dialCode: "+44" },
751
+ { code: "GN", name: "Guinea", region: "Africa", dialCode: "+224" },
752
+ { code: "GW", name: "Guinea-Bissau", region: "Africa", dialCode: "+245" },
753
+ { code: "GY", name: "Guyana", region: "South America", dialCode: "+592" },
754
+ { code: "HT", name: "Haiti", region: "North America", dialCode: "+509" },
755
+ { code: "HN", name: "Honduras", region: "North America", dialCode: "+504" },
756
+ { code: "HU", name: "Hungary", region: "Europe", dialCode: "+36" },
757
+ { code: "IS", name: "Iceland", region: "Europe", dialCode: "+354" },
758
+ { code: "IN", name: "India", region: "Asia", dialCode: "+91" },
759
+ { code: "ID", name: "Indonesia", region: "Asia", dialCode: "+62" },
760
+ { code: "IR", name: "Iran", region: "Asia", dialCode: "+98" },
761
+ { code: "IQ", name: "Iraq", region: "Asia", dialCode: "+964" },
762
+ { code: "IE", name: "Ireland", region: "Europe", dialCode: "+353" },
763
+ { code: "IM", name: "Isle of Man", region: "Europe", dialCode: "+44" },
764
+ { code: "IL", name: "Israel", region: "Asia", dialCode: "+972" },
765
+ { code: "IT", name: "Italy", region: "Europe", dialCode: "+39" },
766
+ { code: "JM", name: "Jamaica", region: "North America", dialCode: "+1" },
767
+ { code: "JP", name: "Japan", region: "Asia", dialCode: "+81" },
768
+ { code: "JE", name: "Jersey", region: "Europe", dialCode: "+44" },
769
+ { code: "JO", name: "Jordan", region: "Asia", dialCode: "+962" },
770
+ { code: "KZ", name: "Kazakhstan", region: "Asia", dialCode: "+7" },
771
+ { code: "KE", name: "Kenya", region: "Africa", dialCode: "+254" },
772
+ { code: "KI", name: "Kiribati", region: "Oceania", dialCode: "+686" },
773
+ { code: "XK", name: "Kosovo", region: "Europe", dialCode: "+383" },
774
+ { code: "KW", name: "Kuwait", region: "Asia", dialCode: "+965" },
775
+ { code: "KG", name: "Kyrgyzstan", region: "Asia", dialCode: "+996" },
776
+ { code: "LA", name: "Laos", region: "Asia", dialCode: "+856" },
777
+ { code: "LV", name: "Latvia", region: "Europe", dialCode: "+371" },
778
+ { code: "LB", name: "Lebanon", region: "Asia", dialCode: "+961" },
779
+ { code: "LS", name: "Lesotho", region: "Africa", dialCode: "+266" },
780
+ { code: "LR", name: "Liberia", region: "Africa", dialCode: "+231" },
781
+ { code: "LY", name: "Libya", region: "Africa", dialCode: "+218" },
782
+ { code: "LI", name: "Liechtenstein", region: "Europe", dialCode: "+423" },
783
+ { code: "LT", name: "Lithuania", region: "Europe", dialCode: "+370" },
784
+ { code: "LU", name: "Luxembourg", region: "Europe", dialCode: "+352" },
785
+ { code: "MG", name: "Madagascar", region: "Africa", dialCode: "+261" },
786
+ { code: "MW", name: "Malawi", region: "Africa", dialCode: "+265" },
787
+ { code: "MY", name: "Malaysia", region: "Asia", dialCode: "+60" },
788
+ { code: "MV", name: "Maldives", region: "Asia", dialCode: "+960" },
789
+ { code: "ML", name: "Mali", region: "Africa", dialCode: "+223" },
790
+ { code: "MT", name: "Malta", region: "Europe", dialCode: "+356" },
791
+ { code: "MH", name: "Marshall Islands", region: "Oceania", dialCode: "+692" },
792
+ { code: "MQ", name: "Martinique", region: "North America", dialCode: "+596" },
793
+ { code: "MR", name: "Mauritania", region: "Africa", dialCode: "+222" },
794
+ { code: "MU", name: "Mauritius", region: "Africa", dialCode: "+230" },
795
+ { code: "YT", name: "Mayotte", region: "Africa", dialCode: "+262" },
796
+ { code: "MX", name: "Mexico", region: "North America", dialCode: "+52" },
797
+ { code: "FM", name: "Micronesia", region: "Oceania", dialCode: "+691" },
798
+ { code: "MD", name: "Moldova", region: "Europe", dialCode: "+373" },
799
+ { code: "MC", name: "Monaco", region: "Europe", dialCode: "+377" },
800
+ { code: "MN", name: "Mongolia", region: "Asia", dialCode: "+976" },
801
+ { code: "ME", name: "Montenegro", region: "Europe", dialCode: "+382" },
802
+ { code: "MS", name: "Montserrat", region: "North America", dialCode: "+1" },
803
+ { code: "MA", name: "Morocco", region: "Africa", dialCode: "+212" },
804
+ { code: "MZ", name: "Mozambique", region: "Africa", dialCode: "+258" },
805
+ { code: "MM", name: "Myanmar", region: "Asia", dialCode: "+95" },
806
+ { code: "NA", name: "Namibia", region: "Africa", dialCode: "+264" },
807
+ { code: "NR", name: "Nauru", region: "Oceania", dialCode: "+674" },
808
+ { code: "NP", name: "Nepal", region: "Asia", dialCode: "+977" },
809
+ { code: "NL", name: "Netherlands", region: "Europe", dialCode: "+31" },
810
+ { code: "NC", name: "New Caledonia", region: "Oceania", dialCode: "+687" },
811
+ { code: "NZ", name: "New Zealand", region: "Oceania", dialCode: "+64" },
812
+ { code: "NI", name: "Nicaragua", region: "North America", dialCode: "+505" },
813
+ { code: "NE", name: "Niger", region: "Africa", dialCode: "+227" },
814
+ { code: "NG", name: "Nigeria", region: "Africa", dialCode: "+234" },
815
+ { code: "NU", name: "Niue", region: "Oceania", dialCode: "+683" },
816
+ { code: "NF", name: "Norfolk Island", region: "Oceania", dialCode: "+672" },
817
+ { code: "KP", name: "North Korea", region: "Asia", dialCode: "+850" },
818
+ { code: "MK", name: "North Macedonia", region: "Europe", dialCode: "+389" },
819
+ { code: "MP", name: "Northern Mariana Islands", region: "Oceania", dialCode: "+1" },
820
+ { code: "NO", name: "Norway", region: "Europe", dialCode: "+47" },
821
+ { code: "OM", name: "Oman", region: "Asia", dialCode: "+968" },
822
+ { code: "PK", name: "Pakistan", region: "Asia", dialCode: "+92" },
823
+ { code: "PW", name: "Palau", region: "Oceania", dialCode: "+680" },
824
+ { code: "PS", name: "Palestine", region: "Asia", dialCode: "+970" },
825
+ { code: "PA", name: "Panama", region: "North America", dialCode: "+507" },
826
+ { code: "PG", name: "Papua New Guinea", region: "Oceania", dialCode: "+675" },
827
+ { code: "PY", name: "Paraguay", region: "South America", dialCode: "+595" },
828
+ { code: "PE", name: "Peru", region: "South America", dialCode: "+51" },
829
+ { code: "PH", name: "Philippines", region: "Asia", dialCode: "+63" },
830
+ { code: "PL", name: "Poland", region: "Europe", dialCode: "+48" },
831
+ { code: "PT", name: "Portugal", region: "Europe", dialCode: "+351" },
832
+ { code: "PR", name: "Puerto Rico", region: "North America", dialCode: "+1" },
833
+ { code: "QA", name: "Qatar", region: "Asia", dialCode: "+974" },
834
+ { code: "RE", name: "Réunion", region: "Africa", dialCode: "+262" },
835
+ { code: "RO", name: "Romania", region: "Europe", dialCode: "+40" },
836
+ { code: "RU", name: "Russia", region: "Europe", dialCode: "+7" },
837
+ { code: "RW", name: "Rwanda", region: "Africa", dialCode: "+250" },
838
+ { code: "BL", name: "Saint Barthélemy", region: "North America", dialCode: "+590" },
839
+ { code: "KN", name: "Saint Kitts and Nevis", region: "North America", dialCode: "+1" },
840
+ { code: "LC", name: "Saint Lucia", region: "North America", dialCode: "+1" },
841
+ { code: "MF", name: "Saint Martin", region: "North America", dialCode: "+590" },
842
+ { code: "PM", name: "Saint Pierre and Miquelon", region: "North America", dialCode: "+508" },
843
+ { code: "VC", name: "Saint Vincent and the Grenadines", region: "North America", dialCode: "+1" },
844
+ { code: "WS", name: "Samoa", region: "Oceania", dialCode: "+685" },
845
+ { code: "SM", name: "San Marino", region: "Europe", dialCode: "+378" },
846
+ { code: "ST", name: "São Tomé and Príncipe", region: "Africa", dialCode: "+239" },
847
+ { code: "SA", name: "Saudi Arabia", region: "Asia", dialCode: "+966" },
848
+ { code: "SN", name: "Senegal", region: "Africa", dialCode: "+221" },
849
+ { code: "RS", name: "Serbia", region: "Europe", dialCode: "+381" },
850
+ { code: "SC", name: "Seychelles", region: "Africa", dialCode: "+248" },
851
+ { code: "SL", name: "Sierra Leone", region: "Africa", dialCode: "+232" },
852
+ { code: "SG", name: "Singapore", region: "Asia", dialCode: "+65" },
853
+ { code: "SX", name: "Sint Maarten", region: "North America", dialCode: "+1" },
854
+ { code: "SK", name: "Slovakia", region: "Europe", dialCode: "+421" },
855
+ { code: "SI", name: "Slovenia", region: "Europe", dialCode: "+386" },
856
+ { code: "SB", name: "Solomon Islands", region: "Oceania", dialCode: "+677" },
857
+ { code: "SO", name: "Somalia", region: "Africa", dialCode: "+252" },
858
+ { code: "ZA", name: "South Africa", region: "Africa", dialCode: "+27" },
859
+ { code: "KR", name: "South Korea", region: "Asia", dialCode: "+82" },
860
+ { code: "SS", name: "South Sudan", region: "Africa", dialCode: "+211" },
861
+ { code: "ES", name: "Spain", region: "Europe", dialCode: "+34" },
862
+ { code: "LK", name: "Sri Lanka", region: "Asia", dialCode: "+94" },
863
+ { code: "SD", name: "Sudan", region: "Africa", dialCode: "+249" },
864
+ { code: "SR", name: "Suriname", region: "South America", dialCode: "+597" },
865
+ { code: "SE", name: "Sweden", region: "Europe", dialCode: "+46" },
866
+ { code: "CH", name: "Switzerland", region: "Europe", dialCode: "+41" },
867
+ { code: "SY", name: "Syria", region: "Asia", dialCode: "+963" },
868
+ { code: "TJ", name: "Tajikistan", region: "Asia", dialCode: "+992" },
869
+ { code: "TZ", name: "Tanzania", region: "Africa", dialCode: "+255" },
870
+ { code: "TH", name: "Thailand", region: "Asia", dialCode: "+66" },
871
+ { code: "TG", name: "Togo", region: "Africa", dialCode: "+228" },
872
+ { code: "TK", name: "Tokelau", region: "Oceania", dialCode: "+690" },
873
+ { code: "TO", name: "Tonga", region: "Oceania", dialCode: "+676" },
874
+ { code: "TT", name: "Trinidad and Tobago", region: "North America", dialCode: "+1" },
875
+ { code: "TN", name: "Tunisia", region: "Africa", dialCode: "+216" },
876
+ { code: "TR", name: "Türkiye", region: "Europe", dialCode: "+90" },
877
+ { code: "TM", name: "Turkmenistan", region: "Asia", dialCode: "+993" },
878
+ { code: "TC", name: "Turks and Caicos Islands", region: "North America", dialCode: "+1" },
879
+ { code: "TV", name: "Tuvalu", region: "Oceania", dialCode: "+688" },
880
+ { code: "UG", name: "Uganda", region: "Africa", dialCode: "+256" },
881
+ { code: "UA", name: "Ukraine", region: "Europe", dialCode: "+380" },
882
+ { code: "AE", name: "United Arab Emirates", region: "Asia", dialCode: "+971" },
883
+ { code: "GB", name: "United Kingdom", region: "Europe", dialCode: "+44" },
884
+ { code: "US", name: "United States", region: "North America", dialCode: "+1" },
885
+ { code: "UY", name: "Uruguay", region: "South America", dialCode: "+598" },
886
+ { code: "UZ", name: "Uzbekistan", region: "Asia", dialCode: "+998" },
887
+ { code: "VU", name: "Vanuatu", region: "Oceania", dialCode: "+678" },
888
+ { code: "VA", name: "Vatican City", region: "Europe", dialCode: "+39" },
889
+ { code: "VE", name: "Venezuela", region: "South America", dialCode: "+58" },
890
+ { code: "VN", name: "Vietnam", region: "Asia", dialCode: "+84" },
891
+ { code: "VI", name: "U.S. Virgin Islands", region: "North America", dialCode: "+1" },
892
+ { code: "WF", name: "Wallis and Futuna", region: "Oceania", dialCode: "+681" },
893
+ { code: "EH", name: "Western Sahara", region: "Africa", dialCode: "+212" },
894
+ { code: "YE", name: "Yemen", region: "Asia", dialCode: "+967" },
895
+ { code: "ZM", name: "Zambia", region: "Africa", dialCode: "+260" },
896
+ { code: "ZW", name: "Zimbabwe", region: "Africa", dialCode: "+263" }
897
+ ];
898
+ function useTelControl({ countries: countries$1, name, value, onChange }) {
899
+ const allCountries = useMemo(() => countries$1 || countries, [countries$1]);
900
+ const [selected, setSelected] = useState(null);
901
+ const [localNumber, setLocalNumber] = useState("");
902
+ const toStringValue = (val) => {
903
+ if (typeof val === "string")
904
+ return val;
905
+ if (typeof val === "number")
906
+ return String(val);
907
+ if (Array.isArray(val))
908
+ return val[0] ?? "";
909
+ return "";
910
+ };
911
+ useEffect(() => {
912
+ const s = toStringValue(value).trim();
913
+ const m = s.match(/^\+\d{1,4}/);
914
+ const dial = m ? m[0] : "";
915
+ const number = s.slice(dial.length).trimStart();
916
+ setLocalNumber(number || "");
917
+ if (dial) {
918
+ const found = allCountries.find((c) => c.dialCode === dial) || null;
919
+ setSelected(found);
920
+ } else {
921
+ setSelected(null);
922
+ }
923
+ }, [value, allCountries]);
924
+ const emitChange = (combined) => {
925
+ if (!onChange)
926
+ return;
927
+ const evt = {
928
+ target: { value: combined, name },
929
+ currentTarget: { value: combined, name }
930
+ };
931
+ onChange(evt);
932
+ };
933
+ const filtered = (query) => {
934
+ if (!query)
935
+ return allCountries;
936
+ const q = query.toLowerCase();
937
+ return allCountries.filter(
938
+ (c) => c.name.toLowerCase().includes(q) || c.region && c.region.toLowerCase().includes(q) || c.dialCode.replace("+", "").includes(q.replace("+", ""))
939
+ );
940
+ };
941
+ const onSelectCountry = (c) => {
942
+ setSelected(c);
943
+ const combined = `${c.dialCode} ${localNumber}`.trim();
944
+ emitChange(combined);
945
+ };
946
+ const onChangeLocal = (next) => {
947
+ setLocalNumber(next);
948
+ const combined = `${(selected == null ? void 0 : selected.dialCode) || ""} ${next}`.trim();
949
+ emitChange(combined);
950
+ };
951
+ return {
952
+ allCountries,
953
+ selected,
954
+ setSelected,
955
+ localNumber,
956
+ setLocalNumber,
957
+ filtered,
958
+ onSelectCountry,
959
+ onChangeLocal
960
+ };
961
+ }
962
+ const TelInput = forwardRef(
963
+ (props, ref) => {
964
+ const {
965
+ label,
966
+ name,
967
+ required,
968
+ requiredClassName,
969
+ className,
970
+ labelClassName,
971
+ inputClassName,
972
+ countries: countries2,
973
+ placeholder,
974
+ value,
975
+ onChange
976
+ } = props;
977
+ const { allCountries, selected, localNumber, onSelectCountry, onChangeLocal } = useTelControl({ countries: countries2, name, value, onChange });
978
+ const [open, setOpen] = useState(false);
979
+ const [query, setQuery] = useState("");
980
+ const panelRef = useRef(null);
981
+ const buttonRef = useRef(null);
982
+ useEffect(() => {
983
+ const onDocClick = (e) => {
984
+ const target = e.target;
985
+ if (open && panelRef.current && !panelRef.current.contains(target) && buttonRef.current && !buttonRef.current.contains(target)) {
986
+ setOpen(false);
987
+ }
988
+ };
989
+ document.addEventListener("mousedown", onDocClick);
990
+ return () => document.removeEventListener("mousedown", onDocClick);
991
+ }, [open]);
992
+ const filtered = useMemo(() => {
993
+ if (!query)
994
+ return allCountries;
995
+ const q = query.toLowerCase();
996
+ return allCountries.filter(
997
+ (c) => c.name.toLowerCase().includes(q) || c.region && c.region.toLowerCase().includes(q) || c.dialCode.replace("+", "").includes(q.replace("+", ""))
998
+ );
999
+ }, [allCountries, query]);
1000
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: clsx("w-full", className), children: [
1001
+ label ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
1002
+ "label",
1003
+ {
1004
+ htmlFor: name,
1005
+ className: clsx(
1006
+ "mb-1 inline-flex items-center text-sm text-gray-600",
1007
+ labelClassName
1008
+ ),
1009
+ children: [
1010
+ label,
1011
+ required ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: requiredClassName, children: "*" }) : null
1012
+ ]
1013
+ }
1014
+ ) : null,
1015
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
1016
+ "div",
1017
+ {
1018
+ className: clsx(
1019
+ "flex h-10 items-center gap-2 rounded-md focus-within:ring-2 focus-within:ring-primary-300 focus-within:ring-offset-0 bg-white",
1020
+ inputClassName
1021
+ ),
1022
+ children: [
1023
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative h-full", children: [
1024
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
1025
+ "button",
1026
+ {
1027
+ ref: buttonRef,
1028
+ type: "button",
1029
+ "aria-haspopup": "listbox",
1030
+ "aria-expanded": open,
1031
+ onClick: () => setOpen((v) => !v),
1032
+ className: "h-full min-w-[200px] inline-flex items-center justify-between whitespace-nowrap rounded-md px-3 py-0 text-sm",
1033
+ children: [
1034
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate text-left leading-tight", children: selected ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
1035
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium", children: selected.name }),
1036
+ selected.region ? /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-gray-500", children: [
1037
+ ", ",
1038
+ selected.region
1039
+ ] }) : null,
1040
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ml-1 text-gray-700", children: selected.dialCode })
1041
+ ] }) : "Select Country/Region" }),
1042
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1043
+ "svg",
1044
+ {
1045
+ className: "ml-2 h-4 w-4 text-gray-500",
1046
+ viewBox: "0 0 20 20",
1047
+ fill: "currentColor",
1048
+ "aria-hidden": "true",
1049
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1050
+ "path",
1051
+ {
1052
+ fillRule: "evenodd",
1053
+ d: "M5.23 7.21a.75.75 0 011.06.02L10 11.085l3.71-3.855a.75.75 0 111.08 1.04l-4.24 4.41a.75.75 0 01-1.08 0L5.25 8.27a.75.75 0 01-.02-1.06z",
1054
+ clipRule: "evenodd"
1055
+ }
1056
+ )
1057
+ }
1058
+ )
1059
+ ]
1060
+ }
1061
+ ),
1062
+ open ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
1063
+ "div",
1064
+ {
1065
+ ref: panelRef,
1066
+ className: "absolute z-50 mt-2 w-[320px] rounded-md border border-gray-200 bg-white p-2 shadow-lg",
1067
+ children: [
1068
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 rounded-md border border-gray-200 px-2 py-1.5", children: [
1069
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1070
+ "input",
1071
+ {
1072
+ value: query,
1073
+ onChange: (e) => setQuery(e.target.value),
1074
+ placeholder: "Enter Country/Region",
1075
+ className: "h-8 w-full bg-transparent text-sm outline-none"
1076
+ }
1077
+ ),
1078
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1079
+ "svg",
1080
+ {
1081
+ className: "h-4 w-4 text-gray-500",
1082
+ viewBox: "0 0 20 20",
1083
+ fill: "currentColor",
1084
+ "aria-hidden": "true",
1085
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1086
+ "path",
1087
+ {
1088
+ fillRule: "evenodd",
1089
+ d: "M8.5 3.5a5 5 0 013.996 8.1l3.202 3.203a.75.75 0 11-1.06 1.06l-3.203-3.202A5 5 0 118.5 3.5zm0 1.5a3.5 3.5 0 100 7 3.5 3.5 0 000-7z",
1090
+ clipRule: "evenodd"
1091
+ }
1092
+ )
1093
+ }
1094
+ )
1095
+ ] }),
1096
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 max-h-64 overflow-auto pr-1", children: filtered.map((c) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
1097
+ "button",
1098
+ {
1099
+ type: "button",
1100
+ onClick: () => {
1101
+ onSelectCountry(c);
1102
+ setOpen(false);
1103
+ },
1104
+ className: "flex w-full items-center gap-2 rounded-md px-2 py-2 text-left text-sm hover:bg-gray-50",
1105
+ children: [
1106
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1107
+ "span",
1108
+ {
1109
+ className: clsx(
1110
+ "inline-block h-3 w-3 rounded-full border",
1111
+ (selected == null ? void 0 : selected.code) === c.code ? "border-primary-500 ring-4 ring-primary-200" : "border-gray-400"
1112
+ )
1113
+ }
1114
+ ),
1115
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex-1 truncate", children: [
1116
+ c.name,
1117
+ c.region ? /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-gray-500", children: [
1118
+ ", ",
1119
+ c.region
1120
+ ] }) : null
1121
+ ] }),
1122
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-gray-700", children: c.dialCode })
1123
+ ]
1124
+ },
1125
+ c.code
1126
+ )) })
1127
+ ]
1128
+ }
1129
+ ) : null
1130
+ ] }),
1131
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1132
+ "input",
1133
+ {
1134
+ ref,
1135
+ id: name,
1136
+ name,
1137
+ required,
1138
+ placeholder: placeholder || "Phone number",
1139
+ inputMode: "tel",
1140
+ className: clsx("h-10 w-full px-3 py-0 text-sm leading-tight ring-0 outline-none rounded-md"),
1141
+ value: localNumber,
1142
+ onChange: (e) => {
1143
+ const next = e.target.value;
1144
+ onChangeLocal(next);
1145
+ },
1146
+ autoComplete: name,
1147
+ autoCorrect: "off",
1148
+ spellCheck: false
1149
+ }
1150
+ )
1151
+ ]
1152
+ }
1153
+ )
1154
+ ] });
1155
+ }
1156
+ );
1157
+ const TelInput2 = forwardRef(
1158
+ (props, ref) => {
1159
+ const {
1160
+ label,
1161
+ name,
1162
+ required,
1163
+ requiredClassName,
1164
+ className,
1165
+ labelClassName,
1166
+ inputClassName,
1167
+ countries: countries2,
1168
+ placeholder,
1169
+ selectPlaceholder,
1170
+ value,
1171
+ onChange
1172
+ } = props;
1173
+ const { labelRef, paddingLeft } = useInlineLabelPadding(16);
1174
+ const {
1175
+ allCountries,
1176
+ selected,
1177
+ localNumber,
1178
+ onSelectCountry,
1179
+ onChangeLocal
1180
+ } = useTelControl({ countries: countries2, name, value, onChange });
1181
+ const [open, setOpen] = useState(false);
1182
+ const [query, setQuery] = useState("");
1183
+ const panelRef = useRef(null);
1184
+ const buttonRef = useRef(null);
1185
+ const filtered = useMemo(() => {
1186
+ if (!query)
1187
+ return allCountries;
1188
+ const q = query.toLowerCase();
1189
+ return allCountries.filter(
1190
+ (c) => c.name.toLowerCase().includes(q) || c.region && c.region.toLowerCase().includes(q) || c.dialCode.replace("+", "").includes(q.replace("+", ""))
1191
+ );
1192
+ }, [allCountries, query]);
1193
+ useEffect(() => {
1194
+ const onDocClick = (e) => {
1195
+ const target = e.target;
1196
+ if (open && panelRef.current && !panelRef.current.contains(target) && buttonRef.current && !buttonRef.current.contains(target)) {
1197
+ setOpen(false);
1198
+ }
1199
+ };
1200
+ document.addEventListener("mousedown", onDocClick);
1201
+ return () => document.removeEventListener("mousedown", onDocClick);
1202
+ }, [open]);
1203
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: clsx("relative w-full", className), children: [
1204
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
1205
+ "label",
1206
+ {
1207
+ ref: labelRef,
1208
+ htmlFor: name,
1209
+ className: clsx(
1210
+ "absolute left-3 top-1/2 -translate-y-1/2 mt-1 opacity-70 text-sm pointer-events-none whitespace-nowrap",
1211
+ labelClassName
1212
+ ),
1213
+ children: [
1214
+ label,
1215
+ required ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: requiredClassName, children: "*" }) : null
1216
+ ]
1217
+ }
1218
+ ),
1219
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
1220
+ "div",
1221
+ {
1222
+ className: clsx(
1223
+ "flex h-10 items-center gap-2 rounded-md shadow-sm focus-within:ring-2 focus-within:ring-primary-300",
1224
+ inputClassName
1225
+ ),
1226
+ style: { paddingLeft },
1227
+ children: [
1228
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative h-full", children: [
1229
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
1230
+ "button",
1231
+ {
1232
+ ref: buttonRef,
1233
+ type: "button",
1234
+ "aria-haspopup": "listbox",
1235
+ "aria-expanded": open,
1236
+ onClick: () => setOpen((v) => !v),
1237
+ className: "h-full min-w-[100px] inline-flex items-center justify-between whitespace-nowrap rounded-md px-3 py-0 text-sm",
1238
+ children: [
1239
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate text-left leading-tight", children: selected ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium", children: selected.dialCode }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-gray-500", children: "+ Code" }) }),
1240
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1241
+ "svg",
1242
+ {
1243
+ className: "ml-2 h-4 w-4 text-gray-500",
1244
+ viewBox: "0 0 20 20",
1245
+ fill: "currentColor",
1246
+ "aria-hidden": "true",
1247
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1248
+ "path",
1249
+ {
1250
+ fillRule: "evenodd",
1251
+ d: "M5.23 7.21a.75.75 0 011.06.02L10 11.085l3.71-3.855a.75.75 0 111.08 1.04l-4.24 4.41a.75.75 0 01-1.08 0L5.25 8.27a.75.75 0 01-.02-1.06z",
1252
+ clipRule: "evenodd"
1253
+ }
1254
+ )
1255
+ }
1256
+ )
1257
+ ]
1258
+ }
1259
+ ),
1260
+ open ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
1261
+ "div",
1262
+ {
1263
+ ref: panelRef,
1264
+ className: "absolute z-50 mt-2 w-[320px] rounded-md border border-gray-200 bg-white p-2 shadow-lg",
1265
+ children: [
1266
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 rounded-md border border-gray-200 px-2 py-1.5", children: [
1267
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1268
+ "input",
1269
+ {
1270
+ value: query,
1271
+ onChange: (e) => setQuery(e.target.value),
1272
+ placeholder: selectPlaceholder || "Enter Country/Region",
1273
+ className: "h-8 w-full bg-transparent text-sm outline-none"
1274
+ }
1275
+ ),
1276
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1277
+ "svg",
1278
+ {
1279
+ className: "h-4 w-4 text-gray-500",
1280
+ viewBox: "0 0 20 20",
1281
+ fill: "currentColor",
1282
+ "aria-hidden": "true",
1283
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1284
+ "path",
1285
+ {
1286
+ fillRule: "evenodd",
1287
+ d: "M8.5 3.5a5 5 0 013.996 8.1l3.202 3.203a.75.75 0 11-1.06 1.06l-3.203-3.202A5 5 0 118.5 3.5zm0 1.5a3.5 3.5 0 100 7 3.5 3.5 0 000-7z",
1288
+ clipRule: "evenodd"
1289
+ }
1290
+ )
1291
+ }
1292
+ )
1293
+ ] }),
1294
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 max-h-64 overflow-auto pr-1", children: filtered.map((c) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
1295
+ "button",
1296
+ {
1297
+ type: "button",
1298
+ onClick: () => {
1299
+ onSelectCountry(c);
1300
+ setOpen(false);
1301
+ },
1302
+ className: "flex w-full items-center gap-2 rounded-md px-2 py-2 text-left text-sm hover:bg-gray-50",
1303
+ children: [
1304
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1305
+ "span",
1306
+ {
1307
+ className: clsx(
1308
+ "inline-block h-3 w-3 rounded-full border",
1309
+ (selected == null ? void 0 : selected.code) === c.code ? "border-primary-500 ring-4 ring-primary-200" : "border-gray-400"
1310
+ )
1311
+ }
1312
+ ),
1313
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex-1 truncate", children: [
1314
+ c.name,
1315
+ c.region ? /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-gray-500", children: [
1316
+ ", ",
1317
+ c.region
1318
+ ] }) : null
1319
+ ] }),
1320
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-gray-700", children: c.dialCode })
1321
+ ]
1322
+ },
1323
+ c.code
1324
+ )) })
1325
+ ]
1326
+ }
1327
+ ) : null
1328
+ ] }),
1329
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1330
+ "input",
1331
+ {
1332
+ ref,
1333
+ id: name,
1334
+ name,
1335
+ required,
1336
+ placeholder: placeholder || "Phone number",
1337
+ inputMode: "tel",
1338
+ className: clsx(
1339
+ "h-10 w-full appearance-none rounded-md border-0 bg-transparent px-3 py-0 text-sm leading-tight focus:outline-none"
1340
+ ),
1341
+ value: localNumber,
1342
+ onChange: (e) => onChangeLocal(e.target.value),
1343
+ autoComplete: name,
1344
+ autoCorrect: "off",
1345
+ spellCheck: false
1346
+ }
1347
+ )
1348
+ ]
1349
+ }
1350
+ )
1351
+ ] });
1352
+ }
1353
+ );
1354
+ const getControl = (controlName, fieldStyle) => {
1355
+ switch (controlName) {
1356
+ case "Input":
1357
+ return getInput(fieldStyle);
1358
+ case "Textarea":
1359
+ return getTextarea(fieldStyle);
1360
+ case "TelInput":
1361
+ return getTelInput(fieldStyle);
1362
+ default:
1363
+ return getInput(fieldStyle);
1364
+ }
1365
+ };
1366
+ const getInput = (fieldStyle) => {
1367
+ if (fieldStyle === "default") {
1368
+ return Input;
1369
+ } else if (fieldStyle === "label-inline") {
1370
+ return Input2;
1371
+ } else {
1372
+ return Input;
1373
+ }
1374
+ };
1375
+ const getTextarea = (fieldStyle) => {
1376
+ if (fieldStyle === "default") {
1377
+ return Textarea;
1378
+ } else if (fieldStyle === "label-inline") {
1379
+ return Textarea2;
1380
+ } else {
1381
+ return Textarea;
1382
+ }
1383
+ };
1384
+ const getTelInput = (fieldStyle) => {
1385
+ if (fieldStyle === "label-inline") {
1386
+ return TelInput2;
1387
+ }
1388
+ return TelInput;
1389
+ };
1390
+ const getFileUpload = (fieldStyle) => {
1391
+ if (fieldStyle === "default") {
1392
+ return FileUpload2;
1393
+ } else if (fieldStyle === "label-inline") {
1394
+ return FileUpload2;
1395
+ } else {
1396
+ return FileUpload2;
1397
+ }
1398
+ };
1399
+ const ContactForm = forwardRef(
1400
+ (props, ref) => {
1401
+ const {
1402
+ submit,
1403
+ actionUrl = "/api/ask-for-quote",
1404
+ formSalt,
1405
+ fields = [],
1406
+ className,
1407
+ classNames,
1408
+ fromCta,
1409
+ ...rest
1410
+ } = props;
1411
+ const [formData, setFormData] = useState({
1412
+ name: "",
1413
+ email: "",
1414
+ company: "",
1415
+ message: "",
1416
+ phone: "",
1417
+ // 初始化蜜罐字段
1418
+ attachments: []
1419
+ // 附件 ID 列表
1420
+ });
1421
+ const [errors, setErrors] = useState({});
1422
+ const [submitting, setSubmitting] = useState(false);
1423
+ const [isUploading, setIsUploading] = useState(false);
1424
+ const [submitStatus, setSubmitStatus] = useState({});
1425
+ const lastCta = window.lastCta;
1426
+ const handleChange = (e, isExtends) => {
1427
+ const { name, value } = e.target;
1428
+ if (isExtends) {
1429
+ setFormData((prev) => ({
1430
+ ...prev,
1431
+ extents: {
1432
+ ...prev.extends || {},
1433
+ [name]: value
1434
+ }
1435
+ }));
1436
+ } else {
1437
+ setFormData((prev) => ({
1438
+ ...prev,
1439
+ [name]: value
1440
+ }));
1441
+ }
1442
+ setSubmitStatus({});
1443
+ if (errors[name]) {
1444
+ setErrors((prev) => ({
1445
+ ...prev,
1446
+ [name]: void 0
1447
+ }));
1448
+ }
1449
+ };
1450
+ const handleFileChange = (fileInfo) => {
1451
+ setFormData((prev) => ({
1452
+ ...prev,
1453
+ attachments: fileInfo ? [fileInfo] : []
1454
+ // 存储完整的文件信息
1455
+ }));
1456
+ };
1457
+ const handleFileUploadStateChange = (uploading) => {
1458
+ setIsUploading(uploading);
1459
+ };
1460
+ const validateForm = () => {
1461
+ var _a, _b;
1462
+ const newErrors = {};
1463
+ if (!((_a = formData.email) == null ? void 0 : _a.trim())) {
1464
+ newErrors.email = "Please enter your email";
1465
+ } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
1466
+ newErrors.email = "Please enter a valid email address";
1467
+ }
1468
+ if (!((_b = formData.message) == null ? void 0 : _b.trim())) {
1469
+ newErrors.message = "Please enter your message";
1470
+ }
1471
+ if (formData.phone) {
1472
+ console.log("Honeypot triggered - likely spam submission");
1473
+ return false;
1474
+ }
1475
+ setErrors(newErrors);
1476
+ return Object.keys(newErrors).length === 0;
1477
+ };
1478
+ const handleClick = async (event) => {
1479
+ event.preventDefault();
1480
+ if (!validateForm()) {
1481
+ return;
1482
+ }
1483
+ try {
1484
+ setSubmitting(true);
1485
+ setSubmitStatus({});
1486
+ const response = await fetch(actionUrl, {
1487
+ method: "POST",
1488
+ body: JSON.stringify({
1489
+ ...formData,
1490
+ //TODO:后面改成从window 或者传入
1491
+ fromCta: fromCta || lastCta,
1492
+ encryptedField: encrypt(formData.phone, formSalt)
1493
+ }),
1494
+ headers: {
1495
+ "X-Request-URL": window.location.href,
1496
+ "Content-Type": "application/json"
1497
+ }
1498
+ });
1499
+ if (!response.ok) {
1500
+ throw new Error(`Server responded with status: ${response.status}`);
1501
+ }
1502
+ const result = await response.json();
1503
+ setSubmitStatus({
1504
+ success: result.success,
1505
+ message: result.message
1506
+ });
1507
+ if (result.success) {
1508
+ setFormData({
1509
+ name: "",
1510
+ email: "",
1511
+ company: "",
1512
+ message: "",
1513
+ phone: "",
1514
+ attachments: []
1515
+ });
1516
+ window.location.href = "/thanks";
1517
+ }
1518
+ } catch (error) {
1519
+ console.error("Form submission error:", error);
1520
+ setSubmitStatus({
1521
+ success: false,
1522
+ message: error instanceof Error ? `Error: ${error.message}` : "Failed to submit the form. Please try again later."
1523
+ });
1524
+ } finally {
1525
+ setSubmitting(false);
1526
+ }
1527
+ };
1528
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
1529
+ "div",
1530
+ {
1531
+ ref,
1532
+ className: clsx(
1533
+ "py-4 grid max-w-2xl grid-cols-1 gap-x-6 sm:grid-cols-2 w-full",
1534
+ className || "gap-y-6"
1535
+ ),
1536
+ ...rest,
1537
+ children: [
1538
+ fields.map((field, index) => {
1539
+ const {
1540
+ controlName,
1541
+ feildStyle,
1542
+ isExtends,
1543
+ name,
1544
+ className: className2,
1545
+ inputClassName,
1546
+ labelClassName,
1547
+ ...rest2
1548
+ } = field;
1549
+ if (controlName === "FileUpload") {
1550
+ const FileUploadControl = getFileUpload(feildStyle);
1551
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
1552
+ FileUploadControl,
1553
+ {
1554
+ name: name || "attachments",
1555
+ formSalt,
1556
+ className: clsx(classNames == null ? void 0 : classNames.inputContainer, className2),
1557
+ inputClassName: clsx(classNames == null ? void 0 : classNames.input, inputClassName),
1558
+ labelClassName: clsx(classNames == null ? void 0 : classNames.label, labelClassName),
1559
+ maxSize: 3 * 1024 * 1024,
1560
+ onChange: handleFileChange,
1561
+ onUploadStateChange: handleFileUploadStateChange,
1562
+ placeholder: "Click to select file",
1563
+ uploadingText: "Uploading...",
1564
+ selectedText: "Selected: {name}",
1565
+ accept: "*/*",
1566
+ ...rest2
1567
+ },
1568
+ name || index
1569
+ );
1570
+ }
1571
+ const Control = getControl(controlName, feildStyle);
1572
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
1573
+ Control,
1574
+ {
1575
+ name,
1576
+ className: clsx(classNames == null ? void 0 : classNames.inputContainer, className2),
1577
+ labelClassName: clsx(classNames == null ? void 0 : classNames.label, labelClassName),
1578
+ inputClassName: clsx(classNames == null ? void 0 : classNames.input, inputClassName),
1579
+ requiredClassName: classNames == null ? void 0 : classNames.required,
1580
+ value: formData[name],
1581
+ onChange: (e) => handleChange(e, isExtends),
1582
+ error: errors[name],
1583
+ ...rest2
1584
+ },
1585
+ name || index
1586
+ );
1587
+ }),
1588
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { display: "none" }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1589
+ "input",
1590
+ {
1591
+ type: "text",
1592
+ name: "phone",
1593
+ value: formData.phone,
1594
+ onChange: handleChange,
1595
+ tabIndex: -1,
1596
+ autoComplete: "off"
1597
+ }
1598
+ ) }),
1599
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
1600
+ "div",
1601
+ {
1602
+ className: clsx(
1603
+ "col-span-full flex items-center gap-x-6 px-0 py-2",
1604
+ submit == null ? void 0 : submit.containerClassName
1605
+ ),
1606
+ children: [
1607
+ submitStatus.message && !submitStatus.success && /* @__PURE__ */ jsxRuntimeExports.jsx(
1608
+ "div",
1609
+ {
1610
+ className: `text-sm ${submitStatus.success ? "text-green-600" : "text-red-600"}`,
1611
+ children: submitStatus.message
1612
+ }
1613
+ ),
1614
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1615
+ Submit,
1616
+ {
1617
+ className: clsx(
1618
+ "flex gap-2 items-center relative shadow-sm btn btn-lg nowrap",
1619
+ (submit == null ? void 0 : submit.className) || "btn-primary"
1620
+ ),
1621
+ title: (submit == null ? void 0 : submit.title) || "Send Message",
1622
+ spinner: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "left-8 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "animate-spin rounded-full h-5 w-5 border-t-2 border-b-2 border-white" }) }),
1623
+ rawHtml: submit == null ? void 0 : submit.rawHtml,
1624
+ needClasses: submit == null ? void 0 : submit.needClasses,
1625
+ submitting,
1626
+ disabled: submitting || isUploading,
1627
+ onClick: handleClick
1628
+ }
1629
+ )
1630
+ ]
1631
+ }
1632
+ )
1633
+ ]
1634
+ }
1635
+ );
1636
+ }
1637
+ );
1638
+ export {
1639
+ ContactForm,
1640
+ FileUpload2,
1641
+ Input,
1642
+ Submit,
1643
+ Textarea,
1644
+ encrypt,
1645
+ verifyEncryption
1646
+ };
1647
+ //# sourceMappingURL=forms.mjs.map