mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
Simplify custom Actions: universal Action predicates instead of
action-specific guards.
This commit is contained in:
parent
512e506798
commit
c8d4e8c421
9 changed files with 105 additions and 193 deletions
|
@ -1,25 +0,0 @@
|
|||
import { Action, ActionName, DisableFn, EnableFn } from "./types";
|
||||
|
||||
const disablers = {} as Record<ActionName, DisableFn[]>;
|
||||
const enablers = {} as Record<Action["name"], EnableFn[]>;
|
||||
|
||||
export const getActionDisablers = () => disablers;
|
||||
export const getActionEnablers = () => enablers;
|
||||
|
||||
export const registerDisableFn = (name: ActionName, disabler: DisableFn) => {
|
||||
if (!(name in disablers)) {
|
||||
disablers[name] = [] as DisableFn[];
|
||||
}
|
||||
if (!disablers[name].includes(disabler)) {
|
||||
disablers[name].push(disabler);
|
||||
}
|
||||
};
|
||||
|
||||
export const registerEnableFn = (name: Action["name"], enabler: EnableFn) => {
|
||||
if (!(name in enablers)) {
|
||||
enablers[name] = [] as EnableFn[];
|
||||
}
|
||||
if (!enablers[name].includes(enabler)) {
|
||||
enablers[name].push(enabler);
|
||||
}
|
||||
};
|
|
@ -2,15 +2,12 @@ import React from "react";
|
|||
import {
|
||||
Action,
|
||||
UpdaterFn,
|
||||
ActionName,
|
||||
ActionResult,
|
||||
PanelComponentProps,
|
||||
ActionSource,
|
||||
DisableFn,
|
||||
EnableFn,
|
||||
ActionPredicateFn,
|
||||
isActionName,
|
||||
} from "./types";
|
||||
import { getActionDisablers, getActionEnablers } from "./guards";
|
||||
import { ExcalidrawElement } from "../element/types";
|
||||
import { AppClassProperties, AppState } from "../types";
|
||||
import { trackEvent } from "../analytics";
|
||||
|
@ -44,10 +41,8 @@ const trackAction = (
|
|||
};
|
||||
|
||||
export class ActionManager {
|
||||
actions = {} as Record<ActionName | Action["name"], Action>;
|
||||
|
||||
disablers = {} as Record<ActionName, DisableFn[]>;
|
||||
enablers = {} as Record<Action["name"], EnableFn[]>;
|
||||
actions = {} as Record<Action["name"], Action>;
|
||||
actionPredicates = [] as ActionPredicateFn[];
|
||||
|
||||
updater: (actionResult: ActionResult | Promise<ActionResult>) => void;
|
||||
|
||||
|
@ -75,36 +70,9 @@ export class ActionManager {
|
|||
this.app = app;
|
||||
}
|
||||
|
||||
registerActionGuards() {
|
||||
const disablers = getActionDisablers();
|
||||
for (const d in disablers) {
|
||||
const dName = d as ActionName;
|
||||
disablers[dName].forEach((disabler) =>
|
||||
this.registerDisableFn(dName, disabler),
|
||||
);
|
||||
}
|
||||
const enablers = getActionEnablers();
|
||||
for (const e in enablers) {
|
||||
const eName = e as Action["name"];
|
||||
enablers[e].forEach((enabler) => this.registerEnableFn(eName, enabler));
|
||||
}
|
||||
}
|
||||
|
||||
registerDisableFn(name: ActionName, disabler: DisableFn) {
|
||||
if (!(name in this.disablers)) {
|
||||
this.disablers[name] = [] as DisableFn[];
|
||||
}
|
||||
if (!this.disablers[name].includes(disabler)) {
|
||||
this.disablers[name].push(disabler);
|
||||
}
|
||||
}
|
||||
|
||||
registerEnableFn(name: Action["name"], enabler: EnableFn) {
|
||||
if (!(name in this.enablers)) {
|
||||
this.enablers[name] = [] as EnableFn[];
|
||||
}
|
||||
if (!this.enablers[name].includes(enabler)) {
|
||||
this.enablers[name].push(enabler);
|
||||
registerActionPredicate(predicate: ActionPredicateFn) {
|
||||
if (!this.actionPredicates.includes(predicate)) {
|
||||
this.actionPredicates.push(predicate);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -196,10 +164,7 @@ export class ActionManager {
|
|||
/**
|
||||
* @param data additional data sent to the PanelComponent
|
||||
*/
|
||||
renderAction = (
|
||||
name: ActionName | Action["name"],
|
||||
data?: PanelComponentProps["data"],
|
||||
) => {
|
||||
renderAction = (name: Action["name"], data?: PanelComponentProps["data"]) => {
|
||||
const canvasActions = this.app.props.UIOptions.canvasActions;
|
||||
|
||||
if (
|
||||
|
@ -243,7 +208,7 @@ export class ActionManager {
|
|||
};
|
||||
|
||||
isActionEnabled = (
|
||||
action: Action | ActionName,
|
||||
action: Action,
|
||||
opts?: {
|
||||
elements?: readonly ExcalidrawElement[];
|
||||
data?: Record<string, any>;
|
||||
|
@ -254,29 +219,19 @@ export class ActionManager {
|
|||
const appState = this.getAppState();
|
||||
const data = opts?.data;
|
||||
|
||||
const _action = isActionName(action) ? this.actions[action] : action;
|
||||
|
||||
if (
|
||||
!opts?.guardsOnly &&
|
||||
_action.predicate &&
|
||||
!_action.predicate(elements, appState, this.app.props, this.app, data)
|
||||
action.predicate &&
|
||||
!action.predicate(elements, appState, this.app.props, this.app, data)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isActionName(_action.name)) {
|
||||
return !(
|
||||
_action.name in this.disablers &&
|
||||
this.disablers[_action.name].some((fn) =>
|
||||
fn(elements, appState, _action.name as ActionName),
|
||||
)
|
||||
);
|
||||
}
|
||||
return (
|
||||
_action.name in this.enablers &&
|
||||
this.enablers[_action.name].some((fn) =>
|
||||
fn(elements, appState, _action.name),
|
||||
)
|
||||
);
|
||||
let enabled = true;
|
||||
this.actionPredicates.forEach((fn) => {
|
||||
if (!fn(action, elements, appState, data)) {
|
||||
enabled = false;
|
||||
}
|
||||
});
|
||||
return enabled;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
import { Action, isActionName } from "./types";
|
||||
import { Action } from "./types";
|
||||
|
||||
let actions: readonly Action[] = [];
|
||||
let customActions: readonly Action[] = [];
|
||||
export const getCustomActions = () => customActions;
|
||||
export const getActions = () => actions;
|
||||
export let actions: readonly Action[] = [];
|
||||
|
||||
export const register = <T extends Action>(action: T) => {
|
||||
if (!isActionName(action.name)) {
|
||||
customActions = customActions.concat(action);
|
||||
}
|
||||
actions = actions.concat(action);
|
||||
return action as T & {
|
||||
keyTest?: unknown extends T["keyTest"] ? never : T["keyTest"];
|
||||
|
|
|
@ -31,20 +31,13 @@ type ActionFn = (
|
|||
app: AppClassProperties,
|
||||
) => ActionResult | Promise<ActionResult>;
|
||||
|
||||
// Return `true` to indicate the standard Action with name `actionName`
|
||||
// should be disabled given `elements` and `appState`.
|
||||
export type DisableFn = (
|
||||
// Return `true` *unless* `Action` should be disabled
|
||||
// given `elements`, `appState`, and optionally `data`.
|
||||
export type ActionPredicateFn = (
|
||||
action: Action,
|
||||
elements: readonly ExcalidrawElement[],
|
||||
appState: AppState,
|
||||
actionName: ActionName,
|
||||
) => boolean;
|
||||
|
||||
// Return `true` to indicate the custom Action with name `actionName`
|
||||
// should be enabled given `elements` and `appState`.
|
||||
export type EnableFn = (
|
||||
elements: readonly ExcalidrawElement[],
|
||||
appState: AppState,
|
||||
actionName: Action["name"],
|
||||
data?: Record<string, any>,
|
||||
) => boolean;
|
||||
|
||||
export type UpdaterFn = (res: ActionResult) => void;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue