jattac.libs.web.zest-textbox 0.2.1 → 0.2.3

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.
@@ -0,0 +1,14 @@
1
+ import { InputParser, InputValidator } from "../types";
2
+ interface UseParsedAndValidatedInputArgs<T> {
3
+ rawValue: string;
4
+ inputType?: string;
5
+ parser: InputParser<T> | undefined;
6
+ validator: InputValidator<T> | undefined;
7
+ onParsedAndValidatedChange: ((value: T | undefined) => void) | undefined;
8
+ }
9
+ export declare const useParsedAndValidatedInput: <T>({ rawValue, inputType, parser, validator, onParsedAndValidatedChange, }: UseParsedAndValidatedInputArgs<T>) => {
10
+ parsedValue: T | undefined;
11
+ isValid: boolean;
12
+ validationMessage: string | undefined;
13
+ };
14
+ export {};
@@ -1,2 +1,2 @@
1
1
  import { ZestProps, ResolvedZestProps } from "../types";
2
- export declare const useZestTextboxConfig: (componentZestProps: ZestProps | undefined) => ResolvedZestProps;
2
+ export declare const useZestTextboxConfig: (componentZestProps: ZestProps | undefined, inputType?: string) => ResolvedZestProps;
package/dist/index.d.ts CHANGED
@@ -2,6 +2,8 @@ import React, { ReactNode } from 'react';
2
2
 
3
3
  type ZestTextboxSize = "sm" | "md" | "lg";
4
4
  type ZestConfigValue<T> = T | (() => T) | (() => Promise<T>);
5
+ type InputParser<T> = (value: string, inputType?: string) => T | undefined;
6
+ type InputValidator<T> = (value: T | undefined, inputType?: string) => boolean | string;
5
7
  interface HelperTextConfig {
6
8
  /**
7
9
  * A function to process the raw input value into a new string.
@@ -31,11 +33,11 @@ interface ZestProps {
31
33
  */
32
34
  helperTextConfig?: ZestConfigValue<HelperTextConfig>;
33
35
  /**
34
- * A callback that provides the raw string value of the input on every change.
35
- * This is a convenience prop to avoid using `event.target.value`.
36
- * @param value The current string value of the input.
36
+ * A callback that provides the parsed and validated value of the input on every valid change.
37
+ * This is a convenience prop to avoid using `event.target.value` and manual parsing/validation.
38
+ * @param value The current parsed and validated value of the input, or `undefined` if parsing failed.
37
39
  */
38
- onTextChanged?: (value: string) => void;
40
+ onTextChanged?: <T>(value: T | undefined) => void;
39
41
  /**
40
42
  * Sets the size of the textbox, affecting padding and font size.
41
43
  * @default 'md'
@@ -70,6 +72,16 @@ interface ZestProps {
70
72
  * @default false
71
73
  */
72
74
  isMultiline?: ZestConfigValue<boolean>;
75
+ /**
76
+ * A function to parse the raw string input into a desired type.
77
+ * Returns `undefined` if parsing fails.
78
+ */
79
+ parser?: ZestConfigValue<InputParser<any>>;
80
+ /**
81
+ * A function to validate the parsed value.
82
+ * Returns `true` for valid, or a string error message for invalid.
83
+ */
84
+ validator?: ZestConfigValue<InputValidator<any>>;
73
85
  }
74
86
 
75
87
  type SharedProps = {
package/dist/index.esm.js CHANGED
@@ -111,8 +111,8 @@ function styleInject(css, ref) {
111
111
  }
112
112
  }
113
113
 
