mtrl-addons 0.1.2 → 0.2.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.
Files changed (115) hide show
  1. package/build.js +139 -86
  2. package/package.json +13 -4
  3. package/scripts/debug/vlist-selection.ts +121 -0
  4. package/src/components/index.ts +5 -41
  5. package/src/components/{list → vlist}/config.ts +66 -95
  6. package/src/components/vlist/constants.ts +23 -0
  7. package/src/components/vlist/features/api.ts +322 -0
  8. package/src/components/vlist/features/index.ts +10 -0
  9. package/src/components/vlist/features/selection.ts +444 -0
  10. package/src/components/vlist/features/viewport.ts +65 -0
  11. package/src/components/vlist/index.ts +16 -0
  12. package/src/components/{list → vlist}/types.ts +104 -26
  13. package/src/components/vlist/vlist.ts +92 -0
  14. package/src/core/compose/features/gestures/index.ts +227 -0
  15. package/src/core/compose/features/gestures/longpress.ts +383 -0
  16. package/src/core/compose/features/gestures/pan.ts +424 -0
  17. package/src/core/compose/features/gestures/pinch.ts +475 -0
  18. package/src/core/compose/features/gestures/rotate.ts +485 -0
  19. package/src/core/compose/features/gestures/swipe.ts +492 -0
  20. package/src/core/compose/features/gestures/tap.ts +334 -0
  21. package/src/core/compose/features/index.ts +2 -38
  22. package/src/core/compose/index.ts +13 -29
  23. package/src/core/gestures/index.ts +31 -0
  24. package/src/core/gestures/longpress.ts +68 -0
  25. package/src/core/gestures/manager.ts +418 -0
  26. package/src/core/gestures/pan.ts +48 -0
  27. package/src/core/gestures/pinch.ts +58 -0
  28. package/src/core/gestures/rotate.ts +58 -0
  29. package/src/core/gestures/swipe.ts +66 -0
  30. package/src/core/gestures/tap.ts +45 -0
  31. package/src/core/gestures/types.ts +387 -0
  32. package/src/core/gestures/utils.ts +128 -0
  33. package/src/core/index.ts +27 -151
  34. package/src/core/layout/schema.ts +73 -35
  35. package/src/core/layout/types.ts +5 -2
  36. package/src/core/viewport/constants.ts +140 -0
  37. package/src/core/viewport/features/base.ts +73 -0
  38. package/src/core/viewport/features/collection.ts +882 -0
  39. package/src/core/viewport/features/events.ts +130 -0
  40. package/src/core/viewport/features/index.ts +20 -0
  41. package/src/core/{list-manager/features/viewport → viewport/features}/item-size.ts +27 -30
  42. package/src/core/{list-manager/features/viewport → viewport/features}/loading.ts +4 -4
  43. package/src/core/viewport/features/momentum.ts +260 -0
  44. package/src/core/viewport/features/placeholders.ts +335 -0
  45. package/src/core/viewport/features/rendering.ts +568 -0
  46. package/src/core/viewport/features/scrollbar.ts +434 -0
  47. package/src/core/viewport/features/scrolling.ts +618 -0
  48. package/src/core/viewport/features/utils.ts +88 -0
  49. package/src/core/viewport/features/virtual.ts +384 -0
  50. package/src/core/viewport/index.ts +31 -0
  51. package/src/core/viewport/types.ts +133 -0
  52. package/src/core/viewport/utils/speed-tracker.ts +79 -0
  53. package/src/core/viewport/viewport.ts +246 -0
  54. package/src/index.ts +0 -7
  55. package/src/styles/components/_vlist.scss +331 -0
  56. package/src/styles/index.scss +1 -1
  57. package/test/components/vlist-selection.test.ts +240 -0
  58. package/test/components/vlist.test.ts +63 -0
  59. package/test/core/collection/adapter.test.ts +161 -0
  60. package/bun.lock +0 -792
  61. package/src/components/list/api.ts +0 -314
  62. package/src/components/list/constants.ts +0 -56
  63. package/src/components/list/features/api.ts +0 -428
  64. package/src/components/list/features/index.ts +0 -31
  65. package/src/components/list/features/list-manager.ts +0 -502
  66. package/src/components/list/index.ts +0 -39
  67. package/src/components/list/list.ts +0 -234
  68. package/src/core/collection/base-collection.ts +0 -100
  69. package/src/core/collection/collection-composer.ts +0 -178
  70. package/src/core/collection/collection.ts +0 -745
  71. package/src/core/collection/constants.ts +0 -172
  72. package/src/core/collection/events.ts +0 -428
  73. package/src/core/collection/features/api/loading.ts +0 -279
  74. package/src/core/collection/features/operations/data-operations.ts +0 -147
  75. package/src/core/collection/index.ts +0 -104
  76. package/src/core/collection/state.ts +0 -497
  77. package/src/core/collection/types.ts +0 -404
  78. package/src/core/compose/features/collection.ts +0 -119
  79. package/src/core/compose/features/selection.ts +0 -213
  80. package/src/core/compose/features/styling.ts +0 -108
  81. package/src/core/list-manager/api.ts +0 -599
  82. package/src/core/list-manager/config.ts +0 -593
  83. package/src/core/list-manager/constants.ts +0 -268
  84. package/src/core/list-manager/features/api.ts +0 -58
  85. package/src/core/list-manager/features/collection/collection.ts +0 -705
  86. package/src/core/list-manager/features/collection/index.ts +0 -17
  87. package/src/core/list-manager/features/viewport/constants.ts +0 -42
  88. package/src/core/list-manager/features/viewport/index.ts +0 -16
  89. package/src/core/list-manager/features/viewport/placeholders.ts +0 -281
  90. package/src/core/list-manager/features/viewport/rendering.ts +0 -575
  91. package/src/core/list-manager/features/viewport/scrollbar.ts +0 -495
  92. package/src/core/list-manager/features/viewport/scrolling.ts +0 -795
  93. package/src/core/list-manager/features/viewport/template.ts +0 -220
  94. package/src/core/list-manager/features/viewport/viewport.ts +0 -654
  95. package/src/core/list-manager/features/viewport/virtual.ts +0 -309
  96. package/src/core/list-manager/index.ts +0 -279
  97. package/src/core/list-manager/list-manager.ts +0 -206
  98. package/src/core/list-manager/types.ts +0 -439
  99. package/src/core/list-manager/utils/calculations.ts +0 -290
  100. package/src/core/list-manager/utils/range-calculator.ts +0 -349
  101. package/src/core/list-manager/utils/speed-tracker.ts +0 -273
  102. package/src/styles/components/_list.scss +0 -244
  103. package/src/types/mtrl.d.ts +0 -6
  104. package/test/components/list.test.ts +0 -256
  105. package/test/core/collection/failed-ranges.test.ts +0 -270
  106. package/test/core/compose/features.test.ts +0 -183
  107. package/test/core/list-manager/features/collection.test.ts +0 -704
  108. package/test/core/list-manager/features/viewport.test.ts +0 -698
  109. package/test/core/list-manager/list-manager.test.ts +0 -593
  110. package/test/core/list-manager/utils/calculations.test.ts +0 -433
  111. package/test/core/list-manager/utils/range-calculator.test.ts +0 -569
  112. package/test/core/list-manager/utils/speed-tracker.test.ts +0 -530
  113. package/tsconfig.build.json +0 -23
  114. /package/src/components/{list → vlist}/features.ts +0 -0
  115. /package/src/core/{compose → viewport}/features/performance.ts +0 -0
@@ -1,28 +1,32 @@
1
1
  /**
2
- * Types for the mtrl-addons list component
3
- *
4
- * Extends collection types with list-specific functionality
2
+ * VList Types - Virtual List with direct viewport integration
5
3
  */
6
4
 
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";
5
+ import type { BaseComponent, ElementComponent } from "mtrl";
6
+ // Collection types are not exposed by mtrl; define minimal interfaces locally
7
+ export interface CollectionItem {
8
+ id: string | number;
9
+ [key: string]: any;
10
+ }
11
+
12
+ export interface CollectionConfig<T = any> {
13
+ adapter?: {
14
+ read(params?: any): Promise<{ items: T[]; meta?: any; error?: any }>;
15
+ };
16
+ }
17
+
18
+ export interface Collection<T = any> {
19
+ loadMissingRanges?: (
20
+ range: { start: number; end: number },
21
+ reason?: string
22
+ ) => Promise<void>;
23
+ }
24
+ import type { ViewportComponent } from "../../core/viewport/types";
18
25
 
19
26
  /**
20
27
  * List item interface - extends collection item
21
28
  */
22
- export interface ListItem extends CollectionItem {
23
- id: string;
24
- [key: string]: any;
25
- }
29
+ export interface ListItem extends CollectionItem {}
26
30
 
27
31
  /**
28
32
  * List adapter interface - extends collection adapter
@@ -71,7 +75,7 @@ export interface ListAdapterResponse<T extends ListItem = ListItem> {
71
75
  /**
72
76
  * List template definition
73
77
  */
74
- export type ListTemplate = TemplateDefinition;
78
+ export type ListTemplate = (item: any, index: number) => string | HTMLElement;
75
79
 
76
80
  /**
77
81
  * List item template function
@@ -89,8 +93,6 @@ export interface ListScrollConfig {
89
93
  virtual?: boolean;
90
94
  /** Item size (fixed) or 'auto' for dynamic - height for vertical, width for horizontal */
91
95
  itemSize?: number | "auto";
92
- /** Estimated item size for dynamic sizing */
93
- estimatedItemSize?: number;
94
96
  /** Number of items to render outside viewport */
95
97
  overscan?: number;
96
98
  /** Enable scroll animations */
@@ -145,6 +147,8 @@ export interface ListSelectionConfig {
145
147
  selectedIndices?: number[];
146
148
  /** Selection change callback */
147
149
  onSelectionChange?: (selectedItems: any[], selectedIndices: number[]) => void;
150
+ /** Require keyboard modifiers for multi-select (default: false) */
151
+ requireModifiers?: boolean;
148
152
  }
149
153
 
150
154
  /**
@@ -174,12 +178,13 @@ export interface ListPaginationConfig {
174
178
  /**
175
179
  * Complete List component configuration
176
180
  */
177
- export interface ListConfig<T = any> extends ListStyleConfig {
181
+ export interface ListConfig<T extends ListItem = ListItem>
182
+ extends ListStyleConfig {
178
183
  // Data layer (Collection) configuration
179
184
  collection?: Partial<CollectionConfig<T>>;
180
185
 
181
186
  // Performance layer (List Manager) configuration
182
- listManager?: Partial<ListManagerConfig>;
187
+ listManager?: Partial<any>;
183
188
 
184
189
  // Pagination configuration
185
190
  pagination?: ListPaginationConfig;
@@ -325,7 +330,7 @@ export interface ListEvents<T = any> {
325
330
  /**
326
331
  * List component API
327
332
  */
328
- export interface ListAPI<T = any> {
333
+ export interface ListAPI<T extends ListItem = ListItem> {
329
334
  // Data management
330
335
  /** Load data */
331
336
  loadData(): Promise<void>;
@@ -451,7 +456,7 @@ export interface ListAPI<T = any> {
451
456
  /**
452
457
  * List component interface (extends mtrl component patterns)
453
458
  */
454
- export interface ListComponent<T = any>
459
+ export interface ListComponent<T extends ListItem = ListItem>
455
460
  extends BaseComponent,
456
461
  ElementComponent,
457
462
  ListAPI<T> {
@@ -459,7 +464,7 @@ export interface ListComponent<T = any>
459
464
  collection: Collection<T>;
460
465
 
461
466
  /** List manager instance (performance layer) */
462
- listManager: ListManager;
467
+ listManager: any;
463
468
 
464
469
  /** Current list state */
465
470
  state: ListState;
@@ -511,3 +516,76 @@ export interface ListFeatures {
511
516
  styling?: boolean;
512
517
  performance?: boolean;
513
518
  }
519
+
520
+ /**
521
+ * VList configuration interface
522
+ */
523
+ export interface VListConfig<T extends ListItem = ListItem> {
524
+ // Container
525
+ parent?: HTMLElement | string;
526
+ container?: HTMLElement | string; // Also support container
527
+
528
+ // Basic properties
529
+ class?: string;
530
+ className?: string; // Also support className
531
+ prefix?: string;
532
+ ariaLabel?: string;
533
+ debug?: boolean;
534
+
535
+ // Data source
536
+ items?: T[];
537
+
538
+ // Template for rendering items
539
+ template?: (
540
+ item: T,
541
+ index: number
542
+ ) => string | HTMLElement | any[] | Record<string, any>;
543
+
544
+ // Collection configuration
545
+ collection?: {
546
+ adapter?: ListAdapter<T>;
547
+ transform?: (item: T) => T;
548
+ };
549
+
550
+ // Pagination configuration
551
+ pagination?: {
552
+ strategy?: "page" | "offset" | "cursor";
553
+ limit?: number;
554
+ };
555
+
556
+ // Virtual scrolling configuration
557
+ virtual?: {
558
+ itemSize?: number;
559
+ overscan?: number;
560
+ };
561
+
562
+ // Scrolling configuration
563
+ scrolling?: {
564
+ orientation?: "vertical" | "horizontal";
565
+ animation?: boolean;
566
+ measureItems?: boolean;
567
+ };
568
+
569
+ // Performance settings
570
+ performance?: {
571
+ recycleElements?: boolean;
572
+ bufferSize?: number;
573
+ renderDebounce?: number;
574
+ maxConcurrentRequests?: number;
575
+ };
576
+
577
+ // Selection configuration
578
+ selection?: ListSelectionConfig;
579
+
580
+ // Event handlers
581
+ on?: ListEventHandlers<T>;
582
+ }
583
+
584
+ export type VListComponent<T extends ListItem = ListItem> = ListComponent<T> & {
585
+ viewport: ViewportComponent["viewport"];
586
+ };
587
+
588
+ export type VListItem = ListItem;
589
+ export type VListAPI<T extends ListItem = ListItem> = ListAPI<T>;
590
+ export type VListState = ListState;
591
+ export type VListEvents<T = any> = ListEvents<T>;
@@ -0,0 +1,92 @@
1
+ // src/components/vlist/vlist.ts
2
+
3
+ /**
4
+ * VList Component - Virtual List with direct viewport integration
5
+ *
6
+ * A simplified virtual list that uses the viewport feature directly
7
+ * without the list-manager abstraction layer.
8
+ */
9
+
10
+ import type { VListConfig, VListComponent, VListItem } from "./types";
11
+
12
+ // Import mtrl compose system
13
+ import { pipe } from "mtrl";
14
+ import { createBase, withElement } from "mtrl";
15
+ import { withEvents, withLifecycle } from "mtrl";
16
+
17
+ // Import viewport feature
18
+ import { withViewport } from "./features/viewport";
19
+ import { withAPI } from "./features/api";
20
+ import { withSelection } from "./features/selection";
21
+
22
+ /**
23
+ * Creates a new VList component using direct viewport integration
24
+ *
25
+ * @param {VListConfig} config - List configuration options
26
+ * @returns {VListComponent} A fully configured virtual list component
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * const vlist = createVList({
31
+ * container: '#my-list',
32
+ * collection: myAdapter,
33
+ * rangeSize: 20,
34
+ * paginationStrategy: 'page',
35
+ * template: (item, index) => [
36
+ * { class: 'viewport-item', attributes: { 'data-id': item.id }},
37
+ * [{ class: 'viewport-item__name', text: item.name }],
38
+ * [{ class: 'viewport-item__value', text: item.value }]
39
+ * ]
40
+ * });
41
+ * ```
42
+ */
43
+ export const createVList = <T extends VListItem = VListItem>(
44
+ config: VListConfig<T> = {}
45
+ ): VListComponent<T> => {
46
+ try {
47
+ // console.log(`📋 Creating VList component with direct viewport integration`);
48
+
49
+ // Note: Transform should be applied by the collection feature in viewport
50
+ // VList should not intercept collection reads as it bypasses the loading manager
51
+
52
+ // Create the component through functional composition
53
+ const enhancers = [
54
+ // 1. Foundation layer
55
+ createBase,
56
+ withEvents(),
57
+ withElement({
58
+ tag: "div",
59
+ className: config.className || "mtrl-vlist",
60
+ attributes: {
61
+ role: "list",
62
+ "aria-label": config.ariaLabel || "Virtual List",
63
+ },
64
+ }),
65
+
66
+ // 2. Viewport integration
67
+ withViewport(config),
68
+
69
+ // 3. Component lifecycle
70
+ withLifecycle(),
71
+
72
+ // 4. Public API layer
73
+ withAPI(config),
74
+ ];
75
+
76
+ // 4.5. Selection capabilities (if enabled) - must be after API
77
+ if (config.selection?.enabled) {
78
+ enhancers.push(withSelection(config));
79
+ }
80
+
81
+ const component = pipe(...enhancers)({
82
+ ...config,
83
+ componentName: "vlist",
84
+ prefix: config.prefix || "mtrl",
85
+ });
86
+
87
+ return component as VListComponent<T>;
88
+ } catch (error) {
89
+ console.error("❌ [VLIST] Failed to create VList component:", error);
90
+ throw error;
91
+ }
92
+ };
@@ -0,0 +1,227 @@
1
+ // src/core/compose/features/gestures.ts
2
+ /**
3
+ * @module core/compose/features
4
+ * @description Adds gesture recognition capabilities to components
5
+ */
6
+
7
+ import { BaseComponent, ElementComponent } from "mtrl";
8
+ import {
9
+ createGestureManager,
10
+ GestureManager,
11
+ GestureConfig,
12
+ GestureHandler,
13
+ AnyGestureEvent,
14
+ } from "../../../gestures";
15
+ import { hasLifecycle, hasEmit } from "mtrl";
16
+
17
+ /**
18
+ * Configuration for gestures feature
19
+ */
20
+ export interface GesturesFeatureConfig extends GestureConfig {
21
+ /**
22
+ * Whether to enable gesture recognition immediately
23
+ * @default true
24
+ */
25
+ enableGestures?: boolean;
26
+
27
+ /**
28
+ * Initial gesture event handlers
29
+ */
30
+ gestureHandlers?: Record<string, GestureHandler>;
31
+
32
+ [key: string]: any;
33
+ }
34
+
35
+ /**
36
+ * Component with gesture recognition capabilities
37
+ */
38
+ export interface GesturesComponent extends BaseComponent {
39
+ /**
40
+ * Gesture manager instance
41
+ */
42
+ gestures: GestureManager;
43
+
44
+ /**
45
+ * Add a gesture event handler
46
+ * @param eventType - Type of gesture event
47
+ * @param handler - Event handler function
48
+ * @returns GesturesComponent for chaining
49
+ */
50
+ onGesture: (eventType: string, handler: GestureHandler) => GesturesComponent;
51
+
52
+ /**
53
+ * Remove a gesture event handler
54
+ * @param eventType - Type of gesture event
55
+ * @param handler - Event handler function
56
+ * @returns GesturesComponent for chaining
57
+ */
58
+ offGesture: (eventType: string, handler: GestureHandler) => GesturesComponent;
59
+
60
+ /**
61
+ * Check if a gesture type is supported on the current device
62
+ * @param gestureType - Type of gesture to check
63
+ * @returns Whether the gesture is supported
64
+ */
65
+ isGestureSupported: (gestureType: string) => boolean;
66
+
67
+ /**
68
+ * Enable gesture recognition
69
+ * @returns GesturesComponent for chaining
70
+ */
71
+ enableGestures: () => GesturesComponent;
72
+
73
+ /**
74
+ * Disable gesture recognition
75
+ * @returns GesturesComponent for chaining
76
+ */
77
+ disableGestures: () => GesturesComponent;
78
+ }
79
+
80
+ /**
81
+ * Adds gesture recognition capabilities to a component.
82
+ * This is a comprehensive gesture feature that adds support for all gesture types.
83
+ * For more lightweight, specific gestures, use the individual gesture features.
84
+ *
85
+ * @param config - Configuration object containing gesture settings
86
+ * @returns Function that enhances a component with gesture capabilities
87
+ *
88
+ * @example
89
+ * ```ts
90
+ * // Add gesture recognition to a component
91
+ * const component = pipe(
92
+ * createBase,
93
+ * withElement(...),
94
+ * withGestures({
95
+ * swipeThreshold: 50,
96
+ * gestureHandlers: {
97
+ * 'tap': (e) => handleTap(e),
98
+ * 'swipeleft': (e) => navigateForward(e),
99
+ * 'swiperight': (e) => navigateBack(e)
100
+ * }
101
+ * })
102
+ * )(config);
103
+ * ```
104
+ */
105
+ export const withGestures =
106
+ (config: GesturesFeatureConfig = {}) =>
107
+ <C extends ElementComponent>(component: C): C & GesturesComponent => {
108
+ if (!component.element) {
109
+ console.warn("Cannot add gesture recognition: missing element");
110
+ return component as C & GesturesComponent;
111
+ }
112
+
113
+ // Default configuration
114
+ const {
115
+ enableGestures = true,
116
+ gestureHandlers = {},
117
+ ...gestureConfig
118
+ } = config;
119
+
120
+ // Create gesture manager
121
+ const gestureManager = createGestureManager(
122
+ component.element,
123
+ gestureConfig
124
+ );
125
+
126
+ // Add initial gesture handlers
127
+ Object.entries(gestureHandlers).forEach(([eventType, handler]) => {
128
+ gestureManager.on(eventType, handler);
129
+ });
130
+
131
+ // Enable/disable based on config
132
+ if (!enableGestures) {
133
+ gestureManager.disable();
134
+ }
135
+
136
+ // Connect with existing event system if available
137
+ if (hasEmit(component)) {
138
+ // Forward gesture events to the component's event system
139
+ const forwardGestureEvents = (event: AnyGestureEvent) => {
140
+ component.emit(event.type, event);
141
+ };
142
+
143
+ // Register forwarder for common gesture types
144
+ [
145
+ "tap",
146
+ "swipe",
147
+ "swipeleft",
148
+ "swiperight",
149
+ "swipeup",
150
+ "swipedown",
151
+ "longpress",
152
+ "pinch",
153
+ "rotate",
154
+ "pan",
155
+ ].forEach((type) => {
156
+ gestureManager.on(type, forwardGestureEvents);
157
+ });
158
+ }
159
+
160
+ // Handle lifecycle integration
161
+ if (hasLifecycle(component)) {
162
+ const originalDestroy = component.lifecycle.destroy;
163
+
164
+ component.lifecycle.destroy = () => {
165
+ // Clean up gesture manager
166
+ gestureManager.destroy();
167
+
168
+ // Call original destroy method
169
+ originalDestroy.call(component.lifecycle);
170
+ };
171
+ }
172
+
173
+ // Create enhanced component
174
+ return {
175
+ ...component,
176
+ gestures: gestureManager,
177
+
178
+ /**
179
+ * Add a gesture event handler
180
+ * @param eventType - Type of gesture event
181
+ * @param handler - Event handler function
182
+ * @returns GesturesComponent for chaining
183
+ */
184
+ onGesture(eventType: string, handler: GestureHandler) {
185
+ gestureManager.on(eventType, handler);
186
+ return this;
187
+ },
188
+
189
+ /**
190
+ * Remove a gesture event handler
191
+ * @param eventType - Type of gesture event
192
+ * @param handler - Event handler function
193
+ * @returns GesturesComponent for chaining
194
+ */
195
+ offGesture(eventType: string, handler: GestureHandler) {
196
+ gestureManager.off(eventType, handler);
197
+ return this;
198
+ },
199
+
200
+ /**
201
+ * Check if a gesture type is supported on the current device
202
+ * @param gestureType - Type of gesture to check
203
+ * @returns Whether the gesture is supported
204
+ */
205
+ isGestureSupported(gestureType: string) {
206
+ return gestureManager.isSupported(gestureType);
207
+ },
208
+
209
+ /**
210
+ * Enable gesture recognition
211
+ * @returns GesturesComponent for chaining
212
+ */
213
+ enableGestures() {
214
+ gestureManager.enable();
215
+ return this;
216
+ },
217
+
218
+ /**
219
+ * Disable gesture recognition
220
+ * @returns GesturesComponent for chaining
221
+ */
222
+ disableGestures() {
223
+ gestureManager.disable();
224
+ return this;
225
+ },
226
+ };
227
+ };