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,
|
canChangeSharpness,
|
||||||
canHaveArrowheads,
|
canHaveArrowheads,
|
||||||
getCommonAttributeOfSelectedElements,
|
getCommonAttributeOfSelectedElements,
|
||||||
|
getSelectedElements,
|
||||||
getTargetElements,
|
getTargetElements,
|
||||||
isSomeElementSelected,
|
isSomeElementSelected,
|
||||||
} from "../scene";
|
} from "../scene";
|
||||||
import { hasStrokeColor } from "../scene/comparisons";
|
import { hasStrokeColor } from "../scene/comparisons";
|
||||||
import Scene from "../scene/Scene";
|
import Scene from "../scene/Scene";
|
||||||
|
import { arrayToMap } from "../utils";
|
||||||
import { register } from "./register";
|
import { register } from "./register";
|
||||||
|
|
||||||
const changeProperty = (
|
const changeProperty = (
|
||||||
elements: readonly ExcalidrawElement[],
|
elements: readonly ExcalidrawElement[],
|
||||||
appState: AppState,
|
appState: AppState,
|
||||||
callback: (element: ExcalidrawElement) => ExcalidrawElement,
|
callback: (element: ExcalidrawElement) => ExcalidrawElement,
|
||||||
|
includeBoundText = false,
|
||||||
) => {
|
) => {
|
||||||
|
const selectedElementIds = arrayToMap(
|
||||||
|
getSelectedElements(elements, appState, includeBoundText),
|
||||||
|
);
|
||||||
return elements.map((element) => {
|
return elements.map((element) => {
|
||||||
if (
|
if (
|
||||||
appState.selectedElementIds[element.id] ||
|
selectedElementIds.get(element.id) ||
|
||||||
element.id === appState.editingElement?.id
|
element.id === appState.editingElement?.id
|
||||||
) {
|
) {
|
||||||
return callback(element);
|
return callback(element);
|
||||||
@ -427,7 +433,10 @@ export const actionChangeFontSize = register({
|
|||||||
name: "changeFontSize",
|
name: "changeFontSize",
|
||||||
perform: (elements, appState, value) => {
|
perform: (elements, appState, value) => {
|
||||||
return {
|
return {
|
||||||
elements: changeProperty(elements, appState, (el) => {
|
elements: changeProperty(
|
||||||
|
elements,
|
||||||
|
appState,
|
||||||
|
(el) => {
|
||||||
if (isTextElement(el)) {
|
if (isTextElement(el)) {
|
||||||
const element: ExcalidrawTextElement = newElementWith(el, {
|
const element: ExcalidrawTextElement = newElementWith(el, {
|
||||||
fontSize: value,
|
fontSize: value,
|
||||||
@ -436,12 +445,14 @@ export const actionChangeFontSize = register({
|
|||||||
if (el.containerId) {
|
if (el.containerId) {
|
||||||
container = Scene.getScene(el)!.getElement(el.containerId);
|
container = Scene.getScene(el)!.getElement(el.containerId);
|
||||||
}
|
}
|
||||||
redrawTextBoundingBox(element, container);
|
redrawTextBoundingBox(element, container, appState);
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
return el;
|
return el;
|
||||||
}),
|
},
|
||||||
|
true,
|
||||||
|
),
|
||||||
appState: {
|
appState: {
|
||||||
...appState,
|
...appState,
|
||||||
currentItemFontSize: value,
|
currentItemFontSize: value,
|
||||||
@ -492,7 +503,10 @@ export const actionChangeFontFamily = register({
|
|||||||
name: "changeFontFamily",
|
name: "changeFontFamily",
|
||||||
perform: (elements, appState, value) => {
|
perform: (elements, appState, value) => {
|
||||||
return {
|
return {
|
||||||
elements: changeProperty(elements, appState, (el) => {
|
elements: changeProperty(
|
||||||
|
elements,
|
||||||
|
appState,
|
||||||
|
(el) => {
|
||||||
if (isTextElement(el)) {
|
if (isTextElement(el)) {
|
||||||
const element: ExcalidrawTextElement = newElementWith(el, {
|
const element: ExcalidrawTextElement = newElementWith(el, {
|
||||||
fontFamily: value,
|
fontFamily: value,
|
||||||
@ -501,12 +515,14 @@ export const actionChangeFontFamily = register({
|
|||||||
if (el.containerId) {
|
if (el.containerId) {
|
||||||
container = Scene.getScene(el)!.getElement(el.containerId);
|
container = Scene.getScene(el)!.getElement(el.containerId);
|
||||||
}
|
}
|
||||||
redrawTextBoundingBox(element, container);
|
redrawTextBoundingBox(element, container, appState);
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
return el;
|
return el;
|
||||||
}),
|
},
|
||||||
|
true,
|
||||||
|
),
|
||||||
appState: {
|
appState: {
|
||||||
...appState,
|
...appState,
|
||||||
currentItemFontFamily: value,
|
currentItemFontFamily: value,
|
||||||
@ -560,7 +576,10 @@ export const actionChangeTextAlign = register({
|
|||||||
name: "changeTextAlign",
|
name: "changeTextAlign",
|
||||||
perform: (elements, appState, value) => {
|
perform: (elements, appState, value) => {
|
||||||
return {
|
return {
|
||||||
elements: changeProperty(elements, appState, (el) => {
|
elements: changeProperty(
|
||||||
|
elements,
|
||||||
|
appState,
|
||||||
|
(el) => {
|
||||||
if (isTextElement(el)) {
|
if (isTextElement(el)) {
|
||||||
const element: ExcalidrawTextElement = newElementWith(el, {
|
const element: ExcalidrawTextElement = newElementWith(el, {
|
||||||
textAlign: value,
|
textAlign: value,
|
||||||
@ -569,12 +588,14 @@ export const actionChangeTextAlign = register({
|
|||||||
if (el.containerId) {
|
if (el.containerId) {
|
||||||
container = Scene.getScene(el)!.getElement(el.containerId);
|
container = Scene.getScene(el)!.getElement(el.containerId);
|
||||||
}
|
}
|
||||||
redrawTextBoundingBox(element, container);
|
redrawTextBoundingBox(element, container, appState);
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
return el;
|
return el;
|
||||||
}),
|
},
|
||||||
|
true,
|
||||||
|
),
|
||||||
appState: {
|
appState: {
|
||||||
...appState,
|
...appState,
|
||||||
currentItemTextAlign: value,
|
currentItemTextAlign: value,
|
||||||
|
@ -71,7 +71,11 @@ export const actionPasteStyles = register({
|
|||||||
element.containerId,
|
element.containerId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
redrawTextBoundingBox(element as ExcalidrawTextElement, container);
|
redrawTextBoundingBox(
|
||||||
|
element as ExcalidrawTextElement,
|
||||||
|
container,
|
||||||
|
appState,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return newElement;
|
return newElement;
|
||||||
}
|
}
|
||||||
|
@ -10,24 +10,52 @@ import { mutateElement } from "./mutateElement";
|
|||||||
import { BOUND_TEXT_PADDING } from "../constants";
|
import { BOUND_TEXT_PADDING } from "../constants";
|
||||||
import { MaybeTransformHandleType } from "./transformHandles";
|
import { MaybeTransformHandleType } from "./transformHandles";
|
||||||
import Scene from "../scene/Scene";
|
import Scene from "../scene/Scene";
|
||||||
|
import { AppState } from "../types";
|
||||||
|
import { isTextElement } from ".";
|
||||||
|
|
||||||
export const redrawTextBoundingBox = (
|
export const redrawTextBoundingBox = (
|
||||||
element: ExcalidrawTextElement,
|
element: ExcalidrawTextElement,
|
||||||
container: ExcalidrawElement | null,
|
container: ExcalidrawElement | null,
|
||||||
|
appState: AppState,
|
||||||
) => {
|
) => {
|
||||||
const maxWidth = container
|
const maxWidth = container
|
||||||
? container.width - BOUND_TEXT_PADDING * 2
|
? container.width - BOUND_TEXT_PADDING * 2
|
||||||
: undefined;
|
: 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(
|
const metrics = measureText(
|
||||||
element.originalText,
|
element.originalText,
|
||||||
getFontString(element),
|
getFontString(element),
|
||||||
maxWidth,
|
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, {
|
mutateElement(element, {
|
||||||
width: metrics.width,
|
width: metrics.width,
|
||||||
height: metrics.height,
|
height: metrics.height,
|
||||||
baseline: metrics.baseline,
|
baseline: metrics.baseline,
|
||||||
|
y: coordY,
|
||||||
|
text,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ export const textWysiwyg = ({
|
|||||||
if (updatedElement && isTextElement(updatedElement)) {
|
if (updatedElement && isTextElement(updatedElement)) {
|
||||||
let coordX = updatedElement.x;
|
let coordX = updatedElement.x;
|
||||||
let coordY = updatedElement.y;
|
let coordY = updatedElement.y;
|
||||||
let container = updatedElement?.containerId
|
const container = updatedElement?.containerId
|
||||||
? Scene.getScene(updatedElement)!.getElement(updatedElement.containerId)
|
? Scene.getScene(updatedElement)!.getElement(updatedElement.containerId)
|
||||||
: null;
|
: null;
|
||||||
let maxWidth = updatedElement.width;
|
let maxWidth = updatedElement.width;
|
||||||
@ -123,21 +123,12 @@ export const textWysiwyg = ({
|
|||||||
height = editorHeight;
|
height = editorHeight;
|
||||||
}
|
}
|
||||||
if (propertiesUpdated) {
|
if (propertiesUpdated) {
|
||||||
const currentContainer = Scene.getScene(updatedElement)?.getElement(
|
|
||||||
updatedElement.containerId,
|
|
||||||
) as ExcalidrawBindableElement;
|
|
||||||
approxLineHeight = isTextElement(updatedElement)
|
approxLineHeight = isTextElement(updatedElement)
|
||||||
? getApproxLineHeight(getFontString(updatedElement))
|
? getApproxLineHeight(getFontString(updatedElement))
|
||||||
: 0;
|
: 0;
|
||||||
if (
|
|
||||||
updatedElement.height >
|
originalContainerHeight = container.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 };
|
|
||||||
}
|
|
||||||
// update height of the editor after properties updated
|
// update height of the editor after properties updated
|
||||||
height = updatedElement.height;
|
height = updatedElement.height;
|
||||||
}
|
}
|
||||||
|
@ -77,4 +77,4 @@ export const getTargetElements = (
|
|||||||
) =>
|
) =>
|
||||||
appState.editingElement
|
appState.editingElement
|
||||||
? [appState.editingElement]
|
? [appState.editingElement]
|
||||||
: getSelectedElements(elements, appState);
|
: getSelectedElements(elements, appState, true);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user