summaryrefslogtreecommitdiffstats
path: root/packages/gitbook-core
diff options
context:
space:
mode:
Diffstat (limited to 'packages/gitbook-core')
-rw-r--r--packages/gitbook-core/src/InjectedComponent.js9
-rw-r--r--packages/gitbook-core/src/InjectedComponentSet.js9
-rw-r--r--packages/gitbook-core/src/components/InjectedComponent.js91
-rw-r--r--packages/gitbook-core/src/components/UnsafeComponent.js83
-rw-r--r--packages/gitbook-core/src/index.js3
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,