rspress-plugin-file-tree 0.1.3-beta.0 → 0.1.4-beta.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/index.js +1 -1
- package/package.json +6 -2
- package/CHANGELOG.md +0 -40
- package/dist/components/Tree/Expand.d.ts +0 -14
- package/dist/components/Tree/Expand.js +0 -104
- package/dist/components/Tree/FileIcon.d.ts +0 -8
- package/dist/components/Tree/FileIcon.js +0 -12
- package/dist/components/Tree/FileTreeRender.d.ts +0 -11
- package/dist/components/Tree/FileTreeRender.js +0 -7
- package/dist/components/Tree/Tree.d.ts +0 -20
- package/dist/components/Tree/Tree.js +0 -52
- package/dist/components/Tree/TreeContext.d.ts +0 -9
- package/dist/components/Tree/TreeContext.js +0 -7
- package/dist/components/Tree/TreeFile.d.ts +0 -14
- package/dist/components/Tree/TreeFile.js +0 -26
- package/dist/components/Tree/TreeFolder.d.ts +0 -14
- package/dist/components/Tree/TreeFolder.js +0 -37
- package/dist/components/Tree/TreeFolderIcon.d.ts +0 -8
- package/dist/components/Tree/TreeFolderIcon.js +0 -12
- package/dist/components/Tree/TreeIndents.d.ts +0 -6
- package/dist/components/Tree/TreeIndents.js +0 -10
- package/dist/components/Tree/TreeStatusIcon.d.ts +0 -9
- package/dist/components/Tree/TreeStatusIcon.js +0 -13
- package/dist/components/helpers.d.ts +0 -5
- package/dist/components/helpers.js +0 -35
- package/dist/components/presets.d.ts +0 -2
- package/dist/components/presets.js +0 -4
- package/doc_build/static/search_index.aad48136.json +0 -1
- package/docs/index.md +0 -26
- package/image.png +0 -0
- package/rspress.config.ts +0 -13
- package/src/index.ts +0 -50
- package/src/parser.ts +0 -50
- package/tsconfig.json +0 -8
- /package/{src/components → components}/Tree/Expand.tsx +0 -0
- /package/{src/components → components}/Tree/FileIcon.tsx +0 -0
- /package/{src/components → components}/Tree/FileTreeRender.tsx +0 -0
- /package/{src/components → components}/Tree/Tree.tsx +0 -0
- /package/{src/components → components}/Tree/TreeContext.tsx +0 -0
- /package/{src/components → components}/Tree/TreeFile.tsx +0 -0
- /package/{src/components → components}/Tree/TreeFolder.tsx +0 -0
- /package/{src/components → components}/Tree/TreeFolderIcon.tsx +0 -0
- /package/{src/components → components}/Tree/TreeIndents.tsx +0 -0
- /package/{src/components → components}/Tree/TreeStatusIcon.tsx +0 -0
- /package/{src/components → components}/Tree/index.less +0 -0
- /package/{src/components → components}/helpers.ts +0 -0
- /package/{src/components → components}/presets.ts +0 -0
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ export default function rspressPluginFileTree(options = {}) {
|
|
|
7
7
|
components: [
|
|
8
8
|
{
|
|
9
9
|
lang: 'tree',
|
|
10
|
-
componentPath: path.join(__dirname, '
|
|
10
|
+
componentPath: path.join(__dirname, '../components/Tree/FileTreeRender.tsx'),
|
|
11
11
|
propsProvider(code) {
|
|
12
12
|
return {
|
|
13
13
|
tree: parseInput(code),
|
package/package.json
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rspress-plugin-file-tree",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4-beta.0",
|
|
4
4
|
"description": "Rspress plugin that add support for file tree component.",
|
|
5
|
+
"files": [
|
|
6
|
+
"dist",
|
|
7
|
+
"components"
|
|
8
|
+
],
|
|
5
9
|
"keywords": [
|
|
6
10
|
"rspress",
|
|
7
11
|
"plugin",
|
|
@@ -25,7 +29,7 @@
|
|
|
25
29
|
},
|
|
26
30
|
"dependencies": {
|
|
27
31
|
"@rspress/shared": "^1.17.1",
|
|
28
|
-
"rspress-plugin-devkit": "^0.1.3
|
|
32
|
+
"rspress-plugin-devkit": "^0.1.3"
|
|
29
33
|
},
|
|
30
34
|
"devDependencies": {
|
|
31
35
|
"@types/node": "^20.12.5",
|
package/CHANGELOG.md
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
# rspress-plugin-file-tree
|
|
2
|
-
|
|
3
|
-
## 0.1.3-beta.0
|
|
4
|
-
|
|
5
|
-
### Patch Changes
|
|
6
|
-
|
|
7
|
-
- Updated dependencies [2891f27]
|
|
8
|
-
- rspress-plugin-devkit@0.1.3-beta.0
|
|
9
|
-
|
|
10
|
-
## 0.1.2
|
|
11
|
-
|
|
12
|
-
### Patch Changes
|
|
13
|
-
|
|
14
|
-
- Updated dependencies [932dbb7]
|
|
15
|
-
- rspress-plugin-devkit@0.1.2
|
|
16
|
-
|
|
17
|
-
## 0.1.2-beta.0
|
|
18
|
-
|
|
19
|
-
### Patch Changes
|
|
20
|
-
|
|
21
|
-
- Updated dependencies [932dbb7]
|
|
22
|
-
- rspress-plugin-devkit@0.1.2-beta.0
|
|
23
|
-
|
|
24
|
-
## 0.1.1
|
|
25
|
-
|
|
26
|
-
### Patch Changes
|
|
27
|
-
|
|
28
|
-
- 546dcf0: release beta
|
|
29
|
-
- 5e28903: fixup publish config
|
|
30
|
-
- Updated dependencies [546dcf0]
|
|
31
|
-
- Updated dependencies [5e28903]
|
|
32
|
-
- rspress-plugin-devkit@0.1.1
|
|
33
|
-
|
|
34
|
-
## 0.1.1-beta.0
|
|
35
|
-
|
|
36
|
-
### Patch Changes
|
|
37
|
-
|
|
38
|
-
- 546dcf0: release beta
|
|
39
|
-
- Updated dependencies [546dcf0]
|
|
40
|
-
- rspress-plugin-devkit@0.1.1-beta.0
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
export type ShapeType = {
|
|
3
|
-
width: number;
|
|
4
|
-
height: number;
|
|
5
|
-
};
|
|
6
|
-
export declare const getRealShape: (el: HTMLElement | null) => ShapeType;
|
|
7
|
-
export type ShapeResult = [ShapeType, () => void];
|
|
8
|
-
export type ExpandProps = {
|
|
9
|
-
isExpanded?: boolean;
|
|
10
|
-
delay?: number;
|
|
11
|
-
parentExpanded?: boolean[];
|
|
12
|
-
};
|
|
13
|
-
declare const Expand: React.FC<React.PropsWithChildren<ExpandProps>>;
|
|
14
|
-
export default Expand;
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useRef, useState } from 'react';
|
|
3
|
-
import clsx from 'clsx';
|
|
4
|
-
export const getRealShape = (el) => {
|
|
5
|
-
const defaultShape = { width: 0, height: 0 };
|
|
6
|
-
if (!el || typeof window === 'undefined')
|
|
7
|
-
return defaultShape;
|
|
8
|
-
const rect = el.getBoundingClientRect();
|
|
9
|
-
const { width, height } = window.getComputedStyle(el);
|
|
10
|
-
const getCSSStyleVal = (str, parentNum) => {
|
|
11
|
-
if (!str)
|
|
12
|
-
return 0;
|
|
13
|
-
const strVal = str.includes('px')
|
|
14
|
-
? +str.split('px')[0]
|
|
15
|
-
: str.includes('%')
|
|
16
|
-
? +str.split('%')[0] * parentNum * 0.01
|
|
17
|
-
: str;
|
|
18
|
-
return Number.isNaN(+strVal) ? 0 : +strVal;
|
|
19
|
-
};
|
|
20
|
-
return {
|
|
21
|
-
width: getCSSStyleVal(`${width}`, rect.width),
|
|
22
|
-
height: getCSSStyleVal(`${height}`, rect.height),
|
|
23
|
-
};
|
|
24
|
-
};
|
|
25
|
-
const useRealShape = (ref) => {
|
|
26
|
-
const [state, setState] = useState({
|
|
27
|
-
width: 0,
|
|
28
|
-
height: 0,
|
|
29
|
-
});
|
|
30
|
-
const update = () => {
|
|
31
|
-
const { width, height } = getRealShape(ref.current);
|
|
32
|
-
setState({ width, height });
|
|
33
|
-
};
|
|
34
|
-
useEffect(() => update(), [ref.current]);
|
|
35
|
-
return [state, update];
|
|
36
|
-
};
|
|
37
|
-
const defaultProps = {
|
|
38
|
-
isExpanded: false,
|
|
39
|
-
delay: 200,
|
|
40
|
-
};
|
|
41
|
-
const Expand = ({ isExpanded, delay, parentExpanded = [], children, }) => {
|
|
42
|
-
const [height, setHeight] = useState(isExpanded ? 'auto' : '0');
|
|
43
|
-
const [selfExpanded, setSelfExpanded] = useState(isExpanded);
|
|
44
|
-
const [visible, setVisible] = useState(isExpanded);
|
|
45
|
-
const contentRef = useRef(null);
|
|
46
|
-
const entryTimer = useRef();
|
|
47
|
-
const leaveTimer = useRef();
|
|
48
|
-
const resetTimer = useRef();
|
|
49
|
-
const [state, updateShape] = useRealShape(contentRef);
|
|
50
|
-
const [parentClosed, setParentClosed] = useState(false);
|
|
51
|
-
useEffect(() => setHeight(`${state.height}px`), [state.height]);
|
|
52
|
-
useEffect(() => {
|
|
53
|
-
// show element or reset height.
|
|
54
|
-
// force an update once manually, even if the element does not change.
|
|
55
|
-
// (the height of the element might be "auto")
|
|
56
|
-
if (isExpanded) {
|
|
57
|
-
setVisible(isExpanded);
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
updateShape();
|
|
61
|
-
setHeight(`${state.height}px`);
|
|
62
|
-
}
|
|
63
|
-
// show expand animation
|
|
64
|
-
entryTimer.current = window.setTimeout(() => {
|
|
65
|
-
setSelfExpanded(isExpanded);
|
|
66
|
-
clearTimeout(entryTimer.current);
|
|
67
|
-
}, 30);
|
|
68
|
-
// Reset height after animation
|
|
69
|
-
if (isExpanded) {
|
|
70
|
-
resetTimer.current = window.setTimeout(() => {
|
|
71
|
-
setHeight('auto');
|
|
72
|
-
clearTimeout(resetTimer.current);
|
|
73
|
-
}, delay);
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
leaveTimer.current = window.setTimeout(() => {
|
|
77
|
-
setVisible(isExpanded);
|
|
78
|
-
clearTimeout(leaveTimer.current);
|
|
79
|
-
}, delay / 2);
|
|
80
|
-
}
|
|
81
|
-
return () => {
|
|
82
|
-
clearTimeout(entryTimer.current);
|
|
83
|
-
clearTimeout(leaveTimer.current);
|
|
84
|
-
clearTimeout(resetTimer.current);
|
|
85
|
-
};
|
|
86
|
-
}, [isExpanded]);
|
|
87
|
-
useEffect(() => {
|
|
88
|
-
const parentClosed = parentExpanded.some((i) => i === false);
|
|
89
|
-
setParentClosed(parentClosed);
|
|
90
|
-
}, [parentExpanded]);
|
|
91
|
-
return (_jsx("div", { className: clsx('rspress-file-tree-expand-container', {
|
|
92
|
-
'rspress-file-tree-expand-container-expanded': isExpanded,
|
|
93
|
-
}), style: {
|
|
94
|
-
height: 0,
|
|
95
|
-
visibility: visible && !parentClosed ? 'visible' : 'hidden',
|
|
96
|
-
transition: `height ${delay}ms ease`,
|
|
97
|
-
...(selfExpanded
|
|
98
|
-
? { height, visibility: parentClosed ? 'hidden' : 'visible' }
|
|
99
|
-
: {}),
|
|
100
|
-
}, children: _jsx("div", { ref: contentRef, className: clsx('rspress-file-tree-expand-content'), children: children }) }));
|
|
101
|
-
};
|
|
102
|
-
Expand.defaultProps = defaultProps;
|
|
103
|
-
Expand.displayName = 'GeistExpand';
|
|
104
|
-
export default Expand;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { buildClassName } from '../presets';
|
|
3
|
-
const defaultProps = {
|
|
4
|
-
width: 22,
|
|
5
|
-
height: 22,
|
|
6
|
-
};
|
|
7
|
-
const TreeFileIcon = ({ color, width, height, }) => {
|
|
8
|
-
return (_jsxs("svg", { className: buildClassName('file-icon'), viewBox: "0 0 24 24", width: width, height: height, stroke: "currentColor", strokeWidth: "1", strokeLinecap: "round", strokeLinejoin: "round", fill: "none", shapeRendering: "geometricPrecision", children: [_jsx("path", { d: "M13 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V9z" }), _jsx("path", { d: "M13 2v7h7" })] }));
|
|
9
|
-
};
|
|
10
|
-
TreeFileIcon.defaultProps = defaultProps;
|
|
11
|
-
TreeFileIcon.displayName = 'GeistTreeFileIcon';
|
|
12
|
-
export default TreeFileIcon;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import Tree from './Tree';
|
|
2
|
-
import TreeFile from './TreeFile';
|
|
3
|
-
import TreeFolder from './TreeFolder';
|
|
4
|
-
import './index.less';
|
|
5
|
-
export type TreeComponentType = typeof Tree & {
|
|
6
|
-
File: typeof TreeFile;
|
|
7
|
-
Folder: typeof TreeFolder;
|
|
8
|
-
};
|
|
9
|
-
export type { TreeProps, TreeFile } from './Tree';
|
|
10
|
-
declare const _default: TreeComponentType;
|
|
11
|
-
export default _default;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
export declare const tuple: <T extends string[]>(...args: T) => T;
|
|
3
|
-
declare const FileTreeValueType: ["directory", "file"];
|
|
4
|
-
export type TreeFile = {
|
|
5
|
-
type: (typeof FileTreeValueType)[number];
|
|
6
|
-
name: string;
|
|
7
|
-
extra?: string;
|
|
8
|
-
files?: Array<TreeFile>;
|
|
9
|
-
};
|
|
10
|
-
interface Props {
|
|
11
|
-
tree?: TreeFile[];
|
|
12
|
-
initialExpand?: boolean;
|
|
13
|
-
onClick?: (path: string) => void;
|
|
14
|
-
className?: string;
|
|
15
|
-
initialExpandDepth?: number;
|
|
16
|
-
}
|
|
17
|
-
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>;
|
|
18
|
-
export type TreeProps = Props & NativeAttrs;
|
|
19
|
-
declare const Tree: React.FC<React.PropsWithChildren<TreeProps>>;
|
|
20
|
-
export default Tree;
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useMemo } from 'react';
|
|
3
|
-
import TreeFile from './TreeFile';
|
|
4
|
-
import TreeFolder from './TreeFolder';
|
|
5
|
-
import { TreeContext } from './TreeContext';
|
|
6
|
-
import { sortChildren } from '../helpers';
|
|
7
|
-
import { buildClassName } from '../presets';
|
|
8
|
-
import { useDark } from 'rspress/runtime';
|
|
9
|
-
export const tuple = (...args) => args;
|
|
10
|
-
const FileTreeValueType = tuple('directory', 'file');
|
|
11
|
-
const directoryType = FileTreeValueType[0];
|
|
12
|
-
const defaultProps = {
|
|
13
|
-
initialExpand: false,
|
|
14
|
-
className: '',
|
|
15
|
-
};
|
|
16
|
-
const makeChildren = (value = []) => {
|
|
17
|
-
if (!value || !value.length)
|
|
18
|
-
return null;
|
|
19
|
-
return value
|
|
20
|
-
.sort((a, b) => {
|
|
21
|
-
if (a.type !== b.type)
|
|
22
|
-
return a.type !== directoryType ? 1 : -1;
|
|
23
|
-
return `${a.name}`.charCodeAt(0) - `${b.name}`.charCodeAt(0);
|
|
24
|
-
})
|
|
25
|
-
.map((item, index) => {
|
|
26
|
-
if (item.type === directoryType)
|
|
27
|
-
return (_jsx(TreeFolder, { name: item.name, extra: item.extra, parentExpanded: [], children: makeChildren(item.files) }, `folder-${item.name}-${index}`));
|
|
28
|
-
return (_jsx(TreeFile, { name: item.name, extra: item.extra, parentExpanded: [] }, `file-${item.name}-${index}`));
|
|
29
|
-
});
|
|
30
|
-
};
|
|
31
|
-
const Tree = ({ children, onClick, initialExpand, initialExpandDepth, tree, className, ...props }) => {
|
|
32
|
-
if (!tree)
|
|
33
|
-
return null;
|
|
34
|
-
const isImperative = Boolean(tree.length > 0);
|
|
35
|
-
const onFileClick = (path) => {
|
|
36
|
-
onClick && onClick(path);
|
|
37
|
-
};
|
|
38
|
-
const initialValue = useMemo(() => ({
|
|
39
|
-
onFileClick,
|
|
40
|
-
initialExpand,
|
|
41
|
-
initialExpandDepth,
|
|
42
|
-
isImperative,
|
|
43
|
-
}), [initialExpand]);
|
|
44
|
-
const customChildren = isImperative
|
|
45
|
-
? makeChildren(tree)
|
|
46
|
-
: sortChildren(children, TreeFolder);
|
|
47
|
-
const dark = useDark();
|
|
48
|
-
return (_jsx(TreeContext.Provider, { value: initialValue, children: _jsx("div", { "data-dark": String(dark), className: buildClassName(), ...props, children: customChildren }) }));
|
|
49
|
-
};
|
|
50
|
-
Tree.defaultProps = defaultProps;
|
|
51
|
-
Tree.displayName = 'GeistTree';
|
|
52
|
-
export default Tree;
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
export interface TreeConfig {
|
|
3
|
-
onFileClick?: (path: string) => void;
|
|
4
|
-
initialExpand: boolean;
|
|
5
|
-
initialExpandDepth?: number;
|
|
6
|
-
isImperative: boolean;
|
|
7
|
-
}
|
|
8
|
-
export declare const TreeContext: React.Context<TreeConfig>;
|
|
9
|
-
export declare const useTreeContext: () => TreeConfig;
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
interface Props {
|
|
3
|
-
name: string;
|
|
4
|
-
extra?: string;
|
|
5
|
-
parentPath?: string;
|
|
6
|
-
level?: number;
|
|
7
|
-
className?: string;
|
|
8
|
-
}
|
|
9
|
-
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>;
|
|
10
|
-
export type TreeFileProps = Props & NativeAttrs & {
|
|
11
|
-
parentExpanded: boolean[];
|
|
12
|
-
};
|
|
13
|
-
declare const TreeFile: React.FC<React.PropsWithChildren<TreeFileProps>>;
|
|
14
|
-
export default TreeFile;
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useMemo } from 'react';
|
|
3
|
-
import TreeFileIcon from './FileIcon';
|
|
4
|
-
import { useTreeContext } from './TreeContext';
|
|
5
|
-
import TreeIndents from './TreeIndents';
|
|
6
|
-
import { makeChildPath, stopPropagation } from '../helpers';
|
|
7
|
-
import { buildClassName } from '../presets';
|
|
8
|
-
const defaultProps = {
|
|
9
|
-
level: 0,
|
|
10
|
-
className: '',
|
|
11
|
-
parentPath: '',
|
|
12
|
-
};
|
|
13
|
-
const TreeFile = ({ name, parentPath, level, extra, className, parentExpanded, ...props }) => {
|
|
14
|
-
const { onFileClick } = useTreeContext();
|
|
15
|
-
const currentPath = useMemo(() => makeChildPath(name, parentPath), []);
|
|
16
|
-
const clickHandler = (event) => {
|
|
17
|
-
stopPropagation(event);
|
|
18
|
-
onFileClick && onFileClick(currentPath);
|
|
19
|
-
};
|
|
20
|
-
return (_jsx("div", { className: buildClassName('file'), onClick: clickHandler, ...props, children: _jsxs("div", { className: buildClassName('file-names'), style: {
|
|
21
|
-
marginLeft: `calc(1.875rem * ${level})`,
|
|
22
|
-
}, children: [_jsx(TreeIndents, { count: level }), _jsx("span", { className: buildClassName('file-icon'), children: _jsx(TreeFileIcon, {}) }), _jsxs("span", { className: buildClassName('file-name'), children: [name, extra && (_jsx("span", { className: buildClassName('file-extra'), children: extra }))] })] }) }));
|
|
23
|
-
};
|
|
24
|
-
TreeFile.defaultProps = defaultProps;
|
|
25
|
-
TreeFile.displayName = 'GeistTreeFile';
|
|
26
|
-
export default TreeFile;
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
interface Props {
|
|
3
|
-
name: string;
|
|
4
|
-
extra?: string;
|
|
5
|
-
parentPath?: string;
|
|
6
|
-
level?: number;
|
|
7
|
-
className?: string;
|
|
8
|
-
}
|
|
9
|
-
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>;
|
|
10
|
-
export type TreeFolderProps = Props & NativeAttrs & {
|
|
11
|
-
parentExpanded: boolean[];
|
|
12
|
-
};
|
|
13
|
-
declare const TreeFolder: React.FC<React.PropsWithChildren<TreeFolderProps>>;
|
|
14
|
-
export default TreeFolder;
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useMemo, useState } from 'react';
|
|
3
|
-
import TreeFile from './TreeFile';
|
|
4
|
-
import Expand from './Expand';
|
|
5
|
-
import TreeIndents from './TreeIndents';
|
|
6
|
-
import { useTreeContext } from './TreeContext';
|
|
7
|
-
import TreeFolderIcon from './TreeFolderIcon';
|
|
8
|
-
import TreeStatusIcon from './TreeStatusIcon';
|
|
9
|
-
import { sortChildren, makeChildPath, stopPropagation, setChildrenProps, } from '../helpers';
|
|
10
|
-
import { buildClassName } from '../presets';
|
|
11
|
-
const defaultProps = {
|
|
12
|
-
level: 0,
|
|
13
|
-
className: '',
|
|
14
|
-
parentPath: '',
|
|
15
|
-
};
|
|
16
|
-
const TreeFolder = ({ name, children, parentPath, level: parentLevel, extra, className, parentExpanded = [], ...props }) => {
|
|
17
|
-
const { initialExpand, isImperative, initialExpandDepth = 0, } = useTreeContext();
|
|
18
|
-
const [expanded, setExpanded] = useState(() => {
|
|
19
|
-
return parentLevel + 1 <= initialExpandDepth;
|
|
20
|
-
});
|
|
21
|
-
const currentPath = useMemo(() => makeChildPath(name, parentPath), []);
|
|
22
|
-
const clickHandler = () => setExpanded(!expanded);
|
|
23
|
-
const nextChildren = setChildrenProps(children, {
|
|
24
|
-
parentPath: currentPath,
|
|
25
|
-
level: parentLevel + 1,
|
|
26
|
-
parentExpanded: [...parentExpanded, expanded],
|
|
27
|
-
}, [TreeFolder, TreeFile]);
|
|
28
|
-
const sortedChildren = isImperative
|
|
29
|
-
? nextChildren
|
|
30
|
-
: sortChildren(nextChildren, TreeFolder);
|
|
31
|
-
return (_jsxs("div", { className: buildClassName('folder'), onClick: clickHandler, ...props, children: [_jsxs("div", { className: buildClassName('folder-names'), style: {
|
|
32
|
-
marginLeft: `calc(1.875rem * ${parentLevel})`,
|
|
33
|
-
}, children: [_jsx(TreeIndents, { count: parentLevel }), _jsx("span", { className: buildClassName('folder-status'), children: _jsx(TreeStatusIcon, { active: expanded }) }), _jsx("span", { className: buildClassName('folder-icon'), children: _jsx(TreeFolderIcon, {}) }), _jsxs("span", { className: buildClassName('folder-name'), children: [name, extra && (_jsx("span", { className: buildClassName('folder-extra'), children: extra }))] })] }), _jsx(Expand, { isExpanded: expanded, parentExpanded: parentExpanded, children: _jsx("div", { className: buildClassName('folder-content'), onClick: stopPropagation, children: sortedChildren }) })] }));
|
|
34
|
-
};
|
|
35
|
-
TreeFolder.defaultProps = defaultProps;
|
|
36
|
-
TreeFolder.displayName = 'GeistTreeFolder';
|
|
37
|
-
export default TreeFolder;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { buildClassName } from '../presets';
|
|
3
|
-
const defaultProps = {
|
|
4
|
-
width: 22,
|
|
5
|
-
height: 22,
|
|
6
|
-
};
|
|
7
|
-
const TreeFolderIcon = ({ color, width, height, }) => {
|
|
8
|
-
return (_jsx("svg", { className: buildClassName('folder-icon'), viewBox: "0 0 24 24", width: width, height: height, stroke: "currentColor", strokeWidth: "1", strokeLinecap: "round", strokeLinejoin: "round", fill: "none", shapeRendering: "geometricPrecision", children: _jsx("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" }) }));
|
|
9
|
-
};
|
|
10
|
-
TreeFolderIcon.defaultProps = defaultProps;
|
|
11
|
-
TreeFolderIcon.displayName = 'GeistTreeFolderIcon';
|
|
12
|
-
export default TreeFolderIcon;
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { buildClassName } from '../presets';
|
|
3
|
-
const TreeIndents = ({ count }) => {
|
|
4
|
-
if (count === 0)
|
|
5
|
-
return null;
|
|
6
|
-
return (_jsx(_Fragment, { children: [...new Array(count)].map((_, index) => (_jsx("span", { className: buildClassName('indent'), style: {
|
|
7
|
-
left: `calc(-1.875rem * ${index + 1} + 0.75rem)`,
|
|
8
|
-
} }, `indent-${index}`))) }));
|
|
9
|
-
};
|
|
10
|
-
export default TreeIndents;
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { buildClassName } from '../presets';
|
|
3
|
-
const defaultProps = {
|
|
4
|
-
width: 12,
|
|
5
|
-
height: 12,
|
|
6
|
-
active: false,
|
|
7
|
-
};
|
|
8
|
-
const TreeStatusIcon = ({ color, width, height, active, }) => {
|
|
9
|
-
return (_jsxs("svg", { className: buildClassName('folder-status-icon'), viewBox: "0 0 24 24", width: width, height: height, stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", fill: "none", shapeRendering: "geometricPrecision", children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }), !active && _jsx("path", { d: "M12 8v8" }), _jsx("path", { d: "M8 12h8" })] }));
|
|
10
|
-
};
|
|
11
|
-
TreeStatusIcon.defaultProps = defaultProps;
|
|
12
|
-
TreeStatusIcon.displayName = 'GeistTreeStatusIcon';
|
|
13
|
-
export default TreeStatusIcon;
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import React, { type ReactNode } from 'react';
|
|
2
|
-
export declare const sortChildren: (children: ReactNode | undefined, folderComponentType: React.ElementType) => (string | number | React.ReactElement<any, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | React.ReactPortal)[];
|
|
3
|
-
export declare const makeChildPath: (name: string, parentPath?: string) => string;
|
|
4
|
-
export declare const stopPropagation: (event: React.MouseEvent) => void;
|
|
5
|
-
export declare const setChildrenProps: (children: ReactNode | undefined, props: Record<string, unknown>, targetComponents?: Array<React.ElementType>) => ReactNode | undefined;
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
export const sortChildren = (children, folderComponentType) => {
|
|
3
|
-
return React.Children.toArray(children).sort((a, b) => {
|
|
4
|
-
if (!React.isValidElement(a) || !React.isValidElement(b))
|
|
5
|
-
return 0;
|
|
6
|
-
if (a.type !== b.type)
|
|
7
|
-
return a.type !== folderComponentType ? 1 : -1;
|
|
8
|
-
return `${a.props.name}`.charCodeAt(0) - `${b.props.name}`.charCodeAt(0);
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
export const makeChildPath = (name, parentPath) => {
|
|
12
|
-
if (!parentPath)
|
|
13
|
-
return name;
|
|
14
|
-
return `${parentPath}/${name}`;
|
|
15
|
-
};
|
|
16
|
-
export const stopPropagation = (event) => {
|
|
17
|
-
event.stopPropagation();
|
|
18
|
-
event.nativeEvent.stopImmediatePropagation();
|
|
19
|
-
};
|
|
20
|
-
export const setChildrenProps = (children, props, targetComponents = []) => {
|
|
21
|
-
if (React.Children.count(children) === 0)
|
|
22
|
-
return [];
|
|
23
|
-
const allowAll = targetComponents.length === 0;
|
|
24
|
-
const clone = (child, props = {}) => React.cloneElement(child, props);
|
|
25
|
-
return React.Children.map(children, (item) => {
|
|
26
|
-
if (!React.isValidElement(item))
|
|
27
|
-
return item;
|
|
28
|
-
if (allowAll)
|
|
29
|
-
return clone(item, props);
|
|
30
|
-
const isAllowed = targetComponents.find((child) => child === item.type);
|
|
31
|
-
if (isAllowed)
|
|
32
|
-
return clone(item, props);
|
|
33
|
-
return item;
|
|
34
|
-
});
|
|
35
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
[{"id":0,"title":"Remark x File Tree","content":"#\n\n","routePath":"/","lang":"","toc":[],"domain":"","frontmatter":{},"version":""}]
|
package/docs/index.md
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# Remark x File Tree
|
|
2
|
-
|
|
3
|
-
```tree
|
|
4
|
-
.
|
|
5
|
-
├── rspress.config.ts
|
|
6
|
-
├── src
|
|
7
|
-
│ ├── components
|
|
8
|
-
│ │ ├── FileTreeRender.tsx
|
|
9
|
-
│ │ ├── Tree
|
|
10
|
-
│ │ │ ├── Expand.tsx
|
|
11
|
-
│ │ │ ├── FileIcon.tsx
|
|
12
|
-
│ │ │ ├── Tree.tsx
|
|
13
|
-
│ │ │ ├── TreeContext.tsx
|
|
14
|
-
│ │ │ ├── TreeFile.tsx
|
|
15
|
-
│ │ │ ├── TreeFolder.tsx
|
|
16
|
-
│ │ │ ├── TreeFolderIcon.tsx
|
|
17
|
-
│ │ │ ├── TreeIndents.tsx
|
|
18
|
-
│ │ │ ├── TreeStatusIcon.tsx
|
|
19
|
-
│ │ │ ├── index.less
|
|
20
|
-
│ │ │ └── index.tsx
|
|
21
|
-
│ │ ├── helpers.ts
|
|
22
|
-
│ │ └── presets.ts
|
|
23
|
-
│ ├── index.ts
|
|
24
|
-
│ └── parser.ts
|
|
25
|
-
└── tsconfig.json
|
|
26
|
-
```
|
package/image.png
DELETED
|
Binary file
|
package/rspress.config.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import * as path from 'path';
|
|
2
|
-
import { defineConfig } from 'rspress/config';
|
|
3
|
-
import fileTree from './src';
|
|
4
|
-
|
|
5
|
-
export default defineConfig({
|
|
6
|
-
root: path.join(__dirname, 'docs'),
|
|
7
|
-
title: 'Rspress x FileTree Example',
|
|
8
|
-
plugins: [
|
|
9
|
-
fileTree({
|
|
10
|
-
initialExpandDepth: 1,
|
|
11
|
-
}),
|
|
12
|
-
],
|
|
13
|
-
});
|
package/src/index.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
PresetConfigMutator,
|
|
5
|
-
RemarkCodeBlockToGlobalComponentPluginFactory,
|
|
6
|
-
} from 'rspress-plugin-devkit';
|
|
7
|
-
|
|
8
|
-
import { parseInput } from './parser';
|
|
9
|
-
|
|
10
|
-
import type { RspressPlugin } from '@rspress/shared';
|
|
11
|
-
|
|
12
|
-
interface RspressPluginFileTreeOptions {
|
|
13
|
-
initialExpandDepth?: number;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export default function rspressPluginFileTree(
|
|
17
|
-
options: RspressPluginFileTreeOptions = {},
|
|
18
|
-
): RspressPlugin {
|
|
19
|
-
const { initialExpandDepth = 0 } = options;
|
|
20
|
-
|
|
21
|
-
const remarkFileTree = new RemarkCodeBlockToGlobalComponentPluginFactory({
|
|
22
|
-
components: [
|
|
23
|
-
{
|
|
24
|
-
lang: 'tree',
|
|
25
|
-
componentPath: path.join(
|
|
26
|
-
__dirname,
|
|
27
|
-
'./components/Tree/FileTreeRender.tsx',
|
|
28
|
-
),
|
|
29
|
-
propsProvider(code) {
|
|
30
|
-
return {
|
|
31
|
-
tree: parseInput(code),
|
|
32
|
-
initialExpandDepth,
|
|
33
|
-
};
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
],
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
return {
|
|
40
|
-
name: 'rspress-plugin-file-tree',
|
|
41
|
-
config(config) {
|
|
42
|
-
return new PresetConfigMutator(config).disableMdxRs().toConfig();
|
|
43
|
-
},
|
|
44
|
-
markdown: {
|
|
45
|
-
remarkPlugins: [remarkFileTree.remarkPlugin],
|
|
46
|
-
globalComponents: remarkFileTree.mdxComponents,
|
|
47
|
-
},
|
|
48
|
-
builderConfig: remarkFileTree.builderConfig,
|
|
49
|
-
};
|
|
50
|
-
}
|
package/src/parser.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
type TreeItem = {
|
|
2
|
-
type: 'file' | 'directory';
|
|
3
|
-
name: string;
|
|
4
|
-
files?: TreeItem[];
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
function countLeadingSpaces(line: string): number {
|
|
8
|
-
const matches = line.match(/^(\s*\│\s*)*/);
|
|
9
|
-
if (!matches) return 0;
|
|
10
|
-
return matches[0].length;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function parseInput(input: string): TreeItem[] {
|
|
14
|
-
const lines = input.split('\n').filter((line) => line.trim());
|
|
15
|
-
const tree: TreeItem[] = [];
|
|
16
|
-
const stack: { level: number; item: TreeItem }[] = [];
|
|
17
|
-
|
|
18
|
-
for (let i = 0; i < lines.length; i++) {
|
|
19
|
-
const line = lines[i];
|
|
20
|
-
|
|
21
|
-
if (line === '.') continue;
|
|
22
|
-
|
|
23
|
-
const level = countLeadingSpaces(line);
|
|
24
|
-
|
|
25
|
-
const name = line.trim().split(' ').slice(-1)[0];
|
|
26
|
-
|
|
27
|
-
const nextLine = lines[i + 1] || '';
|
|
28
|
-
const nextLineLevel = countLeadingSpaces(nextLine);
|
|
29
|
-
const type = nextLineLevel > level ? 'directory' : 'file';
|
|
30
|
-
const item: TreeItem =
|
|
31
|
-
type === 'directory' ? { type, name, files: [] } : { type, name };
|
|
32
|
-
|
|
33
|
-
while (stack.length > 0 && stack[stack.length - 1].level >= level) {
|
|
34
|
-
stack.pop();
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (stack.length === 0) {
|
|
38
|
-
tree.push(item);
|
|
39
|
-
} else {
|
|
40
|
-
const parentItem = stack[stack.length - 1].item;
|
|
41
|
-
parentItem.files?.push(item);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (item.type === 'directory') {
|
|
45
|
-
stack.push({ level, item });
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return tree;
|
|
50
|
-
}
|
package/tsconfig.json
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|