Home | History | Annotate | Download | only in paper-shadow
      1 <!--
      2 Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
      3 This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
      4 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
      5 The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
      6 Code distributed by Google as part of the polymer project is also
      7 subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
      8 -->
      9 
     10 <!--
     11 The `paper-shadow` element is a helper to add shadows to elements.
     12 Paper shadows are composed of two shadows on top of each other. We
     13 mimic this effect by using two elements on top of each other, each with a
     14 different drop shadow. You can apply the shadow to an element by assigning
     15 it as the target. If you do not specify a target, the shadow is applied to
     16 the `paper-shadow` element's parent element or shadow host element if its
     17 parent is a shadow root. Alternatively, you can use the CSS classes included
     18 by this element directly.
     19 
     20 Example:
     21 
     22     <div id="myCard" class="card"></div>
     23     <paper-shadow id="myShadow" z="1"></div>
     24 
     25     // Assign a target explicitly
     26     myShadow.target = document.getElementById('myCard');
     27 
     28     // Auto-assign the target.
     29     <div class="card">
     30       <paper-shadow z="1"></paper-shadow>
     31     </div>
     32 
     33     // Use the classes directly
     34     <div class="card paper-shadow-top paper-shadow-top-z-1">
     35       <div class="card-inner paper-shadow-bottom paper-shadow-bottom-z-1"></div>
     36     </div>
     37 
     38 If you assign a target to a `paper-shadow` element, it creates two nodes and inserts
     39 them as the first children of the target, or the first children of the target's shadow
     40 root if there is one. This implies:
     41 
     42   1. If the primary node that drops the shadow has styling that affects its shape,
     43      the same styling must be applied to elements with class `paper-shadow`.
     44      `border-radius` is a very common property and is inherited automatically.
     45 
     46   2. The target's overflow property will be set to `overflow: visible` because the
     47      shadow is rendered beyond the bounds of its container. Position the shadow as a
     48      separate layer and use a different child element for clipping if needed.
     49 
     50 @group Paper Elements
     51 @class paper-shadow
     52 -->
     53 
     54 <link href="../polymer/polymer.html" rel="import">
     55 
     56 <polymer-element name="paper-shadow">
     57 
     58   <template>
     59 
     60     <link no-shim href="paper-shadow.css" rel="stylesheet">
     61 
     62   </template>
     63 
     64   <script>
     65     Polymer('paper-shadow', {
     66 
     67       publish: {
     68         /**
     69          * If set, the shadow is applied to this node.
     70          *
     71          * @attribute target
     72          * @type Element
     73          * @default null
     74          */
     75         target: {value: null, reflect: true},
     76 
     77         /**
     78          * The z-depth of this shadow, from 0-5.
     79          *
     80          * @attribute z
     81          * @type number
     82          * @default 1
     83          */
     84         z: {value: 1, reflect: true},
     85 
     86         /**
     87          * If true, the shadow animates between z-depth changes.
     88          *
     89          * @attribute animated
     90          * @type boolean
     91          * @default false
     92          */
     93         animated: {value: false, reflect: true},
     94 
     95         /**
     96          * Workaround: getComputedStyle is wrong sometimes so `paper-shadow`
     97          * may overwrite the `position` CSS property. Set this property to
     98          * true to prevent this.
     99          *
    100          * @attribute hasPosition
    101          * @type boolean
    102          * @default false
    103          */
    104         hasPosition: {value: false}
    105       },
    106 
    107       // NOTE: include template so that styles are loaded, but remove
    108       // so that we can decide dynamically what part to include
    109       registerCallback: function(polymerElement) {
    110         var template = polymerElement.querySelector('template');
    111         this._style = template.content.querySelector('style');
    112         this._style.removeAttribute('no-shim');
    113       },
    114 
    115       fetchTemplate: function() {
    116         return null;
    117       },
    118 
    119       attached: function() {
    120         this.installScopeStyle(this._style);
    121 
    122         // If no target is bound at attach, default the target to the parent
    123         // element or shadow host.
    124         if (!this.target) {
    125           if (!this.parentElement && this.parentNode.host) {
    126             this.target = this.parentNode.host;
    127           } else if (this.parentElement && (window.ShadowDOMPolyfill ? this.parentElement !== wrap(document.body) : this.parentElement !== document.body)) {
    128             this.target = this.parentElement;
    129           }
    130         }
    131       },
    132 
    133       targetChanged: function(old) {
    134         if (old) {
    135           this.removeShadow(old);
    136         }
    137         if (this.target) {
    138           this.addShadow(this.target);
    139         }
    140       },
    141 
    142       zChanged: function(old) {
    143         if (this.target && this.target._paperShadow) {
    144           var shadow = this.target._paperShadow;
    145           ['top', 'bottom'].forEach(function(s) {
    146             shadow[s].classList.remove('paper-shadow-' + s + '-z-' + old);
    147             shadow[s].classList.add('paper-shadow-' + s + '-z-' + this.z);
    148           }.bind(this));
    149         }
    150       },
    151 
    152       animatedChanged: function() {
    153         if (this.target && this.target._paperShadow) {
    154           var shadow = this.target._paperShadow;
    155           ['top', 'bottom'].forEach(function(s) {
    156             if (this.animated) {
    157               shadow[s].classList.add('paper-shadow-animated');
    158             } else {
    159               shadow[s].classList.remove('paper-shadow-animated');
    160             }
    161           }.bind(this));
    162         }
    163       },
    164 
    165       addShadow: function(node) {
    166         if (node._paperShadow) {
    167           return;
    168         }
    169 
    170         var computed = getComputedStyle(node);
    171         if (!this.hasPosition && computed.position === 'static') {
    172           node.style.position = 'relative';
    173         }
    174         node.style.overflow = 'visible';
    175 
    176         // Both the top and bottom shadows are children of the target, so
    177         // it does not affect the classes and CSS properties of the target.
    178         ['top', 'bottom'].forEach(function(s) {
    179           var inner = (node._paperShadow && node._paperShadow[s]) || document.createElement('div');
    180           inner.classList.add('paper-shadow');
    181           inner.classList.add('paper-shadow-' + s + '-z-' + this.z);
    182           if (this.animated) {
    183             inner.classList.add('paper-shadow-animated');
    184           }
    185 
    186           if (node.shadowRoot) {
    187             node.shadowRoot.insertBefore(inner, node.shadowRoot.firstChild);
    188           } else {
    189             node.insertBefore(inner, node.firstChild);
    190           }
    191 
    192           node._paperShadow = node._paperShadow || {};
    193           node._paperShadow[s] = inner;
    194         }.bind(this));
    195 
    196       },
    197 
    198       removeShadow: function(node) {
    199         if (!node._paperShadow) {
    200           return;
    201         }
    202 
    203         ['top', 'bottom'].forEach(function(s) {
    204           node._paperShadow[s].remove();
    205         });
    206         node._paperShadow = null;
    207 
    208         node.style.position = null;
    209       }
    210 
    211     });
    212   </script>
    213 </polymer-element>
    214