1.0.26 • Published 8 months ago

tac-form v1.0.26

Weekly downloads
-
License
MIT
Repository
github
Last release
8 months ago

tac-form

tac-form is a powerful, JSON Schema-based library for creating dynamic forms in React applications. It offers a flexible and intuitive way to generate, manage, and validate forms with minimal boilerplate.

Table of Contents

  1. Key Features
  2. Installation
  3. Basic Usage
  4. Form Configuration
  5. Field Types
  6. Conditional Rendering
  7. Validation
  8. External Component Integration
  9. Context and Form Management
  10. Theming and Styling
  11. Advanced Examples
  12. API Reference
  13. Contributing
  14. License

Key Features

  • Dynamic form generation based on JSON configuration
  • Support for multiple field types
  • Conditional field rendering
  • Form-level and field-level validation
  • Integration with external UI libraries
  • Dark mode support
  • Multi-form management via React Context

Installation

  1. Set up Tailwind CSS (if not already configured in your project)

  2. Install the library:

npm install tac-form
  1. Copy the component to your working directory:
npx tac-form add

Basic Usage

Here's a simple example of how to use tac-form:

import React from 'react';
import { DynamicForm, FormProvider } from 'tac-form';

const App = () => {
  const formConfig = {
    form: {
      id: 'simpleForm',
      submitText: 'Submit',
      onSubmit: (data) => {
        console.log('Form submitted:', data);
      },
    },
    fields: [
      {
        name: 'email',
        label: 'Email',
        type: 'email',
      },
      {
        name: 'password',
        label: 'Password',
        type: 'password',
      },
    ],
  };

  return (
    <FormProvider>
      <DynamicForm id="simpleForm" config={formConfig} />
    </FormProvider>
  );
};

export default App;

Form Configuration

The FormConfig object is the core of tac-form. It defines the structure and behavior of your form:

interface FormConfig<T extends Record<string, unknown>> {
  form: {
    id: string;
    submitText: ReactNode | string;
    onSubmit: (data: T) => void;
  };
  fields: FieldInput<T>[];
}

Field Types

tac-form supports various field types out of the box:

Field TypeComponentDescription
textTextInputStandard text input
textareaTextareaInputMulti-line text input
numberNumberInputNumeric input
emailEmailInputEmail input
selectSelectInputDropdown selection
radioRadioInputRadio button group
checkboxCheckboxInputCheckbox or checkbox group
phonePhoneInputPhone number input
dateDateInputDate picker
readonlyReadonlyInputDisplay-only field

Example of different field types:

const formConfig = {
  form: {
    id: 'multiFieldForm',
    submitText: 'Submit',
    onSubmit: (data) => console.log(data),
  },
  fields: [
    {
      name: 'fullName',
      label: 'Full Name',
      type: 'text',
    },
    {
      name: 'bio',
      label: 'Biography',
      type: 'textarea',
    },
    {
      name: 'age',
      label: 'Age',
      type: 'number',
    },
    {
      name: 'gender',
      label: 'Gender',
      type: 'select',
      options: [
        { label: 'Male', value: 'male' },
        { label: 'Female', value: 'female' },
        { label: 'Other', value: 'other' },
      ],
    },
    {
      name: 'newsletter',
      label: 'Subscribe to newsletter',
      type: 'checkbox',
    },
  ],
};

Conditional Rendering

You can conditionally render fields based on the values of other fields:

const formConfig = {
  // ... other config
  fields: [
    {
      name: 'hasChildren',
      label: 'Do you have children?',
      type: 'radio',
      options: [
        { label: 'Yes', value: 'yes' },
        { label: 'No', value: 'no' },
      ],
    },
    {
      name: 'numberOfChildren',
      label: 'Number of children',
      type: 'number',
      dependency: {
        on: ['hasChildren'],
        condition: (values) => values.hasChildren === 'yes',
      },
    },
  ],
};

Validation

tac-form integrates with Zod for schema validation:

import { z } from 'zod';

const schema = z.object({
  email: z.string().email('Invalid email address'),
  password: z.string().min(8, 'Password must be at least 8 characters'),
});

