diff --git a/README.md b/README.md index 08a3db0..08b13ea 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ React.render(, container); | notFoundContent | Set mentions content when not match | ReactNode | 'Not Found' | | placement | Set popup placement | 'top' \| 'bottom' | 'bottom' | | prefix | Set trigger prefix keyword | string \| string[] | '@' | +| suffix | Set suffix string when option is selected | string | '' | | rows | Set row count | number | 1 | | split | Set split string before and after selected mention | string | ' ' | | validateSearch | Customize trigger search logic | (text: string, props: MentionsProps) => void | - | diff --git a/examples/suffix.js b/examples/suffix.js new file mode 100644 index 0000000..da778fc --- /dev/null +++ b/examples/suffix.js @@ -0,0 +1,44 @@ +/* eslint no-console: 0 */ + +import React from 'react'; +import Mentions from '../src'; +import '../assets/index.less'; + +const { Option } = Mentions; + +class Demo extends React.Component { + onSelect = (option, prefix) => { + console.log('Select:', prefix, '-', option.value); + }; + + onFocus = () => { + console.log('onFocus'); + }; + + onBlur = () => { + console.log('onBlur'); + }; + + render() { + return ( +
+ + + + + +
+ ); + } +} + +export default Demo; diff --git a/src/Mentions.tsx b/src/Mentions.tsx index 5a9f451..d8e6288 100644 --- a/src/Mentions.tsx +++ b/src/Mentions.tsx @@ -34,6 +34,7 @@ export interface MentionsProps extends BaseTextareaAttrs { transitionName?: string; placement?: Placement; prefix?: string | string[]; + suffix?: string; prefixCls?: string; value?: string; filterOption?: false | typeof defaultFilterOption; @@ -240,13 +241,14 @@ class Mentions extends React.Component { public selectOption = (option: OptionProps) => { const { value, measureLocation, measurePrefix } = this.state; - const { split, onSelect } = this.props; + const { split, suffix = '', onSelect } = this.props; const { value: mentionValue = '' } = option; const { text, selectionLocation } = replaceWithMeasure(value, { measureLocation, targetText: mentionValue, prefix: measurePrefix, + suffix, selectionStart: this.textarea!.selectionStart, split: split!, }); diff --git a/src/util.ts b/src/util.ts index fb39b50..1c92838 100644 --- a/src/util.ts +++ b/src/util.ts @@ -54,6 +54,7 @@ export function getLastMeasureIndex(text: string, prefix: string | string[] = '' interface MeasureConfig { measureLocation: number; prefix: string; + suffix: string; targetText: string; selectionStart: number; split: string; @@ -91,7 +92,7 @@ function reduceText(text: string, targetText: string, split: string) { * => little @light test */ export function replaceWithMeasure(text: string, measureConfig: MeasureConfig) { - const { measureLocation, prefix, targetText, selectionStart, split } = measureConfig; + const { measureLocation, prefix, suffix, targetText, selectionStart, split } = measureConfig; // Before text will append one space if have other text let beforeMeasureText = text.slice(0, measureLocation); @@ -112,8 +113,7 @@ export function replaceWithMeasure(text: string, measureConfig: MeasureConfig) { restText = restText.slice(split.length); } - const connectedStartText = `${beforeMeasureText}${prefix}${targetText}${split}`; - + const connectedStartText = `${beforeMeasureText}${prefix}${targetText}${suffix}${split}`; return { text: `${connectedStartText}${restText}`, selectionLocation: connectedStartText.length, diff --git a/tests/FullProcess.spec.jsx b/tests/FullProcess.spec.jsx index 292fc8f..a9f331b 100644 --- a/tests/FullProcess.spec.jsx +++ b/tests/FullProcess.spec.jsx @@ -31,6 +31,8 @@ describe('Full Process', () => { ]); simulateInput(wrapper, '@a'); + wrapper.find('textarea').instance().selectionStart = 2; + wrapper.find('textarea').simulate('keyUp', {}); expect(wrapper.find('DropdownMenu').props().options).toMatchObject([ { value: 'bamboo' }, { value: 'cat' }, @@ -110,4 +112,27 @@ describe('Full Process', () => { expect(wrapper.state().measuring).toBe(false); }); + + it('add suffix after target if suffix exists', () => { + const onChange = jest.fn(); + const onKeyDown = jest.fn(); + const wrapper = createMentions({ + prefix: '{', + suffix: '}', + split: ' ', + onChange, + onKeyDown, + }); + + simulateInput(wrapper, '{'); + wrapper.find('textarea').instance().selectionStart = 1; + expect(wrapper.state().measuring).toBe(true); + + wrapper.find('textarea').simulate('keyDown', { + which: KeyCode.ENTER, + }); + + expect(wrapper.state().measuring).toBe(false); + expect(onChange).toBeCalledWith('{bamboo} '); + }); }); diff --git a/tests/shared/input.js b/tests/shared/input.js index 8676c11..745f412 100644 --- a/tests/shared/input.js +++ b/tests/shared/input.js @@ -5,6 +5,10 @@ export function simulateInput(wrapper, text = '', keyEvent) { const myKeyEvent = keyEvent || { which: lastChar.charCodeAt(0), key: lastChar, + target: { + value: text, + selectionStart: text.length, + }, }; wrapper.find('textarea').simulate('keyDown', myKeyEvent);