mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
Context menu with some commands (#217)
This commit is contained in:
parent
9fe3fe5091
commit
257f697a98
6 changed files with 250 additions and 25 deletions
|
@ -1,5 +1,6 @@
|
|||
import React from "react";
|
||||
import { TwitterPicker } from "react-color";
|
||||
import { Popover } from "./Popover";
|
||||
|
||||
export function ColorPicker({
|
||||
color,
|
||||
|
@ -17,8 +18,7 @@ export function ColorPicker({
|
|||
onClick={() => setActive(!isActive)}
|
||||
/>
|
||||
{isActive ? (
|
||||
<div className="popover">
|
||||
<div className="cover" onClick={() => setActive(false)} />
|
||||
<Popover onCloseRequest={() => setActive(false)}>
|
||||
<TwitterPicker
|
||||
colors={[
|
||||
"#000000",
|
||||
|
@ -39,7 +39,7 @@ export function ColorPicker({
|
|||
onChange(changedColor.hex);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Popover>
|
||||
) : null}
|
||||
<input
|
||||
type="text"
|
||||
|
|
34
src/components/ContextMenu.css
Normal file
34
src/components/ContextMenu.css
Normal file
|
@ -0,0 +1,34 @@
|
|||
.context-menu {
|
||||
position: relative;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0px 3px 10px rgba(0, 0, 0, 0.2);
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.context-menu__option {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.context-menu-option {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.context-menu-option:focus {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.context-menu__option:first-child .context-menu-option {
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
|
||||
.context-menu__option:last-child .context-menu-option {
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
85
src/components/ContextMenu.tsx
Normal file
85
src/components/ContextMenu.tsx
Normal file
|
@ -0,0 +1,85 @@
|
|||
import React from "react";
|
||||
import { Popover } from "./Popover";
|
||||
import { render, unmountComponentAtNode } from "react-dom";
|
||||
|
||||
import "./ContextMenu.css";
|
||||
|
||||
type ContextMenuOption = {
|
||||
label: string;
|
||||
action(): void;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
options: ContextMenuOption[];
|
||||
onCloseRequest?(): void;
|
||||
top: number;
|
||||
left: number;
|
||||
};
|
||||
|
||||
function ContextMenu({ options, onCloseRequest, top, left }: Props) {
|
||||
return (
|
||||
<Popover onCloseRequest={onCloseRequest} top={top} left={left}>
|
||||
<ul className="context-menu" onContextMenu={e => e.preventDefault()}>
|
||||
{options.map((option, idx) => (
|
||||
<li
|
||||
key={idx}
|
||||
className="context-menu__option"
|
||||
onClick={onCloseRequest}
|
||||
>
|
||||
<ContextMenuOption {...option} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
function ContextMenuOption({ label, action }: ContextMenuOption) {
|
||||
return (
|
||||
<button className="context-menu-option" onClick={action}>
|
||||
{label}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
let contextMenuNode: HTMLDivElement;
|
||||
function getContextMenuNode(): HTMLDivElement {
|
||||
if (contextMenuNode) {
|
||||
return contextMenuNode;
|
||||
}
|
||||
const div = document.createElement("div");
|
||||
document.body.appendChild(div);
|
||||
return (contextMenuNode = div);
|
||||
}
|
||||
|
||||
type ContextMenuParams = {
|
||||
options: (ContextMenuOption | false | null | undefined)[];
|
||||
top: number;
|
||||
left: number;
|
||||
};
|
||||
|
||||
function handleClose() {
|
||||
unmountComponentAtNode(getContextMenuNode());
|
||||
}
|
||||
|
||||
export default {
|
||||
push(params: ContextMenuParams) {
|
||||
const options = Array.of<ContextMenuOption>();
|
||||
params.options.forEach(option => {
|
||||
if (option) {
|
||||
options.push(option);
|
||||
}
|
||||
});
|
||||
if (options.length) {
|
||||
render(
|
||||
<ContextMenu
|
||||
top={params.top}
|
||||
left={params.left}
|
||||
options={options}
|
||||
onCloseRequest={handleClose}
|
||||
/>,
|
||||
getContextMenuNode()
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
24
src/components/Popover.tsx
Normal file
24
src/components/Popover.tsx
Normal file
|
@ -0,0 +1,24 @@
|
|||
import React from "react";
|
||||
|
||||
type Props = {
|
||||
top?: number;
|
||||
left?: number;
|
||||
children?: React.ReactNode;
|
||||
onCloseRequest?(): void;
|
||||
};
|
||||
|
||||
export function Popover({ children, left, onCloseRequest, top }: Props) {
|
||||
return (
|
||||
<div className="popover" style={{ top: top, left: left }}>
|
||||
<div
|
||||
className="cover"
|
||||
onClick={onCloseRequest}
|
||||
onContextMenu={e => {
|
||||
e.preventDefault();
|
||||
if (onCloseRequest) onCloseRequest();
|
||||
}}
|
||||
/>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue