Files
2026-07-13 13:08:41 +08:00

188 lines
5.8 KiB
TypeScript

import type { ReactNode } from "react";
import type { SlideElement } from "@/components/slide-editor/types";
import type { TemplateFontOption } from "@/components/slide-editor/text/google-fonts";
import type { TextSelectionRange } from "@/components/slide-editor/text/text-runs";
import type { TableCellSelection } from "@/components/slide-editor/state/state";
import type { ComponentActionsMenuActions } from "@/components/slide-editor/selection/ComponentActionsMenu";
import { BulletsToolbar } from "@/components/slide-editor/text/BulletsToolbar";
import { ChartToolbar } from "@/components/slide-editor/charts/ChartToolbar";
import { DesignVariablesToolbar } from "@/components/slide-editor/toolbar/DesignVariablesToolbar";
import { ImageToolbar } from "@/components/slide-editor/images/ImageToolbar";
import { LineToolbar } from "@/components/slide-editor/shapes/LineToolbar";
import { ShapeToolbar } from "@/components/slide-editor/shapes/ShapeToolbar";
import { TableToolbar } from "@/components/slide-editor/tables/TableToolbar";
import { TextToolbar } from "@/components/slide-editor/text/TextToolbar";
type ElementToolbarProps = {
element: SlideElement;
index: number;
anchorBox?: {
x: number;
y: number;
width: number;
height: number;
} | null;
path: string;
scale: number;
componentActions?: ComponentActionsMenuActions | null;
selectedTableCell: TableCellSelection | null;
templateFonts?: TemplateFontOption[];
textSelectionRange?: TextSelectionRange | null;
onChange: (index: number, element: SlideElement, path?: string) => void;
onEditImage: (index: number, path?: string) => void;
onEditText?: (index: number, path?: string) => void;
};
type ToolbarRenderer = (props: ElementToolbarProps) => ReactNode;
const TOOLBAR_RENDERERS: Partial<
Record<SlideElement["type"], ToolbarRenderer>
> = {
text: ({
element,
anchorBox,
index,
onChange,
path,
scale,
componentActions,
templateFonts,
textSelectionRange,
}) =>
element.type === "text" ? (
<TextToolbar
element={element}
index={index}
anchorBox={anchorBox}
scale={scale}
componentActions={componentActions}
selectionRange={textSelectionRange}
templateFonts={templateFonts}
onChange={(index, element) => onChange(index, element, path)}
/>
) : null,
"text-list": ({
element,
anchorBox,
index,
onChange,
path,
scale,
componentActions,
templateFonts,
textSelectionRange,
}) =>
element.type === "text-list" ? (
<BulletsToolbar
element={element}
index={index}
anchorBox={anchorBox}
scale={scale}
componentActions={componentActions}
selectionRange={textSelectionRange}
templateFonts={templateFonts}
onChange={(index, element) => onChange(index, element, path)}
/>
) : null,
image: ({ anchorBox, element, index, onChange, onEditImage, path, scale }) =>
element.type === "image" ? (
<ImageToolbar
element={element}
index={index}
anchorBox={anchorBox}
scale={scale}
onChange={(index, element) => onChange(index, element, path)}
onUpload={(index) => onEditImage(index, path)}
/>
) : null,
rectangle: ({ anchorBox, componentActions, element, index, onChange, path, scale }) =>
element.type === "rectangle" || element.type === "ellipse" ? (
<ShapeToolbar
element={element}
index={index}
anchorBox={anchorBox}
scale={scale}
componentActions={componentActions}
onChange={(index, element) => onChange(index, element, path)}
/>
) : null,
ellipse: ({ anchorBox, componentActions, element, index, onChange, path, scale }) =>
element.type === "rectangle" || element.type === "ellipse" ? (
<ShapeToolbar
element={element}
index={index}
anchorBox={anchorBox}
scale={scale}
componentActions={componentActions}
onChange={(index, element) => onChange(index, element, path)}
/>
) : null,
line: ({ anchorBox, componentActions, element, index, onChange, path, scale }) =>
element.type === "line" ? (
<LineToolbar
element={element}
index={index}
anchorBox={anchorBox}
scale={scale}
componentActions={componentActions}
onChange={(index, element) => onChange(index, element, path)}
/>
) : null,
chart: ({ anchorBox, element, index, onChange, path, scale }) =>
element.type === "chart" ? (
<ChartToolbar
element={element}
index={index}
anchorBox={anchorBox}
scale={scale}
onChange={(index, element) => onChange(index, element, path)}
/>
) : null,
table: ({
anchorBox,
element,
index,
onChange,
path,
scale,
selectedTableCell,
}) =>
element.type === "table" ? (
<TableToolbar
element={element}
index={index}
anchorBox={anchorBox}
scale={scale}
selectedCell={
(selectedTableCell?.elementPath ??
(selectedTableCell
? String(selectedTableCell.elementIndex)
: null)) === path
? selectedTableCell
: null
}
onChange={(index, element) => onChange(index, element, path)}
/>
) : null,
};
export function ElementToolbar(props: ElementToolbarProps) {
if (props.element.design_variables?.length) {
return (
<DesignVariablesToolbar
element={props.element}
index={props.index}
anchorBox={props.anchorBox}
scale={props.scale}
onChange={(index, element) =>
props.onChange(index, element, props.path)
}
/>
);
}
const renderToolbar = TOOLBAR_RENDERERS[props.element.type];
return renderToolbar ? renderToolbar(props) : null;
}