@atlaskit/editor-plugin-mentions 16.1.4 → 16.2.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.
- package/CHANGELOG.md +17 -0
- package/compass.yml +2 -2
- package/dist/cjs/entry-points/mention-node-data-provider.js +12 -0
- package/dist/cjs/nodeviews/mentionAvatarRenderer.js +75 -0
- package/dist/cjs/nodeviews/mentionNodeView.js +79 -10
- package/dist/cjs/nodeviews/profileCardRenderer.js +11 -9
- package/dist/cjs/pm-plugins/main.js +1 -1
- package/dist/cjs/providers/mention-node-data-provider.js +205 -0
- package/dist/es2019/entry-points/mention-node-data-provider.js +3 -0
- package/dist/es2019/nodeviews/mentionAvatarRenderer.js +71 -0
- package/dist/es2019/nodeviews/mentionNodeView.js +75 -10
- package/dist/es2019/nodeviews/profileCardRenderer.js +7 -5
- package/dist/es2019/pm-plugins/main.js +1 -1
- package/dist/es2019/providers/mention-node-data-provider.js +115 -0
- package/dist/esm/entry-points/mention-node-data-provider.js +3 -0
- package/dist/esm/nodeviews/mentionAvatarRenderer.js +69 -0
- package/dist/esm/nodeviews/mentionNodeView.js +79 -10
- package/dist/esm/nodeviews/profileCardRenderer.js +11 -9
- package/dist/esm/pm-plugins/main.js +1 -1
- package/dist/esm/providers/mention-node-data-provider.js +198 -0
- package/dist/types/entry-points/mention-node-data-provider.d.ts +2 -0
- package/dist/types/nodeviews/mentionAvatarRenderer.d.ts +12 -0
- package/dist/types/nodeviews/mentionNodeView.d.ts +6 -0
- package/dist/types/providers/mention-node-data-provider.d.ts +39 -0
- package/dist/types/types/index.d.ts +6 -0
- package/mention-node-data-provider/package.json +10 -0
- package/package.json +17 -9
|
@@ -1,14 +1,23 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
1
2
|
import { getBrowserInfo } from '@atlaskit/editor-common/browser';
|
|
2
3
|
import { ZERO_WIDTH_SPACE } from '@atlaskit/editor-common/whitespace';
|
|
3
4
|
import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
|
|
5
|
+
import { UNKNOWN_USER_ID } from '@atlaskit/mention/constants';
|
|
4
6
|
import { isResolvingMentionProvider, MentionNameStatus } from '@atlaskit/mention/resource';
|
|
5
7
|
import { isRestricted } from '@atlaskit/mention/types';
|
|
8
|
+
import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
|
|
6
9
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
7
10
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
8
11
|
import { expVal } from '@atlaskit/tmp-editor-statsig/expVal';
|
|
9
12
|
import { disabledTooltipRenderer } from './disabledTooltipRenderer';
|
|
13
|
+
import { mentionAvatarRenderer } from './mentionAvatarRenderer';
|
|
10
14
|
import { profileCardRenderer } from './profileCardRenderer';
|
|
11
15
|
const primitiveClassName = 'editor-mention-primitive';
|
|
16
|
+
const primitiveWithAvatarClassName = 'editor-mention-primitive-with-avatar';
|
|
17
|
+
const avatarContainerClassName = 'editor-mention-avatar';
|
|
18
|
+
const mentionTextClassName = 'editor-mention-text';
|
|
19
|
+
const genericMentionIds = ['HipChat', 'all', 'here'];
|
|
20
|
+
const unknownMentionText = `@${UNKNOWN_USER_ID}`;
|
|
12
21
|
// eslint-disable-next-line require-unicode-regexp
|
|
13
22
|
const AT_PREFIX_REGEX = /^@/;
|
|
14
23
|
const getAccessibilityLabelFromName = name => name.replace(AT_PREFIX_REGEX, '');
|
|
@@ -31,7 +40,7 @@ const isAgentMentionsExperimentEnabled = () => expVal('platform_editor_agent_men
|
|
|
31
40
|
* consistent and can't drift apart.
|
|
32
41
|
*/
|
|
33
42
|
const isAgentAaidText = (node, isAgentMentionsEnabled) => isAgentMentionsEnabled && (node.attrs.userType === 'APP' || node.attrs.userType === 'AGENT') && !!node.attrs.text && isMentionTextAnAaid(node.attrs.text);
|
|
34
|
-
const toDOM = node => {
|
|
43
|
+
const toDOM = (node, hasAvatarSlot) => {
|
|
35
44
|
// packages/elements/mention/src/components/Mention/index.tsx
|
|
36
45
|
let mentionAttrs = {
|
|
37
46
|
contenteditable: 'false',
|
|
@@ -58,14 +67,25 @@ const toDOM = node => {
|
|
|
58
67
|
}
|
|
59
68
|
const browser = getBrowserInfo();
|
|
60
69
|
const hasAgentAaidText = isAgentAaidText(node, isAgentMentionsEnabled);
|
|
70
|
+
const mentionText = node.attrs.text && !hasAgentAaidText ? node.attrs.text : '@…';
|
|
71
|
+
const visibleMentionText = hasAvatarSlot && mentionText.startsWith('@') ? mentionText.slice(1) : mentionText;
|
|
72
|
+
const mentionContentSpec = hasAvatarSlot ? ['span', {
|
|
73
|
+
spellcheck: 'false',
|
|
74
|
+
class: `${primitiveClassName} ${primitiveWithAvatarClassName}`
|
|
75
|
+
}, ['span', {
|
|
76
|
+
class: avatarContainerClassName,
|
|
77
|
+
'aria-hidden': 'true'
|
|
78
|
+
}], ['span', {
|
|
79
|
+
class: mentionTextClassName
|
|
80
|
+
}, visibleMentionText]] : ['span', {
|
|
81
|
+
spellcheck: 'false',
|
|
82
|
+
class: primitiveClassName
|
|
83
|
+
}, mentionText];
|
|
61
84
|
return ['span', mentionAttrs, ['span', {
|
|
62
85
|
class: 'zeroWidthSpaceContainer'
|
|
63
86
|
}, ['span', {
|
|
64
87
|
class: 'inlineNodeViewAddZeroWidthSpace'
|
|
65
|
-
}, ZERO_WIDTH_SPACE]], ['span', {
|
|
66
|
-
spellcheck: 'false',
|
|
67
|
-
class: primitiveClassName
|
|
68
|
-
}, node.attrs.text && !hasAgentAaidText ? node.attrs.text : '@…'], browser.android ? ['span', {
|
|
88
|
+
}, ZERO_WIDTH_SPACE]], mentionContentSpec, browser.android ? ['span', {
|
|
69
89
|
class: 'zeroWidthSpaceContainer',
|
|
70
90
|
contenteditable: 'false'
|
|
71
91
|
}, ['span', {
|
|
@@ -75,7 +95,7 @@ const toDOM = node => {
|
|
|
75
95
|
}, ZERO_WIDTH_SPACE]];
|
|
76
96
|
};
|
|
77
97
|
const processName = name => {
|
|
78
|
-
return name.status === MentionNameStatus.OK ? `@${name.name || ''}` :
|
|
98
|
+
return name.status === MentionNameStatus.OK ? `@${name.name || ''}` : unknownMentionText;
|
|
79
99
|
};
|
|
80
100
|
const handleProviderName = async (mentionProvider, node, isAgentMentionsEnabled) => {
|
|
81
101
|
// Also resolve when text is a raw AAID — provider will return the real display name.
|
|
@@ -101,22 +121,37 @@ const getNewState = (isHighlighted, isRestricted, isDisabled) => {
|
|
|
101
121
|
export class MentionNodeView {
|
|
102
122
|
constructor(node, config) {
|
|
103
123
|
var _this$domElement$quer, _api$mention$sharedSt;
|
|
124
|
+
_defineProperty(this, "hasAvatarSlot", false);
|
|
125
|
+
_defineProperty(this, "isDestroyed", false);
|
|
104
126
|
const {
|
|
105
127
|
options,
|
|
106
128
|
api,
|
|
107
129
|
portalProviderAPI,
|
|
108
130
|
editorView
|
|
109
131
|
} = config;
|
|
132
|
+
this.hasAvatarSlot = Boolean(options === null || options === void 0 ? void 0 : options.mentionNodeDataProvider) && node.attrs.userType !== 'SPECIAL' && !genericMentionIds.includes(node.attrs.id) && isExperimentEnabled('platform_editor_mention_node_avatar');
|
|
110
133
|
const {
|
|
111
134
|
dom,
|
|
112
135
|
contentDOM
|
|
113
|
-
} = DOMSerializer.renderSpec(document, toDOM(node));
|
|
136
|
+
} = DOMSerializer.renderSpec(document, toDOM(node, this.hasAvatarSlot));
|
|
114
137
|
this.dom = dom;
|
|
115
138
|
this.contentDOM = contentDOM;
|
|
116
139
|
this.config = config;
|
|
117
140
|
this.node = node;
|
|
118
141
|
this.domElement = dom instanceof HTMLElement ? dom : undefined;
|
|
119
142
|
this.mentionPrimitiveElement = this.domElement ? (_this$domElement$quer = this.domElement.querySelector(`.${primitiveClassName}`)) !== null && _this$domElement$quer !== void 0 ? _this$domElement$quer : undefined : undefined;
|
|
143
|
+
this.mentionTextElement = this.mentionPrimitiveElement;
|
|
144
|
+
if (this.hasAvatarSlot) {
|
|
145
|
+
var _this$domElement$quer2, _this$domElement, _this$domElement2;
|
|
146
|
+
this.mentionTextElement = (_this$domElement$quer2 = (_this$domElement = this.domElement) === null || _this$domElement === void 0 ? void 0 : _this$domElement.querySelector(`.${mentionTextClassName}`)) !== null && _this$domElement$quer2 !== void 0 ? _this$domElement$quer2 : this.mentionPrimitiveElement;
|
|
147
|
+
const avatarContainer = (_this$domElement2 = this.domElement) === null || _this$domElement2 === void 0 ? void 0 : _this$domElement2.querySelector(`.${avatarContainerClassName}`);
|
|
148
|
+
if (avatarContainer) {
|
|
149
|
+
this.mentionAvatar = mentionAvatarRenderer({
|
|
150
|
+
container: avatarContainer
|
|
151
|
+
});
|
|
152
|
+
this.resolveMentionAvatar(options === null || options === void 0 ? void 0 : options.mentionNodeDataProvider);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
120
155
|
const {
|
|
121
156
|
mentionProvider
|
|
122
157
|
} = (_api$mention$sharedSt = api === null || api === void 0 ? void 0 : api.mention.sharedState.currentState()) !== null && _api$mention$sharedSt !== void 0 ? _api$mention$sharedSt : {};
|
|
@@ -242,9 +277,36 @@ export class MentionNodeView {
|
|
|
242
277
|
setTextContent(name, isAgentMentionsEnabled) {
|
|
243
278
|
// Also overwrite when text is a raw AAID so the resolved name takes precedence.
|
|
244
279
|
const textIsAaid = isAgentAaidText(this.node, isAgentMentionsEnabled);
|
|
245
|
-
if (name && (!this.node.attrs.text || textIsAaid) && this.
|
|
246
|
-
this.
|
|
280
|
+
if (name && (!this.node.attrs.text || textIsAaid) && this.mentionTextElement) {
|
|
281
|
+
this.setVisibleText(name);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
setVisibleText(text) {
|
|
285
|
+
if (!this.mentionTextElement) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
this.mentionTextElement.textContent = this.hasAvatarSlot && text.startsWith('@') && text !== unknownMentionText ? text.slice(1) : text;
|
|
289
|
+
}
|
|
290
|
+
resolveMentionAvatar(mentionNodeDataProvider) {
|
|
291
|
+
if (!this.hasAvatarSlot || !mentionNodeDataProvider || this.node.attrs.userType === 'SPECIAL') {
|
|
292
|
+
return;
|
|
247
293
|
}
|
|
294
|
+
const mention = {
|
|
295
|
+
id: this.node.attrs.id,
|
|
296
|
+
userType: this.node.attrs.userType
|
|
297
|
+
};
|
|
298
|
+
const applyData = data => {
|
|
299
|
+
var _this$mentionAvatar;
|
|
300
|
+
if (this.isDestroyed || !(data !== null && data !== void 0 && data.avatarUrl)) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
(_this$mentionAvatar = this.mentionAvatar) === null || _this$mentionAvatar === void 0 ? void 0 : _this$mentionAvatar.render(data);
|
|
304
|
+
};
|
|
305
|
+
mentionNodeDataProvider.getMentionData(mention, payload => {
|
|
306
|
+
if (payload.data) {
|
|
307
|
+
applyData(payload.data);
|
|
308
|
+
}
|
|
309
|
+
});
|
|
248
310
|
}
|
|
249
311
|
shouldHighlightMention(mentionProvider) {
|
|
250
312
|
var _this$config$options;
|
|
@@ -316,7 +378,8 @@ export class MentionNodeView {
|
|
|
316
378
|
return true;
|
|
317
379
|
}
|
|
318
380
|
destroy() {
|
|
319
|
-
var _this$cleanup, _this$destroyProfileC, _this$disabledTooltip, _this$unsubscribeFrom2;
|
|
381
|
+
var _this$cleanup, _this$destroyProfileC, _this$mentionAvatar2, _this$disabledTooltip, _this$unsubscribeFrom2;
|
|
382
|
+
this.isDestroyed = true;
|
|
320
383
|
// Surface the destruction to the provider before tearing down so the
|
|
321
384
|
// chat layer can react (e.g. drop the agent id from `selectedAgentIds`).
|
|
322
385
|
// This is the lowest-level deletion signal — fires for backspace,
|
|
@@ -332,6 +395,8 @@ export class MentionNodeView {
|
|
|
332
395
|
}
|
|
333
396
|
(_this$cleanup = this.cleanup) === null || _this$cleanup === void 0 ? void 0 : _this$cleanup.call(this);
|
|
334
397
|
(_this$destroyProfileC = this.destroyProfileCard) === null || _this$destroyProfileC === void 0 ? void 0 : _this$destroyProfileC.call(this);
|
|
398
|
+
(_this$mentionAvatar2 = this.mentionAvatar) === null || _this$mentionAvatar2 === void 0 ? void 0 : _this$mentionAvatar2.destroy();
|
|
399
|
+
this.mentionAvatar = undefined;
|
|
335
400
|
(_this$disabledTooltip = this.disabledTooltip) === null || _this$disabledTooltip === void 0 ? void 0 : _this$disabledTooltip.destroy();
|
|
336
401
|
this.disabledTooltip = undefined;
|
|
337
402
|
(_this$unsubscribeFrom2 = this.unsubscribeFromDisabledStateChanges) === null || _this$unsubscribeFrom2 === void 0 ? void 0 : _this$unsubscribeFrom2.call(this);
|
|
@@ -57,14 +57,16 @@ export const profileCardRenderer = ({
|
|
|
57
57
|
const renderEditorProfileCard = () => {
|
|
58
58
|
const isReducedProfileCards = expVal('platform_editor_reduced_agent_profile_cards', 'isEnabled', false);
|
|
59
59
|
const clickedNode = isReducedProfileCards ? currentNode : node;
|
|
60
|
-
const activeMention = isReducedProfileCards ? ((
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
const
|
|
60
|
+
const activeMention = isReducedProfileCards ? ((_ref, _ref$textContent, _currentNode, _currentNode2, _currentNode2$attrs, _currentNode3, _currentNode3$attrs) => {
|
|
61
|
+
// Avatar mentions keep their resolved display name in the nested text span.
|
|
62
|
+
// Control mentions preserve the legacy attrs-first fallback.
|
|
63
|
+
const mentionTextElement = dom instanceof HTMLElement ? dom.querySelector('.editor-mention-text') : undefined;
|
|
64
|
+
const primitiveText = (_ref = mentionTextElement !== null && mentionTextElement !== void 0 ? mentionTextElement : dom instanceof HTMLElement ? dom.querySelector('.editor-mention-primitive') : undefined) === null || _ref === void 0 ? void 0 : (_ref$textContent = _ref.textContent) === null || _ref$textContent === void 0 ? void 0 : _ref$textContent.trim();
|
|
65
|
+
const resolvedText = primitiveText && !primitiveText.startsWith('@') ? `@${primitiveText}` : primitiveText;
|
|
64
66
|
return {
|
|
65
67
|
attrs: {
|
|
66
68
|
...((_currentNode = currentNode) === null || _currentNode === void 0 ? void 0 : _currentNode.attrs),
|
|
67
|
-
text: ((_currentNode2 = currentNode) === null || _currentNode2 === void 0 ? void 0 : (_currentNode2$attrs = _currentNode2.attrs) === null || _currentNode2$attrs === void 0 ? void 0 : _currentNode2$attrs.text) || primitiveText || undefined
|
|
69
|
+
text: mentionTextElement ? resolvedText || ((_currentNode2 = currentNode) === null || _currentNode2 === void 0 ? void 0 : (_currentNode2$attrs = _currentNode2.attrs) === null || _currentNode2$attrs === void 0 ? void 0 : _currentNode2$attrs.text) || undefined : ((_currentNode3 = currentNode) === null || _currentNode3 === void 0 ? void 0 : (_currentNode3$attrs = _currentNode3.attrs) === null || _currentNode3$attrs === void 0 ? void 0 : _currentNode3$attrs.text) || primitiveText || undefined
|
|
68
70
|
}
|
|
69
71
|
};
|
|
70
72
|
})() : {
|
|
@@ -51,7 +51,7 @@ const AI_STREAMING_TRANSFORMATION_META_KEY = 'isAIStreamingTransformation';
|
|
|
51
51
|
const AGENT_MENTION_INACTIVITY_MS = 3000;
|
|
52
52
|
const MAX_PENDING_TYPED_AGENT_MENTION_FOCUS_DEFERS = 20;
|
|
53
53
|
const PACKAGE_NAME = "@atlaskit/editor-plugin-mentions";
|
|
54
|
-
const PACKAGE_VERSION = "16.1.
|
|
54
|
+
const PACKAGE_VERSION = "16.1.5";
|
|
55
55
|
const setProvider = provider => (state, dispatch) => {
|
|
56
56
|
if (dispatch) {
|
|
57
57
|
dispatch(state.tr.setMeta(mentionPluginKey, {
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import { NodeDataProvider } from '@atlaskit/node-data-provider/node-data-provider';
|
|
3
|
+
const getAppType = userType => userType === 'APP' || userType === 'AGENT' ? 'agent' : 'user';
|
|
4
|
+
const enrichMentionNodeData = (mention, data) => {
|
|
5
|
+
var _data$appType;
|
|
6
|
+
return {
|
|
7
|
+
...data,
|
|
8
|
+
appType: (_data$appType = data === null || data === void 0 ? void 0 : data.appType) !== null && _data$appType !== void 0 ? _data$appType : getAppType(mention.userType)
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
const toMentionNode = ({
|
|
12
|
+
id,
|
|
13
|
+
userType
|
|
14
|
+
}) => ({
|
|
15
|
+
attrs: {
|
|
16
|
+
id,
|
|
17
|
+
// NodeDataProvider uses an ADF node internally, so normalize the runtime-only value.
|
|
18
|
+
userType: userType === 'AGENT' ? 'APP' : userType
|
|
19
|
+
},
|
|
20
|
+
type: 'mention'
|
|
21
|
+
});
|
|
22
|
+
const isMentionNode = node => node.type === 'mention' && node.attrs !== undefined && 'id' in node.attrs && typeof node.attrs.id === 'string';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Page-scoped data provider for mention avatars.
|
|
26
|
+
*
|
|
27
|
+
* Products inject their resolution strategy so this package remains independent
|
|
28
|
+
* of Jira and Confluence APIs. Reuse the instance for the lifetime of a renderer
|
|
29
|
+
* or editor rather than creating one for every mention.
|
|
30
|
+
*
|
|
31
|
+
* Deterministic products can supply `resolveMentionNodeData` to return an
|
|
32
|
+
* SSR-safe avatar URL synchronously. It must return the same value on the server
|
|
33
|
+
* and the client's first render to preserve hydration parity.
|
|
34
|
+
*
|
|
35
|
+
* Network-backed products supply `fetchMentionNodeData`. Calls made in the same
|
|
36
|
+
* microtask are combined and account IDs are deduplicated before the fetcher is
|
|
37
|
+
* invoked. A Relay adapter can query `users(accountIds:)`, then map each user's
|
|
38
|
+
* `picture` and app type to a `MentionNodeData` record keyed by account ID. The
|
|
39
|
+
* resolved result is cached by the underlying `NodeDataProvider`.
|
|
40
|
+
*/
|
|
41
|
+
export class MentionNodeDataProvider extends NodeDataProvider {
|
|
42
|
+
constructor(fetchMentionNodeData, resolveMentionNodeData = undefined) {
|
|
43
|
+
super();
|
|
44
|
+
_defineProperty(this, "name", 'mentionNodeDataProvider');
|
|
45
|
+
_defineProperty(this, "queuedFetches", []);
|
|
46
|
+
_defineProperty(this, "isFlushScheduled", false);
|
|
47
|
+
this.fetchMentionNodeData = fetchMentionNodeData;
|
|
48
|
+
this.resolveMentionNodeData = resolveMentionNodeData;
|
|
49
|
+
}
|
|
50
|
+
isNodeSupported(node) {
|
|
51
|
+
return isMentionNode(node) && node.attrs.userType !== 'SPECIAL';
|
|
52
|
+
}
|
|
53
|
+
nodeDataKey(node) {
|
|
54
|
+
return node.attrs.id;
|
|
55
|
+
}
|
|
56
|
+
async fetchNodesData(nodes) {
|
|
57
|
+
const accountIds = Array.from(new Set(nodes.map(({
|
|
58
|
+
attrs
|
|
59
|
+
}) => attrs.id)));
|
|
60
|
+
const dataByAccountId = await this.enqueueFetch(accountIds);
|
|
61
|
+
return nodes.map(({
|
|
62
|
+
attrs
|
|
63
|
+
}) => enrichMentionNodeData(attrs, dataByAccountId[attrs.id]));
|
|
64
|
+
}
|
|
65
|
+
getMentionData(mention, callback) {
|
|
66
|
+
var _this$resolveMentionN;
|
|
67
|
+
const resolvedData = (_this$resolveMentionN = this.resolveMentionNodeData) === null || _this$resolveMentionN === void 0 ? void 0 : _this$resolveMentionN.call(this, mention);
|
|
68
|
+
if (resolvedData) {
|
|
69
|
+
callback({
|
|
70
|
+
data: enrichMentionNodeData(mention, resolvedData)
|
|
71
|
+
});
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
this.getData(toMentionNode(mention), callback);
|
|
75
|
+
}
|
|
76
|
+
getMentionDataFromCache(mention) {
|
|
77
|
+
var _this$resolveMentionN2, _this$getNodeDataFrom;
|
|
78
|
+
const resolvedData = (_this$resolveMentionN2 = this.resolveMentionNodeData) === null || _this$resolveMentionN2 === void 0 ? void 0 : _this$resolveMentionN2.call(this, mention);
|
|
79
|
+
if (resolvedData) {
|
|
80
|
+
return enrichMentionNodeData(mention, resolvedData);
|
|
81
|
+
}
|
|
82
|
+
const data = (_this$getNodeDataFrom = this.getNodeDataFromCache(toMentionNode(mention))) === null || _this$getNodeDataFrom === void 0 ? void 0 : _this$getNodeDataFrom.data;
|
|
83
|
+
return data ? enrichMentionNodeData(mention, data) : undefined;
|
|
84
|
+
}
|
|
85
|
+
enqueueFetch(accountIds) {
|
|
86
|
+
const result = new Promise((resolve, reject) => {
|
|
87
|
+
this.queuedFetches.push({
|
|
88
|
+
accountIds,
|
|
89
|
+
reject,
|
|
90
|
+
resolve
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
if (!this.isFlushScheduled) {
|
|
94
|
+
this.isFlushScheduled = true;
|
|
95
|
+
void Promise.resolve().then(() => this.flushFetches());
|
|
96
|
+
}
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
async flushFetches() {
|
|
100
|
+
this.isFlushScheduled = false;
|
|
101
|
+
const queuedFetches = this.queuedFetches;
|
|
102
|
+
this.queuedFetches = [];
|
|
103
|
+
const accountIds = Array.from(new Set(queuedFetches.flatMap(request => request.accountIds)));
|
|
104
|
+
try {
|
|
105
|
+
const data = await this.fetchMentionNodeData(accountIds);
|
|
106
|
+
queuedFetches.forEach(({
|
|
107
|
+
resolve
|
|
108
|
+
}) => resolve(data));
|
|
109
|
+
} catch (error) {
|
|
110
|
+
queuedFetches.forEach(({
|
|
111
|
+
reject
|
|
112
|
+
}) => reject(error));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { bind } from 'bind-event-listener';
|
|
2
|
+
var avatarImageClassName = 'editor-mention-avatar-image';
|
|
3
|
+
var agentAvatarClassName = 'editor-mention-avatar-agent';
|
|
4
|
+
var fallbackAvatarClassName = 'editor-mention-avatar-fallback';
|
|
5
|
+
var avatarSize = 16;
|
|
6
|
+
/**
|
|
7
|
+
* Populates the fixed-size avatar slot owned by the vanilla ProseMirror NodeView.
|
|
8
|
+
* No React root or editor portal is created for individual mention nodes.
|
|
9
|
+
*/
|
|
10
|
+
export var mentionAvatarRenderer = function mentionAvatarRenderer(_ref) {
|
|
11
|
+
var container = _ref.container;
|
|
12
|
+
var image;
|
|
13
|
+
var fallback;
|
|
14
|
+
var unbindImageError;
|
|
15
|
+
var removeImage = function removeImage() {
|
|
16
|
+
var _unbindImageError, _image;
|
|
17
|
+
(_unbindImageError = unbindImageError) === null || _unbindImageError === void 0 || _unbindImageError();
|
|
18
|
+
unbindImageError = undefined;
|
|
19
|
+
(_image = image) === null || _image === void 0 || _image.remove();
|
|
20
|
+
image = undefined;
|
|
21
|
+
};
|
|
22
|
+
var removeFallback = function removeFallback() {
|
|
23
|
+
var _fallback;
|
|
24
|
+
(_fallback = fallback) === null || _fallback === void 0 || _fallback.remove();
|
|
25
|
+
fallback = undefined;
|
|
26
|
+
container.classList.remove(fallbackAvatarClassName);
|
|
27
|
+
};
|
|
28
|
+
var showFallback = function showFallback() {
|
|
29
|
+
removeImage();
|
|
30
|
+
removeFallback();
|
|
31
|
+
fallback = container.ownerDocument.createTextNode('@');
|
|
32
|
+
container.appendChild(fallback);
|
|
33
|
+
container.classList.add(fallbackAvatarClassName);
|
|
34
|
+
};
|
|
35
|
+
var destroy = function destroy() {
|
|
36
|
+
removeImage();
|
|
37
|
+
removeFallback();
|
|
38
|
+
};
|
|
39
|
+
return {
|
|
40
|
+
destroy: destroy,
|
|
41
|
+
render: function render(_ref2) {
|
|
42
|
+
var appType = _ref2.appType,
|
|
43
|
+
avatarUrl = _ref2.avatarUrl,
|
|
44
|
+
isAvatarImagePreShaped = _ref2.isAvatarImagePreShaped;
|
|
45
|
+
container.classList.toggle(agentAvatarClassName, !isAvatarImagePreShaped && appType === 'agent');
|
|
46
|
+
removeFallback();
|
|
47
|
+
if (!avatarUrl) {
|
|
48
|
+
removeImage();
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (!image) {
|
|
52
|
+
image = container.ownerDocument.createElement('img');
|
|
53
|
+
image.alt = '';
|
|
54
|
+
image.className = avatarImageClassName;
|
|
55
|
+
image.decoding = 'async';
|
|
56
|
+
image.draggable = false;
|
|
57
|
+
image.height = avatarSize;
|
|
58
|
+
image.loading = 'lazy';
|
|
59
|
+
image.width = avatarSize;
|
|
60
|
+
unbindImageError = bind(image, {
|
|
61
|
+
type: 'error',
|
|
62
|
+
listener: showFallback
|
|
63
|
+
});
|
|
64
|
+
container.appendChild(image);
|
|
65
|
+
}
|
|
66
|
+
image.src = avatarUrl;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
};
|
|
@@ -8,14 +8,22 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
8
8
|
import { getBrowserInfo } from '@atlaskit/editor-common/browser';
|
|
9
9
|
import { ZERO_WIDTH_SPACE } from '@atlaskit/editor-common/whitespace';
|
|
10
10
|
import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
|
|
11
|
+
import { UNKNOWN_USER_ID } from '@atlaskit/mention/constants';
|
|
11
12
|
import { isResolvingMentionProvider, MentionNameStatus } from '@atlaskit/mention/resource';
|
|
12
13
|
import { isRestricted } from '@atlaskit/mention/types';
|
|
14
|
+
import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
|
|
13
15
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
14
16
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
15
17
|
import { expVal } from '@atlaskit/tmp-editor-statsig/expVal';
|
|
16
18
|
import { disabledTooltipRenderer } from './disabledTooltipRenderer';
|
|
19
|
+
import { mentionAvatarRenderer } from './mentionAvatarRenderer';
|
|
17
20
|
import { profileCardRenderer } from './profileCardRenderer';
|
|
18
21
|
var primitiveClassName = 'editor-mention-primitive';
|
|
22
|
+
var primitiveWithAvatarClassName = 'editor-mention-primitive-with-avatar';
|
|
23
|
+
var avatarContainerClassName = 'editor-mention-avatar';
|
|
24
|
+
var mentionTextClassName = 'editor-mention-text';
|
|
25
|
+
var genericMentionIds = ['HipChat', 'all', 'here'];
|
|
26
|
+
var unknownMentionText = "@".concat(UNKNOWN_USER_ID);
|
|
19
27
|
// eslint-disable-next-line require-unicode-regexp
|
|
20
28
|
var AT_PREFIX_REGEX = /^@/;
|
|
21
29
|
var getAccessibilityLabelFromName = function getAccessibilityLabelFromName(name) {
|
|
@@ -46,7 +54,7 @@ var isAgentMentionsExperimentEnabled = function isAgentMentionsExperimentEnabled
|
|
|
46
54
|
var isAgentAaidText = function isAgentAaidText(node, isAgentMentionsEnabled) {
|
|
47
55
|
return isAgentMentionsEnabled && (node.attrs.userType === 'APP' || node.attrs.userType === 'AGENT') && !!node.attrs.text && isMentionTextAnAaid(node.attrs.text);
|
|
48
56
|
};
|
|
49
|
-
var toDOM = function toDOM(node) {
|
|
57
|
+
var toDOM = function toDOM(node, hasAvatarSlot) {
|
|
50
58
|
// packages/elements/mention/src/components/Mention/index.tsx
|
|
51
59
|
var mentionAttrs = {
|
|
52
60
|
contenteditable: 'false',
|
|
@@ -71,14 +79,25 @@ var toDOM = function toDOM(node) {
|
|
|
71
79
|
}
|
|
72
80
|
var browser = getBrowserInfo();
|
|
73
81
|
var hasAgentAaidText = isAgentAaidText(node, isAgentMentionsEnabled);
|
|
82
|
+
var mentionText = node.attrs.text && !hasAgentAaidText ? node.attrs.text : '@…';
|
|
83
|
+
var visibleMentionText = hasAvatarSlot && mentionText.startsWith('@') ? mentionText.slice(1) : mentionText;
|
|
84
|
+
var mentionContentSpec = hasAvatarSlot ? ['span', {
|
|
85
|
+
spellcheck: 'false',
|
|
86
|
+
class: "".concat(primitiveClassName, " ").concat(primitiveWithAvatarClassName)
|
|
87
|
+
}, ['span', {
|
|
88
|
+
class: avatarContainerClassName,
|
|
89
|
+
'aria-hidden': 'true'
|
|
90
|
+
}], ['span', {
|
|
91
|
+
class: mentionTextClassName
|
|
92
|
+
}, visibleMentionText]] : ['span', {
|
|
93
|
+
spellcheck: 'false',
|
|
94
|
+
class: primitiveClassName
|
|
95
|
+
}, mentionText];
|
|
74
96
|
return ['span', mentionAttrs, ['span', {
|
|
75
97
|
class: 'zeroWidthSpaceContainer'
|
|
76
98
|
}, ['span', {
|
|
77
99
|
class: 'inlineNodeViewAddZeroWidthSpace'
|
|
78
|
-
}, ZERO_WIDTH_SPACE]], ['span', {
|
|
79
|
-
spellcheck: 'false',
|
|
80
|
-
class: primitiveClassName
|
|
81
|
-
}, node.attrs.text && !hasAgentAaidText ? node.attrs.text : '@…'], browser.android ? ['span', {
|
|
100
|
+
}, ZERO_WIDTH_SPACE]], mentionContentSpec, browser.android ? ['span', {
|
|
82
101
|
class: 'zeroWidthSpaceContainer',
|
|
83
102
|
contenteditable: 'false'
|
|
84
103
|
}, ['span', {
|
|
@@ -88,7 +107,7 @@ var toDOM = function toDOM(node) {
|
|
|
88
107
|
}, ZERO_WIDTH_SPACE]];
|
|
89
108
|
};
|
|
90
109
|
var processName = function processName(name) {
|
|
91
|
-
return name.status === MentionNameStatus.OK ? "@".concat(name.name || '') :
|
|
110
|
+
return name.status === MentionNameStatus.OK ? "@".concat(name.name || '') : unknownMentionText;
|
|
92
111
|
};
|
|
93
112
|
var handleProviderName = /*#__PURE__*/function () {
|
|
94
113
|
var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(mentionProvider, node, isAgentMentionsEnabled) {
|
|
@@ -136,11 +155,14 @@ export var MentionNodeView = /*#__PURE__*/function () {
|
|
|
136
155
|
_api$mention$sharedSt,
|
|
137
156
|
_this = this;
|
|
138
157
|
_classCallCheck(this, MentionNodeView);
|
|
158
|
+
_defineProperty(this, "hasAvatarSlot", false);
|
|
159
|
+
_defineProperty(this, "isDestroyed", false);
|
|
139
160
|
var options = config.options,
|
|
140
161
|
api = config.api,
|
|
141
162
|
portalProviderAPI = config.portalProviderAPI,
|
|
142
163
|
editorView = config.editorView;
|
|
143
|
-
|
|
164
|
+
this.hasAvatarSlot = Boolean(options === null || options === void 0 ? void 0 : options.mentionNodeDataProvider) && node.attrs.userType !== 'SPECIAL' && !genericMentionIds.includes(node.attrs.id) && isExperimentEnabled('platform_editor_mention_node_avatar');
|
|
165
|
+
var _DOMSerializer$render = DOMSerializer.renderSpec(document, toDOM(node, this.hasAvatarSlot)),
|
|
144
166
|
dom = _DOMSerializer$render.dom,
|
|
145
167
|
contentDOM = _DOMSerializer$render.contentDOM;
|
|
146
168
|
this.dom = dom;
|
|
@@ -149,6 +171,18 @@ export var MentionNodeView = /*#__PURE__*/function () {
|
|
|
149
171
|
this.node = node;
|
|
150
172
|
this.domElement = dom instanceof HTMLElement ? dom : undefined;
|
|
151
173
|
this.mentionPrimitiveElement = this.domElement ? (_this$domElement$quer = this.domElement.querySelector(".".concat(primitiveClassName))) !== null && _this$domElement$quer !== void 0 ? _this$domElement$quer : undefined : undefined;
|
|
174
|
+
this.mentionTextElement = this.mentionPrimitiveElement;
|
|
175
|
+
if (this.hasAvatarSlot) {
|
|
176
|
+
var _this$domElement$quer2, _this$domElement, _this$domElement2;
|
|
177
|
+
this.mentionTextElement = (_this$domElement$quer2 = (_this$domElement = this.domElement) === null || _this$domElement === void 0 ? void 0 : _this$domElement.querySelector(".".concat(mentionTextClassName))) !== null && _this$domElement$quer2 !== void 0 ? _this$domElement$quer2 : this.mentionPrimitiveElement;
|
|
178
|
+
var avatarContainer = (_this$domElement2 = this.domElement) === null || _this$domElement2 === void 0 ? void 0 : _this$domElement2.querySelector(".".concat(avatarContainerClassName));
|
|
179
|
+
if (avatarContainer) {
|
|
180
|
+
this.mentionAvatar = mentionAvatarRenderer({
|
|
181
|
+
container: avatarContainer
|
|
182
|
+
});
|
|
183
|
+
this.resolveMentionAvatar(options === null || options === void 0 ? void 0 : options.mentionNodeDataProvider);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
152
186
|
var _ref2 = (_api$mention$sharedSt = api === null || api === void 0 ? void 0 : api.mention.sharedState.currentState()) !== null && _api$mention$sharedSt !== void 0 ? _api$mention$sharedSt : {},
|
|
153
187
|
mentionProvider = _ref2.mentionProvider;
|
|
154
188
|
this.updateState(mentionProvider);
|
|
@@ -280,9 +314,41 @@ export var MentionNodeView = /*#__PURE__*/function () {
|
|
|
280
314
|
value: function setTextContent(name, isAgentMentionsEnabled) {
|
|
281
315
|
// Also overwrite when text is a raw AAID so the resolved name takes precedence.
|
|
282
316
|
var textIsAaid = isAgentAaidText(this.node, isAgentMentionsEnabled);
|
|
283
|
-
if (name && (!this.node.attrs.text || textIsAaid) && this.
|
|
284
|
-
this.
|
|
317
|
+
if (name && (!this.node.attrs.text || textIsAaid) && this.mentionTextElement) {
|
|
318
|
+
this.setVisibleText(name);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}, {
|
|
322
|
+
key: "setVisibleText",
|
|
323
|
+
value: function setVisibleText(text) {
|
|
324
|
+
if (!this.mentionTextElement) {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
this.mentionTextElement.textContent = this.hasAvatarSlot && text.startsWith('@') && text !== unknownMentionText ? text.slice(1) : text;
|
|
328
|
+
}
|
|
329
|
+
}, {
|
|
330
|
+
key: "resolveMentionAvatar",
|
|
331
|
+
value: function resolveMentionAvatar(mentionNodeDataProvider) {
|
|
332
|
+
var _this3 = this;
|
|
333
|
+
if (!this.hasAvatarSlot || !mentionNodeDataProvider || this.node.attrs.userType === 'SPECIAL') {
|
|
334
|
+
return;
|
|
285
335
|
}
|
|
336
|
+
var mention = {
|
|
337
|
+
id: this.node.attrs.id,
|
|
338
|
+
userType: this.node.attrs.userType
|
|
339
|
+
};
|
|
340
|
+
var applyData = function applyData(data) {
|
|
341
|
+
var _this3$mentionAvatar;
|
|
342
|
+
if (_this3.isDestroyed || !(data !== null && data !== void 0 && data.avatarUrl)) {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
(_this3$mentionAvatar = _this3.mentionAvatar) === null || _this3$mentionAvatar === void 0 || _this3$mentionAvatar.render(data);
|
|
346
|
+
};
|
|
347
|
+
mentionNodeDataProvider.getMentionData(mention, function (payload) {
|
|
348
|
+
if (payload.data) {
|
|
349
|
+
applyData(payload.data);
|
|
350
|
+
}
|
|
351
|
+
});
|
|
286
352
|
}
|
|
287
353
|
}, {
|
|
288
354
|
key: "shouldHighlightMention",
|
|
@@ -379,7 +445,8 @@ export var MentionNodeView = /*#__PURE__*/function () {
|
|
|
379
445
|
}, {
|
|
380
446
|
key: "destroy",
|
|
381
447
|
value: function destroy() {
|
|
382
|
-
var _this$cleanup, _this$destroyProfileC, _this$disabledTooltip, _this$unsubscribeFrom2;
|
|
448
|
+
var _this$cleanup, _this$destroyProfileC, _this$mentionAvatar, _this$disabledTooltip, _this$unsubscribeFrom2;
|
|
449
|
+
this.isDestroyed = true;
|
|
383
450
|
// Surface the destruction to the provider before tearing down so the
|
|
384
451
|
// chat layer can react (e.g. drop the agent id from `selectedAgentIds`).
|
|
385
452
|
// This is the lowest-level deletion signal — fires for backspace,
|
|
@@ -395,6 +462,8 @@ export var MentionNodeView = /*#__PURE__*/function () {
|
|
|
395
462
|
}
|
|
396
463
|
(_this$cleanup = this.cleanup) === null || _this$cleanup === void 0 || _this$cleanup.call(this);
|
|
397
464
|
(_this$destroyProfileC = this.destroyProfileCard) === null || _this$destroyProfileC === void 0 || _this$destroyProfileC.call(this);
|
|
465
|
+
(_this$mentionAvatar = this.mentionAvatar) === null || _this$mentionAvatar === void 0 || _this$mentionAvatar.destroy();
|
|
466
|
+
this.mentionAvatar = undefined;
|
|
398
467
|
(_this$disabledTooltip = this.disabledTooltip) === null || _this$disabledTooltip === void 0 || _this$disabledTooltip.destroy();
|
|
399
468
|
this.disabledTooltip = undefined;
|
|
400
469
|
(_this$unsubscribeFrom2 = this.unsubscribeFromDisabledStateChanges) === null || _this$unsubscribeFrom2 === void 0 || _this$unsubscribeFrom2.call(this);
|
|
@@ -60,13 +60,15 @@ export var profileCardRenderer = function profileCardRenderer(_ref) {
|
|
|
60
60
|
var renderEditorProfileCard = function renderEditorProfileCard() {
|
|
61
61
|
var isReducedProfileCards = expVal('platform_editor_reduced_agent_profile_cards', 'isEnabled', false);
|
|
62
62
|
var clickedNode = isReducedProfileCards ? currentNode : node;
|
|
63
|
-
var activeMention = isReducedProfileCards ? function (
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
var
|
|
63
|
+
var activeMention = isReducedProfileCards ? function (_ref3, _currentNode, _currentNode2, _currentNode3) {
|
|
64
|
+
// Avatar mentions keep their resolved display name in the nested text span.
|
|
65
|
+
// Control mentions preserve the legacy attrs-first fallback.
|
|
66
|
+
var mentionTextElement = dom instanceof HTMLElement ? dom.querySelector('.editor-mention-text') : undefined;
|
|
67
|
+
var primitiveText = (_ref3 = mentionTextElement !== null && mentionTextElement !== void 0 ? mentionTextElement : dom instanceof HTMLElement ? dom.querySelector('.editor-mention-primitive') : undefined) === null || _ref3 === void 0 || (_ref3 = _ref3.textContent) === null || _ref3 === void 0 ? void 0 : _ref3.trim();
|
|
68
|
+
var resolvedText = primitiveText && !primitiveText.startsWith('@') ? "@".concat(primitiveText) : primitiveText;
|
|
67
69
|
return {
|
|
68
70
|
attrs: _objectSpread(_objectSpread({}, (_currentNode = currentNode) === null || _currentNode === void 0 ? void 0 : _currentNode.attrs), {}, {
|
|
69
|
-
text: ((_currentNode2 = currentNode) === null || _currentNode2 === void 0 || (_currentNode2 = _currentNode2.attrs) === null || _currentNode2 === void 0 ? void 0 : _currentNode2.text) || primitiveText || undefined
|
|
71
|
+
text: mentionTextElement ? resolvedText || ((_currentNode2 = currentNode) === null || _currentNode2 === void 0 || (_currentNode2 = _currentNode2.attrs) === null || _currentNode2 === void 0 ? void 0 : _currentNode2.text) || undefined : ((_currentNode3 = currentNode) === null || _currentNode3 === void 0 || (_currentNode3 = _currentNode3.attrs) === null || _currentNode3 === void 0 ? void 0 : _currentNode3.text) || primitiveText || undefined
|
|
70
72
|
})
|
|
71
73
|
};
|
|
72
74
|
}() : {
|
|
@@ -102,10 +104,10 @@ export var profileCardRenderer = function profileCardRenderer(_ref) {
|
|
|
102
104
|
});
|
|
103
105
|
});
|
|
104
106
|
};
|
|
105
|
-
var renderUserProfileCard = function renderUserProfileCard(
|
|
106
|
-
var userId =
|
|
107
|
-
cloudId =
|
|
108
|
-
renderUserMentionCard =
|
|
107
|
+
var renderUserProfileCard = function renderUserProfileCard(_ref4) {
|
|
108
|
+
var userId = _ref4.userId,
|
|
109
|
+
cloudId = _ref4.cloudId,
|
|
110
|
+
renderUserMentionCard = _ref4.renderUserMentionCard;
|
|
109
111
|
if (!renderUserMentionCard) {
|
|
110
112
|
return false;
|
|
111
113
|
}
|
|
@@ -58,7 +58,7 @@ var AI_STREAMING_TRANSFORMATION_META_KEY = 'isAIStreamingTransformation';
|
|
|
58
58
|
var AGENT_MENTION_INACTIVITY_MS = 3000;
|
|
59
59
|
var MAX_PENDING_TYPED_AGENT_MENTION_FOCUS_DEFERS = 20;
|
|
60
60
|
var PACKAGE_NAME = "@atlaskit/editor-plugin-mentions";
|
|
61
|
-
var PACKAGE_VERSION = "16.1.
|
|
61
|
+
var PACKAGE_VERSION = "16.1.5";
|
|
62
62
|
var setProvider = function setProvider(provider) {
|
|
63
63
|
return function (state, dispatch) {
|
|
64
64
|
if (dispatch) {
|