excalidraw/src/components/SubtypeButton.tsx
2023-01-07 15:47:19 -06:00

104 lines
3.4 KiB
TypeScript

import { getShortcutKey, updateActiveTool } from "../utils";
import { t } from "../i18n";
import { Action } from "../actions/types";
import { ToolButton } from "./ToolButton";
import clsx from "clsx";
import { Subtype, isValidSubtype, subtypeCollides } from "../subtypes";
import { ExcalidrawElement, Theme } from "../element/types";
export const SubtypeButton = (
subtype: Subtype,
parentType: ExcalidrawElement["type"],
icon: ({ theme }: { theme: Theme }) => JSX.Element,
key?: string,
) => {
const title = key !== undefined ? ` - ${getShortcutKey(key)}` : "";
const keyTest: Action["keyTest"] =
key !== undefined ? (event) => event.code === `Key${key}` : undefined;
const subtypeAction: Action = {
name: subtype,
trackEvent: false,
predicate: (...rest) => rest[4]?.source === subtype,
perform: (elements, appState) => {
const inactive = !appState.activeSubtypes?.includes(subtype) ?? true;
const activeSubtypes: Subtype[] = [];
if (appState.activeSubtypes) {
activeSubtypes.push(...appState.activeSubtypes);
}
let activated = false;
if (inactive) {
// Ensure `element.subtype` is well-defined
if (!subtypeCollides(subtype, activeSubtypes)) {
activeSubtypes.push(subtype);
activated = true;
}
} else {
// Can only be active if appState.activeSubtypes is defined
// and contains subtype.
activeSubtypes.splice(activeSubtypes.indexOf(subtype), 1);
}
const type =
appState.activeTool.type !== "custom" &&
isValidSubtype(subtype, appState.activeTool.type)
? appState.activeTool.type
: parentType;
const activeTool = !inactive
? appState.activeTool
: updateActiveTool(appState, { type });
const selectedElementIds = activated ? {} : appState.selectedElementIds;
const selectedGroupIds = activated ? {} : appState.selectedGroupIds;
return {
appState: {
...appState,
activeSubtypes,
selectedElementIds,
selectedGroupIds,
activeTool,
},
commitToHistory: true,
};
},
keyTest,
PanelComponent: ({ elements, appState, updateData, data }) => (
<ToolButton
type="icon"
icon={icon.call(this, { theme: appState.theme })}
selected={
appState.activeSubtypes !== undefined &&
appState.activeSubtypes.includes(subtype)
}
className={clsx({
selected:
appState.activeSubtypes &&
appState.activeSubtypes.includes(subtype),
})}
title={`${t(`toolBar.${subtype}`)}${title}`}
aria-label={t(`toolBar.${subtype}`)}
onClick={() => {
updateData(null);
}}
onContextMenu={
data && "onContextMenu" in data
? (event: React.MouseEvent) => {
if (
appState.activeSubtypes === undefined ||
(appState.activeSubtypes !== undefined &&
!appState.activeSubtypes.includes(subtype))
) {
updateData(null);
}
data.onContextMenu(event, subtype);
}
: undefined
}
size={data?.size || "medium"}
></ToolButton>
),
};
if (key === "") {
delete subtypeAction.keyTest;
}
return subtypeAction;
};