├── .eslintignore
├── .eslintrc.cjs
├── .github
└── workflows
│ └── gh-pages.yml
├── .gitignore
├── .prettierignore
├── .prettierrc.cjs
├── README.md
├── dumi
├── .umi-production
│ └── umi.ts
├── config
│ └── config.js
├── docs
│ ├── Button.md
│ └── index.md
└── public
│ └── images
│ └── boty-icon.jpg
├── package.json
├── packages
├── components
│ ├── README.md
│ ├── __tests__
│ │ └── components.spec.tsx
│ ├── package.json
│ ├── src
│ │ ├── Button
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ ├── Modal
│ │ │ ├── REAMDME.md
│ │ │ ├── __tests__
│ │ │ │ └── index.spec.tsx
│ │ │ ├── hooks.tsx
│ │ │ ├── index.less
│ │ │ ├── index.tsx
│ │ │ └── type.ts
│ │ ├── Select
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ ├── assets
│ │ │ └── css
│ │ │ │ ├── index.less
│ │ │ │ ├── reset.less
│ │ │ │ └── vars.less
│ │ ├── config
│ │ │ ├── provider.js
│ │ │ └── provider.ts
│ │ └── index.ts
│ └── vite.config.ts
├── examples
│ ├── index.html
│ ├── package.json
│ ├── src
│ │ ├── App.tsx
│ │ └── main.tsx
│ └── vite.config.ts
├── form-generator
│ ├── .gitignore
│ ├── README.md
│ ├── __tests__
│ │ └── form-generator.test.js
│ ├── package.json
│ ├── src
│ │ ├── components
│ │ │ ├── Field.tsx
│ │ │ └── Form.tsx
│ │ ├── hooks
│ │ │ ├── useFormContext.ts
│ │ │ ├── useFormItem.ts
│ │ │ └── useValidator.ts
│ │ ├── index.ts
│ │ ├── internals
│ │ │ ├── context.ts
│ │ │ ├── symbols.ts
│ │ │ └── transaction.ts
│ │ └── types.ts
│ └── tsconfig.json
├── icons
│ ├── README.md
│ ├── __tests__
│ │ └── icons.test.js
│ ├── lib
│ │ └── icons.js
│ └── package.json
├── scripts
│ ├── README.md
│ ├── __tests__
│ │ └── scripts.test.js
│ ├── generator.js
│ ├── package.json
│ └── templates
│ │ └── components
│ │ ├── Templatecomponent.js
│ │ └── template-component.css
├── themes
│ ├── README.md
│ ├── __tests__
│ │ └── themes.test.js
│ ├── default.css
│ └── package.json
└── utils
│ ├── README.md
│ ├── __tests__
│ └── utils.test.js
│ ├── package.json
│ └── src
│ ├── index.ts
│ ├── isPromise.ts
│ ├── merge.ts
│ └── usePrevious.ts
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── tsconfig.json
└── webpack.config.js
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | dist/
3 |
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | node: true
5 | },
6 | extends: [
7 | 'eslint:recommended',
8 | 'plugin:react/recommended',
9 | 'plugin:@typescript-eslint/recommended',
10 | 'prettier',
11 | 'plugin:prettier/recommended'
12 | ],
13 | parser: '@typescript-eslint/parser',
14 | parserOptions: {
15 | ecmaFeatures: {
16 | jsx: true
17 | },
18 | ecmaVersion: 'latest',
19 | sourceType: 'module'
20 | },
21 | plugins: ['react', '@typescript-eslint', 'prettier'],
22 | rules: {
23 | 'prettier/prettier': 'error',
24 | quotes: ['error', 'single'],
25 | semi: ['error', 'always'],
26 | 'react/react-in-jsx-scope': 'off'
27 | },
28 | settings: {
29 | react: {
30 | version: 'detect'
31 | }
32 | }
33 | };
34 |
--------------------------------------------------------------------------------
/.github/workflows/gh-pages.yml:
--------------------------------------------------------------------------------
1 | name: github pages
2 |
3 | on:
4 | push:
5 | branches:
6 | - master # default branch
7 |
8 | jobs:
9 | deploy:
10 | runs-on: ubuntu-18.04
11 | steps:
12 | - uses: actions/checkout@v2
13 | - run: yarn install
14 | - run: yarn run doc:build
15 | - name: Deploy
16 | uses: peaceiris/actions-gh-pages@v3
17 | with:
18 | github_token: ${{ secrets.GITHUB_TOKEN }}
19 | publish_dir: ./dumi/dist
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | logs
2 | *.log
3 | npm-debug.log*
4 | yarn-debug.log*
5 | yarn-error.log*
6 | package-lock.json
7 | yarn.lock
8 | node_modules/
9 | typings/
10 | *.tsbuildinfo
11 | .npm
12 | .eslintcache
13 | .env
14 | .env.test
15 | .DS_Store
16 | .vscode
17 | .idea
18 | .umi
19 |
20 | dumi/.umi/
21 | dumi/.umi-production/
22 | dumi/dist/
23 |
24 | dist/
25 | lib/
26 |
27 | pnpm-lock.yaml
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | dist/
3 |
--------------------------------------------------------------------------------
/.prettierrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | printWidth: 80, //一行的字符数,如果超过会进行换行,默认为80
3 | tabWidth: 2, // 一个 tab 代表几个空格数,默认为 2 个
4 | useTabs: false, //是否使用 tab 进行缩进,默认为false,表示用空格进行缩减
5 | singleQuote: true, // 字符串是否使用单引号,默认为 false,使用双引号
6 | semi: true, // 行尾是否使用分号,默认为true
7 | trailingComma: 'none', // 是否使用尾逗号
8 | bracketSpacing: true // 对象大括号直接是否有空格,默认为 true,效果:{ a: 1 }
9 | };
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # boty-design
2 |
3 |
4 |
5 |
6 | 从零打造一套企业级react 组件库生态
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | ## 文档
22 |
23 | [中文文档](https://boty-design.github.io/boty-design/)
24 |
25 | ## 安装
26 | ```jsx
27 | $ npm install @boty-design/components --save
28 |
29 | $ yarn add @boty-design/components
30 |
31 | $ pnpm i @boty-design/components
32 |
33 | ```
34 |
35 | ## 使用
36 | ```jsx
37 | import { Button } from "@boty-design/components";
38 |
39 | const App = () => (
40 |
43 | );
44 | ```
45 |
46 | ## 技术
47 |
48 | - 框架支持【react】 [https://zh-hans.reactjs.org/](https://zh-hans.reactjs.org/)
49 | - 组件学习【ant design】 [https://ant.design/index-cn](https://ant.design/index-cn)
50 | - 本地开发【vite】 [https://cn.vitejs.dev/](https://cn.vitejs.dev/)
51 | - 项目文档【umi文档】 [https://d.umijs.org/zh-CN](https://d.umijs.org/zh-CN)
52 | - 持续集成【Travis CI】 [https://travis-ci.org](https://travis-ci.org/)
53 | - 持续部署【 github Aciton】 [https://docs.github.com/cn/actions](https://docs.github.com/cn/actions)
54 | - npm管理【lerna】[https://github.com/lerna/lerna](https://github.com/lerna/lerna)
55 | - 组件生成【plop】[https://github.com/plopjs/plop](https://github.com/plopjs/plop)
56 |
57 |
58 | ## 待做清单
59 |
60 | ✅ 已完成
61 | ⭐️ 完善中
62 | ❌ 未开始
63 | ### 通用
64 | - Button 按钮 ⭐️
65 | - Layout 布局 ❌
66 | - Icon 图标 ❌
67 |
68 | ### 表单
69 | - Form 表单组 ⭐️
70 | - Field 表单单位 ⭐️
71 | - Input 输入框 ❌
72 | - Radio单选框 ❌
73 | - Switch开关 ❌
74 | - Checkbox多选框 ❌
75 | - Select 选择器 ❌
76 | - Slider 滑块 ❌
77 | - TimePicker 时间选择器 ❌
78 | - DatePicker 日期选择器 ❌
79 | - DateTimePicker 日期时间选择器 ❌
80 | - Upload 上传 ❌
81 |
82 |
83 | ### 数据
84 | - Table 表格 ❌
85 | - tree 树 ❌
86 | - Tag 标签 ❌
87 | - Progress 进度条 ❌
88 | - Pagination 分页 ❌
89 | - Badge 标记 ❌
90 |
91 | ### 交互
92 |
93 | - Dialog 对话框 ❌
94 | - MessageBox 弹框 ❌
95 | - Drawer 抽屉 ❌
96 | - Calendar 日历 ❌
97 | - Carousel 轮播走马灯 ❌
98 |
99 |
100 | ### 反馈
101 |
102 | - Message 消息提示 ❌
103 | - loading 加载 ❌
104 | - Notification 通知 ❌
105 |
106 | ## LICENSE
107 |
108 | [MIT](https://en.wikipedia.org/wiki/MIT_License)
109 |
110 |
111 |
--------------------------------------------------------------------------------
/dumi/.umi-production/umi.ts:
--------------------------------------------------------------------------------
1 | // @ts-nocheck
2 | import './core/polyfill';
3 |
4 | import { plugin } from './core/plugin';
5 | import './core/pluginRegister';
6 | import { createHistory } from './core/history';
7 | import { ApplyPluginsType } from 'D:/self_git/vite-react-boty-desing/node_modules/umi/node_modules/@umijs/runtime';
8 | import { renderClient } from 'D:/self_git/vite-react-boty-desing/node_modules/umi/node_modules/@umijs/renderer-react/dist/index.js';
9 | import { getRoutes } from './core/routes';
10 |
11 |
12 |
13 |
14 | const getClientRender = (args: { hot?: boolean; routes?: any[] } = {}) => plugin.applyPlugins({
15 | key: 'render',
16 | type: ApplyPluginsType.compose,
17 | initialValue: () => {
18 | const opts = plugin.applyPlugins({
19 | key: 'modifyClientRenderOpts',
20 | type: ApplyPluginsType.modify,
21 | initialValue: {
22 | routes: args.routes || getRoutes(),
23 | plugin,
24 | history: createHistory(args.hot),
25 | isServer: process.env.__IS_SERVER,
26 | rootElement: 'root',
27 | defaultTitle: ``,
28 | },
29 | });
30 | return renderClient(opts);
31 | },
32 | args,
33 | });
34 |
35 | const clientRender = getClientRender();
36 | export default clientRender();
37 |
38 |
39 | window.g_umi = {
40 | version: '3.3.9',
41 | };
42 |
43 |
44 | // hot module replacement
45 | // @ts-ignore
46 | if (module.hot) {
47 | // @ts-ignore
48 | module.hot.accept('./core/routes', () => {
49 | const ret = require('./core/routes');
50 | if (ret.then) {
51 | ret.then(({ getRoutes }) => {
52 | getClientRender({ hot: true, routes: getRoutes() })();
53 | });
54 | } else {
55 | getClientRender({ hot: true, routes: ret.getRoutes() })();
56 | }
57 | });
58 | }
59 |
--------------------------------------------------------------------------------
/dumi/config/config.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Cookie
3 | * @Date: 2021-03-03 13:15:14
4 | * @LastEditors: Cookie
5 | * @LastEditTime: 2021-03-05 17:15:42
6 | * @Description:
7 | */
8 | import { join } from 'path';
9 |
10 | export default {
11 | base: '/boty-design',
12 | publicPath: '/boty-design/',
13 | chainWebpack(memo) {
14 | memo.module
15 | .rule('js')
16 | .test(/\.(js|mjs|jsx|ts|tsx)$/)
17 | .include.add(join(__dirname, '..', '..', 'packages/components/src'))
18 | .end()
19 | .exclude.add(/node_modules/)
20 | .end()
21 | .use('babel-loader');
22 | },
23 | alias: {
24 | '@boty-design/components': join(
25 | __dirname,
26 | '..',
27 | '..',
28 | 'packages/components/src'
29 | ),
30 | },
31 | exportStatic: {},
32 | logo: 'https://avatars.githubusercontent.com/u/79920730',
33 | };
34 |
--------------------------------------------------------------------------------
/dumi/docs/Button.md:
--------------------------------------------------------------------------------
1 |
8 |
9 | ---
10 |
11 | ## Button
12 |
13 | ```jsx
14 | /**
15 | * title: 普通配置
16 | * desc: 参考 antd
17 | */
18 | import React from 'react';
19 | import Button from '@boty-design/components/Button';
20 | export default () => ;
21 | ```
22 |
23 | ```jsx
24 | /**
25 | * title: loading
26 | * desc: 单loading属性存在时,会将点击方法视为异步,用 await 承接
27 | */
28 |
29 | import React from 'react';
30 | import Button from '@boty-design/components/Button';
31 | const handlerSyncClick = () => {
32 | return new Promise((resolve) => {
33 | setTimeout(() => {
34 | alert('回调结束');
35 | resolve(true);
36 | }, 1000);
37 | });
38 | };
39 | export default () => (
40 |
43 | );
44 | ```
45 |
46 |
47 |
--------------------------------------------------------------------------------
/dumi/docs/index.md:
--------------------------------------------------------------------------------
1 |
8 |
9 | # 快速开始
10 |
11 |
12 |
13 |
14 | 从零打造一套企业级react 组件库生态
15 |
16 |
17 | ## 安装
18 | ```jsx| pure
19 | $ npm install boty-design --save
20 |
21 | $ yarn add boty-design
22 | ```
23 |
24 | ## 使用
25 | ```jsx| pure
26 | import { Button } from "boty-design";
27 |
28 | const App = () => (
29 |
32 | );
33 | ```
34 |
35 | ## 技术
36 |
37 | - 框架支持【react】 [https://zh-hans.reactjs.org/](https://zh-hans.reactjs.org/)
38 | - 组件学习【ant design】 [https://ant.design/index-cn](https://ant.design/index-cn)
39 | - 本地开发【vite】 [https://cn.vitejs.dev/](https://cn.vitejs.dev/)
40 | - 项目文档【umi文档】 [https://d.umijs.org/zh-CN](https://d.umijs.org/zh-CN)
41 | - 持续集成【Travis CI】 [https://travis-ci.org](https://travis-ci.org/)
42 | - 持续部署【 github Aciton】 [https://docs.github.com/cn/actions](https://docs.github.com/cn/actions)
43 |
44 |
45 |
46 | ## LICENSE
47 |
48 | [MIT](https://en.wikipedia.org/wiki/MIT_License)
49 |
50 |
51 |
--------------------------------------------------------------------------------
/dumi/public/images/boty-icon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ignition-Space/boty-design/e55d4dda0642112aa2d689e9f081998ff27d8820/dumi/public/images/boty-icon.jpg
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "boty-design",
3 | "private": true,
4 | "files": [
5 | "dist"
6 | ],
7 | "scripts": {
8 | "dev": "pnpm --filter @boty-design/examples dev",
9 | "build": "pnpm --filter @boty-design/ build",
10 | "doc": "cross-env APP_ROOT=dumi dumi dev",
11 | "doc:build": "cross-env APP_ROOT=dumi dumi build",
12 | "generator": "lerna exec --scope \"@boty-design/scripts\" yarn generator",
13 | "lint": "eslint --ext .js,.jsx,.ts,.tsx --fix --quiet ./"
14 | },
15 | "devDependencies": {
16 | "@testing-library/react": "^13.4.0",
17 | "@types/classnames": "^2.3.0",
18 | "@types/node": "^18.7.18",
19 | "@types/react": "^18.0.21",
20 | "@types/react-dom": "^18.0.6",
21 | "@typescript-eslint/eslint-plugin": "^5.40.0",
22 | "@typescript-eslint/parser": "^5.40.0",
23 | "@vitejs/plugin-react": "^2.1.0",
24 | "@vitejs/plugin-react-refresh": "^1.1.0",
25 | "classes": "^0.3.0",
26 | "classnames": "^2.3.2",
27 | "cross-env": "^7.0.3",
28 | "dumi": "^1.1.47",
29 | "eslint": "^8.25.0",
30 | "eslint-config-prettier": "^8.5.0",
31 | "eslint-plugin-prettier": "^4.2.1",
32 | "eslint-plugin-react": "^7.31.10",
33 | "happy-dom": "^7.4.0",
34 | "lerna": "^5.5.2",
35 | "less": "^4.1.3",
36 | "prettier": "^2.7.1",
37 | "typescript": "^4.8.3",
38 | "vite": "^3.1.3",
39 | "vite-plugin-eslint": "^1.8.1",
40 | "vitest": "^0.24.1"
41 | },
42 | "workspaces": [
43 | "packages/*"
44 | ],
45 | "dependencies": {
46 | "@ant-design/icons": "^4.7.0",
47 | "react": "^18.2.0",
48 | "react-dom": "^18.2.0"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/packages/components/README.md:
--------------------------------------------------------------------------------
1 | # `boty-design/components`
2 |
3 | > TODO: description
4 |
5 | ## Usage
6 |
7 | ```
8 | const components = require('components');
9 |
10 | // TODO: DEMONSTRATE API
11 | ```
12 |
--------------------------------------------------------------------------------
/packages/components/__tests__/components.spec.tsx:
--------------------------------------------------------------------------------
1 | import { describe, test } from 'vitest';
2 | import { render } from '@testing-library/react';
3 | import { Button } from '../src/index';
4 |
5 | describe('components test', () => {
6 | test('button test', () => {
7 | render();
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/packages/components/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@boty-design/components",
3 | "version": "0.0.1",
4 | "description": "> TODO: description",
5 | "author": "boty",
6 | "homepage": "https://github.com/boty-design/boty-design#readme",
7 | "license": "ISC",
8 | "main": "src/index.ts",
9 | "directories": {
10 | "lib": "lib",
11 | "test": "__tests__"
12 | },
13 | "files": [
14 | "lib"
15 | ],
16 | "publishConfig": {
17 | "access": "public"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/boty-design/boty-design.git"
22 | },
23 | "scripts": {
24 | "dev": "vite",
25 | "build": "tsc && vite build",
26 | "test": "vitest",
27 | "prepublish": "yarn build"
28 | },
29 | "bugs": {
30 | "url": "https://github.com/boty-design/boty-design/issues"
31 | },
32 | "dependencies": {
33 | "@ant-design/icons": "^4.5.0",
34 | "@boty-design/utils": "workspace:*"
35 | },
36 | "gitHead": "e672c3c1d4971d8b81c231c2adc79eca30db5ed8",
37 | "devDependencies": {
38 | "@testing-library/react": "^13.4.0",
39 | "happy-dom": "^7.4.0",
40 | "vite-plugin-eslint": "^1.8.1",
41 | "vitest": "^0.24.1"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/packages/components/src/Button/index.less:
--------------------------------------------------------------------------------
1 | @import '../assets/css/index.less';
2 | .boty-btn {
3 | min-width: 88px;
4 | height: 32px;
5 | padding: 4px 15px;
6 | font-size: @default-font-size;
7 | cursor: pointer;
8 | text-align: center;
9 | border: 1px solid #d9d9d9;
10 | display: inline-block;
11 | font-weight: @font-weight-normal;
12 | white-space: nowrap;
13 | user-select: none;
14 | background: #fff;
15 | border-radius: @default-fillet;
16 | touch-action: manipulation;
17 | outline: none;
18 | position: relative;
19 | box-sizing: border-box;
20 | margin: 0 5px;
21 |
22 | .boty-btn-icon {
23 | font-size: @default-font-size;
24 |
25 | &.boty-btn-icon-lg {
26 | font-size: @large-font-size;
27 | }
28 |
29 | &.boty-btn-icon-sm {
30 | font-size: @default-font-size;
31 | }
32 | }
33 |
34 | &.boty-btn-icon-only {
35 | height: 32px;
36 | width: 32px;
37 | padding: 2.4px 0;
38 | font-size: @large-font-size;
39 | }
40 |
41 | &.boty-btn-lg {
42 | height: 40px;
43 | padding: 6.4px 15px;
44 | font-size: @large-font-size;
45 | }
46 |
47 | &.boty-btn-sm {
48 | height: 24px;
49 | padding: 0 7px;
50 | font-size: @default-font-size;
51 | }
52 |
53 | &.boty-btn-circle {
54 | min-width: 32px;
55 | width: 32px;
56 | height: 32px;
57 | border-radius: 50%;
58 | }
59 |
60 | &.boty-btn-color-primary {
61 | background-color: @primary;
62 | color: @white;
63 | }
64 | &.boty-btn-color-info {
65 | background-color: @info;
66 | color: @white;
67 | }
68 | &.boty-btn-color-success {
69 | background-color: @success;
70 | color: @white;
71 | }
72 | &.boty-btn-color-error {
73 | background-color: @error;
74 | color: @white;
75 | }
76 | &.boty-btn-color-warning {
77 | background-color: @warning;
78 | color: @white;
79 | }
80 | &.boty-btn-has-icon{
81 | .anticon{
82 | margin-right: 5px;
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/packages/components/src/Button/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import classNames from 'classnames';
3 | import { LoadingOutlined } from '@ant-design/icons';
4 | import { tuple } from '@boty-design/utils';
5 | import { getPrefixCls } from '../config/provider';
6 |
7 | import './index.less';
8 |
9 | const ButtonTypes = tuple(
10 | 'default',
11 | 'primary',
12 | 'ghost',
13 | 'dashed',
14 | 'link',
15 | 'text'
16 | );
17 | export type ButtonType = typeof ButtonTypes[number];
18 |
19 | const ButtonShapes = tuple('circle', 'round');
20 | export type ButtonShape = typeof ButtonShapes[number];
21 |
22 | const ButtonHTMLTypes = tuple('submit', 'button', 'reset');
23 | export type ButtonHTMLType = typeof ButtonHTMLTypes[number];
24 |
25 | export type SizeType = 'small' | 'middle' | 'large' | undefined;
26 |
27 | const colorSchemes = tuple("primary", "success", "error", "warning");
28 | export type colorScheme = typeof colorSchemes[number];
29 |
30 | interface BaseProps {
31 | /**
32 | * @description 自定义样式名
33 | */
34 | className?: string;
35 | /**
36 | * @description 自定义样式
37 | */
38 | style?: React.HTMLProps;
39 | }
40 |
41 | interface BaseButtonProps {
42 | /**
43 | * @description 按钮类型
44 | */
45 | type?: ButtonType;
46 | /**
47 | * @description 图标组件
48 | */
49 | icon?: React.ReactNode;
50 | /**
51 | * @description 形状
52 | */
53 | shape?: ButtonShape;
54 | /**
55 | * @description 大小
56 | */
57 | size?: SizeType;
58 | /**
59 | * @description loading状态,设置之后会执行异步方法
60 | */
61 | loading?: boolean;
62 | /**
63 | * @description 样式前缀
64 | */
65 | prefixCls?: string;
66 | /**
67 | * @description 按钮颜色
68 | */
69 | colorSchemes?: string;
70 | /**
71 | * @description 危险类型
72 | */
73 | danger?: boolean;
74 | /**
75 | * @description 按钮原生类型
76 | */
77 | htmlType?: ButtonHTMLType;
78 | children?: React.ReactNode;
79 | }
80 |
81 |
82 | interface INativeButtonProps {
83 | /**
84 | * @description 是否生效
85 | */
86 | disabled?: boolean;
87 | /**
88 | * @description 点击方法
89 | */
90 | onClick?: React.MouseEventHandler;
91 | }
92 |
93 | export type IButtonProps = BaseProps & BaseButtonProps & INativeButtonProps;
94 |
95 | type Loading = number | boolean;
96 |
97 | const Button = (props: IButtonProps) => {
98 | const {
99 | htmlType = 'button' as IButtonProps['htmlType'],
100 | prefixCls,
101 | type,
102 | shape,
103 | size: customizeSize,
104 | className,
105 | colorSchemes,
106 | children,
107 | loading,
108 | style: customStyle,
109 | icon,
110 | } = props;
111 |
112 | const [innerLoading, setLoading] = useState(false);
113 |
114 | let sizeCls = '';
115 |
116 | switch (customizeSize) {
117 | case 'large':
118 | sizeCls = 'lg';
119 | break;
120 | case 'small':
121 | sizeCls = 'sm';
122 | break;
123 | default:
124 | break;
125 | }
126 |
127 | const handleClick = async (
128 | e: React.MouseEvent
129 | ) => {
130 | const { onClick } = props;
131 | if (innerLoading) return;
132 | if (loading) {
133 | setLoading(true);
134 | await onClick?.(e);
135 | setLoading(false);
136 | } else {
137 | onClick?.(e);
138 | }
139 | };
140 |
141 | const selfPrefixCls = getPrefixCls(prefixCls || 'btn');
142 |
143 | const iconType = innerLoading ? 'loading' : icon;
144 |
145 | const iconPrefixCls = getPrefixCls('btn-icon');
146 |
147 | const iconClasses = classNames(
148 | iconPrefixCls,
149 | {
150 | [`${iconPrefixCls}-${sizeCls}`]: sizeCls,
151 | },
152 | className
153 | );
154 |
155 | const LoadingNode = () => {
156 | if (icon) return icon;
157 | return innerLoading && ;
158 | };
159 |
160 | const childrenNode = children || null;
161 |
162 | const classes = classNames(
163 | selfPrefixCls,
164 | {
165 | [`${selfPrefixCls}-${type}`]: type,
166 | [`${selfPrefixCls}-${shape}`]: shape,
167 | [`${selfPrefixCls}-${sizeCls}`]: sizeCls,
168 | [`${selfPrefixCls}-icon-only`]: !children && children !== 0 && iconType,
169 | [`${selfPrefixCls}-color-${colorSchemes}`]: colorSchemes,
170 | },
171 | className,
172 | LoadingNode() && childrenNode ? `${selfPrefixCls}-has-icon` : ''
173 | );
174 |
175 |
176 |
177 |
178 | return (
179 |
188 | );
189 | };
190 |
191 | export default Button;
192 |
--------------------------------------------------------------------------------
/packages/components/src/Modal/REAMDME.md:
--------------------------------------------------------------------------------
1 | # Modal
2 |
3 | ## 已完成功能
4 | | 参数 | 说明 | 类型 | 默认值 | 版本 |
5 | | - | - | - | - | - |
6 | - [ ] afterClose Modal 完全关闭后的回调 function -
7 | - [x] bodyStyle Modal body 样式 CSSProperties
8 | - [x] cancelButtonProps cancel 按钮 props ButtonProps -
9 | - [x] cancelText 取消按钮文字 ReactNode 取消
10 | - [ ] centered 垂直居中展示 Modal boolean false
11 | - [x] closable 是否显示右上角的关闭按钮 boolean true
12 | - [ ] closeIcon 自定义关闭图标 ReactNode ``
13 | - [x] confirmLoading 确定按钮 loading boolean false
14 | - [ ] destroyOnClose 关闭时销毁 Modal 里的子元素 boolean false
15 | - [ ] focusTriggerAfterClose 对话框关闭后是否需要聚焦触发元素 boolean true
16 | - [x] footer 底部内容,当不需要默认底部按钮时,可以设为 footer={null} ReactNode (确定取消按钮)
17 | - [ ] forceRender 强制渲染 Modal boolean false
18 | - [x] getContainer 指定 Modal 挂载的节点,但依旧为全局展示,false 为挂载在当前位置 HTMLElement | () => HTMLElement | Selectors | false document.body
19 | - [ ] keyboard 是否支持键盘 esc 关闭 boolean true
20 | - [x] mask 是否展示遮罩 boolean true
21 | - [x] maskClosable 点击蒙层是否允许关闭 boolean true
22 | - [x] maskStyle 遮罩样式 CSSProperties
23 | - [ ] modalRender 自定义渲染对话框 (node: ReactNode) => ReactNode
24 | - [x] okButtonProps ok 按钮 props ButtonProps -
25 | - [x] okText 确认按钮文字 ReactNode 确定
26 | - [x] okType 确认按钮类型 string primary
27 | - [x] style 可用于设置浮层的样式,调整浮层位置等 CSSProperties -
28 | - [x] title 标题 ReactNode -
29 | - [x] open 对话框是否可见
30 | - [x] width 宽度 string | number 520
31 | - [x] wrapClassName 对话框外层容器的类名 string -
32 | - [x] zIndex 设置 Modal 的 z-index number 1000
33 | - [x] onCancel 点击遮罩层或右上角叉或取消按钮的回调 function(e) -
34 | - [x] onOk 点击确定回调 function(e) -
--------------------------------------------------------------------------------
/packages/components/src/Modal/__tests__/index.spec.tsx:
--------------------------------------------------------------------------------
1 | import Modal from '..';
2 | import { render, screen } from '@testing-library/react';
3 | import { describe, test } from 'vitest';
4 |
5 | describe('modal test', () => {
6 | test('renders modal component', () => {
7 | render(内容);
8 | screen.debug();
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/packages/components/src/Modal/hooks.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef } from 'react';
2 |
3 | import type { ModalProps } from './type';
4 |
5 | export const useModal = (props: ModalProps) => {
6 | const {
7 | open,
8 | keyboard = true,
9 |
10 | onCancel,
11 | onOk
12 | } = props;
13 |
14 | // doto auto focus for handleKeyDown
15 | const modalRef = useRef();
16 |
17 | useEffect(() => {
18 | modalRef.current.style.display = open ? 'block' : 'none';
19 | }, [open]);
20 |
21 | /** 键盘 esc 关闭 */
22 | const handleKeyDown = (e: React.KeyboardEvent) => {
23 | if (!keyboard) return;
24 | if (e.key === 'Escape') {
25 | e.stopPropagation();
26 | onCancel?.();
27 | }
28 | };
29 |
30 | /** modal 关闭 */
31 | const handleClose = () => {
32 | onCancel?.();
33 | };
34 |
35 | /** modal 确定 */
36 | const handleOk = () => {
37 | onOk?.();
38 | };
39 |
40 | return {
41 | modalRef,
42 | handleKeyDown,
43 | handleClose,
44 | handleOk
45 | };
46 | };
47 |
--------------------------------------------------------------------------------
/packages/components/src/Modal/index.less:
--------------------------------------------------------------------------------
1 | @import '../assets/css/index.less';
2 | .boty-modal {
3 | &-root {
4 | }
5 |
6 | &-close {
7 | position: absolute;
8 | right: 0;
9 | top: 0;
10 | width: 54px;
11 | height: 54px;
12 | text-align: center;
13 | line-height: 54px;
14 | }
15 |
16 | &-mask {
17 | position: fixed;
18 | top: 0;
19 | right: 0;
20 | bottom: 0;
21 | left: 0;
22 | z-index: 1000;
23 | height: 100%;
24 | opacity: 0.45;
25 | background-color: @black;
26 | }
27 |
28 | &-wapper {
29 | position: fixed;
30 | right: 0;
31 | left: 0;
32 | outline: 0;
33 | z-index: 1000;
34 | }
35 |
36 | &-container {
37 | position: relative;
38 | max-width: calc(100vw - 16px);
39 | margin: 8px auto;
40 | background-color: @white;
41 | background-clip: padding-box;
42 | border: 0;
43 | border-radius: @default-fillet;
44 | box-shadow: 0 3px 6px -4px rgb(0 0 0 / 12%), 0 6px 16px 0 rgb(0 0 0 / 8%),
45 | 0 9px 28px 8px rgb(0 0 0 / 5%);
46 | }
47 |
48 | &-header {
49 | padding: 16px 24px;
50 | color: rgba(0, 0, 0, 0.85);
51 | background: @white;
52 | border-bottom: 1px solid #f0f0f0;
53 | border-radius: @default-fillet @default-fillet 0 0;
54 | }
55 |
56 | &-body {
57 | padding: 24px;
58 | line-height: 1.5;
59 | word-wrap: break-word;
60 | }
61 |
62 | &-footer {
63 | padding: 10px 16px;
64 | text-align: right;
65 | background: transparent;
66 | border-top: 1px solid @gray100;
67 | border-radius: 0 0 @default-fillet @default-fillet;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/packages/components/src/Modal/index.tsx:
--------------------------------------------------------------------------------
1 | import { createPortal } from 'react-dom';
2 | import classnames from 'classnames';
3 |
4 | import { getPrefixCls } from '../config/provider';
5 | import Button from '../Button';
6 | import { useModal } from './hooks';
7 |
8 | import type { FC, PropsWithChildren } from 'react';
9 | import type { ModalProps } from './type';
10 |
11 | import './index.less';
12 |
13 | const Modal: FC> = (props) => {
14 | const {
15 | className,
16 | wrapClassName,
17 | style,
18 |
19 | closable = true,
20 | mask = true,
21 | maskClosable = true,
22 | maskStyle,
23 |
24 | getContainer = document.body,
25 | bodyStyle,
26 | zIndex = 1000,
27 | width = 520,
28 |
29 | title,
30 | footer = true,
31 | cancelText = '取消',
32 | cancelButtonProps,
33 | okText = '确定',
34 | okButtonProps,
35 | confirmLoading,
36 | children
37 | } = props;
38 |
39 | const { modalRef, handleClose, handleOk, handleKeyDown } = useModal(props);
40 |
41 | const selfPrefixCls = getPrefixCls('modal');
42 |
43 | const rootClass = classnames(className, `${selfPrefixCls}-root`);
44 | const wapperClass = classnames(wrapClassName, `${selfPrefixCls}-wapper`);
45 |
46 | const DefaultFooter = () => (
47 | <>
48 |
51 |
54 | >
55 | );
56 |
57 | const ModalDOM = (
58 |
64 | {mask && (
65 |
70 | )}
71 |
72 |
76 | {closable ? (
77 |
78 | X
79 |
80 | ) : null}
81 |
82 | {title ? (
83 |
{title}
84 | ) : null}
85 |
86 |
{children}
87 | {footer ? (
88 |
89 | {footer === true ? : footer}
90 |
91 | ) : null}
92 |
93 |
94 |
95 | );
96 |
97 | const attach =
98 | typeof getContainer === 'function' ? getContainer() : getContainer;
99 |
100 | return attach ? createPortal(ModalDOM, attach) : ModalDOM;
101 | };
102 |
103 | export default Modal;
104 |
--------------------------------------------------------------------------------
/packages/components/src/Modal/type.ts:
--------------------------------------------------------------------------------
1 | import type { CSSProperties, ReactNode } from 'react';
2 | import type { IButtonProps } from '../Button';
3 |
4 | type BaseProps = {
5 | /** 对话框外层容器的类名 */
6 | className?: string;
7 |
8 | /** 对话框外层容器的自定义样式 */
9 | style?: React.HTMLProps;
10 | };
11 |
12 | export type ModalProps = {
13 | /** 对话框外层容器的类名 */
14 | wrapClassName?: string;
15 |
16 | /** 指定 Modal 挂载的节点,但依旧为全局展示,false 为挂载在当前位置, 默认为document.body */
17 | getContainer?: HTMLElement | (() => HTMLElement) | false;
18 |
19 | /** 是否展示遮罩, 默认为 true */
20 | mask?: boolean;
21 |
22 | /** 点击遮罩是否允许关闭, 默认为 true */
23 | maskClosable?: boolean;
24 |
25 | /** 遮罩样式 */
26 | maskStyle?: CSSProperties;
27 |
28 | /** 是否支持键盘 esc 关闭, 默认为 true */
29 | keyboard?: boolean;
30 |
31 | /** Modal body 样式 */
32 | bodyStyle?: CSSProperties;
33 |
34 | /** 宽度, 默认为 520 */
35 | width?: string | number;
36 |
37 | /** 设置 Modal 的 z-index, 默认为 1000 */
38 | zIndex?: number;
39 |
40 | /** 取消按钮文字, 默认为 取消 */
41 | cancelText?: ReactNode;
42 |
43 | /** 确认按钮文字, 默认为 确认 */
44 | okText?: ReactNode;
45 |
46 | /** 标题 */
47 | title?: ReactNode;
48 |
49 | /** 底部内容,当不需要默认底部按钮时,可以设为 footer={null}, 默认为 (确定取消按钮) */
50 | footer?: ReactNode;
51 |
52 | /** 对话框是否可见 */
53 | open?: boolean;
54 |
55 | /** 点击遮罩层或右上角叉或取消按钮的回调 */
56 | onCancel?: () => void;
57 |
58 | /** 点击确定回调 */
59 | onOk?: () => void;
60 |
61 | /** 确定按钮 loading */
62 | confirmLoading?: boolean;
63 |
64 | /** ok 按钮 props */
65 | okButtonProps?: IButtonProps;
66 |
67 | /** cancel 按钮 props */
68 | cancelButtonProps?: IButtonProps;
69 |
70 | /** 是否显示右上角的关闭按钮 default true */
71 | closable?: boolean;
72 | } & BaseProps;
73 |
--------------------------------------------------------------------------------
/packages/components/src/Select/index.less:
--------------------------------------------------------------------------------
1 | @import '../assets/css/index.less';
2 | .boty-Select {
3 |
4 |
5 | }
--------------------------------------------------------------------------------
/packages/components/src/Select/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import { getPrefixCls } from '../config/provider';
4 |
5 | import './index.less';
6 |
7 | interface BaseProps {
8 | /**
9 | * @description 自定义样式名
10 | */
11 | className?: string;
12 | /**
13 | * @description 自定义样式
14 | */
15 | style?: React.HTMLProps;
16 |
17 | testProp: boolean;
18 | }
19 |
20 | interface BaseSelectProps {
21 | /**
22 | * @description 样式前缀
23 | */
24 | prefixCls?: string;
25 | }
26 |
27 | interface INativeSelectProps {
28 | /**
29 | * @description 是否生效
30 | */
31 | disabled?: boolean;
32 | }
33 |
34 | export type ISelectProps = BaseProps & BaseSelectProps & INativeSelectProps;
35 |
36 | const Select = (props: ISelectProps) => {
37 | console.log('rendered select');
38 | const { prefixCls, className, style: customStyle } = props;
39 |
40 | const selfPrefixCls = getPrefixCls(prefixCls || 'Select');
41 |
42 | const classes = classNames(selfPrefixCls, className);
43 |
44 | return ;
45 | };
46 |
47 | export default Select;
48 |
--------------------------------------------------------------------------------
/packages/components/src/assets/css/index.less:
--------------------------------------------------------------------------------
1 | @import './reset.less';
--------------------------------------------------------------------------------
/packages/components/src/assets/css/reset.less:
--------------------------------------------------------------------------------
1 | /*
2 | 重置标签样式 和基类
3 | */
4 | @import './vars.less';
5 | html {
6 | -ms-text-size-adjust: 100%; /* 2 */
7 | -webkit-text-size-adjust: 100%; /* 2 */
8 | }
9 |
10 | /**
11 | * Add the correct display in IE 9-.
12 | */
13 |
14 | article,
15 | aside,
16 | footer,
17 | header,
18 | nav,
19 | section {
20 | display: block;
21 | }
22 |
23 | /**
24 | * Add the correct display in IE 9-.
25 | * 1. Add the correct display in IE.
26 | */
27 |
28 | figcaption,
29 | figure,
30 | main {
31 | /* 1 */
32 | display: block;
33 | }
34 |
35 | /**
36 | * 1. Add the correct box sizing in Firefox.
37 | * 2. Show the overflow in Edge and IE.
38 | */
39 |
40 | hr {
41 | box-sizing: content-box; /* 1 */
42 | height: 0; /* 1 */
43 | overflow: visible; /* 2 */
44 | }
45 |
46 | /**
47 | * 1. Correct the inheritance and scaling of font size in all browsers.
48 | * 2. Correct the odd `em` font sizing in all browsers.
49 | */
50 |
51 | pre {
52 | font-size: 1em; /* 2 */
53 | }
54 |
55 | /**
56 | * 1. Remove the gray background on active links in IE 10.
57 | * 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
58 | */
59 |
60 | a {
61 | background-color: transparent; /* 1 */
62 | -webkit-text-decoration-skip: objects; /* 2 */
63 | }
64 |
65 | /**
66 | * 1. Remove the bottom border in Chrome 57- and Firefox 39-.
67 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
68 | */
69 |
70 | abbr[title] {
71 | border-bottom: none; /* 1 */
72 | text-decoration: underline; /* 2 */
73 | text-decoration: underline dotted; /* 2 */
74 | }
75 |
76 | /**
77 | * Prevent the duplicate application of `bolder` by the next rule in Safari 6.
78 | */
79 |
80 | b,
81 | strong {
82 | font-weight: inherit;
83 | }
84 |
85 | /**
86 | * Add the correct font weight in Chrome, Edge, and Safari.
87 | */
88 |
89 | b,
90 | strong {
91 | font-weight: bold;
92 | }
93 |
94 | /**
95 | * 1. Correct the inheritance and scaling of font size in all browsers.
96 | * 2. Correct the odd `em` font sizing in all browsers.
97 | */
98 |
99 | code,
100 | kbd,
101 | samp {
102 | font-size: 1em; /* 2 */
103 | }
104 |
105 | /**
106 | * Add the correct font style in Android 4.3-.
107 | */
108 |
109 | dfn {
110 | font-style: italic;
111 | }
112 |
113 | /**
114 | * Prevent `sub` and `sup` elements from affecting the line height in
115 | * all browsers.
116 | */
117 |
118 | sub,
119 | sup {
120 | font-size: 75%;
121 | line-height: 0;
122 | position: relative;
123 | vertical-align: baseline;
124 | }
125 |
126 | sub {
127 | bottom: -0.25em;
128 | }
129 |
130 | sup {
131 | top: -0.5em;
132 | }
133 |
134 | /* Embedded content
135 | ========================================================================== */
136 |
137 | /**
138 | * Add the correct display in IE 9-.
139 | */
140 |
141 | audio,
142 | video {
143 | display: inline-block;
144 | }
145 |
146 | /**
147 | * Add the correct display in iOS 4-7.
148 | */
149 |
150 | audio:not([controls]) {
151 | display: none;
152 | height: 0;
153 | }
154 |
155 | /**
156 | * Remove the border on images inside links in IE 10-.
157 | */
158 |
159 | img {
160 | border-style: none;
161 | }
162 |
163 | /**
164 | * Hide the overflow in IE.
165 | */
166 |
167 | svg:not(:root) {
168 | overflow: hidden;
169 | }
170 |
171 | /* Forms
172 | ========================================================================== */
173 |
174 | /**
175 | * 1. Change the font styles in all browsers (opinionated).
176 | * 2. Remove the margin in Firefox and Safari.
177 | */
178 |
179 | button,
180 | input,
181 | optgroup,
182 | select,
183 | textarea {
184 | margin: 0; /* 2 */
185 | }
186 |
187 | /**
188 | * Show the overflow in IE.
189 | * 1. Show the overflow in Edge.
190 | */
191 |
192 | button,
193 | input {
194 | /* 1 */
195 | overflow: visible;
196 | }
197 |
198 | /**
199 | * Remove the inheritance of text transform in Edge, Firefox, and IE.
200 | * 1. Remove the inheritance of text transform in Firefox.
201 | */
202 |
203 | button,
204 | select {
205 | /* 1 */
206 | text-transform: none;
207 | }
208 |
209 | /**
210 | * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
211 | * controls in Android 4.
212 | * 2. Correct the inability to style clickable types in iOS and Safari.
213 | */
214 |
215 | button,
216 | html [type='button'],
217 | [type='reset'],
218 | [type='submit'] {
219 | -webkit-appearance: button; /* 2 */
220 | }
221 |
222 | /**
223 | * Remove the inner border and padding in Firefox.
224 | */
225 |
226 | button::-moz-focus-inner,
227 | [type='button']::-moz-focus-inner,
228 | [type='reset']::-moz-focus-inner,
229 | [type='submit']::-moz-focus-inner {
230 | border-style: none;
231 | padding: 0;
232 | }
233 |
234 | /**
235 | * Restore the focus styles unset by the previous rule.
236 | */
237 |
238 | button:-moz-focusring,
239 | [type='button']:-moz-focusring,
240 | [type='reset']:-moz-focusring,
241 | [type='submit']:-moz-focusring {
242 | outline: 1px dotted ButtonText;
243 | }
244 |
245 | /**
246 | * 1. Correct the text wrapping in Edge and IE.
247 | * 2. Correct the color inheritance from `fieldset` elements in IE.
248 | * 3. Remove the padding so developers are not caught out when they zero out
249 | * `fieldset` elements in all browsers.
250 | */
251 |
252 | legend {
253 | box-sizing: border-box; /* 1 */
254 | color: inherit; /* 2 */
255 | display: table; /* 1 */
256 | max-width: 100%; /* 1 */
257 | padding: 0; /* 3 */
258 | white-space: normal; /* 1 */
259 | }
260 |
261 | /**
262 | * 1. Add the correct display in IE 9-.
263 | * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
264 | */
265 |
266 | progress {
267 | display: inline-block; /* 1 */
268 | vertical-align: baseline; /* 2 */
269 | }
270 |
271 | /**
272 | * Remove the default vertical scrollbar in IE.
273 | */
274 |
275 | textarea {
276 | overflow: auto;
277 | }
278 |
279 | /**
280 | * 1. Add the correct box sizing in IE 10-.
281 | * 2. Remove the padding in IE 10-.
282 | */
283 |
284 | [type='checkbox'],
285 | [type='radio'] {
286 | box-sizing: border-box; /* 1 */
287 | padding: 0; /* 2 */
288 | }
289 |
290 | /**
291 | * Correct the cursor style of increment and decrement buttons in Chrome.
292 | */
293 |
294 | [type='number']::-webkit-inner-spin-button,
295 | [type='number']::-webkit-outer-spin-button {
296 | height: auto;
297 | }
298 |
299 | /**
300 | * 1. Correct the odd appearance in Chrome and Safari.
301 | * 2. Correct the outline style in Safari.
302 | */
303 |
304 | [type='search'] {
305 | -webkit-appearance: textfield; /* 1 */
306 | outline-offset: -2px; /* 2 */
307 | }
308 |
309 | /**
310 | * Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
311 | */
312 |
313 | [type='search']::-webkit-search-cancel-button,
314 | [type='search']::-webkit-search-decoration {
315 | -webkit-appearance: none;
316 | }
317 |
318 | /**
319 | * 1. Correct the inability to style clickable types in iOS and Safari.
320 | * 2. Change font properties to `inherit` in Safari.
321 | */
322 |
323 | ::-webkit-file-upload-button {
324 | -webkit-appearance: button; /* 1 */
325 | font: inherit; /* 2 */
326 | }
327 |
328 | /*
329 | * Add the correct display in IE 9-.
330 | * 1. Add the correct display in Edge, IE, and Firefox.
331 | */
332 |
333 | details,
334 | menu {
335 | display: block;
336 | }
337 |
338 | /*
339 | * Add the correct display in all browsers.
340 | */
341 |
342 | summary {
343 | display: list-item;
344 | }
345 |
346 | /**
347 | * Add the correct display in IE 9-.
348 | */
349 |
350 | canvas {
351 | display: inline-block;
352 | }
353 |
354 | /**
355 | * Add the correct display in IE.
356 | */
357 |
358 | template {
359 | display: none;
360 | }
361 |
362 | /**
363 | * Add the correct display in IE 10-.
364 | */
365 |
366 | [hidden] {
367 | display: none;
368 | }
369 |
370 | /*
371 | * Reset styles
372 | */
373 | html,
374 | body,
375 | div,
376 | span,
377 | applet,
378 | object,
379 | iframe,
380 | h1,
381 | h2,
382 | h3,
383 | h4,
384 | h5,
385 | h6,
386 | p,
387 | blockquote,
388 | pre,
389 | a,
390 | abbr,
391 | acronym,
392 | address,
393 | big,
394 | cite,
395 | code,
396 | del,
397 | dfn,
398 | em,
399 | img,
400 | ins,
401 | kbd,
402 | q,
403 | s,
404 | samp,
405 | small,
406 | strike,
407 | strong,
408 | sub,
409 | sup,
410 | tt,
411 | var,
412 | b,
413 | u,
414 | i,
415 | center,
416 | dl,
417 | dt,
418 | dd,
419 | ol,
420 | ul,
421 | li,
422 | fieldset,
423 | form,
424 | label,
425 | legend,
426 | table,
427 | caption,
428 | tbody,
429 | tfoot,
430 | thead,
431 | tr,
432 | th,
433 | td,
434 | article,
435 | aside,
436 | canvas,
437 | details,
438 | embed,
439 | figure,
440 | figcaption,
441 | footer,
442 | header,
443 | hgroup,
444 | menu,
445 | nav,
446 | output,
447 | ruby,
448 | section,
449 | summary,
450 | time,
451 | mark,
452 | audio,
453 | video,
454 | button {
455 | margin: 0;
456 | padding: 0;
457 | border: 0;
458 | font-size: 100%;
459 | color: @default;
460 | vertical-align: baseline;
461 | font-family: @font-family;
462 | }
463 |
464 | body {
465 | font-family: @font-family;
466 | font-weight: @font-weight-normal;
467 | font-size: @font-weight-normal;
468 | }
469 |
470 | a {
471 | text-decoration: none;
472 | }
473 |
474 | ul,
475 | ol {
476 | list-style: none;
477 | }
--------------------------------------------------------------------------------
/packages/components/src/assets/css/vars.less:
--------------------------------------------------------------------------------
1 | /*
2 | 全局的less变量
3 | */
4 | @default: rgba(0,0,0,.85);
5 | @white: #ffffff;
6 | @black: #000000;
7 | @transparent: transparent;
8 | @primary: #1890ff;
9 | @secondary: #050505;
10 | @info: #4582C8;
11 | @success: #579E6E;
12 | @error: #D44C46;
13 | @warning: #D07035;
14 | @gray100: #f6f9fc;
15 | @gray200: #e9ecef;
16 | @gray300: #dee2e6;
17 | @gray400: #ced4da;
18 | @gray500: #adb5bd;
19 | @gray600: #8898aa;
20 | @gray700: #525f7f;
21 | @gray800: #32325d;
22 | @gray900: #212529;
23 | @default-font-size: 14px;
24 | @large-font-size: 16px;
25 | @font-weight-bold: bold;
26 | @font-weight-normal:normal;
27 | @default-border-color:#d9d9d9;
28 | @default-fillet:2px;
29 | @font-family:'-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji';
30 |
--------------------------------------------------------------------------------
/packages/components/src/config/provider.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Cookie
3 | * @Date: 2021-03-03 12:00:19
4 | * @LastEditors: Please set LastEditors
5 | * @LastEditTime: 2022-05-16 15:39:04
6 | * @Description:
7 | */
8 |
9 | export const getPrefixCls = (prefixCls) => {
10 | return prefixCls ? `boty-${prefixCls}` : 'boty';
11 | };
12 |
--------------------------------------------------------------------------------
/packages/components/src/config/provider.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Cookie
3 | * @Date: 2021-03-03 12:00:19
4 | * @LastEditors: Cookie
5 | * @LastEditTime: 2021-03-03 12:02:03
6 | * @Description:
7 | */
8 |
9 | export const getPrefixCls = (prefixCls: string) => {
10 | return prefixCls ? `boty-${prefixCls}` : 'boty';
11 | };
12 |
--------------------------------------------------------------------------------
/packages/components/src/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 项目入口
3 | */
4 |
5 | export { default as Button } from './Button';
6 | export { default as Select } from './Select';
7 | export { default as Modal } from './Modal';
8 |
--------------------------------------------------------------------------------
/packages/components/vite.config.ts:
--------------------------------------------------------------------------------
1 | ///
2 | /*
3 | * @Author: Cookie
4 | * @Date: 2021-03-02 13:47:42
5 | * @LastEditors: Please set LastEditors
6 | * @LastEditTime: 2022-09-23 10:52:25
7 | * @FilePath: /vite-react-boty-desing/packages/components/vite.config.ts
8 | * @Description:
9 | */
10 | import { defineConfig } from 'vite';
11 | import react from '@vitejs/plugin-react';
12 | import viteEslint from 'vite-plugin-eslint';
13 | import { resolve } from 'path';
14 |
15 | // https://vitejs.dev/config/
16 | export default defineConfig({
17 | plugins: [react(), viteEslint()],
18 | build: {
19 | lib: {
20 | entry: resolve(__dirname, 'src/index.ts'),
21 | name: 'boty-design',
22 | formats: ['es', 'umd', 'cjs']
23 | }
24 | },
25 | test: {
26 | globals: true,
27 | environment: 'happy-dom'
28 | }
29 | });
30 |
--------------------------------------------------------------------------------
/packages/examples/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 | Vite React App
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/packages/examples/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@boty-design/examples",
3 | "private": true,
4 | "version": "0.0.2",
5 | "description": "> TODO: description",
6 | "author": "boty",
7 | "homepage": "https://github.com/boty-design/boty-design#readme",
8 | "directories": {
9 | "lib": "lib",
10 | "test": "__tests__"
11 | },
12 | "files": [
13 | "lib"
14 | ],
15 | "repository": {
16 | "type": "git",
17 | "url": "git+https://github.com/boty-design/boty-design.git"
18 | },
19 | "scripts": {
20 | "dev": "vite"
21 | },
22 | "bugs": {
23 | "url": "https://github.com/boty-design/boty-design/issues"
24 | },
25 | "dependencies": {
26 | "@boty-design/components": "workspace:*",
27 | "@boty-design/form-generator": "workspace:*",
28 | "@boty-design/utils": "workspace:*"
29 | }
30 | }
--------------------------------------------------------------------------------
/packages/examples/src/App.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Cookie
3 | * @Date: 2021-02-27 16:11:42
4 | * @LastEditors: Please set LastEditors
5 | * @LastEditTime: 2022-09-23 11:29:09
6 | * @Description:
7 | */
8 | import React, { useState } from 'react';
9 | import { Button, Select } from '@boty-design/components';
10 | import { LoadingOutlined, CheckOutlined } from '@ant-design/icons';
11 | import { useFormItem } from '@boty-design/form-generator';
12 | import Form from '@boty-design/form-generator/src/components/Form';
13 | import Field from '@boty-design/form-generator/src/components/Field';
14 |
15 | function App() {
16 | const [count, setCount] = useState(0);
17 |
18 | const handlerSyncClick = () => {
19 | return new Promise((resolve) => {
20 | setTimeout(() => {
21 | console.log('sss');
22 | resolve(true);
23 | }, 1000);
24 | });
25 | };
26 |
27 | const handlerClick = () => {
28 | console.log('sss');
29 | };
30 | const btnStyle = {
31 | color: 'red',
32 | };
33 |
34 | console.log('render');
35 | return (
36 |
37 |
40 |
41 |
}>
42 |
}>
43 | 成功
44 |
45 |
46 |
49 |
50 |
104 |
105 | );
106 | }
107 |
108 | export default App;
109 |
--------------------------------------------------------------------------------
/packages/examples/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | // import '@boty-design/themes/default.css';
4 | import App from './App';
5 |
6 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
7 |
8 |
9 |
10 | );
11 |
--------------------------------------------------------------------------------
/packages/examples/vite.config.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Cookie
3 | * @Date: 2021-03-02 13:47:42
4 | * @LastEditors: Cookie
5 | * @LastEditTime: 2021-03-03 02:02:09
6 | * @FilePath: /vite-react-boty-desing/vite.config.ts
7 | * @Description:
8 | */
9 | import { defineConfig } from 'vite';
10 | import react from '@vitejs/plugin-react';
11 |
12 | // https://vitejs.dev/config/
13 | export default defineConfig({
14 | server: {
15 | port: 5222
16 | },
17 | resolve: {},
18 | plugins: [react()]
19 | });
20 |
--------------------------------------------------------------------------------
/packages/form-generator/.gitignore:
--------------------------------------------------------------------------------
1 | types/
--------------------------------------------------------------------------------
/packages/form-generator/README.md:
--------------------------------------------------------------------------------
1 | # `@boty-design/form-generator`
2 |
3 | > TODO: description
4 |
5 | ## Usage
6 |
7 | ```
8 | const formGenerator = require('@boty-design/form-generator');
9 |
10 | // TODO: DEMONSTRATE API
11 | ```
12 |
--------------------------------------------------------------------------------
/packages/form-generator/__tests__/form-generator.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const formGenerator = require('..');
4 |
5 | describe('@boty-design/form-generator', () => {
6 | it('needs tests');
7 | });
8 |
--------------------------------------------------------------------------------
/packages/form-generator/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@boty-design/form-generator",
3 | "version": "1.0.1",
4 | "description": "> TODO: description",
5 | "author": "boty",
6 | "homepage": "https://github.com/boty-design/boty-design#readme",
7 | "license": "ISC",
8 | "main": "src/index.ts",
9 | "directories": {
10 | "lib": "lib",
11 | "test": "__tests__"
12 | },
13 | "files": [
14 | "lib"
15 | ],
16 | "publishConfig": {
17 | "access": "public"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/boty-design/boty-design.git"
22 | },
23 | "scripts": {
24 | "test": "echo \"Error: run tests from root\" && exit 1"
25 | },
26 | "bugs": {
27 | "url": "https://github.com/boty-design/boty-design/issues"
28 | },
29 | "dependencies": {
30 | "@boty-design/utils": "workspace:*",
31 | "invariant": "^2.2.4",
32 | "reflect-metadata": "^0.1.13"
33 | },
34 | "devDependencies": {
35 | "@types/invariant": "^2.2.34"
36 | }
37 | }
--------------------------------------------------------------------------------
/packages/form-generator/src/components/Field.tsx:
--------------------------------------------------------------------------------
1 | import invariant from 'invariant';
2 | import React, { useMemo } from 'react';
3 | import useFormContext from '../hooks/useFormContext';
4 | import { FormStoreProps, ExtractProps } from '../types';
5 | import usePrevious from '@boty-design/utils/src/usePrevious';
6 | import { Select } from '@boty-design/components';
7 | import { Merge } from '@boty-design/utils/src/merge';
8 | const fieldNames: Array> = [
9 | 'touched',
10 | 'values',
11 | 'errors',
12 | 'validating',
13 | ];
14 |
15 | function Field>({
16 | name,
17 | render,
18 | as,
19 | ...rest
20 | }: {
21 | name: string;
22 | render?: any;
23 | as: C;
24 | } & ExtractProps) {
25 | const Component = as;
26 | const { state, dispatch } = useFormContext();
27 |
28 | const fieldValues = fieldNames.map((field) => state[field][name]);
29 | const prev = usePrevious(fieldValues);
30 |
31 | const isDirty = fieldValues.some((val, i) => {
32 | if (!prev) return true;
33 | return val !== prev[i];
34 | });
35 |
36 | if (!Component) {
37 | invariant(true, 'you need to provide a component.');
38 | return null;
39 | }
40 |
41 | console.log('render field', isDirty);
42 |
43 | const renderResult = useMemo(() => {
44 | return ;
45 | }, [isDirty]);
46 |
47 | return renderResult;
48 | }
49 |
50 | export default Field;
51 |
--------------------------------------------------------------------------------
/packages/form-generator/src/components/Form.tsx:
--------------------------------------------------------------------------------
1 | import React, {
2 | Reducer,
3 | useReducer,
4 | ReactNode,
5 | useMemo,
6 | useRef,
7 | useEffect,
8 | createContext,
9 | } from 'react';
10 | import invariant from 'invariant';
11 | import {
12 | ValidateRule,
13 | Wrapped,
14 | FormErrors,
15 | FormTouched,
16 | FormValidators,
17 | FormActions,
18 | ActionEnums,
19 | FormContext,
20 | FormValidating,
21 | FormChangeEvent,
22 | FormChildrenProps,
23 | FormStoreProps,
24 | } from '../types';
25 | import useValidator from '../hooks/useValidator';
26 | import { FormContextProvider } from '../internals/context';
27 |
28 | type FormValues = {
29 | [key in string]: any;
30 | };
31 |
32 | /**
33 | * Props provided to
34 | */
35 | type FormProps = {
36 | initialValues: V;
37 | initialTouched?: FormTouched;
38 | initialErrors?: FormErrors;
39 | validationSchema?: {
40 | [K in keyof V]?: Wrapped>>;
41 | };
42 | };
43 |
44 | function Form({
45 | initialValues,
46 | initialErrors,
47 | initialTouched,
48 | children,
49 | validationSchema,
50 | }: FormProps & {
51 | children: (props: FormChildrenProps) => ReactNode | ReactNode;
52 | }) {
53 | const initialValidating = Object.keys(initialValues).reduce(
54 | (prev, fieldName) => {
55 | prev[fieldName] = false;
56 | return prev;
57 | },
58 | {}
59 | ) as FormValidating;
60 |
61 | const [state, dispatch] = useReducer<
62 | Reducer, FormActions>
63 | >(
64 | (state, { type, payload }) => {
65 | // console.log('action dispatched', ActionEnums[type], payload);
66 |
67 | switch (type) {
68 | case ActionEnums.SET_ERRORS:
69 | return { ...state, errors: { ...state.errors, ...payload } };
70 | case ActionEnums.SET_FIELD:
71 | return { ...state, values: { ...state.values, ...payload } };
72 | case ActionEnums.SET_VALIDATING_FIELD: {
73 | return { ...state, validating: { ...state.validating, ...payload } };
74 | }
75 | default:
76 | invariant(false, 'you provided an unkown action!');
77 | }
78 | },
79 | {
80 | values: initialValues,
81 | touched: {},
82 | errors: { ...initialErrors },
83 | validating: initialValidating,
84 | isValid: true,
85 | }
86 | );
87 |
88 | const validationContext = useRef>({
89 | state,
90 | dispatch,
91 | });
92 |
93 | useEffect(() => {
94 | validationContext.current = { state, dispatch };
95 | });
96 |
97 | const validators = useMemo(() => {
98 | const vals = {};
99 |
100 | Object.keys(initialValues).map((fieldName) => {
101 | const validator = useValidator(
102 | fieldName,
103 | validationSchema,
104 | validationContext
105 | );
106 | vals[fieldName] = validator;
107 | });
108 | return vals;
109 | }, [validationSchema]) as FormValidators;
110 |
111 | const handleChange = useMemo(() => {
112 | const handlers = {};
113 | Object.keys(initialValues).map((fieldName) => {
114 | handlers[fieldName] = (val: any) =>
115 | dispatch({
116 | type: ActionEnums.SET_FIELD,
117 | payload: {
118 | [fieldName]: val,
119 | },
120 | } as FormActions);
121 | });
122 |
123 | return handlers;
124 | }, [initialValues]) as FormChangeEvent;
125 |
126 | return (
127 |
128 | {children({ ...state, validators, handleChange })}
129 |
130 | );
131 | }
132 |
133 | export default Form;
134 |
--------------------------------------------------------------------------------
/packages/form-generator/src/hooks/useFormContext.ts:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react';
2 | import { context } from '../internals/context';
3 | import { FormContext } from '../types';
4 |
5 | function useFormContext() {
6 | return useContext>(context);
7 | }
8 |
9 | export default useFormContext;
10 |
--------------------------------------------------------------------------------
/packages/form-generator/src/hooks/useFormItem.ts:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import { FormItemType } from '../internals/symbols';
3 |
4 | export default function useFormItem(proto: any) {
5 | Reflect.getMetadata(FormItemType, proto);
6 | }
7 |
--------------------------------------------------------------------------------
/packages/form-generator/src/hooks/useValidator.ts:
--------------------------------------------------------------------------------
1 | import { useCallback, MutableRefObject } from 'react';
2 | import { ValidateRule, Wrapped, FormContext, ActionEnums } from '../types';
3 | import isPromise from '@boty-design/utils/src/isPromise';
4 | import { FormActions } from '../types';
5 |
6 | function setValidating(
7 | field: keyof V,
8 | status: boolean,
9 | context: FormContext
10 | ) {
11 | return context.dispatch({
12 | type: ActionEnums.SET_VALIDATING_FIELD,
13 | payload: { [field]: status },
14 | } as FormActions);
15 | }
16 |
17 | function useValidator(
18 | fieldName: keyof V,
19 | schema: {
20 | [K in keyof V]?: Wrapped>>;
21 | },
22 | contextRef: MutableRefObject>
23 | ) {
24 | return (val: V) => {
25 | const context = contextRef.current;
26 |
27 | setValidating(fieldName, true, context);
28 | context.dispatch({
29 | type: ActionEnums.SET_ERRORS,
30 | payload: { [fieldName]: [] },
31 | } as FormActions);
32 |
33 | if (!schema[fieldName]) {
34 | return [];
35 | }
36 |
37 | const rules: ValidateRule>[] = [].concat(
38 | schema[fieldName]
39 | );
40 |
41 | const results = rules.map((rule) => rule(val, context));
42 | const asyncResults: Promise[] = results.filter((result) =>
43 | isPromise(result)
44 | ) as Promise[];
45 | const syncResults: string[] = results.filter(
46 | (result) => !isPromise(result)
47 | ) as string[];
48 |
49 | if (!asyncResults.length) {
50 | setValidating(fieldName, false, context);
51 | return syncResults.filter(Boolean);
52 | } else {
53 | return Promise.all(asyncResults).then(([...resolved]) => {
54 | context.dispatch({
55 | type: ActionEnums.SET_ERRORS,
56 | payload: {
57 | [fieldName]: syncResults.concat(resolved).filter(Boolean),
58 | },
59 | } as FormActions);
60 |
61 | setValidating(fieldName, false, context);
62 |
63 | return syncResults.concat(resolved).filter(Boolean);
64 | });
65 | }
66 | };
67 | }
68 |
69 | export default useValidator;
70 |
--------------------------------------------------------------------------------
/packages/form-generator/src/index.ts:
--------------------------------------------------------------------------------
1 | import 'reflect-metadata';
2 |
3 | export { default as useFormItem } from './hooks/useFormItem';
4 | export { default as Form } from './components/Form';
5 | export { default as Field } from './components/Field';
6 | export * from './types';
7 |
--------------------------------------------------------------------------------
/packages/form-generator/src/internals/context.ts:
--------------------------------------------------------------------------------
1 | import { createContext } from 'react';
2 | import { FormContext } from '../types';
3 |
4 | export const context = createContext>(null);
5 | export const FormContextProvider = context.Provider;
6 | export const FormContextConsumer = context.Consumer;
7 |
--------------------------------------------------------------------------------
/packages/form-generator/src/internals/symbols.ts:
--------------------------------------------------------------------------------
1 | export const FormItemType = Symbol('FormItemType');
2 |
--------------------------------------------------------------------------------
/packages/form-generator/src/internals/transaction.ts:
--------------------------------------------------------------------------------
1 | import invariant from 'invariant';
2 |
3 | let inTransaction = false;
4 | let taskQueue = [];
5 | export const startTransaction = () => {
6 | inTransaction = true;
7 | };
8 |
9 | export const enqueueTask = (cb) => {
10 | taskQueue.push(cb);
11 | };
12 |
13 | export const commitTransaction = () => {
14 | invariant(
15 | inTransaction === true,
16 | 'cant commit since not currently in transaction.'
17 | );
18 |
19 | while (taskQueue.length > 0) {
20 | const task = taskQueue.pop();
21 | task();
22 | }
23 |
24 | inTransaction = false;
25 | };
26 |
--------------------------------------------------------------------------------
/packages/form-generator/src/types.ts:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export type Wrapped = T | T[];
4 |
5 | export type ValidateRule = (
6 | value: T,
7 | context?: C /** FIX ME!!! */
8 | ) => void | string | Promise;
9 |
10 | export type ValidateResultFor = R extends (
11 | ...args: infer U
12 | ) => void | string | Promise
13 | ? (...args: U) => string[] | Promise
14 | : never;
15 |
16 | export interface IFormItemAttributes {
17 | label: string | React.ReactElement;
18 | rules: Wrapped>;
19 | value: T;
20 | validators: Wrapped>;
21 | }
22 |
23 | export type ExtractProps = ReactComponent extends React.FC<
24 | infer P
25 | >
26 | ? P
27 | : ReactComponent extends React.Component
28 | ? P
29 | : never;
30 |
31 | export enum ActionEnums {
32 | SET_FIELD,
33 | SET_ERRORS,
34 | SET_VALIDATING_FIELD,
35 | SET_VALIDING,
36 | }
37 |
38 | type Action = { type: T; payload: P };
39 |
40 | export type FormActions =
41 | | Action>>
42 | | Action>
43 | | Action>;
44 |
45 | /**
46 | * An object containing error messages whose keys correspond to FormValues.
47 | * Should always be an object of strings, but any is allowed to support i18n libraries.
48 | */
49 | export type FormErrors = {
50 | [K in keyof FormValues]?: FormValues[K] extends any[]
51 | ? FormValues[K][number] extends object // [number] is the special sauce to get the type of array's element. More here https://github.com/Microsoft/TypeScript/pull/21316
52 | ? FormErrors[] | string | string[]
53 | : string | string[]
54 | : FormValues[K] extends object
55 | ? FormErrors
56 | : string[];
57 | };
58 |
59 | export type FormChangeEvent = {
60 | [K in keyof FormValues]: (val: any) => void;
61 | };
62 |
63 | /**
64 | * An object containing touched state of the form whose keys correspond to FormValues.
65 | */
66 | export type FormTouched = {
67 | [K in keyof FormValues]?: FormValues[K] extends any[]
68 | ? FormValues[K][number] extends object // [number] is the special sauce to get the type of array's element. More here https://github.com/Microsoft/TypeScript/pull/21316
69 | ? FormTouched[]
70 | : boolean
71 | : FormValues[K] extends object
72 | ? FormTouched
73 | : boolean;
74 | };
75 |
76 | export type FormValidating = {
77 | [K in keyof FormValues]: boolean;
78 | };
79 |
80 | // export type FormValidating = {
81 | // [K in keyof FormValues]?: FormValues[K] extends any[]
82 | // ? FormValues[K][number] extends object // [number] is the special sauce to get the type of array's element. More here https://github.com/Microsoft/TypeScript/pull/21316
83 | // ? FormValidating[]
84 | // : boolean
85 | // : FormValues[K] extends object
86 | // ? FormValidating
87 | // : boolean;
88 | // };
89 |
90 | export type FormValidators> = {
91 | [K in keyof FormValues]: (val: FormValues[K]) => string[] | Promise;
92 | };
93 | export type FormContext = {
94 | state: FormStoreProps;
95 | dispatch: React.Dispatch>;
96 | };
97 |
98 | export type FormChildrenProps = FormStoreProps & {
99 | validators: FormValidators;
100 | handleChange: FormChangeEvent;
101 | };
102 |
103 | export type FormStoreProps = {
104 | values: V;
105 | touched: FormTouched;
106 | validating: FormValidating;
107 | errors: FormErrors;
108 | isValid: boolean;
109 | };
110 |
--------------------------------------------------------------------------------
/packages/form-generator/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "noEmit": false,
5 | "outDir": "lib/",
6 | "baseUrl": ".",
7 | "declaration": true,
8 | "declarationDir": "types/"
9 | },
10 | "include": [
11 | "src/**/*"
12 | ]
13 | }
--------------------------------------------------------------------------------
/packages/icons/README.md:
--------------------------------------------------------------------------------
1 | # `@boty-design/icons`
2 |
3 | > TODO: description
4 |
5 | ## Usage
6 |
7 | ```
8 | const icons = require('@boty-design/icons');
9 |
10 | // TODO: DEMONSTRATE API
11 | ```
12 |
--------------------------------------------------------------------------------
/packages/icons/__tests__/icons.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const icons = require('..');
4 |
5 | describe('@boty-design/icons', () => {
6 | it('needs tests');
7 | });
8 |
--------------------------------------------------------------------------------
/packages/icons/lib/icons.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = icons;
4 |
5 | function icons() {
6 | // TODO
7 | }
8 |
--------------------------------------------------------------------------------
/packages/icons/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@boty-design/icons",
3 | "version": "1.0.1",
4 | "description": "> TODO: description",
5 | "author": "boty",
6 | "homepage": "https://github.com/boty-design/boty-design#readme",
7 | "license": "ISC",
8 | "main": "lib/icons.js",
9 | "directories": {
10 | "lib": "lib",
11 | "test": "__tests__"
12 | },
13 | "files": [
14 | "lib"
15 | ],
16 | "publishConfig": {
17 | "access": "public"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/boty-design/boty-design.git"
22 | },
23 | "scripts": {
24 | "test": "echo \"Error: run tests from root\" && exit 1"
25 | },
26 | "bugs": {
27 | "url": "https://github.com/boty-design/boty-design/issues"
28 | },
29 | "gitHead": "e672c3c1d4971d8b81c231c2adc79eca30db5ed8"
30 | }
--------------------------------------------------------------------------------
/packages/scripts/README.md:
--------------------------------------------------------------------------------
1 | # `scripts`
2 |
3 | > 自动化脚本
4 |
5 | ## 功能
6 |
7 | - 通过plop自动化创建项目模版配置
--------------------------------------------------------------------------------
/packages/scripts/__tests__/scripts.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const scripts = require('..');
4 |
5 | describe('scripts', () => {
6 | it('needs tests');
7 | });
8 |
--------------------------------------------------------------------------------
/packages/scripts/generator.js:
--------------------------------------------------------------------------------
1 | module.exports = function (plop) {
2 | plop.setGenerator('component', {
3 | description: '组件',
4 | prompts: [{
5 | type: 'input',
6 | name: 'name',
7 | message: '请输入创建组件的名字, 如Button',
8 | validate: function (value) {
9 | if ((/([A-Z][a-z]+)+/).test(value)) { return true; }
10 | return '组件名称必须为驼峰形式';
11 | }
12 | }],
13 | actions: [
14 | /**
15 | * TemplateComponent.tsx
16 | */
17 | {
18 | type: 'add',
19 | path: '../components/src/{{name}}/index.tsx',
20 | templateFile: './templates/components/TemplateComponent.js'
21 | },
22 | /**
23 | * template-component.less
24 | */
25 | {
26 | type: 'add',
27 | path: '../components/src/{{name}}/index.less',
28 | templateFile: './templates/components/template-component.css'
29 | },
30 | {
31 | type: 'modify',
32 | path: '../components/src/index.ts',
33 | transform:(fileStr,enterObj)=>{
34 | return fileStr + '\n' + `export { default as ${enterObj.name} } from './${enterObj.name}';`
35 | }
36 | }
37 | ]
38 | });
39 | };
--------------------------------------------------------------------------------
/packages/scripts/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@boty-design/scripts",
3 | "version": "1.0.0",
4 | "description": "项目中的各种脚本",
5 | "author": "gzg1023 ",
6 | "homepage": "https://github.com/boty-design/boty-design#readme",
7 | "license": "MIT",
8 | "directories": {
9 | "lib": "lib"
10 | },
11 | "publishConfig": {
12 | "registry": "https://registry.yarnpkg.com"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "git+https://github.com/boty-design/boty-design.git"
17 | },
18 | "scripts": {
19 | "generator": "plop --plopfile generator.js"
20 | },
21 | "bugs": {
22 | "url": "https://github.com/boty-design/boty-design/issues"
23 | },
24 | "devDependencies": {
25 | "plop": "^2.7.4"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/packages/scripts/templates/components/Templatecomponent.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import { getPrefixCls } from '../config/provider';
4 |
5 | import './index.less';
6 |
7 |
8 | interface BaseProps {
9 | /**
10 | * @description 自定义样式名
11 | */
12 | className?: string;
13 | /**
14 | * @description 自定义样式
15 | */
16 | style?: React.HTMLProps;
17 | }
18 |
19 | interface Base{{name}}Props {
20 | /**
21 | * @description 样式前缀
22 | */
23 | prefixCls?: string;
24 | }
25 |
26 | interface INative{{name}}Props {
27 | /**
28 | * @description 是否生效
29 | */
30 | disabled?: boolean;
31 | }
32 |
33 | export type I{{name}}Props = BaseProps & Base{{name}}Props & INative{{name}}Props;
34 |
35 |
36 | const {{name}} = (props: I{{name}}Props) => {
37 | const {
38 | prefixCls,
39 | className,
40 | style: customStyle,
41 | } = props;
42 |
43 |
44 | const selfPrefixCls = getPrefixCls(prefixCls || '{{name}}');
45 |
46 | const classes = classNames(
47 | selfPrefixCls,
48 | className
49 | );
50 |
51 | return (
52 |
56 |
57 | );
58 | };
59 |
60 | export default {{name}};
61 |
--------------------------------------------------------------------------------
/packages/scripts/templates/components/template-component.css:
--------------------------------------------------------------------------------
1 | @import '../assets/css/index.less';
2 | .boty-{{name}} {
3 |
4 |
5 | }
--------------------------------------------------------------------------------
/packages/themes/README.md:
--------------------------------------------------------------------------------
1 | # `themes`
2 |
3 | > TODO: description
4 |
5 | ## Usage
6 |
7 | ```
8 | const themes = require('themes');
9 |
10 | // TODO: DEMONSTRATE API
11 | ```
12 |
--------------------------------------------------------------------------------
/packages/themes/__tests__/themes.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const themes = require('..');
4 |
5 | describe('themes', () => {
6 | it('needs tests');
7 | });
8 |
--------------------------------------------------------------------------------
/packages/themes/default.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ignition-Space/boty-design/e55d4dda0642112aa2d689e9f081998ff27d8820/packages/themes/default.css
--------------------------------------------------------------------------------
/packages/themes/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@boty-design/themes",
3 | "version": "1.0.1",
4 | "description": "> TODO: description",
5 | "author": "boty",
6 | "homepage": "https://github.com/boty-design/boty-design#readme",
7 | "license": "ISC",
8 | "directories": {
9 | "lib": "lib",
10 | "test": "__tests__"
11 | },
12 | "files": [
13 | "lib"
14 | ],
15 | "repository": {
16 | "type": "git",
17 | "url": "git+https://github.com/boty-design/boty-design.git"
18 | },
19 | "scripts": {
20 | "test": "echo \"Error: run tests from root\" && exit 1"
21 | },
22 | "bugs": {
23 | "url": "https://github.com/boty-design/boty-design/issues"
24 | },
25 | "gitHead": "e672c3c1d4971d8b81c231c2adc79eca30db5ed8"
26 | }
--------------------------------------------------------------------------------
/packages/utils/README.md:
--------------------------------------------------------------------------------
1 | # `utils`
2 |
3 | > TODO: description
4 |
5 | ## Usage
6 |
7 | ```
8 | const utils = require('utils');
9 |
10 | // TODO: DEMONSTRATE API
11 | ```
12 |
--------------------------------------------------------------------------------
/packages/utils/__tests__/utils.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const utils = require('..');
4 |
5 | describe('utils', () => {
6 | it('needs tests');
7 | });
8 |
--------------------------------------------------------------------------------
/packages/utils/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@boty-design/utils",
3 | "version": "0.0.1",
4 | "description": "> TODO: description",
5 | "author": "boty",
6 | "homepage": "https://github.com/boty-design/boty-design#readme",
7 | "license": "ISC",
8 | "main": "src/index.ts",
9 | "directories": {
10 | "lib": "lib",
11 | "test": "__tests__"
12 | },
13 | "files": [
14 | "lib"
15 | ],
16 | "repository": {
17 | "type": "git",
18 | "url": "git+https://github.com/boty-design/boty-design.git"
19 | },
20 | "scripts": {
21 | "test": "echo \"Error: run tests from root\" && exit 1"
22 | },
23 | "bugs": {
24 | "url": "https://github.com/boty-design/boty-design/issues"
25 | },
26 | "gitHead": "e672c3c1d4971d8b81c231c2adc79eca30db5ed8"
27 | }
28 |
--------------------------------------------------------------------------------
/packages/utils/src/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Cookie
3 | * @Date: 2021-02-27 16:57:17
4 | * @LastEditors: Cookie
5 | * @LastEditTime: 2021-02-27 17:04:30
6 | * @Description:
7 | */
8 |
9 | export const tuple = (...args: T) => args;
10 | export const tupleNum = (...args: T) => args;
11 |
--------------------------------------------------------------------------------
/packages/utils/src/isPromise.ts:
--------------------------------------------------------------------------------
1 | const isPromise = function isPromise(obj: any) {
2 | return (
3 | !!obj &&
4 | (typeof obj === 'object' || typeof obj === 'function') &&
5 | typeof obj.then === 'function'
6 | );
7 | };
8 |
9 | export default isPromise;
10 |
--------------------------------------------------------------------------------
/packages/utils/src/merge.ts:
--------------------------------------------------------------------------------
1 | // Passing types through Expand makes TS expand them instead of lazily
2 | // evaluating the type. This also has the benefit that intersections are merged
3 | // to show as one object.
4 | type Primitive = string | number | boolean | bigint | symbol | null | undefined;
5 | type Expand = T extends Primitive ? T : { [K in keyof T]: T[K] };
6 |
7 | type OptionalKeys = {
8 | [K in keyof T]-?: T extends Record ? never : K;
9 | }[keyof T];
10 |
11 | type RequiredKeys = {
12 | [K in keyof T]-?: T extends Record ? K : never;
13 | }[keyof T] &
14 | keyof T;
15 |
16 | type RequiredMergeKeys = RequiredKeys & RequiredKeys;
17 |
18 | type OptionalMergeKeys =
19 | | OptionalKeys
20 | | OptionalKeys
21 | | Exclude, RequiredKeys>
22 | | Exclude, RequiredKeys>;
23 |
24 | type MergeNonUnionObjects = Expand<
25 | {
26 | [K in RequiredMergeKeys]: Expand>;
27 | } &
28 | {
29 | [K in OptionalMergeKeys]?: K extends keyof T
30 | ? K extends keyof U
31 | ? Expand, Exclude>>
32 | : T[K]
33 | : K extends keyof U
34 | ? U[K]
35 | : never;
36 | }
37 | >;
38 |
39 | type MergeObjects = [T] extends [never]
40 | ? U extends any
41 | ? MergeNonUnionObjects
42 | : never
43 | : [U] extends [never]
44 | ? T extends any
45 | ? MergeNonUnionObjects
46 | : never
47 | : T extends any
48 | ? U extends any
49 | ? MergeNonUnionObjects
50 | : never
51 | : never;
52 |
53 | export type Merge =
54 | | Extract
55 | | MergeObjects, Exclude>;
56 |
--------------------------------------------------------------------------------
/packages/utils/src/usePrevious.ts:
--------------------------------------------------------------------------------
1 | import { useRef, useEffect } from 'react';
2 |
3 | function usePrevious(value: V) {
4 | const ref = useRef();
5 |
6 | useEffect(() => {
7 | ref.current = value;
8 | }, [value]);
9 |
10 | return ref.current;
11 | }
12 |
13 | export default usePrevious;
14 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - "packages/*"
3 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
5 | "types": ["vite/client"],
6 | "allowJs": false,
7 | "skipLibCheck": false,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx"
18 | },
19 | "include": [
20 | "./src",
21 | "packages/components/src/index.js",
22 | "packages/utils/types.ts",
23 | "packages/components/src/config",
24 | "packages/components/src/Button"
25 | ],
26 | "exclude": ["node_modules", "**/*.spec.ts"]
27 | }
28 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Cookie
3 | * @Date: 2021-03-05 17:25:38
4 | * @LastEditors: Cookie
5 | * @LastEditTime: 2021-03-05 17:27:33
6 | * @FilePath: /vite-react-boty-desing/webpack.config.js
7 | * @Description:
8 | */
9 |
10 | /**
11 | * 不是真实的 webpack 配置,仅为兼容 webstorm 和 intellij idea 代码跳转
12 | * ref: https://github.com/umijs/umi/issues/1109#issuecomment-423380125
13 | */
14 |
15 | module.exports = {
16 | resolve: {
17 | alias: {
18 | '@boty-design': require('path').resolve(__dirname, 'packages'), // eslint-disable-line
19 | },
20 | },
21 | };
22 |
--------------------------------------------------------------------------------