diff --git a/packages/material-components-web/index.js b/packages/material-components-web/index.js
index 883f1c20042..21e14e68bf4 100644
--- a/packages/material-components-web/index.js
+++ b/packages/material-components-web/index.js
@@ -17,6 +17,7 @@
import * as base from '@material/base';
import * as checkbox from '@material/checkbox';
import * as formField from '@material/form-field';
+import * as gridList from '@material/grid-list';
import * as iconToggle from '@material/icon-toggle';
import * as radio from '@material/radio';
import * as ripple from '@material/ripple';
@@ -31,6 +32,7 @@ import autoInit from '@material/auto-init';
autoInit.register('MDCCheckbox', checkbox.MDCCheckbox);
autoInit.register('MDCTemporaryDrawer', drawer.MDCTemporaryDrawer);
autoInit.register('MDCRipple', ripple.MDCRipple);
+autoInit.register('MDCGridList', gridList.MDCGridList);
autoInit.register('MDCIconToggle', iconToggle.MDCIconToggle);
autoInit.register('MDCRadio', radio.MDCRadio);
autoInit.register('MDCSnackbar', snackbar.MDCSnackbar);
@@ -43,6 +45,7 @@ export {
base,
checkbox,
formField,
+ gridList,
iconToggle,
radio,
ripple,
diff --git a/packages/material-components-web/material-components-web.scss b/packages/material-components-web/material-components-web.scss
index 06a2ecd1d6a..4d5c691d6bf 100644
--- a/packages/material-components-web/material-components-web.scss
+++ b/packages/material-components-web/material-components-web.scss
@@ -22,6 +22,7 @@
@import "@material/elevation/mdc-elevation";
@import "@material/fab/mdc-fab";
@import "@material/form-field/mdc-form-field";
+@import "@material/grid-list/mdc-grid-list";
@import "@material/icon-toggle/mdc-icon-toggle";
@import "@material/layout-grid/mdc-layout-grid";
@import "@material/list/mdc-list";
diff --git a/packages/material-components-web/package.json b/packages/material-components-web/package.json
index 3245cc5a387..b9801b64814 100644
--- a/packages/material-components-web/package.json
+++ b/packages/material-components-web/package.json
@@ -23,6 +23,7 @@
"@material/elevation": "^0.1.3",
"@material/fab": "^0.3.2",
"@material/form-field": "^0.2.0",
+ "@material/grid-list": "^0.0.0",
"@material/icon-toggle": "^0.1.4",
"@material/layout-grid": "^0.1.1",
"@material/list": "^0.2.2",
diff --git a/packages/mdc-grid-list/README.md b/packages/mdc-grid-list/README.md
new file mode 100644
index 00000000000..c61d59949a5
--- /dev/null
+++ b/packages/mdc-grid-list/README.md
@@ -0,0 +1,195 @@
+# MDC Grid List
+
+MDC Grid List provides a RTL-aware Material Design Grid Lists component adhering to the
+[Material Design Grid List spec](https://material.io/guidelines/components/grid-lists.html)
+Grid List is best suited to presenting homogenous data, typically images. Each data in
+grid list is called a tile and tile maintains fixed width, height and padding between each
+other as window resizes. Meanwhile, margin changes to keep grid list remain centered.
+
+
+## Installation
+
+```
+npm install --save @material/grid-list
+```
+
+
+## Usage
+
+Basic grid list has the following structure:
+
+```html
+
+
+
+
+
+
+
+ Title
+
+
+
+
+
+
+
+ Title
+
+
+
+
+```
+
+The above markup will give you a grid list of tiles that:
+
+- Has with 4px padding in between
+- With 1x1 aspect ratio
+- Has one line footer caption with no icon
+
+You just need to put the content you want to load in `src` of
+``. However, if your
+assets don't have the same aspect ratio you as specified in the tile, it will
+distort those assets, we provide a solution of that case in
+[Use div in replace of img](#use-div-in-replace-of-img) section.
+
+Besides default setting, we provide the following modifiers. You can mix and
+match them to modify your grid list.
+
+
+### Change tile padding
+
+Grid list tiles can also have 1px padding instead of 4px by adding
+`mdc-grid-list--tile-gutter-1` modifier.
+
+```html
+
+
+ ...
+
+
+```
+
+### Change aspect ratio of tile
+
+Grid list tiles support all material guideline recommended aspect ratio:
+
+- 1x1
+- 16x9
+- 2x3
+- 3x2
+- 4x3
+- 3x4
+
+```html
+
+
+
+ ...
+
+
+```
+
+As pointed out in the previous section as well, if your
+assets don't have the same aspect ratio you as specified in the tile, it will
+distort those assets, we provide a solution of that case in
+[Use div in replace of img](#use-div-in-replace-of-img) section.
+
+
+### Add support text to secondary content (caption)
+
+Grid list support one line caption by default. You can also add a line of support
+text if needed by adding `mdc-grid-list--twoline-caption` modifier and additional
+markup
+
+```html
+
+
+
+
+
+
+
+
+ Title
+ Support text
+
+
+
+
+```
+
+### Add icon to secondary content (caption)
+
+You can add icon to caption by adding `mdc-grid-list--with-icon-align-start` or
+`mdc-grid-list--with-icon-align-end` and changing the markup.
+
+```html
+
+
+
+
+
+
+
+
+ star_border
+ Title
+
+
+
+
+```
+
+### Use div in replace of img
+
+In case you cannot ensure all your assets will have the same aspect ratio, you
+can use div instead of img markup. It will resize the assets to cover the tile
+and crop the assets to display the center part.
+
+```html
+
+
+
+
+
+
+
+ Title
+
+
+
+
+```
+
+In this case, you should set `background-image:url(...)` instead of `src`. However,
+the method results in a less semantic markup, so we don't use this method by
+default.
+
+
+### RTL Support
+
+`mdc-grid-list` is automatically RTL-aware, and will re-position elements whenever
+it, or its ancestors, has a `dir="rtl"` attribute.
+
+
+### Theme
+
+`mdc-grid-list` support theme. Tile primary uses background and text on
+background, secondary uses the primary color and text on primary.
+
+
+### Using the Foundation Class
+
+MDCGridList ships with an `MDCGridListFoundation` class that external frameworks and libraries
+can use to build their own MDCGridList components with minimal effort. As with all foundation
+classes, an adapter object must be provided. The adapter for grid list must provide the following
+functions, with correct signatures:
+
+| Method Signature | Description |
+| --- | --- |
+| `getOffsetWidth() => number` | Get root element offsetWidth. |
+| `getTileOffsetWidthAtIndex(index: number) => number` | Get offsetWidth of tile at specified index. |
+| `setTilesWidth(value: number) => void` | Set tiles container width to value. |
+| `registerResizeHandler(handler: Function) => void` | Registers a handler to be called when the surface (or its viewport) resizes. Our default implementation adds the handler as a listener to the window's `resize()` event. |
+| `deregisterResizeHandler(handler: Function) => void` | Unregisters a handler to be called when the surface (or its viewport) resizes. Our default implementation removes the handler as a listener to the window's `resize()` event. |
\ No newline at end of file
diff --git a/packages/mdc-grid-list/constants.js b/packages/mdc-grid-list/constants.js
new file mode 100644
index 00000000000..e766f0b624c
--- /dev/null
+++ b/packages/mdc-grid-list/constants.js
@@ -0,0 +1,19 @@
+/**
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+export const strings = {
+ TILES_SELECTOR: '.mdc-grid-list__tiles',
+ TILE_SELECTOR: '.mdc-grid-tile',
+};
diff --git a/packages/mdc-grid-list/foundation.js b/packages/mdc-grid-list/foundation.js
new file mode 100644
index 00000000000..6d8f12cd8ab
--- /dev/null
+++ b/packages/mdc-grid-list/foundation.js
@@ -0,0 +1,50 @@
+/**
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {MDCFoundation} from '@material/base';
+import {strings} from './constants';
+
+export default class MDCGridListFoundation extends MDCFoundation {
+ static get strings() {
+ return strings;
+ }
+
+ static get defaultAdapter() {
+ return {
+ getOffsetWidth: () => /* number */ 0,
+ getTileOffsetWidthAtIndex: (/* index: number | null */) => /* number */ 0,
+ setTilesWidth: (/* value: number | null */) => {},
+ registerResizeHandler: (/* handler: EventListener */) => {},
+ deregisterResizeHandler: (/* handler: EventListener */) => {},
+ };
+ }
+ constructor(adapter) {
+ super(Object.assign(MDCGridListFoundation.defaultAdapter, adapter));
+ this.resizeHandler_ = () => this.alignCenter();
+ }
+ init() {
+ this.alignCenter();
+ this.adapter_.registerResizeHandler(this.resizeHandler_);
+ }
+ destroy() {
+ this.adapter_.deregisterResizeHandler(this.resizeHandler_);
+ }
+ alignCenter() {
+ const gridWidth = this.adapter_.getOffsetWidth();
+ const itemWidth = this.adapter_.getTileOffsetWidthAtIndex(0);
+ this.adapter_.setTilesWidth(itemWidth * Math.floor(gridWidth / itemWidth));
+ }
+}
diff --git a/packages/mdc-grid-list/index.js b/packages/mdc-grid-list/index.js
new file mode 100644
index 00000000000..a136e95dca5
--- /dev/null
+++ b/packages/mdc-grid-list/index.js
@@ -0,0 +1,41 @@
+/**
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {MDCComponent} from '@material/base';
+
+import MDCGridListFoundation from './foundation';
+
+export {MDCGridListFoundation};
+
+export class MDCGridList extends MDCComponent {
+ static attachTo(root) {
+ return new MDCGridList(root);
+ }
+
+ getDefaultFoundation() {
+ return new MDCGridListFoundation({
+ getOffsetWidth: () => this.root_.offsetWidth,
+ getTileOffsetWidthAtIndex: (index) => {
+ return this.root_.querySelectorAll(MDCGridListFoundation.strings.TILE_SELECTOR)[index].offsetWidth;
+ },
+ setTilesWidth: (value) => {
+ this.root_.querySelector(MDCGridListFoundation.strings.TILES_SELECTOR).style.width = value + 'px';
+ },
+ registerResizeHandler: (handler) => window.addEventListener('resize', handler),
+ deregisterResizeHandler: (handler) => window.removeEventListener('resize', handler),
+ });
+ }
+}
diff --git a/packages/mdc-grid-list/mdc-grid-list.scss b/packages/mdc-grid-list/mdc-grid-list.scss
new file mode 100644
index 00000000000..278ff8642fa
--- /dev/null
+++ b/packages/mdc-grid-list/mdc-grid-list.scss
@@ -0,0 +1,237 @@
+/**
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@import "@material/rtl/mixins";
+@import "@material/theme/mixins";
+
+$mdc-grid-list-tile-width: 200px;
+$mdc-grid-list-tile-secondary-padding: 16px;
+$mdc-grid-list-tile-secondary-padding-narrow: 8px;
+$mdc-grid-list-oneline-cap-secondary-height: 48px;
+$mdc-grid-list-twoline-cap-secondary-height: 68px;
+$mdc-grid-list-tile-secondary-icon-size: 24px;
+
+// postcss-bem-linter: define grid-list
+// stylelint-disable plugin/selector-bem-pattern
+@mixin mdc-grid-list--tile-aspect($width-height-ratio) {
+ .mdc-grid-tile .mdc-grid-tile__primary {
+ padding-bottom: calc(100% / #{$width-height-ratio});
+ }
+}
+
+@mixin mdc-grid-list--tile-gutter($gutter-width) {
+ .mdc-grid-tile {
+ padding: 0 $gutter-width/2;
+ margin: $gutter-width/2 0;
+
+ &__secondary {
+ left: $gutter-width/2;
+ width: calc(100% - #{$gutter-width});
+ }
+ }
+
+ .mdc-grid-list__tiles {
+ margin: $gutter-width/2 auto;
+ }
+}
+// stylelint-enable plugin/selector-bem-pattern
+
+.mdc-grid-list {
+ /*
+ * Default grid list style:
+ * - mdc-grid-list--tile-gutter-4
+ * - mdc-grid-list--tile-aspect-1x1
+ * - mdc-grid-list--oneline-caption
+ * - mdc-grid-list--footer-caption
+ * - mdc-grid-list--no-icon
+ *
+ */
+ @include mdc-grid-list--tile-aspect(1);
+ @include mdc-grid-list--tile-gutter(4px);
+
+ // stylelint-disable plugin/selector-bem-pattern
+ .mdc-grid-tile {
+ &__secondary {
+ bottom: 0;
+ height: $mdc-grid-list-oneline-cap-secondary-height;
+ padding: $mdc-grid-list-tile-secondary-padding;
+ }
+
+ &__icon {
+ font-size: 0;
+ }
+ }
+ // stylelint-enable plugin/selector-bem-pattern
+
+ &__tiles {
+ margin: 0;
+ padding: 0;
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ }
+}
+
+// stylelint-disable plugin/selector-bem-pattern
+.mdc-grid-list--tile-gutter-1 {
+ @include mdc-grid-list--tile-gutter(1px);
+}
+
+.mdc-grid-list--tile-aspect-16x9 {
+ @include mdc-grid-list--tile-aspect(16 / 9);
+}
+
+.mdc-grid-list--tile-aspect-3x2 {
+ @include mdc-grid-list--tile-aspect(3 / 2);
+}
+
+.mdc-grid-list--tile-aspect-2x3 {
+ @include mdc-grid-list--tile-aspect(2 / 3);
+}
+
+.mdc-grid-list--tile-aspect-4x3 {
+ @include mdc-grid-list--tile-aspect(4 / 3);
+}
+
+.mdc-grid-list--tile-aspect-3x4 {
+ @include mdc-grid-list--tile-aspect(3 / 4);
+}
+
+.mdc-grid-list--twoline-caption {
+ .mdc-grid-tile__secondary {
+ height: $mdc-grid-list-twoline-cap-secondary-height;
+ }
+}
+
+.mdc-grid-list--header-caption {
+ .mdc-grid-tile__secondary {
+ top: 0;
+ bottom: auto;
+ }
+}
+
+.mdc-grid-list--with-icon-align-start {
+ .mdc-grid-tile__secondary {
+ padding-right: $mdc-grid-list-tile-secondary-padding-narrow;
+ padding-left: $mdc-grid-list-tile-secondary-padding * 2 + $mdc-grid-list-tile-secondary-icon-size;
+
+ .mdc-grid-tile__icon {
+ left: $mdc-grid-list-tile-secondary-padding;
+ font-size: $mdc-grid-list-tile-secondary-icon-size;
+ }
+ }
+
+ @include mdc-rtl {
+ .mdc-grid-tile__secondary {
+ padding-right: $mdc-grid-list-tile-secondary-padding * 2 + $mdc-grid-list-tile-secondary-icon-size;
+ padding-left: $mdc-grid-list-tile-secondary-padding-narrow;
+
+ .mdc-grid-tile__icon {
+ right: $mdc-grid-list-tile-secondary-padding;
+ }
+ }
+ }
+}
+
+.mdc-grid-list--with-icon-align-end {
+ .mdc-grid-tile__secondary {
+ padding-right: $mdc-grid-list-tile-secondary-padding * 2 + $mdc-grid-list-tile-secondary-icon-size;
+
+ .mdc-grid-tile__icon {
+ right: $mdc-grid-list-tile-secondary-padding;
+ font-size: $mdc-grid-list-tile-secondary-icon-size;
+ }
+ }
+
+ @include mdc-rtl {
+ .mdc-grid-tile__secondary {
+ padding-left: $mdc-grid-list-tile-secondary-padding * 2 + $mdc-grid-list-tile-secondary-icon-size;
+
+ .mdc-grid-tile__icon {
+ left: $mdc-grid-list-tile-secondary-padding;
+ }
+ }
+ }
+}
+// stylelint-enable plugin/selector-bem-pattern
+// postcss-bem-linter: end
+
+// postcss-bem-linter: define grid-tile
+.mdc-grid-tile {
+ // stylelint-disable plugin/selector-bem-pattern
+ --mdc-grid-tile-width: 200px;
+ // stylelint-enable plugin/selector-bem-pattern
+
+ display: block;
+ position: relative;
+ width: var(--mdc-grid-list-tile-width);
+ width: $mdc-grid-list-tile-width;
+
+ &__primary {
+ position: relative;
+ height: 0;
+
+ @include mdc-theme-prop(background-color, background);
+ @include mdc-theme-prop(color, text-primary-on-background);
+
+ &-content {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-repeat: no-repeat;
+ background-position: center;
+ background-size: cover;
+ }
+ }
+
+ &__secondary {
+ position: absolute;
+ box-sizing: border-box;
+
+ @include mdc-theme-prop(background-color, primary);
+ @include mdc-theme-prop(color, text-primary-on-primary);
+
+ .mdc-grid-tile__title {
+ display: block;
+ margin: 0;
+ padding: 0;
+ font-size: 16px;
+ font-weight: 500;
+ line-height: 16px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+ }
+
+ .mdc-grid-tile__support-text {
+ display: block;
+ margin: 0;
+ margin-top: 4px;
+ padding: 0;
+ font-size: 14px;
+ line-height: 20px;
+ }
+
+ .mdc-grid-tile__icon {
+ position: absolute;
+ top: calc(50% - #{$mdc-grid-list-tile-secondary-icon-size} / 2);
+ }
+ }
+}
+// postcss-bem-linter: end
diff --git a/packages/mdc-grid-list/package.json b/packages/mdc-grid-list/package.json
new file mode 100644
index 00000000000..b6a8cef5eb2
--- /dev/null
+++ b/packages/mdc-grid-list/package.json
@@ -0,0 +1,23 @@
+{
+ "name": "@material/grid-list",
+ "version": "0.0.0",
+ "description": "The Material Components for the web grid list component",
+ "license": "Apache-2.0",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/material-components/material-components-web.git"
+ },
+ "keywords": [
+ "material components",
+ "material design",
+ "grid list"
+ ],
+ "dependencies": {
+ "@material/base": "^0.1.2",
+ "@material/rtl": "^0.1.2",
+ "@material/theme": "^0.1.2"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/webpack.config.js b/webpack.config.js
index 52c15b10c36..319b025648c 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -77,6 +77,7 @@ module.exports = [{
checkbox: [path.resolve('./packages/mdc-checkbox/index.js')],
drawer: [path.resolve('./packages/mdc-drawer/index.js')],
formField: [path.resolve('./packages/mdc-form-field/index.js')],
+ gridList: [path.resolve('./packages/mdc-grid-list/index.js')],
iconToggle: [path.resolve('./packages/mdc-icon-toggle/index.js')],
menu: [path.resolve('./packages/mdc-menu/index.js')],
radio: [path.resolve('./packages/mdc-radio/index.js')],
@@ -143,6 +144,7 @@ module.exports = [{
'mdc.elevation': path.resolve('./packages/mdc-elevation/mdc-elevation.scss'),
'mdc.fab': path.resolve('./packages/mdc-fab/mdc-fab.scss'),
'mdc.form-field': path.resolve('./packages/mdc-form-field/mdc-form-field.scss'),
+ 'mdc.grid-list': path.resolve('./packages/mdc-grid-list/mdc-grid-list.scss'),
'mdc.icon-toggle': path.resolve('./packages/mdc-icon-toggle/mdc-icon-toggle.scss'),
'mdc.layout-grid': path.resolve('./packages/mdc-layout-grid/mdc-layout-grid.scss'),
'mdc.list': path.resolve('./packages/mdc-list/mdc-list.scss'),