Update to latest Action
changes.
This commit is contained in:
parent
45faf7d58f
commit
ace031e992
@ -75,7 +75,7 @@ export class ActionManager {
|
|||||||
this.app = app;
|
this.app = app;
|
||||||
}
|
}
|
||||||
|
|
||||||
public registerActionGuards() {
|
registerActionGuards() {
|
||||||
const disablers = getActionDisablers();
|
const disablers = getActionDisablers();
|
||||||
for (const d in disablers) {
|
for (const d in disablers) {
|
||||||
const dName = d as ActionName;
|
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)) {
|
if (!(name in this.disablers)) {
|
||||||
this.disablers[name] = [] as DisableFn[];
|
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)) {
|
if (!(name in this.enablers)) {
|
||||||
this.enablers[name] = [] as EnableFn[];
|
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) {
|
registerAction(action: Action) {
|
||||||
this.actions[action.name] = action;
|
this.actions[action.name] = action;
|
||||||
}
|
}
|
||||||
|
@ -147,10 +147,6 @@ export type PanelComponentProps = {
|
|||||||
export interface Action {
|
export interface Action {
|
||||||
name: string;
|
name: string;
|
||||||
PanelComponent?: React.FC<PanelComponentProps>;
|
PanelComponent?: React.FC<PanelComponentProps>;
|
||||||
panelComponentPredicate?: (
|
|
||||||
elements: readonly ExcalidrawElement[],
|
|
||||||
appState: AppState,
|
|
||||||
) => boolean;
|
|
||||||
perform: ActionFn;
|
perform: ActionFn;
|
||||||
keyPriority?: number;
|
keyPriority?: number;
|
||||||
keyTest?: (
|
keyTest?: (
|
||||||
@ -158,11 +154,6 @@ export interface Action {
|
|||||||
appState: AppState,
|
appState: AppState,
|
||||||
elements: readonly ExcalidrawElement[],
|
elements: readonly ExcalidrawElement[],
|
||||||
) => boolean;
|
) => boolean;
|
||||||
shapeConfigPredicate?: (
|
|
||||||
elements: readonly ExcalidrawElement[],
|
|
||||||
appState: AppState,
|
|
||||||
data?: Record<string, any>,
|
|
||||||
) => boolean;
|
|
||||||
contextItemLabel?:
|
contextItemLabel?:
|
||||||
| string
|
| string
|
||||||
| ((
|
| ((
|
||||||
|
@ -3,7 +3,7 @@ import { ActionManager } from "../actions/manager";
|
|||||||
import { getNonDeletedElements } from "../element";
|
import { getNonDeletedElements } from "../element";
|
||||||
import { ExcalidrawElement, PointerType } from "../element/types";
|
import { ExcalidrawElement, PointerType } from "../element/types";
|
||||||
import { t } from "../i18n";
|
import { t } from "../i18n";
|
||||||
import { useDevice } from "../components/App";
|
import { useDevice, useExcalidrawActionManager } from "../components/App";
|
||||||
import {
|
import {
|
||||||
canChangeRoundness,
|
canChangeRoundness,
|
||||||
canHaveArrowheads,
|
canHaveArrowheads,
|
||||||
@ -28,7 +28,6 @@ import { trackEvent } from "../analytics";
|
|||||||
import { hasBoundTextElement } from "../element/typeChecks";
|
import { hasBoundTextElement } from "../element/typeChecks";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { actionToggleZenMode } from "../actions";
|
import { actionToggleZenMode } from "../actions";
|
||||||
import { getCustomActions } from "../actions/register";
|
|
||||||
import "./Actions.scss";
|
import "./Actions.scss";
|
||||||
import { Tooltip } from "./Tooltip";
|
import { Tooltip } from "./Tooltip";
|
||||||
import { shouldAllowVerticalAlign } from "../element/textElement";
|
import { shouldAllowVerticalAlign } from "../element/textElement";
|
||||||
@ -93,15 +92,9 @@ export const SelectedShapeActions = ({
|
|||||||
{showChangeBackgroundIcons && (
|
{showChangeBackgroundIcons && (
|
||||||
<div>{renderAction("changeBackgroundColor")}</div>
|
<div>{renderAction("changeBackgroundColor")}</div>
|
||||||
)}
|
)}
|
||||||
{getCustomActions().map((action) => {
|
{useExcalidrawActionManager()
|
||||||
if (
|
.getCustomActions({ elements: targetElements })
|
||||||
action.panelComponentPredicate &&
|
.map((action) => renderAction(action.name))}
|
||||||
action.panelComponentPredicate(targetElements, appState)
|
|
||||||
) {
|
|
||||||
return renderAction(action.name);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
})}
|
|
||||||
{showFillIcons && renderAction("changeFillStyle")}
|
{showFillIcons && renderAction("changeFillStyle")}
|
||||||
|
|
||||||
{(hasStrokeWidth(appState.activeTool.type) ||
|
{(hasStrokeWidth(appState.activeTool.type) ||
|
||||||
|
@ -38,7 +38,7 @@ import {
|
|||||||
} from "../actions";
|
} from "../actions";
|
||||||
import { createRedoAction, createUndoAction } from "../actions/actionHistory";
|
import { createRedoAction, createUndoAction } from "../actions/actionHistory";
|
||||||
import { ActionManager } from "../actions/manager";
|
import { ActionManager } from "../actions/manager";
|
||||||
import { getActions, getCustomActions } from "../actions/register";
|
import { getActions } from "../actions/register";
|
||||||
import { ActionResult } from "../actions/types";
|
import { ActionResult } from "../actions/types";
|
||||||
import { trackEvent } from "../analytics";
|
import { trackEvent } from "../analytics";
|
||||||
import { getDefaultAppState, isEraserActive } from "../appState";
|
import { getDefaultAppState, isEraserActive } from "../appState";
|
||||||
@ -430,19 +430,12 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
this.id = nanoid();
|
this.id = nanoid();
|
||||||
|
|
||||||
this.library = new Library(this);
|
this.library = new Library(this);
|
||||||
this.scene = new Scene();
|
|
||||||
this.fonts = new Fonts({
|
|
||||||
scene: this.scene,
|
|
||||||
onSceneUpdated: this.onSceneUpdated,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.actionManager = new ActionManager(
|
this.actionManager = new ActionManager(
|
||||||
this.syncActionResult,
|
this.syncActionResult,
|
||||||
() => this.state,
|
() => this.state,
|
||||||
() => this.scene.getElementsIncludingDeleted(),
|
() => this.scene.getElementsIncludingDeleted(),
|
||||||
this,
|
this,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (excalidrawRef) {
|
if (excalidrawRef) {
|
||||||
const readyPromise =
|
const readyPromise =
|
||||||
("current" in excalidrawRef && excalidrawRef.current?.readyPromise) ||
|
("current" in excalidrawRef && excalidrawRef.current?.readyPromise) ||
|
||||||
@ -486,6 +479,11 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
id: this.id,
|
id: this.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.scene = new Scene();
|
||||||
|
this.fonts = new Fonts({
|
||||||
|
scene: this.scene,
|
||||||
|
onSceneUpdated: this.onSceneUpdated,
|
||||||
|
});
|
||||||
this.history = new History();
|
this.history = new History();
|
||||||
this.actionManager.registerAll(getActions());
|
this.actionManager.registerAll(getActions());
|
||||||
|
|
||||||
@ -621,7 +619,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
this.actionManager.renderAction(
|
this.actionManager.renderAction(
|
||||||
subtype,
|
subtype,
|
||||||
hasAlwaysEnabledActions(subtype)
|
hasAlwaysEnabledActions(subtype)
|
||||||
? { onContextMenu: this.handleShapeContextMenu }
|
? { onContextMenu: this.handleCustomContextMenu }
|
||||||
: {},
|
: {},
|
||||||
),
|
),
|
||||||
)}
|
)}
|
||||||
@ -1368,6 +1366,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
);
|
);
|
||||||
cursorButton[socketId] = user.button;
|
cursorButton[socketId] = user.button;
|
||||||
});
|
});
|
||||||
|
|
||||||
const refresh = () => {
|
const refresh = () => {
|
||||||
// If a scene refresh is cued, restart the countdown.
|
// If a scene refresh is cued, restart the countdown.
|
||||||
// This way we are not calling this.setState({}) once per
|
// 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>,
|
event: React.MouseEvent<HTMLButtonElement>,
|
||||||
source: string,
|
source: string,
|
||||||
) => {
|
) => {
|
||||||
@ -6062,7 +6061,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
contextMenu: {
|
contextMenu: {
|
||||||
top,
|
top,
|
||||||
left,
|
left,
|
||||||
items: this.getContextMenuItems("shape", source),
|
items: this.getContextMenuItems("custom", source),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -6239,40 +6238,17 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private getContextMenuItems = (
|
private getContextMenuItems = (
|
||||||
type: "canvas" | "element" | "shape",
|
type: "canvas" | "element" | "custom",
|
||||||
source?: string,
|
source?: string,
|
||||||
): ContextMenuItems => {
|
): ContextMenuItems => {
|
||||||
const options: ContextMenuItems = [];
|
const options: ContextMenuItems = [];
|
||||||
const allElements = this.actionManager.getElementsIncludingDeleted();
|
this.actionManager
|
||||||
const appState = this.actionManager.getAppState();
|
.getCustomActions({ data: { source: source ?? "" } })
|
||||||
let addedCustom = false;
|
.forEach((action) => options.push(action));
|
||||||
getCustomActions().forEach((action) => {
|
if (type === "custom") {
|
||||||
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") {
|
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
if (addedCustom) {
|
if (options.length > 0) {
|
||||||
options.push(CONTEXT_MENU_SEPARATOR);
|
options.push(CONTEXT_MENU_SEPARATOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ export const SubtypeButton = (
|
|||||||
const subtypeAction: Action = {
|
const subtypeAction: Action = {
|
||||||
name: subtype,
|
name: subtype,
|
||||||
trackEvent: false,
|
trackEvent: false,
|
||||||
|
predicate: (...rest) => rest[4]?.source === subtype,
|
||||||
perform: (elements, appState) => {
|
perform: (elements, appState) => {
|
||||||
const inactive = !appState.activeSubtypes?.includes(subtype) ?? true;
|
const inactive = !appState.activeSubtypes?.includes(subtype) ?? true;
|
||||||
const activeSubtypes: Subtype[] = [];
|
const activeSubtypes: Subtype[] = [];
|
||||||
|
@ -1301,7 +1301,7 @@ const createMathActions = () => {
|
|||||||
const mathActions: Action[] = [];
|
const mathActions: Action[] = [];
|
||||||
const actionUseTexTrue: Action = {
|
const actionUseTexTrue: Action = {
|
||||||
name: "useTexTrue",
|
name: "useTexTrue",
|
||||||
perform: (elements, appState, useTex: boolean | null) => {
|
perform: (elements, appState) => {
|
||||||
const mathOnly = getMathProps.getMathOnly(appState);
|
const mathOnly = getMathProps.getMathOnly(appState);
|
||||||
const customData = appState.customData ?? {};
|
const customData = appState.customData ?? {};
|
||||||
customData[`${mathSubtype}`] = { useTex: true, mathOnly };
|
customData[`${mathSubtype}`] = { useTex: true, mathOnly };
|
||||||
@ -1315,13 +1315,12 @@ const createMathActions = () => {
|
|||||||
getMathProps.getUseTex(appState)
|
getMathProps.getUseTex(appState)
|
||||||
? "labels.useTexTrueActive"
|
? "labels.useTexTrueActive"
|
||||||
: "labels.useTexTrueInactive",
|
: "labels.useTexTrueInactive",
|
||||||
shapeConfigPredicate: (elements, appState, data) =>
|
predicate: (...rest) => rest.length < 5 || rest[4]?.source === mathSubtype,
|
||||||
data?.source === mathSubtype,
|
|
||||||
trackEvent: false,
|
trackEvent: false,
|
||||||
};
|
};
|
||||||
const actionUseTexFalse: Action = {
|
const actionUseTexFalse: Action = {
|
||||||
name: "useTexTrue",
|
name: "useTexFalse",
|
||||||
perform: (elements, appState, useTex: boolean | null) => {
|
perform: (elements, appState) => {
|
||||||
const mathOnly = getMathProps.getMathOnly(appState);
|
const mathOnly = getMathProps.getMathOnly(appState);
|
||||||
const customData = appState.customData ?? {};
|
const customData = appState.customData ?? {};
|
||||||
customData[`${mathSubtype}`] = { useTex: false, mathOnly };
|
customData[`${mathSubtype}`] = { useTex: false, mathOnly };
|
||||||
@ -1335,8 +1334,7 @@ const createMathActions = () => {
|
|||||||
!getMathProps.getUseTex(appState)
|
!getMathProps.getUseTex(appState)
|
||||||
? "labels.useTexFalseActive"
|
? "labels.useTexFalseActive"
|
||||||
: "labels.useTexFalseInactive",
|
: "labels.useTexFalseInactive",
|
||||||
shapeConfigPredicate: (elements, appState, data) =>
|
predicate: (...rest) => rest.length < 5 || rest[4]?.source === mathSubtype,
|
||||||
data?.source === mathSubtype,
|
|
||||||
trackEvent: false,
|
trackEvent: false,
|
||||||
};
|
};
|
||||||
const actionResetUseTex: Action = {
|
const actionResetUseTex: Action = {
|
||||||
@ -1464,8 +1462,9 @@ const createMathActions = () => {
|
|||||||
/>
|
/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
),
|
),
|
||||||
panelComponentPredicate: (elements, appState) =>
|
predicate: (...rest) =>
|
||||||
enableActionChangeMathProps(elements, appState),
|
rest[4]?.source === undefined &&
|
||||||
|
enableActionChangeMathProps(rest[0], rest[1]),
|
||||||
trackEvent: false,
|
trackEvent: false,
|
||||||
};
|
};
|
||||||
const actionMath = SubtypeButton(mathSubtype, "text", mathSubtypeIcon, "M");
|
const actionMath = SubtypeButton(mathSubtype, "text", mathSubtypeIcon, "M");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user