@lightningjs/blits 0.3.15 → 0.4.1

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 CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ # v0.4.1
4
+
5
+ _13 nov 2023_
6
+
7
+ - Added support for using dynamic import of components in routes
8
+ - Added support for returning components in an async function / Promise in routes
9
+
10
+ ## v0.4.0
11
+
12
+ _9 nov 2023_
13
+
14
+ - Fixed bug related to animating percentage based values
15
+ - Added (customizable) navigation transitions to the router
16
+ - Renamed `.eslintrc.js` to `.eslintrc.cjs` in boilerplate code to make linting work on new projects
17
+ - Fixed issue in Settings when the default value was set to `false`
18
+ - Replace underscored keys for private properties with Symbols
19
+ - Added `keepAlive` option to the router (i.e. keeping a page in memory when navigating to a new page)
20
+ - Introduced a `this.$trigger` function to force a reevaluation of a reactive value (without changing the value)
21
+ - Exposed `currentRoute`, `routes` and `navigating` on the `this.$router` object
22
+ - Upgraded to lates version of `@lightningjs/renderer` (v0.5.0)
23
+
3
24
  ## v0.3.15
4
25
 
5
26
  _23 oct 2023_
package/README.md CHANGED
@@ -20,8 +20,14 @@ Firstly, it's short, memorable and just has a nice ring to it.
20
20
 
21
21
  Finally it's a nerdy reference to the computer term "Bit Blits" commonly used in graphics processing, for rapidly moving a block of data into memory - quite similar to what Lightning is doing 🙂
22
22
 
23
+ ## Documentation
24
+
23
25
  Check out the [documentation](https://lightning-js.github.io/blits) to learn more.
24
26
 
27
+ ## Examples
28
+
29
+ Check out the open source [Demo/Example app](https://github.com/lightning-js/blits-example-app) to see what Blits and Lightning are capable of and learn how to use Blits by example.
30
+
25
31
  ## Changelog
26
32
 
27
33
  The full Changelog can be found [here](./CHANGELOG.md)
@@ -31,6 +31,6 @@
31
31
  "vite": "^4.0.4"
32
32
  },
33
33
  "dependencies": {
34
- "@lightningjs/blits": "^0.3.15"
34
+ "@lightningjs/blits": "^0.4.1"
35
35
  }
36
36
  }
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@lightningjs/blits",
3
- "version": "0.3.15",
3
+ "version": "0.4.1",
4
4
  "description": "Blits: The Lightning 3 App Development Framework",
5
5
  "bin": "bin/index.cjs",
6
6
  "exports": {
7
7
  ".": "./index.js",
8
8
  "./vite": "./vite/index.js",
9
- "./fontloader": "./src/fontLoader.js"
9
+ "./fontloader": "./src/fontLoader.js",
10
+ "./transitions": "./src/router/transitions/index.js"
10
11
  },
11
12
  "scripts": {
12
13
  "test": "c8 npm run test:run",
@@ -41,7 +42,7 @@
41
42
  "tape": "^5.5.0"
42
43
  },
43
44
  "dependencies": {
44
- "@lightningjs/renderer": "^0.4.0",
45
+ "@lightningjs/renderer": "^0.5.0",
45
46
  "@lightningjs/vite-plugin-import-chunk-url": "^0.3.0"
46
47
  }
47
48
  }
@@ -19,7 +19,66 @@ import {default as Component, ComponentInstance} from './component'
19
19
 
