@lightningtv/solid 3.0.0-9 → 3.0.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/LICENSE +1 -1
- package/README.md +6 -0
- package/dist/jsx-runtime.d.ts +14 -0
- package/dist/src/activeElement.d.ts +1 -1
- package/dist/src/core/animation.d.ts +35 -0
- package/dist/src/core/animation.js +119 -0
- package/dist/src/core/animation.js.map +1 -0
- package/dist/src/core/config.d.ts +49 -0
- package/dist/src/core/config.js +33 -0
- package/dist/src/core/config.js.map +1 -0
- package/dist/src/core/domRenderer.d.ts +115 -0
- package/dist/src/core/domRenderer.js +1152 -0
- package/dist/src/core/domRenderer.js.map +1 -0
- package/dist/src/core/elementNode.d.ts +463 -0
- package/dist/src/core/elementNode.js +833 -0
- package/dist/src/core/elementNode.js.map +1 -0
- package/dist/src/core/flex.d.ts +2 -0
- package/dist/src/core/flex.js +243 -0
- package/dist/src/core/flex.js.map +1 -0
- package/dist/src/core/focusKeyTypes.d.ts +42 -0
- package/dist/src/core/focusKeyTypes.js +2 -0
- package/dist/src/core/focusKeyTypes.js.map +1 -0
- package/dist/src/core/focusManager.d.ts +13 -0
- package/dist/src/core/focusManager.js +276 -0
- package/dist/src/core/focusManager.js.map +1 -0
- package/dist/src/core/index.d.ts +12 -0
- package/dist/src/core/index.js +12 -0
- package/dist/src/core/index.js.map +1 -0
- package/dist/src/core/intrinsicTypes.d.ts +90 -0
- package/dist/src/core/intrinsicTypes.js +2 -0
- package/dist/src/core/intrinsicTypes.js.map +1 -0
- package/dist/src/core/lightningInit.d.ts +89 -0
- package/dist/src/core/lightningInit.js +26 -0
- package/dist/src/core/lightningInit.js.map +1 -0
- package/dist/src/core/nodeTypes.d.ts +6 -0
- package/dist/src/core/nodeTypes.js +6 -0
- package/dist/src/core/nodeTypes.js.map +1 -0
- package/dist/src/core/shaders.d.ts +51 -0
- package/dist/src/core/shaders.js +446 -0
- package/dist/src/core/shaders.js.map +1 -0
- package/dist/src/core/states.d.ts +12 -0
- package/dist/src/core/states.js +84 -0
- package/dist/src/core/states.js.map +1 -0
- package/dist/src/core/utils.d.ts +39 -0
- package/dist/src/core/utils.js +164 -0
- package/dist/src/core/utils.js.map +1 -0
- package/dist/src/devtools/index.d.ts +1 -1
- package/dist/src/devtools/index.js +1 -1
- package/dist/src/devtools/index.js.map +1 -1
- package/dist/src/index.d.ts +3 -3
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/primitives/Column.jsx +9 -10
- package/dist/src/primitives/Column.jsx.map +1 -1
- package/dist/src/primitives/FPSCounter.jsx +15 -2
- package/dist/src/primitives/FPSCounter.jsx.map +1 -1
- package/dist/src/primitives/Image.d.ts +8 -0
- package/dist/src/primitives/Image.jsx +24 -0
- package/dist/src/primitives/Image.jsx.map +1 -0
- package/dist/src/primitives/KeepAlive.d.ts +30 -0
- package/dist/src/primitives/KeepAlive.jsx +77 -0
- package/dist/src/primitives/KeepAlive.jsx.map +1 -0
- package/dist/src/primitives/Lazy.d.ts +8 -7
- package/dist/src/primitives/Lazy.jsx +52 -20
- package/dist/src/primitives/Lazy.jsx.map +1 -1
- package/dist/src/primitives/LazyImport.d.ts +8 -0
- package/dist/src/primitives/LazyImport.js +40 -0
- package/dist/src/primitives/LazyImport.js.map +1 -0
- package/dist/src/primitives/Preserve.d.ts +4 -0
- package/dist/src/primitives/Preserve.jsx +11 -0
- package/dist/src/primitives/Preserve.jsx.map +1 -0
- package/dist/src/primitives/Row.jsx +9 -10
- package/dist/src/primitives/Row.jsx.map +1 -1
- package/dist/src/primitives/Suspense.d.ts +22 -0
- package/dist/src/primitives/Suspense.jsx +33 -0
- package/dist/src/primitives/Suspense.jsx.map +1 -0
- package/dist/src/primitives/Virtual.d.ts +18 -0
- package/dist/src/primitives/Virtual.jsx +443 -0
- package/dist/src/primitives/Virtual.jsx.map +1 -0
- package/dist/src/primitives/VirtualGrid.d.ts +13 -0
- package/dist/src/primitives/VirtualGrid.jsx +160 -0
- package/dist/src/primitives/VirtualGrid.jsx.map +1 -0
- package/dist/src/primitives/Visible.d.ts +0 -1
- package/dist/src/primitives/Visible.jsx +1 -1
- package/dist/src/primitives/Visible.jsx.map +1 -1
- package/dist/src/primitives/announcer/announcer.d.ts +1 -0
- package/dist/src/primitives/announcer/announcer.js +4 -3
- package/dist/src/primitives/announcer/announcer.js.map +1 -1
- package/dist/src/primitives/announcer/speech.d.ts +1 -1
- package/dist/src/primitives/announcer/speech.js +98 -8
- package/dist/src/primitives/announcer/speech.js.map +1 -1
- package/dist/src/primitives/createFocusStack.d.ts +4 -4
- package/dist/src/primitives/createFocusStack.jsx +15 -6
- package/dist/src/primitives/createFocusStack.jsx.map +1 -1
- package/dist/src/primitives/createTag.d.ts +8 -0
- package/dist/src/primitives/createTag.jsx +20 -0
- package/dist/src/primitives/createTag.jsx.map +1 -0
- package/dist/src/primitives/index.d.ts +13 -4
- package/dist/src/primitives/index.js +12 -3
- package/dist/src/primitives/index.js.map +1 -1
- package/dist/src/primitives/types.d.ts +3 -2
- package/dist/src/primitives/useFocusManager.d.ts +2 -2
- package/dist/src/primitives/useFocusManager.js +2 -2
- package/dist/src/primitives/useFocusManager.js.map +1 -1
- package/dist/src/primitives/useMouse.d.ts +18 -2
- package/dist/src/primitives/useMouse.js +171 -47
- package/dist/src/primitives/useMouse.js.map +1 -1
- package/dist/src/primitives/utils/createBlurredImage.d.ts +56 -0
- package/dist/src/primitives/utils/createBlurredImage.js +223 -0
- package/dist/src/primitives/utils/createBlurredImage.js.map +1 -0
- package/dist/src/primitives/utils/createSpriteMap.d.ts +2 -2
- package/dist/src/primitives/utils/createSpriteMap.js +3 -3
- package/dist/src/primitives/utils/createSpriteMap.js.map +1 -1
- package/dist/src/primitives/utils/handleNavigation.d.ts +79 -5
- package/dist/src/primitives/utils/handleNavigation.js +241 -69
- package/dist/src/primitives/utils/handleNavigation.js.map +1 -1
- package/dist/src/primitives/utils/withScrolling.d.ts +12 -2
- package/dist/src/primitives/utils/withScrolling.js +59 -7
- package/dist/src/primitives/utils/withScrolling.js.map +1 -1
- package/dist/src/render.d.ts +5 -4
- package/dist/src/render.js +5 -1
- package/dist/src/render.js.map +1 -1
- package/dist/src/shaders/Rounded.d.ts +7 -0
- package/dist/src/shaders/Rounded.js +88 -0
- package/dist/src/shaders/Rounded.js.map +1 -0
- package/dist/src/shaders/RoundedWithBorder.d.ts +3 -0
- package/dist/src/shaders/RoundedWithBorder.js +217 -0
- package/dist/src/shaders/RoundedWithBorder.js.map +1 -0
- package/dist/src/shaders/index.d.ts +4 -0
- package/dist/src/shaders/index.js +5 -0
- package/dist/src/shaders/index.js.map +1 -0
- package/dist/src/shaders/templates/RoundedTemplate.d.ts +12 -0
- package/dist/src/shaders/templates/RoundedTemplate.js +48 -0
- package/dist/src/shaders/templates/RoundedTemplate.js.map +1 -0
- package/dist/src/shaders/templates/RoundedWithBorderTemplate.d.ts +20 -0
- package/dist/src/shaders/templates/RoundedWithBorderTemplate.js +93 -0
- package/dist/src/shaders/templates/RoundedWithBorderTemplate.js.map +1 -0
- package/dist/src/shaders/utils.d.ts +3 -0
- package/dist/src/shaders/utils.js +31 -0
- package/dist/src/shaders/utils.js.map +1 -0
- package/dist/src/solidOpts.d.ts +1 -7
- package/dist/src/solidOpts.js +9 -1
- package/dist/src/solidOpts.js.map +1 -1
- package/dist/src/types.d.ts +1 -13
- package/dist/src/utils.d.ts +3 -1
- package/dist/src/utils.js +9 -1
- package/dist/src/utils.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/jsx-runtime.d.ts +1 -1
- package/package.json +28 -16
- package/src/activeElement.ts +1 -1
- package/src/core/animation.ts +185 -0
- package/src/core/config.ts +89 -0
- package/src/core/domRenderer.ts +1300 -0
- package/src/core/elementNode.ts +1458 -0
- package/src/core/flex.ts +284 -0
- package/src/core/focusKeyTypes.ts +90 -0
- package/src/core/focusManager.ts +381 -0
- package/src/core/index.ts +13 -0
- package/src/core/intrinsicTypes.ts +199 -0
- package/src/core/lightningInit.ts +147 -0
- package/src/core/nodeTypes.ts +6 -0
- package/src/core/shaders.ts +567 -0
- package/src/core/states.ts +91 -0
- package/src/core/utils.ts +222 -0
- package/src/devtools/index.ts +1 -1
- package/src/index.ts +3 -3
- package/src/primitives/Column.tsx +10 -12
- package/src/primitives/FPSCounter.tsx +16 -2
- package/src/primitives/Image.tsx +36 -0
- package/src/primitives/KeepAlive.tsx +124 -0
- package/src/primitives/Lazy.tsx +66 -37
- package/src/primitives/LazyImport.ts +53 -0
- package/src/primitives/Preserve.tsx +18 -0
- package/src/primitives/Row.tsx +13 -14
- package/src/primitives/Suspense.tsx +39 -0
- package/src/primitives/Virtual.tsx +486 -0
- package/src/primitives/VirtualGrid.tsx +220 -0
- package/src/primitives/Visible.tsx +1 -2
- package/src/primitives/announcer/announcer.ts +10 -3
- package/src/primitives/announcer/speech.ts +113 -6
- package/src/primitives/createFocusStack.tsx +18 -7
- package/src/primitives/createTag.tsx +33 -0
- package/src/primitives/index.ts +13 -4
- package/src/primitives/types.ts +3 -2
- package/src/primitives/useFocusManager.ts +3 -3
- package/src/primitives/useMouse.ts +306 -67
- package/src/primitives/utils/createBlurredImage.ts +366 -0
- package/src/primitives/utils/createSpriteMap.ts +8 -6
- package/src/primitives/utils/handleNavigation.ts +300 -84
- package/src/primitives/utils/withScrolling.ts +76 -18
- package/src/render.ts +7 -3
- package/src/shaders/Rounded.ts +100 -0
- package/src/shaders/RoundedWithBorder.ts +245 -0
- package/src/shaders/index.ts +4 -0
- package/src/shaders/templates/RoundedTemplate.ts +57 -0
- package/src/shaders/templates/RoundedWithBorderTemplate.ts +110 -0
- package/src/shaders/utils.ts +44 -0
- package/src/solidOpts.ts +9 -7
- package/src/types.ts +1 -15
- package/src/utils.ts +11 -1
- package/dist/src/client.d.ts +0 -1
- package/dist/src/client.js +0 -2
- package/dist/src/client.js.map +0 -1
- package/dist/src/core.d.ts +0 -1
- package/dist/src/core.js +0 -3
- package/dist/src/core.js.map +0 -1
- package/dist/src/jsx-runtime.d.ts +0 -10
- package/dist/src/jsx-runtime.js +0 -2
- package/dist/src/jsx-runtime.js.map +0 -1
- package/dist/src/primitives/Infinite.d.ts +0 -15
- package/dist/src/primitives/Infinite.jsx +0 -59
- package/dist/src/primitives/Infinite.jsx.map +0 -1
- package/dist/src/primitives/LazyUp.d.ts +0 -11
- package/dist/src/primitives/LazyUp.jsx +0 -38
- package/dist/src/primitives/LazyUp.jsx.map +0 -1
- package/dist/src/primitives/sprite.d.ts +0 -9
- package/dist/src/primitives/sprite.js +0 -18
- package/dist/src/primitives/sprite.js.map +0 -1
- package/dist/src/primitives/utils/createFocusStack.d.ts +0 -24
- package/dist/src/primitives/utils/createFocusStack.js +0 -59
- package/dist/src/primitives/utils/createFocusStack.js.map +0 -1
- package/dist/src/primitives/utils/scrollToIndex.d.ts +0 -2
- package/dist/src/primitives/utils/scrollToIndex.js +0 -33
- package/dist/src/primitives/utils/scrollToIndex.js.map +0 -1
- package/dist/src/renderClient.d.ts +0 -21
- package/dist/src/renderClient.js +0 -64
- package/dist/src/renderClient.js.map +0 -1
|
@@ -1,101 +1,317 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
return
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
import * as s from 'solid-js';
|
|
2
|
+
import * as lng from '../../index.js';
|
|
3
|
+
import * as lngp from '../index.js';
|
|
4
|
+
|
|
5
|
+
function idxInArray(idx: number, arr: readonly any[]): boolean {
|
|
6
|
+
return idx >= 0 && idx < arr.length;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function findFirstFocusableChildIdx(
|
|
10
|
+
el: lngp.NavigableElement,
|
|
11
|
+
from = 0,
|
|
12
|
+
delta = 1,
|
|
13
|
+
): number {
|
|
14
|
+
for (let i = from; ; i += delta) {
|
|
15
|
+
if (!idxInArray(i, el.children)) {
|
|
16
|
+
if (el.wrap) {
|
|
17
|
+
i = (i + el.children.length) % el.children.length;
|
|
18
|
+
} else break;
|
|
19
|
+
}
|
|
20
|
+
if (!el.children[i]?.skipFocus) {
|
|
21
|
+
return i;
|
|
17
22
|
}
|
|
18
|
-
|
|
23
|
+
}
|
|
24
|
+
return -1;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function selectChild(el: lngp.NavigableElement, index: number): boolean {
|
|
28
|
+
const child = el.children[index];
|
|
29
|
+
|
|
30
|
+
if (child == null || child.skipFocus) {
|
|
31
|
+
el.selected = -1;
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const lastSelected = el.selected;
|
|
36
|
+
el.selected = index;
|
|
37
|
+
|
|
38
|
+
if (!lng.isFocused(child)) {
|
|
19
39
|
child.setFocus();
|
|
40
|
+
}
|
|
20
41
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
42
|
+
// Always call onSelectedChanged on first focus for clients
|
|
43
|
+
el.onSelectedChanged?.(index, el, child as lng.ElementNode, lastSelected);
|
|
44
|
+
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** @deprecated Use {@link navigableForwardFocus} instead */
|
|
49
|
+
export function onGridFocus(
|
|
50
|
+
_?: lngp.OnSelectedChanged,
|
|
51
|
+
): lng.ForwardFocusHandler {
|
|
52
|
+
return function () {
|
|
53
|
+
return navigableForwardFocus.call(this, this);
|
|
26
54
|
};
|
|
27
55
|
}
|
|
28
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Forwards focus to the first focusable child of a {@link lngp.NavigableElement} and
|
|
59
|
+
* selects it.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```tsx
|
|
63
|
+
* <view
|
|
64
|
+
* selected={0}
|
|
65
|
+
* forwardFocus={navigableForwardFocus}
|
|
66
|
+
* onSelectedChanged={(idx, el, child, lastIdx) => {...}}
|
|
67
|
+
* >
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export const navigableForwardFocus: lng.ForwardFocusHandler = function () {
|
|
71
|
+
const navigable = this as lngp.NavigableElement;
|
|
72
|
+
|
|
73
|
+
let selected = Math.max(navigable.selected, 0);
|
|
74
|
+
|
|
75
|
+
if (selected !== 0) {
|
|
76
|
+
selected = lng.clamp(selected, 0, Math.max(0, this.children.length - 1));
|
|
77
|
+
while (!idxInArray(selected, this.children)) {
|
|
78
|
+
selected--;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
selected = findFirstFocusableChildIdx(navigable, selected);
|
|
83
|
+
// update selected as firstfocusable maybe different if first element has skipFocus
|
|
84
|
+
navigable.selected = selected;
|
|
85
|
+
return selectChild(navigable, selected);
|
|
86
|
+
};
|
|
87
|
+
|
|
29
88
|
export function handleNavigation(
|
|
30
89
|
direction: 'up' | 'right' | 'down' | 'left',
|
|
31
|
-
): KeyHandler {
|
|
90
|
+
): lng.KeyHandler {
|
|
32
91
|
return function () {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
92
|
+
return moveSelection(
|
|
93
|
+
this as lngp.NavigableElement,
|
|
94
|
+
direction === 'up' || direction === 'left' ? -1 : 1,
|
|
95
|
+
);
|
|
96
|
+
};
|
|
97
|
+
}
|
|
36
98
|
|
|
37
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Handles navigation key events for navigable elements, \
|
|
101
|
+
* such as {@link lngp.Row} and {@link lngp.Column}.
|
|
102
|
+
*
|
|
103
|
+
* Uses {@link moveSelection} to select the next or previous child based on the key pressed.
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```tsx
|
|
107
|
+
* <view
|
|
108
|
+
* selected={0}
|
|
109
|
+
* onUp={navigableHandleNavigation}
|
|
110
|
+
* onDown={navigableHandleNavigation}
|
|
111
|
+
* onSelectedChanged={(idx, el, child, lastIdx) => {...}}
|
|
112
|
+
* >
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
export const navigableHandleNavigation: lng.KeyHandler = function (e) {
|
|
116
|
+
return moveSelection(
|
|
117
|
+
this as lngp.NavigableElement,
|
|
118
|
+
e.key === 'ArrowUp' || e.key === 'ArrowLeft' ? -1 : 1,
|
|
119
|
+
);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Moves the selection within a {@link lngp.NavigableElement}.
|
|
124
|
+
*/
|
|
125
|
+
export function moveSelection(
|
|
126
|
+
el: lngp.NavigableElement,
|
|
127
|
+
delta: number,
|
|
128
|
+
): boolean {
|
|
129
|
+
let selected = findFirstFocusableChildIdx(el, el.selected + delta, delta);
|
|
130
|
+
|
|
131
|
+
if (selected === -1) {
|
|
132
|
+
if (
|
|
133
|
+
!idxInArray(el.selected, el.children) ||
|
|
134
|
+
el.children[el.selected]?.skipFocus ||
|
|
135
|
+
lng.isFocused(el.children[el.selected]!)
|
|
136
|
+
) {
|
|
38
137
|
return false;
|
|
39
138
|
}
|
|
139
|
+
selected = el.selected;
|
|
140
|
+
}
|
|
40
141
|
|
|
41
|
-
|
|
42
|
-
do {
|
|
43
|
-
this.selected = ((this.selected || 0) % numChildren) + 1;
|
|
44
|
-
if (this.selected >= numChildren) {
|
|
45
|
-
if (!wrap) {
|
|
46
|
-
this.selected = -1;
|
|
47
|
-
break;
|
|
48
|
-
}
|
|
49
|
-
this.selected = 0;
|
|
50
|
-
}
|
|
51
|
-
} while (this.children[this.selected]?.skipFocus);
|
|
52
|
-
} else if (direction === 'left' || direction === 'up') {
|
|
53
|
-
do {
|
|
54
|
-
this.selected = ((this.selected || 0) % numChildren) - 1;
|
|
55
|
-
if (this.selected < 0) {
|
|
56
|
-
if (!wrap) {
|
|
57
|
-
this.selected = -1;
|
|
58
|
-
break;
|
|
59
|
-
}
|
|
60
|
-
this.selected = numChildren - 1;
|
|
61
|
-
}
|
|
62
|
-
} while (this.children[this.selected]?.skipFocus);
|
|
63
|
-
}
|
|
142
|
+
const active = el.children[selected]!;
|
|
64
143
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
144
|
+
if (el.plinko) {
|
|
145
|
+
// Set the next item to have the same selected index
|
|
146
|
+
// so we move up / down directly
|
|
147
|
+
const lastSelectedChild = el.children[el.selected];
|
|
148
|
+
lng.assertTruthy(lastSelectedChild instanceof lng.ElementNode);
|
|
149
|
+
|
|
150
|
+
const num = lastSelectedChild.selected || 0;
|
|
151
|
+
active.selected =
|
|
152
|
+
num < active.children.length ? num : active.children.length - 1;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return selectChild(el, selected);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function distanceBetweenRectCenters(a: lng.Rect, b: lng.Rect): number {
|
|
159
|
+
const dx = Math.abs(a.x + a.width / 2 - (b.x + b.width / 2)) / 2;
|
|
160
|
+
const dy = Math.abs(a.y + a.height / 2 - (b.y + b.height / 2)) / 2;
|
|
161
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function findClosestFocusableChildIdx(
|
|
165
|
+
el: lng.ElementNode,
|
|
166
|
+
prevEl: lng.ElementNode,
|
|
167
|
+
): number {
|
|
168
|
+
// select child closest to the previous active element
|
|
169
|
+
const prevRect = lng.getElementScreenRect(prevEl);
|
|
170
|
+
const elRect = lng.getElementScreenRect(el);
|
|
171
|
+
const childRect: lng.Rect = { x: 0, y: 0, width: 0, height: 0 };
|
|
172
|
+
|
|
173
|
+
let closestIdx = -1;
|
|
174
|
+
let closestDist = Infinity;
|
|
175
|
+
|
|
176
|
+
for (const [idx, child] of el.children.entries()) {
|
|
177
|
+
if (!child.skipFocus) {
|
|
178
|
+
lng.getElementScreenRect(child, el, childRect);
|
|
179
|
+
childRect.x += elRect.x;
|
|
180
|
+
childRect.y += elRect.y;
|
|
181
|
+
const distance = distanceBetweenRectCenters(prevRect, childRect);
|
|
182
|
+
if (distance < closestDist) {
|
|
183
|
+
closestDist = distance;
|
|
184
|
+
closestIdx = idx;
|
|
74
185
|
}
|
|
75
186
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
navigableThis.onSelectedChanged &&
|
|
81
|
-
navigableThis.onSelectedChanged.call(
|
|
82
|
-
navigableThis,
|
|
83
|
-
navigableThis.selected,
|
|
84
|
-
navigableThis,
|
|
85
|
-
active,
|
|
86
|
-
lastSelected,
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
if (this.plinko) {
|
|
90
|
-
// Set the next item to have the same selected index
|
|
91
|
-
// so we move up / down directly
|
|
92
|
-
const lastSelectedChild = this.children[lastSelected];
|
|
93
|
-
assertTruthy(lastSelectedChild instanceof ElementNode);
|
|
94
|
-
const num = lastSelectedChild.selected || 0;
|
|
95
|
-
active.selected =
|
|
96
|
-
num < active.children.length ? num : active.children.length - 1;
|
|
97
|
-
}
|
|
98
|
-
active.setFocus();
|
|
99
|
-
return true;
|
|
100
|
-
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return closestIdx;
|
|
101
190
|
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Forwards focus to the closest or first focusable child of a {@link lngp.NavigableElement} and
|
|
194
|
+
* selects it.
|
|
195
|
+
*
|
|
196
|
+
* To determine the closest child, it uses the distance between the center of the previous focused element
|
|
197
|
+
* and the center of each child element.
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* ```tsx
|
|
201
|
+
* <view
|
|
202
|
+
* selected={0}
|
|
203
|
+
* forwardFocus={spatialForwardFocus}
|
|
204
|
+
* onSelectedChanged={(idx, el, child, lastIdx) => {...}}
|
|
205
|
+
* >
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
export const spatialForwardFocus: lng.ForwardFocusHandler = function () {
|
|
209
|
+
const prevEl = s.untrack(lng.activeElement);
|
|
210
|
+
if (prevEl) {
|
|
211
|
+
const idx = findClosestFocusableChildIdx(this, prevEl);
|
|
212
|
+
const selected = selectChild(this as lngp.NavigableElement, idx);
|
|
213
|
+
if (selected) return true;
|
|
214
|
+
}
|
|
215
|
+
const idx = findFirstFocusableChildIdx(this as lngp.NavigableElement);
|
|
216
|
+
return selectChild(this as lngp.NavigableElement, idx);
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Handles spatial navigation within a {@link lngp.NavigableElement} by moving focus
|
|
221
|
+
* based on the arrow keys pressed.
|
|
222
|
+
*
|
|
223
|
+
* This function allows for navigation in a grid-like manner for flex-wrap containers, \
|
|
224
|
+
* where pressing the arrow keys will either:
|
|
225
|
+
* - move focus to the next/prev child in the same row/column
|
|
226
|
+
* - or find the closest child in the next/prev row/column.
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* ```tsx
|
|
230
|
+
* <view
|
|
231
|
+
* selected={0}
|
|
232
|
+
* display="flex"
|
|
233
|
+
* flexWrap="wrap"
|
|
234
|
+
* onUp={spatialHandleNavigation}
|
|
235
|
+
* onDown={spatialHandleNavigation}
|
|
236
|
+
* onSelectedChanged={(idx, el, child, lastIdx) => {...}}
|
|
237
|
+
* >
|
|
238
|
+
* ```
|
|
239
|
+
*/
|
|
240
|
+
export const spatialHandleNavigation: lng.KeyHandler = function (e) {
|
|
241
|
+
let selected = this.selected;
|
|
242
|
+
|
|
243
|
+
if (typeof selected !== 'number' || !idxInArray(selected, this.children)) {
|
|
244
|
+
selected = findFirstFocusableChildIdx(this as lngp.NavigableElement);
|
|
245
|
+
return selectChild(this as lngp.NavigableElement, selected);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const prevChild = this.children[selected]!;
|
|
249
|
+
|
|
250
|
+
const move = { x: 0, y: 0 };
|
|
251
|
+
switch (e.key) {
|
|
252
|
+
case 'ArrowLeft':
|
|
253
|
+
move.x = -1;
|
|
254
|
+
break;
|
|
255
|
+
case 'ArrowRight':
|
|
256
|
+
move.x = 1;
|
|
257
|
+
break;
|
|
258
|
+
case 'ArrowUp':
|
|
259
|
+
move.y = -1;
|
|
260
|
+
break;
|
|
261
|
+
case 'ArrowDown':
|
|
262
|
+
move.y = 1;
|
|
263
|
+
break;
|
|
264
|
+
default:
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const flexDir = this.flexDirection === 'column' ? 'y' : 'x';
|
|
269
|
+
const crossDir = flexDir === 'x' ? 'y' : 'x';
|
|
270
|
+
const flexDelta = move[flexDir];
|
|
271
|
+
const crossDelta = move[crossDir];
|
|
272
|
+
|
|
273
|
+
// Select next/prev child in the current column/row
|
|
274
|
+
if (flexDelta !== 0) {
|
|
275
|
+
for (
|
|
276
|
+
let i = selected + flexDelta;
|
|
277
|
+
idxInArray(i, this.children);
|
|
278
|
+
i += flexDelta
|
|
279
|
+
) {
|
|
280
|
+
const child = this.children[i]!;
|
|
281
|
+
if (child.skipFocus) continue;
|
|
282
|
+
|
|
283
|
+
// Different column/row
|
|
284
|
+
if (child[crossDir] !== prevChild[crossDir]) break;
|
|
285
|
+
|
|
286
|
+
return selectChild(this as lngp.NavigableElement, i);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// Find child in next/prev column/row
|
|
290
|
+
else {
|
|
291
|
+
let closestIdx = -1;
|
|
292
|
+
let closestDist = Infinity;
|
|
293
|
+
|
|
294
|
+
for (
|
|
295
|
+
let i = selected + crossDelta;
|
|
296
|
+
idxInArray(i, this.children);
|
|
297
|
+
i += crossDelta
|
|
298
|
+
) {
|
|
299
|
+
const child = this.children[i]!;
|
|
300
|
+
if (child.skipFocus) continue;
|
|
301
|
+
|
|
302
|
+
// Same column/row, skip
|
|
303
|
+
if (child[crossDir] === prevChild[crossDir]) continue;
|
|
304
|
+
|
|
305
|
+
// Different column/row, check distance
|
|
306
|
+
const distance = Math.abs(child[flexDir] - prevChild[flexDir]);
|
|
307
|
+
if (distance >= closestDist) break; // getting further away
|
|
308
|
+
|
|
309
|
+
closestDist = distance;
|
|
310
|
+
closestIdx = i;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return selectChild(this as lngp.NavigableElement, closestIdx);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return false;
|
|
317
|
+
};
|
|
@@ -3,14 +3,23 @@ import type {
|
|
|
3
3
|
ElementText,
|
|
4
4
|
INode,
|
|
5
5
|
Styles,
|
|
6
|
-
} from '
|
|
6
|
+
} from '../../core/index.js';
|
|
7
|
+
|
|
8
|
+
export type Scroller = (
|
|
9
|
+
selected: number | ElementNode,
|
|
10
|
+
component?: ElementNode,
|
|
11
|
+
selectedElement?: ElementNode | ElementText,
|
|
12
|
+
lastSelected?: number,
|
|
13
|
+
) => void;
|
|
7
14
|
|
|
8
15
|
// Adds properties expected by withScrolling
|
|
9
16
|
export interface ScrollableElement extends ElementNode {
|
|
10
17
|
scrollIndex?: number;
|
|
18
|
+
scroll?: 'always' | 'none' | 'edge' | 'auto' | 'center' | 'bounded';
|
|
11
19
|
selected: number;
|
|
12
20
|
offset?: number;
|
|
13
21
|
endOffset?: number;
|
|
22
|
+
upCount?: number;
|
|
14
23
|
onScrolled?: (
|
|
15
24
|
elm: ScrollableElement,
|
|
16
25
|
offset: number,
|
|
@@ -32,16 +41,25 @@ const isNotShown = (node: ElementNode | ElementText) => {
|
|
|
32
41
|
Always scroll moves the list every time
|
|
33
42
|
*/
|
|
34
43
|
|
|
35
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Checks if the selected index is in the non-scrollable zone (last upCount items).
|
|
46
|
+
*/
|
|
47
|
+
export function checkIsInNonScrollableZone(
|
|
48
|
+
componentRef: ScrollableElement,
|
|
49
|
+
): boolean {
|
|
50
|
+
const totalItems = componentRef.children.length;
|
|
51
|
+
const upCount = componentRef.upCount || 6;
|
|
52
|
+
const selected = componentRef.selected || 0;
|
|
53
|
+
const nonScrollableZoneStart = Math.max(0, totalItems - upCount);
|
|
54
|
+
return selected >= nonScrollableZoneStart;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** @deprecated Use {@link scrollRow} or {@link scrollColumn} */
|
|
58
|
+
export function withScrolling(isRow: boolean): Scroller {
|
|
36
59
|
const dimension = isRow ? 'width' : 'height';
|
|
37
60
|
const axis = isRow ? 'x' : 'y';
|
|
38
61
|
|
|
39
|
-
return (
|
|
40
|
-
selected: number | ElementNode,
|
|
41
|
-
component?: ElementNode,
|
|
42
|
-
selectedElement?: ElementNode | ElementText,
|
|
43
|
-
lastSelected?: number,
|
|
44
|
-
) => {
|
|
62
|
+
return (selected, component, selectedElement, lastSelected) => {
|
|
45
63
|
let componentRef = component as ScrollableElement;
|
|
46
64
|
if (typeof selected !== 'number') {
|
|
47
65
|
componentRef = selected as ScrollableElement;
|
|
@@ -50,6 +68,7 @@ export function withScrolling(isRow: boolean) {
|
|
|
50
68
|
if (
|
|
51
69
|
!componentRef ||
|
|
52
70
|
componentRef.scroll === 'none' ||
|
|
71
|
+
selected === lastSelected ||
|
|
53
72
|
!componentRef.children.length
|
|
54
73
|
)
|
|
55
74
|
return;
|
|
@@ -58,8 +77,8 @@ export function withScrolling(isRow: boolean) {
|
|
|
58
77
|
componentRef._initialPosition = componentRef[axis];
|
|
59
78
|
}
|
|
60
79
|
|
|
61
|
-
const lng = componentRef.lng as INode;
|
|
62
|
-
const screenSize = isRow ? lng.stage.root.
|
|
80
|
+
const lng = componentRef.lng as unknown as INode;
|
|
81
|
+
const screenSize = isRow ? lng.stage.root.w : lng.stage.root.h;
|
|
63
82
|
// Determine if movement is incremental or decremental
|
|
64
83
|
const isIncrementing =
|
|
65
84
|
lastSelected === undefined || lastSelected - 1 !== selected;
|
|
@@ -68,6 +87,7 @@ export function withScrolling(isRow: boolean) {
|
|
|
68
87
|
if (componentRef.parent!.clipping) {
|
|
69
88
|
const p = componentRef.parent!;
|
|
70
89
|
componentRef.endOffset =
|
|
90
|
+
componentRef.endOffset ??
|
|
71
91
|
screenSize - ((isRow ? p.absX : p.absY) || 0) - p[dimension];
|
|
72
92
|
}
|
|
73
93
|
|
|
@@ -78,14 +98,20 @@ export function withScrolling(isRow: boolean) {
|
|
|
78
98
|
|
|
79
99
|
const screenOffset = componentRef._screenOffset;
|
|
80
100
|
const gap = componentRef.gap || 0;
|
|
81
|
-
|
|
101
|
+
// when creating we set scroll to always so we setup the right location for selected and scrollIndex
|
|
102
|
+
const scroll =
|
|
103
|
+
componentRef.scroll ||
|
|
104
|
+
(lastSelected === undefined
|
|
105
|
+
? componentRef.scrollIndex
|
|
106
|
+
? 'center'
|
|
107
|
+
: 'always'
|
|
108
|
+
: 'auto');
|
|
82
109
|
|
|
83
110
|
// Allows manual position control
|
|
84
111
|
const targetPosition = componentRef._targetPosition ?? componentRef[axis];
|
|
85
|
-
const rootPosition =
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
: Math.max(targetPosition, componentRef[axis]);
|
|
112
|
+
const rootPosition = isIncrementing
|
|
113
|
+
? Math.min(targetPosition, componentRef[axis])
|
|
114
|
+
: Math.max(targetPosition, componentRef[axis]);
|
|
89
115
|
componentRef.offset = componentRef.offset ?? rootPosition;
|
|
90
116
|
const offset = componentRef.offset;
|
|
91
117
|
selectedElement =
|
|
@@ -107,7 +133,7 @@ export function withScrolling(isRow: boolean) {
|
|
|
107
133
|
screenSize -
|
|
108
134
|
containerSize -
|
|
109
135
|
screenOffset -
|
|
110
|
-
(componentRef.endOffset
|
|
136
|
+
(componentRef.endOffset ?? 2 * gap),
|
|
111
137
|
offset,
|
|
112
138
|
);
|
|
113
139
|
|
|
@@ -123,6 +149,35 @@ export function withScrolling(isRow: boolean) {
|
|
|
123
149
|
nextPosition = -selectedPosition + (screenSize - selectedSizeScaled) / 2;
|
|
124
150
|
} else if (scroll === 'always') {
|
|
125
151
|
nextPosition = -selectedPosition + offset;
|
|
152
|
+
} else if (scroll === 'bounded') {
|
|
153
|
+
const totalItems = componentRef.children.length;
|
|
154
|
+
const upCount = componentRef.upCount || 6;
|
|
155
|
+
const nonScrollableZoneStart = Math.max(0, totalItems - upCount);
|
|
156
|
+
const isInNonScrollableZone = selected >= nonScrollableZoneStart;
|
|
157
|
+
const isFirstOfNonScrollableZone = selected === nonScrollableZoneStart;
|
|
158
|
+
const isEnteringZone =
|
|
159
|
+
isFirstOfNonScrollableZone &&
|
|
160
|
+
lastSelected !== undefined &&
|
|
161
|
+
lastSelected < nonScrollableZoneStart;
|
|
162
|
+
|
|
163
|
+
if (!isInNonScrollableZone) {
|
|
164
|
+
nextPosition = -selectedPosition + offset;
|
|
165
|
+
} else if (isIncrementing) {
|
|
166
|
+
if (isEnteringZone) {
|
|
167
|
+
const firstOfZoneElement =
|
|
168
|
+
componentRef.children[nonScrollableZoneStart];
|
|
169
|
+
const firstOfZonePosition = firstOfZoneElement?.[axis] ?? 0;
|
|
170
|
+
nextPosition = firstOfZoneElement
|
|
171
|
+
? -firstOfZonePosition + offset
|
|
172
|
+
: rootPosition;
|
|
173
|
+
} else {
|
|
174
|
+
nextPosition = rootPosition;
|
|
175
|
+
}
|
|
176
|
+
} else if (isFirstOfNonScrollableZone) {
|
|
177
|
+
nextPosition = -selectedPosition + offset;
|
|
178
|
+
} else {
|
|
179
|
+
nextPosition = rootPosition;
|
|
180
|
+
}
|
|
126
181
|
} else if (scroll === 'center') {
|
|
127
182
|
const centerPosition =
|
|
128
183
|
-selectedPosition +
|
|
@@ -148,7 +203,7 @@ export function withScrolling(isRow: boolean) {
|
|
|
148
203
|
nextPosition = rootPosition + selectedSize + gap;
|
|
149
204
|
}
|
|
150
205
|
} else if (isIncrementing) {
|
|
151
|
-
nextPosition = -
|
|
206
|
+
nextPosition = rootPosition - selectedSize - gap;
|
|
152
207
|
} else {
|
|
153
208
|
nextPosition = rootPosition + selectedSize + gap;
|
|
154
209
|
}
|
|
@@ -161,7 +216,7 @@ export function withScrolling(isRow: boolean) {
|
|
|
161
216
|
|
|
162
217
|
// Prevent container from moving beyond bounds
|
|
163
218
|
nextPosition =
|
|
164
|
-
isIncrementing && scroll !== 'always'
|
|
219
|
+
isIncrementing && scroll !== 'always' && scroll !== 'bounded'
|
|
165
220
|
? Math.max(nextPosition, maxOffset)
|
|
166
221
|
: Math.min(nextPosition, offset);
|
|
167
222
|
|
|
@@ -178,3 +233,6 @@ export function withScrolling(isRow: boolean) {
|
|
|
178
233
|
}
|
|
179
234
|
};
|
|
180
235
|
}
|
|
236
|
+
|
|
237
|
+
export const scrollRow = /* @__PURE__ */ withScrolling(true);
|
|
238
|
+
export const scrollColumn = /* @__PURE__ */ withScrolling(false);
|
package/src/render.ts
CHANGED
|
@@ -5,8 +5,7 @@ import {
|
|
|
5
5
|
type TextProps,
|
|
6
6
|
startLightningRenderer,
|
|
7
7
|
type RendererMainSettings,
|
|
8
|
-
|
|
9
|
-
} from '@lightningtv/core';
|
|
8
|
+
} from './core/index.js';
|
|
10
9
|
import nodeOpts from './solidOpts.js';
|
|
11
10
|
import {
|
|
12
11
|
splitProps,
|
|
@@ -22,7 +21,7 @@ import { activeElement, setActiveElement } from './activeElement.js';
|
|
|
22
21
|
|
|
23
22
|
const solidRenderer = solidCreateRenderer<SolidNode>(nodeOpts);
|
|
24
23
|
|
|
25
|
-
let renderer
|
|
24
|
+
let renderer;
|
|
26
25
|
export const rootNode = nodeOpts.createElement('App');
|
|
27
26
|
|
|
28
27
|
const render = function (code: () => JSXElement) {
|
|
@@ -157,3 +156,8 @@ export const Text = (props: TextProps) => {
|
|
|
157
156
|
spread(el, props, false);
|
|
158
157
|
return el as unknown as JSXElement;
|
|
159
158
|
};
|
|
159
|
+
|
|
160
|
+
export function registerDefaultShader(name: string, shader: any) {
|
|
161
|
+
// noop for v2
|
|
162
|
+
// renderer.stage.shManager.registerShaderType('rounded', Rounded);
|
|
163
|
+
}
|