@lightningtv/solid 2.10.6 → 2.10.7
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/dist/src/primitives/Virtual.d.ts +3 -2
- package/dist/src/primitives/Virtual.jsx +333 -130
- package/dist/src/primitives/Virtual.jsx.map +1 -1
- package/dist/src/primitives/utils/handleNavigation.js +1 -1
- package/dist/src/primitives/utils/handleNavigation.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/primitives/Virtual.tsx +337 -132
- package/src/primitives/utils/handleNavigation.ts +1 -1
|
@@ -10,24 +10,22 @@ export type VirtualProps<T> = lng.NewOmit<lngp.RowProps, 'children'> & {
|
|
|
10
10
|
bufferSize?: number;
|
|
11
11
|
wrap?: boolean;
|
|
12
12
|
scrollIndex?: number;
|
|
13
|
-
doScroll?: lngp.Scroller;
|
|
14
13
|
onEndReached?: () => void;
|
|
15
14
|
onEndReachedThreshold?: number;
|
|
16
|
-
|
|
15
|
+
debugInfo?: boolean;
|
|
16
|
+
factorScale?: boolean;
|
|
17
|
+
uniformSize?: boolean;
|
|
17
18
|
children: (item: s.Accessor<T>, index: s.Accessor<number>) => s.JSX.Element;
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
function createVirtual<T>(
|
|
21
22
|
component: typeof lngp.Row | typeof lngp.Column,
|
|
22
23
|
props: VirtualProps<T>,
|
|
23
|
-
scrollFn: ReturnType<typeof lngp.withScrolling>,
|
|
24
24
|
keyHandlers: Record<string, lng.KeyHandler>
|
|
25
25
|
) {
|
|
26
26
|
const [cursor, setCursor] = s.createSignal(props.selected ?? 0);
|
|
27
27
|
const bufferSize = s.createMemo(() => props.bufferSize || 2);
|
|
28
|
-
const scrollIndex = s.createMemo(() =>
|
|
29
|
-
return props.scrollIndex || 0;
|
|
30
|
-
});
|
|
28
|
+
const scrollIndex = s.createMemo(() => props.scrollIndex || 0);
|
|
31
29
|
const items = s.createMemo(() => props.each || []);
|
|
32
30
|
const itemCount = s.createMemo(() => items().length);
|
|
33
31
|
const scrollType = s.createMemo(() => props.scroll || 'auto');
|
|
@@ -39,55 +37,270 @@ function createVirtual<T>(
|
|
|
39
37
|
return props.selected || 0;
|
|
40
38
|
};
|
|
41
39
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return Math.min(Math.max(cursor() - bufferSize(), 0), itemCount() - props.displaySize - bufferSize());
|
|
49
|
-
}
|
|
50
|
-
if (scrollType() === 'auto') {
|
|
51
|
-
return utils.clamp(cursor() - Math.max(bufferSize(), scrollIndex()), 0, Math.max(0, itemCount() - props.displaySize - bufferSize()));
|
|
52
|
-
}
|
|
53
|
-
return utils.clamp(cursor() - bufferSize(), 0, Math.max(0, itemCount() - props.displaySize));
|
|
54
|
-
};
|
|
40
|
+
let cachedScaledSize: number | undefined;
|
|
41
|
+
let targetPosition: number | undefined;
|
|
42
|
+
let cachedAnimationController: lng.IAnimationController | undefined;
|
|
43
|
+
const uniformSize = s.createMemo(() => {
|
|
44
|
+
return props.uniformSize !== false;
|
|
45
|
+
});
|
|
55
46
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
47
|
+
type SliceState = { start: number; slice: T[]; selected: number, delta: number, shiftBy: number, atStart: boolean };
|
|
48
|
+
const [slice, setSlice] = s.createSignal<SliceState>({
|
|
49
|
+
start: 0,
|
|
50
|
+
slice: [],
|
|
51
|
+
selected: 0,
|
|
52
|
+
delta: 0,
|
|
53
|
+
shiftBy: 0,
|
|
54
|
+
atStart: true,
|
|
55
|
+
});
|
|
63
56
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
57
|
+
function normalizeDeltaForWindow(delta: number, windowLen: number): number {
|
|
58
|
+
if (!windowLen) return 0;
|
|
59
|
+
const half = windowLen / 2;
|
|
60
|
+
if (delta > half) return delta - windowLen;
|
|
61
|
+
if (delta < -half) return delta + windowLen;
|
|
62
|
+
return delta;
|
|
68
63
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
64
|
+
|
|
65
|
+
function computeSize(selected: number = 0) {
|
|
66
|
+
if (uniformSize() && cachedScaledSize) {
|
|
67
|
+
return cachedScaledSize;
|
|
68
|
+
} else if (viewRef) {
|
|
69
|
+
const gap = viewRef.gap || 0;
|
|
70
|
+
const isRow = component === lngp.Row;
|
|
71
|
+
const dimension = isRow ? 'width' : 'height';
|
|
72
|
+
const prevSelectedChild = viewRef.children[selected];
|
|
73
|
+
|
|
74
|
+
if (prevSelectedChild instanceof lng.ElementNode) {
|
|
75
|
+
const itemSize = prevSelectedChild[dimension] || 0;
|
|
76
|
+
const focusStyle = (prevSelectedChild.style?.focus as lng.NodeStyles);
|
|
77
|
+
const scale = (focusStyle?.scale ?? prevSelectedChild.scale ?? 1);
|
|
78
|
+
const scaledSize = itemSize * (props.factorScale ? scale : 1) + gap;
|
|
79
|
+
cachedScaledSize = scaledSize;
|
|
80
|
+
return scaledSize;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return 0;
|
|
74
84
|
}
|
|
75
|
-
return [...items().slice(sIdx), ...items().slice(0, eIdx)];
|
|
76
|
-
});
|
|
77
85
|
|
|
78
|
-
|
|
86
|
+
function computeSlice(c: number, delta: number, prev: SliceState): SliceState {
|
|
87
|
+
const total = itemCount();
|
|
88
|
+
if (total === 0) return { start: 0, slice: [], selected: 0, delta, shiftBy: 0, atStart: true };
|
|
89
|
+
|
|
90
|
+
const length = props.displaySize + bufferSize();
|
|
91
|
+
let start = prev.start;
|
|
92
|
+
let selected = prev.selected;
|
|
93
|
+
let atStart = prev.atStart;
|
|
94
|
+
let shiftBy = -delta;
|
|
95
|
+
|
|
96
|
+
switch (scrollType()) {
|
|
97
|
+
case 'always':
|
|
98
|
+
if (props.wrap) {
|
|
99
|
+
start = utils.mod(c - 1, total);
|
|
100
|
+
selected = 1;
|
|
101
|
+
} else {
|
|
102
|
+
start = utils.clamp(
|
|
103
|
+
c - bufferSize(),
|
|
104
|
+
0,
|
|
105
|
+
Math.max(0, total - props.displaySize - bufferSize()),
|
|
106
|
+
);
|
|
107
|
+
if (delta === 0 && c > 3) {
|
|
108
|
+
shiftBy = c < 3 ? -c : -2;
|
|
109
|
+
selected = 2;
|
|
110
|
+
} else {
|
|
111
|
+
selected =
|
|
112
|
+
c < bufferSize()
|
|
113
|
+
? c
|
|
114
|
+
: c >= total - props.displaySize
|
|
115
|
+
? c - (total - props.displaySize) + bufferSize()
|
|
116
|
+
: bufferSize();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
break;
|
|
120
|
+
|
|
121
|
+
case 'auto':
|
|
122
|
+
if (props.wrap) {
|
|
123
|
+
if (scrollIndex() && prev.selected < scrollIndex()) {
|
|
124
|
+
start = total - 1;
|
|
125
|
+
selected = Math.max(1, prev.selected + delta);
|
|
126
|
+
} else {
|
|
127
|
+
start = utils.mod(c - (scrollIndex() || 1), total);
|
|
128
|
+
selected = Math.max(1, prev.selected);
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
if (delta < 0) {
|
|
132
|
+
// Moving left
|
|
133
|
+
if (prev.start > 0 && prev.selected >= props.displaySize) {
|
|
134
|
+
// Move selection left inside slice
|
|
135
|
+
start = prev.start;
|
|
136
|
+
selected = prev.selected - 1;
|
|
137
|
+
} else if (prev.start > 0) {
|
|
138
|
+
// Move selection left inside slice
|
|
139
|
+
start = prev.start - 1;
|
|
140
|
+
selected = prev.selected;
|
|
141
|
+
// shiftBy = 0;
|
|
142
|
+
} else if (prev.start === 0 && !prev.atStart) {
|
|
143
|
+
start = 0;
|
|
144
|
+
selected = prev.selected - 1;
|
|
145
|
+
atStart = true;
|
|
146
|
+
} else if (selected >= props.displaySize - 1) {
|
|
147
|
+
// Shift window left, keep selection pinned
|
|
148
|
+
start = 0;
|
|
149
|
+
selected = prev.selected - 1;
|
|
150
|
+
} else {
|
|
151
|
+
start = 0;
|
|
152
|
+
selected = prev.selected - 1;
|
|
153
|
+
shiftBy = 0;
|
|
154
|
+
}
|
|
155
|
+
} else if (delta > 0) {
|
|
156
|
+
// Moving right
|
|
157
|
+
if (prev.selected < scrollIndex()) {
|
|
158
|
+
// Move selection right inside slice
|
|
159
|
+
start = prev.start;
|
|
160
|
+
selected = prev.selected + 1;
|
|
161
|
+
shiftBy = 0;
|
|
162
|
+
} else if (prev.selected === scrollIndex()) {
|
|
163
|
+
start = prev.start;
|
|
164
|
+
selected = prev.selected + 1;
|
|
165
|
+
atStart = false;
|
|
166
|
+
} else if (prev.start === 0 && prev.selected === 0) {
|
|
167
|
+
start = 0;
|
|
168
|
+
selected = 1;
|
|
169
|
+
atStart = false;
|
|
170
|
+
} else if (prev.start >= total - props.displaySize) {
|
|
171
|
+
// At end: clamp slice, selection drifts right
|
|
172
|
+
start = prev.start;
|
|
173
|
+
selected = c - start;
|
|
174
|
+
shiftBy = 0;
|
|
175
|
+
} else {
|
|
176
|
+
// Shift window right, keep selection pinned
|
|
177
|
+
start = prev.start + 1;
|
|
178
|
+
selected = Math.max(prev.selected, scrollIndex() + 1);;
|
|
179
|
+
}
|
|
180
|
+
} else {
|
|
181
|
+
// Initial setup
|
|
182
|
+
if (c > 0) {
|
|
183
|
+
start = Math.min(c - (scrollIndex() || 1), total - props.displaySize - bufferSize());
|
|
184
|
+
selected = Math.max(scrollIndex() || 1, c - start);
|
|
185
|
+
shiftBy = total - c < 3 ? c - total : -1;
|
|
186
|
+
atStart = false;
|
|
187
|
+
} else {
|
|
188
|
+
start = prev.start;
|
|
189
|
+
selected = prev.selected;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
break;
|
|
194
|
+
|
|
195
|
+
case 'edge':
|
|
196
|
+
const startScrolling = Math.max(1, props.displaySize - 1);
|
|
197
|
+
if (props.wrap) {
|
|
198
|
+
if (delta > 0) {
|
|
199
|
+
if (prev.selected < startScrolling) {
|
|
200
|
+
selected = prev.selected + 1;
|
|
201
|
+
shiftBy = 0;
|
|
202
|
+
} else {
|
|
203
|
+
start = utils.mod(prev.start + 1, total);
|
|
204
|
+
selected = startScrolling;
|
|
205
|
+
}
|
|
206
|
+
} else if (delta < 0) {
|
|
207
|
+
if (prev.selected > 1) {
|
|
208
|
+
selected = prev.selected - 1;
|
|
209
|
+
shiftBy = 0;
|
|
210
|
+
} else {
|
|
211
|
+
start = utils.mod(prev.start - 1, total);
|
|
212
|
+
selected = 1;
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
start = utils.mod(c - 1, total);
|
|
216
|
+
selected = 1;
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
if (delta === 0 && c > 0) {
|
|
220
|
+
//initial setup
|
|
221
|
+
selected = c > startScrolling ? startScrolling : c;
|
|
222
|
+
start = Math.max(0, c - startScrolling + 1);
|
|
223
|
+
shiftBy = c > startScrolling ? -1 : 0;
|
|
224
|
+
atStart = c < startScrolling;
|
|
225
|
+
} else if (delta > 0) {
|
|
226
|
+
if (prev.selected < startScrolling - 1) {
|
|
227
|
+
selected = prev.selected + 1;
|
|
228
|
+
shiftBy = 0;
|
|
229
|
+
} else {
|
|
230
|
+
start = prev.start + 1;
|
|
231
|
+
selected = prev.selected;
|
|
232
|
+
atStart = false;
|
|
233
|
+
}
|
|
234
|
+
} else if (delta < 0) {
|
|
235
|
+
if (prev.selected > 1) {
|
|
236
|
+
selected = prev.selected - 1;
|
|
237
|
+
shiftBy = 0;
|
|
238
|
+
} else if (c > 1) {
|
|
239
|
+
start = Math.max(0, c - 1);
|
|
240
|
+
selected = 1;
|
|
241
|
+
} else if (!atStart) {
|
|
242
|
+
start = 0;
|
|
243
|
+
selected = 0;
|
|
244
|
+
atStart = true;
|
|
245
|
+
} else {
|
|
246
|
+
start = 0;
|
|
247
|
+
selected = 0;
|
|
248
|
+
shiftBy = 0;
|
|
249
|
+
atStart = true;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
break;
|
|
254
|
+
|
|
255
|
+
case 'none':
|
|
256
|
+
default:
|
|
257
|
+
start = 0;
|
|
258
|
+
selected = c;
|
|
259
|
+
shiftBy = 0;
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
let newSlice = prev.slice;
|
|
264
|
+
if (start !== prev.start || newSlice.length === 0) {
|
|
265
|
+
newSlice = props.wrap
|
|
266
|
+
? Array.from(
|
|
267
|
+
{ length },
|
|
268
|
+
(_, i) => items()[utils.mod(start + i, total)],
|
|
269
|
+
) as T[]
|
|
270
|
+
: items().slice(start, start + length);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const state: SliceState = { start, slice: newSlice, selected, delta, shiftBy, atStart };
|
|
274
|
+
|
|
275
|
+
if (props.debugInfo) {
|
|
276
|
+
console.log(`[Virtual]`, {
|
|
277
|
+
cursor: c,
|
|
278
|
+
delta,
|
|
279
|
+
start,
|
|
280
|
+
selected,
|
|
281
|
+
shiftBy,
|
|
282
|
+
slice: state.slice,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return state;
|
|
287
|
+
}
|
|
79
288
|
|
|
80
289
|
let viewRef!: lngp.NavigableElement;
|
|
81
290
|
|
|
82
291
|
function scrollToIndex(this: lng.ElementNode, index: number) {
|
|
83
292
|
if (itemCount() === 0) return;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
293
|
+
updateSelected([utils.clamp(index, 0, itemCount() - 1)]);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
let lastNavTime = 0;
|
|
297
|
+
|
|
298
|
+
function getAdaptiveDuration(duration: number = 250) {
|
|
299
|
+
const now = performance.now();
|
|
300
|
+
const delta = now - lastNavTime;
|
|
301
|
+
lastNavTime = now;
|
|
302
|
+
if (delta < duration) return delta;
|
|
303
|
+
return duration;
|
|
91
304
|
}
|
|
92
305
|
|
|
93
306
|
const onSelectedChanged: lngp.OnSelectedChanged = function (_idx, elm, _active, _lastIdx) {
|
|
@@ -95,116 +308,108 @@ function createVirtual<T>(
|
|
|
95
308
|
let lastIdx = _lastIdx || 0;
|
|
96
309
|
let active = _active;
|
|
97
310
|
const initialRun = idx === lastIdx;
|
|
311
|
+
const total = itemCount();
|
|
312
|
+
const isRow = component === lngp.Row;
|
|
313
|
+
const axis = isRow ? 'x' : 'y';
|
|
314
|
+
|
|
315
|
+
if (props.onSelectedChanged) {
|
|
316
|
+
props.onSelectedChanged.call(this as lngp.NavigableElement, idx, this as lngp.NavigableElement, active, lastIdx);
|
|
317
|
+
}
|
|
98
318
|
|
|
99
319
|
if (initialRun && !props.wrap) return;
|
|
100
320
|
|
|
321
|
+
const rawDelta = idx - (lastIdx ?? 0);
|
|
322
|
+
const windowLen =
|
|
323
|
+
elm?.children?.length ?? props.displaySize + bufferSize();
|
|
324
|
+
const delta = props.wrap
|
|
325
|
+
? normalizeDeltaForWindow(rawDelta, windowLen)
|
|
326
|
+
: rawDelta;
|
|
327
|
+
|
|
101
328
|
if (!initialRun) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
329
|
+
setCursor(c => {
|
|
330
|
+
const next = c + delta;
|
|
331
|
+
return props.wrap
|
|
332
|
+
? utils.mod(next, total)
|
|
333
|
+
: utils.clamp(next, 0, total - 1);
|
|
334
|
+
});
|
|
107
335
|
|
|
108
|
-
|
|
336
|
+
const newState = computeSlice(cursor(), delta, slice());
|
|
337
|
+
setSlice(newState);
|
|
338
|
+
elm.selected = newState.selected;
|
|
109
339
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
} else if (props.scrollIndex) {
|
|
115
|
-
this.selected = Math.min(c, props.scrollIndex);
|
|
116
|
-
if (c >= itemCount() - props.displaySize + bufferSize()) {
|
|
117
|
-
this.selected = c - (itemCount() - props.displaySize) + bufferSize();
|
|
118
|
-
}
|
|
119
|
-
} else if (scroll === 'always' || scroll === 'auto') {
|
|
120
|
-
if (c < bufferSize()) {
|
|
121
|
-
this.selected = c;
|
|
122
|
-
} else if (c >= itemCount() - props.displaySize) {
|
|
123
|
-
this.selected = c - (itemCount() - props.displaySize) + bufferSize();
|
|
124
|
-
} else {
|
|
125
|
-
this.selected = bufferSize();
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (props.onEndReachedThreshold !== undefined && cursor() >= items().length - props.onEndReachedThreshold) {
|
|
340
|
+
if (
|
|
341
|
+
props.onEndReachedThreshold !== undefined &&
|
|
342
|
+
cursor() >= itemCount() - props.onEndReachedThreshold
|
|
343
|
+
) {
|
|
130
344
|
props.onEndReached?.();
|
|
131
345
|
}
|
|
346
|
+
|
|
347
|
+
if (newState.shiftBy === 0) return;
|
|
132
348
|
}
|
|
133
|
-
|
|
134
|
-
const prevChildPos =
|
|
135
|
-
? this.x + active.x
|
|
136
|
-
: this.y + active.y;
|
|
349
|
+
|
|
350
|
+
const prevChildPos = (targetPosition ?? this[axis]) + active[axis];
|
|
137
351
|
|
|
138
352
|
queueMicrotask(() => {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (scrollIndex() > 0) {
|
|
145
|
-
active = this.children[1] as lng.ElementNode;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
if (component === lngp.Row) {
|
|
149
|
-
this.lng.x = this._targetPosition = prevChildPos - active.x;
|
|
150
|
-
} else {
|
|
151
|
-
this.lng.y = this._targetPosition = prevChildPos - active.y;
|
|
353
|
+
elm.updateLayout();
|
|
354
|
+
const childSize = computeSize(slice().selected);
|
|
355
|
+
|
|
356
|
+
if (cachedAnimationController && cachedAnimationController.state === 'running') {
|
|
357
|
+
cachedAnimationController.stop();;
|
|
152
358
|
}
|
|
153
|
-
|
|
359
|
+
this.lng[axis] = prevChildPos - active[axis];
|
|
360
|
+
let offset = this.lng[axis] + (childSize * slice().shiftBy);
|
|
361
|
+
targetPosition = offset;
|
|
362
|
+
cachedAnimationController = this.animate(
|
|
363
|
+
{ [axis]: offset },
|
|
364
|
+
{ ...this.animationSettings, duration: getAdaptiveDuration(this.animationSettings?.duration)}
|
|
365
|
+
).start();
|
|
154
366
|
});
|
|
155
367
|
};
|
|
156
368
|
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
const updateSelected = ([selected, _items]: [number?, any?]) => {
|
|
160
|
-
if (!viewRef || selected === undefined) return;
|
|
161
|
-
const sel = selected;
|
|
369
|
+
const updateSelected = ([sel, _items]: [number?, any?]) => {
|
|
370
|
+
if (!viewRef || sel === undefined || itemCount() === 0) return;
|
|
162
371
|
const item = items()[sel];
|
|
163
|
-
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
viewRef.
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
372
|
+
setCursor(sel);
|
|
373
|
+
const newState = computeSlice(cursor(), 0, slice());
|
|
374
|
+
setSlice(newState);
|
|
375
|
+
|
|
376
|
+
queueMicrotask(() => {
|
|
377
|
+
viewRef.updateLayout();
|
|
378
|
+
if (slice().shiftBy) {
|
|
379
|
+
const isRow = component === lngp.Row;
|
|
380
|
+
const axis = isRow ? 'x' : 'y';
|
|
381
|
+
const childSize = computeSize(slice().selected);
|
|
382
|
+
viewRef.lng[axis] = viewRef.lng[axis]! + (childSize * slice().shiftBy);
|
|
383
|
+
targetPosition = viewRef.lng[axis];
|
|
384
|
+
}
|
|
385
|
+
let activeIndex = viewRef.children.findIndex(x => x.item === item);
|
|
386
|
+
if (activeIndex === -1) return;
|
|
387
|
+
viewRef.selected = activeIndex;
|
|
388
|
+
viewRef.children[activeIndex]?.setFocus();
|
|
389
|
+
});
|
|
182
390
|
};
|
|
183
391
|
|
|
184
|
-
s.createEffect(s.on([() => props.selected, items], updateSelected));
|
|
392
|
+
s.createEffect(s.on([() => props.selected, items], updateSelected, { defer: true }));
|
|
185
393
|
|
|
186
394
|
s.createEffect(s.on(items, () => {
|
|
187
|
-
if (!viewRef) return;
|
|
395
|
+
if (!viewRef || itemCount() === 0) return;
|
|
188
396
|
if (cursor() >= itemCount()) {
|
|
189
|
-
setCursor(
|
|
397
|
+
setCursor(itemCount() - 1);
|
|
190
398
|
}
|
|
191
|
-
|
|
192
|
-
|
|
399
|
+
const newState = computeSlice(cursor(), 0, slice());
|
|
400
|
+
setSlice(newState);
|
|
401
|
+
viewRef.selected = newState.selected;
|
|
402
|
+
}));
|
|
193
403
|
|
|
194
404
|
return (<view
|
|
195
405
|
{...props}
|
|
406
|
+
{...keyHandlers}
|
|
196
407
|
ref={lngp.chainRefs(el => { viewRef = el as lngp.NavigableElement; }, props.ref)}
|
|
197
408
|
selected={selected()}
|
|
198
409
|
cursor={cursor()}
|
|
199
|
-
{...keyHandlers}
|
|
200
410
|
forwardFocus={/* @once */ lngp.navigableForwardFocus}
|
|
201
411
|
scrollToIndex={/* @once */ scrollToIndex}
|
|
202
|
-
|
|
203
|
-
props.selected
|
|
204
|
-
? lngp.chainFunctions(props.onCreate, scrollFn)
|
|
205
|
-
: props.onCreate
|
|
206
|
-
}
|
|
207
|
-
onSelectedChanged={/* @once */ chainedOnSelectedChanged}
|
|
412
|
+
onSelectedChanged={/* @once */ onSelectedChanged}
|
|
208
413
|
style={/* @once */ lng.combineStyles(
|
|
209
414
|
props.style,
|
|
210
415
|
component === lngp.Row
|
|
@@ -221,21 +426,21 @@ function createVirtual<T>(
|
|
|
221
426
|
}
|
|
222
427
|
)}
|
|
223
428
|
>
|
|
224
|
-
<List each={slice()}>{props.children}</List>
|
|
429
|
+
<List each={slice().slice}>{props.children}</List>
|
|
225
430
|
</view>
|
|
226
431
|
);
|
|
227
432
|
}
|
|
228
433
|
|
|
229
434
|
export function VirtualRow<T>(props: VirtualProps<T>) {
|
|
230
|
-
return createVirtual(lngp.Row, props,
|
|
231
|
-
onLeft: lngp.chainFunctions(props.onLeft, lngp.
|
|
232
|
-
onRight: lngp.chainFunctions(props.onRight, lngp.
|
|
435
|
+
return createVirtual(lngp.Row, props, {
|
|
436
|
+
onLeft: lngp.chainFunctions(props.onLeft, lngp.handleNavigation('left')) as lng.KeyHandler,
|
|
437
|
+
onRight: lngp.chainFunctions(props.onRight, lngp.handleNavigation('right')) as lng.KeyHandler,
|
|
233
438
|
});
|
|
234
439
|
}
|
|
235
440
|
|
|
236
441
|
export function VirtualColumn<T>(props: VirtualProps<T>) {
|
|
237
|
-
return createVirtual(lngp.Column, props,
|
|
238
|
-
onUp: lngp.chainFunctions(props.onUp, lngp.
|
|
239
|
-
onDown: lngp.chainFunctions(props.onDown, lngp.
|
|
442
|
+
return createVirtual(lngp.Column, props, {
|
|
443
|
+
onUp: lngp.chainFunctions(props.onUp, lngp.handleNavigation('up')) as lng.KeyHandler,
|
|
444
|
+
onDown: lngp.chainFunctions(props.onDown, lngp.handleNavigation('down')) as lng.KeyHandler,
|
|
240
445
|
});
|
|
241
446
|
}
|