@lightningjs/blits 1.29.2 → 1.29.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/docs/essentials/settings.md +117 -0
- package/docs/sidebar.json +4 -0
- package/jsconfig.json +12 -0
- package/package.json +2 -2
- package/src/component/base/events.js +21 -0
- package/src/component/base/methods.js +34 -0
- package/src/component/setup/index.js +0 -2
- package/src/component.js +153 -3
- package/src/engines/L3/element.js +101 -29
- package/src/engines/L3/launch.js +10 -1
- package/src/launch.js +70 -0
- package/src/lib/codegenerator/generator.js +12 -5
- package/src/lib/componentId.js +14 -1
- package/src/lib/eventListeners.js +29 -0
- package/src/lib/hooks.js +21 -0
- package/src/lib/lifecycle.js +32 -0
- package/src/lib/log.js +11 -0
- package/src/lib/symbols.js +52 -0
- package/src/lib/utils.js +15 -0
- package/src/router/router.js +83 -2
|
@@ -25,6 +25,12 @@ import Settings from '../../settings.js'
|
|
|
25
25
|
// temporary counter to work around shader caching issues
|
|
26
26
|
let counter = 0
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Creates a padding object from a value and direction.
|
|
30
|
+
* @param {number|object|string|undefined} padding - The padding value.
|
|
31
|
+
* @param {string} direction - The layout direction ('vertical' or 'horizontal').
|
|
32
|
+
* @returns {{start: number, end: number, oppositeStart: number, oppositeEnd: number}} The padding object.
|
|
33
|
+
*/
|
|
28
34
|
const createPaddingObject = (padding, direction) => {
|
|
29
35
|
if (padding === undefined) {
|
|
30
36
|
return { start: 0, end: 0, oppositeStart: 0, oppositeEnd: 0 }
|
|
@@ -67,6 +73,11 @@ const createPaddingObject = (padding, direction) => {
|
|
|
67
73
|
return { start: 0, end: 0, oppositeStart: 0, oppositeEnd: 0 }
|
|
68
74
|
}
|
|
69
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Layout function for arranging children in a layout container.
|
|
78
|
+
* @param {object} config - The layout configuration object.
|
|
79
|
+
* @this {import('../../component.js').BlitsElement}
|
|
80
|
+
*/
|
|
70
81
|
const layoutFn = function (config) {
|
|
71
82
|
const position = config.direction === 'vertical' ? 'y' : 'x'
|
|
72
83
|
const oppositePosition = config.direction === 'vertical' ? 'x' : 'y'
|
|
@@ -135,18 +146,40 @@ const layoutFn = function (config) {
|
|
|
135
146
|
}
|
|
136
147
|
}
|
|
137
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Checks if a value is a transition object.
|
|
151
|
+
* @param {any} value - The value to check.
|
|
152
|
+
* @returns {boolean} True if the value is a transition object, false otherwise.
|
|
153
|
+
*/
|
|
138
154
|
const isTransition = (value) => {
|
|
139
155
|
return value !== null && typeof value === 'object' && 'transition' in value === true
|
|
140
156
|
}
|
|
141
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Checks if a string is an object string (starts and ends with curly braces).
|
|
160
|
+
* @param {string} str - The string to check.
|
|
161
|
+
* @returns {boolean} True if the string is an object string, false otherwise.
|
|
162
|
+
*/
|
|
142
163
|
const isObjectString = (str) => {
|
|
143
164
|
return typeof str === 'string' && str.startsWith('{') && str.endsWith('}')
|
|
144
165
|
}
|
|
145
166
|
|
|
167
|
+
/**
|
|
168
|
+
* Parses a string into an object, converting single quotes to double and adding quotes to keys.
|
|
169
|
+
* @param {string} str - The string to parse.
|
|
170
|
+
* @returns {object} The parsed object.
|
|
171
|
+
*/
|
|
146
172
|
const parseToObject = (str) => {
|
|
147
173
|
return JSON.parse(str.replace(/'/g, '"').replace(/([\w-_]+)\s*:/g, '"$1":'))
|
|
148
174
|
}
|
|
149
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Parses a percentage string or returns the value as-is.
|
|
178
|
+
* @param {string|number} v - The value to parse (may be a percentage string).
|
|
179
|
+
* @param {string} base - The base property name ('width' or 'height').
|
|
180
|
+
* @returns {number|string} The parsed value as a number or the original value.
|
|
181
|
+
* @this {{ element: { config: { parent?: { node?: Record<string, number> } } } }}
|
|
182
|
+
*/
|
|
150
183
|
const parsePercentage = function (v, base) {
|
|
151
184
|
if (typeof v !== 'string') {
|
|
152
185
|
return v
|
|
@@ -160,6 +193,11 @@ const parsePercentage = function (v, base) {
|
|
|
160
193
|
return v
|
|
161
194
|
}
|
|
162
195
|
|
|
196
|
+
/**
|
|
197
|
+
* Unpacks a transition value from an object or returns the value as-is.
|
|
198
|
+
* @param {any} v - The value to unpack.
|
|
199
|
+
* @returns {any} The unpacked value.
|
|
200
|
+
*/
|
|
163
201
|
const unpackTransition = (v) => {
|
|
164
202
|
if (typeof v !== 'object' || v === null) return v
|
|
165
203
|
if (v.constructor === Object) {
|
|
@@ -180,8 +218,18 @@ const colorMap = {
|
|
|
180
218
|
right: 'colorRight',
|
|
181
219
|
}
|
|
182
220
|
|
|
221
|
+
/**
|
|
222
|
+
* Default text settings for text nodes (initialized on first use).
|
|
223
|
+
* @type {object|null}
|
|
224
|
+
*/
|
|
183
225
|
let textDefaults = null
|
|
184
226
|
|
|
227
|
+
/**
|
|
228
|
+
* @typedef {import('../../component.js').BlitsElement} BlitsElement
|
|
229
|
+
*
|
|
230
|
+
* This is always a BlitsElement
|
|
231
|
+
* @this {BlitsElement} this
|
|
232
|
+
*/
|
|
185
233
|
const propsTransformer = {
|
|
186
234
|
set parent(v) {
|
|
187
235
|
this.props['parent'] = v === 'root' ? renderer.root : v.node
|
|
@@ -333,34 +381,12 @@ const propsTransformer = {
|
|
|
333
381
|
v[i].props.color = colors.normalize(v[i].props.color)
|
|
334
382
|
}
|
|
335
383
|
}
|
|
336
|
-
const effectNames = {}
|
|
337
384
|
if (this.element.node === undefined) {
|
|
338
|
-
this.element.effectNames = []
|
|
339
385
|
this.props['shader'] = renderer.createShader('DynamicShader', {
|
|
340
386
|
effects: v.map((effect) => {
|
|
341
|
-
|
|
342
|
-
if (effectNames[name] !== undefined) {
|
|
343
|
-
name += ++effectNames[name]
|
|
344
|
-
} else {
|
|
345
|
-
effectNames[name] = 1
|
|
346
|
-
}
|
|
347
|
-
name += this.element.counter
|
|
348
|
-
this.element.effectNames.push(name)
|
|
349
|
-
// temporary add counter to work around shader caching issues
|
|
350
|
-
return renderer.createEffect(effect.type, effect.props, name)
|
|
387
|
+
return renderer.createEffect(effect.type, effect.props)
|
|
351
388
|
}),
|
|
352
389
|
})
|
|
353
|
-
} else {
|
|
354
|
-
for (let i = 0; i < v.length; i++) {
|
|
355
|
-
const name = this.element.effectNames[i]
|
|
356
|
-
// temporary add counter to work around shader caching issues
|
|
357
|
-
const target = this.element.node.shader.props[name]
|
|
358
|
-
const props = Object.keys(v[i].props)
|
|
359
|
-
if (target == undefined) continue
|
|
360
|
-
for (let j = 0; j < props.length; j++) {
|
|
361
|
-
target[props[j]] = v[i].props[props[j]]
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
390
|
}
|
|
365
391
|
},
|
|
366
392
|
set clipping(v) {
|
|
@@ -448,6 +474,10 @@ const propsTransformer = {
|
|
|
448
474
|
}
|
|
449
475
|
|
|
450
476
|
const Element = {
|
|
477
|
+
/**
|
|
478
|
+
* Populates the element with data
|
|
479
|
+
* @param {import('../../component.js').BlitsElementProps} data
|
|
480
|
+
*/
|
|
451
481
|
populate(data) {
|
|
452
482
|
const props = data
|
|
453
483
|
props['node'] = this.config.node
|
|
@@ -459,6 +489,7 @@ const Element = {
|
|
|
459
489
|
this.props.element = this
|
|
460
490
|
|
|
461
491
|
this.props['parent'] = props['parent'] || this.config.parent
|
|
492
|
+
//@ts-ignore This might be a left over from the old code?
|
|
462
493
|
delete props.parent
|
|
463
494
|
|
|
464
495
|
this.props.raw = data
|
|
@@ -506,12 +537,24 @@ const Element = {
|
|
|
506
537
|
})
|
|
507
538
|
}
|
|
508
539
|
},
|
|
540
|
+
/**
|
|
541
|
+
* Set an individual property on the node
|
|
542
|
+
*
|
|
543
|
+
* @this {import('../../component').BlitsElement} this
|
|
544
|
+
*
|
|
545
|
+
* @param {import('../..//component.js').BlitsElementProps} prop
|
|
546
|
+
* @param {any} value
|
|
547
|
+
* @returns {void}
|
|
548
|
+
*/
|
|
509
549
|
set(prop, value) {
|
|
510
550
|
if (value === undefined) return
|
|
551
|
+
// @ts-ignore
|
|
511
552
|
if (this.props.raw[prop] === value) return
|
|
553
|
+
// @ts-ignore
|
|
512
554
|
this.props.raw[prop] = value
|
|
513
555
|
|
|
514
556
|
this.props.props = {}
|
|
557
|
+
// @ts-ignore
|
|
515
558
|
this.props[prop] = unpackTransition(value)
|
|
516
559
|
|
|
517
560
|
const propsKeys = Object.keys(this.props.props)
|
|
@@ -609,7 +652,7 @@ const Element = {
|
|
|
609
652
|
return
|
|
610
653
|
}
|
|
611
654
|
// fire transition end callback when animation ends (if specified)
|
|
612
|
-
if (transition.end && typeof transition.end === 'function') {
|
|
655
|
+
if (this.node !== undefined && transition.end && typeof transition.end === 'function') {
|
|
613
656
|
transition.end.call(this.component, this, prop, this.node[prop])
|
|
614
657
|
}
|
|
615
658
|
// remove the prop from scheduled transitions
|
|
@@ -622,9 +665,7 @@ const Element = {
|
|
|
622
665
|
destroy() {
|
|
623
666
|
if (this.node === null) return
|
|
624
667
|
|
|
625
|
-
Log.debug('Deleting
|
|
626
|
-
this.node.destroy()
|
|
627
|
-
|
|
668
|
+
Log.debug('Deleting Node', this.nodeId)
|
|
628
669
|
// Clearing transition end callback functions
|
|
629
670
|
const transitionProps = Object.keys(this.scheduledTransitions)
|
|
630
671
|
for (let i = 0; i < transitionProps.length; i++) {
|
|
@@ -635,7 +676,32 @@ const Element = {
|
|
|
635
676
|
}
|
|
636
677
|
}
|
|
637
678
|
|
|
638
|
-
//
|
|
679
|
+
// not setting to null and deleting,
|
|
680
|
+
// because transition stopped might still be fired (maybe a renderer fix resolves that)
|
|
681
|
+
this.scheduledTransitions = {}
|
|
682
|
+
|
|
683
|
+
this.component = null
|
|
684
|
+
delete this.component
|
|
685
|
+
|
|
686
|
+
this.config = null
|
|
687
|
+
delete this.config
|
|
688
|
+
|
|
689
|
+
this.counter = null
|
|
690
|
+
delete this.counter
|
|
691
|
+
|
|
692
|
+
this.props.raw = {}
|
|
693
|
+
this.props.element = null
|
|
694
|
+
this.props.props = null
|
|
695
|
+
this.props = {}
|
|
696
|
+
delete this.props
|
|
697
|
+
|
|
698
|
+
this.triggerLayout = null
|
|
699
|
+
delete this.triggerLayout
|
|
700
|
+
|
|
701
|
+
this.forComponent = null
|
|
702
|
+
delete this.forComponent
|
|
703
|
+
|
|
704
|
+
this.node.destroy()
|
|
639
705
|
this.node = null
|
|
640
706
|
},
|
|
641
707
|
get nodeId() {
|
|
@@ -654,6 +720,13 @@ const Element = {
|
|
|
654
720
|
},
|
|
655
721
|
}
|
|
656
722
|
|
|
723
|
+
/**
|
|
724
|
+
* Returns a new Blits Element
|
|
725
|
+
*
|
|
726
|
+
* @param {Object} config - The configuration object for the element
|
|
727
|
+
* @param {import('../../component.js').BlitsComponent} component - The component to which the element belongs
|
|
728
|
+
* @returns {import('../../component.js').BlitsElement} - The new Blits Element
|
|
729
|
+
*/
|
|
657
730
|
export default (config, component) => {
|
|
658
731
|
if (textDefaults === null) {
|
|
659
732
|
textDefaults = {
|
|
@@ -666,7 +739,6 @@ export default (config, component) => {
|
|
|
666
739
|
scheduledTransitions: {},
|
|
667
740
|
config,
|
|
668
741
|
component,
|
|
669
|
-
effectNames: [],
|
|
670
742
|
counter: counter++,
|
|
671
743
|
})
|
|
672
744
|
}
|
package/src/engines/L3/launch.js
CHANGED
|
@@ -26,7 +26,8 @@ import colors from '../../lib/colors/colors.js'
|
|
|
26
26
|
import fontLoader from './fontLoader.js'
|
|
27
27
|
import shaderLoader from './shaderLoader.js'
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
/** @type {RendererMain|null} */
|
|
30
|
+
export let renderer = null
|
|
30
31
|
|
|
31
32
|
const renderEngine = (settings) => {
|
|
32
33
|
const renderMode = 'renderMode' in settings ? settings.renderMode : 'webgl'
|
|
@@ -63,6 +64,14 @@ const textureMemorySettings = (settings) => {
|
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Launch the render engine and application
|
|
69
|
+
*
|
|
70
|
+
* @param {import('../../launch.js').BlitsAppFactory} App - Factory function that returns the application instance (extends BlitsComponent with .quit())
|
|
71
|
+
* @param {HTMLElement} target - The target element to render the application into
|
|
72
|
+
* @param {Partial<import('../../launch.js').BlitsSettings>} [settings] - The settings for the renderer
|
|
73
|
+
*
|
|
74
|
+
*/
|
|
66
75
|
export default (App, target, settings = {}) => {
|
|
67
76
|
renderer = new RendererMain(
|
|
68
77
|
{
|
package/src/launch.js
CHANGED
|
@@ -20,6 +20,30 @@ import { initLog, Log } from './lib/log.js'
|
|
|
20
20
|
import engine from './engine.js'
|
|
21
21
|
import blitsPackageInfo from '../package.json' assert { type: 'json' }
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* @typedef {Object} Font
|
|
25
|
+
* @property {string} family - Name of the font family
|
|
26
|
+
* @property {string} type - Type of font (web, msdf, sdf)
|
|
27
|
+
* @property {string} [file] - Location to the font file
|
|
28
|
+
* @property {string} [json] - Location of the font map (for msdf/sdf)
|
|
29
|
+
* @property {string} [png] - Location of the font png (for msdf/sdf)
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* @typedef {Object} ShaderEffect
|
|
33
|
+
* @property {string} name
|
|
34
|
+
* @property {any} type
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* @typedef {Object} Shader
|
|
38
|
+
* @property {string} name
|
|
39
|
+
* @property {any} type
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @typedef {import('@lightningjs/renderer').RendererMain} RendererMain
|
|
44
|
+
* @type {RendererMain}
|
|
45
|
+
*/
|
|
46
|
+
// @ts-ignore - We ignore this because it will be replaced by the renderer once the app is launched
|
|
23
47
|
export let renderer = {}
|
|
24
48
|
export const stage = {}
|
|
25
49
|
|
|
@@ -37,6 +61,52 @@ async function rendererVersion() {
|
|
|
37
61
|
}
|
|
38
62
|
}
|
|
39
63
|
|
|
64
|
+
/**
|
|
65
|
+
* @typedef {Object} BlitsSettings
|
|
66
|
+
* @property {number} [w] - Width of the Application
|
|
67
|
+
* @property {number} [h] - Height of the Application
|
|
68
|
+
* @property {boolean} [multithreaded] - Whether to enable multithreaded
|
|
69
|
+
* @property {0|1|2|"info"|"warn"|"error"|"debug"[]} [debugLevel] - Debug level for console log messages
|
|
70
|
+
* @property {Font[]} [fonts] - Fonts to be used in the Application
|
|
71
|
+
* @property {ShaderEffect[]} [effects] - Effects to be used by DynamicShader
|
|
72
|
+
* @property {Shader[]} [shaders] - Shaders to be used in the application
|
|
73
|
+
* @property {string} [defaultFont] - Default font family to use in the Application
|
|
74
|
+
* @property {object} [keymap] - Custom keymapping
|
|
75
|
+
* @property {"Proxy"|"defineProperty"} [reactivityMode] - Mode of reactivity
|
|
76
|
+
* @property {"hd"|"720p"|720|"fhd"|"fullhd"|"1080p"|1080|"4k"|"2160p"|2160} [screenResolution] - Screen resolution of the device
|
|
77
|
+
* @property {"low"|"medium"|"high"|"retina"|number} [renderQuality] - Quality of the rendered App
|
|
78
|
+
* @property {number} [pixelRatio] - Custom pixel ratio of the device
|
|
79
|
+
* @property {number} [fpsInterval] - Interval in ms to receive FPS updates
|
|
80
|
+
* @property {number} [webWorkersLimit] - Maximum number of web workers
|
|
81
|
+
* @property {string} [canvasColor] - Background color of the canvas
|
|
82
|
+
* @property {boolean} [inspector] - Enable inspector
|
|
83
|
+
* @property {number|[number,number,number,number]} [viewportMargin] - Extra margin to the viewport
|
|
84
|
+
* @property {number} [gpuMemoryLimit] - Threshold after which textures are freed (deprecated)
|
|
85
|
+
* @property {object} [gpuMemory] - Configures the gpu memory settings
|
|
86
|
+
* @property {"webgl"|"canvas"} [renderMode] - Defines which mode the renderer should operate in
|
|
87
|
+
* @property {number} [holdTimeout] - Time after which a key press is considered a hold
|
|
88
|
+
* @property {HTMLCanvasElement} [canvas] - Custom canvas object used to render the App
|
|
89
|
+
* @property {number} [textureProcessingTimeLimit] - Max time renderer can process textures in a frame
|
|
90
|
+
* @property {Partial<import('@lightningjs/renderer').RendererMainSettings>} [advanced] - Advanced renderer settings
|
|
91
|
+
* @property {boolean} [announcer] - Whether or not the announcer should be activated on initialization
|
|
92
|
+
*/
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Blits App defines the structure of the Blits application.
|
|
96
|
+
* @typedef {import('./component').BlitsComponent} BlitsComponent
|
|
97
|
+
* @typedef {BlitsComponent & { quit: () => void }} BlitsAppComponent
|
|
98
|
+
* @typedef {() => BlitsAppComponent} BlitsAppFactory
|
|
99
|
+
*/
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Launches the Blits application with the given App, target, and settings.
|
|
103
|
+
* Sets up logging, version info, and initializes the renderer and stage singletons.
|
|
104
|
+
*
|
|
105
|
+
* @param {BlitsAppFactory} App - The root BlitsComponent to launch.
|
|
106
|
+
* @param {HTMLElement} target - The DOM element or rendering target.
|
|
107
|
+
* @param {BlitsSettings} settings - Configuration settings for the application.
|
|
108
|
+
* @returns {void}
|
|
109
|
+
*/
|
|
40
110
|
export default (App, target, settings) => {
|
|
41
111
|
Settings.set(settings)
|
|
42
112
|
|
|
@@ -511,6 +511,7 @@ const generateForLoopCode = function (templateObject, parent) {
|
|
|
511
511
|
}
|
|
512
512
|
effect(eff${index}, ${key})
|
|
513
513
|
effects.push(eff${index})
|
|
514
|
+
component[Symbol.for('effects')].push(eff${index})
|
|
514
515
|
`)
|
|
515
516
|
} else {
|
|
516
517
|
// props shouldn't be wrapped in an effect, but simply passed on
|
|
@@ -562,17 +563,21 @@ const generateForLoopCode = function (templateObject, parent) {
|
|
|
562
563
|
|
|
563
564
|
ctx.renderCode.push(`
|
|
564
565
|
let forEffects${forStartCounter}
|
|
565
|
-
|
|
566
|
+
const eff${forStartCounter} = () => {
|
|
566
567
|
component[Symbol.for('removeGlobalEffects')](forEffects${forStartCounter})
|
|
567
568
|
forEffects${forStartCounter} = null
|
|
568
569
|
forEffects${forStartCounter} = forloop${forStartCounter}(${cast(
|
|
569
570
|
result[2],
|
|
570
571
|
':for'
|
|
571
572
|
)}, elms, created${forStartCounter})
|
|
572
|
-
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
component[Symbol.for('effects')].push(eff${forStartCounter})
|
|
576
|
+
|
|
577
|
+
effect(eff${forStartCounter}, ['${effectKey}', ${effectKeys.join(',')}] )
|
|
573
578
|
`)
|
|
574
579
|
|
|
575
|
-
outerScopeEffects.forEach((effect) => {
|
|
580
|
+
outerScopeEffects.forEach((effect, outerScopeEffectsIndex) => {
|
|
576
581
|
const matches = [...effect.matchAll(scopeRegex)]
|
|
577
582
|
|
|
578
583
|
let l = matches.length
|
|
@@ -589,7 +594,7 @@ const generateForLoopCode = function (templateObject, parent) {
|
|
|
589
594
|
}
|
|
590
595
|
|
|
591
596
|
ctx.renderCode.push(`
|
|
592
|
-
|
|
597
|
+
const eff${forStartCounter}_${outerScopeEffectsIndex} = () => {
|
|
593
598
|
void ${refs.join(', ')}
|
|
594
599
|
for(let __index = 0; __index < ${interpolate(result[2])}.length; __index++) {
|
|
595
600
|
if(__index < from${forStartCounter} || __index >= to${forStartCounter}) continue
|
|
@@ -602,7 +607,9 @@ const generateForLoopCode = function (templateObject, parent) {
|
|
|
602
607
|
ctx.renderCode.push(`
|
|
603
608
|
${effect}
|
|
604
609
|
}
|
|
605
|
-
}
|
|
610
|
+
}
|
|
611
|
+
component[Symbol.for('effects')].push(eff${forStartCounter}_${outerScopeEffectsIndex})
|
|
612
|
+
effect(eff${forStartCounter}_${outerScopeEffectsIndex})
|
|
606
613
|
`)
|
|
607
614
|
})
|
|
608
615
|
|
package/src/lib/componentId.js
CHANGED
|
@@ -18,18 +18,31 @@
|
|
|
18
18
|
const counters = {}
|
|
19
19
|
let counter = 0
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Creates a human-readable component ID string for a given component name.
|
|
23
|
+
*
|
|
24
|
+
* @param {string} name - The name of the component.
|
|
25
|
+
* @returns {string} The generated human-readable component ID.
|
|
26
|
+
*/
|
|
21
27
|
export const createHumanReadableId = (name) => {
|
|
22
28
|
return `BlitsComponent::${name}_${(counters[name] = (counters[name] || 0) + 1)}`
|
|
23
29
|
}
|
|
24
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Generates a new unique internal numeric ID.
|
|
33
|
+
*
|
|
34
|
+
* @returns {number} The next unique internal ID.
|
|
35
|
+
*/
|
|
25
36
|
export const createInternalId = () => {
|
|
26
37
|
return ++counter
|
|
27
38
|
}
|
|
28
39
|
|
|
29
40
|
/**
|
|
30
|
-
* Resets the counter value back to 0.
|
|
41
|
+
* Resets the internal counter value back to 0.
|
|
31
42
|
* Used in automated tests to ensure consistent results, not intended
|
|
32
43
|
* to be used in the actual framework code!
|
|
44
|
+
*
|
|
45
|
+
* @returns {void}
|
|
33
46
|
*/
|
|
34
47
|
export const resetCounter = () => {
|
|
35
48
|
counter = 0
|
|
@@ -19,6 +19,15 @@ const eventsMap = new Map()
|
|
|
19
19
|
const callbackCache = new Map()
|
|
20
20
|
|
|
21
21
|
export default {
|
|
22
|
+
/**
|
|
23
|
+
* Registers a listener callback for a specific event on a component.
|
|
24
|
+
*
|
|
25
|
+
* @param {object} component - The component instance to register the listener for.
|
|
26
|
+
* @param {string} event - The name of the event to listen for.
|
|
27
|
+
* @param {Function} cb - The callback function to execute when the event is emitted.
|
|
28
|
+
* @param {number} [priority=0] - The priority of the listener (higher runs first).
|
|
29
|
+
* @returns {void}
|
|
30
|
+
*/
|
|
22
31
|
registerListener(component, event, cb, priority = 0) {
|
|
23
32
|
let componentsMap = eventsMap.get(event)
|
|
24
33
|
if (componentsMap === undefined) {
|
|
@@ -36,6 +45,13 @@ export default {
|
|
|
36
45
|
callbackCache.delete(event) // Invalidate the callbackCache when a new callback is added
|
|
37
46
|
},
|
|
38
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Deregisters a listener for a specific event on a component.
|
|
50
|
+
*
|
|
51
|
+
* @param {object} component - The component instance to deregister the listener for.
|
|
52
|
+
* @param {string} event - The name of the event to stop listening for.
|
|
53
|
+
* @returns {void}
|
|
54
|
+
*/
|
|
39
55
|
deregisterListener(component, event) {
|
|
40
56
|
let componentsMap = eventsMap.get(event)
|
|
41
57
|
if (componentsMap === undefined) {
|
|
@@ -49,6 +65,13 @@ export default {
|
|
|
49
65
|
}
|
|
50
66
|
},
|
|
51
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Executes all registered listeners for a given event, in priority order.
|
|
70
|
+
*
|
|
71
|
+
* @param {string} event - The name of the event to emit.
|
|
72
|
+
* @param {any} params - The parameters to pass to the event listeners.
|
|
73
|
+
* @returns {boolean} True if all listeners executed without stopping propagation, false if any listener returned false.
|
|
74
|
+
*/
|
|
52
75
|
executeListeners(event, params) {
|
|
53
76
|
const componentsMap = eventsMap.get(event)
|
|
54
77
|
if (componentsMap === undefined || componentsMap.size === 0) {
|
|
@@ -78,6 +101,12 @@ export default {
|
|
|
78
101
|
return true // All listeners executed without stopping propagation
|
|
79
102
|
},
|
|
80
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Removes all listeners for a given component from all events.
|
|
106
|
+
*
|
|
107
|
+
* @param {object} component - The component instance to remove listeners for.
|
|
108
|
+
* @returns {void}
|
|
109
|
+
*/
|
|
81
110
|
removeListeners(component) {
|
|
82
111
|
for (const [event, componentsMap] of eventsMap) {
|
|
83
112
|
if (componentsMap.has(component)) {
|
package/src/lib/hooks.js
CHANGED
|
@@ -19,15 +19,36 @@ import symbols from './symbols.js'
|
|
|
19
19
|
|
|
20
20
|
const cbs = {}
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Emits a registered hook for a given identifier, calling the hook with the provided scope and data.
|
|
24
|
+
*
|
|
25
|
+
* @param {string|symbol} hook - The name or symbol of the hook to emit.
|
|
26
|
+
* @param {string} identifier - The unique identifier for the hook set.
|
|
27
|
+
* @param {object} scope - The value of `this` inside the hook function.
|
|
28
|
+
* @param {Array} [data=[]] - Arguments to pass to the hook function.
|
|
29
|
+
*/
|
|
22
30
|
export const emit = (hook, identifier, scope, data = []) => {
|
|
23
31
|
cbs[identifier] && cbs[identifier][hook] && cbs[identifier][hook].apply(scope, data)
|
|
24
32
|
}
|
|
25
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Emits a registered symbol-based hook for a given identifier, calling the hook with the provided scope.
|
|
36
|
+
*
|
|
37
|
+
* @param {string|symbol} hook - The name or symbol of the hook to emit (will be mapped to a symbol).
|
|
38
|
+
* @param {string} identifier - The unique identifier for the hook set.
|
|
39
|
+
* @param {object} scope - The value of `this` inside the hook function.
|
|
40
|
+
*/
|
|
26
41
|
export const privateEmit = (hook, identifier, scope) => {
|
|
27
42
|
const symHook = symbols[hook]
|
|
28
43
|
cbs[identifier] && cbs[identifier][symHook] && cbs[identifier][symHook].apply(scope)
|
|
29
44
|
}
|
|
30
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Registers a set of hooks (functions) for a given identifier.
|
|
48
|
+
*
|
|
49
|
+
* @param {Object<string,Function>|Object<symbol,Function>} hooks - An object whose keys are hook names or symbols and values are functions.
|
|
50
|
+
* @param {string} identifier - The unique identifier for the hook set.
|
|
51
|
+
*/
|
|
31
52
|
export const registerHooks = (hooks = {}, identifier) => {
|
|
32
53
|
cbs[identifier] = {}
|
|
33
54
|
// Combines enumerable keys and symbol properties of the 'hooks' object
|
package/src/lib/lifecycle.js
CHANGED
|
@@ -19,6 +19,10 @@ import { Log } from './log.js'
|
|
|
19
19
|
import { emit, privateEmit } from './hooks.js'
|
|
20
20
|
import symbols from './symbols.js'
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* List of valid lifecycle states for a component.
|
|
24
|
+
* @type {Array<string>}
|
|
25
|
+
*/
|
|
22
26
|
const states = [
|
|
23
27
|
'init', // fired upon component instantiation
|
|
24
28
|
'ready', // fired when component instantiated, reactivity setup done and template spawned
|
|
@@ -31,12 +35,40 @@ const states = [
|
|
|
31
35
|
'exit', // fired when leaving the visible viewport
|
|
32
36
|
]
|
|
33
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Lifecycle state manager for components.
|
|
40
|
+
*
|
|
41
|
+
* @typedef {Object} Lifecycle
|
|
42
|
+
* @property {string|null} previous - The previous lifecycle state.
|
|
43
|
+
* @property {string|null} current - The current lifecycle state.
|
|
44
|
+
* @property {Object} [component] - The component instance this lifecycle belongs to.
|
|
45
|
+
* @property {string|null} state - The current lifecycle state (getter/setter).
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Lifecycle state management object for components.
|
|
50
|
+
* Handles state transitions and emits corresponding hooks.
|
|
51
|
+
*
|
|
52
|
+
* @type {Lifecycle}
|
|
53
|
+
* @this {Lifecycle & {component: Object}}
|
|
54
|
+
* The 'component' property is expected to be set by the component instance at runtime.
|
|
55
|
+
*/
|
|
34
56
|
export default {
|
|
35
57
|
previous: null,
|
|
36
58
|
current: null,
|
|
59
|
+
/**
|
|
60
|
+
* Gets the current lifecycle state.
|
|
61
|
+
* @this {Lifecycle & {component: Object}}
|
|
62
|
+
* @returns {string|null}
|
|
63
|
+
*/
|
|
37
64
|
get state() {
|
|
38
65
|
return this.current
|
|
39
66
|
},
|
|
67
|
+
/**
|
|
68
|
+
* Sets the lifecycle state and emits hooks if the state is valid and changed.
|
|
69
|
+
* @this {Lifecycle & {component: Object}}
|
|
70
|
+
* @param {string} v - The new lifecycle state.
|
|
71
|
+
*/
|
|
40
72
|
set state(v) {
|
|
41
73
|
if (states.indexOf(v) > -1 && (v !== this.current || v === 'focus')) {
|
|
42
74
|
Log.debug(
|
package/src/lib/log.js
CHANGED
|
@@ -19,6 +19,10 @@ import Settings from '../settings.js'
|
|
|
19
19
|
|
|
20
20
|
const n = () => {}
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Returns the current time as a locale time string (hh:mm:ss).
|
|
24
|
+
* @returns {string} The current time string.
|
|
25
|
+
*/
|
|
22
26
|
const time = () =>
|
|
23
27
|
new Date().toLocaleTimeString([], {
|
|
24
28
|
hour: '2-digit',
|
|
@@ -26,6 +30,13 @@ const time = () =>
|
|
|
26
30
|
second: '2-digit',
|
|
27
31
|
})
|
|
28
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Returns a logger object for the given context, with info, warn, debug, and error methods.
|
|
35
|
+
* Logging methods are no-ops if the debug level does not match.
|
|
36
|
+
*
|
|
37
|
+
* @param {string} context - The context label for log messages.
|
|
38
|
+
* @returns {Object} Logger object with info, warn, debug, and error methods.
|
|
39
|
+
*/
|
|
29
40
|
const logger = (context) => {
|
|
30
41
|
const level = Settings.get('debugLevel')
|
|
31
42
|
const log = {}
|
package/src/lib/symbols.js
CHANGED
|
@@ -1,3 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Symbol registry for internal framework properties and generated code.
|
|
3
|
+
*
|
|
4
|
+
* @typedef {Object} BlitsSymbols
|
|
5
|
+
* @property {symbol} currentView
|
|
6
|
+
* @property {symbol} cursorTagStart
|
|
7
|
+
* @property {symbol} computedKeys
|
|
8
|
+
* @property {symbol} destroy
|
|
9
|
+
* @property {symbol} getChildren
|
|
10
|
+
* @property {symbol} holder
|
|
11
|
+
* @property {symbol} id
|
|
12
|
+
* @property {symbol} identifier
|
|
13
|
+
* @property {symbol} index
|
|
14
|
+
* @property {symbol} init
|
|
15
|
+
* @property {symbol} inputEvents
|
|
16
|
+
* @property {symbol} intervals
|
|
17
|
+
* @property {symbol} launched
|
|
18
|
+
* @property {symbol} level
|
|
19
|
+
* @property {symbol} methodKeys
|
|
20
|
+
* @property {symbol} originalState
|
|
21
|
+
* @property {symbol} propKeys
|
|
22
|
+
* @property {symbol} raw
|
|
23
|
+
* @property {symbol} ready
|
|
24
|
+
* @property {symbol} renderer
|
|
25
|
+
* @property {symbol} routes
|
|
26
|
+
* @property {symbol} routerHooks
|
|
27
|
+
* @property {symbol} settings
|
|
28
|
+
* @property {symbol} state
|
|
29
|
+
* @property {symbol} stateKeys
|
|
30
|
+
* @property {symbol} textnode
|
|
31
|
+
* @property {symbol} timeouts
|
|
32
|
+
* @property {symbol} type
|
|
33
|
+
* @property {symbol} watchers
|
|
34
|
+
* @property {symbol} watchKeys
|
|
35
|
+
* @property {symbol} wrapper
|
|
36
|
+
* @property {symbol} children
|
|
37
|
+
* @property {symbol} components
|
|
38
|
+
* @property {symbol} config
|
|
39
|
+
* @property {symbol} isSlot
|
|
40
|
+
* @property {symbol} props
|
|
41
|
+
* @property {symbol} slots
|
|
42
|
+
* @property {symbol} componentType
|
|
43
|
+
* @property {symbol} isComponent
|
|
44
|
+
* @property {symbol} effects
|
|
45
|
+
* @property {symbol} removeGlobalEffects
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Internal symbol registry for Blits framework and generated code.
|
|
50
|
+
*
|
|
51
|
+
* @type {BlitsSymbols}
|
|
52
|
+
*/
|
|
1
53
|
export default {
|
|
2
54
|
currentView: Symbol('currentView'),
|
|
3
55
|
cursorTagStart: Symbol('cursorTagStart'),
|
package/src/lib/utils.js
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A mapping of common screen resolution names to their scale factors relative to 1080p (Full HD).
|
|
3
|
+
*
|
|
4
|
+
* @type {Object<string|number, number>}
|
|
5
|
+
* @property {number} hd - 0.666... (720p)
|
|
6
|
+
* @property {number} '720p' - 0.666... (720p)
|
|
7
|
+
* @property {number} 720 - 0.666... (720p)
|
|
8
|
+
* @property {number} fhd - 1 (Full HD)
|
|
9
|
+
* @property {number} fullhd - 1 (Full HD)
|
|
10
|
+
* @property {number} '1080p' - 1 (Full HD)
|
|
11
|
+
* @property {number} 1080 - 1 (Full HD)
|
|
12
|
+
* @property {number} '4k' - 2 (2160p)
|
|
13
|
+
* @property {number} '2160p' - 2 (2160p)
|
|
14
|
+
* @property {number} 2160 - 2 (2160p)
|
|
15
|
+
*/
|
|
1
16
|
export const screenResolutions = {
|
|
2
17
|
hd: 0.66666667,
|
|
3
18
|
'720p': 0.66666667,
|