UNPKG

oo7-react

Version:
554 lines (466 loc) 15.9 kB
'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var React = require('react'); var _require = require('oo7'), Bond = _require.Bond, TimeBond = _require.TimeBond, ReactiveBond = _require.ReactiveBond, TransformBond = _require.TransformBond; /** * React element in which app should be placed if it needs to wait for the parent * frame to inject the BondCache. */ var InjectedCacheWaiter = function (_React$Component) { _inherits(InjectedCacheWaiter, _React$Component); function InjectedCacheWaiter() { _classCallCheck(this, InjectedCacheWaiter); var _this = _possibleConstructorReturn(this, (InjectedCacheWaiter.__proto__ || Object.getPrototypeOf(InjectedCacheWaiter)).call(this)); _this.state = { haveCache: window ? window.injectedBondCache ? true : null : false }; if (_this.state.haveCache === null) { _this._timers = [window.setInterval(_this.checkInject.bind(_this), 100), window.setInterval(_this.checksTimeout.bind(_this), 2000)]; } return _this; } _createClass(InjectedCacheWaiter, [{ key: 'checkInject', value: function checkInject() { if (window.injectedBondCache) { Bond.cache = window.injectedBondCache; this._timers.forEach(window.clearInterval); this.setState({ haveCache: true }); } } }, { key: 'checksTimeout', value: function checksTimeout() { this._timers.forEach(window.clearInterval); this.setState({ haveCache: false }); } }, { key: 'render', value: function render() { return this.state.haveCache === null ? React.createElement( 'div', null, 'Waiting for cache...' ) : this.props.children; } }]); return InjectedCacheWaiter; }(React.Component); /** * @summary A derivable class for creating React components that can transparently * accept deal with prop values that are {@link Bond}s. * * This class is almost exactly equivalent to the basic {React.Component} class: * You can subclass it, just as you would with the basic {React.Component}, to * create new React-framework components. However, it provides awareness for * prop values provided that are {@link Bond}s. In the case of a {@link Bond} * prop, then the `state` of the object (specifically the field in `state` with the * same name as the prop) is kept up to date with the representative * value of the prop's {@link Bond}. * * The props that are {@link Bond}-aware must be enumerated at construction. Props * not named there will just pass the {@link Bond} object through transparently. * * In addition to the normal {ReactiveComponent#render} function which can be used * normally, there are also {ReactiveComponent#readyRender} and {ReactiveComponent#unreadyRender}, * which allow different render functions to be given depending on whether all * {@link Bond}-based props are considered _ready_. {ReactiveComponent#unreadyRender} has * a default render function, so you may typically implement just {ReactiveComponent#readyRender}. * * The {ReactiveComponent#ready} function is provided for determining whether all * {@link Bond}-based props are considered _ready_. * * If you override the functions {ReactiveComponent.componentWillMount}, * {ReactiveComponent.componentWillUnmount} or {ReactiveComponent.receiveProps}, ensure * you first call the superclass implementation. */ var ReactiveComponent = function (_React$Component2) { _inherits(ReactiveComponent, _React$Component2); /** * Construct an instance of this class. * * @param {array} reactiveProps - The names of each prop for which a corresponding * key/value in `this.state` should be maintained for its representative value. * @param {object} bonds - An object defining the {@link Bond}s and their names * which should have state entries maintained to the current values of the * {@link Bond}s. * * @example * class Clock extends ReactiveComponent { * constructor() { super([], {time: new TimeBond}); } * readyRender() { return <span>{this.state.time.toString()}</span>; } * } */ function ReactiveComponent() { var reactiveProps = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; var bonds = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; _classCallCheck(this, ReactiveComponent); var _this2 = _possibleConstructorReturn(this, (ReactiveComponent.__proto__ || Object.getPrototypeOf(ReactiveComponent)).call(this)); _this2.reactiveProps = reactiveProps; _this2.bonds = bonds; _this2.allBondKeys = [].concat(reactiveProps).concat(Object.keys(bonds)); _this2.state = {}; return _this2; } /** * Overridden function from React.Component. * * Ensure that any further derivations of this function call this superclass * implementation. */ _createClass(ReactiveComponent, [{ key: 'componentWillMount', value: function componentWillMount() { this.initProps(); } /** * Overridden function from React.Component. * * Ensure that any further derivations of this function call this superclass * implementation. */ }, { key: 'componentWillReceiveProps', value: function componentWillReceiveProps(nextProps) { this.updateProps(nextProps); } /** * Overridden function from React.Component. * * Ensure that any further derivations of this function call this superclass * implementation. */ }, { key: 'componentWillUnmount', value: function componentWillUnmount() { this.finiProps(); } }, { key: 'initProps', value: function initProps() { this.manageProps({}, this.props); var that = this; var bonds = this.bonds; var bondKeys = Object.keys(bonds).filter(function (k) { return typeof bonds[k] !== 'function'; }); this._consolidatedExtraBonds = new ReactiveBond(bondKeys.map(function (f) { return bonds[f]; }), [], function (a) { var s = that.state || {}; bondKeys.forEach(function (f, i) { s[f] = a[i]; }); that.setState(s); }).use(); } }, { key: 'finiProps', value: function finiProps() { if (this._consolidatedExtraBonds) { var x = this._consolidatedExtraBonds; delete this._consolidatedExtraBonds; x.drop(); } if (this._consolidatedBonds) { var _x3 = this._consolidatedBonds; delete this._consolidatedBonds; _x3.drop(); } if (this._derivedBonds) { var _x4 = this._derivedBonds; delete this._derivedBonds; _x4.drop(); } } }, { key: 'updateProps', value: function updateProps(nextProps) { this.manageProps(this.props, nextProps); } }, { key: 'manageProps', value: function manageProps(props, nextProps) { var _this3 = this; var that = this; var bonds = this.bonds; var derivedBondKeys = Object.keys(bonds).filter(function (k) { return typeof bonds[k] === 'function'; }); if (this._derivedBonds) { var x = this._derivedBonds; delete this._derivedBonds; x.drop(); } if (this._consolidatedBonds) { var _x5 = this._consolidatedBonds; delete this._consolidatedBonds; _x5.drop(); } if (that.reactiveProps.length > 0) { this._consolidatedBonds = new TransformBond(function () { for (var _len = arguments.length, a = Array(_len), _key = 0; _key < _len; _key++) { a[_key] = arguments[_key]; } var s = {}; that.reactiveProps.forEach(function (f, i) { s[f] = a[i]; }); that.setState(s); return s; }, this.reactiveProps.map(function (f) { return nextProps[f]; }), []).use().subscriptable(); } if (derivedBondKeys.length > 0) { this._derivedBonds = new ReactiveBond(derivedBondKeys.map(function (f) { return bonds[f](_this3._consolidatedBonds); }), [], function (a) { var s = {}; derivedBondKeys.forEach(function (f, i) { return s[f] = a[i]; }); that.setState(s); }).use(); } } /** * Determine whether all props are ready. * * @returns {boolean} - `true` if and only if all props, specifically those * which are {@link Bond} values and which are {@link Bond} aware, are _ready_. */ }, { key: 'ready', value: function ready() { var _this4 = this; return this.allBondKeys.every(function (k) { return _this4.state[k] !== undefined; }); } /** * Render this object with present state and props. * * This will only be called when all {@link Bond}-aware props are _ready_ and * have a corresponding value in `this.state`. */ }, { key: 'readyRender', value: function readyRender() { return this.unreadyRender(); } /** * Render this object with present state and props. * * This will only be called when not all {@link Bond}-aware props are _ready_. */ }, { key: 'unreadyRender', value: function unreadyRender() { return React.createElement('span', null); } /** * Overridden function from React.Component. Render the object with present * state and props. */ }, { key: 'render', value: function render() { return this.ready() ? this.readyRender() : this.unreadyRender(); } }]); return ReactiveComponent; }(React.Component); /** * Simple coditional to output one item over another dependent on some condition. * * @example * class Clock extends React.Component { * constructor (someBond) { this._someBond = someBond } * render () { * return <If condition={this.someBond.ready()} * then={<Rspan>{this.someBond}</Rspan>} * else='Not ready' * /> * } * } */ var If = function (_ReactiveComponent) { _inherits(If, _ReactiveComponent); function If() { _classCallCheck(this, If); return _possibleConstructorReturn(this, (If.__proto__ || Object.getPrototypeOf(If)).call(this, ['condition'])); } _createClass(If, [{ key: 'render', value: function render() { var x = (this.state.condition ? this.props.then : this.props.else) || React.createElement('span', null); return typeof x === 'function' ? x() : x; } }]); return If; }(ReactiveComponent); /** * {@link Bond}-aware, variant of `span` component. * * `className` and `style` props, and the child, behave as expected but are * {@link Bond}-aware. * * @example * class Clock extends React.Component { * render () { return <Rspan>{(new TimeBond).map(_=>_.toString())}</Rspan>; } * } */ var Rspan = function (_ReactiveComponent2) { _inherits(Rspan, _ReactiveComponent2); function Rspan() { _classCallCheck(this, Rspan); return _possibleConstructorReturn(this, (Rspan.__proto__ || Object.getPrototypeOf(Rspan)).call(this, ['className', 'style', 'children'])); } _createClass(Rspan, [{ key: 'render', value: function render() { return React.createElement( 'span', { className: this.state.className, style: this.state.style, name: this.props.name }, this.state.children ); } }]); return Rspan; }(ReactiveComponent); /** * {@link Bond}-aware, variant of `div` component. * * `className` and `style` props, and the child, behave as expected but are * {@link Bond}-aware. * * @example * class Clock extends React.Component { * render () { return <Rdiv>{(new TimeBond).map(_=>_.toString())}</Rdiv>; } * } */ var Rdiv = function (_ReactiveComponent3) { _inherits(Rdiv, _ReactiveComponent3); function Rdiv() { _classCallCheck(this, Rdiv); return _possibleConstructorReturn(this, (Rdiv.__proto__ || Object.getPrototypeOf(Rdiv)).call(this, ['className', 'style', 'children'])); } _createClass(Rdiv, [{ key: 'render', value: function render() { return React.createElement( 'div', { className: this.state.className, style: this.state.style, name: this.props.name }, this.state.children ); } }]); return Rdiv; }(ReactiveComponent); /** * {@link Bond}-aware, variant of `a` component. * * `href`, `target`, `className` and `style` props, and the child, behave as * expected but are {@link Bond}-aware. */ var Ra = function (_ReactiveComponent4) { _inherits(Ra, _ReactiveComponent4); function Ra() { _classCallCheck(this, Ra); return _possibleConstructorReturn(this, (Ra.__proto__ || Object.getPrototypeOf(Ra)).call(this, ['href', 'target', 'className', 'style', 'children'])); } _createClass(Ra, [{ key: 'render', value: function render() { return React.createElement( 'a', { href: this.state.href, target: this.state.target, className: this.state.className, style: this.state.style, name: this.props.name }, this.state.children ); } }]); return Ra; }(ReactiveComponent); /** * {@link Bond}-aware, variant of `img` component. * * `src`, `className` and `style` props, and the child, behave as * expected but are {@link Bond}-aware. */ var Rimg = function (_ReactiveComponent5) { _inherits(Rimg, _ReactiveComponent5); function Rimg() { _classCallCheck(this, Rimg); return _possibleConstructorReturn(this, (Rimg.__proto__ || Object.getPrototypeOf(Rimg)).call(this, ['src', 'className', 'style'])); } _createClass(Rimg, [{ key: 'render', value: function render() { return React.createElement('img', { src: this.state.src, className: this.state.className, style: this.state.style, name: this.props.name }); } }]); return Rimg; }(ReactiveComponent); /** * {@link Bond}-aware component for displaying hash values. * * Hash value (encoded as hex and `0x` prefixed) should be placed in `value` prop. * * `value`, `className` and `style` props behave as expected but are {@link Bond}-aware. */ var Hash = function (_ReactiveComponent6) { _inherits(Hash, _ReactiveComponent6); function Hash() { _classCallCheck(this, Hash); return _possibleConstructorReturn(this, (Hash.__proto__ || Object.getPrototypeOf(Hash)).call(this, ['value', 'className', 'style'])); } _createClass(Hash, [{ key: 'render', value: function render() { var v = this.state.value; var d = typeof v === 'string' && v.startsWith('0x') && v.length >= 18 ? v.substr(0, 8) + '…' + v.substr(v.length - 4) : v; return React.createElement( 'span', { className: this.state.className, style: this.state.style, title: this.state.value, name: this.props.name }, d ); } }]); return Hash; }(ReactiveComponent); Hash.defaultProps = { className: '_hash' }; module.exports = { ReactiveComponent: ReactiveComponent, Rspan: Rspan, Rdiv: Rdiv, Ra: Ra, Rimg: Rimg, Hash: Hash, InjectedCacheWaiter: InjectedCacheWaiter, If: If };