@lightningjs/blits 0.5.6 → 0.5.8

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,19 @@
1
1
  # Changelog
2
2
 
3
+ # v0.5.8
4
+
5
+ _15 dec 2023_
6
+
7
+ - Internal refactor of the `Element`
8
+
9
+ # v0.5.7
10
+
11
+ _13 dec 2023_
12
+
13
+ - Added history and backhandling to the router
14
+ - Linked effects directly to the available shaders exposed by the L3 renderer (starting 0.6.0)
15
+ - Added setting `reactivityMode` to control whether `Proxy` (default) or `defineProperty` is used to trigger reactive side effects
16
+
3
17
  # v0.5.6
4
18
 
5
19
  _11 dec 2023_
@@ -31,6 +31,6 @@
31
31
  "vite": "^4.0.4"
32
32
  },
33
33
  "dependencies": {
34
- "@lightningjs/blits": "^0.5.6"
34
+ "@lightningjs/blits": "^0.5.8"
35
35
  }
36
36
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightningjs/blits",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
4
4
  "description": "Blits: The Lightning 3 App Development Framework",
5
5
  "bin": "bin/index.cjs",
6
6
  "exports": {
package/src/component.js CHANGED
@@ -35,6 +35,7 @@ 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 Settings from './settings.js'
38
39
  import symbols from './lib/symbols.js'
39
40
 
40
41
  const stage = {
@@ -142,7 +143,7 @@ const Component = (name = required('name'), config = required('config')) => {
142
143
  configurable: false,
143
144
  },
144
145
  [symbols.props]: {
145
- value: reactive(opts.props || {}),
146
+ value: reactive(opts.props || {}, Settings.get('reactivityMode')),
146
147
  writable: false,
147
148
  enumerable: false,
148
149
  configurable: false,
@@ -157,7 +158,7 @@ const Component = (name = required('name'), config = required('config')) => {
157
158
  })
158
159
 
159
160
  Object.defineProperty(this, symbols.state, {
160
- value: reactive(this[symbols.originalState]),
161
+ value: reactive(this[symbols.originalState], Settings.get('reactivityMode')),
161
162
  writable: false,
162
163
  enumerable: false,
163
164
  configurable: false,
@@ -43,4 +43,9 @@ export default () =>
43
43
  this.activeView && this.activeView.focus()
44
44
  },
45
45
  },
46
+ input: {
47
+ back(e) {
48
+ if (!Router.back()) this.parent.focus(e)
49
+ },
50
+ },
46
51
  })
