@mieweb/ui 0.11.0-dev.27 → 0.11.0-dev.29

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 (63) hide show
  1. package/MIGRATION.md +49 -0
  2. package/dist/chunk-2VISTX7O.js +802 -0
  3. package/dist/chunk-2VISTX7O.js.map +1 -0
  4. package/dist/{chunk-UWHVSRSU.js → chunk-43QBEF6N.js} +51 -18
  5. package/dist/chunk-43QBEF6N.js.map +1 -0
  6. package/dist/{chunk-FA7STF2M.cjs → chunk-6GKIISNZ.cjs} +51 -18
  7. package/dist/chunk-6GKIISNZ.cjs.map +1 -0
  8. package/dist/{chunk-IEZXJWHJ.cjs → chunk-7XKM6V2K.cjs} +3 -3
  9. package/dist/{chunk-IEZXJWHJ.cjs.map → chunk-7XKM6V2K.cjs.map} +1 -1
  10. package/dist/{chunk-STDGAFW2.js → chunk-AK4LHJPC.js} +6 -3
  11. package/dist/chunk-AK4LHJPC.js.map +1 -0
  12. package/dist/chunk-AQUHEKXL.cjs +829 -0
  13. package/dist/chunk-AQUHEKXL.cjs.map +1 -0
  14. package/dist/{chunk-GTAHQBUK.js → chunk-MBDT2MGR.js} +4 -4
  15. package/dist/{chunk-GTAHQBUK.js.map → chunk-MBDT2MGR.js.map} +1 -1
  16. package/dist/{chunk-DXFP53KF.js → chunk-NIO3XVUV.js} +3 -3
  17. package/dist/{chunk-DXFP53KF.js.map → chunk-NIO3XVUV.js.map} +1 -1
  18. package/dist/{chunk-S2QKRL5S.cjs → chunk-PRA2V5BP.cjs} +7 -7
  19. package/dist/{chunk-S2QKRL5S.cjs.map → chunk-PRA2V5BP.cjs.map} +1 -1
  20. package/dist/{chunk-3E7C5N2G.js → chunk-SFSBYC46.js} +3 -3
  21. package/dist/{chunk-3E7C5N2G.js.map → chunk-SFSBYC46.js.map} +1 -1
  22. package/dist/{chunk-ZWKC4JML.cjs → chunk-ZIJMLRI3.cjs} +9 -9
  23. package/dist/{chunk-ZWKC4JML.cjs.map → chunk-ZIJMLRI3.cjs.map} +1 -1
  24. package/dist/{chunk-7S3GBPRD.cjs → chunk-ZLBNYCYT.cjs} +6 -3
  25. package/dist/chunk-ZLBNYCYT.cjs.map +1 -0
  26. package/dist/components/EmailEditor/index.cjs +16 -16
  27. package/dist/components/EmailEditor/index.js +7 -7
  28. package/dist/components/RecordHeader/index.cjs +4 -4
  29. package/dist/components/RecordHeader/index.js +2 -2
  30. package/dist/components/RichTextEditor/index.cjs +5 -5
  31. package/dist/components/RichTextEditor/index.d.cts +21 -1
  32. package/dist/components/RichTextEditor/index.d.ts +21 -1
  33. package/dist/components/RichTextEditor/index.js +2 -2
  34. package/dist/components/SuperChat/index.cjs +2 -2
  35. package/dist/components/SuperChat/index.js +2 -2
  36. package/dist/components/Textarea/index.cjs +22 -4
  37. package/dist/components/Textarea/index.d.cts +40 -14
  38. package/dist/components/Textarea/index.d.ts +40 -14
  39. package/dist/components/Textarea/index.js +15 -1
  40. package/dist/esheet.cjs +10 -5
  41. package/dist/esheet.cjs.map +1 -1
  42. package/dist/esheet.js +7 -2
  43. package/dist/esheet.js.map +1 -1
  44. package/dist/index.cjs +147 -150
  45. package/dist/index.cjs.map +1 -1
  46. package/dist/index.d.cts +117 -56
  47. package/dist/index.d.ts +117 -56
  48. package/dist/index.js +14 -37
  49. package/dist/index.js.map +1 -1
  50. package/dist/styles.css +1 -1
  51. package/dist/tailwind-preset.cjs +4 -4
  52. package/dist/tailwind-preset.js +1 -1
  53. package/dist/utils/index.cjs +9 -9
  54. package/dist/utils/index.js +1 -1
  55. package/package.json +1 -1
  56. package/dist/chunk-7S3GBPRD.cjs.map +0 -1
  57. package/dist/chunk-BUN6RUJ5.cjs +0 -393
  58. package/dist/chunk-BUN6RUJ5.cjs.map +0 -1
  59. package/dist/chunk-CPG7SDON.js +0 -370
  60. package/dist/chunk-CPG7SDON.js.map +0 -1
  61. package/dist/chunk-FA7STF2M.cjs.map +0 -1
  62. package/dist/chunk-STDGAFW2.js.map +0 -1
  63. package/dist/chunk-UWHVSRSU.js.map +0 -1
