fix preferred tool

This commit is contained in:
Ryan Di
2025-09-25 18:31:51 +10:00
parent f6c8880f2e
commit 9215b2043d
7 changed files with 36 additions and 38 deletions

View File

@@ -11,7 +11,6 @@ import {
THEME, THEME,
DEFAULT_GRID_STEP, DEFAULT_GRID_STEP,
isTestEnv, isTestEnv,
isMobileOrTablet,
} from "@excalidraw/common"; } from "@excalidraw/common";
import type { AppState, NormalizedZoomValue } from "./types"; import type { AppState, NormalizedZoomValue } from "./types";
@@ -56,7 +55,7 @@ export const getDefaultAppState = (): Omit<
fromSelection: false, fromSelection: false,
lastActiveTool: null, lastActiveTool: null,
}, },
preferredSelectionTool: isMobileOrTablet() ? "lasso" : "selection", preferredSelectionTool: "selection",
penMode: false, penMode: false,
penDetected: false, penDetected: false,
errorMessage: null, errorMessage: null,

View File

@@ -1034,12 +1034,12 @@ export const MobileShapeActions = ({
export const ShapesSwitcher = ({ export const ShapesSwitcher = ({
activeTool, activeTool,
appState, setAppState,
app, app,
UIOptions, UIOptions,
}: { }: {
activeTool: UIAppState["activeTool"]; activeTool: UIAppState["activeTool"];
appState: UIAppState; setAppState: React.Component<any, AppState>["setState"];
app: AppClassProperties; app: AppClassProperties;
UIOptions: AppProps["UIOptions"]; UIOptions: AppProps["UIOptions"];
}) => { }) => {
@@ -1061,7 +1061,9 @@ export const ShapesSwitcher = ({
const frameToolSelected = activeTool.type === "frame"; const frameToolSelected = activeTool.type === "frame";
const laserToolSelected = activeTool.type === "laser"; const laserToolSelected = activeTool.type === "laser";
const lassoToolSelected = const lassoToolSelected =
activeTool.type === "lasso" && app.state.preferredSelectionTool !== "lasso"; app.state.stylesPanelMode === "full" &&
activeTool.type === "lasso" &&
app.state.preferredSelectionTool !== "lasso";
const embeddableToolSelected = activeTool.type === "embeddable"; const embeddableToolSelected = activeTool.type === "embeddable";
@@ -1092,14 +1094,14 @@ export const ShapesSwitcher = ({
// use a ToolPopover for selection/lasso toggle as well // use a ToolPopover for selection/lasso toggle as well
if ( if (
(value === "selection" || value === "lasso") && (value === "selection" || value === "lasso") &&
appState.stylesPanelMode === "compact" app.state.stylesPanelMode === "compact"
) { ) {
return ( return (
<ToolPopover <ToolPopover
key={"selection-popover"} key={"selection-popover"}
app={app} app={app}
options={SELECTION_TOOLS} options={SELECTION_TOOLS}
activeTool={activeTool as any} activeTool={activeTool}
defaultOption={app.state.preferredSelectionTool} defaultOption={app.state.preferredSelectionTool}
namePrefix="selectionType" namePrefix="selectionType"
title={capitalizeString(t("toolBar.selection"))} title={capitalizeString(t("toolBar.selection"))}
@@ -1107,7 +1109,9 @@ export const ShapesSwitcher = ({
onToolChange={(type: string) => { onToolChange={(type: string) => {
if (type === "selection" || type === "lasso") { if (type === "selection" || type === "lasso") {
app.setActiveTool({ type }); app.setActiveTool({ type });
app.state.preferredSelectionTool = type as any; setAppState({
preferredSelectionTool: type,
});
} }
}} }}
displayedOption={ displayedOption={
@@ -1115,9 +1119,7 @@ export const ShapesSwitcher = ({
(tool) => tool.type === app.state.preferredSelectionTool, (tool) => tool.type === app.state.preferredSelectionTool,
) || SELECTION_TOOLS[0] ) || SELECTION_TOOLS[0]
} }
isActive={ fillable={activeTool.type === "selection"}
activeTool.type === "selection" || activeTool.type === "lasso"
}
/> />
); );
} }
@@ -1136,12 +1138,12 @@ export const ShapesSwitcher = ({
aria-keyshortcuts={shortcut} aria-keyshortcuts={shortcut}
data-testid={`toolbar-${value}`} data-testid={`toolbar-${value}`}
onPointerDown={({ pointerType }) => { onPointerDown={({ pointerType }) => {
if (!appState.penDetected && pointerType === "pen") { if (!app.state.penDetected && pointerType === "pen") {
app.togglePenMode(true); app.togglePenMode(true);
} }
if (value === "selection") { if (value === "selection") {
if (appState.activeTool.type === "selection") { if (app.state.activeTool.type === "selection") {
app.setActiveTool({ type: "lasso" }); app.setActiveTool({ type: "lasso" });
} else { } else {
app.setActiveTool({ type: "selection" }); app.setActiveTool({ type: "selection" });
@@ -1149,7 +1151,7 @@ export const ShapesSwitcher = ({
} }
}} }}
onChange={({ pointerType }) => { onChange={({ pointerType }) => {
if (appState.activeTool.type !== value) { if (app.state.activeTool.type !== value) {
trackEvent("toolbar", value, "ui"); trackEvent("toolbar", value, "ui");
} }
if (value === "image") { if (value === "image") {
@@ -1222,7 +1224,7 @@ export const ShapesSwitcher = ({
> >
{t("toolBar.laser")} {t("toolBar.laser")}
</DropdownMenu.Item> </DropdownMenu.Item>
{appState.stylesPanelMode === "full" && ( {app.state.stylesPanelMode === "full" && (
<DropdownMenu.Item <DropdownMenu.Item
onSelect={() => app.setActiveTool({ type: "lasso" })} onSelect={() => app.setActiveTool({ type: "lasso" })}
icon={LassoIcon} icon={LassoIcon}

View File

@@ -2375,12 +2375,13 @@ class App extends React.Component<AppProps, AppState> {
activeTool.type === "selection" activeTool.type === "selection"
? { ? {
...activeTool, ...activeTool,
type: this.state.preferredSelectionTool, type: scene.appState.preferredSelectionTool,
} }
: scene.appState.activeTool, : scene.appState.activeTool,
isLoading: false, isLoading: false,
toast: this.state.toast, toast: this.state.toast,
}; };
if (initialData?.scrollToContent) { if (initialData?.scrollToContent) {
scene.appState = { scene.appState = {
...scene.appState, ...scene.appState,

View File

@@ -368,7 +368,7 @@ const LayerUI = ({
/> />
<ShapesSwitcher <ShapesSwitcher
appState={appState} setAppState={setAppState}
activeTool={appState.activeTool} activeTool={appState.activeTool}
UIOptions={UIOptions} UIOptions={UIOptions}
app={app} app={app}

View File

@@ -97,9 +97,9 @@ export const MobileMenu = ({
const renderToolbar = () => { const renderToolbar = () => {
return ( return (
<MobileToolBar <MobileToolBar
appState={appState}
app={app} app={app}
onHandToolToggle={onHandToolToggle} onHandToolToggle={onHandToolToggle}
setAppState={setAppState}
/> />
); );
}; };

View File

@@ -82,17 +82,17 @@ const LINEAR_ELEMENT_TOOLS = [
] as const; ] as const;
type MobileToolBarProps = { type MobileToolBarProps = {
appState: UIAppState;
app: AppClassProperties; app: AppClassProperties;
onHandToolToggle: () => void; onHandToolToggle: () => void;
setAppState: React.Component<any, UIAppState>["setState"];
}; };
export const MobileToolBar = ({ export const MobileToolBar = ({
appState,
app, app,
onHandToolToggle, onHandToolToggle,
setAppState,
}: MobileToolBarProps) => { }: MobileToolBarProps) => {
const activeTool = appState.activeTool; const activeTool = app.state.activeTool;
const [isOtherShapesMenuOpen, setIsOtherShapesMenuOpen] = useState(false); const [isOtherShapesMenuOpen, setIsOtherShapesMenuOpen] = useState(false);
const [lastActiveGenericShape, setLastActiveGenericShape] = useState< const [lastActiveGenericShape, setLastActiveGenericShape] = useState<
"rectangle" | "diamond" | "ellipse" "rectangle" | "diamond" | "ellipse"
@@ -128,12 +128,12 @@ export const MobileToolBar = ({
const { TTDDialogTriggerTunnel } = useTunnels(); const { TTDDialogTriggerTunnel } = useTunnels();
const handleToolChange = (toolType: string, pointerType?: string) => { const handleToolChange = (toolType: string, pointerType?: string) => {
if (appState.activeTool.type !== toolType) { if (app.state.activeTool.type !== toolType) {
trackEvent("toolbar", toolType, "ui"); trackEvent("toolbar", toolType, "ui");
} }
if (toolType === "selection") { if (toolType === "selection") {
if (appState.activeTool.type === "selection") { if (app.state.activeTool.type === "selection") {
// Toggle selection tool behavior if needed // Toggle selection tool behavior if needed
} else { } else {
app.setActiveTool({ type: "selection" }); app.setActiveTool({ type: "selection" });
@@ -172,17 +172,17 @@ export const MobileToolBar = ({
} }
return true; return true;
}); });
const extraToolSelected = extraTools.includes(appState.activeTool.type); const extraToolSelected = extraTools.includes(activeTool.type);
const extraIcon = extraToolSelected const extraIcon = extraToolSelected
? appState.activeTool.type === "frame" ? activeTool.type === "frame"
? frameToolIcon ? frameToolIcon
: appState.activeTool.type === "embeddable" : activeTool.type === "embeddable"
? EmbedIcon ? EmbedIcon
: appState.activeTool.type === "laser" : activeTool.type === "laser"
? laserPointerToolIcon ? laserPointerToolIcon
: appState.activeTool.type === "text" : activeTool.type === "text"
? TextIcon ? TextIcon
: appState.activeTool.type === "magicframe" : activeTool.type === "magicframe"
? MagicIcon ? MagicIcon
: extraToolsIcon : extraToolsIcon
: extraToolsIcon; : extraToolsIcon;
@@ -191,7 +191,7 @@ export const MobileToolBar = ({
<div className="mobile-toolbar" ref={toolbarRef}> <div className="mobile-toolbar" ref={toolbarRef}>
{/* Hand Tool */} {/* Hand Tool */}
<HandButton <HandButton
checked={isHandToolActive(appState)} checked={isHandToolActive(app.state)}
onChange={onHandToolToggle} onChange={onHandToolToggle}
title={t("toolBar.hand")} title={t("toolBar.hand")}
isMobile isMobile
@@ -209,7 +209,9 @@ export const MobileToolBar = ({
onToolChange={(type: string) => { onToolChange={(type: string) => {
if (type === "selection" || type === "lasso") { if (type === "selection" || type === "lasso") {
app.setActiveTool({ type }); app.setActiveTool({ type });
app.state.preferredSelectionTool = type; setAppState({
preferredSelectionTool: type,
});
} }
}} }}
displayedOption={ displayedOption={
@@ -217,9 +219,6 @@ export const MobileToolBar = ({
(tool) => tool.type === app.state.preferredSelectionTool, (tool) => tool.type === app.state.preferredSelectionTool,
) || SELECTION_TOOLS[0] ) || SELECTION_TOOLS[0]
} }
isActive={
activeTool.type === "selection" || activeTool.type === "lasso"
}
/> />
{/* Free Draw */} {/* Free Draw */}
@@ -285,7 +284,6 @@ export const MobileToolBar = ({
SHAPE_TOOLS.find((tool) => tool.type === lastActiveGenericShape) || SHAPE_TOOLS.find((tool) => tool.type === lastActiveGenericShape) ||
SHAPE_TOOLS[0] SHAPE_TOOLS[0]
} }
isActive={["rectangle", "diamond", "ellipse"].includes(activeTool.type)}
/> />
{/* Arrow/Line */} {/* Arrow/Line */}
@@ -315,7 +313,6 @@ export const MobileToolBar = ({
(tool) => tool.type === lastActiveLinearElement, (tool) => tool.type === lastActiveLinearElement,
) || LINEAR_ELEMENT_TOOLS[0] ) || LINEAR_ELEMENT_TOOLS[0]
} }
isActive={["arrow", "line"].includes(activeTool.type)}
/> />
{/* Image */} {/* Image */}

View File

@@ -30,7 +30,6 @@ type ToolPopoverProps = {
"data-testid": string; "data-testid": string;
onToolChange: (type: string) => void; onToolChange: (type: string) => void;
displayedOption: ToolOption; displayedOption: ToolOption;
isActive: boolean;
fillable?: boolean; fillable?: boolean;
}; };
@@ -44,12 +43,12 @@ export const ToolPopover = ({
title, title,
"data-testid": dataTestId, "data-testid": dataTestId,
onToolChange, onToolChange,
isActive,
displayedOption, displayedOption,
fillable = false, fillable = false,
}: ToolPopoverProps) => { }: ToolPopoverProps) => {
const [isPopupOpen, setIsPopupOpen] = useState(false); const [isPopupOpen, setIsPopupOpen] = useState(false);
const currentType = activeTool.type; const currentType = activeTool.type;
const isActive = displayedOption.type === currentType;
const SIDE_OFFSET = 32 / 2 + 10; const SIDE_OFFSET = 32 / 2 + 10;
// if currentType is not in options, close popup // if currentType is not in options, close popup