rename auto to field

This commit is contained in:
cupcakearmy 2019-10-19 08:45:14 +02:00
parent 0cf1b6e73b
commit 5d6a420283
8 changed files with 249 additions and 256 deletions

164
README.md
View File

@ -22,9 +22,9 @@ npm i formhero
### 👁 Demos
- [__*Live Web*__](https://cupcakearmy.github.io/formhero/)
- [__*Live Codesandbox*__](https://codesandbox.io/embed/formhero-simple-bdcx2?expanddevtools=1&fontsize=14)
- [__*Live React-Native*__](https://snack.expo.io/@cupcakearmy/useform)
- [**_Live Web_**](https://cupcakearmy.github.io/formhero/)
- [**_Live Codesandbox_**](https://codesandbox.io/embed/formhero-simple-bdcx2?expanddevtools=1&fontsize=14)
- [**_Live React-Native_**](https://snack.expo.io/@cupcakearmy/useform)
### Links
@ -35,7 +35,7 @@ npm i formhero
- [Validators](#validators)
- [Options](#options)
- Returns
- [auto](#auto)
- [field](#field)
- [form](#form)
- [errors](#errors)
- [isValid](#isvalid)
@ -43,7 +43,7 @@ npm i formhero
## 🤔 Motivation
So why write yet another form utility you might ask? First off, I don't like the Formik approach. In my humble opition formik is very verbose and requires lots of boilerplate. Also does not work with hooks. [react-hook-form](https://react-hook-form.com/) is a very cool library and it is the main inspiration for formhero. It does almost everything right... typescript, no deps, small, concise.
So why write yet another form utility you might ask? First off, I don't like the Formik approach. In my humble opition formik is very verbose and requires lots of boilerplate. Also does not work with hooks. [react-hook-form](https://react-hook-form.com/) is a very cool library and it is the main inspiration for formhero. It does almost everything right... typescript, no deps, small, concise.
The problem that I found while using it was that 3rd party ui libs like [Ant Design](https://ant.design/) or [Fabric UI](https://developer.microsoft.com/en-us/fabric#/controls/web) do not always have the standart `onChange` or `value` props in their components. That is where react-hook-form starts falling apart. This is what formhero tries to address in the most minimalistic way possible, with as little code as needed. All in pure typescript and no deps.
@ -54,8 +54,7 @@ import ReactDOM from 'react-dom'
import { useForm } from 'formhero'
const Form = () => {
const { auto, form } = useForm({
const { field, form } = useForm({
username: '',
password: '',
})
@ -68,9 +67,8 @@ const Form = () => {
return (
<div>
<form onSubmit={_submit}>
<input {...auto('username')} />
<input {...auto('password')} />
<input {...field('username')} />
<input {...field('password')} />
<button type="submit">Go 🚀</button>
</form>
@ -85,43 +83,43 @@ const Form = () => {
```typescript
const Form = () => {
const { auto, form, errors } = useForm({
username: '',
email: '',
password: ''
}, {
username: value => value.length > 3,
email: {
validator: /@/,
message: 'Must contain an @',
const { field, form, errors } = useForm(
{
username: '',
email: '',
password: '',
},
password: [
{
validator: /[A-Z]/,
message: 'Must contain an uppercase letter'
{
username: value => value.length > 3,
email: {
validator: /@/,
message: 'Must contain an @',
},
{
validator: /[\d]/,
message: 'Must contain a digit'
},
]
})
password: [
{
validator: /[A-Z]/,
message: 'Must contain an uppercase letter',
},
{
validator: /[\d]/,
message: 'Must contain a digit',
},
],
}
)
return (
<form>
<h1>Errors & Validation</h1>
<input {...auto('username')} placeholder="Username" />
<input {...field('username')} placeholder="Username" />
{errors.username && 'Must be longer than 3'}
<input {...auto('email')} placeholder="EMail" />
<input {...field('email')} placeholder="EMail" />
{errors.email}
<input {...auto('password')} placeholder="Password" type="password" />
<input {...field('password')} placeholder="Password" type="password" />
{errors.password}
</form>
)
}
@ -133,30 +131,32 @@ Often it happens that you use a specific input or framework, so the default gett
```typescript
const Form = () => {
const { auto, form, errors } = useForm({
const { field, form, errors } = useForm({
awesome: true,
})
return (
<form onSubmit={e => {
e.preventDefault()
console.log(form)
}}>
<form
onSubmit={e => {
e.preventDefault()
console.log(form)
}}
>
<h1>Custom</h1>
<label>
<input type="checkbox" {...auto('awesome', {
setter: 'checked',
getter: 'onChange',
extractor: (e) => e.target.checked
})} />
<input
type="checkbox"
{...field('awesome', {
setter: 'checked',
getter: 'onChange',
extractor: e => e.target.checked,
})}
/>
Is it awesome?
</label>
</label>
<input type="submit" />
</form>
)
}
@ -167,7 +167,7 @@ const Form = () => {
### `useForm`
```typescript
const {auto, errors, update, form, isValid} = useForm(initial, validators, options)
const { field, errors, update, form, isValid } = useForm(initial, validators, options)
```
### Initial
@ -192,7 +192,7 @@ A validator is an object that taked in either a `RegExp` or a `Function` (can be
```javascript
const validators = {
// Only contains letters.
// Only contains letters.
// This could also be a (also async) function that returns a boolean.
username: /^[A-z]*$/,
}
@ -213,7 +213,7 @@ const validators = {
username: {
validator: /^[A-z]*$/,
message: 'My custom error message',
}
},
}
```
@ -227,12 +227,12 @@ const validators = {
message: 'My custom error message',
},
/[\d]/,
async (value) => value.length > 0,
async value => value.length > 0,
{
validator: (value) => true,
validator: value => true,
message: 'Some other error',
}
]
},
],
}
```
@ -245,33 +245,30 @@ Sometimes it's practical to have some different default values when using for ex
[Check the Expo Snack for a live preview](https://snack.expo.io/@cupcakearmy/useform)
```javascript
import * as React from 'react';
import { Text, SafeAreaView, TextInput } from 'react-native';
import { useForm } from 'formhero';
import * as React from 'react'
import { Text, SafeAreaView, TextInput } from 'react-native'
import { useForm } from 'formhero'
const initial = {
username: 'i am all lowercase',
};
const validators = {};
}
const validators = {}
const options = {
setter: 'value', // This is not stricly necessarry as 'value' would already be the default.
getter: 'onChangeText',
extractor: text => text.toLowerCase(),
};
}
export default () => {
const { form, auto } = useForm(initial, validators, options);
const { form, field } = useForm(initial, validators, options)
return (
<SafeAreaView>
<TextInput
style={{ height: 40, borderColor: 'gray', borderWidth: 2 }}
{...auto('username')}
/>
<TextInput style={{ height: 40, borderColor: 'gray', borderWidth: 2 }} {...field('username')} />
<Text>{form.username}</Text>
</SafeAreaView>
);
};
)
}
```
###### Example: React Native (Method 2 - Local overwrite)
@ -280,15 +277,15 @@ export default () => {
// ...
export default () => {
const { form, auto } = useForm({
const { form, field } = useForm({
username: 'i am all lowercase',
});
})
return (
<SafeAreaView>
<TextInput
style={{ height: 40, borderColor: 'gray', borderWidth: 2 }}
{...auto('username', {
{...field('username', {
setter: 'value', // This is not stricly necessarry as 'value' would already be the default.
getter: 'onChangeText',
extractor: text => text.toLowerCase(),
@ -296,44 +293,41 @@ export default () => {
/>
<Text>{form.username}</Text>
</SafeAreaView>
);
};
)
}
```
### Auto
The `auto` object is used to bind the form state to the input.
### field
The `field` object is used to bind the form state to the input.
###### Example: Simple
```javascript
const { auto } = useForm()
const { field } = useForm()
<input {...auto('username')} />
<input {...field('username')} />
```
###### Example: With custom options
All are optional.
```javascript
const { auto } = useForm()
const { field } = useForm()
<input {...auto('username', {
<input {...field('username', {
getter: 'onChage',
setter: 'value',
extractor: (e) => e.target.value
})} />
```
## Form
## Form
This is the form state that you can use when submitting the data
###### Example
###### Example
```javascript

View File

@ -4,32 +4,34 @@ import ReactDOM from 'react-dom'
import { useForm } from '../dist'
const Index: React.FC = () => {
const { field, form, errors } = useForm({
awesome: true,
})
const { auto, form, errors } = useForm({
awesome: true,
})
return (
<form
onSubmit={e => {
e.preventDefault()
console.log(form)
}}
>
<h1>Custom</h1>
return (
<form onSubmit={e => {
e.preventDefault()
console.log(form)
}}>
<label>
<input
type="checkbox"
{...field('awesome', {
setter: 'checked',
getter: 'onChange',
extractor: e => e.target.checked,
})}
/>
Is it awesome?
</label>
<h1>Custom</h1>
<label>
<input type="checkbox" {...auto('awesome', {
setter: 'checked',
getter: 'onChange',
extractor: (e) => e.target.checked
})} />
Is it awesome?
</label>
<input type="submit" />
</form>
)
<input type="submit" />
</form>
)
}
ReactDOM.render(<Index />, document.getElementById('custom'))
ReactDOM.render(<Index />, document.getElementById('custom'))

View File

@ -4,50 +4,52 @@ import ReactDOM from 'react-dom'
import { useForm } from '../'
const Index: React.FC = () => {
const { field, form, errors, isValid } = useForm(
{
username: '',
email: '',
password: '',
},
{
username: value => value.length > 3,
email: {
validator: /@/,
message: 'Must contain an @',
},
password: [
{
validator: /[A-Z]/,
message: 'Must contain an uppercase letter',
},
{
validator: /[\d]/,
message: 'Must contain a digit',
},
],
}
)
const { auto, form, errors, isValid } = useForm({
username: '',
email: '',
password: ''
}, {
username: value => value.length > 3,
email: {
validator: /@/,
message: 'Must contain an @',
},
password: [
{
validator: /[A-Z]/,
message: 'Must contain an uppercase letter'
},
{
validator: /[\d]/,
message: 'Must contain a digit'
},
]
})
return (
<form
onSubmit={e => {
e.preventDefault()
if (isValid) console.log(form)
}}
>
<h1>Errors & Validation</h1>
return (
<form onSubmit={(e) => {
e.preventDefault()
if (isValid) console.log(form)
}}>
<input {...field('username')} placeholder="Username" />
{errors.username && 'Must be longer than 3'}
<h1>Errors & Validation</h1>
<input {...field('email')} placeholder="EMail" />
{errors.email}
<input {...auto('username')} placeholder="Username" />
{errors.username && 'Must be longer than 3'}
<input {...field('password')} placeholder="Password" type="password" />
{errors.password}
<input {...auto('email')} placeholder="EMail" />
{errors.email}
<input {...auto('password')} placeholder="Password" type="password" />
{errors.password}
<input type="submit" />
</form>
)
<input type="submit" />
</form>
)
}
ReactDOM.render(<Index />, document.getElementById('errors'))
ReactDOM.render(<Index />, document.getElementById('errors'))

View File

@ -1,67 +1,64 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Form Hero</title>
<style>
body {
padding: 1em;
margin: 0;
font-family: 'Courier New', Courier, monospace;
}
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Form Hero</title>
<style>
body {
padding: 1em;
margin: 0;
font-family: 'Courier New', Courier, monospace;
}
section {
text-align: center;
max-width: 25em;
margin: 1em auto;
padding: 1em;
background-color: #f3f3f3;
border-radius: 0.5em;
box-shadow: 0 8px 16px -16px rgba(0, 0, 0, 0.5);
}
section {
text-align: center;
max-width: 25em;
margin: 1em auto;
padding: 1em;
background-color: #f3f3f3;
border-radius: .5em;
box-shadow: 0 8px 16px -16px rgba(0, 0, 0, .5);
}
input {
font-size: 1.25em;
display: block;
padding: 0.5em 1em;
margin-top: 0.5em;
border-radius: 1em;
width: 100%;
outline: none;
border: 0.15em solid white;
}
input {
font-size: 1.25em;
display: block;
padding: .5em 1em;
margin-top: .5em;
border-radius: 1em;
width: 100%;
outline: none;
border: .15em solid white;
}
input:focus,
input:hover {
border-color: #31def5;
}
input:focus,
input:hover {
border-color: #31def5;
input[type='submit'] {
cursor: pointer;
}
}
input[type='checkbox'] {
display: inline;
width: initial;
}
</style>
</head>
input[type="submit"] {
cursor: pointer;
}
<body>
<section>
<h3>Open the console to see the submitted data</h3>
</section>
<section id="simple"></section>
<section id="errors"></section>
<section id="select"></section>
<section id="custom"></section>
input[type="checkbox"] {
display: inline;
width: initial;
}
</style>
</head>
<body>
<section>
<h3>Open the console to see the submitted data</h3>
</section>
<section id="simple"></section>
<section id="errors"></section>
<section id="select"></section>
<section id="custom"></section>
<script src="./simple.tsx"></script>
<script src="./errorsAndValidation.tsx"></script>
<script src="./select.tsx"></script>
<script src="./custom.tsx"></script>
</body>
</html>
<script src="./simple.tsx"></script>
<script src="./errorsAndValidation.tsx"></script>
<script src="./select.tsx"></script>
<script src="./custom.tsx"></script>
</body>
</html>

View File

@ -4,30 +4,29 @@ import ReactDOM from 'react-dom'
import { useForm } from '../dist'
const Index: React.FC = () => {
const { field, form, errors } = useForm({
type: 'formhero',
})
const { auto, form, errors } = useForm({
type: 'formhero',
})
return (
<form
onSubmit={e => {
e.preventDefault()
console.log(form)
}}
>
<h1>Select</h1>
return (
<form onSubmit={e => {
e.preventDefault()
console.log(form)
}}>
<select {...field('type')}>
<option value="redux-form">Redux-Form</option>
<option value="react-hook-forms">React-Hook-Forms</option>
<option value="formik">Formik</option>
<option value="formhero">FormHero</option>
</select>
<h1>Select</h1>
<select {...auto('type')}>
<option value="redux-form">Redux-Form</option>
<option value="react-hook-forms">React-Hook-Forms</option>
<option value="formik">Formik</option>
<option value="formhero">FormHero</option>
</select>
<input type="submit" />
</form>
)
<input type="submit" />
</form>
)
}
ReactDOM.render(<Index />, document.getElementById('select'))
ReactDOM.render(<Index />, document.getElementById('select'))

View File

@ -4,27 +4,26 @@ import ReactDOM from 'react-dom'
import { useForm } from '../dist'
const Index: React.FC = () => {
const { field, form, errors } = useForm({
username: 'unicorn',
password: '',
})
const { auto, form, errors } = useForm({
username: 'unicorn',
password: '',
})
return (
<form
onSubmit={e => {
e.preventDefault()
console.log(form)
}}
>
<h1>Simple</h1>
return (
<form onSubmit={e => {
e.preventDefault()
console.log(form)
}}>
<input {...field('username')} placeholder="Username" />
<input {...field('password')} placeholder="Password" type="password" />
<h1>Simple</h1>
<input {...auto('username')} placeholder="Username" />
<input {...auto('password')} placeholder="Password" type="password" />
<input type="submit" />
</form>
)
<input type="submit" />
</form>
)
}
ReactDOM.render(<Index />, document.getElementById('simple'))
ReactDOM.render(<Index />, document.getElementById('simple'))

View File

@ -88,10 +88,10 @@ export const useForm = <T, U extends { [key in keyof T]: useFormValidatorParamet
_validate(key, extracted)
}
const auto = (key: keyof T, opts: useFormOptions = {}) => ({
const field = (key: keyof T, opts: useFormOptions = {}) => ({
[opts.getter || options.getter || 'onChange']: update(key, opts.extractor),
[opts.setter || options.setter || 'value']: form[key] as any,
})
return { form, update, auto, errors, isValid }
return { form, update, field, errors, isValid }
}

View File

@ -6,7 +6,7 @@ import { useForm } from '../'
const TextError: React.FC<{ error?: string }> = ({ error }) => (!error ? null : <div className="has-text-danger">{error}</div>)
const Index: React.FC = () => {
const { auto, form, errors, isValid } = useForm(
const { field, form, errors, isValid } = useForm(
{
username: '',
password: '',
@ -40,20 +40,20 @@ const Index: React.FC = () => {
<div>
<form onSubmit={_submit}>
<div>Username</div>
<input className="input" {...auto('username')} />
<input className="input" {...field('username')} />
<TextError error={errors.username} />
<br />
<br />
<div>Password</div>
<input className="input" {...auto('password')} />
<input className="input" {...field('password')} />
<TextError error={errors.password} />
<br />
<br />
<div>Which one to choose?</div>
<div className="select">
<select {...auto('type')}>
<select {...field('type')}>
<option value="redux-form">Redux-Form</option>
<option value="react-hook-forms">React-Hook-Forms</option>
<option value="formik">Formik</option>
@ -66,7 +66,7 @@ const Index: React.FC = () => {
<label className="checkbox">
<input
type="checkbox"
{...auto('awesome', {
{...field('awesome', {
setter: 'checked',
getter: 'onChange',
extractor: e => e.target.checked,