@timeax/form-palette 0.0.3 → 0.0.5

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 (120) hide show
  1. package/{src/schema/adapter.ts → dist/adapters.d.mts} +118 -43
  2. package/dist/adapters.d.ts +292 -0
  3. package/dist/adapters.js +13283 -0
  4. package/dist/adapters.js.map +1 -0
  5. package/dist/adapters.mjs +13269 -0
  6. package/dist/adapters.mjs.map +1 -0
  7. package/dist/index.d.mts +3744 -0
  8. package/dist/index.d.ts +3744 -0
  9. package/dist/index.js +43014 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/index.mjs +42965 -0
  12. package/dist/index.mjs.map +1 -0
  13. package/package.json +22 -7
  14. package/.scaffold-cache.json +0 -537
  15. package/src/.scaffold-cache.json +0 -544
  16. package/src/adapters/axios.ts +0 -117
  17. package/src/adapters/index.ts +0 -91
  18. package/src/adapters/inertia.ts +0 -187
  19. package/src/core/adapter-registry.ts +0 -87
  20. package/src/core/bound/bind-host.ts +0 -14
  21. package/src/core/bound/observe-bound-field.ts +0 -172
  22. package/src/core/bound/wait-for-bound-field.ts +0 -57
  23. package/src/core/context.ts +0 -23
  24. package/src/core/core-provider.tsx +0 -818
  25. package/src/core/core-root.tsx +0 -72
  26. package/src/core/core-shell.tsx +0 -44
  27. package/src/core/errors/error-strip.tsx +0 -71
  28. package/src/core/errors/index.ts +0 -2
  29. package/src/core/errors/map-error-bag.ts +0 -51
  30. package/src/core/errors/map-zod.ts +0 -39
  31. package/src/core/hooks/use-button.ts +0 -220
  32. package/src/core/hooks/use-core-context.ts +0 -20
  33. package/src/core/hooks/use-core-utility.ts +0 -0
  34. package/src/core/hooks/use-core.ts +0 -13
  35. package/src/core/hooks/use-field.ts +0 -497
  36. package/src/core/hooks/use-optional-field.ts +0 -28
  37. package/src/core/index.ts +0 -0
  38. package/src/core/registry/binder-registry.ts +0 -82
  39. package/src/core/registry/field-registry.ts +0 -187
  40. package/src/core/test.tsx +0 -17
  41. package/src/global.d.ts +0 -14
  42. package/src/index.ts +0 -68
  43. package/src/input/index.ts +0 -4
  44. package/src/input/input-field.tsx +0 -854
  45. package/src/input/input-layout-graph.ts +0 -230
  46. package/src/input/input-props.ts +0 -190
  47. package/src/lib/get-global-countries.ts +0 -87
  48. package/src/lib/utils.ts +0 -6
  49. package/src/presets/index.ts +0 -0
  50. package/src/presets/shadcn-preset.ts +0 -0
  51. package/src/presets/shadcn-variants/checkbox.tsx +0 -849
  52. package/src/presets/shadcn-variants/chips.tsx +0 -756
  53. package/src/presets/shadcn-variants/color.tsx +0 -284
  54. package/src/presets/shadcn-variants/custom.tsx +0 -227
  55. package/src/presets/shadcn-variants/date.tsx +0 -796
  56. package/src/presets/shadcn-variants/file.tsx +0 -764
  57. package/src/presets/shadcn-variants/keyvalue.tsx +0 -556
  58. package/src/presets/shadcn-variants/multiselect.tsx +0 -1132
  59. package/src/presets/shadcn-variants/number.tsx +0 -176
  60. package/src/presets/shadcn-variants/password.tsx +0 -737
  61. package/src/presets/shadcn-variants/phone.tsx +0 -628
  62. package/src/presets/shadcn-variants/radio.tsx +0 -578
  63. package/src/presets/shadcn-variants/select.tsx +0 -956
  64. package/src/presets/shadcn-variants/slider.tsx +0 -622
  65. package/src/presets/shadcn-variants/text.tsx +0 -343
  66. package/src/presets/shadcn-variants/textarea.tsx +0 -66
  67. package/src/presets/shadcn-variants/toggle.tsx +0 -218
  68. package/src/presets/shadcn-variants/treeselect.tsx +0 -784
  69. package/src/presets/ui/badge.tsx +0 -46
  70. package/src/presets/ui/button.tsx +0 -60
  71. package/src/presets/ui/calendar.tsx +0 -214
  72. package/src/presets/ui/checkbox.tsx +0 -115
  73. package/src/presets/ui/custom.tsx +0 -0
  74. package/src/presets/ui/dialog.tsx +0 -141
  75. package/src/presets/ui/field.tsx +0 -246
  76. package/src/presets/ui/input-mask.tsx +0 -739
  77. package/src/presets/ui/input-otp.tsx +0 -77
  78. package/src/presets/ui/input.tsx +0 -1011
  79. package/src/presets/ui/label.tsx +0 -22
  80. package/src/presets/ui/number.tsx +0 -1370
  81. package/src/presets/ui/popover.tsx +0 -46
  82. package/src/presets/ui/radio-group.tsx +0 -43
  83. package/src/presets/ui/scroll-area.tsx +0 -56
  84. package/src/presets/ui/select.tsx +0 -190
  85. package/src/presets/ui/separator.tsx +0 -28
  86. package/src/presets/ui/slider.tsx +0 -61
  87. package/src/presets/ui/switch.tsx +0 -32
  88. package/src/presets/ui/textarea.tsx +0 -634
  89. package/src/presets/ui/time-dropdowns.tsx +0 -350
  90. package/src/schema/core.ts +0 -429
  91. package/src/schema/field-map.ts +0 -0
  92. package/src/schema/field.ts +0 -224
  93. package/src/schema/index.ts +0 -0
  94. package/src/schema/input-field.ts +0 -260
  95. package/src/schema/presets.ts +0 -0
  96. package/src/schema/variant.ts +0 -216
  97. package/src/variants/core/checkbox.tsx +0 -54
  98. package/src/variants/core/chips.tsx +0 -22
  99. package/src/variants/core/color.tsx +0 -16
  100. package/src/variants/core/custom.tsx +0 -18
  101. package/src/variants/core/date.tsx +0 -25
  102. package/src/variants/core/file.tsx +0 -9
  103. package/src/variants/core/keyvalue.tsx +0 -12
  104. package/src/variants/core/multiselect.tsx +0 -28
  105. package/src/variants/core/number.tsx +0 -115
  106. package/src/variants/core/password.tsx +0 -35
  107. package/src/variants/core/phone.tsx +0 -16
  108. package/src/variants/core/radio.tsx +0 -38
  109. package/src/variants/core/select.tsx +0 -15
  110. package/src/variants/core/slider.tsx +0 -55
  111. package/src/variants/core/text.tsx +0 -114
  112. package/src/variants/core/textarea.tsx +0 -22
  113. package/src/variants/core/toggle.tsx +0 -50
  114. package/src/variants/core/treeselect.tsx +0 -11
  115. package/src/variants/helpers/selection-summary.tsx +0 -236
  116. package/src/variants/index.ts +0 -75
  117. package/src/variants/registry.ts +0 -38
  118. package/src/variants/select-shared.ts +0 -0
  119. package/src/variants/shared.ts +0 -126
  120. package/tsconfig.json +0 -14
