@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.
Files changed (206) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +6 -0
  3. package/dist/src/activeElement.d.ts +1 -1
  4. package/dist/src/core/animation.d.ts +35 -0
  5. package/dist/src/core/animation.js +120 -0
  6. package/dist/src/core/animation.js.map +1 -0
  7. package/dist/src/core/config.d.ts +47 -0
  8. package/dist/src/core/config.js +23 -0
  9. package/dist/src/core/config.js.map +1 -0
  10. package/dist/src/core/domRenderer.d.ts +117 -0
  11. package/dist/src/core/domRenderer.js +1160 -0
  12. package/dist/src/core/domRenderer.js.map +1 -0
  13. package/dist/src/core/elementNode.d.ts +209 -0
  14. package/dist/src/core/elementNode.js +829 -0
  15. package/dist/src/core/elementNode.js.map +1 -0
  16. package/dist/src/core/flex.d.ts +2 -0
  17. package/dist/src/core/flex.js +243 -0
  18. package/dist/src/core/flex.js.map +1 -0
  19. package/dist/src/core/focusKeyTypes.d.ts +42 -0
  20. package/dist/src/core/focusKeyTypes.js +2 -0
  21. package/dist/src/core/focusKeyTypes.js.map +1 -0
  22. package/dist/src/core/focusManager.d.ts +13 -0
  23. package/dist/src/core/focusManager.js +269 -0
  24. package/dist/src/core/focusManager.js.map +1 -0
  25. package/dist/src/core/index.d.ts +12 -0
  26. package/dist/src/core/index.js +12 -0
  27. package/dist/src/core/index.js.map +1 -0
  28. package/dist/src/core/intrinsicTypes.d.ts +90 -0
  29. package/dist/src/core/intrinsicTypes.js +2 -0
  30. package/dist/src/core/intrinsicTypes.js.map +1 -0
  31. package/dist/src/core/lightningInit.d.ts +89 -0
  32. package/dist/src/core/lightningInit.js +26 -0
  33. package/dist/src/core/lightningInit.js.map +1 -0
  34. package/dist/src/core/nodeTypes.d.ts +6 -0
  35. package/dist/src/core/nodeTypes.js +6 -0
  36. package/dist/src/core/nodeTypes.js.map +1 -0
  37. package/dist/src/core/shaders.d.ts +51 -0
  38. package/dist/src/core/shaders.js +446 -0
  39. package/dist/src/core/shaders.js.map +1 -0
  40. package/dist/src/core/states.d.ts +12 -0
  41. package/dist/src/core/states.js +84 -0
  42. package/dist/src/core/states.js.map +1 -0
  43. package/dist/src/core/timings.d.ts +36 -0
  44. package/dist/src/core/timings.js +199 -0
  45. package/dist/src/core/timings.js.map +1 -0
  46. package/dist/src/core/utils.d.ts +39 -0
  47. package/dist/src/core/utils.js +164 -0
  48. package/dist/src/core/utils.js.map +1 -0
  49. package/dist/src/devtools/index.d.ts +1 -1
  50. package/dist/src/devtools/index.js +1 -1
  51. package/dist/src/devtools/index.js.map +1 -1
  52. package/dist/src/index.d.ts +3 -3
  53. package/dist/src/index.js +1 -1
  54. package/dist/src/index.js.map +1 -1
  55. package/dist/src/jsx-runtime.d.ts +1 -3
  56. package/dist/src/primitives/Column.jsx +9 -10
  57. package/dist/src/primitives/Column.jsx.map +1 -1
  58. package/dist/src/primitives/FPSCounter.jsx +14 -1
  59. package/dist/src/primitives/FPSCounter.jsx.map +1 -1
  60. package/dist/src/primitives/Grid.d.ts +15 -6
  61. package/dist/src/primitives/Grid.jsx +35 -22
  62. package/dist/src/primitives/Grid.jsx.map +1 -1
  63. package/dist/src/primitives/Image.d.ts +8 -0
  64. package/dist/src/primitives/Image.jsx +24 -0
  65. package/dist/src/primitives/Image.jsx.map +1 -0
  66. package/dist/src/primitives/KeepAlive.d.ts +30 -0
  67. package/dist/src/primitives/KeepAlive.jsx +77 -0
  68. package/dist/src/primitives/KeepAlive.jsx.map +1 -0
  69. package/dist/src/primitives/Lazy.d.ts +8 -7
  70. package/dist/src/primitives/Lazy.jsx +52 -23
  71. package/dist/src/primitives/Lazy.jsx.map +1 -1
  72. package/dist/src/primitives/Marquee.d.ts +64 -0
  73. package/dist/src/primitives/Marquee.jsx +86 -0
  74. package/dist/src/primitives/Marquee.jsx.map +1 -0
  75. package/dist/src/primitives/Preserve.d.ts +4 -0
  76. package/dist/src/primitives/Preserve.jsx +11 -0
  77. package/dist/src/primitives/Preserve.jsx.map +1 -0
  78. package/dist/src/primitives/Row.jsx +9 -10
  79. package/dist/src/primitives/Row.jsx.map +1 -1
  80. package/dist/src/primitives/Suspense.d.ts +22 -0
  81. package/dist/src/primitives/Suspense.jsx +33 -0
  82. package/dist/src/primitives/Suspense.jsx.map +1 -0
  83. package/dist/src/primitives/Virtual.d.ts +18 -0
  84. package/dist/src/primitives/Virtual.jsx +434 -0
  85. package/dist/src/primitives/Virtual.jsx.map +1 -0
  86. package/dist/src/primitives/VirtualGrid.d.ts +13 -0
  87. package/dist/src/primitives/VirtualGrid.jsx +160 -0
  88. package/dist/src/primitives/VirtualGrid.jsx.map +1 -0
  89. package/dist/src/primitives/VirtualList.d.ts +11 -0
  90. package/dist/src/primitives/VirtualList.jsx +96 -0
  91. package/dist/src/primitives/VirtualList.jsx.map +1 -0
  92. package/dist/src/primitives/VirtualRow.d.ts +13 -0
  93. package/dist/src/primitives/VirtualRow.jsx +97 -0
  94. package/dist/src/primitives/VirtualRow.jsx.map +1 -0
  95. package/dist/src/primitives/Visible.d.ts +0 -1
  96. package/dist/src/primitives/Visible.jsx +1 -1
  97. package/dist/src/primitives/Visible.jsx.map +1 -1
  98. package/dist/src/primitives/announcer/announcer.d.ts +2 -0
  99. package/dist/src/primitives/announcer/announcer.js +7 -5
  100. package/dist/src/primitives/announcer/announcer.js.map +1 -1
  101. package/dist/src/primitives/announcer/index.d.ts +5 -1
  102. package/dist/src/primitives/announcer/index.js +8 -2
  103. package/dist/src/primitives/announcer/index.js.map +1 -1
  104. package/dist/src/primitives/announcer/speech.d.ts +2 -2
  105. package/dist/src/primitives/announcer/speech.js +157 -28
  106. package/dist/src/primitives/announcer/speech.js.map +1 -1
  107. package/dist/src/primitives/createFocusStack.d.ts +4 -4
  108. package/dist/src/primitives/createFocusStack.jsx +15 -6
  109. package/dist/src/primitives/createFocusStack.jsx.map +1 -1
  110. package/dist/src/primitives/createTag.d.ts +8 -0
  111. package/dist/src/primitives/createTag.jsx +20 -0
  112. package/dist/src/primitives/createTag.jsx.map +1 -0
  113. package/dist/src/primitives/index.d.ts +14 -4
  114. package/dist/src/primitives/index.js +13 -3
  115. package/dist/src/primitives/index.js.map +1 -1
  116. package/dist/src/primitives/types.d.ts +5 -2
  117. package/dist/src/primitives/useFocusManager.d.ts +2 -2
  118. package/dist/src/primitives/useFocusManager.js +2 -2
  119. package/dist/src/primitives/useFocusManager.js.map +1 -1
  120. package/dist/src/primitives/useHold.d.ts +27 -0
  121. package/dist/src/primitives/useHold.js +54 -0
  122. package/dist/src/primitives/useHold.js.map +1 -0
  123. package/dist/src/primitives/useMouse.d.ts +18 -2
  124. package/dist/src/primitives/useMouse.js +171 -47
  125. package/dist/src/primitives/useMouse.js.map +1 -1
  126. package/dist/src/primitives/utils/chainFunctions.d.ts +30 -4
  127. package/dist/src/primitives/utils/chainFunctions.js +14 -3
  128. package/dist/src/primitives/utils/chainFunctions.js.map +1 -1
  129. package/dist/src/primitives/utils/createBlurredImage.d.ts +56 -0
  130. package/dist/src/primitives/utils/createBlurredImage.js +223 -0
  131. package/dist/src/primitives/utils/createBlurredImage.js.map +1 -0
  132. package/dist/src/primitives/utils/createSpriteMap.d.ts +2 -2
  133. package/dist/src/primitives/utils/createSpriteMap.js +1 -1
  134. package/dist/src/primitives/utils/createSpriteMap.js.map +1 -1
  135. package/dist/src/primitives/utils/handleNavigation.d.ts +79 -5
  136. package/dist/src/primitives/utils/handleNavigation.js +242 -69
  137. package/dist/src/primitives/utils/handleNavigation.js.map +1 -1
  138. package/dist/src/primitives/utils/withScrolling.d.ts +14 -2
  139. package/dist/src/primitives/utils/withScrolling.js +66 -7
  140. package/dist/src/primitives/utils/withScrolling.js.map +1 -1
  141. package/dist/src/render.d.ts +8 -7
  142. package/dist/src/render.js +5 -1
  143. package/dist/src/render.js.map +1 -1
  144. package/dist/src/solidOpts.d.ts +1 -7
  145. package/dist/src/solidOpts.js +32 -16
  146. package/dist/src/solidOpts.js.map +1 -1
  147. package/dist/src/types.d.ts +1 -13
  148. package/dist/src/universal.d.ts +25 -0
  149. package/dist/src/universal.js +232 -0
  150. package/dist/src/universal.js.map +1 -0
  151. package/dist/src/utils.d.ts +3 -1
  152. package/dist/src/utils.js +9 -1
  153. package/dist/src/utils.js.map +1 -1
  154. package/dist/tsconfig.tsbuildinfo +1 -1
  155. package/jsx-runtime.d.ts +2 -4
  156. package/package.json +17 -15
  157. package/src/activeElement.ts +1 -1
  158. package/src/core/animation.ts +183 -0
  159. package/src/core/config.ts +77 -0
  160. package/src/core/domRenderer.ts +1308 -0
  161. package/src/core/elementNode.ts +1198 -0
  162. package/src/core/flex.ts +284 -0
  163. package/src/core/focusKeyTypes.ts +87 -0
  164. package/src/core/focusManager.ts +359 -0
  165. package/src/core/index.ts +13 -0
  166. package/src/core/intrinsicTypes.ts +199 -0
  167. package/src/core/lightningInit.ts +147 -0
  168. package/src/core/nodeTypes.ts +6 -0
  169. package/src/core/shaders.ts +567 -0
  170. package/src/core/states.ts +91 -0
  171. package/src/core/timings.ts +261 -0
  172. package/src/core/utils.ts +222 -0
  173. package/src/devtools/index.ts +1 -1
  174. package/src/index.ts +3 -3
  175. package/src/primitives/Column.tsx +10 -12
  176. package/src/primitives/FPSCounter.tsx +15 -1
  177. package/src/primitives/Grid.tsx +57 -33
  178. package/src/primitives/Image.tsx +36 -0
  179. package/src/primitives/KeepAlive.tsx +124 -0
  180. package/src/primitives/Lazy.tsx +66 -37
  181. package/src/primitives/Marquee.tsx +149 -0
  182. package/src/primitives/Preserve.tsx +18 -0
  183. package/src/primitives/Row.tsx +13 -14
  184. package/src/primitives/Suspense.tsx +39 -0
  185. package/src/primitives/Virtual.tsx +478 -0
  186. package/src/primitives/VirtualGrid.tsx +220 -0
  187. package/src/primitives/Visible.tsx +1 -2
  188. package/src/primitives/announcer/announcer.ts +16 -10
  189. package/src/primitives/announcer/index.ts +12 -2
  190. package/src/primitives/announcer/speech.ts +188 -27
  191. package/src/primitives/createFocusStack.tsx +18 -7
  192. package/src/primitives/createTag.tsx +31 -0
  193. package/src/primitives/index.ts +18 -4
  194. package/src/primitives/types.ts +12 -2
  195. package/src/primitives/useFocusManager.ts +3 -3
  196. package/src/primitives/useHold.ts +69 -0
  197. package/src/primitives/useMouse.ts +306 -67
  198. package/src/primitives/utils/chainFunctions.ts +40 -9
  199. package/src/primitives/utils/createBlurredImage.ts +366 -0
  200. package/src/primitives/utils/createSpriteMap.ts +6 -4
  201. package/src/primitives/utils/handleNavigation.ts +300 -84
  202. package/src/primitives/utils/withScrolling.ts +91 -18
  203. package/src/render.ts +10 -8
  204. package/src/solidOpts.ts +31 -24
  205. package/src/types.ts +1 -15
  206. package/src/utils.ts +11 -1
