fix: Narrow the type of Action.name while still allowing custom names

This commit is contained in:
Daniel J. Geiger 2023-11-18 10:56:17 -06:00
parent cbb349e34b
commit 00691631d8
7 changed files with 28 additions and 16 deletions

View File

@ -2,6 +2,7 @@ import React from "react";
import { import {
Action, Action,
UpdaterFn, UpdaterFn,
ActionName,
ActionResult, ActionResult,
PanelComponentProps, PanelComponentProps,
ActionSource, ActionSource,
@ -40,7 +41,7 @@ const trackAction = (
}; };
export class ActionManager { export class ActionManager {
actions = {} as Record<Action["name"], Action>; actions = {} as Record<ActionName, Action>;
actionPredicates = [] as ActionPredicateFn[]; actionPredicates = [] as ActionPredicateFn[];
updater: (actionResult: ActionResult | Promise<ActionResult>) => void; updater: (actionResult: ActionResult | Promise<ActionResult>) => void;
@ -92,7 +93,7 @@ export class ActionManager {
const actions: Action[] = []; const actions: Action[] = [];
for (const key in this.actions) { for (const key in this.actions) {
const action = this.actions[key]; const action = this.actions[key as ActionName];
if (filter(action, elements, appState, data)) { if (filter(action, elements, appState, data)) {
actions.push(action); actions.push(action);
} }
@ -167,7 +168,7 @@ export class ActionManager {
/** /**
* @param data additional data sent to the PanelComponent * @param data additional data sent to the PanelComponent
*/ */
renderAction = (name: Action["name"], data?: PanelComponentProps["data"]) => { renderAction = (name: ActionName, data?: PanelComponentProps["data"]) => {
const canvasActions = this.app.props.UIOptions.canvasActions; const canvasActions = this.app.props.UIOptions.canvasActions;
if ( if (

View File

@ -45,6 +45,7 @@ export type UpdaterFn = (res: ActionResult) => void;
export type ActionFilterFn = (action: Action) => void; export type ActionFilterFn = (action: Action) => void;
export type ActionName = export type ActionName =
| `custom.${string}`
| "copy" | "copy"
| "cut" | "cut"
| "paste" | "paste"
@ -145,7 +146,7 @@ export type PanelComponentProps = {
}; };
export interface Action { export interface Action {
name: string; name: ActionName;
PanelComponent?: React.FC<PanelComponentProps>; PanelComponent?: React.FC<PanelComponentProps>;
perform: ActionFn; perform: ActionFn;
keyPriority?: number; keyPriority?: number;

View File

@ -28,7 +28,7 @@ export const SubtypeButton = (
const keyTest: Action["keyTest"] = const keyTest: Action["keyTest"] =
key !== undefined ? (event) => event.code === `Key${key}` : undefined; key !== undefined ? (event) => event.code === `Key${key}` : undefined;
const subtypeAction: Action = { const subtypeAction: Action = {
name: subtype, name: `custom.${subtype}`,
trackEvent: false, trackEvent: false,
predicate: (...rest) => rest[4]?.subtype === subtype, predicate: (...rest) => rest[4]?.subtype === subtype,
perform: (elements, appState) => { perform: (elements, appState) => {
@ -147,7 +147,7 @@ export const SubtypeToggles = () => {
<> <>
{getSubtypeNames().map((subtype) => {getSubtypeNames().map((subtype) =>
am.renderAction( am.renderAction(
subtype, `custom.${subtype}`,
hasAlwaysEnabledActions(subtype) ? { onContextMenu } : {}, hasAlwaysEnabledActions(subtype) ? { onContextMenu } : {},
), ),
)} )}

View File

@ -28,7 +28,7 @@ let parentTypeMap: readonly {
}[] = []; }[] = [];
let subtypeActionMap: readonly { let subtypeActionMap: readonly {
subtype: Subtype; subtype: Subtype;
actions: readonly SubtypeActionName[]; actions: readonly ActionName[];
}[] = []; }[] = [];
let disabledActionMap: readonly { let disabledActionMap: readonly {
subtype: Subtype; subtype: Subtype;
@ -91,7 +91,7 @@ const isDisabledActionName = (s: any): s is DisabledActionName =>
// by `subtype` (if `isAdded` is false)? // by `subtype` (if `isAdded` is false)?
const isForSubtype = ( const isForSubtype = (
subtype: ExcalidrawElement["subtype"], subtype: ExcalidrawElement["subtype"],
actionName: ActionName | SubtypeActionName, actionName: ActionName,
isAdded: boolean, isAdded: boolean,
) => { ) => {
const actions = isAdded ? subtypeActionMap : disabledActionMap; const actions = isAdded ? subtypeActionMap : disabledActionMap;
@ -371,7 +371,12 @@ export const prepareSubtype = (
if (record.actionNames) { if (record.actionNames) {
subtypeActionMap = [ subtypeActionMap = [
...subtypeActionMap, ...subtypeActionMap,
{ subtype, actions: record.actionNames }, {
subtype,
actions: record.actionNames.map(
(actionName) => `custom.${actionName}` as ActionName,
),
},
]; ];
} }
if (record.disabledNames) { if (record.disabledNames) {
@ -383,7 +388,12 @@ export const prepareSubtype = (
if (record.alwaysEnabledNames) { if (record.alwaysEnabledNames) {
alwaysEnabledMap = [ alwaysEnabledMap = [
...alwaysEnabledMap, ...alwaysEnabledMap,
{ subtype, actions: record.alwaysEnabledNames }, {
subtype,
actions: record.alwaysEnabledNames.map(
(actionName) => `custom.${actionName}` as ActionName,
),
},
]; ];
} }
if (record.shortcutMap) { if (record.shortcutMap) {

View File

@ -1395,7 +1395,7 @@ const enableActionChangeMathProps = (
const createMathActions = () => { const createMathActions = () => {
const mathActions: Action[] = []; const mathActions: Action[] = [];
const actionUseTexTrue: Action = { const actionUseTexTrue: Action = {
name: "useTexTrue", name: "custom.useTexTrue",
perform: (elements, appState) => { perform: (elements, appState) => {
const mathOnly = getMathProps.getMathOnly(appState); const mathOnly = getMathProps.getMathOnly(appState);
const customData = appState.customData ?? {}; const customData = appState.customData ?? {};
@ -1414,7 +1414,7 @@ const createMathActions = () => {
trackEvent: false, trackEvent: false,
}; };
const actionUseTexFalse: Action = { const actionUseTexFalse: Action = {
name: "useTexFalse", name: "custom.useTexFalse",
perform: (elements, appState) => { perform: (elements, appState) => {
const mathOnly = getMathProps.getMathOnly(appState); const mathOnly = getMathProps.getMathOnly(appState);
const customData = appState.customData ?? {}; const customData = appState.customData ?? {};
@ -1433,7 +1433,7 @@ const createMathActions = () => {
trackEvent: false, trackEvent: false,
}; };
const actionResetUseTex: Action = { const actionResetUseTex: Action = {
name: "resetUseTex", name: "custom.resetUseTex",
perform: (elements, appState) => { perform: (elements, appState) => {
const useTex = getMathProps.getUseTex(appState); const useTex = getMathProps.getUseTex(appState);
const modElements = changeProperty( const modElements = changeProperty(
@ -1481,7 +1481,7 @@ const createMathActions = () => {
trackEvent: false, trackEvent: false,
}; };
const actionChangeMathOnly: Action = { const actionChangeMathOnly: Action = {
name: "changeMathOnly", name: "custom.changeMathOnly",
perform: (elements, appState, mathOnly: boolean | null) => { perform: (elements, appState, mathOnly: boolean | null) => {
if (mathOnly === null) { if (mathOnly === null) {
mathOnly = getFormValue( mathOnly = getFormValue(

View File

@ -37,7 +37,7 @@ describe("regression tests", () => {
const el23: ExcalidrawElement[] = [el2, el3]; const el23: ExcalidrawElement[] = [el2, el3];
const el123: ExcalidrawElement[] = [el1, el2, el3]; const el123: ExcalidrawElement[] = [el1, el2, el3];
// Set up the custom Action enablers // Set up the custom Action enablers
const enableName = "custom" as Action["name"]; const enableName = "custom.enable";
const enableAction: Action = { const enableAction: Action = {
name: enableName, name: enableName,
perform: (): ActionResult => { perform: (): ActionResult => {

View File

@ -81,7 +81,7 @@ const test1: SubtypeRecord = {
}; };
const testAction: Action = { const testAction: Action = {
name: TEST_ACTION, name: `custom.${TEST_ACTION}`,
trackEvent: false, trackEvent: false,
perform: (elements, appState) => { perform: (elements, appState) => {
return { return {