UNPKG

@dialpad/dialtone

Version:

Dialpad's Dialtone design system monorepo

189 lines (188 loc) 6.63 kB
"use strict"; Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } }); const dom = require("./dom.cjs"); const common_utils = require("../utils.cjs"); const ERROR_INVALID_LIST_ELEMENT = "listElementKey is required or the referenced element doesn't exist. Received listElement: "; const KeyboardNavigation = ({ // Role of the list items in the component. This is used to identify the list items // so you must update this if the role of your list items is anything other than 'option' listItemRole = "option", // Key of the data prop that will be added to the component. indexKey = "highlightIndex", idKey = "highlightId", // Key of the method that references the list element. listElementKey = "listRef", // Optional, Key of the computed prop that references the currently active item element. activeItemKey = "", // Optional, name of the method that toggles the list visibility. Used for // opening the list when up or down is pressed. openMethod = null, // Optional, method to call when the highlightIndex is changed. afterHighlightMethod = null, // Optional, method to call when the highlightIndex goes past the beginning of the list. beginningOfListMethod = null, // Optional, method to call when the highlightIndex goes past the end of the list. endOfListMethod = null, // Scroll the active element into view when highlighted by a keyboard event. scrollToOnHighlight = true, // Focus the active element on keyboard navigation. focusOnKeyboardNavigation = false } = {}) => ({ mixins: [dom.default], data() { return { [indexKey]: -1, [idKey]: "", scrollToOnHighlight, focusOnKeyboardNavigation }; }, provide() { return { highlightId: () => this[idKey] }; }, methods: { // Returns the list element // this[listElement]() can return a Vue component, in which case we need to target // the $el property, or it can simply be an html element. _getListElement() { var _a; return common_utils.returnFirstEl((_a = this[listElementKey]()) == null ? void 0 : _a.$el) || this[listElementKey](); }, // Gets the length of all the items in the list, uses the listItemRole param to determine // whether an element is a list item. _itemsLength() { const listItems = this._getListItemNodes(); if (listItems === null) { return 0; } return listItems.length; }, // Gets all the list item nodes within the list element _getListItemNodes() { const listElement = this._getListElement(); if (!listElement) { console.error(ERROR_INVALID_LIST_ELEMENT, listElement); return null; } return Array.from(listElement.querySelectorAll(`[role="${listItemRole}"], #sr-only-close-button`)); }, onUpKey() { if (openMethod) { this[openMethod](true); } if (this[indexKey] > 0) { this.setHighlightIndex(this[indexKey] - 1); } else if (beginningOfListMethod) { this[beginningOfListMethod](); } this.scrollActiveItemIntoViewIfNeeded(); this.focusActiveItemIfNeeded(); }, onDownKey() { if (openMethod) { this[openMethod](true); } if (this[indexKey] < this._itemsLength() - 1) { this.setHighlightIndex(this[indexKey] + 1); } else if (endOfListMethod) { this[endOfListMethod](); } this.scrollActiveItemIntoViewIfNeeded(); this.focusActiveItemIfNeeded(); }, onHomeKey() { this.jumpToBeginning(); this.scrollActiveItemIntoViewIfNeeded(); this.focusActiveItemIfNeeded(); }, onEndKey() { this.jumpToEnd(); this.scrollActiveItemIntoViewIfNeeded(); this.focusActiveItemIfNeeded(); }, onNavigationKey(key) { const listItems = this._getListItemNodes(); const matchingItems = listItems.filter((item) => { const content = item.textContent.trim().toLowerCase(); return content.startsWith(key.toLowerCase()); }); if (matchingItems.length <= 0) { return; } const highlightedMatchingItemIndex = matchingItems.findIndex((item) => { return this[indexKey] === listItems.indexOf(item); }); const nextHighlightedItemIndex = listItems.indexOf( highlightedMatchingItemIndex < matchingItems.length - 1 ? matchingItems[highlightedMatchingItemIndex + 1] : matchingItems[0] ); this.setHighlightIndex(nextHighlightedItemIndex); this.scrollActiveItemIntoViewIfNeeded(); this.focusActiveItemIfNeeded(); }, isValidLetter(key) { if (key.length > 1) { return false; } return key >= "a" && key <= "z" || key >= "A" && key <= "Z"; }, jumpToBeginning() { this.setHighlightIndex(0); }, jumpToEnd() { this.setHighlightIndex(this._itemsLength() - 1); }, setHighlightIndex(num) { this[indexKey] = num; this[idKey] = this._getItemId(num); if (this._itemsLength() && afterHighlightMethod) { this[afterHighlightMethod](num); } }, setHighlightId(id) { this[idKey] = id; this[indexKey] = this._getItemIndex(id); if (this._itemsLength() && afterHighlightMethod) { this[afterHighlightMethod](this._getItemIndex(id)); } }, _getItemIndex(id) { const listElement = this._getListElement(); if (!listElement) { return; } const listItems = Array.from(listElement.querySelectorAll(`[role="${listItemRole}"], #sr-only-close-button`)); return listItems.indexOf(listElement.querySelector(`#${id}`)); }, _getItemId(index) { var _a; const listElement = this._getListElement(); if (!listElement) { return; } return (_a = listElement.querySelectorAll(`[role="${listItemRole}"], #sr-only-close-button`)[index]) == null ? void 0 : _a.id; }, scrollActiveItemIntoViewIfNeeded() { if (!this.scrollToOnHighlight) { return; } const activeItemEl = this[activeItemKey]; if (activeItemEl) { const listElement = this._getListElement(); this.scrollElementIntoViewIfNeeded(activeItemEl, null, null, listElement); } }, focusActiveItemIfNeeded() { if (!this.focusOnKeyboardNavigation) { return; } const activeItemEl = this[activeItemKey]; if (activeItemEl) { activeItemEl.focus(); } } } }); exports.default = KeyboardNavigation; //# sourceMappingURL=keyboard_list_navigation.cjs.map