@lightningtv/solid 2.10.3 → 2.10.5
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/Column.jsx +4 -5
- package/dist/src/primitives/Column.jsx.map +1 -1
- package/dist/src/primitives/Row.jsx +4 -5
- package/dist/src/primitives/Row.jsx.map +1 -1
- package/dist/src/primitives/Virtual.d.ts +3 -0
- package/dist/src/primitives/Virtual.jsx +128 -36
- package/dist/src/primitives/Virtual.jsx.map +1 -1
- package/dist/src/primitives/VirtualGrid.jsx +1 -1
- package/dist/src/primitives/VirtualGrid.jsx.map +1 -1
- package/dist/src/primitives/index.d.ts +1 -1
- package/dist/src/primitives/index.js +1 -1
- package/dist/src/primitives/index.js.map +1 -1
- package/dist/src/primitives/utils/withScrolling.d.ts +5 -1
- package/dist/src/primitives/utils/withScrolling.js +3 -0
- package/dist/src/primitives/utils/withScrolling.js.map +1 -1
- package/dist/src/render.d.ts +1 -0
- package/dist/src/render.js +4 -0
- package/dist/src/render.js.map +1 -1
- package/dist/src/utils.d.ts +1 -0
- package/dist/src/utils.js +5 -0
- package/dist/src/utils.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/primitives/Column.tsx +4 -6
- package/src/primitives/Row.tsx +4 -6
- package/src/primitives/Virtual.tsx +154 -73
- package/src/primitives/VirtualGrid.tsx +1 -1
- package/src/primitives/index.ts +1 -1
- package/src/primitives/utils/withScrolling.ts +13 -7
- package/src/render.ts +5 -0
- package/src/utils.ts +5 -0
|
@@ -8,6 +8,9 @@ export type VirtualProps<T> = lng.NewOmit<lngp.RowProps, 'children'> & {
|
|
|
8
8
|
each: readonly T[] | undefined | null | false;
|
|
9
9
|
displaySize: number;
|
|
10
10
|
bufferSize?: number;
|
|
11
|
+
wrap?: boolean;
|
|
12
|
+
scrollIndex?: number;
|
|
13
|
+
doScroll?: lngp.Scroller;
|
|
11
14
|
onEndReached?: () => void;
|
|
12
15
|
onEndReachedThreshold?: number;
|
|
13
16
|
fallback?: s.JSX.Element;
|
|
@@ -22,80 +25,157 @@ function createVirtual<T>(
|
|
|
22
25
|
) {
|
|
23
26
|
const [cursor, setCursor] = s.createSignal(props.selected ?? 0);
|
|
24
27
|
const bufferSize = s.createMemo(() => props.bufferSize || 2);
|
|
28
|
+
const scrollIndex = s.createMemo(() => {
|
|
29
|
+
return props.scrollIndex || 0;
|
|
30
|
+
});
|
|
25
31
|
const items = s.createMemo(() => props.each || []);
|
|
32
|
+
const itemCount = s.createMemo(() => items().length);
|
|
33
|
+
const scrollType = s.createMemo(() => props.scroll || 'auto');
|
|
34
|
+
|
|
35
|
+
const selected = () => {
|
|
36
|
+
if (props.wrap) {
|
|
37
|
+
return Math.max(bufferSize(), scrollIndex());
|
|
38
|
+
}
|
|
39
|
+
return props.selected || 0;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const start = () => {
|
|
43
|
+
if (itemCount() === 0) return 0;
|
|
44
|
+
if (props.wrap) {
|
|
45
|
+
return utils.mod(cursor() - Math.max(bufferSize(), scrollIndex()), itemCount());
|
|
46
|
+
}
|
|
47
|
+
if (scrollType() === 'always') {
|
|
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
|
+
};
|
|
26
55
|
|
|
27
|
-
const
|
|
28
|
-
|
|
56
|
+
const end = () => {
|
|
57
|
+
if (itemCount() === 0) return 0;
|
|
58
|
+
if (props.wrap) {
|
|
59
|
+
return (start() + props.displaySize + bufferSize()) % itemCount();
|
|
60
|
+
}
|
|
61
|
+
return Math.min(itemCount(), start() + props.displaySize + bufferSize());
|
|
62
|
+
};
|
|
29
63
|
|
|
30
|
-
const
|
|
31
|
-
|
|
64
|
+
const getSlice = s.createMemo(() => {
|
|
65
|
+
if (itemCount() === 0) return [];
|
|
66
|
+
if (!props.wrap) {
|
|
67
|
+
return items().slice(start(), end());
|
|
68
|
+
}
|
|
69
|
+
// Wrapping slice
|
|
70
|
+
const sIdx = start();
|
|
71
|
+
const eIdx = (sIdx + props.displaySize + bufferSize()) % itemCount();
|
|
72
|
+
if (sIdx < eIdx) {
|
|
73
|
+
return items().slice(sIdx, eIdx);
|
|
74
|
+
}
|
|
75
|
+
return [...items().slice(sIdx), ...items().slice(0, eIdx)];
|
|
76
|
+
});
|
|
32
77
|
|
|
33
|
-
const [slice, setSlice] = s.createSignal(
|
|
78
|
+
const [slice, setSlice] = s.createSignal(getSlice());
|
|
34
79
|
|
|
35
80
|
let viewRef!: lngp.NavigableElement;
|
|
36
81
|
|
|
37
82
|
function scrollToIndex(this: lng.ElementNode, index: number) {
|
|
38
|
-
|
|
83
|
+
if (itemCount() === 0) return;
|
|
84
|
+
let target = index;
|
|
85
|
+
if (props.wrap) {
|
|
86
|
+
target = utils.mod(index, itemCount());
|
|
87
|
+
} else {
|
|
88
|
+
target = utils.clamp(index, 0, itemCount() - 1);
|
|
89
|
+
}
|
|
90
|
+
updateSelected([target]);
|
|
39
91
|
}
|
|
40
92
|
|
|
41
|
-
const onSelectedChanged: lngp.OnSelectedChanged = function (_idx, elm,
|
|
93
|
+
const onSelectedChanged: lngp.OnSelectedChanged = function (_idx, elm, _active, _lastIdx) {
|
|
42
94
|
let idx = _idx;
|
|
43
|
-
let lastIdx = _lastIdx;
|
|
95
|
+
let lastIdx = _lastIdx || 0;
|
|
96
|
+
let active = _active;
|
|
97
|
+
const initialRun = idx === lastIdx;
|
|
44
98
|
|
|
45
|
-
if (
|
|
99
|
+
if (initialRun && !props.wrap) return;
|
|
46
100
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
101
|
+
if (!initialRun) {
|
|
102
|
+
if (props.wrap) {
|
|
103
|
+
setCursor(c => utils.mod(c + idx - lastIdx, itemCount()));
|
|
104
|
+
} else {
|
|
105
|
+
setCursor(c => utils.clamp(c + idx - lastIdx, 0, Math.max(0, itemCount() - 1)));
|
|
106
|
+
}
|
|
52
107
|
|
|
53
|
-
|
|
54
|
-
setSlice(items().slice(start(), end()));
|
|
108
|
+
setSlice(getSlice());
|
|
55
109
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
110
|
+
const c = cursor();
|
|
111
|
+
const scroll = scrollType();
|
|
112
|
+
if (props.wrap) {
|
|
113
|
+
this.selected = Math.max(bufferSize(), scrollIndex());
|
|
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
|
+
}
|
|
60
128
|
|
|
61
|
-
|
|
62
|
-
|
|
129
|
+
if (props.onEndReachedThreshold !== undefined && cursor() >= items().length - props.onEndReachedThreshold) {
|
|
130
|
+
props.onEndReached?.();
|
|
131
|
+
}
|
|
63
132
|
}
|
|
133
|
+
const isRow = component === lngp.Row;
|
|
134
|
+
const prevChildPos = isRow
|
|
135
|
+
? this.x + active.x
|
|
136
|
+
: this.y + active.y;
|
|
64
137
|
|
|
65
138
|
queueMicrotask(() => {
|
|
66
139
|
this.updateLayout();
|
|
67
|
-
|
|
140
|
+
if (this._initialPosition === undefined && props.wrap) {
|
|
141
|
+
this.offset = 0;
|
|
142
|
+
const axis = isRow ? 'x' : 'y';
|
|
143
|
+
this._initialPosition = this[axis];
|
|
144
|
+
if (scrollIndex() > 0) {
|
|
145
|
+
active = this.children[1] as lng.ElementNode;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
68
148
|
if (component === lngp.Row) {
|
|
69
|
-
this.lng.x = prevChildPos - active.x;
|
|
149
|
+
this.lng.x = this._targetPosition = prevChildPos - active.x;
|
|
70
150
|
} else {
|
|
71
|
-
this.lng.y = prevChildPos - active.y;
|
|
151
|
+
this.lng.y = this._targetPosition = prevChildPos - active.y;
|
|
72
152
|
}
|
|
73
|
-
|
|
74
153
|
scrollFn(idx, elm, active, lastIdx);
|
|
75
154
|
});
|
|
76
155
|
};
|
|
77
156
|
|
|
78
157
|
const chainedOnSelectedChanged = lngp.chainFunctions(props.onSelectedChanged, onSelectedChanged)!;
|
|
79
158
|
|
|
80
|
-
const updateSelected = ([selected, _items]
|
|
81
|
-
if (!viewRef ||
|
|
82
|
-
const
|
|
159
|
+
const updateSelected = ([selected, _items]: [number?, any?]) => {
|
|
160
|
+
if (!viewRef || selected === undefined) return;
|
|
161
|
+
const sel = selected;
|
|
162
|
+
const item = items()[sel];
|
|
83
163
|
let active = viewRef.children.find(x => x.item === item);
|
|
84
164
|
const lastSelected = viewRef.selected;
|
|
85
165
|
|
|
86
166
|
if (active instanceof lng.ElementNode) {
|
|
87
167
|
viewRef.selected = viewRef.children.indexOf(active);
|
|
88
168
|
chainedOnSelectedChanged.call(viewRef, viewRef.selected, viewRef, active, lastSelected);
|
|
169
|
+
active.setFocus();
|
|
89
170
|
} else {
|
|
90
|
-
setCursor(
|
|
91
|
-
setSlice(
|
|
171
|
+
setCursor(sel);
|
|
172
|
+
setSlice(getSlice());
|
|
92
173
|
queueMicrotask(() => {
|
|
93
174
|
viewRef.updateLayout();
|
|
94
175
|
active = viewRef.children.find(x => x.item === item);
|
|
95
176
|
if (active instanceof lng.ElementNode) {
|
|
96
177
|
viewRef.selected = viewRef.children.indexOf(active);
|
|
97
178
|
chainedOnSelectedChanged.call(viewRef, viewRef.selected, viewRef, active, lastSelected);
|
|
98
|
-
active.setFocus();
|
|
99
179
|
}
|
|
100
180
|
});
|
|
101
181
|
}
|
|
@@ -105,56 +185,57 @@ function createVirtual<T>(
|
|
|
105
185
|
|
|
106
186
|
s.createEffect(s.on(items, () => {
|
|
107
187
|
if (!viewRef) return;
|
|
108
|
-
|
|
188
|
+
if (cursor() >= itemCount()) {
|
|
189
|
+
setCursor(Math.max(0, itemCount() - 1));
|
|
190
|
+
}
|
|
191
|
+
setSlice(getSlice());
|
|
109
192
|
}, { defer: true }));
|
|
110
193
|
|
|
111
194
|
return (<view
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
);
|
|
195
|
+
{...props}
|
|
196
|
+
ref={lngp.chainRefs(el => { viewRef = el as lngp.NavigableElement; }, props.ref)}
|
|
197
|
+
selected={selected()}
|
|
198
|
+
cursor={cursor()}
|
|
199
|
+
{...keyHandlers}
|
|
200
|
+
forwardFocus={/* @once */ lngp.navigableForwardFocus}
|
|
201
|
+
scrollToIndex={/* @once */ scrollToIndex}
|
|
202
|
+
onCreate={/* @once */
|
|
203
|
+
props.selected
|
|
204
|
+
? lngp.chainFunctions(props.onCreate, scrollFn)
|
|
205
|
+
: props.onCreate
|
|
206
|
+
}
|
|
207
|
+
onSelectedChanged={/* @once */ chainedOnSelectedChanged}
|
|
208
|
+
style={/* @once */ lng.combineStyles(
|
|
209
|
+
props.style,
|
|
210
|
+
component === lngp.Row
|
|
211
|
+
? {
|
|
212
|
+
display: 'flex',
|
|
213
|
+
gap: 30,
|
|
214
|
+
transition: { x: { duration: 250, easing: 'ease-out' } },
|
|
215
|
+
}
|
|
216
|
+
: {
|
|
217
|
+
display: 'flex',
|
|
218
|
+
flexDirection: 'column',
|
|
219
|
+
gap: 30,
|
|
220
|
+
transition: { y: { duration: 250, easing: 'ease-out' } },
|
|
221
|
+
}
|
|
222
|
+
)}
|
|
223
|
+
>
|
|
224
|
+
<List each={slice()}>{props.children}</List>
|
|
225
|
+
</view>
|
|
226
|
+
);
|
|
145
227
|
}
|
|
146
228
|
|
|
147
|
-
|
|
148
229
|
export function VirtualRow<T>(props: VirtualProps<T>) {
|
|
149
|
-
return createVirtual(lngp.Row, props, lngp.withScrolling(true), {
|
|
150
|
-
onLeft: lngp.navigableHandleNavigation,
|
|
151
|
-
onRight: lngp.navigableHandleNavigation,
|
|
230
|
+
return createVirtual(lngp.Row, props, props.doScroll || lngp.withScrolling(true), {
|
|
231
|
+
onLeft: lngp.chainFunctions(props.onLeft, lngp.navigableHandleNavigation) as lng.KeyHandler,
|
|
232
|
+
onRight: lngp.chainFunctions(props.onRight, lngp.navigableHandleNavigation) as lng.KeyHandler,
|
|
152
233
|
});
|
|
153
234
|
}
|
|
154
235
|
|
|
155
236
|
export function VirtualColumn<T>(props: VirtualProps<T>) {
|
|
156
|
-
return createVirtual(lngp.Column, props, lngp.withScrolling(false), {
|
|
157
|
-
onUp: lngp.navigableHandleNavigation,
|
|
158
|
-
onDown: lngp.navigableHandleNavigation,
|
|
237
|
+
return createVirtual(lngp.Column, props, props.doScroll || lngp.withScrolling(false), {
|
|
238
|
+
onUp: lngp.chainFunctions(props.onUp, lngp.navigableHandleNavigation) as lng.KeyHandler,
|
|
239
|
+
onDown: lngp.chainFunctions(props.onDown, lngp.navigableHandleNavigation) as lng.KeyHandler,
|
|
159
240
|
});
|
|
160
241
|
}
|
|
@@ -164,7 +164,7 @@ export function VirtualGrid<T>(props: VirtualGridProps<T>): s.JSX.Element {
|
|
|
164
164
|
return (
|
|
165
165
|
<view
|
|
166
166
|
{...props}
|
|
167
|
-
scroll='always'
|
|
167
|
+
scroll={props.scroll || 'always'}
|
|
168
168
|
ref={lngp.chainRefs(el => { viewRef = el as lngp.NavigableElement; }, props.ref)}
|
|
169
169
|
selected={props.selected || 0}
|
|
170
170
|
cursor={cursor()}
|
package/src/primitives/index.ts
CHANGED
|
@@ -18,7 +18,7 @@ export * from './createFocusStack.jsx';
|
|
|
18
18
|
export * from './useHold.js';
|
|
19
19
|
export * from './VirtualGrid.jsx';
|
|
20
20
|
export * from './Virtual.jsx';
|
|
21
|
-
export
|
|
21
|
+
export * from './utils/withScrolling.js';
|
|
22
22
|
export {
|
|
23
23
|
type AnyFunction,
|
|
24
24
|
chainFunctions,
|
|
@@ -5,6 +5,13 @@ import type {
|
|
|
5
5
|
Styles,
|
|
6
6
|
} from '@lightningtv/core';
|
|
7
7
|
|
|
8
|
+
export type Scroller = (
|
|
9
|
+
selected: number | ElementNode,
|
|
10
|
+
component?: ElementNode,
|
|
11
|
+
selectedElement?: ElementNode | ElementText,
|
|
12
|
+
lastSelected?: number,
|
|
13
|
+
) => void;
|
|
14
|
+
|
|
8
15
|
// Adds properties expected by withScrolling
|
|
9
16
|
export interface ScrollableElement extends ElementNode {
|
|
10
17
|
scrollIndex?: number;
|
|
@@ -33,16 +40,12 @@ const isNotShown = (node: ElementNode | ElementText) => {
|
|
|
33
40
|
Always scroll moves the list every time
|
|
34
41
|
*/
|
|
35
42
|
|
|
36
|
-
|
|
43
|
+
/** @deprecated Use {@link scrollRow} or {@link scrollColumn} */
|
|
44
|
+
export function withScrolling(isRow: boolean): Scroller {
|
|
37
45
|
const dimension = isRow ? 'width' : 'height';
|
|
38
46
|
const axis = isRow ? 'x' : 'y';
|
|
39
47
|
|
|
40
|
-
return (
|
|
41
|
-
selected: number | ElementNode,
|
|
42
|
-
component?: ElementNode,
|
|
43
|
-
selectedElement?: ElementNode | ElementText,
|
|
44
|
-
lastSelected?: number,
|
|
45
|
-
) => {
|
|
48
|
+
return (selected, component, selectedElement, lastSelected) => {
|
|
46
49
|
let componentRef = component as ScrollableElement;
|
|
47
50
|
if (typeof selected !== 'number') {
|
|
48
51
|
componentRef = selected as ScrollableElement;
|
|
@@ -187,3 +190,6 @@ export function withScrolling(isRow: boolean) {
|
|
|
187
190
|
}
|
|
188
191
|
};
|
|
189
192
|
}
|
|
193
|
+
|
|
194
|
+
export const scrollRow = /* @__PURE__ */ withScrolling(true);
|
|
195
|
+
export const scrollColumn = /* @__PURE__ */ withScrolling(false);
|
package/src/render.ts
CHANGED
|
@@ -156,3 +156,8 @@ export const Text = (props: TextProps) => {
|
|
|
156
156
|
spread(el, props, false);
|
|
157
157
|
return el as unknown as JSXElement;
|
|
158
158
|
};
|
|
159
|
+
|
|
160
|
+
export function registerDefaultShader(name: string, shader: any) {
|
|
161
|
+
// noop for v2
|
|
162
|
+
// renderer.stage.shManager.registerShaderType('rounded', Rounded);
|
|
163
|
+
}
|
package/src/utils.ts
CHANGED
|
@@ -66,3 +66,8 @@ export const clamp = (value: number, min: number, max: number) =>
|
|
|
66
66
|
min < max
|
|
67
67
|
? Math.min(Math.max(value, min), max)
|
|
68
68
|
: Math.min(Math.max(value, max), min);
|
|
69
|
+
|
|
70
|
+
export function mod(n: number, m: number): number {
|
|
71
|
+
if (m === 0) return 0;
|
|
72
|
+
return ((n % m) + m) % m;
|
|
73
|
+
}
|