@rxdrag/website-lib-react 0.0.4 → 0.0.7

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 (99) hide show
  1. package/dist/ReactModalTrigger-9207e763.js +26 -0
  2. package/dist/ReactModalTrigger-9207e763.js.map +1 -0
  3. package/dist/components/ContactForm/ContactForm.d.ts +2 -1
  4. package/dist/components/Icon/index.d.ts +2 -1
  5. package/dist/components/RichTextOutline/parseOutline.d.ts +5 -0
  6. package/dist/components/all.d.ts +0 -21
  7. package/dist/components/index.d.ts +0 -5
  8. package/dist/forms.d.ts +1 -0
  9. package/dist/forms.mjs +1649 -0
  10. package/dist/forms.mjs.map +1 -0
  11. package/dist/index.mjs +9 -3918
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/jsx-runtime-c02cc059.js +325 -0
  14. package/dist/jsx-runtime-c02cc059.js.map +1 -0
  15. package/dist/media.d.ts +1 -0
  16. package/dist/media.mjs +613 -0
  17. package/dist/media.mjs.map +1 -0
  18. package/dist/richtext.d.ts +1 -0
  19. package/dist/richtext.mjs +191 -0
  20. package/dist/richtext.mjs.map +1 -0
  21. package/dist/ui.d.ts +10 -0
  22. package/dist/ui.mjs +687 -0
  23. package/dist/ui.mjs.map +1 -0
  24. package/dist/video.d.ts +2 -0
  25. package/dist/video.mjs +426 -0
  26. package/dist/video.mjs.map +1 -0
  27. package/forms.ts +1 -0
  28. package/index.ts +1 -0
  29. package/media.ts +1 -0
  30. package/package.json +40 -5
  31. package/richtext.ts +1 -0
  32. package/src/components/Analytics/eventHandlers.ts +173 -0
  33. package/src/components/Analytics/index.tsx +21 -0
  34. package/src/components/Analytics/singleton.ts +214 -0
  35. package/src/components/Analytics/tracking.ts +221 -0
  36. package/src/components/Analytics/types.ts +60 -0
  37. package/src/components/Analytics/utils.ts +95 -0
  38. package/src/components/AttachmentIcon/index.tsx +53 -0
  39. package/src/components/BackgroundHlsVideoPlayer.tsx +97 -0
  40. package/src/components/BackgroundVideoPlayer.tsx +32 -0
  41. package/src/components/Bulletin.tsx +30 -0
  42. package/src/components/ContactForm/ContactForm.tsx +296 -0
  43. package/src/components/ContactForm/FileUpload2.tsx +423 -0
  44. package/src/components/ContactForm/Input.tsx +48 -0
  45. package/src/components/ContactForm/Input2.tsx +59 -0
  46. package/src/components/ContactForm/Submit.tsx +48 -0
  47. package/src/components/ContactForm/TelInput.tsx +215 -0
  48. package/src/components/ContactForm/TelInput2.tsx +213 -0
  49. package/src/components/ContactForm/Textarea.tsx +48 -0
  50. package/src/components/ContactForm/Textarea2.tsx +89 -0
  51. package/src/components/ContactForm/countryDialCodes.ts +243 -0
  52. package/src/components/ContactForm/factory.tsx +60 -0
  53. package/src/components/ContactForm/funcs.ts +64 -0
  54. package/src/components/ContactForm/hooks/useInlineLabelPadding.ts +43 -0
  55. package/src/components/ContactForm/hooks/useTelControl.ts +81 -0
  56. package/src/components/ContactForm/index.ts +7 -0
  57. package/src/components/ContactForm/types.ts +68 -0
  58. package/src/components/Icon/index.tsx +20 -0
  59. package/src/components/Medias/MainMedia.tsx +257 -0
  60. package/src/components/Medias/Thumbnail.tsx +62 -0
  61. package/src/components/Medias/VideoPlayer.tsx +114 -0
  62. package/src/components/Medias/index.tsx +271 -0
  63. package/src/components/ProductCard/ProductCard.tsx +24 -0
  64. package/src/components/ProductCard/ProductCta/index.tsx +28 -0
  65. package/src/components/ProductCard/ProductCta/style.css +4 -0
  66. package/src/components/ProductCard/ProductDescription/index.tsx +13 -0
  67. package/src/components/ProductCard/ProductDescription/style.css +6 -0
  68. package/src/components/ProductCard/ProductMedia/index.tsx +35 -0
  69. package/src/components/ProductCard/ProductMedia/style.css +6 -0
  70. package/src/components/ProductCard/ProductTitle/index.tsx +7 -0
  71. package/src/components/ProductCard/ProductTitle/style.css +4 -0
  72. package/src/components/ProductCard/ProductView.tsx +36 -0
  73. package/src/components/ProductCard/index.ts +5 -0
  74. package/src/components/ProductCard/useQueryProduct.ts +32 -0
  75. package/src/components/ReactModalTrigger.tsx +28 -0
  76. package/src/components/ReactVideoPlayer.tsx +332 -0
  77. package/src/components/RichTextOutline/index.tsx +74 -0
  78. package/src/components/RichTextOutline/parseOutline.ts +63 -0
  79. package/src/components/RichTextOutline/useAcitviedHeading.ts +142 -0
  80. package/src/components/RichTextOutline/useAnchorScroll.ts +24 -0
  81. package/src/components/Scroller.tsx +39 -0
  82. package/src/components/SearchInput.tsx +21 -0
  83. package/src/components/Share/index.tsx +86 -0
  84. package/src/components/Share/socials.tsx +80 -0
  85. package/src/components/Share//350/265/204/346/226/231.md +7 -0
  86. package/src/components/ToTop.tsx +72 -0
  87. package/src/components/VideoPlayIcon.tsx +43 -0
  88. package/src/components/all.ts +25 -0
  89. package/src/components/index.ts +12 -0
  90. package/src/forms.ts +1 -0
  91. package/src/index.ts +1 -0
  92. package/src/media.ts +1 -0
  93. package/src/richtext.ts +1 -0
  94. package/src/types/view-model.ts +37 -0
  95. package/src/ui.ts +10 -0
  96. package/src/video.ts +2 -0
  97. package/ui.ts +1 -0
  98. package/video.ts +1 -0
  99. package/dist/style.css +0 -17
