zent
Version:
一套前端设计语言和基于React的实现
216 lines (215 loc) • 10.2 kB
JavaScript
import { __assign, __extends, __rest } from "tslib";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { Component } from 'react';
import { findDOMNode } from 'react-dom';
import cx from 'classnames';
import isEqual from '../utils/isEqual';
import Input from '../input';
import Popover from '../popover';
import getCaretCoordinates from '../utils/dom/getCaretCoordinates';
import isFirefox from '../utils/isFirefox';
import SelectMenu from '../select-menu';
import { I18nReceiver as Receiver } from '../i18n';
import { findMentionAtCaretPosition } from './findMentionAtCaretPosition';
import * as SelectionChangeEventHub from './SelectionChangeEventHub';
import * as Utils from './utils';
import { getPopoverBottomPosition, getPopoverTopPosition } from './position';
import { MENTION_FOUND } from './constants';
import defer from '../utils/defer';
import { runOnceInNextFrame } from '../utils/nextFrame';
var NAV_KEYS = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];
var DEFAULT_STATE = {
suggestionVisible: false,
search: null,
};
var Mention = (function (_super) {
__extends(Mention, _super);
function Mention() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.input = null;
_this.suggestionList = null;
_this.state = __assign(__assign({}, DEFAULT_STATE), { position: undefined, placeholder: null });
_this.BottomPosition = getPopoverBottomPosition(_this);
_this.TopPosition = getPopoverTopPosition(_this);
_this.onSuggestionVisibleChange = function (visible) {
if (!visible) {
_this.setStateIfChange(_this.getDefaultState());
}
};
_this.onCloseMenuList = function () {
_this.onSuggestionVisibleChange(false);
};
_this.onSelectSuggestion = function (val) {
_this.onCloseMenuList();
var _a = _this.props, value = _a.value, onChange = _a.onChange;
var placeholder = _this.state.placeholder;
var newValue = Utils.replaceSubstring(value, placeholder.start, placeholder.end, val);
onChange(newValue.value);
defer(function () {
if (_this.input) {
_this.input.setSelectionRange(newValue.caret, newValue.caret);
_this.input.blur();
_this.input.focus();
}
});
};
_this.onInputChange = function (evt) {
_this.props.onChange(evt.target.value);
};
_this.onInputScroll = runOnceInNextFrame(function () {
if (_this.state.suggestionVisible) {
_this.setSuggestionVisible();
}
});
_this.onInputCompositionStart = function () {
_this._compositing = true;
};
_this.onInputCompositionEnd = function () {
_this._compositing = false;
};
_this.onInputBlur = function (evt) {
if (!_this.state.suggestionVisible) {
_this.setStateIfChange(_this.getDefaultState());
}
_this.triggerEventCallback('onBlur', evt);
};
_this.onInputKeyUp = function (evt) {
if (isFirefox &&
(evt.altKey ||
evt.ctrlKey ||
evt.metaKey ||
NAV_KEYS.indexOf(evt.key) !== -1)) {
defer(_this.setSuggestionVisible, _this.props.value);
}
_this.triggerEventCallback('onKeyUp', evt);
};
_this.onInputKeyDown = function (evt) {
if (_this.state.suggestionVisible && _this.suggestionList) {
var key = evt.key;
if (key === 'ArrowUp') {
_this.suggestionList.moveFocusIndexUp();
evt.preventDefault();
}
else if (key === 'ArrowDown') {
_this.suggestionList.moveFocusIndexDown();
evt.preventDefault();
}
else if (key === 'Enter') {
_this.suggestionList.selectCurrentFocusIndex(evt);
evt.preventDefault();
}
else if (key === 'Escape') {
_this.setStateIfChange(_this.getDefaultState());
}
}
_this.triggerEventCallback('onKeyDown', evt);
};
_this.onSelectionChange = function () {
_this.setSuggestionVisible(_this.props.value);
};
_this.saveInputRef = function (instance) {
if (_this.input) {
SelectionChangeEventHub.uninstall({
node: _this.input,
callback: _this.onSelectionChange,
});
}
if (!instance) {
return;
}
var inputNode = Utils.getInputNodeForTrigger(findDOMNode(instance));
_this.input = inputNode;
if (_this.input) {
SelectionChangeEventHub.install({
node: _this.input,
callback: _this.onSelectionChange,
});
}
};
_this.onSuggestionListRefChange = function (instance) {
_this.suggestionList = instance;
};
_this.setSuggestionVisible = function (value) {
if (!_this.input || _this._compositing) {
return;
}
value = value !== undefined ? value : _this.props.value;
var triggerText = _this.props.triggerText;
var mention = findMentionAtCaretPosition({
input: _this.input,
value: value,
triggerText: triggerText,
});
var newState = _this.getDefaultState();
if (mention.code === MENTION_FOUND) {
newState.suggestionVisible = true;
newState.search = Utils.substring(value, mention.searchStart, mention.searchEnd + 1);
newState.position = _this.getCaretCoordinates(mention.caretMeasureStart);
newState.placeholder = {
start: mention.placeholderStart,
end: mention.placeholderEnd,
};
}
_this.setStateIfChange(newState);
};
return _this;
}
Mention.prototype.render = function () {
var _this = this;
var _a = this.props, multiLine = _a.multiLine, position = _a.position, suggestions = _a.suggestions, onSearchChange = _a.onSearchChange, suggestionNotFoundContent = _a.suggestionNotFoundContent, loading = _a.loading, triggerText = _a.triggerText, onChange = _a.onChange, onBlur = _a.onBlur, onKeyUp = _a.onKeyUp, onKeyDown = _a.onKeyDown, type = _a.type, className = _a.className, inline = _a.inline, passThroughProps = __rest(_a, ["multiLine", "position", "suggestions", "onSearchChange", "suggestionNotFoundContent", "loading", "triggerText", "onChange", "onBlur", "onKeyUp", "onKeyDown", "type", "className", "inline"]);
var inputType = multiLine ? 'textarea' : 'text';
var suggestionVisible = this.state.suggestionVisible;
return (_jsx(Receiver, __assign({ componentName: "Mention" }, { children: function (i18n) {
return (_jsxs(Popover, __assign({ visible: suggestionVisible, onVisibleChange: _this.onSuggestionVisibleChange, position: position === 'bottom' ? _this.BottomPosition : _this.TopPosition }, { children: [_jsx(Popover.Trigger.Click, __assign({ getElement: Utils.getInputNodeForTrigger }, { children: _jsx(Input, __assign({ type: inputType, ref: _this.saveInputRef, className: cx('zent-mention', className), onChange: _this.onInputChange, onBlur: _this.onInputBlur, onKeyUp: _this.onInputKeyUp, onKeyDown: _this.onInputKeyDown, onScroll: _this.onInputScroll, onWheel: _this.onInputScroll, onCompositionStart: _this.onInputCompositionStart, onCompositionEnd: _this.onInputCompositionEnd, inline: inline }, passThroughProps), void 0) }), void 0), _jsx(Popover.Content, { children: _jsx(SelectMenu, { ref: _this.onSuggestionListRefChange, items: Utils.getMenuListItems(suggestions, i18n.noContent), onRequestClose: _this.onCloseMenuList, onSelect: _this.onSelectSuggestion }, void 0) }, void 0)] }), void 0));
} }), void 0));
};
Mention.prototype.componentDidUpdate = function (prevProps) {
if (prevProps.value !== this.props.value) {
this.setSuggestionVisible(this.props.value);
}
};
Mention.prototype.componentDidMount = function () {
this.setSuggestionVisible(this.props.value);
};
Mention.prototype.triggerEventCallback = function (eventName, evt) {
var fn = this.props[eventName];
if (typeof fn === 'function') {
fn(evt);
}
};
Mention.prototype.setStateIfChange = function (state) {
var isSearchChanged = state.search !== this.state.search;
if (!isEqual(this.state, state)) {
var onSearchChange = this.props.onSearchChange;
if (isSearchChanged && typeof onSearchChange === 'function') {
onSearchChange(state.search);
}
this.setState(state);
}
};
Mention.prototype.getCaretCoordinates = function (start) {
var position = getCaretCoordinates(this.input, start, { debug: false });
var _a = this.input, scrollLeft = _a.scrollLeft, scrollTop = _a.scrollTop;
if (scrollLeft) {
position.left = position.left - scrollLeft;
}
if (scrollTop) {
position.top = position.top - scrollTop;
}
return position;
};
Mention.prototype.getDefaultState = function () {
return __assign(__assign({}, this.state), DEFAULT_STATE);
};
Mention.defaultProps = {
multiLine: false,
position: 'bottom',
suggestionNotFoundContent: '',
suggestions: [],
triggerText: '@',
inline: true,
};
return Mention;
}(Component));
export { Mention };
export default Mention;