@versini/ui-textinput 4.0.6 → 4.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.
package/README.md CHANGED
@@ -1,3 +1,428 @@
1
1
  # @versini/ui-textinput
2
2
 
3
- A simple text input component for React.
3
+ [![npm version](https://img.shields.io/npm/v/@versini/ui-textinput?style=flat-square)](https://www.npmjs.com/package/@versini/ui-textinput)
4
+
5
+ > Accessible and feature-rich React text input components built with TypeScript and TailwindCSS.
6
+
7
+ The TextInput package provides standard text inputs and masked text inputs with comprehensive form features including validation states, helper text, and customizable styling. Perfect for building accessible forms and user interfaces.
8
+
9
+ ## Table of Contents
10
+
11
+ - [Features](#features)
12
+ - [Installation](#installation)
13
+ - [Usage](#usage)
14
+ - [API](#api)
15
+
16
+ ## Features
17
+
18
+ - **🎯 Multiple Input Types**: TextInput and TextInputMask components
19
+ - **♿ Accessible**: WCAG compliant with proper labeling and keyboard navigation
20
+ - **🎨 Customizable**: Multiple sizes, themes, and styling options
21
+ - **🔧 Form Integration**: Built-in validation states and helper text
22
+ - **🌲 Tree-shakeable**: Lightweight and optimized for bundle size
23
+ - **🔧 TypeScript**: Fully typed with comprehensive prop definitions
24
+ - **🎭 Theme Support**: Built-in support for light, dark, and system themes
25
+
26
+ ## Installation
27
+
28
+ ```bash
29
+ npm install @versini/ui-textinput
30
+ ```
31
+
32
+ > **Note**: This component requires TailwindCSS and the `@versini/ui-styles` plugin for proper styling. See the [root README](../../README.md#tailwindcss-setup) for complete setup instructions.
33
+
34
+ ## Usage
35
+
36
+ ### Basic TextInput
37
+
38
+ ```tsx
39
+ import { TextInput } from "@versini/ui-textinput";
40
+
41
+ function App() {
42
+ return (
43
+ <TextInput
44
+ label="Email Address"
45
+ name="email"
46
+ type="email"
47
+ placeholder="Enter your email"
48
+ />
49
+ );
50
+ }
51
+ ```
52
+
53
+ ### TextInput with Helper Text
54
+
55
+ ```tsx
56
+ import { TextInput } from "@versini/ui-textinput";
57
+
58
+ function App() {
59
+ return (
60
+ <TextInput
61
+ label="Password"
62
+ name="password"
63
+ type="password"
64
+ helperText="Must be at least 8 characters long"
65
+ placeholder="Enter your password"
66
+ />
67
+ );
68
+ }
69
+ ```
70
+
71
+ ### TextInput with Error State
72
+
73
+ ```tsx
74
+ import { TextInput } from "@versini/ui-textinput";
75
+
76
+ function App() {
77
+ return (
78
+ <TextInput
79
+ label="Username"
80
+ name="username"
81
+ error
82
+ helperText="Username is already taken"
83
+ defaultValue="invalid-username"
84
+ />
85
+ );
86
+ }
87
+ ```
88
+
89
+ ### TextInput with Right Element
90
+
91
+ ```tsx
92
+ import { TextInput } from "@versini/ui-textinput";
93
+ import { Button } from "@versini/ui-button";
94
+
95
+ function App() {
96
+ return (
97
+ <TextInput
98
+ label="Search"
99
+ name="search"
100
+ placeholder="Search for anything..."
101
+ rightElement={
102
+ <Button size="small" variant="primary">
103
+ Search
104
+ </Button>
105
+ }
106
+ />
107
+ );
108
+ }
109
+ ```
110
+
111
+ ### TextInputMask (Password with Toggle)
112
+
113
+ ```tsx
114
+ import { TextInputMask } from "@versini/ui-textinput";
115
+ import { ButtonIcon } from "@versini/ui-button";
116
+ import { IconEye, IconEyeOff } from "@versini/ui-icons";
117
+ import { useState } from "react";
118
+
119
+ function App() {
120
+ const [masked, setMasked] = useState(true);
121
+
122
+ return (
123
+ <TextInputMask
124
+ label="Password"
125
+ name="password"
126
+ type={masked ? "password" : "text"}
127
+ onMaskChange={setMasked}
128
+ rightElement={
129
+ <ButtonIcon
130
+ label={masked ? "Show password" : "Hide password"}
131
+ noBackground
132
+ >
133
+ {masked ? <IconEye /> : <IconEyeOff />}
134
+ </ButtonIcon>
135
+ }
136
+ />
137
+ );
138
+ }
139
+ ```
140
+
141
+ ## API
142
+
143
+ ### TextInput Props
144
+
145
+ | Prop | Type | Default | Description |
146
+ | --------------------- | ----------------------------------------------- | ---------- | -------------------------------------------------- |
147
+ | label | `string` | - | The label of the TextInput (required) |
148
+ | name | `string` | - | The name of the TextInput (required) |
149
+ | rightElement | `React.ReactElement` | - | Element to render on the right side |
150
+ | rightElementClassName | `string` | - | Extra classes for the right element container |
151
+ | error | `boolean` | `false` | Whether the TextInput is in error state |
152
+ | helperText | `string` | - | Text to add to the bottom of the TextInput |
153
+ | size | `"xs" \| "sm" \| "md" \| "lg" \| "xl"` | `"md"` | Controls input height and padding |
154
+ | mode | `"dark" \| "light" \| "system" \| "alt-system"` | `"system"` | The mode of TextInput (controls color) |
155
+ | focusMode | `"dark" \| "light" \| "system" \| "alt-system"` | `"system"` | The focus ring color mode |
156
+ | labelHidden | `boolean` | `false` | Hides the label visually but retains accessibility |
157
+ | labelId | `string` | - | Id to use for the TextInput label |
158
+ | noBorder | `boolean` | `false` | Whether the TextInput has a border |
159
+ | raw | `boolean` | `false` | Whether to render with styles |
160
+ | inputClassName | `string` | - | CSS class(es) for the actual input element |
161
+
162
+ _Also supports all standard HTML input element attributes_
163
+
164
+ ### TextInputMask Props
165
+
166
+ | Prop | Type | Default | Description |
167
+ | ------------------- | --------------------------- | ------- | ---------------------------------------------- |
168
+ | rightElement | `React.ReactElement` | - | Element to render on the right side (required) |
169
+ | onMaskChange | `(masked: boolean) => void` | - | Callback fired when mask state changes |
170
+ | onTextInputMaskBlur | `() => void` | - | Callback fired when user blurs out |
171
+
172
+ _Also supports all TextInput props except rightElement is required_
173
+
174
+ ## Examples
175
+
176
+ ### Form with Validation
177
+
178
+ ```tsx
179
+ import { TextInput } from "@versini/ui-textinput";
180
+ import { Button } from "@versini/ui-button";
181
+ import { useState } from "react";
182
+
183
+ function ValidationForm() {
184
+ const [errors, setErrors] = useState({});
185
+ const [values, setValues] = useState({
186
+ email: "",
187
+ password: "",
188
+ confirmPassword: ""
189
+ });
190
+
191
+ const handleSubmit = (e) => {
192
+ e.preventDefault();
193
+ // Validation logic here
194
+ };
195
+
196
+ return (
197
+ <form onSubmit={handleSubmit} className="space-y-4 max-w-md">
198
+ <TextInput
199
+ label="Email"
200
+ name="email"
201
+ type="email"
202
+ value={values.email}
203
+ onChange={(e) => setValues({ ...values, email: e.target.value })}
204
+ error={!!errors.email}
205
+ helperText={errors.email || "We'll never share your email"}
206
+ required
207
+ />
208
+
209
+ <TextInput
210
+ label="Password"
211
+ name="password"
212
+ type="password"
213
+ value={values.password}
214
+ onChange={(e) => setValues({ ...values, password: e.target.value })}
215
+ error={!!errors.password}
216
+ helperText={errors.password || "Must be at least 8 characters"}
217
+ required
218
+ />
219
+
220
+ <TextInput
221
+ label="Confirm Password"
222
+ name="confirmPassword"
223
+ type="password"
224
+ value={values.confirmPassword}
225
+ onChange={(e) =>
226
+ setValues({ ...values, confirmPassword: e.target.value })
227
+ }
228
+ error={!!errors.confirmPassword}
229
+ helperText={errors.confirmPassword}
230
+ required
231
+ />
232
+
233
+ <Button type="submit" fullWidth>
234
+ Create Account
235
+ </Button>
236
+ </form>
237
+ );
238
+ }
239
+ ```
240
+
241
+ ### Different Sizes
242
+
243
+ ```tsx
244
+ import { TextInput } from "@versini/ui-textinput";
245
+
246
+ function SizeExamples() {
247
+ return (
248
+ <div className="space-y-4">
249
+ <TextInput
250
+ label="Extra Small"
251
+ name="xs"
252
+ size="xs"
253
+ placeholder="Extra small input"
254
+ />
255
+ <TextInput label="Small" name="sm" size="sm" placeholder="Small input" />
256
+ <TextInput
257
+ label="Medium"
258
+ name="md"
259
+ size="md"
260
+ placeholder="Medium input (default)"
261
+ />
262
+ <TextInput label="Large" name="lg" size="lg" placeholder="Large input" />
263
+ <TextInput
264
+ label="Extra Large"
265
+ name="xl"
266
+ size="xl"
267
+ placeholder="Extra large input"
268
+ />
269
+ </div>
270
+ );
271
+ }
272
+ ```
273
+
274
+ ### Search Input with Icon
275
+
276
+ ```tsx
277
+ import { TextInput } from "@versini/ui-textinput";
278
+ import { ButtonIcon } from "@versini/ui-button";
279
+ import { IconSearch } from "@versini/ui-icons";
280
+
281
+ function SearchInput() {
282
+ return (
283
+ <TextInput
284
+ label="Search Products"
285
+ name="search"
286
+ placeholder="Search for products..."
287
+ rightElement={
288
+ <ButtonIcon label="Search" variant="primary" size="small" noBackground>
289
+ <IconSearch />
290
+ </ButtonIcon>
291
+ }
292
+ rightElementClassName="mr-2"
293
+ />
294
+ );
295
+ }
296
+ ```
297
+
298
+ ### Clearable Input
299
+
300
+ ```tsx
301
+ import { TextInput } from "@versini/ui-textinput";
302
+ import { ButtonIcon } from "@versini/ui-button";
303
+ import { IconClose } from "@versini/ui-icons";
304
+ import { useState } from "react";
305
+
306
+ function ClearableInput() {
307
+ const [value, setValue] = useState("");
308
+
309
+ return (
310
+ <TextInput
311
+ label="Search Query"
312
+ name="query"
313
+ value={value}
314
+ onChange={(e) => setValue(e.target.value)}
315
+ placeholder="Type to search..."
316
+ rightElement={
317
+ value ? (
318
+ <ButtonIcon label="Clear" noBackground onClick={() => setValue("")}>
319
+ <IconClose />
320
+ </ButtonIcon>
321
+ ) : undefined
322
+ }
323
+ />
324
+ );
325
+ }
326
+ ```
327
+
328
+ ### Password Input with Strength Indicator
329
+
330
+ ```tsx
331
+ import { TextInputMask } from "@versini/ui-textinput";
332
+ import { ButtonIcon } from "@versini/ui-button";
333
+ import { IconEye, IconEyeOff } from "@versini/ui-icons";
334
+ import { useState } from "react";
335
+
336
+ function PasswordWithStrength() {
337
+ const [password, setPassword] = useState("");
338
+ const [masked, setMasked] = useState(true);
339
+
340
+ const getPasswordStrength = (password) => {
341
+ // Simple strength calculation
342
+ let strength = 0;
343
+ if (password.length >= 8) strength++;
344
+ if (/[A-Z]/.test(password)) strength++;
345
+ if (/[0-9]/.test(password)) strength++;
346
+ if (/[^A-Za-z0-9]/.test(password)) strength++;
347
+ return strength;
348
+ };
349
+
350
+ const strength = getPasswordStrength(password);
351
+ const strengthColors = ["red", "orange", "yellow", "green"];
352
+ const strengthLabels = ["Weak", "Fair", "Good", "Strong"];
353
+
354
+ return (
355
+ <div className="space-y-2">
356
+ <TextInputMask
357
+ label="Password"
358
+ name="password"
359
+ type={masked ? "password" : "text"}
360
+ value={password}
361
+ onChange={(e) => setPassword(e.target.value)}
362
+ onMaskChange={setMasked}
363
+ rightElement={
364
+ <ButtonIcon
365
+ label={masked ? "Show password" : "Hide password"}
366
+ noBackground
367
+ >
368
+ {masked ? <IconEye /> : <IconEyeOff />}
369
+ </ButtonIcon>
370
+ }
371
+ />
372
+
373
+ {password && (
374
+ <div className="space-y-1">
375
+ <div className="flex space-x-1">
376
+ {[...Array(4)].map((_, i) => (
377
+ <div
378
+ key={i}
379
+ className={`h-1 flex-1 rounded ${
380
+ i < strength
381
+ ? `bg-${strengthColors[strength - 1]}-500`
382
+ : "bg-gray-200"
383
+ }`}
384
+ />
385
+ ))}
386
+ </div>
387
+ <p className="text-sm text-gray-600">
388
+ Password strength: {strengthLabels[strength - 1] || "Too weak"}
389
+ </p>
390
+ </div>
391
+ )}
392
+ </div>
393
+ );
394
+ }
395
+ ```
396
+
397
+ ### Theme Variations
398
+
399
+ ```tsx
400
+ import { TextInput } from "@versini/ui-textinput";
401
+
402
+ function ThemeVariations() {
403
+ return (
404
+ <div className="space-y-4">
405
+ <TextInput
406
+ label="Light Theme"
407
+ name="light"
408
+ mode="light"
409
+ placeholder="Light theme input"
410
+ />
411
+
412
+ <TextInput
413
+ label="Dark Theme"
414
+ name="dark"
415
+ mode="dark"
416
+ placeholder="Dark theme input"
417
+ />
418
+
419
+ <TextInput
420
+ label="System Theme"
421
+ name="system"
422
+ mode="system"
423
+ placeholder="System theme input"
424
+ />
425
+ </div>
426
+ );
427
+ }
428
+ ```
@@ -1,14 +1,14 @@
1
- import { jsx as f, jsxs as H } from "react/jsx-runtime";
2
- import z, { useRef as y, useCallback as W, useEffect as _, useState as $, useMemo as J, useId as K, useReducer as Q, useLayoutEffect as L } from "react";
1
+ import { jsx as f, jsxs as q } from "react/jsx-runtime";
2
+ import G, { useRef as y, useCallback as H, useEffect as R, useState as L, useMemo as z, useId as W, useReducer as J, useLayoutEffect as _ } from "react";
3
3
  import c from "clsx";
4
- const P = "av-text-input", me = "av-text-input-simple", Y = "av-text-input-wrapper", I = "av-text-input-helper-text";
5
- function Z() {
4
+ const S = "av-text-input", he = "av-text-input-simple", K = "av-text-input-wrapper", N = "av-text-input-helper-text";
5
+ function Q() {
6
6
  const e = y(!1);
7
- return _(() => (e.current = !0, () => {
7
+ return R(() => (e.current = !0, () => {
8
8
  e.current = !1;
9
- }), []), W(() => e.current, []);
9
+ }), []), H(() => e.current, []);
10
10
  }
11
- const B = {
11
+ const Y = {
12
12
  x: 0,
13
13
  y: 0,
14
14
  width: 0,
@@ -18,19 +18,19 @@ const B = {
18
18
  bottom: 0,
19
19
  right: 0
20
20
  };
21
- function ee(e) {
22
- const n = Z(), t = y(0), r = y(null), [a, o] = $(B), l = J(() => typeof ResizeObserver > "u" ? null : new ResizeObserver((p) => {
21
+ function Z(e) {
22
+ const n = Q(), t = y(0), r = y(null), [a, o] = L(Y), l = z(() => typeof ResizeObserver > "u" ? null : new ResizeObserver((p) => {
23
23
  const i = p[0];
24
24
  i && (cancelAnimationFrame(t.current), t.current = requestAnimationFrame(() => {
25
25
  r.current && n() && o(i.contentRect);
26
26
  }));
27
27
  }), [n]);
28
- return _(() => (r.current && (l == null || l.observe(r.current, e)), () => {
29
- l == null || l.disconnect(), t.current && cancelAnimationFrame(t.current);
28
+ return R(() => (r.current && l?.observe(r.current, e), () => {
29
+ l?.disconnect(), t.current && cancelAnimationFrame(t.current);
30
30
  }), [l, e]), [r, a];
31
31
  }
32
- function te(e) {
33
- const n = K();
32
+ function B(e) {
33
+ const n = W();
34
34
  if (!e)
35
35
  return n;
36
36
  if (typeof e == "number" || typeof e == "string")
@@ -40,7 +40,7 @@ function te(e) {
40
40
  return typeof t == "number" || typeof t == "string" ? `${r}${t}` : `${r}${n}`;
41
41
  }
42
42
  }
43
- const D = "SET_ANNOUNCEMENT", w = "CLEAR_ANNOUNCEMENT", re = {
43
+ const P = "SET_ANNOUNCEMENT", D = "CLEAR_ANNOUNCEMENT", ee = {
44
44
  alert: null,
45
45
  alertdialog: null,
46
46
  log: "polite",
@@ -48,14 +48,14 @@ const D = "SET_ANNOUNCEMENT", w = "CLEAR_ANNOUNCEMENT", re = {
48
48
  progressbar: null,
49
49
  status: "polite",
50
50
  timer: "assertive"
51
- }, ne = (e, n) => {
52
- switch (n == null ? void 0 : n.type) {
53
- case D:
51
+ }, te = (e, n) => {
52
+ switch (n?.type) {
53
+ case P:
54
54
  return {
55
55
  ...e,
56
56
  announcement: n.payload
57
57
  };
58
- case w:
58
+ case D:
59
59
  return {
60
60
  ...e,
61
61
  announcement: null
@@ -63,14 +63,14 @@ const D = "SET_ANNOUNCEMENT", w = "CLEAR_ANNOUNCEMENT", re = {
63
63
  default:
64
64
  return e;
65
65
  }
66
- }, ae = ({
66
+ }, re = ({
67
67
  onAnnouncementClear: e,
68
68
  dispatch: n
69
69
  }) => {
70
70
  n({
71
- type: w
71
+ type: D
72
72
  }), typeof e == "function" && e();
73
- }, S = ({
73
+ }, C = ({
74
74
  children: e,
75
75
  clearAnnouncementDelay: n,
76
76
  clearAnnouncementTimeoutRef: t,
@@ -78,16 +78,16 @@ const D = "SET_ANNOUNCEMENT", w = "CLEAR_ANNOUNCEMENT", re = {
78
78
  dispatch: a
79
79
  }) => {
80
80
  clearTimeout(t.current), e !== null && a({
81
- type: D,
81
+ type: P,
82
82
  payload: e
83
83
  }), n && (t.current = setTimeout(
84
- () => ae({
84
+ () => re({
85
85
  onAnnouncementClear: r,
86
86
  dispatch: a
87
87
  }),
88
88
  n
89
89
  ));
90
- }, le = ({
90
+ }, ne = ({
91
91
  children: e,
92
92
  announcementTimeoutRef: n,
93
93
  announcementDelay: t,
@@ -96,13 +96,13 @@ const D = "SET_ANNOUNCEMENT", w = "CLEAR_ANNOUNCEMENT", re = {
96
96
  onAnnouncementClear: o,
97
97
  dispatch: l
98
98
  }) => {
99
- clearTimeout(n.current), t ? n.current = setTimeout(S, t, {
99
+ clearTimeout(n.current), t ? n.current = setTimeout(C, t, {
100
100
  children: e,
101
101
  clearAnnouncementDelay: r,
102
102
  clearAnnouncementTimeoutRef: a,
103
103
  onAnnouncementClear: o,
104
104
  dispatch: l
105
- }) : S({
105
+ }) : C({
106
106
  children: e,
107
107
  clearAnnouncementDelay: r,
108
108
  clearAnnouncementTimeoutRef: a,
@@ -110,7 +110,7 @@ const D = "SET_ANNOUNCEMENT", w = "CLEAR_ANNOUNCEMENT", re = {
110
110
  dispatch: l
111
111
  });
112
112
  };
113
- function ce({
113
+ function ae({
114
114
  children: e,
115
115
  className: n,
116
116
  politeness: t,
@@ -121,13 +121,13 @@ function ce({
121
121
  visible: p,
122
122
  ...i
123
123
  }) {
124
- const x = y(null), m = y(null), [s, g] = Q(ne, {
124
+ const h = y(null), m = y(null), [s, g] = J(te, {
125
125
  announcement: null
126
126
  });
127
127
  let d = t;
128
- typeof d > "u" && (d = r ? re[r] : "assertive"), _(() => {
129
- le({
130
- announcementTimeoutRef: x,
128
+ typeof d > "u" && (d = r ? ee[r] : "assertive"), R(() => {
129
+ ne({
130
+ announcementTimeoutRef: h,
131
131
  announcementDelay: a,
132
132
  children: e,
133
133
  clearAnnouncementDelay: o,
@@ -156,33 +156,33 @@ function ce({
156
156
  );
157
157
  }
158
158
  /*!
159
- @versini/ui-liveregion v2.0.6
159
+ @versini/ui-liveregion v2.0.7
160
160
  © 2025 gizmette.com
161
161
  */
162
162
  try {
163
163
  window.__VERSINI_UI_LIVEREGION__ || (window.__VERSINI_UI_LIVEREGION__ = {
164
- version: "2.0.6",
165
- buildTime: "07/14/2025 12:04 PM EDT",
164
+ version: "2.0.7",
165
+ buildTime: "08/07/2025 04:11 PM EDT",
166
166
  homepage: "https://github.com/aversini/ui-components",
167
167
  license: "MIT"
168
168
  });
169
169
  } catch {
170
170
  }
171
- const oe = ({
171
+ const le = ({
172
172
  mode: e
173
173
  }) => c({
174
174
  "bg-surface-darker text-copy-lighter caret-copy-light": e === "dark",
175
175
  "bg-surface-lighter text-copy-dark caret-copy-dark": e === "light",
176
176
  "bg-surface-lighter text-copy-dark caret-copy-dark dark:bg-surface-darker dark:text-copy-lighter dark:caret-copy-light": e === "system",
177
177
  "bg-surface-darker text-copy-lighter caret-copy-light dark:bg-surface-lighter dark:text-copy-dark dark:caret-copy-dark": e === "alt-system"
178
- }), se = ({
178
+ }), ce = ({
179
179
  focusMode: e
180
180
  }) => c("focus:outline focus:outline-2 focus:outline-offset-2", {
181
181
  "focus:outline-focus-dark": e === "dark",
182
182
  "focus:outline-focus-light": e === "light",
183
183
  "focus:outline-focus-light dark:focus:outline-focus-dark": e === "alt-system",
184
184
  "focus:outline-focus-dark dark:focus:outline-focus-light": e === "system"
185
- }), ue = ({
185
+ }), oe = ({
186
186
  noBorder: e,
187
187
  error: n
188
188
  }) => c("border-2", {
@@ -190,7 +190,7 @@ const oe = ({
190
190
  "focus:border-border-dark": !e && n,
191
191
  "border-border-error-dark": !e && n,
192
192
  "border-transparent": e
193
- }), ie = ({
193
+ }), se = ({
194
194
  disabled: e,
195
195
  raw: n,
196
196
  error: t,
@@ -214,7 +214,7 @@ const oe = ({
214
214
  "text-copy-error-dark dark:text-copy-error-light dark:bg-surface-darker": r === "system",
215
215
  "text-copy-lighter dark:text-copy-error-dark": r === "alt-system"
216
216
  });
217
- }, pe = ({
217
+ }, ue = ({
218
218
  error: e,
219
219
  raw: n,
220
220
  mode: t,
@@ -224,24 +224,24 @@ const oe = ({
224
224
  return "";
225
225
  if (r)
226
226
  return c(
227
- I,
227
+ N,
228
228
  "absolute px-2 cursor-not-allowed opacity-50 font-medium"
229
229
  );
230
230
  if (!e)
231
- return c(I, "absolute px-2 font-medium", {
231
+ return c(N, "absolute px-2 font-medium", {
232
232
  "text-copy-lighter": t === "dark",
233
233
  "text-copy-dark": t === "light",
234
234
  "text-copy-dark dark:text-copy-lighter": t === "system",
235
235
  "text-copy-lighter dark:text-copy-dark": t === "alt-system"
236
236
  });
237
237
  if (e)
238
- return c(I, "absolute px-2 font-medium", {
238
+ return c(N, "absolute px-2 font-medium", {
239
239
  "text-copy-error-light bg-surface-darker": t === "dark",
240
240
  "text-copy-error-dark": t === "light",
241
241
  "text-copy-error-dark dark:text-copy-error-light dark:bg-surface-darker": t === "system",
242
242
  "dark:text-copy-error-dark text-copy-error-light bg-surface-darker": t === "alt-system"
243
243
  });
244
- }, de = ({
244
+ }, ie = ({
245
245
  className: e,
246
246
  inputClassName: n,
247
247
  raw: t,
@@ -251,11 +251,11 @@ const oe = ({
251
251
  mode: l,
252
252
  focusMode: p,
253
253
  size: i,
254
- rightElementClassName: x
254
+ rightElementClassName: h
255
255
  }) => {
256
256
  const m = t ? e : c(
257
257
  "relative flex w-full flex-col justify-center",
258
- Y,
258
+ K,
259
259
  e
260
260
  );
261
261
  let s = "";
@@ -277,36 +277,36 @@ const oe = ({
277
277
  break;
278
278
  }
279
279
  const g = t ? c(n) : c(
280
- P,
280
+ S,
281
281
  s,
282
282
  "rounded-md text-base px-4",
283
- oe({ mode: l }),
284
- se({ focusMode: p }),
285
- ue({ noBorder: a, error: o }),
283
+ le({ mode: l }),
284
+ ce({ focusMode: p }),
285
+ oe({ noBorder: a, error: o }),
286
286
  {
287
287
  "disabled:cursor-not-allowed disabled:opacity-50": r
288
288
  },
289
289
  n
290
- ), d = t ? void 0 : "sr-only", u = ie({
290
+ ), d = t ? void 0 : "sr-only", u = se({
291
291
  disabled: r,
292
292
  raw: t,
293
293
  error: o,
294
294
  mode: l
295
- }), b = pe({
295
+ }), b = ue({
296
296
  error: o,
297
297
  raw: t,
298
298
  mode: l,
299
299
  disabled: r
300
- }), v = t ? void 0 : c("absolute right-3", x);
300
+ }), A = t ? void 0 : c("absolute right-3", h);
301
301
  return {
302
302
  wrapper: m,
303
303
  input: g,
304
304
  accessibleLabel: d,
305
305
  visibleLabel: u,
306
306
  helperText: b,
307
- rightElement: v
307
+ rightElement: A
308
308
  };
309
- }, fe = z.forwardRef(
309
+ }, pe = G.forwardRef(
310
310
  ({
311
311
  id: e,
312
312
  name: n,
@@ -317,107 +317,106 @@ const oe = ({
317
317
  inputClassName: l,
318
318
  mode: p = "system",
319
319
  focusMode: i = "system",
320
- disabled: x = !1,
320
+ disabled: h = !1,
321
321
  noBorder: m = !1,
322
322
  labelId: s,
323
323
  labelHidden: g = !1,
324
324
  type: d = "text",
325
325
  helperText: u = "",
326
326
  rightElement: b,
327
- rightElementClassName: v,
328
- size: N = "md",
329
- ...U
330
- }, M) => {
331
- const [F, T] = ee(), [O, X] = $(0), k = te({ id: e, prefix: `${P}-` }), j = `${n} error, ${u}`, A = y(null), E = y(null), V = {
327
+ rightElementClassName: A,
328
+ size: E = "md",
329
+ ...$
330
+ }, w) => {
331
+ const [U, T] = Z(), [M, F] = L(0), k = B({ id: e, prefix: `${S}-` }), O = `${n} error, ${u}`, v = y(null), I = y(null), X = {
332
332
  xs: { label: "-25px", helperText: "30px" },
333
333
  sm: { label: "-29px", helperText: "34px" },
334
334
  md: { label: "-33px", helperText: "38px" },
335
335
  lg: { label: "-15px", helperText: "22px" },
336
336
  xl: { label: "-19px", helperText: "25px" }
337
- }, h = de({
337
+ }, x = ie({
338
338
  className: o,
339
339
  inputClassName: l,
340
340
  error: r,
341
341
  raw: a,
342
342
  focusMode: i,
343
- disabled: x,
343
+ disabled: h,
344
344
  noBorder: m,
345
345
  mode: p,
346
- size: N,
347
- rightElementClassName: v
346
+ size: E,
347
+ rightElementClassName: A
348
348
  });
349
- return L(() => {
350
- T && T.width && X(T.width + 18 + 10);
351
- }, [T]), L(() => {
352
- var R, C;
353
- const { label: q, helperText: G } = V[N];
354
- (R = A == null ? void 0 : A.current) == null || R.style.setProperty("--av-text-input-label", q), (C = E == null ? void 0 : E.current) == null || C.style.setProperty(
349
+ return _(() => {
350
+ T && T.width && F(T.width + 18 + 10);
351
+ }, [T]), _(() => {
352
+ const { label: j, helperText: V } = X[E];
353
+ v?.current?.style.setProperty("--av-text-input-label", j), I?.current?.style.setProperty(
355
354
  "--av-text-input-helper-text",
356
- G
355
+ V
357
356
  );
358
- }, [N]), /* @__PURE__ */ H("div", { className: h.wrapper, children: [
357
+ }, [E]), /* @__PURE__ */ q("div", { className: x.wrapper, children: [
359
358
  /* @__PURE__ */ f(
360
359
  "label",
361
360
  {
362
361
  htmlFor: k,
363
362
  id: s,
364
- className: h.accessibleLabel,
363
+ className: x.accessibleLabel,
365
364
  children: t
366
365
  }
367
366
  ),
368
367
  /* @__PURE__ */ f(
369
368
  "input",
370
369
  {
371
- ref: M,
370
+ ref: w,
372
371
  id: k,
373
372
  name: n,
374
373
  type: d,
375
- disabled: x,
374
+ disabled: h,
376
375
  placeholder: a ? void 0 : " ",
377
- className: h.input,
376
+ className: x.input,
378
377
  ...u && { "aria-describedby": `${k}-helper` },
379
378
  ...r && { "aria-invalid": "true" },
380
- ...b && !a && { style: { paddingRight: O } },
381
- ...U
379
+ ...b && !a && { style: { paddingRight: M } },
380
+ ...$
382
381
  }
383
382
  ),
384
383
  !a && !g && /* @__PURE__ */ f(
385
384
  "label",
386
385
  {
387
- ref: A,
386
+ ref: v,
388
387
  "aria-hidden": !0,
389
388
  htmlFor: k,
390
- className: h.visibleLabel,
389
+ className: x.visibleLabel,
391
390
  children: t
392
391
  }
393
392
  ),
394
393
  u && /* @__PURE__ */ f(
395
394
  "div",
396
395
  {
397
- ref: E,
396
+ ref: I,
398
397
  id: `${k}-helper`,
399
- className: h.helperText,
398
+ className: x.helperText,
400
399
  children: u
401
400
  }
402
401
  ),
403
402
  b && /* @__PURE__ */ f(
404
403
  "div",
405
404
  {
406
- ref: F,
407
- className: h.rightElement,
405
+ ref: U,
406
+ className: x.rightElement,
408
407
  children: b
409
408
  }
410
409
  ),
411
- r && u && /* @__PURE__ */ f(ce, { politeness: "polite", clearAnnouncementDelay: 500, children: j })
410
+ r && u && /* @__PURE__ */ f(ae, { politeness: "polite", clearAnnouncementDelay: 500, children: O })
412
411
  ] });
413
412
  }
414
413
  );
415
- fe.displayName = "TextInput";
414
+ pe.displayName = "TextInput";
416
415
  export {
417
- P as TEXT_INPUT_CLASSNAME,
418
- I as TEXT_INPUT_HELPER_TEXT_CLASSNAME,
419
- me as TEXT_INPUT_SIMPLE_CLASSNAME,
420
- Y as TEXT_INPUT_WRAPPER_CLASSNAME,
421
- fe as TextInput,
422
- ce as x
416
+ S as TEXT_INPUT_CLASSNAME,
417
+ N as TEXT_INPUT_HELPER_TEXT_CLASSNAME,
418
+ he as TEXT_INPUT_SIMPLE_CLASSNAME,
419
+ K as TEXT_INPUT_WRAPPER_CLASSNAME,
420
+ pe as TextInput,
421
+ ae as x
423
422
  };
@@ -1,5 +1,5 @@
1
1
  import "react/jsx-runtime";
2
- import { TextInput as m } from "../../chunks/TextInput.DaBe7aRj.js";
2
+ import { TextInput as m } from "../../chunks/TextInput.9wpoUp2i.js";
3
3
  import "react";
4
4
  export {
5
5
  m as TextInput
@@ -1,85 +1,84 @@
1
- import { TextInput as F, x as H } from "../../chunks/TextInput.DaBe7aRj.js";
2
- import { jsxs as L, Fragment as B, jsx as x } from "react/jsx-runtime";
3
- import A, { useMemo as j, useState as _, useRef as m, useEffect as b } from "react";
4
- function $(r) {
5
- return j(() => r.every((t) => t == null) ? () => {
1
+ import { TextInput as y, x as D } from "../../chunks/TextInput.9wpoUp2i.js";
2
+ import { jsxs as F, Fragment as H, jsx as M } from "react/jsx-runtime";
3
+ import N, { useMemo as L, useState as x, useRef as i, useEffect as B } from "react";
4
+ function j(s) {
5
+ return L(() => s.every((t) => t == null) ? () => {
6
6
  } : (t) => {
7
- r.forEach((n) => {
7
+ s.forEach((n) => {
8
8
  typeof n == "function" ? n(t) : n != null && (n.current = t);
9
9
  });
10
- }, [...r]);
10
+ }, [...s]);
11
11
  }
12
- const q = 500, z = 5e3, G = 2e4, J = A.forwardRef(
12
+ const b = 500, $ = 5e3, q = 2e4, z = N.forwardRef(
13
13
  ({
14
- name: r,
14
+ name: s,
15
15
  disabled: t,
16
16
  label: n,
17
- labelHidden: k,
17
+ labelHidden: A,
18
18
  onMaskChange: u,
19
- onChange: T,
20
- onBlur: d,
21
- onFocus: p,
22
- onTextInputMaskBlur: f,
23
- rightElement: O,
24
- ...U
25
- }, E) => {
26
- const [h, R] = _(!0), [l, g] = _({
19
+ onChange: m,
20
+ onBlur: p,
21
+ onFocus: T,
22
+ onTextInputMaskBlur: d,
23
+ rightElement: _,
24
+ ...k
25
+ }, f) => {
26
+ const [E, h] = x(!0), [l, R] = x({
27
27
  message: null,
28
28
  politeness: null
29
- }), c = m(!0), o = m(null), I = m(null), w = $([E, I]), C = h ? "Show" : "Hide", a = () => {
29
+ }), c = i(!0), o = i(null), g = i(null), O = j([f, g]), U = E ? "Show" : "Hide", a = () => {
30
30
  o.current && clearTimeout(o.current), c.current || (o.current = setTimeout(() => {
31
- c.current = !0, R(!0), g({
32
- announcementTimeout: z,
31
+ c.current = !0, h(!0), R({
32
+ announcementTimeout: $,
33
33
  politeness: "polite",
34
34
  message: `${n} hiding characters`
35
35
  }), u && u(!0);
36
- }, G));
37
- }, S = (e) => {
36
+ }, q));
37
+ }, w = (e) => {
38
38
  e.preventDefault();
39
- const s = !c.current;
40
- c.current = s, a(), R(s), g({
41
- announcementTimeout: q,
39
+ const r = !c.current;
40
+ c.current = r, a(), h(r), R({
41
+ announcementTimeout: b,
42
42
  politeness: "assertive",
43
- message: s ? "Characters hidden" : "Characters showing"
44
- }), u && u(s);
45
- }, M = (e) => {
46
- var N;
47
- const { relatedTarget: s } = e, i = (N = I.current) == null ? void 0 : N.parentElement;
48
- i != null && i.contains(s) || f && f();
49
- }, v = (e) => {
50
- a(), d && d(e), M(e);
51
- }, y = (e) => {
52
- a(), p && p(e);
53
- }, D = (e) => {
43
+ message: r ? "Characters hidden" : "Characters showing"
44
+ }), u && u(r);
45
+ }, I = (e) => {
46
+ const { relatedTarget: r } = e;
47
+ g.current?.parentElement?.contains(r) || d && d();
48
+ }, C = (e) => {
49
+ a(), p && p(e), I(e);
50
+ }, S = (e) => {
54
51
  a(), T && T(e);
52
+ }, v = (e) => {
53
+ a(), m && m(e);
55
54
  };
56
- return b(() => () => {
55
+ return B(() => () => {
57
56
  o.current && clearTimeout(o.current);
58
- }, []), /* @__PURE__ */ L(B, { children: [
59
- /* @__PURE__ */ x(
60
- F,
57
+ }, []), /* @__PURE__ */ F(H, { children: [
58
+ /* @__PURE__ */ M(
59
+ y,
61
60
  {
62
- ref: w,
63
- name: r,
61
+ ref: O,
62
+ name: s,
64
63
  label: n,
65
- labelHidden: k,
66
- type: h ? "password" : "text",
64
+ labelHidden: A,
65
+ type: E ? "password" : "text",
67
66
  disabled: t,
68
- onBlur: v,
69
- onFocus: y,
70
- onChange: D,
71
- rightElement: A.cloneElement(O, {
72
- ref: E,
73
- label: C,
74
- onClick: S,
75
- onBlur: M,
67
+ onBlur: C,
68
+ onFocus: S,
69
+ onChange: v,
70
+ rightElement: N.cloneElement(_, {
71
+ ref: f,
72
+ label: U,
73
+ onClick: w,
74
+ onBlur: I,
76
75
  disabled: t
77
76
  }),
78
- ...U
77
+ ...k
79
78
  }
80
79
  ),
81
- /* @__PURE__ */ x(
82
- H,
80
+ /* @__PURE__ */ M(
81
+ D,
83
82
  {
84
83
  role: "status",
85
84
  politeness: l.politeness,
@@ -90,7 +89,7 @@ const q = 500, z = 5e3, G = 2e4, J = A.forwardRef(
90
89
  ] });
91
90
  }
92
91
  );
93
- J.displayName = "TextInputMask";
92
+ z.displayName = "TextInputMask";
94
93
  export {
95
- J as TextInputMask
94
+ z as TextInputMask
96
95
  };
package/dist/index.js CHANGED
@@ -1,13 +1,13 @@
1
- import { TEXT_INPUT_CLASSNAME as I, TEXT_INPUT_HELPER_TEXT_CLASSNAME as e, TEXT_INPUT_SIMPLE_CLASSNAME as o, TEXT_INPUT_WRAPPER_CLASSNAME as t, TextInput as N } from "./chunks/TextInput.DaBe7aRj.js";
1
+ import { TEXT_INPUT_CLASSNAME as I, TEXT_INPUT_HELPER_TEXT_CLASSNAME as e, TEXT_INPUT_SIMPLE_CLASSNAME as o, TEXT_INPUT_WRAPPER_CLASSNAME as t, TextInput as N } from "./chunks/TextInput.9wpoUp2i.js";
2
2
  import { TextInputMask as P } from "./components/TextInput/TextInputMask.js";
3
3
  /*!
4
- @versini/ui-textinput v4.0.6
4
+ @versini/ui-textinput v4.0.7
5
5
  © 2025 gizmette.com
6
6
  */
7
7
  try {
8
8
  window.__VERSINI_UI_TEXTINPUT__ || (window.__VERSINI_UI_TEXTINPUT__ = {
9
- version: "4.0.6",
10
- buildTime: "07/14/2025 12:04 PM EDT",
9
+ version: "4.0.7",
10
+ buildTime: "08/07/2025 04:11 PM EDT",
11
11
  homepage: "https://github.com/aversini/ui-components",
12
12
  license: "MIT"
13
13
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@versini/ui-textinput",
3
- "version": "4.0.6",
3
+ "version": "4.0.7",
4
4
  "license": "MIT",
5
5
  "author": "Arno Versini",
6
6
  "publishConfig": {
@@ -27,6 +27,7 @@
27
27
  "dev:types": "tsup --watch src",
28
28
  "dev": "npm-run-all clean --parallel dev:js dev:types",
29
29
  "lint": "biome lint src",
30
+ "lint:fix": "biome check src --write --no-errors-on-unmatched",
30
31
  "prettier": "biome check --write --no-errors-on-unmatched",
31
32
  "start": "static-server dist --port 5173",
32
33
  "test:coverage:ui": "vitest --coverage --ui",
@@ -39,18 +40,18 @@
39
40
  "react-dom": "^18.3.1 || ^19.0.0"
40
41
  },
41
42
  "devDependencies": {
42
- "@testing-library/jest-dom": "6.6.3",
43
- "@versini/ui-types": "5.0.5"
43
+ "@testing-library/jest-dom": "6.6.4",
44
+ "@versini/ui-types": "5.0.6"
44
45
  },
45
46
  "dependencies": {
46
47
  "@tailwindcss/typography": "0.5.16",
47
48
  "@versini/ui-hooks": "4.7.5",
48
- "@versini/ui-liveregion": "2.0.6",
49
+ "@versini/ui-liveregion": "2.0.7",
49
50
  "clsx": "2.1.1",
50
51
  "tailwindcss": "4.1.11"
51
52
  },
52
53
  "sideEffects": [
53
54
  "**/*.css"
54
55
  ],
55
- "gitHead": "097e81ba959c30dc2ee37ff9050981a02420360b"
56
+ "gitHead": "42daab1fb88c366495abbc1db78a9987de05e59b"
56
57
  }