const FormWithValidation = () => {
  const formConfig = {
    // ... form config
  };

  return <DynamicForm id="validatedForm" config={formConfig} schema={schema} />;
};

Note: schema is optional. If not provided, the form will not be validated. but you have to pass the schema for all the fields. and if field is atleast add z.string().min(1) to it as, field uses '' as default value .

External Component Integration

You can integrate external UI components:

  • External library component or Custom component are also supported having name, value and onChange as required props.
import { TextField } from '@mui/material';

const formConfig = {
  // ... other config
  fields: [
    {
      name: 'customInput',
      label: 'Custom Input',
      type: 'text',
      component: TextField,
    },
  ],
};

Context and Form Management

The FormProvider allows you to manage multiple forms:

import { useFormContext } from 'tac-form';


const ParentComponent = () => {
  const handleClick = (id: string) => {
  const value = getFormValue(id, 'textField')
    console.log({ value })
  }
  return (
      <DynamicForm id="myForm" config={formConfig} />
  );
};

Theming and Styling

tac-form supports dark mode and custom styling:

const formConfig = {
  // ... other config
  fields: [
    {
      name: 'styledInput',
      label: 'Styled Input',
      type: 'text',
      customClassName: {
        container: 'my-custom-container',
        input: 'my-custom-input',
        label: 'my-custom-label',
      },
      styles: {
        input: { borderColor: 'blue' },
      },
    },
  ],
};

const ThemedForm = () => {
  return <DynamicForm id="themedForm" config={formConfig} darkMode={true} />;
};

Advanced Examples

Typescript Form

type testFormConfig = {
	textField: string,
	readOnly: string,
	textareaField: string,
	numberField: number,
	selectField: string,
	radioField: string,
	checkboxField: string[],
	phoneField: string,
	dateField: string,
}
const testFormConfig: FormConfig<testFormConfig> = {
	form: {
		id: 'test',
		submitText: 'submit',
		onSubmit: (data) => {
			console.log(data)
		}
	},
	fields: [
	{
		name: 'textField',
		label: 'Text Field',
		type: 'text',
		placeholder: 'Enter text',
	},
	{
		name: 'readOnly',
		label: 'read only',
		type: 'readonly',
		placeholder: 'Enter text',
		value: 'Sample text',
	},
	{
		name: 'textareaField',
		label: 'Textarea Field',
		type: 'textarea',
		placeholder: 'Enter detailed text',
		value: 'Sample textarea content',
	},
	{
		name: 'numberField',
		label: 'Number Field',
		type: 'number',
		placeholder: 'Enter a number',
		value: 12345,
	},
	{
		name: 'selectField',
		label: 'Select Field',
		type: 'select',
		options: [
			{ label: 'Option 1', value: 'option1' },
			{ label: 'Option 2', value: 'option2' },
			{ label: 'Option 3', value: 'option3' }
		],
		placeholder: 'slec',
		value: 'option2',
	},
	{
		name: 'radioField',
		label: 'Radio Field',
		type: 'radio',
		options: [
			{ label: 'Radio 1', value: 'radio1' },
			{ label: 'Radio 2', value: 'radio2' },
			{ label: 'Radio 3', value: 'radio3' }
		],
		value: 'radio1',
	},
	{
		name: 'checkboxField',
		label: 'Checkbox Field',
		type: 'checkbox',
		options: [
			{ label: 'Checkbox 1', value: 'checkbox1' },
			{ label: 'Checkbox 2', value: 'checkbox2' },
			{ label: 'Checkbox 3', value: 'checkbox3' }
		],
		value: ['checkbox3', 'checkbox1'],
	},
	{
		name: 'phoneField',
		label: 'Phone Number Field',
		type: 'phone',
		placeholder: 'Enter phone number',
		value: '918340453292',
	},
	{
		name: 'dateField',
		label: 'Date Field',
		type: 'date',
		placeholder: 'Select date',
		value: '2023-08-27',
	},
	]
}

