UNPKG

@atlaskit/editor-plugin-card

Version:

Card plugin for @atlaskit/editor-core

835 lines (832 loc) 39.1 kB
import React from 'react'; import { isSafeUrl } from '@atlaskit/adf-schema'; import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID as ACTION_SUBJECTID, EVENT_TYPE, INPUT_METHOD, buildOpenedSettingsPayload, buildVisitedNonHyperLinkPayload } from '@atlaskit/editor-common/analytics'; import { buildLayoutButtons, buildLayoutDropdown, commandWithMetadata } from '@atlaskit/editor-common/card'; import { getLinkPreferencesURLFromENV } from '@atlaskit/editor-common/link'; import commonMessages, { annotationMessages, linkMessages, linkToolbarMessages, cardMessages as messages } from '@atlaskit/editor-common/messages'; import { FLOATING_TOOLBAR_LINKPICKER_CLASSNAME, richMediaClassName } from '@atlaskit/editor-common/styles'; import { areToolbarFlagsEnabled } from '@atlaskit/editor-common/toolbar-flag-check'; import { canRenderDatasource } from '@atlaskit/editor-common/utils'; import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity'; import { NodeSelection } from '@atlaskit/editor-prosemirror/state'; import { findDomRefAtPos, removeSelectedNode } from '@atlaskit/editor-prosemirror/utils'; import { akEditorSelectedNodeClassName } from '@atlaskit/editor-shared-styles'; import CommentIcon from '@atlaskit/icon/core/comment'; import CopyIcon from '@atlaskit/icon/core/copy'; import DeleteIcon from '@atlaskit/icon/core/delete'; import EditIcon from '@atlaskit/icon/core/edit'; import LinkBrokenIcon from '@atlaskit/icon/core/link-broken'; import LinkExternalIcon from '@atlaskit/icon/core/link-external'; import CogIcon from '@atlaskit/icon/core/settings'; import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals'; import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments'; import { changeSelectedCardToText } from '../pm-plugins/doc'; import { pluginKey } from '../pm-plugins/plugin-key'; import { appearanceForNodeType, displayInfoForCard, findCardInfo, isDatasourceNode, titleUrlPairFromNode } from '../pm-plugins/utils'; import { DatasourceAppearanceButton } from './DatasourceAppearanceButton'; import { buildEditLinkToolbar, editLinkToolbarConfig, getEditLinkCallback } from './EditLinkToolbar'; import { EditToolbarButton } from './EditToolbarButton'; import { HyperlinkToolbarAppearance } from './HyperlinkToolbarAppearance'; import { getCustomHyperlinkAppearanceDropdown } from './HyperlinkToolbarAppearanceDropdown'; import { LinkToolbarAppearance } from './LinkToolbarAppearance'; import { getLinkAppearanceDropdown } from './LinkToolbarAppearanceDropdown'; import { OpenPreviewPanelToolbarButton } from './OpenPreviewButton'; import { ToolbarViewedEvent } from './ToolbarViewedEvent'; export const removeCard = editorAnalyticsApi => commandWithMetadata((state, dispatch) => { if (!(state.selection instanceof NodeSelection)) { return false; } const type = state.selection.node.type.name; const payload = { action: ACTION.DELETED, actionSubject: ACTION_SUBJECT.SMART_LINK, actionSubjectId: type, attributes: { inputMethod: INPUT_METHOD.FLOATING_TB, displayMode: type }, eventType: EVENT_TYPE.TRACK }; if (dispatch) { const { tr } = state; removeSelectedNode(tr); editorAnalyticsApi === null || editorAnalyticsApi === void 0 ? void 0 : editorAnalyticsApi.attachAnalyticsEvent(payload)(tr); dispatch(tr); } return true; }, { action: ACTION.DELETED }); export const visitCardLinkAnalytics = (editorAnalyticsApi, inputMethod, resolvedAttributes) => (state, dispatch) => { if (!(state.selection instanceof NodeSelection)) { return false; } const { type } = state.selection.node; if (dispatch) { const { tr } = state; const shouldIncludeResolvedAttributes = expValEquals('cc_integrations_editor_open_link_click_analytics', 'isEnabled', true); editorAnalyticsApi === null || editorAnalyticsApi === void 0 ? void 0 : editorAnalyticsApi.attachAnalyticsEvent(buildVisitedNonHyperLinkPayload(type.name, inputMethod, shouldIncludeResolvedAttributes ? resolvedAttributes : undefined))(tr); dispatch(tr); } return true; }; const fireOpenLinkToolbarAnalytics = (editorAnalyticsApi, inputMethod, resolvedAttributes = {}) => (state, dispatch) => { const linkAnalyticsRecorded = visitCardLinkAnalytics(editorAnalyticsApi, inputMethod, resolvedAttributes)(state, dispatch); if (!linkAnalyticsRecorded) { return false; } if (expValEquals('cc_integrations_editor_open_link_click_analytics', 'isEnabled', true)) { editorAnalyticsApi === null || editorAnalyticsApi === void 0 ? void 0 : editorAnalyticsApi.fireAnalyticsEvent({ action: ACTION.CLICKED, actionSubject: ACTION_SUBJECT.BUTTON, actionSubjectId: ACTION_SUBJECTID.OPEN_LINK, attributes: { displayCategory: resolvedAttributes.displayCategory, extensionKey: resolvedAttributes.extensionKey, status: resolvedAttributes.status, statusDetails: resolvedAttributes.statusDetails }, eventType: EVENT_TYPE.UI }); } return true; }; export const openLinkSettings = editorAnalyticsApi => (state, dispatch) => { if (!(state.selection instanceof NodeSelection)) { return false; } if (dispatch) { const { tr, selection: { node: { type } } } = state; editorAnalyticsApi === null || editorAnalyticsApi === void 0 ? void 0 : editorAnalyticsApi.attachAnalyticsEvent(buildOpenedSettingsPayload(type.name))(tr); dispatch(tr); } return true; }; export const floatingToolbar = (cardOptions, lpLinkPicker, linkPickerOptions, pluginInjectionApi, disableFloatingToolbar) => { return (state, intl, providerFactory) => { if (disableFloatingToolbar) { return; } const { inlineCard, blockCard, embedCard } = state.schema.nodes; const nodeType = [inlineCard, blockCard, embedCard]; const pluginState = pluginKey.getState(state); if (!(state.selection instanceof NodeSelection)) { return; } const selectedNode = state.selection.node; if (!selectedNode) { return; } const isEmbedCard = appearanceForNodeType(selectedNode.type) === 'embed'; /* add an offset to embeds due to extra padding */ const toolbarOffset = isEmbedCard ? { offset: [0, 24] } : {}; // Applies padding override for when link picker is currently displayed const className = pluginState !== null && pluginState !== void 0 && pluginState.showLinkingToolbar ? FLOATING_TOOLBAR_LINKPICKER_CLASSNAME : undefined; const isLinkPickerEnabled = !!lpLinkPicker; return { title: intl.formatMessage(messages.card), className, nodeType, preventPopupOverflow: isLinkPickerEnabled, ...toolbarOffset, getDomRef: view => { // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting const element = findDomRefAtPos(view.state.selection.from, view.domAtPos.bind(view)); if (!element) { return undefined; } if (isEmbedCard) { // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting const richMediaElement = element.querySelector(`.${richMediaClassName}`); if (!editorExperiment('platform_editor_preview_panel_responsiveness', true, { exposure: true })) { return richMediaElement; } if (richMediaElement) { return richMediaElement; } } return element; }, items: generateToolbarItems(state, intl, providerFactory, cardOptions, lpLinkPicker, linkPickerOptions, pluginInjectionApi), scrollable: pluginState !== null && pluginState !== void 0 && pluginState.showLinkingToolbar ? false : true, ...editLinkToolbarConfig(Boolean(pluginState === null || pluginState === void 0 ? void 0 : pluginState.showLinkingToolbar), isLinkPickerEnabled) }; }; }; const unlinkCard = (node, state, editorAnalyticsApi) => { const displayInfo = displayInfoForCard(node, findCardInfo(state)); const text = displayInfo.title || displayInfo.url; if (text) { return commandWithMetadata(changeSelectedCardToText(text, editorAnalyticsApi), { action: ACTION.UNLINK }); } return () => false; }; const buildAlignmentOptions = (state, intl, widthPluginDependencyApi, analyticsApi, cardOptions, areAnyNewToolbarFlagsEnabled) => { if (areAnyNewToolbarFlagsEnabled) { return buildLayoutDropdown(state, intl, state.schema.nodes.embedCard, widthPluginDependencyApi, analyticsApi, true, true, cardOptions === null || cardOptions === void 0 ? void 0 : cardOptions.allowWrapping, cardOptions === null || cardOptions === void 0 ? void 0 : cardOptions.allowAlignment); } return buildLayoutButtons(state, intl, state.schema.nodes.embedCard, widthPluginDependencyApi, analyticsApi, true, true, cardOptions === null || cardOptions === void 0 ? void 0 : cardOptions.allowWrapping, cardOptions === null || cardOptions === void 0 ? void 0 : cardOptions.allowAlignment); }; const withToolbarMetadata = command => commandWithMetadata(command, { inputMethod: INPUT_METHOD.FLOATING_TB }); const getToolbarViewedItem = (url, display) => { if (!url) { return []; } return [{ type: 'custom', fallback: [], render: editorView => /*#__PURE__*/React.createElement(ToolbarViewedEvent, { key: "edit.link.menu.viewed", url: url, display: display, editorView: editorView }) }]; }; const generateToolbarItems = (state, intl, providerFactory, cardOptions, lpLinkPicker, linkPicker, pluginInjectionApi) => node => { var _pluginInjectionApi$a, _pluginInjectionApi$d, _pluginInjectionApi$d2, _pluginInjectionApi$a2, _node$attrs, _node$attrs$datasourc; const { url } = titleUrlPairFromNode(node); const { actions: editorAnalyticsApi } = (_pluginInjectionApi$a = pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : pluginInjectionApi.analytics) !== null && _pluginInjectionApi$a !== void 0 ? _pluginInjectionApi$a : {}; let metadata = {}; if (url && !isSafeUrl(url)) { return []; } else { const { title } = displayInfoForCard(node, findCardInfo(state)); metadata = { url: url, title: title }; } const areAllNewToolbarFlagsDisabled = !areToolbarFlagsEnabled(Boolean(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : pluginInjectionApi.toolbar)); const currentAppearance = appearanceForNodeType(node.type); const { hoverDecoration } = (_pluginInjectionApi$d = pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$d2 = pluginInjectionApi.decorations) === null || _pluginInjectionApi$d2 === void 0 ? void 0 : _pluginInjectionApi$d2.actions) !== null && _pluginInjectionApi$d !== void 0 ? _pluginInjectionApi$d : {}; const isDatasource = isDatasourceNode(node); const pluginState = pluginKey.getState(state); const annotationApiState = pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$a2 = pluginInjectionApi.annotation) === null || _pluginInjectionApi$a2 === void 0 ? void 0 : _pluginInjectionApi$a2.sharedState.currentState(); const activeCommentMark = node.marks.find(mark => mark.type.name === 'annotation' && (annotationApiState === null || annotationApiState === void 0 ? void 0 : annotationApiState.annotations[mark.attrs.id]) === false); const isCommentEnabled = annotationApiState && annotationApiState.isVisible && !annotationApiState.bookmark && !annotationApiState.mouseData.isSelecting && !activeCommentMark && node.type === state.schema.nodes.inlineCard; const onCommentButtonClick = (state, dispatch) => { var _pluginInjectionApi$a3, _pluginInjectionApi$a4; if (!(pluginInjectionApi !== null && pluginInjectionApi !== void 0 && pluginInjectionApi.annotation) || !isCommentEnabled) { return false; } pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$a3 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a3 === void 0 ? void 0 : (_pluginInjectionApi$a4 = _pluginInjectionApi$a3.actions) === null || _pluginInjectionApi$a4 === void 0 ? void 0 : _pluginInjectionApi$a4.fireAnalyticsEvent({ action: ACTION.CLICKED, actionSubject: ACTION_SUBJECT.BUTTON, actionSubjectId: ACTION_SUBJECTID.CREATE_INLINE_COMMENT_FROM_HIGHLIGHT_ACTIONS_MENU, eventType: EVENT_TYPE.UI, attributes: { source: 'highlightActionsMenu', pageMode: 'edit', sourceNode: 'inlineCard' } }); const { setInlineCommentDraftState } = pluginInjectionApi.annotation.actions; const command = setInlineCommentDraftState(true, INPUT_METHOD.FLOATING_TB); return command(state, dispatch); }; const shouldRenderDatasourceToolbar = isDatasource && cardOptions.allowDatasource && canRenderDatasource(node === null || node === void 0 ? void 0 : (_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : (_node$attrs$datasourc = _node$attrs.datasource) === null || _node$attrs$datasourc === void 0 ? void 0 : _node$attrs$datasourc.id); if (pluginState !== null && pluginState !== void 0 && pluginState.showLinkingToolbar) { return [buildEditLinkToolbar({ providerFactory, linkPicker, node, pluginInjectionApi, lpLinkPicker })]; } else if (shouldRenderDatasourceToolbar) { return getDatasourceButtonGroup(metadata, intl, editorAnalyticsApi, node, hoverDecoration, node.attrs.datasource.id, state, cardOptions, currentAppearance, pluginInjectionApi); } else { var _pluginInjectionApi$c, _pluginInjectionApi$c2, _pluginInjectionApi$c3, _pluginState$resolved; const { inlineCard } = state.schema.nodes; const editItems = cardOptions.allowDatasource ? [{ type: 'custom', fallback: [], render: editorView => /*#__PURE__*/React.createElement(EditToolbarButton, { key: "edit-toolbar-item", url: url, intl: intl, editorAnalyticsApi: editorAnalyticsApi, editorView: editorView, onLinkEditClick: getEditLinkCallback(editorAnalyticsApi, true), currentAppearance: currentAppearance, areAnyNewToolbarFlagsEnabled: !areAllNewToolbarFlagsDisabled }) }] : [{ id: 'editor.link.edit', type: 'button', selected: false, metadata: metadata, title: intl.formatMessage(linkToolbarMessages.editLink), showTitle: true, testId: 'link-toolbar-edit-link-button', onClick: getEditLinkCallback(editorAnalyticsApi, true) }, { type: 'separator' }]; const commentItems = isCommentEnabled ? [{ id: 'editor.link.commentLink', type: 'button', icon: CommentIcon, testId: 'inline-card-toolbar-comment-button', iconFallback: CommentIcon, title: intl.formatMessage(annotationMessages.createComment), showTitle: editorExperiment('platform_editor_controls', 'control') ? undefined : true, onClick: onCommentButtonClick, disabled: isOfflineMode(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$c = pluginInjectionApi.connectivity) === null || _pluginInjectionApi$c === void 0 ? void 0 : (_pluginInjectionApi$c2 = _pluginInjectionApi$c.sharedState) === null || _pluginInjectionApi$c2 === void 0 ? void 0 : (_pluginInjectionApi$c3 = _pluginInjectionApi$c2.currentState()) === null || _pluginInjectionApi$c3 === void 0 ? void 0 : _pluginInjectionApi$c3.mode) }, { type: 'separator' }] : []; const openLinkInputMethod = INPUT_METHOD.FLOATING_TB; const editButtonItems = cardOptions.allowDatasource ? [{ type: 'custom', fallback: [], render: editorView => /*#__PURE__*/React.createElement(EditToolbarButton, { key: "edit-toolbar-item", url: url, intl: intl, editorAnalyticsApi: editorAnalyticsApi, editorView: editorView, onLinkEditClick: getEditLinkCallback(editorAnalyticsApi, true), currentAppearance: currentAppearance, areAnyNewToolbarFlagsEnabled: !areAllNewToolbarFlagsDisabled }) }] : [{ id: 'editor.link.edit', type: 'button', selected: false, metadata: metadata, title: intl.formatMessage(linkToolbarMessages.editLink), icon: EditIcon, testId: 'link-toolbar-edit-link-button', onClick: getEditLinkCallback(editorAnalyticsApi, true) }]; const openPreviewPanelItems = editorExperiment('platform_editor_preview_panel_linking_exp', true, { exposure: true }) ? [{ type: 'custom', fallback: [], render: () => /*#__PURE__*/React.createElement(OpenPreviewPanelToolbarButton, { node: node, intl: intl, editorAnalyticsApi: editorAnalyticsApi, areAnyNewToolbarFlagsEnabled: !areAllNewToolbarFlagsDisabled }) }] : []; const resolvedToolbarAttributes = url ? (_pluginState$resolved = pluginState === null || pluginState === void 0 ? void 0 : pluginState.resolvedToolbarAttributesByUrl[url]) !== null && _pluginState$resolved !== void 0 ? _pluginState$resolved : {} : {}; const openLinkToolbarItem = { id: 'editor.link.openLink', type: 'button', icon: LinkExternalIcon, iconFallback: LinkExternalIcon, metadata: metadata, className: 'hyperlink-open-link', title: intl.formatMessage(linkMessages.openLink), onClick: fireOpenLinkToolbarAnalytics(editorAnalyticsApi, openLinkInputMethod, resolvedToolbarAttributes), href: url, target: '_blank' }; const toolbarItems = areAllNewToolbarFlagsDisabled ? [...editItems, ...commentItems, ...openPreviewPanelItems, openLinkToolbarItem, { type: 'separator' }, ...getUnlinkButtonGroup(state, intl, node, inlineCard, editorAnalyticsApi, !areAllNewToolbarFlagsDisabled), { type: 'copy-button', items: [{ state, formatMessage: intl.formatMessage, nodeType: node.type }] }, { type: 'separator' }, getSettingsButton(intl, editorAnalyticsApi, cardOptions.userPreferencesLink), { type: 'separator' }, { id: 'editor.link.delete', focusEditoronEnter: true, type: 'button', appearance: 'danger', icon: DeleteIcon, iconFallback: DeleteIcon, onMouseEnter: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(node.type, true), onMouseLeave: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(node.type, false), onFocus: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(node.type, true), onBlur: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(node.type, false), title: intl.formatMessage(commonMessages.remove), onClick: withToolbarMetadata(removeCard(editorAnalyticsApi)) }] : [...editButtonItems, ...[...getUnlinkButtonGroup(state, intl, node, inlineCard, editorAnalyticsApi, !areAllNewToolbarFlagsDisabled), { type: 'separator', fullHeight: true }], ...openPreviewPanelItems, openLinkToolbarItem, ...(commentItems.length > 1 ? [{ type: 'separator', fullHeight: true }, commentItems[0]] : commentItems)]; if (currentAppearance === 'embed') { var _pluginInjectionApi$a5; const alignmentOptions = buildAlignmentOptions(state, intl, pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : pluginInjectionApi.width, pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$a5 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a5 === void 0 ? void 0 : _pluginInjectionApi$a5.actions, cardOptions, !areAllNewToolbarFlagsDisabled); if (alignmentOptions.length && areAllNewToolbarFlagsDisabled) { alignmentOptions.push({ type: 'separator' }); } toolbarItems.unshift(...alignmentOptions); } const { allowBlockCards, allowEmbeds, allowDatasource, showUpgradeDiscoverability } = cardOptions; // This code will be executed only for appearances such as "inline", "block" & "embed" // For url appearance, please see HyperlinkToolbarAppearanceProps if (currentAppearance) { const showDatasourceAppearance = allowDatasource && url; toolbarItems.unshift(...getToolbarViewedItem(url, currentAppearance), areAllNewToolbarFlagsDisabled ? { type: 'custom', fallback: [], render: editorView => /*#__PURE__*/React.createElement(LinkToolbarAppearance, { key: "link-appearance", url: url, intl: intl, currentAppearance: currentAppearance, editorView: editorView, editorState: state, allowEmbeds: allowEmbeds, allowBlockCards: allowBlockCards, editorAnalyticsApi: editorAnalyticsApi, showUpgradeDiscoverability: showUpgradeDiscoverability, areAnyNewToolbarFlagsEnabled: false }) } : getLinkAppearanceDropdown({ url, intl, currentAppearance, editorState: state, allowEmbeds, allowBlockCards: allowBlockCards, editorAnalyticsApi, allowDatasource: cardOptions.allowDatasource, showUpgradeDiscoverability: showUpgradeDiscoverability, settingsConfig: getSettingsButton(intl, editorAnalyticsApi, cardOptions.userPreferencesLink), isDatasourceView: isDatasource, areAnyNewToolbarFlagsEnabled: !areAllNewToolbarFlagsDisabled }), ...(showDatasourceAppearance && areAllNewToolbarFlagsDisabled ? [{ type: 'custom', fallback: [], render: editorView => /*#__PURE__*/React.createElement(DatasourceAppearanceButton, { intl: intl, editorAnalyticsApi: editorAnalyticsApi, url: url, editorView: editorView, editorState: state, inputMethod: INPUT_METHOD.FLOATING_TB, areAnyNewToolbarFlagsEnabled: !areAllNewToolbarFlagsDisabled }) }] : []), ...(!areAllNewToolbarFlagsDisabled ? [] : [{ type: 'separator' }])); } if (!areAllNewToolbarFlagsDisabled) { const hoverDecorationProps = (nodeType, className) => ({ onMouseEnter: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(nodeType, true, className), onMouseLeave: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(nodeType, false, className), onFocus: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(nodeType, true, className), onBlur: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(nodeType, false, className) }); // testId is required to show focus on trigger button on ESC key press // see hideOnEsc in platform/packages/editor/editor-plugin-floating-toolbar/src/ui/Dropdown.tsx const testId = 'card-overflow-dropdown-trigger'; const overflowMenuConfig = [{ type: 'separator', fullHeight: true }, { type: 'overflow-dropdown', testId, options: [{ title: intl.formatMessage(commonMessages.copyToClipboard), onClick: () => { var _pluginInjectionApi$c4, _pluginInjectionApi$f; pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$c4 = pluginInjectionApi.core) === null || _pluginInjectionApi$c4 === void 0 ? void 0 : _pluginInjectionApi$c4.actions.execute( // @ts-ignore pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$f = pluginInjectionApi.floatingToolbar) === null || _pluginInjectionApi$f === void 0 ? void 0 : _pluginInjectionApi$f.commands.copyNode(node.type, INPUT_METHOD.FLOATING_TB)); return true; }, icon: /*#__PURE__*/React.createElement(CopyIcon, { label: intl.formatMessage(commonMessages.copyToClipboard) }), ...hoverDecorationProps(node.type, akEditorSelectedNodeClassName) }, { title: intl.formatMessage(commonMessages.delete), onClick: withToolbarMetadata(removeCard(editorAnalyticsApi)), icon: /*#__PURE__*/React.createElement(DeleteIcon, { label: intl.formatMessage(commonMessages.delete) }), ...hoverDecorationProps(node.type) }] }]; toolbarItems.push(...overflowMenuConfig); } return toolbarItems; } }; const getUnlinkButtonGroup = (state, intl, node, inlineCard, editorAnalyticsApi, areAnyNewToolbarFlagsEnabled) => { return node.type === inlineCard ? [{ id: 'editor.link.unlink', focusEditoronEnter: true, type: 'button', title: intl.formatMessage(linkToolbarMessages.unlink), icon: LinkBrokenIcon, iconFallback: LinkBrokenIcon, onClick: withToolbarMetadata(unlinkCard(node, state, editorAnalyticsApi)) }, ...(areAnyNewToolbarFlagsEnabled ? [] : [{ type: 'separator' }])] : []; }; export const getSettingsButton = (intl, editorAnalyticsApi, userPreferencesLink) => { return { id: 'editor.link.settings', type: 'button', icon: CogIcon, iconFallback: CogIcon, title: intl.formatMessage(linkToolbarMessages.settingsLink), onClick: openLinkSettings(editorAnalyticsApi), href: userPreferencesLink || getLinkPreferencesURLFromENV(), target: '_blank' }; }; const getDatasourceButtonGroup = (metadata, intl, editorAnalyticsApi, node, hoverDecoration, datasourceId, state, cardOptions, currentAppearance, pluginInjectionApi) => { var _node$attrs2, _node$attrs3; const toolbarItems = []; toolbarItems.push(...getToolbarViewedItem(node === null || node === void 0 ? void 0 : (_node$attrs2 = node.attrs) === null || _node$attrs2 === void 0 ? void 0 : _node$attrs2.url, currentAppearance !== null && currentAppearance !== void 0 ? currentAppearance : 'url')); const areAllNewToolbarFlagsDisabled = !areToolbarFlagsEnabled(Boolean(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : pluginInjectionApi.toolbar)); const canShowAppearanceSwitch = () => { // we do not show smart-link or the datasource icons when the node does not have a url to resolve if (!metadata.url) { return false; } return true; }; if (canShowAppearanceSwitch()) { const { allowBlockCards, allowEmbeds, showUpgradeDiscoverability } = cardOptions; const { url } = metadata; if (areAllNewToolbarFlagsDisabled) { toolbarItems.push({ type: 'custom', fallback: [], render: editorView => { return /*#__PURE__*/React.createElement(LinkToolbarAppearance, { key: "link-appearance", url: url, intl: intl, currentAppearance: currentAppearance, editorView: editorView, editorState: state, allowEmbeds: allowEmbeds, allowBlockCards: allowBlockCards, editorAnalyticsApi: editorAnalyticsApi, showUpgradeDiscoverability: showUpgradeDiscoverability, isDatasourceView: true, areAnyNewToolbarFlagsEnabled: false }); } }, { type: 'custom', fallback: [], render: editorView => /*#__PURE__*/React.createElement(DatasourceAppearanceButton, { intl: intl, editorAnalyticsApi: editorAnalyticsApi, url: url, editorView: editorView, editorState: state, selected: true, inputMethod: INPUT_METHOD.FLOATING_TB, areAnyNewToolbarFlagsEnabled: !areAllNewToolbarFlagsDisabled }) }, { type: 'separator' }); } else { toolbarItems.push(getLinkAppearanceDropdown({ url, intl, currentAppearance, editorState: state, allowEmbeds, allowBlockCards: allowBlockCards, editorAnalyticsApi, allowDatasource: cardOptions.allowDatasource, showUpgradeDiscoverability: showUpgradeDiscoverability, settingsConfig: getSettingsButton(intl, editorAnalyticsApi, cardOptions.userPreferencesLink), isDatasourceView: true, areAnyNewToolbarFlagsEnabled: !areAllNewToolbarFlagsDisabled }), { type: 'separator' }); } } const openLinkInputMethod = INPUT_METHOD.FLOATING_TB; const pluginState = pluginKey.getState(state); toolbarItems.push({ type: 'custom', fallback: [], render: editorView => /*#__PURE__*/React.createElement(EditToolbarButton, { datasourceId: datasourceId, node: node, key: "edit-toolbar-item", url: metadata.url, intl: intl, editorAnalyticsApi: editorAnalyticsApi, editorView: editorView, onLinkEditClick: getEditLinkCallback(editorAnalyticsApi, false), currentAppearance: "datasource", areAnyNewToolbarFlagsEnabled: !areAllNewToolbarFlagsDisabled }) }); if (node !== null && node !== void 0 && (_node$attrs3 = node.attrs) !== null && _node$attrs3 !== void 0 && _node$attrs3.url) { var _pluginState$resolved2; toolbarItems.push({ id: 'editor.link.openLink', type: 'button', icon: LinkExternalIcon, iconFallback: LinkExternalIcon, metadata: metadata, className: 'hyperlink-open-link', title: intl.formatMessage(linkMessages.openLink), onClick: fireOpenLinkToolbarAnalytics(editorAnalyticsApi, openLinkInputMethod, (_pluginState$resolved2 = pluginState === null || pluginState === void 0 ? void 0 : pluginState.resolvedToolbarAttributesByUrl[node.attrs.url]) !== null && _pluginState$resolved2 !== void 0 ? _pluginState$resolved2 : {}), href: node.attrs.url, target: '_blank' }); if (areAllNewToolbarFlagsDisabled) { toolbarItems.push({ type: 'separator' }); } } if (areAllNewToolbarFlagsDisabled) { toolbarItems.push({ type: 'copy-button', supportsViewMode: true, items: [{ state, formatMessage: intl.formatMessage, nodeType: node.type }] }, { type: 'separator' }, getSettingsButton(intl, editorAnalyticsApi, cardOptions === null || cardOptions === void 0 ? void 0 : cardOptions.userPreferencesLink), { type: 'separator' }, { id: 'editor.link.delete', focusEditoronEnter: true, type: 'button', appearance: 'danger', icon: DeleteIcon, iconFallback: DeleteIcon, onMouseEnter: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(node.type, true), onMouseLeave: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(node.type, false), onFocus: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(node.type, true), onBlur: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(node.type, false), title: intl.formatMessage(commonMessages.remove), onClick: withToolbarMetadata(removeCard(editorAnalyticsApi)) }); } else { // testId is required to show focus on trigger button on ESC key press // see hideOnEsc in platform/packages/editor/editor-plugin-floating-toolbar/src/ui/Dropdown.tsx const testId = 'datasource-overflow-dropdown-trigger'; // When canShowAppearanceSwitch is true, and platform_editor_controls is on, the link appearance dropdown shows, which includes a link preference button // So only add the link appearance button when canShowAppearanceSwitch is false if (!canShowAppearanceSwitch()) { const linkPreferenceButton = getSettingsButton(intl, editorAnalyticsApi, cardOptions === null || cardOptions === void 0 ? void 0 : cardOptions.userPreferencesLink); toolbarItems.push({ type: 'separator', fullHeight: true }, linkPreferenceButton, { type: 'separator', fullHeight: true }); } else { toolbarItems.push({ type: 'separator', fullHeight: true }); } const overflowMenuConfig = [{ type: 'overflow-dropdown', testId, options: [{ title: intl.formatMessage(commonMessages.copyToClipboard), onClick: () => { var _pluginInjectionApi$c5, _pluginInjectionApi$f2; pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$c5 = pluginInjectionApi.core) === null || _pluginInjectionApi$c5 === void 0 ? void 0 : _pluginInjectionApi$c5.actions.execute( // @ts-ignore pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$f2 = pluginInjectionApi.floatingToolbar) === null || _pluginInjectionApi$f2 === void 0 ? void 0 : _pluginInjectionApi$f2.commands.copyNode(node.type, INPUT_METHOD.FLOATING_TB)); return true; }, onMouseEnter: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(node.type, true, akEditorSelectedNodeClassName), onMouseLeave: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(node.type, false, akEditorSelectedNodeClassName), onFocus: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(node.type, true, akEditorSelectedNodeClassName), onBlur: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(node.type, false, akEditorSelectedNodeClassName), icon: /*#__PURE__*/React.createElement(CopyIcon, { label: intl.formatMessage(commonMessages.copyToClipboard) }) }, { title: intl.formatMessage(commonMessages.delete), onClick: withToolbarMetadata(removeCard(editorAnalyticsApi)), onMouseEnter: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(node.type, true), onMouseLeave: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(node.type, false), onFocus: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(node.type, true), onBlur: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(node.type, false), icon: /*#__PURE__*/React.createElement(DeleteIcon, { label: intl.formatMessage(commonMessages.delete) }) }] }]; toolbarItems.push(...overflowMenuConfig); } return toolbarItems; }; export const shouldRenderToolbarPulse = (embedEnabled, appearance, status, isDiscoverabilityEnabled) => { return embedEnabled && appearance === 'inline' && status === 'resolved' && isDiscoverabilityEnabled; }; export const getStartingToolbarItems = (options, api) => { return (intl, link, onEditLink, metadata, _state) => { const areAllNewToolbarFlagsDisabled = !areToolbarFlagsEnabled(Boolean(api === null || api === void 0 ? void 0 : api.toolbar)); const editLinkItem = options.allowDatasource ? [{ type: 'custom', fallback: [], render: editorView => { var _api$analytics; if (!editorView) { return null; } return /*#__PURE__*/React.createElement(EditToolbarButton, { key: "edit-toolbar-button", intl: intl, editorAnalyticsApi: api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions, url: link, editorView: editorView, onLinkEditClick: onEditLink, currentAppearance: "url", areAnyNewToolbarFlagsEnabled: !areAllNewToolbarFlagsDisabled }); } }] : [{ id: 'editor.link.edit', testId: 'editor.link.edit', type: 'button', onClick: onEditLink, title: intl.formatMessage(linkToolbarMessages.editLink), showTitle: editorExperiment('platform_editor_controls', 'variant1') ? false : true, metadata: metadata, icon: editorExperiment('platform_editor_controls', 'variant1') ? EditIcon : undefined }, { type: 'separator' }]; if (!areAllNewToolbarFlagsDisabled) { var _api$analytics2, _api$analytics3; const hyperlinkAppearance = [getCustomHyperlinkAppearanceDropdown({ url: link, intl, editorAnalyticsApi: api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions, allowDatasource: options.allowDatasource, editorPluginApi: api, cardOptions: options, settingsConfig: getSettingsButton(intl, api === null || api === void 0 ? void 0 : (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : _api$analytics3.actions, options.userPreferencesLink), isDatasourceView: false })]; return [{ type: 'custom', fallback: [], render: editorView => /*#__PURE__*/React.createElement(ToolbarViewedEvent, { key: "edit.link.menu.viewed", url: link, display: "url", editorView: editorView }) }, ...hyperlinkAppearance, ...editLinkItem]; } return [{ type: 'custom', fallback: [], render: editorView => /*#__PURE__*/React.createElement(ToolbarViewedEvent, { key: "edit.link.menu.viewed", url: link, display: "url", editorView: editorView }) }, { type: 'custom', fallback: [], render: editorView => { var _api$analytics4; if (!editorView) { return null; } return /*#__PURE__*/React.createElement(HyperlinkToolbarAppearance, { key: "link-appearance", url: link, intl: intl, editorView: editorView, editorState: editorView.state, cardOptions: options, editorAnalyticsApi: api === null || api === void 0 ? void 0 : (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 ? void 0 : _api$analytics4.actions, editorPluginApi: api }); } }, ...editLinkItem]; }; }; export const getEndingToolbarItems = (options, api) => (intl, _link) => { /** * Require either provider to be supplied (controls link preferences) * Or explicit user preferences config in order to enable button */ if ((options.provider || options.userPreferencesLink) && !areToolbarFlagsEnabled(Boolean(api === null || api === void 0 ? void 0 : api.toolbar))) { var _api$analytics5; return [{ type: 'separator' }, getSettingsButton(intl, api === null || api === void 0 ? void 0 : (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : _api$analytics5.actions, options.userPreferencesLink)]; } return []; };