@material/icon-toggle
Version:
The Material Components for the web icon toggle component
241 lines (200 loc) • 6.91 kB
JavaScript
/**
* @license
* Copyright 2016 Google Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import MDCFoundation from '@material/base/foundation';
/* eslint-disable no-unused-vars */
import {MDCIconToggleAdapter, IconToggleEvent} from './adapter';
import {cssClasses, strings} from './constants';
/**
* @extends {MDCFoundation<!MDCIconToggleAdapter>}
*/
class MDCIconToggleFoundation extends MDCFoundation {
static get cssClasses() {
return cssClasses;
}
static get strings() {
return strings;
}
static get defaultAdapter() {
return {
addClass: (/* className: string */) => {},
removeClass: (/* className: string */) => {},
registerInteractionHandler: (/* type: string, handler: EventListener */) => {},
deregisterInteractionHandler: (/* type: string, handler: EventListener */) => {},
setText: (/* text: string */) => {},
getTabIndex: () => /* number */ 0,
setTabIndex: (/* tabIndex: number */) => {},
getAttr: (/* name: string */) => /* string */ '',
setAttr: (/* name: string, value: string */) => {},
rmAttr: (/* name: string */) => {},
notifyChange: (/* evtData: IconToggleEvent */) => {},
};
}
constructor(adapter) {
super(Object.assign(MDCIconToggleFoundation.defaultAdapter, adapter));
/** @private {boolean} */
this.on_ = false;
/** @private {boolean} */
this.disabled_ = false;
/** @private {number} */
this.savedTabIndex_ = -1;
/** @private {?IconToggleState} */
this.toggleOnData_ = null;
/** @private {?IconToggleState} */
this.toggleOffData_ = null;
this.clickHandler_ = /** @private {!EventListener} */ (
() => this.toggleFromEvt_());
/** @private {boolean} */
this.isHandlingKeydown_ = false;
this.keydownHandler_ = /** @private {!EventListener} */ ((/** @type {!KeyboardKey} */ evt) => {
if (isSpace(evt)) {
this.isHandlingKeydown_ = true;
return evt.preventDefault();
}
});
this.keyupHandler_ = /** @private {!EventListener} */ ((/** @type {!KeyboardKey} */ evt) => {
if (isSpace(evt)) {
this.isHandlingKeydown_ = false;
this.toggleFromEvt_();
}
});
}
init() {
this.refreshToggleData();
this.savedTabIndex_ = this.adapter_.getTabIndex();
this.adapter_.registerInteractionHandler('click', this.clickHandler_);
this.adapter_.registerInteractionHandler('keydown', this.keydownHandler_);
this.adapter_.registerInteractionHandler('keyup', this.keyupHandler_);
}
refreshToggleData() {
const {DATA_TOGGLE_ON, DATA_TOGGLE_OFF} = MDCIconToggleFoundation.strings;
this.toggleOnData_ = this.parseJsonDataAttr_(DATA_TOGGLE_ON);
this.toggleOffData_ = this.parseJsonDataAttr_(DATA_TOGGLE_OFF);
}
destroy() {
this.adapter_.deregisterInteractionHandler('click', this.clickHandler_);
this.adapter_.deregisterInteractionHandler('keydown', this.keydownHandler_);
this.adapter_.deregisterInteractionHandler('keyup', this.keyupHandler_);
}
/** @private */
toggleFromEvt_() {
this.toggle();
const {on_: isOn} = this;
this.adapter_.notifyChange(/** @type {!IconToggleEvent} */ ({isOn}));
}
/** @return {boolean} */
isOn() {
return this.on_;
}
/** @param {boolean=} isOn */
toggle(isOn = !this.on_) {
this.on_ = isOn;
const {ARIA_LABEL, ARIA_PRESSED} = MDCIconToggleFoundation.strings;
if (this.on_) {
this.adapter_.setAttr(ARIA_PRESSED, 'true');
} else {
this.adapter_.setAttr(ARIA_PRESSED, 'false');
}
const {cssClass: classToRemove} =
this.on_ ? this.toggleOffData_ : this.toggleOnData_;
if (classToRemove) {
this.adapter_.removeClass(classToRemove);
}
const {content, label, cssClass} = this.on_ ? this.toggleOnData_ : this.toggleOffData_;
if (cssClass) {
this.adapter_.addClass(cssClass);
}
if (content) {
this.adapter_.setText(content);
}
if (label) {
this.adapter_.setAttr(ARIA_LABEL, label);
}
}
/**
* @param {string} dataAttr
* @return {!IconToggleState}
*/
parseJsonDataAttr_(dataAttr) {
const val = this.adapter_.getAttr(dataAttr);
if (!val) {
return {};
}
return /** @type {!IconToggleState} */ (JSON.parse(val));
}
/** @return {boolean} */
isDisabled() {
return this.disabled_;
}
/** @param {boolean} isDisabled */
setDisabled(isDisabled) {
this.disabled_ = isDisabled;
const {DISABLED} = MDCIconToggleFoundation.cssClasses;
const {ARIA_DISABLED} = MDCIconToggleFoundation.strings;
if (this.disabled_) {
this.savedTabIndex_ = this.adapter_.getTabIndex();
this.adapter_.setTabIndex(-1);
this.adapter_.setAttr(ARIA_DISABLED, 'true');
this.adapter_.addClass(DISABLED);
} else {
this.adapter_.setTabIndex(this.savedTabIndex_);
this.adapter_.rmAttr(ARIA_DISABLED);
this.adapter_.removeClass(DISABLED);
}
}
/** @return {boolean} */
isKeyboardActivated() {
return this.isHandlingKeydown_;
}
}
/**
* @typedef {{
* key: string,
* keyCode: number
* }}
*/
let KeyboardKey;
/**
* @param {!KeyboardKey} keyboardKey
* @return {boolean}
*/
function isSpace(keyboardKey) {
return keyboardKey.key === 'Space' || keyboardKey.keyCode === 32;
}
/** @record */
class IconToggleState {}
/**
* The aria-label value of the icon toggle, or undefined if there is no aria-label.
* @export {string|undefined}
*/
IconToggleState.prototype.label;
/**
* The text for the icon toggle, or undefined if there is no text.
* @export {string|undefined}
*/
IconToggleState.prototype.content;
/**
* The CSS class to add to the icon toggle, or undefined if there is no CSS class.
* @export {string|undefined}
*/
IconToggleState.prototype.cssClass;
export default MDCIconToggleFoundation;