@progress/kendo-react-layout
Version:
React Layout components enable you to create a perceptive and intuitive layout of web projects. KendoReact Layout package
233 lines (232 loc) • 9.16 kB
JavaScript
/**
* @license
*-------------------------------------------------------------------------------------------
* Copyright © 2026 Progress Software Corporation. All rights reserved.
* Licensed under commercial license. See LICENSE.md in the package root for more information
*-------------------------------------------------------------------------------------------
*/
import * as m from "react";
import i from "prop-types";
import { Keys as h, classNames as g, dispatchEvent as b } from "@progress/kendo-react-common";
import { EMPTY_ID as r, getItemById as a, isIdZeroLevel as p, getDirectParentId as C, getRootParentId as R, isIdEmptyOrZeroLevel as l, ZERO_LEVEL_ZERO_ITEM_ID as f } from "../utils/itemsIdsUtils.mjs";
import { prepareInputItemsForInternalWork as D } from "../utils/prepareInputItemsForInternalWork.mjs";
import { getNewItemIdUponKeyboardNavigation as I } from "../utils/getNewItemIdUponKeyboardNavigation.mjs";
import { getHoverOpenDelay as H, getHoverCloseDelay as O } from "../utils/hoverDelay.mjs";
import { MenuItemInternalsList as y } from "./MenuItemInternal.mjs";
import { DirectionHolder as k } from "../utils/DirectionHolder.mjs";
import { MouseOverHandler as L } from "../utils/MouseOverHandler.mjs";
const u = {
focusedItemId: r,
hoveredItemId: r,
tabbableItemId: f
}, n = class n extends m.Component {
constructor(s) {
super(s), this.menuWrapperEl = null, this.directionHolder = new k(), this.inputItems = [], this.items = [], this.reset = () => {
this.clearItemHoverAndLeaveRequestsIfApplicable(), this.setState(u);
}, this.onKeyDown = (e) => {
if (this.state.focusedItemId !== r) {
const t = a(this.state.focusedItemId, this.items);
let o = I(
this.items,
t.id,
e.keyCode,
e.key,
this.props.vertical,
this.directionHolder.getIsDirectionRightToLeft()
);
const d = a(o, this.items);
if (d && d.separator && (o = I(
this.items,
o,
e.keyCode,
e.key,
this.props.vertical,
this.directionHolder.getIsDirectionRightToLeft()
)), t.id !== o && (e.preventDefault(), this.setFocusedItemId(o)), (e.keyCode === h.enter || e.keyCode === h.space) && !t.disabled && (this.mouseOverHandler.handleItemSelectedViaKeyboard(), this.dispatchSelectEventIfWired(e, t.id), !e.isDefaultPrevented() && t.items.length === 0 && t.url && window.location.assign(t.url)), e.keyCode === h.esc && p(t.id) && this.props.onClose) {
const v = this.getInputItem(t.id);
this.props.onClose({
target: this,
item: v,
itemId: t.id
});
}
}
}, this.onItemMouseOver = (e) => {
this.mouseOverHandler.IsMouseOverEnabled && (this.clearItemHoverAndLeaveRequestsIfApplicable(), this.itemHoverRequest = window.setTimeout(() => {
this.setHoveredItemId(e), this.itemHoverRequest = null;
}, H(this.props)));
}, this.onItemMouseLeave = (e) => {
this.mouseOverHandler.IsMouseOverEnabled && this.isItemWithDefaultClose(e) && (this.clearItemHoverAndLeaveRequestsIfApplicable(), this.itemLeaveRequest = window.setTimeout(() => {
this.setHoveredItemId(r), this.itemLeaveRequest = null;
}, O(this.props)));
}, this.onItemMouseDown = () => {
this.mouseOverHandler.handleItemMouseDown();
}, this.onItemFocus = (e) => {
this.setFocusedItemId(e), this.mouseOverHandler.handleItemFocus();
}, this.onItemClick = (e, t) => {
const o = a(t, this.items);
o.disabled || (this.setFocusedItemId(t), this.mouseOverHandler.handleItemClick(t, this.isItemWithDefaultClose(t)), this.dispatchSelectEventIfWired(e, t), !e.isDefaultPrevented() && o.url && window.location.assign(o.url));
}, this.onPopupClose = (e, t) => {
this.props.onClose && this.props.onClose({
target: this,
item: this.getInputItem(e),
itemId: e,
popupCloseEvent: t
});
}, this.onItemBlur = (e, t) => {
if (this.isItemWithDefaultClose(e) && this.setFocusedItemId(r), t.relatedTarget && t.relatedTarget.nodeName === "LI") {
const o = t.relatedTarget.getAttribute("id");
if (o && o.includes(this.menuItemId))
return;
}
if (p(e) && this.props.onClose) {
const o = this.getInputItem(e);
this.props.onClose({
target: this,
item: o,
itemId: e
});
}
}, this.getInputItem = (e) => a(e, this.inputItems), this.mouseOverHandler = new L(this.props.openOnClick, this.reset, this.onItemMouseOver), this.state = Object.assign({}, u, { isFirstRender: !0 });
}
get menuItemId() {
return this.props.id;
}
/**
* The current element or `null` if there is no one.
*/
get element() {
return this.menuWrapperEl;
}
get animate() {
return this.props.animate !== void 0 ? this.props.animate : n.defaultProps.animate;
}
/**
* @hidden
*/
render() {
this.prepareItems(), this.state.isFirstRender || this.directionHolder.setIsDirectionRightToLeft(this.checkIsDirectionRightToLeft());
const s = this.state.hoveredItemId ? this.state.hoveredItemId : this.state.focusedItemId ? C(this.state.focusedItemId) : r;
return /* @__PURE__ */ m.createElement(
"div",
{
id: this.props.id,
onKeyDown: this.onKeyDown,
style: this.props.style,
className: this.directionHolder.getIsDirectionRightToLeft() ? "k-rtl" : void 0,
ref: (e) => {
this.menuWrapperEl = e;
}
},
/* @__PURE__ */ m.createElement(
y,
{
className: this.getMenuClassName(),
"aria-orientation": this.props.vertical ? "vertical" : void 0,
items: this.items,
animate: this.animate,
isMenuVertical: this.props.vertical,
isDirectionRightToLeft: this.directionHolder.getIsDirectionRightToLeft(),
focusedItemId: this.state.focusedItemId,
lastItemIdToBeOpened: s,
tabbableItemId: this.state.tabbableItemId,
itemRender: this.props.itemRender,
linkRender: this.props.linkRender,
menuGuid: this.menuItemId,
onMouseLeave: this.onItemMouseLeave,
onMouseOver: this.onItemMouseOver,
onMouseDown: this.onItemMouseDown,
onFocus: this.onItemFocus,
onClick: this.onItemClick,
onBlur: this.onItemBlur,
onOriginalItemNeeded: this.getInputItem,
onPopupClose: this.onPopupClose,
role: this.props.role
}
)
);
}
/**
* @hidden
*/
componentDidMount() {
this.setState({ isFirstRender: !1 });
}
/**
* @hidden
*/
componentDidUpdate(s) {
(!!s.vertical != !!this.props.vertical || this.directionHolder.hasDirectionChanged()) && this.reset(), this.mouseOverHandler.OpenOnClick = this.props.openOnClick;
}
/**
* @hidden
*/
componentWillUnmount() {
this.clearItemHoverAndLeaveRequestsIfApplicable();
}
setFocusedItemId(s) {
this.setState((e) => {
const t = s === r ? e.tabbableItemId : R(s);
return { hoveredItemId: s === r || l(e.hoveredItemId) && l(s) ? e.hoveredItemId : r, focusedItemId: s, tabbableItemId: t };
});
}
setHoveredItemId(s) {
this.setState((e) => l(s) && l(e.focusedItemId) ? {
hoveredItemId: s,
focusedItemId: e.focusedItemId,
tabbableItemId: e.tabbableItemId
} : { hoveredItemId: s, focusedItemId: r, tabbableItemId: f });
}
getMenuClassName() {
return g(
"k-reset",
"k-header",
"k-menu",
{ "k-menu-horizontal": !this.props.vertical },
{ "k-menu-vertical": this.props.vertical },
this.props.className
);
}
clearItemHoverAndLeaveRequestsIfApplicable() {
this.itemHoverRequest && (clearTimeout(this.itemHoverRequest), this.itemHoverRequest = null), this.itemLeaveRequest && (clearTimeout(this.itemLeaveRequest), this.itemLeaveRequest = null);
}
isItemWithDefaultClose(s) {
return !this.props.customCloseItemIds || this.props.customCloseItemIds.indexOf(s) === -1;
}
checkIsDirectionRightToLeft() {
return !!(this.props.dir !== void 0 ? this.props.dir === "rtl" : this.menuWrapperEl && getComputedStyle(this.menuWrapperEl).direction === "rtl");
}
prepareItems() {
const { items: s, inputItems: e } = D(this.props.items, this.props.children);
this.items = s, this.inputItems = e;
}
dispatchSelectEventIfWired(s, e) {
b(this.props.onSelect, s, this, { item: this.getInputItem(e), itemId: e });
}
};
n.propTypes = {
vertical: i.bool,
items: i.arrayOf(i.object),
style: i.object,
animate: i.oneOfType([
i.bool,
i.shape({
openDuration: i.number,
closeDuration: i.number
})
]),
dir: i.string,
hoverOpenDelay: i.number,
hoverCloseDelay: i.number,
openOnClick: i.bool,
itemRender: i.any,
linkRender: i.any,
customCloseItemIds: i.arrayOf(i.string),
onSelect: i.func,
onClose: i.func,
role: i.string
}, n.defaultProps = { vertical: !1, animate: !0 };
let c = n;
export {
c as Menu
};