@anymux/ui-kit 0.1.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.
Files changed (244) hide show
  1. package/dist/ExplorerLayout-CSIJd7N4.js +105 -0
  2. package/dist/ExplorerLayout-CSIJd7N4.js.map +1 -0
  3. package/dist/FileBrowserContext-B6jixa2j.js +11 -0
  4. package/dist/FileBrowserContext-B6jixa2j.js.map +1 -0
  5. package/dist/calendar-DSlrbHoj.js +761 -0
  6. package/dist/calendar-DSlrbHoj.js.map +1 -0
  7. package/dist/calendar.d.ts +3 -0
  8. package/dist/calendar.js +3 -0
  9. package/dist/contacts-DQXTZzHc.js +539 -0
  10. package/dist/contacts-DQXTZzHc.js.map +1 -0
  11. package/dist/contacts.d.ts +3 -0
  12. package/dist/contacts.js +3 -0
  13. package/dist/file-browser-m5atC3kF.js +6755 -0
  14. package/dist/file-browser-m5atC3kF.js.map +1 -0
  15. package/dist/file-browser.d.ts +11 -0
  16. package/dist/file-browser.js +9 -0
  17. package/dist/git-B55e6LL-.js +561 -0
  18. package/dist/git-B55e6LL-.js.map +1 -0
  19. package/dist/git.d.ts +2 -0
  20. package/dist/git.js +3 -0
  21. package/dist/iconMap-V4B8P-Uh.js +206 -0
  22. package/dist/iconMap-V4B8P-Uh.js.map +1 -0
  23. package/dist/icons-CIsIOZXR.js +0 -0
  24. package/dist/icons.d.ts +2 -0
  25. package/dist/icons.js +4 -0
  26. package/dist/index-BNmNIWBL.d.ts +71 -0
  27. package/dist/index-BNmNIWBL.d.ts.map +1 -0
  28. package/dist/index-Bryv_GCG.d.ts +1481 -0
  29. package/dist/index-Bryv_GCG.d.ts.map +1 -0
  30. package/dist/index-CuQIjSXs.d.ts +134 -0
  31. package/dist/index-CuQIjSXs.d.ts.map +1 -0
  32. package/dist/index-DSu19mq0.d.ts +153 -0
  33. package/dist/index-DSu19mq0.d.ts.map +1 -0
  34. package/dist/index-DmsyeHFr.d.ts +149 -0
  35. package/dist/index-DmsyeHFr.d.ts.map +1 -0
  36. package/dist/index-DxnJ8FYM.d.ts +17 -0
  37. package/dist/index-DxnJ8FYM.d.ts.map +1 -0
  38. package/dist/index-DzfY1Tok.d.ts +32 -0
  39. package/dist/index-DzfY1Tok.d.ts.map +1 -0
  40. package/dist/index-Ml_SgiKa.d.ts +1847 -0
  41. package/dist/index-Ml_SgiKa.d.ts.map +1 -0
  42. package/dist/index-kHr9udZD.d.ts +1025 -0
  43. package/dist/index-kHr9udZD.d.ts.map +1 -0
  44. package/dist/index.d.ts +11 -0
  45. package/dist/index.js +15 -0
  46. package/dist/layout-Ca_4r8ka.js +89 -0
  47. package/dist/layout-Ca_4r8ka.js.map +1 -0
  48. package/dist/layout.d.ts +2 -0
  49. package/dist/layout.js +5 -0
  50. package/dist/list-CxfT6hix.js +6831 -0
  51. package/dist/list-CxfT6hix.js.map +1 -0
  52. package/dist/list.d.ts +2 -0
  53. package/dist/list.js +5 -0
  54. package/dist/media-DZ292aKK.js +557 -0
  55. package/dist/media-DZ292aKK.js.map +1 -0
  56. package/dist/media.d.ts +3 -0
  57. package/dist/media.js +3 -0
  58. package/dist/tree-Dd9Z0Aso.js +3351 -0
  59. package/dist/tree-Dd9Z0Aso.js.map +1 -0
  60. package/dist/tree.d.ts +2 -0
  61. package/dist/tree.js +6 -0
  62. package/dist/types-common-CB3kRek8.d.ts +26 -0
  63. package/dist/types-common-CB3kRek8.d.ts.map +1 -0
  64. package/dist/utils-B4fdKKsy.js +3 -0
  65. package/package.json +109 -0
  66. package/src/calendar/AgendaView.tsx +37 -0
  67. package/src/calendar/CalendarBrowser.tsx +90 -0
  68. package/src/calendar/CalendarModel.ts +142 -0
  69. package/src/calendar/CalendarSidebar.tsx +81 -0
  70. package/src/calendar/DayView.tsx +76 -0
  71. package/src/calendar/EventCard.tsx +51 -0
  72. package/src/calendar/MockCalendarProvider.ts +98 -0
  73. package/src/calendar/MonthView.tsx +77 -0
  74. package/src/calendar/WeekView.tsx +129 -0
  75. package/src/calendar/index.ts +18 -0
  76. package/src/calendar/types.ts +25 -0
  77. package/src/contacts/ContactAvatar.tsx +35 -0
  78. package/src/contacts/ContactBrowser.tsx +56 -0
  79. package/src/contacts/ContactCard.tsx +37 -0
  80. package/src/contacts/ContactDetail.tsx +63 -0
  81. package/src/contacts/ContactGroupSidebar.tsx +40 -0
  82. package/src/contacts/ContactList.tsx +32 -0
  83. package/src/contacts/ContactListModel.ts +120 -0
  84. package/src/contacts/MockContactProvider.ts +77 -0
  85. package/src/contacts/index.ts +17 -0
  86. package/src/contacts/types.ts +26 -0
  87. package/src/demos/CalendarBrowserDemo.tsx +15 -0
  88. package/src/demos/ContactBrowserDemo.tsx +15 -0
  89. package/src/demos/MediaBrowserDemo.tsx +15 -0
  90. package/src/file-browser/adapters/DocumentViewerAdapter.ts +371 -0
  91. package/src/file-browser/adapters/FileSystemBridge.ts +168 -0
  92. package/src/file-browser/adapters/GitBrowserAdapter.ts +546 -0
  93. package/src/file-browser/adapters/README.md +504 -0
  94. package/src/file-browser/adapters/index.ts +27 -0
  95. package/src/file-browser/adapters/types.ts +70 -0
  96. package/src/file-browser/architecture.md +645 -0
  97. package/src/file-browser/components/CreateItemDialog.tsx +71 -0
  98. package/src/file-browser/components/DeleteConfirmDialog.tsx +58 -0
  99. package/src/file-browser/components/FileBrowser.tsx +473 -0
  100. package/src/file-browser/components/FileBrowserContent.tsx +209 -0
  101. package/src/file-browser/components/FileBrowserHeader.tsx +151 -0
  102. package/src/file-browser/components/FileBrowserToolbar.tsx +145 -0
  103. package/src/file-browser/components/LeftPanel/LeftPanel.tsx +103 -0
  104. package/src/file-browser/components/LeftPanel/LeftPanelTabs.tsx +70 -0
  105. package/src/file-browser/components/LeftPanel/TreeNavigationView.tsx +256 -0
  106. package/src/file-browser/components/PreviewPane.tsx +146 -0
  107. package/src/file-browser/components/RightPanel/FilePreview.tsx +219 -0
  108. package/src/file-browser/components/RightPanel/RightPanel.tsx +186 -0
  109. package/src/file-browser/components/RightPanel/RightPanelToolbar.tsx +113 -0
  110. package/src/file-browser/components/UploadProgress.tsx +123 -0
  111. package/src/file-browser/components/ViewerHost.tsx +208 -0
  112. package/src/file-browser/components/mobile/MobileNavigation.tsx +227 -0
  113. package/src/file-browser/components/navigation/NavigationButtons.tsx +171 -0
  114. package/src/file-browser/components/shared/ErrorBoundary.tsx +116 -0
  115. package/src/file-browser/components/shared/FileBrowserItem.tsx +195 -0
  116. package/src/file-browser/components/shared/FileIcon.tsx +169 -0
  117. package/src/file-browser/components/toolbar/ViewModeToggle.tsx +200 -0
  118. package/src/file-browser/components/views/ListView/ListView.tsx +484 -0
  119. package/src/file-browser/components/views/ThumbnailView/ThumbnailView.tsx +323 -0
  120. package/src/file-browser/components/views/TreeView/TreeNode.tsx +186 -0
  121. package/src/file-browser/components/views/TreeView/TreeNodeList.tsx +191 -0
  122. package/src/file-browser/components/views/TreeView/TreeView.tsx +200 -0
  123. package/src/file-browser/components/views/TreemapView/TreemapView.tsx +339 -0
  124. package/src/file-browser/context/FileBrowserContext.tsx +13 -0
  125. package/src/file-browser/examples/BasicUsage.tsx +20 -0
  126. package/src/file-browser/index.ts +98 -0
  127. package/src/file-browser/models/FileBrowserModel.ts +623 -0
  128. package/src/file-browser/models/LeftPanelManagerModel.ts +105 -0
  129. package/src/file-browser/models/NavigationManagerModel.ts +312 -0
  130. package/src/file-browser/models/ResponsiveLayoutManagerModel.ts +437 -0
  131. package/src/file-browser/models/RightPanelManagerModel.ts +190 -0
  132. package/src/file-browser/models/SelectionManagerModel.ts +252 -0
  133. package/src/file-browser/models/ToolbarManagerModel.ts +144 -0
  134. package/src/file-browser/models/UploadModel.ts +147 -0
  135. package/src/file-browser/models/ViewModeManagerModel.ts +185 -0
  136. package/src/file-browser/models/ViewerHostModel.ts +44 -0
  137. package/src/file-browser/models/ui/ListViewUIModel.ts +265 -0
  138. package/src/file-browser/models/ui/PreviewUIModel.ts +297 -0
  139. package/src/file-browser/models/ui/ThumbnailViewUIModel.ts +254 -0
  140. package/src/file-browser/models/ui/TreeViewUIModel.ts +128 -0
  141. package/src/file-browser/models/ui/TreemapViewUIModel.ts +350 -0
  142. package/src/file-browser/providers/FileSystemListProvider.ts +552 -0
  143. package/src/file-browser/providers/FileSystemProvider.ts +401 -0
  144. package/src/file-browser/providers/FileSystemTreeProvider.ts +231 -0
  145. package/src/file-browser/providers/GitProvider.ts +337 -0
  146. package/src/file-browser/providers/GitRepositoryProvider.ts +376 -0
  147. package/src/file-browser/providers/IFileBrowserProvider.ts +56 -0
  148. package/src/file-browser/providers/MemoryProvider.ts +303 -0
  149. package/src/file-browser/providers/index.ts +4 -0
  150. package/src/file-browser/registry/ViewerRegistry.ts +551 -0
  151. package/src/file-browser/registry/types.ts +144 -0
  152. package/src/file-browser/scripts/performanceBenchmark.ts +553 -0
  153. package/src/file-browser/services/ThumbnailCacheService.ts +128 -0
  154. package/src/file-browser/tasks.md +537 -0
  155. package/src/file-browser/types/FileBrowserTypes.ts +126 -0
  156. package/src/file-browser/types/ProviderTypes.ts +155 -0
  157. package/src/file-browser/types/UITypes.ts +235 -0
  158. package/src/file-browser/types/ViewModeTypes.ts +150 -0
  159. package/src/file-browser/utils/gestures.ts +327 -0
  160. package/src/file-browser/utils/performance.ts +563 -0
  161. package/src/file-browser/viewers/ImageViewer.tsx +163 -0
  162. package/src/file-browser/viewers/ImageViewerModel.ts +79 -0
  163. package/src/file-browser/viewers/TextViewer.tsx +95 -0
  164. package/src/file-browser/viewers/UnsupportedFileViewer.tsx +57 -0
  165. package/src/file-browser/viewers/index.ts +61 -0
  166. package/src/git/BranchList.tsx +128 -0
  167. package/src/git/CommitGraph.tsx +239 -0
  168. package/src/git/CommitList.tsx +258 -0
  169. package/src/git/DiffViewer.tsx +219 -0
  170. package/src/git/index.ts +4 -0
  171. package/src/icons/iconMap.ts +146 -0
  172. package/src/icons/index.ts +9 -0
  173. package/src/index.ts +13 -0
  174. package/src/layout/README.md +307 -0
  175. package/src/layout/components/ExplorerLayout/ExplorerLayout.tsx +178 -0
  176. package/src/layout/examples/SimpleExample.tsx +60 -0
  177. package/src/layout/index.ts +6 -0
  178. package/src/lib/utils.ts +1 -0
  179. package/src/list/README.md +303 -0
  180. package/src/list/architecture.md +807 -0
  181. package/src/list/components/CalculatedGridView.tsx +252 -0
  182. package/src/list/components/DragPreview.tsx +102 -0
  183. package/src/list/components/ListContextMenu.tsx +274 -0
  184. package/src/list/components/ListItem.tsx +761 -0
  185. package/src/list/components/ListItems.tsx +919 -0
  186. package/src/list/components/MasonryView.tsx +241 -0
  187. package/src/list/components/SearchFilter.tsx +44 -0
  188. package/src/list/components/TreemapView.tsx +709 -0
  189. package/src/list/components/ViewSizeControls.tsx +205 -0
  190. package/src/list/components/ViewTypeSelector.tsx +312 -0
  191. package/src/list/components/VirtualizedDetailsView.tsx +231 -0
  192. package/src/list/components/VirtualizedGrid.tsx +164 -0
  193. package/src/list/components/VirtualizedList.tsx +154 -0
  194. package/src/list/components/VirtualizedMasonryView.tsx +344 -0
  195. package/src/list/components/shared/EmptyState.tsx +103 -0
  196. package/src/list/components/shared/ErrorBoundary.tsx +123 -0
  197. package/src/list/components/shared/ErrorDisplay.tsx +100 -0
  198. package/src/list/components/shared/ListLoader.tsx +146 -0
  199. package/src/list/components/shared/LoadingIndicator.tsx +80 -0
  200. package/src/list/index.ts +92 -0
  201. package/src/list/models/ListItemsModel.ts +1301 -0
  202. package/src/list/models/TreemapModel.ts +204 -0
  203. package/src/list/providers/ListItemsProvider.ts +313 -0
  204. package/src/list/providers/TestListProvider.ts +604 -0
  205. package/src/list/tasks.md +937 -0
  206. package/src/list/types/ListTypes.ts +178 -0
  207. package/src/list/utils/BenchmarkLogger.ts +243 -0
  208. package/src/list/utils/DragDropManager.ts +320 -0
  209. package/src/list/utils/GridLayoutCalculator.ts +290 -0
  210. package/src/list/utils/ListAccessibility.ts +367 -0
  211. package/src/list/utils/ListKeyboard.ts +414 -0
  212. package/src/list/utils/MasonryLayoutCalculator.ts +302 -0
  213. package/src/list/utils/MasonryLayoutEngine.ts +401 -0
  214. package/src/list/utils/__tests__/MasonryLayoutEngine.test.ts +157 -0
  215. package/src/list/utils/__tests__/VirtualizedMasonryView.test.tsx +251 -0
  216. package/src/media/AlbumSidebar.tsx +48 -0
  217. package/src/media/MediaBrowser.tsx +92 -0
  218. package/src/media/MediaBrowserModel.ts +138 -0
  219. package/src/media/MediaGrid.tsx +50 -0
  220. package/src/media/MediaList.tsx +49 -0
  221. package/src/media/MediaPreview.tsx +63 -0
  222. package/src/media/MediaTimeline.tsx +38 -0
  223. package/src/media/MockMediaProvider.ts +70 -0
  224. package/src/media/index.ts +18 -0
  225. package/src/media/types.ts +21 -0
  226. package/src/styles/variables.css +60 -0
  227. package/src/tree/DEVELOPMENT_SUMMARY.md +170 -0
  228. package/src/tree/__tests__/TreeModel.test.ts +16 -0
  229. package/src/tree/architecture.md +530 -0
  230. package/src/tree/components/Tree.tsx +283 -0
  231. package/src/tree/components/TreeCheckbox.tsx +147 -0
  232. package/src/tree/components/TreeContextMenu.tsx +139 -0
  233. package/src/tree/components/TreeNodeList.tsx +329 -0
  234. package/src/tree/components/TreeTable.tsx +382 -0
  235. package/src/tree/index.ts +58 -0
  236. package/src/tree/models/TreeModel.ts +839 -0
  237. package/src/tree/providers/SimpleTreeProvider.ts +463 -0
  238. package/src/tree/providers/TestTreeProvider.ts +946 -0
  239. package/src/tree/providers/TreeProvider.ts +308 -0
  240. package/src/tree/tasks.md +2046 -0
  241. package/src/tree/types/TreeTypes.ts +279 -0
  242. package/src/tree/utils/SelectionTheme.ts +150 -0
  243. package/src/tree/utils/logger.ts +203 -0
  244. package/src/types-common.ts +21 -0
