Compare commits

..

3 Commits

Author SHA1 Message Date
zsviczian 407ee62a5c merge upstream (#5821)
* fix: hide canvas-modifying UI in view mode (#5815)

* fix: hide canvas-modifying UI in view mode

* add class for better targeting

* fix missing `key`

* fix: useOutsideClick not working in view mode

* fix: Corrected typo in toggle theme shortcut (#5813)

* fix: incorrectly selecting linear elements on creation while tool-locked (#5785)

* fix: syncing 1-point lines to remote clients (#5677)

* feat: stop deleting whole line when no point select in line editor (#5676)

* feat: stop deleting whole line when no point select in line editor

* Comments typo

Co-authored-by: DanielJGeiger <1852529+DanielJGeiger@users.noreply.github.com>

Co-authored-by: David Luzar <luzar.david@gmail.com>
Co-authored-by: Paul Yi <paulyiengr@gmail.com>
Co-authored-by: DanielJGeiger <1852529+DanielJGeiger@users.noreply.github.com>
2022-11-02 20:36:07 +01:00
zsviczian 49b74cddb9 hide export menu and save file based on UIOptions 2022-11-01 21:45:31 +01:00
zsviczian 377b9fbdff Update MobileMenu.tsx 2022-11-01 21:22:12 +01:00
9 changed files with 31 additions and 198 deletions
-22
View File
@@ -89,28 +89,6 @@ export const actionChangeExportScale = register({
},
});
export const actionChangeExportPadding = register({
name: "changeExportPadding",
trackEvent: { category: "export", action: "togglePadding" },
perform: (_elements, appState, value) => {
return {
appState: {
...appState,
exportPadding: value ? DEFAULT_EXPORT_PADDING : 0,
},
commitToHistory: false,
};
},
PanelComponent: ({ appState, updateData }) => (
<CheckboxItem
checked={!!appState.exportPadding}
onChange={(checked) => updateData(checked)}
>
{"Padding"}
</CheckboxItem>
),
});
export const actionChangeExportBackground = register({
name: "changeExportBackground",
trackEvent: { category: "export", action: "toggleBackground" },
-1
View File
@@ -68,7 +68,6 @@ export type ActionName =
| "finalize"
| "changeProjectName"
| "changeExportBackground"
| "changeExportPadding"
| "changeExportEmbedScene"
| "changeExportScale"
| "saveToActiveFile"
-3
View File
@@ -1,6 +1,5 @@
import oc from "open-color";
import {
DEFAULT_EXPORT_PADDING,
DEFAULT_FONT_FAMILY,
DEFAULT_FONT_SIZE,
DEFAULT_TEXT_ALIGN,
@@ -56,7 +55,6 @@ export const getDefaultAppState = (): Omit<
exportScale: defaultExportScale,
exportEmbedScene: false,
exportWithDarkMode: false,
exportPadding: DEFAULT_EXPORT_PADDING,
fileHandle: null,
gridSize: null,
isBindingEnabled: true,
@@ -147,7 +145,6 @@ const APP_STATE_STORAGE_CONF = (<
exportBackground: { browser: true, export: false, server: false },
exportEmbedScene: { browser: true, export: false, server: false },
exportScale: { browser: true, export: false, server: false },
exportPadding: { browser: true, export: false, server: false },
exportWithDarkMode: { browser: true, export: false, server: false },
fileHandle: { browser: false, export: false, server: false },
gridSize: { browser: true, export: true, server: true },
+7 -3
View File
@@ -79,6 +79,7 @@ const ImageExportModal = ({
elements,
appState,
files,
exportPadding = DEFAULT_EXPORT_PADDING,
actionManager,
onExportToPng,
onExportToSvg,
@@ -87,6 +88,7 @@ const ImageExportModal = ({
appState: AppState;
elements: readonly NonDeletedExcalidrawElement[];
files: BinaryFiles;
exportPadding?: number;
actionManager: ActionManager;
onExportToPng: ExportCB;
onExportToSvg: ExportCB;
@@ -114,7 +116,7 @@ const ImageExportModal = ({
exportToCanvas(exportedElements, appState, files, {
exportBackground,
viewBackgroundColor,
exportPadding: appState.exportPadding,
exportPadding,
})
.then((canvas) => {
// if converting to blob fails, there's some problem that will
@@ -132,6 +134,7 @@ const ImageExportModal = ({
files,
exportedElements,
exportBackground,
exportPadding,
viewBackgroundColor,
]);
@@ -148,10 +151,8 @@ const ImageExportModal = ({
// dunno why this is needed, but when the items wrap it creates
// an overflow
overflow: "hidden",
gap: ".6rem",
}}
>
{actionManager.renderAction("changeExportPadding")}
{actionManager.renderAction("changeExportBackground")}
{someElementIsSelected && (
<CheckboxItem
@@ -220,6 +221,7 @@ export const ImageExportDialog = ({
appState,
setAppState,
files,
exportPadding = DEFAULT_EXPORT_PADDING,
actionManager,
onExportToPng,
onExportToSvg,
@@ -229,6 +231,7 @@ export const ImageExportDialog = ({
setAppState: React.Component<any, AppState>["setState"];
elements: readonly NonDeletedExcalidrawElement[];
files: BinaryFiles;
exportPadding?: number;
actionManager: ActionManager;
onExportToPng: ExportCB;
onExportToSvg: ExportCB;
@@ -246,6 +249,7 @@ export const ImageExportDialog = ({
elements={elements}
appState={appState}
files={files}
exportPadding={exportPadding}
actionManager={actionManager}
onExportToPng={onExportToPng}
onExportToSvg={onExportToSvg}
+11 -9
View File
@@ -144,7 +144,6 @@ const LayerUI = ({
exportBackground: appState.exportBackground,
name: appState.name,
viewBackgroundColor: appState.viewBackgroundColor,
exportPadding: appState.exportPadding,
},
)
.catch(muteFSAbortError)
@@ -218,16 +217,19 @@ const LayerUI = ({
actionManager.renderAction("loadScene")}
{/* // TODO barnabasmolnar/editor-redesign */}
{/* is this fine here? */}
{appState.fileHandle &&
{UIOptions.canvasActions.saveToActiveFile &&
appState.fileHandle &&
actionManager.renderAction("saveToActiveFile")}
{renderJSONExportDialog()}
<MenuItem
label={t("buttons.exportImage")}
icon={ExportImageIcon}
dataTestId="image-export-button"
onClick={() => setAppState({ openDialog: "imageExport" })}
shortcut={getShortcutFromShortcutName("imageExport")}
/>
{UIOptions.canvasActions.export && (
<MenuItem
label={t("buttons.exportImage")}
icon={ExportImageIcon}
dataTestId="image-export-button"
onClick={() => setAppState({ openDialog: "imageExport" })}
shortcut={getShortcutFromShortcutName("imageExport")}
/>
)}
{onCollabButtonClick && (
<CollabButton
isCollaborating={isCollaborating}
+1 -1
View File
@@ -111,8 +111,8 @@ export const MobileMenu = ({
/>
</Stack.Row>
</Island>
{renderTopRightUI && renderTopRightUI(true, appState)}
<div className="mobile-misc-tools-container">
{renderTopRightUI && renderTopRightUI(true, appState)}
<PenModeButton
checked={appState.penMode}
onChange={onPenModeToggle}
+1 -1
View File
@@ -492,7 +492,7 @@ export const getElementBounds = (
export const getCommonBounds = (
elements: readonly ExcalidrawElement[],
): [minX: number, minY: number, maxX: number, maxY: number] => {
): [number, number, number, number] => {
if (!elements.length) {
return [0, 0, 0, 0];
}
+11 -157
View File
@@ -14,102 +14,6 @@ import {
export const SVG_EXPORT_TAG = `<!-- svg-source:excalidraw -->`;
const getExactBoundingBox = async (
elements: readonly NonDeletedExcalidrawElement[],
appState: {
exportBackground: boolean;
exportPadding?: number;
exportScale?: number;
viewBackgroundColor: string;
exportWithDarkMode?: boolean;
exportEmbedScene?: boolean;
},
files: BinaryFiles,
): Promise<
[offsetLeft: number, offsetTop: number, width: number, height: number]
> => {
const padding = DEFAULT_EXPORT_PADDING;
// const padding = 0;
const [minX, minY, width, height] = getApproximateCanvasSize(
elements,
padding,
);
const canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
const { imageCache } = await updateImageCache({
imageCache: new Map(),
fileIds: getInitializedImageElements(elements).map(
(element) => element.fileId,
),
files,
});
const defaultAppState = getDefaultAppState();
renderScene({
elements,
// @ts-ignore
appState,
scale: 1,
rc: rough.canvas(canvas),
canvas,
renderConfig: {
viewBackgroundColor: null,
scrollX: -minX + padding,
scrollY: -minY + padding,
zoom: defaultAppState.zoom,
remotePointerViewportCoords: {},
remoteSelectedElementIds: {},
shouldCacheIgnoreZoom: false,
remotePointerUsernames: {},
remotePointerUserStates: {},
theme: "light",
imageCache,
renderScrollbars: false,
renderSelection: false,
renderGrid: false,
isExporting: true,
},
});
const ctx = canvas.getContext("2d")!;
const { data } = ctx.getImageData(0, 0, width, height);
let _minX = Infinity;
let _minY = Infinity;
let _maxX = -Infinity;
let _maxY = -Infinity;
const rows = [];
let row: number[][] = [];
for (let i = 0; i < data.length - 1; i = i + 4) {
if (i && i % (width * 4) === 0) {
rows.push(row);
row = [];
}
const pixel = [data[i], data[i + 1], data[i + 2], data[i + 3]];
row.push(pixel);
}
for (const [y, row] of rows.entries()) {
for (const [x, pixel] of row.entries()) {
if (pixel[3] > 0) {
_minX = Math.min(_minX, x);
_minY = Math.min(_minY, y);
_maxX = Math.max(_maxX, x);
_maxY = Math.max(_maxY, y);
}
}
}
const offsetLeft = padding - _minX;
const offsetTop = padding - _minY;
return [offsetLeft, offsetTop, _maxX - _minX, _maxY - _minY];
};
export const exportToCanvas = async (
elements: readonly NonDeletedExcalidrawElement[],
appState: AppState,
@@ -133,12 +37,7 @@ export const exportToCanvas = async (
return { canvas, scale: appState.exportScale };
},
) => {
const [scrollX, scrollY, width, height] = await getCanvasSize(
elements,
appState,
files,
exportPadding,
);
const [minX, minY, width, height] = getCanvasSize(elements, exportPadding);
const { canvas, scale = 1 } = createCanvas(width, height);
@@ -160,8 +59,8 @@ export const exportToCanvas = async (
canvas,
renderConfig: {
viewBackgroundColor: exportBackground ? viewBackgroundColor : null,
scrollX,
scrollY,
scrollX: -minX + exportPadding,
scrollY: -minY + exportPadding,
zoom: defaultAppState.zoom,
remotePointerViewportCoords: {},
remoteSelectedElementIds: {},
@@ -210,12 +109,7 @@ export const exportToSvg = async (
console.error(error);
}
}
const [minX, minY, width, height] = await getCanvasSize(
elements,
appState,
files || {},
exportPadding,
);
const [minX, minY, width, height] = getCanvasSize(elements, exportPadding);
// initialize SVG root
const svgRoot = document.createElementNS(SVG_NS, "svg");
@@ -278,66 +172,26 @@ export const exportToSvg = async (
return svgRoot;
};
const getApproximateCanvasSize = (
// calculate smallest area to fit the contents in
const getCanvasSize = (
elements: readonly NonDeletedExcalidrawElement[],
exportPadding: number,
): [number, number, number, number] => {
const bounds = getCommonBounds(elements);
const minX = Math.floor(bounds[0]);
const minY = Math.floor(bounds[1]);
const maxX = Math.ceil(bounds[2]);
const maxY = Math.ceil(bounds[3]);
const [minX, minY, maxX, maxY] = getCommonBounds(elements);
const width = distance(minX, maxX) + exportPadding * 2;
const height =
Math.ceil(distance(minY, maxY)) + exportPadding + exportPadding;
const height = distance(minY, maxY) + exportPadding + exportPadding;
return [minX, minY, width, height];
};
// calculate smallest area to fit the contents in
const getCanvasSize = async (
elements: readonly NonDeletedExcalidrawElement[],
appState: {
exportBackground: boolean;
exportPadding?: number;
exportScale?: number;
viewBackgroundColor: string;
exportWithDarkMode?: boolean;
exportEmbedScene?: boolean;
},
files: BinaryFiles,
exportPadding: number,
): Promise<[number, number, number, number]> => {
if (exportPadding) {
const [minX, minY, width, height] = getApproximateCanvasSize(
elements,
exportPadding,
);
return [-minX + exportPadding, -minY + exportPadding, width, height];
} else {
const [minX, minY] = getApproximateCanvasSize(elements, exportPadding);
const [offsetLeft, offsetRight, width, height] = await getExactBoundingBox(
elements,
appState,
files,
);
return [-minX + offsetLeft, -minY + offsetRight, width, height];
}
};
export const getExportSize = (
elements: readonly NonDeletedExcalidrawElement[],
exportPadding: number,
scale: number,
): [number, number] => {
const [, , width, height] = getApproximateCanvasSize(
elements,
exportPadding,
).map((dimension) => Math.trunc(dimension * scale));
const [, , width, height] = getCanvasSize(elements, exportPadding).map(
(dimension) => Math.trunc(dimension * scale),
);
return [width, height];
};
-1
View File
@@ -113,7 +113,6 @@ export type AppState = {
exportEmbedScene: boolean;
exportWithDarkMode: boolean;
exportScale: number;
exportPadding: number;
currentItemStrokeColor: string;
currentItemBackgroundColor: string;
currentItemFillStyle: ExcalidrawElement["fillStyle"];