excalidraw/src/actions/manager.tsx

99 lines
2.5 KiB
TypeScript
Raw Normal View History

import React from "react";
import {
Action,
ActionsManagerInterface,
UpdaterFn,
2020-01-24 12:04:54 +02:00
ActionFilterFn,
} from "./types";
import { ExcalidrawElement } from "../element/types";
import { AppState } from "../types";
import { TFunction } from "i18next";
export class ActionManager implements ActionsManagerInterface {
actions: { [keyProp: string]: Action } = {};
updater:
| ((elements: ExcalidrawElement[], appState: AppState) => void)
| null = null;
setUpdater(
2020-01-24 12:04:54 +02:00
updater: (elements: ExcalidrawElement[], appState: AppState) => void,
) {
this.updater = updater;
}
registerAction(action: Action) {
this.actions[action.name] = action;
}
handleKeyDown(
event: KeyboardEvent,
elements: readonly ExcalidrawElement[],
2020-01-24 12:04:54 +02:00
appState: AppState,
) {
const data = Object.values(this.actions)
.sort((a, b) => (b.keyPriority || 0) - (a.keyPriority || 0))
.filter(
Feature: Multi Point Arrows (#338) * Add points to arrow on double click * Use line generator instead of path to generate line segments * Switch color of the circle when it is on an existing point in the segment * Check point against both ends of the line segment to find collinearity * Keep drawing the arrow based on mouse position until shape is changed * Always select the arrow when in multi element mode * Use curves instead of lines when drawing arrow points * Add basic collision detection with some debug points * Use roughjs shape when performing hit testing * Draw proper handler rectangles for arrows * Add argument to renderScene in export * Globally resize all points on the arrow when bounds are resized * Hide handler rectangles if an arrow has no size - Allow continuing adding arrows when selected element is deleted * Add dragging functionality to arrows * Add SHIFT functionality to two point arrows - Fix arrow positions when scrolling - Revert the element back to selection when not in multi select mode * Clean app state for export (JSON) * Set curve options manually instead of using global options - For some reason, this fixed the flickering issue in all shapes when arrows are rendered * Set proper options for the arrow * Increaase accuracy of hit testing arrows - Additionally, skip testing if point is outside the domain of arrow and each curve * Calculate bounding box of arrow based on roughjs curves - Remove domain check per curve * Change bounding box threshold to 10 and remove unnecessary code * Fix handler rectangles for 2 and multi point arrows - Fix margins of handler rectangles when using arrows - Show handler rectangles in endpoints of 2-point arrows * Remove unnecessary values from app state for export * Use `resetTransform` instead of "retranslating" canvas space after each element rendering * Allow resizing 2-point arrows - Fix position of one of the handler rectangles * refactor variable initialization * Refactored to extract out mult-point generation to the abstracted function * prevent dragging on arrow creation if under threshold * Finalize selection during multi element mode when ENTER or ESC is clicked * Set dragging element to null when finalizing * Remove pathSegmentCircle from code * Check if element is any "non-value" instead of NULL * Show two points on any two point arrow and fix visibility of arrows during scroll * Resume recording when done with drawing - When deleting a multi select element, revert back to selection element type * Resize arrow starting points perfectly * Fix direction of arrow resize based for NW * Resume recording history when there is more than one arrow * Set dragging element to NULL when element is not locked * Blur active element when finalizing * Disable undo/redo for multielement, editingelement, and resizing element - Allow undoing parts of the arrow * Disable element visibility for arrow * Use points array for arrow bounds when bezier curve shape is not available Co-authored-by: David Luzar <luzar.david@gmail.com> Co-authored-by: Preet <833927+pshihn@users.noreply.github.com>
2020-01-31 21:16:33 +04:00
action => action.keyTest && action.keyTest(event, appState, elements),
);
if (data.length === 0) return null;
event.preventDefault();
return data[0].perform(elements, appState, null);
}
getContextMenuItems(
elements: readonly ExcalidrawElement[],
appState: AppState,
updater: UpdaterFn,
actionFilter: ActionFilterFn = action => action,
2020-01-24 12:04:54 +02:00
t?: TFunction,
) {
return Object.values(this.actions)
.filter(actionFilter)
.filter(action => "contextItemLabel" in action)
.sort(
(a, b) =>
(a.contextMenuOrder !== undefined ? a.contextMenuOrder : 999) -
2020-01-24 12:04:54 +02:00
(b.contextMenuOrder !== undefined ? b.contextMenuOrder : 999),
)
.map(action => ({
label:
t && action.contextItemLabel
? t(action.contextItemLabel)
: action.contextItemLabel!,
action: () => {
updater(action.perform(elements, appState, null));
2020-01-24 12:04:54 +02:00
},
}));
}
renderAction(
name: string,
elements: readonly ExcalidrawElement[],
appState: AppState,
updater: UpdaterFn,
2020-01-24 12:04:54 +02:00
t: TFunction,
) {
if (this.actions[name] && "PanelComponent" in this.actions[name]) {
const action = this.actions[name];
const PanelComponent = action.PanelComponent!;
const updateData = (formState: any) => {
updater(action.perform(elements, appState, formState));
};
return (
<PanelComponent
elements={elements}
appState={appState}
updateData={updateData}
t={t}
/>
);
}
return null;
}
}