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.
- package/{src/components/index.ts → dist/components/index.d.ts} +0 -2
- package/dist/components/vlist/config.d.ts +86 -0
- package/{src/components/vlist/constants.ts → dist/components/vlist/constants.d.ts} +10 -11
- package/dist/components/vlist/features/api.d.ts +7 -0
- package/{src/components/vlist/features/index.ts → dist/components/vlist/features/index.d.ts} +0 -2
- package/dist/components/vlist/features/selection.d.ts +6 -0
- package/dist/components/vlist/features/viewport.d.ts +9 -0
- package/dist/components/vlist/features.d.ts +31 -0
- package/{src/components/vlist/index.ts → dist/components/vlist/index.d.ts} +1 -9
- package/dist/components/vlist/types.d.ts +596 -0
- package/dist/components/vlist/vlist.d.ts +29 -0
- package/dist/core/compose/features/gestures/index.d.ts +86 -0
- package/dist/core/compose/features/gestures/longpress.d.ts +85 -0
- package/dist/core/compose/features/gestures/pan.d.ts +108 -0
- package/dist/core/compose/features/gestures/pinch.d.ts +111 -0
- package/dist/core/compose/features/gestures/rotate.d.ts +111 -0
- package/dist/core/compose/features/gestures/swipe.d.ts +149 -0
- package/dist/core/compose/features/gestures/tap.d.ts +79 -0
- package/{src/core/compose/features/index.ts → dist/core/compose/features/index.d.ts} +1 -2
- package/{src/core/compose/index.ts → dist/core/compose/index.d.ts} +2 -11
- package/{src/core/gestures/index.ts → dist/core/gestures/index.d.ts} +1 -20
- package/dist/core/gestures/longpress.d.ts +23 -0
- package/dist/core/gestures/manager.d.ts +14 -0
- package/dist/core/gestures/pan.d.ts +12 -0
- package/dist/core/gestures/pinch.d.ts +14 -0
- package/dist/core/gestures/rotate.d.ts +14 -0
- package/dist/core/gestures/swipe.d.ts +20 -0
- package/dist/core/gestures/tap.d.ts +12 -0
- package/dist/core/gestures/types.d.ts +320 -0
- package/dist/core/gestures/utils.d.ts +57 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/layout/config.d.ts +33 -0
- package/dist/core/layout/index.d.ts +51 -0
- package/dist/core/layout/jsx.d.ts +65 -0
- package/dist/core/layout/schema.d.ts +112 -0
- package/dist/core/layout/types.d.ts +69 -0
- package/dist/core/viewport/constants.d.ts +105 -0
- package/dist/core/viewport/features/base.d.ts +14 -0
- package/dist/core/viewport/features/collection.d.ts +41 -0
- package/dist/core/viewport/features/events.d.ts +13 -0
- package/{src/core/viewport/features/index.ts → dist/core/viewport/features/index.d.ts} +0 -7
- package/dist/core/viewport/features/item-size.d.ts +30 -0
- package/dist/core/viewport/features/loading.d.ts +34 -0
- package/dist/core/viewport/features/momentum.d.ts +17 -0
- package/dist/core/viewport/features/performance.d.ts +53 -0
- package/dist/core/viewport/features/placeholders.d.ts +38 -0
- package/dist/core/viewport/features/rendering.d.ts +16 -0
- package/dist/core/viewport/features/scrollbar.d.ts +26 -0
- package/dist/core/viewport/features/scrolling.d.ts +16 -0
- package/dist/core/viewport/features/utils.d.ts +43 -0
- package/dist/core/viewport/features/virtual.d.ts +18 -0
- package/{src/core/viewport/index.ts → dist/core/viewport/index.d.ts} +1 -17
- package/dist/core/viewport/types.d.ts +96 -0
- package/dist/core/viewport/utils/speed-tracker.d.ts +22 -0
- package/dist/core/viewport/viewport.d.ts +11 -0
- package/{src/index.ts → dist/index.d.ts} +0 -4
- package/dist/index.js +5143 -0
- package/dist/index.mjs +5111 -0
- package/dist/styles.css +254 -0
- package/dist/styles.css.map +1 -0
- package/package.json +6 -1
- package/src/styles/components/_vlist.scss +234 -213
- package/.cursorrules +0 -117
- package/AI.md +0 -241
- package/build.js +0 -201
- package/scripts/analyze-orphaned-functions.ts +0 -387
- package/scripts/debug/vlist-selection.ts +0 -121
- package/src/components/vlist/config.ts +0 -323
- package/src/components/vlist/features/api.ts +0 -322
- package/src/components/vlist/features/selection.ts +0 -444
- package/src/components/vlist/features/viewport.ts +0 -65
- package/src/components/vlist/features.ts +0 -112
- package/src/components/vlist/types.ts +0 -591
- package/src/components/vlist/vlist.ts +0 -92
- package/src/core/compose/features/gestures/index.ts +0 -227
- package/src/core/compose/features/gestures/longpress.ts +0 -383
- package/src/core/compose/features/gestures/pan.ts +0 -424
- package/src/core/compose/features/gestures/pinch.ts +0 -475
- package/src/core/compose/features/gestures/rotate.ts +0 -485
- package/src/core/compose/features/gestures/swipe.ts +0 -492
- package/src/core/compose/features/gestures/tap.ts +0 -334
- package/src/core/gestures/longpress.ts +0 -68
- package/src/core/gestures/manager.ts +0 -418
- package/src/core/gestures/pan.ts +0 -48
- package/src/core/gestures/pinch.ts +0 -58
- package/src/core/gestures/rotate.ts +0 -58
- package/src/core/gestures/swipe.ts +0 -66
- package/src/core/gestures/tap.ts +0 -45
- package/src/core/gestures/types.ts +0 -387
- package/src/core/gestures/utils.ts +0 -128
- package/src/core/index.ts +0 -43
- package/src/core/layout/config.ts +0 -102
- package/src/core/layout/index.ts +0 -168
- package/src/core/layout/jsx.ts +0 -174
- package/src/core/layout/schema.ts +0 -1001
- package/src/core/layout/types.ts +0 -95
- package/src/core/viewport/constants.ts +0 -140
- package/src/core/viewport/features/base.ts +0 -73
- package/src/core/viewport/features/collection.ts +0 -882
- package/src/core/viewport/features/events.ts +0 -130
- package/src/core/viewport/features/item-size.ts +0 -271
- package/src/core/viewport/features/loading.ts +0 -263
- package/src/core/viewport/features/momentum.ts +0 -260
- package/src/core/viewport/features/performance.ts +0 -161
- package/src/core/viewport/features/placeholders.ts +0 -335
- package/src/core/viewport/features/rendering.ts +0 -568
- package/src/core/viewport/features/scrollbar.ts +0 -434
- package/src/core/viewport/features/scrolling.ts +0 -618
- package/src/core/viewport/features/utils.ts +0 -88
- package/src/core/viewport/features/virtual.ts +0 -384
- package/src/core/viewport/types.ts +0 -133
- package/src/core/viewport/utils/speed-tracker.ts +0 -79
- package/src/core/viewport/viewport.ts +0 -246
- package/test/benchmarks/layout/advanced.test.ts +0 -656
- package/test/benchmarks/layout/comparison.test.ts +0 -519
- package/test/benchmarks/layout/performance-comparison.test.ts +0 -274
- package/test/benchmarks/layout/real-components.test.ts +0 -733
- package/test/benchmarks/layout/simple.test.ts +0 -321
- package/test/benchmarks/layout/stress.test.ts +0 -990
- package/test/collection/basic.test.ts +0 -304
- package/test/components/vlist-selection.test.ts +0 -240
- package/test/components/vlist.test.ts +0 -63
- package/test/core/collection/adapter.test.ts +0 -161
- package/test/core/collection/collection.test.ts +0 -394
- package/test/core/layout/layout.test.ts +0 -201
- package/test/utils/dom-helpers.ts +0 -275
- package/test/utils/performance-helpers.ts +0 -392
- package/tsconfig.json +0 -20
|
@@ -1,444 +0,0 @@
|
|
|
1
|
-
// src/components/vlist/features/selection.ts
|
|
2
|
-
|
|
3
|
-
import type { VListConfig, VListComponent, VListItem } from "../types";
|
|
4
|
-
import { VLIST_CLASSES } from "../constants";
|
|
5
|
-
import { PREFIX, addClass, removeClass } from "mtrl";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Selection state interface
|
|
9
|
-
*/
|
|
10
|
-
interface SelectionState {
|
|
11
|
-
selectedIds: Set<string | number>;
|
|
12
|
-
mode: "none" | "single" | "multiple";
|
|
13
|
-
lastSelectedIndex?: number;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Adds selection management capabilities to VList component
|
|
18
|
-
* Works with viewport's virtual scrolling architecture
|
|
19
|
-
*
|
|
20
|
-
* @param config - VList configuration with selection options
|
|
21
|
-
* @returns Function that enhances a component with selection management
|
|
22
|
-
*/
|
|
23
|
-
export const withSelection = <T extends VListItem = VListItem>(
|
|
24
|
-
config: VListConfig<T>
|
|
25
|
-
) => {
|
|
26
|
-
return (component: VListComponent<T>): VListComponent<T> => {
|
|
27
|
-
// Skip if selection is not enabled
|
|
28
|
-
if (!config.selection?.enabled || config.selection?.mode === "none") {
|
|
29
|
-
// console.log("🎯 [Selection] Skipped - not enabled or mode is none");
|
|
30
|
-
return component;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// console.log("🎯 [Selection] Initializing selection feature", {
|
|
34
|
-
// enabled: config.selection?.enabled,
|
|
35
|
-
// mode: config.selection?.mode,
|
|
36
|
-
// });
|
|
37
|
-
|
|
38
|
-
// Initialize selection state
|
|
39
|
-
const state: SelectionState = {
|
|
40
|
-
selectedIds: new Set(),
|
|
41
|
-
mode: config.selection?.mode || "single",
|
|
42
|
-
lastSelectedIndex: undefined,
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
// Get configuration options
|
|
46
|
-
const requireModifiers = config.selection?.requireModifiers ?? false;
|
|
47
|
-
|
|
48
|
-
// Add BEM modifier class to container element
|
|
49
|
-
const addContainerModifier = () => {
|
|
50
|
-
if (component.element) {
|
|
51
|
-
// Add selection mode modifier class following BEM convention
|
|
52
|
-
addClass(component.element, `vlist--selection`);
|
|
53
|
-
// Also add specific mode modifier
|
|
54
|
-
addClass(component.element, `vlist--selection-${state.mode}`);
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
// Defer initialization of pre-selected items until after setup
|
|
59
|
-
const initializePreselectedItems = () => {
|
|
60
|
-
if (config.selection?.selectedIndices && component.getItems) {
|
|
61
|
-
config.selection.selectedIndices.forEach((index) => {
|
|
62
|
-
const items = component.getItems();
|
|
63
|
-
const item = items?.[index];
|
|
64
|
-
if (item && (item as any).id !== undefined) {
|
|
65
|
-
state.selectedIds.add((item as any).id);
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Apply selection class to an element
|
|
73
|
-
*/
|
|
74
|
-
const applySelectionClass = (element: HTMLElement, isSelected: boolean) => {
|
|
75
|
-
if (isSelected) {
|
|
76
|
-
addClass(element, VLIST_CLASSES.SELECTED);
|
|
77
|
-
} else {
|
|
78
|
-
removeClass(element, VLIST_CLASSES.SELECTED);
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Get item ID from element or item data
|
|
84
|
-
*/
|
|
85
|
-
const getItemId = (item: any): string | number | undefined => {
|
|
86
|
-
if (item?.id !== undefined) return item.id;
|
|
87
|
-
if (typeof item === "string" || typeof item === "number") return item;
|
|
88
|
-
return undefined;
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Handle item click for selection
|
|
93
|
-
*/
|
|
94
|
-
const handleItemClick = (e: MouseEvent) => {
|
|
95
|
-
// console.log("🎯 [Selection] Click detected on:", e.target);
|
|
96
|
-
|
|
97
|
-
// Find the clicked viewport item element (wrapper)
|
|
98
|
-
const viewportItem = (e.target as HTMLElement).closest(
|
|
99
|
-
`.${PREFIX}-viewport-item[data-index]`
|
|
100
|
-
) as HTMLElement;
|
|
101
|
-
if (!viewportItem) {
|
|
102
|
-
console.log("🎯 [Selection] No viewport item found for click");
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const index = parseInt(viewportItem.dataset.index || "-1");
|
|
107
|
-
// console.log(`🎯 [Selection] Clicked item index: ${index}`);
|
|
108
|
-
if (index < 0) return;
|
|
109
|
-
|
|
110
|
-
// Get the item data
|
|
111
|
-
const enhancedComponent = component as any;
|
|
112
|
-
const items = enhancedComponent.getItems?.();
|
|
113
|
-
const item = items?.[index];
|
|
114
|
-
if (!item) return;
|
|
115
|
-
|
|
116
|
-
const itemId = getItemId(item);
|
|
117
|
-
if (itemId === undefined) return;
|
|
118
|
-
|
|
119
|
-
// Handle selection based on mode
|
|
120
|
-
const wasSelected = state.selectedIds.has(itemId);
|
|
121
|
-
|
|
122
|
-
// console.log("🎯 [Selection] Click detected:", {
|
|
123
|
-
// index,
|
|
124
|
-
// itemId,
|
|
125
|
-
// wasSelected,
|
|
126
|
-
// mode: state.mode,
|
|
127
|
-
// shiftKey: e.shiftKey,
|
|
128
|
-
// ctrlKey: e.ctrlKey,
|
|
129
|
-
// metaKey: e.metaKey,
|
|
130
|
-
// lastSelectedIndex: state.lastSelectedIndex,
|
|
131
|
-
// });
|
|
132
|
-
|
|
133
|
-
if (state.mode === "single") {
|
|
134
|
-
// Clear previous selection
|
|
135
|
-
state.selectedIds.clear();
|
|
136
|
-
|
|
137
|
-
// Toggle selection
|
|
138
|
-
if (!wasSelected) {
|
|
139
|
-
state.selectedIds.add(itemId);
|
|
140
|
-
state.lastSelectedIndex = index;
|
|
141
|
-
} else {
|
|
142
|
-
state.lastSelectedIndex = undefined;
|
|
143
|
-
}
|
|
144
|
-
} else if (state.mode === "multiple") {
|
|
145
|
-
// Handle multi-select with keyboard modifiers
|
|
146
|
-
if (e.shiftKey && state.lastSelectedIndex !== undefined) {
|
|
147
|
-
// Range selection
|
|
148
|
-
const start = Math.min(state.lastSelectedIndex, index);
|
|
149
|
-
const end = Math.max(state.lastSelectedIndex, index);
|
|
150
|
-
|
|
151
|
-
if (!e.ctrlKey && !e.metaKey) {
|
|
152
|
-
// Clear existing selection if not holding ctrl/cmd
|
|
153
|
-
state.selectedIds.clear();
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
for (let i = start; i <= end; i++) {
|
|
157
|
-
const rangeItem = items?.[i];
|
|
158
|
-
const rangeItemId = getItemId(rangeItem);
|
|
159
|
-
if (rangeItemId !== undefined) {
|
|
160
|
-
state.selectedIds.add(rangeItemId);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
} else if (e.ctrlKey || e.metaKey) {
|
|
164
|
-
// Toggle individual selection with Ctrl/Cmd
|
|
165
|
-
if (wasSelected) {
|
|
166
|
-
state.selectedIds.delete(itemId);
|
|
167
|
-
} else {
|
|
168
|
-
state.selectedIds.add(itemId);
|
|
169
|
-
}
|
|
170
|
-
state.lastSelectedIndex = index;
|
|
171
|
-
} else {
|
|
172
|
-
// Single click without modifiers
|
|
173
|
-
// console.log("🎯 [Selection] Single click without modifiers:", {
|
|
174
|
-
// requireModifiers,
|
|
175
|
-
// wasSelected,
|
|
176
|
-
// willToggle: !requireModifiers,
|
|
177
|
-
// });
|
|
178
|
-
|
|
179
|
-
if (requireModifiers) {
|
|
180
|
-
// If modifiers are required, single click selects only this item
|
|
181
|
-
state.selectedIds.clear();
|
|
182
|
-
state.selectedIds.add(itemId);
|
|
183
|
-
} else {
|
|
184
|
-
// If modifiers are NOT required, single click toggles selection
|
|
185
|
-
if (wasSelected) {
|
|
186
|
-
state.selectedIds.delete(itemId);
|
|
187
|
-
} else {
|
|
188
|
-
state.selectedIds.add(itemId);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
state.lastSelectedIndex = index;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Update visible elements
|
|
196
|
-
updateVisibleElements();
|
|
197
|
-
|
|
198
|
-
// Emit selection change event
|
|
199
|
-
const selectedItems =
|
|
200
|
-
items?.filter((item: any) => {
|
|
201
|
-
const id = getItemId(item);
|
|
202
|
-
return id !== undefined && state.selectedIds.has(id);
|
|
203
|
-
}) || [];
|
|
204
|
-
|
|
205
|
-
const selectedIndices =
|
|
206
|
-
items?.reduce((acc: number[], item: any, idx: number) => {
|
|
207
|
-
const id = getItemId(item);
|
|
208
|
-
if (id !== undefined && state.selectedIds.has(id)) {
|
|
209
|
-
acc.push(idx);
|
|
210
|
-
}
|
|
211
|
-
return acc;
|
|
212
|
-
}, [] as number[]) || [];
|
|
213
|
-
|
|
214
|
-
component.emit?.("selection:change", {
|
|
215
|
-
selectedItems,
|
|
216
|
-
selectedIndices,
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
// Call the selection change callback if provided
|
|
220
|
-
if (config.selection?.onSelectionChange) {
|
|
221
|
-
config.selection.onSelectionChange(selectedItems, selectedIndices);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// Emit individual item selection event
|
|
225
|
-
component.emit?.("item:selection:change", {
|
|
226
|
-
item,
|
|
227
|
-
index,
|
|
228
|
-
isSelected: state.selectedIds.has(itemId),
|
|
229
|
-
});
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Update selection state for visible elements
|
|
234
|
-
*/
|
|
235
|
-
const updateVisibleElements = () => {
|
|
236
|
-
const container = component.element?.querySelector(
|
|
237
|
-
`.${PREFIX}-viewport-items`
|
|
238
|
-
);
|
|
239
|
-
if (!container) {
|
|
240
|
-
// console.warn("🎯 [Selection] No viewport items container found");
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const viewportItems = container.querySelectorAll(
|
|
245
|
-
`.${PREFIX}-viewport-item[data-index]`
|
|
246
|
-
);
|
|
247
|
-
// console.log(
|
|
248
|
-
// `🎯 [Selection] Updating ${viewportItems.length} visible elements`
|
|
249
|
-
// );
|
|
250
|
-
|
|
251
|
-
const enhancedComponent = component as any;
|
|
252
|
-
const items = enhancedComponent.getItems?.();
|
|
253
|
-
|
|
254
|
-
viewportItems.forEach((viewportItem) => {
|
|
255
|
-
const index = parseInt(
|
|
256
|
-
(viewportItem as HTMLElement).dataset.index || "-1"
|
|
257
|
-
);
|
|
258
|
-
if (index < 0) return;
|
|
259
|
-
|
|
260
|
-
const item = items?.[index];
|
|
261
|
-
if (!item) return;
|
|
262
|
-
|
|
263
|
-
const itemId = getItemId(item);
|
|
264
|
-
if (itemId === undefined) return;
|
|
265
|
-
|
|
266
|
-
const isSelected = state.selectedIds.has(itemId);
|
|
267
|
-
|
|
268
|
-
// Apply selection class to the viewport item itself
|
|
269
|
-
// The new layout system doesn't have a separate inner item
|
|
270
|
-
applySelectionClass(viewportItem as HTMLElement, isSelected);
|
|
271
|
-
});
|
|
272
|
-
};
|
|
273
|
-
|
|
274
|
-
// Setup listeners after component is fully initialized
|
|
275
|
-
// Since selection is applied last, we can just use a timeout
|
|
276
|
-
setTimeout(() => {
|
|
277
|
-
// console.log("🎯 [Selection] Setting up listeners after initialization");
|
|
278
|
-
|
|
279
|
-
// Add BEM modifier classes to the container
|
|
280
|
-
addContainerModifier();
|
|
281
|
-
|
|
282
|
-
setupSelectionListeners();
|
|
283
|
-
}, 0);
|
|
284
|
-
|
|
285
|
-
function setupSelectionListeners() {
|
|
286
|
-
// Wait for viewport to be ready
|
|
287
|
-
setTimeout(() => {
|
|
288
|
-
// Initialize pre-selected items now that component is ready
|
|
289
|
-
initializePreselectedItems();
|
|
290
|
-
|
|
291
|
-
// Listen for render complete to update selection state
|
|
292
|
-
// Using type assertion since viewport:rendered is not in ListEvents type
|
|
293
|
-
(component as any).on?.("viewport:rendered", () => {
|
|
294
|
-
updateVisibleElements();
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
// Add click listener to the viewport element
|
|
298
|
-
if (component.element) {
|
|
299
|
-
// Use capture phase to ensure we get the event
|
|
300
|
-
component.element.addEventListener("click", handleItemClick, true);
|
|
301
|
-
// console.log(
|
|
302
|
-
// "🎯 [Selection] Click handler attached to element (capture phase)"
|
|
303
|
-
// );
|
|
304
|
-
|
|
305
|
-
// Test if handler works
|
|
306
|
-
// setTimeout(() => {
|
|
307
|
-
// const testItem = component.element?.querySelector(
|
|
308
|
-
// `.${PREFIX}-viewport-item`
|
|
309
|
-
// );
|
|
310
|
-
// // console.log("🎯 [Selection] Test item found:", !!testItem);
|
|
311
|
-
// }, 500);
|
|
312
|
-
}
|
|
313
|
-
}, 100);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// Clean up on destroy
|
|
317
|
-
const originalDestroy = component.destroy;
|
|
318
|
-
component.destroy = () => {
|
|
319
|
-
if (component.element) {
|
|
320
|
-
component.element.removeEventListener("click", handleItemClick, true);
|
|
321
|
-
}
|
|
322
|
-
originalDestroy?.();
|
|
323
|
-
};
|
|
324
|
-
|
|
325
|
-
// Create the enhanced component
|
|
326
|
-
const enhancedComponent = {
|
|
327
|
-
...component,
|
|
328
|
-
|
|
329
|
-
// Selection API methods
|
|
330
|
-
selectItems(indices: number[]) {
|
|
331
|
-
// Use type assertion to access getItems which is added by API feature
|
|
332
|
-
const getItemsFn = (this as any).getItems;
|
|
333
|
-
if (!getItemsFn) {
|
|
334
|
-
console.warn("🎯 [Selection] getItems not available yet");
|
|
335
|
-
return;
|
|
336
|
-
}
|
|
337
|
-
const items = getItemsFn();
|
|
338
|
-
|
|
339
|
-
if (state.mode === "single" && indices.length > 1) {
|
|
340
|
-
// In single mode, only select the first item
|
|
341
|
-
indices = [indices[0]];
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
indices.forEach((index) => {
|
|
345
|
-
const item = items?.[index];
|
|
346
|
-
const itemId = getItemId(item);
|
|
347
|
-
if (itemId !== undefined) {
|
|
348
|
-
state.selectedIds.add(itemId);
|
|
349
|
-
}
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
// Update lastSelectedIndex for shift+click range selection
|
|
353
|
-
if (indices.length > 0) {
|
|
354
|
-
state.lastSelectedIndex = indices[indices.length - 1];
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
updateVisibleElements();
|
|
358
|
-
(this as any).emit?.("selection:change", {
|
|
359
|
-
selectedItems: (this as any).getSelectedItems(),
|
|
360
|
-
selectedIndices: (this as any).getSelectedIndices(),
|
|
361
|
-
});
|
|
362
|
-
},
|
|
363
|
-
|
|
364
|
-
deselectItems(indices: number[]) {
|
|
365
|
-
// Use type assertion to access getItems which is added by API feature
|
|
366
|
-
const getItemsFn = (this as any).getItems;
|
|
367
|
-
if (!getItemsFn) {
|
|
368
|
-
console.warn("🎯 [Selection] getItems not available yet");
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
const items = getItemsFn();
|
|
372
|
-
|
|
373
|
-
indices.forEach((index) => {
|
|
374
|
-
const item = items?.[index];
|
|
375
|
-
const itemId = getItemId(item);
|
|
376
|
-
if (itemId !== undefined) {
|
|
377
|
-
state.selectedIds.delete(itemId);
|
|
378
|
-
}
|
|
379
|
-
});
|
|
380
|
-
|
|
381
|
-
updateVisibleElements();
|
|
382
|
-
(this as any).emit?.("selection:change", {
|
|
383
|
-
selectedItems: (this as any).getSelectedItems(),
|
|
384
|
-
selectedIndices: (this as any).getSelectedIndices(),
|
|
385
|
-
});
|
|
386
|
-
},
|
|
387
|
-
|
|
388
|
-
clearSelection() {
|
|
389
|
-
state.selectedIds.clear();
|
|
390
|
-
state.lastSelectedIndex = undefined;
|
|
391
|
-
updateVisibleElements();
|
|
392
|
-
(this as any).emit?.("selection:change", {
|
|
393
|
-
selectedItems: [],
|
|
394
|
-
selectedIndices: [],
|
|
395
|
-
});
|
|
396
|
-
},
|
|
397
|
-
|
|
398
|
-
getSelectedItems(): T[] {
|
|
399
|
-
// Use type assertion to access getItems which is added by API feature
|
|
400
|
-
const getItemsFn = (this as any).getItems;
|
|
401
|
-
if (!getItemsFn) {
|
|
402
|
-
return [];
|
|
403
|
-
}
|
|
404
|
-
const items = getItemsFn() || [];
|
|
405
|
-
return items.filter((item: any) => {
|
|
406
|
-
const id = getItemId(item);
|
|
407
|
-
return id !== undefined && state.selectedIds.has(id);
|
|
408
|
-
});
|
|
409
|
-
},
|
|
410
|
-
|
|
411
|
-
getSelectedIndices(): number[] {
|
|
412
|
-
// Use type assertion to access getItems which is added by API feature
|
|
413
|
-
const getItemsFn = (this as any).getItems;
|
|
414
|
-
if (!getItemsFn) {
|
|
415
|
-
return [];
|
|
416
|
-
}
|
|
417
|
-
const items = getItemsFn() || [];
|
|
418
|
-
return items.reduce((acc: number[], item: any, index: number) => {
|
|
419
|
-
const id = getItemId(item);
|
|
420
|
-
if (id !== undefined && state.selectedIds.has(id)) {
|
|
421
|
-
acc.push(index);
|
|
422
|
-
}
|
|
423
|
-
return acc;
|
|
424
|
-
}, [] as number[]);
|
|
425
|
-
},
|
|
426
|
-
|
|
427
|
-
isSelected(index: number): boolean {
|
|
428
|
-
// Use type assertion to access getItems which is added by API feature
|
|
429
|
-
const getItemsFn = (this as any).getItems;
|
|
430
|
-
if (!getItemsFn) {
|
|
431
|
-
return false;
|
|
432
|
-
}
|
|
433
|
-
const items = getItemsFn();
|
|
434
|
-
const item = items?.[index];
|
|
435
|
-
const itemId = getItemId(item);
|
|
436
|
-
return itemId !== undefined && state.selectedIds.has(itemId);
|
|
437
|
-
},
|
|
438
|
-
};
|
|
439
|
-
|
|
440
|
-
return enhancedComponent;
|
|
441
|
-
};
|
|
442
|
-
};
|
|
443
|
-
|
|
444
|
-
export default withSelection;
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
// src/components/vlist/features/viewport.ts
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Viewport feature for VList
|
|
5
|
-
* Integrates the core viewport functionality with VList component
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { VListConfig, VListItem } from "../types";
|
|
9
|
-
import { createViewport } from "../../../core/viewport";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Adds viewport functionality to VList
|
|
13
|
-
*/
|
|
14
|
-
export const withViewport = <T extends VListItem = VListItem>(
|
|
15
|
-
config: VListConfig<T>
|
|
16
|
-
) => {
|
|
17
|
-
return (component: any) => {
|
|
18
|
-
// console.log("📋 [VList] Applying viewport feature", {
|
|
19
|
-
// hasElement: !!component.element,
|
|
20
|
-
// hasItems: !!config.items,
|
|
21
|
-
// itemCount: config.items?.length || 0,
|
|
22
|
-
// });
|
|
23
|
-
|
|
24
|
-
// Set initial items if provided
|
|
25
|
-
if (config.items) {
|
|
26
|
-
component.items = config.items;
|
|
27
|
-
component.totalItems = config.items.length;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Set template if provided
|
|
31
|
-
if (config.template) {
|
|
32
|
-
component.template = config.template;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Ensure the element has both vlist and viewport classes
|
|
36
|
-
const viewportConfig = {
|
|
37
|
-
...config,
|
|
38
|
-
className: "mtrl-viewport", // This will be added by viewport base feature
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
// Pass VList config directly to viewport
|
|
42
|
-
const viewportEnhanced = createViewport(viewportConfig as any)(component);
|
|
43
|
-
|
|
44
|
-
// Handle parent element if provided
|
|
45
|
-
if (config.parent || config.container) {
|
|
46
|
-
const container = config.parent || config.container;
|
|
47
|
-
const element =
|
|
48
|
-
typeof container === "string"
|
|
49
|
-
? document.querySelector(container)
|
|
50
|
-
: container;
|
|
51
|
-
|
|
52
|
-
if (element && viewportEnhanced.element) {
|
|
53
|
-
element.appendChild(viewportEnhanced.element);
|
|
54
|
-
|
|
55
|
-
// Ensure viewport is initialized after DOM attachment
|
|
56
|
-
if (viewportEnhanced.viewport && viewportEnhanced.viewport.initialize) {
|
|
57
|
-
// console.log("📋 [VList] Initializing viewport after DOM attachment");
|
|
58
|
-
viewportEnhanced.viewport.initialize();
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return viewportEnhanced;
|
|
64
|
-
};
|
|
65
|
-
};
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* List component features
|
|
3
|
-
*
|
|
4
|
-
* Modular features that can be composed with the list component.
|
|
5
|
-
* Following mtrl's functional composition pattern.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { ListComponent, ListConfig, ListItem } from "./types";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Virtual scrolling feature (future implementation)
|
|
12
|
-
* This will provide window-based virtual scrolling for massive datasets
|
|
13
|
-
*/
|
|
14
|
-
export function withVirtualScroll<T extends ListItem = ListItem>(
|
|
15
|
-
config: any = {}
|
|
16
|
-
) {
|
|
17
|
-
return (list: ListComponent<T>): ListComponent<T> => {
|
|
18
|
-
console.log(
|
|
19
|
-
"🔄 [MTRL-ADDONS-LIST] Virtual scrolling feature (future implementation)"
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
// TODO: Implement virtual scrolling
|
|
23
|
-
// - Window-based strategy
|
|
24
|
-
// - Dynamic height calculation
|
|
25
|
-
// - GPU-accelerated positioning
|
|
26
|
-
|
|
27
|
-
return list;
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Enhanced list selection feature (future implementation)
|
|
33
|
-
* This will provide advanced selection patterns
|
|
34
|
-
*/
|
|
35
|
-
export function withListSelection<T extends ListItem = ListItem>(
|
|
36
|
-
config: any = {}
|
|
37
|
-
) {
|
|
38
|
-
return (list: ListComponent<T>): ListComponent<T> => {
|
|
39
|
-
console.log(
|
|
40
|
-
"✅ [MTRL-ADDONS-LIST] Enhanced selection feature (future implementation)"
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
// TODO: Implement advanced selection
|
|
44
|
-
// - Keyboard navigation
|
|
45
|
-
// - Range selection (Shift+Click)
|
|
46
|
-
// - Checkbox selection modes
|
|
47
|
-
// - Selection persistence
|
|
48
|
-
|
|
49
|
-
return list;
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Advanced list styling feature (future implementation)
|
|
55
|
-
* This will provide theme integration and advanced styling
|
|
56
|
-
*/
|
|
57
|
-
export function withListStyling<T extends ListItem = ListItem>(
|
|
58
|
-
config: any = {}
|
|
59
|
-
) {
|
|
60
|
-
return (list: ListComponent<T>): ListComponent<T> => {
|
|
61
|
-
console.log(
|
|
62
|
-
"�� [MTRL-ADDONS-LIST] Advanced styling feature (future implementation)"
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
// TODO: Implement advanced styling
|
|
66
|
-
// - Theme integration
|
|
67
|
-
// - Dynamic styling
|
|
68
|
-
// - CSS custom properties
|
|
69
|
-
// - Responsive layouts
|
|
70
|
-
|
|
71
|
-
return list;
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Performance optimization feature (future implementation)
|
|
77
|
-
* This will provide advanced performance monitoring and optimization
|
|
78
|
-
*/
|
|
79
|
-
export function withListPerformance<T extends ListItem = ListItem>(
|
|
80
|
-
config: any = {}
|
|
81
|
-
) {
|
|
82
|
-
return (list: ListComponent<T>): ListComponent<T> => {
|
|
83
|
-
console.log(
|
|
84
|
-
"⚡ [MTRL-ADDONS-LIST] Advanced performance feature (future implementation)"
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
// TODO: Implement performance optimizations
|
|
88
|
-
// - Memory usage tracking
|
|
89
|
-
// - Render time optimization
|
|
90
|
-
// - Batch updates
|
|
91
|
-
// - Performance budgets
|
|
92
|
-
|
|
93
|
-
return list;
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Re-export the internal composition functions for advanced usage
|
|
99
|
-
* These are the actual working implementations used in createList
|
|
100
|
-
*/
|
|
101
|
-
// export {
|
|
102
|
-
// // These would be imported from the main list.ts file
|
|
103
|
-
// // withListBase,
|
|
104
|
-
// // withListStyling as withListStylingInternal,
|
|
105
|
-
// // withListSelection as withListSelectionInternal,
|
|
106
|
-
// // withListPerformance as withListPerformanceInternal,
|
|
107
|
-
// // withListAPI
|
|
108
|
-
// } from './list';
|
|
109
|
-
|
|
110
|
-
// Note: The above imports are commented out because they would create circular imports
|
|
111
|
-
// In a real implementation, we'd restructure to have the composition functions
|
|
112
|
-
// in separate files to avoid this issue
|