├── .eslintignore ├── .eslintrc ├── .gitignore ├── .npmignore ├── .prettierrc ├── .storybook ├── addons.js ├── config.js ├── manager-head.html ├── preview-head.html └── webpack.config.js ├── .travis.yml ├── LICENSE.md ├── README.md ├── README.zh-CN.md ├── jest.config.js ├── lerna.json ├── now.json ├── package.json ├── src ├── Markdown.less ├── Markdown.tsx ├── Welcome.stories.tsx ├── form-pro │ ├── .babelrc.js │ ├── .npmignore │ ├── LICENSE │ ├── README.md │ ├── README.zh-CN.md │ ├── gulpfile.js │ ├── index.js │ ├── package.json │ ├── src │ │ ├── FormPro.tsx │ │ ├── __stories__ │ │ │ ├── CustomizedFormControls.stories.tsx │ │ │ ├── FormPro.module.less │ │ │ ├── FormPro.stories.tsx │ │ │ ├── WithModal.stories.tsx │ │ │ ├── data.ts │ │ │ └── index.stories.tsx │ │ ├── __tests__ │ │ │ └── FormPro.test.tsx │ │ ├── create.tsx │ │ ├── index.ts │ │ └── style │ │ │ ├── index.less │ │ │ └── index.ts │ └── tsconfig.json ├── react-app-env.d.ts ├── setupTests.js └── tsconfig.settings.json ├── tsconfig.json └── tslint.json /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | lib 4 | es -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["react-app", "plugin:prettier/recommended"], 3 | "plugins": ["prettier"], 4 | "root": true, 5 | "rules": { 6 | "prettier/prettier": "error" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # General 2 | node_modules 3 | npm-debug.log 4 | yarn-error.log 5 | yarn.lock 6 | package-lock.json 7 | .coverage 8 | 9 | # Mac General 10 | .DS_Store 11 | .AppleDouble 12 | .LSOverride 13 | 14 | # Storybook 15 | build 16 | 17 | # Build 18 | lib 19 | es -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | .babelrc.js -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "trailingComma": "all", 5 | "endOfLine": "auto" 6 | } 7 | -------------------------------------------------------------------------------- /.storybook/addons.js: -------------------------------------------------------------------------------- 1 | import '@storybook/addon-actions/register' 2 | import '@storybook/addon-options/register' 3 | import '@storybook/addon-console' 4 | import '@storybook/addon-storysource/register' 5 | import '@storybook/addon-knobs/register' 6 | -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | import { configure, addParameters } from '@storybook/react' 2 | import { setOptions } from '@storybook/addon-options' 3 | import { themes } from '@storybook/theming' 4 | 5 | setOptions({ 6 | name: 'Antd Extension', 7 | showNav: true, 8 | showPanel: false, 9 | showAddonPanel: false, 10 | isToolshown: false, 11 | }) 12 | 13 | const req = require.context('../src', true, /.stories.tsx$/) 14 | 15 | const loadStories = () => { 16 | require('../src/Welcome.stories') 17 | req.keys().forEach(filename => req(filename)) 18 | } 19 | 20 | configure(loadStories, module) 21 | -------------------------------------------------------------------------------- /.storybook/manager-head.html: -------------------------------------------------------------------------------- 1 | 63 | -------------------------------------------------------------------------------- /.storybook/preview-head.html: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /.storybook/webpack.config.js: -------------------------------------------------------------------------------- 1 | const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false' 2 | 3 | module.exports = async ({ config }) => { 4 | // Filter out some rules from create-react-app's webpack configuration. 5 | const lintRule = config.module.rules.find( 6 | item => '.js'.match(item.test) && item.enforce === 'pre', 7 | ) 8 | const sassRule = config.module.rules.find(item => '.sass'.match(item.test)) 9 | const sassModuleRule = config.module.rules.find( 10 | item => '.module.sass'.match(item.test) && !item.exclude, 11 | ) 12 | const babelRule = config.module.rules.find( 13 | item => item.loader && item.loader.includes('babel-loader'), 14 | ) 15 | 16 | // Style files regexes 17 | const lessRegex = /\.less$/ 18 | const lessModuleRegex = /\.module\.less$/ 19 | 20 | // Common function to get less loader'use list by sassRule in CAR's webpack configuration. 21 | const getLessUse = sassRule => 22 | sassRule.use 23 | .filter(item => { 24 | const loaderPath = typeof item === 'string' ? item : item.loader 25 | return !loaderPath.includes('sass-loader') 26 | }) 27 | .concat({ 28 | loader: require.resolve('less-loader'), 29 | options: { 30 | sourceMap: shouldUseSourceMap, 31 | javascriptEnabled: true, 32 | }, 33 | }) 34 | 35 | // 1. Exclude files in node_modules using eslint-loader. 36 | lintRule.exclude = [/node_modules/] 37 | 38 | // 2. Adds support for LESS (using .less extensions). 39 | config.module.rules.push({ 40 | test: lessRegex, 41 | exclude: lessModuleRegex, 42 | use: getLessUse(sassRule), 43 | sideEffects: true, 44 | }) 45 | 46 | // 3. Adds support for CSS Modules, but using LESS (using .module.less extensions). 47 | config.module.rules.push({ 48 | test: lessModuleRegex, 49 | use: getLessUse(sassModuleRule), 50 | }) 51 | 52 | // 4. Added bable plugin, such as babel-plugin-import. 53 | // https://github.com/ant-design/babel-plugin-import 54 | babelRule.options.plugins.push([ 55 | require.resolve('babel-plugin-import'), 56 | { libraryName: 'antd', style: true }, 57 | ]) 58 | 59 | // 5. Added storybook addon addon-storysource. 60 | config.module.rules.push({ 61 | test: /\.stories\.tsx?$/, 62 | loaders: [ 63 | { 64 | loader: require.resolve('@storybook/addon-storysource/loader'), 65 | options: { parser: 'typescript' }, 66 | }, 67 | ], 68 | }) 69 | 70 | return config 71 | } 72 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - node 4 | script: 5 | - npm run export-static-storybook 6 | before_install: 7 | - yarn global add now 8 | after_script: 9 | - now build -A ../now.json -t $NOW_TOKEN 10 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT LICENSE 2 | 3 | Copyright (c) 2019 zuiidea (zuiiidea@gmail.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | English | [简体中文](./README.zh-CN.md) 2 | 3 | # Ant Design Extension 4 | 5 | `Ant Design Extension` is an extended set of components for [Ant Design](https://ant.design/docs/react/introduce-cn) that conforms to the `Ant Design` specification and has visually exposed UI components. 6 | 7 | ## Features 8 | 9 | - High quality React components in compliance with Ant Design specifications. 10 | - Written in TypeScript with complete defined types. 11 | - Support for original theme customization within the Ant Design project. 12 | - There is no coupling between the expansion components and they are used independently. 13 | 14 | ## Components 15 | 16 | | Component Name | Package Name | Description | 17 | | -------------- | ------------ | ------------------------------------------------- | 18 | | [FormPro](./src/form-pro/README.md) | `form-pro` | Configurable on-demand loading of form components | 19 | 20 | ## Installation 21 | 22 | We recommend using npm or yarn to install. 23 | 24 | ```bash 25 | $ npm install [Package Name] -S 26 | $ yarn add [Package Name] -S 27 | ``` 28 | 29 | ```bash 30 | # For Example 31 | $ npm install form-pro -S 32 | $ yarn add form-pro -S 33 | ``` 34 | 35 | If you are in a bad network environment, you can try other registries and tools like [cnpm](https://github.com/cnpm/cnpm). 36 | 37 | 38 | ## Document and examples 39 | 40 | [https://antd-extension.now.sh](https://antd-extension.now.sh) -------------------------------------------------------------------------------- /README.zh-CN.md: -------------------------------------------------------------------------------- 1 | 简体中文 | [English](./README.md) 2 | 3 | # Ant Design Extension 4 | 5 | `Ant Design Extension` 是 [Ant Design](https://ant.design/docs/react/introduce-cn) 的扩展组件集,符合 `Ant Design` 规范、且带有视觉展现的 UI 组件。 6 | 7 | ## 特性 8 | 9 | - 符合 Ant Design 规范的高质量 React 组件。 10 | - 使用 TypeScript 构建,提供完整的类型定义文件。 11 | - 支持 Ant Design 项目内原有的主题定制。 12 | - 扩展组件之间无耦合,独立使用。 13 | 14 | ## 组件 15 | 16 | | 名称 | 独立包名 | 描述 | 17 | | ------- | ---------- | ------------------------ | 18 | | [FormPro](./src/form-pro/README.zh-CN.md) | `form-pro` | 配置式的按需加载表单组件 | 19 | 20 | ## 安装 21 | 22 | 推荐使用 npm 或 yarn 的方式进行开发。 23 | 24 | ```bash 25 | $ npm install [独立包名] -S 26 | $ yarn add [独立包名] -S 27 | ``` 28 | 29 | ```bash 30 | # For Example 31 | $ npm install form-pro -S 32 | $ yarn add form-pro -S 33 | ``` 34 | 35 | 如果你的网络环境不佳,推荐使用 [cnpm](https://github.com/cnpm/cnpm)。 36 | 37 | ## 文档与例子 38 | 39 | [https://antd-extension.now.sh](https://antd-extension.now.sh) 40 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | const config = require('../../jest.config') 2 | 3 | module.exports = { 4 | ...config, 5 | roots: [__dirname], 6 | moduleNameMapper: { 7 | '\\.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': 8 | '/__mocks__/fileMock.js', 9 | '\\.(css|scss)$': '/__mocks__/styleMock.js', 10 | '\\.(md)$': '/__mocks__/htmlMock.js', 11 | }, 12 | transform: { 13 | ...config.transform, 14 | '^.+\\.svg$': 15 | '/node_modules/react-scripts/config/jest/fileTransform.js', 16 | }, 17 | moduleDirectories: ['/node_modules', 'src'], 18 | } 19 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "lerna": "3.10.7", 3 | "npmClient": "yarn", 4 | "packages": ["src/*"], 5 | "version": "independent" 6 | } 7 | -------------------------------------------------------------------------------- /now.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "antd-extension", 3 | "version": 1, 4 | "alias": ["form-pro.now.sh", "antd-extension.now.sh"], 5 | "type": "static", 6 | "public": true 7 | } 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "antd-extension", 3 | "version": "0.0.1", 4 | "scripts": { 5 | "storybook": "NODE_ENV=development start-storybook -p 9002", 6 | "debug": "NODE_ENV=development start-storybook -p 9002 --debug-webpack", 7 | "start": "npm run storybook", 8 | "test": "react-scripts test --env=jsdom", 9 | "build": "npm run build:cjs && npm run build:cjs:type && npm run build:esm && npm run build:esm:type && npm run build:less", 10 | "build:cjs:type": "lerna exec --parallel -- tsc -d --emitDeclarationOnly --outDir lib", 11 | "build:esm:type": "lerna exec --parallel -- tsc -d --emitDeclarationOnly --outDir es", 12 | "build:cjs": "lerna exec --parallel -- BABEL_ENV=production NODE_ENV=production TARGET_ENV=cjs babel src --extensions .ts,.tsx -d lib", 13 | "build:esm": "lerna exec --parallel -- BABEL_ENV=production NODE_ENV=production TARGET_ENV=esm babel src --extensions .ts,.tsx -d es", 14 | "build:less": "lerna exec gulp less", 15 | "bootstrap": "lerna bootstrap", 16 | "export-static-storybook": "build-storybook -c .storybook -o build", 17 | "lint:ts": "lerna exec --parallel -- tslint src/**/*.ts{,x} --exclude /lib/**/*.ts{,x},es/**/*.ts{,x}", 18 | "lint:js": "eslint src/**/*.js", 19 | "lint": "npm run lint:ts && npm run lint:js", 20 | "publish-packages": "lerna publish", 21 | "prepublish-packages": "npm run build", 22 | "precommit": "lint-staged" 23 | }, 24 | "devDependencies": { 25 | "@babel/cli": "^7.4.4", 26 | "@storybook/addon-actions": "5.1.9", 27 | "@storybook/addon-console": "^1.1.1", 28 | "@storybook/addon-knobs": "^5.1.9", 29 | "@storybook/addon-options": "^5.1.9", 30 | "@storybook/addon-storysource": "^5.1.9", 31 | "@storybook/react": "5.1.9", 32 | "@types/enzyme": "^3.9.3", 33 | "@types/jest": "^24.0.14", 34 | "@types/node": "12.0.8", 35 | "@types/react": "16.8.20", 36 | "@types/react-dom": "16.8.4", 37 | "@types/storybook__react": "^4.0.2", 38 | "babel-plugin-import": "^1.12.0", 39 | "babel-preset-react-app": "^9.0.0", 40 | "enzyme": "^3.10.0", 41 | "enzyme-adapter-react-16": "^1.14.0", 42 | "enzyme-to-json": "^3.3.5", 43 | "eslint-config-prettier": "^6.0.0", 44 | "eslint-plugin-prettier": "^3.1.0", 45 | "gulp": "^4.0.2", 46 | "gulp-less": "^4.0.1", 47 | "husky": "^3.0.0", 48 | "lerna": "^3.15.0", 49 | "less": "^3.9.0", 50 | "less-loader": "^5.0.0", 51 | "lint-staged": "^9.2.0", 52 | "prettier": "1.15.2", 53 | "react-docgen-typescript-loader": "^3.1.0", 54 | "react-markdown": "^4.1.0", 55 | "react-scripts": "^3.0.1", 56 | "tslint": "^5.17.0", 57 | "tslint-config-airbnb": "^5.11.1", 58 | "tslint-react": "^4.0.0", 59 | "typescript": "3.5.1" 60 | }, 61 | "dependencies": { 62 | "antd": "^3.19.3", 63 | "react": "^16.8.6", 64 | "react-dom": "^16.8.6" 65 | }, 66 | "husky": { 67 | "hooks": { 68 | "pre-commit": "lint-staged" 69 | } 70 | }, 71 | "lint-staged": { 72 | "*.{ts,tsx}": [ 73 | "prettier --write", 74 | "git add" 75 | ] 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Markdown.less: -------------------------------------------------------------------------------- 1 | .markdown { 2 | color: #314659; 3 | font-size: 14px; 4 | line-height: 2; 5 | } 6 | .highlight { 7 | line-height: 1.5; 8 | } 9 | .markdown img { 10 | max-width: calc(100% - 32px); 11 | } 12 | .markdown p > img { 13 | margin: 34px 0; 14 | -webkit-box-shadow: 0 8px 20px rgba(143, 168, 191, 0.35); 15 | box-shadow: 0 8px 20px rgba(143, 168, 191, 0.35); 16 | } 17 | .markdown p > img.markdown-inline-image { 18 | margin: 0 34px; 19 | -webkit-box-shadow: none; 20 | box-shadow: none; 21 | } 22 | .markdown h1 { 23 | margin-top: 8px; 24 | margin-bottom: 20px; 25 | color: #0d1a26; 26 | font-weight: 500; 27 | font-size: 30px; 28 | font-family: Avenir, -apple-system, BlinkMacSystemFont, 'Segoe UI', 29 | 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', 30 | Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 31 | 'Segoe UI Symbol', sans-serif; 32 | line-height: 38px; 33 | } 34 | .markdown h1 .subtitle { 35 | margin-left: 12px; 36 | } 37 | .markdown h2 { 38 | font-size: 24px; 39 | line-height: 32px; 40 | } 41 | .markdown h2, 42 | .markdown h3, 43 | .markdown h4, 44 | .markdown h5, 45 | .markdown h6 { 46 | clear: both; 47 | margin: 1.6em 0 0.6em; 48 | color: #0d1a26; 49 | font-weight: 500; 50 | font-family: Avenir, -apple-system, BlinkMacSystemFont, 'Segoe UI', 51 | 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', 52 | Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 53 | 'Segoe UI Symbol', sans-serif; 54 | } 55 | .markdown h3 { 56 | font-size: 18px; 57 | } 58 | .markdown h4 { 59 | font-size: 16px; 60 | } 61 | .markdown h5 { 62 | font-size: 14px; 63 | } 64 | .markdown h6 { 65 | font-size: 12px; 66 | } 67 | .markdown hr { 68 | clear: both; 69 | height: 1px; 70 | margin: 56px 0; 71 | background: #ebedf0; 72 | border: 0; 73 | } 74 | .markdown p, 75 | .markdown pre { 76 | margin: 1em 0; 77 | } 78 | .markdown pre { 79 | margin: 16px 0; 80 | padding: 12px 20px; 81 | overflow: auto; 82 | } 83 | .markdown ul > li { 84 | margin-left: 20px; 85 | padding-left: 4px; 86 | list-style-type: circle; 87 | } 88 | .markdown ul > li:empty { 89 | display: none; 90 | } 91 | .markdown ol > li { 92 | margin-left: 20px; 93 | padding-left: 4px; 94 | list-style-type: decimal; 95 | } 96 | .markdown ul > li > p, 97 | .markdown ol > li > p { 98 | margin: 0.2em 0; 99 | } 100 | .markdown code { 101 | margin: 0 1px; 102 | padding: 0.2em 0.4em; 103 | font-size: 0.9em; 104 | background: #f2f4f5; 105 | border: 1px solid #eee; 106 | border-radius: 3px; 107 | } 108 | .markdown pre { 109 | font-family: 'Lucida Console', Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', 110 | monospace; 111 | background: #f2f4f5; 112 | border-radius: 2px; 113 | } 114 | .markdown pre code { 115 | margin: 0; 116 | padding: 0; 117 | overflow: auto; 118 | color: #314659; 119 | font-size: 13px; 120 | background: #f2f4f5; 121 | border: none; 122 | } 123 | .markdown strong, 124 | .markdown b { 125 | font-weight: 500; 126 | } 127 | .markdown > table { 128 | width: 100%; 129 | margin: 8px 0 16px; 130 | empty-cells: show; 131 | border: 1px solid #ebedf0; 132 | border-collapse: collapse; 133 | border-spacing: 0; 134 | } 135 | .markdown > table th { 136 | color: #5c6b77; 137 | font-weight: 500; 138 | white-space: nowrap; 139 | background: rgba(0, 0, 0, 0.02); 140 | } 141 | .markdown > table th, 142 | .markdown > table td { 143 | padding: 16px 24px; 144 | text-align: left; 145 | border: 1px solid #ebedf0; 146 | } 147 | .markdown blockquote { 148 | margin: 1em 0; 149 | padding-left: 0.8em; 150 | color: #697b8c; 151 | font-size: 90%; 152 | border-left: 4px solid #ebedf0; 153 | } 154 | .markdown blockquote p { 155 | margin: 0; 156 | } 157 | 158 | .markdown h1:hover .anchor, 159 | .markdown h2:hover .anchor, 160 | .markdown h3:hover .anchor, 161 | .markdown h4:hover .anchor, 162 | .markdown h5:hover .anchor, 163 | .markdown h6:hover .anchor { 164 | display: inline-block; 165 | opacity: 1; 166 | } 167 | .markdown > br, 168 | .markdown > p > br { 169 | clear: both; 170 | } 171 | 172 | .markdown.api-container { 173 | overflow-x: auto; 174 | } 175 | 176 | .markdown.api-container table { 177 | min-width: 719px; 178 | margin: 2em 0; 179 | font-size: 14px; 180 | font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, 181 | monospace; 182 | line-height: 1.5; 183 | border-width: 0; 184 | } 185 | 186 | .markdown.api-container table th, 187 | .markdown.api-container table td { 188 | padding: 14px 16px; 189 | border-color: #e8e8e8; 190 | border-width: 1px 0; 191 | } 192 | 193 | .markdown.api-container table th { 194 | border-width: 0 0 2px 0; 195 | } 196 | 197 | .markdown.api-container table td:first-child { 198 | width: 20%; 199 | color: #003a8c; 200 | font-weight: 500; 201 | } 202 | 203 | .markdown.api-container table td:nth-child(3) { 204 | width: 22%; 205 | color: #c41d7f; 206 | font-size: 13px; 207 | word-break: break-all; 208 | } 209 | 210 | .markdown.api-container table td:nth-child(4) { 211 | width: 13%; 212 | font-size: 13px; 213 | } 214 | -------------------------------------------------------------------------------- /src/Markdown.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import ReactMarkdown from 'react-markdown' 3 | import './Markdown.less' 4 | 5 | interface MarkdownProps { 6 | source?: string 7 | api?: boolean 8 | } 9 | 10 | const Markdown: React.FC = ({ source, api }) => { 11 | return ( 12 |
13 | 14 |
15 | ) 16 | } 17 | 18 | export default Markdown 19 | -------------------------------------------------------------------------------- /src/Welcome.stories.tsx: -------------------------------------------------------------------------------- 1 | import { storiesOf } from '@storybook/react' 2 | import * as React from 'react' 3 | import Markdown from './Markdown' 4 | import ReadMe from '../README.md' 5 | import ReadMeZHCN from '../README.zh-CN.md' 6 | import License from '../LICENSE.md' 7 | 8 | storiesOf('Antd Extension|Welcome', module) 9 | .add('Getting Started', () => ( 10 | 19 | )) 20 | .add('Getting Started (zh-CN)', () => ( 21 | 30 | )) 31 | .add('License', () =>
{License}
) 32 | -------------------------------------------------------------------------------- /src/form-pro/.babelrc.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | const env = api.cache(() => process.env.TARGET_ENV) 3 | return { 4 | presets: [ 5 | [ 6 | '@babel/env', 7 | { 8 | modules: env === 'esm' ? false : 'auto', 9 | }, 10 | ], 11 | 12 | ['react-app', { flow: false, typescript: true, absoluteRuntime: false }], 13 | ], 14 | 15 | plugins: [['babel-plugin-import', { libraryName: 'antd', style: true }]], 16 | 17 | ignore: ['src/__tests__/', 'src/__stories__/'], 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/form-pro/.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | .babelrc.js 3 | setupTests.js 4 | react-app-env.d.ts 5 | tsconfig.settings.json -------------------------------------------------------------------------------- /src/form-pro/LICENSE: -------------------------------------------------------------------------------- 1 | MIT LICENSE 2 | 3 | Copyright (c) 2019 zuiidea (zuiiidea@gmail.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /src/form-pro/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

6 | 7 | English | [简体中文](./README.zh-CN.md) 8 | 9 | ## FormPro 10 | 11 | - Generate forms with configuration, No need to use `import` cumbersome form controls. 12 | - Use the `React.lazy` API to support loading most of the `Ant Design` data input components on demand. 13 | - Flexible design, support for custom, preset form controls. 14 | - Written in `TypeScript` with complete defined types. 15 | 16 | [Live demo](https://codesandbox.io/s/standard-formpro-18usg?fontsize=14) 17 | 18 | ## When To Use 19 | 20 | Can completely replace Ant Design's [Form](https://ant.design/components/form/#Form) component, dealing with complex forms, pop-up windows, etc. 21 | 22 | ## How To Use 23 | 24 | ### Installation 25 | 26 | We recommend using npm or yarn to install. 27 | 28 | ```bash 29 | $ npm install form-pro --save 30 | # or 31 | $ yarn add form-pro --save 32 | ``` 33 | 34 | > If you are in a bad network environment, you can try other registries and tools like [cnpm](https://github.com/cnpm/cnpm). 35 | 36 | ### Usage 37 | 38 | ```js 39 | import FormPro from 'form-pro' 40 | import 'form-pro/lib/style' // If the development environment supports Less 41 | // If the development environment does not support Less, please use 42 | // import 'form-pro/lib/style/index.css' 43 | 44 | 56 | ``` 57 | 58 | ### Examples 59 | 60 | - [Standard FormPro](https://form-pro.now.sh/?path=/story/components-formpro--standard-formpro) 61 | [![Edit Standard FormPro](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/standard-formpro-18usg?fontsize=14) 62 | 63 | - [With Modal](https://form-pro.now.sh/?path=/?path=/story/components-formpro--with-modal) 64 | - [Customized Form Controls](https://form-pro.now.sh/?path=/story/components-formpro--customized-form-controls) 65 | 66 | ### Environment Support 67 | 68 | - React version is greater than 16.6. 69 | 70 | ## API 71 | 72 | ### FormPro 73 | 74 | | Property | Description | Type | Default | 75 | | --- | --- | --- | --- | 76 | | columns | Columns of table | ColumnProps [] | - | 77 | | formProps | Consistent with the Ant Design [Form](https://ant.design/components/form-cn/#Form) configuration | FormProps | - | 78 | | onChange | Triggered when the form value changes | (allValues, changedValues) => void | - | 79 | | onSubmit | Triggered when the submit button is clicked | (values) => void | - | 80 | | showSubmit | Whether to show the submit button | boolean | `true` | 81 | | submitText | Submit button text | React.ReactNode | `Submit` | 82 | | footer | Bottom of the form | React.ReactNode | - | 83 | | loading | Loading when all form controls are loaded | React.ReactNode | - | 84 | 85 | ### Column 86 | 87 | Form control configuration data object, which is an item in columns, Column uses the same API. 88 | 89 | | Property | Description | Type | Default | 90 | | --- | --- | --- | --- | 91 | | type | Form control type | string | - | 92 | | render | Custom form controls, when used with the `type` field, use `render` first. | React.ReactNode | - | 93 | | name | Field name within the form field | string | - | 94 | | options | Consistent with the [options](https://ant.design/components/form/#getFieldDecorator(id,-options)-parameters) parameter of getFieldDecorator(id, options) | object | - | 95 | | extraProps | Additional custom parameters for form controls, different control parameters, corresponding to `type` | object | - | 96 | | formItemProps | The parameters of the form control, different control parameters, corresponding to `type` | object | - | 97 | | loading | Loading when the current form control loads | React.ReactNode | - | 98 | 99 | ### FormPro.create(options) 100 | 101 | Use the `FormPro.create` function to create a new `FormPro` component. The new `FormPro` component built in `type` will be determined by `options`. 102 | 103 | | Property | Description | Type | Default | 104 | | --- | --- | --- | --- | 105 | | type | Form control type | string | - | 106 | | component | The component corresponding to the form control | any | - | 107 | | formItemRender | Custom rendering of FormItem | (itemOptions: any, Component: any) => JSX.Element | - | 108 | 109 | Create a `FormPro` component with a built-in `Checkbox` type. 110 | 111 | ```javascript 112 | const NewFormPro = create([{ 113 | type: 'Checkbox', 114 | component: React.lazy(() => import('antd/lib/checkbox')), 115 | }]) 116 | 117 | ... 118 | 119 | 120 | ``` 121 | 122 | ## Document and examples 123 | 124 | [https://form-pro.now.sh](https://form-pro.now.sh/?path=/story/components-formpro--read-me) -------------------------------------------------------------------------------- /src/form-pro/README.zh-CN.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

6 | 7 | 简体中文 | [English](./README.md) 8 | 9 | ## FormPro 10 | 11 | - 采用配置式生成表单,无需 `import` 繁琐的表单控件。 12 | - 使用 `React.lazy` API,支持按需加载大部分 `Ant Design` 数据输入组件。 13 | - 设计灵活,支持自定义、预置表单控件。 14 | - 使用 `TypeScript` 构建,提供完整的类型定义文件。 15 | 16 | [在线演示](https://codesandbox.io/s/standard-formpro-18usg?fontsize=14) 17 | 18 | ## 何时使用 19 | 20 | 可完全替代 `Ant Design` 的 [Form]((https://ant.design/components/form-cn/#Form)) 组件,可应对复杂表单,弹窗表单等场景。 21 | 22 | ## 如何使用 23 | 24 | ### 安装 25 | 26 | 推荐使用 npm 或 yarn 的方式进行开发。 27 | 28 | ```bash 29 | $ npm install form-pro --save 30 | # 或者 31 | $ yarn add form-pro --save 32 | ``` 33 | 34 | > 如果你的网络环境不佳,推荐使用 [cnpm](https://github.com/cnpm/cnpm)。 35 | 36 | ### 使用 37 | 38 | ```js 39 | import FormPro from 'form-pro' 40 | import 'form-pro/lib/style' // 如果项目支持 Less 41 | // 如果项目不支持 Less,请使用 42 | // import 'form-pro/lib/style/index.css' 43 | 44 | 56 | ``` 57 | 58 | ### 例子 59 | 60 | - [Standard FormPro](https://form-pro.now.sh/?path=/story/components-formpro--standard-formpro) 61 | [![Edit Standard FormPro](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/standard-formpro-18usg?fontsize=14) 62 | 63 | - [With Modal](https://form-pro.now.sh/?path=/?path=/story/components-formpro--with-modal) 64 | - [Customized Form Controls](https://form-pro.now.sh/?path=/story/components-formpro--customized-form-controls) 65 | 66 | ### 支持环境 67 | 68 | - React version is greater than 16.6。 69 | 70 | ## API 71 | 72 | ### FormPro 73 | 74 | | 属性 | 说明 | 类型 | 默认值 | 75 | | --- | --- | --- | --- | 76 | | columns | 表单控件的配置描述,具体项见下表 | ColumnProps [] | - | 77 | | formProps | 与 [Ant Design Form](https://ant.design/components/form-cn/#Form) 配置项保持一致 | FormProps | - | 78 | | onChange | 表单值发生变化时触发 | (allValues, changedValues) => void | - | 79 | | onSubmit | 点击提交按钮时触发 | (values) => void | - | 80 | | showSubmit | 是否展示提交按钮 | boolean | `true` | 81 | | submitText | 提交按钮文字 | React.ReactNode | `Submit` | 82 | | footer | 表单底部内容 | React.ReactNode | - | 83 | | loading | 所有表单控件加载时Loading | React.ReactNode | - | 84 | 85 | ### Column 86 | 87 | 表单控件配置数据对象,是 columns 中的一项,Column 使用相同的 API。 88 | 89 | | 属性 | 说明 | 类型 | 默认值 | 90 | | --- | --- | --- | --- | 91 | | type | 表单控件类型 | string | - | 92 | | render | 自定义表单控件,与 `type` 字段同时存在时,优先使用 `render` | React.ReactNode | - | 93 | | name | 表单域内字段名称 | string | - | 94 | | options | 与 getFieldDecorator(id, options) 的 [options](https://ant.design/components/form-cn/#getFieldDecorator(id,-options)-%E5%8F%82%E6%95%B0) 参数保持一致 | object | - | 95 | | extraProps | 表单控件的额外自定义参数,不同控件参数不同,与 `type` 对应 | object | - | 96 | | formItemProps | 表单控件的参数,不同控件参数不同,与 `type` 对应 | object | - | 97 | | loading | 当前表单控件加载时Loading | React.ReactNode | - | 98 | 99 | 100 | ### FormPro.create(options) 101 | 102 | 使用 `FormPro.create`函数可以创建一个新的 `FormPro` 组件,新的 `FormPro` 组件内置 `type` 将由 `options` 决定。 103 | 104 | | 属性 | 说明 | 类型 | 默认值 | 105 | | --- | --- | --- | --- | 106 | | type | 表单控件类型 | string | - | 107 | | component | 表单控件对应的组件 | any | - | 108 | | formItemRender | 自定义渲染FormItem | (itemOptions: any, Component: any) => JSX.Element | - | 109 | 110 | 创建一个内置`Checkbox`类型的 `FormPro` 组件 111 | 112 | ```javascript 113 | const NewFormPro = create([{ 114 | type: 'Checkbox', 115 | component: React.lazy(() => import('antd/lib/checkbox')), 116 | }]) 117 | 118 | ... 119 | 120 | 121 | ``` 122 | 123 | ## Document and examples 124 | 125 | [https://form-pro.now.sh](https://form-pro.now.sh/?path=/story/components-formpro--read-me-zh-cn) -------------------------------------------------------------------------------- /src/form-pro/gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp') 2 | const less = require('gulp-less') 3 | 4 | gulp.task('less', function() { 5 | return gulp 6 | .src('./src/style/index.less') 7 | .pipe( 8 | less({ 9 | javascriptEnabled: true, 10 | }), 11 | ) 12 | .pipe(gulp.dest('./lib/style/')) 13 | .pipe(gulp.dest('./lib/style/')) 14 | }) 15 | -------------------------------------------------------------------------------- /src/form-pro/index.js: -------------------------------------------------------------------------------- 1 | import 'antd/lib/auto-complete/style' 2 | import 'antd/lib/checkbox/style' 3 | import 'antd/lib/date-picker/style' 4 | import 'antd/lib/cascader/style' 5 | import 'antd/lib/input/style' 6 | import 'antd/lib/input-number/style' 7 | import 'antd/lib/switch/style' 8 | import 'antd/lib/select/style' 9 | import 'antd/lib/slider/style' 10 | import 'antd/lib/mentions/style' 11 | import 'antd/lib/rate/style' 12 | import 'antd/lib/radio/style' 13 | import 'antd/lib/tree-select/style' 14 | import 'antd/lib/time-picker/style' 15 | import 'antd/lib/upload/style' 16 | -------------------------------------------------------------------------------- /src/form-pro/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "form-pro", 3 | "version": "0.1.2", 4 | "description": "Expansion Ant Desgin From Component", 5 | "main": "lib/index.js", 6 | "module": "es/index.js", 7 | "author": "zuiidea", 8 | "keywords": ["Form Pro", "Form Extension", "Ant Design", "Ant Design Extension", "Configurable", "React lazy", "Load on demand"], 9 | "dependencies": { 10 | "antd": "^3.19.2", 11 | "react": "^16.8.6", 12 | "react-dom": "^16.8.6" 13 | }, 14 | "publishConfig": { 15 | "access": "public" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/form-pro/src/FormPro.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import Cascader from 'antd/lib/cascader' 3 | import DatePicker from 'antd/lib/date-picker' 4 | import Input from 'antd/lib/input' 5 | import Mentions from 'antd/lib/mentions' 6 | import Radio from 'antd/lib/radio' 7 | import Select from 'antd/lib/select' 8 | import create from './create' 9 | 10 | const { lazy } = React 11 | export interface IDataSourceItem { 12 | value: string 13 | label: string 14 | } 15 | 16 | const generateFormItemRender = ( 17 | FormItem: React.ComponentType, 18 | FormItemOption: React.ComponentType, 19 | ) => { 20 | return (itemOptions: any) => { 21 | const { extraProps = {} } = itemOptions 22 | const { dataSource = [], optionProps = {} } = extraProps 23 | 24 | return ( 25 | 26 | {dataSource.map((item: IDataSourceItem) => ( 27 | 28 | {item.label} 29 | 30 | ))} 31 | 32 | ) 33 | } 34 | } 35 | 36 | export const defaultOptions = [ 37 | { 38 | type: 'AutoComplete', 39 | component: lazy(() => import('antd/lib/auto-complete')), 40 | }, 41 | { 42 | type: 'Checkbox', 43 | component: lazy(() => import('antd/lib/checkbox')), 44 | }, 45 | { 46 | type: 'CheckboxGroup', 47 | component: lazy(() => import('antd/lib/checkbox/Group')), 48 | }, 49 | { 50 | type: 'Cascader', 51 | component: Cascader, 52 | }, 53 | { 54 | type: 'DatePicker', 55 | component: DatePicker, 56 | }, 57 | { 58 | type: 'MonthPicker', 59 | component: DatePicker.MonthPicker, 60 | }, 61 | { 62 | type: 'WeekPicker', 63 | component: DatePicker.WeekPicker, 64 | }, 65 | { 66 | type: 'RangePicker', 67 | component: DatePicker.RangePicker, 68 | }, 69 | { 70 | type: 'Input', 71 | component: Input, 72 | }, 73 | { 74 | type: 'InputSearch', 75 | component: Input.Search, 76 | }, 77 | { 78 | type: 'InputGroup', 79 | component: Input.Group, 80 | }, 81 | { 82 | type: 'InputTextArea', 83 | component: Input.TextArea, 84 | }, 85 | { 86 | type: 'InputPassword', 87 | component: Input.Password, 88 | }, 89 | { 90 | type: 'InputNumber', 91 | component: lazy(() => import('antd/lib/input-number')), 92 | }, 93 | { 94 | type: 'Mentions', 95 | formItemRender: generateFormItemRender(Mentions, Mentions.Option), 96 | }, 97 | { 98 | type: 'Rate', 99 | component: lazy(() => import('antd/lib/rate')), 100 | }, 101 | { 102 | type: 'Radio', 103 | component: Radio, 104 | }, 105 | { 106 | type: 'RadioGroup', 107 | formItemRender: generateFormItemRender(Radio.Group, Radio), 108 | }, 109 | { 110 | type: 'RadioButtonGroup', 111 | formItemRender: generateFormItemRender(Radio.Group, Radio.Button), 112 | }, 113 | { 114 | type: 'Switch', 115 | component: lazy(() => import('antd/lib/switch')), 116 | }, 117 | { 118 | type: 'Slider', 119 | component: lazy(() => import('antd/lib/slider')), 120 | }, 121 | { 122 | type: 'Select', 123 | formItemRender: generateFormItemRender(Select, Select.Option), 124 | }, 125 | { 126 | type: 'TreeSelect', 127 | component: lazy(() => import('antd/lib/tree-select')), 128 | }, 129 | { 130 | type: 'TimePicker', 131 | component: lazy(() => import('antd/lib/time-picker')), 132 | }, 133 | { 134 | type: 'Upload', 135 | component: lazy(() => import('antd/lib/upload')), 136 | }, 137 | { 138 | type: 'UploadDragger', 139 | component: lazy(() => import('antd/lib/upload/Dragger')), 140 | }, 141 | ] 142 | 143 | const FormPro = create(defaultOptions) 144 | 145 | export default FormPro 146 | -------------------------------------------------------------------------------- /src/form-pro/src/__stories__/CustomizedFormControls.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Input, Select } from 'antd' 2 | import * as React from 'react' 3 | import * as styles from './FormPro.module.less' 4 | import FormPro from '../FormPro' 5 | import '../style/' 6 | 7 | const { PureComponent } = React 8 | const { Option } = Select 9 | 10 | class PriceInput extends PureComponent { 11 | static getDerivedStateFromProps(nextProps: any) { 12 | // Should be a controlled component. 13 | if ('value' in nextProps) { 14 | return { 15 | ...(nextProps.value || {}), 16 | } 17 | } 18 | return null 19 | } 20 | 21 | constructor(props: any) { 22 | super(props) 23 | 24 | const value = props.value || {} 25 | this.state = { 26 | number: value.number || 0, 27 | currency: value.currency || 'rmb', 28 | } 29 | } 30 | 31 | handleNumberChange = (e: any) => { 32 | const number = parseInt(e.target.value || 0, 10) 33 | if (Number.isNaN(number)) { 34 | return 35 | } 36 | if (!('value' in this.props)) { 37 | this.setState({ number }) 38 | } 39 | this.triggerChange({ number }) 40 | } 41 | 42 | handleCurrencyChange = (currency: any) => { 43 | if (!('value' in this.props)) { 44 | this.setState({ currency }) 45 | } 46 | this.triggerChange({ currency }) 47 | } 48 | 49 | triggerChange = (changedValue: any) => { 50 | // Should provide an event to pass value to Form. 51 | const { onChange } = this.props 52 | if (onChange) { 53 | onChange(Object.assign({}, this.state, changedValue)) 54 | } 55 | } 56 | 57 | render() { 58 | const { size } = this.props 59 | const { state } = this 60 | return ( 61 | 62 | 69 | 78 | 79 | ) 80 | } 81 | } 82 | 83 | const columns = [ 84 |
Examples
, 85 | { 86 | name: 'price', 87 | required: true, 88 | render: , 89 | }, 90 | ] 91 | 92 | class CustomizedFormControls extends PureComponent { 93 | render() { 94 | const formProps = { 95 | wrapperCol: { span: 14 }, 96 | } 97 | 98 | return ( 99 |
100 | { 104 | console.log(values) 105 | }} 106 | onSubmit={(values: any) => { 107 | console.log(values) 108 | }} 109 | /> 110 |
111 | ) 112 | } 113 | } 114 | 115 | export default CustomizedFormControls 116 | -------------------------------------------------------------------------------- /src/form-pro/src/__stories__/FormPro.module.less: -------------------------------------------------------------------------------- 1 | .container { 2 | max-width: 600px; 3 | } 4 | 5 | .title { 6 | font-size: 18px; 7 | font-weight: 700; 8 | margin-bottom: 16px; 9 | color: #333; 10 | } 11 | -------------------------------------------------------------------------------- /src/form-pro/src/__stories__/FormPro.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Button, Icon } from 'antd' 3 | import { object } from '@storybook/addon-knobs' 4 | import * as styles from './FormPro.module.less' 5 | import { countryList, colorList, cityList, fruitList, treeData } from './data' 6 | import FormPro from '../FormPro' 7 | import '../style/' 8 | 9 | const columns = [ 10 | { 11 | type: 'AutoComplete', 12 | formItemProps: { 13 | dataSource: fruitList, 14 | }, 15 | }, 16 | { 17 | type: 'Checkbox', 18 | formItemProps: { 19 | children: 'Checkbox', 20 | }, 21 | }, 22 | { 23 | type: 'CheckboxGroup', 24 | formItemProps: { 25 | options: fruitList, 26 | }, 27 | }, 28 | { 29 | type: 'DatePicker', 30 | }, 31 | { 32 | type: 'MonthPicker', 33 | }, 34 | { 35 | type: 'RangePicker', 36 | }, 37 | { 38 | type: 'WeekPicker', 39 | }, 40 | { 41 | type: 'Cascader', 42 | formItemProps: { 43 | options: cityList, 44 | }, 45 | }, 46 | { 47 | type: 'Input', 48 | formItemProps: { 49 | placeholder: 'Please input your name', 50 | }, 51 | }, 52 | { 53 | type: 'InputSearch', 54 | formItemProps: { 55 | placeholder: 'Please input search keywords', 56 | }, 57 | }, 58 | { 59 | type: 'InputPassword', 60 | formItemProps: { 61 | placeholder: 'Please input password', 62 | }, 63 | }, 64 | { 65 | type: 'InputTextArea', 66 | }, 67 | { 68 | type: 'InputNumber', 69 | formItemProps: { 70 | min: 1, 71 | max: 10, 72 | }, 73 | }, 74 | { 75 | type: 'Mentions', 76 | extraProps: { 77 | dataSource: countryList.concat(colorList), 78 | }, 79 | }, 80 | { 81 | type: 'Select', 82 | formItemProps: { 83 | placeholder: 'Please select a country', 84 | }, 85 | extraProps: { 86 | dataSource: countryList, 87 | }, 88 | }, 89 | { 90 | type: 'Select', 91 | name: 'SelectMultiple', 92 | label: 'Select[multiple]', 93 | formItemProps: { 94 | placeholder: 'Please select your favourite colors', 95 | mode: 'multiple', 96 | }, 97 | extraProps: { 98 | dataSource: colorList, 99 | }, 100 | }, 101 | { 102 | type: 'Slider', 103 | formItemProps: { 104 | marks: { 105 | 0: 'A', 106 | 20: 'B', 107 | 40: 'C', 108 | 60: 'D', 109 | 80: 'E', 110 | 100: 'F', 111 | }, 112 | }, 113 | }, 114 | { 115 | type: 'Rate', 116 | }, 117 | { 118 | type: 'Radio', 119 | formItemProps: { 120 | children: 'Radio', 121 | }, 122 | }, 123 | { 124 | type: 'RadioGroup', 125 | extraProps: { 126 | dataSource: colorList, 127 | }, 128 | }, 129 | { 130 | type: 'RadioButtonGroup', 131 | extraProps: { 132 | dataSource: countryList, 133 | }, 134 | }, 135 | { 136 | type: 'TreeSelect', 137 | formItemProps: { 138 | treeData, 139 | }, 140 | }, 141 | { 142 | type: 'TimePicker', 143 | }, 144 | { 145 | type: 'Upload', 146 | formItemProps: { 147 | name: 'logo', 148 | action: '/upload.do', 149 | listType: 'picture', 150 | children: ( 151 | 154 | ), 155 | }, 156 | }, 157 | { 158 | type: 'UploadDragger', 159 | formItemProps: { 160 | name: 'logo', 161 | action: '/upload.do', 162 | listType: 'picture', 163 | children: , 164 | }, 165 | }, 166 | // tslint:disable-next-line:ter-arrow-parens 167 | ].map(item => { 168 | item.label = item.label || item.type 169 | item.name = item.name || item.type 170 | return item 171 | }) 172 | 173 | const StandardFormPro = () => { 174 | const formProps = object( 175 | 'formProps', 176 | { 177 | labelCol: { span: 6 }, 178 | wrapperCol: { span: 14 }, 179 | }, 180 | 'formProps', 181 | ) 182 | 183 | return ( 184 |
185 | Examples
, 188 | // tslint:disable-next-line:ter-arrow-parens 189 | ...columns.map(item => { 190 | if (item.type === 'Upload' || item.type === 'UploadDragger') { 191 | return item 192 | } 193 | return object(item.name, item, 'columns') 194 | }), 195 | ]} 196 | formProps={formProps} 197 | onChange={(values: any, changedValues: any) => { 198 | console.log(values, changedValues) 199 | }} 200 | onSubmit={(values: any) => { 201 | console.log(values) 202 | }} 203 | /> 204 | 205 | ) 206 | } 207 | 208 | export default StandardFormPro 209 | -------------------------------------------------------------------------------- /src/form-pro/src/__stories__/WithModal.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Icon, Modal } from 'antd' 2 | import * as React from 'react' 3 | import FormPro from '../FormPro' 4 | import * as styles from './FormPro.module.less' 5 | import '../style/' 6 | 7 | const { Fragment, PureComponent } = React 8 | 9 | const columns = [ 10 | { 11 | name: 'username', 12 | type: 'Input', 13 | required: true, 14 | formItemProps: { 15 | prefix: , 16 | placeholder: 'UserName', 17 | }, 18 | options: { 19 | rules: [ 20 | { 21 | type: 'string', 22 | required: true, 23 | }, 24 | ], 25 | }, 26 | }, 27 | { 28 | name: 'password', 29 | type: 'Input', 30 | required: true, 31 | formItemProps: { 32 | prefix: , 33 | placeholder: 'Password', 34 | }, 35 | options: { 36 | rules: [ 37 | { 38 | type: 'string', 39 | required: true, 40 | }, 41 | ], 42 | }, 43 | }, 44 | ] 45 | 46 | interface IWithModalProps {} 47 | 48 | interface IWithModalState { 49 | visible: boolean 50 | } 51 | 52 | export default class WithModal extends PureComponent< 53 | IWithModalProps, 54 | IWithModalState 55 | > { 56 | formPro: any 57 | 58 | constructor(props: IWithModalProps) { 59 | super(props) 60 | this.state = { 61 | visible: false, 62 | } 63 | } 64 | 65 | handleSubmit = () => { 66 | this.formPro.submit((errors: any, values: any) => { 67 | console.log(errors, values) 68 | }) 69 | } 70 | 71 | render() { 72 | const formProps = { 73 | wrapperCol: { span: 14 }, 74 | } 75 | 76 | return ( 77 | 78 |
79 |
Examples
80 | 90 |
91 | 92 | { 98 | this.setState({ 99 | visible: false, 100 | }) 101 | }} 102 | > 103 | { 108 | this.formPro = ref 109 | }} 110 | /> 111 | 112 |
113 | ) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/form-pro/src/__stories__/data.ts: -------------------------------------------------------------------------------- 1 | export const countryList = [ 2 | { 3 | value: 'China', 4 | label: 'China', 5 | }, 6 | { 7 | value: 'U.S.A', 8 | label: 'U.S.A', 9 | }, 10 | ] 11 | 12 | export const colorList = [ 13 | { 14 | label: 'Red', 15 | value: 'Red', 16 | }, 17 | { 18 | label: 'Green', 19 | value: 'Green', 20 | }, 21 | { 22 | label: 'Blue', 23 | value: 'Blue', 24 | }, 25 | ] 26 | 27 | export const cityList = [ 28 | { 29 | value: 'zhejiang', 30 | label: 'Zhejiang', 31 | children: [ 32 | { 33 | value: 'hangzhou', 34 | label: 'Hangzhou', 35 | children: [ 36 | { 37 | value: 'xihu', 38 | label: 'West Lake', 39 | }, 40 | ], 41 | }, 42 | ], 43 | }, 44 | { 45 | value: 'jiangsu', 46 | label: 'Jiangsu', 47 | children: [ 48 | { 49 | value: 'nanjing', 50 | label: 'Nanjing', 51 | children: [ 52 | { 53 | value: 'zhonghuamen', 54 | label: 'Zhong Hua Men', 55 | }, 56 | ], 57 | }, 58 | ], 59 | }, 60 | ] 61 | 62 | export const fruitList = ['Apple', 'Pear', 'Orange'] 63 | 64 | export const treeData = [ 65 | { 66 | title: 'Node1', 67 | value: '0-0', 68 | key: '0-0', 69 | children: [ 70 | { 71 | title: 'Child Node1', 72 | value: '0-0-1', 73 | key: '0-0-1', 74 | }, 75 | { 76 | title: 'Child Node2', 77 | value: '0-0-2', 78 | key: '0-0-2', 79 | }, 80 | ], 81 | }, 82 | { 83 | title: 'Node2', 84 | value: '0-1', 85 | key: '0-1', 86 | }, 87 | ] 88 | -------------------------------------------------------------------------------- /src/form-pro/src/__stories__/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import { withConsole } from '@storybook/addon-console' 2 | import { storiesOf } from '@storybook/react' 3 | import * as React from 'react' 4 | import StandardFormPro from './FormPro.stories' 5 | import WithModal from './WithModal.stories' 6 | import CustomizedFormControls from './CustomizedFormControls.stories' 7 | import Markdown from '../../../Markdown' 8 | import ReadMe from '../../README.md' 9 | import ReadMeZHCN from '../../README.zh-CN.md' 10 | 11 | storiesOf('COMPONENTS|FormPro', module) 12 | .addDecorator((storyFn: any, context: any) => withConsole()(storyFn)(context)) 13 | .add('Read Me', () => ( 14 | 21 | )) 22 | .add('Read Me (zh-CN)', () => ( 23 | 30 | )) 31 | .add('Standard FormPro', () => ) 32 | .add('With Modal', () => ) 33 | .add('Customized Form Controls', () => ) 34 | -------------------------------------------------------------------------------- /src/form-pro/src/__tests__/FormPro.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { shallow } from 'enzyme' 3 | import FormPro from '../FormPro' 4 | 5 | describe('FormPro', () => { 6 | it('renders', () => { 7 | const wrapper = shallow(OK) 8 | expect(wrapper).toMatchInlineSnapshot('ShallowWrapper {}') 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /src/form-pro/src/create.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import 'antd/es/button/style' 3 | import 'antd/es/form/style' 4 | import Form, { FormItemProps, FormCreateOption, FormProps } from 'antd/es/form' 5 | import { GetFieldDecoratorOptions } from 'antd/es/form/Form' 6 | import Button from 'antd/es/button' 7 | 8 | const FormItem = Form.Item 9 | const { PureComponent, Suspense, Fragment } = React 10 | 11 | export interface IAnyObject { 12 | [propName: string]: any 13 | } 14 | export interface IColumnBase extends FormItemProps { 15 | type?: string 16 | render?: any 17 | name?: string 18 | options?: GetFieldDecoratorOptions 19 | extraProps?: IAnyObject 20 | formItemProps?: object 21 | loading?: React.ReactNode 22 | } 23 | 24 | export type IColumn = IColumnBase | React.ReactNode 25 | export interface IFormProBaseProps { 26 | columns: IColumn[] 27 | formProps?: FormProps 28 | form: any 29 | onChange?: any 30 | onSubmit?: any 31 | showSubmit?: boolean 32 | submitText: React.ReactNode 33 | wrappedComponentRef: any 34 | footer?: React.ReactNode 35 | loading?: React.ReactNode 36 | } 37 | 38 | export interface ICreateFormProItemProps { 39 | type?: string 40 | component?: any 41 | formItemRender?: (itemOptions: any, Component: any) => JSX.Element 42 | } 43 | 44 | export interface IFormProBaseState {} 45 | 46 | const create = ( 47 | options: ICreateFormProItemProps[], 48 | createOptions?: FormCreateOption, 49 | ): any => { 50 | class FormProBase extends PureComponent< 51 | IFormProBaseProps, 52 | IFormProBaseState 53 | > { 54 | static get defaultProps() { 55 | return { 56 | showSubmit: true, 57 | } 58 | } 59 | 60 | isColumn = ( 61 | element: IColumnBase | React.ReactNode, 62 | ): element is IColumnBase => { 63 | return !React.isValidElement(element) 64 | } 65 | 66 | renderFormItem = (item: IColumnBase | React.ReactNode, index: number) => { 67 | if (!this.isColumn(item)) { 68 | return {item} 69 | } 70 | 71 | const { getFieldDecorator } = this.props.form 72 | const { 73 | type, 74 | name, 75 | render, 76 | loading, 77 | extraProps = {}, 78 | formItemProps = {}, 79 | ...restProps 80 | } = item 81 | 82 | const { addonBefore, addonAfter } = extraProps 83 | const itemOptions = options.find(_ => type === _.type) 84 | 85 | let formItemContent 86 | 87 | if (render) { 88 | // customized form controls 89 | formItemContent = React.cloneElement(render, formItemProps) 90 | } else if (itemOptions) { 91 | // built-in form controls 92 | const { formItemRender } = itemOptions 93 | const FormItemComponent = itemOptions.component 94 | formItemContent = formItemRender ? ( 95 | React.cloneElement( 96 | formItemRender(item, FormItemComponent), 97 | formItemProps, 98 | ) 99 | ) : ( 100 | 101 | ) 102 | } else { 103 | return 104 | } 105 | 106 | return ( 107 | 108 | {addonBefore} 109 | 110 | {getFieldDecorator(name, item.options)(formItemContent)} 111 | 112 | {addonAfter} 113 | 114 | ) 115 | } 116 | 117 | renderFooter = () => { 118 | const { 119 | submitText = 'Submit', 120 | showSubmit, 121 | formProps, 122 | footer, 123 | } = this.props 124 | if (footer) { 125 | return footer 126 | } 127 | 128 | if (!showSubmit) { 129 | return null 130 | } 131 | 132 | return ( 133 | } 136 | labelCol={formProps!.labelCol} 137 | wrapperCol={formProps!.wrapperCol} 138 | > 139 | 142 | 143 | ) 144 | } 145 | 146 | handleSubmit = () => { 147 | const { onSubmit, form } = this.props 148 | const { validateFieldsAndScroll } = form 149 | 150 | validateFieldsAndScroll((err: any, values: any) => { 151 | if (!err) { 152 | onSubmit && onSubmit(values) 153 | } 154 | }) 155 | } 156 | 157 | submit = this.props.form.validateFieldsAndScroll 158 | 159 | reset = this.props.form.resetFields 160 | 161 | render() { 162 | const { columns, formProps } = this.props 163 | 164 | return ( 165 |
166 | {columns.map(this.renderFormItem)} 167 | {this.renderFooter()} 168 |
169 | ) 170 | } 171 | } 172 | 173 | return Form.create({ 174 | onValuesChange: (props, changedValues, allValues) => { 175 | props.onChange && props.onChange(allValues, changedValues) 176 | }, 177 | ...createOptions, 178 | })(FormProBase) 179 | } 180 | 181 | export default create 182 | -------------------------------------------------------------------------------- /src/form-pro/src/index.ts: -------------------------------------------------------------------------------- 1 | import FormPro, { defaultOptions } from './FormPro' 2 | import create from './create' 3 | 4 | FormPro.create = create 5 | FormPro.defaultOptions = defaultOptions 6 | 7 | export default FormPro 8 | -------------------------------------------------------------------------------- /src/form-pro/src/style/index.less: -------------------------------------------------------------------------------- 1 | @import 'antd/lib/style/index.less'; 2 | 3 | @import 'antd/lib/auto-complete/style/index.less'; 4 | @import 'antd/lib/checkbox/style/index.less'; 5 | @import 'antd/lib/date-picker/style/index.less'; 6 | @import 'antd/lib/cascader/style/index.less'; 7 | @import 'antd/lib/input/style/index.less'; 8 | @import 'antd/lib/input-number/style/index.less'; 9 | @import 'antd/lib/switch/style/index.less'; 10 | @import 'antd/lib/select/style/index.less'; 11 | @import 'antd/lib/slider/style/index.less'; 12 | @import 'antd/lib/mentions/style/index.less'; 13 | @import 'antd/lib/rate/style/index.less'; 14 | @import 'antd/lib/radio/style/index.less'; 15 | @import 'antd/lib/tree-select/style/index.less'; 16 | @import 'antd/lib/time-picker/style/index.less'; 17 | @import 'antd/lib/upload/style/index.less'; 18 | 19 | @import 'antd/lib/empty/style/index.less'; 20 | @import 'antd/lib/button/style/index.less'; 21 | @import 'antd/lib/tag/style/index.less'; 22 | @import 'antd/lib/tooltip/style/index.less'; 23 | @import 'antd/lib/spin/style/index.less'; 24 | @import 'antd/lib/progress/style/index.less'; 25 | @import 'antd/lib/grid/style/index.less'; 26 | @import 'antd/lib/form/style/index.less'; 27 | -------------------------------------------------------------------------------- /src/form-pro/src/style/index.ts: -------------------------------------------------------------------------------- 1 | import 'antd/lib/auto-complete/style' 2 | import 'antd/lib/checkbox/style' 3 | import 'antd/lib/date-picker/style' 4 | import 'antd/lib/cascader/style' 5 | import 'antd/lib/input/style' 6 | import 'antd/lib/input-number/style' 7 | import 'antd/lib/switch/style' 8 | import 'antd/lib/select/style' 9 | import 'antd/lib/slider/style' 10 | import 'antd/lib/mentions/style' 11 | import 'antd/lib/rate/style' 12 | import 'antd/lib/radio/style' 13 | import 'antd/lib/tree-select/style' 14 | import 'antd/lib/time-picker/style' 15 | import 'antd/lib/upload/style' 16 | import 'antd/lib/form/style' 17 | import 'antd/lib/button/style' 18 | -------------------------------------------------------------------------------- /src/form-pro/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "jsx": "react" 5 | }, 6 | "include": ["src"], 7 | "exclude": ["src/__stories__/*", "src/__tests__/*", "lib", "es"] 8 | } 9 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '@storybook/addon-knobs' { 4 | export const withKnobs: function 5 | export const object: function 6 | } 7 | 8 | declare module '@storybook/addon-console' { 9 | export const withConsole: function 10 | } 11 | 12 | declare namespace NodeJS { 13 | interface ProcessEnv { 14 | readonly NODE_ENV: 'development' | 'production' | 'test' 15 | readonly PUBLIC_URL: string 16 | } 17 | } 18 | 19 | declare module '*.bmp' { 20 | const src: string 21 | export default src 22 | } 23 | 24 | declare module '*.gif' { 25 | const src: string 26 | export default src 27 | } 28 | 29 | declare module '*.jpg' { 30 | const src: string 31 | export default src 32 | } 33 | 34 | declare module '*.jpeg' { 35 | const src: string 36 | export default src 37 | } 38 | 39 | declare module '*.png' { 40 | const src: string 41 | export default src 42 | } 43 | 44 | declare module '*.webp' { 45 | const src: string 46 | export default src 47 | } 48 | 49 | declare module '*.svg' { 50 | import * as React from 'react' 51 | 52 | export const ReactComponent: React.FunctionComponent< 53 | React.SVGProps 54 | > 55 | 56 | const src: string 57 | export default src 58 | } 59 | 60 | declare module '*.module.css' 61 | 62 | declare module '*.module.less' 63 | 64 | declare module '*.md' 65 | -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | import Enzyme from 'enzyme' 2 | import Adapter from 'enzyme-adapter-react-16' 3 | 4 | Enzyme.configure({ adapter: new Adapter() }) 5 | -------------------------------------------------------------------------------- /src/tsconfig.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "skipLibCheck": true, 6 | "esModuleInterop": true, 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true, 9 | "strictNullChecks": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "moduleResolution": "node", 12 | "resolveJsonModule": true, 13 | "experimentalDecorators": true, 14 | "noUnusedParameters": true, 15 | "noUnusedLocals": true, 16 | "noImplicitAny": true, 17 | "skipDefaultLibCheck":true, 18 | "jsx": "preserve", 19 | } 20 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["es5", "es6", "es7", "es2017", "dom"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "module": "commonjs", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "experimentalDecorators": true, 15 | "noUnusedParameters": true, 16 | "noUnusedLocals": true, 17 | "noImplicitAny": true, 18 | "isolatedModules": false, 19 | "noEmit": true, 20 | "skipDefaultLibCheck":true, 21 | "jsx": "react" 22 | }, 23 | "include": ["src"], 24 | "exclude": ["src/setupTests.js"] 25 | } 26 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint-config-airbnb", 3 | "rules": { 4 | "semicolon": [true, "never"], 5 | "variable-name": [true, "ban-keywords", "check-format", "allow-pascal-case"], 6 | "import-name": false 7 | } 8 | } 9 | --------------------------------------------------------------------------------