Skip to content

pureliani/formix

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

84 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@gapu/formix

@gapu/formix is a powerful form management library for SolidJS. It provides a simple and flexible API for handling complex form state, validation, and submission.

Installation

To use @gapu/formix in your project, you need to install both @gapu/formix and Zod for schema validation.

# Using npm
npm install @gapu/formix zod

# Using yarn
yarn add @gapu/formix zod

# Using pnpm
pnpm add @gapu/formix zod

# Using bun
bun add @gapu/formix zod

Now you're ready to start using @gapu/formix in your SolidJS project!

Table of contents

Form initialization

The createForm function is the entry point for creating and managing your form with the specified schema, initial state, and submission handler.

import { createForm } from '@gapu/formix';
import { z } from 'zod';

const formContext = createForm({
  schema: z.object({
    username: z.string().min(3),
    email: z.string().email(),
    age: z.number().min(18)
  }),
  initialState: {
    username: '',
    email: '',
    age: 18,
  },
  onSubmit: async (state) => {
    console.log('Form submitted:', state);
  },
});

Parameters

createForm accepts an object with the following properties:

  • schema: A Zod schema that defines the structure and validation rules for your form data.
  • initialState: The initial state of your form which matches your zod schema.
  • onSubmit: A function that will be called when the form is submitted successfully. It doesn't run if the form is invalid and it receives the validated form state as its argument.
  • undoLimit (optional, default = 500): The maximum number of undo steps to keep in history.

Return Value:

createForm returns a form context object with various methods and properties for managing your form:

  • initialState: The initial state of the form.
  • formSchema: Zod schema used for the form validation.
  • isFieldRequired: A function to check if a field is required, given its path and optional variant.
  • state: A signal containing the current form state.
  • setState: A function to update the form state (use empty path: "" to update entire form state).
  • isValidating: A function to check if the form is currently being validated by zod.
  • isSubmitting: A function to check if the form is currently being submitted.
  • fieldMetas: A signal containing metadata for all fields.
  • setFieldMetas: A function to update metadata for all fields.
  • errors: A signal containing any current validation errors.
  • reset: A function to reset the form to its initial state.
  • submit: A function to trigger form submission.
  • undo: A function to undo the last change in the form state.
  • redo: A function to redo the last undone change in the form state.
  • canUndo: A function that returns whether an undo operation is possible.
  • canRedo: A function that returns whether a redo operation is possible.
  • wasModified: A function that returns whether the form state has been modified from its initial state.
  • setFieldMeta: A function to update metadata for a specific field.

Example usage

const formContext = createForm({
  schema: ...,
  initialState: ...,
  onSubmit: ...,
  undoLimit: ... // (optional)
});

// Access current form state
const currentState = formContext.state();

// Update form state
formContext.setState("", newState);

// Check if form is currently submitting
const isSubmitting = formContext.isSubmitting();

// Get all current form errors
const formErrors = formContext.errors();

// Undo last change
if (formContext.canUndo()) {
  formContext.undo();
}

// Check if a specific field is required
const isNameRequired = formContext.isFieldRequired('name');

Form Component

The Form component is a crucial part of this library. Its primary purpose is to serve as a context provider, making the form context available to all descendant components.

Usage

import { createForm, Form } from '@gapu/formix';

const formContext = createForm({ ... })

<Form context={formContext}>
  {/* Your form fields and components go here */}
</Form>

Parameters

The Form component accepts two props:

  • context: This is the form context object returned by the createForm function. It contains all the form state, methods, and properties needed to manage your form.
  • children: This can be any valid JSX content. Typically, this will include your form fields, submit buttons, and any other UI elements that make up your form.

How it works

The Form component uses Solid's Context API to provide the form context to all its children. It wraps its children in a <form> element, which handles the submit event. When the form is submitted, it prevents the default form submission behavior and if the form is valid, calls the submit function from the provided context.

Example

import { createForm, Form, useField } from '@gapu/formix';
import { z } from 'zod';

const MyForm = () => {
  const formContext = createForm({
    schema: z.object({
      name: z.string().min(2),
      email: z.string().email(),
    }),
    initialState: {
      name: '',
      email: '',
    },
    onSubmit: async (state) => {
      console.log('Form submitted:', state);
    },
  });

  return (
    <Form context={formContext}>
      <NameField />
      <EmailField />
      <button type="submit">Submit</button>
    </Form>
  );
};

const NameField = () => {
  const field = useField<string>('name');
  
  ...
};

