@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,563 @@
|
|
|
1
|
+
// Simple debounce implementation
|
|
2
|
+
const debounce = <T extends (...args: any[]) => void>(
|
|
3
|
+
func: T,
|
|
4
|
+
wait: number
|
|
5
|
+
): ((...args: Parameters<T>) => void) => {
|
|
6
|
+
let timeout: NodeJS.Timeout;
|
|
7
|
+
return (...args: Parameters<T>) => {
|
|
8
|
+
clearTimeout(timeout);
|
|
9
|
+
timeout = setTimeout(() => func(...args), wait);
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// Simple throttle implementation
|
|
14
|
+
const throttle = <T extends (...args: any[]) => void>(
|
|
15
|
+
func: T,
|
|
16
|
+
wait: number
|
|
17
|
+
): ((...args: Parameters<T>) => void) => {
|
|
18
|
+
let lastRun = 0;
|
|
19
|
+
return (...args: Parameters<T>) => {
|
|
20
|
+
if (Date.now() - lastRun >= wait) {
|
|
21
|
+
func(...args);
|
|
22
|
+
lastRun = Date.now();
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Performance optimization utilities for FileBrowser responsive design
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
export interface PerformanceMetrics {
|
|
32
|
+
renderTime: number;
|
|
33
|
+
interactionLatency: number;
|
|
34
|
+
memoryUsage: number;
|
|
35
|
+
bundleSize: number;
|
|
36
|
+
frameRate: number;
|
|
37
|
+
touchLatency: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface PerformanceThresholds {
|
|
41
|
+
maxRenderTime: number;
|
|
42
|
+
maxInteractionLatency: number;
|
|
43
|
+
maxMemoryUsage: number;
|
|
44
|
+
minFrameRate: number;
|
|
45
|
+
maxTouchLatency: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export class PerformanceMonitor {
|
|
49
|
+
private metrics: Partial<PerformanceMetrics> = {};
|
|
50
|
+
private thresholds: PerformanceThresholds;
|
|
51
|
+
private warnings: string[] = [];
|
|
52
|
+
private observers: PerformanceObserver[] = [];
|
|
53
|
+
|
|
54
|
+
constructor(thresholds: Partial<PerformanceThresholds> = {}) {
|
|
55
|
+
this.thresholds = {
|
|
56
|
+
maxRenderTime: 100, // 100ms
|
|
57
|
+
maxInteractionLatency: 50, // 50ms
|
|
58
|
+
maxMemoryUsage: 100 * 1024 * 1024, // 100MB
|
|
59
|
+
minFrameRate: 30, // 30fps
|
|
60
|
+
maxTouchLatency: 16, // 16ms for 60fps
|
|
61
|
+
...thresholds
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
this.setupPerformanceObservers();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private setupPerformanceObservers() {
|
|
68
|
+
if (typeof PerformanceObserver !== 'undefined') {
|
|
69
|
+
// Monitor paint metrics
|
|
70
|
+
const paintObserver = new PerformanceObserver((list) => {
|
|
71
|
+
for (const entry of list.getEntries()) {
|
|
72
|
+
if (entry.name === 'first-contentful-paint') {
|
|
73
|
+
this.metrics.renderTime = entry.startTime;
|
|
74
|
+
this.checkThreshold('renderTime', entry.startTime, this.thresholds.maxRenderTime);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
paintObserver.observe({ entryTypes: ['paint'] });
|
|
80
|
+
this.observers.push(paintObserver);
|
|
81
|
+
|
|
82
|
+
// Monitor user interactions
|
|
83
|
+
const eventObserver = new PerformanceObserver((list) => {
|
|
84
|
+
for (const entry of list.getEntries()) {
|
|
85
|
+
const latency = entry.processingEnd - entry.startTime;
|
|
86
|
+
this.metrics.interactionLatency = latency;
|
|
87
|
+
this.checkThreshold('interactionLatency', latency, this.thresholds.maxInteractionLatency);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
eventObserver.observe({ entryTypes: ['event'] });
|
|
93
|
+
this.observers.push(eventObserver);
|
|
94
|
+
} catch (e) {
|
|
95
|
+
// Event timing API not supported
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private checkThreshold(metric: keyof PerformanceMetrics, value: number, threshold: number) {
|
|
101
|
+
if (value > threshold) {
|
|
102
|
+
const warning = `Performance warning: ${metric} (${value.toFixed(2)}) exceeds threshold (${threshold})`;
|
|
103
|
+
this.warnings.push(warning);
|
|
104
|
+
console.warn(warning);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
recordMetric(name: keyof PerformanceMetrics, value: number) {
|
|
109
|
+
this.metrics[name] = value;
|
|
110
|
+
|
|
111
|
+
const threshold = this.getThresholdForMetric(name);
|
|
112
|
+
if (threshold !== undefined) {
|
|
113
|
+
this.checkThreshold(name, value, threshold);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private getThresholdForMetric(name: keyof PerformanceMetrics): number | undefined {
|
|
118
|
+
switch (name) {
|
|
119
|
+
case 'renderTime': return this.thresholds.maxRenderTime;
|
|
120
|
+
case 'interactionLatency': return this.thresholds.maxInteractionLatency;
|
|
121
|
+
case 'memoryUsage': return this.thresholds.maxMemoryUsage;
|
|
122
|
+
case 'touchLatency': return this.thresholds.maxTouchLatency;
|
|
123
|
+
case 'frameRate': return this.thresholds.minFrameRate;
|
|
124
|
+
default: return undefined;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
getMetrics(): Partial<PerformanceMetrics> {
|
|
129
|
+
return { ...this.metrics };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
getWarnings(): string[] {
|
|
133
|
+
return [...this.warnings];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
getRecommendations(): string[] {
|
|
137
|
+
const recommendations: string[] = [];
|
|
138
|
+
|
|
139
|
+
if (this.metrics.renderTime && this.metrics.renderTime > this.thresholds.maxRenderTime) {
|
|
140
|
+
recommendations.push('Enable virtualization for large lists');
|
|
141
|
+
recommendations.push('Implement React.memo for expensive components');
|
|
142
|
+
recommendations.push('Use code splitting for heavy components');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (this.metrics.interactionLatency && this.metrics.interactionLatency > this.thresholds.maxInteractionLatency) {
|
|
146
|
+
recommendations.push('Debounce rapid user interactions');
|
|
147
|
+
recommendations.push('Use requestAnimationFrame for smooth animations');
|
|
148
|
+
recommendations.push('Optimize event handlers');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (this.metrics.memoryUsage && this.metrics.memoryUsage > this.thresholds.maxMemoryUsage) {
|
|
152
|
+
recommendations.push('Implement LRU cache with size limits');
|
|
153
|
+
recommendations.push('Clean up event listeners on unmount');
|
|
154
|
+
recommendations.push('Use weak references for large objects');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (this.metrics.touchLatency && this.metrics.touchLatency > this.thresholds.maxTouchLatency) {
|
|
158
|
+
recommendations.push('Optimize touch event handlers');
|
|
159
|
+
recommendations.push('Use passive event listeners');
|
|
160
|
+
recommendations.push('Reduce touch target processing time');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return recommendations;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
dispose() {
|
|
167
|
+
this.observers.forEach(observer => observer.disconnect());
|
|
168
|
+
this.observers = [];
|
|
169
|
+
this.warnings = [];
|
|
170
|
+
this.metrics = {};
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Debounced resize handler for responsive layouts
|
|
176
|
+
*/
|
|
177
|
+
export const createDebouncedResizeHandler = (
|
|
178
|
+
handler: (width: number, height: number) => void,
|
|
179
|
+
delay = 250
|
|
180
|
+
) => {
|
|
181
|
+
return debounce((event: Event) => {
|
|
182
|
+
const width = window.innerWidth;
|
|
183
|
+
const height = window.innerHeight;
|
|
184
|
+
handler(width, height);
|
|
185
|
+
}, delay);
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Throttled scroll handler for efficient scrolling
|
|
190
|
+
*/
|
|
191
|
+
export const createThrottledScrollHandler = (
|
|
192
|
+
handler: (scrollTop: number, scrollLeft: number) => void,
|
|
193
|
+
delay = 16 // 60fps
|
|
194
|
+
) => {
|
|
195
|
+
return throttle((event: Event) => {
|
|
196
|
+
const target = event.target as HTMLElement;
|
|
197
|
+
handler(target.scrollTop, target.scrollLeft);
|
|
198
|
+
}, delay);
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Intersection Observer for lazy loading
|
|
203
|
+
*/
|
|
204
|
+
export class LazyLoadManager {
|
|
205
|
+
private observer: IntersectionObserver;
|
|
206
|
+
private loadedItems = new Set<string>();
|
|
207
|
+
|
|
208
|
+
constructor(
|
|
209
|
+
private loadCallback: (element: Element) => void,
|
|
210
|
+
options: IntersectionObserverInit = {}
|
|
211
|
+
) {
|
|
212
|
+
const defaultOptions: IntersectionObserverInit = {
|
|
213
|
+
root: null,
|
|
214
|
+
rootMargin: '50px',
|
|
215
|
+
threshold: 0.1,
|
|
216
|
+
...options
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
this.observer = new IntersectionObserver((entries) => {
|
|
220
|
+
entries.forEach(entry => {
|
|
221
|
+
if (entry.isIntersecting && !this.loadedItems.has(entry.target.id)) {
|
|
222
|
+
this.loadedItems.add(entry.target.id);
|
|
223
|
+
this.loadCallback(entry.target);
|
|
224
|
+
this.observer.unobserve(entry.target);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}, defaultOptions);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
observe(element: Element) {
|
|
231
|
+
if (!this.loadedItems.has(element.id)) {
|
|
232
|
+
this.observer.observe(element);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
unobserve(element: Element) {
|
|
237
|
+
this.observer.unobserve(element);
|
|
238
|
+
this.loadedItems.delete(element.id);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
disconnect() {
|
|
242
|
+
this.observer.disconnect();
|
|
243
|
+
this.loadedItems.clear();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Memory management utilities
|
|
249
|
+
*/
|
|
250
|
+
export class MemoryManager {
|
|
251
|
+
private caches = new Map<string, Map<string, any>>();
|
|
252
|
+
private maxCacheSize = 100;
|
|
253
|
+
|
|
254
|
+
createLRUCache<T>(name: string, maxSize = this.maxCacheSize): LRUCache<T> {
|
|
255
|
+
const cache = new LRUCache<T>(maxSize);
|
|
256
|
+
this.caches.set(name, cache.storage);
|
|
257
|
+
return cache;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
clearCaches() {
|
|
261
|
+
this.caches.forEach(cache => cache.clear());
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
getCacheStats() {
|
|
265
|
+
const stats: Record<string, { size: number; maxSize: number }> = {};
|
|
266
|
+
|
|
267
|
+
this.caches.forEach((cache, name) => {
|
|
268
|
+
stats[name] = {
|
|
269
|
+
size: cache.size,
|
|
270
|
+
maxSize: this.maxCacheSize
|
|
271
|
+
};
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
return stats;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
getMemoryUsage(): number {
|
|
278
|
+
if ('memory' in performance) {
|
|
279
|
+
return (performance as any).memory.usedJSHeapSize;
|
|
280
|
+
}
|
|
281
|
+
return 0;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
onMemoryPressure(callback: () => void) {
|
|
285
|
+
if ('memory' in performance) {
|
|
286
|
+
// Monitor memory usage and trigger callback when high
|
|
287
|
+
const checkMemory = () => {
|
|
288
|
+
const usage = this.getMemoryUsage();
|
|
289
|
+
const limit = 50 * 1024 * 1024; // 50MB threshold
|
|
290
|
+
|
|
291
|
+
if (usage > limit) {
|
|
292
|
+
callback();
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
setInterval(checkMemory, 5000); // Check every 5 seconds
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* LRU Cache implementation
|
|
303
|
+
*/
|
|
304
|
+
export class LRUCache<T> {
|
|
305
|
+
public storage = new Map<string, T>();
|
|
306
|
+
private accessOrder = new Map<string, number>();
|
|
307
|
+
private accessCounter = 0;
|
|
308
|
+
|
|
309
|
+
constructor(private maxSize: number) {}
|
|
310
|
+
|
|
311
|
+
get(key: string): T | undefined {
|
|
312
|
+
const value = this.storage.get(key);
|
|
313
|
+
if (value !== undefined) {
|
|
314
|
+
this.accessOrder.set(key, ++this.accessCounter);
|
|
315
|
+
}
|
|
316
|
+
return value;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
set(key: string, value: T): void {
|
|
320
|
+
if (this.storage.size >= this.maxSize && !this.storage.has(key)) {
|
|
321
|
+
this.evictLeastRecentlyUsed();
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
this.storage.set(key, value);
|
|
325
|
+
this.accessOrder.set(key, ++this.accessCounter);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
has(key: string): boolean {
|
|
329
|
+
return this.storage.has(key);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
delete(key: string): boolean {
|
|
333
|
+
this.accessOrder.delete(key);
|
|
334
|
+
return this.storage.delete(key);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
clear(): void {
|
|
338
|
+
this.storage.clear();
|
|
339
|
+
this.accessOrder.clear();
|
|
340
|
+
this.accessCounter = 0;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
private evictLeastRecentlyUsed(): void {
|
|
344
|
+
let oldestKey = '';
|
|
345
|
+
let oldestAccess = Infinity;
|
|
346
|
+
|
|
347
|
+
for (const [key, access] of this.accessOrder) {
|
|
348
|
+
if (access < oldestAccess) {
|
|
349
|
+
oldestAccess = access;
|
|
350
|
+
oldestKey = key;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (oldestKey) {
|
|
355
|
+
this.delete(oldestKey);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
get size(): number {
|
|
360
|
+
return this.storage.size;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Bundle size optimization utilities
|
|
366
|
+
*/
|
|
367
|
+
export class BundleOptimizer {
|
|
368
|
+
private loadedModules = new Set<string>();
|
|
369
|
+
private modulePromises = new Map<string, Promise<any>>();
|
|
370
|
+
|
|
371
|
+
async loadModule<T>(
|
|
372
|
+
moduleName: string,
|
|
373
|
+
importFunction: () => Promise<{ default: T }>
|
|
374
|
+
): Promise<T> {
|
|
375
|
+
if (this.loadedModules.has(moduleName)) {
|
|
376
|
+
return (await this.modulePromises.get(moduleName)!).default;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (!this.modulePromises.has(moduleName)) {
|
|
380
|
+
const promise = importFunction();
|
|
381
|
+
this.modulePromises.set(moduleName, promise);
|
|
382
|
+
|
|
383
|
+
promise.then(() => {
|
|
384
|
+
this.loadedModules.add(moduleName);
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const module = await this.modulePromises.get(moduleName)!;
|
|
389
|
+
return module.default;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
getLoadedModules(): string[] {
|
|
393
|
+
return Array.from(this.loadedModules);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
preloadModule(moduleName: string, importFunction: () => Promise<any>): void {
|
|
397
|
+
if (!this.loadedModules.has(moduleName) && !this.modulePromises.has(moduleName)) {
|
|
398
|
+
this.loadModule(moduleName, importFunction);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Touch performance optimization
|
|
405
|
+
*/
|
|
406
|
+
export class TouchOptimizer {
|
|
407
|
+
private lastTouchTime = 0;
|
|
408
|
+
private touchLatencies: number[] = [];
|
|
409
|
+
|
|
410
|
+
createOptimizedTouchHandler<T extends Event>(
|
|
411
|
+
handler: (event: T) => void,
|
|
412
|
+
options: { passive?: boolean; capture?: boolean } = {}
|
|
413
|
+
) {
|
|
414
|
+
const optimizedHandler = (event: T) => {
|
|
415
|
+
const startTime = performance.now();
|
|
416
|
+
|
|
417
|
+
// Throttle rapid touch events
|
|
418
|
+
if (startTime - this.lastTouchTime < 16) {
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
this.lastTouchTime = startTime;
|
|
423
|
+
|
|
424
|
+
// Execute handler
|
|
425
|
+
handler(event);
|
|
426
|
+
|
|
427
|
+
// Track latency
|
|
428
|
+
const latency = performance.now() - startTime;
|
|
429
|
+
this.touchLatencies.push(latency);
|
|
430
|
+
|
|
431
|
+
// Keep only recent latencies
|
|
432
|
+
if (this.touchLatencies.length > 100) {
|
|
433
|
+
this.touchLatencies.shift();
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
return {
|
|
438
|
+
handler: optimizedHandler,
|
|
439
|
+
options: {
|
|
440
|
+
passive: true,
|
|
441
|
+
capture: false,
|
|
442
|
+
...options
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
getAverageTouchLatency(): number {
|
|
448
|
+
if (this.touchLatencies.length === 0) return 0;
|
|
449
|
+
|
|
450
|
+
const sum = this.touchLatencies.reduce((a, b) => a + b, 0);
|
|
451
|
+
return sum / this.touchLatencies.length;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
clearLatencyData(): void {
|
|
455
|
+
this.touchLatencies = [];
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Device-specific optimizations
|
|
461
|
+
*/
|
|
462
|
+
export class DeviceOptimizer {
|
|
463
|
+
private deviceInfo = this.getDeviceInfo();
|
|
464
|
+
|
|
465
|
+
private getDeviceInfo() {
|
|
466
|
+
const userAgent = navigator.userAgent;
|
|
467
|
+
const isMobile = /Mobile|Android|iPhone|iPad/.test(userAgent);
|
|
468
|
+
const isTablet = /iPad|Tablet/.test(userAgent);
|
|
469
|
+
const isLowEnd = this.isLowEndDevice();
|
|
470
|
+
|
|
471
|
+
return {
|
|
472
|
+
isMobile,
|
|
473
|
+
isTablet,
|
|
474
|
+
isDesktop: !isMobile && !isTablet,
|
|
475
|
+
isLowEnd,
|
|
476
|
+
pixelRatio: window.devicePixelRatio || 1,
|
|
477
|
+
connectionType: this.getConnectionType()
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
private isLowEndDevice(): boolean {
|
|
482
|
+
// Detect low-end devices based on available metrics
|
|
483
|
+
if ('memory' in navigator) {
|
|
484
|
+
return (navigator as any).memory.deviceMemory < 4; // Less than 4GB RAM
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if ('hardwareConcurrency' in navigator) {
|
|
488
|
+
return navigator.hardwareConcurrency < 4; // Less than 4 CPU cores
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
return false;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
private getConnectionType(): string {
|
|
495
|
+
if ('connection' in navigator) {
|
|
496
|
+
return (navigator as any).connection.effectiveType || 'unknown';
|
|
497
|
+
}
|
|
498
|
+
return 'unknown';
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
getOptimizationStrategy() {
|
|
502
|
+
const { isMobile, isTablet, isLowEnd, connectionType } = this.deviceInfo;
|
|
503
|
+
|
|
504
|
+
return {
|
|
505
|
+
// Rendering optimizations
|
|
506
|
+
enableVirtualization: isLowEnd || isMobile,
|
|
507
|
+
reduceAnimations: isLowEnd || connectionType === 'slow-2g',
|
|
508
|
+
lazyLoadImages: isMobile || connectionType === 'slow-2g',
|
|
509
|
+
|
|
510
|
+
// Interaction optimizations
|
|
511
|
+
debounceDelay: isLowEnd ? 500 : 250,
|
|
512
|
+
throttleDelay: isLowEnd ? 32 : 16,
|
|
513
|
+
|
|
514
|
+
// Memory optimizations
|
|
515
|
+
maxCacheSize: isLowEnd ? 20 : 100,
|
|
516
|
+
enableImageCompression: isMobile || connectionType === 'slow-2g',
|
|
517
|
+
|
|
518
|
+
// Bundle optimizations
|
|
519
|
+
enableCodeSplitting: true,
|
|
520
|
+
preloadCriticalModules: !isLowEnd,
|
|
521
|
+
|
|
522
|
+
// Touch optimizations
|
|
523
|
+
enablePassiveListeners: isMobile,
|
|
524
|
+
optimizeTouchTargets: isMobile || isTablet
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
getDeviceInfo() {
|
|
529
|
+
return { ...this.deviceInfo };
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Performance optimization hook factory
|
|
535
|
+
*/
|
|
536
|
+
export const createPerformanceOptimizations = () => {
|
|
537
|
+
const monitor = new PerformanceMonitor();
|
|
538
|
+
const memoryManager = new MemoryManager();
|
|
539
|
+
const bundleOptimizer = new BundleOptimizer();
|
|
540
|
+
const touchOptimizer = new TouchOptimizer();
|
|
541
|
+
const deviceOptimizer = new DeviceOptimizer();
|
|
542
|
+
|
|
543
|
+
const strategy = deviceOptimizer.getOptimizationStrategy();
|
|
544
|
+
|
|
545
|
+
return {
|
|
546
|
+
monitor,
|
|
547
|
+
memoryManager,
|
|
548
|
+
bundleOptimizer,
|
|
549
|
+
touchOptimizer,
|
|
550
|
+
deviceOptimizer,
|
|
551
|
+
strategy,
|
|
552
|
+
|
|
553
|
+
// Utility functions
|
|
554
|
+
debouncedResize: createDebouncedResizeHandler,
|
|
555
|
+
throttledScroll: createThrottledScrollHandler,
|
|
556
|
+
|
|
557
|
+
// Cleanup function
|
|
558
|
+
dispose: () => {
|
|
559
|
+
monitor.dispose();
|
|
560
|
+
memoryManager.clearCaches();
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
};
|