@versini/ui-textarea 5.0.7 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,25 +1,29 @@
1
- import React from 'react';
2
-
3
- declare const TEXT_AREA_CLASSNAME = "av-text-area";
4
- declare const TEXT_AREA_WRAPPER_CLASSNAME = "av-text-area-wrapper";
5
- declare const TEXT_AREA_HELPER_TEXT_CLASSNAME = "av-text-area-helper-text";
6
- declare const TEXT_AREA_CONTROL_RIGHT_CLASSNAME = "av-text-area__control--right";
7
- declare const TEXT_AREA_CONTROL_LEFT_CLASSNAME = "av-text-area__control--left";
8
-
9
- declare const TextArea: React.ForwardRefExoticComponent<{
10
- label: string;
11
- name: string;
12
- error?: boolean;
13
- focusMode?: "dark" | "light" | "system" | "alt-system";
14
- helperText?: string;
15
- helperTextOnFocus?: boolean;
16
- labelId?: string;
17
- mode?: "dark" | "light" | "system" | "alt-system";
18
- noBorder?: boolean;
19
- raw?: boolean;
20
- rightElement?: React.ReactElement;
21
- leftElement?: React.ReactElement;
22
- textAreaClassName?: string;
23
- } & React.TextareaHTMLAttributes<HTMLTextAreaElement> & React.RefAttributes<HTMLTextAreaElement>>;
24
-
25
- export { TEXT_AREA_CLASSNAME, TEXT_AREA_CONTROL_LEFT_CLASSNAME, TEXT_AREA_CONTROL_RIGHT_CLASSNAME, TEXT_AREA_HELPER_TEXT_CLASSNAME, TEXT_AREA_WRAPPER_CLASSNAME, TextArea };
1
+ import { default as React_2 } from 'react';
2
+
3
+ export declare const TEXT_AREA_CLASSNAME = "av-text-area";
4
+
5
+ export declare const TEXT_AREA_CONTROL_LEFT_CLASSNAME = "av-text-area__control--left";
6
+
7
+ export declare const TEXT_AREA_CONTROL_RIGHT_CLASSNAME = "av-text-area__control--right";
8
+
9
+ export declare const TEXT_AREA_HELPER_TEXT_CLASSNAME = "av-text-area-helper-text";
10
+
11
+ export declare const TEXT_AREA_WRAPPER_CLASSNAME = "av-text-area-wrapper";
12
+
13
+ export declare const TextArea: React_2.ForwardRefExoticComponent<{
14
+ label: string;
15
+ name: string;
16
+ error?: boolean;
17
+ focusMode?: "dark" | "light" | "system" | "alt-system";
18
+ helperText?: string;
19
+ helperTextOnFocus?: boolean;
20
+ labelId?: string;
21
+ mode?: "dark" | "light" | "system" | "alt-system";
22
+ noBorder?: boolean;
23
+ raw?: boolean;
24
+ rightElement?: React_2.ReactElement;
25
+ leftElement?: React_2.ReactElement;
26
+ textAreaClassName?: string;
27
+ } & React_2.TextareaHTMLAttributes<HTMLTextAreaElement> & React_2.RefAttributes<HTMLTextAreaElement>>;
28
+
29
+ export { }
package/dist/index.js CHANGED
@@ -1,22 +1,446 @@
1
- import { TEXT_AREA_CLASSNAME as T, TEXT_AREA_CONTROL_LEFT_CLASSNAME as R, TEXT_AREA_CONTROL_RIGHT_CLASSNAME as e, TEXT_AREA_HELPER_TEXT_CLASSNAME as S, TEXT_AREA_WRAPPER_CLASSNAME as i, TextArea as o } from "./components/TextArea/TextArea.js";
2
1
  /*!
3
- @versini/ui-textarea v5.0.7
2
+ @versini/ui-textarea v5.1.0
4
3
  © 2025 gizmette.com
5
4
  */
6
5
  try {
7
- window.__VERSINI_UI_TEXTAREA__ || (window.__VERSINI_UI_TEXTAREA__ = {
8
- version: "5.0.7",
9
- buildTime: "10/24/2025 07:46 PM EDT",
10
- homepage: "https://github.com/aversini/ui-components",
11
- license: "MIT"
12
- });
13
- } catch {
6
+ if (!window.__VERSINI_UI_TEXTAREA__) {
7
+ window.__VERSINI_UI_TEXTAREA__ = {
8
+ version: "5.1.0",
9
+ buildTime: "11/04/2025 03:43 PM EST",
10
+ homepage: "https://github.com/aversini/ui-components",
11
+ license: "MIT",
12
+ };
13
+ }
14
+ } catch (error) {
15
+ // nothing to declare officer
14
16
  }
