include-media-redux
Version:
An adaption of include-media-export for redux
331 lines (290 loc) • 10.9 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('lodash/get'), require('lodash/isPlainObject'), require('react'), require('hoist-non-react-statics'), require('react-redux'), require('lodash/debounce'), require('flux-standard-action')) :
typeof define === 'function' && define.amd ? define('include-media-redux', ['lodash/get', 'lodash/isPlainObject', 'react', 'hoist-non-react-statics', 'react-redux', 'lodash/debounce', 'flux-standard-action'], factory) :
(global['include-media-redux'] = factory(global.get,global.isPlainObject,global.React,global.hoistStatics,global.reactRedux,global.debounce,global.fluxStandardAction));
}(this, (function (get,isPlainObject,React,hoistStatics,reactRedux,debounce,fluxStandardAction) { 'use strict';
get = 'default' in get ? get['default'] : get;
isPlainObject = 'default' in isPlainObject ? isPlainObject['default'] : isPlainObject;
var React__default = 'default' in React ? React['default'] : React;
hoistStatics = 'default' in hoistStatics ? hoistStatics['default'] : hoistStatics;
debounce = 'default' in debounce ? debounce['default'] : debounce;
var classCallCheck = function (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;
};
}();
var defineProperty = function (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;
};
var inherits = function (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 possibleConstructorReturn = function (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;
};
/**
* Figures out breakpoint data in relation to the current width of the viewport.
* @param {number} viewportWidth The current width of the viewport
* @param {Object.<string, number>} breakpoints A custom object that describes the breakpoint of an app.
* @return {Object.<string, boolean>}
*/
function read(viewportWidth, breakpoints) {
var newBreakpointData = Object.keys(breakpoints).reduce(function (currentObject, breakpoint) {
var _babelHelpers$extends;
return _extends({}, currentObject, (_babelHelpers$extends = {}, defineProperty(_babelHelpers$extends, breakpoint, breakpoints[breakpoint] < viewportWidth), defineProperty(_babelHelpers$extends, breakpoint + "Equal", breakpoints[breakpoint] === viewportWidth), _babelHelpers$extends));
}, {});
return newBreakpointData;
}
/**
* Given a redux store, returns whether the current screen size is less
* than, greater than or equal to a given media breakpoint. You can also
* compound the query. For example, to find out if the current viewport is less
* than `sm` you would simple write:
*
* ```js
* is.lessThan('sm');
* ```
*
* If you want to know if the current viewport is less than OR equal
* `sm` you'd write:
*
* ```js
* is.lessThan.orEqualTo('sm');
* ```
*
* @param {function} getSelector - A function that returns a selector that should grab the breakpoint data
* in the redux store.
* @return {boolean}
*/
var callCallbacks = function callCallbacks(getSelector) {
return function (callback) {
return function (state) {
var breakpointData = getSelector()(state);
var callbacks = [].concat(callback);
for (var i = 0; i < callbacks.length; i += 1) {
var someCallbackValue = callbacks[i](breakpointData);
if (someCallbackValue) return true;
}
return false;
};
};
};
var lessThanCallback = function lessThanCallback(breakpoint) {
return function (breakpointData) {
return !get(breakpointData, breakpoint);
};
};
var greaterThanCallback = function greaterThanCallback(breakpoint) {
return function (breakpointData) {
return !!get(breakpointData, breakpoint);
};
};
var orEqualToCallback = function orEqualToCallback(breakpoint) {
return function (breakpointData) {
return !!get(breakpointData, breakpoint + 'Equal');
};
};
function lessThan(breakpoint) {
return callCallbacks(this.getSelector)(lessThanCallback(breakpoint));
}
function greaterThan(breakpoint) {
return callCallbacks(this.getSelector)(greaterThanCallback(breakpoint));
}
function orEqualTo(ancillaryCallback, breakpoint) {
return callCallbacks(this.getSelector)([orEqualToCallback(breakpoint), ancillaryCallback(breakpoint)]);
}
var is = { lessThan: lessThan, greaterThan: greaterThan };
lessThan.orEqualTo = orEqualTo.bind(is, lessThanCallback);
greaterThan.orEqualTo = orEqualTo.bind(is, greaterThanCallback);
function media(config) {
if (isPlainObject(config)) {
media.config = _extends({}, media.config, config);
}
}
media.read = function (viewportWidth) {
var newBreakpointData = read(viewportWidth, media.config.breakpoints);
media.config.log.trace({ newBreakpointData: newBreakpointData }, 'Updated breakpoints');
return newBreakpointData;
};
media.is = is;
media.is.getSelector = function () {
return media.config.selector;
};
var DEFAULT_BREAKPOINTS = {
phonePortrait: 320,
phoneLandscape: 480,
tabletPortrait: 768,
tabletLandscape: 1024
};
var DEFAULT_CONFIG = {
selector: function selector(state) {
return get(state, 'breakpoints', {});
},
breakpoints: DEFAULT_BREAKPOINTS,
log: console,
debug: false,
debounceDelay: 100,
initialState: {}
};
media.config = DEFAULT_CONFIG;
var UPDATE_BREAKPOINTS = '@@include-media-redux/UPDATE_BREAKPOINTS';
/**
* A flux standard action
* @typedef {Object} FluxStandardAction
* @property {string} type - The type of Flux Standard Action being dispatched
* @property {*} payload - The payload of the Flux Standard Action being dispatched
*/
/**
* Returns the Flux Standard Action to dispatch to the redux store
* @param {Object.<string, boolean>} breakpointData - The breakpoint data read from media.read
* @return {FluxStandardAction}
*/
var action = function (breakpointData) {
return {
type: UPDATE_BREAKPOINTS,
payload: {
breakpoints: breakpointData
}
};
};
var updater = function updater(dispatch) {
var windowWidth = document.documentElement.clientWidth;
if (windowWidth) {
var newBreakpointData = media.read(windowWidth);
dispatch(action(newBreakpointData));
}
};
var debouncedUpdater = debounce(updater, media.config.debounceDelay);
var nonDebouncedListener = function nonDebouncedListener(dispatch) {
return function () {
return updater(dispatch);
};
};
var listener = function listener(dispatch) {
return function () {
return debouncedUpdater(dispatch);
};
};
var withMedia = function withMedia(ComposedComponent) {
var EnhancedWithMedia = function (_Component) {
inherits(EnhancedWithMedia, _Component);
function EnhancedWithMedia(props) {
classCallCheck(this, EnhancedWithMedia);
var _this = possibleConstructorReturn(this, (EnhancedWithMedia.__proto__ || Object.getPrototypeOf(EnhancedWithMedia)).call(this, props));
_this.listener = listener(props.dispatch);
return _this;
}
createClass(EnhancedWithMedia, [{
key: 'componentDidMount',
value: function componentDidMount() {
media.config.log.debug('Setting initial client-side breakpoint data within the redux store');
this.listener();
media.config.log.debug('Registering include-media resize handler');
window.addEventListener('resize', this.listener);
}
}, {
key: 'componentWillUnmount',
value: function componentWillUnmount() {
media.config.log.debug('De-registering include-media resize handler');
window.removeEventListener('resize', this.listener);
}
}, {
key: 'render',
value: function render() {
return React__default.createElement(ComposedComponent, this.props);
}
}]);
return EnhancedWithMedia;
}(React.Component);
var EnhancedComponent = reactRedux.connect(null, null, null, { pure: false })(EnhancedWithMedia);
return hoistStatics(EnhancedComponent, ComposedComponent);
};
var initialState = {
breakpoints: media.config.initialState
};
var reducer = function () {
var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialState;
var action = arguments[1];
switch (action.type) {
case UPDATE_BREAKPOINTS:
{
if (media.config.debug && !fluxStandardAction.isFSA(action)) {
media.config.log.warn('Not dispatching a Flux standard action.', 'See https://github.com/acdlite/flux-standard-action for more information.');
}
var breakpoints = get(action, 'payload.breakpoints');
if (!isPlainObject(breakpoints)) {
if (media.config.debug) {
media.config.log.warn('Must dispatch a payload with a breakpoints key, whose value is an object.');
}
breakpoints = {};
}
return _extends({}, state, {
breakpoints: breakpoints
});
}
default:
return state;
}
};
var index = {
media: media,
withMedia: withMedia,
reducer: reducer,
UPDATE_BREAKPOINTS: UPDATE_BREAKPOINTS,
nonDebouncedListener: nonDebouncedListener,
listener: listener
};
return index;
})));
//# sourceMappingURL=include-media-redux.js.map