1<!-- 2@license 3Copyright (c) 2015 The Polymer Project Authors. All rights reserved. 4This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt 5The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt 6The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt 7Code distributed by Google as part of the polymer project is also 8subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt 9--> 10 11<link rel="import" href="../polymer/polymer.html"> 12<link rel="import" href="../iron-a11y-keys-behavior/iron-a11y-keys-behavior.html"> 13<link rel="import" href="iron-control-state.html"> 14 15<script> 16 17 /** 18 * @demo demo/index.html 19 * @polymerBehavior Polymer.IronButtonState 20 */ 21 Polymer.IronButtonStateImpl = { 22 23 properties: { 24 25 /** 26 * If true, the user is currently holding down the button. 27 */ 28 pressed: { 29 type: Boolean, 30 readOnly: true, 31 value: false, 32 reflectToAttribute: true, 33 observer: '_pressedChanged' 34 }, 35 36 /** 37 * If true, the button toggles the active state with each tap or press 38 * of the spacebar. 39 */ 40 toggles: { 41 type: Boolean, 42 value: false, 43 reflectToAttribute: true 44 }, 45 46 /** 47 * If true, the button is a toggle and is currently in the active state. 48 */ 49 active: { 50 type: Boolean, 51 value: false, 52 notify: true, 53 reflectToAttribute: true 54 }, 55 56 /** 57 * True if the element is currently being pressed by a "pointer," which 58 * is loosely defined as mouse or touch input (but specifically excluding 59 * keyboard input). 60 */ 61 pointerDown: { 62 type: Boolean, 63 readOnly: true, 64 value: false 65 }, 66 67 /** 68 * True if the input device that caused the element to receive focus 69 * was a keyboard. 70 */ 71 receivedFocusFromKeyboard: { 72 type: Boolean, 73 readOnly: true 74 }, 75 76 /** 77 * The aria attribute to be set if the button is a toggle and in the 78 * active state. 79 */ 80 ariaActiveAttribute: { 81 type: String, 82 value: 'aria-pressed', 83 observer: '_ariaActiveAttributeChanged' 84 } 85 }, 86 87 listeners: { 88 down: '_downHandler', 89 up: '_upHandler', 90 tap: '_tapHandler' 91 }, 92 93 observers: [ 94 '_focusChanged(focused)', 95 '_activeChanged(active, ariaActiveAttribute)' 96 ], 97 98 keyBindings: { 99 'enter:keydown': '_asyncClick', 100 'space:keydown': '_spaceKeyDownHandler', 101 'space:keyup': '_spaceKeyUpHandler', 102 }, 103 104 _mouseEventRe: /^mouse/, 105 106 _tapHandler: function() { 107 if (this.toggles) { 108 // a tap is needed to toggle the active state 109 this._userActivate(!this.active); 110 } else { 111 this.active = false; 112 } 113 }, 114 115 _focusChanged: function(focused) { 116 this._detectKeyboardFocus(focused); 117 118 if (!focused) { 119 this._setPressed(false); 120 } 121 }, 122 123 _detectKeyboardFocus: function(focused) { 124 this._setReceivedFocusFromKeyboard(!this.pointerDown && focused); 125 }, 126 127 // to emulate native checkbox, (de-)activations from a user interaction fire 128 // 'change' events 129 _userActivate: function(active) { 130 if (this.active !== active) { 131 this.active = active; 132 this.fire('change'); 133 } 134 }, 135 136 _downHandler: function(event) { 137 this._setPointerDown(true); 138 this._setPressed(true); 139 this._setReceivedFocusFromKeyboard(false); 140 }, 141 142 _upHandler: function() { 143 this._setPointerDown(false); 144 this._setPressed(false); 145 }, 146 147 /** 148 * @param {!KeyboardEvent} event . 149 */ 150 _spaceKeyDownHandler: function(event) { 151 var keyboardEvent = event.detail.keyboardEvent; 152 var target = Polymer.dom(keyboardEvent).localTarget; 153 154 // Ignore the event if this is coming from a focused light child, since that 155 // element will deal with it. 156 if (this.isLightDescendant(/** @type {Node} */(target))) 157 return; 158 159 keyboardEvent.preventDefault(); 160 keyboardEvent.stopImmediatePropagation(); 161 this._setPressed(true); 162 }, 163 164 /** 165 * @param {!KeyboardEvent} event . 166 */ 167 _spaceKeyUpHandler: function(event) { 168 var keyboardEvent = event.detail.keyboardEvent; 169 var target = Polymer.dom(keyboardEvent).localTarget; 170 171 // Ignore the event if this is coming from a focused light child, since that 172 // element will deal with it. 173 if (this.isLightDescendant(/** @type {Node} */(target))) 174 return; 175 176 if (this.pressed) { 177 this._asyncClick(); 178 } 179 this._setPressed(false); 180 }, 181 182 // trigger click asynchronously, the asynchrony is useful to allow one 183 // event handler to unwind before triggering another event 184 _asyncClick: function() { 185 this.async(function() { 186 this.click(); 187 }, 1); 188 }, 189 190 // any of these changes are considered a change to button state 191 192 _pressedChanged: function(pressed) { 193 this._changedButtonState(); 194 }, 195 196 _ariaActiveAttributeChanged: function(value, oldValue) { 197 if (oldValue && oldValue != value && this.hasAttribute(oldValue)) { 198 this.removeAttribute(oldValue); 199 } 200 }, 201 202 _activeChanged: function(active, ariaActiveAttribute) { 203 if (this.toggles) { 204 this.setAttribute(this.ariaActiveAttribute, 205 active ? 'true' : 'false'); 206 } else { 207 this.removeAttribute(this.ariaActiveAttribute); 208 } 209 this._changedButtonState(); 210 }, 211 212 _controlStateChanged: function() { 213 if (this.disabled) { 214 this._setPressed(false); 215 } else { 216 this._changedButtonState(); 217 } 218 }, 219 220 // provide hook for follow-on behaviors to react to button-state 221 222 _changedButtonState: function() { 223 if (this._buttonStateChanged) { 224 this._buttonStateChanged(); // abstract 225 } 226 } 227 228 }; 229 230 /** @polymerBehavior */ 231 Polymer.IronButtonState = [ 232 Polymer.IronA11yKeysBehavior, 233 Polymer.IronButtonStateImpl 234 ]; 235 236</script> 237