mirror of https://github.com/buster-so/buster.git
Change component names
This commit is contained in:
parent
8f2663c3b2
commit
e2a298bf9e
|
@ -1,22 +0,0 @@
|
|||
import type React from 'react';
|
||||
import { cn } from '@/lib/classMerge';
|
||||
import type { BusterInputContainerProps } from './BusterInput.types';
|
||||
|
||||
export const BusterInputContainer: React.FC<BusterInputContainerProps> = ({
|
||||
children,
|
||||
className,
|
||||
style,
|
||||
sendIcon,
|
||||
secondaryActions,
|
||||
submitting,
|
||||
disabled,
|
||||
onStop,
|
||||
onSubmit,
|
||||
variant,
|
||||
}) => {
|
||||
return (
|
||||
<div data-testid="buster-input-container" className="flex flex-col gap-2">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,62 +0,0 @@
|
|||
import type {
|
||||
BusterInputProps,
|
||||
BusterOnSelectParams,
|
||||
GroupOverrideProps,
|
||||
} from './BusterInput.types';
|
||||
import { BusterInputGroup } from './BusterInputGroup';
|
||||
import { BusterInputItem } from './BusterInputItem';
|
||||
import { BusterInputSeparator } from './BusterInputSeparator';
|
||||
|
||||
export const BusterItemSelector = ({
|
||||
item,
|
||||
onSelect,
|
||||
addValueToInput,
|
||||
closeOnSelect,
|
||||
}: {
|
||||
item: BusterInputProps['suggestionItems'][number];
|
||||
onSelect: (params: BusterOnSelectParams) => void;
|
||||
} & GroupOverrideProps) => {
|
||||
if (item.type === 'separator') {
|
||||
return <BusterInputSeparator />;
|
||||
}
|
||||
|
||||
if (item.type === 'group') {
|
||||
return <BusterInputGroup {...item} onSelect={onSelect} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<BusterInputItem
|
||||
{...item}
|
||||
onSelect={onSelect}
|
||||
addValueToInput={item?.addValueToInput ?? addValueToInput}
|
||||
closeOnSelect={item?.closeOnSelect ?? closeOnSelect}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const BusterItemsSelector = ({
|
||||
suggestionItems,
|
||||
onSelect,
|
||||
addValueToInput,
|
||||
closeOnSelect,
|
||||
}: {
|
||||
suggestionItems: BusterInputProps['suggestionItems'];
|
||||
onSelect: (params: BusterOnSelectParams) => void;
|
||||
} & GroupOverrideProps) => {
|
||||
if (!suggestionItems) return null;
|
||||
return suggestionItems.map((item, index) => (
|
||||
<BusterItemSelector
|
||||
key={keySelector(item, index)}
|
||||
item={item}
|
||||
onSelect={onSelect}
|
||||
addValueToInput={addValueToInput}
|
||||
closeOnSelect={closeOnSelect}
|
||||
/>
|
||||
));
|
||||
};
|
||||
|
||||
const keySelector = (item: BusterInputProps['suggestionItems'][number], index: number) => {
|
||||
if (item.type === 'separator') return `separator${index}`;
|
||||
if (item.type === 'group') return `${item.label} + index`;
|
||||
return item.value;
|
||||
};
|
|
@ -1,42 +0,0 @@
|
|||
/** biome-ignore-all lint/complexity/noUselessFragments: Intersting bug when NOT using fragments */
|
||||
import { Command } from 'cmdk';
|
||||
import React, { forwardRef } from 'react';
|
||||
import { MentionInput, type MentionInputProps, type MentionInputRef } from '../MentionInput';
|
||||
import type { BusterInputProps } from './BusterInput.types';
|
||||
|
||||
export type BusterMentionsInputProps = Pick<
|
||||
BusterInputProps,
|
||||
| 'mentions'
|
||||
| 'value'
|
||||
| 'placeholder'
|
||||
| 'defaultValue'
|
||||
| 'shouldFilter'
|
||||
| 'filter'
|
||||
| 'onMentionItemClick'
|
||||
> & {
|
||||
onChange: MentionInputProps['onChange'];
|
||||
onPressEnter: MentionInputProps['onPressEnter'];
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
autoFocus?: boolean;
|
||||
readOnly?: boolean;
|
||||
commandListNavigatedRef?: React.RefObject<boolean>;
|
||||
};
|
||||
|
||||
export const BusterMentionsInput = forwardRef<MentionInputRef, BusterMentionsInputProps>(
|
||||
({ value: valueProp, placeholder, defaultValue, mentions, value, ...props }, ref) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<MentionInput ref={ref} mentions={mentions} defaultValue={value} {...props} />
|
||||
<Command.Input
|
||||
value={value}
|
||||
autoFocus={false}
|
||||
className="sr-only hidden h-0 border-0 p-0 pointer-events-none w-full"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
BusterMentionsInput.displayName = 'BusterMentionsInput';
|
|
@ -3,6 +3,7 @@ import Document from '@tiptap/extension-document';
|
|||
import Paragraph from '@tiptap/extension-paragraph';
|
||||
import Text from '@tiptap/extension-text';
|
||||
import { EditorContent, EditorContext, useEditor } from '@tiptap/react';
|
||||
import { cva } from 'class-variance-authority';
|
||||
import { forwardRef, useImperativeHandle, useMemo } from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { MentionExtension } from './MentionExtension';
|
||||
|
@ -14,6 +15,15 @@ import type {
|
|||
import { SubmitOnEnter } from './SubmitEnterExtension';
|
||||
import { onUpdateTransformer } from './update-transformers';
|
||||
|
||||
const variants = cva('outline-0', {
|
||||
variants: {
|
||||
variant: {
|
||||
default: '',
|
||||
ghost: '',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const MentionInput = forwardRef<MentionInputRef, MentionInputProps>(
|
||||
(
|
||||
{
|
||||
|
@ -29,9 +39,11 @@ export const MentionInput = forwardRef<MentionInputRef, MentionInputProps>(
|
|||
disabled,
|
||||
onPressEnter,
|
||||
commandListNavigatedRef,
|
||||
variant = 'default',
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const classes = variants({ variant });
|
||||
const mentionsByTrigger = useMemo(() => {
|
||||
return mentions.reduce(
|
||||
(acc, mention) => {
|
||||
|
@ -61,7 +73,7 @@ export const MentionInput = forwardRef<MentionInputRef, MentionInputProps>(
|
|||
editable: !disabled && !readOnly,
|
||||
editorProps: {
|
||||
attributes: {
|
||||
class: cn('p-1 border rounded outline-0', className),
|
||||
class: cn('p-1', classes, className),
|
||||
},
|
||||
},
|
||||
onUpdate: ({ editor }) => {
|
||||
|
|
|
@ -100,6 +100,7 @@ export type MentionInputProps = {
|
|||
readOnly?: boolean;
|
||||
disabled?: boolean;
|
||||
commandListNavigatedRef?: React.RefObject<boolean>;
|
||||
variant?: 'default' | 'ghost';
|
||||
};
|
||||
|
||||
export type MentionInputRef = {
|
||||
|
|
|
@ -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';
|
||||
import { MentionInputSuggestions } from './MentionInputSuggestions';
|
||||
import type { MentionInputSuggestionsProps } from './MentionInputSuggestions.types';
|
||||
|
||||
const meta: Meta<typeof BusterInput> = {
|
||||
title: 'UI/Inputs/BusterInput',
|
||||
component: BusterInput,
|
||||
const meta: Meta<typeof MentionInputSuggestions> = {
|
||||
title: 'UI/Inputs/MentionInputSuggestions',
|
||||
component: MentionInputSuggestions,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<div style={{ width: '300px', minHeight: '500px' }}>
|
||||
|
@ -16,9 +16,9 @@ const meta: Meta<typeof BusterInput> = {
|
|||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof BusterInput>;
|
||||
type Story = StoryObj<typeof MentionInputSuggestions>;
|
||||
|
||||
const items: BusterInputProps['suggestionItems'] = [
|
||||
const items: MentionInputSuggestionsProps['suggestionItems'] = [
|
||||
...Array.from({ length: 3 }, (_, i) => ({
|
||||
label: `Item ${i + 1}`,
|
||||
value: `item${i + 1}`,
|
||||
|
@ -40,7 +40,7 @@ const items: BusterInputProps['suggestionItems'] = [
|
|||
},
|
||||
];
|
||||
|
||||
const mentions: BusterInputProps['mentions'] = [
|
||||
const mentions: MentionInputSuggestionsProps['mentions'] = [
|
||||
// {
|
||||
// trigger: '@',
|
||||
// items: [
|
||||
|
@ -79,5 +79,6 @@ export const Default: Story = {
|
|||
suggestionItems: items,
|
||||
mentions,
|
||||
onSubmit: fn(),
|
||||
children: <div className="bg-red-100 min-h-20">Hello</div>,
|
||||
},
|
||||
};
|
|
@ -2,14 +2,17 @@ import { Command } from 'cmdk';
|
|||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useMemoizedFn } from '@/hooks/useMemoizedFn';
|
||||
import type { MentionInputRef } from '../MentionInput';
|
||||
import type { BusterInputProps, BusterOnSelectParams } from './BusterInput.types';
|
||||
import { BusterInputContainer } from './BusterInputContainer';
|
||||
import { BusterInputEmpty } from './BusterInputEmpty';
|
||||
import { BusterInputList } from './BusterInputList';
|
||||
import { BusterItemsSelector } from './BusterItemSelector';
|
||||
import { BusterMentionsInput } from './BusterMentionsInput';
|
||||
import type {
|
||||
MentionInputSuggestionsOnSelectParams,
|
||||
MentionInputSuggestionsProps,
|
||||
} from './MentionInputSuggestions.types';
|
||||
import { MentionInputSuggestionsContainer } from './MentionInputSuggestionsContainer';
|
||||
import { MentionInputSuggestionsEmpty } from './MentionInputSuggestionsEmpty';
|
||||
import { MentionInputSuggestionsItemsSelector } from './MentionInputSuggestionsItemSelector';
|
||||
import { MentionInputSuggestionsList } from './MentionInputSuggestionsList';
|
||||
import { MentionInputSuggestionsMentionsInput } from './MentionInputSuggestionsMentionsInput';
|
||||
|
||||
export const BusterInput = ({
|
||||
export const MentionInputSuggestions = ({
|
||||
placeholder,
|
||||
defaultValue,
|
||||
value: valueProp,
|
||||
|
@ -23,9 +26,10 @@ export const BusterInput = ({
|
|||
secondaryActions,
|
||||
variant = 'default',
|
||||
onChange,
|
||||
ariaLabel = 'Buster Input',
|
||||
ariaLabel = 'Mention Input Suggestions',
|
||||
readOnly,
|
||||
autoFocus,
|
||||
children,
|
||||
//suggestions
|
||||
suggestionItems,
|
||||
closeSuggestionOnSelect = true,
|
||||
|
@ -36,7 +40,7 @@ export const BusterInput = ({
|
|||
//mentions
|
||||
onMentionItemClick,
|
||||
mentions,
|
||||
}: BusterInputProps) => {
|
||||
}: MentionInputSuggestionsProps) => {
|
||||
const [hasClickedSelect, setHasClickedSelect] = useState(false);
|
||||
const [value, setValue] = useState(valueProp ?? defaultValue);
|
||||
const commandListNavigatedRef = useRef(false);
|
||||
|
@ -54,29 +58,31 @@ export const BusterInput = ({
|
|||
onChange?.(value);
|
||||
}, []);
|
||||
|
||||
const onSelectItem = useMemoizedFn(({ onClick, ...params }: BusterOnSelectParams) => {
|
||||
const { addValueToInput, loading, inputValue, label, disabled } = params;
|
||||
if (disabled) {
|
||||
console.warn('Item is disabled', params);
|
||||
return;
|
||||
const onSelectItem = useMemoizedFn(
|
||||
({ onClick, ...params }: MentionInputSuggestionsOnSelectParams) => {
|
||||
const { addValueToInput, loading, inputValue, label, disabled } = params;
|
||||
if (disabled) {
|
||||
console.warn('Item is disabled', params);
|
||||
return;
|
||||
}
|
||||
if (submitting) {
|
||||
console.warn('Input is submitting');
|
||||
return;
|
||||
}
|
||||
if (loading) {
|
||||
console.warn('Item is loading', params);
|
||||
return;
|
||||
}
|
||||
if (addValueToInput) {
|
||||
const stringValue = inputValue ?? String(label);
|
||||
mentionsInputRef.current?.editor?.commands.setContent(stringValue);
|
||||
setValue(stringValue);
|
||||
}
|
||||
onClick?.();
|
||||
if (closeSuggestionOnSelect) setHasClickedSelect(true);
|
||||
onSuggestionItemClick?.(params);
|
||||
}
|
||||
if (submitting) {
|
||||
console.warn('Input is submitting');
|
||||
return;
|
||||
}
|
||||
if (loading) {
|
||||
console.warn('Item is loading', params);
|
||||
return;
|
||||
}
|
||||
if (addValueToInput) {
|
||||
const stringValue = inputValue ?? String(label);
|
||||
mentionsInputRef.current?.editor?.commands.setContent(stringValue);
|
||||
setValue(stringValue);
|
||||
}
|
||||
onClick?.();
|
||||
if (closeSuggestionOnSelect) setHasClickedSelect(true);
|
||||
onSuggestionItemClick?.(params);
|
||||
});
|
||||
);
|
||||
|
||||
const onSubmitPreflight = useMemoizedFn((value: string) => {
|
||||
if (submitting) {
|
||||
|
@ -123,16 +129,8 @@ export const BusterInput = ({
|
|||
|
||||
return (
|
||||
<Command ref={commandRef} value={value} label={ariaLabel} className="relative">
|
||||
<BusterInputContainer
|
||||
onSubmit={onSubmitPreflight}
|
||||
onStop={onStopPreflight}
|
||||
submitting={submitting}
|
||||
disabled={disabledGlobal}
|
||||
sendIcon={sendIcon}
|
||||
secondaryActions={secondaryActions}
|
||||
variant={variant}
|
||||
>
|
||||
<BusterMentionsInput
|
||||
<MentionInputSuggestionsContainer>
|
||||
<MentionInputSuggestionsMentionsInput
|
||||
ref={mentionsInputRef}
|
||||
defaultValue={defaultValue}
|
||||
readOnly={readOnly}
|
||||
|
@ -147,16 +145,19 @@ export const BusterInput = ({
|
|||
onPressEnter={onPressEnter || onSubmit}
|
||||
commandListNavigatedRef={commandListNavigatedRef}
|
||||
/>
|
||||
</BusterInputContainer>
|
||||
<BusterInputList show={showSuggestionList}>
|
||||
<BusterItemsSelector
|
||||
{children}
|
||||
</MentionInputSuggestionsContainer>
|
||||
<MentionInputSuggestionsList show={showSuggestionList}>
|
||||
<MentionInputSuggestionsItemsSelector
|
||||
suggestionItems={suggestionItems}
|
||||
onSelect={onSelectItem}
|
||||
addValueToInput={addSuggestionValueToInput}
|
||||
closeOnSelect={closeSuggestionOnSelect}
|
||||
/>
|
||||
{emptyComponent && <BusterInputEmpty>{emptyComponent}</BusterInputEmpty>}
|
||||
</BusterInputList>
|
||||
{emptyComponent && (
|
||||
<MentionInputSuggestionsEmpty>{emptyComponent}</MentionInputSuggestionsEmpty>
|
||||
)}
|
||||
</MentionInputSuggestionsList>
|
||||
</Command>
|
||||
);
|
||||
};
|
|
@ -11,7 +11,7 @@ export type GroupOverrideProps = {
|
|||
closeOnSelect: boolean | undefined;
|
||||
};
|
||||
|
||||
export type BusterInputDropdownItem<T = string> = {
|
||||
export type MentionInputSuggestionsDropdownItem<T = string> = {
|
||||
value: T;
|
||||
inputValue?: string; //if this is undefined, the label will be used (string casted), must have addValueToInput set to true
|
||||
label: string | React.ReactNode;
|
||||
|
@ -25,9 +25,9 @@ export type BusterInputDropdownItem<T = string> = {
|
|||
addValueToInput?: boolean; //defaults to group addValueToInput
|
||||
};
|
||||
|
||||
export type BusterOnSelectParams = NonNullable<
|
||||
export type MentionInputSuggestionsOnSelectParams = NonNullable<
|
||||
Pick<
|
||||
NonNullable<BusterInputDropdownItem>,
|
||||
NonNullable<MentionInputSuggestionsDropdownItem>,
|
||||
| 'value'
|
||||
| 'label'
|
||||
| 'addValueToInput'
|
||||
|
@ -39,19 +39,19 @@ export type BusterOnSelectParams = NonNullable<
|
|||
>
|
||||
>;
|
||||
|
||||
export type BusterInputDropdownGroup<T = string> = {
|
||||
export type MentionInputSuggestionsDropdownGroup<T = string> = {
|
||||
label: string | React.ReactNode;
|
||||
suggestionItems: BusterInputDropdownItem<T>[];
|
||||
suggestionItems: MentionInputSuggestionsDropdownItem<T>[];
|
||||
addValueToInput?: boolean;
|
||||
closeOnSelect?: boolean;
|
||||
type: 'group';
|
||||
};
|
||||
|
||||
export type BusterInputSeperator = {
|
||||
export type MentionInputSuggestionsSeparator = {
|
||||
type: 'separator';
|
||||
};
|
||||
|
||||
export type BusterInputProps<T = string> = {
|
||||
export type MentionInputSuggestionsProps<T = string> = {
|
||||
defaultValue: string;
|
||||
value?: string;
|
||||
onChange?: (value: string) => void;
|
||||
|
@ -63,30 +63,31 @@ export type BusterInputProps<T = string> = {
|
|||
onStop: () => void;
|
||||
variant?: 'default';
|
||||
autoFocus?: boolean;
|
||||
sendIcon?: React.ReactNode;
|
||||
secondaryActions?: React.ReactNode;
|
||||
placeholder?: string;
|
||||
ariaLabel?: string;
|
||||
emptyComponent?: React.ReactNode | string | false; //if false, no empty component will be shown
|
||||
children?: React.ReactNode;
|
||||
sendIcon?: React.ReactNode;
|
||||
secondaryActions?: React.ReactNode;
|
||||
//mentions
|
||||
onMentionItemClick?: (params: MentionTriggerItem<T>) => void;
|
||||
mentions: MentionSuggestionExtension[];
|
||||
//suggestions
|
||||
suggestionItems: (
|
||||
| BusterInputDropdownItem<T>
|
||||
| BusterInputDropdownGroup<T>
|
||||
| BusterInputSeperator
|
||||
| MentionInputSuggestionsDropdownItem<T>
|
||||
| MentionInputSuggestionsDropdownGroup<T>
|
||||
| MentionInputSuggestionsSeparator
|
||||
)[];
|
||||
onSuggestionItemClick?: (params: Omit<BusterOnSelectParams, 'onClick'>) => void;
|
||||
onSuggestionItemClick?: (params: Omit<MentionInputSuggestionsOnSelectParams, 'onClick'>) => void;
|
||||
addSuggestionValueToInput?: boolean; //defaults to true
|
||||
closeSuggestionOnSelect?: boolean; //defaults to true
|
||||
} & Pick<React.ComponentProps<typeof Command>, 'filter' | 'shouldFilter'>;
|
||||
|
||||
export type BusterInputContainerProps = {
|
||||
export type MentionInputSuggestionsContainerProps = {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
} & Pick<
|
||||
BusterInputProps,
|
||||
MentionInputSuggestionsProps,
|
||||
'sendIcon' | 'secondaryActions' | 'submitting' | 'disabled' | 'onStop' | 'onSubmit' | 'variant'
|
||||
>;
|
|
@ -0,0 +1,20 @@
|
|||
import type React from 'react';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { cn } from '@/lib/classMerge';
|
||||
|
||||
export const MentionInputSuggestionsContainer: React.FC<
|
||||
PropsWithChildren<{
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
}>
|
||||
> = ({ children, className, style }) => {
|
||||
return (
|
||||
<div
|
||||
data-testid="mention-input-suggestions-container"
|
||||
className={cn('flex flex-col border rounded overflow-hidden', className)}
|
||||
style={style}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -2,7 +2,7 @@ import { Command } from 'cmdk';
|
|||
import type React from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export const BusterInputEmpty = ({
|
||||
export const MentionInputSuggestionsEmpty = ({
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentPropsWithoutRef<typeof Command.Empty>) => {
|
|
@ -1,17 +1,17 @@
|
|||
import { Command } from 'cmdk';
|
||||
import type React from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { BusterInputDropdownGroup, BusterOnSelectParams } from './BusterInput.types';
|
||||
import { BusterItemsSelector } from './BusterItemSelector';
|
||||
import type { MentionInputSuggestionsDropdownGroup, MentionInputSuggestionsOnSelectParams } from './MentionInputSuggestions.types';
|
||||
import { MentionInputSuggestionsItemsSelector } from './MentionInputSuggestionsItemSelector';
|
||||
|
||||
export type BusterInputGroupProps = BusterInputDropdownGroup & {
|
||||
onSelect: (params: BusterOnSelectParams) => void;
|
||||
export type MentionInputSuggestionsGroupProps = MentionInputSuggestionsDropdownGroup & {
|
||||
onSelect: (params: MentionInputSuggestionsOnSelectParams) => void;
|
||||
} & {
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
};
|
||||
|
||||
export const BusterInputGroup = ({
|
||||
export const MentionInputSuggestionsGroup = ({
|
||||
suggestionItems,
|
||||
label,
|
||||
onSelect,
|
||||
|
@ -19,7 +19,7 @@ export const BusterInputGroup = ({
|
|||
className,
|
||||
closeOnSelect,
|
||||
style,
|
||||
}: BusterInputGroupProps) => {
|
||||
}: MentionInputSuggestionsGroupProps) => {
|
||||
return (
|
||||
<Command.Group
|
||||
className={cn(
|
||||
|
@ -29,7 +29,7 @@ export const BusterInputGroup = ({
|
|||
style={style}
|
||||
heading={label}
|
||||
>
|
||||
<BusterItemsSelector
|
||||
<MentionInputSuggestionsItemsSelector
|
||||
suggestionItems={suggestionItems}
|
||||
onSelect={onSelect}
|
||||
addValueToInput={addValueToInput}
|
|
@ -1,16 +1,16 @@
|
|||
import { Command } from 'cmdk';
|
||||
import type React from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { BusterInputDropdownItem, BusterOnSelectParams } from './BusterInput.types';
|
||||
import type { MentionInputSuggestionsDropdownItem, MentionInputSuggestionsOnSelectParams } from './MentionInputSuggestions.types';
|
||||
|
||||
export type BusterInputItemProps = {
|
||||
onSelect: (d: BusterOnSelectParams) => void;
|
||||
} & BusterInputDropdownItem & {
|
||||
export type MentionInputSuggestionsItemProps = {
|
||||
onSelect: (d: MentionInputSuggestionsOnSelectParams) => void;
|
||||
} & MentionInputSuggestionsDropdownItem & {
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
};
|
||||
|
||||
export const BusterInputItem = ({
|
||||
export const MentionInputSuggestionsItem = ({
|
||||
value,
|
||||
inputValue,
|
||||
label,
|
||||
|
@ -24,7 +24,7 @@ export const BusterInputItem = ({
|
|||
addValueToInput,
|
||||
onSelect,
|
||||
...props
|
||||
}: BusterInputItemProps) => {
|
||||
}: MentionInputSuggestionsItemProps) => {
|
||||
return (
|
||||
<Command.Item
|
||||
className={cn(
|
|
@ -0,0 +1,65 @@
|
|||
import type {
|
||||
MentionInputSuggestionsProps,
|
||||
MentionInputSuggestionsOnSelectParams,
|
||||
GroupOverrideProps,
|
||||
} from './MentionInputSuggestions.types';
|
||||
import { MentionInputSuggestionsGroup } from './MentionInputSuggestionsGroup';
|
||||
import { MentionInputSuggestionsItem } from './MentionInputSuggestionsItem';
|
||||
import { MentionInputSuggestionsSeparator } from './MentionInputSuggestionsSeparator';
|
||||
|
||||
export const MentionInputSuggestionsItemSelector = ({
|
||||
item,
|
||||
onSelect,
|
||||
addValueToInput,
|
||||
closeOnSelect,
|
||||
}: {
|
||||
item: MentionInputSuggestionsProps['suggestionItems'][number];
|
||||
onSelect: (params: MentionInputSuggestionsOnSelectParams) => void;
|
||||
} & GroupOverrideProps) => {
|
||||
if (item.type === 'separator') {
|
||||
return <MentionInputSuggestionsSeparator />;
|
||||
}
|
||||
|
||||
if (item.type === 'group') {
|
||||
return <MentionInputSuggestionsGroup {...item} onSelect={onSelect} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<MentionInputSuggestionsItem
|
||||
{...item}
|
||||
onSelect={onSelect}
|
||||
addValueToInput={item?.addValueToInput ?? addValueToInput}
|
||||
closeOnSelect={item?.closeOnSelect ?? closeOnSelect}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const MentionInputSuggestionsItemsSelector = ({
|
||||
suggestionItems,
|
||||
onSelect,
|
||||
addValueToInput,
|
||||
closeOnSelect,
|
||||
}: {
|
||||
suggestionItems: MentionInputSuggestionsProps['suggestionItems'];
|
||||
onSelect: (params: MentionInputSuggestionsOnSelectParams) => void;
|
||||
} & GroupOverrideProps) => {
|
||||
if (!suggestionItems) return null;
|
||||
return suggestionItems.map((item, index) => (
|
||||
<MentionInputSuggestionsItemSelector
|
||||
key={keySelector(item, index)}
|
||||
item={item}
|
||||
onSelect={onSelect}
|
||||
addValueToInput={addValueToInput}
|
||||
closeOnSelect={closeOnSelect}
|
||||
/>
|
||||
));
|
||||
};
|
||||
|
||||
const keySelector = (
|
||||
item: MentionInputSuggestionsProps['suggestionItems'][number],
|
||||
index: number
|
||||
) => {
|
||||
if (item.type === 'separator') return `separator${index}`;
|
||||
if (item.type === 'group') return `${item.label}${index}`;
|
||||
return item.value;
|
||||
};
|
|
@ -1,19 +1,19 @@
|
|||
import { Command } from 'cmdk';
|
||||
import type React from 'react';
|
||||
|
||||
interface BusterInputListProps {
|
||||
interface MentionInputSuggestionsListProps {
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
children: React.ReactNode;
|
||||
show?: boolean;
|
||||
}
|
||||
|
||||
export const BusterInputList = ({
|
||||
export const MentionInputSuggestionsList = ({
|
||||
className,
|
||||
style,
|
||||
children,
|
||||
show = true,
|
||||
}: BusterInputListProps) => {
|
||||
}: MentionInputSuggestionsListProps) => {
|
||||
if (!show) return null;
|
||||
return (
|
||||
<Command.List className={className} style={style}>
|
|
@ -0,0 +1,43 @@
|
|||
/** biome-ignore-all lint/complexity/noUselessFragments: Intersting bug when NOT using fragments */
|
||||
import { Command } from 'cmdk';
|
||||
import React, { forwardRef } from 'react';
|
||||
import { MentionInput, type MentionInputProps, type MentionInputRef } from '../MentionInput';
|
||||
import type { MentionInputSuggestionsProps } from './MentionInputSuggestions.types';
|
||||
|
||||
export type MentionInputSuggestionsMentionsInputProps = Pick<
|
||||
MentionInputSuggestionsProps,
|
||||
| 'mentions'
|
||||
| 'value'
|
||||
| 'placeholder'
|
||||
| 'defaultValue'
|
||||
| 'shouldFilter'
|
||||
| 'filter'
|
||||
| 'onMentionItemClick'
|
||||
> & {
|
||||
onChange: MentionInputProps['onChange'];
|
||||
onPressEnter: MentionInputProps['onPressEnter'];
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
autoFocus?: boolean;
|
||||
readOnly?: boolean;
|
||||
commandListNavigatedRef?: React.RefObject<boolean>;
|
||||
};
|
||||
|
||||
export const MentionInputSuggestionsMentionsInput = forwardRef<
|
||||
MentionInputRef,
|
||||
MentionInputSuggestionsMentionsInputProps
|
||||
>(({ value: valueProp, placeholder, defaultValue, mentions, value, ...props }, ref) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<MentionInput ref={ref} mentions={mentions} defaultValue={value} {...props} />
|
||||
<Command.Input
|
||||
value={value}
|
||||
autoFocus={false}
|
||||
className="sr-only hidden h-0 border-0 p-0 pointer-events-none w-full"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
});
|
||||
|
||||
MentionInputSuggestionsMentionsInput.displayName = 'MentionInputSuggestionsMentionsInput';
|
|
@ -2,7 +2,7 @@ import { Command } from 'cmdk';
|
|||
import type React from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export const BusterInputSeparator = ({
|
||||
export const MentionInputSuggestionsSeparator = ({
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentPropsWithoutRef<typeof Command.Separator>) => {
|
|
@ -1,20 +1,23 @@
|
|||
import type { MentionTriggerItem } from '../MentionInput';
|
||||
import type { BusterInputDropdownGroup, BusterInputDropdownItem } from './BusterInput.types';
|
||||
import type {
|
||||
MentionInputSuggestionsDropdownGroup,
|
||||
MentionInputSuggestionsDropdownItem,
|
||||
} from './MentionInputSuggestions.types';
|
||||
|
||||
export const createInputItem = <T = string>() => {
|
||||
return (item: BusterInputDropdownItem<T>) => item;
|
||||
return (item: MentionInputSuggestionsDropdownItem<T>) => item;
|
||||
};
|
||||
|
||||
export const createInputItems = <T = string>() => {
|
||||
return (items: BusterInputDropdownItem<T>[]) => items;
|
||||
return (items: MentionInputSuggestionsDropdownItem<T>[]) => items;
|
||||
};
|
||||
|
||||
export const createInputGroup = <T = string>() => {
|
||||
return (group: BusterInputDropdownGroup<T>) => group;
|
||||
return (group: MentionInputSuggestionsDropdownGroup<T>) => group;
|
||||
};
|
||||
|
||||
export const createInputGroups = <T = string>() => {
|
||||
return (groups: BusterInputDropdownGroup<T>[]) => groups;
|
||||
return (groups: MentionInputSuggestionsDropdownGroup<T>[]) => groups;
|
||||
};
|
||||
|
||||
export const createInputMention = <T = string>() => {
|
Loading…
Reference in New Issue