const EmailField = () => {
  const field = useField<string>('email');

  ...
};

Key Points

  • The Form component is essentially a context provider. It doesn't directly handle form state or validation itself.
  • All components and hooks from @gapu/formix (such as useField) must be used within a Form component to access the form context.
  • The Form component renders a regular <form> element and handles the onSubmit event internally.
  • Child components can access the form context using hooks like useForm, useField or useArrayField.

By using the Form component, you ensure that all parts of your form have access to the shared form context, allowing for seamless integration of form state, validation, and submission handling throughout your form's component tree.

useForm Hook

The useForm hook provides access to the entire form context within any component that is a child of a Form component.

Purpose

The useForm hook allows you to:

  • Access and modify the entire form state
  • Handle form-wide operations like submission, reset, and validation
  • Perform undo and redo operations on the form state

Usage

import { useForm } from '@gapu/formix';

const MyFormComponent = () => {
    const form = useForm<MyFormState>();
    
    ...
};

Return Value

useForm returns the form context object with the following properties and methods:

  • initialState: The initial state of the form.
  • formSchema: Zod schema used for the form validation.
  • isFieldRequired: A function to check if a field is required, given its path and optional variant.
  • state: A signal containing the current form state.
  • setState: A function to update the form state (use empty path: "" to update entire form state).
  • isValidating: A function to check if the form is currently being validated by zod.
  • isSubmitting: A function to check if the form is currently being submitted.
  • fieldMetas: A signal containing metadata for all fields.
  • setFieldMetas: A function to update metadata for all fields.
  • errors: A signal containing any current validation errors.
  • reset: A function to reset the form to its initial state.
  • submit: A function to trigger form submission.
  • undo: A function to undo the last change in the form state.
  • redo: A function to redo the last undone change in the form state.
  • canUndo: A function that returns whether an undo operation is possible.
  • canRedo: A function that returns whether a redo operation is possible.
  • wasModified: A function that returns whether the form state has been modified from its initial state.
  • setFieldMeta: A function to update metadata for a specific field.

Example

import { useForm } from '@gapu/formix';

const FormSummary = () => {
  const form = useForm();
  
  return (
    <div>
      <h3>Form Summary</h3>
      <p>Modified: {form.wasModified() ? 'Yes' : 'No'}</p>
      <p>Can Undo: {form.canUndo() ? 'Yes' : 'No'}</p>
      <p>Can Redo: {form.canRedo() ? 'Yes' : 'No'}</p>
      <button onClick={form.reset} disabled={!form.wasModified()}>
        Reset Form
      </button>
      <button onClick={form.undo} disabled={!form.canUndo()}>
        Undo
      </button>
      <button onClick={form.redo} disabled={!form.canRedo()}>
        Redo
      </button>
      <button onClick={form.submit}>Submit</button>
    </div>
  );
};

Key Points

  • useForm must be used within a component that is a child of a Form component.
  • It provides access to the entire form context, allowing for form-wide operations and state management.
  • The hook is generic, allowing you to specify the type of the form state for better type safety.
  • It's particularly useful for creating components that need to interact with the overall form state or perform form-wide actions.

By using the useForm hook, you can create components that have full access to the form's state and functionality, enabling you to build complex form interactions and custom form controls.

useField Hook

The useField hook provides a way to interact with individual form fields within a Form context.

Purpose

The useField hook allows you to:

  • Access and modify the value of a specific field in your form
  • Handle field-specific metadata (like touched, dirty, disabled states)
  • Access field-specific validation errors
  • Perform field-specific actions like resetting or checking if the field was modified

Usage

import { useField } from '@gapu/formix';

const MyFormField = () => {
    const field = useField<string>('fieldName');
    
    ...
};

Parameters

The useField hook takes one parameter:

  • path: A string representing the path to the field in your form state. For nested objects and arrays, use dot notation (e.g. 'user.name', 'contacts.1')

Return Value

useField returns an object with the following properties and methods:

  • value: A function that returns the current value of the field.
  • setValue: A function to update the value of the field.
  • meta: A function that returns the current metadata state of the field. The metadata includes:
    • touched: A boolean indicating whether the field has been interacted with.
    • dirty: A boolean indicating whether the field's value has changed from its initial value.
    • loading: A boolean indicating whether the field is in a loading state.
    • disabled: A boolean indicating whether the field is currently disabled.
    • readOnly: A boolean indicating whether the field is in a read-only state.
    • show: A boolean indicating whether the field should be displayed.
  • setMeta: A function to update the metadata state of the field.
  • errors: A function that returns an array of current validation errors for the field.
  • reset: A function to reset the field to its initial value.
  • wasModified: A function that returns whether the field has been modified from its initial value.
  • isRequired: A function that returns whether the field is required based on the form schema.

