• 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      '_detectKeyboardFocus(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    _detectKeyboardFocus: function(focused) {
116      this._setReceivedFocusFromKeyboard(!this.pointerDown && focused);
117    },
118
119    // to emulate native checkbox, (de-)activations from a user interaction fire
120    // 'change' events
121    _userActivate: function(active) {
122      if (this.active !== active) {
123        this.active = active;
124        this.fire('change');
125      }
126    },
127
128    _downHandler: function(event) {
129      this._setPointerDown(true);
130      this._setPressed(true);
131      this._setReceivedFocusFromKeyboard(false);
132    },
133
134    _upHandler: function() {
135      this._setPointerDown(false);
136      this._setPressed(false);
137    },
138
139    /**
140     * @param {!KeyboardEvent} event .
141     */
142    _spaceKeyDownHandler: function(event) {
143      var keyboardEvent = event.detail.keyboardEvent;
144      var target = Polymer.dom(keyboardEvent).localTarget;
145
146      // Ignore the event if this is coming from a focused light child, since that
147      // element will deal with it.
148      if (this.isLightDescendant(/** @type {Node} */(target)))
149        return;
150
151      keyboardEvent.preventDefault();
152      keyboardEvent.stopImmediatePropagation();
153      this._setPressed(true);
154    },
155
156    /**
157     * @param {!KeyboardEvent} event .
158     */
159    _spaceKeyUpHandler: function(event) {
160      var keyboardEvent = event.detail.keyboardEvent;
161      var target = Polymer.dom(keyboardEvent).localTarget;
162
163      // Ignore the event if this is coming from a focused light child, since that
164      // element will deal with it.
165      if (this.isLightDescendant(/** @type {Node} */(target)))
166        return;
167
168      if (this.pressed) {
169        this._asyncClick();
170      }
171      this._setPressed(false);
172    },
173
174    // trigger click asynchronously, the asynchrony is useful to allow one
175    // event handler to unwind before triggering another event
176    _asyncClick: function() {
177      this.async(function() {
178        this.click();
179      }, 1);
180    },
181
182    // any of these changes are considered a change to button state
183
184    _pressedChanged: function(pressed) {
185      this._changedButtonState();
186    },
187
188    _ariaActiveAttributeChanged: function(value, oldValue) {
189      if (oldValue && oldValue != value && this.hasAttribute(oldValue)) {
190        this.removeAttribute(oldValue);
191      }
192    },
193
194    _activeChanged: function(active, ariaActiveAttribute) {
195      if (this.toggles) {
196        this.setAttribute(this.ariaActiveAttribute,
197                          active ? 'true' : 'false');
198      } else {
199        this.removeAttribute(this.ariaActiveAttribute);
200      }
201      this._changedButtonState();
202    },
203
204    _controlStateChanged: function() {
205      if (this.disabled) {
206        this._setPressed(false);
207      } else {
208        this._changedButtonState();
209      }
210    },
211
212    // provide hook for follow-on behaviors to react to button-state
213
214    _changedButtonState: function() {
215      if (this._buttonStateChanged) {
216        this._buttonStateChanged(); // abstract
217      }
218    }
219
220  };
221
222  /** @polymerBehavior */
223  Polymer.IronButtonState = [
224    Polymer.IronA11yKeysBehavior,
225    Polymer.IronButtonStateImpl
226  ];
227
228</script>
229