├── .prettierignore ├── src ├── hooks │ ├── index.tsx │ └── useCssInJs.ts ├── util │ ├── index.tsx │ ├── index.md │ ├── SessionStorageUtils.ts │ ├── ReactDomUtils.tsx │ └── demo │ │ └── reactdom.tsx ├── ext-select │ ├── index.tsx │ ├── index.md │ ├── demo │ │ └── base.tsx │ └── ExtSelect.tsx ├── fieldset │ ├── index.md │ ├── index.tsx │ ├── demo │ │ └── base.tsx │ ├── styles.ts │ └── fieldset.tsx ├── page-header │ ├── index.md │ ├── index.tsx │ ├── demo │ │ └── base.tsx │ ├── styles.tsx │ └── page-header.tsx ├── app-toolbar │ ├── index.md │ ├── index.tsx │ ├── demo │ │ └── base.tsx │ ├── styles.ts │ └── app-toolbar.tsx ├── ext-switch │ ├── index.tsx │ ├── index.md │ ├── demo │ │ └── base.tsx │ └── ExtSwitch.tsx ├── fields-mapping │ ├── index.md │ ├── index.tsx │ ├── demo │ │ └── base.tsx │ ├── images.tsx │ ├── styles.tsx │ └── fields-mapping.tsx ├── grid-table │ ├── index.tsx │ ├── index.md │ ├── demo │ │ ├── base.tsx │ │ └── fit-table.tsx │ ├── styles.ts │ └── grid-table.tsx ├── ext-input │ ├── index.md │ ├── demo │ │ └── base.tsx │ ├── ExtInput.tsx │ ├── index.tsx │ ├── ExtInputPassword.tsx │ ├── ExtInputTextArea.tsx │ └── ExtInputOTP.tsx ├── ext-radio │ ├── index.md │ ├── index.tsx │ ├── demo │ │ └── base.tsx │ ├── ExtRadio.tsx │ └── ExtRadioGroup.tsx ├── picture-upload │ ├── index.md │ ├── index.tsx │ ├── demo │ │ └── base.tsx │ ├── styles.ts │ └── PictureUpload.tsx ├── table-toolbar │ ├── index.md │ ├── index.tsx │ ├── styles.ts │ ├── demo │ │ └── base.tsx │ └── table-toolbar.tsx ├── video-upload │ ├── index.tsx │ ├── styles.ts │ ├── VideoUpload.tsx │ └── Icons.tsx ├── avatar-editor │ ├── index.md │ ├── index.tsx │ ├── demo │ │ └── base.tsx │ ├── styles.ts │ ├── AvatarEditor.tsx │ └── AvatarCropModal.tsx ├── ext-checkbox │ ├── index.md │ ├── index.tsx │ ├── demo │ │ └── base.tsx │ ├── ExtCheckbox.tsx │ └── ExtCheckboxGroup.tsx ├── ext-tree-select │ ├── index.tsx │ ├── index.md │ ├── demo │ │ └── base.tsx │ └── ExtTreeSelect.tsx ├── fetch-select │ ├── index.tsx │ ├── index.md │ ├── demo │ │ ├── base.tsx │ │ └── set-initial-option.tsx │ └── fetch-select.tsx ├── field-wrapper │ ├── index.md │ ├── index.tsx │ ├── demo │ │ └── base.tsx │ ├── styles.ts │ └── field-wrapper.tsx ├── editable-desc │ ├── index.tsx │ ├── index.md │ ├── styles.ts │ ├── demo │ │ ├── base.tsx │ │ └── form.tsx │ └── editable-desc.tsx ├── drawer-form │ ├── index.md │ ├── demo │ │ ├── show.tsx │ │ └── base.tsx │ ├── drawer-inner-form.tsx │ ├── index.tsx │ └── drawer-form.tsx ├── ext-date-picker │ ├── index.md │ ├── index.tsx │ ├── demo │ │ └── base.tsx │ ├── ExtDatePicker.tsx │ └── ExtDateRangePicker.tsx ├── ext-input-number │ ├── index.md │ ├── index.tsx │ ├── demo │ │ └── base.tsx │ └── ExtInputNumber.tsx ├── modal-form │ ├── index.md │ ├── demo │ │ ├── show.tsx │ │ └── base.tsx │ ├── modal-inner-form.tsx │ ├── modal-form.tsx │ └── index.tsx ├── search-toolbar │ ├── index.tsx │ ├── index.md │ ├── styled.ts │ ├── demo │ │ └── base.tsx │ └── search-toolbar.tsx ├── fetch-tree-select │ ├── index.md │ ├── index.tsx │ ├── demo │ │ └── base.tsx │ └── fetch-tree-select.tsx ├── ext-form-field │ ├── index.tsx │ └── ExtFormField.tsx ├── verification-code-input │ ├── index.tsx │ ├── styles.ts │ └── verification-code-input.tsx ├── layout │ ├── index.md │ ├── index.tsx │ ├── demo │ │ ├── layout-nest.tsx │ │ └── base.tsx │ ├── item.tsx │ ├── layout.tsx │ ├── styles.ts │ └── sider.tsx └── index.ts ├── .stylelintrc ├── public └── logo.png ├── images └── shuque_wx.jpg ├── .gitignore ├── .eslintrc.js ├── .fatherrc.ts ├── docs ├── guide.md └── index.md ├── .editorconfig ├── tsconfig.json ├── .prettierrc.js ├── .github └── workflows │ └── gh-pages.yml ├── .dumirc.ts ├── LICENSE ├── README.md └── package.json /.prettierignore: -------------------------------------------------------------------------------- 1 | /dist 2 | *.yaml 3 | -------------------------------------------------------------------------------- /src/hooks/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './useCssInJs'; 2 | -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@umijs/lint/dist/config/stylelint" 3 | } 4 | -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trionesdev/antd-react-ext/HEAD/public/logo.png -------------------------------------------------------------------------------- /src/util/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './ReactDomUtils'; 2 | export * from './SessionStorageUtils'; 3 | -------------------------------------------------------------------------------- /images/shuque_wx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trionesdev/antd-react-ext/HEAD/images/shuque_wx.jpg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /dist 3 | .dumi/tmp 4 | .dumi/tmp-test 5 | .dumi/tmp-production 6 | .DS_Store 7 | .idea 8 | .npmrc 9 | -------------------------------------------------------------------------------- /src/ext-select/index.tsx: -------------------------------------------------------------------------------- 1 | import {ExtSelect,ExtSelectProps} from "./ExtSelect" 2 | 3 | export type {ExtSelectProps} 4 | export default ExtSelect -------------------------------------------------------------------------------- /src/fieldset/index.md: -------------------------------------------------------------------------------- 1 | # Fieldset 分组标签 2 | 3 | > 对相关元素进行分组 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/page-header/index.md: -------------------------------------------------------------------------------- 1 | # PageHeader 页头 2 | 3 | > 页头 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/app-toolbar/index.md: -------------------------------------------------------------------------------- 1 | # AppToolbar 应用工具栏 2 | 3 | > 应用工具栏 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/ext-switch/index.tsx: -------------------------------------------------------------------------------- 1 | import {ExtSwitch,ExtSwitchProps} from "./ExtSwitch" 2 | 3 | export type {ExtSwitchProps} 4 | export default ExtSwitch 5 | -------------------------------------------------------------------------------- /src/fieldset/index.tsx: -------------------------------------------------------------------------------- 1 | import {Fieldset,FieldsetProps} from './fieldset'; 2 | 3 | export type { FieldsetProps }; 4 | export default Fieldset; 5 | -------------------------------------------------------------------------------- /src/fields-mapping/index.md: -------------------------------------------------------------------------------- 1 | # FieldsMapping 字段映射 2 | 3 | > 字段映射 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/grid-table/index.tsx: -------------------------------------------------------------------------------- 1 | import GridTable, {GridTableProps} from './grid-table'; 2 | 3 | export type {GridTableProps} 4 | export default GridTable 5 | -------------------------------------------------------------------------------- /src/app-toolbar/index.tsx: -------------------------------------------------------------------------------- 1 | import AppToolbar, {AppToolbarProps} from "./app-toolbar" 2 | 3 | export type {AppToolbarProps} 4 | export default AppToolbar 5 | -------------------------------------------------------------------------------- /src/ext-input/index.md: -------------------------------------------------------------------------------- 1 | # ExtInput 扩展Input 2 | 3 | > 增加Input的只读模式,用于展示 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/ext-radio/index.md: -------------------------------------------------------------------------------- 1 | # ExtRadio 扩展Radio 2 | 3 | > 增加Radio的只读模式,用于展示 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/page-header/index.tsx: -------------------------------------------------------------------------------- 1 | import PageHeader, {PageHeaderProps} from "./page-header"; 2 | 3 | export type {PageHeaderProps} 4 | export default PageHeader 5 | -------------------------------------------------------------------------------- /src/picture-upload/index.md: -------------------------------------------------------------------------------- 1 | # PictureUpload 图片上传 2 | 3 | > 图片上传组件 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/table-toolbar/index.md: -------------------------------------------------------------------------------- 1 | # TableToolbar 表格工具栏 2 | 3 | > Table 的工具栏 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/ext-select/index.md: -------------------------------------------------------------------------------- 1 | # ExtSelect 扩展Select 2 | 3 | > 增加Select的只读模式,用于展示 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/ext-switch/index.md: -------------------------------------------------------------------------------- 1 | # ExtSwitch 扩展Switch 2 | 3 | > 增加Switch的只读模式,用于展示 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/video-upload/index.tsx: -------------------------------------------------------------------------------- 1 | import { VideoUpload, VideoUploadProps } from './VideoUpload'; 2 | export type { VideoUploadProps }; 3 | export default VideoUpload; 4 | -------------------------------------------------------------------------------- /src/avatar-editor/index.md: -------------------------------------------------------------------------------- 1 | # AvatarEditor 头像编辑器 2 | 3 | > 头像编辑器,带图片裁剪功能的头像组件 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/ext-checkbox/index.md: -------------------------------------------------------------------------------- 1 | # ExtCheckbox 扩展Checkbox 2 | 3 | > 增加Checkbox的只读模式,用于展示 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/ext-tree-select/index.tsx: -------------------------------------------------------------------------------- 1 | import {ExtTreeSelect,ExtTreeSelectProps} from "./ExtTreeSelect" 2 | 3 | export type {ExtTreeSelectProps} 4 | export default ExtTreeSelect 5 | -------------------------------------------------------------------------------- /src/fetch-select/index.tsx: -------------------------------------------------------------------------------- 1 | import { FetchSelect, FetchSelectProps } from './fetch-select'; 2 | 3 | export type { FetchSelectProps }; 4 | export default FetchSelect; 5 | -------------------------------------------------------------------------------- /src/field-wrapper/index.md: -------------------------------------------------------------------------------- 1 | # FileWrapper 字段包装器 2 | 3 | > 字段包装器,用于将内容包装成antd的表单字段风格 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/table-toolbar/index.tsx: -------------------------------------------------------------------------------- 1 | import TableToolbar, {TableToolbarProps} from "./table-toolbar"; 2 | 3 | export type {TableToolbarProps} 4 | export default TableToolbar 5 | -------------------------------------------------------------------------------- /src/avatar-editor/index.tsx: -------------------------------------------------------------------------------- 1 | import { AvatarEditor, AvatarEditorProps } from './AvatarEditor'; 2 | 3 | export type { AvatarEditorProps }; 4 | export default AvatarEditor; 5 | -------------------------------------------------------------------------------- /src/editable-desc/index.tsx: -------------------------------------------------------------------------------- 1 | import { EditableDesc, EditableDescProps } from './editable-desc'; 2 | 3 | export type { EditableDescProps }; 4 | export default EditableDesc; 5 | -------------------------------------------------------------------------------- /src/ext-tree-select/index.md: -------------------------------------------------------------------------------- 1 | # ExtTreeSelect 扩展TreeSelect 2 | 3 | > 增加TreeSelect的只读模式,用于展示 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/field-wrapper/index.tsx: -------------------------------------------------------------------------------- 1 | import { FieldWrapper, FieldWrapperProps } from './field-wrapper'; 2 | 3 | export type { FieldWrapperProps }; 4 | export default FieldWrapper; 5 | -------------------------------------------------------------------------------- /src/drawer-form/index.md: -------------------------------------------------------------------------------- 1 | # DrawerForm 抽屉表单 2 | 3 | > 抽屉表单 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/ext-date-picker/index.md: -------------------------------------------------------------------------------- 1 | # ExtDatePicker 扩展DatePicker 2 | 3 | > 增加DatePicker的只读模式,用于展示 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/ext-input-number/index.md: -------------------------------------------------------------------------------- 1 | # ExtInputNumber 扩展InputNumber 2 | 3 | > 增加InputNumber的只读模式,用于展示 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/ext-input-number/index.tsx: -------------------------------------------------------------------------------- 1 | import {ExtInputNumber,ExtInputNumberProps} from "./ExtInputNumber" 2 | 3 | export type {ExtInputNumberProps} 4 | export default ExtInputNumber 5 | -------------------------------------------------------------------------------- /src/fields-mapping/index.tsx: -------------------------------------------------------------------------------- 1 | import FieldsMapping, { FieldsMappingProps } from './fields-mapping'; 2 | 3 | export type { FieldsMappingProps }; 4 | export default FieldsMapping; 5 | -------------------------------------------------------------------------------- /src/modal-form/index.md: -------------------------------------------------------------------------------- 1 | # ModalForm 模态框表单 2 | 3 | > 模态框表单 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/picture-upload/index.tsx: -------------------------------------------------------------------------------- 1 | import { PictureUpload, PictureUploadProps } from './PictureUpload'; 2 | 3 | export type { PictureUploadProps }; 4 | export default PictureUpload; 5 | -------------------------------------------------------------------------------- /src/search-toolbar/index.tsx: -------------------------------------------------------------------------------- 1 | import SearchToolbar, { SearchToolbarProps } from './search-toolbar'; 2 | 3 | export type { SearchToolbarProps }; 4 | export default SearchToolbar; 5 | -------------------------------------------------------------------------------- /src/fetch-tree-select/index.md: -------------------------------------------------------------------------------- 1 | # FetchTreeSelect 远程数据的TreeSelect 2 | 3 | > 获取远程数据的TreeSelect 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/ext-form-field/index.tsx: -------------------------------------------------------------------------------- 1 | import {ExtFormField} from "./ExtFormField" 2 | 3 | export type {CommonExtFormFieldProps, ExtFormFieldProps} from "./ExtFormField" 4 | export default ExtFormField; 5 | -------------------------------------------------------------------------------- /src/fetch-tree-select/index.tsx: -------------------------------------------------------------------------------- 1 | import { FetchTreeSelect, FetchTreeSelectProps } from './fetch-tree-select'; 2 | 3 | export type { FetchTreeSelectProps }; 4 | export default FetchTreeSelect; 5 | -------------------------------------------------------------------------------- /src/editable-desc/index.md: -------------------------------------------------------------------------------- 1 | # EditableDesc 可编辑描述 2 | 3 | > 表单内容可编辑 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: require.resolve('@umijs/lint/dist/config/eslint'), 3 | rules: { 4 | 'no-unused-vars': 'off', 5 | '@typescript-eslint/no-unused-vars': ['off'], 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /.fatherrc.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'father'; 2 | 3 | export default defineConfig({ 4 | // more father config: https://github.com/umijs/father/blob/master/docs/config.md 5 | esm: { output: 'dist' }, 6 | }); 7 | -------------------------------------------------------------------------------- /src/grid-table/index.md: -------------------------------------------------------------------------------- 1 | # GridTable 表格 2 | 3 | > 表格 4 | 5 | 6 | 7 | > 撑满一个高度为300px的容器,内部高度滚动 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/search-toolbar/index.md: -------------------------------------------------------------------------------- 1 | # SearchToolbar 搜索工具栏 2 | 3 | > 搜索工具栏 4 | 5 | > 响应式栅格,配置建议 span={6} xxl={4} xl={6} lg={6} md={8} sm={12} xs={24} 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/util/index.md: -------------------------------------------------------------------------------- 1 | # Util 工具类 2 | 3 | > 工具类 4 | 5 | ReactDomUtils 6 | 7 | ReactDomUtils.render 通过静态函数创建组件,可以指定挂载到的元素,如不指定则挂载到body下。多用于创建模态框,抽屉等组件。 8 | 组件销毁,可以调用通过调用 onDestroy 函数。 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/verification-code-input/index.tsx: -------------------------------------------------------------------------------- 1 | import VerificationCodeInput, { 2 | VerificationCodeInputProps, 3 | } from './verification-code-input'; 4 | 5 | export type { VerificationCodeInputProps }; 6 | 7 | export default VerificationCodeInput; 8 | -------------------------------------------------------------------------------- /docs/guide.md: -------------------------------------------------------------------------------- 1 | # TrionesDev Antd Ext 2 | 3 | ## 概述 4 | 5 | 该项目是基于 antd 的一个扩展组件库 6 | 7 | ## 安装 8 | 9 | ``` 10 | npm install @trionesdev/antd-react-ext@latest 11 | ``` 12 | 13 | ``` 14 | yarn add @trionesdev/antd-react-ext@latest 15 | ``` 16 | -------------------------------------------------------------------------------- /src/field-wrapper/demo/base.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { FieldWrapper } from '../field-wrapper'; 3 | 4 | export default () => { 5 | return ( 6 | 7 |
这是一个字段
8 |
9 | ); 10 | }; 11 | -------------------------------------------------------------------------------- /src/fieldset/demo/base.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Fieldset} from '@trionesdev/antd-react-ext'; 3 | 4 | export default ()=>{ 5 | return
6 |
7 |
Fieldset Content
8 |
9 |
10 | } 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /src/fetch-select/index.md: -------------------------------------------------------------------------------- 1 | # FetchSelect 远程数据的Select 2 | 3 | > 获取远程数据的Select,例如列表场景下使用,可以通过行数据中组装initialValueOption,满足Select在不请求数据的情况下,直接渲染Select的场景 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/picture-upload/demo/base.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PictureUpload from '../index'; 3 | 4 | export default () => { 5 | return ( 6 |
7 | 8 |
只读模式
9 | 10 |
11 | ); 12 | }; 13 | -------------------------------------------------------------------------------- /src/layout/index.md: -------------------------------------------------------------------------------- 1 | # Layout 布局 2 | 3 | > 布局 4 | 5 | 6 | 7 | 8 | #### Layout 9 | 10 | #### Layout.Item 11 | 12 | #### Layout.Sider 13 | 14 | -------------------------------------------------------------------------------- /src/modal-form/demo/show.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from 'antd'; 2 | import React from 'react'; 3 | import ModalForm from '../index'; 4 | 5 | export default () => { 6 | return ( 7 |
8 | 15 |
16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /src/page-header/demo/base.tsx: -------------------------------------------------------------------------------- 1 | import { PageHeader } from '@trionesdev/antd-react-ext'; 2 | import { Button } from 'antd'; 3 | import React from 'react'; 4 | 5 | export default () => ( 6 | 11 | 发布 12 | , 13 | ]} 14 | /> 15 | ); 16 | -------------------------------------------------------------------------------- /src/drawer-form/demo/show.tsx: -------------------------------------------------------------------------------- 1 | import { DrawerForm } from '@trionesdev/antd-react-ext'; 2 | import { Button } from 'antd'; 3 | import React from 'react'; 4 | 5 | export default () => { 6 | return ( 7 |
8 | 15 |
16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "declaration": true, 5 | "skipLibCheck": true, 6 | "esModuleInterop": true, 7 | "jsx": "react", 8 | "baseUrl": "./", 9 | "paths": { 10 | "@@/*": [".dumi/tmp/*"], 11 | "@trionesdev/antd-react-ext": ["src"], 12 | "@trionesdev/antd-react-ext/*": ["src/*", "*"] 13 | } 14 | }, 15 | "include": [".dumirc.ts", "src/**/*"] 16 | } 17 | -------------------------------------------------------------------------------- /src/ext-radio/index.tsx: -------------------------------------------------------------------------------- 1 | import {ExtRadio as InternalExtRadio, ExtRadioProps} from "./ExtRadio" 2 | import {ExtRadioGroup, ExtRadioGroupProps} from "./ExtRadioGroup" 3 | 4 | type CompoundedComponent = typeof InternalExtRadio & { 5 | Group: typeof ExtRadioGroup 6 | } 7 | 8 | const ExtRadio = InternalExtRadio as CompoundedComponent 9 | ExtRadio.Group = ExtRadioGroup 10 | export type {ExtRadioProps, ExtRadioGroupProps} 11 | export default ExtRadio 12 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | pluginSearchDirs: false, 3 | plugins: [ 4 | require.resolve('prettier-plugin-organize-imports'), 5 | require.resolve('prettier-plugin-packagejson'), 6 | ], 7 | printWidth: 80, 8 | proseWrap: 'never', 9 | singleQuote: true, 10 | trailingComma: 'all', 11 | overrides: [ 12 | { 13 | files: '*.md', 14 | options: { 15 | proseWrap: 'preserve', 16 | }, 17 | }, 18 | ], 19 | }; 20 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | hero: 3 | title: TrionesDev Antd Ext 4 | description: 基于Antd组件库的扩展 5 | actions: 6 | - text: Get Started → 7 | link: /guide 8 | features: 9 | - title: 丰富 10 | emoji: 💎 11 | description: 基于antd组件库进行扩展,根据业务需求,封装出符合业务要求的组件 12 | - title: 简单 13 | emoji: 🌈 14 | description: 不引入额外的UI组件库,除了antd其他的效果直接开发 15 | - title: 易用 16 | emoji: 🚀 17 | description: 尽量复用antd组件的属性配置,使用方式尽可能简单 18 | --- 19 | 20 | antd 组件扩展库 21 | -------------------------------------------------------------------------------- /src/layout/index.tsx: -------------------------------------------------------------------------------- 1 | import { LayoutItem } from './item'; 2 | import { Layout as InternalLayout, LayoutProps } from './layout'; 3 | import { LayoutSider } from './sider'; 4 | 5 | type CompoundedComponent = typeof InternalLayout & { 6 | Item: typeof LayoutItem; 7 | Sider: typeof LayoutSider; 8 | }; 9 | const Layout = InternalLayout as CompoundedComponent; 10 | Layout.Item = LayoutItem; 11 | Layout.Sider = LayoutSider; 12 | export type { LayoutProps }; 13 | export default Layout; 14 | -------------------------------------------------------------------------------- /src/ext-checkbox/index.tsx: -------------------------------------------------------------------------------- 1 | import { ExtCheckBox as InternalExtCheckbox,ExtCheckBoxProps } from './ExtCheckbox'; 2 | import { ExtCheckboxGroup,ExtCheckBoxGroupProps } from './ExtCheckboxGroup'; 3 | 4 | 5 | type CompoundedComponent = typeof InternalExtCheckbox & { 6 | Group: typeof ExtCheckboxGroup; 7 | }; 8 | 9 | const ExtCheckbox = InternalExtCheckbox as CompoundedComponent; 10 | ExtCheckbox.Group = ExtCheckboxGroup; 11 | export type { ExtCheckBoxProps, ExtCheckBoxGroupProps }; 12 | export default ExtCheckbox; 13 | -------------------------------------------------------------------------------- /src/ext-date-picker/index.tsx: -------------------------------------------------------------------------------- 1 | import { ExtDatePicker as InternalExtDatePicker,ExtDatePickerProps } from './ExtDatePicker'; 2 | import { ExtDateRangePicker,ExtDateRangePickerProps } from './ExtDateRangePicker'; 3 | type CompoundedComponent = typeof InternalExtDatePicker &{ 4 | RangePicker: typeof ExtDateRangePicker; 5 | }; 6 | 7 | const ExtDatePicker = InternalExtDatePicker as CompoundedComponent; 8 | ExtDatePicker.RangePicker = ExtDateRangePicker; 9 | 10 | export type { ExtDatePickerProps,ExtDateRangePickerProps }; 11 | 12 | export default ExtDatePicker 13 | -------------------------------------------------------------------------------- /src/ext-switch/demo/base.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Switch } from 'antd'; 3 | import { ExtSwitch } from '@trionesdev/antd-react-ext'; 4 | 5 | export default ()=>{ 6 | const [readOnly, setReadOnly] = useState(false); 7 | const [value, setValue] = useState(); 8 | return ( 9 | <> 10 |
11 | 12 |
13 | 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /src/grid-table/demo/base.tsx: -------------------------------------------------------------------------------- 1 | import { GridTable } from '@trionesdev/antd-react-ext'; 2 | import React from 'react'; 3 | 4 | export default () => { 5 | const columns = [ 6 | { 7 | title: '姓名', 8 | dataIndex: 'name', 9 | }, 10 | { 11 | title: '年龄', 12 | dataIndex: 'age', 13 | }, 14 | ]; 15 | 16 | const dataScore = [ 17 | { name: '小明', age: 19 }, 18 | { name: '小王', age: 38 }, 19 | ]; 20 | 21 | return ( 22 |
23 | 24 |
25 | ); 26 | }; 27 | -------------------------------------------------------------------------------- /src/fieldset/styles.ts: -------------------------------------------------------------------------------- 1 | import { GlobalToken } from 'antd'; 2 | 3 | export const genFieldsetStyle = ( 4 | prefixCls: string, 5 | token: GlobalToken, 6 | ): any => { 7 | return { 8 | [`.${prefixCls}`]: { 9 | [`&-title`]: { 10 | color: token.colorTextBase, 11 | fontSize: token.fontSizeHeading5, 12 | fontWeight: token.fontWeightStrong, 13 | borderBottom: `1px solid ${token.colorBorder}`, 14 | marginBottom: '16px', 15 | paddingBottom: '8px', 16 | paddingTop: '16px', 17 | }, 18 | }, 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /src/fetch-select/demo/base.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import FetchSelect from '../index'; 3 | 4 | export default () => { 5 | const [value, setValue] = React.useState(2); 6 | return ( 7 | { 9 | return Promise.resolve([ 10 | { id: 1, name: '小明' }, 11 | { id: 2, name: '小红' }, 12 | ]); 13 | }} 14 | fieldNames={{ label: 'name', value: 'id' }} 15 | initialValueOptions={[{ id: 2, name: '小红' }]} 16 | value={value} 17 | onChange={setValue} 18 | /> 19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /src/verification-code-input/styles.ts: -------------------------------------------------------------------------------- 1 | import { CSSInterpolation } from '@ant-design/cssinjs'; 2 | import { GlobalToken } from 'antd'; 3 | 4 | export const genValidationCodeInputStyle = ( 5 | prefixCls: string, 6 | token: GlobalToken, 7 | ): CSSInterpolation => { 8 | return { 9 | [`.${prefixCls}`]: { 10 | [`&-send`]: { 11 | cursor: 'pointer', 12 | '&:hover': { 13 | color: token.colorPrimary, 14 | }, 15 | }, 16 | [`&-counting`]: { 17 | cursor: 'not-allowed', 18 | color: token.colorTextDisabled, 19 | }, 20 | }, 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /src/ext-date-picker/demo/base.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Input, Switch } from 'antd'; 3 | import { ExtDatePicker, ExtInput } from '@trionesdev/antd-react-ext'; 4 | 5 | export default () => { 6 | const [readOnly, setReadOnly] = useState(false); 7 | const [value, setValue] = useState(); 8 | return ( 9 |
10 |
11 | 12 |
13 |
14 | 15 | 16 |
17 |
18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /src/video-upload/styles.ts: -------------------------------------------------------------------------------- 1 | import { GlobalToken } from 'antd'; 2 | 3 | export const genVideoUploadStyle = ( 4 | prefixCls: string, 5 | token: GlobalToken, 6 | ): any => { 7 | return { 8 | [`.${prefixCls}`]: { 9 | borderRadius: token.borderRadiusLG, 10 | border: `1px solid ${token.colorBorder}`, 11 | padding: 8, 12 | [`&-player`]: { 13 | overflow: 'hidden', 14 | [`&-upload-wrapper`]: { 15 | height: '100%', 16 | display: 'flex', 17 | justifyContent: 'center', 18 | alignItems: 'center', 19 | }, 20 | }, 21 | }, 22 | }; 23 | }; 24 | -------------------------------------------------------------------------------- /src/avatar-editor/demo/base.tsx: -------------------------------------------------------------------------------- 1 | import { AvatarEditor } from '@trionesdev/antd-react-ext'; 2 | import { Button } from 'antd'; 3 | import React from 'react'; 4 | 5 | export default () => { 6 | const [avatar, setAvatar] = React.useState(''); 7 | 8 | return ( 9 |
10 | 11 | 20 | 21 |
22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /src/search-toolbar/styled.ts: -------------------------------------------------------------------------------- 1 | import {GlobalToken} from 'antd'; 2 | import {CSSInterpolation} from '@ant-design/cssinjs'; 3 | 4 | export const genSearchToolbarStyle = ( 5 | prefixCls: string, 6 | token: GlobalToken 7 | ): CSSInterpolation => { 8 | return { 9 | [`.${prefixCls}`]: { 10 | paddingTop: 16, 11 | paddingLeft: 16, 12 | paddingRight: 16, 13 | boxSizing: 'border-box', 14 | [`.ant-form-item`]:{ 15 | marginBottom:16 16 | }, 17 | [`&-col-hidden`]: { 18 | display: 'none', 19 | }, 20 | }, 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /src/hooks/useCssInJs.ts: -------------------------------------------------------------------------------- 1 | import { CSSInterpolation, Theme, useStyleRegister } from '@ant-design/cssinjs'; 2 | import { theme } from 'antd'; 3 | 4 | const { useToken } = theme; 5 | 6 | export type CssInJsProps = { 7 | prefix: string; 8 | styleFun?: (prefix: string, token?: any) => CSSInterpolation; 9 | }; 10 | export const useCssInJs = (params?: CssInJsProps) => { 11 | const { theme, token, hashId } = useToken(); 12 | // @ts-ignore 13 | const ss: Theme = theme; 14 | useStyleRegister( 15 | { theme: ss, token, hashId, path: [`${params?.prefix}`] }, 16 | () => [params?.styleFun?.(params?.prefix, token)], 17 | ); 18 | return { hashId }; 19 | }; 20 | -------------------------------------------------------------------------------- /src/editable-desc/styles.ts: -------------------------------------------------------------------------------- 1 | import { CSSInterpolation } from '@ant-design/cssinjs'; 2 | import { GlobalToken } from 'antd'; 3 | 4 | export const genEditableDescStyle = ( 5 | prefixCls: string, 6 | token: GlobalToken, 7 | ): CSSInterpolation => { 8 | return { 9 | [`.${prefixCls}`]: { 10 | display: 'inline-flex', 11 | gap: 4, 12 | alignItems: 'center', 13 | lineHeight: '31px', 14 | [`&-col-auto`]: { 15 | flex: '1 auto', 16 | }, 17 | [`&-render`]: { 18 | minWidth: 24, 19 | minHeight: 31, 20 | [`&.editable`]: { 21 | cursor: 'pointer', 22 | }, 23 | }, 24 | }, 25 | }; 26 | }; 27 | -------------------------------------------------------------------------------- /src/fetch-tree-select/demo/base.tsx: -------------------------------------------------------------------------------- 1 | import { FetchTreeSelect } from '@trionesdev/antd-react-ext'; 2 | import React from 'react'; 3 | 4 | export default () => { 5 | const [value, setValue] = React.useState(2); 6 | return ( 7 | { 10 | return Promise.resolve([ 11 | { id: 1, name: '小明', children: [{ id: 11, name: '小明1' }] }, 12 | { id: 2, name: '小红' }, 13 | ]); 14 | }} 15 | fieldNames={{ label: 'name', value: 'id' }} 16 | initialValueOptions={[{ id: 2, name: '小红' }]} 17 | value={value} 18 | onChange={setValue} 19 | /> 20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /src/modal-form/demo/base.tsx: -------------------------------------------------------------------------------- 1 | import { ModalForm } from '@trionesdev/antd-react-ext'; 2 | import { Button, Form, Input } from 'antd'; 3 | import React from 'react'; 4 | 5 | export default () => { 6 | const [open, setOpen] = React.useState(false); 7 | return ( 8 |
9 | 新建} 11 | open={open} 12 | onTriggerClick={() => { 13 | setOpen(true); 14 | }} 15 | 16 | onCancel={() => setOpen(false)} 17 | title={`表单`} 18 | > 19 | 20 | 21 | 22 | 23 |
24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /src/ext-input-number/demo/base.tsx: -------------------------------------------------------------------------------- 1 | import React, {useState} from "react"; 2 | import {Input, Switch} from "antd"; 3 | import {ExtInputNumber} from "@trionesdev/antd-react-ext"; 4 | 5 | export default () => { 6 | const [readOnly, setReadOnly] = useState(false); 7 | const [value, setValue] = useState(); 8 | return
9 |
10 | 11 |
12 |
13 | { 14 | setValue(v) 15 | }}/> 16 | { 17 | setValue(v) 18 | }}/> 19 |
20 |
21 | } 22 | -------------------------------------------------------------------------------- /src/table-toolbar/styles.ts: -------------------------------------------------------------------------------- 1 | import {GlobalToken} from "antd"; 2 | import {CSSInterpolation} from "@ant-design/cssinjs"; 3 | 4 | export const genTableToolbarStyle = ( 5 | prefixCls: string, 6 | token: GlobalToken, 7 | ): CSSInterpolation => { 8 | return { 9 | [`.${prefixCls}`]: { 10 | display: 'flex', 11 | justifyContent: 'space-between', 12 | padding: '8px', 13 | boxSizing: 'border-box', 14 | [`&-title`]: { 15 | display: 'flex', 16 | justifyContent: 'flex-start', 17 | alignItems: 'center', 18 | }, 19 | [`&-extra`]: { 20 | display: 'flex', 21 | justifyContent: 'flex-end', 22 | }, 23 | }, 24 | }; 25 | }; 26 | -------------------------------------------------------------------------------- /.github/workflows/gh-pages.yml: -------------------------------------------------------------------------------- 1 | name: github pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - master # default branch 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | deploy: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | - run: npm install 17 | # 文档编译命令,如果是 react 模板需要修改为 npm run docs:build 18 | - run: npm run docs:build 19 | - name: Deploy 20 | uses: peaceiris/actions-gh-pages@v3 21 | with: 22 | deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }} 23 | # github_token: ${{ secrets.GITHUB_TOKEN }} 24 | # 文档目录,如果是 react 模板需要修改为 docs-dist 25 | publish_dir: ./docs-dist 26 | -------------------------------------------------------------------------------- /src/app-toolbar/demo/base.tsx: -------------------------------------------------------------------------------- 1 | import { AppToolbar } from '@trionesdev/antd-react-ext'; 2 | import { Button } from 'antd'; 3 | import React from 'react'; 4 | 5 | export default () => { 6 | const items = [ 7 | { 8 | key: 'user', 9 | label: '用户', 10 | }, 11 | { 12 | key: 'permission', 13 | label: '权限', 14 | }, 15 | { 16 | key: '1', 17 | label: '功能清单', 18 | }, 19 | ]; 20 | return ( 21 |
22 | 27 | 设置 28 | , 29 | ]} 30 | /> 31 |
32 | ); 33 | }; 34 | -------------------------------------------------------------------------------- /src/drawer-form/demo/base.tsx: -------------------------------------------------------------------------------- 1 | import { DrawerForm } from '@trionesdev/antd-react-ext'; 2 | import { Button, Form, Input } from 'antd'; 3 | import React from 'react'; 4 | 5 | export default () => { 6 | const [open, setOpen] = React.useState(false); 7 | return ( 8 |
9 | 新建} 11 | title={`表单`} 12 | open={open} 13 | onTriggerClick={() => { 14 | setOpen(true); 15 | }} 16 | onClose={() => setOpen(false)} 17 | onCancel={() => setOpen(false)} 18 | > 19 | 20 | 21 | 22 | 23 |
24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /src/ext-input/demo/base.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Input, Switch } from 'antd'; 3 | import { ExtInput } from '@trionesdev/antd-react-ext'; 4 | 5 | export default () => { 6 | const [readOnly, setReadOnly] = useState(false); 7 | const [value, setValue] = useState(); 8 | return ( 9 |
10 |
11 | 12 |
13 |
14 | { 17 | console.log(e.target.value); 18 | setValue(e.target.value); 19 | }} 20 | readonly={readOnly} 21 | /> 22 |
23 |
24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /src/layout/demo/layout-nest.tsx: -------------------------------------------------------------------------------- 1 | import { Layout } from '@trionesdev/antd-react-ext'; 2 | import React from 'react'; 3 | 4 | export default () => { 5 | return ( 6 |
7 | 8 | header 9 | 10 | left 11 | content 12 | 13 | right 14 | 15 | 16 | footer 17 | 18 |
19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /src/ext-input/ExtInput.tsx: -------------------------------------------------------------------------------- 1 | import { Input, InputProps } from 'antd'; 2 | import React, { FC } from 'react'; 3 | import ExtFormField, { CommonExtFormFieldProps } from '../ext-form-field'; 4 | 5 | export type ExtInputProps = InputProps & CommonExtFormFieldProps; 6 | 7 | export const ExtInput: FC = ({ 8 | readonly, 9 | valueRender, 10 | defaultRender, 11 | emptyPlaceholder, 12 | ...rest 13 | }) => { 14 | return ( 15 | 23 | 24 | 25 | ); 26 | }; 27 | -------------------------------------------------------------------------------- /src/field-wrapper/styles.ts: -------------------------------------------------------------------------------- 1 | import { CSSInterpolation } from '@ant-design/cssinjs'; 2 | import { GlobalToken } from 'antd'; 3 | 4 | export const genFieldWrapperStyle = ( 5 | prefixCls: string, 6 | token: GlobalToken, 7 | ): CSSInterpolation => { 8 | return { 9 | [`.${prefixCls}`]: { 10 | boxSizing: 'border-box', 11 | border: `1px solid ${token.colorBorder}`, 12 | borderRadius: token.borderRadius, 13 | paddingBlock: 4, 14 | paddingInline: 11, 15 | '&:hover': { 16 | border: `1px solid ${token.colorPrimaryBorderHover}`, 17 | }, 18 | [`&-sm`]: { 19 | paddingBlock: 0, 20 | paddingInline: 11, 21 | }, 22 | [`&-lg`]: { 23 | paddingBlock: 7, 24 | paddingInline: 11, 25 | }, 26 | }, 27 | }; 28 | }; 29 | -------------------------------------------------------------------------------- /src/ext-input/index.tsx: -------------------------------------------------------------------------------- 1 | import {ExtInput as InternalExtInput, ExtInputProps} from "./ExtInput" 2 | import {ExtInputPassword, ExtInputPasswordProps} from "./ExtInputPassword" 3 | import {ExtInputTextArea, ExtInputTextAreaProps} from "./ExtInputTextArea" 4 | import {ExtInputOTP, ExtInputOPTProps} from "./ExtInputOTP" 5 | 6 | type CompoundedComponent = typeof InternalExtInput & { 7 | TextArea: typeof ExtInputTextArea; 8 | Password: typeof ExtInputPassword; 9 | OTP: typeof ExtInputOTP; 10 | }; 11 | 12 | const ExtInput = InternalExtInput as CompoundedComponent; 13 | ExtInput.TextArea = ExtInputTextArea; 14 | ExtInput.Password = ExtInputPassword; 15 | ExtInput.OTP = ExtInputOTP; 16 | 17 | export type {ExtInputProps, ExtInputTextAreaProps, ExtInputPasswordProps, ExtInputOPTProps} 18 | export default ExtInput 19 | -------------------------------------------------------------------------------- /src/table-toolbar/demo/base.tsx: -------------------------------------------------------------------------------- 1 | import { GridTable, TableToolbar } from '@trionesdev/antd-react-ext'; 2 | import { Button } from 'antd'; 3 | import React from 'react'; 4 | 5 | export default () => { 6 | const columns = [ 7 | { 8 | title: '姓名', 9 | dataIndex: 'name', 10 | }, 11 | { 12 | title: '年龄', 13 | dataIndex: 'age', 14 | }, 15 | ]; 16 | 17 | const dataScore = [ 18 | { name: '小明', age: 19 }, 19 | { name: '小王', age: 38 }, 20 | ]; 21 | 22 | const tableBar = ( 23 | 新建用户]} 26 | /> 27 | ); 28 | 29 | return ( 30 | 36 | ); 37 | }; 38 | -------------------------------------------------------------------------------- /src/fieldset/fieldset.tsx: -------------------------------------------------------------------------------- 1 | import { useCssInJs } from '@trionesdev/antd-react-ext'; 2 | import classNames from 'classnames'; 3 | import React, { FC } from 'react'; 4 | import { genFieldsetStyle } from './styles'; 5 | 6 | export type FieldsetProps = { 7 | children?: React.ReactNode; 8 | title?: React.ReactNode; 9 | legend?: React.ReactNode; 10 | }; 11 | export const Fieldset: FC = ({ children, title, legend }) => { 12 | const prefixCls = 'triones-ant-fieldset'; 13 | const { hashId } = useCssInJs({ 14 | prefix: prefixCls, 15 | styleFun: genFieldsetStyle, 16 | }); 17 | return ( 18 |
19 | {(title || legend) && ( 20 |
21 | {title || legend} 22 |
23 | )} 24 | {children} 25 |
26 | ); 27 | }; 28 | -------------------------------------------------------------------------------- /src/util/SessionStorageUtils.ts: -------------------------------------------------------------------------------- 1 | type StorageDataType = { 2 | data: any; 3 | expire: number; 4 | }; 5 | export class SessionStorageUtils { 6 | static setExpireItem(key?: string, data?: any, expire?: number) { 7 | if (!key || !data) { 8 | return; 9 | } 10 | sessionStorage.setItem( 11 | key, 12 | JSON.stringify({ 13 | data, 14 | expire: Date.now() + (expire ?? 0) * 1000, 15 | }), 16 | ); 17 | } 18 | 19 | static getExpireItem(key?: string, expireRemove?: boolean) { 20 | if (!key) { 21 | return null; 22 | } 23 | const val = sessionStorage.getItem(key!); 24 | if (val) { 25 | const storageData = JSON.parse(val); 26 | if (Date.now() > storageData.expire) { 27 | sessionStorage.removeItem(key!); 28 | return null; 29 | } else { 30 | return storageData?.data; 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/layout/item.tsx: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | import React, { FC } from 'react'; 3 | import { useCssInJs } from '../hooks'; 4 | import { genItemStyle } from './styles'; 5 | 6 | export type LayoutItemProps = { 7 | children?: React.ReactNode; 8 | className?: string | undefined; 9 | style?: React.CSSProperties; 10 | auto?: boolean; 11 | }; 12 | export const LayoutItem: FC = ({ 13 | children, 14 | className, 15 | style, 16 | auto, 17 | }) => { 18 | const prefixCls = 'triones-ant-layout-item'; 19 | const { hashId } = useCssInJs({ 20 | prefix: prefixCls, 21 | styleFun: genItemStyle, 22 | }); 23 | return ( 24 |
33 | {children} 34 |
35 | ); 36 | }; 37 | -------------------------------------------------------------------------------- /src/util/ReactDomUtils.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | 4 | export type ReactDomRenderType = { 5 | onDestroy?: () => void; 6 | container?: HTMLElement; 7 | }; 8 | 9 | export class ReactDomUtils { 10 | static render(children: React.ReactElement, container?: HTMLElement) { 11 | const div = document.createElement('div'); 12 | const body = container || document.body; 13 | body.appendChild(div); 14 | 15 | const root = ReactDOM.createRoot(div as HTMLElement); 16 | 17 | function destroy() { 18 | setTimeout(() => root.unmount()); 19 | if (div.parentNode) { 20 | div.parentNode.removeChild(div); 21 | } 22 | } 23 | 24 | root.render( 25 | React.cloneElement(children as React.ReactElement, { 26 | ...children!.props, 27 | onDestroy: destroy, 28 | container: div, 29 | }), 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/search-toolbar/demo/base.tsx: -------------------------------------------------------------------------------- 1 | import { SearchToolbar } from '@trionesdev/antd-react-ext'; 2 | import { Input } from 'antd'; 3 | import React from 'react'; 4 | 5 | export default () => { 6 | const items: any[] = [ 7 | { 8 | label: '年龄', 9 | name: 'age', 10 | children: , 11 | }, 12 | { 13 | label: '姓名', 14 | name: 'name', 15 | children: , 16 | }, 17 | { 18 | label: '手机号码', 19 | name: 'phone', 20 | children: , 21 | }, 22 | { 23 | label: '地址', 24 | name: 'addr', 25 | children: , 26 | }, 27 | { 28 | label: '邮箱', 29 | name: 'email', 30 | children: , 31 | }, 32 | ]; 33 | return ( 34 | 44 | ); 45 | }; 46 | -------------------------------------------------------------------------------- /src/ext-input/ExtInputPassword.tsx: -------------------------------------------------------------------------------- 1 | import { Input } from 'antd'; 2 | import { PasswordProps } from 'antd/es/input/Password'; 3 | import React, { FC } from 'react'; 4 | import ExtFormField from '../ext-form-field'; 5 | 6 | export type ExtInputPasswordProps = PasswordProps & { 7 | readonly?: boolean; 8 | valueRender?: ((value?: any) => React.ReactNode) | React.ReactNode; 9 | defaultRender?: React.ReactNode; 10 | emptyPlaceholder?: React.ReactNode; 11 | }; 12 | export const ExtInputPassword: FC = ({ 13 | readonly, 14 | valueRender, 15 | defaultRender, 16 | emptyPlaceholder, 17 | ...rest 18 | }) => { 19 | return ( 20 | 28 | 29 | 30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /src/ext-input/ExtInputTextArea.tsx: -------------------------------------------------------------------------------- 1 | import { Input } from 'antd'; 2 | import { TextAreaProps } from 'antd/es/input/TextArea'; 3 | import React, { FC } from 'react'; 4 | import ExtFormField from '../ext-form-field'; 5 | 6 | export type ExtInputTextAreaProps = TextAreaProps & { 7 | readonly?: boolean; 8 | valueRender?: ((value?: any) => React.ReactNode) | React.ReactNode; 9 | defaultRender?: React.ReactNode; 10 | emptyPlaceholder?: React.ReactNode; 11 | }; 12 | export const ExtInputTextArea: FC = ({ 13 | readonly, 14 | valueRender, 15 | defaultRender, 16 | emptyPlaceholder, 17 | ...rest 18 | }) => { 19 | return ( 20 | 28 | 29 | 30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /src/layout/layout.tsx: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | import React, { FC } from 'react'; 3 | import { useCssInJs } from '../hooks'; 4 | import { genLayoutStyle } from './styles'; 5 | 6 | export type LayoutProps = { 7 | children?: React.ReactNode; 8 | className?: string | undefined; 9 | style?: React.CSSProperties; 10 | direction?: 'vertical' | 'horizontal'; 11 | gap?: number; 12 | }; 13 | 14 | export const Layout: FC = ({ 15 | children, 16 | className, 17 | style, 18 | direction = 'horizontal', 19 | gap, 20 | }) => { 21 | const prefixCls = 'triones-ant-layout'; 22 | 23 | const { hashId } = useCssInJs({ 24 | prefix: prefixCls, 25 | styleFun: genLayoutStyle, 26 | }); 27 | 28 | return ( 29 |
38 | {children} 39 |
40 | ); 41 | }; 42 | -------------------------------------------------------------------------------- /src/ext-checkbox/demo/base.tsx: -------------------------------------------------------------------------------- 1 | 2 | import React, { useState } from 'react'; 3 | import { Radio, Switch } from 'antd'; 4 | import { ExtCheckbox } from '@trionesdev/antd-react-ext'; 5 | 6 | 7 | export default ()=>{ 8 | const [readOnly, setReadOnly] = useState(false); 9 | const [value, setValue] = useState(); 10 | return ( 11 | <> 12 |
13 | 14 |
15 |
16 | 17 | 是 18 | 19 | { 23 | setValue(v); 24 | }} 25 | options={[ 26 | { 27 | label: '是', 28 | value: 1, 29 | }, 30 | { 31 | label: '否', 32 | value: 2, 33 | }, 34 | ]} 35 | /> 36 |
37 | 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /src/fetch-select/demo/set-initial-option.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from 'antd'; 2 | import React, { useState } from 'react'; 3 | import FetchSelect from '../index'; 4 | 5 | export default () => { 6 | const [value, setValue] = React.useState(3); 7 | const [initialValueOption, setInitialValueOption] = useState(); 8 | return ( 9 |
10 | { 12 | return Promise.resolve([ 13 | { id: 1, name: '小明' }, 14 | { id: 2, name: '小红' }, 15 | ]); 16 | }} 17 | fieldNames={{ label: 'name', value: 'id' }} 18 | dropdownFetch={true} 19 | initialValueOptions={initialValueOption} 20 | fixedOptions={[{ id: 4, name: '小蓝' }]} 21 | value={value} 22 | onChange={setValue} 23 | /> 24 | 31 |
32 | ); 33 | }; 34 | -------------------------------------------------------------------------------- /src/ext-radio/demo/base.tsx: -------------------------------------------------------------------------------- 1 | import { ExtRadio } from '@trionesdev/antd-react-ext'; 2 | import React, { useState } from 'react'; 3 | import { Radio, Switch } from 'antd'; 4 | 5 | 6 | export default ()=>{ 7 | const [readOnly, setReadOnly] = useState(false); 8 | const [value, setValue] = useState(); 9 | return ( 10 | <> 11 |
12 | 13 |
14 |
15 | 16 | 是 17 | 18 | { 22 | setValue(v.target.value); 23 | }} 24 | options={[ 25 | { 26 | label: '是', 27 | value: 1, 28 | }, 29 | { 30 | label: '否', 31 | value: 2, 32 | }, 33 | ]} 34 | > 35 | 36 |
37 | 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /src/page-header/styles.tsx: -------------------------------------------------------------------------------- 1 | import {GlobalToken} from "antd"; 2 | import {CSSInterpolation} from "@ant-design/cssinjs"; 3 | 4 | export const genPageHeaderStyle = ( 5 | prefixCls: string, 6 | token: GlobalToken, 7 | ): CSSInterpolation => { 8 | return { 9 | [`.${prefixCls}`]: { 10 | backgroundColor: 'white', 11 | padding: '8px', 12 | [`&-breadcrumb`]: {}, 13 | [`&-heading`]: { 14 | display: 'flex', 15 | justifyContent: 'space-between', 16 | [`&-title`]: { 17 | color: '#000000d9', 18 | fontWeight: 600, 19 | fontSize: '20px', 20 | lineHeight: '32px', 21 | overflow: 'hidden', 22 | whiteSpace: 'nowrap', 23 | textOverflow: 'ellipsis', 24 | }, 25 | [`&-sub-title`]: { 26 | color: '#00000073', 27 | fontSize: '14px', 28 | lineHeight: 1.5715, 29 | overflow: 'hidden', 30 | whiteSpace: 'nowrap', 31 | textOverflow: 'ellipsis', 32 | }, 33 | }, 34 | }, 35 | }; 36 | }; 37 | -------------------------------------------------------------------------------- /.dumirc.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'dumi'; 2 | import process from 'node:process'; 3 | const apiParserEnable = 4 | process.env.NODE_ENV === 'production' || process.env.API_PARSER == 'true'; 5 | export default defineConfig({ 6 | base: '/antd-react-ext/', 7 | publicPath: '/antd-react-ext/', 8 | outputPath: 'docs-dist', 9 | apiParser: apiParserEnable ? {} : false, 10 | resolve: { 11 | // 配置入口文件路径,API 解析将从这里开始 12 | entryFile: './src/index.ts', 13 | }, 14 | themeConfig: { 15 | editLink: true, 16 | name: 'Antd Extensions', 17 | logo: '/antd-react-ext/logo.png', 18 | nav: [ 19 | { title: '指南', link: '/guide' }, 20 | { title: '组件', link: '/components' }, 21 | ], 22 | socialLinks: { 23 | github: 'https://github.com/trionesdev/antd-react-ext', 24 | zhihu: 'https://www.ithere.net/', 25 | }, 26 | footer: 27 | '
Copyright © 2015-present TrionesDev
友情链接: 书阙 TrionesDev
', 28 | }, 29 | 30 | }); 31 | -------------------------------------------------------------------------------- /src/ext-tree-select/demo/base.tsx: -------------------------------------------------------------------------------- 1 | import React, {useState} from "react"; 2 | import {Switch} from "antd"; 3 | import {ExtTreeSelect} from "@trionesdev/antd-react-ext"; 4 | 5 | export default ()=>{ 6 | const [readOnly, setReadOnly] = useState(false); 7 | const [value, setValue] = useState(); 8 | const [value2, setValue2] = useState(); 9 | const [options, setOptions] = React.useState([ 10 | {value: 1, label: '小明'}, 11 | {value: 2, label: '小红'}, 12 | ]); 13 | 14 | return ( 15 |
16 |
17 | 18 |
19 |
20 | 27 | 35 |
36 |
37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /src/ext-checkbox/ExtCheckbox.tsx: -------------------------------------------------------------------------------- 1 | import { Checkbox, CheckboxProps } from 'antd'; 2 | import React, { FC, memo } from 'react'; 3 | import ExtFormField from '../ext-form-field'; 4 | 5 | export type ExtCheckBoxProps = CheckboxProps & { 6 | readonly?: boolean; 7 | valueRender?: 8 | | ((value?: any, options?: any) => React.ReactNode) 9 | | React.ReactNode; 10 | defaultRender?: React.ReactNode; 11 | emptyPlaceholder?: React.ReactNode; 12 | }; 13 | 14 | export const ExtCheckBox: FC = memo( 15 | ({ 16 | readonly = false, 17 | valueRender, 18 | defaultRender, 19 | emptyPlaceholder, 20 | ...rest 21 | }) => { 22 | const handleRender = (value: any, options: any) => { 23 | return rest.children; 24 | }; 25 | return ( 26 | 34 | 35 | 36 | ); 37 | }, 38 | ); 39 | -------------------------------------------------------------------------------- /src/ext-input-number/ExtInputNumber.tsx: -------------------------------------------------------------------------------- 1 | import {InputNumber, InputNumberProps} from "antd"; 2 | import React, {FC} from "react"; 3 | import ExtFormField from "../ext-form-field"; 4 | 5 | export type ExtInputNumberProps = InputNumberProps & { 6 | readonly?: boolean; 7 | valueRender?: ((value?: any) => React.ReactNode) | React.ReactNode; 8 | defaultRender?: React.ReactNode; 9 | emptyPlaceholder?: React.ReactNode; 10 | }; 11 | 12 | export const ExtInputNumber: FC = ({readonly, valueRender, defaultRender,emptyPlaceholder, ...rest}) => { 13 | 14 | const handleRender = (value: any, options: any) => { 15 | if (value){ 16 | return <> {value}{rest.suffix} 17 | } 18 | return emptyPlaceholder 19 | }; 20 | 21 | return ( 22 | 31 | 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/ext-radio/ExtRadio.tsx: -------------------------------------------------------------------------------- 1 | import { Radio, RadioProps } from 'antd'; 2 | import React, { FC } from 'react'; 3 | import ExtFormField from '../ext-form-field'; 4 | 5 | export type ExtRadioProps = RadioProps & { 6 | readonly?: boolean; 7 | valueRender?: 8 | | ((value?: any, options?: any) => React.ReactNode) 9 | | React.ReactNode; 10 | defaultRender?: React.ReactNode; 11 | emptyPlaceholder?: React.ReactNode; 12 | }; 13 | export const ExtRadio: FC = ({ 14 | readonly = false, 15 | valueRender, 16 | defaultRender, 17 | emptyPlaceholder, 18 | ...rest 19 | }) => { 20 | const handleValueOptions = (value: any) => {}; 21 | 22 | const handleRender = (value: any, options: any) => { 23 | return rest.children; 24 | }; 25 | 26 | return ( 27 | 36 | 37 | 38 | ); 39 | }; 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) fengxiaotx@163.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ant Design 扩展组件库 2 | 3 | [![NPM version](https://img.shields.io/npm/v/@trionesdev/antd-react-ext.svg?style=flat)](https://npmjs.org/package/@trionesdev/antd-react-ext) 4 | [![NPM downloads](http://img.shields.io/npm/dm/@trionesdev/antd-react-ext.svg?style=flat)](https://npmjs.org/package/@trionesdev/antd-react-ext) 5 | 6 | > 基于And Design 组件库的扩展组件库。需要依赖 [ant-design](https://github.com/ant-design/ant-design) 7 | 8 | [文档网站](https://trionesdev.github.io/antd-react-ext/components/table-toolbar) 9 | 10 | --- 11 | ## 组件列表 12 | - [x] AppToolbar 应用工具栏 13 | - [x] AvatarEditor 头像编辑器 14 | - [x] DrawerForm 抽屉表单 15 | - [x] EditableDesc 可编辑描述 16 | - [x] FetchSelect 远程数据的Select 17 | - [x] FetchTreeSelect 远程数据的TreeSelect 18 | - [x] FieldsMapping 字段映射 19 | - [x] FileWrapper 字段包装器 20 | - [x] GridTable 表格 21 | - [x] Layout 布局 22 | - [x] ModalForm 模态框表单 23 | - [x] PageHeader 页头 24 | - [x] PictureUpload 图片上传 25 | - [x] SearchToolbar 搜索工具栏 26 | - [x] TableToolbar 表格工具栏 27 | 28 | ## LICENSE 29 | 30 | MIT 31 | 32 | 33 | --- 34 | 35 | ## 关注我们,一起交流 36 | > 留言回复不及时,可以通过关注公众号联系我们 37 |
38 | 39 |
40 | 41 | 42 | -------------------------------------------------------------------------------- /src/ext-radio/ExtRadioGroup.tsx: -------------------------------------------------------------------------------- 1 | import { Radio, RadioGroupProps } from 'antd'; 2 | import React, { FC } from 'react'; 3 | import ExtFormField from '../ext-form-field'; 4 | 5 | export type ExtRadioGroupProps = Omit & { 6 | readonly?: boolean; 7 | valueRender?: 8 | | ((value?: any, option?: any) => React.ReactNode) 9 | | React.ReactNode; 10 | defaultRender?: React.ReactNode; 11 | emptyPlaceholder?: React.ReactNode; 12 | }; 13 | 14 | export const ExtRadioGroup: FC = ({ 15 | readonly = false, 16 | valueRender, 17 | defaultRender, 18 | emptyPlaceholder, 19 | ...rest 20 | }) => { 21 | const handleRender = (value: any, options: any) => { 22 | return options?.find((item: any) => item.value === value)?.label || value; 23 | }; 24 | 25 | return ( 26 | 36 | 37 | 38 | ); 39 | 40 | }; 41 | -------------------------------------------------------------------------------- /src/ext-select/demo/base.tsx: -------------------------------------------------------------------------------- 1 | import {Switch} from 'antd'; 2 | import React, {useState} from 'react'; 3 | import {ExtSelect} from "../ExtSelect"; 4 | 5 | export default () => { 6 | const [readOnly, setReadOnly] = useState(false); 7 | const [value, setValue] = useState(); 8 | const [value2, setValue2] = useState(); 9 | const [options, setOptions] = React.useState([ 10 | {value: 1, label: '小明'}, 11 | {value: 2, label: '小红'}, 12 | ]); 13 | return ( 14 |
15 |
16 | 17 |
18 |
19 | 23 | 27 | 31 |
32 |
33 | ); 34 | }; 35 | -------------------------------------------------------------------------------- /src/grid-table/demo/fit-table.tsx: -------------------------------------------------------------------------------- 1 | import { GridTable } from '@trionesdev/antd-react-ext'; 2 | import React from 'react'; 3 | 4 | export default () => { 5 | const columns = [ 6 | { 7 | title: '姓名', 8 | dataIndex: 'name', 9 | }, 10 | { 11 | title: '年龄', 12 | dataIndex: 'age', 13 | }, 14 | ]; 15 | 16 | const dataScore = [ 17 | { name: '小明', age: 19 }, 18 | { name: '小王', age: 38 }, 19 | { name: '小王', age: 38 }, 20 | { name: '小王', age: 38 }, 21 | { name: '小王', age: 38 }, 22 | { name: '小王', age: 38 }, 23 | { name: '小王', age: 38 }, 24 | { name: '小王', age: 38 }, 25 | { name: '小王', age: 38 }, 26 | { name: '小王', age: 38 }, 27 | { name: '小王', age: 38 }, 28 | { name: '小王', age: 38 }, 29 | { name: '小王', age: 38 }, 30 | { name: '小王', age: 38 }, 31 | { name: '小王', age: 38 }, 32 | { name: '小王', age: 38 }, 33 | { name: '小王', age: 38 }, 34 | { name: '小王', age: 38 }, 35 | { name: '小王', age: 38 }, 36 | { name: '小王', age: 38 }, 37 | { name: '小王', age: 38 }, 38 | { name: '小王', age: 38 }, 39 | ]; 40 | 41 | return ( 42 |
43 | 44 |
45 | ); 46 | }; 47 | -------------------------------------------------------------------------------- /src/table-toolbar/table-toolbar.tsx: -------------------------------------------------------------------------------- 1 | import { Space } from 'antd'; 2 | import classNames from 'classnames'; 3 | import React, { FC } from 'react'; 4 | import { useCssInJs } from '../hooks'; 5 | import { genTableToolbarStyle } from './styles'; 6 | 7 | export type TableToolbarProps = { 8 | className?: string; 9 | style?: React.CSSProperties; 10 | /** 11 | * @description 标题 12 | * @default null 13 | */ 14 | title?: React.ReactNode; 15 | /** 16 | * @description 操作区,位于 title 行的行尾 17 | * @default [] 18 | */ 19 | extra?: React.ReactNode; 20 | }; 21 | 22 | const TableToolbar: FC = ({ 23 | className, 24 | style, 25 | title, 26 | extra, 27 | }) => { 28 | const prefixCls = 'triones-ant-table-toolbar'; 29 | 30 | const { hashId } = useCssInJs({ 31 | prefix: prefixCls, 32 | styleFun: genTableToolbarStyle, 33 | }); 34 | 35 | return ( 36 |
37 | 38 | {title} 39 | 40 | 41 | {extra} 42 | 43 |
44 | ); 45 | }; 46 | export default TableToolbar; 47 | -------------------------------------------------------------------------------- /src/ext-input/ExtInputOTP.tsx: -------------------------------------------------------------------------------- 1 | import {Input} from 'antd'; 2 | import {OTPProps} from 'antd/es/input/OTP'; 3 | import React, {FC} from 'react'; 4 | import ExtFormField from '../ext-form-field'; 5 | 6 | export type ExtInputOPTProps = OTPProps & { 7 | readonly?: boolean; 8 | valueRender?: ((value?: any) => React.ReactNode) | React.ReactNode; 9 | defaultRender?: React.ReactNode; 10 | emptyPlaceholder?: React.ReactNode; 11 | }; 12 | export const ExtInputOTP: FC = ({ 13 | readonly, 14 | valueRender, 15 | defaultRender, 16 | emptyPlaceholder, 17 | ...rest 18 | }) => { 19 | return ( 20 | 28 | 29 | 30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /src/ext-switch/ExtSwitch.tsx: -------------------------------------------------------------------------------- 1 | import {Switch, SwitchProps} from "antd"; 2 | import React, {FC} from "react"; 3 | import ExtFormField from "../ext-form-field"; 4 | 5 | export type ExtSwitchProps = SwitchProps & { 6 | readonly?: boolean; 7 | valueRender?: 8 | | ((value?: any, option?: any) => React.ReactNode) 9 | | React.ReactNode; 10 | defaultRender?: React.ReactNode; 11 | }; 12 | 13 | export const ExtSwitch: FC = ({ 14 | readonly = false, 15 | valueRender, 16 | defaultRender, 17 | ...rest 18 | }) => { 19 | 20 | const handleRender = (value: any, options: any) => { 21 | return value ? (rest.checkedChildren || '是') : (rest.unCheckedChildren || '否'); 22 | }; 23 | 24 | return ( 25 | { 32 | return handleRender(value, options); 33 | }} 34 | > 35 | 36 | 37 | ); 38 | }; -------------------------------------------------------------------------------- /src/app-toolbar/styles.ts: -------------------------------------------------------------------------------- 1 | import { CSSInterpolation } from '@ant-design/cssinjs'; 2 | import { GlobalToken } from 'antd'; 3 | 4 | export const genAppToolbarStyle = ( 5 | prefixCls: string, 6 | token: GlobalToken, 7 | ): CSSInterpolation => { 8 | return { 9 | [`.${prefixCls}`]: { 10 | height: '60px', 11 | display: 'flex', 12 | alignItems: 'center', 13 | justifyContent: 'center', 14 | borderBottom: `1px solid ${token.colorBorder}`, 15 | padding: '0px 8px', 16 | boxSizing: 'border-box', 17 | [`&-heading`]: { 18 | display: 'flex', 19 | justifyContent: 'space-between', 20 | alignItems: 'center', 21 | width: '100%', 22 | gap: '8px', 23 | 24 | [`&-left`]: { 25 | ['.ant-avatar']: { 26 | display: 'flex', 27 | }, 28 | [`&-title`]: { 29 | color: token.colorTextBase, 30 | fontWeight: 600, 31 | fontSize: '20px', 32 | lineHeight: '32px', 33 | overflow: 'hidden', 34 | whiteSpace: 'nowrap', 35 | textOverflow: 'ellipsis', 36 | }, 37 | }, 38 | [`&-right`]: {}, 39 | [`.ant-menu-horizontal`]: { 40 | flex: '1 auto', 41 | height: '60px', 42 | backgroundColor: 'inherit', 43 | li: { 44 | display: 'flex', 45 | alignItems: 'center', 46 | }, 47 | }, 48 | }, 49 | }, 50 | }; 51 | }; 52 | -------------------------------------------------------------------------------- /src/util/demo/reactdom.tsx: -------------------------------------------------------------------------------- 1 | import { ReactDomUtils } from '@trionesdev/antd-react-ext'; 2 | import { Button, Modal } from 'antd'; 3 | import React, { useRef, useState } from 'react'; 4 | 5 | const El = ({ onDestroy }: { onDestroy?: () => void }) => { 6 | return ( 7 |
8 | ReactDomUtils 9 |
10 | ); 11 | }; 12 | 13 | const ModalEl = ({ 14 | container, 15 | onDestroy, 16 | }: { 17 | container?: any; 18 | onDestroy?: () => void; 19 | }) => { 20 | const [open, setOpen] = useState(true); 21 | const handleClose = () => { 22 | onDestroy?.(); 23 | }; 24 | return ( 25 | 31 | 这是一个可以关闭的弹窗 32 | 33 | ); 34 | }; 35 | 36 | export default () => { 37 | const devRef = useRef(); 38 | 39 | return ( 40 | <> 41 |
42 | 49 | 56 | 63 | 64 | ); 65 | }; 66 | -------------------------------------------------------------------------------- /src/field-wrapper/field-wrapper.tsx: -------------------------------------------------------------------------------- 1 | import { useCssInJs } from '@trionesdev/antd-react-ext'; 2 | import { SizeType } from 'antd/es/config-provider/SizeContext'; 3 | import classNames from 'classnames'; 4 | import React, { FC, useMemo } from 'react'; 5 | import { genFieldWrapperStyle } from './styles'; 6 | 7 | export type FieldWrapperProps = { 8 | /** 9 | * @description 类名 10 | * @default 11 | */ 12 | className?: string; 13 | /** 14 | * @description 样式 15 | * @default 16 | */ 17 | style?: React.CSSProperties; 18 | children?: React.ReactNode; 19 | /** 20 | * @description 大小 21 | * @default middle 22 | */ 23 | size?: SizeType; 24 | [key: string]: any; 25 | }; 26 | 27 | export const FieldWrapper: FC = ({ 28 | className, 29 | style, 30 | size, 31 | children, 32 | ...props 33 | }) => { 34 | const prefixCls = 'triones-ant-field-wrapper'; 35 | const { hashId } = useCssInJs({ 36 | prefix: prefixCls, 37 | styleFun: genFieldWrapperStyle, 38 | }); 39 | 40 | const sizeCls = useMemo(() => { 41 | switch (size) { 42 | case 'small': 43 | return `${prefixCls}-sm`; 44 | case 'large': 45 | return `${prefixCls}-lg`; 46 | default: 47 | return ''; 48 | } 49 | }, [size]); 50 | 51 | return ( 52 |
57 | {children} 58 |
59 | ); 60 | }; 61 | -------------------------------------------------------------------------------- /src/modal-form/modal-inner-form.tsx: -------------------------------------------------------------------------------- 1 | import { Form, FormProps } from 'antd'; 2 | import React, { forwardRef, useEffect, useImperativeHandle } from 'react'; 3 | 4 | export interface ModalInnerFormHandle { 5 | submit: () => void; 6 | resetFields: (fields?: any[]) => void; 7 | } 8 | 9 | type ModalInnerFormProps = { 10 | children: React.ReactElement | React.ReactNode; 11 | formValues?: any; 12 | onSubmit?: (values: any) => Promise | void; 13 | } & Omit; 14 | export const ModalInnerForm = forwardRef< 15 | ModalInnerFormHandle, 16 | ModalInnerFormProps 17 | >(({ children, formValues, onSubmit, ...rest }, componentRef) => { 18 | const [form] = Form.useForm(); 19 | /**支持自己传入form,外部传入form的话使用外部传入的form */ 20 | const finalFrom = rest.form ? rest.form : form; 21 | useImperativeHandle(componentRef, () => { 22 | return { 23 | submit: () => { 24 | finalFrom 25 | .validateFields() 26 | .then((values: any) => { 27 | onSubmit?.(values); 28 | }) 29 | .catch((ex: any) => { 30 | console.error(ex.message); 31 | }); 32 | }, 33 | resetFields: (fields?: any[]) => { 34 | finalFrom.resetFields(fields); 35 | }, 36 | }; 37 | }); 38 | 39 | useEffect(() => { 40 | if (formValues) { 41 | finalFrom.setFieldsValue(formValues); 42 | } 43 | }, [formValues]); 44 | 45 | return ( 46 |
47 | {children} 48 |
49 | ); 50 | }); 51 | export default ModalInnerForm; 52 | -------------------------------------------------------------------------------- /src/drawer-form/drawer-inner-form.tsx: -------------------------------------------------------------------------------- 1 | import {Form, FormInstance, FormProps} from 'antd'; 2 | import React, {forwardRef, useEffect, useImperativeHandle} from 'react'; 3 | 4 | export interface DrawerInnerFormHandle { 5 | submit: () => void; 6 | } 7 | 8 | export type DrawerInnerFormProps = { 9 | children?: React.ReactElement | React.ReactNode; 10 | formValues?: any; 11 | onSubmit?: (values: any, form?: FormInstance) => Promise | void; 12 | } & Omit; 13 | 14 | export const DrawerInnerForm = forwardRef< 15 | DrawerInnerFormHandle, 16 | DrawerInnerFormProps 17 | >(({children, formValues, onSubmit, ...rest}, componentRef) => { 18 | const [form] = Form.useForm(); 19 | /**支持自己传入form,外部传入form的话使用外部传入的form */ 20 | const finalFrom = rest.form ? rest.form : form; 21 | useImperativeHandle(componentRef, () => { 22 | return { 23 | submit: () => { 24 | finalFrom 25 | .validateFields() 26 | .then((values: any) => { 27 | if (onSubmit) { 28 | return onSubmit(values, finalFrom); 29 | } else { 30 | return Promise.resolve(); 31 | } 32 | }) 33 | .catch((ex: any) => { 34 | console.log(ex); 35 | }); 36 | }, 37 | }; 38 | }); 39 | 40 | useEffect(() => { 41 | if (formValues) { 42 | finalFrom?.setFieldsValue(formValues); 43 | } 44 | }, [formValues]); 45 | 46 | return ( 47 |
48 | {children} 49 |
50 | ); 51 | }); 52 | export default DrawerInnerForm; 53 | -------------------------------------------------------------------------------- /src/picture-upload/styles.ts: -------------------------------------------------------------------------------- 1 | import { GlobalToken } from 'antd'; 2 | 3 | export const genPictureUploadStyle = ( 4 | prefixCls: string, 5 | token: GlobalToken, 6 | ): any => { 7 | return { 8 | [`.${prefixCls}`]: { 9 | [`&-image`]: { 10 | position: 'relative', 11 | objectFit: 'cover', 12 | borderRadius: token.borderRadiusLG, 13 | '&:hover': { 14 | [`.${prefixCls}-image-tooltip`]: { 15 | visibility: 'visible', 16 | }, 17 | }, 18 | '.ant-image': { 19 | borderRadius: 4, 20 | overflow: 'hidden', 21 | '.ant-image-img':{ 22 | objectFit: 'cover', 23 | } 24 | }, 25 | [`&-tooltip`]: { 26 | position: 'absolute', 27 | left: '50%', 28 | bottom: 8, 29 | transform: 'translateX(-50%)', 30 | background: 'rgba(18, 18, 18, 0.75) none repeat scroll 0% 0%', 31 | borderRadius: 4, 32 | visibility: 'hidden', 33 | button: { 34 | color: 'white', 35 | '&:hover': { 36 | color: 'white!important', 37 | }, 38 | }, 39 | '.ant-divider': { 40 | borderInlineStart: '1px solid white', 41 | }, 42 | }, 43 | }, 44 | '.ant-upload-wrapper': { 45 | width: '100%', 46 | height: '100%', 47 | display: 'flex', 48 | '.ant-upload': { 49 | width: '100%!important', 50 | height: '100%!important', 51 | }, 52 | }, 53 | }, 54 | }; 55 | }; 56 | -------------------------------------------------------------------------------- /src/ext-checkbox/ExtCheckboxGroup.tsx: -------------------------------------------------------------------------------- 1 | import { Checkbox } from 'antd'; 2 | import { CheckboxGroupProps } from 'antd/es/checkbox'; 3 | import React, { FC, memo } from 'react'; 4 | import ExtFormField from '../ext-form-field'; 5 | import { includes } from 'lodash-es'; 6 | 7 | export type ExtCheckBoxGroupProps = Omit & { 8 | readonly?: boolean; 9 | valueRender?: 10 | | ((value?: any, options?: any) => React.ReactNode) 11 | | React.ReactNode; 12 | defaultRender?: React.ReactNode; 13 | emptyPlaceholder?: React.ReactNode; 14 | }; 15 | 16 | export const ExtCheckboxGroup: FC = memo( 17 | ({ 18 | readonly = false, 19 | valueRender, 20 | defaultRender, 21 | emptyPlaceholder, 22 | ...rest 23 | }) => { 24 | 25 | const handleValueOptions = (value: any) => { 26 | return rest.options?.filter((option:any) => { 27 | return includes(value, option['value']); 28 | }); 29 | }; 30 | 31 | const handleRender = (value: any, options: any) => { 32 | return options?.map((option: any) => option['label']).join(', '); 33 | }; 34 | 35 | return ( 36 | 46 | 47 | 48 | ); 49 | }, 50 | ); 51 | -------------------------------------------------------------------------------- /src/ext-date-picker/ExtDatePicker.tsx: -------------------------------------------------------------------------------- 1 | import ExtFormField from '../ext-form-field'; 2 | import {DatePicker, DatePickerProps} from 'antd'; 3 | import dayjs from 'dayjs'; 4 | import React, {FC} from 'react'; 5 | 6 | export type ExtDatePickerProps = DatePickerProps & { 7 | readonly?: boolean; 8 | valueRender?: ((value?: any) => React.ReactNode) | React.ReactNode; 9 | defaultRender?: React.ReactNode; 10 | emptyPlaceholder?: React.ReactNode; 11 | }; 12 | export const ExtDatePicker: FC = ({ 13 | readonly, 14 | valueRender, 15 | defaultRender, 16 | emptyPlaceholder, 17 | ...rest 18 | }) => { 19 | const handleFiledRender = (value: any, valueOptions: any) => { 20 | if (!value) { 21 | return undefined; 22 | } 23 | switch (rest.picker) { 24 | case 'month': 25 | return dayjs(value).format('YYYY-MM'); 26 | case 'quarter': 27 | return dayjs(value).format('YYYY-Q'); 28 | case 'week': 29 | return dayjs(value).format('YYYY-WW'); 30 | case 'year': 31 | return dayjs(value).format('YYYY'); 32 | default: { 33 | const format = 34 | (rest.format as any)?.format || rest.format || 'YYYY-MM-DD'; 35 | return dayjs(value).format(format); 36 | } 37 | } 38 | }; 39 | 40 | return ( 41 | 50 | 51 | 52 | ); 53 | }; 54 | -------------------------------------------------------------------------------- /src/fields-mapping/demo/base.tsx: -------------------------------------------------------------------------------- 1 | import { FieldsMapping } from '@trionesdev/antd-react-ext'; 2 | import React from 'react'; 3 | import { Column } from '../fields-mapping'; 4 | 5 | export default () => { 6 | const sourceColumns: Column[] = [ 7 | { 8 | key: 'field', 9 | title: '字段', 10 | width: 80, 11 | primaryKey: true, 12 | }, 13 | { 14 | key: 'type', 15 | title: '类型', 16 | width: 80, 17 | }, 18 | ]; 19 | const sourceData = [ 20 | { 21 | field: 'id', 22 | type: 'string', 23 | }, 24 | { 25 | field: 'name', 26 | type: 'string', 27 | }, 28 | { 29 | field: 'age', 30 | type: 'int', 31 | }, 32 | ]; 33 | 34 | const targetColumns: Column[] = [ 35 | { 36 | key: 'field', 37 | title: '字段', 38 | width: 80, 39 | primaryKey: true, 40 | }, 41 | { 42 | key: 'type', 43 | title: '类型', 44 | width: 80, 45 | }, 46 | ]; 47 | const targetData = [ 48 | { 49 | field: 'id', 50 | type: 'string', 51 | }, 52 | 53 | { 54 | field: 'age', 55 | type: 'int', 56 | }, 57 | { 58 | field: 'name', 59 | type: 'string', 60 | }, 61 | ]; 62 | 63 | const mappingData = [ 64 | { sourceKey: 'id', targetKey: 'id' }, 65 | // {sourceKey:'age',targetKey:'age'}, 66 | // {sourceKey:'name',targetKey:'name'}, 67 | ]; 68 | 69 | return ( 70 | 77 | ); 78 | }; 79 | -------------------------------------------------------------------------------- /src/layout/demo/base.tsx: -------------------------------------------------------------------------------- 1 | import { DesktopOutlined, PieChartOutlined } from '@ant-design/icons'; 2 | import { Layout } from '@trionesdev/antd-react-ext'; 3 | import { Menu } from 'antd'; 4 | import React from 'react'; 5 | 6 | export default () => { 7 | const menuItems = [ 8 | { 9 | key: '1', 10 | label: 'Option A', 11 | icon: , 12 | }, 13 | { 14 | key: '2', 15 | label: 'Option B', 16 | icon: , 17 | }, 18 | ]; 19 | 20 | return ( 21 |
22 |
23 | 24 | sss 25 | 26 | auto 27 | 28 | 29 |
30 |
31 | 32 | sss 33 | 34 | auto 35 | 36 | 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | auto 45 | 46 | 47 |
48 |
49 | ); 50 | }; 51 | -------------------------------------------------------------------------------- /src/modal-form/modal-form.tsx: -------------------------------------------------------------------------------- 1 | import { FormInstance, FormProps, Modal, ModalProps } from 'antd'; 2 | import React, { FC, useRef, useState, type SyntheticEvent } from 'react'; 3 | import ModalInnerForm, { ModalInnerFormHandle } from './modal-inner-form'; 4 | 5 | export type ModalFormProps = { 6 | /** 7 | * @description 触发标签 8 | * @default 9 | */ 10 | trigger?: React.ReactNode | React.ReactElement; 11 | 12 | /** 13 | * @description 触发标签点击 14 | * @default 15 | */ 16 | onTriggerClick?: () => void; 17 | /** 18 | * @description 提交回调 19 | * @default 20 | */ 21 | onSubmit?: (values: any) => Promise | void; 22 | form?: FormInstance; 23 | formValues?: any; 24 | formProps?: Omit; 25 | } & ModalProps; 26 | 27 | export const ModalForm: FC = ({ 28 | trigger, 29 | onTriggerClick, 30 | onSubmit, 31 | form, 32 | formValues, 33 | formProps, 34 | ...rest 35 | }) => { 36 | const formRef = useRef({} as ModalInnerFormHandle); 37 | const [scopeOpen, setScopeOpen] = useState(false); 38 | 39 | const handleSubmit = (e: React.MouseEvent) => { 40 | rest?.onOk?.(e); 41 | if (onSubmit) { 42 | formRef.current.submit(); 43 | } 44 | }; 45 | 46 | return ( 47 | <> 48 | {trigger && React.isValidElement(trigger) && 49 | React.cloneElement(trigger, { 50 | ...trigger.props, 51 | onClick: (e?: SyntheticEvent) => { 52 | trigger.props.onClick?.(e); 53 | onTriggerClick?.(); 54 | }, 55 | })} 56 | 57 | 64 | {rest.children} 65 | 66 | 67 | 68 | ); 69 | }; 70 | -------------------------------------------------------------------------------- /src/modal-form/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { type SyntheticEvent } from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import { ModalForm as InternalModalForm, ModalFormProps } from './modal-form'; 4 | 5 | type ModalFormShowProps = Omit< 6 | ModalFormProps, 7 | 'open' | 'afterOpenChange' | 'onClose' | 'onCancel' 8 | > & { 9 | /** 10 | * @description 关闭回调,如果有回调函数,返回结果是 Promise.resolve(true) 的时候关闭。如果没有回调函数,直接关闭 11 | * @default 12 | */ 13 | onClose?: (e: SyntheticEvent) => Promise; 14 | /** 15 | * @description 取消回调 16 | * @default 17 | */ 18 | onCancel?: (e: React.MouseEvent) => Promise; 19 | }; 20 | const show = (options?: ModalFormShowProps) => { 21 | const div = document.createElement('div'); 22 | const body = document.body; 23 | body.appendChild(div); 24 | const root = ReactDOM.createRoot(div as HTMLElement); 25 | 26 | function destroy() { 27 | setTimeout(() => root.unmount()); 28 | if (div.parentNode) { 29 | div.parentNode.removeChild(div); 30 | } 31 | } 32 | 33 | const handleClose = (e: SyntheticEvent) => { 34 | if (options?.onClose) { 35 | options?.onClose(e).then((result) => { 36 | destroy(); 37 | }); 38 | } else { 39 | destroy(); 40 | } 41 | }; 42 | 43 | const handleCancel = (e: React.MouseEvent) => { 44 | if (options?.onCancel) { 45 | options?.onCancel?.(e).then((res) => { 46 | destroy(); 47 | }); 48 | } else { 49 | destroy(); 50 | } 51 | }; 52 | 53 | root.render( 54 | , 59 | ); 60 | }; 61 | 62 | type CompoundedComponent = typeof InternalModalForm & { 63 | show: typeof show; 64 | }; 65 | export type { ModalFormProps }; 66 | const ModalForm = InternalModalForm as CompoundedComponent; 67 | ModalForm.show = show; 68 | export default ModalForm; 69 | -------------------------------------------------------------------------------- /src/drawer-form/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import { 4 | DrawerFormProps, 5 | DrawerForm as InternalDrawerForm, 6 | } from './drawer-form'; 7 | 8 | type DrawerFormShowProps = Omit< 9 | DrawerFormProps, 10 | 'open' | 'afterOpenChange' | 'onClose' | 'onCancel' 11 | > & { 12 | /** 13 | * @description 关闭回调,如果有回调函数,返回结果是 Promise.resolve(true) 的时候关闭。如果没有回调函数,直接关闭 14 | * @default 15 | */ 16 | onClose?: (e: React.MouseEvent | React.KeyboardEvent) => Promise; 17 | /** 18 | * @description 取消回调 19 | * @default 20 | */ 21 | onCancel?: (e: React.MouseEvent) => Promise; 22 | }; 23 | 24 | const show = (options?: DrawerFormShowProps) => { 25 | const div = document.createElement('div'); 26 | const body = document.body; 27 | body.appendChild(div); 28 | const root = ReactDOM.createRoot(div as HTMLElement); 29 | 30 | function destroy() { 31 | setTimeout(() => root.unmount()); 32 | if (div.parentNode) { 33 | div.parentNode.removeChild(div); 34 | } 35 | } 36 | 37 | const handleClose = (e: React.MouseEvent | React.KeyboardEvent) => { 38 | if (options?.onClose) { 39 | options?.onClose(e).then((result) => { 40 | destroy(); 41 | }); 42 | } else { 43 | destroy(); 44 | } 45 | }; 46 | 47 | const handleCancel = (e: React.MouseEvent) => { 48 | if (options?.onCancel) { 49 | options?.onCancel?.(e).then((res) => { 50 | destroy(); 51 | }); 52 | } else { 53 | destroy(); 54 | } 55 | }; 56 | 57 | root.render( 58 | , 64 | ); 65 | }; 66 | type CompoundedComponent = typeof InternalDrawerForm & { 67 | show: typeof show; 68 | }; 69 | 70 | export type { DrawerFormProps }; 71 | const DrawerForm = InternalDrawerForm as CompoundedComponent; 72 | DrawerForm.show = show; 73 | export default DrawerForm; 74 | -------------------------------------------------------------------------------- /src/app-toolbar/app-toolbar.tsx: -------------------------------------------------------------------------------- 1 | import { Avatar, AvatarProps, Menu, MenuProps, Space } from 'antd'; 2 | import classNames from 'classnames'; 3 | import React, { FC } from 'react'; 4 | import { useCssInJs } from '../hooks'; 5 | import { genAppToolbarStyle } from './styles'; 6 | import { isEmpty } from 'lodash-es'; 7 | 8 | export type AppToolbarProps = { 9 | className?: string; 10 | style?: React.CSSProperties; 11 | avatar?: AvatarProps; 12 | /** 13 | * @description 标题 14 | * @default null 15 | */ 16 | title?: React.ReactNode; 17 | /** 18 | * @description 其他组件,在右侧展示 19 | * @default [] 20 | */ 21 | extra?: React.ReactNode; 22 | /** 23 | * @description 导航菜单项 24 | * @default null 25 | */ 26 | navItems?: MenuProps['items']; 27 | selectedKeys?: string[]; 28 | }; 29 | const AppToolbar: FC = ({ 30 | className, 31 | style, 32 | avatar, 33 | title, 34 | extra, 35 | navItems, 36 | selectedKeys, 37 | }) => { 38 | const prefixCls = 'triones-ant-app-toolbar'; 39 | const { hashId } = useCssInJs({ 40 | prefix: prefixCls, 41 | styleFun: genAppToolbarStyle, 42 | }); 43 | 44 | return
45 |
46 |
47 | 48 | {avatar && ( 49 | 52 | )} 53 |
56 | {title} 57 |
58 |
59 |
60 | {!isEmpty(navItems) && ( 61 | 66 | )} 67 |
68 | {extra} 69 |
70 |
71 |
72 | }; 73 | export default AppToolbar; 74 | -------------------------------------------------------------------------------- /src/avatar-editor/styles.ts: -------------------------------------------------------------------------------- 1 | import {CSSInterpolation} from '@ant-design/cssinjs'; 2 | import {GlobalToken} from 'antd'; 3 | 4 | export const genAvatarEditorStyle = ( 5 | prefixCls: string, 6 | token: GlobalToken, 7 | ): CSSInterpolation => { 8 | return { 9 | [`.${prefixCls}`]: { 10 | boxSizing: 'border-box', 11 | borderRadius: token.borderRadius, 12 | overflow: 'hidden', 13 | [`&-avatar`]: { 14 | position: 'relative', 15 | '.ant-avatar-square': { 16 | borderRadius: '0px!important', 17 | }, 18 | [`&:hover`]: { 19 | [`.${prefixCls}-avatar-mask`]: { 20 | visibility: 'visible', 21 | }, 22 | }, 23 | [`&-mask`]: { 24 | position: 'absolute', 25 | top: 0, 26 | left: 0, 27 | width: '100%', 28 | height: '100%', 29 | background: 'rgba(0,0,0,0.3)', 30 | display: 'flex', 31 | visibility: 'hidden', 32 | alignItems: 'center', 33 | justifyContent: 'center', 34 | color: '#fff', 35 | cursor: 'pointer', 36 | transition: 'all .3s', 37 | '&:hover': { 38 | background: 'rgba(0,0,0,.8)', 39 | }, 40 | label: { 41 | fontSize: '30px', 42 | '.anticon': { 43 | cursor: 'pointer', 44 | }, 45 | }, 46 | }, 47 | }, 48 | '.avatar-uploader': { 49 | width: '100%', 50 | height: '100%', 51 | '.ant-upload-select': { 52 | width: '100%!important', 53 | height: '100%!important', 54 | '.ant-upload': { 55 | fontSize: '30px', 56 | color: '#0000004a', 57 | }, 58 | }, 59 | }, 60 | }, 61 | }; 62 | }; 63 | 64 | export const genAvatarCropModalStyle = ( 65 | prefixCls: string, 66 | token: GlobalToken, 67 | ): CSSInterpolation => { 68 | return { 69 | [`.${prefixCls}`]: { 70 | '&-cropper': {}, 71 | img: { 72 | width: '100%', 73 | maxWidth: '100%', 74 | }, 75 | }, 76 | }; 77 | }; 78 | -------------------------------------------------------------------------------- /src/ext-select/ExtSelect.tsx: -------------------------------------------------------------------------------- 1 | import {Select, SelectProps} from 'antd'; 2 | import { includes } from 'lodash-es'; 3 | import React, {FC} from 'react'; 4 | import ExtFormField from '../ext-form-field'; 5 | 6 | export type ExtSelectProps = Omit & { 7 | readonly?: boolean; 8 | valueRender?: 9 | | ((value?: any, option?: any) => React.ReactNode) 10 | | React.ReactNode; 11 | defaultRender?: React.ReactNode; 12 | }; 13 | export const ExtSelect: FC = ({ 14 | readonly = false, 15 | valueRender, 16 | defaultRender, 17 | ...rest 18 | }) => { 19 | const valueField = rest.fieldNames?.value ?? 'value'; 20 | const labelField = rest.fieldNames?.label ?? 'label'; 21 | 22 | const handleValueOptions = (value: any) => { 23 | if (rest.mode === 'multiple' || rest.mode === 'tags') { 24 | return rest.options?.filter((option) => { 25 | return includes(value, option[valueField]); 26 | }); 27 | } else { 28 | return rest.options?.find((option) => option[valueField] === value); 29 | } 30 | }; 31 | 32 | const handleRender = (value: any, options: any) => { 33 | if (rest.mode === 'multiple' || rest.mode === 'tags') { 34 | return options?.map((option: any) => option[labelField]).join(', '); 35 | } else { 36 | return options?.[labelField]; 37 | } 38 | }; 39 | 40 | return ( 41 | { 49 | return handleRender(value, options); 50 | }} 51 | > 52 | 29 | 30 | 31 | 32 | { 34 | if (v === 'MALE') { 35 | return '男'; 36 | } else { 37 | return '女'; 38 | } 39 | }} 40 | clickEdit={true} 41 | manualChange={true} 42 | editIcon={false} 43 | editing={editing} 44 | > 45 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 70 | 77 | 80 | 81 | 82 | 83 | ); 84 | }; 85 | -------------------------------------------------------------------------------- /src/editable-desc/demo/form.tsx: -------------------------------------------------------------------------------- 1 | import { EditableDesc } from '@trionesdev/antd-react-ext'; 2 | import { Button, Form, Input, Select, Space } from 'antd'; 3 | import React, { useEffect, useState } from 'react'; 4 | 5 | export default () => { 6 | const [form] = Form.useForm(); 7 | const [editing, setEditing] = useState(false); 8 | 9 | const handleSave = () => { 10 | form.validateFields().then((values) => { 11 | setEditing(false); 12 | console.log(values); 13 | }); 14 | }; 15 | 16 | useEffect(() => { 17 | form.setFieldsValue({ 18 | name: '小明', 19 | gender: 'MALE', 20 | description: '描述', 21 | }); 22 | }, []); 23 | 24 | return ( 25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 | { 34 | if (v === 'MALE') { 35 | return '男'; 36 | } else { 37 | return '女'; 38 | } 39 | }} 40 | editing={editing} 41 | > 42 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |