mtrl-addons 0.1.1 → 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.
- package/build.js +139 -108
- package/package.json +13 -4
- package/scripts/debug/vlist-selection.ts +121 -0
- package/src/components/index.ts +5 -41
- package/src/components/{list → vlist}/config.ts +66 -95
- package/src/components/vlist/constants.ts +23 -0
- package/src/components/vlist/features/api.ts +322 -0
- package/src/components/vlist/features/index.ts +10 -0
- package/src/components/vlist/features/selection.ts +444 -0
- package/src/components/vlist/features/viewport.ts +65 -0
- package/src/components/vlist/index.ts +16 -0
- package/src/components/{list → vlist}/types.ts +104 -26
- package/src/components/vlist/vlist.ts +92 -0
- package/src/core/compose/features/gestures/index.ts +227 -0
- package/src/core/compose/features/gestures/longpress.ts +383 -0
- package/src/core/compose/features/gestures/pan.ts +424 -0
- package/src/core/compose/features/gestures/pinch.ts +475 -0
- package/src/core/compose/features/gestures/rotate.ts +485 -0
- package/src/core/compose/features/gestures/swipe.ts +492 -0
- package/src/core/compose/features/gestures/tap.ts +334 -0
- package/src/core/compose/features/index.ts +2 -38
- package/src/core/compose/index.ts +13 -29
- package/src/core/gestures/index.ts +31 -0
- package/src/core/gestures/longpress.ts +68 -0
- package/src/core/gestures/manager.ts +418 -0
- package/src/core/gestures/pan.ts +48 -0
- package/src/core/gestures/pinch.ts +58 -0
- package/src/core/gestures/rotate.ts +58 -0
- package/src/core/gestures/swipe.ts +66 -0
- package/src/core/gestures/tap.ts +45 -0
- package/src/core/gestures/types.ts +387 -0
- package/src/core/gestures/utils.ts +128 -0
- package/src/core/index.ts +27 -151
- package/src/core/layout/schema.ts +73 -35
- package/src/core/layout/types.ts +5 -2
- package/src/core/viewport/constants.ts +140 -0
- package/src/core/viewport/features/base.ts +73 -0
- package/src/core/viewport/features/collection.ts +882 -0
- package/src/core/viewport/features/events.ts +130 -0
- package/src/core/viewport/features/index.ts +20 -0
- package/src/core/{list-manager/features/viewport → viewport/features}/item-size.ts +27 -30
- package/src/core/{list-manager/features/viewport → viewport/features}/loading.ts +4 -4
- package/src/core/viewport/features/momentum.ts +260 -0
- package/src/core/viewport/features/placeholders.ts +335 -0
- package/src/core/viewport/features/rendering.ts +568 -0
- package/src/core/viewport/features/scrollbar.ts +434 -0
- package/src/core/viewport/features/scrolling.ts +618 -0
- package/src/core/viewport/features/utils.ts +88 -0
- package/src/core/viewport/features/virtual.ts +384 -0
- package/src/core/viewport/index.ts +31 -0
- package/src/core/viewport/types.ts +133 -0
- package/src/core/viewport/utils/speed-tracker.ts +79 -0
- package/src/core/viewport/viewport.ts +246 -0
- package/src/index.ts +0 -7
- package/src/styles/components/_vlist.scss +331 -0
- package/src/styles/index.scss +1 -1
- package/test/components/vlist-selection.test.ts +240 -0
- package/test/components/vlist.test.ts +63 -0
- package/test/core/collection/adapter.test.ts +161 -0
- package/bun.lock +0 -792
- package/src/components/list/api.ts +0 -314
- package/src/components/list/constants.ts +0 -56
- package/src/components/list/features/api.ts +0 -428
- package/src/components/list/features/index.ts +0 -31
- package/src/components/list/features/list-manager.ts +0 -502
- package/src/components/list/index.ts +0 -39
- package/src/components/list/list.ts +0 -234
- package/src/core/collection/base-collection.ts +0 -100
- package/src/core/collection/collection-composer.ts +0 -178
- package/src/core/collection/collection.ts +0 -745
- package/src/core/collection/constants.ts +0 -172
- package/src/core/collection/events.ts +0 -428
- package/src/core/collection/features/api/loading.ts +0 -279
- package/src/core/collection/features/operations/data-operations.ts +0 -147
- package/src/core/collection/index.ts +0 -104
- package/src/core/collection/state.ts +0 -497
- package/src/core/collection/types.ts +0 -404
- package/src/core/compose/features/collection.ts +0 -119
- package/src/core/compose/features/selection.ts +0 -213
- package/src/core/compose/features/styling.ts +0 -108
- package/src/core/list-manager/api.ts +0 -599
- package/src/core/list-manager/config.ts +0 -593
- package/src/core/list-manager/constants.ts +0 -268
- package/src/core/list-manager/features/api.ts +0 -58
- package/src/core/list-manager/features/collection/collection.ts +0 -705
- package/src/core/list-manager/features/collection/index.ts +0 -17
- package/src/core/list-manager/features/viewport/constants.ts +0 -42
- package/src/core/list-manager/features/viewport/index.ts +0 -16
- package/src/core/list-manager/features/viewport/placeholders.ts +0 -281
- package/src/core/list-manager/features/viewport/rendering.ts +0 -575
- package/src/core/list-manager/features/viewport/scrollbar.ts +0 -495
- package/src/core/list-manager/features/viewport/scrolling.ts +0 -795
- package/src/core/list-manager/features/viewport/template.ts +0 -220
- package/src/core/list-manager/features/viewport/viewport.ts +0 -654
- package/src/core/list-manager/features/viewport/virtual.ts +0 -309
- package/src/core/list-manager/index.ts +0 -279
- package/src/core/list-manager/list-manager.ts +0 -206
- package/src/core/list-manager/types.ts +0 -439
- package/src/core/list-manager/utils/calculations.ts +0 -290
- package/src/core/list-manager/utils/range-calculator.ts +0 -349
- package/src/core/list-manager/utils/speed-tracker.ts +0 -273
- package/src/styles/components/_list.scss +0 -244
- package/src/types/mtrl.d.ts +0 -6
- package/test/components/list.test.ts +0 -256
- package/test/core/collection/failed-ranges.test.ts +0 -270
- package/test/core/compose/features.test.ts +0 -183
- package/test/core/list-manager/features/collection.test.ts +0 -704
- package/test/core/list-manager/features/viewport.test.ts +0 -698
- package/test/core/list-manager/list-manager.test.ts +0 -593
- package/test/core/list-manager/utils/calculations.test.ts +0 -433
- package/test/core/list-manager/utils/range-calculator.test.ts +0 -569
- package/test/core/list-manager/utils/speed-tracker.test.ts +0 -530
- package/tsconfig.build.json +0 -14
- /package/src/components/{list → vlist}/features.ts +0 -0
- /package/src/core/{compose → viewport}/features/performance.ts +0 -0
|
@@ -180,45 +180,80 @@ function releaseFragment(fragment: DocumentFragment): void {
|
|
|
180
180
|
|
|
181
181
|
/**
|
|
182
182
|
* Optimized class processing with minimal string operations
|
|
183
|
-
* Handles arrays, strings,
|
|
183
|
+
* Handles arrays, strings, className aliases, and rawClass efficiently
|
|
184
184
|
*/
|
|
185
185
|
function processClassNames(
|
|
186
186
|
options: Record<string, any>,
|
|
187
187
|
skipPrefix = false
|
|
188
188
|
): Record<string, any> {
|
|
189
|
-
if (!options
|
|
189
|
+
if (!options) return options;
|
|
190
190
|
|
|
191
|
-
const
|
|
192
|
-
|
|
191
|
+
const hasRawClass = options.rawClass;
|
|
192
|
+
const hasRegularClass = options.class || options.className;
|
|
193
193
|
|
|
194
|
-
|
|
194
|
+
// Fast path: no class properties at all
|
|
195
|
+
if (!hasRawClass && !hasRegularClass) return options;
|
|
196
|
+
|
|
197
|
+
// Fast path: only rawClass and skipping prefix (most common rawClass scenario)
|
|
198
|
+
if (hasRawClass && !hasRegularClass && skipPrefix) {
|
|
199
|
+
const processed = { ...options };
|
|
200
|
+
delete processed.rawClass;
|
|
195
201
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
// Handle arrays by joining first
|
|
200
|
-
if (Array.isArray(processed.class)) {
|
|
201
|
-
classString += processed.class.join(" ");
|
|
202
|
+
// Direct assignment for simple string
|
|
203
|
+
if (typeof hasRawClass === "string") {
|
|
204
|
+
processed.class = hasRawClass;
|
|
202
205
|
} else {
|
|
203
|
-
|
|
206
|
+
// Handle array case
|
|
207
|
+
processed.class = hasRawClass.join(" ");
|
|
208
|
+
}
|
|
209
|
+
return processed;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Full processing path (only when needed)
|
|
213
|
+
const processed = { ...options };
|
|
214
|
+
let finalClasses = "";
|
|
215
|
+
|
|
216
|
+
// Handle prefixed classes only if not skipping prefix
|
|
217
|
+
if (!skipPrefix && hasRegularClass) {
|
|
218
|
+
let prefixedString = "";
|
|
219
|
+
|
|
220
|
+
if (processed.class) {
|
|
221
|
+
prefixedString += Array.isArray(processed.class)
|
|
222
|
+
? processed.class.join(" ")
|
|
223
|
+
: processed.class;
|
|
224
|
+
}
|
|
225
|
+
if (processed.className) {
|
|
226
|
+
prefixedString += (prefixedString ? " " : "") + processed.className;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (prefixedString) {
|
|
230
|
+
finalClasses = prefixedString
|
|
231
|
+
.split(/\s+/)
|
|
232
|
+
.filter(Boolean)
|
|
233
|
+
.map((cls) =>
|
|
234
|
+
cls.startsWith(PREFIX_WITH_DASH) ? cls : PREFIX_WITH_DASH + cls
|
|
235
|
+
)
|
|
236
|
+
.join(" ");
|
|
204
237
|
}
|
|
205
238
|
}
|
|
206
|
-
|
|
207
|
-
|
|
239
|
+
|
|
240
|
+
// Handle rawClass (always processed when present)
|
|
241
|
+
if (hasRawClass) {
|
|
242
|
+
const rawString = Array.isArray(hasRawClass)
|
|
243
|
+
? hasRawClass.filter(Boolean).join(" ")
|
|
244
|
+
: hasRawClass;
|
|
245
|
+
|
|
246
|
+
finalClasses += (finalClasses ? " " : "") + rawString;
|
|
208
247
|
}
|
|
209
248
|
|
|
210
|
-
if (
|
|
211
|
-
|
|
212
|
-
processed.class = classString
|
|
213
|
-
.split(/\s+/)
|
|
214
|
-
.filter(Boolean)
|
|
215
|
-
.map((cls) =>
|
|
216
|
-
cls && !cls.startsWith(PREFIX_WITH_DASH) ? PREFIX_WITH_DASH + cls : cls
|
|
217
|
-
)
|
|
218
|
-
.join(" ");
|
|
249
|
+
if (finalClasses) {
|
|
250
|
+
processed.class = finalClasses;
|
|
219
251
|
}
|
|
220
252
|
|
|
253
|
+
// Clean up in one operation
|
|
221
254
|
delete processed.className;
|
|
255
|
+
delete processed.rawClass;
|
|
256
|
+
|
|
222
257
|
return processed;
|
|
223
258
|
}
|
|
224
259
|
|
|
@@ -272,7 +307,7 @@ function extractParameters(
|
|
|
272
307
|
return {
|
|
273
308
|
creator: creator || defaultCreator,
|
|
274
309
|
name,
|
|
275
|
-
options: options || {},
|
|
310
|
+
options: (options || {}) as Record<string, any>,
|
|
276
311
|
consumed,
|
|
277
312
|
};
|
|
278
313
|
}
|
|
@@ -618,7 +653,7 @@ function getLayoutType(element: HTMLElement): string {
|
|
|
618
653
|
* Optimized with parameter extraction and integrated configuration
|
|
619
654
|
*/
|
|
620
655
|
function processArraySchema(
|
|
621
|
-
schema: SchemaItem[],
|
|
656
|
+
schema: SchemaItem[] | any,
|
|
622
657
|
parentElement: HTMLElement | null = null,
|
|
623
658
|
level: number = 0,
|
|
624
659
|
options: LayoutOptions = {}
|
|
@@ -633,7 +668,7 @@ function processArraySchema(
|
|
|
633
668
|
return createLayoutResult(layout);
|
|
634
669
|
}
|
|
635
670
|
|
|
636
|
-
const defaultCreator = options.creator || createElement;
|
|
671
|
+
const defaultCreator = (options as any).creator || createElement;
|
|
637
672
|
|
|
638
673
|
for (let i = 0; i < schema.length; i++) {
|
|
639
674
|
const item = schema[i];
|
|
@@ -668,12 +703,15 @@ function processArraySchema(
|
|
|
668
703
|
// Advance index by consumed items minus 1 (loop increment handles the +1)
|
|
669
704
|
i += consumed - 1;
|
|
670
705
|
|
|
671
|
-
// Process options with prefix
|
|
706
|
+
// Process options with prefix - optimized decision logic
|
|
672
707
|
const shouldApplyPrefix =
|
|
673
708
|
"prefix" in itemOptions ? itemOptions.prefix : options.prefix !== false;
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
709
|
+
|
|
710
|
+
// Fast path: process only when needed
|
|
711
|
+
const processedOptions =
|
|
712
|
+
shouldApplyPrefix || itemOptions.rawClass
|
|
713
|
+
? processClassNames(itemOptions, !shouldApplyPrefix)
|
|
714
|
+
: itemOptions; // No copy needed if no processing
|
|
677
715
|
|
|
678
716
|
// Add name to options if needed
|
|
679
717
|
if (
|
|
@@ -734,7 +772,7 @@ function processArraySchema(
|
|
|
734
772
|
* Simplified and optimized for better performance
|
|
735
773
|
*/
|
|
736
774
|
function processObjectSchema(
|
|
737
|
-
schema: Record<string, any
|
|
775
|
+
schema: Record<string, any> | string,
|
|
738
776
|
parentElement: HTMLElement | null = null,
|
|
739
777
|
options: LayoutOptions = {}
|
|
740
778
|
): LayoutResult {
|
|
@@ -742,8 +780,8 @@ function processObjectSchema(
|
|
|
742
780
|
const defaultCreator = options.creator || createElement;
|
|
743
781
|
|
|
744
782
|
// Handle root element creation
|
|
745
|
-
if (schema.element && !parentElement) {
|
|
746
|
-
const elementDef = schema.element;
|
|
783
|
+
if ((schema as any).element && !parentElement) {
|
|
784
|
+
const elementDef = (schema as any).element;
|
|
747
785
|
const createElementFn = elementDef.creator || defaultCreator;
|
|
748
786
|
|
|
749
787
|
const elementOptions = elementDef.options || {};
|
|
@@ -778,8 +816,8 @@ function processObjectSchema(
|
|
|
778
816
|
// Process normal schema elements
|
|
779
817
|
const fragment = parentElement ? createFragment() : null;
|
|
780
818
|
|
|
781
|
-
for (const key in schema) {
|
|
782
|
-
const def = schema[key];
|
|
819
|
+
for (const key in schema as Record<string, any>) {
|
|
820
|
+
const def = (schema as Record<string, any>)[key];
|
|
783
821
|
if (!def) continue;
|
|
784
822
|
|
|
785
823
|
const elementCreator = def.creator || defaultCreator;
|
package/src/core/layout/types.ts
CHANGED
|
@@ -37,12 +37,15 @@ export interface ElementOptions extends Record<string, any> {
|
|
|
37
37
|
/** Layout item configuration */
|
|
38
38
|
layoutItem?: LayoutItemConfig;
|
|
39
39
|
|
|
40
|
-
/** CSS classes to apply */
|
|
40
|
+
/** CSS classes to apply (with automatic mtrl- prefix) */
|
|
41
41
|
class?: string;
|
|
42
42
|
|
|
43
43
|
/** Additional CSS classes (alias for class) */
|
|
44
44
|
className?: string;
|
|
45
45
|
|
|
46
|
+
/** CSS classes to apply without prefix */
|
|
47
|
+
rawClass?: string | string[];
|
|
48
|
+
|
|
46
49
|
/** HTML tag name for createElement */
|
|
47
50
|
tag?: string;
|
|
48
51
|
|
|
@@ -89,4 +92,4 @@ export interface Schema {
|
|
|
89
92
|
|
|
90
93
|
/** Additional elements */
|
|
91
94
|
[key: string]: ElementDefinition | undefined;
|
|
92
|
-
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// src/core/viewport/constants.ts
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Viewport Constants
|
|
5
|
+
* Centralized constants for all viewport functionality
|
|
6
|
+
* Consolidated from viewport, viewport/features, and list-manager constants
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export const VIEWPORT_CONSTANTS = {
|
|
10
|
+
// Virtual scrolling defaults
|
|
11
|
+
VIRTUAL_SCROLL: {
|
|
12
|
+
DEFAULT_ITEM_SIZE: 48,
|
|
13
|
+
OVERSCAN_BUFFER: 2,
|
|
14
|
+
SCROLL_SENSITIVITY: 0.7,
|
|
15
|
+
MAX_VIRTUAL_SIZE: 100 * 1000 * 1000, // 100M pixels - modern browsers can handle this
|
|
16
|
+
AUTO_DETECT_ITEM_SIZE: true, // Enable automatic item size detection
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
// Scrolling settings
|
|
20
|
+
SCROLLING: {
|
|
21
|
+
OVERSCAN: 1, // From features/constants
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
// Rendering settings
|
|
25
|
+
RENDERING: {
|
|
26
|
+
// Element recycling
|
|
27
|
+
DEFAULT_MAX_POOL_SIZE: 100,
|
|
28
|
+
CLASSES: {
|
|
29
|
+
ITEM: "viewport-item",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
// Loading settings
|
|
34
|
+
LOADING: {
|
|
35
|
+
CANCEL_THRESHOLD: 2, // px/ms - velocity above which loads cancel
|
|
36
|
+
MAX_CONCURRENT_REQUESTS: 1, // Parallel requests allowed
|
|
37
|
+
DEFAULT_RANGE_SIZE: 20, // Items per request
|
|
38
|
+
DEBOUNCE_LOADING: 150, // Debounce delay (ms)
|
|
39
|
+
MIN_RANGE_SIZE: 10, // Minimum items per load
|
|
40
|
+
MAX_RANGE_SIZE: 100, // Maximum items per load
|
|
41
|
+
REQUEST_TIMEOUT: 5000, // Request timeout (ms)
|
|
42
|
+
RETRY_ATTEMPTS: 2, // Failed request retries
|
|
43
|
+
RETRY_DELAY: 1000, // Delay between retries (ms)
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
// Request queue configuration (from features/constants)
|
|
47
|
+
REQUEST_QUEUE: {
|
|
48
|
+
ENABLED: true, // Enable request queuing
|
|
49
|
+
MAX_QUEUE_SIZE: 1, // Max queued requests
|
|
50
|
+
MAX_ACTIVE_REQUESTS: 2, // Max concurrent active requests
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
// Placeholder settings
|
|
54
|
+
PLACEHOLDER: {
|
|
55
|
+
MASK_CHARACTER: "X", // Updated from list-manager
|
|
56
|
+
CLASS: "viewport-item--placeholder",
|
|
57
|
+
MAX_SAMPLE_SIZE: 20,
|
|
58
|
+
PLACEHOLDER_FLAG: "_placeholder",
|
|
59
|
+
RANDOM_LENGTH_VARIANCE: true,
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
// Speed tracking (from list-manager)
|
|
63
|
+
SPEED_TRACKING: {
|
|
64
|
+
// Velocity calculation
|
|
65
|
+
DECELERATION_FACTOR: 0.85, // velocity decay per frame
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
// Momentum settings
|
|
69
|
+
MOMENTUM: {
|
|
70
|
+
ENABLED: false, // Enable momentum by default
|
|
71
|
+
DECELERATION_FACTOR: 0.85, // How quickly velocity decreases per frame
|
|
72
|
+
MIN_VELOCITY: 0.1, // Minimum velocity before stopping (px/ms)
|
|
73
|
+
MIN_DURATION: 300, // Maximum gesture duration to trigger momentum (ms)
|
|
74
|
+
MIN_VELOCITY_THRESHOLD: 0.5, // Minimum velocity to trigger momentum (px/ms)
|
|
75
|
+
FRAME_TIME: 16, // Assumed frame time for calculations (ms)
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
// Initial load configuration (from list-manager)
|
|
79
|
+
INITIAL_LOAD: {
|
|
80
|
+
STRATEGY: "placeholders", // "placeholders" | "direct" | "progressive"
|
|
81
|
+
VIEWPORT_MULTIPLIER: 1.5, // load 1.5x viewport capacity
|
|
82
|
+
MIN_ITEMS: 10,
|
|
83
|
+
MAX_ITEMS: 100,
|
|
84
|
+
PLACEHOLDER_COUNT: 20, // default placeholder count
|
|
85
|
+
SHOW_LOADING_STATE: true,
|
|
86
|
+
LOADING_DELAY: 100, // ms - delay before showing loading state
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
// Scrollbar settings (from list-manager)
|
|
90
|
+
SCROLLBAR: {
|
|
91
|
+
// CSS classes
|
|
92
|
+
CLASSES: {
|
|
93
|
+
SCROLLBAR: "viewport__scrollbar",
|
|
94
|
+
SCROLLBAR_TRACK: "viewport__scrollbar-track",
|
|
95
|
+
SCROLLBAR_THUMB: "viewport__scrollbar-thumb",
|
|
96
|
+
SCROLLBAR_VISIBLE: "viewport__scrollbar--visible",
|
|
97
|
+
SCROLLBAR_DRAGGING: "viewport__scrollbar--dragging",
|
|
98
|
+
SCROLLBAR_THUMB_DRAGGING: "viewport__scrollbar-thumb--dragging",
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
// Orientation (from list-manager)
|
|
103
|
+
ORIENTATION: {
|
|
104
|
+
DEFAULT_ORIENTATION: "vertical",
|
|
105
|
+
DEFAULT_CROSS_AXIS_ALIGNMENT: "stretch",
|
|
106
|
+
REVERSE_DIRECTION: false,
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
PAGINATION: {
|
|
110
|
+
DEFAULT_STRATEGY: "offset" as "offset" | "page" | "cursor",
|
|
111
|
+
DEFAULT_LIMIT: 20,
|
|
112
|
+
STRATEGIES: {
|
|
113
|
+
PAGE: "page" as const,
|
|
114
|
+
OFFSET: "offset" as const,
|
|
115
|
+
CURSOR: "cursor" as const,
|
|
116
|
+
},
|
|
117
|
+
CURSOR_CLEANUP_INTERVAL: 60000, // Clean up old cursors every minute
|
|
118
|
+
MAX_CURSOR_MAP_SIZE: 1000, // Maximum number of cursors to keep in memory
|
|
119
|
+
// Cursor-specific virtual sizing
|
|
120
|
+
CURSOR_SCROLL_MARGIN_MULTIPLIER: 3, // Multiply rangeSize by this for scroll margin
|
|
121
|
+
CURSOR_MIN_VIRTUAL_SIZE_MULTIPLIER: 3, // Minimum virtual size as multiplier of rangeSize
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Type for overriding constants at runtime
|
|
127
|
+
*/
|
|
128
|
+
export type ViewportConstants = typeof VIEWPORT_CONSTANTS;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Helper function to merge user constants with defaults
|
|
132
|
+
*/
|
|
133
|
+
export function mergeConstants(
|
|
134
|
+
userConstants: Partial<ViewportConstants> = {}
|
|
135
|
+
): ViewportConstants {
|
|
136
|
+
return {
|
|
137
|
+
...VIEWPORT_CONSTANTS,
|
|
138
|
+
...userConstants,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// src/core/viewport/features/base.ts
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Base Viewport Feature - Sets up core viewport structure
|
|
5
|
+
* Provides the foundation for other viewport features
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ViewportContext, ViewportComponent } from "../types";
|
|
9
|
+
import { wrapInitialize, getViewportState } from "./utils";
|
|
10
|
+
|
|
11
|
+
export interface BaseConfig {
|
|
12
|
+
className?: string;
|
|
13
|
+
orientation?: "vertical" | "horizontal";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Base feature for viewport
|
|
18
|
+
* Sets up the DOM structure and core styles
|
|
19
|
+
*/
|
|
20
|
+
export const withBase = (config: BaseConfig = {}) => {
|
|
21
|
+
return <T extends ViewportContext & ViewportComponent>(component: T): T => {
|
|
22
|
+
const { className = "mtrl-viewport", orientation = "vertical" } = config;
|
|
23
|
+
|
|
24
|
+
// Use the shared wrapper utility
|
|
25
|
+
wrapInitialize(component, () => {
|
|
26
|
+
const element = component.element;
|
|
27
|
+
if (!element) return;
|
|
28
|
+
|
|
29
|
+
// Create viewport structure
|
|
30
|
+
let viewportElement = element.querySelector(
|
|
31
|
+
".mtrl-viewport"
|
|
32
|
+
) as HTMLElement;
|
|
33
|
+
|
|
34
|
+
if (!viewportElement) {
|
|
35
|
+
viewportElement = document.createElement("div");
|
|
36
|
+
viewportElement.className = className;
|
|
37
|
+
viewportElement.style.cssText = `
|
|
38
|
+
position: relative;
|
|
39
|
+
width: 100%;
|
|
40
|
+
height: 100%;
|
|
41
|
+
overflow: hidden;
|
|
42
|
+
`;
|
|
43
|
+
|
|
44
|
+
// Set orientation
|
|
45
|
+
viewportElement.setAttribute("data-orientation", orientation);
|
|
46
|
+
|
|
47
|
+
// Create items container
|
|
48
|
+
const itemsContainer = document.createElement("div");
|
|
49
|
+
itemsContainer.className = "mtrl-viewport-items";
|
|
50
|
+
itemsContainer.style.cssText = `
|
|
51
|
+
position: relative;
|
|
52
|
+
width: 100%;
|
|
53
|
+
`;
|
|
54
|
+
|
|
55
|
+
viewportElement.appendChild(itemsContainer);
|
|
56
|
+
element.appendChild(viewportElement);
|
|
57
|
+
|
|
58
|
+
// Update viewport state with containers using utility
|
|
59
|
+
const state = getViewportState(component);
|
|
60
|
+
if (state) {
|
|
61
|
+
state.viewportElement = viewportElement;
|
|
62
|
+
state.itemsContainer = itemsContainer;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Store references on component
|
|
66
|
+
(component as any).viewportElement = viewportElement;
|
|
67
|
+
(component as any).itemsContainer = itemsContainer;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return component;
|
|
72
|
+
};
|
|
73
|
+
};
|