jattac.libs.web.zest-textbox 0.2.4 → 0.2.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 (2) hide show
  1. package/README.md +121 -26
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -15,6 +15,7 @@ A delightful, feature-rich, and highly customizable React textbox component. Bui
15
15
  - [Password Input with Visibility Toggle](#password-input-with-visibility-toggle)
16
16
  - [Character Counter & Progress Bar](#character-counter--progress-bar)
17
17
  - [Enhanced Numeric Input](#enhanced-numeric-input)
18
+ - [Custom Parser & Validator](#custom-parser--validator)
18
19
  - [Sizing](#sizing)
19
20
  - [Theming (Light/Dark/System)](#theming-lightdarksystem)
20
21
  - [Multiline Textarea](#multiline-textarea)
@@ -35,6 +36,7 @@ A delightful, feature-rich, and highly customizable React textbox component. Bui
35
36
  * **Password Visibility Toggle:** A crucial accessibility and usability feature for password fields.
36
37
  * **Character Counter & Progress Bar:** Visual feedback on input length, with engaging animations as limits are approached.
37
38
  * **Enhanced Numeric Input:** Intelligent filtering for numbers, decimals, and negative values.
39
+ * **Customizable Parsing & Validation:** Define how raw string input is converted to a desired type and validated, with context of the input `type`.
38
40
  * **Engaging Animations:** Subtle, delightful micro-interactions on focus and input.
39
41
  * **Accessible:** Built with `rem` units and best practices for inclusivity.
40
42
  * **Centralized Configuration:** Easily manage default behaviors and styles across your application using React Context.
@@ -53,7 +55,7 @@ Get started with `ZestTextbox` in seconds. It behaves just like a standard HTML
53
55
 
54
56
  ```jsx
55
57
  import React from 'react';
56
- import { ZestTextbox } from 'jattac.libs.web.zest-textbox';
58
+ import ZestTextbox from 'jattac.libs.web.zest-textbox';
57
59
 
58
60
  const App = () => {
59
61
  return (
@@ -76,37 +78,41 @@ export default App;
76
78
  | `zest` | `ZestProps` | `undefined` | An object containing all custom configurations and behaviors specific to the ZestTextbox component. See `ZestProps` interface for details below. |
77
79
  | `className` | `string` | `""` | A custom CSS class to apply to the main textbox element. |
78
80
  | `maxLength` | `number` | `undefined` | The maximum number of characters allowed. Enables the character counter and progress bar. |
79
- | `type` | `string` | `'text'` | The type of the input element. All standard HTML input types are supported. Special handling is applied for `password` and `number`. |
81
+ | `type` | `HtmlInputType` | `'text'` | The type of the input element. All standard HTML input types are supported. Special handling is applied for `password` and `number`. |
80
82
 
81
83
  ### `ZestProps` Interface Details
82
84
 
83
85
  The `zest` prop is an object that encapsulates all the unique features of `ZestTextbox`. Its properties can accept primitive values, functions, or asynchronous functions (`ZestConfigValue<T>`) for dynamic configuration, especially useful with [Centralized Configuration](#centralized-configuration).
84
86
 
85
87
  ```typescript
86
- import { ZestTextboxSize, ZestConfigValue, HelperTextConfig } from 'jattac.libs.web.zest-textbox';
88
+ import { ZestTextboxSize, ZestConfigValue, HelperTextConfig, InputParser, InputValidator, HtmlInputType } from 'jattac.libs.web.zest-textbox';
87
89
 
88
90
  interface ZestProps {
89
91
  helperTextConfig?: ZestConfigValue<HelperTextConfig>;
90
- onTextChanged?: (value: string) => void; // Callback for text changes
92
+ onTextChanged?: <T>(value: T | undefined) => void; // Callback for parsed & validated text changes
91
93
  zSize?: ZestConfigValue<ZestTextboxSize>; // "sm" | "md" | "lg"
92
94
  stretch?: ZestConfigValue<boolean>; // Full width
93
95
  showProgressBar?: ZestConfigValue<boolean>; // Display character progress bar
94
96
  animatedCounter?: ZestConfigValue<boolean>; // Counter color changes
95
97
  theme?: ZestConfigValue<"light" | "dark" | "system">; // Color scheme
96
98
  isMultiline?: ZestConfigValue<boolean>; // Render as <textarea>
99
+ parser?: ZestConfigValue<InputParser<any>>; // Custom parser function
100
+ validator?: ZestConfigValue<InputValidator<any>>; // Custom validator function
97
101
  }
98
102
  ```
99
103
 
100
104
  | Property | Type (`ZestConfigValue<T>`) | Default | Description |
101
105
  | ----------------- | --------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
102
106
  | `helperTextConfig`| `ZestConfigValue<HelperTextConfig>` | `undefined` | Configuration for dynamic helper text displayed below the input. Can be an object, a function returning an object, or a promise of an object. |
103
- | `onTextChanged` | `(value: string) => void` | `undefined` | A callback function that is invoked whenever the textbox's value changes. Provides the raw string value. |
107
+ | `onTextChanged` | `<T>(value: T | undefined) => void` | `undefined` | A callback function that is invoked whenever the textbox's value changes. Provides the *parsed and validated* value. `T` is the type returned by the `parser`. |
104
108
  | `zSize` | `ZestConfigValue<"sm" | "md" | "lg">` | `'md'` | Sets the size of the textbox, affecting padding and font size. |
105
109
  | `stretch` | `ZestConfigValue<boolean>` | `false` | If `true`, the component will stretch to the full width of its container. |
106
110
  | `showProgressBar` | `ZestConfigValue<boolean>` | `false` | If `true`, a progress bar indicating character count vs. `maxLength` will be displayed. Requires `maxLength` to be set. |
107
111
  | `animatedCounter` | `ZestConfigValue<boolean>` | `false` | If `true`, the character counter will change color as it approaches the `maxLength`. Requires `maxLength` to be set. |
108
112
  | `theme` | `ZestConfigValue<"light" | "dark" | "system">` | `'system'` | Controls the component's color scheme. `'system'` automatically detects the OS/browser preference. |
109
113
  | `isMultiline` | `ZestConfigValue<boolean>` | `false` | If `true`, the component will render as a `<textarea>`. If `false` or undefined, it renders as an `<input>`. |
114
+ | `parser` | `ZestConfigValue<InputParser<any>>` | `undefined` | A function to parse the raw string input into a desired type. Receives `(value: string, inputType?: HtmlInputType)`. Returns `undefined` if parsing fails. Default parsers are provided for `type="number"`. |
115
+ | `validator` | `ZestConfigValue<InputValidator<any>>` | `undefined` | A function to validate the parsed value. Receives `(value: T | undefined, inputType?: HtmlInputType)`. Returns `true` for valid, or a string error message for invalid. Default validators are provided for `type="number"`. |
110
116
 
111
117
  ## Feature Examples
112
118
 
@@ -118,7 +124,7 @@ Simply set the `type` prop to `"password"` to enable the built-in visibility tog
118
124
 
119
125
  ```jsx
120
126
  import React from 'react';
121
- import { ZestTextbox } from 'jattac.libs.web.zest-textbox';
127
+ import ZestTextbox from 'jattac.libs.web.zest-textbox';
122
128
 
123
129
  const PasswordExample = () => {
124
130
  return (
@@ -138,7 +144,7 @@ Enable the character counter with `maxLength` and add a visual progress bar and
138
144
 
139
145
  ```jsx
140
146
  import React from 'react';
141
- import { ZestTextbox } from 'jattac.libs.web.zest-textbox';
147
+ import ZestTextbox from 'jattac.libs.web.zest-textbox';
142
148
 
143
149
  const CounterExample = () => {
144
150
  const [text, setText] = React.useState('');
@@ -164,21 +170,33 @@ export default CounterExample;
164
170
 
165
171
  ### Enhanced Numeric Input
166
172
 
167
- Set `type="number"` for intelligent filtering that allows only digits, a single decimal point, and a single leading negative sign.
173
+ Set `type="number"` for intelligent filtering that allows only digits, a single decimal point, and a single leading negative sign. The `onTextChanged` callback will now receive a `number | undefined`.
168
174
 
169
175
  ```jsx
170
176
  import React from 'react';
171
- import { ZestTextbox } from 'jattac.libs.web.zest-textbox';
177
+ import ZestTextbox from 'jattac.libs.web.zest-textbox';
172
178
 
173
179
  const NumericExample = () => {
180
+ const [age, setAge] = React.useState<number | undefined>(undefined);
181
+ const [price, setPrice] = React.useState<number | undefined>(undefined);
182
+
174
183
  return (
175
184
  <div style={{ padding: '2rem', maxWidth: '400px', margin: '0 auto' }}>
176
- <h2>Numeric Input</h2>
177
- <ZestTextbox type="number" placeholder="Enter a number" />
178
- <br /><br />
179
- <ZestTextbox type="number" placeholder="Enter a decimal" defaultValue="3.14" />
185
+ <h2>Enhanced Numeric Input</h2>
186
+ <p>Age: {age === undefined ? 'N/A' : age}</p>
187
+ <ZestTextbox
188
+ type="number"
189
+ placeholder="Enter your age"
190
+ onTextChanged={setAge}
191
+ />
180
192
  <br /><br />
181
- <ZestTextbox type="number" placeholder="Enter a negative number" defaultValue="-100" />
193
+ <p>Price: {price === undefined ? 'N/A' : `$${price.toFixed(2)}`}</p>
194
+ <ZestTextbox
195
+ type="number"
196
+ placeholder="Enter a price"
197
+ onTextChanged={setPrice}
198
+ defaultValue="19.99"
199
+ />
182
200
  </div>
183
201
  );
184
202
  };
@@ -186,13 +204,66 @@ const NumericExample = () => {
186
204
  export default NumericExample;
187
205
  ```
188
206
 
207
+ ### Custom Parser & Validator
208
+
209
+ Define your own `parser` and `validator` functions to handle specific input requirements. The `inputType` is passed to these functions for context. Here, we'll create a custom parser and validator for a "positive integer" number input.
210
+
211
+ ```jsx
212
+ import React from 'react';
213
+ import ZestTextbox, { InputParser, InputValidator, HtmlInputType } from 'jattac.libs.web.zest-textbox';
214
+
215
+ // Custom parser for positive integers
216
+ const positiveIntegerParser: InputParser<number> = (value: string, inputType?: HtmlInputType) => {
217
+ if (inputType === 'number') {
218
+ const parsed = parseInt(value, 10);
219
+ return isNaN(parsed) ? undefined : parsed;
220
+ }
221
+ return undefined;
222
+ };
223
+
224
+ // Custom validator for positive integers
225
+ const positiveIntegerValidator: InputValidator<number> = (parsedValue: number | undefined, inputType?: HtmlInputType) => {
226
+ if (inputType === 'number') {
227
+ if (parsedValue === undefined) {
228
+ return "Please enter a valid integer.";
229
+ }
230
+ if (parsedValue <= 0) {
231
+ return "Value must be a positive integer.";
232
+ }
233
+ }
234
+ return true;
235
+ };
236
+
237
+ const CustomNumericParserValidatorExample = () => {
238
+ const [quantity, setQuantity] = React.useState<number | undefined>(undefined);
239
+
240
+ return (
241
+ <div style={{ padding: '2rem', maxWidth: '400px', margin: '0 auto' }}>
242
+ <h2>Custom Numeric Parser & Validator</h2>
243
+ <p>Quantity: {quantity === undefined ? 'N/A' : quantity}</p>
244
+ <ZestTextbox
245
+ type="number"
246
+ placeholder="Enter positive quantity"
247
+ onTextChanged={setQuantity}
248
+ zest={{
249
+ parser: positiveIntegerParser,
250
+ validator: positiveIntegerValidator,
251
+ }}
252
+ />
253
+ </div>
254
+ );
255
+ };
256
+
257
+ export default CustomNumericParserValidatorExample;
258
+ ```
259
+
189
260
  ### Sizing
190
261
 
191
262
  Control the size of the textbox with the `zSize` property within the `zest` prop. Options are `"sm"`, `"md"` (default), and `"lg"`.
192
263
 
193
264
  ```jsx
194
265
  import React from 'react';
195
- import { ZestTextbox } from 'jattac.libs.web.zest-textbox';
266
+ import ZestTextbox from 'jattac.libs.web.zest-textbox';
196
267
 
197
268
  const SizingExample = () => {
198
269
  return (
@@ -216,7 +287,7 @@ Force the component into a specific theme or let it adapt to the user's system p
216
287
 
217
288
  ```jsx
218
289
  import React from 'react';
219
- import { ZestTextbox } from 'jattac.libs.web.zest-textbox';
290
+ import ZestTextbox from 'jattac.libs.web.zest-textbox';
220
291
 
221
292
  const ThemingExample = () => {
222
293
  return (
@@ -240,7 +311,7 @@ Render `ZestTextbox` as a `<textarea>` by setting `isMultiline` to `true` in the
240
311
 
241
312
  ```jsx
242
313
  import React from 'react';
243
- import { ZestTextbox } from 'jattac.libs.web.zest-textbox';
314
+ import ZestTextbox from 'jattac.libs.web.zest-textbox';
244
315
 
245
316
  const MultilineExample = () => {
246
317
  return (
@@ -264,7 +335,7 @@ Provide dynamic helper text below the input using `helperTextConfig`. You can fo
264
335
 
265
336
  ```jsx
266
337
  import React from 'react';
267
- import { ZestTextbox } from 'jattac.libs.web.zest-textbox';
338
+ import ZestTextbox from 'jattac.libs.web.zest-textbox';
268
339
 
269
340
  const HelperTextExample = () => {
270
341
  const [value, setValue] = React.useState('');
@@ -321,7 +392,7 @@ Make the textbox take up the full width of its parent container by setting `stre
321
392
 
322
393
  ```jsx
323
394
  import React from 'react';
324
- import { ZestTextbox } from 'jattac.libs.web.zest-textbox';
395
+ import ZestTextbox from 'jattac.libs.web.zest-textbox';
325
396
 
326
397
  const StretchExample = () => {
327
398
  return (
@@ -346,11 +417,30 @@ To maintain consistency and reduce boilerplate, `ZestTextbox` supports centraliz
346
417
 
347
418
  ### Usage Example
348
419
 
349
- Here's how you can set up a global theme and size, and then override it for a specific component:
420
+ Here's how you can set up a global theme and size, and then override it for a specific component. You can also define global default parsers and validators here.
350
421
 
351
422
  ```jsx
352
423
  import React from 'react';
353
- import { ZestTextbox, ZestTextboxConfigProvider } from 'jattac.libs.web.zest-textbox';
424
+ import ZestTextbox, { ZestTextboxConfigProvider, InputParser, InputValidator, HtmlInputType } from 'jattac.libs.web.zest-textbox';
425
+
426
+ // Global default parser for positive numbers
427
+ const globalPositiveNumberParser: InputParser<number> = (value: string, inputType?: HtmlInputType) => {
428
+ if (inputType === 'number') {
429
+ const parsed = parseFloat(value);
430
+ return isNaN(parsed) || parsed <= 0 ? undefined : parsed;
431
+ }
432
+ return undefined;
433
+ };
434
+
435
+ // Global default validator for positive numbers
436
+ const globalPositiveNumberValidator: InputValidator<number> = (parsedValue: number | undefined, inputType?: HtmlInputType) => {
437
+ if (inputType === 'number') {
438
+ if (parsedValue === undefined) {
439
+ return "Please enter a valid positive number.";
440
+ }
441
+ }
442
+ return true;
443
+ };
354
444
 
355
445
  const AppWithCentralConfig = () => {
356
446
  // Define your global default ZestProps
@@ -358,15 +448,20 @@ const AppWithCentralConfig = () => {
358
448
  theme: "dark", // All textboxes will be dark by default
359
449
  zSize: () => "lg", // All textboxes will be large by default (function example)
360
450
  animatedCounter: Promise.resolve(true), // Async example
451
+ parser: globalPositiveNumberParser, // Apply global positive number parser
452
+ validator: globalPositiveNumberValidator, // Apply global positive number validator
361
453
  };
362
454
 
455
+ const [amount, setAmount] = React.useState<number | undefined>(undefined);
456
+
363
457
  return (
364
458
  <ZestTextboxConfigProvider value={globalDefaultZestProps}>
365
459
  <div style={{ padding: '2rem', maxWidth: '600px', margin: '0 auto' }}>
366
460
  <h1>Centralized Configuration Example</h1>
367
461
 
368
462
  <h3>Default ZestTextbox (inherits global defaults)</h3>
369
- <ZestTextbox placeholder="I'm large, dark, and animated!" />
463
+ <ZestTextbox placeholder="I'm large, dark, animated, and expect positive numbers!" type="number" onTextChanged={setAmount} />
464
+ <p>Amount: {amount === undefined ? 'N/A' : amount}</p>
370
465
  <br /><br />
371
466
 
372
467
  <h3>Overridden ZestTextbox (component props take precedence)</h3>
@@ -377,7 +472,7 @@ const AppWithCentralConfig = () => {
377
472
  <br /><br />
378
473
 
379
474
  <h3>Another Default ZestTextbox</h3>
380
- <ZestTextbox placeholder="I'm also large, dark, and animated!" />
475
+ <ZestTextbox placeholder="I'm also large, dark, animated, and expect positive numbers!" type="number" />
381
476
  </div>
382
477
  </ZestTextboxConfigProvider>
383
478
  );
@@ -399,10 +494,10 @@ The resolution order for `ZestProps` is as follows:
399
494
  The `ZestTextbox` component has been refactored internally for improved maintainability, readability, and reusability. Its core logic is now distributed across several custom React hooks and smaller, focused sub-components. This internal restructuring does **not** introduce any breaking changes to the public API.
400
495
 
401
496
  The internal structure now includes:
402
- * `UI/hooks/`: Contains custom React hooks (e.g., `useThemeDetector`, `usePasswordVisibility`, `useCharacterCounter`, `useHelperText`, `useZestTextboxConfig`).
497
+ * `UI/hooks/`: Contains custom React hooks (e.g., `useThemeDetector`, `usePasswordVisibility`, `useCharacterCounter`, `useHelperText`, `useZestTextboxConfig`, `useParsedAndValidatedInput`).
403
498
  * `UI/components/`: Contains smaller, focused UI components (e.g., `PasswordToggleButton`, `CharacterCounter`, `ProgressBar`, `HelperTextDisplay`).
404
- * `UI/utils/`: Contains utility functions (e.g., `numericInputFilter`).
405
- * `UI/types.ts`: Defines shared TypeScript interfaces and types.
499
+ * `UI/utils/`: Contains utility functions (e.g., `numericInputFilter`, `defaultParsersAndValidators`).
500
+ * `UI/types.ts`: Defines shared TypeScript interfaces and types (e.g., `HtmlInputType`, `InputParser`, `InputValidator`, `ZestConfigValue`, `ResolvedZestProps`).
406
501
  * `UI/contexts/`: Contains React Contexts and Providers (e.g., `ZestTextboxConfigContext`, `ZestTextboxConfigProvider`).
407
502
 
408
503
  ## Breaking Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jattac.libs.web.zest-textbox",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "A delightful, feature-rich, and highly customizable React textbox component. Packed with features like automatic theme detection, password visibility toggle, character counters, and engaging animations.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",