UNPKG

carbon-custom-elements

Version:

A Carbon Design System variant that's as easy to use as native HTML elements, with no framework tax, no framework silo.

159 lines (131 loc) 4.39 kB
/** * @license * * Copyright IBM Corp. 2019 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ import on from 'carbon-components/es/globals/js/misc/on'; /** * @param config Plugin configuration. * @returns A Flatpickr plugin to manage focus to align with the UX pattern in our design system. */ export default (config => fp => { /** * Focuses on calendar dropdown. */ const focusCalendar = () => { const { calendarContainer, selectedDateElem, todayDateElem } = fp; (selectedDateElem || todayDateElem || calendarContainer).focus(); }; /** * Handles `blur` event to move the focus back to the `<input>`. */ const handleBlur = ({ target, relatedTarget }) => { // Obtains `beingUpdated` up-front because it'll be flushed out shortly const { calendarContainer, isOpen } = fp; if (isOpen && calendarContainer.contains(target) && !calendarContainer.contains(relatedTarget)) { Promise.resolve().then(() => { const rootNode = target.getRootNode(); // This `blur` event handler can be called from Flatpickr's code cleaning up calenar dropdown's DOM, // and changing focus in such condition causes removing an orphaned DOM node, // because Flatpickr redraws the calendar dropdown when the `<input>` gets focus. if (rootNode.nodeType === Node.DOCUMENT_NODE || rootNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { if (fp._lastFocusInput === config.inputTo) { config.inputTo.focus(); } else { config.inputFrom.focus(); } // Closing after moving focus. Reversing the order will cause re-opening calendar dropdown upon focusing fp.close(); } }); } }; /** * Handles `keydown` event to move focus on calendar dropdown. */ const handleInputKeydown = event => { const { key } = event; if (key === 'ArrowDown' || key === 'Down' || key === 'Enter') { event.preventDefault(); fp.open(); if (key !== 'Enter') { focusCalendar(); } else { // Hitting Enter key blurs the `<input>`, causing any element to lose focus setTimeout(focusCalendar, 0); } } }; /** * Handles `focus` event on `<input>` for starting/end date to track the lastly focused one. */ const handleInputFocus = ({ target }) => { fp._lastFocusInput = target; }; /** * Releases event listeners used in this Flatpickr plugin. */ const release = () => { if (fp._hBXCEDatePickerFocusPluginBlur) { fp._hBXCEDatePickerFocusPluginBlur = fp._hBXCEDatePickerFocusPluginBlur.release(); } if (fp._hBXCEDatePickerFocusPluginFocusTo) { fp._hBXCEDatePickerFocusPluginFocusTo = fp._hBXCEDatePickerFocusPluginFocusTo.release(); } if (fp._hBXCEDatePickerFocusPluginFocusFrom) { fp._hBXCEDatePickerFocusPluginFocusFrom = fp._hBXCEDatePickerFocusPluginFocusFrom.release(); } if (fp._hBXCEDatePickerFocusPluginKeydownTo) { fp._hBXCEDatePickerFocusPluginKeydownTo = fp._hBXCEDatePickerFocusPluginKeydownTo.release(); } if (fp._hBXCEDatePickerFocusPluginKeydownFrom) { fp._hBXCEDatePickerFocusPluginKeydownFrom = fp._hBXCEDatePickerFocusPluginKeydownFrom.release(); } }; /** * Sets up event listeners used for this Flatpickr plugin. */ const init = () => { release(); const { inputFrom, inputTo } = config; fp._hBXCEDatePickerFocusPluginBlur = on(fp.calendarContainer, 'blur', handleBlur, true); fp._hBXCEDatePickerFocusPluginKeydownFrom = on(inputFrom, 'keydown', handleInputKeydown); if (inputTo) { fp._hBXCEDatePickerFocusPluginKeydownTo = on(inputTo, 'keydown', handleInputKeydown); } fp._hBXCEDatePickerFocusPluginFocusFrom = on(inputFrom, 'focus', handleInputFocus); if (inputTo) { fp._hBXCEDatePickerFocusPluginFocusTo = on(inputTo, 'focus', handleInputFocus); } }; /** * Registers this Flatpickr plugin. * @param calendar The Flatpickr instance. */ const register = () => { fp.loadedPlugins.push('carbonFlatpickrFocusPlugin'); }; return { onReady: [register, init], onDestroy: release }; }); //# sourceMappingURL=focus-plugin.js.map