Example

import { useField } from '@gapu/formix';
import { Index } from 'solid-js';

const EmailField = () => {
  const field = useField<string>('email');
  
  return (
    <div>
      <label>Email:</label>
      <input
        value={field.value()}
        onInput={(e) => field.setValue(e.currentTarget.value)}
        onFocus={() => field.setMeta(prev => ({ ...prev, touched: true }))}
        disabled={field.meta().disabled}
      />
      <Index each={hobbies.errors()}>
        {(error) => (
          <p class="error">{error().message}</p>
        )}
      </Index>
      {field.wasModified() && <span>Field was modified</span>}
      <button onClick={() => field.reset()}>Reset</button>
    </div>
  );
};

Key Points

  • useField must be used within a component that is a child of a Form component.
  • This hook provides a comprehensive API for interacting with a single form field.
  • It handles both the value of the field and its metadata (like disabled state).
  • It provides access to field-specific validation errors.
  • The hook is generic, allowing you to specify the type of the field value for better type safety.
  • The meta function provides access to various metadata about the field's state, such as whether it has been touched, is dirty, or is disabled.

By using the useField hook, you can create reusable, type-safe form field components that are automatically connected to your form's state and validation logic.

useArrayField Hook

The useArrayField hook is designed to handle array fields in your form. It provides an extended useField API for manipulating array-type form fields.

Purpose

  • Access and modify an array field in your form
  • Perform array-specific operations like pushing, removing, moving, and swapping items
  • Handle field-specific metadata and validation errors for the entire array
  • Perform field-specific actions like resetting or checking if the array was modified

Usage

import { useArrayField } from '@gapu/formix';

type ItemType = { ... }

const MyArrayField = () => {
    const arrayField = useArrayField<ItemType>('arrayFieldName');

    ...
};

Parameters

The useArrayField hook takes one parameter:

  • path: A string representing the path to the array field in your form state. For nested objects, use dot notation (e.g., 'user.hobbies').

Return Value

useArrayField returns an object that includes all properties and methods from useField, plus these additional array-specific methods:

  • push: A function to add an item to the end of the array.
  • remove: A function to remove an item at a specific index.
  • move: A function to move an item from one index to another.
  • insert: A function to insert an item at a specific index.
  • replace: A function to replace an item at a specific index.
  • empty: A function to remove all items from the array.
  • swap: A function to swap the positions of two items in the array.

Example

import { useArrayField } from '@gapu/formix';
import { Index } from 'solid-js';

const HobbiesField = () => {
  const hobbies = useArrayField<string>('hobbies');

  return (
    <div>
      <h3>Hobbies</h3>
      <Index each={hobbies.value()}>
        {(hobby, index) => (
          <div>
            <input
              value={hobby()}
              onInput={(e) => hobbies.replace(index, e.currentTarget.value)}
            />
            <button onClick={() => hobbies.remove(index)}>Remove</button>
          </div>
        )}
      </Index>
      <button onClick={() => hobbies.push('')}>Add Hobby</button>
      <Index each={hobbies.errors()}>
        {(error) => (
          <p class="error">{error().message}</p>
        )}
      </Index>
    </div>
  );
};

Key Points

  • useArrayField must be used within a component that is a child of a Form component.
  • It provides all the functionality of useField, plus additional methods for array manipulation.
  • The hook is generic, allowing you to specify the type of the array items for better type safety.
  • Array operations (push, remove, move, etc.) automatically trigger form state updates and validation.
  • You can use the base field methods (setValue, setMeta, etc.) to manipulate the entire array at once.

Advanced Usage

useArrayField allows for complex array manipulations:

const hobbies = useArrayField<string>('hobbies');

// Move the first hobby to the end
hobbies.move(0, hobbies.value().length - 1);
// Swap the first two hobbies
hobbies.swap(0, 1);
// Insert a new hobby at the beginning
hobbies.insert(0, 'New Hobby');
// Replace all hobbies
hobbies.setValue(['Hobby1', 'Hobby2', 'Hobby3']);

By using the useArrayField hook, you can easily create dynamic form sections that allow users to add, remove, and reorder items in an efficient manner.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

No packages published