UNPKG

@atlaskit/editor-plugin-card

Version:

Card plugin for @atlaskit/editor-core

267 lines 15 kB
import React, { memo, useCallback, useMemo, useState } from 'react'; import { ACTION_SUBJECT_ID, EVENT_TYPE, ACTION, ACTION_SUBJECT } from '@atlaskit/editor-common/analytics'; import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks'; import { HoverLinkOverlay } from '@atlaskit/editor-common/ui'; import { NodeSelection } from '@atlaskit/editor-prosemirror/state'; import { extractSmartLinkEmbed } from '@atlaskit/link-extractors'; import { isWithinPreviewPanelIFrame } from '@atlaskit/linking-common/utils'; import { getObjectAri, getObjectName, getObjectIconUrl } from '@atlaskit/smart-card'; import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals'; import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments'; import { registerRemoveOverlay } from '../pm-plugins/actions'; import { pluginKey } from '../pm-plugins/plugin-key'; import { AwarenessWrapper } from '../ui/AwarenessWrapper'; import { PreviewInvoker } from '../ui/preview/PreviewInvoker'; import { InlineCard } from './inlineCard'; const selector = states => { var _states$editorViewMod, _states$selectionStat; return { mode: (_states$editorViewMod = states.editorViewModeState) === null || _states$editorViewMod === void 0 ? void 0 : _states$editorViewMod.mode, selection: (_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection }; }; export const InlineCardWithAwareness = /*#__PURE__*/memo(({ node, cardContext, actionOptions, useAlternativePreloader, view, getPos, pluginInjectionApi, onClick, isPulseEnabled, isOverlayEnabled, isSelected, isPageSSRed, provider, appearance }) => { var _pluginInjectionApi$c; const [isHovered, setIsHovered] = useState(false); const [isInserted, setIsInserted] = useState(false); const [isResolvedViewRendered, setIsResolvedViewRendered] = useState(false); const editorAppearance = pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$c = pluginInjectionApi.card.sharedState.currentState()) === null || _pluginInjectionApi$c === void 0 ? void 0 : _pluginInjectionApi$c.editorAppearance; const onResolve = useCallback((tr, title) => { const metadata = tr.getMeta(pluginKey); if (metadata && metadata.type === 'REGISTER') { registerRemoveOverlay(() => setIsInserted(false), metadata.info)(tr); } else { registerRemoveOverlay(() => setIsInserted(false))(tr); } if (title) { setIsResolvedViewRendered(true); } }, []); const markMostRecentlyInsertedLink = useCallback(isLinkMostRecentlyInserted => { if (isOverlayEnabled) { setIsInserted(isLinkMostRecentlyInserted); } }, [isOverlayEnabled]); const setOverlayHoveredStyles = useCallback(isHovered => { if (isOverlayEnabled) { setIsHovered(isHovered); } }, [isOverlayEnabled]); const { mode, selection } = useSharedPluginStateWithSelector(pluginInjectionApi, ['selection', 'editorViewMode'], selector); const floatingToolbarNode = selection instanceof NodeSelection && selection.node; // This is a prop to show Hover card, Hover card should be shown only in Live View and Classic Renderer (note when only Editor controls enabled we don't show in Live view) const showHoverPreview = floatingToolbarNode !== node && editorExperiment('platform_editor_preview_panel_linking_exp', true, { exposure: true }); const innerCardWithOpenButtonOverlay = useMemo(() => { var _pluginInjectionApi$a; return /*#__PURE__*/React.createElement(HoverLinkOverlay, { isVisible: isResolvedViewRendered, url: node.attrs.url, compactPadding: editorAppearance === 'comment' || editorAppearance === 'chromeless', editorAnalyticsApi: pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$a = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a === void 0 ? void 0 : _pluginInjectionApi$a.actions, view: view }, /*#__PURE__*/React.createElement(InlineCard, { node: node, view: view, getPos: getPos, useAlternativePreloader: useAlternativePreloader, actionOptions: actionOptions, onResolve: onResolve, onClick: onClick, cardContext: cardContext, isHovered: isHovered, isPageSSRed: isPageSSRed, provider: provider, pluginInjectionApi: pluginInjectionApi, disablePreviewPanel: true })); }, [isResolvedViewRendered, node, editorAppearance, view, getPos, useAlternativePreloader, actionOptions, onResolve, onClick, cardContext, isHovered, isPageSSRed, provider, pluginInjectionApi]); const innerCardOriginal = useMemo(() => /*#__PURE__*/React.createElement(InlineCard, { node: node, view: view, getPos: getPos, useAlternativePreloader: useAlternativePreloader, actionOptions: actionOptions, onResolve: onResolve, onClick: onClick, cardContext: cardContext, isHovered: isHovered, isPageSSRed: isPageSSRed, provider: provider, pluginInjectionApi: pluginInjectionApi, showHoverPreview: false }), [actionOptions, cardContext, getPos, isHovered, node, onClick, onResolve, useAlternativePreloader, view, isPageSSRed, provider, pluginInjectionApi]); const shouldShowOpenButtonOverlay = useMemo(() => { const shouldShowOpenButtonOverlayInChomeless = editorAppearance === 'chromeless'; return (mode === 'edit' || editorAppearance === 'comment' || shouldShowOpenButtonOverlayInChomeless) && (editorExperiment('platform_editor_controls', 'variant1') || editorExperiment('platform_editor_preview_panel_linking_exp', true, { exposure: true })); }, [mode, editorAppearance]); let innerCard = shouldShowOpenButtonOverlay ? innerCardWithOpenButtonOverlay : innerCardOriginal; if (mode === 'view' && editorExperiment('platform_editor_preview_panel_linking_exp', true, { exposure: true })) { var _cardContext$value, _cardContext$value$st; const url = node.attrs.url; const cardState = cardContext === null || cardContext === void 0 ? void 0 : (_cardContext$value = cardContext.value) === null || _cardContext$value === void 0 ? void 0 : (_cardContext$value$st = _cardContext$value.store) === null || _cardContext$value$st === void 0 ? void 0 : _cardContext$value$st.getState()[url]; if (cardState) { var _cardContext$value2, _cardContext$value2$i, _cardContext$value3; const ari = getObjectAri(cardState.details); const name = getObjectName(cardState.details); const iconUrl = getObjectIconUrl(cardState.details); const isPanelAvailable = ari && (cardContext === null || cardContext === void 0 ? void 0 : (_cardContext$value2 = cardContext.value) === null || _cardContext$value2 === void 0 ? void 0 : (_cardContext$value2$i = _cardContext$value2.isPreviewPanelAvailable) === null || _cardContext$value2$i === void 0 ? void 0 : _cardContext$value2$i.call(_cardContext$value2, { ari })); const openPreviewPanel = cardContext === null || cardContext === void 0 ? void 0 : (_cardContext$value3 = cardContext.value) === null || _cardContext$value3 === void 0 ? void 0 : _cardContext$value3.openPreviewPanel; const isPreviewPanelAvailable = Boolean(openPreviewPanel && isPanelAvailable); const firePreviewPanelClickEvent = ({ previewType }) => { var _cardContext$value4, _pluginInjectionApi$a2; const store = cardContext === null || cardContext === void 0 ? void 0 : (_cardContext$value4 = cardContext.value) === null || _cardContext$value4 === void 0 ? void 0 : _cardContext$value4.store; const urlState = store === null || store === void 0 ? void 0 : store.getState()[url || '']; if (pluginInjectionApi !== null && pluginInjectionApi !== void 0 && (_pluginInjectionApi$a2 = pluginInjectionApi.analytics) !== null && _pluginInjectionApi$a2 !== void 0 && _pluginInjectionApi$a2.actions) { var _pluginInjectionApi$a3, _pluginInjectionApi$a4, _urlState$details$met, _urlState$details, _urlState$details$met2, _urlState$details$met3, _urlState$details2, _urlState$details2$me; 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.SMART_LINK, actionSubjectId: ACTION_SUBJECT_ID.HOVER_LABEL, eventType: EVENT_TYPE.UI, attributes: { previewType, destinationProduct: (_urlState$details$met = urlState === null || urlState === void 0 ? void 0 : (_urlState$details = urlState.details) === null || _urlState$details === void 0 ? void 0 : (_urlState$details$met2 = _urlState$details.meta) === null || _urlState$details$met2 === void 0 ? void 0 : _urlState$details$met2.product) !== null && _urlState$details$met !== void 0 ? _urlState$details$met : null, destinationSubproduct: (_urlState$details$met3 = urlState === null || urlState === void 0 ? void 0 : (_urlState$details2 = urlState.details) === null || _urlState$details2 === void 0 ? void 0 : (_urlState$details2$me = _urlState$details2.meta) === null || _urlState$details2$me === void 0 ? void 0 : _urlState$details2$me.subproduct) !== null && _urlState$details$met3 !== void 0 ? _urlState$details$met3 : null } }); } }; const innerCardWithPanelButtonOverlay = /*#__PURE__*/React.createElement(PreviewInvoker, { url: url, appearance: "inline" }, ({ canPreview, invokePreview }) => { const isPreviewModalAvailable = Boolean(canPreview && invokePreview); // In view mode we show HoverLinkOverlay only with if preview mode or panel is available // otherwise a use can click on smartlink itself to open the link in a new tab. const isPreviewAvailable = isPreviewPanelAvailable || isPreviewModalAvailable; // When inside preview panel iframe, hide the overlay button const isInPreviewPanel = isWithinPreviewPanelIFrame(); const showPanelButton = isInPreviewPanel ? isPreviewPanelAvailable : isPreviewAvailable; if (isPreviewAvailable) { var _pluginInjectionApi$a5; return /*#__PURE__*/React.createElement(HoverLinkOverlay, { isVisible: isResolvedViewRendered, url: url, compactPadding: editorAppearance === 'comment' || editorAppearance === 'chromeless', editorAnalyticsApi: pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$a5 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a5 === void 0 ? void 0 : _pluginInjectionApi$a5.actions, view: view, showPanelButton: showPanelButton, showPanelButtonIcon: isPreviewAvailable && isPreviewPanelAvailable ? 'panel' : 'modal' // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) , onClick: event => { if (isPreviewPanelAvailable) { var _extractSmartLinkEmbe; event.preventDefault(); openPreviewPanel === null || openPreviewPanel === void 0 ? void 0 : openPreviewPanel({ url, ari: ari || '', name: name || '', iconUrl, panelData: { embedUrl: expValEquals('platform_hover_card_preview_panel', 'cohort', 'test') ? (_extractSmartLinkEmbe = extractSmartLinkEmbed(cardState === null || cardState === void 0 ? void 0 : cardState.details)) === null || _extractSmartLinkEmbe === void 0 ? void 0 : _extractSmartLinkEmbe.src : undefined } }); firePreviewPanelClickEvent({ previewType: 'panel' }); } else if (isPreviewModalAvailable) { event.preventDefault(); invokePreview === null || invokePreview === void 0 ? void 0 : invokePreview(); firePreviewPanelClickEvent({ previewType: 'modal' }); } } }, /*#__PURE__*/React.createElement(InlineCard, { node: node, view: view, getPos: getPos, useAlternativePreloader: useAlternativePreloader, actionOptions: actionOptions, onResolve: onResolve, onClick: onClick, cardContext: cardContext, isHovered: isHovered, isPageSSRed: isPageSSRed, provider: provider, pluginInjectionApi: pluginInjectionApi, showHoverPreview: mode === 'view' && showHoverPreview, disablePreviewPanel: true })); } else { return /*#__PURE__*/React.createElement(InlineCard, { node: node, view: view, getPos: getPos, useAlternativePreloader: useAlternativePreloader, actionOptions: actionOptions, onResolve: onResolve, onClick: onClick, cardContext: cardContext, isHovered: isHovered, isPageSSRed: isPageSSRed, provider: provider, pluginInjectionApi: pluginInjectionApi, showHoverPreview: mode === 'view' && showHoverPreview }); } }); innerCard = innerCardWithPanelButtonOverlay; } } const getPosFunction = typeof getPos === 'function' ? getPos : undefined; const placeholderUniqId = (getPosFunction === null || getPosFunction === void 0 ? void 0 : getPosFunction()) || 0; return isOverlayEnabled || isPulseEnabled ? /*#__PURE__*/React.createElement(AwarenessWrapper, { isOverlayEnabled: isOverlayEnabled, isPulseEnabled: isPulseEnabled, cardContext: cardContext, getPos: getPos, isHovered: isHovered, isInserted: isInserted, url: node.attrs.url, isSelected: isSelected, isResolvedViewRendered: isResolvedViewRendered, markMostRecentlyInsertedLink: markMostRecentlyInsertedLink, pluginInjectionApi: pluginInjectionApi, setOverlayHoveredStyles: setOverlayHoveredStyles, appearance: appearance }, innerCard) : /*#__PURE__*/React.createElement("span", { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 className: "card", "data-vc": "editor-plugin-inline-card", "data-ssr-placeholder": `editor-plugin-inline-card-${placeholderUniqId}`, "data-ssr-placeholder-replace": `editor-plugin-inline-card-${placeholderUniqId}` }, innerCard); });