mtrl-addons 0.2.2 → 0.2.4

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 (129) 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 -10
  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 +16 -2
  62. package/.cursorrules +0 -117
  63. package/AI.md +0 -39
  64. package/CLAUDE.md +0 -882
  65. package/build.js +0 -377
  66. package/index.ts +0 -7
  67. package/scripts/analyze-orphaned-functions.ts +0 -387
  68. package/scripts/debug/vlist-selection.ts +0 -121
  69. package/src/components/vlist/config.ts +0 -323
  70. package/src/components/vlist/features/api.ts +0 -626
  71. package/src/components/vlist/features/selection.ts +0 -436
  72. package/src/components/vlist/features/viewport.ts +0 -59
  73. package/src/components/vlist/features.ts +0 -112
  74. package/src/components/vlist/types.ts +0 -723
  75. package/src/components/vlist/vlist.ts +0 -92
  76. package/src/core/compose/features/gestures/index.ts +0 -227
  77. package/src/core/compose/features/gestures/longpress.ts +0 -383
  78. package/src/core/compose/features/gestures/pan.ts +0 -424
  79. package/src/core/compose/features/gestures/pinch.ts +0 -475
  80. package/src/core/compose/features/gestures/rotate.ts +0 -485
  81. package/src/core/compose/features/gestures/swipe.ts +0 -492
  82. package/src/core/compose/features/gestures/tap.ts +0 -334
  83. package/src/core/gestures/longpress.ts +0 -68
  84. package/src/core/gestures/manager.ts +0 -418
  85. package/src/core/gestures/pan.ts +0 -48
  86. package/src/core/gestures/pinch.ts +0 -58
  87. package/src/core/gestures/rotate.ts +0 -58
  88. package/src/core/gestures/swipe.ts +0 -66
  89. package/src/core/gestures/tap.ts +0 -45
  90. package/src/core/gestures/types.ts +0 -387
  91. package/src/core/gestures/utils.ts +0 -128
  92. package/src/core/index.ts +0 -43
  93. package/src/core/layout/config.ts +0 -102
  94. package/src/core/layout/index.ts +0 -168
  95. package/src/core/layout/jsx.ts +0 -174
  96. package/src/core/layout/schema.ts +0 -1044
  97. package/src/core/layout/types.ts +0 -95
  98. package/src/core/viewport/constants.ts +0 -145
  99. package/src/core/viewport/features/base.ts +0 -73
  100. package/src/core/viewport/features/collection.ts +0 -1182
  101. package/src/core/viewport/features/events.ts +0 -130
  102. package/src/core/viewport/features/item-size.ts +0 -271
  103. package/src/core/viewport/features/loading.ts +0 -263
  104. package/src/core/viewport/features/momentum.ts +0 -269
  105. package/src/core/viewport/features/performance.ts +0 -161
  106. package/src/core/viewport/features/placeholders.ts +0 -335
  107. package/src/core/viewport/features/rendering.ts +0 -962
  108. package/src/core/viewport/features/scrollbar.ts +0 -434
  109. package/src/core/viewport/features/scrolling.ts +0 -634
  110. package/src/core/viewport/features/utils.ts +0 -94
  111. package/src/core/viewport/features/virtual.ts +0 -525
  112. package/src/core/viewport/types.ts +0 -133
  113. package/src/core/viewport/utils/speed-tracker.ts +0 -79
  114. package/src/core/viewport/viewport.ts +0 -265
  115. package/test/benchmarks/layout/advanced.test.ts +0 -656
  116. package/test/benchmarks/layout/comparison.test.ts +0 -519
  117. package/test/benchmarks/layout/performance-comparison.test.ts +0 -274
  118. package/test/benchmarks/layout/real-components.test.ts +0 -733
  119. package/test/benchmarks/layout/simple.test.ts +0 -321
  120. package/test/benchmarks/layout/stress.test.ts +0 -990
  121. package/test/collection/basic.test.ts +0 -304
  122. package/test/components/vlist-selection.test.ts +0 -240
  123. package/test/components/vlist.test.ts +0 -63
  124. package/test/core/collection/adapter.test.ts +0 -161
  125. package/test/core/collection/collection.test.ts +0 -394
  126. package/test/core/layout/layout.test.ts +0 -201
  127. package/test/utils/dom-helpers.ts +0 -275
  128. package/test/utils/performance-helpers.ts +0 -392
  129. package/tsconfig.json +0 -20
