diff options
Diffstat (limited to 'packages/gitbook-core')
-rw-r--r-- | packages/gitbook-core/src/InjectedComponent.js | 9 | ||||
-rw-r--r-- | packages/gitbook-core/src/InjectedComponentSet.js | 9 | ||||
-rw-r--r-- | packages/gitbook-core/src/components/InjectedComponent.js | 91 | ||||
-rw-r--r-- | packages/gitbook-core/src/components/UnsafeComponent.js | 83 | ||||
-rw-r--r-- | packages/gitbook-core/src/index.js | 3 |
5 files changed, 175 insertions, 20 deletions
diff --git a/packages/gitbook-core/src/InjectedComponent.js b/packages/gitbook-core/src/InjectedComponent.js deleted file mode 100644 index 1a2f1df..0000000 --- a/packages/gitbook-core/src/InjectedComponent.js +++ /dev/null @@ -1,9 +0,0 @@ -const React = require('react'); - -const InjectedComponent = React.createClass({ - render() { - - } -}); - -module.exports = InjectedComponent; diff --git a/packages/gitbook-core/src/InjectedComponentSet.js b/packages/gitbook-core/src/InjectedComponentSet.js deleted file mode 100644 index 4269b73..0000000 --- a/packages/gitbook-core/src/InjectedComponentSet.js +++ /dev/null @@ -1,9 +0,0 @@ -const React = require('react'); - -const InjectedComponentSet = React.createClass({ - render() { - - } -}); - -module.exports = InjectedComponentSet; diff --git a/packages/gitbook-core/src/components/InjectedComponent.js b/packages/gitbook-core/src/components/InjectedComponent.js new file mode 100644 index 0000000..fe386ef --- /dev/null +++ b/packages/gitbook-core/src/components/InjectedComponent.js @@ -0,0 +1,91 @@ +const React = require('react'); +const connect = require('./connect'); + +const UnsafeComponent = require('./UnsafeComponent'); + +/* + Public: InjectedComponent makes it easy to include a set of dynamically registered + components inside of your React render method. Rather than explicitly render + an array of buttons, for example, you can use InjectedComponentSet: + + ```js + <InjectedComponentSet className="message-actions" + matching={{role: 'ThreadActionButton'}} + props={{ a: 1 }}> + ``` + + InjectedComponentSet will look up components registered for the location you provide, + render them inside a {Flexbox} and pass them `exposedProps`. By default, all injected + children are rendered inside {UnsafeComponent} wrappers to prevent third-party code + from throwing exceptions that break React renders. + + InjectedComponentSet monitors the ComponentStore for changes. If a new component + is registered into the location you provide, InjectedComponentSet will re-render. + If no matching components is found, the InjectedComponent renders an empty span. + */ + + +const InjectedComponentSet = React.createClass({ + propTypes: { + components: React.PropTypes.arrayOf(React.PropTypes.func).isRequired, + props: React.PropTypes.object, + withContainer: React.PropTypes.bool + }, + + render() { + const { components, props, ...divProps } = this.props; + + const inner = components.map(Component => { + if (Component.sandbox === false) { + return <Component key={Component.displayName} {...props} />; + } else { + return <UnsafeComponent key={Component.displayName} Component={Component} props={props} />; + } + }); + + return ( + <div {...divProps}> + {inner} + </div> + ); + } +}); + +/** + * Find all components matching a descriptor + * @param {List<ComponentDescriptor>} components + * @param {String} matching.role + */ +function findMatchingComponents(components, matching) { + return components + .filter(({descriptor}) => { + if (matching.role && matching.role === descriptor.role) { + return false; + } + + return true; + }) + .map(component => component.Component); +} + +/** + * Map Redux state to InjectedComponentSet's props + */ +function mapStateToProps(state, props) { + const { components } = state; + const { matching } = props; + + return { + components: findMatchingComponents(components, matching) + }; +} + +module.exports = { + InjectedComponent: connect(InjectedComponentSet, (state, props) => { + const result = mapStateToProps(state, props); + result.components = result.components.slice(0, 1); + result.withContainer = false; + return result; + }), + InjectedComponentSet: connect(InjectedComponentSet, mapStateToProps) +}; diff --git a/packages/gitbook-core/src/components/UnsafeComponent.js b/packages/gitbook-core/src/components/UnsafeComponent.js new file mode 100644 index 0000000..de62471 --- /dev/null +++ b/packages/gitbook-core/src/components/UnsafeComponent.js @@ -0,0 +1,83 @@ +/* eslint-disable no-console */ +const React = require('react'); +const ReactDOM = require('react-dom'); +const ReactRedux = require('react-redux'); + +/* + Public: Renders a component provided via the `component` prop, and ensures that + failures in the component's code do not cause state inconsistencies elsewhere in + the application. This component is used by {InjectedComponent} and + {InjectedComponentSet} to isolate third party code that could be buggy. + + Occasionally, having your component wrapped in {UnsafeComponent} can cause style + issues. For example, in a Flexbox, the `div.unsafe-component-wrapper` will cause + your `flex` and `order` values to be one level too deep. For these scenarios, + UnsafeComponent looks for `containerStyles` on your React component and attaches + them to the wrapper div. + */ + + +const UnsafeComponent = React.createClass({ + propTypes: { + Component: React.PropTypes.func.isRequired, + props: React.PropTypes.object + }, + + componentDidMount() { + return this.renderInjected(); + }, + + componentDidUpdate() { + return this.renderInjected(); + }, + + componentWillUnmount() { + return this.unmountInjected(); + }, + + renderInjected() { + const { Component, props } = this.props; + const { store } = this.context; + + const node = ReactDOM.findDOMNode(this); + + try { + this.injected = ( + <ReactRedux.Provider store={store}> + <Component {...props}/> + </ReactRedux.Provider> + ); + + ReactDOM.render(this.injected, node); + } catch (err) { + console.error(err); + } + }, + + unmountInjected() { + try { + const node = ReactDOM.findDOMNode(this); + return ReactDOM.unmountComponentAtNode(node); + } catch (err) { + console.error(err); + } + }, + + focus() { + if (this.injected.focus != null) { + return this.injected.focus(); + } + }, + + blur() { + if (this.injected.blur != null) { + return this.injected.blur(); + } + }, + + render() { + return <div name="unsafe-component-wrapper" />; + } +}); + +module.exports = ReactRedux.connect(UnsafeComponent); diff --git a/packages/gitbook-core/src/index.js b/packages/gitbook-core/src/index.js index bd9124b..5059463 100644 --- a/packages/gitbook-core/src/index.js +++ b/packages/gitbook-core/src/index.js @@ -1,10 +1,9 @@ const ACTIONS = require('./actions/TYPES'); const connect = require('./connect'); -const InjectedComponent = require('./InjectedComponent'); -const InjectedComponentSet = require('./InjectedComponentSet'); const createPlugin = require('./createPlugin'); const createReducer = require('./createReducer'); const createStore = require('./createStore'); +const { InjectedComponent, InjectedComponentSet } = require('./components/InjectedComponent'); module.exports = { ACTIONS, |