plane/web/components/editor/lite-text-editor/toolbar.tsx
Anmol Singh Bhatia d3556f457b
chore: web app use client directive (#4747)
* chore: use client directive

* chore: use client directive
2024-06-10 15:13:10 +05:30

175 lines
5.9 KiB
TypeScript

"use client";
import React, { useEffect, useState, useCallback } from "react";
import { Globe2, Lock, LucideIcon } from "lucide-react";
// editor
import { EditorMenuItemNames } from "@plane/document-editor";
import { EditorRefApi } from "@plane/lite-text-editor";
// ui
import { Button, Tooltip } from "@plane/ui";
// constants
import { TOOLBAR_ITEMS } from "@/constants/editor";
import { EIssueCommentAccessSpecifier } from "@/constants/issue";
// helpers
import { cn } from "@/helpers/common.helper";
type Props = {
accessSpecifier?: EIssueCommentAccessSpecifier;
executeCommand: (commandName: EditorMenuItemNames) => void;
handleAccessChange?: (accessKey: EIssueCommentAccessSpecifier) => void;
handleSubmit: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
isCommentEmpty: boolean;
isSubmitting: boolean;
showAccessSpecifier: boolean;
showSubmitButton: boolean;
editorRef: React.MutableRefObject<EditorRefApi | null> | null;
};
type TCommentAccessType = {
icon: LucideIcon;
key: EIssueCommentAccessSpecifier;
label: "Private" | "Public";
};
const COMMENT_ACCESS_SPECIFIERS: TCommentAccessType[] = [
{
icon: Lock,
key: EIssueCommentAccessSpecifier.INTERNAL,
label: "Private",
},
{
icon: Globe2,
key: EIssueCommentAccessSpecifier.EXTERNAL,
label: "Public",
},
];
const toolbarItems = TOOLBAR_ITEMS.lite;
export const IssueCommentToolbar: React.FC<Props> = (props) => {
const {
accessSpecifier,
executeCommand,
handleAccessChange,
handleSubmit,
isCommentEmpty,
isSubmitting,
showAccessSpecifier,
showSubmitButton,
editorRef,
} = props;
// State to manage active states of toolbar items
const [activeStates, setActiveStates] = useState<Record<string, boolean>>({});
// Function to update active states
const updateActiveStates = useCallback(() => {
if (editorRef?.current) {
const newActiveStates: Record<string, boolean> = {};
Object.values(toolbarItems)
.flat()
.forEach((item) => {
// Assert that editorRef.current is not null
newActiveStates[item.key] = (editorRef.current as EditorRefApi).isMenuItemActive(item.key);
});
setActiveStates(newActiveStates);
}
}, [editorRef]);
// useEffect to call updateActiveStates when isActive prop changes
useEffect(() => {
if (!editorRef?.current) return;
const unsubscribe = editorRef.current.onStateChange(updateActiveStates);
updateActiveStates();
return () => unsubscribe();
}, [editorRef, updateActiveStates]);
return (
<div className="flex h-9 w-full items-stretch gap-1.5 bg-custom-background-90 overflow-x-scroll">
{showAccessSpecifier && (
<div className="flex flex-shrink-0 items-stretch gap-0.5 rounded border-[0.5px] border-custom-border-200 p-1">
{COMMENT_ACCESS_SPECIFIERS.map((access) => {
const isAccessActive = accessSpecifier === access.key;
return (
<Tooltip key={access.key} tooltipContent={access.label}>
<button
type="button"
onClick={() => handleAccessChange?.(access.key)}
className={cn("grid place-items-center aspect-square rounded-sm p-1 hover:bg-custom-background-80", {
"bg-custom-background-80": isAccessActive,
})}
>
<access.icon
className={cn("h-3.5 w-3.5 text-custom-text-400", {
"text-custom-text-100": isAccessActive,
})}
strokeWidth={2}
/>
</button>
</Tooltip>
);
})}
</div>
)}
<div className="flex w-full items-stretch justify-between gap-2 rounded border-[0.5px] border-custom-border-200 p-1">
<div className="flex items-stretch">
{Object.keys(toolbarItems).map((key, index) => (
<div
key={key}
className={cn("flex items-stretch gap-0.5 border-r border-custom-border-200 px-2.5", {
"pl-0": index === 0,
"pr-0": index === Object.keys(toolbarItems).length - 1,
})}
>
{toolbarItems[key].map((item) => (
<Tooltip
key={item.key}
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
type="button"
onClick={() => executeCommand(item.key)}
className={cn(
"grid place-items-center aspect-square rounded-sm p-0.5 text-custom-text-400 hover:bg-custom-background-80",
{
"bg-custom-background-80 text-custom-text-100": activeStates[item.key],
}
)}
>
<item.icon
className={cn("h-3.5 w-3.5", {
"text-custom-text-100": activeStates[item.key],
})}
strokeWidth={2.5}
/>
</button>
</Tooltip>
))}
</div>
))}
</div>
{showSubmitButton && (
<div className="sticky right-1">
<Button
type="submit"
variant="primary"
className="px-2.5 py-1.5 text-xs"
onClick={handleSubmit}
disabled={isCommentEmpty}
loading={isSubmitting}
>
Comment
</Button>
</div>
)}
</div>
</div>
);
};