@lightningtv/solid 3.0.0-1 → 3.0.0-3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/devtools/index.d.ts +6 -0
- package/dist/src/devtools/index.js +65 -0
- package/dist/src/devtools/index.js.map +1 -0
- 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/jsx-runtime.d.ts +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 +11 -8
- package/dist/src/primitives/Grid.jsx.map +1 -1
- package/dist/src/primitives/Lazy.jsx +11 -4
- package/dist/src/primitives/Lazy.jsx.map +1 -1
- package/dist/src/primitives/LazyUp.jsx +1 -0
- package/dist/src/primitives/LazyUp.jsx.map +1 -1
- package/dist/src/primitives/Row.jsx +2 -2
- package/dist/src/primitives/Row.jsx.map +1 -1
- package/dist/src/primitives/utils/createSpriteMap.d.ts +2 -2
- package/dist/src/primitives/utils/createSpriteMap.js.map +1 -1
- package/dist/src/primitives/utils/handleNavigation.js +3 -2
- package/dist/src/primitives/utils/handleNavigation.js.map +1 -1
- package/dist/src/render.d.ts +2 -2
- 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 +27 -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 -3
- package/package.json +18 -5
- 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/Grid.tsx +30 -16
- package/src/primitives/Lazy.tsx +14 -5
- package/src/primitives/Row.tsx +1 -1
- package/src/primitives/utils/createSpriteMap.ts +3 -5
- package/src/primitives/utils/handleNavigation.ts +2 -2
- package/src/render.ts +10 -7
- package/src/solidOpts.ts +38 -7
- package/src/primitives/jsx-runtime.d.ts +0 -8
|
@@ -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
|
};
|
package/src/primitives/Grid.tsx
CHANGED
|
@@ -1,23 +1,38 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { For, createSignal, createMemo, JSX } from "solid-js";
|
|
2
|
+
import { type NodeProps, type ElementNode, isFunction, NewOmit } from "@lightningtv/solid";
|
|
3
3
|
|
|
4
|
-
export
|
|
5
|
-
item:
|
|
4
|
+
export interface GridItemProps<T> {
|
|
5
|
+
item: T
|
|
6
|
+
index: number
|
|
7
|
+
width: number
|
|
8
|
+
height: number
|
|
9
|
+
x: number
|
|
10
|
+
y: number
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface GridProps<T> extends NewOmit<NodeProps, 'children'> {
|
|
14
|
+
items: T[];
|
|
15
|
+
children: (props: GridItemProps<T>) => JSX.Element,
|
|
6
16
|
itemHeight?: number;
|
|
7
17
|
itemWidth?: number;
|
|
8
18
|
itemOffset?: number;
|
|
9
|
-
items: T[];
|
|
10
19
|
columns?: number;
|
|
11
20
|
looping?: boolean;
|
|
12
21
|
scroll?: "auto" | "none";
|
|
13
22
|
onSelectedChanged?: (index: number, grid: ElementNode, elm?: ElementNode) => void;
|
|
14
|
-
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function Grid<T>(props: GridProps<T>): JSX.Element {
|
|
26
|
+
|
|
15
27
|
const [focusedIndex, setFocusedIndex] = createSignal(0);
|
|
16
28
|
const baseColumns = 4;
|
|
17
29
|
|
|
30
|
+
const itemWidth = () => props.itemWidth ?? 300
|
|
31
|
+
const itemHeight = () => props.itemHeight ?? 300
|
|
32
|
+
|
|
18
33
|
const columns = createMemo(() => props.columns || baseColumns);
|
|
19
|
-
const totalWidth = createMemo(() => (
|
|
20
|
-
const totalHeight = createMemo(() => (
|
|
34
|
+
const totalWidth = createMemo(() => itemWidth() + (props.itemOffset ?? 0));
|
|
35
|
+
const totalHeight = createMemo(() => itemHeight() + (props.itemOffset ?? 0));
|
|
21
36
|
|
|
22
37
|
const moveFocus = (delta: number, elm: ElementNode) => {
|
|
23
38
|
if (!props.items || props.items.length === 0) return false;
|
|
@@ -72,7 +87,7 @@ export const Grid = <T,>(props: {
|
|
|
72
87
|
);
|
|
73
88
|
|
|
74
89
|
return (
|
|
75
|
-
<
|
|
90
|
+
<view
|
|
76
91
|
transition={{ y: true }}
|
|
77
92
|
{...props}
|
|
78
93
|
onUp={(_e, elm) => moveFocus(-columns(), elm)}
|
|
@@ -85,17 +100,16 @@ export const Grid = <T,>(props: {
|
|
|
85
100
|
>
|
|
86
101
|
<For each={props.items}>
|
|
87
102
|
{(item, index) => (
|
|
88
|
-
<
|
|
89
|
-
{
|
|
90
|
-
|
|
91
|
-
width={
|
|
92
|
-
height={
|
|
103
|
+
<props.children
|
|
104
|
+
item={item}
|
|
105
|
+
index={index()}
|
|
106
|
+
width={itemWidth()}
|
|
107
|
+
height={itemHeight()}
|
|
93
108
|
x={(index() % columns()) * totalWidth()}
|
|
94
109
|
y={Math.floor(index() / columns()) * totalHeight()}
|
|
95
110
|
/>
|
|
96
111
|
)}
|
|
97
112
|
</For>
|
|
98
|
-
</
|
|
113
|
+
</view>
|
|
99
114
|
);
|
|
100
115
|
};
|
|
101
|
-
|
package/src/primitives/Lazy.tsx
CHANGED
|
@@ -31,23 +31,22 @@ function createLazy<T>(
|
|
|
31
31
|
const [offset, setOffset] = createSignal(1);
|
|
32
32
|
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
33
33
|
|
|
34
|
-
createEffect(() => setOffset(props.selected || 1));
|
|
34
|
+
createEffect(() => setOffset((props.selected || 0) + 1));
|
|
35
35
|
|
|
36
36
|
if (props.sync) {
|
|
37
37
|
setOffset(props.upCount);
|
|
38
38
|
} else {
|
|
39
39
|
createEffect(() => {
|
|
40
40
|
if (props.each) {
|
|
41
|
-
let count = untrack(offset);
|
|
42
|
-
|
|
43
41
|
const loadItems = () => {
|
|
42
|
+
let count = untrack(offset);
|
|
44
43
|
if (count < props.upCount) {
|
|
45
44
|
setOffset(count + 1);
|
|
46
45
|
timeoutId = setTimeout(loadItems, 16); // ~60fps
|
|
47
46
|
count++;
|
|
48
47
|
} else if (props.eagerLoad) {
|
|
49
48
|
const maxOffset = props.each ? props.each.length : 0;
|
|
50
|
-
if (offset()
|
|
49
|
+
if (offset() >= maxOffset) return;
|
|
51
50
|
setOffset((prev) => Math.min(prev + 1, maxOffset));
|
|
52
51
|
scheduleTask(loadItems);
|
|
53
52
|
}
|
|
@@ -63,7 +62,17 @@ function createLazy<T>(
|
|
|
63
62
|
const maxOffset = props.each ? props.each.length : 0;
|
|
64
63
|
if (offset() >= maxOffset) return;
|
|
65
64
|
|
|
66
|
-
if (
|
|
65
|
+
if (!props.delay) {
|
|
66
|
+
setOffset((prev) => Math.min(prev + 1, maxOffset));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (timeoutId) {
|
|
71
|
+
clearTimeout(timeoutId);
|
|
72
|
+
//Moving faster than the delay so need to go sync
|
|
73
|
+
setOffset((prev) => Math.min(prev + 1, maxOffset));
|
|
74
|
+
}
|
|
75
|
+
|
|
67
76
|
timeoutId = setTimeout(() => {
|
|
68
77
|
setOffset((prev) => Math.min(prev + 1, maxOffset));
|
|
69
78
|
timeoutId = null;
|
package/src/primitives/Row.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type IRendererTexture, renderer } from '@lightningtv/core';
|
|
2
2
|
|
|
3
3
|
export interface SpriteDef {
|
|
4
4
|
name: string | number;
|
|
@@ -11,14 +11,12 @@ export interface SpriteDef {
|
|
|
11
11
|
export function createSpriteMap(
|
|
12
12
|
src: string,
|
|
13
13
|
subTextures: SpriteDef[],
|
|
14
|
-
): Record<string,
|
|
14
|
+
): Record<string, IRendererTexture> {
|
|
15
15
|
const spriteMapTexture = renderer.createTexture('ImageTexture', {
|
|
16
16
|
src,
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
-
return subTextures.reduce<
|
|
20
|
-
Record<string, InstanceType<TextureMap['SubTexture']>>
|
|
21
|
-
>((acc, t) => {
|
|
19
|
+
return subTextures.reduce<Record<string, IRendererTexture>>((acc, t) => {
|
|
22
20
|
const { x, y, width, height } = t;
|
|
23
21
|
acc[t.name] = renderer.createTexture('SubTexture', {
|
|
24
22
|
texture: spriteMapTexture,
|
|
@@ -73,8 +73,8 @@ export function handleNavigation(
|
|
|
73
73
|
return false;
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
|
-
const active = this.children[this.selected || 0];
|
|
77
|
-
|
|
76
|
+
const active = this.children[this.selected || 0] || this.children[0];
|
|
77
|
+
if (!(active instanceof ElementNode)) return false;
|
|
78
78
|
const navigableThis = this as NavigableElement;
|
|
79
79
|
|
|
80
80
|
navigableThis.onSelectedChanged &&
|
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 IRendererMain,
|
|
9
9
|
} from '@lightningtv/core';
|
|
10
10
|
import nodeOpts from './solidOpts.js';
|
|
11
11
|
import {
|
|
@@ -15,13 +15,14 @@ import {
|
|
|
15
15
|
untrack,
|
|
16
16
|
type JSXElement,
|
|
17
17
|
type ValidComponent,
|
|
18
|
+
createRoot,
|
|
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 {
|
package/src/solidOpts.ts
CHANGED
|
@@ -9,6 +9,36 @@ import {
|
|
|
9
9
|
} from '@lightningtv/core';
|
|
10
10
|
import type { SolidNode, SolidRendererOptions } from './types.js';
|
|
11
11
|
|
|
12
|
+
declare module '@lightningtv/core' {
|
|
13
|
+
interface ElementNode {
|
|
14
|
+
/** @internal for managing series of insertions and deletions */
|
|
15
|
+
_queueDelete?: number;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let elementDeleteQueue: ElementNode[] = [];
|
|
20
|
+
|
|
21
|
+
function flushDeleteQueue(): void {
|
|
22
|
+
for (let el of elementDeleteQueue) {
|
|
23
|
+
if (Number(el._queueDelete) < 0) {
|
|
24
|
+
el.destroy();
|
|
25
|
+
}
|
|
26
|
+
el._queueDelete = undefined;
|
|
27
|
+
}
|
|
28
|
+
elementDeleteQueue.length = 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function pushDeleteQueue(node: ElementNode, n: number): void {
|
|
32
|
+
if (node._queueDelete === undefined) {
|
|
33
|
+
node._queueDelete = n;
|
|
34
|
+
if (elementDeleteQueue.push(node) === 1) {
|
|
35
|
+
queueMicrotask(flushDeleteQueue);
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
node._queueDelete += n;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
12
42
|
export default {
|
|
13
43
|
createElement(name: string): ElementNode {
|
|
14
44
|
return new ElementNode(name);
|
|
@@ -30,11 +60,14 @@ export default {
|
|
|
30
60
|
insertNode(parent: ElementNode, node: SolidNode, anchor: SolidNode): void {
|
|
31
61
|
log('INSERT: ', parent, node, anchor);
|
|
32
62
|
|
|
63
|
+
let prevParent = node.parent;
|
|
33
64
|
parent.insertChild(node, anchor);
|
|
34
|
-
node._queueDelete = false;
|
|
35
65
|
|
|
36
66
|
if (node instanceof ElementNode) {
|
|
37
|
-
parent
|
|
67
|
+
node.parent!.rendered && node.render(true);
|
|
68
|
+
if (prevParent !== undefined) {
|
|
69
|
+
pushDeleteQueue(node, 1);
|
|
70
|
+
}
|
|
38
71
|
} else if (isElementText(parent)) {
|
|
39
72
|
// TextNodes can be placed outside of <text> nodes when <Show> is used as placeholder
|
|
40
73
|
parent.text = parent.getText();
|
|
@@ -45,13 +78,11 @@ export default {
|
|
|
45
78
|
},
|
|
46
79
|
removeNode(parent: ElementNode, node: SolidNode): void {
|
|
47
80
|
log('REMOVE: ', parent, node);
|
|
81
|
+
|
|
48
82
|
parent.removeChild(node);
|
|
49
|
-
|
|
83
|
+
|
|
50
84
|
if (node instanceof ElementNode) {
|
|
51
|
-
|
|
52
|
-
// so we need to wait for the next microtask to destroy the node
|
|
53
|
-
// in the event it gets a new parent.
|
|
54
|
-
queueMicrotask(() => node.destroy());
|
|
85
|
+
pushDeleteQueue(node, -1);
|
|
55
86
|
}
|
|
56
87
|
},
|
|
57
88
|
getParentNode(node: SolidNode): ElementNode | ElementText | undefined {
|