@lightningtv/solid 3.1.6 → 3.1.8

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 (48) hide show
  1. package/dist/src/core/animation.d.ts +1 -1
  2. package/dist/src/core/animation.js.map +1 -1
  3. package/dist/src/core/config.d.ts +2 -2
  4. package/dist/src/core/config.js.map +1 -1
  5. package/dist/src/core/{domRenderer.d.ts → dom-renderer/domRenderer.d.ts} +30 -7
  6. package/dist/src/core/{domRenderer.js → dom-renderer/domRenderer.js} +633 -122
  7. package/dist/src/core/dom-renderer/domRenderer.js.map +1 -0
  8. package/dist/src/core/dom-renderer/domRendererTypes.d.ts +111 -0
  9. package/dist/src/core/dom-renderer/domRendererTypes.js +2 -0
  10. package/dist/src/core/dom-renderer/domRendererTypes.js.map +1 -0
  11. package/dist/src/core/dom-renderer/domRendererUtils.d.ts +23 -0
  12. package/dist/src/core/dom-renderer/domRendererUtils.js +231 -0
  13. package/dist/src/core/dom-renderer/domRendererUtils.js.map +1 -0
  14. package/dist/src/core/elementNode.d.ts +8 -8
  15. package/dist/src/core/elementNode.js +54 -15
  16. package/dist/src/core/elementNode.js.map +1 -1
  17. package/dist/src/core/index.d.ts +4 -2
  18. package/dist/src/core/index.js +1 -2
  19. package/dist/src/core/index.js.map +1 -1
  20. package/dist/src/core/intrinsicTypes.d.ts +16 -6
  21. package/dist/src/core/lightningInit.d.ts +7 -89
  22. package/dist/src/core/lightningInit.js +13 -5
  23. package/dist/src/core/lightningInit.js.map +1 -1
  24. package/dist/src/core/shaders.d.ts +12 -11
  25. package/dist/src/core/shaders.js +0 -90
  26. package/dist/src/core/shaders.js.map +1 -1
  27. package/dist/src/primitives/Grid.jsx +14 -2
  28. package/dist/src/primitives/Grid.jsx.map +1 -1
  29. package/dist/src/primitives/Image.jsx +18 -0
  30. package/dist/src/primitives/Image.jsx.map +1 -1
  31. package/dist/src/render.d.ts +3 -3
  32. package/dist/src/render.js.map +1 -1
  33. package/dist/tsconfig.tsbuildinfo +1 -1
  34. package/package.json +2 -2
  35. package/src/core/animation.ts +6 -3
  36. package/src/core/config.ts +5 -2
  37. package/src/core/{domRenderer.ts → dom-renderer/domRenderer.ts} +738 -164
  38. package/src/core/dom-renderer/domRendererTypes.ts +150 -0
  39. package/src/core/dom-renderer/domRendererUtils.ts +291 -0
  40. package/src/core/elementNode.ts +98 -35
  41. package/src/core/index.ts +4 -2
  42. package/src/core/intrinsicTypes.ts +22 -6
  43. package/src/core/lightningInit.ts +20 -124
  44. package/src/core/shaders.ts +17 -110
  45. package/src/primitives/Grid.tsx +23 -7
  46. package/src/primitives/Image.tsx +20 -0
  47. package/src/render.ts +2 -1
  48. package/dist/src/core/domRenderer.js.map +0 -1
