@atlaskit/mention
Version:
A React component used to display user profiles in a list for 'Mention' functionality
232 lines (229 loc) • 8.43 kB
JavaScript
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
import _createClass from "@babel/runtime/helpers/createClass";
import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
import _inherits from "@babel/runtime/helpers/inherits";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
import React from 'react';
import debug from '../../util/logger';
import { actualMouseMove, mouseLocation } from '../../util/mouse';
import MentionItem from '../MentionItem';
import MentionListError from '../MentionListError';
import MessagesIntlProvider from '../MessagesIntlProvider';
import Scrollable from '../Scrollable';
import { MentionListStyle } from './styles';
function wrapIndex(mentions, index) {
var len = mentions.length;
var newIndex = index;
while (newIndex < 0 && len > 0) {
newIndex += len;
}
return newIndex % len;
}
function getKey(index, mentions) {
return mentions && mentions[index] && mentions[index].id;
}
function getIndex(key, mentions) {
var index;
if (mentions) {
index = 0;
while (index < mentions.length && mentions[index].id !== key) {
index++;
}
if (index === mentions.length) {
index = undefined;
}
}
return index;
}
var MentionList = /*#__PURE__*/function (_React$PureComponent) {
function MentionList(props) {
var _this;
_classCallCheck(this, MentionList);
_this = _callSuper(this, MentionList, [props]);
// API
_defineProperty(_this, "selectNext", function () {
var newIndex = wrapIndex(_this.props.mentions, _this.state.selectedIndex + 1);
_this.selectIndex(newIndex);
});
_defineProperty(_this, "selectPrevious", function () {
var newIndex = wrapIndex(_this.props.mentions, _this.state.selectedIndex - 1);
_this.selectIndex(newIndex);
});
_defineProperty(_this, "selectIndex", function (index, callback) {
var mentions = _this.props.mentions;
_this.setState({
selectedIndex: index,
selectedKey: getKey(index, mentions)
}, callback);
});
_defineProperty(_this, "selectId", function (id, callback) {
var mentions = _this.props.mentions;
var index = getIndex(id, mentions);
if (index !== undefined) {
_this.setState({
selectedIndex: index,
selectedKey: id
}, callback);
}
});
_defineProperty(_this, "chooseCurrentSelection", function () {
var _this$props = _this.props,
mentions = _this$props.mentions,
onSelection = _this$props.onSelection;
var selectedIndex = _this.state.selectedIndex;
var selectedMention = mentions && mentions[selectedIndex || 0];
debug('ak-mention-list.chooseCurrentSelection', selectedMention);
if (onSelection && selectedMention) {
onSelection(selectedMention);
}
});
_defineProperty(_this, "mentionsCount", function () {
var mentions = _this.props.mentions;
return mentions && mentions.length || 0;
});
_defineProperty(_this, "selectIndexOnHover", function (mention, event) {
if (!event) {
return;
}
var mousePosition = mouseLocation(event);
if (actualMouseMove(_this.lastMousePosition, mousePosition)) {
_this.selectId(mention.id);
}
_this.lastMousePosition = mousePosition;
});
_defineProperty(_this, "itemSelected", function (mention) {
_this.selectId(mention.id, function () {
_this.chooseCurrentSelection();
});
});
_defineProperty(_this, "handleScrollableRef", function (ref) {
_this.scrollable = ref;
});
_this.setDefaultSelectionState();
return _this;
}
_inherits(MentionList, _React$PureComponent);
return _createClass(MentionList, [{
key: "UNSAFE_componentWillReceiveProps",
value: function UNSAFE_componentWillReceiveProps(nextProps) {
// adjust selection
var mentions = nextProps.mentions;
var selectedKey = this.state.selectedKey;
if (mentions) {
if (!selectedKey) {
// don't explicitly set any selected index and go with default behaviour
return;
}
for (var i = 0; i < mentions.length; i++) {
if (selectedKey === mentions[i].id) {
this.setState({
selectedIndex: i
});
return;
}
}
// existing selection not in results so clear any current selection state and go with default behaviour
this.setDefaultSelectionState();
}
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate() {
var mentions = this.props.mentions;
var selectedIndex = this.state.selectedIndex;
if (mentions && mentions[selectedIndex]) {
this.revealItem(mentions[selectedIndex].id);
}
// FIXME - a React version of this _may_ be required for Confluence
// integration tests. Will remove / fix once known
// emit(elem, mentionListRenderedEvent);
}
}, {
key: "revealItem",
value:
// Internal
function revealItem(key) {
var item = this.items[key];
if (item && this.scrollable) {
this.scrollable.reveal(item);
}
}
/**
* The default selection state is to chose index 0 and not have any particular key selected
*/
}, {
key: "setDefaultSelectionState",
value: function setDefaultSelectionState() {
this.state = {
selectedIndex: 0,
selectedKey: undefined
};
}
}, {
key: "renderItems",
value: function renderItems() {
var _this2 = this;
var mentions = this.props.mentions;
if (mentions && mentions.length) {
this.items = {};
return /*#__PURE__*/React.createElement("div", null, this.props.initialHighlightElement, mentions.map(function (mention, idx) {
var key = mention.id;
var item = /*#__PURE__*/React.createElement(MentionItem, {
mention: mention,
selected: _this2.isSelectedMention(mention, idx),
key: key,
onMouseMove: _this2.selectIndexOnHover
/* Cannot use onclick, as onblur will close the element, and prevent
* onClick from firing.
*/,
onSelection: _this2.itemSelected,
ref: function ref(_ref) {
if (_ref) {
_this2.items[key] = _ref;
} else {
delete _this2.items[key];
}
}
});
return item;
}));
}
return null;
}
}, {
key: "isSelectedMention",
value: function isSelectedMention(mention, index) {
var selectedKey = this.state.selectedKey;
return selectedKey ? selectedKey === mention.id : index === 0;
}
}, {
key: "render",
value: function render() {
var _this$props2 = this.props,
mentions = _this$props2.mentions,
resourceError = _this$props2.resourceError;
var hasMentions = mentions && mentions.length;
// If we get an error, but existing mentions are displayed, lets
// just continue to show the existing mentions we have
var mustShowError = resourceError && !hasMentions;
var errorSection;
var resultSection;
if (mustShowError) {
errorSection = /*#__PURE__*/React.createElement(MentionListError, {
error: resourceError
});
} else if (hasMentions) {
resultSection = /*#__PURE__*/React.createElement(Scrollable, {
ref: this.handleScrollableRef
}, this.renderItems());
}
return /*#__PURE__*/React.createElement(MessagesIntlProvider, null, /*#__PURE__*/React.createElement(MentionListStyle, {
empty: !hasMentions && !resourceError
}, errorSection, resultSection));
}
}]);
}(React.PureComponent);
export { MentionList as default };