* feat: support arrow with text * render arrow -> clear rect-> render text * move bound text when linear elements move * fix centering cursor when linear element rotated * fix y coord when new line added and container has 3 points * update text position when 2nd point moved * support adding label on top of 2nd point when 3 points are present * change linear element editor shortcut to cmd+enter and fix tests * scale bound text points when resizing via bounding box * ohh yeah rotation works :) * fix coords when updating text properties * calculate new position after rotation always from original position * rotate the bound text by same angle as parent * don't rotate text and make sure dimensions and coords are always calculated from original point * hardcoding the text width for now * Move the linear element when bound text hit * Rotation working yaay * consider text element angle when editing * refactor * update x2 coords if needed when text updated * simplify * consider bound text to be part of bounding box when hit * show bounding box correctly when multiple element selected * fix typo * support rotating multiple elements * support multiple element resizing * shift bound text to mid point when odd points * Always render linear element handles inside editor after element rendered so point is visible for bound text * Delete bound text when point attached to it deleted * move bound to mid segement mid point when points are even * shift bound text when points nearby deleted and handle segment deletion * Resize working :) * more resize fixes * don't update cache-its breaking delete points, look for better soln * update mid point cache for bound elements when updated * introduce wrapping when resizing * wrap when resize for 2 pointer linear elements * support adding text for linear elements with more than 3 points * export to svg working :) * clip from nearest enclosing element with non transparent color if present when exporting and fill with correct color in canvas * fix snap * use visible elements * Make export to svg work with Mask :) * remove id * mask canvas linear element area where label is added * decide the position of bound text during render * fix coords when editing * fix multiple resize * update cache when bound text version changes * fix masking when rotated * render text in correct position in preview * remove unnecessary code * fix masking when rotating linear element * fix masking with zoom * fix mask in preview for export * fix offsets in export view * fix coords on svg export * fix mask when element rotated in svg * enable double-click to enter text * fix hint * Position cursor correctly and text dimensiosn when height of element is negative * don't allow 2 pointer linear element with bound text width to go beyond min width * code cleanup * fix freedraw * Add padding * don't show vertical align action for linear element containers * Add specs for getBoundTextElementPosition * more specs * move some utils to linearElementEditor.ts * remove only :p * check absoulte coods in test * Add test to hide vertical align for linear eleemnt with bound text * improve export preview * support labels only for arrows * spec * fix large texts * fix tests * fix zooming * enter line editor with cmd+double click * Allow points to move beyond min width/height for 2 pointer arrow with bound text * fix hint for line editing * attempt to fix arrow getting deselected * fix hint and shortcut * Add padding of 5px when creating bound text and add spec * Wrap bound text when arrow binding containers moved * Add spec * remove * set boundTextElementVersion to null if not present * dont use cache when version mismatch * Add a padding of 5px vertically when creating text * Add box sizing content box * Set bound elements when text element created to fix the padding * fix zooming in editor * fix zoom in export * remove globalCompositeOperation and use clearRect instead of fillRect
419 lines
13 KiB
TypeScript
419 lines
13 KiB
TypeScript
import React from "react";
|
|
import { t } from "../i18n";
|
|
import { isDarwin, isWindows, KEYS } from "../keys";
|
|
import { Dialog } from "./Dialog";
|
|
import { getShortcutKey } from "../utils";
|
|
import "./HelpDialog.scss";
|
|
import { ExternalLinkIcon } from "./icons";
|
|
|
|
const Header = () => (
|
|
<div className="HelpDialog__header">
|
|
<a
|
|
className="HelpDialog__btn"
|
|
href="https://github.com/excalidraw/excalidraw#documentation"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
>
|
|
{t("helpDialog.documentation")}
|
|
<div className="HelpDialog__link-icon">{ExternalLinkIcon}</div>
|
|
</a>
|
|
<a
|
|
className="HelpDialog__btn"
|
|
href="https://blog.excalidraw.com"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
>
|
|
{t("helpDialog.blog")}
|
|
<div className="HelpDialog__link-icon">{ExternalLinkIcon}</div>
|
|
</a>
|
|
<a
|
|
className="HelpDialog__btn"
|
|
href="https://github.com/excalidraw/excalidraw/issues"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
>
|
|
{t("helpDialog.github")}
|
|
<div className="HelpDialog__link-icon">{ExternalLinkIcon}</div>
|
|
</a>
|
|
</div>
|
|
);
|
|
|
|
const Section = (props: { title: string; children: React.ReactNode }) => (
|
|
<>
|
|
<h3>{props.title}</h3>
|
|
<div className="HelpDialog__islands-container">{props.children}</div>
|
|
</>
|
|
);
|
|
|
|
const ShortcutIsland = (props: {
|
|
caption: string;
|
|
children: React.ReactNode;
|
|
className?: string;
|
|
}) => (
|
|
<div className={`HelpDialog__island ${props.className}`}>
|
|
<h4 className="HelpDialog__island-title">{props.caption}</h4>
|
|
<div className="HelpDialog__island-content">{props.children}</div>
|
|
</div>
|
|
);
|
|
|
|
function* intersperse(as: JSX.Element[][], delim: string | null) {
|
|
let first = true;
|
|
for (const x of as) {
|
|
if (!first) {
|
|
yield delim;
|
|
}
|
|
first = false;
|
|
yield x;
|
|
}
|
|
}
|
|
|
|
const Shortcut = ({
|
|
label,
|
|
shortcuts,
|
|
isOr = true,
|
|
}: {
|
|
label: string;
|
|
shortcuts: string[];
|
|
isOr?: boolean;
|
|
}) => {
|
|
const splitShortcutKeys = shortcuts.map((shortcut) => {
|
|
const keys = shortcut.endsWith("++")
|
|
? [...shortcut.slice(0, -2).split("+"), "+"]
|
|
: shortcut.split("+");
|
|
|
|
return keys.map((key) => <ShortcutKey key={key}>{key}</ShortcutKey>);
|
|
});
|
|
|
|
return (
|
|
<div className="HelpDialog__shortcut">
|
|
<div>{label}</div>
|
|
<div className="HelpDialog__key-container">
|
|
{[...intersperse(splitShortcutKeys, isOr ? t("helpDialog.or") : null)]}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const ShortcutKey = (props: { children: React.ReactNode }) => (
|
|
<kbd className="HelpDialog__key" {...props} />
|
|
);
|
|
|
|
export const HelpDialog = ({ onClose }: { onClose?: () => void }) => {
|
|
const handleClose = React.useCallback(() => {
|
|
if (onClose) {
|
|
onClose();
|
|
}
|
|
}, [onClose]);
|
|
|
|
return (
|
|
<>
|
|
<Dialog
|
|
onCloseRequest={handleClose}
|
|
title={t("helpDialog.title")}
|
|
className={"HelpDialog"}
|
|
>
|
|
<Header />
|
|
<Section title={t("helpDialog.shortcuts")}>
|
|
<ShortcutIsland
|
|
className="HelpDialog__island--tools"
|
|
caption={t("helpDialog.tools")}
|
|
>
|
|
<Shortcut
|
|
label={t("toolBar.selection")}
|
|
shortcuts={[KEYS.V, KEYS["1"]]}
|
|
/>
|
|
<Shortcut
|
|
label={t("toolBar.rectangle")}
|
|
shortcuts={[KEYS.R, KEYS["2"]]}
|
|
/>
|
|
<Shortcut
|
|
label={t("toolBar.diamond")}
|
|
shortcuts={[KEYS.D, KEYS["3"]]}
|
|
/>
|
|
<Shortcut
|
|
label={t("toolBar.ellipse")}
|
|
shortcuts={[KEYS.O, KEYS["4"]]}
|
|
/>
|
|
<Shortcut
|
|
label={t("toolBar.arrow")}
|
|
shortcuts={[KEYS.A, KEYS["5"]]}
|
|
/>
|
|
<Shortcut
|
|
label={t("toolBar.line")}
|
|
shortcuts={[KEYS.P, KEYS["6"]]}
|
|
/>
|
|
<Shortcut
|
|
label={t("toolBar.freedraw")}
|
|
shortcuts={["Shift + P", KEYS["7"]]}
|
|
/>
|
|
<Shortcut
|
|
label={t("toolBar.text")}
|
|
shortcuts={[KEYS.T, KEYS["8"]]}
|
|
/>
|
|
<Shortcut label={t("toolBar.image")} shortcuts={[KEYS["9"]]} />
|
|
<Shortcut
|
|
label={t("toolBar.eraser")}
|
|
shortcuts={[KEYS.E, KEYS["0"]]}
|
|
/>
|
|
<Shortcut
|
|
label={t("helpDialog.editSelectedShape")}
|
|
shortcuts={[
|
|
getShortcutKey("CtrlOrCmd+Enter"),
|
|
getShortcutKey(`CtrlOrCmd + ${t("helpDialog.doubleClick")}`),
|
|
]}
|
|
/>
|
|
<Shortcut
|
|
label={t("helpDialog.textNewLine")}
|
|
shortcuts={[
|
|
getShortcutKey("Enter"),
|
|
getShortcutKey("Shift+Enter"),
|
|
]}
|
|
/>
|
|
<Shortcut
|
|
label={t("helpDialog.textFinish")}
|
|
shortcuts={[
|
|
getShortcutKey("Esc"),
|
|
getShortcutKey("CtrlOrCmd+Enter"),
|
|
]}
|
|
/>
|
|
<Shortcut
|
|
label={t("helpDialog.curvedArrow")}
|
|
shortcuts={[
|
|
"A",
|
|
t("helpDialog.click"),
|
|
t("helpDialog.click"),
|
|
t("helpDialog.click"),
|
|
]}
|
|
isOr={false}
|
|
/>
|
|
<Shortcut
|
|
label={t("helpDialog.curvedLine")}
|
|
shortcuts={[
|
|
"L",
|
|
t("helpDialog.click"),
|
|
t("helpDialog.click"),
|
|
t("helpDialog.click"),
|
|
]}
|
|
isOr={false}
|
|
/>
|
|
<Shortcut label={t("toolBar.lock")} shortcuts={[KEYS.Q]} />
|
|
<Shortcut
|
|
label={t("helpDialog.preventBinding")}
|
|
shortcuts={[getShortcutKey("CtrlOrCmd")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("toolBar.link")}
|
|
shortcuts={[getShortcutKey("CtrlOrCmd+K")]}
|
|
/>
|
|
</ShortcutIsland>
|
|
<ShortcutIsland
|
|
className="HelpDialog__island--view"
|
|
caption={t("helpDialog.view")}
|
|
>
|
|
<Shortcut
|
|
label={t("buttons.zoomIn")}
|
|
shortcuts={[getShortcutKey("CtrlOrCmd++")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("buttons.zoomOut")}
|
|
shortcuts={[getShortcutKey("CtrlOrCmd+-")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("buttons.resetZoom")}
|
|
shortcuts={[getShortcutKey("CtrlOrCmd+0")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("helpDialog.zoomToFit")}
|
|
shortcuts={["Shift+1"]}
|
|
/>
|
|
<Shortcut
|
|
label={t("helpDialog.zoomToSelection")}
|
|
shortcuts={["Shift+2"]}
|
|
/>
|
|
<Shortcut label={t("buttons.fullScreen")} shortcuts={["F"]} />
|
|
<Shortcut
|
|
label={t("buttons.zenMode")}
|
|
shortcuts={[getShortcutKey("Alt+Z")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.showGrid")}
|
|
shortcuts={[getShortcutKey("CtrlOrCmd+'")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.viewMode")}
|
|
shortcuts={[getShortcutKey("Alt+R")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.toggleTheme")}
|
|
shortcuts={[getShortcutKey("Alt+Shift+D")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("stats.title")}
|
|
shortcuts={[getShortcutKey("Alt+/")]}
|
|
/>
|
|
</ShortcutIsland>
|
|
<ShortcutIsland
|
|
className="HelpDialog__island--editor"
|
|
caption={t("helpDialog.editor")}
|
|
>
|
|
<Shortcut
|
|
label={t("labels.selectAll")}
|
|
shortcuts={[getShortcutKey("CtrlOrCmd+A")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.multiSelect")}
|
|
shortcuts={[getShortcutKey(`Shift+${t("helpDialog.click")}`)]}
|
|
/>
|
|
<Shortcut
|
|
label={t("helpDialog.deepSelect")}
|
|
shortcuts={[getShortcutKey(`CtrlOrCmd+${t("helpDialog.click")}`)]}
|
|
/>
|
|
<Shortcut
|
|
label={t("helpDialog.deepBoxSelect")}
|
|
shortcuts={[getShortcutKey(`CtrlOrCmd+${t("helpDialog.drag")}`)]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.moveCanvas")}
|
|
shortcuts={[
|
|
getShortcutKey(`Space+${t("helpDialog.drag")}`),
|
|
getShortcutKey(`Wheel+${t("helpDialog.drag")}`),
|
|
]}
|
|
isOr={true}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.cut")}
|
|
shortcuts={[getShortcutKey("CtrlOrCmd+X")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.copy")}
|
|
shortcuts={[getShortcutKey("CtrlOrCmd+C")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.paste")}
|
|
shortcuts={[getShortcutKey("CtrlOrCmd+V")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.pasteAsPlaintext")}
|
|
shortcuts={[getShortcutKey("CtrlOrCmd+Shift+V")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.copyAsPng")}
|
|
shortcuts={[getShortcutKey("Shift+Alt+C")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.copyStyles")}
|
|
shortcuts={[getShortcutKey("CtrlOrCmd+Alt+C")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.pasteStyles")}
|
|
shortcuts={[getShortcutKey("CtrlOrCmd+Alt+V")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.delete")}
|
|
shortcuts={[getShortcutKey("Delete")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.sendToBack")}
|
|
shortcuts={[
|
|
isDarwin
|
|
? getShortcutKey("CtrlOrCmd+Alt+[")
|
|
: getShortcutKey("CtrlOrCmd+Shift+["),
|
|
]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.bringToFront")}
|
|
shortcuts={[
|
|
isDarwin
|
|
? getShortcutKey("CtrlOrCmd+Alt+]")
|
|
: getShortcutKey("CtrlOrCmd+Shift+]"),
|
|
]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.sendBackward")}
|
|
shortcuts={[getShortcutKey("CtrlOrCmd+[")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.bringForward")}
|
|
shortcuts={[getShortcutKey("CtrlOrCmd+]")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.alignTop")}
|
|
shortcuts={[getShortcutKey("CtrlOrCmd+Shift+Up")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.alignBottom")}
|
|
shortcuts={[getShortcutKey("CtrlOrCmd+Shift+Down")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.alignLeft")}
|
|
shortcuts={[getShortcutKey("CtrlOrCmd+Shift+Left")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.alignRight")}
|
|
shortcuts={[getShortcutKey("CtrlOrCmd+Shift+Right")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.duplicateSelection")}
|
|
shortcuts={[
|
|
getShortcutKey("CtrlOrCmd+D"),
|
|
getShortcutKey(`Alt+${t("helpDialog.drag")}`),
|
|
]}
|
|
/>
|
|
<Shortcut
|
|
label={t("helpDialog.toggleElementLock")}
|
|
shortcuts={[getShortcutKey("CtrlOrCmd+Shift+L")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("buttons.undo")}
|
|
shortcuts={[getShortcutKey("CtrlOrCmd+Z")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("buttons.redo")}
|
|
shortcuts={
|
|
isWindows
|
|
? [
|
|
getShortcutKey("CtrlOrCmd+Y"),
|
|
getShortcutKey("CtrlOrCmd+Shift+Z"),
|
|
]
|
|
: [getShortcutKey("CtrlOrCmd+Shift+Z")]
|
|
}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.group")}
|
|
shortcuts={[getShortcutKey("CtrlOrCmd+G")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.ungroup")}
|
|
shortcuts={[getShortcutKey("CtrlOrCmd+Shift+G")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.flipHorizontal")}
|
|
shortcuts={[getShortcutKey("Shift+H")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.flipVertical")}
|
|
shortcuts={[getShortcutKey("Shift+V")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.showStroke")}
|
|
shortcuts={[getShortcutKey("S")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.showBackground")}
|
|
shortcuts={[getShortcutKey("G")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.decreaseFontSize")}
|
|
shortcuts={[getShortcutKey("CtrlOrCmd+Shift+<")]}
|
|
/>
|
|
<Shortcut
|
|
label={t("labels.increaseFontSize")}
|
|
shortcuts={[getShortcutKey("CtrlOrCmd+Shift+>")]}
|
|
/>
|
|
</ShortcutIsland>
|
|
</Section>
|
|
</Dialog>
|
|
</>
|
|
);
|
|
};
|