@huyooo/file-explorer-frontend-react 0.4.18 → 0.4.21

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 (44) hide show
  1. package/dist/index.css +0 -1
  2. package/dist/index.js +1 -3456
  3. package/package.json +4 -4
  4. package/dist/index.css.map +0 -1
  5. package/dist/index.js.map +0 -1
  6. package/src/components/Breadcrumb.css +0 -61
  7. package/src/components/Breadcrumb.tsx +0 -38
  8. package/src/components/CompressDialog.css +0 -267
  9. package/src/components/CompressDialog.tsx +0 -222
  10. package/src/components/ContextMenu.css +0 -155
  11. package/src/components/ContextMenu.tsx +0 -375
  12. package/src/components/FileGrid.css +0 -239
  13. package/src/components/FileGrid.tsx +0 -278
  14. package/src/components/FileIcon.css +0 -41
  15. package/src/components/FileIcon.tsx +0 -86
  16. package/src/components/FileInfoDialog.css +0 -214
  17. package/src/components/FileInfoDialog.tsx +0 -202
  18. package/src/components/FileList.css +0 -169
  19. package/src/components/FileList.tsx +0 -228
  20. package/src/components/FileListView.css +0 -36
  21. package/src/components/FileListView.tsx +0 -355
  22. package/src/components/FileSidebar.css +0 -94
  23. package/src/components/FileSidebar.tsx +0 -66
  24. package/src/components/ProgressDialog.css +0 -211
  25. package/src/components/ProgressDialog.tsx +0 -183
  26. package/src/components/SortIndicator.css +0 -7
  27. package/src/components/SortIndicator.tsx +0 -19
  28. package/src/components/StatusBar.css +0 -20
  29. package/src/components/StatusBar.tsx +0 -21
  30. package/src/components/Toolbar.css +0 -150
  31. package/src/components/Toolbar.tsx +0 -127
  32. package/src/components/Window.css +0 -246
  33. package/src/components/Window.tsx +0 -335
  34. package/src/hooks/useApplicationIcon.ts +0 -80
  35. package/src/hooks/useDragAndDrop.ts +0 -104
  36. package/src/hooks/useMediaPlayer.ts +0 -164
  37. package/src/hooks/useSelection.ts +0 -112
  38. package/src/hooks/useWindowDrag.ts +0 -60
  39. package/src/hooks/useWindowResize.ts +0 -126
  40. package/src/index.css +0 -184
  41. package/src/index.ts +0 -37
  42. package/src/types/index.ts +0 -274
  43. package/src/utils/fileTypeIcon.ts +0 -309
  44. package/src/utils/folderTypeIcon.ts +0 -132
