From 5055530bdf1d63d572c63f2a5610d60b2292c0a4 Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Mon, 29 Sep 2025 11:45:37 -0600 Subject: [PATCH] pass the select through --- .../ui/inputs/BusterInput/BusterInput.tsx | 12 +- .../BusterInput/BusterMentionsInput.tsx | 43 +++-- .../ui/inputs/MentionInput/MentionInput.tsx | 149 ++++++++++-------- .../inputs/MentionInput/MentionInput.types.ts | 6 +- 4 files changed, 118 insertions(+), 92 deletions(-) diff --git a/apps/web/src/components/ui/inputs/BusterInput/BusterInput.tsx b/apps/web/src/components/ui/inputs/BusterInput/BusterInput.tsx index 918fecd5d..6325920b6 100644 --- a/apps/web/src/components/ui/inputs/BusterInput/BusterInput.tsx +++ b/apps/web/src/components/ui/inputs/BusterInput/BusterInput.tsx @@ -1,6 +1,7 @@ import { Command } from 'cmdk'; import { useCallback, useEffect, useRef, useState } from 'react'; import { useMemoizedFn } from '@/hooks/useMemoizedFn'; +import type { MentionInputRef } from '../MentionInput'; import type { BusterInputProps, BusterOnSelectParams } from './BusterInput.types'; import { BusterInputContainer } from './BusterInputContainer'; import { BusterInputEmpty } from './BusterInputEmpty'; @@ -41,6 +42,7 @@ export const BusterInput = ({ const commandListNavigatedRef = useRef(false); const commandRef = useRef(null); + const mentionsInputRef = useRef(null); const showSuggestionList = !hasClickedSelect && suggestionItems.length > 0; @@ -53,7 +55,7 @@ export const BusterInput = ({ }, []); const onSelectItem = useMemoizedFn(({ onClick, ...params }: BusterOnSelectParams) => { - const { addValueToInput, value, loading, inputValue, label, disabled } = params; + const { addValueToInput, loading, inputValue, label, disabled } = params; if (disabled) { console.warn('Item is disabled', params); return; @@ -66,8 +68,11 @@ export const BusterInput = ({ console.warn('Item is loading', params); return; } - console.log('onSelectItem', addValueToInput, params); - if (addValueToInput) setValue(inputValue ?? String(label)); + if (addValueToInput) { + const stringValue = inputValue ?? String(label); + mentionsInputRef.current?.editor?.commands.setContent(stringValue); + setValue(stringValue); + } onClick?.(); if (closeSuggestionOnSelect) setHasClickedSelect(true); onSuggestionItemClick?.(params); @@ -128,6 +133,7 @@ export const BusterInput = ({ variant={variant} > ; }; -export const BusterMentionsInput = ({ - value: valueProp, - placeholder, - defaultValue, - mentions, - value, - ...props -}: BusterMentionsInputProps) => { - return ( - - - - ); -}; +export const BusterMentionsInput = forwardRef( + ({ value: valueProp, placeholder, defaultValue, mentions, value, ...props }, ref) => { + return ( + + + + ); + } +); + +BusterMentionsInput.displayName = 'BusterMentionsInput'; diff --git a/apps/web/src/components/ui/inputs/MentionInput/MentionInput.tsx b/apps/web/src/components/ui/inputs/MentionInput/MentionInput.tsx index af2ac4ef0..79a6e93ef 100644 --- a/apps/web/src/components/ui/inputs/MentionInput/MentionInput.tsx +++ b/apps/web/src/components/ui/inputs/MentionInput/MentionInput.tsx @@ -3,77 +3,96 @@ import Document from '@tiptap/extension-document'; import Paragraph from '@tiptap/extension-paragraph'; import Text from '@tiptap/extension-text'; import { EditorContent, EditorContext, useEditor } from '@tiptap/react'; -import { useMemo } from 'react'; +import { forwardRef, useImperativeHandle, useMemo } from 'react'; import { cn } from '@/lib/utils'; import { MentionExtension } from './MentionExtension'; -import type { MentionInputProps, MentionSuggestionExtension } from './MentionInput.types'; +import type { + MentionInputProps, + MentionInputRef, + MentionSuggestionExtension, +} from './MentionInput.types'; import { SubmitOnEnter } from './SubmitEnterExtension'; import { onUpdateTransformer } from './update-transformers'; -export const MentionInput = ({ - mentions, - onChange, - defaultValue = '', - onFocus, - onBlur, - autoFocus, - style, - className, - readOnly, - disabled, - onPressEnter, - commandListNavigatedRef, -}: MentionInputProps) => { - const mentionsByTrigger = useMemo(() => { - return mentions.reduce( - (acc, mention) => { - if (mention.char) { - acc[mention.char] = mention; - } - return acc; - }, - {} as Record - ); - }, [mentions]); - - const editor = useEditor({ - extensions: [ - Document, - Paragraph, - Text, - MentionExtension(mentions), - SubmitOnEnter({ - mentionsByTrigger, - onPressEnter, - commandListNavigatedRef, - }), - ], - content: defaultValue, - autofocus: autoFocus, - editable: !disabled && !readOnly, - editorProps: { - attributes: { - class: cn('p-1 border rounded outline-0', className), - }, +export const MentionInput = forwardRef( + ( + { + mentions, + onChange, + defaultValue = '', + onFocus, + onBlur, + autoFocus, + style, + className, + readOnly, + disabled, + onPressEnter, + commandListNavigatedRef, }, - onUpdate: ({ editor }) => { - const { transformedValue, transformedJson, editorText } = onUpdateTransformer({ + ref + ) => { + const mentionsByTrigger = useMemo(() => { + return mentions.reduce( + (acc, mention) => { + if (mention.char) { + acc[mention.char] = mention; + } + return acc; + }, + {} as Record + ); + }, [mentions]); + + const editor = useEditor({ + extensions: [ + Document, + Paragraph, + Text, + MentionExtension(mentions), + SubmitOnEnter({ + mentionsByTrigger, + onPressEnter, + commandListNavigatedRef, + }), + ], + content: defaultValue, + autofocus: autoFocus, + editable: !disabled && !readOnly, + editorProps: { + attributes: { + class: cn('p-1 border rounded outline-0', className), + }, + }, + onUpdate: ({ editor }) => { + const { transformedValue, transformedJson, editorText } = onUpdateTransformer({ + editor, + mentionsByTrigger, + }); + onChange?.(transformedValue, transformedJson, editorText); + }, + onFocus: onFocus, + onBlur: onBlur, + }); + + useImperativeHandle( + ref, + () => ({ editor, - mentionsByTrigger, - }); - onChange?.(transformedValue, transformedJson, editorText); - }, - onFocus: onFocus, - onBlur: onBlur, - }); + }), + [editor] + ); - const providerValue = useMemo(() => ({ editor }), [editor]); + const providerValue = useMemo(() => ({ editor }), [editor]); - return ( - - - - - - ); -}; + return ( + + + + + + ); + } +); + +MentionInput.displayName = 'MentionInput'; diff --git a/apps/web/src/components/ui/inputs/MentionInput/MentionInput.types.ts b/apps/web/src/components/ui/inputs/MentionInput/MentionInput.types.ts index fb0aec727..c312478a2 100644 --- a/apps/web/src/components/ui/inputs/MentionInput/MentionInput.types.ts +++ b/apps/web/src/components/ui/inputs/MentionInput/MentionInput.types.ts @@ -1,5 +1,5 @@ import type { MentionNodeAttrs, MentionOptions } from '@tiptap/extension-mention'; -import type { EditorEvents } from '@tiptap/react'; +import type { Editor, EditorEvents } from '@tiptap/react'; import type { MentionPillAttributes } from './MentionPill'; export type MentionOnSelectParams = { @@ -102,6 +102,10 @@ export type MentionInputProps = { commandListNavigatedRef?: React.RefObject; }; +export type MentionInputRef = { + editor: Editor | null; +}; + declare module '@tiptap/core' { interface Storage { mention: {