@waysnx/ui-core
v0.4.0
Published
Core UI components from WaysNX - includes inputs, buttons, date pickers, and form controls
Maintainers
Readme
@waysnx/ui-core
Core UI components from WaysNX - A comprehensive React component library with form controls, inputs, buttons, date pickers, and more.
Table of Contents
- Features
- Installation
- Quick Start
- Components
- Validation
- Input Masking
- Dynamic Options (x-xref)
- File Upload
- JSON Schema
- Theming
- Security
- TypeScript
- Examples
Features
- 🎨 Modern, accessible UI components
- 📝 Complete form controls (Input, Select, Checkbox, Radio, etc.)
- 📅 Date and date range pickers
- 🔒 Built-in XSS protection with DOMPurify
- 🎯 TypeScript support
- 🌙 Dark mode ready
- 📦 Tree-shakeable
- 🎨 CSS variables for easy theming
- 📋 JSON Schema to Form converter
- ✅ Built-in validation system
- 🎭 Input masking (number, phone, currency)
- 🔗 Dynamic options loading (x-xref pattern)
- ⚡ Optimized and minified for production
Installation
Quick Install
npm install @waysnx/ui-core react-datepickerImport CSS
Import the required CSS files in your main entry file (e.g., main.tsx or App.tsx):
import '@waysnx/ui-core/dist/index.css';
import 'react-datepicker/dist/react-datepicker.css';Choosing the Right Package
This package provides core UI components only. If you need schema-driven forms:
- For complete features: Install
@waysnx/ui-kit(includes everything) - For schema forms: Install
@waysnx/ui-core+@waysnx/ui-form-builder
📖 Installation Guide for detailed comparison and examples.
Dependencies
The package includes these dependencies:
dompurify- XSS protection for HTML contentreact-number-format- Number and currency maskingreact-imask- Phone and custom pattern masking
Peer dependencies (you need to install):
react>= 18react-dom>= 18react-datepicker^8.0.0
Quick Start
Get started in 3 simple steps:
import { Input, Button } from '@waysnx/ui-core';
import '@waysnx/ui-core/dist/index.css';
import { useState } from 'react';
function App() {
const [name, setName] = useState('');
return (
<div>
<Input
label="Your Name"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Enter your name"
/>
<Button variant="primary" onClick={() => alert(`Hello, ${name}!`)}>
Submit
</Button>
</div>
);
}Usage
Basic Components
import { Button, Input, Select, Checkbox } from '@waysnx/ui-core';
import { useState } from 'react';
function MyForm() {
const [name, setName] = useState('');
const [country, setCountry] = useState('');
const [agreed, setAgreed] = useState(false);
return (
<div>
<Input
label="Name"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Enter your name"
required
/>
<Select
label="Country"
value={country}
onChange={(e) => setCountry(e.target.value)}
options={[
{ value: 'us', label: 'United States' },
{ value: 'uk', label: 'United Kingdom' },
{ value: 'ca', label: 'Canada' }
]}
/>
<Checkbox
label="I agree to terms and conditions"
checked={agreed}
onChange={(e) => setAgreed(e.target.checked)}
/>
<Button variant="primary" type="submit">
Submit
</Button>
</div>
);
}Switch Component
import { Switch } from '@waysnx/ui-core';
import { useState } from 'react';
function MyComponent() {
const [enabled, setEnabled] = useState(false);
return (
<Switch
label="Enable notifications"
checked={enabled}
onChange={(e) => setEnabled(e.target.checked)}
/>
);
}Date Picker
import { DatePicker } from '@waysnx/ui-core';
import { useState } from 'react';
function MyComponent() {
const [date, setDate] = useState(null);
return (
<DatePicker
label="Select Date"
selected={date}
onChange={setDate}
placeholder="Choose a date"
/>
);
}Multi-Select with Select All
import { Select } from '@waysnx/ui-core';
import { useState } from 'react';
function MyComponent() {
const [selected, setSelected] = useState([]);
return (
<Select
label="Select Skills"
value={selected}
onChange={setSelected}
options={[
{ value: 'js', label: 'JavaScript' },
{ value: 'ts', label: 'TypeScript' },
{ value: 'react', label: 'React' }
]}
multiple
showSelectAll
/>
);
}Searchable Select
Add searchable prop to enable a search box in the dropdown. Works with both single and multi-select.
<Select
label="Country"
searchable
options={[
{ value: 'us', label: 'United States' },
{ value: 'uk', label: 'United Kingdom' },
{ value: 'ca', label: 'Canada' },
]}
value={country}
onChange={setCountry}
/>For JSON Schema, use "x-searchable": true:
{
"country": {
"type": "string",
"enum": ["us", "uk", "ca"],
"x-enum-labels": ["United States", "United Kingdom", "Canada"],
"x-searchable": true
}
}File Upload
Upload files with drag & drop, preview, and validation:
import { FileUpload } from '@waysnx/ui-core';
import { useState } from 'react';
function MyComponent() {
const [file, setFile] = useState(null);
return (
<FileUpload
label="Upload Document"
accept=".pdf,.doc,.docx"
maxSize={5} // 5MB
format="blob"
showPreview
showLastModified
onChange={(fileData) => setFile(fileData)}
onError={(error) => console.error(error)}
/>
);
}FileUpload Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| label | string | - | Label for the upload area |
| accept | string | - | Accepted file types (e.g., '.pdf,.jpg') |
| maxSize | number | - | Max file size in MB |
| format | 'blob' \| 'binary' | 'blob' | Output format |
| multiple | boolean | false | Allow multiple files |
| showPreview | boolean | false | Show image preview |
| showLastModified | boolean | false | Show last modified date |
| autoUpload | boolean | false | Auto-upload on select |
| browseButtonText | string | 'Browse' | Custom button text |
| disabled | boolean | false | Disable upload |
| onChange | (file) => void | - | Called when file selected |
| onError | (error) => void | - | Called on error |
JSON Schema FileUpload
const schema = {
type: 'object',
properties: {
resume: {
type: 'string',
format: 'binary',
title: 'Upload Resume',
'x-accept': '.pdf,.doc,.docx',
'x-file-size': 5, // MB
'x-file-format': 'blob',
'x-show-file-preview': true,
'x-show-last-modified': true,
'x-browse-button-text': 'Choose File',
},
photos: {
type: 'array',
title: 'Upload Photos',
items: {
type: 'string',
format: 'binary',
},
'x-accept': '.jpg,.jpeg,.png',
'x-file-size': 10,
'x-show-file-preview': true,
},
},
};Validation
Built-in validation system with support for common validation rules:
import { Input } from '@waysnx/ui-core';
import { useState } from 'react';
function MyForm() {
const [email, setEmail] = useState('');
return (
<Input
label="Email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$"
minLength={5}
maxLength={100}
errorMessage="Please enter a valid email address"
validateOnBlur
onValidation={(isValid, error) => {
console.log('Valid:', isValid, 'Error:', error);
}}
/>
);
}Validation Props
required- Field is requiredpattern- Regex pattern validationminLength/maxLength- String length validationmin/max- Number range validationerrorMessage- Custom error messagevalidateOnChange- Validate on every changevalidateOnBlur- Validate on blur (default: true)onValidation- Callback with validation result
JSON Schema Validation
const schema = {
type: 'object',
properties: {
email: {
type: 'string',
format: 'email',
title: 'Email',
minLength: 5,
maxLength: 100,
'x-error-message': 'Please enter a valid email'
},
age: {
type: 'number',
title: 'Age',
minimum: 18,
maximum: 120,
'x-error-message': 'Age must be between 18 and 120'
}
},
required: ['email', 'age']
};Input Masking
Professional input masking for numbers, phone numbers, and currency.
Number Masking
import { Input } from '@waysnx/ui-core';
<Input
label="Amount"
type="number"
mask
thousandSeparator=","
decimalSeparator="."
decimalScale={2}
allowNegative={false}
value={amount}
onChange={(e) => setAmount(e.target.value)}
/>
// Output: 1,234.56Phone Masking
import { Input } from '@waysnx/ui-core';
// Custom pattern
<Input
label="Phone"
type="tel"
mask="+1 (000) 000-0000"
value={phone}
onChange={(e) => setPhone(e.target.value)}
/>
// Output: +1 (555) 123-4567
// Basic validation (allows phone characters)
<Input
label="Phone"
type="tel"
mask={true}
value={phone}
onChange={(e) => setPhone(e.target.value)}
/>Currency Component
import { Currency } from '@waysnx/ui-core';
// US Dollar
<Currency
label="Price"
currencySymbol="$"
currencySymbolPosition="start"
precision={2}
value={price}
onChange={setPrice}
/>
// Output: $1,234.56
// Euro (European format)
<Currency
label="Price"
currencySymbol="€"
currencySymbolPosition="end"
precision={2}
thousandSeparator="."
decimalSeparator=","
value={price}
onChange={setPrice}
/>
// Output: 1.234,56€Common Mask Patterns
US Phone: +1 (000) 000-0000
UK Phone: +44 00 0000 0000
Simple Phone: 000-000-0000
Parens Phone: (000) 000-0000JSON Schema Masking
const schema = {
type: 'object',
properties: {
amount: {
type: 'number',
title: 'Amount',
'x-mask': true,
'x-thousand-separator': ',',
'x-decimal-scale': 2
},
phone: {
type: 'string',
format: 'tel',
title: 'Phone',
'x-mask': '+1 (000) 000-0000'
},
price: {
type: 'number',
title: 'Price',
'x-currency-symbol': '$',
'x-currency-position': 'start',
'x-precision': 2
}
}
};Dynamic Options (x-xref Pattern)
Load options dynamically from APIs for Select, Radio, Checkbox, and Autocomplete components.
Basic Usage
import { Select } from '@waysnx/ui-core';
// API client function (you provide this)
const fetchOptions = async (url) => {
const response = await fetch(url);
return response.json();
};
<Select
label="Country"
fetchOptions={fetchOptions}
xrefUrl="/api/countries"
xrefIdProp="id"
xrefDisplayProp="name"
value={country}
onChange={setCountry}
onLoadStart={() => console.log('Loading...')}
onLoadEnd={() => console.log('Loaded!')}
onError={(error) => console.error(error)}
/>How It Works
- You control HTTP requests - Pass your own
fetchOptionsfunction - Library handles UI - Loading states, error display, option mapping
- Secure by design - No SSRF vulnerability (apps control all network calls)
Supported Components
Select- Dropdown with dynamic optionsRadio- Radio group with dynamic optionsCheckbox- Checkbox group with dynamic optionsAutocomplete- Searchable dropdown with dynamic options
Props
fetchOptions- Your function to fetch data:(url: string) => Promise<any[]>xrefUrl- API endpoint URLxrefIdProp- Property name for option value (e.g., 'id')xrefDisplayProp- Property name for option label (e.g., 'name')onLoadStart- Callback when loading startsonLoadEnd- Callback when loading completesonError- Callback on error
Priority
Static options take priority over dynamic options:
// If options prop is provided, x-xref is ignored
<Select
options={[{ value: '1', label: 'Option 1' }]}
xrefUrl="/api/options" // This will be ignored
/>JSON Schema x-xref
import { schemaToFormFields } from '@waysnx/ui-form-builder';
const schema = {
type: 'object',
properties: {
country: {
type: 'string',
title: 'Country',
'x-xref-url': '/api/countries',
'x-xref-id-prop': 'id',
'x-xref-display-prop': 'name'
}
}
};
// Your API client
const fetchOptions = async (url) => {
const response = await fetch(url);
return response.json();
};
// Generate fields
const fields = schemaToFormFields(schema, formData, handleChange);
// Render with fetchOptions
{fields.map(field => (
<div key={field.name}>
{field.useFormFieldLabel && <label>{field.label}</label>}
{React.cloneElement(field.component, { fetchOptions })}
</div>
))}Note: schemaToFormFields is from @waysnx/ui-form-builder package. Install it separately if you need schema-driven forms.
Example API Response
Your API should return an array of objects:
[
{ "id": "us", "name": "United States" },
{ "id": "uk", "name": "United Kingdom" },
{ "id": "ca", "name": "Canada" }
]Or an object with a data property:
{
"data": [
{ "id": "us", "name": "United States" },
{ "id": "uk", "name": "United Kingdom" }
]
}JSON Schema to Form
This package provides the core UI components. For schema-driven forms, install @waysnx/ui-form-builder:
npm install @waysnx/ui-form-builderThe form builder package provides:
schemaToFormFields- Convert JSON Schema to form componentsFormArray- Dynamic repeatable form sections- Conditional logic (x-show-when, x-disable-when, x-required-when)
See the @waysnx/ui-form-builder package documentation for complete JSON Schema form examples.
Available Components
Form Controls
Input- Text, email, password, number inputs with optional password toggle and maskingCurrency- Currency input with symbol, precision, and formattingSelect- Single and multi-select dropdowns with optional select-all, search, and dynamic optionsCheckbox- Single checkbox or checkbox group with dynamic options (supportscolumnsprop for grid layout)Radio- Radio button group with dynamic options (supportscolumnsprop for grid layout)Textarea- Multi-line text inputSwitch- Toggle switchSlider- Range sliderAutocomplete- Searchable dropdown with filtering and dynamic optionsFileUpload- File upload with drag & drop, preview, and validation
Date & Time Components
DatePicker- Single date pickerDateRangePicker- Date range pickerDateTimePicker- Date and time pickerTimePicker- Time picker
Buttons & Navigation
Button- Button with variants (primary, secondary, destructive, outline, ghost)Link- Styled link component
Content & Display
HtmlEditor- Rich text editor (sanitized with DOMPurify)HtmlContent- Display HTML content (sanitized with DOMPurify)
Layout & Structure
Hidden- Conditionally hide contentTree- Tree view component
Utilities
ErrorMessage- Form field error message displayuseDebounce- Debounce hook for performance
Note: For JSON Schema to form conversion, see @waysnx/ui-form-builder package which provides:
schemaToFormFields- Convert JSON Schema to form componentsFormArray- Dynamic repeatable form sections- Conditional logic (x-show-when, x-disable-when, x-required-when)
Button Variants
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="destructive">Delete</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>Theming
All components use CSS variables for theming. Override these in your CSS to match your brand — no per-component configuration needed.
Quick Start
/* my-theme.css */
:root {
--wx-color-primary: #2563eb;
--wx-color-primary-hover: #1d4ed8;
--wx-color-primary-light: #eff6ff;
--wx-shadow-focus: 0 0 0 3px rgba(37, 99, 235, 0.1);
}Import after the library CSS:
import '@waysnx/ui-core/dist/styles/index.css';
import './my-theme.css'; // Your overridesAvailable CSS Variables
:root {
/* Brand */
--wx-color-primary: #f19924;
--wx-color-primary-hover: #e08916;
--wx-color-primary-contrast: #ffffff;
--wx-color-primary-light: #fff5e6;
/* Text */
--wx-color-text: #1e293b;
--wx-color-text-muted: #64748b;
--wx-color-text-light: #94a3b8;
/* Surfaces */
--wx-color-surface: #ffffff;
--wx-color-surface-alt: #f8fafc;
--wx-color-surface-hover: #f1f5f9;
/* Borders */
--wx-color-border: #e2e8f0;
/* Status */
--wx-color-error: #ef4444;
--wx-color-error-hover: #b91c1c;
--wx-color-error-light: #fef2f2;
--wx-color-success: #22c55e;
--wx-color-success-light: #f0fdf4;
--wx-color-warning: #f59e0b;
--wx-color-info: #3b82f6;
/* Focus & Overlay */
--wx-shadow-focus: 0 0 0 3px rgba(241, 153, 36, 0.1);
--wx-overlay-bg: rgba(0, 0, 0, 0.4);
/* Input Height */
--wx-input-height: 40px;
}Dark Mode
[data-theme="dark"] {
--wx-color-surface: #1e1e1e;
--wx-color-surface-alt: #141414;
--wx-color-surface-hover: #334155;
--wx-color-text: #f8fafc;
--wx-color-text-muted: #cbd5e1;
--wx-color-text-light: #94a3b8;
--wx-color-border: #475569;
--wx-color-primary-light: #3b2a10;
}These same variables work across all WaysNX libraries (ui-core, ui-form-builder, ui-layout, ui-feedback).
JSON Schema Extensions
The library supports custom JSON Schema extensions using the x- prefix:
General Extensions
x-placeholder- Placeholder text for inputsx-error-message- Custom validation error messagex-component- Force specific component:'checkbox'- Single checkbox for boolean'checkbox-group'- Multiple checkboxes for array'toggle'or'switch'- Toggle switch for boolean'button'- Render as button'multiselect'- Multi-select dropdown'tree'- Tree view'link'- Link component'htmlEditor'- HTML editor'dateRange'- Date range picker'hidden'- Hidden field
x-button-type- Button type ('submit', 'button', 'reset')x-button-variant- Button variant ('primary', 'secondary', 'destructive', 'outline', 'ghost')
Masking Extensions
x-mask- Enable masking (boolean or pattern string)x-thousand-separator- Thousand separator (default: ',')x-decimal-separator- Decimal separator (default: '.')x-decimal-scale- Number of decimal places (default: 2)x-currency-symbol- Currency symbol (e.g., '$', '€', '£')x-currency-position- Symbol position ('start' or 'end')x-precision- Currency decimal precision (default: 2)
Dynamic Options Extensions (x-xref)
x-xref-url- API endpoint URL for dynamic optionsx-xref-id-prop- Property name for option valuex-xref-display-prop- Property name for option label
File Upload Extensions
x-accept- Accepted file types (e.g., '.pdf,.jpg')x-file-size- Maximum file size in MBx-file-format- Output format ('blob' or 'binary')x-show-file-preview- Show image preview (boolean)x-show-last-modified- Show last modified date (boolean)x-skip-file-upload-btn- Hide browse button (boolean)x-browse-button-text- Custom browse button text
Conditional Logic Extensions
x-show-when- Array of conditions to show fieldx-disable-when- Array of conditions to disable fieldx-required-when- Array of conditions to make field required
Condition Format
{
name: string; // Field name to watch
value?: any; // Value to compare against
operator?: '==' | '!=' | '>' | '<' | '>=' | '<=' | 'notEmpty' | 'isEmpty';
}Conditional Logic Example
const schema = {
type: 'object',
properties: {
accountType: {
type: 'string',
enum: ['personal', 'business'],
},
companyName: {
type: 'string',
title: 'Company Name',
'x-show-when': [{ name: 'accountType', value: 'business' }],
},
age: {
type: 'integer',
title: 'Age',
},
parentConsent: {
type: 'boolean',
title: 'Parent Consent',
'x-show-when': [{ name: 'age', value: 18, operator: '<' }],
},
},
};Note: Conditional logic is implemented in @waysnx/ui-form-builder. Install that package separately for this functionality.
Other Extensions
x-data- Static data for components (tree, autocomplete)x-columns- Number of columns for Radio/Checkbox group grid layout (e.g.,3renders options in 3 columns)
JSON Schema Type Mappings
The library automatically maps JSON Schema types to components:
| Schema Type | Format | Component |
|------------|--------|-----------|
| string | - | Input (text) |
| string | email | Input (email) |
| string | password | Input (password) with toggle |
| string | date | DatePicker |
| string | date-time | DateTimePicker |
| string | time | TimePicker |
| string | binary | FileUpload |
| string | uri | Input (url) |
| string | textarea | Textarea |
| string | html | HtmlEditor |
| string | enum | Select (single) |
| integer / number | - | Input (number) |
| integer / number | with min/max | Slider |
| boolean | - | Switch (default) |
| boolean | x-component: 'checkbox' | Checkbox |
| array | enum items | Select (multi) or Checkbox group |
TypeScript Support
Full TypeScript support with type definitions included:
import type {
InputProps,
SelectProps,
CheckboxProps,
ButtonProps,
// ... other component props
} from '@waysnx/ui-core';
// For JSON Schema types, import from ui-form-builder
import type { JSONSchema, JSONSchemaProperty } from '@waysnx/ui-form-builder';Security
This package is designed with security as a top priority:
XSS Protection ✅
- All HTML content is sanitized using DOMPurify
- HtmlEditor and HtmlContent components use strict allowlists
- No direct
innerHTMLmanipulation - Safe to render user-generated content
SSRF Protection ✅
- Hybrid architecture - Library doesn't make HTTP requests directly
- Applications control all network requests via
fetchOptionscallback - No SSRF vulnerability in library code
- Applications MUST implement URL validation (see SECURITY_GUIDE.md)
Code Injection Protection ✅
- No
eval()ornew Function()usage - Safe operator-based conditional logic
- No dynamic code execution
For detailed security information and best practices, see:
- SECURITY_AUDIT.md - Complete security audit report
- SECURITY_GUIDE.md - Security guide for developers
Browser Support
- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
Peer Dependencies
react>= 18react-dom>= 18react-datepicker^8.0.0
Complete Example
Here's a comprehensive example using validation, masking, and dynamic options:
import { Input, Currency, Select } from '@waysnx/ui-core';
import { schemaToFormFields } from '@waysnx/ui-form-builder';
import { useState } from 'react';
// Your API client
const fetchOptions = async (url) => {
const response = await fetch(url);
return response.json();
};
function CompleteForm() {
const [formData, setFormData] = useState({});
const schema = {
type: 'object',
properties: {
// Text input with validation
email: {
type: 'string',
format: 'email',
title: 'Email',
minLength: 5,
maxLength: 100,
'x-placeholder': '[email protected]',
'x-error-message': 'Please enter a valid email'
},
// Number input with masking
amount: {
type: 'number',
title: 'Amount',
'x-mask': true,
'x-thousand-separator': ',',
'x-decimal-scale': 2,
minimum: 0,
maximum: 1000000
},
// Phone input with pattern
phone: {
type: 'string',
format: 'tel',
title: 'Phone Number',
'x-mask': '+1 (000) 000-0000'
},
// Currency input
price: {
type: 'number',
title: 'Price',
'x-currency-symbol': '$',
'x-currency-position': 'start',
'x-precision': 2
},
// Select with dynamic options
country: {
type: 'string',
title: 'Country',
'x-xref-url': '/api/countries',
'x-xref-id-prop': 'id',
'x-xref-display-prop': 'name'
},
// Date picker
birthdate: {
type: 'string',
format: 'date',
title: 'Date of Birth'
},
// Checkbox group
interests: {
type: 'array',
title: 'Interests',
items: {
type: 'string',
enum: ['Technology', 'Sports', 'Music', 'Travel']
},
'x-component': 'checkbox-group'
}
},
required: ['email', 'phone', 'country']
};
const handleChange = (name, value) => {
setFormData(prev => ({ ...prev, [name]: value }));
};
const handleSubmit = (e) => {
e.preventDefault();
console.log('Form data:', formData);
};
const fields = schemaToFormFields(schema, formData, handleChange);
return (
<form onSubmit={handleSubmit}>
{fields.map(field => (
<div key={field.name}>
{field.useFormFieldLabel && (
<label>
{field.label}
{field.required && <span>*</span>}
</label>
)}
{React.cloneElement(field.component, { fetchOptions })}
</div>
))}
<button type="submit">Submit</button>
</form>
);
}Installation for JSON Schema forms:
npm install @waysnx/ui-core @waysnx/ui-form-builder react-datepicker
## License
MIT
## Links
- **npm Package**: [@waysnx/ui-core](https://www.npmjs.com/package/@waysnx/ui-core)
- **Security**: Contact [email protected] for security documentation
## Support
- 📧 Email: [email protected]
- 📖 Documentation: This README
- 🔒 Security: [email protected]
## Author
**WaysNX Technologies**
---
Made with ❤️ by WaysNX Technologies