@@ -1,434 +0,0 @@
1
- // src/core/viewport/features/scrollbar.ts
2
-
3
- /**
4
- * Scrollbar Feature - Custom scrollbar implementation
5
- * Provides visual scroll indication and drag-to-scroll functionality
6
- */
7
-
8
- import type { ViewportContext, ViewportComponent } from "../types";
9
- import { VIEWPORT_CONSTANTS } from "../constants";
10
- import { wrapInitialize, wrapDestroy, storeFeatureFunction } from "./utils";
11
- import { PREFIX, addClass, removeClass } from "mtrl";
12
-
13
- export interface ScrollbarConfig {
14
- enabled?: boolean;
15
- autoHide?: boolean;
16
- thumbMinHeight?: number;
17
- thumbColor?: string;
18
- trackColor?: string;
19
- borderRadius?: number;
20
- fadeTimeout?: number;
21
- }
22
-
23
- export interface ScrollbarComponent {
24
- scrollbar: {
25
- show: () => void;
26
- hide: () => void;
27
- updateBounds: (totalVirtualSize: number, containerSize: number) => void;
28
- updatePosition: (scrollPosition: number) => void;
29
- };
30
- }
31
-
32
- /**
33
- * Adds scrollbar functionality to viewport component
34
- */
35
- export function withScrollbar(config: ScrollbarConfig = {}) {
36
- return <T extends ViewportContext & ViewportComponent>(
37
- component: T
38
- ): T & ScrollbarComponent => {
39
- const {
40
- enabled = true,
41
- autoHide = true,
42
- thumbMinHeight = 25,
43
- borderRadius = 4,
44
- fadeTimeout = 1000,
45
- } = config;
46
-
47
- // Return no-op if disabled
48
- if (!enabled) {
49
- return {
50
- ...component,
51
- scrollbar: {
52
- show: () => {},
53
- hide: () => {},
54
- updateBounds: () => {},
55
- updatePosition: () => {},
56
- },
57
- };
58
- }
59
-
60
- // State
61
- let viewportElement: HTMLElement | null = null;
62
- let scrollbarTrack: HTMLElement | null = null;
63
- let scrollbarThumb: HTMLElement | null = null;
64
- let isInitialized = false;
65
- let isDragging = false;
66
- let dragStartY = 0;
67
- let dragStartScrollPosition = 0;
68
- let hideTimeout: ReturnType<typeof setTimeout> | null = null;
69
- let totalVirtualSize = 0;
70
- let containerSize = 0;
71
- let thumbHeight = 0;
72
- let animationFrameId: number | null = null;
73
- let lastRequestedScrollPosition: number | null = null;
74
- let isCursorMode = false;
75
- let loadedItemsCount = 0;
76
-
77
- // Create scrollbar elements
78
- const createScrollbarElements = () => {
79
- if (!viewportElement) return;
80
-
81
- scrollbarTrack = document.createElement("div");
82
- addClass(scrollbarTrack, VIEWPORT_CONSTANTS.SCROLLBAR.CLASSES.SCROLLBAR);
83
- scrollbarTrack.style.cssText = `
84
- position: absolute;
85
- top: 0;
86
- right: 0;
87
- height: 100%;
88
- z-index: 10;
89
- `;
90
-
91
- scrollbarThumb = document.createElement("div");
92
-
93
- addClass(
94
- scrollbarThumb,
95
- VIEWPORT_CONSTANTS.SCROLLBAR.CLASSES.SCROLLBAR_THUMB
96
- );
97
-
98
- scrollbarTrack.appendChild(scrollbarThumb);
99
- viewportElement.appendChild(scrollbarTrack);
100
- };
101
-
102
- // Show/hide functions
103
- const show = () => {
104
- if (!scrollbarTrack || totalVirtualSize <= containerSize) return;
105
-
106
- if (hideTimeout) {
107
- clearTimeout(hideTimeout);
108
- hideTimeout = null;
109
- }
110
-
111
- addClass(
112
- scrollbarTrack,
113
- VIEWPORT_CONSTANTS.SCROLLBAR.CLASSES.SCROLLBAR_VISIBLE
114
- );
115
-
116
- if (autoHide && !isDragging) {
117
- hideTimeout = setTimeout(hide, fadeTimeout);
118
- }
119
- };
120
-
121
- const hide = () => {
122
- if (!scrollbarTrack || isDragging) return;
123
- removeClass(
124
- scrollbarTrack,
125
- VIEWPORT_CONSTANTS.SCROLLBAR.CLASSES.SCROLLBAR_VISIBLE
126
- );
127
- };
128
-
129
- // Update scrollbar bounds
130
- const updateBounds = (newTotalSize: number, newContainerSize: number) => {
131
- totalVirtualSize = newTotalSize;
132
- containerSize = newContainerSize;
133
-
134
- if (!scrollbarTrack || !scrollbarThumb) return;
135
-
136
- const needsScrollbar = totalVirtualSize > containerSize;
137
- // console.log(
138
- // `[Scrollbar] updateBounds: totalSize=${totalVirtualSize}, containerSize=${containerSize}, needsScrollbar=${needsScrollbar}`
139
- // );
140
- scrollbarTrack.style.display = needsScrollbar ? "block" : "none";
141
-
142
- if (needsScrollbar) {
143
- let scrollRatio: number;
144
-
145
- if (isCursorMode && loadedItemsCount > 0) {
146
- // For cursor mode, thumb size represents loaded content vs estimated total
147
- // If we don't know total, use loaded items * 2 as estimate
148
- const estimatedTotal =
149
- totalVirtualSize > 0 ? totalVirtualSize : loadedItemsCount * 2;
150
- scrollRatio =
151
- containerSize /
152
- Math.max(estimatedTotal, loadedItemsCount + containerSize);
153
- } else {
154
- // Normal calculation for offset/page strategies
155
- scrollRatio = containerSize / totalVirtualSize;
156
- }
157
-
158
- thumbHeight = Math.max(thumbMinHeight, scrollRatio * containerSize);
159
- scrollbarThumb.style.height = `${thumbHeight}px`;
160
- updatePosition(component.viewport?.getScrollPosition() || 0);
161
- }
162
- };
163
-
164
- // Update thumb position
165
- const updatePosition = (scrollPos: number) => {
166
- if (!scrollbarThumb || !scrollbarTrack) return;
167
-
168
- const scrollableDistance = totalVirtualSize - containerSize;
169
- if (scrollableDistance <= 0) return;
170
-
171
- const scrollRatio = Math.min(
172
- 1,
173
- Math.max(0, scrollPos / scrollableDistance)
174
- );
175
- const maxThumbPosition =
176
- scrollbarTrack.clientHeight - scrollbarThumb.clientHeight;
177
- const thumbPosition = scrollRatio * maxThumbPosition;
178
-
179
- scrollbarThumb.style.transform = `translateY(${thumbPosition}px)`;
180
- };
181
-
182
- // Handle track click
183
- const handleTrackClick = (e: MouseEvent) => {
184
- if (!scrollbarTrack || !scrollbarThumb || e.target === scrollbarThumb)
185
- return;
186
-
187
- const trackRect = scrollbarTrack.getBoundingClientRect();
188
- const clickY = e.clientY - trackRect.top;
189
- const thumbCenterY = clickY - thumbHeight / 2;
190
- const maxThumbPosition = containerSize - thumbHeight;
191
- const thumbPosition = Math.max(
192
- 0,
193
- Math.min(thumbCenterY, maxThumbPosition)
194
- );
195
- const scrollRatio = thumbPosition / maxThumbPosition;
196
- const targetScrollPosition =
197
- scrollRatio * (totalVirtualSize - containerSize);
198
-
199
- component.viewport?.scrollToPosition(targetScrollPosition);
200
- };
201
-
202
- // Mouse event handlers
203
- const handleThumbMouseDown = (e: MouseEvent) => {
204
- e.preventDefault();
205
- e.stopPropagation();
206
-
207
- isDragging = true;
208
- dragStartY = e.clientY;
209
- dragStartScrollPosition = component.viewport?.getScrollPosition() || 0;
210
-
211
- if (scrollbarTrack) {
212
- addClass(
213
- scrollbarTrack,
214
- VIEWPORT_CONSTANTS.SCROLLBAR.CLASSES.SCROLLBAR_DRAGGING
215
- );
216
- }
217
-
218
- // Emit drag start event to notify viewport
219
- component.emit?.("viewport:drag-start", {
220
- source: "scrollbar",
221
- startPosition: dragStartScrollPosition,
222
- });
223
-
224
- document.addEventListener("mousemove", handleMouseMove);
225
- document.addEventListener("mouseup", handleMouseUp);
226
- };
227
-
228
- const handleMouseMove = (e: MouseEvent) => {
229
- if (!isDragging || !scrollbarTrack || !scrollbarThumb) return;
230
-
231
- const deltaY = e.clientY - dragStartY;
232
- const trackHeight = scrollbarTrack.clientHeight;
233
- const thumbHeight = scrollbarThumb.clientHeight;
234
- const maxThumbTravel = trackHeight - thumbHeight;
235
-
236
- if (maxThumbTravel <= 0) return;
237
-
238
- const deltaRatio = deltaY / maxThumbTravel;
239
- const dragStartScrollRatio =
240
- dragStartScrollPosition / (totalVirtualSize - containerSize);
241
- const newScrollRatio = Math.max(
242
- 0,
243
- Math.min(1, dragStartScrollRatio + deltaRatio)
244
- );
245
-
246
- // Update thumb position immediately
247
- const thumbPosition = newScrollRatio * maxThumbTravel;
248
- scrollbarThumb.style.transform = `translateY(${thumbPosition}px)`;
249
-
250
- // Calculate new scroll position
251
- const newPosition = newScrollRatio * (totalVirtualSize - containerSize);
252
- lastRequestedScrollPosition = newPosition;
253
-
254
- // console.log(
255
- // `[Scrollbar] Mouse move: newPos=${newPosition.toFixed(
256
- // 0
257
- // )}, animationFrameId=${animationFrameId}`
258
- // );
259
-
260
- // Throttle viewport updates
261
- if (animationFrameId === null && component.viewport) {
262
- animationFrameId = requestAnimationFrame(() => {
263
- // console.log(
264
- // `[Scrollbar] RAF callback: scrolling to ${lastRequestedScrollPosition}`
265
- // );
266
- if (lastRequestedScrollPosition !== null && component.viewport) {
267
- component.viewport.scrollToPosition(lastRequestedScrollPosition);
268
- }
269
- animationFrameId = null;
270
- });
271
- }
272
- };
273
-
274
- const handleMouseUp = () => {
275
- // console.log("[Scrollbar] Mouse up - ending drag");
276
- isDragging = false;
277
-
278
- if (animationFrameId !== null) {
279
- cancelAnimationFrame(animationFrameId);
280
- animationFrameId = null;
281
- }
282
-
283
- if (lastRequestedScrollPosition !== null && component.viewport) {
284
- component.viewport.scrollToPosition(lastRequestedScrollPosition);
285
- lastRequestedScrollPosition = null;
286
- }
287
-
288
- if (scrollbarTrack) {
289
- removeClass(
290
- scrollbarTrack,
291
- VIEWPORT_CONSTANTS.SCROLLBAR.CLASSES.SCROLLBAR_DRAGGING
292
- );
293
- }
294
-
295
- // Emit drag end event
296
- component.emit?.("viewport:drag-end", {
297
- source: "scrollbar",
298
- endPosition: component.viewport?.getScrollPosition() || 0,
299
- });
300
-
301
- hide();
302
-
303
- document.removeEventListener("mousemove", handleMouseMove);
304
- document.removeEventListener("mouseup", handleMouseUp);
305
- };
306
-
307
- // Initialize function
308
- const initialize = () => {
309
- if (isInitialized) return;
310
-
311
- viewportElement = component.element?.querySelector(
312
- `.${PREFIX}-viewport`
313
- ) as HTMLElement;
314
-
315
- if (!viewportElement) {
316
- console.warn("[Scrollbar] No viewport element found");
317
- return;
318
- }
319
-
320
- if (
321
- viewportElement.querySelector(
322
- `.${PREFIX}-${VIEWPORT_CONSTANTS.SCROLLBAR.CLASSES.SCROLLBAR}`
323
- )
324
- )
325
- return;
326
-
327
- createScrollbarElements();
328
- isInitialized = true;
329
-
330
- if (scrollbarTrack && viewportElement) {
331
- scrollbarTrack.addEventListener("click", handleTrackClick);
332
-
333
- if (scrollbarThumb) {
334
- scrollbarThumb.addEventListener("mousedown", handleThumbMouseDown);
335
- }
336
-
337
- viewportElement.addEventListener("mouseenter", show, { passive: true });
338
- viewportElement.addEventListener(
339
- "mouseleave",
340
- () => {
341
- if (!isDragging) hide();
342
- },
343
- { passive: true }
344
- );
345
- }
346
-
347
- // Initialize with current viewport state
348
- if (component.viewport) {
349
- const info = component.viewport.getViewportInfo();
350
- totalVirtualSize = info.totalVirtualSize;
351
- containerSize = info.containerSize;
352
-
353
- // Check if we're in cursor mode
354
- const collection = (component as any).collection;
355
- const viewportConfig = (component as any).config;
356
- isCursorMode = viewportConfig?.pagination?.strategy === "cursor";
357
-
358
- updateBounds(totalVirtualSize, containerSize);
359
- updatePosition(component.viewport.getScrollPosition());
360
- }
361
-
362
- // Event listeners
363
- component.on?.("viewport:scroll", (data: any) => {
364
- if (!isDragging) {
365
- updatePosition(data.position);
366
- show();
367
- }
368
- });
369
-
370
- component.on?.("viewport:virtual-size-changed", (data: any) => {
371
- totalVirtualSize = data.totalVirtualSize;
372
- updateBounds(data.totalVirtualSize, containerSize);
373
- });
374
-
375
- component.on?.("viewport:container-size-changed", (data: any) => {
376
- containerSize = data.containerSize;
377
- updateBounds(totalVirtualSize, data.containerSize);
378
- });
379
-
380
- // Listen for items loaded to update scrollbar in cursor mode
381
- component.on?.("viewport:items-changed", (data: any) => {
382
- if (isCursorMode) {
383
- loadedItemsCount = data.loadedCount || 0;
384
- updateBounds(totalVirtualSize, containerSize);
385
- }
386
- });
387
-
388
- // Listen for total items changes (important for cursor mode)
389
- component.on?.("viewport:total-items-changed", (data: any) => {
390
- if (isCursorMode && data.total) {
391
- // In cursor mode, the total is dynamic
392
- console.log(
393
- `[Scrollbar] Cursor mode: updating bounds for new total ${data.total}`
394
- );
395
- updateBounds(totalVirtualSize, containerSize);
396
- }
397
- });
398
- };
399
-
400
- // Store initialize function
401
- storeFeatureFunction(component, "_scrollbarInitialize", initialize);
402
-
403
- // Hook into viewport initialization
404
- wrapInitialize(component, initialize);
405
-
406
- // Cleanup
407
- const destroy = () => {
408
- if (scrollbarTrack) {
409
- scrollbarTrack.remove();
410
- scrollbarTrack = null;
411
- }
412
- scrollbarThumb = null;
413
- viewportElement = null;
414
- isInitialized = false;
415
-
416
- if (hideTimeout) {
417
- clearTimeout(hideTimeout);
418
- hideTimeout = null;
419
- }
420
-
421
- document.removeEventListener("mousemove", handleMouseMove);
422
- document.removeEventListener("mouseup", handleMouseUp);
423
- };
424
-
425
- storeFeatureFunction(component, "_scrollbarDestroy", destroy);
426
- wrapDestroy(component, destroy);
427
-
428
- // Return enhanced component
429
- return {
430
- ...component,
431
- scrollbar: { show, hide, updateBounds, updatePosition },
432
- };
433
- };
434
- }