conditional-selection 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -12,11 +12,15 @@
12
12
  "vite-plugin-dts": "^4.5.4"
13
13
  },
14
14
  "name": "conditional-selection",
15
- "version": "1.0.0",
15
+ "version": "1.0.1",
16
16
  "description": "",
17
17
  "main": "dist/ui.cjs.js",
18
18
  "module": "dist/ui.es.js",
19
19
  "types": "dist/types/ui.es.d.ts",
20
+ "files": [
21
+ "dist",
22
+ "README.md"
23
+ ],
20
24
  "directories": {
21
25
  "doc": "docs",
22
26
  "example": "example"
@@ -1,205 +0,0 @@
1
- {
2
- // RHook 代码片段 version 1.0.0, 如果和插件版本号不对应,卸载插件重装或手动升级插件
3
- "获取组件实例": {
4
- "scope": "javascript,typescript",
5
- "prefix": "useCompRef",
6
- "body": ["const ${1:componentInstance} = useCompRef(${2});"],
7
- "description": "useCompRef ",
8
- },
9
- "表单弹窗Hook": {
10
- "scope": "javascript,typescript",
11
- "prefix": "useModalForm",
12
- "body": [
13
- "const { showModal } = useModalForm<{$1}>({",
14
- " title: '操作',",
15
- " layout: 'vertical',",
16
- " stateDefault: {},",
17
- " formGroup: [],",
18
- " onSubmit: async formState => {",
19
- " console.log('onSubmit:formState -->', formState);",
20
- " // await apiXXX(formState)",
21
- " },",
22
- "});",
23
- ],
24
- "description": "表单弹窗Hook",
25
- },
26
- "useModal配合RForm,快速实现新增等功能": {
27
- "scope": "javascript,typescript",
28
- "prefix": "useModal",
29
- "body": [
30
- "const refForm = ref<RFormInstance | null>(null);",
31
- "const formState = reactive({});",
32
- "",
33
- "const { visible, loading, open, close, confirm } = useModal(",
34
- " async () => {",
35
- " await refForm.value?.getFormRef()?.validate();",
36
- " },",
37
- " {",
38
- " afterClose() {",
39
- " refForm.value?.getFormRef()?.resetFields();",
40
- " },",
41
- " },",
42
- ");",
43
- ],
44
- "description": "useModal配合RForm,快速实现新增等功能",
45
- },
46
- "处理表格行合并场景": {
47
- "scope": "javascript,typescript",
48
- "prefix": "useTableMerge",
49
- "body": [
50
- "const list = ref<T[]>([]);",
51
- "const { tableColumns, updateColumns } = useTableMerge(list, {",
52
- " columns:[],",
53
- " // ⚠️ 使用后请删除下列注释 !!!!!",
54
- " mergeColumnFields: ['category_name'],",
55
- " /* 当前列的行合并默认会比较前所有列,可配置跳过某些前置列的比对 */",
56
- " excludeColumnFields: ['id'],",
57
- " /* 默认使用列的dataIndex作为比对字段,dataIndex不存在会取key,可以通过配置修改比对的字段 */",
58
- " compareMap: { category_name: 'category_id' },",
59
- " /* 与customCell一样,可自定义处理,自定义返回的配置项与内部配置的customCell方法返回值合并 */",
60
- " customCellHandler(row, rowIndex, column) {",
61
- " return {}",
62
- " }",
63
- "});",
64
- ],
65
- "description": "处理表格行合并场景",
66
- },
67
- "便捷处理表格选择项": {
68
- "scope": "javascript,typescript",
69
- "prefix": "useTableSelection",
70
- "body": [
71
- "// 在 RTable 中添加 :tableProps=\"{ rowSelection }\" 使用后请删除!;",
72
- "const { rowSelection, selectedRowKeys } = useTableSelection({",
73
- " key: '${1:id}', // 如果是 id 请删除,默认为 id",
74
- " /* 禁止选择项,使用后请删除! */",
75
- " disabledSelect: record => record.td_checkbox_disable,",
76
- " // 根据需求去定",
77
- " rowSelection: {",
78
- " selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT, Table.SELECTION_NONE],",
79
- " },",
80
- "});",
81
- ],
82
- "description": "便捷处理表格选择项",
83
- },
84
- // R组件代码片段
85
- "表格操作栏组件": {
86
- "scope": "html",
87
- "prefix": "RButtonGroup",
88
- "body": ["<RButtonGroup", " :btn-group=\"btnGroup\"", " :group-handler=\"groupHandler\"", "/>"],
89
- "description": "表格操作栏组件",
90
- },
91
- "通用表单": {
92
- "scope": "html",
93
- "prefix": "RForm",
94
- "body": ["<RForm", " ref=\"formRef\"", " v-model:value=\"formState\"", " :form-group=\"formGroup\"", "/>"],
95
- "description": "通用表单",
96
- },
97
- "通用表单弹窗": {
98
- "scope": "html",
99
- "prefix": "RModalForm",
100
- "body": [
101
- "<RModalForm",
102
- " ref=\"refRModalForm\"",
103
- " :form-group=\"formGroup\"",
104
- " :submit=\"onSubmit\"",
105
- " title=\"${1:title}\"",
106
- "/>",
107
- ],
108
- "description": "通用表单弹窗",
109
- },
110
- "通用弹窗": {
111
- "scope": "html",
112
- "prefix": "RModal",
113
- "body": ["<RModal", " v-model:value=\"visible\"", " title=\"${1:title}\"", "/>"],
114
- "description": "通用弹窗",
115
- },
116
- "页尾组件": {
117
- "scope": "html",
118
- "prefix": "RPageFooter",
119
- "body": [
120
- "<RPageFooter",
121
- " :pagination=\"pagination\"",
122
- " position=\"absolute\"",
123
- " show-total",
124
- " show-total-page",
125
- " @pagination-change=\"onPageChange\"",
126
- " @pagination-show-size-change=\"onShowSizeChange\"",
127
- "/>",
128
- ],
129
- "description": "页尾组件",
130
- },
131
- "预览组件,可预览图片、视频、pdf等": {
132
- "scope": "html",
133
- "prefix": "RPreview",
134
- "body": ["<RPreview", " v-model:preview=\"preview\"", " type=\"${1:type}\"", " :value=\"previewValue\"", "/>"],
135
- "description": "预览组件,可预览图片、视频、pdf等",
136
- },
137
- "更符合业务的时间选择框": {
138
- "scope": "html",
139
- "prefix": "RRangePicker",
140
- "body": ["<RRangePicker", " v-model:value=\"rangePickerValue\"", " @change=\"onRangePickerChange\"", "/>"],
141
- "description": "更符合业务的时间选择框",
142
- },
143
- "更符合业务的下拉框": {
144
- "scope": "html",
145
- "prefix": "RSelect",
146
- "body": [
147
- "<RSelect",
148
- " v-model:value=\"selectValue\"",
149
- " :config=\"{ $1 }\"",
150
- " @change=\"onSelectChange\"",
151
- "/>",
152
- ],
153
- "description": "更符合业务的下拉框",
154
- },
155
- "数据统计组件": {
156
- "scope": "html",
157
- "prefix": "RStatistic",
158
- "body": ["<RStatistic", " title=\"${1:title}\"", " :value=\"value\"", "/>"],
159
- "description": "数据统计组件",
160
- },
161
- "通用表格": {
162
- "scope": "html",
163
- "prefix": "RTable",
164
- "body": ["<RTable", " :api=\"apiSearchTable\"", " :columns=\"columns\"", "/>"],
165
- "description": "通用表格",
166
- },
167
- "文本框输入的内容转换为标签": {
168
- "scope": "html",
169
- "prefix": "RTagInput",
170
- "body": [
171
- "<RTagInput",
172
- " v-model:value=\"value\"",
173
- " placeholder=\"请输入商品ID,多个商品ID可用空格键分隔\"",
174
- "/>",
175
- ],
176
- "description": "文本框输入的内容转换为标签",
177
- },
178
- "可折叠文本组件": {
179
- "scope": "html",
180
- "prefix": "RTextCollapse",
181
- "body": ["<RTextCollapse", " :line=\"2\"", " :text=\"${1:$1}\"", " :width=\"300\"", " />"],
182
- "description": "可折叠文本组件",
183
- },
184
- "通用上传组件": {
185
- "scope": "html",
186
- "prefix": "RUpload",
187
- "body": [
188
- "<RUpload",
189
- " v-model:value=\"fileList\"",
190
- " :oss-config=\"{",
191
- " uploadApi: generateAndUploadImage2Oss,",
192
- " }\"",
193
- " show-text=\"${1:text}\"",
194
- " template=\"dragger\"",
195
- "/>",
196
- ],
197
- "description": "通用上传组件",
198
- },
199
- "视频播放组件": {
200
- "scope": "html",
201
- "prefix": "RVideo",
202
- "body": ["<RVideo", " ref=\"refRVideo\"", " height=\"200px\"", " :src=\"${1:http}\"", " width=\"200px\"", "/>"],
203
- "description": "视频播放组件",
204
- },
205
- }
package/docs/README.md DELETED
@@ -1,20 +0,0 @@
1
- # ConditionalSelection 组件库文档
2
-
3
- ## 目录
4
- - [快速开始](./quick-start.md)
5
- - [API 说明](./api.md)
6
- - [常见问题](./faq.md)
7
-
8
- ---
9
-
10
- 本目录下为组件库的开发与使用文档,适合团队成员、二次开发者、业务集成者查阅。
11
-
12
- - 如需查看详细用法和参数,请参考 [API 说明](./api.md)
13
- - 如需快速集成体验,请参考 [快速开始](./quick-start.md)
14
- - 如遇常见问题,可查阅 [常见问题](./faq.md)
15
-
16
- 如有更多问题或建议,欢迎补充!
17
-
18
- ---
19
-
20
- **联系人:no_any@qq.com**
package/docs/api.md DELETED
@@ -1,37 +0,0 @@
1
- # API 说明
2
-
3
- ## ConditionalSelection 组件参数
4
-
5
- | 参数 | 说明 | 类型 | 默认值 |
6
- | ---- | ---- | ---- | ------ |
7
- | conditionalRules | 条件树数据 | TConditionalSelection \| null | null |
8
- | maxLevel | 最大递归层级 | number | 1 |
9
- | zeroLevelMaxLength | 最外层子项最大数量 | number | 不限 |
10
- | disabled | 是否禁用全部/部分操作 | boolean \| object | false |
11
- | onChange | 数据变更回调 | (value) => void | - |
12
- | renderConditionRules | 条件行插槽 | (item, change) => ReactNode | - |
13
- | renderCreateCondition | 新增按钮插槽 | (params) => ReactNode | - |
14
-
15
- ## TConditionalSelection 结构
16
-
17
- ```ts
18
- {
19
- _id: string;
20
- framework: 'group' | 'individual';
21
- link?: 'and' | 'or';
22
- group?: TConditionalSelection[];
23
- individual?: Record<string, any>;
24
- level: number;
25
- }
26
- ```
27
-
28
- ## 事件说明
29
- - `onChange`:每次条件树变更时触发,返回最新数据。
30
- - `getRulesData`:通过 ref 获取当前条件树数据,支持校验。
31
-
32
- ## 插槽说明
33
- - `renderConditionRules(item, change)`:自定义条件行内容,change 用于回传变更。
34
- - `renderCreateCondition({ createRules, rulesData })`:自定义新增按钮。
35
-
36
- ## 其他
37
- - 支持 less 样式、monorepo 结构。
package/docs/faq.md DELETED
@@ -1,21 +0,0 @@
1
- # 常见问题 FAQ
2
-
3
- ### 1. 如何自定义条件行内容?
4
- 使用 `renderConditionRules` 插槽,传入自定义组件并用 change handler 回传数据。
5
-
6
- ### 2. 如何限制条件树层级和数量?
7
- 通过 `maxLevel` 和 `zeroLevelMaxLength` 参数控制。
8
-
9
- ### 3. 如何获取当前条件树数据?
10
- 通过 ref 调用 `getRulesData()` 方法。
11
-
12
- ### 4. 为什么要用 useImmer?
13
- 保证数据不可变、递归更新高效,避免直接修改原始对象。
14
-
15
- ### 5. 如何避免递归组件无谓重渲染?
16
- 递归 handler 用 useCallback 包裹,父组件插槽也建议 useCallback。
17
-
18
- ### 6. 支持哪些样式扩展?
19
- 支持 less,样式可自定义覆盖。
20
-
21
- 如有更多问题,欢迎补充!
@@ -1,32 +0,0 @@
1
- # 快速开始
2
-
3
- ## 安装依赖
4
-
5
- ```bash
6
- npm install
7
- ```
8
-
9
- ## 启动示例项目
10
-
11
- ```bash
12
- npm run dev
13
- ```
14
-
15
- ## 基本用法
16
-
17
- ```tsx
18
- import { ConditionalSelection } from '../packages/ConditionalSelection';
19
-
20
- <ConditionalSelection
21
- conditionalRules={rules}
22
- maxLevel={3}
23
- zeroLevelMaxLength={3}
24
- disabled={false}
25
- onChange={setRules}
26
- renderConditionRules={(item, change) => (
27
- <FlexSelect item={item} onChange={val => change(val)} />
28
- )}
29
- />
30
- ```
31
-
32
- 更多用法详见主目录 README.md。
package/example/App.tsx DELETED
@@ -1,162 +0,0 @@
1
- import React, { useMemo, useRef, useState } from 'react';
2
- import { ConditionalSelection } from '../packages/ConditionalSelection';
3
- import type { TConditionalSelection } from '../packages/ConditionalSelection/types';
4
- import '../packages/ConditionalSelection/index.less';
5
-
6
- const fieldOptions = [
7
- { label: '测试字段1', value: 1 },
8
- { label: '测试字段2', value: 2 },
9
- ];
10
- const functionOptions = [
11
- { label: '等于', value: 1 },
12
- { label: '不等于', value: 2 },
13
- ];
14
- const valueOptions = [
15
- { label: '测试值1', value: 1 },
16
- { label: '测试值2', value: 2 },
17
- ];
18
-
19
- const FlexSelect: React.FC<{ item: TConditionalSelection; onChange: (val: Record<string, any>) => void }> = ({ item, onChange }) => {
20
- // field / function / value 存放在节点的 individual 里
21
- const data = useMemo(() => item.individual ?? {}, [item.individual]);
22
-
23
- const handleFieldChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
24
- const val = e.target.value ? Number(e.target.value) : undefined;
25
- onChange({ field: val, function: '', value: undefined });
26
- };
27
-
28
- const handleFunctionChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
29
- const val = e.target.value ? Number(e.target.value) : undefined;
30
- onChange({ ...data, function: val, value: undefined });
31
- };
32
-
33
- const handleValueChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
34
- const val = e.target.value ? Number(e.target.value) : undefined;
35
- onChange({ ...data, value: val });
36
- };
37
-
38
- return (
39
- <div style={{ display: 'flex', width: '100%' }}>
40
- <select style={{ width: '33%', marginRight: 16 }} value={data.field ?? ''} onChange={handleFieldChange}>
41
- <option value="">请选择字段</option>
42
- {fieldOptions.map(opt => (
43
- <option key={opt.value} value={opt.value}>{opt.label}</option>
44
- ))}
45
- </select>
46
- {data.field && (
47
- <select style={{ width: '33%', marginRight: 16 }} value={data.function ?? ''} onChange={handleFunctionChange}>
48
- <option value="">请选择函数</option>
49
- {functionOptions.map(opt => (
50
- <option key={opt.value} value={opt.value}>{opt.label}</option>
51
- ))}
52
- </select>
53
- )}
54
- {data.function && (
55
- <select style={{ width: '33%', marginRight: 16 }} value={data.value ?? ''} onChange={handleValueChange}>
56
- <option value="">请选择值</option>
57
- {valueOptions.map(opt => (
58
- <option key={opt.value} value={opt.value}>{opt.label}</option>
59
- ))}
60
- </select>
61
- )}
62
- </div>
63
- );
64
- }
65
-
66
- export default function App() {
67
- const refConditionalSelection = useRef<any>(null);
68
- const [rules, setRules] = useState<TConditionalSelection | undefined>();
69
- const [maxLevel, setMaxLevel] = useState(3);
70
- const [zeroLevelMaxLength, setZeroLevelMaxLength] = useState(3);
71
- const [disabled, setDisabled] = useState(false);
72
- const [disabledConfig, setDisabledConfig] = useState({
73
- addItem: false,
74
- delItem: false,
75
- linkChange: false,
76
- });
77
-
78
- const getResult = async () => {
79
- const data = await refConditionalSelection.current.getConditionalSelectionData()
80
- console.log('当前数据:', data);
81
- }
82
-
83
- return (
84
- <div style={{ padding: 24 }}>
85
- <div style={{ display: 'flex', gap: 16, flexWrap: 'wrap', marginBottom: 24 }}>
86
- <div className="level-setting">
87
- 设置层级:
88
- <input
89
- type="number"
90
- min={1}
91
- value={maxLevel}
92
- onChange={e => setMaxLevel(Number(e.target.value) || 1)}
93
- style={{ width: 80, marginLeft: 8 }}
94
- />
95
- </div>
96
- <div className="level-setting">
97
- 设置最外层子项数量:
98
- <input
99
- type="number"
100
- min={2}
101
- value={zeroLevelMaxLength}
102
- onChange={e => setZeroLevelMaxLength(Number(e.target.value) || 2)}
103
- style={{ width: 80, marginLeft: 8 }}
104
- />
105
- </div>
106
- <div className="level-setting">
107
- 全部禁用:
108
- <input
109
- type="checkbox"
110
- checked={disabled}
111
- onChange={e => setDisabled(e.target.checked)}
112
- style={{ marginLeft: 8 }}
113
- />
114
- </div>
115
- <div className="level-setting">
116
- 禁用添加:
117
- <input
118
- type="checkbox"
119
- checked={disabledConfig.addItem}
120
- onChange={e => setDisabledConfig(cfg => ({ ...cfg, addItem: e.target.checked }))}
121
- style={{ marginLeft: 8 }}
122
- />
123
- </div>
124
- <div className="level-setting">
125
- 禁用删除:
126
- <input
127
- type="checkbox"
128
- checked={disabledConfig.delItem}
129
- onChange={e => setDisabledConfig(cfg => ({ ...cfg, delItem: e.target.checked }))}
130
- style={{ marginLeft: 8 }}
131
- />
132
- </div>
133
- <div className="level-setting">
134
- 禁用变化关系:
135
- <input
136
- type="checkbox"
137
- checked={disabledConfig.linkChange}
138
- onChange={e => setDisabledConfig(cfg => ({ ...cfg, linkChange: e.target.checked }))}
139
- style={{ marginLeft: 8 }}
140
- />
141
- </div>
142
- <button className="btn" style={{ marginLeft: 8 }} onClick={() => setRules(undefined)}>清空数据</button>
143
- <button className="btn" style={{ marginLeft: 8 }} onClick={getResult}>获取数据</button>
144
- </div>
145
- <ConditionalSelection
146
- ref={refConditionalSelection}
147
- conditionalRules={rules}
148
- maxLevel={maxLevel}
149
- zeroLevelMaxLength={zeroLevelMaxLength}
150
- disabled={disabled || disabledConfig}
151
- onChange={setRules}
152
- renderConditionRules={(item, change) => (
153
- <FlexSelect
154
- item={item}
155
- onChange={val => change(val)}
156
- />
157
- )}
158
- />
159
- <pre style={{ marginTop: 24, fontSize: 12 }}>{JSON.stringify(rules, null, 2)}</pre>
160
- </div>
161
- );
162
- }
@@ -1,12 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Document</title>
7
- </head>
8
- <body>
9
- <div id="root"></div>
10
- <script type="module" src="./main.tsx"></script>
11
- </body>
12
- </html>
package/example/main.tsx DELETED
@@ -1,8 +0,0 @@
1
- import React from 'react'
2
- import ReactDOM from 'react-dom/client'
3
- import App from './App'
4
- ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
5
- <React.StrictMode>
6
- <App />
7
- </React.StrictMode>
8
- )
@@ -1,33 +0,0 @@
1
- import React from 'react';
2
- import { EConditionalSelectionLink } from '../types';
3
-
4
- interface IConditionalChooseProps {
5
- value: EConditionalSelectionLink;
6
- disabled?: boolean;
7
- onChange: (value: EConditionalSelectionLink) => void;
8
- }
9
-
10
- const logicMaps: Record<EConditionalSelectionLink, string> = {
11
- [EConditionalSelectionLink.OR]: '或',
12
- [EConditionalSelectionLink.AND]: '且',
13
- };
14
-
15
- /**
16
- * 条件切换组件(且/或)
17
- */
18
- const ConditionalChoose: React.FC<IConditionalChooseProps> = ({ value, disabled, onChange }) => {
19
- function toggleLogic() {
20
- if (disabled) return;
21
- onChange(value === EConditionalSelectionLink.OR ? EConditionalSelectionLink.AND : EConditionalSelectionLink.OR);
22
- }
23
-
24
- return (
25
- <div className="link-line">
26
- <div className="link-label" onClick={toggleLogic}>
27
- {logicMaps[value] ?? '且'}
28
- </div>
29
- </div>
30
- );
31
- };
32
-
33
- export default ConditionalChoose;
@@ -1,134 +0,0 @@
1
- import React from 'react';
2
- import ConditionalChoose from './conditional-choose';
3
- import ConditionalHandle from './conditional-handle';
4
- import {
5
- EConditionalSelectionLink,
6
- type TConditionalSelection,
7
- type TConditionalSelectionDisabledProps,
8
- } from '../types';
9
- import { isGroup } from '../composables/useConditionalHandle';
10
- interface IConditionalContentProps {
11
- levelData: TConditionalSelection;
12
- level: number;
13
- disabledConfig: Required<TConditionalSelectionDisabledProps>;
14
- onUpdateNode: (nodeId: string, patch: Partial<TConditionalSelection>) => void;
15
- onCreateRules: (ruleData: TConditionalSelection) => void;
16
- onCreateChildRules: (ruleData: TConditionalSelection) => void;
17
- onDelRules: (ruleData: TConditionalSelection, index: number) => void;
18
- /** 渲染单行条件内容 */
19
- renderConditionItem?: (rulesLevelItem: TConditionalSelection) => React.ReactNode;
20
- }
21
-
22
- /**
23
- * 获取是否是"最后一个条件"位置,用于控制同级新增按钮
24
- */
25
- function getLastCondition(index: number, group: TConditionalSelection[]): boolean {
26
- const lastIndex = group.length - 1;
27
- const tempInfo = group[lastIndex];
28
- let flag = index === lastIndex;
29
- if (!flag && tempInfo && isGroup(tempInfo)) {
30
- flag = index === group.length - 2;
31
- }
32
- return flag;
33
- }
34
-
35
- /**
36
- * 条件内容组件(递归渲染嵌套规则树)
37
- * 对应 Vue 版 relation-content.vue
38
- */
39
- const ConditionalContent: React.FC<IConditionalContentProps> = ({
40
- levelData,
41
- level,
42
- disabledConfig,
43
- onUpdateNode,
44
- onCreateRules,
45
- onCreateChildRules,
46
- onDelRules,
47
- renderConditionItem,
48
- }) => {
49
- const group = levelData.group as TConditionalSelection[];
50
-
51
- function handleLinkChange(value: EConditionalSelectionLink) {
52
- onUpdateNode(levelData._id, { link: value });
53
- }
54
-
55
- return (
56
- <div className="relative-box">
57
- {/* 且/或 切换 */}
58
- <ConditionalChoose
59
- value={levelData.link ?? EConditionalSelectionLink.AND}
60
- disabled={disabledConfig.linkChange}
61
- onChange={handleLinkChange}
62
- />
63
-
64
- <div className="conditional-list-box right-box">
65
- {group.map((item, index) => {
66
- if (isGroup(item)) {
67
- const childRules = item.group as TConditionalSelection[];
68
- return (
69
- <div key={item._id} className="conditional-list">
70
- <div className="relative-box">
71
- <ConditionalChoose
72
- value={item.link ?? EConditionalSelectionLink.AND}
73
- disabled={disabledConfig.linkChange}
74
- onChange={val => onUpdateNode(item._id, { link: val })}
75
- />
76
- {childRules.map((child, childIndex) => {
77
- if (isGroup(child) && (child.group as TConditionalSelection[]).length > 0) {
78
- return (
79
- <div key={child._id} className="conditional-item right-box">
80
- <ConditionalContent
81
- levelData={child}
82
- level={level}
83
- disabledConfig={disabledConfig}
84
- onUpdateNode={onUpdateNode}
85
- onCreateRules={onCreateRules}
86
- onCreateChildRules={onCreateChildRules}
87
- onDelRules={onDelRules}
88
- renderConditionItem={renderConditionItem}
89
- />
90
- </div>
91
- );
92
- }
93
- return (
94
- <div key={child._id} className="conditional-item right-box">
95
- {renderConditionItem?.(child)}
96
- <ConditionalHandle
97
- level={level}
98
- disabledConfig={disabledConfig}
99
- currentItemInfo={child}
100
- isLast={getLastCondition(childIndex, childRules)}
101
- onCreate={() => onCreateRules(item)}
102
- onCreateChildRules={() => onCreateChildRules(child)}
103
- onDel={() => onDelRules(item, childIndex)}
104
- />
105
- </div>
106
- );
107
- })}
108
- </div>
109
- </div>
110
- );
111
- }
112
- return (
113
- <div key={item._id} className="conditional-list">
114
- <div className="conditional-item">
115
- {renderConditionItem?.(item)}
116
- <ConditionalHandle
117
- level={level}
118
- disabledConfig={disabledConfig}
119
- currentItemInfo={item}
120
- isLast={getLastCondition(index, group)}
121
- onCreate={() => onCreateRules(levelData)}
122
- onCreateChildRules={() => onCreateChildRules(item)}
123
- onDel={() => onDelRules(levelData, index)}
124
- />
125
- </div>
126
- </div>
127
- );
128
- })}
129
- </div>
130
- </div>
131
- );
132
- };
133
-
134
- export default ConditionalContent;
@@ -1,83 +0,0 @@
1
- import React, { useMemo } from 'react';
2
- import type {
3
- TConditionalSelection,
4
- TConditionalSelectionDisabledProps,
5
- } from '../types';
6
-
7
- interface IConditionalHandleProps {
8
- level: number;
9
- currentItemInfo: TConditionalSelection;
10
- isLast: boolean;
11
- disabledConfig: Required<TConditionalSelectionDisabledProps>;
12
- onCreate: () => void;
13
- onCreateChildRules: () => void;
14
- onDel: () => void;
15
- }
16
-
17
- function isEmptyValue(val: unknown): boolean {
18
- if (val === null || val === undefined || val === '') return true;
19
- if (Array.isArray(val)) return val.length === 0;
20
- if (val !== null && typeof val === 'object') return Object.keys(val as object).length === 0;
21
- return false;
22
- }
23
-
24
- function allHave(individual?: Record<string, any>): boolean {
25
- return (
26
- !!individual &&
27
- Object.keys(individual).length > 0 &&
28
- Object.keys(individual).every((key: string) => !isEmptyValue(individual[key]))
29
- );
30
- }
31
-
32
- /**
33
- * 规则操作按钮组(同级/子集/删除)
34
- */
35
- const ConditionalHandle: React.FC<IConditionalHandleProps> = ({
36
- level,
37
- currentItemInfo,
38
- isLast,
39
- disabledConfig,
40
- onCreate,
41
- onCreateChildRules,
42
- onDel,
43
- }) => {
44
- /** 是否显示同级新增按钮:当前节点 level >= 1 且是最后一个 */
45
- const showCreate = useMemo(() => currentItemInfo.level >= 1 && isLast, [currentItemInfo.level, isLast]);
46
-
47
- /** 是否显示子集新增按钮:全局 level > 1 且当前节点 level 未达到上限 */
48
- const showCreateChild = useMemo(() => level > 1 && currentItemInfo.level < level, [level, currentItemInfo.level]);
49
-
50
- /** 整体新增按钮是否显示:level > 1 && 当前条件已填写完整 && 未禁用新增 */
51
- const createShow = useMemo(
52
- () => !!(level > 1 && allHave(currentItemInfo.individual) && !disabledConfig.addItem),
53
- [level, currentItemInfo.individual, disabledConfig.addItem],
54
- );
55
-
56
- return (
57
- <div className="conditional-handle">
58
- {createShow && (
59
- <>
60
- {showCreate && (
61
- <span className="create-handle" onClick={onCreate}>
62
- <span style={{ color: '#c85000', marginRight: 4 }}>+</span>
63
- 同级
64
- </span>
65
- )}
66
- {showCreateChild && (
67
- <span className="create-handle" onClick={onCreateChildRules}>
68
- <span style={{ color: '#c85000', marginRight: 4 }}>+</span>
69
- 子级
70
- </span>
71
- )}
72
- </>
73
- )}
74
- {!disabledConfig.delItem && (
75
- <span className="del-handle" onClick={onDel}>
76
-
77
- </span>
78
- )}
79
- </div>
80
- );
81
- };
82
-
83
- export default ConditionalHandle;
@@ -1,234 +0,0 @@
1
- import { useMemo, useCallback } from 'react';
2
- import { useImmer } from 'use-immer';
3
- import { cloneDeep } from 'lodash-es';
4
- import {
5
- EConditionalSelectionLink,
6
- EConditionalSelectionFramework,
7
- type TConditionalSelection,
8
- type TConditionalSelectionProps,
9
- type TConditionalSelectionDisabledProps,
10
- } from '../types';
11
-
12
- function isObject(val: unknown): val is Record<string, any> {
13
- return val !== null && typeof val === 'object' && !Array.isArray(val);
14
- }
15
-
16
- function isEmptyValue(val: unknown): boolean {
17
- if (val === null || val === undefined || val === '') return true;
18
- if (Array.isArray(val)) return val.length === 0;
19
- if (isObject(val)) return Object.keys(val).length === 0;
20
- return false;
21
- }
22
-
23
- export function getTempRules(level = 0): TConditionalSelection {
24
- return cloneDeep({
25
- _id: crypto.randomUUID(),
26
- framework: EConditionalSelectionFramework.INDIVIDUAL,
27
- // INDIVIDUAL 节点只保留 individual,不初始化 group
28
- individual: {},
29
- level,
30
- }) as TConditionalSelection;
31
- }
32
-
33
- export function isGroup(ruleData: TConditionalSelection): boolean {
34
- return ruleData.framework === EConditionalSelectionFramework.GROUP;
35
- }
36
-
37
- function allHave(individual?: Record<string, any>): boolean {
38
- return (
39
- !!individual &&
40
- Object.keys(individual).length > 0 &&
41
- Object.keys(individual).every((key: string) => !isEmptyValue(individual[key]))
42
- );
43
- }
44
-
45
- function checkData(data: TConditionalSelection): boolean {
46
- if (isGroup(data) && Array.isArray(data.group)) {
47
- // GROUP 节点:递归检查 group 数组中的每个子项
48
- return data.group.some(item => checkData(item));
49
- }
50
- // INDIVIDUAL 节点:检查 individual 是否填写完整
51
- return !allHave(data.individual);
52
- }
53
-
54
- function subtractLevel(rulesData: TConditionalSelection): void {
55
- // 只有 GROUP 节点才有 group 数组
56
- if (isGroup(rulesData) && Array.isArray(rulesData.group) && rulesData.group.length > 1) {
57
- rulesData.group.forEach(item => subtractLevel(item));
58
- }
59
- rulesData.level = rulesData.level - 1 < 0 ? 0 : rulesData.level - 1;
60
- }
61
-
62
- /** 在树中按 _id 深度优先查找节点 */
63
- export function findNode(root: TConditionalSelection, id: string): TConditionalSelection | null {
64
- if (root._id === id) return root;
65
- if (Array.isArray(root.group)) {
66
- for (const child of root.group) {
67
- const found = findNode(child, id);
68
- if (found) return found;
69
- }
70
- }
71
- return null;
72
- }
73
-
74
- export interface IUseConditionalHandleReturn {
75
- rulesData: TConditionalSelection;
76
- setRulesData: (data: TConditionalSelection) => void;
77
- updateRulesData: (updater: (draft: TConditionalSelection) => void) => void;
78
- level: number;
79
- disabledConfig: Required<TConditionalSelectionDisabledProps>;
80
- createRules: (ruleData: TConditionalSelection) => void;
81
- createChildRules: (ruleData: TConditionalSelection) => void;
82
- delRules: (ruleData: TConditionalSelection, index: number) => void;
83
- getConditionalSelectionData: (validate?: boolean) => Promise<TConditionalSelection>;
84
- isGroup: (ruleData: TConditionalSelection) => boolean;
85
- getTempRules: (level?: number) => TConditionalSelection;
86
- }
87
-
88
- export function useConditionalHandle(props: TConditionalSelectionProps): IUseConditionalHandleReturn {
89
- const [rulesData, updateRulesData] = useImmer<TConditionalSelection>(() => getTempRules());
90
-
91
- // 对外保持 setRulesData 接口不变(直接替换整棵树)
92
- const setRulesData = useCallback((data: TConditionalSelection) => {
93
- updateRulesData(() => data);
94
- }, [updateRulesData]);
95
-
96
- // level 计算
97
- const level = useMemo(() => {
98
- const v = Math.abs(Number(props.maxLevel ?? 1)) || 1;
99
- if (Number(props.maxLevel ?? 1) < 1) {
100
- console.warn('[TConditionalSelection] Invalid props.maxLevel: maxLevel must be greater than 0.');
101
- }
102
- return v;
103
- }, [props.maxLevel]);
104
-
105
- // disabledConfig 计算
106
- const disabledConfig = useMemo<Required<TConditionalSelectionDisabledProps>>(() => {
107
- const isObj = isObject(props.disabled);
108
- return {
109
- addItem: !!(isObj ? (props.disabled as TConditionalSelectionDisabledProps)?.addItem : props.disabled),
110
- delItem: !!(isObj ? (props.disabled as TConditionalSelectionDisabledProps)?.delItem : props.disabled),
111
- linkChange: !!(isObj ? (props.disabled as TConditionalSelectionDisabledProps)?.linkChange : props.disabled),
112
- };
113
- }, [props.disabled]);
114
-
115
- // 创建同级规则:向当前 GROUP 节点的 group 数组追加一个新 INDIVIDUAL 节点
116
- const createRules = useCallback(
117
- (ruleData: TConditionalSelection) => {
118
- if (!isGroup(ruleData)) return;
119
- const maxLen = Math.abs(Number(props.zeroLevelMaxLength));
120
- if (maxLen) {
121
- if (Number(props.zeroLevelMaxLength) < 2) {
122
- console.warn(
123
- '[TConditionalSelection] Invalid props.zeroLevelMaxLength: zeroLevelMaxLength must be greater than 1.',
124
- );
125
- }
126
- if (ruleData.level === 0 && (ruleData.group as TConditionalSelection[]).length === maxLen) {
127
- console.warn(`最外层子项最多添加${maxLen}个条件`);
128
- return;
129
- }
130
- }
131
- updateRulesData(draft => {
132
- const target = findNode(draft, ruleData._id);
133
- if (target && Array.isArray(target.group)) {
134
- target.group.push(getTempRules(target.level + 1));
135
- }
136
- });
137
- },
138
- [props.zeroLevelMaxLength, updateRulesData],
139
- );
140
-
141
- // 创建子级规则:将当前 INDIVIDUAL 节点升级为 GROUP 节点
142
- const createChildRules = useCallback(
143
- (ruleData: TConditionalSelection) => {
144
- updateRulesData(draft => {
145
- const target = findNode(draft, ruleData._id);
146
- if (!target) return;
147
-
148
- if (target.framework === EConditionalSelectionFramework.INDIVIDUAL) {
149
- const firstChild: TConditionalSelection = {
150
- _id: crypto.randomUUID(),
151
- framework: EConditionalSelectionFramework.INDIVIDUAL,
152
- individual: cloneDeep(target.individual ?? {}),
153
- level: target.level + 1,
154
- };
155
- const secondChild = getTempRules(target.level + 1);
156
- target.framework = EConditionalSelectionFramework.GROUP;
157
- target.link = EConditionalSelectionLink.AND;
158
- target._id = crypto.randomUUID();
159
- target.group = [firstChild, secondChild];
160
- delete target.individual;
161
- } else {
162
- // 已是 GROUP,追加子项
163
- if (Array.isArray(target.group)) {
164
- target.group.push(getTempRules(target.level + 1));
165
- }
166
- }
167
- });
168
- },
169
- [updateRulesData],
170
- );
171
-
172
- // 删除规则
173
- const delRules = useCallback((ruleData: TConditionalSelection, index: number) => {
174
- updateRulesData(draft => {
175
- const target = findNode(draft, ruleData._id);
176
- if (!target || !Array.isArray(target.group)) return;
177
-
178
- target.group.splice(index, 1);
179
-
180
- if (target.framework === EConditionalSelectionFramework.GROUP && target.group.length === 1) {
181
- const onlyChild = target.group[0];
182
- if (
183
- onlyChild.framework === EConditionalSelectionFramework.GROUP &&
184
- Array.isArray(onlyChild.group) &&
185
- onlyChild.group.length > 1
186
- ) {
187
- // 唯一子项是 GROUP,提升到当前节点
188
- target.framework = EConditionalSelectionFramework.GROUP;
189
- target.link = onlyChild.link;
190
- target._id = onlyChild._id;
191
- target.group = onlyChild.group;
192
- delete target.individual;
193
- } else {
194
- // 唯一子项是 INDIVIDUAL,降级
195
- target.framework = EConditionalSelectionFramework.INDIVIDUAL;
196
- target._id = onlyChild._id;
197
- target.individual = cloneDeep(onlyChild.individual ?? {});
198
- delete target.link;
199
- delete target.group;
200
- }
201
- subtractLevel(onlyChild);
202
- }
203
- });
204
- }, [updateRulesData]);
205
-
206
- // 获取并校验数据
207
- const getConditionalSelectionData = useCallback(
208
- (validate = true): Promise<TConditionalSelection> => {
209
- return new Promise((resolve, reject) => {
210
- if (validate && checkData(rulesData)) {
211
- console.error('请填写完整');
212
- reject('含有未填写完整的项');
213
- return;
214
- }
215
- resolve(cloneDeep(rulesData));
216
- });
217
- },
218
- [rulesData],
219
- );
220
-
221
- return {
222
- rulesData,
223
- setRulesData,
224
- level,
225
- disabledConfig,
226
- createRules,
227
- createChildRules,
228
- delRules,
229
- getConditionalSelectionData,
230
- isGroup,
231
- getTempRules,
232
- updateRulesData,
233
- };
234
- }
@@ -1,109 +0,0 @@
1
- import { useEffect, useRef, useImperativeHandle, forwardRef, useCallback } from 'react';
2
- import { cloneDeep } from 'lodash-es';
3
- import ConditionalContent from './components/conditional-content';
4
- import { useConditionalHandle, getTempRules, isGroup, findNode } from './composables/useConditionalHandle';
5
- import { type TConditionalSelectionProps } from './types';
6
- import './index.less';
7
-
8
- export type { TConditionalSelectionProps };
9
-
10
- const ConditionalSelection = forwardRef<
11
- { getConditionalSelectionData: (validate?: boolean) => Promise<any> },
12
- TConditionalSelectionProps
13
- >((props, ref) => {
14
- const { conditionalRules, onChange, renderConditionRules, renderCreateCondition } = props;
15
-
16
- const { rulesData, setRulesData, level, disabledConfig, createRules, createChildRules, delRules, getConditionalSelectionData, updateRulesData } =
17
- useConditionalHandle(props);
18
-
19
- // 暴露 getConditionalSelectionData 给父组件
20
- useImperativeHandle(ref, () => ({ getConditionalSelectionData }), [getConditionalSelectionData]);
21
-
22
- // 监听 rulesData 变化并抛出 onChange
23
- const isFirstRender = useRef(true);
24
- useEffect(() => {
25
- if (isFirstRender.current) {
26
- isFirstRender.current = false;
27
- return;
28
- }
29
- onChange?.(rulesData);
30
- }, [rulesData]);
31
-
32
- //初始化
33
- useEffect(() => {
34
- if (conditionalRules && Object.keys(conditionalRules).length !== 0) {
35
- setRulesData(cloneDeep(conditionalRules));
36
- } else {
37
- setRulesData(getTempRules());
38
- }
39
- }, []);
40
-
41
- // 递归/slot相关 handler 用 useCallback 包裹,依赖项只放 updateRulesData/findNode/renderConditionRules
42
- const handleUpdateNode = useCallback((nodeId: string, patch: Partial<any>) => {
43
- updateRulesData(draft => {
44
- const target = findNode(draft, nodeId);
45
- if (!target) return;
46
- Object.assign(target, patch);
47
- });
48
- }, [updateRulesData]);
49
-
50
- const handleRenderConditionItem = useCallback(
51
- (item: any) => renderConditionRules?.(item, (val: Record<string, any>) => {
52
- updateRulesData(draft => {
53
- const target = findNode(draft, item._id);
54
- if (!target) return;
55
- target.individual = val;
56
- });
57
- }),
58
- [renderConditionRules, updateRulesData]
59
- );
60
-
61
- const handleRenderConditionRules = useCallback(
62
- (rulesData: any, change: (val: Record<string, any>) => void) =>
63
- renderConditionRules?.(rulesData, change),
64
- [renderConditionRules]
65
- );
66
-
67
- return (
68
- <div className="conditional-selection">
69
- {/* 新增条件按钮区域 */}
70
- {!disabledConfig.addItem &&
71
- (renderCreateCondition ? (
72
- renderCreateCondition({ createRules: createChildRules, rulesData })
73
- ) : (
74
- <span className="create-box" onClick={() => createChildRules(rulesData)}>
75
- <span style={{ color: '#00c8be', marginRight: 4 }}>➕</span>
76
- 添加条件
77
- </span>
78
- ))}
79
-
80
- {/* 规则内容区域 */}
81
- <div className="conditional-content">
82
- {isGroup(rulesData) && (rulesData.group as any[]).length > 0 ? (
83
- <ConditionalContent
84
- levelData={rulesData}
85
- level={level}
86
- disabledConfig={disabledConfig}
87
- onUpdateNode={handleUpdateNode}
88
- onCreateRules={createRules}
89
- onCreateChildRules={createChildRules}
90
- onDelRules={delRules}
91
- renderConditionItem={handleRenderConditionItem}
92
- />
93
- ) : (
94
- handleRenderConditionRules(rulesData, (val: Record<string, any>) => {
95
- updateRulesData(draft => {
96
- const target = findNode(draft, rulesData._id);
97
- if (!target) return;
98
- target.individual = val;
99
- });
100
- })
101
- )}
102
- </div>
103
- </div>
104
- );
105
- });
106
-
107
- ConditionalSelection.displayName = 'ConditionalSelection';
108
-
109
- export default ConditionalSelection;
@@ -1,91 +0,0 @@
1
- .conditional-selection {
2
- width: 100%;
3
-
4
- .create-box {
5
- display: inline-block;
6
- margin-bottom: 8px;
7
- cursor: pointer;
8
- }
9
-
10
- .conditional-content {
11
- width: 100%;
12
- }
13
- }
14
-
15
- .relative-box {
16
- position: relative;
17
- width: 100%;
18
-
19
- .conditional-list-box {
20
- flex: 1;
21
- }
22
-
23
- .conditional-item {
24
- display: flex;
25
- align-items: center;
26
- margin-top: 8px;
27
- }
28
-
29
- .conditional-list:first-child {
30
- margin-top: -8px;
31
- }
32
-
33
- .right-box {
34
- margin-left: 32px;
35
- }
36
- }
37
-
38
- .link-line {
39
- position: absolute;
40
- z-index: 999;
41
- height: 100%;
42
- margin: 0 4px;
43
-
44
- .link-label {
45
- position: absolute;
46
- top: 50%;
47
- width: 22px;
48
- color: #c85000;
49
- text-align: center;
50
- cursor: pointer;
51
- user-select: none;
52
- background-color: #f9e5e5;
53
- border-radius: 4px;
54
- transform: translateY(-50%);
55
- }
56
- }
57
-
58
- .link-line::before {
59
- position: absolute;
60
- top: 0;
61
- bottom: 0;
62
- left: 10px;
63
- width: 2px;
64
- content: '';
65
- background-color: #c85000;
66
- opacity: 0.2;
67
- }
68
-
69
- .conditional-handle {
70
- display: flex;
71
- gap: 8px;
72
- align-items: center;
73
-
74
- .create-handle {
75
- display: inline-block;
76
- width: 50px;
77
- cursor: pointer;
78
- font-size: 14px;
79
- }
80
-
81
- .del-handle {
82
- display: inline-block;
83
- font-size: 14px;
84
- width: 24px;
85
- cursor: pointer;
86
- }
87
-
88
- .del-handle:hover {
89
- color: red;
90
- }
91
- }
@@ -1,5 +0,0 @@
1
- import ConditionalSelection from './conditional-selection';
2
- export * from './types';
3
- export {
4
- ConditionalSelection
5
- }
@@ -1,53 +0,0 @@
1
- import type { ReactNode } from 'react';
2
-
3
- export enum EConditionalSelectionFramework {
4
- /** 内容组 */
5
- GROUP = 'group',
6
- /** 内容 */
7
- INDIVIDUAL = 'individual',
8
- }
9
-
10
- export enum EConditionalSelectionLink {
11
- AND = 'and',
12
- OR = 'or',
13
- }
14
-
15
- export type TConditionalSelection<T = any> = {
16
- _id: string;
17
- /** 结构标识 */
18
- framework: EConditionalSelectionFramework;
19
- /** 内容关系 */
20
- link?: EConditionalSelectionLink;
21
- group?: TConditionalSelection<T>[];
22
- individual?: Record<string, any>;
23
- level: number;
24
- }
25
-
26
- export type TConditionalSelectionDisabledProps = {
27
- addItem?: boolean;
28
- delItem?: boolean;
29
- linkChange?: boolean;
30
- };
31
-
32
- export type TConditionalSelectionProps = {
33
- /** 数据内容 */
34
- conditionalRules?: TConditionalSelection | null;
35
- /** deep 层级限制,需大于0,默认1 */
36
- maxLevel?: number;
37
- /** 最外层子项数量限制,需大于1,默认不限 */
38
- zeroLevelMaxLength?: number;
39
- /** 是否禁用新增、删除、关系变更 */
40
- disabled?: boolean | TConditionalSelectionDisabledProps;
41
- /** 数据变更回调 */
42
- onChange?: (value: TConditionalSelection | undefined) => void;
43
- /** 渲染条件行内容的插槽,参数为当前 INDIVIDUAL 节点和变更 handler */
44
- renderConditionRules?: (
45
- conditionRulesData: TConditionalSelection,
46
- change: (data: Record<string, any>) => void
47
- ) => ReactNode;
48
- /** 渲染创建条件按钮插槽(createCondition) */
49
- renderCreateCondition?: (params: {
50
- createRules: (ruleData: TConditionalSelection) => void;
51
- rulesData: TConditionalSelection;
52
- }) => ReactNode;
53
- };
package/packages/index.ts DELETED
@@ -1 +0,0 @@
1
- export * from './ConditionalSelection'
package/tsconfig.json DELETED
@@ -1,15 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ESNext",
4
- "module": "ESNext",
5
- "moduleResolution": "Bundler",
6
- "allowSyntheticDefaultImports": true,
7
- "jsx": "preserve",
8
- "jsxImportSource": "react",
9
- "strict": true,
10
- "skipLibCheck": true,
11
- "forceConsistentCasingInFileNames": true
12
- },
13
- "include": ["packages/**/*"],
14
- "references": [{ "path": "./tsconfig.node.json" }]
15
- }
@@ -1,15 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ESNext",
4
- "module": "ESNext",
5
- "moduleResolution": "Bundler",
6
- "allowSyntheticDefaultImports": true,
7
- "allowImportingTsExtensions": true,
8
- "strict": true,
9
- "skipLibCheck": true,
10
- "emitDeclarationOnly": true,
11
- "types": ["node"],
12
- "composite": true
13
- },
14
- "include": ["vite.config.ts"]
15
- }
package/vite.config.ts DELETED
@@ -1,39 +0,0 @@
1
- import { defineConfig } from 'vite';
2
- import react from '@vitejs/plugin-react-swc'
3
- import path from 'node:path'
4
- import dts from 'vite-plugin-dts'
5
- export default defineConfig(({ command }) => {
6
- const isDev = command === 'serve'
7
- return {
8
- plugins: [react(), dts({
9
- outDir: 'dist/types',
10
- insertTypesEntry: true, //这是dts插件的配置,用于生成类型文件
11
- include: ['packages/**/*.ts', 'packages/**/*.tsx'],
12
- rollupTypes: true
13
- })],
14
- root: isDev ? path.resolve(__dirname, 'example') : undefined,
15
- server: {
16
- port: 3000,
17
- open: true,
18
- },
19
- build: {
20
- outDir: path.resolve(__dirname, 'dist'),
21
- lib: {
22
- entry: path.resolve(__dirname, 'packages/index.ts'),
23
- name: 'ui',
24
- formats: ['es', 'umd', 'cjs', 'iife'],
25
- fileName: (format) => `ui.${format}.js`,
26
- },
27
- emptyOutDir: false,
28
- rollupOptions: {
29
- external: ['react', 'react-dom'],
30
- output: {
31
- globals: {
32
- react: 'React',
33
- 'react-dom': 'ReactDOM',
34
- },
35
- },
36
- },
37
- },
38
- }
39
- });