@lightningjs/blits 1.48.0 → 2.0.0-beta.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/element-test-full.txt +0 -0
- package/index.d.ts +19 -48
- package/package.json +4 -3
- package/src/component/base/methods.js +16 -76
- package/src/component/base/utils.js +18 -4
- package/src/component/setup/props.js +37 -28
- package/src/component/setup/state.js +4 -4
- package/src/component.js +26 -17
- package/src/components/Circle.js +49 -7
- package/src/components/FPScounter.js +226 -88
- package/src/components/RouterView.js +57 -6
- package/src/components/Sprite.js +54 -8
- package/src/engines/L3/element.js +109 -86
- package/src/engines/L3/fontLoader.js +26 -31
- package/src/engines/L3/shaderLoader.js +56 -6
- package/src/focus.js +9 -9
- package/src/launch.js +1 -2
- package/src/lib/codegenerator/generator.js +116 -15
- package/src/lib/colors/colors.js +0 -1
- package/src/lib/lifecycle.js +4 -4
- package/src/lib/shaders/shaders.js +164 -0
- package/src/lib/symbols.js +8 -0
- package/src/lib/templateparser/parser.js +6 -2
- package/src/lib/utils.js +36 -0
- package/src/router/router.js +9 -9
- package/test-output.txt +0 -0
- package/vite/blitsTips.js +57 -0
- package/vite/msdfGenerator.js +52 -6
- package/vscode/data/template-attributes.json +112 -0
package/src/focus.js
CHANGED
|
@@ -50,7 +50,7 @@ export default {
|
|
|
50
50
|
|
|
51
51
|
// unfocus currently focused components
|
|
52
52
|
if (focusedComponent !== null) {
|
|
53
|
-
if (focusChain[focusChain.length - 1] === component.parent) {
|
|
53
|
+
if (focusChain[focusChain.length - 1] === component[symbols.parent]) {
|
|
54
54
|
focusChain.push(component)
|
|
55
55
|
} else {
|
|
56
56
|
const newFocusChain = getAncestors([component])
|
|
@@ -58,7 +58,7 @@ export default {
|
|
|
58
58
|
while (i--) {
|
|
59
59
|
// don't unfocus when part of the new focus chain
|
|
60
60
|
if (newFocusChain.indexOf(focusChain[i]) > -1) break
|
|
61
|
-
focusChain[i].lifecycle.state = 'unfocus'
|
|
61
|
+
focusChain[i][symbols.lifecycle].state = 'unfocus'
|
|
62
62
|
}
|
|
63
63
|
focusChain = newFocusChain
|
|
64
64
|
}
|
|
@@ -67,7 +67,7 @@ export default {
|
|
|
67
67
|
// ensure that all components in the focus path have focus state
|
|
68
68
|
let i = 0
|
|
69
69
|
while (i < focusChain.length - 1) {
|
|
70
|
-
focusChain[i].lifecycle.state = 'focus'
|
|
70
|
+
focusChain[i][symbols.lifecycle].state = 'focus'
|
|
71
71
|
i++
|
|
72
72
|
}
|
|
73
73
|
|
|
@@ -105,8 +105,8 @@ export default {
|
|
|
105
105
|
* @returns array components
|
|
106
106
|
*/
|
|
107
107
|
const getAncestors = (components) => {
|
|
108
|
-
if (components[0].parent !== undefined) {
|
|
109
|
-
components.unshift(components[0].parent)
|
|
108
|
+
if (components[0][symbols.parent] !== undefined) {
|
|
109
|
+
components.unshift(components[0][symbols.parent])
|
|
110
110
|
return getAncestors(components)
|
|
111
111
|
}
|
|
112
112
|
return components
|
|
@@ -125,8 +125,8 @@ export const getComponentWithInputEvent = (component, key) => {
|
|
|
125
125
|
typeof component[symbols.inputEvents].any === 'function')
|
|
126
126
|
) {
|
|
127
127
|
return component
|
|
128
|
-
} else if (component.parent !== undefined) {
|
|
129
|
-
return getComponentWithInputEvent(component.parent, key)
|
|
128
|
+
} else if (component[symbols.parent] !== undefined) {
|
|
129
|
+
return getComponentWithInputEvent(component[symbols.parent], key)
|
|
130
130
|
} else return null
|
|
131
131
|
}
|
|
132
132
|
|
|
@@ -138,11 +138,11 @@ export const getComponentWithInputEvent = (component, key) => {
|
|
|
138
138
|
const setFocus = (component, event) => {
|
|
139
139
|
Log.info(
|
|
140
140
|
'\nFocus chain:\n',
|
|
141
|
-
focusChain.map((c, index) => '\t'.repeat(index) + '↳ ' + c
|
|
141
|
+
focusChain.map((c, index) => '\t'.repeat(index) + '↳ ' + c.$componentId).join('\n')
|
|
142
142
|
)
|
|
143
143
|
|
|
144
144
|
focusedComponent = component
|
|
145
|
-
component.lifecycle.state = 'focus'
|
|
145
|
+
component[symbols.lifecycle].state = 'focus'
|
|
146
146
|
|
|
147
147
|
if (event instanceof KeyboardEvent) {
|
|
148
148
|
const internalEvent = new KeyboardEvent('keydown', event)
|
package/src/launch.js
CHANGED
|
@@ -41,9 +41,8 @@ import blitsPackageInfo from '../package.json' with { type: 'json' }
|
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
43
|
* @typedef {import('@lightningjs/renderer').RendererMain} RendererMain
|
|
44
|
-
* @type {RendererMain}
|
|
44
|
+
* @type {RendererMain | {}}
|
|
45
45
|
*/
|
|
46
|
-
// @ts-ignore - We ignore this because it will be replaced by the renderer once the app is launched
|
|
47
46
|
export let renderer = {}
|
|
48
47
|
export const stage = {}
|
|
49
48
|
|
|
@@ -18,10 +18,13 @@
|
|
|
18
18
|
let counter
|
|
19
19
|
let isDev
|
|
20
20
|
|
|
21
|
+
import { elementAttributes } from '../../engines/L3/element.js'
|
|
22
|
+
|
|
21
23
|
export default function (templateObject = { children: [] }, devMode = false) {
|
|
22
24
|
const ctx = {
|
|
23
25
|
renderCode: [
|
|
24
26
|
'const elms = []',
|
|
27
|
+
`const validAttributes = ${JSON.stringify(elementAttributes)}`,
|
|
25
28
|
'const elementConfigs = []',
|
|
26
29
|
'const forloops = []',
|
|
27
30
|
'const props = []',
|
|
@@ -81,7 +84,7 @@ export default function (templateObject = { children: [] }, devMode = false) {
|
|
|
81
84
|
forloops.length = 0
|
|
82
85
|
props.length = 0
|
|
83
86
|
skips.length = 0
|
|
84
|
-
}}
|
|
87
|
+
}, skips}
|
|
85
88
|
`)
|
|
86
89
|
|
|
87
90
|
return {
|
|
@@ -97,7 +100,16 @@ export default function (templateObject = { children: [] }, devMode = false) {
|
|
|
97
100
|
),
|
|
98
101
|
effects: ctx.effectsCode.map(
|
|
99
102
|
(code) =>
|
|
100
|
-
new Function(
|
|
103
|
+
new Function(
|
|
104
|
+
'component',
|
|
105
|
+
'elms',
|
|
106
|
+
'context',
|
|
107
|
+
'components',
|
|
108
|
+
'rootComponent',
|
|
109
|
+
'skips',
|
|
110
|
+
'effect',
|
|
111
|
+
code
|
|
112
|
+
)
|
|
101
113
|
),
|
|
102
114
|
context: ctx.context,
|
|
103
115
|
}
|
|
@@ -224,13 +236,43 @@ const generateElementCode = function (
|
|
|
224
236
|
}
|
|
225
237
|
})
|
|
226
238
|
|
|
239
|
+
if (templateObject[Symbol.for('tagContent')] !== undefined) {
|
|
240
|
+
const val = templateObject[Symbol.for('tagContent')]
|
|
241
|
+
|
|
242
|
+
// Check if the string contains interpolation (`{{ }}`).
|
|
243
|
+
const regex = /\{\{\s*.+?\s*\}\}/
|
|
244
|
+
const containsInterpolation = regex.test(val)
|
|
245
|
+
|
|
246
|
+
if (containsInterpolation === false) {
|
|
247
|
+
// Inline text is treated as static content when interpolation is not defined.
|
|
248
|
+
renderCode.push(`elementConfigs[${counter}]['content'] = '${val}'`)
|
|
249
|
+
} else {
|
|
250
|
+
const output = parseTagContent(val, options.component)
|
|
251
|
+
renderCode.push(`elementConfigs[${counter}]['content'] = ${output}`)
|
|
252
|
+
|
|
253
|
+
const variableRegEx = /\{\{\s*(?=[^}]*\$).+?\s*\}\}/
|
|
254
|
+
// Check if the interpolation contains $ variable
|
|
255
|
+
const isReactive = variableRegEx.test(val)
|
|
256
|
+
if (isReactive === true) {
|
|
257
|
+
this.effectsCode.push(`
|
|
258
|
+
${elm}.set('content', ${output})
|
|
259
|
+
`)
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
227
264
|
if (options.holder === true) {
|
|
228
265
|
renderCode.push(`
|
|
229
266
|
skips[${counter}] = []
|
|
230
|
-
if(typeof cmps[${counter}] !== 'undefined') {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
267
|
+
if(typeof cmps[${counter}] !== 'undefined' && cmps[${counter}][Symbol.for('config')].props !== undefined) {
|
|
268
|
+
// attributes that are a prop should be removed from element config (even if it's a know element prop)
|
|
269
|
+
let props = cmps[${counter}][Symbol.for('config')].props
|
|
270
|
+
if(Array.isArray(props) === false) props = Object.keys(cmps[${counter}][Symbol.for('config')].props)
|
|
271
|
+
for(let k = 0; k < props.length; k++) {
|
|
272
|
+
const key = props[k]
|
|
273
|
+
if(validAttributes.indexOf(key) !== -1) continue
|
|
274
|
+
delete elementConfigs[${counter}][key]
|
|
275
|
+
skips[${counter}].push(key)
|
|
234
276
|
}
|
|
235
277
|
}
|
|
236
278
|
`)
|
|
@@ -567,9 +609,9 @@ const generateForLoopCode = function (templateObject, parent) {
|
|
|
567
609
|
}
|
|
568
610
|
|
|
569
611
|
// separate effects that only rely on variables in the itteration
|
|
570
|
-
const innerScopeEffects = ctx.effectsCode.filter(
|
|
571
|
-
|
|
572
|
-
)
|
|
612
|
+
const innerScopeEffects = ctx.effectsCode.filter((effect) => {
|
|
613
|
+
return [...effect.matchAll(scopeRegex)].length === 0
|
|
614
|
+
})
|
|
573
615
|
|
|
574
616
|
// separate effects that (also) rely on variables in the outer scope
|
|
575
617
|
const outerScopeEffects = ctx.effectsCode.filter(
|
|
@@ -787,12 +829,12 @@ const cast = (val = '', key = false, component = 'component.') => {
|
|
|
787
829
|
castedValue = parseFloat(val)
|
|
788
830
|
if (val.endsWith('%')) {
|
|
789
831
|
const map = {
|
|
790
|
-
w: '
|
|
791
|
-
width: '
|
|
792
|
-
x: '
|
|
793
|
-
h: '
|
|
794
|
-
height: '
|
|
795
|
-
y: '
|
|
832
|
+
w: 'w',
|
|
833
|
+
width: 'w',
|
|
834
|
+
x: 'w',
|
|
835
|
+
h: 'h',
|
|
836
|
+
height: 'h',
|
|
837
|
+
y: 'h',
|
|
796
838
|
}
|
|
797
839
|
const base = map[key]
|
|
798
840
|
if (base) {
|
|
@@ -845,6 +887,65 @@ const cast = (val = '', key = false, component = 'component.') => {
|
|
|
845
887
|
return castedValue
|
|
846
888
|
}
|
|
847
889
|
|
|
890
|
+
function escapeSingleQuotes(str) {
|
|
891
|
+
return str.replace(/(\\*)'/g, (match, backslashes) => {
|
|
892
|
+
// If the number of backslashes is odd, quote is already escaped
|
|
893
|
+
if (backslashes.length % 2 === 1) {
|
|
894
|
+
return match
|
|
895
|
+
}
|
|
896
|
+
// Otherwise, escape the quote
|
|
897
|
+
return backslashes + "\\'"
|
|
898
|
+
})
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
const parseTagContent = (val = '', component = 'component.') => {
|
|
902
|
+
// unescaped single quotes must be escaped while preserving escaped backslashes
|
|
903
|
+
let escapedVal = escapeSingleQuotes(val)
|
|
904
|
+
|
|
905
|
+
const dynamicParts = /\{\{\s*.+?\s*\}\}/g
|
|
906
|
+
const matches = [...escapedVal.matchAll(dynamicParts)]
|
|
907
|
+
|
|
908
|
+
if (matches.length > 0) {
|
|
909
|
+
const isValStartsWithBrace = escapedVal.startsWith('{{')
|
|
910
|
+
const isValEndsWithBrace = escapedVal.endsWith('}}')
|
|
911
|
+
for (let matchObj of matches) {
|
|
912
|
+
const { 0: match, index } = matchObj
|
|
913
|
+
const isMatchAtStart = index === 0
|
|
914
|
+
const isMatchAtLast = val[index + match.length] === undefined ? true : false
|
|
915
|
+
|
|
916
|
+
let parsedMatch = match
|
|
917
|
+
|
|
918
|
+
const replaceDollar = /\$(\$(?=\$)|\$?)/g
|
|
919
|
+
const dollarMatches = [...parsedMatch.matchAll(replaceDollar)]
|
|
920
|
+
if (dollarMatches.length > 0) {
|
|
921
|
+
parsedMatch = parsedMatch.replace(replaceDollar, (match, group1) => {
|
|
922
|
+
if (group1 === '') {
|
|
923
|
+
return component
|
|
924
|
+
} else if (group1 === '$') {
|
|
925
|
+
return component + '$'
|
|
926
|
+
}
|
|
927
|
+
})
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
parsedMatch = parsedMatch.replace('{{', '(').replace('}}', ')')
|
|
931
|
+
if (isMatchAtStart === false) {
|
|
932
|
+
parsedMatch = `"+${parsedMatch}`
|
|
933
|
+
}
|
|
934
|
+
if (isMatchAtLast === false) {
|
|
935
|
+
parsedMatch = `${parsedMatch}+"`
|
|
936
|
+
}
|
|
937
|
+
escapedVal = escapedVal.replace(match, parsedMatch)
|
|
938
|
+
}
|
|
939
|
+
if (isValStartsWithBrace === false) {
|
|
940
|
+
escapedVal = '"' + escapedVal
|
|
941
|
+
}
|
|
942
|
+
if (isValEndsWithBrace === false) {
|
|
943
|
+
escapedVal = escapedVal + '"'
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
return escapedVal
|
|
947
|
+
}
|
|
948
|
+
|
|
848
949
|
const isReactiveKey = (str) => str.startsWith(':')
|
|
849
950
|
|
|
850
951
|
const parseInlineContent = (val, component) => {
|
package/src/lib/colors/colors.js
CHANGED
package/src/lib/lifecycle.js
CHANGED
|
@@ -75,7 +75,7 @@ export default {
|
|
|
75
75
|
set state(v) {
|
|
76
76
|
if ((states.indexOf(v) > -1 && v !== this.current) || v === 'refocus') {
|
|
77
77
|
Log.debug(
|
|
78
|
-
`Setting lifecycle state from ${this.current} to ${v} for ${this.component
|
|
78
|
+
`Setting lifecycle state from ${this.current} to ${v} for ${this.component.$componentId}`
|
|
79
79
|
)
|
|
80
80
|
this.previous = this.current
|
|
81
81
|
this.current = v
|
|
@@ -84,14 +84,14 @@ export default {
|
|
|
84
84
|
privateEmit(v, this.component[symbols.identifier], this.component)
|
|
85
85
|
// emit 'public' hook
|
|
86
86
|
emit(v, this.component[symbols.identifier], this.component)
|
|
87
|
-
// update the built-in hasFocus state variable
|
|
87
|
+
// update the built-in $hasFocus state variable
|
|
88
88
|
if (v === 'focus' || v === 'unfocus') {
|
|
89
89
|
if (inspectorEnabled === null) {
|
|
90
90
|
inspectorEnabled = Settings.get('inspector', false)
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
if (v === 'focus') {
|
|
94
|
-
this.component[symbols.state]
|
|
94
|
+
this.component[symbols.state].$hasFocus = true
|
|
95
95
|
if (
|
|
96
96
|
inspectorEnabled === true &&
|
|
97
97
|
this.component[symbols.holder] &&
|
|
@@ -101,7 +101,7 @@ export default {
|
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
if (v === 'unfocus') {
|
|
104
|
-
this.component[symbols.state]
|
|
104
|
+
this.component[symbols.state].$hasFocus = false
|
|
105
105
|
if (
|
|
106
106
|
inspectorEnabled === true &&
|
|
107
107
|
this.component[symbols.holder] &&
|
|
@@ -0,0 +1,164 @@
|
|
|
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 { renderer } from '../../engines/L3/launch.js'
|
|
19
|
+
import { parseToObject, isObjectString, isArrayString } from '../utils.js'
|
|
20
|
+
import colors from '../colors/colors.js'
|
|
21
|
+
|
|
22
|
+
const parseBorder = (border, props = {}, prefixed = false) => {
|
|
23
|
+
const prefix = prefixed === true ? 'border-' : ''
|
|
24
|
+
if (isNaN(border) === false) {
|
|
25
|
+
props[prefix + 'w'] = border
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
if (Array.isArray(border) === true) {
|
|
29
|
+
props[prefix + 'w'] = border
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
if (isArrayString(border) === true) {
|
|
33
|
+
props[prefix + 'w'] = JSON.parse(border)
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
if (typeof border === 'object' || isObjectString(border) === true) {
|
|
37
|
+
border = typeof border === 'string' ? parseToObject(border) : border
|
|
38
|
+
for (const key in border) {
|
|
39
|
+
props[prefix + key] = border[key]
|
|
40
|
+
}
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const parseShadow = (shadow, props = {}, prefixed = false) => {
|
|
46
|
+
const prefix = prefixed === true ? 'shadow-' : ''
|
|
47
|
+
if (Array.isArray(shadow) === true) {
|
|
48
|
+
props[prefix + 'projection'] = shadow
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
if (isArrayString(shadow) === true) {
|
|
52
|
+
props[prefix + 'projection'] = JSON.parse(shadow)
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
if (typeof shadow === 'object' || isObjectString(shadow) === true) {
|
|
56
|
+
shadow = typeof shadow === 'string' ? parseToObject(shadow) : shadow
|
|
57
|
+
for (const key in shadow) {
|
|
58
|
+
props[prefix + key] = shadow[key]
|
|
59
|
+
}
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const parseRounded = (rounded, props = {}) => {
|
|
65
|
+
if (isNaN(rounded) === false) {
|
|
66
|
+
props.radius = rounded
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
if (Array.isArray(rounded) === true) {
|
|
70
|
+
props.radius = rounded
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
if (isArrayString(rounded) === true) {
|
|
74
|
+
props.radius = JSON.parse(rounded)
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
if (typeof rounded === 'object' || isObjectString(rounded) === true) {
|
|
78
|
+
rounded = typeof rounded === 'string' ? parseToObject(rounded) : rounded
|
|
79
|
+
for (const key in rounded) {
|
|
80
|
+
props[key] = rounded[key]
|
|
81
|
+
}
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export default {
|
|
87
|
+
createElementProps(v) {
|
|
88
|
+
let { border, shadow, rounded } = v
|
|
89
|
+
const props = {}
|
|
90
|
+
const hasRounded = rounded !== undefined
|
|
91
|
+
const hasBorder = border !== undefined
|
|
92
|
+
const hasShadow = shadow !== undefined
|
|
93
|
+
|
|
94
|
+
if (border !== undefined) {
|
|
95
|
+
parseBorder(border, props, hasRounded || hasShadow)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (shadow !== undefined) {
|
|
99
|
+
parseShadow(shadow, props, hasRounded || hasBorder)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (hasRounded === true) {
|
|
103
|
+
parseRounded(rounded, props)
|
|
104
|
+
}
|
|
105
|
+
return props
|
|
106
|
+
},
|
|
107
|
+
createElementShader(v) {
|
|
108
|
+
let { border, shadow, rounded } = v
|
|
109
|
+
const props = this.createElementProps(v)
|
|
110
|
+
const hasShadow = shadow !== undefined
|
|
111
|
+
const hasBorder = border !== undefined
|
|
112
|
+
const hasRounded = rounded !== undefined
|
|
113
|
+
|
|
114
|
+
if (hasBorder === true && hasShadow === true) {
|
|
115
|
+
return renderer.createShader('roundedWithBorderAndShadow', props)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (hasRounded === true) {
|
|
119
|
+
if (hasBorder === true) {
|
|
120
|
+
return renderer.createShader('roundedWithBorder', props)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (hasShadow === true) {
|
|
124
|
+
return renderer.createShader('roundedWithShadow', props)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return renderer.createShader('rounded', props)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (hasRounded === false && hasBorder === true) {
|
|
131
|
+
return renderer.createShader('border', props)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
//only possible result left
|
|
135
|
+
return renderer.createShader('shadow', props)
|
|
136
|
+
},
|
|
137
|
+
parseProp(v) {
|
|
138
|
+
if (typeof v === 'number') {
|
|
139
|
+
return v
|
|
140
|
+
}
|
|
141
|
+
if (typeof v === 'string') {
|
|
142
|
+
return colors.normalize(v, v)
|
|
143
|
+
}
|
|
144
|
+
if (Array.isArray(v) === true) {
|
|
145
|
+
return v.map((i) => this.parseProp(i))
|
|
146
|
+
}
|
|
147
|
+
return v
|
|
148
|
+
},
|
|
149
|
+
parseProps(v) {
|
|
150
|
+
if (typeof v === 'string') {
|
|
151
|
+
v = parseToObject(v)
|
|
152
|
+
}
|
|
153
|
+
const keys = Object.keys(v)
|
|
154
|
+
const length = keys.length
|
|
155
|
+
for (let i = 0; i < length; i++) {
|
|
156
|
+
const key = keys[i]
|
|
157
|
+
if (key === 'type') {
|
|
158
|
+
continue
|
|
159
|
+
}
|
|
160
|
+
v[key] = this.parseProp(v[key])
|
|
161
|
+
}
|
|
162
|
+
return v
|
|
163
|
+
},
|
|
164
|
+
}
|
package/src/lib/symbols.js
CHANGED
|
@@ -21,12 +21,14 @@
|
|
|
21
21
|
* @property {symbol} isProxy
|
|
22
22
|
* @property {symbol} launched
|
|
23
23
|
* @property {symbol} level
|
|
24
|
+
* @property {symbol} lifecycle
|
|
24
25
|
* @property {symbol} methodKeys
|
|
25
26
|
* @property {symbol} originalState
|
|
26
27
|
* @property {symbol} propKeys
|
|
27
28
|
* @property {symbol} raw
|
|
28
29
|
* @property {symbol} ready
|
|
29
30
|
* @property {symbol} renderer
|
|
31
|
+
* @property {symbol} rootParent
|
|
30
32
|
* @property {symbol} routes
|
|
31
33
|
* @property {symbol} routerHooks
|
|
32
34
|
* @property {symbol} settings
|
|
@@ -43,6 +45,7 @@
|
|
|
43
45
|
* @property {symbol} config
|
|
44
46
|
* @property {symbol} isSlot
|
|
45
47
|
* @property {symbol} props
|
|
48
|
+
* @property {symbol} parent
|
|
46
49
|
* @property {symbol} slots
|
|
47
50
|
* @property {symbol} componentType
|
|
48
51
|
* @property {symbol} isComponent
|
|
@@ -75,12 +78,15 @@ export default {
|
|
|
75
78
|
isProxy: Symbol('isProxy'),
|
|
76
79
|
launched: Symbol('launched'),
|
|
77
80
|
level: Symbol('level'),
|
|
81
|
+
lifecycle: Symbol('lifecycle'),
|
|
78
82
|
methodKeys: Symbol('methodKeys'),
|
|
79
83
|
originalState: Symbol('originalState'),
|
|
84
|
+
parent: Symbol('parent'),
|
|
80
85
|
propKeys: Symbol('propKeys'),
|
|
81
86
|
raw: Symbol('raw'),
|
|
82
87
|
ready: Symbol('ready'),
|
|
83
88
|
renderer: Symbol('renderer'),
|
|
89
|
+
rootParent: Symbol('rootParent'),
|
|
84
90
|
routes: Symbol('routes'),
|
|
85
91
|
routerHooks: Symbol('routerHooks'),
|
|
86
92
|
settings: Symbol('settings'),
|
|
@@ -115,4 +121,6 @@ export default {
|
|
|
115
121
|
effects: Symbol.for('effects'),
|
|
116
122
|
// Symbol 'removeGlobalEffects' utilized within generated code
|
|
117
123
|
removeGlobalEffects: Symbol.for('removeGlobalEffects'),
|
|
124
|
+
// Symbol 'removeGlobalEffects' utilized within generated code
|
|
125
|
+
tagContent: Symbol.for('tagContent'),
|
|
118
126
|
}
|
|
@@ -143,7 +143,7 @@ export default (template = '', componentName, parentComponent, filePath = null)
|
|
|
143
143
|
if (currentTag[symbols.type] === 'opening') {
|
|
144
144
|
const tagContent = template.slice(cursor, template.indexOf('<', cursor))
|
|
145
145
|
if (tagContent) {
|
|
146
|
-
currentTag.
|
|
146
|
+
currentTag[Symbol.for('tagContent')] = tagContent
|
|
147
147
|
cursor += tagContent.length
|
|
148
148
|
}
|
|
149
149
|
}
|
|
@@ -189,7 +189,11 @@ export default (template = '', componentName, parentComponent, filePath = null)
|
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
// process color values
|
|
192
|
-
if (
|
|
192
|
+
if (
|
|
193
|
+
['color', ':color', ':border', 'border', 'shadow', ':shadow', 'shader', ':shader'].includes(
|
|
194
|
+
name
|
|
195
|
+
)
|
|
196
|
+
) {
|
|
193
197
|
return processColors(name, value)
|
|
194
198
|
}
|
|
195
199
|
return { name, value }
|
package/src/lib/utils.js
CHANGED
|
@@ -25,3 +25,39 @@ export const screenResolutions = {
|
|
|
25
25
|
'2160p': 2,
|
|
26
26
|
2160: 2,
|
|
27
27
|
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Checks if a value is a transition object.
|
|
31
|
+
* @param {any} value - The value to check.
|
|
32
|
+
* @returns {boolean} True if the value is a transition object, false otherwise.
|
|
33
|
+
*/
|
|
34
|
+
export const isTransition = (value) => {
|
|
35
|
+
return value !== null && typeof value === 'object' && 'transition' in value === true
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Checks if a string is an object string (starts and ends with curly braces).
|
|
40
|
+
* @param {string} str - The string to check.
|
|
41
|
+
* @returns {boolean} True if the string is an object string, false otherwise.
|
|
42
|
+
*/
|
|
43
|
+
export const isObjectString = (str) => {
|
|
44
|
+
return typeof str === 'string' && str.startsWith('{') && str.endsWith('}')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Checks if a string is an array string (starts and ends with brackets).
|
|
49
|
+
* @param {string} str - The string to check.
|
|
50
|
+
* @returns {boolean} True if the string is an object string, false otherwise.
|
|
51
|
+
*/
|
|
52
|
+
export const isArrayString = (str) => {
|
|
53
|
+
return typeof str === 'string' && str.startsWith('[') && str.endsWith(']')
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Parses a string into an object, converting single quotes to double and adding quotes to keys.
|
|
58
|
+
* @param {string} str - The string to parse.
|
|
59
|
+
* @returns {object} The parsed object.
|
|
60
|
+
*/
|
|
61
|
+
export const parseToObject = (str) => {
|
|
62
|
+
return JSON.parse(str.replace(/'/g, '"').replace(/([\w-_]+)\s*:/g, '"$1":'))
|
|
63
|
+
}
|
package/src/router/router.js
CHANGED
|
@@ -249,17 +249,17 @@ export const navigate = async function () {
|
|
|
249
249
|
Announcer.clear()
|
|
250
250
|
state.navigating = true
|
|
251
251
|
let reuse = false
|
|
252
|
-
if (preventHashChangeNavigation === false && this.parent[symbols.routes]) {
|
|
252
|
+
if (preventHashChangeNavigation === false && this[symbols.parent][symbols.routes]) {
|
|
253
253
|
let previousRoute = currentRoute //? Object.assign({}, currentRoute) : undefined
|
|
254
|
-
let route = matchHash(getHash(document.location.hash), this.parent[symbols.routes])
|
|
254
|
+
let route = matchHash(getHash(document.location.hash), this[symbols.parent][symbols.routes])
|
|
255
255
|
|
|
256
256
|
currentRoute = route
|
|
257
257
|
|
|
258
258
|
if (route) {
|
|
259
259
|
const currentPath = currentRoute.path
|
|
260
260
|
let beforeEachResult
|
|
261
|
-
if (this.parent[symbols.routerHooks]) {
|
|
262
|
-
const hooks = this.parent[symbols.routerHooks]
|
|
261
|
+
if (this[symbols.parent][symbols.routerHooks]) {
|
|
262
|
+
const hooks = this[symbols.parent][symbols.routerHooks]
|
|
263
263
|
if (hooks.beforeEach) {
|
|
264
264
|
try {
|
|
265
265
|
beforeEachResult = await hooks.beforeEach.call(this.parent, route, previousRoute)
|
|
@@ -507,8 +507,8 @@ export const navigate = async function () {
|
|
|
507
507
|
}
|
|
508
508
|
}
|
|
509
509
|
|
|
510
|
-
if (this.parent[symbols.routerHooks]) {
|
|
511
|
-
const hooks = this.parent[symbols.routerHooks]
|
|
510
|
+
if (this[symbols.parent][symbols.routerHooks]) {
|
|
511
|
+
const hooks = this[symbols.parent][symbols.routerHooks]
|
|
512
512
|
if (hooks.afterEach) {
|
|
513
513
|
try {
|
|
514
514
|
await hooks.afterEach.call(
|
|
@@ -535,9 +535,9 @@ export const navigate = async function () {
|
|
|
535
535
|
}
|
|
536
536
|
} else {
|
|
537
537
|
Log.error(`Route ${route.hash} not found`)
|
|
538
|
-
const routerHooks = this.parent[symbols.routerHooks]
|
|
538
|
+
const routerHooks = this[symbols.parent][symbols.routerHooks]
|
|
539
539
|
if (routerHooks && typeof routerHooks.error === 'function') {
|
|
540
|
-
routerHooks.error.call(this.parent, `Route ${route.hash} not found`)
|
|
540
|
+
routerHooks.error.call(this[symbols.parent], `Route ${route.hash} not found`)
|
|
541
541
|
}
|
|
542
542
|
}
|
|
543
543
|
}
|
|
@@ -659,7 +659,7 @@ export const back = function () {
|
|
|
659
659
|
}
|
|
660
660
|
// Construct new path to backtrack to
|
|
661
661
|
path = path.replace(hashEnd, '')
|
|
662
|
-
const route = matchHash(getHash(path), this.parent[symbols.routes])
|
|
662
|
+
const route = matchHash(getHash(path), this[symbols.parent][symbols.routes])
|
|
663
663
|
|
|
664
664
|
if (route && backtrack) {
|
|
665
665
|
to(route.path, route.data, route.options)
|
package/test-output.txt
ADDED
|
Binary file
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 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
|
+
const tips = ['tip1', 'tip2', 'tip3']
|
|
19
|
+
|
|
20
|
+
let hmrCount = 0
|
|
21
|
+
let lastTipLines = 0
|
|
22
|
+
const HMR_THRESHOLD = 4
|
|
23
|
+
|
|
24
|
+
function showTip() {
|
|
25
|
+
const tip = tips[Math.floor(Math.random() * tips.length)]
|
|
26
|
+
|
|
27
|
+
// Clear previous tip (move up and erase lines)
|
|
28
|
+
if (lastTipLines > 0) {
|
|
29
|
+
process.stdout.write(`\x1B[${lastTipLines}A`)
|
|
30
|
+
for (let i = 0; i < lastTipLines; i++) {
|
|
31
|
+
process.stdout.write('\x1B[2K\x1B[1E') // clear line + move down
|
|
32
|
+
}
|
|
33
|
+
process.stdout.write(`\x1B[${lastTipLines}A`)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Print new tip
|
|
37
|
+
const lines = [`💡 Blits Tip: ${tip}`, '']
|
|
38
|
+
lastTipLines = lines.length
|
|
39
|
+
process.stdout.write(lines.join('\n') + '\n')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default function blitsTipsPlugin() {
|
|
43
|
+
return {
|
|
44
|
+
name: 'vite:blits-tips',
|
|
45
|
+
apply: 'serve',
|
|
46
|
+
|
|
47
|
+
configureServer(server) {
|
|
48
|
+
server.watcher.on('change', () => {
|
|
49
|
+
hmrCount++
|
|
50
|
+
if (hmrCount >= HMR_THRESHOLD) {
|
|
51
|
+
showTip()
|
|
52
|
+
hmrCount = 0
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
},
|
|
56
|
+
}
|
|
57
|
+
}
|