UNPKG

@itwin/core-react

Version:

A react component library of iTwin.js UI general purpose components

177 lines 8.21 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module ContextMenu */ import * as React from "react"; import classnames from "classnames"; import { ConditionalBooleanValue } from "@itwin/appui-abstract"; import { ContextMenu } from "./ContextMenu.js"; import { ContextMenuDirection } from "./ContextMenuDirection.js"; import { TildeFinder } from "./TildeFinder.js"; import { Icon } from "../icons/IconComponent.js"; import { SvgCaretRightSmall } from "@itwin/itwinui-icons-react"; import { Badge } from "../badge/Badge.js"; /** Submenu wrapper class for use within a [[ContextMenu]] component. * @public * @deprecated in 4.16.0. Use `subMenuItems` property {@link https://itwinui.bentley.com/docs/dropdownmenu#submenu iTwinUI MenuItem} component instead. */ export class ContextSubMenu extends React.Component { _menuElement = null; _subMenuElement = null; _menuButtonElement = null; _lastLabel; _parsedLabel; static defaultProps = { direction: ContextMenuDirection.BottomRight, disabled: false, hidden: false, autoflip: true, isSelected: false, selectedIndex: 0, }; state; constructor(props) { super(props); this.state = { opened: false, direction: props.direction, }; } render() { const { label, opened, direction, onOutsideClick, onEsc, autoflip, edgeLimit, selectedIndex, floating, parentMenu, parentSubmenu, onSelect, icon, disabled, hidden, onHover, isSelected, onHotKeyParsed, children, onClick, className, badgeType, // eslint-disable-line @typescript-eslint/no-deprecated badgeKind, hideIconContainer, ...props } = this.props; const onOutsideClickWrapper = (event) => { this.close(); onOutsideClick && onOutsideClick(event); }; const contextMenuProps = { onOutsideClick: onOutsideClickWrapper, onSelect, onEsc, autoflip, edgeLimit, selectedIndex, floating, parentMenu, }; const renderDirection = this.state.direction; const isDisabled = ConditionalBooleanValue.getValue(disabled); const isHidden = ConditionalBooleanValue.getValue(hidden); if (this._lastLabel !== label) { this._parsedLabel = TildeFinder.findAfterTilde(label).node; this._lastLabel = label; } return (React.createElement("div", { className: classnames("core-context-submenu", ContextMenu.getCSSClassNameFromDirection(renderDirection), className), // eslint-disable-next-line jsx-a11y/mouse-events-have-key-events onMouseOver: this._handleMouseOver, ref: (el) => { this._subMenuElement = el; }, "data-testid": "core-context-submenu", ...props }, React.createElement("div", { onClick: this._handleClick, ref: (el) => { this._menuButtonElement = el; }, className: classnames("core-context-menu-item", "core-context-submenu-container", isDisabled && "core-context-menu-disabled", isHidden && "core-context-menu-hidden", isSelected && "core-context-menu-is-selected"), "data-testid": "core-context-submenu-container", role: "menuitem", tabIndex: isSelected ? 0 : -1, "aria-disabled": isDisabled, "aria-hidden": isHidden, "aria-haspopup": true }, !hideIconContainer && (React.createElement("div", { className: "core-context-menu-icon" }, icon !== undefined && React.createElement(Icon, { iconSpec: icon }))), React.createElement("div", { className: "core-context-menu-content" }, this._parsedLabel), React.createElement("div", { className: classnames("core-context-submenu-arrow", "icon") }, React.createElement(Icon, { iconSpec: React.createElement(SvgCaretRightSmall, null) })), (badgeKind || badgeType) && (React.createElement("div", { className: "core-context-menu-badge" }, React.createElement(Badge, { type: badgeKind || badgeType })))), React.createElement(ContextMenu, { ref: (el) => { this._menuElement = el; }, className: "core-context-submenu-popup", opened: this.state.opened, direction: renderDirection, parentSubmenu: this, ...contextMenuProps }, children))); } componentDidMount() { document.addEventListener("click", this._handleClickGlobal); this._updateHotkey(this.props.label); this.checkRenderDirection(); } componentWillUnmount() { document.removeEventListener("click", this._handleClickGlobal); } /** @internal */ componentDidUpdate(prevProps, prevState) { const direction = this.props.direction; if ((this.state.opened !== prevState.opened && direction !== this.state.direction) || prevProps.direction !== direction) this.checkRenderDirection(); if (this.props.label !== prevProps.label) { this._updateHotkey(this.props.label); } } getWindow() { const el = this._subMenuElement; const parentDocument = el.ownerDocument; return parentDocument.defaultView; } checkRenderDirection() { const { autoflip } = this.props; const parentWindow = this.getWindow(); let renderDirection = this.state.direction; if (parentWindow && autoflip && this._menuElement) { const menuRect = this._menuElement.getRect(); renderDirection = ContextMenu.autoFlip(renderDirection, menuRect, parentWindow.innerWidth, parentWindow.innerHeight); if (renderDirection !== this.state.direction) this.setState({ direction: renderDirection }); } } _updateHotkey = (node) => { let hotKey; const isDisabled = ConditionalBooleanValue.getValue(this.props.disabled); const isHidden = ConditionalBooleanValue.getValue(this.props.hidden); if (!isDisabled && !isHidden) hotKey = TildeFinder.findAfterTilde(node).character; else hotKey = undefined; if (hotKey && hotKey !== this.state.hotKey) { this.setState({ hotKey }); if (this.props.onHotKeyParsed) this.props.onHotKeyParsed(hotKey); } }; select = () => { this.setState({ opened: true }, () => { if (this._menuElement) this._menuElement.focus(); if (this.props.onSelect !== undefined) this.props.onSelect(undefined); }); }; close = (propagate) => { this.setState({ opened: false }, () => { if (this._menuElement) this._menuElement.blur(); }); if (propagate && this.props.parentMenu && this.props.parentMenu.props.parentSubmenu) { this.props.parentMenu.props.parentSubmenu.close(true); } }; _handleMouseOver = (_event) => { if (this._menuButtonElement && this._menuButtonElement.style.visibility !== "hidden" && this.props.onHover) { this.props.onHover(); } }; _handleClick = (event) => { event.stopPropagation(); const isDisabled = ConditionalBooleanValue.getValue(this.props.disabled); if (!isDisabled) { if (this.props.onClick !== undefined) this.props.onClick(event); if (this.props.opened) this.close(); else this.select(); } }; _handleClickGlobal = (event) => { if (this._subMenuElement && !this._subMenuElement.contains(event.target)) this.setState((_prevState) => ({ opened: false })); }; } //# sourceMappingURL=ContextSubMenu.js.map