@lightningtv/core
Advanced tools
Comparing version 2.3.0-beta.1 to 2.3.0-beta.2
@@ -7,2 +7,6 @@ import { createShader } from './lightningInit.js'; | ||
export type RendererNode = AddColorString<Partial<Omit<INodeProps, 'parent' | 'shader'>>>; | ||
type AnimationEvents = 'animating' | 'tick' | 'finished'; | ||
type AnimationEventHandler = (controller: IAnimationController, name: string, endValue: number, props?: any) => void; | ||
type NodeEvents = 'loaded' | 'failed' | 'freed'; | ||
type EventHandler = (target: ElementNode, event?: Event) => void; | ||
export interface ElementNode extends RendererNode { | ||
@@ -18,3 +22,2 @@ [key: string]: unknown; | ||
_effects?: StyleEffects; | ||
_events?: Array<[string, (target: ElementNode, event?: Event) => void]>; | ||
_id: string | undefined; | ||
@@ -43,3 +46,2 @@ _queueDelete?: boolean; | ||
selected?: number; | ||
$states?: Record<string, Styles>; | ||
preFlexwidth?: number; | ||
@@ -73,5 +75,4 @@ preFlexheight?: number; | ||
transition?: Record<string, AnimationSettings | true | false> | true | false; | ||
onAnimationFinished?: (controller: IAnimationController, propKey: string, endValue: number) => void; | ||
onAnimationStarted?: (controller: IAnimationController, propKey: string, endValue: number) => void; | ||
onBeforeLayout?: (this: ElementNode, target: ElementNode) => boolean | void; | ||
onAnimation?: Record<AnimationEvents, AnimationEventHandler>; | ||
onEvent?: Record<NodeEvents, EventHandler>; | ||
onCreate?: (this: ElementNode, target: ElementNode) => void; | ||
@@ -104,4 +105,2 @@ onDestroy?: (this: ElementNode, elm: ElementNode) => Promise<any> | void; | ||
_destroy(): void; | ||
set onEvents(events: Array<[string, (target: ElementNode, event?: any) => void]>); | ||
get onEvents(): Array<[string, (target: ElementNode, event?: any) => void]> | undefined; | ||
set style(values: (Styles | undefined)[] | Styles); | ||
@@ -120,3 +119,3 @@ get style(): Styles; | ||
get autofocus(): any; | ||
requiresLayout(): true | ((this: ElementNode, target: ElementNode) => boolean | void) | undefined; | ||
requiresLayout(): true | ((this: ElementNode, target: ElementNode) => void) | undefined; | ||
set updateLayoutOn(v: any); | ||
@@ -128,2 +127,3 @@ get updateLayoutOn(): any; | ||
} | ||
export {}; | ||
//# sourceMappingURL=elementNode.d.ts.map |
import { renderer, createShader } from './lightningInit.js'; | ||
import States from './states.js'; | ||
import calculateFlex from './flex.js'; | ||
import { log, isArray, isNumber, isFunc, flattenStyles, isINode, isElementNode, isElementText, isTextNode, logRenderTree, } from './utils.js'; | ||
import { log, isArray, isNumber, isFunc, keyExists, flattenStyles, isINode, isElementNode, isElementText, isTextNode, logRenderTree, } from './utils.js'; | ||
import { Config, isDev } from './config.js'; | ||
@@ -30,6 +30,4 @@ import { assertTruthy } from '@lightningjs/renderer/utils'; | ||
const effects = []; | ||
let index = 0; | ||
for (const [type, props] of Object.entries(styleEffects)) { | ||
effects.push({ name: `effect${index}`, type, props }); | ||
index++; | ||
effects.push({ type, props }); | ||
} | ||
@@ -206,12 +204,11 @@ return createShader('DynamicShader', { effects }); | ||
const animationController = this.animate({ [name]: value }, animationSettings); | ||
if (isFunc(this.onAnimationStarted)) { | ||
animationController.once('animating', (controller) => { | ||
this.onAnimationStarted?.call(this, controller, name, value); | ||
}); | ||
if (this.onAnimation) { | ||
const animationEvents = Object.keys(this.onAnimation); | ||
for (const event of animationEvents) { | ||
const handler = this.onAnimation[event]; | ||
animationController.on(event, (controller, props) => { | ||
handler.call(this, controller, name, value, props); | ||
}); | ||
} | ||
} | ||
if (isFunc(this.onAnimationFinished)) { | ||
animationController.once('stopped', (controller) => { | ||
this.onAnimationFinished?.call(this, controller, name, value); | ||
}); | ||
} | ||
return animationController.start(); | ||
@@ -319,9 +316,2 @@ } | ||
} | ||
// Must be set before render | ||
set onEvents(events) { | ||
this._events = events; | ||
} | ||
get onEvents() { | ||
return this._events; | ||
} | ||
set style(values) { | ||
@@ -407,3 +397,3 @@ if (isArray(values)) { | ||
requiresLayout() { | ||
return this.display === 'flex' || this.onBeforeLayout || this.onLayout; | ||
return this.display === 'flex' || this.onLayout; | ||
} | ||
@@ -420,6 +410,2 @@ set updateLayoutOn(v) { | ||
let changedLayout = false; | ||
if (isFunc(this.onBeforeLayout)) { | ||
console.warn('onBeforeLayout is deprecated'); | ||
changedLayout = this.onBeforeLayout.call(this, this) || false; | ||
} | ||
if (this.display === 'flex') { | ||
@@ -446,3 +432,3 @@ if (calculateFlex(this) || changedLayout) { | ||
const states = this.states; | ||
if (this.$states !== undefined) { | ||
if (this._undoStyles || (this.style && keyExists(this.style, states))) { | ||
this._undoStyles = this._undoStyles || []; | ||
@@ -459,3 +445,3 @@ const stylesToUndo = {}; | ||
const newStyles = states.reduce((acc, state) => { | ||
const styles = this.$states[state]; | ||
const styles = this.style[state]; | ||
return styles ? { ...acc, ...styles } : acc; | ||
@@ -509,3 +495,13 @@ }, {}); | ||
if (node._effects) { | ||
props.shader = convertEffectsToShader(node._effects); | ||
let shader; | ||
// states can change effects so don't use cached shader | ||
if (node.style?.effects && !this._states) { | ||
node.style._shader = | ||
node.style._shader || convertEffectsToShader(node._effects); | ||
shader = node.style._shader; | ||
} | ||
else { | ||
shader = convertEffectsToShader(node._effects); | ||
} | ||
props.shader = shader; | ||
} | ||
@@ -588,6 +584,7 @@ if (isElementText(node)) { | ||
isFunc(this.onCreate) && this.onCreate.call(this, node); | ||
node.onEvents && | ||
node.onEvents.forEach(([name, handler]) => { | ||
if (node.onEvent) { | ||
for (const [name, handler] of Object.entries(node.onEvent)) { | ||
node.lng.on(name, (inode, data) => handler(node, data)); | ||
}); | ||
} | ||
} | ||
// L3 Inspector adds div to the lng object | ||
@@ -594,0 +591,0 @@ //@ts-expect-error - div is not in the typings |
@@ -83,4 +83,4 @@ import { Config } from './config.js'; | ||
while (current) { | ||
if (!current.states.has('focus') || current === currentFocusedElm) { | ||
current.states.add('focus'); | ||
if (!current.states.has('$focus') || current === currentFocusedElm) { | ||
current.states.add('$focus'); | ||
current.onFocus?.call(current, currentFocusedElm, prevFocusedElm); | ||
@@ -94,3 +94,3 @@ current.onFocusChanged?.call(current, true, currentFocusedElm, prevFocusedElm); | ||
if (!fp.includes(elm)) { | ||
elm.states.remove('focus'); | ||
elm.states.remove('$focus'); | ||
elm.onBlur?.call(elm, currentFocusedElm, prevFocusedElm); | ||
@@ -97,0 +97,0 @@ elm.onFocusChanged?.call(elm, false, currentFocusedElm, prevFocusedElm); |
@@ -1,2 +0,2 @@ | ||
import { type FadeOutEffectProps, type GlitchEffectProps, type GrayscaleEffectProps, type AnimationSettings as RendererAnimationSettings, type LinearGradientEffectProps, type RadialGradientEffectProps, type RadialProgressEffectProps, type ITextNodeProps } from '@lightningjs/renderer'; | ||
import { type FadeOutEffectProps, type GlitchEffectProps, type GrayscaleEffectProps, type AnimationSettings as RendererAnimationSettings, type LinearGradientEffectProps, type RadialGradientEffectProps, type RadialProgressEffectProps, type ITextNodeProps, ShaderController } from '@lightningjs/renderer'; | ||
import { ElementNode, type RendererNode } from './elementNode.js'; | ||
@@ -66,3 +66,5 @@ import { NodeStates } from './states.js'; | ||
} | ||
export interface NodeStyles extends NodeProps { | ||
export interface NodeStyles extends NewOmit<NodeProps, 'style'> { | ||
[key: `$${string}`]: NodeProps; | ||
_shader?: ShaderController<'DynamicShader'>; | ||
} | ||
@@ -74,3 +76,5 @@ type NestedNodeStyles = NodeStyles | Array<NestedNodeStyles | undefined>; | ||
} | ||
export interface TextStyles extends TextProps { | ||
export interface TextStyles extends NewOmit<TextProps, 'style'> { | ||
[key: `$${string}`]: TextProps; | ||
_shader?: ShaderController<'DynamicShader'>; | ||
} | ||
@@ -77,0 +81,0 @@ type NestedTextStyles = TextStyles | Array<NestedTextStyles | undefined>; |
@@ -1,12 +0,14 @@ | ||
export type NodeStates = string[] | string | Record<string, boolean | undefined>; | ||
export default class States extends Array<string> { | ||
type DollarString = `$${string}`; | ||
export type NodeStates = DollarString[] | DollarString | Record<DollarString, boolean | undefined>; | ||
export default class States extends Array<DollarString> { | ||
private onChange; | ||
constructor(callback: () => void, initialState?: NodeStates); | ||
has(state: string): boolean; | ||
is(state: string): boolean; | ||
add(state: string): void; | ||
toggle(state: string, force?: boolean): void; | ||
has(state: DollarString): boolean; | ||
is(state: DollarString): boolean; | ||
add(state: DollarString): void; | ||
toggle(state: DollarString, force?: boolean): void; | ||
merge(newStates: NodeStates): this; | ||
remove(state: string): void; | ||
remove(state: DollarString): void; | ||
} | ||
export {}; | ||
//# sourceMappingURL=states.d.ts.map |
@@ -9,3 +9,3 @@ import { isArray, isString } from './utils.js'; | ||
else if (isString(initialState)) { | ||
super(initialState); | ||
super(initialState); // Assert as DollarString | ||
} | ||
@@ -56,3 +56,3 @@ else { | ||
this.length = 0; // Clear the current states | ||
this.push(newStates); | ||
this.push(newStates); // Assert as DollarString | ||
} | ||
@@ -59,0 +59,0 @@ else { |
@@ -43,3 +43,3 @@ import { Config, isDev } from './config.js'; | ||
export function keyExists(obj, keys) { | ||
for (const key of keys) { | ||
for (const key in keys) { | ||
if (key in obj) { | ||
@@ -46,0 +46,0 @@ return true; |
{ | ||
"name": "@lightningtv/core", | ||
"version": "2.3.0-beta.1", | ||
"version": "2.3.0-beta.2", | ||
"description": "Lightning TV Core for Universal Renderers", | ||
@@ -5,0 +5,0 @@ "type": "module", |
# Lightning TV Core for Universal Renderers | ||
Provides an abstraction layer for Lightning Renderer which Universal renders like Solid & Vue can use. | ||
## Migrating states | ||
All states in style object must start with a $. So focus: will be $focus. Additionally, any state keys you use must also be prefixed with a $ - `states={{ $active: true }}` | ||
onBeforeLayout is removed | ||
onAnimation added `onAnimation?: Record<AnimationEvents, AnimationEventHandler>;` | ||
onAnimationStarted and onAnimationFinished removed, use onAnimation. | ||
onEvents is now onEvent with signature `onEvent?: Record<NodeEvents, EventHandler>;` | ||
Use effects in style | ||
New caching layer added to effects. So rather than using borderRadius, use `effects: { radius: { radius: 8 }}` (radius is the only weird one like this) |
@@ -74,7 +74,5 @@ import { renderer, createShader } from './lightningInit.js'; | ||
const effects: EffectDescUnion[] = []; | ||
let index = 0; | ||
for (const [type, props] of Object.entries(styleEffects)) { | ||
effects.push({ name: `effect${index}`, type, props } as EffectDescUnion); | ||
index++; | ||
effects.push({ type, props } as EffectDescUnion); | ||
} | ||
@@ -178,2 +176,11 @@ return createShader('DynamicShader', { effects }); | ||
>; | ||
type AnimationEvents = 'animating' | 'tick' | 'finished'; | ||
type AnimationEventHandler = ( | ||
controller: IAnimationController, | ||
name: string, | ||
endValue: number, | ||
props?: any, | ||
) => void; | ||
type NodeEvents = 'loaded' | 'failed' | 'freed'; | ||
type EventHandler = (target: ElementNode, event?: Event) => void; | ||
export interface ElementNode extends RendererNode { | ||
@@ -193,3 +200,2 @@ [key: string]: unknown; | ||
_effects?: StyleEffects; | ||
_events?: Array<[string, (target: ElementNode, event?: Event) => void]>; | ||
_id: string | undefined; | ||
@@ -220,3 +226,2 @@ _queueDelete?: boolean; | ||
selected?: number; | ||
$states?: Record<string, Styles>; | ||
preFlexwidth?: number; | ||
@@ -258,13 +263,4 @@ preFlexheight?: number; | ||
// Events | ||
onAnimationFinished?: ( | ||
controller: IAnimationController, | ||
propKey: string, | ||
endValue: number, | ||
) => void; | ||
onAnimationStarted?: ( | ||
controller: IAnimationController, | ||
propKey: string, | ||
endValue: number, | ||
) => void; | ||
onBeforeLayout?: (this: ElementNode, target: ElementNode) => boolean | void; | ||
onAnimation?: Record<AnimationEvents, AnimationEventHandler>; | ||
onEvent?: Record<NodeEvents, EventHandler>; | ||
onCreate?: (this: ElementNode, target: ElementNode) => void; | ||
@@ -388,14 +384,17 @@ onDestroy?: (this: ElementNode, elm: ElementNode) => Promise<any> | void; | ||
if (isFunc(this.onAnimationStarted)) { | ||
animationController.once('animating', (controller) => { | ||
this.onAnimationStarted?.call(this, controller, name, value); | ||
}); | ||
if (this.onAnimation) { | ||
const animationEvents = Object.keys( | ||
this.onAnimation, | ||
) as AnimationEvents[]; | ||
for (const event of animationEvents) { | ||
const handler = this.onAnimation[event]; | ||
animationController.on( | ||
event, | ||
(controller: IAnimationController, props?: any) => { | ||
handler.call(this, controller, name, value, props); | ||
}, | ||
); | ||
} | ||
} | ||
if (isFunc(this.onAnimationFinished)) { | ||
animationController.once('stopped', (controller) => { | ||
this.onAnimationFinished?.call(this, controller, name, value); | ||
}); | ||
} | ||
return animationController.start(); | ||
@@ -520,15 +519,2 @@ } | ||
// Must be set before render | ||
set onEvents( | ||
events: Array<[string, (target: ElementNode, event?: any) => void]>, | ||
) { | ||
this._events = events; | ||
} | ||
get onEvents(): | ||
| Array<[string, (target: ElementNode, event?: any) => void]> | ||
| undefined { | ||
return this._events; | ||
} | ||
set style(values: (Styles | undefined)[] | Styles) { | ||
@@ -625,3 +611,3 @@ if (isArray(values)) { | ||
requiresLayout() { | ||
return this.display === 'flex' || this.onBeforeLayout || this.onLayout; | ||
return this.display === 'flex' || this.onLayout; | ||
} | ||
@@ -641,6 +627,2 @@ | ||
let changedLayout = false; | ||
if (isFunc(this.onBeforeLayout)) { | ||
console.warn('onBeforeLayout is deprecated'); | ||
changedLayout = this.onBeforeLayout.call(this, this) || false; | ||
} | ||
@@ -672,3 +654,3 @@ if (this.display === 'flex') { | ||
if (this.$states !== undefined) { | ||
if (this._undoStyles || (this.style && keyExists(this.style, states))) { | ||
this._undoStyles = this._undoStyles || []; | ||
@@ -687,3 +669,3 @@ const stylesToUndo: { [key: string]: any } = {}; | ||
const newStyles: Styles = states.reduce((acc, state) => { | ||
const styles = this.$states![state]; | ||
const styles = this.style![state]; | ||
return styles ? { ...acc, ...styles } : acc; | ||
@@ -749,3 +731,12 @@ }, {}); | ||
if (node._effects) { | ||
props.shader = convertEffectsToShader(node._effects); | ||
let shader; | ||
// states can change effects so don't use cached shader | ||
if (node.style?.effects && !this._states) { | ||
node.style._shader = | ||
node.style._shader || convertEffectsToShader(node._effects); | ||
shader = node.style._shader; | ||
} else { | ||
shader = convertEffectsToShader(node._effects); | ||
} | ||
props.shader = shader; | ||
} | ||
@@ -843,6 +834,7 @@ | ||
node.onEvents && | ||
node.onEvents.forEach(([name, handler]) => { | ||
if (node.onEvent) { | ||
for (const [name, handler] of Object.entries(node.onEvent)) { | ||
(node.lng as INode).on(name, (inode, data) => handler(node, data)); | ||
}); | ||
} | ||
} | ||
@@ -849,0 +841,0 @@ // L3 Inspector adds div to the lng object |
@@ -109,4 +109,4 @@ import { Config } from './config.js'; | ||
while (current) { | ||
if (!current.states.has('focus') || current === currentFocusedElm) { | ||
current.states.add('focus'); | ||
if (!current.states.has('$focus') || current === currentFocusedElm) { | ||
current.states.add('$focus'); | ||
current.onFocus?.call(current, currentFocusedElm, prevFocusedElm); | ||
@@ -126,3 +126,3 @@ current.onFocusChanged?.call( | ||
if (!fp.includes(elm)) { | ||
elm.states.remove('focus'); | ||
elm.states.remove('$focus'); | ||
elm.onBlur?.call(elm, currentFocusedElm, prevFocusedElm); | ||
@@ -129,0 +129,0 @@ elm.onFocusChanged?.call(elm, false, currentFocusedElm, prevFocusedElm); |
@@ -10,2 +10,3 @@ import { | ||
type ITextNodeProps, | ||
ShaderController, | ||
} from '@lightningjs/renderer'; | ||
@@ -128,3 +129,6 @@ import { ElementNode, type RendererNode } from './elementNode.js'; | ||
} | ||
export interface NodeStyles extends NodeProps {} | ||
export interface NodeStyles extends NewOmit<NodeProps, 'style'> { | ||
[key: `$${string}`]: NodeProps; | ||
_shader?: ShaderController<'DynamicShader'>; | ||
} | ||
type NestedNodeStyles = NodeStyles | Array<NestedNodeStyles | undefined>; | ||
@@ -161,3 +165,6 @@ | ||
export interface TextStyles extends TextProps {} | ||
export interface TextStyles extends NewOmit<TextProps, 'style'> { | ||
[key: `$${string}`]: TextProps; | ||
_shader?: ShaderController<'DynamicShader'>; | ||
} | ||
type NestedTextStyles = TextStyles | Array<NestedTextStyles | undefined>; | ||
@@ -164,0 +171,0 @@ |
import { isArray, isString } from './utils.js'; | ||
type DollarString = `$${string}`; | ||
export type NodeStates = | ||
| string[] | ||
| string | ||
| Record<string, boolean | undefined>; | ||
| DollarString[] | ||
| DollarString | ||
| Record<DollarString, boolean | undefined>; | ||
export default class States extends Array<string> { | ||
export default class States extends Array<DollarString> { | ||
private onChange: () => void; | ||
@@ -15,3 +16,3 @@ | ||
} else if (isString(initialState)) { | ||
super(initialState); | ||
super(initialState as DollarString); // Assert as DollarString | ||
} else { | ||
@@ -21,3 +22,3 @@ super( | ||
.filter(([_key, value]) => value) | ||
.map(([key]) => key), | ||
.map(([key]) => key as DollarString), // Assert as DollarString | ||
); | ||
@@ -30,11 +31,11 @@ } | ||
has(state: string) { | ||
has(state: DollarString) { | ||
return this.indexOf(state) >= 0; | ||
} | ||
is(state: string) { | ||
is(state: DollarString) { | ||
return this.indexOf(state) >= 0; | ||
} | ||
add(state: string) { | ||
add(state: DollarString) { | ||
if (this.has(state)) { | ||
@@ -47,3 +48,3 @@ return; | ||
toggle(state: string, force?: boolean) { | ||
toggle(state: DollarString, force?: boolean) { | ||
if (force === true) { | ||
@@ -68,6 +69,9 @@ this.add(state); | ||
this.length = 0; // Clear the current states | ||
this.push(newStates); | ||
this.push(newStates as DollarString); // Assert as DollarString | ||
} else { | ||
for (const state in newStates) { | ||
this.toggle(state, newStates[state]); | ||
this.toggle( | ||
state as DollarString, | ||
newStates[state as keyof NodeStates], | ||
); | ||
} | ||
@@ -78,3 +82,3 @@ } | ||
remove(state: string) { | ||
remove(state: DollarString) { | ||
const stateIndexToRemove = this.indexOf(state); | ||
@@ -81,0 +85,0 @@ if (stateIndexToRemove >= 0) { |
@@ -72,3 +72,3 @@ import { INode } from '@lightningjs/renderer'; | ||
) { | ||
for (const key of keys) { | ||
for (const key in keys) { | ||
if (key in obj) { | ||
@@ -75,0 +75,0 @@ return true; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
217425
3292
17