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.
npm install jattac.libs.web.zest-textboxA delightful, feature-rich, and highly customizable React textbox component. Built with accessibility and developer experience in mind.
ZestTextbox is a standalone component that extends the standard HTML and elements with a touch of zest, providing a polished, professional, and playful user experience.
- Features
- Installation
- Basic Usage
- Props API
- ZestProps Interface Details
- Feature Examples
- Password Input with Visibility Toggle
- Character Counter & Progress Bar
- Enhanced Numeric Input
- Custom Parser & Validator
- Sizing
- Theming (Light/Dark/System)
- Multiline Textarea
- Helper Text with Formatting
- Full Width Stretching
- Centralized Configuration
- Internal Architecture
- Breaking Changes
- Contributing
- License
ZestTextbox isn't just another input field; it's crafted to bring a smile to your users' faces while providing robust functionality and a seamless developer experience.
* Responsive and Mobile-First: Adapts beautifully to any screen size, ensuring a consistent UX.
* Light & Dark Mode: Automatically respects user system preferences or can be explicitly set.
* Password Visibility Toggle: A crucial accessibility and usability feature for password fields.
* Character Counter & Progress Bar: Visual feedback on input length, with engaging animations as limits are approached.
* Enhanced Numeric Input: Intelligent filtering for numbers, decimals, and negative values.
* Customizable Parsing & Validation: Define how raw string input is converted to a desired type and validated, with context of the input type.
* Engaging Animations: Subtle, delightful micro-interactions on focus and input.
* Accessible: Built with rem units and best practices for inclusivity.
* Centralized Configuration: Easily manage default behaviors and styles across your application using React Context.
To install jattac.libs.web.zest-textbox in your project, run:
``bash`
npm install jattac.libs.web.zest-textbox
Get started with ZestTextbox in seconds. It behaves just like a standard HTML or
`jsx
import React from 'react';
import ZestTextbox from 'jattac.libs.web.zest-textbox';
const App = () => {
return (
export default App;
`
ZestTextbox accepts all standard HTML and
| Prop | Type | Default | Description |
| ----------- | ---------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| zest | ZestProps | undefined | An object containing all custom configurations and behaviors specific to the ZestTextbox component. See ZestProps interface for details below. |className
| | string | "" | A custom CSS class to apply to the main textbox element. |maxLength
| | number | undefined | The maximum number of characters allowed. Enables the character counter and progress bar. |type
| | HtmlInputType | 'text' | The type of the input element. All standard HTML input types are supported, plus the semantic types "currency" and "percentage". Special handling is applied for password, number, currency, and percentage. |
The zest prop is an object that encapsulates all the unique features of ZestTextbox. Its properties can accept primitive values, functions that receive the input's type for type-aware logic, or asynchronous versions of those functions (ZestConfigValue). This is especially powerful for dynamic, app-wide settings with Centralized Configuration.
`typescript
import { ZestTextboxSize, ZestConfigValue, HelperTextConfig, InputParser, InputValidator, HtmlInputType } from 'jattac.libs.web.zest-textbox';
interface ZestProps {
helperTextConfig?: ZestConfigValue
onTextChanged?:
zSize?: ZestConfigValue
stretch?: ZestConfigValue
showProgressBar?: ZestConfigValue
animatedCounter?: ZestConfigValue
theme?: ZestConfigValue<"light" | "dark" | "system">; // Color scheme
isMultiline?: ZestConfigValue
| Property | Type (ZestConfigValue) | Default | Description |helperTextConfig
| ----------------- | --------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| | ZestConfigValue | 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. |onTextChanged
| | | 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. |zSize
| | ZestConfigValue<"sm" | "md" | "lg"> | 'md' | Sets the size of the textbox, affecting padding and font size. |stretch
| | ZestConfigValue | false | If true, the component will stretch to the full width of its container. |showProgressBar
| | ZestConfigValue | false | If true, a progress bar indicating character count vs. maxLength will be displayed. Requires maxLength to be set. |animatedCounter
| | ZestConfigValue | false | If true, the character counter will change color as it approaches the maxLength. Requires maxLength to be set. |theme
| | ZestConfigValue<"light" | "dark" | "system"> | 'system' | Controls the component's color scheme. 'system' automatically detects the OS/browser preference. |isMultiline
| | ZestConfigValue | false | If true, the component will render as a
Dive into practical examples demonstrating the power and flexibility of ZestTextbox.
Simply set the type prop to "password" to enable the built-in visibility toggle.
`jsx
import React from 'react';
import ZestTextbox from 'jattac.libs.web.zest-textbox';
const PasswordExample = () => {
return (
export default PasswordExample;
`
Enable the character counter with maxLength and add a visual progress bar and animated counter via zest props.
`jsx
import React from 'react';
import ZestTextbox from 'jattac.libs.web.zest-textbox';
const CounterExample = () => {
const [text, setText] = React.useState('');
return (
export default CounterExample;
`
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.
You can also use the semantic types type="currency" or type="percentage". These behave identically to type="number" by default but provide clearer intent and allow for type-specific logic in global configurations.
`jsx
import React from 'react';
import ZestTextbox from 'jattac.libs.web.zest-textbox';
const NumericExample = () => {
const [age, setAge] = React.useState
const [price, setPrice] = React.useState
const currencyFormatter = (val: string) => {
const num = parseFloat(val);
return isNaN(num) ? '' : ${num.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })};
};
return (
Age (type="number"): {age === undefined ? 'N/A' : age}
Price (type="currency"): {price === undefined ? 'N/A' : price}
}
}}
/>
export default NumericExample;
`
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.
`jsx
import React from 'react';
import ZestTextbox, { InputParser, InputValidator, HtmlInputType } from 'jattac.libs.web.zest-textbox';
// Custom parser for positive integers
const positiveIntegerParser: InputParser
if (inputType === 'number') {
const parsed = parseInt(value, 10);
return isNaN(parsed) ? undefined : parsed;
}
return undefined;
};
// Custom validator for positive integers
const positiveIntegerValidator: InputValidator
if (inputType === 'number') {
if (parsedValue === undefined) {
return "Please enter a valid integer.";
}
if (parsedValue <= 0) {
return "Value must be a positive integer.";
}
}
return true;
};
const CustomNumericParserValidatorExample = () => {
const [quantity, setQuantity] = React.useState
return (
Quantity: {quantity === undefined ? 'N/A' : quantity}
export default CustomNumericParserValidatorExample;
`
Control the size of the textbox with the zSize property within the zest prop. Options are "sm", "md" (default), and "lg".
`jsx
import React from 'react';
import ZestTextbox from 'jattac.libs.web.zest-textbox';
const SizingExample = () => {
return (
export default SizingExample;
`
Force the component into a specific theme or let it adapt to the user's system preference using the theme property.
`jsx
import React from 'react';
import ZestTextbox from 'jattac.libs.web.zest-textbox';
const ThemingExample = () => {
return (
export default ThemingExample;
`
Render ZestTextbox as a
`jsx
import React from 'react';
import ZestTextbox from 'jattac.libs.web.zest-textbox';
const MultilineExample = () => {
return (
export default MultilineExample;
`
Provide dynamic helper text below the input using helperTextConfig. The formatter and templater functions now receive a rich context object, giving you access to the raw value, parsedValue, and the component's props.
`jsx
import React from 'react';
import ZestTextbox, { ZestContext } from 'jattac.libs.web.zest-textbox';
const HelperTextExample = () => {
const [amount, setAmount] = React.useState
const [message, setMessage] = React.useState('');
const currencyFormatter = (context: ZestContext
const num = context.parsedValue;
return num === undefined ? '' : ${num.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })};
};
const messageTemplater = (formattedValue: string, context: ZestContext
(context.props.maxLength || 0) * 0.8 ? 'orange' : 'green' }}>
Length: {context.value.length} / {context.props.maxLength || '∞'}
);
return (
export default HelperTextExample;
`
Make the textbox take up the full width of its parent container by setting stretch to true in the zest prop.
`jsx
import React from 'react';
import ZestTextbox from 'jattac.libs.web.zest-textbox';
const StretchExample = () => {
return (
export default StretchExample;
`
To maintain consistency and reduce boilerplate, ZestTextbox supports centralized configuration using React Context. This allows you to define default ZestProps for all ZestTextbox instances within a provider's scope. Component-level zest props will always override these global defaults.
1. ZestTextboxConfigProvider: Wrap your application or a part of it with this provider. Pass a value prop containing the default ZestProps you want to apply.ZestConfigValue
2. : Properties within ZestProps can be a primitive value (T), a function that receives the inputType and returns T ((inputType?: HtmlInputType) => T), or an asynchronous function that does the same ((inputType?: HtmlInputType) => Promise). This provides immense flexibility for creating dynamic, type-aware defaults.
Here's how you can set up a global theme and size, and then override it for a specific component. The zSize prop is configured to return a different size depending on the inputType.
`jsx
import React from 'react';
import ZestTextbox, { ZestTextboxConfigProvider, InputParser, InputValidator, HtmlInputType } from 'jattac.libs.web.zest-textbox';
const AppWithCentralConfig = () => {
// Define your global default ZestProps
const globalDefaultZestProps = {
theme: "dark", // All textboxes will be dark by default
// Make size dynamic based on the input type
zSize: (inputType) => {
if (inputType === 'password') return 'sm'; // Small for passwords
return 'lg'; // Large for everything else
},
animatedCounter: Promise.resolve(true), // Async example
};
return (
Centralized Configuration Example
Default ZestTextbox (inherits global defaults)
Overridden ZestTextbox (component props take precedence)
zest={{ theme: "light" }} // Overrides the global dark theme
/>
);
};
export default AppWithCentralConfig;
`
The resolution order for ZestProps is as follows:
1. Component-level zest prop: Explicit props passed directly to a ZestTextbox instance have the highest priority.ZestTextboxConfigProvider
2. value prop: Defaults provided by the nearest ZestTextboxConfigProvider come next.zest
3. Hardcoded internal defaults: If no prop is provided on the component and no provider is found (or a specific property isn't defined in the provider), internal hardcoded defaults are used.
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.
The internal structure now includes:
* UI/hooks/: Contains custom React hooks (e.g., useThemeDetector, usePasswordVisibility, useCharacterCounter, useHelperText, useZestTextboxConfig, useParsedAndValidatedInput).UI/components/
* : Contains smaller, focused UI components (e.g., PasswordToggleButton, CharacterCounter, ProgressBar, HelperTextDisplay).UI/utils/
* : Contains utility functions (e.g., numericInputFilter, defaultParsersAndValidators).UI/types.ts
* : Defines shared TypeScript interfaces and types (e.g., HtmlInputType, InputParser, InputValidator, ZestConfigValue, ResolvedZestProps).UI/contexts/
* : Contains React Contexts and Providers (e.g., ZestTextboxConfigContext, ZestTextboxConfigProvider).
All custom props (isMultiline, zSize, stretch, theme, animatedCounter, showProgressBar, onTextChanged, helperTextConfig) have been removed as top-level props and are now encapsulated within a single zest object prop.
Migration Guide:
If you were previously using:
`jsx`
zSize="lg"
theme="dark"
onTextChanged={(value) => console.log(value)}
/>
You should now update your code to:
`jsx`
isMultiline: true,
zSize: "lg",
theme: "dark",
onTextChanged: (value) => console.log(value),
}}
/>
Contributions are welcome! If you have a feature request, bug report, or pull request, please feel free to open an issue or submit a PR.
1. Clone the repository.
2. Install dependencies with npm install.npm run build
3. Run the build with .UI/hooks
4. The internal architecture now includes , UI/components, UI/utils, and UI/contexts` directories for better organization and separation of concerns.
This project is licensed under the ISC License.