From cc1be3426327d1021ee50907909326758a26c9e8 Mon Sep 17 00:00:00 2001 From: Samuel Elliott Date: Thu, 2 May 2019 18:10:39 +0100 Subject: [PATCH] Comments and fix recording selectors for some components (and add one for VueComponent) --- client/src/modules/reactcomponents.js | 41 +++++++++++++++++++++++---- client/src/ui/vueinjector.js | 31 +++++++++++++------- 2 files changed, 55 insertions(+), 17 deletions(-) diff --git a/client/src/modules/reactcomponents.js b/client/src/modules/reactcomponents.js index 51bbfb5e..001c5f60 100644 --- a/client/src/modules/reactcomponents.js +++ b/client/src/modules/reactcomponents.js @@ -190,15 +190,34 @@ class ReactComponent { } } +ReactComponent.important = Symbol('BD.ReactComponent.important'); + export class ReactComponents { + /** @type {ReactComponent[]} */ static get components() { return this._components || (this._components = []) } + + /** @type {Reflection.modules.React.Component[]} */ static get unknownComponents() { return this._unknownComponents || (this._unknownComponents = []) } + + /** @type {{id: string, listeners: function[]}[]} */ static get listeners() { return this._listeners || (this._listeners = []) } + + /** @type {<{name: string, filter: function}[]>} */ static get nameSetters() { return this._nameSetters || (this._nameSetters = []) } - static get componentAliases() { return this._componentAliases || (this._componentAliases = []) } + + /** @type {Object.} */ + static get componentAliases() { return this._componentAliases || (this._componentAliases = {}) } static get ReactComponent() { return ReactComponent } + /** + * Processes a React component. + * @param {Reflection.modules.React.Component} component The React component class + * @param {object} retVal + * @param {object} important + * @param {string} important.selector A query selector the component will render elements matching (used to select all component instances to force them to rerender) + * @return {ReactComponent} + */ static push(component, retVal, important) { if (!(component instanceof Function)) return null; const { displayName } = component; @@ -212,6 +231,8 @@ export class ReactComponents { return component; } + if (!important) important = component[ReactComponent.important]; + const c = new ReactComponent(displayName, component, retVal, important); this.components.push(c); @@ -226,16 +247,19 @@ export class ReactComponents { /** * Finds a component from the components array or by waiting for it to be mounted. - * @param {String} name The component's name - * @param {Object} important An object containing a selector to look for - * @param {Function} filter A function to filter components if a single element is rendered by multiple components - * @return {Promise => ReactComponent} + * @param {string} name The component's name + * @param {object} important An object containing a selector to look for + * @param {function} filter A function to filter components if a single element is rendered by multiple components + * @return {Promise} */ static async getComponent(name, important, filter) { name = this.getComponentName(name); const have = this.components.find(c => c.id === name); - if (have) return have; + if (have) { + if (!have.important) have.important = important; + return have; + } if (important) { const callback = () => { @@ -321,6 +345,11 @@ export class ReactComponents { return this.nameSetters.push({ name, filter }); } + /** + * Processes a React component that isn't known. + * @param {Reflection.modules.React.Component} component + * @param {} retVal + */ static processUnknown(component, retVal) { for (const [fi, filter] of this.nameSetters.entries()) { if (filter.filter.filter(component)) { diff --git a/client/src/ui/vueinjector.js b/client/src/ui/vueinjector.js index 4a94b541..3d54c202 100644 --- a/client/src/ui/vueinjector.js +++ b/client/src/ui/vueinjector.js @@ -8,7 +8,7 @@ * LICENSE file in the root directory of this source tree. */ -import { Reflection } from 'modules'; +import { Reflection, ReactComponents } from 'modules'; import Vue from 'vue'; export default class { @@ -16,12 +16,12 @@ export default class { /** * Creates a new Vue object and mounts it in the passed element. * @param {HTMLElement} root The element to mount the new Vue object at - * @param {Object} options Options to pass to Vue + * @param {Object} options Options to pass to Vue (see https://vuejs.org/v2/api/#Options-Data) * @param {BdNode} bdnode The element to append * @return {Vue} */ static inject(root, options, bdnode) { - if(bdnode) bdnode.appendTo(root); + if (bdnode) bdnode.appendTo(root); const vue = new Vue(options); @@ -37,18 +37,18 @@ export default class { * @return {React.Element} */ static createReactElement(component, props, mountAtTop) { - const { React } = Reflection.modules; - return React.createElement(this.ReactCompatibility, {component, mountAtTop, props}); + return Reflection.modules.React.createElement(this.ReactCompatibility, {component, mountAtTop, props}); } static get ReactCompatibility() { - if (this._ReactCompatibility) return this._ReactCompatibility; + const { React, ReactDOM } = Reflection.modules; - const { React, ReactDOM} = Reflection.modules; - - return this._ReactCompatibility = class VueComponent extends React.Component { + /** + * A React component that renders a Vue component. + */ + const ReactCompatibility = class VueComponent extends React.Component { render() { - return React.createElement('span'); + return React.createElement('span', {className: 'bd-reactVueComponent'}); } componentDidMount() { @@ -89,7 +89,13 @@ export default class { } })); } - } + }; + + // Add a name for ReactComponents + ReactCompatibility.displayName = 'BD.VueComponent'; + ReactCompatibility[ReactComponents.ReactComponent.important] = {selector: '.bd-reactVueComponent'}; + + return Object.defineProperty(this, 'ReactCompatibility', {value: ReactCompatibility}).ReactCompatibility; } static install(Vue) { @@ -98,6 +104,9 @@ export default class { } +/** + * A Vue component that renders a React component. + */ export const ReactComponent = { props: ['component', 'component-props', 'component-children', 'react-element'], render(createElement) {