@lightningtv/solid 3.0.0-2 → 3.0.0-21
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/src/activeElement.d.ts +1 -1
- package/dist/src/core/animation.d.ts +35 -0
- package/dist/src/core/animation.js +120 -0
- package/dist/src/core/animation.js.map +1 -0
- package/dist/src/core/config.d.ts +47 -0
- package/dist/src/core/config.js +23 -0
- package/dist/src/core/config.js.map +1 -0
- package/dist/src/core/domRenderer.d.ts +117 -0
- package/dist/src/core/domRenderer.js +1160 -0
- package/dist/src/core/domRenderer.js.map +1 -0
- package/dist/src/core/elementNode.d.ts +209 -0
- package/dist/src/core/elementNode.js +829 -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 +269 -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/timings.d.ts +36 -0
- package/dist/src/core/timings.js +199 -0
- package/dist/src/core/timings.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/jsx-runtime.d.ts +1 -3
- package/dist/src/primitives/Column.jsx +9 -10
- package/dist/src/primitives/Column.jsx.map +1 -1
- package/dist/src/primitives/FPSCounter.jsx +14 -1
- package/dist/src/primitives/FPSCounter.jsx.map +1 -1
- package/dist/src/primitives/Grid.d.ts +15 -6
- package/dist/src/primitives/Grid.jsx +35 -22
- package/dist/src/primitives/Grid.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 -23
- package/dist/src/primitives/Lazy.jsx.map +1 -1
- package/dist/src/primitives/Marquee.d.ts +64 -0
- package/dist/src/primitives/Marquee.jsx +86 -0
- package/dist/src/primitives/Marquee.jsx.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 +434 -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/VirtualList.d.ts +11 -0
- package/dist/src/primitives/VirtualList.jsx +96 -0
- package/dist/src/primitives/VirtualList.jsx.map +1 -0
- package/dist/src/primitives/VirtualRow.d.ts +13 -0
- package/dist/src/primitives/VirtualRow.jsx +97 -0
- package/dist/src/primitives/VirtualRow.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 +2 -0
- package/dist/src/primitives/announcer/announcer.js +7 -5
- package/dist/src/primitives/announcer/announcer.js.map +1 -1
- package/dist/src/primitives/announcer/index.d.ts +5 -1
- package/dist/src/primitives/announcer/index.js +8 -2
- package/dist/src/primitives/announcer/index.js.map +1 -1
- package/dist/src/primitives/announcer/speech.d.ts +2 -2
- package/dist/src/primitives/announcer/speech.js +157 -28
- 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 +14 -4
- package/dist/src/primitives/index.js +13 -3
- package/dist/src/primitives/index.js.map +1 -1
- package/dist/src/primitives/types.d.ts +5 -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/useHold.d.ts +27 -0
- package/dist/src/primitives/useHold.js +54 -0
- package/dist/src/primitives/useHold.js.map +1 -0
- 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/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/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 +1 -1
- 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 +242 -69
- package/dist/src/primitives/utils/handleNavigation.js.map +1 -1
- package/dist/src/primitives/utils/withScrolling.d.ts +14 -2
- package/dist/src/primitives/utils/withScrolling.js +66 -7
- package/dist/src/primitives/utils/withScrolling.js.map +1 -1
- package/dist/src/render.d.ts +8 -7
- package/dist/src/render.js +5 -1
- package/dist/src/render.js.map +1 -1
- package/dist/src/solidOpts.d.ts +1 -7
- package/dist/src/solidOpts.js +32 -16
- package/dist/src/solidOpts.js.map +1 -1
- package/dist/src/types.d.ts +1 -13
- package/dist/src/universal.d.ts +25 -0
- package/dist/src/universal.js +232 -0
- package/dist/src/universal.js.map +1 -0
- 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 +2 -4
- package/package.json +17 -15
- package/src/activeElement.ts +1 -1
- package/src/core/animation.ts +183 -0
- package/src/core/config.ts +77 -0
- package/src/core/domRenderer.ts +1308 -0
- package/src/core/elementNode.ts +1198 -0
- package/src/core/flex.ts +284 -0
- package/src/core/focusKeyTypes.ts +87 -0
- package/src/core/focusManager.ts +359 -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/timings.ts +261 -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 +15 -1
- package/src/primitives/Grid.tsx +57 -33
- package/src/primitives/Image.tsx +36 -0
- package/src/primitives/KeepAlive.tsx +124 -0
- package/src/primitives/Lazy.tsx +66 -37
- package/src/primitives/Marquee.tsx +149 -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 +478 -0
- package/src/primitives/VirtualGrid.tsx +220 -0
- package/src/primitives/Visible.tsx +1 -2
- package/src/primitives/announcer/announcer.ts +16 -10
- package/src/primitives/announcer/index.ts +12 -2
- package/src/primitives/announcer/speech.ts +188 -27
- package/src/primitives/createFocusStack.tsx +18 -7
- package/src/primitives/createTag.tsx +31 -0
- package/src/primitives/index.ts +18 -4
- package/src/primitives/types.ts +12 -2
- package/src/primitives/useFocusManager.ts +3 -3
- package/src/primitives/useHold.ts +69 -0
- package/src/primitives/useMouse.ts +306 -67
- package/src/primitives/utils/chainFunctions.ts +40 -9
- package/src/primitives/utils/createBlurredImage.ts +366 -0
- package/src/primitives/utils/createSpriteMap.ts +6 -4
- package/src/primitives/utils/handleNavigation.ts +300 -84
- package/src/primitives/utils/withScrolling.ts +91 -18
- package/src/render.ts +10 -8
- package/src/solidOpts.ts +31 -24
- package/src/types.ts +1 -15
- package/src/utils.ts +11 -1
|
@@ -0,0 +1,1198 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IRendererNode,
|
|
3
|
+
IRendererNodeProps,
|
|
4
|
+
IRendererShader,
|
|
5
|
+
IRendererShaderProps,
|
|
6
|
+
IRendererTextNode,
|
|
7
|
+
IRendererTextNodeProps,
|
|
8
|
+
renderer,
|
|
9
|
+
} from './lightningInit.js';
|
|
10
|
+
import {
|
|
11
|
+
type BorderRadius,
|
|
12
|
+
type BorderStyle,
|
|
13
|
+
type StyleEffects,
|
|
14
|
+
type AnimationSettings,
|
|
15
|
+
type ElementText,
|
|
16
|
+
type Styles,
|
|
17
|
+
type AnimationEvents,
|
|
18
|
+
type AnimationEventHandler,
|
|
19
|
+
AddColorString,
|
|
20
|
+
TextProps,
|
|
21
|
+
TextNode,
|
|
22
|
+
type OnEvent,
|
|
23
|
+
NewOmit,
|
|
24
|
+
} from './intrinsicTypes.js';
|
|
25
|
+
import States, { type NodeStates } from './states.js';
|
|
26
|
+
import calculateFlex from './flex.js';
|
|
27
|
+
import {
|
|
28
|
+
log,
|
|
29
|
+
isArray,
|
|
30
|
+
isNumber,
|
|
31
|
+
isFunc,
|
|
32
|
+
keyExists,
|
|
33
|
+
isINode,
|
|
34
|
+
isElementNode,
|
|
35
|
+
isElementText,
|
|
36
|
+
logRenderTree,
|
|
37
|
+
isFunction,
|
|
38
|
+
spliceItem,
|
|
39
|
+
} from './utils.js';
|
|
40
|
+
import { Config, DOM_RENDERING, isDev, SHADERS_ENABLED } from './config.js';
|
|
41
|
+
import type {
|
|
42
|
+
RendererMain,
|
|
43
|
+
INode,
|
|
44
|
+
INodeAnimateProps,
|
|
45
|
+
IAnimationController,
|
|
46
|
+
LinearGradientProps,
|
|
47
|
+
RadialGradientProps,
|
|
48
|
+
ShadowProps,
|
|
49
|
+
CoreShaderNode,
|
|
50
|
+
} from '@lightningjs/renderer';
|
|
51
|
+
import { assertTruthy } from '@lightningjs/renderer/utils';
|
|
52
|
+
import { NodeType } from './nodeTypes.js';
|
|
53
|
+
import {
|
|
54
|
+
ForwardFocusHandler,
|
|
55
|
+
setActiveElement,
|
|
56
|
+
FocusNode,
|
|
57
|
+
} from './focusManager.js';
|
|
58
|
+
import simpleAnimation, { SimpleAnimationSettings } from './animation.js';
|
|
59
|
+
|
|
60
|
+
let layoutRunQueued = false;
|
|
61
|
+
const layoutQueue = new Set<ElementNode>();
|
|
62
|
+
|
|
63
|
+
function addToLayoutQueue(node: ElementNode) {
|
|
64
|
+
layoutQueue.add(node);
|
|
65
|
+
if (!layoutRunQueued) {
|
|
66
|
+
layoutRunQueued = true;
|
|
67
|
+
queueMicrotask(runLayout);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function runLayout() {
|
|
72
|
+
layoutRunQueued = false;
|
|
73
|
+
const queue = [...layoutQueue];
|
|
74
|
+
layoutQueue.clear();
|
|
75
|
+
for (let i = queue.length - 1; i >= 0; i--) {
|
|
76
|
+
const node = queue[i] as ElementNode;
|
|
77
|
+
node.updateLayout();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const parseAndAssignShaderProps = (
|
|
82
|
+
prefix: string,
|
|
83
|
+
obj: Record<string, any>,
|
|
84
|
+
props: Record<string, any> = {},
|
|
85
|
+
) => {
|
|
86
|
+
if (!obj) return;
|
|
87
|
+
props[prefix] = obj;
|
|
88
|
+
Object.entries(obj).forEach(([key, value]) => {
|
|
89
|
+
let transformedKey = key === 'width' ? 'w' : key;
|
|
90
|
+
props[`${prefix}-${transformedKey}`] = value;
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
function convertToShader(_node: ElementNode, v: StyleEffects): IRendererShader {
|
|
95
|
+
let type = 'rounded';
|
|
96
|
+
if (v.border) type += 'WithBorder';
|
|
97
|
+
if (v.shadow) type += 'WithShadow';
|
|
98
|
+
return renderer.createShader(type, v as IRendererShaderProps);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function getPropertyAlias(name: string) {
|
|
102
|
+
if (name === 'w') return 'width';
|
|
103
|
+
if (name === 'h') return 'height';
|
|
104
|
+
return name;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export const LightningRendererNumberProps = [
|
|
108
|
+
'alpha',
|
|
109
|
+
'color',
|
|
110
|
+
'colorTop',
|
|
111
|
+
'colorRight',
|
|
112
|
+
'colorLeft',
|
|
113
|
+
'colorBottom',
|
|
114
|
+
'colorTl',
|
|
115
|
+
'colorTr',
|
|
116
|
+
'colorBl',
|
|
117
|
+
'colorBr',
|
|
118
|
+
'h',
|
|
119
|
+
'fontSize',
|
|
120
|
+
'lineHeight',
|
|
121
|
+
'mount',
|
|
122
|
+
'mountX',
|
|
123
|
+
'mountY',
|
|
124
|
+
'pivot',
|
|
125
|
+
'pivotX',
|
|
126
|
+
'pivotY',
|
|
127
|
+
'rotation',
|
|
128
|
+
'scale',
|
|
129
|
+
'scaleX',
|
|
130
|
+
'scaleY',
|
|
131
|
+
'w',
|
|
132
|
+
'worldX',
|
|
133
|
+
'worldY',
|
|
134
|
+
'x',
|
|
135
|
+
'y',
|
|
136
|
+
'zIndex',
|
|
137
|
+
'zIndexLocked',
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
const LightningRendererNonAnimatingProps = [
|
|
141
|
+
'absX',
|
|
142
|
+
'absY',
|
|
143
|
+
'autosize',
|
|
144
|
+
'clipping',
|
|
145
|
+
'contain',
|
|
146
|
+
'data',
|
|
147
|
+
'destroyed',
|
|
148
|
+
'fontFamily',
|
|
149
|
+
'fontStretch',
|
|
150
|
+
'fontStyle',
|
|
151
|
+
'fontWeight',
|
|
152
|
+
'imageType',
|
|
153
|
+
'letterSpacing',
|
|
154
|
+
'maxHeight',
|
|
155
|
+
'maxLines',
|
|
156
|
+
'maxWidth',
|
|
157
|
+
'offsetY',
|
|
158
|
+
'overflowSuffix',
|
|
159
|
+
'preventCleanup',
|
|
160
|
+
'rtt',
|
|
161
|
+
'scrollable',
|
|
162
|
+
'scrollY',
|
|
163
|
+
'srcHeight',
|
|
164
|
+
'srcWidth',
|
|
165
|
+
'srcX',
|
|
166
|
+
'srcY',
|
|
167
|
+
'strictBounds',
|
|
168
|
+
'text',
|
|
169
|
+
'textAlign',
|
|
170
|
+
'textBaseline',
|
|
171
|
+
'textOverflow',
|
|
172
|
+
'texture',
|
|
173
|
+
'textureOptions',
|
|
174
|
+
'verticalAlign',
|
|
175
|
+
'wordWrap',
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
declare global {
|
|
179
|
+
interface HTMLElement {
|
|
180
|
+
/** Assigned for development, to quickly get ElementNode from selected HTMLElement */
|
|
181
|
+
element?: ElementNode;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export type RendererNode = AddColorString<
|
|
186
|
+
Partial<NewOmit<INode, 'parent' | 'shader' | 'src' | 'children' | 'id'>>
|
|
187
|
+
>;
|
|
188
|
+
export interface ElementNode extends RendererNode, FocusNode {
|
|
189
|
+
[key: string]: unknown;
|
|
190
|
+
|
|
191
|
+
// Properties
|
|
192
|
+
/** @internal for managing series of insertions and deletions */
|
|
193
|
+
_queueDelete?: number;
|
|
194
|
+
preserve?: boolean;
|
|
195
|
+
_animationQueue?:
|
|
196
|
+
| Array<{
|
|
197
|
+
props: Partial<INodeAnimateProps<CoreShaderNode>>;
|
|
198
|
+
animationSettings?: AnimationSettings;
|
|
199
|
+
}>
|
|
200
|
+
| undefined;
|
|
201
|
+
_animationQueueSettings?: AnimationSettings;
|
|
202
|
+
_animationRunning?: boolean;
|
|
203
|
+
_animationSettings?: AnimationSettings;
|
|
204
|
+
_autofocus?: boolean;
|
|
205
|
+
_containsFlexGrow?: boolean | null;
|
|
206
|
+
_hasRenderedChildren?: boolean;
|
|
207
|
+
_effects?: StyleEffects;
|
|
208
|
+
_id: string | undefined;
|
|
209
|
+
_parent: ElementNode | undefined;
|
|
210
|
+
_rendererProps?: any;
|
|
211
|
+
_states?: States;
|
|
212
|
+
_style?: Styles;
|
|
213
|
+
_lastAnyKeyPressTime?: number;
|
|
214
|
+
_type: 'element' | 'textNode';
|
|
215
|
+
_undoStyles?: string[];
|
|
216
|
+
autosize?: boolean;
|
|
217
|
+
bottom?: number;
|
|
218
|
+
children: Array<ElementNode | ElementText>;
|
|
219
|
+
debug?: boolean;
|
|
220
|
+
flexGrow?: number;
|
|
221
|
+
flexWrap?: 'nowrap' | 'wrap';
|
|
222
|
+
flexItem?: boolean;
|
|
223
|
+
flexOrder?: number;
|
|
224
|
+
forwardFocus?: number | ForwardFocusHandler;
|
|
225
|
+
forwardStates?: boolean;
|
|
226
|
+
/** For children of {@link NavigableElement}, set to `true` to prevent being selected */
|
|
227
|
+
skipFocus?: boolean;
|
|
228
|
+
/** function to be called on mouse click */
|
|
229
|
+
onMouseClick?: (
|
|
230
|
+
this: ElementNode,
|
|
231
|
+
event: MouseEvent,
|
|
232
|
+
active: ElementNode,
|
|
233
|
+
) => void;
|
|
234
|
+
lng:
|
|
235
|
+
| Partial<ElementNode>
|
|
236
|
+
| IRendererNode
|
|
237
|
+
| (IRendererTextNode & { shader?: any });
|
|
238
|
+
ref?: ElementNode | ((node: ElementNode) => void) | undefined;
|
|
239
|
+
rendered: boolean;
|
|
240
|
+
renderer?: RendererMain;
|
|
241
|
+
right?: number;
|
|
242
|
+
selected?: number;
|
|
243
|
+
preFlexwidth?: number;
|
|
244
|
+
preFlexheight?: number;
|
|
245
|
+
text?: string;
|
|
246
|
+
alignItems?: 'flexStart' | 'flexEnd' | 'center';
|
|
247
|
+
alignSelf?: 'flexStart' | 'flexEnd' | 'center';
|
|
248
|
+
border?: BorderStyle;
|
|
249
|
+
borderRadius?: BorderRadius;
|
|
250
|
+
center?: boolean;
|
|
251
|
+
centerX?: boolean;
|
|
252
|
+
centerY?: boolean;
|
|
253
|
+
direction?: 'ltr' | 'rtl';
|
|
254
|
+
display?: 'flex' | 'block';
|
|
255
|
+
flexBoundary?: 'contain' | 'fixed';
|
|
256
|
+
flexCrossBoundary?: 'fixed'; // default is contain
|
|
257
|
+
flexDirection?: 'row' | 'column';
|
|
258
|
+
gap?: number;
|
|
259
|
+
rowGap?: number;
|
|
260
|
+
columnGap?: number;
|
|
261
|
+
justifyContent?:
|
|
262
|
+
| 'flexStart'
|
|
263
|
+
| 'flexEnd'
|
|
264
|
+
| 'center'
|
|
265
|
+
| 'spaceBetween'
|
|
266
|
+
| 'spaceAround'
|
|
267
|
+
| 'spaceEvenly';
|
|
268
|
+
linearGradient?: LinearGradientProps;
|
|
269
|
+
radialGradient?: RadialGradientProps;
|
|
270
|
+
marginBottom?: number;
|
|
271
|
+
marginLeft?: number;
|
|
272
|
+
marginRight?: number;
|
|
273
|
+
marginTop?: number;
|
|
274
|
+
padding?: number;
|
|
275
|
+
x: number;
|
|
276
|
+
y: number;
|
|
277
|
+
throttleInput?: number;
|
|
278
|
+
w: number;
|
|
279
|
+
h: number;
|
|
280
|
+
zIndex?: number;
|
|
281
|
+
transition?:
|
|
282
|
+
| Record<string, AnimationSettings | undefined | true | false>
|
|
283
|
+
| true
|
|
284
|
+
| false;
|
|
285
|
+
/**
|
|
286
|
+
* Optional handlers for animation events.
|
|
287
|
+
*
|
|
288
|
+
* Available animation events:
|
|
289
|
+
* - 'animating': Fired when the animation is in progress.
|
|
290
|
+
* - 'tick': Fired at each tick or frame update of the animation.
|
|
291
|
+
* - 'stopped': Fired when the animation stops.
|
|
292
|
+
*
|
|
293
|
+
* Each event handler is optional and maps to a corresponding event.
|
|
294
|
+
*
|
|
295
|
+
* @type {Partial<Record<AnimationEvents, AnimationEventHandler>>}
|
|
296
|
+
*
|
|
297
|
+
* @property {AnimationEventHandler} [animating] - Handler for the 'animating' event.
|
|
298
|
+
* @property {AnimationEventHandler} [tick] - Handler for the 'tick' event.
|
|
299
|
+
* @property {AnimationEventHandler} [stopped] - Handler for the 'stopped' event.
|
|
300
|
+
*
|
|
301
|
+
* @see https://lightning-tv.github.io/solid/#/essentials/transitions?id=animation-callbacks
|
|
302
|
+
*/
|
|
303
|
+
onAnimation?: Partial<Record<AnimationEvents, AnimationEventHandler>>;
|
|
304
|
+
/**
|
|
305
|
+
* Optional handler for when the element is created and rendered.
|
|
306
|
+
*/
|
|
307
|
+
onCreate?: (this: ElementNode, el: ElementNode) => void;
|
|
308
|
+
/**
|
|
309
|
+
* Optional handler for when the element is destroyed.
|
|
310
|
+
* It can return a promise to wait for the cleanup to finish before the element is destroyed.
|
|
311
|
+
*/
|
|
312
|
+
onDestroy?: (this: ElementNode, el: ElementNode) => Promise<any> | void;
|
|
313
|
+
/**
|
|
314
|
+
* Optional handlers for when the element is rendered—after creation and when switching parents.
|
|
315
|
+
*/
|
|
316
|
+
onRender?: (this: ElementNode, el: ElementNode) => void;
|
|
317
|
+
/**
|
|
318
|
+
* Optional handlers for when the element is removed from a parent element.
|
|
319
|
+
*/
|
|
320
|
+
onRemove?: (this: ElementNode, el: ElementNode) => void;
|
|
321
|
+
/**
|
|
322
|
+
* Listen to Events coming from the renderer
|
|
323
|
+
* @param NodeEvents
|
|
324
|
+
*
|
|
325
|
+
* Available events:
|
|
326
|
+
* - 'loaded'
|
|
327
|
+
* - 'failed'
|
|
328
|
+
* - 'freed'
|
|
329
|
+
* - 'inBounds'
|
|
330
|
+
* - 'outOfBounds'
|
|
331
|
+
* - 'inViewport'
|
|
332
|
+
* - 'outOfViewport'
|
|
333
|
+
*
|
|
334
|
+
* @typedef {'loaded' | 'failed' | 'freed' | 'inBounds' | 'outOfBounds' | 'inViewport' | 'outOfViewport'} NodeEvents
|
|
335
|
+
*
|
|
336
|
+
* @param {Partial<Record<NodeEvents, EventHandler>>} events - An object where the keys are event names from NodeEvents and the values are the respective event handlers.
|
|
337
|
+
* @returns {void}
|
|
338
|
+
*
|
|
339
|
+
* @see https://lightning-tv.github.io/solid/#/essentials/events
|
|
340
|
+
*/
|
|
341
|
+
onEvent?: OnEvent;
|
|
342
|
+
onLayout?: (this: ElementNode, target: ElementNode) => void;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export class ElementNode extends Object {
|
|
346
|
+
constructor(name: string) {
|
|
347
|
+
super();
|
|
348
|
+
this._type = name === 'text' ? NodeType.TextNode : NodeType.Element;
|
|
349
|
+
this.rendered = false;
|
|
350
|
+
this.lng = {};
|
|
351
|
+
this.children = [];
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
get effects(): StyleEffects | undefined {
|
|
355
|
+
return this.lng.shader;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
set effects(v: StyleEffects) {
|
|
359
|
+
if (!SHADERS_ENABLED) return;
|
|
360
|
+
let target = this.lng.shader || {};
|
|
361
|
+
if (this.lng.shader?.program) {
|
|
362
|
+
target = this.lng.shader.props;
|
|
363
|
+
}
|
|
364
|
+
if (v.rounded) target.radius = v.rounded.radius;
|
|
365
|
+
if (v.borderRadius) target.radius = v.borderRadius;
|
|
366
|
+
if (v.border) parseAndAssignShaderProps('border', v.border, target);
|
|
367
|
+
if (v.shadow) parseAndAssignShaderProps('shadow', v.shadow, target);
|
|
368
|
+
|
|
369
|
+
if (this.rendered) {
|
|
370
|
+
if (!this.lng.shader) {
|
|
371
|
+
this.lng.shader = convertToShader(this, target);
|
|
372
|
+
} else if (DOM_RENDERING) {
|
|
373
|
+
this.lng.shader = this.lng.shader; // lng.shader is a setter, force style update
|
|
374
|
+
}
|
|
375
|
+
} else {
|
|
376
|
+
this.lng.shader = target;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
set id(id: string) {
|
|
381
|
+
this._id = id;
|
|
382
|
+
if (Config.rendererOptions?.inspector) {
|
|
383
|
+
this.data = { ...this.data, testId: id };
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
get id(): string | undefined {
|
|
388
|
+
return this._id;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
get parent() {
|
|
392
|
+
return this._parent;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
set parent(p) {
|
|
396
|
+
this._parent = p;
|
|
397
|
+
if (this.rendered && p?.rendered) {
|
|
398
|
+
this.lng.parent = (p.lng as IRendererNode) ?? null;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
get height() {
|
|
403
|
+
return this.h;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
set height(h) {
|
|
407
|
+
this.h = h;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
get width() {
|
|
411
|
+
return this.w;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
set width(w) {
|
|
415
|
+
this.w = w;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
set fontWeight(v) {
|
|
419
|
+
this._fontWeight = v;
|
|
420
|
+
this.fontFamily = `{${this.fontFamily}${v}`;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
get fontWeight() {
|
|
424
|
+
return this._fontWeight;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
insertChild(
|
|
428
|
+
node: ElementNode | ElementText | TextNode,
|
|
429
|
+
beforeNode?: ElementNode | ElementText | TextNode | null,
|
|
430
|
+
) {
|
|
431
|
+
// always remove nodes if they have a parent - for back swap of node
|
|
432
|
+
// this will then put the node at the end of the array when re-added
|
|
433
|
+
if (node.parent) {
|
|
434
|
+
node.parent.removeChild(node);
|
|
435
|
+
|
|
436
|
+
// We're inserting a node thats been rendered into a node that hasn't been
|
|
437
|
+
if (!this.rendered) {
|
|
438
|
+
this._hasRenderedChildren = true;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
node.parent = this;
|
|
443
|
+
|
|
444
|
+
if (beforeNode) {
|
|
445
|
+
// SolidJS can move nodes around in the children array.
|
|
446
|
+
// We need to insert following DOM insertBefore which moves elements.
|
|
447
|
+
spliceItem(this.children, node as ElementNode, 1);
|
|
448
|
+
if (spliceItem(this.children, beforeNode as ElementNode, 0, node) > -1) {
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
this.children.push(node as ElementNode);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
removeChild(node: ElementNode | ElementText | TextNode) {
|
|
457
|
+
if (spliceItem(this.children, node, 1) > -1) {
|
|
458
|
+
node.onRemove?.call(node, node);
|
|
459
|
+
if (this.requiresLayout()) {
|
|
460
|
+
addToLayoutQueue(this);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
get selectedNode(): ElementNode | undefined {
|
|
466
|
+
const selectedIndex = this.selected || 0;
|
|
467
|
+
|
|
468
|
+
for (let i = selectedIndex; i < this.children.length; i++) {
|
|
469
|
+
const element = this.children[i];
|
|
470
|
+
if (isElementNode(element)) {
|
|
471
|
+
this.selected = i;
|
|
472
|
+
return element;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
return undefined;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
set shader(
|
|
480
|
+
shaderProps: IRendererShader | [kind: string, props: IRendererShaderProps],
|
|
481
|
+
) {
|
|
482
|
+
this.lng.shader = isArray(shaderProps)
|
|
483
|
+
? renderer.createShader(...shaderProps)
|
|
484
|
+
: shaderProps;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
_sendToLightningAnimatable(name: string, value: number) {
|
|
488
|
+
if (
|
|
489
|
+
this.transition &&
|
|
490
|
+
this.rendered &&
|
|
491
|
+
Config.animationsEnabled &&
|
|
492
|
+
(this.transition === true ||
|
|
493
|
+
this.transition[name] ||
|
|
494
|
+
this.transition[getPropertyAlias(name)])
|
|
495
|
+
) {
|
|
496
|
+
const animationSettings =
|
|
497
|
+
this.transition === true || this.transition[name] === true
|
|
498
|
+
? undefined
|
|
499
|
+
: (this.transition[name] as undefined | AnimationSettings);
|
|
500
|
+
|
|
501
|
+
if (Config.simpleAnimationsEnabled) {
|
|
502
|
+
simpleAnimation.add(
|
|
503
|
+
this,
|
|
504
|
+
name,
|
|
505
|
+
value,
|
|
506
|
+
animationSettings ||
|
|
507
|
+
(this.animationSettings as SimpleAnimationSettings),
|
|
508
|
+
);
|
|
509
|
+
simpleAnimation.register(renderer.stage);
|
|
510
|
+
return;
|
|
511
|
+
} else {
|
|
512
|
+
const animationController = this.animate(
|
|
513
|
+
{ [name]: value },
|
|
514
|
+
animationSettings,
|
|
515
|
+
);
|
|
516
|
+
|
|
517
|
+
if (this.onAnimation) {
|
|
518
|
+
const animationEvents = Object.keys(
|
|
519
|
+
this.onAnimation,
|
|
520
|
+
) as AnimationEvents[];
|
|
521
|
+
for (const event of animationEvents) {
|
|
522
|
+
const handler = this.onAnimation[event];
|
|
523
|
+
animationController.on(
|
|
524
|
+
event,
|
|
525
|
+
(controller: IAnimationController, props?: any) => {
|
|
526
|
+
handler!.call(this, controller, name, value, props);
|
|
527
|
+
},
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
return animationController.start();
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
(this.lng[name as keyof IRendererNode] as number | string) = value;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
animate(
|
|
540
|
+
props: Partial<INodeAnimateProps<CoreShaderNode>>,
|
|
541
|
+
animationSettings?: AnimationSettings,
|
|
542
|
+
): IAnimationController {
|
|
543
|
+
isDev &&
|
|
544
|
+
assertTruthy(this.rendered, 'Node must be rendered before animating');
|
|
545
|
+
return (this.lng as IRendererNode).animate(
|
|
546
|
+
props,
|
|
547
|
+
animationSettings || this.animationSettings || {},
|
|
548
|
+
);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
chain(
|
|
552
|
+
props: Partial<INodeAnimateProps<CoreShaderNode>>,
|
|
553
|
+
animationSettings?: AnimationSettings,
|
|
554
|
+
) {
|
|
555
|
+
if (this._animationRunning) {
|
|
556
|
+
this._animationQueue = [];
|
|
557
|
+
this._animationRunning = false;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
if (animationSettings) {
|
|
561
|
+
this._animationQueueSettings = animationSettings;
|
|
562
|
+
} else if (!this._animationQueueSettings) {
|
|
563
|
+
this._animationQueueSettings =
|
|
564
|
+
animationSettings || this.animationSettings;
|
|
565
|
+
}
|
|
566
|
+
animationSettings = animationSettings || this._animationQueueSettings;
|
|
567
|
+
this._animationQueue = this._animationQueue || [];
|
|
568
|
+
this._animationQueue.push({ props, animationSettings });
|
|
569
|
+
return this;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
async start() {
|
|
573
|
+
let animation = this._animationQueue!.shift();
|
|
574
|
+
while (animation) {
|
|
575
|
+
this._animationRunning = true;
|
|
576
|
+
await this.animate(animation.props, animation.animationSettings)
|
|
577
|
+
.start()
|
|
578
|
+
.waitUntilStopped();
|
|
579
|
+
animation = this._animationQueue!.shift();
|
|
580
|
+
}
|
|
581
|
+
this._animationRunning = false;
|
|
582
|
+
this._animationQueueSettings = undefined;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
emit(event: string, ...args: any[]): boolean {
|
|
586
|
+
let current = this as ElementNode;
|
|
587
|
+
const capitalizedEvent = `on${event.charAt(0).toUpperCase()}${event.slice(1)}`;
|
|
588
|
+
|
|
589
|
+
while (current) {
|
|
590
|
+
const handler = current[capitalizedEvent];
|
|
591
|
+
if (isFunction(handler)) {
|
|
592
|
+
if (handler.call(current, this, ...args) === true) {
|
|
593
|
+
return true;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
current = current.parent!;
|
|
597
|
+
}
|
|
598
|
+
return false;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
setFocus(): void {
|
|
602
|
+
if (this.rendered) {
|
|
603
|
+
// can be 0
|
|
604
|
+
if (this.forwardFocus !== undefined) {
|
|
605
|
+
if (isFunc(this.forwardFocus)) {
|
|
606
|
+
if (this.forwardFocus.call(this, this) !== false) {
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
} else {
|
|
610
|
+
const focusedIndex =
|
|
611
|
+
typeof this.forwardFocus === 'number' ? this.forwardFocus : null;
|
|
612
|
+
const nodes = this.children;
|
|
613
|
+
if (focusedIndex !== null && focusedIndex < nodes.length) {
|
|
614
|
+
const child = nodes[focusedIndex];
|
|
615
|
+
isElementNode(child) && child.setFocus();
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
// Delay setting focus so children can render (useful for Row + Column)
|
|
621
|
+
queueMicrotask(() => setActiveElement(this));
|
|
622
|
+
} else {
|
|
623
|
+
this._autofocus = true;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
_layoutOnLoad() {
|
|
628
|
+
(this.lng as IRendererNode).on('loaded', () => {
|
|
629
|
+
this.parent!.updateLayout();
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
getText(this: ElementText) {
|
|
634
|
+
let result = '';
|
|
635
|
+
for (let i = 0; i < this.children.length; i++) {
|
|
636
|
+
result += this.children[i]!.text;
|
|
637
|
+
}
|
|
638
|
+
return result;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
destroy() {
|
|
642
|
+
if (this.onDestroy) {
|
|
643
|
+
const destroyPromise: unknown = this.onDestroy(this);
|
|
644
|
+
|
|
645
|
+
// If onDestroy returns a promise, wait for it to resolve before destroying
|
|
646
|
+
// Useful with animations waitUntilStopped method which returns promise
|
|
647
|
+
if (destroyPromise instanceof Promise) {
|
|
648
|
+
destroyPromise.then(() => this._destroy());
|
|
649
|
+
} else {
|
|
650
|
+
this._destroy();
|
|
651
|
+
}
|
|
652
|
+
} else {
|
|
653
|
+
this._destroy();
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
_destroy() {
|
|
658
|
+
if (isINode(this.lng)) {
|
|
659
|
+
this.lng.destroy();
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
set style(style: Styles | undefined) {
|
|
664
|
+
if (isDev && this._style) {
|
|
665
|
+
// Avoid processing style changes again
|
|
666
|
+
console.warn(
|
|
667
|
+
'Style already set: https://lightning-tv.github.io/solid/#/essentials/styling?id=style-patterns-to-avoid',
|
|
668
|
+
);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
if (Config.lockStyles && this._style) {
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
if (!style) {
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
this._style = style;
|
|
680
|
+
|
|
681
|
+
// Keys set in JSX are more important
|
|
682
|
+
for (const key in this._style) {
|
|
683
|
+
// be careful of 0 values
|
|
684
|
+
if (this[key as keyof Styles] === undefined) {
|
|
685
|
+
this[key as keyof Styles] = this._style[key as keyof Styles];
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
get style(): Styles {
|
|
691
|
+
return this._style!;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
get hasChildren() {
|
|
695
|
+
return this.children.length > 0;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
set src(src) {
|
|
699
|
+
if (typeof src === 'string') {
|
|
700
|
+
this.lng.src = src;
|
|
701
|
+
if (!this.color && this.rendered) {
|
|
702
|
+
this.color = 0xffffffff;
|
|
703
|
+
}
|
|
704
|
+
} else {
|
|
705
|
+
this.color = 0x00000000;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
get src(): string | null | undefined {
|
|
710
|
+
return this.lng.src;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
getChildById(id: string) {
|
|
714
|
+
return this.children.find((c) => c.id === id);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
searchChildrenById(id: string): ElementNode | undefined {
|
|
718
|
+
// traverse all the childrens children
|
|
719
|
+
for (let i = 0; i < this.children.length; i++) {
|
|
720
|
+
const child = this.children[i];
|
|
721
|
+
if (isElementNode(child)) {
|
|
722
|
+
if (child.id === id) {
|
|
723
|
+
return child;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
const found = child.searchChildrenById(id);
|
|
727
|
+
if (found) {
|
|
728
|
+
return found;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
set states(states: NodeStates) {
|
|
735
|
+
this._states = this._states
|
|
736
|
+
? this._states.merge(states)
|
|
737
|
+
: new States(this._stateChanged.bind(this), states);
|
|
738
|
+
if (this.rendered) {
|
|
739
|
+
this._stateChanged();
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
get states(): States {
|
|
744
|
+
this._states = this._states || new States(this._stateChanged.bind(this));
|
|
745
|
+
return this._states;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
get animationSettings(): AnimationSettings | undefined {
|
|
749
|
+
return this._animationSettings || Config.animationSettings;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
set animationSettings(animationSettings: AnimationSettings | undefined) {
|
|
753
|
+
this._animationSettings = animationSettings;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
set hidden(val: boolean) {
|
|
757
|
+
this.alpha = val ? 0 : 1;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
get hidden() {
|
|
761
|
+
return this.alpha === 0;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* Sets the autofocus state of the element.
|
|
766
|
+
* When set to a truthy value, the element will automatically gain focus.
|
|
767
|
+
* You can also set it to a signal to recalculate
|
|
768
|
+
*
|
|
769
|
+
* @param val - A value to determine if the element should autofocus.
|
|
770
|
+
* A truthy value enables autofocus, otherwise disables it.
|
|
771
|
+
*/
|
|
772
|
+
set autofocus(val: any) {
|
|
773
|
+
this._autofocus = val;
|
|
774
|
+
// Delay setting focus so children can render (useful for Row + Column)
|
|
775
|
+
// which now uses forwardFocus
|
|
776
|
+
val && queueMicrotask(() => this.setFocus());
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
get autofocus() {
|
|
780
|
+
return this._autofocus;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
requiresLayout() {
|
|
784
|
+
return this.display === 'flex' || this.onLayout;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
set updateLayoutOn(v: any) {
|
|
788
|
+
this.updateLayout();
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
get updateLayoutOn() {
|
|
792
|
+
return null;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
updateLayout() {
|
|
796
|
+
if (this.hasChildren) {
|
|
797
|
+
isDev && log('Layout: ', this);
|
|
798
|
+
|
|
799
|
+
if (this.display === 'flex' && this.flexGrow && this.width === 0) {
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
const flexChanged = this.display === 'flex' && calculateFlex(this);
|
|
804
|
+
layoutQueue.delete(this);
|
|
805
|
+
const onLayoutChanged =
|
|
806
|
+
isFunc(this.onLayout) && this.onLayout.call(this, this);
|
|
807
|
+
|
|
808
|
+
if ((flexChanged || onLayoutChanged) && this.parent) {
|
|
809
|
+
addToLayoutQueue(this.parent);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
if (this._containsFlexGrow === true) {
|
|
813
|
+
// Need to reprocess children
|
|
814
|
+
this.children.forEach((c) => {
|
|
815
|
+
if (c.display === 'flex' && isElementNode(c)) {
|
|
816
|
+
// calculating directly to prevent infinite loops recalculating parents
|
|
817
|
+
calculateFlex(c);
|
|
818
|
+
isFunc(c.onLayout) && c.onLayout.call(c, c);
|
|
819
|
+
addToLayoutQueue(this);
|
|
820
|
+
}
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
_stateChanged() {
|
|
827
|
+
isDev && log('State Changed: ', this, this.states);
|
|
828
|
+
|
|
829
|
+
if (this.forwardStates) {
|
|
830
|
+
// apply states to children first
|
|
831
|
+
const states = this.states.slice() as States;
|
|
832
|
+
this.children.forEach((c) => {
|
|
833
|
+
c.states = states;
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
const states = this.states;
|
|
838
|
+
|
|
839
|
+
if (this._undoStyles || keyExists(this, states)) {
|
|
840
|
+
let stylesToUndo: { [key: string]: any } | undefined;
|
|
841
|
+
if (this._undoStyles && this._undoStyles.length) {
|
|
842
|
+
stylesToUndo = {};
|
|
843
|
+
this._undoStyles.forEach((styleKey) => {
|
|
844
|
+
if (isDev) {
|
|
845
|
+
if (this.style[styleKey] === undefined) {
|
|
846
|
+
console.warn('fallback style key not found: ', styleKey);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
stylesToUndo![styleKey] = this.style[styleKey];
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
const numStates = states.length;
|
|
854
|
+
if (numStates === 0) {
|
|
855
|
+
Object.assign(this, stylesToUndo);
|
|
856
|
+
this._undoStyles = [];
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
let newStyles: Styles;
|
|
861
|
+
if (numStates === 1) {
|
|
862
|
+
newStyles = this[states[0] as keyof Styles] as Styles;
|
|
863
|
+
newStyles = stylesToUndo
|
|
864
|
+
? { ...stylesToUndo, ...newStyles }
|
|
865
|
+
: newStyles;
|
|
866
|
+
} else {
|
|
867
|
+
newStyles = states.reduce((acc, state) => {
|
|
868
|
+
const styles = this[state];
|
|
869
|
+
return styles ? { ...acc, ...styles } : acc;
|
|
870
|
+
}, stylesToUndo || {});
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
if (newStyles) {
|
|
874
|
+
this._undoStyles = Object.keys(newStyles);
|
|
875
|
+
// Apply transition first
|
|
876
|
+
if (newStyles.transition !== undefined) {
|
|
877
|
+
this.transition = newStyles.transition as Styles['transition'];
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// Apply the styles
|
|
881
|
+
Object.assign(this, newStyles);
|
|
882
|
+
} else {
|
|
883
|
+
this._undoStyles = [];
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
render(topNode?: boolean) {
|
|
889
|
+
// Elements are inserted from the inside out, then rendered from the outside in.
|
|
890
|
+
// Render starts when an element is inserted with a parent that is already renderered.
|
|
891
|
+
const node = this;
|
|
892
|
+
const parent = this.parent;
|
|
893
|
+
|
|
894
|
+
if (!parent) {
|
|
895
|
+
console.warn('Parent not set - no node created for: ', this);
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
if (!parent.rendered) {
|
|
900
|
+
console.warn('Parent not rendered yet: ', this);
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
if (parent.requiresLayout()) {
|
|
905
|
+
layoutQueue.add(parent);
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
if (this.rendered) {
|
|
909
|
+
// This happens if Array of items is reordered to reuse elements.
|
|
910
|
+
// We return after layout is queued so the change can trigger layout updates.
|
|
911
|
+
this.onRender?.(this);
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
if (this._states) {
|
|
916
|
+
this._stateChanged();
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
const props = node.lng;
|
|
920
|
+
const parentWidth = parent.w || 0;
|
|
921
|
+
const parentHeight = parent.h || 0;
|
|
922
|
+
|
|
923
|
+
props.x = props.x || 0;
|
|
924
|
+
props.y = props.y || 0;
|
|
925
|
+
props.parent = parent.lng as IRendererNode;
|
|
926
|
+
|
|
927
|
+
if (this.right || this.right === 0) {
|
|
928
|
+
props.x = parentWidth - this.right;
|
|
929
|
+
props.mountX = 1;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
if (this.bottom || this.bottom === 0) {
|
|
933
|
+
props.y = parentHeight - this.bottom;
|
|
934
|
+
props.mountY = 1;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
if (this.center) {
|
|
938
|
+
this.centerX = this.centerY = true;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
if (this.centerX) {
|
|
942
|
+
props.x += parentWidth / 2;
|
|
943
|
+
props.mountX = 0.5;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
if (this.centerY) {
|
|
947
|
+
props.y += parentHeight / 2;
|
|
948
|
+
props.mountY = 0.5;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
if (isElementText(node)) {
|
|
952
|
+
const textProps = props as TextProps;
|
|
953
|
+
if (Config.fontSettings) {
|
|
954
|
+
for (const key in Config.fontSettings) {
|
|
955
|
+
if (textProps[key] === undefined) {
|
|
956
|
+
textProps[key] = Config.fontSettings[key];
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
textProps.text = textProps.text || node.getText();
|
|
961
|
+
|
|
962
|
+
if (textProps.textAlign && !textProps.contain) {
|
|
963
|
+
console.warn('Text align requires contain: ', node.getText());
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
// contain is either width or both
|
|
967
|
+
if (textProps.contain) {
|
|
968
|
+
if (!textProps.w) {
|
|
969
|
+
textProps.w =
|
|
970
|
+
parentWidth - textProps.x! - (textProps.marginRight || 0);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
if (
|
|
974
|
+
textProps.contain === 'both' &&
|
|
975
|
+
!textProps.h &&
|
|
976
|
+
!textProps.maxLines
|
|
977
|
+
) {
|
|
978
|
+
textProps.h =
|
|
979
|
+
parentHeight - textProps.y! - (textProps.marginBottom || 0);
|
|
980
|
+
} else if (textProps.maxLines === 1) {
|
|
981
|
+
textProps.h = (textProps.h ||
|
|
982
|
+
textProps.lineHeight ||
|
|
983
|
+
textProps.fontSize) as number;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
if (textProps.contain === 'both') {
|
|
987
|
+
textProps.maxWidth = textProps.w;
|
|
988
|
+
textProps.maxHeight = textProps.h;
|
|
989
|
+
} else if (textProps.contain === 'width') {
|
|
990
|
+
textProps.maxWidth = textProps.w;
|
|
991
|
+
textProps.maxLines = textProps.maxLines ?? 1;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
// Can you put effects on Text nodes? Need to confirm...
|
|
996
|
+
if (SHADERS_ENABLED && props.shader && !props.shader.program) {
|
|
997
|
+
props.shader = convertToShader(node, props.shader);
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
isDev && log('Rendering: ', this, props);
|
|
1001
|
+
node.lng = renderer.createTextNode(
|
|
1002
|
+
props as unknown as IRendererTextNodeProps,
|
|
1003
|
+
);
|
|
1004
|
+
if (parent.requiresLayout()) {
|
|
1005
|
+
if (!props.w || !props.h) {
|
|
1006
|
+
node._layoutOnLoad();
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
} else {
|
|
1010
|
+
// If its not an image or texture apply some defaults
|
|
1011
|
+
if (!props.texture) {
|
|
1012
|
+
// Set width and height to parent less offset
|
|
1013
|
+
if (isNaN(props.w as number)) {
|
|
1014
|
+
props.w = node.flexGrow ? 0 : parentWidth - props.x;
|
|
1015
|
+
node._calcWidth = true;
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
if (isNaN(props.h as number)) {
|
|
1019
|
+
props.h = parentHeight - props.y;
|
|
1020
|
+
node._calcHeight = true;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
if (props.rtt && !props.color) {
|
|
1024
|
+
props.color = 0xffffffff;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
if (!props.color && !props.src) {
|
|
1028
|
+
// Default color to transparent - If you later set a src, you'll need
|
|
1029
|
+
// to set color '#ffffffff'
|
|
1030
|
+
props.color = 0x00000000;
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
if (SHADERS_ENABLED && props.shader && !props.shader.program) {
|
|
1035
|
+
props.shader = convertToShader(node, props.shader);
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
isDev && log('Rendering: ', this, props);
|
|
1039
|
+
node.lng = renderer.createNode(props as IRendererNodeProps);
|
|
1040
|
+
|
|
1041
|
+
if (node._hasRenderedChildren) {
|
|
1042
|
+
node._hasRenderedChildren = false;
|
|
1043
|
+
|
|
1044
|
+
for (const child of node.children) {
|
|
1045
|
+
if (isElementNode(child) && isINode(child.lng)) {
|
|
1046
|
+
child.lng.parent = node.lng as any;
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
node.rendered = true;
|
|
1053
|
+
if (isDev) {
|
|
1054
|
+
// Store props so we can recreate raw renderer code
|
|
1055
|
+
node._rendererProps = props;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
if (node.autosize && parent.requiresLayout()) {
|
|
1059
|
+
node._layoutOnLoad();
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
this.onCreate?.(this);
|
|
1063
|
+
this.onRender?.(this);
|
|
1064
|
+
|
|
1065
|
+
if (node.onEvent) {
|
|
1066
|
+
for (const [name, handler] of Object.entries(node.onEvent)) {
|
|
1067
|
+
node.lng.on(name, (_inode, data) => handler.call(node, node, data));
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
// L3 Inspector adds div to the lng object
|
|
1072
|
+
if (node.lng?.div) {
|
|
1073
|
+
node.lng.div.element = node;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
if (node._type === NodeType.Element) {
|
|
1077
|
+
// only element nodes will have children that need rendering
|
|
1078
|
+
const numChildren = node.children.length;
|
|
1079
|
+
for (let i = 0; i < numChildren; i++) {
|
|
1080
|
+
const c = node.children[i];
|
|
1081
|
+
isDev && assertTruthy(c, 'Child is undefined');
|
|
1082
|
+
// Text elements sneak in from Solid creating tracked nodes
|
|
1083
|
+
if (isElementNode(c)) {
|
|
1084
|
+
c.render();
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
if (topNode && !layoutRunQueued) {
|
|
1089
|
+
//Do one pass of layout, then another with Text loads
|
|
1090
|
+
layoutRunQueued = true;
|
|
1091
|
+
// We use queue because <For> loop will add children one at a time, causing lots of layout
|
|
1092
|
+
queueMicrotask(runLayout);
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
node._autofocus && node.setFocus();
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
for (const key of LightningRendererNumberProps) {
|
|
1100
|
+
Object.defineProperty(ElementNode.prototype, key, {
|
|
1101
|
+
get(): number {
|
|
1102
|
+
return this.lng[key];
|
|
1103
|
+
},
|
|
1104
|
+
set(this: ElementNode, v: number) {
|
|
1105
|
+
this._sendToLightningAnimatable(key, v);
|
|
1106
|
+
},
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
for (const key of LightningRendererNonAnimatingProps) {
|
|
1111
|
+
Object.defineProperty(ElementNode.prototype, key, {
|
|
1112
|
+
get(): unknown {
|
|
1113
|
+
return this.lng[key];
|
|
1114
|
+
},
|
|
1115
|
+
set(v: unknown) {
|
|
1116
|
+
this.lng[key] = v;
|
|
1117
|
+
},
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
function createRawShaderAccessor<T>(key: keyof StyleEffects) {
|
|
1122
|
+
return {
|
|
1123
|
+
set(this: ElementNode, value: T) {
|
|
1124
|
+
this.shader = [key, value as unknown as IRendererShaderProps];
|
|
1125
|
+
},
|
|
1126
|
+
|
|
1127
|
+
get(this: ElementNode) {
|
|
1128
|
+
return this.shader;
|
|
1129
|
+
},
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
function shaderAccessor<T extends Record<string, any> | number>(
|
|
1134
|
+
key: 'border' | 'shadow' | 'rounded',
|
|
1135
|
+
) {
|
|
1136
|
+
return {
|
|
1137
|
+
set(this: ElementNode, value: T) {
|
|
1138
|
+
let target = this.lng.shader || {};
|
|
1139
|
+
|
|
1140
|
+
let animationSettings: AnimationSettings | undefined;
|
|
1141
|
+
if (this.lng.shader?.program) {
|
|
1142
|
+
target = this.lng.shader.props;
|
|
1143
|
+
const transitionKey = key === 'rounded' ? 'borderRadius' : key;
|
|
1144
|
+
if (
|
|
1145
|
+
this.transition &&
|
|
1146
|
+
(this.transition === true || this.transition[transitionKey])
|
|
1147
|
+
) {
|
|
1148
|
+
target = {};
|
|
1149
|
+
animationSettings =
|
|
1150
|
+
this.transition === true || this.transition[transitionKey] === true
|
|
1151
|
+
? undefined
|
|
1152
|
+
: (this.transition[transitionKey] as
|
|
1153
|
+
| undefined
|
|
1154
|
+
| AnimationSettings);
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
if (key === 'rounded' || typeof value === 'number') {
|
|
1159
|
+
target.radius = value;
|
|
1160
|
+
} else {
|
|
1161
|
+
parseAndAssignShaderProps(key, value, target);
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
if (this.rendered) {
|
|
1165
|
+
if (!this.lng.shader) {
|
|
1166
|
+
this.lng.shader = convertToShader(this, target);
|
|
1167
|
+
}
|
|
1168
|
+
} else {
|
|
1169
|
+
this.lng.shader = target;
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
if (animationSettings) {
|
|
1173
|
+
this.animate({ shaderProps: target }, animationSettings).start();
|
|
1174
|
+
}
|
|
1175
|
+
},
|
|
1176
|
+
get(this: ElementNode) {
|
|
1177
|
+
return this.effects?.[key];
|
|
1178
|
+
},
|
|
1179
|
+
};
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
if (isDev) {
|
|
1183
|
+
ElementNode.prototype.lngTree = function () {
|
|
1184
|
+
return logRenderTree(this);
|
|
1185
|
+
};
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
Object.defineProperties(ElementNode.prototype, {
|
|
1189
|
+
border: shaderAccessor<BorderStyle>('border'),
|
|
1190
|
+
shadow: shaderAccessor<ShadowProps>('shadow'),
|
|
1191
|
+
rounded: shaderAccessor<BorderRadius>('rounded'),
|
|
1192
|
+
// Alias for rounded
|
|
1193
|
+
borderRadius: shaderAccessor<BorderRadius>('rounded'),
|
|
1194
|
+
linearGradient:
|
|
1195
|
+
createRawShaderAccessor<LinearGradientProps>('linearGradient'),
|
|
1196
|
+
radialGradient:
|
|
1197
|
+
createRawShaderAccessor<RadialGradientProps>('radialGradient'),
|
|
1198
|
+
});
|