114
- var css_248z = "/* === Base Textbox Styles (input & textarea) === */\n.ZestTextbox-module_textbox__0M5Wq {\n font-family: \"Segoe UI\", Roboto, sans-serif;\n font-weight: 500;\n line-height: 1.25;\n border: 1px solid #ccc;\n border-radius: 0.5rem; /* 8px */\n color: #111827;\n background-color: #ffffff;\n transition: border-color 0.2s ease, box-shadow 0.2s ease;\n display: inline-block;\n width: auto;\n box-sizing: border-box;\n resize: none;\n font-size: 1rem; /* 16px */\n padding-bottom: 0.5rem; /* Reduced padding to make space for progress bar */\n}\n\n.ZestTextbox-module_textbox__0M5Wq:focus {\n outline: none;\n border-color: #8B5CF6;\n box-shadow: 0 0 0 0.125rem rgba(139, 92, 246, 0.25); /* 2px */\n animation: ZestTextbox-module_pulse-light__CKfhA 0.5s 1;\n}\n\n/* === Sizes === */\n.ZestTextbox-module_sm__yyxXO {\n padding: 0.5rem 0.75rem; /* 8px 12px */\n font-size: 0.875rem; /* 14px */\n}\n\n.ZestTextbox-module_md__fvL10 {\n padding: 0.625rem 0.875rem; /* 10px 14px */\n font-size: 1rem; /* 16px */\n}\n\n.ZestTextbox-module_lg__fU93- {\n padding: 0.75rem 1rem; /* 12px 16px */\n font-size: 1.125rem; /* 18px */\n}\n\n/* === Full Width === */\n.ZestTextbox-module_fullWidth__xn4fT {\n width: 100%;\n}\n\n/* === Disabled State === */\n.ZestTextbox-module_textbox__0M5Wq:disabled {\n background-color: #f3f4f6;\n color: #9ca3af;\n cursor: not-allowed;\n pointer-events: none;\n border-color: #d1d5db;\n}\n\n/* === Multiline (textarea) specific enhancements === */\ntextarea.ZestTextbox-module_textbox__0M5Wq {\n min-height: 6.25rem; /* 100px */\n line-height: 1.5;\n resize: vertical;\n}\n\n.ZestTextbox-module_wrapper__0ok2A {\n position: relative;\n display: inline-block;\n}\n\n.ZestTextbox-module_counter__waqIT {\n position: absolute;\n right: 0.625rem; /* 10px */\n bottom: 0.375rem; /* 6px */\n font-size: 0.75rem;\n color: #6b7280;\n pointer-events: none;\n user-select: none;\n}\n\n/* === Dark Mode Support === */\n.dark .ZestTextbox-module_textbox__0M5Wq {\n background-color: #1f2937;\n border-color: #374151;\n color: #f3f4f6;\n}\n\n.dark .ZestTextbox-module_textbox__0M5Wq:focus {\n border-color: #A78BFA;\n box-shadow: 0 0 0 0.125rem rgba(167, 139, 250, 0.35); /* 2px */\n animation: ZestTextbox-module_pulse-dark__L9PYJ 0.5s 1;\n}\n\n.dark .ZestTextbox-module_textbox__0M5Wq:disabled {\n background-color: #374151;\n color: #9ca3af;\n border-color: #4b5563;\n}\n\n.dark .ZestTextbox-module_counter__waqIT {\n color: #9ca3af;\n}\n\n/* === Password Toggle === */\n.ZestTextbox-module_passwordToggle__I2s4O {\n position: absolute;\n right: 0.625rem; /* 10px */\n top: 50%;\n transform: translateY(-50%);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #6b7280;\n}\n\n.ZestTextbox-module_eyeIcon__rKiBL {\n width: 1.25em;\n height: 1.25em;\n transition: transform 0.2s ease-in-out;\n}\n\n.ZestTextbox-module_rotate__Ajx19 {\n transform: rotate(180deg);\n}\n\n.ZestTextbox-module_tooltip__etRdj {\n position: absolute;\n right: 100%;\n top: 50%;\n transform: translateY(-50%);\n background-color: #333;\n color: #fff;\n padding: 0.25rem 0.5rem; /* 4px 8px */\n border-radius: 0.25rem; /* 4px */\n font-size: 0.75rem;\n white-space: nowrap;\n margin-right: 0.5rem; /* 8px */\n opacity: 0;\n visibility: hidden;\n transition: opacity 0.2s ease, visibility 0.2s ease;\n}\n\n.ZestTextbox-module_passwordToggle__I2s4O:hover .ZestTextbox-module_tooltip__etRdj {\n opacity: 1;\n visibility: visible;\n}\n\n.dark .ZestTextbox-module_passwordToggle__I2s4O {\n color: #9ca3af;\n}\n\n.dark .ZestTextbox-module_tooltip__etRdj {\n background-color: #4b5563;\n color: #f3f4f6;\n}\n\n/* === Progress Bar === */\n.ZestTextbox-module_progressBarContainer__0qFKf {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 0.1875rem; /* 3px */\n background-color: #e5e7eb;\n border-bottom-left-radius: 0.5rem; /* 8px */\n border-bottom-right-radius: 0.5rem; /* 8px */\n overflow: hidden;\n}\n\n.ZestTextbox-module_progressBar__vwttj {\n height: 100%;\n background-color: #8B5CF6;\n transition: width 0.2s ease, background-color 0.3s ease;\n}\n\n/* === Animated Counter Colors === */\n.ZestTextbox-module_counterYellow__uYGfs {\n color: #A78BFA;\n}\n\n.ZestTextbox-module_counterOrange__b9baX {\n color: #8B5CF6;\n}\n\n.dark .ZestTextbox-module_progressBarContainer__0qFKf {\n background-color: #374151;\n}\n\n.dark .ZestTextbox-module_progressBar__vwttj {\n background-color: #A78BFA;\n}\n\n.dark .ZestTextbox-module_counterYellow__uYGfs {\n color: #C4B5FD;\n}\n\n.dark .ZestTextbox-module_counterOrange__b9baX {\n color: #A78BFA;\n}\n\n@keyframes ZestTextbox-module_pulse-light__CKfhA {\n 0% {\n box-shadow: 0 0 0 0.125rem rgba(139, 92, 246, 0.25); /* 2px */\n }\n 50% {\n box-shadow: 0 0 0 0.25rem rgba(139, 92, 246, 0.35); /* 4px */\n }\n 100% {\n box-shadow: 0 0 0 0.125rem rgba(139, 92, 246, 0.25); /* 2px */\n }\n}\n\n@keyframes ZestTextbox-module_pulse-dark__L9PYJ {\n 0% {\n box-shadow: 0 0 0 0.125rem rgba(167, 139, 250, 0.35); /* 2px */\n }\n 50% {\n box-shadow: 0 0 0 0.25rem rgba(167, 139, 250, 0.45); /* 4px */\n }\n 100% {\n box-shadow: 0 0 0 0.125rem rgba(167, 139, 250, 0.35); /* 2px */\n }\n}\n\n/* === Media Queries for Responsive Design === */\n@media (min-width: 48rem) { /* 768px */\n /* Tablet */\n .ZestTextbox-module_sm__yyxXO {\n font-size: 0.875rem; /* 14px */\n }\n .ZestTextbox-module_md__fvL10 {\n font-size: 1rem; /* 16px */\n }\n .ZestTextbox-module_lg__fU93- {\n font-size: 1.125rem; /* 18px */\n }\n}\n\n@media (min-width: 64rem) { /* 1024px */\n /* Desktop */\n .ZestTextbox-module_sm__yyxXO {\n padding: 0.375rem 0.625rem; /* 6px 10px */\n font-size: 0.875rem; /* 14px */\n }\n .ZestTextbox-module_md__fvL10 {\n padding: 0.625rem 0.875rem; /* 10px 14px */\n font-size: 1rem; /* 16px */\n }\n .ZestTextbox-module_lg__fU93- {\n padding: 0.75rem 1rem; /* 12px 16px */\n font-size: 1.125rem; /* 18px */\n }\n}\n\n/* === Helper Text === */\n.ZestTextbox-module_helperText__4twSg {\n font-size: 0.875rem; /* 14px */\n color: #6b7280;\n margin-top: 0.25rem; /* 4px */\n animation: ZestTextbox-module_fade-slide-in__re-Ln 0.3s ease-out forwards;\n}\n\n.dark .ZestTextbox-module_helperText__4twSg {\n color: #9ca3af;\n}\n\n@keyframes ZestTextbox-module_fade-slide-in__re-Ln {\n from {\n opacity: 0;\n transform: translateY(5px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n";
115
- var styles = {"textbox":"ZestTextbox-module_textbox__0M5Wq","pulse-light":"ZestTextbox-module_pulse-light__CKfhA","sm":"ZestTextbox-module_sm__yyxXO","md":"ZestTextbox-module_md__fvL10","lg":"ZestTextbox-module_lg__fU93-","fullWidth":"ZestTextbox-module_fullWidth__xn4fT","wrapper":"ZestTextbox-module_wrapper__0ok2A","counter":"ZestTextbox-module_counter__waqIT","pulse-dark":"ZestTextbox-module_pulse-dark__L9PYJ","passwordToggle":"ZestTextbox-module_passwordToggle__I2s4O","eyeIcon":"ZestTextbox-module_eyeIcon__rKiBL","rotate":"ZestTextbox-module_rotate__Ajx19","tooltip":"ZestTextbox-module_tooltip__etRdj","progressBarContainer":"ZestTextbox-module_progressBarContainer__0qFKf","progressBar":"ZestTextbox-module_progressBar__vwttj","counterYellow":"ZestTextbox-module_counterYellow__uYGfs","counterOrange":"ZestTextbox-module_counterOrange__b9baX","helperText":"ZestTextbox-module_helperText__4twSg","fade-slide-in":"ZestTextbox-module_fade-slide-in__re-Ln"};
114
+ var css_248z = "/* === Base Textbox Styles (input & textarea) === */\n.ZestTextbox-module_textbox__0M5Wq {\n font-family: \"Segoe UI\", Roboto, sans-serif;\n font-weight: 500;\n line-height: 1.25;\n border: 1px solid #ccc;\n border-radius: 0.5rem; /* 8px */\n color: #111827;\n background-color: #ffffff;\n transition: border-color 0.2s ease, box-shadow 0.2s ease;\n display: inline-block;\n width: auto;\n box-sizing: border-box;\n resize: none;\n font-size: 1rem; /* 16px */\n padding-bottom: 0.5rem; /* Reduced padding to make space for progress bar */\n}\n\n.ZestTextbox-module_textbox__0M5Wq:focus {\n outline: none;\n border-color: #8B5CF6;\n box-shadow: 0 0 0 0.125rem rgba(139, 92, 246, 0.25); /* 2px */\n animation: ZestTextbox-module_pulse-light__CKfhA 0.5s 1;\n}\n\n/* === Error State === */\n.ZestTextbox-module_error__5RoCP {\n border-color: #ef4444; /* Red-500 */\n box-shadow: 0 0 0 0.125rem rgba(239, 68, 68, 0.25); /* Red-500 with 25% opacity */\n}\n\n.ZestTextbox-module_error__5RoCP:focus {\n border-color: #ef4444;\n box-shadow: 0 0 0 0.125rem rgba(239, 68, 68, 0.35);\n}\n\n/* === Sizes === */\n.ZestTextbox-module_sm__yyxXO {\n padding: 0.5rem 0.75rem; /* 8px 12px */\n font-size: 0.875rem; /* 14px */\n}\n\n.ZestTextbox-module_md__fvL10 {\n padding: 0.625rem 0.875rem; /* 10px 14px */\n font-size: 1rem; /* 16px */\n}\n\n.ZestTextbox-module_lg__fU93- {\n padding: 0.75rem 1rem; /* 12px 16px */\n font-size: 1.125rem; /* 18px */\n}\n\n/* === Full Width === */\n.ZestTextbox-module_fullWidth__xn4fT {\n width: 100%;\n}\n\n/* === Disabled State === */\n.ZestTextbox-module_textbox__0M5Wq:disabled {\n background-color: #f3f4f6;\n color: #9ca3af;\n cursor: not-allowed;\n pointer-events: none;\n border-color: #d1d5db;\n}\n\n/* === Multiline (textarea) specific enhancements === */\ntextarea.ZestTextbox-module_textbox__0M5Wq {\n min-height: 6.25rem; /* 100px */\n line-height: 1.5;\n resize: vertical;\n}\n\n.ZestTextbox-module_wrapper__0ok2A {\n position: relative;\n display: inline-block;\n}\n\n.ZestTextbox-module_counter__waqIT {\n position: absolute;\n right: 0.625rem; /* 10px */\n bottom: 0.375rem; /* 6px */\n font-size: 0.75rem;\n color: #6b7280;\n pointer-events: none;\n user-select: none;\n}\n\n/* === Dark Mode Support === */\n.dark .ZestTextbox-module_textbox__0M5Wq {\n background-color: #1f2937;\n border-color: #374151;\n color: #f3f4f6;\n}\n\n.dark .ZestTextbox-module_textbox__0M5Wq:focus {\n border-color: #A78BFA;\n box-shadow: 0 0 0 0.125rem rgba(167, 139, 250, 0.35); /* 2px */\n animation: ZestTextbox-module_pulse-dark__L9PYJ 0.5s 1;\n}\n\n.dark .ZestTextbox-module_textbox__0M5Wq:disabled {\n background-color: #374151;\n color: #9ca3af;\n border-color: #4b5563;\n}\n\n.dark .ZestTextbox-module_counter__waqIT {\n color: #9ca3af;\n}\n\n.dark .ZestTextbox-module_error__5RoCP {\n border-color: #f87171; /* Red-400 for dark mode */\n box-shadow: 0 0 0 0.125rem rgba(248, 113, 113, 0.35);\n}\n\n.dark .ZestTextbox-module_error__5RoCP:focus {\n border-color: #f87171;\n box-shadow: 0 0 0 0.125rem rgba(248, 113, 113, 0.45);\n}\n\n/* === Password Toggle === */\n.ZestTextbox-module_passwordToggle__I2s4O {\n position: absolute;\n right: 0.625rem; /* 10px */\n top: 50%;\n transform: translateY(-50%);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #6b7280;\n}\n\n.ZestTextbox-module_eyeIcon__rKiBL {\n width: 1.25em;\n height: 1.25em;\n transition: transform 0.2s ease-in-out;\n}\n\n.ZestTextbox-module_rotate__Ajx19 {\n transform: rotate(180deg);\n}\n\n.ZestTextbox-module_tooltip__etRdj {\n position: absolute;\n right: 100%;\n top: 50%;\n transform: translateY(-50%);\n background-color: #333;\n color: #fff;\n padding: 0.25rem 0.5rem; /* 4px 8px */\n border-radius: 0.25rem; /* 4px */\n font-size: 0.75rem;\n white-space: nowrap;\n margin-right: 0.5rem; /* 8px */\n opacity: 0;\n visibility: hidden;\n transition: opacity 0.2s ease, visibility 0.2s ease;\n}\n\n.ZestTextbox-module_passwordToggle__I2s4O:hover .ZestTextbox-module_tooltip__etRdj {\n opacity: 1;\n visibility: visible;\n}\n\n.dark .ZestTextbox-module_passwordToggle__I2s4O {\n color: #9ca3af;\n}\n\n.dark .ZestTextbox-module_tooltip__etRdj {\n background-color: #4b5563;\n color: #f3f4f6;\n}\n\n/* === Progress Bar === */\n.ZestTextbox-module_progressBarContainer__0qFKf {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 0.1875rem; /* 3px */\n background-color: #e5e7eb;\n border-bottom-left-radius: 0.5rem; /* 8px */\n border-bottom-right-radius: 0.5rem; /* 8px */\n overflow: hidden;\n}\n\n.ZestTextbox-module_progressBar__vwttj {\n height: 100%;\n background-color: #8B5CF6;\n transition: width 0.2s ease, background-color 0.3s ease;\n}\n\n/* === Animated Counter Colors === */\n.ZestTextbox-module_counterYellow__uYGfs {\n color: #A78BFA;\n}\n\n.ZestTextbox-module_counterOrange__b9baX {\n color: #8B5CF6;\n}\n\n.dark .ZestTextbox-module_progressBarContainer__0qFKf {\n background-color: #374151;\n}\n\n.dark .ZestTextbox-module_progressBar__vwttj {\n background-color: #A78BFA;\n}\n\n.dark .ZestTextbox-module_counterYellow__uYGfs {\n color: #C4B5FD;\n}\n\n.dark .ZestTextbox-module_counterOrange__b9baX {\n color: #A78BFA;\n}\n\n@keyframes ZestTextbox-module_pulse-light__CKfhA {\n 0% {\n box-shadow: 0 0 0 0.125rem rgba(139, 92, 246, 0.25); /* 2px */\n }\n 50% {\n box-shadow: 0 0 0 0.25rem rgba(139, 92, 246, 0.35); /* 4px */\n }\n 100% {\n box-shadow: 0 0 0 0.125rem rgba(139, 92, 246, 0.25); /* 2px */\n }\n}\n\n@keyframes ZestTextbox-module_pulse-dark__L9PYJ {\n 0% {\n box-shadow: 0 0 0 0.125rem rgba(167, 139, 250, 0.35); /* 2px */\n }\n 50% {\n box-shadow: 0 0 0 0.25rem rgba(167, 139, 250, 0.45); /* 4px */\n }\n 100% {\n box-shadow: 0 0 0 0.125rem rgba(167, 139, 250, 0.35); /* 2px */\n }\n}\n\n/* === Media Queries for Responsive Design === */\n@media (min-width: 48rem) { /* 768px */\n /* Tablet */\n .ZestTextbox-module_sm__yyxXO {\n font-size: 0.875rem; /* 14px */\n }\n .ZestTextbox-module_md__fvL10 {\n font-size: 1rem; /* 16px */\n }\n .ZestTextbox-module_lg__fU93- {\n font-size: 1.125rem; /* 18px */\n }\n}\n\n@media (min-width: 64rem) { /* 1024px */\n /* Desktop */\n .ZestTextbox-module_sm__yyxXO {\n padding: 0.375rem 0.625rem; /* 6px 10px */\n font-size: 0.875rem; /* 14px */\n }\n .ZestTextbox-module_md__fvL10 {\n padding: 0.625rem 0.875rem; /* 10px 14px */\n font-size: 1rem; /* 16px */\n }\n .ZestTextbox-module_lg__fU93- {\n padding: 0.75rem 1rem; /* 12px 16px */\n font-size: 1.125rem; /* 18px */\n }\n}\n\n/* === Helper Text === */\n.ZestTextbox-module_helperText__4twSg {\n font-size: 0.875rem; /* 14px */\n color: #6b7280;\n margin-top: 0.25rem; /* 4px */\n animation: ZestTextbox-module_fade-slide-in__re-Ln 0.3s ease-out forwards;\n}\n\n.dark .ZestTextbox-module_helperText__4twSg {\n color: #9ca3af;\n}\n\n@keyframes ZestTextbox-module_fade-slide-in__re-Ln {\n from {\n opacity: 0;\n transform: translateY(5px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}";
115
+ var styles = {"textbox":"ZestTextbox-module_textbox__0M5Wq","pulse-light":"ZestTextbox-module_pulse-light__CKfhA","error":"ZestTextbox-module_error__5RoCP","sm":"ZestTextbox-module_sm__yyxXO","md":"ZestTextbox-module_md__fvL10","lg":"ZestTextbox-module_lg__fU93-","fullWidth":"ZestTextbox-module_fullWidth__xn4fT","wrapper":"ZestTextbox-module_wrapper__0ok2A","counter":"ZestTextbox-module_counter__waqIT","pulse-dark":"ZestTextbox-module_pulse-dark__L9PYJ","passwordToggle":"ZestTextbox-module_passwordToggle__I2s4O","eyeIcon":"ZestTextbox-module_eyeIcon__rKiBL","rotate":"ZestTextbox-module_rotate__Ajx19","tooltip":"ZestTextbox-module_tooltip__etRdj","progressBarContainer":"ZestTextbox-module_progressBarContainer__0qFKf","progressBar":"ZestTextbox-module_progressBar__vwttj","counterYellow":"ZestTextbox-module_counterYellow__uYGfs","counterOrange":"ZestTextbox-module_counterOrange__b9baX","helperText":"ZestTextbox-module_helperText__4twSg","fade-slide-in":"ZestTextbox-module_fade-slide-in__re-Ln"};
116
116
  styleInject(css_248z);
