mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-10-07 02:58:11 +00:00
206 lines
5.8 KiB
TypeScript
206 lines
5.8 KiB
TypeScript
'use client';
|
|
|
|
import { useCallback, useState } from 'react';
|
|
|
|
import { AIChatPlugin } from '@udecode/plate-ai/react';
|
|
import { BlockquotePlugin } from '@udecode/plate-block-quote/react';
|
|
import { unsetNodes } from '@udecode/plate-common';
|
|
import {
|
|
ParagraphPlugin,
|
|
focusEditor,
|
|
useEditorPlugin,
|
|
} from '@udecode/plate-common/react';
|
|
import { HEADING_KEYS } from '@udecode/plate-heading';
|
|
import { IndentListPlugin } from '@udecode/plate-indent-list/react';
|
|
import {
|
|
BLOCK_CONTEXT_MENU_ID,
|
|
BlockMenuPlugin,
|
|
BlockSelectionPlugin,
|
|
} from '@udecode/plate-selection/react';
|
|
|
|
import { useIsTouchDevice } from '~/lib/hooks/use-is-touch-device';
|
|
|
|
import {
|
|
ContextMenu,
|
|
ContextMenuContent,
|
|
ContextMenuGroup,
|
|
ContextMenuItem,
|
|
ContextMenuSub,
|
|
ContextMenuSubContent,
|
|
ContextMenuSubTrigger,
|
|
ContextMenuTrigger,
|
|
} from './context-menu';
|
|
|
|
type Value = 'askAI' | null;
|
|
|
|
export function BlockContextMenu({ children }: { children: React.ReactNode }) {
|
|
const { api, editor } = useEditorPlugin(BlockMenuPlugin);
|
|
const [value, setValue] = useState<Value>(null);
|
|
const isTouch = useIsTouchDevice();
|
|
|
|
const handleTurnInto = useCallback(
|
|
(type: string) => {
|
|
editor
|
|
.getApi(BlockSelectionPlugin)
|
|
.blockSelection.getNodes()
|
|
.forEach(([node, path]) => {
|
|
if (node[IndentListPlugin.key]) {
|
|
unsetNodes(editor, [IndentListPlugin.key, 'indent'], { at: path });
|
|
}
|
|
|
|
editor.tf.toggle.block({ type }, { at: path });
|
|
});
|
|
},
|
|
[editor]
|
|
);
|
|
|
|
const handleAlign = useCallback(
|
|
(align: 'center' | 'left' | 'right') => {
|
|
editor
|
|
.getTransforms(BlockSelectionPlugin)
|
|
.blockSelection.setNodes({ align });
|
|
},
|
|
[editor]
|
|
);
|
|
|
|
if (isTouch) {
|
|
return children;
|
|
}
|
|
|
|
return (
|
|
<ContextMenu
|
|
onOpenChange={(open) => {
|
|
if (!open) {
|
|
// prevent unselect the block selection
|
|
setTimeout(() => {
|
|
api.blockMenu.hide();
|
|
}, 0);
|
|
}
|
|
}}
|
|
modal={false}
|
|
>
|
|
<ContextMenuTrigger
|
|
asChild
|
|
onContextMenu={(event) => {
|
|
const dataset = (event.target as HTMLElement).dataset;
|
|
|
|
const disabled = dataset?.slateEditor === 'true';
|
|
|
|
if (disabled) return event.preventDefault();
|
|
|
|
api.blockMenu.show(BLOCK_CONTEXT_MENU_ID, {
|
|
x: event.clientX,
|
|
y: event.clientY,
|
|
});
|
|
}}
|
|
>
|
|
<div className="w-full">{children}</div>
|
|
</ContextMenuTrigger>
|
|
<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();
|
|
focusEditor(editor);
|
|
}}
|
|
>
|
|
Delete
|
|
</ContextMenuItem>
|
|
<ContextMenuItem
|
|
onClick={() => {
|
|
editor
|
|
.getTransforms(BlockSelectionPlugin)
|
|
.blockSelection.duplicate(
|
|
editor.getApi(BlockSelectionPlugin).blockSelection.getNodes()
|
|
);
|
|
}}
|
|
>
|
|
Duplicate
|
|
{/* <ContextMenuShortcut>⌘ + D</ContextMenuShortcut> */}
|
|
</ContextMenuItem>
|
|
<ContextMenuSub>
|
|
<ContextMenuSubTrigger>Turn into</ContextMenuSubTrigger>
|
|
<ContextMenuSubContent className="w-48">
|
|
<ContextMenuItem
|
|
onClick={() => handleTurnInto(ParagraphPlugin.key)}
|
|
>
|
|
Paragraph
|
|
</ContextMenuItem>
|
|
|
|
<ContextMenuItem onClick={() => handleTurnInto(HEADING_KEYS.h1)}>
|
|
Heading 1
|
|
</ContextMenuItem>
|
|
<ContextMenuItem onClick={() => handleTurnInto(HEADING_KEYS.h2)}>
|
|
Heading 2
|
|
</ContextMenuItem>
|
|
<ContextMenuItem onClick={() => handleTurnInto(HEADING_KEYS.h3)}>
|
|
Heading 3
|
|
</ContextMenuItem>
|
|
<ContextMenuItem
|
|
onClick={() => handleTurnInto(BlockquotePlugin.key)}
|
|
>
|
|
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>
|
|
);
|
|
}
|