|
|
|
@ -77,6 +77,8 @@ import Stack from "./components/Stack";
|
|
|
|
|
import { FixedSideContainer } from "./components/FixedSideContainer";
|
|
|
|
|
import { ToolIcon } from "./components/ToolIcon";
|
|
|
|
|
import { ExportDialog } from "./components/ExportDialog";
|
|
|
|
|
import { withTranslation } from "react-i18next";
|
|
|
|
|
import "./i18n";
|
|
|
|
|
|
|
|
|
|
let { elements } = createScene();
|
|
|
|
|
const { history } = createHistory();
|
|
|
|
@ -129,7 +131,7 @@ export function viewportCoordsToSceneCoords(
|
|
|
|
|
return { x, y };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class App extends React.Component<{}, AppState> {
|
|
|
|
|
export class App extends React.Component<any, AppState> {
|
|
|
|
|
canvas: HTMLCanvasElement | null = null;
|
|
|
|
|
rc: RoughCanvas | null = null;
|
|
|
|
|
|
|
|
|
@ -359,6 +361,7 @@ export class App extends React.Component<{}, AppState> {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private renderSelectedShapeActions(elements: readonly ExcalidrawElement[]) {
|
|
|
|
|
const { t } = this.props;
|
|
|
|
|
const { elementType, editingElement } = this.state;
|
|
|
|
|
const selectedElements = elements.filter(el => el.isSelected);
|
|
|
|
|
const hasSelectedElements = selectedElements.length > 0;
|
|
|
|
@ -381,7 +384,8 @@ export class App extends React.Component<{}, AppState> {
|
|
|
|
|
"changeStrokeColor",
|
|
|
|
|
elements,
|
|
|
|
|
this.state,
|
|
|
|
|
this.syncActionResult
|
|
|
|
|
this.syncActionResult,
|
|
|
|
|
t
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{(hasBackground(elements) ||
|
|
|
|
@ -391,14 +395,16 @@ export class App extends React.Component<{}, AppState> {
|
|
|
|
|
"changeBackgroundColor",
|
|
|
|
|
elements,
|
|
|
|
|
this.state,
|
|
|
|
|
this.syncActionResult
|
|
|
|
|
this.syncActionResult,
|
|
|
|
|
t
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{this.actionManager.renderAction(
|
|
|
|
|
"changeFillStyle",
|
|
|
|
|
elements,
|
|
|
|
|
this.state,
|
|
|
|
|
this.syncActionResult
|
|
|
|
|
this.syncActionResult,
|
|
|
|
|
t
|
|
|
|
|
)}
|
|
|
|
|
<hr />
|
|
|
|
|
</>
|
|
|
|
@ -411,14 +417,16 @@ export class App extends React.Component<{}, AppState> {
|
|
|
|
|
"changeStrokeWidth",
|
|
|
|
|
elements,
|
|
|
|
|
this.state,
|
|
|
|
|
this.syncActionResult
|
|
|
|
|
this.syncActionResult,
|
|
|
|
|
t
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{this.actionManager.renderAction(
|
|
|
|
|
"changeSloppiness",
|
|
|
|
|
elements,
|
|
|
|
|
this.state,
|
|
|
|
|
this.syncActionResult
|
|
|
|
|
this.syncActionResult,
|
|
|
|
|
t
|
|
|
|
|
)}
|
|
|
|
|
<hr />
|
|
|
|
|
</>
|
|
|
|
@ -430,14 +438,16 @@ export class App extends React.Component<{}, AppState> {
|
|
|
|
|
"changeFontSize",
|
|
|
|
|
elements,
|
|
|
|
|
this.state,
|
|
|
|
|
this.syncActionResult
|
|
|
|
|
this.syncActionResult,
|
|
|
|
|
t
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{this.actionManager.renderAction(
|
|
|
|
|
"changeFontFamily",
|
|
|
|
|
elements,
|
|
|
|
|
this.state,
|
|
|
|
|
this.syncActionResult
|
|
|
|
|
this.syncActionResult,
|
|
|
|
|
t
|
|
|
|
|
)}
|
|
|
|
|
<hr />
|
|
|
|
|
</>
|
|
|
|
@ -447,14 +457,16 @@ export class App extends React.Component<{}, AppState> {
|
|
|
|
|
"changeOpacity",
|
|
|
|
|
elements,
|
|
|
|
|
this.state,
|
|
|
|
|
this.syncActionResult
|
|
|
|
|
this.syncActionResult,
|
|
|
|
|
t
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{this.actionManager.renderAction(
|
|
|
|
|
"deleteSelectedElements",
|
|
|
|
|
elements,
|
|
|
|
|
this.state,
|
|
|
|
|
this.syncActionResult
|
|
|
|
|
this.syncActionResult,
|
|
|
|
|
t
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</Island>
|
|
|
|
@ -462,32 +474,38 @@ export class App extends React.Component<{}, AppState> {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private renderShapesSwitcher() {
|
|
|
|
|
const { t } = this.props;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
{SHAPES.map(({ value, icon }, index) => (
|
|
|
|
|
<ToolIcon
|
|
|
|
|
key={value}
|
|
|
|
|
type="radio"
|
|
|
|
|
icon={icon}
|
|
|
|
|
checked={this.state.elementType === value}
|
|
|
|
|
name="editor-current-shape"
|
|
|
|
|
title={`${capitalizeString(value)} — ${
|
|
|
|
|
capitalizeString(value)[0]
|
|
|
|
|
}, ${index + 1}`}
|
|
|
|
|
onChange={() => {
|
|
|
|
|
this.setState({ elementType: value });
|
|
|
|
|
elements = clearSelection(elements);
|
|
|
|
|
document.documentElement.style.cursor =
|
|
|
|
|
value === "text" ? "text" : "crosshair";
|
|
|
|
|
this.forceUpdate();
|
|
|
|
|
}}
|
|
|
|
|
></ToolIcon>
|
|
|
|
|
))}
|
|
|
|
|
{SHAPES.map(({ value, icon }, index) => {
|
|
|
|
|
const label = t(`toolBar.${value}`);
|
|
|
|
|
return (
|
|
|
|
|
<ToolIcon
|
|
|
|
|
key={value}
|
|
|
|
|
type="radio"
|
|
|
|
|
icon={icon}
|
|
|
|
|
checked={this.state.elementType === value}
|
|
|
|
|
name="editor-current-shape"
|
|
|
|
|
title={`${capitalizeString(label)} — ${
|
|
|
|
|
capitalizeString(label)[0]
|
|
|
|
|
}, ${index + 1}`}
|
|
|
|
|
onChange={() => {
|
|
|
|
|
this.setState({ elementType: value });
|
|
|
|
|
elements = clearSelection(elements);
|
|
|
|
|
document.documentElement.style.cursor =
|
|
|
|
|
value === "text" ? "text" : "crosshair";
|
|
|
|
|
this.forceUpdate();
|
|
|
|
|
}}
|
|
|
|
|
></ToolIcon>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private renderCanvasActions() {
|
|
|
|
|
const { t } = this.props;
|
|
|
|
|
return (
|
|
|
|
|
<Stack.Col gap={4}>
|
|
|
|
|
<Stack.Row justifyContent={"space-between"}>
|
|
|
|
@ -495,13 +513,15 @@ export class App extends React.Component<{}, AppState> {
|
|
|
|
|
"loadScene",
|
|
|
|
|
elements,
|
|
|
|
|
this.state,
|
|
|
|
|
this.syncActionResult
|
|
|
|
|
this.syncActionResult,
|
|
|
|
|
t
|
|
|
|
|
)}
|
|
|
|
|
{this.actionManager.renderAction(
|
|
|
|
|
"saveScene",
|
|
|
|
|
elements,
|
|
|
|
|
this.state,
|
|
|
|
|
this.syncActionResult
|
|
|
|
|
this.syncActionResult,
|
|
|
|
|
t
|
|
|
|
|
)}
|
|
|
|
|
<ExportDialog
|
|
|
|
|
elements={elements}
|
|
|
|
@ -540,14 +560,16 @@ export class App extends React.Component<{}, AppState> {
|
|
|
|
|
"clearCanvas",
|
|
|
|
|
elements,
|
|
|
|
|
this.state,
|
|
|
|
|
this.syncActionResult
|
|
|
|
|
this.syncActionResult,
|
|
|
|
|
t
|
|
|
|
|
)}
|
|
|
|
|
</Stack.Row>
|
|
|
|
|
{this.actionManager.renderAction(
|
|
|
|
|
"changeViewBackgroundColor",
|
|
|
|
|
elements,
|
|
|
|
|
this.state,
|
|
|
|
|
this.syncActionResult
|
|
|
|
|
this.syncActionResult,
|
|
|
|
|
t
|
|
|
|
|
)}
|
|
|
|
|
</Stack.Col>
|
|
|
|
|
);
|
|
|
|
@ -556,6 +578,7 @@ export class App extends React.Component<{}, AppState> {
|
|
|
|
|
public render() {
|
|
|
|
|
const canvasWidth = window.innerWidth - CANVAS_WINDOW_OFFSET_LEFT;
|
|
|
|
|
const canvasHeight = window.innerHeight - CANVAS_WINDOW_OFFSET_TOP;
|
|
|
|
|
const { t } = this.props;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="container">
|
|
|
|
@ -624,14 +647,15 @@ export class App extends React.Component<{}, AppState> {
|
|
|
|
|
ContextMenu.push({
|
|
|
|
|
options: [
|
|
|
|
|
navigator.clipboard && {
|
|
|
|
|
label: "Paste",
|
|
|
|
|
label: t("labels.paste"),
|
|
|
|
|
action: () => this.pasteFromClipboard()
|
|
|
|
|
},
|
|
|
|
|
...this.actionManager.getContextMenuItems(
|
|
|
|
|
elements,
|
|
|
|
|
this.state,
|
|
|
|
|
this.syncActionResult,
|
|
|
|
|
action => this.canvasOnlyActions.includes(action)
|
|
|
|
|
action => this.canvasOnlyActions.includes(action),
|
|
|
|
|
t
|
|
|
|
|
)
|
|
|
|
|
],
|
|
|
|
|
top: e.clientY,
|
|
|
|
@ -649,18 +673,19 @@ export class App extends React.Component<{}, AppState> {
|
|
|
|
|
ContextMenu.push({
|
|
|
|
|
options: [
|
|
|
|
|
navigator.clipboard && {
|
|
|
|
|
label: "Copy",
|
|
|
|
|
label: t("labels.copy"),
|
|
|
|
|
action: this.copyToClipboard
|
|
|
|
|
},
|
|
|
|
|
navigator.clipboard && {
|
|
|
|
|
label: "Paste",
|
|
|
|
|
label: t("labels.paste"),
|
|
|
|
|
action: () => this.pasteFromClipboard()
|
|
|
|
|
},
|
|
|
|
|
...this.actionManager.getContextMenuItems(
|
|
|
|
|
elements,
|
|
|
|
|
this.state,
|
|
|
|
|
this.syncActionResult,
|
|
|
|
|
action => !this.canvasOnlyActions.includes(action)
|
|
|
|
|
action => !this.canvasOnlyActions.includes(action),
|
|
|
|
|
t
|
|
|
|
|
)
|
|
|
|
|
],
|
|
|
|
|
top: e.clientY,
|
|
|
|
@ -1333,5 +1358,7 @@ export class App extends React.Component<{}, AppState> {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const AppWithTrans = withTranslation()(App);
|
|
|
|
|
|
|
|
|
|
const rootElement = document.getElementById("root");
|
|
|
|
|
ReactDOM.render(<App />, rootElement);
|
|
|
|
|
ReactDOM.render(<AppWithTrans />, rootElement);
|
|
|
|
|