'use client';
import * as React from 'react';
import { AIChatPlugin } from '@platejs/ai/react';
import {
BLOCK_CONTEXT_MENU_ID,
BlockMenuPlugin,
BlockSelectionPlugin,
} from '@platejs/selection/react';
import { KEYS } from 'platejs';
import { useEditorPlugin, usePlateState, usePluginOption } from 'platejs/react';
import {
ContextMenu,
ContextMenuContent,
ContextMenuGroup,
ContextMenuItem,
ContextMenuSub,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuTrigger,
} from '@/components/ui/context-menu';
import { useIsTouchDevice } from '@/registry/hooks/use-is-touch-device';
type Value = 'askAI' | null;
export function BlockContextMenu({ children }: { children: React.ReactNode }) {
const { api, editor } = useEditorPlugin(BlockMenuPlugin);
const [value, setValue] = React.useState<Value>(null);
const isTouch = useIsTouchDevice();
const [readOnly] = usePlateState('readOnly');
const openId = usePluginOption(BlockMenuPlugin, 'openId');
const isOpen = openId === BLOCK_CONTEXT_MENU_ID;
const handleTurnInto = React.useCallback(
(type: string) => {
editor
.getApi(BlockSelectionPlugin)
.blockSelection.getNodes()
.forEach(([node, path]) => {
if (node[KEYS.listType]) {
editor.tf.unsetNodes([KEYS.listType, 'indent'], {
at: path,
});
}
editor.tf.toggleBlock(type, { at: path });
});
},
[editor]
);
const handleAlign = React.useCallback(
(align: 'center' | 'left' | 'right') => {
editor
.getTransforms(BlockSelectionPlugin)
.blockSelection.setNodes({ align });
},
[editor]
);
if (isTouch) {
return children;
}
return (
<ContextMenu
onOpenChange={(open) => {
if (!open) {
api.blockMenu.hide();
}
}}
modal={false}
>
<ContextMenuTrigger
asChild
onContextMenu={(event) => {
const dataset = (event.target as HTMLElement).dataset;
const disabled =
dataset?.slateEditor === 'true' ||
readOnly ||
dataset?.plateOpenContextMenu === 'false';
if (disabled) return event.preventDefault();
setTimeout(() => {
api.blockMenu.show(BLOCK_CONTEXT_MENU_ID, {
x: event.clientX,
y: event.clientY,
});
}, 0);
}}
>
<div className="w-full">{children}</div>
</ContextMenuTrigger>
{isOpen && (
<ContextMenuContent
className="w-64"
onCloseAutoFocus={(e) => {
e.preventDefault();
editor.getApi(BlockSelectionPlugin).blockSelection.focus();
if (value === 'askAI') {
editor.getApi(AIChatPlugin).aiChat.show();
}
setValue(null);
}}
>
<ContextMenuGroup>
<ContextMenuItem
onClick={() => {
setValue('askAI');
}}
>
Ask AI
</ContextMenuItem>
<ContextMenuItem
onClick={() => {
editor
.getTransforms(BlockSelectionPlugin)
.blockSelection.removeNodes();
editor.tf.focus();
}}
>
Delete
</ContextMenuItem>
<ContextMenuItem
onClick={() => {
editor
.getTransforms(BlockSelectionPlugin)
.blockSelection.duplicate();
}}
>
Duplicate
{/* <ContextMenuShortcut>⌘ + D</ContextMenuShortcut> */}
</ContextMenuItem>
<ContextMenuSub>
<ContextMenuSubTrigger>Turn into</ContextMenuSubTrigger>
<ContextMenuSubContent className="w-48">
<ContextMenuItem onClick={() => handleTurnInto(KEYS.p)}>
Paragraph
</ContextMenuItem>
<ContextMenuItem onClick={() => handleTurnInto(KEYS.h1)}>
Heading 1
</ContextMenuItem>
<ContextMenuItem onClick={() => handleTurnInto(KEYS.h2)}>
Heading 2
</ContextMenuItem>
<ContextMenuItem onClick={() => handleTurnInto(KEYS.h3)}>
Heading 3
</ContextMenuItem>
<ContextMenuItem
onClick={() => handleTurnInto(KEYS.blockquote)}
>
Blockquote
</ContextMenuItem>
</ContextMenuSubContent>
</ContextMenuSub>
</ContextMenuGroup>
<ContextMenuGroup>
<ContextMenuItem
onClick={() =>
editor
.getTransforms(BlockSelectionPlugin)
.blockSelection.setIndent(1)
}
>
Indent
</ContextMenuItem>
<ContextMenuItem
onClick={() =>
editor
.getTransforms(BlockSelectionPlugin)
.blockSelection.setIndent(-1)
}
>
Outdent
</ContextMenuItem>
<ContextMenuSub>
<ContextMenuSubTrigger>Align</ContextMenuSubTrigger>
<ContextMenuSubContent className="w-48">
<ContextMenuItem onClick={() => handleAlign('left')}>
Left
</ContextMenuItem>
<ContextMenuItem onClick={() => handleAlign('center')}>
Center
</ContextMenuItem>
<ContextMenuItem onClick={() => handleAlign('right')}>
Right
</ContextMenuItem>
</ContextMenuSubContent>
</ContextMenuSub>
</ContextMenuGroup>
</ContextMenuContent>
)}
</ContextMenu>
);
}