nestedreact
Version:
Advanced models, state management, and data binding solution for React
1,149 lines (1,117 loc) • 41.8 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('nestedtypes'), require('prop-types'), require('react-dom')) :
typeof define === 'function' && define.amd ? define(['exports', 'react', 'nestedtypes', 'prop-types', 'react-dom'], factory) :
(factory((global.ReactMVx = {}),global.React,global.Nested,global.PropTypes,global.ReactDOM));
}(this, (function (exports,React,Nested,PropTypes,ReactDOM) { 'use strict';
var Nested__default = 'default' in Nested ? Nested['default'] : Nested;
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/* global Reflect, Promise */
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
function __extends(d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}
var __assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
function __rest(s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
t[p[i]] = s[p[i]];
return t;
}
function __decorate(decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
}
/*****************
* State
*/
function process(definition, BaseComponentClass) {
var prototype = this.prototype;
var state = definition.state, State = definition.State;
if (typeof state === 'function') {
State = state;
state = void 0;
}
if (state) {
var BaseClass = State || prototype.State || Nested.Record;
var ComponentState = (function (_super) {
__extends(ComponentState, _super);
function ComponentState() {
return _super !== null && _super.apply(this, arguments) || this;
}
return ComponentState;
}(BaseClass));
ComponentState.attributes = state;
ComponentState = __decorate([
Nested.define
], ComponentState);
prototype.State = ComponentState;
}
else if (State) {
prototype.State = State;
}
if (state || State) {
this.mixins.merge([StateMixin, UpdateOnNestedChangesMixin]);
}
}
var StateMixin = {
//state : null,
_initializeState: function () {
// props.__keepState is used to workaround issues in Backbone intergation layer
var state = this.state = this.props.__keepState || new this.State();
// Take ownership on state...
state._owner = this;
state._ownerKey = 'state';
},
context: {
_nestedStore: Nested.Store
},
// reference global store to fix model's store locator
getStore: function () {
// Attempt to get the store from the context first. Then - fallback to the state's default store.
// TBD: Need to figure out a good way of managing local stores.
var context, state;
return ((context = this.context) && context._nestedStore) ||
((state = this.state) && state._defaultStore);
},
componentWillUnmount: function () {
var state = this.state;
state._owner = state._ownerKey = void 0;
this._preventDispose /* hack for component-view to preserve the state */ || state.dispose();
this.state = void 0;
}
};
var UpdateOnNestedChangesMixin = {
_onChildrenChange: function () { },
componentDidMount: function () {
this._onChildrenChange = this.asyncUpdate;
}
};
function onDefine$2(definition, BaseClass) {
var store = definition.store, StoreClass = definition.Store;
if (store && store instanceof Nested.Store) {
// Direct reference to an existing store. Put it to the prototype.
this.prototype.store = store;
this.mixins.merge([ExternalStoreMixin, ExposeStoreMixin]);
}
else if (store || definition.Store) {
if (typeof store === 'function') {
StoreClass = store;
store = void 0;
}
if (store) {
var BaseClass_1 = StoreClass || this.prototype.Store || Nested.Store;
var InternalStore = (function (_super) {
__extends(InternalStore, _super);
function InternalStore() {
return _super !== null && _super.apply(this, arguments) || this;
}
return InternalStore;
}(BaseClass_1));
InternalStore.attrbutes = store;
InternalStore = __decorate([
Nested.define
], InternalStore);
this.prototype.Store = InternalStore;
}
else if (StoreClass) {
this.prototype.Store = StoreClass;
}
this.mixins.merge([InternalStoreMixin, UpdateOnNestedChangesMixin, ExposeStoreMixin]);
}
}
/**
* Attached whenever the store declaration of any form is present in the component.
*/
var ExposeStoreMixin = {
childContext: {
_nestedStore: Nested.Store
},
getChildContext: function () {
return { _nestedStore: this.store };
},
getStore: function () {
return this.store;
},
// Will be called by the store when the lookup will fail.
get: function (key) {
// Ask upper store.
var store = StateMixin.getStore.call(this, key);
return store && store.get(key);
}
};
/**
* External store must just track the changes and trigger render.
* TBD: don't use it yet.
*/
var ExternalStoreMixin = {
componentDidMount: function () {
// Start UI updates on state changes.
this.listenTo(this.store, 'change', this.asyncUpdate);
}
};
var InternalStoreMixin = {
componentWillMount: function () {
var store = this.store = new this.Store();
store._owner = this;
store._ownerKey = 'store';
},
componentWillUnmount: function () {
this.store._ownerKey = this.store._owner = void 0;
this.store.dispose();
this.store = void 0;
}
};
function compileSpecs(props) {
var propTypes = {},
// Create NestedTypes model definition to process props spec.
modelProto = Nested.Record.defaults(props).prototype;
var defaults, watchers, changeHandlers;
modelProto.forEachAttr(modelProto._attributes, function (spec, name) {
// Skip auto-generated `id` attribute.
if (name !== 'id') {
var value = spec.value, type = spec.type, options = spec.options;
// Translate props type to the propTypes guard.
propTypes[name] = translateType(type, options.isRequired);
if (options._onChange) {
watchers || (watchers = {});
watchers[name] = toLocalWatcher(options._onChange);
}
// Handle listening to event maps...
if (options.changeHandlers && options.changeHandlers.length) {
changeHandlers || (changeHandlers = {});
changeHandlers[name] = options.changeHandlers;
}
// Handle listening to props changes...
if (options.changeEvents) {
changeHandlers || (changeHandlers = {});
var handlers = changeHandlers[name] || (changeHandlers[name] = []), changeEvents_1 = typeof options.changeEvents === 'string' ? options.changeEvents : null;
handlers.push(function (next, prev, component) {
prev && component.stopListening(prev);
next && component.listenTo(next, changeEvents_1 || next._changeEventName, component.asyncUpdate);
});
}
// If default value is explicitly provided...
if (value !== void 0) {
//...append it to getDefaultProps function.
defaults || (defaults = {});
defaults[name] = spec.convert(value, void 0, null, {});
}
}
});
return { propTypes: propTypes, defaults: defaults, watchers: watchers, changeHandlers: changeHandlers };
}
function toLocalWatcher(ref) {
return typeof ref === 'function' ? ref : function (value, name) {
this[ref] && this[ref](value, name);
};
}
var Node = (function () {
function Node() {
}
return Node;
}());
var Element = (function () {
function Element() {
}
return Element;
}());
function translateType(Type, isRequired) {
var T = _translateType(Type);
return isRequired ? T.isRequired : T;
}
function _translateType(Type) {
switch (Type) {
case Number:
case Number.integer:
return PropTypes.number;
case String:
return PropTypes.string;
case Boolean:
return PropTypes.bool;
case Array:
return PropTypes.array;
case Function:
return PropTypes.func;
case Object:
return PropTypes.object;
case Node:
return PropTypes.node;
case Element:
return PropTypes.element;
case void 0:
case null:
return PropTypes.any;
default:
return PropTypes.instanceOf(Type);
}
}
function onDefine$3(_a, BaseClass) {
var context = _a.context, childContext = _a.childContext;
var prototype = this.prototype;
if (context) {
// Merge in inherited members...
prototype._context = Nested.tools.defaults(context, BaseClass.prototype._context || {});
// Compile to propTypes...
this.contextTypes = compileSpecs(context).propTypes;
}
if (childContext) {
prototype._childContext = Nested.tools.defaults(childContext, BaseClass.prototype._childContext);
this.childContextTypes = compileSpecs(childContext).propTypes;
}
}
function createChangeTokensConstructor(props) {
var propNames = Object.keys(props);
var PropsChangeTokens = new Function('p', 's', "\n var v;\n this._s = s && s._changeToken;\n " + propNames.map(function (name) { return "\n this." + name + " = ( ( v = p." + name + ") && v._changeToken ) || v;\n "; }).join('') + "\n ");
PropsChangeTokens.prototype._hasChanges = new Function('p', 's', "\n var v;\n return ( ( s && s._changeToken ) !== this._s ) " + propNames.map(function (name) { return " ||\n this." + name + " !== ( ( ( v = p." + name + ") && v._changeToken ) || v )\n "; }).join('') + ";\n ");
return PropsChangeTokens;
}
var EmptyPropsChangeTokensCtor = createChangeTokensConstructor({});
var PureRenderMixin = {
shouldComponentUpdate: function (nextProps) {
return this._propsChangeTokens._hasChanges(nextProps);
},
componentDidMount: updateChangeTokens,
componentDidUpdate: updateChangeTokens
};
function updateChangeTokens() {
this._propsChangeTokens = new this.PropsChangeTokens(this.props, this.state);
}
/**
* Handle props specification and everything which is related:
* - local listening to props changes
* - pure render mixin
*/
function onDefine$4(_a, BaseClass) {
var props = _a.props, pureRender = _a.pureRender;
var prototype = this.prototype;
// process props spec...
if (props) {
// Merge with inherited members...
prototype._props = Nested.tools.defaults(props, BaseClass.prototype._props || {});
var _b = compileSpecs(props), propTypes = _b.propTypes, defaults = _b.defaults, watchers = _b.watchers, changeHandlers = _b.changeHandlers;
this.propTypes = propTypes;
if (defaults)
this.defaultProps = defaults;
if (watchers) {
prototype._watchers = watchers;
this.mixins.merge([WatchersMixin]);
}
if (changeHandlers) {
prototype._changeHandlers = changeHandlers;
this.mixins.merge([ChangeHandlersMixin]);
}
if (prototype.pureRender) {
prototype.PropsChangeTokens = createChangeTokensConstructor(props);
}
}
if (pureRender) {
this.mixins.merge([PureRenderMixin]);
}
}
/**
* ChangeHandlers are fired in sequence upon props replacement.
* Fires _after_ UI is updated. Used for managing events subscriptions.
*/
var ChangeHandlersMixin = {
componentDidMount: function () {
handlePropsChanges(this, {}, this.props);
},
componentDidUpdate: function (prev) {
handlePropsChanges(this, prev, this.props);
},
componentWillUnmount: function () {
handlePropsChanges(this, this.props, {});
}
};
function handlePropsChanges(component, prev, next) {
var _changeHandlers = component._changeHandlers;
for (var name_1 in _changeHandlers) {
if (prev[name_1] !== next[name_1]) {
for (var _i = 0, _a = _changeHandlers[name_1]; _i < _a.length; _i++) {
var handler = _a[_i];
handler(next[name_1], prev[name_1], component);
}
}
}
}
/**
* Watchers works on props replacement and fires _before_ any change will be applied and UI is updated.
* Fired in componentWillMount as well, which makes it a nice way to sync state from props.
*/
var WatchersMixin = {
componentWillReceiveProps: function (next) {
var _a = this, _watchers = _a._watchers, props = _a.props;
for (var name_2 in _watchers) {
if (next[name_2] !== props[name_2]) {
_watchers[name_2].call(this, next[name_2], name_2);
}
}
},
componentWillMount: function () {
var _a = this, _watchers = _a._watchers, props = _a.props;
for (var name_3 in _watchers) {
_watchers[name_3].call(this, props[name_3], name_3);
}
}
};
function onDefine$1(definition, BaseClass) {
// Initialize mixins placeholder...
onDefine$2.call(this, definition, BaseClass);
process.call(this, definition, BaseClass);
onDefine$3.call(this, definition, BaseClass);
onDefine$4.call(this, definition, BaseClass);
Nested.Messenger.onDefine.call(this, definition, BaseClass);
}
var ArrayProto = Array.prototype;
var ObjectProto = Object.prototype;
function helpers(value) {
if (value && typeof value === 'object') {
switch (Object.getPrototypeOf(value)) {
case ArrayProto: return arrayHelpers;
case ObjectProto: return objectHelpers;
}
}
return dummyHelpers;
}
// Do nothing for types other than Array and plain Object.
var dummyHelpers = {
clone: function (value) { return value; },
map: function (link, fun) { return []; },
remove: function (value) { return value; }
};
// `map` and `clone` for plain JS objects
var objectHelpers = {
// Map through the link to object
map: function (link, iterator) {
var mapped = [];
for (var key in link.value) {
var element$$1 = iterator(link.at(key), key);
element$$1 === void 0 || (mapped.push(element$$1));
}
return mapped;
},
remove: function (object$$1, key) {
delete object$$1[key];
return object$$1;
},
// Shallow clone plain JS object
clone: function (object$$1) {
var cloned = {};
for (var key in object$$1) {
cloned[key] = object$$1[key];
}
return cloned;
}
};
// `map` and `clone` helpers for arrays.
var arrayHelpers = {
// Shallow clone array
clone: function (array$$1) {
return array$$1.slice();
},
remove: function (array$$1, i) {
array$$1.splice(i, 1);
return array$$1;
},
// Map through the link to array
map: function (link, iterator) {
var length = link.value.length, mapped = Array(length);
for (var i = 0, j = 0; i < length; i++) {
var y = iterator(link.at(i), i);
y === void 0 || (mapped[j++] = y);
}
mapped.length === j || (mapped.length = j);
return mapped;
}
};
/**
* Advanced React links for purely functional two-way data binding
*
* MIT License, (c) 2016 Vlad Balin, Volicon.
*/
// Main Link class. All links must extend it.
var Link$1 = (function () {
// create
function Link(value) {
this.value = value;
}
// Create custom link to arbitrary value
Link.value = function (value, set) {
return new CustomLink(value, set);
};
Object.defineProperty(Link.prototype, "validationError", {
// DEPRECATED: Old error holder for backward compatibility with Volicon code base
get: function () { return this.error; },
enumerable: true,
configurable: true
});
Link.prototype.onChange = function (handler) {
var _this = this;
return new CloneLink(this, function (x) {
handler(x);
_this.set(x);
});
};
Object.defineProperty(Link.prototype, "props", {
// <input { ...link.props } />
get: function () {
var _this = this;
return typeof this.value === 'boolean' ? {
checked: this.value,
onChange: function (e) { return _this.set(Boolean(e.target.checked)); }
} : {
value: this.value,
onChange: function (e) { return _this.set(e.target.value); }
};
},
enumerable: true,
configurable: true
});
// DEPRECATED: Old React method for backward compatibility
Link.prototype.requestChange = function (x) {
this.set(x);
};
// Immediately update the link value using given transform function.
Link.prototype.update = function (transform, e) {
var next = transform(this.clone(), e);
next === void 0 || this.set(next);
};
// Create new link which applies transform function on set.
Link.prototype.pipe = function (handler) {
var _this = this;
return new CloneLink(this, function (x) {
var next = handler(x, _this.value);
next === void 0 || _this.set(next);
});
};
// Create UI event handler function which will update the link with a given transform function.
Link.prototype.action = function (transform) {
var _this = this;
return function (e) { return _this.update(transform, e); };
};
Link.prototype.equals = function (truthyValue) {
return new EqualsLink(this, truthyValue);
};
Link.prototype.enabled = function (defaultValue) {
return new EnabledLink(this, defaultValue || "");
};
// Array-only links methods
Link.prototype.contains = function (element$$1) {
return new ContainsLink(this, element$$1);
};
Link.prototype.push = function () {
var array$$1 = arrayHelpers.clone(this.value);
Array.prototype.push.apply(array$$1, arguments);
this.set(array$$1);
};
Link.prototype.unshift = function () {
var array$$1 = arrayHelpers.clone(this.value);
Array.prototype.unshift.apply(array$$1, arguments);
this.set(array$$1);
};
Link.prototype.splice = function () {
var array$$1 = arrayHelpers.clone(this.value);
Array.prototype.splice.apply(array$$1, arguments);
this.set(array$$1);
};
Link.prototype.map = function (iterator) {
return helpers(this.value).map(this, iterator);
};
Link.prototype.removeAt = function (key) {
var value = this.value, _ = helpers(value);
this.set(_.remove(_.clone(value), key));
};
Link.prototype.at = function (key) {
return new LinkAt(this, key);
};
Link.prototype.clone = function () {
var value = this.value;
return helpers(value).clone(value);
};
Link.prototype.pick = function () {
var links = {};
for (var i = 0; i < arguments.length; i++) {
var key = arguments[i];
links[key] = new LinkAt(this, key);
}
return links;
};
/**
* Validate link with validness predicate and optional custom error object. Can be chained.
*/
Link.prototype.check = function (whenValid, error) {
if (!this.error && !whenValid(this.value)) {
this.error = error || whenValid.error || defaultError;
}
return this;
};
return Link;
}());
var CustomLink = (function (_super) {
__extends(CustomLink, _super);
function CustomLink(value, set) {
var _this = _super.call(this, value) || this;
_this.set = set;
return _this;
}
CustomLink.prototype.set = function (x) { };
return CustomLink;
}(Link$1));
var CloneLink = (function (_super) {
__extends(CloneLink, _super);
function CloneLink(parent, set) {
var _this = _super.call(this, parent.value) || this;
_this.set = set;
var error = parent.error;
if (error)
_this.error = error;
return _this;
}
CloneLink.prototype.set = function (x) { };
return CloneLink;
}(Link$1));
var EqualsLink = (function (_super) {
__extends(EqualsLink, _super);
function EqualsLink(parent, truthyValue) {
var _this = _super.call(this, parent.value === truthyValue) || this;
_this.parent = parent;
_this.truthyValue = truthyValue;
return _this;
}
EqualsLink.prototype.set = function (x) {
this.parent.set(x ? this.truthyValue : null);
};
return EqualsLink;
}(Link$1));
var EnabledLink = (function (_super) {
__extends(EnabledLink, _super);
function EnabledLink(parent, defaultValue) {
var _this = _super.call(this, parent.value != null) || this;
_this.parent = parent;
_this.defaultValue = defaultValue;
return _this;
}
EnabledLink.prototype.set = function (x) {
this.parent.set(x ? this.defaultValue : null);
};
return EnabledLink;
}(Link$1));
var ContainsLink = (function (_super) {
__extends(ContainsLink, _super);
function ContainsLink(parent, element$$1) {
var _this = _super.call(this, parent.value.indexOf(element$$1) >= 0) || this;
_this.parent = parent;
_this.element = element$$1;
return _this;
}
ContainsLink.prototype.set = function (x) {
var _this = this;
var next = Boolean(x);
if (this.value !== next) {
var arr = this.parent.value, nextValue = x ? arr.concat(this.element) : arr.filter(function (el) { return el !== _this.element; });
this.parent.set(nextValue);
}
};
return ContainsLink;
}(Link$1));
var defaultError = 'Invalid value';
/**
* Link to array or object element enclosed in parent link.
* Performs purely functional update of the parent, shallow copying its value on `set`.
*/
var LinkAt = (function (_super) {
__extends(LinkAt, _super);
function LinkAt(parent, key) {
var _this = _super.call(this, parent.value[key]) || this;
_this.parent = parent;
_this.key = key;
return _this;
}
LinkAt.prototype.remove = function () {
this.parent.removeAt(this.key);
};
// Set new element value to parent array or object, performing purely functional update.
LinkAt.prototype.set = function (x) {
var _this = this;
if (this.value !== x) {
this.parent.update(function (value) {
value[_this.key] = x;
return value;
});
}
};
return LinkAt;
}(Link$1));
/**
* Import ValueLink library
* Define value links binding mixins to the Record and Collection
*/
Nested.Mixable.mixins.populate(Link$1);
/**
* Record
*/
Nested.MixinsState.get(Nested.Record).merge([{
// Link to the record's attribute by its key.
linkAt: function (key) {
return cacheLink(getLinksCache(this), this, key);
},
// Link to the attribute of the record's tree by symbolic path.
linkPath: function (path, options) {
return new RecordDeepLink(this, path, options);
},
// Link all (or listed) attributes and return links cache.
linkAll: function () {
var links = getLinksCache(this);
if (arguments.length) {
for (var i = 0; i < arguments.length; i++) {
cacheLink(links, this, arguments[i]);
}
}
else {
var attributes = this.attributes;
for (var key in attributes) {
attributes[key] === void 0 || cacheLink(links, this, key);
}
}
return links;
}
}]);
/**
* Link to Type-R's record attribute.
* Strict evaluation of value, lazy evaluation of validation error.
* Links are cached in the records
*/
var RecordLink = (function (_super) {
__extends(RecordLink, _super);
function RecordLink(record, attr, value) {
var _this = _super.call(this, value) || this;
_this.record = record;
_this.attr = attr;
return _this;
}
RecordLink.prototype.set = function (x) {
this.record[this.attr] = x;
};
Object.defineProperty(RecordLink.prototype, "error", {
get: function () {
return this._error === void 0 ?
this.record.getValidationError(this.attr) :
this._error;
},
set: function (x) {
this._error = x;
},
enumerable: true,
configurable: true
});
return RecordLink;
}(Link$1));
var RecordDeepLink = (function (_super) {
__extends(RecordDeepLink, _super);
function RecordDeepLink(record, path, options) {
var _this = _super.call(this, record.deepGet(path)) || this;
_this.record = record;
_this.path = path;
_this.options = options;
return _this;
}
Object.defineProperty(RecordDeepLink.prototype, "error", {
get: function () {
if (this._error === void 0) {
this._error = this.record.deepValidationError(this.path) || null;
}
return this._error;
},
set: function (x) {
this._error = x;
},
enumerable: true,
configurable: true
});
Object.defineProperty(RecordDeepLink.prototype, "_changeToken", {
get: function () {
return this.record._changeToken;
},
enumerable: true,
configurable: true
});
RecordDeepLink.prototype.set = function (x) {
this.record.deepSet(this.path, x, this.options);
};
return RecordDeepLink;
}(Link$1));
function getLinksCache(record) {
return record._links || (record._links = new record.AttributesCopy({}));
}
function cacheLink(links, record, key) {
var cached = links[key], value = record[key];
return cached && cached.value === value ? cached
: links[key] = new RecordLink(record, key, value);
}
/***********************************
* Collection
*/
Nested.MixinsState.get(Nested.Record.Collection).merge([{
// Boolean link to the record's presence in the collection
linkContains: function (record) {
return new CollectionLink(this, record);
},
// Link to collection's property
linkAt: function (prop) {
var _this = this;
return Link$1.value(this[prop], function (x) { return _this[prop] = x; });
}
}]);
/**
* Boolean link to presence of NestedType's record in collection.
* Strict evaluation of value, no error.
* Safe implementation of _changeToken.
*/
var CollectionLink = (function (_super) {
__extends(CollectionLink, _super);
function CollectionLink(collection, record) {
var _this = _super.call(this, Boolean(collection._byId[record.cid])) || this;
_this.collection = collection;
_this.record = record;
return _this;
}
CollectionLink.prototype.set = function (x) {
this.collection.toggle(this.record, x);
};
return CollectionLink;
}(Link$1));
/**
* React-Type-R component base class. Overrides React component.
*/
exports.Component = (function (_super) {
__extends(Component$$1, _super);
function Component$$1(props, context) {
var _this = _super.call(this, props, context) || this;
_this._initializeState();
return _this;
}
Component$$1.prototype.linkAt = function (key) {
// Quick and dirty hack to suppres type error - refactor later.
return this.state.linkAt(key);
};
Component$$1.prototype.linkAll = function () {
// Quick and dirty hack to suppres type error - refactor later.
var state = this.state;
return state.linkAll.apply(state, arguments);
};
Component$$1.prototype.linkPath = function (path) {
return this.state.linkPath(path);
};
Object.defineProperty(Component$$1.prototype, "links", {
get: function () {
return this.state._links;
},
enumerable: true,
configurable: true
});
Component$$1.prototype._initializeState = function () {
this.state = null;
};
Component$$1.prototype.assignToState = function (x, key) {
this.state.assignFrom((_a = {}, _a[key] = x, _a));
var _a;
};
Component$$1.prototype.componentWillUnmount = function () {
this.dispose();
};
/**
* Performs transactional update for both props and state.
* Suppress updates during the transaction, and force update aftewards.
* Wrapping the sequence of changes in a transactions guarantees that
* React component will be updated _after_ all the changes to the
* both props and local state are applied.
*/
Component$$1.prototype.transaction = function (fun) {
var shouldComponentUpdate = this.shouldComponentUpdate, isRoot = shouldComponentUpdate !== returnFalse;
if (isRoot) {
this.shouldComponentUpdate = returnFalse;
}
var _a = this, state = _a.state, store = _a.store, withStore = store ? function (state) { return store.transaction(function () { return fun(state); }); } : fun;
state ? state.transaction(withStore) : withStore(state);
if (isRoot) {
this.shouldComponentUpdate = shouldComponentUpdate;
this.asyncUpdate();
}
};
// Safe version of the forceUpdate suitable for asynchronous callbacks.
Component$$1.prototype.asyncUpdate = function () {
this.shouldComponentUpdate === returnFalse || this._disposed || this.forceUpdate();
};
return Component$$1;
}(React.Component));
exports.Component.onDefine = onDefine$1;
exports.Component = __decorate([
Nested.define({
PropsChangeTokens: EmptyPropsChangeTokensCtor
}),
Nested.definitions({
// Definitions to be extracted from mixins and statics and passed to `onDefine()`
state: Nested.mixinRules.merge,
State: Nested.mixinRules.value,
store: Nested.mixinRules.merge,
Store: Nested.mixinRules.value,
props: Nested.mixinRules.merge,
context: Nested.mixinRules.merge,
childContext: Nested.mixinRules.merge,
pureRender: Nested.mixinRules.protoValue
}),
Nested.mixinRules({
// Apply old-school React mixin rules.
componentWillMount: Nested.mixinRules.classLast,
componentDidMount: Nested.mixinRules.classLast,
componentWillReceiveProps: Nested.mixinRules.classLast,
componentWillUpdate: Nested.mixinRules.classLast,
componentDidUpdate: Nested.mixinRules.classLast,
componentWillUnmount: Nested.mixinRules.classFirst,
// And a bit more to fix inheritance quirks.
shouldComponentUpdate: Nested.mixinRules.some,
getChildContext: Nested.mixinRules.defaults
})
// Component can send and receive events...
,
Nested.mixins(Nested.Messenger)
], exports.Component);
function returnFalse() { return false; }
// Looks like React guys _really_ want to deprecate it. But no way.
// We will work around their attempt.
Object.defineProperty(exports.Component.prototype, 'isMounted', {
value: function isMounted() {
return !this._disposed;
}
});
// extend React namespace
var ReactMVx = Object.create(React);
// Make it compatible with ES6 module format.
ReactMVx.default = ReactMVx;
// listenToProps, listenToState, model, attributes, Model
ReactMVx.define = Nested.define;
ReactMVx.mixins = Nested.mixins;
ReactMVx.Node = Node.value(null);
ReactMVx.Element = Element.value(null);
ReactMVx.Link = Link$1;
ReactMVx.Component = exports.Component;
var assignToState = ReactMVx.assignToState = function (key) {
return function (prop) {
this.state.assignFrom((_a = {}, _a[key] = prop && prop instanceof Link ? prop.value : prop, _a));
var _a;
};
};
var notEqual = Nested.tools.notEqual;
var BackboneView = (function (_super) {
__extends(BackboneView, _super);
function BackboneView() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.saveRef = function (element$$1) {
_this.root = element$$1;
};
return _this;
}
BackboneView.prototype.shouldComponentUpdate = function (next) {
var props = this.props;
return next.View !== props.View || notEqual(next.options, props.options);
};
BackboneView.prototype.hasUnsavedChanges = function () {
var view = this.view;
return view && (typeof view.hasUnsavedChanges === 'function' ? view.hasUnsavedChanges() : view.hasUnsavedChanges);
};
BackboneView.prototype.render = function () {
return ReactMVx.createElement('div', {
ref: this.saveRef,
className: this.props.className
});
};
BackboneView.prototype.componentDidMount = function () {
this._mountView();
};
BackboneView.prototype.componentDidUpdate = function () {
this._dispose();
this._mountView();
};
BackboneView.prototype.componentWillUnmount = function () {
this._dispose();
};
BackboneView.prototype._mountView = function () {
var el = this.root, p = this.props;
var view = this.view = p.options ? new p.View(p.options) : new p.View();
el.appendChild(view.el);
view.render();
};
BackboneView.prototype._dispose = function () {
var view = this.view;
if (view) {
if (view.dispose) {
view.dispose();
}
else {
view.stopListening();
view.off();
}
this.root.innerHTML = "";
this.view = null;
}
};
return BackboneView;
}(exports.Component));
window.Page || (window.Page = { forceResize: function () { } });
function use(View$$1) {
var dispose = View$$1.prototype.dispose || function () { }, setElement = View$$1.prototype.setElement;
var ComponentView = View$$1.extend({
reactClass: null,
props: {},
element: null,
initialize: function (props) {
// memorise arguments to pass to React
this.options = props || {};
},
setElement: function () {
this.unmountComponent(true);
return setElement.apply(this, arguments);
},
// cached instance of react component...
component: null,
prevState: null,
resize: function () {
window.Page.forceResize();
},
render: function () {
var options = this.prevState ? Nested.tools.fastAssign({ __keepState: this.prevState }, this.options) : this.options, element$$1 = React.createElement(this.reactClass, options), component = ReactDOM.render(element$$1, this.el);
this.component || this.mountComponent(component);
},
mountComponent: function (component) {
this.component = component;
this.prevState = null;
component.trigger && this.listenTo(component, 'all', function () {
this.trigger.apply(this, arguments);
});
},
unmountComponent: function (keepModel) {
var component = this.component;
if (component) {
this.prevState = component.state;
if (component.trigger) {
this.stopListening(component);
}
component._preventDispose = Boolean(keepModel);
ReactDOM.unmountComponentAtNode(this.el);
this.component = null;
}
},
dispose: function () {
this.unmountComponent();
return dispose.apply(this, arguments);
}
});
Object.defineProperty(ComponentView.prototype, 'model', {
get: function () {
this.component || this.render();
return this.component && this.component.state;
}
});
return ComponentView;
}
var dontAutobind = [
'State', 'Store', 'constructor',
'componentWillMount', 'componentDidMount', 'componentWillReceiveProps', 'shouldComponentUpdate',
'componentWillUpdate', 'componentDidUpdate', 'componentWillUnmount',
'render', 'getDefaultProps', 'getChildContext'
];
/**
* ES5 components definition factory
*/
function createClass(_a) {
var statics = _a.statics, a_spec = __rest(_a, ["statics"]);
// Gather all methods to pin them to `this` later.
var methods = [];
var Subclass = exports.Component.extend(__assign({
// Override constructor to autobind all the methods...
constructor: function () {
exports.Component.apply(this, arguments);
for (var _i = 0, methods_1 = methods; _i < methods_1.length; _i++) {
var method = methods_1[_i];
this[method] = this[method].bind(this);
}
} }, a_spec), statics);
// Need to bind methods from mixins as well, so populate it here.
var Proto = Subclass.prototype;
for (var key in Proto) {
if (Proto.hasOwnProperty(key) && dontAutobind.indexOf(key) === -1 && typeof Proto[key] === 'function') {
methods.push(key);
}
}
return Subclass;
}
// Re-export react-mvx
var NestedReact = Object.create(ReactMVx);
// NestedReact backward compatibility layer
NestedReact.subview = BackboneView;
Object.defineProperty(NestedReact, 'createClass', { value: createClass });
Object.defineProperty(NestedReact, 'PropTypes', { value: PropTypes });
var BaseView;
// export hook to override base View class used...
function useView(View$$1) {
BaseView = use(View$$1);
}
var onDefine = NestedReact.Component.onDefine;
NestedReact.Component.onDefine = function (definitions$$1, BaseClass) {
this.View = BaseView.extend({ reactClass: this });
return onDefine.call(this, definitions$$1, BaseClass);
};
// Deprecated API for backward compatibility
var RecordProto = Nested.Record.prototype;
RecordProto.getLink = RecordProto.linkAt;
RecordProto.deepLink = RecordProto.linkPath;
var CollectionProto = Nested.Record.Collection.prototype;
CollectionProto.hasLink = CollectionProto.linkContains;
useView(Nested.View);
// Extend react components to have backbone-style jquery accessors
var BackboneViewProps = {
el: { get: function () { return ReactDOM.findDOMNode(this); } },
$el: { get: function () { return Nested__default.$(this.el); } },
$: { value: function (sel) { return this.$el.find(sel); } }
};
Object.defineProperties(NestedReact.Component.prototype, BackboneViewProps);
exports['default'] = NestedReact;
exports.subview = BackboneView;
exports.PropTypes = PropTypes;
exports.createClass = createClass;
exports.useView = useView;
exports.define = Nested.define;
exports.mixins = Nested.mixins;
exports.Node = Node;
exports.Element = Element;
exports.Link = Link$1;
exports.assignToState = assignToState;
Object.defineProperty(exports, '__esModule', { value: true });
})));
//# sourceMappingURL=index.js.map