UNPKG

include-media-redux

Version:

An adaption of include-media-export for redux

331 lines (290 loc) 10.9 kB
(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