@@ -0,0 +1,215 @@
1
+ import clsx from "clsx";
2
+ import { forwardRef, useEffect, useMemo, useRef, useState } from "react";
3
+ import { CountryDial } from "./countryDialCodes";
4
+ import { useTelControl } from "./hooks/useTelControl";
5
+
6
+ export interface TelInputProps
7
+ extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "type"> {
8
+ label?: string;
9
+ name: string;
10
+ required?: boolean;
11
+ requiredClassName?: string;
12
+ className?: string;
13
+ labelClassName?: string;
14
+ inputClassName?: string;
15
+ countries?: CountryDial[];
16
+ }
17
+
18
+ export const TelInput = forwardRef<HTMLInputElement, TelInputProps>(
19
+ (props, ref) => {
20
+ const {
21
+ label,
22
+ name,
23
+ required,
24
+ requiredClassName,
25
+ className,
26
+ labelClassName,
27
+ inputClassName,
28
+ countries,
29
+ placeholder,
30
+ value,
31
+ onChange,
32
+ } = props;
33
+
34
+ const { allCountries, selected, localNumber, onSelectCountry, onChangeLocal } =
35
+ useTelControl({ countries, name, value, onChange });
36
+
37
+ const [open, setOpen] = useState(false);
38
+ const [query, setQuery] = useState("");
39
+ const panelRef = useRef<HTMLDivElement | null>(null);
40
+ const buttonRef = useRef<HTMLButtonElement | null>(null);
41
+
42
+ useEffect(() => {
43
+ const onDocClick = (e: MouseEvent) => {
44
+ const target = e.target as Node;
45
+ if (
46
+ open &&
47
+ panelRef.current &&
48
+ !panelRef.current.contains(target) &&
49
+ buttonRef.current &&
50
+ !buttonRef.current.contains(target)
51
+ ) {
52
+ setOpen(false);
53
+ }
54
+ };
55
+ document.addEventListener("mousedown", onDocClick);
56
+ return () => document.removeEventListener("mousedown", onDocClick);
57
+ }, [open]);
58
+
59
+ // 使用 useTelControl 负责解析与变更回传
60
+
61
+ const filtered: CountryDial[] = useMemo(() => {
62
+ if (!query) return allCountries;
63
+ const q = query.toLowerCase();
64
+ return allCountries.filter(
65
+ (c: CountryDial) =>
66
+ c.name.toLowerCase().includes(q) ||
67
+ (c.region && c.region.toLowerCase().includes(q)) ||
68
+ c.dialCode.replace("+", "").includes(q.replace("+", ""))
69
+ );
70
+ }, [allCountries, query]);
71
+
72
+ return (
73
+ <div className={clsx("w-full", className)}>
74
+ {label ? (
75
+ <label
76
+ htmlFor={name}
77
+ className={clsx(
78
+ "mb-1 inline-flex items-center text-sm text-gray-600",
79
+ labelClassName
80
+ )}
81
+ >
82
+ {label}
83
+ {required ? <span className={requiredClassName}>*</span> : null}
84
+ </label>
85
+ ) : null}
86
+
87
+ <div
88
+ className={clsx(
89
+ "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",
90
+ inputClassName
91
+ )}
92
+ >
93
+ <div className="relative h-full">
94
+ <button
95
+ ref={buttonRef}
96
+ type="button"
97
+ aria-haspopup="listbox"
98
+ aria-expanded={open}
99
+ onClick={() => setOpen((v) => !v)}
100
+ className={
101
+ "h-full min-w-[200px] inline-flex items-center justify-between whitespace-nowrap rounded-md px-3 py-0 text-sm"
102
+ }
103
+ >
104
+ <span className="truncate text-left leading-tight">
105
+ {selected ? (
106
+ <>
107
+ <span className="font-medium">{selected.name}</span>
108
+ {selected.region ? (
109
+ <span className="text-gray-500">, {selected.region}</span>
110
+ ) : null}
111
+ <span className="ml-1 text-gray-700">
112
+ {selected.dialCode}
113
+ </span>
114
+ </>
115
+ ) : (
116
+ "Select Country/Region"
117
+ )}
118
+ </span>
119
+ <svg
120
+ className="ml-2 h-4 w-4 text-gray-500"
121
+ viewBox="0 0 20 20"
122
+ fill="currentColor"
123
+ aria-hidden="true"
124
+ >
125
+ <path
126
+ fillRule="evenodd"
127
+ 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"
128
+ clipRule="evenodd"
129
+ />
130
+ </svg>
131
+ </button>
132
+
133
+ {open ? (
134
+ <div
135
+ ref={panelRef}
136
+ className="absolute z-50 mt-2 w-[320px] rounded-md border border-gray-200 bg-white p-2 shadow-lg"
137
+ >
138
+ <div className="flex items-center gap-2 rounded-md border border-gray-200 px-2 py-1.5">
139
+ <input
140
+ value={query}
141
+ onChange={(e) => setQuery(e.target.value)}
142
+ placeholder="Enter Country/Region"
143
+ className="h-8 w-full bg-transparent text-sm outline-none"
144
+ />
145
+ <svg
146
+ className="h-4 w-4 text-gray-500"
147
+ viewBox="0 0 20 20"
148
+ fill="currentColor"
149
+ aria-hidden="true"
150
+ >
151
+ <path
152
+ fillRule="evenodd"
153
+ 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"
154
+ clipRule="evenodd"
155
+ />
156
+ </svg>
157
+ </div>
158
+
159
+ <div className="mt-2 max-h-64 overflow-auto pr-1">
160
+ {filtered.map((c: CountryDial) => (
161
+ <button
162
+ key={c.code}
163
+ type="button"
164
+ onClick={() => {
165
+ onSelectCountry(c);
166
+ setOpen(false);
167
+ }}
168
+ className="flex w-full items-center gap-2 rounded-md px-2 py-2 text-left text-sm hover:bg-gray-50"
169
+ >
170
+ <span
171
+ className={clsx(
172
+ "inline-block h-3 w-3 rounded-full border",
173
+ selected?.code === c.code
174
+ ? "border-primary-500 ring-4 ring-primary-200"
175
+ : "border-gray-400"
176
+ )}
177
+ ></span>
178
+ <span className="flex-1 truncate">
179
+ {c.name}
180
+ {c.region ? (
181
+ <span className="text-gray-500">, {c.region}</span>
182
+ ) : null}
183
+ </span>
184
+ <span className="text-gray-700">{c.dialCode}</span>
185
+ </button>
186
+ ))}
187
+ </div>
188
+ </div>
189
+ ) : null}
190
+ </div>
191
+
192
+ <input
193
+ ref={ref}
194
+ id={name}
195
+ name={name}
196
+ required={required}
197
+ placeholder={placeholder || "Phone number"}
198
+ inputMode="tel"
199
+ className={clsx("h-10 w-full px-3 py-0 text-sm leading-tight ring-0 outline-none rounded-md")}
200
+ value={localNumber}
201
+ onChange={(e) => {
202
+ const next = e.target.value;
203
+ onChangeLocal(next);
204
+ }}
205
+ autoComplete={name}
206
+ autoCorrect="off"
207
+ spellCheck={false}
208
+ />
209
+ </div>
210
+ </div>
211
+ );
212
+ }
213
+ );
214
+
215
+ export default TelInput;
@@ -0,0 +1,213 @@
1
+ import clsx from "clsx";
2
+ import { forwardRef, useEffect, useMemo, useRef, useState } from "react";
3
+ import type { CountryDial } from "./countryDialCodes";
4
+ import { useInlineLabelPadding } from "./hooks/useInlineLabelPadding";
5
+ import { useTelControl } from "./hooks/useTelControl";
6
+
7
+ export interface TelInputProps
8
+ extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "type"> {
9
+ label?: string;
10
+ name: string;
11
+ required?: boolean;
12
+ requiredClassName?: string;
13
+ className?: string;
14
+ labelClassName?: string;
15
+ inputClassName?: string;
16
+ countries?: CountryDial[];
17
+ selectPlaceholder?: string;
18
+ }
19
+
20
+ export const TelInput2 = forwardRef<HTMLInputElement, TelInputProps>(
21
+ (props, ref) => {
22
+ const {
23
+ label,
24
+ name,
25
+ required,
26
+ requiredClassName,
27
+ className,
28
+ labelClassName,
29
+ inputClassName,
30
+ countries,
31
+ placeholder,
32
+ selectPlaceholder,
33
+ value,
34
+ onChange,
35
+ } = props;
36
+
37
+ const { labelRef, paddingLeft } = useInlineLabelPadding(16);
38
+ const {
39
+ allCountries,
40
+ selected,
41
+ localNumber,
42
+ onSelectCountry,
43
+ onChangeLocal,
44
+ } = useTelControl({ countries, name, value, onChange });
45
+
46
+ const [open, setOpen] = useState(false);
47
+ const [query, setQuery] = useState("");
48
+ const panelRef = useRef<HTMLDivElement | null>(null);
49
+ const buttonRef = useRef<HTMLButtonElement | null>(null);
50
+
51
+ const filtered = useMemo(() => {
52
+ if (!query) return allCountries;
53
+ const q = query.toLowerCase();
54
+ return allCountries.filter(
55
+ (c) =>
56
+ c.name.toLowerCase().includes(q) ||
57
+ (c.region && c.region.toLowerCase().includes(q)) ||
58
+ c.dialCode.replace("+", "").includes(q.replace("+", ""))
59
+ );
60
+ }, [allCountries, query]);
61
+
62
+ useEffect(() => {
63
+ const onDocClick = (e: MouseEvent) => {
64
+ const target = e.target as Node;
65
+ if (
66
+ open &&
67
+ panelRef.current &&
68
+ !panelRef.current.contains(target) &&
69
+ buttonRef.current &&
70
+ !buttonRef.current.contains(target)
71
+ ) {
72
+ setOpen(false);
73
+ }
74
+ };
75
+ document.addEventListener("mousedown", onDocClick);
76
+ return () => document.removeEventListener("mousedown", onDocClick);
77
+ }, [open]);
78
+
79
+ return (
80
+ <div className={clsx("relative w-full", className)}>
81
+ <label
82
+ ref={labelRef}
83
+ htmlFor={name}
84
+ className={clsx(
85
+ "absolute left-3 top-1/2 -translate-y-1/2 mt-1 opacity-70 text-sm pointer-events-none whitespace-nowrap",
86
+ labelClassName
87
+ )}
88
+ >
89
+ {label}
90
+ {required ? <span className={requiredClassName}>*</span> : null}
91
+ </label>
92
+
93
+ <div
94
+ className={clsx(
95
+ "flex h-10 items-center gap-2 rounded-md shadow-sm focus-within:ring-2 focus-within:ring-primary-300",
96
+ inputClassName
97
+ )}
98
+ style={{ paddingLeft }}
99
+ >
100
+ <div className="relative h-full">
101
+ <button
102
+ ref={buttonRef}
103
+ type="button"
104
+ aria-haspopup="listbox"
105
+ aria-expanded={open}
106
+ onClick={() => setOpen((v) => !v)}
107
+ className={
108
+ "h-full min-w-[100px] inline-flex items-center justify-between whitespace-nowrap rounded-md px-3 py-0 text-sm"
109
+ }
110
+ >
111
+ <span className="truncate text-left leading-tight">
112
+ {selected ? (
113
+ <span className="font-medium">{selected.dialCode}</span>
114
+ ) : (
115
+ <span className="text-gray-500">+ Code</span>
116
+ )}
117
+ </span>
118
+ <svg
119
+ className="ml-2 h-4 w-4 text-gray-500"
120
+ viewBox="0 0 20 20"
121
+ fill="currentColor"
122
+ aria-hidden="true"
123
+ >
124
+ <path
125
+ fillRule="evenodd"
126
+ 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"
127
+ clipRule="evenodd"
128
+ />
129
+ </svg>
130
+ </button>
131
+
132
+ {open ? (
133
+ <div
134
+ ref={panelRef}
135
+ className="absolute z-50 mt-2 w-[320px] rounded-md border border-gray-200 bg-white p-2 shadow-lg"
136
+ >
137
+ <div className="flex items-center gap-2 rounded-md border border-gray-200 px-2 py-1.5">
138
+ <input
139
+ value={query}
140
+ onChange={(e) => setQuery(e.target.value)}
141
+ placeholder={selectPlaceholder || "Enter Country/Region"}
142
+ className="h-8 w-full bg-transparent text-sm outline-none"
143
+ />
144
+ <svg
145
+ className="h-4 w-4 text-gray-500"
146
+ viewBox="0 0 20 20"
147
+ fill="currentColor"
148
+ aria-hidden="true"
149
+ >
150
+ <path
151
+ fillRule="evenodd"
152
+ 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"
153
+ clipRule="evenodd"
154
+ />
155
+ </svg>
156
+ </div>
157
+
158
+ <div className="mt-2 max-h-64 overflow-auto pr-1">
159
+ {filtered.map((c) => (
160
+ <button
161
+ key={c.code}
162
+ type="button"
163
+ onClick={() => {
164
+ onSelectCountry(c);
165
+ setOpen(false);
166
+ }}
167
+ className="flex w-full items-center gap-2 rounded-md px-2 py-2 text-left text-sm hover:bg-gray-50"
168
+ >
169
+ <span
170
+ className={clsx(
171
+ "inline-block h-3 w-3 rounded-full border",
172
+ selected?.code === c.code
173
+ ? "border-primary-500 ring-4 ring-primary-200"
174
+ : "border-gray-400"
175
+ )}
176
+ ></span>
177
+ <span className="flex-1 truncate">
178
+ {c.name}
179
+ {c.region ? (
180
+ <span className="text-gray-500">, {c.region}</span>
181
+ ) : null}
182
+ </span>
183
+ <span className="text-gray-700">{c.dialCode}</span>
184
+ </button>
185
+ ))}
186
+ </div>
187
+ </div>
188
+ ) : null}
189
+ </div>
190
+
191
+ <input
192
+ ref={ref}
193
+ id={name}
194
+ name={name}
195
+ required={required}
196
+ placeholder={placeholder || "Phone number"}
197
+ inputMode="tel"
198
+ className={clsx(
199
+ "h-10 w-full appearance-none rounded-md border-0 bg-transparent px-3 py-0 text-sm leading-tight focus:outline-none"
200
+ )}
201
+ value={localNumber}
202
+ onChange={(e) => onChangeLocal(e.target.value)}
203
+ autoComplete={name}
204
+ autoCorrect="off"
205
+ spellCheck={false}
206
+ />
207
+ </div>
208
+ </div>
209
+ );
210
+ }
211
+ );
212
+
213
+ export default TelInput2;
@@ -0,0 +1,48 @@
1
+ import { forwardRef } from "react";
2
+ import { FieldConfig } from "./types";
3
+
4
+ export type TextareaProps = Omit<FieldConfig, "feildStyle"> & {
5
+ requiredClassName?: string;
6
+ value?: string;
7
+ onChange?: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
8
+ error?: string;
9
+ };
10
+
11
+ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
12
+ (props, ref) => {
13
+ const {
14
+ label,
15
+ name,
16
+ required,
17
+ requiredClassName,
18
+ labelClassName,
19
+ inputClassName,
20
+ rows = 8,
21
+ value,
22
+ onChange,
23
+ error,
24
+ ...rest
25
+ } = props;
26
+
27
+ return (
28
+ <div {...rest}>
29
+ <label htmlFor={name} className={labelClassName}>
30
+ {label}
31
+ {required ? <span className={requiredClassName}>*</span> : ""}
32
+ </label>
33
+ <textarea
34
+ ref={ref}
35
+ id={name}
36
+ name={name}
37
+ required={required}
38
+ className={inputClassName}
39
+ rows={rows}
40
+ value={value}
41
+ onChange={onChange}
42
+ autoComplete={name}
43
+ />
44
+ {error && <p className="text-red-500 mt-1">{error}</p>}
45
+ </div>
46
+ );
47
+ }
48
+ );
@@ -0,0 +1,89 @@
1
+ import { forwardRef, useRef, useEffect, useState } from "react";
2
+ import { TextareaProps } from "./Textarea";
3
+
4
+ export const Textarea2 = forwardRef<HTMLTextAreaElement, TextareaProps>(
5
+ (props, ref) => {
6
+ const {
7
+ label,
8
+ name,
9
+ required,
10
+ requiredClassName,
11
+ labelClassName,
12
+ inputClassName,
13
+ className,
14
+ rows = 6,
15
+ value,
16
+ onChange,
17
+ error,
18
+ ...rest
19
+ } = props;
20
+
21
+ const [paddingTop, setPaddingTop] = useState("2.5rem");
22
+ const labelRef = useRef<HTMLLabelElement>(null);
23
+
24
+ useEffect(() => {
25
+ const labelEl = labelRef.current;
26
+ if (!labelEl) return;
27
+
28
+ let rafId = 0;
29
+ const measure = () => {
30
+ const height = labelEl.getBoundingClientRect().height;
31
+ setPaddingTop(`${height + 12}px`);
32
+ };
33
+
34
+ measure();
35
+ rafId = requestAnimationFrame(measure);
36
+
37
+ if (typeof ResizeObserver === "undefined") {
38
+ return () => {
39
+ cancelAnimationFrame(rafId);
40
+ };
41
+ }
42
+
43
+ const ro = new ResizeObserver(() => {
44
+ measure();
45
+ });
46
+ ro.observe(labelEl);
47
+
48
+ return () => {
49
+ cancelAnimationFrame(rafId);
50
+ ro.disconnect();
51
+ };
52
+ }, [label]);
53
+
54
+ return (
55
+ <div className={className} {...rest}>
56
+ <div className="relative w-full">
57
+ <label
58
+ ref={labelRef}
59
+ htmlFor={name}
60
+ className={`absolute left-3 top-2 opacity-70 text-sm pointer-events-none ${
61
+ labelClassName || ""
62
+ }`}
63
+ >
64
+ {label}
65
+ {required ? <span className={requiredClassName}>*</span> : ""}
66
+ </label>
67
+ <textarea
68
+ ref={ref}
69
+ id={name}
70
+ name={name}
71
+ required={required}
72
+ rows={rows}
73
+ value={value}
74
+ onChange={onChange}
75
+ autoComplete={name}
76
+ style={{ paddingTop }}
77
+ className={`w-full px-3 pb-2 border border-gray-300 rounded-md shadow-sm focus:outline-none
78
+ focus:ring-2 focus:ring-primary-300 focus:border-transparent resize-y bg-transparent focus:bg-white transition-colors duration-200 ${
79
+ inputClassName || ""
80
+ }`}
81
+ />
82
+ </div>
83
+ {error && <p className="text-red-500 mt-1 text-sm">{error}</p>}
84
+ </div>
85
+ );
86
+ }
87
+ );
88
+
89
+ Textarea2.displayName = "Textarea2";