| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- import React from "react";
- import {
- Action,
- ActionsManagerInterface,
- UpdaterFn,
- ActionName,
- ActionResult,
- } from "./types";
- import { ExcalidrawElement } from "../element/types";
- import { AppProps, AppState } from "../types";
- import { MODES } from "../constants";
- import Library from "../data/library";
- // This is the <App> component, but for now we don't care about anything but its
- // `canvas` state.
- type App = {
- canvas: HTMLCanvasElement | null;
- focusContainer: () => void;
- props: AppProps;
- library: Library;
- };
- export class ActionManager implements ActionsManagerInterface {
- actions = {} as ActionsManagerInterface["actions"];
- updater: (actionResult: ActionResult | Promise<ActionResult>) => void;
- getAppState: () => Readonly<AppState>;
- getElementsIncludingDeleted: () => readonly ExcalidrawElement[];
- app: App;
- constructor(
- updater: UpdaterFn,
- getAppState: () => AppState,
- getElementsIncludingDeleted: () => readonly ExcalidrawElement[],
- app: App,
- ) {
- this.updater = (actionResult) => {
- if (actionResult && "then" in actionResult) {
- actionResult.then((actionResult) => {
- return updater(actionResult);
- });
- } else {
- return updater(actionResult);
- }
- };
- this.getAppState = getAppState;
- this.getElementsIncludingDeleted = getElementsIncludingDeleted;
- this.app = app;
- }
- registerAction(action: Action) {
- this.actions[action.name] = action;
- }
- registerAll(actions: readonly Action[]) {
- actions.forEach((action) => this.registerAction(action));
- }
- handleKeyDown(event: React.KeyboardEvent | KeyboardEvent) {
- const canvasActions = this.app.props.UIOptions.canvasActions;
- const data = Object.values(this.actions)
- .sort((a, b) => (b.keyPriority || 0) - (a.keyPriority || 0))
- .filter(
- (action) =>
- (action.name in canvasActions
- ? canvasActions[action.name as keyof typeof canvasActions]
- : true) &&
- action.keyTest &&
- action.keyTest(
- event,
- this.getAppState(),
- this.getElementsIncludingDeleted(),
- ),
- );
- if (data.length === 0) {
- return false;
- }
- const { viewModeEnabled } = this.getAppState();
- if (viewModeEnabled) {
- if (!Object.values(MODES).includes(data[0].name)) {
- return false;
- }
- }
- event.preventDefault();
- this.updater(
- data[0].perform(
- this.getElementsIncludingDeleted(),
- this.getAppState(),
- null,
- this.app,
- ),
- );
- return true;
- }
- executeAction(action: Action) {
- this.updater(
- action.perform(
- this.getElementsIncludingDeleted(),
- this.getAppState(),
- null,
- this.app,
- ),
- );
- }
- // Id is an attribute that we can use to pass in data like keys.
- // This is needed for dynamically generated action components
- // like the user list. We can use this key to extract more
- // data from app state. This is an alternative to generic prop hell!
- renderAction = (name: ActionName, id?: string) => {
- const canvasActions = this.app.props.UIOptions.canvasActions;
- if (
- this.actions[name] &&
- "PanelComponent" in this.actions[name] &&
- (name in canvasActions
- ? canvasActions[name as keyof typeof canvasActions]
- : true)
- ) {
- const action = this.actions[name];
- const PanelComponent = action.PanelComponent!;
- const updateData = (formState?: any) => {
- this.updater(
- action.perform(
- this.getElementsIncludingDeleted(),
- this.getAppState(),
- formState,
- this.app,
- ),
- );
- };
- return (
- <PanelComponent
- elements={this.getElementsIncludingDeleted()}
- appState={this.getAppState()}
- updateData={updateData}
- id={id}
- appProps={this.app.props}
- />
- );
- }
- return null;
- };
- }
|