Youhou
This commit is contained in:
commit
17fd5a7303
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"presets": [
|
||||
["env", {
|
||||
"modules": false
|
||||
}],
|
||||
"stage-0",
|
||||
"react"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"parser": "babel-eslint",
|
||||
"extends": [
|
||||
"standard",
|
||||
"standard-react"
|
||||
],
|
||||
"env": {
|
||||
"es6": true
|
||||
},
|
||||
"plugins": [
|
||||
"react"
|
||||
],
|
||||
"parserOptions": {
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
// don't force es6 functions to include space before paren
|
||||
"space-before-function-paren": 0,
|
||||
|
||||
// allow specifying true explicitly for boolean props
|
||||
"react/jsx-boolean-value": 0
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
.DS_Store
|
||||
node_modules/
|
||||
.idea/
|
||||
example/
|
|
@ -0,0 +1,4 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- 9
|
||||
- 8
|
|
@ -0,0 +1,31 @@
|
|||
# react-forms
|
||||
|
||||
> React form components
|
||||
|
||||
[![NPM](https://img.shields.io/npm/v/react-forms.svg)](https://www.npmjs.com/package/react-forms) [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
npm install --save react-forms
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```jsx
|
||||
import React, { Component } from 'react'
|
||||
|
||||
import MyComponent from 'react-forms'
|
||||
|
||||
class Example extends Component {
|
||||
render () {
|
||||
return (
|
||||
<MyComponent />
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT © [maximerenou](https://github.com/maximerenou)
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
"name": "react-forms",
|
||||
"version": "1.0.0",
|
||||
"description": "React form components",
|
||||
"author": "maximerenou",
|
||||
"license": "MIT",
|
||||
"repository": "maximerenou/react-forms",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.es.js",
|
||||
"jsnext:main": "dist/index.es.js",
|
||||
"engines": {
|
||||
"node": ">=8",
|
||||
"npm": ">=5"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "cross-env CI=1 react-scripts test --env=jsdom",
|
||||
"test:watch": "react-scripts test --env=jsdom",
|
||||
"build": "rollup -c",
|
||||
"start": "rollup -c -w",
|
||||
"prepare": "npm run build",
|
||||
"predeploy": "cd example && npm install && npm run build",
|
||||
"deploy": "gh-pages -d example/build"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prop-types": "^15.5.4",
|
||||
"react": "^15.0.0 || ^16.0.0",
|
||||
"react-dom": "^15.0.0 || ^16.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@svgr/rollup": "^2.4.1",
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-eslint": "^8.2.5",
|
||||
"babel-plugin-external-helpers": "^6.22.0",
|
||||
"babel-preset-env": "^1.7.0",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"babel-preset-stage-0": "^6.24.1",
|
||||
"cross-env": "^5.1.4",
|
||||
"eslint": "^5.0.1",
|
||||
"eslint-config-standard": "^11.0.0",
|
||||
"eslint-config-standard-react": "^6.0.0",
|
||||
"eslint-plugin-import": "^2.13.0",
|
||||
"eslint-plugin-node": "^7.0.1",
|
||||
"eslint-plugin-promise": "^4.0.0",
|
||||
"eslint-plugin-react": "^7.10.0",
|
||||
"eslint-plugin-standard": "^3.1.0",
|
||||
"gh-pages": "^1.2.0",
|
||||
"react": "^16.4.1",
|
||||
"react-dom": "^16.4.1",
|
||||
"react-scripts": "^1.1.4",
|
||||
"rollup": "^0.64.1",
|
||||
"rollup-plugin-babel": "^3.0.7",
|
||||
"rollup-plugin-commonjs": "^9.1.3",
|
||||
"rollup-plugin-node-resolve": "^3.3.0",
|
||||
"rollup-plugin-peer-deps-external": "^2.2.0",
|
||||
"rollup-plugin-postcss": "^1.6.2",
|
||||
"rollup-plugin-url": "^1.4.0"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"dependencies": {
|
||||
"react-toastify": "^4.5.2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
import babel from 'rollup-plugin-babel'
|
||||
import commonjs from 'rollup-plugin-commonjs'
|
||||
import external from 'rollup-plugin-peer-deps-external'
|
||||
import postcss from 'rollup-plugin-postcss'
|
||||
import resolve from 'rollup-plugin-node-resolve'
|
||||
import url from 'rollup-plugin-url'
|
||||
import svgr from '@svgr/rollup'
|
||||
|
||||
import pkg from './package.json'
|
||||
|
||||
export default {
|
||||
input: 'src/index.js',
|
||||
output: [
|
||||
{
|
||||
file: pkg.main,
|
||||
format: 'cjs',
|
||||
sourcemap: true
|
||||
},
|
||||
{
|
||||
file: pkg.module,
|
||||
format: 'es',
|
||||
sourcemap: true
|
||||
}
|
||||
],
|
||||
plugins: [
|
||||
external(),
|
||||
postcss({
|
||||
modules: true
|
||||
}),
|
||||
url(),
|
||||
svgr(),
|
||||
babel({
|
||||
exclude: 'node_modules/**',
|
||||
plugins: [ 'external-helpers' ]
|
||||
}),
|
||||
resolve(),
|
||||
commonjs()
|
||||
]
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"env": {
|
||||
"jest": true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
import React, {Component} from 'react';
|
||||
import {FormConsumer} from "./FormContext";
|
||||
import FormItemError from "./FormItemError";
|
||||
|
||||
export default class CheckBox extends Component
|
||||
{
|
||||
render()
|
||||
{
|
||||
return (
|
||||
<FormConsumer>
|
||||
{(data) => this.renderCheckBox(data)}
|
||||
</FormConsumer>
|
||||
)
|
||||
}
|
||||
|
||||
renderCheckBox(data)
|
||||
{
|
||||
let props = {...this.props}
|
||||
if (props.children) delete props.children
|
||||
if (props.onChangeValue) delete props.onChangeValue
|
||||
return (
|
||||
<div className="form-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={this.props.value}
|
||||
onChange={(e) => this.props.onChangeValue(e.target.checked)}
|
||||
{...props} />
|
||||
{this.renderLabel(data)}
|
||||
<FormItemError name={this.props.name} data={data} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderLabel(data)
|
||||
{
|
||||
if (this.props.children.length) {
|
||||
return (
|
||||
<label className="form-check-label" htmlFor={this.props.id}>{this.props.children}</label>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CheckBox.defaultProps = {
|
||||
name: 'checkbox',
|
||||
id: 'checkbox',
|
||||
className: 'form-check-input',
|
||||
onChangeValue: (checked) => {}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
import React, {Component} from 'react';
|
||||
import FormContext from './FormContext';
|
||||
import {toast} from 'react-toastify';
|
||||
|
||||
export default class Form extends Component
|
||||
{
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
data: props.data
|
||||
}
|
||||
|
||||
this.displayError = true
|
||||
this.handleError(props)
|
||||
}
|
||||
|
||||
componentWillReceiveProps(props)
|
||||
{
|
||||
this.handleError(props)
|
||||
this.setState({
|
||||
data: props.data
|
||||
})
|
||||
}
|
||||
|
||||
handleError(props)
|
||||
{
|
||||
if (props.data && props.data.error && props.toast && this.displayError == true) {
|
||||
toast.error(props.data.error)
|
||||
this.displayError = false
|
||||
}
|
||||
}
|
||||
|
||||
render()
|
||||
{
|
||||
return (
|
||||
<FormContext.Provider value={this.state.data}>
|
||||
<form onSubmit={this._onSubmit}>
|
||||
{this.renderError()}
|
||||
{this.props.children}
|
||||
</form>
|
||||
</FormContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
_onSubmit = (e) => {
|
||||
e.preventDefault()
|
||||
this.props.onSubmit()
|
||||
this.displayError = true
|
||||
return false;
|
||||
}
|
||||
|
||||
renderError()
|
||||
{
|
||||
if (this.state.data && this.state.data.error && !this.props.toast)
|
||||
{
|
||||
return (
|
||||
<p className="alert alert-danger">{this.state.data.error}</p>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
Form.defaultProps = {
|
||||
data: null,
|
||||
toast: false,
|
||||
onSubmit: () => {
|
||||
console.log('[Form] submitted')
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
import React, { Component } from 'react'
|
||||
|
||||
const FormContext = React.createContext({ data: null });
|
||||
|
||||
export default FormContext
|
||||
|
||||
export class FormConsumer extends Component
|
||||
{
|
||||
render()
|
||||
{
|
||||
return (
|
||||
<FormContext.Consumer>
|
||||
{(props) => this.props.children(props)}
|
||||
</FormContext.Consumer>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
import React, {Component} from 'react';
|
||||
|
||||
export default class FormItemError extends Component
|
||||
{
|
||||
render() {
|
||||
const data = this.props.data
|
||||
|
||||
if (!data || !data.errors || !data.errors[this.props.name]) return null
|
||||
|
||||
return (
|
||||
<p className="text-danger">{data.errors[this.props.name].error}</p>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
import React, {Component} from 'react';
|
||||
import {FormConsumer} from "./FormContext";
|
||||
import FormItemError from "./FormItemError";
|
||||
|
||||
export default class Input extends Component
|
||||
{
|
||||
render()
|
||||
{
|
||||
return (
|
||||
<FormConsumer>
|
||||
{(data) => this.renderInput(data)}
|
||||
</FormConsumer>
|
||||
)
|
||||
}
|
||||
|
||||
renderInput(data)
|
||||
{
|
||||
let props = {...this.props}
|
||||
if (props.children) delete props.children
|
||||
if (props.onChangeValue) delete props.onChangeValue
|
||||
return (
|
||||
<div className="form-group">
|
||||
{this.renderLabel(data)}
|
||||
<input onChange={(e) => this.props.onChangeValue(e.target.value)} {...props} />
|
||||
<FormItemError name={this.props.name} data={data} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderLabel(data)
|
||||
{
|
||||
if (this.props.label) {
|
||||
return (
|
||||
<label htmlFor={this.props.id}>{this.props.label}</label>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Input.defaultProps = {
|
||||
name: 'input',
|
||||
id: 'input',
|
||||
className: 'form-control',
|
||||
type: 'text',
|
||||
onChangeValue: (text) => {}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import React, {Component} from 'react';
|
||||
import {OptionGroupConsumer} from "./OptionGroup";
|
||||
|
||||
export default class Option extends Component
|
||||
{
|
||||
render()
|
||||
{
|
||||
return (
|
||||
<OptionGroupConsumer>
|
||||
{(data) => this.renderOption(data)}
|
||||
</OptionGroupConsumer>
|
||||
)
|
||||
}
|
||||
|
||||
renderOption(data)
|
||||
{
|
||||
let props = {...this.props}
|
||||
|
||||
return (
|
||||
<option {...props}>{this.props.children}</option>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Option.defaultProps = {
|
||||
value: 'option'
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
import React, {Component} from 'react';
|
||||
import {FormConsumer} from './FormContext';
|
||||
import FormItemError from "./FormItemError";
|
||||
|
||||
const OptionGroupContext = React.createContext({});
|
||||
|
||||
export class OptionGroupConsumer extends Component
|
||||
{
|
||||
render()
|
||||
{
|
||||
return (
|
||||
<OptionGroupContext.Consumer>
|
||||
{(props) => this.props.children(props)}
|
||||
</OptionGroupContext.Consumer>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default class OptionGroup extends Component
|
||||
{
|
||||
render()
|
||||
{
|
||||
return (
|
||||
<FormConsumer>
|
||||
{(data) => this.renderOptionGroup(data)}
|
||||
</FormConsumer>
|
||||
)
|
||||
}
|
||||
|
||||
renderOptionGroup(data)
|
||||
{
|
||||
const props = {...this.props}
|
||||
return (
|
||||
<div className="form-group">
|
||||
{this.renderLabel()}
|
||||
<select {...this.props.data} {...props} onChange={(e) => this._onChange(e)}>
|
||||
{this.renderChildren()}
|
||||
</select>
|
||||
<FormItemError name={this.props.name} data={data} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
_onChange = (e) => {
|
||||
if (this.props.multiple) {
|
||||
let value = []
|
||||
const options = e.target.options;
|
||||
for (let i = 0, l = options.length; i < l; i++) {
|
||||
if (options[i].selected) {
|
||||
value.push(options[i].value);
|
||||
}
|
||||
}
|
||||
this.props.onChangeValue(value)
|
||||
}
|
||||
else {
|
||||
this.props.onChangeValue(e.target.value)
|
||||
}
|
||||
}
|
||||
|
||||
renderLabel()
|
||||
{
|
||||
if (!this.props.label) return null
|
||||
|
||||
return (
|
||||
<label htmlFor={this.props.id}>{this.props.label}</label>
|
||||
)
|
||||
}
|
||||
|
||||
renderChildren()
|
||||
{
|
||||
return (
|
||||
<OptionGroupContext.Provider value={{}}>
|
||||
{this.props.children}
|
||||
</OptionGroupContext.Provider>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
OptionGroup.defaultProps = {
|
||||
name: 'select',
|
||||
className: 'form-control',
|
||||
onChangeValue: (value) => {}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
import React, {Component} from 'react';
|
||||
import {RadioGroupConsumer} from "./RadioGroup";
|
||||
|
||||
export default class Radio extends Component
|
||||
{
|
||||
render()
|
||||
{
|
||||
return (
|
||||
<RadioGroupConsumer>
|
||||
{(data) => this.renderRadio(data)}
|
||||
</RadioGroupConsumer>
|
||||
)
|
||||
}
|
||||
|
||||
renderRadio(data)
|
||||
{
|
||||
let props = {...this.props}
|
||||
if (props.children) delete props.children
|
||||
|
||||
return (
|
||||
<div className="form-check">
|
||||
<input
|
||||
type="radio"
|
||||
checked={data.value === this.props.value}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked)
|
||||
data.onChangeValue(this.props.value)
|
||||
}}
|
||||
{...props} />
|
||||
{this.renderLabel(data)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderLabel(data)
|
||||
{
|
||||
if (this.props.children.length) {
|
||||
return (
|
||||
<label className="form-check-label" htmlFor={this.props.id}>{this.props.children}</label>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Radio.defaultProps = {
|
||||
value: 'option',
|
||||
className: 'form-check-input'
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
import React, {Component} from 'react';
|
||||
import {FormConsumer} from './FormContext';
|
||||
import FormItemError from "./FormItemError";
|
||||
|
||||
const RadioGroupContext = React.createContext({});
|
||||
|
||||
export class RadioGroupConsumer extends Component
|
||||
{
|
||||
render()
|
||||
{
|
||||
return (
|
||||
<RadioGroupContext.Consumer>
|
||||
{(props) => this.props.children(props)}
|
||||
</RadioGroupContext.Consumer>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default class RadioGroup extends Component
|
||||
{
|
||||
render()
|
||||
{
|
||||
return (
|
||||
<FormConsumer>
|
||||
{(data) => this.renderRadioGroup(data)}
|
||||
</FormConsumer>
|
||||
)
|
||||
}
|
||||
|
||||
renderRadioGroup(data)
|
||||
{
|
||||
return (
|
||||
<div className="form-options mb-3">
|
||||
{this.renderLabel()}
|
||||
{this.renderChildren()}
|
||||
<FormItemError name={this.props.name} data={data} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderLabel()
|
||||
{
|
||||
if (!this.props.label) return null
|
||||
|
||||
return (
|
||||
<p className="mb-0 mt-2">{this.props.label}</p>
|
||||
)
|
||||
}
|
||||
|
||||
renderChildren()
|
||||
{
|
||||
return (
|
||||
<RadioGroupContext.Provider value={{
|
||||
onChangeValue: (value) => this.props.onChangeValue(value),
|
||||
value: this.props.value
|
||||
}}>
|
||||
{this.props.children}
|
||||
</RadioGroupContext.Provider>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
RadioGroup.defaultProps = {
|
||||
name: 'radio',
|
||||
onChangeValue: (value) => {}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import React, {Component} from 'react';
|
||||
|
||||
export default class Submit extends Component {
|
||||
render() {
|
||||
return (
|
||||
<button type="submit" {...this.props}>{this.props.children}</button>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Submit.defaultProps = {
|
||||
className: 'btn btn-primary'
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
import React, {Component} from 'react';
|
||||
import {FormConsumer} from "./FormContext";
|
||||
import FormItemError from "./FormItemError";
|
||||
|
||||
export default class TextArea extends Component
|
||||
{
|
||||
render()
|
||||
{
|
||||
return (
|
||||
<FormConsumer>
|
||||
{(data) => this.renderTextArea(data)}
|
||||
</FormConsumer>
|
||||
)
|
||||
}
|
||||
|
||||
renderTextArea(data)
|
||||
{
|
||||
let props = {...this.props}
|
||||
if (props.onChangeValue) delete props.onChangeValue
|
||||
return (
|
||||
<div className="form-group">
|
||||
{this.renderLabel(data)}
|
||||
<textarea onChange={(e) => this.props.onChangeValue(e.target.value)} {...props} />
|
||||
<FormItemError name={this.props.name} data={data} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderLabel(data)
|
||||
{
|
||||
if (this.props.label) {
|
||||
return (
|
||||
<label htmlFor={this.props.id}>{this.props.label}</label>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextArea.defaultProps = {
|
||||
name: 'textarea',
|
||||
id: 'textarea',
|
||||
className: 'form-control',
|
||||
onChangeValue: (text) => {}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import CheckBox from './CheckBox'
|
||||
import Form from './Form'
|
||||
import Input from './Input'
|
||||
import Option from './Option'
|
||||
import OptionGroup from './OptionGroup'
|
||||
import Radio from './Radio'
|
||||
import RadioGroup from './RadioGroup'
|
||||
import Submit from './Submit'
|
||||
import TextArea from './TextArea'
|
||||
|
||||
export {
|
||||
CheckBox, Form, Input, Option, OptionGroup, RadioGroup, Radio, Submit, TextArea
|
||||
}
|
Loading…
Reference in New Issue