UNPKG

@pmndrs/handle

Version:

framework agnostic expandable handle implementation for threejs

103 lines (102 loc) 3.09 kB
import { Object3D } from 'three'; import { defaultApply } from '../store.js'; export class HandlesContext { target; getOptions; handles = []; hoveredTagMap = new Map(); hoverSubscriptions = []; applySubscriptions = []; space; constructor(target, getOptions) { this.target = target; this.getOptions = getOptions; } getSpace() { return this.space ?? 'world'; } getTarget() { if (this.target instanceof Object3D) { return this.target; } return this.target.current; } getHandleOptions(tag, getOverrideOptions) { const providedOptions = this.getOptions?.(); const overrideOptions = getOverrideOptions?.(); return { ...providedOptions, ...overrideOptions, apply: (state, target) => { this.onApply(tag, state, target); return (overrideOptions?.apply ?? providedOptions?.apply ?? defaultApply)?.(state, target); }, }; } registerHandle(store, object, tag) { const entry = { object, store, tag, }; this.handles.push(entry); const unbind = store.bind(object); const enterListener = this.onPointerEnter.bind(this, tag); const leaveListener = this.onPointerLeave.bind(this); object.addEventListener('pointerenter', enterListener); object.addEventListener('pointerleave', leaveListener); return () => { const index = this.handles.indexOf(entry); if (index != -1) { this.handles.splice(index, 1); } unbind(); store.cancel(); }; } subscribeHover(fn) { this.hoverSubscriptions.push(fn); fn(Array.from(this.hoveredTagMap.values())); return () => { const index = this.hoverSubscriptions.indexOf(fn); if (index === -1) { return; } this.hoverSubscriptions.splice(index, 1); }; } subscribeApply(fn) { this.applySubscriptions.push(fn); return () => { const index = this.applySubscriptions.indexOf(fn); if (index === -1) { return; } this.applySubscriptions.splice(index, 1); }; } update(time) { for (const { store } of this.handles) { store.update(time); } } onPointerEnter(tag, e) { this.hoveredTagMap.set(e.pointerId, tag); this.updateHover(); } onPointerLeave(e) { this.hoveredTagMap.delete(e.pointerId); this.updateHover(); } updateHover() { const tags = Array.from(this.hoveredTagMap.values()); for (const hoverSubscription of this.hoverSubscriptions) { hoverSubscription(tags); } } onApply(tag, state, target) { for (const applySubscription of this.applySubscriptions) { applySubscription(tag, state, target); } } }