blob: c5221b8e1dd3b24ab281b8054c2d7c41e2943594 [file] [log] [blame]
<!--
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<link rel="import" href="../polymer/polymer.html">
<link rel="import" href="../iron-a11y-keys-behavior/iron-a11y-keys-behavior.html">
<link rel="import" href="iron-control-state.html">
<script>
/**
* @demo demo/index.html
* @polymerBehavior Polymer.IronButtonState
*/
Polymer.IronButtonStateImpl = {
properties: {
/**
* If true, the user is currently holding down the button.
*/
pressed: {
type: Boolean,
readOnly: true,
value: false,
reflectToAttribute: true,
observer: '_pressedChanged'
},
/**
* If true, the button toggles the active state with each tap or press
* of the spacebar.
*/
toggles: {
type: Boolean,
value: false,
reflectToAttribute: true
},
/**
* If true, the button is a toggle and is currently in the active state.
*/
active: {
type: Boolean,
value: false,
notify: true,
reflectToAttribute: true
},
/**
* True if the element is currently being pressed by a "pointer," which
* is loosely defined as mouse or touch input (but specifically excluding
* keyboard input).
*/
pointerDown: {
type: Boolean,
readOnly: true,
value: false
},
/**
* True if the input device that caused the element to receive focus
* was a keyboard.
*/
receivedFocusFromKeyboard: {
type: Boolean,
readOnly: true
},
/**
* The aria attribute to be set if the button is a toggle and in the
* active state.
*/
ariaActiveAttribute: {
type: String,
value: 'aria-pressed',
observer: '_ariaActiveAttributeChanged'
}
},
listeners: {
down: '_downHandler',
up: '_upHandler',
tap: '_tapHandler'
},
observers: [
'_focusChanged(focused)',
'_activeChanged(active, ariaActiveAttribute)'
],
keyBindings: {
'enter:keydown': '_asyncClick',
'space:keydown': '_spaceKeyDownHandler',
'space:keyup': '_spaceKeyUpHandler',
},
_mouseEventRe: /^mouse/,
_tapHandler: function() {
if (this.toggles) {
// a tap is needed to toggle the active state
this._userActivate(!this.active);
} else {
this.active = false;
}
},
_focusChanged: function(focused) {
this._detectKeyboardFocus(focused);
if (!focused) {
this._setPressed(false);
}
},
_detectKeyboardFocus: function(focused) {
this._setReceivedFocusFromKeyboard(!this.pointerDown && focused);
},
// to emulate native checkbox, (de-)activations from a user interaction fire
// 'change' events
_userActivate: function(active) {
if (this.active !== active) {
this.active = active;
this.fire('change');
}
},
_downHandler: function(event) {
this._setPointerDown(true);
this._setPressed(true);
this._setReceivedFocusFromKeyboard(false);
},
_upHandler: function() {
this._setPointerDown(false);
this._setPressed(false);
},
/**
* @param {!KeyboardEvent} event .
*/
_spaceKeyDownHandler: function(event) {
var keyboardEvent = event.detail.keyboardEvent;
var target = Polymer.dom(keyboardEvent).localTarget;
// Ignore the event if this is coming from a focused light child, since that
// element will deal with it.
if (this.isLightDescendant(/** @type {Node} */(target)))
return;
keyboardEvent.preventDefault();
keyboardEvent.stopImmediatePropagation();
this._setPressed(true);
},
/**
* @param {!KeyboardEvent} event .
*/
_spaceKeyUpHandler: function(event) {
var keyboardEvent = event.detail.keyboardEvent;
var target = Polymer.dom(keyboardEvent).localTarget;
// Ignore the event if this is coming from a focused light child, since that
// element will deal with it.
if (this.isLightDescendant(/** @type {Node} */(target)))
return;
if (this.pressed) {
this._asyncClick();
}
this._setPressed(false);
},
// trigger click asynchronously, the asynchrony is useful to allow one
// event handler to unwind before triggering another event
_asyncClick: function() {
this.async(function() {
this.click();
}, 1);
},
// any of these changes are considered a change to button state
_pressedChanged: function(pressed) {
this._changedButtonState();
},
_ariaActiveAttributeChanged: function(value, oldValue) {
if (oldValue && oldValue != value && this.hasAttribute(oldValue)) {
this.removeAttribute(oldValue);
}
},
_activeChanged: function(active, ariaActiveAttribute) {
if (this.toggles) {
this.setAttribute(this.ariaActiveAttribute,
active ? 'true' : 'false');
} else {
this.removeAttribute(this.ariaActiveAttribute);
}
this._changedButtonState();
},
_controlStateChanged: function() {
if (this.disabled) {
this._setPressed(false);
} else {
this._changedButtonState();
}
},
// provide hook for follow-on behaviors to react to button-state
_changedButtonState: function() {
if (this._buttonStateChanged) {
this._buttonStateChanged(); // abstract
}
}
};
/** @polymerBehavior */
Polymer.IronButtonState = [
Polymer.IronA11yKeysBehavior,
Polymer.IronButtonStateImpl
];
</script>