@logo-elements/component-base
Version:
A set of mixins used by Logo Elements which is extended from Vaadin components.
119 lines (105 loc) • 3.31 kB
JavaScript
/**
* @license
* Copyright LOGO YAZILIM SANAYİ VE TİCARET A.Ş.
*
* Save to the extent permitted by law, you may not use, copy, modify,
* distribute or create derivative works of this material or any part
* of it without the prior written consent of LOGO YAZILIM SANAYİ VE TİCARET A.Ş. Limited.
* Any reproduction of this material must contain this notice.
*/
import { dedupingMixin } from '@polymer/polymer/lib/utils/mixin.js';
// We consider the keyboard to be active if the window has received a keydown
// event since the last mousedown event.
let keyboardActive = false;
// Listen for top-level keydown and mousedown events.
// Use capture phase so we detect events even if they're handled.
window.addEventListener(
'keydown',
() => {
keyboardActive = true;
},
{ capture: true }
);
window.addEventListener(
'mousedown',
() => {
keyboardActive = false;
},
{ capture: true }
);
/**
* A mixin to handle `focused` and `focus-ring` attributes based on focus.
*
* @polymerMixin
*/
export const FocusMixin = dedupingMixin(
(superclass) =>
class FocusMixinClass extends superclass {
/**
* @protected
* @return {boolean}
*/
get _keyboardActive() {
return keyboardActive;
}
/** @protected */
ready() {
this.addEventListener('focusin', (e) => {
if (this._shouldSetFocus(e)) {
this._setFocused(true);
}
});
this.addEventListener('focusout', (e) => {
if (this._shouldRemoveFocus(e)) {
this._setFocused(false);
}
});
// In super.ready() other 'focusin' and 'focusout' listeners might be
// added, so we call it after our own ones to ensure they execute first.
// Issue to watch out: when incorrect, <logo-elements-combo-box> refocuses the
// input field on iOS after “Done” is pressed.
super.ready();
}
/** @protected */
disconnectedCallback() {
super.disconnectedCallback();
// in non-Chrome browsers, blur does not fire on the element when it is disconnected.
// reproducible in `<logo-elements-date-picker>` when closing on `Cancel` or `Today` click.
if (this.hasAttribute('focused')) {
this._setFocused(false);
}
}
/**
* Override to change how focused and focus-ring attributes are set.
*
* @param {boolean} focused
* @protected
*/
_setFocused(focused) {
this.toggleAttribute('focused', focused);
// focus-ring is true when the element was focused from the keyboard.
// Focus Ring [A11ycasts]: https://youtu.be/ilj2P5-5CjI
this.toggleAttribute('focus-ring', focused && this._keyboardActive);
}
/**
* Override to define if the field receives focus based on the event.
*
* @param {FocusEvent} _event
* @return {boolean}
* @protected
*/
_shouldSetFocus(_event) {
return true;
}
/**
* Override to define if the field loses focus based on the event.
*
* @param {FocusEvent} _event
* @return {boolean}
* @protected
*/
_shouldRemoveFocus(_event) {
return true;
}
}
);