mirror of https://github.com/buster-so/buster.git
pass the select through
This commit is contained in:
parent
8469471a45
commit
5055530bdf
|
@ -1,6 +1,7 @@
|
||||||
import { Command } from 'cmdk';
|
import { Command } from 'cmdk';
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { useMemoizedFn } from '@/hooks/useMemoizedFn';
|
import { useMemoizedFn } from '@/hooks/useMemoizedFn';
|
||||||
|
import type { MentionInputRef } from '../MentionInput';
|
||||||
import type { BusterInputProps, BusterOnSelectParams } from './BusterInput.types';
|
import type { BusterInputProps, BusterOnSelectParams } from './BusterInput.types';
|
||||||
import { BusterInputContainer } from './BusterInputContainer';
|
import { BusterInputContainer } from './BusterInputContainer';
|
||||||
import { BusterInputEmpty } from './BusterInputEmpty';
|
import { BusterInputEmpty } from './BusterInputEmpty';
|
||||||
|
@ -41,6 +42,7 @@ export const BusterInput = ({
|
||||||
const commandListNavigatedRef = useRef(false);
|
const commandListNavigatedRef = useRef(false);
|
||||||
|
|
||||||
const commandRef = useRef<HTMLDivElement>(null);
|
const commandRef = useRef<HTMLDivElement>(null);
|
||||||
|
const mentionsInputRef = useRef<MentionInputRef>(null);
|
||||||
|
|
||||||
const showSuggestionList = !hasClickedSelect && suggestionItems.length > 0;
|
const showSuggestionList = !hasClickedSelect && suggestionItems.length > 0;
|
||||||
|
|
||||||
|
@ -53,7 +55,7 @@ export const BusterInput = ({
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onSelectItem = useMemoizedFn(({ onClick, ...params }: BusterOnSelectParams) => {
|
const onSelectItem = useMemoizedFn(({ onClick, ...params }: BusterOnSelectParams) => {
|
||||||
const { addValueToInput, value, loading, inputValue, label, disabled } = params;
|
const { addValueToInput, loading, inputValue, label, disabled } = params;
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
console.warn('Item is disabled', params);
|
console.warn('Item is disabled', params);
|
||||||
return;
|
return;
|
||||||
|
@ -66,8 +68,11 @@ export const BusterInput = ({
|
||||||
console.warn('Item is loading', params);
|
console.warn('Item is loading', params);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log('onSelectItem', addValueToInput, params);
|
if (addValueToInput) {
|
||||||
if (addValueToInput) setValue(inputValue ?? String(label));
|
const stringValue = inputValue ?? String(label);
|
||||||
|
mentionsInputRef.current?.editor?.commands.setContent(stringValue);
|
||||||
|
setValue(stringValue);
|
||||||
|
}
|
||||||
onClick?.();
|
onClick?.();
|
||||||
if (closeSuggestionOnSelect) setHasClickedSelect(true);
|
if (closeSuggestionOnSelect) setHasClickedSelect(true);
|
||||||
onSuggestionItemClick?.(params);
|
onSuggestionItemClick?.(params);
|
||||||
|
@ -128,6 +133,7 @@ export const BusterInput = ({
|
||||||
variant={variant}
|
variant={variant}
|
||||||
>
|
>
|
||||||
<BusterMentionsInput
|
<BusterMentionsInput
|
||||||
|
ref={mentionsInputRef}
|
||||||
defaultValue={defaultValue}
|
defaultValue={defaultValue}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly}
|
||||||
autoFocus={autoFocus}
|
autoFocus={autoFocus}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/** biome-ignore-all lint/complexity/noUselessFragments: Intersting bug when NOT using fragments */
|
/** biome-ignore-all lint/complexity/noUselessFragments: Intersting bug when NOT using fragments */
|
||||||
import { Command } from 'cmdk';
|
import { Command } from 'cmdk';
|
||||||
import React from 'react';
|
import React, { forwardRef } from 'react';
|
||||||
import { MentionInput, type MentionInputProps } from '../MentionInput';
|
import { MentionInput, type MentionInputProps, type MentionInputRef } from '../MentionInput';
|
||||||
import type { BusterInputProps } from './BusterInput.types';
|
import type { BusterInputProps } from './BusterInput.types';
|
||||||
|
|
||||||
export type BusterMentionsInputProps = Pick<
|
export type BusterMentionsInputProps = Pick<
|
||||||
|
@ -23,24 +23,21 @@ export type BusterMentionsInputProps = Pick<
|
||||||
commandListNavigatedRef?: React.RefObject<boolean>;
|
commandListNavigatedRef?: React.RefObject<boolean>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const BusterMentionsInput = ({
|
export const BusterMentionsInput = forwardRef<MentionInputRef, BusterMentionsInputProps>(
|
||||||
value: valueProp,
|
({ value: valueProp, placeholder, defaultValue, mentions, value, ...props }, ref) => {
|
||||||
placeholder,
|
return (
|
||||||
defaultValue,
|
<React.Fragment>
|
||||||
mentions,
|
<MentionInput ref={ref} mentions={mentions} defaultValue={value} {...props} />
|
||||||
value,
|
<Command.Input
|
||||||
...props
|
value={value}
|
||||||
}: BusterMentionsInputProps) => {
|
autoFocus={false}
|
||||||
return (
|
// className="sr-only hidden h-0 border-0 p-0"
|
||||||
<React.Fragment>
|
className="absolute -top-5 left-0 w-full outline-1 outline-amber-200 pointer-events-none"
|
||||||
<MentionInput mentions={mentions} defaultValue={value} {...props} />
|
aria-hidden="true"
|
||||||
<Command.Input
|
/>
|
||||||
value={value}
|
</React.Fragment>
|
||||||
autoFocus={false}
|
);
|
||||||
// className="sr-only hidden h-0 border-0 p-0"
|
}
|
||||||
className="absolute -top-5 left-0 w-full outline-1 outline-amber-200 pointer-events-none"
|
);
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
BusterMentionsInput.displayName = 'BusterMentionsInput';
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
|
@ -3,77 +3,96 @@ import Document from '@tiptap/extension-document';
|
||||||
import Paragraph from '@tiptap/extension-paragraph';
|
import Paragraph from '@tiptap/extension-paragraph';
|
||||||
import Text from '@tiptap/extension-text';
|
import Text from '@tiptap/extension-text';
|
||||||
import { EditorContent, EditorContext, useEditor } from '@tiptap/react';
|
import { EditorContent, EditorContext, useEditor } from '@tiptap/react';
|
||||||
import { useMemo } from 'react';
|
import { forwardRef, useImperativeHandle, useMemo } from 'react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { MentionExtension } from './MentionExtension';
|
import { MentionExtension } from './MentionExtension';
|
||||||
import type { MentionInputProps, MentionSuggestionExtension } from './MentionInput.types';
|
import type {
|
||||||
|
MentionInputProps,
|
||||||
|
MentionInputRef,
|
||||||
|
MentionSuggestionExtension,
|
||||||
|
} from './MentionInput.types';
|
||||||
import { SubmitOnEnter } from './SubmitEnterExtension';
|
import { SubmitOnEnter } from './SubmitEnterExtension';
|
||||||
import { onUpdateTransformer } from './update-transformers';
|
import { onUpdateTransformer } from './update-transformers';
|
||||||
|
|
||||||
export const MentionInput = ({
|
export const MentionInput = forwardRef<MentionInputRef, MentionInputProps>(
|
||||||
mentions,
|
(
|
||||||
onChange,
|
{
|
||||||
defaultValue = '',
|
mentions,
|
||||||
onFocus,
|
onChange,
|
||||||
onBlur,
|
defaultValue = '',
|
||||||
autoFocus,
|
onFocus,
|
||||||
style,
|
onBlur,
|
||||||
className,
|
autoFocus,
|
||||||
readOnly,
|
style,
|
||||||
disabled,
|
className,
|
||||||
onPressEnter,
|
readOnly,
|
||||||
commandListNavigatedRef,
|
disabled,
|
||||||
}: MentionInputProps) => {
|
onPressEnter,
|
||||||
const mentionsByTrigger = useMemo(() => {
|
commandListNavigatedRef,
|
||||||
return mentions.reduce(
|
|
||||||
(acc, mention) => {
|
|
||||||
if (mention.char) {
|
|
||||||
acc[mention.char] = mention;
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
{} as Record<string, MentionSuggestionExtension>
|
|
||||||
);
|
|
||||||
}, [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 }) => {
|
ref
|
||||||
const { transformedValue, transformedJson, editorText } = onUpdateTransformer({
|
) => {
|
||||||
|
const mentionsByTrigger = useMemo(() => {
|
||||||
|
return mentions.reduce(
|
||||||
|
(acc, mention) => {
|
||||||
|
if (mention.char) {
|
||||||
|
acc[mention.char] = mention;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Record<string, MentionSuggestionExtension>
|
||||||
|
);
|
||||||
|
}, [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,
|
editor,
|
||||||
mentionsByTrigger,
|
}),
|
||||||
});
|
[editor]
|
||||||
onChange?.(transformedValue, transformedJson, editorText);
|
);
|
||||||
},
|
|
||||||
onFocus: onFocus,
|
|
||||||
onBlur: onBlur,
|
|
||||||
});
|
|
||||||
|
|
||||||
const providerValue = useMemo(() => ({ editor }), [editor]);
|
const providerValue = useMemo(() => ({ editor }), [editor]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ClientOnly>
|
<ClientOnly>
|
||||||
<EditorContext.Provider value={providerValue}>
|
<EditorContext.Provider value={providerValue}>
|
||||||
<EditorContent className="outline-0" editor={editor} style={style} />
|
<EditorContent className="outline-0" editor={editor} style={style} />
|
||||||
</EditorContext.Provider>
|
</EditorContext.Provider>
|
||||||
</ClientOnly>
|
</ClientOnly>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
MentionInput.displayName = 'MentionInput';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { MentionNodeAttrs, MentionOptions } from '@tiptap/extension-mention';
|
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';
|
import type { MentionPillAttributes } from './MentionPill';
|
||||||
|
|
||||||
export type MentionOnSelectParams<T = unknown> = {
|
export type MentionOnSelectParams<T = unknown> = {
|
||||||
|
@ -102,6 +102,10 @@ export type MentionInputProps = {
|
||||||
commandListNavigatedRef?: React.RefObject<boolean>;
|
commandListNavigatedRef?: React.RefObject<boolean>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type MentionInputRef = {
|
||||||
|
editor: Editor | null;
|
||||||
|
};
|
||||||
|
|
||||||
declare module '@tiptap/core' {
|
declare module '@tiptap/core' {
|
||||||
interface Storage {
|
interface Storage {
|
||||||
mention: {
|
mention: {
|
||||||
|
|
Loading…
Reference in New Issue