UNPKG

@atlassian/aui

Version:

Atlassian User Interface library

169 lines (150 loc) 4.98 kB
import $ from './jquery'; import keyCode from './key-code'; import amdify from './internal/amdify'; import skate from './internal/skate'; import state from './internal/state'; import { warn } from './internal/log'; export function getTrigger(element) { return state(element).get('last-trigger') || findControllers(element)[0]; } export function setTrigger(element, trigger) { var validTrigger = trigger && trigger.nodeType && trigger.nodeType === 1; return state(element).set('last-trigger', validTrigger ? trigger : false); } export function hasTrigger(element) { return !!getTrigger(element); } export function doIfTrigger(element, callback) { var trigger = getTrigger(element); if (trigger) { callback(trigger); } } export function forEachTrigger(element, callback) { return Array.prototype.forEach.call(findControllers(element), callback); } function isNestedAnchor(trigger, target) { var $closestAnchor = $(target).closest('a[href]', trigger); return !!$closestAnchor.length && $closestAnchor[0] !== trigger; } function findControllers(element) { const frames = window.frames; const selector = `[aria-controls="${element.id}"]`; let controllers = []; let someFramesAreCrossOrigin = false; for (let i = 0; i < frames.length; i++) { try { let nodeList = frames[i].document.querySelectorAll(selector); controllers = controllers.concat(Array.prototype.slice.apply(nodeList)); // eslint-disable-next-line no-unused-vars } catch (e) { // Silently catch DOM exceptions related to accessing cross-origin frames someFramesAreCrossOrigin = true; } } const currentDocumentControllers = document.querySelectorAll(selector); const allControllers = Array.prototype.slice .apply(currentDocumentControllers) .concat(controllers); if (allControllers.length === 0 && someFramesAreCrossOrigin === true) { warn( [ `No triggers found for element (${element.id}) in iframes from the same origin.`, 'However some iframes in this document are cross-origin.', 'The trigger-element relations crossing the origin boundary are not supported.', ].join(' ') ); } return allControllers; } function findControlled(trigger) { return document.getElementById(trigger.getAttribute('aria-controls')); } function isEnabled(element) { return element.getAttribute('aria-disabled') !== 'true'; } function triggerMessage(trigger, e) { if (isEnabled(trigger)) { var component = findControlled(trigger); if (component && component.message) { component.message(e); } } } /** * Converts native or jQuery events in to a "message" object. * Basically helps us keep message types consistent. */ function msg(e, type) { const { target, currentTarget, relatedTarget } = e; const { keyCode, which } = e; return { type, data: type === 'keydown' ? which || keyCode : undefined, target, currentTarget, relatedTarget, preventDefault: () => e.preventDefault(), }; } function focusingToControlledElement(trigger, e) { let relatedTarget = e.relatedTarget; // relatedTarget is always null in IE11 but activeElement is set to correct value if (!relatedTarget) { relatedTarget = document.activeElement; } const $component = $(findControlled(trigger)); return $component.find(relatedTarget).length > 0; } const events = { click(trigger, e) { if (!isNestedAnchor(trigger, e.target)) { triggerMessage(trigger, e); e.preventDefault(); } }, keydown(trigger, e) { const key = e.data; if (key === keyCode.ENTER || key === keyCode.SPACE) { e.preventDefault(); e.type = 'click'; events.click(trigger, e); } }, mouseenter(trigger, e) { triggerMessage(trigger, e); }, mouseleave(trigger, e) { triggerMessage(trigger, e); }, focus(trigger, e) { triggerMessage(trigger, e); }, blur(trigger, e) { if (focusingToControlledElement(trigger, e)) { return; } triggerMessage(trigger, e); }, }; Object.keys(events).forEach(function (name) { const handler = events[name]; $(document).on(`${name}.aui-trigger`, '[data-aui-trigger]', function (e) { handler(e.currentTarget, msg(e, name)); }); }); skate('data-aui-trigger', { type: skate.type.ATTRIBUTE, prototype: { disable: function () { this.setAttribute('aria-disabled', 'true'); }, enable: function () { this.setAttribute('aria-disabled', 'false'); }, isEnabled: function () { return isEnabled(this); }, }, }); amdify('aui/trigger');