package/src/element.js CHANGED
@@ -33,172 +33,212 @@ const parseToObject = (str) => {
33
33
  return JSON.parse(str.replace(/'/g, '"').replace(/([\w-_]+)\s*:/g, '"$1":'))
34
34
  }
35
35
 
36
- const transformations = {
37
- unpackTransition(obj) {
38
- if (typeof obj === 'object' && obj.constructor.name === 'Object') {
39
- if ('value' in obj) {
40
- return obj.value
41
- } else if ('transition' in obj) {
42
- return this.unpackTransition(obj.transition)
43
- } else {
44
- return obj
45
- }
46
- } else {
47
- return obj
36
+ const parsePercentage = function (v, base) {
37
+ if (typeof v === 'string' && v.endsWith('%')) {
38
+ return this.element.node._parent[base] * (parseFloat(v) / 100)
39
+ }
40
+ return v
41
+ }
42
+
43
+ const unpackTransition = (obj) => {
44
+ if (typeof obj === 'object' && obj.constructor === Object) {
45
+ if ('value' in obj) {
46
+ return obj.value
47
+ } else if ('transition' in obj) {
48
+ return unpackTransition(obj.transition)
48
49
  }
50
+ }
51
+ return obj
52
+ }
53
+
54
+ const Props = {
55
+ set parent(v) {
56
+ this._props.parent = v === 'root' ? renderer.root : v.node
57
+ this._set.add('parent')
49
58
  },
50
- remap(props) {
51
- 'w' in props && (props.width = props.w)
52
- delete props.w
53
- 'h' in props && (props.height = props.h)
54
- delete props.h
55
- 'z' in props && (props.zIndex = props.z)
56
- delete props.z
57
- 'wordWrap' in props && (props.width = props.wordWrap)
58
- delete props.wordWrap
59
- },
60
- parent(props) {
61
- if (props.parent === 'root') {
62
- props.parent = renderer.root
63
- } else {
64
- props.parent = props.parent.node
65
- }
59
+ set rotation(v) {
60
+ this._props.rotation = v * (Math.PI / 180)
61
+ this._set.add('rotation')
62
+ },
63
+ set w(v) {
64
+ this._props.width = parsePercentage.call(this, v, 'width')
65
+ this._set.add('width')
66
+ },
67
+ set width(v) {
68
+ this._props.width = parsePercentage.call(this, v, 'width')
69
+ this._set.add('width')
66
70
  },
67
- color(props) {
68
- if (
69
- typeof props.color === 'object' ||
70
- (isObjectString(props.color) && (props.color = parseToObject(props.color)))
71
- ) {
71
+ set h(v) {
72
+ this._props.height = parsePercentage.call(this, v, 'height')
73
+ this._set.add('height')
74
+ },
75
+ set height(v) {
76
+ this._props.height = parsePercentage.call(this, v, 'height')
77
+ this._set.add('height')
78
+ },
79
+ set x(v) {
80
+ this._props.x = parsePercentage.call(this, v, 'width')
81
+ this._set.add('x')
82
+ },
83
+ set y(v) {
84
+ this._props.y = parsePercentage.call(this, v, 'height')
85
+ this._set.add('y')
86
+ },
87
+ set z(v) {
88
+ this._props.zIndex = v
89
+ this._set.add('zIndex')
90
+ },
91
+ set zIndex(v) {
92
+ this._props.zIndex = v
93
+ this._set.add('zIndex')
94
+ },
95
+ set color(v) {
96
+ if (typeof v === 'object' || (isObjectString(v) && (v = parseToObject(v)))) {
72
97
  const map = {
73
98
  top: 'colorTop',
74
99
  bottom: 'colorBottom',
75
100
  left: 'colorLeft',
76
101
  right: 'colorRight',
77
102
  }
78
- Object.entries(props.color).forEach((color) => {
79
- props[map[color[0]]] = colors.normalize(color[1])
103
+ this._props.color = 0
104
+ Object.entries(v).forEach((color) => {
105
+ this._props[map[color[0]]] = colors.normalize(color[1])
106
+ this._set.add(map[color[0]])
80
107
  })
81
- delete props.color
82
108
  } else {
83
- props.color = colors.normalize(props.color)
109
+ this._props.color = colors.normalize(v)
84
110
  }
111
+ this._set.add('color')
85
112
  },
86
- percentage(props, prop) {
87
- const map = {
88
- w: 'width',
89
- width: 'width',
90
- x: 'width',
91
- h: 'height',
92
- height: 'height',
93
- y: 'height',
94
- }
95
-
96
- const base = map[prop]
97
- if (base) {
98
- props[prop] = this.node._parent[base] * (parseFloat(props[prop]) / 100)
113
+ set src(v) {
114
+ this._props.src = v
115
+ if (!this._set.has('color')) {
116
+ this._props.color = 0xffffffff
99
117
  }
118
+ this._set.add('src')
100
119
  },
101
- mount(props) {
102
- if (
103
- typeof props.mount === 'object' ||
104
- (isObjectString(props.mount) && (props.mount = parseToObject(props.mount)))
105
- ) {
106
- const map = {
107
- x: 'mountX',
108
- y: 'mountY',
120
+ set texture(v) {
121
+ this._props.texture = v
122
+ this._set.add('texture')
123
+ },
124
+ set mount(v) {
125
+ if (typeof v === 'object' || (isObjectString(v) && (v = parseToObject(v)))) {
126
+ if (v.x) {
127
+ this._props.mountX = v.x
128
+ this._set.add('mountX')
109
129
  }
110
- Object.entries(props.mount).forEach((mount) => {
111
- props[map[mount[0]]] = mount[1]
112
- })
113
- delete props.mount
130
+ if (v.y) {
131
+ this._props.mountY = v.y
132
+ this._set.add('mountY')
133
+ }
134
+ } else {
135
+ this._props.mountX = this._props.mountY = v
136
+ this._set.add('mountX')
137
+ this._set.add('mountY')
114
138
  }
115
139
  },
116
- pivot(props) {
117
- if (
118
- typeof props.pivot === 'object' ||
119
- (isObjectString(props.pivot) && (props.pivot = parseToObject(props.pivot)))
120
- ) {
121
- const map = {
122
- x: 'pivotX',
123
- y: 'pivotY',
140
+ set pivot(v) {
141
+ if (typeof v === 'object' || (isObjectString(v) && (v = parseToObject(v)))) {
142
+ if (v.x) {
143
+ this._set.add('pivotX')
144
+ this._props.pivotX = v.x
124
145
  }
125
- Object.entries(props.pivot).forEach((pivot) => {
126
- props[map[pivot[0]]] = pivot[1]
127
- })
128
- delete props.pivot
146
+ if (v.y) {
147
+ this._set.add('pivotY')
148
+ this._props.pivotY = v.y
149
+ }
150
+ } else {
151
+ this._props.pivotX = this._props.pivotY = v
152
+ this._set.add('pivotX')
153
+ this._set.add('pivotY')
129
154
  }
130
155
  },
131
- show(props) {
132
- props.alpha = props.show ? 1 : 0
133
- delete props.show
156
+ set scale(v) {
157
+ if (typeof v === 'object' || (isObjectString(v) && (v = parseToObject(v)))) {
158
+ if (v.x) this._props.scaleX = v.x
159
+ if (v.y) this._props.scaleY = v.y
160
+ } else {
161
+ this._props.scale = v
162
+ }
163
+ this._set.add('scale')
134
164
  },
135
- rotation(props) {
136
- props.rotation = props.rotation * (Math.PI / 180)
165
+ set show(v) {
166
+ this.alpha = v ? 1 : 0
137
167
  },
138
- text(props) {
139
- props.text = props.text !== undefined ? props.text.toString() : ''
168
+ set alpha(v) {
169
+ this._props.alpha = v
170
+ this._set.add('alpha')
140
171
  },
141
- textureColor(props) {
142
- if (!('color' in props)) {
143
- props.color = 'src' in props || 'texture' in props ? '0xffffffff' : '0x00000000'
144
- }
172
+ set text(v) {
173
+ this._props.text = v !== undefined ? v.toString() : ''
145
174
  },
146
- effects(props) {
147
- props.shader = renderer.createShader('DynamicShader', {
148
- effects: props.effects.map((eff) => {
175
+ set effects(v) {
176
+ this._props.shader = renderer.createShader('DynamicShader', {
177
+ effects: v.map((eff) => {
149
178
  if (eff.props && eff.props.color) {
150
179
  eff.props.color = colors.normalize(eff.props.color)
151
180
  }
152
181
  return eff
153
182
  }),
154
183
  })
155
-
156
- delete props.effects
184
+ this._set.add('effects')
157
185
  },
158
- src(props, setProperties) {
159
- if (setProperties.indexOf('color') === -1) {
160
- props.color = 0xffffffff
161
- }
186
+ set fontFamily(v) {
187
+ this._props.fontFamily = v
162
188
  },
163
- texture(props, setProperties) {
164
- this.src(props, setProperties)
189
+ set fontSize(v) {
190
+ this._props.fontSize = v
165
191
  },
166
- maxLines(props, setProperties, element) {
167
- if (props.maxLines) {
168
- props.height = props.maxLines * element.node.fontSize
169
- }
170
- delete props.maxLines
192
+ set wordWrap(v) {
193
+ this._props.width = v
194
+ },
195
+ set contain(v) {
196
+ this._props.contain = v
197
+ },
198
+ set maxLines(v) {
199
+ this.height = v * this.element.node.fontSize
200
+ },
201
+ set letterSpacing(v) {
202
+ this._props.letterSpacing = v
203
+ },
204
+ set textAlign(v) {
205
+ this._props.textAlign = v
171
206
  },
172
207
  }
173
208
 
174
209
  const Element = {
175
- defaults: {
176
- rotation: 0,
177
- },
210
+ defaults: {},
178
211
  populate(data) {
179
212
  const props = {
180
213
  ...this.defaults,
181
214
  ...this.config,
182
215
  ...data,
183
216
  }
217
+
184
218
  this.initData = data
185
219
 
186
220
  if (props[symbols.isSlot]) {
187
221
  this[symbols.isSlot] = true
188
222
  }
189
223
 
190
- transformations.remap(props)
191
- Object.keys(props).forEach((prop) => {
192
- if (transformations[prop]) {
193
- props[prop] = transformations.unpackTransition(props[prop])
194
- transformations[prop](props, this.setProperties)
224
+ this.props.element = this
225
+
226
+ for (const [prop, value] of Object.entries(props)) {
227
+ const descriptor = Object.getOwnPropertyDescriptor(Props, prop)
228
+ if (descriptor && descriptor.set) {
229
+ this.props[prop] = unpackTransition(value)
195
230
  }
196
- this.setProperties.push(prop)
197
- })
231
+ }
198
232
 
199
- transformations.textureColor(props, this.setProperties)
233
+ // correct for default white nodes
234
+ if (!this.props._set.has('color')) {
235
+ this.props._props.color =
236
+ this.props._set.has('src') || this.props._set.has('texture') ? 0xffffffff : 0
237
+ }
200
238
 
201
- this.node = props.__textnode ? renderer.createTextNode(props) : renderer.createNode(props)
239
+ this.node = props.__textnode
240
+ ? renderer.createTextNode(this.props._props)
241
+ : renderer.createNode(this.props._props)
202
242
 
203
243
  if (props['@loaded']) {
204
244
  this.node.on('loaded', (el, { type, dimensions }) => {
@@ -213,43 +253,24 @@ const Element = {
213
253
  }
214
254
  },
215
255
  set(prop, value) {
216
- if (isTransition(value) && this.setProperties.indexOf(prop) > -1) {
217
- this.animate(prop, value.transition)
218
- } else {
219
- const props = {}
220
- if (prop !== 'texture') {
221
- props[prop] = transformations.unpackTransition(value)
222
- } else {
223
- props[prop] = value
224
- }
256
+ const propsSet = new Set(this.props._set)
225
257
 
226
- transformations.remap(props)
227
- if (transformations[prop]) {
228
- transformations[prop](props, this.setProperties, this)
229
- }
258
+ this.props._props = {}
259
+ this.props[prop] = unpackTransition(value)
230
260
 
231
- Object.keys(props).forEach((prop) => {
232
- if (typeof props[prop] === 'string' && props[prop].endsWith('%')) {
233
- transformations.percentage.call(this, props, prop)
234
- }
235
- this.node[prop] = props[prop]
236
- })
261
+ for (const [p, v] of Object.entries(this.props._props)) {
262
+ if (isTransition(value) && propsSet.has(p)) {
263
+ return this.animate(p, v, value.transition)
264
+ } else {
265
+ this.node[p] = v
266
+ }
237
267
  }
238
- this.setProperties.indexOf(prop) === -1 && this.setProperties.push(prop)
239
268
  },
240
- delete() {
241
- Log.debug('Deleting Node', this.nodeId, this.node)
242
- this.node.parent = null
243
- },
244
- get nodeId() {
245
- return this.node && this.node.id
246
- },
247
- get ref() {
248
- return this.initData.ref || null
249
- },
250
- animate(prop, value) {
269
+ animate(prop, value, transition) {
270
+ if (this.node[prop] === value) return Promise.resolve()
271
+ // check if a transition is already schedule or running on the same prop
251
272
  if (this.scheduledTransitions[prop]) {
252
- clearTimeout(this.scheduledTransitions[prop].timeout)
273
+ // clearTimeout(this.scheduledTransitions[prop].timeout)
253
274
  if (this.scheduledTransitions[prop].f.state === 'running') {
254
275
  this.scheduledTransitions[prop].f.pause()
255
276
  // fastforward to final value
@@ -258,55 +279,73 @@ const Element = {
258
279
  }
259
280
 
260
281
  const props = {}
261
- props[prop] = transformations.unpackTransition(value)
262
-
263
- transformations.remap(props)
282
+ props[prop] = value
264
283
 
265
- if (transformations[prop]) {
266
- transformations[prop](props)
267
- }
284
+ // construct animate function
285
+ const f = this.node.animate(props, {
286
+ duration:
287
+ typeof transition === 'object'
288
+ ? 'duration' in transition
289
+ ? transition.duration
290
+ : 300
291
+ : 300,
292
+ easing:
293
+ typeof transition === 'object'
294
+ ? 'function' in transition
295
+ ? transition.function
296
+ : 'ease'
297
+ : 'ease',
298
+ })
268
299
 
269
- // works for now
270
- prop = Object.keys(props).pop()
300
+ // schedule transition
301
+ return new Promise((resolve) => {
302
+ this.scheduledTransitions[prop] = {
303
+ v: props[prop],
304
+ f,
305
+ timeout: setTimeout(() => {
306
+ // fire transition start callback if specified
307
+ transition.start &&
308
+ typeof transition.start === 'function' &&
309
+ transition.start.call(this.component, this, prop, props[prop])
271
310
 
272
- if (typeof props[prop] === 'string' && props[prop].endsWith('%')) {
273
- transformations.percentage.call(this, props, prop)
274
- }
275
-
276
- if (this.node[prop] !== props[prop]) {
277
- const f = this.node.animate(props, {
278
- duration: typeof value === 'object' ? ('duration' in value ? value.duration : 300) : 300,
279
- easing:
280
- typeof value === 'object' ? ('function' in value ? value.function : 'ease') : 'ease',
281
- })
282
- return new Promise((resolve) => {
283
- this.scheduledTransitions[prop] = {
284
- v: props[prop],
285
- f,
286
- timeout: setTimeout(() => {
287
- value.start &&
288
- typeof value.start === 'function' &&
289
- value.start.call(this.component, this, prop, props[prop])
311
+ // start the animation
312
+ try {
290
313
  f.start()
291
314
  .waitUntilStopped()
292
315
  .then(() => delete this.scheduledTransitions[prop])
293
316
  .then(() => {
294
- value.end &&
295
- typeof value.end === 'function' &&
296
- value.end.call(this.component, this, prop, props[prop])
317
+ // fire transition end callback if specified
318
+ transition.end &&
319
+ typeof transition.end === 'function' &&
320
+ transition.end.call(this.component, this, prop, props[prop])
297
321
  })
298
322
  .then(resolve)
299
- }, value.delay || 0),
300
- }
301
- })
302
- }
323
+ } catch (e) {
324
+ Log.error(e)
325
+ }
326
+ }, transition.delay || 0),
327
+ }
328
+ })
329
+ },
330
+ delete() {
331
+ Log.debug('Deleting Node', this.nodeId)
332
+ Object.values(this.scheduledTransitions).forEach((scheduledTransition) => {
333
+ clearTimeout(scheduledTransition.timeout)
334
+ })
335
+ this.node.parent = null
336
+ },
337
+ get nodeId() {
338
+ return this.node && this.node.id
339
+ },
340
+ get ref() {
341
+ return this.initData.ref || null
303
342
  },
304
343
  }
305
344
 
306
345
  export default (config, component) =>
307
346
  Object.assign(Object.create(Element), {
347
+ props: Object.assign(Object.create(Props), { _props: {}, _set: new Set() }),
308
348
  node: null,
309
- setProperties: [],
310
349
  scheduledTransitions: {},
311
350
  initData: {},
312
351
  config,
package/src/launch.d.ts CHANGED
@@ -55,6 +55,7 @@ type Font = WebFont | SdfFont
55
55
 
56
56
  export type DebugLevel = 0 | 1 | 2
57
57
  export type LogTypes = 'info' | 'warn' | 'error' | 'debug'
58
+ export type ReactivityModes = 'Proxy' | 'defineProperty'
58
59
 
59
60
  /**
60
61
  * Settings
@@ -90,7 +91,11 @@ export interface Settings {
90
91
  /**
91
92
  * Custom keymapping
92
93
  */
93
- keymap?: object
94
+ keymap?: object,
95
+ /**
96
+ * Mode of reactivity (`Proxy` or `defineProperty`)
97
+ */
98
+ reactivityMode?: ReactivityModes
94
99
  }
95
100
 
96
101
  /**
@@ -27,8 +27,12 @@ const arrayMethods = [
27
27
  'shift',
28
28
  'splice',
29
29
  'unshift',
30
+ 'sort',
31
+ 'reverse',
30
32
  ]
31
33
 
34
+ const arrayPatchMethods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse']
35
+
32
36
  const proxyMap = new WeakMap()
33
37
 
34
38
  const reactiveProxy = (target) => {
@@ -79,17 +83,22 @@ const reactiveDefineProperty = (target) => {
79
83
  Object.keys(target).forEach((key) => {
80
84
  let internalValue = target[key]
81
85
 
82
- if (
83
- target[key] !== null &&
84
- typeof target[key] === 'object' &&
85
- Object.getPrototypeOf(target[key]) === Object.prototype
86
- ) {
87
- return reactiveDefineProperty(target[key])
86
+ if (target[key] !== null && typeof target[key] === 'object') {
87
+ if (Object.getPrototypeOf(target[key]) === Object.prototype) {
88
+ return reactiveDefineProperty(target[key])
89
+ } else if (Array.isArray(target[key])) {
90
+ for (let i = 0; i < arrayPatchMethods.length - 1; i++) {
91
+ target[key][arrayPatchMethods[i]] = function (v) {
92
+ Array.prototype[arrayPatchMethods[i]].call(this, v)
93
+ trigger(target, key)
94
+ }
95
+ }
96
+ }
88
97
  }
89
98
 
90
99
  Object.defineProperty(target, key, {
91
- enumerable: true, // ?
92
- configurable: true, // ?
100
+ enumerable: true,
101
+ configurable: true,
93
102
  get() {
94
103
  track(target, key)
95
104
  return internalValue
@@ -107,9 +116,8 @@ const reactiveDefineProperty = (target) => {
107
116
  return target
108
117
  }
109
118
 
110
- // maybe an options object?
111
- export const reactive = (target, type = 'proxy') => {
112
- return type === 'proxy' ? reactiveProxy(target) : reactiveDefineProperty(target)
119
+ export const reactive = (target, mode = 'Proxy') => {
120
+ return mode === 'defineProperty' ? reactiveDefineProperty(target) : reactiveProxy(target)
113
121
  }
114
122
 
115
123
  export const memo = (raw) => {
@@ -17,7 +17,7 @@
17
17
 
18
18
  import { renderer } from '../../launch.js'
19
19
  import Focus from '../../focus.js'
20
- import { to, currentRoute, navigating } from '../../router/router.js'
20
+ import { to, currentRoute, navigating, back } from '../../router/router.js'
21
21
  import Image from '../../components/Image.js'
22
22
  import Circle from '../../components/Circle.js'
23
23
  import RouterView from '../../components/RouterView.js'
@@ -29,16 +29,8 @@ import symbols from '../symbols.js'
29
29
 
30
30
  import { trigger } from '../reactivity/effect.js'
31
31
 
32
- const shaders = {
33
- radius: 'radius',
32
+ const shaderAlias = {
34
33
  rounded: 'radius',
35
- border: 'border',
36
- borderTop: 'borderTop',
37
- borderBottom: 'borderBottom',
38
- borderLeft: 'borderLeft',
39
- borderRight: 'borderRight',
40
- grayScale: 'grayscale',
41
- glitch: 'glitch',
42
34
  }
43
35
 
44
36
  export default (component) => {
@@ -101,9 +93,12 @@ export default (component) => {
101
93
  },
102
94
  shader: {
103
95
  value: function (type, args) {
104
- if (type in shaders) {
96
+ const target = shaderAlias[type] || type
97
+ const shaders = renderer.driver.stage.shManager.getRegisteredEffects()
98
+
99
+ if (target in shaders) {
105
100
  return {
106
- type: shaders[type],
101
+ type: target,
107
102
  props: args,
108
103
  }
109
104
  } else {
@@ -117,6 +112,7 @@ export default (component) => {
117
112
  $router: {
118
113
  value: {
119
114
  to,
115
+ back,
120
116
  get currentRoute() {
121
117
  return currentRoute
122
118
  },
@@ -26,6 +26,7 @@ export let currentRoute
26
26
  export let navigating = false
27
27
 
28
28
  const cacheMap = new WeakMap()
29
+ const history = []
29
30
 
30
31
  export const getHash = () => {
31
32
  return (document.location.hash || '/').replace(/^#/, '')
@@ -49,6 +50,7 @@ export const navigate = async function () {
49
50
  const route = matchHash(hash, this.parent[symbols.routes])
50
51
 
51
52
  if (route) {
53
+ if (history[history.length - 1] !== hash) history.push(hash)
52
54
  // apply default transition if none specified
53
55
  if (!('transition' in route)) {
54
56
  route.transition = fadeInFadeOutTransition
@@ -163,7 +165,7 @@ const removeView = async (route, view, transition) => {
163
165
 
164
166
  const setOrAnimate = (node, transition, shouldAnimate = true) => {
165
167
  return shouldAnimate
166
- ? node.animate(transition.prop, transition)
168
+ ? node.animate(transition.prop, transition.value, transition)
167
169
  : node.set(transition.prop, transition.value)
168
170
  }
169
171
 
@@ -171,7 +173,18 @@ export const to = (location) => {
171
173
  window.location.hash = `#${location}`
172
174
  }
173
175
 
176
+ export const back = () => {
177
+ if (history.length > 1) {
178
+ to(history[history.length - 2])
179
+ history.splice(-2, 2)
180
+ return true
181
+ } else {
182
+ return false
183
+ }
184
+ }
185
+
174
186
  export default {
175
187
  navigate,
176
188
  to,
189
+ back,
177
190
  }
package/src/router.js DELETED
@@ -1,72 +0,0 @@
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 { Log } from './lib/log.js'
19
-
20
- import symbols from './lib/symbols.js'
21
-
22
- export const getHash = () => {
23
- return (document.location.hash || '/').replace(/^#/, '')
24
- }
25
-
26
- export const matchHash = (path, routes = []) => {
27
- const route = routes
28
- .filter((r) => {
29
- return r.path === path
30
- })
31
- .pop()
32
- return route
33
- }
34
-
35
- export const navigate = function () {
36
- if (this.parent[symbols.routes]) {
37
- const hash = getHash()
38
- const route = matchHash(hash, this.parent[symbols.routes])
39
- if (route) {
40
- if (this[symbols.currentView]) {
41
- for (let i = 0; i < this[symbols.currentView][symbols.children].length - 1; i++) {
42
- if (
43
- this[symbols.currentView][symbols.children][i] &&
44
- this[symbols.currentView][symbols.children][i].destroy
45
- ) {
46
- this[symbols.currentView][symbols.children][i].destroy()
47
- this[symbols.currentView][symbols.children][i] = null
48
- }
49
- }
50
- this[symbols.currentView].destroy()
51
- this[symbols.currentView] = null
52
- }
53
- this[symbols.children][1] = this[symbols.currentView] = route.component(
54
- this[symbols.props],
55
- this[symbols.children][0],
56
- this
57
- )
58
- this[symbols.currentView].focus()
59
- } else {
60
- Log.error(`Route ${hash} not found`)
61
- }
62
- }
63
- }
64
-
65
- export const to = (location) => {
66
- window.location.hash = `#${location}`
67
- }
68
-
69
- export default {
70
- navigate,
71
- to,
72
- }