mirror of https://github.com/buster-so/buster.git
update mention input for
This commit is contained in:
parent
d4075311a3
commit
4807e092bb
|
@ -1,11 +1,11 @@
|
|||
import type { Meta, StoryObj } from '@storybook/react-vite';
|
||||
import { fn } from 'storybook/test';
|
||||
import { BusterInput } from './BusterInput';
|
||||
import type { BusterInputProps } from './BusterInput.types';
|
||||
|
||||
const meta: Meta<typeof BusterInput> = {
|
||||
title: 'UI/Inputs/BusterInput',
|
||||
component: BusterInput,
|
||||
tags: ['autodocs'],
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<div style={{ width: '300px', minHeight: '500px' }}>
|
||||
|
@ -78,5 +78,6 @@ export const Default: Story = {
|
|||
value: 'Sample text value',
|
||||
suggestionItems: items,
|
||||
mentions,
|
||||
onSubmit: fn(),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -15,6 +15,7 @@ export const BusterInput = ({
|
|||
emptyComponent,
|
||||
submitting,
|
||||
onSubmit,
|
||||
onPressEnter,
|
||||
disabled: disabledGlobal = false,
|
||||
onStop,
|
||||
sendIcon,
|
||||
|
@ -105,6 +106,7 @@ export const BusterInput = ({
|
|||
shouldFilter={shouldFilter}
|
||||
filter={filter}
|
||||
onMentionItemClick={onMentionItemClick}
|
||||
onPressEnter={onPressEnter || onSubmit}
|
||||
/>
|
||||
</BusterInputContainer>
|
||||
<BusterInputList show={showSuggestionList}>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type { Command } from 'cmdk';
|
||||
import type React from 'react';
|
||||
import type { MentionSuggestionExtension } from '../MentionInput';
|
||||
import type { MentionTriggerItem } from '../MentionInput/MentionInput.types';
|
||||
import type { MentionInputProps, MentionTriggerItem } from '../MentionInput/MentionInput.types';
|
||||
|
||||
/**
|
||||
* @description Override the addValueToInput and closeOnSelect props for the item based on the group props
|
||||
|
@ -59,6 +59,7 @@ export type BusterInputProps<T = string> = {
|
|||
disabled?: boolean;
|
||||
readOnly?: boolean;
|
||||
onSubmit: (value: string) => void;
|
||||
onPressEnter: MentionInputProps['onPressEnter'];
|
||||
onStop: () => void;
|
||||
variant?: 'default';
|
||||
autoFocus?: boolean;
|
||||
|
|
|
@ -15,6 +15,7 @@ export type BusterMentionsInputProps = Pick<
|
|||
| 'onMentionItemClick'
|
||||
> & {
|
||||
onChange: MentionInputProps['onChange'];
|
||||
onPressEnter: MentionInputProps['onPressEnter'];
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
autoFocus?: boolean;
|
||||
|
@ -35,7 +36,9 @@ export const BusterMentionsInput = ({
|
|||
<Command.Input
|
||||
value={value}
|
||||
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"
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
|
|
@ -2,23 +2,13 @@ import { ClientOnly } from '@tanstack/react-router';
|
|||
import Document from '@tiptap/extension-document';
|
||||
import Paragraph from '@tiptap/extension-paragraph';
|
||||
import Text from '@tiptap/extension-text';
|
||||
import {
|
||||
type Editor,
|
||||
EditorContent,
|
||||
EditorContext,
|
||||
type NodeType,
|
||||
type TextType,
|
||||
useEditor,
|
||||
} from '@tiptap/react';
|
||||
import { EditorContent, EditorContext, useEditor } from '@tiptap/react';
|
||||
import { useMemo } from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { MentionExtension } from './MentionExtension';
|
||||
import type {
|
||||
MentionArrayItem,
|
||||
MentionInputProps,
|
||||
MentionSuggestionExtension,
|
||||
} from './MentionInput.types';
|
||||
import type { MentionPillAttributes } from './MentionPill';
|
||||
import type { MentionInputProps, MentionSuggestionExtension } from './MentionInput.types';
|
||||
import { SubmitOnEnter } from './SubmitEnterExtension';
|
||||
import { onUpdateTransformer } from './update-transformers';
|
||||
|
||||
export const MentionInput = ({
|
||||
mentions,
|
||||
|
@ -31,6 +21,7 @@ export const MentionInput = ({
|
|||
className,
|
||||
readOnly,
|
||||
disabled,
|
||||
onPressEnter,
|
||||
}: MentionInputProps) => {
|
||||
const mentionsByTrigger = useMemo(() => {
|
||||
return mentions.reduce(
|
||||
|
@ -45,7 +36,16 @@ export const MentionInput = ({
|
|||
}, [mentions]);
|
||||
|
||||
const editor = useEditor({
|
||||
extensions: [Document, Paragraph, Text, MentionExtension(mentions)],
|
||||
extensions: [
|
||||
Document,
|
||||
Paragraph,
|
||||
Text,
|
||||
MentionExtension(mentions),
|
||||
SubmitOnEnter({
|
||||
mentionsByTrigger,
|
||||
onPressEnter,
|
||||
}),
|
||||
],
|
||||
content: defaultValue,
|
||||
autofocus: autoFocus,
|
||||
editable: !disabled && !readOnly,
|
||||
|
@ -75,48 +75,3 @@ export const MentionInput = ({
|
|||
</ClientOnly>
|
||||
);
|
||||
};
|
||||
|
||||
const onUpdateTransformer = ({
|
||||
editor,
|
||||
mentionsByTrigger,
|
||||
}: {
|
||||
editor: Editor;
|
||||
mentionsByTrigger: Record<string, MentionSuggestionExtension>;
|
||||
}) => {
|
||||
const editorText = editor.getText();
|
||||
const editorJson = editor.getJSON();
|
||||
const transformedJson: MentionArrayItem[] = editorJson.content.reduce<MentionArrayItem[]>(
|
||||
(acc, item) => {
|
||||
if (item.type === 'paragraph') {
|
||||
item.content?.forEach((item) => {
|
||||
if (item.type === 'text') {
|
||||
const _item = item as TextType;
|
||||
acc.push({ type: 'text', text: _item.text });
|
||||
} else if (item.type === 'mention') {
|
||||
const _item = item as NodeType<'mention', MentionPillAttributes>;
|
||||
acc.push({ type: 'mention', attrs: _item.attrs });
|
||||
}
|
||||
});
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
[]
|
||||
);
|
||||
const transformedValue = transformedJson.reduce((acc, item) => {
|
||||
if (item.type === 'text') {
|
||||
return acc + item.text;
|
||||
}
|
||||
if (item.type === 'mention') {
|
||||
const onChangeTransform = mentionsByTrigger[item.attrs.trigger]?.onChangeTransform;
|
||||
if (onChangeTransform) return acc + onChangeTransform(item.attrs);
|
||||
return acc + item.attrs.label;
|
||||
}
|
||||
return acc;
|
||||
}, '');
|
||||
|
||||
return {
|
||||
transformedValue,
|
||||
transformedJson,
|
||||
editorText,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -90,6 +90,7 @@ export type MentionOnChangeParams = (
|
|||
export type MentionInputProps = {
|
||||
mentions: MentionSuggestionExtension[];
|
||||
onChange: MentionOnChangeParams;
|
||||
onPressEnter?: MentionOnChangeParams;
|
||||
onFocus?: (v: EditorEvents['focus']) => void;
|
||||
onBlur?: (v: EditorEvents['blur']) => void;
|
||||
defaultValue?: string;
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import { Extension } from '@tiptap/core';
|
||||
import type { MentionInputProps, MentionSuggestionExtension } from './MentionInput.types';
|
||||
import { onUpdateTransformer } from './update-transformers';
|
||||
|
||||
export const SubmitOnEnter = ({
|
||||
onPressEnter,
|
||||
mentionsByTrigger,
|
||||
}: {
|
||||
onPressEnter?: MentionInputProps['onPressEnter'];
|
||||
mentionsByTrigger: Record<string, MentionSuggestionExtension>;
|
||||
}) =>
|
||||
Extension.create({
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
Enter: ({ editor }) => {
|
||||
console.log('Enter', onPressEnter);
|
||||
if (onPressEnter) {
|
||||
const { transformedValue, transformedJson, editorText } = onUpdateTransformer({
|
||||
editor,
|
||||
mentionsByTrigger,
|
||||
});
|
||||
onPressEnter?.(transformedValue, transformedJson, editorText);
|
||||
}
|
||||
return !!onPressEnter;
|
||||
},
|
||||
'Shift-Enter': () => this.editor.commands.newlineInCode(), // or insert a break
|
||||
};
|
||||
},
|
||||
});
|
|
@ -0,0 +1,48 @@
|
|||
import type { Editor, NodeType, TextType } from '@tiptap/react';
|
||||
import type { MentionArrayItem, MentionSuggestionExtension } from './MentionInput.types';
|
||||
import type { MentionPillAttributes } from './MentionPill';
|
||||
|
||||
export const onUpdateTransformer = ({
|
||||
editor,
|
||||
mentionsByTrigger,
|
||||
}: {
|
||||
editor: Editor;
|
||||
mentionsByTrigger: Record<string, MentionSuggestionExtension>;
|
||||
}) => {
|
||||
const editorText = editor.getText();
|
||||
const editorJson = editor.getJSON();
|
||||
const transformedJson: MentionArrayItem[] = editorJson.content.reduce<MentionArrayItem[]>(
|
||||
(acc, item) => {
|
||||
if (item.type === 'paragraph') {
|
||||
item.content?.forEach((item) => {
|
||||
if (item.type === 'text') {
|
||||
const _item = item as TextType;
|
||||
acc.push({ type: 'text', text: _item.text });
|
||||
} else if (item.type === 'mention') {
|
||||
const _item = item as NodeType<'mention', MentionPillAttributes>;
|
||||
acc.push({ type: 'mention', attrs: _item.attrs });
|
||||
}
|
||||
});
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
[]
|
||||
);
|
||||
const transformedValue = transformedJson.reduce((acc, item) => {
|
||||
if (item.type === 'text') {
|
||||
return acc + item.text;
|
||||
}
|
||||
if (item.type === 'mention') {
|
||||
const onChangeTransform = mentionsByTrigger[item.attrs.trigger]?.onChangeTransform;
|
||||
if (onChangeTransform) return acc + onChangeTransform(item.attrs);
|
||||
return acc + item.attrs.label;
|
||||
}
|
||||
return acc;
|
||||
}, '');
|
||||
|
||||
return {
|
||||
transformedValue,
|
||||
transformedJson,
|
||||
editorText,
|
||||
};
|
||||
};
|
Loading…
Reference in New Issue