UNPKG

@atlaskit/editor-plugin-card

Version:

Card plugin for @atlaskit/editor-core

297 lines (292 loc) 13.6 kB
import _get from "@babel/runtime/helpers/get"; import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; import _createClass from "@babel/runtime/helpers/createClass"; import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn"; import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf"; import _inherits from "@babel/runtime/helpers/inherits"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; function _superPropGet(t, o, e, r) { var p = _get(_getPrototypeOf(1 & r ? t.prototype : t), o, e); return 2 & r && "function" == typeof p ? function (t) { return p.apply(e, t); } : p; } function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); } function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } import React 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 { getBrowserInfo } from '@atlaskit/editor-common/browser'; import ReactNodeView from '@atlaskit/editor-common/react-node-view'; import { findOverflowScrollParent, UnsupportedBlock } from '@atlaskit/editor-common/ui'; import { canRenderDatasource } from '@atlaskit/editor-common/utils'; 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 { Datasource } from '../nodeviews/datasource'; import { registerCard, removeCard as _removeCard } from '../pm-plugins/actions'; import { isDatasourceNode } from '../pm-plugins/utils'; import { Card } from './genericCard'; // eslint-disable-next-line @repo/internal/react/no-class-components export var BlockCardComponent = /*#__PURE__*/function (_React$PureComponent) { function BlockCardComponent(props) { var _this; _classCallCheck(this, BlockCardComponent); _this = _callSuper(this, BlockCardComponent, [props]); // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting _defineProperty(_this, "onResolve", function (data) { var _this$props = _this.props, getPos = _this$props.getPos, view = _this$props.view; 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 () { var pos = getPos(); if (typeof pos !== 'number') { return; } view.dispatch(registerCard({ title: title, url: url, pos: pos, id: _this.props.id })(view.state.tr)); })(); }); _defineProperty(_this, "removeCardDispatched", false); _defineProperty(_this, "gapCursorSpan", function () { var browser = getBrowserInfo(); // Don't render in EdgeHTMl version <= 18 (Edge version 44) // as it forces the edit popup to render 24px lower than it should if (browser.ie && browser.ie_version < 79) { return; } // render an empty span afterwards to get around Webkit bug // that puts caret in next editable text element return /*#__PURE__*/React.createElement("span", { contentEditable: true }); }); _defineProperty(_this, "onError", function (_ref) { var err = _ref.err; if (err) { throw err; } }); _this.scrollContainer = findOverflowScrollParent(props.view.dom) || undefined; return _this; } _inherits(BlockCardComponent, _React$PureComponent); return _createClass(BlockCardComponent, [{ key: "componentWillUnmount", value: function componentWillUnmount() { this.removeCard(); } }, { key: "removeCard", value: function removeCard() { if (this.removeCardDispatched) { return; } this.removeCardDispatched = true; var tr = this.props.view.state.tr; _removeCard({ id: this.props.id })(tr); this.props.view.dispatch(tr); } }, { key: "render", value: function render() { var _this$props2 = this.props, node = _this$props2.node, cardContext = _this$props2.cardContext, actionOptions = _this$props2.actionOptions, onClick = _this$props2.onClick, CompetitorPrompt = _this$props2.CompetitorPrompt, isPageSSRed = _this$props2.isPageSSRed; var _node$attrs = node.attrs, url = _node$attrs.url, data = _node$attrs.data; var cardInner = isPageSSRed ? /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(CardSSR, { key: url, url: url !== null && url !== void 0 ? url : data.url, container: this.scrollContainer, appearance: "block", onClick: onClick, onResolve: this.onResolve, onError: this.onError, platform: 'web', actionOptions: actionOptions, CompetitorPrompt: CompetitorPrompt, hideIconLoadingSkeleton: true }), this.gapCursorSpan()) : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(SmartCard, { key: url, url: url !== null && url !== void 0 ? url : data.url, container: this.scrollContainer, appearance: "block", onClick: onClick, onResolve: this.onResolve, onError: this.onError, platform: 'web', actionOptions: actionOptions, CompetitorPrompt: CompetitorPrompt }), this.gapCursorSpan()); // [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 /*#__PURE__*/React.createElement(SmartLinkDraggable, { url: url, appearance: SMART_LINK_APPEARANCE.BLOCK, source: SMART_LINK_DRAG_TYPES.EDITOR }, /*#__PURE__*/React.createElement("div", null, cardContext && cardContext.value ? /*#__PURE__*/React.createElement(cardContext.Provider, { value: cardContext.value }, cardInner) : data ? cardInner : null)); } }]); }(React.PureComponent); var WrappedBlockCard = Card(BlockCardComponent, UnsupportedBlock); export var BlockCard = /*#__PURE__*/function (_ReactNodeView) { function BlockCard() { var _this2; _classCallCheck(this, BlockCard); for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } _this2 = _callSuper(this, BlockCard, [].concat(args)); // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead _defineProperty(_this2, "id", uuid()); _defineProperty(_this2, "updateContentEditable", function (editorViewModeState, divElement) { divElement.contentEditable = (editorViewModeState === null || editorViewModeState === void 0 ? void 0 : editorViewModeState.mode) === 'view' ? 'false' : 'true'; }); return _this2; } _inherits(BlockCard, _ReactNodeView); return _createClass(BlockCard, [{ key: "createDomRef", value: function createDomRef() { var _this$reactComponentP, _this3 = this, _this$reactComponentP2; var domRef = document.createElement('div'); // workaround Chrome bug in https://product-fabric.atlassian.net/browse/ED-5379 // see also: https://github.com/ProseMirror/prosemirror/issues/884 this.unsubscribe = (_this$reactComponentP = this.reactComponentProps.pluginInjectionApi) === null || _this$reactComponentP === void 0 || (_this$reactComponentP = _this$reactComponentP.editorViewMode) === null || _this$reactComponentP === void 0 ? void 0 : _this$reactComponentP.sharedState.onChange(function (_ref2) { var nextSharedState = _ref2.nextSharedState; return _this3.updateContentEditable(nextSharedState, domRef); }); this.updateContentEditable((_this$reactComponentP2 = this.reactComponentProps.pluginInjectionApi) === null || _this$reactComponentP2 === void 0 || (_this$reactComponentP2 = _this$reactComponentP2.editorViewMode) === null || _this$reactComponentP2 === void 0 ? void 0 : _this$reactComponentP2.sharedState.currentState(), domRef); domRef.setAttribute('spellcheck', 'false'); return domRef; } }, { key: "validUpdate", value: // Need this function to check if the datasource attribute was added or not to a blockCard. // If so, we return false so we can get the node to re-render properly as a datasource node instead. // Otherwise, the node view will still consider the node as a blockCard and render a regular blockCard. function validUpdate(currentNode, newNode) { var isCurrentNodeBlockCard = !isDatasourceNode(currentNode); var isNewNodeDatasource = isDatasourceNode(newNode); // need to return falsy to update node return !(isCurrentNodeBlockCard && isNewNodeDatasource); } }, { key: "update", value: function update(node, decorations, _innerDecorations) { return _superPropGet(BlockCard, "update", this, 3)([node, decorations, _innerDecorations, this.validUpdate]); } }, { key: "render", value: function render() { var _this$reactComponentP3 = this.reactComponentProps, actionOptions = _this$reactComponentP3.actionOptions, pluginInjectionApi = _this$reactComponentP3.pluginInjectionApi, onClickCallback = _this$reactComponentP3.onClickCallback, CompetitorPrompt = _this$reactComponentP3.CompetitorPrompt, isPageSSRed = _this$reactComponentP3.isPageSSRed, provider = _this$reactComponentP3.provider; return /*#__PURE__*/React.createElement(WrappedBlockCard, { node: this.node, view: this.view, getPos: this.getPos, actionOptions: actionOptions, pluginInjectionApi: pluginInjectionApi, onClickCallback: onClickCallback, id: this.id, CompetitorPrompt: CompetitorPrompt, isPageSSRed: isPageSSRed, provider: provider }); } /** * Prevent ProseMirror from handling drag events on the smart-element-link, * allowing native drag to work so SmartLinkDraggable can intercept it. * @see {@link https://prosemirror.net/docs/ref/#view.NodeView.stopEvent} */ }, { key: "stopEvent", value: function stopEvent(event) { if (event.type === 'dragstart') { var target = event.target; if (target instanceof HTMLElement && target.closest('[data-smart-element-link]') && fg('cc_drag_and_drop_smart_link_from_content_to_tree')) { return true; } } return false; } }, { key: "destroy", value: function destroy() { var _this$unsubscribe; (_this$unsubscribe = this.unsubscribe) === null || _this$unsubscribe === void 0 || _this$unsubscribe.call(this); _superPropGet(BlockCard, "destroy", this, 3)([]); } }]); }(ReactNodeView); export var blockCardNodeView = function blockCardNodeView(_ref3) { var pmPluginFactoryParams = _ref3.pmPluginFactoryParams, actionOptions = _ref3.actionOptions, pluginInjectionApi = _ref3.pluginInjectionApi, onClickCallback = _ref3.onClickCallback, allowDatasource = _ref3.allowDatasource, inlineCardViewProducer = _ref3.inlineCardViewProducer, CompetitorPrompt = _ref3.CompetitorPrompt, isPageSSRed = _ref3.isPageSSRed, provider = _ref3.provider; return function (node, view, getPos, decorations) { var portalProviderAPI = pmPluginFactoryParams.portalProviderAPI, eventDispatcher = pmPluginFactoryParams.eventDispatcher; var reactComponentProps = { actionOptions: actionOptions, pluginInjectionApi: pluginInjectionApi, onClickCallback: onClickCallback, CompetitorPrompt: CompetitorPrompt, isPageSSRed: isPageSSRed, provider: provider }; var isDatasource = isDatasourceNode(node); if (isDatasource) { var _node$attrs2; if (allowDatasource && canRenderDatasource(node === null || node === void 0 || (_node$attrs2 = node.attrs) === null || _node$attrs2 === void 0 || (_node$attrs2 = _node$attrs2.datasource) === null || _node$attrs2 === void 0 ? void 0 : _node$attrs2.id)) { var datasourcePosition = typeof getPos === 'function' && getPos(); var datasourceResolvedPosition = datasourcePosition && view.state.doc.resolve(datasourcePosition); var isNodeNested = !!(datasourceResolvedPosition && datasourceResolvedPosition.depth > 0); return new Datasource({ node: node, view: view, getPos: getPos, portalProviderAPI: portalProviderAPI, eventDispatcher: eventDispatcher, pluginInjectionApi: pluginInjectionApi, isNodeNested: isNodeNested }).init(); } else { return inlineCardViewProducer(node, view, getPos, decorations); } } return new BlockCard(node, view, getPos, portalProviderAPI, eventDispatcher, reactComponentProps, undefined).init(); }; };