Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"xo.enable": true,
"eslint.enable": false
}
84 changes: 84 additions & 0 deletions demo/connector.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">

<title>polymer-redux Connector Demo</title>

<script src="../../webcomponentsjs/webcomponents-lite.js"></script>

<link rel="import" href="../polymer-redux.html">
<link rel="import" href="./redux-mixin.html">
</head>
<body>
<dom-module id="office-quotes">
<template>
<p>[[message]]</p>
<button on-click="handleUpdateMessage">Update quote</button>
</template>
<script>
/**
* Create our "pure" Element.
*
*/
class OfficeQuotes extends Polymer.Element {
static get is() { return 'office-quotes'; }

// Properties have NO statePath
static get properties() {
return {
message: {
type: String
}
};
}

handleUpdateMessage() {
// We dispatch native events to cause actions
this.dispatchEvent(new CustomEvent('update-quote', {
detail: {
message: 'Nothing changes by staying the same, quite literally.'
}
}));
}
}

/**
* To bind state to properties, we define a function that will select
* the state values for the property bindings.
*
*/
function mapStateToProps(state) {
return {
message: state.message,
};
}

/**
* Mapping element events to dispatch a Redux action. This is only called
* once, during the contructor() method. Each event listener will be given
* the `event` object and the `state` object which is the current store state.
*
*/
function mapEventToDispatch(dispatch) {
return {
// the method name is the actual event name
['update-quote'](event, state) {
dispatch({
type: 'UPDATE_MESSAGE',
message: event.detail.message
});
}
};
}

// Then generate our ConnectedElement using the "pure" one and our mapping functions
const ConnectedQuotes = ReduxMixin(OfficeQuotes, mapStateToProps, mapEventToDispatch);
window.customElements.define(ConnectedQuotes.is, ConnectedQuotes);
</script>
</dom-module>

<office-quotes></office-quotes>
</body>
</html>
114 changes: 89 additions & 25 deletions dist/polymer-redux.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

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; }

// Expose globals
var CustomEvent = window_1.CustomEvent;
var Polymer = window_1.Polymer;
Expand All @@ -32,8 +34,6 @@
*
* Creates a Class mixin for decorating Elements with a given Redux store.
*
* @polymerMixin
*
* @param {Object} store Redux store.
* @return {Function} Class mixin.
*/
Expand Down Expand Up @@ -61,12 +61,14 @@
* @param {Object} properties
* @return {Function} Update function.
*/
var bind = function bind(element, properties) {
var bind = function bind(element, properties, mapStateToProps) {
var elementName = element.constructor.is;

var bindings = Object.keys(properties).filter(function (name) {
var property = properties[name];
if (Object.prototype.hasOwnProperty.call(property, 'statePath')) {
if (!property.readOnly && property.notify) {
console_1.warn('PolymerRedux: <' + element.constructor.is + '>.' + name + ' has "notify" enabled, two-way bindings goes against Redux\'s paradigm');
console_1.warn('PolymerRedux: <' + elementName + '>.' + name + ' has "notify" enabled, two-way bindings goes against Redux\'s paradigm.');
}
return true;
}
Expand All @@ -80,19 +82,20 @@
* @param {Object} state
*/
var update = function update(state) {
var propertiesChanged = false;
bindings.forEach(function (name) {
// Perhaps .reduce() to a boolean?
var mappedState = mapStateToProps ? mapStateToProps(state) : {};
var updates = bindings.reduce(function (props, name) {
var statePath = properties[name].statePath;

var value = typeof statePath === 'function' ? statePath.call(element, state) : Polymer.Path.get(state, statePath);

var changed = element._setPendingPropertyOrPath(name, value, true);
propertiesChanged = propertiesChanged || changed;
});
if (propertiesChanged) {
element._invalidateProperties();
}
// Warn about double bindings
if (props[name]) {
console_1.warn('PolymerRedux: <' + elementName + '>.' + name + ' has double bindings, statePath binding has priority.');
}

return _extends({}, props, _defineProperty({}, name, value));
}, mappedState);

element.setProperties(updates, true);
};

// Redux listener
Expand All @@ -104,8 +107,9 @@
});

subscribers.set(element, unsubscribe);
update(store.getState());

return update(store.getState());
return update;
};

/**
Expand Down Expand Up @@ -138,41 +142,101 @@
return res;
};

/**
* Adds events listeners to a element.
*
* @param {HTMLElement} element
* @param {Object} listeners List of event listeners to add.
*/
function addListeners(element, listeners) {
Object.keys(listeners).forEach(function (name) {
element.addEventListener(name, listeners[name]);
});
}

/**
* Removes events listeners from an element.
*
* @param {HTMLElement} element
* @param {Object} listeners List of event listeners to remove.
*/
function removeListeners(element, listeners) {
Object.keys(listeners).forEach(function (name) {
element.removeEventListeners(name, listeners[name]);
});
}

/**
* ReduxMixin
*
* @example
* const ReduxMixin = PolymerRedux(store)
* class Foo extends ReduxMixin(Polymer.Element) { }
*
* @polymerMixinClass
* @polymer
* @mixinFunction
*
* @param {Polymer.Element} parent The polymer parent element.
* @return {Function} PolymerRedux mixed class.
*/
return function (parent) {
return function (parent, mapStateToProps, mapEventsToDispatch) {
if (mapStateToProps && typeof mapStateToProps !== 'function') {
throw new TypeError('PolymerRedux: expects mapStateToProps to be a function');
}

if (mapEventsToDispatch && typeof mapEventsToDispatch !== 'function') {
throw new TypeError('PolymerRedux: expects mapEventsToDispatch to be a function');
}

/**
* @polymer
* @mixinClass
*/
return class ReduxMixin extends parent {
constructor() {
super();
var _this;

_this = super();

// Collect the action creators first as property changes trigger
// dispatches from observers, see #65, #66, #67
var actions = collect(this.constructor, 'actions');
Object.defineProperty(this, '_reduxActions', {
configurable: true,
value: actions
var reduxActions = collect(this.constructor, 'actions');

// If we have any mapped events to dispatch, build the liseners object
var mappedEvents = mapEventsToDispatch ? mapEventsToDispatch(function () {
return _this.dispatch.apply(_this, arguments);
}) : {};

// Bind the listeners to call with the event and the current state
var reduxMappedListeners = Object.keys(mappedEvents).reduce(function (listeners, name) {
return _extends({}, listeners, _defineProperty({}, name, function (event) {
event.stopImmediatePropagation();
mappedEvents[name](event, store.getState());
}));
}, {});

// Define properties
Object.defineProperties(this, {
_reduxActions: {
value: reduxActions
},
_reduxMappedListeners: {
value: reduxMappedListeners
}
});
}

connectedCallback() {
super.connectedCallback();
var properties = collect(this.constructor, 'properties');
bind(this, properties);
bind(this, properties, mapStateToProps);
addListeners(this, this._reduxMappedListeners);
}

disconnectedCallback() {
super.disconnectedCallback();
unbind(this);
removeListeners(this, this._reduxMappedListeners);
}

/**
Expand All @@ -193,7 +257,7 @@
* @return {Object} The action.
*/
dispatch() {
var _this = this;
var _this2 = this;

for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
Expand Down Expand Up @@ -221,7 +285,7 @@

// Replace redux dispatch
args.splice(0, 1, function () {
return _this.dispatch.apply(_this, arguments);
return _this2.dispatch.apply(_this2, arguments);
});
return originalAction.apply(undefined, args);
};
Expand Down
Loading