From 2a96521811b9d459f43d52d0b07ec82a11ed76bf Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Wed, 28 Jun 2017 11:02:00 +1200 Subject: [PATCH 1/5] Update grid list * Use camelCase attribute names * Use compassable component api. * Use composability mixins * Add basic render tests * Remove demo grid-list warning --- addon/components/paper-grid-list.js | 33 +++--- addon/components/paper-grid-tile.js | 11 +- .../templates/components/paper-grid-list.hbs | 3 + .../templates/components/paper-grid-tile.hbs | 4 +- tests/dummy/app/templates/application.hbs | 2 +- tests/dummy/app/templates/demo/grid-list.hbs | 105 +++++++++--------- .../components/paper-grid-list-test.js | 34 ++++++ 7 files changed, 114 insertions(+), 78 deletions(-) create mode 100644 addon/templates/components/paper-grid-list.hbs create mode 100644 tests/integration/components/paper-grid-list-test.js diff --git a/addon/components/paper-grid-list.js b/addon/components/paper-grid-list.js index 313a19c20..4b3a3cf76 100644 --- a/addon/components/paper-grid-list.js +++ b/addon/components/paper-grid-list.js @@ -2,9 +2,11 @@ * @module ember-paper */ import Ember from 'ember'; +import layout from '../templates/components/paper-grid-list'; +import { ParentMixin } from 'ember-composability-tools'; import gridLayout from '../utils/grid-layout'; -const { Component, inject, computed, A, run, get, isEqual } = Ember; +const { Component, inject, computed, run, get, isEqual } = Ember; const unitCSS = (units) => { return `${units.share}% - (${units.gutter} * ${units.gutterShare})`; @@ -26,7 +28,8 @@ const media = (mediaName) => { * @class PaperGridList * @extends Ember.Component */ -export default Component.extend({ +export default Component.extend(ParentMixin, { + layout, tagName: 'md-grid-list', constants: inject.service(), @@ -34,9 +37,7 @@ export default Component.extend({ layoutInvalidated: false, tilesInvalidated: false, lastLayoutProps: {}, - tiles: computed(function() { - return A(); - }), + tiles: computed.alias('childComponents'), _invalidateLayoutListener: computed(function() { return run.bind(this, () => { @@ -47,7 +48,7 @@ export default Component.extend({ didInsertElement() { this._super(...arguments); this._watchMedia(); - this._watchResponsiveAttributes(['md-cols', 'md-row-height', 'md-gutter'], run.bind(this, this.layoutIfMediaMatch)); + this._watchResponsiveAttributes(['cols', 'rowHeight', 'gutter'], run.bind(this, this.layoutIfMediaMatch)); }, @@ -56,10 +57,6 @@ export default Component.extend({ this._unwatchMedia(); }, - registerGridTile(gridTile) { - this.get('tiles').addObject(gridTile); - }, - doLayout() { if (this.isDestroyed) { return; @@ -192,7 +189,7 @@ export default Component.extend({ // Base veritcal size of a row. vUnit = unitCSS({ share: vShare, gutterShare: hGutterShare, gutter }); - // padidngTop and marginTop are used to maintain the given aspect ratio, as + // paddingTop and marginTop are used to maintain the given aspect ratio, as // a percentage-based value for these properties is applied to the *width* of the // containing block. See http://www.w3.org/TR/CSS2/box.html#margin-properties style.paddingTop = dimensionCSS({ unit: vUnit, span: spans.row, gutter }); @@ -251,26 +248,26 @@ export default Component.extend({ _getTileSpans(tileElements) { return [].map.call(tileElements, (ele) => { return { - row: parseInt(this._getResponsiveAttribute(ele, 'md-rowspan'), 10) || 1, - col: parseInt(this._getResponsiveAttribute(ele, 'md-colspan'), 10) || 1 + row: parseInt(this._getResponsiveAttribute(ele, 'rowspan'), 10) || 1, + col: parseInt(this._getResponsiveAttribute(ele, 'colspan'), 10) || 1 }; }); }, _getColumnCount() { - let colCount = parseInt(this._getResponsiveAttribute(this, 'md-cols'), 10); + let colCount = parseInt(this._getResponsiveAttribute(this, 'cols'), 10); if (isNaN(colCount)) { - throw 'md-grid-list: md-cols attribute was not found, or contained a non-numeric value'; + throw 'md-grid-list: cols attribute was not found, or contained a non-numeric value'; } return colCount; }, _getGutter() { - return this._applyDefaultUnit(this._getResponsiveAttribute(this, 'md-gutter') || 1); + return this._applyDefaultUnit(this._getResponsiveAttribute(this, 'gutter') || 1); }, _getRowHeight() { - let rowHeight = this._getResponsiveAttribute(this, 'md-row-height'); + let rowHeight = this._getResponsiveAttribute(this, 'rowHeight'); switch (this._getRowMode()) { case 'fixed': { return this._applyDefaultUnit(rowHeight); @@ -286,7 +283,7 @@ export default Component.extend({ }, _getRowMode() { - let rowHeight = this._getResponsiveAttribute(this, 'md-row-height'); + let rowHeight = this._getResponsiveAttribute(this, 'rowHeight'); if (rowHeight === 'fit') { return 'fit'; } else if (rowHeight.indexOf(':') !== -1) { diff --git a/addon/components/paper-grid-tile.js b/addon/components/paper-grid-tile.js index a62b52882..ff9e63be7 100644 --- a/addon/components/paper-grid-tile.js +++ b/addon/components/paper-grid-tile.js @@ -3,7 +3,7 @@ */ import Ember from 'ember'; import layout from '../templates/components/paper-grid-tile'; -import PaperGridList from './paper-grid-list'; +import { ChildMixin } from 'ember-composability-tools'; const { Component, computed, inject, get } = Ember; @@ -11,7 +11,7 @@ const { Component, computed, inject, get } = Ember; * @class PaperGridTile * @extends Ember.Component */ -export default Component.extend({ +export default Component.extend(ChildMixin, { layout, tagName: 'md-grid-tile', @@ -20,10 +20,9 @@ export default Component.extend({ didInsertElement() { this._super(...arguments); - this.get('gridList').registerGridTile(this); this.get('gridList').send('invalidateTiles'); - this._watchResponsiveAttributes(['md-colspan', 'md-rowspan'], (mediaName) => { + this._watchResponsiveAttributes(['colspan', 'rowspan'], (mediaName) => { this.get('gridList').send('invalidateLayout', mediaName); }); }, @@ -34,9 +33,7 @@ export default Component.extend({ this.get('gridList').send('invalidateLayout'); }, - gridList: computed(function() { - return this.nearestOfType(PaperGridList); - }), + gridList: computed.alias('parentComponent'), _watchResponsiveAttributes(attrNames, watchFn) { diff --git a/addon/templates/components/paper-grid-list.hbs b/addon/templates/components/paper-grid-list.hbs new file mode 100644 index 000000000..63970837c --- /dev/null +++ b/addon/templates/components/paper-grid-list.hbs @@ -0,0 +1,3 @@ +{{yield (hash + tile=(component 'paper-grid-tile') +)}} diff --git a/addon/templates/components/paper-grid-tile.hbs b/addon/templates/components/paper-grid-tile.hbs index d4136f002..c365538f6 100644 --- a/addon/templates/components/paper-grid-tile.hbs +++ b/addon/templates/components/paper-grid-tile.hbs @@ -1,3 +1,5 @@
- {{yield}} + {{yield (hash + footer=(component 'paper-grid-tile-footer') + )}}
diff --git a/tests/dummy/app/templates/application.hbs b/tests/dummy/app/templates/application.hbs index 4c18a8d7a..b49eeb0a0 100644 --- a/tests/dummy/app/templates/application.hbs +++ b/tests/dummy/app/templates/application.hbs @@ -24,7 +24,7 @@ {{#submenu-item active=(is-active "demo.chips" currentRouteName) onClick=(transition-to "demo.chips")}}Chips{{/submenu-item}} {{#submenu-item active=(is-active "demo.dialog" currentRouteName) onClick=(transition-to "demo.dialog")}}Dialog{{/submenu-item}} {{#submenu-item active=(is-active "demo.divider" currentRouteName) onClick=(transition-to "demo.divider")}}Divider{{/submenu-item}} - {{#submenu-item active=(is-active "demo.grid-list" currentRouteName) onClick=(transition-to "demo.grid-list")}}Grid List {{paper-icon "warning" title="Not updated yet."}}{{/submenu-item}} + {{#submenu-item active=(is-active "demo.grid-list" currentRouteName) onClick=(transition-to "demo.grid-list")}}Grid List{{/submenu-item}} {{#submenu-item active=(is-active "demo.icons" currentRouteName) onClick=(transition-to "demo.icons")}}Icons{{/submenu-item}} {{#submenu-item active=(is-active "demo.input" currentRouteName) onClick=(transition-to "demo.input")}}Input{{/submenu-item}} {{#submenu-item active=(is-active "demo.list" currentRouteName) onClick=(transition-to "demo.list")}}List{{/submenu-item}} diff --git a/tests/dummy/app/templates/demo/grid-list.hbs b/tests/dummy/app/templates/demo/grid-list.hbs index a6fd40310..736ec0edb 100644 --- a/tests/dummy/app/templates/demo/grid-list.hbs +++ b/tests/dummy/app/templates/demo/grid-list.hbs @@ -1,49 +1,50 @@ -{{page-toolbar pageTitle="Grid List" isDemo=true isWIP=true}} +{{page-toolbar pageTitle="Grid List" isDemo=true}} {{#doc-content}}

Basic Usage

{{! BEGIN-SNIPPET grid-list.basic-usage }} {{#paper-content class="md-whiteframe-z1 grid-list-demo1"}} - {{#paper-grid-list md-cols-sm="1" md-cols-md="2" md-cols-gt-md="6" - md-row-height-gt-md="1:1" md-row-height="2:2" - md-gutter="12px" md-gutter-gt-sm="8px"}} - - {{#paper-grid-tile class="gray" md-rowspan="3" md-colspan="2" md-colspan-sm="1"}} - {{#paper-grid-tile-footer}} -

#1: (3r x 2c)

- {{/paper-grid-tile-footer}} - {{/paper-grid-tile}} - - {{#paper-grid-tile class="green"}} - {{#paper-grid-tile-footer}} -

#2: (1r x 1c)

- {{/paper-grid-tile-footer}} - {{/paper-grid-tile}} - - {{#paper-grid-tile class="yellow"}} - {{#paper-grid-tile-footer}} -

#3: (1r x 1c)

- {{/paper-grid-tile-footer}} - {{/paper-grid-tile}} - - {{#paper-grid-tile class="blue" md-rowspan="2"}} - {{#paper-grid-tile-footer}} + {{#paper-grid-list + cols-sm="1" cols-md="2" cols-gt-md="6" + rowHeight-gt-md="1:1" rowHeight="2:2" + gutter="12px" gutter-gt-sm="8px" as |grid|}} + + {{#grid.tile class="gray" rowspan="3" colspan="2" colspan-sm="1" as |tile|}} + {{#tile.footer}} +

#1: (3r x 2c)

+ {{/tile.footer}} + {{/grid.tile}} + + {{#grid.tile class="green" as |tile|}} + {{#tile.footer}} +

#2: (1r x 1c)

+ {{/tile.footer}} + {{/grid.tile}} + + {{#grid.tile class="yellow" as |tile|}} + {{#tile.footer}} +

#3: (1r x 1c)

+ {{/tile.footer}} + {{/grid.tile}} + + {{#grid.tile class="blue" rowspan="2" as |tile|}} + {{#tile.footer}}

#4: (2r x 1c)

- {{/paper-grid-tile-footer}} - {{/paper-grid-tile}} + {{/tile.footer}} + {{/grid.tile}} - {{#paper-grid-tile class="red" md-rowspan="2" md-colspan="2" md-colspan-sm="1"}} - {{#paper-grid-tile-footer}} -

#5: (2r x 2c)

- {{/paper-grid-tile-footer}} - {{/paper-grid-tile}} + {{#grid.tile class="red" rowspan="2" colspan="2" colspan-sm="1" as |tile|}} + {{#tile.footer}} +

#5: (2r x 2c)

+ {{/tile.footer}} + {{/grid.tile}} - {{#paper-grid-tile class="green" md-rowspan="2"}} - {{#paper-grid-tile-footer}} -

#6: (2r x 1c)

- {{/paper-grid-tile-footer}} - {{/paper-grid-tile}} + {{#grid.tile class="green" rowspan="2" as |tile|}} + {{#tile.footer}} +

#6: (2r x 1c)

+ {{/tile.footer}} + {{/grid.tile}} {{/paper-grid-list}} @@ -56,17 +57,18 @@

Dynamic Tiles

{{! BEGIN-SNIPPET grid-list.dynamic-tiles }} {{#paper-content class="md-whiteframe-z1 grid-list-demo-dynamicTiles"}} - {{#paper-grid-list md-cols-sm="1" md-cols-md="2" md-cols-gt-md="6" - md-row-height-gt-md="1:1" md-row-height="4:3" - md-gutter="8px" md-gutter-gt-sm="4px"}} + {{#paper-grid-list + cols-sm="1" cols-md="2" cols-gt-md="6" + rowHeight-gt-md="1:1" rowHeight="4:3" + mdGutter="8px" gutter-gt-sm="4px" as |grid|}} - {{#each tiles as |tile|}} - {{#paper-grid-tile md-rowspan=tile.span.row md-colspan=tile.span.col md-colspan-sm="1" class=tile.background}} + {{#each tiles as |dynamicTile|}} + {{#grid.tile rowspan=dynamicTile.span.row colspan=dynamicTile.span.col colspan-sm="1" class=dynamicTile.background as |tile|}} - {{#paper-grid-tile-footer}} -

{{tile.title}}

- {{/paper-grid-tile-footer}} - {{/paper-grid-tile}} + {{#tile.footer}} +

{{dynamicTile.title}}

+ {{/tile.footer}} + {{/grid.tile}} {{/each}} {{/paper-grid-list}} @@ -80,14 +82,15 @@ {{! BEGIN-SNIPPET grid-list.responsive }} {{#paper-content class="md-whiteframe-z1 grid-list-demo-responsiveTiles" layout-padding=''}} - {{#paper-grid-list md-cols-gt-md="12" md-cols-sm="3" md-cols-md="8" - md-row-height-gt-md="1:1" md-row-height="4:3" - md-gutter-gt-md="16px" md-gutter-gt-sm="8px" md-gutter="4px"}} + {{#paper-grid-list + cols-gt-md="12" cols-sm="3" cols-md="8" + rowHeight-gt-md="1:1" rowHeight="4:3" + gutter-gt-md="16px" gutter-gt-sm="8px" gutter="4px" as |grid|}} {{#each colorTiles as |tile|}} - {{#paper-grid-tile md-colspan-gt-sm=tile.colspan md-rowspan-gt-sm=tile.rowspan class=tile.style}} - {{/paper-grid-tile}} + {{#grid.tile colspan-gt-sm=tile.colspan rowspan-gt-sm=tile.rowspan class=tile.style}} + {{/grid.tile}} {{/each}} {{/paper-grid-list}} diff --git a/tests/integration/components/paper-grid-list-test.js b/tests/integration/components/paper-grid-list-test.js new file mode 100644 index 000000000..9824ccd96 --- /dev/null +++ b/tests/integration/components/paper-grid-list-test.js @@ -0,0 +1,34 @@ +import { moduleForComponent, test } from 'ember-qunit'; +import hbs from 'htmlbars-inline-precompile'; + +moduleForComponent('paper-grid-list', 'Integration | Component | paper grid list', { + integration: true +}); + +test('it renders tiles with tag name', function(assert) { + assert.expect(1); + + this.render(hbs` + {{#paper-grid-list cols="1" rowHeight="4:3" as |grid|}} + {{#grid.tile}} + {{/grid.tile}} + {{/paper-grid-list}} + `); + + assert.equal(this.$('md-grid-tile').length, 1); +}); + +test('it renders tiles with footer', function(assert) { + assert.expect(1); + + this.render(hbs` + {{#paper-grid-list cols="1" rowHeight="4:3" as |grid|}} + {{#grid.tile as |tile|}} + {{#tile.footer}} + {{/tile.footer}} + {{/grid.tile}} + {{/paper-grid-list}} + `); + + assert.equal(this.$('md-grid-tile-footer').length, 1); +}); From e6c243507ac669e07b1741c0ca10f53ae50fee76 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Fri, 7 Jul 2017 22:31:45 +1200 Subject: [PATCH 2/5] New attribute api + reworked internals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Now uses attrs like `cols=“1 gt-xs-2 gt-md-4”`. * Re worked internals, using computed properties and `didUpdateAttrs` instead of observers. * Fixed `undefined` error messages when tile `colspans` weren’t compatible with grid `cols`. * Fixed `removeListener` to properly remove event listeners. * Should be some performance increase as it no longer re calculates everything all the time. --- addon/components/paper-grid-list.js | 356 +++++++----------- addon/components/paper-grid-tile.js | 137 +++++-- addon/utils/grid-layout.js | 67 +--- tests/dummy/app/controllers/demo/grid-list.js | 6 +- tests/dummy/app/templates/demo/grid-list.hbs | 26 +- 5 files changed, 250 insertions(+), 342 deletions(-) diff --git a/addon/components/paper-grid-list.js b/addon/components/paper-grid-list.js index 4b3a3cf76..1537092fd 100644 --- a/addon/components/paper-grid-list.js +++ b/addon/components/paper-grid-list.js @@ -6,16 +6,15 @@ import layout from '../templates/components/paper-grid-list'; import { ParentMixin } from 'ember-composability-tools'; import gridLayout from '../utils/grid-layout'; -const { Component, inject, computed, run, get, isEqual } = Ember; +const { Component, inject, computed, run } = Ember; + +const mediaRegex = /(^|\s)((?:print-)|(?:[a-z]{2}-){1,2})?(\d+)(?!\S)/g; +const rowHeightRegex = /(^|\s)((?:print-)|(?:[a-z]{2}-){1,2})?(\d+(?:[a-z]{2,3}|%)?|\d+:\d+|fit)(?!\S)/g; const unitCSS = (units) => { return `${units.share}% - (${units.gutter} * ${units.gutterShare})`; }; -const positionCSS = (positions) => { - return `calc((${positions.unit} + ${positions.gutter}) * ${positions.offset})`; -}; - const dimensionCSS = (dimensions) => { return `calc((${dimensions.unit}) * ${dimensions.span} + (${dimensions.span} - 1) * ${dimensions.gutter})`; }; @@ -24,6 +23,10 @@ const media = (mediaName) => { return ((mediaName.charAt(0) !== '(') ? (`(${mediaName})`) : mediaName); }; +const mediaListenerName = (name) => { + return `${name.replace('-', '')}Listener`; +}; + /** * @class PaperGridList * @extends Ember.Component @@ -34,190 +37,81 @@ export default Component.extend(ParentMixin, { constants: inject.service(), - layoutInvalidated: false, - tilesInvalidated: false, - lastLayoutProps: {}, tiles: computed.alias('childComponents'), - _invalidateLayoutListener: computed(function() { - return run.bind(this, () => { - this.send('invalidateLayout'); - }); - }), - didInsertElement() { this._super(...arguments); - this._watchMedia(); - this._watchResponsiveAttributes(['cols', 'rowHeight', 'gutter'], run.bind(this, this.layoutIfMediaMatch)); + this._installMediaListener(); + this._updateCurrentMedia(); + }, + didUpdateAttrs() { + this._super(...arguments); + this.updateGrid(); }, willDestroyElement() { this._super(...arguments); - this._unwatchMedia(); + this._uninstallMediaListener(); }, - doLayout() { - if (this.isDestroyed) { - return; - } - try { - let tilesInvalidated = this.get('tilesInvalidated'); - this._layoutDelegate(tilesInvalidated); - } finally { - this.setProperties({ - 'layoutInvalidated': false, - 'tilesInvalidated': false + // Sets up a listener for each media query + _installMediaListener() { + for (let mediaName in this.get('constants.MEDIA')) { + let query = this.get('constants.MEDIA')[mediaName] || media(mediaName); + let mediaList = window.matchMedia(query); + let listenerName = mediaListenerName(mediaName); + + // Sets mediaList to a property so removeListener can access it + this.set(`${listenerName}List`, mediaList); + // Creates a function based on mediaName so that removeListener can remove it. + this[listenerName] = run.bind(this, (result) => { + this._mediaDidChange(mediaName, result.matches); }); - } - }, - layoutIfMediaMatch(mediaName) { - if (mediaName == null) { - this.send('invalidateLayout'); - } else if (window.matchMedia(mediaName)) { - this.send('invalidateLayout'); + // Calls '_mediaDidChange' once + this[listenerName](mediaList); + + mediaList.addListener(this[listenerName]); } }, - _watchMedia() { - - let invalidateLayoutListener = this.get('_invalidateLayoutListener'); - + _uninstallMediaListener() { for (let mediaName in this.get('constants.MEDIA')) { - let query = this.get('constants.MEDIA')[mediaName] || media(mediaName); - window.matchMedia(query).addListener(invalidateLayoutListener); + let listenerName = mediaListenerName(mediaName); + let mediaList = this.get(`${listenerName}List`); + mediaList.removeListener(this[listenerName]); } }, - _watchResponsiveAttributes(attrNames, watchFn) { - let checkObserverValues = (sender, key, mediaName) => { - let oldValue = sender.get(`old${key}`); - let newValue = sender.get(key); - - if (oldValue !== newValue) { - watchFn(mediaName); - } - - }; - - attrNames.forEach((attrName) => { - if (get(this, attrName)) { - this.set(`old${attrName}`, get(this, attrName)); - - let customObserver = run.bind(this, checkObserverValues, this, attrName); - - this.addObserver(attrName, customObserver); - } - - for (let mediaName in this.get('constants.MEDIA')) { - let normalizedName = `${attrName}-${mediaName}`; - if (get(this, normalizedName)) { - let customObserverNormalized = run.bind(this, checkObserverValues, this, normalizedName, mediaName); - this.addObserver(normalizedName, customObserverNormalized); - } - } - - }); - }, - - _unwatchMedia() { - let invalidateLayoutListener = this.get('_invalidateLayoutListener'); - for (let mediaName in this.get('constants.MEDIA')) { - let query = this.get('constants.MEDIA')[mediaName] || media(mediaName); - window.matchMedia(query).removeListener(invalidateLayoutListener); - } + _mediaDidChange(mediaName, matches) { + this.set(mediaName, matches); + run.debounce(this, this._updateCurrentMedia, 150); }, - _getResponsiveAttribute(component, attrName) { + _updateCurrentMedia() { let mediaPriorities = this.get('constants.MEDIA_PRIORITY'); - for (let i = 0; i < mediaPriorities.length; i++) { - let mediaName = mediaPriorities[i]; - let query = this.get('constants.MEDIA')[mediaName] || media(mediaName); - - if (!window.matchMedia(query).matches) { - continue; - } - - let normalizedName = `${attrName}-${mediaName}`; - if (get(component, normalizedName)) { - return get(component, normalizedName); - } - } - - // fallback on unprefixed - return get(component, attrName); + let currentMedia = mediaPriorities.filter((mediaName) => { + return this.get(mediaName); + }); + this.set('currentMedia', currentMedia); + this.updateGrid(); }, - _getTileStyle(position, spans, colCount, rowCount, gutter, rowMode, rowHeight) { - - // Percent of the available horizontal space that one column takes up. - let hShare = (1 / colCount) * 100; - - // Fraction of the gutter size that each column takes up. - let hGutterShare = (colCount - 1) / colCount; - - // Base horizontal size of a column. - let hUnit = unitCSS({ share: hShare, gutterShare: hGutterShare, gutter }); - - // The width and horizontal position of each tile is always calculated the same way, but the - // height and vertical position depends on the rowMode. - let style = { - left: positionCSS({ unit: hUnit, offset: position.col, gutter }), - width: dimensionCSS({ unit: hUnit, span: spans.col, gutter }), - // resets - paddingTop: '', - marginTop: '', - top: '', - height: '' - }; - - let vShare, vUnit; - - switch (rowMode) { - case 'fixed': { - // In fixed mode, simply use the given rowHeight. - style.top = positionCSS({ unit: rowHeight, offset: position.row, gutter }); - style.height = dimensionCSS({ unit: rowHeight, span: spans.row, gutter }); - break; - } - case 'ratio': { - // Percent of the available vertical space that one row takes up. Here, rowHeight holds - // the ratio value. For example, if the width:height ratio is 4:3, rowHeight = 1.333. - vShare = hShare / rowHeight; - - // Base veritcal size of a row. - vUnit = unitCSS({ share: vShare, gutterShare: hGutterShare, gutter }); - - // paddingTop and marginTop are used to maintain the given aspect ratio, as - // a percentage-based value for these properties is applied to the *width* of the - // containing block. See http://www.w3.org/TR/CSS2/box.html#margin-properties - style.paddingTop = dimensionCSS({ unit: vUnit, span: spans.row, gutter }); - style.marginTop = positionCSS({ unit: vUnit, offset: position.row, gutter }); - break; - } - case 'fit': { - // Fraction of the gutter size that each column takes up. - let vGutterShare = (rowCount - 1) / rowCount; - - // Percent of the available vertical space that one row takes up. - vShare = (1 / rowCount) * 100; - - // Base vertical size of a row. - vUnit = unitCSS({ share: vShare, gutterShare: vGutterShare, gutter }); - - style.top = positionCSS({ unit: vUnit, offset: position.row, gutter }); - style.height = dimensionCSS({ unit: vUnit, span: spans.row, gutter }); - break; - } - } - - return style; - + updateGrid() { + this.$().css(this.get('gridStyle')); + this.get('tiles').forEach((tile) => { + tile.$().css(tile.get('tileStyle')); + }); }, - _getGridStyle(colCount, rowCount, gutter, rowMode, rowHeight) { + gridStyle: computed('currentCols', 'rowCount', 'currentGutter', 'currentRowMode', 'currentRowHeight', function() { let style = {}; + let colCount = this.get('currentCols'); + let rowCount = this.get('rowCount'); + let gutter = this.get('currentGutter'); + let rowHeight = this.get('currentRowHeight'); + let rowMode = this.get('currentRowMode'); switch (rowMode) { case 'fixed': { @@ -243,32 +137,76 @@ export default Component.extend(ParentMixin, { } return style; - }, + }), - _getTileSpans(tileElements) { - return [].map.call(tileElements, (ele) => { + tileSpans: computed('tiles.[]', 'tiles.@each.{currentRowspan,currentColspan}', function() { + return this.get('tiles').map((tile) => { return { - row: parseInt(this._getResponsiveAttribute(ele, 'rowspan'), 10) || 1, - col: parseInt(this._getResponsiveAttribute(ele, 'colspan'), 10) || 1 + row: tile.get('currentRowspan'), + col: tile.get('currentColspan') }; }); - }, + }), - _getColumnCount() { - let colCount = parseInt(this._getResponsiveAttribute(this, 'cols'), 10); - if (isNaN(colCount)) { - throw 'md-grid-list: cols attribute was not found, or contained a non-numeric value'; + // Parses attribute string and returns hash of media sizes + _extractResponsiveSizes(string, regex = mediaRegex) { + let matches = {}; + let match; + while ((match = regex.exec(string))) { + if (match[2]) { + matches[match[2].slice(0, -1)] = match[3]; + } else { + matches.base = match[3]; + } } - return colCount; + return matches; }, - _getGutter() { - return this._applyDefaultUnit(this._getResponsiveAttribute(this, 'gutter') || 1); + // Gets attribute for current media + _getAttributeForMedia(sizes, currentMedia) { + for (let i = 0; i < currentMedia.length; i++) { + if (sizes[currentMedia[i]]) { + return sizes[currentMedia[i]]; + } + } + return sizes.base; }, - _getRowHeight() { - let rowHeight = this._getResponsiveAttribute(this, 'rowHeight'); - switch (this._getRowMode()) { + colsMedia: computed('cols', function() { + // TODO: verify valid + let sizes = this._extractResponsiveSizes(this.get('cols')); + if (Object.keys(sizes).length === 0) { + throw new Error('md-grid-list: No valid cols found'); + } + return sizes; + }), + + currentCols: computed('colsMedia', 'currentMedia.[]', function() { + return this._getAttributeForMedia(this.get('colsMedia'), this.get('currentMedia')) || 1; + }), + + gutterMedia: computed('gutter', function() { + // TODO: verify valid + return this._extractResponsiveSizes(this.get('gutter'), rowHeightRegex); + }), + + currentGutter: computed('gutterMedia', 'currentMedia.[]', function() { + return this._applyDefaultUnit(this._getAttributeForMedia(this.get('gutterMedia'), this.get('currentMedia')) || 1); + }), + + rowHeightMedia: computed('rowHeight', function() { + // TODO: verify valid + let rowHeights = this._extractResponsiveSizes(this.get('rowHeight'), rowHeightRegex); + if (Object.keys(rowHeights).length === 0) { + throw new Error('md-grid-list: No valid rowHeight found'); + } + return rowHeights; + }), + + currentRowHeight: computed('rowHeightMedia', 'currentMedia.[]', function() { + let rowHeight = this._getAttributeForMedia(this.get('rowHeightMedia'), this.get('currentMedia')); + this.set('currentRowMode', this._getRowMode(rowHeight)); + switch (this._getRowMode(rowHeight)) { case 'fixed': { return this._applyDefaultUnit(rowHeight); } @@ -280,10 +218,9 @@ export default Component.extend(ParentMixin, { return 0; } } - }, + }), - _getRowMode() { - let rowHeight = this._getResponsiveAttribute(this, 'rowHeight'); + _getRowMode(rowHeight) { if (rowHeight === 'fit') { return 'fit'; } else if (rowHeight.indexOf(':') !== -1) { @@ -293,57 +230,24 @@ export default Component.extend(ParentMixin, { } }, - _layoutDelegate(tilesInvalidated) { + rowCount: computed.alias('gridLayoutInfo.rowCount'), + + // Calculates tile positions + gridLayoutInfo: computed('tiles.[]', 'tileSpans', 'currentCols', function() { let tiles = this.get('tiles'); - let props = { - tileSpans: this._getTileSpans(tiles), - colCount: this._getColumnCount(), - rowMode: this._getRowMode(), - rowHeight: this._getRowHeight(), - gutter: this._getGutter() - }; - - if (!tilesInvalidated && isEqual(props, this.get('lastLayoutProps'))) { - return; - } + let layoutInfo = gridLayout(this.get('currentCols'), this.get('tileSpans')).layoutInfo(); - gridLayout(props.colCount, props.tileSpans, tiles) - .map((tilePositions, rowCount) => { - return { - grid: { - element: this.$(), - style: this._getGridStyle(props.colCount, rowCount, props.gutter, props.rowMode, props.rowHeight) - }, - tiles: tilePositions.map((ps, i) => { - return { - element: tiles[i].$(), - style: this._getTileStyle(ps.position, ps.spans, props.colCount, rowCount, props.gutter, props.rowMode, props.rowHeight) - }; - }) - }; - }) - .reflow(); - - this.set('lastLayoutProps', props); + tiles.forEach((tile, i) => { + let positioning = layoutInfo.positioning[i]; + tile.set('position', positioning.position); + tile.set('spans', positioning.spans); + }); - }, + return layoutInfo; + }), _applyDefaultUnit(val) { return /\D$/.test(val) ? val : `${val}px`; - }, - - actions: { - invalidateTiles() { - this.set('tilesInvalidated', true); - this.send('invalidateLayout'); - }, - - invalidateLayout() { - if (this.get('layoutInvalidated') || this.get('isDestroyed') || this.get('isDestroying')) { - return; - } - this.set('layoutInvalidated', true); - run.next(this, this.doLayout); - } } + }); diff --git a/addon/components/paper-grid-tile.js b/addon/components/paper-grid-tile.js index ff9e63be7..45630847d 100644 --- a/addon/components/paper-grid-tile.js +++ b/addon/components/paper-grid-tile.js @@ -5,7 +5,19 @@ import Ember from 'ember'; import layout from '../templates/components/paper-grid-tile'; import { ChildMixin } from 'ember-composability-tools'; -const { Component, computed, inject, get } = Ember; +const { Component, computed, run } = Ember; + +const positionCSS = (positions) => { + return `calc((${positions.unit} + ${positions.gutter}) * ${positions.offset})`; +}; + +const dimensionCSS = (dimensions) => { + return `calc((${dimensions.unit}) * ${dimensions.span} + (${dimensions.span} - 1) * ${dimensions.gutter})`; +}; + +const unitCSS = (units) => { + return `${units.share}% - (${units.gutter} * ${units.gutterShare})`; +}; /** * @class PaperGridTile @@ -15,54 +27,107 @@ export default Component.extend(ChildMixin, { layout, tagName: 'md-grid-tile', - constants: inject.service(), + gridList: computed.alias('parentComponent'), - didInsertElement() { + didUpdateAttrs() { this._super(...arguments); - - this.get('gridList').send('invalidateTiles'); - - this._watchResponsiveAttributes(['colspan', 'rowspan'], (mediaName) => { - this.get('gridList').send('invalidateLayout', mediaName); - }); + this.updateTile(); }, - willDestroyElement() { - this._super(...arguments); - - this.get('gridList').send('invalidateLayout'); + updateTile() { + let gridList = this.get('gridList'); + run.debounce(gridList, gridList.updateGrid, 150); }, - gridList: computed.alias('parentComponent'), - - _watchResponsiveAttributes(attrNames, watchFn) { - - let checkObserverValues = (sender, key) => { - let oldValue = this.get(`old${key}`); - let newValue = sender.get(key); - - if (oldValue !== newValue) { - watchFn(); - } + colspanMedia: computed('colspan', function() { + return this.get('gridList')._extractResponsiveSizes(this.get('colspan')); + }), + + currentColspan: computed('colspanMedia', 'gridList.currentMedia.[]', function() { + let colspan = this.get('gridList')._getAttributeForMedia(this.get('colspanMedia'), this.get('gridList.currentMedia')); + return parseInt(colspan, 10) || 1; + }), + + rowspanMedia: computed('rowspan', function() { + return this.get('gridList')._extractResponsiveSizes(this.get('rowspan')); + }), + + currentRowspan: computed('rowspanMedia', 'gridList.currentMedia.[]', function() { + let rowspan = this.get('gridList')._getAttributeForMedia(this.get('rowspanMedia'), this.get('gridList.currentMedia')); + return parseInt(rowspan, 10) || 1; + }), + + tileStyle: computed('position', 'spans', 'gridList.{rowCount,currentCols,currentGutter,currentRowMode,currentRowHeight}', function() { + let position = this.get('position'); + let spans = this.get('spans'); + let rowCount = this.get('gridList.rowCount'); + let colCount = this.get('gridList.currentCols'); + let gutter = this.get('gridList.currentGutter'); + let rowMode = this.get('gridList.currentRowMode'); + let rowHeight = this.get('gridList.currentRowHeight'); + + // Percent of the available horizontal space that one column takes up. + let hShare = (1 / colCount) * 100; + + // Fraction of the gutter size that each column takes up. + let hGutterShare = (colCount - 1) / colCount; + + // Base horizontal size of a column. + let hUnit = unitCSS({ share: hShare, gutterShare: hGutterShare, gutter }); + + // The width and horizontal position of each tile is always calculated the same way, but the + // height and vertical position depends on the rowMode. + let style = { + left: positionCSS({ unit: hUnit, offset: position.col, gutter }), + width: dimensionCSS({ unit: hUnit, span: spans.col, gutter }), + // resets + paddingTop: '', + marginTop: '', + top: '', + height: '' }; - attrNames.forEach((attrName) => { - if (get(this, attrName)) { - this.set(`old${attrName}`, get(this, attrName)); + let vShare, vUnit; - this.addObserver(attrName, checkObserverValues); + switch (rowMode) { + case 'fixed': { + // In fixed mode, simply use the given rowHeight. + style.top = positionCSS({ unit: rowHeight, offset: position.row, gutter }); + style.height = dimensionCSS({ unit: rowHeight, span: spans.row, gutter }); + break; + } + case 'ratio': { + // Percent of the available vertical space that one row takes up. Here, rowHeight holds + // the ratio value. For example, if the width:height ratio is 4:3, rowHeight = 1.333. + vShare = hShare / rowHeight; + + // Base veritcal size of a row. + vUnit = unitCSS({ share: vShare, gutterShare: hGutterShare, gutter }); + + // paddingTop and marginTop are used to maintain the given aspect ratio, as + // a percentage-based value for these properties is applied to the *width* of the + // containing block. See http://www.w3.org/TR/CSS2/box.html#margin-properties + style.paddingTop = dimensionCSS({ unit: vUnit, span: spans.row, gutter }); + style.marginTop = positionCSS({ unit: vUnit, offset: position.row, gutter }); + break; } + case 'fit': { + // Fraction of the gutter size that each column takes up. + let vGutterShare = (rowCount - 1) / rowCount; + + // Percent of the available vertical space that one row takes up. + vShare = (1 / rowCount) * 100; - for (let mediaName in this.get('constants.MEDIA')) { - let normalizedName = `${attrName}-${mediaName}`; - if (get(this, normalizedName)) { - this.set(`old${normalizedName}`, get(this, normalizedName)); + // Base vertical size of a row. + vUnit = unitCSS({ share: vShare, gutterShare: vGutterShare, gutter }); - this.addObserver(normalizedName, checkObserverValues); - } + style.top = positionCSS({ unit: vUnit, offset: position.row, gutter }); + style.height = dimensionCSS({ unit: vUnit, span: spans.row, gutter }); + break; } + } - }); - } + return style; + }) }); diff --git a/addon/utils/grid-layout.js b/addon/utils/grid-layout.js index 307b6e4a4..052f9883d 100644 --- a/addon/utils/grid-layout.js +++ b/addon/utils/grid-layout.js @@ -1,81 +1,22 @@ /** * @module ember-paper */ -let defaultAnimator = GridTileAnimator; /** * Publish layout function */ function GridLayout(colCount, tileSpans) { - let self, layoutInfo, gridStyles, layoutTime, mapTime, reflowTime; - - layoutInfo = calculateGridfor(colCount, tileSpans); - - return self = { - + let layoutInfo = calculateGridfor(colCount, tileSpans); + return { /* * An array of objects describing each tile's position in the grid. */ layoutInfo() { return layoutInfo; - }, - - /* - * Maps grid positioning to an element and a set of styles using the - * provided updateFn. - */ - map(updateFn) { - let info = self.layoutInfo(); - gridStyles = updateFn(info.positioning, info.rowCount); - - return self; - }, - - /* - * Default animator simply sets the element.css( ). An alternate - * animator can be provided as an argument. The function has the following - * signature: - * - * function({grid: {element: JQLite, style: Object}, tiles: Array<{element: JQLite, style: Object}>) - */ - reflow(animatorFn) { - let animator = animatorFn || defaultAnimator; - animator(gridStyles.grid, gridStyles.tiles); - return self; - }, - - /* - * Timing for the most recent layout run. - */ - performance() { - return { - tileCount: tileSpans.length, - layoutTime, - mapTime, - reflowTime, - totalTime: layoutTime + mapTime + reflowTime - }; } }; } -/* - * Default Gridlist animator simple sets the css for each element; - * NOTE: any transitions effects must be manually set in the CSS. - * e.g. - * - * md-grid-tile { - * transition: all 700ms ease-out 50ms; - * } - * - */ -function GridTileAnimator(grid, tiles) { - grid.element.css(grid.style); - tiles.forEach(function(t) { - t.element.css(t.style); - }); -} - /* * Calculates the positions of tiles. * @@ -109,9 +50,7 @@ function calculateGridfor(colCount, tileSpans) { function reserveSpace(spans, i) { if (spans.col > colCount) { - throw `md-grid-list: Tile at position ${i} has a colspan - (${spans.col}) that exceeds the column count - (${colCount})`; + throw new Error(`md-grid-list: Tile at position ${i} has a colspan (${spans.col}) that exceeds the column count (${colCount})`); } let start = 0; diff --git a/tests/dummy/app/controllers/demo/grid-list.js b/tests/dummy/app/controllers/demo/grid-list.js index 93f8b0006..8189324e9 100644 --- a/tests/dummy/app/controllers/demo/grid-list.js +++ b/tests/dummy/app/controllers/demo/grid-list.js @@ -68,11 +68,11 @@ function randomColor() { function randomSpan() { let r = Math.random(); if (r < 0.8) { - return 1; + return 'gt-sm-1'; } else if (r < 0.9) { - return 2; + return 'gt-sm-2'; } else { - return 3; + return 'gt-sm-3'; } } diff --git a/tests/dummy/app/templates/demo/grid-list.hbs b/tests/dummy/app/templates/demo/grid-list.hbs index 736ec0edb..a9466aa89 100644 --- a/tests/dummy/app/templates/demo/grid-list.hbs +++ b/tests/dummy/app/templates/demo/grid-list.hbs @@ -6,11 +6,11 @@ {{#paper-content class="md-whiteframe-z1 grid-list-demo1"}} {{#paper-grid-list - cols-sm="1" cols-md="2" cols-gt-md="6" - rowHeight-gt-md="1:1" rowHeight="2:2" - gutter="12px" gutter-gt-sm="8px" as |grid|}} + cols="1 md-2 gt-md-6" + rowHeight="gt-md-1:1 2:2" + gutter="12px gt-sm-8px" as |grid|}} - {{#grid.tile class="gray" rowspan="3" colspan="2" colspan-sm="1" as |tile|}} + {{#grid.tile class="gray" rowspan="2" colspan="sm-1 gt-sm-2" as |tile|}} {{#tile.footer}}

#1: (3r x 2c)

{{/tile.footer}} @@ -34,7 +34,7 @@ {{/tile.footer}} {{/grid.tile}} - {{#grid.tile class="red" rowspan="2" colspan="2" colspan-sm="1" as |tile|}} + {{#grid.tile class="red" rowspan="2" colspan="sm-1 gt-sm-2" as |tile|}} {{#tile.footer}}

#5: (2r x 2c)

{{/tile.footer}} @@ -58,12 +58,12 @@ {{! BEGIN-SNIPPET grid-list.dynamic-tiles }} {{#paper-content class="md-whiteframe-z1 grid-list-demo-dynamicTiles"}} {{#paper-grid-list - cols-sm="1" cols-md="2" cols-gt-md="6" - rowHeight-gt-md="1:1" rowHeight="4:3" - mdGutter="8px" gutter-gt-sm="4px" as |grid|}} + cols="sm-2 md-4 gt-md-6" + rowHeight="4:3 gt-md-1:1" + gutter="8px gt-sm-4px" as |grid|}} {{#each tiles as |dynamicTile|}} - {{#grid.tile rowspan=dynamicTile.span.row colspan=dynamicTile.span.col colspan-sm="1" class=dynamicTile.background as |tile|}} + {{#grid.tile rowspan=dynamicTile.span.row colspan=(concat dynamicTile.span.col ' xs-1') class=dynamicTile.background as |tile|}} {{#tile.footer}}

{{dynamicTile.title}}

@@ -83,13 +83,13 @@ {{#paper-content class="md-whiteframe-z1 grid-list-demo-responsiveTiles" layout-padding=''}} {{#paper-grid-list - cols-gt-md="12" cols-sm="3" cols-md="8" - rowHeight-gt-md="1:1" rowHeight="4:3" - gutter-gt-md="16px" gutter-gt-sm="8px" gutter="4px" as |grid|}} + cols="3 md-8 gt-md-12" + rowHeight="4:3 gt-md-1:1" + gutter="4px md-8px gt-md-16px" as |grid|}} {{#each colorTiles as |tile|}} - {{#grid.tile colspan-gt-sm=tile.colspan rowspan-gt-sm=tile.rowspan class=tile.style}} + {{#grid.tile colspan=tile.colspan rowspan=tile.rowspan class=tile.style}} {{/grid.tile}} {{/each}} From 86217469b53f125b619c9bef0fb19afa0930f17b Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Fri, 7 Jul 2017 22:41:23 +1200 Subject: [PATCH 3/5] remove leftover comments --- addon/components/paper-grid-list.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/addon/components/paper-grid-list.js b/addon/components/paper-grid-list.js index 1537092fd..c6f51e32c 100644 --- a/addon/components/paper-grid-list.js +++ b/addon/components/paper-grid-list.js @@ -173,7 +173,6 @@ export default Component.extend(ParentMixin, { }, colsMedia: computed('cols', function() { - // TODO: verify valid let sizes = this._extractResponsiveSizes(this.get('cols')); if (Object.keys(sizes).length === 0) { throw new Error('md-grid-list: No valid cols found'); @@ -186,7 +185,6 @@ export default Component.extend(ParentMixin, { }), gutterMedia: computed('gutter', function() { - // TODO: verify valid return this._extractResponsiveSizes(this.get('gutter'), rowHeightRegex); }), @@ -195,7 +193,6 @@ export default Component.extend(ParentMixin, { }), rowHeightMedia: computed('rowHeight', function() { - // TODO: verify valid let rowHeights = this._extractResponsiveSizes(this.get('rowHeight'), rowHeightRegex); if (Object.keys(rowHeights).length === 0) { throw new Error('md-grid-list: No valid rowHeight found'); From d4e324cc77d3740f69e4198a9a0d1af925b3281a Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Sun, 9 Jul 2017 11:19:36 +1200 Subject: [PATCH 4/5] Remove redundant _updateCurrentMedia --- addon/components/paper-grid-list.js | 52 +++++++++++------------------ addon/components/paper-grid-tile.js | 17 +++++----- addon/utils/grid-layout.js | 35 ++++++++----------- 3 files changed, 42 insertions(+), 62 deletions(-) diff --git a/addon/components/paper-grid-list.js b/addon/components/paper-grid-list.js index c6f51e32c..53035a28f 100644 --- a/addon/components/paper-grid-list.js +++ b/addon/components/paper-grid-list.js @@ -42,7 +42,6 @@ export default Component.extend(ParentMixin, { didInsertElement() { this._super(...arguments); this._installMediaListener(); - this._updateCurrentMedia(); }, didUpdateAttrs() { @@ -65,9 +64,9 @@ export default Component.extend(ParentMixin, { // Sets mediaList to a property so removeListener can access it this.set(`${listenerName}List`, mediaList); // Creates a function based on mediaName so that removeListener can remove it. - this[listenerName] = run.bind(this, (result) => { + this.set(listenerName, run.bind(this, (result) => { this._mediaDidChange(mediaName, result.matches); - }); + })); // Calls '_mediaDidChange' once this[listenerName](mediaList); @@ -86,7 +85,7 @@ export default Component.extend(ParentMixin, { _mediaDidChange(mediaName, matches) { this.set(mediaName, matches); - run.debounce(this, this._updateCurrentMedia, 150); + run.debounce(this, this._updateCurrentMedia, 50); }, _updateCurrentMedia() { @@ -99,19 +98,21 @@ export default Component.extend(ParentMixin, { }, updateGrid() { - this.$().css(this.get('gridStyle')); + this.$().css(this._gridStyle()); this.get('tiles').forEach((tile) => { - tile.$().css(tile.get('tileStyle')); + tile.$().css(tile._tileStyle()); }); }, - gridStyle: computed('currentCols', 'rowCount', 'currentGutter', 'currentRowMode', 'currentRowHeight', function() { + _gridStyle() { + this._setTileLayout(); + let style = {}; let colCount = this.get('currentCols'); - let rowCount = this.get('rowCount'); let gutter = this.get('currentGutter'); let rowHeight = this.get('currentRowHeight'); let rowMode = this.get('currentRowMode'); + let rowCount = this.get('rowCount'); switch (rowMode) { case 'fixed': { @@ -137,16 +138,19 @@ export default Component.extend(ParentMixin, { } return style; - }), + }, - tileSpans: computed('tiles.[]', 'tiles.@each.{currentRowspan,currentColspan}', function() { - return this.get('tiles').map((tile) => { - return { - row: tile.get('currentRowspan'), - col: tile.get('currentColspan') - }; + // Calculates tile positions + _setTileLayout() { + let tiles = this.get('tiles'); + let layoutInfo = gridLayout(this.get('currentCols'), tiles); + + tiles.forEach((tile, i) => { + tile.set('position', layoutInfo.positions[i]); }); - }), + + this.set('rowCount', layoutInfo.rowCount); + }, // Parses attribute string and returns hash of media sizes _extractResponsiveSizes(string, regex = mediaRegex) { @@ -227,22 +231,6 @@ export default Component.extend(ParentMixin, { } }, - rowCount: computed.alias('gridLayoutInfo.rowCount'), - - // Calculates tile positions - gridLayoutInfo: computed('tiles.[]', 'tileSpans', 'currentCols', function() { - let tiles = this.get('tiles'); - let layoutInfo = gridLayout(this.get('currentCols'), this.get('tileSpans')).layoutInfo(); - - tiles.forEach((tile, i) => { - let positioning = layoutInfo.positioning[i]; - tile.set('position', positioning.position); - tile.set('spans', positioning.spans); - }); - - return layoutInfo; - }), - _applyDefaultUnit(val) { return /\D$/.test(val) ? val : `${val}px`; } diff --git a/addon/components/paper-grid-tile.js b/addon/components/paper-grid-tile.js index 45630847d..8cb6ad046 100644 --- a/addon/components/paper-grid-tile.js +++ b/addon/components/paper-grid-tile.js @@ -36,7 +36,7 @@ export default Component.extend(ChildMixin, { updateTile() { let gridList = this.get('gridList'); - run.debounce(gridList, gridList.updateGrid, 150); + run.debounce(gridList, gridList.updateGrid, 50); }, colspanMedia: computed('colspan', function() { @@ -57,9 +57,10 @@ export default Component.extend(ChildMixin, { return parseInt(rowspan, 10) || 1; }), - tileStyle: computed('position', 'spans', 'gridList.{rowCount,currentCols,currentGutter,currentRowMode,currentRowHeight}', function() { + _tileStyle() { let position = this.get('position'); - let spans = this.get('spans'); + let currentColspan = this.get('currentColspan'); + let currentRowspan = this.get('currentRowspan'); let rowCount = this.get('gridList.rowCount'); let colCount = this.get('gridList.currentCols'); let gutter = this.get('gridList.currentGutter'); @@ -79,7 +80,7 @@ export default Component.extend(ChildMixin, { // height and vertical position depends on the rowMode. let style = { left: positionCSS({ unit: hUnit, offset: position.col, gutter }), - width: dimensionCSS({ unit: hUnit, span: spans.col, gutter }), + width: dimensionCSS({ unit: hUnit, span: currentColspan, gutter }), // resets paddingTop: '', marginTop: '', @@ -93,7 +94,7 @@ export default Component.extend(ChildMixin, { case 'fixed': { // In fixed mode, simply use the given rowHeight. style.top = positionCSS({ unit: rowHeight, offset: position.row, gutter }); - style.height = dimensionCSS({ unit: rowHeight, span: spans.row, gutter }); + style.height = dimensionCSS({ unit: rowHeight, span: currentRowspan, gutter }); break; } case 'ratio': { @@ -107,7 +108,7 @@ export default Component.extend(ChildMixin, { // paddingTop and marginTop are used to maintain the given aspect ratio, as // a percentage-based value for these properties is applied to the *width* of the // containing block. See http://www.w3.org/TR/CSS2/box.html#margin-properties - style.paddingTop = dimensionCSS({ unit: vUnit, span: spans.row, gutter }); + style.paddingTop = dimensionCSS({ unit: vUnit, span: currentRowspan, gutter }); style.marginTop = positionCSS({ unit: vUnit, offset: position.row, gutter }); break; } @@ -122,12 +123,12 @@ export default Component.extend(ChildMixin, { vUnit = unitCSS({ share: vShare, gutterShare: vGutterShare, gutter }); style.top = positionCSS({ unit: vUnit, offset: position.row, gutter }); - style.height = dimensionCSS({ unit: vUnit, span: spans.row, gutter }); + style.height = dimensionCSS({ unit: vUnit, span: currentRowspan, gutter }); break; } } return style; - }) + } }); diff --git a/addon/utils/grid-layout.js b/addon/utils/grid-layout.js index 052f9883d..3cff959b2 100644 --- a/addon/utils/grid-layout.js +++ b/addon/utils/grid-layout.js @@ -5,16 +5,8 @@ /** * Publish layout function */ -function GridLayout(colCount, tileSpans) { - let layoutInfo = calculateGridfor(colCount, tileSpans); - return { - /* - * An array of objects describing each tile's position in the grid. - */ - layoutInfo() { - return layoutInfo; - } - }; +function GridLayout(colCount, tiles) { + return calculateGridfor(colCount, tiles); } /* @@ -33,24 +25,23 @@ function GridLayout(colCount, tileSpans) { * tile, spaceTracker's elements are each decremented by 1 to a minimum * of 0. Rows are searched in this fashion until space is found. */ -function calculateGridfor(colCount, tileSpans) { +function calculateGridfor(colCount, tiles) { let curCol = 0; let curRow = 0; let spaceTracker = newSpaceTracker(); return { - positioning: tileSpans.map(function(spans, i) { - return { - spans, - position: reserveSpace(spans, i) - }; + positions: tiles.map(function(tile, i) { + return reserveSpace(tile, i); }), rowCount: curRow + Math.max(...spaceTracker) }; - function reserveSpace(spans, i) { - if (spans.col > colCount) { - throw new Error(`md-grid-list: Tile at position ${i} has a colspan (${spans.col}) that exceeds the column count (${colCount})`); + function reserveSpace(tile, i) { + let colspan = tile.get('currentColspan'); + let rowspan = tile.get('currentRowspan'); + if (colspan > colCount) { + throw new Error(`md-grid-list: Tile at position ${i} has a colspan (${colspan}) that exceeds the column count (${colCount})`); } let start = 0; @@ -61,7 +52,7 @@ function calculateGridfor(colCount, tileSpans) { // this, recognize that you've iterated across an entire row looking for // space, and if so fast-forward by the minimum rowSpan count. Repeat // until the required space opens up. - while (end - start < spans.col) { + while (end - start < colspan) { if (curCol >= colCount) { nextRow(); continue; @@ -77,8 +68,8 @@ function calculateGridfor(colCount, tileSpans) { curCol = end + 1; } - adjustRow(start, spans.col, spans.row); - curCol = start + spans.col; + adjustRow(start, colspan, rowspan); + curCol = start + colspan; return { col: start, From 551a63c0cd8464cd74294ef551df2d1ba34e4575 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Sun, 9 Jul 2017 11:19:56 +1200 Subject: [PATCH 5/5] Use wait in tests --- tests/integration/components/paper-grid-list-test.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/integration/components/paper-grid-list-test.js b/tests/integration/components/paper-grid-list-test.js index 9824ccd96..341a745ef 100644 --- a/tests/integration/components/paper-grid-list-test.js +++ b/tests/integration/components/paper-grid-list-test.js @@ -1,5 +1,6 @@ import { moduleForComponent, test } from 'ember-qunit'; import hbs from 'htmlbars-inline-precompile'; +import wait from 'ember-test-helpers/wait'; moduleForComponent('paper-grid-list', 'Integration | Component | paper grid list', { integration: true @@ -14,8 +15,9 @@ test('it renders tiles with tag name', function(assert) { {{/grid.tile}} {{/paper-grid-list}} `); - - assert.equal(this.$('md-grid-tile').length, 1); + return wait().then(() => { + assert.equal(this.$('md-grid-tile').length, 1); + }); }); test('it renders tiles with footer', function(assert) { @@ -29,6 +31,7 @@ test('it renders tiles with footer', function(assert) { {{/grid.tile}} {{/paper-grid-list}} `); - - assert.equal(this.$('md-grid-tile-footer').length, 1); + return wait().then(() => { + assert.equal(this.$('md-grid-tile-footer').length, 1); + }); });