• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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