UNPKG

@atlassian/aui

Version:

Atlassian User Interface Framework

241 lines (224 loc) 8.19 kB
'use strict'; import './spin'; import './tooltip'; import { setBooleanAttribute } from './internal/attributes'; import $ from './jquery'; import enforce from './internal/enforcer'; import keyCode from './key-code'; import skateTemplateHtml from 'skatejs-template-html'; import skate from './internal/skate'; import { INPUT_SUFFIX } from './internal/constants'; import CustomEvent from './polyfills/custom-event'; function fireChangeEvent(element) { if (element._canFireEventsNow) { element.dispatchEvent(new CustomEvent('change', { bubbles: true })); } } function getInput (element) { return element._input || (element._input = element.querySelector('input')); } function removedAttributeHandler(attributeName, element) { getInput(element).removeAttribute(attributeName); } function fallbackAttributeHandler(attributeName, element, change) { getInput(element).setAttribute(attributeName, change.newValue); } function getAttributeHandler (attributeName) { return { removed: removedAttributeHandler.bind(this, attributeName), fallback: fallbackAttributeHandler.bind(this, attributeName) }; } const formAttributeHandler = { removed: function (element) { removedAttributeHandler.call(this, 'form', element); element._formId = null; }, fallback: function (element, change) { fallbackAttributeHandler.call(this, 'form', element, change); element._formId = change.newValue; } }; var idAttributeHandler = { removed: removedAttributeHandler.bind(this, 'id'), fallback: function (element, change) { getInput(element).setAttribute('id', `${change.newValue}${INPUT_SUFFIX}`); } }; var checkedAttributeHandler = { removed: function (element) { getInput(element).checked = false; fireChangeEvent(element); }, fallback: function (element) { getInput(element).checked = true; fireChangeEvent(element); } }; var labelHandler = { removed: function (element) { getInput(element).removeAttribute('aria-label'); }, fallback: function (element, change) { getInput(element).setAttribute('aria-label', change.newValue); } }; function clickHandler(element, e) { if (!element.disabled && !element.busy && e.target !== element._input) { element._input.checked = !element._input.checked; } setBooleanAttribute(element, 'checked', getInput(element).checked); } function setDisabledForLabels(element, disabled) { if (!element.id) { return; } Array.prototype.forEach.call(document.querySelectorAll(`aui-label[for="${element.id}"]`), function (el) { el.disabled = disabled; }); } /** * Workaround to prevent pressing SPACE on busy state. * Preventing click event still makes the toggle flip and revert back. * So on CSS side, the input has "pointer-events: none" on busy state. */ function bindEventsToInput(element) { element._input.addEventListener('keydown', function (e) { if (element.busy && e.keyCode === keyCode.SPACE) { e.preventDefault(); } }); // prevent toggle can be trigger through SPACE key on Firefox if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) { element._input.addEventListener('click', function (e) { if (element.busy) { e.preventDefault(); } }); } } skate('aui-toggle', { // "assistive" class avoids direct interaction with the <input> element // (which prevents our click handler from being called), // while allow the element to still participate in the form. template: skateTemplateHtml( '<input type="checkbox" class="aui-toggle-input assistive">', '<span class="aui-toggle-view">', '<span class="aui-toggle-tick aui-icon aui-icon-small aui-iconfont-success"></span>', '<span class="aui-toggle-cross aui-icon aui-icon-small aui-iconfont-close-dialog"></span>', '</span>' ), created: function (element) { element._input = getInput(element); // avoid using _input in attribute handlers element._tick = element.querySelector('.aui-toggle-tick'); element._cross = element.querySelector('.aui-toggle-cross'); $(element._input).tooltip({ title: function () { return this.checked ? this.getAttribute('tooltip-on') : this.getAttribute('tooltip-off'); }, gravity: 's', hoverable: false }); bindEventsToInput(element); if (element.hasAttribute('checked')) { getInput(element).setAttribute('checked', ''); } element._canFireEventsNow = true; }, attached: function (element) { enforce(element).attributeExists('label'); }, events: { click: clickHandler }, attributes: { id: idAttributeHandler, checked: checkedAttributeHandler, disabled: getAttributeHandler('disabled'), form: formAttributeHandler, name: getAttributeHandler('name'), value: getAttributeHandler('value'), 'tooltip-on': { value: AJS.I18n.getText('aui.toggle.on'), fallback: function (element, change) { getInput(element).setAttribute('tooltip-on', change.newValue || AJS.I18n.getText('aui.toggle.on')); } }, 'tooltip-off': { value: AJS.I18n.getText('aui.toggle.off'), fallback: function (element, change) { getInput(element).setAttribute('tooltip-off', change.newValue || AJS.I18n.getText('aui.toggle.off')); } }, label: labelHandler }, prototype: { focus: function () { this._input.focus(); return this; }, get checked () { return this._input.checked; }, set checked (value) { // Need to explicitly set the property on the checkbox because the // checkbox's property doesn't change with it's attribute after it // is clicked. if (this._input.checked !== value) { this._input.checked = value; setBooleanAttribute(this, 'checked', value); } }, get disabled () { return this._input.disabled; }, set disabled (value) { return setBooleanAttribute(this, 'disabled', value); }, get form () { return document.getElementById(this._formId); }, set form (value) { formAttributeHandler.fallback.call(this, this, { newValue: value || null }); return this.form; }, get name () { return this._input.name; }, set name (value) { this.setAttribute('name', value); return value; }, get value () { return this._input.value; }, set value (value) { // Setting the value of an input to null sets it to empty string. this.setAttribute('value', value === null ? '' : value); return value; }, get busy () { return this._input.getAttribute('aria-busy') === 'true'; }, set busy (value) { setBooleanAttribute(this, 'busy', value); if (value) { this._input.setAttribute('aria-busy', 'true'); this._input.indeterminate = true; if (this.checked) { $(this._input).addClass('indeterminate-checked'); $(this._tick).spin({zIndex: null}); } else { $(this._cross).spin({zIndex: null, color: 'black'}); } } else { $(this._input).removeClass('indeterminate-checked'); this._input.indeterminate = false; this._input.removeAttribute('aria-busy'); $(this._cross).spinStop(); $(this._tick).spinStop(); } setDisabledForLabels(this, !!value); return value; } } });