@@ -0,0 +1,150 @@
1
+ import * as lng from '@lightningjs/renderer';
2
+ import { CoreAnimation } from '../intrinsicTypes.js';
3
+ import { EventEmitter } from '@lightningjs/renderer/utils';
4
+ import {
5
+ ShaderBorderPrefixedProps,
6
+ ShaderHolePunchProps,
7
+ ShaderLinearGradientProps,
8
+ ShaderRadialGradientProps,
9
+ ShaderRoundedProps,
10
+ ShaderShadowPrefixedProps,
11
+ } from '../shaders.js';
12
+
13
+ /** Based on {@link lng.CoreRenderer} */
14
+ export interface IRendererCoreRenderer {
15
+ mode: 'canvas' | 'webgl' | undefined;
16
+ boundsMargin?: number | [number, number, number, number];
17
+ }
18
+ /** Based on {@link lng.TrFontManager} */
19
+ export interface IRendererFontManager {
20
+ addFontFace: (...a: any[]) => void;
21
+ }
22
+ /** Based on {@link lng.Stage} */
23
+ export interface IRendererStage {
24
+ root: IRendererNode;
25
+ renderer: IRendererCoreRenderer;
26
+ shManager: IRendererShaderManager;
27
+ animationManager: {
28
+ registerAnimation: (anim: CoreAnimation) => void;
29
+ unregisterAnimation: (anim: CoreAnimation) => void;
30
+ };
31
+ loadFont: lng.Stage['loadFont'];
32
+ reprocessUpdates?: (callback?: () => void) => void;
33
+ cleanup(full: boolean): void;
34
+ }
35
+
36
+ /** Based on {@link lng.CoreShaderManager} */
37
+ export interface IRendererShaderManager {
38
+ registerShaderType: (name: string, shader: any) => void;
39
+ }
40
+
41
+ /** Based on {@link lng.CoreShaderType} */
42
+ export interface IRendererShaderType {}
43
+
44
+ export type IRendererShaderProps = Partial<ShaderBorderPrefixedProps> &
45
+ Partial<ShaderShadowPrefixedProps> &
46
+ Partial<ShaderRoundedProps> &
47
+ Partial<ShaderHolePunchProps> &
48
+ Partial<ShaderRadialGradientProps> &
49
+ Partial<ShaderLinearGradientProps>;
50
+
51
+ /** Based on {@link lng.CoreShaderNode} */
52
+ export interface IRendererShader extends Partial<lng.CoreShaderType> {
53
+ shaderType: IRendererShaderType;
54
+ props?: IRendererShaderProps;
55
+ program?: {};
56
+ }
57
+
58
+ export type ExtractProps<Type> = Type extends { z$__type__Props: infer Props }
59
+ ? Props
60
+ : never;
61
+
62
+ export interface IEventEmitter<
63
+ T extends object = { [s: string]: (target: any, data: any) => void },
64
+ > {
65
+ on<K extends keyof T>(event: Extract<K, string>, listener: T[K]): void;
66
+ once<K extends keyof T>(event: Extract<K, string>, listener: T[K]): void;
67
+ off<K extends keyof T>(event: Extract<K, string>, listener: T[K]): void;
68
+ emit<K extends keyof T>(
69
+ event: Extract<K, string>,
70
+ data: Parameters<any>[1],
71
+ ): void;
72
+ }
73
+
74
+ export interface IRendererNodeShaded extends EventEmitter {
75
+ stage: IRendererStage;
76
+ id: number;
77
+ animate: (
78
+ props: Partial<lng.INodeAnimateProps<any>>,
79
+ settings: Partial<lng.AnimationSettings>,
80
+ ) => lng.IAnimationController;
81
+ get absX(): number;
82
+ get absY(): number;
83
+ }
84
+
85
+ /** Based on {@link lng.INodeProps} */
86
+ export interface IRendererNodeProps
87
+ extends Omit<lng.INodeProps, 'shader' | 'parent'> {
88
+ shader: IRendererShader | null;
89
+ parent: IRendererNode | null;
90
+ }
91
+
92
+ /** Based on {@link lng.CoreNode} */
93
+ export interface IRendererNode extends IRendererNodeShaded, IRendererNodeProps {
94
+ div?: HTMLElement;
95
+ props: IRendererNodeProps;
96
+ renderState: lng.CoreNodeRenderState;
97
+ }
98
+
99
+ /** Based on {@link lng.ITextNodeProps} */
100
+ export interface IRendererTextNodeProps
101
+ extends Omit<lng.ITextNodeProps, 'shader' | 'parent'> {
102
+ shader: IRendererShader | null;
103
+ parent: IRendererNode | null;
104
+ fontWeight?: string;
105
+ fontStretch?: string;
106
+ }
107
+
108
+ /** Based on {@link lng.ITextNode} */
109
+ export interface IRendererTextNode
110
+ extends IRendererNodeShaded,
111
+ IRendererTextNodeProps {
112
+ div?: HTMLElement;
113
+ props: IRendererTextNodeProps;
114
+ renderState: lng.CoreNodeRenderState;
115
+ }
116
+
117
+ /** Based on {@link lng.RendererMain} */
118
+ export interface IRendererMain extends IEventEmitter {
119
+ root: IRendererNode;
120
+ stage: IRendererStage;
121
+ canvas: HTMLCanvasElement;
122
+ createTextNode(props: Partial<IRendererTextNodeProps>): IRendererTextNode;
123
+ createNode(props: Partial<IRendererNodeProps>): IRendererNode;
124
+ createShader: typeof lng.RendererMain.prototype.createShader;
125
+ createTexture: typeof lng.RendererMain.prototype.createTexture;
126
+ //createEffect: typeof lng.RendererMain.prototype.createEffect;
127
+ }
128
+
129
+ export interface DomRendererMainSettings {
130
+ /**
131
+ * The logical width of the application (default: 1920)
132
+ */
133
+ appWidth?: number;
134
+
135
+ /**
136
+ * The logical height of the application (default: 1080)
137
+ */
138
+ appHeight?: number;
139
+
140
+ /**
141
+ * Device logical pixel ratio (default: 1)
142
+ */
143
+ deviceLogicalPixelRatio?: number;
144
+
145
+ /**
146
+ * Bounds margin for the renderer
147
+ * Can be a single number (applied to all sides) or an array [top, right, bottom, left]
148
+ */
149
+ boundsMargin?: number | [number, number, number, number];
150
+ }
@@ -0,0 +1,291 @@
1
+ // Utilities extracted from domRenderer.ts for clarity
2
+ import * as lng from '@lightningjs/renderer';
3
+ import { Config } from '../config.js';
4
+ import { DOMNode } from './domRenderer.js';
5
+ import { isFunc } from '../utils.js';
6
+
7
+ // #region Color & Gradient Utils
8
+
9
+ export const colorToRgba = (c: number) =>
10
+ `rgba(${(c >> 24) & 0xff},${(c >> 16) & 0xff},${(c >> 8) & 0xff},${(c & 0xff) / 255})`;
11
+
12
+ export function buildGradientStops(colors: number[], stops?: number[]): string {
13
+ if (!Array.isArray(colors) || colors.length === 0) return '';
14
+ const positions: number[] = [];
15
+ if (Array.isArray(stops) && stops.length === colors.length) {
16
+ for (let v of stops) {
17
+ if (typeof v !== 'number' || !isFinite(v)) {
18
+ positions.push(0);
19
+ continue;
20
+ }
21
+ let pct = v <= 1 ? v * 100 : v;
22
+ if (pct < 0) pct = 0;
23
+ if (pct > 100) pct = 100;
24
+ positions.push(pct);
25
+ }
26
+ } else {
27
+ const lastIndex = colors.length - 1;
28
+ for (let i = 0; i < colors.length; i++) {
29
+ positions.push(lastIndex === 0 ? 0 : (i / lastIndex) * 100);
30
+ }
31
+ }
32
+ if (positions.length !== colors.length) {
33
+ while (positions.length < colors.length)
34
+ positions.push(positions.length === 0 ? 0 : 100);
35
+ }
36
+ return colors
37
+ .map((color, idx) => `${colorToRgba(color)} ${positions[idx]!.toFixed(2)}%`)
38
+ .join(', ');
39
+ }
40
+
41
+ export function getNodeLineHeight(props: {
42
+ lineHeight?: number;
43
+ fontSize: number;
44
+ }): number {
45
+ return (
46
+ props.lineHeight ?? Config.fontSettings.lineHeight ?? 1.2 * props.fontSize
47
+ );
48
+ }
49
+
50
+ /** Legacy object-fit fall back for unsupported browsers */
51
+ export function computeLegacyObjectFit(
52
+ node: DOMNode,
53
+ img: HTMLImageElement,
54
+ resizeMode: ({ type?: string } & Record<string, any>) | undefined,
55
+ clipX: number,
56
+ clipY: number,
57
+ srcPos: null | { x: number; y: number },
58
+ supportsObjectFit: boolean,
59
+ supportsObjectPosition: boolean,
60
+ ) {
61
+ if (supportsObjectFit && supportsObjectPosition) return;
62
+ const containerW = node.props.w || img.naturalWidth;
63
+ const containerH = node.props.h || img.naturalHeight;
64
+ const naturalW = img.naturalWidth || 1;
65
+ const naturalH = img.naturalHeight || 1;
66
+ let fitType = resizeMode?.type || (srcPos ? 'none' : 'fill');
67
+ let drawW = naturalW;
68
+ let drawH = naturalH;
69
+ switch (fitType) {
70
+ case 'cover': {
71
+ const scale = Math.max(containerW / naturalW, containerH / naturalH);
72
+ drawW = naturalW * scale;
73
+ drawH = naturalH * scale;
74
+ break;
75
+ }
76
+ case 'contain': {
77
+ const scale = Math.min(containerW / naturalW, containerH / naturalH);
78
+ drawW = naturalW * scale;
79
+ drawH = naturalH * scale;
80
+ break;
81
+ }
82
+ case 'fill': {
83
+ drawW = containerW;
84
+ drawH = containerH;
85
+ break;
86
+ }
87
+ }
88
+ let offsetX = (containerW - drawW) * clipX;
89
+ let offsetY = (containerH - drawH) * clipY;
90
+ if (srcPos) {
91
+ offsetX = -srcPos.x;
92
+ offsetY = -srcPos.y;
93
+ }
94
+ const styleParts = [
95
+ 'position: absolute',
96
+ `width: ${Math.round(drawW)}px`,
97
+ `height: ${Math.round(drawH)}px`,
98
+ `left: ${Math.round(offsetX)}px`,
99
+ `top: ${Math.round(offsetY)}px`,
100
+ 'display: block',
101
+ 'pointer-events: none',
102
+ ];
103
+ img.style.removeProperty('object-fit');
104
+ img.style.removeProperty('object-position');
105
+ if (resizeMode?.type === 'none') {
106
+ styleParts[1] = `width: ${naturalW}px`;
107
+ styleParts[2] = `height: ${naturalH}px`;
108
+ }
109
+ img.setAttribute('style', styleParts.join('; ') + ';');
110
+ }
111
+
112
+ export function applySubTextureScaling(
113
+ node: DOMNode,
114
+ img: HTMLImageElement,
115
+ srcPos: InstanceType<lng.TextureMap['SubTexture']>['props'] | null,
116
+ ) {
117
+ if (!srcPos) return;
118
+ const regionW = node.props.srcWidth ?? srcPos.w;
119
+ const regionH = node.props.srcHeight ?? srcPos.h;
120
+ if (!regionW || !regionH) return;
121
+ const targetW = node.props.w || regionW;
122
+ const targetH = node.props.h || regionH;
123
+ if (targetW === regionW && targetH === regionH) return;
124
+ const naturalW = img.naturalWidth || regionW;
125
+ const naturalH = img.naturalHeight || regionH;
126
+ const scaleX = targetW / regionW;
127
+ const scaleY = targetH / regionH;
128
+ img.style.width = naturalW + 'px';
129
+ img.style.height = naturalH + 'px';
130
+ img.style.objectFit = 'none';
131
+ img.style.objectPosition = '0 0';
132
+ img.style.transformOrigin = '0 0';
133
+ const translateX = Math.round(-srcPos.x * scaleX);
134
+ const translateY = Math.round(-srcPos.y * scaleY);
135
+ img.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scaleX}, ${scaleY})`;
136
+ img.style.setProperty('-webkit-transform', img.style.transform);
137
+ if (node.divBg) {
138
+ const styleEl = node.divBg.style;
139
+ if (
140
+ styleEl.maskImage ||
141
+ styleEl.webkitMaskImage ||
142
+ /mask-image:/.test(node.divBg.getAttribute('style') || '')
143
+ ) {
144
+ img.style.display = 'none';
145
+ const maskW = Math.round(naturalW * scaleX);
146
+ const maskH = Math.round(naturalH * scaleY);
147
+ const maskPosX = translateX;
148
+ const maskPosY = translateY;
149
+ styleEl.setProperty?.('mask-size', `${maskW}px ${maskH}px`);
150
+ styleEl.setProperty?.('mask-position', `${maskPosX}px ${maskPosY}px`);
151
+ styleEl.setProperty?.('-webkit-mask-size', `${maskW}px ${maskH}px`);
152
+ styleEl.setProperty?.(
153
+ '-webkit-mask-position',
154
+ `${maskPosX}px ${maskPosY}px`,
155
+ );
156
+ }
157
+ }
158
+ }
159
+ export function applyEasing(
160
+ easing: string | lng.TimingFunction,
161
+ progress: number,
162
+ ): number {
163
+ if (isFunc(easing)) {
164
+ return easing(progress);
165
+ }
166
+
167
+ switch (easing) {
168
+ case 'linear':
169
+ default:
170
+ return progress;
171
+ case 'ease-in':
172
+ return progress * progress;
173
+ case 'ease-out':
174
+ return progress * (2 - progress);
175
+ case 'ease-in-out':
176
+ return progress < 0.5
177
+ ? 2 * progress * progress
178
+ : -1 + (4 - 2 * progress) * progress;
179
+ }
180
+ }
181
+ function interpolate(start: number, end: number, t: number): number {
182
+ return start + (end - start) * t;
183
+ }
184
+
185
+ function interpolateColor(start: number, end: number, t: number): number {
186
+ return (
187
+ (interpolate((start >> 24) & 0xff, (end >> 24) & 0xff, t) << 24) |
188
+ (interpolate((start >> 16) & 0xff, (end >> 16) & 0xff, t) << 16) |
189
+ (interpolate((start >> 8) & 0xff, (end >> 8) & 0xff, t) << 8) |
190
+ interpolate(start & 0xff, end & 0xff, t)
191
+ );
192
+ }
193
+
194
+ export function interpolateProp(
195
+ name: string,
196
+ start: number,
197
+ end: number,
198
+ t: number,
199
+ ): number {
200
+ return name.startsWith('color')
201
+ ? interpolateColor(start, end, t)
202
+ : interpolate(start, end, t);
203
+ }
204
+
205
+ export function compactString(input: string): string {
206
+ return input.replace(/\s*\n\s*/g, ' ');
207
+ }
208
+
209
+ // #region Renderer State Utils
210
+
211
+ export function isRenderStateInBounds(state: lng.CoreNodeRenderState): boolean {
212
+ return state === 4 || state === 8;
213
+ }
214
+
215
+ export function nodeHasTextureSource(node: DOMNode): boolean {
216
+ const textureType = node.props.texture?.type;
217
+ return (
218
+ !!node.props.src ||
219
+ textureType === lng.TextureType.image ||
220
+ textureType === lng.TextureType.subTexture
221
+ );
222
+ }
223
+
224
+ export function normalizeBoundsMargin(
225
+ margin: number | [number, number, number, number] | null | undefined,
226
+ ): [number, number, number, number] {
227
+ if (margin == null) return [0, 0, 0, 0];
228
+ if (typeof margin === 'number') {
229
+ return [margin, margin, margin, margin];
230
+ }
231
+ if (Array.isArray(margin) && margin.length === 4) {
232
+ return [margin[0] ?? 0, margin[1] ?? 0, margin[2] ?? 0, margin[3] ?? 0];
233
+ }
234
+ return [0, 0, 0, 0];
235
+ }
236
+
237
+ export function computeRenderStateForNode(
238
+ node: DOMNode,
239
+ ): lng.CoreNodeRenderState | null {
240
+ const stageRoot = node.stage.root as DOMNode | undefined;
241
+ if (!stageRoot || stageRoot === node) return null;
242
+
243
+ const rootWidth = stageRoot.props.w ?? 0;
244
+ const rootHeight = stageRoot.props.h ?? 0;
245
+ if (rootWidth <= 0 || rootHeight <= 0) return 4;
246
+
247
+ const rootLeft = stageRoot.absX;
248
+ const rootTop = stageRoot.absY;
249
+ const rootRight = rootLeft + rootWidth;
250
+ const rootBottom = rootTop + rootHeight;
251
+
252
+ const [marginTop, marginRight, marginBottom, marginLeft] =
253
+ normalizeBoundsMargin(
254
+ node.props.boundsMargin ?? node.stage.renderer.boundsMargin,
255
+ );
256
+
257
+ const width = node.props.w ?? 0;
258
+ const height = node.props.h ?? 0;
259
+
260
+ const left = node.absX;
261
+ const top = node.absY;
262
+ const right = left + width;
263
+ const bottom = top + height;
264
+
265
+ const expandedLeft = rootLeft - marginLeft;
266
+ const expandedTop = rootTop - marginTop;
267
+ const expandedRight = rootRight + marginRight;
268
+ const expandedBottom = rootBottom + marginBottom;
269
+
270
+ const intersectsBounds =
271
+ right >= expandedLeft &&
272
+ left <= expandedRight &&
273
+ bottom >= expandedTop &&
274
+ top <= expandedBottom;
275
+
276
+ if (!intersectsBounds) {
277
+ return 2;
278
+ }
279
+
280
+ const intersectsViewport =
281
+ right >= rootLeft &&
282
+ left <= rootRight &&
283
+ bottom >= rootTop &&
284
+ top <= rootBottom;
285
+
286
+ if (intersectsViewport) {
287
+ return 8;
288
+ }
289
+
290
+ return 4;
291
+ }