From d9c692dae3a35fb02276da578bee4fef20fcde1b Mon Sep 17 00:00:00 2001 From: kevinkim-ogp Date: Thu, 6 Nov 2025 17:14:43 +0800 Subject: [PATCH] chore: refactor RTE to accept custom menu options --- packages/backend/src/graphql/schema.graphql | 4 ++++ .../src/components/InputCreator/index.tsx | 1 + .../src/components/RichTextEditor/MenuBar.tsx | 22 +++++++++++++++++-- .../src/components/RichTextEditor/index.tsx | 6 +++++ .../frontend/src/graphql/queries/get-apps.ts | 1 + packages/types/index.d.ts | 6 ++++- 6 files changed, 37 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/graphql/schema.graphql b/packages/backend/src/graphql/schema.graphql index 3f8bc1169..ee8eb1c2e 100644 --- a/packages/backend/src/graphql/schema.graphql +++ b/packages/backend/src/graphql/schema.graphql @@ -220,6 +220,9 @@ type ActionSubstepArgument { # Only for multirow subFields: [ActionSubstepArgument] + + # Only for rich text + customRteMenuOptions: [String] } type DropdownClickableLink { @@ -262,6 +265,7 @@ enum AppCategory { data communication logic + ai others } diff --git a/packages/frontend/src/components/InputCreator/index.tsx b/packages/frontend/src/components/InputCreator/index.tsx index d6e7114b7..5e8a9640b 100644 --- a/packages/frontend/src/components/InputCreator/index.tsx +++ b/packages/frontend/src/components/InputCreator/index.tsx @@ -121,6 +121,7 @@ export default function InputCreator(props: InputCreatorProps): JSX.Element { variablesEnabled={variables} isRich noVariablesMessage={noVariablesMessage} + customRteMenuOptions={schema?.customRteMenuOptions} /> ) } diff --git a/packages/frontend/src/components/RichTextEditor/MenuBar.tsx b/packages/frontend/src/components/RichTextEditor/MenuBar.tsx index 5587f7757..f38a565ea 100644 --- a/packages/frontend/src/components/RichTextEditor/MenuBar.tsx +++ b/packages/frontend/src/components/RichTextEditor/MenuBar.tsx @@ -63,7 +63,7 @@ enum MenuLabels { Undo = 'Undo', Redo = 'Redo', } -const menuButtons = [ +const DEFAULT_MENU_BUTTONS = [ { label: MenuLabels.Bold, onClick: (editor: Editor) => editor.chain().focus().toggleBold().run(), @@ -238,9 +238,15 @@ interface MenuBarProps { editor: Editor | null variableMap: VariableInfoMap editable: boolean + customMenuOptions?: string[] } -export const MenuBar = ({ editor, variableMap, editable }: MenuBarProps) => { +export const MenuBar = ({ + editor, + variableMap, + editable, + customMenuOptions, +}: MenuBarProps) => { const { isOpen: isDialogOpen, onClose, @@ -321,6 +327,18 @@ export const MenuBar = ({ editor, variableMap, editable }: MenuBarProps) => { [editor, dialogValue, onDialogClose], ) + const menuButtons = useMemo(() => { + if (customMenuOptions) { + return customMenuOptions + .map((option) => + DEFAULT_MENU_BUTTONS.find(({ label }) => label === option), + ) + .filter((button): button is NonNullable => !!button) + } + + return DEFAULT_MENU_BUTTONS + }, [customMenuOptions]) + if (!editor) { return null } diff --git a/packages/frontend/src/components/RichTextEditor/index.tsx b/packages/frontend/src/components/RichTextEditor/index.tsx index bc793367e..37f8a5ee1 100644 --- a/packages/frontend/src/components/RichTextEditor/index.tsx +++ b/packages/frontend/src/components/RichTextEditor/index.tsx @@ -100,6 +100,7 @@ interface EditorProps { autoFocus?: boolean singleVariableSelection?: boolean noVariablesMessage?: string + customRteMenuOptions?: string[] } const Editor = ({ onChange, @@ -114,6 +115,7 @@ const Editor = ({ singleVariableSelection, autoFocus = false, noVariablesMessage, + customRteMenuOptions, }: EditorProps) => { const { priorExecutionSteps } = useContext(StepExecutionsContext) const { allApps } = useContext(EditorContext) @@ -292,6 +294,7 @@ const Editor = ({ editor={editor} variableMap={varInfo} editable={editable ?? false} + customMenuOptions={customRteMenuOptions} /> )} { const { readOnly } = useContext(EditorContext) const { control, getValues } = useFormContext() @@ -413,6 +418,7 @@ const RichTextEditor = ({ autoFocus={shouldAutoFocus} singleVariableSelection={singleVariableSelection} noVariablesMessage={noVariablesMessage} + customRteMenuOptions={customRteMenuOptions} /> )} /> diff --git a/packages/frontend/src/graphql/queries/get-apps.ts b/packages/frontend/src/graphql/queries/get-apps.ts index 2dd8115ea..b9e03c79a 100644 --- a/packages/frontend/src/graphql/queries/get-apps.ts +++ b/packages/frontend/src/graphql/queries/get-apps.ts @@ -226,6 +226,7 @@ export const GET_APPS = gql` url } singleVariableSelection + customRteMenuOptions # Only for multi-row subFields { label diff --git a/packages/types/index.d.ts b/packages/types/index.d.ts index 53d3b6e59..1208e801c 100644 --- a/packages/types/index.d.ts +++ b/packages/types/index.d.ts @@ -435,6 +435,10 @@ export interface IFieldMultiRow extends IBaseField { export interface IFieldRichText extends IBaseField { type: 'rich-text' value?: string + + // Specifies the order and what menu options to show in the RTE + // 'divider' is specified manually to determine when a divider should be shown + customRteMenuOptions?: string[] } export interface IFieldDragDrop extends IBaseField { @@ -599,7 +603,7 @@ export interface IApp { setupMessage?: SetupMessage } -export type AppCategory = 'data' | 'communication' | 'logic' | 'others' +export type AppCategory = 'data' | 'communication' | 'logic' | 'others' | 'ai' export type TBeforeRequest = ( $: IGlobalVariable,