UNPKG

@atlaskit/mention

Version:

A React component used to display user profiles in a list for 'Mention' functionality

239 lines (236 loc) 8.65 kB
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 { MentionItemWithRef } 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.itemsRefs = new Map(); _this.setDefaultSelectionState(); return _this; } _inherits(MentionList, _React$PureComponent); return _createClass(MentionList, [{ key: "createItemRef", value: function createItemRef(key) { var itemRef = /*#__PURE__*/React.createRef(); this.itemsRefs.set(key, itemRef); return itemRef; } }, { key: "getItemRef", value: function getItemRef(key) { return this.itemsRefs.get(key); } }, { 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 itemRef = this.getItemRef(key); if (itemRef && this.scrollable) { itemRef && this.scrollable.revealRef(itemRef); } } /** * 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) { return /*#__PURE__*/React.createElement("div", null, this.props.initialHighlightElement, mentions.map(function (mention, idx) { var key = mention.id; var ref = _this2.createItemRef(key); var item = /*#__PURE__*/React.createElement(MentionItemWithRef, { 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: ref }); 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 };