2024-04-11 15:58:59 +00:00
|
|
|
import React, { useEffect, useState, useCallback } from "react";
|
2024-04-29 10:34:37 +00:00
|
|
|
import { Check, ChevronDown } from "lucide-react";
|
2024-04-11 15:58:59 +00:00
|
|
|
// editor
|
|
|
|
import { EditorMenuItemNames, EditorRefApi } from "@plane/document-editor";
|
|
|
|
// ui
|
2024-04-29 10:34:37 +00:00
|
|
|
import { CustomMenu, Tooltip } from "@plane/ui";
|
2024-04-11 15:58:59 +00:00
|
|
|
// constants
|
2024-04-29 10:34:37 +00:00
|
|
|
import { TOOLBAR_ITEMS, TYPOGRAPHY_ITEMS, ToolbarMenuItem } from "@/constants/editor";
|
2024-04-11 15:58:59 +00:00
|
|
|
// helpers
|
|
|
|
import { cn } from "@/helpers/common.helper";
|
|
|
|
|
|
|
|
type Props = {
|
|
|
|
editorRef: EditorRefApi;
|
|
|
|
};
|
|
|
|
|
|
|
|
type ToolbarButtonProps = {
|
|
|
|
item: ToolbarMenuItem;
|
|
|
|
isActive: boolean;
|
|
|
|
executeCommand: (commandName: EditorMenuItemNames) => void;
|
|
|
|
};
|
|
|
|
|
|
|
|
const ToolbarButton: React.FC<ToolbarButtonProps> = React.memo((props) => {
|
|
|
|
const { item, isActive, executeCommand } = props;
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Tooltip
|
|
|
|
tooltipContent={
|
|
|
|
<p className="flex flex-col gap-1 text-center text-xs">
|
|
|
|
<span className="font-medium">{item.name}</span>
|
|
|
|
{item.shortcut && <kbd className="text-custom-text-400">{item.shortcut.join(" + ")}</kbd>}
|
|
|
|
</p>
|
|
|
|
}
|
|
|
|
>
|
|
|
|
<button
|
|
|
|
key={item.key}
|
|
|
|
type="button"
|
|
|
|
onClick={() => executeCommand(item.key)}
|
2024-04-29 10:34:37 +00:00
|
|
|
className={cn("grid size-7 place-items-center rounded text-custom-text-300 hover:bg-custom-background-80", {
|
2024-04-11 15:58:59 +00:00
|
|
|
"bg-custom-background-80 text-custom-text-100": isActive,
|
|
|
|
})}
|
|
|
|
>
|
|
|
|
<item.icon
|
2024-04-29 10:34:37 +00:00
|
|
|
className={cn("size-4", {
|
2024-04-11 15:58:59 +00:00
|
|
|
"text-custom-text-100": isActive,
|
|
|
|
})}
|
|
|
|
/>
|
|
|
|
</button>
|
|
|
|
</Tooltip>
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
ToolbarButton.displayName = "ToolbarButton";
|
|
|
|
|
|
|
|
const toolbarItems = TOOLBAR_ITEMS.document;
|
|
|
|
|
|
|
|
export const PageToolbar: React.FC<Props> = ({ editorRef }) => {
|
|
|
|
const [activeStates, setActiveStates] = useState<Record<string, boolean>>({});
|
|
|
|
|
|
|
|
const updateActiveStates = useCallback(() => {
|
|
|
|
const newActiveStates: Record<string, boolean> = {};
|
|
|
|
Object.values(toolbarItems)
|
|
|
|
.flat()
|
|
|
|
.forEach((item) => {
|
|
|
|
newActiveStates[item.key] = editorRef.isMenuItemActive(item.key);
|
|
|
|
});
|
|
|
|
setActiveStates(newActiveStates);
|
|
|
|
}, [editorRef]);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
const unsubscribe = editorRef.onStateChange(updateActiveStates);
|
|
|
|
updateActiveStates();
|
|
|
|
return () => unsubscribe();
|
|
|
|
}, [editorRef, updateActiveStates]);
|
|
|
|
|
2024-04-29 10:34:37 +00:00
|
|
|
const activeTypography = TYPOGRAPHY_ITEMS.find((item) => editorRef.isMenuItemActive(item.key));
|
|
|
|
|
2024-04-11 15:58:59 +00:00
|
|
|
return (
|
|
|
|
<div className="flex flex-wrap items-center divide-x divide-custom-border-200">
|
2024-04-29 10:34:37 +00:00
|
|
|
<CustomMenu
|
|
|
|
customButton={
|
|
|
|
<span className="text-custom-text-300 text-sm border-[0.5px] border-custom-border-300 hover:bg-custom-background-80 h-7 w-24 rounded px-2 flex items-center justify-between gap-2 whitespace-nowrap text-left">
|
|
|
|
{activeTypography?.name || "Text"}
|
|
|
|
<ChevronDown className="flex-shrink-0 size-3" />
|
|
|
|
</span>
|
|
|
|
}
|
|
|
|
className="pr-2"
|
|
|
|
placement="bottom-start"
|
|
|
|
closeOnSelect
|
|
|
|
maxHeight="lg"
|
|
|
|
>
|
|
|
|
{TYPOGRAPHY_ITEMS.map((item) => (
|
|
|
|
<CustomMenu.MenuItem
|
|
|
|
key={item.key}
|
|
|
|
className="flex items-center justify-between gap-2"
|
|
|
|
onClick={() => editorRef.executeMenuItemCommand(item.key)}
|
|
|
|
>
|
|
|
|
<span className="flex items-center gap-2">
|
|
|
|
<item.icon className="size-3" />
|
|
|
|
{item.name}
|
|
|
|
</span>
|
|
|
|
{activeTypography?.key === item.key && <Check className="size-3 text-custom-text-300 flex-shrink-0" />}
|
|
|
|
</CustomMenu.MenuItem>
|
|
|
|
))}
|
|
|
|
</CustomMenu>
|
2024-04-11 15:58:59 +00:00
|
|
|
{Object.keys(toolbarItems).map((key) => (
|
|
|
|
<div key={key} className="flex items-center gap-0.5 px-2 first:pl-0 last:pr-0">
|
|
|
|
{toolbarItems[key].map((item) => (
|
|
|
|
<ToolbarButton
|
|
|
|
key={item.key}
|
|
|
|
item={item}
|
|
|
|
isActive={activeStates[item.key]}
|
|
|
|
executeCommand={editorRef.executeMenuItemCommand}
|
|
|
|
/>
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
};
|