import React from "react";
import type { SelectItemOptionsType } from "@/list";
import { List } from "@/list";
import "./tailwind.css";
export interface TriggerConfig {
char: string;
data: SelectItemOptionsType;
optionLabel: string;
optionGroupLabel?: string;
optionGroupChildren?: string;
}
export type OnTextInject = (newText: string, newCursorPosition: number) => void;
interface SuggestionProps {
message: string;
textareaRef: React.RefObject<HTMLTextAreaElement | null>;
triggerConfigs: TriggerConfig[];
onInject: OnTextInject;
}
const useSuggestionContext = (
message: string,
textareaRef: React.RefObject<HTMLTextAreaElement | null>,
triggerConfigs: TriggerConfig[],
) => {
const context = React.useMemo(() => {
const textarea = textareaRef?.current;
if (!textarea) return null;
const caretPositionIndex = textarea.selectionStart ?? message.length;
let currentConfig: TriggerConfig | undefined;
let triggerIndex = -1;
let queryText = "";
for (let i = caretPositionIndex - 1; i >= 0; i--) {
const char = message[i];
const matchedConfig = triggerConfigs.find(
(config) => config.char === char,
);
if (matchedConfig) {
currentConfig = matchedConfig;
triggerIndex = i;
queryText = message.slice(i + 1, caretPositionIndex);
break;
}
}
if (!currentConfig) return null;
return {
currentConfig,
triggerIndex,
queryText,
};
}, [message, textareaRef, triggerConfigs]);
return context;
};
export default function Suggestion({
message,
textareaRef,
triggerConfigs,
onInject,
}: SuggestionProps) {
const context = useSuggestionContext(message, textareaRef, triggerConfigs);
if (!context) return null;
return (
<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">
<List
options={context.currentConfig.data}
optionLabel={context.currentConfig.optionLabel ?? "default value"}
optionGroupLabel={context.currentConfig.optionGroupLabel}
optionGroupChildren={context.currentConfig.optionGroupChildren}
onChange={(e) => {
const newText: string = (e.value ?? e.target.value) as string;
onInject(newText, context.triggerIndex);
}}
/>
</div>
);
}