@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.
- package/dist/ExplorerLayout-CSIJd7N4.js +105 -0
- package/dist/ExplorerLayout-CSIJd7N4.js.map +1 -0
- package/dist/FileBrowserContext-B6jixa2j.js +11 -0
- package/dist/FileBrowserContext-B6jixa2j.js.map +1 -0
- package/dist/calendar-DSlrbHoj.js +761 -0
- package/dist/calendar-DSlrbHoj.js.map +1 -0
- package/dist/calendar.d.ts +3 -0
- package/dist/calendar.js +3 -0
- package/dist/contacts-DQXTZzHc.js +539 -0
- package/dist/contacts-DQXTZzHc.js.map +1 -0
- package/dist/contacts.d.ts +3 -0
- package/dist/contacts.js +3 -0
- package/dist/file-browser-m5atC3kF.js +6755 -0
- package/dist/file-browser-m5atC3kF.js.map +1 -0
- package/dist/file-browser.d.ts +11 -0
- package/dist/file-browser.js +9 -0
- package/dist/git-B55e6LL-.js +561 -0
- package/dist/git-B55e6LL-.js.map +1 -0
- package/dist/git.d.ts +2 -0
- package/dist/git.js +3 -0
- package/dist/iconMap-V4B8P-Uh.js +206 -0
- package/dist/iconMap-V4B8P-Uh.js.map +1 -0
- package/dist/icons-CIsIOZXR.js +0 -0
- package/dist/icons.d.ts +2 -0
- package/dist/icons.js +4 -0
- package/dist/index-BNmNIWBL.d.ts +71 -0
- package/dist/index-BNmNIWBL.d.ts.map +1 -0
- package/dist/index-Bryv_GCG.d.ts +1481 -0
- package/dist/index-Bryv_GCG.d.ts.map +1 -0
- package/dist/index-CuQIjSXs.d.ts +134 -0
- package/dist/index-CuQIjSXs.d.ts.map +1 -0
- package/dist/index-DSu19mq0.d.ts +153 -0
- package/dist/index-DSu19mq0.d.ts.map +1 -0
- package/dist/index-DmsyeHFr.d.ts +149 -0
- package/dist/index-DmsyeHFr.d.ts.map +1 -0
- package/dist/index-DxnJ8FYM.d.ts +17 -0
- package/dist/index-DxnJ8FYM.d.ts.map +1 -0
- package/dist/index-DzfY1Tok.d.ts +32 -0
- package/dist/index-DzfY1Tok.d.ts.map +1 -0
- package/dist/index-Ml_SgiKa.d.ts +1847 -0
- package/dist/index-Ml_SgiKa.d.ts.map +1 -0
- package/dist/index-kHr9udZD.d.ts +1025 -0
- package/dist/index-kHr9udZD.d.ts.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +15 -0
- package/dist/layout-Ca_4r8ka.js +89 -0
- package/dist/layout-Ca_4r8ka.js.map +1 -0
- package/dist/layout.d.ts +2 -0
- package/dist/layout.js +5 -0
- package/dist/list-CxfT6hix.js +6831 -0
- package/dist/list-CxfT6hix.js.map +1 -0
- package/dist/list.d.ts +2 -0
- package/dist/list.js +5 -0
- package/dist/media-DZ292aKK.js +557 -0
- package/dist/media-DZ292aKK.js.map +1 -0
- package/dist/media.d.ts +3 -0
- package/dist/media.js +3 -0
- package/dist/tree-Dd9Z0Aso.js +3351 -0
- package/dist/tree-Dd9Z0Aso.js.map +1 -0
- package/dist/tree.d.ts +2 -0
- package/dist/tree.js +6 -0
- package/dist/types-common-CB3kRek8.d.ts +26 -0
- package/dist/types-common-CB3kRek8.d.ts.map +1 -0
- package/dist/utils-B4fdKKsy.js +3 -0
- package/package.json +109 -0
- package/src/calendar/AgendaView.tsx +37 -0
- package/src/calendar/CalendarBrowser.tsx +90 -0
- package/src/calendar/CalendarModel.ts +142 -0
- package/src/calendar/CalendarSidebar.tsx +81 -0
- package/src/calendar/DayView.tsx +76 -0
- package/src/calendar/EventCard.tsx +51 -0
- package/src/calendar/MockCalendarProvider.ts +98 -0
- package/src/calendar/MonthView.tsx +77 -0
- package/src/calendar/WeekView.tsx +129 -0
- package/src/calendar/index.ts +18 -0
- package/src/calendar/types.ts +25 -0
- package/src/contacts/ContactAvatar.tsx +35 -0
- package/src/contacts/ContactBrowser.tsx +56 -0
- package/src/contacts/ContactCard.tsx +37 -0
- package/src/contacts/ContactDetail.tsx +63 -0
- package/src/contacts/ContactGroupSidebar.tsx +40 -0
- package/src/contacts/ContactList.tsx +32 -0
- package/src/contacts/ContactListModel.ts +120 -0
- package/src/contacts/MockContactProvider.ts +77 -0
- package/src/contacts/index.ts +17 -0
- package/src/contacts/types.ts +26 -0
- package/src/demos/CalendarBrowserDemo.tsx +15 -0
- package/src/demos/ContactBrowserDemo.tsx +15 -0
- package/src/demos/MediaBrowserDemo.tsx +15 -0
- package/src/file-browser/adapters/DocumentViewerAdapter.ts +371 -0
- package/src/file-browser/adapters/FileSystemBridge.ts +168 -0
- package/src/file-browser/adapters/GitBrowserAdapter.ts +546 -0
- package/src/file-browser/adapters/README.md +504 -0
- package/src/file-browser/adapters/index.ts +27 -0
- package/src/file-browser/adapters/types.ts +70 -0
- package/src/file-browser/architecture.md +645 -0
- package/src/file-browser/components/CreateItemDialog.tsx +71 -0
- package/src/file-browser/components/DeleteConfirmDialog.tsx +58 -0
- package/src/file-browser/components/FileBrowser.tsx +473 -0
- package/src/file-browser/components/FileBrowserContent.tsx +209 -0
- package/src/file-browser/components/FileBrowserHeader.tsx +151 -0
- package/src/file-browser/components/FileBrowserToolbar.tsx +145 -0
- package/src/file-browser/components/LeftPanel/LeftPanel.tsx +103 -0
- package/src/file-browser/components/LeftPanel/LeftPanelTabs.tsx +70 -0
- package/src/file-browser/components/LeftPanel/TreeNavigationView.tsx +256 -0
- package/src/file-browser/components/PreviewPane.tsx +146 -0
- package/src/file-browser/components/RightPanel/FilePreview.tsx +219 -0
- package/src/file-browser/components/RightPanel/RightPanel.tsx +186 -0
- package/src/file-browser/components/RightPanel/RightPanelToolbar.tsx +113 -0
- package/src/file-browser/components/UploadProgress.tsx +123 -0
- package/src/file-browser/components/ViewerHost.tsx +208 -0
- package/src/file-browser/components/mobile/MobileNavigation.tsx +227 -0
- package/src/file-browser/components/navigation/NavigationButtons.tsx +171 -0
- package/src/file-browser/components/shared/ErrorBoundary.tsx +116 -0
- package/src/file-browser/components/shared/FileBrowserItem.tsx +195 -0
- package/src/file-browser/components/shared/FileIcon.tsx +169 -0
- package/src/file-browser/components/toolbar/ViewModeToggle.tsx +200 -0
- package/src/file-browser/components/views/ListView/ListView.tsx +484 -0
- package/src/file-browser/components/views/ThumbnailView/ThumbnailView.tsx +323 -0
- package/src/file-browser/components/views/TreeView/TreeNode.tsx +186 -0
- package/src/file-browser/components/views/TreeView/TreeNodeList.tsx +191 -0
- package/src/file-browser/components/views/TreeView/TreeView.tsx +200 -0
- package/src/file-browser/components/views/TreemapView/TreemapView.tsx +339 -0
- package/src/file-browser/context/FileBrowserContext.tsx +13 -0
- package/src/file-browser/examples/BasicUsage.tsx +20 -0
- package/src/file-browser/index.ts +98 -0
- package/src/file-browser/models/FileBrowserModel.ts +623 -0
- package/src/file-browser/models/LeftPanelManagerModel.ts +105 -0
- package/src/file-browser/models/NavigationManagerModel.ts +312 -0
- package/src/file-browser/models/ResponsiveLayoutManagerModel.ts +437 -0
- package/src/file-browser/models/RightPanelManagerModel.ts +190 -0
- package/src/file-browser/models/SelectionManagerModel.ts +252 -0
- package/src/file-browser/models/ToolbarManagerModel.ts +144 -0
- package/src/file-browser/models/UploadModel.ts +147 -0
- package/src/file-browser/models/ViewModeManagerModel.ts +185 -0
- package/src/file-browser/models/ViewerHostModel.ts +44 -0
- package/src/file-browser/models/ui/ListViewUIModel.ts +265 -0
- package/src/file-browser/models/ui/PreviewUIModel.ts +297 -0
- package/src/file-browser/models/ui/ThumbnailViewUIModel.ts +254 -0
- package/src/file-browser/models/ui/TreeViewUIModel.ts +128 -0
- package/src/file-browser/models/ui/TreemapViewUIModel.ts +350 -0
- package/src/file-browser/providers/FileSystemListProvider.ts +552 -0
- package/src/file-browser/providers/FileSystemProvider.ts +401 -0
- package/src/file-browser/providers/FileSystemTreeProvider.ts +231 -0
- package/src/file-browser/providers/GitProvider.ts +337 -0
- package/src/file-browser/providers/GitRepositoryProvider.ts +376 -0
- package/src/file-browser/providers/IFileBrowserProvider.ts +56 -0
- package/src/file-browser/providers/MemoryProvider.ts +303 -0
- package/src/file-browser/providers/index.ts +4 -0
- package/src/file-browser/registry/ViewerRegistry.ts +551 -0
- package/src/file-browser/registry/types.ts +144 -0
- package/src/file-browser/scripts/performanceBenchmark.ts +553 -0
- package/src/file-browser/services/ThumbnailCacheService.ts +128 -0
- package/src/file-browser/tasks.md +537 -0
- package/src/file-browser/types/FileBrowserTypes.ts +126 -0
- package/src/file-browser/types/ProviderTypes.ts +155 -0
- package/src/file-browser/types/UITypes.ts +235 -0
- package/src/file-browser/types/ViewModeTypes.ts +150 -0
- package/src/file-browser/utils/gestures.ts +327 -0
- package/src/file-browser/utils/performance.ts +563 -0
- package/src/file-browser/viewers/ImageViewer.tsx +163 -0
- package/src/file-browser/viewers/ImageViewerModel.ts +79 -0
- package/src/file-browser/viewers/TextViewer.tsx +95 -0
- package/src/file-browser/viewers/UnsupportedFileViewer.tsx +57 -0
- package/src/file-browser/viewers/index.ts +61 -0
- package/src/git/BranchList.tsx +128 -0
- package/src/git/CommitGraph.tsx +239 -0
- package/src/git/CommitList.tsx +258 -0
- package/src/git/DiffViewer.tsx +219 -0
- package/src/git/index.ts +4 -0
- package/src/icons/iconMap.ts +146 -0
- package/src/icons/index.ts +9 -0
- package/src/index.ts +13 -0
- package/src/layout/README.md +307 -0
- package/src/layout/components/ExplorerLayout/ExplorerLayout.tsx +178 -0
- package/src/layout/examples/SimpleExample.tsx +60 -0
- package/src/layout/index.ts +6 -0
- package/src/lib/utils.ts +1 -0
- package/src/list/README.md +303 -0
- package/src/list/architecture.md +807 -0
- package/src/list/components/CalculatedGridView.tsx +252 -0
- package/src/list/components/DragPreview.tsx +102 -0
- package/src/list/components/ListContextMenu.tsx +274 -0
- package/src/list/components/ListItem.tsx +761 -0
- package/src/list/components/ListItems.tsx +919 -0
- package/src/list/components/MasonryView.tsx +241 -0
- package/src/list/components/SearchFilter.tsx +44 -0
- package/src/list/components/TreemapView.tsx +709 -0
- package/src/list/components/ViewSizeControls.tsx +205 -0
- package/src/list/components/ViewTypeSelector.tsx +312 -0
- package/src/list/components/VirtualizedDetailsView.tsx +231 -0
- package/src/list/components/VirtualizedGrid.tsx +164 -0
- package/src/list/components/VirtualizedList.tsx +154 -0
- package/src/list/components/VirtualizedMasonryView.tsx +344 -0
- package/src/list/components/shared/EmptyState.tsx +103 -0
- package/src/list/components/shared/ErrorBoundary.tsx +123 -0
- package/src/list/components/shared/ErrorDisplay.tsx +100 -0
- package/src/list/components/shared/ListLoader.tsx +146 -0
- package/src/list/components/shared/LoadingIndicator.tsx +80 -0
- package/src/list/index.ts +92 -0
- package/src/list/models/ListItemsModel.ts +1301 -0
- package/src/list/models/TreemapModel.ts +204 -0
- package/src/list/providers/ListItemsProvider.ts +313 -0
- package/src/list/providers/TestListProvider.ts +604 -0
- package/src/list/tasks.md +937 -0
- package/src/list/types/ListTypes.ts +178 -0
- package/src/list/utils/BenchmarkLogger.ts +243 -0
- package/src/list/utils/DragDropManager.ts +320 -0
- package/src/list/utils/GridLayoutCalculator.ts +290 -0
- package/src/list/utils/ListAccessibility.ts +367 -0
- package/src/list/utils/ListKeyboard.ts +414 -0
- package/src/list/utils/MasonryLayoutCalculator.ts +302 -0
- package/src/list/utils/MasonryLayoutEngine.ts +401 -0
- package/src/list/utils/__tests__/MasonryLayoutEngine.test.ts +157 -0
- package/src/list/utils/__tests__/VirtualizedMasonryView.test.tsx +251 -0
- package/src/media/AlbumSidebar.tsx +48 -0
- package/src/media/MediaBrowser.tsx +92 -0
- package/src/media/MediaBrowserModel.ts +138 -0
- package/src/media/MediaGrid.tsx +50 -0
- package/src/media/MediaList.tsx +49 -0
- package/src/media/MediaPreview.tsx +63 -0
- package/src/media/MediaTimeline.tsx +38 -0
- package/src/media/MockMediaProvider.ts +70 -0
- package/src/media/index.ts +18 -0
- package/src/media/types.ts +21 -0
- package/src/styles/variables.css +60 -0
- package/src/tree/DEVELOPMENT_SUMMARY.md +170 -0
- package/src/tree/__tests__/TreeModel.test.ts +16 -0
- package/src/tree/architecture.md +530 -0
- package/src/tree/components/Tree.tsx +283 -0
- package/src/tree/components/TreeCheckbox.tsx +147 -0
- package/src/tree/components/TreeContextMenu.tsx +139 -0
- package/src/tree/components/TreeNodeList.tsx +329 -0
- package/src/tree/components/TreeTable.tsx +382 -0
- package/src/tree/index.ts +58 -0
- package/src/tree/models/TreeModel.ts +839 -0
- package/src/tree/providers/SimpleTreeProvider.ts +463 -0
- package/src/tree/providers/TestTreeProvider.ts +946 -0
- package/src/tree/providers/TreeProvider.ts +308 -0
- package/src/tree/tasks.md +2046 -0
- package/src/tree/types/TreeTypes.ts +279 -0
- package/src/tree/utils/SelectionTheme.ts +150 -0
- package/src/tree/utils/logger.ts +203 -0
- 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
|
+
}
|