const testFormSchema = z.object({
	textField: z.string().min(8, { message: 'Minimum 8 characters required' }),
	readOnly: z.string(),
	textareaField: z.string(),
	numberField: z.number(),
	selectField: z.string(),
	radioField: z.string(),
	checkboxField: z.array(z.string()),
	phoneField: z.string(),
	dateField: z.string().or(z.array(z.date())),
})


const Home = () => {
	const { forms, getFormValue, addForm } = useFormContext<testFormConfig>()

	const handleClick = (id: string) => {
		const value = getFormValue(id, 'textField')
		console.log({ value })
	}
	return (
		<>
		<button onClick={() => handleClick('2')}>	click me	</button>
		<div className='w-full dark h-[100vh] flex justify-center items-center' >
			<div className='w-[80%] h-auto bg-white dark:bg-slate-600 rounded-lg px-10 py-4'>
				<DynamicForm
					id='2'
				config={testFormConfig}
				schema={testFormSchema}
				/>
			</div>
		</div>
		</>
	)
}

export default Home

Multi-step Form

import React, { useState } from 'react';
import { DynamicForm, FormProvider } from 'tac-form';

const MultiStepForm = () => {
  const [step, setStep] = useState(1);

  const stepOneConfig = {
    form: {
      id: 'stepOne',
      submitText: 'Next',
      onSubmit: (data) => {
        console.log('Step One Data:', data);
        setStep(2);
      },
    },
    fields: [
      { name: 'firstName', label: 'First Name', type: 'text' },
      { name: 'lastName', label: 'Last Name', type: 'text' },
    ],
  };

  const stepTwoConfig = {
    form: {
      id: 'stepTwo',
      submitText: 'Submit',
      onSubmit: (data) => {
        console.log('Step Two Data:', data);
        // Handle final submission
      },
    },
    fields: [
      { name: 'email', label: 'Email', type: 'email' },
      { name: 'phone', label: 'Phone', type: 'phone' },
    ],
  };

  return (
    <FormProvider>
      {step === 1 && <DynamicForm id="stepOne" config={stepOneConfig} />}
      {step === 2 && <DynamicForm id="stepTwo" config={stepTwoConfig} />}
    </FormProvider>
  );
};

export default MultiStepForm;

Form with Complex Validation

import { z } from 'zod';
import { DynamicForm } from 'tac-form';

const complexSchema = z.object({
  username: z.string().min(3, 'Username must be at least 3 characters'),
  email: z.string().email('Invalid email address'),
  password: z.string().min(8, 'Password must be at least 8 characters'),
  confirmPassword: z.string(),
}).refine((data) => data.password === data.confirmPassword, {
  message: "Passwords don't match",
  path: ['confirmPassword'],
});

const ComplexForm = () => {
  const formConfig = {
    form: {
      id: 'complexForm',
      submitText: 'Register',
      onSubmit: (data) => console.log('Form submitted:', data),
    },
    fields: [
      { name: 'username', label: 'Username', type: 'text' },
      { name: 'email', label: 'Email', type: 'email' },
      { name: 'password', label: 'Password', type: 'password' },
      { name: 'confirmPassword', label: 'Confirm Password', type: 'password' },
    ],
  };

  return <DynamicForm id="complexForm" config={formConfig} schema={complexSchema} />;
};

export default ComplexForm;

Future Work

  • Otp Form
  • Native Multi Step Form
  • File Upload Form
  • Image Crop Form
  • Signature Form

License

tac-form is released under the MIT License.

1.0.19

8 months ago

1.0.18

8 months ago

1.0.17

8 months ago

1.0.16

8 months ago

1.0.9

8 months ago

1.0.8

8 months ago

1.0.6

8 months ago

1.0.5

8 months ago

1.0.4

8 months ago

1.0.3

8 months ago

1.0.22

8 months ago

1.0.21

8 months ago

1.0.20

8 months ago

1.0.26

8 months ago

1.0.25

8 months ago

1.0.24

8 months ago

1.0.23

8 months ago

1.0.11

8 months ago

1.0.10

8 months ago

1.0.15

8 months ago

1.0.14

8 months ago

1.0.13

8 months ago

1.0.12

8 months ago

1.0.2

8 months ago

1.0.1

8 months ago

1.0.0

8 months ago