diff --git a/CHANGELOG.md b/CHANGELOG.md
index bf6c555..d45cbdf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# CHANGELOG
+## 1.3.0 (2017-xx-xx)
+
+- Resize Observer can be enabled (will be the default behaviour in 2.0.0)
+- Resize observation can be switched on / off.
+- First adjustment on Container instantiation can be switched off
+- Dependency upgrades
+
## 1.2.6 (2017-03-26)
- Fixed exports for non es-6 environments
diff --git a/README.md b/README.md
index 39ecf57..c49ec40 100644
--- a/README.md
+++ b/README.md
@@ -6,64 +6,588 @@
[](https://greenkeeper.io/)
A PostCSS plugin and Javascript runtime combination, which allows you to write
-@container queries in your CSS the same way you would write @media queries.
+**container queries** in your CSS the same way you would write **media queries**.
-## screenshot
+## Installation
-## How to use
+`yarn add --dev @zeecoder/container-query`
-### Install
+or
`npm install --save-dev @zeecoder/container-query`
-## PublicAPI
-- `Container.js` - JS Runtime
-- `containerQuery.js` - PostCSS Plugin
-- `initialiseAllContainers.js` - helper function
-
-The rest in build/ is not considered to be a part of the Public API, which means
-anything in it can change at any time. (Including minor / patch releases.)
-
-## Limitations
-- `@container` queries cannot be nested
-
-## Supported Browsers
-- Works with all modern browsers and IE9+
-
-## Notes
-- Lead with ## WHAT (image) followed by ## WHY
-- list supported browsers
-- BEM-inspiration: Block -> inside of which are elements (unique classnames!)
-- @container queries must always be preceded by a @define-container
-- All @containers following a @defined-container will be related to that
-- @define-container declarations will be ignored inside @container declarations
-- Example function to save all container configurations in separate JSON files in a dir
-- Gulp example
- - PostCSS example
- - SASS example: works out of the box
- - Less -> separate pipeline
-- Containers can have more than one instance of their elements
-- The container units are relative to the container's "inner" height / width.
-(Without the borders.)
-- Note possible circular issues
- - A container should not use container units for properties that would affect
- its width / height. These situations are not currently handled, so try to
- avoid them.
-- To avoid circular deps, use overflow: hidden and avoid using container units on defined containers
-- Use native CSS techniques to achieve your goal whenever possible (css grid, flexbox)
+## Introduction
+
+The way it works:
+
+> PostCSS plugin => JSON => Runtime
+
+Container queries work the same way media queriesdo: they allow you to apply
+styles to elements (and their descendants) when certain conditions are met.
+
+While media queries are relative to the viewport's size, container queries are
+relative to a container element's size.
+
+**What is a container?**
+
+A container is just an HTML element, which may contain other elements.
+You may want to think of them as "**Blocks**" ([BEM](http://getbem.com/naming/))
+or "**Components**" ([React](https://facebook.github.io/react/docs/components-and-props.html)).
+
+### Highlights
+
+- Built with webpack / React in mind
+- Uses a [ResizeObserver polyfill](https://github.com/que-etc/resize-observer-polyfill)
+to detect size changes. Once the [spec](https://wicg.github.io/ResizeObserver/)
+is implemented by browsers, it's going to be [even more performant](https://developers.google.com/web/updates/2016/10/resizeobserver#out_now).
+- Uses media query like syntax: `@container (...) { /* ... */ }`
+- Supports container units: chpx, cwpx, cminpx, cmaxpx. (Useful to set font-size
+and other properties to a value that's changing with the container's size.)
+
+### Browser Support
+
+Works with all modern browsers and IE9+
+
+### In action
+
+```pcss
+.User {
+ @define-container;
+ // All container queries and container units must be preceded by a container
+ // definition. The rest of the classes generated here are expected to be
+ // "descendants" of the container.
+
+ background: red;
+
+ @container (width >= 200px) and (height >= 200px) {
+ // Container queries are relative to the previous @defined-container.
+ background: green;
+ }
+
+ &__name {
+ font-size: 10chpx;
+ // The above is a container unit.
+ // It resolves to 10 percent of the container's height in pixels.
+ // Resolves to 12px, if the container's height is 120px.
+ }
+
+ &__avatar {
+ display: none;
+
+ @container (width >= 200px) and (height >= 200px) {
+ display: block;
+ }
+ }
+}
+```
+
+```html
+
+
+
+
![]()
+
+
+
+
+
+
![]()
+
+```
+
+## How to use
+
+This solution consists of a PostCSS plugin and a JS (`Container`) class.
+
+> PostCSS plugin => JSON => Runtime
+
+The plugin analyses the given CSS, and extracts all container-query related
+lines, producing a JSON file. Depending on your setup (Gulp / webpack, etc)
+this file may or may not contain more than one container's data.
+
+Once the JSON file is generated, a new Container instance needs to be created
+for all container HTML Elements, with the right json stats.
+
+### JSON structure
+
+```json
+{
+ ".User": {},
+ ".Post": {},
+ ".Avatar": {}
+}
+```
+
+As you can see, selectors are considered to be the unique identifiers of
+defined containers. While technically nothing will stop you from having
+`.page .container .User` as a container's selector, it is *not recommended*.
+
+Instead, use the BEM methodology or something similar.
+
+Support for [CSS Modules](https://github.com/css-modules/css-modules#user-content-implementations)
+and [CSS-in-JS](https://github.com/MicheleBertoli/css-in-js#user-content-features)
+is planned, to automate this pattern.
+
+(You might want to watch Mark Dalgleish's talk called
+"[A Unified Styling Language](https://www.youtube.com/watch?v=X_uTCnaRe94)" to
+have an idea why the latter might be a good thing.)
+
+### webpack + React
+
+I recommend you to set up [postcss-loader](https://github.com/postcss/postcss-loader)
+with [postcss-nested](https://github.com/postcss/postcss-nested) with
+`bubble: ['container']` option, or to use SASS.
+
+**Avatar.pcss**
+```pcss
+.Avatar {
+ @define-container;
+ /* ... */
+
+ @container (aspect-ratio: > 3) {
+ /* ... */
+ }
+
+ @container (width > 100px) and (height > 100px) {
+ /* ... */
+ }
+}
+```
+
+**Avatar.js**
+```js
+import React, {Component} from 'react';
+import ReactDOM from 'react-dom';
+import Container from "@zeecoder/container-query/Container";
+
+// Generates `Avatar.json` in the same folder.
+require('./Avatar.pcss');
+// We only defined the Avatar container in the pcss file, so there's nothing
+// else there apart from the '.Avatar' property.
+const containerStats = require('./Avatar.json')['.Avatar'];
+// `['.Avatar']` will be unnecessary once Issue#17 is done
+
+export default class Avatar extends Component {
+ componentDidMount() {
+ new Container(
+ ReactDOM.findDOMNode(this),
+ containerStats,
+ {adjustOnResize: true}
+ );
+ // adjustOnResize will be the default behaviour in 2.0
+ }
+
+ render() {
+ return (
+
+ );
+ }
+}
+```
+
+**And that's it!**
+
+Now all new *Avatar* components will automatically adjust to the component's size.
+
+### Gulp setup
+
+If you're not a fan of processing styles with webpack, then you can use a task
+runner instead, like Gulp.
+
+Your task could look something like this:
+
+```js
+const gulp = require('gulp');
+const postcss = require('gulp-postcss');
+const rename = require('gulp-rename');
+const postcssImport = require('postcss-import');
+const postcssNested = require('postcss-nested');
+const containerQuery = require('@zeecoder/container-query/containerQuery');
+
+gulp.task('styles', () => {
+ return gulp.src('styles/main.pcss')
+ .pipe(postcss([
+ postcssImport(),
+ postcssNested({ bubble: ['container'] }),
+ containerQuery(),
+ ]))
+ .pipe(rename('main.css'))
+ .pipe(gulp.dest('web/dist'));
+});
+
+```
+
+Now you'll have both main.css and main.json. The CSS can then be served separately
+from the JS, while webpack could still `require()` the JSON and do its thing.
+
+### Without webpack
+
+Even though the library was made with webpack in mind, there's no reason why
+other bundlers wouldn't work, or other UI libraries for that matter. (Instead
+of React.)
+
+**Just follow the same steps**
+
+1) Process your styles with the PostCSS plugin, to extract all container-related
+information
+2) Save the JSON(s) somewhere
+3) Serve the JSON(s) to the JS some way
+4) Create a Container instance for all container html elements
+
+For instance, imagine you have a main.pcss file, which imports all other
+components.(Using Gulp as described above.)
+
+Then, you can serve the JSON from the backend, and bundle the JS with your
+favourite JS bundler, to grab that JSON and instantiate the Container class for
+all elements found:
+
+```js
+// Assumptions:
+// Browserify as the bundler
+// JSON is available in a script tag, served by the backend:
+// ``
+
+import Container from "@zeecoder/container-query/Container";
+
+const containerStats = JSON.parse(
+ document.getElementById('container-stats').innerHTML
+);
+
+// Initialising all containers by finding all instances based on the selectors
+for (let containerSelector in containerStats) {
+ document.querySelectorAll(containerSelector).forEach(element => {
+ // Initialising all HTML Elements with the right json stats
+ new Container(
+ element,
+ containerStats[containerSelector],
+ {adjustOnResize: true}
+ );
+ });
+}
+```
+
+The above doesn't cover dynamically created elements, but you get the idea.
+
+## Syntax
+
+### Declaration
+
+As previous examples show, containers can be declared by adding
+`@define-container;` inside a rule that's meant to be used as a container.
+
+Multiple such definitions in a single CSS file are allowed. All container
+queries and units will be relative to the previous declaration.
+
+Like so:
+
+```pcss
+.User {
+ @define-container;
+
+ &__name {
+ display: none;
+ font-size: 10chpx;
+ }
+
+ @container (width > 200px) {
+ display: block;
+ }
+}
+
+.Avatar {
+ @define-container;
+
+ border-radius: 100%;
+ border: 1chpx solid;
+
+ @container (width < 30px), (height < 30px) {
+ background: grey;
+ }
+}
+```
+
+Note that for container queries and container units to work, all elements must
+be descendants of the container.
+
+Using the above example, an element with the `.User__name` class will not have
+its font-size adjusted, unless it's a descendant of a container element with
+the `.User` class.
+
+### Queries
+
+Container queries have the same syntax media queries do:
+
+```pcss
+@container (condition) and (condition), (condition) {
+ // styles
+}
+```
+
+However, instead of writing min-width, min-height you can use the following
+operators: `<`, `<=`, `>`, `>=`.
+
+(In accordance width [CSS Media Queries Level 4](https://drafts.csswg.org/mediaqueries/#mq-range-context))
+
+The following conditions are supported: width, height, aspect-ratio, orientation.
+
+**Examples**
+
+```pcss
+@condition (orientation: landscape) {}
+@condition (orientation: portrait) {}
+@condition (width > 100px) {}
+@condition (height < 100px) {}
+@condition (aspect-ratio > 3) {}
+@condition (orientation: landscape) and (aspect-ratio > 3) {}
+```
+
+If you want the same syntax for your media queries, then I recommend [this plugin](https://github.com/postcss/postcss-media-minmax).
+
+### Units
+
+Container units are like viewport units (vh, vw, vmin and vmax), only relative
+to the container. They are useful to generate values based on the container's
+size.
+
+The supported units are: **chpx**, **cwpx**, **cminpx**, **cmaxpx**.
+
+**Syntax**: `px`
+
+Depending on whether ch or cw is used, value stands for a percentage of the
+container's width or height.
+
+If a container's size is:
+
+- width: 120px
+- height: 130px
+
+then
+
+- 100cwpx => 120px
+- 100chpx => 130px
+- 100cminpx => 120px
+- 100cmaxpx => 130px
+- 15chpx => 11.53846px
+- 15cwpx => 12.5px
+
+And so on.
+
+**Example**
+
+```pcss
+.User {
+ @define-container;
+
+ &__name {
+ font-size: 10chpx;
+ }
+
+ &__avatar {
+ border-radius: 100%;
+ border: 1vminpx solid;
+ }
+
+ @container (height > 150px) {
+ font-size: 15chpx;
+ border: 5vminpx solid;
+ }
+}
+```
+
+Technically, you can produce any CSS units, like: chem/cwem/cminem/cmaxem,
+chrem/cwrem/cminrem/cmaxrem), but they're planned to be [phased out](https://github.com/ZeeCoder/container-query/issues/16).
+
+Also note that recalculating and applying these values is costly, since it's
+done on each resize event (or `adjust` call).
+
+**Example**
+
+You might be tempted to use container units to set an aspect ratio between the
+container's width / height:
+
+```pcss
+.Container {
+ @define-container;
+ height: 50cwpx;
+ // Height will now be 50% of it's width
+}
+```
+
+While this works, there's a [pure CSS solution too](https://codepen.io/ZeeCaptein/pen/ZyEowo).
+
+Admittedly more boilerplate, but it might worth avoiding JS when it's not really
+necessary by using flexbox, CSS grid and other vanilla CSS solutions instead.
+
+## API
+
+### Container (Runtime)
+
+**Instantiation**
+
+`new Container(Element, statsJSON, options)`
+
+Where `Element` is an HTMLElement, `statsJSON` is a json object from the PostCSS
+plugin, and options are extra options about how the instance should behave.
+
+**Default options:**
+
+```js
+{
+ adjustOnResize: false, // Will be true by default in ^2.0.0
+ adjustOnInstantiation: true
+}
+```
+
+- *adjustOnResize*: If true, then the container will readjust itself based on the
+element's height automatically.
+This is done by using a [ResizeObserver](https://github.com/que-etc/resize-observer-polyfill) polyfill.
+- *adjustOnInstantiation*: Whether to do an initial adjustment call on instantiation.
+
+These options may be useful to you, if you want to fine-tune when readjustments
+should happen.
+
+*For example*: You could optimise animations, or only readjust containers on window
+resize, if that fits your needs.
+
+**Instance methods**
+
+- `adjust(containerDimensions)`: Calling `adjust()` will readjust the container
+based on it's size. You might have the containers size already, however, in which
+case you can just pass that in, so you can save the browser the layout / repaint
+work: `{ width: , height: }`. This could be useful if you're
+animating the container's size, and on each "tick" you know what the dimensions
+are already.
+- `observeResize()`: Makes the container observe resize events and readjust
+itself automatically. Passing in the {adjustOnResize: true} option has the same
+effect.
+- `unobserveResize()`: Stops a container observing resize events.
+
+### containerQuery (PostCSS plugin)
+
+```js
+postCSS([
+ containerQuery({
+ getJSON: function(cssPath, jsonStats) {
+ // `cssPath`: the original css' path that was processed. Useful if
+ // you want to save the JSON relative to the original css.
+ //
+ // `jsonStats`: the json stats having all the container-related data
+ // needed for the Container instances.
+ // Structural Reminder:
+ // {
+ // ".SomeComponent": {},
+ // ".OtherComponent": {},
+ // }
+ // Keys here are the selectors having the `@define-container`
+ // declaration.
+ }
+ })
+])
+```
+
+## Compatibility with other CSS preprocessors
+
+From the examples above, you can see that I recommend using PostCSS.
+However, other css preprocessors would work too, as long as they support custom
+at-rules.
+
+### SASS
+
+Sass works out of the box with at-rules, even when they're nested.
+
+They behave the same way media queries, which is great!
+
+You can write things like:
+
+```pcss
+.Avatar {
+ @define-container;
+ /* ... */
+
+ @container (width > 200px) {
+ /* ... */
+ }
+
+ @container (height > 200px) {
+ /* ... */
+ }
+}
+```
+
+Which compiles to:
+
+```css
+.Avatar {
+ @define-container;
+ /* ... */
+}
+
+@container (width > 200px) {
+ .Avatar {
+ /* ... */
+ }
+}
+
+@container (height > 200px) {
+ .Avatar {
+ /* ... */
+ }
+}
+```
+
+### LESS
+
+Support for at-rules is limited, but it'll work fine with v2.6.0 and above as
+long as you avoid nesting.
+
+## Caveats / Notes
+
+There are some things to look out for when using this library.
+
+- Resize Observer reacts in ~20ms. Should be good for animation even, but if not,
+it can be switched off to use requestAnimationFrame() instead. Also: the more
+you nest containers, the slower change propagates from top to bottom. This is
+due to the fact that a container's size cannot be checked without having a
+layout / repaint first.
+- Currently, styles are applied through the Element.style object. I'll probably
+replace this mechanic with [Styletron](https://github.com/rtsao/styletron), or
+something similar in the future.
+- With element / container query solutions, circularity issues may arise. While
+[an attempt](https://github.com/ZeeCoder/container-query/issues/20) to tackle
+this was made, the same is still unfortunately true to this library as well.
+Use your best judgement when setting up queries / units to avoid these issues.
## Thoughts on design
-Here is a list of goals I started with, in case you're wondering about the
-tool's design:
-
-- Should be thoroughly unit tested
-- Use containers (as opposed to "element query"),
-- Resemble @media queries so that it's familiar and easy to use,
-- Uses PostCSS for preprocessing instead of a JS runtime parser,
-- Modular, so it plays nicely with js bundlers and "Component-based" UI
-libraries (Webpack / Browserify / React etc.)
-- Doesn't need to be valid CSS syntax (since it's based on PostCSS)
-- Be easy enough to use, but a transpiling step in the frontend build
-process would be assumed
-- Should work especially well with css component naming methodologies, like BEM or SUIT
+In case you're wondering about the tool's design, here is a list of goals I
+started with:
+
+- Should be tested.
+- Use containers instead of elements.
+- Use media query syntax so that it's familiar and easy to use.
+- Should be easy enough to use, but a transpiling step would be assumed.
+- Uses PostCSS for preprocessing instead of having a JS runtime parser.
+- Use JS modules, so it plays nicely with js bundlers (webpack, Browserify,
+etc.) and Component-oriented UI libraries (React, Vue, etc.)
+- Don't limit the tool to CSS syntax. With PostCSS, it's easy to parse custom
+at-rules instead. The end result will still be valid CSS.
+- Should work with component naming methodologies - like BEM or SUIT - the best.
+
+## Next up
+
+[Ideas for enhancement](https://goo.gl/7XtjDe)
+
+## Alternatives
+
+Finally, if you like the idea of container queries, but are not particularly
+convinced by this solution, then I encourage you to look at these alternatives:
+
+- [EQCSS](https://github.com/eqcss/eqcss)
+- [CSS Element Queries](https://github.com/marcj/css-element-queries)
+- [CQ Prolyfill](https://github.com/ausi/cq-prolyfill)
+- [React Container Query](https://github.com/d6u/react-container-query)
diff --git a/demo/gulpfile.js b/demo/gulpfile.js
index acfd58e..aa22b6c 100644
--- a/demo/gulpfile.js
+++ b/demo/gulpfile.js
@@ -1,49 +1,60 @@
-'use strict';
+"use strict";
-const gulp = require('gulp');
-const fs = require('fs');
-const path = require('path');
-const postcss = require('gulp-postcss');
-const rename = require('gulp-rename');
-const nested = require('postcss-nested');
-const autoprefixer = require('autoprefixer');
-const postcssImport = require('postcss-import');
-const containerQuery = require('@zeecoder/container-query/containerQuery');
-const writeFileSync = require('fs').writeFileSync;
+const gulp = require("gulp");
+const fs = require("fs");
+const path = require("path");
+const postcss = require("gulp-postcss");
+const rename = require("gulp-rename");
+const nested = require("postcss-nested");
+const autoprefixer = require("autoprefixer");
+const postcssImport = require("postcss-import");
+const containerQuery = require("@zeecoder/container-query/containerQuery");
+const writeFileSync = require("fs").writeFileSync;
-function containerSelectorToFilename (selector) {
+function containerSelectorToFilename(selector) {
return selector.substr(1);
}
-gulp.task('css', function () {
- return gulp.src('src/css/main.pcss')
- .pipe(postcss([
- postcssImport(),
- nested(),
- autoprefixer(),
- containerQuery({
- getJSON: (cssPath, containers) => {
- // Saving the container query stats individually
- for (let containerSelector in containers) {
- let component = containerSelectorToFilename(containerSelector);
+gulp.task("css", function() {
+ return gulp
+ .src("src/css/main.pcss")
+ .pipe(
+ postcss([
+ postcssImport(),
+ nested({
+ bubble: ["container"]
+ }),
+ autoprefixer(),
+ containerQuery({
+ getJSON: (cssPath, containers) => {
+ // Saving the container query stats individually
+ for (let containerSelector in containers) {
+ let component = containerSelectorToFilename(
+ containerSelector
+ );
+ writeFileSync(
+ `${__dirname}/src/css/components/${component}/${component}.json`,
+ JSON.stringify(containers[containerSelector])
+ );
+ }
+
+ // Then saving the container names
writeFileSync(
- `${__dirname}/src/css/components/${component}/${component}.json`,
- JSON.stringify(containers[containerSelector])
+ `${__dirname}/src/js/containers.json`,
+ JSON.stringify(
+ Object.keys(containers).map(
+ containerSelectorToFilename
+ )
+ )
);
}
-
- // Then saving the container names
- writeFileSync(
- `${__dirname}/src/js/containers.json`,
- JSON.stringify(Object.keys(containers).map(containerSelectorToFilename))
- );
- }
- }),
- ]))
- .pipe( rename('main.css') )
- .pipe( gulp.dest('web/dist') );
+ })
+ ])
+ )
+ .pipe(rename("main.css"))
+ .pipe(gulp.dest("web/dist"));
});
-gulp.task('watch', function() {
- gulp.watch('src/**/*.pcss', ['css']);
+gulp.task("watch", function() {
+ gulp.watch("src/**/*.pcss", ["css"]);
});
diff --git a/demo/package.json b/demo/package.json
index c698a5b..a945b38 100644
--- a/demo/package.json
+++ b/demo/package.json
@@ -18,7 +18,8 @@
},
"scripts": {
"build": "gulp css && webpack",
- "watch": "gulp watch & webpack --watch &"
+ "watch:gulp": "gulp watch",
+ "watch:webpack": "webpack --watch"
},
"dependencies": {
"autoprefixer": "^6.7.6"
diff --git a/demo/src/css/components/social-container/social-container.json b/demo/src/css/components/social-container/social-container.json
index b8a4a7a..ea8e74f 100644
--- a/demo/src/css/components/social-container/social-container.json
+++ b/demo/src/css/components/social-container/social-container.json
@@ -1 +1 @@
-{"selector":".social-container","queries":[{"elements":[{"selector":".social-container","styles":{}},{"selector":".social-container__cell","styles":{}},{"selector":".social-container__cell:nth-child(1)","styles":{"width":""}},{"selector":".social-container__cell:nth-child(2)","styles":{"width":""}},{"selector":".social-container__cell:nth-child(3)","styles":{"width":""}},{"selector":".social-container__cell:nth-child(4)","styles":{"width":""}},{"selector":".social-container__cell:nth-child(5)","styles":{"width":""}}]},{"conditions":[[["width",">",250]]],"elements":[{"selector":".social-container__cell:nth-child(1)","styles":{"width":"50%"}},{"selector":".social-container__cell:nth-child(2)","styles":{"width":"50%"}},{"selector":".social-container__cell:nth-child(3)","styles":{"width":"33%"}},{"selector":".social-container__cell:nth-child(4)","styles":{"width":"34%"}},{"selector":".social-container__cell:nth-child(5)","styles":{"width":"33%"}}]}]}
\ No newline at end of file
+{"selector":".social-container","queries":[{"elements":[{"selector":".social-container","styles":{}},{"selector":".social-container__cell","styles":{}},{"selector":".social-container__cell:nth-child(1)","styles":{"width":""}},{"selector":".social-container__cell:nth-child(2)","styles":{"width":""}},{"selector":".social-container__cell:nth-child(3)","styles":{"width":""}},{"selector":".social-container__cell:nth-child(4)","styles":{"width":""}},{"selector":".social-container__cell:nth-child(5)","styles":{"width":""}},{"selector":".social-container__cell:nth-child(6)","styles":{"width":""}}]},{"conditions":[[["width",">",250]]],"elements":[{"selector":".social-container__cell:nth-child(1)","styles":{"width":"50%"}},{"selector":".social-container__cell:nth-child(2)","styles":{"width":"50%"}},{"selector":".social-container__cell:nth-child(3)","styles":{"width":"33%"}},{"selector":".social-container__cell:nth-child(4)","styles":{"width":"34%"}},{"selector":".social-container__cell:nth-child(5)","styles":{"width":"33%"}},{"selector":".social-container__cell:nth-child(6)","styles":{"width":"40%"}}]}]}
\ No newline at end of file
diff --git a/demo/src/css/components/social-container/social-container.pcss b/demo/src/css/components/social-container/social-container.pcss
index c91a38b..6ec8630 100644
--- a/demo/src/css/components/social-container/social-container.pcss
+++ b/demo/src/css/components/social-container/social-container.pcss
@@ -1,16 +1,12 @@
.social-container {
@define-container;
-
display: flex;
- flex-direction: column;
overflow: hidden;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
-}
-.social-container {
flex-direction: row;
flex-wrap: wrap;
}
@@ -18,22 +14,25 @@
.social-container__cell {
width: 100%;
position: relative;
-}
-@container (width > 250px) {
- .social-container__cell:nth-child(1) {
- width: 50%;
- }
- .social-container__cell:nth-child(2) {
- width: 50%;
- }
- .social-container__cell:nth-child(3) {
- width: 33%;
- }
- .social-container__cell:nth-child(4) {
- width: 34%;
- }
- .social-container__cell:nth-child(5) {
- width: 33%;
+ @container (width > 250px) {
+ &:nth-child(1) {
+ width: 50%;
+ }
+ &:nth-child(2) {
+ width: 50%;
+ }
+ &:nth-child(3) {
+ width: 33%;
+ }
+ &:nth-child(4) {
+ width: 34%;
+ }
+ &:nth-child(5) {
+ width: 33%;
+ }
+ &:nth-child(6) {
+ width: 40%;
+ }
}
}
diff --git a/demo/src/css/components/social-link/social-link.json b/demo/src/css/components/social-link/social-link.json
index 8499165..d2b8db8 100644
--- a/demo/src/css/components/social-link/social-link.json
+++ b/demo/src/css/components/social-link/social-link.json
@@ -1 +1 @@
-{"selector":".social-link","queries":[{"elements":[{"selector":".social-link","styles":{"border":"calc(0.2chpx + 0.2cwpx) solid #999","borderRadius":"calc(0.3chpx + 0.3cwpx)","fontSize":"85cminpx"}},{"selector":".social-link:hover","styles":{}},{"selector":".social-link__icon","styles":{"marginLeft":""}},{"selector":".social-link__name","styles":{"display":"","marginLeft":"","marginRight":""}}]},{"conditions":[[["aspect-ratio",">",3]]],"elements":[{"selector":".social-link","styles":{"fontSize":"60chpx"}},{"selector":".social-link__icon","styles":{"marginLeft":"20chpx"}},{"selector":".social-link__name","styles":{"display":"block","marginLeft":"5cwpx","marginRight":"20chpx"}}]}]}
\ No newline at end of file
+{"selector":".social-link","queries":[{"elements":[{"selector":".social-link","styles":{"border":"calc(0.2chpx + 0.2cwpx) solid #999","borderRadius":"calc(0.3chpx + 0.3cwpx)","fontSize":"85cminpx"}},{"selector":".social-link:hover","styles":{}},{"selector":".social-link__icon","styles":{"marginLeft":""}},{"selector":".social-link__name","styles":{"display":"","marginLeft":"","marginRight":""}}]},{"conditions":[[["aspect-ratio",">",3]]],"elements":[{"selector":".social-link","styles":{"fontSize":"60chpx"}}]},{"conditions":[[["aspect-ratio",">",3]]],"elements":[{"selector":".social-link__icon","styles":{"marginLeft":"20chpx"}}]},{"conditions":[[["aspect-ratio",">",3]]],"elements":[{"selector":".social-link__name","styles":{"display":"block","marginLeft":"5cwpx","marginRight":"20chpx"}}]}]}
\ No newline at end of file
diff --git a/demo/src/css/components/social-link/social-link.pcss b/demo/src/css/components/social-link/social-link.pcss
index 3c3dad5..944e257 100644
--- a/demo/src/css/components/social-link/social-link.pcss
+++ b/demo/src/css/components/social-link/social-link.pcss
@@ -1,6 +1,5 @@
.social-link {
@define-container;
-
overflow: hidden;
background: #ccc;
border: calc(0.2chpx + 0.2cwpx) solid #999;
@@ -18,12 +17,19 @@
height: 100%;
transition: background-color .3s, color .3s;
+ @container (aspect-ratio > 3) {
+ font-size: 60chpx;
+ }
+
&:hover {
background-color: #333;
color: #eee;
}
&__icon {
+ @container (aspect-ratio > 3) {
+ margin-left: 20chpx;
+ }
}
&__name {
@@ -31,21 +37,11 @@
overflow: hidden;
text-overflow: ellipsis;
font-size: 0.5em;
- }
-}
-
-@container (aspect-ratio > 3) {
- .social-link {
- font-size: 60chpx;
- }
-
- .social-link__icon {
- margin-left: 20chpx;
- }
- .social-link__name {
- display: block;
- margin-left: 5cwpx;
- margin-right: 20chpx;
+ @container (aspect-ratio > 3) {
+ display: block;
+ margin-left: 5cwpx;
+ margin-right: 20chpx;
+ }
}
}
diff --git a/demo/src/css/components/user/user.json b/demo/src/css/components/user/user.json
index d069e54..60b5e63 100644
--- a/demo/src/css/components/user/user.json
+++ b/demo/src/css/components/user/user.json
@@ -1 +1 @@
-{"selector":".user","queries":[{"elements":[{"selector":".user","styles":{"border":"0.5cmaxpx solid","borderRadius":"3cminpx"}},{"selector":".user__info","styles":{"padding":"1cmaxpx","webkitBoxOrient":"","webkitBoxDirection":"","msFlexDirection":"","flexDirection":""}},{"selector":".user__image","styles":{"height":"30cwpx"}},{"selector":".user__name","styles":{"paddingLeft":"5cwpx","display":""}},{"selector":".user__social","styles":{}},{"selector":".user--1","styles":{}},{"selector":".user--2","styles":{}}]},{"conditions":[[["width",">",200]]],"elements":[{"selector":".user__name","styles":{"display":"block"}}]},{"conditions":[[["width",">",300]]],"elements":[{"selector":".user__info","styles":{"webkitBoxOrient":"horizontal","webkitBoxDirection":"normal","msFlexDirection":"row","flexDirection":"row"}}]}]}
\ No newline at end of file
+{"selector":".user","queries":[{"elements":[{"selector":".user","styles":{"border":"0.5cmaxpx solid","borderRadius":"3cminpx"}},{"selector":".user__info","styles":{"padding":"1cmaxpx","webkitBoxOrient":"","webkitBoxDirection":"","msFlexDirection":"","flexDirection":""}},{"selector":".user__image","styles":{"height":"30cwpx"}},{"selector":".user__name","styles":{"paddingLeft":"5cwpx","display":""}},{"selector":".user__social","styles":{}},{"selector":".user--1","styles":{}},{"selector":".user--2","styles":{}}]},{"conditions":[[["width",">",300]]],"elements":[{"selector":".user__info","styles":{"webkitBoxOrient":"horizontal","webkitBoxDirection":"normal","msFlexDirection":"row","flexDirection":"row"}}]},{"conditions":[[["width",">",200]]],"elements":[{"selector":".user__name","styles":{"display":"block"}}]}]}
\ No newline at end of file
diff --git a/demo/src/css/components/user/user.pcss b/demo/src/css/components/user/user.pcss
index d25f046..cb84707 100644
--- a/demo/src/css/components/user/user.pcss
+++ b/demo/src/css/components/user/user.pcss
@@ -11,13 +11,18 @@
max-height: 300px;
position: fixed;
top: 50%;
- transform: translateY(-50%);
+ transform: translateY(-50%) translateZ(0);
+ transition: width 1s;
&__info {
display: flex;
flex-direction: column;
align-items: center;
padding: 1cmaxpx;
+
+ @container (width > 300px) {
+ flex-direction: row;
+ }
}
&__image {
@@ -30,6 +35,10 @@
flex: 1 0 0;
padding-left: 5cwpx;
display: none;
+
+ @container (width > 200px) {
+ display: block;
+ }
}
&__social {
@@ -49,15 +58,3 @@
height: 50%;
}
}
-
-@container (width > 200px) {
- .user__name {
- display: block;
- }
-}
-
-@container (width > 300px) {
- .user__info {
- flex-direction: row;
- }
-}
diff --git a/demo/src/js/main.js b/demo/src/js/main.js
index 1535d8e..78805c4 100644
--- a/demo/src/js/main.js
+++ b/demo/src/js/main.js
@@ -1,8 +1,8 @@
-import Container from '@zeecoder/container-query/Container';
+import Container from "@zeecoder/container-query/Container";
-const containers = require('./containers.json');
+const containers = require("./containers.json");
-function initialiseContainer (jsonData) {
+function initialiseContainer(jsonData) {
/**
* @type NodeList
*/
@@ -10,18 +10,45 @@ function initialiseContainer (jsonData) {
const htmlElementsLength = htmlElements.length;
for (let i = 0; i < htmlElementsLength; i++) {
- const containerInstance = new Container(htmlElements[i], jsonData);
- window.addEventListener('resize', containerInstance.adjust);
- requestAnimationFrame(() => {
- containerInstance.adjust();
+ // console.log(htmlElements[i]);
+ const containerInstance = new Container(htmlElements[i], jsonData, {
+ adjustOnResize: true
});
+
+ // const containerInstance = new Container(htmlElements[i], jsonData);
+ // window.addEventListener('resize', containerInstance.adjust);
+ // requestAnimationFrame(() => {
+ // containerInstance.adjust();
+ // });
}
}
-containers.forEach((containerFileName) => {
- initialiseContainer(require(`../css/components/${containerFileName}/${containerFileName}.json`));
+containers.forEach(containerFileName => {
+ initialiseContainer(
+ require(`../css/components/${containerFileName}/${containerFileName}.json`)
+ );
});
+function startAnimating() {
+ let isWide = false;
+ const element = document.getElementById("to-animate");
+
+ function doAnimate() {
+ if (isWide) {
+ element.style.width = "100px";
+ } else {
+ element.style.width = "700px";
+ }
+
+ isWide = !isWide;
+
+ setTimeout(doAnimate, 1000);
+ }
+
+ doAnimate();
+}
+
+setTimeout(startAnimating, 3000);
// import initialiseAllContainers from '../initialiseAllContainers';
diff --git a/demo/web/index.html b/demo/web/index.html
index 47e92fd..1d0c076 100644
--- a/demo/web/index.html
+++ b/demo/web/index.html
@@ -9,7 +9,7 @@
-
+

diff --git a/package.json b/package.json
index ba3b640..dcf67b6 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@zeecoder/container-query",
- "version": "1.2.6",
+ "version": "1.3.0",
"description": "A PostCSS plugin and Javascript runtime combination, which allows you to write @container queries in your CSS the same way you would write @media queries.",
"main": "index.js",
"author": "Viktor Hubert
",
@@ -25,10 +25,14 @@
],
"license": "MIT",
"dependencies": {
+ "es6-weak-map": "^2.0.2",
"lodash.camelcase": "^4.3.0",
"lodash.trim": "^4.5.1",
+ "mutation-observer": "^1.0.2",
"object-assign": "^4.1.1",
- "postcss": "^6.0.0"
+ "postcss": "^6.0.0",
+ "raf": "^3.3.2",
+ "resize-observer-polyfill": "^1.4.2"
},
"repository": {
"type": "git",
@@ -58,11 +62,12 @@
"lint": "eslint src",
"coveralls": "cat ./coverage/lcov.info | ./node_modules/.bin/coveralls",
"build": "babel src --out-dir build --ignore spec.js",
+ "watch:build": "babel src --watch --out-dir build --ignore spec.js",
"watch:test": "jest --watch --notify",
"prepublish": "yarn run build",
"precommit": "lint-staged",
"eslint-check": "eslint --print-config .eslintrc.js | eslint-config-prettier-check",
- "prettify": "prettier --write --tab-width=4 '{src|__mocks__}/**/*.js'"
+ "prettify": "prettier --write --tab-width 4 '{src,__mocks__}/**/*.js'"
},
"lint-staged": {
"*.js": [
diff --git a/src/postcss/containerQuery.js b/src/postcss/containerQuery.js
index 462b991..702f90f 100644
--- a/src/postcss/containerQuery.js
+++ b/src/postcss/containerQuery.js
@@ -34,6 +34,7 @@ function shouldProcessNode(node) {
}
/**
+ * @todo refactor a bit to make testing easier
* @param {{ getJSON: function }} options
*/
function containerQuery(options = {}) {
diff --git a/src/runtime/Container.js b/src/runtime/Container.js
index 7e0d9b7..0eb611a 100644
--- a/src/runtime/Container.js
+++ b/src/runtime/Container.js
@@ -1,19 +1,115 @@
import processConfig from "./processConfig";
import adjustContainer from "./adjustContainer";
+import objectAssign from "object-assign";
+import ResizeObserver from "resize-observer-polyfill";
+import MutationObserver from "mutation-observer";
+import WeakMap from "es6-weak-map";
+import raf from "raf";
+
+const containerRegistry = new WeakMap();
+
+const resizeObserver = new ResizeObserver(entries => {
+ if (!Array.isArray(entries)) {
+ return;
+ }
+
+ entries.forEach(entry => {
+ const container = containerRegistry.get(entry.target);
+
+ if (
+ typeof container === "undefined" ||
+ typeof container !== "object" ||
+ typeof container.adjust !== "function"
+ ) {
+ console.warn(
+ "Could not find Container instance for element:",
+ entry.target
+ );
+ return;
+ }
+
+ container.adjust({
+ width: entry.contentRect.width,
+ height: entry.contentRect.height
+ });
+ });
+});
+
+const mutationObserver = new MutationObserver(mutationsRecords => {
+ mutationsRecords.forEach(mutationsRecord => {
+ // Remove container element from registry and unobserve resize changes
+ mutationsRecord.removedNodes.forEach(node => {
+ if (containerRegistry.has(node) === false) {
+ return;
+ }
+
+ resizeObserver.unobserve(node);
+ containerRegistry.delete(node);
+ });
+ });
+});
/**
* @class
- * @property {HTMLElement} container
- * @property {Object} config
+ * @property {Element} containerElement
+ * @property {Object} jsonStats
+ * @property {Object} opts
*/
export default class Container {
- constructor(container, config) {
- this.adjust = adjustContainer.bind(
- this,
- container,
- processConfig(config)
+ constructor(containerElement, jsonStats, opts = {}) {
+ this.containerElement = containerElement;
+ this.processedJsonStats = processConfig(jsonStats);
+
+ this.opts = objectAssign(
+ {
+ adjustOnResize: false,
+ adjustOnInstantiation: true
+ },
+ opts
);
- this.adjust();
+ this.observeResize = this.observeResize.bind(this);
+ this.unobserveResize = this.unobserveResize.bind(this);
+ this.adjust = this.adjust.bind(this);
+
+ containerRegistry.set(containerElement, this);
+ mutationObserver.observe(this.containerElement.parentNode, {
+ childList: true
+ });
+
+ if (this.opts.adjustOnResize) {
+ this.observeResize();
+ }
+
+ if (this.opts.adjustOnInstantiation) {
+ raf(this.adjust);
+ }
+ }
+
+ /**
+ * Starts observing resize changes.
+ */
+ observeResize() {
+ resizeObserver.observe(this.containerElement);
+ }
+
+ /**
+ * Stops observing resize changes.
+ */
+ unobserveResize() {
+ resizeObserver.unobserve(this.containerElement);
+ }
+
+ /**
+ * Adjusts the container to it's current dimensions, or to the ones given.
+ *
+ * @param {ContainerDimensions} containerDimensions
+ */
+ adjust(containerDimensions = null) {
+ adjustContainer(
+ this.containerElement,
+ this.processedJsonStats,
+ containerDimensions
+ );
}
}
diff --git a/src/runtime/Container.spec.js b/src/runtime/Container.spec.js
index 5118e7d..f0d5dac 100644
--- a/src/runtime/Container.spec.js
+++ b/src/runtime/Container.spec.js
@@ -1,26 +1,219 @@
import Container from "./Container";
-jest.mock("./processConfig");
-jest.mock("./adjustContainer");
+console.warn = jest.fn();
+jest.mock("./processConfig", () => jest.fn(config => config));
+jest.mock("./adjustContainer", () => jest.fn());
+jest.mock("raf", () => jest.fn(cb => cb()));
+jest.mock("es6-weak-map", () => {
+ const mock = jest.fn();
-test("appropriate instantiation", () => {
- const processConfig = require("./processConfig").default;
- const adjustContainer = require("./adjustContainer").default;
+ mock.prototype.set = jest.fn();
+ mock.prototype.get = jest.fn();
+ mock.prototype.has = jest.fn();
+ mock.prototype.delete = jest.fn();
+
+ return mock;
+});
+
+jest.mock("resize-observer-polyfill", () => {
+ const mock = jest.fn(cb => {
+ mock.triggerEvent = cb;
+ });
+
+ mock.prototype.observe = jest.fn();
+ mock.prototype.unobserve = jest.fn();
+
+ return mock;
+});
+
+jest.mock("mutation-observer", () => {
+ const mock = jest.fn(cb => {
+ mock.triggerEvent = cb;
+ });
+
+ mock.prototype.observe = jest.fn();
+ mock.prototype.unobserve = jest.fn();
+
+ return mock;
+});
+
+beforeEach(() => {
+ require("raf").mockClear();
+ require("./adjustContainer").mockClear();
+});
+
+test("should instantiate properly", () => {
+ const ResizeObserver = require("resize-observer-polyfill");
+ const processConfig = require("./processConfig");
+ const adjustContainer = require("./adjustContainer");
+ const raf = require("raf");
+
+ const containerElement = {
+ parentNode: document.createElement("div")
+ };
- const containerElement = {};
const config = {};
const processedConfig = {};
- processConfig.mockImplementation(() => processedConfig);
const containerInstance = new Container(containerElement, config);
containerInstance.adjust();
containerInstance.adjust();
containerInstance.adjust();
+ expect(ResizeObserver).toHaveBeenCalledTimes(1);
+ expect(ResizeObserver.prototype.observe).toHaveBeenCalledTimes(0);
+ expect(raf).toHaveBeenCalledTimes(1);
expect(processConfig).toHaveBeenCalledTimes(1);
+ expect(processConfig.mock.calls[0][0]).toBe(config);
expect(adjustContainer).toHaveBeenCalledTimes(4);
expect(adjustContainer.mock.calls[0][0]).toBe(containerElement);
- expect(adjustContainer.mock.calls[0][1]).toBe(processedConfig);
+ expect(adjustContainer.mock.calls[0][1]).toEqual(processedConfig);
expect(adjustContainer.mock.calls[1][0]).toBe(containerElement);
- expect(adjustContainer.mock.calls[1][1]).toBe(processedConfig);
+ expect(adjustContainer.mock.calls[1][1]).toEqual(processedConfig);
+});
+
+test("should be able to observe resize events and switch off initial adjust call", () => {
+ const ResizeObserver = require("resize-observer-polyfill");
+ const raf = require("raf");
+ const adjustContainer = require("./adjustContainer");
+
+ const containerElement = {
+ parentNode: document.createElement("div")
+ };
+ const config = {};
+
+ const containerInstance = new Container(containerElement, config, {
+ adjustOnInstantiation: false,
+ adjustOnResize: true
+ });
+ containerInstance.observeResize();
+ containerInstance.unobserveResize();
+ containerInstance.observeResize();
+
+ expect(raf).toHaveBeenCalledTimes(0);
+ expect(adjustContainer).toHaveBeenCalledTimes(0);
+ expect(ResizeObserver).toHaveBeenCalledTimes(1);
+ expect(ResizeObserver.prototype.observe).toHaveBeenCalledTimes(3);
+ expect(ResizeObserver.prototype.unobserve).toHaveBeenCalledTimes(1);
+ expect(ResizeObserver.prototype.observe.mock.calls[0][0]).toBe(
+ containerElement
+ );
+ expect(ResizeObserver.prototype.observe.mock.calls[1][0]).toBe(
+ containerElement
+ );
+ expect(ResizeObserver.prototype.observe.mock.calls[2][0]).toBe(
+ containerElement
+ );
+ expect(ResizeObserver.prototype.unobserve.mock.calls[0][0]).toBe(
+ containerElement
+ );
+});
+
+test("should call adjust() on resize changes", () => {
+ const WeakMap = require("es6-weak-map");
+ WeakMap.prototype.set = jest.fn();
+ WeakMap.prototype.get.mockImplementationOnce(() => undefined);
+ WeakMap.prototype.get.mockImplementationOnce(element => {
+ expect(element).toBe(containerElement);
+
+ return containerInstance;
+ });
+ const ResizeObserver = require("resize-observer-polyfill");
+ const parentElement = document.createElement("div");
+ const containerElement = document.createElement("div");
+ parentElement.appendChild(containerElement);
+ const config = {};
+ const containerInstance = new Container(containerElement, config, {
+ adjustOnInstantiation: false,
+ adjustOnResize: true
+ });
+ expect(WeakMap.prototype.set).toHaveBeenCalledTimes(1);
+ expect(WeakMap.prototype.set).toHaveBeenCalledWith(
+ containerElement,
+ containerInstance
+ );
+
+ expect(ResizeObserver).toHaveBeenCalledTimes(1);
+ expect(typeof ResizeObserver.triggerEvent).toBe("function");
+ expect(() => ResizeObserver.triggerEvent()).not.toThrow();
+ expect(() => {
+ ResizeObserver.triggerEvent([
+ {
+ target: ""
+ }
+ ]);
+ }).not.toThrow();
+ expect(console.warn).toHaveBeenCalledTimes(1);
+
+ containerInstance.adjust = jest.fn();
+ expect(() => {
+ ResizeObserver.triggerEvent([
+ {
+ target: containerElement,
+ contentRect: {
+ width: 1,
+ height: 2
+ }
+ }
+ ]);
+ }).not.toThrow();
+ expect(console.warn).toHaveBeenCalledTimes(1);
+ expect(WeakMap.prototype.get).toHaveBeenCalledTimes(2);
+ expect(containerInstance.adjust).toHaveBeenCalledTimes(1);
+ expect(containerInstance.adjust).toHaveBeenCalledWith({
+ width: 1,
+ height: 2
+ });
+});
+
+test("should clean up after container element is detached from the DOM", () => {
+ const WeakMap = require("es6-weak-map");
+ WeakMap.prototype.set = jest.fn();
+ WeakMap.prototype.has = jest.fn(() => true);
+ const MutationObserver = require("mutation-observer");
+ const ResizeObserver = require("resize-observer-polyfill");
+ ResizeObserver.prototype.unobserve = jest.fn();
+ const parentElement = document.createElement("div");
+ const containerElement = document.createElement("div");
+ parentElement.appendChild(containerElement);
+ const config = {};
+ const containerInstance = new Container(containerElement, config, {
+ adjustOnInstantiation: false,
+ adjustOnResize: false
+ });
+ expect(WeakMap.prototype.set).toHaveBeenCalledTimes(1);
+ expect(WeakMap.prototype.set).toHaveBeenCalledWith(
+ containerElement,
+ containerInstance
+ );
+
+ let mutationRecords = [
+ {
+ removedNodes: [containerElement]
+ }
+ ];
+
+ MutationObserver.triggerEvent(mutationRecords);
+
+ expect(WeakMap.prototype.has).toHaveBeenCalledTimes(1);
+ expect(WeakMap.prototype.delete).toHaveBeenCalledTimes(1);
+ expect(ResizeObserver.prototype.unobserve).toHaveBeenCalledTimes(1);
+ expect(WeakMap.prototype.delete).toHaveBeenCalledWith(containerElement);
+ expect(ResizeObserver.prototype.unobserve).toHaveBeenCalledWith(
+ containerElement
+ );
+
+ // Should not clean up after non-container elements
+ mutationRecords = [
+ {
+ removedNodes: [document.createElement("div")]
+ }
+ ];
+
+ WeakMap.prototype.has = jest.fn(() => false);
+ MutationObserver.triggerEvent(mutationRecords);
+
+ expect(WeakMap.prototype.has).toHaveBeenCalledTimes(1);
+ expect(WeakMap.prototype.delete).toHaveBeenCalledTimes(1);
+ expect(ResizeObserver.prototype.unobserve).toHaveBeenCalledTimes(1);
});
diff --git a/src/runtime/adjustContainer.js b/src/runtime/adjustContainer.js
index 204be2b..a074d8d 100644
--- a/src/runtime/adjustContainer.js
+++ b/src/runtime/adjustContainer.js
@@ -11,13 +11,21 @@ import applyStylesToElements from "./applyStylesToElements";
* @param {HTMLElement} container
* @param {Object} [config] Expects a configuration object that was processed
* (and validated) by `processConfig`
+ * @param {ContainerDimensions} [containerDimensions]
*/
-export default function adjustContainer(container, config = null) {
+export default function adjustContainer(
+ container,
+ config = null,
+ containerDimensions = null
+) {
if (config === null) {
return;
}
- const containerDimensions = getContainerDimensions(container);
+ if (!containerDimensions) {
+ containerDimensions = getContainerDimensions(container);
+ }
+
const queriesLength = config.queries.length;
const changeSets = {};
diff --git a/src/runtime/adjustContainer.spec.js b/src/runtime/adjustContainer.spec.js
index e2e5dca..3d1f791 100644
--- a/src/runtime/adjustContainer.spec.js
+++ b/src/runtime/adjustContainer.spec.js
@@ -77,6 +77,20 @@ beforeEach(() => {
require("./getContainerDimensions").default.mockClear();
});
+test("should accept container dimensions", () => {
+ const getContainerDimensions = require("./getContainerDimensions").default;
+ let config = {
+ queries: []
+ };
+
+ const container = {};
+ const containerDimensions = { width: 1, height: 2 };
+
+ adjustContainer(container, config, containerDimensions);
+
+ expect(getContainerDimensions).toHaveBeenCalledTimes(0);
+});
+
test("The container and its elements should be properly adjusted with the defaults", () => {
const getContainerDimensionsMock = require(
"./getContainerDimensions"
diff --git a/yarn.lock b/yarn.lock
index 89396bc..803109f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1065,7 +1065,7 @@ es6-symbol@3.1.1, es6-symbol@^3.1, es6-symbol@^3.1.1, es6-symbol@~3.1, es6-symbo
d "1"
es5-ext "~0.10.14"
-es6-weak-map@^2.0.1:
+es6-weak-map@^2.0.1, es6-weak-map@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.2.tgz#5e3ab32251ffd1538a1f8e5ffa1357772f92d96f"
dependencies:
@@ -2407,6 +2407,10 @@ ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+mutation-observer@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/mutation-observer/-/mutation-observer-1.0.2.tgz#5059b3836180cced1d8f74efd7b3aaf7fa678841"
+
mute-stream@0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0"
@@ -2665,6 +2669,10 @@ performance-now@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5"
+performance-now@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
+
pify@^2.0.0, pify@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
@@ -2759,6 +2767,12 @@ qs@~6.4.0:
version "6.4.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
+raf@^3.3.2:
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/raf/-/raf-3.3.2.tgz#0c13be0b5b49b46f76d6669248d527cf2b02fe27"
+ dependencies:
+ performance-now "^2.1.0"
+
randomatic@^1.1.3:
version "1.1.6"
resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.6.tgz#110dcabff397e9dcff7c0789ccc0a49adf1ec5bb"
@@ -2955,6 +2969,10 @@ require-uncached@^1.0.2:
caller-path "^0.1.0"
resolve-from "^1.0.0"
+resize-observer-polyfill@^1.4.2:
+ version "1.4.2"
+ resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.4.2.tgz#a37198e6209e888acb1532a9968e06d38b6788e5"
+
resolve-from@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226"