diff --git a/apps/web/src/app/test/report-playground/ReportPlayground.tsx b/apps/web/src/app/test/report-playground/ReportPlayground.tsx index 8ebd2bb2b..795f5a7db 100644 --- a/apps/web/src/app/test/report-playground/ReportPlayground.tsx +++ b/apps/web/src/app/test/report-playground/ReportPlayground.tsx @@ -201,6 +201,16 @@ const value: ReportElements = [ id: '39ZlrKsOyn', lineHeight: 3 }, + { + children: [ + { + text: 'sdfasdf' + } + ], + icon: '😄', + type: 'callout', + id: 'KQ0_YKgdqy' + }, { type: 'p', id: 'k5Id6hcBYM', diff --git a/apps/web/src/components/ui/icons/NucleoIconOutlined/grid-layout-cols-3.tsx b/apps/web/src/components/ui/icons/NucleoIconOutlined/grid-layout-cols-3.tsx index 2b461854a..fb462c43e 100644 --- a/apps/web/src/components/ui/icons/NucleoIconOutlined/grid-layout-cols-3.tsx +++ b/apps/web/src/components/ui/icons/NucleoIconOutlined/grid-layout-cols-3.tsx @@ -15,9 +15,9 @@ function gridLayoutCols3(props: iconProps) { transform="rotate(90 9 9)" fill="none" stroke="#212121" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="1.5"> + strokeLinecap="round" + strokeLinejoin="round" + strokeWidth="1.5"> + strokeLinecap="round" + strokeLinejoin="round" + strokeWidth="1.5"> + strokeLinecap="round" + strokeLinejoin="round" + strokeWidth="1.5"> ); diff --git a/apps/web/src/components/ui/report/config/addMenuItems.tsx b/apps/web/src/components/ui/report/config/addMenuItems.tsx new file mode 100644 index 000000000..0543405be --- /dev/null +++ b/apps/web/src/components/ui/report/config/addMenuItems.tsx @@ -0,0 +1,235 @@ +'use client'; + +import * as React from 'react'; +import type { PlateEditor } from 'platejs/react'; +import { KEYS } from 'platejs'; +import { AIChatPlugin } from '@platejs/ai/react'; +import { Minus } from '@/components/ui/icons'; +import { NodeTypeIcons } from './icons'; +import { NodeTypeLabels, MenuGroupLabels } from './labels'; +import { insertBlock, insertInlineElement } from '../elements/transforms'; + +// Shared types for menu items +export interface MenuItem { + icon: React.ReactNode; + value: string; + onSelect: (editor: PlateEditor, value: string) => void; + className?: string; + focusEditor?: boolean; + keywords?: readonly string[]; + label?: string; + description?: string; +} + +export interface MenuGroup { + group: string; + items: MenuItem[]; +} + +// Shared menu groups used by both SlashNode and InsertToolbarButton +export const menuGroups: MenuGroup[] = [ + // { + // group: 'AI', + // items: [ + // { + // focusEditor: false, + // icon: , + // label: NodeTypeLabels.aiChat.label, + // keywords: NodeTypeLabels.aiChat.keywords, + // value: 'AI', + // onSelect: (editor) => { + // editor.getApi(AIChatPlugin).aiChat.show(); + // } + // } + // ] + // }, + { + group: MenuGroupLabels.basicBlocks, + items: [ + { + icon: , + keywords: NodeTypeLabels.paragraph.keywords, + label: NodeTypeLabels.paragraph.label, + value: KEYS.p + }, + { + icon: , + keywords: NodeTypeLabels.h1.keywords, + label: NodeTypeLabels.h1.label, + value: 'h1' + }, + { + icon: , + keywords: NodeTypeLabels.h2.keywords, + label: NodeTypeLabels.h2.label, + value: 'h2' + }, + { + icon: , + keywords: NodeTypeLabels.h3.keywords, + label: NodeTypeLabels.h3.label, + value: 'h3' + }, + { + icon: , + keywords: NodeTypeLabels.table.keywords, + label: NodeTypeLabels.table.label, + value: KEYS.table + }, + { + icon: , + keywords: NodeTypeLabels.codeBlock.keywords, + label: NodeTypeLabels.codeBlock.label, + value: KEYS.codeBlock + }, + { + icon: , + keywords: NodeTypeLabels.blockquote.keywords, + label: NodeTypeLabels.blockquote.label, + value: KEYS.blockquote + }, + { + icon: , + label: NodeTypeLabels.divider.label, + value: KEYS.hr + }, + { + description: 'Insert a highlighted block.', + icon: , + keywords: NodeTypeLabels.callout.keywords, + label: NodeTypeLabels.callout.label, + value: KEYS.callout + } + ].map((item) => ({ + ...item, + onSelect: (editor, value) => { + insertBlock(editor, value); + } + })) + }, + { + group: MenuGroupLabels.lists, + items: [ + { + icon: , + keywords: NodeTypeLabels.bulletedList.keywords, + label: NodeTypeLabels.bulletedList.label, + value: KEYS.ul + }, + { + icon: , + keywords: NodeTypeLabels.numberedList.keywords, + label: NodeTypeLabels.numberedList.label, + value: KEYS.ol + }, + { + icon: , + keywords: NodeTypeLabels.todoList.keywords, + label: NodeTypeLabels.todoList.label, + value: KEYS.listTodo + }, + { + icon: , + keywords: NodeTypeLabels.toggleList.keywords, + label: NodeTypeLabels.toggleList.label, + value: KEYS.toggle + } + ].map((item) => ({ + ...item, + onSelect: (editor, value) => { + insertBlock(editor, value); + } + })) + }, + { + group: MenuGroupLabels.media, + items: [ + { + icon: , + keywords: NodeTypeLabels.image?.keywords, + label: NodeTypeLabels.image.label, + value: KEYS.img + }, + { + icon: , + keywords: NodeTypeLabels.embed?.keywords, + label: NodeTypeLabels.embed.label, + value: KEYS.mediaEmbed + } + ].map((item) => ({ + ...item, + onSelect: (editor, value) => { + insertBlock(editor, value); + } + })) + }, + { + group: MenuGroupLabels.advancedBlocks, + items: [ + { + icon: , + keywords: NodeTypeLabels.tableOfContents.keywords, + label: NodeTypeLabels.tableOfContents.label, + value: KEYS.toc + }, + { + icon: , + keywords: NodeTypeLabels.columnsThree.keywords, + label: NodeTypeLabels.columnsThree.label, + value: 'action_three_columns' + }, + { + focusEditor: false, + icon: , + keywords: NodeTypeLabels.equation.keywords, + label: NodeTypeLabels.equation.label, + value: KEYS.equation + } + ].map((item) => ({ + ...item, + onSelect: (editor, value) => { + insertBlock(editor, value); + } + })) + }, + { + group: MenuGroupLabels.inline, + items: [ + { + icon: , + keywords: NodeTypeLabels.link?.keywords, + label: NodeTypeLabels.link.label, + value: KEYS.link + }, + { + focusEditor: true, + icon: , + keywords: NodeTypeLabels.date.keywords, + label: NodeTypeLabels.date.label, + value: KEYS.date + }, + { + focusEditor: false, + icon: , + keywords: NodeTypeLabels.inlineEquation.keywords, + label: NodeTypeLabels.inlineEquation.label, + value: KEYS.inlineEquation + } + ].map((item) => ({ + ...item, + onSelect: (editor, value) => { + insertInlineElement(editor, value); + } + })) + } +]; + +// Helper function to get groups for slash command and insert button +export function getSlashGroups(): MenuGroup[] { + return menuGroups; +} + +// Helper function to get groups for insert button (same as slash groups) +export function getInsertGroups(): MenuGroup[] { + return menuGroups; +} diff --git a/apps/web/src/components/ui/report/elements/CalloutNode.tsx b/apps/web/src/components/ui/report/elements/CalloutNode.tsx index 8c0eb1cdf..00c3beabc 100644 --- a/apps/web/src/components/ui/report/elements/CalloutNode.tsx +++ b/apps/web/src/components/ui/report/elements/CalloutNode.tsx @@ -57,7 +57,7 @@ export function CalloutElement({ }> -
{children}
+
{children}
); diff --git a/apps/web/src/components/ui/report/elements/InsertToolbarButton.tsx b/apps/web/src/components/ui/report/elements/InsertToolbarButton.tsx index b895e9da8..dd83ca971 100644 --- a/apps/web/src/components/ui/report/elements/InsertToolbarButton.tsx +++ b/apps/web/src/components/ui/report/elements/InsertToolbarButton.tsx @@ -4,11 +4,10 @@ import * as React from 'react'; import type { DropdownMenuProps } from '@radix-ui/react-dropdown-menu'; -import { Minus, Plus } from '@/components/ui/icons'; -import { NodeTypeIcons } from '../config/icons'; -import { createLabel, NodeTypeLabels, MenuGroupLabels } from '../config/labels'; -import { KEYS } from 'platejs'; -import { type PlateEditor, useEditorRef } from 'platejs/react'; +import { Plus } from '@/components/ui/icons'; +import { createLabel } from '../config/labels'; +import { useEditorRef } from 'platejs/react'; +import { getInsertGroups } from '../config/addMenuItems'; import { DropdownMenu, @@ -16,183 +15,10 @@ import { DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'; -import { insertBlock, insertInlineElement } from './transforms'; import { ToolbarButton, ToolbarMenuGroup } from '@/components/ui/toolbar/Toolbar'; -type Group = { - group: string; - items: Item[]; -}; - -interface Item { - icon: React.ReactNode; - value: string; - onSelect: (editor: PlateEditor, value: string) => void; - focusEditor?: boolean; - label?: string; -} - -const groups: Group[] = [ - { - group: MenuGroupLabels.basicBlocks, - items: [ - { - icon: , - label: NodeTypeLabels.paragraph.label, - value: KEYS.p - }, - { - icon: , - label: NodeTypeLabels.h1.label, - value: 'h1' - }, - { - icon: , - label: NodeTypeLabels.h2.label, - value: 'h2' - }, - { - icon: , - label: NodeTypeLabels.h3.label, - value: 'h3' - }, - { - icon: , - label: NodeTypeLabels.table.label, - value: KEYS.table - }, - { - icon: , - label: NodeTypeLabels.codeBlock.label, - value: KEYS.codeBlock - }, - { - icon: , - label: NodeTypeLabels.blockquote.label, - value: KEYS.blockquote - }, - { - icon: , - label: NodeTypeLabels.divider.label, - value: KEYS.hr - }, - { - icon: , - label: NodeTypeLabels.callout.label, - value: KEYS.callout - } - ].map((item) => ({ - ...item, - onSelect: (editor, value) => { - insertBlock(editor, value); - } - })) - }, - { - group: MenuGroupLabels.lists, - items: [ - { - icon: , - label: NodeTypeLabels.bulletedList.label, - value: KEYS.ul - }, - { - icon: , - label: NodeTypeLabels.numberedList.label, - value: KEYS.ol - }, - { - icon: , - label: NodeTypeLabels.todoList.label, - value: KEYS.listTodo - }, - { - icon: , - label: NodeTypeLabels.toggleList.label, - value: KEYS.toggle - } - ].map((item) => ({ - ...item, - onSelect: (editor, value) => { - insertBlock(editor, value); - } - })) - }, - { - group: MenuGroupLabels.media, - items: [ - { - icon: , - label: NodeTypeLabels.image.label, - value: KEYS.img - }, - { - icon: , - label: NodeTypeLabels.embed.label, - value: KEYS.mediaEmbed - } - ].map((item) => ({ - ...item, - onSelect: (editor, value) => { - insertBlock(editor, value); - } - })) - }, - { - group: MenuGroupLabels.advancedBlocks, - items: [ - { - icon: , - label: NodeTypeLabels.tableOfContents.label, - value: KEYS.toc - }, - { - icon: , - label: NodeTypeLabels.columnsThree.label, - value: 'action_three_columns' - }, - { - focusEditor: false, - icon: , - label: NodeTypeLabels.equation.label, - value: KEYS.equation - } - ].map((item) => ({ - ...item, - onSelect: (editor, value) => { - insertBlock(editor, value); - } - })) - }, - { - group: MenuGroupLabels.inline, - items: [ - { - icon: , - label: NodeTypeLabels.link.label, - value: KEYS.link - }, - { - focusEditor: true, - icon: , - label: NodeTypeLabels.date.label, - value: KEYS.date - }, - { - focusEditor: false, - icon: , - label: NodeTypeLabels.inlineEquation.label, - value: KEYS.inlineEquation - } - ].map((item) => ({ - ...item, - onSelect: (editor, value) => { - insertInlineElement(editor, value); - } - })) - } -]; +const groups = getInsertGroups(); export function InsertToolbarButton(props: DropdownMenuProps) { const editor = useEditorRef(); diff --git a/apps/web/src/components/ui/report/elements/SlashNode.tsx b/apps/web/src/components/ui/report/elements/SlashNode.tsx index d9f8960e7..6d3a96f01 100644 --- a/apps/web/src/components/ui/report/elements/SlashNode.tsx +++ b/apps/web/src/components/ui/report/elements/SlashNode.tsx @@ -2,15 +2,11 @@ import * as React from 'react'; -import type { PlateEditor, PlateElementProps } from 'platejs/react'; +import type { PlateElementProps } from 'platejs/react'; -import { AIChatPlugin } from '@platejs/ai/react'; -import { type TComboboxInputElement, KEYS } from 'platejs'; +import { type TComboboxInputElement } from 'platejs'; import { PlateElement } from 'platejs/react'; -import { NodeTypeIcons } from '../config/icons'; -import { NodeTypeLabels, MenuGroupLabels } from '../config/labels'; - -import { insertBlock, insertInlineElement } from './transforms'; +import { getSlashGroups } from '../config/addMenuItems'; import { InlineCombobox, @@ -22,175 +18,7 @@ import { InlineComboboxItem } from './InlineCombobox'; -type Group = { - group: string; - items: Item[]; -}; - -interface Item { - icon: React.ReactNode; - value: string; - onSelect: (editor: PlateEditor, value: string) => void; - className?: string; - focusEditor?: boolean; - keywords?: readonly string[]; - label?: string; - description?: string; -} - -const groups: Group[] = [ - // { - // group: 'AI', - // items: [ - // { - // focusEditor: false, - // icon: , - // label: NodeTypeLabels.aiChat.label, - // keywords: NodeTypeLabels.aiChat.keywords, - // value: 'AI', - // onSelect: (editor) => { - // editor.getApi(AIChatPlugin).aiChat.show(); - // } - // } - // ] - // }, - { - group: MenuGroupLabels.basicBlocks, - items: [ - { - icon: , - keywords: NodeTypeLabels.paragraph.keywords, - label: NodeTypeLabels.paragraph.label, - value: KEYS.p - }, - { - icon: , - keywords: NodeTypeLabels.h1.keywords, - label: NodeTypeLabels.h1.label, - value: KEYS.h1 - }, - { - icon: , - keywords: NodeTypeLabels.h2.keywords, - label: NodeTypeLabels.h2.label, - value: KEYS.h2 - }, - { - icon: , - keywords: NodeTypeLabels.h3.keywords, - label: NodeTypeLabels.h3.label, - value: KEYS.h3 - }, - { - icon: , - keywords: NodeTypeLabels.bulletedList.keywords, - label: NodeTypeLabels.bulletedList.label, - value: KEYS.ul - }, - { - icon: , - keywords: NodeTypeLabels.numberedList.keywords, - label: NodeTypeLabels.numberedList.label, - value: KEYS.ol - }, - { - icon: , - keywords: NodeTypeLabels.todoList.keywords, - label: NodeTypeLabels.todoList.label, - value: KEYS.listTodo - }, - { - icon: , - keywords: NodeTypeLabels.toggleList.keywords, - label: NodeTypeLabels.toggleList.label, - value: KEYS.toggle - }, - { - icon: , - keywords: NodeTypeLabels.codeBlock.keywords, - label: NodeTypeLabels.codeBlock.label, - value: KEYS.codeBlock - }, - { - icon: , - keywords: NodeTypeLabels.table.keywords, - label: NodeTypeLabels.table.label, - value: KEYS.table - }, - { - icon: , - keywords: NodeTypeLabels.blockquote.keywords, - label: NodeTypeLabels.blockquote.label, - value: KEYS.blockquote - }, - { - description: 'Insert a highlighted block.', - icon: , - keywords: NodeTypeLabels.callout.keywords, - label: NodeTypeLabels.callout.label, - value: KEYS.callout - } - ].map((item) => ({ - ...item, - onSelect: (editor, value) => { - insertBlock(editor, value); - } - })) - }, - { - group: MenuGroupLabels.advancedBlocks, - items: [ - { - icon: , - keywords: NodeTypeLabels.tableOfContents.keywords, - label: NodeTypeLabels.tableOfContents.label, - value: KEYS.toc - }, - { - icon: , - keywords: NodeTypeLabels.columnsThree.keywords, - label: NodeTypeLabels.columnsThree.label, - value: 'action_three_columns' - }, - { - focusEditor: false, - icon: , - keywords: NodeTypeLabels.equation.keywords, - label: NodeTypeLabels.equation.label, - value: KEYS.equation - } - ].map((item) => ({ - ...item, - onSelect: (editor, value) => { - insertBlock(editor, value); - } - })) - }, - { - group: MenuGroupLabels.inline, - items: [ - { - focusEditor: true, - icon: , - keywords: NodeTypeLabels.date.keywords, - label: NodeTypeLabels.date.label, - value: KEYS.date - }, - { - focusEditor: false, - icon: , - keywords: NodeTypeLabels.inlineEquation.keywords, - label: NodeTypeLabels.inlineEquation.label, - value: KEYS.inlineEquation - } - ].map((item) => ({ - ...item, - onSelect: (editor, value) => { - insertInlineElement(editor, value); - } - })) - } -]; +const groups = getSlashGroups(); export function SlashInputElement(props: PlateElementProps) { const { editor, element } = props;