@lightningjs/blits 1.46.0 → 1.47.0
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 +8 -0
- package/package.json +1 -1
- package/src/component/base/methods.js +2 -0
- package/src/engines/L3/element.js +10 -2
- package/src/lib/reactivityguard/computedprops.js +79 -13
- package/src/plugins/theme.d.ts +7 -0
- package/src/plugins/theme.js +30 -3
- package/src/router/router.js +1 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v1.47.0
|
|
4
|
+
|
|
5
|
+
_26 jan 2026_
|
|
6
|
+
|
|
7
|
+
- Fixed issue with reactivity guard vite plugin failing for commented-out code
|
|
8
|
+
- Fixed issue with autosizing of holder node by adding extra `eol` check
|
|
9
|
+
- Added support for _variants_ to the Theme plugin
|
|
10
|
+
|
|
3
11
|
## v1.46.0
|
|
4
12
|
|
|
5
13
|
_22 jan 2026_
|
package/package.json
CHANGED
|
@@ -112,6 +112,8 @@ export default {
|
|
|
112
112
|
// pass focus to the parent so we don't get lost in focus limbo
|
|
113
113
|
if (this.hasFocus === true) this.parent.$focus()
|
|
114
114
|
|
|
115
|
+
// @todo - is this really necessary?
|
|
116
|
+
// This cause an issue with auto sizing of parent (and required an extra eol check there)
|
|
115
117
|
for (let key in this[symbols.state]) {
|
|
116
118
|
if (Array.isArray(this[symbols.state][key])) {
|
|
117
119
|
this[symbols.state][key] = []
|
|
@@ -243,7 +243,11 @@ const propsTransformer = {
|
|
|
243
243
|
},
|
|
244
244
|
set w(v) {
|
|
245
245
|
this.props['width'] = parsePercentage.call(this, v, 'width')
|
|
246
|
-
if (
|
|
246
|
+
if (
|
|
247
|
+
this.___wrapper === true &&
|
|
248
|
+
this.element.component.eol !== true &&
|
|
249
|
+
this.element.component[symbols.holder] !== undefined
|
|
250
|
+
) {
|
|
247
251
|
this.element.component[symbols.holder].set('w', this.props['width'])
|
|
248
252
|
}
|
|
249
253
|
},
|
|
@@ -252,7 +256,11 @@ const propsTransformer = {
|
|
|
252
256
|
},
|
|
253
257
|
set h(v) {
|
|
254
258
|
this.props['height'] = parsePercentage.call(this, v, 'height')
|
|
255
|
-
if (
|
|
259
|
+
if (
|
|
260
|
+
this.___wrapper === true &&
|
|
261
|
+
this.element.component.eol !== true &&
|
|
262
|
+
this.element.component[symbols.holder] !== undefined
|
|
263
|
+
) {
|
|
256
264
|
this.element.component[symbols.holder].set('h', this.props['height'])
|
|
257
265
|
}
|
|
258
266
|
},
|
|
@@ -35,10 +35,12 @@ const findMatchingBrace = (text, startPos) => {
|
|
|
35
35
|
// Skip comments when not in strings
|
|
36
36
|
else if (!inString && char === '/' && text[position + 1] === '/') {
|
|
37
37
|
// Skip line comment
|
|
38
|
-
|
|
38
|
+
const nextNewline = text.indexOf('\n', position + 2)
|
|
39
|
+
position = nextNewline === -1 ? text.length - 1 : nextNewline
|
|
39
40
|
} else if (!inString && char === '/' && text[position + 1] === '*') {
|
|
40
41
|
// Skip block comment
|
|
41
|
-
|
|
42
|
+
const end = text.indexOf('*/', position + 2)
|
|
43
|
+
position = end === -1 ? text.length - 1 : end + 1
|
|
42
44
|
}
|
|
43
45
|
// Count braces only outside strings and comments
|
|
44
46
|
else if (!inString) {
|
|
@@ -60,6 +62,44 @@ const findComputedFunctionEnd = (computedObj, openingBracePos) => {
|
|
|
60
62
|
return findMatchingBrace(computedObj, openingBracePos)
|
|
61
63
|
}
|
|
62
64
|
|
|
65
|
+
// Skip whitespace + commas + comments (line and block) starting from `pos`, return next code position.
|
|
66
|
+
const skipNonCode = (text, pos) => {
|
|
67
|
+
let p = pos
|
|
68
|
+
while (p < text.length) {
|
|
69
|
+
const c = text[p]
|
|
70
|
+
|
|
71
|
+
// whitespace
|
|
72
|
+
if (c === ' ' || c === '\t' || c === '\n' || c === '\r') {
|
|
73
|
+
p++
|
|
74
|
+
continue
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// separators between object members
|
|
78
|
+
if (c === ',') {
|
|
79
|
+
p++
|
|
80
|
+
continue
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// line comment
|
|
84
|
+
if (c === '/' && text[p + 1] === '/') {
|
|
85
|
+
const nextNewline = text.indexOf('\n', p + 2)
|
|
86
|
+
p = nextNewline === -1 ? text.length : nextNewline + 1
|
|
87
|
+
continue
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// block comment
|
|
91
|
+
if (c === '/' && text[p + 1] === '*') {
|
|
92
|
+
const end = text.indexOf('*/', p + 2)
|
|
93
|
+
p = end === -1 ? text.length : end + 2
|
|
94
|
+
continue
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return p
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return p
|
|
101
|
+
}
|
|
102
|
+
|
|
63
103
|
export default (code) => {
|
|
64
104
|
// Find component declarations
|
|
65
105
|
const componentRegex =
|
|
@@ -83,6 +123,8 @@ export default (code) => {
|
|
|
83
123
|
|
|
84
124
|
if (computedEnd !== -1) {
|
|
85
125
|
const computedObj = configObject.substring(computedStart, computedEnd)
|
|
126
|
+
const computedBlockStart = computedKeywordMatch.index
|
|
127
|
+
const computedBlockEnd = computedEnd
|
|
86
128
|
|
|
87
129
|
// Process each computed property
|
|
88
130
|
const computedProps = []
|
|
@@ -93,15 +135,20 @@ export default (code) => {
|
|
|
93
135
|
// using cursor-based parsing gives better accuracy
|
|
94
136
|
while (currentPos < computedObj.length - 1) {
|
|
95
137
|
// -1 to exclude the closing brace
|
|
96
|
-
//
|
|
97
|
-
|
|
138
|
+
// Skip whitespace/comments so we don't match commented-out computed functions
|
|
139
|
+
currentPos = skipNonCode(computedObj, currentPos)
|
|
140
|
+
if (currentPos >= computedObj.length - 1) break
|
|
141
|
+
|
|
142
|
+
// Find the next function name (computed properties are `name() { ... }`)
|
|
143
|
+
const functionNameMatch = /^(\w+)\s*\(\)\s*/.exec(computedObj.substring(currentPos))
|
|
98
144
|
if (!functionNameMatch) break
|
|
99
145
|
|
|
100
146
|
const funcName = functionNameMatch[1]
|
|
101
|
-
const funcNamePos = currentPos
|
|
147
|
+
const funcNamePos = currentPos
|
|
102
148
|
|
|
103
149
|
// Find opening brace for this function
|
|
104
|
-
const
|
|
150
|
+
const afterSignaturePos = funcNamePos + functionNameMatch[0].length
|
|
151
|
+
const openingBracePos = computedObj.indexOf('{', afterSignaturePos)
|
|
105
152
|
if (openingBracePos === -1) break
|
|
106
153
|
|
|
107
154
|
// Find closing brace for individual computed function
|
|
@@ -133,14 +180,15 @@ export default (code) => {
|
|
|
133
180
|
}
|
|
134
181
|
|
|
135
182
|
// Store the original computed object for later replacement
|
|
136
|
-
const originalComputedObj = configObject.substring(
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
)
|
|
183
|
+
const originalComputedObj = configObject.substring(computedBlockStart, computedBlockEnd)
|
|
184
|
+
const computedObjOffsetInOriginal = computedStart - computedBlockStart
|
|
185
|
+
const computedObjEndInOriginal = computedObjOffsetInOriginal + computedObj.length
|
|
140
186
|
|
|
141
187
|
let modifiedComputedObj = originalComputedObj
|
|
142
188
|
let hasChanges = false
|
|
143
189
|
|
|
190
|
+
const replacements = []
|
|
191
|
+
|
|
144
192
|
for (const prop of computedProps) {
|
|
145
193
|
const thisRefs = extractThisReferences(prop.body)
|
|
146
194
|
|
|
@@ -181,13 +229,31 @@ export default (code) => {
|
|
|
181
229
|
prop.fullText.substring(0, openBraceIndex).match(/^(\s*)/)[0] +
|
|
182
230
|
'}'
|
|
183
231
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
232
|
+
replacements.push({
|
|
233
|
+
start: prop.startPos,
|
|
234
|
+
end: prop.endPos,
|
|
235
|
+
text: modifiedPropText,
|
|
236
|
+
})
|
|
187
237
|
}
|
|
188
238
|
}
|
|
189
239
|
}
|
|
190
240
|
|
|
241
|
+
if (replacements.length > 0) {
|
|
242
|
+
let modifiedComputedInner = computedObj
|
|
243
|
+
|
|
244
|
+
replacements.sort((a, b) => b.start - a.start)
|
|
245
|
+
for (const r of replacements) {
|
|
246
|
+
modifiedComputedInner =
|
|
247
|
+
modifiedComputedInner.slice(0, r.start) + r.text + modifiedComputedInner.slice(r.end)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
modifiedComputedObj =
|
|
251
|
+
originalComputedObj.slice(0, computedObjOffsetInOriginal) +
|
|
252
|
+
modifiedComputedInner +
|
|
253
|
+
originalComputedObj.slice(computedObjEndInOriginal)
|
|
254
|
+
hasChanges = true
|
|
255
|
+
}
|
|
256
|
+
|
|
191
257
|
if (hasChanges) {
|
|
192
258
|
// Store the modification of the entire computed object
|
|
193
259
|
modifications.push({
|
package/src/plugins/theme.d.ts
CHANGED
|
@@ -18,13 +18,20 @@
|
|
|
18
18
|
export interface ThemePlugin {
|
|
19
19
|
get<T = unknown>(key: string): T | undefined
|
|
20
20
|
get<T>(key: string, fallback: T): T
|
|
21
|
+
/**
|
|
22
|
+
* @deprecated
|
|
23
|
+
* Deprecated: use `$theme.current()` instead
|
|
24
|
+
*/
|
|
21
25
|
set(theme: string): void
|
|
26
|
+
current(theme: string): void
|
|
27
|
+
variant(theme: string): void
|
|
22
28
|
}
|
|
23
29
|
|
|
24
30
|
export interface ThemePluginConfig {
|
|
25
31
|
themes?: Record<string, Record<string, unknown>>
|
|
26
32
|
current?: string
|
|
27
33
|
base?: string
|
|
34
|
+
variant?: string
|
|
28
35
|
}
|
|
29
36
|
|
|
30
37
|
declare const theme: {
|
package/src/plugins/theme.js
CHANGED
|
@@ -20,13 +20,17 @@ import { reactive } from '../lib/reactivity/reactive.js'
|
|
|
20
20
|
|
|
21
21
|
import Settings from '../settings.js'
|
|
22
22
|
|
|
23
|
-
const getValueByDotNotation = (obj, base, path) => {
|
|
23
|
+
const getValueByDotNotation = (obj, base, variant, path) => {
|
|
24
24
|
const keys = path.split('.')
|
|
25
25
|
|
|
26
26
|
for (let i = 0; i < keys.length; i++) {
|
|
27
27
|
const key = keys[i]
|
|
28
28
|
|
|
29
|
-
if (
|
|
29
|
+
if (variant !== null && key in variant === true) {
|
|
30
|
+
obj = variant[key]
|
|
31
|
+
variant = variant !== null && key in variant === true ? variant[key] : null
|
|
32
|
+
base = base !== null && key in base === true ? base[key] : null
|
|
33
|
+
} else if (obj !== null && key in obj === true) {
|
|
30
34
|
obj = obj[key]
|
|
31
35
|
base = base !== null && key in base === true ? base[key] : null
|
|
32
36
|
}
|
|
@@ -51,6 +55,7 @@ export default {
|
|
|
51
55
|
const state = reactive(
|
|
52
56
|
{
|
|
53
57
|
current: 'default',
|
|
58
|
+
variant: null,
|
|
54
59
|
},
|
|
55
60
|
Settings.get('reactivityMode'),
|
|
56
61
|
true
|
|
@@ -62,6 +67,7 @@ export default {
|
|
|
62
67
|
themes = config.themes
|
|
63
68
|
state.current = config.current || 'default'
|
|
64
69
|
base = config.base || 'default'
|
|
70
|
+
state.variant = config.variant || null
|
|
65
71
|
} else {
|
|
66
72
|
themes = { default: config }
|
|
67
73
|
}
|
|
@@ -71,8 +77,14 @@ export default {
|
|
|
71
77
|
// reference state.current, to trigger reactive effects for
|
|
72
78
|
// theme get function when current theme is modified
|
|
73
79
|
state.current
|
|
80
|
+
state.variant
|
|
74
81
|
if (themeMap[key] !== undefined) return themeMap[key]
|
|
75
|
-
const value = getValueByDotNotation(
|
|
82
|
+
const value = getValueByDotNotation(
|
|
83
|
+
themes[state.current],
|
|
84
|
+
themes[base] || null,
|
|
85
|
+
themes[state.variant] || null,
|
|
86
|
+
key
|
|
87
|
+
)
|
|
76
88
|
if (value !== undefined) {
|
|
77
89
|
// store the value for next time
|
|
78
90
|
themeMap[key] = value
|
|
@@ -82,6 +94,11 @@ export default {
|
|
|
82
94
|
},
|
|
83
95
|
|
|
84
96
|
set(theme) {
|
|
97
|
+
Log.warn('$theme.set() is deprecated, use $theme.current() instead')
|
|
98
|
+
this.current(theme)
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
current(theme) {
|
|
85
102
|
if (theme in themes) {
|
|
86
103
|
themeMap = {}
|
|
87
104
|
state.current = theme
|
|
@@ -89,6 +106,16 @@ export default {
|
|
|
89
106
|
Log.warn(`Theme ${theme} not found`)
|
|
90
107
|
}
|
|
91
108
|
},
|
|
109
|
+
|
|
110
|
+
variant(theme) {
|
|
111
|
+
if (theme in themes) {
|
|
112
|
+
themeMap = {}
|
|
113
|
+
state.variant = theme
|
|
114
|
+
console.log('change variant', theme)
|
|
115
|
+
} else {
|
|
116
|
+
Log.warn(`Variant ${theme} not found`)
|
|
117
|
+
}
|
|
118
|
+
},
|
|
92
119
|
}
|
|
93
120
|
},
|
|
94
121
|
}
|
package/src/router/router.js
CHANGED
|
@@ -482,15 +482,13 @@ export const navigate = async function () {
|
|
|
482
482
|
}
|
|
483
483
|
|
|
484
484
|
let shouldAnimate = false
|
|
485
|
-
// Declare oldView in broader scope so it can be used in hooks below
|
|
486
|
-
let oldView = null
|
|
487
485
|
|
|
488
486
|
// apply out out transition on previous view if available, unless
|
|
489
487
|
// we're reusing the prvious page component
|
|
490
488
|
if (previousRoute !== undefined && reuse === false) {
|
|
491
489
|
// only animate when there is a previous route
|
|
492
490
|
shouldAnimate = true
|
|
493
|
-
oldView = this[symbols.children].splice(1, 1).pop()
|
|
491
|
+
const oldView = this[symbols.children].splice(1, 1).pop()
|
|
494
492
|
if (oldView) {
|
|
495
493
|
await removeView(previousRoute, oldView, route.transition.out, navigatingBack)
|
|
496
494
|
}
|