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