import React from "react"; import { ActionManager } from "../actions/manager"; import { getNonDeletedElements } from "../element"; import { ExcalidrawElement } from "../element/types"; import { t } from "../i18n"; import { useIsMobile } from "../components/App"; import { canChangeSharpness, canHaveArrowheads, getTargetElements, hasBackground, hasStrokeStyle, hasStrokeWidth, hasText, } from "../scene"; import { SHAPES } from "../shapes"; import { AppState, Zoom } from "../types"; import { capitalizeString, isTransparent, setCursorForShape } from "../utils"; import Stack from "./Stack"; import { ToolButton } from "./ToolButton"; export const SelectedShapeActions = ({ appState, elements, renderAction, elementType, }: { appState: AppState; elements: readonly ExcalidrawElement[]; renderAction: ActionManager["renderAction"]; elementType: ExcalidrawElement["type"]; }) => { const targetElements = getTargetElements( getNonDeletedElements(elements), appState, ); const isEditing = Boolean(appState.editingElement); const isMobile = useIsMobile(); const isRTL = document.documentElement.getAttribute("dir") === "rtl"; const showFillIcons = hasBackground(elementType) || targetElements.some( (element) => hasBackground(element.type) && !isTransparent(element.backgroundColor), ); const showChangeBackgroundIcons = hasBackground(elementType) || targetElements.some((element) => hasBackground(element.type)); return (
{renderAction("changeStrokeColor")} {showChangeBackgroundIcons && renderAction("changeBackgroundColor")} {showFillIcons && renderAction("changeFillStyle")} {(hasStrokeWidth(elementType) || targetElements.some((element) => hasStrokeWidth(element.type))) && renderAction("changeStrokeWidth")} {(elementType === "freedraw" || targetElements.some((element) => element.type === "freedraw")) && renderAction("changeStrokeShape")} {(hasStrokeStyle(elementType) || targetElements.some((element) => hasStrokeStyle(element.type))) && ( <> {renderAction("changeStrokeStyle")} {renderAction("changeSloppiness")} )} {(canChangeSharpness(elementType) || targetElements.some((element) => canChangeSharpness(element.type))) && ( <>{renderAction("changeSharpness")} )} {(hasText(elementType) || targetElements.some((element) => hasText(element.type))) && ( <> {renderAction("changeFontSize")} {renderAction("changeFontFamily")} {renderAction("changeTextAlign")} )} {(canHaveArrowheads(elementType) || targetElements.some((element) => canHaveArrowheads(element.type))) && ( <>{renderAction("changeArrowhead")} )} {renderAction("changeOpacity")}
{t("labels.layers")}
{renderAction("sendToBack")} {renderAction("sendBackward")} {renderAction("bringToFront")} {renderAction("bringForward")}
{targetElements.length > 1 && (
{t("labels.align")}
{ // swap this order for RTL so the button positions always match their action // (i.e. the leftmost button aligns left) } {isRTL ? ( <> {renderAction("alignRight")} {renderAction("alignHorizontallyCentered")} {renderAction("alignLeft")} ) : ( <> {renderAction("alignLeft")} {renderAction("alignHorizontallyCentered")} {renderAction("alignRight")} )} {targetElements.length > 2 && renderAction("distributeHorizontally")}
{renderAction("alignTop")} {renderAction("alignVerticallyCentered")} {renderAction("alignBottom")} {targetElements.length > 2 && renderAction("distributeVertically")}
)} {!isMobile && !isEditing && targetElements.length > 0 && (
{t("labels.actions")}
{renderAction("duplicateSelection")} {renderAction("deleteSelectedElements")} {renderAction("group")} {renderAction("ungroup")}
)}
); }; export const ShapesSwitcher = ({ canvas, elementType, setAppState, }: { canvas: HTMLCanvasElement | null; elementType: ExcalidrawElement["type"]; setAppState: React.Component["setState"]; }) => ( <> {SHAPES.map(({ value, icon, key }, index) => { const label = t(`toolBar.${value}`); const letter = typeof key === "string" ? key : key[0]; const shortcut = `${capitalizeString(letter)} ${t("helpDialog.or")} ${ index + 1 }`; return ( { setAppState({ elementType: value, multiElement: null, selectedElementIds: {}, }); setCursorForShape(canvas, value); setAppState({}); }} /> ); })} ); export const ZoomActions = ({ renderAction, zoom, }: { renderAction: ActionManager["renderAction"]; zoom: Zoom; }) => ( {renderAction("zoomIn")} {renderAction("zoomOut")} {renderAction("resetZoom")} );