@@ -0,0 +1,829 @@
1
+ 'use strict';
2
+
3
+ var chunk6GKIISNZ_cjs = require('./chunk-6GKIISNZ.cjs');
4
+ var chunkZ5S5JEJD_cjs = require('./chunk-Z5S5JEJD.cjs');
5
+ var chunkS5RVSXDV_cjs = require('./chunk-S5RVSXDV.cjs');
6
+ var React2 = require('react');
7
+ var jsxRuntime = require('react/jsx-runtime');
8
+ var classVarianceAuthority = require('class-variance-authority');
9
+
10
+ function _interopNamespace(e) {
11
+ if (e && e.__esModule) return e;
12
+ var n = Object.create(null);
13
+ if (e) {
14
+ Object.keys(e).forEach(function (k) {
15
+ if (k !== 'default') {
16
+ var d = Object.getOwnPropertyDescriptor(e, k);
17
+ Object.defineProperty(n, k, d.get ? d : {
18
+ enumerable: true,
19
+ get: function () { return e[k]; }
20
+ });
21
+ }
22
+ });
23
+ }
24
+ n.default = e;
25
+ return Object.freeze(n);
26
+ }
27
+
28
+ var React2__namespace = /*#__PURE__*/_interopNamespace(React2);
29
+
30
+ var TextAreaRich = React2__namespace.forwardRef(
31
+ ({ className, markdownShortcuts = true, defaultToolbar = "hidden", ...rest }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
32
+ chunk6GKIISNZ_cjs.RichTextEditor,
33
+ {
34
+ ref,
35
+ chromeless: true,
36
+ blockMenu: true,
37
+ markdownShortcuts,
38
+ defaultToolbar,
39
+ className: chunkS5RVSXDV_cjs.cn(
40
+ // Textarea's chrome: border, radius, focus ring (on the wrapper via
41
+ // focus-within since the editable sits inside it).
42
+ "border-input rounded-lg border",
43
+ "transition-colors duration-200",
44
+ "focus-within:ring-ring focus-within:border-transparent focus-within:ring-2",
45
+ className
46
+ ),
47
+ ...rest
48
+ }
49
+ )
50
+ );
51
+ TextAreaRich.displayName = "TextAreaRich";
52
+ var textareaVariants = classVarianceAuthority.cva(
53
+ [
54
+ "w-full px-3 py-2",
55
+ "border border-input rounded-lg",
56
+ "bg-background text-foreground",
57
+ "placeholder:text-muted-foreground",
58
+ "transition-colors duration-200",
59
+ "focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent",
60
+ "disabled:cursor-not-allowed disabled:opacity-50",
61
+ "resize-y"
62
+ ],
63
+ {
64
+ variants: {
65
+ size: {
66
+ sm: "text-sm min-h-[60px]",
67
+ md: "text-base min-h-[80px]",
68
+ lg: "text-lg min-h-[100px]"
69
+ },
70
+ hasError: {
71
+ true: "border-destructive focus:ring-destructive",
72
+ false: ""
73
+ },
74
+ resize: {
75
+ none: "resize-none",
76
+ vertical: "resize-y",
77
+ horizontal: "resize-x",
78
+ both: "resize"
79
+ },
80
+ labelVariant: {
81
+ stacked: "",
82
+ floating: "peer placeholder:text-transparent"
83
+ },
84
+ autoResize: {
85
+ true: "",
86
+ false: ""
87
+ }
88
+ },
89
+ compoundVariants: [
90
+ // Floating labels reserve top padding so entered text always starts
91
+ // below the floated label, plus a little extra min-height.
92
+ {
93
+ labelVariant: "floating",
94
+ size: "sm",
95
+ className: "pt-5 min-h-[76px]"
96
+ },
97
+ {
98
+ labelVariant: "floating",
99
+ size: "md",
100
+ className: "pt-6 min-h-[96px]"
101
+ },
102
+ {
103
+ labelVariant: "floating",
104
+ size: "lg",
105
+ className: "pt-7 min-h-[120px]"
106
+ },
107
+ // Auto-growing textareas start at the same visual height as a
108
+ // single-line Input (h-8/h-10/h-12): min-height matches the Input
109
+ // size token and the vertical padding is tuned so one line of text
110
+ // plus padding plus borders lands exactly on that height.
111
+ {
112
+ autoResize: true,
113
+ labelVariant: "stacked",
114
+ size: "sm",
115
+ className: "min-h-8 py-[5px]"
116
+ },
117
+ {
118
+ autoResize: true,
119
+ labelVariant: "stacked",
120
+ size: "md",
121
+ className: "min-h-10 py-[7px]"
122
+ },
123
+ {
124
+ autoResize: true,
125
+ labelVariant: "stacked",
126
+ size: "lg",
127
+ className: "min-h-12 py-[9px]"
128
+ },
129
+ // Floating label + auto-grow: single line below the floated label.
130
+ {
131
+ autoResize: true,
132
+ labelVariant: "floating",
133
+ size: "sm",
134
+ className: "min-h-[50px]"
135
+ },
136
+ {
137
+ autoResize: true,
138
+ labelVariant: "floating",
139
+ size: "md",
140
+ className: "min-h-[58px]"
141
+ },
142
+ {
143
+ autoResize: true,
144
+ labelVariant: "floating",
145
+ size: "lg",
146
+ className: "min-h-[66px]"
147
+ }
148
+ ],
149
+ defaultVariants: {
150
+ size: "md",
151
+ hasError: false,
152
+ resize: "vertical",
153
+ labelVariant: "stacked",
154
+ autoResize: true
155
+ }
156
+ }
157
+ );
158
+ var floatingLabelVariants = classVarianceAuthority.cva(
159
+ [
160
+ "absolute start-3 pointer-events-none select-none",
161
+ "transition-all duration-200 ease-out"
162
+ ],
163
+ {
164
+ variants: {
165
+ size: {
166
+ sm: [
167
+ "text-sm top-3",
168
+ "peer-focus:top-1 peer-[:not(:placeholder-shown)]:top-1 peer-autofill:top-1",
169
+ "peer-focus:text-[0.65rem] peer-[:not(:placeholder-shown)]:text-[0.65rem] peer-autofill:text-[0.65rem]"
170
+ ],
171
+ md: [
172
+ "text-base top-4",
173
+ "peer-focus:top-1.5 peer-[:not(:placeholder-shown)]:top-1.5 peer-autofill:top-1.5",
174
+ "peer-focus:text-xs peer-[:not(:placeholder-shown)]:text-xs peer-autofill:text-xs"
175
+ ],
176
+ lg: [
177
+ "text-lg top-5",
178
+ "peer-focus:top-2 peer-[:not(:placeholder-shown)]:top-2 peer-autofill:top-2",
179
+ "peer-focus:text-sm peer-[:not(:placeholder-shown)]:text-sm peer-autofill:text-sm"
180
+ ]
181
+ },
182
+ hasError: {
183
+ true: "text-destructive",
184
+ false: "text-muted-foreground"
185
+ }
186
+ },
187
+ defaultVariants: {
188
+ size: "md",
189
+ hasError: false
190
+ }
191
+ }
192
+ );
193
+ var TextAreaPlain = React2__namespace.forwardRef(
194
+ ({
195
+ className,
196
+ size,
197
+ hasError,
198
+ resize,
199
+ label,
200
+ labelVariant = "stacked",
201
+ hideLabel,
202
+ error,
203
+ helperText,
204
+ maxLength,
205
+ showCount = false,
206
+ autoResize = true,
207
+ required = false,
208
+ requiredVariant,
209
+ id,
210
+ rows,
211
+ value,
212
+ defaultValue,
213
+ onChange,
214
+ placeholder,
215
+ "aria-describedby": ariaDescribedBy,
216
+ ...props
217
+ }, ref) => {
218
+ const internalRef = React2__namespace.useRef(null);
219
+ const [internalValue, setInternalValue] = React2__namespace.useState(
220
+ defaultValue || ""
221
+ );
222
+ const generatedId = React2__namespace.useId();
223
+ const textareaId = id || generatedId;
224
+ const errorId = `${textareaId}-error`;
225
+ const helperId = `${textareaId}-helper`;
226
+ const countId = `${textareaId}-count`;
227
+ React2__namespace.useImperativeHandle(ref, () => internalRef.current);
228
+ const currentValue = value !== void 0 ? String(value) : internalValue;
229
+ const characterCount = currentValue.length;
230
+ const userFloorRef = React2__namespace.useRef(null);
231
+ const expectedHeightRef = React2__namespace.useRef(null);
232
+ const initialFloorCapturedRef = React2__namespace.useRef(false);
233
+ const adjustHeight = React2__namespace.useCallback(() => {
234
+ const textarea = internalRef.current;
235
+ if (textarea && autoResize) {
236
+ textarea.style.height = "auto";
237
+ const borders = textarea.offsetHeight - textarea.clientHeight;
238
+ const next = Math.max(
239
+ textarea.scrollHeight + borders,
240
+ userFloorRef.current ?? 0
241
+ );
242
+ textarea.style.height = `${next}px`;
243
+ expectedHeightRef.current = textarea.offsetHeight || next;
244
+ }
245
+ }, [autoResize]);
246
+ React2__namespace.useEffect(() => {
247
+ const textarea = internalRef.current;
248
+ if (textarea && autoResize && !initialFloorCapturedRef.current) {
249
+ initialFloorCapturedRef.current = true;
250
+ const configured = textarea.offsetHeight;
251
+ adjustHeight();
252
+ if (configured - textarea.offsetHeight > 1) {
253
+ userFloorRef.current = configured;
254
+ textarea.style.height = `${configured}px`;
255
+ expectedHeightRef.current = textarea.offsetHeight || configured;
256
+ }
257
+ return;
258
+ }
259
+ adjustHeight();
260
+ }, [currentValue, adjustHeight, autoResize]);
261
+ React2__namespace.useEffect(() => {
262
+ const textarea = internalRef.current;
263
+ if (!autoResize || !textarea || typeof ResizeObserver === "undefined") {
264
+ return;
265
+ }
266
+ let lastWidth = textarea.offsetWidth;
267
+ const observer = new ResizeObserver(() => {
268
+ const expected = expectedHeightRef.current;
269
+ const height = textarea.offsetHeight;
270
+ if (expected !== null && Math.abs(height - expected) > 1) {
271
+ userFloorRef.current = height;
272
+ expectedHeightRef.current = height;
273
+ }
274
+ if (textarea.offsetWidth !== lastWidth) {
275
+ lastWidth = textarea.offsetWidth;
276
+ adjustHeight();
277
+ }
278
+ });
279
+ observer.observe(textarea);
280
+ return () => observer.disconnect();
281
+ }, [autoResize, adjustHeight]);
282
+ const handleChange = React2__namespace.useCallback(
283
+ (e) => {
284
+ if (value === void 0) {
285
+ setInternalValue(e.target.value);
286
+ }
287
+ onChange?.(e);
288
+ adjustHeight();
289
+ },
290
+ [value, onChange, adjustHeight]
291
+ );
292
+ const describedByIds = [
293
+ error ? errorId : null,
294
+ helperText && !error ? helperId : null,
295
+ showCount ? countId : null,
296
+ ariaDescribedBy
297
+ ].filter(Boolean).join(" ");
298
+ const isFloating = labelVariant === "floating" && !!label && !hideLabel;
299
+ const requiredMark = required && /* @__PURE__ */ jsxRuntime.jsx(chunkZ5S5JEJD_cjs.RequiredMark, { variant: requiredVariant });
300
+ const textareaElement = /* @__PURE__ */ jsxRuntime.jsx(
301
+ "textarea",
302
+ {
303
+ "data-slot": "textarea",
304
+ "data-auto-resize": autoResize ? "true" : void 0,
305
+ ref: internalRef,
306
+ id: textareaId,
307
+ value,
308
+ defaultValue: value === void 0 ? defaultValue : void 0,
309
+ onChange: handleChange,
310
+ maxLength,
311
+ "aria-invalid": hasError || !!error,
312
+ "aria-describedby": describedByIds || void 0,
313
+ required,
314
+ rows: autoResize ? rows ?? 1 : rows,
315
+ placeholder: isFloating ? " " : placeholder,
316
+ className: chunkS5RVSXDV_cjs.cn(
317
+ textareaVariants({
318
+ size,
319
+ hasError: hasError || !!error,
320
+ resize,
321
+ labelVariant: isFloating ? "floating" : "stacked",
322
+ autoResize
323
+ }),
324
+ className
325
+ ),
326
+ ...props
327
+ }
328
+ );
329
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-slot": "textarea-wrapper", className: "flex flex-col gap-1.5", children: [
330
+ label && !isFloating && /* @__PURE__ */ jsxRuntime.jsxs(
331
+ "label",
332
+ {
333
+ "data-slot": "textarea-label",
334
+ htmlFor: textareaId,
335
+ className: chunkS5RVSXDV_cjs.cn(
336
+ "text-foreground text-sm font-medium",
337
+ hideLabel && "sr-only"
338
+ ),
339
+ children: [
340
+ label,
341
+ requiredMark
342
+ ]
343
+ }
344
+ ),
345
+ isFloating ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
346
+ textareaElement,
347
+ /* @__PURE__ */ jsxRuntime.jsxs(
348
+ "label",
349
+ {
350
+ "data-slot": "textarea-label",
351
+ htmlFor: textareaId,
352
+ className: floatingLabelVariants({
353
+ size,
354
+ hasError: hasError || !!error
355
+ }),
356
+ children: [
357
+ label,
358
+ requiredMark
359
+ ]
360
+ }
361
+ )
362
+ ] }) : textareaElement,
363
+ /* @__PURE__ */ jsxRuntime.jsxs(
364
+ "div",
365
+ {
366
+ "data-slot": "textarea-footer",
367
+ className: "flex items-center justify-between gap-2",
368
+ children: [
369
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
370
+ error && /* @__PURE__ */ jsxRuntime.jsx(
371
+ "p",
372
+ {
373
+ id: errorId,
374
+ "data-slot": "textarea-error",
375
+ className: "text-destructive-700 dark:text-destructive-400 text-sm",
376
+ role: "alert",
377
+ children: error
378
+ }
379
+ ),
380
+ helperText && !error && /* @__PURE__ */ jsxRuntime.jsx(
381
+ "p",
382
+ {
383
+ id: helperId,
384
+ "data-slot": "textarea-helper",
385
+ className: "text-muted-foreground text-sm",
386
+ children: helperText
387
+ }
388
+ )
389
+ ] }),
390
+ showCount && /* @__PURE__ */ jsxRuntime.jsxs(
391
+ "p",
392
+ {
393
+ id: countId,
394
+ "data-slot": "textarea-count",
395
+ className: chunkS5RVSXDV_cjs.cn(
396
+ "text-muted-foreground shrink-0 text-xs",
397
+ maxLength && characterCount >= maxLength && "text-destructive"
398
+ ),
399
+ children: [
400
+ characterCount,
401
+ maxLength && `/${maxLength}`
402
+ ]
403
+ }
404
+ )
405
+ ]
406
+ }
407
+ )
408
+ ] });
409
+ }
410
+ );
411
+ TextAreaPlain.displayName = "TextAreaPlain";
412
+ function escapeHtml(text) {
413
+ return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
414
+ }
415
+ function normalizeNewlines(text) {
416
+ return text.replace(/\r\n?/g, "\n");
417
+ }
418
+ function plainToHtml(text) {
419
+ if (!text) return "";
420
+ return normalizeNewlines(text).split("\n").map((line) => line ? `<p>${escapeHtml(line)}</p>` : "<p><br></p>").join("");
421
+ }
422
+ var BLOCK_TAGS = /* @__PURE__ */ new Set([
423
+ "P",
424
+ "DIV",
425
+ "H1",
426
+ "H2",
427
+ "H3",
428
+ "H4",
429
+ "H5",
430
+ "H6",
431
+ "LI",
432
+ "UL",
433
+ "OL",
434
+ "BLOCKQUOTE",
435
+ "PRE",
436
+ "TABLE",
437
+ "CAPTION",
438
+ "THEAD",
439
+ "TBODY",
440
+ "TFOOT",
441
+ "TR"
442
+ ]);
443
+ var CELL_TAGS = /* @__PURE__ */ new Set(["TD", "TH"]);
444
+ var TABLE_STRUCTURE_TAGS = /* @__PURE__ */ new Set([
445
+ "TABLE",
446
+ "THEAD",
447
+ "TBODY",
448
+ "TFOOT",
449
+ "TR"
450
+ ]);
451
+ var IGNORED_TAGS = /* @__PURE__ */ new Set(["SCRIPT", "STYLE", "TEMPLATE", "NOSCRIPT"]);
452
+ function nodeListToText(nodes) {
453
+ let out = "";
454
+ let afterCell = false;
455
+ for (const node of nodes) {
456
+ if (node.nodeType === Node.TEXT_NODE) {
457
+ const text = node.textContent ?? "";
458
+ const parentTag = node.parentElement?.tagName;
459
+ if (!text.trim() && parentTag && TABLE_STRUCTURE_TAGS.has(parentTag)) {
460
+ continue;
461
+ }
462
+ out += text;
463
+ if (text.trim()) afterCell = false;
464
+ continue;
465
+ }
466
+ if (node.nodeType !== Node.ELEMENT_NODE) continue;
467
+ const el = node;
468
+ if (IGNORED_TAGS.has(el.tagName)) continue;
469
+ if (el.tagName === "BR") {
470
+ afterCell = false;
471
+ out += "\n";
472
+ continue;
473
+ }
474
+ if (CELL_TAGS.has(el.tagName)) {
475
+ if (afterCell) out += " ";
476
+ out += nodeListToText(el.childNodes);
477
+ afterCell = true;
478
+ continue;
479
+ }
480
+ afterCell = false;
481
+ if (BLOCK_TAGS.has(el.tagName)) {
482
+ if (out && !out.endsWith("\n")) out += "\n";
483
+ let inner = nodeListToText(el.childNodes);
484
+ if (inner === "\n") inner = "";
485
+ out += inner;
486
+ if (inner === "" || !inner.endsWith("\n")) out += "\n";
487
+ continue;
488
+ }
489
+ out += nodeListToText(el.childNodes);
490
+ }
491
+ return out;
492
+ }
493
+ function htmlToPlain(html) {
494
+ if (!html) return "";
495
+ const root = document.createElement("template");
496
+ root.innerHTML = html;
497
+ const text = nodeListToText(root.content.childNodes);
498
+ return text.endsWith("\n") ? text.slice(0, -1) : text;
499
+ }
500
+ function sanitizePastedHtml(html) {
501
+ return plainToHtml(htmlToPlain(html));
502
+ }
503
+ var warned = /* @__PURE__ */ new Set();
504
+ function warnOnce(key, message) {
505
+ if (process.env.NODE_ENV !== "production" && !warned.has(key)) {
506
+ warned.add(key);
507
+ console.warn(`[mieweb/ui Textarea] ${message}`);
508
+ }
509
+ }
510
+ var RichTextarea = React2__namespace.forwardRef(
511
+ ({
512
+ label,
513
+ labelVariant = "stacked",
514
+ hideLabel,
515
+ error,
516
+ helperText,
517
+ maxLength,
518
+ showCount,
519
+ autoResize = true,
520
+ requiredVariant,
521
+ size = "md",
522
+ hasError,
523
+ resize,
524
+ className,
525
+ value,
526
+ defaultValue,
527
+ onChange,
528
+ onBlur,
529
+ onFocus,
530
+ onKeyDown,
531
+ onKeyUp,
532
+ name,
533
+ form,
534
+ id,
535
+ rows,
536
+ required,
537
+ readOnly,
538
+ disabled,
539
+ placeholder,
540
+ "aria-describedby": ariaDescribedBy,
541
+ "aria-label": ariaLabel,
542
+ "aria-labelledby": ariaLabelledBy
543
+ }, ref) => {
544
+ warnOnce(
545
+ "rich-upgrade",
546
+ 'Since 0.12.0 Textarea renders a rich text surface (plain-text value contract unchanged). Watch for: CSS "textarea" selectors, E2E fill()/type(), and maxLength no longer blocking typing. Opt out per field with richText={false} or use TextAreaPlain. See MIGRATION.md.'
547
+ );
548
+ if (labelVariant === "floating") {
549
+ warnOnce(
550
+ "floating",
551
+ 'labelVariant="floating" is not supported by the rich surface; falling back to a stacked label. Use TextAreaPlain for floating labels.'
552
+ );
553
+ }
554
+ if (autoResize === false) {
555
+ warnOnce(
556
+ "autoResize",
557
+ "autoResize={false} is ignored; the rich surface always grows with content. Use TextAreaPlain for a fixed-height textarea."
558
+ );
559
+ }
560
+ if (resize !== void 0) {
561
+ warnOnce(
562
+ "resize",
563
+ "resize is ignored; the rich surface has no native resize handle. Use TextAreaPlain for drag-resizing."
564
+ );
565
+ }
566
+ if (maxLength !== void 0) {
567
+ warnOnce(
568
+ "maxLength",
569
+ "maxLength is not enforced while typing; the character count still reflects it. Enforce limits in onChange or use TextAreaPlain."
570
+ );
571
+ }
572
+ const reactId = React2__namespace.useId();
573
+ const errorId = `${reactId}-error`;
574
+ const helperId = `${reactId}-helper`;
575
+ const countId = `${reactId}-count`;
576
+ const controlledValue = value !== void 0 ? normalizeNewlines(String(value)) : void 0;
577
+ const [state, setState] = React2__namespace.useState(() => {
578
+ const plain = controlledValue ?? (defaultValue !== void 0 ? normalizeNewlines(String(defaultValue)) : "");
579
+ return { html: plainToHtml(plain), plain };
580
+ });
581
+ if (controlledValue !== void 0 && controlledValue !== state.plain) {
582
+ setState({ html: plainToHtml(controlledValue), plain: controlledValue });
583
+ }
584
+ const hiddenRef = React2__namespace.useRef(null);
585
+ const editorWrapperRef = React2__namespace.useRef(null);
586
+ React2__namespace.useEffect(() => {
587
+ const editable = editorWrapperRef.current?.querySelector(
588
+ '[role="textbox"]'
589
+ );
590
+ if (editable && htmlToPlain(editable.innerHTML) !== state.plain) {
591
+ editable.innerHTML = state.html;
592
+ }
593
+ });
594
+ const focusEditor = React2__namespace.useCallback(() => {
595
+ editorWrapperRef.current?.querySelector('[role="textbox"]')?.focus();
596
+ }, []);
597
+ React2__namespace.useImperativeHandle(ref, () => {
598
+ const node = hiddenRef.current;
599
+ node.focus = focusEditor;
600
+ node.blur = () => {
601
+ const editable = editorWrapperRef.current?.querySelector(
602
+ '[role="textbox"]'
603
+ );
604
+ if (document.activeElement === editable) editable?.blur();
605
+ };
606
+ return node;
607
+ }, [focusEditor]);
608
+ const [externalLabelId, setExternalLabelId] = React2__namespace.useState();
609
+ React2__namespace.useEffect(() => {
610
+ if (!id) return;
611
+ const externalLabel = document.querySelector(
612
+ `label[for="${globalThis.CSS.escape(id)}"]`
613
+ );
614
+ if (!externalLabel) return;
615
+ if (!externalLabel.id) externalLabel.id = `${reactId}-external-label`;
616
+ setExternalLabelId(externalLabel.id);
617
+ const handleClick = () => focusEditor();
618
+ externalLabel.addEventListener("click", handleClick);
619
+ return () => externalLabel.removeEventListener("click", handleClick);
620
+ }, [id, reactId, focusEditor]);
621
+ const retargetEvent = React2__namespace.useCallback(
622
+ (event) => {
623
+ const node = hiddenRef.current;
624
+ if (!node) return event;
625
+ const retargeted = Object.create(event);
626
+ Object.defineProperties(retargeted, {
627
+ target: { value: node, enumerable: true },
628
+ currentTarget: { value: node, enumerable: true },
629
+ stopPropagation: { value: () => event.stopPropagation() },
630
+ preventDefault: { value: () => event.preventDefault() }
631
+ });
632
+ return retargeted;
633
+ },
634
+ []
635
+ );
636
+ const focusStaysInside = (event) => {
637
+ const related = event.relatedTarget;
638
+ if (!related) return false;
639
+ if (event.currentTarget.contains(related)) return true;
640
+ const menuId = related.closest('[data-slot="dropdown-menu"]')?.querySelector('[role="menu"]')?.id;
641
+ return !!menuId && !!event.currentTarget.querySelector(
642
+ `[aria-controls="${globalThis.CSS.escape(menuId)}"]`
643
+ );
644
+ };
645
+ const handleHtmlChange = (html) => {
646
+ const plain = htmlToPlain(html);
647
+ setState({ html, plain });
648
+ const node = hiddenRef.current;
649
+ if (onChange && node) {
650
+ node.value = plain;
651
+ const nativeEvent = new Event("change");
652
+ onChange({
653
+ target: node,
654
+ currentTarget: node,
655
+ type: "change",
656
+ bubbles: false,
657
+ cancelable: false,
658
+ defaultPrevented: false,
659
+ eventPhase: 0,
660
+ isTrusted: false,
661
+ timeStamp: Date.now(),
662
+ nativeEvent,
663
+ preventDefault: () => {
664
+ },
665
+ stopPropagation: () => {
666
+ },
667
+ isDefaultPrevented: () => false,
668
+ isPropagationStopped: () => false,
669
+ persist: () => {
670
+ }
671
+ });
672
+ }
673
+ };
674
+ const showError = !!error;
675
+ const invalid = hasError || showError;
676
+ const describedByIds = [
677
+ showError ? errorId : null,
678
+ helperText && !showError ? helperId : null,
679
+ showCount ? countId : null,
680
+ ariaDescribedBy
681
+ ].filter(Boolean).join(" ") || void 0;
682
+ const characterCount = state.plain.length;
683
+ const lineHeightPx = size === "sm" ? 20 : size === "lg" ? 28 : 24;
684
+ const editorStyle = rows ? (
685
+ // 7px block padding each side, matching the editable's py-[7px].
686
+ { minHeight: `${rows * lineHeightPx + 14}px` }
687
+ ) : void 0;
688
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-slot": "textarea-wrapper", className: "flex flex-col gap-1.5", children: [
689
+ label && /* @__PURE__ */ jsxRuntime.jsxs(
690
+ "label",
691
+ {
692
+ "data-slot": "textarea-label",
693
+ className: chunkS5RVSXDV_cjs.cn(
694
+ "text-foreground text-sm font-medium",
695
+ hideLabel && "sr-only"
696
+ ),
697
+ children: [
698
+ label,
699
+ required && /* @__PURE__ */ jsxRuntime.jsx(chunkZ5S5JEJD_cjs.RequiredMark, { variant: requiredVariant })
700
+ ]
701
+ }
702
+ ),
703
+ /* @__PURE__ */ jsxRuntime.jsx(
704
+ "div",
705
+ {
706
+ ref: editorWrapperRef,
707
+ className: "w-full",
708
+ onBlur: onBlur && ((event) => {
709
+ if (focusStaysInside(event)) return;
710
+ onBlur(retargetEvent(event));
711
+ }),
712
+ onFocus: onFocus && ((event) => {
713
+ if (focusStaysInside(event)) return;
714
+ onFocus(retargetEvent(event));
715
+ }),
716
+ onKeyDown: onKeyDown && ((event) => onKeyDown(retargetEvent(event))),
717
+ onKeyUp: onKeyUp && ((event) => onKeyUp(retargetEvent(event))),
718
+ children: /* @__PURE__ */ jsxRuntime.jsx(
719
+ TextAreaRich,
720
+ {
721
+ value: state.html,
722
+ onChange: handleHtmlChange,
723
+ placeholder,
724
+ disabled,
725
+ readOnly,
726
+ id,
727
+ sanitizeHtml: sanitizePastedHtml,
728
+ "aria-label": ariaLabel ?? label,
729
+ "aria-labelledby": ariaLabelledBy ?? externalLabelId,
730
+ "aria-describedby": describedByIds,
731
+ "aria-invalid": invalid || void 0,
732
+ "aria-required": required || void 0,
733
+ editorClassName: chunkS5RVSXDV_cjs.cn(
734
+ // A native textarea renders repeated spaces and tabs
735
+ // faithfully; normal HTML layout would collapse them.
736
+ "whitespace-pre-wrap",
737
+ size === "sm" && "text-sm",
738
+ size === "lg" && "text-lg"
739
+ ),
740
+ editorStyle,
741
+ className: chunkS5RVSXDV_cjs.cn(
742
+ size === "sm" && "text-sm",
743
+ size === "lg" && "text-lg",
744
+ invalid && "border-destructive focus-within:ring-destructive focus-within:border-destructive",
745
+ disabled && "cursor-not-allowed opacity-50",
746
+ className
747
+ )
748
+ }
749
+ )
750
+ }
751
+ ),
752
+ /* @__PURE__ */ jsxRuntime.jsx(
753
+ "textarea",
754
+ {
755
+ "data-slot": "textarea-mirror",
756
+ ref: hiddenRef,
757
+ hidden: true,
758
+ readOnly: true,
759
+ tabIndex: -1,
760
+ "aria-hidden": "true",
761
+ name,
762
+ form,
763
+ required,
764
+ disabled,
765
+ value: state.plain
766
+ }
767
+ ),
768
+ /* @__PURE__ */ jsxRuntime.jsxs(
769
+ "div",
770
+ {
771
+ "data-slot": "textarea-footer",
772
+ className: "flex items-center justify-between gap-2",
773
+ children: [
774
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
775
+ showError && /* @__PURE__ */ jsxRuntime.jsx(
776
+ "p",
777
+ {
778
+ id: errorId,
779
+ "data-slot": "textarea-error",
780
+ className: "text-destructive-700 dark:text-destructive-400 text-sm",
781
+ role: "alert",
782
+ children: error
783
+ }
784
+ ),
785
+ helperText && !showError && /* @__PURE__ */ jsxRuntime.jsx(
786
+ "p",
787
+ {
788
+ id: helperId,
789
+ "data-slot": "textarea-helper",
790
+ className: "text-muted-foreground text-sm",
791
+ children: helperText
792
+ }
793
+ )
794
+ ] }),
795
+ showCount && /* @__PURE__ */ jsxRuntime.jsxs(
796
+ "p",
797
+ {
798
+ id: countId,
799
+ "data-slot": "textarea-count",
800
+ className: chunkS5RVSXDV_cjs.cn(
801
+ "text-muted-foreground shrink-0 text-xs",
802
+ maxLength && characterCount >= maxLength && "text-destructive"
803
+ ),
804
+ children: [
805
+ characterCount,
806
+ maxLength && `/${maxLength}`
807
+ ]
808
+ }
809
+ )
810
+ ]
811
+ }
812
+ )
813
+ ] });
814
+ }
815
+ );
816
+ RichTextarea.displayName = "RichTextarea";
817
+ var Textarea = React2__namespace.forwardRef(
818
+ ({ richText = true, ...props }, ref) => richText ? /* @__PURE__ */ jsxRuntime.jsx(RichTextarea, { ref, ...props }) : /* @__PURE__ */ jsxRuntime.jsx(TextAreaPlain, { ref, ...props })
819
+ );
820
+ Textarea.displayName = "Textarea";
821
+
822
+ exports.TextAreaPlain = TextAreaPlain;
823
+ exports.TextAreaRich = TextAreaRich;
824
+ exports.Textarea = Textarea;
825
+ exports.htmlToPlain = htmlToPlain;
826
+ exports.plainToHtml = plainToHtml;
827
+ exports.textareaVariants = textareaVariants;
828
+ //# sourceMappingURL=chunk-AQUHEKXL.cjs.map
829
+ //# sourceMappingURL=chunk-AQUHEKXL.cjs.map