Update to latest Action changes.

This commit is contained in:
Daniel J. Geiger 2023-01-07 15:47:19 -06:00
parent 45faf7d58f
commit ace031e992
6 changed files with 51 additions and 72 deletions

View File

@ -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<string, any>;
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;
}

View File

@ -147,10 +147,6 @@ export type PanelComponentProps = {
export interface Action {
name: string;
PanelComponent?: React.FC<PanelComponentProps>;
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<string, any>,
) => boolean;
contextItemLabel?:
| string
| ((

View File

@ -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 && (
<div>{renderAction("changeBackgroundColor")}</div>
)}
{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) ||

View File

@ -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<AppProps, AppState> {
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<AppProps, AppState> {
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<AppProps, AppState> {
this.actionManager.renderAction(
subtype,
hasAlwaysEnabledActions(subtype)
? { onContextMenu: this.handleShapeContextMenu }
? { onContextMenu: this.handleCustomContextMenu }
: {},
),
)}
@ -1368,6 +1366,7 @@ class App extends React.Component<AppProps, AppState> {
);
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<AppProps, AppState> {
}
};
private handleShapeContextMenu = (
private handleCustomContextMenu = (
event: React.MouseEvent<HTMLButtonElement>,
source: string,
) => {
@ -6062,7 +6061,7 @@ class App extends React.Component<AppProps, AppState> {
contextMenu: {
top,
left,
items: this.getContextMenuItems("shape", source),
items: this.getContextMenuItems("custom", source),
},
});
});
@ -6239,40 +6238,17 @@ class App extends React.Component<AppProps, AppState> {
};
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);
}

View File

@ -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[] = [];

View File

@ -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 = () => {
/>
</fieldset>
),
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");