Skip to content

Commit

Permalink
fix: prevent breaking words when resizing text elements and change ho…
Browse files Browse the repository at this point in the history
…w text elements are resized (#110)
  • Loading branch information
jmjuanes authored Oct 28, 2024
1 parent 5ea6a0a commit dfad930
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 62 deletions.
8 changes: 5 additions & 3 deletions app/components/elements/editable-text.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,16 @@ export const EditableText = props => {
const previewStyles = {
width: props.width + "px",
height: props.height + "px",
whiteSpace: "pre-wrap", // "pre-wrap",
whiteSpace: "pre-wrap",
color: props.textColor,
fontFamily: props.textFont,
fontSize: props.textSize + "px",
lineHeight: "normal",
textAlign: props.textAlign,
userSelect: "none",
wordBreak: "break-all",
// wordBreak: "break-all",
overflow: "hidden",
overflowWrap: "break-word",
};

// Enable autofocus when element changes to editable
Expand Down Expand Up @@ -65,7 +66,8 @@ export const EditableText = props => {
textAlign: props.textAlign,
// transform: "translateX(-50%) translateY(-50%)",
whiteSpace: "pre-wrap",
wordBreak: "break-all",
// wordBreak: "break-all",
overflowWrap: "break-word",
}}
onPointerDown={stopEventPropagation}
onMouseDown={stopEventPropagation}
Expand Down
2 changes: 1 addition & 1 deletion app/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ export const TEXT_SIZES = {
XLARGE: 64,
};

export const TEXT_SIZE_STEP = 8;
export const TEXT_SIZE_STEP = 2;
export const TEXT_SIZE_MIN = 8;
export const TEXT_SIZE_MAX = 256;

Expand Down
129 changes: 72 additions & 57 deletions app/elements.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,57 +91,55 @@ const checkStrokeStyleValue = initialValue => {

// allow to perserve the aspect ratio of the element
const preserveAspectRatio = (element, snapshot, event) => {
if (event.shiftKey) {
const ratio = (snapshot.y2 - snapshot.y1) / Math.max(1, snapshot.x2 - snapshot.x1);
if (event.handler === HANDLERS.CORNER_TOP_LEFT) {
if (event.dx * ratio < event.dy) {
element.y1 = snapshot.y1 + ((element.x1 - snapshot.x1) * ratio);
}
else {
element.x1 = snapshot.x1 + ((element.y1 - snapshot.y1) / ratio);
}
const ratio = (snapshot.y2 - snapshot.y1) / Math.max(1, snapshot.x2 - snapshot.x1);
if (event.handler === HANDLERS.CORNER_TOP_LEFT) {
if (event.dx * ratio < event.dy) {
element.y1 = snapshot.y1 + ((element.x1 - snapshot.x1) * ratio);
}
else if (event.handler === HANDLERS.CORNER_TOP_RIGHT) {
if ((-1) * event.dx * ratio < event.dy) {
element.y1 = snapshot.y1 - ((element.x2 - snapshot.x2) * ratio);
}
else {
element.x2 = snapshot.x2 - ((element.y1 - snapshot.y1) / ratio);
}
else {
element.x1 = snapshot.x1 + ((element.y1 - snapshot.y1) / ratio);
}
else if (event.handler === HANDLERS.CORNER_BOTTOM_LEFT) {
if ((-1) * event.dx * ratio > event.dy) {
element.y2 = snapshot.y2 - ((element.x1 - snapshot.x1) * ratio);
}
else {
element.x1 = snapshot.x1 - ((element.y2 - snapshot.y2) / ratio);
}
}
else if (event.handler === HANDLERS.CORNER_TOP_RIGHT) {
if ((-1) * event.dx * ratio < event.dy) {
element.y1 = snapshot.y1 - ((element.x2 - snapshot.x2) * ratio);
}
else if (event.handler === HANDLERS.CORNER_BOTTOM_RIGHT) {
if (event.dx * ratio > event.dy) {
element.y2 = snapshot.y2 + ((element.x2 - snapshot.x2) * ratio);
}
else {
element.x2 = snapshot.x2 + ((element.y2 - snapshot.y2) / ratio);
}
else {
element.x2 = snapshot.x2 - ((element.y1 - snapshot.y1) / ratio);
}
else if (event.handler === HANDLERS.EDGE_TOP) {
element.x1 = snapshot.x1 + ((snapshot.x2 - snapshot.x1) / 2) - ((element.y2 - element.y1) / (2 * ratio));
element.x2 = snapshot.x2 - ((snapshot.x2 - snapshot.x1) / 2) + ((element.y2 - element.y1) / (2 * ratio));
}
else if (event.handler === HANDLERS.CORNER_BOTTOM_LEFT) {
if ((-1) * event.dx * ratio > event.dy) {
element.y2 = snapshot.y2 - ((element.x1 - snapshot.x1) * ratio);
}
else if (event.handler === HANDLERS.EDGE_BOTTOM) {
element.x1 = snapshot.x1 + ((snapshot.x2 - snapshot.x1) / 2) - ((element.y2 - element.y1) / (2 * ratio));
element.x2 = snapshot.x2 - ((snapshot.x2 - snapshot.x1) / 2) + ((element.y2 - element.y1) / (2 * ratio));
else {
element.x1 = snapshot.x1 - ((element.y2 - snapshot.y2) / ratio);
}
else if (event.handler === HANDLERS.EDGE_LEFT) {
element.y1 = snapshot.y1 + ((snapshot.y2 - snapshot.y1) / 2) - ((element.x2 - element.x1) * ratio) / 2;
element.y2 = snapshot.y2 - ((snapshot.y2 - snapshot.y1) / 2) + ((element.x2 - element.x1) * ratio) / 2;
}
else if (event.handler === HANDLERS.CORNER_BOTTOM_RIGHT) {
if (event.dx * ratio > event.dy) {
element.y2 = snapshot.y2 + ((element.x2 - snapshot.x2) * ratio);
}
else if (event.handler === HANDLERS.EDGE_RIGHT) {
element.y1 = snapshot.y1 + ((snapshot.y2 - snapshot.y1) / 2) - ((element.x2 - element.x1) * ratio) / 2;
element.y2 = snapshot.y2 - ((snapshot.y2 - snapshot.y1) / 2) + ((element.x2 - element.x1) * ratio) / 2;
else {
element.x2 = snapshot.x2 + ((element.y2 - snapshot.y2) / ratio);
}
}
else if (event.handler === HANDLERS.EDGE_TOP) {
element.x1 = snapshot.x1 + ((snapshot.x2 - snapshot.x1) / 2) - ((element.y2 - element.y1) / (2 * ratio));
element.x2 = snapshot.x2 - ((snapshot.x2 - snapshot.x1) / 2) + ((element.y2 - element.y1) / (2 * ratio));
}
else if (event.handler === HANDLERS.EDGE_BOTTOM) {
element.x1 = snapshot.x1 + ((snapshot.x2 - snapshot.x1) / 2) - ((element.y2 - element.y1) / (2 * ratio));
element.x2 = snapshot.x2 - ((snapshot.x2 - snapshot.x1) / 2) + ((element.y2 - element.y1) / (2 * ratio));
}
else if (event.handler === HANDLERS.EDGE_LEFT) {
element.y1 = snapshot.y1 + ((snapshot.y2 - snapshot.y1) / 2) - ((element.x2 - element.x1) * ratio) / 2;
element.y2 = snapshot.y2 - ((snapshot.y2 - snapshot.y1) / 2) + ((element.x2 - element.x1) * ratio) / 2;
}
else if (event.handler === HANDLERS.EDGE_RIGHT) {
element.y1 = snapshot.y1 + ((snapshot.y2 - snapshot.y1) / 2) - ((element.x2 - element.x1) * ratio) / 2;
element.y2 = snapshot.y2 - ((snapshot.y2 - snapshot.y1) / 2) + ((element.x2 - element.x1) * ratio) / 2;
}
};

export const elementsConfig = {
Expand Down Expand Up @@ -193,14 +191,29 @@ export const elementsConfig = {
y2: Math.max(element.y1, element.y2),
});
},
onResize: preserveAspectRatio,
onResize: (element, snapshot, event) => {
if (event.shiftKey) {
preserveAspectRatio(element, snapshot, event);
}
// Check if we have a text inside the shape
if (element.text) {
const width = Math.abs(element.x2 - element.x1);
const [textWidth, textHeight] = measureText(element.text || " ", element.textSize, element.textFont, width + "px");
element.textWidth = textWidth;
element.textHeight = textHeight;
}
},
onUpdate: (element, changedKeys) => {
if (element.text && (changedKeys.has("textFont") || changedKeys.has("textSize"))) {
const [textWidth, textHeight] = measureText(element.text || " ", element.textSize, element.textFont);
const width = Math.abs(element.x2 - element.x1);
const [textWidth, textHeight] = measureText(element.text || " ", element.textSize, element.textFont, width + "px");
element.textWidth = textWidth;
element.textHeight = textHeight;
}
},
getUpdatedFields: element => {
return element.text ? ["textWidth", "textHeight"] : [];
},
},
[ELEMENTS.ARROW]: {
displayName: "Arrow",
Expand Down Expand Up @@ -433,9 +446,10 @@ export const elementsConfig = {
},
onResize: (element, snapshot, event) => {
const handler = event.handler || "";
const width = Math.abs(element.x2 - element.x1);
const height = Math.abs(element.y2 - element.y1);
if (isCornerHandler(handler) || handler === HANDLERS.EDGE_BOTTOM || handler === HANDLERS.EDGE_TOP) {
preserveAspectRatio(element, snapshot, event);
const width = Math.abs(element.x2 - element.x1);
const height = Math.abs(element.y2 - element.y1);
let textSize = TEXT_SIZE_MIN;
while (textSize <= TEXT_SIZE_MAX) {
const size = measureText(element.text || " ", textSize, element.textFont, width + "px");
Expand All @@ -447,20 +461,13 @@ export const elementsConfig = {
element.textHeight = size[1];
textSize = textSize + TEXT_SIZE_STEP;
}
// Terrible hack to prevent having 0px text elements
if (handler === HANDLERS.EDGE_BOTTOM || handler === HANDLERS.CORNER_BOTTOM_LEFT || handler === HANDLERS.CORNER_BOTTOM_RIGHT) {
element.y2 = element.y1 + Math.max(height, element.textHeight, GRID_SIZE);
}
else {
element.y1 = element.y2 - Math.max(height, element.textHeight, GRID_SIZE);
}
}
else if (handler === HANDLERS.EDGE_LEFT || handler === HANDLERS.EDGE_RIGHT) {
const width = Math.abs(element.x2 - element.x1);
const sizes = measureText(element.text || " ", element.textSize, element.textFont, width + "px");
element.textWidth = sizes[0];
element.textHeight = sizes[1];
element.y1 = snapshot.y1;
element.y2 = element.y1 + Math.ceil(sizes[1] / GRID_SIZE) * GRID_SIZE;
element.y2 = element.y1 + element.textHeight; // fix height
}
// Terrible hack to prevent having 0px text elements
if (handler === HANDLERS.EDGE_LEFT || handler === HANDLERS.CORNER_TOP_LEFT || handler === HANDLERS.CORNER_BOTTOM_LEFT) {
Expand Down Expand Up @@ -524,7 +531,11 @@ export const elementsConfig = {
element.drawWidth = Math.abs(element.x2 - element.x1);
element.drawHeight = Math.abs(element.y2 - element.y1);
},
onResize: preserveAspectRatio,
onResize: (element, snapshot, event) => {
if (event.shiftKey) {
return preserveAspectRatio(element, snapshot, event);
}
},
},
[ELEMENTS.IMAGE]: {
displayName: "Image",
Expand All @@ -533,7 +544,11 @@ export const elementsConfig = {
[FIELDS.ASSET_ID]: "",
[FIELDS.OPACITY]: DEFAULTS.OPACITY,
}),
onResize: preserveAspectRatio,
onResize: (element, snapshot, event) => {
if (event.shiftKey) {
return preserveAspectRatio(element, snapshot, event);
}
},
},
[ELEMENTS.NOTE]: {
displayName: "Note",
Expand Down
3 changes: 2 additions & 1 deletion app/utils/math.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ export const measureText = (text, textSize, textFont, maxWidth) => {
measureText.container.style.left = "-9999px";
measureText.container.style.lineHeight = "normal"; // Set line-height as normal
measureText.container.style.whiteSpace = "pre-wrap";
measureText.container.style.wordBreak = "break-all";
// measureText.container.style.wordBreak = "keep-all";
measureText.container.style.overflowWrap = "break-word";
measureText.container.style.minHeight = "1em";
measureText.container.style.minWidth = "1em";
measureText.container.style.margin = "0";
Expand Down

0 comments on commit dfad930

Please sign in to comment.