@kne/file-system-view 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/README.md +290 -0
- package/dist/index.css +125 -0
- package/dist/index.css.map +1 -0
- package/dist/index.js +1907 -0
- package/dist/index.js.map +1 -0
- package/dist/index.modern.js +1909 -0
- package/dist/index.modern.js.map +1 -0
- package/dist/locale/en-US.js +6 -0
- package/dist/locale/en-US.js.map +1 -0
- package/dist/locale/en-US.modern.js +6 -0
- package/dist/locale/en-US.modern.js.map +1 -0
- package/dist/locale/zh-CN.js +6 -0
- package/dist/locale/zh-CN.js.map +1 -0
- package/dist/locale/zh-CN.modern.js +6 -0
- package/dist/locale/zh-CN.modern.js.map +1 -0
- package/package.json +104 -0
package/README.md
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# file-system-view
|
|
2
|
+
|
|
3
|
+
### 描述
|
|
4
|
+
|
|
5
|
+
A React component for displaying file directory structure with tree view, file type icons and context menu
|
|
6
|
+
|
|
7
|
+
### 安装
|
|
8
|
+
|
|
9
|
+
```shell
|
|
10
|
+
npm i --save @kne/file-system-view
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### 概述
|
|
14
|
+
|
|
15
|
+
一个用于浏览文件目录结构的 React 组件,支持树形展示、文件夹展开收起、文件类型图标、hover 操作菜单等功能。
|
|
16
|
+
|
|
17
|
+
### 特性
|
|
18
|
+
|
|
19
|
+
- 📁 **树形结构** - 支持多级目录展示,文件夹可展开/收起,带有层级连接线
|
|
20
|
+
- 🎨 **文件图标** - 自动识别 20+ 种文件类型,显示对应图标
|
|
21
|
+
- ⚡ **操作菜单** - hover 显示操作按钮,支持自定义菜单项
|
|
22
|
+
- 🌐 **国际化** - 内置中英文支持
|
|
23
|
+
- 📱 **美观易用** - 精心设计的交互和视觉样式
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### 示例
|
|
27
|
+
|
|
28
|
+
#### 示例代码
|
|
29
|
+
|
|
30
|
+
- 基础用法
|
|
31
|
+
- 展示完整的文件系统浏览功能,包含文件夹展开收起、文件类型图标、hover操作菜单等特性
|
|
32
|
+
- _FileSystemView(@kne/current-lib_file-system-view)[import * as _FileSystemView from "@kne/file-system-view"],(@kne/current-lib_file-system-view/dist/index.css)[import "@kne/file-system-view/dist/index.css"],antd(antd)[import * as antd from "antd"]
|
|
33
|
+
|
|
34
|
+
```jsx
|
|
35
|
+
const { default: FileSystemView } = _FileSystemView;
|
|
36
|
+
const { message } = antd;
|
|
37
|
+
|
|
38
|
+
const fileData = [
|
|
39
|
+
{
|
|
40
|
+
name: 'src',
|
|
41
|
+
type: 'directory',
|
|
42
|
+
children: [
|
|
43
|
+
{
|
|
44
|
+
name: 'components',
|
|
45
|
+
type: 'directory',
|
|
46
|
+
children: [
|
|
47
|
+
{ name: 'Button.tsx', type: 'file' },
|
|
48
|
+
{ name: 'Input.tsx', type: 'file' },
|
|
49
|
+
{ name: 'Modal.tsx', type: 'file' },
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: 'utils',
|
|
54
|
+
type: 'directory',
|
|
55
|
+
children: [
|
|
56
|
+
{ name: 'format.js', type: 'file' },
|
|
57
|
+
{ name: 'request.js', type: 'file' },
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
{ name: 'index.ts', type: 'file' },
|
|
61
|
+
{ name: 'App.tsx', type: 'file' },
|
|
62
|
+
{ name: 'styles.scss', type: 'file' },
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'public',
|
|
67
|
+
type: 'directory',
|
|
68
|
+
children: [
|
|
69
|
+
{ name: 'index.html', type: 'file' },
|
|
70
|
+
{ name: 'favicon.svg', type: 'file' },
|
|
71
|
+
{ name: 'logo.png', type: 'file' },
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'docs',
|
|
76
|
+
type: 'directory',
|
|
77
|
+
children: [
|
|
78
|
+
{ name: 'README.md', type: 'file' },
|
|
79
|
+
{ name: 'API.md', type: 'file' },
|
|
80
|
+
{ name: 'guide.pdf', type: 'file' },
|
|
81
|
+
{ name: 'very-long-file-name-that-demonstrates-text-overflow-handling-in-the-component-v2.0.0-final.pdf', type: 'file' },
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: 'media',
|
|
86
|
+
type: 'directory',
|
|
87
|
+
children: [
|
|
88
|
+
{
|
|
89
|
+
name: 'videos',
|
|
90
|
+
type: 'directory',
|
|
91
|
+
children: [
|
|
92
|
+
{ name: 'intro.mp4', type: 'file' },
|
|
93
|
+
{ name: 'demo.mov', type: 'file' },
|
|
94
|
+
{ name: 'tutorial.webm', type: 'file' },
|
|
95
|
+
],
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: 'audio',
|
|
99
|
+
type: 'directory',
|
|
100
|
+
children: [
|
|
101
|
+
{ name: 'bgm.mp3', type: 'file' },
|
|
102
|
+
{ name: 'sound.wav', type: 'file' },
|
|
103
|
+
{ name: 'voice.flac', type: 'file' },
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
},
|
|
108
|
+
{ name: 'package.json', type: 'file' },
|
|
109
|
+
{ name: 'tsconfig.json', type: 'file' },
|
|
110
|
+
{ name: '.gitignore', type: 'file' },
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
const menuItems = [
|
|
114
|
+
{
|
|
115
|
+
label: '打开',
|
|
116
|
+
onClick: (data, key) => message.info(`打开: ${key}`),
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
label: '复制路径',
|
|
120
|
+
onClick: (data, key) => {
|
|
121
|
+
navigator.clipboard.writeText(key);
|
|
122
|
+
message.success('路径已复制');
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
{ type: 'divider' },
|
|
126
|
+
{
|
|
127
|
+
label: '重命名',
|
|
128
|
+
onClick: (data, key) => message.info(`重命名: ${data.name}`),
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
label: '删除',
|
|
132
|
+
danger: true,
|
|
133
|
+
onClick: (data, key) => message.warning(`删除: ${key}`),
|
|
134
|
+
},
|
|
135
|
+
];
|
|
136
|
+
|
|
137
|
+
const BaseExample = () => {
|
|
138
|
+
return (
|
|
139
|
+
<div style={{ padding: 24, background: '#fafafa', borderRadius: 8 }}>
|
|
140
|
+
<FileSystemView
|
|
141
|
+
data={fileData}
|
|
142
|
+
menuItems={menuItems}
|
|
143
|
+
defaultExpandAll
|
|
144
|
+
onFileClick={(data, key) => message.info(`点击文件: ${key}`)}
|
|
145
|
+
/>
|
|
146
|
+
</div>
|
|
147
|
+
);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
render(<BaseExample />);
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
- 简单列表
|
|
155
|
+
- 不包含操作菜单的简单文件列表示例
|
|
156
|
+
- _FileSystemView(@kne/current-lib_file-system-view)[import * as _FileSystemView from "@kne/file-system-view"],(@kne/current-lib_file-system-view/dist/index.css)[import "@kne/file-system-view/dist/index.css"]
|
|
157
|
+
|
|
158
|
+
```jsx
|
|
159
|
+
const { default: FileSystemView } = _FileSystemView;
|
|
160
|
+
|
|
161
|
+
const fileData = [
|
|
162
|
+
{ name: 'index.js', type: 'file' },
|
|
163
|
+
{ name: 'utils.js', type: 'file' },
|
|
164
|
+
{ name: 'styles.css', type: 'file' },
|
|
165
|
+
];
|
|
166
|
+
|
|
167
|
+
const SimpleExample = () => {
|
|
168
|
+
return (
|
|
169
|
+
<div style={{ padding: 24, background: '#fafafa', borderRadius: 8 }}>
|
|
170
|
+
<FileSystemView
|
|
171
|
+
data={fileData}
|
|
172
|
+
onFileClick={(data, key) => console.log('点击文件:', key)}
|
|
173
|
+
/>
|
|
174
|
+
</div>
|
|
175
|
+
);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
render(<SimpleExample />);
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
- 受控模式
|
|
183
|
+
- 通过 expandedKeys 和 selectedKey 控制展开和选中状态,可配合外部按钮实现展开全部/收起全部功能
|
|
184
|
+
- _FileSystemView(@kne/current-lib_file-system-view)[import * as _FileSystemView from "@kne/file-system-view"],(@kne/current-lib_file-system-view/dist/index.css)[import "@kne/file-system-view/dist/index.css"],antd(antd)[import * as antd from "antd"]
|
|
185
|
+
|
|
186
|
+
```jsx
|
|
187
|
+
const { default: FileSystemView } = _FileSystemView;
|
|
188
|
+
const { useState } = React;
|
|
189
|
+
const { Button, Space, message } = antd;
|
|
190
|
+
|
|
191
|
+
const fileData = [
|
|
192
|
+
{
|
|
193
|
+
name: 'project',
|
|
194
|
+
type: 'directory',
|
|
195
|
+
children: [
|
|
196
|
+
{
|
|
197
|
+
name: 'src',
|
|
198
|
+
type: 'directory',
|
|
199
|
+
children: [
|
|
200
|
+
{ name: 'main.js', type: 'file' },
|
|
201
|
+
{ name: 'app.js', type: 'file' },
|
|
202
|
+
],
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
name: 'lib',
|
|
206
|
+
type: 'directory',
|
|
207
|
+
children: [{ name: 'utils.js', type: 'file' }],
|
|
208
|
+
},
|
|
209
|
+
{ name: 'config.json', type: 'file' },
|
|
210
|
+
],
|
|
211
|
+
},
|
|
212
|
+
];
|
|
213
|
+
|
|
214
|
+
const ControlledExample = () => {
|
|
215
|
+
const [expandedKeys, setExpandedKeys] = useState([]);
|
|
216
|
+
|
|
217
|
+
const expandAll = () => {
|
|
218
|
+
const allKeys = ['project', 'project/src', 'project/lib'];
|
|
219
|
+
setExpandedKeys(allKeys);
|
|
220
|
+
message.success('已展开所有目录');
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const collapseAll = () => {
|
|
224
|
+
setExpandedKeys([]);
|
|
225
|
+
message.success('已收起所有目录');
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
return (
|
|
229
|
+
<div>
|
|
230
|
+
<Space style={{ marginBottom: 16 }}>
|
|
231
|
+
<Button onClick={expandAll}>展开全部</Button>
|
|
232
|
+
<Button onClick={collapseAll}>收起全部</Button>
|
|
233
|
+
</Space>
|
|
234
|
+
<div style={{ padding: 24, background: '#fafafa', borderRadius: 8 }}>
|
|
235
|
+
<FileSystemView
|
|
236
|
+
data={fileData}
|
|
237
|
+
expandedKeys={expandedKeys}
|
|
238
|
+
onExpand={(keys) => setExpandedKeys(keys)}
|
|
239
|
+
/>
|
|
240
|
+
</div>
|
|
241
|
+
</div>
|
|
242
|
+
);
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
render(<ControlledExample />);
|
|
246
|
+
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### API
|
|
250
|
+
|
|
251
|
+
### Props
|
|
252
|
+
|
|
253
|
+
| 属性 | 类型 | 默认值 | 说明 |
|
|
254
|
+
|----|----|-----|----|
|
|
255
|
+
| data | `FileItem[]` | `[]` | 目录结构数据 |
|
|
256
|
+
| menuItems | `MenuItem[]` | - | 操作菜单项配置,不传则不显示操作按钮 |
|
|
257
|
+
| defaultExpandAll | `boolean` | `false` | 是否默认展开所有目录 |
|
|
258
|
+
| expandedKeys | `string[]` | - | (受控)展开的节点 key 数组 |
|
|
259
|
+
| onExpand | `(keys: string[], info: { node, expanded, nativeEvent }) => void` | - | 展开/收起节点时的回调 |
|
|
260
|
+
| onFileClick | `(data: FileItem, key: string) => void` | - | 点击文件时的回调 |
|
|
261
|
+
|
|
262
|
+
### FileItem
|
|
263
|
+
|
|
264
|
+
| 属性 | 类型 | 说明 |
|
|
265
|
+
|----|----|----|
|
|
266
|
+
| name | `string` | 文件或文件夹名称 |
|
|
267
|
+
| type | `'file' \| 'directory'` | 类型,文件夹需要有 children 或 type 为 'directory' |
|
|
268
|
+
| children | `FileItem[]` | 子项列表(仅文件夹) |
|
|
269
|
+
|
|
270
|
+
### MenuItem
|
|
271
|
+
|
|
272
|
+
| 属性 | 类型 | 说明 |
|
|
273
|
+
|----|----|----|
|
|
274
|
+
| label | `string` | 菜单项文本 |
|
|
275
|
+
| icon | `ReactNode` | 菜单项图标 |
|
|
276
|
+
| onClick | `(data: FileItem, key: string) => void` | 点击回调 |
|
|
277
|
+
| danger | `boolean` | 是否为危险操作(红色文本) |
|
|
278
|
+
| disabled | `(data: FileItem, key: string) => boolean` | 是否禁用的判断函数 |
|
|
279
|
+
|
|
280
|
+
### 支持的文件类型图标
|
|
281
|
+
|
|
282
|
+
组件内置以下文件类型的图标识别:
|
|
283
|
+
|
|
284
|
+
- **代码文件**: js, jsx, ts, tsx, json, css, scss, less, html, vue, py, java, go, rs
|
|
285
|
+
- **文档文件**: md, txt, pdf, doc, docx, xls, xlsx
|
|
286
|
+
- **图片文件**: png, jpg, jpeg, gif, svg, webp
|
|
287
|
+
- **视频文件**: mp4, avi, mov, wmv, mkv, webm, flv
|
|
288
|
+
- **音频文件**: mp3, wav, flac, aac, ogg, wma
|
|
289
|
+
- **压缩文件**: zip, rar, 7z, tar, gz
|
|
290
|
+
- **其他**: 默认文件图标
|
package/dist/index.css
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
._qMhn9 {
|
|
2
|
+
font-size: 14px;
|
|
3
|
+
-webkit-user-select: none;
|
|
4
|
+
user-select: none;
|
|
5
|
+
}
|
|
6
|
+
._qMhn9 .ant-tree {
|
|
7
|
+
background: transparent;
|
|
8
|
+
}
|
|
9
|
+
._qMhn9 .ant-tree .ant-tree-treenode {
|
|
10
|
+
padding: 0;
|
|
11
|
+
width: 100%;
|
|
12
|
+
position: relative;
|
|
13
|
+
}
|
|
14
|
+
._qMhn9 .ant-tree .ant-tree-treenode:before {
|
|
15
|
+
bottom: 0;
|
|
16
|
+
}
|
|
17
|
+
._qMhn9 .ant-tree .ant-tree-treenode .ant-tree-node-content-wrapper {
|
|
18
|
+
padding: 0;
|
|
19
|
+
flex: 1;
|
|
20
|
+
min-width: 0;
|
|
21
|
+
}
|
|
22
|
+
._qMhn9 .ant-tree .ant-tree-treenode .ant-tree-node-content-wrapper:hover {
|
|
23
|
+
background: transparent;
|
|
24
|
+
}
|
|
25
|
+
._qMhn9 .ant-tree .ant-tree-treenode .ant-tree-switcher {
|
|
26
|
+
display: flex;
|
|
27
|
+
align-items: center;
|
|
28
|
+
justify-content: center;
|
|
29
|
+
width: 10px;
|
|
30
|
+
flex-shrink: 0;
|
|
31
|
+
}
|
|
32
|
+
._qMhn9 .ant-tree .ant-tree-treenode .ant-tree-switcher:before {
|
|
33
|
+
display: none;
|
|
34
|
+
}
|
|
35
|
+
._qMhn9 .ant-tree-list-holder-inner .ant-tree-treenode {
|
|
36
|
+
padding-left: 0;
|
|
37
|
+
}
|
|
38
|
+
._qMhn9 .ant-tree-switcher-line-icon {
|
|
39
|
+
color: #d9d9d9;
|
|
40
|
+
}
|
|
41
|
+
._qMhn9 .ant-tree-node-content-wrapper .tree-node {
|
|
42
|
+
border-left: 2px solid transparent;
|
|
43
|
+
transition: all 0.2s ease;
|
|
44
|
+
}
|
|
45
|
+
._qMhn9 .ant-tree-treenode[style*="padding-left: 0px"] .tree-node, ._qMhn9 .ant-tree-treenode:first-child .tree-node {
|
|
46
|
+
background: rgba(0, 0, 0, 0.02);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
._t015K {
|
|
50
|
+
display: flex;
|
|
51
|
+
align-items: center;
|
|
52
|
+
padding: 4px 8px;
|
|
53
|
+
border-radius: 4px;
|
|
54
|
+
cursor: pointer;
|
|
55
|
+
transition: all 0.2s ease;
|
|
56
|
+
position: relative;
|
|
57
|
+
min-height: 28px;
|
|
58
|
+
width: 100%;
|
|
59
|
+
}
|
|
60
|
+
._t015K:hover {
|
|
61
|
+
background: rgba(0, 0, 0, 0.04);
|
|
62
|
+
}
|
|
63
|
+
._t015K:hover ._mLgqh {
|
|
64
|
+
margin-left: 4px;
|
|
65
|
+
opacity: 1;
|
|
66
|
+
visibility: visible;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
._dgomj {
|
|
70
|
+
display: flex;
|
|
71
|
+
align-items: center;
|
|
72
|
+
justify-content: center;
|
|
73
|
+
width: 16px;
|
|
74
|
+
height: 16px;
|
|
75
|
+
margin-right: 6px;
|
|
76
|
+
flex-shrink: 0;
|
|
77
|
+
}
|
|
78
|
+
._dgomj .anticon {
|
|
79
|
+
font-size: 14px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
._r6Ngq {
|
|
83
|
+
flex: 1;
|
|
84
|
+
overflow: hidden;
|
|
85
|
+
white-space: nowrap;
|
|
86
|
+
color: #333;
|
|
87
|
+
font-size: 13px;
|
|
88
|
+
display: flex;
|
|
89
|
+
min-width: 0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
._st428 {
|
|
93
|
+
overflow: hidden;
|
|
94
|
+
text-overflow: ellipsis;
|
|
95
|
+
white-space: nowrap;
|
|
96
|
+
min-width: 0;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
._POSao {
|
|
100
|
+
flex-shrink: 0;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
._mLgqh {
|
|
104
|
+
opacity: 0;
|
|
105
|
+
visibility: hidden;
|
|
106
|
+
transition: all 0.2s ease;
|
|
107
|
+
margin-left: auto;
|
|
108
|
+
padding: 0 2px;
|
|
109
|
+
height: 20px;
|
|
110
|
+
width: 20px;
|
|
111
|
+
flex-shrink: 0;
|
|
112
|
+
color: #999;
|
|
113
|
+
}
|
|
114
|
+
._mLgqh:hover {
|
|
115
|
+
color: #1677ff;
|
|
116
|
+
background: rgba(22, 119, 255, 0.1);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
._4zeDm {
|
|
120
|
+
padding: 40px 20px;
|
|
121
|
+
text-align: center;
|
|
122
|
+
color: #999;
|
|
123
|
+
font-size: 14px;
|
|
124
|
+
}
|
|
125
|
+
/*# sourceMappingURL=index.css.map */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["style.module.scss"],"names":[],"mappings":"AAAA;EACE,eAAe;EACf,yBAAiB;UAAjB,iBAAiB;AACnB;AACA;EACE,uBAAuB;AACzB;AACA;EACE,UAAU;EACV,WAAW;EACX,kBAAkB;AACpB;AACA;EACE,SAAS;AACX;AACA;EACE,UAAU;EACV,OAAO;EACP,YAAY;AACd;AACA;EACE,uBAAuB;AACzB;AACA;EACE,aAAa;EACb,mBAAmB;EACnB,uBAAuB;EACvB,WAAW;EACX,cAAc;AAChB;AACA;EACE,aAAa;AACf;AACA;EACE,eAAe;AACjB;AACA;EACE,cAAc;AAChB;AACA;EACE,kCAAkC;EAClC,yBAAyB;AAC3B;AACA;EACE,+BAA+B;AACjC;;AAEA;EACE,aAAa;EACb,mBAAmB;EACnB,gBAAgB;EAChB,kBAAkB;EAClB,eAAe;EACf,yBAAyB;EACzB,kBAAkB;EAClB,gBAAgB;EAChB,WAAW;AACb;AACA;EACE,+BAA+B;AACjC;AACA;EACE,gBAAgB;EAChB,UAAU;EACV,mBAAmB;AACrB;;AAEA;EACE,aAAa;EACb,mBAAmB;EACnB,uBAAuB;EACvB,WAAW;EACX,YAAY;EACZ,iBAAiB;EACjB,cAAc;AAChB;AACA;EACE,eAAe;AACjB;;AAEA;EACE,OAAO;EACP,gBAAgB;EAChB,mBAAmB;EACnB,WAAW;EACX,eAAe;EACf,aAAa;EACb,YAAY;AACd;;AAEA;EACE,gBAAgB;EAChB,uBAAuB;EACvB,mBAAmB;EACnB,YAAY;AACd;;AAEA;EACE,cAAc;AAChB;;AAEA;EACE,UAAU;EACV,kBAAkB;EAClB,yBAAyB;EACzB,iBAAiB;EACjB,cAAc;EACd,YAAY;EACZ,WAAW;EACX,cAAc;EACd,WAAW;AACb;AACA;EACE,cAAc;EACd,mCAAmC;AACrC;;AAEA;EACE,kBAAkB;EAClB,kBAAkB;EAClB,WAAW;EACX,eAAe;AACjB","file":"index.css","sourcesContent":[".file-system-view {\n font-size: 14px;\n user-select: none;\n}\n.file-system-view :global .ant-tree {\n background: transparent;\n}\n.file-system-view :global .ant-tree .ant-tree-treenode {\n padding: 0;\n width: 100%;\n position: relative;\n}\n.file-system-view :global .ant-tree .ant-tree-treenode:before {\n bottom: 0;\n}\n.file-system-view :global .ant-tree .ant-tree-treenode .ant-tree-node-content-wrapper {\n padding: 0;\n flex: 1;\n min-width: 0;\n}\n.file-system-view :global .ant-tree .ant-tree-treenode .ant-tree-node-content-wrapper:hover {\n background: transparent;\n}\n.file-system-view :global .ant-tree .ant-tree-treenode .ant-tree-switcher {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 10px;\n flex-shrink: 0;\n}\n.file-system-view :global .ant-tree .ant-tree-treenode .ant-tree-switcher:before {\n display: none;\n}\n.file-system-view :global .ant-tree-list-holder-inner .ant-tree-treenode {\n padding-left: 0;\n}\n.file-system-view :global .ant-tree-switcher-line-icon {\n color: #d9d9d9;\n}\n.file-system-view :global .ant-tree-node-content-wrapper .tree-node {\n border-left: 2px solid transparent;\n transition: all 0.2s ease;\n}\n.file-system-view :global .ant-tree-treenode[style*=\"padding-left: 0px\"] .tree-node, .file-system-view :global .ant-tree-treenode:first-child .tree-node {\n background: rgba(0, 0, 0, 0.02);\n}\n\n.tree-node {\n display: flex;\n align-items: center;\n padding: 4px 8px;\n border-radius: 4px;\n cursor: pointer;\n transition: all 0.2s ease;\n position: relative;\n min-height: 28px;\n width: 100%;\n}\n.tree-node:hover {\n background: rgba(0, 0, 0, 0.04);\n}\n.tree-node:hover .action-btn {\n margin-left: 4px;\n opacity: 1;\n visibility: visible;\n}\n\n.node-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n margin-right: 6px;\n flex-shrink: 0;\n}\n.node-icon :global .anticon {\n font-size: 14px;\n}\n\n.node-title {\n flex: 1;\n overflow: hidden;\n white-space: nowrap;\n color: #333;\n font-size: 13px;\n display: flex;\n min-width: 0;\n}\n\n.node-name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n min-width: 0;\n}\n\n.node-ext {\n flex-shrink: 0;\n}\n\n.action-btn {\n opacity: 0;\n visibility: hidden;\n transition: all 0.2s ease;\n margin-left: auto;\n padding: 0 2px;\n height: 20px;\n width: 20px;\n flex-shrink: 0;\n color: #999;\n}\n.action-btn:hover {\n color: #1677ff;\n background: rgba(22, 119, 255, 0.1);\n}\n\n.empty {\n padding: 40px 20px;\n text-align: center;\n color: #999;\n font-size: 14px;\n}"]}
|