diff --git a/src/components/ExportDialog.css b/src/components/ExportDialog.css index b90ae906..460f70f8 100644 --- a/src/components/ExportDialog.css +++ b/src/components/ExportDialog.css @@ -44,3 +44,9 @@ justify-content: space-between; flex-wrap: wrap; } + +.ExportDialog__scales { + display: flex; + align-items: baseline; + justify-content: flex-end; +} diff --git a/src/components/ExportDialog.tsx b/src/components/ExportDialog.tsx index d9d82bdf..022585e8 100644 --- a/src/components/ExportDialog.tsx +++ b/src/components/ExportDialog.tsx @@ -18,6 +18,11 @@ const probablySupportsClipboard = "write" in navigator.clipboard && "ClipboardItem" in window; +const scales = [1, 2, 3]; +const defaultScale = scales.includes(devicePixelRatio) ? devicePixelRatio : 1; + +type ExportCB = (elements: readonly ExcalidrawElement[], scale: number) => void; + export function ExportDialog({ elements, appState, @@ -32,11 +37,12 @@ export function ExportDialog({ exportPadding?: number; actionManager: ActionsManagerInterface; syncActionResult: UpdaterFn; - onExportToPng(elements: readonly ExcalidrawElement[]): void; - onExportToClipboard(elements: readonly ExcalidrawElement[]): void; + onExportToPng: ExportCB; + onExportToClipboard: ExportCB; }) { const someElementIsSelected = elements.some(element => element.isSelected); const [modalIsShown, setModalIsShown] = useState(false); + const [scale, setScale] = useState(defaultScale); const [exportSelected, setExportSelected] = useState(someElementIsSelected); const previeRef = useRef(null); const { exportBackground, viewBackgroundColor } = appState; @@ -54,7 +60,8 @@ export function ExportDialog({ const canvas = getExportCanvasPreview(exportedElements, { exportBackground, viewBackgroundColor, - exportPadding + exportPadding, + scale }); previewNode?.appendChild(canvas); return () => { @@ -65,7 +72,8 @@ export function ExportDialog({ exportedElements, exportBackground, exportPadding, - viewBackgroundColor + viewBackgroundColor, + scale ]); function handleClose() { @@ -98,7 +106,7 @@ export function ExportDialog({ icon={downloadFile} title="Export to PNG" aria-label="Export to PNG" - onClick={() => onExportToPng(exportedElements)} + onClick={() => onExportToPng(exportedElements, scale)} /> {probablySupportsClipboard && ( @@ -107,7 +115,9 @@ export function ExportDialog({ icon={clipboard} title="Copy to clipboard" aria-label="Copy to clipboard" - onClick={() => onExportToClipboard(exportedElements)} + onClick={() => + onExportToClipboard(exportedElements, scale) + } /> )} @@ -119,6 +129,21 @@ export function ExportDialog({ syncActionResult )} +
+ + {scales.map(s => ( + setScale(s)} + /> + ))} + +
{actionManager.renderAction( "changeExportBackground", elements, diff --git a/src/components/Stack.tsx b/src/components/Stack.tsx index 7e40b4cb..f1b9c329 100644 --- a/src/components/Stack.tsx +++ b/src/components/Stack.tsx @@ -5,7 +5,7 @@ import React from "react"; type StackProps = { children: React.ReactNode; gap?: number; - align?: "start" | "center" | "end"; + align?: "start" | "center" | "end" | "baseline"; }; function RowStack({ children, gap, align }: StackProps) { diff --git a/src/components/ToolIcon.scss b/src/components/ToolIcon.scss index 64a5d8f0..f5225c4a 100644 --- a/src/components/ToolIcon.scss +++ b/src/components/ToolIcon.scss @@ -20,6 +20,12 @@ } } +.ToolIcon_size_s .ToolIcon__icon { + width: 25px; + height: 25px; + font-size: 0.8em; +} + .ToolIcon_type_button { padding: 0; border: none; diff --git a/src/components/ToolIcon.tsx b/src/components/ToolIcon.tsx index 374149eb..84a2a506 100644 --- a/src/components/ToolIcon.tsx +++ b/src/components/ToolIcon.tsx @@ -2,6 +2,8 @@ import "./ToolIcon.scss"; import React from "react"; +type ToolIconSize = "s" | "m"; + type ToolIconProps = | { type: "button"; @@ -11,6 +13,7 @@ type ToolIconProps = name?: string; id?: string; onClick?(): void; + size?: ToolIconSize; } | { type: "radio"; @@ -20,12 +23,17 @@ type ToolIconProps = id?: string; checked: boolean; onChange?(): void; + size?: ToolIconSize; }; +const DEFAULT_SIZE: ToolIconSize = "m"; + export function ToolIcon(props: ToolIconProps) { + const sizeCn = `ToolIcon_size_${props.size || DEFAULT_SIZE}`; + if (props.type === "button") return ( -