@fe-free/core 2.1.6 → 2.2.0
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 +17 -0
- package/package.json +2 -2
- package/src/index.ts +3 -0
- package/src/page_layout/index.tsx +9 -3
- package/src/page_layout/page_layout.stories.tsx +40 -0
- package/src/style.scss +41 -0
- package/src/tailwindcss.stories.tsx +8 -0
- package/src/tree/index.tsx +130 -0
- package/src/tree/tree.stories.tsx +56 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @fe-free/core
|
|
2
2
|
|
|
3
|
+
## 2.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- feat: tree and page layout
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- @fe-free/tool@2.2.0
|
|
12
|
+
|
|
13
|
+
## 2.1.7
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- feat: tailwindcss
|
|
18
|
+
- @fe-free/tool@2.1.7
|
|
19
|
+
|
|
3
20
|
## 2.1.6
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fe-free/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"author": "",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"remark-gfm": "^4.0.1",
|
|
40
40
|
"vanilla-jsoneditor": "^0.23.1",
|
|
41
41
|
"zustand": "^4.5.4",
|
|
42
|
-
"@fe-free/tool": "2.
|
|
42
|
+
"@fe-free/tool": "2.2.0"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"@ant-design/pro-components": "^2.8.7",
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import './style.scss';
|
|
2
|
+
|
|
1
3
|
export { useGlobalInfiniteScroll } from './ahooks/use_global_infinite_scroll';
|
|
2
4
|
export { useGlobalRequest } from './ahooks/use_global_request';
|
|
3
5
|
export { LoadingButton } from './button';
|
|
@@ -30,5 +32,6 @@ export { Markdown } from './markdown';
|
|
|
30
32
|
export { PageLayout } from './page_layout';
|
|
31
33
|
export { Table } from './table';
|
|
32
34
|
export type { TableProps } from './table';
|
|
35
|
+
export { Tree } from './tree';
|
|
33
36
|
export { useLocalforageState } from './use_localforage_state';
|
|
34
37
|
export { CustomValueTypeEnum, customValueTypeMap } from './value_type_map';
|
|
@@ -6,6 +6,9 @@ interface PageLayoutProps {
|
|
|
6
6
|
children?: React.ReactNode;
|
|
7
7
|
end?: React.ReactNode;
|
|
8
8
|
className?: string;
|
|
9
|
+
startClassName?: string;
|
|
10
|
+
childrenClassName?: string;
|
|
11
|
+
endClassName?: string;
|
|
9
12
|
}
|
|
10
13
|
|
|
11
14
|
function PageLayout({
|
|
@@ -14,6 +17,9 @@ function PageLayout({
|
|
|
14
17
|
children,
|
|
15
18
|
end,
|
|
16
19
|
className,
|
|
20
|
+
startClassName,
|
|
21
|
+
childrenClassName,
|
|
22
|
+
endClassName,
|
|
17
23
|
}: PageLayoutProps) {
|
|
18
24
|
return (
|
|
19
25
|
<div
|
|
@@ -26,9 +32,9 @@ function PageLayout({
|
|
|
26
32
|
className,
|
|
27
33
|
)}
|
|
28
34
|
>
|
|
29
|
-
<div className=
|
|
30
|
-
<div className=
|
|
31
|
-
<div className=
|
|
35
|
+
<div className={cn('flex-none', startClassName)}>{start}</div>
|
|
36
|
+
<div className={cn('flex-1 overflow-auto', childrenClassName)}>{children}</div>
|
|
37
|
+
<div className={cn('flex-none', endClassName)}>{end}</div>
|
|
32
38
|
</div>
|
|
33
39
|
);
|
|
34
40
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { PageLayout } from '@fe-free/core';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof PageLayout> = {
|
|
5
|
+
title: '@fe-free/core/PageLayout',
|
|
6
|
+
component: PageLayout,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
type Story = StoryObj<typeof meta>;
|
|
12
|
+
|
|
13
|
+
export const Default: Story = {
|
|
14
|
+
args: {
|
|
15
|
+
start: <div className="bg-red-500 w-[100px] h-[100px]">start</div>,
|
|
16
|
+
children: <div className="bg-blue-500 h-[100px]">children</div>,
|
|
17
|
+
end: <div className="bg-green-500 w-[100px] h-[100px]">end</div>,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const DirectionVertical: Story = {
|
|
22
|
+
args: {
|
|
23
|
+
direction: 'vertical',
|
|
24
|
+
start: <div className="bg-red-500 h-[100px]">start</div>,
|
|
25
|
+
children: <div className="bg-blue-500 h-[100px]">children</div>,
|
|
26
|
+
end: <div className="bg-green-500 h-[100px]">end</div>,
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const ClassName: Story = {
|
|
31
|
+
args: {
|
|
32
|
+
className: '!w-[500px] !h-[500px]',
|
|
33
|
+
startClassName: 'p-4 bg-red-200',
|
|
34
|
+
childrenClassName: 'p-4 bg-blue-200',
|
|
35
|
+
endClassName: 'p-4 bg-green-200',
|
|
36
|
+
start: <div className="bg-red-500 w-[100px] h-full">start</div>,
|
|
37
|
+
children: <div className="bg-blue-500 h-full">children</div>,
|
|
38
|
+
end: <div className="bg-green-500 w-[100px] h-full">end</div>,
|
|
39
|
+
},
|
|
40
|
+
};
|
package/src/style.scss
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** 和 antd 冲突,不开启 */
|
|
2
|
+
/* stylelint-disable-next-line at-rule-no-unknown */
|
|
3
|
+
// @tailwind base;
|
|
4
|
+
|
|
5
|
+
/* stylelint-disable-next-line at-rule-no-unknown */
|
|
6
|
+
@tailwind components;
|
|
7
|
+
|
|
8
|
+
/* stylelint-disable-next-line at-rule-no-unknown */
|
|
9
|
+
@tailwind utilities;
|
|
10
|
+
|
|
11
|
+
.c-border {
|
|
12
|
+
border: 1px solid #f0f0f0;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.c-border-bottom {
|
|
16
|
+
border-bottom: 1px solid #f0f0f0;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.c-border-top {
|
|
20
|
+
border-top: 1px solid #f0f0f0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.c-border-left {
|
|
24
|
+
border-left: 1px solid #f0f0f0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.c-border-right {
|
|
28
|
+
border-right: 1px solid #f0f0f0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.c-bg {
|
|
32
|
+
background-color: #f3f5f6;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.c-bg-hover {
|
|
36
|
+
background-color: #f3f5f6;
|
|
37
|
+
|
|
38
|
+
&:hover {
|
|
39
|
+
background-color: #e9ebec;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -12,6 +12,14 @@ export const Default = () => {
|
|
|
12
12
|
<span className="text-secondary">text-secondary</span>
|
|
13
13
|
<span className="text-desc">text-desc</span>
|
|
14
14
|
</div>
|
|
15
|
+
<div>
|
|
16
|
+
<div className="c-border-bottom">c-border-bottom</div>
|
|
17
|
+
<div className="c-border-top">c-border-top</div>
|
|
18
|
+
<div className="c-border-left">c-border-left</div>
|
|
19
|
+
<div className="c-border-right">c-border-right</div>
|
|
20
|
+
<div className="c-bg">c-bg</div>
|
|
21
|
+
<div className="c-bg-hover">c-bg-hover</div>
|
|
22
|
+
</div>
|
|
15
23
|
</div>
|
|
16
24
|
);
|
|
17
25
|
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { PageLayout } from '@fe-free/core';
|
|
2
|
+
import { useDebounce } from 'ahooks';
|
|
3
|
+
import type { TreeProps as AntdTreeProps } from 'antd';
|
|
4
|
+
import { Tree as AntdTree, Input } from 'antd';
|
|
5
|
+
import type { DataNode } from 'antd/es/tree';
|
|
6
|
+
import { useMemo, useState } from 'react';
|
|
7
|
+
|
|
8
|
+
interface TreeProps<T extends DataNode> extends AntdTreeProps<T> {
|
|
9
|
+
enableSearch?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function useHighLightTreeData({ treeData, search }) {
|
|
13
|
+
return useMemo(() => {
|
|
14
|
+
if (!search) {
|
|
15
|
+
return treeData;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const loop = (data) => {
|
|
19
|
+
return data.map((item) => {
|
|
20
|
+
const strTitle = item.title as string;
|
|
21
|
+
const index = strTitle.indexOf(search);
|
|
22
|
+
const beforeStr = strTitle.substring(0, index);
|
|
23
|
+
const afterStr = strTitle.slice(index + search.length);
|
|
24
|
+
const title =
|
|
25
|
+
index > -1 ? (
|
|
26
|
+
<span key={item.key}>
|
|
27
|
+
{beforeStr}
|
|
28
|
+
<span className="text-red-500">{search}</span>
|
|
29
|
+
{afterStr}
|
|
30
|
+
</span>
|
|
31
|
+
) : (
|
|
32
|
+
<span key={item.key}>{strTitle}</span>
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
if (item.children) {
|
|
36
|
+
return { ...item, title, children: loop(item.children) };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
...item,
|
|
41
|
+
title,
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
return loop(treeData);
|
|
47
|
+
}, [search, treeData]);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function useFilterTreeData({ treeData, search }) {
|
|
51
|
+
return useMemo(() => {
|
|
52
|
+
if (!search || !treeData) {
|
|
53
|
+
return treeData;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const searchLower = search.toLowerCase();
|
|
57
|
+
|
|
58
|
+
// 检查节点是否匹配搜索条件
|
|
59
|
+
const isNodeMatch = (node) => {
|
|
60
|
+
const title = typeof node.title === 'string' ? node.title : '';
|
|
61
|
+
return title.toLowerCase().includes(searchLower);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// 递归过滤树形数据
|
|
65
|
+
const filterTree = (nodes) => {
|
|
66
|
+
if (!nodes || nodes.length === 0) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return nodes
|
|
71
|
+
.map((node) => {
|
|
72
|
+
const children = node.children ? filterTree(node.children) : [];
|
|
73
|
+
const isMatch = isNodeMatch(node);
|
|
74
|
+
const hasMatchingChildren = children.length > 0;
|
|
75
|
+
|
|
76
|
+
// 如果当前节点匹配或者有匹配的子节点,则保留该节点
|
|
77
|
+
if (isMatch || hasMatchingChildren) {
|
|
78
|
+
return {
|
|
79
|
+
...node,
|
|
80
|
+
children: hasMatchingChildren ? children : undefined,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return null;
|
|
85
|
+
})
|
|
86
|
+
.filter(Boolean); // 过滤掉null值
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
return filterTree(treeData);
|
|
90
|
+
}, [treeData, search]);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function Tree<T extends DataNode>(props: TreeProps<T>) {
|
|
94
|
+
const { enableSearch, treeData, ...rest } = props;
|
|
95
|
+
const [search, setSearch] = useState('');
|
|
96
|
+
const debouncedSearch = useDebounce(search, { wait: 300 });
|
|
97
|
+
|
|
98
|
+
const filterTreeData = useFilterTreeData({ treeData, search: debouncedSearch });
|
|
99
|
+
const highlightedTreeData = useHighLightTreeData({
|
|
100
|
+
treeData: filterTreeData,
|
|
101
|
+
search: debouncedSearch,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const node = <AntdTree {...rest} treeData={highlightedTreeData} />;
|
|
105
|
+
|
|
106
|
+
if (!enableSearch) {
|
|
107
|
+
return node;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<PageLayout
|
|
112
|
+
direction="vertical"
|
|
113
|
+
start={
|
|
114
|
+
enableSearch && (
|
|
115
|
+
<div className="pb-2">
|
|
116
|
+
<Input.Search
|
|
117
|
+
placeholder="搜索"
|
|
118
|
+
value={search}
|
|
119
|
+
onChange={(e) => setSearch(e.target.value)}
|
|
120
|
+
/>
|
|
121
|
+
</div>
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
>
|
|
125
|
+
{node}
|
|
126
|
+
</PageLayout>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export { Tree };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Tree } from '@fe-free/core';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof Tree> = {
|
|
5
|
+
title: '@fe-free/core/Tree',
|
|
6
|
+
component: Tree,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
type Story = StoryObj<typeof meta>;
|
|
12
|
+
|
|
13
|
+
export const Default: Story = {
|
|
14
|
+
args: {
|
|
15
|
+
defaultExpandAll: true,
|
|
16
|
+
treeData: [
|
|
17
|
+
{
|
|
18
|
+
title: '0-0',
|
|
19
|
+
key: '0-0',
|
|
20
|
+
children: [
|
|
21
|
+
{ title: '0-0-0', key: '0-0-0' },
|
|
22
|
+
{ title: '0-0-1', key: '0-0-1' },
|
|
23
|
+
{ title: '0-0-2', key: '0-0-2' },
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const EnableSearch: Story = {
|
|
31
|
+
args: {
|
|
32
|
+
enableSearch: true,
|
|
33
|
+
defaultExpandAll: true,
|
|
34
|
+
treeData: [
|
|
35
|
+
{
|
|
36
|
+
title: '0-0',
|
|
37
|
+
key: '0-0',
|
|
38
|
+
children: [
|
|
39
|
+
{ title: '0-0-0', key: '0-0-0' },
|
|
40
|
+
{ title: '0-0-1', key: '0-0-1' },
|
|
41
|
+
{ title: '0-0-2', key: '0-0-2' },
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
title: '0-1',
|
|
46
|
+
key: '0-1',
|
|
47
|
+
children: [{ title: '0-1-0', key: '0-1-0' }],
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
title: '0-2',
|
|
51
|
+
key: '0-2',
|
|
52
|
+
children: [{ title: '0-2-0', key: '0-2-0' }],
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
};
|