UNPKG

@razorpay/blade

Version:

The Design System that powers Razorpay

278 lines (257 loc) 9.27 kB
import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray'; import { dropdownComponentIds } from './dropdownComponentIds.js'; // Save a list of named combobox actions, for future readability var SelectActions = { Close: 'Close', CloseSelect: 'CloseSelect', First: 'First', Last: 'Last', Next: 'Next', Open: 'Open', PageDown: 'PageDown', PageUp: 'PageUp', Previous: 'Previous', Select: 'Select', Type: 'Type' }; /** * Filter an array of options against an input string * returns an array of options that begin with the filter string, case-independent * */ function filterOptions() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; var filter = arguments.length > 1 ? arguments[1] : undefined; var exclude = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : []; return options.filter(function (option) { var matches = option.toLowerCase().startsWith(filter.toLowerCase()); return matches && !exclude.includes(option); }); } /** * Map a keypress to action */ function getActionFromKey(e, isOpen, dropdownTriggerer) { if (!e) { return undefined; } var altKey = e.altKey, ctrlKey = e.ctrlKey, metaKey = e.metaKey; var key = ''; if ('key' in e) { key = e.key; } var openKeys = ['ArrowDown', 'ArrowUp', 'Enter', ' ']; // all keys that will do the default open action if (!key) return undefined; // handle opening when closed if (!isOpen && key && openKeys.includes(key)) { return SelectActions.Open; } // home and end move the selected option when open or closed if (key === 'Home') { return SelectActions.First; } if (key === 'End') { return SelectActions.Last; } // handle typing characters when open or closed if (key === 'Backspace' || key === 'Clear' || key.length === 1 && key !== ' ' && !altKey && !ctrlKey && !metaKey) { return SelectActions.Type; } // handle keys when open if (isOpen) { if (key === 'ArrowUp' && altKey) { return SelectActions.CloseSelect; } else if (key === 'ArrowDown' && !altKey) { return SelectActions.Next; } else if (key === 'ArrowUp') { return SelectActions.Previous; } else if (key === 'PageUp') { return SelectActions.PageUp; } else if (key === 'PageDown') { return SelectActions.PageDown; } else if (key === 'Escape') { return SelectActions.Close; } else if (key === 'Enter' || // we ignore the spacebar select in autocomplete since hitting spacebar might be expected while typing dropdownTriggerer !== dropdownComponentIds.triggers.AutoComplete && dropdownTriggerer !== dropdownComponentIds.triggers.SearchInput && key === ' ') { return SelectActions.CloseSelect; } } return undefined; } /** * Return the index of an option from an array of options, based on a search string * * if the filter is multiple iterations of the same letter (e.g "aaa"), then cycle through first-letter matches **/ function getIndexByLetter(options, filter) { var startIndex = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; var orderedOptions = [].concat(_toConsumableArray(options.slice(startIndex)), _toConsumableArray(options.slice(0, startIndex))); var firstMatch = filterOptions(orderedOptions, filter)[0]; var allSameLetter = function allSameLetter(array) { return array.every(function (letter) { return letter === array[0]; }); }; // first check if there is an exact match for the typed string if (firstMatch) { return options.indexOf(firstMatch); } // if the same letter is being repeated, cycle through first-letter matches else if (allSameLetter(filter.split(''))) { var matches = filterOptions(orderedOptions, filter[0]); return options.indexOf(matches[0]); } // if no matches, return -1 else { return -1; } } /** * This functions makes sure the optionsIndex is not going out of possible options */ function getUpdatedIndex(_ref) { var currentIndex = _ref.currentIndex, maxIndex = _ref.maxIndex, actionType = _ref.actionType; // On PageUP or PageDown, we jump focus by 10 items or to the first or last element // Details: https://www.w3.org/WAI/ARIA/apg/example-index/combobox/combobox-select-only.html#:~:text=PageUp,to%20last%20option). var pageSize = 10; switch (actionType) { case SelectActions.First: return 0; case SelectActions.Last: return maxIndex; case SelectActions.Previous: return Math.max(0, currentIndex - 1); case SelectActions.Next: return Math.min(maxIndex, currentIndex + 1); case SelectActions.PageUp: return Math.max(0, currentIndex - pageSize); case SelectActions.PageDown: return Math.min(maxIndex, currentIndex + pageSize); default: return currentIndex; } } /** * Checks if the given HTML element is visible on screen */ function isElementVisibleOnScreen(element) { var bounding = element.getBoundingClientRect(); return bounding.top >= 0 && bounding.left >= 0 && bounding.bottom <= (window.innerHeight || document.documentElement.clientHeight) && bounding.right <= (window.innerWidth || document.documentElement.clientWidth); } /** * Checks if element is visible inside the given container */ function isElementVisible(container, element) { var containerRect = container.getBoundingClientRect(); var elementRect = element.getBoundingClientRect(); var isVerticalVisible = elementRect.top >= containerRect.top && elementRect.bottom <= containerRect.bottom; return isVerticalVisible; } /** * Checks if the dropdown is scrollable */ function isScrollable(element) { return element && element.clientHeight < element.scrollHeight; } /** * Performs the action when actionType is passed * * This function handles all the keydown actions. */ var performAction = function performAction(action, payload, actions) { var event = payload.event; switch (action) { case SelectActions.Last: // @ts-expect-error: intentional fallthrough, ignoring the warning case SelectActions.First: actions.setIsOpen(true); // intentional fallthrough case SelectActions.Next: case SelectActions.Previous: case SelectActions.PageUp: case SelectActions.PageDown: event.preventDefault(); actions.onOptionChange(action); return true; case SelectActions.CloseSelect: event.preventDefault(); actions.selectCurrentOption(); return true; case SelectActions.Close: event.preventDefault(); actions.close(); return true; case SelectActions.Type: actions.onComboType(event.key, action); return true; case SelectActions.Open: event.preventDefault(); actions.setIsOpen(true); return true; default: break; } return false; }; /** * When options list is large, it can have a scrollbar. * * This function ensures the active option is always in the viewport */ var ensureScrollVisiblity = function ensureScrollVisiblity(newActiveIndex, containerElement, options) { // ensure the new option is in view if (containerElement) { if (isScrollable(containerElement)) { var optionEl = containerElement.querySelectorAll('[role="option"], [role="menuitem"]'); // Making sure its the same element as the one from options state if (newActiveIndex >= 0 && optionEl[newActiveIndex].dataset.value === options[newActiveIndex]) { var activeElement = optionEl[newActiveIndex]; if (!isElementVisible(containerElement, activeElement)) { activeElement.scrollIntoView({ inline: 'nearest' }); } if (!isElementVisibleOnScreen(optionEl[newActiveIndex])) { activeElement.scrollIntoView({ behavior: 'smooth' }); } } } } }; /** * value that is set in the actual form input */ var makeInputValue = function makeInputValue(selectedIndices, options) { if (options.length === 0) { return ''; } return selectedIndices.map(function (selectedIndex) { var _options$selectedInde; return (_options$selectedInde = options[selectedIndex]) === null || _options$selectedInde === void 0 ? void 0 : _options$selectedInde.value; }).join(', '); }; /** * Value that is displayed inside the select field */ var makeInputDisplayValue = function makeInputDisplayValue(selectedIndices, options) { // When no item is selected or no item is present if (options.length === 0 || selectedIndices.length === 0) { return ''; } // When one item is selected, we display that item's title in input if (selectedIndices.length === 1) { var _options$selectedIndi; return (_options$selectedIndi = options[selectedIndices[0]]) === null || _options$selectedIndi === void 0 ? void 0 : _options$selectedIndi.title; } // When more than one item is selected, we display the count of items return "".concat(selectedIndices.length, " items selected"); }; export { ensureScrollVisiblity, filterOptions, getActionFromKey, getIndexByLetter, getUpdatedIndex, isElementVisibleOnScreen, isScrollable, makeInputDisplayValue, makeInputValue, performAction }; //# sourceMappingURL=dropdownUtils.js.map