@lightningtv/solid 2.7.19 → 2.8.0
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/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/primitives/Column.jsx +2 -2
- package/dist/src/primitives/Column.jsx.map +1 -1
- package/dist/src/primitives/FPSCounter.jsx +60 -61
- package/dist/src/primitives/FPSCounter.jsx.map +1 -1
- package/dist/src/primitives/Grid.d.ts +15 -6
- package/dist/src/primitives/Grid.jsx +36 -23
- package/dist/src/primitives/Grid.jsx.map +1 -1
- package/dist/src/primitives/Lazy.jsx +1 -1
- package/dist/src/primitives/Lazy.jsx.map +1 -1
- package/dist/src/primitives/Marquee.d.ts +2 -3
- package/dist/src/primitives/Row.jsx +2 -2
- package/dist/src/primitives/Row.jsx.map +1 -1
- package/dist/src/primitives/index.d.ts +2 -2
- package/dist/src/primitives/index.js +2 -2
- package/dist/src/primitives/index.js.map +1 -1
- package/dist/src/primitives/utils/chainFunctions.d.ts +30 -4
- package/dist/src/primitives/utils/chainFunctions.js +14 -3
- package/dist/src/primitives/utils/chainFunctions.js.map +1 -1
- package/dist/src/primitives/utils/createSpriteMap.js.map +1 -1
- package/dist/src/primitives/utils/withScrolling.js +1 -1
- package/dist/src/primitives/utils/withScrolling.js.map +1 -1
- package/dist/src/render.d.ts +5 -5
- package/dist/src/render.js +8 -6
- package/dist/src/render.js.map +1 -1
- package/dist/src/solidOpts.d.ts +6 -0
- package/dist/src/solidOpts.js +31 -7
- package/dist/src/solidOpts.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/{src/jsx-runtime.ts → jsx-runtime.d.ts} +2 -0
- package/package.json +21 -6
- package/src/devtools/index.ts +77 -0
- package/src/index.ts +1 -1
- package/src/primitives/Column.tsx +2 -2
- package/src/primitives/FPSCounter.tsx +61 -61
- package/src/primitives/FadeInOut.tsx +34 -0
- package/src/primitives/Grid.tsx +59 -35
- package/src/primitives/Lazy.tsx +1 -1
- package/src/primitives/Row.tsx +1 -1
- package/src/primitives/index.ts +6 -2
- package/src/primitives/utils/chainFunctions.ts +40 -9
- package/src/primitives/utils/createSpriteMap.ts +1 -1
- package/src/primitives/utils/withScrolling.ts +4 -3
- package/src/render.ts +13 -12
- package/src/solidOpts.ts +41 -7
- package/src/primitives/LazyUp.tsx +0 -72
- package/src/primitives/jsx-runtime.d.ts +0 -8
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import * as debug from '@solid-devtools/debugger/types';
|
|
2
|
+
import * as lng from '@lightningtv/core';
|
|
3
|
+
|
|
4
|
+
const EMPTY_CHILDREN: (lng.ElementNode | lng.ElementText)[] = [];
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Implementation of the solid-devtools element interface for Lightning elements
|
|
8
|
+
*/
|
|
9
|
+
export const elementInterface: debug.ElementInterface<
|
|
10
|
+
lng.ElementNode | lng.ElementText
|
|
11
|
+
> = {
|
|
12
|
+
isElement(node): node is lng.ElementNode | lng.ElementText {
|
|
13
|
+
return (
|
|
14
|
+
'_type' in node &&
|
|
15
|
+
(node._type === lng.NodeType.Element ||
|
|
16
|
+
node._type === lng.NodeType.TextNode)
|
|
17
|
+
);
|
|
18
|
+
},
|
|
19
|
+
getChildren(node) {
|
|
20
|
+
return node instanceof lng.ElementNode ? node.children : EMPTY_CHILDREN;
|
|
21
|
+
},
|
|
22
|
+
getName(node) {
|
|
23
|
+
return node._type === lng.NodeType.Element ? 'view' : 'text';
|
|
24
|
+
},
|
|
25
|
+
getParent(node) {
|
|
26
|
+
return node.parent ?? null;
|
|
27
|
+
},
|
|
28
|
+
getRect(node) {
|
|
29
|
+
let { width, height } = node;
|
|
30
|
+
let x = 0,
|
|
31
|
+
y = 0;
|
|
32
|
+
|
|
33
|
+
if (node.scaleX != null) width *= node.scaleX;
|
|
34
|
+
if (node.scaleY != null) height *= node.scaleY;
|
|
35
|
+
|
|
36
|
+
let curr = node as lng.ElementNode | undefined | null;
|
|
37
|
+
while (curr != null) {
|
|
38
|
+
x += curr.x;
|
|
39
|
+
y += curr.y;
|
|
40
|
+
|
|
41
|
+
if (curr.scaleX != null) {
|
|
42
|
+
x += (curr.width / 2) * (1 - curr.scaleX);
|
|
43
|
+
}
|
|
44
|
+
if (curr.scaleY != null) {
|
|
45
|
+
y += (curr.height / 2) * (1 - curr.scaleY);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
curr = curr.parent;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (lng.Config.rendererOptions != null) {
|
|
52
|
+
let dpr = lng.Config.rendererOptions.deviceLogicalPixelRatio;
|
|
53
|
+
if (dpr != null) {
|
|
54
|
+
x *= dpr;
|
|
55
|
+
y *= dpr;
|
|
56
|
+
width *= dpr;
|
|
57
|
+
height *= dpr;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return { x, y, width, height };
|
|
62
|
+
},
|
|
63
|
+
getElementAt(e) {
|
|
64
|
+
let target = e.target as any;
|
|
65
|
+
return target != null && target.element instanceof lng.ElementNode
|
|
66
|
+
? target.element
|
|
67
|
+
: null;
|
|
68
|
+
},
|
|
69
|
+
getLocation(node) {
|
|
70
|
+
if (typeof node[debug.LOCATION_ATTRIBUTE_NAME] === 'string') {
|
|
71
|
+
return (
|
|
72
|
+
debug.parseLocationString(node[debug.LOCATION_ATTRIBUTE_NAME]) ?? null
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
},
|
|
77
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Component } from 'solid-js';
|
|
2
|
-
import { ElementNode,
|
|
2
|
+
import { ElementNode, combineStyles, type NodeStyles } from '@lightningtv/solid';
|
|
3
3
|
import {
|
|
4
4
|
handleNavigation,
|
|
5
5
|
onGridFocus,
|
|
@@ -32,7 +32,7 @@ function scrollToIndex(this: ElementNode, index: number) {
|
|
|
32
32
|
|
|
33
33
|
export const Column: Component<ColumnProps> = (props) => {
|
|
34
34
|
return (
|
|
35
|
-
<
|
|
35
|
+
<view
|
|
36
36
|
{...props}
|
|
37
37
|
onUp={/* @once */ chainFunctions(props.onUp, onUp)}
|
|
38
38
|
onDown={/* @once */ chainFunctions(props.onDown, onDown)}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type Stage, type NodeProps, RendererMain } from '@lightningtv/solid';
|
|
2
2
|
import { createSignal } from 'solid-js';
|
|
3
3
|
|
|
4
4
|
const fpsStyle = {
|
|
@@ -90,90 +90,90 @@ export function setupFPS(root: any) {
|
|
|
90
90
|
|
|
91
91
|
export const FPSCounter = (props: NodeProps) => {
|
|
92
92
|
return (
|
|
93
|
-
<
|
|
94
|
-
<
|
|
95
|
-
<
|
|
96
|
-
<
|
|
93
|
+
<view {...props} style={fpsStyle}>
|
|
94
|
+
<view y={6}>
|
|
95
|
+
<text style={fpsLabel}>FPS:</text>
|
|
96
|
+
<text style={fpsValue} x={90}>
|
|
97
97
|
{fps().toString()}
|
|
98
|
-
</
|
|
99
|
-
</
|
|
98
|
+
</text>
|
|
99
|
+
</view>
|
|
100
100
|
|
|
101
|
-
<
|
|
102
|
-
<
|
|
103
|
-
<
|
|
101
|
+
<view y={6} x={160}>
|
|
102
|
+
<text style={fpsLabel}>AVG:</text>
|
|
103
|
+
<text style={fpsValue} x={100}>
|
|
104
104
|
{avgFps().toString()}
|
|
105
|
-
</
|
|
106
|
-
</
|
|
105
|
+
</text>
|
|
106
|
+
</view>
|
|
107
107
|
|
|
108
|
-
<
|
|
109
|
-
<
|
|
110
|
-
<
|
|
108
|
+
<view x={0} y={26}>
|
|
109
|
+
<text style={fpsLabel}>MIN:</text>
|
|
110
|
+
<text style={fpsValue} x={90}>
|
|
111
111
|
{minFps().toString()}
|
|
112
|
-
</
|
|
113
|
-
</
|
|
112
|
+
</text>
|
|
113
|
+
</view>
|
|
114
114
|
|
|
115
|
-
<
|
|
116
|
-
<
|
|
117
|
-
<
|
|
115
|
+
<view x={160} y={26}>
|
|
116
|
+
<text style={fpsLabel}>MAX:</text>
|
|
117
|
+
<text style={fpsValue} x={100}>
|
|
118
118
|
{maxFps().toString()}
|
|
119
|
-
</
|
|
120
|
-
</
|
|
119
|
+
</text>
|
|
120
|
+
</view>
|
|
121
121
|
|
|
122
|
-
<
|
|
123
|
-
<
|
|
124
|
-
<
|
|
122
|
+
<view display="flex" flexDirection="column" y={58} gap={4}>
|
|
123
|
+
<view height={infoFontSize}>
|
|
124
|
+
<text fontSize={infoFontSize} style={fpsLabel}>
|
|
125
125
|
criticalThreshold:
|
|
126
|
-
</
|
|
127
|
-
<
|
|
126
|
+
</text>
|
|
127
|
+
<text fontSize={infoFontSize} style={fpsLabel} x={230}>
|
|
128
128
|
{criticalThresholdSignal()}
|
|
129
|
-
</
|
|
130
|
-
</
|
|
129
|
+
</text>
|
|
130
|
+
</view>
|
|
131
131
|
|
|
132
|
-
<
|
|
133
|
-
<
|
|
132
|
+
<view height={infoFontSize}>
|
|
133
|
+
<text fontSize={infoFontSize} style={fpsLabel}>
|
|
134
134
|
targetThreshold:
|
|
135
|
-
</
|
|
136
|
-
<
|
|
135
|
+
</text>
|
|
136
|
+
<text fontSize={infoFontSize} style={fpsLabel} x={230}>
|
|
137
137
|
{targetThresholdSignal()}
|
|
138
|
-
</
|
|
139
|
-
</
|
|
138
|
+
</text>
|
|
139
|
+
</view>
|
|
140
140
|
|
|
141
|
-
<
|
|
142
|
-
<
|
|
141
|
+
<view height={infoFontSize}>
|
|
142
|
+
<text fontSize={infoFontSize} style={fpsLabel}>
|
|
143
143
|
renderableMemUsed:
|
|
144
|
-
</
|
|
145
|
-
<
|
|
144
|
+
</text>
|
|
145
|
+
<text fontSize={infoFontSize} style={fpsLabel} x={230}>
|
|
146
146
|
{renderableMemUsedSignal()}
|
|
147
|
-
</
|
|
148
|
-
</
|
|
147
|
+
</text>
|
|
148
|
+
</view>
|
|
149
149
|
|
|
150
|
-
<
|
|
151
|
-
<
|
|
150
|
+
<view height={infoFontSize}>
|
|
151
|
+
<text fontSize={infoFontSize} style={fpsLabel}>
|
|
152
152
|
memUsed:
|
|
153
|
-
</
|
|
154
|
-
<
|
|
153
|
+
</text>
|
|
154
|
+
<text fontSize={infoFontSize} style={fpsLabel} x={230}>
|
|
155
155
|
{memUsedSignal()}
|
|
156
|
-
</
|
|
157
|
-
</
|
|
156
|
+
</text>
|
|
157
|
+
</view>
|
|
158
158
|
|
|
159
|
-
<
|
|
160
|
-
<
|
|
159
|
+
<view height={infoFontSize}>
|
|
160
|
+
<text fontSize={infoFontSize} style={fpsLabel}>
|
|
161
161
|
renderableTexturesLoaded:
|
|
162
|
-
</
|
|
163
|
-
<
|
|
162
|
+
</text>
|
|
163
|
+
<text fontSize={infoFontSize} style={fpsLabel} x={230}>
|
|
164
164
|
{renderableTexturesLoadedSignal().toString()}
|
|
165
|
-
</
|
|
166
|
-
</
|
|
165
|
+
</text>
|
|
166
|
+
</view>
|
|
167
167
|
|
|
168
|
-
<
|
|
169
|
-
<
|
|
168
|
+
<view height={infoFontSize}>
|
|
169
|
+
<text fontSize={infoFontSize} style={fpsLabel}>
|
|
170
170
|
loadedTextures:
|
|
171
|
-
</
|
|
172
|
-
<
|
|
171
|
+
</text>
|
|
172
|
+
<text fontSize={infoFontSize} style={fpsLabel} x={230}>
|
|
173
173
|
{loadedTexturesSignal().toString()}
|
|
174
|
-
</
|
|
175
|
-
</
|
|
176
|
-
</
|
|
177
|
-
</
|
|
174
|
+
</text>
|
|
175
|
+
</view>
|
|
176
|
+
</view>
|
|
177
|
+
</view>
|
|
178
178
|
);
|
|
179
179
|
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ElementNode, NodeProps, View } from '@lightningtv/solid';
|
|
2
|
+
import { Show } from 'solid-js';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
transition?: {
|
|
6
|
+
duration?: number;
|
|
7
|
+
easing?: string;
|
|
8
|
+
};
|
|
9
|
+
when?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const DEFAULT_PROPS = {
|
|
13
|
+
duration: 250,
|
|
14
|
+
easing: 'ease-in-out',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export default function FadeInOut(props: Props & NodeProps) {
|
|
18
|
+
const config = Object.assign({}, DEFAULT_PROPS, props.transition);
|
|
19
|
+
function onCreate(elm: ElementNode) {
|
|
20
|
+
elm.alpha = 0;
|
|
21
|
+
elm.animate({ alpha: 1 }, { duration: config.duration, easing: config.easing }).start();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function onDestroy(elm: ElementNode) {
|
|
25
|
+
elm.rtt = true;
|
|
26
|
+
return elm.animate({ alpha: 0 }, { duration: config.duration, easing: config.easing })
|
|
27
|
+
.start().waitUntilStopped();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<Show when={props.when} keyed>
|
|
32
|
+
<View {...props} onDestroy={onDestroy} onCreate={onCreate} />
|
|
33
|
+
</Show>);
|
|
34
|
+
}
|
package/src/primitives/Grid.tsx
CHANGED
|
@@ -1,25 +1,51 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { For, createSignal, createMemo, createEffect, JSX } from "solid-js";
|
|
2
|
+
import { type NodeProps, ElementNode, NewOmit } from "@lightningtv/solid";
|
|
3
|
+
import { chainRefs } from "./utils/chainFunctions.js";
|
|
3
4
|
|
|
4
|
-
export
|
|
5
|
-
item:
|
|
5
|
+
export interface GridItemProps<T> {
|
|
6
|
+
item: T
|
|
7
|
+
index: number
|
|
8
|
+
width: number
|
|
9
|
+
height: number
|
|
10
|
+
x: number
|
|
11
|
+
y: number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface GridProps<T> extends NewOmit<NodeProps, 'children'> {
|
|
15
|
+
items: readonly T[];
|
|
16
|
+
children: (props: GridItemProps<T>) => JSX.Element,
|
|
6
17
|
itemHeight?: number;
|
|
7
18
|
itemWidth?: number;
|
|
8
19
|
itemOffset?: number;
|
|
9
|
-
items: T[];
|
|
10
20
|
columns?: number;
|
|
11
21
|
looping?: boolean;
|
|
12
22
|
scroll?: "auto" | "none";
|
|
13
23
|
onSelectedChanged?: (index: number, grid: ElementNode, elm?: ElementNode) => void;
|
|
14
|
-
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function Grid<T>(props: GridProps<T>): JSX.Element {
|
|
27
|
+
|
|
15
28
|
const [focusedIndex, setFocusedIndex] = createSignal(0);
|
|
16
29
|
const baseColumns = 4;
|
|
17
30
|
|
|
31
|
+
const itemWidth = () => props.itemWidth ?? 300
|
|
32
|
+
const itemHeight = () => props.itemHeight ?? 300
|
|
33
|
+
|
|
18
34
|
const columns = createMemo(() => props.columns || baseColumns);
|
|
19
|
-
const totalWidth = createMemo(() => (
|
|
20
|
-
const totalHeight = createMemo(() => (
|
|
35
|
+
const totalWidth = createMemo(() => itemWidth() + (props.itemOffset ?? 0));
|
|
36
|
+
const totalHeight = createMemo(() => itemHeight() + (props.itemOffset ?? 0));
|
|
37
|
+
|
|
38
|
+
function focus() {
|
|
39
|
+
const focusedElm = gridRef.children[focusedIndex()];
|
|
40
|
+
if (focusedElm instanceof ElementNode && !focusedElm.states.has('$focus')) {
|
|
41
|
+
focusedElm.setFocus();
|
|
42
|
+
props.onSelectedChanged?.call(gridRef, focusedIndex(), gridRef, focusedElm);
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
21
47
|
|
|
22
|
-
|
|
48
|
+
function moveFocus(delta: number) {
|
|
23
49
|
if (!props.items || props.items.length === 0) return false;
|
|
24
50
|
const newIndex = focusedIndex() + delta;
|
|
25
51
|
|
|
@@ -37,13 +63,10 @@ export const Grid = <T,>(props: {
|
|
|
37
63
|
} else {
|
|
38
64
|
return false;
|
|
39
65
|
}
|
|
40
|
-
|
|
41
|
-
focusedElm.setFocus();
|
|
42
|
-
isFunction(props.onSelectedChanged) && props.onSelectedChanged.call(elm, focusedIndex(), elm, focusedElm);
|
|
43
|
-
return true;
|
|
66
|
+
return focus();
|
|
44
67
|
};
|
|
45
68
|
|
|
46
|
-
|
|
69
|
+
function handleHorizontalFocus(delta: number) {
|
|
47
70
|
if (!props.items || props.items.length === 0) return false;
|
|
48
71
|
const newIndex = focusedIndex() + delta;
|
|
49
72
|
const isWithinRow = Math.floor(newIndex / columns()) === Math.floor(focusedIndex() / columns());
|
|
@@ -57,45 +80,46 @@ export const Grid = <T,>(props: {
|
|
|
57
80
|
} else {
|
|
58
81
|
return false;
|
|
59
82
|
}
|
|
60
|
-
|
|
61
|
-
focusedElm.setFocus();
|
|
62
|
-
isFunction(props.onSelectedChanged) && props.onSelectedChanged.call(elm, focusedIndex(), elm, focusedElm);
|
|
63
|
-
return true;
|
|
83
|
+
return focus();
|
|
64
84
|
};
|
|
65
85
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
86
|
+
// Handle focus when items change - important for autofocus
|
|
87
|
+
createEffect(() => {
|
|
88
|
+
if (props.items && props.items.length > 0 && gridRef && gridRef.states.has('$focus')) {
|
|
89
|
+
queueMicrotask(focus)
|
|
90
|
+
}
|
|
91
|
+
})
|
|
69
92
|
|
|
70
93
|
const scrollY = createMemo(() =>
|
|
71
94
|
props.scroll === "none" ? props.y ?? 0 : -Math.floor(focusedIndex() / columns()) * totalHeight() + (props.y || 0)
|
|
72
95
|
);
|
|
73
96
|
|
|
97
|
+
let gridRef!: ElementNode;
|
|
74
98
|
return (
|
|
75
|
-
<
|
|
76
|
-
transition={{ y: true }}
|
|
99
|
+
<view
|
|
77
100
|
{...props}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
101
|
+
ref={chainRefs(el => gridRef = el, props.ref)}
|
|
102
|
+
transition={{ y: true }}
|
|
103
|
+
onUp={() => moveFocus(-columns())}
|
|
104
|
+
onDown={() => moveFocus(columns())}
|
|
105
|
+
onLeft={() => handleHorizontalFocus(-1)}
|
|
106
|
+
onRight={() => handleHorizontalFocus(1)}
|
|
107
|
+
onFocus={() => handleHorizontalFocus(0)}
|
|
83
108
|
strictBounds={false}
|
|
84
109
|
y={scrollY()}
|
|
85
110
|
>
|
|
86
111
|
<For each={props.items}>
|
|
87
112
|
{(item, index) => (
|
|
88
|
-
<
|
|
89
|
-
{
|
|
90
|
-
|
|
91
|
-
width={
|
|
92
|
-
height={
|
|
113
|
+
<props.children
|
|
114
|
+
item={item}
|
|
115
|
+
index={index()}
|
|
116
|
+
width={itemWidth()}
|
|
117
|
+
height={itemHeight()}
|
|
93
118
|
x={(index() % columns()) * totalWidth()}
|
|
94
119
|
y={Math.floor(index() / columns()) * totalHeight()}
|
|
95
120
|
/>
|
|
96
121
|
)}
|
|
97
122
|
</For>
|
|
98
|
-
</
|
|
123
|
+
</view>
|
|
99
124
|
);
|
|
100
125
|
};
|
|
101
|
-
|
package/src/primitives/Lazy.tsx
CHANGED
package/src/primitives/Row.tsx
CHANGED
package/src/primitives/index.ts
CHANGED
|
@@ -3,7 +3,6 @@ export * from './announcer/index.js';
|
|
|
3
3
|
export * from './createInfiniteItems.js';
|
|
4
4
|
export * from './useMouse.js';
|
|
5
5
|
export * from './portal.jsx';
|
|
6
|
-
export * from './LazyUp.jsx';
|
|
7
6
|
export * from './Lazy.jsx';
|
|
8
7
|
export * from './Visible.jsx';
|
|
9
8
|
export * from './router.js';
|
|
@@ -11,11 +10,16 @@ export * from './Column.jsx';
|
|
|
11
10
|
export * from './Row.jsx';
|
|
12
11
|
export * from './Grid.jsx';
|
|
13
12
|
export * from './FPSCounter.jsx';
|
|
13
|
+
export * from './FadeInOut.jsx';
|
|
14
14
|
export * from './createFocusStack.jsx';
|
|
15
15
|
export * from './marquee.jsx';
|
|
16
16
|
export * from './useHold.js';
|
|
17
17
|
export { withScrolling } from './utils/withScrolling.js';
|
|
18
|
-
export {
|
|
18
|
+
export {
|
|
19
|
+
type AnyFunction,
|
|
20
|
+
chainFunctions,
|
|
21
|
+
chainRefs,
|
|
22
|
+
} from './utils/chainFunctions.js';
|
|
19
23
|
export { handleNavigation, onGridFocus } from './utils/handleNavigation.js';
|
|
20
24
|
export { createSpriteMap, type SpriteDef } from './utils/createSpriteMap.js';
|
|
21
25
|
|
|
@@ -1,13 +1,29 @@
|
|
|
1
|
-
|
|
1
|
+
import * as s from 'solid-js';
|
|
2
2
|
|
|
3
|
-
export
|
|
4
|
-
export function chainFunctions<T>(...args: (ChainableFunction | T)[]): T;
|
|
3
|
+
export type AnyFunction = (this: any, ...args: any[]) => any;
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
/**
|
|
6
|
+
* take an array of functions and if you return `true` from a function, it will stop the chain
|
|
7
|
+
* @param fns list of functions to chain together, can be `undefined`, `null`, or `false` to skip them
|
|
8
|
+
* @returns a function that will call each function in the list until one returns `true` or all functions are called.
|
|
9
|
+
* If no functions are provided, it will return `undefined`.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```tsx
|
|
13
|
+
* function Button (props: NodeProps) {
|
|
14
|
+
* function onEnter (el: ElementNode) {...}
|
|
15
|
+
* return <view onEnter={chainFunctions(props.onEnter, onEnter)} />
|
|
16
|
+
* }
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export function chainFunctions<T extends AnyFunction>(...fns: T[]): T;
|
|
20
|
+
export function chainFunctions<T extends AnyFunction>(
|
|
21
|
+
...fns: (T | undefined | null | false)[]
|
|
22
|
+
): T | undefined;
|
|
23
|
+
export function chainFunctions(
|
|
24
|
+
...fns: (AnyFunction | undefined | null | false)[]
|
|
25
|
+
): AnyFunction | undefined {
|
|
26
|
+
const onlyFunctions = fns.filter((func) => typeof func === 'function');
|
|
11
27
|
if (onlyFunctions.length === 0) {
|
|
12
28
|
return undefined;
|
|
13
29
|
}
|
|
@@ -16,7 +32,7 @@ export function chainFunctions<T extends ChainableFunction>(
|
|
|
16
32
|
return onlyFunctions[0];
|
|
17
33
|
}
|
|
18
34
|
|
|
19
|
-
return function (
|
|
35
|
+
return function (...innerArgs) {
|
|
20
36
|
let result;
|
|
21
37
|
for (const func of onlyFunctions) {
|
|
22
38
|
result = func.apply(this, innerArgs);
|
|
@@ -27,3 +43,18 @@ export function chainFunctions<T extends ChainableFunction>(
|
|
|
27
43
|
return result;
|
|
28
44
|
};
|
|
29
45
|
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Utility for chaining multiple `ref` assignments with `props.ref` forwarding.
|
|
49
|
+
* @param refs list of ref setters. Can be a `props.ref` prop for ref forwarding or a setter to a local variable (`el => ref = el`).
|
|
50
|
+
* @example
|
|
51
|
+
* ```tsx
|
|
52
|
+
* function Button (props: NodeProps) {
|
|
53
|
+
* let localRef: ElementNode | undefined
|
|
54
|
+
* return <view ref={chainRefs(props.ref, el => localRef = el)} />
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export const chainRefs = chainFunctions as <T>(
|
|
59
|
+
...refs: (s.Ref<T> | undefined)[]
|
|
60
|
+
) => (el: T) => void;
|
|
@@ -82,9 +82,10 @@ export function withScrolling(isRow: boolean) {
|
|
|
82
82
|
|
|
83
83
|
// Allows manual position control
|
|
84
84
|
const targetPosition = componentRef._targetPosition ?? componentRef[axis];
|
|
85
|
-
const rootPosition =
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
const rootPosition =
|
|
86
|
+
isIncrementing || scroll === 'auto'
|
|
87
|
+
? Math.min(targetPosition, componentRef[axis])
|
|
88
|
+
: Math.max(targetPosition, componentRef[axis]);
|
|
88
89
|
componentRef.offset = componentRef.offset ?? rootPosition;
|
|
89
90
|
const offset = componentRef.offset;
|
|
90
91
|
selectedElement =
|
package/src/render.ts
CHANGED
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
type NodeProps,
|
|
5
5
|
type TextProps,
|
|
6
6
|
startLightningRenderer,
|
|
7
|
-
type RendererMain,
|
|
8
7
|
type RendererMainSettings,
|
|
8
|
+
type RendererMain as IRendererMain,
|
|
9
9
|
} from '@lightningtv/core';
|
|
10
10
|
import nodeOpts from './solidOpts.js';
|
|
11
11
|
import {
|
|
@@ -14,14 +14,15 @@ import {
|
|
|
14
14
|
createRenderEffect,
|
|
15
15
|
untrack,
|
|
16
16
|
type JSXElement,
|
|
17
|
-
|
|
17
|
+
createRoot,
|
|
18
|
+
type Component,
|
|
18
19
|
} from 'solid-js';
|
|
19
20
|
import type { SolidNode } from './types.js';
|
|
20
21
|
import { activeElement, setActiveElement } from './activeElement.js';
|
|
21
22
|
|
|
22
23
|
const solidRenderer = solidCreateRenderer<SolidNode>(nodeOpts);
|
|
23
24
|
|
|
24
|
-
let renderer:
|
|
25
|
+
let renderer: IRendererMain;
|
|
25
26
|
export const rootNode = nodeOpts.createElement('App');
|
|
26
27
|
|
|
27
28
|
const render = function (code: () => JSXElement) {
|
|
@@ -71,11 +72,13 @@ type Task = () => void;
|
|
|
71
72
|
const taskQueue: Task[] = [];
|
|
72
73
|
let tasksEnabled = false;
|
|
73
74
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
createRoot(() => {
|
|
76
|
+
createRenderEffect(() => {
|
|
77
|
+
// should change whenever a keypress occurs, so we disable the task queue
|
|
78
|
+
// until the renderer is idle again.
|
|
79
|
+
activeElement();
|
|
80
|
+
tasksEnabled = false;
|
|
81
|
+
});
|
|
79
82
|
});
|
|
80
83
|
|
|
81
84
|
export function setTasksEnabled(enabled: boolean): void {
|
|
@@ -117,10 +120,8 @@ function processTasks(): void {
|
|
|
117
120
|
* ```
|
|
118
121
|
* @description https://www.solidjs.com/docs/latest/api#dynamic
|
|
119
122
|
*/
|
|
120
|
-
export function Dynamic<T
|
|
121
|
-
props: T & {
|
|
122
|
-
component?: ValidComponent;
|
|
123
|
-
},
|
|
123
|
+
export function Dynamic<T extends Record<string, any>>(
|
|
124
|
+
props: T & { component?: Component<T> | undefined | null },
|
|
124
125
|
): JSXElement {
|
|
125
126
|
const [p, others] = splitProps(props, ['component']);
|
|
126
127
|
|