Skip to content

Commit

Permalink
Merge branch 'for-obsodian-fix-jumpingText' into fix-jumpingText
Browse files Browse the repository at this point in the history
  • Loading branch information
zsviczian committed Jan 22, 2022
2 parents 2aea28f + 5fe0a0a commit ef7e1e4
Show file tree
Hide file tree
Showing 29 changed files with 307 additions and 21 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@
"private": true,
"scripts": {
"build-node": "node ./scripts/build-node.js",
"build:app:docker": "REACT_APP_DISABLE_SENTRY=true react-scripts build",
"build:app": "REACT_APP_GIT_SHA=$VERCEL_GIT_COMMIT_SHA react-scripts build",
"build:app:docker": "cross-env REACT_APP_DISABLE_SENTRY=true react-scripts build",
"build:app": "cross-env REACT_APP_GIT_SHA=$VERCEL_GIT_COMMIT_SHA react-scripts build",
"build:version": "node ./scripts/build-version.js",
"build": "yarn build:app && yarn build:version",
"eject": "react-scripts eject",
Expand Down
26 changes: 19 additions & 7 deletions src/actions/actionCanvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ export const actionResetZoom = register({
const zoomValueToFitBoundsOnViewport = (
bounds: [number, number, number, number],
viewportDimensions: { width: number; height: number },
maxZoom: number = 1,
) => {
const [x1, y1, x2, y2] = bounds;
const commonBoundsWidth = x2 - x1;
Expand All @@ -194,15 +195,17 @@ const zoomValueToFitBoundsOnViewport = (
Math.floor(smallestZoomValue / ZOOM_STEP) * ZOOM_STEP;
const clampedZoomValueToFitElements = Math.min(
Math.max(zoomAdjustedToSteps, ZOOM_STEP),
1,
maxZoom,
);
return clampedZoomValueToFitElements as NormalizedZoomValue;
};

const zoomToFitElements = (
export const zoomToFitElements = (
elements: readonly ExcalidrawElement[],
appState: Readonly<AppState>,
zoomToSelection: boolean,
maxZoom: number = 1,
margin: number = 0,
) => {
const nonDeletedElements = getNonDeletedElements(elements);
const selectedElements = getSelectedElements(nonDeletedElements, appState);
Expand All @@ -212,10 +215,14 @@ const zoomToFitElements = (
? getCommonBounds(selectedElements)
: getCommonBounds(nonDeletedElements);

const zoomValue = zoomValueToFitBoundsOnViewport(commonBounds, {
width: appState.width,
height: appState.height,
});
const zoomValue = zoomValueToFitBoundsOnViewport(
commonBounds,
{
width: appState.width - appState.width * margin,
height: appState.height - appState.height * margin,
},
maxZoom,
);
const newZoom = getNewZoom(zoomValue, appState.zoom, {
left: appState.offsetLeft,
top: appState.offsetTop,
Expand Down Expand Up @@ -263,7 +270,12 @@ export const actionZoomToFit = register({

export const actionToggleTheme = register({
name: "toggleTheme",
perform: (_, appState, value) => {
perform: (_, appState, value, app) => {
if (app.props.onThemeChange) {
app.props.onThemeChange(
value || (appState.theme === THEME.LIGHT ? THEME.DARK : THEME.LIGHT),
);
}
return {
appState: {
...appState,
Expand Down
7 changes: 7 additions & 0 deletions src/actions/actionProperties.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { AppState } from "../../src/types";
import { ButtonIconSelect } from "../components/ButtonIconSelect";
import { ColorPicker } from "../components/ColorPicker";
import { IconPicker } from "../components/IconPicker";
import { showFourthFont } from "../components/App";
import {
ArrowheadArrowIcon,
ArrowheadBarIcon,
Expand All @@ -16,6 +17,7 @@ import {
FontFamilyCodeIcon,
FontFamilyHandDrawnIcon,
FontFamilyNormalIcon,
FontFamilyLocalFontIcon,
FontSizeExtraLargeIcon,
FontSizeLargeIcon,
FontSizeMediumIcon,
Expand Down Expand Up @@ -663,6 +665,11 @@ export const actionChangeFontFamily = register({
text: t("labels.code"),
icon: <FontFamilyCodeIcon theme={appState.theme} />,
},
... showFourthFont ? [{
value: FONT_FAMILY.LocalFont,
text: t("labels.localFont"),
icon: <FontFamilyLocalFontIcon theme={appState.theme} />,
}] : [],
];

return (
Expand Down
1 change: 1 addition & 0 deletions src/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export {
actionResetZoom,
actionZoomToFit,
actionToggleTheme,
zoomToFitElements,
} from "./actionCanvas";

export { actionFinalize } from "./actionFinalize";
Expand Down
4 changes: 4 additions & 0 deletions src/charts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ const chartXLabels = (
backgroundColor,
...commonProps,
text: label.length > 8 ? `${label.slice(0, 5)}...` : label,
rawText: label.length > 8 ? `${label.slice(0, 5)}...` : label,
x: x + index * (BAR_WIDTH + BAR_GAP) + BAR_GAP * 2,
y: y + BAR_GAP / 2,
width: BAR_WIDTH,
Expand All @@ -211,6 +212,7 @@ const chartYLabels = (
x: x - BAR_GAP,
y: y - BAR_GAP,
text: "0",
rawText: "0",
textAlign: "right",
});

Expand All @@ -221,6 +223,7 @@ const chartYLabels = (
x: x - BAR_GAP,
y: y - BAR_HEIGHT - minYLabel.height / 2,
text: Math.max(...spreadsheet.values).toLocaleString(),
rawText: Math.max(...spreadsheet.values).toLocaleString(),
textAlign: "right",
});

Expand Down Expand Up @@ -305,6 +308,7 @@ const chartBaseElements = (
groupIds: [groupId],
...commonProps,
text: spreadsheet.title,
rawText: spreadsheet.title,
x: x + chartWidth / 2,
y: y - BAR_HEIGHT - BAR_GAP * 2 - DEFAULT_FONT_SIZE,
strokeSharpness: "sharp",
Expand Down
92 changes: 90 additions & 2 deletions src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
actionToggleStats,
actionToggleZenMode,
actionUngroup,
zoomToFitElements,
} from "../actions";
import { createRedoAction, createUndoAction } from "../actions/actionHistory";
import { ActionManager } from "../actions/manager";
Expand Down Expand Up @@ -140,6 +141,7 @@ import {
InitializedExcalidrawImageElement,
ExcalidrawImageElement,
FileId,
NonDeletedExcalidrawElement,
} from "../element/types";
import { getCenter, getDistance } from "../gesture";
import {
Expand All @@ -161,7 +163,7 @@ import {
isArrowKey,
KEYS,
} from "../keys";
import { distance2d, getGridPoint, isPathALoop } from "../math";
import { distance2d, getGridPoint, isPathALoop, rotate } from "../math";
import { renderScene } from "../renderer";
import { invalidateShapeForElement } from "../renderer/renderElement";
import {
Expand Down Expand Up @@ -238,7 +240,9 @@ import {
getBoundTextElementId,
} from "../element/textElement";
import { isHittingElementNotConsideringBoundingBox } from "../element/collision";
import { resizeSingleElement } from "../element/resizeElements";

export let showFourthFont: boolean = false;
const IsMobileContext = React.createContext(false);
export const useIsMobile = () => useContext(IsMobileContext);
const ExcalidrawContainerContext = React.createContext<{
Expand Down Expand Up @@ -339,13 +343,16 @@ class App extends React.Component<AppProps, AppState> {
clear: this.resetHistory,
},
scrollToContent: this.scrollToContent,
zoomToFit: this.zoomToFit,
getSceneElements: this.getSceneElements,
getAppState: () => this.state,
getFiles: () => this.files,
refresh: this.refresh,
importLibrary: this.importLibraryFromUrl,
setToastMessage: this.setToastMessage,
updateContainerSize: this.updateContainerSize,
id: this.id,
setLocalFont: this.setLocalFont,
} as const;
if (typeof excalidrawRef === "function") {
excalidrawRef(api);
Expand Down Expand Up @@ -1457,6 +1464,7 @@ class App extends React.Component<AppProps, AppState> {
opacity: this.state.currentItemOpacity,
strokeSharpness: this.state.currentItemStrokeSharpness,
text,
rawText: text,
fontSize: this.state.currentItemFontSize,
fontFamily: this.state.currentItemFontFamily,
textAlign: this.state.currentItemTextAlign,
Expand Down Expand Up @@ -1524,6 +1532,37 @@ class App extends React.Component<AppProps, AppState> {
});
};

zoomToFit = (
target: readonly ExcalidrawElement[] = this.scene.getElements(),
maxZoom: number = 1, //null will zoom to max based on viewport
margin: number = 0.03, //percentage of viewport width&height
) => {
if (!target) {
target = this.scene.getElements();
}
if (target.length === 0) {
maxZoom = 1;
}
this.setState(
zoomToFitElements(target, this.state, false, maxZoom, margin).appState,
);
};

updateContainerSize = withBatchedUpdates(
(containers: NonDeletedExcalidrawElement[]) => {
containers.forEach((el: ExcalidrawElement) => {
const [x, y] = rotate(
el.x + el.width,
el.y + el.height,
el.x + el.width / 2,
el.y + el.height / 2,
el.angle,
);
resizeSingleElement(el, true, el, "se", true, x, y);
});
},
);

clearToast = () => {
this.setState({ toastMessage: null });
};
Expand Down Expand Up @@ -1577,6 +1616,12 @@ class App extends React.Component<AppProps, AppState> {
},
);

public setLocalFont: ExcalidrawImperativeAPI["setLocalFont"] = (
showOnPanel: boolean,
) => {
showFourthFont = showOnPanel;
};

public updateScene = withBatchedUpdates(
<K extends keyof AppState>(sceneData: {
elements?: SceneData["elements"];
Expand Down Expand Up @@ -1912,6 +1957,7 @@ class App extends React.Component<AppProps, AppState> {
text: string,
originalText: string,
isDeleted: boolean,
rawText?: string,
) => {
this.scene.replaceAllElements([
...this.scene.getElementsIncludingDeleted().map((_element) => {
Expand All @@ -1920,13 +1966,33 @@ class App extends React.Component<AppProps, AppState> {
text,
isDeleted,
originalText,
rawText: rawText ?? originalText,
});
}
return _element;
}),
]);
};

if (isExistingElement && this.props.onBeforeTextEdit) {
const text = this.props.onBeforeTextEdit(element);
if (text) {
this.scene.replaceAllElements([
...this.scene.getElementsIncludingDeleted().map((_element) => {
if (_element.id === element.id && isTextElement(_element)) {
element = updateTextElement(_element, {
text,
isDeleted: false,
originalText: text,
});
return element;
}
return _element;
}),
]);
}
}

textWysiwyg({
id: element.id,
appState: this.state,
Expand All @@ -1952,7 +2018,19 @@ class App extends React.Component<AppProps, AppState> {
}),
onSubmit: withBatchedUpdates(({ text, viaKeyboard, originalText }) => {
const isDeleted = !text.trim();
updateElement(text, originalText, isDeleted);
const rawText = originalText; //should this be originalText??
if (this.props.onBeforeTextSubmit) {
const [updatedText, updatedOriginalText] =
this.props.onBeforeTextSubmit(
element,
text,
originalText,
isDeleted,
);
text = updatedText ?? text;
originalText = updatedOriginalText ?? originalText;
}
updateElement(text, originalText, isDeleted, rawText);
// select the created text element only if submitting via keyboard
// (when submitting via click it should act as signal to deselect)
if (!isDeleted && viaKeyboard) {
Expand Down Expand Up @@ -2163,6 +2241,7 @@ class App extends React.Component<AppProps, AppState> {
opacity: this.state.currentItemOpacity,
strokeSharpness: this.state.currentItemStrokeSharpness,
text: "",
rawText: "",
fontSize: this.state.currentItemFontSize,
fontFamily: this.state.currentItemFontFamily,
textAlign: parentCenterPosition
Expand Down Expand Up @@ -4659,6 +4738,15 @@ class App extends React.Component<AppProps, AppState> {

private handleAppOnDrop = async (event: React.DragEvent<HTMLDivElement>) => {
try {
if (this.props.onDrop) {
try {
if ((await this.props.onDrop(event)) === false) {
return;
}
} catch (e) {
console.error(e);
}
}
const file = event.dataTransfer.files[0];

if (isSupportedImageFile(file)) {
Expand Down
11 changes: 11 additions & 0 deletions src/components/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,17 @@ export const FontFamilyHandDrawnIcon = React.memo(
),
);

export const FontFamilyLocalFontIcon = React.memo(
({ theme }: { theme: Theme }) =>
createIcon(
<path
fill={iconFillColor(theme)}
d="m 151.43258,8.7863685 q -7.87381,6.7489805 -14.62279,12.3731315 0,11.248301 0,23.621433 8.99864,35.994564 13.49796,76.488447 -4.49932,7.87381 -11.2483,13.49796 -16.87245,1.12483 -29.24559,-3.37449 -6.74898,-4.49932 -10.123467,-11.2483 -7.87381,-2.24966 -15.747621,0 -6.748981,1.12483 -11.248302,5.62415 l -16.872451,16.87245 q -11.248302,2.24966 -19.122113,8.99864 -6.74898,6.74899 -6.74898,17.99729 2.24966,3.37449 4.49932,7.87381 2.24966,4.49932 2.24966,12.37313 -16.872452,31.49524 -30.3704131,65.24015 -2.2496602,23.62143 0,47.24286 6.7489811,10.12347 15.7476221,20.24694 10.123471,4.49932 20.246942,13.49797 25.871093,4.49932 53.991846,8.99864 23.621437,-5.62415 50.617357,-5.62415 3.37449,2.24966 6.74898,5.62415 0,25.87109 0,55.11667 1.12483,19.12211 5.62415,38.24423 v 61.86565 q 2.24966,5.62415 8.99864,8.99864 10.12347,0 19.12211,-3.37449 3.37449,2.24966 10.12347,2.24966 7.87382,-13.49796 17.99729,-24.74626 5.62415,-34.86973 7.87381,-70.8643 -5.62415,-40.49388 -5.62415,-82.1126 l 7.87381,-7.87381 h 16.87245 q 21.37177,-8.99864 44.9932,-13.49796 14.6228,-7.87381 30.37042,-15.74762 3.37449,-1.12483 8.99864,-1.12483 11.2483,-10.12347 23.62143,-22.4966 2.24966,-6.74898 6.74898,-12.37313 V 230.3779 q 4.49932,-14.62279 6.74898,-30.37041 4.49932,-11.2483 10.12347,-20.24694 2.24966,-17.99728 2.24966,-37.1194 -5.62415,-7.87381 -7.87381,-15.74762 -6.74898,-6.74898 -13.49796,-13.49796 -11.2483,-6.74898 -20.24694,-10.12347 -46.11804,-4.499321 -92.23607,0 -10.12347,4.49932 -22.4966,4.49932 -6.74898,-2.24966 -10.12347,-7.873811 2.24966,-23.621433 2.24966,-46.118035 -5.62415,-8.998641 -4.49932,-19.122113 Q 203.17477,22.28433 195.30095,13.285689 187.42714,3.1622179 178.4285,2.0373877 164.93054,5.4118781 151.43258,8.7863685 Z M 136.80979,175.26123 q 7.87381,2.24966 11.2483,12.37313 -1.12483,39.36905 1.12483,80.98777 4.49932,6.74898 3.37449,15.74762 -5.62415,6.74898 -12.37313,11.2483 -17.99728,1.12483 -33.74491,5.62415 -19.122108,0 -37.11939,-1.12483 -6.748981,-4.49932 -11.248301,-7.87381 -1.12483,-14.62279 1.12483,-29.24558 16.872452,-30.37042 28.120753,-62.99049 4.499321,-7.87381 11.248301,-13.49796 14.622787,-3.37449 29.245587,-5.62415 4.49932,-3.37449 8.99864,-5.62415 z M 283.0377,146.01564 q 8.99865,-1.12483 17.99729,2.24966 4.49932,2.24966 8.99864,7.87382 2.24966,8.99864 1.12483,20.24694 -6.74898,11.2483 -10.12347,22.4966 -4.49932,25.87109 -10.12348,52.86702 -24.74626,11.2483 -47.24286,24.74626 -10.12347,2.24966 -19.12211,4.49932 l -10.12347,-10.12347 q 0,-44.99321 3.37449,-84.36226 -3.37449,-13.49796 -6.74898,-23.62143 4.49932,-6.74898 12.37313,-11.24831 29.24558,-3.37449 59.61599,-5.62415 z M 638.74,93.24 766.76,221.26 488.77,499.25 374.63,511.85 c -15.28,1.69 -28.19,-11.23 -26.49,-26.51 l 12.7,-114.22 z M 845.94,74.18 785.83,14.07 c -18.75,-18.75 -49.16,-18.75 -67.91,0 l -56.55,56.55 128.02,128.02 56.55,-56.55 c 18.75,-18.76 18.75,-49.16 0,-67.91 z"
/>,
{ width: 896, height: 512 },
),
);

export const FontFamilyNormalIcon = React.memo(({ theme }: { theme: Theme }) =>
createIcon(
<>
Expand Down
1 change: 1 addition & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export const FONT_FAMILY = {
Virgil: 1,
Helvetica: 2,
Cascadia: 3,
LocalFont: 4,
};

export const THEME = {
Expand Down
1 change: 1 addition & 0 deletions src/data/restore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ const restoreElement = (
fontSize,
fontFamily,
text: element.text ?? "",
rawText: element.rawText ?? "",
baseline: element.baseline,
textAlign: element.textAlign || DEFAULT_TEXT_ALIGN,
verticalAlign: element.verticalAlign || DEFAULT_VERTICAL_ALIGN,
Expand Down
1 change: 1 addition & 0 deletions src/element/newElement.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ it("clones text element", () => {
roughness: 1,
opacity: 100,
text: "hello",
rawText: "hello",
fontSize: 20,
fontFamily: FONT_FAMILY.Virgil,
textAlign: "left",
Expand Down
Loading

0 comments on commit ef7e1e4

Please sign in to comment.