spincycle
Version: 
A reactive message router and object manager that lets clients subscribe to object property changes on the server
221 lines (182 loc) • 6.12 kB
HTML
<!--
@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="../iron-resizable-behavior/iron-resizable-behavior.html">
<link rel="import" href="../iron-selector/iron-selectable.html">
<link rel="import" href="neon-animation-runner-behavior.html">
<!--
Material design: [Meaningful transitions](https://www.google.com/design/spec/animation/meaningful-transitions.html)
`neon-animated-pages` manages a set of pages and runs an animation when switching between them. Its
children pages should implement `Polymer.NeonAnimatableBehavior` and define `entry` and `exit`
animations to be run when switching to or switching out of the page.
@group Neon Elements
@element neon-animated-pages
@demo demo/index.html
-->
<dom-module id="neon-animated-pages">
  <template>
    <style>
      :host {
        display: block;
        position: relative;
      }
      :host > ::content > * {
        position: absolute;
        top: 0;
        left: 0;
        bottom: 0;
        right: 0;
      }
      :host > ::content > :not(.iron-selected):not(.neon-animating) {
        display: none ;
      }
      :host > ::content > .neon-animating {
        pointer-events: none;
      }
    </style>
    
    <content id="content"></content>
  </template>
</dom-module>
<script>
(function() {
  Polymer({
    is: 'neon-animated-pages',
    behaviors: [
      Polymer.IronResizableBehavior,
      Polymer.IronSelectableBehavior,
      Polymer.NeonAnimationRunnerBehavior
    ],
    properties: {
      activateEvent: {
        type: String,
        value: ''
      },
      // if true, the initial page selection will also be animated according to its animation config.
      animateInitialSelection: {
        type: Boolean,
        value: false
      }
    },
    listeners: {
      'iron-select': '_onIronSelect',
      'neon-animation-finish': '_onNeonAnimationFinish'
    },
    _onIronSelect: function(event) {
      var selectedPage = event.detail.item;
      // Only consider child elements.
      if (this.items.indexOf(selectedPage) < 0) {
        return;
      }
      var oldPage = this._valueToItem(this._prevSelected) || false;
      this._prevSelected = this.selected;
      // on initial load and if animateInitialSelection is negated, simply display selectedPage.
      if (!oldPage && !this.animateInitialSelection) {
        this._completeSelectedChanged();
        return;
      }
      this.animationConfig = [];
      // configure selectedPage animations.
      if (this.entryAnimation) {
        this.animationConfig.push({
          name: this.entryAnimation,
          node: selectedPage
        });
      } else {
        if (selectedPage.getAnimationConfig) {
          this.animationConfig.push({
            animatable: selectedPage,
            type: 'entry'
          });
        }
      }
      // configure oldPage animations iff exists.
      if (oldPage) {
        // cancel the currently running animation if one is ongoing.
        if (oldPage.classList.contains('neon-animating')) {
          this._squelchNextFinishEvent = true;
          this.cancelAnimation();
          this._completeSelectedChanged();
          this._squelchNextFinishEvent = false;
        }
        // configure the animation.
        if (this.exitAnimation) {
          this.animationConfig.push({
            name: this.exitAnimation,
            node: oldPage
          });
        } else {
          if (oldPage.getAnimationConfig) {
            this.animationConfig.push({
              animatable: oldPage,
              type: 'exit'
            });
          }
        }
        // display the oldPage during the transition.
        oldPage.classList.add('neon-animating');
      }
      // display the selectedPage during the transition.
      selectedPage.classList.add('neon-animating');
      // actually run the animations.
      if (this.animationConfig.length >= 1) {
        // on first load, ensure we run animations only after element is attached.
        if (!this.isAttached) {
          this.async(function () {
            this.playAnimation(undefined, {
              fromPage: null,
              toPage: selectedPage
            });
          });
        } else {
          this.playAnimation(undefined, {
            fromPage: oldPage,
            toPage: selectedPage
          });
        }
      } else {
        this._completeSelectedChanged(oldPage, selectedPage);
      }
    },
    /**
     * @param {Object=} oldPage
     * @param {Object=} selectedPage
     */
    _completeSelectedChanged: function(oldPage, selectedPage) {
      if (selectedPage) {
        selectedPage.classList.remove('neon-animating');
      }
      if (oldPage) {
        oldPage.classList.remove('neon-animating');
      }
      if (!selectedPage || !oldPage) {
        var nodes = Polymer.dom(this.$.content).getDistributedNodes();
        for (var node, index = 0; node = nodes[index]; index++) {
          node.classList && node.classList.remove('neon-animating');
        }
      }
      this.async(this._notifyPageResize);
    },
    _onNeonAnimationFinish: function(event) {
      if (this._squelchNextFinishEvent) {
        this._squelchNextFinishEvent = false;
        return;
      }
      this._completeSelectedChanged(event.detail.fromPage, event.detail.toPage);
    },
    _notifyPageResize: function() {
      var selectedPage = this.selectedItem || this._valueToItem(this.selected);
      this.resizerShouldNotify = function(element) {
        return element == selectedPage;
      }
      this.notifyResize();
    }
  })
})();
</script>