import * as loader from "tc-loader"; import {Stage} from "tc-loader"; import { closeContextMenu, ContextMenuEntry, ContextMenuEntryNormal, ContextMenuFactory, MenuEntryLabel, } from "tc-shared/ui/ContextMenu"; import * as React from "react"; import * as ReactDOM from "react-dom"; import {IconRenderer, RemoteIconRenderer} from "tc-shared/ui/react-elements/Icon"; import {useContext} from "react"; const cssStyle = require("./ReactRenderer.scss"); const CloseCallback = React.createContext<() => void>(undefined); let globalMouseListener; let globalContainer: HTMLDivElement; let refRenderer = React.createRef(); const MenuEntryIconRenderer = (props: { entry: ContextMenuEntryNormal }) => { if(!props.entry.icon || typeof props.entry.icon === "string") { return ; } else { return ; } }; const MenuLabelRenderer = (props: { label: MenuEntryLabel }) => { let text; let classes = []; if(typeof props.label === "string") { text = props.label; } else { text = props.label.text; if(props.label.bold) { classes.push(cssStyle.bold); } } classes.push(cssStyle.label); return
{text}
; } const MenuEntryRenderer = (props: { entry: ContextMenuEntry }) => { const closeCallback = useContext(CloseCallback); const clickListener = () => { try { if("click" in props.entry && typeof props.entry.click === "function") { props.entry.click(); } } finally { closeCallback(); } }; if(typeof props.entry.visible === "boolean" && !props.entry.visible) { return null; } switch (props.entry.type) { case "separator": return
; case "checkbox": return (
); case "normal": return (
{!props.entry.subMenu?.length ? undefined :
}
); } return null; } const MenuRenderer = (props: { entries: ContextMenuEntry[], subMenu: boolean }) => { return (
{props.entries.map(entry => )}
) }; class ContextMenuRenderer extends React.Component<{}, { entries: ContextMenuEntry[], pageX: number, pageY: number, callbackClose: () => void }> { constructor(props) { super(props); this.state = { pageY: 0, pageX: 0, entries: [], callbackClose: () => {} } } render() { return ( { if(this.state.callbackClose) { this.state.callbackClose(); } this.setState({ entries: [], callbackClose: undefined }); }}>
) } } let uniqueIdIndex = 0; function generateUniqueIds(entry: ContextMenuEntry) { if(typeof entry.uniqueId !== "string") { entry.uniqueId = "_" + (++uniqueIdIndex); } if(entry.type === "normal" && entry.subMenu) { entry.subMenu.forEach(generateUniqueIds); } } export let reactContextMenuInstance: ContextMenuFactory; loader.register_task(Stage.JAVASCRIPT_INITIALIZING, { priority: 80, name: "context menu init", function: async () => { document.addEventListener("mousedown", globalMouseListener = event => { if(refRenderer.current?.state.entries?.length) { let target: HTMLElement = event.target as any; while (target) { if(target.classList.contains(cssStyle.container)) { return; } target = target.parentElement; } closeContextMenu(); } }); globalContainer = document.createElement("div"); globalContainer.classList.add(cssStyle.globalContainer); document.body.append(globalContainer); ReactDOM.render(, globalContainer); reactContextMenuInstance = new class implements ContextMenuFactory { spawnContextMenu(position: { pageX: number; pageY: number }, entries: ContextMenuEntry[], closeCallback: () => void) { entries.forEach(generateUniqueIds); refRenderer.current?.setState({ entries: entries, pageX: position.pageX, pageY: position.pageY, callbackClose: closeCallback }); } closeContextMenu() { if(refRenderer.current?.state.entries?.length) { const callback = refRenderer.current?.state.callbackClose; if(callback) { callback(); } refRenderer.current?.setState({ callbackClose: undefined, entries: [] }); } } }; } })