├── preview.png
├── .prettierrc
├── .babelrc
├── docs
├── gh-pages.js
├── styles.less
├── index.html
└── index.js
├── .gitignore
├── src
└── index.js
├── LICENSE
├── README.md
├── package.json
├── .eslintrc.js
└── webpack.config.js
/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rsuite/schema-form/HEAD/preview.png
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "tabWidth": 2,
4 | "singleQuote": true
5 | }
6 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "es2015",
4 | "react",
5 | "stage-0"
6 | ],
7 | "plugins": [
8 | "transform-class-properties"
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/docs/gh-pages.js:
--------------------------------------------------------------------------------
1 | var ghpages = require('gh-pages');
2 | var path = require('path');
3 |
4 | ghpages.publish(path.join(__dirname, '../assets'), function (err) {
5 | console.log(err);
6 | });
7 |
--------------------------------------------------------------------------------
/docs/styles.less:
--------------------------------------------------------------------------------
1 | @import '~rsuite/styles/less/index.less';
2 |
3 | .page {
4 | padding: 50px;
5 | }
6 |
7 | .example {
8 | margin: 20px 0;
9 | border: 1px solid #eee;
10 | padding: 20px;
11 | }
12 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <%= htmlWebpackPlugin.options.title %>
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # nyc test coverage
18 | .nyc_output
19 |
20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21 | .grunt
22 |
23 | # node-waf configuration
24 | .lock-wscript
25 |
26 | # Compiled binary addons (http://nodejs.org/api/addons.html)
27 | build/Release
28 |
29 | # Dependency directories
30 | node_modules
31 | jspm_packages
32 |
33 | # Optional npm cache directory
34 | .npm
35 |
36 | # Optional REPL history
37 | .node_repl_history
38 |
39 | lib
40 | yarn.lock
41 | package-lock.json
42 | assets
43 |
44 | dev-publish.sh
45 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Form,
4 | FormGroup,
5 | ControlLabel,
6 | HelpBlock,
7 | FormControl,
8 | Schema
9 | } from 'rsuite';
10 |
11 | export default function(schema = []) {
12 | const schemaModel = {};
13 | schema.forEach(item => {
14 | schemaModel[item.key] = item.type;
15 | });
16 | const model = Schema.Model(schemaModel);
17 |
18 | return React.forwardRef((props, ref) => (
19 |
36 | ));
37 | }
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 - 2019 HYPERS
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # schema-form
2 |
3 | Form generation and validation based on `schema-typed` and `rsuite`.
4 |
5 | ## Installation
6 |
7 | ```
8 | npm install @rsuite/schema-form --save
9 | ```
10 |
11 | ## Usage
12 |
13 | ```js
14 | import SchemaForm from '@rsuite/schema-form';
15 | import { InputNumber, SelectPicker, Schema } from 'rsuite';
16 |
17 | const { StringType, NumberType } = Schema.Types;
18 |
19 | const Form = SchemaForm([
20 | {
21 | key: 'username',
22 | type: StringType().isRequired('This field is required'),
23 | label: 'Username'
24 | },
25 | {
26 | key: 'email',
27 | type: StringType().isEmail('Please enter a valid email address'),
28 | label: 'Email',
29 | helpBlock: 'Please enter your company email address'
30 | },
31 | {
32 | key: 'age',
33 | type: NumberType('Please enter a valid number'),
34 | label: 'Age',
35 | componentClass: InputNumber,
36 | componentProps: {
37 | autoComplete: 'off',
38 | }
39 | },
40 | {
41 | key: 'group',
42 | type: NumberType(),
43 | label: 'User Group',
44 | componentClass: SelectPicker,
45 | componentProps: {
46 | style: {
47 | width: 300
48 | },
49 | data: [{ value: 1, label: 'Admin' }, { value: 2, label: 'User' }],
50 | }
51 | }
52 | ]);
53 |
54 | ReactDOM.render(, mountNode);
55 | ```
56 |
57 | 
58 |
59 | ## Documentation
60 |
61 | - [Form examples](https://rsuitejs.com/en/components/form#Examples)
62 | - [Form props](https://rsuitejs.com/en/components/form#Props)
63 |
64 | ## License
65 |
66 | MIT licensed
67 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@rsuite/schema-form",
3 | "version": "0.0.2",
4 | "description": "Form generation and validation based on schema-typed and rsiute",
5 | "main": "lib",
6 | "scripts": {
7 | "dev": "NODE_ENV=development webpack-dev-server --inline --progress --colors --port 3100 --host 0.0.0.0 ",
8 | "build": "rm -rf lib && babel src --out-dir lib ",
9 | "docs": "rm -rf assets && NODE_ENV=production webpack",
10 | "publish-docs": "node docs/gh-pages.js",
11 | "lint": "eslint src *.js",
12 | "test": "npm run lint"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "git+https://github.com/rsuite/schema-form.git"
17 | },
18 | "files": [
19 | "CHANGELOG.md",
20 | "lib",
21 | "src"
22 | ],
23 | "keywords": [
24 | "form",
25 | "schema"
26 | ],
27 | "author": "simonguo.2009@gmail.com",
28 | "license": "MIT",
29 | "bugs": {
30 | "url": "https://github.com/rsuite/schema-form/issues"
31 | },
32 | "homepage": "https://github.com/rsuite/schema-form#readme",
33 | "peerDependencies": {
34 | "rsuite": "^3.5.2"
35 | },
36 | "devDependencies": {
37 | "babel-cli": "^6.26.0",
38 | "babel-core": "^6.7.6",
39 | "babel-eslint": "^6.1.2",
40 | "babel-loader": "^6.2.4",
41 | "babel-plugin-transform-class-properties": "^6.24.1",
42 | "babel-preset-es2015": "^6.24.1",
43 | "babel-preset-react": "^6.24.1",
44 | "babel-preset-stage-0": "^6.24.1",
45 | "css-loader": "^2.1.0",
46 | "eslint": "^3.19.0",
47 | "eslint-config-airbnb": "^15.0.1",
48 | "eslint-plugin-babel": "^4.1.1",
49 | "eslint-plugin-flowtype": "^2.41.0",
50 | "eslint-plugin-import": "^2.6.1",
51 | "eslint-plugin-json": "^1.2.0",
52 | "eslint-plugin-jsx-a11y": "^6.0.3",
53 | "eslint-plugin-react": "^7.10.0",
54 | "extract-text-webpack-plugin": "^3.0.2",
55 | "gh-pages": "^2.0.1",
56 | "html-webpack-plugin": "^2.29.0",
57 | "less": "^3.9.0",
58 | "less-loader": "^4.0.5",
59 | "react": "^16.7.0",
60 | "react-dom": "^16.7.0",
61 | "react-hot-loader": "^3.1.3",
62 | "rsuite": "^3.5.2",
63 | "style-loader": "^0.18.2",
64 | "url-loader": "^0.5.9",
65 | "webpack": "^3.0.0",
66 | "webpack-dev-server": "^2.3.0"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/docs/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 |
4 | import { InputNumber, SelectPicker, Schema, Button } from 'rsuite';
5 | import './styles.less';
6 | import SchemaForm from '../src';
7 |
8 | const { StringType, NumberType } = Schema.Types;
9 | const Form = SchemaForm([
10 | {
11 | key: 'username',
12 | type: StringType().isRequired('This field is required'),
13 | label: 'Username'
14 | },
15 | {
16 | key: 'email',
17 | type: StringType().isEmail('Please enter a valid email address'),
18 | label: 'Email',
19 | helpBlock: 'Please enter your company email address'
20 | },
21 | {
22 | key: 'age',
23 | type: NumberType('Please enter a valid number'),
24 | label: 'Age',
25 | componentClass: InputNumber,
26 | componentProps: {
27 | autoComplete: 'off'
28 | }
29 | },
30 | {
31 | key: 'group',
32 | type: NumberType(),
33 | label: 'User Group',
34 | componentClass: SelectPicker,
35 | componentProps: {
36 | style: {
37 | width: 300
38 | },
39 | data: [
40 | { value: 1, label: 'Admin' },
41 | { value: 2, label: 'User' }
42 | ]
43 | }
44 | }
45 | ]);
46 |
47 | class App extends React.Component {
48 | constructor(props) {
49 | super(props);
50 | this.state = {
51 | formData: {}
52 | };
53 | }
54 | form = null;
55 | bindFormRef = ref => {
56 | this.form = ref;
57 | };
58 | handleChange = formData => {
59 | this.setState({
60 | formData
61 | });
62 | };
63 | handleSubmit = () => {
64 | if (this.form.check()) {
65 | console.log(this.state.formData);
66 | }
67 | };
68 | render() {
69 | return (
70 |
84 | );
85 | }
86 | }
87 |
88 | ReactDOM.render(, document.getElementById('app'));
89 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const OFF = 0;
4 | const WARNING = 1;
5 | const ERROR = 2;
6 |
7 | module.exports = {
8 | extends: 'airbnb',
9 | env: {
10 | browser: true,
11 | node: true,
12 | es6: true,
13 | mocha: true
14 | },
15 | globals: {
16 | assert: true
17 | },
18 | parser: 'babel-eslint',
19 | plugins: ['react', 'babel', 'json', 'flowtype'],
20 | parserOptions: {
21 | ecmaVersion: 6,
22 | sourceType: 'module',
23 | ecmaFeatures: {
24 | jsx: true,
25 | experimentalObjectRestSpread: true
26 | }
27 | },
28 | rules: {
29 | indent: [ERROR, 2, { SwitchCase: 1 }], //规定代码的缩进方式:2个空格
30 | camelcase: ERROR, //强制驼峰法命名
31 | curly: ERROR, //必须使用 if(){} 中的{}
32 | eqeqeq: ERROR, //必须使用全等
33 | 'brace-style': [ERROR, '1tbs'], //大括号风格
34 | quotes: [ERROR, 'single'], //引号类型
35 | semi: [ERROR, 'always'], //语句强制分号结尾
36 | 'space-infix-ops': ERROR, //中缀操作符周围要不要有空格
37 | 'no-param-reassign': OFF, //不允许对函数的形参进行赋值
38 | 'prefer-spread': ERROR, //首选展开运算
39 | 'comma-dangle': OFF, //不允许或强制在对象字面量或者数组属性的结尾使用逗号
40 | 'padded-blocks': OFF, //规定代码块前后是否要加空行
41 | 'prefer-const': OFF,
42 | 'no-multi-spaces': ERROR,
43 | 'no-var': OFF,
44 | 'one-var': OFF,
45 | 'class-methods-use-this': WARNING,
46 | 'no-unused-expressions': [ERROR, { allowShortCircuit: true }],
47 | 'arrow-parens': [ERROR, 'as-needed'],
48 | 'no-mixed-operators': OFF,
49 | 'no-continue': OFF,
50 | /**
51 | * https://github.com/airbnb/javascript/tree/master/react
52 | */
53 | 'react/prefer-es6-class': [WARNING, 'always'], //使用 class extends React.Component
54 | 'react/jsx-pascal-case': ERROR, //骆驼式命名
55 | 'react/jsx-closing-bracket-location': ERROR, //JSX语法缩进/格式
56 | 'react/jsx-curly-spacing': ERROR, //JSX {} 引用括号里两边加空格
57 | 'react/jsx-boolean-value': [OFF, 'always'], //如果属性值为 true, 可以直接省略
58 | 'jsx-quotes': [ERROR, 'prefer-double'], //JSX属性值总是使用双引号(")
59 | 'react/no-string-refs': ERROR, //Refs里使用回调函数
60 | 'react/jsx-wrap-multilines': ERROR, //多行的JSX标签写在 ()里
61 | 'react/self-closing-comp': ERROR, //没有子元素的标签来说总是自己关闭标签
62 | 'react/jsx-no-bind': ERROR, //当在 render() 里使用事件处理方法时,提前在构造函数里把 this 绑定上去
63 | 'react/no-is-mounted': ERROR, //不要再使用 isMounted
64 | 'react/prop-types': [ERROR, { ignore: ['children', 'className', 'style'] }],
65 | 'jsx-a11y/href-no-hash': OFF,
66 | 'jsx-a11y/label-has-for': OFF,
67 | 'react/jsx-filename-extension': OFF,
68 | 'react/prefer-stateless-function': OFF,
69 | 'react/require-default-props': OFF,
70 | 'react/no-find-dom-node': OFF,
71 |
72 | /**
73 | * Flowtype
74 | */
75 | 'flowtype/define-flow-type': ERROR,
76 | 'flowtype/require-valid-file-annotation': OFF,
77 | 'flowtype/require-parameter-type': OFF,
78 | 'flowtype/require-return-type': OFF,
79 | 'flowtype/space-after-type-colon': OFF,
80 | 'flowtype/space-before-type-colon': OFF,
81 | 'flowtype/type-id-match': OFF,
82 | 'flowtype/use-flow-type': ERROR
83 | }
84 | };
85 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const HtmlwebpackPlugin = require('html-webpack-plugin');
4 | const ExtractTextPlugin = require('extract-text-webpack-plugin');
5 | const { NODE_ENV } = process.env;
6 |
7 | const extractLess = new ExtractTextPlugin({
8 | filename: '[name].[contenthash].css',
9 | disable: NODE_ENV === 'development'
10 | });
11 |
12 | const docsPath = NODE_ENV === 'development' ? './assets' : './';
13 | const plugins = [
14 | new webpack.HotModuleReplacementPlugin(),
15 | new webpack.NamedModulesPlugin(),
16 | new webpack.DefinePlugin({
17 | NODE_ENV: JSON.stringify(NODE_ENV)
18 | }),
19 | extractLess,
20 | new HtmlwebpackPlugin({
21 | title: 'Schema Form',
22 | filename: 'index.html',
23 | template: 'docs/index.html',
24 | inject: true,
25 | hash: true,
26 | path: docsPath
27 | })
28 | ];
29 |
30 | if (process.env.NODE_ENV === 'production') {
31 | plugins.push(new webpack.optimize.UglifyJsPlugin());
32 | plugins.push(
33 | new webpack.BannerPlugin({
34 | banner: `Last update: ${new Date().toString()}`
35 | })
36 | );
37 | }
38 |
39 | const common = {
40 | entry: path.resolve(__dirname, 'docs/index.js'),
41 | devServer: {
42 | hot: true,
43 | contentBase: path.resolve(__dirname, ''),
44 | publicPath: '/'
45 | },
46 | output: {
47 | path: path.resolve(__dirname, 'assets'),
48 | filename: 'bundle.js',
49 | publicPath: './'
50 | },
51 | plugins,
52 | module: {
53 | rules: [
54 | {
55 | test: /\.jsx?$/,
56 | use: ['babel-loader?babelrc'],
57 | exclude: /node_modules/
58 | },
59 | {
60 | test: /\.less$/,
61 | loader: extractLess.extract({
62 | use: [
63 | {
64 | loader: 'css-loader'
65 | },
66 | {
67 | loader: 'less-loader',
68 | options: { javascriptEnabled: true }
69 | }
70 | ],
71 | // use style-loader in development
72 | fallback: 'style-loader'
73 | })
74 | },
75 | {
76 | test: /\.(woff|woff2|eot|ttf|svg)($|\?)/,
77 | use: [
78 | {
79 | loader:
80 | 'url-loader?limit=1&hash=sha512&digest=hex&size=16&name=resources/[hash].[ext]'
81 | }
82 | ]
83 | }
84 | ]
85 | }
86 | };
87 |
88 | module.exports = (env = {}) => {
89 | if (NODE_ENV === 'development') {
90 | return Object.assign({}, common, {
91 | entry: [
92 | 'react-hot-loader/patch',
93 | 'webpack-dev-server/client?http://127.0.0.1:3100',
94 | 'webpack/hot/only-dev-server',
95 | path.resolve(__dirname, 'docs/index')
96 | ],
97 | devtool: 'source-map'
98 | });
99 | }
100 |
101 | return Object.assign({}, common, {
102 | entry: [path.resolve(__dirname, 'docs/index')]
103 | });
104 | };
105 |
--------------------------------------------------------------------------------