Skip to content

Commit 8305aa1

Browse files
committed
Cleanup
1 parent 8906dcd commit 8305aa1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+1083
-32
lines changed

.npmignore

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
/node_modules
22
/src
3+
/stories
34
/tests
4-
/jest.config.js
55
/.eslintrc.js
6+
/.gitattributes
7+
/.gitignore
68
/.prettierrc.js
79
/.idea
10+
/.storybook
11+
/.vscode
812
/.travis.yml
913
/CONTRIBUTING.md
10-
/LICENSE.md
14+
/jest.config.js
1115
/tsconfig.json
16+
/tsconfig.stories.json

README.md

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
Talesoft React Forms
2+
===================
3+
4+
What is it
5+
----------
6+
7+
A form handling library for React very similar to
8+
Formik.
9+
10+
It's laid out to be performant and thus uses Faceboook's
11+
ImmutableJS internally to handle the whole form state.
12+
13+
It also primarily uses hooks and functional components.
14+
15+
You don't need to know anything about the ImmutableJS
16+
API, it's completely hidden in the features of this library.
17+
18+
Features
19+
--------
20+
21+
- React & React Native support.
22+
- Hooks to easily build custom components for it.
23+
- Blazing fast. Got a form with over 300 fields? No problem, thanks to ImmutableJS.
24+
- All HTML inputs available as fully integrated and easy to use components.
25+
- Validation in-built, validator-agnostic.
26+
27+
How to use it
28+
-------------
29+
30+
**Basic Login Form**
31+
32+
```jsx
33+
import { Form, FormElement, InputField } from '@talesoft/react-forms'
34+
import React, { useMemo } from 'react'
35+
36+
function MyForm() {
37+
const initialValue = useMemo(() => ({
38+
email: '',
39+
password: '',
40+
}), [])
41+
42+
function onSubmit(value) {
43+
myApi.tokens.create({
44+
email: value.email,
45+
password: value.password,
46+
})
47+
}
48+
49+
return (
50+
{/* This is the core of a form */}
51+
<Form initialValue={initialValue} onSubmit={onSubmit}>
52+
{/* The HTML Form element */}
53+
<FormElement>
54+
{/* This acts like a normal HTML <input /> */}
55+
<InputField type="email" name="email" />
56+
57+
<InputField type="password" name="password" />
58+
59+
<button type="submit">Login</button>
60+
</FormElement>
61+
</Form>
62+
)
63+
}
64+
```
65+
66+
**Nested Form**
67+
68+
```jsx
69+
import { Form, FormElement, InputField } from '@talesoft/react-forms'
70+
import React, { useMemo } from 'react'
71+
72+
function MyForm() {
73+
const initialValue = useMemo(() => ({
74+
firstName: 'John',
75+
lastName: 'Doe',
76+
social: {
77+
linkedIn: '',
78+
twitter: '',
79+
facebook: '',
80+
},
81+
}), [])
82+
83+
function onSubmit(value) {
84+
myApi.users.update(value)
85+
}
86+
87+
return (
88+
<Form initialValue={initialValue} onSubmit={onSubmit}>
89+
<FormElement>
90+
<InputField name="firstName" />
91+
<InputField name="lastName" />
92+
93+
<InputField name="social.linkedIn" />
94+
<InputField name="social.twitter" />
95+
<InputField name="social.facebook" />
96+
97+
<button type="submit">Update</button>
98+
</FormElement>
99+
</Form>
100+
)
101+
}
102+
```
103+
104+
**Form Arrays**
105+
106+
```jsx
107+
import {
108+
Form,
109+
FormElement,
110+
InputField,
111+
FieldArray,
112+
SelectField
113+
} from '@talesoft/react-forms'
114+
import React, { useMemo } from 'react'
115+
116+
function MyForm() {
117+
const initialValue = useMemo(() => ({
118+
address: '',
119+
openingHours: [
120+
{
121+
weekday: 'mo',
122+
startTime: '10:30:00',
123+
endTime: '18:30:00',
124+
},
125+
]
126+
}), [])
127+
128+
function onSubmit(value) {
129+
myApi.users.update(value)
130+
}
131+
132+
return (
133+
<Form initialValue={initialValue} onSubmit={onSubmit}>
134+
<FormElement>
135+
<InputField name="address" />
136+
137+
<h3>Opening Hours</h3>
138+
<FieldArray name="openingHours">
139+
{({ map, push }) => (
140+
<div>
141+
{map(({ childName, remove }) => (
142+
<div>
143+
<SelectField name={childName('weekday')}>
144+
<option value="mo">Monday</option>
145+
<option value="tu">Tuesday</option>
146+
<option value="we">Wednesday</option>
147+
{/* etc.. */}
148+
</SelectField>
149+
<InputField type="time" name={childName('startTime')} />
150+
<InputField type="time" name={childName('endTime')} />
151+
</div>
152+
))}
153+
<button type="button" onClick={() => push(initialValue)}>
154+
Add another weekday
155+
</button>
156+
</div>
157+
)}
158+
</FieldArray>
159+
160+
<button type="submit">Update</button>
161+
</FormElement>
162+
</Form>
163+
)
164+
}
165+
```
166+
167+
**Too bulky? Use hooks to build own components!**
168+
169+
```jsx
170+
// OpeningHoursField.jsx
171+
import React, { useMemo } from 'react'
172+
import { useFieldArray } from '@talesoft/react-forms'
173+
174+
export default function OpeningHoursField({ name }) {
175+
const { map, push } = useFieldArray(name)
176+
return (
177+
<div>
178+
{map(({ childName, remove }) => (
179+
<div>
180+
<SelectField name={childName('weekday')}>
181+
<option value="mo">Monday</option>
182+
<option value="tu">Tuesday</option>
183+
<option value="we">Wednesday</option>
184+
{/* etc.. */}
185+
</SelectField>
186+
<InputField type="time" name={childName('startTime')} />
187+
<InputField type="time" name={childName('endTime')}/>
188+
</div>
189+
))}
190+
<button type="button" onClick={() => push(initialValue)}>
191+
Add another weekday
192+
</button>
193+
</div>
194+
)
195+
}
196+
```
197+
198+
```jsx
199+
// MyForm.jsx
200+
import {
201+
Form,
202+
FormElement,
203+
} from '@talesoft/react-forms'
204+
import OpeningHoursField from './OpeningHoursField'
205+
import React, { useMemo } from 'react'
206+
207+
function MyForm() {
208+
const initialValue = useMemo(() => ({
209+
address: '',
210+
openingHours: [
211+
{
212+
weekday: 'mo',
213+
startTime: '10:30:00',
214+
endTime: '18:30:00',
215+
},
216+
]
217+
}), [])
218+
219+
function onSubmit(value) {
220+
myApi.users.update(value)
221+
}
222+
223+
return (
224+
<Form initialValue={initialValue} onSubmit={onSubmit}>
225+
<FormElement>
226+
<InputField name="address" />
227+
228+
<h3>Opening Hours</h3>
229+
<OpeningHoursField name="openingHours" />
230+
231+
<button type="submit">Update</button>
232+
</FormElement>
233+
</Form>
234+
)
235+
}
236+
```

