field-day

0.3.1 • Public • Published

🤸 field-day

No one wants to spend hours thinking about how to align fields in a form, so I did it for you.

Visual demo of library

Table of Contents

Introduction

Forms can be a pain to align correctly. It's easy enough to make a grid of fields, but what happens when you add labels? When those labels overflow to multiple lines? When you want some fields to span multiple columns?

field-day provides a config-free sane field grid experience, plus all the power tools you need to implement your own specialized field structures.

It looks like this:

Simple demo

import Field from 'field-day';

<Field.Group>
  <Field label="Hello">
    <Input />
  </Field>
  <Field label="World">
    <Input />
  </Field>
</Field.Group>;

Or like this:

Simple demo

<Field.Group columns={4}>
  <Field label="medium" columnSpan={2}>
    <Input />
  </Field>
  <Field label="small">
    <Input />
  </Field>
  <Field label="small">
    <Input />
  </Field>
  <Field label="wide" columnSpan={3}>
    <Input />
  </Field>
  <Field label="medium" columnSpan={2}>
    <Input />
  </Field>
  <Field label="small">
    <Input />
  </Field>
</Field.Group>

... and that's only scratching the surface of the options available.

What's in the box

<Field>

Props

Prop Name Type Description
columnSpan number Controls the number of columns this field will take up in the layout. If two adjacent fields are too wide to fit together on a row, the second one will wrap.
children `string Node`
label `string Node`
fieldId string In a default Field, you can pass an explicit id string to indicate the id applied to your input, and the label will use it as a for attribute.
align string In a default Field, this prop will align the content of your field within the available space. Can be start, end, stretch, center; defaults to stretch.

Static properties

Property Description
Group A convenience reference for the FieldGroup component which MUST wrap all fields as a direct parent.

By default, Field renders a label and content pair. The label is placed above the content. They are placed 5px from each other vertically. However, all of these details are customizable.

As a bonus, in addition to the fieldId behavior mentioned above, the default Field label will take a look at the childen you pass into the Field itself and see if it's a node with an id prop. If it is, the label will automatically use that as its for attribute.

<Field.Group>

Props

Prop Name Type Description
columns number The number of columns in the grid. Defaults 2
children Array<Field> Children of a FieldGroup must be Fields, and all Field components must be rendered directly inside a FieldGroup*
fieldSpacing CSSValue A string CSS value which determines the amount of space between fields in a row.
style {} Override style properties on the Group element
id string Supplies an id to the Group element
className string Supplies a class name to the Group element
fieldElements Array<FieldElementConfig> Advanced: Customize the behavior of the elements within a field by supplying customized configs for each field element. This allows changing element heights and vertical alignment of elements. Specifying fieldElements is required if you customize the rendering of Fields inside this Group.

* You could probably actually render a Field nested within a Group, but only if none of the components between the Group and Field actually render DOM nodes. Field layout is reliant on the Field content being a direct child of the Group DOM element.

Static Properties

Property Description
defaultFieldElements Advanced: The default configurations which Field uses to render its contents. Useful for extending if you customize the way Field contents align, or what constitutes a Field.

Customizing Behavior

This library allows you to specify advanced custom behaviors, building on top of the layout framework it provides.

Here are some things you can do:

  • Reorder label and content
  • Remove elements (label, content) or add your own custom elements
  • Change how elements align vertically
  • Change the spacing between elements

To do any of these things, you'll need to override a Field.Group's fieldElements prop.

fieldElements is an array of FieldElementConfig objects, which look like this:

{
  height: 'auto',
  verticalAlign: 'center',
  horizontalAlign: 'start',
  render: ({ fieldProps, style }) =>
    fieldProps.children && <div style={style}>{fieldProps.children}</div>,
}

What you see above is actually the default FieldElementConfig for the content of a field.

The height of an element determines the characteristics of its row in the grid. auto just means a row will grow or shrink to fit the horizontal siblings of that element (so, if there was a 100px tall input in this row of fields, all of the content areas would be 100px tall). You can also enforce a static height (then, even if there are no elements at all, the space will still be there). I recommend using the CSS minmax function, which will establish a sane minimum while allowing contents to grow dynamically if you do something like minmax(20px, auto).

The verticalAlign of an element determines how it 'sticks' vetically to its fellow field elements. This makes a big difference as adjacent elements vary in size. For instance, if the label in the next column is 100px high, and you want your label to 'stick' to the bottom of the created area so that it sits just above the input, you'd want to use verticalAlign: 'end' (this is the default behavior for labels, by the way). center, stretch, start are all also options (or, technically, anything you could supply to the align-self CSS property).

Finally, the render function. This does all the work of actually rendering the element based on the available information in the Field. It's passed a single object, { gridArea, fieldProps, config }. You must apply gridArea to the CSS styles of the top-level DOM element of your field element. After that, you're free to do whatever you like utilizing fieldProps, which are all the props passed to your field. That means you can pass custom props to your field and use them when rendering your custom field elements, letting you craft your own component API. You also get config, which is the original FieldElementConfig you defined for this field element. You can use that to apply default values that you may have defined in the config.

As an example, let's add a "Help Text" element to our fields. The help text should sit below the input, 'sticking' to the bottom of it. It shouldn't be more than 20px high at most. And we want to render it using italics, getting its content from a helpText prop we supply to our Field.

const customFieldElements = [
  // we spread in the default elements; we still want those
  ...Field.Group.defaultFieldElements,
  // our 'helptext' custom element
  {
    height: 'minmax(auto, 20px)',
    verticalAlign: 'top',
    render: ({ gridArea, fieldProps }) =>
      fieldProps.helpText && <i style={{ gridArea }}>{fieldProps.helpText}</i>,
  },
];

const rendered = (
  <Field.Group fieldElements={customFieldElements}>
    <Field label="Label" helpText="An explanation of the field">
      <Input />
    </Field>
    <Field label="Label 2">
      <Input />
    </Field>
  </Field.Group>
);

That wasn't so hard! We can now easily pass through a helpText prop to our fields and get a nicely rendered help text element:

HelpText demo

Now, what if we want all our field groups to behave this way? Well, we can create our own copy of Field and FieldGroup in our project using recompose's withProps.

import Field from 'field-day';
import { withProps } from 'recompose';

Field.Group = withProps({
  fieldElements: customFieldElements,
})(Field.Group);

export default Field;

Nice!

How does it work?

field-day uses CSS grid to generate its field layout grid, and then manages keeping track of columns, column spans, and assigning the correct CSS properties to children for you.

Inspect the generated DOM to see the gist of how things get aligned.

field-day has no dependencies, so it's pretty lightweight. And it utilizes native CSS, not costly Javascript alignment, so it's quick, too!

Readme

Keywords

none

Package Sidebar

Install

npm i field-day

Weekly Downloads

0

Version

0.3.1

License

MIT

Unpacked Size

1.96 MB

Total Files

17

Last publish

Collaborators

  • a-type