uxcore-mention
Version:
Mention anywhere with this component
216 lines (205 loc) • 5.89 kB
JSX
import React from 'react';
import PropTypes from 'prop-types';
import { polyfill } from 'react-lifecycles-compat';
import BaseEditor from './baseEditor';
import { parseStrByDelimiter, getCaretOffset, getCaretPosition, createEvent } from '../utils/util';
/**
* @i18n {zh-CN} input中使用mention
* @i18n {en-US} mention in input
*/
class InputEditor extends BaseEditor {
static displayName = 'InputEditor';
static propTypes = {
/**
* @i18n {zh-CN} class前缀
* @i18n {en-US} class prefix
*/
prefixCls: PropTypes.string,
/**
* @i18n {zh-CN} 编辑区域宽度
* @i18n {en-US} editor's width
*/
width: PropTypes.oneOfType([
PropTypes.number,
PropTypes.string,
]),
/**
* @i18n {zh-CN} 编辑区域高度
* @i18n {en-US} editor's height
*/
height: PropTypes.number,
/**
* @i18n {zh-CN} placeholder
* @i18n {en-US} placeholder
*/
placeholder: PropTypes.string,
/**
* @i18n {zh-CN} 自定义插入的mention内容
* @i18n {en-US} customize the insert content with this function | function
*/
mentionFormatter: PropTypes.func,
/**
* @i18n {zh-CN} 发生变化后的触发
* @i18n {en-US} trigger when editor content change
*/
// onChange: React.PropTypes.func,
/**
* @i18n {zh-CN} 添加mention后触发
* @i18n {en-US} Callback invoked when a mention has been added
*/
onAdd: PropTypes.func,
/**
* @i18n {zh-CN} 默认内容
* @i18n {en-US} default value
*/
defaultValue: PropTypes.string,
/**
* @i18n {zh-CN} 内容
* @i18n {en-US} value
*/
value: PropTypes.string,
/**
* @i18n {zh-CN} 只读
* @i18n {en-US} read only
*/
readOnly: PropTypes.bool,
/**
* @i18n {zh-CN} 触发字符
* @i18n {en-US} Defines the char sequence upon which to trigger querying the data source
*/
delimiter: PropTypes.string,
/**
* @i18n {zh-CN} 最大长度
* @i18n {en-US} max length of content
*/
maxLength: PropTypes.number,
};
static defaultProps = {
prefixCls: '',
width: 200,
height: 30,
placeholder: '',
mentionFormatter: data => ` @${data.text} `,
// onChange: () => {},
onAdd: () => {},
defaultValue: '',
readOnly: false,
delimiter: '@',
value: '',
maxLength: -1,
};
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.value !== prevState.prveValue) {
return {
value: nextProps.value,
prveValue: nextProps.value,
};
}
return null;
}
constructor(props) {
super(props);
this.state = {
value: props.value || props.defaultValue,
prevValue: props.value || props.defaultValue,
};
this.handleChange = this.handleChange.bind(this);
}
componentDidMount() {
this.selectionPosition = {
start: 0,
end: 0,
};
}
handleDefaultKeyup() {
const editor = this.editor;
const { delimiter } = this.props;
const offset = getCaretOffset(editor);
let { value } = this.state;
value = value.replace(/(\r\n)|\n|\r/g, '\n');
const originStr = value.slice(0, offset.end);
const str = parseStrByDelimiter(originStr, delimiter);
this.props.matcher(str);
this.selectionPosition = {
start: offset.start - str.length - 1,
end: offset.end,
};
if (str !== false) {
const position = getCaretPosition(editor);
this.props.setCursorPos({
x: position.left,
y: position.top,
});
}
}
insert(mentionContent) {
this.insertContentAtCaret(mentionContent);
}
insertContentAtCaret(text) {
const editor = this.editor;
if (document.selection) {
editor.focus();
if (editor.createTextRange) {
const range = editor.createTextRange();
range.collapse(true);
range.moveStart('character', this.selectionPosition.start);
range.moveEnd('character', this.selectionPosition.end - this.selectionPosition.start);
range.text = text;
} else if (editor.setSelectionRange) {
editor.setSelectionRange(this.selectionPosition.start, this.selectionPosition.end);
}
} else {
const scrollTop = editor.scrollTop;
let { value } = this.state;
// editor.value = editor.value.substring(0, this.selectionPosition.start) +
// text +
// editor.value.substring(this.selectionPosition.end, editor.value.length);
value = value.substring(0, this.selectionPosition.start) +
text +
value.substring(this.selectionPosition.end, editor.value.length);
this.setState({
value,
}, () => {
editor.focus();
editor.scrollTop = scrollTop;
});
}
setTimeout(() => {
const changeEvt = createEvent(editor, 'change');
this.props.onChange(changeEvt, this.state.value);
}, 0);
}
handleChange(e) {
this.setState({
value: e.target.value,
});
this.props.onChange(e, e.target.value);
}
render() {
const { value } = this.state;
const { readOnly, placeholder, maxLength } = this.props;
const style = {
width: this.props.width,
height: this.props.height,
};
return (
<div className={this.props.prefixCls}>
<input
{...(Number(maxLength) > 0 ? { maxLength } : {})}
className={`${this.props.prefixCls}-editor kuma-input`}
ref={el => (this.editor = el)}
style={style}
readOnly={readOnly}
placeholder={placeholder}
onChange={this.handleChange}
onKeyDown={this.onKeydown.bind(this)}
onKeyUp={this.onKeyup.bind(this)}
onFocus={this.onFocus.bind(this)}
value={value}
/>
</div>
);
}
}
polyfill(InputEditor);
export default InputEditor;