dist/fields/FieldArray.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { PropsWithChildren, ReactElement } from 'react';
2+
import { FieldArrayDispatchers } from './useFieldArray';
3+
export interface FieldArrayProps {
4+
name: string;
5+
children: (dispatchers: FieldArrayDispatchers) => ReactElement;
6+
}
7+
export default function FieldArray({ name, children }: PropsWithChildren<FieldArrayProps>): ReactElement<any, string | ((props: any) => ReactElement<any, string | any | (new (props: any) => import("react").Component<any, any, any>)> | null) | (new (props: any) => import("react").Component<any, any, any>)>;

dist/fields/FieldArray.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
"use strict";
2+
var __importDefault = (this && this.__importDefault) || function (mod) {
3+
return (mod && mod.__esModule) ? mod : { "default": mod };
4+
};
5+
Object.defineProperty(exports, "__esModule", { value: true });
6+
const useFieldArray_1 = __importDefault(require("./useFieldArray"));
7+
function FieldArray({ name, children }) {
8+
const dispatchers = useFieldArray_1.default(name);
9+
return children(dispatchers);
10+
}
11+
exports.default = FieldArray;

dist/fields/FieldState.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import ValidationState from '../forms/ValidationState';
2+
import FormErrorList from '../forms/FormErrorList';
3+
export default interface FieldState {
4+
errors: FormErrorList;
5+
validationState: ValidationState;
6+
changed: boolean;
7+
}

dist/fields/FieldState.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
"use strict";
2+
Object.defineProperty(exports, "__esModule", { value: true });

