UNPKG

@atlaskit/editor-plugin-mentions

Version:

Mentions plugin for @atlaskit/editor-core

255 lines (254 loc) 10 kB
import React, { useEffect, useMemo } from 'react'; import { useIntl } from 'react-intl'; // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead import uuid from 'uuid'; import { INPUT_METHOD } from '@atlaskit/editor-common/analytics'; import { toolbarInsertBlockMessages as messages, mentionMessages } from '@atlaskit/editor-common/messages'; import { WithProviders } from '@atlaskit/editor-common/provider-factory'; import { IconMention } from '@atlaskit/editor-common/quick-insert'; import { isResolvingMentionProvider } from '@atlaskit/mention/resource'; import { MentionNameStatus, isPromise } from '@atlaskit/mention/types'; import { fg } from '@atlaskit/platform-feature-flags'; import { insertMention } from './editor-commands'; import { mentionNodeSpec } from './nodeviews/mentionNodeSpec'; import { mentionPluginKey } from './pm-plugins/key'; import { ACTIONS, createMentionPlugin } from './pm-plugins/main'; import { createMentionPlaceholderPlugin } from './pm-plugins/mentionPlaceholder'; import { InlineInviteRecaptchaContainer } from './ui/InlineInviteRecaptchaContainer'; import { SecondaryToolbarComponent } from './ui/SecondaryToolbarComponent'; import { createTypeAheadConfig } from './ui/type-ahead'; const processName = (name, intl) => { const unknownLabel = intl.formatMessage(mentionMessages.unknownLabel); if (name.status === MentionNameStatus.OK) { return `@${name.name || unknownLabel}`; } else { return `@${unknownLabel}`; } }; /** * We will need to clean this up once mentionProvider is * put inside mention plugin. * See: https://product-fabric.atlassian.net/browse/ED-26011 */ function Component({ mentionProvider, api }) { const mentionProviderMemo = useMemo(() => { return mentionProvider; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const intl = useIntl(); useEffect(() => { mentionProviderMemo === null || mentionProviderMemo === void 0 ? void 0 : mentionProviderMemo.then(mentionProviderSync => { var _api$base, _api$base$actions; api === null || api === void 0 ? void 0 : (_api$base = api.base) === null || _api$base === void 0 ? void 0 : (_api$base$actions = _api$base.actions) === null || _api$base$actions === void 0 ? void 0 : _api$base$actions.registerMarks(({ tr, node, pos }) => { const { doc } = tr; const { schema } = doc.type; const { mention: mentionNodeType } = schema.nodes; const { id } = node.attrs; if (node.type === mentionNodeType) { if (isResolvingMentionProvider(mentionProviderSync)) { const nameDetail = mentionProviderSync === null || mentionProviderSync === void 0 ? void 0 : mentionProviderSync.resolveMentionName(id); let newText; if (isPromise(nameDetail)) { newText = `@${intl.formatMessage(mentionMessages.unknownLabel)}`; } else { newText = processName(nameDetail, intl); } const currentPos = tr.mapping.map(pos); tr.replaceWith(currentPos, currentPos + node.nodeSize, schema.text(newText, node.marks)); } } }); }); }, [mentionProviderMemo, api, intl]); return null; } const mentionsPlugin = ({ config: options, api }) => { var _options$sanitizePriv, _options$insertDispla; // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead const sessionId = uuid(); let previousMediaProvider; const fireEvent = (payload, channel) => { var _api$analytics, _api$analytics$action; const fireAnalyticsEvent = api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : (_api$analytics$action = _api$analytics.actions) === null || _api$analytics$action === void 0 ? void 0 : _api$analytics$action.fireAnalyticsEvent; if (!fireAnalyticsEvent) { return; } if (payload.attributes && !payload.attributes.sessionId) { payload.attributes.sessionId = sessionId; } fireAnalyticsEvent(payload, channel); }; const typeAhead = createTypeAheadConfig({ sanitizePrivateContent: options === null || options === void 0 ? void 0 : options.sanitizePrivateContent, mentionInsertDisplayName: options === null || options === void 0 ? void 0 : options.insertDisplayName, HighlightComponent: options === null || options === void 0 ? void 0 : options.HighlightComponent, handleMentionsChanged: options === null || options === void 0 ? void 0 : options.handleMentionsChanged, fireEvent, api }); return { name: 'mention', nodes() { return [{ name: 'mention', node: mentionNodeSpec() }]; }, pmPlugins() { const plugins = [{ name: 'mention', plugin: pmPluginFactoryParams => createMentionPlugin({ pmPluginFactoryParams, fireEvent, options, api }) }]; if (fg('jira_invites_auto_tag_new_user_in_mentions_fg')) { plugins.push({ name: 'mentionPlaceholder', plugin: () => createMentionPlaceholderPlugin() }); } return plugins; }, contentComponent({ editorView, providerFactory }) { if (!editorView) { return null; } return /*#__PURE__*/React.createElement(WithProviders // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) , { providers: ['mentionProvider'], providerFactory: providerFactory // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) , renderNode: ({ mentionProvider }) => { return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Component, { mentionProvider: mentionProvider, api: api }), fg('jira_invites_auto_tag_new_user_in_mentions_fg') && /*#__PURE__*/React.createElement(InlineInviteRecaptchaContainer, { mentionProvider: mentionProvider, api: api })); } }); }, secondaryToolbarComponent({ editorView, disabled }) { if (!editorView) { return null; } return /*#__PURE__*/React.createElement(SecondaryToolbarComponent, { editorView: editorView, api: api, disabled: disabled, typeAhead: typeAhead }); }, commands: { insertMention: insertMention({ sanitizePrivateContent: (_options$sanitizePriv = options === null || options === void 0 ? void 0 : options.sanitizePrivateContent) !== null && _options$sanitizePriv !== void 0 ? _options$sanitizePriv : false, mentionInsertDisplayName: (_options$insertDispla = options === null || options === void 0 ? void 0 : options.insertDisplayName) !== null && _options$insertDispla !== void 0 ? _options$insertDispla : false, api }) }, actions: { openTypeAhead(inputMethod) { var _api$typeAhead, _api$typeAhead$action; return Boolean(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({ triggerHandler: typeAhead, inputMethod })); }, announceMentionsInsertion: mentionChanges => { if (options !== null && options !== void 0 && options.handleMentionsChanged) { options.handleMentionsChanged(mentionChanges); } }, setProvider: async providerPromise => { var _api$core$actions$exe; if (!fg('platform_editor_mention_provider_via_plugin_config')) { return false; } const provider = await providerPromise; // Prevent someone trying to set the exact same provider twice for performance reasons if (previousMediaProvider === provider) { return false; } previousMediaProvider = provider; return (_api$core$actions$exe = api === null || api === void 0 ? void 0 : api.core.actions.execute(({ tr }) => tr.setMeta(mentionPluginKey, { action: ACTIONS.SET_PROVIDER, params: { provider } }))) !== null && _api$core$actions$exe !== void 0 ? _api$core$actions$exe : false; } }, getSharedState(editorState) { if (!editorState) { return undefined; } const mentionPluginState = mentionPluginKey.getState(editorState); return { ...mentionPluginState, typeAheadHandler: typeAhead }; }, pluginsOptions: { quickInsert: ({ formatMessage }) => [{ id: 'mention', title: formatMessage(messages.mention), description: formatMessage(messages.mentionDescription), keywords: ['team', 'user'], priority: 400, keyshortcut: '@', icon: () => /*#__PURE__*/React.createElement(IconMention, null), action(insert, state) { var _api$typeAhead2; const tr = insert(undefined); const pluginState = mentionPluginKey.getState(state); if (pluginState && pluginState.canInsertMention === false) { return false; } api === null || api === void 0 ? void 0 : (_api$typeAhead2 = api.typeAhead) === null || _api$typeAhead2 === void 0 ? void 0 : _api$typeAhead2.actions.openAtTransaction({ triggerHandler: typeAhead, inputMethod: INPUT_METHOD.QUICK_INSERT })(tr); return tr; } }], typeAhead } }; }; export { mentionsPlugin };