lunchboxjs 0.1.4016 → 0.2.1001-beta.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.
@@ -1,31 +1,39 @@
1
- import { ensureRenderer, ensuredScene, ensuredCamera } from '.'
1
+ // import {
2
+ //ensureRenderer,
3
+ // ensuredScene,
4
+ // ensuredCamera,
5
+ // } from '.'
2
6
  import { Lunch } from '..'
3
- import { toRaw, watch, WatchStopHandle } from 'vue'
7
+ import { inject, toRaw, watch, WatchStopHandle } from 'vue'
8
+ import * as Keys from '../keys'
4
9
 
5
- let frameID: number
6
- let watchStopHandle: WatchStopHandle
10
+ // let frameID: number
11
+ // let watchStopHandle: WatchStopHandle
7
12
 
8
- export const beforeRender = [] as Lunch.UpdateCallback[]
9
- export const afterRender = [] as Lunch.UpdateCallback[]
13
+ // export const beforeRender = [] as Lunch.UpdateCallback[]
14
+ // export const afterRender = [] as Lunch.UpdateCallback[]
10
15
 
11
16
  const requestUpdate = (opts: Lunch.UpdateCallbackProperties) => {
12
- cancelUpdate()
13
- frameID = requestAnimationFrame(() =>
14
- update({
15
- app: opts.app,
16
- renderer: ensureRenderer.value?.instance,
17
- scene: ensuredScene.value.instance,
18
- camera: ensuredCamera.value?.instance,
19
- updateSource: opts.updateSource,
20
- })
17
+ if (typeof opts.app.config.globalProperties.lunchbox.frameId === 'number') {
18
+ cancelAnimationFrame(opts.app.config.globalProperties.lunchbox.frameId)
19
+ }
20
+ opts.app.config.globalProperties.lunchbox.frameId = requestAnimationFrame(
21
+ () =>
22
+ update({
23
+ app: opts.app,
24
+ renderer: opts.renderer,
25
+ scene: opts.scene,
26
+ camera: opts.camera,
27
+ updateSource: opts.updateSource,
28
+ })
21
29
  )
22
30
  }
23
31
 
24
32
  export const update: Lunch.UpdateCallback = (opts) => {
25
33
  if (opts.updateSource) {
26
- if (!watchStopHandle) {
34
+ if (!opts.app.config.globalProperties.lunchbox.watchStopHandle) {
27
35
  // request next frame only when state changes
28
- watchStopHandle = watch(
36
+ opts.app.config.globalProperties.lunchbox.watchStopHandle = watch(
29
37
  opts.updateSource,
30
38
  () => {
31
39
  requestUpdate(opts)
@@ -44,10 +52,8 @@ export const update: Lunch.UpdateCallback = (opts) => {
44
52
  const { app, renderer, scene, camera } = opts
45
53
 
46
54
  // BEFORE RENDER
47
- beforeRender.forEach((cb: Lunch.UpdateCallback | undefined) => {
48
- if (cb) {
49
- cb(opts)
50
- }
55
+ app.config.globalProperties.lunchbox.beforeRender.forEach((cb) => {
56
+ cb?.(opts)
51
57
  })
52
58
 
53
59
  // RENDER
@@ -55,56 +61,101 @@ export const update: Lunch.UpdateCallback = (opts) => {
55
61
  if (app.customRender) {
56
62
  app.customRender(opts)
57
63
  } else {
58
- renderer.render(toRaw(scene), toRaw(camera))
64
+ renderer.render(
65
+ toRaw(scene),
66
+ // opts.app.config.globalProperties.lunchbox.camera!
67
+ toRaw(camera)
68
+ )
59
69
  }
60
70
  }
61
71
 
62
72
  // AFTER RENDER
63
- afterRender.forEach((cb: Lunch.UpdateCallback | undefined) => {
64
- if (cb) {
65
- cb(opts)
66
- }
73
+ app.config.globalProperties.lunchbox.afterRender.forEach((cb) => {
74
+ cb?.(opts)
67
75
  })
68
76
  }
69
77
 
70
- export const onBeforeRender = (cb: Lunch.UpdateCallback, index = Infinity) => {
71
- if (index === Infinity) {
72
- beforeRender.push(cb)
73
- } else {
74
- beforeRender.splice(index, 0, cb)
78
+ // before render
79
+ // ====================
80
+ // TODO: document
81
+ export const useBeforeRender = () => {
82
+ return {
83
+ onBeforeRender: inject<typeof onBeforeRender>(Keys.onBeforeRenderKey),
84
+ offBeforeRender: inject<typeof offBeforeRender>(
85
+ Keys.offBeforeRenderKey
86
+ ),
75
87
  }
76
88
  }
77
89
 
90
+ // TODO: document
91
+ export const onBeforeRender = (cb: Lunch.UpdateCallback, index = Infinity) => {
92
+ useBeforeRender().onBeforeRender?.(cb, index)
93
+ }
94
+
95
+ // TODO: document
78
96
  export const offBeforeRender = (cb: Lunch.UpdateCallback | number) => {
79
- if (isFinite(cb as number)) {
80
- beforeRender.splice(cb as number, 1)
81
- } else {
82
- const idx = beforeRender.findIndex((v) => v == cb)
83
- beforeRender.splice(idx, 1)
97
+ useBeforeRender().offBeforeRender?.(cb)
98
+ }
99
+
100
+ // after render
101
+ // ====================
102
+ // TODO: document
103
+ export const useAfterRender = () => {
104
+ return {
105
+ onAfterRender: inject<typeof onAfterRender>(Keys.onBeforeRenderKey),
106
+ offAfterRender: inject<typeof offAfterRender>(Keys.offBeforeRenderKey),
84
107
  }
85
108
  }
86
109
 
110
+ // TODO: document
87
111
  export const onAfterRender = (cb: Lunch.UpdateCallback, index = Infinity) => {
88
- if (index === Infinity) {
89
- afterRender.push(cb)
90
- } else {
91
- afterRender.splice(index, 0, cb)
92
- }
112
+ useBeforeRender().onBeforeRender?.(cb, index)
93
113
  }
94
114
 
115
+ // TODO: document
95
116
  export const offAfterRender = (cb: Lunch.UpdateCallback | number) => {
96
- if (isFinite(cb as number)) {
97
- afterRender.splice(cb as number, 1)
98
- } else {
99
- const idx = afterRender.findIndex((v) => v == cb)
100
- afterRender.splice(idx, 1)
117
+ useBeforeRender().offBeforeRender?.(cb)
118
+ }
119
+
120
+ // export const onAfterRender = (cb: Lunch.UpdateCallback, index = Infinity) => {
121
+ // if (index === Infinity) {
122
+ // afterRender.push(cb)
123
+ // } else {
124
+ // afterRender.splice(index, 0, cb)
125
+ // }
126
+ // }
127
+
128
+ // export const offAfterRender = (cb: Lunch.UpdateCallback | number) => {
129
+ // if (isFinite(cb as number)) {
130
+ // afterRender.splice(cb as number, 1)
131
+ // } else {
132
+ // const idx = afterRender.findIndex((v) => v == cb)
133
+ // afterRender.splice(idx, 1)
134
+ // }
135
+ // }
136
+
137
+ // TODO: document
138
+ export const useCancelUpdate = () => {
139
+ const frameId = inject<number>(Keys.frameIdKey)
140
+ return () => {
141
+ if (frameId !== undefined) cancelAnimationFrame(frameId)
101
142
  }
102
143
  }
103
144
 
145
+ // TODO: document
104
146
  export const cancelUpdate = () => {
105
- if (frameID) cancelAnimationFrame(frameID)
147
+ useCancelUpdate()?.()
148
+ }
149
+
150
+ // TODO: document
151
+ export const useCancelUpdateSource = () => {
152
+ const cancel = inject<
153
+ Lunch.App['config']['globalProperties']['watchStopHandle']
154
+ >(Keys.watchStopHandleKey)
155
+ return () => cancel?.()
106
156
  }
107
157
 
158
+ // TODO: document
108
159
  export const cancelUpdateSource = () => {
109
- if (watchStopHandle) watchStopHandle()
160
+ useCancelUpdateSource()?.()
110
161
  }
package/src/index.ts CHANGED
@@ -1,82 +1,54 @@
1
- import { computed, createRenderer, Component, ref, watch } from 'vue'
1
+ import {
2
+ computed,
3
+ createRenderer,
4
+ Component,
5
+ inject,
6
+ watch,
7
+ reactive,
8
+ } from 'vue'
2
9
  import { nodeOps } from './nodeOps'
3
10
  import {
4
- // createdCamera,
5
- // createdRenderer,
6
- // autoScene,
7
11
  ensuredCamera,
8
12
  ensureRenderer,
9
13
  ensuredScene,
10
- ensureRootNode,
11
14
  extend,
12
- inputActive,
13
- mousePos,
14
- rootUuid,
15
+ MiniDom,
15
16
  } from './core'
16
17
  import { components } from './components'
17
18
  import { Lunch } from './types'
18
19
 
19
- export { lunchboxRootNode as lunchboxTree } from './core'
20
- export {
21
- offAfterRender,
22
- offBeforeRender,
23
- onAfterRender,
24
- onBeforeRender,
25
- onStart,
26
- } from './core'
20
+ // export { lunchboxRootNode as lunchboxTree } from './core'
21
+ export * from './core'
27
22
  export * from './types'
28
23
 
24
+ import * as Keys from './keys'
25
+ export * from './keys'
26
+
29
27
  // Utilities
30
28
  export * from './utils/find'
31
29
 
32
- /** Useful globals. */
33
- export const globals = {
34
- dpr: ref(1),
35
- inputActive,
36
- mousePos,
37
- }
38
-
39
30
  /** The current camera. Often easier to use `useCamera` instead of this. */
40
- export const camera = computed(() => ensuredCamera.value?.instance ?? null)
41
- /** Run a function using the current camera when it's present. */
42
- export function useCamera<T extends THREE.Camera = THREE.PerspectiveCamera>(
43
- callback: (cam: T) => void
44
- ) {
45
- return watch(
46
- camera,
47
- (newVal) => {
48
- if (!newVal) return
49
- callback(newVal as unknown as T)
50
- },
51
- { immediate: true }
52
- )
53
- }
31
+ // TODO: update docs
32
+ export const camera = ensuredCamera
33
+ // TODO: update docs
34
+ export const useCamera = () => ensuredCamera()
54
35
 
55
- /** The current renderer. Often easier to use `useRenderer` instead of this. */
56
- export const renderer = computed(() => ensureRenderer.value?.instance ?? null)
36
+ /** The current renderer as a computed value. Often easier to use `useRenderer` instead of this. */
37
+ export const renderer = ensureRenderer
57
38
  /** Run a function using the current renderer when it's present. */
58
- export function useRenderer<T extends THREE.Renderer = THREE.WebGLRenderer>(
59
- callback: (rend: T) => void
60
- ) {
61
- return watch(
62
- renderer,
63
- (newVal) => {
64
- if (!newVal) return
65
- callback(newVal as unknown as T)
66
- },
67
- { immediate: true }
68
- )
69
- }
39
+ export const useRenderer = () => ensureRenderer()!
70
40
 
71
41
  /** The current scene. Often easier to use `useScene` instead of this. */
72
- export const scene = computed(() => ensuredScene.value.instance)
42
+ // TODO: update docs
43
+ export const scene = ensuredScene
73
44
  /** Run a function using the current scene when it's present. */
45
+ // TODO: update docs
74
46
  export function useScene(callback: (newScene: THREE.Scene) => void) {
75
47
  return watch(
76
48
  scene,
77
49
  (newVal) => {
78
50
  if (!newVal) return
79
- callback(newVal as any)
51
+ callback(newVal.value)
80
52
  },
81
53
  { immediate: true }
82
54
  )
@@ -84,38 +56,207 @@ export function useScene(callback: (newScene: THREE.Scene) => void) {
84
56
 
85
57
  // CUSTOM RENDER SUPPORT
86
58
  // ====================
87
- let app: Lunch.App | null = null
88
- let queuedCustomRenderFunction:
89
- | ((opts: Lunch.UpdateCallbackProperties) => void)
90
- | null = null
91
-
92
59
  /** Set a custom render function, overriding the Lunchbox app's default render function.
93
60
  * Changing this requires the user to manually render their scene.
61
+ *
62
+ * Invokes immediately - use `useCustomRender().setCustomRender`
63
+ * if you need to call somewhere outside of `setup`.
94
64
  */
95
65
  export const setCustomRender = (
96
66
  render: (opts: Lunch.UpdateCallbackProperties) => void
97
67
  ) => {
98
- if (app) app.setCustomRender(render)
99
- else queuedCustomRenderFunction = render
68
+ useCustomRender()?.setCustomRender?.(render)
100
69
  }
101
70
 
102
- /** Clear the active app's custom render function. */
71
+ /** Clear the active app's custom render function.
72
+ *
73
+ * Invokes immediately - use `useCustomRender().clearCustomRender`
74
+ * if you need to call somewhere outside of `setup`.
75
+ */
103
76
  export const clearCustomRender = () => {
104
- if (app) app.clearCustomRender()
105
- else queuedCustomRenderFunction = null
77
+ useCustomRender()?.clearCustomRender?.()
78
+ }
79
+
80
+ /** Provides `setCustomRender` and `clearCustomRender` functions to be called in a non-`setup` context. */
81
+ export const useCustomRender = () => {
82
+ return {
83
+ /** Set a custom render function, overriding the Lunchbox app's default render function.
84
+ * Changing this requires the user to manually render their scene. */
85
+ setCustomRender: inject<Lunch.CustomRenderFunctionSetter>(
86
+ Keys.setCustomRenderKey
87
+ ),
88
+ /** Clear the active app's custom render function. */
89
+ clearCustomRender: inject<() => void>(Keys.clearCustomRenderKey),
90
+ }
91
+ }
92
+
93
+ /** Use app-level globals. */
94
+ export const useGlobals = () =>
95
+ inject<Lunch.AppGlobals>(Keys.globalsInjectionKey)!
96
+
97
+ /** Construct a function to update your app-level globals.
98
+ *
99
+ * ```js
100
+ * // in setup():
101
+ * const updateGlobals = useUpdateGlobals()
102
+ *
103
+ * // ...later, to update the device pixel resolution...
104
+ * updateGlobals({ dpr: 2 })
105
+ * ```
106
+ */
107
+ export const useUpdateGlobals = () =>
108
+ inject<Lunch.AppGlobalsUpdate>(Keys.updateGlobalsInjectionKey)
109
+
110
+ /** Update app-level globals.
111
+ *
112
+ * Invokes immediately - use `useUpdateGlobals`
113
+ * if you need to call somewhere outside of `setup`.
114
+ */
115
+ export const updateGlobals = (newValue: Partial<Lunch.AppGlobals>) => {
116
+ useUpdateGlobals()?.(newValue)
117
+ }
118
+
119
+ // TODO: document
120
+ export const useRootNode = () =>
121
+ inject<MiniDom.RendererRootNode>(Keys.appRootNodeKey)
122
+
123
+ // TODO: document
124
+ export const useApp = () => inject<Lunch.App>(Keys.appKey)
125
+
126
+ // TODO: document
127
+ export const useStartCallbacks = () =>
128
+ inject<Lunch.UpdateCallback[]>(Keys.startCallbackKey) //[] as Lunch.UpdateCallback[]
129
+
130
+ // TODO: document
131
+ export const onStart = (cb: Lunch.UpdateCallback, index = Infinity) => {
132
+ const callbacks = useStartCallbacks()
133
+ if (index === Infinity) {
134
+ callbacks?.push(cb)
135
+ } else {
136
+ callbacks?.splice(index, 0, cb)
137
+ }
106
138
  }
107
139
 
108
140
  // CREATE APP
109
141
  // ====================
110
142
  export const createApp = (root: Component) => {
111
- app = createRenderer(nodeOps).createApp(root) as Lunch.App
143
+ const app = createRenderer(nodeOps).createApp(root) as Lunch.App
112
144
 
113
145
  // register all components
146
+ // ====================
114
147
  Object.keys(components).forEach((key) => {
115
148
  app?.component(key, (components as any)[key])
116
149
  })
117
150
 
151
+ // provide custom renderer functions
152
+ // ====================
153
+ app.provide(
154
+ Keys.setCustomRenderKey,
155
+ (render: (opts: Lunch.UpdateCallbackProperties) => void) => {
156
+ app.setCustomRender(render)
157
+ }
158
+ )
159
+ app.provide(Keys.clearCustomRenderKey, () => {
160
+ app.clearCustomRender()
161
+ })
162
+
163
+ // before render
164
+ // ====================
165
+ const beforeRender = [] as Lunch.UpdateCallback[]
166
+ app.provide(Keys.beforeRenderKey, beforeRender)
167
+ app.provide(
168
+ Keys.onBeforeRenderKey,
169
+ (cb: Lunch.UpdateCallback, index = Infinity) => {
170
+ if (index === Infinity) {
171
+ beforeRender.push(cb)
172
+ } else {
173
+ beforeRender.splice(index, 0, cb)
174
+ }
175
+ }
176
+ )
177
+ app.provide(
178
+ Keys.offBeforeRenderKey,
179
+ (cb: Lunch.UpdateCallback | number) => {
180
+ if (isFinite(cb as number)) {
181
+ beforeRender.splice(cb as number, 1)
182
+ } else {
183
+ const idx = beforeRender.findIndex((v) => v == cb)
184
+ if (idx !== -1) {
185
+ beforeRender.splice(idx, 1)
186
+ }
187
+ }
188
+ }
189
+ )
190
+
191
+ // after render
192
+ // ====================
193
+ const afterRender = [] as Lunch.UpdateCallback[]
194
+ app.provide(Keys.afterRenderKey, afterRender)
195
+ app.provide(
196
+ Keys.onAfterRenderKey,
197
+ (cb: Lunch.UpdateCallback, index = Infinity) => {
198
+ if (index === Infinity) {
199
+ afterRender.push(cb)
200
+ } else {
201
+ afterRender.splice(index, 0, cb)
202
+ }
203
+ }
204
+ )
205
+ app.provide(Keys.offAfterRenderKey, (cb: Lunch.UpdateCallback | number) => {
206
+ if (isFinite(cb as number)) {
207
+ afterRender.splice(cb as number, 1)
208
+ } else {
209
+ const idx = afterRender.findIndex((v) => v == cb)
210
+ if (idx !== -1) {
211
+ afterRender.splice(idx, 1)
212
+ }
213
+ }
214
+ })
215
+
216
+ // save app-level components
217
+ // ====================
218
+ app.config.globalProperties.lunchbox = reactive({
219
+ afterRender,
220
+ beforeRender,
221
+ camera: null,
222
+ dpr: 1,
223
+ frameId: -1,
224
+ renderer: null,
225
+ scene: null,
226
+ watchStopHandle: null,
227
+
228
+ // TODO: inputActive, mousePos
229
+ })
230
+
231
+ // provide app-level globals & globals update method
232
+ // ====================
233
+ app.provide(Keys.globalsInjectionKey, app.config.globalProperties.lunchbox)
234
+ app.provide<Lunch.AppGlobalsUpdate>(
235
+ Keys.updateGlobalsInjectionKey,
236
+ (newGlobals: Partial<Lunch.AppGlobals>) => {
237
+ Object.keys(newGlobals).forEach((key) => {
238
+ const typedKey = key as keyof Lunch.AppGlobals
239
+ // TODO: fix
240
+ app.config.globalProperties.lunchbox[typedKey] = newGlobals[
241
+ typedKey
242
+ ] as any
243
+ })
244
+ }
245
+ )
246
+
247
+ // frame ID (used for update functions)
248
+ // ====================
249
+ app.provide(Keys.frameIdKey, app.config.globalProperties.lunchbox.frameId)
250
+
251
+ // watch stop handler (used for conditional update loop)
252
+ // ====================
253
+ app.provide(
254
+ Keys.watchStopHandleKey,
255
+ app.config.globalProperties.lunchbox.watchStopHandle
256
+ )
257
+
118
258
  // update mount function to match Lunchbox.Node
259
+ // ====================
119
260
  const { mount } = app
120
261
  app.mount = (root, ...args) => {
121
262
  // find DOM element to use as app root
@@ -123,43 +264,65 @@ export const createApp = (root: Component) => {
123
264
  typeof root === 'string' ? document.querySelector(root) : root
124
265
  ) as HTMLElement
125
266
  // create or find root node
126
- const rootNode = ensureRootNode({
267
+ const rootNode = new MiniDom.RendererRootNode({
127
268
  domElement,
128
269
  isLunchboxRootNode: true,
129
270
  name: 'root',
130
271
  metaType: 'rootMeta',
131
272
  type: 'root',
132
- uuid: rootUuid,
273
+ uuid: 'LUNCHBOX_ROOT',
133
274
  })
134
- app!.rootNode = rootNode
275
+ app.rootNode = rootNode
276
+ app.provide(Keys.appRootNodeKey, rootNode)
135
277
  const mounted = mount(rootNode, ...args)
136
278
  return mounted
137
279
  }
138
280
 
139
281
  // embed .extend function
282
+ // ====================
140
283
  app.extend = (targets: Record<string, any>) => {
141
284
  extend({ app: app!, ...targets })
142
285
  return app!
143
286
  }
144
287
 
288
+ // start callback functions
289
+ // ====================
290
+ const startCallbacks: Lunch.UpdateCallback[] = []
291
+ app.provide(Keys.startCallbackKey, startCallbacks)
292
+
145
293
  // prep for custom render support
294
+ // ====================
146
295
  app.setCustomRender = (
147
296
  newRender: (opts: Lunch.UpdateCallbackProperties) => void
148
297
  ) => {
149
- app!.customRender = newRender
150
- }
151
-
152
- // add queued custom render if we have one
153
- if (queuedCustomRenderFunction) {
154
- app.setCustomRender(queuedCustomRenderFunction)
155
- queuedCustomRenderFunction = null
298
+ if (app) {
299
+ app.customRender = newRender
300
+ }
156
301
  }
157
302
 
158
303
  // add custom render removal
159
304
  app.clearCustomRender = () => {
160
- app!.customRender = null
305
+ if (app) {
306
+ app.customRender = null
307
+ }
161
308
  }
162
309
 
310
+ // provide app
311
+ // ====================
312
+ app.provide(Keys.appKey, app)
313
+ app.provide(
314
+ Keys.appRenderersKey,
315
+ computed(() => app.config.globalProperties.lunchbox.renderer)
316
+ )
317
+ app.provide(
318
+ Keys.appSceneKey,
319
+ computed(() => app.config.globalProperties.lunchbox.scene)
320
+ )
321
+ app.provide(
322
+ Keys.appCameraKey,
323
+ computed(() => app.config.globalProperties.lunchbox.camera)
324
+ )
325
+
163
326
  // done
164
327
  return app
165
328
  }
package/src/keys.ts ADDED
@@ -0,0 +1,24 @@
1
+ export const globalsInjectionKey = Symbol()
2
+ export const updateGlobalsInjectionKey = Symbol()
3
+
4
+ export const setCustomRenderKey = Symbol()
5
+ export const clearCustomRenderKey = Symbol()
6
+
7
+ export const beforeRenderKey = Symbol()
8
+ export const onBeforeRenderKey = Symbol()
9
+ export const offBeforeRenderKey = Symbol()
10
+
11
+ export const afterRenderKey = Symbol()
12
+ export const onAfterRenderKey = Symbol()
13
+ export const offAfterRenderKey = Symbol()
14
+
15
+ export const frameIdKey = Symbol()
16
+ export const watchStopHandleKey = Symbol()
17
+
18
+ export const appRootNodeKey = Symbol()
19
+ export const appKey = Symbol()
20
+ export const appRenderersKey = Symbol()
21
+ export const appSceneKey = Symbol()
22
+ export const appCameraKey = Symbol()
23
+
24
+ export const startCallbackKey = Symbol()
@@ -10,13 +10,10 @@ export const createElement = (
10
10
  isCustomizedBuiltin?: string,
11
11
  vnodeProps?: Lunch.LunchboxMetaProps
12
12
  ) => {
13
- const options = { type } as Partial<Lunch.MetaBase>
14
- if (vnodeProps) {
15
- options.props = vnodeProps
16
- }
13
+ const options: Partial<Lunch.MetaBase> = { type, props: vnodeProps }
17
14
 
18
15
  // handle dom node
19
- const isDomNode = isLunchboxDomComponent(type)
16
+ const isDomNode = isLunchboxDomComponent(options)
20
17
  if (isDomNode) {
21
18
  const node = createDomNode(options)
22
19
  return node