Added support for autocompleting at mentions for users with non-alphanumeric names (#4717)
Этот коммит содержится в:
коммит произвёл
enahum
родитель
01015dd30e
Коммит
b477e384d8
@@ -15,6 +15,7 @@ import {Constants, ActionTypes} from 'utils/constants.jsx';
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {FormattedMessage} from 'react-intl';
|
||||||
|
import XRegExp from 'xregexp';
|
||||||
|
|
||||||
class AtMentionSuggestion extends Suggestion {
|
class AtMentionSuggestion extends Suggestion {
|
||||||
render() {
|
render() {
|
||||||
@@ -124,7 +125,7 @@ export default class AtMentionProvider {
|
|||||||
handlePretextChanged(suggestionId, pretext) {
|
handlePretextChanged(suggestionId, pretext) {
|
||||||
const hadSuggestions = this.clearTimeout(this.timeoutId);
|
const hadSuggestions = this.clearTimeout(this.timeoutId);
|
||||||
|
|
||||||
const captured = (/(?:^|\W)@([a-z0-9\-._]*)$/i).exec(pretext.toLowerCase());
|
const captured = XRegExp.cache('(?:^|\\W)@([\\pL\\d\\-_.]*)$', 'i').exec(pretext.toLowerCase());
|
||||||
if (captured) {
|
if (captured) {
|
||||||
const prefix = captured[1];
|
const prefix = captured[1];
|
||||||
|
|
||||||
|
|||||||
@@ -22,11 +22,15 @@ export default class SuggestionBox extends React.Component {
|
|||||||
|
|
||||||
this.handleCompleteWord = this.handleCompleteWord.bind(this);
|
this.handleCompleteWord = this.handleCompleteWord.bind(this);
|
||||||
this.handleChange = this.handleChange.bind(this);
|
this.handleChange = this.handleChange.bind(this);
|
||||||
|
this.handleCompositionUpdate = this.handleCompositionUpdate.bind(this);
|
||||||
this.handleKeyDown = this.handleKeyDown.bind(this);
|
this.handleKeyDown = this.handleKeyDown.bind(this);
|
||||||
this.handlePretextChanged = this.handlePretextChanged.bind(this);
|
this.handlePretextChanged = this.handlePretextChanged.bind(this);
|
||||||
|
|
||||||
this.suggestionId = Utils.generateId();
|
this.suggestionId = Utils.generateId();
|
||||||
SuggestionStore.registerSuggestionBox(this.suggestionId);
|
SuggestionStore.registerSuggestionBox(this.suggestionId);
|
||||||
|
|
||||||
|
// Keep track of whether we're composing a CJK character so we can make suggetsions for partial characters
|
||||||
|
this.partialCharacter = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@@ -83,20 +87,28 @@ export default class SuggestionBox extends React.Component {
|
|||||||
handleChange(e) {
|
handleChange(e) {
|
||||||
const textbox = this.getTextbox();
|
const textbox = this.getTextbox();
|
||||||
const caret = Utils.getCaretPosition(textbox);
|
const caret = Utils.getCaretPosition(textbox);
|
||||||
const pretext = textbox.value.substring(0, caret);
|
const pretext = textbox.value.substring(0, caret) + this.partialCharacter;
|
||||||
|
|
||||||
|
if (SuggestionStore.getPretext(this.suggestionId) !== pretext) {
|
||||||
GlobalActions.emitSuggestionPretextChanged(this.suggestionId, pretext);
|
GlobalActions.emitSuggestionPretextChanged(this.suggestionId, pretext);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.props.onChange) {
|
if (this.props.onChange) {
|
||||||
this.props.onChange(e);
|
this.props.onChange(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleCompositionUpdate(e) {
|
||||||
|
// Save the currently composing character so that it can be used for autocomplete suggestions
|
||||||
|
// when handleChange is called immediately after this
|
||||||
|
this.partialCharacter = e.data;
|
||||||
|
}
|
||||||
|
|
||||||
handleCompleteWord(term, matchedPretext) {
|
handleCompleteWord(term, matchedPretext) {
|
||||||
const textbox = this.getTextbox();
|
const textbox = this.getTextbox();
|
||||||
const caret = Utils.getCaretPosition(textbox);
|
const caret = Utils.getCaretPosition(textbox);
|
||||||
const text = this.props.value;
|
const text = this.props.value;
|
||||||
const pretext = text.substring(0, caret);
|
const pretext = text.substring(0, caret) + this.partialCharacter;
|
||||||
|
|
||||||
let prefix;
|
let prefix;
|
||||||
if (pretext.endsWith(matchedPretext)) {
|
if (pretext.endsWith(matchedPretext)) {
|
||||||
@@ -168,37 +180,38 @@ export default class SuggestionBox extends React.Component {
|
|||||||
...props
|
...props
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
// Don't pass props used by SuggestionBox
|
||||||
|
Reflect.deleteProperty(props, 'providers');
|
||||||
|
|
||||||
|
const childProps = {
|
||||||
|
ref: 'textbox',
|
||||||
|
onInput: this.handleChange,
|
||||||
|
onCompositionUpdate: this.handleCompositionUpdate,
|
||||||
|
onKeyDown: this.handleKeyDown
|
||||||
|
};
|
||||||
|
|
||||||
let textbox = null;
|
let textbox = null;
|
||||||
if (type === 'input') {
|
if (type === 'input') {
|
||||||
textbox = (
|
textbox = (
|
||||||
<input
|
<input
|
||||||
ref='textbox'
|
|
||||||
type='text'
|
type='text'
|
||||||
{...props}
|
{...props}
|
||||||
onInput={this.handleChange}
|
{...childProps}
|
||||||
onKeyDown={this.handleKeyDown}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (type === 'search') {
|
} else if (type === 'search') {
|
||||||
const newProps = {...props};
|
|
||||||
Reflect.deleteProperty(newProps, 'providers');
|
|
||||||
textbox = (
|
textbox = (
|
||||||
<input
|
<input
|
||||||
ref='textbox'
|
|
||||||
type='search'
|
type='search'
|
||||||
{...newProps}
|
{...props}
|
||||||
onInput={this.handleChange}
|
{...childProps}
|
||||||
onKeyDown={this.handleKeyDown}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (type === 'textarea') {
|
} else if (type === 'textarea') {
|
||||||
textbox = (
|
textbox = (
|
||||||
<AutosizeTextarea
|
<AutosizeTextarea
|
||||||
id={this.suggestionId}
|
|
||||||
ref='textbox'
|
|
||||||
{...props}
|
{...props}
|
||||||
onInput={this.handleChange}
|
{...childProps}
|
||||||
onKeyDown={this.handleKeyDown}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user