npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

formland

v1.0.1

Published

A simple, super-flexible, extensible config based form generator for React.

Downloads

3

Readme

Formland

Build Status npm version

A simple, super-flexible, extensible config based form generator for React.

Features

  • Super-flexible
  • Config based form element creation
  • Take super control over all the form elements
  • Use third party components as form element

Install

  npm i formland

  # or

  yarn add formland

Usage

import 'formland/css/index.css'
import React, { Component } from 'react'
import Form from 'formland'

class Example extends Component {
  constructor() {
    super()
    this.state = {}
    this.onChange = this.onChange.bind(this)
  }

  onChange(newState) {
    this.setState(newState)
  }

  render() {
    const config = [
      {
        id: 'firstName',
        type: 'text',
        displayName: 'First Name',
        resultPath: 'name.firstName',
        placeholder: 'Enter your first name',
      },
    ]

    return <Form config={config} store={this.state} onChange={this.onChange} />
  }
}

Props

| Property | Type | Default | Description | | ------------------------ | ---------------------------- | --------- | ------------------------------------------------------------------- | | config | array | [] | Config to populate form elements. | | store | object | array | {} | The data store to read the values from. | | onChange | function | undefined | Callback for onChange event | | onBlur | function | undefined | Callback for onBlur event | | onFocus | function | undefined | Callback fro onFocus event | | customComponentResolvers | array | undefined | Array of resolvers to resolve custom form elements | | customValueResolvers | array | undefined | Array of resolvers to resolve the value of the custom form elements | | useNativeEvent | boolean | false | Whether to return the new state or native JS event object on events | | primaryButton | string | false | undefined | 'Submit' | Title for the primary button. false to remove button | | secondaryButton | string | false | undefined | 'Cancel' | Title for the secondary button. false to remove button | | onSecondaryButtonClick | function | ()=> {} | onClick event handler for secondary button |

Configuration File Properties

| Property | Type | Default | Description | | ----------------------- | -------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | id | string | undefined | ID for each form field. This should be unique. | | type | string | undefined | Type of the form field to be generated. The native package supports text, color, date, email, month, number, tel, time, url, week, toggle, radio, checkbox, range, textarea, dropdown types. New types can be added via Custom Form Elements. | | resultPath | string | undefined | A dot-prop-immutable compatible path to store the form field value. | | displayName | string | undefined | Display text for the form field. | | className | string | undefined | ClassName for the root form field element. | | isHidden | function | undefined | Method to hide form field element in runtime. (store) => boolean | | optional | boolean | undefined | Says if the form field is optional. | | placeholder | string | undefined | Placeholder text for the form elements | | helpText | string | undefined | Help text for a form element | | helpTextOptions | object | undefined | See rc-tooltip props | | options | array | [] | Array of options. ({ value: any, label: any })[] | | separator | string | ',' | Separator to join/separate multiple values. Used by checkbox, multi-dropdown, etc. | | topComponent | function | undefined | Method to render any additional elements to the top of the form field element. (store) => JSX.Element | | simpleValues | boolean | false | Handle multi values as string. Works along with separator. | | bottomComponent | function | undefined | Method to render any additional elements to the bottom of the form field element. (store) => JSX.Element | | componentProps | object | undefined | Any additional props that should be passed to the underlying form field element. For example, to make the dropdown multi-select, pass componentProps: { multiple: true }. | | instantValidation | boolean | false | Do validation on user input. | | required | boolean | false | Make the field required. | | validation | function | undefined | Method to perform validation. (value) => errorMessage|null | | modifyStoreBeforeChange | function | undefined | Method to modify store before onChange handler is called. (config, value, store) => store. |

Available componentProps

type: toggle

| Property | Type | Default | Description | | -------- | ------ | --------- | ----------------------------------------------------------- | | infoText | string | undefined | Info text to be displayed to the right of the toggle switch |

type: range

| Property | Type | Default | Description | | --------- | ------- | ------- | --------------------------------------------- | | showRange | boolean | true | Shows the min and max value of the slider | | showValue | boolean | true | Shows the current range value explicitly |

Form Groups

It is possible to group form elements. Take a look at the following example.

import 'formland/css/index.css'
import React, { Component } from 'react'
import Form from 'formland'

class Example extends Component {
  constructor() {
    super()
    this.state = {}
    this.onChange = this.onChange.bind(this)
  }

  onChange(newState) {
    this.setState(newState)
  }

