feat: Enter and Exit line editor via context menu (#5719)
* feat: Enter and exit line editor via context menu * Add tests * fix * review fixes * fix
This commit is contained in:
parent
b477c2ad6b
commit
6c1246ef77
49
src/actions/actionLinearEditor.ts
Normal file
49
src/actions/actionLinearEditor.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { getNonDeletedElements } from "../element";
|
||||||
|
import { LinearElementEditor } from "../element/linearElementEditor";
|
||||||
|
import { isLinearElement } from "../element/typeChecks";
|
||||||
|
import { ExcalidrawLinearElement } from "../element/types";
|
||||||
|
import { getSelectedElements } from "../scene";
|
||||||
|
import { register } from "./register";
|
||||||
|
|
||||||
|
export const actionToggleLinearEditor = register({
|
||||||
|
name: "toggleLinearEditor",
|
||||||
|
trackEvent: {
|
||||||
|
category: "element",
|
||||||
|
},
|
||||||
|
contextItemPredicate: (elements, appState) => {
|
||||||
|
const selectedElements = getSelectedElements(elements, appState);
|
||||||
|
if (selectedElements.length === 1 && isLinearElement(selectedElements[0])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
perform(elements, appState, _, app) {
|
||||||
|
const selectedElement = getSelectedElements(
|
||||||
|
getNonDeletedElements(elements),
|
||||||
|
appState,
|
||||||
|
true,
|
||||||
|
)[0] as ExcalidrawLinearElement;
|
||||||
|
|
||||||
|
const editingLinearElement =
|
||||||
|
appState.editingLinearElement?.elementId === selectedElement.id
|
||||||
|
? null
|
||||||
|
: new LinearElementEditor(selectedElement, app.scene);
|
||||||
|
return {
|
||||||
|
appState: {
|
||||||
|
...appState,
|
||||||
|
editingLinearElement,
|
||||||
|
},
|
||||||
|
commitToHistory: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
contextItemLabel: (elements, appState) => {
|
||||||
|
const selectedElement = getSelectedElements(
|
||||||
|
getNonDeletedElements(elements),
|
||||||
|
appState,
|
||||||
|
true,
|
||||||
|
)[0] as ExcalidrawLinearElement;
|
||||||
|
return appState.editingLinearElement?.elementId === selectedElement.id
|
||||||
|
? "labels.lineEditor.exit"
|
||||||
|
: "labels.lineEditor.edit";
|
||||||
|
},
|
||||||
|
});
|
@ -85,3 +85,4 @@ export { actionToggleStats } from "./actionToggleStats";
|
|||||||
export { actionUnbindText, actionBindText } from "./actionBoundText";
|
export { actionUnbindText, actionBindText } from "./actionBoundText";
|
||||||
export { actionLink } from "../element/Hyperlink";
|
export { actionLink } from "../element/Hyperlink";
|
||||||
export { actionToggleLock } from "./actionToggleLock";
|
export { actionToggleLock } from "./actionToggleLock";
|
||||||
|
export { actionToggleLinearEditor } from "./actionLinearEditor";
|
||||||
|
@ -111,7 +111,8 @@ export type ActionName =
|
|||||||
| "hyperlink"
|
| "hyperlink"
|
||||||
| "eraser"
|
| "eraser"
|
||||||
| "bindText"
|
| "bindText"
|
||||||
| "toggleLock";
|
| "toggleLock"
|
||||||
|
| "toggleLinearEditor";
|
||||||
|
|
||||||
export type PanelComponentProps = {
|
export type PanelComponentProps = {
|
||||||
elements: readonly ExcalidrawElement[];
|
elements: readonly ExcalidrawElement[];
|
||||||
|
@ -34,6 +34,7 @@ import {
|
|||||||
actionUngroup,
|
actionUngroup,
|
||||||
actionLink,
|
actionLink,
|
||||||
actionToggleLock,
|
actionToggleLock,
|
||||||
|
actionToggleLinearEditor,
|
||||||
} from "../actions";
|
} from "../actions";
|
||||||
import { createRedoAction, createUndoAction } from "../actions/actionHistory";
|
import { createRedoAction, createUndoAction } from "../actions/actionHistory";
|
||||||
import { ActionManager } from "../actions/manager";
|
import { ActionManager } from "../actions/manager";
|
||||||
@ -5876,6 +5877,12 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
this.actionManager.getAppState(),
|
this.actionManager.getAppState(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const mayBeAllowToggleLineEditing =
|
||||||
|
actionToggleLinearEditor.contextItemPredicate(
|
||||||
|
this.actionManager.getElementsIncludingDeleted(),
|
||||||
|
this.actionManager.getAppState(),
|
||||||
|
);
|
||||||
|
|
||||||
const separator = "separator";
|
const separator = "separator";
|
||||||
|
|
||||||
const elements = this.scene.getNonDeletedElements();
|
const elements = this.scene.getNonDeletedElements();
|
||||||
@ -6017,6 +6024,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
maybeFlipHorizontal && actionFlipHorizontal,
|
maybeFlipHorizontal && actionFlipHorizontal,
|
||||||
maybeFlipVertical && actionFlipVertical,
|
maybeFlipVertical && actionFlipVertical,
|
||||||
(maybeFlipHorizontal || maybeFlipVertical) && separator,
|
(maybeFlipHorizontal || maybeFlipVertical) && separator,
|
||||||
|
mayBeAllowToggleLineEditing && actionToggleLinearEditor,
|
||||||
actionLink.contextItemPredicate(elements, this.state) && actionLink,
|
actionLink.contextItemPredicate(elements, this.state) && actionLink,
|
||||||
actionDuplicateSelection,
|
actionDuplicateSelection,
|
||||||
actionToggleLock,
|
actionToggleLock,
|
||||||
|
@ -114,6 +114,11 @@
|
|||||||
"create": "Create link",
|
"create": "Create link",
|
||||||
"label": "Link"
|
"label": "Link"
|
||||||
},
|
},
|
||||||
|
"lineEditor": {
|
||||||
|
"edit": "Edit line",
|
||||||
|
"exit": "Exit line editor"
|
||||||
|
},
|
||||||
|
|
||||||
"elementLock": {
|
"elementLock": {
|
||||||
"lock": "Lock",
|
"lock": "Lock",
|
||||||
"unlock": "Unlock",
|
"unlock": "Unlock",
|
||||||
|
@ -5,11 +5,12 @@ import { centerPoint } from "../math";
|
|||||||
import { reseed } from "../random";
|
import { reseed } from "../random";
|
||||||
import * as Renderer from "../renderer/renderScene";
|
import * as Renderer from "../renderer/renderScene";
|
||||||
import { Keyboard, Pointer } from "./helpers/ui";
|
import { Keyboard, Pointer } from "./helpers/ui";
|
||||||
import { screen, render, fireEvent } from "./test-utils";
|
import { screen, render, fireEvent, GlobalTestState } from "./test-utils";
|
||||||
import { API } from "../tests/helpers/api";
|
import { API } from "../tests/helpers/api";
|
||||||
import { Point } from "../types";
|
import { Point } from "../types";
|
||||||
import { KEYS } from "../keys";
|
import { KEYS } from "../keys";
|
||||||
import { LinearElementEditor } from "../element/linearElementEditor";
|
import { LinearElementEditor } from "../element/linearElementEditor";
|
||||||
|
import { queryByText } from "@testing-library/react";
|
||||||
|
|
||||||
const renderScene = jest.spyOn(Renderer, "renderScene");
|
const renderScene = jest.spyOn(Renderer, "renderScene");
|
||||||
|
|
||||||
@ -150,6 +151,42 @@ describe(" Test Linear Elements", () => {
|
|||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should allow entering and exiting line editor via context menu", () => {
|
||||||
|
createTwoPointerLinearElement("line");
|
||||||
|
fireEvent.contextMenu(GlobalTestState.canvas, {
|
||||||
|
button: 2,
|
||||||
|
clientX: midpoint[0],
|
||||||
|
clientY: midpoint[1],
|
||||||
|
});
|
||||||
|
// Enter line editor
|
||||||
|
let contextMenu = document.querySelector(".context-menu");
|
||||||
|
fireEvent.contextMenu(GlobalTestState.canvas, {
|
||||||
|
button: 2,
|
||||||
|
clientX: midpoint[0],
|
||||||
|
clientY: midpoint[1],
|
||||||
|
});
|
||||||
|
fireEvent.click(queryByText(contextMenu as HTMLElement, "Edit line")!);
|
||||||
|
|
||||||
|
expect(h.state.editingLinearElement?.elementId).toEqual(h.elements[0].id);
|
||||||
|
|
||||||
|
// Exiting line editor
|
||||||
|
fireEvent.contextMenu(GlobalTestState.canvas, {
|
||||||
|
button: 2,
|
||||||
|
clientX: midpoint[0],
|
||||||
|
clientY: midpoint[1],
|
||||||
|
});
|
||||||
|
contextMenu = document.querySelector(".context-menu");
|
||||||
|
fireEvent.contextMenu(GlobalTestState.canvas, {
|
||||||
|
button: 2,
|
||||||
|
clientX: midpoint[0],
|
||||||
|
clientY: midpoint[1],
|
||||||
|
});
|
||||||
|
fireEvent.click(
|
||||||
|
queryByText(contextMenu as HTMLElement, "Exit line editor")!,
|
||||||
|
);
|
||||||
|
expect(h.state.editingLinearElement?.elementId).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
describe("Inside editor", () => {
|
describe("Inside editor", () => {
|
||||||
it("should allow dragging line from midpoint in 2 pointer lines", async () => {
|
it("should allow dragging line from midpoint in 2 pointer lines", async () => {
|
||||||
createTwoPointerLinearElement("line");
|
createTwoPointerLinearElement("line");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user