@lightningjs/blits 1.7.1 → 1.8.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 +11 -0
- package/docs/essentials/displaying_text.md +53 -1
- package/docs/transitions_animations/transitions.md +21 -1
- package/index.d.ts +819 -14
- package/package.json +4 -3
- package/src/engines/L3/element.js +9 -1
- package/vite/msdfGenerator.js +89 -10
- package/src/application.d.ts +0 -174
- package/src/component.d.ts +0 -455
- package/src/launch.d.ts +0 -273
- package/src/plugin.d.ts +0 -71
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightningjs/blits",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "Blits: The Lightning 3 App Development Framework",
|
|
5
5
|
"bin": "bin/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"prepublishOnly": "node scripts/prepublishOnly.js",
|
|
20
20
|
"postpublish": "node scripts/postpublish.js"
|
|
21
21
|
},
|
|
22
|
+
"types": "./index.d.ts",
|
|
22
23
|
"lint-staged": {
|
|
23
24
|
"*.js": [
|
|
24
25
|
"eslint --fix"
|
|
@@ -47,8 +48,8 @@
|
|
|
47
48
|
"tape": "^5.5.0"
|
|
48
49
|
},
|
|
49
50
|
"dependencies": {
|
|
50
|
-
"@lightningjs/msdf-generator": "^1.0
|
|
51
|
-
"@lightningjs/renderer": "^2.
|
|
51
|
+
"@lightningjs/msdf-generator": "^1.1.0",
|
|
52
|
+
"@lightningjs/renderer": "^2.4.0"
|
|
52
53
|
},
|
|
53
54
|
"repository": {
|
|
54
55
|
"type": "git",
|
|
@@ -386,13 +386,21 @@ const Element = {
|
|
|
386
386
|
f,
|
|
387
387
|
}
|
|
388
388
|
|
|
389
|
-
if (transition.start && typeof transition.start === 'function') {
|
|
389
|
+
if (transition.start !== undefined && typeof transition.start === 'function') {
|
|
390
390
|
// fire transition start callback when animation really starts (depending on specified delay)
|
|
391
391
|
f.once('animating', () => {
|
|
392
392
|
transition.start.call(this.component, this, prop, startValue)
|
|
393
393
|
})
|
|
394
394
|
}
|
|
395
395
|
|
|
396
|
+
if (transition.progress !== undefined && typeof transition.progress === 'function') {
|
|
397
|
+
let prevProgress = 0
|
|
398
|
+
f.on('tick', (_node, { progress }) => {
|
|
399
|
+
transition.progress.call(this.component, this, prop, progress, prevProgress)
|
|
400
|
+
prevProgress = progress
|
|
401
|
+
})
|
|
402
|
+
}
|
|
403
|
+
|
|
396
404
|
f.once('stopped', () => {
|
|
397
405
|
// remove the prop from scheduled transitions
|
|
398
406
|
this.scheduledTransitions[prop] = undefined
|
package/vite/msdfGenerator.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
2
|
import * as fs from 'fs'
|
|
3
|
+
import { createHash } from 'crypto'
|
|
3
4
|
import { genFont, setGeneratePaths } from '@lightningjs/msdf-generator'
|
|
4
5
|
import { adjustFont } from '@lightningjs/msdf-generator/adjustFont'
|
|
5
6
|
|
|
@@ -21,6 +22,8 @@ class TaskQueue {
|
|
|
21
22
|
|
|
22
23
|
const fontGenerationQueue = new TaskQueue()
|
|
23
24
|
let config
|
|
25
|
+
let checksumPaths = []
|
|
26
|
+
let checksumData = []
|
|
24
27
|
|
|
25
28
|
export default function () {
|
|
26
29
|
let msdfOutputDir = ''
|
|
@@ -53,10 +56,12 @@ export default function () {
|
|
|
53
56
|
// Attempt to find the font file with supported extensions
|
|
54
57
|
const supportedExtensions = ['ttf', 'otf', 'woff']
|
|
55
58
|
let fontFile = null
|
|
59
|
+
let fontType = null
|
|
56
60
|
for (const fontExt of supportedExtensions) {
|
|
57
61
|
const potentialPath = path.join(fontDir, `${fontName}.${fontExt}`)
|
|
58
62
|
if (fs.existsSync(potentialPath)) {
|
|
59
63
|
fontFile = potentialPath
|
|
64
|
+
fontType = '.' + fontExt
|
|
60
65
|
break
|
|
61
66
|
}
|
|
62
67
|
}
|
|
@@ -70,10 +75,13 @@ export default function () {
|
|
|
70
75
|
const mimeType = ext === 'png' ? 'image/png' : 'application/json'
|
|
71
76
|
|
|
72
77
|
await fontGenerationQueue.enqueue(async () => {
|
|
73
|
-
if (
|
|
74
|
-
|
|
78
|
+
if (isGenerationRequired(fontDir, targetDir, fontName, fontType)) {
|
|
79
|
+
const configFilePath = path.join(fontDir, `${fontName}.config.json`)
|
|
80
|
+
|
|
75
81
|
console.log(`\nGenerating ${targetDir}/${fontName}.msdf.${ext}`)
|
|
76
|
-
await generateSDF(fontFile, path.join(targetDir))
|
|
82
|
+
await generateSDF(fontFile, path.join(targetDir), configFilePath)
|
|
83
|
+
|
|
84
|
+
saveChecksum()
|
|
77
85
|
}
|
|
78
86
|
})
|
|
79
87
|
|
|
@@ -109,14 +117,17 @@ export default function () {
|
|
|
109
117
|
|
|
110
118
|
for (const fontFile of fontFiles) {
|
|
111
119
|
const relativePath = path.relative(publicDir, path.dirname(fontFile))
|
|
120
|
+
const fontExt = path.extname(fontFile)
|
|
112
121
|
const baseName = path.basename(fontFile).replace(/\.(ttf|otf|woff)$/i, '')
|
|
113
|
-
const
|
|
114
|
-
const msdfPngPath = path.join(msdfOutputDir, relativePath, `${baseName}.msdf.png`)
|
|
122
|
+
const targetDir = path.join(msdfOutputDir, relativePath)
|
|
115
123
|
|
|
116
|
-
// Check
|
|
117
|
-
if (
|
|
124
|
+
// Check MSDF generation is required
|
|
125
|
+
if (isGenerationRequired(path.dirname(fontFile), targetDir, baseName, fontExt)) {
|
|
126
|
+
const configFilePath = path.join(path.dirname(fontFile), `${baseName}.config.json`)
|
|
118
127
|
console.log(`Generating missing MSDF files for ${fontFile}`)
|
|
119
|
-
await generateSDF(fontFile, path.join(msdfOutputDir, relativePath))
|
|
128
|
+
await generateSDF(fontFile, path.join(msdfOutputDir, relativePath), configFilePath)
|
|
129
|
+
|
|
130
|
+
saveChecksum()
|
|
120
131
|
}
|
|
121
132
|
}
|
|
122
133
|
|
|
@@ -127,11 +138,11 @@ export default function () {
|
|
|
127
138
|
}
|
|
128
139
|
}
|
|
129
140
|
|
|
130
|
-
const generateSDF = async (inputFilePath, outputDirPath) => {
|
|
141
|
+
const generateSDF = async (inputFilePath, outputDirPath, configFilePath) => {
|
|
131
142
|
// Ensure the destination directory exists
|
|
132
143
|
fs.mkdirSync(outputDirPath, { recursive: true })
|
|
133
144
|
|
|
134
|
-
setGeneratePaths(path.dirname(inputFilePath), outputDirPath)
|
|
145
|
+
setGeneratePaths(path.dirname(inputFilePath), outputDirPath, configFilePath)
|
|
135
146
|
|
|
136
147
|
let font = await genFont(path.basename(inputFilePath), 'msdf')
|
|
137
148
|
|
|
@@ -168,6 +179,74 @@ const copyDir = (src, dest) => {
|
|
|
168
179
|
const srcPath = path.join(src, entry.name)
|
|
169
180
|
const destPath = path.join(dest, entry.name)
|
|
170
181
|
|
|
182
|
+
// Should not copy .checksum files to dist directory
|
|
183
|
+
if (srcPath.indexOf('.checksum') !== -1) continue
|
|
184
|
+
|
|
171
185
|
entry.isDirectory() ? copyDir(srcPath, destPath) : fs.copyFileSync(srcPath, destPath) // Copy files
|
|
172
186
|
}
|
|
173
187
|
}
|
|
188
|
+
|
|
189
|
+
// Check font config.json or font file modified since last generation
|
|
190
|
+
const isGenerationRequired = (fontDir, targetDir, fontName, fontExt) => {
|
|
191
|
+
const fontFilePath = path.resolve(fontDir, fontName + fontExt)
|
|
192
|
+
const fontChecksumPath = path.resolve(targetDir, fontName + fontExt + '.checksum')
|
|
193
|
+
|
|
194
|
+
const configJsonPath = path.resolve(fontDir, fontName + '.config.json')
|
|
195
|
+
const configChecksumPath = path.resolve(targetDir, fontName + '.config.checksum')
|
|
196
|
+
|
|
197
|
+
// If font file checksum not exists, should generate msdf
|
|
198
|
+
if (!fs.existsSync(fontChecksumPath)) {
|
|
199
|
+
checksumPaths.push(fontChecksumPath)
|
|
200
|
+
checksumData.push(generateHash(fontFilePath))
|
|
201
|
+
|
|
202
|
+
if (fs.existsSync(configJsonPath)) {
|
|
203
|
+
checksumPaths.push(configChecksumPath)
|
|
204
|
+
checksumData.push(generateHash(configJsonPath))
|
|
205
|
+
}
|
|
206
|
+
return true
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// If config.json exists, can be newly added or modified since last generation of msdf
|
|
210
|
+
if (fs.existsSync(configJsonPath)) {
|
|
211
|
+
// If config.json checksum not exists, should generate msdf
|
|
212
|
+
if (!fs.existsSync(configChecksumPath)) {
|
|
213
|
+
checksumPaths.push(configChecksumPath)
|
|
214
|
+
checksumData.push(generateHash(configJsonPath))
|
|
215
|
+
return true
|
|
216
|
+
}
|
|
217
|
+
const isConfigModified = isInputFileModified(configJsonPath, configChecksumPath)
|
|
218
|
+
|
|
219
|
+
// If config.json is modified, should generate msdf
|
|
220
|
+
if (isConfigModified) return true
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Check font file modified, if modified, should generate msdf
|
|
224
|
+
return isInputFileModified(fontFilePath, fontChecksumPath)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const isInputFileModified = (inputFilePath, targetFilePath) => {
|
|
228
|
+
const inputChecksum = generateHash(inputFilePath)
|
|
229
|
+
const targetChecksum = fs.readFileSync(targetFilePath).toString()
|
|
230
|
+
if (inputChecksum !== targetChecksum) {
|
|
231
|
+
checksumPaths.push(targetFilePath)
|
|
232
|
+
checksumData.push(inputChecksum)
|
|
233
|
+
return true
|
|
234
|
+
}
|
|
235
|
+
return false
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const generateHash = (filePath) => {
|
|
239
|
+
const buffer = fs.readFileSync(filePath)
|
|
240
|
+
return createHash('md5').update(buffer).digest('hex')
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const saveChecksum = () => {
|
|
244
|
+
if (checksumPaths.length > 0 && checksumData.length > 0) {
|
|
245
|
+
for (let i = 0; i < checksumPaths.length; i++) {
|
|
246
|
+
// Save checksum
|
|
247
|
+
fs.writeFileSync(checksumPaths[i], checksumData[i])
|
|
248
|
+
}
|
|
249
|
+
checksumData.length = 0
|
|
250
|
+
checksumPaths.length = 0
|
|
251
|
+
}
|
|
252
|
+
}
|
package/src/application.d.ts
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
/*
|
|
3
|
-
* Copyright 2023 Comcast Cable Communications Management, LLC
|
|
4
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
* you may not use this file except in compliance with the License.
|
|
6
|
-
* You may obtain a copy of the License at
|
|
7
|
-
*
|
|
8
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
*
|
|
10
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
* See the License for the specific language governing permissions and
|
|
14
|
-
* limitations under the License.
|
|
15
|
-
*
|
|
16
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import {default as Component} from './component'
|
|
20
|
-
|
|
21
|
-
declare namespace Application {
|
|
22
|
-
|
|
23
|
-
interface Transition {
|
|
24
|
-
/**
|
|
25
|
-
* Name of the prop to transition (i.e. 'x', 'y', 'alpha', 'color')
|
|
26
|
-
*/
|
|
27
|
-
prop: string,
|
|
28
|
-
/**
|
|
29
|
-
* Value the prop should transition to (i.e. 0, 100, '#223388')
|
|
30
|
-
*/
|
|
31
|
-
value: any,
|
|
32
|
-
/**
|
|
33
|
-
* Duration of the transition in milliseconds, defaults to 300
|
|
34
|
-
*/
|
|
35
|
-
duration?: number,
|
|
36
|
-
/**
|
|
37
|
-
* Easing function to apply to the transition
|
|
38
|
-
*/
|
|
39
|
-
easing?: string,
|
|
40
|
-
/**
|
|
41
|
-
* Delay before the transition starts in milliseconds
|
|
42
|
-
*/
|
|
43
|
-
delay?: number
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
interface Before {
|
|
47
|
-
/**
|
|
48
|
-
* Name of the prop to set before the transition starts
|
|
49
|
-
*/
|
|
50
|
-
prop: string,
|
|
51
|
-
/**
|
|
52
|
-
* Value the prop to set before the transition starts
|
|
53
|
-
*/
|
|
54
|
-
value: any,
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
interface RouteTransition {
|
|
58
|
-
/**
|
|
59
|
-
* Setting or Array of Settings before new view enters into the router view
|
|
60
|
-
*/
|
|
61
|
-
before: Before | Before[],
|
|
62
|
-
/**
|
|
63
|
-
* Transition or Array of Transitions for new view to enters into the router view
|
|
64
|
-
*/
|
|
65
|
-
in: Transition | Transition[],
|
|
66
|
-
/**
|
|
67
|
-
* Transition or Array of Transitions for old view to leave the router view
|
|
68
|
-
*/
|
|
69
|
-
out: Transition | Transition[],
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
type RouteTransitionFunction = (previousRoute: Route, currentRoute: Route) => RequireAtLeastOne<RouteTransition>
|
|
73
|
-
|
|
74
|
-
interface RouteAnnounce {
|
|
75
|
-
/**
|
|
76
|
-
* Message to be announced
|
|
77
|
-
*/
|
|
78
|
-
message: String,
|
|
79
|
-
/**
|
|
80
|
-
* Politeness level
|
|
81
|
-
*
|
|
82
|
-
* Defaults to 'off'
|
|
83
|
-
*/
|
|
84
|
-
politeness?: 'off' | 'polite' | 'assertive'
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
type RequireAtLeastOne<T> = {
|
|
88
|
-
[K in keyof T]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<keyof T, K>>>
|
|
89
|
-
}[keyof T]
|
|
90
|
-
|
|
91
|
-
interface RouteHooks {
|
|
92
|
-
before?: (to: Route, from: Route) => string | Route;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
type Route = {
|
|
96
|
-
/**
|
|
97
|
-
* URI path for the route
|
|
98
|
-
*/
|
|
99
|
-
path: string,
|
|
100
|
-
/**
|
|
101
|
-
* Component to load when activating the route
|
|
102
|
-
*/
|
|
103
|
-
component: Component,
|
|
104
|
-
/**
|
|
105
|
-
* Transition configuration for the route
|
|
106
|
-
*/
|
|
107
|
-
transition?: RequireAtLeastOne<RouteTransition> | RouteTransitionFunction,
|
|
108
|
-
/**
|
|
109
|
-
* Extra route options
|
|
110
|
-
*/
|
|
111
|
-
options?: object // todo: specify which options are available,
|
|
112
|
-
/**
|
|
113
|
-
* Message to be announced when visiting the route (often used for accessibility purposes)
|
|
114
|
-
*
|
|
115
|
-
* Can be either a `String` with the message or an object that defines the message and the
|
|
116
|
-
* politeness level
|
|
117
|
-
*/
|
|
118
|
-
announce?: String | RouteAnnounce
|
|
119
|
-
/**
|
|
120
|
-
* Register hooks for the route
|
|
121
|
-
*/
|
|
122
|
-
hooks?: RouteHooks
|
|
123
|
-
/**
|
|
124
|
-
* Route path parameters
|
|
125
|
-
*/
|
|
126
|
-
readonly params?: {
|
|
127
|
-
[key: string]: string | number
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Allows for attaching custom data to a route, either hardcoded in the
|
|
131
|
-
* route definition or asigned to the route object in a before hook
|
|
132
|
-
*
|
|
133
|
-
* Will be merged with the route params and navigation data and passed as
|
|
134
|
-
* props into the route component
|
|
135
|
-
*/
|
|
136
|
-
data?: {
|
|
137
|
-
[key: string]: any
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export interface ApplicationInstance extends Component.ComponentInstance {}
|
|
142
|
-
|
|
143
|
-
export interface ApplicationConfig<Props extends string, S, M> extends Component.ComponentConfig<Props, S, M> {
|
|
144
|
-
/**
|
|
145
|
-
* Routes definition
|
|
146
|
-
*
|
|
147
|
-
* @example
|
|
148
|
-
*
|
|
149
|
-
* ```js
|
|
150
|
-
* routes: [
|
|
151
|
-
* { path: '/', component: Home },
|
|
152
|
-
* { path: '/details', component: Details },
|
|
153
|
-
* { path: '/account', component: Account },
|
|
154
|
-
* ]
|
|
155
|
-
* ```
|
|
156
|
-
*/
|
|
157
|
-
routes?: Route[]
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Blits.Application()
|
|
163
|
-
*
|
|
164
|
-
* Root App component
|
|
165
|
-
*/
|
|
166
|
-
declare function Application<
|
|
167
|
-
Props extends string,
|
|
168
|
-
S extends Component.State,
|
|
169
|
-
M extends Component.Methods
|
|
170
|
-
>(
|
|
171
|
-
config: Application.ApplicationConfig<Props, S, M>
|
|
172
|
-
) : Application.ApplicationInstance
|
|
173
|
-
|
|
174
|
-
export default Application;
|