UNPKG

@atlaskit/editor-plugin-card

Version:

Card plugin for @atlaskit/editor-core

244 lines (242 loc) 11.2 kB
import _extends from "@babel/runtime/helpers/extends"; import React, { memo, useCallback, useEffect, useMemo, useRef } from 'react'; import rafSchedule from 'raf-schd'; // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead import uuid from 'uuid/v4'; import { INPUT_METHOD } from '@atlaskit/editor-common/analytics'; import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks'; import { UnsupportedInline, findOverflowScrollParent } from '@atlaskit/editor-common/ui'; import { SmartLinkDraggable, SMART_LINK_DRAG_TYPES, SMART_LINK_APPEARANCE } from '@atlaskit/editor-smart-link-draggable'; import { fg } from '@atlaskit/platform-feature-flags'; import { Card as SmartCard } from '@atlaskit/smart-card'; import { CardSSR } from '@atlaskit/smart-card/ssr'; import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals'; import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments'; import { registerCard, removeCard } from '../pm-plugins/actions'; import { getAwarenessProps } from '../pm-plugins/utils'; import { visitCardLinkAnalytics } from '../ui/toolbar'; import { Card } from './genericCard'; import { InlineCardWithAwareness } from './inlineCardWithAwareness'; export var InlineCard = /*#__PURE__*/memo(function (_ref) { var _cardContext$value; var node = _ref.node, cardContext = _ref.cardContext, actionOptions = _ref.actionOptions, useAlternativePreloader = _ref.useAlternativePreloader, view = _ref.view, getPos = _ref.getPos, propsOnClick = _ref.onClick, onRes = _ref.onResolve, isHovered = _ref.isHovered, showHoverPreview = _ref.showHoverPreview, hoverPreviewOptions = _ref.hoverPreviewOptions, isPageSSRed = _ref.isPageSSRed, pluginInjectionApi = _ref.pluginInjectionApi, disablePreviewPanel = _ref.disablePreviewPanel; var _node$attrs = node.attrs, url = _node$attrs.url, data = _node$attrs.data; // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead var refId = useRef(uuid()); var removeCardDispatched = useRef(false); var _ref2 = (cardContext === null || cardContext === void 0 || (_cardContext$value = cardContext.value) === null || _cardContext$value === void 0 ? void 0 : _cardContext$value.store) || {}, getSmartlinkState = _ref2.getState; var cardState = getSmartlinkState === null || getSmartlinkState === void 0 ? void 0 : getSmartlinkState()[url || '']; useEffect(function () { var id = refId.current; removeCardDispatched.current = false; return function () { if (expValEquals('platform_editor_inline_card_dispatch_guard', 'isEnabled', true) && removeCardDispatched.current) { return; } removeCardDispatched.current = true; var tr = view.state.tr; removeCard({ id: id })(tr); view.dispatch(tr); }; }, [getPos, view]); var scrollContainer = useMemo( // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting function () { return findOverflowScrollParent(view.dom) || undefined; }, [view.dom]); var onResolve = useCallback(function (data) { if (!getPos || typeof getPos === 'boolean') { return; } var title = data.title, url = data.url; // don't dispatch immediately since we might be in the middle of // rendering a nodeview rafSchedule(function () { // prosemirror-bump-fix var pos = getPos(); if (typeof pos !== 'number') { return; } var tr = view.state.tr; registerCard({ title: title, url: url, pos: pos, id: refId.current })(tr); onRes === null || onRes === void 0 || onRes(tr, title); view.dispatch(tr); })(); }, [getPos, view, onRes]); var onError = useCallback(function (data) { var url = data.url, err = data.err; if (err) { throw err; } onResolve({ url: url }); }, [onResolve]); var handleOnClick = useCallback(function (event) { if (event.metaKey || event.ctrlKey) { var _pluginInjectionApi$a; var _ref3 = (_pluginInjectionApi$a = pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : pluginInjectionApi.analytics) !== null && _pluginInjectionApi$a !== void 0 ? _pluginInjectionApi$a : {}, editorAnalyticsApi = _ref3.actions; visitCardLinkAnalytics(editorAnalyticsApi, INPUT_METHOD.META_CLICK)(view.state, view.dispatch); window.open(url, '_blank'); } else { // only trigger the provided onClick callback if the meta key or ctrl key is not pressed propsOnClick === null || propsOnClick === void 0 || propsOnClick(event); } }, [propsOnClick, url, view, pluginInjectionApi]); var onClick = editorExperiment('platform_editor_controls', 'variant1') ? handleOnClick : propsOnClick; var card = useMemo(function () { if ((isPageSSRed || cardState && expValEquals('platform_editor_smartlink_local_cache', 'isEnabled', true)) && url) { return /*#__PURE__*/React.createElement(CardSSR, { key: url, url: url, appearance: "inline", onClick: onClick, container: scrollContainer, onResolve: onResolve, onError: onError, inlinePreloaderStyle: useAlternativePreloader ? 'on-right-without-skeleton' : undefined, actionOptions: actionOptions, isHovered: isHovered, showHoverPreview: showHoverPreview, hoverPreviewOptions: hoverPreviewOptions, disablePreviewPanel: disablePreviewPanel, hideIconLoadingSkeleton: true }); } return /*#__PURE__*/React.createElement(SmartCard, { key: url, url: url !== null && url !== void 0 ? url : data.url, appearance: "inline", onClick: onClick, container: scrollContainer, onResolve: onResolve, onError: onError, inlinePreloaderStyle: useAlternativePreloader ? 'on-right-without-skeleton' : undefined, actionOptions: actionOptions, isHovered: isHovered, showHoverPreview: showHoverPreview, hoverPreviewOptions: hoverPreviewOptions, disablePreviewPanel: disablePreviewPanel }); }, [url, data, onClick, scrollContainer, onResolve, onError, useAlternativePreloader, actionOptions, isHovered, showHoverPreview, hoverPreviewOptions, isPageSSRed, disablePreviewPanel, cardState]); // [WS-2307]: we only render card wrapped into a Provider when the value is ready, // otherwise if we got data, we can render the card directly since it doesn't need the Provider return cardContext && cardContext.value ? /*#__PURE__*/React.createElement(cardContext.Provider, { value: cardContext.value }, card) : data ? card : null; }); var WrappedInlineCardWithAwareness = Card(InlineCardWithAwareness, UnsupportedInline); var selectorWithCard = function selectorWithCard(states) { var _states$editorViewMod, _states$cardState; return { mode: (_states$editorViewMod = states.editorViewModeState) === null || _states$editorViewMod === void 0 ? void 0 : _states$editorViewMod.mode, resolvedInlineSmartLinks: (_states$cardState = states.cardState) === null || _states$cardState === void 0 ? void 0 : _states$cardState.resolvedInlineSmartLinks }; }; var selectorWithoutCard = function selectorWithoutCard(states) { var _states$editorViewMod2; return { mode: (_states$editorViewMod2 = states.editorViewModeState) === null || _states$editorViewMod2 === void 0 ? void 0 : _states$editorViewMod2.mode, resolvedInlineSmartLinks: undefined }; }; /** * Inline card node view component that renders a Smart Link inline card within the editor. * * @param props * @example */ export function InlineCardNodeView(props) { var _resolvedInlineSmartL; var useAlternativePreloader = props.useAlternativePreloader, node = props.node, view = props.view, getPos = props.getPos, actionOptions = props.actionOptions, allowEmbeds = props.allowEmbeds, allowBlockCards = props.allowBlockCards, enableInlineUpgradeFeatures = props.enableInlineUpgradeFeatures, pluginInjectionApi = props.pluginInjectionApi, onClickCallback = props.onClickCallback, isPageSSRed = props.isPageSSRed, provider = props.provider, CompetitorPrompt = props.CompetitorPrompt; var _useSharedPluginState = useSharedPluginStateWithSelector(pluginInjectionApi, fg('cc_dnd_smart_link_changeboard_po_template_gate') ? ['editorViewMode', 'card'] : ['editorViewMode'], fg('cc_dnd_smart_link_changeboard_po_template_gate') ? selectorWithCard : selectorWithoutCard), mode = _useSharedPluginState.mode, resolvedInlineSmartLinks = _useSharedPluginState.resolvedInlineSmartLinks; var url = node.attrs.url; var CompetitorPromptComponent = CompetitorPrompt && url ? /*#__PURE__*/React.createElement(CompetitorPrompt, { sourceUrl: url, linkType: "inline" }) : null; useEffect(function () { if (expValEquals('platform_editor_smartlink_local_cache', 'isEnabled', true)) { // Refresh cache in the background provider === null || provider === void 0 || provider.then(function (providerInstance) { var _refreshCache, _ref4; (_refreshCache = (_ref4 = providerInstance).refreshCache) === null || _refreshCache === void 0 || _refreshCache.call(_ref4, props.node); }); } }, [provider, props.node]); var linkPosition = useMemo(function () { if (!getPos || typeof getPos === 'boolean') { return undefined; } var pos = getPos(); return typeof pos === 'number' ? pos : undefined; }, [getPos]); var isChangeboardTarget = linkPosition !== undefined && (resolvedInlineSmartLinks === null || resolvedInlineSmartLinks === void 0 || (_resolvedInlineSmartL = resolvedInlineSmartLinks[0]) === null || _resolvedInlineSmartL === void 0 ? void 0 : _resolvedInlineSmartL.pos) === linkPosition; var inlineCardContent = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(WrappedInlineCardWithAwareness, _extends({ node: node, view: view, getPos: getPos, actionOptions: actionOptions, useAlternativePreloader: useAlternativePreloader, pluginInjectionApi: pluginInjectionApi, onClickCallback: onClickCallback, isPageSSRed: isPageSSRed, provider: provider, appearance: "inline" // Ignored via go/ees005 // eslint-disable-next-line react/jsx-props-no-spreading }, enableInlineUpgradeFeatures && getAwarenessProps(view.state, getPos, allowEmbeds, allowBlockCards, mode === 'view'))), CompetitorPromptComponent); return /*#__PURE__*/React.createElement(SmartLinkDraggable, { url: url, appearance: SMART_LINK_APPEARANCE.INLINE, source: SMART_LINK_DRAG_TYPES.EDITOR, isChangeboardTarget: isChangeboardTarget }, inlineCardContent); } export var inlineCardNodeView = function inlineCardNodeView(_ref5) { var inlineCardViewProducer = _ref5.inlineCardViewProducer; return function (node, view, getPos, decorations) { return inlineCardViewProducer(node, view, getPos, decorations); }; };