@progress/kendo-angular-menu
Version:
Kendo UI Angular Menu component
105 lines (104 loc) • 3.11 kB
JavaScript
/**-----------------------------------------------------------------------------------------
* Copyright © 2025 Progress Software Corporation. All rights reserved.
* Licensed under commercial license. See LICENSE.md in the project root for more information
*-------------------------------------------------------------------------------------------*/
import { NODE_INDEX } from './constants';
const DEFAULT_ID = 'kendo-matches-container';
const focusableRegex = /^(?:a|input|select|option|textarea|button|object)$/i;
const matches = (element, selector) => (element.matches || element.msMatchesSelector).call(element, selector);
/**
* @hidden
*/
export const closest = (node, predicate) => {
while (node && !predicate(node)) {
node = node.parentNode;
}
return node;
};
/**
* @hidden
*/
export const closestInScope = (node, predicate, scope) => {
while (node && node !== scope && !predicate(node)) {
node = node.parentNode;
}
if (node !== scope) {
return node;
}
};
/**
* @hidden
*/
export const isFocusable = (element) => {
if (element.tagName) {
const tagName = element.tagName.toLowerCase();
const tabIndex = element.getAttribute('tabIndex');
const skipTab = tabIndex === '-1';
let focusable = tabIndex !== null && !skipTab;
if (focusableRegex.test(tagName)) {
focusable = !element.disabled && !skipTab;
}
return focusable;
}
return false;
};
const toClassList = (classNames) => String(classNames).trim().split(' ');
/**
* @hidden
*/
export const hasClass = (element, name) => {
return toClassList(element.className).indexOf(name) >= 0;
};
/**
* @hidden
*/
export const matchesClasses = (classes) => {
const list = toClassList(classes);
return (element) => {
const classList = toClassList(element.className);
return Boolean(list.find(name => classList.indexOf(name) >= 0));
};
};
/**
* @hidden
*/
export const nodeIndex = (node) => node.getAttribute(NODE_INDEX);
/**
* @hidden
*/
export const closestItem = (node, scope) => closestInScope(node, nodeIndex, scope);
/**
* @hidden
*/
export const closestList = (node) => {
let list = closest(node, matchesClasses('k-menu-popup k-menu k-menu-group'));
if (list && hasClass(list, 'k-menu-popup')) {
list = list.querySelector('.k-menu-group');
}
return list;
};
/**
* @hidden
*/
export const inMenu = (node, itemsService) => {
if (node === itemsService.lists[0].element.nativeElement) {
return false;
}
const list = closestList(node);
return list && itemsService.containsList(list);
};
/**
* @hidden
*/
export const findInContainer = (element, selector, container) => {
const id = container.getAttribute('id');
if (!id) {
container.setAttribute('id', DEFAULT_ID);
}
const contextSelector = `#${id || DEFAULT_ID} ${selector}`;
const match = closestInScope(element, node => matches(node, contextSelector), container);
if (!id) {
container.removeAttribute('id');
}
return match;
};