├── .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 | logo 5 |

6 |

从零打造一套企业级react 组件库生态

7 | 8 |

9 | 10 | vue 11 | 12 | 13 | element-ui 14 | 15 | 16 | lerna 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 | logo 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( 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 | 45 | 46 | 49 | 50 |
{ 55 | return '密码饿2'; 56 | }, 57 | (val) => { 58 | return new Promise((resolve) => { 59 | if (val !== 'boty') { 60 | setTimeout(() => resolve('密码错误'), 500); 61 | } else { 62 | resolve(); 63 | } 64 | }); 65 | }, 66 | ], 67 | username: (val, context) => { }, 68 | }} 69 | > 70 | {({ values, errors, validators, handleChange, validating }) => ( 71 |
72 | {errors.password} 73 | { 76 | handleChange.password(e.target.value); 77 | validators.password(e.target.value); 78 | }} 79 | > 80 | {validating.password && ( 81 | <> 82 | 验证中... 83 | 84 | 85 | )} 86 | 87 | {/* 88 | { 91 | handleChange.email(e.target.value); 92 | validators.email(e.target.value); 93 | }} 94 | > 95 | */} 96 | 97 | 98 | 99 | 100 | {(props) =>
}
101 |
102 | )} 103 |
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 | --------------------------------------------------------------------------------