From ace031e992b3db82406b1f4a74ea2bb7f4e9b55f Mon Sep 17 00:00:00 2001 From: "Daniel J. Geiger" <1852529+DanielJGeiger@users.noreply.github.com> Date: Sat, 7 Jan 2023 15:47:19 -0600 Subject: [PATCH] Update to latest `Action` changes. --- src/actions/manager.tsx | 25 ++++++++- src/actions/types.ts | 9 --- src/components/Actions.tsx | 15 ++--- src/components/App.tsx | 56 ++++++------------- src/components/SubtypeButton.tsx | 1 + .../extensions/ts/mathjax/implementation.tsx | 17 +++--- 6 files changed, 51 insertions(+), 72 deletions(-) diff --git a/src/actions/manager.tsx b/src/actions/manager.tsx index 3aa435d99..cdc4f1f9c 100644 --- a/src/actions/manager.tsx +++ b/src/actions/manager.tsx @@ -75,7 +75,7 @@ export class ActionManager { this.app = app; } - public registerActionGuards() { + registerActionGuards() { const disablers = getActionDisablers(); for (const d in disablers) { const dName = d as ActionName; @@ -90,7 +90,7 @@ export class ActionManager { } } - private registerDisableFn(name: ActionName, disabler: DisableFn) { + registerDisableFn(name: ActionName, disabler: DisableFn) { if (!(name in this.disablers)) { this.disablers[name] = [] as DisableFn[]; } @@ -99,7 +99,7 @@ export class ActionManager { } } - private registerEnableFn(name: Action["name"], enabler: EnableFn) { + registerEnableFn(name: Action["name"], enabler: EnableFn) { if (!(name in this.enablers)) { this.enablers[name] = [] as EnableFn[]; } @@ -108,6 +108,25 @@ export class ActionManager { } } + getCustomActions(opts?: { + elements?: readonly ExcalidrawElement[]; + data?: Record; + guardsOnly?: boolean; + }): Action[] { + // For testing + if (this === undefined) { + return []; + } + const customActions: Action[] = []; + for (const key in this.actions) { + const action = this.actions[key]; + if (!isActionName(action.name) && this.isActionEnabled(action, opts)) { + customActions.push(action); + } + } + return customActions; + } + registerAction(action: Action) { this.actions[action.name] = action; } diff --git a/src/actions/types.ts b/src/actions/types.ts index 039d495a3..6d097cd72 100644 --- a/src/actions/types.ts +++ b/src/actions/types.ts @@ -147,10 +147,6 @@ export type PanelComponentProps = { export interface Action { name: string; PanelComponent?: React.FC; - panelComponentPredicate?: ( - elements: readonly ExcalidrawElement[], - appState: AppState, - ) => boolean; perform: ActionFn; keyPriority?: number; keyTest?: ( @@ -158,11 +154,6 @@ export interface Action { appState: AppState, elements: readonly ExcalidrawElement[], ) => boolean; - shapeConfigPredicate?: ( - elements: readonly ExcalidrawElement[], - appState: AppState, - data?: Record, - ) => boolean; contextItemLabel?: | string | (( diff --git a/src/components/Actions.tsx b/src/components/Actions.tsx index d895c295c..6afc487f1 100644 --- a/src/components/Actions.tsx +++ b/src/components/Actions.tsx @@ -3,7 +3,7 @@ import { ActionManager } from "../actions/manager"; import { getNonDeletedElements } from "../element"; import { ExcalidrawElement, PointerType } from "../element/types"; import { t } from "../i18n"; -import { useDevice } from "../components/App"; +import { useDevice, useExcalidrawActionManager } from "../components/App"; import { canChangeRoundness, canHaveArrowheads, @@ -28,7 +28,6 @@ import { trackEvent } from "../analytics"; import { hasBoundTextElement } from "../element/typeChecks"; import clsx from "clsx"; import { actionToggleZenMode } from "../actions"; -import { getCustomActions } from "../actions/register"; import "./Actions.scss"; import { Tooltip } from "./Tooltip"; import { shouldAllowVerticalAlign } from "../element/textElement"; @@ -93,15 +92,9 @@ export const SelectedShapeActions = ({ {showChangeBackgroundIcons && (
{renderAction("changeBackgroundColor")}
)} - {getCustomActions().map((action) => { - if ( - action.panelComponentPredicate && - action.panelComponentPredicate(targetElements, appState) - ) { - return renderAction(action.name); - } - return null; - })} + {useExcalidrawActionManager() + .getCustomActions({ elements: targetElements }) + .map((action) => renderAction(action.name))} {showFillIcons && renderAction("changeFillStyle")} {(hasStrokeWidth(appState.activeTool.type) || diff --git a/src/components/App.tsx b/src/components/App.tsx index 21db3f51d..a7fda3f5e 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -38,7 +38,7 @@ import { } from "../actions"; import { createRedoAction, createUndoAction } from "../actions/actionHistory"; import { ActionManager } from "../actions/manager"; -import { getActions, getCustomActions } from "../actions/register"; +import { getActions } from "../actions/register"; import { ActionResult } from "../actions/types"; import { trackEvent } from "../analytics"; import { getDefaultAppState, isEraserActive } from "../appState"; @@ -430,19 +430,12 @@ class App extends React.Component { this.id = nanoid(); this.library = new Library(this); - this.scene = new Scene(); - this.fonts = new Fonts({ - scene: this.scene, - onSceneUpdated: this.onSceneUpdated, - }); - this.actionManager = new ActionManager( this.syncActionResult, () => this.state, () => this.scene.getElementsIncludingDeleted(), this, ); - if (excalidrawRef) { const readyPromise = ("current" in excalidrawRef && excalidrawRef.current?.readyPromise) || @@ -486,6 +479,11 @@ class App extends React.Component { id: this.id, }; + this.scene = new Scene(); + this.fonts = new Fonts({ + scene: this.scene, + onSceneUpdated: this.onSceneUpdated, + }); this.history = new History(); this.actionManager.registerAll(getActions()); @@ -621,7 +619,7 @@ class App extends React.Component { this.actionManager.renderAction( subtype, hasAlwaysEnabledActions(subtype) - ? { onContextMenu: this.handleShapeContextMenu } + ? { onContextMenu: this.handleCustomContextMenu } : {}, ), )} @@ -1368,6 +1366,7 @@ class App extends React.Component { ); cursorButton[socketId] = user.button; }); + const refresh = () => { // If a scene refresh is cued, restart the countdown. // This way we are not calling this.setState({}) once per @@ -6046,7 +6045,7 @@ class App extends React.Component { } }; - private handleShapeContextMenu = ( + private handleCustomContextMenu = ( event: React.MouseEvent, source: string, ) => { @@ -6062,7 +6061,7 @@ class App extends React.Component { contextMenu: { top, left, - items: this.getContextMenuItems("shape", source), + items: this.getContextMenuItems("custom", source), }, }); }); @@ -6239,40 +6238,17 @@ class App extends React.Component { }; private getContextMenuItems = ( - type: "canvas" | "element" | "shape", + type: "canvas" | "element" | "custom", source?: string, ): ContextMenuItems => { const options: ContextMenuItems = []; - const allElements = this.actionManager.getElementsIncludingDeleted(); - const appState = this.actionManager.getAppState(); - let addedCustom = false; - getCustomActions().forEach((action) => { - if (action.predicate && type !== "shape") { - if ( - action.predicate!( - allElements, - appState, - this.actionManager.app.props, - this.actionManager.app, - ) && - this.actionManager.isActionEnabled(action) - ) { - addedCustom = true; - options.push(action); - } - } else if (action.shapeConfigPredicate && type === "shape") { - if ( - action.shapeConfigPredicate!(allElements, appState, { source }) && - this.actionManager.isActionEnabled(action) - ) { - options.push(action); - } - } - }); - if (type === "shape") { + this.actionManager + .getCustomActions({ data: { source: source ?? "" } }) + .forEach((action) => options.push(action)); + if (type === "custom") { return options; } - if (addedCustom) { + if (options.length > 0) { options.push(CONTEXT_MENU_SEPARATOR); } diff --git a/src/components/SubtypeButton.tsx b/src/components/SubtypeButton.tsx index 8632cd4a4..dd64f2e89 100644 --- a/src/components/SubtypeButton.tsx +++ b/src/components/SubtypeButton.tsx @@ -18,6 +18,7 @@ export const SubtypeButton = ( const subtypeAction: Action = { name: subtype, trackEvent: false, + predicate: (...rest) => rest[4]?.source === subtype, perform: (elements, appState) => { const inactive = !appState.activeSubtypes?.includes(subtype) ?? true; const activeSubtypes: Subtype[] = []; diff --git a/src/packages/extensions/ts/mathjax/implementation.tsx b/src/packages/extensions/ts/mathjax/implementation.tsx index 4df8bfaa5..75121d731 100644 --- a/src/packages/extensions/ts/mathjax/implementation.tsx +++ b/src/packages/extensions/ts/mathjax/implementation.tsx @@ -1301,7 +1301,7 @@ const createMathActions = () => { const mathActions: Action[] = []; const actionUseTexTrue: Action = { name: "useTexTrue", - perform: (elements, appState, useTex: boolean | null) => { + perform: (elements, appState) => { const mathOnly = getMathProps.getMathOnly(appState); const customData = appState.customData ?? {}; customData[`${mathSubtype}`] = { useTex: true, mathOnly }; @@ -1315,13 +1315,12 @@ const createMathActions = () => { getMathProps.getUseTex(appState) ? "labels.useTexTrueActive" : "labels.useTexTrueInactive", - shapeConfigPredicate: (elements, appState, data) => - data?.source === mathSubtype, + predicate: (...rest) => rest.length < 5 || rest[4]?.source === mathSubtype, trackEvent: false, }; const actionUseTexFalse: Action = { - name: "useTexTrue", - perform: (elements, appState, useTex: boolean | null) => { + name: "useTexFalse", + perform: (elements, appState) => { const mathOnly = getMathProps.getMathOnly(appState); const customData = appState.customData ?? {}; customData[`${mathSubtype}`] = { useTex: false, mathOnly }; @@ -1335,8 +1334,7 @@ const createMathActions = () => { !getMathProps.getUseTex(appState) ? "labels.useTexFalseActive" : "labels.useTexFalseInactive", - shapeConfigPredicate: (elements, appState, data) => - data?.source === mathSubtype, + predicate: (...rest) => rest.length < 5 || rest[4]?.source === mathSubtype, trackEvent: false, }; const actionResetUseTex: Action = { @@ -1464,8 +1462,9 @@ const createMathActions = () => { /> ), - panelComponentPredicate: (elements, appState) => - enableActionChangeMathProps(elements, appState), + predicate: (...rest) => + rest[4]?.source === undefined && + enableActionChangeMathProps(rest[0], rest[1]), trackEvent: false, }; const actionMath = SubtypeButton(mathSubtype, "text", mathSubtypeIcon, "M");