@getoccasion/mitragyna
Advanced tools
+1041
| (function (global, factory) { | ||
| if (typeof define === "function" && define.amd) { | ||
| define(['exports', 'react', 'prop-types', 'active-resource', 'underscore', 'classnames', 'shallowequal'], factory); | ||
| } else if (typeof exports !== "undefined") { | ||
| factory(exports, require('react'), require('prop-types'), require('active-resource'), require('underscore'), require('classnames'), require('shallowequal')); | ||
| } else { | ||
| var mod = { | ||
| exports: {} | ||
| }; | ||
| factory(mod.exports, global.react, global.propTypes, global.activeResource, global.underscore, global.classnames, global.shallowequal); | ||
| global.mitragyna = mod.exports; | ||
| } | ||
| })(this, function (exports, _react, _propTypes, _activeResource, _underscore, _classnames, _shallowequal) { | ||
| 'use strict'; | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| exports.Resource = exports.Field = exports.ErrorsFor = exports.Collection = undefined; | ||
| var _react2 = _interopRequireDefault(_react); | ||
| var _propTypes2 = _interopRequireDefault(_propTypes); | ||
| var _activeResource2 = _interopRequireDefault(_activeResource); | ||
| var _underscore2 = _interopRequireDefault(_underscore); | ||
| var _classnames2 = _interopRequireDefault(_classnames); | ||
| var _shallowequal2 = _interopRequireDefault(_shallowequal); | ||
| function _interopRequireDefault(obj) { | ||
| return obj && obj.__esModule ? obj : { | ||
| default: obj | ||
| }; | ||
| } | ||
| function _defineProperty(obj, key, value) { | ||
| if (key in obj) { | ||
| Object.defineProperty(obj, key, { | ||
| value: value, | ||
| enumerable: true, | ||
| configurable: true, | ||
| writable: true | ||
| }); | ||
| } else { | ||
| obj[key] = value; | ||
| } | ||
| return obj; | ||
| } | ||
| var _extends = Object.assign || function (target) { | ||
| for (var i = 1; i < arguments.length; i++) { | ||
| var source = arguments[i]; | ||
| for (var key in source) { | ||
| if (Object.prototype.hasOwnProperty.call(source, key)) { | ||
| target[key] = source[key]; | ||
| } | ||
| } | ||
| } | ||
| return target; | ||
| }; | ||
| function _classCallCheck(instance, Constructor) { | ||
| if (!(instance instanceof Constructor)) { | ||
| throw new TypeError("Cannot call a class as a function"); | ||
| } | ||
| } | ||
| 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 _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 Collection = exports.Collection = function (_React$PureComponent) { | ||
| _inherits(Collection, _React$PureComponent); | ||
| // link to global state by enabling afterLoad, afterAdd, afterRemove, afterUpdate callbacks that can call | ||
| // an action linked to dispatch | ||
| function Collection() { | ||
| _classCallCheck(this, Collection); | ||
| var _this = _possibleConstructorReturn(this, (Collection.__proto__ || Object.getPrototypeOf(Collection)).call(this)); | ||
| _this.state = { | ||
| target: _activeResource2.default.prototype.Collection.build() | ||
| }; | ||
| _underscore2.default.bindAll(_this, 'buildOnTarget', 'cloneTarget', 'replaceOnTarget', 'removeFromTarget'); | ||
| return _this; | ||
| } | ||
| _createClass(Collection, [{ | ||
| key: 'componentDidMount', | ||
| value: function componentDidMount() { | ||
| this.setTarget(this.props); | ||
| } | ||
| }, { | ||
| key: 'componentWillReceiveProps', | ||
| value: function componentWillReceiveProps(nextProps) { | ||
| this.setTarget(nextProps); | ||
| } | ||
| }, { | ||
| key: 'setTarget', | ||
| value: function setTarget(props) { | ||
| var subject = props.subject; | ||
| this.setState({ target: subject.target() }); | ||
| } | ||
| }, { | ||
| key: 'buildOnTarget', | ||
| value: function buildOnTarget(attributes) { | ||
| var subject = this.props.subject; | ||
| var target = this.cloneTarget(); | ||
| target.push(subject.build(attributes)); | ||
| this.setState({ target: target }); | ||
| } | ||
| }, { | ||
| key: 'replaceOnTarget', | ||
| value: function replaceOnTarget(newItem, oldItem) { | ||
| var target = this.cloneTarget(); | ||
| target.replace(oldItem, newItem); | ||
| return this.setState({ target: target }); | ||
| } | ||
| }, { | ||
| key: 'removeFromTarget', | ||
| value: function removeFromTarget(item) { | ||
| var target = this.cloneTarget(); | ||
| target.delete(item); | ||
| return this.setState({ target: target }); | ||
| } | ||
| }, { | ||
| key: 'cloneTarget', | ||
| value: function cloneTarget() { | ||
| return this.state.target.clone(); | ||
| } | ||
| }, { | ||
| key: 'render', | ||
| value: function render() { | ||
| var _this2 = this; | ||
| var _props = this.props, | ||
| blankComponent = _props.blankComponent, | ||
| children = _props.children, | ||
| className = _props.className, | ||
| component = _props.component, | ||
| componentProps = _props.componentProps, | ||
| reflection = _props.reflection; | ||
| var target = this.state.target; | ||
| return _react2.default.createElement( | ||
| 'section', | ||
| { className: className }, | ||
| target.size() > 0 ? target.map(function (t, indexOf) { | ||
| return _react2.default.createElement( | ||
| Resource, | ||
| { afterUpdate: _this2.replaceOnTarget, | ||
| component: component, componentProps: _extends({}, componentProps, { indexOf: indexOf }), | ||
| key: t.id || t.klass().className + '-' + indexOf, | ||
| reflection: reflection, | ||
| subject: t }, | ||
| children | ||
| ); | ||
| }).toArray() : blankComponent != null && blankComponent() | ||
| ); | ||
| } | ||
| }]); | ||
| return Collection; | ||
| }(_react2.default.PureComponent); | ||
| Collection.propTypes = { | ||
| children: _propTypes2.default.oneOfType([_propTypes2.default.array, _propTypes2.default.node]), | ||
| className: _propTypes2.default.string, | ||
| blankComponent: _propTypes2.default.func, | ||
| component: _propTypes2.default.func, | ||
| componentProps: _propTypes2.default.object, | ||
| subject: _propTypes2.default.oneOfType([_propTypes2.default.object, _propTypes2.default.func]).isRequired, | ||
| reflection: _propTypes2.default.string | ||
| }; | ||
| Collection.defaultProps = { | ||
| inlineRows: false | ||
| }; | ||
| var ErrorsFor = exports.ErrorsFor = function (_React$Component) { | ||
| _inherits(ErrorsFor, _React$Component); | ||
| function ErrorsFor() { | ||
| _classCallCheck(this, ErrorsFor); | ||
| return _possibleConstructorReturn(this, (ErrorsFor.__proto__ || Object.getPrototypeOf(ErrorsFor)).apply(this, arguments)); | ||
| } | ||
| _createClass(ErrorsFor, [{ | ||
| key: 'shouldComponentUpdate', | ||
| value: function shouldComponentUpdate(nextProps, nextState, nextContext) { | ||
| return !((0, _shallowequal2.default)(this.props, nextProps) && (0, _shallowequal2.default)(this.state, nextState) && (0, _shallowequal2.default)(this.context, nextContext)); | ||
| } | ||
| }, { | ||
| key: 'render', | ||
| value: function render() { | ||
| var resource = this.context.resource; | ||
| var _props2 = this.props, | ||
| component = _props2.component, | ||
| field = _props2.field; | ||
| var errors = resource.errors().forField(field); | ||
| if (errors.empty()) return null; | ||
| var customProps = _underscore2.default.omit(this.props, _underscore2.default.keys(ErrorsFor.propTypes)); | ||
| var finalComponent = component || 'summary'; | ||
| return _react2.default.createElement(finalComponent, _extends({}, customProps, { | ||
| key: field | ||
| }), errors.map(function (error) { | ||
| return _react2.default.createElement( | ||
| 'span', | ||
| { key: error.code }, | ||
| error.message | ||
| ); | ||
| }).toArray()); | ||
| } | ||
| }]); | ||
| return ErrorsFor; | ||
| }(_react2.default.Component); | ||
| ErrorsFor.propTypes = { | ||
| component: _propTypes2.default.func, | ||
| field: _propTypes2.default.string | ||
| }; | ||
| ErrorsFor.contextTypes = { | ||
| resource: _propTypes2.default.object | ||
| }; | ||
| ; | ||
| var Field = exports.Field = function (_React$Component2) { | ||
| _inherits(Field, _React$Component2); | ||
| function Field() { | ||
| _classCallCheck(this, Field); | ||
| var _this4 = _possibleConstructorReturn(this, (Field.__proto__ || Object.getPrototypeOf(Field)).call(this)); | ||
| _underscore2.default.bindAll(_this4, 'afterChange', 'changeRadio', 'classNames', 'commonInputProps', 'customInputProps', 'getValue', 'handleChange', 'renderCheckboxComponent', 'renderInputComponent', 'renderRadioComponent', 'renderSelectComponent', 'renderTextareaComponent', 'setValue', 'valueFor'); | ||
| _this4.state = {}; | ||
| return _this4; | ||
| } | ||
| _createClass(Field, [{ | ||
| key: 'shouldComponentUpdate', | ||
| value: function shouldComponentUpdate(nextProps, nextState, nextContext) { | ||
| return !((0, _shallowequal2.default)(this.props, nextProps) && (0, _shallowequal2.default)(this.state, nextState) && (0, _shallowequal2.default)(this.context, nextContext)); | ||
| } | ||
| }, { | ||
| key: 'getChildContext', | ||
| value: function getChildContext() { | ||
| var type = this.props.type; | ||
| var value = this.state.value; | ||
| switch (type) { | ||
| case 'radioGroup': | ||
| return { | ||
| changeRadio: this.changeRadio, | ||
| radioValue: value | ||
| }; | ||
| } | ||
| } | ||
| }, { | ||
| key: 'changeRadio', | ||
| value: function changeRadio(value) { | ||
| this.setState({ value: value }); | ||
| } | ||
| }, { | ||
| key: 'componentWillMount', | ||
| value: function componentWillMount() { | ||
| var type = this.props.type; | ||
| var resource = this.context.resource; | ||
| // Set initial value to that of the resources | ||
| this.setState({ | ||
| resource: resource, | ||
| value: this.valueFor(resource, this.props) | ||
| }); | ||
| switch (type) { | ||
| case 'email': | ||
| case 'number': | ||
| case 'text': | ||
| case 'textarea': | ||
| this.afterChange = _underscore2.default.debounce(this.afterChange, 500); | ||
| } | ||
| } | ||
| }, { | ||
| key: 'componentDidUpdate', | ||
| value: function componentDidUpdate(prevProps, prevState) { | ||
| var prevResource = prevState.resource; | ||
| var resource = this.context.resource; | ||
| if (prevResource !== resource) { | ||
| this.setState({ resource: resource }); | ||
| } | ||
| if (!(_underscore2.default.isNull(prevResource.id) || _underscore2.default.isUndefined(prevResource.id)) && prevResource.id !== resource.id) { | ||
| this.setState({ | ||
| value: this.valueFor(resource, this.props) | ||
| }); | ||
| } | ||
| } | ||
| }, { | ||
| key: 'classNames', | ||
| value: function classNames() { | ||
| var _props3 = this.props, | ||
| className = _props3.className, | ||
| invalidClassName = _props3.invalidClassName, | ||
| name = _props3.name; | ||
| var resource = this.context.resource; | ||
| return (0, _classnames2.default)(className, _defineProperty({}, invalidClassName, !resource.errors().forField(name).empty())); | ||
| } | ||
| }, { | ||
| key: 'commonInputProps', | ||
| value: function commonInputProps() { | ||
| var name = this.props.name; | ||
| var props = { | ||
| className: this.classNames(), | ||
| key: name, | ||
| name: name, | ||
| onChange: this.handleChange | ||
| }; | ||
| return props; | ||
| } | ||
| }, { | ||
| key: 'componentFor', | ||
| value: function componentFor(type) { | ||
| switch (type) { | ||
| case 'checkbox': | ||
| return this.renderCheckboxComponent(); | ||
| case 'radio': | ||
| return this.renderRadioComponent(); | ||
| case 'radioGroup': | ||
| return this.renderRadioGroupComponent(); | ||
| case 'select': | ||
| return this.renderSelectComponent(); | ||
| case 'textarea': | ||
| return this.renderTextareaComponent(); | ||
| default: | ||
| return this.renderInputComponent(); | ||
| } | ||
| } | ||
| // @note type='radio' will pass down +name+ prop | ||
| // @note type='select' will only pass down +type+ prop if +component+ prop is defined | ||
| }, { | ||
| key: 'customInputProps', | ||
| value: function customInputProps() { | ||
| var _props4 = this.props, | ||
| component = _props4.component, | ||
| type = _props4.type; | ||
| var omittedProps; | ||
| switch (type) { | ||
| case 'radio': | ||
| omittedProps = _underscore2.default.omit(Field.propTypes, ['type', 'name']); | ||
| break; | ||
| case 'select': | ||
| omittedProps = component ? _underscore2.default.omit(Field.propTypes, 'type') : Field.propTypes; | ||
| break; | ||
| default: | ||
| omittedProps = _underscore2.default.omit(Field.propTypes, 'type'); | ||
| } | ||
| return _underscore2.default.omit(this.props, _underscore2.default.keys(omittedProps)); | ||
| } | ||
| // TODO: Add support for non-resource options on select and radioGroup | ||
| }, { | ||
| key: 'valueFor', | ||
| value: function valueFor(resource, props) { | ||
| var name = props.name, | ||
| type = props.type, | ||
| uncheckedValue = props.uncheckedValue, | ||
| value = props.value; | ||
| switch (type) { | ||
| case 'checkbox': | ||
| var resourceValue = resource[name]; | ||
| if (resourceValue == value) { | ||
| return true; | ||
| } else if (resourceValue == uncheckedValue || _underscore2.default.isUndefined(resourceValue) || _underscore2.default.isNull(resourceValue)) { | ||
| return false; | ||
| } else { | ||
| throw 'Field ' + name + ' with value ' + resource[name] + ' does not match value or uncheckedValue for checkbox'; | ||
| } | ||
| case 'radioGroup': | ||
| case 'select': | ||
| var val = resource[name](); | ||
| return val ? val.id : ''; | ||
| default: | ||
| var val = resource[name]; | ||
| return val ? val : ''; | ||
| } | ||
| } | ||
| }, { | ||
| key: 'render', | ||
| value: function render() { | ||
| var type = this.props.type; | ||
| return this.componentFor(type); | ||
| } | ||
| }, { | ||
| key: 'renderCheckboxComponent', | ||
| value: function renderCheckboxComponent() { | ||
| var component = this.props.component; | ||
| var finalComponent = component || 'input'; | ||
| return _react2.default.createElement(finalComponent, _extends({}, this.commonInputProps(), this.customInputProps(), { | ||
| checked: this.state.value | ||
| })); | ||
| } | ||
| }, { | ||
| key: 'renderInputComponent', | ||
| value: function renderInputComponent() { | ||
| var component = this.props.component; | ||
| var finalComponent = component || 'input'; | ||
| return _react2.default.createElement(finalComponent, _extends({}, this.commonInputProps(), this.customInputProps(), { | ||
| value: this.state.value | ||
| })); | ||
| } | ||
| }, { | ||
| key: 'renderRadioComponent', | ||
| value: function renderRadioComponent() { | ||
| var _props5 = this.props, | ||
| component = _props5.component, | ||
| value = _props5.value; | ||
| var radioValue = this.context.radioValue; | ||
| if (_underscore2.default.isUndefined(value)) { | ||
| throw 'Input type="radio" must have prop "value"'; | ||
| } | ||
| var finalComponent = component || 'input'; | ||
| return _react2.default.createElement(finalComponent, _extends({}, this.commonInputProps(), this.customInputProps(), { | ||
| checked: value.id == radioValue, | ||
| value: value.id, | ||
| name: value.questionId | ||
| })); | ||
| } | ||
| }, { | ||
| key: 'renderRadioGroupComponent', | ||
| value: function renderRadioGroupComponent() { | ||
| return _react2.default.createElement( | ||
| 'div', | ||
| null, | ||
| this.props.children | ||
| ); | ||
| } | ||
| }, { | ||
| key: 'renderSelectComponent', | ||
| value: function renderSelectComponent() { | ||
| var _props6 = this.props, | ||
| component = _props6.component, | ||
| includeBlank = _props6.includeBlank, | ||
| options = _props6.options, | ||
| optionsLabel = _props6.optionsLabel; | ||
| var selectOptions = null; | ||
| if (options.empty()) { | ||
| throw 'Input type="select" must have options'; | ||
| } else { | ||
| selectOptions = options.map(function (o) { | ||
| return _react2.default.createElement( | ||
| 'option', | ||
| { key: o.id, value: o.id }, | ||
| _underscore2.default.isString(optionsLabel) ? o[optionsLabel] : optionsLabel(o) | ||
| ); | ||
| }); | ||
| if (includeBlank) { | ||
| selectOptions.unshift(_react2.default.createElement('option', { key: -1, value: '' })); | ||
| } | ||
| } | ||
| var finalComponent = component || 'select'; | ||
| return _react2.default.createElement(finalComponent, _extends({}, this.commonInputProps(), this.customInputProps(), { | ||
| value: this.state.value | ||
| }), selectOptions.toArray()); | ||
| } | ||
| }, { | ||
| key: 'renderTextareaComponent', | ||
| value: function renderTextareaComponent() { | ||
| var component = this.props.component; | ||
| var finalComponent = component || 'textarea'; | ||
| return _react2.default.createElement(finalComponent, _extends({}, this.commonInputProps(), this.customInputProps(), { | ||
| value: this.state.value | ||
| })); | ||
| } | ||
| }, { | ||
| key: 'handleChange', | ||
| value: function handleChange(e) { | ||
| e.persist(); | ||
| var _props7 = this.props, | ||
| max = _props7.max, | ||
| min = _props7.min, | ||
| type = _props7.type; | ||
| var changeRadio = this.context.changeRadio; | ||
| var value = void 0; | ||
| switch (type) { | ||
| case 'checkbox': | ||
| value = e.target.checked; | ||
| break; | ||
| case 'number': | ||
| if (e.target.value > max) { | ||
| value = max; | ||
| } else if (e.target.value < min) { | ||
| value = min; | ||
| } else { | ||
| value = e.target.value || min; | ||
| } | ||
| break; | ||
| case 'radio': | ||
| changeRadio(e.target.value); | ||
| break; | ||
| default: | ||
| value = e.target.value; | ||
| } | ||
| this.setState({ value: value }, this.afterChange); | ||
| } | ||
| }, { | ||
| key: 'afterChange', | ||
| value: function afterChange() { | ||
| var _props8 = this.props, | ||
| name = _props8.name, | ||
| type = _props8.type, | ||
| options = _props8.options, | ||
| uncheckedValue = _props8.uncheckedValue, | ||
| value = _props8.value; | ||
| var stateValue = this.state.value; | ||
| var queueChange = this.context.queueChange; | ||
| var mappedValue = void 0; | ||
| switch (type) { | ||
| case 'checkbox': | ||
| if (stateValue) { | ||
| mappedValue = value; | ||
| } else { | ||
| mappedValue = uncheckedValue; | ||
| } | ||
| break; | ||
| case 'radio': | ||
| mappedValue = value; | ||
| break; | ||
| case 'select': | ||
| mappedValue = options.detect(function (o) { | ||
| return o.id === stateValue; | ||
| }); | ||
| break; | ||
| default: | ||
| mappedValue = stateValue; | ||
| } | ||
| queueChange(_defineProperty({}, name, mappedValue)); | ||
| } | ||
| }, { | ||
| key: 'getValue', | ||
| value: function getValue() { | ||
| return this.state.value; | ||
| } | ||
| }, { | ||
| key: 'setValue', | ||
| value: function setValue(value) { | ||
| var type = this.props.type; | ||
| var mappedValue = { persist: _underscore2.default.noop }; | ||
| switch (type) { | ||
| case 'checkbox': | ||
| mappedValue = _extends({}, mappedValue, { target: { checked: value } }); | ||
| break; | ||
| default: | ||
| mappedValue = _extends({}, mappedValue, { target: { value: value } }); | ||
| } | ||
| this.handleChange(mappedValue); | ||
| } | ||
| }]); | ||
| return Field; | ||
| }(_react2.default.Component); | ||
| Field.contextTypes = { | ||
| changeRadio: _propTypes2.default.func, | ||
| queueChange: _propTypes2.default.func, | ||
| radioValue: _propTypes2.default.any, | ||
| resource: _propTypes2.default.object | ||
| }; | ||
| Field.childContextTypes = { | ||
| changeRadio: _propTypes2.default.func, | ||
| radioValue: _propTypes2.default.any | ||
| }; | ||
| Field.propTypes = { | ||
| className: _propTypes2.default.string, | ||
| component: _propTypes2.default.func, | ||
| includeBlank: _propTypes2.default.bool, | ||
| name: _propTypes2.default.string.isRequired, | ||
| options: _propTypes2.default.instanceOf(_activeResource2.default.Collection), | ||
| optionsLabel: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.func]), | ||
| type: _propTypes2.default.string.isRequired, | ||
| uncheckedValue: _propTypes2.default.oneOfType([_propTypes2.default.object, _propTypes2.default.func, _propTypes2.default.string, _propTypes2.default.number, _propTypes2.default.bool]), | ||
| invalidClassName: _propTypes2.default.string, | ||
| value: _propTypes2.default.oneOfType([_propTypes2.default.object, _propTypes2.default.func, _propTypes2.default.string, _propTypes2.default.number, _propTypes2.default.bool]) | ||
| }; | ||
| var Resource = exports.Resource = function (_React$Component3) { | ||
| _inherits(Resource, _React$Component3); | ||
| function Resource(props, context) { | ||
| _classCallCheck(this, Resource); | ||
| var _this5 = _possibleConstructorReturn(this, (Resource.__proto__ || Object.getPrototypeOf(Resource)).call(this)); | ||
| _underscore2.default.bindAll(_this5, "afterUpdate", "assignChanges", "queueReflectionChange", "shiftReflectionQueue", "queueChange", "handleSubmit", "updateRoot"); | ||
| var root = context.root; | ||
| var parent = props.parent, | ||
| reflection = props.reflection, | ||
| subject = props.subject; | ||
| var state = { resource: subject }; | ||
| if (reflection) { | ||
| var reflectionInstance = (root || parent).klass().reflectOnAssociation(reflection); | ||
| if (_underscore2.default.isUndefined(reflectionInstance)) throw "Reflection " + reflection + " not found."; | ||
| var inverseReflection = reflectionInstance.inverseOf(); | ||
| if (_underscore2.default.isUndefined(inverseReflection)) throw "Reflection " + reflection + " must have inverse."; | ||
| state = _extends({}, state, { | ||
| inverseReflection: inverseReflection, | ||
| queuedChanges: {}, | ||
| reflection: reflectionInstance, | ||
| updating: false | ||
| }); | ||
| } else { | ||
| state = _extends({}, state, { | ||
| queuedReflectionChanges: [] | ||
| }); | ||
| } | ||
| _this5.beforeSubmit = props.beforeSubmit; | ||
| _this5.state = state; | ||
| return _this5; | ||
| } | ||
| _createClass(Resource, [{ | ||
| key: 'componentWillReceiveProps', | ||
| value: function componentWillReceiveProps(nextProps) { | ||
| var afterUpdate = this.props.afterUpdate; | ||
| var inverseReflection = this.state.inverseReflection; | ||
| var _context = this.context, | ||
| afterUpdateRoot = _context.afterUpdateRoot, | ||
| queuedReflectionChanges = _context.queuedReflectionChanges, | ||
| shiftReflectionQueue = _context.shiftReflectionQueue; | ||
| this.setState({ resource: nextProps.subject }); | ||
| if (afterUpdate && !inverseReflection) { | ||
| this.setState({ updating: false }); | ||
| this.assignChanges(); | ||
| } else { | ||
| if (afterUpdateRoot && inverseReflection && queuedReflectionChanges[0] === this) { | ||
| shiftReflectionQueue(); | ||
| this.assignChanges(); | ||
| } | ||
| } | ||
| } | ||
| }, { | ||
| key: 'componentDidCatch', | ||
| value: function componentDidCatch(error) { | ||
| return _react2.default.createElement( | ||
| 'p', | ||
| null, | ||
| error | ||
| ); | ||
| } | ||
| }, { | ||
| key: 'afterUpdate', | ||
| value: function afterUpdate(newResource) { | ||
| var updateRoot = this.context.updateRoot; | ||
| var _state = this.state, | ||
| inverseReflection = _state.inverseReflection, | ||
| resource = _state.resource; | ||
| if (inverseReflection) { | ||
| var oldTarget = resource.association(inverseReflection.name).target; | ||
| var newTarget = newResource.association(inverseReflection.name).target; | ||
| if (inverseReflection.collection()) { | ||
| // FIXME: Allow autosave inverseOf collection to appropriately handle multiple resources in the collection, | ||
| // not just the first. If changing multiple fields of resource quickly, root may not be found in oldTarget | ||
| // because it has already been replaced by a previous change | ||
| // var index = oldTarget.indexOf(root); | ||
| // var newRoot = newTarget.get(index); | ||
| updateRoot(newTarget.first()); | ||
| } else { | ||
| updateRoot(newTarget); | ||
| } | ||
| } else { | ||
| this.updateRoot(newResource); | ||
| } | ||
| } | ||
| }, { | ||
| key: 'assignChanges', | ||
| value: function assignChanges() { | ||
| var _state2 = this.state, | ||
| queuedChanges = _state2.queuedChanges, | ||
| resource = _state2.resource; | ||
| if (_underscore2.default.keys(queuedChanges).length == 0) return; | ||
| var newResource = resource.assignAttributes(queuedChanges); | ||
| this.setState({ queuedChanges: {} }); | ||
| this.afterUpdate(newResource); | ||
| } | ||
| }, { | ||
| key: 'queueChange', | ||
| value: function queueChange(change) { | ||
| var _this6 = this; | ||
| var afterUpdate = this.props.afterUpdate; | ||
| var _state3 = this.state, | ||
| inverseReflection = _state3.inverseReflection, | ||
| queuedChanges = _state3.queuedChanges, | ||
| updating = _state3.updating; | ||
| this.setState({ | ||
| queuedChanges: _extends({}, queuedChanges, change) | ||
| }, function () { | ||
| var _context2 = _this6.context, | ||
| afterUpdateRoot = _context2.afterUpdateRoot, | ||
| queueReflectionChange = _context2.queueReflectionChange, | ||
| updatingRoot = _context2.updatingRoot; | ||
| if (afterUpdate || afterUpdateRoot) { | ||
| if (inverseReflection) { | ||
| if (updatingRoot) { | ||
| queueReflectionChange(_this6); | ||
| } else { | ||
| _this6.assignChanges(); | ||
| } | ||
| } else { | ||
| if (!updating) _this6.assignChanges(); | ||
| } | ||
| } else { | ||
| _this6.assignChanges(); | ||
| } | ||
| }); | ||
| } | ||
| }, { | ||
| key: 'queueReflectionChange', | ||
| value: function queueReflectionChange(resource) { | ||
| var queuedReflectionChanges = this.state.queuedReflectionChanges; | ||
| queuedReflectionChanges.push(resource); | ||
| this.setState({ queuedReflectionChanges: queuedReflectionChanges }); | ||
| } | ||
| }, { | ||
| key: 'shiftReflectionQueue', | ||
| value: function shiftReflectionQueue() { | ||
| var queuedReflectionChanges = this.state.queuedReflectionChanges; | ||
| queuedReflectionChanges.shift(); | ||
| this.setState({ queuedReflectionChanges: queuedReflectionChanges }); | ||
| } | ||
| }, { | ||
| key: 'getChildContext', | ||
| value: function getChildContext() { | ||
| var _props9 = this.props, | ||
| afterUpdate = _props9.afterUpdate, | ||
| parent = _props9.parent; | ||
| var root = this.context.root; | ||
| var _state4 = this.state, | ||
| resource = _state4.resource, | ||
| queuedReflectionChanges = _state4.queuedReflectionChanges, | ||
| updating = _state4.updating; | ||
| var childContext = { | ||
| afterUpdateRoot: afterUpdate, | ||
| isNestedResource: true, | ||
| queueChange: this.queueChange, | ||
| queuedReflectionChanges: queuedReflectionChanges, | ||
| queueReflectionChange: this.queueReflectionChange, | ||
| shiftReflectionQueue: this.shiftReflectionQueue, | ||
| root: parent || root || resource, | ||
| resource: resource, | ||
| updateRoot: this.updateRoot, | ||
| updatingRoot: updating | ||
| }; | ||
| return childContext; | ||
| } | ||
| }, { | ||
| key: 'handleSubmit', | ||
| value: function handleSubmit(e, callback) { | ||
| if (e) e.preventDefault(); | ||
| var _props10 = this.props, | ||
| onSubmit = _props10.onSubmit, | ||
| onInvalidSubmit = _props10.onInvalidSubmit; | ||
| var resource = this.state.resource; | ||
| var onSubmitCallback = function onSubmitCallback(resourceToSubmit) { | ||
| if (!_underscore2.default.isUndefined(onSubmit)) { | ||
| onSubmit(resourceToSubmit); | ||
| } | ||
| if (!_underscore2.default.isUndefined(callback)) { | ||
| callback(resourceToSubmit); | ||
| } | ||
| }; | ||
| var onInvalidSubmitCallback = function onInvalidSubmitCallback(invalidResource) { | ||
| if (!_underscore2.default.isUndefined(onInvalidSubmit)) { | ||
| onInvalidSubmit(invalidResource); | ||
| } | ||
| if (!_underscore2.default.isUndefined(callback)) { | ||
| callback(invalidResource); | ||
| } | ||
| }; | ||
| var beforeSubmit = this.beforeSubmit || this.componentRef && this.componentRef.beforeSubmit; | ||
| if (!_underscore2.default.isUndefined(beforeSubmit)) { | ||
| new Promise(function (resolve, reject) { | ||
| try { | ||
| var result = beforeSubmit(resource); | ||
| resolve(result); | ||
| } catch (invalid) { | ||
| reject(invalid); | ||
| } | ||
| }).then(onSubmitCallback).catch(onInvalidSubmitCallback); | ||
| } else { | ||
| onSubmitCallback(resource); | ||
| } | ||
| } | ||
| }, { | ||
| key: 'render', | ||
| value: function render() { | ||
| var _this7 = this; | ||
| var isNestedResource = this.context.isNestedResource; | ||
| var _props11 = this.props, | ||
| afterError = _props11.afterError, | ||
| children = _props11.children, | ||
| className = _props11.className, | ||
| component = _props11.component, | ||
| componentProps = _props11.componentProps, | ||
| componentRef = _props11.componentRef; | ||
| var resource = this.state.resource; | ||
| var body = void 0; | ||
| if (component) { | ||
| body = _react2.default.createElement(component, _extends({}, componentProps, { | ||
| afterUpdate: this.afterUpdate, | ||
| afterError: afterError, | ||
| onSubmit: this.handleSubmit, | ||
| subject: resource, | ||
| ref: function ref(c) { | ||
| _this7.componentRef = c; | ||
| componentRef(c); | ||
| } | ||
| })); | ||
| } else { | ||
| body = children; | ||
| } | ||
| if (isNestedResource) { | ||
| return _react2.default.createElement( | ||
| 'section', | ||
| { className: className }, | ||
| body | ||
| ); | ||
| } else { | ||
| return _react2.default.createElement( | ||
| 'form', | ||
| { className: className, onSubmit: this.handleSubmit }, | ||
| body | ||
| ); | ||
| } | ||
| } | ||
| }, { | ||
| key: 'updateRoot', | ||
| value: function updateRoot(newRoot) { | ||
| var afterUpdate = this.props.afterUpdate; | ||
| var resource = this.state.resource; | ||
| this.setState({ resource: newRoot }); | ||
| if (afterUpdate) { | ||
| afterUpdate(newRoot, resource); | ||
| this.setState({ updating: true }); | ||
| } | ||
| } | ||
| }]); | ||
| return Resource; | ||
| }(_react2.default.Component); | ||
| Resource.propTypes = { | ||
| afterError: _propTypes2.default.func, | ||
| afterUpdate: _propTypes2.default.func, | ||
| children: _propTypes2.default.oneOfType([_propTypes2.default.array, _propTypes2.default.node]), | ||
| className: _propTypes2.default.string, | ||
| component: _propTypes2.default.func, | ||
| componentProps: _propTypes2.default.object, | ||
| onInvalidSubmit: _propTypes2.default.func, | ||
| onSubmit: _propTypes2.default.func, | ||
| parent: _propTypes2.default.object, | ||
| reflection: _propTypes2.default.string, | ||
| subject: _propTypes2.default.object.isRequired | ||
| }; | ||
| Resource.contextTypes = { | ||
| afterUpdateRoot: _propTypes2.default.func, | ||
| isNestedResource: _propTypes2.default.bool, | ||
| queuedReflectionChanges: _propTypes2.default.array, | ||
| queueReflectionChange: _propTypes2.default.func, | ||
| shiftReflectionQueue: _propTypes2.default.func, | ||
| root: _propTypes2.default.object, | ||
| updateRoot: _propTypes2.default.func, | ||
| updatingRoot: _propTypes2.default.bool | ||
| }; | ||
| Resource.childContextTypes = { | ||
| afterUpdateRoot: _propTypes2.default.func, | ||
| isNestedResource: _propTypes2.default.bool, | ||
| queueChange: _propTypes2.default.func, | ||
| queuedReflectionChanges: _propTypes2.default.array, | ||
| queueReflectionChange: _propTypes2.default.func, | ||
| shiftReflectionQueue: _propTypes2.default.func, | ||
| resource: _propTypes2.default.object, | ||
| root: _propTypes2.default.object, | ||
| updateRoot: _propTypes2.default.func, | ||
| updatingRoot: _propTypes2.default.bool | ||
| }; | ||
| Resource.defaultProps = { | ||
| componentProps: {}, | ||
| componentRef: _underscore2.default.noop | ||
| }; | ||
| }); |
| import React from 'react'; | ||
| import PropTypes from 'prop-types'; | ||
| import ActiveResource from 'active-resource'; | ||
| import _ from 'underscore'; | ||
| export class Collection extends React.PureComponent { | ||
| static propTypes = { | ||
| children: PropTypes.oneOfType([ | ||
| PropTypes.array, | ||
| PropTypes.node, | ||
| ]), | ||
| className: PropTypes.string, | ||
| blankComponent: PropTypes.func, | ||
| component: PropTypes.func, | ||
| componentProps: PropTypes.object, | ||
| subject: PropTypes.oneOfType([ | ||
| PropTypes.object, | ||
| PropTypes.func, | ||
| ]).isRequired, | ||
| reflection: PropTypes.string, | ||
| }; | ||
| static defaultProps = { | ||
| inlineRows: false | ||
| }; | ||
| // link to global state by enabling afterLoad, afterAdd, afterRemove, afterUpdate callbacks that can call | ||
| // an action linked to dispatch | ||
| constructor() { | ||
| super(); | ||
| this.state = { | ||
| target: ActiveResource.prototype.Collection.build() | ||
| }; | ||
| _.bindAll(this, | ||
| 'buildOnTarget', | ||
| 'cloneTarget', | ||
| 'replaceOnTarget', | ||
| 'removeFromTarget', | ||
| ); | ||
| } | ||
| componentDidMount() { | ||
| this.setTarget(this.props); | ||
| } | ||
| componentWillReceiveProps(nextProps) { | ||
| this.setTarget(nextProps); | ||
| } | ||
| setTarget(props) { | ||
| const { subject } = props; | ||
| this.setState({ target: subject.target() }) | ||
| } | ||
| buildOnTarget(attributes) { | ||
| const { subject } = this.props; | ||
| let target = this.cloneTarget(); | ||
| target.push(subject.build(attributes)); | ||
| this.setState({ target: target }); | ||
| } | ||
| replaceOnTarget(newItem, oldItem) { | ||
| let target = this.cloneTarget(); | ||
| target.replace(oldItem, newItem); | ||
| return this.setState({ target }); | ||
| } | ||
| removeFromTarget(item) { | ||
| let target = this.cloneTarget(); | ||
| target.delete(item); | ||
| return this.setState({ target }); | ||
| } | ||
| cloneTarget() { | ||
| return this.state.target.clone(); | ||
| } | ||
| render() { | ||
| const { blankComponent, children, className, component, componentProps, reflection } = this.props; | ||
| const { target } = this.state; | ||
| return ( | ||
| <section className={ className }> | ||
| { | ||
| target.size() > 0 ? ( | ||
| target.map((t, indexOf) => | ||
| <Resource afterUpdate={this.replaceOnTarget} | ||
| component={component} componentProps={{...componentProps, indexOf}} | ||
| key={t.id || (t.klass().className + '-' + indexOf)} | ||
| reflection={reflection} | ||
| subject={t}> | ||
| {children} | ||
| </Resource> | ||
| ).toArray() | ||
| ) : (blankComponent != null && | ||
| blankComponent() | ||
| ) | ||
| } | ||
| </section> | ||
| ); | ||
| } | ||
| } | ||
| export class ErrorsFor extends React.Component { | ||
| static propTypes = { | ||
| component: PropTypes.func, | ||
| field: PropTypes.string, | ||
| }; | ||
| static contextTypes = { | ||
| resource: PropTypes.object, | ||
| }; | ||
| shouldComponentUpdate(nextProps, nextState, nextContext) { | ||
| return !(shallowEqual(this.props, nextProps) && shallowEqual(this.state, nextState) && shallowEqual(this.context, nextContext)); | ||
| } | ||
| render() { | ||
| const { resource } = this.context; | ||
| const { component, field } = this.props; | ||
| var errors = resource.errors().forField(field); | ||
| if(errors.empty()) return null; | ||
| let customProps = _.omit(this.props, _.keys(ErrorsFor.propTypes)); | ||
| let finalComponent = component || 'summary'; | ||
| return React.createElement(finalComponent, { | ||
| ...customProps, | ||
| key: field, | ||
| }, | ||
| errors.map((error) => { | ||
| return <span key={ error.code }>{ error.message }</span> | ||
| }).toArray() | ||
| ); | ||
| } | ||
| }; | ||
| import classNames from 'classnames'; | ||
| import shallowEqual from 'shallowequal'; | ||
| export class Field extends React.Component { | ||
| static contextTypes = { | ||
| changeRadio: PropTypes.func, | ||
| queueChange: PropTypes.func, | ||
| radioValue: PropTypes.any, | ||
| resource: PropTypes.object, | ||
| }; | ||
| static childContextTypes = { | ||
| changeRadio: PropTypes.func, | ||
| radioValue: PropTypes.any, | ||
| }; | ||
| static propTypes = { | ||
| className: PropTypes.string, | ||
| component: PropTypes.func, | ||
| includeBlank: PropTypes.bool, | ||
| name: PropTypes.string.isRequired, | ||
| options: PropTypes.instanceOf(ActiveResource.Collection), | ||
| optionsLabel: PropTypes.oneOfType([ | ||
| PropTypes.string, | ||
| PropTypes.func, | ||
| ]), | ||
| type: PropTypes.string.isRequired, | ||
| uncheckedValue: PropTypes.oneOfType([ | ||
| PropTypes.object, | ||
| PropTypes.func, | ||
| PropTypes.string, | ||
| PropTypes.number, | ||
| PropTypes.bool, | ||
| ]), | ||
| invalidClassName: PropTypes.string, | ||
| value: PropTypes.oneOfType([ | ||
| PropTypes.object, | ||
| PropTypes.func, | ||
| PropTypes.string, | ||
| PropTypes.number, | ||
| PropTypes.bool, | ||
| ]) | ||
| }; | ||
| constructor() { | ||
| super(); | ||
| _.bindAll(this, | ||
| 'afterChange', | ||
| 'changeRadio', | ||
| 'classNames', | ||
| 'commonInputProps', | ||
| 'customInputProps', | ||
| 'getValue', | ||
| 'handleChange', | ||
| 'renderCheckboxComponent', | ||
| 'renderInputComponent', | ||
| 'renderRadioComponent', | ||
| 'renderSelectComponent', | ||
| 'renderTextareaComponent', | ||
| 'setValue', | ||
| 'valueFor', | ||
| ); | ||
| this.state = {}; | ||
| } | ||
| shouldComponentUpdate(nextProps, nextState, nextContext) { | ||
| return !(shallowEqual(this.props, nextProps) && shallowEqual(this.state, nextState) && shallowEqual(this.context, nextContext)); | ||
| } | ||
| getChildContext() { | ||
| const { type } = this.props; | ||
| const { value } = this.state; | ||
| switch(type) { | ||
| case 'radioGroup': | ||
| return { | ||
| changeRadio: this.changeRadio, | ||
| radioValue: value, | ||
| }; | ||
| } | ||
| } | ||
| changeRadio(value) { | ||
| this.setState({ value }); | ||
| } | ||
| componentWillMount() { | ||
| const { type } = this.props; | ||
| const { resource } = this.context; | ||
| // Set initial value to that of the resources | ||
| this.setState({ | ||
| resource, | ||
| value: this.valueFor(resource, this.props) | ||
| }); | ||
| switch(type) { | ||
| case 'email': | ||
| case 'number': | ||
| case 'text': | ||
| case 'textarea': | ||
| this.afterChange = _.debounce(this.afterChange, 500); | ||
| } | ||
| } | ||
| componentDidUpdate(prevProps, prevState) { | ||
| const { resource: prevResource } = prevState | ||
| const { resource } = this.context | ||
| if(prevResource !== resource) { | ||
| this.setState({ resource }) | ||
| } | ||
| if(!(_.isNull(prevResource.id) || _.isUndefined(prevResource.id)) && prevResource.id !== resource.id) { | ||
| this.setState({ | ||
| value: this.valueFor(resource, this.props) | ||
| }) | ||
| } | ||
| } | ||
| classNames() { | ||
| const { className, invalidClassName, name } = this.props; | ||
| const { resource } = this.context; | ||
| return classNames( | ||
| className, | ||
| { | ||
| [invalidClassName]: !resource.errors().forField(name).empty() | ||
| } | ||
| ); | ||
| } | ||
| commonInputProps() { | ||
| const { name } = this.props; | ||
| let props = { | ||
| className: this.classNames(), | ||
| key: name, | ||
| name, | ||
| onChange: this.handleChange, | ||
| }; | ||
| return props; | ||
| } | ||
| componentFor(type) { | ||
| switch(type) { | ||
| case 'checkbox': | ||
| return this.renderCheckboxComponent(); | ||
| case 'radio': | ||
| return this.renderRadioComponent(); | ||
| case 'radioGroup': | ||
| return this.renderRadioGroupComponent(); | ||
| case 'select': | ||
| return this.renderSelectComponent(); | ||
| case 'textarea': | ||
| return this.renderTextareaComponent(); | ||
| default: | ||
| return this.renderInputComponent(); | ||
| } | ||
| } | ||
| // @note type='radio' will pass down +name+ prop | ||
| // @note type='select' will only pass down +type+ prop if +component+ prop is defined | ||
| customInputProps() { | ||
| const { component, type } = this.props; | ||
| var omittedProps; | ||
| switch(type) { | ||
| case 'radio': | ||
| omittedProps = _.omit(Field.propTypes, ['type', 'name']); | ||
| break; | ||
| case 'select': | ||
| omittedProps = component ? _.omit(Field.propTypes, 'type') : Field.propTypes; | ||
| break; | ||
| default: | ||
| omittedProps = _.omit(Field.propTypes, 'type'); | ||
| } | ||
| return _.omit(this.props, _.keys(omittedProps)); | ||
| } | ||
| // TODO: Add support for non-resource options on select and radioGroup | ||
| valueFor(resource, props) { | ||
| const { name, type, uncheckedValue, value } = props; | ||
| switch(type) { | ||
| case 'checkbox': | ||
| var resourceValue = resource[name]; | ||
| if(resourceValue == value) { | ||
| return true; | ||
| } else if(resourceValue == uncheckedValue || _.isUndefined(resourceValue) || _.isNull(resourceValue)) { | ||
| return false; | ||
| } else { | ||
| throw 'Field ' + name + ' with value ' + resource[name] + ' does not match value or uncheckedValue for checkbox' | ||
| } | ||
| case 'radioGroup': | ||
| case 'select': | ||
| var val = resource[name](); | ||
| return val ? val.id : ''; | ||
| default: | ||
| var val = resource[name]; | ||
| return val ? val : ''; | ||
| } | ||
| } | ||
| render() { | ||
| const { type } = this.props; | ||
| return this.componentFor(type); | ||
| } | ||
| renderCheckboxComponent() { | ||
| const { component } = this.props; | ||
| let finalComponent = component || 'input'; | ||
| return React.createElement(finalComponent, { | ||
| ...this.commonInputProps(), | ||
| ...this.customInputProps(), | ||
| checked: this.state.value, | ||
| }); | ||
| } | ||
| renderInputComponent() { | ||
| const { component } = this.props; | ||
| let finalComponent = component || 'input'; | ||
| return React.createElement(finalComponent, { | ||
| ...this.commonInputProps(), | ||
| ...this.customInputProps(), | ||
| value: this.state.value, | ||
| }); | ||
| } | ||
| renderRadioComponent() { | ||
| const { component, value } = this.props; | ||
| const { radioValue } = this.context; | ||
| if (_.isUndefined(value)) { | ||
| throw 'Input type="radio" must have prop "value"'; | ||
| } | ||
| let finalComponent = component || 'input'; | ||
| return React.createElement(finalComponent, { | ||
| ...this.commonInputProps(), | ||
| ...this.customInputProps(), | ||
| checked: value.id == radioValue, | ||
| value: value.id, | ||
| name: value.questionId | ||
| }); | ||
| } | ||
| renderRadioGroupComponent() { | ||
| return <div> | ||
| { this.props.children } | ||
| </div>; | ||
| } | ||
| renderSelectComponent() { | ||
| const { component, includeBlank, options, optionsLabel } = this.props; | ||
| let selectOptions = null; | ||
| if (options.empty()) { | ||
| throw 'Input type="select" must have options'; | ||
| } else { | ||
| selectOptions = options.map((o) => { | ||
| return <option key={o.id} value={o.id}> | ||
| { | ||
| _.isString(optionsLabel) ? ( | ||
| o[optionsLabel] | ||
| ) : ( | ||
| optionsLabel(o) | ||
| ) | ||
| } | ||
| </option>; | ||
| }); | ||
| if (includeBlank) { | ||
| selectOptions.unshift(<option key={-1} value=''></option>); | ||
| } | ||
| } | ||
| let finalComponent = component || 'select'; | ||
| return React.createElement(finalComponent, { | ||
| ...this.commonInputProps(), | ||
| ...this.customInputProps(), | ||
| value: this.state.value, | ||
| }, selectOptions.toArray()); | ||
| } | ||
| renderTextareaComponent() { | ||
| const { component } = this.props; | ||
| let finalComponent = component || 'textarea'; | ||
| return React.createElement(finalComponent, { | ||
| ...this.commonInputProps(), | ||
| ...this.customInputProps(), | ||
| value: this.state.value, | ||
| }); | ||
| } | ||
| handleChange(e) { | ||
| e.persist(); | ||
| const { max, min, type } = this.props; | ||
| const { changeRadio } = this.context; | ||
| let value; | ||
| switch(type) { | ||
| case 'checkbox': | ||
| value = e.target.checked; | ||
| break; | ||
| case 'number': | ||
| if(e.target.value > max) { | ||
| value = max; | ||
| } else if(e.target.value < min) { | ||
| value = min; | ||
| } else { | ||
| value = e.target.value || min; | ||
| } | ||
| break; | ||
| case 'radio': | ||
| changeRadio(e.target.value); | ||
| break; | ||
| default: | ||
| value = e.target.value; | ||
| } | ||
| this.setState({ value }, this.afterChange); | ||
| } | ||
| afterChange() { | ||
| const { name, type, options, uncheckedValue, value } = this.props; | ||
| const { value: stateValue } = this.state; | ||
| const { queueChange } = this.context; | ||
| let mappedValue; | ||
| switch(type) { | ||
| case 'checkbox': | ||
| if(stateValue) { | ||
| mappedValue = value; | ||
| } else { | ||
| mappedValue = uncheckedValue; | ||
| } | ||
| break; | ||
| case 'radio': | ||
| mappedValue = value; | ||
| break; | ||
| case 'select': | ||
| mappedValue = options.detect((o) => o.id === stateValue); | ||
| break; | ||
| default: | ||
| mappedValue = stateValue; | ||
| } | ||
| queueChange({ [name]: mappedValue }); | ||
| } | ||
| getValue() { | ||
| return this.state.value; | ||
| } | ||
| setValue(value) { | ||
| const { type } = this.props; | ||
| let mappedValue = { persist: _.noop }; | ||
| switch(type) { | ||
| case 'checkbox': | ||
| mappedValue = { ...mappedValue, target: { checked: value } }; | ||
| break; | ||
| default: | ||
| mappedValue = { ...mappedValue, target: { value } }; | ||
| } | ||
| this.handleChange(mappedValue); | ||
| } | ||
| } | ||
| export class Resource extends React.Component { | ||
| static propTypes = { | ||
| afterError: PropTypes.func, | ||
| afterUpdate: PropTypes.func, | ||
| children: PropTypes.oneOfType([PropTypes.array, PropTypes.node]), | ||
| className: PropTypes.string, | ||
| component: PropTypes.func, | ||
| componentProps: PropTypes.object, | ||
| onInvalidSubmit: PropTypes.func, | ||
| onSubmit: PropTypes.func, | ||
| parent: PropTypes.object, | ||
| reflection: PropTypes.string, | ||
| subject: PropTypes.object.isRequired, | ||
| }; | ||
| static contextTypes = { | ||
| afterUpdateRoot: PropTypes.func, | ||
| isNestedResource: PropTypes.bool, | ||
| queuedReflectionChanges: PropTypes.array, | ||
| queueReflectionChange: PropTypes.func, | ||
| shiftReflectionQueue: PropTypes.func, | ||
| root: PropTypes.object, | ||
| updateRoot: PropTypes.func, | ||
| updatingRoot: PropTypes.bool, | ||
| }; | ||
| static childContextTypes = { | ||
| afterUpdateRoot: PropTypes.func, | ||
| isNestedResource: PropTypes.bool, | ||
| queueChange: PropTypes.func, | ||
| queuedReflectionChanges: PropTypes.array, | ||
| queueReflectionChange: PropTypes.func, | ||
| shiftReflectionQueue: PropTypes.func, | ||
| resource: PropTypes.object, | ||
| root: PropTypes.object, | ||
| updateRoot: PropTypes.func, | ||
| updatingRoot: PropTypes.bool, | ||
| }; | ||
| static defaultProps = { | ||
| componentProps: {}, | ||
| componentRef: _.noop, | ||
| }; | ||
| constructor(props, context) { | ||
| super(); | ||
| _.bindAll( | ||
| this, | ||
| "afterUpdate", | ||
| "assignChanges", | ||
| "queueReflectionChange", | ||
| "shiftReflectionQueue", | ||
| "queueChange", | ||
| "handleSubmit", | ||
| "updateRoot" | ||
| ); | ||
| const root = context.root; | ||
| const { parent, reflection, subject } = props; | ||
| let state = { resource: subject }; | ||
| if (reflection) { | ||
| var reflectionInstance = (root || parent).klass().reflectOnAssociation(reflection); | ||
| if (_.isUndefined(reflectionInstance)) throw "Reflection " + reflection + " not found."; | ||
| var inverseReflection = reflectionInstance.inverseOf(); | ||
| if (_.isUndefined(inverseReflection)) throw "Reflection " + reflection + " must have inverse."; | ||
| state = { | ||
| ...state, | ||
| inverseReflection, | ||
| queuedChanges: {}, | ||
| reflection: reflectionInstance, | ||
| updating: false, | ||
| }; | ||
| } else { | ||
| state = { | ||
| ...state, | ||
| queuedReflectionChanges: [], | ||
| }; | ||
| } | ||
| this.beforeSubmit = props.beforeSubmit; | ||
| this.state = state; | ||
| } | ||
| componentWillReceiveProps(nextProps) { | ||
| const { afterUpdate } = this.props; | ||
| const { inverseReflection } = this.state; | ||
| const { afterUpdateRoot, queuedReflectionChanges, shiftReflectionQueue } = this.context; | ||
| this.setState({ resource: nextProps.subject }); | ||
| if (afterUpdate && !inverseReflection) { | ||
| this.setState({ updating: false }); | ||
| this.assignChanges(); | ||
| } else { | ||
| if (afterUpdateRoot && inverseReflection && queuedReflectionChanges[0] === this) { | ||
| shiftReflectionQueue(); | ||
| this.assignChanges(); | ||
| } | ||
| } | ||
| } | ||
| componentDidCatch(error) { | ||
| return <p>{error}</p>; | ||
| } | ||
| afterUpdate(newResource) { | ||
| const { updateRoot } = this.context; | ||
| const { inverseReflection, resource } = this.state; | ||
| if (inverseReflection) { | ||
| var oldTarget = resource.association(inverseReflection.name).target; | ||
| var newTarget = newResource.association(inverseReflection.name).target; | ||
| if (inverseReflection.collection()) { | ||
| // FIXME: Allow autosave inverseOf collection to appropriately handle multiple resources in the collection, | ||
| // not just the first. If changing multiple fields of resource quickly, root may not be found in oldTarget | ||
| // because it has already been replaced by a previous change | ||
| // var index = oldTarget.indexOf(root); | ||
| // var newRoot = newTarget.get(index); | ||
| updateRoot(newTarget.first()); | ||
| } else { | ||
| updateRoot(newTarget); | ||
| } | ||
| } else { | ||
| this.updateRoot(newResource); | ||
| } | ||
| } | ||
| assignChanges() { | ||
| const { queuedChanges, resource } = this.state; | ||
| if (_.keys(queuedChanges).length == 0) return; | ||
| var newResource = resource.assignAttributes(queuedChanges); | ||
| this.setState({ queuedChanges: {} }); | ||
| this.afterUpdate(newResource); | ||
| } | ||
| queueChange(change) { | ||
| const { afterUpdate } = this.props; | ||
| const { inverseReflection, queuedChanges, updating } = this.state; | ||
| this.setState( | ||
| { | ||
| queuedChanges: { | ||
| ...queuedChanges, | ||
| ...change, | ||
| }, | ||
| }, | ||
| () => { | ||
| const { afterUpdateRoot, queueReflectionChange, updatingRoot } = this.context; | ||
| if (afterUpdate || afterUpdateRoot) { | ||
| if (inverseReflection) { | ||
| if (updatingRoot) { | ||
| queueReflectionChange(this); | ||
| } else { | ||
| this.assignChanges(); | ||
| } | ||
| } else { | ||
| if (!updating) this.assignChanges(); | ||
| } | ||
| } else { | ||
| this.assignChanges(); | ||
| } | ||
| } | ||
| ); | ||
| } | ||
| queueReflectionChange(resource) { | ||
| let { queuedReflectionChanges } = this.state; | ||
| queuedReflectionChanges.push(resource); | ||
| this.setState({ queuedReflectionChanges }); | ||
| } | ||
| shiftReflectionQueue() { | ||
| let { queuedReflectionChanges } = this.state; | ||
| queuedReflectionChanges.shift(); | ||
| this.setState({ queuedReflectionChanges }); | ||
| } | ||
| getChildContext() { | ||
| const { afterUpdate, parent } = this.props; | ||
| const { root } = this.context; | ||
| const { resource, queuedReflectionChanges, updating } = this.state; | ||
| let childContext = { | ||
| afterUpdateRoot: afterUpdate, | ||
| isNestedResource: true, | ||
| queueChange: this.queueChange, | ||
| queuedReflectionChanges: queuedReflectionChanges, | ||
| queueReflectionChange: this.queueReflectionChange, | ||
| shiftReflectionQueue: this.shiftReflectionQueue, | ||
| root: parent || root || resource, | ||
| resource, | ||
| updateRoot: this.updateRoot, | ||
| updatingRoot: updating, | ||
| }; | ||
| return childContext; | ||
| } | ||
| handleSubmit(e, callback) { | ||
| if (e) e.preventDefault(); | ||
| const { onSubmit, onInvalidSubmit } = this.props; | ||
| const { resource } = this.state; | ||
| var onSubmitCallback = (resourceToSubmit) => { | ||
| if (!_.isUndefined(onSubmit)) { | ||
| onSubmit(resourceToSubmit); | ||
| } | ||
| if (!_.isUndefined(callback)) { | ||
| callback(resourceToSubmit); | ||
| } | ||
| }; | ||
| var onInvalidSubmitCallback = (invalidResource) => { | ||
| if (!_.isUndefined(onInvalidSubmit)) { | ||
| onInvalidSubmit(invalidResource); | ||
| } | ||
| if (!_.isUndefined(callback)) { | ||
| callback(invalidResource); | ||
| } | ||
| }; | ||
| let beforeSubmit = this.beforeSubmit || (this.componentRef && this.componentRef.beforeSubmit); | ||
| if (!_.isUndefined(beforeSubmit)) { | ||
| new Promise((resolve, reject) => { | ||
| try { | ||
| var result = beforeSubmit(resource); | ||
| resolve(result); | ||
| } catch (invalid) { | ||
| reject(invalid); | ||
| } | ||
| }) | ||
| .then(onSubmitCallback) | ||
| .catch(onInvalidSubmitCallback); | ||
| } else { | ||
| onSubmitCallback(resource); | ||
| } | ||
| } | ||
| render() { | ||
| const { isNestedResource } = this.context; | ||
| const { afterError, children, className, component, componentProps, componentRef } = this.props; | ||
| const { resource } = this.state; | ||
| let body; | ||
| if (component) { | ||
| body = React.createElement(component, { | ||
| ...componentProps, | ||
| afterUpdate: this.afterUpdate, | ||
| afterError, | ||
| onSubmit: this.handleSubmit, | ||
| subject: resource, | ||
| ref: (c) => { | ||
| this.componentRef = c; | ||
| componentRef(c); | ||
| }, | ||
| }); | ||
| } else { | ||
| body = children; | ||
| } | ||
| if (isNestedResource) { | ||
| return <section className={className}>{body}</section>; | ||
| } else { | ||
| return ( | ||
| <form className={className} onSubmit={this.handleSubmit}> | ||
| {body} | ||
| </form> | ||
| ); | ||
| } | ||
| } | ||
| updateRoot(newRoot) { | ||
| const { afterUpdate } = this.props; | ||
| const { resource } = this.state; | ||
| this.setState({ resource: newRoot }); | ||
| if (afterUpdate) { | ||
| afterUpdate(newRoot, resource); | ||
| this.setState({ updating: true }); | ||
| } | ||
| } | ||
| } |
+100
| module.exports = function(grunt) { | ||
| require('load-grunt-tasks')(grunt); | ||
| // configure the tasks | ||
| grunt.initConfig({ | ||
| pkg: grunt.file.readJSON('package.json'), | ||
| clean: { | ||
| dist: { | ||
| src: [ 'dist' ] | ||
| }, | ||
| specs: { | ||
| src: 'spec/spec.js' | ||
| } | ||
| }, | ||
| babel: { | ||
| options: { | ||
| presets: ['env'], | ||
| plugins: [ | ||
| 'transform-class-properties', | ||
| 'transform-es2015-modules-umd', | ||
| 'transform-object-rest-spread', | ||
| 'transform-react-jsx' | ||
| ] | ||
| }, | ||
| build: { | ||
| files: { | ||
| 'build/mitragyna.js': 'build/mitragyna.jsx' | ||
| } | ||
| }, | ||
| specs: { | ||
| files: { | ||
| 'spec/spec.js': [ 'spec/**/*.jsx' ] | ||
| } | ||
| } | ||
| }, | ||
| concat: { | ||
| release: { | ||
| options: { | ||
| banner: | ||
| '/*\n' + | ||
| '\tmitragyna <%= pkg.version %>\n' + | ||
| '\t(c) <%= grunt.template.today("yyyy") %> Nick Landgrebe\n' + | ||
| '\tmitragyna may be freely distributed under the MIT license\n' + | ||
| '*/\n\n' | ||
| }, | ||
| files: { | ||
| 'dist/mitragyna.js': ['build/mitragyna.js'], | ||
| 'dist/mitragyna.min.js': ['build/mitragyna.min.js'], | ||
| 'dist/mitragyna.min.js.map': ['build/mitragyna.min.js.map'] | ||
| } | ||
| }, | ||
| build: { | ||
| files: { | ||
| 'build/mitragyna.jsx': 'src/**/*.jsx' | ||
| } | ||
| } | ||
| }, | ||
| connect: { | ||
| test: { | ||
| options: { | ||
| port: 8000 | ||
| } | ||
| } | ||
| } | ||
| }); | ||
| // load the tasks | ||
| grunt.loadNpmTasks('grunt-contrib-clean'); | ||
| grunt.loadNpmTasks('grunt-contrib-concat'); | ||
| grunt.loadNpmTasks('grunt-contrib-uglify'); | ||
| grunt.loadNpmTasks('grunt-umd'); | ||
| grunt.loadNpmTasks('grunt-contrib-watch'); | ||
| grunt.loadNpmTasks('grunt-contrib-connect'); | ||
| // define the tasks | ||
| grunt.registerTask( | ||
| 'spec', | ||
| 'Compiles and runs the Javascript spec files for source code.', | ||
| [ 'clean:specs', 'babel:specs', 'connect:test' ] | ||
| ); | ||
| grunt.registerTask( | ||
| 'build', | ||
| 'Creates a build of the library in the build folder, then runs the specs on it.', | ||
| [ 'concat:build', 'babel:build' ] | ||
| ); | ||
| grunt.registerTask( | ||
| 'release', | ||
| 'Creates a new release of the library in the dist folder', | ||
| [ 'clean:dist', 'build', 'concat:release' ] | ||
| ); | ||
| grunt.registerTask( | ||
| 'default', | ||
| 'Watches the project for changes, automatically builds them and runs specs.', | ||
| [ 'build', 'watch' ] | ||
| ); | ||
| }; |
+76
| ActiveResource.js binding framework for React components | ||
| ## Installation | ||
| ```javascript | ||
| yarn add @getoccasion/mitragyna | ||
| ``` | ||
| You can also use the CDN address https://unpkg.com/@getoccasion/mitragyna to add it to your AMD loader or into your page: | ||
| ```html | ||
| <script type="text/javascript" src="https://unpkg.com/@getoccasion/mitragyna"></script> | ||
| ``` | ||
| ## Getting Started | ||
| 1. Set a resource, and attach a child component to render with that subject | ||
| ```jsx | ||
| <Resource component={Customer} reflection="customer" subject={customer} /> | ||
| ``` | ||
| 2. Have a field in the child component that responds to the subject from the parent Resource | ||
| ```jsx | ||
| <Field | ||
| type="email" | ||
| name="email" | ||
| id="email" | ||
| component={Input} | ||
| invalidClassName="is-invalid" | ||
| placeholder="jane.doe@example.com" | ||
| /> | ||
| <ErrorsFor className="customer-email-errors" component={FormFeedback} field="email" /> | ||
| ``` | ||
| 3. You can nest Resources with setting a reflection | ||
| ```jsx | ||
| <Resource component={Customer} reflection="customer" subject={customer} /> | ||
| ``` | ||
| 4. Bind some callback logic to the (root) resource | ||
| - afterError: PropTypes.func | ||
| - afterUpdate: PropTypes.func | ||
| - onInvalidSubmit: PropTypes.func | ||
| - onSubmit: PropTypes.func | ||
| - beforeSubmit: PropTypes.func | ||
| ### If using nested resources | ||
| ```jsx | ||
| <Resource component={Customer} reflection="customer" subject={subject.customer()} parent={subject} /> | ||
| ``` | ||
| ### Binding and updating values from Fields | ||
| ```jsx | ||
| const fulfillmentType = useRef() | ||
| // Render <Input> component for value that keeps subject up to date | ||
| <Field | ||
| type="hidden" | ||
| name="fulfillmentType" | ||
| id="fulfillmentType" | ||
| component={Input} | ||
| ref={fulfillmentType} | ||
| /> | ||
| // Get value | ||
| fulfillmentType.current && fulfillmentType.current.state.value | ||
| // Update value | ||
| fulfillmentType.current && fulfillmentType.current.setValue('value here') | ||
| ``` |
+6
-22
@@ -1,29 +0,13 @@ | ||
| ## [0.1.2] - 2020-05-07 | ||
| ## [0.2.0] - 2020-05-013 | ||
| ### Added | ||
| - add active resource as a peer dependency | ||
| ### Fixed | ||
| - main pointing to index.js | ||
| - Allow boolean for value and uncheckedValue prop | ||
| ### Removed | ||
| ### Added | ||
| - Underscore dependency | ||
| - Add support for multi-nested Resources with the `parent={}` prop | ||
| ## [0.1.1] - 2020-05-07 | ||
| ## [0.0.3] | ||
| ### Fixed | ||
| - use Component to allow impure resources | ||
| ## [0.1.0] - 2020-05-07 | ||
| ### Fixed | ||
| - Allow boolean for value and uncheckedValue prop | ||
| ### Removed | ||
| - Build with grunt, now using src as a main | ||
| - No changelog |
+26
-11
| { | ||
| "name": "@getoccasion/mitragyna", | ||
| "version": "0.1.2", | ||
| "version": "0.2.0", | ||
| "description": "A library for managing ActiveResource.js as React components", | ||
| "main": "src/index.js", | ||
| "main": "build/mitragyna.js", | ||
| "scripts": { | ||
| "test": "grunt spec", | ||
| "develop": "grunt", | ||
| "build": "grunt build", | ||
| "version": "npm version", | ||
| "publish": "npm publish --access public" | ||
| }, | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "git+https://github.com/getOccasion/mitragyna.git" | ||
| "url": "git+https://github.com/getoccasion/mitragyna.git" | ||
| }, | ||
@@ -14,8 +21,8 @@ "keywords": [ | ||
| ], | ||
| "author": "Nick Landgrebe, Kieran Klaassen & Pelle ten Cate", | ||
| "author": "Nick Landgrebe, Pelle ten Cate & Kieran Klaassen", | ||
| "license": "MIT", | ||
| "bugs": { | ||
| "url": "https://github.com/getOccasion/mitragyna/issues" | ||
| "url": "https://github.com/getoccasion/mitragyna/issues" | ||
| }, | ||
| "homepage": "https://github.com/getOccasion/mitragyna#readme", | ||
| "homepage": "https://github.com/getoccasion/mitragyna#readme", | ||
| "devDependencies": { | ||
@@ -27,13 +34,21 @@ "babel-core": "^6.26.0", | ||
| "babel-plugin-transform-react-jsx": "^6.24.1", | ||
| "babel-preset-env": "^1.6.1" | ||
| "babel-preset-env": "^1.6.1", | ||
| "grunt": "0.x.x", | ||
| "grunt-babel": "^7.0.0", | ||
| "grunt-contrib-clean": "0.5.x", | ||
| "grunt-contrib-concat": "^1.0.1", | ||
| "grunt-contrib-connect": "0.4.x", | ||
| "grunt-contrib-uglify": "^3.3.0", | ||
| "grunt-contrib-watch": "0.5.x", | ||
| "grunt-umd": "^2.4.0", | ||
| "load-grunt-tasks": "^3.5.2" | ||
| }, | ||
| "dependencies": { | ||
| "active-resource": "GetOccasion/activeresource.js#track_local_changes", | ||
| "classnames": "^2.2.5", | ||
| "prop-types": "^15.6.0", | ||
| "react": "^16.2.0", | ||
| "shallowequal": "^1.0.2" | ||
| }, | ||
| "peerDependencies": { | ||
| "active-resource": "GetOccasion/activeresource.js#track_local_changes" | ||
| "shallowequal": "^1.0.2", | ||
| "underscore": "^1.8.3" | ||
| } | ||
| } |
+49
-34
@@ -1,8 +0,12 @@ | ||
| import React from "react"; | ||
| import PropTypes from "prop-types"; | ||
| import ActiveResource from "active-resource"; | ||
| import React from 'react'; | ||
| import PropTypes from 'prop-types'; | ||
| import ActiveResource from 'active-resource'; | ||
| import _ from 'underscore'; | ||
| export default class Collection extends React.PureComponent { | ||
| export class Collection extends React.PureComponent { | ||
| static propTypes = { | ||
| children: PropTypes.oneOfType([PropTypes.array, PropTypes.node]), | ||
| children: PropTypes.oneOfType([ | ||
| PropTypes.array, | ||
| PropTypes.node, | ||
| ]), | ||
| className: PropTypes.string, | ||
@@ -12,3 +16,6 @@ blankComponent: PropTypes.func, | ||
| componentProps: PropTypes.object, | ||
| subject: PropTypes.oneOfType([PropTypes.object, PropTypes.func]).isRequired, | ||
| subject: PropTypes.oneOfType([ | ||
| PropTypes.object, | ||
| PropTypes.func, | ||
| ]).isRequired, | ||
| reflection: PropTypes.string, | ||
@@ -18,3 +25,3 @@ }; | ||
| static defaultProps = { | ||
| inlineRows: false, | ||
| inlineRows: false | ||
| }; | ||
@@ -29,4 +36,11 @@ | ||
| this.state = { | ||
| target: ActiveResource.prototype.Collection.build(), | ||
| target: ActiveResource.prototype.Collection.build() | ||
| }; | ||
| _.bindAll(this, | ||
| 'buildOnTarget', | ||
| 'cloneTarget', | ||
| 'replaceOnTarget', | ||
| 'removeFromTarget', | ||
| ); | ||
| } | ||
@@ -45,6 +59,6 @@ | ||
| this.setState({ target: subject.target() }); | ||
| this.setState({ target: subject.target() }) | ||
| } | ||
| buildOnTarget = (attributes) => { | ||
| buildOnTarget(attributes) { | ||
| const { subject } = this.props; | ||
@@ -56,5 +70,5 @@ let target = this.cloneTarget(); | ||
| this.setState({ target: target }); | ||
| }; | ||
| } | ||
| replaceOnTarget = (newItem, oldItem) => { | ||
| replaceOnTarget(newItem, oldItem) { | ||
| let target = this.cloneTarget(); | ||
@@ -65,5 +79,5 @@ | ||
| return this.setState({ target }); | ||
| }; | ||
| } | ||
| removeFromTarget = (item) => { | ||
| removeFromTarget(item) { | ||
| let target = this.cloneTarget(); | ||
@@ -74,7 +88,7 @@ | ||
| return this.setState({ target }); | ||
| }; | ||
| } | ||
| cloneTarget = () => { | ||
| cloneTarget() { | ||
| return this.state.target.clone(); | ||
| }; | ||
| } | ||
@@ -86,19 +100,20 @@ render() { | ||
| return ( | ||
| <section className={className}> | ||
| {target.size() > 0 | ||
| ? target | ||
| .map((t, indexOf) => ( | ||
| <Resource | ||
| afterUpdate={this.replaceOnTarget} | ||
| component={component} | ||
| componentProps={{ ...componentProps, indexOf }} | ||
| key={t.id || t.klass().className + "-" + indexOf} | ||
| reflection={reflection} | ||
| subject={t} | ||
| > | ||
| {children} | ||
| </Resource> | ||
| )) | ||
| .toArray() | ||
| : blankComponent != null && blankComponent()} | ||
| <section className={ className }> | ||
| { | ||
| target.size() > 0 ? ( | ||
| target.map((t, indexOf) => | ||
| <Resource afterUpdate={this.replaceOnTarget} | ||
| component={component} componentProps={{...componentProps, indexOf}} | ||
| key={t.id || (t.klass().className + '-' + indexOf)} | ||
| reflection={reflection} | ||
| subject={t}> | ||
| {children} | ||
| </Resource> | ||
| ).toArray() | ||
| ) : (blankComponent != null && | ||
| blankComponent() | ||
| ) | ||
| } | ||
| </section> | ||
@@ -105,0 +120,0 @@ ); |
+12
-20
@@ -1,2 +0,2 @@ | ||
| export default class ErrorsFor extends React.Component { | ||
| export class ErrorsFor extends React.Component { | ||
| static propTypes = { | ||
@@ -12,7 +12,3 @@ component: PropTypes.func, | ||
| shouldComponentUpdate(nextProps, nextState, nextContext) { | ||
| return !( | ||
| shallowEqual(this.props, nextProps) && | ||
| shallowEqual(this.state, nextState) && | ||
| shallowEqual(this.context, nextContext) | ||
| ); | ||
| return !(shallowEqual(this.props, nextProps) && shallowEqual(this.state, nextState) && shallowEqual(this.context, nextContext)); | ||
| } | ||
@@ -26,20 +22,16 @@ | ||
| if (errors.empty()) return null; | ||
| if(errors.empty()) return null; | ||
| let customProps = _.omit(this.props, _.keys(ErrorsFor.propTypes)); | ||
| let finalComponent = component || "summary"; | ||
| return React.createElement( | ||
| finalComponent, | ||
| { | ||
| ...customProps, | ||
| key: field, | ||
| }, | ||
| errors | ||
| .map((error) => { | ||
| return <span key={error.code}>{error.message}</span>; | ||
| }) | ||
| .toArray() | ||
| let finalComponent = component || 'summary'; | ||
| return React.createElement(finalComponent, { | ||
| ...customProps, | ||
| key: field, | ||
| }, | ||
| errors.map((error) => { | ||
| return <span key={ error.code }>{ error.message }</span> | ||
| }).toArray() | ||
| ); | ||
| } | ||
| } | ||
| }; |
+109
-102
@@ -1,5 +0,5 @@ | ||
| import classNames from "classnames"; | ||
| import shallowEqual from "shallowequal"; | ||
| import classNames from 'classnames'; | ||
| import shallowEqual from 'shallowequal'; | ||
| export default class Field extends React.Component { | ||
| export class Field extends React.Component { | ||
| static contextTypes = { | ||
@@ -23,3 +23,6 @@ changeRadio: PropTypes.func, | ||
| options: PropTypes.instanceOf(ActiveResource.Collection), | ||
| optionsLabel: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), | ||
| optionsLabel: PropTypes.oneOfType([ | ||
| PropTypes.string, | ||
| PropTypes.func, | ||
| ]), | ||
| type: PropTypes.string.isRequired, | ||
@@ -34,3 +37,9 @@ uncheckedValue: PropTypes.oneOfType([ | ||
| invalidClassName: PropTypes.string, | ||
| value: PropTypes.oneOfType([PropTypes.object, PropTypes.func, PropTypes.string, PropTypes.number, PropTypes.bool]), | ||
| value: PropTypes.oneOfType([ | ||
| PropTypes.object, | ||
| PropTypes.func, | ||
| PropTypes.string, | ||
| PropTypes.number, | ||
| PropTypes.bool, | ||
| ]) | ||
| }; | ||
@@ -41,18 +50,17 @@ | ||
| _.bindAll( | ||
| this, | ||
| "afterChange", | ||
| "changeRadio", | ||
| "classNames", | ||
| "commonInputProps", | ||
| "customInputProps", | ||
| "getValue", | ||
| "handleChange", | ||
| "renderCheckboxComponent", | ||
| "renderInputComponent", | ||
| "renderRadioComponent", | ||
| "renderSelectComponent", | ||
| "renderTextareaComponent", | ||
| "setValue", | ||
| "valueFor" | ||
| _.bindAll(this, | ||
| 'afterChange', | ||
| 'changeRadio', | ||
| 'classNames', | ||
| 'commonInputProps', | ||
| 'customInputProps', | ||
| 'getValue', | ||
| 'handleChange', | ||
| 'renderCheckboxComponent', | ||
| 'renderInputComponent', | ||
| 'renderRadioComponent', | ||
| 'renderSelectComponent', | ||
| 'renderTextareaComponent', | ||
| 'setValue', | ||
| 'valueFor', | ||
| ); | ||
@@ -64,7 +72,3 @@ | ||
| shouldComponentUpdate(nextProps, nextState, nextContext) { | ||
| return !( | ||
| shallowEqual(this.props, nextProps) && | ||
| shallowEqual(this.state, nextState) && | ||
| shallowEqual(this.context, nextContext) | ||
| ); | ||
| return !(shallowEqual(this.props, nextProps) && shallowEqual(this.state, nextState) && shallowEqual(this.context, nextContext)); | ||
| } | ||
@@ -76,4 +80,4 @@ | ||
| switch (type) { | ||
| case "radioGroup": | ||
| switch(type) { | ||
| case 'radioGroup': | ||
| return { | ||
@@ -97,10 +101,10 @@ changeRadio: this.changeRadio, | ||
| resource, | ||
| value: this.valueFor(resource, this.props), | ||
| value: this.valueFor(resource, this.props) | ||
| }); | ||
| switch (type) { | ||
| case "email": | ||
| case "number": | ||
| case "text": | ||
| case "textarea": | ||
| switch(type) { | ||
| case 'email': | ||
| case 'number': | ||
| case 'text': | ||
| case 'textarea': | ||
| this.afterChange = _.debounce(this.afterChange, 500); | ||
@@ -111,13 +115,13 @@ } | ||
| componentDidUpdate(prevProps, prevState) { | ||
| const { resource: prevResource } = prevState; | ||
| const { resource } = this.context; | ||
| const { resource: prevResource } = prevState | ||
| const { resource } = this.context | ||
| if (prevResource !== resource) { | ||
| this.setState({ resource }); | ||
| if(prevResource !== resource) { | ||
| this.setState({ resource }) | ||
| } | ||
| if (!(_.isNull(prevResource.id) || _.isUndefined(prevResource.id)) && prevResource.id !== resource.id) { | ||
| if(!(_.isNull(prevResource.id) || _.isUndefined(prevResource.id)) && prevResource.id !== resource.id) { | ||
| this.setState({ | ||
| value: this.valueFor(resource, this.props), | ||
| }); | ||
| value: this.valueFor(resource, this.props) | ||
| }) | ||
| } | ||
@@ -130,5 +134,8 @@ } | ||
| return classNames(className, { | ||
| [invalidClassName]: !resource.errors().forField(name).empty(), | ||
| }); | ||
| return classNames( | ||
| className, | ||
| { | ||
| [invalidClassName]: !resource.errors().forField(name).empty() | ||
| } | ||
| ); | ||
| } | ||
@@ -150,12 +157,12 @@ | ||
| componentFor(type) { | ||
| switch (type) { | ||
| case "checkbox": | ||
| switch(type) { | ||
| case 'checkbox': | ||
| return this.renderCheckboxComponent(); | ||
| case "radio": | ||
| case 'radio': | ||
| return this.renderRadioComponent(); | ||
| case "radioGroup": | ||
| case 'radioGroup': | ||
| return this.renderRadioGroupComponent(); | ||
| case "select": | ||
| case 'select': | ||
| return this.renderSelectComponent(); | ||
| case "textarea": | ||
| case 'textarea': | ||
| return this.renderTextareaComponent(); | ||
@@ -173,11 +180,11 @@ default: | ||
| var omittedProps; | ||
| switch (type) { | ||
| case "radio": | ||
| omittedProps = _.omit(Field.propTypes, ["type", "name"]); | ||
| switch(type) { | ||
| case 'radio': | ||
| omittedProps = _.omit(Field.propTypes, ['type', 'name']); | ||
| break; | ||
| case "select": | ||
| omittedProps = component ? _.omit(Field.propTypes, "type") : Field.propTypes; | ||
| case 'select': | ||
| omittedProps = component ? _.omit(Field.propTypes, 'type') : Field.propTypes; | ||
| break; | ||
| default: | ||
| omittedProps = _.omit(Field.propTypes, "type"); | ||
| omittedProps = _.omit(Field.propTypes, 'type'); | ||
| } | ||
@@ -192,22 +199,20 @@ | ||
| switch (type) { | ||
| case "checkbox": | ||
| switch(type) { | ||
| case 'checkbox': | ||
| var resourceValue = resource[name]; | ||
| if (resourceValue == value) { | ||
| if(resourceValue == value) { | ||
| return true; | ||
| } else if (resourceValue == uncheckedValue || _.isUndefined(resourceValue) || _.isNull(resourceValue)) { | ||
| } else if(resourceValue == uncheckedValue || _.isUndefined(resourceValue) || _.isNull(resourceValue)) { | ||
| return false; | ||
| } else { | ||
| throw ( | ||
| "Field " + name + " with value " + resource[name] + " does not match value or uncheckedValue for checkbox" | ||
| ); | ||
| throw 'Field ' + name + ' with value ' + resource[name] + ' does not match value or uncheckedValue for checkbox' | ||
| } | ||
| case "radioGroup": | ||
| case "select": | ||
| case 'radioGroup': | ||
| case 'select': | ||
| var val = resource[name](); | ||
| return val ? val.id : ""; | ||
| return val ? val.id : ''; | ||
| default: | ||
| var val = resource[name]; | ||
| return val ? val : ""; | ||
| return val ? val : ''; | ||
| } | ||
@@ -225,3 +230,3 @@ } | ||
| let finalComponent = component || "input"; | ||
| let finalComponent = component || 'input'; | ||
| return React.createElement(finalComponent, { | ||
@@ -237,3 +242,3 @@ ...this.commonInputProps(), | ||
| let finalComponent = component || "input"; | ||
| let finalComponent = component || 'input'; | ||
| return React.createElement(finalComponent, { | ||
@@ -254,3 +259,3 @@ ...this.commonInputProps(), | ||
| let finalComponent = component || "input"; | ||
| let finalComponent = component || 'input'; | ||
| return React.createElement(finalComponent, { | ||
@@ -261,3 +266,3 @@ ...this.commonInputProps(), | ||
| value: value.id, | ||
| name: value.questionId, | ||
| name: value.questionId | ||
| }); | ||
@@ -267,3 +272,5 @@ } | ||
| renderRadioGroupComponent() { | ||
| return <div>{this.props.children}</div>; | ||
| return <div> | ||
| { this.props.children } | ||
| </div>; | ||
| } | ||
@@ -279,23 +286,23 @@ | ||
| selectOptions = options.map((o) => { | ||
| return ( | ||
| <option key={o.id} value={o.id}> | ||
| {_.isString(optionsLabel) ? o[optionsLabel] : optionsLabel(o)} | ||
| </option> | ||
| ); | ||
| return <option key={o.id} value={o.id}> | ||
| { | ||
| _.isString(optionsLabel) ? ( | ||
| o[optionsLabel] | ||
| ) : ( | ||
| optionsLabel(o) | ||
| ) | ||
| } | ||
| </option>; | ||
| }); | ||
| if (includeBlank) { | ||
| selectOptions.unshift(<option key={-1} value=""></option>); | ||
| selectOptions.unshift(<option key={-1} value=''></option>); | ||
| } | ||
| } | ||
| let finalComponent = component || "select"; | ||
| return React.createElement( | ||
| finalComponent, | ||
| { | ||
| ...this.commonInputProps(), | ||
| ...this.customInputProps(), | ||
| value: this.state.value, | ||
| }, | ||
| selectOptions.toArray() | ||
| ); | ||
| let finalComponent = component || 'select'; | ||
| return React.createElement(finalComponent, { | ||
| ...this.commonInputProps(), | ||
| ...this.customInputProps(), | ||
| value: this.state.value, | ||
| }, selectOptions.toArray()); | ||
| } | ||
@@ -306,3 +313,3 @@ | ||
| let finalComponent = component || "textarea"; | ||
| let finalComponent = component || 'textarea'; | ||
| return React.createElement(finalComponent, { | ||
@@ -323,10 +330,10 @@ ...this.commonInputProps(), | ||
| switch (type) { | ||
| case "checkbox": | ||
| switch(type) { | ||
| case 'checkbox': | ||
| value = e.target.checked; | ||
| break; | ||
| case "number": | ||
| if (e.target.value > max) { | ||
| case 'number': | ||
| if(e.target.value > max) { | ||
| value = max; | ||
| } else if (e.target.value < min) { | ||
| } else if(e.target.value < min) { | ||
| value = min; | ||
@@ -338,3 +345,3 @@ } else { | ||
| break; | ||
| case "radio": | ||
| case 'radio': | ||
| changeRadio(e.target.value); | ||
@@ -355,5 +362,5 @@ break; | ||
| let mappedValue; | ||
| switch (type) { | ||
| case "checkbox": | ||
| if (stateValue) { | ||
| switch(type) { | ||
| case 'checkbox': | ||
| if(stateValue) { | ||
| mappedValue = value; | ||
@@ -364,6 +371,6 @@ } else { | ||
| break; | ||
| case "radio": | ||
| case 'radio': | ||
| mappedValue = value; | ||
| break; | ||
| case "select": | ||
| case 'select': | ||
| mappedValue = options.detect((o) => o.id === stateValue); | ||
@@ -386,4 +393,4 @@ break; | ||
| let mappedValue = { persist: _.noop }; | ||
| switch (type) { | ||
| case "checkbox": | ||
| switch(type) { | ||
| case 'checkbox': | ||
| mappedValue = { ...mappedValue, target: { checked: value } }; | ||
@@ -390,0 +397,0 @@ break; |
+7
-6
@@ -1,2 +0,2 @@ | ||
| export default class Resource extends React.Component { | ||
| export class Resource extends React.Component { | ||
| static propTypes = { | ||
@@ -11,2 +11,3 @@ afterError: PropTypes.func, | ||
| onSubmit: PropTypes.func, | ||
| parent: PropTypes.object, | ||
| reflection: PropTypes.string, | ||
@@ -59,4 +60,4 @@ subject: PropTypes.object.isRequired, | ||
| const { root } = context; | ||
| const { reflection, subject } = props; | ||
| const root = context.root; | ||
| const { parent, reflection, subject } = props; | ||
@@ -66,3 +67,3 @@ let state = { resource: subject }; | ||
| if (reflection) { | ||
| var reflectionInstance = root.klass().reflectOnAssociation(reflection); | ||
| var reflectionInstance = (root || parent).klass().reflectOnAssociation(reflection); | ||
| if (_.isUndefined(reflectionInstance)) throw "Reflection " + reflection + " not found."; | ||
@@ -193,3 +194,3 @@ var inverseReflection = reflectionInstance.inverseOf(); | ||
| getChildContext() { | ||
| const { afterUpdate } = this.props; | ||
| const { afterUpdate, parent } = this.props; | ||
| const { root } = this.context; | ||
@@ -205,3 +206,3 @@ const { resource, queuedReflectionChanges, updating } = this.state; | ||
| shiftReflectionQueue: this.shiftReflectionQueue, | ||
| root: root || resource, | ||
| root: parent || root || resource, | ||
| resource, | ||
@@ -208,0 +209,0 @@ updateRoot: this.updateRoot, |
| export { default as Collection } from "./Collection.jsx"; | ||
| export { default as ErrorsFor } from "./ErrorsFor.jsx"; | ||
| export { default as Field } from "./Field.jsx"; | ||
| export { default as Resource } from "./Resource.jsx"; |
GitHub dependency
Supply chain riskContains a dependency which resolves to a GitHub URL. Dependencies fetched from GitHub specifiers are not immutable can be used to inject untrusted code or reduce the likelihood of a reproducible install.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
82531
251.21%11
37.5%2368
244.19%1
-50%77
Infinity%6
20%15
150%1
Infinity%1
Infinity%1
Infinity%+ Added
+ Added
+ Added