@lightningjs/blits 1.30.1 → 1.30.2
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/.editorconfig +7 -7
- package/.eslintignore +1 -1
- package/.eslintrc.cjs +65 -65
- package/.github/workflows/comment-lint-results.yml +124 -124
- package/.github/workflows/comment-test-results.yml +108 -108
- package/.github/workflows/deploy-docs.yml +27 -27
- package/.github/workflows/run-blits-tests.yml +141 -141
- package/.github/workflows/run-lint.yml +127 -127
- package/.husky/pre-commit +1 -1
- package/.vscode/extensions.json +7 -7
- package/.vscode/settings.json +9 -9
- package/CHANGELOG.md +1019 -1012
- package/COPYING +1 -0
- package/LICENSE +202 -202
- package/NOTICE +12 -12
- package/README.md +33 -33
- package/bin/index.js +8 -8
- package/blits.d.ts +6 -6
- package/docs/README.md +19 -19
- package/docs/_sidebar.md +37 -37
- package/docs/built-in/directives.md +50 -50
- package/docs/built-in/for-loop.md +203 -203
- package/docs/built-in/layout.md +168 -168
- package/docs/components/component_state.md +90 -90
- package/docs/components/computed_properties.md +45 -45
- package/docs/components/lifecycle_events.md +42 -42
- package/docs/components/methods.md +45 -45
- package/docs/components/props.md +56 -56
- package/docs/components/user_input.md +186 -186
- package/docs/components/utility_methods.md +320 -320
- package/docs/components/watchers.md +34 -34
- package/docs/essentials/application_root.md +19 -19
- package/docs/essentials/components.md +68 -68
- package/docs/essentials/displaying_images.md +67 -67
- package/docs/essentials/displaying_text.md +135 -135
- package/docs/essentials/element_attributes.md +195 -195
- package/docs/essentials/settings.md +117 -117
- package/docs/essentials/template_syntax.md +83 -83
- package/docs/getting_started/file_structure.md +58 -58
- package/docs/getting_started/getting_started.md +45 -45
- package/docs/getting_started/intro.md +20 -20
- package/docs/index.html +47 -47
- package/docs/performance/lazy-loading.md +161 -161
- package/docs/plugins/global_app_state.md +98 -98
- package/docs/plugins/language.md +172 -172
- package/docs/plugins/text-to-speech-announcer.md +119 -119
- package/docs/plugins/theme.md +176 -176
- package/docs/router/basics.md +175 -175
- package/docs/router/hooks.md +70 -70
- package/docs/router/transitions.md +146 -146
- package/docs/sidebar.json +171 -171
- package/docs/transitions_animations/transitions.md +150 -150
- package/index.d.ts +1099 -1099
- package/index.js +28 -28
- package/jsconfig.json +12 -12
- package/package.json +56 -56
- package/public/assets/fps_sprite.base64.js +1 -1
- package/scripts/postpublish.js +59 -59
- package/scripts/prepublishOnly.js +84 -84
- package/scripts/prepush.js +78 -78
- package/src/announcer/announcer.js +162 -162
- package/src/announcer/speechSynthesis.js +107 -107
- package/src/application.js +100 -100
- package/src/component/base/announcer.js +27 -27
- package/src/component/base/events.js +69 -69
- package/src/component/base/index.js +33 -33
- package/src/component/base/methods.js +244 -244
- package/src/component/base/router.js +41 -41
- package/src/component/base/timeouts_intervals.js +99 -99
- package/src/component/base/utils.js +72 -72
- package/src/component/setup/computed.js +48 -48
- package/src/component/setup/index.js +62 -62
- package/src/component/setup/input.js +30 -30
- package/src/component/setup/methods.js +37 -37
- package/src/component/setup/props.js +62 -62
- package/src/component/setup/props.test.js +238 -238
- package/src/component/setup/routes.js +42 -42
- package/src/component/setup/state.js +59 -59
- package/src/component/setup/watch.js +35 -35
- package/src/component.js +437 -437
- package/src/component.test.js +388 -388
- package/src/components/Circle.js +73 -73
- package/src/components/Circle.orig.js +31 -31
- package/src/components/FPScounter.js +1510 -1510
- package/src/components/FPScounter.orig.js +121 -121
- package/src/components/RouterView.js +81 -81
- package/src/components/RouterView.orig.js +54 -54
- package/src/components/Sprite.js +94 -94
- package/src/components/Sprite.orig.js +58 -58
- package/src/components/index.js +28 -28
- package/src/components/index.orig.js +28 -28
- package/src/constants.js +21 -21
- package/src/engine.js +1 -1
- package/src/engines/L3/element.js +744 -744
- package/src/engines/L3/element.test.js +579 -579
- package/src/engines/L3/fontLoader.js +38 -38
- package/src/engines/L3/index.js +7 -7
- package/src/engines/L3/launch.js +123 -123
- package/src/engines/L3/shaderLoader.js +12 -12
- package/src/engines/index.js +1 -1
- package/src/focus.js +90 -90
- package/src/helpers/deepEqualArray.js +50 -50
- package/src/helpers/deepEqualArray.test.js +127 -127
- package/src/helpers/fetchJson.js +34 -34
- package/src/helpers/sequence.js +26 -26
- package/src/launch.js +123 -123
- package/src/lib/blitsfileconverter/blitsfileconverter.js +52 -52
- package/src/lib/codegenerator/generator.js +788 -788
- package/src/lib/codegenerator/generator.test.js +3801 -3801
- package/src/lib/colors/colors.js +92 -92
- package/src/lib/colors/colors.test.js +290 -290
- package/src/lib/colors/htmlColors.js +168 -168
- package/src/lib/componentId.js +49 -49
- package/src/lib/componentId.test.js +84 -84
- package/src/lib/eventListeners.js +123 -123
- package/src/lib/eventListeners.test.js +236 -236
- package/src/lib/hooks.js +59 -59
- package/src/lib/lifecycle.js +85 -85
- package/src/lib/log.js +117 -117
- package/src/lib/precompiler/precompiler.js +73 -73
- package/src/lib/reactivity/effect.js +93 -93
- package/src/lib/reactivity/effect.test.js +35 -35
- package/src/lib/reactivity/reactive.js +208 -208
- package/src/lib/reactivity/reactive.test.js +27 -27
- package/src/lib/reactivityguard/computedprops.js +218 -218
- package/src/lib/symbols.js +108 -108
- package/src/lib/templateparser/parser.js +386 -386
- package/src/lib/templateparser/parser.test.js +1411 -1411
- package/src/lib/utils.js +27 -27
- package/src/plugin.js +27 -27
- package/src/plugins/appstate.js +26 -26
- package/src/plugins/index.js +20 -20
- package/src/plugins/language.js +152 -152
- package/src/plugins/log.js +25 -25
- package/src/plugins/theme.js +94 -94
- package/src/router/router.js +514 -514
- package/src/router/router.test.js +473 -473
- package/src/router/transitions/fadeIn.js +27 -27
- package/src/router/transitions/fadeInOut.js +33 -33
- package/src/router/transitions/index.js +21 -21
- package/src/router/transitions/slideIn.js +28 -28
- package/src/router/transitions/slideInOut.js +33 -33
- package/src/settings.js +40 -40
- package/vite/blitsFileConverter.js +67 -67
- package/vite/index.js +28 -28
- package/vite/msdfGenerator.js +269 -269
- package/vite/preCompiler.js +42 -42
- package/vite/reactivityGuard.js +45 -45
- package/vscode/data/template-attributes.json +589 -589
package/src/component.js
CHANGED
|
@@ -1,437 +1,437 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright 2023 Comcast Cable Communications Management, LLC
|
|
3
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
* you may not use this file except in compliance with the License.
|
|
5
|
-
* You may obtain a copy of the License at
|
|
6
|
-
*
|
|
7
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
*
|
|
9
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
* See the License for the specific language governing permissions and
|
|
13
|
-
* limitations under the License.
|
|
14
|
-
*
|
|
15
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import { Log } from './lib/log.js'
|
|
19
|
-
import parser from './lib/templateparser/parser.js'
|
|
20
|
-
import codegenerator from './lib/codegenerator/generator.js'
|
|
21
|
-
import { createHumanReadableId, createInternalId } from './lib/componentId.js'
|
|
22
|
-
import { emit } from './lib/hooks.js'
|
|
23
|
-
import { reactive, getRaw } from './lib/reactivity/reactive.js'
|
|
24
|
-
import { effect } from './lib/reactivity/effect.js'
|
|
25
|
-
import Lifecycle from './lib/lifecycle.js'
|
|
26
|
-
import symbols from './lib/symbols.js'
|
|
27
|
-
|
|
28
|
-
import { stage, renderer } from './launch.js'
|
|
29
|
-
|
|
30
|
-
import { default as Base, shared } from './component/base/index.js'
|
|
31
|
-
|
|
32
|
-
import Settings from './settings.js'
|
|
33
|
-
|
|
34
|
-
import setupComponent from './component/setup/index.js'
|
|
35
|
-
import components from './components/index.js'
|
|
36
|
-
|
|
37
|
-
import { plugins } from './plugin.js'
|
|
38
|
-
|
|
39
|
-
// object to store global components
|
|
40
|
-
let globalComponents
|
|
41
|
-
|
|
42
|
-
const required = (name) => {
|
|
43
|
-
throw new Error(`Parameter ${name} is required`)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* @typedef {function} BlitsComponentFactory
|
|
48
|
-
* @param {object} opts - The options for the component instance
|
|
49
|
-
* @param {BlitsComponent} parentEl - The parent element for the component instance
|
|
50
|
-
* @param {BlitsComponent} parentComponent - The parent component for the component instance
|
|
51
|
-
* @param {BlitsComponent} rootComponent - The root component for the component instance
|
|
52
|
-
* @returns {BlitsComponent} - The component instance
|
|
53
|
-
*/
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Structure of a Blits component:
|
|
57
|
-
* Component:
|
|
58
|
-
* <Holder>
|
|
59
|
-
* <Wrapper>
|
|
60
|
-
* []<Elements>
|
|
61
|
-
* </Wrapper>
|
|
62
|
-
* </Holder>
|
|
63
|
-
*
|
|
64
|
-
* A Blits Element
|
|
65
|
-
* @typedef {Object} BlitsAnnouncer
|
|
66
|
-
* @property {(message: string) => void} assertive - Function to assertively announce a message.
|
|
67
|
-
* @property {() => void} clear - Function to clear the announcer.
|
|
68
|
-
* @property {() => void} disable - Function to disable the announcer.
|
|
69
|
-
* @property {() => void} enable - Function to enable the announcer.
|
|
70
|
-
* @property {(delay: number) => void} pause - Function to pause the announcer.
|
|
71
|
-
* @property {(message: string) => void} polite - Function to politely announce a message.
|
|
72
|
-
* @property {(message: string, politeness: 'off'|'polite'|'assertive') => void} announce - Function to announce a message with specified politeness.
|
|
73
|
-
* @property {() => void} stop - Function to stop the announcer.
|
|
74
|
-
* @property {(v: boolean) => void} toggle - Function to toggle the announcer.
|
|
75
|
-
*
|
|
76
|
-
* @typedef {Object} BlitsElementConfig
|
|
77
|
-
* @property {BlitsElement} parent - The parent element of this element.
|
|
78
|
-
* @property {any} node - The node object for the element.
|
|
79
|
-
*
|
|
80
|
-
* @typedef {Object} BlitsElementProps
|
|
81
|
-
* @property {boolean} __textnode - Indicates if the element is a text node.
|
|
82
|
-
* @property {boolean} __layout - Indicates if the element is a layout node.
|
|
83
|
-
* @property {BlitsElement} element - The element to which the props belong?? Do we need this?
|
|
84
|
-
* @property {BlitsElementConfig} config - Configuration object for the element.
|
|
85
|
-
* @property {Object<string, any>} props - The props object containing the properties of the element.
|
|
86
|
-
* @property {Object<string, any>} raw - The raw input props.
|
|
87
|
-
* @property {Object<string, any>} scheduledTransitions - Tracks transitions by property name.
|
|
88
|
-
*
|
|
89
|
-
* @typedef {Object} BlitsElement
|
|
90
|
-
* @property {BlitsComponent} component - Reference to the owning Blits component.
|
|
91
|
-
* @property {BlitsElementConfig} config - Configuration object for the element.
|
|
92
|
-
* @property {number} counter - Unique counter used for shader workarounds. FIXME?
|
|
93
|
-
* @property {string[]} effectNames - Names of active shader effects.
|
|
94
|
-
* @property {any} node - The underlying renderer node (e.g., WebGL node or text node).
|
|
95
|
-
* @property {BlitsElementProps} props - Proxy-like object containing transformed props.
|
|
96
|
-
* @property {any[]} children - WVB I cant see this populated? Filtered list of children owned by this element. FIXME?
|
|
97
|
-
* @property {any} parent - WVB Shortcut to the parent CoreNode?
|
|
98
|
-
* @property {number} nodeId - ID of the CoreNode, if available.
|
|
99
|
-
* @property {string|null} ref - Ref name (if defined).
|
|
100
|
-
* @property {function(Object):void} populate - Initializes the element with props and hooks.
|
|
101
|
-
* @property {function(string, any):void} set - Updates a single property.
|
|
102
|
-
* @property {function(string, any, Object):void} animate - Animates a property with transition options.
|
|
103
|
-
* @property {function():void} destroy - Destroys the underlying node and cancels transitions.
|
|
104
|
-
* @property {function(any): any} triggerLayout - Triggers a layout update for the element.
|
|
105
|
-
*
|
|
106
|
-
* @typedef {Object} BlitsLifecycle
|
|
107
|
-
* @property {BlitsComponent} component - The Blits comonent instance this lifecycle belongs to.
|
|
108
|
-
* @property {'init'|'ready'|'destroyed'} current - The current lifecycle state of the component.
|
|
109
|
-
* @property {'init'|'ready'|'destroyed'|null} previous - The previous lifecycle state of the component.
|
|
110
|
-
* @property {string} state - The current lifecycle state of the component.
|
|
111
|
-
*
|
|
112
|
-
* @typedef {object} BlitsComponent
|
|
113
|
-
* Main properties of the Blits component:
|
|
114
|
-
* @property {string} componentId - The unique identifier for the component instance
|
|
115
|
-
* @property {BlitsLifecycle} lifecycle - The lifecycle object for the component instance
|
|
116
|
-
* @property {BlitsComponent} parent - The parent component of the current component instance
|
|
117
|
-
* @property {BlitsComponent} rootParent - Reference to the root component in case of slots, otherwise this is the same as parent
|
|
118
|
-
* @property {BlitsComponent|BlitsElement[]} [children] - The children of the component instance
|
|
119
|
-
* @property {BlitsElement} [holder] - The wrapper of the entire component instance
|
|
120
|
-
* @property {number} [id] - The internal ID for the component instance
|
|
121
|
-
* @property {number} [identifier] - The identifier for the component instance WVB Whats the difference with id?
|
|
122
|
-
* @property {any[]} [intervals] - The intervals created by the component instance WVB Fixme
|
|
123
|
-
* @property {Object<string, any>} [originalState] - The original state of the component instance
|
|
124
|
-
* @property {Object<string, any>} [props] - The reactive properties of the component instance
|
|
125
|
-
* @property {any[]} [slots] - The slots of the component instance WVB Fixme
|
|
126
|
-
* @property {Object<string, any>} [state] - The reactive state of the component instance
|
|
127
|
-
* @property {any[]} [timeouts] - The timeouts created by the component instance
|
|
128
|
-
* @property {BlitsElement} [wrapper] - The reference to the outer element of the component instance
|
|
129
|
-
* @property {object} [effects] - The effects of the component instance
|
|
130
|
-
* @property {object} [watchers] - The watchers of the component instance
|
|
131
|
-
* @property {Object<string, any>} computedKeys - The computed keys of the component instance
|
|
132
|
-
* @property {any[]} [effects] - The effects of the component instance
|
|
133
|
-
* @property {string[]} [propKeys] - The keys of the props of the component instance
|
|
134
|
-
* @property {any[]} [stateKeys] - The keys of the state of the component instance
|
|
135
|
-
* Single props:
|
|
136
|
-
* @property {object} activeView - The active view of the component instance
|
|
137
|
-
* @property {boolean} hasFocus - Indicates if the component has focus
|
|
138
|
-
* @property {string} ref - The reference name of the component instance
|
|
139
|
-
* @property {number|undefined} index - The index in a for loop
|
|
140
|
-
* @property {number|undefined} activeRow - The active row in a for loop
|
|
141
|
-
* @property {string} color - The color of the component instance
|
|
142
|
-
* @property {number} radius - The radius of the component instance
|
|
143
|
-
* @property {number} size - The size of the component instance
|
|
144
|
-
* Methods:
|
|
145
|
-
* @property {function(): void} destroy - Destroys the component and its children, clearing listeners and effects.
|
|
146
|
-
* @property {function(any): any} focus - ⚠️ Deprecated. Use `$focus()` instead.
|
|
147
|
-
* @property {function(string): any} select - ⚠️ Deprecated. Use `$select(ref)` instead.
|
|
148
|
-
* @property {function(string, object): {type: string, props: object}} shader - Creates a shader definition object.
|
|
149
|
-
* @property {function(string): void} trigger - ⚠️ Deprecated. Use `$trigger(key)` instead.
|
|
150
|
-
* @property {function(): void} unfocus - Clears the focus state and sets lifecycle to 'unfocus'.
|
|
151
|
-
* Builtins:
|
|
152
|
-
* @property {BlitsAnnouncer} $announcer - The announcer object for the component instance
|
|
153
|
-
* @property {() => void} $clearTimeouts - Clears all timeouts created by the component instance
|
|
154
|
-
* @property {(timeoutId: number) => void} $clearTimeout - Clears all timeouts created by the component instance
|
|
155
|
-
* @property {() => void} $clearIntervals - Clears all intervals created by the component instance
|
|
156
|
-
* @property {(intervalId: number) => void} $clearInterval - Clears all intervals created by the component instance
|
|
157
|
-
* @property {Object} $colors - The colors object for the component instance
|
|
158
|
-
* @property {(event: string, params: object) => void} $emit - Emits an event with the specified parameters
|
|
159
|
-
* @property {function(any): void} $focus - Sets the component focus state and delegates to the Focus manager.
|
|
160
|
-
* @property {Object} $language - The language object for the component instance
|
|
161
|
-
* @property {function(any): any} $listen - The listen object for the component instance
|
|
162
|
-
* @property {Object} $log - The log object for the component instance
|
|
163
|
-
* @property {Object} $router - The router object for the component instance
|
|
164
|
-
* @property {function(string): any} $select - Selects a child component by `ref`.
|
|
165
|
-
* @property {function(any): any} $setInterval - Sets an interval for the component instance
|
|
166
|
-
* @property {function(any): any} $setTimeout - Sets a timeout for the component instance
|
|
167
|
-
* @property {function(any): any} $size - The size object for the component instance
|
|
168
|
-
* @property {function(any): any} $sizes - The sizes object for the component instance
|
|
169
|
-
* @property {function(string): void} $trigger - Forces a reactivity trigger on a property in `originalState`.
|
|
170
|
-
* @property {function(any): any} $unlisten - The unlisten object for the component instance
|
|
171
|
-
*
|
|
172
|
-
*/
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* @typedef {Object} BlitsComponentConfig
|
|
176
|
-
* @property {string} template - The template string for the component.
|
|
177
|
-
* @property {function(this: BlitsComponent): Object} [state] - State factory function.
|
|
178
|
-
* @property {Object} [hooks] - Lifecycle hooks (frameTick, idle, attach, detach, enter, exit, etc).
|
|
179
|
-
* @property {Object} [code] - Compiled render/effects code (render: Function, effects: Function[]).
|
|
180
|
-
* @property {string} [name] - Optional name for the component.
|
|
181
|
-
* @property {any} [data] - Optional static data for the component.
|
|
182
|
-
* @property {Object} [options] - Optional options for the component.
|
|
183
|
-
*/
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Component factory function
|
|
187
|
-
* @param {string} name - The name of the component
|
|
188
|
-
* @param {BlitsComponentConfig} config - The configuration object for the component
|
|
189
|
-
* @returns {BlitsComponentFactory} - The component factory function
|
|
190
|
-
*/
|
|
191
|
-
const Component = (name = required('name'), config = required('config')) => {
|
|
192
|
-
let base = undefined
|
|
193
|
-
|
|
194
|
-
const component = function (opts, parentEl, parentComponent, rootComponent) {
|
|
195
|
-
// generate a human readable ID for the component instance (i.e. Blits::ComponentName1)
|
|
196
|
-
this.componentId = createHumanReadableId(name)
|
|
197
|
-
|
|
198
|
-
this[symbols.effects] = []
|
|
199
|
-
|
|
200
|
-
// instantiate a lifecycle object for this instance
|
|
201
|
-
this.lifecycle = Object.assign(Object.create(Lifecycle), {
|
|
202
|
-
component: this,
|
|
203
|
-
previous: null,
|
|
204
|
-
current: null,
|
|
205
|
-
})
|
|
206
|
-
|
|
207
|
-
// set a reference to the parent component
|
|
208
|
-
this.parent = parentComponent
|
|
209
|
-
|
|
210
|
-
//
|
|
211
|
-
this.rootParent = rootComponent
|
|
212
|
-
|
|
213
|
-
// set a reference to the holder / parentElement
|
|
214
|
-
// Components are wrapped in a holder node (used to apply positioning and transforms
|
|
215
|
-
// such as rotation and scale to components)
|
|
216
|
-
this[symbols.holder] = parentEl
|
|
217
|
-
|
|
218
|
-
// generate an internal id (simple counter)
|
|
219
|
-
this[symbols.id] = createInternalId()
|
|
220
|
-
|
|
221
|
-
// set the internal props and make them reactive (based on the reactivity mode)
|
|
222
|
-
this[symbols.props] = reactive(opts.props || {}, Settings.get('reactivityMode'))
|
|
223
|
-
|
|
224
|
-
// create an empty array for storing timeouts created by this component (via this.$setTimeout)
|
|
225
|
-
this[symbols.timeouts] = []
|
|
226
|
-
|
|
227
|
-
// create an empty array for storing intervals created by this component (via this.$setInterval)
|
|
228
|
-
this[symbols.intervals] = []
|
|
229
|
-
|
|
230
|
-
// apply the state function (passing in the this reference to utilize configured props)
|
|
231
|
-
// and store a reference to this original state
|
|
232
|
-
this[symbols.originalState] =
|
|
233
|
-
(config.state && typeof config.state === 'function' && config.state.apply(this)) || {}
|
|
234
|
-
// add hasFocus key in
|
|
235
|
-
this[symbols.originalState]['hasFocus'] = false
|
|
236
|
-
|
|
237
|
-
// generate a reactive state (using the result of previously execute state function)
|
|
238
|
-
// and store it
|
|
239
|
-
this[symbols.state] = reactive(this[symbols.originalState], Settings.get('reactivityMode'))
|
|
240
|
-
|
|
241
|
-
// all basic setup has been done now, set the lifecycle to state 'init'
|
|
242
|
-
this.lifecycle.state = 'init'
|
|
243
|
-
|
|
244
|
-
// execute the render code that constructs the initial state of the component
|
|
245
|
-
// and store the children result (a flat map of elements and components)
|
|
246
|
-
this[symbols.children] =
|
|
247
|
-
config.code.render.apply(stage, [
|
|
248
|
-
parentEl,
|
|
249
|
-
this,
|
|
250
|
-
config,
|
|
251
|
-
globalComponents,
|
|
252
|
-
effect,
|
|
253
|
-
getRaw,
|
|
254
|
-
Log,
|
|
255
|
-
]) || []
|
|
256
|
-
|
|
257
|
-
// create a reference to the wrapper element of the component (i.e. the root Element of the component)
|
|
258
|
-
this[symbols.wrapper] = this[symbols.children][0]
|
|
259
|
-
|
|
260
|
-
// create a reference to an array of children that are slots
|
|
261
|
-
this[symbols.slots] = this[symbols.children].filter((child) => child[symbols.isSlot])
|
|
262
|
-
|
|
263
|
-
// register hooks if component has hooks specified
|
|
264
|
-
if (config.hooks) {
|
|
265
|
-
// frame tick event
|
|
266
|
-
if (config.hooks.frameTick) {
|
|
267
|
-
renderer.on('frameTick', (r, data) =>
|
|
268
|
-
emit('frameTick', this[symbols.identifier], this, [data])
|
|
269
|
-
)
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
if (config.hooks.idle) {
|
|
273
|
-
renderer.on('idle', () => {
|
|
274
|
-
emit('idle', this[symbols.identifier], this)
|
|
275
|
-
})
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// inBounds event emiting a lifecycle attach event
|
|
279
|
-
if (config.hooks.attach) {
|
|
280
|
-
this[symbols.wrapper].node.on('inBounds', () => {
|
|
281
|
-
this.lifecycle.state = 'attach'
|
|
282
|
-
})
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// outOfBounds event emiting a lifeycle detach event
|
|
286
|
-
if (config.hooks.detach) {
|
|
287
|
-
this[symbols.wrapper].node.on('outOfBounds', (node, { previous }) => {
|
|
288
|
-
if (previous > 0) this.lifecycle.state = 'detach'
|
|
289
|
-
})
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// inViewport event emiting a lifecycle enter event
|
|
293
|
-
if (config.hooks.enter) {
|
|
294
|
-
this[symbols.wrapper].node.on('inViewport', () => {
|
|
295
|
-
this.lifecycle.state = 'enter'
|
|
296
|
-
})
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// outOfViewport event emitting a lifecycle exit event
|
|
300
|
-
if (config.hooks.exit) {
|
|
301
|
-
this[symbols.wrapper].node.on('outOfBounds', () => {
|
|
302
|
-
this.lifecycle.state = 'exit'
|
|
303
|
-
})
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// setup (and execute) all the generated side effects based on the
|
|
308
|
-
// reactive bindings define in the template
|
|
309
|
-
const effects = config.code.effects
|
|
310
|
-
for (let i = 0; i < effects.length; i++) {
|
|
311
|
-
const eff = () => {
|
|
312
|
-
effects[i](this, this[symbols.children], config, globalComponents, rootComponent, effect)
|
|
313
|
-
}
|
|
314
|
-
// store reference to the effect
|
|
315
|
-
this[symbols.effects].push(eff)
|
|
316
|
-
effect(eff)
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// setup watchers if the components has watchers specified
|
|
320
|
-
if (this[symbols.watchers]) {
|
|
321
|
-
const watcherkeys = Object.keys(this[symbols.watchers])
|
|
322
|
-
const watcherkeysLength = watcherkeys.length
|
|
323
|
-
for (let i = 0; i < watcherkeysLength; i++) {
|
|
324
|
-
let target = this
|
|
325
|
-
let key = watcherkeys[i]
|
|
326
|
-
const watchKey = key
|
|
327
|
-
// when dot notation used, find the nested target
|
|
328
|
-
if (key.indexOf('.') > -1) {
|
|
329
|
-
const keys = key.split('.')
|
|
330
|
-
key = keys.pop()
|
|
331
|
-
for (let i = 0; i < keys.length; i++) {
|
|
332
|
-
target = target[keys[i]]
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
let old = this[key]
|
|
337
|
-
|
|
338
|
-
const eff = (force = false) => {
|
|
339
|
-
const newValue = target[key]
|
|
340
|
-
if (old !== newValue || force === true) {
|
|
341
|
-
this[symbols.watchers][watchKey].apply(this, [newValue, old])
|
|
342
|
-
old = newValue
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
// store reference to the effect
|
|
347
|
-
this[symbols.effects].push(eff)
|
|
348
|
-
effect(eff)
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
// finaly set the lifecycle state to ready (in the next tick)
|
|
353
|
-
setTimeout(() => (this.lifecycle.state = 'ready'))
|
|
354
|
-
|
|
355
|
-
// and return this
|
|
356
|
-
return this
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/**
|
|
360
|
-
* Component factory function
|
|
361
|
-
* @typedef {BlitsComponentFactory}
|
|
362
|
-
* @param {Object} options
|
|
363
|
-
* @param {BlitsComponent} parentEl
|
|
364
|
-
* @param {BlitsComponent} parentComponent
|
|
365
|
-
* @param {BlitsComponent} rootComponent
|
|
366
|
-
* @returns {BlitsComponent}
|
|
367
|
-
*/
|
|
368
|
-
const factory = (options = {}, parentEl, parentComponent, rootComponent) => {
|
|
369
|
-
if (Base[symbols['launched']] === false) {
|
|
370
|
-
// Register user defined plugins once on the Base object (after launch)
|
|
371
|
-
const pluginKeys = Object.keys(plugins)
|
|
372
|
-
const pluginKeysLength = pluginKeys.length
|
|
373
|
-
/** @type {Object} */
|
|
374
|
-
const pluginInstances = {}
|
|
375
|
-
for (let i = 0; i < pluginKeysLength; i++) {
|
|
376
|
-
const pluginName = pluginKeys[i]
|
|
377
|
-
const prefixedPluginName = `$${pluginName}`
|
|
378
|
-
if (prefixedPluginName in Base) {
|
|
379
|
-
Log.warn(
|
|
380
|
-
`"${pluginName}" (this.${prefixedPluginName}) already exists as a property or plugin on the Base Component. You may be overwriting built-in functionality. Proceed with care!`
|
|
381
|
-
)
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
const plugin = plugins[pluginName]
|
|
385
|
-
|
|
386
|
-
pluginInstances[prefixedPluginName] = {
|
|
387
|
-
// instantiate the plugin, passing in provided options
|
|
388
|
-
value: Object.defineProperties(plugin.plugin(plugin.options), shared),
|
|
389
|
-
writable: false,
|
|
390
|
-
enumerable: true,
|
|
391
|
-
configurable: true,
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
Object.defineProperties(Base, pluginInstances)
|
|
396
|
-
|
|
397
|
-
// expose other plugins inside each plugin
|
|
398
|
-
for (const plugin in pluginInstances) {
|
|
399
|
-
Object.defineProperties(Base[plugin], pluginInstances)
|
|
400
|
-
// but remove reference to plugin itself
|
|
401
|
-
delete Base[plugin][plugin]
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// register global components once
|
|
405
|
-
globalComponents = components()
|
|
406
|
-
|
|
407
|
-
// mark launched true
|
|
408
|
-
Base[symbols['launched']] = true
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// setup the component once per component type, using Base as the prototype
|
|
412
|
-
if (base === undefined) {
|
|
413
|
-
Log.debug(`Setting up ${name} component`)
|
|
414
|
-
base = setupComponent(Object.create(Base), config)
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
// one time code generation (only if precompilation is turned off)
|
|
418
|
-
if (config.code === undefined) {
|
|
419
|
-
Log.debug(`Generating code for ${name} component`)
|
|
420
|
-
config.code = codegenerator.call(config, parser(config.template, name))
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// create an instance of the component, using base as the prototype (which contains Base)
|
|
424
|
-
return component.call(Object.create(base), options, parentEl, parentComponent, rootComponent)
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
// store the config on the factory, in order to access the config
|
|
428
|
-
// during the code generation step
|
|
429
|
-
factory[Symbol.for('config')] = config
|
|
430
|
-
|
|
431
|
-
// To determine whether dynamic component is actual Blits component or not
|
|
432
|
-
factory[symbols.isComponent] = true
|
|
433
|
-
|
|
434
|
-
return factory
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
export default Component
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 Comcast Cable Communications Management, LLC
|
|
3
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License.
|
|
5
|
+
* You may obtain a copy of the License at
|
|
6
|
+
*
|
|
7
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
*
|
|
9
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
* See the License for the specific language governing permissions and
|
|
13
|
+
* limitations under the License.
|
|
14
|
+
*
|
|
15
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { Log } from './lib/log.js'
|
|
19
|
+
import parser from './lib/templateparser/parser.js'
|
|
20
|
+
import codegenerator from './lib/codegenerator/generator.js'
|
|
21
|
+
import { createHumanReadableId, createInternalId } from './lib/componentId.js'
|
|
22
|
+
import { emit } from './lib/hooks.js'
|
|
23
|
+
import { reactive, getRaw } from './lib/reactivity/reactive.js'
|
|
24
|
+
import { effect } from './lib/reactivity/effect.js'
|
|
25
|
+
import Lifecycle from './lib/lifecycle.js'
|
|
26
|
+
import symbols from './lib/symbols.js'
|
|
27
|
+
|
|
28
|
+
import { stage, renderer } from './launch.js'
|
|
29
|
+
|
|
30
|
+
import { default as Base, shared } from './component/base/index.js'
|
|
31
|
+
|
|
32
|
+
import Settings from './settings.js'
|
|
33
|
+
|
|
34
|
+
import setupComponent from './component/setup/index.js'
|
|
35
|
+
import components from './components/index.js'
|
|
36
|
+
|
|
37
|
+
import { plugins } from './plugin.js'
|
|
38
|
+
|
|
39
|
+
// object to store global components
|
|
40
|
+
let globalComponents
|
|
41
|
+
|
|
42
|
+
const required = (name) => {
|
|
43
|
+
throw new Error(`Parameter ${name} is required`)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @typedef {function} BlitsComponentFactory
|
|
48
|
+
* @param {object} opts - The options for the component instance
|
|
49
|
+
* @param {BlitsComponent} parentEl - The parent element for the component instance
|
|
50
|
+
* @param {BlitsComponent} parentComponent - The parent component for the component instance
|
|
51
|
+
* @param {BlitsComponent} rootComponent - The root component for the component instance
|
|
52
|
+
* @returns {BlitsComponent} - The component instance
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Structure of a Blits component:
|
|
57
|
+
* Component:
|
|
58
|
+
* <Holder>
|
|
59
|
+
* <Wrapper>
|
|
60
|
+
* []<Elements>
|
|
61
|
+
* </Wrapper>
|
|
62
|
+
* </Holder>
|
|
63
|
+
*
|
|
64
|
+
* A Blits Element
|
|
65
|
+
* @typedef {Object} BlitsAnnouncer
|
|
66
|
+
* @property {(message: string) => void} assertive - Function to assertively announce a message.
|
|
67
|
+
* @property {() => void} clear - Function to clear the announcer.
|
|
68
|
+
* @property {() => void} disable - Function to disable the announcer.
|
|
69
|
+
* @property {() => void} enable - Function to enable the announcer.
|
|
70
|
+
* @property {(delay: number) => void} pause - Function to pause the announcer.
|
|
71
|
+
* @property {(message: string) => void} polite - Function to politely announce a message.
|
|
72
|
+
* @property {(message: string, politeness: 'off'|'polite'|'assertive') => void} announce - Function to announce a message with specified politeness.
|
|
73
|
+
* @property {() => void} stop - Function to stop the announcer.
|
|
74
|
+
* @property {(v: boolean) => void} toggle - Function to toggle the announcer.
|
|
75
|
+
*
|
|
76
|
+
* @typedef {Object} BlitsElementConfig
|
|
77
|
+
* @property {BlitsElement} parent - The parent element of this element.
|
|
78
|
+
* @property {any} node - The node object for the element.
|
|
79
|
+
*
|
|
80
|
+
* @typedef {Object} BlitsElementProps
|
|
81
|
+
* @property {boolean} __textnode - Indicates if the element is a text node.
|
|
82
|
+
* @property {boolean} __layout - Indicates if the element is a layout node.
|
|
83
|
+
* @property {BlitsElement} element - The element to which the props belong?? Do we need this?
|
|
84
|
+
* @property {BlitsElementConfig} config - Configuration object for the element.
|
|
85
|
+
* @property {Object<string, any>} props - The props object containing the properties of the element.
|
|
86
|
+
* @property {Object<string, any>} raw - The raw input props.
|
|
87
|
+
* @property {Object<string, any>} scheduledTransitions - Tracks transitions by property name.
|
|
88
|
+
*
|
|
89
|
+
* @typedef {Object} BlitsElement
|
|
90
|
+
* @property {BlitsComponent} component - Reference to the owning Blits component.
|
|
91
|
+
* @property {BlitsElementConfig} config - Configuration object for the element.
|
|
92
|
+
* @property {number} counter - Unique counter used for shader workarounds. FIXME?
|
|
93
|
+
* @property {string[]} effectNames - Names of active shader effects.
|
|
94
|
+
* @property {any} node - The underlying renderer node (e.g., WebGL node or text node).
|
|
95
|
+
* @property {BlitsElementProps} props - Proxy-like object containing transformed props.
|
|
96
|
+
* @property {any[]} children - WVB I cant see this populated? Filtered list of children owned by this element. FIXME?
|
|
97
|
+
* @property {any} parent - WVB Shortcut to the parent CoreNode?
|
|
98
|
+
* @property {number} nodeId - ID of the CoreNode, if available.
|
|
99
|
+
* @property {string|null} ref - Ref name (if defined).
|
|
100
|
+
* @property {function(Object):void} populate - Initializes the element with props and hooks.
|
|
101
|
+
* @property {function(string, any):void} set - Updates a single property.
|
|
102
|
+
* @property {function(string, any, Object):void} animate - Animates a property with transition options.
|
|
103
|
+
* @property {function():void} destroy - Destroys the underlying node and cancels transitions.
|
|
104
|
+
* @property {function(any): any} triggerLayout - Triggers a layout update for the element.
|
|
105
|
+
*
|
|
106
|
+
* @typedef {Object} BlitsLifecycle
|
|
107
|
+
* @property {BlitsComponent} component - The Blits comonent instance this lifecycle belongs to.
|
|
108
|
+
* @property {'init'|'ready'|'destroyed'} current - The current lifecycle state of the component.
|
|
109
|
+
* @property {'init'|'ready'|'destroyed'|null} previous - The previous lifecycle state of the component.
|
|
110
|
+
* @property {string} state - The current lifecycle state of the component.
|
|
111
|
+
*
|
|
112
|
+
* @typedef {object} BlitsComponent
|
|
113
|
+
* Main properties of the Blits component:
|
|
114
|
+
* @property {string} componentId - The unique identifier for the component instance
|
|
115
|
+
* @property {BlitsLifecycle} lifecycle - The lifecycle object for the component instance
|
|
116
|
+
* @property {BlitsComponent} parent - The parent component of the current component instance
|
|
117
|
+
* @property {BlitsComponent} rootParent - Reference to the root component in case of slots, otherwise this is the same as parent
|
|
118
|
+
* @property {BlitsComponent|BlitsElement[]} [children] - The children of the component instance
|
|
119
|
+
* @property {BlitsElement} [holder] - The wrapper of the entire component instance
|
|
120
|
+
* @property {number} [id] - The internal ID for the component instance
|
|
121
|
+
* @property {number} [identifier] - The identifier for the component instance WVB Whats the difference with id?
|
|
122
|
+
* @property {any[]} [intervals] - The intervals created by the component instance WVB Fixme
|
|
123
|
+
* @property {Object<string, any>} [originalState] - The original state of the component instance
|
|
124
|
+
* @property {Object<string, any>} [props] - The reactive properties of the component instance
|
|
125
|
+
* @property {any[]} [slots] - The slots of the component instance WVB Fixme
|
|
126
|
+
* @property {Object<string, any>} [state] - The reactive state of the component instance
|
|
127
|
+
* @property {any[]} [timeouts] - The timeouts created by the component instance
|
|
128
|
+
* @property {BlitsElement} [wrapper] - The reference to the outer element of the component instance
|
|
129
|
+
* @property {object} [effects] - The effects of the component instance
|
|
130
|
+
* @property {object} [watchers] - The watchers of the component instance
|
|
131
|
+
* @property {Object<string, any>} computedKeys - The computed keys of the component instance
|
|
132
|
+
* @property {any[]} [effects] - The effects of the component instance
|
|
133
|
+
* @property {string[]} [propKeys] - The keys of the props of the component instance
|
|
134
|
+
* @property {any[]} [stateKeys] - The keys of the state of the component instance
|
|
135
|
+
* Single props:
|
|
136
|
+
* @property {object} activeView - The active view of the component instance
|
|
137
|
+
* @property {boolean} hasFocus - Indicates if the component has focus
|
|
138
|
+
* @property {string} ref - The reference name of the component instance
|
|
139
|
+
* @property {number|undefined} index - The index in a for loop
|
|
140
|
+
* @property {number|undefined} activeRow - The active row in a for loop
|
|
141
|
+
* @property {string} color - The color of the component instance
|
|
142
|
+
* @property {number} radius - The radius of the component instance
|
|
143
|
+
* @property {number} size - The size of the component instance
|
|
144
|
+
* Methods:
|
|
145
|
+
* @property {function(): void} destroy - Destroys the component and its children, clearing listeners and effects.
|
|
146
|
+
* @property {function(any): any} focus - ⚠️ Deprecated. Use `$focus()` instead.
|
|
147
|
+
* @property {function(string): any} select - ⚠️ Deprecated. Use `$select(ref)` instead.
|
|
148
|
+
* @property {function(string, object): {type: string, props: object}} shader - Creates a shader definition object.
|
|
149
|
+
* @property {function(string): void} trigger - ⚠️ Deprecated. Use `$trigger(key)` instead.
|
|
150
|
+
* @property {function(): void} unfocus - Clears the focus state and sets lifecycle to 'unfocus'.
|
|
151
|
+
* Builtins:
|
|
152
|
+
* @property {BlitsAnnouncer} $announcer - The announcer object for the component instance
|
|
153
|
+
* @property {() => void} $clearTimeouts - Clears all timeouts created by the component instance
|
|
154
|
+
* @property {(timeoutId: number) => void} $clearTimeout - Clears all timeouts created by the component instance
|
|
155
|
+
* @property {() => void} $clearIntervals - Clears all intervals created by the component instance
|
|
156
|
+
* @property {(intervalId: number) => void} $clearInterval - Clears all intervals created by the component instance
|
|
157
|
+
* @property {Object} $colors - The colors object for the component instance
|
|
158
|
+
* @property {(event: string, params: object) => void} $emit - Emits an event with the specified parameters
|
|
159
|
+
* @property {function(any): void} $focus - Sets the component focus state and delegates to the Focus manager.
|
|
160
|
+
* @property {Object} $language - The language object for the component instance
|
|
161
|
+
* @property {function(any): any} $listen - The listen object for the component instance
|
|
162
|
+
* @property {Object} $log - The log object for the component instance
|
|
163
|
+
* @property {Object} $router - The router object for the component instance
|
|
164
|
+
* @property {function(string): any} $select - Selects a child component by `ref`.
|
|
165
|
+
* @property {function(any): any} $setInterval - Sets an interval for the component instance
|
|
166
|
+
* @property {function(any): any} $setTimeout - Sets a timeout for the component instance
|
|
167
|
+
* @property {function(any): any} $size - The size object for the component instance
|
|
168
|
+
* @property {function(any): any} $sizes - The sizes object for the component instance
|
|
169
|
+
* @property {function(string): void} $trigger - Forces a reactivity trigger on a property in `originalState`.
|
|
170
|
+
* @property {function(any): any} $unlisten - The unlisten object for the component instance
|
|
171
|
+
*
|
|
172
|
+
*/
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* @typedef {Object} BlitsComponentConfig
|
|
176
|
+
* @property {string} template - The template string for the component.
|
|
177
|
+
* @property {function(this: BlitsComponent): Object} [state] - State factory function.
|
|
178
|
+
* @property {Object} [hooks] - Lifecycle hooks (frameTick, idle, attach, detach, enter, exit, etc).
|
|
179
|
+
* @property {Object} [code] - Compiled render/effects code (render: Function, effects: Function[]).
|
|
180
|
+
* @property {string} [name] - Optional name for the component.
|
|
181
|
+
* @property {any} [data] - Optional static data for the component.
|
|
182
|
+
* @property {Object} [options] - Optional options for the component.
|
|
183
|
+
*/
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Component factory function
|
|
187
|
+
* @param {string} name - The name of the component
|
|
188
|
+
* @param {BlitsComponentConfig} config - The configuration object for the component
|
|
189
|
+
* @returns {BlitsComponentFactory} - The component factory function
|
|
190
|
+
*/
|
|
191
|
+
const Component = (name = required('name'), config = required('config')) => {
|
|
192
|
+
let base = undefined
|
|
193
|
+
|
|
194
|
+
const component = function (opts, parentEl, parentComponent, rootComponent) {
|
|
195
|
+
// generate a human readable ID for the component instance (i.e. Blits::ComponentName1)
|
|
196
|
+
this.componentId = createHumanReadableId(name)
|
|
197
|
+
|
|
198
|
+
this[symbols.effects] = []
|
|
199
|
+
|
|
200
|
+
// instantiate a lifecycle object for this instance
|
|
201
|
+
this.lifecycle = Object.assign(Object.create(Lifecycle), {
|
|
202
|
+
component: this,
|
|
203
|
+
previous: null,
|
|
204
|
+
current: null,
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
// set a reference to the parent component
|
|
208
|
+
this.parent = parentComponent
|
|
209
|
+
|
|
210
|
+
//
|
|
211
|
+
this.rootParent = rootComponent
|
|
212
|
+
|
|
213
|
+
// set a reference to the holder / parentElement
|
|
214
|
+
// Components are wrapped in a holder node (used to apply positioning and transforms
|
|
215
|
+
// such as rotation and scale to components)
|
|
216
|
+
this[symbols.holder] = parentEl
|
|
217
|
+
|
|
218
|
+
// generate an internal id (simple counter)
|
|
219
|
+
this[symbols.id] = createInternalId()
|
|
220
|
+
|
|
221
|
+
// set the internal props and make them reactive (based on the reactivity mode)
|
|
222
|
+
this[symbols.props] = reactive(opts.props || {}, Settings.get('reactivityMode'))
|
|
223
|
+
|
|
224
|
+
// create an empty array for storing timeouts created by this component (via this.$setTimeout)
|
|
225
|
+
this[symbols.timeouts] = []
|
|
226
|
+
|
|
227
|
+
// create an empty array for storing intervals created by this component (via this.$setInterval)
|
|
228
|
+
this[symbols.intervals] = []
|
|
229
|
+
|
|
230
|
+
// apply the state function (passing in the this reference to utilize configured props)
|
|
231
|
+
// and store a reference to this original state
|
|
232
|
+
this[symbols.originalState] =
|
|
233
|
+
(config.state && typeof config.state === 'function' && config.state.apply(this)) || {}
|
|
234
|
+
// add hasFocus key in
|
|
235
|
+
this[symbols.originalState]['hasFocus'] = false
|
|
236
|
+
|
|
237
|
+
// generate a reactive state (using the result of previously execute state function)
|
|
238
|
+
// and store it
|
|
239
|
+
this[symbols.state] = reactive(this[symbols.originalState], Settings.get('reactivityMode'))
|
|
240
|
+
|
|
241
|
+
// all basic setup has been done now, set the lifecycle to state 'init'
|
|
242
|
+
this.lifecycle.state = 'init'
|
|
243
|
+
|
|
244
|
+
// execute the render code that constructs the initial state of the component
|
|
245
|
+
// and store the children result (a flat map of elements and components)
|
|
246
|
+
this[symbols.children] =
|
|
247
|
+
config.code.render.apply(stage, [
|
|
248
|
+
parentEl,
|
|
249
|
+
this,
|
|
250
|
+
config,
|
|
251
|
+
globalComponents,
|
|
252
|
+
effect,
|
|
253
|
+
getRaw,
|
|
254
|
+
Log,
|
|
255
|
+
]) || []
|
|
256
|
+
|
|
257
|
+
// create a reference to the wrapper element of the component (i.e. the root Element of the component)
|
|
258
|
+
this[symbols.wrapper] = this[symbols.children][0]
|
|
259
|
+
|
|
260
|
+
// create a reference to an array of children that are slots
|
|
261
|
+
this[symbols.slots] = this[symbols.children].filter((child) => child[symbols.isSlot])
|
|
262
|
+
|
|
263
|
+
// register hooks if component has hooks specified
|
|
264
|
+
if (config.hooks) {
|
|
265
|
+
// frame tick event
|
|
266
|
+
if (config.hooks.frameTick) {
|
|
267
|
+
renderer.on('frameTick', (r, data) =>
|
|
268
|
+
emit('frameTick', this[symbols.identifier], this, [data])
|
|
269
|
+
)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (config.hooks.idle) {
|
|
273
|
+
renderer.on('idle', () => {
|
|
274
|
+
emit('idle', this[symbols.identifier], this)
|
|
275
|
+
})
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// inBounds event emiting a lifecycle attach event
|
|
279
|
+
if (config.hooks.attach) {
|
|
280
|
+
this[symbols.wrapper].node.on('inBounds', () => {
|
|
281
|
+
this.lifecycle.state = 'attach'
|
|
282
|
+
})
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// outOfBounds event emiting a lifeycle detach event
|
|
286
|
+
if (config.hooks.detach) {
|
|
287
|
+
this[symbols.wrapper].node.on('outOfBounds', (node, { previous }) => {
|
|
288
|
+
if (previous > 0) this.lifecycle.state = 'detach'
|
|
289
|
+
})
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// inViewport event emiting a lifecycle enter event
|
|
293
|
+
if (config.hooks.enter) {
|
|
294
|
+
this[symbols.wrapper].node.on('inViewport', () => {
|
|
295
|
+
this.lifecycle.state = 'enter'
|
|
296
|
+
})
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// outOfViewport event emitting a lifecycle exit event
|
|
300
|
+
if (config.hooks.exit) {
|
|
301
|
+
this[symbols.wrapper].node.on('outOfBounds', () => {
|
|
302
|
+
this.lifecycle.state = 'exit'
|
|
303
|
+
})
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// setup (and execute) all the generated side effects based on the
|
|
308
|
+
// reactive bindings define in the template
|
|
309
|
+
const effects = config.code.effects
|
|
310
|
+
for (let i = 0; i < effects.length; i++) {
|
|
311
|
+
const eff = () => {
|
|
312
|
+
effects[i](this, this[symbols.children], config, globalComponents, rootComponent, effect)
|
|
313
|
+
}
|
|
314
|
+
// store reference to the effect
|
|
315
|
+
this[symbols.effects].push(eff)
|
|
316
|
+
effect(eff)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// setup watchers if the components has watchers specified
|
|
320
|
+
if (this[symbols.watchers]) {
|
|
321
|
+
const watcherkeys = Object.keys(this[symbols.watchers])
|
|
322
|
+
const watcherkeysLength = watcherkeys.length
|
|
323
|
+
for (let i = 0; i < watcherkeysLength; i++) {
|
|
324
|
+
let target = this
|
|
325
|
+
let key = watcherkeys[i]
|
|
326
|
+
const watchKey = key
|
|
327
|
+
// when dot notation used, find the nested target
|
|
328
|
+
if (key.indexOf('.') > -1) {
|
|
329
|
+
const keys = key.split('.')
|
|
330
|
+
key = keys.pop()
|
|
331
|
+
for (let i = 0; i < keys.length; i++) {
|
|
332
|
+
target = target[keys[i]]
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
let old = this[key]
|
|
337
|
+
|
|
338
|
+
const eff = (force = false) => {
|
|
339
|
+
const newValue = target[key]
|
|
340
|
+
if (old !== newValue || force === true) {
|
|
341
|
+
this[symbols.watchers][watchKey].apply(this, [newValue, old])
|
|
342
|
+
old = newValue
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// store reference to the effect
|
|
347
|
+
this[symbols.effects].push(eff)
|
|
348
|
+
effect(eff)
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// finaly set the lifecycle state to ready (in the next tick)
|
|
353
|
+
setTimeout(() => (this.lifecycle.state = 'ready'))
|
|
354
|
+
|
|
355
|
+
// and return this
|
|
356
|
+
return this
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Component factory function
|
|
361
|
+
* @typedef {BlitsComponentFactory}
|
|
362
|
+
* @param {Object} options
|
|
363
|
+
* @param {BlitsComponent} parentEl
|
|
364
|
+
* @param {BlitsComponent} parentComponent
|
|
365
|
+
* @param {BlitsComponent} rootComponent
|
|
366
|
+
* @returns {BlitsComponent}
|
|
367
|
+
*/
|
|
368
|
+
const factory = (options = {}, parentEl, parentComponent, rootComponent) => {
|
|
369
|
+
if (Base[symbols['launched']] === false) {
|
|
370
|
+
// Register user defined plugins once on the Base object (after launch)
|
|
371
|
+
const pluginKeys = Object.keys(plugins)
|
|
372
|
+
const pluginKeysLength = pluginKeys.length
|
|
373
|
+
/** @type {Object} */
|
|
374
|
+
const pluginInstances = {}
|
|
375
|
+
for (let i = 0; i < pluginKeysLength; i++) {
|
|
376
|
+
const pluginName = pluginKeys[i]
|
|
377
|
+
const prefixedPluginName = `$${pluginName}`
|
|
378
|
+
if (prefixedPluginName in Base) {
|
|
379
|
+
Log.warn(
|
|
380
|
+
`"${pluginName}" (this.${prefixedPluginName}) already exists as a property or plugin on the Base Component. You may be overwriting built-in functionality. Proceed with care!`
|
|
381
|
+
)
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const plugin = plugins[pluginName]
|
|
385
|
+
|
|
386
|
+
pluginInstances[prefixedPluginName] = {
|
|
387
|
+
// instantiate the plugin, passing in provided options
|
|
388
|
+
value: Object.defineProperties(plugin.plugin(plugin.options), shared),
|
|
389
|
+
writable: false,
|
|
390
|
+
enumerable: true,
|
|
391
|
+
configurable: true,
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
Object.defineProperties(Base, pluginInstances)
|
|
396
|
+
|
|
397
|
+
// expose other plugins inside each plugin
|
|
398
|
+
for (const plugin in pluginInstances) {
|
|
399
|
+
Object.defineProperties(Base[plugin], pluginInstances)
|
|
400
|
+
// but remove reference to plugin itself
|
|
401
|
+
delete Base[plugin][plugin]
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// register global components once
|
|
405
|
+
globalComponents = components()
|
|
406
|
+
|
|
407
|
+
// mark launched true
|
|
408
|
+
Base[symbols['launched']] = true
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// setup the component once per component type, using Base as the prototype
|
|
412
|
+
if (base === undefined) {
|
|
413
|
+
Log.debug(`Setting up ${name} component`)
|
|
414
|
+
base = setupComponent(Object.create(Base), config)
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// one time code generation (only if precompilation is turned off)
|
|
418
|
+
if (config.code === undefined) {
|
|
419
|
+
Log.debug(`Generating code for ${name} component`)
|
|
420
|
+
config.code = codegenerator.call(config, parser(config.template, name))
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// create an instance of the component, using base as the prototype (which contains Base)
|
|
424
|
+
return component.call(Object.create(base), options, parentEl, parentComponent, rootComponent)
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// store the config on the factory, in order to access the config
|
|
428
|
+
// during the code generation step
|
|
429
|
+
factory[Symbol.for('config')] = config
|
|
430
|
+
|
|
431
|
+
// To determine whether dynamic component is actual Blits component or not
|
|
432
|
+
factory[symbols.isComponent] = true
|
|
433
|
+
|
|
434
|
+
return factory
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
export default Component
|