mtrl-addons 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/{src/components/index.ts → dist/components/index.d.ts} +0 -2
  2. package/dist/components/vlist/config.d.ts +86 -0
  3. package/{src/components/vlist/constants.ts → dist/components/vlist/constants.d.ts} +10 -11
  4. package/dist/components/vlist/features/api.d.ts +7 -0
  5. package/{src/components/vlist/features/index.ts → dist/components/vlist/features/index.d.ts} +0 -2
  6. package/dist/components/vlist/features/selection.d.ts +6 -0
  7. package/dist/components/vlist/features/viewport.d.ts +9 -0
  8. package/dist/components/vlist/features.d.ts +31 -0
  9. package/{src/components/vlist/index.ts → dist/components/vlist/index.d.ts} +1 -9
  10. package/dist/components/vlist/types.d.ts +596 -0
  11. package/dist/components/vlist/vlist.d.ts +29 -0
  12. package/dist/core/compose/features/gestures/index.d.ts +86 -0
  13. package/dist/core/compose/features/gestures/longpress.d.ts +85 -0
  14. package/dist/core/compose/features/gestures/pan.d.ts +108 -0
  15. package/dist/core/compose/features/gestures/pinch.d.ts +111 -0
  16. package/dist/core/compose/features/gestures/rotate.d.ts +111 -0
  17. package/dist/core/compose/features/gestures/swipe.d.ts +149 -0
  18. package/dist/core/compose/features/gestures/tap.d.ts +79 -0
  19. package/{src/core/compose/features/index.ts → dist/core/compose/features/index.d.ts} +1 -2
  20. package/{src/core/compose/index.ts → dist/core/compose/index.d.ts} +2 -11
  21. package/{src/core/gestures/index.ts → dist/core/gestures/index.d.ts} +1 -20
  22. package/dist/core/gestures/longpress.d.ts +23 -0
  23. package/dist/core/gestures/manager.d.ts +14 -0
  24. package/dist/core/gestures/pan.d.ts +12 -0
  25. package/dist/core/gestures/pinch.d.ts +14 -0
  26. package/dist/core/gestures/rotate.d.ts +14 -0
  27. package/dist/core/gestures/swipe.d.ts +20 -0
  28. package/dist/core/gestures/tap.d.ts +12 -0
  29. package/dist/core/gestures/types.d.ts +320 -0
  30. package/dist/core/gestures/utils.d.ts +57 -0
  31. package/dist/core/index.d.ts +13 -0
  32. package/dist/core/layout/config.d.ts +33 -0
  33. package/dist/core/layout/index.d.ts +51 -0
  34. package/dist/core/layout/jsx.d.ts +65 -0
  35. package/dist/core/layout/schema.d.ts +112 -0
  36. package/dist/core/layout/types.d.ts +69 -0
  37. package/dist/core/viewport/constants.d.ts +105 -0
  38. package/dist/core/viewport/features/base.d.ts +14 -0
  39. package/dist/core/viewport/features/collection.d.ts +41 -0
  40. package/dist/core/viewport/features/events.d.ts +13 -0
  41. package/{src/core/viewport/features/index.ts → dist/core/viewport/features/index.d.ts} +0 -7
  42. package/dist/core/viewport/features/item-size.d.ts +30 -0
  43. package/dist/core/viewport/features/loading.d.ts +34 -0
  44. package/dist/core/viewport/features/momentum.d.ts +17 -0
  45. package/dist/core/viewport/features/performance.d.ts +53 -0
  46. package/dist/core/viewport/features/placeholders.d.ts +38 -0
  47. package/dist/core/viewport/features/rendering.d.ts +16 -0
  48. package/dist/core/viewport/features/scrollbar.d.ts +26 -0
  49. package/dist/core/viewport/features/scrolling.d.ts +16 -0
  50. package/dist/core/viewport/features/utils.d.ts +43 -0
  51. package/dist/core/viewport/features/virtual.d.ts +18 -0
  52. package/{src/core/viewport/index.ts → dist/core/viewport/index.d.ts} +1 -17
  53. package/dist/core/viewport/types.d.ts +96 -0
  54. package/dist/core/viewport/utils/speed-tracker.d.ts +22 -0
  55. package/dist/core/viewport/viewport.d.ts +11 -0
  56. package/{src/index.ts → dist/index.d.ts} +0 -4
  57. package/dist/index.js +5143 -0
  58. package/dist/index.mjs +5111 -0
  59. package/dist/styles.css +254 -0
  60. package/dist/styles.css.map +1 -0
  61. package/package.json +6 -1
  62. package/src/styles/components/_vlist.scss +234 -213
  63. package/.cursorrules +0 -117
  64. package/AI.md +0 -241
  65. package/build.js +0 -201
  66. package/scripts/analyze-orphaned-functions.ts +0 -387
  67. package/scripts/debug/vlist-selection.ts +0 -121
  68. package/src/components/vlist/config.ts +0 -323
  69. package/src/components/vlist/features/api.ts +0 -322
  70. package/src/components/vlist/features/selection.ts +0 -444
  71. package/src/components/vlist/features/viewport.ts +0 -65
  72. package/src/components/vlist/features.ts +0 -112
  73. package/src/components/vlist/types.ts +0 -591
  74. package/src/components/vlist/vlist.ts +0 -92
  75. package/src/core/compose/features/gestures/index.ts +0 -227
  76. package/src/core/compose/features/gestures/longpress.ts +0 -383
  77. package/src/core/compose/features/gestures/pan.ts +0 -424
  78. package/src/core/compose/features/gestures/pinch.ts +0 -475
  79. package/src/core/compose/features/gestures/rotate.ts +0 -485
  80. package/src/core/compose/features/gestures/swipe.ts +0 -492
  81. package/src/core/compose/features/gestures/tap.ts +0 -334
  82. package/src/core/gestures/longpress.ts +0 -68
  83. package/src/core/gestures/manager.ts +0 -418
  84. package/src/core/gestures/pan.ts +0 -48
  85. package/src/core/gestures/pinch.ts +0 -58
  86. package/src/core/gestures/rotate.ts +0 -58
  87. package/src/core/gestures/swipe.ts +0 -66
  88. package/src/core/gestures/tap.ts +0 -45
  89. package/src/core/gestures/types.ts +0 -387
  90. package/src/core/gestures/utils.ts +0 -128
  91. package/src/core/index.ts +0 -43
  92. package/src/core/layout/config.ts +0 -102
  93. package/src/core/layout/index.ts +0 -168
  94. package/src/core/layout/jsx.ts +0 -174
  95. package/src/core/layout/schema.ts +0 -1001
  96. package/src/core/layout/types.ts +0 -95
  97. package/src/core/viewport/constants.ts +0 -140
  98. package/src/core/viewport/features/base.ts +0 -73
  99. package/src/core/viewport/features/collection.ts +0 -882
  100. package/src/core/viewport/features/events.ts +0 -130
  101. package/src/core/viewport/features/item-size.ts +0 -271
  102. package/src/core/viewport/features/loading.ts +0 -263
  103. package/src/core/viewport/features/momentum.ts +0 -260
  104. package/src/core/viewport/features/performance.ts +0 -161
  105. package/src/core/viewport/features/placeholders.ts +0 -335
  106. package/src/core/viewport/features/rendering.ts +0 -568
  107. package/src/core/viewport/features/scrollbar.ts +0 -434
  108. package/src/core/viewport/features/scrolling.ts +0 -618
  109. package/src/core/viewport/features/utils.ts +0 -88
  110. package/src/core/viewport/features/virtual.ts +0 -384
  111. package/src/core/viewport/types.ts +0 -133
  112. package/src/core/viewport/utils/speed-tracker.ts +0 -79
  113. package/src/core/viewport/viewport.ts +0 -246
  114. package/test/benchmarks/layout/advanced.test.ts +0 -656
  115. package/test/benchmarks/layout/comparison.test.ts +0 -519
  116. package/test/benchmarks/layout/performance-comparison.test.ts +0 -274
  117. package/test/benchmarks/layout/real-components.test.ts +0 -733
  118. package/test/benchmarks/layout/simple.test.ts +0 -321
  119. package/test/benchmarks/layout/stress.test.ts +0 -990
  120. package/test/collection/basic.test.ts +0 -304
  121. package/test/components/vlist-selection.test.ts +0 -240
  122. package/test/components/vlist.test.ts +0 -63
  123. package/test/core/collection/adapter.test.ts +0 -161
  124. package/test/core/collection/collection.test.ts +0 -394
  125. package/test/core/layout/layout.test.ts +0 -201
  126. package/test/utils/dom-helpers.ts +0 -275
  127. package/test/utils/performance-helpers.ts +0 -392
  128. package/tsconfig.json +0 -20
