rspress-plugin-file-tree 0.4.0 → 1.0.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/dist/components/FileTree/FileTree.d.ts +7 -0
- package/dist/components/FileTree/FileTree.js +13 -0
- package/dist/components/FileTree/FileTree.module.js +5 -0
- package/dist/components/FileTree/FileTreeItem.d.ts +8 -0
- package/dist/components/FileTree/FileTreeItem.js +80 -0
- package/dist/components/FileTree/FileTreeItem.module.js +11 -0
- package/dist/components/FileTree/FileTreeItem_module.css +71 -0
- package/dist/components/FileTree/FileTree_module.css +12 -0
- package/dist/components/FileTree/RemoteSvgIcon.d.ts +11 -0
- package/dist/components/FileTree/RemoteSvgIcon.js +26 -0
- package/dist/components/FileTree/index.d.ts +4 -0
- package/dist/components/FileTree/index.js +4 -0
- package/dist/components/folder-icons.d.ts +32 -0
- package/dist/components/folder-icons.js +177 -0
- package/dist/components/languages.d.ts +7 -0
- package/dist/components/languages.js +98 -0
- package/dist/components/tree-parser/tree-parser.d.ts +12 -0
- package/dist/components/tree-parser/tree-parser.js +68 -0
- package/dist/components/tree-parser/types.d.ts +10 -0
- package/dist/components/tree-parser/types.js +0 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +18 -17
- package/package.json +11 -8
- package/components/Tree/Expand.tsx +0 -149
- package/components/Tree/FileIcon.tsx +0 -41
- package/components/Tree/FileTreeRender.tsx +0 -16
- package/components/Tree/Tree.tsx +0 -112
- package/components/Tree/TreeContext.tsx +0 -18
- package/components/Tree/TreeFile.tsx +0 -69
- package/components/Tree/TreeFolder.tsx +0 -108
- package/components/Tree/TreeFolderIcon.tsx +0 -40
- package/components/Tree/TreeIndents.tsx +0 -26
- package/components/Tree/TreeStatusIcon.tsx +0 -45
- package/components/Tree/index.less +0 -178
- package/components/helpers.ts +0 -42
- package/components/presets.ts +0 -5
- package/dist/parser.d.ts +0 -8
- package/dist/parser.js +0 -40
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,31 +1,32 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
1
|
+
import node_path from "node:path";
|
|
2
|
+
import { RemarkCodeBlockToGlobalComponentPluginFactory } from "rspress-plugin-devkit";
|
|
3
|
+
import { parseTreeContent } from "./components/tree-parser/tree-parser.js";
|
|
4
|
+
const PACKAGE_ROOT = node_path.resolve(__dirname, '../');
|
|
5
|
+
function rspressPluginFileTree(options = {}) {
|
|
5
6
|
const { initialExpandDepth = 0 } = options;
|
|
6
7
|
const remarkFileTree = new RemarkCodeBlockToGlobalComponentPluginFactory({
|
|
7
8
|
components: [
|
|
8
9
|
{
|
|
9
10
|
lang: 'tree',
|
|
10
|
-
componentPath:
|
|
11
|
-
propsProvider(code) {
|
|
11
|
+
componentPath: node_path.join(PACKAGE_ROOT, 'dist/components/FileTree/FileTree'),
|
|
12
|
+
propsProvider (code) {
|
|
12
13
|
return {
|
|
13
|
-
|
|
14
|
-
initialExpandDepth
|
|
14
|
+
nodes: parseTreeContent(code).nodes,
|
|
15
|
+
initialExpandDepth
|
|
15
16
|
};
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
]
|
|
19
20
|
});
|
|
20
21
|
return {
|
|
21
22
|
name: 'rspress-plugin-file-tree',
|
|
22
|
-
config(config) {
|
|
23
|
-
return new PresetConfigMutator(config).disableMdxRs().toConfig();
|
|
24
|
-
},
|
|
25
23
|
markdown: {
|
|
26
|
-
remarkPlugins: [
|
|
27
|
-
|
|
24
|
+
remarkPlugins: [
|
|
25
|
+
remarkFileTree.remarkPlugin
|
|
26
|
+
],
|
|
27
|
+
globalComponents: remarkFileTree.mdxComponents
|
|
28
28
|
},
|
|
29
|
-
builderConfig: remarkFileTree.builderConfig
|
|
29
|
+
builderConfig: remarkFileTree.builderConfig
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
|
+
export { rspressPluginFileTree as default };
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rspress-plugin-file-tree",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Rspress plugin that add support for file tree component.",
|
|
5
5
|
"files": [
|
|
6
|
-
"dist"
|
|
7
|
-
"components"
|
|
6
|
+
"dist"
|
|
8
7
|
],
|
|
8
|
+
"type": "module",
|
|
9
9
|
"keywords": [
|
|
10
10
|
"rspress",
|
|
11
11
|
"plugin",
|
|
@@ -28,20 +28,23 @@
|
|
|
28
28
|
"access": "public"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"
|
|
32
|
-
"rspress-plugin-devkit": "^0.3.0"
|
|
31
|
+
"rspress-plugin-devkit": "^1.0.0"
|
|
33
32
|
},
|
|
34
33
|
"devDependencies": {
|
|
34
|
+
"@rsbuild/plugin-less": "^1.2.1",
|
|
35
|
+
"@rsbuild/plugin-react": "^1.1.1",
|
|
36
|
+
"@rslib/core": "^0.19.1",
|
|
35
37
|
"@types/node": "^20.12.5",
|
|
36
38
|
"@types/react": "^18.2.74",
|
|
39
|
+
"material-icon-theme": "^5.30.0",
|
|
37
40
|
"typescript": "^5.4.4"
|
|
38
41
|
},
|
|
39
42
|
"peerDependencies": {
|
|
40
|
-
"rspress": "
|
|
43
|
+
"@rspress/core": "^2.0.0-rc.4 || ^2.0.0"
|
|
41
44
|
},
|
|
42
45
|
"scripts": {
|
|
43
|
-
"build": "
|
|
44
|
-
"dev": "
|
|
46
|
+
"build": "rslib build",
|
|
47
|
+
"dev": "rslib build -w",
|
|
45
48
|
"docs:build": "rspress build",
|
|
46
49
|
"docs:dev": "rspress dev"
|
|
47
50
|
}
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useRef, useState, MutableRefObject } from 'react';
|
|
2
|
-
import clsx from 'clsx';
|
|
3
|
-
|
|
4
|
-
export type ShapeType = {
|
|
5
|
-
width: number;
|
|
6
|
-
height: number;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export const getRealShape = (el: HTMLElement | null): ShapeType => {
|
|
10
|
-
const defaultShape: ShapeType = { width: 0, height: 0 };
|
|
11
|
-
if (!el || typeof window === 'undefined') return defaultShape;
|
|
12
|
-
|
|
13
|
-
const rect = el.getBoundingClientRect();
|
|
14
|
-
const { width, height } = window.getComputedStyle(el);
|
|
15
|
-
|
|
16
|
-
const getCSSStyleVal = (str: string, parentNum: number) => {
|
|
17
|
-
if (!str) return 0;
|
|
18
|
-
const strVal = str.includes('px')
|
|
19
|
-
? +str.split('px')[0]
|
|
20
|
-
: str.includes('%')
|
|
21
|
-
? +str.split('%')[0] * parentNum * 0.01
|
|
22
|
-
: str;
|
|
23
|
-
|
|
24
|
-
return Number.isNaN(+strVal) ? 0 : +strVal;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
return {
|
|
28
|
-
width: getCSSStyleVal(`${width}`, rect.width),
|
|
29
|
-
height: getCSSStyleVal(`${height}`, rect.height),
|
|
30
|
-
};
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
export type ShapeResult = [ShapeType, () => void];
|
|
34
|
-
|
|
35
|
-
const useRealShape = <T extends HTMLElement>(
|
|
36
|
-
ref: MutableRefObject<T | null>,
|
|
37
|
-
): ShapeResult => {
|
|
38
|
-
const [state, setState] = useState<ShapeType>({
|
|
39
|
-
width: 0,
|
|
40
|
-
height: 0,
|
|
41
|
-
});
|
|
42
|
-
const update = () => {
|
|
43
|
-
const { width, height } = getRealShape(ref.current);
|
|
44
|
-
setState({ width, height });
|
|
45
|
-
};
|
|
46
|
-
useEffect(() => update(), [ref.current]);
|
|
47
|
-
|
|
48
|
-
return [state, update];
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
export type ExpandProps = {
|
|
52
|
-
isExpanded?: boolean;
|
|
53
|
-
delay?: number;
|
|
54
|
-
parentExpanded?: boolean[];
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const defaultProps = {
|
|
58
|
-
isExpanded: false,
|
|
59
|
-
delay: 200,
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const Expand: React.FC<React.PropsWithChildren<ExpandProps>> = ({
|
|
63
|
-
isExpanded,
|
|
64
|
-
delay,
|
|
65
|
-
parentExpanded = [],
|
|
66
|
-
children,
|
|
67
|
-
}: React.PropsWithChildren<ExpandProps> & typeof defaultProps) => {
|
|
68
|
-
const [height, setHeight] = useState<string>(isExpanded ? 'auto' : '0');
|
|
69
|
-
const [selfExpanded, setSelfExpanded] = useState<boolean>(isExpanded);
|
|
70
|
-
const [visible, setVisible] = useState<boolean>(isExpanded);
|
|
71
|
-
const contentRef = useRef<HTMLDivElement>(null);
|
|
72
|
-
const entryTimer = useRef<number>();
|
|
73
|
-
const leaveTimer = useRef<number>();
|
|
74
|
-
const resetTimer = useRef<number>();
|
|
75
|
-
const [state, updateShape] = useRealShape<HTMLDivElement>(contentRef);
|
|
76
|
-
|
|
77
|
-
const [parentClosed, setParentClosed] = useState<boolean>(false);
|
|
78
|
-
|
|
79
|
-
useEffect(() => setHeight(`${state.height}px`), [state.height]);
|
|
80
|
-
useEffect(() => {
|
|
81
|
-
// show element or reset height.
|
|
82
|
-
// force an update once manually, even if the element does not change.
|
|
83
|
-
// (the height of the element might be "auto")
|
|
84
|
-
if (isExpanded) {
|
|
85
|
-
setVisible(isExpanded);
|
|
86
|
-
} else {
|
|
87
|
-
updateShape();
|
|
88
|
-
setHeight(`${state.height}px`);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// show expand animation
|
|
92
|
-
entryTimer.current = window.setTimeout(() => {
|
|
93
|
-
setSelfExpanded(isExpanded);
|
|
94
|
-
clearTimeout(entryTimer.current);
|
|
95
|
-
}, 30);
|
|
96
|
-
|
|
97
|
-
// Reset height after animation
|
|
98
|
-
if (isExpanded) {
|
|
99
|
-
resetTimer.current = window.setTimeout(() => {
|
|
100
|
-
setHeight('auto');
|
|
101
|
-
clearTimeout(resetTimer.current);
|
|
102
|
-
}, delay);
|
|
103
|
-
} else {
|
|
104
|
-
leaveTimer.current = window.setTimeout(() => {
|
|
105
|
-
setVisible(isExpanded);
|
|
106
|
-
clearTimeout(leaveTimer.current);
|
|
107
|
-
}, delay / 2);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return () => {
|
|
111
|
-
clearTimeout(entryTimer.current);
|
|
112
|
-
clearTimeout(leaveTimer.current);
|
|
113
|
-
clearTimeout(resetTimer.current);
|
|
114
|
-
};
|
|
115
|
-
}, [isExpanded]);
|
|
116
|
-
|
|
117
|
-
useEffect(() => {
|
|
118
|
-
const parentClosed = parentExpanded.some((i) => i === false);
|
|
119
|
-
|
|
120
|
-
setParentClosed(parentClosed);
|
|
121
|
-
}, [parentExpanded]);
|
|
122
|
-
|
|
123
|
-
return (
|
|
124
|
-
<div
|
|
125
|
-
className={clsx('rspress-file-tree-expand-container', {
|
|
126
|
-
'rspress-file-tree-expand-container-expanded': isExpanded,
|
|
127
|
-
})}
|
|
128
|
-
style={{
|
|
129
|
-
height: 0,
|
|
130
|
-
visibility: visible && !parentClosed ? 'visible' : 'hidden',
|
|
131
|
-
transition: `height ${delay}ms ease`,
|
|
132
|
-
...(selfExpanded
|
|
133
|
-
? { height, visibility: parentClosed ? 'hidden' : 'visible' }
|
|
134
|
-
: {}),
|
|
135
|
-
}}
|
|
136
|
-
>
|
|
137
|
-
<div
|
|
138
|
-
ref={contentRef}
|
|
139
|
-
className={clsx('rspress-file-tree-expand-content')}
|
|
140
|
-
>
|
|
141
|
-
{children}
|
|
142
|
-
</div>
|
|
143
|
-
</div>
|
|
144
|
-
);
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
Expand.defaultProps = defaultProps;
|
|
148
|
-
Expand.displayName = 'GeistExpand';
|
|
149
|
-
export default Expand;
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { buildClassName } from '../presets';
|
|
3
|
-
|
|
4
|
-
export interface TreeFileIconProps {
|
|
5
|
-
color?: string;
|
|
6
|
-
width?: number;
|
|
7
|
-
height?: number;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const defaultProps = {
|
|
11
|
-
width: 22,
|
|
12
|
-
height: 22,
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const TreeFileIcon: React.FC<TreeFileIconProps> = ({
|
|
16
|
-
color,
|
|
17
|
-
width,
|
|
18
|
-
height,
|
|
19
|
-
}: TreeFileIconProps & typeof defaultProps) => {
|
|
20
|
-
return (
|
|
21
|
-
<svg
|
|
22
|
-
className={buildClassName('file-icon')}
|
|
23
|
-
viewBox="0 0 24 24"
|
|
24
|
-
width={width}
|
|
25
|
-
height={height}
|
|
26
|
-
stroke="currentColor"
|
|
27
|
-
strokeWidth="1"
|
|
28
|
-
strokeLinecap="round"
|
|
29
|
-
strokeLinejoin="round"
|
|
30
|
-
fill="none"
|
|
31
|
-
shapeRendering="geometricPrecision"
|
|
32
|
-
>
|
|
33
|
-
<path d="M13 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V9z" />
|
|
34
|
-
<path d="M13 2v7h7" />
|
|
35
|
-
</svg>
|
|
36
|
-
);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
TreeFileIcon.defaultProps = defaultProps;
|
|
40
|
-
TreeFileIcon.displayName = 'GeistTreeFileIcon';
|
|
41
|
-
export default TreeFileIcon;
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import Tree from './Tree';
|
|
2
|
-
import TreeFile from './TreeFile';
|
|
3
|
-
import TreeFolder from './TreeFolder';
|
|
4
|
-
|
|
5
|
-
import './index.less';
|
|
6
|
-
|
|
7
|
-
export type TreeComponentType = typeof Tree & {
|
|
8
|
-
File: typeof TreeFile;
|
|
9
|
-
Folder: typeof TreeFolder;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
(Tree as TreeComponentType).File = TreeFile;
|
|
13
|
-
(Tree as TreeComponentType).Folder = TreeFolder;
|
|
14
|
-
|
|
15
|
-
export type { TreeProps, TreeFile } from './Tree';
|
|
16
|
-
export default Tree as TreeComponentType;
|
package/components/Tree/Tree.tsx
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import React, { useMemo } from 'react';
|
|
2
|
-
import TreeFile from './TreeFile';
|
|
3
|
-
import TreeFolder from './TreeFolder';
|
|
4
|
-
import { TreeContext } from './TreeContext';
|
|
5
|
-
import { sortChildren } from '../helpers';
|
|
6
|
-
import { buildClassName } from '../presets';
|
|
7
|
-
import { useDark } from 'rspress/runtime';
|
|
8
|
-
|
|
9
|
-
export const tuple = <T extends string[]>(...args: T) => args;
|
|
10
|
-
|
|
11
|
-
const FileTreeValueType = tuple('directory', 'file');
|
|
12
|
-
|
|
13
|
-
const directoryType = FileTreeValueType[0];
|
|
14
|
-
|
|
15
|
-
export type TreeFile = {
|
|
16
|
-
type: (typeof FileTreeValueType)[number];
|
|
17
|
-
name: string;
|
|
18
|
-
extra?: string;
|
|
19
|
-
files?: Array<TreeFile>;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
interface Props {
|
|
23
|
-
tree?: TreeFile[];
|
|
24
|
-
initialExpand?: boolean;
|
|
25
|
-
onClick?: (path: string) => void;
|
|
26
|
-
className?: string;
|
|
27
|
-
initialExpandDepth?: number;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const defaultProps = {
|
|
31
|
-
initialExpand: false,
|
|
32
|
-
className: '',
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>;
|
|
36
|
-
export type TreeProps = Props & NativeAttrs;
|
|
37
|
-
|
|
38
|
-
const makeChildren = (value: Array<TreeFile> = []) => {
|
|
39
|
-
if (!value || !value.length) return null;
|
|
40
|
-
return value
|
|
41
|
-
.sort((a, b) => {
|
|
42
|
-
if (a.type !== b.type) return a.type !== directoryType ? 1 : -1;
|
|
43
|
-
|
|
44
|
-
return `${a.name}`.charCodeAt(0) - `${b.name}`.charCodeAt(0);
|
|
45
|
-
})
|
|
46
|
-
.map((item, index) => {
|
|
47
|
-
if (item.type === directoryType)
|
|
48
|
-
return (
|
|
49
|
-
<TreeFolder
|
|
50
|
-
name={item.name}
|
|
51
|
-
extra={item.extra}
|
|
52
|
-
key={`folder-${item.name}-${index}`}
|
|
53
|
-
parentExpanded={[]}
|
|
54
|
-
>
|
|
55
|
-
{makeChildren(item.files)}
|
|
56
|
-
</TreeFolder>
|
|
57
|
-
);
|
|
58
|
-
return (
|
|
59
|
-
<TreeFile
|
|
60
|
-
name={item.name}
|
|
61
|
-
extra={item.extra}
|
|
62
|
-
key={`file-${item.name}-${index}`}
|
|
63
|
-
parentExpanded={[]}
|
|
64
|
-
/>
|
|
65
|
-
);
|
|
66
|
-
});
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
const Tree: React.FC<React.PropsWithChildren<TreeProps>> = ({
|
|
70
|
-
children,
|
|
71
|
-
onClick,
|
|
72
|
-
initialExpand,
|
|
73
|
-
initialExpandDepth,
|
|
74
|
-
tree,
|
|
75
|
-
className,
|
|
76
|
-
...props
|
|
77
|
-
}: React.PropsWithChildren<TreeProps> & typeof defaultProps) => {
|
|
78
|
-
if (!tree) return null;
|
|
79
|
-
|
|
80
|
-
const isImperative = Boolean(tree.length > 0);
|
|
81
|
-
const onFileClick = (path: string) => {
|
|
82
|
-
onClick && onClick(path);
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
const initialValue = useMemo(
|
|
86
|
-
() => ({
|
|
87
|
-
onFileClick,
|
|
88
|
-
initialExpand,
|
|
89
|
-
initialExpandDepth,
|
|
90
|
-
isImperative,
|
|
91
|
-
}),
|
|
92
|
-
[initialExpand],
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
const customChildren = isImperative
|
|
96
|
-
? makeChildren(tree)
|
|
97
|
-
: sortChildren(children, TreeFolder);
|
|
98
|
-
|
|
99
|
-
const dark = useDark();
|
|
100
|
-
|
|
101
|
-
return (
|
|
102
|
-
<TreeContext.Provider value={initialValue}>
|
|
103
|
-
<div data-dark={String(dark)} className={buildClassName()} {...props}>
|
|
104
|
-
{customChildren}
|
|
105
|
-
</div>
|
|
106
|
-
</TreeContext.Provider>
|
|
107
|
-
);
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
Tree.defaultProps = defaultProps;
|
|
111
|
-
Tree.displayName = 'GeistTree';
|
|
112
|
-
export default Tree;
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
|
|
3
|
-
export interface TreeConfig {
|
|
4
|
-
onFileClick?: (path: string) => void;
|
|
5
|
-
initialExpand: boolean;
|
|
6
|
-
initialExpandDepth?: number;
|
|
7
|
-
isImperative: boolean;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const defaultContext = {
|
|
11
|
-
initialExpand: false,
|
|
12
|
-
isImperative: false,
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export const TreeContext = React.createContext<TreeConfig>(defaultContext);
|
|
16
|
-
|
|
17
|
-
export const useTreeContext = (): TreeConfig =>
|
|
18
|
-
React.useContext<TreeConfig>(TreeContext);
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import React, { useMemo } from 'react';
|
|
2
|
-
import TreeFileIcon from './FileIcon';
|
|
3
|
-
import { useTreeContext } from './TreeContext';
|
|
4
|
-
import TreeIndents from './TreeIndents';
|
|
5
|
-
import { makeChildPath, stopPropagation } from '../helpers';
|
|
6
|
-
import { buildClassName } from '../presets';
|
|
7
|
-
|
|
8
|
-
interface Props {
|
|
9
|
-
name: string;
|
|
10
|
-
extra?: string;
|
|
11
|
-
parentPath?: string;
|
|
12
|
-
level?: number;
|
|
13
|
-
className?: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const defaultProps = {
|
|
17
|
-
level: 0,
|
|
18
|
-
className: '',
|
|
19
|
-
parentPath: '',
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>;
|
|
23
|
-
export type TreeFileProps = Props &
|
|
24
|
-
NativeAttrs & {
|
|
25
|
-
parentExpanded: boolean[];
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const TreeFile: React.FC<React.PropsWithChildren<TreeFileProps>> = ({
|
|
29
|
-
name,
|
|
30
|
-
parentPath,
|
|
31
|
-
level,
|
|
32
|
-
extra,
|
|
33
|
-
className,
|
|
34
|
-
parentExpanded,
|
|
35
|
-
...props
|
|
36
|
-
}: React.PropsWithChildren<TreeFileProps> & typeof defaultProps) => {
|
|
37
|
-
const { onFileClick } = useTreeContext();
|
|
38
|
-
const currentPath = useMemo(() => makeChildPath(name, parentPath), []);
|
|
39
|
-
const clickHandler = (event: React.MouseEvent) => {
|
|
40
|
-
stopPropagation(event);
|
|
41
|
-
onFileClick && onFileClick(currentPath);
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
return (
|
|
45
|
-
<div className={buildClassName('file')} onClick={clickHandler} {...props}>
|
|
46
|
-
<div
|
|
47
|
-
className={buildClassName('file-names')}
|
|
48
|
-
style={{
|
|
49
|
-
marginLeft: `calc(1.875rem * ${level})`,
|
|
50
|
-
}}
|
|
51
|
-
>
|
|
52
|
-
<TreeIndents count={level} />
|
|
53
|
-
<span className={buildClassName('file-icon')}>
|
|
54
|
-
<TreeFileIcon />
|
|
55
|
-
</span>
|
|
56
|
-
<span className={buildClassName('file-name')}>
|
|
57
|
-
{name}
|
|
58
|
-
{extra && (
|
|
59
|
-
<span className={buildClassName('file-extra')}>{extra}</span>
|
|
60
|
-
)}
|
|
61
|
-
</span>
|
|
62
|
-
</div>
|
|
63
|
-
</div>
|
|
64
|
-
);
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
TreeFile.defaultProps = defaultProps;
|
|
68
|
-
TreeFile.displayName = 'GeistTreeFile';
|
|
69
|
-
export default TreeFile;
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useMemo, useState } from 'react';
|
|
2
|
-
import TreeFile from './TreeFile';
|
|
3
|
-
import Expand from './Expand';
|
|
4
|
-
import TreeIndents from './TreeIndents';
|
|
5
|
-
import { useTreeContext } from './TreeContext';
|
|
6
|
-
import TreeFolderIcon from './TreeFolderIcon';
|
|
7
|
-
import TreeStatusIcon from './TreeStatusIcon';
|
|
8
|
-
import {
|
|
9
|
-
sortChildren,
|
|
10
|
-
makeChildPath,
|
|
11
|
-
stopPropagation,
|
|
12
|
-
setChildrenProps,
|
|
13
|
-
} from '../helpers';
|
|
14
|
-
import { buildClassName } from '../presets';
|
|
15
|
-
|
|
16
|
-
interface Props {
|
|
17
|
-
name: string;
|
|
18
|
-
extra?: string;
|
|
19
|
-
parentPath?: string;
|
|
20
|
-
level?: number;
|
|
21
|
-
className?: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const defaultProps = {
|
|
25
|
-
level: 0,
|
|
26
|
-
className: '',
|
|
27
|
-
parentPath: '',
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>;
|
|
31
|
-
export type TreeFolderProps = Props &
|
|
32
|
-
NativeAttrs & {
|
|
33
|
-
parentExpanded: boolean[];
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const TreeFolder: React.FC<React.PropsWithChildren<TreeFolderProps>> = ({
|
|
37
|
-
name,
|
|
38
|
-
children,
|
|
39
|
-
parentPath,
|
|
40
|
-
level: parentLevel,
|
|
41
|
-
extra,
|
|
42
|
-
className,
|
|
43
|
-
parentExpanded = [],
|
|
44
|
-
...props
|
|
45
|
-
}: React.PropsWithChildren<TreeFolderProps> & typeof defaultProps) => {
|
|
46
|
-
const {
|
|
47
|
-
initialExpand,
|
|
48
|
-
isImperative,
|
|
49
|
-
initialExpandDepth = 0,
|
|
50
|
-
} = useTreeContext();
|
|
51
|
-
const [expanded, setExpanded] = useState<boolean>(() => {
|
|
52
|
-
return parentLevel + 1 <= initialExpandDepth;
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
const currentPath = useMemo(() => makeChildPath(name, parentPath), []);
|
|
56
|
-
const clickHandler = () => setExpanded(!expanded);
|
|
57
|
-
|
|
58
|
-
const nextChildren = setChildrenProps(
|
|
59
|
-
children,
|
|
60
|
-
{
|
|
61
|
-
parentPath: currentPath,
|
|
62
|
-
level: parentLevel + 1,
|
|
63
|
-
parentExpanded: [...parentExpanded, expanded],
|
|
64
|
-
},
|
|
65
|
-
[TreeFolder, TreeFile],
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
const sortedChildren = isImperative
|
|
69
|
-
? nextChildren
|
|
70
|
-
: sortChildren(nextChildren, TreeFolder);
|
|
71
|
-
|
|
72
|
-
return (
|
|
73
|
-
<div className={buildClassName('folder')} onClick={clickHandler} {...props}>
|
|
74
|
-
<div
|
|
75
|
-
className={buildClassName('folder-names')}
|
|
76
|
-
style={{
|
|
77
|
-
marginLeft: `calc(1.875rem * ${parentLevel})`,
|
|
78
|
-
}}
|
|
79
|
-
>
|
|
80
|
-
<TreeIndents count={parentLevel} />
|
|
81
|
-
<span className={buildClassName('folder-status')}>
|
|
82
|
-
<TreeStatusIcon active={expanded} />
|
|
83
|
-
</span>
|
|
84
|
-
<span className={buildClassName('folder-icon')}>
|
|
85
|
-
<TreeFolderIcon />
|
|
86
|
-
</span>
|
|
87
|
-
<span className={buildClassName('folder-name')}>
|
|
88
|
-
{name}
|
|
89
|
-
{extra && (
|
|
90
|
-
<span className={buildClassName('folder-extra')}>{extra}</span>
|
|
91
|
-
)}
|
|
92
|
-
</span>
|
|
93
|
-
</div>
|
|
94
|
-
<Expand isExpanded={expanded} parentExpanded={parentExpanded}>
|
|
95
|
-
<div
|
|
96
|
-
className={buildClassName('folder-content')}
|
|
97
|
-
onClick={stopPropagation}
|
|
98
|
-
>
|
|
99
|
-
{sortedChildren}
|
|
100
|
-
</div>
|
|
101
|
-
</Expand>
|
|
102
|
-
</div>
|
|
103
|
-
);
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
TreeFolder.defaultProps = defaultProps;
|
|
107
|
-
TreeFolder.displayName = 'GeistTreeFolder';
|
|
108
|
-
export default TreeFolder;
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { buildClassName } from '../presets';
|
|
3
|
-
|
|
4
|
-
export interface TreeFolderIconProps {
|
|
5
|
-
color?: string;
|
|
6
|
-
width?: number;
|
|
7
|
-
height?: number;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const defaultProps = {
|
|
11
|
-
width: 22,
|
|
12
|
-
height: 22,
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const TreeFolderIcon: React.FC<TreeFolderIconProps> = ({
|
|
16
|
-
color,
|
|
17
|
-
width,
|
|
18
|
-
height,
|
|
19
|
-
}: TreeFolderIconProps & typeof defaultProps) => {
|
|
20
|
-
return (
|
|
21
|
-
<svg
|
|
22
|
-
className={buildClassName('folder-icon')}
|
|
23
|
-
viewBox="0 0 24 24"
|
|
24
|
-
width={width}
|
|
25
|
-
height={height}
|
|
26
|
-
stroke="currentColor"
|
|
27
|
-
strokeWidth="1"
|
|
28
|
-
strokeLinecap="round"
|
|
29
|
-
strokeLinejoin="round"
|
|
30
|
-
fill="none"
|
|
31
|
-
shapeRendering="geometricPrecision"
|
|
32
|
-
>
|
|
33
|
-
<path d="M2.707 7.454V5.62C2.707 4.725 3.469 4 4.409 4h4.843c.451 0 .884.17 1.204.474l.49.467c.126.12.296.186.473.186h8.399c.94 0 1.55.695 1.55 1.59v.737m-18.661 0h-.354a.344.344 0 00-.353.35l.508 11.587c.015.34.31.609.668.609h17.283c.358 0 .652-.269.667-.61L22 7.805a.344.344 0 00-.353-.35h-.278m-18.662 0h18.662" />
|
|
34
|
-
</svg>
|
|
35
|
-
);
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
TreeFolderIcon.defaultProps = defaultProps;
|
|
39
|
-
TreeFolderIcon.displayName = 'GeistTreeFolderIcon';
|
|
40
|
-
export default TreeFolderIcon;
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { buildClassName } from '../presets';
|
|
3
|
-
|
|
4
|
-
interface Props {
|
|
5
|
-
count: number;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
const TreeIndents: React.FC<Props> = ({ count }) => {
|
|
9
|
-
if (count === 0) return null;
|
|
10
|
-
|
|
11
|
-
return (
|
|
12
|
-
<>
|
|
13
|
-
{[...new Array(count)].map((_, index) => (
|
|
14
|
-
<span
|
|
15
|
-
className={buildClassName('indent')}
|
|
16
|
-
key={`indent-${index}`}
|
|
17
|
-
style={{
|
|
18
|
-
left: `calc(-1.875rem * ${index + 1} + 0.75rem)`,
|
|
19
|
-
}}
|
|
20
|
-
></span>
|
|
21
|
-
))}
|
|
22
|
-
</>
|
|
23
|
-
);
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
export default TreeIndents;
|