@@ -0,0 +1,200 @@
1
+ import React, { useEffect } from 'react';
2
+ import { observer } from 'mobx-react-lite';
3
+ import { cn } from '../../../../lib/utils';
4
+ import { FileBrowserItem } from '../../../types/FileBrowserTypes';
5
+ import { TreeViewUIModel } from '../../../models/ui/TreeViewUIModel';
6
+ import TreeNodeList from './TreeNodeList';
7
+
8
+ export interface TreeViewProps {
9
+ items: FileBrowserItem[];
10
+ treeModel: TreeViewUIModel;
11
+ onItemClick?: (item: FileBrowserItem) => void;
12
+ onItemDoubleClick?: (item: FileBrowserItem) => void;
13
+ onItemActivate?: (item: FileBrowserItem) => void;
14
+ onSelectionChange?: (selectedItems: FileBrowserItem[]) => void;
15
+ onPathChange?: (path: string) => void;
16
+ selectedItemIds?: Set<string>;
17
+ focusedItemId?: string;
18
+ className?: string;
19
+ getItemIcon?: (item: FileBrowserItem) => any;
20
+ loadFolder?: (path: string) => Promise<FileBrowserItem[]>;
21
+ virtualization?: boolean;
22
+ maxHeight?: number;
23
+ }
24
+
25
+ const TreeView: React.FC<TreeViewProps> = observer(({
26
+ items,
27
+ treeModel,
28
+ onItemClick,
29
+ onItemDoubleClick,
30
+ onItemActivate,
31
+ onSelectionChange,
32
+ onPathChange,
33
+ selectedItemIds = new Set<string>(),
34
+ focusedItemId,
35
+ className,
36
+ getItemIcon,
37
+ loadFolder,
38
+ virtualization = false,
39
+ maxHeight,
40
+ }) => {
41
+ // Build hierarchical tree structure from flat items array
42
+ const buildTreeStructure = (items: FileBrowserItem[]): FileBrowserItem[] => {
43
+ const itemMap = new Map<string, FileBrowserItem>();
44
+ const rootItems: FileBrowserItem[] = [];
45
+
46
+ // First pass: create map of all items
47
+ items.forEach(item => {
48
+ itemMap.set(item.path, { ...item, children: [] });
49
+ });
50
+
51
+ // Second pass: build hierarchy
52
+ items.forEach(item => {
53
+ const itemCopy = itemMap.get(item.path);
54
+ if (!itemCopy) return;
55
+
56
+ // Find parent path
57
+ const pathParts = item.path.split('/').filter(Boolean);
58
+ if (pathParts.length === 1) {
59
+ // Root level item
60
+ rootItems.push(itemCopy);
61
+ } else {
62
+ // Find parent
63
+ const parentPath = '/' + pathParts.slice(0, -1).join('/');
64
+ const parent = itemMap.get(parentPath);
65
+ if (parent && parent.children) {
66
+ parent.children.push(itemCopy);
67
+ } else {
68
+ // Parent not found, treat as root item
69
+ rootItems.push(itemCopy);
70
+ }
71
+ }
72
+ });
73
+
74
+ return rootItems;
75
+ };
76
+
77
+ const treeItems = React.useMemo(() => {
78
+ return buildTreeStructure(items);
79
+ }, [items]);
80
+
81
+ const handleItemClick = (item: FileBrowserItem) => {
82
+ onItemClick?.(item);
83
+
84
+ // If it's a directory, handle expansion based on settings
85
+ if (item.type === 'directory') {
86
+ if (treeModel.expandOnSingleClick) {
87
+ treeModel.toggleFolder(item.path);
88
+
89
+ // Load folder contents if needed
90
+ if (!treeModel.isFolderExpanded(item.path) && loadFolder) {
91
+ handleFolderLoad(item.path);
92
+ }
93
+ }
94
+ }
95
+ };
96
+
97
+ const handleItemDoubleClick = (item: FileBrowserItem) => {
98
+ onItemDoubleClick?.(item);
99
+ onItemActivate?.(item);
100
+
101
+ // Handle directory navigation or file activation
102
+ if (item.type === 'directory') {
103
+ // Navigate to directory or toggle expansion
104
+ if (treeModel.collapseOnSecondClick && treeModel.isFolderExpanded(item.path)) {
105
+ treeModel.collapseFolder(item.path);
106
+ } else {
107
+ onPathChange?.(item.path);
108
+ }
109
+ } else {
110
+ // File activation
111
+ onItemActivate?.(item);
112
+ }
113
+ };
114
+
115
+ const handleFolderLoad = async (folderPath: string) => {
116
+ if (!loadFolder) return;
117
+
118
+ try {
119
+ treeModel.logger?.info(`Loading folder: ${folderPath}`);
120
+ const folderItems = await loadFolder(folderPath);
121
+
122
+ // Update the item in the tree to include children
123
+ // This would need integration with the parent model
124
+ treeModel.logger?.info(`Loaded ${folderItems.length} items for ${folderPath}`);
125
+ } catch (error) {
126
+ treeModel.logger?.error(`Failed to load folder ${folderPath}:`, error);
127
+ }
128
+ };
129
+
130
+ // Handle folder expansion when state changes
131
+ useEffect(() => {
132
+ const expandedFolders = treeModel.expandedFoldersArray;
133
+
134
+ // Load contents for newly expanded folders
135
+ expandedFolders.forEach((folderPath: string) => {
136
+ // Check if folder needs loading (doesn't have children but should)
137
+ const item = items.find(item => item.path === folderPath);
138
+ if (item &&
139
+ item.type === 'directory' &&
140
+ (!item.children || item.children.length === 0) &&
141
+ item.hasChildren &&
142
+ loadFolder) {
143
+ handleFolderLoad(folderPath);
144
+ }
145
+ });
146
+ }, [items, loadFolder]);
147
+
148
+ // Keyboard shortcuts
149
+ const handleKeyDown = (e: React.KeyboardEvent) => {
150
+ switch (e.key) {
151
+ case 'a':
152
+ if (e.ctrlKey || e.metaKey) {
153
+ e.preventDefault();
154
+ treeModel.expandAll();
155
+ }
156
+ break;
157
+ case 'c':
158
+ if (e.ctrlKey || e.metaKey) {
159
+ e.preventDefault();
160
+ treeModel.collapseAll();
161
+ }
162
+ break;
163
+ }
164
+ };
165
+
166
+ if (!treeItems || treeItems.length === 0) {
167
+ return (
168
+ <div className={cn('p-4 text-center text-muted-foreground', className)}>
169
+ <p className="text-sm">No folders or files to display</p>
170
+ </div>
171
+ );
172
+ }
173
+
174
+ return (
175
+ <div
176
+ className={cn('tree-view h-full overflow-hidden', className)}
177
+ onKeyDown={handleKeyDown}
178
+ role="tree"
179
+ aria-label="File tree view"
180
+ >
181
+ <TreeNodeList
182
+ items={treeItems}
183
+ treeModel={treeModel}
184
+ onItemClick={handleItemClick}
185
+ onItemDoubleClick={handleItemDoubleClick}
186
+ onSelectionChange={onSelectionChange}
187
+ selectedItemIds={selectedItemIds}
188
+ focusedItemId={focusedItemId}
189
+ getItemIcon={getItemIcon}
190
+ virtualization={virtualization}
191
+ maxHeight={maxHeight}
192
+ className="h-full"
193
+ />
194
+ </div>
195
+ );
196
+ });
197
+
198
+ TreeView.displayName = 'TreeView';
199
+
200
+ export default TreeView;
@@ -0,0 +1,339 @@
1
+ import React, { useRef, useEffect, useState } from 'react';
2
+ import { observer } from 'mobx-react-lite';
3
+ import { cn } from '../../../../lib/utils';
4
+ import { FileBrowserItem } from '../../../types/FileBrowserTypes';
5
+ import { TreemapViewUIModel, TreemapNode } from '../../../models/ui/TreemapViewUIModel';
6
+ import { EmptyState } from '@anymux/ui/components/empty-state';
7
+
8
+ export interface TreemapViewProps {
9
+ items: FileBrowserItem[];
10
+ treemapModel: TreemapViewUIModel;
11
+ onItemClick?: (item: FileBrowserItem) => void;
12
+ onItemDoubleClick?: (item: FileBrowserItem) => void;
13
+ onItemActivate?: (item: FileBrowserItem) => void;
14
+ onSelectionChange?: (selectedItems: FileBrowserItem[]) => void;
15
+ selectedItemIds?: Set<string>;
16
+ focusedItemId?: string;
17
+ className?: string;
18
+ maxHeight?: number;
19
+ }
20
+
21
+ const TreemapView: React.FC<TreemapViewProps> = observer(({
22
+ items,
23
+ treemapModel,
24
+ onItemClick,
25
+ onItemDoubleClick,
26
+ onItemActivate,
27
+ onSelectionChange,
28
+ selectedItemIds = new Set<string>(),
29
+ focusedItemId,
30
+ className,
31
+ maxHeight,
32
+ }) => {
33
+ const containerRef = useRef<HTMLDivElement>(null);
34
+ const [containerSize, setContainerSize] = useState({ width: 0, height: 0 });
35
+ const [treemapNodes, setTreemapNodes] = useState<TreemapNode[]>([]);
36
+
37
+ // Handle container resize
38
+ useEffect(() => {
39
+ const updateContainerSize = () => {
40
+ if (containerRef.current) {
41
+ const rect = containerRef.current.getBoundingClientRect();
42
+ setContainerSize({
43
+ width: rect.width,
44
+ height: rect.height || 400, // Default height
45
+ });
46
+ }
47
+ };
48
+
49
+ updateContainerSize();
50
+
51
+ const resizeObserver = new ResizeObserver(updateContainerSize);
52
+ if (containerRef.current) {
53
+ resizeObserver.observe(containerRef.current);
54
+ }
55
+
56
+ return () => {
57
+ resizeObserver.disconnect();
58
+ };
59
+ }, []);
60
+
61
+ // Calculate treemap layout when items or container size changes
62
+ useEffect(() => {
63
+ if (containerSize.width > 0 && containerSize.height > 0 && items.length > 0) {
64
+ const nodes = treemapModel.calculateTreemapLayout(
65
+ items,
66
+ containerSize.width,
67
+ containerSize.height
68
+ );
69
+ setTreemapNodes(nodes);
70
+ }
71
+ }, [items, containerSize, treemapModel]);
72
+
73
+ const handleItemClick = (item: FileBrowserItem, event: React.MouseEvent) => {
74
+ event.stopPropagation();
75
+ onItemClick?.(item);
76
+ };
77
+
78
+ const handleItemDoubleClick = (item: FileBrowserItem, event: React.MouseEvent) => {
79
+ event.stopPropagation();
80
+ onItemDoubleClick?.(item);
81
+ onItemActivate?.(item);
82
+
83
+ // Handle drill-down for directories
84
+ if (item.type === 'directory' && treemapModel.enableDrillDown) {
85
+ treemapModel.drillDown(item.id);
86
+ }
87
+ };
88
+
89
+ const handleNodeClick = (node: TreemapNode, event: React.MouseEvent) => {
90
+ const item = items.find(item => item.id === node.id);
91
+ if (item) {
92
+ handleItemClick(item, event);
93
+ }
94
+ };
95
+
96
+ const handleNodeDoubleClick = (node: TreemapNode, event: React.MouseEvent) => {
97
+ const item = items.find(item => item.id === node.id);
98
+ if (item) {
99
+ handleItemDoubleClick(item, event);
100
+ }
101
+ };
102
+
103
+ const renderTreemapNode = (node: TreemapNode) => {
104
+ const isSelected = selectedItemIds.has(node.id);
105
+ const isFocused = focusedItemId === node.id;
106
+ const item = items.find(item => item.id === node.id);
107
+
108
+ if (!item || node.width < treemapModel.effectiveMinTileSize || node.height < treemapModel.effectiveMinTileSize) {
109
+ return null;
110
+ }
111
+
112
+ const showLabel = treemapModel.showLabels &&
113
+ node.width > 60 &&
114
+ node.height > 30;
115
+
116
+ const showSize = treemapModel.showSizes &&
117
+ node.width > 80 &&
118
+ node.height > 50;
119
+
120
+ return (
121
+ <div
122
+ key={node.id}
123
+ className={cn(
124
+ 'absolute cursor-pointer transition-all duration-200',
125
+ 'border border-border/50 hover:border-border',
126
+ 'flex flex-col items-center justify-center text-center',
127
+ isSelected && 'ring-2 ring-primary ring-offset-1',
128
+ isFocused && 'ring-2 ring-offset-2 ring-primary'
129
+ )}
130
+ style={{
131
+ left: node.x,
132
+ top: node.y,
133
+ width: node.width,
134
+ height: node.height,
135
+ backgroundColor: node.color || '#e5e7eb',
136
+ borderWidth: treemapModel.borderWidth,
137
+ padding: treemapModel.padding,
138
+ }}
139
+ onClick={(e) => handleNodeClick(node, e)}
140
+ onDoubleClick={(e) => handleNodeDoubleClick(node, e)}
141
+ tabIndex={0}
142
+ role="button"
143
+ aria-label={`${node.type === 'directory' ? 'Folder' : 'File'}: ${node.name}`}
144
+ aria-selected={isSelected}
145
+ title={`${node.name}${showSize ? ` (${formatFileSize(node.size)})` : ''}`}
146
+ >
147
+ {/* Label */}
148
+ {showLabel && (
149
+ <div className="text-xs font-medium text-foreground/80 truncate px-1 max-w-full">
150
+ {node.name}
151
+ </div>
152
+ )}
153
+
154
+ {/* Size */}
155
+ {showSize && (
156
+ <div className="text-xs text-foreground/60 mt-1">
157
+ {formatFileSize(node.size)}
158
+ </div>
159
+ )}
160
+
161
+ {/* Directory indicator */}
162
+ {node.type === 'directory' && node.width > 40 && node.height > 40 && (
163
+ <div className="absolute top-1 right-1 w-3 h-3 bg-primary/60 rounded-full" />
164
+ )}
165
+
166
+ {/* Selection overlay */}
167
+ {isSelected && (
168
+ <div className="absolute inset-0 bg-primary/20 pointer-events-none" />
169
+ )}
170
+ </div>
171
+ );
172
+ };
173
+
174
+ const formatFileSize = (bytes: number): string => {
175
+ if (bytes === 0) return '0 B';
176
+
177
+ const units = ['B', 'KB', 'MB', 'GB', 'TB'];
178
+ let size = bytes;
179
+ let unitIndex = 0;
180
+
181
+ while (size >= 1024 && unitIndex < units.length - 1) {
182
+ size /= 1024;
183
+ unitIndex++;
184
+ }
185
+
186
+ return `${size.toFixed(unitIndex === 0 ? 0 : 1)} ${units[unitIndex]}`;
187
+ };
188
+
189
+ const handleContainerClick = (event: React.MouseEvent) => {
190
+ // Clear selection when clicking on empty space
191
+ if (event.target === event.currentTarget) {
192
+ onSelectionChange?.([]);
193
+ }
194
+ };
195
+
196
+ const handleKeyDown = (event: React.KeyboardEvent) => {
197
+ switch (event.key) {
198
+ case 'Escape':
199
+ if (treemapModel.currentRoot) {
200
+ treemapModel.drillUp();
201
+ }
202
+ break;
203
+ case 'r':
204
+ if (event.ctrlKey || event.metaKey) {
205
+ event.preventDefault();
206
+ treemapModel.resetView();
207
+ }
208
+ break;
209
+ case '+':
210
+ case '=':
211
+ if (treemapModel.enableZoom) {
212
+ event.preventDefault();
213
+ treemapModel.setZoomLevel(treemapModel.zoomLevel * 1.2);
214
+ }
215
+ break;
216
+ case '-':
217
+ if (treemapModel.enableZoom) {
218
+ event.preventDefault();
219
+ treemapModel.setZoomLevel(treemapModel.zoomLevel / 1.2);
220
+ }
221
+ break;
222
+ }
223
+ };
224
+
225
+ if (!items || items.length === 0) {
226
+ return (
227
+ <div className={cn('h-full', className)}>
228
+ <EmptyState
229
+ preset="empty-folder"
230
+ className="h-full"
231
+ />
232
+ </div>
233
+ );
234
+ }
235
+
236
+ return (
237
+ <div
238
+ ref={containerRef}
239
+ className={cn('treemap-view h-full relative overflow-hidden', className)}
240
+ onClick={handleContainerClick}
241
+ onKeyDown={handleKeyDown}
242
+ tabIndex={0}
243
+ role="application"
244
+ aria-label="Treemap view"
245
+ style={{
246
+ ...(maxHeight && { maxHeight }),
247
+ transform: `scale(${treemapModel.zoomLevel})`,
248
+ transformOrigin: 'top left',
249
+ }}
250
+ >
251
+ {/* Treemap nodes */}
252
+ {treemapNodes.map(renderTreemapNode)}
253
+
254
+ {/* Controls overlay */}
255
+ <div className="absolute top-2 right-2 flex gap-2 bg-background/80 backdrop-blur-sm rounded-md p-2">
256
+ {/* Zoom controls */}
257
+ {treemapModel.enableZoom && (
258
+ <div className="flex gap-1">
259
+ <button
260
+ onClick={() => treemapModel.setZoomLevel(treemapModel.zoomLevel * 1.2)}
261
+ className="w-6 h-6 flex items-center justify-center text-xs bg-primary text-primary-foreground rounded hover:bg-primary/90"
262
+ title="Zoom in (+)"
263
+ >
264
+ +
265
+ </button>
266
+ <button
267
+ onClick={() => treemapModel.setZoomLevel(treemapModel.zoomLevel / 1.2)}
268
+ className="w-6 h-6 flex items-center justify-center text-xs bg-primary text-primary-foreground rounded hover:bg-primary/90"
269
+ title="Zoom out (-)"
270
+ >
271
+ -
272
+ </button>
273
+ </div>
274
+ )}
275
+
276
+ {/* Reset button */}
277
+ <button
278
+ onClick={() => treemapModel.resetView()}
279
+ className="px-2 py-1 text-xs bg-secondary text-secondary-foreground rounded hover:bg-secondary/90"
280
+ title="Reset view (Ctrl+R)"
281
+ >
282
+ Reset
283
+ </button>
284
+
285
+ {/* Drill up button */}
286
+ {treemapModel.currentRoot && (
287
+ <button
288
+ onClick={() => treemapModel.drillUp()}
289
+ className="px-2 py-1 text-xs bg-secondary text-secondary-foreground rounded hover:bg-secondary/90"
290
+ title="Go back (Esc)"
291
+ >
292
+ Back
293
+ </button>
294
+ )}
295
+ </div>
296
+
297
+ {/* Color scheme legend */}
298
+ <div className="absolute bottom-2 left-2 bg-background/80 backdrop-blur-sm rounded-md p-2">
299
+ <div className="text-xs font-medium text-foreground mb-1">
300
+ Color: {treemapModel.colorScheme}
301
+ </div>
302
+ <div className="flex gap-2 text-xs text-muted-foreground">
303
+ {treemapModel.colorScheme === 'type' && (
304
+ <>
305
+ <div className="flex items-center gap-1">
306
+ <div className="w-3 h-3 bg-blue-500 rounded" />
307
+ <span>Folders</span>
308
+ </div>
309
+ <div className="flex items-center gap-1">
310
+ <div className="w-3 h-3 bg-green-500 rounded" />
311
+ <span>Files</span>
312
+ </div>
313
+ </>
314
+ )}
315
+ {treemapModel.colorScheme === 'size' && (
316
+ <>
317
+ <div className="flex items-center gap-1">
318
+ <div className="w-3 h-3 bg-green-400 rounded" />
319
+ <span>Small</span>
320
+ </div>
321
+ <div className="flex items-center gap-1">
322
+ <div className="w-3 h-3 bg-yellow-500 rounded" />
323
+ <span>Medium</span>
324
+ </div>
325
+ <div className="flex items-center gap-1">
326
+ <div className="w-3 h-3 bg-red-500 rounded" />
327
+ <span>Large</span>
328
+ </div>
329
+ </>
330
+ )}
331
+ </div>
332
+ </div>
333
+ </div>
334
+ );
335
+ });
336
+
337
+ TreemapView.displayName = 'TreemapView';
338
+
339
+ export default TreemapView;
@@ -0,0 +1,13 @@
1
+ import { createContext, useContext } from 'react';
2
+
3
+ export interface FileBrowserContextValue {
4
+ renameState: { itemId: string; currentName: string; source: 'list' | 'tree' } | null;
5
+ onRenameCommit: (newName: string) => void;
6
+ onRenameCancel: () => void;
7
+ }
8
+
9
+ export const FileBrowserContext = createContext<FileBrowserContextValue | null>(null);
10
+
11
+ export function useFileBrowserContext() {
12
+ return useContext(FileBrowserContext);
13
+ }
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ import { FileBrowser } from '../components/FileBrowser';
3
+ import { IFileSystem } from '@anymux/file-system';
4
+
5
+ interface BasicUsageProps {
6
+ fileSystem: IFileSystem;
7
+ }
8
+
9
+ export const BasicUsage: React.FC<BasicUsageProps> = ({ fileSystem }) => {
10
+ return (
11
+ <div className="w-full h-96 border border-gray-300 rounded-lg">
12
+ <FileBrowser
13
+ fileSystem={fileSystem}
14
+ initialPath="/"
15
+ showBreadcrumbs
16
+ showNavigation
17
+ />
18
+ </div>
19
+ );
20
+ };
@@ -0,0 +1,98 @@
1
+ // Main component
2
+ export { FileBrowser } from './components/FileBrowser';
3
+
4
+ // Shared components
5
+ export { default as FileBrowserItemComponent } from './components/shared/FileBrowserItem';
6
+ export { ErrorBoundary, type ErrorBoundaryProps } from './components/shared/ErrorBoundary';
7
+ export { default as FileIcon } from './components/shared/FileIcon';
8
+
9
+ // Types
10
+ export type {
11
+ FileBrowserProps,
12
+ FileBrowserItem,
13
+ FileBrowserItemDetails,
14
+ FileBrowserEventHandlers,
15
+ LoadingState,
16
+ ErrorState,
17
+ FilterCriteria,
18
+ SortCriteria,
19
+ NavigationState,
20
+ SelectionState,
21
+ IconDefinition,
22
+ PreviewData,
23
+ ItemChange
24
+ } from './types/FileBrowserTypes';
25
+
26
+ // Provider interface and implementations
27
+ export type { IFileBrowserProvider, ProviderCapabilities } from './providers/IFileBrowserProvider';
28
+ export { FileSystemProvider } from './providers/FileSystemProvider';
29
+ export { GitRepositoryProvider } from './providers/GitRepositoryProvider';
30
+ export { GitProvider } from './providers/GitProvider';
31
+
32
+ // Models
33
+ export { FileBrowserModel } from './models/FileBrowserModel';
34
+ export { UploadModel } from './models/UploadModel';
35
+ export type { UploadFileEntry } from './models/UploadModel';
36
+ export { ViewerHostModel } from './models/ViewerHostModel';
37
+ export { SelectionManagerModel } from './models/SelectionManagerModel';
38
+ export { NavigationManagerModel } from './models/NavigationManagerModel';
39
+ export { ToolbarManagerModel } from './models/ToolbarManagerModel';
40
+ export { ViewModeManagerModel } from './models/ViewModeManagerModel';
41
+
42
+ // Adapters
43
+ export { FileSystemBridge } from './adapters/FileSystemBridge';
44
+
45
+ // Providers (bridge)
46
+ export { FileSystemTreeProvider } from './providers/FileSystemTreeProvider';
47
+ export { FileSystemListProvider } from './providers/FileSystemListProvider';
48
+
49
+ // Viewer registry and plugin API
50
+ export { globalViewerRegistry, ViewerRegistry, viewerConfigToPlugin } from './registry/ViewerRegistry';
51
+ export type { ViewerConfig, ViewerProps } from './registry/ViewerRegistry';
52
+ export type { FileViewerPlugin, FileMatchContext, ResolvedViewer, ToolbarAction, KeyboardShortcut } from './registry/types';
53
+
54
+ // ViewerHost component
55
+ export { ViewerHost, type ViewerHostProps } from './components/ViewerHost';
56
+
57
+ // UI Models
58
+ export { TreeViewUIModel } from './models/ui/TreeViewUIModel';
59
+ export { ListViewUIModel, type ColumnDefinition } from './models/ui/ListViewUIModel';
60
+ export { ThumbnailViewUIModel } from './models/ui/ThumbnailViewUIModel';
61
+
62
+ // Layout Components
63
+ export { default as FileBrowserHeader, type FileBrowserHeaderProps } from './components/FileBrowserHeader';
64
+ export { default as FileBrowserContent, type FileBrowserContentProps } from './components/FileBrowserContent';
65
+
66
+ // View Components
67
+ export { default as FileBrowserTreeView, type TreeViewProps as FileBrowserTreeViewProps } from './components/views/TreeView/TreeView';
68
+ export { default as ListView, type ListViewProps } from './components/views/ListView/ListView';
69
+
70
+ // Navigation Components
71
+ export { default as NavigationButtons, type NavigationButtonsProps } from './components/navigation/NavigationButtons';
72
+
73
+ // Toolbar Components
74
+ export { default as ViewModeToggle, type ViewModeToggleProps } from './components/toolbar/ViewModeToggle';
75
+
76
+ // View mode types (for extensions)
77
+ export type {
78
+ ViewModeDefinition,
79
+ ViewModeCapabilities,
80
+ ViewModeSettings,
81
+ TreeViewSettings,
82
+ ListViewSettings,
83
+ ThumbnailViewSettings
84
+ } from './types/ViewModeTypes';
85
+
86
+ // Provider types
87
+ export * from './types/ProviderTypes';
88
+
89
+ // UI types (for custom components)
90
+ export type {
91
+ UIComponentProps,
92
+ FileIconProps as FileIconUIProps,
93
+ BreadcrumbProps,
94
+ ToolbarActionConfig
95
+ } from './types/UITypes';
96
+
97
+ // Utils
98
+ export { cn } from '../lib/utils';