Repository: PolymerLabs/app-layout
Branch: master
Commit: 5882cd26f0e6
Files: 148
Total size: 1.4 MB
Directory structure:
gitextract_x_yw3dwn/
├── .github/
│ ├── CODEOWNERS
│ └── ISSUE_TEMPLATE.md
├── .gitignore
├── .npmignore
├── .travis.yml
├── CONTRIBUTING.md
├── README.md
├── app-box/
│ ├── README.md
│ ├── app-box.js
│ └── demo/
│ ├── document-scroll.html
│ ├── index.html
│ └── scrolling-region.html
├── app-drawer/
│ ├── README.md
│ ├── app-drawer.js
│ ├── demo/
│ │ ├── index.html
│ │ ├── left-drawer.html
│ │ └── right-drawer.html
│ └── test/
│ ├── app-drawer.html
│ └── index.html
├── app-drawer-layout/
│ ├── README.md
│ ├── app-drawer-layout.js
│ ├── demo/
│ │ └── index.html
│ └── test/
│ ├── app-drawer-layout.html
│ └── index.html
├── app-grid/
│ ├── README.md
│ ├── app-grid-style.js
│ ├── demo/
│ │ ├── aspect-ratio.html
│ │ ├── flickr-grid-layout.html
│ │ ├── index.html
│ │ ├── md-grid-layout.html
│ │ └── simple-responsive-grid.html
│ └── test/
│ ├── app-grid-1.html
│ ├── app-grid-2.html
│ ├── app-grid-3.html
│ └── index.html
├── app-header/
│ ├── README.md
│ ├── app-header.js
│ ├── demo/
│ │ ├── blend-background-1.html
│ │ ├── blend-background-2.html
│ │ ├── blend-background-3.html
│ │ ├── contacts.html
│ │ ├── custom-sticky-element-1.html
│ │ ├── custom-sticky-element-2.html
│ │ ├── give.html
│ │ ├── index.html
│ │ ├── music.html
│ │ ├── no-effects.html
│ │ └── notes.html
│ └── test/
│ ├── app-header.html
│ └── index.html
├── app-header-layout/
│ ├── README.md
│ ├── app-header-layout.js
│ ├── demo/
│ │ ├── footer.html
│ │ ├── index.html
│ │ ├── music.html
│ │ ├── scrolling-region.html
│ │ └── simple.html
│ └── test/
│ ├── app-header-layout.html
│ └── index.html
├── app-layout-behavior/
│ └── app-layout-behavior.js
├── app-layout.js
├── app-scroll-effects/
│ ├── README.md
│ ├── app-scroll-effects-behavior.js
│ ├── app-scroll-effects.js
│ ├── effects/
│ │ ├── blend-background.js
│ │ ├── fade-background.js
│ │ ├── material.js
│ │ ├── parallax-background.js
│ │ ├── resize-snapped-title.js
│ │ ├── resize-title.js
│ │ └── waterfall.js
│ └── test/
│ ├── app-scroll-effects-behavior.html
│ ├── blend-background.html
│ ├── fade-background.html
│ ├── index.html
│ ├── parallax-background.html
│ ├── resize-snapped-title.html
│ ├── resize-title.html
│ ├── utils.js
│ ├── waterfall.html
│ └── x-container.js
├── app-toolbar/
│ ├── README.md
│ ├── app-toolbar.js
│ ├── demo/
│ │ └── index.html
│ └── test/
│ ├── app-toolbar.html
│ └── index.html
├── bower.json
├── demo/
│ ├── contacts.json
│ ├── demo1.html
│ ├── demo2.html
│ ├── demo3.html
│ ├── demo4.html
│ ├── demo5.html
│ ├── demo6.html
│ ├── demo7.html
│ ├── index.html
│ └── sample-content.js
├── formatconfig.json
├── gen-tsd.json
├── helpers/
│ ├── helpers.js
│ └── test/
│ ├── index.html
│ ├── register-effect.html
│ └── scroll.html
├── manifest.json
├── package.json
├── patterns/
│ ├── expand-card/
│ │ └── index.html
│ ├── md-responsive-toolbar/
│ │ └── index.html
│ └── transform-navigation/
│ ├── index.html
│ └── x-app.js
├── templates/
│ ├── getting-started/
│ │ ├── index.html
│ │ ├── package.json
│ │ └── x-app.js
│ ├── landing-page/
│ │ ├── index.html
│ │ ├── package.json
│ │ └── x-app.js
│ ├── pesto/
│ │ ├── data/
│ │ │ └── recipes.json
│ │ ├── index.html
│ │ ├── manifest.json
│ │ ├── package.json
│ │ └── src/
│ │ ├── app-icons.js
│ │ ├── recipe-app.js
│ │ ├── recipe-detail.js
│ │ └── recipe-list.js
│ ├── publishing/
│ │ ├── data/
│ │ │ └── articles.json
│ │ ├── index.html
│ │ ├── manifest.json
│ │ ├── package.json
│ │ └── src/
│ │ ├── app-icons.js
│ │ ├── article-detail.js
│ │ ├── article-headline.js
│ │ ├── blog-app.js
│ │ └── two-columns-grid.js
│ ├── shrine/
│ │ ├── data/
│ │ │ ├── featured.json
│ │ │ └── items.json
│ │ ├── index.html
│ │ ├── manifest.json
│ │ ├── package.json
│ │ └── src/
│ │ ├── shrine-app.js
│ │ ├── shrine-detail.js
│ │ ├── shrine-featured-item.js
│ │ ├── shrine-item.js
│ │ ├── shrine-list.js
│ │ └── shrine-simple-item.js
│ └── test-drive/
│ ├── index.html
│ ├── package.json
│ └── test-app.js
├── test/
│ └── index.html
└── wct.conf.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/CODEOWNERS
================================================
* @keanulee @frankiefu
/.travis.yml @azakus
================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
### Description
### Expected outcome
### Actual outcome
### Live Demo
### Steps to reproduce
### Browsers Affected
- [ ] Chrome
- [ ] Firefox
- [ ] Safari 9
- [ ] Safari 8
- [ ] Safari 7
- [ ] Edge
- [ ] IE 11
- [ ] IE 10
================================================
FILE: .gitignore
================================================
bower_components*
bower-*.json
node_modules
*.d.ts
*.tgz
================================================
FILE: .npmignore
================================================
*.tgz
.github
.travis.yml
formatconfig.json
gen-tsd.json
test/
wct.conf.json
================================================
FILE: .travis.yml
================================================
language: node_js
sudo: 'false'
dist: trusty
node_js: node
addons:
firefox: latest
chrome: stable
before_script:
- npm install -g polymer-cli
- git checkout package-lock.json
- >-
npm run format && git diff --exit-code || (echo -e '\n\033[31mERROR:\033[0m
Project is not formatted. Please run "npm run format".' && false)
script:
- xvfb-run polymer test --module-resolution=node --npm
- >-
if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then polymer test
--module-resolution=node --npm -s 'default'; fi
env:
global:
- secure: >-
UFbrYeLJe/Z54rZ+WFw9R6BnkkSYwBM6RMUEcVRJmZpEErkXxna9mbIStFJgqPTBcRbEkU5CINj0NbHgQ/OOJZY8Dx1jozNPG6tHOxH9XGQKE6S78hOh4r6WlHiq4yUye0XJgYeZwfOaQDsPTbnL2IsNzTM5wuJr8OPHAylxkHbTzemyiXnrd9co6EGhe+ucwZ6eR+q84agdFxF2T+FbcA5V5a04KKbwrfVdTnUa1nb3wkfjMYNQMigp378HXUtMjaJkq4q16OGzORkmwheWGlPV1Paw/lh7ED6HtfkriTWOONKcrGSeW4XH4u3onyL8gKDjk1cAjcHsz3YD+b2+3Yh2r6tsSoHQk/Tk0z20qUpJ02VOB8PHYRXpHKBUVQjsvamPNx//UFoA95HOy3jix2GBfaf+cC9gElFd6IzBRZd80+WDQ2pdwYv+BJYG1Peyn+5UdqBkK7UzStWHVrqdLGMEC//c9aBLnvvguG7PGiu7ecQRrNgUib1zOgCE5IBHtqOIaxCIB9po29NYZmydpfPcuNsZyvBH+kjJQwJ0sp37mJ20B/nBwQ1m9I2b64BatkeHpnlhuAGtvGxTW1yg62OUAy5kYamWZo+rLEkcHBR98Jg6lh1kMRJWgd5pBxRs05JL28NXN+ITr0njCFxOhaJbcmhpcd/WQcS1Sar+lK4=
- secure: >-
g6vl6SKb0pwi5z5YUtwg5oI5Yg2oT7PQAfkS7Sl83oiL6uXWQGzpxIrzp92BLeZJbBJ/niwh1bPPZUe8ct08VD6o+6grESvxfia3wf11UQ21xGVt0y6koQPa9aZLgsfURpVUZCLDhf8zWyFAs4jN6nBdkv8VFxaIW7eRnouuQOQyiDRQYlnZv3nP6nK+D0ItOzNCIXoI27LuEGW5TgeJNm7rfE9GuGNS5ZwLgq/BeVxq17fo/3g43TwRwFXdGah1uDnhy4b7WaCzZcXyiNdN3A0TmWnWROI9s+BhAkG5fQsT0JuKpCgZwPdPn+MK3JCykcGxSot3TdnxwelvVAwxMy18BFkA+CdG7H6X0Qd8d/VeighU8AXq1qLLi5rwB+AWylQ6nWFIsAvuroZOHPeKCpq9l3MDwa4D+t5aXVrkfpi5ZtaooZF4mPKB/U4BKU2+pprCun/9qP//cwiMnJrJkP+ObgxTzl9Z6OfzbjLAE01kHzi4B8Ju/jcLkxk08Q/AyOQANpC4H1hCl+P77RJjkNN9+F4qjiz0kJftpj6hlbwmsRqTHXVBkrItTuIQ6aBLHgw415WCYrpRWVfCOL3fhe/doZnIr1KLcGm3Ht8TwOVUXxFCFapaax80rlzSrmV3eKw8ybIkOuLHjDn3vg6xc+O6O4unWmDhs0admkoRjIM=
cache:
directories:
- node_modules
================================================
FILE: CONTRIBUTING.md
================================================
# Polymer Elements
## Guide for Contributors
Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
### Filing Issues
**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
1. **Who will use the feature?** _“As someone filling out a form…”_
2. **When will they use the feature?** _“When I enter an invalid value…”_
3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
**If you are filing an issue to report a bug**, please provide:
1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
```markdown
The `paper-foo` element causes the page to turn pink when clicked.
## Expected outcome
The page stays the same color.
## Actual outcome
The page turns pink.
## Steps to reproduce
1. Put a `paper-foo` element in the page.
2. Open the page in a web browser.
3. Click the `paper-foo` element.
```
2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
### Submitting Pull Requests
**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
When submitting pull requests, please provide:
1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
```markdown
(For a single issue)
Fixes #20
(For multiple issues)
Fixes #32, fixes #40
```
2. **A succinct description of the design** used to fix any related issues. For example:
```markdown
This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
```
3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
================================================
FILE: README.md
================================================
[](https://www.npmjs.com/package/@polymer/app-layout)
[](https://travis-ci.org/PolymerElements/app-layout)
[](https://webcomponents.org/element/@polymer/app-layout)
## App Layout
A collection of elements, along with guidelines and templates that can be used to structure your app’s layout.
## What is inside
### Elements
- [app-box](https://github.com/PolymerElements/app-layout/tree/master/app-box) - A container element that can have scroll effects - visual effects based on scroll position.
- [app-drawer](https://github.com/PolymerElements/app-layout/tree/master/app-drawer) - A navigation drawer that can slide in from the left or right.
- [app-drawer-layout](https://github.com/PolymerElements/app-layout/tree/master/app-drawer-layout) - A wrapper element that positions an app-drawer and other content.
- [app-grid](https://github.com/PolymerElements/app-layout/tree/master/app-grid) - A helper class useful for creating responsive, fluid grid layouts using custom properties.
- [app-header](https://github.com/PolymerElements/app-layout/tree/master/app-header) - A container element for app-toolbars at the top of the screen that can have scroll effects - visual effects based on scroll position.
- [app-header-layout](https://github.com/PolymerElements/app-layout/tree/master/app-header-layout) - A wrapper element that positions an app-header and other content.
- [app-toolbar](https://github.com/PolymerElements/app-layout/tree/master/app-toolbar) - A horizontal toolbar containing items that can be used for label, navigation, search and actions.
### Templates
The templates are a means to define, illustrate and share best practices in App Layout. Pick a template and customize it:
- **Getting started**
([Demo](https://polymerelements.github.io/app-layout/templates/getting-started) - [Source](/templates/getting-started))
- **Landing page**
([Demo](https://polymerelements.github.io/app-layout/templates/landing-page) - [Source](/templates/landing-page))
- **Publishing: Zuperkülblog**
([Demo](https://polymerelements.github.io/app-layout/templates/publishing) - [Source](/templates/publishing))
- **Shop: Shrine**
([Demo](https://polymerelements.github.io/app-layout/templates/shrine) - [Source](/templates/shrine))
- **Blog: Pesto**
([Demo](https://polymerelements.github.io/app-layout/templates/pesto) - [Source](/templates/pesto))
- **Scroll effects: Test drive**
([Demo](https://polymerelements.github.io/app-layout/templates/test-drive) - [Source](/templates/test-drive))
### Patterns
Sample code for various UI patterns:
- **Transform navigation:**
As more screen space is available, side navigation can transform into tabs.
([Demo](https://www.webcomponents.org/element/PolymerElements/app-layout/demo/patterns/transform-navigation/index.html) - [Source](/patterns/transform-navigation/x-app.html))
- **Expand Card:**
Content cards may expand to take up more horizontal space.
([Demo](https://www.webcomponents.org/element/PolymerElements/app-layout/demo/patterns/expand-card/index.html) - [Source](/patterns/expand-card/index.html))
- **Material Design Responsive Toolbar:**
Toolbar changes its height and padding to adapt mobile screen size.
([Demo](https://www.webcomponents.org/element/PolymerElements/app-layout/demo/patterns/md-responsive-toolbar/index.html) - [Source](/patterns/md-responsive-toolbar/index.html))
## Users
Here are some web apps built with App Layout:
- [Youtube Web](https://www.youtube.com/new)
- [Google I/O 2016](https://events.google.com/io2016/)
- [Polymer project site](https://www.polymer-project.org/summit)
- [Polymer summit](https://www.polymer-project.org/summit)
- [Shop](https://shop.polymer-project.org)
- [News](https://news.polymer-project.org)
- [webcomponents.org](https://www.webcomponents.org/)
- [Chrome Status](https://www.chromestatus.com/)
- [Project Fi](https://fi.google.com/about/)
- [NASA Open Source Software](https://code.nasa.gov/)
See: [Documentation](https://www.webcomponents.org/element/@polymer/app-layout),
[Demo](https://www.webcomponents.org/element/@polymer/app-layout/demo/demo/index.html).
## Usage
### Installation
```
npm install --save @polymer/app-layout
```
### In an html file
```html
My app
```
### In a Polymer 3 element
```js
import {PolymerElement, html} from '@polymer/polymer';
import '@polymer/app-layout/app-layout.js';
class SampleElement extends PolymerElement {
static get template() {
return html`
My app
`;
}
}
customElements.define('sample-element', SampleElement);
```
## Contributing
If you want to send a PR to this element, here are
the instructions for running the tests and demo locally:
### Installation
```sh
git clone https://github.com/PolymerElements/app-layout
cd app-layout
npm install
npm install -g polymer-cli
```
### Running the demo locally
```sh
polymer serve --npm
open http://127.0.0.1:/demo/
```
### Running the tests
```sh
polymer test --npm
```
================================================
FILE: app-box/README.md
================================================
## <app-box>
app-box is a container element that can have scroll effects - visual effects based on
scroll position. For example, the parallax effect can be used to move an image at a slower
rate than the foreground.
```html
```
Notice the `background` attribute in the `img` element; this attribute specifies that that
image is used as the background. By adding the background to the light dom, you can compose
backgrounds that can change dynamically. Alternatively, the mixin `--app-box-background-front-layer`
allows to style the background. For example:
```css
.parallaxAppBox {
--app-box-background-front-layer: {
background-image: url(picture.png);
};
}
```
Finally, app-box can have content inside. For example:
```html
Sub title
```
#### Importing the effects
To use the scroll effects, you must explicitly import them in addition to `app-box`:
```js
import '@polymer/app-layout/app-scroll-effects/app-scroll-effects.js';
```
#### List of effects
* **parallax-background**
A simple parallax effect that vertically translates the backgrounds based on a fraction
of the scroll position. For example:
```css
app-header {
--app-header-background-front-layer: {
background-image: url(...);
};
}
```
```html
App name
```
The fraction determines how far the background moves relative to the scroll position.
This value can be assigned via the `scalar` config value and it is typically a value
between 0 and 1 inclusive. If `scalar=0`, the background doesn't move away from the header.
## Styling
Mixin | Description | Default
----------------|-------------|----------
`--app-box-background-front-layer` | Applies to the front layer of the background | {}
================================================
FILE: app-box/app-box.js
================================================
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
part of the polymer project is also subject to an additional IP rights grant
found at http://polymer.github.io/PATENTS.txt
*/
import '@polymer/polymer/polymer-legacy.js';
import '@polymer/iron-flex-layout/iron-flex-layout.js';
import {IronResizableBehavior} from '@polymer/iron-resizable-behavior/iron-resizable-behavior.js';
import {Polymer} from '@polymer/polymer/lib/legacy/polymer-fn.js';
import {html} from '@polymer/polymer/lib/utils/html-tag.js';
import {AppScrollEffectsBehavior} from '../app-scroll-effects/app-scroll-effects-behavior.js';
/**
app-box is a container element that can have scroll effects - visual effects
based on scroll position. For example, the parallax effect can be used to move
an image at a slower rate than the foreground.
```html
```
Notice the `background` attribute in the `img` element; this attribute specifies
that that image is used as the background. By adding the background to the light
dom, you can compose backgrounds that can change dynamically. Alternatively, the
mixin `--app-box-background-front-layer` allows to style the background. For
example:
```css
.parallaxAppBox {
--app-box-background-front-layer: {
background-image: url(picture.png);
};
}
```
Finally, app-box can have content inside. For example:
```html
Sub title
```
#### Importing the effects
To use the scroll effects, you must explicitly import them in addition to
`app-box`:
```js
import '@polymer/app-layout/app-scroll-effects/app-scroll-effects.js';
```
#### List of effects
* **parallax-background**
A simple parallax effect that vertically translates the backgrounds based on a
fraction of the scroll position. For example:
```css
app-header {
--app-header-background-front-layer: {
background-image: url(...);
};
}
```
```html
App name
```
The fraction determines how far the background moves relative to the scroll
position. This value can be assigned via the `scalar` config value and it is
typically a value between 0 and 1 inclusive. If `scalar=0`, the background
doesn't move away from the header.
## Styling
Mixin | Description | Default
----------------|-------------|----------
`--app-box-background-front-layer` | Applies to the front layer of the background | {}
@element app-box
@demo app-box/demo/document-scroll.html Document Scroll
@demo app-box/demo/scrolling-region.html Scrolling Region
*/
Polymer({
/** @override */
_template: html`
`,
is: 'app-box',
behaviors: [AppScrollEffectsBehavior, IronResizableBehavior],
listeners: {'iron-resize': '_resizeHandler'},
/**
* The current scroll progress.
*
* @type {number}
*/
_progress: 0,
/** @override */
attached: function() {
this.resetLayout();
},
_debounceRaf: function(fn) {
var self = this;
if (this._raf) {
window.cancelAnimationFrame(this._raf);
}
this._raf = window.requestAnimationFrame(function() {
self._raf = null;
fn.call(self);
});
},
/**
* Resets the layout. This method is automatically called when the element is
* attached to the DOM.
*
* @method resetLayout
*/
resetLayout: function() {
this._debounceRaf(function() {
// noop if the box isn't in the rendered tree
if (this.offsetWidth === 0 && this.offsetHeight === 0) {
return;
}
var scrollTop = this._clampedScrollTop;
var savedDisabled = this.disabled;
this.disabled = true;
this._elementTop = this._getElementTop();
this._elementHeight = this.offsetHeight;
this._cachedScrollTargetHeight = this._scrollTargetHeight;
this._setUpEffect();
this._updateScrollState(scrollTop);
this.disabled = savedDisabled;
});
},
_getElementTop: function() {
var currentNode = this;
var top = 0;
while (currentNode && currentNode !== this.scrollTarget) {
top += currentNode.offsetTop;
currentNode = currentNode.offsetParent;
}
return top;
},
_updateScrollState: function(scrollTop) {
if (this.isOnScreen()) {
var viewportTop = this._elementTop - scrollTop;
this._progress = 1 -
(viewportTop + this._elementHeight) / this._cachedScrollTargetHeight;
this._runEffects(this._progress, scrollTop);
}
},
/**
* Returns true if this app-box is on the screen.
* That is, visible in the current viewport.
*
* @method isOnScreen
* @return {boolean}
*/
isOnScreen: function() {
return this._elementTop <
this._scrollTop + this._cachedScrollTargetHeight &&
this._elementTop + this._elementHeight > this._scrollTop;
},
_resizeHandler: function() {
this.resetLayout();
},
_getDOMRef: function(id) {
if (id === 'background') {
return this.$.background;
}
if (id === 'backgroundFrontLayer') {
return this.$.backgroundFrontLayer;
}
},
/**
* Returns an object containing the progress value of the scroll effects.
*
* @method getScrollState
* @return {Object}
*/
getScrollState: function() {
return {progress: this._progress};
}
});
================================================
FILE: app-box/demo/document-scroll.html
================================================
app-box demo using the document scroll
Polymer 1.0 replaces the shadow DOM polyfill with a lightweight shim, uses a new, faster data-binding system, and significantly reduces code size.
For Modern Browsers
Polymer is built from the ground up for modern browsers, using the latest web platform APIs. Polyfills provide support on evergreen browsers for APIs that aren't universal yet.
Using Web Components
Polymer leverages web components, a new set of standards designed to provide reusable components for the web.
Create your own elements
The Polymer library makes it easy to create your own powerful elements. Give your element some markup and properties, and then use it on a site. Polymer provides useful features like templating and data binding to reduce the amount of boilerplate you need to write.
================================================
FILE: app-box/demo/scrolling-region.html
================================================
app-box using a custom scrolling region
Polymer 1.0 replaces the shadow DOM polyfill with a lightweight shim, uses a new, faster data-binding system, and significantly reduces code size.
For Modern Browsers
Polymer is built from the ground up for modern browsers, using the latest web platform APIs. Polyfills provide support on evergreen browsers for APIs that aren't universal yet.
Using Web Components
Polymer leverages web components, a new set of standards designed to provide reusable components for the web.
Create your own elements
The Polymer library makes it easy to create your own powerful elements. Give your element some markup and properties, and then use it on a site. Polymer provides useful features like templating and data binding to reduce the amount of boilerplate you need to write.
================================================
FILE: app-drawer/README.md
================================================
## <app-drawer>

app-drawer is a navigation drawer that can slide in from the left or right.
Example:
Align the drawer at the start, which is left in LTR layouts (default):
```html
```
Align the drawer at the end:
```html
```
To make the contents of the drawer scrollable, create a wrapper for the scroll
content, and apply height and overflow styles to it.
```html
```
### Styling
Custom property | Description | Default
---------------------------------|----------------------------------------|--------------------
`--app-drawer-width` | Width of the drawer | 256px
`--app-drawer-content-container` | Mixin for the drawer content container | {}
`--app-drawer-content-padding` | Padding of the drawer content container| 120px 0
`--app-drawer-scrim-background` | Background for the scrim | rgba(0, 0, 0, 0.5)
**NOTE:** If you use `` with `` and specify a value for
`--app-drawer-width`, that value must be accessible by both elements. This can be done by
defining the value on the `:host` that contains `` (or `html` if outside
a shadow root):
```css
:host {
--app-drawer-width: 300px;
}
```
================================================
FILE: app-drawer/app-drawer.js
================================================
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
part of the polymer project is also subject to an additional IP rights grant
found at http://polymer.github.io/PATENTS.txt
*/
import '@polymer/polymer/polymer-legacy.js';
import '@polymer/iron-flex-layout/iron-flex-layout.js';
import {Polymer} from '@polymer/polymer/lib/legacy/polymer-fn.js';
import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
import {html} from '@polymer/polymer/lib/utils/html-tag.js';
import {afterNextRender} from '@polymer/polymer/lib/utils/render-status.js';
/**
app-drawer is a navigation drawer that can slide in from the left or right.
Example:
Align the drawer at the start, which is left in LTR layouts (default):
```html
```
Align the drawer at the end:
```html
```
To make the contents of the drawer scrollable, create a wrapper for the scroll
content, and apply height and overflow styles to it.
```html
```
### Styling
Custom property | Description | Default
---------------------------------|----------------------------------------|--------------------
`--app-drawer-width` | Width of the drawer | 256px
`--app-drawer-content-container` | Mixin for the drawer content container | {}
`--app-drawer-scrim-background` | Background for the scrim | rgba(0, 0, 0, 0.5)
**NOTE:** If you use `` with `` and specify a
value for
`--app-drawer-width`, that value must be accessible by both elements. This can
be done by defining the value on the `:host` that contains ``
(or `html` if outside a shadow root):
```css
:host {
--app-drawer-width: 300px;
}
```
@element app-drawer
@demo app-drawer/demo/left-drawer.html Simple Left Drawer
@demo app-drawer/demo/right-drawer.html Right Drawer with Icons
*/
Polymer({
/** @override */
_template: html`
`,
is: 'app-drawer',
properties: {
/**
* The opened state of the drawer.
*/
opened:
{type: Boolean, value: false, notify: true, reflectToAttribute: true},
/**
* The drawer does not have a scrim and cannot be swiped close.
*/
persistent: {type: Boolean, value: false, reflectToAttribute: true},
/**
* The transition duration of the drawer in milliseconds.
*/
transitionDuration: {type: Number, value: 200},
/**
* The alignment of the drawer on the screen ('left', 'right', 'start' or
* 'end'). 'start' computes to left and 'end' to right in LTR layout and
* vice versa in RTL layout.
*/
align: {type: String, value: 'left'},
/**
* The computed, read-only position of the drawer on the screen ('left' or
* 'right').
*/
position: {type: String, readOnly: true, reflectToAttribute: true},
/**
* Create an area at the edge of the screen to swipe open the drawer.
*/
swipeOpen: {type: Boolean, value: false, reflectToAttribute: true},
/**
* Trap keyboard focus when the drawer is opened and not persistent.
*/
noFocusTrap: {type: Boolean, value: false},
/**
* Disables swiping on the drawer.
*/
disableSwipe: {type: Boolean, value: false}
},
observers: [
'resetLayout(position, isAttached)',
'_resetPosition(align, isAttached)',
'_styleTransitionDuration(transitionDuration)',
'_openedPersistentChanged(opened, persistent)'
],
_translateOffset: 0,
_trackDetails: null,
_drawerState: 0,
_boundEscKeydownHandler: null,
_firstTabStop: null,
_lastTabStop: null,
/** @override */
attached: function() {
afterNextRender(this, function() {
this._boundEscKeydownHandler = this._escKeydownHandler.bind(this);
this.addEventListener('keydown', this._tabKeydownHandler.bind(this));
// Only listen for horizontal track so you can vertically scroll
// inside the drawer.
this.listen(this, 'track', '_track');
this.setScrollDirection('y');
});
this.fire('app-reset-layout');
},
/** @override */
detached: function() {
document.removeEventListener('keydown', this._boundEscKeydownHandler);
},
/**
* Opens the drawer.
*/
open: function() {
this.opened = true;
},
/**
* Closes the drawer.
*/
close: function() {
this.opened = false;
},
/**
* Toggles the drawer open and close.
*/
toggle: function() {
this.opened = !this.opened;
},
/**
* Gets the width of the drawer.
*
* @return {number} The width of the drawer in pixels.
*/
getWidth: function() {
return this._savedWidth || this.$.contentContainer.offsetWidth;
},
_isRTL: function() {
return window.getComputedStyle(this).direction === 'rtl';
},
_resetPosition: function() {
switch (this.align) {
case 'start':
this._setPosition(this._isRTL() ? 'right' : 'left');
return;
case 'end':
this._setPosition(this._isRTL() ? 'left' : 'right');
return;
}
this._setPosition(this.align);
},
_escKeydownHandler: function(event) {
var ESC_KEYCODE = 27;
if (event.keyCode === ESC_KEYCODE) {
// Prevent any side effects if app-drawer closes.
event.preventDefault();
this.close();
}
},
_track: function(event) {
if (this.persistent || this.disableSwipe) {
return;
}
// Disable user selection on desktop.
event.preventDefault();
switch (event.detail.state) {
case 'start':
this._trackStart(event);
break;
case 'track':
this._trackMove(event);
break;
case 'end':
this._trackEnd(event);
break;
}
},
_trackStart: function(event) {
this._drawerState = this._DRAWER_STATE.TRACKING;
var rect = this.$.contentContainer.getBoundingClientRect();
this._savedWidth = rect.width;
if (this.position === 'left') {
this._translateOffset = rect.left;
} else {
this._translateOffset = rect.right - window.innerWidth;
}
this._trackDetails = [];
// Disable transitions since style attributes will reflect user track
// events.
this._styleTransitionDuration(0);
this.style.visibility = 'visible';
},
_trackMove: function(event) {
this._translateDrawer(event.detail.dx + this._translateOffset);
// Use Date.now() since event.timeStamp is inconsistent across browsers
// (e.g. most browsers use milliseconds but FF 44 uses microseconds).
this._trackDetails.push({dx: event.detail.dx, timeStamp: Date.now()});
},
_trackEnd: function(event) {
var x = event.detail.dx + this._translateOffset;
var drawerWidth = this.getWidth();
var isPositionLeft = this.position === 'left';
var isInEndState = isPositionLeft ? (x >= 0 || x <= -drawerWidth) :
(x <= 0 || x >= drawerWidth);
if (!isInEndState) {
// No longer need the track events after this method returns - allow them
// to be GC'd.
var trackDetails = this._trackDetails;
this._trackDetails = null;
this._flingDrawer(event, trackDetails);
if (this._drawerState === this._DRAWER_STATE.FLINGING) {
return;
}
}
// If the drawer is not flinging, toggle the opened state based on the
// position of the drawer.
var halfWidth = drawerWidth / 2;
if (event.detail.dx < -halfWidth) {
this.opened = this.position === 'right';
} else if (event.detail.dx > halfWidth) {
this.opened = this.position === 'left';
}
if (isInEndState) {
this.debounce('_resetDrawerState', this._resetDrawerState);
} else {
this.debounce(
'_resetDrawerState', this._resetDrawerState, this.transitionDuration);
}
this._styleTransitionDuration(this.transitionDuration);
this._resetDrawerTranslate();
this.style.visibility = '';
},
_calculateVelocity: function(event, trackDetails) {
// Find the oldest track event that is within 100ms using binary search.
var now = Date.now();
var timeLowerBound = now - 100;
var trackDetail;
var min = 0;
var max = trackDetails.length - 1;
while (min <= max) {
// Floor of average of min and max.
var mid = (min + max) >> 1;
var d = trackDetails[mid];
if (d.timeStamp >= timeLowerBound) {
trackDetail = d;
max = mid - 1;
} else {
min = mid + 1;
}
}
if (trackDetail) {
var dx = event.detail.dx - trackDetail.dx;
var dt = (now - trackDetail.timeStamp) || 1;
return dx / dt;
}
return 0;
},
_flingDrawer: function(event, trackDetails) {
var velocity = this._calculateVelocity(event, trackDetails);
// Do not fling if velocity is not above a threshold.
if (Math.abs(velocity) < this._MIN_FLING_THRESHOLD) {
return;
}
this._drawerState = this._DRAWER_STATE.FLINGING;
var x = event.detail.dx + this._translateOffset;
var drawerWidth = this.getWidth();
var isPositionLeft = this.position === 'left';
var isVelocityPositive = velocity > 0;
var isClosingLeft = !isVelocityPositive && isPositionLeft;
var isClosingRight = isVelocityPositive && !isPositionLeft;
var dx;
if (isClosingLeft) {
dx = -(x + drawerWidth);
} else if (isClosingRight) {
dx = (drawerWidth - x);
} else {
dx = -x;
}
// Enforce a minimum transition velocity to make the drawer feel snappy.
if (isVelocityPositive) {
velocity = Math.max(velocity, this._MIN_TRANSITION_VELOCITY);
this.opened = this.position === 'left';
} else {
velocity = Math.min(velocity, -this._MIN_TRANSITION_VELOCITY);
this.opened = this.position === 'right';
}
// Calculate the amount of time needed to finish the transition based on the
// initial slope of the timing function.
var t = this._FLING_INITIAL_SLOPE * dx / velocity;
this._styleTransitionDuration(t);
this._styleTransitionTimingFunction(this._FLING_TIMING_FUNCTION);
this._resetDrawerTranslate();
this.debounce('_resetDrawerState', this._resetDrawerState, t);
},
_styleTransitionDuration: function(duration) {
this.style.transitionDuration = duration + 'ms';
this.$.contentContainer.style.transitionDuration = duration + 'ms';
this.$.scrim.style.transitionDuration = duration + 'ms';
},
_styleTransitionTimingFunction: function(timingFunction) {
this.$.contentContainer.style.transitionTimingFunction = timingFunction;
this.$.scrim.style.transitionTimingFunction = timingFunction;
},
_translateDrawer: function(x) {
var drawerWidth = this.getWidth();
if (this.position === 'left') {
x = Math.max(-drawerWidth, Math.min(x, 0));
this.$.scrim.style.opacity = 1 + x / drawerWidth;
} else {
x = Math.max(0, Math.min(x, drawerWidth));
this.$.scrim.style.opacity = 1 - x / drawerWidth;
}
this.translate3d(x + 'px', '0', '0', this.$.contentContainer);
},
_resetDrawerTranslate: function() {
this.$.scrim.style.opacity = '';
this.transform('', this.$.contentContainer);
},
_resetDrawerState: function() {
var oldState = this._drawerState;
// If the drawer was flinging, we need to reset the style attributes.
if (oldState === this._DRAWER_STATE.FLINGING) {
this._styleTransitionDuration(this.transitionDuration);
this._styleTransitionTimingFunction('');
this.style.visibility = '';
}
this._savedWidth = null;
if (this.opened) {
this._drawerState = this.persistent ?
this._DRAWER_STATE.OPENED_PERSISTENT :
this._DRAWER_STATE.OPENED;
} else {
this._drawerState = this._DRAWER_STATE.CLOSED;
}
if (oldState !== this._drawerState) {
if (this._drawerState === this._DRAWER_STATE.OPENED) {
this._setKeyboardFocusTrap();
document.addEventListener('keydown', this._boundEscKeydownHandler);
document.body.style.overflow = 'hidden';
} else {
document.removeEventListener('keydown', this._boundEscKeydownHandler);
document.body.style.overflow = '';
}
// Don't fire the event on initial load.
if (oldState !== this._DRAWER_STATE.INIT) {
this.fire('app-drawer-transitioned');
}
}
},
/**
* Resets the layout.
*
* @method resetLayout
*/
resetLayout: function() {
this.fire('app-reset-layout');
},
_setKeyboardFocusTrap: function() {
if (this.noFocusTrap) {
return;
}
// NOTE: Unless we use /deep/ (which we shouldn't since it's deprecated),
// this will not select focusable elements inside shadow roots.
var focusableElementsSelector = [
'a[href]:not([tabindex="-1"])',
'area[href]:not([tabindex="-1"])',
'input:not([disabled]):not([tabindex="-1"])',
'select:not([disabled]):not([tabindex="-1"])',
'textarea:not([disabled]):not([tabindex="-1"])',
'button:not([disabled]):not([tabindex="-1"])',
'iframe:not([tabindex="-1"])',
'[tabindex]:not([tabindex="-1"])',
'[contentEditable=true]:not([tabindex="-1"])'
].join(',');
var focusableElements =
dom(this).querySelectorAll(focusableElementsSelector);
if (focusableElements.length > 0) {
this._firstTabStop = focusableElements[0];
this._lastTabStop = focusableElements[focusableElements.length - 1];
} else {
// Reset saved tab stops when there are no focusable elements in the
// drawer.
this._firstTabStop = null;
this._lastTabStop = null;
}
// Focus on app-drawer if it has non-zero tabindex. Otherwise, focus the
// first focusable element in the drawer, if it exists. Use the tabindex
// attribute since the this.tabIndex property in IE/Edge returns 0 (instead
// of -1) when the attribute is not set.
var tabindex = this.getAttribute('tabindex');
if (tabindex && parseInt(tabindex, 10) > -1) {
this.focus();
} else if (this._firstTabStop) {
this._firstTabStop.focus();
}
},
_tabKeydownHandler: function(event) {
if (this.noFocusTrap) {
return;
}
var TAB_KEYCODE = 9;
if (this._drawerState === this._DRAWER_STATE.OPENED &&
event.keyCode === TAB_KEYCODE) {
if (event.shiftKey) {
if (this._firstTabStop &&
dom(event).localTarget === this._firstTabStop) {
event.preventDefault();
this._lastTabStop.focus();
}
} else {
if (this._lastTabStop && dom(event).localTarget === this._lastTabStop) {
event.preventDefault();
this._firstTabStop.focus();
}
}
}
},
_openedPersistentChanged: function(opened, persistent) {
this.toggleClass('visible', opened && !persistent, this.$.scrim);
// Use a debounce timer instead of transitionend since transitionend won't
// fire when app-drawer is display: none.
this.debounce(
'_resetDrawerState', this._resetDrawerState, this.transitionDuration);
},
_MIN_FLING_THRESHOLD: 0.2,
_MIN_TRANSITION_VELOCITY: 1.2,
_FLING_TIMING_FUNCTION: 'cubic-bezier(0.667, 1, 0.667, 1)',
_FLING_INITIAL_SLOPE: 1.5,
_DRAWER_STATE: {
INIT: 0,
OPENED: 1,
OPENED_PERSISTENT: 2,
CLOSED: 3,
TRACKING: 4,
FLINGING: 5
}
/**
* Fired when the layout of app-drawer has changed.
*
* @event app-reset-layout
*/
/**
* Fired when app-drawer has finished transitioning.
*
* @event app-drawer-transitioned
*/
});
================================================
FILE: app-drawer/demo/index.html
================================================
app-drawer demos
================================================
FILE: app-drawer/test/app-drawer.html
================================================
test for app-drawer
Div
Not focusable
================================================
FILE: app-drawer/test/index.html
================================================
app-drawer tests
================================================
FILE: app-drawer-layout/README.md
================================================
## <app-drawer-layout>
app-drawer-layout is a wrapper element that positions an app-drawer and other content. When
the viewport width is smaller than `responsiveWidth`, this element changes to narrow layout.
In narrow layout, the drawer will be stacked on top of the main content. The drawer will slide
in/out to hide/reveal the main content.
By default the drawer is aligned to the start, which is left in LTR layouts:
```html
drawer content
main content
```
Align the drawer at the end:
```html
drawer content
main content
```
With an app-header-layout:
```html
drawer-content
App name
main content
```
Add the `drawer-toggle` attribute to elements inside `app-drawer-layout` that toggle the drawer on click events:
```html
drawer-content
App name
main content
```
**NOTE:** With app-layout 2.0, the `drawer-toggle` element needs to be manually hidden
when app-drawer-layout is not in narrow layout. To add this, add the following CSS rule where
app-drawer-layout is used:
```css
app-drawer-layout:not([narrow]) [drawer-toggle] {
display: none;
}
```
Add the `fullbleed` attribute to app-drawer-layout to make it fit the size of its container:
```html
drawer content
main content
```
### Styling
Custom property | Description | Default
-----------------------------------------|--------------------------------------|---------
`--app-drawer-width` | Width of the drawer | 256px
`--app-drawer-layout-content-transition` | Transition for the content container | none
**NOTE:** If you use with and specify a value for
`--app-drawer-width`, that value must be accessible by both elements. This can be done by
defining the value on the `:host` that contains (or `html` if outside
a shadow root):
```css
:host {
--app-drawer-width: 300px;
}
```
================================================
FILE: app-drawer-layout/app-drawer-layout.js
================================================
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
part of the polymer project is also subject to an additional IP rights grant
found at http://polymer.github.io/PATENTS.txt
*/
import '@polymer/polymer/polymer-legacy.js';
import '@polymer/iron-media-query/iron-media-query.js';
import {Polymer} from '@polymer/polymer/lib/legacy/polymer-fn.js';
import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
import {html} from '@polymer/polymer/lib/utils/html-tag.js';
import {afterNextRender} from '@polymer/polymer/lib/utils/render-status.js';
import {AppLayoutBehavior} from '../app-layout-behavior/app-layout-behavior.js';
/**
app-drawer-layout is a wrapper element that positions an app-drawer and other
content. When the viewport width is smaller than `responsiveWidth`, this element
changes to narrow layout. In narrow layout, the drawer will be stacked on top of
the main content. The drawer will slide in/out to hide/reveal the main content.
By default the drawer is aligned to the start, which is left in LTR layouts:
```html
drawer content
main content
```
Align the drawer at the end:
```html
drawer content
main content
```
With an app-header-layout:
```html
drawer-content
App name
main content
```
Add the `drawer-toggle` attribute to elements inside `app-drawer-layout` that
toggle the drawer on click events:
```html
drawer-content
App name
main content
```
**NOTE:** With app-layout 2.0, the `drawer-toggle` element needs to be manually
hidden when app-drawer-layout is not in narrow layout. To add this, add the
following CSS rule where app-drawer-layout is used:
```css
app-drawer-layout:not([narrow]) [drawer-toggle] {
display: none;
}
```
Add the `fullbleed` attribute to app-drawer-layout to make it fit the size of
its container:
```html
drawer content
main content
```
### Styling
Custom property | Description | Default
-----------------------------------------|--------------------------------------|---------
`--app-drawer-width` | Width of the drawer | 256px
`--app-drawer-layout-content-transition` | Transition for the content container | none
**NOTE:** If you use with and specify a value
for
`--app-drawer-width`, that value must be accessible by both elements. This can
be done by defining the value on the `:host` that contains
(or `html` if outside a shadow root):
```css
:host {
--app-drawer-width: 300px;
}
```
@element app-drawer-layout
@demo app-drawer-layout/demo/index.html
*/
Polymer({
/** @override */
_template: html`
`,
is: 'app-drawer-layout',
behaviors: [AppLayoutBehavior],
properties: {
/**
* If true, ignore `responsiveWidth` setting and force the narrow layout.
*/
forceNarrow: {type: Boolean, value: false},
/**
* If the viewport's width is smaller than this value, the panel will change
* to narrow layout. In the mode the drawer will be closed.
*/
responsiveWidth: {type: String, value: '640px'},
/**
* Returns true if it is in narrow layout. This is useful if you need to
* show/hide elements based on the layout.
*/
narrow:
{type: Boolean, reflectToAttribute: true, readOnly: true, notify: true},
/**
* If true, the drawer will initially be opened when in narrow layout mode.
*/
openedWhenNarrow: {type: Boolean, value: false},
_drawerPosition: {type: String}
},
listeners: {'click': '_clickHandler'},
observers: ['_narrowChanged(narrow)'],
/**
* A reference to the app-drawer element.
*
* @property drawer
*/
get drawer() {
return dom(this.$.drawerSlot).getDistributedNodes()[0];
},
/** @override */
attached: function() {
// Disable drawer transitions until after app-drawer-layout sets the initial
// opened state.
var drawer = this.drawer;
if (drawer) {
drawer.setAttribute('no-transition', '');
}
},
_clickHandler: function(e) {
var target = dom(e).localTarget;
if (target && target.hasAttribute('drawer-toggle')) {
var drawer = this.drawer;
if (drawer && !drawer.persistent) {
drawer.toggle();
}
}
},
_updateLayoutStates: function() {
var drawer = this.drawer;
if (!this.isAttached || !drawer) {
return;
}
this._drawerPosition = this.narrow ? null : drawer.position;
if (this._drawerNeedsReset) {
if (this.narrow) {
drawer.opened = this.openedWhenNarrow;
drawer.persistent = false;
} else {
drawer.opened = drawer.persistent = true;
}
if (drawer.hasAttribute('no-transition')) {
// Enable drawer transitions after app-drawer-layout sets the initial
// opened state.
afterNextRender(this, function() {
drawer.removeAttribute('no-transition');
});
}
this._drawerNeedsReset = false;
}
},
_narrowChanged: function() {
this._drawerNeedsReset = true;
this.resetLayout();
},
_onQueryMatchesChanged: function(event) {
this._setNarrow(event.detail.value);
},
_computeMediaQuery: function(forceNarrow, responsiveWidth) {
return forceNarrow ? '(min-width: 0px)' :
'(max-width: ' + responsiveWidth + ')';
}
});
================================================
FILE: app-drawer-layout/demo/index.html
================================================
app-drawer-layout demo
================================================
FILE: app-drawer-layout/test/app-drawer-layout.html
================================================
test for app-drawer-layoutDrawer
Toggle
Content
================================================
FILE: app-drawer-layout/test/index.html
================================================
app-drawer-layout tests
================================================
FILE: app-grid/README.md
================================================
## <app-grid>
app-grid is a helper class useful for creating responsive, fluid grid layouts using custom properties.
Because custom properties can be defined inside a `@media` rule, you can customize the grid layout
for different responsive breakpoints.
Example:
Import `app-grid-style.html` and include `app-grid-style` in the style of an element's definition.
Then, add the class `app-grid` to a container such as `ul` or `div`:
```html
1
2
3
```
In this example, the grid will take 3 columns per row and only 1 column if the viewport width is
smaller than 640px.
### Expandible items
In many cases, it's useful to expand an item more than 1 column. To achieve this type of layout,
you can specify the number of columns the item should expand to by setting the custom property
`--app-grid-expandible-item-columns`. To indicate which item should expand, apply the mixin
`--app-grid-expandible-item` to a rule with a selector to the item. For example:
```html
```
### Preserving the aspect ratio
When the size of a grid item should preserve the aspect ratio, you can add the `has-aspect-ratio`
attribute to the element with the class `app-grid`. Now, every item element becomes a wrapper around
the item content. For example:
```html
item 1
item 2
item 3
```
### Styling
Custom property | Description | Default
----------------------------------------------|------------------------------------------------------------|------------------
`--app-grid-columns` | The number of columns per row. | 1
`--app-grid-gutter` | The space between two items. | 0px
`--app-grid-item-height` | The height of the items. | auto
`--app-grid-expandible-item-columns` | The number of columns an expandible item should expand to. | 1
### CSS grid layout
[CSS grid layout](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout) is a new layout system for CSS that lets you create complex grids. It's richer than app-grid in many cases, but it's only supported in a [few browsers](http://caniuse.com/#search=css%20grid%20layout).
================================================
FILE: app-grid/app-grid-style.js
================================================
/**
@license
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
part of the polymer project is also subject to an additional IP rights grant
found at http://polymer.github.io/PATENTS.txt
*/
/**
app-grid is a helper class useful for creating responsive, fluid grid layouts
using custom properties. Because custom properties can be defined inside a
`@media` rule, you can customize the grid layout for different responsive
breakpoints.
Example:
Import `app-grid-style.html` and include `app-grid-style` in the style of an
element's definition. Then, add the class `app-grid` to a container such as `ul`
or `div`:
```html
1
2
3
```
In the example above, the grid will take 3 columns per row.
### Expandible items
In many cases, it's useful to expand an item more than 1 column. To achieve this
type of layout, you can specify the number of columns the item should expand to
by setting the custom property
`--app-grid-expandible-item-columns`. To indicate which item should expand,
apply the mixin
`--app-grid-expandible-item` to a rule with a selector to the item. For example:
<template>
<style include="app-grid-style">
:host {
--app-grid-columns: 3;
--app-grid-item-height: 100px;
--app-grid-expandible-item-columns: 3;
}
/* Only the first item should expand *\/
.item:first-child {
@apply --app-grid-expandible-item;
}
</style>
</template>
### Preserving the aspect ratio
When the size of a grid item should preserve the aspect ratio, you can add the
`has-aspect-ratio` attribute to the element with the class `.app-grid`. Now,
every item element becomes a wrapper around the item content. For example:
```html
item 1
item 2
item 3
```
### Styling
Custom property | Description | Default
----------------------------------------------|------------------------------------------------------------|------------------
`--app-grid-columns` | The number of columns per row. | 1
`--app-grid-gutter` | The space between two items. | 0px
`--app-grid-item-height` | The height of the items. | auto
`--app-grid-expandible-item-columns` | The number of columns an expandible item should expand to. | 1
@pseudoElement app-grid
@demo app-grid/demo/index.html
*/
import '@polymer/polymer/polymer-legacy.js';
const $_documentContainer = document.createElement('template');
$_documentContainer.setAttribute('style', 'display: none;');
$_documentContainer.innerHTML = ``;
document.head.appendChild($_documentContainer.content);
================================================
FILE: app-grid/demo/aspect-ratio.html
================================================
Preserving aspect ratio using app-grid-style
Aspect ratio: items have an aspect ratio of 1:1 on large screens and 2:1 on small ones.
================================================
FILE: app-grid/demo/md-grid-layout.html
================================================
Material design grid layout using app-grid
Material responsive grid
Nature [[item]]
================================================
FILE: app-grid/demo/simple-responsive-grid.html
================================================
Responsive grid layout using app-grid-style
3 columns on large screens and 2 columns on small ones.
================================================
FILE: app-grid/test/app-grid-1.html
================================================
test for app-grid
[[item]]
================================================
FILE: app-grid/test/app-grid-2.html
================================================
test for app-grid
[[item]]
================================================
FILE: app-grid/test/app-grid-3.html
================================================
test for app-grid
[[item]]
================================================
FILE: app-grid/test/index.html
================================================
app-grid tests
================================================
FILE: app-header/README.md
================================================
## <app-header>

app-header is container element for app-toolbars at the top of the screen that can have scroll
effects. By default, an app-header moves away from the viewport when scrolling down and
if using `reveals`, the header slides back when scrolling back up. For example:
```html
App name
```
app-header can also condense when scrolling down. To achieve this behavior, the header
must have a larger height than the `sticky` element in the light DOM. For example:
```html
App name
```
In this case the header is initially `96px` tall, and it shrinks to `64px` when scrolling down.
That is what is meant by "condensing".
### Sticky element
The element that is positioned fixed to top of the header's `scrollTarget` when a threshold
is reached, similar to `position: sticky` in CSS. This element **must** be an immediate
child of app-header. By default, the `sticky` element is the first `app-toolbar that
is an immediate child of app-header.
```html
Sticky element
```
#### Customizing the sticky element
```html
Sticky element
```
### Scroll target
The app-header's `scrollTarget` property allows to customize the scrollable element to which
the header responds when the user scrolls. By default, app-header uses the document as
the scroll target, but you can customize this property by setting the id of the element, e.g.
```html
```
In this case, the `scrollTarget` property points to the outer div element. Alternatively,
you can set this property programmatically:
```js
appHeader.scrollTarget = document.querySelector("#scrollingRegion");
```
## Backgrounds
app-header has two background layers that can be used for styling when the header is condensed
or when the scrollable element is scrolled to the top.
## Scroll effects
Scroll effects are _optional_ visual effects applied in app-header based on scroll position. For example,
The [Material Design scrolling techniques](https://www.google.com/design/spec/patterns/scrolling-techniques.html)
recommends effects that can be installed via the `effects` property. e.g.
```html
App name
```
#### Importing the effects
To use the scroll effects, you must explicitly import them in addition to `app-header`:
```js
import '@polymer/app-layout/app-scroll-effects/app-scroll-effects.js';
```
#### List of effects
* **blend-background**
Fades in/out two background elements by applying CSS opacity based on scroll position.
You can use this effect to smoothly change the background color or image of the header.
For example, using the mixin `--app-header-background-rear-layer` lets you assign a different
background when the header is condensed:
```css
app-header {
background-color: red;
--app-header-background-rear-layer: {
/* The header is blue when condensed */
background-color: blue;
};
}
```
* **fade-background**
Upon scrolling past a threshold, this effect will trigger an opacity transition to
fade in/out the backgrounds. Compared to the `blend-background` effect,
this effect doesn't interpolate the opacity based on scroll position.
* **parallax-background**
A simple parallax effect that vertically translates the backgrounds based on a fraction
of the scroll position. For example:
```css
app-header {
--app-header-background-front-layer: {
background-image: url(...);
};
}
```
```html
App name
```
The fraction determines how far the background moves relative to the scroll position.
This value can be assigned via the `scalar` config value and it is typically a value
between 0 and 1 inclusive. If `scalar=0`, the background doesn't move away from the header.
* **resize-title**
Progressively interpolates the size of the title from the element with the `main-title` attribute
to the element with the `condensed-title` attribute as the header condenses. For example:
```html
App name
App name
```
* **resize-snapped-title**
Upon scrolling past a threshold, this effect fades in/out the titles using opacity transitions.
Similarly to `resize-title`, the `main-title` and `condensed-title` elements must be placed in the
light DOM.
* **waterfall**
Toggles the shadow property in app-header to create a sense of depth (as recommended in the
MD spec) between the header and the underneath content. You can change the shadow by
customizing the `--app-header-shadow` mixin. For example:
```css
app-header {
--app-header-shadow: {
box-shadow: inset 0px 5px 2px -3px rgba(0, 0, 0, 0.2);
};
}
```
```html
App name
```
* **material**
Installs the waterfall, resize-title, blend-background and parallax-background effects.
### Content attributes
Attribute | Description | Default
----------|---------------------|----------------------------------------
`sticky` | Element that remains at the top when the header condenses. | The first app-toolbar in the light DOM.
## Styling
Mixin | Description | Default
------|-------------|----------
`--app-header-background-front-layer` | Applies to the front layer of the background. | {}
`--app-header-background-rear-layer` | Applies to the rear layer of the background. | {}
`--app-header-shadow` | Applies to the shadow. | {}
================================================
FILE: app-header/app-header.js
================================================
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
part of the polymer project is also subject to an additional IP rights grant
found at http://polymer.github.io/PATENTS.txt
*/
import '@polymer/polymer/polymer-legacy.js';
import '@polymer/iron-flex-layout/iron-flex-layout.js';
import {Polymer} from '@polymer/polymer/lib/legacy/polymer-fn.js';
import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
import {html} from '@polymer/polymer/lib/utils/html-tag.js';
import {AppLayoutBehavior} from '../app-layout-behavior/app-layout-behavior.js';
import {AppScrollEffectsBehavior} from '../app-scroll-effects/app-scroll-effects-behavior.js';
/**
app-header is container element for app-toolbars at the top of the screen that
can have scroll effects. By default, an app-header moves away from the viewport
when scrolling down and if using `reveals`, the header slides back when
scrolling back up. For example:
```html
App name
```
app-header can also condense when scrolling down. To achieve this behavior, the
header must have a larger height than the `sticky` element in the light DOM. For
example:
```html
App name
```
In this case the header is initially `96px` tall, and it shrinks to `64px` when
scrolling down. That is what is meant by "condensing".
### Sticky element
The element that is positioned fixed to top of the header's `scrollTarget` when
a threshold is reached, similar to `position: sticky` in CSS. This element
**must** be an immediate child of app-header. By default, the `sticky` element
is the first `app-toolbar that is an immediate child of app-header.
```html
Sticky element
```
#### Customizing the sticky element
```html
Sticky element
```
### Scroll target
The app-header's `scrollTarget` property allows to customize the scrollable
element to which the header responds when the user scrolls. By default,
app-header uses the document as the scroll target, but you can customize this
property by setting the id of the element, e.g.
```html
```
In this case, the `scrollTarget` property points to the outer div element.
Alternatively, you can set this property programmatically:
```js
appHeader.scrollTarget = document.querySelector("#scrollingRegion");
```
## Backgrounds
app-header has two background layers that can be used for styling when the
header is condensed or when the scrollable element is scrolled to the top.
## Scroll effects
Scroll effects are _optional_ visual effects applied in app-header based on
scroll position. For example, The [Material Design scrolling
techniques](https://www.google.com/design/spec/patterns/scrolling-techniques.html)
recommends effects that can be installed via the `effects` property. e.g.
```html
App name
```
#### Importing the effects
To use the scroll effects, you must explicitly import them in addition to
`app-header`:
```js
import '@polymer/app-layout/app-scroll-effects/app-scroll-effects.js';
```
#### List of effects
* **blend-background**
Fades in/out two background elements by applying CSS opacity based on scroll
position. You can use this effect to smoothly change the background color or
image of the header. For example, using the mixin
`--app-header-background-rear-layer` lets you assign a different background when
the header is condensed:
```css
app-header {
background-color: red;
--app-header-background-rear-layer: {
/* The header is blue when condensed *\/
background-color: blue;
};
}
```
* **fade-background**
Upon scrolling past a threshold, this effect will trigger an opacity transition
to fade in/out the backgrounds. Compared to the `blend-background` effect, this
effect doesn't interpolate the opacity based on scroll position.
* **parallax-background**
A simple parallax effect that vertically translates the backgrounds based on a
fraction of the scroll position. For example:
```css
app-header {
--app-header-background-front-layer: {
background-image: url(...);
};
}
```
```html
App name
```
The fraction determines how far the background moves relative to the scroll
position. This value can be assigned via the `scalar` config value and it is
typically a value between 0 and 1 inclusive. If `scalar=0`, the background
doesn't move away from the header.
* **resize-title**
Progressively interpolates the size of the title from the element with the
`main-title` attribute to the element with the `condensed-title` attribute as
the header condenses. For example:
```html
App name
App name
```
* **resize-snapped-title**
Upon scrolling past a threshold, this effect fades in/out the titles using
opacity transitions. Similarly to `resize-title`, the `main-title` and
`condensed-title` elements must be placed in the light DOM.
* **waterfall**
Toggles the shadow property in app-header to create a sense of depth (as
recommended in the MD spec) between the header and the underneath content. You
can change the shadow by customizing the `--app-header-shadow` mixin. For
example:
```css
app-header {
--app-header-shadow: {
box-shadow: inset 0px 5px 2px -3px rgba(0, 0, 0, 0.2);
};
}
```
```html
App name
```
* **material**
Installs the waterfall, resize-title, blend-background and parallax-background
effects.
### Content attributes
Attribute | Description | Default
----------|---------------------|----------------------------------------
`sticky` | Element that remains at the top when the header condenses. | The first app-toolbar in the light DOM.
## Styling
Mixin | Description | Default
------|-------------|----------
`--app-header-background-front-layer` | Applies to the front layer of the background. | {}
`--app-header-background-rear-layer` | Applies to the rear layer of the background. | {}
`--app-header-shadow` | Applies to the shadow. | {}
@element app-header
@demo app-header/demo/blend-background-1.html Blend Background Image
@demo app-header/demo/blend-background-2.html Blend 2 Background Images
@demo app-header/demo/blend-background-3.html Blend Background Colors
@demo app-header/demo/contacts.html Contacts Demo
@demo app-header/demo/give.html Resize Snapped Title Demo
@demo app-header/demo/music.html Reveals Demo
@demo app-header/demo/no-effects.html Condenses and Reveals Demo
@demo app-header/demo/notes.html Fixed with Dynamic Shadow Demo
@demo app-header/demo/custom-sticky-element-1.html Custom Sticky Element Demo 1
@demo app-header/demo/custom-sticky-element-2.html Custom Sticky Element Demo 2
*/
Polymer({
/** @override */
_template: html`
`,
is: 'app-header',
behaviors: [AppScrollEffectsBehavior, AppLayoutBehavior],
properties: {
/**
* If true, the header will automatically collapse when scrolling down.
* That is, the `sticky` element remains visible when the header is fully
*condensed whereas the rest of the elements will collapse below `sticky`
*element.
*
* By default, the `sticky` element is the first toolbar in the light DOM:
*
*```html
*
* This toolbar remains on top
*
*
*
* ```
*
* Additionally, you can specify which toolbar or element remains visible in
*condensed mode by adding the `sticky` attribute to that element. For
*example: if we want the last toolbar to remain visible, we can add the
*`sticky` attribute to it.
*
*```html
*
*
*
* This toolbar remains on top
*
* ```
*
* Note the `sticky` element must be a direct child of `app-header`.
*/
condenses: {type: Boolean, value: false},
/**
* Mantains the header fixed at the top so it never moves away.
*/
fixed: {type: Boolean, value: false},
/**
* Slides back the header when scrolling back up.
*/
reveals: {type: Boolean, value: false},
/**
* Displays a shadow below the header.
*/
shadow: {type: Boolean, reflectToAttribute: true, value: false}
},
observers: ['_configChanged(isAttached, condenses, fixed)'],
/**
* A cached offsetHeight of the current element.
*
* @type {number}
*/
_height: 0,
/**
* The distance in pixels the header will be translated to when scrolling.
*
* @type {number}
*/
_dHeight: 0,
/**
* The offsetTop of `_stickyEl`
*
* @type {number}
*/
_stickyElTop: 0,
/**
* A reference to the element that remains visible when the header condenses.
*
* @type {HTMLElement}
*/
_stickyElRef: null,
/**
* The header's top value used for the `transformY`
*
* @type {number}
*/
_top: 0,
/**
* The current scroll progress.
*
* @type {number}
*/
_progress: 0,
_wasScrollingDown: false,
_initScrollTop: 0,
_initTimestamp: 0,
_lastTimestamp: 0,
_lastScrollTop: 0,
/**
* The distance the header is allowed to move away.
*
* @type {number}
*/
get _maxHeaderTop() {
return this.fixed ? this._dHeight : this._height + 5;
},
/**
* Returns a reference to the sticky element.
*
* @return {HTMLElement}?
*/
get _stickyEl() {
if (this._stickyElRef) {
return this._stickyElRef;
}
var nodes = dom(this.$.slot).getDistributedNodes();
// Get the element with the sticky attribute on it or the first element in
// the light DOM.
for (var i = 0, node; node = /** @type {!HTMLElement} */ (nodes[i]); i++) {
if (node.nodeType === Node.ELEMENT_NODE) {
if (node.hasAttribute('sticky')) {
this._stickyElRef = node;
break;
} else if (!this._stickyElRef) {
this._stickyElRef = node;
}
}
}
return this._stickyElRef;
},
_configChanged: function() {
this.resetLayout();
this._notifyLayoutChanged();
},
_updateLayoutStates: function() {
if (this.offsetWidth === 0 && this.offsetHeight === 0) {
return;
}
var scrollTop = this._clampedScrollTop;
var firstSetup = this._height === 0 || scrollTop === 0;
var currentDisabled = this.disabled;
this._height = this.offsetHeight;
this._stickyElRef = null;
this.disabled = true;
// prepare for measurement
if (!firstSetup) {
this._updateScrollState(0, true);
}
if (this._mayMove()) {
this._dHeight =
this._stickyEl ? this._height - this._stickyEl.offsetHeight : 0;
} else {
this._dHeight = 0;
}
this._stickyElTop = this._stickyEl ? this._stickyEl.offsetTop : 0;
this._setUpEffect();
if (firstSetup) {
this._updateScrollState(scrollTop, true);
} else {
this._updateScrollState(this._lastScrollTop, true);
this._layoutIfDirty();
}
// restore no transition
this.disabled = currentDisabled;
},
/**
* Updates the scroll state.
*
* @param {number} scrollTop
* @param {boolean=} forceUpdate (default: false)
*/
_updateScrollState: function(scrollTop, forceUpdate) {
if (this._height === 0) {
return;
}
var progress = 0;
var top = 0;
var lastTop = this._top;
var lastScrollTop = this._lastScrollTop;
var maxHeaderTop = this._maxHeaderTop;
var dScrollTop = scrollTop - this._lastScrollTop;
var absDScrollTop = Math.abs(dScrollTop);
var isScrollingDown = scrollTop > this._lastScrollTop;
var now = performance.now();
if (this._mayMove()) {
top = this._clamp(
this.reveals ? lastTop + dScrollTop : scrollTop, 0, maxHeaderTop);
}
if (scrollTop >= this._dHeight) {
top = this.condenses && !this.fixed ? Math.max(this._dHeight, top) : top;
this.style.transitionDuration = '0ms';
}
if (this.reveals && !this.disabled && absDScrollTop < 100) {
// set the initial scroll position
if (now - this._initTimestamp > 300 ||
this._wasScrollingDown !== isScrollingDown) {
this._initScrollTop = scrollTop;
this._initTimestamp = now;
}
if (scrollTop >= maxHeaderTop) {
// check if the header is allowed to snap
if (Math.abs(this._initScrollTop - scrollTop) > 30 ||
absDScrollTop > 10) {
if (isScrollingDown && scrollTop >= maxHeaderTop) {
top = maxHeaderTop;
} else if (!isScrollingDown && scrollTop >= this._dHeight) {
top = this.condenses && !this.fixed ? this._dHeight : 0;
}
var scrollVelocity = dScrollTop / (now - this._lastTimestamp);
this.style.transitionDuration =
this._clamp((top - lastTop) / scrollVelocity, 0, 300) + 'ms';
} else {
top = this._top;
}
}
}
if (this._dHeight === 0) {
progress = scrollTop > 0 ? 1 : 0;
} else {
progress = top / this._dHeight;
}
if (!forceUpdate) {
this._lastScrollTop = scrollTop;
this._top = top;
this._wasScrollingDown = isScrollingDown;
this._lastTimestamp = now;
}
if (forceUpdate || progress !== this._progress || lastTop !== top ||
scrollTop === 0) {
this._progress = progress;
this._runEffects(progress, top);
this._transformHeader(top);
}
},
/**
* Returns true if the current header is allowed to move as the user scrolls.
*
* @return {boolean}
*/
_mayMove: function() {
return this.condenses || !this.fixed;
},
/**
* Returns true if the current header will condense based on the size of the
* header and the `consenses` property.
*
* @return {boolean}
*/
willCondense: function() {
return this._dHeight > 0 && this.condenses;
},
/**
* Returns true if the current element is on the screen.
* That is, visible in the current viewport.
*
* @method isOnScreen
* @return {boolean}
*/
isOnScreen: function() {
return this._height !== 0 && this._top < this._height;
},
/**
* Returns true if there's content below the current element.
*
* @method isContentBelow
* @return {boolean}
*/
isContentBelow: function() {
return this._top === 0 ? this._clampedScrollTop > 0 :
this._clampedScrollTop - this._maxHeaderTop >= 0;
},
/**
* Transforms the header.
*
* @param {number} y
*/
_transformHeader: function(y) {
this.translate3d(0, (-y) + 'px', 0);
if (this._stickyEl) {
this.translate3d(
0,
this.condenses && y >= this._stickyElTop ?
(Math.min(y, this._dHeight) - this._stickyElTop) + 'px' :
0,
0,
this._stickyEl);
}
},
_clamp: function(v, min, max) {
return Math.min(max, Math.max(min, v));
},
_ensureBgContainers: function() {
if (!this._bgContainer) {
this._bgContainer = document.createElement('div');
this._bgContainer.id = 'background';
this._bgRear = document.createElement('div');
this._bgRear.id = 'backgroundRearLayer';
this._bgContainer.appendChild(this._bgRear);
this._bgFront = document.createElement('div');
this._bgFront.id = 'backgroundFrontLayer';
this._bgContainer.appendChild(this._bgFront);
dom(this.root).insertBefore(this._bgContainer, this.$.contentContainer);
}
},
_getDOMRef: function(id) {
switch (id) {
case 'backgroundFrontLayer':
this._ensureBgContainers();
return this._bgFront;
case 'backgroundRearLayer':
this._ensureBgContainers();
return this._bgRear;
case 'background':
this._ensureBgContainers();
return this._bgContainer;
case 'mainTitle':
return dom(this).querySelector('[main-title]');
case 'condensedTitle':
return dom(this).querySelector('[condensed-title]');
}
return null;
},
/**
* Returns an object containing the progress value of the scroll effects
* and the top position of the header.
*
* @method getScrollState
* @return {Object}
*/
getScrollState: function() {
return {progress: this._progress, top: this._top};
}
});
================================================
FILE: app-header/demo/blend-background-1.html
================================================
app-header demo