UNPKG

@atlaskit/editor-plugin-media

Version:

Media plugin for @atlaskit/editor-core

324 lines (319 loc) 16.1 kB
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 _get from "@babel/runtime/helpers/get"; import _inherits from "@babel/runtime/helpers/inherits"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; 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; })(); } 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; } /** * @jsxRuntime classic * @jsx jsx * @jsxFrag */ import { useCallback, useMemo } from 'react'; // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled, @typescript-eslint/consistent-type-imports import { jsx } from '@emotion/react'; import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks'; import { WithProviders } from '@atlaskit/editor-common/provider-factory'; import ReactNodeView from '@atlaskit/editor-common/react-node-view'; import { useSharedPluginStateSelector } from '@atlaskit/editor-common/use-shared-plugin-state-selector'; // eslint-disable-next-line @typescript-eslint/consistent-type-imports import { isNodeSelectedOrInRange } from '@atlaskit/editor-common/utils'; import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals'; import { MEDIA_CONTENT_WRAP_CLASS_NAME } from '../pm-plugins/main'; import { MediaSingleNodeNext } from './mediaSingleNext'; var selector = function selector(states) { var _states$mediaState, _states$mediaState2, _states$annotationSta, _states$annotationSta2, _states$widthState, _states$widthState2, _states$editorDisable, _states$editorViewMod; return { mediaProviderPromise: (_states$mediaState = states.mediaState) === null || _states$mediaState === void 0 ? void 0 : _states$mediaState.mediaProvider, addPendingTask: (_states$mediaState2 = states.mediaState) === null || _states$mediaState2 === void 0 ? void 0 : _states$mediaState2.addPendingTask, isDrafting: (_states$annotationSta = states.annotationState) === null || _states$annotationSta === void 0 ? void 0 : _states$annotationSta.isDrafting, targetNodeId: (_states$annotationSta2 = states.annotationState) === null || _states$annotationSta2 === void 0 ? void 0 : _states$annotationSta2.targetNodeId, width: (_states$widthState = states.widthState) === null || _states$widthState === void 0 ? void 0 : _states$widthState.width, lineLength: (_states$widthState2 = states.widthState) === null || _states$widthState2 === void 0 ? void 0 : _states$widthState2.lineLength, editorDisabled: (_states$editorDisable = states.editorDisabledState) === null || _states$editorDisable === void 0 ? void 0 : _states$editorDisable.editorDisabled, viewMode: (_states$editorViewMod = states.editorViewModeState) === null || _states$editorViewMod === void 0 ? void 0 : _states$editorViewMod.mode }; }; var MediaSingleNodeWrapper = function MediaSingleNodeWrapper(_ref) { var pluginInjectionApi = _ref.pluginInjectionApi, contextIdentifierProvider = _ref.contextIdentifierProvider, node = _ref.node, getPos = _ref.getPos, mediaOptions = _ref.mediaOptions, view = _ref.view, fullWidthMode = _ref.fullWidthMode, selected = _ref.selected, eventDispatcher = _ref.eventDispatcher, dispatchAnalyticsEvent = _ref.dispatchAnalyticsEvent, forwardRef = _ref.forwardRef, editorAppearance = _ref.editorAppearance; var _useSharedPluginState = useSharedPluginStateWithSelector(pluginInjectionApi, ['width', 'media', 'annotation', 'editorDisabled', 'editorViewMode'], selector), mediaProviderPromise = _useSharedPluginState.mediaProviderPromise, addPendingTask = _useSharedPluginState.addPendingTask, isDrafting = _useSharedPluginState.isDrafting, targetNodeId = _useSharedPluginState.targetNodeId, width = _useSharedPluginState.width, lineLength = _useSharedPluginState.lineLength, editorDisabled = _useSharedPluginState.editorDisabled, viewMode = _useSharedPluginState.viewMode; var interactionState = useSharedPluginStateSelector(pluginInjectionApi, 'interaction.interactionState'); var mediaProvider = useMemo(function () { return mediaProviderPromise ? Promise.resolve(mediaProviderPromise) : undefined; }, [mediaProviderPromise]); var isSelectedAndInteracted = useCallback(function () { return Boolean(selected() && interactionState !== 'hasNotHadInteraction'); }, [interactionState, selected]); return jsx(MediaSingleNodeNext, { width: width || 0, lineLength: lineLength || 0, node: node, getPos: getPos, mediaProvider: mediaProvider, contextIdentifierProvider: contextIdentifierProvider, mediaOptions: mediaOptions, view: view, fullWidthMode: fullWidthMode, selected: isSelectedAndInteracted, eventDispatcher: eventDispatcher, addPendingTask: addPendingTask, isDrafting: isDrafting, targetNodeId: targetNodeId, dispatchAnalyticsEvent: dispatchAnalyticsEvent, forwardRef: forwardRef, pluginInjectionApi: pluginInjectionApi, editorDisabled: editorDisabled, editorViewMode: viewMode === 'view', editorAppearance: editorAppearance }); }; var MediaSingleNodeView = /*#__PURE__*/function (_ReactNodeView) { function MediaSingleNodeView() { var _this; _classCallCheck(this, MediaSingleNodeView); for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } _this = _callSuper(this, MediaSingleNodeView, [].concat(args)); _defineProperty(_this, "lastOffsetLeft", 0); _defineProperty(_this, "forceViewUpdate", false); _defineProperty(_this, "selectionType", null); _defineProperty(_this, "hasResized", false); _defineProperty(_this, "checkAndUpdateSelectionType", function () { var getPos = _this.getPos; var selection = _this.view.state.selection; /** * ED-19831 * There is a getPos issue coming from this code. We need to apply this workaround for now and apply a patch * directly to confluence since this bug is now in production. */ var pos; try { pos = getPos ? getPos() : undefined; } catch (_unused) { pos = undefined; } var isNodeSelected = isNodeSelectedOrInRange(selection.$anchor.pos, selection.$head.pos, pos, _this.node.nodeSize); _this.selectionType = isNodeSelected; return isNodeSelected; }); _defineProperty(_this, "isNodeSelected", function () { _this.checkAndUpdateSelectionType(); return _this.selectionType !== null; }); return _this; } _inherits(MediaSingleNodeView, _ReactNodeView); return _createClass(MediaSingleNodeView, [{ key: "createDomRef", value: function createDomRef() { var _this$reactComponentP, _this$reactComponentP2; var domRef = document.createElement('div'); // control the domRef contentEditable attribute based on the editor view mode this.unsubscribeToViewModeChange = this.subscribeToViewModeChange(domRef); var initialViewMode = (_this$reactComponentP = this.reactComponentProps.pluginInjectionApi) === null || _this$reactComponentP === void 0 || (_this$reactComponentP = _this$reactComponentP.editorViewMode) === null || _this$reactComponentP === void 0 || (_this$reactComponentP = _this$reactComponentP.sharedState.currentState()) === null || _this$reactComponentP === void 0 ? void 0 : _this$reactComponentP.mode; this.updateDomRefContentEditable(domRef, initialViewMode); if ((_this$reactComponentP2 = this.reactComponentProps.mediaOptions) !== null && _this$reactComponentP2 !== void 0 && _this$reactComponentP2.allowPixelResizing) { domRef.classList.add('media-extended-resize-experience'); } domRef.setAttribute('data-media-vc-wrapper', 'true'); return domRef; } }, { key: "getContentDOM", value: function getContentDOM() { var dom = document.createElement('div'); dom.classList.add(MEDIA_CONTENT_WRAP_CLASS_NAME); return { dom: dom }; } }, { key: "viewShouldUpdate", value: function viewShouldUpdate(nextNode) { if (this.forceViewUpdate) { this.forceViewUpdate = false; return true; } if (this.node.attrs !== nextNode.attrs) { return true; } if (this.selectionType !== this.checkAndUpdateSelectionType()) { return true; } if (this.node.childCount !== nextNode.childCount) { return true; } return _superPropGet(MediaSingleNodeView, "viewShouldUpdate", this, 3)([nextNode]); } }, { key: "subscribeToViewModeChange", value: function subscribeToViewModeChange(domRef) { var _this$reactComponentP3, _this2 = this; return (_this$reactComponentP3 = this.reactComponentProps.pluginInjectionApi) === null || _this$reactComponentP3 === void 0 || (_this$reactComponentP3 = _this$reactComponentP3.editorViewMode) === null || _this$reactComponentP3 === void 0 ? void 0 : _this$reactComponentP3.sharedState.onChange(function (viewModeState) { var _viewModeState$nextSh; _this2.updateDomRefContentEditable(domRef, (_viewModeState$nextSh = viewModeState.nextSharedState) === null || _viewModeState$nextSh === void 0 ? void 0 : _viewModeState$nextSh.mode); }); } }, { key: "updateDomRefContentEditable", value: function updateDomRefContentEditable(domRef, editorViewMode) { var _this$reactComponentP4; // if the editor is in view mode, we should not allow editing if (editorViewMode === 'view') { domRef.contentEditable = 'false'; return; } // if the editor is in edit mode, we should allow editing if the media options allow it if ((_this$reactComponentP4 = this.reactComponentProps.mediaOptions) !== null && _this$reactComponentP4 !== void 0 && _this$reactComponentP4.allowMediaSingleEditable) { // workaround Chrome bug in https://product-fabric.atlassian.net/browse/ED-5379 // see also: https://github.com/ProseMirror/prosemirror/issues/884 domRef.contentEditable = 'true'; } } }, { key: "getNodeMediaId", value: function getNodeMediaId(node) { if (node.firstChild) { return node.firstChild.attrs.id; } return undefined; } }, { key: "stopEvent", value: function stopEvent(event) { if (this.isNodeSelected() && event instanceof KeyboardEvent && (event === null || event === void 0 ? void 0 : event.target) instanceof HTMLElement) { var targetType = event.target.type; if (event.key === 'Enter' && targetType === 'button') { return true; } } return false; } }, { key: "update", value: function update(node, decorations, _innerDecorations, isValidUpdate) { var _this3 = this; if (!isValidUpdate) { isValidUpdate = function isValidUpdate(currentNode, newNode) { return _this3.getNodeMediaId(currentNode) === _this3.getNodeMediaId(newNode); }; } // Detect mediaSingle width attribute changes and signal child media node to update if (!this.hasResized && this.node.attrs.width !== node.attrs.width && expValEquals('platform_editor_media_vc_fixes', 'isEnabled', true)) { var target = this.dom.querySelector('div[data-prosemirror-node-name="media"]'); target === null || target === void 0 || target.dispatchEvent(new CustomEvent('resized')); } return _superPropGet(MediaSingleNodeView, "update", this, 3)([node, decorations, _innerDecorations, isValidUpdate]); } }, { key: "render", value: function render(props, forwardRef) { var _this4 = this; var _this$reactComponentP5 = this.reactComponentProps, eventDispatcher = _this$reactComponentP5.eventDispatcher, fullWidthMode = _this$reactComponentP5.fullWidthMode, providerFactory = _this$reactComponentP5.providerFactory, mediaOptions = _this$reactComponentP5.mediaOptions, dispatchAnalyticsEvent = _this$reactComponentP5.dispatchAnalyticsEvent, pluginInjectionApi = _this$reactComponentP5.pluginInjectionApi, editorAppearance = _this$reactComponentP5.editorAppearance; // getPos is a boolean for marks, since this is a node we know it must be a function var getPos = this.getPos; return jsx(WithProviders // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) , { providers: ['contextIdentifierProvider'], providerFactory: providerFactory // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) , renderNode: function renderNode(_ref2) { var contextIdentifierProvider = _ref2.contextIdentifierProvider; return jsx(MediaSingleNodeWrapper, { pluginInjectionApi: pluginInjectionApi, contextIdentifierProvider: contextIdentifierProvider, node: _this4.node, getPos: getPos, mediaOptions: mediaOptions, view: _this4.view, fullWidthMode: fullWidthMode, selected: _this4.isNodeSelected, eventDispatcher: eventDispatcher // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion , dispatchAnalyticsEvent: dispatchAnalyticsEvent // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion , forwardRef: forwardRef, editorAppearance: editorAppearance }); } }); } }, { key: "ignoreMutation", value: function ignoreMutation() { // DOM has changed; recalculate if we need to re-render if (this.dom) { // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting var offsetLeft = this.dom.offsetLeft; if (offsetLeft !== this.lastOffsetLeft) { this.lastOffsetLeft = offsetLeft; this.forceViewUpdate = true; this.update(this.node, [], undefined, function () { return true; }); } } return true; } }, { key: "destroy", value: function destroy() { var _this$unsubscribeToVi; (_this$unsubscribeToVi = this.unsubscribeToViewModeChange) === null || _this$unsubscribeToVi === void 0 || _this$unsubscribeToVi.call(this); } }]); }(ReactNodeView); export var ReactMediaSingleNode = function ReactMediaSingleNode(portalProviderAPI, eventDispatcher, providerFactory, pluginInjectionApi, dispatchAnalyticsEvent) { var mediaOptions = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {}; return function (node, view, getPos) { return new MediaSingleNodeView(node, view, getPos, portalProviderAPI, eventDispatcher, { eventDispatcher: eventDispatcher, fullWidthMode: mediaOptions.fullWidthEnabled, providerFactory: providerFactory, mediaOptions: mediaOptions, dispatchAnalyticsEvent: dispatchAnalyticsEvent, isCopyPasteEnabled: mediaOptions.isCopyPasteEnabled, pluginInjectionApi: pluginInjectionApi, editorAppearance: mediaOptions.editorAppearance }).init(); }; };