mtrl-addons 0.1.0 → 0.1.1
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/.cursorrules +117 -0
- package/AI.md +241 -0
- package/build.js +170 -0
- package/bun.lock +792 -0
- package/index.ts +7 -0
- package/package.json +10 -17
- package/scripts/analyze-orphaned-functions.ts +387 -0
- package/src/components/index.ts +45 -0
- package/src/components/list/api.ts +314 -0
- package/src/components/list/config.ts +352 -0
- package/src/components/list/constants.ts +56 -0
- package/src/components/list/features/api.ts +428 -0
- package/src/components/list/features/index.ts +31 -0
- package/src/components/list/features/list-manager.ts +502 -0
- package/src/components/list/features.ts +112 -0
- package/src/components/list/index.ts +39 -0
- package/src/components/list/list.ts +234 -0
- package/src/components/list/types.ts +513 -0
- package/src/core/collection/base-collection.ts +100 -0
- package/src/core/collection/collection-composer.ts +178 -0
- package/src/core/collection/collection.ts +745 -0
- package/src/core/collection/constants.ts +172 -0
- package/src/core/collection/events.ts +428 -0
- package/src/core/collection/features/api/loading.ts +279 -0
- package/src/core/collection/features/operations/data-operations.ts +147 -0
- package/src/core/collection/index.ts +104 -0
- package/src/core/collection/state.ts +497 -0
- package/src/core/collection/types.ts +404 -0
- package/src/core/compose/features/collection.ts +119 -0
- package/src/core/compose/features/index.ts +39 -0
- package/src/core/compose/features/performance.ts +161 -0
- package/src/core/compose/features/selection.ts +213 -0
- package/src/core/compose/features/styling.ts +108 -0
- package/src/core/compose/index.ts +31 -0
- package/src/core/index.ts +167 -0
- package/src/core/layout/config.ts +102 -0
- package/src/core/layout/index.ts +168 -0
- package/src/core/layout/jsx.ts +174 -0
- package/src/core/layout/schema.ts +963 -0
- package/src/core/layout/types.ts +92 -0
- package/src/core/list-manager/api.ts +599 -0
- package/src/core/list-manager/config.ts +593 -0
- package/src/core/list-manager/constants.ts +268 -0
- package/src/core/list-manager/features/api.ts +58 -0
- package/src/core/list-manager/features/collection/collection.ts +705 -0
- package/src/core/list-manager/features/collection/index.ts +17 -0
- package/src/core/list-manager/features/viewport/constants.ts +42 -0
- package/src/core/list-manager/features/viewport/index.ts +16 -0
- package/src/core/list-manager/features/viewport/item-size.ts +274 -0
- package/src/core/list-manager/features/viewport/loading.ts +263 -0
- package/src/core/list-manager/features/viewport/placeholders.ts +281 -0
- package/src/core/list-manager/features/viewport/rendering.ts +575 -0
- package/src/core/list-manager/features/viewport/scrollbar.ts +495 -0
- package/src/core/list-manager/features/viewport/scrolling.ts +795 -0
- package/src/core/list-manager/features/viewport/template.ts +220 -0
- package/src/core/list-manager/features/viewport/viewport.ts +654 -0
- package/src/core/list-manager/features/viewport/virtual.ts +309 -0
- package/src/core/list-manager/index.ts +279 -0
- package/src/core/list-manager/list-manager.ts +206 -0
- package/src/core/list-manager/types.ts +439 -0
- package/src/core/list-manager/utils/calculations.ts +290 -0
- package/src/core/list-manager/utils/range-calculator.ts +349 -0
- package/src/core/list-manager/utils/speed-tracker.ts +273 -0
- package/src/index.ts +17 -0
- package/src/styles/components/_list.scss +244 -0
- package/src/styles/index.scss +12 -0
- package/src/types/mtrl.d.ts +6 -0
- package/test/benchmarks/layout/advanced.test.ts +656 -0
- package/test/benchmarks/layout/comparison.test.ts +519 -0
- package/test/benchmarks/layout/performance-comparison.test.ts +274 -0
- package/test/benchmarks/layout/real-components.test.ts +733 -0
- package/test/benchmarks/layout/simple.test.ts +321 -0
- package/test/benchmarks/layout/stress.test.ts +990 -0
- package/test/collection/basic.test.ts +304 -0
- package/test/components/list.test.ts +256 -0
- package/test/core/collection/collection.test.ts +394 -0
- package/test/core/collection/failed-ranges.test.ts +270 -0
- package/test/core/compose/features.test.ts +183 -0
- package/test/core/layout/layout.test.ts +201 -0
- package/test/core/list-manager/features/collection.test.ts +704 -0
- package/test/core/list-manager/features/viewport.test.ts +698 -0
- package/test/core/list-manager/list-manager.test.ts +593 -0
- package/test/core/list-manager/utils/calculations.test.ts +433 -0
- package/test/core/list-manager/utils/range-calculator.test.ts +569 -0
- package/test/core/list-manager/utils/speed-tracker.test.ts +530 -0
- package/test/utils/dom-helpers.ts +275 -0
- package/test/utils/performance-helpers.ts +392 -0
- package/tsconfig.build.json +14 -0
- package/tsconfig.json +20 -0
- package/dist/index.d.ts +0 -5
- package/dist/index.js +0 -38
- package/dist/index.mjs +0 -8
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the mtrl-addons list component
|
|
3
|
+
*
|
|
4
|
+
* Extends collection types with list-specific functionality
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type {
|
|
8
|
+
CollectionItem,
|
|
9
|
+
CollectionConfig,
|
|
10
|
+
Collection,
|
|
11
|
+
TemplateDefinition,
|
|
12
|
+
} from "../../core/collection";
|
|
13
|
+
import type { BaseComponent, ElementComponent } from "mtrl/src/core/compose";
|
|
14
|
+
import type {
|
|
15
|
+
ListManager,
|
|
16
|
+
ListManagerConfig,
|
|
17
|
+
} from "../../core/list-manager/types";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* List item interface - extends collection item
|
|
21
|
+
*/
|
|
22
|
+
export interface ListItem extends CollectionItem {
|
|
23
|
+
id: string;
|
|
24
|
+
[key: string]: any;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* List adapter interface - extends collection adapter
|
|
29
|
+
*/
|
|
30
|
+
export interface ListAdapter<T extends ListItem = ListItem> {
|
|
31
|
+
read(params?: ListAdapterParams): Promise<ListAdapterResponse<T>>;
|
|
32
|
+
write?(items: T[]): Promise<ListAdapterResponse<T>>;
|
|
33
|
+
delete?(ids: string[]): Promise<ListAdapterResponse<T>>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* List adapter parameters
|
|
38
|
+
*/
|
|
39
|
+
export interface ListAdapterParams {
|
|
40
|
+
page?: number;
|
|
41
|
+
pageSize?: number;
|
|
42
|
+
offset?: number;
|
|
43
|
+
cursor?: string;
|
|
44
|
+
search?: string;
|
|
45
|
+
filters?: Record<string, any>;
|
|
46
|
+
sort?: {
|
|
47
|
+
field: string;
|
|
48
|
+
direction: "asc" | "desc";
|
|
49
|
+
}[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* List adapter response
|
|
54
|
+
*/
|
|
55
|
+
export interface ListAdapterResponse<T extends ListItem = ListItem> {
|
|
56
|
+
items: T[];
|
|
57
|
+
meta?: {
|
|
58
|
+
total?: number;
|
|
59
|
+
page?: number;
|
|
60
|
+
pageSize?: number;
|
|
61
|
+
hasNext?: boolean;
|
|
62
|
+
hasPrev?: boolean;
|
|
63
|
+
cursor?: string;
|
|
64
|
+
};
|
|
65
|
+
error?: {
|
|
66
|
+
message: string;
|
|
67
|
+
code?: string;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* List template definition
|
|
73
|
+
*/
|
|
74
|
+
export type ListTemplate = TemplateDefinition;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* List item template function
|
|
78
|
+
*/
|
|
79
|
+
export type ListItemTemplate<T = any> = (
|
|
80
|
+
item: T,
|
|
81
|
+
index: number
|
|
82
|
+
) => string | HTMLElement;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* List scroll behavior configuration
|
|
86
|
+
*/
|
|
87
|
+
export interface ListScrollConfig {
|
|
88
|
+
/** Enable virtual scrolling */
|
|
89
|
+
virtual?: boolean;
|
|
90
|
+
/** Item size (fixed) or 'auto' for dynamic - height for vertical, width for horizontal */
|
|
91
|
+
itemSize?: number | "auto";
|
|
92
|
+
/** Estimated item size for dynamic sizing */
|
|
93
|
+
estimatedItemSize?: number;
|
|
94
|
+
/** Number of items to render outside viewport */
|
|
95
|
+
overscan?: number;
|
|
96
|
+
/** Enable scroll animations */
|
|
97
|
+
animation?: boolean;
|
|
98
|
+
/** Restore scroll position on reload */
|
|
99
|
+
restorePosition?: boolean;
|
|
100
|
+
/** Enable item measurement for dynamic sizing (default: false) */
|
|
101
|
+
measureItems?: boolean;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* List styling configuration
|
|
106
|
+
*/
|
|
107
|
+
export interface ListStyleConfig {
|
|
108
|
+
/** CSS class prefix */
|
|
109
|
+
prefix?: string;
|
|
110
|
+
/** Component name for class generation */
|
|
111
|
+
componentName?: string;
|
|
112
|
+
/** Additional CSS classes */
|
|
113
|
+
className?: string;
|
|
114
|
+
/** List variant */
|
|
115
|
+
variant?: "default" | "dense" | "comfortable";
|
|
116
|
+
/** List density */
|
|
117
|
+
density?: "default" | "compact" | "comfortable";
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* List event handlers
|
|
122
|
+
*/
|
|
123
|
+
export interface ListEventHandlers<T = any> {
|
|
124
|
+
/** Item click handler */
|
|
125
|
+
onItemClick?: (item: T, index: number, event: MouseEvent) => void;
|
|
126
|
+
/** Item selection change */
|
|
127
|
+
onSelectionChange?: (selectedItems: T[], selectedIndices: number[]) => void;
|
|
128
|
+
/** Scroll event */
|
|
129
|
+
onScroll?: (scrollTop: number, direction: "up" | "down" | "none") => void;
|
|
130
|
+
/** Viewport change event */
|
|
131
|
+
onViewportChange?: (visibleRange: { start: number; end: number }) => void;
|
|
132
|
+
/** Load more data event */
|
|
133
|
+
onLoadMore?: (direction: "forward" | "backward") => void | Promise<void>;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* List selection configuration
|
|
138
|
+
*/
|
|
139
|
+
export interface ListSelectionConfig {
|
|
140
|
+
/** Enable selection */
|
|
141
|
+
enabled?: boolean;
|
|
142
|
+
/** Selection mode */
|
|
143
|
+
mode?: "single" | "multiple" | "none";
|
|
144
|
+
/** Initially selected indices */
|
|
145
|
+
selectedIndices?: number[];
|
|
146
|
+
/** Selection change callback */
|
|
147
|
+
onSelectionChange?: (selectedItems: any[], selectedIndices: number[]) => void;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* List orientation configuration
|
|
152
|
+
*/
|
|
153
|
+
export interface ListOrientationConfig {
|
|
154
|
+
/** List orientation */
|
|
155
|
+
orientation?: "horizontal" | "vertical";
|
|
156
|
+
/** Whether to auto-detect orientation based on container */
|
|
157
|
+
autoDetect?: boolean;
|
|
158
|
+
/** Reverse direction (RTL for horizontal, bottom-to-top for vertical) */
|
|
159
|
+
reverse?: boolean;
|
|
160
|
+
/** Cross-axis alignment */
|
|
161
|
+
crossAxisAlignment?: "start" | "center" | "end" | "stretch";
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* List pagination configuration
|
|
166
|
+
*/
|
|
167
|
+
export interface ListPaginationConfig {
|
|
168
|
+
/** Pagination strategy */
|
|
169
|
+
strategy?: "page" | "offset" | "cursor";
|
|
170
|
+
/** Fixed page size/limit (overrides viewport-based calculation) */
|
|
171
|
+
limit?: number;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Complete List component configuration
|
|
176
|
+
*/
|
|
177
|
+
export interface ListConfig<T = any> extends ListStyleConfig {
|
|
178
|
+
// Data layer (Collection) configuration
|
|
179
|
+
collection?: Partial<CollectionConfig<T>>;
|
|
180
|
+
|
|
181
|
+
// Performance layer (List Manager) configuration
|
|
182
|
+
listManager?: Partial<ListManagerConfig>;
|
|
183
|
+
|
|
184
|
+
// Pagination configuration
|
|
185
|
+
pagination?: ListPaginationConfig;
|
|
186
|
+
|
|
187
|
+
// Presentation layer configuration
|
|
188
|
+
/** Container element or selector */
|
|
189
|
+
container?: HTMLElement | string;
|
|
190
|
+
|
|
191
|
+
/** List orientation configuration */
|
|
192
|
+
orientation?: ListOrientationConfig;
|
|
193
|
+
|
|
194
|
+
/** Item template function */
|
|
195
|
+
template?: ListItemTemplate<T>;
|
|
196
|
+
|
|
197
|
+
/** Static items (for non-API lists) */
|
|
198
|
+
items?: T[];
|
|
199
|
+
|
|
200
|
+
/** Data adapter (can be passed at top level or nested under collection) */
|
|
201
|
+
adapter?: ListAdapter<T>;
|
|
202
|
+
|
|
203
|
+
/** Scroll behavior configuration */
|
|
204
|
+
scroll?: ListScrollConfig;
|
|
205
|
+
|
|
206
|
+
/** Selection configuration */
|
|
207
|
+
selection?: ListSelectionConfig;
|
|
208
|
+
|
|
209
|
+
/** Event handlers */
|
|
210
|
+
on?: ListEventHandlers<T>;
|
|
211
|
+
|
|
212
|
+
/** Enable debugging */
|
|
213
|
+
debug?: boolean;
|
|
214
|
+
|
|
215
|
+
/** Accessibility label */
|
|
216
|
+
ariaLabel?: string;
|
|
217
|
+
|
|
218
|
+
/** Loading message template */
|
|
219
|
+
loadingTemplate?: string | (() => string | HTMLElement);
|
|
220
|
+
|
|
221
|
+
/** Empty state template */
|
|
222
|
+
emptyTemplate?: string | (() => string | HTMLElement);
|
|
223
|
+
|
|
224
|
+
/** Error state template */
|
|
225
|
+
errorTemplate?: string | ((error: Error) => string | HTMLElement);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* List state interface
|
|
230
|
+
*/
|
|
231
|
+
export interface ListState {
|
|
232
|
+
/** Current loading state */
|
|
233
|
+
isLoading: boolean;
|
|
234
|
+
|
|
235
|
+
/** Current error state */
|
|
236
|
+
error: Error | null;
|
|
237
|
+
|
|
238
|
+
/** Whether list is empty */
|
|
239
|
+
isEmpty: boolean;
|
|
240
|
+
|
|
241
|
+
/** Current scroll position */
|
|
242
|
+
scrollTop: number;
|
|
243
|
+
|
|
244
|
+
/** Current visible range */
|
|
245
|
+
visibleRange: { start: number; end: number; count: number };
|
|
246
|
+
|
|
247
|
+
/** Current render range */
|
|
248
|
+
renderRange: { start: number; end: number; count: number };
|
|
249
|
+
|
|
250
|
+
/** Selected item indices */
|
|
251
|
+
selectedIndices: number[];
|
|
252
|
+
|
|
253
|
+
/** Total number of items */
|
|
254
|
+
totalItems: number;
|
|
255
|
+
|
|
256
|
+
/** Whether virtual scrolling is active */
|
|
257
|
+
isVirtual: boolean;
|
|
258
|
+
|
|
259
|
+
/** Whether scrolling animations are enabled */
|
|
260
|
+
animationEnabled: boolean;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* List item rendering context
|
|
265
|
+
*/
|
|
266
|
+
export interface ListItemContext<T = any> {
|
|
267
|
+
/** Item data */
|
|
268
|
+
item: T;
|
|
269
|
+
|
|
270
|
+
/** Item index */
|
|
271
|
+
index: number;
|
|
272
|
+
|
|
273
|
+
/** Whether item is selected */
|
|
274
|
+
isSelected: boolean;
|
|
275
|
+
|
|
276
|
+
/** Whether item is visible in viewport */
|
|
277
|
+
isVisible: boolean;
|
|
278
|
+
|
|
279
|
+
/** Whether item is in render range */
|
|
280
|
+
isInRenderRange: boolean;
|
|
281
|
+
|
|
282
|
+
/** Item element (if rendered) */
|
|
283
|
+
element?: HTMLElement;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* List component events
|
|
288
|
+
*/
|
|
289
|
+
export interface ListEvents<T = any> {
|
|
290
|
+
/** Item clicked */
|
|
291
|
+
"item:click": { item: T; index: number; event: MouseEvent };
|
|
292
|
+
|
|
293
|
+
/** Item selected/deselected */
|
|
294
|
+
"item:selection:change": { item: T; index: number; isSelected: boolean };
|
|
295
|
+
|
|
296
|
+
/** Selection changed */
|
|
297
|
+
"selection:change": { selectedItems: T[]; selectedIndices: number[] };
|
|
298
|
+
|
|
299
|
+
/** Scroll position changed */
|
|
300
|
+
"scroll:change": { scrollTop: number; direction: "up" | "down" | "none" };
|
|
301
|
+
|
|
302
|
+
/** Viewport changed */
|
|
303
|
+
"viewport:change": {
|
|
304
|
+
visibleRange: { start: number; end: number; count: number };
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
/** Load more triggered */
|
|
308
|
+
"load:more": { direction: "forward" | "backward" };
|
|
309
|
+
|
|
310
|
+
/** Data loaded */
|
|
311
|
+
"data:loaded": { items: T[]; total: number };
|
|
312
|
+
|
|
313
|
+
/** Loading state changed */
|
|
314
|
+
"loading:change": { isLoading: boolean };
|
|
315
|
+
|
|
316
|
+
/** Error occurred */
|
|
317
|
+
error: { error: Error };
|
|
318
|
+
|
|
319
|
+
/** List rendered */
|
|
320
|
+
"render:complete": {
|
|
321
|
+
renderRange: { start: number; end: number; count: number };
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* List component API
|
|
327
|
+
*/
|
|
328
|
+
export interface ListAPI<T = any> {
|
|
329
|
+
// Data management
|
|
330
|
+
/** Load data */
|
|
331
|
+
loadData(): Promise<void>;
|
|
332
|
+
|
|
333
|
+
/** Reload data */
|
|
334
|
+
reload(): Promise<void>;
|
|
335
|
+
|
|
336
|
+
/** Clear all data */
|
|
337
|
+
clear(): void;
|
|
338
|
+
|
|
339
|
+
/** Add items */
|
|
340
|
+
addItems(items: T[], position?: "start" | "end"): void;
|
|
341
|
+
|
|
342
|
+
/** Remove items by indices */
|
|
343
|
+
removeItems(indices: number[]): void;
|
|
344
|
+
|
|
345
|
+
/** Update item at index */
|
|
346
|
+
updateItem(index: number, item: T): void;
|
|
347
|
+
|
|
348
|
+
/** Get item at index */
|
|
349
|
+
getItem(index: number): T | undefined;
|
|
350
|
+
|
|
351
|
+
/** Get all items */
|
|
352
|
+
getItems(): T[];
|
|
353
|
+
|
|
354
|
+
/** Get total item count */
|
|
355
|
+
getItemCount(): number;
|
|
356
|
+
|
|
357
|
+
// Scrolling
|
|
358
|
+
/** Scroll to item index */
|
|
359
|
+
scrollToIndex(
|
|
360
|
+
index: number,
|
|
361
|
+
alignment?: "start" | "center" | "end"
|
|
362
|
+
): Promise<void>;
|
|
363
|
+
|
|
364
|
+
/** Scroll to top */
|
|
365
|
+
scrollToTop(): Promise<void>;
|
|
366
|
+
|
|
367
|
+
/** Scroll to bottom */
|
|
368
|
+
scrollToBottom(): Promise<void>;
|
|
369
|
+
|
|
370
|
+
/** Get current scroll position */
|
|
371
|
+
getScrollPosition(): number;
|
|
372
|
+
|
|
373
|
+
// Animation control
|
|
374
|
+
/** Enable or disable scroll animations */
|
|
375
|
+
setAnimationEnabled(enabled: boolean): void;
|
|
376
|
+
|
|
377
|
+
/** Get current animation enabled state */
|
|
378
|
+
getAnimationEnabled(): boolean;
|
|
379
|
+
|
|
380
|
+
/** Toggle animation on/off */
|
|
381
|
+
toggleAnimation(): void;
|
|
382
|
+
|
|
383
|
+
// Selection
|
|
384
|
+
/** Select items by indices */
|
|
385
|
+
selectItems(indices: number[]): void;
|
|
386
|
+
|
|
387
|
+
/** Deselect items by indices */
|
|
388
|
+
deselectItems(indices: number[]): void;
|
|
389
|
+
|
|
390
|
+
/** Clear selection */
|
|
391
|
+
clearSelection(): void;
|
|
392
|
+
|
|
393
|
+
/** Get selected items */
|
|
394
|
+
getSelectedItems(): T[];
|
|
395
|
+
|
|
396
|
+
/** Get selected indices */
|
|
397
|
+
getSelectedIndices(): number[];
|
|
398
|
+
|
|
399
|
+
/** Check if item is selected */
|
|
400
|
+
isSelected(index: number): boolean;
|
|
401
|
+
|
|
402
|
+
// State
|
|
403
|
+
/** Get current list state */
|
|
404
|
+
getState(): ListState;
|
|
405
|
+
|
|
406
|
+
/** Check if list is loading */
|
|
407
|
+
isLoading(): boolean;
|
|
408
|
+
|
|
409
|
+
/** Check if list has error */
|
|
410
|
+
hasError(): boolean;
|
|
411
|
+
|
|
412
|
+
/** Check if list is empty */
|
|
413
|
+
isEmpty(): boolean;
|
|
414
|
+
|
|
415
|
+
// Rendering
|
|
416
|
+
/** Force re-render */
|
|
417
|
+
render(): void;
|
|
418
|
+
|
|
419
|
+
/** Update viewport */
|
|
420
|
+
updateViewport(): void;
|
|
421
|
+
|
|
422
|
+
/** Get visible range */
|
|
423
|
+
getVisibleRange(): { start: number; end: number; count: number };
|
|
424
|
+
|
|
425
|
+
/** Get render range */
|
|
426
|
+
getRenderRange(): { start: number; end: number; count: number };
|
|
427
|
+
|
|
428
|
+
// Templates
|
|
429
|
+
/** Update item template */
|
|
430
|
+
setTemplate(template: ListItemTemplate<T>): void;
|
|
431
|
+
|
|
432
|
+
/** Set loading template */
|
|
433
|
+
setLoadingTemplate(template: string | (() => string | HTMLElement)): void;
|
|
434
|
+
|
|
435
|
+
/** Set empty template */
|
|
436
|
+
setEmptyTemplate(template: string | (() => string | HTMLElement)): void;
|
|
437
|
+
|
|
438
|
+
/** Set error template */
|
|
439
|
+
setErrorTemplate(
|
|
440
|
+
template: string | ((error: Error) => string | HTMLElement)
|
|
441
|
+
): void;
|
|
442
|
+
|
|
443
|
+
// Configuration
|
|
444
|
+
/** Update list configuration */
|
|
445
|
+
updateConfig(config: Partial<ListConfig<T>>): void;
|
|
446
|
+
|
|
447
|
+
/** Get current configuration */
|
|
448
|
+
getConfig(): ListConfig<T>;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* List component interface (extends mtrl component patterns)
|
|
453
|
+
*/
|
|
454
|
+
export interface ListComponent<T = any>
|
|
455
|
+
extends BaseComponent,
|
|
456
|
+
ElementComponent,
|
|
457
|
+
ListAPI<T> {
|
|
458
|
+
/** Collection instance (data layer) */
|
|
459
|
+
collection: Collection<T>;
|
|
460
|
+
|
|
461
|
+
/** List manager instance (performance layer) */
|
|
462
|
+
listManager: ListManager;
|
|
463
|
+
|
|
464
|
+
/** Current list state */
|
|
465
|
+
state: ListState;
|
|
466
|
+
|
|
467
|
+
/** List configuration */
|
|
468
|
+
config: ListConfig<T>;
|
|
469
|
+
|
|
470
|
+
/** Component lifecycle methods */
|
|
471
|
+
lifecycle: {
|
|
472
|
+
init(): void;
|
|
473
|
+
destroy(): void;
|
|
474
|
+
update(): void;
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
/** Event system (inherited from BaseComponent) */
|
|
478
|
+
on<K extends keyof ListEvents<T>>(
|
|
479
|
+
event: K,
|
|
480
|
+
handler: (payload: ListEvents<T>[K]) => void
|
|
481
|
+
): void;
|
|
482
|
+
emit<K extends keyof ListEvents<T>>(
|
|
483
|
+
event: K,
|
|
484
|
+
payload: ListEvents<T>[K]
|
|
485
|
+
): void;
|
|
486
|
+
off<K extends keyof ListEvents<T>>(
|
|
487
|
+
event: K,
|
|
488
|
+
handler?: (payload: ListEvents<T>[K]) => void
|
|
489
|
+
): void;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* List performance metrics
|
|
494
|
+
*/
|
|
495
|
+
export interface ListPerformanceMetrics {
|
|
496
|
+
renderCount: number;
|
|
497
|
+
scrollCount: number;
|
|
498
|
+
averageRenderTime: number;
|
|
499
|
+
averageScrollTime: number;
|
|
500
|
+
memoryUsage: number;
|
|
501
|
+
virtualizedItems: number;
|
|
502
|
+
recycledElements: number;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* List feature options
|
|
507
|
+
*/
|
|
508
|
+
export interface ListFeatures {
|
|
509
|
+
virtualScroll?: boolean;
|
|
510
|
+
selection?: boolean;
|
|
511
|
+
styling?: boolean;
|
|
512
|
+
performance?: boolean;
|
|
513
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base Collection (Minimal Core)
|
|
3
|
+
*
|
|
4
|
+
* Pure data storage with zero features - everything else is plugins
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { CollectionItem, CollectionConfig, BaseCollection } from "./types";
|
|
8
|
+
import { createCollectionState, type CollectionDataState } from "./state";
|
|
9
|
+
import { createCollectionEventEmitter } from "./events";
|
|
10
|
+
import { COLLECTION_DEFAULTS, DATA_LOGGING } from "./constants";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Creates the minimal base collection
|
|
14
|
+
* All features (persistence, validation, etc.) are added via plugins
|
|
15
|
+
*/
|
|
16
|
+
export function createBaseCollection<T extends CollectionItem = CollectionItem>(
|
|
17
|
+
config: CollectionConfig<T> = {}
|
|
18
|
+
): BaseCollection<T> {
|
|
19
|
+
// Initialize minimal state
|
|
20
|
+
const stateStore = createCollectionState<T>({
|
|
21
|
+
items: config.items || [],
|
|
22
|
+
totalCount: config.items?.length || 0,
|
|
23
|
+
pageSize: config.pageSize || COLLECTION_DEFAULTS.PAGE_SIZE,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Event system
|
|
27
|
+
const eventEmitter = createCollectionEventEmitter<T>();
|
|
28
|
+
|
|
29
|
+
// Lifecycle tracking
|
|
30
|
+
let isDestroyed = false;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Base collection API - minimal data operations only
|
|
34
|
+
*/
|
|
35
|
+
const baseCollection: BaseCollection<T> = {
|
|
36
|
+
// Core data access
|
|
37
|
+
getItems(): T[] {
|
|
38
|
+
return stateStore.get().items;
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
getItem(id: string): T | undefined {
|
|
42
|
+
return stateStore.get().items.find((item) => item.id === id);
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
getSize(): number {
|
|
46
|
+
return stateStore.get().items.length;
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
getTotalCount(): number {
|
|
50
|
+
return stateStore.get().totalCount;
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
// Basic state
|
|
54
|
+
isLoading(): boolean {
|
|
55
|
+
return stateStore.get().loading;
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
getError(): Error | null {
|
|
59
|
+
return stateStore.get().error;
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
// State management (for plugins)
|
|
63
|
+
getState(): CollectionDataState<T> {
|
|
64
|
+
return stateStore.get();
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
setState(newState: Partial<CollectionDataState<T>>): void {
|
|
68
|
+
stateStore.set(newState);
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
// Event system (for plugins)
|
|
72
|
+
subscribe(observer: (payload: any) => void) {
|
|
73
|
+
return eventEmitter.subscribe(observer);
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
emit(event: string, data: any): void {
|
|
77
|
+
eventEmitter.emit(event as any, data);
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
// Lifecycle
|
|
81
|
+
destroy(): void {
|
|
82
|
+
if (isDestroyed) return;
|
|
83
|
+
|
|
84
|
+
stateStore.destroy();
|
|
85
|
+
eventEmitter.destroy();
|
|
86
|
+
isDestroyed = true;
|
|
87
|
+
|
|
88
|
+
console.log(`${DATA_LOGGING.PREFIX} Base collection destroyed`);
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
// Plugin support
|
|
92
|
+
_config: config,
|
|
93
|
+
_stateStore: stateStore,
|
|
94
|
+
_eventEmitter: eventEmitter,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
console.log(`${DATA_LOGGING.PREFIX} Base collection created`);
|
|
98
|
+
|
|
99
|
+
return baseCollection;
|
|
100
|
+
}
|