webamp/js/components/ContextMenu.tsx
2018-10-11 22:15:43 -07:00

132 lines
2.7 KiB
TypeScript

import invariant from "invariant";
import React from "react";
import { createPortal } from "react-dom";
import { connect } from "react-redux";
import classnames from "classnames";
import "../../css/context-menu.css";
import { AppState } from "../types";
interface PortalProps {
zIndex: number;
top: number;
left: number;
}
class Portal extends React.Component<PortalProps> {
_node?: HTMLDivElement;
componentWillMount() {
this._node = document.createElement("div");
this._node.id = "webamp-context-menu";
this._node.style.position = "absolute";
this._node.style.top = "0";
this._node.style.left = "0";
this._node.style.zIndex = String(this.props.zIndex + 1);
document.body.appendChild(this._node);
}
componentWillUnmount() {
if (this._node) {
document.body.removeChild(this._node);
}
}
render() {
const style: React.CSSProperties = {
top: this.props.top,
left: this.props.left,
position: "absolute"
};
return createPortal(
<div style={style}>{this.props.children}</div>,
this._node!
);
}
}
export const Hr = () => (
<li className="hr">
<hr />
</li>
);
interface ParentProps {
label: string;
children: React.ReactNode;
}
export const Parent = ({ children, label }: ParentProps) => (
<li className="parent">
<ul>{children}</ul>
{label}
</li>
);
interface LinkNodeProps {
label: string;
href: string;
}
export const LinkNode = (props: LinkNodeProps) => (
<li>
<a {...props}>{props.label}</a>
</li>
);
interface NodeProps {
label: string;
checked?: boolean;
hotkey?: string;
className?: string;
// TODO: Figure out how to do passthrough props
onClick?: () => void;
}
export const Node = (props: NodeProps) => {
const { label, checked, className = "", ...passThroughProps } = props;
return (
<li className={classnames(className, { checked })} {...passThroughProps}>
{label}
</li>
);
};
interface ContextMenuProps {
children: React.ReactNode;
offsetTop: number;
offsetLeft: number;
top?: boolean;
bottom?: boolean;
selected: boolean;
zIndex: number;
}
class ContextMenu extends React.Component<ContextMenuProps> {
render() {
const {
children,
offsetTop,
offsetLeft,
top,
bottom,
selected,
zIndex
} = this.props;
return (
selected && (
<Portal top={offsetTop} left={offsetLeft} zIndex={zIndex}>
<ul className={classnames("context-menu", { top, bottom })}>
{children}
</ul>
</Portal>
)
);
}
}
const mapStateToProps = (state: AppState) => ({
zIndex: state.display.zIndex
});
export default connect(mapStateToProps)(ContextMenu);