feat: Support updating text properties by clicking on container (#4499)
This commit is contained in:
parent
11396a21de
commit
dd8e465304
@ -57,21 +57,27 @@ import {
|
||||
canChangeSharpness,
|
||||
canHaveArrowheads,
|
||||
getCommonAttributeOfSelectedElements,
|
||||
getSelectedElements,
|
||||
getTargetElements,
|
||||
isSomeElementSelected,
|
||||
} from "../scene";
|
||||
import { hasStrokeColor } from "../scene/comparisons";
|
||||
import Scene from "../scene/Scene";
|
||||
import { arrayToMap } from "../utils";
|
||||
import { register } from "./register";
|
||||
|
||||
const changeProperty = (
|
||||
elements: readonly ExcalidrawElement[],
|
||||
appState: AppState,
|
||||
callback: (element: ExcalidrawElement) => ExcalidrawElement,
|
||||
includeBoundText = false,
|
||||
) => {
|
||||
const selectedElementIds = arrayToMap(
|
||||
getSelectedElements(elements, appState, includeBoundText),
|
||||
);
|
||||
return elements.map((element) => {
|
||||
if (
|
||||
appState.selectedElementIds[element.id] ||
|
||||
selectedElementIds.get(element.id) ||
|
||||
element.id === appState.editingElement?.id
|
||||
) {
|
||||
return callback(element);
|
||||
@ -427,21 +433,26 @@ export const actionChangeFontSize = register({
|
||||
name: "changeFontSize",
|
||||
perform: (elements, appState, value) => {
|
||||
return {
|
||||
elements: changeProperty(elements, appState, (el) => {
|
||||
if (isTextElement(el)) {
|
||||
const element: ExcalidrawTextElement = newElementWith(el, {
|
||||
fontSize: value,
|
||||
});
|
||||
let container = null;
|
||||
if (el.containerId) {
|
||||
container = Scene.getScene(el)!.getElement(el.containerId);
|
||||
elements: changeProperty(
|
||||
elements,
|
||||
appState,
|
||||
(el) => {
|
||||
if (isTextElement(el)) {
|
||||
const element: ExcalidrawTextElement = newElementWith(el, {
|
||||
fontSize: value,
|
||||
});
|
||||
let container = null;
|
||||
if (el.containerId) {
|
||||
container = Scene.getScene(el)!.getElement(el.containerId);
|
||||
}
|
||||
redrawTextBoundingBox(element, container, appState);
|
||||
return element;
|
||||
}
|
||||
redrawTextBoundingBox(element, container);
|
||||
return element;
|
||||
}
|
||||
|
||||
return el;
|
||||
}),
|
||||
return el;
|
||||
},
|
||||
true,
|
||||
),
|
||||
appState: {
|
||||
...appState,
|
||||
currentItemFontSize: value,
|
||||
@ -492,21 +503,26 @@ export const actionChangeFontFamily = register({
|
||||
name: "changeFontFamily",
|
||||
perform: (elements, appState, value) => {
|
||||
return {
|
||||
elements: changeProperty(elements, appState, (el) => {
|
||||
if (isTextElement(el)) {
|
||||
const element: ExcalidrawTextElement = newElementWith(el, {
|
||||
fontFamily: value,
|
||||
});
|
||||
let container = null;
|
||||
if (el.containerId) {
|
||||
container = Scene.getScene(el)!.getElement(el.containerId);
|
||||
elements: changeProperty(
|
||||
elements,
|
||||
appState,
|
||||
(el) => {
|
||||
if (isTextElement(el)) {
|
||||
const element: ExcalidrawTextElement = newElementWith(el, {
|
||||
fontFamily: value,
|
||||
});
|
||||
let container = null;
|
||||
if (el.containerId) {
|
||||
container = Scene.getScene(el)!.getElement(el.containerId);
|
||||
}
|
||||
redrawTextBoundingBox(element, container, appState);
|
||||
return element;
|
||||
}
|
||||
redrawTextBoundingBox(element, container);
|
||||
return element;
|
||||
}
|
||||
|
||||
return el;
|
||||
}),
|
||||
return el;
|
||||
},
|
||||
true,
|
||||
),
|
||||
appState: {
|
||||
...appState,
|
||||
currentItemFontFamily: value,
|
||||
@ -560,21 +576,26 @@ export const actionChangeTextAlign = register({
|
||||
name: "changeTextAlign",
|
||||
perform: (elements, appState, value) => {
|
||||
return {
|
||||
elements: changeProperty(elements, appState, (el) => {
|
||||
if (isTextElement(el)) {
|
||||
const element: ExcalidrawTextElement = newElementWith(el, {
|
||||
textAlign: value,
|
||||
});
|
||||
let container = null;
|
||||
if (el.containerId) {
|
||||
container = Scene.getScene(el)!.getElement(el.containerId);
|
||||
elements: changeProperty(
|
||||
elements,
|
||||
appState,
|
||||
(el) => {
|
||||
if (isTextElement(el)) {
|
||||
const element: ExcalidrawTextElement = newElementWith(el, {
|
||||
textAlign: value,
|
||||
});
|
||||
let container = null;
|
||||
if (el.containerId) {
|
||||
container = Scene.getScene(el)!.getElement(el.containerId);
|
||||
}
|
||||
redrawTextBoundingBox(element, container, appState);
|
||||
return element;
|
||||
}
|
||||
redrawTextBoundingBox(element, container);
|
||||
return element;
|
||||
}
|
||||
|
||||
return el;
|
||||
}),
|
||||
return el;
|
||||
},
|
||||
true,
|
||||
),
|
||||
appState: {
|
||||
...appState,
|
||||
currentItemTextAlign: value,
|
||||
|
@ -71,7 +71,11 @@ export const actionPasteStyles = register({
|
||||
element.containerId,
|
||||
);
|
||||
}
|
||||
redrawTextBoundingBox(element as ExcalidrawTextElement, container);
|
||||
redrawTextBoundingBox(
|
||||
element as ExcalidrawTextElement,
|
||||
container,
|
||||
appState,
|
||||
);
|
||||
}
|
||||
return newElement;
|
||||
}
|
||||
|
@ -10,24 +10,52 @@ import { mutateElement } from "./mutateElement";
|
||||
import { BOUND_TEXT_PADDING } from "../constants";
|
||||
import { MaybeTransformHandleType } from "./transformHandles";
|
||||
import Scene from "../scene/Scene";
|
||||
import { AppState } from "../types";
|
||||
import { isTextElement } from ".";
|
||||
|
||||
export const redrawTextBoundingBox = (
|
||||
element: ExcalidrawTextElement,
|
||||
container: ExcalidrawElement | null,
|
||||
appState: AppState,
|
||||
) => {
|
||||
const maxWidth = container
|
||||
? container.width - BOUND_TEXT_PADDING * 2
|
||||
: undefined;
|
||||
let text = element.originalText;
|
||||
|
||||
// Call wrapText only when updating text properties
|
||||
// By clicking on the container
|
||||
if (container && !isTextElement(appState.editingElement)) {
|
||||
text = wrapText(
|
||||
element.originalText,
|
||||
getFontString(element),
|
||||
container.width,
|
||||
);
|
||||
}
|
||||
const metrics = measureText(
|
||||
element.originalText,
|
||||
getFontString(element),
|
||||
maxWidth,
|
||||
);
|
||||
|
||||
let coordY = element.y;
|
||||
// Resize container and vertically center align the text
|
||||
if (container) {
|
||||
coordY = container.y + container.height / 2 - metrics.height / 2;
|
||||
let nextHeight = container.height;
|
||||
if (metrics.height > container.height - BOUND_TEXT_PADDING * 2) {
|
||||
nextHeight = metrics.height + BOUND_TEXT_PADDING * 2;
|
||||
coordY = container.y + nextHeight / 2 - metrics.height / 2;
|
||||
}
|
||||
mutateElement(container, { height: nextHeight });
|
||||
}
|
||||
|
||||
mutateElement(element, {
|
||||
width: metrics.width,
|
||||
height: metrics.height,
|
||||
baseline: metrics.baseline,
|
||||
y: coordY,
|
||||
text,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -102,7 +102,7 @@ export const textWysiwyg = ({
|
||||
if (updatedElement && isTextElement(updatedElement)) {
|
||||
let coordX = updatedElement.x;
|
||||
let coordY = updatedElement.y;
|
||||
let container = updatedElement?.containerId
|
||||
const container = updatedElement?.containerId
|
||||
? Scene.getScene(updatedElement)!.getElement(updatedElement.containerId)
|
||||
: null;
|
||||
let maxWidth = updatedElement.width;
|
||||
@ -123,21 +123,12 @@ export const textWysiwyg = ({
|
||||
height = editorHeight;
|
||||
}
|
||||
if (propertiesUpdated) {
|
||||
const currentContainer = Scene.getScene(updatedElement)?.getElement(
|
||||
updatedElement.containerId,
|
||||
) as ExcalidrawBindableElement;
|
||||
approxLineHeight = isTextElement(updatedElement)
|
||||
? getApproxLineHeight(getFontString(updatedElement))
|
||||
: 0;
|
||||
if (
|
||||
updatedElement.height >
|
||||
currentContainer.height - BOUND_TEXT_PADDING * 2
|
||||
) {
|
||||
const nextHeight = updatedElement.height + BOUND_TEXT_PADDING * 2;
|
||||
originalContainerHeight = nextHeight;
|
||||
mutateElement(container, { height: nextHeight });
|
||||
container = { ...container, height: nextHeight };
|
||||
}
|
||||
|
||||
originalContainerHeight = container.height;
|
||||
|
||||
// update height of the editor after properties updated
|
||||
height = updatedElement.height;
|
||||
}
|
||||
|
@ -77,4 +77,4 @@ export const getTargetElements = (
|
||||
) =>
|
||||
appState.editingElement
|
||||
? [appState.editingElement]
|
||||
: getSelectedElements(elements, appState);
|
||||
: getSelectedElements(elements, appState, true);
|
||||
|
Loading…
x
Reference in New Issue
Block a user