rspress-plugin-file-tree 0.1.3 → 0.1.4-beta.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/dist/index.js +1 -1
- package/package.json +5 -1
- package/CHANGELOG.md +0 -47
- package/dist/components/Tree/Expand.d.ts +0 -14
- package/dist/components/Tree/Expand.jsx +0 -107
- package/dist/components/Tree/FileIcon.d.ts +0 -8
- package/dist/components/Tree/FileIcon.jsx +0 -15
- package/dist/components/Tree/FileTreeRender.d.ts +0 -11
- package/dist/components/Tree/FileTreeRender.jsx +0 -7
- package/dist/components/Tree/Tree.d.ts +0 -20
- package/dist/components/Tree/Tree.jsx +0 -57
- package/dist/components/Tree/TreeContext.d.ts +0 -9
- package/dist/components/Tree/TreeContext.jsx +0 -7
- package/dist/components/Tree/TreeFile.d.ts +0 -14
- package/dist/components/Tree/TreeFile.jsx +0 -36
- package/dist/components/Tree/TreeFolder.d.ts +0 -14
- package/dist/components/Tree/TreeFolder.jsx +0 -55
- package/dist/components/Tree/TreeFolderIcon.d.ts +0 -8
- package/dist/components/Tree/TreeFolderIcon.jsx +0 -14
- package/dist/components/Tree/TreeIndents.d.ts +0 -6
- package/dist/components/Tree/TreeIndents.jsx +0 -12
- package/dist/components/Tree/TreeStatusIcon.d.ts +0 -9
- package/dist/components/Tree/TreeStatusIcon.jsx +0 -17
- 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
package/CHANGELOG.md
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# rspress-plugin-file-tree
|
|
2
|
-
|
|
3
|
-
## 0.1.3
|
|
4
|
-
|
|
5
|
-
### Patch Changes
|
|
6
|
-
|
|
7
|
-
- Updated dependencies [2891f27]
|
|
8
|
-
- rspress-plugin-devkit@0.1.3
|
|
9
|
-
|
|
10
|
-
## 0.1.3-beta.0
|
|
11
|
-
|
|
12
|
-
### Patch Changes
|
|
13
|
-
|
|
14
|
-
- Updated dependencies [2891f27]
|
|
15
|
-
- rspress-plugin-devkit@0.1.3-beta.0
|
|
16
|
-
|
|
17
|
-
## 0.1.2
|
|
18
|
-
|
|
19
|
-
### Patch Changes
|
|
20
|
-
|
|
21
|
-
- Updated dependencies [932dbb7]
|
|
22
|
-
- rspress-plugin-devkit@0.1.2
|
|
23
|
-
|
|
24
|
-
## 0.1.2-beta.0
|
|
25
|
-
|
|
26
|
-
### Patch Changes
|
|
27
|
-
|
|
28
|
-
- Updated dependencies [932dbb7]
|
|
29
|
-
- rspress-plugin-devkit@0.1.2-beta.0
|
|
30
|
-
|
|
31
|
-
## 0.1.1
|
|
32
|
-
|
|
33
|
-
### Patch Changes
|
|
34
|
-
|
|
35
|
-
- 546dcf0: release beta
|
|
36
|
-
- 5e28903: fixup publish config
|
|
37
|
-
- Updated dependencies [546dcf0]
|
|
38
|
-
- Updated dependencies [5e28903]
|
|
39
|
-
- rspress-plugin-devkit@0.1.1
|
|
40
|
-
|
|
41
|
-
## 0.1.1-beta.0
|
|
42
|
-
|
|
43
|
-
### Patch Changes
|
|
44
|
-
|
|
45
|
-
- 546dcf0: release beta
|
|
46
|
-
- Updated dependencies [546dcf0]
|
|
47
|
-
- 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,107 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
-
import clsx from 'clsx';
|
|
3
|
-
export const getRealShape = (el) => {
|
|
4
|
-
const defaultShape = { width: 0, height: 0 };
|
|
5
|
-
if (!el || typeof window === 'undefined')
|
|
6
|
-
return defaultShape;
|
|
7
|
-
const rect = el.getBoundingClientRect();
|
|
8
|
-
const { width, height } = window.getComputedStyle(el);
|
|
9
|
-
const getCSSStyleVal = (str, parentNum) => {
|
|
10
|
-
if (!str)
|
|
11
|
-
return 0;
|
|
12
|
-
const strVal = str.includes('px')
|
|
13
|
-
? +str.split('px')[0]
|
|
14
|
-
: str.includes('%')
|
|
15
|
-
? +str.split('%')[0] * parentNum * 0.01
|
|
16
|
-
: str;
|
|
17
|
-
return Number.isNaN(+strVal) ? 0 : +strVal;
|
|
18
|
-
};
|
|
19
|
-
return {
|
|
20
|
-
width: getCSSStyleVal(`${width}`, rect.width),
|
|
21
|
-
height: getCSSStyleVal(`${height}`, rect.height),
|
|
22
|
-
};
|
|
23
|
-
};
|
|
24
|
-
const useRealShape = (ref) => {
|
|
25
|
-
const [state, setState] = useState({
|
|
26
|
-
width: 0,
|
|
27
|
-
height: 0,
|
|
28
|
-
});
|
|
29
|
-
const update = () => {
|
|
30
|
-
const { width, height } = getRealShape(ref.current);
|
|
31
|
-
setState({ width, height });
|
|
32
|
-
};
|
|
33
|
-
useEffect(() => update(), [ref.current]);
|
|
34
|
-
return [state, update];
|
|
35
|
-
};
|
|
36
|
-
const defaultProps = {
|
|
37
|
-
isExpanded: false,
|
|
38
|
-
delay: 200,
|
|
39
|
-
};
|
|
40
|
-
const Expand = ({ isExpanded, delay, parentExpanded = [], children, }) => {
|
|
41
|
-
const [height, setHeight] = useState(isExpanded ? 'auto' : '0');
|
|
42
|
-
const [selfExpanded, setSelfExpanded] = useState(isExpanded);
|
|
43
|
-
const [visible, setVisible] = useState(isExpanded);
|
|
44
|
-
const contentRef = useRef(null);
|
|
45
|
-
const entryTimer = useRef();
|
|
46
|
-
const leaveTimer = useRef();
|
|
47
|
-
const resetTimer = useRef();
|
|
48
|
-
const [state, updateShape] = useRealShape(contentRef);
|
|
49
|
-
const [parentClosed, setParentClosed] = useState(false);
|
|
50
|
-
useEffect(() => setHeight(`${state.height}px`), [state.height]);
|
|
51
|
-
useEffect(() => {
|
|
52
|
-
// show element or reset height.
|
|
53
|
-
// force an update once manually, even if the element does not change.
|
|
54
|
-
// (the height of the element might be "auto")
|
|
55
|
-
if (isExpanded) {
|
|
56
|
-
setVisible(isExpanded);
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
updateShape();
|
|
60
|
-
setHeight(`${state.height}px`);
|
|
61
|
-
}
|
|
62
|
-
// show expand animation
|
|
63
|
-
entryTimer.current = window.setTimeout(() => {
|
|
64
|
-
setSelfExpanded(isExpanded);
|
|
65
|
-
clearTimeout(entryTimer.current);
|
|
66
|
-
}, 30);
|
|
67
|
-
// Reset height after animation
|
|
68
|
-
if (isExpanded) {
|
|
69
|
-
resetTimer.current = window.setTimeout(() => {
|
|
70
|
-
setHeight('auto');
|
|
71
|
-
clearTimeout(resetTimer.current);
|
|
72
|
-
}, delay);
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
leaveTimer.current = window.setTimeout(() => {
|
|
76
|
-
setVisible(isExpanded);
|
|
77
|
-
clearTimeout(leaveTimer.current);
|
|
78
|
-
}, delay / 2);
|
|
79
|
-
}
|
|
80
|
-
return () => {
|
|
81
|
-
clearTimeout(entryTimer.current);
|
|
82
|
-
clearTimeout(leaveTimer.current);
|
|
83
|
-
clearTimeout(resetTimer.current);
|
|
84
|
-
};
|
|
85
|
-
}, [isExpanded]);
|
|
86
|
-
useEffect(() => {
|
|
87
|
-
const parentClosed = parentExpanded.some((i) => i === false);
|
|
88
|
-
setParentClosed(parentClosed);
|
|
89
|
-
}, [parentExpanded]);
|
|
90
|
-
return (<div className={clsx('rspress-file-tree-expand-container', {
|
|
91
|
-
'rspress-file-tree-expand-container-expanded': isExpanded,
|
|
92
|
-
})} style={{
|
|
93
|
-
height: 0,
|
|
94
|
-
visibility: visible && !parentClosed ? 'visible' : 'hidden',
|
|
95
|
-
transition: `height ${delay}ms ease`,
|
|
96
|
-
...(selfExpanded
|
|
97
|
-
? { height, visibility: parentClosed ? 'hidden' : 'visible' }
|
|
98
|
-
: {}),
|
|
99
|
-
}}>
|
|
100
|
-
<div ref={contentRef} className={clsx('rspress-file-tree-expand-content')}>
|
|
101
|
-
{children}
|
|
102
|
-
</div>
|
|
103
|
-
</div>);
|
|
104
|
-
};
|
|
105
|
-
Expand.defaultProps = defaultProps;
|
|
106
|
-
Expand.displayName = 'GeistExpand';
|
|
107
|
-
export default Expand;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { buildClassName } from '../presets';
|
|
3
|
-
const defaultProps = {
|
|
4
|
-
width: 22,
|
|
5
|
-
height: 22,
|
|
6
|
-
};
|
|
7
|
-
const TreeFileIcon = ({ color, width, height, }) => {
|
|
8
|
-
return (<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">
|
|
9
|
-
<path d="M13 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V9z"/>
|
|
10
|
-
<path d="M13 2v7h7"/>
|
|
11
|
-
</svg>);
|
|
12
|
-
};
|
|
13
|
-
TreeFileIcon.defaultProps = defaultProps;
|
|
14
|
-
TreeFileIcon.displayName = 'GeistTreeFileIcon';
|
|
15
|
-
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,57 +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
|
-
export const tuple = (...args) => args;
|
|
9
|
-
const FileTreeValueType = tuple('directory', 'file');
|
|
10
|
-
const directoryType = FileTreeValueType[0];
|
|
11
|
-
const defaultProps = {
|
|
12
|
-
initialExpand: false,
|
|
13
|
-
className: '',
|
|
14
|
-
};
|
|
15
|
-
const makeChildren = (value = []) => {
|
|
16
|
-
if (!value || !value.length)
|
|
17
|
-
return null;
|
|
18
|
-
return value
|
|
19
|
-
.sort((a, b) => {
|
|
20
|
-
if (a.type !== b.type)
|
|
21
|
-
return a.type !== directoryType ? 1 : -1;
|
|
22
|
-
return `${a.name}`.charCodeAt(0) - `${b.name}`.charCodeAt(0);
|
|
23
|
-
})
|
|
24
|
-
.map((item, index) => {
|
|
25
|
-
if (item.type === directoryType)
|
|
26
|
-
return (<TreeFolder name={item.name} extra={item.extra} key={`folder-${item.name}-${index}`} parentExpanded={[]}>
|
|
27
|
-
{makeChildren(item.files)}
|
|
28
|
-
</TreeFolder>);
|
|
29
|
-
return (<TreeFile name={item.name} extra={item.extra} key={`file-${item.name}-${index}`} parentExpanded={[]}/>);
|
|
30
|
-
});
|
|
31
|
-
};
|
|
32
|
-
const Tree = ({ children, onClick, initialExpand, initialExpandDepth, tree, className, ...props }) => {
|
|
33
|
-
if (!tree)
|
|
34
|
-
return null;
|
|
35
|
-
const isImperative = Boolean(tree.length > 0);
|
|
36
|
-
const onFileClick = (path) => {
|
|
37
|
-
onClick && onClick(path);
|
|
38
|
-
};
|
|
39
|
-
const initialValue = useMemo(() => ({
|
|
40
|
-
onFileClick,
|
|
41
|
-
initialExpand,
|
|
42
|
-
initialExpandDepth,
|
|
43
|
-
isImperative,
|
|
44
|
-
}), [initialExpand]);
|
|
45
|
-
const customChildren = isImperative
|
|
46
|
-
? makeChildren(tree)
|
|
47
|
-
: sortChildren(children, TreeFolder);
|
|
48
|
-
const dark = useDark();
|
|
49
|
-
return (<TreeContext.Provider value={initialValue}>
|
|
50
|
-
<div data-dark={String(dark)} className={buildClassName()} {...props}>
|
|
51
|
-
{customChildren}
|
|
52
|
-
</div>
|
|
53
|
-
</TreeContext.Provider>);
|
|
54
|
-
};
|
|
55
|
-
Tree.defaultProps = defaultProps;
|
|
56
|
-
Tree.displayName = 'GeistTree';
|
|
57
|
-
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,36 +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
|
-
const defaultProps = {
|
|
8
|
-
level: 0,
|
|
9
|
-
className: '',
|
|
10
|
-
parentPath: '',
|
|
11
|
-
};
|
|
12
|
-
const TreeFile = ({ name, parentPath, level, extra, className, parentExpanded, ...props }) => {
|
|
13
|
-
const { onFileClick } = useTreeContext();
|
|
14
|
-
const currentPath = useMemo(() => makeChildPath(name, parentPath), []);
|
|
15
|
-
const clickHandler = (event) => {
|
|
16
|
-
stopPropagation(event);
|
|
17
|
-
onFileClick && onFileClick(currentPath);
|
|
18
|
-
};
|
|
19
|
-
return (<div className={buildClassName('file')} onClick={clickHandler} {...props}>
|
|
20
|
-
<div className={buildClassName('file-names')} style={{
|
|
21
|
-
marginLeft: `calc(1.875rem * ${level})`,
|
|
22
|
-
}}>
|
|
23
|
-
<TreeIndents count={level}/>
|
|
24
|
-
<span className={buildClassName('file-icon')}>
|
|
25
|
-
<TreeFileIcon />
|
|
26
|
-
</span>
|
|
27
|
-
<span className={buildClassName('file-name')}>
|
|
28
|
-
{name}
|
|
29
|
-
{extra && (<span className={buildClassName('file-extra')}>{extra}</span>)}
|
|
30
|
-
</span>
|
|
31
|
-
</div>
|
|
32
|
-
</div>);
|
|
33
|
-
};
|
|
34
|
-
TreeFile.defaultProps = defaultProps;
|
|
35
|
-
TreeFile.displayName = 'GeistTreeFile';
|
|
36
|
-
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,55 +0,0 @@
|
|
|
1
|
-
import React, { 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 { sortChildren, makeChildPath, stopPropagation, setChildrenProps, } from '../helpers';
|
|
9
|
-
import { buildClassName } from '../presets';
|
|
10
|
-
const defaultProps = {
|
|
11
|
-
level: 0,
|
|
12
|
-
className: '',
|
|
13
|
-
parentPath: '',
|
|
14
|
-
};
|
|
15
|
-
const TreeFolder = ({ name, children, parentPath, level: parentLevel, extra, className, parentExpanded = [], ...props }) => {
|
|
16
|
-
const { initialExpand, isImperative, initialExpandDepth = 0, } = useTreeContext();
|
|
17
|
-
const [expanded, setExpanded] = useState(() => {
|
|
18
|
-
return parentLevel + 1 <= initialExpandDepth;
|
|
19
|
-
});
|
|
20
|
-
const currentPath = useMemo(() => makeChildPath(name, parentPath), []);
|
|
21
|
-
const clickHandler = () => setExpanded(!expanded);
|
|
22
|
-
const nextChildren = setChildrenProps(children, {
|
|
23
|
-
parentPath: currentPath,
|
|
24
|
-
level: parentLevel + 1,
|
|
25
|
-
parentExpanded: [...parentExpanded, expanded],
|
|
26
|
-
}, [TreeFolder, TreeFile]);
|
|
27
|
-
const sortedChildren = isImperative
|
|
28
|
-
? nextChildren
|
|
29
|
-
: sortChildren(nextChildren, TreeFolder);
|
|
30
|
-
return (<div className={buildClassName('folder')} onClick={clickHandler} {...props}>
|
|
31
|
-
<div className={buildClassName('folder-names')} style={{
|
|
32
|
-
marginLeft: `calc(1.875rem * ${parentLevel})`,
|
|
33
|
-
}}>
|
|
34
|
-
<TreeIndents count={parentLevel}/>
|
|
35
|
-
<span className={buildClassName('folder-status')}>
|
|
36
|
-
<TreeStatusIcon active={expanded}/>
|
|
37
|
-
</span>
|
|
38
|
-
<span className={buildClassName('folder-icon')}>
|
|
39
|
-
<TreeFolderIcon />
|
|
40
|
-
</span>
|
|
41
|
-
<span className={buildClassName('folder-name')}>
|
|
42
|
-
{name}
|
|
43
|
-
{extra && (<span className={buildClassName('folder-extra')}>{extra}</span>)}
|
|
44
|
-
</span>
|
|
45
|
-
</div>
|
|
46
|
-
<Expand isExpanded={expanded} parentExpanded={parentExpanded}>
|
|
47
|
-
<div className={buildClassName('folder-content')} onClick={stopPropagation}>
|
|
48
|
-
{sortedChildren}
|
|
49
|
-
</div>
|
|
50
|
-
</Expand>
|
|
51
|
-
</div>);
|
|
52
|
-
};
|
|
53
|
-
TreeFolder.defaultProps = defaultProps;
|
|
54
|
-
TreeFolder.displayName = 'GeistTreeFolder';
|
|
55
|
-
export default TreeFolder;
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { buildClassName } from '../presets';
|
|
3
|
-
const defaultProps = {
|
|
4
|
-
width: 22,
|
|
5
|
-
height: 22,
|
|
6
|
-
};
|
|
7
|
-
const TreeFolderIcon = ({ color, width, height, }) => {
|
|
8
|
-
return (<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">
|
|
9
|
-
<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"/>
|
|
10
|
-
</svg>);
|
|
11
|
-
};
|
|
12
|
-
TreeFolderIcon.defaultProps = defaultProps;
|
|
13
|
-
TreeFolderIcon.displayName = 'GeistTreeFolderIcon';
|
|
14
|
-
export default TreeFolderIcon;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { buildClassName } from '../presets';
|
|
3
|
-
const TreeIndents = ({ count }) => {
|
|
4
|
-
if (count === 0)
|
|
5
|
-
return null;
|
|
6
|
-
return (<>
|
|
7
|
-
{[...new Array(count)].map((_, index) => (<span className={buildClassName('indent')} key={`indent-${index}`} style={{
|
|
8
|
-
left: `calc(-1.875rem * ${index + 1} + 0.75rem)`,
|
|
9
|
-
}}></span>))}
|
|
10
|
-
</>);
|
|
11
|
-
};
|
|
12
|
-
export default TreeIndents;
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
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 (<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">
|
|
10
|
-
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
|
|
11
|
-
{!active && <path d="M12 8v8"/>}
|
|
12
|
-
<path d="M8 12h8"/>
|
|
13
|
-
</svg>);
|
|
14
|
-
};
|
|
15
|
-
TreeStatusIcon.defaultProps = defaultProps;
|
|
16
|
-
TreeStatusIcon.displayName = 'GeistTreeStatusIcon';
|
|
17
|
-
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
|