@@ -1,634 +0,0 @@
1
- import * as React from "react";
2
- import { cn } from "@/lib/utils";
3
-
4
- export interface TextareaIconControlProps {
5
- // lower icons (overlaid in textarea-field)
6
- leadingIcons?: React.ReactNode[];
7
- trailingIcons?: React.ReactNode[];
8
- icon?: React.ReactNode;
9
-
10
- iconGap?: number;
11
- leadingIconSpacing?: number;
12
- trailingIconSpacing?: number;
13
-
14
- // lower side controls (outside the text area by default)
15
- leadingControl?: React.ReactNode;
16
- trailingControl?: React.ReactNode;
17
- leadingControlClassName?: string;
18
- trailingControlClassName?: string;
19
-
20
- /**
21
- * If true, move the visual box (border, bg, radius, focus) from
22
- * `textarea-field` to `textarea-inner` so the side controls are
23
- * inside the same frame.
24
- *
25
- * Default: false (controls sit outside the border).
26
- */
27
- extendBoxToControls?: boolean;
28
-
29
- /**
30
- * If true, move the visual box all the way up to `textarea-box`,
31
- * so the upper toolbox and the inner row share a single frame.
32
- *
33
- * When this is true, it overrides `extendBoxToControls`.
34
- *
35
- * Default: false.
36
- */
37
- extendBoxToToolbox?: boolean;
38
-
39
- /**
40
- * Extra padding knobs (same semantics as Input).
41
- *
42
- * px → symmetric horizontal padding
43
- * py → symmetric vertical padding
44
- * ps/pe → logical start/end padding adjustments
45
- * pb → extra bottom padding (stacked with py)
46
- */
47
- px?: number;
48
- py?: number;
49
- ps?: number;
50
- pe?: number;
51
- pb?: number;
52
-
53
- /**
54
- * Extra classes merged into the raw <textarea>.
55
- * (The box padding/border live on the wrappers.)
56
- */
57
- inputClassName?: string;
58
- }
59
-
60
- export interface TextareaSizeProps {
61
- size?: "sm" | "md" | "lg" | (string & {});
62
- density?:
63
- | "compact"
64
- | "normal"
65
- | "relaxed"
66
- | "dense"
67
- | "loose"
68
- | (string & {});
69
- }
70
-
71
- export interface TextareaProps
72
- extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, "size">,
73
- TextareaIconControlProps,
74
- TextareaSizeProps {
75
- /**
76
- * Auto-resize based on content.
77
- * Default: true.
78
- */
79
- autoResize?: boolean;
80
-
81
- /**
82
- * Minimum number of visual rows.
83
- * Default: 1.
84
- */
85
- rows?: number;
86
-
87
- /**
88
- * Maximum number of visual rows.
89
- * Undefined → unlimited.
90
- */
91
- maxRows?: number;
92
-
93
- /**
94
- * Optional upper toolbox area.
95
- */
96
- upperControl?: React.ReactNode;
97
- upperControlClassName?: string;
98
- }
99
-
100
- // ─────────────────────────────────────────────
101
- // Helpers
102
- // ─────────────────────────────────────────────
103
-
104
- function resolveSizeDensityClasses(size: unknown, density: unknown) {
105
- const s = (size as string | undefined) ?? "md";
106
- const d = (density as string | undefined) ?? "normal";
107
-
108
- let textCls = "text-base md:text-sm";
109
-
110
- if (s === "sm") {
111
- textCls = "text-sm";
112
- } else if (s === "lg") {
113
- textCls = "text-base";
114
- }
115
-
116
- let densityCls = "";
117
- if (d === "dense" || d === "compact") {
118
- densityCls = "leading-tight";
119
- } else if (d === "relaxed" || d === "loose") {
120
- densityCls = "leading-relaxed";
121
- }
122
-
123
- return { textCls, densityCls };
124
- }
125
-
126
- function resolveBasePadding(size: unknown, density: unknown) {
127
- // mirror Input baseline
128
- let px = 12;
129
- let py = 8;
130
-
131
- const s = (size as string | undefined) ?? "md";
132
- const d = (density as string | undefined) ?? "normal";
133
-
134
- if (s === "sm") {
135
- px = 10;
136
- py = 6;
137
- } else if (s === "lg") {
138
- px = 14;
139
- py = 10;
140
- }
141
-
142
- if (d === "dense" || d === "compact") {
143
- py = Math.max(2, py - 1);
144
- } else if (d === "relaxed" || d === "loose") {
145
- py = py + 1;
146
- }
147
-
148
- return { px, py };
149
- }
150
-
151
- // ─────────────────────────────────────────────
152
- // Component
153
- // ─────────────────────────────────────────────
154
-
155
- export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
156
- function Textarea(rawProps, forwardedRef) {
157
- const {
158
- // layout wrapper
159
- className,
160
- style,
161
-
162
- // native textarea bits
163
- disabled,
164
- readOnly,
165
- required,
166
- onChange,
167
- onFocus,
168
- onBlur,
169
- placeholder,
170
-
171
- // size / density
172
- size = "md",
173
- density = "normal",
174
-
175
- // auto-resize
176
- autoResize = true,
177
- rows: minRowsProp,
178
- maxRows,
179
-
180
- // controls / icons
181
- leadingIcons,
182
- trailingIcons,
183
- icon,
184
- iconGap,
185
- leadingIconSpacing,
186
- trailingIconSpacing,
187
- leadingControl,
188
- trailingControl,
189
- leadingControlClassName,
190
- trailingControlClassName,
191
- extendBoxToControls = false,
192
- extendBoxToToolbox = false,
193
- px,
194
- py,
195
- ps,
196
- pe,
197
- pb,
198
- inputClassName,
199
-
200
- // upper toolbox
201
- upperControl,
202
- upperControlClassName,
203
-
204
- // rest of <textarea> props
205
- ...rest
206
- } = rawProps;
207
-
208
- const sizeKey = (size as string | undefined) ?? "md";
209
- const densityKey = (density as string | undefined) ?? "normal";
210
-
211
- const innerRef = React.useRef<HTMLTextAreaElement | null>(null);
212
- React.useImperativeHandle(
213
- forwardedRef,
214
- () => innerRef.current as HTMLTextAreaElement,
215
- []
216
- );
217
-
218
- // icons
219
- const resolvedLeadingIcons: React.ReactNode[] = (() => {
220
- if (leadingIcons && leadingIcons.length) return leadingIcons;
221
- if (icon) return [icon];
222
- return [];
223
- })();
224
- const resolvedTrailingIcons: React.ReactNode[] = trailingIcons ?? [];
225
-
226
- const hasLeadingIcons = resolvedLeadingIcons.length > 0;
227
- const hasTrailingIcons = resolvedTrailingIcons.length > 0;
228
- const hasLeadingControl = !!leadingControl;
229
- const hasTrailingControl = !!trailingControl;
230
-
231
- const hasIcons = hasLeadingIcons || hasTrailingIcons;
232
- const hasControls = hasLeadingControl || hasTrailingControl;
233
- const hasExtras = hasIcons || hasControls;
234
-
235
- const baseIconGap = iconGap ?? 1;
236
- const leadingGap = leadingIconSpacing ?? baseIconGap;
237
- const trailingGap = trailingIconSpacing ?? baseIconGap;
238
-
239
- const leadingIconsRef = React.useRef<HTMLDivElement | null>(null);
240
- const trailingIconsRef = React.useRef<HTMLDivElement | null>(null);
241
- const [leadingIconsWidth, setLeadingIconsWidth] = React.useState(0);
242
- const [trailingIconsWidth, setTrailingIconsWidth] = React.useState(0);
243
-
244
- const measureIconWidths = React.useCallback(() => {
245
- if (typeof window === "undefined") return;
246
-
247
- const lead = leadingIconsRef.current;
248
- const trail = trailingIconsRef.current;
249
-
250
- if (lead) {
251
- const rect = lead.getBoundingClientRect();
252
- setLeadingIconsWidth(rect.width);
253
- } else {
254
- setLeadingIconsWidth(0);
255
- }
256
-
257
- if (trail) {
258
- const rect = trail.getBoundingClientRect();
259
- setTrailingIconsWidth(rect.width);
260
- } else {
261
- setTrailingIconsWidth(0);
262
- }
263
- }, []);
264
-
265
- // MutationObserver → recompute icon widths when content changes
266
- React.useLayoutEffect(() => {
267
- if (
268
- typeof window === "undefined" ||
269
- typeof MutationObserver === "undefined"
270
- ) {
271
- measureIconWidths();
272
- return;
273
- }
274
-
275
- const observers: MutationObserver[] = [];
276
- const lead = leadingIconsRef.current;
277
- const trail = trailingIconsRef.current;
278
-
279
- if (lead) {
280
- const obs = new MutationObserver(() => measureIconWidths());
281
- obs.observe(lead, {
282
- childList: true,
283
- subtree: true,
284
- attributes: true,
285
- });
286
- observers.push(obs);
287
- }
288
-
289
- if (trail) {
290
- const obs = new MutationObserver(() => measureIconWidths());
291
- obs.observe(trail, {
292
- childList: true,
293
- subtree: true,
294
- attributes: true,
295
- });
296
- observers.push(obs);
297
- }
298
-
299
- measureIconWidths();
300
-
301
- return () => observers.forEach((o) => o.disconnect());
302
- }, [measureIconWidths, hasLeadingIcons, hasTrailingIcons]);
303
-
304
- // row height / rows
305
- const [rowHeight, setRowHeight] = React.useState<number | null>(null);
306
- const baseMinRows = Math.max(minRowsProp ?? 1, 1);
307
- const [rows, setRows] = React.useState<number>(baseMinRows);
308
-
309
- // measure a single-row height from the textarea itself
310
- React.useLayoutEffect(() => {
311
- if (typeof window === "undefined") return;
312
- const el = innerRef.current;
313
- if (!el) return;
314
-
315
- const prevValue = el.value;
316
- const prevHeight = el.style.height;
317
-
318
- el.value = "X";
319
- el.style.height = "0px";
320
- const singleRowHeight = el.scrollHeight;
321
-
322
- el.value = prevValue;
323
- el.style.height = prevHeight;
324
-
325
- if (singleRowHeight > 0 && Number.isFinite(singleRowHeight)) {
326
- setRowHeight(singleRowHeight);
327
- setRows(baseMinRows);
328
- }
329
- }, [sizeKey, densityKey, baseMinRows]);
330
-
331
- // auto-resize helper
332
- const recomputeHeight = React.useCallback(() => {
333
- if (!autoResize) return;
334
- if (!innerRef.current) return;
335
- if (!rowHeight) return;
336
-
337
- const el = innerRef.current;
338
-
339
- el.style.height = "0px";
340
- const scrollH = el.scrollHeight;
341
-
342
- // if empty, keep exactly minRows
343
- if (!el.value || el.value.length === 0) {
344
- const h = baseMinRows * rowHeight;
345
- el.style.height = `${h}px`;
346
- setRows(baseMinRows);
347
- return;
348
- }
349
-
350
- const rawRows = scrollH / rowHeight;
351
- let nextRows = Math.max(baseMinRows, Math.ceil(rawRows));
352
- if (typeof maxRows === "number" && maxRows > 0) {
353
- nextRows = Math.min(nextRows, maxRows);
354
- }
355
-
356
- const nextHeight = nextRows * rowHeight;
357
- el.style.height = `${nextHeight}px`;
358
- setRows(nextRows);
359
- }, [autoResize, rowHeight, baseMinRows, maxRows]);
360
-
361
- // run when controlled value changes or initial mount
362
- React.useLayoutEffect(() => {
363
- recomputeHeight();
364
- // eslint-disable-next-line react-hooks/exhaustive-deps
365
- }, [recomputeHeight, rest.value, rest.defaultValue]);
366
-
367
- // padding (frame-level)
368
- const { px: pxDefault, py: pyDefault } = resolveBasePadding(size, density);
369
-
370
- const extraPx = typeof px === "number" ? px : 0;
371
- const extraPy = typeof py === "number" ? py : 0;
372
- const extraPs = typeof ps === "number" ? ps : 0;
373
- const extraPe = typeof pe === "number" ? pe : 0;
374
- const extraPb = typeof pb === "number" ? pb : 0;
375
-
376
- const basePaddingStart = pxDefault + extraPx + extraPs;
377
- const basePaddingEnd = pxDefault + extraPx + extraPe;
378
- const paddingTop = pyDefault + extraPy;
379
- const paddingBottom = pyDefault + extraPy + extraPb;
380
-
381
- // extra space text needs because of icons
382
- const iconsLeftExtra =
383
- hasLeadingIcons && leadingIconsWidth > 0
384
- ? leadingIconsWidth + baseIconGap
385
- : 0;
386
-
387
- const iconsRightExtra =
388
- hasTrailingIcons && trailingIconsWidth > 0
389
- ? trailingIconsWidth + baseIconGap
390
- : 0;
391
-
392
- const { textCls, densityCls } = resolveSizeDensityClasses(size, density);
393
-
394
- // vars for the frame: both base + adjusted
395
- const vars: React.CSSProperties = {
396
- "--fp-pl-base": `${basePaddingStart}px`,
397
- "--fp-pr-base": `${basePaddingEnd}px`,
398
- "--fp-pl": `${basePaddingStart + iconsLeftExtra}px`,
399
- "--fp-pr": `${basePaddingEnd + iconsRightExtra}px`,
400
- "--fp-pt": `${paddingTop}px`,
401
- "--fp-pb": `${paddingBottom}px`,
402
- "--fp-row-height": rowHeight ? `${rowHeight}px` : undefined,
403
- "--fp-rows": rows,
404
- } as React.CSSProperties;
405
-
406
- const mergedWrapperStyle: React.CSSProperties = {
407
- ...(style ?? {}),
408
- ...vars,
409
- };
410
-
411
- // visual frame (border/background/focus)
412
- const frameClasses = cn(
413
- "border-input placeholder:text-muted-foreground focus-within:border-ring focus-within:ring-ring/50 focus-within:ring-[3px]",
414
- "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
415
- "dark:bg-input/30 rounded-md border bg-transparent shadow-xs transition-[color,box-shadow] outline-none",
416
- "disabled:cursor-not-allowed disabled:opacity-50"
417
- );
418
-
419
- // padding utility using adjusted vars (--fp-pl / --fp-pr)
420
- const framePaddingClasses = cn(
421
- "px-(--fp-pl,--spacing(3)) pr-(--fp-pr,--spacing(3))",
422
- "pt-(--fp-pt,--spacing(1)) pb-(--fp-pb,--spacing(1))"
423
- );
424
-
425
- // which element owns the frame?
426
- const boxOnToolbox = extendBoxToToolbox;
427
- const boxOnInner = !boxOnToolbox && extendBoxToControls;
428
- const boxOnField = !boxOnToolbox && !boxOnInner;
429
-
430
- const wrapperClasses = cn("w-full", className);
431
-
432
- const boxClasses = cn(
433
- "flex flex-col gap-1",
434
- boxOnToolbox && frameClasses,
435
- boxOnToolbox && framePaddingClasses
436
- );
437
-
438
- const innerRowClasses = cn(
439
- "flex items-stretch gap-1",
440
- boxOnInner && frameClasses,
441
- boxOnInner && framePaddingClasses
442
- );
443
-
444
- const fieldWrapperClasses = cn(
445
- "relative flex-1 min-w-0",
446
- boxOnField && frameClasses,
447
- boxOnField && framePaddingClasses
448
- );
449
-
450
- const textareaClasses = cn(
451
- "block w-full min-h-[1px] resize-none bg-transparent border-none outline-none shadow-none",
452
- "px-0 py-0",
453
- "placeholder:text-muted-foreground",
454
- textCls,
455
- densityCls,
456
- inputClassName
457
- );
458
-
459
- const focusTextarea = () => {
460
- if (innerRef.current) innerRef.current.focus();
461
- };
462
-
463
- const handleFocus = React.useCallback(
464
- (event: React.FocusEvent<HTMLTextAreaElement>) => {
465
- onFocus?.(event);
466
- },
467
- [onFocus]
468
- );
469
-
470
- const handleBlur = React.useCallback(
471
- (event: React.FocusEvent<HTMLTextAreaElement>) => {
472
- onBlur?.(event);
473
- },
474
- [onBlur]
475
- );
476
-
477
- const handleChange = React.useCallback(
478
- (event: React.ChangeEvent<HTMLTextAreaElement>) => {
479
- onChange?.(event);
480
- // for uncontrolled usage, recompute on each keystroke
481
- recomputeHeight();
482
- },
483
- [onChange, recomputeHeight]
484
- );
485
-
486
- const handleIconMouseDown = (e: React.MouseEvent) => {
487
- e.preventDefault();
488
- focusTextarea();
489
- };
490
-
491
- const controlCellStyle: React.CSSProperties | undefined =
492
- rowHeight != null ? { height: `${rowHeight}px` } : undefined;
493
-
494
- const lowerControlAlignStyle: React.CSSProperties = {
495
- marginTop: "auto",
496
- ...controlCellStyle,
497
- };
498
-
499
- const leadingArea = hasLeadingControl ? (
500
- <div
501
- data-slot="textarea-leading-area"
502
- className={cn("flex flex-col h-full", leadingControlClassName)}
503
- >
504
- <div
505
- data-slot="textarea-leading-control"
506
- className="flex items-center mt-auto"
507
- style={lowerControlAlignStyle}
508
- onMouseDown={(e) => {
509
- e.preventDefault();
510
- focusTextarea();
511
- }}
512
- >
513
- {leadingControl}
514
- </div>
515
- </div>
516
- ) : null;
517
-
518
- const trailingArea = hasTrailingControl ? (
519
- <div
520
- data-slot="textarea-trailing-area"
521
- className={cn("flex flex-col h-full mt-auto", trailingControlClassName)}
522
- >
523
- <div
524
- data-slot="textarea-trailing-control"
525
- className="flex items-center"
526
- style={lowerControlAlignStyle}
527
- onMouseDown={(e) => {
528
- e.preventDefault();
529
- focusTextarea();
530
- }}
531
- >
532
- {trailingControl}
533
- </div>
534
- </div>
535
- ) : null;
536
-
537
- return (
538
- <div
539
- data-slot="textarea-wrapper"
540
- className={wrapperClasses}
541
- style={mergedWrapperStyle}
542
- data-size={sizeKey}
543
- data-density={densityKey}
544
- >
545
- <div
546
- data-slot="textarea-box"
547
- className={boxClasses}
548
- data-has-extras={hasExtras ? "true" : "false"}
549
- >
550
- {upperControl && (
551
- <div
552
- data-slot="textarea-upper"
553
- className={cn("flex items-center", upperControlClassName)}
554
- >
555
- {upperControl}
556
- </div>
557
- )}
558
-
559
- <div data-slot="textarea-inner" className={innerRowClasses}>
560
- {leadingArea}
561
-
562
- <div data-slot="textarea-field" className={fieldWrapperClasses}>
563
- <textarea
564
- ref={innerRef}
565
- data-slot="textarea"
566
- className={textareaClasses}
567
- disabled={disabled}
568
- readOnly={readOnly}
569
- aria-required={required ? "true" : undefined}
570
- rows={autoResize ? undefined : baseMinRows}
571
- placeholder={placeholder}
572
- onChange={handleChange}
573
- onFocus={handleFocus}
574
- onBlur={handleBlur}
575
- {...rest}
576
- />
577
-
578
- {hasLeadingIcons && (
579
- <div
580
- ref={leadingIconsRef}
581
- data-slot="textarea-leading-icons"
582
- className="pointer-events-auto absolute left-0 flex items-end"
583
- style={{
584
- gap: leadingGap,
585
- // anchor from base padding, NOT icon-adjusted padding
586
- paddingLeft: "var(--fp-pl-base)",
587
- bottom: "calc(var(--fp-pb, 0px) + 2px)",
588
- }}
589
- onMouseDown={handleIconMouseDown}
590
- >
591
- {resolvedLeadingIcons.map((node, idx) => (
592
- <span
593
- key={idx}
594
- className="flex items-center justify-center"
595
- >
596
- {node}
597
- </span>
598
- ))}
599
- </div>
600
- )}
601
-
602
- {hasTrailingIcons && (
603
- <div
604
- ref={trailingIconsRef}
605
- data-slot="textarea-trailing-icons"
606
- className="pointer-events-auto absolute right-0 flex items-end"
607
- style={{
608
- gap: trailingGap,
609
- paddingRight: "var(--fp-pr-base)",
610
- bottom: "calc(var(--fp-pb, 0px) + 2px)",
611
- }}
612
- onMouseDown={handleIconMouseDown}
613
- >
614
- {resolvedTrailingIcons.map((node, idx) => (
615
- <span
616
- key={idx}
617
- className="flex items-center justify-center"
618
- >
619
- {node}
620
- </span>
621
- ))}
622
- </div>
623
- )}
624
- </div>
625
-
626
- {trailingArea}
627
- </div>
628
- </div>
629
- </div>
630
- );
631
- }
632
- );
633
-
634
- Textarea.displayName = "Textarea";