@@ -0,0 +1,359 @@
1
+ import { Config, isDev } from './config.js';
2
+ export type * from './focusKeyTypes.js';
3
+ import { ElementNode } from './elementNode.js';
4
+ import type {
5
+ KeyNameOrKeyCode,
6
+ KeyHoldOptions,
7
+ KeyMap,
8
+ } from './focusKeyTypes.js';
9
+ import { isFunction } from './utils.js';
10
+
11
+ const keyMapEntries: Record<KeyNameOrKeyCode, string> = {
12
+ ArrowLeft: 'Left',
13
+ ArrowRight: 'Right',
14
+ ArrowUp: 'Up',
15
+ ArrowDown: 'Down',
16
+ Enter: 'Enter',
17
+ l: 'Last',
18
+ ' ': 'Space',
19
+ Backspace: 'Back',
20
+ Escape: 'Escape',
21
+ };
22
+
23
+ const keyHoldMapEntries: Record<KeyNameOrKeyCode, string> = {
24
+ // Enter: 'EnterHold',
25
+ };
26
+
27
+ const flattenKeyMap = (keyMap: any, targetMap: any): void => {
28
+ for (const [key, value] of Object.entries(keyMap)) {
29
+ if (Array.isArray(value)) {
30
+ value.forEach((v) => {
31
+ targetMap[v] = key;
32
+ });
33
+ } else if (value === null) {
34
+ delete targetMap[key];
35
+ } else {
36
+ targetMap[value as keyof any] = key;
37
+ }
38
+ }
39
+ };
40
+
41
+ let needFocusDebugStyles = true;
42
+ const addFocusDebug = (
43
+ prevFocusPath: ElementNode[],
44
+ newFocusPath: ElementNode[],
45
+ ) => {
46
+ if (needFocusDebugStyles) {
47
+ const style = document.createElement('style');
48
+ style.type = 'text/css';
49
+ style.innerHTML = `
50
+ [data-focus="3"] {
51
+ border: 2px solid rgba(255, 33, 33, 0.2);
52
+ border-radius: 5px;
53
+ transition: border-color 0.3s ease;
54
+ }
55
+
56
+ [data-focus="2"] {
57
+ border: 2px solid rgba(255, 33, 33, 0.4);
58
+ border-radius: 5px;
59
+ transition: border-color 0.3s ease;
60
+ }
61
+
62
+ [data-focus="1"] {
63
+ border: 4px solid rgba(255, 33, 33, 0.9);
64
+ border-radius: 5px;
65
+ transition: border-color 0.5s ease;
66
+ }
67
+ `;
68
+ document.head.appendChild(style);
69
+ needFocusDebugStyles = false;
70
+ }
71
+
72
+ prevFocusPath.forEach((elm) => {
73
+ elm.data = {
74
+ ...elm.data,
75
+ focus: undefined,
76
+ };
77
+ });
78
+
79
+ newFocusPath.forEach((elm, i) => {
80
+ elm.data = {
81
+ ...elm.data,
82
+ focus: i + 1,
83
+ };
84
+ });
85
+ };
86
+
87
+ let activeElement: ElementNode | undefined;
88
+ export const setActiveElement = (elm: ElementNode) => {
89
+ updateFocusPath(elm, activeElement);
90
+ activeElement = elm;
91
+ // Callback for libraries to use signals / refs
92
+ Config.setActiveElement(elm);
93
+ };
94
+
95
+ let focusPath: ElementNode[] = [];
96
+ const updateFocusPath = (
97
+ currentFocusedElm: ElementNode,
98
+ prevFocusedElm: ElementNode | undefined,
99
+ ) => {
100
+ let current = currentFocusedElm;
101
+ const fp: ElementNode[] = [];
102
+ while (current) {
103
+ if (
104
+ !current.states.has(Config.focusStateKey) ||
105
+ current === currentFocusedElm
106
+ ) {
107
+ current.states.add(Config.focusStateKey);
108
+ current.onFocus?.call(current, currentFocusedElm, prevFocusedElm);
109
+ current.onFocusChanged?.call(
110
+ current,
111
+ true,
112
+ currentFocusedElm,
113
+ prevFocusedElm,
114
+ );
115
+ }
116
+ fp.push(current);
117
+ current = current.parent!;
118
+ }
119
+
120
+ focusPath.forEach((elm) => {
121
+ if (!fp.includes(elm)) {
122
+ elm.states.remove(Config.focusStateKey);
123
+ elm.onBlur?.call(elm, currentFocusedElm, prevFocusedElm!);
124
+ elm.onFocusChanged?.call(elm, false, currentFocusedElm, prevFocusedElm);
125
+ }
126
+ });
127
+
128
+ if (Config.focusDebug) {
129
+ addFocusDebug(focusPath, fp);
130
+ }
131
+
132
+ focusPath = fp;
133
+ return fp;
134
+ };
135
+
136
+ let lastGlobalKeyPressTime = 0;
137
+
138
+ const propagateKeyPress = (
139
+ e: KeyboardEvent,
140
+ mappedEvent?: string,
141
+ isHold: boolean = false,
142
+ isUp: boolean = false,
143
+ ): boolean => {
144
+ const currentTime = performance.now();
145
+ if (!isUp && Config.throttleInput) {
146
+ if (currentTime - lastGlobalKeyPressTime < Config.throttleInput) {
147
+ if (isDev && Config.keyDebug) {
148
+ console.log(
149
+ `Keypress throttled by global Config.throttleInput: ${Config.throttleInput}ms`,
150
+ );
151
+ }
152
+ return false;
153
+ }
154
+ lastGlobalKeyPressTime = currentTime;
155
+ }
156
+ let finalFocusElm: ElementNode | undefined;
157
+ let handlerAvailable: ElementNode | undefined;
158
+ const numItems = focusPath.length;
159
+ const captureEvent =
160
+ `onCapture${mappedEvent || e.key}` + isUp ? 'Release' : '';
161
+ const captureKey = isUp ? 'onCaptureKeyRelease' : 'onCaptureKey';
162
+
163
+ for (let i = numItems - 1; i >= 0; i--) {
164
+ const elm = focusPath[i]!;
165
+
166
+ // Check throttle for capture phase
167
+ if (elm.throttleInput) {
168
+ if (
169
+ elm._lastAnyKeyPressTime !== undefined &&
170
+ currentTime - elm._lastAnyKeyPressTime < elm.throttleInput
171
+ ) {
172
+ return true;
173
+ }
174
+ }
175
+
176
+ const captureHandler = elm[captureEvent] || elm[captureKey];
177
+ if (
178
+ isFunction(captureHandler) &&
179
+ captureHandler.call(elm, e, elm, finalFocusElm, mappedEvent) === true
180
+ ) {
181
+ elm._lastAnyKeyPressTime = currentTime;
182
+ return true;
183
+ }
184
+ }
185
+
186
+ let eventHandlerKey: string | undefined;
187
+ let releaseEventHandlerKey: string | undefined;
188
+ let fallbackHandlerKey: 'onKeyHold' | 'onKeyPress' | undefined;
189
+
190
+ if (mappedEvent) {
191
+ eventHandlerKey = `on${mappedEvent}`;
192
+ releaseEventHandlerKey = `on${mappedEvent}Release`;
193
+ }
194
+
195
+ if (!isUp) {
196
+ fallbackHandlerKey = isHold ? 'onKeyHold' : 'onKeyPress';
197
+ }
198
+
199
+ for (let i = 0; i < numItems; i++) {
200
+ const elm = focusPath[i]!;
201
+ if (!finalFocusElm) {
202
+ finalFocusElm = elm;
203
+ }
204
+
205
+ // Check throttle for bubbling phase
206
+ if (elm.throttleInput) {
207
+ if (
208
+ elm._lastAnyKeyPressTime !== undefined &&
209
+ currentTime - elm._lastAnyKeyPressTime < elm.throttleInput
210
+ ) {
211
+ return true;
212
+ }
213
+ }
214
+
215
+ let handled = false;
216
+
217
+ // Check for the release event handler if isUp is true and the key is defined
218
+ if (isUp && releaseEventHandlerKey) {
219
+ const eventHandler = elm[releaseEventHandlerKey];
220
+ if (isFunction(eventHandler)) {
221
+ handlerAvailable = elm;
222
+ if (eventHandler.call(elm, e, elm, finalFocusElm) === true)
223
+ handled = true;
224
+ }
225
+ } else if (!isUp && eventHandlerKey) {
226
+ // Check for the regular event handler if isUp is false and the key is defined
227
+ const eventHandler = elm[eventHandlerKey];
228
+ if (isFunction(eventHandler)) {
229
+ handlerAvailable = elm;
230
+ if (eventHandler.call(elm, e, elm, finalFocusElm) === true)
231
+ handled = true;
232
+ }
233
+ }
234
+
235
+ // Check for the fallback handler if its key is defined and not already handled by specific key handler
236
+ if (!handled && fallbackHandlerKey) {
237
+ const fallbackHandler = elm[fallbackHandlerKey];
238
+ if (isFunction(fallbackHandler)) {
239
+ handlerAvailable = elm;
240
+ if (
241
+ fallbackHandler.call(elm, e, mappedEvent, elm, finalFocusElm) === true
242
+ )
243
+ handled = true;
244
+ }
245
+ }
246
+
247
+ if (handled) {
248
+ elm._lastAnyKeyPressTime = currentTime;
249
+ return true;
250
+ }
251
+ }
252
+
253
+ if (isDev && Config.keyDebug && !isUp) {
254
+ if (handlerAvailable) {
255
+ console.log(
256
+ `Keypress bubbled, key="${e.key}", mappedEvent=${mappedEvent}, isHold=${isHold}, isUp=${isUp}`,
257
+ handlerAvailable,
258
+ );
259
+ } else {
260
+ console.log(
261
+ `No event handler available for keypress: key="${e.key}", mappedEvent=${mappedEvent}, isHold=${isHold}, isUp=${isUp}`,
262
+ );
263
+ }
264
+ }
265
+
266
+ return false;
267
+ };
268
+
269
+ const DEFAULT_KEY_HOLD_THRESHOLD = 500; // ms
270
+ const keyHoldTimeouts: { [key: KeyNameOrKeyCode]: number | true } = {};
271
+
272
+ const handleKeyEvents = (
273
+ delay: number,
274
+ keydown?: KeyboardEvent,
275
+ keyup?: KeyboardEvent,
276
+ ) => {
277
+ if (keydown) {
278
+ const key: KeyNameOrKeyCode = keydown.key || keydown.keyCode;
279
+ const mappedKeyHoldEvent =
280
+ keyHoldMapEntries[keydown.key] || keyHoldMapEntries[keydown.keyCode];
281
+ const mappedKeyEvent =
282
+ keyMapEntries[keydown.key] || keyMapEntries[keydown.keyCode];
283
+ if (mappedKeyHoldEvent) {
284
+ if (!keyHoldTimeouts[key]) {
285
+ keyHoldTimeouts[key] = window.setTimeout(() => {
286
+ keyHoldTimeouts[key] = true;
287
+ propagateKeyPress(keydown, mappedKeyHoldEvent, true);
288
+ }, delay);
289
+ }
290
+ return;
291
+ }
292
+
293
+ propagateKeyPress(keydown, mappedKeyEvent, false);
294
+ } else if (keyup) {
295
+ const key: KeyNameOrKeyCode = keyup.key || keyup.keyCode;
296
+ const mappedKeyEvent =
297
+ keyMapEntries[keyup.key] || keyMapEntries[keyup.keyCode];
298
+ if (keyHoldTimeouts[key] === true) {
299
+ delete keyHoldTimeouts[key];
300
+ } else if (keyHoldTimeouts[key]) {
301
+ clearTimeout(keyHoldTimeouts[key]);
302
+ delete keyHoldTimeouts[key];
303
+ // trigger key down event when hold didn't finish
304
+ propagateKeyPress(keyup, mappedKeyEvent, false);
305
+ }
306
+
307
+ propagateKeyPress(keyup, mappedKeyEvent, false, true);
308
+ }
309
+ };
310
+
311
+ interface FocusManagerOptions {
312
+ userKeyMap?: Partial<KeyMap>;
313
+ keyHoldOptions?: KeyHoldOptions;
314
+ ownerContext?: (cb: () => void) => void;
315
+ }
316
+
317
+ export const useFocusManager = ({
318
+ userKeyMap,
319
+ keyHoldOptions,
320
+ ownerContext = (cb) => {
321
+ cb();
322
+ },
323
+ }: FocusManagerOptions = {}) => {
324
+ if (userKeyMap) {
325
+ flattenKeyMap(userKeyMap, keyMapEntries);
326
+ }
327
+
328
+ if (keyHoldOptions?.userKeyHoldMap) {
329
+ flattenKeyMap(keyHoldOptions.userKeyHoldMap, keyHoldMapEntries);
330
+ }
331
+
332
+ const delay = keyHoldOptions?.holdThreshold || DEFAULT_KEY_HOLD_THRESHOLD;
333
+ const runKeyEvent = handleKeyEvents.bind(null, delay);
334
+
335
+ // Owner context is for frameworks that need effects
336
+ const keyPressHandler = (event: KeyboardEvent) =>
337
+ ownerContext(() => {
338
+ runKeyEvent(event, undefined);
339
+ });
340
+
341
+ const keyUpHandler = (event: KeyboardEvent) =>
342
+ ownerContext(() => {
343
+ runKeyEvent(undefined, event);
344
+ });
345
+
346
+ document.addEventListener('keyup', keyUpHandler);
347
+ document.addEventListener('keydown', keyPressHandler);
348
+
349
+ return {
350
+ cleanup: () => {
351
+ document.removeEventListener('keydown', keyPressHandler);
352
+ document.removeEventListener('keyup', keyUpHandler);
353
+ for (const [_, timeout] of Object.entries(keyHoldTimeouts)) {
354
+ if (timeout && timeout !== true) clearTimeout(timeout);
355
+ }
356
+ },
357
+ focusPath: () => focusPath,
358
+ };
359
+ };
@@ -0,0 +1,13 @@
1
+ export * from './elementNode.js';
2
+ export * from './lightningInit.js';
3
+ export * from './nodeTypes.js';
4
+ export * from './utils.js';
5
+ export * from './intrinsicTypes.js';
6
+ export * from './focusKeyTypes.js';
7
+ export * from './config.js';
8
+ export * from './shaders.js';
9
+ export type * from '@lightningjs/renderer';
10
+ export { type AnimationSettings } from './intrinsicTypes.js';
11
+ // hopefully fix up webpack error
12
+ import { assertTruthy, deg2Rad } from '@lightningjs/renderer/utils';
13
+ export { assertTruthy, deg2Rad };
@@ -0,0 +1,199 @@
1
+ import * as lngr from '@lightningjs/renderer';
2
+ import { ElementNode, type RendererNode } from './elementNode.js';
3
+ import { NodeStates } from './states.js';
4
+ import {
5
+ ShaderBorderProps,
6
+ ShaderHolePunchProps,
7
+ ShaderLinearGradientProps,
8
+ ShaderRadialGradientProps,
9
+ ShaderRoundedProps,
10
+ ShaderShadowProps,
11
+ } from './shaders.js';
12
+ import {
13
+ EventHandlers,
14
+ DefaultKeyMap,
15
+ KeyHoldMap,
16
+ FocusNode,
17
+ } from './focusKeyTypes.js';
18
+ import type { JSXElement } from 'solid-js';
19
+
20
+ export type AnimationSettings = Partial<lngr.AnimationSettings>;
21
+
22
+ export type AddColorString<T> = {
23
+ [K in keyof T]: K extends `color${string}` ? string | number : T[K];
24
+ };
25
+
26
+ export interface BorderStyleObject {
27
+ width: number;
28
+ color: number | string;
29
+ }
30
+
31
+ export type DollarString = `$${string}`;
32
+ export type BorderStyle = BorderStyleObject;
33
+ export type BorderRadius = number | number[];
34
+
35
+ export interface Effects {
36
+ linearGradient?: Partial<ShaderLinearGradientProps>;
37
+ radialGradient?: Partial<ShaderRadialGradientProps>;
38
+ holePunch?: Partial<ShaderHolePunchProps>;
39
+ shadow?: Partial<ShaderShadowProps>;
40
+ rounded?: Partial<ShaderRoundedProps>;
41
+ borderRadius?: Partial<BorderRadius>;
42
+ border?: Partial<ShaderBorderProps>;
43
+ }
44
+
45
+ export type StyleEffects = Effects;
46
+
47
+ export type NewOmit<T, K extends PropertyKey> = {
48
+ [P in keyof T as Exclude<P, K>]: T[P];
49
+ };
50
+
51
+ export type RemoveUnderscoreProps<T> = {
52
+ [K in keyof T as K extends `_${string}` ? never : K]: T[K];
53
+ };
54
+
55
+ type RendererText = AddColorString<
56
+ Partial<Omit<lngr.ITextNodeProps, 'debug' | 'shader' | 'parent'>>
57
+ >;
58
+
59
+ type CleanElementNode = NewOmit<
60
+ RemoveUnderscoreProps<ElementNode>,
61
+ | 'parent'
62
+ | 'insertChild'
63
+ | 'removeChild'
64
+ | 'selectedNode'
65
+ | 'shader'
66
+ | 'animate'
67
+ | 'chain'
68
+ | 'start'
69
+ | 'isTextNode'
70
+ | 'getText'
71
+ | 'destroy'
72
+ | 'hasChildren'
73
+ | 'getChildById'
74
+ | 'searchChildrenById'
75
+ | 'states'
76
+ | 'requiresLayout'
77
+ | 'updateLayout'
78
+ | 'render'
79
+ | 'style'
80
+ >;
81
+ /** Node text, children of a ElementNode of type TextNode */
82
+ export interface ElementText
83
+ extends NewOmit<
84
+ ElementNode,
85
+ '_type' | 'parent' | 'children' | 'src' | 'scale'
86
+ >,
87
+ NewOmit<RendererText, 'x' | 'y' | 'w' | 'h'> {
88
+ _type: 'textNode';
89
+ parent?: ElementNode;
90
+ children: TextNode[];
91
+ text: string;
92
+ style: TextStyles;
93
+ }
94
+
95
+ export interface TextNode {
96
+ _type: 'text';
97
+ parent?: ElementText;
98
+ text: string;
99
+ [key: string]: any;
100
+ }
101
+
102
+ export interface NodeProps
103
+ extends RendererNode,
104
+ EventHandlers<DefaultKeyMap>,
105
+ EventHandlers<KeyHoldMap>,
106
+ FocusNode,
107
+ Partial<
108
+ NewOmit<
109
+ CleanElementNode,
110
+ | 'children'
111
+ | 'text'
112
+ | 'lng'
113
+ | 'rendered'
114
+ | 'renderer'
115
+ | 'emit'
116
+ | 'preFlexwidth'
117
+ | 'preFlexHeight'
118
+ >
119
+ > {
120
+ states?: NodeStates;
121
+ style?: NodeStyles;
122
+ children?: JSXElement | undefined;
123
+ }
124
+ export interface NodeStyles extends NewOmit<NodeProps, 'style'> {
125
+ [key: `$${string}`]: NodeProps;
126
+ }
127
+
128
+ export interface TextProps
129
+ extends RendererText,
130
+ Partial<
131
+ NewOmit<
132
+ CleanElementNode,
133
+ | 'lng'
134
+ | 'rendered'
135
+ | 'renderer'
136
+ | 'alignItems'
137
+ | 'autosize'
138
+ | 'children'
139
+ | 'data'
140
+ | 'direction'
141
+ | 'display'
142
+ | 'flexBoundary'
143
+ | 'flexDirection'
144
+ | 'gap'
145
+ | 'justifyContent'
146
+ | 'forwardFocus'
147
+ | 'forwardStates'
148
+ | 'linearGradient'
149
+ | 'src'
150
+ | 'scale'
151
+ | 'texture'
152
+ | 'textureOptions'
153
+ >
154
+ > {
155
+ states?: NodeStates;
156
+ fontWeight?: number | string;
157
+ style?: TextStyles;
158
+ children?: string | string[] | undefined;
159
+ }
160
+
161
+ export interface TextStyles extends NewOmit<TextProps, 'style'> {
162
+ [key: `$${string}`]: TextProps;
163
+ }
164
+
165
+ export type Styles = NodeStyles | TextStyles;
166
+
167
+ // TODO: deprecated
168
+ export interface IntrinsicNodeProps extends NodeProps {}
169
+ export interface IntrinsicNodeStyleProps extends NodeStyles {}
170
+ export interface IntrinsicTextNodeStyleProps extends TextStyles {}
171
+
172
+ export type AnimationEvents = 'animating' | 'tick' | 'stopped';
173
+ export type AnimationEventHandler = (
174
+ controller: lngr.IAnimationController,
175
+ name: string,
176
+ endValue: number,
177
+ props?: any,
178
+ ) => void;
179
+
180
+ type EventPayloadMap = {
181
+ loaded: lngr.NodeLoadedPayload;
182
+ failed: lngr.NodeFailedPayload;
183
+ freed: Event;
184
+ inBounds: Event;
185
+ outOfBounds: Event;
186
+ inViewport: Event;
187
+ outOfViewport: Event;
188
+ };
189
+
190
+ type NodeEvents = keyof EventPayloadMap;
191
+
192
+ type EventHandler<E extends NodeEvents> = (
193
+ target: ElementNode,
194
+ event?: EventPayloadMap[E],
195
+ ) => void;
196
+
197
+ export type OnEvent = Partial<{
198
+ [K in NodeEvents]: EventHandler<K>;
199
+ }>;
@@ -0,0 +1,147 @@
1
+ import * as lng from '@lightningjs/renderer';
2
+ import { DOMRendererMain } from './domRenderer.js';
3
+ import { DOM_RENDERING } from './config.js';
4
+ import {
5
+ ShaderBorderPrefixedProps,
6
+ ShaderHolePunchProps,
7
+ ShaderLinearGradientProps,
8
+ ShaderRadialGradientProps,
9
+ ShaderRoundedProps,
10
+ ShaderShadowPrefixedProps,
11
+ } from './shaders.js';
12
+
13
+ export type SdfFontType = 'ssdf' | 'msdf';
14
+
15
+ /** Based on {@link lng.CoreRenderer} */
16
+ export interface IRendererCoreRenderer {
17
+ mode: 'canvas' | 'webgl' | undefined;
18
+ }
19
+ /** Based on {@link lng.TrFontManager} */
20
+ export interface IRendererFontManager {
21
+ addFontFace: (...a: any[]) => void;
22
+ }
23
+ /** Based on {@link lng.Stage} */
24
+ export interface IRendererStage {
25
+ root: IRendererNode;
26
+ renderer: IRendererCoreRenderer;
27
+ shManager: IRendererShaderManager;
28
+ animationManager: {
29
+ registerAnimation: (anim: any) => void;
30
+ unregisterAnimation: (anim: any) => void;
31
+ };
32
+ loadFont(kind: string, props: any): Promise<void>;
33
+ }
34
+
35
+ /** Based on {@link lng.CoreShaderManager} */
36
+ export interface IRendererShaderManager {
37
+ registerShaderType: (name: string, shader: any) => void;
38
+ }
39
+
40
+ /** Based on {@link lng.CoreShaderNode} */
41
+ export interface IRendererShader {
42
+ shaderType: IRendererShaderType;
43
+ props?: IRendererShaderProps;
44
+ program?: {};
45
+ }
46
+ /** Based on {@link lng.CoreShaderType} */
47
+ export interface IRendererShaderType {}
48
+ export type IRendererShaderProps = Partial<ShaderBorderPrefixedProps> &
49
+ Partial<ShaderShadowPrefixedProps> &
50
+ Partial<ShaderRoundedProps> &
51
+ Partial<ShaderHolePunchProps> &
52
+ Partial<ShaderRadialGradientProps> &
53
+ Partial<ShaderLinearGradientProps>;
54
+
55
+ /** Based on {@link lng.Texture} */
56
+ export interface IRendererTexture {
57
+ props: IRendererTextureProps;
58
+ type: lng.TextureType;
59
+ }
60
+ export interface IRendererTextureProps {}
61
+
62
+ export interface IEventEmitter {
63
+ on: (e: string, cb: (...a: any[]) => void) => void;
64
+ }
65
+
66
+ export interface IRendererNodeShaded extends IEventEmitter {
67
+ stage: IRendererStage;
68
+ id: number;
69
+ animate: (
70
+ props: Partial<lng.INodeAnimateProps<any>>,
71
+ settings: Partial<lng.AnimationSettings>,
72
+ ) => lng.IAnimationController;
73
+ get absX(): number;
74
+ get absY(): number;
75
+ }
76
+
77
+ /** Based on {@link lng.INodeProps} */
78
+ export interface IRendererNodeProps
79
+ extends Omit<lng.INodeProps<lng.CoreShaderNode>, 'shader' | 'parent'> {
80
+ shader: IRendererShader | null;
81
+ parent: IRendererNode | null;
82
+ }
83
+ /** Based on {@link lng.INode} */
84
+ export interface IRendererNode extends IRendererNodeShaded, IRendererNodeProps {
85
+ div?: HTMLElement;
86
+ props: IRendererNodeProps;
87
+ renderState: lng.CoreNodeRenderState;
88
+ }
89
+
90
+ /** Based on {@link lng.ITextNodeProps} */
91
+ export interface IRendererTextNodeProps
92
+ extends Omit<lng.ITextNodeProps, 'shader' | 'parent'> {
93
+ shader: IRendererShader | null;
94
+ parent: IRendererNode | null;
95
+ fontWeight?: string;
96
+ }
97
+ /** Based on {@link lng.ITextNode} */
98
+ export interface IRendererTextNode
99
+ extends IRendererNodeShaded,
100
+ IRendererTextNodeProps {
101
+ div?: HTMLElement;
102
+ props: IRendererTextNodeProps;
103
+ renderState: lng.CoreNodeRenderState;
104
+ }
105
+
106
+ /** Based on {@link lng.RendererMain} */
107
+ export interface IRendererMain extends IEventEmitter {
108
+ stage: IRendererStage;
109
+ root: IRendererNode;
110
+ createTextNode(props: Partial<IRendererTextNodeProps>): IRendererTextNode;
111
+ createNode(props: Partial<IRendererNodeProps>): IRendererNode;
112
+ createShader(kind: string, props: IRendererShaderProps): IRendererShader;
113
+ createTexture(
114
+ kind: keyof lng.TextureMap,
115
+ props: IRendererTextureProps,
116
+ ): IRendererTexture;
117
+ }
118
+
119
+ export let renderer: IRendererMain;
120
+
121
+ export const getRenderer = () => renderer;
122
+
123
+ export function startLightningRenderer(
124
+ options: lng.RendererMainSettings,
125
+ rootId: string | HTMLElement = 'app',
126
+ ) {
127
+ renderer = DOM_RENDERING
128
+ ? new DOMRendererMain(options, rootId)
129
+ : (new lng.RendererMain(options, rootId) as any as IRendererMain);
130
+ return renderer;
131
+ }
132
+ export function loadFonts(fonts: any[]) {
133
+ for (const font of fonts) {
134
+ // WebGL — SDF
135
+ if (
136
+ renderer.stage.renderer.mode === 'webgl' &&
137
+ 'type' in font &&
138
+ (font.type === 'msdf' || font.type === 'ssdf')
139
+ ) {
140
+ renderer.stage.loadFont('sdf', font);
141
+ }
142
+ // Canvas — Web
143
+ else if ('fontUrl' in font && renderer.stage.renderer.mode !== 'webgl') {
144
+ renderer.stage.loadFont('canvas', font);
145
+ }
146
+ }
147
+ }