@@ -1,323 +0,0 @@
1
- /**
2
- * mtrl-addons List Component Configuration
3
- *
4
- * Configuration utilities and defaults for the list component.
5
- * Follows mtrl patterns for component configuration.
6
- */
7
-
8
- import { createComponentConfig, createElementConfig } from "mtrl";
9
- import { VLIST_CLASSES } from "./constants";
10
- import type { ListConfig, ListItem } from "./types";
11
- import type { ListComponent } from "./types";
12
-
13
- /**
14
- * Default configuration for the mtrl-addons List component
15
- */
16
- export const defaultConfig: Partial<ListConfig> = {
17
- // Collection settings (for API-connected lists)
18
- adapter: undefined,
19
-
20
- // Static data (for in-memory lists)
21
- items: [],
22
-
23
- // Template settings
24
- template: undefined, // Will use default template if not provided
25
-
26
- // Selection settings
27
- selection: {
28
- enabled: false,
29
- mode: "none",
30
- selectedIndices: [],
31
- },
32
-
33
- // Scroll settings
34
- scroll: {
35
- virtual: true,
36
- itemSize: 50,
37
- overscan: 5,
38
- animation: false,
39
- restorePosition: false,
40
- },
41
-
42
- // Component settings
43
- className: VLIST_CLASSES.LIST,
44
- prefix: "mtrl",
45
- componentName: "list",
46
- ariaLabel: "List",
47
- debug: false,
48
- };
49
-
50
- /**
51
- * Creates the base configuration for List component
52
- * @param {ListConfig} config - User provided configuration
53
- * @returns {ListConfig} Complete configuration with defaults applied
54
- */
55
- // Minimal converter to avoid hardcoding external helpers
56
- function convertRenderItemToTemplate(render: any) {
57
- if (typeof render === "function") return render;
58
- if (render && typeof render === "object") {
59
- const { headline, supporting, leading, trailing } = render as any;
60
- return (item: any, index: number) => {
61
- const container = document.createElement("div");
62
- container.className = "mtrl-list-item";
63
- if (leading) {
64
- const el = document.createElement("div");
65
- el.className = "mtrl-list-item__leading";
66
- el.innerHTML =
67
- typeof leading === "function"
68
- ? leading(item, index)
69
- : String(leading);
70
- container.appendChild(el);
71
- }
72
- const content = document.createElement("div");
73
- content.className = "mtrl-list-item__content";
74
- if (headline) {
75
- const h = document.createElement("div");
76
- h.className = "mtrl-list-item__headline";
77
- h.innerHTML =
78
- typeof headline === "function"
79
- ? headline(item, index)
80
- : String(headline);
81
- content.appendChild(h);
82
- }
83
- if (supporting) {
84
- const s = document.createElement("div");
85
- s.className = "mtrl-list-item__supporting";
86
- s.innerHTML =
87
- typeof supporting === "function"
88
- ? supporting(item, index)
89
- : String(supporting);
90
- content.appendChild(s);
91
- }
92
- container.appendChild(content);
93
- if (trailing) {
94
- const el = document.createElement("div");
95
- el.className = "mtrl-list-item__trailing";
96
- el.innerHTML =
97
- typeof trailing === "function"
98
- ? trailing(item, index)
99
- : String(trailing);
100
- container.appendChild(el);
101
- }
102
- return container;
103
- };
104
- }
105
- return (item: any) => String(item);
106
- }
107
-
108
- export function createBaseConfig<T extends ListItem = ListItem>(
109
- config: ListConfig<T> = {}
110
- ): Required<ListConfig<T>> {
111
- console.log("🔧 [MTRL-ADDONS-LIST] Creating base configuration");
112
-
113
- // Validate required configuration
114
- if (!config.items && !config.adapter) {
115
- throw new Error(
116
- "List requires either static items or an adapter for data loading"
117
- );
118
- }
119
-
120
- const mergedConfig = createComponentConfig(
121
- defaultConfig as any,
122
- config as any,
123
- "list"
124
- ) as any;
125
-
126
- // Convert renderItem object to template function if provided
127
- if ((config as any).renderItem && !mergedConfig.template) {
128
- mergedConfig.template = convertRenderItemToTemplate(
129
- (config as any).renderItem
130
- );
131
- }
132
-
133
- // Validate selection configuration
134
- if (
135
- mergedConfig.selection?.enabled &&
136
- mergedConfig.selection?.mode === undefined
137
- ) {
138
- mergedConfig.selection.mode = "single";
139
- }
140
-
141
- return mergedConfig as Required<ListConfig<T>>;
142
- }
143
-
144
- /**
145
- * Creates element configuration for withElement
146
- * @param {ListConfig} config - List configuration
147
- * @returns {Object} Element configuration
148
- */
149
- export function getElementConfig<T extends ListItem = ListItem>(
150
- config: ListConfig<T>
151
- ): any {
152
- const attributes = {
153
- "data-component": "list",
154
- "data-addons": "true",
155
- role: "list",
156
- "aria-label": config.ariaLabel || "List",
157
- };
158
-
159
- return createElementConfig(config as any, {
160
- tag: "div",
161
- attributes,
162
- className: VLIST_CLASSES.LIST,
163
- });
164
- }
165
-
166
- /**
167
- * Creates API configuration for the List component
168
- * @param {Object} component - Component with list functionality
169
- * @returns {Object} API configuration object for withApi
170
- */
171
- export function getApiConfig<T extends ListItem = ListItem>(component: any) {
172
- return {
173
- // Data operations
174
- data: {
175
- add: component.add,
176
- update: component.update,
177
- remove: component.remove,
178
- clear: component.clear,
179
- refresh: component.refresh,
180
- getItems: component.getItems,
181
- getItem: component.getItem,
182
- query: component.query,
183
- sort: component.sort,
184
- getSize: component.getSize,
185
- isEmpty: component.isEmpty,
186
- isLoading: component.isLoading,
187
- getError: component.getError,
188
- },
189
-
190
- // Selection operations (if enabled)
191
- selection: component.config?.selection?.enabled
192
- ? {
193
- selectItem: component.selectItem,
194
- deselectItem: component.deselectItem,
195
- selectAll: component.selectAll,
196
- deselectAll: component.deselectAll,
197
- getSelectedItems: component.getSelectedItems,
198
- getSelectedIds: component.getSelectedIds,
199
- }
200
- : undefined,
201
-
202
- // Scrolling operations
203
- scrolling: {
204
- scrollToItem: component.scrollToItem,
205
- scrollToIndex: component.scrollToIndex,
206
- scrollToPage: component.scrollToPage,
207
- },
208
-
209
- // Performance operations
210
- performance: {
211
- getMetrics: component.getMetrics,
212
- resetMetrics: component.resetMetrics,
213
- },
214
-
215
- // Template operations
216
- template: {
217
- setTemplate: component.setTemplate,
218
- getTemplate: component.getTemplate,
219
- },
220
-
221
- // Events system
222
- events: {
223
- on: component.on,
224
- off: component.off,
225
- emit: component.emit,
226
- subscribe: component.subscribe,
227
- },
228
-
229
- // Lifecycle operations
230
- lifecycle: {
231
- destroy: component.destroy,
232
- },
233
-
234
- // Configuration access
235
- config: {
236
- selection: component.config?.selection,
237
- scroll: component.config?.scroll,
238
- },
239
- };
240
- }
241
-
242
- /**
243
- * Validates list configuration
244
- */
245
- export const validateConfig = (config: ListConfig): void => {
246
- // Validate container
247
- if (config.container) {
248
- if (typeof config.container === "string") {
249
- const element = document.querySelector(config.container);
250
- if (!element) {
251
- throw new Error(
252
- `List container element not found: ${config.container}`
253
- );
254
- }
255
- } else if (!(config.container instanceof HTMLElement)) {
256
- throw new Error(
257
- "List container must be an HTMLElement or CSS selector string"
258
- );
259
- }
260
- }
261
-
262
- // Validate template
263
- if (config.template && typeof config.template !== "function") {
264
- throw new Error("List template must be a function");
265
- }
266
-
267
- // Validate items
268
- if (config.items && !Array.isArray(config.items)) {
269
- throw new Error("List items must be an array");
270
- }
271
-
272
- // Validate selection mode
273
- if (
274
- config.selection?.mode &&
275
- !["single", "multiple", "none"].includes(config.selection.mode)
276
- ) {
277
- throw new Error(
278
- 'List selection mode must be "single", "multiple", or "none"'
279
- );
280
- }
281
-
282
- // Validate scroll configuration
283
- if (
284
- config.scroll?.itemSize !== "auto" &&
285
- typeof config.scroll?.itemSize === "number" &&
286
- config.scroll.itemSize <= 0
287
- ) {
288
- throw new Error('List item size must be positive number or "auto"');
289
- }
290
-
291
- // Validate density
292
- if (
293
- config.density &&
294
- !["default", "compact", "comfortable"].includes(config.density)
295
- ) {
296
- throw new Error(
297
- 'List density must be "default", "compact", or "comfortable"'
298
- );
299
- }
300
-
301
- // Validate variant
302
- if (
303
- config.variant &&
304
- !["default", "dense", "comfortable"].includes(config.variant)
305
- ) {
306
- throw new Error(
307
- 'List variant must be "default", "dense", or "comfortable"'
308
- );
309
- }
310
- };
311
-
312
- /**
313
- * Creates Collection configuration from List config
314
- */
315
- export const getCollectionConfig = (config: ListConfig) => ({
316
- adapter: config.adapter,
317
- items: config.items,
318
- cache: (config.collection as any)?.cache || {
319
- enabled: true,
320
- maxSize: 1000,
321
- ttl: 300000, // 5 minutes
322
- },
323
- });
@@ -1,322 +0,0 @@
1
- /**
2
- * API feature for VList
3
- * Provides a clean public API for the VList component
4
- */
5
-
6
- import type { VListConfig, VListItem } from "../types";
7
-
8
- /**
9
- * Adds public API methods to VList
10
- */
11
- export const withAPI = <T extends VListItem = VListItem>(
12
- config: VListConfig<T>
13
- ) => {
14
- return (component: any) => {
15
- // Initialize viewport on creation
16
- setTimeout(() => {
17
- if (component.viewport && component.viewport.initialize) {
18
- component.viewport.initialize();
19
- }
20
- }, 0);
21
-
22
- // Track loading state
23
- let isLoading = false;
24
- let selectedIds = new Set<string | number>();
25
-
26
- // Listen for collection events
27
- component.on?.("collection:range-loaded", () => {
28
- isLoading = false;
29
- });
30
- component.on?.("collection:range-failed", () => {
31
- isLoading = false;
32
- });
33
-
34
- return {
35
- ...component,
36
-
37
- // Data operations
38
- setItems(items: T[]) {
39
- if (component.viewport?.collection) {
40
- component.viewport.collection.setItems(items);
41
- } else {
42
- component.items = items;
43
- component.totalItems = items.length;
44
- }
45
- component.emit?.("items:set", { items, total: items.length });
46
- },
47
-
48
- getItems(): T[] {
49
- // Collection is added directly to component, not to viewport.collection
50
- if ((component as any).collection?.getItems) {
51
- return (component as any).collection.getItems();
52
- }
53
- return component.items || [];
54
- },
55
-
56
- getVisibleItems(): T[] {
57
- const range = component.viewport?.getVisibleRange() || {
58
- start: 0,
59
- end: 0,
60
- };
61
- const items = this.getItems();
62
- return items.slice(range.start, range.end);
63
- },
64
-
65
- getItemCount(): number {
66
- if (component.viewport?.collection) {
67
- return component.viewport.collection.getTotalItems();
68
- }
69
- return component.totalItems || component.items?.length || 0;
70
- },
71
-
72
- // Loading operations
73
- async loadRange(
74
- page: number,
75
- limit: number,
76
- strategy: string = "page",
77
- alignment?: string
78
- ) {
79
- isLoading = true;
80
-
81
- if (component.viewport?.collection) {
82
- const offset = strategy === "page" ? (page - 1) * limit : page;
83
- try {
84
- await component.viewport.collection.loadRange(offset, limit);
85
- component.emit?.("load", { page, limit, strategy });
86
-
87
- // Scroll to the loaded range if alignment is specified
88
- if (alignment) {
89
- const index = offset;
90
- this.scrollToIndex(index, alignment);
91
- }
92
- } catch (error) {
93
- component.emit?.("error", { error });
94
- throw error;
95
- } finally {
96
- isLoading = false;
97
- }
98
- }
99
- },
100
-
101
- // Data loading
102
- loadNext: async function () {
103
- console.log(`[VList] loadNext()`);
104
-
105
- if (component.viewport?.collection) {
106
- const totalItems = component.viewport.collection.getTotalItems();
107
- const loadedRanges = component.viewport.collection.getLoadedRanges();
108
-
109
- // Find the next unloaded range
110
- let nextOffset = 0;
111
- for (const rangeId of loadedRanges) {
112
- const rangeEnd = (rangeId + 1) * (config.pagination?.limit || 20);
113
- if (rangeEnd > nextOffset) {
114
- nextOffset = rangeEnd;
115
- }
116
- }
117
-
118
- if (nextOffset < totalItems) {
119
- await component.viewport.collection.loadRange(
120
- nextOffset,
121
- config.pagination?.limit || 20
122
- );
123
- }
124
- }
125
-
126
- return Promise.resolve();
127
- },
128
-
129
- isLoading(): boolean {
130
- return isLoading;
131
- },
132
-
133
- hasNext(): boolean {
134
- if (component.viewport?.collection) {
135
- const total = component.viewport.collection.getTotalItems();
136
- const items = component.viewport.collection.getItems();
137
- return items.length < total;
138
- }
139
- return false;
140
- },
141
-
142
- // Note: Selection operations are handled by the withSelection feature
143
- // These are kept for backward compatibility but delegate to selection feature if available
144
- getSelectedIds(): (string | number)[] {
145
- if (typeof component.getSelectedIndices === "function") {
146
- // Use selection feature if available
147
- const items = this.getItems();
148
- const indices = component.getSelectedIndices();
149
- return indices.map((idx: number) => (items[idx] as any)?.id || idx);
150
- }
151
- // Fallback to local tracking
152
- return Array.from(selectedIds);
153
- },
154
-
155
- selectItem(id: string | number) {
156
- if (typeof component.selectItems === "function") {
157
- // Use selection feature if available
158
- const items = this.getItems();
159
- const index = items.findIndex((item: any) => item.id === id);
160
- if (index >= 0) {
161
- component.selectItems([index]);
162
- }
163
- } else {
164
- // Fallback to local tracking
165
- selectedIds.add(id);
166
- component.emit?.("selection:change", {
167
- selected: this.getSelectedIds(),
168
- });
169
- }
170
- },
171
-
172
- deselectItem(id: string | number) {
173
- if (typeof component.deselectItems === "function") {
174
- // Use selection feature if available
175
- const items = this.getItems();
176
- const index = items.findIndex((item: any) => item.id === id);
177
- if (index >= 0) {
178
- component.deselectItems([index]);
179
- }
180
- } else {
181
- // Fallback to local tracking
182
- selectedIds.delete(id);
183
- component.emit?.("selection:change", {
184
- selected: this.getSelectedIds(),
185
- });
186
- }
187
- },
188
-
189
- clearSelection() {
190
- if (typeof component.clearSelection === "function") {
191
- // Use selection feature if available
192
- component.clearSelection();
193
- } else {
194
- // Fallback to local tracking
195
- selectedIds.clear();
196
- component.emit?.("selection:change", { selected: [] });
197
- }
198
- },
199
-
200
- // Scrolling methods
201
- scrollToIndex: (
202
- index: number,
203
- alignment: "start" | "center" | "end" = "start",
204
- animate?: boolean
205
- ) => {
206
- if (component.viewport) {
207
- component.viewport.scrollToIndex(index, alignment);
208
- }
209
- return Promise.resolve();
210
- },
211
-
212
- scrollToItem: async function (
213
- itemId: string,
214
- alignment: "start" | "center" | "end" = "start",
215
- animate?: boolean
216
- ) {
217
- const items = this.getItems();
218
- const index = items.findIndex(
219
- (item: any) => String(item.id) === String(itemId)
220
- );
221
-
222
- if (index >= 0) {
223
- return this.scrollToIndex(index, alignment, animate);
224
- }
225
-
226
- console.warn(`Item ${itemId} not found`);
227
- return Promise.resolve();
228
- },
229
-
230
- scrollToTop: function () {
231
- return this.scrollToIndex(0, "start");
232
- },
233
-
234
- scrollToBottom: function () {
235
- const totalItems = this.getItemCount();
236
- if (totalItems > 0) {
237
- return this.scrollToIndex(totalItems - 1, "end");
238
- }
239
- return Promise.resolve();
240
- },
241
-
242
- scrollToPage: async function (
243
- pageNum: number,
244
- alignment: "start" | "center" | "end" = "start",
245
- animate?: boolean
246
- ) {
247
- // console.log(`[VList] scrollToPage(${pageNum})`);
248
-
249
- // Get limit from config (rangeSize) or default
250
- const limit = config.pagination?.limit || 20;
251
-
252
- // Check if we're in cursor mode
253
- if (config.pagination?.strategy === "cursor") {
254
- // In cursor mode, we need to handle sequential loading
255
- const collection = (component.viewport as any)?.collection;
256
- if (collection) {
257
- const loadedRanges = collection.getLoadedRanges();
258
- const highestLoadedPage = loadedRanges.size;
259
-
260
- if (pageNum > highestLoadedPage + 1) {
261
- console.warn(
262
- `[VList] Cannot jump to page ${pageNum} in cursor mode. ` +
263
- `Loading pages sequentially from ${
264
- highestLoadedPage + 1
265
- } to ${pageNum}`
266
- );
267
- }
268
- }
269
- }
270
-
271
- // Use viewport's scrollToPage if available
272
- if ((component.viewport as any)?.scrollToPage) {
273
- (component.viewport as any).scrollToPage(pageNum, limit, alignment);
274
- } else {
275
- // Fallback to scrollToIndex
276
- const targetIndex = (pageNum - 1) * limit;
277
- await this.scrollToIndex(targetIndex, alignment);
278
- }
279
- },
280
-
281
- getScrollPosition: () => {
282
- return component.viewport?.getScrollPosition() || 0;
283
- },
284
-
285
- getCurrentCursor: () => {
286
- const collection = (component.viewport as any)?.collection;
287
- return collection?.getCurrentCursor?.() || null;
288
- },
289
-
290
- setScrollAnimation(enabled: boolean) {
291
- // Store animation preference (would be used by viewport scrolling feature)
292
- component.scrollAnimation = enabled;
293
- },
294
-
295
- // State
296
- getState() {
297
- return {
298
- items: this.getItems(),
299
- totalItems: component.viewport?.collection
300
- ? component.viewport.collection.getTotalItems()
301
- : component.totalItems || 0,
302
- visibleRange: component.viewport?.getVisibleRange() || {
303
- start: 0,
304
- end: 0,
305
- },
306
- scrollPosition: component.viewport?.getScrollPosition() || 0,
307
- selectedIds: this.getSelectedIds(),
308
- isLoading: this.isLoading(),
309
- };
310
- },
311
-
312
- // Lifecycle
313
- destroy() {
314
- if (component.viewport?.destroy) {
315
- component.viewport.destroy();
316
- }
317
- selectedIds.clear();
318
- component.emit?.("destroyed");
319
- },
320
- };
321
- };
322
- };