@atlaskit/editor-plugin-mentions 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/.eslintrc.js +7 -0
  2. package/CHANGELOG.md +1 -0
  3. package/LICENSE.md +13 -0
  4. package/README.md +9 -0
  5. package/dist/cjs/analytics.js +157 -0
  6. package/dist/cjs/index.js +12 -0
  7. package/dist/cjs/messages.js +29 -0
  8. package/dist/cjs/nodeviews/mention.js +117 -0
  9. package/dist/cjs/plugin.js +135 -0
  10. package/dist/cjs/pm-plugins/key.js +8 -0
  11. package/dist/cjs/pm-plugins/main.js +156 -0
  12. package/dist/cjs/pm-plugins/utils.js +23 -0
  13. package/dist/cjs/type-ahead/index.js +362 -0
  14. package/dist/cjs/types.js +5 -0
  15. package/dist/cjs/ui/InviteItem/index.js +76 -0
  16. package/dist/cjs/ui/InviteItem/styles.js +19 -0
  17. package/dist/cjs/ui/Mention/index.js +98 -0
  18. package/dist/cjs/ui/ToolbarMention/index.js +63 -0
  19. package/dist/cjs/utils.js +32 -0
  20. package/dist/es2019/analytics.js +147 -0
  21. package/dist/es2019/index.js +1 -0
  22. package/dist/es2019/messages.js +23 -0
  23. package/dist/es2019/nodeviews/mention.js +80 -0
  24. package/dist/es2019/plugin.js +123 -0
  25. package/dist/es2019/pm-plugins/key.js +2 -0
  26. package/dist/es2019/pm-plugins/main.js +143 -0
  27. package/dist/es2019/pm-plugins/utils.js +14 -0
  28. package/dist/es2019/type-ahead/index.js +338 -0
  29. package/dist/es2019/types.js +1 -0
  30. package/dist/es2019/ui/InviteItem/index.js +67 -0
  31. package/dist/es2019/ui/InviteItem/styles.js +47 -0
  32. package/dist/es2019/ui/Mention/index.js +70 -0
  33. package/dist/es2019/ui/ToolbarMention/index.js +33 -0
  34. package/dist/es2019/utils.js +20 -0
  35. package/dist/esm/analytics.js +150 -0
  36. package/dist/esm/index.js +1 -0
  37. package/dist/esm/messages.js +23 -0
  38. package/dist/esm/nodeviews/mention.js +107 -0
  39. package/dist/esm/plugin.js +129 -0
  40. package/dist/esm/pm-plugins/key.js +2 -0
  41. package/dist/esm/pm-plugins/main.js +148 -0
  42. package/dist/esm/pm-plugins/utils.js +16 -0
  43. package/dist/esm/type-ahead/index.js +350 -0
  44. package/dist/esm/types.js +1 -0
  45. package/dist/esm/ui/InviteItem/index.js +66 -0
  46. package/dist/esm/ui/InviteItem/styles.js +12 -0
  47. package/dist/esm/ui/Mention/index.js +90 -0
  48. package/dist/esm/ui/ToolbarMention/index.js +53 -0
  49. package/dist/esm/utils.js +26 -0
  50. package/dist/types/analytics.d.ts +13 -0
  51. package/dist/types/index.d.ts +2 -0
  52. package/dist/types/messages.d.ts +22 -0
  53. package/dist/types/nodeviews/mention.d.ts +9 -0
  54. package/dist/types/plugin.d.ts +3 -0
  55. package/dist/types/pm-plugins/key.d.ts +3 -0
  56. package/dist/types/pm-plugins/main.d.ts +6 -0
  57. package/dist/types/pm-plugins/utils.d.ts +4 -0
  58. package/dist/types/type-ahead/index.d.ts +17 -0
  59. package/dist/types/types.d.ts +38 -0
  60. package/dist/types/ui/InviteItem/index.d.ts +24 -0
  61. package/dist/types/ui/InviteItem/styles.d.ts +8 -0
  62. package/dist/types/ui/Mention/index.d.ts +19 -0
  63. package/dist/types/ui/ToolbarMention/index.d.ts +13 -0
  64. package/dist/types/utils.d.ts +8 -0
  65. package/dist/types-ts4.5/analytics.d.ts +13 -0
  66. package/dist/types-ts4.5/index.d.ts +2 -0
  67. package/dist/types-ts4.5/messages.d.ts +22 -0
  68. package/dist/types-ts4.5/nodeviews/mention.d.ts +9 -0
  69. package/dist/types-ts4.5/plugin.d.ts +3 -0
  70. package/dist/types-ts4.5/pm-plugins/key.d.ts +3 -0
  71. package/dist/types-ts4.5/pm-plugins/main.d.ts +6 -0
  72. package/dist/types-ts4.5/pm-plugins/utils.d.ts +4 -0
  73. package/dist/types-ts4.5/type-ahead/index.d.ts +17 -0
  74. package/dist/types-ts4.5/types.d.ts +41 -0
  75. package/dist/types-ts4.5/ui/InviteItem/index.d.ts +24 -0
  76. package/dist/types-ts4.5/ui/InviteItem/styles.d.ts +8 -0
  77. package/dist/types-ts4.5/ui/Mention/index.d.ts +19 -0
  78. package/dist/types-ts4.5/ui/ToolbarMention/index.d.ts +13 -0
  79. package/dist/types-ts4.5/utils.d.ts +8 -0
  80. package/package.json +112 -0
  81. package/report.api.md +92 -0
  82. package/tmp/api-report-tmp.d.ts +63 -0
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.shouldKeepInviteItem = exports.isTeamType = exports.isTeamStats = exports.isInviteItem = void 0;
7
+ var _InviteItem = require("./ui/InviteItem");
8
+ var isTeamType = exports.isTeamType = function isTeamType(userType) {
9
+ return userType === 'TEAM';
10
+ };
11
+ var isTeamStats = exports.isTeamStats = function isTeamStats(stat) {
12
+ return stat && !isNaN(stat.teamMentionDuration);
13
+ };
14
+ var isInviteItem = exports.isInviteItem = function isInviteItem(mention) {
15
+ return mention && mention.id === _InviteItem.INVITE_ITEM_DESCRIPTION.id;
16
+ };
17
+
18
+ /**
19
+ * Actions
20
+ */
21
+ var shouldKeepInviteItem = exports.shouldKeepInviteItem = function shouldKeepInviteItem(query, firstQueryWithoutResults) {
22
+ if (!firstQueryWithoutResults) {
23
+ return true;
24
+ }
25
+ var lastIndexWithResults = firstQueryWithoutResults.length - 1;
26
+ var suffix = query.slice(lastIndexWithResults);
27
+ if (query[lastIndexWithResults - 1] === ' ') {
28
+ suffix = ' ' + suffix;
29
+ }
30
+ var depletedExtraWords = /\s[^\s]+\s/.test(suffix);
31
+ return !depletedExtraWords;
32
+ };
@@ -0,0 +1,147 @@
1
+ import { OPERATIONAL_EVENT_TYPE, UI_EVENT_TYPE } from '@atlaskit/analytics-gas-types';
2
+ import { isSpecialMention } from '@atlaskit/mention/resource';
3
+ import { isTeamType } from './utils';
4
+ const componentName = 'mention';
5
+ export const buildAnalyticsPayload = (actionSubject, action, eventType, sessionId, otherAttributes = {}, tags = []) => ({
6
+ action,
7
+ actionSubject,
8
+ eventType,
9
+ attributes: {
10
+ componentName,
11
+ sessionId,
12
+ ...otherAttributes
13
+ },
14
+ tags
15
+ });
16
+ const emptyQueryResponse = {
17
+ queryLength: 0,
18
+ spaceInQuery: false
19
+ };
20
+ const extractAttributesFromQuery = query => {
21
+ if (query) {
22
+ return {
23
+ queryLength: query.length,
24
+ spaceInQuery: query.indexOf(' ') !== -1
25
+ };
26
+ }
27
+ return emptyQueryResponse;
28
+ };
29
+ export const buildTypeAheadCancelPayload = (duration, upKeyCount, downKeyCount, sessionId, query) => {
30
+ const {
31
+ queryLength,
32
+ spaceInQuery
33
+ } = extractAttributesFromQuery(query);
34
+ return buildAnalyticsPayload('mentionTypeahead', 'cancelled', UI_EVENT_TYPE, sessionId, {
35
+ duration,
36
+ downKeyCount,
37
+ upKeyCount,
38
+ queryLength,
39
+ spaceInQuery
40
+ });
41
+ };
42
+ const getPosition = (mentionList, selectedMention) => {
43
+ if (mentionList) {
44
+ const index = mentionList.findIndex(mention => mention.id === selectedMention.id);
45
+ return index === -1 ? undefined : index;
46
+ }
47
+ return;
48
+ };
49
+ const isClicked = insertType => insertType === 'selected';
50
+ export const buildTypeAheadInviteItemViewedPayload = (sessionId, contextIdentifierProvider, userRole) => {
51
+ const {
52
+ containerId,
53
+ objectId,
54
+ childObjectId
55
+ } = contextIdentifierProvider || {};
56
+ return buildAnalyticsPayload('inviteItem', 'rendered', UI_EVENT_TYPE, sessionId, {
57
+ containerId,
58
+ objectId,
59
+ childObjectId,
60
+ userRole
61
+ });
62
+ };
63
+ export const buildTypeAheadInviteExposurePayload = (sessionId, contextIdentifierProvider, inviteExperimentCohort, userRole) => {
64
+ const {
65
+ containerId,
66
+ objectId,
67
+ childObjectId
68
+ } = contextIdentifierProvider || {};
69
+ return buildAnalyticsPayload('feature', 'exposed', OPERATIONAL_EVENT_TYPE, sessionId, {
70
+ flagKey: 'confluence.frontend.invite.from.mention',
71
+ value: inviteExperimentCohort || 'not-enrolled',
72
+ containerId,
73
+ objectId,
74
+ childObjectId,
75
+ userRole
76
+ }, ['measurement', 'hasCustomAttributes']);
77
+ };
78
+ export const buildTypeAheadInviteItemClickedPayload = (duration, upKeyCount, downKeyCount, sessionId, insertType, query, contextIdentifierProvider, userRole) => {
79
+ const {
80
+ queryLength,
81
+ spaceInQuery
82
+ } = extractAttributesFromQuery(query);
83
+ const {
84
+ containerId,
85
+ objectId,
86
+ childObjectId
87
+ } = contextIdentifierProvider || {};
88
+ return buildAnalyticsPayload('inviteItem', isClicked(insertType) ? 'clicked' : 'pressed', UI_EVENT_TYPE, sessionId, {
89
+ duration,
90
+ queryLength,
91
+ spaceInQuery,
92
+ upKeyCount,
93
+ downKeyCount,
94
+ containerId,
95
+ objectId,
96
+ childObjectId,
97
+ userRole
98
+ });
99
+ };
100
+ export const buildTypeAheadInsertedPayload = (duration, upKeyCount, downKeyCount, sessionId, insertType, mention, mentionList, query, contextIdentifierProvider) => {
101
+ const {
102
+ queryLength,
103
+ spaceInQuery
104
+ } = extractAttributesFromQuery(query);
105
+ let analyticsPayload = buildAnalyticsPayload('mentionTypeahead', isClicked(insertType) ? 'clicked' : 'pressed', UI_EVENT_TYPE, sessionId, {
106
+ duration,
107
+ position: getPosition(mentionList, mention),
108
+ keyboardKey: isClicked(insertType) ? undefined : insertType,
109
+ source: mention.source,
110
+ queryLength,
111
+ spaceInQuery,
112
+ isSpecial: isSpecialMention(mention),
113
+ accessLevel: mention.accessLevel || '',
114
+ userType: mention.userType,
115
+ userId: mention.id,
116
+ upKeyCount,
117
+ downKeyCount,
118
+ memberCount: isTeamType(mention.userType) && mention.context ? mention.context.memberCount : null,
119
+ includesYou: isTeamType(mention.userType) && mention.context ? mention.context.includesYou : null
120
+ });
121
+ if (contextIdentifierProvider) {
122
+ analyticsPayload.containerId = contextIdentifierProvider.containerId || undefined;
123
+ analyticsPayload.objectId = contextIdentifierProvider.objectId || undefined;
124
+ analyticsPayload.childObjectId = contextIdentifierProvider.childObjectId || undefined;
125
+ }
126
+ return analyticsPayload;
127
+ };
128
+ export const buildTypeAheadRenderedPayload = (duration, userIds, query, teams) => {
129
+ const {
130
+ queryLength,
131
+ spaceInQuery
132
+ } = extractAttributesFromQuery(query);
133
+ const actionSubject = userIds ? 'mentionTypeahead' : 'teamMentionTypeahead';
134
+ return {
135
+ action: 'rendered',
136
+ actionSubject,
137
+ eventType: OPERATIONAL_EVENT_TYPE,
138
+ attributes: {
139
+ componentName,
140
+ duration,
141
+ userIds,
142
+ teams,
143
+ queryLength,
144
+ spaceInQuery
145
+ }
146
+ };
147
+ };
@@ -0,0 +1 @@
1
+ export { mentionsPlugin } from './plugin';
@@ -0,0 +1,23 @@
1
+ import { defineMessages } from 'react-intl-next';
2
+ export const messages = defineMessages({
3
+ inviteItemTitle: {
4
+ id: 'fabric.editor.inviteItem.title',
5
+ defaultMessage: '{userRole, select, admin {Invite} trusted {Invite} other {Add}} teammate to {productName}',
6
+ description: 'Title of the invite teammate item in typeahead plugin'
7
+ },
8
+ mentionsAddLabel: {
9
+ id: 'fabric.editor.mentionsAddLabel',
10
+ defaultMessage: 'add-icon',
11
+ description: 'icon label to describe adding a new mention'
12
+ },
13
+ mentionsIconLabel: {
14
+ id: 'fabric.editor.mentionsIconLabel',
15
+ defaultMessage: 'Mention',
16
+ description: 'icon label to describe the mention icon'
17
+ },
18
+ mentionsNodeLabel: {
19
+ id: 'fabric.editor.mentionNode.label',
20
+ defaultMessage: 'Tagged user',
21
+ description: 'Label to indicate mention node to Screen reader users, that preceeds with user name ex: "Tagged user @XXX'
22
+ }
23
+ });
@@ -0,0 +1,80 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { useIntl } from 'react-intl-next';
3
+ import { WithProviders } from '@atlaskit/editor-common/provider-factory';
4
+ import { isResolvingMentionProvider, MentionNameStatus } from '@atlaskit/mention';
5
+ import { isPromise } from '@atlaskit/mention/types';
6
+ import { messages } from '../messages';
7
+ import Mention from '../ui/Mention';
8
+ const UNKNOWN_USER_ID = '_|unknown|_';
9
+ const MentionAssistiveTextComponent = ({
10
+ id,
11
+ text,
12
+ providers,
13
+ accessLevel,
14
+ mentionProvider
15
+ }) => {
16
+ const [resolvedName, setResolvedName] = useState(text);
17
+ const intl = useIntl();
18
+ const processName = name => {
19
+ if (name.status === MentionNameStatus.OK) {
20
+ return `@${name.name || ''}`;
21
+ } else {
22
+ return `@${UNKNOWN_USER_ID}`;
23
+ }
24
+ };
25
+ useEffect(() => {
26
+ if (mentionProvider) {
27
+ mentionProvider.then(async provider => {
28
+ if (!text && isResolvingMentionProvider(provider)) {
29
+ const nameDetail = provider.resolveMentionName(id);
30
+ if (isPromise(nameDetail)) {
31
+ return processName(await nameDetail);
32
+ } else {
33
+ return processName(nameDetail);
34
+ }
35
+ } else {
36
+ return text;
37
+ }
38
+ }).then(resolvedName => {
39
+ setResolvedName(resolvedName);
40
+ });
41
+ }
42
+ }, [id, text, mentionProvider]);
43
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", {
44
+ className: 'assistive'
45
+ }, `${intl.formatMessage(messages.mentionsNodeLabel)} ${resolvedName}`), /*#__PURE__*/React.createElement("span", {
46
+ "aria-hidden": "true"
47
+ }, /*#__PURE__*/React.createElement(Mention, {
48
+ id: id,
49
+ text: resolvedName,
50
+ accessLevel: accessLevel,
51
+ providers: providers
52
+ })));
53
+ };
54
+ export const MentionNodeView = props => {
55
+ const {
56
+ providerFactory
57
+ } = props;
58
+ const {
59
+ id,
60
+ text,
61
+ accessLevel
62
+ } = props.node.attrs;
63
+ const renderAssistiveTextWithProviders = providers => {
64
+ const {
65
+ mentionProvider
66
+ } = providers;
67
+ return /*#__PURE__*/React.createElement(MentionAssistiveTextComponent, {
68
+ mentionProvider: mentionProvider,
69
+ id: id,
70
+ text: text,
71
+ providers: providerFactory,
72
+ accessLevel: accessLevel
73
+ });
74
+ };
75
+ return /*#__PURE__*/React.createElement(WithProviders, {
76
+ providers: ['mentionProvider', 'profilecardProvider'],
77
+ providerFactory: providerFactory,
78
+ renderNode: renderAssistiveTextWithProviders
79
+ });
80
+ };
@@ -0,0 +1,123 @@
1
+ import React from 'react';
2
+ import uuid from 'uuid';
3
+ import { mention } from '@atlaskit/adf-schema';
4
+ import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
5
+ import { toolbarInsertBlockMessages as messages } from '@atlaskit/editor-common/messages';
6
+ import { IconMention } from '@atlaskit/editor-common/quick-insert';
7
+ import { WithPluginState } from '@atlaskit/editor-common/with-plugin-state';
8
+ import { ELEMENTS_CHANNEL } from '@atlaskit/mention/resource';
9
+ import { mentionPluginKey } from './pm-plugins/key';
10
+ import { createMentionPlugin } from './pm-plugins/main';
11
+ import { createTypeAheadConfig } from './type-ahead';
12
+ import ToolbarMention from './ui/ToolbarMention';
13
+ const mentionsPlugin = ({
14
+ config: options,
15
+ api
16
+ }) => {
17
+ let sessionId = uuid();
18
+ const fireEvent = payload => {
19
+ var _api$analytics$shared, _api$analytics;
20
+ const {
21
+ createAnalyticsEvent
22
+ } = (_api$analytics$shared = api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.sharedState.currentState()) !== null && _api$analytics$shared !== void 0 ? _api$analytics$shared : {};
23
+ if (!createAnalyticsEvent) {
24
+ return;
25
+ }
26
+ if (payload.attributes && !payload.attributes.sessionId) {
27
+ payload.attributes.sessionId = sessionId;
28
+ }
29
+ createAnalyticsEvent(payload).fire(ELEMENTS_CHANNEL);
30
+ };
31
+ const typeAhead = createTypeAheadConfig({
32
+ sanitizePrivateContent: options === null || options === void 0 ? void 0 : options.sanitizePrivateContent,
33
+ mentionInsertDisplayName: options === null || options === void 0 ? void 0 : options.insertDisplayName,
34
+ HighlightComponent: options === null || options === void 0 ? void 0 : options.HighlightComponent,
35
+ fireEvent
36
+ });
37
+ return {
38
+ name: 'mention',
39
+ nodes() {
40
+ return [{
41
+ name: 'mention',
42
+ node: mention
43
+ }];
44
+ },
45
+ pmPlugins() {
46
+ return [{
47
+ name: 'mention',
48
+ plugin: pmPluginFactoryParams => createMentionPlugin(pmPluginFactoryParams, fireEvent, options)
49
+ }];
50
+ },
51
+ secondaryToolbarComponent({
52
+ editorView,
53
+ disabled
54
+ }) {
55
+ const openMentionTypeAhead = () => {
56
+ var _api$typeAhead, _api$typeAhead$action;
57
+ api === null || api === void 0 ? void 0 : (_api$typeAhead = api.typeAhead) === null || _api$typeAhead === void 0 ? void 0 : (_api$typeAhead$action = _api$typeAhead.actions) === null || _api$typeAhead$action === void 0 ? void 0 : _api$typeAhead$action.open({
58
+ triggerHandler: typeAhead,
59
+ inputMethod: INPUT_METHOD.INSERT_MENU
60
+ });
61
+ };
62
+ return /*#__PURE__*/React.createElement(WithPluginState, {
63
+ editorView: editorView,
64
+ plugins: {
65
+ mentionState: mentionPluginKey
66
+ },
67
+ render: ({
68
+ mentionState = {}
69
+ }) => !mentionState.mentionProvider ? null : /*#__PURE__*/React.createElement(ToolbarMention, {
70
+ editorView: editorView,
71
+ onInsertMention: openMentionTypeAhead,
72
+ isDisabled: disabled || (api === null || api === void 0 ? void 0 : api.typeAhead.actions.isAllowed(editorView.state))
73
+ })
74
+ });
75
+ },
76
+ actions: {
77
+ openTypeAhead(inputMethod) {
78
+ var _api$typeAhead2, _api$typeAhead2$actio;
79
+ return Boolean(api === null || api === void 0 ? void 0 : (_api$typeAhead2 = api.typeAhead) === null || _api$typeAhead2 === void 0 ? void 0 : (_api$typeAhead2$actio = _api$typeAhead2.actions) === null || _api$typeAhead2$actio === void 0 ? void 0 : _api$typeAhead2$actio.open({
80
+ triggerHandler: typeAhead,
81
+ inputMethod
82
+ }));
83
+ }
84
+ },
85
+ getSharedState(editorState) {
86
+ if (!editorState) {
87
+ return undefined;
88
+ }
89
+ const mentionPluginState = mentionPluginKey.getState(editorState);
90
+ return {
91
+ ...mentionPluginState,
92
+ typeAheadHandler: typeAhead
93
+ };
94
+ },
95
+ pluginsOptions: {
96
+ quickInsert: ({
97
+ formatMessage
98
+ }) => [{
99
+ id: 'mention',
100
+ title: formatMessage(messages.mention),
101
+ description: formatMessage(messages.mentionDescription),
102
+ keywords: ['team', 'user'],
103
+ priority: 400,
104
+ keyshortcut: '@',
105
+ icon: () => /*#__PURE__*/React.createElement(IconMention, null),
106
+ action(insert, state) {
107
+ const tr = insert(undefined);
108
+ const pluginState = mentionPluginKey.getState(state);
109
+ if (pluginState && pluginState.canInsertMention === false) {
110
+ return false;
111
+ }
112
+ api === null || api === void 0 ? void 0 : api.typeAhead.actions.openAtTransaction({
113
+ triggerHandler: typeAhead,
114
+ inputMethod: INPUT_METHOD.QUICK_INSERT
115
+ })(tr);
116
+ return tr;
117
+ }
118
+ }],
119
+ typeAhead
120
+ }
121
+ };
122
+ };
123
+ export { mentionsPlugin };
@@ -0,0 +1,2 @@
1
+ import { PluginKey } from '@atlaskit/editor-prosemirror/state';
2
+ export const mentionPluginKey = new PluginKey('mentionPlugin');
@@ -0,0 +1,143 @@
1
+ import { getInlineNodeViewProducer } from '@atlaskit/editor-common/react-node-view';
2
+ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
3
+ import { buildSliPayload, SLI_EVENT_TYPE, SMART_EVENT_TYPE } from '@atlaskit/mention/resource';
4
+ import { MentionNodeView } from '../nodeviews/mention';
5
+ import { mentionPluginKey } from './key';
6
+ import { canMentionBeCreatedInRange } from './utils';
7
+ const ACTIONS = {
8
+ SET_PROVIDER: 'SET_PROVIDER',
9
+ SET_CONTEXT: 'SET_CONTEXT'
10
+ };
11
+ const setProvider = provider => (state, dispatch) => {
12
+ if (dispatch) {
13
+ dispatch(state.tr.setMeta(mentionPluginKey, {
14
+ action: ACTIONS.SET_PROVIDER,
15
+ params: {
16
+ provider
17
+ }
18
+ }));
19
+ }
20
+ return true;
21
+ };
22
+ export const setContext = context => (state, dispatch) => {
23
+ if (dispatch) {
24
+ dispatch(state.tr.setMeta(mentionPluginKey, {
25
+ action: ACTIONS.SET_CONTEXT,
26
+ params: {
27
+ context
28
+ }
29
+ }));
30
+ }
31
+ return true;
32
+ };
33
+ export function createMentionPlugin(pmPluginFactoryParams, fireEvent, options) {
34
+ let mentionProvider;
35
+ const sendAnalytics = (event, actionSubject, action, attributes) => {
36
+ if (event === SLI_EVENT_TYPE || event === SMART_EVENT_TYPE) {
37
+ fireEvent(buildSliPayload(actionSubject, action, attributes));
38
+ }
39
+ };
40
+ return new SafePlugin({
41
+ key: mentionPluginKey,
42
+ state: {
43
+ init(_, state) {
44
+ const canInsertMention = canMentionBeCreatedInRange(state.selection.from, state.selection.to)(state);
45
+ return {
46
+ canInsertMention
47
+ };
48
+ },
49
+ apply(tr, pluginState, oldState, newState) {
50
+ const {
51
+ action,
52
+ params
53
+ } = tr.getMeta(mentionPluginKey) || {
54
+ action: null,
55
+ params: null
56
+ };
57
+ let hasNewPluginState = false;
58
+ let newPluginState = pluginState;
59
+ const hasPositionChanged = oldState.selection.from !== newState.selection.from || oldState.selection.to !== newState.selection.to;
60
+ if (tr.docChanged || tr.selectionSet && hasPositionChanged) {
61
+ newPluginState = {
62
+ ...pluginState,
63
+ canInsertMention: canMentionBeCreatedInRange(newState.selection.from, newState.selection.to)(newState)
64
+ };
65
+ hasNewPluginState = true;
66
+ }
67
+ switch (action) {
68
+ case ACTIONS.SET_PROVIDER:
69
+ newPluginState = {
70
+ ...newPluginState,
71
+ mentionProvider: params.provider
72
+ };
73
+ hasNewPluginState = true;
74
+ break;
75
+ case ACTIONS.SET_CONTEXT:
76
+ newPluginState = {
77
+ ...newPluginState,
78
+ contextIdentifierProvider: params.context
79
+ };
80
+ hasNewPluginState = true;
81
+ break;
82
+ }
83
+ if (hasNewPluginState) {
84
+ pmPluginFactoryParams.dispatch(mentionPluginKey, newPluginState);
85
+ }
86
+ return newPluginState;
87
+ }
88
+ },
89
+ props: {
90
+ nodeViews: {
91
+ mention: getInlineNodeViewProducer({
92
+ pmPluginFactoryParams,
93
+ Component: MentionNodeView,
94
+ extraComponentProps: {
95
+ providerFactory: pmPluginFactoryParams.providerFactory,
96
+ options
97
+ }
98
+ })
99
+ }
100
+ },
101
+ view(editorView) {
102
+ const providerHandler = (name, providerPromise) => {
103
+ switch (name) {
104
+ case 'mentionProvider':
105
+ if (!providerPromise) {
106
+ return setProvider(undefined)(editorView.state, editorView.dispatch);
107
+ }
108
+ providerPromise.then(provider => {
109
+ if (mentionProvider) {
110
+ mentionProvider.unsubscribe('mentionPlugin');
111
+ }
112
+ mentionProvider = provider;
113
+ setProvider(provider)(editorView.state, editorView.dispatch);
114
+ provider.subscribe('mentionPlugin', undefined, undefined, undefined, undefined, sendAnalytics);
115
+ }).catch(() => setProvider(undefined)(editorView.state, editorView.dispatch));
116
+ break;
117
+ case 'contextIdentifierProvider':
118
+ if (!providerPromise) {
119
+ return setContext(undefined)(editorView.state, editorView.dispatch);
120
+ }
121
+ providerPromise.then(provider => {
122
+ setContext(provider)(editorView.state, editorView.dispatch);
123
+ });
124
+ break;
125
+ }
126
+ return;
127
+ };
128
+ pmPluginFactoryParams.providerFactory.subscribe('mentionProvider', providerHandler);
129
+ pmPluginFactoryParams.providerFactory.subscribe('contextIdentifierProvider', providerHandler);
130
+ return {
131
+ destroy() {
132
+ if (pmPluginFactoryParams.providerFactory) {
133
+ pmPluginFactoryParams.providerFactory.unsubscribe('mentionProvider', providerHandler);
134
+ pmPluginFactoryParams.providerFactory.unsubscribe('contextIdentifierProvider', providerHandler);
135
+ }
136
+ if (mentionProvider) {
137
+ mentionProvider.unsubscribe('mentionPlugin');
138
+ }
139
+ }
140
+ };
141
+ }
142
+ });
143
+ }
@@ -0,0 +1,14 @@
1
+ import { canInsert } from '@atlaskit/editor-prosemirror/utils';
2
+ import { mentionPluginKey } from './key';
3
+ export function getMentionPluginState(state) {
4
+ return mentionPluginKey.getState(state);
5
+ }
6
+ export const canMentionBeCreatedInRange = (from, to) => state => {
7
+ const $from = state.doc.resolve(from);
8
+ const $to = state.doc.resolve(to);
9
+ const mention = state.schema.nodes.mention.createChecked();
10
+ if ($from.parent === $to.parent && canInsert($from, mention)) {
11
+ return true;
12
+ }
13
+ return false;
14
+ };