@atlaskit/mention
Version:
A React component used to display user profiles in a list for 'Mention' functionality
205 lines (203 loc) • 6.96 kB
JavaScript
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import { Text } from '@atlaskit/primitives/compiled';
import withAnalyticsEvents from '@atlaskit/analytics-next/withAnalyticsEvents';
import React from 'react';
import { IntlProvider, injectIntl } from 'react-intl-next';
import { fireAnalyticsMentionTypeaheadEvent } from '../../util/analytics';
import uniqueId from '../../util/id';
import debug from '../../util/logger';
import Popup from '../Popup';
import ResourcedMentionList from '../ResourcedMentionList';
import { MentionPickerInfoStyle, MentionPickerStyle } from './styles';
/**
* @class MentionPicker
*/
export class MentionPicker extends React.PureComponent {
constructor(props) {
super(props);
_defineProperty(this, "selectNext", () => {
if (this.mentionListRef) {
this.mentionListRef.selectNext();
}
});
_defineProperty(this, "selectPrevious", () => {
if (this.mentionListRef) {
this.mentionListRef.selectPrevious();
}
});
_defineProperty(this, "selectIndex", (index, callback) => {
if (this.mentionListRef) {
this.mentionListRef.selectIndex(index, callback);
}
});
_defineProperty(this, "selectId", (id, callback) => {
if (this.mentionListRef) {
this.mentionListRef.selectId(id, callback);
}
});
_defineProperty(this, "chooseCurrentSelection", () => {
if (this.mentionListRef) {
this.mentionListRef.chooseCurrentSelection();
}
});
_defineProperty(this, "mentionsCount", () => {
if (this.mentionListRef) {
return this.mentionListRef.mentionsCount();
}
return 0;
});
/**
* Called after the 'visible' state is changed to decide whether the onOpen or onClose
* handlers should be called.
*
* It should be noted that the visible state of the component is not considered in
* this function. Instead the old state and new state should be passed as parameters.
*/
_defineProperty(this, "onFilterVisibilityChange", (oldVisibility, newVisibility) => {
if (oldVisibility !== newVisibility) {
if (newVisibility) {
if (this.props.onOpen) {
this.props.onOpen();
}
} else {
if (this.props.onClose) {
this.props.onClose();
}
}
}
});
// internal, used for callbacks
_defineProperty(this, "filterChange", (mentions, query, stats) => {
debug('ak-mention-picker.filterChange', mentions.length);
const wasVisible = this.state.visible;
const visible = mentions.length > 0;
this.setState({
visible
});
this.onFilterVisibilityChange(wasVisible, visible);
fireAnalyticsMentionTypeaheadEvent(this.props)('rendered', stats && stats.duration, mentions.map(mention => mention.id), query);
});
_defineProperty(this, "filterError", error => {
debug('ak-mention-picker.filterError', error);
const wasVisible = this.state.visible;
this.setState({
visible: true,
info: undefined
});
this.onFilterVisibilityChange(wasVisible, true);
});
_defineProperty(this, "filterInfo", info => {
debug('ak-mention-picker.filterInfo', info);
this.setState({
info
});
});
_defineProperty(this, "handleMentionListRef", ref => {
this.mentionListRef = ref;
});
this.subscriberKey = uniqueId('ak-mention-picker');
this.state = {
visible: false
};
this.applyPropChanges({}, props);
}
componentDidMount() {
this.subscribeResourceProvider(this.props.resourceProvider);
}
UNSAFE_componentWillReceiveProps(nextProps) {
this.applyPropChanges(this.props, nextProps);
}
componentWillUnmount() {
this.unsubscribeResourceProvider(this.props.resourceProvider);
}
// Internal
applyPropChanges(prevProps, nextProps) {
const oldResourceProvider = prevProps.resourceProvider;
const newResourceProvider = nextProps.resourceProvider;
const resourceProviderChanged = oldResourceProvider !== newResourceProvider;
// resource provider
if (resourceProviderChanged) {
this.unsubscribeResourceProvider(oldResourceProvider);
this.subscribeResourceProvider(newResourceProvider);
}
}
subscribeResourceProvider(resourceProvider) {
if (resourceProvider) {
resourceProvider.subscribe(this.subscriberKey, this.filterChange, this.filterError, this.filterInfo, undefined);
}
}
unsubscribeResourceProvider(resourceProvider) {
if (resourceProvider) {
resourceProvider.unsubscribe(this.subscriberKey);
}
}
render() {
const {
resourceProvider,
presenceProvider,
onSelection,
query,
target,
position,
zIndex,
offsetX,
offsetY,
intl
} = this.props;
const {
visible,
info
} = this.state;
const resourceMentionList = /*#__PURE__*/React.createElement(ResourcedMentionList, {
resourceProvider: resourceProvider,
presenceProvider: presenceProvider,
onSelection: onSelection,
query: query,
ref: this.handleMentionListRef
});
const infoContent = info && !visible ? /*#__PURE__*/React.createElement(MentionPickerInfoStyle, null, /*#__PURE__*/React.createElement(Text, {
as: "p"
}, info)) : null;
let content;
if (position) {
debug('target, position', target, position);
if (target) {
content = /*#__PURE__*/React.createElement(Popup, {
target: target,
relativePosition: position,
zIndex: zIndex,
offsetX: offsetX,
offsetY: offsetY
}, /*#__PURE__*/React.createElement(IntlProvider, {
locale: (intl === null || intl === void 0 ? void 0 : intl.locale) || 'en'
}, /*#__PURE__*/React.createElement("div", null, resourceMentionList, infoContent)));
} else {
// don't show if we have a position, but no target yet
content = null;
}
} else {
content = /*#__PURE__*/React.createElement("div", null, resourceMentionList, infoContent);
}
return (
/*#__PURE__*/
/* old classnames are essential for Confluence tests */
React.createElement(MentionPickerStyle
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
, {
className: "ak-mention-picker",
visible: visible || info
}, content)
);
}
}
_defineProperty(MentionPicker, "defaultProps", {
onSelection: () => {},
onOpen: () => {},
onClose: () => {}
});
const MentionPickerWithIntl = injectIntl(MentionPicker, {
forwardRef: true
});
// @ts-ignore: [PIT-1685] Fails in post-office due to backwards incompatibility issue with React 18
export const MentionPickerWithAnalytics = withAnalyticsEvents({})(MentionPickerWithIntl);
export default MentionPickerWithAnalytics;