  render() {
    const config = [
      {
        id: 'group-1',
        type: 'group',
        displayName: 'Group 1',
        description: 'This is group 1',
        elements: [
          {
            id: 'firstName',
            type: 'text',
            displayName: 'First Name',
            resultPath: 'name.firstName',
            placeholder: 'Enter your first name',
          },
          {
            id: 'LastName',
            type: 'text',
            displayName: 'Last Name',
            resultPath: 'name.LastName',
            placeholder: 'Enter your last name',
          },
        ],
      },
      {
        id: 'group-2',
        type: 'group',
        displayName: 'Group 2',
        description: 'This is group 1',
        elements: [
          {
            id: 'country',
            type: 'text',
            displayName: 'First Name',
            resultPath: 'name.country',
            placeholder: 'Enter your country',
          },
          {
            id: 'state',
            type: 'text',
            displayName: 'Last Name',
            resultPath: 'name.state',
            placeholder: 'Enter your state',
          },
        ],
      },
    ]

    return <Form config={config} store={this.state} onChange={this.onChange} />
  }
}

Validation

Validations are done per form element. Add a validation() method in the form element config. Validations can be done in two ways.

  • Set instantValidation: true in the form element config. Now, the validation method will be called each time on user input.

  • Use validate() method of the form instance. This returns an array of errors, if any. Useful for conditional based approach.

import 'formland/css/index.css'
import React, { Component } from 'react'
import Form from 'formland'

class Example extends Component {
  constructor() {
    super()
    this.state = {}
    this.onChange = this.onChange.bind(this)
  }

  onChange(newState) {
    this.setState(newState)
  }

  onSubmit() {
    const errors = this.form.validate()
    console.log(errors)
  }

  render() {
    const config = [
      {
        id: 'answer',
        type: 'text',
        displayName: 'What is 4 + 5',
        resultPath: 'answer',
        placeholder: 'Answer',
        validation: value => value !== '9' && 'Wrong Answer',
      },
    ]

    return (
      <Form
        ref={el => (this.form = el)}
        config={config}
        store={this.state}
        onChange={this.onChange}
      />
    )
  }
}

Modify store value directly from config

You can access the store and change any store value directly from the config using modifyStoreBeforeChange method. This method will be called before onChange event. You can return a modified state and it will be passed to the onChange handler. The primary use case for this will be, to change a form value based on another form value.

import 'formland/css/index.css'
import React, { Component } from 'react'
import Form from 'formland'

class Example extends Component {
  constructor() {
    super()
    this.state = {}
    this.onChange = this.onChange.bind(this)
  }

  onChange(newState) {
    this.setState(newState)
  }

  render() {
    const config = [
      {
        id: 'firstName',
        type: 'text',
        displayName: 'First Name',
        resultPath: 'name.firstName',
        placeholder: 'Enter your first name',
        modifyStoreBeforeChange: (config, value, store) => {
          store.readonly = value
          return store
        },
      },
      {
        id: 'readyonly',
        type: 'text',
        displayName: 'Output',
        resultPath: 'readonly',
        componentProps: {
          disabled: true,
        },
      },
    ]

    return <Form config={config} store={this.state} onChange={this.onChange} />
  }
}

Custom Buttons

It is possible to add custom buttons. Just wrap your buttons with <Form>.

<Form
  ref={el => (this.form = el)}
  config={config}
  store={this.state}
  onChange={this.onChange}
>
  <button type="submit">Custom Button</button>
</Form>

Custom Form Elements

Custom form elements can be added to any instance of formland via customComponentResolvers and customValueResolvers props.

  • customComponentResolvers(type: string) => ({ config: IReactFormConfig, value: any, callbacks: any }) => JSX.Element

Returns a valid React stateless function based on the custom type.

  • customValueResolvers(config: IReactFormConfig, value: any) => any

Returns a resolved value based on the custom type.

See the following example to understand how it works.

import 'formland/css/index.css'
import React, { Component } from 'react'
import Select from 'react-select'
import Form, { getNewState } from 'formland'

const customComponentsResolver = (type) => {
  switch(type) {
    case 'custom-dropdown':
      return ({ config, value = '', callbacks= {}}) =>
        <Select
          value={value}
          options={config.options}
          { ...callbacks }
          { ...config.componentProps } />
    default:
      return <span/>
  }
}

const customValueResolver = (config: any, value: any) => {
  switch (config.type) {
    case 'react-select':
      return value ? value.value : ''
  }
}

class Example extends Component {
  constructor() {
    super()
    this.state = {}
    this.onChange = this.onChange.bind(this)
  }

  onChange(newState) {
    this.setState(newState)
  }

  render() {
    const config = [
      {
        id: 'firstName',
        type: 'custom-dropdown',
        resultPath: 'country'
        displayName: 'Shipping Country',
        placeholder: 'Select a country',
        options: [
          {
            value: 'india',
            label: 'India',
          },
          {
            value: 'usa',
            label: 'The United States',
          },
          {
            value: 'china',
            label: 'China',
          }
        ],
      },
    ]

    return <Form
      customComponentResolvers={[customComponentsResolver]}
      customValueResolvers={[customValueResolver]}
      config={config}
      store={this.state}
      onChange={this.onChange} />
  }
}

Custom Form Element Packages

Development

yarn install
yarn build
yarn start:examples

Refer package.json for more commands

License

MIT