import * as React from 'react'; import classNames from 'classnames'; import RcMentions from 'rc-mentions'; import { MentionsProps as RcMentionsProps } from 'rc-mentions/lib/Mentions'; import { composeRef } from 'rc-util/lib/ref'; import Spin from '../spin'; import { ConfigContext } from '../config-provider'; export const { Option } = RcMentions; function loadingFilterOption() { return true; } export type MentionPlacement = 'top' | 'bottom'; export interface OptionProps { value: string; children: React.ReactNode; [key: string]: any; } export interface MentionProps extends RcMentionsProps { loading?: boolean; } export interface MentionState { focused: boolean; } interface MentionsConfig { prefix?: string | string[]; split?: string; } interface MentionsEntity { prefix: string; value: string; } interface CompoundedComponent extends React.ForwardRefExoticComponent> { Option: typeof Option; getMentions: (value: string, config?: MentionsConfig) => MentionsEntity[]; } const InternalMentions: React.ForwardRefRenderFunction = ( { prefixCls: customizePrefixCls, className, disabled, loading, filterOption, children, notFoundContent, ...restProps }, ref, ) => { const [focused, setFocused] = React.useState(false); const innerRef = React.useRef(); const mergedRef = composeRef(ref, innerRef); const { getPrefixCls, renderEmpty, direction } = React.useContext(ConfigContext); const onFocus: React.FocusEventHandler = (...args) => { if (restProps.onFocus) { restProps.onFocus(...args); } setFocused(true); }; const onBlur: React.FocusEventHandler = (...args) => { if (restProps.onBlur) { restProps.onBlur(...args); } setFocused(false); }; const getNotFoundContent = () => { if (notFoundContent !== undefined) { return notFoundContent; } return renderEmpty('Select'); }; const getOptions = () => { if (loading) { return ( ); } return children; }; const getFilterOption = (): any => { if (loading) { return loadingFilterOption; } return filterOption; }; const prefixCls = getPrefixCls('mentions', customizePrefixCls); const mergedClassName = classNames( { [`${prefixCls}-disabled`]: disabled, [`${prefixCls}-focused`]: focused, [`${prefixCls}-rtl`]: direction === 'rtl', }, className, ); return ( {getOptions()} ); }; const Mentions = React.forwardRef(InternalMentions) as CompoundedComponent; Mentions.displayName = 'Mentions'; Mentions.Option = Option; Mentions.getMentions = (value: string = '', config?: MentionsConfig): MentionsEntity[] => { const { prefix = '@', split = ' ' } = config || {}; const prefixList: string[] = Array.isArray(prefix) ? prefix : [prefix]; return value .split(split) .map((str = ''): MentionsEntity | null => { let hitPrefix: string | null = null; prefixList.some(prefixStr => { const startStr = str.slice(0, prefixStr.length); if (startStr === prefixStr) { hitPrefix = prefixStr; return true; } return false; }); if (hitPrefix !== null) { return { prefix: hitPrefix, value: str.slice((hitPrefix as string).length), }; } return null; }) .filter((entity): entity is MentionsEntity => !!entity && !!entity.value); }; export default Mentions;