@atlaskit/editor-plugin-mentions
Version:
Mentions plugin for @atlaskit/editor-core
187 lines • 8.37 kB
JavaScript
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import { getBrowserInfo } from '@atlaskit/editor-common/browser';
import { ZERO_WIDTH_SPACE } from '@atlaskit/editor-common/whitespace';
import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
import { isResolvingMentionProvider, MentionNameStatus } from '@atlaskit/mention/resource';
import { isRestricted } from '@atlaskit/mention/types';
import { fg } from '@atlaskit/platform-feature-flags';
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
import { profileCardRenderer } from './profileCardRenderer';
const primitiveClassName = 'editor-mention-primitive';
// @ts-ignore - TS1501 TypeScript 5.9.2 upgrade
const getAccessibilityLabelFromName = name => name.replace(/^@/u, '');
const toDOM = node => {
// packages/elements/mention/src/components/Mention/index.tsx
let mentionAttrs = {
contenteditable: 'false',
'data-access-level': node.attrs.accessLevel,
'data-mention-id': node.attrs.id,
'data-prosemirror-content-type': 'node',
'data-prosemirror-node-inline': 'true',
'data-prosemirror-node-name': 'mention',
'data-prosemirror-node-view-type': 'vanilla',
class: 'mentionView-content-wrap inlineNodeView'
};
if (fg('platform_editor_adf_with_localid')) {
mentionAttrs = {
...mentionAttrs,
'data-local-id': node.attrs.localId
};
}
const browser = getBrowserInfo();
return ['span', mentionAttrs, ['span', {
class: 'zeroWidthSpaceContainer'
}, ['span', {
class: 'inlineNodeViewAddZeroWidthSpace'
}, ZERO_WIDTH_SPACE]], ['span', {
spellcheck: 'false',
class: primitiveClassName
}, node.attrs.text || '@…'], browser.android ? ['span', {
class: 'zeroWidthSpaceContainer',
contenteditable: 'false'
}, ['span', {
class: 'inlineNodeViewAddZeroWidthSpace'
}, ZERO_WIDTH_SPACE]] : ['span', {
class: 'inlineNodeViewAddZeroWidthSpace'
}, ZERO_WIDTH_SPACE]];
};
const processName = name => {
return name.status === MentionNameStatus.OK ? `@${name.name || ''}` : `|unknown|_`;
};
const handleProviderName = async (mentionProvider, node) => {
if (isResolvingMentionProvider(mentionProvider) && node.attrs.id && !node.attrs.text) {
const nameDetail = mentionProvider === null || mentionProvider === void 0 ? void 0 : mentionProvider.resolveMentionName(node.attrs.id);
const resolvedNameDetail = await nameDetail;
return processName(resolvedNameDetail);
}
};
const getNewState = (isHighlighted, isRestricted) => {
if (isHighlighted) {
return 'self';
}
if (isRestricted) {
return 'restricted';
}
return 'default';
};
export class MentionNodeView {
constructor(node, config) {
var _this$domElement$quer, _api$mention$sharedSt;
_defineProperty(this, "state", 'default');
const {
options,
api,
portalProviderAPI
} = config;
const {
dom,
contentDOM
} = DOMSerializer.renderSpec(document, toDOM(node));
this.dom = dom;
this.contentDOM = contentDOM;
this.config = config;
this.node = node;
this.domElement = dom instanceof HTMLElement ? dom : undefined;
this.mentionPrimitiveElement = this.domElement ? (_this$domElement$quer = this.domElement.querySelector(`.${primitiveClassName}`)) !== null && _this$domElement$quer !== void 0 ? _this$domElement$quer : undefined : undefined;
const {
mentionProvider
} = (_api$mention$sharedSt = api === null || api === void 0 ? void 0 : api.mention.sharedState.currentState()) !== null && _api$mention$sharedSt !== void 0 ? _api$mention$sharedSt : {};
this.updateState(mentionProvider);
this.cleanup = api === null || api === void 0 ? void 0 : api.mention.sharedState.onChange(({
nextSharedState
}) => {
this.updateState(nextSharedState === null || nextSharedState === void 0 ? void 0 : nextSharedState.mentionProvider);
});
const {
destroyProfileCard,
removeProfileCard
} = profileCardRenderer({
dom,
options,
portalProviderAPI,
node,
api
});
// Accessibility attributes - based on `packages/people-and-teams/profilecard/src/components/User/ProfileCardTrigger.tsx`
if (this.domElement && options !== null && options !== void 0 && options.profilecardProvider) {
if (node.attrs.text) {
this.domElement.setAttribute('aria-label', getAccessibilityLabelFromName(node.attrs.text));
}
this.domElement.setAttribute('aria-expanded', 'false');
this.domElement.setAttribute('role', 'button');
this.domElement.setAttribute('tabindex', '0');
this.domElement.setAttribute('aria-haspopup', 'dialog');
}
this.destroyProfileCard = destroyProfileCard;
this.removeProfileCard = removeProfileCard;
}
setClassList(state) {
var _this$mentionPrimitiv, _this$mentionPrimitiv2;
(_this$mentionPrimitiv = this.mentionPrimitiveElement) === null || _this$mentionPrimitiv === void 0 ? void 0 : _this$mentionPrimitiv.classList.toggle('mention-self', state === 'self');
(_this$mentionPrimitiv2 = this.mentionPrimitiveElement) === null || _this$mentionPrimitiv2 === void 0 ? void 0 : _this$mentionPrimitiv2.classList.toggle('mention-restricted', state === 'restricted');
}
setTextContent(name) {
if (name && !this.node.attrs.text && this.mentionPrimitiveElement) {
this.mentionPrimitiveElement.textContent = name;
}
}
shouldHighlightMention(mentionProvider) {
var _this$config$options;
const {
currentUserId
} = (_this$config$options = this.config.options) !== null && _this$config$options !== void 0 ? _this$config$options : {};
// Check options first (immediate), then provider (async), then default to false
if (currentUserId && this.node.attrs.id === currentUserId) {
return true;
} else {
var _mentionProvider$shou;
return (_mentionProvider$shou = mentionProvider === null || mentionProvider === void 0 ? void 0 : mentionProvider.shouldHighlightMention({
id: this.node.attrs.id
})) !== null && _mentionProvider$shou !== void 0 ? _mentionProvider$shou : false;
}
}
async updateState(mentionProvider) {
var _mentionProvider$shou2, _this$config$options2;
const isHighlighted = expValEquals('platform_editor_vc90_transition_mentions', 'isEnabled', true) ? this.shouldHighlightMention(mentionProvider) : (_mentionProvider$shou2 = mentionProvider === null || mentionProvider === void 0 ? void 0 : mentionProvider.shouldHighlightMention({
id: this.node.attrs.id
})) !== null && _mentionProvider$shou2 !== void 0 ? _mentionProvider$shou2 : false;
const newState = getNewState(isHighlighted, isRestricted(this.node.attrs.accessLevel));
if (newState !== this.state) {
this.setClassList(newState);
this.state = newState;
}
const name = await handleProviderName(mentionProvider, this.node);
this.setTextContent(name);
if (name && this.domElement && (_this$config$options2 = this.config.options) !== null && _this$config$options2 !== void 0 && _this$config$options2.profilecardProvider) {
this.domElement.setAttribute('aria-label', getAccessibilityLabelFromName(name));
}
}
nodeIsEqual(nextNode) {
var _this$config$options3;
if ((_this$config$options3 = this.config.options) !== null && _this$config$options3 !== void 0 && _this$config$options3.sanitizePrivateContent) {
// Compare nodes but ignore the text parameter as it may be sanitized
const nextNodeAttrs = {
...nextNode.attrs,
text: this.node.attrs.text
};
return this.node.hasMarkup(nextNode.type, nextNodeAttrs, nextNode.marks);
}
return this.node.sameMarkup(nextNode);
}
update(node) {
if (!this.nodeIsEqual(node)) {
return false;
}
this.node = node;
return true;
}
destroy() {
var _this$cleanup, _this$destroyProfileC;
(_this$cleanup = this.cleanup) === null || _this$cleanup === void 0 ? void 0 : _this$cleanup.call(this);
(_this$destroyProfileC = this.destroyProfileCard) === null || _this$destroyProfileC === void 0 ? void 0 : _this$destroyProfileC.call(this);
}
deselectNode() {
var _this$removeProfileCa;
(_this$removeProfileCa = this.removeProfileCard) === null || _this$removeProfileCa === void 0 ? void 0 : _this$removeProfileCa.call(this);
}
}