choerodon-ui
Version:
An enterprise-class UI design language and React-based implementation
453 lines (369 loc) • 15 kB
JavaScript
import _objectSpread from "@babel/runtime/helpers/objectSpread2";
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
import _createClass from "@babel/runtime/helpers/createClass";
import _assertThisInitialized from "@babel/runtime/helpers/assertThisInitialized";
import _inherits from "@babel/runtime/helpers/inherits";
import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
function _createSuper(Derived) {
function isNativeReflectConstruct() {
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
if (Reflect.construct.sham) return false;
if (typeof Proxy === "function") return true;
try {
Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
return true;
} catch (e) {
return false;
}
}
return function () {
var Super = _getPrototypeOf(Derived),
result;
if (isNativeReflectConstruct()) {
var NewTarget = _getPrototypeOf(this).constructor;
result = Reflect.construct(Super, arguments, NewTarget);
} else {
result = Super.apply(this, arguments);
}
return _possibleConstructorReturn(this, result);
};
}
import React, { Children, cloneElement, Component, isValidElement } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import { decode } from 'draft-js/lib/DraftOffsetKey';
import cx from 'classnames';
import scrollIntoView from 'dom-scroll-into-view';
import Animate from '../../../animate';
import Nav from './Nav.react';
import SuggetionWrapper from './SuggestionWrapper.react';
import insertMention from '../utils/insertMention';
import clearMention from '../utils/clearMention';
import getOffset from '../utils/getOffset';
import getMentions from '../utils/getMentions';
import getSearchWord from '../utils/getSearchWord';
var isNotFalse = function isNotFalse(i) {
return i !== false;
};
var Suggestions =
/*#__PURE__*/
function (_Component) {
_inherits(Suggestions, _Component);
var _super = _createSuper(Suggestions);
function Suggestions(props) {
var _this;
_classCallCheck(this, Suggestions);
_this = _super.call(this, props);
_defineProperty(_assertThisInitialized(_this), "onEditorStateChange", function (editorState) {
var offset = _this.props.store.getOffset();
if (offset.size === 0) {
_this.closeDropDown();
return editorState;
}
var selection = editorState.getSelection(); // 修复: 焦点移出再移入时, dropdown 会闪动一下
// 原因: https://github.com/facebook/draft-js/blob/67c5e69499e3b0c149ce83b004872afdf4180463/src/component/handlers/edit/editOnFocus.js#L33
// 此处强制 update 了一下,因此 onEditorStateChange 会 call 两次
if (!_this.props.callbacks.getEditorState().getSelection().getHasFocus() && selection.getHasFocus()) {
return editorState;
}
var _getSearchWord = getSearchWord(editorState, selection),
word = _getSearchWord.word;
if (!word) {
_this.closeDropDown();
return editorState;
}
var selectionInsideMention = offset.map(function (_ref) {
var offsetKey = _ref.offsetKey;
var _decode = decode(offsetKey),
blockKey = _decode.blockKey,
decoratorKey = _decode.decoratorKey,
leafKey = _decode.leafKey;
if (blockKey !== selection.anchorKey) {
return false;
}
var leaf = editorState.getBlockTree(blockKey).getIn([decoratorKey, 'leaves', leafKey]);
if (!leaf) {
return false;
}
var startKey = leaf.get('start');
var endKey = leaf.get('end'); // 处理只有一个 `@` 符号时的情况
if (!word) {
return false;
}
if (startKey === endKey - 1) {
return selection.anchorOffset >= startKey + 1 && selection.anchorOffset <= endKey ? offsetKey : false;
}
return selection.anchorOffset > startKey + 1 && selection.anchorOffset <= endKey ? offsetKey : false;
});
var selectionInText = selectionInsideMention.some(isNotFalse);
_this.activeOffsetKey = selectionInsideMention.find(isNotFalse);
var trigger = _this.props.store.getTrigger(_this.activeOffsetKey);
if (!selectionInText || !selection.getHasFocus()) {
_this.closeDropDown();
return editorState;
}
var searchValue = word.substring(trigger.length, word.length);
if (_this.lastSearchValue !== searchValue || _this.lastTrigger !== trigger) {
_this.lastSearchValue = searchValue;
_this.lastTrigger = trigger;
_this.props.onSearchChange(searchValue, trigger);
}
if (!_this.state.active) {
// 暂时没有更优雅的方法
if (!trigger || word.indexOf(trigger) !== -1) {
_this.openDropDown();
}
}
return editorState;
});
_defineProperty(_assertThisInitialized(_this), "onUpArrow", function (ev) {
ev.preventDefault();
if (_this.props.suggestions.length > 0) {
var newIndex = _this.state.focusedIndex - 1;
_this.setState({
focusedIndex: Math.max(newIndex, 0)
});
}
});
_defineProperty(_assertThisInitialized(_this), "onBlur", function (ev) {
ev.preventDefault();
_this.closeDropDown();
});
_defineProperty(_assertThisInitialized(_this), "onDownArrow", function (ev) {
ev.preventDefault();
var newIndex = _this.state.focusedIndex + 1;
_this.setState({
focusedIndex: newIndex >= _this.props.suggestions.length ? 0 : newIndex
});
});
_defineProperty(_assertThisInitialized(_this), "getContainer", function () {
var popupContainer = document.createElement('div');
var mountNode;
if (_this.props.getSuggestionContainer) {
mountNode = _this.props.getSuggestionContainer();
popupContainer.style.position = 'relative';
} else {
mountNode = document.body;
}
mountNode.appendChild(popupContainer);
return popupContainer;
});
_defineProperty(_assertThisInitialized(_this), "handleKeyBinding", function (command) {
return command === 'split-block';
});
_defineProperty(_assertThisInitialized(_this), "handleReturn", function (ev) {
ev.preventDefault();
var selectedSuggestion = _this.props.suggestions[_this.state.focusedIndex];
if (selectedSuggestion) {
if (isValidElement(selectedSuggestion)) {
_this.onMentionSelect(selectedSuggestion.props.value, selectedSuggestion.props.data);
} else {
_this.onMentionSelect(selectedSuggestion);
}
_this.lastSearchValue = null;
_this.lastTrigger = null;
return true;
}
return false;
});
_defineProperty(_assertThisInitialized(_this), "renderReady", function () {
var container = _this.dropdownContainer;
if (!container) {
return;
}
var active = _this.state.active;
var _assertThisInitialize = _assertThisInitialized(_this),
activeOffsetKey = _assertThisInitialize.activeOffsetKey;
var offset = _this.props.store.getOffset();
var dropDownPosition = offset.get(activeOffsetKey);
if (active && dropDownPosition) {
var placement = _this.props.placement;
var dropDownStyle = _this.getPositionStyle(true, dropDownPosition.position()); // Check if the above space is crowded
var isTopCrowded = parseFloat(dropDownStyle.top) - window.scrollY - container.offsetHeight < 0; // Check if the under space is crowded
var isBottomCrowded = (window.innerHeight || document.documentElement.clientHeight) - (parseFloat(dropDownStyle.top) - window.scrollY) - container.offsetHeight < 0;
if (placement === 'top' && !isTopCrowded) {
// The above space isn't crowded
dropDownStyle.top = "".concat(parseFloat(dropDownStyle.top) - container.offsetHeight || 0, "px");
}
if (placement === 'bottom' && isBottomCrowded && !isTopCrowded) {
// The above space isn't crowded and the under space is crowded.
dropDownStyle.top = "".concat(parseFloat(dropDownStyle.top) - container.offsetHeight || 0, "px");
}
Object.keys(dropDownStyle).forEach(function (key) {
container.style[key] = dropDownStyle[key];
});
}
if (!_this.focusItem) {
return;
}
scrollIntoView(ReactDOM.findDOMNode(_this.focusItem), container, {
onlyScrollIfNeeded: true
});
});
_defineProperty(_assertThisInitialized(_this), "getNavigations", function () {
var _this$props = _this.props,
prefixCls = _this$props.prefixCls,
suggestions = _this$props.suggestions;
var focusedIndex = _this.state.focusedIndex;
return suggestions.length ? Children.map(suggestions, function (element, index) {
var focusItem = index === focusedIndex;
var ref = focusItem ? function (node) {
_this.focusItem = node;
} : null;
var mentionClass = cx("".concat(prefixCls, "-dropdown-item"), {
focus: focusItem
});
if (isValidElement(element)) {
return cloneElement(element, {
className: mentionClass,
onMouseDown: function onMouseDown() {
return _this.onMentionSelect(element.props.value, element.props.data);
},
ref: ref
});
}
return React.createElement(Nav, {
ref: ref,
className: mentionClass,
onMouseDown: function onMouseDown() {
return _this.onMentionSelect(element);
}
}, element);
}, _assertThisInitialized(_this)) : React.createElement("div", {
className: "".concat(prefixCls, "-dropdown-notfound ").concat(prefixCls, "-dropdown-item")
}, _this.props.notFoundContent);
});
_this.state = {
isActive: false,
focusedIndex: 0,
container: false
};
return _this;
}
_createClass(Suggestions, [{
key: "componentDidMount",
value: function componentDidMount() {
this.props.callbacks.onChange = this.onEditorStateChange;
}
}, {
key: "componentWillReceiveProps",
value: function componentWillReceiveProps(nextProps) {
if (nextProps.suggestions.length !== this.props.suggestions.length) {
this.setState({
focusedIndex: 0
});
}
}
}, {
key: "onMentionSelect",
value: function onMentionSelect(mention, data) {
var editorState = this.props.callbacks.getEditorState();
var _this$props2 = this.props,
store = _this$props2.store,
onSelect = _this$props2.onSelect;
var trigger = store.getTrigger(this.activeOffsetKey);
if (onSelect) {
onSelect(mention, data || mention);
}
if (this.props.noRedup) {
var mentions = getMentions(editorState.getCurrentContent(), trigger);
if (mentions.indexOf("".concat(trigger).concat(mention)) !== -1) {
// eslint-disable-next-line
console.warn('you have specified `noRedup` props but have duplicated mentions.');
this.closeDropDown();
this.props.callbacks.setEditorState(clearMention(editorState));
return;
}
}
this.props.callbacks.setEditorState(insertMention(editorState, "".concat(trigger).concat(mention), data, this.props.mode), true);
this.closeDropDown();
}
}, {
key: "getPositionStyle",
value: function getPositionStyle(isActive, position) {
if (this.props.getSuggestionStyle) {
return this.props.getSuggestionStyle(isActive, position);
}
var container = this.props.getSuggestionContainer ? this.state.container : document.body;
var offset = getOffset(container);
return position ? _objectSpread({
position: 'absolute',
left: "".concat(position.left - offset.left, "px"),
top: "".concat(position.top - offset.top, "px")
}, this.props.style) : {};
}
}, {
key: "openDropDown",
value: function openDropDown() {
this.props.callbacks.onUpArrow = this.onUpArrow;
this.props.callbacks.handleReturn = this.handleReturn;
this.props.callbacks.handleKeyBinding = this.handleKeyBinding;
this.props.callbacks.onDownArrow = this.onDownArrow;
this.props.callbacks.onBlur = this.onBlur;
this.setState({
active: true,
container: this.state.container || this.getContainer()
});
}
}, {
key: "closeDropDown",
value: function closeDropDown() {
this.props.callbacks.onUpArrow = null;
this.props.callbacks.handleReturn = null;
this.props.callbacks.handleKeyBinding = null;
this.props.callbacks.onDownArrow = null;
this.props.callbacks.onBlur = null;
this.setState({
active: false
});
}
}, {
key: "render",
value: function render() {
var _objectSpread2,
_this2 = this;
var _this$props3 = this.props,
prefixCls = _this$props3.prefixCls,
className = _this$props3.className,
placement = _this$props3.placement;
var _this$state = this.state,
container = _this$state.container,
active = _this$state.active;
var cls = cx(_objectSpread((_objectSpread2 = {}, _defineProperty(_objectSpread2, "".concat(prefixCls, "-dropdown"), true), _defineProperty(_objectSpread2, "".concat(prefixCls, "-dropdown-placement-").concat(placement), true), _objectSpread2), className));
var transitionName = placement === 'top' ? 'slide-down' : 'slide-up';
var navigations = this.getNavigations();
return container ? React.createElement(SuggetionWrapper, {
renderReady: this.renderReady,
container: container
}, React.createElement(Animate, {
transitionName: transitionName
}, active ? React.createElement("div", {
className: cls,
ref: function ref(node) {
_this2.dropdownContainer = node;
}
}, navigations) : null)) : null;
}
}]);
return Suggestions;
}(Component);
_defineProperty(Suggestions, "propTypes", {
callbacks: PropTypes.object,
suggestions: PropTypes.array,
store: PropTypes.object,
onSearchChange: PropTypes.func,
prefixCls: PropTypes.string,
mode: PropTypes.string,
style: PropTypes.object,
onSelect: PropTypes.func,
getSuggestionContainer: PropTypes.func,
notFoundContent: PropTypes.any,
getSuggestionStyle: PropTypes.func,
className: PropTypes.string,
noRedup: PropTypes.bool,
placement: PropTypes.string
});
export { Suggestions as default };
//# sourceMappingURL=Suggestions.react.js.map