UNPKG

@material/icon-toggle

Version:
241 lines (200 loc) 6.91 kB
/** * @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;