-
Notifications
You must be signed in to change notification settings - Fork 2
/
GlobalContextMenu.tsx
110 lines (98 loc) · 3.57 KB
/
GlobalContextMenu.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/*---------------------------------------------------------------------------------------------
* 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 * as ReactDOM from "react-dom";
import type { ContextMenuProps } from "./ContextMenu";
import { ContextMenu } from "./ContextMenu";
/** Properties for the [[GlobalContextMenu]] component
* @public
*/
export interface GlobalContextMenuProps extends ContextMenuProps {
/** Unique identifier, to distinguish from other GlobalContextMenu components. Needed only if multiple GlobalContextMenus are used simultaneously. */
identifier?: string;
/** Specifies the x/horizontal position on the viewport. */
x: number | string;
/** Specifies the y/vertical position on the viewport. */
y: number | string;
/** Context menu element. Default: ContextMenu */
contextMenuComponent?: React.ComponentType<ContextMenuProps>;
}
/** @internal */
interface GlobalContextMenuState {
parentDocument: Document | null;
}
/** GlobalContextMenu React component used to display a [[ContextMenu]] at the cursor
* @public
*/
export class GlobalContextMenu extends React.PureComponent<
GlobalContextMenuProps,
GlobalContextMenuState
> {
private _container?: HTMLDivElement;
public override readonly state: GlobalContextMenuState = {
parentDocument: null,
};
constructor(props: GlobalContextMenuProps) {
super(props);
}
public override componentWillUnmount() {
// istanbul ignore else
if (this._container && this._container.parentElement) {
// cleanup
this._container.parentElement.removeChild(this._container);
}
}
private _handleRefSet = (popupDiv: HTMLElement | null) => {
const parentDocument = popupDiv?.ownerDocument ?? null;
if (parentDocument) {
this._container = parentDocument.createElement("div");
this._container.id =
this.props.identifier !== undefined
? `dialog-${this.props.identifier}`
: "core-global-context-menu";
let rt = parentDocument.getElementById(
"core-global-context-menu-root"
) as HTMLDivElement;
if (!rt) {
rt = parentDocument.createElement("div");
rt.id = "core-global-context-menu-root";
// istanbul ignore next
(
parentDocument.body.querySelector(
'[data-root-container="appui-root-id"]'
) ?? parentDocument.body
).appendChild(rt);
}
rt.appendChild(this._container);
// used to support component rendering in pop-out window
this.setState({ parentDocument });
}
};
public override render(): React.ReactNode {
const { x, y, identifier, contextMenuComponent, ...props } = this.props;
const positioningStyle: React.CSSProperties = {
left: x,
top: y,
};
const CtxMenu = contextMenuComponent || ContextMenu;
return (
<div ref={this._handleRefSet} style={{ display: "none" }}>
{this.state.parentDocument &&
ReactDOM.createPortal(
<div className="core-context-menu-global" style={positioningStyle}>
<CtxMenu {...props} />
</div>,
// istanbul ignore next
this.state.parentDocument.body.querySelector(
'[data-root-container="appui-root-id"]'
) ?? this.state.parentDocument.body
)}
</div>
);
}
}