UNPKG

ids-enterprise

Version:

Infor Design System (IDS) Enterprise Components for the web

245 lines (211 loc) • 6.67 kB
import { DOM } from './dom'; /** * HideFocus Behavior * Only shows the focus state on key entry (tabs or arrows). * @param {HTMLElement|SVGElement} element the base element * @returns {HideFocus} component instance */ function HideFocus(element) { return this.init(element); } HideFocus.prototype = { init(element) { if (!this.element && (element instanceof HTMLElement || element instanceof SVGElement)) { this.element = element; } const $el = $(element); let isClick = false; let isFocused = false; let labelClicked = false; // Checkbox, Radio buttons or Switch if ($el.is('.checkbox, .radio, .switch')) { let label = $el.next(); if (label.is('[type="hidden"]')) { label = label.next(); } this.label = label[0]; $el.addClass('hide-focus') .on('focusin.hide-focus', (e) => { if (!isClick && !isFocused && !labelClicked) { $el.removeClass('hide-focus'); $el.triggerHandler('hidefocusremove', [e]); } isClick = false; isFocused = true; labelClicked = false; }) .on('focusout.hide-focus', (e) => { $el.addClass('hide-focus'); labelClicked = label.is(labelClicked); isClick = false; isFocused = false; $el.triggerHandler('hidefocusadd', [e]); }); label.on('mousedown.hide-focus', function (e) { labelClicked = this; isClick = true; $el.addClass('hide-focus'); $el.triggerHandler('hidefocusadd', [e]); }); } else { // All other elements (ie. Hyperlinks) $el.addClass('hide-focus') .on('mousedown.hide-focus touchstart.hide-focus', (e) => { isClick = true; $el.addClass('hide-focus'); $el.triggerHandler('hidefocusadd', [e]); }) .on('focusin.hide-focus', (e) => { if (!isClick && !isFocused) { $el.removeClass('hide-focus'); $el.triggerHandler('hidefocusremove', [e]); } isClick = false; isFocused = true; }) .on('focusout.hide-focus', (e) => { $el.addClass('hide-focus'); isClick = false; isFocused = false; $el.triggerHandler('hidefocusadd', [e]); }); } return this; }, updated() { return this .teardown() .init(); }, teardown() { if (this.label) { $(this.label).off('mousedown.hide-focus'); } const elemEvents = [ 'focusin.hide-focus', 'focusout.hide-focus', 'mousedown.hide-focus', 'touchstart.hide-focus' ]; $(this.element).off(elemEvents.join(' ')); return this; } }; /** * jQuery component wrapper for the HideFocus behavior * @returns {jQuery[]} components being acted on */ $.fn.hideFocus = function () { return this.each(function () { let instance = $.data(this, 'hidefocus'); if (instance) { instance.updated(); } else { instance = $.data(this, 'hidefocus', new HideFocus(this)); instance.destroy = function destroy() { this.teardown(); $.removeData(this, 'hidefocus'); }; } }); }; /** * Allows for the smooth scrolling of an element's content area. * @param {HTMLElement|SVGElement|jQuery[]} el The element being manipulated. * @param {number} target target distance. * @param {number} duration the time that will be needed for the scrolling to complete. * @returns {$.Deferred} promise that resolved when scrolling completes. */ function smoothScrollTo(el, target, duration) { const dfd = $.Deferred(); if (!DOM.isElement(el)) { // Not a workable element return dfd.reject(); } // Strip the jQuery if (el instanceof $ && el.length) { el = el[0]; } // undefined (not zero) target should instantly resolve if (target === undefined || target === null) { return dfd.resolve(); } if (isNaN(duration)) { duration = 0; } target = Math.round(target); duration = Math.round(duration); if (duration < 0) { // bad duration return dfd.fail(); } if (duration === 0) { el.scrollLeft += target; return dfd.resolve(); } const startTime = Date.now(); const endTime = startTime + duration; const startLeft = el.scrollLeft; const distance = target; // based on http://en.wikipedia.org/wiki/Smoothstep function smoothStep(start, end, point) { if (point <= start) { return 0; } if (point >= end) { return 1; } const x = (point - start) / (end - start); // interpolation return x * x * (3 - 2 * x); } // This is to keep track of where the element's scrollLeft is // supposed to be, based on what we're doing let previousLeft = el.scrollLeft; // This is like a think function from a game loop function scrollFrame() { if (el.scrollLeft !== previousLeft) { // interrupted dfd.reject(); return; } // set the scrollLeft for this frame const now = Date.now(); const point = smoothStep(startTime, endTime, now); const frameLeft = Math.round(startLeft + (distance * point)); el.scrollLeft = frameLeft; // check if we're done! if (now >= endTime) { dfd.resolve(); return; } // If we were supposed to scroll but didn't, then we // probably hit the limit, so consider it done; not // interrupted. if (el.scrollLeft === previousLeft && el.scrollLeft !== frameLeft) { dfd.resolve(); return; } previousLeft = el.scrollLeft; // schedule next frame for execution setTimeout(scrollFrame, 0); } // boostrap the animation process setTimeout(scrollFrame, 0); return dfd; } /** * Binds the Soho Behavior _smoothScrollTo()_ to a jQuery selector * @param {number} target target distance to scroll the element * @param {number} duration the time that will be needed for the scrolling to complete. * @returns {$.Deferred} promise that resolved when scrolling completes. */ $.fn.smoothScroll = function (target, duration) { return smoothScrollTo(this, target, duration); }; /** * Uses 'requestAnimationFrame' or 'setTimeout' to defer a function. * @param {function} callback the callback that runs on a deferment. * @param {number} timer how long to delay before running the callback. * @returns {function} either `requestAnimationFrame` or `setTimeout` */ function defer(callback, timer) { const deferMethod = typeof window.requestAnimationFrame !== 'undefined' ? window.requestAnimationFrame : setTimeout; return deferMethod(callback, timer); } export { HideFocus, smoothScrollTo, defer };