paper-toolbar.html 10.7 KB
<!--
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
-->

<link rel="import" href="../polymer/polymer.html">
<link rel="import" href="../paper-styles/default-theme.html">
<link rel="import" href="../paper-styles/typography.html">
<link rel="import" href="../iron-flex-layout/iron-flex-layout.html">

<!--
Material design: [Toolbars](https://www.google.com/design/spec/components/toolbars.html)

`paper-toolbar` is a horizontal bar containing items that can be used for
label, navigation, search and actions.  The items placed inside the
`paper-toolbar` are projected into a `class="horizontal center layout"` container inside of
`paper-toolbar`'s Shadow DOM.  You can use flex attributes to control the items'
sizing.

Example:

```html
<paper-toolbar>
  <paper-icon-button icon="menu" on-tap="menuAction"></paper-icon-button>
  <div class="title">Title</div>
  <paper-icon-button icon="more-vert" on-tap="moreAction"></paper-icon-button>
</paper-toolbar>
```

`paper-toolbar` has a standard height, but can made be taller by setting `tall`
class on the `paper-toolbar`.  This will make the toolbar 3x the normal height.

```html
<paper-toolbar class="tall">
  <paper-icon-button icon="menu"></paper-icon-button>
</paper-toolbar>
```

Apply `medium-tall` class to make the toolbar medium tall.  This will make the
toolbar 2x the normal height.

```html
<paper-toolbar class="medium-tall">
  <paper-icon-button icon="menu"></paper-icon-button>
</paper-toolbar>
```

When `tall`, items can pin to either the top (default), middle or bottom.  Use
`middle` class for middle content and `bottom` class for bottom content.

```html
<paper-toolbar class="tall">
  <paper-icon-button icon="menu"></paper-icon-button>
  <div class="middle title">Middle Title</div>
  <div class="bottom title">Bottom Title</div>
</paper-toolbar>
```

For `medium-tall` toolbar, the middle and bottom contents overlap and are
pinned to the bottom.  But `middleJustify` and `bottomJustify` attributes are
still honored separately.

To make an element completely fit at the bottom of the toolbar, use `fit` along
with `bottom`.

```html
<paper-toolbar class="tall">
  <div id="progressBar" class="bottom fit"></div>
</paper-toolbar>
```

### Styling

The following custom properties and mixins are available for styling:

Custom property | Description | Default
----------------|-------------|----------
`--paper-toolbar-title`      | Mixin applied to the title of the toolbar | `{}`
`--paper-toolbar-background` | Toolbar background color     | `--default-primary-color`
`--paper-toolbar-color`      | Toolbar foreground color     | `--text-primary-color`
`--paper-toolbar-height`     | Custom height for toolbar    | `64px`
`--paper-toolbar-sm-height`  | Custom height for small screen toolbar | `56px`
`--paper-toolbar`            | Mixin applied to the toolbar | `{}`
`--paper-toolbar-content`    | Mixin applied to the content section of the toolbar | `{}`
`--paper-toolbar-medium`     | Mixin applied to medium height toolbar | `{}`
`--paper-toolbar-tall`       | Mixin applied to tall height toolbar | `{}`

### Accessibility

`<paper-toolbar>` has `role="toolbar"` by default. Any elements with the class `title` will
be used as the label of the toolbar via `aria-labelledby`.

@demo demo/index.html
-->

<dom-module id="paper-toolbar">
  <template>
    <style>
      :host {
        /* technical */
        display: block;
        position: relative;
        box-sizing: border-box;
        -moz-box-sizing: border-box;

        /* size */
        height: var(--paper-toolbar-height, 64px);

        background: var(--paper-toolbar-background, --default-primary-color);
        color: var(--paper-toolbar-color, --text-primary-color);

        @apply(--paper-toolbar);
      }

      :host(.animate) {
        /* transition */
        transition: height 0.18s ease-in;
      }

      :host(.medium-tall) {
        height: calc(var(--paper-toolbar-height, 64px) * 2);
        @apply(--paper-toolbar-medium);
      }

      :host(.tall) {
        height: calc(var(--paper-toolbar-height, 64px) * 3);
        @apply(--paper-toolbar-tall);
      }

      .toolbar-tools {
        position: relative;
        height: var(--paper-toolbar-height, 64px);
        padding: 0 16px;
        pointer-events: none;
        @apply(--layout-horizontal);
        @apply(--layout-center);
        @apply(--paper-toolbar-content);
      }

      /*
       * TODO: Where should media query breakpoints live so they can be shared between elements?
       */

      @media (max-width: 600px) {
        :host {
          height: var(--paper-toolbar-sm-height, 56px);
        }

        :host(.medium-tall) {
          height: calc(var(--paper-toolbar-sm-height, 56px) * 2);
        }

        :host(.tall) {
          height: calc(var(--paper-toolbar-sm-height, 56px) * 3);
        }

        .toolbar-tools {
          height: var(--paper-toolbar-sm-height, 56px);
        }
      }

      #topBar {
        position: relative;
      }

      /* middle bar */
      #middleBar {
        position: absolute;
        top: 0;
        right: 0;
        left: 0;
      }

      :host(.tall) #middleBar,
      :host(.medium-tall) #middleBar {
        -webkit-transform: translateY(100%);
        transform: translateY(100%);
      }

      /* bottom bar */
      #bottomBar {
        position: absolute;
        right: 0;
        bottom: 0;
        left: 0;
      }

      /*
       * make elements (e.g. buttons) respond to mouse/touch events
       *
       * `.toolbar-tools` disables touch events so multiple toolbars can stack and not
       * absorb events. All children must have pointer events re-enabled to work as
       * expected.
       */
      .toolbar-tools > ::content > *:not([disabled]) {
        pointer-events: auto;
      }

      .toolbar-tools > ::content .title {
        @apply(--paper-font-common-base);

        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        font-size: 20px;
        font-weight: 400;
        line-height: 1;
        pointer-events: none;

        @apply(--layout-flex);
        @apply(--paper-toolbar-title);
      }

      /**
       * TODO: Refactor these selectors
       * Work in progress.
       */
      .toolbar-tools > ::content paper-icon-button[icon=menu] {
        margin-right: 24px;
      }

      .toolbar-tools > ::content > .title,
      .toolbar-tools > ::content[select=".middle"] > .title,
      .toolbar-tools > ::content[select=".bottom"] > .title {
        margin-left: 56px;
      }

      .toolbar-tools > ::content > paper-icon-button + .title,
      .toolbar-tools > ::content[select=".middle"] paper-icon-button + .title,
      .toolbar-tools > ::content[select=".bottom"] paper-icon-button + .title {
        margin-left: 0;
      }

      .toolbar-tools > ::content > .fit {
        position: absolute;
        top: auto;
        right: 0;
        bottom: 0;
        left: 0;
        width: auto;
        margin: 0;
      }

      /* TODO(noms): Until we have a better solution for classes that don't use
       * /deep/ create our own.
       */
      .start-justified {
        @apply(--layout-start-justified);
      }

      .center-justified {
        @apply(--layout-center-justified);
      }

      .end-justified {
        @apply(--layout-end-justified);
      }

      .around-justified {
        @apply(--layout-around-justified);
      }

      .justified {
        @apply(--layout-justified);
      }
    </style>

    <div id="topBar" class$="toolbar-tools [[_computeBarExtraClasses(justify)]]">
      <content select=":not(.middle):not(.bottom)"></content>
    </div>

    <div id="middleBar" class$="toolbar-tools [[_computeBarExtraClasses(middleJustify)]]">
      <content select=".middle"></content>
    </div>

    <div id="bottomBar" class$="toolbar-tools [[_computeBarExtraClasses(bottomJustify)]]">
      <content select=".bottom"></content>
    </div>
  </template>

  <script>
    Polymer({
      is: 'paper-toolbar',

      hostAttributes: {
        'role': 'toolbar'
      },

      properties: {
        /**
         * Controls how the items are aligned horizontally when they are placed
         * at the bottom.
         * Options are `start`, `center`, `end`, `justified` and `around`.
         */
        bottomJustify: {
          type: String,
          value: ''
        },

        /**
         * Controls how the items are aligned horizontally.
         * Options are `start`, `center`, `end`, `justified` and `around`.
         */
        justify: {
          type: String,
          value: ''
        },

        /**
         * Controls how the items are aligned horizontally when they are placed
         * in the middle.
         * Options are `start`, `center`, `end`, `justified` and `around`.
         */
        middleJustify: {
          type: String,
          value: ''
        }

      },

      attached: function() {
        this._observer = this._observe(this);
        this._updateAriaLabelledBy();
      },

      detached: function() {
        if (this._observer) {
          this._observer.disconnect();
        }
      },

      _observe: function(node) {
        var observer = new MutationObserver(function() {
          this._updateAriaLabelledBy();
        }.bind(this));
        observer.observe(node, {
          childList: true,
          subtree: true
        });
        return observer;
      },

      _updateAriaLabelledBy: function() {
        var labelledBy = [];
        var contents = Polymer.dom(this.root).querySelectorAll('content');
        for (var content, index = 0; content = contents[index]; index++) {
          var nodes = Polymer.dom(content).getDistributedNodes();
          for (var node, jndex = 0; node = nodes[jndex]; jndex++) {
            if (node.classList && node.classList.contains('title')) {
              if (node.id) {
                labelledBy.push(node.id);
              } else {
                var id = 'paper-toolbar-label-' + Math.floor(Math.random() * 10000);
                node.id = id;
                labelledBy.push(id);
              }
            }
          }
        }
        if (labelledBy.length > 0) {
          this.setAttribute('aria-labelledby', labelledBy.join(' '));
        }
      },

      _computeBarExtraClasses: function(barJustify) {
        if (!barJustify) return '';

        return barJustify + (barJustify === 'justified' ? '' : '-justified');
      }
    });
  </script>
</dom-module>