refactor: improve InlineCombobox context handling and optimize search functionality

This commit is contained in:
Anish Sarkar 2026-02-17 00:05:38 +05:30
parent 648b00da64
commit 0edfd116af
2 changed files with 24 additions and 11 deletions

View file

@ -28,6 +28,18 @@ import { useComposedRef, useEditorRef } from 'platejs/react';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
function useRequiredComboboxContext() {
const context = useComboboxContext();
if (!context) {
throw new Error(
'InlineCombobox compound components must be rendered within InlineCombobox'
);
}
return context;
}
type FilterFn = ( type FilterFn = (
item: { value: string; group?: string; keywords?: string[]; label?: string }, item: { value: string; group?: string; keywords?: string[]; label?: string },
search: string search: string
@ -56,7 +68,7 @@ const defaultFilter: FilterFn = (
); );
return Array.from(uniqueTerms).some((keyword) => return Array.from(uniqueTerms).some((keyword) =>
filterWords(keyword!, search) filterWords(keyword as string, search)
); );
}; };
@ -91,7 +103,7 @@ const InlineCombobox = ({
// Check if current user is the creator of this element (for Yjs collaboration) // Check if current user is the creator of this element (for Yjs collaboration)
const isCreator = React.useMemo(() => { const isCreator = React.useMemo(() => {
const elementUserId = (element as any).userId; const elementUserId = (element as Record<string, unknown>).userId;
const currentUserId = editor.meta.userId; const currentUserId = editor.meta.userId;
// If no userId (backwards compatibility or non-Yjs), allow // If no userId (backwards compatibility or non-Yjs), allow
@ -170,10 +182,8 @@ const InlineCombobox = ({
trigger, trigger,
showTrigger, showTrigger,
filter, filter,
inputRef,
inputProps, inputProps,
removeInput, removeInput,
setHasEmpty,
] ]
); );
@ -189,6 +199,8 @@ const InlineCombobox = ({
* item. * item.
*/ */
React.useEffect(() => { React.useEffect(() => {
if (items.length === 0) return;
if (!store.getState().activeId) { if (!store.getState().activeId) {
store.setActiveId(store.first()); store.setActiveId(store.first());
} }
@ -225,7 +237,7 @@ const InlineComboboxInput = ({
trigger, trigger,
} = React.useContext(InlineComboboxContext); } = React.useContext(InlineComboboxContext);
const store = useComboboxContext()!; const store = useRequiredComboboxContext();
const value = store.useState('value'); const value = store.useState('value');
const ref = useComposedRef(propRef, contextRef); const ref = useComposedRef(propRef, contextRef);
@ -341,14 +353,15 @@ const InlineComboboxItem = ({
const { filter, removeInput } = React.useContext(InlineComboboxContext); const { filter, removeInput } = React.useContext(InlineComboboxContext);
const store = useComboboxContext()!; const store = useRequiredComboboxContext();
// Optimization: Do not subscribe to value if filter is false // Always call hook unconditionally; only use value if filter is active
const search = filter && store.useState('value'); const storeValue = store.useState('value');
const search = filter ? storeValue : '';
const visible = React.useMemo( const visible = React.useMemo(
() => () =>
!filter || filter({ group, keywords, label, value }, search as string), !filter || filter({ group, keywords, label, value }, search),
[filter, group, keywords, label, value, search] [filter, group, keywords, label, value, search]
); );
@ -371,7 +384,7 @@ const InlineComboboxEmpty = ({
className, className,
}: React.HTMLAttributes<HTMLDivElement>) => { }: React.HTMLAttributes<HTMLDivElement>) => {
const { setHasEmpty } = React.useContext(InlineComboboxContext); const { setHasEmpty } = React.useContext(InlineComboboxContext);
const store = useComboboxContext()!; const store = useRequiredComboboxContext();
const items = store.useState('items'); const items = store.useState('items');
React.useEffect(() => { React.useEffect(() => {

View file

@ -186,7 +186,7 @@ export function SlashInputElement({
> >
<InlineComboboxInput /> <InlineComboboxInput />
<InlineComboboxContent> <InlineComboboxContent className="dark:bg-neutral-800 dark:border dark:border-neutral-700">
<InlineComboboxEmpty>No results found.</InlineComboboxEmpty> <InlineComboboxEmpty>No results found.</InlineComboboxEmpty>
{slashCommandGroups.map(({ heading, items }) => ( {slashCommandGroups.map(({ heading, items }) => (