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