@atlaskit/editor-plugin-card
Version:
Card plugin for @atlaskit/editor-core
835 lines (832 loc) • 39.1 kB
JavaScript
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 [];
};