feat: support creating text containers programatically
This commit is contained in:
@@ -35,6 +35,8 @@ import {
|
||||
wrapText,
|
||||
getBoundTextMaxWidth,
|
||||
getDefaultLineHeight,
|
||||
bindTextToContainer,
|
||||
isValidTextContainer,
|
||||
} from "./textElement";
|
||||
import {
|
||||
DEFAULT_ELEMENT_PROPS,
|
||||
@@ -44,10 +46,10 @@ import {
|
||||
DEFAULT_VERTICAL_ALIGN,
|
||||
VERTICAL_ALIGN,
|
||||
} from "../constants";
|
||||
import { isArrowElement } from "./typeChecks";
|
||||
import { isArrowElement, isTextElement } from "./typeChecks";
|
||||
import { MarkOptional, Merge, Mutable } from "../utility-types";
|
||||
|
||||
type ElementConstructorOpts = MarkOptional<
|
||||
export type ElementConstructorOpts = MarkOptional<
|
||||
Omit<ExcalidrawGenericElement, "id" | "type" | "isDeleted" | "updated">,
|
||||
| "width"
|
||||
| "height"
|
||||
@@ -140,6 +142,7 @@ const getTextElementPositionOffsets = (
|
||||
height: number;
|
||||
},
|
||||
) => {
|
||||
console.log("metrics", metrics);
|
||||
return {
|
||||
x:
|
||||
opts.textAlign === "center"
|
||||
@@ -643,3 +646,17 @@ export const duplicateElements = (
|
||||
|
||||
return clonedElements;
|
||||
};
|
||||
|
||||
export const updateElementChildren = (
|
||||
element: {
|
||||
type: ExcalidrawElement["type"];
|
||||
} & MarkOptional<ElementConstructorOpts, "x" | "y">,
|
||||
) => {
|
||||
const textElement = element.children!.find((child) => child.text !== null);
|
||||
if (isValidTextContainer(element) && textElement) {
|
||||
//@ts-ignore
|
||||
const elements = bindTextToContainer(element, textElement);
|
||||
return elements;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { getFontString, arrayToMap, isTestEnv } from "../utils";
|
||||
import {
|
||||
ExcalidrawElement,
|
||||
ExcalidrawGenericElement,
|
||||
ExcalidrawTextContainer,
|
||||
ExcalidrawTextElement,
|
||||
ExcalidrawTextElementWithContainer,
|
||||
@@ -20,7 +21,7 @@ import {
|
||||
} from "../constants";
|
||||
import { MaybeTransformHandleType } from "./transformHandles";
|
||||
import Scene from "../scene/Scene";
|
||||
import { isTextElement } from ".";
|
||||
import { isTextElement, newElement, newTextElement } from ".";
|
||||
import { isBoundToContainer, isArrowElement } from "./typeChecks";
|
||||
import { LinearElementEditor } from "./linearElementEditor";
|
||||
import { AppState } from "../types";
|
||||
@@ -32,7 +33,8 @@ import {
|
||||
resetOriginalContainerCache,
|
||||
updateOriginalContainerCache,
|
||||
} from "./textWysiwyg";
|
||||
import { ExtractSetType } from "../utility-types";
|
||||
import { ExtractSetType, MarkOptional } from "../utility-types";
|
||||
import { ElementConstructorOpts } from "./newElement";
|
||||
|
||||
export const normalizeText = (text: string) => {
|
||||
return (
|
||||
@@ -83,21 +85,27 @@ export const redrawTextBoundingBox = (
|
||||
boundTextUpdates.baseline = metrics.baseline;
|
||||
|
||||
if (container) {
|
||||
const containerDims = getContainerDims(container);
|
||||
const maxContainerHeight = getBoundTextMaxHeight(
|
||||
container,
|
||||
textElement as ExcalidrawTextElementWithContainer,
|
||||
);
|
||||
const maxContainerWidth = getBoundTextMaxWidth(container);
|
||||
|
||||
let nextHeight = containerDims.height;
|
||||
if (metrics.height > maxContainerHeight) {
|
||||
nextHeight = computeContainerDimensionForBoundText(
|
||||
const nextHeight = computeContainerDimensionForBoundText(
|
||||
metrics.height,
|
||||
container.type,
|
||||
);
|
||||
mutateElement(container, { height: nextHeight });
|
||||
updateOriginalContainerCache(container.id, nextHeight);
|
||||
}
|
||||
if (metrics.width > maxContainerWidth) {
|
||||
const nextWidth = computeContainerDimensionForBoundText(
|
||||
metrics.width,
|
||||
container.type,
|
||||
);
|
||||
mutateElement(container, { width: nextWidth });
|
||||
}
|
||||
const updatedTextElement = {
|
||||
...textElement,
|
||||
...boundTextUpdates,
|
||||
@@ -864,8 +872,9 @@ const VALID_CONTAINER_TYPES = new Set([
|
||||
"arrow",
|
||||
]);
|
||||
|
||||
export const isValidTextContainer = (element: ExcalidrawElement) =>
|
||||
VALID_CONTAINER_TYPES.has(element.type);
|
||||
export const isValidTextContainer = (element: {
|
||||
type: ExcalidrawElement["type"];
|
||||
}) => VALID_CONTAINER_TYPES.has(element.type);
|
||||
|
||||
export const computeContainerDimensionForBoundText = (
|
||||
dimension: number,
|
||||
@@ -971,3 +980,34 @@ export const getDefaultLineHeight = (fontFamily: FontFamilyValues) => {
|
||||
}
|
||||
return DEFAULT_LINE_HEIGHT[DEFAULT_FONT_FAMILY];
|
||||
};
|
||||
|
||||
export const bindTextToContainer = (
|
||||
containerProps: {
|
||||
type: ExcalidrawGenericElement["type"];
|
||||
} & MarkOptional<ElementConstructorOpts, "x" | "y">,
|
||||
textProps: { text: string } & MarkOptional<ElementConstructorOpts, "x" | "y">,
|
||||
) => {
|
||||
const container = newElement({
|
||||
x: 0,
|
||||
y: 0,
|
||||
...containerProps,
|
||||
});
|
||||
const textElement: ExcalidrawTextElement = newTextElement({
|
||||
x: 0,
|
||||
y: 0,
|
||||
...textProps,
|
||||
containerId: container.id,
|
||||
textAlign: TEXT_ALIGN.CENTER,
|
||||
verticalAlign: VERTICAL_ALIGN.MIDDLE,
|
||||
});
|
||||
mutateElement(container, {
|
||||
boundElements: (container.boundElements || []).concat({
|
||||
type: "text",
|
||||
id: textElement.id,
|
||||
}),
|
||||
});
|
||||
|
||||
redrawTextBoundingBox(textElement, container);
|
||||
|
||||
return [container, textElement];
|
||||
};
|
||||
|
||||
@@ -6,7 +6,8 @@ import {
|
||||
THEME,
|
||||
VERTICAL_ALIGN,
|
||||
} from "../constants";
|
||||
import { MarkNonNullable, ValueOf } from "../utility-types";
|
||||
import { MarkNonNullable, MarkOptional, ValueOf } from "../utility-types";
|
||||
import { ElementConstructorOpts } from "./newElement";
|
||||
|
||||
export type ChartType = "bar" | "line";
|
||||
export type FillStyle = "hachure" | "cross-hatch" | "solid" | "zigzag";
|
||||
@@ -65,6 +66,9 @@ type _ExcalidrawElementBase = Readonly<{
|
||||
link: string | null;
|
||||
locked: boolean;
|
||||
customData?: Record<string, any>;
|
||||
children?: [
|
||||
{ text: string } & MarkOptional<ElementConstructorOpts, "x" | "y">,
|
||||
];
|
||||
}>;
|
||||
|
||||
export type ExcalidrawSelectionElement = _ExcalidrawElementBase & {
|
||||
|
||||
Reference in New Issue
Block a user