paper-toggle-button.html 7.18 KB
<!--
@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
-->

<link rel="import" href="../polymer/polymer.html">
<link rel="import" href="../paper-styles/color.html">
<link rel="import" href="../paper-styles/default-theme.html">
<link rel="import" href="../paper-ripple/paper-ripple.html">
<link rel="import" href="../paper-behaviors/paper-inky-focus-behavior.html">
<link rel="import" href="../iron-checked-element-behavior/iron-checked-element-behavior.html">

<!--
`paper-toggle-button` provides a ON/OFF switch that user can toggle the state
by tapping or by dragging the switch.

Example:

    <paper-toggle-button></paper-toggle-button>

### Styling

The following custom properties and mixins are available for styling:

Custom property | Description | Default
----------------|-------------|----------
`--paper-toggle-button-unchecked-bar-color` | Slider color when the input is not checked | `#000000`
`--paper-toggle-button-unchecked-button-color` | Button color when the input is not checked | `--paper-grey-50`
`--paper-toggle-button-unchecked-ink-color` | Selected/focus ripple color when the input is not checked | `--dark-primary-color`
`--paper-toggle-button-checked-bar-color` | Slider button color when the input is checked | `--default-primary-color`
`--paper-toggle-button-checked-button-color` | Button color when the input is checked | `--default-primary-color`
`--paper-toggle-button-checked-ink-color` | Selected/focus ripple color when the input is checked | `--default-primary-color`
`--paper-toggle-button-unchecked-bar` | Mixin applied to the slider when the input is not checked | `{}`
`--paper-toggle-button-unchecked-button` | Mixin applied to the slider button when the input is not checked | `{}`
`--paper-toggle-button-checked-bar` | Mixin applied to the slider when the input is checked | `{}`
`--paper-toggle-button-checked-button` | Mixin applied to the slider button when the input is checked | `{}`

@group Paper Elements
@element paper-toggle-button
@hero hero.svg
@demo demo/index.html
-->

<dom-module id="paper-toggle-button">
  <template>

    <style>
      :host {
        display: inline-block;
      }

      :host([disabled]) {
        pointer-events: none;
      }

      :host(:focus) {
        outline:none;
      }

      .toggle-bar {
        position: absolute;
        height: 100%;
        width: 100%;
        border-radius: 8px;
        pointer-events: none;
        opacity: 0.4;
        transition: background-color linear .08s;
        background-color: var(--paper-toggle-button-unchecked-bar-color, #000000);
        @apply(--paper-toggle-button-unchecked-bar);
      }

      .toggle-button {
        position: absolute;
        top: -3px;
        height: 20px;
        width: 20px;
        border-radius: 50%;
        box-shadow: 0 1px 5px 0 rgba(0, 0, 0, 0.6);
        transition: -webkit-transform linear .08s, background-color linear .08s;
        transition: transform linear .08s, background-color linear .08s;
        will-change: transform;
        background-color: var(--paper-toggle-button-unchecked-button-color, --paper-grey-50);
        @apply(--paper-toggle-button-unchecked-button);
      }

      .toggle-button.dragging {
        -webkit-transition: none;
        transition: none;
      }

      :host([checked]):not([disabled]) .toggle-bar {
        opacity: 0.5;
        background-color: var(--paper-toggle-button-checked-bar-color, --default-primary-color);
        @apply(--paper-toggle-button-checked-bar);
      }

      :host([disabled]) .toggle-bar {
        background-color: #000;
        opacity: 0.12;
      }

      :host([checked]) .toggle-button {
        -webkit-transform: translate(16px, 0);
        transform: translate(16px, 0);
      }

      :host([checked]):not([disabled]) .toggle-button {
        background-color: var(--paper-toggle-button-checked-button-color, --default-primary-color);
        @apply(--paper-toggle-button-checked-button);
      }

      :host([disabled]) .toggle-button {
        background-color: #bdbdbd;
        opacity: 1;
      }

      .toggle-ink {
        position: absolute;
        top: -14px;
        left: -14px;
        width: 48px;
        height: 48px;
        opacity: 0.5;
        pointer-events: none;
        color: var(--paper-toggle-button-unchecked-ink-color, --primary-text-color);
      }

      :host([checked]) .toggle-ink {
        color: var(--paper-toggle-button-checked-ink-color, --default-primary-color);
      }

      .toggle-container {
        position: relative;
        width: 36px;
        height: 14px;
      }
    </style>

    <div class="toggle-container">
      <div id="toggleBar" class="toggle-bar"></div>
      <div id="toggleButton" class="toggle-button">
        <paper-ripple id="ink" class="toggle-ink circle" recenters></paper-ripple>
      </div>
    </div>

  </template>

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

      behaviors: [
        Polymer.PaperInkyFocusBehavior,
        Polymer.IronCheckedElementBehavior
      ],

      hostAttributes: {
        role: 'button',
        'aria-pressed': 'false',
        tabindex: 0
      },

      properties: {
        /**
         * Fired when the checked state changes due to user interaction.
         *
         * @event change
         */
        /**
         * Fired when the checked state changes.
         *
         * @event iron-change
         */
      },

      listeners: {
        track: '_ontrack'
      },

      ready: function() {
        this._isReady = true;
      },

      // button-behavior hook
      _buttonStateChanged: function() {
        if (this.disabled) {
          return;
        }
        if (this._isReady) {
          this.checked = this.active;
        }
      },

      _ontrack: function(event) {
        var track = event.detail;
        if (track.state === 'start') {
          this._trackStart(track);
        } else if (track.state === 'track') {
          this._trackMove(track);
        } else if (track.state === 'end') {
          this._trackEnd(track);
        }
      },

      _trackStart: function(track) {
        this._width = this.$.toggleBar.offsetWidth / 2;
        /*
         * keep an track-only check state to keep the dragging behavior smooth
         * while toggling activations
         */
        this._trackChecked = this.checked;
        this.$.toggleButton.classList.add('dragging');
      },

      _trackMove: function(track) {
        var dx = track.dx;
        this._x = Math.min(this._width,
            Math.max(0, this._trackChecked ? this._width + dx : dx));
        this.translate3d(this._x + 'px', 0, 0, this.$.toggleButton);
        this._userActivate(this._x > (this._width / 2));
      },

      _trackEnd: function(track) {
        this.$.toggleButton.classList.remove('dragging');
        this.transform('', this.$.toggleButton);
      }

    });
  </script>
</dom-module>