@bright-lab/tw-form is available as an [npm package] (https://www.npmjs.org/package/@bright-lab/tw-form)
// with npm
npm install @bright-lab/tw-form
// with yarn
yarn add @bright-lab/tw-form
This Form is so smart that it will handle everything for you, as well as you can style your own inputs, custom components, error validation and responsiveness!
import '@bright-lab/tw-form/css';
import React, { useState } from 'react';
import { DynamicForm, DynamicFields } from '@bright-lab/tw-form';
const fields: DynamicFields = [
{
label: 'Email',
type: 'email',
name: 'email',
required: true,
grid: {
xs: 12,
md: 6,
},
},
{
label: 'Password',
type: 'password',
name: 'password',
required: true,
grid: {
xs: 12,
md: 6,
},
},
{
label: 'Nationality',
type: 'select',
name: 'nationality',
data: [
{
title: 'Lebanon',
value: 'Lebanon',
},
{
title: 'Spain',
value: 'Spain',
},
],
grid: {
xs: 12,
},
},
{
label: 'seperator',
type: 'seperator',
name: 'seperator',
},
{
label: 'Gender',
type: 'radioGroup',
name: 'gender',
content: 'between',
data: [
{
label: 'Male',
value: 'male',
},
{
label: 'Female',
value: 'female',
},
],
grid: {
xs: 12,
md: 6,
},
},
{
label: 'Software',
type: 'checkboxGroup',
name: 'software',
content: 'between',
data: [
{
label: 'Adobe',
value: 'Adobe',
},
{
label: 'VSCode',
value: 'VSCode',
},
],
grid: {
xs: 12,
md: 6,
},
},
{
label: 'Married?',
type: 'checkbox',
name: 'married',
grid: {
xs: 12,
md: 6,
},
},
{
label: 'Kids',
type: 'number',
name: 'kids',
min: 0,
required: true,
grid: {
xs: 12,
md: 6,
},
},
{
label: 'Hobbies',
type: 'chips',
name: 'hobbies',
grid: {
xs: 12,
md: 6,
},
},
];
const validation = {
email: (value: string) => {
if (value?.trim()?.length === 0) {
return 'Email is required';
}
if (!/\S+@\S+\.\S+/.test(value)) {
return 'Email is invalid';
}
return '';
},
password: (value: string) => {
if (value?.length < 8) {
return 'Password is shorter than the minimum length (8)';
}
return '';
},
};
type values = {
name: string,
email: string,
password: string,
married: boolean,
gender: string,
nationality: string,
kids: number,
hobbies: string[],
};
const [values, setValues] = useState({
name: '',
email: '',
password: '',
married: false,
gender: 'male',
nationality: 'Lebanon',
kids: 0,
hobbies: ['basketball', 'football'],
});
const [isError, setIsError] = useState(false);
return (
<div>
<h1>Dynamic Form</h1>
<div className="max-w-[800px] mx-auto bg-slate-50 p-5">
<DynamicForm
fields={fields}
values={values}
setValues={setValues}
validation={validation}
isError={(error) => setIsError(error)} //returns boolean
/>
<button disabled={isError}>Submit</button>
</div>
</div>
);
{
label: 'file',
type: 'custom',
name: 'image',
Component: (customValue: File, customOnChange: () => void, errors: Record<string, string>, expectedErrors: Record<string, string>) => {
return <File value={customValue} onChange={customOnChange} errors={errors} expectedErrors={expectedErrors} />;
},
grid: {
xs: 12,
md: 6,
},
},
const [values, setValues] = useState({
name: '',
email: '',
drink: ''
});
interface Props {
onChange: (value: string) => void;
value: string;
errors: Record<string, string>;
expectedErrors: Record<string, string>;
handleBlur: (e: React.ChangeEvent<HTMLInputElement>) => void;
}
const Drink: React.FC<Props> = ({
onChange,
value,
errors,
expectedErrors,
}) => {
return (
<>
<input
type="text"
name="drink"
value={value}
onChange={(e) => {
onChange(e.target.value);
}}
onBlur={handleBlur} // error validation on blur
/>
{errors?.drink && 'error in drinks'}
</>
);
};
export default File;
import { useRef } from 'react';
import { DynamicForm, DynamicRef } from '@bright-lab/tw-form';
Add the reference to the DynamicForm component, a submit button, and let's handle the functionality.
const dynamicFormRef = useRef<DynamicRef>();
const handleSubmit = () => {
if (isError) {
return dynamicFormRef.current?.onSubmit();
}
console.log('no errors')
};
return (
<>
<DynamicForm
ref={dynamicFormRef}
fields={fields}
values={values}
setValues={setValues}
validation={validation}
isError={(error) => setIsError(error)}
gap={{ rowGap: '3px', columnGap: '3px' }}
/>
<button
onClick={handleSubmit}
>
Submit
</button>
</>
);
:root {
--tw-label-text-color: #000;
--tw-label-text-size: 1rem;
--tw-input-bg-color: #fefefe;
--tw-input-text-color: #4b5563;
--tw-input-text-size: 1rem;
--tw-input-border-color: #d1d5db;
--tw-input-border-focus-color: #475569;
--tw-input-border-radius: 0.5rem;
--tw-input-padding: 0.5rem 0.7rem;
--tw-input-error-border-color: #ef4444;
--tw-input-error-text-color: #ef4444;
--tw-input-radio-checked-color: dodgerblue;
--tw-input-checkbox-checked-color: dodgerblue;
--tw-input-select-arrow-color: #999;
--tw-chips-add-btn-color: dodgerblue;
--tw-chips-dlt-btn-color: #475569;
--tw-chips-selected-bg-color: #e2e8f0;
}
Property | Type | Initial State | Example |
---|---|---|---|
isError |
Callback function returns boolean | null |
(error) => setIsError(error) |
gap |
object | null |
rowGap: '10px', columnGap: '10px' |
useCheckboxAsBoolean |
boolean | true |
false |
darkMode |
boolean | false |
true |
Key | Type | Example | Required | For |
---|---|---|---|---|
label |
String or JSX Element | Name |
No | All |
type |
String | text |
Yes | All |
name |
String | username |
Yes | All |
placeholder |
String | write here.. |
No | All |
required |
Boolean | true |
No | All |
disabled |
Boolean | false |
No | All |
grid |
Object | {xs: 12, md: 6, lg: 3, xl: 2} |
No | All |
groupGrid |
Object | {xs: 12, md: 6, lg: 3, xl: 2} |
No | RadioGroup, checkboxGroup |
minLength |
Number | 5 |
No | Text/Email/Password/Textarea |
maxLength |
Number | 20 |
No | Text/Email/Password/Textarea |
min |
Number | 1 |
No | Number |
max |
Number | 100 |
No | Number |
rows |
Number | 5 |
No | Textarea |
cols |
Number | 5 |
No | Textarea |
generatePassword |
Boolean | true |
No | Password |
data |
Array of Objects | [{label: 'Cream', value:'cream'}] |
Yes | Select, RadioGroup, checkboxGroup |
content |
String | between, around, center, end |
No | RadioGroup, checkboxGroup |
groupCols |
boolean | true |
No | RadioGroup, checkboxGroup |
style |
Object | {marginBottom: '5px'} |
No | All |
The changelog is regularly updated to reflect what's changed in each new release.