117
117
 
118
118
  var filterNumericInput = function (value) {
@@ -252,6 +252,18 @@ var useZestTextboxConfig$1 = function () {
252
252
  return context;
253
253
  };
254
254
 
255
+ var defaultNumberParser = function (value) {
256
+ var parsed = parseFloat(value);
257
+ return isNaN(parsed) ? undefined : parsed;
258
+ };
259
+ var defaultNumberValidator = function (value) {
260
+ if (value === undefined) {
261
+ return "Invalid number format.";
262
+ }
263
+ return true;
264
+ };
265
+ // You can add more default parsers/validators here for other types like 'email', 'date', etc.
266
+
255
267
  // Helper function to resolve a ZestConfigValue
256
268
  function resolveZestConfigValue(configValue, defaultValue) {
257
269
  return __awaiter(this, void 0, void 0, function () {
@@ -287,20 +299,36 @@ var defaultResolvedZestProps = {
287
299
  isMultiline: false,
288
300
  onTextChanged: undefined,
289
301
  helperTextConfig: undefined,
302
+ parser: undefined,
303
+ validator: undefined,
290
304
  };
291
- var useZestTextboxConfig = function (componentZestProps) {
305
+ var useZestTextboxConfig = function (componentZestProps, inputType) {
292
306
  var contextDefaultZestProps = useZestTextboxConfig$1().defaultZestProps;
293
307
  var _a = useState(defaultResolvedZestProps), resolvedZestProps = _a[0], setResolvedZestProps = _a[1];
294
308
  // Memoize the merged props to avoid unnecessary re-renders
295
309
  var mergedZestProps = useMemo(function () {
296
- // Component props take precedence over context default props, which take precedence over hardcoded defaults
297
- return __assign(__assign(__assign({}, defaultResolvedZestProps), contextDefaultZestProps), componentZestProps);
298
- }, [contextDefaultZestProps, componentZestProps]);
310
+ // Start with hardcoded defaults
311
+ var currentMergedProps = __assign({}, defaultResolvedZestProps);
312
+ // Apply context defaults
313
+ currentMergedProps = __assign(__assign({}, currentMergedProps), contextDefaultZestProps);
314
+ // Apply type-specific defaults if not already overridden by context
315
+ if (inputType === "number") {
316
+ if (currentMergedProps.parser === undefined) {
317
+ currentMergedProps.parser = defaultNumberParser;
318
+ }
319
+ if (currentMergedProps.validator === undefined) {
320
+ currentMergedProps.validator = defaultNumberValidator;
321
+ }
322
+ }
323
+ // Apply component-level props (highest precedence)
324
+ currentMergedProps = __assign(__assign({}, currentMergedProps), componentZestProps);
325
+ return currentMergedProps;
326
+ }, [contextDefaultZestProps, componentZestProps, inputType]); // Added inputType to dependencies
299
327
  useEffect(function () {
300
328
  var resolveProps = function () { return __awaiter(void 0, void 0, void 0, function () {
301
- var newResolvedProps, _a, _b, _c, _d, _e, _f, _g;
302
- return __generator(this, function (_h) {
303
- switch (_h.label) {
329
+ var newResolvedProps, _a, _b, _c, _d, _e, _f, _g, _h, _j;
330
+ return __generator(this, function (_k) {
331
+ switch (_k.label) {
304
332
  case 0:
305
333
  newResolvedProps = __assign({}, defaultResolvedZestProps);
306
334
  // Resolve each property that can be a ZestConfigValue
@@ -308,33 +336,41 @@ var useZestTextboxConfig = function (componentZestProps) {
308
336
  return [4 /*yield*/, resolveZestConfigValue(mergedZestProps.zSize, defaultResolvedZestProps.zSize)];
309
337
  case 1:
310
338
  // Resolve each property that can be a ZestConfigValue
311
- _a.zSize = _h.sent();
339
+ _a.zSize = _k.sent();
312
340
  _b = newResolvedProps;
313
341
  return [4 /*yield*/, resolveZestConfigValue(mergedZestProps.stretch, defaultResolvedZestProps.stretch)];
314
342
  case 2:
315
- _b.stretch = _h.sent();
343
+ _b.stretch = _k.sent();
316
344
  _c = newResolvedProps;
317
345
  return [4 /*yield*/, resolveZestConfigValue(mergedZestProps.showProgressBar, defaultResolvedZestProps.showProgressBar)];
318
346
  case 3:
319
- _c.showProgressBar = _h.sent();
347
+ _c.showProgressBar = _k.sent();
320
348
  _d = newResolvedProps;
321
349
  return [4 /*yield*/, resolveZestConfigValue(mergedZestProps.animatedCounter, defaultResolvedZestProps.animatedCounter)];
322
350
  case 4:
323
- _d.animatedCounter = _h.sent();
351
+ _d.animatedCounter = _k.sent();
324
352
  _e = newResolvedProps;
325
353
  return [4 /*yield*/, resolveZestConfigValue(mergedZestProps.theme, defaultResolvedZestProps.theme)];
326
354
  case 5:
327
- _e.theme = _h.sent();
355
+ _e.theme = _k.sent();
328
356
  _f = newResolvedProps;
329
357
  return [4 /*yield*/, resolveZestConfigValue(mergedZestProps.isMultiline, defaultResolvedZestProps.isMultiline)];
330
358
  case 6:
331
- _f.isMultiline = _h.sent();
359
+ _f.isMultiline = _k.sent();
332
360
  // onTextChanged is no longer a ZestConfigValue, so it's directly assigned
333
361
  newResolvedProps.onTextChanged = mergedZestProps.onTextChanged;
334
362
  _g = newResolvedProps;
335
363
  return [4 /*yield*/, resolveZestConfigValue(mergedZestProps.helperTextConfig, defaultResolvedZestProps.helperTextConfig)];
336
364
  case 7:
337
- _g.helperTextConfig = _h.sent();
365
+ _g.helperTextConfig = _k.sent();
366
+ _h = newResolvedProps;
367
+ return [4 /*yield*/, resolveZestConfigValue(mergedZestProps.parser, defaultResolvedZestProps.parser)];
368
+ case 8:
369
+ _h.parser = _k.sent();
370
+ _j = newResolvedProps;
371
+ return [4 /*yield*/, resolveZestConfigValue(mergedZestProps.validator, defaultResolvedZestProps.validator)];
372
+ case 9:
373
+ _j.validator = _k.sent();
338
374
  setResolvedZestProps(newResolvedProps);
339
375
  return [2 /*return*/];
340
376
  }
@@ -345,24 +381,77 @@ var useZestTextboxConfig = function (componentZestProps) {
345
381
  return resolvedZestProps;
346
382
  };
347
383
 
384
+ var useParsedAndValidatedInput = function (_a) {
385
+ var rawValue = _a.rawValue, inputType = _a.inputType, // Destructure inputType
386
+ parser = _a.parser, validator = _a.validator, onParsedAndValidatedChange = _a.onParsedAndValidatedChange;
387
+ var _b = useState(undefined), parsedValue = _b[0], setParsedValue = _b[1];
388
+ var _c = useState(true), isValid = _c[0], setIsValid = _c[1];
389
+ var _d = useState(undefined), validationMessage = _d[0], setValidationMessage = _d[1];
390
+ useEffect(function () {
391
+ var currentParsedValue = undefined;
392
+ var currentIsValid = true;
393
+ var currentValidationMessage = undefined;
394
+ // 1. Parse the raw value
395
+ if (parser) {
396
+ currentParsedValue = parser(rawValue, inputType); // Pass inputType to parser
397
+ }
398
+ else {
399
+ // If no parser, treat rawValue as the parsed value (e.g., for text inputs)
400
+ currentParsedValue = rawValue;
401
+ }
402
+ // 2. Validate the parsed value
403
+ if (validator) {
404
+ var validationResult = validator(currentParsedValue, inputType); // Pass inputType to validator
405
+ if (typeof validationResult === "string") {
406
+ currentIsValid = false;
407
+ currentValidationMessage = validationResult;
408
+ }
409
+ else {
410
+ currentIsValid = validationResult;
411
+ if (!currentIsValid) {
412
+ currentValidationMessage = "Invalid input."; // Generic message if validator returns false
413
+ }
414
+ }
415
+ }
416
+ setParsedValue(currentParsedValue);
417
+ setIsValid(currentIsValid);
418
+ setValidationMessage(currentValidationMessage);
419
+ // 3. Call the consumer's callback with the parsed and validated value
420
+ // Only call if a callback is provided and the input is valid
421
+ if (onParsedAndValidatedChange && currentIsValid) {
422
+ onParsedAndValidatedChange(currentParsedValue);
423
+ }
424
+ }, [rawValue, inputType, parser, validator, onParsedAndValidatedChange]); // Add inputType to dependencies
425
+ return { parsedValue: parsedValue, isValid: isValid, validationMessage: validationMessage };
426
+ };
427
+
348
428
  // ... other imports
349
429
  var ZestTextbox = function (props) {
350
430
  var _a = props.className, className = _a === void 0 ? "" : _a, maxLength = props.maxLength, onChange = props.onChange, type = props.type, zest = props.zest, // Destructure the new zest prop
351
431
  rest = __rest(props, ["className", "maxLength", "onChange", "type", "zest"]);
352
- var resolvedZestProps = useZestTextboxConfig(zest);
353
- var zSize = resolvedZestProps.zSize, fullWidth = resolvedZestProps.stretch, showProgressBar = resolvedZestProps.showProgressBar, animatedCounter = resolvedZestProps.animatedCounter, theme = resolvedZestProps.theme, helperTextConfig = resolvedZestProps.helperTextConfig, onTextChanged = resolvedZestProps.onTextChanged, isMultiline = resolvedZestProps.isMultiline;
432
+ var resolvedZestProps = useZestTextboxConfig(zest, type);
433
+ var zSize = resolvedZestProps.zSize, fullWidth = resolvedZestProps.stretch, showProgressBar = resolvedZestProps.showProgressBar, animatedCounter = resolvedZestProps.animatedCounter, theme = resolvedZestProps.theme, helperTextConfig = resolvedZestProps.helperTextConfig, onTextChanged = resolvedZestProps.onTextChanged, isMultiline = resolvedZestProps.isMultiline, parser = resolvedZestProps.parser, validator = resolvedZestProps.validator;
354
434
  var _b = useState(""), value = _b[0], setValue = _b[1];
355
435
  var isDark = useThemeDetector(theme);
356
436
  var isPassword = type === "password";
357
437
  var _c = usePasswordVisibility(isPassword), isPasswordVisible = _c.isPasswordVisible, togglePasswordVisibility = _c.togglePasswordVisibility;
358
438
  var _d = useCharacterCounter(value, maxLength, animatedCounter), currentLength = _d.currentLength, charPercentage = _d.charPercentage, counterColorClass = _d.counterColorClass, showCounter = _d.showCounter;
359
- var helperTextNode = useHelperText(value, helperTextConfig);
439
+ var _e = useParsedAndValidatedInput({
440
+ rawValue: value,
441
+ inputType: type, // Pass the type prop here
442
+ parser: parser,
443
+ validator: validator,
444
+ onParsedAndValidatedChange: onTextChanged,
445
+ }), isValid = _e.isValid, validationMessage = _e.validationMessage;
446
+ // Prioritize validation message over regular helper text
447
+ var finalHelperTextNode = validationMessage ? (jsx("span", { style: { color: "red" }, children: validationMessage })) : useHelperText(value, helperTextConfig);
360
448
  var classList = [
361
449
  styles.textbox,
362
450
  styles[zSize],
363
451
  fullWidth ? styles.fullWidth : "",
364
452
  className,
365
453
  isDark ? styles.dark : "",
454
+ !isValid ? styles.error : "", // Add error class if not valid
366
455
  ]
367
456
  .filter(Boolean)
368
457
  .join(" ");
@@ -377,14 +466,13 @@ var ZestTextbox = function (props) {
377
466
  setValue(newValue);
378
467
  if (onChange)
379
468
  onChange(e);
380
- if (onTextChanged)
381
- onTextChanged(newValue);
469
+ // onTextChanged is now handled by useParsedAndValidatedInput
382
470
  };
383
471
  var isNumeric = type === "number" || type === "tel";
384
472
  var inputType = isPassword && isPasswordVisible ? "text" : isNumeric ? "tel" : type;
385
473
  var commonProps = __assign({ className: classList, maxLength: maxLength, onChange: handleInputChange, value: value, type: inputType }, rest);
386
474
  return (jsxs("div", { className: styles.wrapper, children: [isMultiline ? ( // Use isMultiline from zest
387
- jsx("textarea", __assign({}, commonProps))) : (jsx("input", __assign({}, commonProps))), jsx(HelperTextDisplay, { helperTextNode: helperTextNode, className: (helperTextConfig === null || helperTextConfig === void 0 ? void 0 : helperTextConfig.className) || '' }), jsx(CharacterCounter, { showCounter: showCounter, currentLength: currentLength, maxLength: maxLength, counterColorClass: counterColorClass }), jsx(PasswordToggleButton, { isPassword: isPassword, isPasswordVisible: isPasswordVisible, onToggle: togglePasswordVisibility }), jsx(ProgressBar, { showProgressBar: showProgressBar, showCounter: showCounter, charPercentage: charPercentage, counterColorClass: counterColorClass })] }));
475
+ jsx("textarea", __assign({}, commonProps))) : (jsx("input", __assign({}, commonProps))), jsx(HelperTextDisplay, { helperTextNode: finalHelperTextNode, className: (helperTextConfig === null || helperTextConfig === void 0 ? void 0 : helperTextConfig.className) || '' }), jsx(CharacterCounter, { showCounter: showCounter, currentLength: currentLength, maxLength: maxLength, counterColorClass: counterColorClass }), jsx(PasswordToggleButton, { isPassword: isPassword, isPasswordVisible: isPasswordVisible, onToggle: togglePasswordVisibility }), jsx(ProgressBar, { showProgressBar: showProgressBar, showCounter: showCounter, charPercentage: charPercentage, counterColorClass: counterColorClass })] }));
388
476
  };
389
477
 
390
478
  export { CharacterCounter, HelperTextDisplay, PasswordToggleButton, ProgressBar, ZestTextbox, ZestTextboxConfigProvider };