@px-petals/roku.motion-web 0.0.0-stage → 1.1.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/LICENSE +201 -0
- package/audio/audio.cues.js +27 -0
- package/audio/audio.js +259 -0
- package/bands/bands.js +392 -0
- package/bundle/bundle.fonts.js +55 -0
- package/bundle/bundle.js +131 -0
- package/focus/focus.direction.js +46 -0
- package/focus/focus.dom.js +158 -0
- package/focus/focus.js +277 -0
- package/focus/focus.tree.js +146 -0
- package/hit/hit.geometry.js +113 -0
- package/hit/hit.js +129 -0
- package/list/list.flex.js +181 -0
- package/list/list.items.js +80 -0
- package/list/list.js +518 -0
- package/lottie/lottie.js +124 -0
- package/lottie/lottie.layers.js +416 -0
- package/lottie/lottie.paint.js +238 -0
- package/lottie/lottie.paths.js +474 -0
- package/lottie/lottie.props.js +265 -0
- package/lottie/lottie.shapes.js +259 -0
- package/mixer/mixer.js +307 -0
- package/mixer/mixer.mix.js +107 -0
- package/mixer/mixer.stage.js +431 -0
- package/motion/motion.convert.js +404 -0
- package/motion/motion.flow.js +421 -0
- package/motion/motion.keys.js +150 -0
- package/motion/motion.machine.js +382 -0
- package/motion/motion.nested.js +345 -0
- package/motion/motion.values.js +368 -0
- package/motion.js +1155 -0
- package/package.json +47 -4
- package/player/overlay/overlay.js +325 -0
- package/player/overlay/overlay.layout.js +208 -0
- package/player/player.fit.js +101 -0
- package/player/player.js +421 -0
- package/player/player.raster.js +127 -0
- package/pointer/pointer.js +292 -0
- package/readme.md +345 -0
- package/script/luau/lib/base/base.js +202 -0
- package/script/luau/lib/base/base.table.js +189 -0
- package/script/luau/lib/lib.args.js +152 -0
- package/script/luau/lib/lib.js +23 -0
- package/script/luau/lib/math/math.bit32.js +144 -0
- package/script/luau/lib/math/math.js +288 -0
- package/script/luau/lib/math/math.vector.js +125 -0
- package/script/luau/lib/string/string.format.js +143 -0
- package/script/luau/lib/string/string.js +160 -0
- package/script/luau/lib/string/string.pattern.js +505 -0
- package/script/luau/load/load.js +362 -0
- package/script/luau/load/load.ops.js +79 -0
- package/script/luau/luau.convert.js +259 -0
- package/script/luau/luau.js +132 -0
- package/script/luau/luau.md +111 -0
- package/script/luau/vm/vm.js +167 -0
- package/script/luau/vm/vm.meta.js +401 -0
- package/script/luau/vm/vm.num.js +236 -0
- package/script/luau/vm/vm.run.js +842 -0
- package/script/luau/vm/vm.value.js +406 -0
- package/script/play/play.js +199 -0
- package/script/play/play.objects.js +295 -0
- package/script/play/play.scope.js +91 -0
- package/script/rive/api/api.statics.js +31 -0
- package/script/rive/api/api.type.js +188 -0
- package/script/rive/api/artboard/artboard.js +89 -0
- package/script/rive/api/assets/assets.js +72 -0
- package/script/rive/api/data/data.value.js +99 -0
- package/script/rive/api/draw/draw.contours.js +163 -0
- package/script/rive/api/draw/draw.measure.js +196 -0
- package/script/rive/api/draw/draw.paint.js +235 -0
- package/script/rive/api/draw/draw.path.js +153 -0
- package/script/rive/api/draw/draw.renderer.js +117 -0
- package/script/rive/api/math/math.color.js +96 -0
- package/script/rive/api/math/math.mat2d.js +180 -0
- package/script/rive/api/math/math.vector.js +121 -0
- package/script/rive/api/media/media.audio.js +113 -0
- package/script/rive/api/media/media.draw.js +73 -0
- package/script/rive/api/media/media.image.js +61 -0
- package/script/rive/api/media/media.mesh.js +50 -0
- package/script/rive/api/pointer/pointer.event.js +64 -0
- package/script/rive/api/pointer/pointer.route.js +69 -0
- package/script/rive/api/text/text.font.js +129 -0
- package/script/rive/api/text/text.js +278 -0
- package/script/rive/api/text/text.layout.js +259 -0
- package/script/rive/context/context.js +76 -0
- package/script/rive/context/context.memory.js +196 -0
- package/script/rive/context/context.viewmodel.js +302 -0
- package/script/rive/context/lists/lists.js +160 -0
- package/script/rive/context/lists/lists.values.js +281 -0
- package/script/rive/host/host.globals.js +44 -0
- package/script/rive/host/host.js +533 -0
- package/script/rive/host/host.node.js +45 -0
- package/script/rive/rive.js +23 -0
- package/script/rive/rive.md +180 -0
- package/script/script.js +11 -0
- package/skin/skin.deform.js +514 -0
- package/skin/skin.ik.js +260 -0
- package/skin/skin.js +297 -0
- package/README.md +0 -3
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The state machine engine for dotLottie machines, as Roku's Motion runs it
|
|
3
|
+
* (runtime/roku/components/motion/state/state.bs): it answers "which state
|
|
4
|
+
* next?" and runs actions against a values table. It holds no state of its
|
|
5
|
+
* own; the flow owns the current state, the values and the pending events.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {{ initial: string, states: any[], inputs?: any[], interactions?: any[], layers?: any[] }} Graph
|
|
10
|
+
* @typedef {Record<string, any>} Values input name → value
|
|
11
|
+
* @typedef {Record<string, boolean>} Events pending events, name → true
|
|
12
|
+
* @typedef {{ changed: boolean, events: string[], focus: any[] }} Effects `focus`: the focus actions (FocusTarget, FocusMove, FocusClear) to run once the values are set
|
|
13
|
+
* @typedef {{ elapsed: number, length: number }} Clock how long the current state has played, and how long its timeline is, in ms
|
|
14
|
+
* @typedef {import('../script/play/play.js').Scripts} Scripts the scripts a bundle runs; without them a Script guard fails and a RunScript action does nothing
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {any} graph
|
|
19
|
+
* @returns {boolean}
|
|
20
|
+
*/
|
|
21
|
+
export function exists(graph) {
|
|
22
|
+
return !!graph && Array.isArray(graph.states) && typeof graph.initial === 'string'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {Graph} graph
|
|
27
|
+
* @param {string} name
|
|
28
|
+
*/
|
|
29
|
+
export function find(graph, name) {
|
|
30
|
+
for (let n = 0; n < graph.states.length; n++) if (graph.states[n].name === name) return graph.states[n]
|
|
31
|
+
return null
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The graphs of a machine's layers, each with the machine's inputs and
|
|
36
|
+
* interactions, which layers share. A machine without `layers` is one layer.
|
|
37
|
+
*
|
|
38
|
+
* @param {Graph} graph
|
|
39
|
+
* @returns {Graph[]}
|
|
40
|
+
*/
|
|
41
|
+
export function layersOf(graph) {
|
|
42
|
+
const layers = graph.layers
|
|
43
|
+
if (!layers || layers.length < 2) return [ graph ]
|
|
44
|
+
return layers.map((/** @type {any} */ layer, /** @type {number} */ n) => (n === 0 ? graph : { initial: layer.initial, states: layer.states, inputs: graph.inputs, interactions: graph.interactions }))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Every input's authored value; events have none.
|
|
49
|
+
*
|
|
50
|
+
* @param {Graph} graph
|
|
51
|
+
* @returns {Values}
|
|
52
|
+
*/
|
|
53
|
+
export function defaults(graph) {
|
|
54
|
+
/** @type {Values} */
|
|
55
|
+
const values = {}
|
|
56
|
+
const inputs = graph.inputs || []
|
|
57
|
+
for (let n = 0; n < inputs.length; n++) if (inputs[n].type !== 'Event') values[inputs[n].name] = inputs[n].value
|
|
58
|
+
return values
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @param {Graph} graph
|
|
63
|
+
* @param {string} name
|
|
64
|
+
*/
|
|
65
|
+
export function isEvent(graph, name) {
|
|
66
|
+
const inputs = graph.inputs || []
|
|
67
|
+
for (let n = 0; n < inputs.length; n++) if (inputs[n].name === name) return inputs[n].type === 'Event'
|
|
68
|
+
return false
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Transitions
|
|
72
|
+
// ■■■■■■■■■■■
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Global states' transitions are checked first, then the current state's;
|
|
76
|
+
* the first whose guards all pass wins. A transition back into the current
|
|
77
|
+
* state from a global one is skipped, or it would fire forever.
|
|
78
|
+
*
|
|
79
|
+
* @param {Graph} graph
|
|
80
|
+
* @param {string} current
|
|
81
|
+
* @param {Values} values
|
|
82
|
+
* @param {Events} events
|
|
83
|
+
* @param {Clock | null} [clock] the current state's time, for exit times; without it exit times are not checked
|
|
84
|
+
* @param {Scripts | null} [scripts]
|
|
85
|
+
* @returns {string} the next state, or ""
|
|
86
|
+
*/
|
|
87
|
+
export function nextState(graph, current, values, events, clock, scripts) {
|
|
88
|
+
const transition = takeTransition(graph, current, values, events, clock || null, scripts || null)
|
|
89
|
+
return transition ? transition.toState : ''
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* The transition `nextState` takes, whole: its `durationMs` is how long the
|
|
94
|
+
* outgoing and incoming states cross-fade.
|
|
95
|
+
*
|
|
96
|
+
* @param {Graph} graph
|
|
97
|
+
* @param {string} current
|
|
98
|
+
* @param {Values} values
|
|
99
|
+
* @param {Events} events
|
|
100
|
+
* @param {Clock | null} clock
|
|
101
|
+
* @param {Scripts | null} [scripts]
|
|
102
|
+
* @returns {any} the transition, or null
|
|
103
|
+
*/
|
|
104
|
+
export function takeTransition(graph, current, values, events, clock, scripts) {
|
|
105
|
+
for (let n = 0; n < graph.states.length; n++) {
|
|
106
|
+
const state = graph.states[n]
|
|
107
|
+
if (state.type !== 'GlobalState') continue
|
|
108
|
+
const transition = firstPassing(state.transitions, values, events, current, clock, scripts || null)
|
|
109
|
+
if (transition) return transition
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const state = find(graph, current)
|
|
113
|
+
return state ? firstPassing(state.transitions, values, events, '', clock, scripts || null) : null
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* When a transition's exit time is reached, in ms into its state.
|
|
118
|
+
*
|
|
119
|
+
* @param {any} transition
|
|
120
|
+
* @param {number} length the state's timeline, in ms
|
|
121
|
+
* @returns {number} 0 for a transition without an exit time
|
|
122
|
+
*/
|
|
123
|
+
export function exitMs(transition, length) {
|
|
124
|
+
const exit = transition.exitTime
|
|
125
|
+
if (!exit) return 0
|
|
126
|
+
return typeof exit.ms === 'number' ? exit.ms : (exit.fraction || 0) * length
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* The earliest exit time among a state's transitions after `after`, in ms.
|
|
131
|
+
*
|
|
132
|
+
* @param {any} state
|
|
133
|
+
* @param {number} length the state's timeline, in ms
|
|
134
|
+
* @param {number} after an exit time already reached, or -1
|
|
135
|
+
* @returns {number} -1 when no exit time is left
|
|
136
|
+
*/
|
|
137
|
+
export function nextExit(state, length, after) {
|
|
138
|
+
let next = -1
|
|
139
|
+
const list = (state && state.transitions) || []
|
|
140
|
+
for (let n = 0; n < list.length; n++) {
|
|
141
|
+
if (!list[n].exitTime) continue
|
|
142
|
+
const at = exitMs(list[n], length)
|
|
143
|
+
if (at > after && (next < 0 || at < next)) next = at
|
|
144
|
+
}
|
|
145
|
+
return next
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* @param {any[]} transitions
|
|
150
|
+
* @param {Values} values
|
|
151
|
+
* @param {Events} events
|
|
152
|
+
* @param {string} skip a target to pass over, or ""
|
|
153
|
+
* @param {Clock | null} clock
|
|
154
|
+
* @param {Scripts | null} scripts
|
|
155
|
+
*/
|
|
156
|
+
function firstPassing(transitions, values, events, skip, clock, scripts) {
|
|
157
|
+
const list = transitions || []
|
|
158
|
+
for (let n = 0; n < list.length; n++) {
|
|
159
|
+
const transition = list[n]
|
|
160
|
+
if (transition.toState === skip) continue
|
|
161
|
+
|
|
162
|
+
// with a clock, an exit time is checked exactly and replaces the guard that waits for the whole timeline
|
|
163
|
+
const isTimed = !!clock && !!transition.exitTime
|
|
164
|
+
if (isTimed && clock.elapsed < exitMs(transition, clock.length)) continue
|
|
165
|
+
if (guardsPass(transition.guards, values, events, isTimed, scripts)) return transition
|
|
166
|
+
}
|
|
167
|
+
return null
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* @param {any[]} guards no guards always pass
|
|
172
|
+
* @param {Values} values
|
|
173
|
+
* @param {Events} events
|
|
174
|
+
* @param {boolean} isTimed the transition's exit time is checked apart; its `exit` guard is skipped
|
|
175
|
+
* @param {Scripts | null} scripts
|
|
176
|
+
*/
|
|
177
|
+
function guardsPass(guards, values, events, isTimed, scripts) {
|
|
178
|
+
const list = guards || []
|
|
179
|
+
for (let n = 0; n < list.length; n++) {
|
|
180
|
+
if (isTimed && list[n].exit === true) continue
|
|
181
|
+
if (!guardPasses(list[n], values, events, scripts)) return false
|
|
182
|
+
}
|
|
183
|
+
return true
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* A guard compares only values of its own type, as the Roku engine must.
|
|
188
|
+
*
|
|
189
|
+
* @param {any} guard
|
|
190
|
+
* @param {Values} values
|
|
191
|
+
* @param {Events} events
|
|
192
|
+
* @param {Scripts | null} scripts
|
|
193
|
+
*/
|
|
194
|
+
function guardPasses(guard, values, events, scripts) {
|
|
195
|
+
if (guard.type === 'Event') return events[guard.inputName] === true
|
|
196
|
+
// a scripted condition: the script says; with no way to run it, it does not pass
|
|
197
|
+
if (guard.type === 'Script') return !!scripts && scripts.evaluate(guard, values)
|
|
198
|
+
|
|
199
|
+
const value = values[guard.inputName]
|
|
200
|
+
const expected = resolve(guard.compareTo, values)
|
|
201
|
+
const condition = guard.conditionType
|
|
202
|
+
|
|
203
|
+
const isNumeric = guard.type === 'Numeric' && typeof value === 'number' && typeof expected === 'number'
|
|
204
|
+
if (isNumeric) return compare(value, condition, expected)
|
|
205
|
+
|
|
206
|
+
const isBoolean = guard.type === 'Boolean' && typeof value === 'boolean' && typeof expected === 'boolean'
|
|
207
|
+
const isString = guard.type === 'String' && typeof value === 'string' && typeof expected === 'string'
|
|
208
|
+
if (!isBoolean && !isString) return false
|
|
209
|
+
|
|
210
|
+
return condition === 'NotEqual' ? value !== expected : value === expected
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* @param {number} a
|
|
215
|
+
* @param {string} condition
|
|
216
|
+
* @param {number} b
|
|
217
|
+
*/
|
|
218
|
+
function compare(a, condition, b) {
|
|
219
|
+
switch (condition) {
|
|
220
|
+
case 'NotEqual': return a !== b
|
|
221
|
+
case 'GreaterThan': return a > b
|
|
222
|
+
case 'GreaterThanOrEqual': return a >= b
|
|
223
|
+
case 'LessThan': return a < b
|
|
224
|
+
case 'LessThanOrEqual': return a <= b
|
|
225
|
+
default: return a === b
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Actions
|
|
230
|
+
// ■■■■■■■
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Runs actions against the values. SetFrame, SetProgress and SetTheme have
|
|
234
|
+
* nothing to act on here and are skipped, as on Roku.
|
|
235
|
+
*
|
|
236
|
+
* @param {any[]} actions
|
|
237
|
+
* @param {Graph} graph
|
|
238
|
+
* @param {Values} values updated in place
|
|
239
|
+
* @param {Events} events updated in place: Fire adds a pending event
|
|
240
|
+
* @param {Scripts | null} [scripts] runs a RunScript action
|
|
241
|
+
* @param {string} [trigger] the interaction the actions run for, which a script's pointer event is of
|
|
242
|
+
* @returns {Effects}
|
|
243
|
+
*/
|
|
244
|
+
export function perform(actions, graph, values, events, scripts, trigger) {
|
|
245
|
+
/** @type {Effects} */
|
|
246
|
+
const effects = { changed: false, events: [], focus: [] }
|
|
247
|
+
const list = actions || []
|
|
248
|
+
for (let n = 0; n < list.length; n++) runOne(list[n], graph, values, events, effects, scripts || null, trigger || '')
|
|
249
|
+
return effects
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* @param {any} action
|
|
254
|
+
* @param {Graph} graph
|
|
255
|
+
* @param {Values} values
|
|
256
|
+
* @param {Events} events
|
|
257
|
+
* @param {Effects} effects
|
|
258
|
+
* @param {Scripts | null} scripts
|
|
259
|
+
* @param {string} trigger
|
|
260
|
+
*/
|
|
261
|
+
function runOne(action, graph, values, events, effects, scripts, trigger) {
|
|
262
|
+
const name = action.inputName
|
|
263
|
+
const current = values[name]
|
|
264
|
+
switch (action.type) {
|
|
265
|
+
case 'Fire':
|
|
266
|
+
events[name] = true
|
|
267
|
+
break
|
|
268
|
+
case 'FireCustomEvent':
|
|
269
|
+
effects.events.push(action.value)
|
|
270
|
+
break
|
|
271
|
+
case 'FocusTarget':
|
|
272
|
+
case 'FocusMove':
|
|
273
|
+
case 'FocusClear':
|
|
274
|
+
effects.focus.push(action)
|
|
275
|
+
break
|
|
276
|
+
case 'OpenUrl':
|
|
277
|
+
effects.events.push('openUrl:' + action.url)
|
|
278
|
+
break
|
|
279
|
+
case 'RunScript':
|
|
280
|
+
if (scripts && scripts.perform(action, trigger, values, events)) effects.changed = true
|
|
281
|
+
break
|
|
282
|
+
case 'Toggle':
|
|
283
|
+
if (typeof current === 'boolean') setValue(values, name, !current, effects)
|
|
284
|
+
break
|
|
285
|
+
case 'Increment':
|
|
286
|
+
case 'Decrement':
|
|
287
|
+
if (typeof current === 'number') setValue(values, name, current + stepSize(action, values), effects)
|
|
288
|
+
break
|
|
289
|
+
case 'Reset':
|
|
290
|
+
setValue(values, name, defaults(graph)[name], effects)
|
|
291
|
+
break
|
|
292
|
+
case 'SetBoolean':
|
|
293
|
+
case 'SetNumeric':
|
|
294
|
+
case 'SetString':
|
|
295
|
+
setValue(values, name, resolve(action.value, values), effects)
|
|
296
|
+
break
|
|
297
|
+
default:
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* The action's value, or 1 when it names no number; negative to decrement.
|
|
303
|
+
*
|
|
304
|
+
* @param {any} action
|
|
305
|
+
* @param {Values} values
|
|
306
|
+
*/
|
|
307
|
+
function stepSize(action, values) {
|
|
308
|
+
const size = resolve(action.value, values)
|
|
309
|
+
const step = typeof size === 'number' ? size : 1
|
|
310
|
+
return action.type === 'Decrement' ? -step : step
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* A literal, or "$name" naming another input.
|
|
315
|
+
*
|
|
316
|
+
* @param {any} value
|
|
317
|
+
* @param {Values} values
|
|
318
|
+
*/
|
|
319
|
+
function resolve(value, values) {
|
|
320
|
+
return typeof value === 'string' && value.charAt(0) === '$' ? values[value.slice(1)] : value
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* @param {Values} values
|
|
325
|
+
* @param {string} name
|
|
326
|
+
* @param {any} value
|
|
327
|
+
* @param {Effects} effects
|
|
328
|
+
*/
|
|
329
|
+
function setValue(values, name, value, effects) {
|
|
330
|
+
if (value === undefined || value === null) return
|
|
331
|
+
if (values[name] === value) return
|
|
332
|
+
values[name] = value
|
|
333
|
+
effects.changed = true
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Interactions
|
|
337
|
+
// ■■■■■■■■■■■■
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Every action the matching interactions run, in order.
|
|
341
|
+
*
|
|
342
|
+
* @param {Graph} graph
|
|
343
|
+
* @param {string} kind "Click", "OnComplete", or a pointer interaction ("PointerEnter", ...)
|
|
344
|
+
* @param {string} stateName for lifecycle interactions; "" matches any
|
|
345
|
+
* @param {string} [target] a pointer interaction's target `rid`; "" for those without a target; omitted matches any
|
|
346
|
+
* @returns {any[]}
|
|
347
|
+
*/
|
|
348
|
+
export function interactionActions(graph, kind, stateName, target) {
|
|
349
|
+
const actions = []
|
|
350
|
+
const list = graph.interactions || []
|
|
351
|
+
for (let n = 0; n < list.length; n++) {
|
|
352
|
+
const interaction = list[n]
|
|
353
|
+
const isStateMatch = stateName === '' || typeof interaction.stateName !== 'string' || interaction.stateName === stateName
|
|
354
|
+
const isTargetMatch = target === undefined || (interaction.target ? interaction.target.rid : '') === target
|
|
355
|
+
if (interaction.type === kind && isStateMatch && isTargetMatch) for (let k = 0; k < interaction.actions.length; k++) actions.push(interaction.actions[k])
|
|
356
|
+
}
|
|
357
|
+
return actions
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/** The interactions a pointer sets off */
|
|
361
|
+
const POINTER_TYPES = [ 'Click', 'PointerDown', 'PointerUp', 'PointerEnter', 'PointerExit', 'PointerMove' ]
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Where the pointer interactions listen: each target's `rid`, or "" for the
|
|
365
|
+
* listeners without one, which watch the whole artboard, and the types on it.
|
|
366
|
+
*
|
|
367
|
+
* @param {Graph} graph
|
|
368
|
+
* @returns {Record<string, string[]>}
|
|
369
|
+
*/
|
|
370
|
+
export function pointerTargetsOf(graph) {
|
|
371
|
+
/** @type {Record<string, string[]>} */
|
|
372
|
+
const found = {}
|
|
373
|
+
const list = graph.interactions || []
|
|
374
|
+
for (let n = 0; n < list.length; n++) {
|
|
375
|
+
const interaction = list[n]
|
|
376
|
+
if (POINTER_TYPES.indexOf(interaction.type) < 0) continue
|
|
377
|
+
const rid = interaction.target ? interaction.target.rid : ''
|
|
378
|
+
if (!found[rid]) found[rid] = []
|
|
379
|
+
if (found[rid].indexOf(interaction.type) < 0) found[rid].push(interaction.type)
|
|
380
|
+
}
|
|
381
|
+
return found
|
|
382
|
+
}
|