@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,437 @@
1
+ import { makeAutoObservable, observable } from 'mobx';
2
+
3
+ interface ResponsiveOwner {
4
+ logger?: { info: (message: string) => void; warn: (message: string) => void; error: (message: string) => void } | null;
5
+ }
6
+
7
+ export type ScreenSize = 'mobile' | 'tablet' | 'desktop';
8
+ export type PanelLayout = 'single' | 'dual' | 'overlay';
9
+
10
+ export interface ResponsiveBreakpoints {
11
+ mobile: number;
12
+ tablet: number;
13
+ desktop: number;
14
+ }
15
+
16
+ export interface LayoutConfiguration {
17
+ screenSize: ScreenSize;
18
+ panelLayout: PanelLayout;
19
+ showLeftPanel: boolean;
20
+ showRightPanel: boolean;
21
+ leftPanelWidth: string;
22
+ rightPanelWidth: string;
23
+ isLeftPanelCollapsed: boolean;
24
+ isRightPanelCollapsed: boolean;
25
+ }
26
+
27
+ export class ResponsiveLayoutManagerModel {
28
+ // Screen dimensions
29
+ screenWidth = 1200;
30
+ screenHeight = 800;
31
+
32
+ // Breakpoints (in pixels)
33
+ breakpoints: ResponsiveBreakpoints = {
34
+ mobile: 768,
35
+ tablet: 1024,
36
+ desktop: 1200
37
+ };
38
+
39
+ // Panel states
40
+ isLeftPanelVisible = true;
41
+ isRightPanelVisible = true;
42
+ isLeftPanelCollapsed = false;
43
+ isRightPanelCollapsed = false;
44
+
45
+ // Mobile-specific states
46
+ isMobileMenuOpen = false;
47
+ activePanel: 'left' | 'right' | null = null;
48
+
49
+ // Animation states
50
+ isAnimating = false;
51
+ animationDuration = 300; // ms
52
+
53
+ // Focus management
54
+ previousFocusElement: HTMLElement | null = null;
55
+ trapFocus = false;
56
+
57
+ // Layout preferences
58
+ leftPanelWidth = '300px';
59
+ rightPanelWidth = '400px';
60
+ minPanelWidth = 200;
61
+ maxPanelWidth = 600;
62
+
63
+ // Adaptive view modes
64
+ adaptiveViewModes = observable.map<ScreenSize, string[]>();
65
+
66
+ private owner: ResponsiveOwner;
67
+ private resizeObserver: ResizeObserver | null = null;
68
+ private mediaQueryListeners: MediaQueryList[] = [];
69
+
70
+ constructor(owner: ResponsiveOwner) {
71
+ this.owner = owner;
72
+ makeAutoObservable(this);
73
+
74
+ this.setupAdaptiveViewModes();
75
+ this.setupMediaQueryListeners();
76
+ this.owner.logger?.info('ResponsiveLayoutManagerModel initialized');
77
+ }
78
+
79
+ private setupAdaptiveViewModes() {
80
+ // Mobile: Limited view modes for better UX
81
+ this.adaptiveViewModes.set('mobile', ['list']);
82
+
83
+ // Tablet: More view modes available
84
+ this.adaptiveViewModes.set('tablet', ['list', 'thumbnail']);
85
+
86
+ // Desktop: All view modes available
87
+ this.adaptiveViewModes.set('desktop', ['list', 'thumbnail', 'treemap', 'detail']);
88
+ }
89
+
90
+ private setupMediaQueryListeners() {
91
+ // Clean up existing listeners
92
+ this.mediaQueryListeners.forEach(mql => {
93
+ mql.removeEventListener('change', this.handleMediaQueryChange);
94
+ });
95
+ this.mediaQueryListeners = [];
96
+
97
+ // Create media query listeners for each breakpoint
98
+ const mobileQuery = window.matchMedia(`(max-width: ${this.breakpoints.mobile - 1}px)`);
99
+ const tabletQuery = window.matchMedia(`(min-width: ${this.breakpoints.mobile}px) and (max-width: ${this.breakpoints.tablet - 1}px)`);
100
+ const desktopQuery = window.matchMedia(`(min-width: ${this.breakpoints.tablet}px)`);
101
+
102
+ // Add listeners
103
+ [mobileQuery, tabletQuery, desktopQuery].forEach(mql => {
104
+ mql.addEventListener('change', this.handleMediaQueryChange);
105
+ this.mediaQueryListeners.push(mql);
106
+ });
107
+
108
+ // Initial check
109
+ this.updateLayoutForScreenSize();
110
+ }
111
+
112
+ private handleMediaQueryChange = () => {
113
+ this.updateLayoutForScreenSize();
114
+ };
115
+
116
+ private updateLayoutForScreenSize() {
117
+ const currentScreenSize = this.currentScreenSize;
118
+
119
+ // Auto-collapse panels on mobile
120
+ if (currentScreenSize === 'mobile') {
121
+ this.isLeftPanelCollapsed = true;
122
+ this.isRightPanelCollapsed = true;
123
+ this.activePanel = null;
124
+ } else if (currentScreenSize === 'tablet') {
125
+ this.isLeftPanelCollapsed = false;
126
+ this.isRightPanelCollapsed = true;
127
+ } else {
128
+ this.isLeftPanelCollapsed = false;
129
+ this.isRightPanelCollapsed = false;
130
+ }
131
+
132
+ this.owner.logger?.info(`Layout updated for screen size: ${currentScreenSize}`);
133
+ }
134
+
135
+ // Computed values
136
+ get currentScreenSize(): ScreenSize {
137
+ if (this.screenWidth < this.breakpoints.mobile) {
138
+ return 'mobile';
139
+ } else if (this.screenWidth < this.breakpoints.tablet) {
140
+ return 'tablet';
141
+ } else {
142
+ return 'desktop';
143
+ }
144
+ }
145
+
146
+ get currentPanelLayout(): PanelLayout {
147
+ const screenSize = this.currentScreenSize;
148
+
149
+ if (screenSize === 'mobile') {
150
+ return this.activePanel ? 'overlay' : 'single';
151
+ } else if (screenSize === 'tablet') {
152
+ return this.isRightPanelCollapsed ? 'single' : 'dual';
153
+ } else {
154
+ return 'dual';
155
+ }
156
+ }
157
+
158
+ get currentLayoutConfiguration(): LayoutConfiguration {
159
+ return {
160
+ screenSize: this.currentScreenSize,
161
+ panelLayout: this.currentPanelLayout,
162
+ showLeftPanel: this.shouldShowLeftPanel,
163
+ showRightPanel: this.shouldShowRightPanel,
164
+ leftPanelWidth: this.leftPanelWidth,
165
+ rightPanelWidth: this.rightPanelWidth,
166
+ isLeftPanelCollapsed: this.isLeftPanelCollapsed,
167
+ isRightPanelCollapsed: this.isRightPanelCollapsed
168
+ };
169
+ }
170
+
171
+ get shouldShowLeftPanel(): boolean {
172
+ const screenSize = this.currentScreenSize;
173
+
174
+ if (screenSize === 'mobile') {
175
+ return this.isMobileMenuOpen && this.activePanel === 'left';
176
+ }
177
+
178
+ return this.isLeftPanelVisible && !this.isLeftPanelCollapsed;
179
+ }
180
+
181
+ get shouldShowRightPanel(): boolean {
182
+ const screenSize = this.currentScreenSize;
183
+
184
+ if (screenSize === 'mobile') {
185
+ return this.activePanel === 'right';
186
+ }
187
+
188
+ return this.isRightPanelVisible && !this.isRightPanelCollapsed;
189
+ }
190
+
191
+ get availableViewModes(): string[] {
192
+ return this.adaptiveViewModes.get(this.currentScreenSize) || ['list'];
193
+ }
194
+
195
+ get isMobile(): boolean {
196
+ return this.currentScreenSize === 'mobile';
197
+ }
198
+
199
+ get isTablet(): boolean {
200
+ return this.currentScreenSize === 'tablet';
201
+ }
202
+
203
+ get isDesktop(): boolean {
204
+ return this.currentScreenSize === 'desktop';
205
+ }
206
+
207
+ get canShowDualPanels(): boolean {
208
+ return this.currentScreenSize !== 'mobile';
209
+ }
210
+
211
+ get shouldShowHamburgerMenu(): boolean {
212
+ return this.currentScreenSize === 'mobile';
213
+ }
214
+
215
+ // Actions
216
+ setScreenDimensions = (width: number, height: number) => {
217
+ this.screenWidth = width;
218
+ this.screenHeight = height;
219
+ this.updateLayoutForScreenSize();
220
+ };
221
+
222
+ setBreakpoints = (breakpoints: Partial<ResponsiveBreakpoints>) => {
223
+ this.breakpoints = { ...this.breakpoints, ...breakpoints };
224
+ this.setupMediaQueryListeners();
225
+ this.owner.logger?.info(`Breakpoints updated: ${JSON.stringify(this.breakpoints)}`);
226
+ };
227
+
228
+ // Panel visibility actions
229
+ toggleLeftPanel = () => {
230
+ if (this.currentScreenSize === 'mobile') {
231
+ this.toggleMobilePanel('left');
232
+ } else {
233
+ this.isLeftPanelCollapsed = !this.isLeftPanelCollapsed;
234
+ }
235
+ this.owner.logger?.info(`Left panel toggled: ${this.shouldShowLeftPanel}`);
236
+ };
237
+
238
+ toggleRightPanel = () => {
239
+ if (this.currentScreenSize === 'mobile') {
240
+ this.toggleMobilePanel('right');
241
+ } else {
242
+ this.isRightPanelCollapsed = !this.isRightPanelCollapsed;
243
+ }
244
+ this.owner.logger?.info(`Right panel toggled: ${this.shouldShowRightPanel}`);
245
+ };
246
+
247
+ setLeftPanelVisibility = (visible: boolean) => {
248
+ if (this.currentScreenSize === 'mobile') {
249
+ if (visible) {
250
+ this.showMobilePanel('left');
251
+ } else {
252
+ this.hideMobilePanel();
253
+ }
254
+ } else {
255
+ this.isLeftPanelCollapsed = !visible;
256
+ }
257
+ };
258
+
259
+ setRightPanelVisibility = (visible: boolean) => {
260
+ if (this.currentScreenSize === 'mobile') {
261
+ if (visible) {
262
+ this.showMobilePanel('right');
263
+ } else {
264
+ this.hideMobilePanel();
265
+ }
266
+ } else {
267
+ this.isRightPanelCollapsed = !visible;
268
+ }
269
+ };
270
+
271
+ // Mobile-specific actions
272
+ toggleMobileMenu = () => {
273
+ if (this.isAnimating) return;
274
+
275
+ const willOpen = !this.isMobileMenuOpen;
276
+
277
+ if (willOpen) {
278
+ this.storeFocus();
279
+ } else {
280
+ this.restoreFocus();
281
+ this.activePanel = null;
282
+ }
283
+
284
+ this.isMobileMenuOpen = willOpen;
285
+ this.animatePanel();
286
+
287
+ this.owner.logger?.info(`Mobile menu toggled: ${this.isMobileMenuOpen}`);
288
+ };
289
+
290
+ showMobilePanel = (panel: 'left' | 'right') => {
291
+ if (this.currentScreenSize !== 'mobile' || this.isAnimating) return;
292
+
293
+ // Store focus before showing panel
294
+ if (!this.isMobileMenuOpen) {
295
+ this.storeFocus();
296
+ }
297
+
298
+ this.isMobileMenuOpen = true;
299
+ this.activePanel = panel;
300
+ this.trapFocus = true;
301
+ this.animatePanel();
302
+
303
+ this.owner.logger?.info(`Mobile panel shown: ${panel}`);
304
+ };
305
+
306
+ hideMobilePanel = () => {
307
+ if (this.isAnimating) return;
308
+
309
+ this.isMobileMenuOpen = false;
310
+ this.activePanel = null;
311
+ this.trapFocus = false;
312
+ this.animatePanel();
313
+
314
+ // Restore focus after animation
315
+ setTimeout(() => {
316
+ this.restoreFocus();
317
+ }, this.animationDuration);
318
+
319
+ this.owner.logger?.info('Mobile panel hidden');
320
+ };
321
+
322
+ toggleMobilePanel = (panel: 'left' | 'right') => {
323
+ if (this.activePanel === panel) {
324
+ this.hideMobilePanel();
325
+ } else {
326
+ this.showMobilePanel(panel);
327
+ }
328
+ };
329
+
330
+ // Panel sizing actions
331
+ setLeftPanelWidth = (width: string) => {
332
+ this.leftPanelWidth = width;
333
+ this.owner.logger?.info(`Left panel width set: ${width}`);
334
+ };
335
+
336
+ setRightPanelWidth = (width: string) => {
337
+ this.rightPanelWidth = width;
338
+ this.owner.logger?.info(`Right panel width set: ${width}`);
339
+ };
340
+
341
+ // Adaptive view mode management
342
+ setAdaptiveViewModes = (screenSize: ScreenSize, viewModes: string[]) => {
343
+ this.adaptiveViewModes.set(screenSize, viewModes);
344
+ this.owner.logger?.info(`Adaptive view modes set for ${screenSize}: ${JSON.stringify(viewModes)}`);
345
+ };
346
+
347
+ isViewModeAvailable = (viewMode: string): boolean => {
348
+ return this.availableViewModes.includes(viewMode);
349
+ };
350
+
351
+ // Animation methods
352
+ private animatePanel = () => {
353
+ this.isAnimating = true;
354
+
355
+ // Animation handled by CSS transitions
356
+ setTimeout(() => {
357
+ this.isAnimating = false;
358
+ }, this.animationDuration);
359
+ };
360
+
361
+ // Focus management methods
362
+ private storeFocus = () => {
363
+ if (typeof document !== 'undefined') {
364
+ this.previousFocusElement = document.activeElement as HTMLElement;
365
+ }
366
+ };
367
+
368
+ private restoreFocus = () => {
369
+ if (this.previousFocusElement && typeof this.previousFocusElement.focus === 'function') {
370
+ this.previousFocusElement.focus({ preventScroll: true });
371
+ this.previousFocusElement = null;
372
+ }
373
+ };
374
+
375
+ // Touch-friendly target size validation
376
+ isTouchFriendlySize = (element: HTMLElement): boolean => {
377
+ const rect = element.getBoundingClientRect();
378
+ const minSize = 44; // 44px minimum per iOS HIG and Material Design
379
+ return rect.width >= minSize && rect.height >= minSize;
380
+ };
381
+
382
+ // Gesture integration
383
+ enableGestureNavigation = (element: HTMLElement) => {
384
+ // This will be used by components to enable swipe gestures
385
+ return {
386
+ onSwipeLeft: () => {
387
+ if (this.currentScreenSize === 'mobile') {
388
+ this.showMobilePanel('right');
389
+ }
390
+ },
391
+ onSwipeRight: () => {
392
+ if (this.currentScreenSize === 'mobile') {
393
+ this.showMobilePanel('left');
394
+ }
395
+ },
396
+ onSwipeDown: () => {
397
+ if (this.currentScreenSize === 'mobile' && this.isMobileMenuOpen) {
398
+ this.hideMobilePanel();
399
+ }
400
+ }
401
+ };
402
+ };
403
+
404
+ // Utility methods
405
+ observeElementResize = (element: HTMLElement) => {
406
+ if (this.resizeObserver) {
407
+ this.resizeObserver.disconnect();
408
+ }
409
+
410
+ this.resizeObserver = new ResizeObserver((entries) => {
411
+ const entry = entries[0];
412
+ if (entry) {
413
+ const { width, height } = entry.contentRect;
414
+ this.setScreenDimensions(width, height);
415
+ }
416
+ });
417
+
418
+ this.resizeObserver.observe(element);
419
+ };
420
+
421
+ // Cleanup
422
+ dispose = () => {
423
+ // Remove media query listeners
424
+ this.mediaQueryListeners.forEach(mql => {
425
+ mql.removeEventListener('change', this.handleMediaQueryChange);
426
+ });
427
+ this.mediaQueryListeners = [];
428
+
429
+ // Disconnect resize observer
430
+ if (this.resizeObserver) {
431
+ this.resizeObserver.disconnect();
432
+ this.resizeObserver = null;
433
+ }
434
+
435
+ this.owner.logger?.info('ResponsiveLayoutManagerModel disposed');
436
+ };
437
+ }
@@ -0,0 +1,190 @@
1
+ import { makeAutoObservable, observable } from 'mobx';
2
+ import type { ListViewUIModel } from './ui/ListViewUIModel';
3
+ import type { ThumbnailViewUIModel } from './ui/ThumbnailViewUIModel';
4
+ import type { TreemapViewUIModel } from './ui/TreemapViewUIModel';
5
+ import { PreviewUIModel } from './ui/PreviewUIModel';
6
+
7
+ export interface ViewModeDefinition {
8
+ id: string;
9
+ name: string;
10
+ icon: string;
11
+ component?: React.ComponentType<any>;
12
+ applicableFor?: (contentType: 'file' | 'folder' | 'empty') => boolean;
13
+ }
14
+
15
+ // PreviewUIModel is now imported from ./ui/PreviewUIModel
16
+
17
+ export class RightPanelManagerModel {
18
+ // Content type (file or folder)
19
+ contentType: 'file' | 'folder' | 'empty' = 'empty';
20
+
21
+ // Current selected item for context
22
+ selectedItem: any = null;
23
+
24
+ // View modes for folder content
25
+ availableViewModes = observable.map<string, ViewModeDefinition>();
26
+ currentViewMode = 'list'; // list, thumbnail, treemap, detail
27
+
28
+ // View-specific UI models
29
+ listViewUI: ListViewUIModel;
30
+ thumbnailViewUI: ThumbnailViewUIModel;
31
+ treemapViewUI: TreemapViewUIModel;
32
+ previewUI: PreviewUIModel;
33
+
34
+ constructor(
35
+ private owner: { logger?: any },
36
+ listViewUI: ListViewUIModel,
37
+ thumbnailViewUI: ThumbnailViewUIModel,
38
+ treemapViewUI: TreemapViewUIModel
39
+ ) {
40
+ this.listViewUI = listViewUI;
41
+ this.thumbnailViewUI = thumbnailViewUI;
42
+ this.treemapViewUI = treemapViewUI;
43
+ this.previewUI = new PreviewUIModel(owner);
44
+
45
+ // Add default view modes
46
+ this.addDefaultViewModes();
47
+
48
+ makeAutoObservable(this);
49
+ this.owner.logger?.info('RightPanelManagerModel initialized');
50
+ }
51
+
52
+ private addDefaultViewModes() {
53
+ this.availableViewModes.set('list', {
54
+ id: 'list',
55
+ name: 'List',
56
+ icon: 'list',
57
+ applicableFor: (contentType) => contentType === 'folder'
58
+ });
59
+
60
+ this.availableViewModes.set('thumbnail', {
61
+ id: 'thumbnail',
62
+ name: 'Thumbnails',
63
+ icon: 'grid-3x3',
64
+ applicableFor: (contentType) => contentType === 'folder'
65
+ });
66
+
67
+ this.availableViewModes.set('treemap', {
68
+ id: 'treemap',
69
+ name: 'Treemap',
70
+ icon: 'layout-dashboard',
71
+ applicableFor: (contentType) => contentType === 'folder'
72
+ });
73
+
74
+ this.availableViewModes.set('detail', {
75
+ id: 'detail',
76
+ name: 'Details',
77
+ icon: 'info',
78
+ applicableFor: (contentType) => contentType === 'folder'
79
+ });
80
+ }
81
+
82
+ // Actions
83
+ setSelectedItem = (item: any) => {
84
+ this.selectedItem = item;
85
+
86
+ if (!item) {
87
+ this.contentType = 'empty';
88
+ this.previewUI.setCurrentItem(null);
89
+ } else if (item.type === 'file') {
90
+ this.contentType = 'file';
91
+ this.previewUI.setCurrentItem(item);
92
+ } else if (item.type === 'folder' || item.type === 'directory') {
93
+ this.contentType = 'folder';
94
+ this.previewUI.setCurrentItem(null);
95
+ }
96
+
97
+ this.owner.logger?.info(`Right panel content type: ${this.contentType}`);
98
+ };
99
+
100
+ setCurrentViewMode = (viewModeId: string) => {
101
+ if (this.contentType !== 'folder') {
102
+ this.owner.logger?.warn('View modes only available for folders');
103
+ return;
104
+ }
105
+
106
+ if (this.availableViewModes.has(viewModeId)) {
107
+ this.currentViewMode = viewModeId;
108
+ this.owner.logger?.info(`Right panel view mode changed to: ${viewModeId}`);
109
+ } else {
110
+ this.owner.logger?.warn(`Unknown view mode: ${viewModeId}`);
111
+ }
112
+ };
113
+
114
+ addViewMode = (viewMode: ViewModeDefinition) => {
115
+ this.availableViewModes.set(viewMode.id, viewMode);
116
+ this.owner.logger?.info(`Added view mode: ${viewMode.id}`);
117
+ };
118
+
119
+ removeViewMode = (viewModeId: string) => {
120
+ // Prevent removing default view modes
121
+ const defaultModes = ['list', 'thumbnail', 'treemap', 'detail'];
122
+ if (defaultModes.includes(viewModeId)) {
123
+ this.owner.logger?.warn(`Cannot remove default view mode: ${viewModeId}`);
124
+ return;
125
+ }
126
+
127
+ this.availableViewModes.delete(viewModeId);
128
+
129
+ // Switch to list if current mode was removed
130
+ if (this.currentViewMode === viewModeId) {
131
+ this.currentViewMode = 'list';
132
+ }
133
+
134
+ this.owner.logger?.info(`Removed view mode: ${viewModeId}`);
135
+ };
136
+
137
+ // Preview actions are now handled by PreviewUIModel
138
+
139
+ // Computed
140
+ get availableViewModesList() {
141
+ return Array.from(this.availableViewModes.values()).filter(mode =>
142
+ mode.applicableFor ? mode.applicableFor(this.contentType) : true
143
+ );
144
+ }
145
+
146
+ get fileBrowserModel() {
147
+ return this.owner as any; // Cast to access FileBrowserModel properties
148
+ }
149
+
150
+ get currentViewModeData() {
151
+ return this.availableViewModes.get(this.currentViewMode);
152
+ }
153
+
154
+ get hasViewModeOptions() {
155
+ return this.contentType === 'folder' && this.availableViewModesList.length > 1;
156
+ }
157
+
158
+ get currentUIModel() {
159
+ if (this.contentType === 'file') {
160
+ return this.previewUI;
161
+ }
162
+
163
+ switch (this.currentViewMode) {
164
+ case 'list':
165
+ return this.listViewUI;
166
+ case 'thumbnail':
167
+ return this.thumbnailViewUI;
168
+ case 'treemap':
169
+ return this.treemapViewUI;
170
+ case 'detail':
171
+ return this.listViewUI; // Use list UI for detail view for now
172
+ default:
173
+ return this.listViewUI;
174
+ }
175
+ }
176
+
177
+ get shouldShowPreview() {
178
+ return this.contentType === 'file';
179
+ }
180
+
181
+ get shouldShowFolderContent() {
182
+ return this.contentType === 'folder';
183
+ }
184
+
185
+ get shouldShowEmpty() {
186
+ return this.contentType === 'empty';
187
+ }
188
+
189
+ // Helper methods are now in PreviewUIModel
190
+ }