Suggestion
matechat/suggestionFreeComponent
MateChat Suggestion
Install it
npx shadcn@latest add http://matechat.noctisynth.org/r/suggestion.jsonSource
1import React from "react";2import type { SelectItemOptionsType } from "@/list";3import { List } from "@/list";45export interface TriggerConfig {6 char: string;7 data: SelectItemOptionsType;8 optionLabel: string;9 optionGroupLabel?: string;10 optionGroupChildren?: string;11}1213export type OnTextInject = (newText: string, newCursorPosition: number) => void;1415interface SuggestionProps {16 message: string;17 textareaRef: React.RefObject<HTMLTextAreaElement | null>;18 triggerConfigs: TriggerConfig[];19 onInject: OnTextInject;20}2122const useSuggestionContext = (23 message: string,24 textareaRef: React.RefObject<HTMLTextAreaElement | null>,25 triggerConfigs: TriggerConfig[],26) => {27 const context = React.useMemo(() => {28 const textarea = textareaRef?.current;29 if (!textarea) return null;3031 const caretPositionIndex = textarea.selectionStart ?? message.length;32 let currentConfig: TriggerConfig | undefined;33 let triggerIndex = -1;34 let queryText = "";3536 for (let i = caretPositionIndex - 1; i >= 0; i--) {37 const char = message[i];38 const matchedConfig = triggerConfigs.find(39 (config) => config.char === char,40 );41 if (matchedConfig) {42 currentConfig = matchedConfig;43 triggerIndex = i;44 queryText = message.slice(i + 1, caretPositionIndex);45 break;46 }47 }48 if (!currentConfig) return null;49 return {50 currentConfig,51 triggerIndex,52 queryText,53 };54 }, [message, textareaRef, triggerConfigs]);55 return context;56};57export default function Suggestion({58 message,59 textareaRef,60 triggerConfigs,61 onInject,62}: SuggestionProps) {63 const context = useSuggestionContext(message, textareaRef, triggerConfigs);6465 if (!context) return null;6667 return (68 <div className="absolute bottom-full left-0 w-full bg-white dark:bg-gray-50 rounded-lg shadow-amber-50 max-h-64 overflow-y-auto">69 <List70 options={context.currentConfig.data}71 optionLabel={context.currentConfig.optionLabel ?? "default value"}72 optionGroupLabel={context.currentConfig.optionGroupLabel}73 optionGroupChildren={context.currentConfig.optionGroupChildren}74 onChange={(e) => {75 const newText: string = (e.value ?? e.target.value) as string;76 onInject(newText, context.triggerIndex);77 }}78 />79 </div>80 );81}