15
- export {
16
- T as TEXT_AREA_CLASSNAME,
17
- R as TEXT_AREA_CONTROL_LEFT_CLASSNAME,
18
- e as TEXT_AREA_CONTROL_RIGHT_CLASSNAME,
19
- S as TEXT_AREA_HELPER_TEXT_CLASSNAME,
20
- i as TEXT_AREA_WRAPPER_CLASSNAME,
21
- o as TextArea
17
+
18
+ import { jsx, jsxs } from "react/jsx-runtime";
19
+ import { useMergeRefs, useResizeObserver, useUncontrolled, useUniqueId } from "@versini/ui-hooks";
20
+ import { LiveRegion } from "@versini/ui-liveregion";
21
+ import react, { useLayoutEffect, useRef, useState } from "react";
22
+ import clsx from "clsx";
23
+
24
+ ;// CONCATENATED MODULE: ./src/common/constants.ts
25
+ const TEXT_AREA_CLASSNAME = "av-text-area";
26
+ const TEXT_AREA_WRAPPER_CLASSNAME = "av-text-area-wrapper";
27
+ const TEXT_AREA_HELPER_TEXT_CLASSNAME = "av-text-area-helper-text";
28
+ const TEXT_AREA_CONTROL_RIGHT_CLASSNAME = "av-text-area__control--right";
29
+ const TEXT_AREA_CONTROL_LEFT_CLASSNAME = "av-text-area__control--left";
30
+
31
+ ;// CONCATENATED MODULE: external "react/jsx-runtime"
32
+
33
+ ;// CONCATENATED MODULE: external "@versini/ui-hooks"
34
+
35
+ ;// CONCATENATED MODULE: external "@versini/ui-liveregion"
36
+
37
+ ;// CONCATENATED MODULE: external "react"
38
+
39
+ ;// CONCATENATED MODULE: external "clsx"
40
+
41
+ ;// CONCATENATED MODULE: ./src/components/TextArea/utilities.ts
42
+
43
+
44
+ const getTextAreaBaseClasses = ()=>{
45
+ /**
46
+ * overflow-hidden is needed to prevent the text area from
47
+ * showing a scrollbar. We automatically expand the text area
48
+ * when the user types more than one line, so there is no
49
+ * need for a scrollbar.
50
+ */ return "rounded-md text-base h-20 min-h-[80px] resize-none overflow-hidden px-4 py-7";
22
51
  };
52
+ const getTextAreaColorClasses = ({ mode })=>{
53
+ return clsx({
54
+ "bg-surface-darker text-copy-lighter caret-copy-light": mode === "dark",
55
+ "bg-surface-lighter text-copy-dark caret-copy-dark": mode === "light",
56
+ "bg-surface-lighter text-copy-dark caret-copy-dark dark:bg-surface-darker dark:text-copy-lighter dark:caret-copy-light": mode === "system",
57
+ "bg-surface-darker text-copy-lighter caret-copy-light dark:bg-surface-lighter dark:text-copy-dark dark:caret-copy-dark": mode === "alt-system"
58
+ });
59
+ };
60
+ const getTextAreaFocusClasses = ({ focusMode })=>{
61
+ return clsx("focus:outline focus:outline-2 focus:outline-offset-2", {
62
+ "focus:outline-focus-dark": focusMode === "dark",
63
+ "focus:outline-focus-light": focusMode === "light",
64
+ "focus:outline-focus-light dark:focus:outline-focus-dark": focusMode === "alt-system",
65
+ "focus:outline-focus-dark dark:focus:outline-focus-light": focusMode === "system"
66
+ });
67
+ };
68
+ const getTextAreaBorderClasses = ({ noBorder, error })=>{
69
+ return clsx("border-2", {
70
+ "border-border-dark": !noBorder && !error,
71
+ "focus:border-border-dark": !noBorder && error,
72
+ "border-border-error-dark": !noBorder && error,
73
+ "border-transparent": noBorder
74
+ });
75
+ };
76
+ const getTextAreaLabelClasses = ({ disabled, raw, error, mode, leftElement, rightElement })=>{
77
+ if (raw) {
78
+ return "";
79
+ }
80
+ if (disabled) {
81
+ return clsx("transform translate-y-0 scale-100 absolute px-2 cursor-not-allowed opacity-50 font-medium", {
82
+ "translate-x-[10px]": rightElement === true && !leftElement || !rightElement && !leftElement
83
+ });
84
+ }
85
+ if (!error) {
86
+ return clsx("absolute px-2 cursor-text font-medium transform translate-y-0 scale-100", {
87
+ "translate-x-[10px]": rightElement === true && !leftElement || !rightElement && !leftElement,
88
+ "text-copy-medium": mode === "dark",
89
+ "text-copy-dark": mode === "light",
90
+ "text-copy-dark dark:text-copy-medium": mode === "system",
91
+ "text-copy-medium dark:text-copy-dark": mode === "alt-system"
92
+ });
93
+ }
94
+ if (error) {
95
+ return clsx("absolute px-2 cursor-text font-medium transform translate-y-0 scale-100", {
96
+ "translate-x-[10px]": rightElement === true && !leftElement || !rightElement && !leftElement,
97
+ "text-copy-medium": mode === "dark",
98
+ "text-copy-error-dark": mode === "light",
99
+ "text-copy-error-dark dark:text-copy-error-light": mode === "system",
100
+ "text-copy-medium dark:text-copy-error-dark": mode === "alt-system"
101
+ });
102
+ }
103
+ };
104
+ const getTextAreaHelperTextClasses = ({ error, raw, mode, disabled })=>{
105
+ if (raw) {
106
+ return "";
107
+ }
108
+ if (disabled) {
109
+ return clsx(TEXT_AREA_HELPER_TEXT_CLASSNAME, "absolute px-2 cursor-not-allowed opacity-50 font-medium");
110
+ }
111
+ if (!error) {
112
+ return clsx(TEXT_AREA_HELPER_TEXT_CLASSNAME, "absolute px-2 font-medium", {
113
+ "text-copy-medium": mode === "dark",
114
+ "text-copy-dark": mode === "light",
115
+ "text-copy-dark dark:text-copy-medium": mode === "system",
116
+ "text-copy-medium dark:text-copy-dark": mode === "alt-system"
117
+ });
118
+ }
119
+ if (error) {
120
+ return clsx(TEXT_AREA_HELPER_TEXT_CLASSNAME, "absolute px-2 font-medium", {
121
+ "text-copy-error-light": mode === "dark",
122
+ "text-copy-error-dark": mode === "light",
123
+ "text-copy-error-dark dark:text-copy-error-light": mode === "system",
124
+ "dark:text-copy-error-dark text-copy-error-light": mode === "alt-system"
125
+ });
126
+ }
127
+ };
128
+ const getTextAreaClasses = ({ className, textAreaClassName, raw, focusMode, disabled, noBorder, error, mode, leftElement, rightElement })=>{
129
+ const wrapper = raw ? className : clsx("relative flex w-full flex-col justify-center", TEXT_AREA_WRAPPER_CLASSNAME, className);
130
+ const textArea = raw ? clsx(textAreaClassName) : clsx(TEXT_AREA_CLASSNAME, textAreaClassName, getTextAreaBaseClasses(), getTextAreaColorClasses({
131
+ mode
132
+ }), getTextAreaFocusClasses({
133
+ focusMode
134
+ }), getTextAreaBorderClasses({
135
+ noBorder,
136
+ error
137
+ }), {
138
+ "disabled:cursor-not-allowed disabled:opacity-50": disabled
139
+ });
140
+ const accessibleLabel = raw ? undefined : "sr-only";
141
+ const visibleLabel = getTextAreaLabelClasses({
142
+ disabled,
143
+ raw,
144
+ error,
145
+ mode,
146
+ rightElement,
147
+ leftElement
148
+ });
149
+ const helperText = getTextAreaHelperTextClasses({
150
+ error,
151
+ raw,
152
+ mode,
153
+ disabled
154
+ });
155
+ const rightClasses = raw ? undefined : clsx(TEXT_AREA_CONTROL_RIGHT_CLASSNAME, "absolute");
156
+ const leftClasses = raw ? undefined : clsx(TEXT_AREA_CONTROL_LEFT_CLASSNAME, "absolute");
157
+ return {
158
+ wrapper,
159
+ textArea,
160
+ accessibleLabel,
161
+ visibleLabel,
162
+ helperText,
163
+ rightElement: rightClasses,
164
+ leftElement: leftClasses
165
+ };
166
+ };
167
+ const adjustLabelAndHelperText = ({ scrollHeight, currentHeight, currentLabelOffset = 0, currentHelperTextOffset = 0 })=>{
168
+ const ROW_HEIGHT = 24;
169
+ const TRANSLATION_Y_OFFSET = ROW_HEIGHT / 2;
170
+ let labelOffset, helperTextOffset;
171
+ if (scrollHeight > 0 && scrollHeight !== currentHeight) {
172
+ const diff = scrollHeight - currentHeight;
173
+ const totalRows = Math.abs(diff / ROW_HEIGHT);
174
+ labelOffset = currentLabelOffset + -1 * Math.sign(diff) * (TRANSLATION_Y_OFFSET * totalRows);
175
+ helperTextOffset = currentHelperTextOffset + Math.sign(diff) * (TRANSLATION_Y_OFFSET * totalRows);
176
+ }
177
+ return {
178
+ labelOffset,
179
+ helperTextOffset,
180
+ scrollHeight
181
+ };
182
+ };
183
+
184
+ ;// CONCATENATED MODULE: ./src/components/TextArea/TextArea.tsx
185
+
186
+
187
+
188
+
189
+
190
+
191
+ const TextArea = /*#__PURE__*/ react.forwardRef(({ id, name, label, error = false, raw = false, className, textAreaClassName, mode = "system", focusMode = "system", value, defaultValue, disabled = false, noBorder = false, labelId, helperText = "", helperTextOnFocus = false, rightElement, leftElement, onChange, onFocus, onBlur, ...extraProps }, ref)=>{
192
+ const textAreaRef = useRef(null);
193
+ const mergedTextAreaRef = useMergeRefs([
194
+ ref,
195
+ textAreaRef
196
+ ]);
197
+ const [rightElementRef, rect] = useResizeObserver();
198
+ const [leftElementRef, lRect] = useResizeObserver();
199
+ const textAreaHeightRef = useRef(80);
200
+ const labelOffsetRef = useRef(-25);
201
+ const labelRef = useRef(null);
202
+ const helperTextOffsetRef = useRef(30);
203
+ const helperTextRef = useRef(null);
204
+ const textAreaId = useUniqueId({
205
+ id,
206
+ prefix: `${TEXT_AREA_CLASSNAME}-`
207
+ });
208
+ const [textAreaPaddingRight, setTextAreaPaddingRight] = useState(0);
209
+ const [textAreaPaddingLeft, setTextAreaPaddingLeft] = useState(0);
210
+ const [showHelperText, setShowHelperText] = useState(Boolean(!helperTextOnFocus && helperText));
211
+ const liveErrorMessage = `${name} error, ${helperText}`;
212
+ const textTextAreaClassName = getTextAreaClasses({
213
+ className,
214
+ textAreaClassName,
215
+ error,
216
+ raw,
217
+ focusMode,
218
+ disabled,
219
+ noBorder,
220
+ mode,
221
+ rightElement: Boolean(rightElement),
222
+ leftElement: Boolean(leftElement)
223
+ });
224
+ /**
225
+ * useUncontrolled hook is used to make the textarea
226
+ * both controlled and uncontrolled.
227
+ */ const [userInput, setUserInput] = useUncontrolled({
228
+ value,
229
+ initialControlledDelay: 20,
230
+ defaultValue,
231
+ onChange: (value)=>{
232
+ const e = {
233
+ target: {
234
+ value
235
+ }
236
+ };
237
+ onChange && onChange(e);
238
+ }
239
+ });
240
+ const handleChange = (e)=>{
241
+ setUserInput(e.target.value);
242
+ };
243
+ const handleFocus = (e)=>{
244
+ if (helperTextOnFocus && helperText) {
245
+ setShowHelperText(true);
246
+ }
247
+ onFocus && onFocus(e);
248
+ };
249
+ const handleBlur = (e)=>{
250
+ if (helperTextOnFocus && helperText && !userInput) {
251
+ setShowHelperText(false);
252
+ }
253
+ onBlur && onBlur(e);
254
+ };
255
+ /**
256
+ * This effect is used to add padding to the rightElement so
257
+ * that the input text in the textarea does not overlap with the
258
+ * rightElement.
259
+ */ /* c8 ignore start - ResizeObserver is tough to test... */ useLayoutEffect(()=>{
260
+ if (rect && rect.width) {
261
+ /**
262
+ * - rect.width is the width of the right element (Button, Icon, etc.)
263
+ * - The main input field has default left/right paddings of
264
+ * 16px (px-4) + 2px border (border-2) = 18px
265
+ * - We add 10px to the right padding to give some space between the right
266
+ * element and the input field.
267
+ */ setTextAreaPaddingRight(rect.width + 18 + 10);
268
+ }
269
+ }, [
270
+ rect
271
+ ]);
272
+ /* c8 ignore end */ /**
273
+ * This effect is used to add padding to the leftElement so
274
+ * that the input text in the textarea does not overlap with the
275
+ * leftElement.
276
+ */ /* c8 ignore start - ResizeObserver is tough to test... */ useLayoutEffect(()=>{
277
+ if (lRect && lRect.width) {
278
+ /**
279
+ * - rect.width is the width of the right element (Button, Icon, etc.)
280
+ * - The main input field has default left/right paddings of
281
+ * 16px (px-4) + 2px border (border-2) = 18px
282
+ * - We add 10px to the left padding to give some space between the left
283
+ * element and the input field.
284
+ */ setTextAreaPaddingLeft(lRect.width + 18 + 10);
285
+ }
286
+ }, [
287
+ lRect
288
+ ]);
289
+ /* c8 ignore end */ /**
290
+ * This effect is used to resize the textarea based
291
+ * on the content, so that the user can see all the
292
+ * content they have typed.
293
+ */ useLayoutEffect(()=>{
294
+ if (raw) {
295
+ return;
296
+ }
297
+ if (textAreaRef && textAreaRef.current && userInput !== undefined) {
298
+ textAreaRef.current.style.height = "inherit";
299
+ // Set the height to match the content
300
+ textAreaRef.current.style.height = textAreaRef.current.scrollHeight + "px";
301
+ }
302
+ }, [
303
+ userInput,
304
+ raw
305
+ ]);
306
+ /**
307
+ * This section is to toggle the transitions.
308
+ * This is to prevent the label and helper text from
309
+ * animating when the user is typing. The animation is
310
+ * re-enabled when there is nothing in the textarea.
311
+ *
312
+ * The reason for the timeout is to prevent it to be
313
+ * re-enabled too soon when the user clears out the
314
+ * whole textarea.
315
+ */ useLayoutEffect(()=>{
316
+ if (raw) {
317
+ return;
318
+ }
319
+ setTimeout(()=>{
320
+ labelRef?.current?.style.setProperty("--av-text-area-wrapper-transition", !userInput ? "all 0.2s ease-out" : "none");
321
+ }, 0);
322
+ }, [
323
+ userInput,
324
+ raw
325
+ ]);
326
+ /**
327
+ * This effect is used to adjust the label and helper text
328
+ * when the height of the textarea changes.
329
+ * This is done by calculating the difference in
330
+ * height and then adjusting the label and helper
331
+ * text by that amount.
332
+ */ useLayoutEffect(()=>{
333
+ if (raw) {
334
+ return;
335
+ }
336
+ if (textAreaRef && textAreaRef.current && userInput !== undefined) {
337
+ const { labelOffset, helperTextOffset, scrollHeight } = adjustLabelAndHelperText({
338
+ scrollHeight: textAreaRef.current.scrollHeight,
339
+ currentHeight: textAreaHeightRef.current,
340
+ currentLabelOffset: labelOffsetRef.current,
341
+ currentHelperTextOffset: helperTextOffsetRef.current
342
+ });
343
+ /* v8 ignore next 7 */ if (labelOffset) {
344
+ labelOffsetRef.current = labelOffset;
345
+ labelRef?.current?.style.setProperty("--av-text-area-label", `${labelOffset}px`);
346
+ }
347
+ /* v8 ignore next 7 */ if (helperTextOffset) {
348
+ helperTextOffsetRef.current = helperTextOffset;
349
+ helperTextRef?.current?.style.setProperty("--av-text-area-helper-text", `${helperTextOffset}px`);
350
+ }
351
+ textAreaHeightRef.current = scrollHeight || textAreaHeightRef.current;
352
+ }
353
+ }, [
354
+ userInput,
355
+ raw
356
+ ]);
357
+ /**
358
+ * If there is a left element, we need to translate the label on the x-axis
359
+ * to the right so that it does not overlap with the left element.
360
+ * NOTE: 12px is the default translate if there are no left elements.
361
+ */ if (lRect.width > 0) {
362
+ labelRef?.current?.style.setProperty("--tw-translate-x", `${12 + lRect.width + 5}px`);
363
+ }
364
+ return /*#__PURE__*/ jsxs("div", {
365
+ className: textTextAreaClassName.wrapper,
366
+ children: [
367
+ /*#__PURE__*/ jsx("label", {
368
+ htmlFor: textAreaId,
369
+ id: labelId,
370
+ className: textTextAreaClassName.accessibleLabel,
371
+ children: label
372
+ }),
373
+ leftElement && /*#__PURE__*/ jsx("div", {
374
+ ref: leftElementRef,
375
+ className: textTextAreaClassName.leftElement,
376
+ children: leftElement
377
+ }),
378
+ /*#__PURE__*/ jsx("textarea", {
379
+ ref: mergedTextAreaRef,
380
+ id: textAreaId,
381
+ name: name,
382
+ disabled: disabled,
383
+ placeholder: !raw ? " " : undefined,
384
+ className: textTextAreaClassName.textArea,
385
+ rows: 1,
386
+ ...helperText && {
387
+ "aria-describedby": `${textAreaId}-helper`
388
+ },
389
+ ...error && {
390
+ "aria-invalid": "true"
391
+ },
392
+ ...rightElement && !leftElement && !raw && {
393
+ style: {
394
+ paddingRight: textAreaPaddingRight
395
+ }
396
+ },
397
+ ...leftElement && !rightElement && !raw && {
398
+ style: {
399
+ paddingLeft: textAreaPaddingLeft
400
+ }
401
+ },
402
+ ...rightElement && leftElement && !raw && {
403
+ style: {
404
+ paddingRight: textAreaPaddingRight,
405
+ paddingLeft: textAreaPaddingLeft
406
+ }
407
+ },
408
+ value: userInput,
409
+ onChange: handleChange,
410
+ onFocus: handleFocus,
411
+ onBlur: handleBlur,
412
+ ...extraProps
413
+ }),
414
+ !raw && /*#__PURE__*/ jsx("label", {
415
+ ref: labelRef,
416
+ "aria-hidden": true,
417
+ htmlFor: textAreaId,
418
+ className: `${textTextAreaClassName.visibleLabel}`,
419
+ children: label
420
+ }),
421
+ showHelperText && /*#__PURE__*/ jsx("div", {
422
+ ref: helperTextRef,
423
+ id: `${textAreaId}-helper`,
424
+ className: textTextAreaClassName.helperText,
425
+ children: helperText
426
+ }),
427
+ rightElement && /*#__PURE__*/ jsx("div", {
428
+ ref: rightElementRef,
429
+ className: textTextAreaClassName.rightElement,
430
+ children: rightElement
431
+ }),
432
+ error && helperText && /*#__PURE__*/ jsx(LiveRegion, {
433
+ politeness: "polite",
434
+ clearAnnouncementDelay: 500,
435
+ children: liveErrorMessage
436
+ })
437
+ ]
438
+ });
439
+ });
440
+ TextArea.displayName = "TextArea";
441
+
442
+ ;// CONCATENATED MODULE: ./src/components/index.ts
443
+
444
+
445
+
446
+ export { TEXT_AREA_CLASSNAME, TEXT_AREA_CONTROL_LEFT_CLASSNAME, TEXT_AREA_CONTROL_RIGHT_CLASSNAME, TEXT_AREA_HELPER_TEXT_CLASSNAME, TEXT_AREA_WRAPPER_CLASSNAME, TextArea };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@versini/ui-textarea",
3
- "version": "5.0.7",
3
+ "version": "5.1.0",
4
4
  "license": "MIT",
5
5
  "author": "Arno Versini",
6
6
  "publishConfig": {
@@ -20,13 +20,13 @@
20
20
  ],
21
21
  "scripts": {
22
22
  "build:check": "tsc",
23
- "build:js": "vite build",
24
- "build:types": "tsup",
25
- "build": "npm-run-all --serial clean build:check build:js build:types",
23
+ "build:js": "rslib build",
24
+ "build:types": "echo 'Types now built with rslib'",
25
+ "build": "npm-run-all --serial clean build:check build:js",
26
26
  "clean": "rimraf dist tmp",
27
- "dev:js": "vite build --watch --mode development",
28
- "dev:types": "tsup --watch src",
29
- "dev": "npm-run-all clean --parallel dev:js dev:types",
27
+ "dev:js": "rslib build --watch",
28
+ "dev:types": "echo 'Types now watched with rslib'",
29
+ "dev": "rslib build --watch",
30
30
  "lint": "biome lint src",
31
31
  "lint:fix": "biome check src --write --no-errors-on-unmatched",
32
32
  "prettier": "biome check --write --no-errors-on-unmatched",
@@ -42,13 +42,13 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "@tailwindcss/typography": "0.5.19",
45
- "@versini/ui-hooks": "5.1.1",
46
- "@versini/ui-liveregion": "3.0.2",
45
+ "@versini/ui-hooks": "5.2.0",
46
+ "@versini/ui-liveregion": "3.1.0",
47
47
  "clsx": "2.1.1",
48
48
  "tailwindcss": "4.1.16"
49
49
  },
50
50
  "sideEffects": [
51
51
  "**/*.css"
52
52
  ],
53
- "gitHead": "26763e1e738bcdcb0806c39612161666e4bc4459"
53
+ "gitHead": "7484ad443b77ef31e52ae3a7d88b8129bc6cdf1d"
54
54
  }
