rspress-plugin-file-tree 0.4.0 → 1.0.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.
Files changed (59) hide show
  1. package/dist/0~142.js +2 -0
  2. package/dist/0~164.js +2 -0
  3. package/dist/0~2.js +2 -0
  4. package/dist/0~225.js +2 -0
  5. package/dist/0~288.js +2 -0
  6. package/dist/0~299.js +2 -0
  7. package/dist/0~3.js +2 -0
  8. package/dist/0~311.js +2 -0
  9. package/dist/0~326.js +2 -0
  10. package/dist/0~40.js +2 -0
  11. package/dist/0~43.js +2 -0
  12. package/dist/0~433.js +2 -0
  13. package/dist/0~453.js +2 -0
  14. package/dist/0~457.js +2 -0
  15. package/dist/0~497.js +2 -0
  16. package/dist/0~511.js +2 -0
  17. package/dist/0~603.js +2 -0
  18. package/dist/0~616.js +2 -0
  19. package/dist/0~659.js +2 -0
  20. package/dist/0~670.js +2 -0
  21. package/dist/0~687.js +2 -0
  22. package/dist/0~706.js +2 -0
  23. package/dist/0~729.js +2 -0
  24. package/dist/0~768.js +2 -0
  25. package/dist/0~819.js +2 -0
  26. package/dist/0~830.js +2 -0
  27. package/dist/0~854.js +2 -0
  28. package/dist/0~863.js +2 -0
  29. package/dist/0~879.js +2 -0
  30. package/dist/0~96.js +2 -0
  31. package/dist/0~97.js +2 -0
  32. package/dist/components/FileTree/FileTree.css +83 -0
  33. package/dist/components/FileTree/FileTree.d.ts +7 -0
  34. package/dist/components/FileTree/FileTree.js +516 -0
  35. package/dist/components/FileTree/FileTreeItem.d.ts +8 -0
  36. package/dist/components/FileTree/RemoteSvgIcon.d.ts +11 -0
  37. package/dist/components/FileTree/index.d.ts +4 -0
  38. package/dist/components/folder-icons.d.ts +32 -0
  39. package/dist/components/languages.d.ts +7 -0
  40. package/dist/components/tree-parser/tree-parser.d.ts +12 -0
  41. package/dist/components/tree-parser/types.d.ts +10 -0
  42. package/dist/index.d.ts +1 -1
  43. package/dist/index.js +85 -17
  44. package/package.json +11 -8
  45. package/components/Tree/Expand.tsx +0 -149
  46. package/components/Tree/FileIcon.tsx +0 -41
  47. package/components/Tree/FileTreeRender.tsx +0 -16
  48. package/components/Tree/Tree.tsx +0 -112
  49. package/components/Tree/TreeContext.tsx +0 -18
  50. package/components/Tree/TreeFile.tsx +0 -69
  51. package/components/Tree/TreeFolder.tsx +0 -108
  52. package/components/Tree/TreeFolderIcon.tsx +0 -40
  53. package/components/Tree/TreeIndents.tsx +0 -26
  54. package/components/Tree/TreeStatusIcon.tsx +0 -45
  55. package/components/Tree/index.less +0 -178
  56. package/components/helpers.ts +0 -42
  57. package/components/presets.ts +0 -5
  58. package/dist/parser.d.ts +0 -8
  59. package/dist/parser.js +0 -40
