feat: Bind text to container if double clicked on filled shape or stroke (#6250)

* feat: bind text to container when clicked on filled shape or element stroke

* Bind if double clicked on stroke as well

* remove

* specs

* remove

* shuffle

* fix

* back to normal
This commit is contained in:
Aakansha Doshi 2023-02-16 20:46:51 +05:30 committed by GitHub
parent 5acb99777a
commit b9ba407f96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 77 additions and 11 deletions

View File

@ -226,6 +226,7 @@ import {
setEraserCursor,
updateActiveTool,
getShortcutKey,
isTransparent,
} from "../utils";
import {
ContextMenu,
@ -2762,7 +2763,15 @@ class App extends React.Component<AppProps, AppState> {
sceneY,
);
if (container) {
if (isArrowElement(container) || hasBoundTextElement(container)) {
if (
isArrowElement(container) ||
hasBoundTextElement(container) ||
!isTransparent(container.backgroundColor) ||
isHittingElementNotConsideringBoundingBox(container, this.state, [
sceneX,
sceneY,
])
) {
const midPoint = getContainerCenter(container, this.state);
sceneX = midPoint.x;

View File

@ -463,14 +463,21 @@ describe("textWysiwyg", () => {
});
});
it("should bind text to container when double clicked on center of filled container", async () => {
it("should bind text to container when double clicked inside filled container", async () => {
const rectangle = API.createElement({
type: "rectangle",
x: 10,
y: 20,
width: 90,
height: 75,
backgroundColor: "red",
});
h.elements = [rectangle];
expect(h.elements.length).toBe(1);
expect(h.elements[0].id).toBe(rectangle.id);
mouse.doubleClickAt(
rectangle.x + rectangle.width / 2,
rectangle.y + rectangle.height / 2,
);
mouse.doubleClickAt(rectangle.x + 10, rectangle.y + 10);
expect(h.elements.length).toBe(2);
const text = h.elements[1] as ExcalidrawTextElementWithContainer;
@ -504,24 +511,37 @@ describe("textWysiwyg", () => {
});
h.elements = [rectangle];
mouse.doubleClickAt(rectangle.x + 10, rectangle.y + 10);
expect(h.elements.length).toBe(2);
let text = h.elements[1] as ExcalidrawTextElementWithContainer;
expect(text.type).toBe("text");
expect(text.containerId).toBe(null);
mouse.down();
let editor = document.querySelector(
".excalidraw-textEditorContainer > textarea",
) as HTMLTextAreaElement;
await new Promise((r) => setTimeout(r, 0));
editor.blur();
mouse.doubleClickAt(
rectangle.x + rectangle.width / 2,
rectangle.y + rectangle.height / 2,
);
expect(h.elements.length).toBe(2);
expect(h.elements.length).toBe(3);
const text = h.elements[1] as ExcalidrawTextElementWithContainer;
text = h.elements[1] as ExcalidrawTextElementWithContainer;
expect(text.type).toBe("text");
expect(text.containerId).toBe(rectangle.id);
mouse.down();
const editor = document.querySelector(
editor = document.querySelector(
".excalidraw-textEditorContainer > textarea",
) as HTMLTextAreaElement;
fireEvent.change(editor, { target: { value: "Hello World!" } });
await new Promise((r) => setTimeout(r, 0));
editor.blur();
expect(rectangle.boundElements).toStrictEqual([
{ id: text.id, type: "text" },
]);
@ -551,6 +571,43 @@ describe("textWysiwyg", () => {
]);
});
it("should bind text to container when double clicked on container stroke", async () => {
const rectangle = API.createElement({
type: "rectangle",
x: 10,
y: 20,
width: 90,
height: 75,
strokeWidth: 4,
});
h.elements = [rectangle];
expect(h.elements.length).toBe(1);
expect(h.elements[0].id).toBe(rectangle.id);
mouse.doubleClickAt(rectangle.x + 2, rectangle.y + 2);
expect(h.elements.length).toBe(2);
const text = h.elements[1] as ExcalidrawTextElementWithContainer;
expect(text.type).toBe("text");
expect(text.containerId).toBe(rectangle.id);
expect(rectangle.boundElements).toStrictEqual([
{ id: text.id, type: "text" },
]);
mouse.down();
const editor = document.querySelector(
".excalidraw-textEditorContainer > textarea",
) as HTMLTextAreaElement;
fireEvent.change(editor, { target: { value: "Hello World!" } });
await new Promise((r) => setTimeout(r, 0));
editor.blur();
expect(rectangle.boundElements).toStrictEqual([
{ id: text.id, type: "text" },
]);
});
it("shouldn't bind to non-text-bindable containers", async () => {
const freedraw = API.createElement({
type: "freedraw",

View File

@ -137,7 +137,7 @@ export const isExcalidrawElement = (element: any): boolean => {
export const hasBoundTextElement = (
element: ExcalidrawElement | null,
): element is ExcalidrawBindableElement => {
): element is MarkNonNullable<ExcalidrawBindableElement, "boundElements"> => {
return (
isBindableElement(element) &&
!!element.boundElements?.some(({ type }) => type === "text")