diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/AIButton.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/AIButton.tsx
index f33c113a..f819984b 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/AIButton.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/AIButton.tsx
@@ -2,10 +2,17 @@ import {
useBlockNoteEditor,
useComponentsContext,
useSelectedBlocks,
+ ComponentProps,
} from '@blocknote/react';
-import { ReactNode, useMemo } from 'react';
+import { mergeRefs } from "@mantine/hooks";
-import { Box, BoxButton, Text } from '@/components';
+import { ReactNode, useMemo, forwardRef, useRef, useCallback, useState, createContext } from 'react';
+
+import {
+ Menu as MantineMenu,
+} from "@mantine/core";
+
+import {Box, Text } from '@/components';
import { Doc } from '../../doc-management';
import { AIActions, useAIRewrite } from '../api/useAIRewrite';
@@ -32,116 +39,45 @@ export function AIGroupButton({ doc }: AIGroupButtonProps) {
AI
-
- {[
- {
- label: (
- <>
-
- text_fields
- {' '}
- Use as prompt
- >
- ),
- action: 'prompt',
- },
- {
- label: (
- <>
-
- refresh
- {' '}
- Rephrase
- >
- ),
- action: 'rephrase',
- },
- {
- label: (
- <>
-
- summarize
- {' '}
- Summarize
- >
- ),
- action: 'summarize',
- },
- {
- label: (
- <>
-
- check
- {' '}
- Correct
- >
- ),
- action: 'correct',
- },
- {
- label: (
- <>
-
- translate
- {' '}
- French
- >
- ),
- action: 'translate_fr',
- },
- {
- label: (
- <>
-
- translate
- {' '}
- English
- >
- ),
- action: 'translate_en',
- },
- {
- label: (
- <>
-
- translate
- {' '}
- German
- >
- ),
- action: 'translate_de',
- },
- ].map(({ label, action }) => (
-
- ))}
+
+ text_fields}>
+ Use as prompt
+
+ refresh}>
+ Rephrase
+
+ summarize}>
+ Summarize
+
+ check}>
+ Correct
+
+
);
}
-interface AIButtonProps {
+interface AIMenuItemProps {
action: AIActions;
- label: ReactNode;
docId: Doc['id'];
+ children: ReactNode;
+ icon: ReactNode;
}
-const AIButton = ({ action, label, docId }: AIButtonProps) => {
+const AIMenuItem = ({ action, docId, children, icon }: AIMenuItemProps) => {
const editor = useBlockNoteEditor();
+ const Components = useComponentsContext()!;
const { mutateAsync: requestAI, isPending } = useAIRewrite();
- const handleRephraseAI = async (action: AIActions) => {
+ const handleAIAction = useCallback(async () => {
const textCursorPosition = editor.getSelectedText();
const newText = await requestAI({
@@ -152,27 +88,122 @@ const AIButton = ({ action, label, docId }: AIButtonProps) => {
editor.insertInlineContent([
newText,
- //{ type: 'text', text: 'World', styles: { bold: true } },
]);
- };
+ }, [editor, requestAI, docId, action]);
return (
- void handleRephraseAI(action)}
- $hasTransition
- $radius="6px"
- $width="100%"
- $justify="flex-start"
- $align="center"
- $gap="1rem"
- $css="&:hover{background-color: #f2f8ff;}text-transform: capitalize!important;"
- $direction="row"
+ }
>
- {label}
- {isPending && }
-
+ {children}
+
);
};
+
+interface TranslateMenuProps {
+ docId: Doc['id'];
+}
+
+const TranslateMenu = ({ docId }: TranslateMenuProps) => {
+ const Components = useComponentsContext()!;
+
+ return (
+ translate} close>
+
+
+ Translate
+
+
+
+
+ English
+
+
+ French
+
+
+ German
+
+
+
+ );
+};
+
+const SubMenuContext = createContext<
+ | {
+ onMenuMouseOver: () => void;
+ onMenuMouseLeave: () => void;
+ }
+ | undefined
+>(undefined);
+
+
+const SubMenu = forwardRef<
+ HTMLButtonElement,
+ ComponentProps["Generic"]["Menu"]["Root"]
+>((props, ref) => {
+ const {
+ children,
+ onOpenChange,
+ position,
+ icon,
+ sub, // not used
+ ...rest
+ } = props;
+
+ const [opened, setOpened] = useState(false);
+
+ const itemRef = useRef(null);
+
+ const menuCloseTimer = useRef | undefined>();
+
+ const mouseLeave = useCallback(() => {
+ if (menuCloseTimer.current) {
+ clearTimeout(menuCloseTimer.current);
+ }
+ menuCloseTimer.current = setTimeout(() => {
+ setOpened(false);
+ }, 250);
+ }, []);
+
+ const mouseOver = useCallback(() => {
+ if (menuCloseTimer.current) {
+ clearTimeout(menuCloseTimer.current);
+ }
+ setOpened(true);
+ }, []);
+
+ return (
+
+
+ onOpenChange?.(false)}
+ onOpen={() => onOpenChange?.(true)}
+ position={position}>
+ {children}
+
+
+
+ );
+});
\ No newline at end of file