@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,104 +0,0 @@
1
- import { useState, useCallback } from 'react';
2
- import type { FileItem } from '../types';
3
- import { FileType } from '../types';
4
-
5
- /**
6
- * 拖拽操作管理
7
- */
8
- export function useDragAndDrop(
9
- getSelectedIds: () => Set<string>,
10
- onSelect: (id: string, multi: boolean, range: boolean) => void,
11
- onMove: (targetFolderId: string, itemIds: Set<string>) => void
12
- ) {
13
- const [dragOverId, setDragOverId] = useState<string | null>(null);
14
- const [isDragging, setIsDragging] = useState(false);
15
-
16
- /**
17
- * 开始拖拽
18
- */
19
- const handleDragStart = useCallback((e: React.DragEvent, itemId: string) => {
20
- if (!e.dataTransfer) return;
21
-
22
- const selectedIds = getSelectedIds();
23
-
24
- // 如果拖拽的项目未被选中,先选中它
25
- if (!selectedIds.has(itemId)) {
26
- onSelect(itemId, false, false);
27
- }
28
-
29
- // 设置拖拽数据
30
- const draggedIds = selectedIds.has(itemId) ? selectedIds : new Set([itemId]);
31
- e.dataTransfer.setData('text/plain', JSON.stringify([...draggedIds]));
32
- e.dataTransfer.effectAllowed = 'move';
33
-
34
- setIsDragging(true);
35
- }, [getSelectedIds, onSelect]);
36
-
37
- /**
38
- * 拖拽经过
39
- */
40
- const handleDragOver = useCallback((e: React.DragEvent, item: FileItem) => {
41
- if (!isDragging) return;
42
-
43
- // 只有文件夹可以作为放置目标
44
- if (item.type === FileType.FOLDER) {
45
- const selectedIds = getSelectedIds();
46
- // 不能拖拽到自己身上
47
- if (!selectedIds.has(item.id)) {
48
- e.preventDefault();
49
- setDragOverId(item.id);
50
- }
51
- }
52
- }, [isDragging, getSelectedIds]);
53
-
54
- /**
55
- * 拖拽离开
56
- */
57
- const handleDragLeave = useCallback(() => {
58
- setDragOverId(null);
59
- }, []);
60
-
61
- /**
62
- * 放置
63
- */
64
- const handleDrop = useCallback((e: React.DragEvent, targetItem: FileItem) => {
65
- setDragOverId(null);
66
- setIsDragging(false);
67
-
68
- if (!e.dataTransfer || targetItem.type !== FileType.FOLDER) return;
69
-
70
- const data = e.dataTransfer.getData('text/plain');
71
- if (!data) return;
72
-
73
- try {
74
- const draggedIds: string[] = JSON.parse(data);
75
- const itemIds = new Set(draggedIds);
76
-
77
- // 不能移动到自己
78
- if (itemIds.has(targetItem.id)) return;
79
-
80
- onMove(targetItem.id, itemIds);
81
- } catch {
82
- // 忽略解析错误
83
- }
84
- }, [onMove]);
85
-
86
- /**
87
- * 拖拽结束
88
- */
89
- const handleDragEnd = useCallback(() => {
90
- setDragOverId(null);
91
- setIsDragging(false);
92
- }, []);
93
-
94
- return {
95
- dragOverId,
96
- isDragging,
97
- handleDragStart,
98
- handleDragOver,
99
- handleDragLeave,
100
- handleDrop,
101
- handleDragEnd
102
- };
103
- }
104
-
@@ -1,164 +0,0 @@
1
- import { useState, useCallback, useEffect, useRef } from 'react';
2
- import { FileType } from '../types';
3
-
4
- /**
5
- * 媒体播放器功能管理
6
- */
7
- export function useMediaPlayer(
8
- mediaType: FileType,
9
- mediaRef: React.RefObject<HTMLVideoElement | HTMLAudioElement>
10
- ) {
11
- const [isPlaying, setIsPlaying] = useState(false);
12
- const [progress, setProgress] = useState(0);
13
- const [currentTime, setCurrentTime] = useState(0);
14
- const [duration, setDuration] = useState(0);
15
- const [isMuted, setIsMuted] = useState(false);
16
- const [volume, setVolume] = useState(1);
17
- const [lastVolume, setLastVolume] = useState(1);
18
- const [showControls, setShowControls] = useState(false);
19
-
20
- const isAudio = mediaType === FileType.MUSIC;
21
-
22
- /**
23
- * 更新播放进度
24
- */
25
- const updateProgress = useCallback(() => {
26
- const media = mediaRef.current;
27
- if (media) {
28
- const curr = media.currentTime;
29
- const dur = media.duration;
30
- setCurrentTime(curr);
31
- if (dur && !isNaN(dur)) {
32
- setDuration(dur);
33
- setProgress((curr / dur) * 100);
34
- }
35
- }
36
- }, [mediaRef]);
37
-
38
- /**
39
- * 切换播放/暂停
40
- */
41
- const togglePlay = useCallback(() => {
42
- const media = mediaRef.current;
43
- if (media) {
44
- if (isPlaying) {
45
- media.pause();
46
- setIsPlaying(false);
47
- } else {
48
- media.play();
49
- setIsPlaying(true);
50
- }
51
- }
52
- }, [isPlaying, mediaRef]);
53
-
54
- /**
55
- * 切换静音
56
- */
57
- const toggleMute = useCallback(() => {
58
- const media = mediaRef.current;
59
- if (media) {
60
- if (isMuted) {
61
- const volToRestore = lastVolume || 1;
62
- media.volume = volToRestore;
63
- media.muted = false;
64
- setVolume(volToRestore);
65
- setIsMuted(false);
66
- } else {
67
- setLastVolume(volume);
68
- media.volume = 0;
69
- media.muted = true;
70
- setVolume(0);
71
- setIsMuted(true);
72
- }
73
- }
74
- }, [isMuted, lastVolume, volume, mediaRef]);
75
-
76
- /**
77
- * 音量变化
78
- */
79
- const handleVolumeChange = useCallback((val: number) => {
80
- setVolume(val);
81
- const media = mediaRef.current;
82
- if (media) {
83
- media.volume = val;
84
- if (val === 0) {
85
- setIsMuted(true);
86
- media.muted = true;
87
- } else {
88
- setIsMuted(false);
89
- media.muted = false;
90
- }
91
- }
92
- }, [mediaRef]);
93
-
94
- /**
95
- * 跳转到指定时间
96
- */
97
- const seekTo = useCallback((time: number) => {
98
- const media = mediaRef.current;
99
- if (media && duration) {
100
- media.currentTime = time;
101
- setCurrentTime(time);
102
- setProgress((time / duration) * 100);
103
- }
104
- }, [duration, mediaRef]);
105
-
106
- /**
107
- * 格式化时间显示
108
- */
109
- const formatTime = useCallback((time: number) => {
110
- if (isNaN(time)) return '0:00';
111
- const minutes = Math.floor(time / 60);
112
- const seconds = Math.floor(time % 60);
113
- return `${minutes}:${seconds.toString().padStart(2, '0')}`;
114
- }, []);
115
-
116
- /**
117
- * 自动播放
118
- */
119
- const autoPlay = useCallback(() => {
120
- const media = mediaRef.current;
121
- if ((mediaType === FileType.VIDEO || isAudio) && media) {
122
- media.volume = volume;
123
- media.play().catch(() => {});
124
- setIsPlaying(true);
125
- }
126
- }, [mediaType, isAudio, volume, mediaRef]);
127
-
128
- /**
129
- * 监听媒体事件
130
- */
131
- useEffect(() => {
132
- const media = mediaRef.current;
133
- if (media) {
134
- media.addEventListener('timeupdate', updateProgress);
135
- media.addEventListener('loadedmetadata', updateProgress);
136
- media.addEventListener('ended', () => {
137
- setIsPlaying(false);
138
- });
139
- return () => {
140
- media.removeEventListener('timeupdate', updateProgress);
141
- media.removeEventListener('loadedmetadata', updateProgress);
142
- };
143
- }
144
- }, [mediaRef, updateProgress]);
145
-
146
- return {
147
- isPlaying,
148
- progress,
149
- currentTime,
150
- duration,
151
- isMuted,
152
- volume,
153
- showControls,
154
- isAudio,
155
- togglePlay,
156
- toggleMute,
157
- handleVolumeChange,
158
- seekTo,
159
- formatTime,
160
- autoPlay,
161
- updateProgress
162
- };
163
- }
164
-
@@ -1,112 +0,0 @@
1
- import { useState, useCallback } from 'react';
2
- import type { FileItem } from '../types';
3
-
4
- /**
5
- * 文件选择状态管理
6
- */
7
- export function useSelection() {
8
- const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
9
- const [lastSelectedId, setLastSelectedId] = useState<string | null>(null);
10
- const [editingId, setEditingId] = useState<string | null>(null);
11
-
12
- /**
13
- * 清除选择
14
- */
15
- const clearSelection = useCallback(() => {
16
- setSelectedIds(new Set());
17
- setLastSelectedId(null);
18
- }, []);
19
-
20
- /**
21
- * 选择项目
22
- * @param id 项目 ID(null 表示清除选择)
23
- * @param items 当前显示的项目列表
24
- * @param multi 是否多选(Cmd/Ctrl)
25
- * @param range 是否范围选择(Shift)
26
- */
27
- const selectItem = useCallback((
28
- id: string | null,
29
- items: FileItem[],
30
- multi: boolean = false,
31
- range: boolean = false
32
- ) => {
33
- if (!id) {
34
- clearSelection();
35
- return;
36
- }
37
-
38
- setSelectedIds(prev => {
39
- setLastSelectedId(prevLast => {
40
- // 范围选择
41
- if (range && prevLast) {
42
- const indexA = items.findIndex(i => i.id === prevLast);
43
- const indexB = items.findIndex(i => i.id === id);
44
-
45
- if (indexA !== -1 && indexB !== -1) {
46
- const start = Math.min(indexA, indexB);
47
- const end = Math.max(indexA, indexB);
48
- const newSet = new Set(multi ? prev : []);
49
- for (let i = start; i <= end; i++) {
50
- const item = items[i];
51
- if (item) {
52
- newSet.add(item.id);
53
- }
54
- }
55
- setSelectedIds(newSet);
56
- return id;
57
- }
58
- }
59
-
60
- // 多选切换
61
- if (multi) {
62
- const newSet = new Set(prev);
63
- if (newSet.has(id)) {
64
- newSet.delete(id);
65
- } else {
66
- newSet.add(id);
67
- }
68
- setSelectedIds(newSet);
69
- return id;
70
- }
71
-
72
- // 单选
73
- setSelectedIds(new Set([id]));
74
- return id;
75
- });
76
- return prev;
77
- });
78
- }, [clearSelection]);
79
-
80
- /**
81
- * 全选
82
- */
83
- const selectAll = useCallback((items: FileItem[]) => {
84
- setSelectedIds(new Set(items.map(i => i.id)));
85
- }, []);
86
-
87
- /**
88
- * 设置编辑状态
89
- */
90
- const setEditing = useCallback((id: string | null) => {
91
- setEditingId(id);
92
- }, []);
93
-
94
- /**
95
- * 检查是否选中
96
- */
97
- const isSelected = useCallback((id: string): boolean => {
98
- return selectedIds.has(id);
99
- }, [selectedIds]);
100
-
101
- return {
102
- selectedIds,
103
- lastSelectedId,
104
- editingId,
105
- clearSelection,
106
- selectItem,
107
- selectAll,
108
- setEditing,
109
- isSelected
110
- };
111
- }
112
-
@@ -1,60 +0,0 @@
1
- import { useState, useCallback, useEffect } from 'react';
2
-
3
- /**
4
- * 窗口拖拽功能管理
5
- */
6
- export function useWindowDrag() {
7
- const [position, setPosition] = useState({ x: 0, y: 0 });
8
- const [isDragging, setIsDragging] = useState(false);
9
-
10
- /**
11
- * 鼠标移动处理
12
- */
13
- const handleMouseMove = useCallback((e: MouseEvent) => {
14
- if (!isDragging) return;
15
- setPosition(prev => ({
16
- x: prev.x + e.movementX,
17
- y: prev.y + e.movementY
18
- }));
19
- }, [isDragging]);
20
-
21
- /**
22
- * 鼠标释放处理
23
- */
24
- const handleMouseUp = useCallback(() => {
25
- setIsDragging(false);
26
- }, []);
27
-
28
- /**
29
- * 开始拖拽
30
- */
31
- const startDrag = useCallback((e: React.MouseEvent) => {
32
- const target = e.target as HTMLElement;
33
- // 检查是否点击在可拖拽区域,但排除按钮
34
- if (target.closest('.draggable-area') && !target.closest('button')) {
35
- e.preventDefault();
36
- setIsDragging(true);
37
- }
38
- }, []);
39
-
40
- /**
41
- * 监听拖拽状态变化
42
- */
43
- useEffect(() => {
44
- if (isDragging) {
45
- window.addEventListener('mousemove', handleMouseMove);
46
- window.addEventListener('mouseup', handleMouseUp);
47
- return () => {
48
- window.removeEventListener('mousemove', handleMouseMove);
49
- window.removeEventListener('mouseup', handleMouseUp);
50
- };
51
- }
52
- }, [isDragging, handleMouseMove, handleMouseUp]);
53
-
54
- return {
55
- position,
56
- isDragging,
57
- startDrag
58
- };
59
- }
60
-
@@ -1,126 +0,0 @@
1
- import { useState, useCallback, useEffect } from 'react';
2
-
3
- type ResizeDirection = 'n' | 's' | 'e' | 'w' | 'ne' | 'nw' | 'se' | 'sw';
4
-
5
- /**
6
- * 窗口调整大小功能管理
7
- */
8
- export function useWindowResize(
9
- initialWidth: number,
10
- initialHeight: number,
11
- minWidth: number,
12
- minHeight: number,
13
- maxWidth: number,
14
- maxHeight: number
15
- ) {
16
- const [width, setWidth] = useState(initialWidth);
17
- const [height, setHeight] = useState(initialHeight);
18
- const [isResizing, setIsResizing] = useState(false);
19
- const [resizeDirection, setResizeDirection] = useState<ResizeDirection | null>(null);
20
- const [startX, setStartX] = useState(0);
21
- const [startY, setStartY] = useState(0);
22
- const [startWidth, setStartWidth] = useState(0);
23
- const [startHeight, setStartHeight] = useState(0);
24
-
25
- /**
26
- * 鼠标移动处理
27
- */
28
- const handleMouseMove = useCallback((e: MouseEvent) => {
29
- if (!isResizing || !resizeDirection) return;
30
-
31
- const deltaX = e.clientX - startX;
32
- const deltaY = e.clientY - startY;
33
-
34
- let newWidth = startWidth;
35
- let newHeight = startHeight;
36
-
37
- const direction = resizeDirection;
38
-
39
- // 处理水平方向
40
- if (direction.includes('e')) {
41
- newWidth = Math.min(Math.max(startWidth + deltaX, minWidth), maxWidth);
42
- } else if (direction.includes('w')) {
43
- newWidth = Math.min(Math.max(startWidth - deltaX, minWidth), maxWidth);
44
- }
45
-
46
- // 处理垂直方向
47
- if (direction.includes('s')) {
48
- newHeight = Math.min(Math.max(startHeight + deltaY, minHeight), maxHeight);
49
- } else if (direction.includes('n')) {
50
- newHeight = Math.min(Math.max(startHeight - deltaY, minHeight), maxHeight);
51
- }
52
-
53
- setWidth(newWidth);
54
- setHeight(newHeight);
55
- }, [isResizing, resizeDirection, startX, startY, startWidth, startHeight, minWidth, minHeight, maxWidth, maxHeight]);
56
-
57
- /**
58
- * 鼠标释放处理
59
- */
60
- const handleMouseUp = useCallback(() => {
61
- setIsResizing(false);
62
- setResizeDirection(null);
63
- }, []);
64
-
65
- /**
66
- * 开始调整大小
67
- */
68
- const startResize = useCallback((
69
- e: React.MouseEvent,
70
- direction: ResizeDirection,
71
- currentWidth: number,
72
- currentHeight: number
73
- ) => {
74
- e.preventDefault();
75
- e.stopPropagation();
76
- setIsResizing(true);
77
- setResizeDirection(direction);
78
- setStartX(e.clientX);
79
- setStartY(e.clientY);
80
- setStartWidth(currentWidth);
81
- setStartHeight(currentHeight);
82
- }, []);
83
-
84
- /**
85
- * 获取对应方向的鼠标样式
86
- */
87
- const getCursorForDirection = useCallback((direction: ResizeDirection): string => {
88
- const cursors: Record<ResizeDirection, string> = {
89
- n: 'n-resize',
90
- s: 's-resize',
91
- e: 'e-resize',
92
- w: 'w-resize',
93
- ne: 'ne-resize',
94
- nw: 'nw-resize',
95
- se: 'se-resize',
96
- sw: 'sw-resize'
97
- };
98
- return cursors[direction];
99
- }, []);
100
-
101
- /**
102
- * 监听调整大小状态变化
103
- */
104
- useEffect(() => {
105
- if (isResizing) {
106
- window.addEventListener('mousemove', handleMouseMove);
107
- window.addEventListener('mouseup', handleMouseUp);
108
- document.body.style.cursor = getCursorForDirection(resizeDirection || 'se');
109
- document.body.style.userSelect = 'none';
110
- return () => {
111
- window.removeEventListener('mousemove', handleMouseMove);
112
- window.removeEventListener('mouseup', handleMouseUp);
113
- document.body.style.cursor = '';
114
- document.body.style.userSelect = '';
115
- };
116
- }
117
- }, [isResizing, resizeDirection, handleMouseMove, handleMouseUp, getCursorForDirection]);
118
-
119
- return {
120
- width,
121
- height,
122
- isResizing,
123
- startResize
124
- };
125
- }
126
-