@khanacademy/wonder-blocks-layout
Advanced tools
Comparing version 1.1.1 to 1.2.0
@@ -1,2 +0,3 @@ | ||
import { createElement, Component } from 'react'; | ||
import Spacing from '@khanacademy/wonder-blocks-spacing'; | ||
import { createElement, Component, createContext } from 'react'; | ||
import { StyleSheet } from 'aphrodite'; | ||
@@ -27,2 +28,17 @@ import { View } from '@khanacademy/wonder-blocks-core'; | ||
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; | ||
} | ||
function _inherits(subClass, superClass) { | ||
@@ -75,3 +91,303 @@ if (typeof superClass !== "function" && superClass !== null) { | ||
// All possible valid media sizes | ||
var VALID_MEDIA_SIZES = ["small", "medium", "large"]; | ||
var mediaDefaultSpecLargeMarginWidth = Spacing.large; // The default spec for media layout, currently available in | ||
// three different settings (roughly mobile, tablet, and desktop). | ||
var MEDIA_DEFAULT_SPEC = { | ||
small: { | ||
query: "(max-width: 767px)", | ||
totalColumns: 4, | ||
gutterWidth: Spacing.medium, | ||
marginWidth: Spacing.medium | ||
}, | ||
medium: { | ||
query: "(min-width: 768px) and (max-width: 1023px)", | ||
totalColumns: 8, | ||
gutterWidth: Spacing.xLarge, | ||
marginWidth: Spacing.large | ||
}, | ||
large: { | ||
query: "(min-width: 1024px)", | ||
totalColumns: 12, | ||
gutterWidth: Spacing.xLarge, | ||
marginWidth: mediaDefaultSpecLargeMarginWidth, | ||
maxWidth: 1120 + mediaDefaultSpecLargeMarginWidth * 2 | ||
} | ||
}; // Used for internal tools | ||
var MEDIA_INTERNAL_SPEC = { | ||
large: { | ||
query: "(min-width: 1px)", | ||
totalColumns: 12, | ||
gutterWidth: Spacing.xLarge, | ||
marginWidth: Spacing.medium | ||
} | ||
}; // The default used for modals | ||
var MEDIA_MODAL_SPEC = { | ||
small: { | ||
query: "(max-width: 767px)", | ||
totalColumns: 4, | ||
gutterWidth: Spacing.medium, | ||
marginWidth: Spacing.medium | ||
}, | ||
large: { | ||
query: "(min-width: 768px)", | ||
totalColumns: 12, | ||
gutterWidth: Spacing.xLarge, | ||
marginWidth: Spacing.xxLarge | ||
} | ||
}; | ||
var defaultContext = { | ||
ssrSize: "large", | ||
mediaSpec: MEDIA_DEFAULT_SPEC | ||
}; | ||
var MediaLayoutContext = createContext(defaultContext); | ||
// If for some reason we're not able to resolve the current media size we | ||
// fall back to this state. | ||
var DEFAULT_SIZE = "large"; | ||
/** | ||
* `MediaLayout` is responsible for changing the rendering of contents at | ||
* differently sized viewports. `MediaLayoutContext.Provider` can be used | ||
* to specify different breakpoint configurations. By default it uses | ||
* `MEDIA_DEFAULT_SPEC`. See media-layout-context.js for additiional options. | ||
*/ | ||
var MediaLayout = | ||
/*#__PURE__*/ | ||
function (_React$Component) { | ||
_inherits(MediaLayout, _React$Component); | ||
function MediaLayout() { | ||
var _getPrototypeOf2; | ||
var _this; | ||
_classCallCheck(this, MediaLayout); | ||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
_this = _possibleConstructorReturn(this, (_getPrototypeOf2 = _getPrototypeOf(MediaLayout)).call.apply(_getPrototypeOf2, [this].concat(args))); | ||
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "watchHandlers", void 0); | ||
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "state", { | ||
size: undefined | ||
}); | ||
return _this; | ||
} | ||
_createClass(MediaLayout, [{ | ||
key: "componentWillUnmount", | ||
value: function componentWillUnmount() { | ||
// We go through the component and remove all of the listeners | ||
// that getCurrentSize attached. | ||
var _arr = Object.keys(MediaLayout.WATCHERS); | ||
for (var _i = 0; _i < _arr.length; _i++) { | ||
var _query = _arr[_i]; | ||
var watcher = MediaLayout.WATCHERS[_query]; | ||
if (watcher && this.watchHandlers) { | ||
var watchHandlers = this.watchHandlers; | ||
var handler = this.watchHandlers[_query]; | ||
watcher.removeListener(handler); | ||
delete watchHandlers[_query]; | ||
} | ||
} | ||
} | ||
}, { | ||
key: "getCurrentSize", | ||
value: function getCurrentSize(spec) { | ||
var _this2 = this; | ||
// If we have a state with the current size in it then we always want | ||
// to use that. This will happen if the viewport changes sizes after | ||
// we've already initialized. | ||
if (this.state.size) { | ||
return this.state.size; | ||
} // watchHandlers should never be undefined when state.size is also | ||
// undefined, but just in case we fall back to the default size | ||
if (this.watchHandlers) { | ||
return DEFAULT_SIZE; | ||
} // We then go through and set up matchMedia watchers for each breakpoint | ||
// (if they haven't been created already) and we add listeners to | ||
// watch for when the viewport changes size. | ||
this.watchHandlers = {}; | ||
var matchedSize; | ||
var _iteratorNormalCompletion = true; | ||
var _didIteratorError = false; | ||
var _iteratorError = undefined; | ||
try { | ||
var _loop = function _loop() { | ||
var size = _step.value; | ||
// Flow has issues with checking !spec[size] directly so we store | ||
// spec[size] in a new variable to get around the issue. | ||
var _spec = spec[size]; | ||
if (!_spec) { | ||
return "continue"; | ||
} | ||
var query = _spec.query; // Don't watch sizes that don't have an associated query | ||
if (!query) { | ||
return "continue"; | ||
} // Create a new matchMedia watcher if one doesn't exist yet | ||
// TODO(kevinb): move this to componentWillMount | ||
if (!MediaLayout.WATCHERS[query]) { | ||
MediaLayout.WATCHERS[query] = window.matchMedia(query); | ||
} | ||
var watcher = MediaLayout.WATCHERS[query]; // Attach a handler that watches for the change, saving a | ||
// references to it so we can remove it later | ||
var handler = _this2.watchHandlers[query] = function (e) { | ||
if (e.matches) { | ||
_this2.setState({ | ||
size: size | ||
}); | ||
} | ||
}; | ||
watcher.addListener(handler); // If one of the watchers matches the current size, then save | ||
// the size that was matched. | ||
if (watcher.matches) { | ||
matchedSize = size; | ||
} | ||
}; | ||
for (var _iterator = VALID_MEDIA_SIZES[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
var _ret = _loop(); | ||
if (_ret === "continue") continue; | ||
} // If a size was never defined, or matched, then we return the default | ||
// media layout size | ||
} catch (err) { | ||
_didIteratorError = true; | ||
_iteratorError = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion && _iterator.return != null) { | ||
_iterator.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError) { | ||
throw _iteratorError; | ||
} | ||
} | ||
} | ||
return matchedSize || DEFAULT_SIZE; | ||
} // We assume that we're running on the server (or, at least, an unsupported | ||
// environment) if there is no window object or matchMedia function | ||
// available. | ||
}, { | ||
key: "isServerSide", | ||
value: function isServerSide() { | ||
return typeof window === "undefined" || !window.matchMedia; | ||
} // Generate a mock Aphrodite StyleSheet based upon the current mediaSize | ||
// We do this by looking at all of the stylesheets specified in the | ||
// styleSheets prop and then all of the individual styles. We merge the | ||
// styles together | ||
}, { | ||
key: "getMockStyleSheet", | ||
value: function getMockStyleSheet(mediaSize) { | ||
var styleSheets = this.props.styleSheets; | ||
var mockStyleSheet = {}; // If no stylesheets were specified then we just return an empty object | ||
if (!styleSheets) { | ||
return mockStyleSheet; | ||
} // Go through all of the stylesheets that were specified | ||
var _arr2 = Object.keys(styleSheets); | ||
for (var _i2 = 0; _i2 < _arr2.length; _i2++) { | ||
var styleSize = _arr2[_i2]; | ||
var styleSheet = styleSheets[styleSize]; | ||
if (!styleSheet) { | ||
continue; | ||
} // And then through each key of each stylesheet | ||
var _arr3 = Object.keys(styleSheet); | ||
for (var _i3 = 0; _i3 < _arr3.length; _i3++) { | ||
var name = _arr3[_i3]; | ||
if (mockStyleSheet.hasOwnProperty(name)) { | ||
continue; | ||
} // We create an entry that combines the values from all of | ||
// the stylesheets together in least-specific to most-specific | ||
// priority (thus small/medium/large styles will always have | ||
// precedence over "all" or mdOrSmaller/mdOrLarger/etc.). | ||
mockStyleSheet[name] = [styleSheets.all && styleSheets.all[name], mediaSize === "small" && [styleSheets.mdOrSmaller && styleSheets.mdOrSmaller[name], styleSheets.small && styleSheets.small[name]], mediaSize === "medium" && [styleSheets.mdOrSmaller && styleSheets.mdOrSmaller[name], styleSheets.mdOrLarger && styleSheets.mdOrLarger[name], styleSheets.medium && styleSheets.medium[name]], mediaSize === "large" && [styleSheets.mdOrLarger && styleSheets.mdOrLarger[name], styleSheets.large && styleSheets.large[name]]]; | ||
} | ||
} | ||
return mockStyleSheet; | ||
} | ||
}, { | ||
key: "renderChildren", | ||
value: function renderChildren(_ref) { | ||
var overrideSize = _ref.overrideSize, | ||
ssrSize = _ref.ssrSize, | ||
mediaSpec = _ref.mediaSpec; | ||
var children = this.props.children; // We need to figure out what the current media size is | ||
// If an override has been specified, we use that. | ||
// If we're rendering on the server then we use the default | ||
// SSR rendering size. | ||
// Otherwise we attempt to get the current size based on | ||
// the current MediaSpec. | ||
var mediaSize = overrideSize || this.isServerSide() && ssrSize || this.getCurrentSize(mediaSpec); // Generate a mock stylesheet | ||
var styles = this.getMockStyleSheet(mediaSize); | ||
return children({ | ||
mediaSize: mediaSize, | ||
mediaSpec: mediaSpec, | ||
styles: styles | ||
}); | ||
} | ||
}, { | ||
key: "render", | ||
value: function render() { | ||
var _this3 = this; | ||
// We listen to the MediaLayoutContext to see what defaults we're | ||
// being given (this can be overriden by wrapping this component in | ||
// a MediaLayoutContext.Consumer). | ||
return createElement(MediaLayoutContext.Consumer, null, function (contextValue) { | ||
return _this3.renderChildren(contextValue); | ||
}); | ||
} | ||
}]); | ||
return MediaLayout; | ||
}(Component); | ||
_defineProperty(MediaLayout, "WATCHERS", {}); | ||
/** | ||
* Expands to fill space between sibling components. | ||
@@ -151,2 +467,37 @@ * | ||
export { Spring, Strut }; | ||
/** | ||
* Return where a media size matches a media query. | ||
* | ||
* examples: | ||
* - `queryMatchesSize("all", "small")` returns `true` | ||
* - `queryMatchesSize("mdOrLarger", "small")` returns `false` | ||
* | ||
* @param {MediaQuery} mediaQuery | ||
* @param {MediaSize} mediaSize | ||
*/ | ||
var queryMatchesSize = function queryMatchesSize(mediaQuery, mediaSize) { | ||
switch (mediaQuery) { | ||
case "all": | ||
return true; | ||
case "small": | ||
return mediaSize === "small"; | ||
case "mdOrSmaller": | ||
return mediaSize === "medium" || mediaSize === "small"; | ||
case "medium": | ||
return mediaSize === "medium"; | ||
case "mdOrLarger": | ||
return mediaSize === "medium" || mediaSize === "large"; | ||
case "large": | ||
return mediaSize === "large"; | ||
default: | ||
throw new Error("Unsupported mediaSize: ".concat(mediaSize)); | ||
} | ||
}; | ||
export { MediaLayout, MediaLayoutContext, Spring, Strut, queryMatchesSize, VALID_MEDIA_SIZES, MEDIA_DEFAULT_SPEC, MEDIA_INTERNAL_SPEC, MEDIA_MODAL_SPEC }; |
@@ -85,3 +85,3 @@ module.exports = | ||
/******/ // Load entry module and return exports | ||
/******/ return __webpack_require__(__webpack_require__.s = 3); | ||
/******/ return __webpack_require__(__webpack_require__.s = 4); | ||
/******/ }) | ||
@@ -93,3 +93,3 @@ /************************************************************************/ | ||
module.exports = require("react"); | ||
module.exports = require("@khanacademy/wonder-blocks-spacing"); | ||
@@ -100,3 +100,3 @@ /***/ }), | ||
module.exports = require("@khanacademy/wonder-blocks-core"); | ||
module.exports = require("react"); | ||
@@ -107,6 +107,12 @@ /***/ }), | ||
module.exports = require("@khanacademy/wonder-blocks-core"); | ||
/***/ }), | ||
/* 3 */ | ||
/***/ (function(module, exports) { | ||
module.exports = require("aphrodite"); | ||
/***/ }), | ||
/* 3 */ | ||
/* 4 */ | ||
/***/ (function(module, __webpack_exports__, __webpack_require__) { | ||
@@ -118,11 +124,69 @@ | ||
// EXTERNAL MODULE: external "react" | ||
var external_react_ = __webpack_require__(0); | ||
var external_react_ = __webpack_require__(1); | ||
// EXTERNAL MODULE: external "aphrodite" | ||
var external_aphrodite_ = __webpack_require__(2); | ||
// EXTERNAL MODULE: external "@khanacademy/wonder-blocks-spacing" | ||
var wonder_blocks_spacing_ = __webpack_require__(0); | ||
var wonder_blocks_spacing_default = /*#__PURE__*/__webpack_require__.n(wonder_blocks_spacing_); | ||
// EXTERNAL MODULE: external "@khanacademy/wonder-blocks-core" | ||
var wonder_blocks_core_ = __webpack_require__(1); | ||
// CONCATENATED MODULE: ./packages/wonder-blocks-layout/util/specs.js | ||
// CONCATENATED MODULE: ./packages/wonder-blocks-layout/components/spring.js | ||
// All possible valid media sizes | ||
var VALID_MEDIA_SIZES = ["small", "medium", "large"]; | ||
var mediaDefaultSpecLargeMarginWidth = wonder_blocks_spacing_default.a.large; // The default spec for media layout, currently available in | ||
// three different settings (roughly mobile, tablet, and desktop). | ||
var MEDIA_DEFAULT_SPEC = { | ||
small: { | ||
query: "(max-width: 767px)", | ||
totalColumns: 4, | ||
gutterWidth: wonder_blocks_spacing_default.a.medium, | ||
marginWidth: wonder_blocks_spacing_default.a.medium | ||
}, | ||
medium: { | ||
query: "(min-width: 768px) and (max-width: 1023px)", | ||
totalColumns: 8, | ||
gutterWidth: wonder_blocks_spacing_default.a.xLarge, | ||
marginWidth: wonder_blocks_spacing_default.a.large | ||
}, | ||
large: { | ||
query: "(min-width: 1024px)", | ||
totalColumns: 12, | ||
gutterWidth: wonder_blocks_spacing_default.a.xLarge, | ||
marginWidth: mediaDefaultSpecLargeMarginWidth, | ||
maxWidth: 1120 + mediaDefaultSpecLargeMarginWidth * 2 | ||
} | ||
}; // Used for internal tools | ||
var MEDIA_INTERNAL_SPEC = { | ||
large: { | ||
query: "(min-width: 1px)", | ||
totalColumns: 12, | ||
gutterWidth: wonder_blocks_spacing_default.a.xLarge, | ||
marginWidth: wonder_blocks_spacing_default.a.medium | ||
} | ||
}; // The default used for modals | ||
var MEDIA_MODAL_SPEC = { | ||
small: { | ||
query: "(max-width: 767px)", | ||
totalColumns: 4, | ||
gutterWidth: wonder_blocks_spacing_default.a.medium, | ||
marginWidth: wonder_blocks_spacing_default.a.medium | ||
}, | ||
large: { | ||
query: "(min-width: 768px)", | ||
totalColumns: 12, | ||
gutterWidth: wonder_blocks_spacing_default.a.xLarge, | ||
marginWidth: wonder_blocks_spacing_default.a.xxLarge | ||
} | ||
}; | ||
// CONCATENATED MODULE: ./packages/wonder-blocks-layout/components/media-layout-context.js | ||
var defaultContext = { | ||
ssrSize: "large", | ||
mediaSpec: MEDIA_DEFAULT_SPEC | ||
}; | ||
/* harmony default export */ var media_layout_context = (external_react_["createContext"](defaultContext)); | ||
// CONCATENATED MODULE: ./packages/wonder-blocks-layout/components/media-layout.js | ||
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } | ||
@@ -138,4 +202,2 @@ | ||
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } | ||
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } | ||
@@ -147,6 +209,282 @@ | ||
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } | ||
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; } | ||
// If for some reason we're not able to resolve the current media size we | ||
// fall back to this state. | ||
var DEFAULT_SIZE = "large"; | ||
/** | ||
* `MediaLayout` is responsible for changing the rendering of contents at | ||
* differently sized viewports. `MediaLayoutContext.Provider` can be used | ||
* to specify different breakpoint configurations. By default it uses | ||
* `MEDIA_DEFAULT_SPEC`. See media-layout-context.js for additiional options. | ||
*/ | ||
var media_layout_MediaLayout = | ||
/*#__PURE__*/ | ||
function (_React$Component) { | ||
_inherits(MediaLayout, _React$Component); | ||
function MediaLayout() { | ||
var _getPrototypeOf2; | ||
var _this; | ||
_classCallCheck(this, MediaLayout); | ||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
_this = _possibleConstructorReturn(this, (_getPrototypeOf2 = _getPrototypeOf(MediaLayout)).call.apply(_getPrototypeOf2, [this].concat(args))); | ||
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "watchHandlers", void 0); | ||
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "state", { | ||
size: undefined | ||
}); | ||
return _this; | ||
} | ||
_createClass(MediaLayout, [{ | ||
key: "componentWillUnmount", | ||
value: function componentWillUnmount() { | ||
// We go through the component and remove all of the listeners | ||
// that getCurrentSize attached. | ||
var _arr = Object.keys(MediaLayout.WATCHERS); | ||
for (var _i = 0; _i < _arr.length; _i++) { | ||
var _query = _arr[_i]; | ||
var watcher = MediaLayout.WATCHERS[_query]; | ||
if (watcher && this.watchHandlers) { | ||
var watchHandlers = this.watchHandlers; | ||
var handler = this.watchHandlers[_query]; | ||
watcher.removeListener(handler); | ||
delete watchHandlers[_query]; | ||
} | ||
} | ||
} | ||
}, { | ||
key: "getCurrentSize", | ||
value: function getCurrentSize(spec) { | ||
var _this2 = this; | ||
// If we have a state with the current size in it then we always want | ||
// to use that. This will happen if the viewport changes sizes after | ||
// we've already initialized. | ||
if (this.state.size) { | ||
return this.state.size; | ||
} // watchHandlers should never be undefined when state.size is also | ||
// undefined, but just in case we fall back to the default size | ||
if (this.watchHandlers) { | ||
return DEFAULT_SIZE; | ||
} // We then go through and set up matchMedia watchers for each breakpoint | ||
// (if they haven't been created already) and we add listeners to | ||
// watch for when the viewport changes size. | ||
this.watchHandlers = {}; | ||
var matchedSize; | ||
var _iteratorNormalCompletion = true; | ||
var _didIteratorError = false; | ||
var _iteratorError = undefined; | ||
try { | ||
var _loop = function _loop() { | ||
var size = _step.value; | ||
// Flow has issues with checking !spec[size] directly so we store | ||
// spec[size] in a new variable to get around the issue. | ||
var _spec = spec[size]; | ||
if (!_spec) { | ||
return "continue"; | ||
} | ||
var query = _spec.query; // Don't watch sizes that don't have an associated query | ||
if (!query) { | ||
return "continue"; | ||
} // Create a new matchMedia watcher if one doesn't exist yet | ||
// TODO(kevinb): move this to componentWillMount | ||
if (!MediaLayout.WATCHERS[query]) { | ||
MediaLayout.WATCHERS[query] = window.matchMedia(query); | ||
} | ||
var watcher = MediaLayout.WATCHERS[query]; // Attach a handler that watches for the change, saving a | ||
// references to it so we can remove it later | ||
var handler = _this2.watchHandlers[query] = function (e) { | ||
if (e.matches) { | ||
_this2.setState({ | ||
size: size | ||
}); | ||
} | ||
}; | ||
watcher.addListener(handler); // If one of the watchers matches the current size, then save | ||
// the size that was matched. | ||
if (watcher.matches) { | ||
matchedSize = size; | ||
} | ||
}; | ||
for (var _iterator = VALID_MEDIA_SIZES[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
var _ret = _loop(); | ||
if (_ret === "continue") continue; | ||
} // If a size was never defined, or matched, then we return the default | ||
// media layout size | ||
} catch (err) { | ||
_didIteratorError = true; | ||
_iteratorError = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion && _iterator.return != null) { | ||
_iterator.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError) { | ||
throw _iteratorError; | ||
} | ||
} | ||
} | ||
return matchedSize || DEFAULT_SIZE; | ||
} // We assume that we're running on the server (or, at least, an unsupported | ||
// environment) if there is no window object or matchMedia function | ||
// available. | ||
}, { | ||
key: "isServerSide", | ||
value: function isServerSide() { | ||
return typeof window === "undefined" || !window.matchMedia; | ||
} // Generate a mock Aphrodite StyleSheet based upon the current mediaSize | ||
// We do this by looking at all of the stylesheets specified in the | ||
// styleSheets prop and then all of the individual styles. We merge the | ||
// styles together | ||
}, { | ||
key: "getMockStyleSheet", | ||
value: function getMockStyleSheet(mediaSize) { | ||
var styleSheets = this.props.styleSheets; | ||
var mockStyleSheet = {}; // If no stylesheets were specified then we just return an empty object | ||
if (!styleSheets) { | ||
return mockStyleSheet; | ||
} // Go through all of the stylesheets that were specified | ||
var _arr2 = Object.keys(styleSheets); | ||
for (var _i2 = 0; _i2 < _arr2.length; _i2++) { | ||
var styleSize = _arr2[_i2]; | ||
var styleSheet = styleSheets[styleSize]; | ||
if (!styleSheet) { | ||
continue; | ||
} // And then through each key of each stylesheet | ||
var _arr3 = Object.keys(styleSheet); | ||
for (var _i3 = 0; _i3 < _arr3.length; _i3++) { | ||
var name = _arr3[_i3]; | ||
if (mockStyleSheet.hasOwnProperty(name)) { | ||
continue; | ||
} // We create an entry that combines the values from all of | ||
// the stylesheets together in least-specific to most-specific | ||
// priority (thus small/medium/large styles will always have | ||
// precedence over "all" or mdOrSmaller/mdOrLarger/etc.). | ||
mockStyleSheet[name] = [styleSheets.all && styleSheets.all[name], mediaSize === "small" && [styleSheets.mdOrSmaller && styleSheets.mdOrSmaller[name], styleSheets.small && styleSheets.small[name]], mediaSize === "medium" && [styleSheets.mdOrSmaller && styleSheets.mdOrSmaller[name], styleSheets.mdOrLarger && styleSheets.mdOrLarger[name], styleSheets.medium && styleSheets.medium[name]], mediaSize === "large" && [styleSheets.mdOrLarger && styleSheets.mdOrLarger[name], styleSheets.large && styleSheets.large[name]]]; | ||
} | ||
} | ||
return mockStyleSheet; | ||
} | ||
}, { | ||
key: "renderChildren", | ||
value: function renderChildren(_ref) { | ||
var overrideSize = _ref.overrideSize, | ||
ssrSize = _ref.ssrSize, | ||
mediaSpec = _ref.mediaSpec; | ||
var children = this.props.children; // We need to figure out what the current media size is | ||
// If an override has been specified, we use that. | ||
// If we're rendering on the server then we use the default | ||
// SSR rendering size. | ||
// Otherwise we attempt to get the current size based on | ||
// the current MediaSpec. | ||
var mediaSize = overrideSize || this.isServerSide() && ssrSize || this.getCurrentSize(mediaSpec); // Generate a mock stylesheet | ||
var styles = this.getMockStyleSheet(mediaSize); | ||
return children({ | ||
mediaSize: mediaSize, | ||
mediaSpec: mediaSpec, | ||
styles: styles | ||
}); | ||
} | ||
}, { | ||
key: "render", | ||
value: function render() { | ||
var _this3 = this; | ||
// We listen to the MediaLayoutContext to see what defaults we're | ||
// being given (this can be overriden by wrapping this component in | ||
// a MediaLayoutContext.Consumer). | ||
return external_react_["createElement"](media_layout_context.Consumer, null, function (contextValue) { | ||
return _this3.renderChildren(contextValue); | ||
}); | ||
} | ||
}]); | ||
return MediaLayout; | ||
}(external_react_["Component"]); | ||
_defineProperty(media_layout_MediaLayout, "WATCHERS", {}); | ||
// EXTERNAL MODULE: external "aphrodite" | ||
var external_aphrodite_ = __webpack_require__(3); | ||
// EXTERNAL MODULE: external "@khanacademy/wonder-blocks-core" | ||
var wonder_blocks_core_ = __webpack_require__(2); | ||
// CONCATENATED MODULE: ./packages/wonder-blocks-layout/components/spring.js | ||
function spring_typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { spring_typeof = function _typeof(obj) { return typeof obj; }; } else { spring_typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return spring_typeof(obj); } | ||
function spring_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
function spring_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); } } | ||
function spring_createClass(Constructor, protoProps, staticProps) { if (protoProps) spring_defineProperties(Constructor.prototype, protoProps); if (staticProps) spring_defineProperties(Constructor, staticProps); return Constructor; } | ||
function spring_possibleConstructorReturn(self, call) { if (call && (spring_typeof(call) === "object" || typeof call === "function")) { return call; } return spring_assertThisInitialized(self); } | ||
function spring_assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } | ||
function spring_getPrototypeOf(o) { spring_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return spring_getPrototypeOf(o); } | ||
function spring_inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) spring_setPrototypeOf(subClass, superClass); } | ||
function spring_setPrototypeOf(o, p) { spring_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return spring_setPrototypeOf(o, p); } | ||
/** | ||
* Expands to fill space between sibling components. | ||
@@ -160,11 +498,11 @@ * | ||
function (_React$Component) { | ||
_inherits(Spring, _React$Component); | ||
spring_inherits(Spring, _React$Component); | ||
function Spring() { | ||
_classCallCheck(this, Spring); | ||
spring_classCallCheck(this, Spring); | ||
return _possibleConstructorReturn(this, _getPrototypeOf(Spring).apply(this, arguments)); | ||
return spring_possibleConstructorReturn(this, spring_getPrototypeOf(Spring).apply(this, arguments)); | ||
} | ||
_createClass(Spring, [{ | ||
spring_createClass(Spring, [{ | ||
key: "render", | ||
@@ -251,9 +589,55 @@ value: function render() { | ||
}; | ||
// CONCATENATED MODULE: ./packages/wonder-blocks-layout/util/util.js | ||
/** | ||
* Return where a media size matches a media query. | ||
* | ||
* examples: | ||
* - `queryMatchesSize("all", "small")` returns `true` | ||
* - `queryMatchesSize("mdOrLarger", "small")` returns `false` | ||
* | ||
* @param {MediaQuery} mediaQuery | ||
* @param {MediaSize} mediaSize | ||
*/ | ||
var queryMatchesSize = function queryMatchesSize(mediaQuery, mediaSize) { | ||
switch (mediaQuery) { | ||
case "all": | ||
return true; | ||
case "small": | ||
return mediaSize === "small"; | ||
case "mdOrSmaller": | ||
return mediaSize === "medium" || mediaSize === "small"; | ||
case "medium": | ||
return mediaSize === "medium"; | ||
case "mdOrLarger": | ||
return mediaSize === "medium" || mediaSize === "large"; | ||
case "large": | ||
return mediaSize === "large"; | ||
default: | ||
throw new Error("Unsupported mediaSize: ".concat(mediaSize)); | ||
} | ||
}; | ||
// CONCATENATED MODULE: ./packages/wonder-blocks-layout/index.js | ||
/* concated harmony reexport */__webpack_require__.d(__webpack_exports__, "MediaLayout", function() { return media_layout_MediaLayout; }); | ||
/* concated harmony reexport */__webpack_require__.d(__webpack_exports__, "MediaLayoutContext", function() { return media_layout_context; }); | ||
/* concated harmony reexport */__webpack_require__.d(__webpack_exports__, "Spring", function() { return spring_Spring; }); | ||
/* concated harmony reexport */__webpack_require__.d(__webpack_exports__, "Strut", function() { return strut_Strut; }); | ||
/* concated harmony reexport */__webpack_require__.d(__webpack_exports__, "VALID_MEDIA_SIZES", function() { return VALID_MEDIA_SIZES; }); | ||
/* concated harmony reexport */__webpack_require__.d(__webpack_exports__, "MEDIA_DEFAULT_SPEC", function() { return MEDIA_DEFAULT_SPEC; }); | ||
/* concated harmony reexport */__webpack_require__.d(__webpack_exports__, "MEDIA_INTERNAL_SPEC", function() { return MEDIA_INTERNAL_SPEC; }); | ||
/* concated harmony reexport */__webpack_require__.d(__webpack_exports__, "MEDIA_MODAL_SPEC", function() { return MEDIA_MODAL_SPEC; }); | ||
/* concated harmony reexport */__webpack_require__.d(__webpack_exports__, "queryMatchesSize", function() { return queryMatchesSize; }); | ||
/***/ }) | ||
/******/ ]); |
58
docs.md
@@ -0,1 +1,3 @@ | ||
## Spring and Strut | ||
`Spring` and `Strut` are two components that can make certain layouts easier to implement. | ||
@@ -35,1 +37,57 @@ `Spring` is infinitely compressible and expands to fill available space while `Strut` | ||
``` | ||
## MediaLayout | ||
`MediaLayout` is a container component takes in a `styleSheets` object whose keys are | ||
media sizes. I listens for changes to the current media size and passes the current | ||
`mediaSize`, `mediaSpec`, and `styles` to `children` which is a render function taking | ||
those three values as object. | ||
Valid keys for the `styleSheets` object are (in order of precedence): | ||
- `small`, `medium`, `large` | ||
- `mdOrSmaller`, `mdOrLarger` | ||
- `all` | ||
`MediaLayout` will merge style rules from multiple styles that match the current media | ||
query, e.g. `"(min-width: 1024px)"`. | ||
The `mediaSpec` is an object with one or more of the following keys: `small`, `medium`, | ||
or `large`. Each value contains the following data: | ||
- `query: string` e.g. "(min-width: 1024px)" | ||
- `totalColumns: number` | ||
- `gutterWidth: number` | ||
- `marginWidth: number` | ||
- `maxWidth: number` | ||
By default, `MediaLayout` uses `MEDIA_DEFAULT_SPEC` but others can be specified using | ||
`MediaLayoutContext.Provider`. See media-layout-context.test.js for examples of how | ||
to do this. | ||
```js | ||
const {StyleSheet} = require("aphrodite"); | ||
const {View} = require("@khanacademy/wonder-blocks-core"); | ||
const styleSheets = { | ||
large: StyleSheet.create({ | ||
test: { | ||
backgroundColor: "blue", | ||
}, | ||
}), | ||
medium: StyleSheet.create({ | ||
test: { | ||
backgroundColor: "green", | ||
}, | ||
}), | ||
small: StyleSheet.create({ | ||
test: { | ||
backgroundColor: "orange", | ||
}, | ||
}), | ||
}; | ||
<MediaLayout styleSheets={styleSheets}> | ||
{({mediaSize, mediaSpec, styles}) => { | ||
return <View style={styles.test}>Hello, world!</View>; | ||
}} | ||
</MediaLayout> | ||
``` |
@@ -11,2 +11,3 @@ // This file is auto-generated by gen-snapshot-tests.js | ||
jest.mock("react-dom"); | ||
import MediaLayout from "./components/media-layout.js"; | ||
import Spring from "./components/spring.js"; | ||
@@ -54,2 +55,34 @@ import Strut from "./components/strut.js"; | ||
const styleSheets = { | ||
large: StyleSheet.create({ | ||
test: { | ||
backgroundColor: "blue", | ||
}, | ||
}), | ||
medium: StyleSheet.create({ | ||
test: { | ||
backgroundColor: "green", | ||
}, | ||
}), | ||
small: StyleSheet.create({ | ||
test: { | ||
backgroundColor: "orange", | ||
}, | ||
}), | ||
}; | ||
const example = ( | ||
<MediaLayout styleSheets={styleSheets}> | ||
{({mediaSize, mediaSpec, styles}) => { | ||
return <View style={styles.test}>Hello, world!</View>; | ||
}} | ||
</MediaLayout> | ||
); | ||
const tree = renderer.create(example).toJSON(); | ||
expect(tree).toMatchSnapshot(); | ||
}); | ||
it("example 3", () => { | ||
const {StyleSheet} = require("aphrodite"); | ||
const {View} = require("@khanacademy/wonder-blocks-core"); | ||
const styles = StyleSheet.create({ | ||
@@ -73,3 +106,3 @@ row: { | ||
}); | ||
it("example 3", () => { | ||
it("example 4", () => { | ||
const {StyleSheet} = require("aphrodite"); | ||
@@ -76,0 +109,0 @@ const {View} = require("@khanacademy/wonder-blocks-core"); |
16
index.js
// @flow | ||
import type {MediaQuery, MediaSize, MediaSpec} from "./util/types.js"; | ||
import type {Context} from "./components/media-layout-context.js"; | ||
export {default as MediaLayout} from "./components/media-layout.js"; | ||
export { | ||
default as MediaLayoutContext, | ||
} from "./components/media-layout-context.js"; | ||
export {default as Spring} from "./components/spring.js"; | ||
export {default as Strut} from "./components/strut.js"; | ||
export * from "./util/specs.js"; | ||
export {queryMatchesSize} from "./util/util.js"; | ||
export type { | ||
MediaQuery, | ||
MediaSize, | ||
MediaSpec, | ||
Context as MediaLayoutContextValue, | ||
}; |
@@ -11,7 +11,16 @@ // @flow | ||
// Assert | ||
expect(Object.keys(result).sort()).toEqual([ | ||
"Strut", | ||
"Spring" | ||
].sort()); | ||
expect(Object.keys(result).sort()).toEqual( | ||
[ | ||
"Strut", | ||
"Spring", | ||
"MediaLayout", | ||
"MediaLayoutContext", | ||
"VALID_MEDIA_SIZES", | ||
"MEDIA_DEFAULT_SPEC", | ||
"MEDIA_INTERNAL_SPEC", | ||
"MEDIA_MODAL_SPEC", | ||
"queryMatchesSize", | ||
].sort(), | ||
); | ||
}); | ||
}); |
{ | ||
"name": "@khanacademy/wonder-blocks-layout", | ||
"version": "1.1.1", | ||
"version": "1.2.0", | ||
"design": "v1", | ||
@@ -20,3 +20,3 @@ "publishConfig": { | ||
"devDependencies": { | ||
"@khanacademy/wonder-blocks-button": "^2.3.1", | ||
"@khanacademy/wonder-blocks-button": "^2.3.2", | ||
"wb-dev-build-settings": "^0.0.2" | ||
@@ -23,0 +23,0 @@ }, |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
117643
22
2442
1