UNPKG

awv3

Version:
184 lines (167 loc) 7.17 kB
import * as THREE from 'three' import Lifecycle from './lifecycle.js' import { halt } from '../core/error' import { actions } from './store/plugins' import { actions as elementActions, base as elementsBase } from './store/elements' import { actions as globalActions } from './store/globals' import { actions as connectionActions, base as connectionBase } from './store/connections' import { base as pluginBase } from './store/plugins' import { createObserver } from './helpers' import Element from './element' import { Group, Button, Spacer, Divider } from './elements' import { prepare, render, destroy } from './renderer' export default class Plugin extends Lifecycle() { static persistent = false static measurable = false constructor(session = halt('plugin must be initialized with a session'), { feature, connection, pool, ...props }) { super(session, actions, state => state.plugins[this.id], { type: '', name: '', title: '', resources: {}, icon: undefined, enabled: false, active: false, collapsed: true, closeable: true, parent: undefined, elements: [], feature: feature ? feature.id : false, connection: connection ? connection.id : undefined, managed: true, ...props, }) // If a parent exists (which mean this is a linked plugin, invoked by a parent plugin) if (this.parent) { let parentPlugin = pluginBase.references[this.parent] // And if the parent is a feature, it gets to store its own pool under the parent, while // it is allowed to pose as a feature plugin itself if (parentPlugin.feature) { feature = parentPlugin connection = parentPlugin.connection this.pool = new THREE.Group() this.pool.updateParentMaterials = false this.pool.measurable = false this.pool.name = `plugin.linked_pool.${this.type}${this.name ? '+' + this.name : ''}` feature.pool.add(this.pool) } } if (feature) { Object.defineProperty(this, 'feature', { get: () => feature.id }) Object.defineProperty(this, 'connection', { get: () => connection }) if (!this.parent) { this.pool = pool this.reset = new Button(this, { name: 'Reset', flex: 0 }) this.apply = new Button(this, { name: 'Apply', flex: 0 }) this.addElement( new Group(this, { index: 10000000, children: [ new Divider(this), new Spacer(this), new Group(this, { format: Group.Format.Rows, flexDirection: 'row-reverse', children: [this.apply, new Spacer(this), this.reset], }), ], }), ) } } else { Object.defineProperty(this, 'connection', { get: () => connectionBase.references[this.session.globals.activeConnection], }) this.pool = new THREE.Group() this.pool.updateParentMaterials = false this.pool.measurable = false this.pool.name = `plugin.pool.${this.type}${this.name ? '+' + this.name : ''}` this.session.pool.temporary.add(this.pool) } // this.tree refers to the connections tree Object.defineProperty(this, 'tree', { get: () => this.connection.tree }) Object.defineProperty(this, 'root', { get: () => this.tree[this.tree.root] }) Object.defineProperty(this, 'view', { get: () => this.pool.view }) // Internal observer for enabled state, gets cleared after destroy // The enabled flag has to be kept locally as the destroyed event can be dispatched // without calling destroy() directly, leaving the linked state empty this.__enabled = this.enabled this.__enabledUnsubscribe = this.observe( // selector state => state.enabled, // onChanged (enabled, prev) => { this.__enabled = enabled if (enabled !== prev) { if (enabled) { this.onEnabled() if (this.render) render(this, this.render.bind(this)) } else { this.onDisabled() if (this.render) destroy(this.id) } } !enabled && this.removeSubscriptions() }, // options { manager: undefined }, ) } findElement(condition, elements = this.elements) { for (let key of elements) { const el = this.session.elements[key] const test = el.plugin === this.id && condition(el) if (test) { return el.id } else if (el.children.length) { return this.findElement(condition, el.children) } } } findElementClass(condition) { return elementsBase.references[this.findElement(condition)] } addElement(...elements) { elements.forEach(element => this.store.dispatch(actions.addElement(this.id, element instanceof Element ? element.id : element)), ) } removeElement(element) { this.store.dispatch(actions.removeElement(this.id, element instanceof Element ? element.id : element)) } destroyElements() { this.store.dispatch( elementActions.unregister( Object.keys(this.session.elements).filter(id => this.session.elements[id].plugin === this.id), ), ) } resetElements() { this.store.dispatch( elementActions.merge(this.dependencies.reduce((prev, obj) => ({ ...prev, [obj.id]: obj.props }), {})), ) } createToolbar(keys, cb, columns = 7) { let children = [] for (let count = 0; count < keys.length / columns; count++) { children.push( new Group(this, { format: Group.Format.Buttons, margin: count == keys.length / columns - 1, children: cb(keys.slice(count * columns, Math.min(count * columns + columns, keys.length))), }), ) } return new Group(this, { children }) } onEnabled() {} onDisabled() {} __onDestroyed() { this.store.dispatch(globalActions.unlinkPlugins(this.id)) if (this.connection) this.store.dispatch(connectionActions.unlinkPlugins(this.connection.id, this.id)) this.__enabledUnsubscribe() this.__enabled && this.onDisabled() this.render && destroy(this.id) this.pool && this.pool.destroy() } }