@fe-free/core 2.8.0 → 2.8.2
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/CHANGELOG.md +14 -0
- package/package.json +2 -2
- package/src/tree/file_tree.stories.tsx +22 -0
- package/src/tree/file_tree.tsx +82 -35
- package/src/tree/tree.tsx +17 -10
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fe-free/core",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"author": "",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"remark-gfm": "^4.0.1",
|
|
42
42
|
"vanilla-jsoneditor": "^0.23.1",
|
|
43
43
|
"zustand": "^4.5.4",
|
|
44
|
-
"@fe-free/tool": "2.8.
|
|
44
|
+
"@fe-free/tool": "2.8.2"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"@ant-design/pro-components": "2.8.9",
|
|
@@ -92,8 +92,30 @@ export const Default: Story = {
|
|
|
92
92
|
key: '4',
|
|
93
93
|
children: [],
|
|
94
94
|
},
|
|
95
|
+
{
|
|
96
|
+
title: 'more operateIsDisabled',
|
|
97
|
+
key: '5',
|
|
98
|
+
children: [],
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
title: 'more operateIsHidden',
|
|
102
|
+
key: '6',
|
|
103
|
+
children: [],
|
|
104
|
+
},
|
|
95
105
|
],
|
|
96
106
|
},
|
|
107
|
+
createProps: {
|
|
108
|
+
operateIsDisabled: (nodeData) => nodeData?.key === '5',
|
|
109
|
+
operateIsHidden: (nodeData) => nodeData?.key === '6',
|
|
110
|
+
},
|
|
111
|
+
updateProps: {
|
|
112
|
+
operateIsDisabled: (nodeData) => nodeData.key === '5',
|
|
113
|
+
operateIsHidden: (nodeData) => nodeData.key === '6',
|
|
114
|
+
},
|
|
115
|
+
deleteProps: {
|
|
116
|
+
operateIsDisabled: (nodeData) => nodeData.key === '5',
|
|
117
|
+
operateIsHidden: (nodeData) => nodeData.key === '6',
|
|
118
|
+
},
|
|
97
119
|
},
|
|
98
120
|
};
|
|
99
121
|
|
package/src/tree/file_tree.tsx
CHANGED
|
@@ -16,6 +16,20 @@ interface FileTreeProps<D extends DataNode> extends TreeProps<D> {
|
|
|
16
16
|
requestCreateByValues?: (values: { key?: string; title: string }) => Promise<false | void>;
|
|
17
17
|
requestUpdateByValues?: (values: { key: string; title: string }) => Promise<false | void>;
|
|
18
18
|
requestDeleteByRecord?: (values: { key: string }) => Promise<void>;
|
|
19
|
+
/** 注意,没法控制 title 区域的添加(由 actions 来控制) */
|
|
20
|
+
createProps?: {
|
|
21
|
+
operateIsDisabled?: (nodeData: D) => boolean;
|
|
22
|
+
|
|
23
|
+
operateIsHidden?: (nodeData: D) => boolean;
|
|
24
|
+
};
|
|
25
|
+
updateProps?: {
|
|
26
|
+
operateIsDisabled?: (nodeData: D) => boolean;
|
|
27
|
+
operateIsHidden?: (nodeData: D) => boolean;
|
|
28
|
+
};
|
|
29
|
+
deleteProps?: {
|
|
30
|
+
operateIsDisabled?: (nodeData: D) => boolean;
|
|
31
|
+
operateIsHidden?: (nodeData: D) => boolean;
|
|
32
|
+
};
|
|
19
33
|
}
|
|
20
34
|
|
|
21
35
|
function Detail<D extends DataNode>({
|
|
@@ -77,44 +91,69 @@ function More({
|
|
|
77
91
|
requestUpdateByValues,
|
|
78
92
|
requestDeleteByRecord,
|
|
79
93
|
requestCreateByValues,
|
|
94
|
+
createProps,
|
|
95
|
+
updateProps,
|
|
96
|
+
deleteProps,
|
|
80
97
|
}) {
|
|
98
|
+
const isCreateDisabled = createProps?.operateIsDisabled?.(nodeData);
|
|
99
|
+
const isCreateHidden = createProps?.operateIsHidden?.(nodeData);
|
|
100
|
+
const isUpdateDisabled = updateProps?.operateIsDisabled?.(nodeData);
|
|
101
|
+
const isUpdateHidden = updateProps?.operateIsHidden?.(nodeData);
|
|
102
|
+
const isDeleteDisabled = deleteProps?.operateIsDisabled?.(nodeData);
|
|
103
|
+
const isDeleteHidden = deleteProps?.operateIsHidden?.(nodeData);
|
|
104
|
+
|
|
105
|
+
const menuItems = [
|
|
106
|
+
actions?.includes('create') &&
|
|
107
|
+
!isCreateHidden && {
|
|
108
|
+
label: isCreateDisabled ? (
|
|
109
|
+
<div className="cursor-not-allowed text-desc">新建子目录</div>
|
|
110
|
+
) : (
|
|
111
|
+
<Detail
|
|
112
|
+
action="create"
|
|
113
|
+
nodeData={{ key: nodeData.key }}
|
|
114
|
+
requestCreateByValues={(values) => requestCreateByValues?.({ ...values })}
|
|
115
|
+
trigger={<div>新建子目录</div>}
|
|
116
|
+
/>
|
|
117
|
+
),
|
|
118
|
+
key: 'create',
|
|
119
|
+
},
|
|
120
|
+
actions?.includes('update') &&
|
|
121
|
+
!isUpdateHidden && {
|
|
122
|
+
label: isUpdateDisabled ? (
|
|
123
|
+
<div className="cursor-not-allowed text-desc">编辑</div>
|
|
124
|
+
) : (
|
|
125
|
+
<Detail
|
|
126
|
+
action="update"
|
|
127
|
+
nodeData={nodeData}
|
|
128
|
+
requestUpdateByValues={(values) => requestUpdateByValues?.({ ...values })}
|
|
129
|
+
trigger={<div>编辑</div>}
|
|
130
|
+
/>
|
|
131
|
+
),
|
|
132
|
+
key: 'update',
|
|
133
|
+
},
|
|
134
|
+
actions?.includes('delete') &&
|
|
135
|
+
!isDeleteHidden && {
|
|
136
|
+
label: isDeleteDisabled ? (
|
|
137
|
+
<div className="cursor-not-allowed text-desc">删除</div>
|
|
138
|
+
) : (
|
|
139
|
+
<OperateDelete
|
|
140
|
+
name={nodeData.title}
|
|
141
|
+
onDelete={() => requestDeleteByRecord?.({ key: nodeData.key })}
|
|
142
|
+
/>
|
|
143
|
+
),
|
|
144
|
+
key: 'delete',
|
|
145
|
+
},
|
|
146
|
+
].filter(Boolean);
|
|
147
|
+
|
|
148
|
+
if (menuItems.length === 0) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
|
|
81
152
|
return (
|
|
82
153
|
<Dropdown
|
|
83
154
|
placement="bottomRight"
|
|
84
155
|
menu={{
|
|
85
|
-
items:
|
|
86
|
-
actions?.includes('create') && {
|
|
87
|
-
label: (
|
|
88
|
-
<Detail
|
|
89
|
-
action="create"
|
|
90
|
-
nodeData={{ key: nodeData.key }}
|
|
91
|
-
requestCreateByValues={(values) => requestCreateByValues?.({ ...values })}
|
|
92
|
-
trigger={<div>新建子目录</div>}
|
|
93
|
-
/>
|
|
94
|
-
),
|
|
95
|
-
key: 'create',
|
|
96
|
-
},
|
|
97
|
-
actions?.includes('update') && {
|
|
98
|
-
label: (
|
|
99
|
-
<Detail
|
|
100
|
-
action="update"
|
|
101
|
-
nodeData={nodeData}
|
|
102
|
-
requestUpdateByValues={(values) => requestUpdateByValues?.({ ...values })}
|
|
103
|
-
trigger={<div>编辑</div>}
|
|
104
|
-
/>
|
|
105
|
-
),
|
|
106
|
-
key: 'update',
|
|
107
|
-
},
|
|
108
|
-
actions?.includes('delete') && {
|
|
109
|
-
label: (
|
|
110
|
-
<OperateDelete
|
|
111
|
-
name={nodeData.title}
|
|
112
|
-
onDelete={() => requestDeleteByRecord?.({ key: nodeData.key })}
|
|
113
|
-
/>
|
|
114
|
-
),
|
|
115
|
-
key: 'delete',
|
|
116
|
-
},
|
|
117
|
-
].filter(Boolean),
|
|
156
|
+
items: menuItems,
|
|
118
157
|
}}
|
|
119
158
|
>
|
|
120
159
|
<div onClick={(e) => e.preventDefault()}>
|
|
@@ -141,14 +180,16 @@ function FileTree<D extends DataNode>(props: FileTreeProps<D>) {
|
|
|
141
180
|
|
|
142
181
|
const titleRender = useCallback(
|
|
143
182
|
(nodeData) => {
|
|
144
|
-
console.log(nodeData);
|
|
145
183
|
const hasMore = props.actions?.includes('update') || props.actions?.includes('delete');
|
|
146
184
|
return (
|
|
147
185
|
<div className="group flex gap-1">
|
|
148
186
|
{nodeData.children ? (
|
|
149
187
|
<FileCard.FileIcon isDirectory className="text-base" />
|
|
150
188
|
) : (
|
|
151
|
-
<FileCard.FileIcon
|
|
189
|
+
<FileCard.FileIcon
|
|
190
|
+
name={nodeData.originData?.title || nodeData.title}
|
|
191
|
+
className="text-base"
|
|
192
|
+
/>
|
|
152
193
|
)}
|
|
153
194
|
<div className="flex-1 truncate">{nodeData.title}</div>
|
|
154
195
|
<div className={classNames('text-desc', { 'group-hover:hidden': hasMore })}>
|
|
@@ -162,6 +203,9 @@ function FileTree<D extends DataNode>(props: FileTreeProps<D>) {
|
|
|
162
203
|
requestCreateByValues={props.requestCreateByValues}
|
|
163
204
|
requestUpdateByValues={props.requestUpdateByValues}
|
|
164
205
|
requestDeleteByRecord={props.requestDeleteByRecord}
|
|
206
|
+
createProps={props.createProps}
|
|
207
|
+
updateProps={props.updateProps}
|
|
208
|
+
deleteProps={props.deleteProps}
|
|
165
209
|
/>
|
|
166
210
|
</div>
|
|
167
211
|
)}
|
|
@@ -173,6 +217,9 @@ function FileTree<D extends DataNode>(props: FileTreeProps<D>) {
|
|
|
173
217
|
props.requestCreateByValues,
|
|
174
218
|
props.requestDeleteByRecord,
|
|
175
219
|
props.requestUpdateByValues,
|
|
220
|
+
props.createProps,
|
|
221
|
+
props.updateProps,
|
|
222
|
+
props.deleteProps,
|
|
176
223
|
],
|
|
177
224
|
);
|
|
178
225
|
|
package/src/tree/tree.tsx
CHANGED
|
@@ -30,10 +30,10 @@ function useHighLightTreeData({ treeData, search }) {
|
|
|
30
30
|
|
|
31
31
|
const loop = (data) => {
|
|
32
32
|
return data.map((item) => {
|
|
33
|
-
const
|
|
34
|
-
const index =
|
|
35
|
-
const beforeStr =
|
|
36
|
-
const afterStr =
|
|
33
|
+
const originStrTitle = item.title as string;
|
|
34
|
+
const index = originStrTitle.indexOf(search);
|
|
35
|
+
const beforeStr = originStrTitle.substring(0, index);
|
|
36
|
+
const afterStr = originStrTitle.slice(index + search.length);
|
|
37
37
|
const title =
|
|
38
38
|
index > -1 ? (
|
|
39
39
|
<span key={item.key}>
|
|
@@ -42,7 +42,7 @@ function useHighLightTreeData({ treeData, search }) {
|
|
|
42
42
|
{afterStr}
|
|
43
43
|
</span>
|
|
44
44
|
) : (
|
|
45
|
-
<span key={item.key}>{
|
|
45
|
+
<span key={item.key}>{originStrTitle}</span>
|
|
46
46
|
);
|
|
47
47
|
|
|
48
48
|
if (item.children) {
|
|
@@ -52,6 +52,7 @@ function useHighLightTreeData({ treeData, search }) {
|
|
|
52
52
|
return {
|
|
53
53
|
...item,
|
|
54
54
|
title,
|
|
55
|
+
originData: item,
|
|
55
56
|
};
|
|
56
57
|
});
|
|
57
58
|
};
|
|
@@ -76,21 +77,23 @@ function useFilterTreeData({ treeData, search }) {
|
|
|
76
77
|
|
|
77
78
|
// 递归过滤树形数据
|
|
78
79
|
const filterTree = (nodes) => {
|
|
80
|
+
// 返回自己。 而非 [] undefined,因为要保留原数据格式。
|
|
79
81
|
if (!nodes || nodes.length === 0) {
|
|
80
|
-
return
|
|
82
|
+
return nodes;
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
return nodes
|
|
84
86
|
.map((node) => {
|
|
85
|
-
const children = node.children ? filterTree(node.children) : [];
|
|
86
87
|
const isMatch = isNodeMatch(node);
|
|
87
|
-
|
|
88
|
+
|
|
89
|
+
const children = filterTree(node.children);
|
|
90
|
+
const hasMatchingChildren = children?.length > 0;
|
|
88
91
|
|
|
89
92
|
// 如果当前节点匹配或者有匹配的子节点,则保留该节点
|
|
90
93
|
if (isMatch || hasMatchingChildren) {
|
|
91
94
|
return {
|
|
92
95
|
...node,
|
|
93
|
-
children
|
|
96
|
+
children,
|
|
94
97
|
};
|
|
95
98
|
}
|
|
96
99
|
|
|
@@ -105,7 +108,11 @@ function useFilterTreeData({ treeData, search }) {
|
|
|
105
108
|
|
|
106
109
|
function useIsAllLeaf(treeData?: DataNode[]) {
|
|
107
110
|
return useMemo(() => {
|
|
108
|
-
|
|
111
|
+
if (treeData) {
|
|
112
|
+
return !treeData.find((item) => item.children);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return true;
|
|
109
116
|
}, [treeData]);
|
|
110
117
|
}
|
|
111
118
|
|