20
20
  declare namespace Application {
21
21
 
22
- interface Route {
22
+ interface Transition {
23
+ /**
24
+ * Name of the prop to transition (i.e. 'x', 'y', 'alpha', 'color')
25
+ */
26
+ prop: string,
27
+ /**
28
+ * Value the prop should transition to (i.e. 0, 100, '#223388')
29
+ */
30
+ value: any,
31
+ /**
32
+ * Duration of the transition in milliseconds, defaults to 300
33
+ */
34
+ duration?: number,
35
+ /**
36
+ * Easing function to apply to the transition
37
+ */
38
+ function?: string,
39
+ /**
40
+ * Delay before the transition starts in milliseconds
41
+ */
42
+ delay?: number
43
+ }
44
+
45
+ interface Before {
46
+ /**
47
+ * Name of the prop to set before the transition starts
48
+ */
49
+ prop: string,
50
+ /**
51
+ * Value the prop to set before the transition starts
52
+ */
53
+ value: any,
54
+ }
55
+
56
+ interface RouteTransition {
57
+ /**
58
+ * Setting or Array of Settings before new view enters into the router view
59
+ */
60
+ before: Before | Before[],
61
+ /**
62
+ * Transition or Array of Transitions for new view to enters into the router view
63
+ */
64
+ in: Transition | Transition[],
65
+ /**
66
+ * Transition or Array of Transitions for old view to leave the router view
67
+ */
68
+ out: Transition | Transition[],
69
+ }
70
+
71
+ type RouteTransitionFunction = (previousRoute: Route, currentRoute: Route) => RequireAtLeastOne<RouteTransition>
72
+
73
+ function Application(
74
+ config: Application.ApplicationConfig
75
+ ) : Application.ApplicationInstance
76
+
77
+ type RequireAtLeastOne<T> = {
78
+ [K in keyof T]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<keyof T, K>>>
79
+ }[keyof T]
80
+
81
+ type Route = {
23
82
  /**
24
83
  * URI path for the route
25
84
  */
@@ -27,7 +86,15 @@ declare namespace Application {
27
86
  /**
28
87
  * Component to load when activating the route
29
88
  */
30
- component: typeof ComponentInstance
89
+ component: typeof ComponentInstance,
90
+ /**
91
+ * Transition configuration for the route
92
+ */
93
+ transition?: RequireAtLeastOne<RouteTransition> | RouteTransitionFunction,
94
+ /**
95
+ * Extra route options
96
+ */
97
+ options?: object // todo: specify which options are available
31
98
  }
32
99
 
33
100
  export interface ApplicationInstance extends ComponentInstance {}
@@ -46,7 +113,7 @@ declare namespace Application {
46
113
  * ]
47
114
  * ```
48
115
  */
49
- routes: Route[]
116
+ routes?: Route[]
50
117
  }
51
118
  }
52
119
 
@@ -19,6 +19,8 @@ import Component from './component.js'
19
19
  import Focus from './focus.js'
20
20
  import Settings from './settings.js'
21
21
 
22
+ import symbols from './lib/symbols.js'
23
+
22
24
  const Application = (config) => {
23
25
  const defaultKeyMap = {
24
26
  ArrowLeft: 'left',
@@ -43,11 +45,11 @@ const Application = (config) => {
43
45
 
44
46
  let handler
45
47
 
46
- config.hooks.___destroy = function () {
48
+ config.hooks[symbols.destroy] = function () {
47
49
  document.removeEventListener('keydown', handler)
48
50
  }
49
51
 
50
- config.hooks.___init = function () {
52
+ config.hooks[symbols.init] = function () {
51
53
  const keyMap = { ...defaultKeyMap, ...Settings.get('keymap', {}) }
52
54
 
53
55
  handler = (e) => {
@@ -57,7 +59,8 @@ const Application = (config) => {
57
59
 
58
60
  document.addEventListener('keydown', handler)
59
61
 
60
- Focus.set(this)
62
+ // next tick
63
+ setTimeout(() => Focus.set(this))
61
64
  }
62
65
 
63
66
  const App = Component('App', config)
package/src/component.js CHANGED
@@ -21,7 +21,7 @@ import codegenerator from './lib/codegenerator/generator.js'
21
21
  import element from './element.js'
22
22
 
23
23
  import { createHumanReadableId, createInternalId } from './lib/componentId.js'
24
- import { registerHooks, emit } from './lib/hooks.js'
24
+ import { registerHooks, emit, privateEmit } from './lib/hooks.js'
25
25
 
26
26
  import { reactive } from './lib/reactivity/reactive.js'
27
27
  import setupBase from './lib/setup/base.js'
@@ -35,6 +35,8 @@ import setupWatch from './lib/setup/watch.js'
35
35
  import { effect } from './lib/reactivity/effect.js'
36
36
  import { Log } from './lib/log.js'
37
37
 
38
+ import symbols from './lib/symbols.js'
39
+
38
40
  const stage = {
39
41
  element,
40
42
  }
@@ -101,7 +103,7 @@ const Component = (name = required('name'), config = required('config')) => {
101
103
  )
102
104
  this.previous = this.current
103
105
  // emit 'private' hook
104
- emit(`___${v}`, name, scope)
106
+ privateEmit(v, name, scope)
105
107
  // emit 'public' hook
106
108
  emit(v, name, scope)
107
109
  this.current = v
@@ -118,6 +120,7 @@ const Component = (name = required('name'), config = required('config')) => {
118
120
  }
119
121
 
120
122
  this.parent = parentComponent
123
+ this.wrapper = parentEl
121
124
 
122
125
  Object.defineProperties(this, {
123
126
  type: {
@@ -132,13 +135,13 @@ const Component = (name = required('name'), config = required('config')) => {
132
135
  enumerable: true,
133
136
  configurable: false,
134
137
  },
135
- ___id: {
138
+ [symbols.id]: {
136
139
  value: createInternalId(),
137
140
  writable: false,
138
141
  enumerable: false,
139
142
  configurable: false,
140
143
  },
141
- ___props: {
144
+ [symbols.props]: {
142
145
  value: reactive(opts.props || {}),
143
146
  writable: false,
144
147
  enumerable: false,
@@ -146,10 +149,15 @@ const Component = (name = required('name'), config = required('config')) => {
146
149
  },
147
150
  })
148
151
 
149
- Object.defineProperty(this, '___state', {
150
- value: reactive(
151
- (config.state && typeof config.state === 'function' && config.state.apply(this)) || {}
152
- ),
152
+ Object.defineProperty(this, symbols.originalState, {
153
+ value: (config.state && typeof config.state === 'function' && config.state.apply(this)) || {},
154
+ writable: false,
155
+ enumerable: false,
156
+ configurable: false,
157
+ })
158
+
159
+ Object.defineProperty(this, symbols.state, {
160
+ value: reactive(this[symbols.originalState]),
153
161
  writable: false,
154
162
  enumerable: false,
155
163
  configurable: false,
@@ -157,15 +165,15 @@ const Component = (name = required('name'), config = required('config')) => {
157
165
 
158
166
  this.lifecycle.state = 'init'
159
167
 
160
- Object.defineProperty(this, '___children', {
168
+ Object.defineProperty(this, symbols.children, {
161
169
  value: code.render.apply(stage, [parentEl, this, code.context]),
162
170
  writable: false,
163
171
  enumerable: false,
164
172
  configurable: false,
165
173
  })
166
174
 
167
- Object.defineProperty(this, '___slots', {
168
- value: this.___children.filter((child) => child.___isSlot),
175
+ Object.defineProperty(this, symbols.slots, {
176
+ value: this[symbols.children].filter((child) => child[symbols.isSlot]),
169
177
  writable: false,
170
178
  enumerable: false,
171
179
  configurable: false,
@@ -173,16 +181,16 @@ const Component = (name = required('name'), config = required('config')) => {
173
181
 
174
182
  code.effects.forEach((eff) => {
175
183
  effect(() => {
176
- eff.apply(stage, [this, this.___children, code.context])
184
+ eff.apply(stage, [this, this[symbols.children], code.context])
177
185
  })
178
186
  })
179
187
 
180
- if (this.___watchers) {
181
- Object.keys(this.___watchers).forEach((watchKey) => {
188
+ if (this[symbols.watchers]) {
189
+ Object.keys(this[symbols.watchers]).forEach((watchKey) => {
182
190
  let old = this[watchKey]
183
- effect(() => {
184
- if (old !== this[watchKey]) {
185
- this.___watchers[watchKey].apply(this, [this[watchKey], old])
191
+ effect((force = false) => {
192
+ if (old !== this[watchKey] || force === true) {
193
+ this[symbols.watchers][watchKey].apply(this, [this[watchKey], old])
186
194
  old = this[watchKey]
187
195
  }
188
196
  })
@@ -16,14 +16,14 @@
16
16
  */
17
17
 
18
18
  import Component from '../component.js'
19
- import Router from '../router.js'
19
+ import Router from '../router/router.js'
20
20
 
21
21
  let handler
22
22
 
23
23
  export default () =>
24
24
  Component('RouterView', {
25
25
  template: `
26
- <Element></Element>
26
+ <Element w="100%" height="100%"></Element>
27
27
  `,
28
28
  hooks: {
29
29
  ready() {
@@ -17,6 +17,8 @@
17
17
 
18
18
  import Component from '../component.js'
19
19
 
20
+ import symbols from '../lib/symbols.js'
21
+
20
22
  export default () =>
21
23
  Component('Sprite', {
22
24
  template: `
@@ -36,7 +38,7 @@ export default () =>
36
38
  : this.map[this.frame]
37
39
 
38
40
  if (this.spriteTexture && options) {
39
- return this.___renderer.createTexture('SubTexture', {
41
+ return this[symbols.renderer].createTexture('SubTexture', {
40
42
  texture: this.spriteTexture,
41
43
  x: options.x,
42
44
  y: options.y,
@@ -48,7 +50,7 @@ export default () =>
48
50
  },
49
51
  hooks: {
50
52
  ready() {
51
- this.spriteTexture = this.___renderer.createTexture('ImageTexture', {
53
+ this.spriteTexture = this[symbols.renderer].createTexture('ImageTexture', {
52
54
  src: `${window.location.protocol}//${window.location.host}/${this.image}`,
53
55
  })
54
56
  },
package/src/element.js CHANGED
@@ -19,6 +19,7 @@ import { renderer } from './launch.js'
19
19
  import colors from './lib/colors/colors.js'
20
20
 
21
21
  import { Log } from './lib/log.js'
22
+ import symbols from './lib/symbols.js'
22
23
 
23
24
  const isTransition = (value) => {
24
25
  return typeof value === 'object' && 'transition' in value
@@ -174,8 +175,8 @@ const Element = {
174
175
  }
175
176
  this.initData = data
176
177
 
177
- if (props.___isSlot) {
178
- this.___isSlot = true
178
+ if (props[symbols.isSlot]) {
179
+ this[symbols.isSlot] = true
179
180
  }
180
181
 
181
182
  transformations.remap(props)
@@ -192,15 +193,13 @@ const Element = {
192
193
  this.node = props.__textnode ? renderer.createTextNode(props) : renderer.createNode(props)
193
194
 
194
195
  if (props['@loaded']) {
195
- const event = props.__textnode ? 'textLoaded' : 'txLoaded'
196
- this.node.on(event, (el, { width: w, height: h }) => {
197
- props['@loaded']({ w, h }, this)
196
+ this.node.on('loaded', (el, { type, dimensions }) => {
197
+ props['@loaded']({ w: dimensions.width, h: dimensions.height, type }, this)
198
198
  })
199
199
  }
200
200
 
201
201
  if (props['@error']) {
202
- const event = props.__textnode ? 'textFailed' : 'txFailed'
203
- this.node.on(event, (el, error) => {
202
+ this.node.on('failed', (el, error) => {
204
203
  props['@error'](error, this)
205
204
  })
206
205
  }
@@ -253,7 +252,7 @@ const Element = {
253
252
  // works for now
254
253
  prop = Object.keys(props).pop()
255
254
 
256
- if (typeof value === 'string' && props[prop].endsWith('%')) {
255
+ if (typeof props[prop] === 'string' && props[prop].endsWith('%')) {
257
256
  transformations.percentage.call(this, props, prop)
258
257
  }
259
258
 
@@ -263,8 +262,11 @@ const Element = {
263
262
  easing:
264
263
  typeof value === 'object' ? ('function' in value ? value.function : 'ease') : 'ease',
265
264
  })
266
-
267
- value.delay ? setTimeout(() => f.start(), value.delay) : f.start()
265
+ return new Promise((resolve) => {
266
+ value.delay
267
+ ? setTimeout(() => f.start().waitUntilStopped().then(resolve), value.delay)
268
+ : f.start().waitUntilStopped().then(resolve)
269
+ })
268
270
  }
269
271
  },
270
272
  }
package/src/focus.js CHANGED
@@ -15,6 +15,9 @@
15
15
  * SPDX-License-Identifier: Apache-2.0
16
16
  */
17
17
 
18
+ import symbols from './lib/symbols.js'
19
+ import { navigating } from './router/router.js'
20
+
18
21
  let focusedComponent = null
19
22
 
20
23
  export default {
@@ -34,6 +37,7 @@ export default {
34
37
  }
35
38
  },
36
39
  input(key, event) {
40
+ if (navigating === true) return
37
41
  const focusChain = walkChain([focusedComponent], key)
38
42
  const componentWithInputEvent = focusChain.shift()
39
43
 
@@ -42,10 +46,10 @@ export default {
42
46
  focusChain.reverse().forEach((component) => component.unfocus())
43
47
  componentWithInputEvent.focus()
44
48
  }
45
- if (componentWithInputEvent.___inputEvents[key]) {
46
- componentWithInputEvent.___inputEvents[key].call(componentWithInputEvent, event)
47
- } else if (componentWithInputEvent.___inputEvents.any) {
48
- componentWithInputEvent.___inputEvents.any.call(componentWithInputEvent, event)
49
+ if (componentWithInputEvent[symbols.inputEvents][key]) {
50
+ componentWithInputEvent[symbols.inputEvents][key].call(componentWithInputEvent, event)
51
+ } else if (componentWithInputEvent[symbols.inputEvents].any) {
52
+ componentWithInputEvent[symbols.inputEvents].any.call(componentWithInputEvent, event)
49
53
  }
50
54
  }
51
55
  },
@@ -53,9 +57,9 @@ export default {
53
57
 
54
58
  const walkChain = (components, key) => {
55
59
  if (
56
- components[0].___inputEvents &&
57
- (typeof components[0].___inputEvents[key] === 'function' ||
58
- typeof components[0].___inputEvents.any === 'function')
60
+ components[0][symbols.inputEvents] &&
61
+ (typeof components[0][symbols.inputEvents][key] === 'function' ||
62
+ typeof components[0][symbols.inputEvents].any === 'function')
59
63
  ) {
60
64
  return components
61
65
  } else if (components[0].parent) {
@@ -56,7 +56,7 @@ const generateElementCode = function (
56
56
 
57
57
  renderCode.push(`
58
58
  if(!${elm}) {
59
- ${elm} = this.element({componentId: component.___id, parent: parent || 'root'})
59
+ ${elm} = this.element({componentId: component[Symbol.for('id')], parent: parent || 'root'})
60
60
  }
61
61
  const elementConfig${counter} = {}
62
62
  `)
@@ -67,14 +67,14 @@ const generateElementCode = function (
67
67
  Object.keys(templateObject).forEach((key) => {
68
68
  if (key === 'type') {
69
69
  if (templateObject[key] === 'Slot') {
70
- renderCode.push(`elementConfig${counter}['___isSlot'] = true`)
70
+ renderCode.push(`elementConfig${counter}[Symbol.for('isSlot')] = true`)
71
71
  }
72
72
  return
73
73
  }
74
74
 
75
75
  if (key === 'slot') {
76
76
  renderCode.push(`
77
- elementConfig${counter}['parent'] = component.___slots.filter(slot => slot.ref === '${templateObject.slot}').shift() || component.___slots[0] || parent
77
+ elementConfig${counter}['parent'] = component[Symbol.for('slots')].filter(slot => slot.ref === '${templateObject.slot}').shift() || component[Symbol.for('slots')][0] || parent
78
78
  `)
79
79
  }
80
80
 
@@ -120,7 +120,7 @@ const generateComponentCode = function (
120
120
  renderCode.push(`
121
121
  const cmp${counter} =
122
122
  (context.components && context.components['${templateObject.type}']) ||
123
- component.___components['${templateObject.type}']
123
+ component[Symbol.for('components')]['${templateObject.type}']
124
124
  `)
125
125
 
126
126
  if ('key' in templateObject) {
@@ -152,7 +152,7 @@ const generateComponentCode = function (
152
152
  if (key === 'type') return
153
153
  if (isReactiveKey(key)) {
154
154
  this.effectsCode.push(`
155
- ${elm}.___props['${key.substring(1)}'] = ${interpolate(
155
+ ${elm}[Symbol.for('props')]['${key.substring(1)}'] = ${interpolate(
156
156
  templateObject[key],
157
157
  options.component
158
158
  )}`)
@@ -171,12 +171,12 @@ const generateComponentCode = function (
171
171
 
172
172
  renderCode.push(`
173
173
  if(!${elm}) {
174
- ${elm} = (context.components && context.components['${templateObject.type}'] || component.___components['${templateObject.type}'] || (() => { console.log('component ${templateObject.type} not found')})).call(null, {props: props${counter}}, ${parent}, component)
175
- if (${elm}.___slots[0]) {
176
- parent = ${elm}.___slots[0]
174
+ ${elm} = (context.components && context.components['${templateObject.type}'] || component[Symbol.for('components')]['${templateObject.type}'] || (() => { console.log('component ${templateObject.type} not found')})).call(null, {props: props${counter}}, ${parent}, component)
175
+ if (${elm}[Symbol.for('slots')][0]) {
176
+ parent = ${elm}[Symbol.for('slots')][0]
177
177
  component = ${elm}
178
178
  } else {
179
- parent = ${elm}.___children[0]
179
+ parent = ${elm}[Symbol.for('children')][0]
180
180
  }
181
181
  }
182
182
  `)