mtrl-addons 0.1.2 → 0.2.2
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/AI.md +28 -230
- package/CLAUDE.md +882 -0
- package/build.js +253 -24
- package/package.json +14 -4
- package/scripts/debug/vlist-selection.ts +121 -0
- package/src/components/index.ts +5 -41
- package/src/components/{list → vlist}/config.ts +66 -95
- package/src/components/vlist/constants.ts +23 -0
- package/src/components/vlist/features/api.ts +626 -0
- package/src/components/vlist/features/index.ts +10 -0
- package/src/components/vlist/features/selection.ts +436 -0
- package/src/components/vlist/features/viewport.ts +59 -0
- package/src/components/vlist/index.ts +17 -0
- package/src/components/{list → vlist}/types.ts +242 -32
- package/src/components/vlist/vlist.ts +92 -0
- package/src/core/compose/features/gestures/index.ts +227 -0
- package/src/core/compose/features/gestures/longpress.ts +383 -0
- package/src/core/compose/features/gestures/pan.ts +424 -0
- package/src/core/compose/features/gestures/pinch.ts +475 -0
- package/src/core/compose/features/gestures/rotate.ts +485 -0
- package/src/core/compose/features/gestures/swipe.ts +492 -0
- package/src/core/compose/features/gestures/tap.ts +334 -0
- package/src/core/compose/features/index.ts +2 -38
- package/src/core/compose/index.ts +13 -29
- package/src/core/gestures/index.ts +31 -0
- package/src/core/gestures/longpress.ts +68 -0
- package/src/core/gestures/manager.ts +418 -0
- package/src/core/gestures/pan.ts +48 -0
- package/src/core/gestures/pinch.ts +58 -0
- package/src/core/gestures/rotate.ts +58 -0
- package/src/core/gestures/swipe.ts +66 -0
- package/src/core/gestures/tap.ts +45 -0
- package/src/core/gestures/types.ts +387 -0
- package/src/core/gestures/utils.ts +128 -0
- package/src/core/index.ts +27 -151
- package/src/core/layout/schema.ts +153 -72
- package/src/core/layout/types.ts +5 -2
- package/src/core/viewport/constants.ts +145 -0
- package/src/core/viewport/features/base.ts +73 -0
- package/src/core/viewport/features/collection.ts +1182 -0
- package/src/core/viewport/features/events.ts +130 -0
- package/src/core/viewport/features/index.ts +20 -0
- package/src/core/{list-manager/features/viewport → viewport/features}/item-size.ts +31 -34
- package/src/core/{list-manager/features/viewport → viewport/features}/loading.ts +4 -4
- package/src/core/viewport/features/momentum.ts +269 -0
- package/src/core/viewport/features/placeholders.ts +335 -0
- package/src/core/viewport/features/rendering.ts +962 -0
- package/src/core/viewport/features/scrollbar.ts +434 -0
- package/src/core/viewport/features/scrolling.ts +634 -0
- package/src/core/viewport/features/utils.ts +94 -0
- package/src/core/viewport/features/virtual.ts +525 -0
- package/src/core/viewport/index.ts +31 -0
- package/src/core/viewport/types.ts +133 -0
- package/src/core/viewport/utils/speed-tracker.ts +79 -0
- package/src/core/viewport/viewport.ts +265 -0
- package/src/index.ts +0 -7
- package/src/styles/components/_vlist.scss +352 -0
- package/src/styles/index.scss +1 -1
- package/test/components/vlist-selection.test.ts +240 -0
- package/test/components/vlist.test.ts +63 -0
- package/test/core/collection/adapter.test.ts +161 -0
- package/bun.lock +0 -792
- package/src/components/list/api.ts +0 -314
- package/src/components/list/constants.ts +0 -56
- package/src/components/list/features/api.ts +0 -428
- package/src/components/list/features/index.ts +0 -31
- package/src/components/list/features/list-manager.ts +0 -502
- package/src/components/list/index.ts +0 -39
- package/src/components/list/list.ts +0 -234
- package/src/core/collection/base-collection.ts +0 -100
- package/src/core/collection/collection-composer.ts +0 -178
- package/src/core/collection/collection.ts +0 -745
- package/src/core/collection/constants.ts +0 -172
- package/src/core/collection/events.ts +0 -428
- package/src/core/collection/features/api/loading.ts +0 -279
- package/src/core/collection/features/operations/data-operations.ts +0 -147
- package/src/core/collection/index.ts +0 -104
- package/src/core/collection/state.ts +0 -497
- package/src/core/collection/types.ts +0 -404
- package/src/core/compose/features/collection.ts +0 -119
- package/src/core/compose/features/selection.ts +0 -213
- package/src/core/compose/features/styling.ts +0 -108
- package/src/core/list-manager/api.ts +0 -599
- package/src/core/list-manager/config.ts +0 -593
- package/src/core/list-manager/constants.ts +0 -268
- package/src/core/list-manager/features/api.ts +0 -58
- package/src/core/list-manager/features/collection/collection.ts +0 -705
- package/src/core/list-manager/features/collection/index.ts +0 -17
- package/src/core/list-manager/features/viewport/constants.ts +0 -42
- package/src/core/list-manager/features/viewport/index.ts +0 -16
- package/src/core/list-manager/features/viewport/placeholders.ts +0 -281
- package/src/core/list-manager/features/viewport/rendering.ts +0 -575
- package/src/core/list-manager/features/viewport/scrollbar.ts +0 -495
- package/src/core/list-manager/features/viewport/scrolling.ts +0 -795
- package/src/core/list-manager/features/viewport/template.ts +0 -220
- package/src/core/list-manager/features/viewport/viewport.ts +0 -654
- package/src/core/list-manager/features/viewport/virtual.ts +0 -309
- package/src/core/list-manager/index.ts +0 -279
- package/src/core/list-manager/list-manager.ts +0 -206
- package/src/core/list-manager/types.ts +0 -439
- package/src/core/list-manager/utils/calculations.ts +0 -290
- package/src/core/list-manager/utils/range-calculator.ts +0 -349
- package/src/core/list-manager/utils/speed-tracker.ts +0 -273
- package/src/styles/components/_list.scss +0 -244
- package/src/types/mtrl.d.ts +0 -6
- package/test/components/list.test.ts +0 -256
- package/test/core/collection/failed-ranges.test.ts +0 -270
- package/test/core/compose/features.test.ts +0 -183
- package/test/core/list-manager/features/collection.test.ts +0 -704
- package/test/core/list-manager/features/viewport.test.ts +0 -698
- package/test/core/list-manager/list-manager.test.ts +0 -593
- package/test/core/list-manager/utils/calculations.test.ts +0 -433
- package/test/core/list-manager/utils/range-calculator.test.ts +0 -569
- package/test/core/list-manager/utils/speed-tracker.test.ts +0 -530
- package/tsconfig.build.json +0 -23
- /package/src/components/{list → vlist}/features.ts +0 -0
- /package/src/core/{compose → viewport}/features/performance.ts +0 -0
|
@@ -1,502 +0,0 @@
|
|
|
1
|
-
import type { ListConfig, ListComponent, ListState } from "../types";
|
|
2
|
-
import type { BaseComponent, ElementComponent } from "mtrl/src/core/compose";
|
|
3
|
-
import type { CollectionItem } from "../../../core/collection/types";
|
|
4
|
-
import { createListManagerAPI } from "../../../core/list-manager/api";
|
|
5
|
-
import type { ListManagerConfig } from "../../../core/list-manager/types";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Calculate optimal collection limit based on viewport dimensions
|
|
9
|
-
* @param containerSize - Viewport container size in pixels
|
|
10
|
-
* @param estimatedItemSize - Estimated item size in pixels
|
|
11
|
-
* @param overscan - Number of items to render outside viewport
|
|
12
|
-
* @returns Optimal limit for collection API requests
|
|
13
|
-
*/
|
|
14
|
-
function calculateOptimalLimit(
|
|
15
|
-
containerSize: number,
|
|
16
|
-
estimatedItemSize: number,
|
|
17
|
-
overscan: number = 5
|
|
18
|
-
): number {
|
|
19
|
-
// Calculate how many items fit in the viewport
|
|
20
|
-
const itemsInViewport = Math.ceil(containerSize / estimatedItemSize);
|
|
21
|
-
|
|
22
|
-
// Add overscan buffer for smooth scrolling
|
|
23
|
-
const withOverscan = itemsInViewport + overscan * 2;
|
|
24
|
-
|
|
25
|
-
// Add preload buffer for better UX (50% more items)
|
|
26
|
-
const withPreload = Math.ceil(withOverscan * 1.5);
|
|
27
|
-
|
|
28
|
-
// Ensure minimum of 10 items and maximum of 100 items per request
|
|
29
|
-
const optimalLimit = Math.max(10, Math.min(100, withPreload));
|
|
30
|
-
|
|
31
|
-
return optimalLimit;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* List Manager feature that creates and manages the enhanced List Manager
|
|
36
|
-
* with built-in Collection support using Phase 1 functional composition.
|
|
37
|
-
*
|
|
38
|
-
* This replaces the complex orchestration layer with a simple feature
|
|
39
|
-
* that delegates everything to the List Manager.
|
|
40
|
-
*/
|
|
41
|
-
export const withListManager =
|
|
42
|
-
<T = any>(config: ListConfig<T>) =>
|
|
43
|
-
<C extends BaseComponent & ElementComponent>(
|
|
44
|
-
component: C
|
|
45
|
-
): C & Partial<ListComponent<T>> => {
|
|
46
|
-
console.log("🔍 [LIST-MANAGER] withListManager config:", {
|
|
47
|
-
hasPagination: !!config.pagination,
|
|
48
|
-
paginationLimit: config.pagination?.limit,
|
|
49
|
-
paginationStrategy: config.pagination?.strategy,
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
// Get container dimensions for optimal limit calculation
|
|
53
|
-
const containerElement = component.element;
|
|
54
|
-
const containerSize = containerElement.offsetHeight || 600; // Default to 600px
|
|
55
|
-
const estimatedItemSize = config.scroll?.estimatedItemSize || 50;
|
|
56
|
-
const overscan = config.scroll?.overscan || 5;
|
|
57
|
-
|
|
58
|
-
// Use fixed limit from pagination config if provided, otherwise calculate optimal limit
|
|
59
|
-
const pageSize =
|
|
60
|
-
config.pagination?.limit ||
|
|
61
|
-
calculateOptimalLimit(containerSize, estimatedItemSize, overscan);
|
|
62
|
-
|
|
63
|
-
// Convert List Config to Phase 1 List Manager Config
|
|
64
|
-
const listManagerConfig: ListManagerConfig = {
|
|
65
|
-
container: component.element,
|
|
66
|
-
items: config.items || [],
|
|
67
|
-
|
|
68
|
-
// Template configuration - config.template is already the converted function
|
|
69
|
-
template: {
|
|
70
|
-
template:
|
|
71
|
-
config.template ||
|
|
72
|
-
((item: any, index: number) =>
|
|
73
|
-
`<div>Item ${index}: ${JSON.stringify(item)}</div>`),
|
|
74
|
-
},
|
|
75
|
-
|
|
76
|
-
// Virtual scrolling configuration
|
|
77
|
-
virtual: {
|
|
78
|
-
enabled: true,
|
|
79
|
-
itemSize: config.scroll?.itemSize || "auto",
|
|
80
|
-
estimatedItemSize: config.scroll?.estimatedItemSize || 50,
|
|
81
|
-
overscan: config.scroll?.overscan || 5,
|
|
82
|
-
measureItems: config.scroll?.measureItems, // Pass measureItems option
|
|
83
|
-
},
|
|
84
|
-
|
|
85
|
-
// Orientation configuration
|
|
86
|
-
orientation: {
|
|
87
|
-
orientation: config.orientation?.orientation || "vertical",
|
|
88
|
-
reverse: config.orientation?.reverse || false,
|
|
89
|
-
crossAxisAlignment: config.orientation?.crossAxisAlignment || "stretch",
|
|
90
|
-
},
|
|
91
|
-
|
|
92
|
-
// Collection configuration (if using API) - Now uses pageSize from pagination config
|
|
93
|
-
collection: config.adapter
|
|
94
|
-
? {
|
|
95
|
-
adapter: config.adapter,
|
|
96
|
-
pageSize: pageSize, // Use fixed limit or calculated optimal limit
|
|
97
|
-
strategy: config.pagination?.strategy || ("page" as const),
|
|
98
|
-
}
|
|
99
|
-
: undefined,
|
|
100
|
-
|
|
101
|
-
// Required properties for ListManagerConfig
|
|
102
|
-
initialLoad: {
|
|
103
|
-
strategy: "placeholders" as const,
|
|
104
|
-
viewportMultiplier: 1.5,
|
|
105
|
-
minItems: 10,
|
|
106
|
-
maxItems: 100,
|
|
107
|
-
},
|
|
108
|
-
|
|
109
|
-
errorHandling: {
|
|
110
|
-
timeout: 3000,
|
|
111
|
-
showErrorItems: true,
|
|
112
|
-
retryAttempts: 2,
|
|
113
|
-
preserveScrollOnError: true,
|
|
114
|
-
},
|
|
115
|
-
|
|
116
|
-
positioning: {
|
|
117
|
-
precisePositioning: true,
|
|
118
|
-
allowPartialItems: true,
|
|
119
|
-
snapToItems: false,
|
|
120
|
-
},
|
|
121
|
-
|
|
122
|
-
boundaries: {
|
|
123
|
-
preventOverscroll: true,
|
|
124
|
-
maintainEdgeRanges: true,
|
|
125
|
-
boundaryResistance: 0.3,
|
|
126
|
-
},
|
|
127
|
-
|
|
128
|
-
recycling: {
|
|
129
|
-
enabled: true,
|
|
130
|
-
maxPoolSize: 50,
|
|
131
|
-
minPoolSize: 10,
|
|
132
|
-
},
|
|
133
|
-
|
|
134
|
-
performance: {
|
|
135
|
-
frameScheduling: true,
|
|
136
|
-
memoryCleanup: true,
|
|
137
|
-
},
|
|
138
|
-
|
|
139
|
-
intersection: {
|
|
140
|
-
pagination: {
|
|
141
|
-
enabled: false,
|
|
142
|
-
rootMargin: "0px",
|
|
143
|
-
threshold: 0.1,
|
|
144
|
-
},
|
|
145
|
-
loading: {
|
|
146
|
-
enabled: false,
|
|
147
|
-
},
|
|
148
|
-
},
|
|
149
|
-
|
|
150
|
-
// Debug and styling
|
|
151
|
-
debug: false,
|
|
152
|
-
prefix: config.prefix || "mtrl",
|
|
153
|
-
componentName: "list-manager",
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
// Create Phase 1 List Manager with functional composition
|
|
157
|
-
const manager = createListManagerAPI(listManagerConfig);
|
|
158
|
-
|
|
159
|
-
console.log(`📋 List Manager created with Phase 1 functional composition`);
|
|
160
|
-
|
|
161
|
-
// Simple state tracking (much simpler than orchestration)
|
|
162
|
-
const state: ListState = {
|
|
163
|
-
isLoading: false,
|
|
164
|
-
error: null,
|
|
165
|
-
isEmpty: true,
|
|
166
|
-
scrollTop: 0,
|
|
167
|
-
visibleRange: { start: 0, end: 0, count: 0 },
|
|
168
|
-
renderRange: { start: 0, end: 0, count: 0 },
|
|
169
|
-
selectedIndices: config.selection?.selectedIndices || [],
|
|
170
|
-
totalItems: 0,
|
|
171
|
-
isVirtual: config.scroll?.virtual ?? true,
|
|
172
|
-
animationEnabled: true,
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
// Store original component emit to avoid recursion
|
|
176
|
-
const originalEmit = (component as any).emit;
|
|
177
|
-
|
|
178
|
-
// Subscribe to List Manager events for component event emission
|
|
179
|
-
if (manager.on) {
|
|
180
|
-
manager.on("viewport:changed", (data: any) => {
|
|
181
|
-
state.visibleRange = data.visibleRange || {
|
|
182
|
-
start: 0,
|
|
183
|
-
end: 0,
|
|
184
|
-
count: 0,
|
|
185
|
-
};
|
|
186
|
-
state.scrollTop = data.scrollPosition || 0;
|
|
187
|
-
|
|
188
|
-
if (originalEmit) {
|
|
189
|
-
originalEmit.call(component, "viewport:change", {
|
|
190
|
-
visibleRange: state.visibleRange,
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
config.on?.onViewportChange?.(state.visibleRange);
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
manager.on("scroll:position:changed", (data: any) => {
|
|
197
|
-
state.scrollTop = data.position || 0;
|
|
198
|
-
if (originalEmit) {
|
|
199
|
-
originalEmit.call(component, "scroll:change", {
|
|
200
|
-
scrollTop: state.scrollTop,
|
|
201
|
-
direction: data.direction || "none",
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
config.on?.onScroll?.(state.scrollTop, data.direction || "none");
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
manager.on("range:loaded", (data: any) => {
|
|
208
|
-
if (originalEmit) {
|
|
209
|
-
originalEmit.call(component, "data:loaded", data);
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
manager.on("error:occurred", (error: any) => {
|
|
214
|
-
state.error = error;
|
|
215
|
-
if (originalEmit) {
|
|
216
|
-
originalEmit.call(component, "error", error);
|
|
217
|
-
}
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Handle item clicks
|
|
222
|
-
const handleItemClick = (
|
|
223
|
-
item: T,
|
|
224
|
-
index: number,
|
|
225
|
-
event: MouseEvent
|
|
226
|
-
): void => {
|
|
227
|
-
// Handle selection
|
|
228
|
-
if (config.selection?.enabled) {
|
|
229
|
-
if (state.selectedIndices.includes(index)) {
|
|
230
|
-
state.selectedIndices = state.selectedIndices.filter(
|
|
231
|
-
(i) => i !== index
|
|
232
|
-
);
|
|
233
|
-
} else {
|
|
234
|
-
if (config.selection.mode === "single") {
|
|
235
|
-
state.selectedIndices = [index];
|
|
236
|
-
} else {
|
|
237
|
-
state.selectedIndices = [...state.selectedIndices, index];
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// Emit click event
|
|
243
|
-
if (originalEmit) {
|
|
244
|
-
originalEmit.call(component, "item:click", { item, index, event });
|
|
245
|
-
}
|
|
246
|
-
config.on?.onItemClick?.(item, index, event);
|
|
247
|
-
|
|
248
|
-
// Emit selection events
|
|
249
|
-
const items = manager.getItems();
|
|
250
|
-
const selectedItems = state.selectedIndices
|
|
251
|
-
.map((i) => items[i])
|
|
252
|
-
.filter(Boolean);
|
|
253
|
-
|
|
254
|
-
if (originalEmit) {
|
|
255
|
-
originalEmit.call(component, "selection:change", {
|
|
256
|
-
selectedItems,
|
|
257
|
-
selectedIndices: [...state.selectedIndices],
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
// Initialize List Manager
|
|
263
|
-
const initialize = (): void => {
|
|
264
|
-
manager.initialize();
|
|
265
|
-
console.log(`🚀 List component initialized with Phase 1 List Manager`);
|
|
266
|
-
};
|
|
267
|
-
|
|
268
|
-
// Initialize on next tick
|
|
269
|
-
setTimeout(initialize, 0);
|
|
270
|
-
|
|
271
|
-
// Type assertion to access enhanced properties
|
|
272
|
-
const enhancedManager = manager as any;
|
|
273
|
-
|
|
274
|
-
// Return enhanced component with Phase 1 List Manager API
|
|
275
|
-
return Object.assign(component, {
|
|
276
|
-
// Core instances (simplified)
|
|
277
|
-
listManager: manager,
|
|
278
|
-
state,
|
|
279
|
-
config,
|
|
280
|
-
|
|
281
|
-
// Direct access to enhancers via type assertion
|
|
282
|
-
viewport: enhancedManager.viewport,
|
|
283
|
-
collection: enhancedManager.collection,
|
|
284
|
-
speedTracker: enhancedManager.speedTracker,
|
|
285
|
-
placeholders: enhancedManager.placeholders,
|
|
286
|
-
|
|
287
|
-
// Data operations (delegate to List Manager)
|
|
288
|
-
loadData: () =>
|
|
289
|
-
enhancedManager.collection?.loadRange(0, 20) || Promise.resolve([]),
|
|
290
|
-
reload: () => {
|
|
291
|
-
enhancedManager.collection?.getLoadedRanges().clear();
|
|
292
|
-
return (
|
|
293
|
-
enhancedManager.collection?.loadRange(0, 20) || Promise.resolve([])
|
|
294
|
-
);
|
|
295
|
-
},
|
|
296
|
-
clear: () => {
|
|
297
|
-
enhancedManager.collection?.setItems([]);
|
|
298
|
-
},
|
|
299
|
-
addItems: (items: T[], position: "start" | "end" = "end") => {
|
|
300
|
-
const currentItems = manager.getItems();
|
|
301
|
-
const newItems =
|
|
302
|
-
position === "start"
|
|
303
|
-
? [...items, ...currentItems]
|
|
304
|
-
: [...currentItems, ...items];
|
|
305
|
-
enhancedManager.collection?.setItems(newItems);
|
|
306
|
-
},
|
|
307
|
-
removeItems: (indices: number[]) => {
|
|
308
|
-
const currentItems = manager.getItems();
|
|
309
|
-
const newItems = currentItems.filter(
|
|
310
|
-
(_: any, index: number) => !indices.includes(index)
|
|
311
|
-
);
|
|
312
|
-
enhancedManager.collection?.setItems(newItems);
|
|
313
|
-
},
|
|
314
|
-
updateItem: (index: number, item: T) => {
|
|
315
|
-
const currentItems = [...manager.getItems()];
|
|
316
|
-
if (index >= 0 && index < currentItems.length) {
|
|
317
|
-
currentItems[index] = item;
|
|
318
|
-
enhancedManager.collection?.setItems(currentItems);
|
|
319
|
-
}
|
|
320
|
-
},
|
|
321
|
-
getItem: (index: number) => {
|
|
322
|
-
const items = manager.getItems();
|
|
323
|
-
return items[index];
|
|
324
|
-
},
|
|
325
|
-
getItems: () => manager.getItems(),
|
|
326
|
-
getItemCount: () => manager.getTotalItems(),
|
|
327
|
-
|
|
328
|
-
// Scrolling operations (delegate to viewport)
|
|
329
|
-
scrollToIndex: (
|
|
330
|
-
index: number,
|
|
331
|
-
alignment: "start" | "center" | "end" = "start",
|
|
332
|
-
animate?: boolean
|
|
333
|
-
) => {
|
|
334
|
-
enhancedManager.viewport?.scrollToIndex(index, alignment);
|
|
335
|
-
return Promise.resolve();
|
|
336
|
-
},
|
|
337
|
-
|
|
338
|
-
// NEW: Business logic for loading data ranges with positioning
|
|
339
|
-
loadRange: (
|
|
340
|
-
pageOrOffset: number,
|
|
341
|
-
size: number,
|
|
342
|
-
strategy: "page" | "offset" = "page",
|
|
343
|
-
alignment: "start" | "center" | "end" = "start"
|
|
344
|
-
) => {
|
|
345
|
-
// Delegate to the enhanced list manager's loadRange method
|
|
346
|
-
if (enhancedManager.loadRange) {
|
|
347
|
-
return enhancedManager.loadRange(
|
|
348
|
-
pageOrOffset,
|
|
349
|
-
size,
|
|
350
|
-
strategy,
|
|
351
|
-
alignment
|
|
352
|
-
);
|
|
353
|
-
} else {
|
|
354
|
-
console.error(
|
|
355
|
-
"❌ [LIST-COMPONENT] loadRange method not available on list manager"
|
|
356
|
-
);
|
|
357
|
-
return Promise.resolve();
|
|
358
|
-
}
|
|
359
|
-
},
|
|
360
|
-
|
|
361
|
-
scrollToItem: async (
|
|
362
|
-
itemId: string,
|
|
363
|
-
alignment: "start" | "center" | "end" = "start",
|
|
364
|
-
animate?: boolean
|
|
365
|
-
) => {
|
|
366
|
-
// Find item in current data
|
|
367
|
-
const items = manager.getItems();
|
|
368
|
-
const index = items.findIndex(
|
|
369
|
-
(item: any) => String(item.id) === String(itemId)
|
|
370
|
-
);
|
|
371
|
-
|
|
372
|
-
if (index >= 0) {
|
|
373
|
-
enhancedManager.viewport?.scrollToIndex(index, alignment);
|
|
374
|
-
return Promise.resolve();
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
console.warn(`Item ${itemId} not found in current data`);
|
|
378
|
-
return Promise.resolve();
|
|
379
|
-
},
|
|
380
|
-
|
|
381
|
-
scrollToTop: () => {
|
|
382
|
-
enhancedManager.viewport?.scrollToIndex(0, "start");
|
|
383
|
-
return Promise.resolve();
|
|
384
|
-
},
|
|
385
|
-
|
|
386
|
-
scrollToBottom: () => {
|
|
387
|
-
const totalItems = manager.getTotalItems() || 0;
|
|
388
|
-
if (totalItems > 0) {
|
|
389
|
-
enhancedManager.viewport?.scrollToIndex(totalItems - 1, "end");
|
|
390
|
-
}
|
|
391
|
-
return Promise.resolve();
|
|
392
|
-
},
|
|
393
|
-
|
|
394
|
-
getScrollPosition: () =>
|
|
395
|
-
enhancedManager.viewport?.getScrollPosition() || 0,
|
|
396
|
-
|
|
397
|
-
// Scroll animation control
|
|
398
|
-
setScrollAnimation: (enabled: boolean) => {
|
|
399
|
-
// This would need to be implemented in viewport
|
|
400
|
-
return component;
|
|
401
|
-
},
|
|
402
|
-
getScrollAnimation: () => true,
|
|
403
|
-
toggleScrollAnimation: () => {
|
|
404
|
-
// This would need to be implemented in viewport
|
|
405
|
-
return component;
|
|
406
|
-
},
|
|
407
|
-
|
|
408
|
-
// Selection operations
|
|
409
|
-
selectItems: (indices: number[]) => {
|
|
410
|
-
state.selectedIndices = [...indices];
|
|
411
|
-
},
|
|
412
|
-
deselectItems: (indices: number[]) => {
|
|
413
|
-
state.selectedIndices = state.selectedIndices.filter(
|
|
414
|
-
(i) => !indices.includes(i)
|
|
415
|
-
);
|
|
416
|
-
},
|
|
417
|
-
clearSelection: () => {
|
|
418
|
-
state.selectedIndices = [];
|
|
419
|
-
},
|
|
420
|
-
getSelectedItems: () => {
|
|
421
|
-
const items = manager.getItems();
|
|
422
|
-
return state.selectedIndices.map((i) => items[i]).filter(Boolean);
|
|
423
|
-
},
|
|
424
|
-
getSelectedIndices: () => [...state.selectedIndices],
|
|
425
|
-
isSelected: (index: number) => state.selectedIndices.includes(index),
|
|
426
|
-
|
|
427
|
-
// Template management
|
|
428
|
-
setTemplate: (
|
|
429
|
-
newTemplate: (item: T, index: number) => string | HTMLElement
|
|
430
|
-
) => {
|
|
431
|
-
// This would need to be implemented to update the manager's template
|
|
432
|
-
console.warn("setTemplate not yet implemented in Phase 1 List Manager");
|
|
433
|
-
},
|
|
434
|
-
|
|
435
|
-
// Additional methods for compatibility
|
|
436
|
-
hasNext: () =>
|
|
437
|
-
enhancedManager.collection?.getPendingRanges().size > 0 || false,
|
|
438
|
-
hasPrevious: () => false, // Not implemented yet
|
|
439
|
-
getSelectedIds: () => {
|
|
440
|
-
const items = manager.getItems();
|
|
441
|
-
return state.selectedIndices.map((i) => items[i]?.id).filter(Boolean);
|
|
442
|
-
},
|
|
443
|
-
getMetrics: () => ({
|
|
444
|
-
renderCount: 0,
|
|
445
|
-
scrollCount: 0,
|
|
446
|
-
averageRenderTime: 0,
|
|
447
|
-
averageScrollTime: 0,
|
|
448
|
-
memoryUsage: 0,
|
|
449
|
-
recycledElements: 0,
|
|
450
|
-
speedTracker: enhancedManager.speedTracker?.getTracker() || null,
|
|
451
|
-
placeholderStructure:
|
|
452
|
-
enhancedManager.placeholders?.getPlaceholderStructure() || null,
|
|
453
|
-
}),
|
|
454
|
-
|
|
455
|
-
// State queries
|
|
456
|
-
getState: () => ({ ...state }),
|
|
457
|
-
isLoading: () =>
|
|
458
|
-
enhancedManager.collection?.getPendingRanges().size > 0 || false,
|
|
459
|
-
hasError: () => state.error !== null,
|
|
460
|
-
isEmpty: () => (manager.getTotalItems() || 0) === 0,
|
|
461
|
-
|
|
462
|
-
// Rendering
|
|
463
|
-
render: () => {
|
|
464
|
-
// Phase 1 List Manager handles rendering internally
|
|
465
|
-
},
|
|
466
|
-
updateViewport: () => enhancedManager.viewport?.updateViewport(),
|
|
467
|
-
getVisibleRange: () =>
|
|
468
|
-
enhancedManager.viewport?.getVisibleRange() || { start: 0, end: 0 },
|
|
469
|
-
getRenderRange: () =>
|
|
470
|
-
enhancedManager.viewport?.getVisibleRange() || { start: 0, end: 0 },
|
|
471
|
-
|
|
472
|
-
// Configuration
|
|
473
|
-
updateConfig: (newConfig: Partial<ListConfig<T>>) => {
|
|
474
|
-
Object.assign(config, newConfig);
|
|
475
|
-
manager.updateConfig(newConfig as any);
|
|
476
|
-
},
|
|
477
|
-
getConfig: () => ({ ...config }),
|
|
478
|
-
|
|
479
|
-
// Lifecycle
|
|
480
|
-
lifecycle: {
|
|
481
|
-
init: initialize,
|
|
482
|
-
destroy: () => {
|
|
483
|
-
manager.destroy();
|
|
484
|
-
},
|
|
485
|
-
update: () => {
|
|
486
|
-
// List Manager handles updates internally
|
|
487
|
-
},
|
|
488
|
-
},
|
|
489
|
-
|
|
490
|
-
// Event subscription
|
|
491
|
-
subscribe: (observer: (payload: any) => void) => {
|
|
492
|
-
return manager.on || (() => {});
|
|
493
|
-
},
|
|
494
|
-
|
|
495
|
-
// Event emission
|
|
496
|
-
emit: (event: string, data: any) => {
|
|
497
|
-
if (originalEmit) {
|
|
498
|
-
originalEmit.call(component, event, data);
|
|
499
|
-
}
|
|
500
|
-
},
|
|
501
|
-
}) as unknown as C & Partial<ListComponent<T>>;
|
|
502
|
-
};
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* mtrl-addons List Component
|
|
3
|
-
*
|
|
4
|
-
* Next-generation list component powered by the collection system.
|
|
5
|
-
* Built for high performance, template-driven rendering, and modern UX.
|
|
6
|
-
*
|
|
7
|
-
* @module components/list
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
// Main component export
|
|
11
|
-
export { createList as default, createList } from "./list";
|
|
12
|
-
|
|
13
|
-
// Type exports
|
|
14
|
-
export type {
|
|
15
|
-
ListConfig,
|
|
16
|
-
ListComponent,
|
|
17
|
-
ListAPI,
|
|
18
|
-
ListState,
|
|
19
|
-
ListEvents,
|
|
20
|
-
ListItemTemplate,
|
|
21
|
-
ListScrollConfig,
|
|
22
|
-
ListSelectionConfig,
|
|
23
|
-
ListEventHandlers,
|
|
24
|
-
ListItemContext,
|
|
25
|
-
} from "./types";
|
|
26
|
-
|
|
27
|
-
// Configuration exports
|
|
28
|
-
export {
|
|
29
|
-
createBaseConfig,
|
|
30
|
-
getElementConfig,
|
|
31
|
-
getApiConfig,
|
|
32
|
-
getCollectionConfig,
|
|
33
|
-
getListManagerConfig,
|
|
34
|
-
validateConfig,
|
|
35
|
-
} from "./config";
|
|
36
|
-
|
|
37
|
-
// Feature exports
|
|
38
|
-
export { withListManager } from "./features/list-manager";
|
|
39
|
-
export { withAPI } from "./api";
|