lunchboxjs 0.2.1001-beta.0 → 0.2.1001-beta.301

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,18 +1,7 @@
1
- // import {
2
- //ensureRenderer,
3
- // ensuredScene,
4
- // ensuredCamera,
5
- // } from '.'
6
- import { Lunch } from '..'
7
- import { inject, toRaw, watch, WatchStopHandle } from 'vue'
1
+ import type { Lunch } from '..'
2
+ import { inject, toRaw, watch } from 'vue'
8
3
  import * as Keys from '../keys'
9
4
 
10
- // let frameID: number
11
- // let watchStopHandle: WatchStopHandle
12
-
13
- // export const beforeRender = [] as Lunch.UpdateCallback[]
14
- // export const afterRender = [] as Lunch.UpdateCallback[]
15
-
16
5
  const requestUpdate = (opts: Lunch.UpdateCallbackProperties) => {
17
6
  if (typeof opts.app.config.globalProperties.lunchbox.frameId === 'number') {
18
7
  cancelAnimationFrame(opts.app.config.globalProperties.lunchbox.frameId)
@@ -49,7 +38,7 @@ export const update: Lunch.UpdateCallback = (opts) => {
49
38
  }
50
39
 
51
40
  // prep options
52
- const { app, renderer, scene, camera } = opts
41
+ const { app, renderer, scene } = opts
53
42
 
54
43
  // BEFORE RENDER
55
44
  app.config.globalProperties.lunchbox.beforeRender.forEach((cb) => {
@@ -57,14 +46,14 @@ export const update: Lunch.UpdateCallback = (opts) => {
57
46
  })
58
47
 
59
48
  // RENDER
60
- if (renderer && scene && camera) {
49
+ if (renderer && scene && opts.app.config.globalProperties.lunchbox.camera) {
61
50
  if (app.customRender) {
62
51
  app.customRender(opts)
63
52
  } else {
64
53
  renderer.render(
65
54
  toRaw(scene),
66
- // opts.app.config.globalProperties.lunchbox.camera!
67
- toRaw(camera)
55
+ opts.app.config.globalProperties.lunchbox.camera
56
+ // toRaw(camera)
68
57
  )
69
58
  }
70
59
  }
@@ -77,7 +66,7 @@ export const update: Lunch.UpdateCallback = (opts) => {
77
66
 
78
67
  // before render
79
68
  // ====================
80
- // TODO: document
69
+ /** Obtain callback methods for `onBeforeRender` and `offBeforeRender`. Usually used internally by Lunchbox. */
81
70
  export const useBeforeRender = () => {
82
71
  return {
83
72
  onBeforeRender: inject<typeof onBeforeRender>(Keys.onBeforeRenderKey),
@@ -87,19 +76,25 @@ export const useBeforeRender = () => {
87
76
  }
88
77
  }
89
78
 
90
- // TODO: document
79
+ /** Run a function before every render.
80
+ *
81
+ * Note that if `updateSource` is set in the Lunchbox wrapper component, this will **only** run
82
+ * before a render triggered by that `updateSource`. Normally, the function should run every frame.
83
+ */
91
84
  export const onBeforeRender = (cb: Lunch.UpdateCallback, index = Infinity) => {
92
85
  useBeforeRender().onBeforeRender?.(cb, index)
93
86
  }
94
87
 
95
- // TODO: document
88
+ /** Remove a function from the `beforeRender` callback list. Useful for tearing down functions added
89
+ * by `onBeforeRender`.
90
+ */
96
91
  export const offBeforeRender = (cb: Lunch.UpdateCallback | number) => {
97
92
  useBeforeRender().offBeforeRender?.(cb)
98
93
  }
99
94
 
100
95
  // after render
101
96
  // ====================
102
- // TODO: document
97
+ /** Obtain callback methods for `onAfterRender` and `offAfterRender`. Usually used internally by Lunchbox. */
103
98
  export const useAfterRender = () => {
104
99
  return {
105
100
  onAfterRender: inject<typeof onAfterRender>(Keys.onBeforeRenderKey),
@@ -107,34 +102,25 @@ export const useAfterRender = () => {
107
102
  }
108
103
  }
109
104
 
110
- // TODO: document
105
+ /** Run a function after every render.
106
+ *
107
+ * Note that if `updateSource` is set in the Lunchbox wrapper component, this will **only** run
108
+ * after a render triggered by that `updateSource`. Normally, the function should run every frame.
109
+ */
111
110
  export const onAfterRender = (cb: Lunch.UpdateCallback, index = Infinity) => {
112
111
  useBeforeRender().onBeforeRender?.(cb, index)
113
112
  }
114
113
 
115
- // TODO: document
114
+ /** Remove a function from the `afterRender` callback list. Useful for tearing down functions added
115
+ * by `onAfterRender`.
116
+ */
116
117
  export const offAfterRender = (cb: Lunch.UpdateCallback | number) => {
117
118
  useBeforeRender().offBeforeRender?.(cb)
118
119
  }
119
120
 
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
121
+ /** Obtain a function used to cancel the current update frame. Use `cancelUpdate` if you wish
122
+ * to immediately invoke the cancellation function. Usually used internally by Lunchbox.
123
+ */
138
124
  export const useCancelUpdate = () => {
139
125
  const frameId = inject<number>(Keys.frameIdKey)
140
126
  return () => {
@@ -142,12 +128,14 @@ export const useCancelUpdate = () => {
142
128
  }
143
129
  }
144
130
 
145
- // TODO: document
131
+ /** Cancel the current update frame. Usually used internally by Lunchbox. */
146
132
  export const cancelUpdate = () => {
147
133
  useCancelUpdate()?.()
148
134
  }
149
135
 
150
- // TODO: document
136
+ /** Obtain a function used to cancel an update source. Use `cancelUpdateSource` if you wish to
137
+ * immediately invoke the cancellation function. Usually used internally by Lunchbox.
138
+ */
151
139
  export const useCancelUpdateSource = () => {
152
140
  const cancel = inject<
153
141
  Lunch.App['config']['globalProperties']['watchStopHandle']
@@ -155,7 +143,7 @@ export const useCancelUpdateSource = () => {
155
143
  return () => cancel?.()
156
144
  }
157
145
 
158
- // TODO: document
146
+ /** Cancel an update source. Usually used internally by Lunchbox. */
159
147
  export const cancelUpdateSource = () => {
160
148
  useCancelUpdateSource()?.()
161
149
  }
@@ -1,34 +1,25 @@
1
1
  import { isEventKey, isLunchboxStandardNode } from '../utils'
2
2
  import { addEventListener } from './interaction'
3
3
  import { get, isNumber, set } from 'lodash'
4
- import { Lunch } from '..'
5
-
6
- /** Update the given node so all of its props are current. */
7
- export function updateAllObjectProps({ node }: { node: Lunch.Node }) {
8
- // set props
9
- const props = node.props || {}
10
- let output = node
11
- Object.keys(props).forEach((key) => {
12
- output = updateObjectProp({ node, key, value: props[key] })
13
- })
14
-
15
- return output
16
- }
4
+ import type { Lunch } from '..'
5
+ import type { Ref } from 'vue'
17
6
 
18
7
  /** Update a single prop on a given node. */
19
8
  export function updateObjectProp({
20
9
  node,
21
10
  key,
11
+ interactables,
22
12
  value,
23
13
  }: {
24
14
  node: Lunch.Node
25
15
  key: string
16
+ interactables: Ref<Lunch.Node[]>
26
17
  value: any
27
18
  }) {
28
19
  // handle and return early if prop is an event
29
20
  // (event list from react-three-fiber)
30
21
  if (isEventKey(key)) {
31
- return addEventListener({ node, key, value })
22
+ return addEventListener({ node, key, interactables, value })
32
23
  }
33
24
 
34
25
  // update THREE property
@@ -67,6 +58,7 @@ export function updateObjectProp({
67
58
  }
68
59
 
69
60
  // change property
61
+ // first, save as array in case we need to spread it
70
62
  if (liveProperty && isNumber(value) && liveProperty.setScalar) {
71
63
  // if value is a number and the property has a `setScalar` method, use that
72
64
  liveProperty.setScalar(value)
@@ -75,8 +67,21 @@ export function updateObjectProp({
75
67
  const nextValueAsArray = Array.isArray(value) ? value : [value]
76
68
  ;(target as any)[finalKey].set(...nextValueAsArray)
77
69
  } else if (typeof liveProperty === 'function') {
78
- // if property is a function, let's try calling it
79
- liveProperty.bind(node.instance)(...value)
70
+ // some function properties are set rather than called, so let's handle them
71
+ if (
72
+ finalKey.toLowerCase() === 'onbeforerender' ||
73
+ finalKey.toLowerCase() === 'onafterrender'
74
+ ) {
75
+ ;(target as any)[finalKey] = value
76
+ } else {
77
+ if (!Array.isArray(value)) {
78
+ throw new Error(
79
+ 'Arguments on a declarative method must be wrapped in an array.\nWorks:\n<example :methodCall="[256]" />\nDoesn\'t work:\n<example :methodCall="256" />'
80
+ )
81
+ }
82
+ // if property is a function, let's try calling it
83
+ liveProperty.bind(node.instance)(...value)
84
+ }
80
85
 
81
86
  // pass the result to the parent
82
87
  // const parent = node.parentNode
package/src/index.ts CHANGED
@@ -2,22 +2,17 @@ import {
2
2
  computed,
3
3
  createRenderer,
4
4
  Component,
5
+ ComputedRef,
5
6
  inject,
6
7
  watch,
7
8
  reactive,
9
+ Ref,
8
10
  } from 'vue'
9
- import { nodeOps } from './nodeOps'
10
- import {
11
- ensuredCamera,
12
- ensureRenderer,
13
- ensuredScene,
14
- extend,
15
- MiniDom,
16
- } from './core'
11
+ import { createNodeOps } from './nodeOps'
12
+ import { extend, MiniDom } from './core'
17
13
  import { components } from './components'
18
14
  import { Lunch } from './types'
19
15
 
20
- // export { lunchboxRootNode as lunchboxTree } from './core'
21
16
  export * from './core'
22
17
  export * from './types'
23
18
 
@@ -27,28 +22,58 @@ export * from './keys'
27
22
  // Utilities
28
23
  export * from './utils/find'
29
24
 
30
- /** The current camera. Often easier to use `useCamera` instead of this. */
31
- // TODO: update docs
32
- export const camera = ensuredCamera
33
- // TODO: update docs
34
- export const useCamera = () => ensuredCamera()
25
+ /** The current camera as a computed value. */
26
+ export const useCamera = <T extends THREE.Camera = THREE.Camera>() =>
27
+ inject<ComputedRef<T>>(Keys.appCameraKey)!
28
+ /** Run a function using the current camera when it's present. */
29
+ export const onCameraReady = <T extends THREE.Camera = THREE.Camera>(
30
+ cb: (camera?: T) => void
31
+ ) => {
32
+ const stopWatch = watch(
33
+ useCamera<T>(),
34
+ (newVal) => {
35
+ if (newVal) {
36
+ cb(newVal)
37
+ stopWatch()
38
+ }
39
+ },
40
+ { immediate: true }
41
+ )
42
+ }
35
43
 
36
- /** The current renderer as a computed value. Often easier to use `useRenderer` instead of this. */
37
- export const renderer = ensureRenderer
44
+ /** The current renderer as a computed value. */
45
+ export const useRenderer = <T extends THREE.Renderer = THREE.WebGLRenderer>() =>
46
+ inject<ComputedRef<T>>(Keys.appRenderersKey)!
38
47
  /** Run a function using the current renderer when it's present. */
39
- export const useRenderer = () => ensureRenderer()!
48
+ export const onRendererReady = <T extends THREE.Renderer = THREE.Renderer>(
49
+ cb: (renderer?: T) => void
50
+ ) => {
51
+ const stopWatch = watch(
52
+ useRenderer<T>(),
53
+ (newVal) => {
54
+ if (newVal) {
55
+ cb(newVal)
56
+ stopWatch()
57
+ }
58
+ },
59
+ { immediate: true }
60
+ )
61
+ }
40
62
 
41
- /** The current scene. Often easier to use `useScene` instead of this. */
42
- // TODO: update docs
43
- export const scene = ensuredScene
63
+ /** The current scene as a computed value. */
64
+ export const useScene = <T extends THREE.Scene = THREE.Scene>() =>
65
+ inject<ComputedRef<T>>(Keys.appSceneKey)!
44
66
  /** Run a function using the current scene when it's present. */
45
- // TODO: update docs
46
- export function useScene(callback: (newScene: THREE.Scene) => void) {
47
- return watch(
48
- scene,
67
+ export const onSceneReady = <T extends THREE.Scene = THREE.Scene>(
68
+ cb: (scene?: T) => void
69
+ ) => {
70
+ const stopWatch = watch(
71
+ useScene<T>(),
49
72
  (newVal) => {
50
- if (!newVal) return
51
- callback(newVal.value)
73
+ if (newVal) {
74
+ cb(newVal)
75
+ stopWatch()
76
+ }
52
77
  },
53
78
  { immediate: true }
54
79
  )
@@ -116,18 +141,15 @@ export const updateGlobals = (newValue: Partial<Lunch.AppGlobals>) => {
116
141
  useUpdateGlobals()?.(newValue)
117
142
  }
118
143
 
119
- // TODO: document
120
- export const useRootNode = () =>
121
- inject<MiniDom.RendererRootNode>(Keys.appRootNodeKey)
122
-
123
- // TODO: document
144
+ /** Use the current Lunchbox app. Usually used internally by Lunchbox. */
124
145
  export const useApp = () => inject<Lunch.App>(Keys.appKey)
125
146
 
126
- // TODO: document
147
+ /** Obtain a list of the start callback functions. Usually used internally by Lunchbox. */
127
148
  export const useStartCallbacks = () =>
128
- inject<Lunch.UpdateCallback[]>(Keys.startCallbackKey) //[] as Lunch.UpdateCallback[]
149
+ inject<Lunch.UpdateCallback[]>(Keys.startCallbackKey)
129
150
 
130
- // TODO: document
151
+ /** Run a given callback once when the Lunchbox app starts. Include an index to
152
+ * splice the callback at that index in the callback queue. */
131
153
  export const onStart = (cb: Lunch.UpdateCallback, index = Infinity) => {
132
154
  const callbacks = useStartCallbacks()
133
155
  if (index === Infinity) {
@@ -137,11 +159,20 @@ export const onStart = (cb: Lunch.UpdateCallback, index = Infinity) => {
137
159
  }
138
160
  }
139
161
 
162
+ /** Obtain a list of interactable objects (registered via onClick, onHover, etc events). Usually used internally by Lunchbox. */
163
+ export const useLunchboxInteractables = () =>
164
+ inject<Ref<Lunch.Node[]>>(Keys.lunchboxInteractables)
165
+
140
166
  // CREATE APP
141
167
  // ====================
142
168
  export const createApp = (root: Component) => {
169
+ const { nodeOps, interactables } = createNodeOps()
143
170
  const app = createRenderer(nodeOps).createApp(root) as Lunch.App
144
171
 
172
+ // provide Lunchbox interaction handlers flag (modified when user references events via
173
+ // @click, etc)
174
+ app.provide(Keys.lunchboxInteractables, interactables)
175
+
145
176
  // register all components
146
177
  // ====================
147
178
  Object.keys(components).forEach((key) => {
package/src/keys.ts CHANGED
@@ -20,5 +20,6 @@ export const appKey = Symbol()
20
20
  export const appRenderersKey = Symbol()
21
21
  export const appSceneKey = Symbol()
22
22
  export const appCameraKey = Symbol()
23
+ export const lunchboxInteractables = Symbol()
23
24
 
24
25
  export const startCallbackKey = Symbol()
@@ -1,70 +1,83 @@
1
- import { RendererOptions } from 'vue'
1
+ import { RendererOptions, ref } from 'vue'
2
2
  import { createElement } from './createElement'
3
3
  import { insert } from './insert'
4
4
  import { remove } from './remove'
5
5
  import { isLunchboxDomComponent, isLunchboxRootNode } from '../utils'
6
- import {
7
- createCommentNode,
8
- createTextNode,
9
- MiniDom,
10
- updateObjectProp,
11
- } from '../core'
12
- import { Lunch } from '..'
6
+ import { createCommentNode, createTextNode, updateObjectProp } from '../core'
7
+ import type { MiniDom } from '../core'
8
+ import type { Lunch } from '..'
13
9
 
14
10
  /*
15
11
  Elements are `create`d from the outside in, then `insert`ed from the inside out.
16
12
  */
17
13
 
18
- export const nodeOps: RendererOptions<
19
- MiniDom.RendererBaseNode,
20
- MiniDom.RendererBaseNode
21
- > = {
22
- createElement,
23
- createText(text) {
24
- return createTextNode({ text })
25
- },
26
- createComment(text) {
27
- return createCommentNode({ text })
28
- },
29
- insert,
30
- nextSibling(node) {
31
- const result = node.nextSibling
32
- // console.log('found', result)
33
- if (!result) return null
34
- return result as MiniDom.RendererBaseNode
35
- },
36
- parentNode(node) {
37
- const result = node.parentNode
38
- if (!result) return null
39
- return result as MiniDom.RendererBaseNode
40
- },
41
- patchProp(node, key, prevValue, nextValue) {
42
- if (isLunchboxDomComponent(node)) {
43
- // handle DOM node
44
- if (key === 'style') {
45
- // special handling for style
46
- Object.keys(nextValue).forEach((k) => {
47
- ;(node.domElement.style as any)[k] = nextValue[k]
48
- })
49
- } else {
50
- node.domElement.setAttribute(key, nextValue)
14
+ export const createNodeOps = () => {
15
+ // APP-LEVEL GLOBALS
16
+ // ====================
17
+ // These need to exist at the app level in a place where the node ops can access them.
18
+ // It'd be better to set these via `app.provide` at app creation, but the node ops need access
19
+ // to these values before the app is instantiated, so this is the next-best place for them to exist.
20
+ const interactables = ref([] as Lunch.Node[])
21
+
22
+ // NODE OPS
23
+ // ====================
24
+ const nodeOps: RendererOptions<
25
+ MiniDom.RendererBaseNode,
26
+ MiniDom.RendererBaseNode
27
+ > = {
28
+ createElement,
29
+ createText(text) {
30
+ return createTextNode({ text })
31
+ },
32
+ createComment(text) {
33
+ return createCommentNode({ text })
34
+ },
35
+ insert,
36
+ nextSibling(node) {
37
+ const result = node.nextSibling
38
+ if (!result) return null
39
+ return result as MiniDom.RendererBaseNode
40
+ },
41
+ parentNode(node) {
42
+ const result = node.parentNode
43
+ if (!result) return null
44
+ return result as MiniDom.RendererBaseNode
45
+ },
46
+ patchProp(node, key, prevValue, nextValue) {
47
+ if (isLunchboxDomComponent(node)) {
48
+ // handle DOM node
49
+ if (key === 'style') {
50
+ // special handling for style
51
+ Object.keys(nextValue).forEach((k) => {
52
+ ;(node.domElement.style as any)[k] = nextValue[k]
53
+ })
54
+ } else {
55
+ node.domElement.setAttribute(key, nextValue)
56
+ }
57
+ return
58
+ }
59
+
60
+ // ignore if root node, or Lunchbox internal prop
61
+ if (isLunchboxRootNode(node) || key.startsWith('$')) {
62
+ return
51
63
  }
52
- return
53
- }
54
64
 
55
- // ignore if root node, or Lunchbox internal prop
56
- if (isLunchboxRootNode(node) || key.startsWith('$')) {
57
- return
58
- }
65
+ // otherwise, update prop
66
+ updateObjectProp({
67
+ node: node as Lunch.Node,
68
+ key,
69
+ interactables,
70
+ value: nextValue,
71
+ })
72
+ },
73
+ remove,
74
+ setElementText() {
75
+ // noop
76
+ },
77
+ setText() {
78
+ // noop
79
+ },
80
+ }
59
81
 
60
- // otherwise, update prop
61
- updateObjectProp({ node: node as Lunch.Node, key, value: nextValue })
62
- },
63
- remove,
64
- setElementText() {
65
- // noop
66
- },
67
- setText() {
68
- // noop
69
- },
82
+ return { nodeOps, interactables }
70
83
  }
package/src/types.ts CHANGED
@@ -36,10 +36,6 @@ export declare namespace Lunch {
36
36
  update: UpdateCallback
37
37
  }
38
38
 
39
- // type AppGlobals = {
40
- // /** Device pixel resolution */
41
- // dpr: number
42
- // }
43
39
  type AppGlobals = App['config']['globalProperties']['lunchbox']
44
40
 
45
41
  type AppGlobalsUpdate = (newValue: Partial<AppGlobals>) => void
@@ -159,10 +155,6 @@ export declare namespace Lunch {
159
155
  renderer?: THREE.Renderer | null
160
156
  camera?: THREE.Camera | null
161
157
  updateSource?: WatchSource | null
162
-
163
- // sceneNode: Node<THREE.Scene> | null
164
- // rendererNode: Node<THREE.Renderer> | null
165
- // cameraNode: Node<THREE.Camera> | null
166
158
  }
167
159
 
168
160
  /** Universally unique identifier. */
@@ -43,3 +43,13 @@ export const isLunchboxStandardNode = (
43
43
  export const isLunchboxRootNode = (node: any): node is Lunch.RootMeta => {
44
44
  return node.isLunchboxRootNode
45
45
  }
46
+
47
+ export const waitFor = async (get: () => any) => {
48
+ let output = get()
49
+ while (!output) {
50
+ await new Promise((resolve) => requestAnimationFrame(resolve))
51
+ output = get()
52
+ console.log(output)
53
+ }
54
+ return output
55
+ }
@@ -1,3 +0,0 @@
1
- import { Lunch } from '..'
2
-
3
- export const catalogue: Lunch.Catalogue = {}
@@ -1,16 +0,0 @@
1
- import { ComputedRef, inject } from 'vue'
2
- import * as Keys from '../keys'
3
-
4
- export const ensuredCamera = <T extends THREE.Camera = THREE.Camera>() =>
5
- inject<ComputedRef<T>>(Keys.appCameraKey)!
6
-
7
- // ENSURE RENDERER
8
- // ====================
9
- export const ensureRenderer = <
10
- T extends THREE.Renderer = THREE.WebGLRenderer
11
- >() => inject<ComputedRef<T>>(Keys.appRenderersKey)
12
-
13
- // ENSURE SCENE
14
- // ====================
15
- export const ensuredScene = <T extends THREE.Scene = THREE.Scene>() =>
16
- inject<ComputedRef<T>>(Keys.appSceneKey)