UNPKG

@itwin/core-react

Version:

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

184 lines 7.57 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 RadialMenu */ import "./RadialMenu.scss"; import classnames from "classnames"; import * as React from "react"; import { Key } from "ts-key-enum"; import { Icon } from "../icons/IconComponent.js"; import { Point } from "../utils/Point.js"; import { AnnularSector, Annulus } from "./Annulus.js"; /** A context menu arranged in a radial layout. * @public * @deprecated in 4.14.0. Use {@link ContextMenu} or {@link https://itwinui.bentley.com/docs/dropdownmenu iTwinUI dropdown menu} instead. */ export class RadialMenu extends React.Component { _root = null; _selectedButton = null; static defaultProps = { labelRotate: false, selected: -1, }; state = { sectors: [], }; constructor(props) { super(props); } render() { const width = 2 * (this.props.outerRadius + 1); let x = this.props.left, y = this.props.top; if (this.props.left && this.props.top && typeof this.props.left === "number" && typeof this.props.top === "number") { x = this.props.left; y = this.props.top; if (x < 0) x = 0; if (x > window.innerWidth - width) x = window.innerWidth - width; if (y < 0) y = 0; if (y > window.innerHeight - width) y = window.innerHeight - width; } const divStyle = { left: x, top: y, ...this.props.style, }; return (React.createElement("div", { ref: (el) => { this._root = el; }, className: classnames("core-radial-menu", { opened: this.props.opened }, this.props.className), style: divStyle, "data-testid": "core-radial-menu" }, React.createElement("svg", { xmlns: "http://w3.org/2000/svg", version: "1.1", width: width, height: width, className: "core-radial-menu-container" }, React.Children.map(this.props.children, (child, index) => { if (!child || typeof child !== "object" || !("props" in child)) return child; const childElement = child; return React.cloneElement(childElement, { key: index, ref: (el) => { if (this.props.selected === index) this._selectedButton = el; }, selected: index === this.props.selected, labelRotate: childElement.props.labelRotate || this.props.labelRotate, annularSector: this.state.sectors[index], }); })))); } componentDidMount() { this._generateAnnularSectors(); setTimeout(() => { window.addEventListener("keyup", this._handleKeyUp); window.addEventListener("mouseup", this._handleClick); }); } componentWillUnmount() { window.removeEventListener("keyup", this._handleKeyUp); window.removeEventListener("mouseup", this._handleClick); } /** @internal */ componentDidUpdate(prevProps) { if (prevProps.innerRadius !== this.props.innerRadius || prevProps.outerRadius !== this.props.outerRadius) { this._generateAnnularSectors(); } } _handleKeyUp = (event) => { if (event.key === Key.Escape.valueOf() && this.props.onEsc) this.props.onEsc(event); }; _handleClick = (event) => { if (event.target instanceof HTMLElement && this._root && !event.target.contains(this._root) && this.props.onBlur) this.props.onBlur(event); }; /** Manually call onSelect of highlighted button. */ select = () => { if (this._selectedButton) this._selectedButton.select(); }; _generateAnnularSectors = () => { const n = React.Children.count(this.props.children); const angle = (2 * Math.PI) / n; const outer = this.props.outerRadius; const inner = this.props.innerRadius; const offset = -Math.PI / 8; const annulus = new Annulus(new Point(outer + 1, outer + 1), inner + 1, outer - 1); const sectors = []; for (let i = 0; i < n; i++) { sectors.push(new AnnularSector(annulus, angle * i + offset, angle * (i + 1) + offset)); } this.setState({ sectors }); }; } /** Button for use within a [[RadialMenu]] * @public * @deprecated in 4.14.0. Component used in a deprecated component {@link RadialMenu}. */ export class RadialButton extends React.Component { state = { hover: this.props.selected || false, }; constructor(props) { super(props); } render() { const sector = this.props.annularSector; let p = new Point(); let size = 0; let t = ""; let path = ""; if (sector) { size = sector.start.p1.getDistanceTo(sector.end.p2) * 2; path = sector.path; const parent = sector.parent; const { x: cx, y: cy } = parent.center; const r = (parent.inner.radius + parent.outer.radius) / 2; const angle = (sector.startAngle + sector.endAngle) / 2; p = new Point(cx + r * Math.cos(angle), cy + r * Math.sin(angle)); if (this.props.labelRotate) { let a = (angle * 180) / Math.PI + 90; while (a > 180) a -= 360; while (a < -180) a += 360; if (a > 90) a -= 180; if (a < -90) a += 180; t = `rotate(${a} ${p.x}, ${p.y})`; } } return (React.createElement("g", { onMouseOver: this._handleMouseOver, onMouseOut: this._handleMouseOut, onClick: this._handleClick }, React.createElement("path", { className: classnames("core-radial-menu-sector", { selected: this.state.hover }, this.props.className), style: this.props.style, d: path }), React.createElement("foreignObject", { transform: t, x: p.x - size / 2, y: p.y - 16, width: size, height: size, className: "core-radial-menu-button-svg" }, React.createElement("div", { xmlns: "http://www.w3.org/1999/xhtml", className: "core-radial-menu-button-container" }, React.createElement("div", { className: "core-radial-menu-button-icon" }, React.createElement(Icon, { iconSpec: this.props.icon })), React.createElement("div", { className: "core-radial-menu-button-content" }, this.props.children))))); } /** Manually call this.props.onSelect */ select = () => { if (this.props.onSelect) this.props.onSelect(undefined); }; _handleClick = (event) => { if (this.props.onSelect) this.props.onSelect(event); }; _handleMouseOver = (_event) => { this.setState({ hover: true }); }; _handleMouseOut = (_event) => { this.setState({ hover: false }); }; } //# sourceMappingURL=RadialMenu.js.map