UNPKG

@atlaskit/editor-plugin-hyperlink

Version:

Hyperlink plugin for @atlaskit/editor-core

210 lines (209 loc) 10.8 kB
import React from 'react'; import { link } from '@atlaskit/adf-schema'; import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics'; import { addLink, tooltip } from '@atlaskit/editor-common/keymaps'; import { LinkAction } from '@atlaskit/editor-common/link'; import { toolbarInsertBlockMessages as messages } from '@atlaskit/editor-common/messages'; import { editorCommandToPMCommand } from '@atlaskit/editor-common/preset'; import { IconLink } from '@atlaskit/editor-common/quick-insert'; import { canLinkBeCreatedInRange } from '@atlaskit/editor-common/utils'; import LinkIcon from '@atlaskit/icon/core/link'; import { fg } from '@atlaskit/platform-feature-flags'; import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments'; import { hideLinkToolbarSetMeta, insertLinkWithAnalytics, removeLinkEditorCommand, showLinkToolbar, updateLink, updateLinkEditorCommand } from './editor-commands/commands'; import fakeCursorToolbarPlugin from './pm-plugins/fake-cursor-for-toolbar'; import { createInputRulePlugin } from './pm-plugins/input-rule'; import { createKeymapPlugin } from './pm-plugins/keymap'; import { plugin, stateKey } from './pm-plugins/main'; import { toolbarButtonsPlugin } from './pm-plugins/toolbar-buttons'; import { getToolbarComponents } from './ui/toolbar-components'; import { getToolbarConfig } from './ui/toolbar/Toolbar'; const getPosFromActiveLinkMark = state => { if (state === undefined) { return undefined; } switch (state.type) { case 'EDIT': case 'EDIT_INSERTED': return state.pos; case 'INSERT': return undefined; } }; const selectionToolbarLinkButtonTestId = 'ak-editor-selection-toolbar-link-button'; /** * Hyperlink plugin to be added to an `EditorPresetBuilder` and used with `ComposableEditor` * from `@atlaskit/editor-core`. */ export const hyperlinkPlugin = ({ config: options = {}, api }) => { let primaryToolbarComponent; const isToolbarAIFCEnabled = Boolean(api === null || api === void 0 ? void 0 : api.toolbar); if (isToolbarAIFCEnabled) { var _api$toolbar; api === null || api === void 0 ? void 0 : (_api$toolbar = api.toolbar) === null || _api$toolbar === void 0 ? void 0 : _api$toolbar.actions.registerComponents(getToolbarComponents(api)); } return { name: 'hyperlink', marks() { return [{ name: 'link', mark: link }]; }, commands: { showLinkToolbar: (inputMethod = INPUT_METHOD.TOOLBAR) => { var _api$analytics; return showLinkToolbar(inputMethod, api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions); }, updateLink: (href, text) => { var _api$hyperlink, _api$hyperlink$shared; const linkMark = api === null || api === void 0 ? void 0 : (_api$hyperlink = api.hyperlink) === null || _api$hyperlink === void 0 ? void 0 : (_api$hyperlink$shared = _api$hyperlink.sharedState.currentState()) === null || _api$hyperlink$shared === void 0 ? void 0 : _api$hyperlink$shared.activeLinkMark; const pos = getPosFromActiveLinkMark(linkMark); if (pos === undefined) { return () => null; } return updateLinkEditorCommand(href, text, pos); }, removeLink: () => { var _api$hyperlink2, _api$hyperlink2$share, _api$analytics2; const linkMark = api === null || api === void 0 ? void 0 : (_api$hyperlink2 = api.hyperlink) === null || _api$hyperlink2 === void 0 ? void 0 : (_api$hyperlink2$share = _api$hyperlink2.sharedState.currentState()) === null || _api$hyperlink2$share === void 0 ? void 0 : _api$hyperlink2$share.activeLinkMark; const pos = getPosFromActiveLinkMark(linkMark); if (pos === undefined) { return () => null; } return removeLinkEditorCommand(pos, api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions); } }, actions: { hideLinkToolbar: hideLinkToolbarSetMeta, insertLink: (inputMethod, from, to, href, title, displayText, cardsAvailable = false, sourceEvent = undefined, appearance) => { var _api$card, _api$analytics3; return insertLinkWithAnalytics(inputMethod, from, to, href, api === null || api === void 0 ? void 0 : (_api$card = api.card) === null || _api$card === void 0 ? void 0 : _api$card.actions, api === null || api === void 0 ? void 0 : (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : _api$analytics3.actions, title, displayText, cardsAvailable, sourceEvent, appearance); }, updateLink: updateLink }, getSharedState(editorState) { if (!editorState) { return undefined; } return stateKey.getState(editorState); }, pmPlugins() { return [{ name: 'hyperlink', plugin: ({ dispatch, getIntl }) => plugin(dispatch, getIntl(), options === null || options === void 0 ? void 0 : options.editorAppearance, api, options === null || options === void 0 ? void 0 : options.onClickCallback, // @ts-ignore Temporary solution to check for Live Page editor. options.__livePage) }, { name: 'fakeCursorToolbarPlugin', plugin: () => fakeCursorToolbarPlugin }, { name: 'hyperlinkInputRule', plugin: ({ schema }) => { var _api$analytics4; return createInputRulePlugin(schema, api === null || api === void 0 ? void 0 : (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 ? void 0 : _api$analytics4.actions, options.autoLinkOnBlur); } }, { name: 'hyperlinkKeymap', plugin: () => { var _api$analytics5; return createKeymapPlugin(api === null || api === void 0 ? void 0 : (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : _api$analytics5.actions); } }, { name: 'hyperlinkToolbarButtons', plugin: () => { var _api$card2; const hasCard = !!(api !== null && api !== void 0 && (_api$card2 = api.card) !== null && _api$card2 !== void 0 && _api$card2.actions); return toolbarButtonsPlugin(hasCard ? { skipAnalytics: true } : undefined); } }]; }, pluginsOptions: { quickInsert: ({ formatMessage }) => [{ id: 'hyperlink', title: formatMessage(messages.link), description: formatMessage(messages.linkDescription), keywords: ['hyperlink', 'url'], priority: 1200, keyshortcut: tooltip(addLink), icon: () => /*#__PURE__*/React.createElement(IconLink, null), action(insert, state) { var _api$analytics6, _api$analytics6$actio, _api$analytics6$actio2; const tr = insert(undefined); tr.setMeta(stateKey, { type: LinkAction.SHOW_INSERT_TOOLBAR, inputMethod: INPUT_METHOD.QUICK_INSERT }); const analyticsAttached = api === null || api === void 0 ? void 0 : (_api$analytics6 = api.analytics) === null || _api$analytics6 === void 0 ? void 0 : (_api$analytics6$actio = _api$analytics6.actions) === null || _api$analytics6$actio === void 0 ? void 0 : (_api$analytics6$actio2 = _api$analytics6$actio.attachAnalyticsEvent) === null || _api$analytics6$actio2 === void 0 ? void 0 : _api$analytics6$actio2.call(_api$analytics6$actio, { action: ACTION.INVOKED, actionSubject: ACTION_SUBJECT.TYPEAHEAD, actionSubjectId: ACTION_SUBJECT_ID.TYPEAHEAD_LINK, attributes: { inputMethod: INPUT_METHOD.QUICK_INSERT }, eventType: EVENT_TYPE.UI })(tr); return analyticsAttached !== false ? tr : false; } }], floatingToolbar: getToolbarConfig(options, api), ...(!isToolbarAIFCEnabled && { selectionToolbar: (state, { formatMessage }) => { var _api$userPreferences, _api$userPreferences$, _api$selectionToolbar, _api$selectionToolbar2, _api$selectionToolbar3; const toolbarDocking = fg('platform_editor_use_preferences_plugin') ? api === null || api === void 0 ? void 0 : (_api$userPreferences = api.userPreferences) === null || _api$userPreferences === void 0 ? void 0 : (_api$userPreferences$ = _api$userPreferences.sharedState.currentState()) === null || _api$userPreferences$ === void 0 ? void 0 : _api$userPreferences$.preferences.toolbarDockingPosition : api === null || api === void 0 ? void 0 : (_api$selectionToolbar = api.selectionToolbar) === null || _api$selectionToolbar === void 0 ? void 0 : (_api$selectionToolbar2 = _api$selectionToolbar.sharedState) === null || _api$selectionToolbar2 === void 0 ? void 0 : (_api$selectionToolbar3 = _api$selectionToolbar2.currentState()) === null || _api$selectionToolbar3 === void 0 ? void 0 : _api$selectionToolbar3.toolbarDocking; if (toolbarDocking === 'none' && editorExperiment('platform_editor_controls', 'variant1', { exposure: true })) { const toolbarButton = () => { const { from, to } = state.selection; const isEnabled = canLinkBeCreatedInRange(from, to)(state); const title = formatMessage(messages.link); return { type: 'button', disabled: !isEnabled, testId: `${selectionToolbarLinkButtonTestId}`, icon: LinkIcon, title: title, tooltipContent: tooltip(addLink, title), showTitle: false, onClick: (state, dispatch) => { var _api$analytics7; return editorCommandToPMCommand(showLinkToolbar(INPUT_METHOD.FLOATING_TB, api === null || api === void 0 ? void 0 : (_api$analytics7 = api.analytics) === null || _api$analytics7 === void 0 ? void 0 : _api$analytics7.actions))(state, dispatch); } }; }; return { isToolbarAbove: true, items: [toolbarButton()], rank: 2 }; } else { return undefined; } } }), ...(!isToolbarAIFCEnabled && { primaryToolbarComponent: !(api !== null && api !== void 0 && api.primaryToolbar) && editorExperiment('platform_editor_controls', 'variant1', { exposure: true }) ? primaryToolbarComponent : undefined }) } }; };