package/dist/index.js CHANGED
@@ -1,31 +1,99 @@
1
- import path from 'node:path';
2
- import { PresetConfigMutator, RemarkCodeBlockToGlobalComponentPluginFactory, } from 'rspress-plugin-devkit';
3
- import { parseInput } from './parser';
4
- export default function rspressPluginFileTree(options = {}) {
1
+ import node_path from "node:path";
2
+ import { RemarkCodeBlockToGlobalComponentPluginFactory } from "rspress-plugin-devkit";
3
+ function parseTreeContent(content) {
4
+ const lines = content.split('\n').filter((line)=>line.trim());
5
+ const nodes = [];
6
+ const stack = [];
7
+ for (const line of lines){
8
+ const indent = calculateIndent(line);
9
+ const fullName = extractName(line);
10
+ const commentMatch = fullName.match(/^(.*?)(?:\s*\/\/\s*(.*))?$/);
11
+ const name = commentMatch ? commentMatch[1].trim() : fullName;
12
+ if (!name) continue;
13
+ const isDirectory = isDirectoryName(name);
14
+ const node = {
15
+ name: name.replace(/\/$/, ''),
16
+ type: isDirectory ? 'directory' : 'file',
17
+ children: [],
18
+ extension: isDirectory ? void 0 : getExtension(name)
19
+ };
20
+ while(stack.length > 0 && stack[stack.length - 1].indent >= indent)stack.pop();
21
+ if (0 === stack.length) nodes.push(node);
22
+ else stack[stack.length - 1].node.children.push(node);
23
+ if ('directory' === node.type) stack.push({
24
+ node,
25
+ indent
26
+ });
27
+ }
28
+ return {
29
+ nodes,
30
+ raw: content
31
+ };
32
+ }
33
+ function calculateIndent(line) {
34
+ let indent = 0;
35
+ let i = 0;
36
+ while(i < line.length){
37
+ const char = line[i];
38
+ if ('│' === char && '│ ' === line.substring(i, i + 4)) {
39
+ indent++;
40
+ i += 4;
41
+ continue;
42
+ }
43
+ if (' ' === line.substring(i, i + 4)) {
44
+ indent++;
45
+ i += 4;
46
+ continue;
47
+ }
48
+ if ('├' === char || '└' === char) {
49
+ if ('├──' === line.substring(i, i + 3) || '└──' === line.substring(i, i + 3)) indent++;
50
+ }
51
+ break;
52
+ }
53
+ return indent;
54
+ }
55
+ function extractName(line) {
56
+ return line.replace(/^[\s│]*/g, '').replace(/^[├└]──\s*/, '').trim();
57
+ }
58
+ function isDirectoryName(name) {
59
+ const cleanName = name.split(/\s+\/\//)[0].trim();
60
+ if (cleanName.endsWith('/')) return true;
61
+ const lastPart = cleanName.split('/').pop() || cleanName;
62
+ if (lastPart.startsWith('.')) return false;
63
+ if (/\.[a-zA-Z0-9]+$/.test(lastPart)) return false;
64
+ return true;
65
+ }
66
+ function getExtension(name) {
67
+ const match = name.match(/\.([^.]+)$/);
68
+ return match ? match[1] : '';
69
+ }
70
+ const PACKAGE_ROOT = node_path.resolve(__dirname, '../');
71
+ function rspressPluginFileTree(options = {}) {
5
72
  const { initialExpandDepth = 0 } = options;
6
73
  const remarkFileTree = new RemarkCodeBlockToGlobalComponentPluginFactory({
7
74
  components: [
8
75
  {
9
76
  lang: 'tree',
10
- componentPath: path.join(__dirname, '../components/Tree/FileTreeRender.tsx'),
11
- propsProvider(code) {
77
+ componentPath: node_path.join(PACKAGE_ROOT, 'dist/components/FileTree/FileTree'),
78
+ propsProvider (code) {
12
79
  return {
13
- tree: parseInput(code),
14
- initialExpandDepth,
80
+ nodes: parseTreeContent(code).nodes,
81
+ initialExpandDepth
15
82
  };
16
- },
17
- },
18
- ],
83
+ }
84
+ }
85
+ ]
19
86
  });
20
87
  return {
21
88
  name: 'rspress-plugin-file-tree',
22
- config(config) {
23
- return new PresetConfigMutator(config).disableMdxRs().toConfig();
24
- },
25
89
  markdown: {
26
- remarkPlugins: [remarkFileTree.remarkPlugin],
27
- globalComponents: remarkFileTree.mdxComponents,
90
+ remarkPlugins: [
91
+ remarkFileTree.remarkPlugin
92
+ ],
93
+ globalComponents: remarkFileTree.mdxComponents
28
94
  },
29
- builderConfig: remarkFileTree.builderConfig,
95
+ globalStyles: node_path.join(PACKAGE_ROOT, 'dist/components/FileTree/FileTree.css'),
96
+ builderConfig: remarkFileTree.builderConfig
30
97
  };
31
98
  }
99
+ export default rspressPluginFileTree;
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "rspress-plugin-file-tree",
3
- "version": "0.4.0",
3
+ "version": "1.0.1",
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
- "@rspress/shared": "^1.17.1",
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": "tsc --declarationMap false",
44
- "dev": "tsc -w",
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;
@@ -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;