@@ -1,331 +0,0 @@
1
- import { jsxs as oe, jsx as h } from "react/jsx-runtime";
2
- import { useMergeRefs as ce, useResizeObserver as B, useUniqueId as le, useUncontrolled as ne } from "@versini/ui-hooks";
3
- import { LiveRegion as ie } from "@versini/ui-liveregion";
4
- import ue, { useRef as T, useState as H, useLayoutEffect as m } from "react";
5
- import o from "clsx";
6
- const U = "av-text-area", de = "av-text-area-wrapper", I = "av-text-area-helper-text", fe = "av-text-area__control--right", xe = "av-text-area__control--left", pe = () => "rounded-md text-base h-20 min-h-[80px] resize-none overflow-hidden px-4 py-7", ye = ({ mode: e }) => o({
7
- "bg-surface-darker text-copy-lighter caret-copy-light": e === "dark",
8
- "bg-surface-lighter text-copy-dark caret-copy-dark": e === "light",
9
- "bg-surface-lighter text-copy-dark caret-copy-dark dark:bg-surface-darker dark:text-copy-lighter dark:caret-copy-light": e === "system",
10
- "bg-surface-darker text-copy-lighter caret-copy-light dark:bg-surface-lighter dark:text-copy-dark dark:caret-copy-dark": e === "alt-system"
11
- }), he = ({
12
- focusMode: e
13
- }) => o("focus:outline focus:outline-2 focus:outline-offset-2", {
14
- "focus:outline-focus-dark": e === "dark",
15
- "focus:outline-focus-light": e === "light",
16
- "focus:outline-focus-light dark:focus:outline-focus-dark": e === "alt-system",
17
- "focus:outline-focus-dark dark:focus:outline-focus-light": e === "system"
18
- }), ge = ({
19
- noBorder: e,
20
- error: a
21
- }) => o("border-2", {
22
- "border-border-dark": !e && !a,
23
- "focus:border-border-dark": !e && a,
24
- "border-border-error-dark": !e && a,
25
- "border-transparent": e
26
- }), ke = ({
27
- disabled: e,
28
- raw: a,
29
- error: t,
30
- mode: s,
31
- leftElement: r,
32
- rightElement: c
33
- }) => {
34
- if (a)
35
- return "";
36
- if (e)
37
- return o(
38
- "transform translate-y-0 scale-100 absolute px-2 cursor-not-allowed opacity-50 font-medium",
39
- {
40
- "translate-x-[10px]": c === !0 && !r || !c && !r
41
- }
42
- );
43
- if (!t)
44
- return o(
45
- "absolute px-2 cursor-text font-medium transform translate-y-0 scale-100",
46
- {
47
- "translate-x-[10px]": c === !0 && !r || !c && !r,
48
- "text-copy-medium": s === "dark",
49
- "text-copy-dark": s === "light",
50
- "text-copy-dark dark:text-copy-medium": s === "system",
51
- "text-copy-medium dark:text-copy-dark": s === "alt-system"
52
- }
53
- );
54
- if (t)
55
- return o(
56
- "absolute px-2 cursor-text font-medium transform translate-y-0 scale-100",
57
- {
58
- "translate-x-[10px]": c === !0 && !r || !c && !r,
59
- "text-copy-medium": s === "dark",
60
- "text-copy-error-dark": s === "light",
61
- "text-copy-error-dark dark:text-copy-error-light": s === "system",
62
- "text-copy-medium dark:text-copy-error-dark": s === "alt-system"
63
- }
64
- );
65
- }, Ae = ({
66
- error: e,
67
- raw: a,
68
- mode: t,
69
- disabled: s
70
- }) => {
71
- if (a)
72
- return "";
73
- if (s)
74
- return o(
75
- I,
76
- "absolute px-2 cursor-not-allowed opacity-50 font-medium"
77
- );
78
- if (!e)
79
- return o(I, "absolute px-2 font-medium", {
80
- "text-copy-medium": t === "dark",
81
- "text-copy-dark": t === "light",
82
- "text-copy-dark dark:text-copy-medium": t === "system",
83
- "text-copy-medium dark:text-copy-dark": t === "alt-system"
84
- });
85
- if (e)
86
- return o(I, "absolute px-2 font-medium", {
87
- "text-copy-error-light": t === "dark",
88
- "text-copy-error-dark": t === "light",
89
- "text-copy-error-dark dark:text-copy-error-light": t === "system",
90
- "dark:text-copy-error-dark text-copy-error-light": t === "alt-system"
91
- });
92
- }, Te = ({
93
- className: e,
94
- textAreaClassName: a,
95
- raw: t,
96
- focusMode: s,
97
- disabled: r,
98
- noBorder: c,
99
- error: u,
100
- mode: d,
101
- leftElement: p,
102
- rightElement: g
103
- }) => {
104
- const N = t ? e : o(
105
- "relative flex w-full flex-col justify-center",
106
- de,
107
- e
108
- ), _ = t ? o(a) : o(
109
- U,
110
- a,
111
- pe(),
112
- ye({ mode: d }),
113
- he({ focusMode: s }),
114
- ge({
115
- noBorder: c,
116
- error: u
117
- }),
118
- {
119
- "disabled:cursor-not-allowed disabled:opacity-50": r
120
- }
121
- ), O = t ? void 0 : "sr-only", S = ke({
122
- disabled: r,
123
- raw: t,
124
- error: u,
125
- mode: d,
126
- rightElement: g,
127
- leftElement: p
128
- }), n = Ae({
129
- error: u,
130
- raw: t,
131
- mode: d,
132
- disabled: r
133
- }), b = t ? void 0 : o(fe, "absolute"), f = t ? void 0 : o(xe, "absolute");
134
- return {
135
- wrapper: N,
136
- textArea: _,
137
- accessibleLabel: O,
138
- visibleLabel: S,
139
- helperText: n,
140
- rightElement: b,
141
- leftElement: f
142
- };
143
- }, be = ({
144
- scrollHeight: e,
145
- currentHeight: a,
146
- currentLabelOffset: t = 0,
147
- currentHelperTextOffset: s = 0
148
- }) => {
149
- let u, d;
150
- if (e > 0 && e !== a) {
151
- const p = e - a, g = Math.abs(p / 24);
152
- u = t + -1 * Math.sign(p) * (12 * g), d = s + Math.sign(p) * (12 * g);
153
- }
154
- return {
155
- labelOffset: u,
156
- helperTextOffset: d,
157
- scrollHeight: e
158
- };
159
- }, Re = ue.forwardRef(
160
- ({
161
- id: e,
162
- name: a,
163
- label: t,
164
- error: s = !1,
165
- raw: r = !1,
166
- className: c,
167
- textAreaClassName: u,
168
- mode: d = "system",
169
- focusMode: p = "system",
170
- value: g,
171
- defaultValue: N,
172
- disabled: _ = !1,
173
- noBorder: O = !1,
174
- labelId: S,
175
- helperText: n = "",
176
- helperTextOnFocus: b = !1,
177
- rightElement: f,
178
- leftElement: k,
179
- onChange: P,
180
- onFocus: F,
181
- onBlur: $,
182
- ...Y
183
- }, z) => {
184
- const i = T(null), D = ce([z, i]), [q, v] = B(), [J, A] = B(), E = T(80), M = T(-25), L = T(null), X = T(30), j = T(null), R = le({ id: e, prefix: `${U}-` }), [w, K] = H(0), [G, Q] = H(0), [V, W] = H(
185
- !!(!b && n)
186
- ), Z = `${a} error, ${n}`, y = Te({
187
- className: c,
188
- textAreaClassName: u,
189
- error: s,
190
- raw: r,
191
- focusMode: p,
192
- disabled: _,
193
- noBorder: O,
194
- mode: d,
195
- rightElement: !!f,
196
- leftElement: !!k
197
- }), [x, ee] = ne({
198
- value: g,
199
- initialControlledDelay: 20,
200
- defaultValue: N,
201
- onChange: (l) => {
202
- P && P({
203
- target: {
204
- value: l
205
- }
206
- });
207
- }
208
- }), te = (l) => {
209
- ee(l.target.value);
210
- }, re = (l) => {
211
- b && n && W(!0), F && F(l);
212
- }, se = (l) => {
213
- b && n && !x && W(!1), $ && $(l);
214
- };
215
- return m(() => {
216
- v && v.width && K(v.width + 18 + 10);
217
- }, [v]), m(() => {
218
- A && A.width && Q(A.width + 18 + 10);
219
- }, [A]), m(() => {
220
- r || i && i.current && x !== void 0 && (i.current.style.height = "inherit", i.current.style.height = i.current.scrollHeight + "px");
221
- }, [x, r]), m(() => {
222
- r || setTimeout(() => {
223
- L?.current?.style.setProperty(
224
- "--av-text-area-wrapper-transition",
225
- x ? "none" : "all 0.2s ease-out"
226
- );
227
- }, 0);
228
- }, [x, r]), m(() => {
229
- if (!r && i && i.current && x !== void 0) {
230
- const { labelOffset: l, helperTextOffset: C, scrollHeight: ae } = be({
231
- scrollHeight: i.current.scrollHeight,
232
- currentHeight: E.current,
233
- currentLabelOffset: M.current,
234
- currentHelperTextOffset: X.current
235
- });
236
- l && (M.current = l, L?.current?.style.setProperty(
237
- "--av-text-area-label",
238
- `${l}px`
239
- )), C && (X.current = C, j?.current?.style.setProperty(
240
- "--av-text-area-helper-text",
241
- `${C}px`
242
- )), E.current = ae || E.current;
243
- }
244
- }, [x, r]), A.width > 0 && L?.current?.style.setProperty(
245
- "--tw-translate-x",
246
- `${12 + A.width + 5}px`
247
- ), /* @__PURE__ */ oe("div", { className: y.wrapper, children: [
248
- /* @__PURE__ */ h(
249
- "label",
250
- {
251
- htmlFor: R,
252
- id: S,
253
- className: y.accessibleLabel,
254
- children: t
255
- }
256
- ),
257
- k && /* @__PURE__ */ h(
258
- "div",
259
- {
260
- ref: J,
261
- className: y.leftElement,
262
- children: k
263
- }
264
- ),
265
- /* @__PURE__ */ h(
266
- "textarea",
267
- {
268
- ref: D,
269
- id: R,
270
- name: a,
271
- disabled: _,
272
- placeholder: r ? void 0 : " ",
273
- className: y.textArea,
274
- rows: 1,
275
- ...n && { "aria-describedby": `${R}-helper` },
276
- ...s && { "aria-invalid": "true" },
277
- ...f && !k && !r && { style: { paddingRight: w } },
278
- ...k && !f && !r && { style: { paddingLeft: G } },
279
- ...f && k && !r && {
280
- style: {
281
- paddingRight: w,
282
- paddingLeft: G
283
- }
284
- },
285
- value: x,
286
- onChange: te,
287
- onFocus: re,
288
- onBlur: se,
289
- ...Y
290
- }
291
- ),
292
- !r && /* @__PURE__ */ h(
293
- "label",
294
- {
295
- ref: L,
296
- "aria-hidden": !0,
297
- htmlFor: R,
298
- className: `${y.visibleLabel}`,
299
- children: t
300
- }
301
- ),
302
- V && /* @__PURE__ */ h(
303
- "div",
304
- {
305
- ref: j,
306
- id: `${R}-helper`,
307
- className: y.helperText,
308
- children: n
309
- }
310
- ),
311
- f && /* @__PURE__ */ h(
312
- "div",
313
- {
314
- ref: q,
315
- className: y.rightElement,
316
- children: f
317
- }
318
- ),
319
- s && n && /* @__PURE__ */ h(ie, { politeness: "polite", clearAnnouncementDelay: 500, children: Z })
320
- ] });
321
- }
322
- );
323
- Re.displayName = "TextArea";
324
- export {
325
- U as TEXT_AREA_CLASSNAME,
326
- xe as TEXT_AREA_CONTROL_LEFT_CLASSNAME,
327
- fe as TEXT_AREA_CONTROL_RIGHT_CLASSNAME,
328
- I as TEXT_AREA_HELPER_TEXT_CLASSNAME,
329
- de as TEXT_AREA_WRAPPER_CLASSNAME,
330
- Re as TextArea
331
- };