@lightningjs/blits 1.46.0 → 1.48.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 +22 -0
- package/package.json +2 -2
- package/scripts/prepush.js +21 -1
- package/src/component/base/methods.js +2 -0
- package/src/component.js +3 -0
- package/src/engines/L3/element.js +43 -8
- package/src/lib/codegenerator/generator.js +22 -11
- package/src/lib/lifecycle.js +2 -2
- package/src/lib/reactivity/effect.js +3 -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 +2 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
|
|
4
|
+
## v1.48.0
|
|
5
|
+
|
|
6
|
+
_10 feb 2026_
|
|
7
|
+
|
|
8
|
+
- Improved cleanup of for-loop effects
|
|
9
|
+
- Fixed router issue with params not being reset
|
|
10
|
+
- Added next tick debouncing for transitions on multiple props
|
|
11
|
+
- Bumped renderer to 2.21.0
|
|
12
|
+
- Added support for using function-notation in `@loaded`, `@error` and `@updated` callbacks
|
|
13
|
+
- Added displaying of correct component type in inspector data when using `is`-attribute
|
|
14
|
+
- Replaced `$`-prefix for automatic inspector data with `blits-` prefix (fixes fatal error in Safari)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## v1.47.0
|
|
18
|
+
|
|
19
|
+
_26 jan 2026_
|
|
20
|
+
|
|
21
|
+
- Fixed issue with reactivity guard vite plugin failing for commented-out code
|
|
22
|
+
- Fixed issue with autosizing of holder node by adding extra `eol` check
|
|
23
|
+
- Added support for _variants_ to the Theme plugin
|
|
24
|
+
|
|
3
25
|
## v1.46.0
|
|
4
26
|
|
|
5
27
|
_22 jan 2026_
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightningjs/blits",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.48.0",
|
|
4
4
|
"description": "Blits: The Lightning 3 App Development Framework",
|
|
5
5
|
"bin": "bin/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
},
|
|
75
75
|
"dependencies": {
|
|
76
76
|
"@lightningjs/msdf-generator": "^1.2.0",
|
|
77
|
-
"@lightningjs/renderer": "^2.
|
|
77
|
+
"@lightningjs/renderer": "^2.21.0",
|
|
78
78
|
"magic-string": "^0.30.21"
|
|
79
79
|
},
|
|
80
80
|
"repository": {
|
package/scripts/prepush.js
CHANGED
|
@@ -69,7 +69,6 @@ try {
|
|
|
69
69
|
// Run ESLint on the changed files
|
|
70
70
|
execSync(`npx eslint ${fileList.join(' ')}`, { stdio: 'inherit' })
|
|
71
71
|
console.log('Linting passed successfully!\n')
|
|
72
|
-
process.exit(0)
|
|
73
72
|
} catch (error) {
|
|
74
73
|
console.log('\n==========================================================================')
|
|
75
74
|
console.log('ESLint found issues in your code. Please fix them before pushing.')
|
|
@@ -79,3 +78,24 @@ try {
|
|
|
79
78
|
console.log('==========================================================================\n')
|
|
80
79
|
process.exit(1)
|
|
81
80
|
}
|
|
81
|
+
|
|
82
|
+
// By default, always run tests unless RUN_TEST=skip
|
|
83
|
+
const runTestsFlag = process.env.RUN_TEST || ''
|
|
84
|
+
|
|
85
|
+
if (runTestsFlag.toLowerCase() === 'skip') {
|
|
86
|
+
console.log('Skipping tests due to RUN_TEST=skip. Proceeding with push.')
|
|
87
|
+
process.exit(0)
|
|
88
|
+
} else {
|
|
89
|
+
// For any other value (including empty or unknown), run tests
|
|
90
|
+
try {
|
|
91
|
+
console.log('Running tests...\n')
|
|
92
|
+
execSync('npm run test', { stdio: 'inherit' })
|
|
93
|
+
console.log('Tests passed successfully!\n')
|
|
94
|
+
process.exit(0)
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.log('\n==========================================================================')
|
|
97
|
+
console.log('Some tests failed. Please fix them before pushing.')
|
|
98
|
+
console.log('==========================================================================\n')
|
|
99
|
+
process.exit(1)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -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] = []
|
package/src/component.js
CHANGED
|
@@ -450,6 +450,9 @@ const Component = (name = required('name'), config = required('config')) => {
|
|
|
450
450
|
// during the code generation step
|
|
451
451
|
factory[Symbol.for('config')] = config
|
|
452
452
|
|
|
453
|
+
// Display name for inspector
|
|
454
|
+
factory[Symbol.for('componentType')] = name
|
|
455
|
+
|
|
453
456
|
// To determine whether dynamic component is actual Blits component or not
|
|
454
457
|
factory[symbols.isComponent] = true
|
|
455
458
|
|
|
@@ -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
|
},
|
|
@@ -594,7 +602,7 @@ const Element = {
|
|
|
594
602
|
this.props.props['data'] = {}
|
|
595
603
|
}
|
|
596
604
|
|
|
597
|
-
// Merge framework data (
|
|
605
|
+
// Merge framework data (blits-* keys data attributes)
|
|
598
606
|
Object.assign(this.props['data'], data)
|
|
599
607
|
Object.assign(this.props.props['data'], data)
|
|
600
608
|
|
|
@@ -648,13 +656,31 @@ const Element = {
|
|
|
648
656
|
}
|
|
649
657
|
},
|
|
650
658
|
animate(prop, value, transition) {
|
|
659
|
+
// Clear any existing debounce timeout for this property
|
|
660
|
+
if (this.debounceTimeouts[prop] !== undefined) {
|
|
661
|
+
clearTimeout(this.debounceTimeouts[prop])
|
|
662
|
+
Log.debug(`Cleared debounce timeout for property "${prop}"`)
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// Debounce the animation execution
|
|
666
|
+
this.debounceTimeouts[prop] = setTimeout(() => {
|
|
667
|
+
delete this.debounceTimeouts[prop]
|
|
668
|
+
this._executeAnimation(prop, value, transition)
|
|
669
|
+
}, 0)
|
|
670
|
+
},
|
|
671
|
+
_executeAnimation(prop, value, transition) {
|
|
651
672
|
// check if a transition is already scheduled to run on the same prop
|
|
652
673
|
// and cancels it if it does
|
|
674
|
+
const stateOfAnimation =
|
|
675
|
+
this.scheduledTransitions[prop] !== undefined
|
|
676
|
+
? this.scheduledTransitions[prop].f.state
|
|
677
|
+
: undefined
|
|
678
|
+
|
|
653
679
|
if (
|
|
654
|
-
|
|
655
|
-
|
|
680
|
+
stateOfAnimation !== undefined &&
|
|
681
|
+
(stateOfAnimation === 'scheduled' || stateOfAnimation === 'running')
|
|
656
682
|
) {
|
|
657
|
-
this.scheduledTransitions[prop].f.stop()
|
|
683
|
+
this.scheduledTransitions[prop].f.stop(stateOfAnimation === 'running' ? false : true)
|
|
658
684
|
}
|
|
659
685
|
|
|
660
686
|
// if current value is the same as the value to animate to, instantly resolve
|
|
@@ -692,7 +718,7 @@ const Element = {
|
|
|
692
718
|
|
|
693
719
|
// Update inspector metadata when transition starts
|
|
694
720
|
if (inspectorEnabled === true) {
|
|
695
|
-
this.setInspectorMetadata({
|
|
721
|
+
this.setInspectorMetadata({ 'blits-isTransitioning': true })
|
|
696
722
|
}
|
|
697
723
|
|
|
698
724
|
if (transition.start !== undefined && typeof transition.start === 'function') {
|
|
@@ -732,7 +758,7 @@ const Element = {
|
|
|
732
758
|
// Update inspector metadata when transition ends
|
|
733
759
|
if (inspectorEnabled === true) {
|
|
734
760
|
this.setInspectorMetadata({
|
|
735
|
-
|
|
761
|
+
'blits-isTransitioning': Object.keys(this.scheduledTransitions).length > 0,
|
|
736
762
|
})
|
|
737
763
|
}
|
|
738
764
|
})
|
|
@@ -744,6 +770,14 @@ const Element = {
|
|
|
744
770
|
if (this.node === null) return
|
|
745
771
|
|
|
746
772
|
Log.debug('Deleting Node', this.nodeId)
|
|
773
|
+
|
|
774
|
+
// Clear all pending debounce timeouts
|
|
775
|
+
const debounceProps = Object.keys(this.debounceTimeouts)
|
|
776
|
+
for (let i = 0; i < debounceProps.length; i++) {
|
|
777
|
+
clearTimeout(this.debounceTimeouts[debounceProps[i]])
|
|
778
|
+
}
|
|
779
|
+
this.debounceTimeouts = null
|
|
780
|
+
|
|
747
781
|
// Clearing transition end callback functions
|
|
748
782
|
const transitionProps = Object.keys(this.scheduledTransitions)
|
|
749
783
|
for (let i = 0; i < transitionProps.length; i++) {
|
|
@@ -826,6 +860,7 @@ export default (config, component) => {
|
|
|
826
860
|
return Object.assign(Object.create(Element), {
|
|
827
861
|
props: Object.assign(Object.create(propsTransformer), { props: {} }),
|
|
828
862
|
scheduledTransitions: {},
|
|
863
|
+
debounceTimeouts: {},
|
|
829
864
|
config,
|
|
830
865
|
component,
|
|
831
866
|
})
|
|
@@ -360,17 +360,17 @@ const generateComponentCode = function (
|
|
|
360
360
|
}
|
|
361
361
|
`)
|
|
362
362
|
|
|
363
|
-
// For forloops, this code runs per instance, setting metadata for each component instance
|
|
364
363
|
if (isDev === true) {
|
|
365
|
-
const
|
|
364
|
+
const templateTagName = templateObject[Symbol.for('componentType')]
|
|
366
365
|
const holderElm = options.key
|
|
367
366
|
? `elms[${holderCounter}][${options.key}]`
|
|
368
367
|
: `elms[${holderCounter}]`
|
|
368
|
+
const componentDisplayName = `typeof componentType === 'string'
|
|
369
|
+
? componentType
|
|
370
|
+
: (componentType?.[Symbol.for('componentType')] || '${templateTagName}')`
|
|
369
371
|
renderCode.push(`
|
|
370
372
|
if (${holderElm} !== undefined && typeof ${holderElm}.setInspectorMetadata === 'function') {
|
|
371
|
-
${holderElm}.setInspectorMetadata({
|
|
372
|
-
$componentType: '${componentType}'
|
|
373
|
-
})
|
|
373
|
+
${holderElm}.setInspectorMetadata({ 'blits-componentType': ${componentDisplayName} })
|
|
374
374
|
}
|
|
375
375
|
`)
|
|
376
376
|
}
|
|
@@ -503,10 +503,16 @@ const generateForLoopCode = function (templateObject, parent) {
|
|
|
503
503
|
|
|
504
504
|
component !== null && component[Symbol.for('removeGlobalEffects')](effects[${forStartCounter}])
|
|
505
505
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
const
|
|
509
|
-
|
|
506
|
+
const effectsToRemove = new Set(effects[${forStartCounter}].slice(0))
|
|
507
|
+
if (effectsToRemove.size > 0) {
|
|
508
|
+
const componentEffects = component?.[Symbol.for('effects')] || []
|
|
509
|
+
let writeIndex = 0
|
|
510
|
+
for (let readIndex = 0; readIndex < componentEffects.length; readIndex++) {
|
|
511
|
+
if (!effectsToRemove.has(componentEffects[readIndex])) {
|
|
512
|
+
componentEffects[writeIndex++] = componentEffects[readIndex]
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
componentEffects.length = writeIndex
|
|
510
516
|
}
|
|
511
517
|
|
|
512
518
|
effects[${forStartCounter}].length = 0
|
|
@@ -804,8 +810,13 @@ const cast = (val = '', key = false, component = 'component.') => {
|
|
|
804
810
|
}
|
|
805
811
|
// @-listener
|
|
806
812
|
else if (key.startsWith('@') && val) {
|
|
807
|
-
const
|
|
808
|
-
|
|
813
|
+
const trimmed = val.trim()
|
|
814
|
+
if (/^\$?\w+$/.test(trimmed)) {
|
|
815
|
+
const c = component.slice(0, -1)
|
|
816
|
+
castedValue = `${c}['${trimmed.replace('$', '')}'] && ${c}['${trimmed.replace('$', '')}'].bind(${c})`
|
|
817
|
+
} else {
|
|
818
|
+
castedValue = interpolate(trimmed, component)
|
|
819
|
+
}
|
|
809
820
|
}
|
|
810
821
|
// dynamic value
|
|
811
822
|
else if (val.startsWith('$')) {
|
package/src/lib/lifecycle.js
CHANGED
|
@@ -97,7 +97,7 @@ export default {
|
|
|
97
97
|
this.component[symbols.holder] &&
|
|
98
98
|
typeof this.component[symbols.holder].setInspectorMetadata === 'function'
|
|
99
99
|
) {
|
|
100
|
-
this.component[symbols.holder].setInspectorMetadata({
|
|
100
|
+
this.component[symbols.holder].setInspectorMetadata({ 'blits-hasFocus': true })
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
if (v === 'unfocus') {
|
|
@@ -107,7 +107,7 @@ export default {
|
|
|
107
107
|
this.component[symbols.holder] &&
|
|
108
108
|
typeof this.component[symbols.holder].setInspectorMetadata === 'function'
|
|
109
109
|
) {
|
|
110
|
-
this.component[symbols.holder].setInspectorMetadata({
|
|
110
|
+
this.component[symbols.holder].setInspectorMetadata({ 'blits-hasFocus': false })
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
}
|
|
@@ -32,9 +32,10 @@ const objectMap = new WeakMap()
|
|
|
32
32
|
const globalEffectsMap = new Map()
|
|
33
33
|
|
|
34
34
|
export const removeGlobalEffects = (effectsToRemove) => {
|
|
35
|
-
if (globalEffectsMap.size === 0) return
|
|
35
|
+
if (globalEffectsMap.size === 0 || effectsToRemove.length === 0) return
|
|
36
|
+
const effectsToRemoveSet = new Set(effectsToRemove)
|
|
36
37
|
for (const [effect, target] of globalEffectsMap) {
|
|
37
|
-
if (
|
|
38
|
+
if (!effectsToRemoveSet.has(effect)) continue
|
|
38
39
|
const effectsSet = objectMap.get(target)
|
|
39
40
|
if (effectsSet === undefined) continue
|
|
40
41
|
for (const set of effectsSet.values()) {
|
|
@@ -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
|
@@ -403,7 +403,7 @@ export const navigate = async function () {
|
|
|
403
403
|
// Update router state after announcements and final route resolution,
|
|
404
404
|
// right before initializing or restoring the view
|
|
405
405
|
state.path = route.path
|
|
406
|
-
state.params = route.params
|
|
406
|
+
state.params = Object.keys(route.params).length === 0 ? null : route.params
|
|
407
407
|
state.hash = route.hash
|
|
408
408
|
state.data = null
|
|
409
409
|
state.data = route.data || {}
|
|
@@ -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
|
}
|