@@ -1,355 +0,0 @@
1
- import { useState, useCallback, useImperativeHandle, forwardRef } from 'react';
2
- import { Icon } from '@iconify/react';
3
- import { FileGrid } from './FileGrid';
4
- import { FileList } from './FileList';
5
- import { FileType, type FileItem, type SortConfig, type FileExplorerAdapter } from '../types';
6
- import './FileListView.css';
7
-
8
- interface FileListViewProps {
9
- items: FileItem[];
10
- viewMode?: 'grid' | 'list';
11
- loading?: boolean;
12
- adapter?: FileExplorerAdapter;
13
- currentPath?: string;
14
- getAppIconUrl?: (item: FileItem) => string | undefined;
15
- onOpen?: (item: FileItem) => void;
16
- onSelectionChange?: (ids: Set<string>, items: FileItem[]) => void;
17
- onContextMenu?: (event: React.MouseEvent, item: FileItem) => void;
18
- onContextMenuEmpty?: (event: React.MouseEvent) => void;
19
- onRename?: (item: FileItem, newName: string) => void;
20
- onSortChange?: (config: SortConfig) => void;
21
- onMove?: (sourceIds: string[], targetId: string) => void;
22
- }
23
-
24
- export interface FileListViewHandle {
25
- clearSelection: () => void;
26
- startRename: (id: string) => void;
27
- selectAll: () => void;
28
- selectedIds: Set<string>;
29
- selectedItems: FileItem[];
30
- }
31
-
32
- export const FileListView = forwardRef<FileListViewHandle, FileListViewProps>(
33
- (
34
- {
35
- items,
36
- viewMode = 'grid',
37
- loading = false,
38
- adapter,
39
- currentPath,
40
- getAppIconUrl,
41
- onOpen,
42
- onSelectionChange,
43
- onContextMenu,
44
- onContextMenuEmpty,
45
- onRename,
46
- onSortChange,
47
- onMove,
48
- },
49
- ref
50
- ) => {
51
- const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
52
- const [editingId, setEditingId] = useState<string | null>(null);
53
- const [dragOverId, setDragOverId] = useState<string | null>(null);
54
- const [sortConfig, setSortConfig] = useState<SortConfig>({
55
- field: 'name',
56
- direction: 'asc',
57
- });
58
-
59
- // 获取选中的文件项
60
- const selectedItems = items.filter((item) => selectedIds.has(item.id));
61
-
62
- // 选择处理
63
- const handleSelect = useCallback(
64
- (item: FileItem, e: React.MouseEvent) => {
65
- if (e.metaKey || e.ctrlKey) {
66
- // 多选
67
- setSelectedIds((prev) => {
68
- const newSet = new Set(prev);
69
- if (newSet.has(item.id)) {
70
- newSet.delete(item.id);
71
- } else {
72
- newSet.add(item.id);
73
- }
74
- const newItems = items.filter((i) => newSet.has(i.id));
75
- onSelectionChange?.(newSet, newItems);
76
- return newSet;
77
- });
78
- } else if (e.shiftKey && selectedIds.size > 0) {
79
- // 范围选择
80
- const lastId = Array.from(selectedIds).pop();
81
- const lastIndex = items.findIndex((i) => i.id === lastId);
82
- const currentIndex = items.findIndex((i) => i.id === item.id);
83
- const start = Math.min(lastIndex, currentIndex);
84
- const end = Math.max(lastIndex, currentIndex);
85
- const newSet = new Set<string>();
86
- for (let i = start; i <= end; i++) {
87
- newSet.add(items[i]!.id);
88
- }
89
- setSelectedIds(newSet);
90
- const newItems = items.filter((i) => newSet.has(i.id));
91
- onSelectionChange?.(newSet, newItems);
92
- } else {
93
- // 单选
94
- const newSet = new Set([item.id]);
95
- setSelectedIds(newSet);
96
- onSelectionChange?.(newSet, [item]);
97
- }
98
- },
99
- [items, selectedIds, onSelectionChange]
100
- );
101
-
102
- const handleEmptyClick = useCallback(
103
- (e: React.MouseEvent) => {
104
- if (e.target === e.currentTarget) {
105
- clearSelection();
106
- }
107
- },
108
- []
109
- );
110
-
111
- // 清除选择
112
- const clearSelection = useCallback(() => {
113
- setSelectedIds(new Set());
114
- onSelectionChange?.(new Set(), []);
115
- }, [onSelectionChange]);
116
-
117
- // 打开文件/文件夹
118
- const handleOpen = useCallback(
119
- (item: FileItem) => {
120
- onOpen?.(item);
121
- },
122
- [onOpen]
123
- );
124
-
125
- // 右键菜单
126
- const handleContextMenu = useCallback(
127
- (item: FileItem, e: React.MouseEvent) => {
128
- if (!selectedIds.has(item.id)) {
129
- const newSet = new Set([item.id]);
130
- setSelectedIds(newSet);
131
- onSelectionChange?.(newSet, [item]);
132
- }
133
- onContextMenu?.(e, item);
134
- },
135
- [selectedIds, onSelectionChange, onContextMenu]
136
- );
137
-
138
- const handleEmptyContextMenu = useCallback(
139
- (e: React.MouseEvent) => {
140
- const target = e.target as HTMLElement;
141
- if (
142
- !target.closest('.file-grid-item') &&
143
- !target.closest('.file-list-row')
144
- ) {
145
- clearSelection();
146
- onContextMenuEmpty?.(e);
147
- }
148
- },
149
- [clearSelection, onContextMenuEmpty]
150
- );
151
-
152
- // 从子组件触发的空白处右键菜单
153
- const handleEmptyContextMenuFromChild = useCallback(
154
- (e: React.MouseEvent) => {
155
- clearSelection();
156
- onContextMenuEmpty?.(e);
157
- },
158
- [clearSelection, onContextMenuEmpty]
159
- );
160
-
161
- // 名称点击(用于重命名)
162
- const handleNameClick = useCallback(
163
- (item: FileItem, e: React.MouseEvent) => {
164
- if (selectedIds.has(item.id) && selectedIds.size === 1) {
165
- setTimeout(() => {
166
- if (selectedIds.has(item.id)) {
167
- setEditingId(item.id);
168
- }
169
- }, 500);
170
- }
171
- },
172
- [selectedIds]
173
- );
174
-
175
- // 重命名
176
- const handleRename = useCallback(
177
- (item: FileItem, newName: string) => {
178
- if (newName && newName !== item.name) {
179
- onRename?.(item, newName);
180
- }
181
- setEditingId(null);
182
- },
183
- [onRename]
184
- );
185
-
186
- const handleRenameCancel = useCallback(() => {
187
- setEditingId(null);
188
- }, []);
189
-
190
- // 排序
191
- const handleSort = useCallback(
192
- (field: string) => {
193
- setSortConfig((prev) => {
194
- const newConfig: SortConfig =
195
- prev.field === field
196
- ? { ...prev, direction: prev.direction === 'asc' ? 'desc' : 'asc' }
197
- : { field: field as SortConfig['field'], direction: 'asc' };
198
- onSortChange?.(newConfig);
199
- return newConfig;
200
- });
201
- },
202
- [onSortChange]
203
- );
204
-
205
- // 拖拽
206
- const handleDragStart = useCallback(
207
- (e: React.DragEvent, item: FileItem) => {
208
- if (!selectedIds.has(item.id)) {
209
- const newSet = new Set([item.id]);
210
- setSelectedIds(newSet);
211
- onSelectionChange?.(newSet, [item]);
212
- }
213
- e.dataTransfer.setData(
214
- 'text/plain',
215
- JSON.stringify([...selectedIds])
216
- );
217
- },
218
- [selectedIds, onSelectionChange]
219
- );
220
-
221
- const handleDragOver = useCallback(
222
- (e: React.DragEvent, item: FileItem) => {
223
- if (item.type === FileType.FOLDER && !selectedIds.has(item.id)) {
224
- setDragOverId(item.id);
225
- }
226
- },
227
- [selectedIds]
228
- );
229
-
230
- const handleDragLeave = useCallback(() => {
231
- setDragOverId(null);
232
- }, []);
233
-
234
- const handleDrop = useCallback(
235
- (e: React.DragEvent, targetItem: FileItem) => {
236
- setDragOverId(null);
237
-
238
- if (targetItem.type !== FileType.FOLDER) return;
239
-
240
- const data = e.dataTransfer.getData('text/plain');
241
- if (!data) return;
242
-
243
- try {
244
- const draggedIds: string[] = JSON.parse(data);
245
- if (draggedIds.includes(targetItem.id)) return;
246
-
247
- onMove?.(draggedIds, targetItem.id);
248
- clearSelection();
249
- } catch (error) {
250
- console.error('拖拽解析失败:', error);
251
- }
252
- },
253
- [onMove, clearSelection]
254
- );
255
-
256
- // 开始重命名(供外部调用)
257
- const startRename = useCallback((id: string) => {
258
- setEditingId(id);
259
- }, []);
260
-
261
- // 全选
262
- const selectAll = useCallback(() => {
263
- const newSet = new Set(items.map((i) => i.id));
264
- setSelectedIds(newSet);
265
- onSelectionChange?.(newSet, items);
266
- }, [items, onSelectionChange]);
267
-
268
- // 暴露方法
269
- useImperativeHandle(
270
- ref,
271
- () => ({
272
- clearSelection,
273
- startRename,
274
- selectAll,
275
- selectedIds,
276
- selectedItems,
277
- }),
278
- [selectedIds, selectedItems, clearSelection, startRename, selectAll]
279
- );
280
-
281
- return (
282
- <div
283
- className="file-list-view"
284
- onClick={handleEmptyClick}
285
- onContextMenu={(e) => {
286
- e.preventDefault();
287
- handleEmptyContextMenu(e);
288
- }}
289
- >
290
- {/* 加载中 */}
291
- {loading && (
292
- <div className="file-list-view-loading">
293
- <div className="file-list-view-spinner"></div>
294
- <p>加载中...</p>
295
- </div>
296
- )}
297
-
298
- {/* 空文件夹 */}
299
- {!loading && items.length === 0 && (
300
- <div className="file-list-view-empty">
301
- <Icon icon="lucide:folder-open" width={64} height={64} className="file-list-view-empty-icon" />
302
- <p>文件夹为空</p>
303
- </div>
304
- )}
305
-
306
- {/* 网格视图 */}
307
- {!loading && items.length > 0 && viewMode === 'grid' && (
308
- <FileGrid
309
- items={items}
310
- selectedIds={selectedIds}
311
- editingId={editingId}
312
- dragOverId={dragOverId}
313
- getAppIconUrl={getAppIconUrl}
314
- onSelect={handleSelect}
315
- onOpen={handleOpen}
316
- onContextMenu={handleContextMenu}
317
- onContextMenuEmpty={handleEmptyContextMenuFromChild}
318
- onNameClick={handleNameClick}
319
- onRename={handleRename}
320
- onRenameCancel={handleRenameCancel}
321
- onDragStart={handleDragStart}
322
- onDragOver={handleDragOver}
323
- onDragLeave={handleDragLeave}
324
- onDrop={handleDrop}
325
- />
326
- )}
327
-
328
- {/* 列表视图 */}
329
- {!loading && items.length > 0 && viewMode === 'list' && (
330
- <FileList
331
- items={items}
332
- selectedIds={selectedIds}
333
- editingId={editingId}
334
- dragOverId={dragOverId}
335
- sortConfig={sortConfig}
336
- onSelect={handleSelect}
337
- onOpen={handleOpen}
338
- onContextMenu={handleContextMenu}
339
- onContextMenuEmpty={handleEmptyContextMenuFromChild}
340
- onNameClick={handleNameClick}
341
- onRename={handleRename}
342
- onRenameCancel={handleRenameCancel}
343
- onSort={handleSort}
344
- onDragStart={handleDragStart}
345
- onDragOver={handleDragOver}
346
- onDragLeave={handleDragLeave}
347
- onDrop={handleDrop}
348
- />
349
- )}
350
- </div>
351
- );
352
- }
353
- );
354
-
355
- FileListView.displayName = 'FileListView';
@@ -1,94 +0,0 @@
1
- .file-sidebar {
2
- width: 12rem;
3
- background: var(--huyooo-muted);
4
- backdrop-filter: blur(24px);
5
- border-right: 1px solid var(--huyooo-border);
6
- display: flex;
7
- flex-direction: column;
8
- padding-top: 2rem;
9
- padding-bottom: 1rem;
10
- height: 100%;
11
- box-sizing: border-box;
12
- overflow-y: auto;
13
- overflow-x: hidden;
14
- user-select: none;
15
- -webkit-app-region: drag; /* 整个侧边栏可拖拽 */
16
- }
17
-
18
- /* 自定义滚动条样式 */
19
- .file-sidebar::-webkit-scrollbar {
20
- width: 6px;
21
- }
22
-
23
- .file-sidebar::-webkit-scrollbar-track {
24
- background: transparent;
25
- }
26
-
27
- .file-sidebar::-webkit-scrollbar-thumb {
28
- background: var(--huyooo-scrollbar);
29
- border-radius: 3px;
30
- }
31
-
32
- .file-sidebar::-webkit-scrollbar-thumb:hover {
33
- background: var(--huyooo-scrollbar-hover);
34
- }
35
-
36
- .file-sidebar-section {
37
- margin-bottom: 0;
38
- -webkit-app-region: no-drag; /* section 内容区域不可拖拽 */
39
- }
40
-
41
- .file-sidebar-section-title {
42
- padding: 0 1rem;
43
- margin-bottom: 0.5rem;
44
- font-size: 0.75rem;
45
- font-weight: 600;
46
- color: var(--huyooo-text-muted);
47
- text-transform: uppercase;
48
- letter-spacing: 0.05em;
49
- }
50
-
51
- .file-sidebar-section + .file-sidebar-section .file-sidebar-section-title {
52
- margin-top: 1.5rem;
53
- }
54
-
55
- .file-sidebar-list {
56
- list-style: none;
57
- display: flex;
58
- flex-direction: column;
59
- gap: 0.25rem;
60
- padding: 0 0.5rem;
61
- margin: 0;
62
- }
63
-
64
- .file-sidebar-item {
65
- display: flex;
66
- align-items: center;
67
- gap: 0.75rem;
68
- padding: 0.375rem 0.75rem;
69
- border-radius: 0.375rem;
70
- cursor: pointer;
71
- font-size: 0.875rem;
72
- font-weight: 500;
73
- color: var(--huyooo-text-muted);
74
- transition: all 200ms;
75
- /* no-drag 已由 section 继承 */
76
- }
77
-
78
- .file-sidebar-item:hover {
79
- background: var(--huyooo-muted-hover);
80
- color: var(--huyooo-text);
81
- }
82
-
83
- .file-sidebar-item--active {
84
- background: var(--huyooo-muted-hover);
85
- color: var(--huyooo-text);
86
- }
87
-
88
- .file-sidebar-item-icon {
89
- color: var(--huyooo-text-muted);
90
- }
91
-
92
- .file-sidebar-item-icon--active {
93
- color: var(--huyooo-primary);
94
- }
@@ -1,66 +0,0 @@
1
- import { Icon } from '@iconify/react';
2
- import type { SidebarItem } from '../types';
3
- import './FileSidebar.css';
4
-
5
- interface SidebarSection {
6
- id: string;
7
- title: string;
8
- items: SidebarItem[];
9
- }
10
-
11
- interface FileSidebarProps {
12
- sections: SidebarSection[];
13
- activeId?: string;
14
- onNavigate?: (item: SidebarItem) => void;
15
- }
16
-
17
- export function FileSidebar({ sections, activeId, onNavigate }: FileSidebarProps) {
18
- const handleNavigate = (item: SidebarItem) => {
19
- onNavigate?.(item);
20
- };
21
-
22
- // 将 Lucide 图标名称转换为 Iconify 格式
23
- const getIconName = (iconName?: string): string => {
24
- if (!iconName) return 'mdi:folder';
25
- // 如果已经是 Iconify 格式,直接返回
26
- if (iconName.includes(':')) return iconName;
27
- // 否则转换为 lucide: 格式
28
- return `lucide:${iconName.toLowerCase()}`;
29
- };
30
-
31
- return (
32
- <div className="file-sidebar">
33
- {sections.map((section) => (
34
- <div key={section.id} className="file-sidebar-section">
35
- <div className="file-sidebar-section-title">{section.title}</div>
36
- <ul className="file-sidebar-list">
37
- {section.items.map((item) => {
38
- const isActive = activeId === item.id;
39
- return (
40
- <li
41
- key={item.id}
42
- onClick={() => handleNavigate(item)}
43
- className={`file-sidebar-item ${
44
- isActive ? 'file-sidebar-item--active' : ''
45
- }`}
46
- >
47
- <Icon
48
- icon={getIconName(item.icon)}
49
- width={18}
50
- height={18}
51
- className={
52
- isActive
53
- ? 'file-sidebar-item-icon--active'
54
- : 'file-sidebar-item-icon'
55
- }
56
- />
57
- <span>{item.label}</span>
58
- </li>
59
- );
60
- })}
61
- </ul>
62
- </div>
63
- ))}
64
- </div>
65
- );
66
- }
@@ -1,211 +0,0 @@
1
- .progress-dialog-overlay {
2
- position: fixed;
3
- inset: 0;
4
- background: var(--huyooo-overlay);
5
- display: flex;
6
- align-items: center;
7
- justify-content: center;
8
- z-index: 10001;
9
- }
10
-
11
- .progress-dialog {
12
- background: var(--huyooo-panel-bg);
13
- border-radius: 12px;
14
- box-shadow: var(--huyooo-shadow-lg);
15
- width: 380px;
16
- max-width: 90vw;
17
- overflow: hidden;
18
- display: flex;
19
- flex-direction: column;
20
- }
21
-
22
- .progress-dialog-header {
23
- display: flex;
24
- align-items: center;
25
- justify-content: space-between;
26
- padding: 16px 20px;
27
- border-bottom: 1px solid var(--huyooo-border);
28
- }
29
-
30
- .progress-dialog-title {
31
- display: flex;
32
- align-items: center;
33
- gap: 10px;
34
- font-weight: 600;
35
- font-size: 16px;
36
- color: var(--huyooo-text);
37
- }
38
-
39
- .progress-dialog-close {
40
- background: none;
41
- border: none;
42
- padding: 4px;
43
- cursor: pointer;
44
- color: var(--huyooo-text-muted);
45
- border-radius: 4px;
46
- display: flex;
47
- align-items: center;
48
- justify-content: center;
49
- }
50
-
51
- .progress-dialog-close:hover {
52
- background: var(--huyooo-muted);
53
- color: var(--huyooo-text);
54
- }
55
-
56
- .progress-dialog-content {
57
- padding: 24px 20px;
58
- display: flex;
59
- flex-direction: column;
60
- gap: 16px;
61
- }
62
-
63
- .progress-dialog-status {
64
- font-size: 14px;
65
- color: var(--huyooo-text);
66
- text-align: center;
67
- }
68
-
69
- .progress-dialog-bar-container {
70
- display: flex;
71
- align-items: center;
72
- gap: 12px;
73
- }
74
-
75
- .progress-dialog-bar {
76
- flex: 1;
77
- height: 8px;
78
- background: var(--huyooo-border);
79
- border-radius: 4px;
80
- overflow: hidden;
81
- }
82
-
83
- .progress-dialog-bar-fill {
84
- height: 100%;
85
- background: var(--huyooo-primary);
86
- border-radius: 4px;
87
- transition: width 0.3s ease;
88
- }
89
-
90
- .progress-dialog-percent {
91
- font-size: 13px;
92
- font-weight: 500;
93
- color: var(--huyooo-text);
94
- min-width: 40px;
95
- text-align: right;
96
- }
97
-
98
- .progress-dialog-current-file {
99
- font-size: 12px;
100
- color: var(--huyooo-text-muted);
101
- text-align: center;
102
- white-space: nowrap;
103
- overflow: hidden;
104
- text-overflow: ellipsis;
105
- }
106
-
107
- .progress-dialog-count {
108
- font-size: 12px;
109
- color: var(--huyooo-text-muted);
110
- text-align: center;
111
- }
112
-
113
- .progress-dialog-error {
114
- padding: 12px;
115
- background: var(--huyooo-danger-bg);
116
- border: 1px solid var(--huyooo-danger-border);
117
- border-radius: 6px;
118
- color: var(--huyooo-danger);
119
- font-size: 13px;
120
- }
121
-
122
- .progress-dialog-output {
123
- padding: 12px;
124
- background: var(--huyooo-success-bg);
125
- border: 1px solid var(--huyooo-success-border);
126
- border-radius: 6px;
127
- font-size: 12px;
128
- display: flex;
129
- flex-direction: column;
130
- gap: 4px;
131
- }
132
-
133
- .progress-dialog-output-label {
134
- color: var(--huyooo-success);
135
- font-weight: 500;
136
- }
137
-
138
- .progress-dialog-output-path {
139
- color: var(--huyooo-success);
140
- word-break: break-all;
141
- }
142
-
143
- .progress-dialog-footer {
144
- display: flex;
145
- justify-content: flex-end;
146
- gap: 12px;
147
- padding: 16px 20px;
148
- border-top: 1px solid var(--huyooo-border);
149
- }
150
-
151
- .progress-dialog-btn {
152
- padding: 8px 16px;
153
- border-radius: 6px;
154
- font-size: 14px;
155
- font-weight: 500;
156
- cursor: pointer;
157
- transition: all 0.2s;
158
- display: flex;
159
- align-items: center;
160
- gap: 6px;
161
- }
162
-
163
- .progress-dialog-btn-cancel {
164
- background: var(--huyooo-surface-2);
165
- border: 1px solid var(--huyooo-border);
166
- color: var(--huyooo-text);
167
- }
168
-
169
- .progress-dialog-btn-cancel:hover {
170
- background: var(--huyooo-surface);
171
- }
172
-
173
- .progress-dialog-btn-folder {
174
- background: var(--huyooo-success-bg);
175
- border: 1px solid var(--huyooo-success-border);
176
- color: var(--huyooo-success);
177
- }
178
-
179
- .progress-dialog-btn-folder:hover {
180
- background: color-mix(in srgb, var(--huyooo-success-bg) 75%, var(--huyooo-success) 25%);
181
- }
182
-
183
- .progress-dialog-btn-close {
184
- background: var(--huyooo-primary);
185
- border: 1px solid var(--huyooo-primary);
186
- color: white;
187
- }
188
-
189
- .progress-dialog-btn-close:hover {
190
- background: var(--huyooo-primary-hover, var(--huyooo-primary));
191
- }
192
-
193
- /* 旋转动画 */
194
- .progress-dialog-icon-spin {
195
- animation: spin 1s linear infinite;
196
- color: var(--huyooo-primary);
197
- }
198
-
199
- @keyframes spin {
200
- from { transform: rotate(0deg); }
201
- to { transform: rotate(360deg); }
202
- }
203
-
204
- .progress-dialog-icon-success {
205
- color: var(--huyooo-success);
206
- }
207
-
208
- .progress-dialog-icon-error {
209
- color: var(--huyooo-danger);
210
- }
211
-