dist/fields/FieldStateMap.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import FormFieldStateRecord from './FieldStateRecord';
2+
import { Map } from 'immutable';
3+
declare type FormFieldStateMap = Map<string, FormFieldStateRecord>;
4+
export default FormFieldStateMap;

dist/fields/FieldStateMap.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
"use strict";
2+
Object.defineProperty(exports, "__esModule", { value: true });

dist/fields/FieldStateRecord.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { Record } from 'immutable';
2+
import FieldState from './FieldState';
3+
declare type FieldStateRecord = Record<FieldState>;
4+
export default FieldStateRecord;

dist/fields/FieldStateRecord.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
"use strict";
2+
Object.defineProperty(exports, "__esModule", { value: true });

dist/fields/InputField.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { HTMLProps } from 'react';
2+
export declare type InputProps = HTMLProps<HTMLInputElement>;
3+
export default function InputField(props: InputProps): JSX.Element;

dist/fields/InputField.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"use strict";
2+
var __importDefault = (this && this.__importDefault) || function (mod) {
3+
return (mod && mod.__esModule) ? mod : { "default": mod };
4+
};
5+
Object.defineProperty(exports, "__esModule", { value: true });
6+
const react_1 = __importDefault(require("react"));
7+
const useField_1 = __importDefault(require("./useField"));
8+
function toScriptValue(target) {
9+
var _a, _b;
10+
switch (target.type) {
11+
case 'file':
12+
return target.multiple ? target.files : (_b = (_a = target.files) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : null;
13+
case 'checkbox':
14+
return target.checked;
15+
case 'number':
16+
case 'range':
17+
return parseInt(target.value, 10);
18+
case 'time':
19+
case 'date':
20+
case 'datetime-local':
21+
return Math.floor(target.valueAsNumber / 1000);
22+
default:
23+
return target.value;
24+
}
25+
}
26+
function toFieldValueProps(inputProps, value) {
27+
switch (inputProps.type) {
28+
case 'file':
29+
return {};
30+
case 'checkbox':
31+
return { checked: !!value };
32+
case 'radio':
33+
return { checked: value === inputProps.value };
34+
case 'number':
35+
case 'range':
36+
return { value: parseInt(value, 10) };
37+
case 'time':
38+
return { value: new Date(value * 1000).toISOString().substr(11, 8) };
39+
case 'date':
40+
return { value: new Date(value * 1000).toISOString().substr(0, 10) };
41+
case 'datetime-local':
42+
return { value: new Date(value * 1000).toISOString().substr(0, 19) };
43+
default:
44+
return { value: value };
45+
}
46+
}
47+
function InputField(props) {
48+
var _a;
49+
const { value, setValue } = useField_1.default((_a = props.name) !== null && _a !== void 0 ? _a : '');
50+
const valueProps = toFieldValueProps(props, value);
51+
return react_1.default.createElement("input", Object.assign({}, props, valueProps, { onChange: event => setValue(toScriptValue(event.target)) }));
52+
}
53+
exports.default = InputField;

dist/fields/SelectField.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { HTMLProps } from 'react';
2+
export declare type SelectProps = HTMLProps<HTMLSelectElement>;
3+
export default function SelectField(props: SelectProps): JSX.Element;

dist/fields/SelectField.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
"use strict";
2+
var __importDefault = (this && this.__importDefault) || function (mod) {
3+
return (mod && mod.__esModule) ? mod : { "default": mod };
4+
};
5+
Object.defineProperty(exports, "__esModule", { value: true });
6+
const react_1 = __importDefault(require("react"));
7+
const useField_1 = __importDefault(require("./useField"));
8+
function SelectField(props) {
9+
var _a;
10+
const { value, setValue } = useField_1.default((_a = props.name) !== null && _a !== void 0 ? _a : '');
11+
console.log(value);
12+
return (react_1.default.createElement("select", Object.assign({}, props, { value: value, onChange: event => setValue(props.multiple
13+
? Array.from(event.target.options)
14+
.filter(opt => opt.selected)
15+
.map(opt => opt.value)
16+
: event.target.value) })));
17+
}
18+
exports.default = SelectField;

dist/fields/SpanField.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { HTMLProps } from 'react';
2+
export declare type SpanProps = HTMLProps<HTMLSpanElement>;
3+
export interface SpanFieldProps extends SpanProps {
4+
name?: string;
5+
}
6+
export default function InputField({ name, ...spanProps }: SpanFieldProps): JSX.Element;

0 commit comments

Comments
 (0)