UNPKG

@atlaskit/editor-plugin-media

Version:

Media plugin for @atlaskit/editor-core

369 lines (367 loc) 17.6 kB
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator"; import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; import _createClass from "@babel/runtime/helpers/createClass"; import _assertThisInitialized from "@babel/runtime/helpers/assertThisInitialized"; import _inherits from "@babel/runtime/helpers/inherits"; import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn"; import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; import _regeneratorRuntime from "@babel/runtime/regenerator"; function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } import React from 'react'; import { injectIntl } from 'react-intl-next'; import { useSharedPluginState } from '@atlaskit/editor-common/hooks'; import { WithProviders } from '@atlaskit/editor-common/provider-factory'; import ReactNodeView from '@atlaskit/editor-common/react-node-view'; import { isNodeSelectedOrInRange, SelectedState, setNodeSelection } from '@atlaskit/editor-common/utils'; import EditorCloseIcon from '@atlaskit/icon/glyph/editor/close'; import { getMediaFeatureFlag } from '@atlaskit/media-common'; import { Filmstrip } from '@atlaskit/media-filmstrip'; import { stateKey as mediaStateKey } from '../pm-plugins/plugin-key'; import { MediaNodeUpdater } from './mediaNodeUpdater'; import { messages } from './messages'; var isMediaGroupSelectedFromProps = function isMediaGroupSelectedFromProps(props) { /** * 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 = props.getPos ? props.getPos() : undefined; } catch (e) { pos = undefined; } if (typeof pos !== 'number') { return false; } return isNodeSelectedOrInRange(props.anchorPos, props.headPos, pos, props.node.nodeSize); }; var hasSelectionChanged = function hasSelectionChanged(oldProps, newProps) { if (isMediaGroupSelectedFromProps(oldProps) !== isMediaGroupSelectedFromProps(newProps)) { return true; } if (isMediaGroupSelectedFromProps(newProps) === SelectedState.selectedInside) { return oldProps.anchorPos !== newProps.anchorPos; } return false; }; // eslint-disable-next-line @repo/internal/react/no-class-components var MediaGroup = /*#__PURE__*/function (_React$Component) { _inherits(MediaGroup, _React$Component); var _super = _createSuper(MediaGroup); function MediaGroup(_props) { var _this; _classCallCheck(this, MediaGroup); _this = _super.call(this, _props); _defineProperty(_assertThisInitialized(_this), "state", { viewMediaClientConfig: undefined }); _defineProperty(_assertThisInitialized(_this), "updateNodeAttrs", function (props, node, getPos) { var view = props.view, mediaProvider = props.mediaProvider, contextIdentifierProvider = props.contextIdentifierProvider; var mediaNodeUpdater = new MediaNodeUpdater({ view: view, mediaProvider: mediaProvider, contextIdentifierProvider: contextIdentifierProvider, node: node, isMediaSingle: false }); mediaNodeUpdater.updateNodeAttrs(getPos); }); _defineProperty(_assertThisInitialized(_this), "setMediaItems", function (props) { var _this$mediaPluginStat; var updatedAttrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var node = props.node; var oldMediaNodes = _this.mediaNodes; _this.mediaNodes = []; node.forEach(function (item, childOffset) { var getPos = function getPos() { var pos = props.getPos(); if (typeof pos !== 'number') { // That may seems weird, but the previous type wasn't match with the real ProseMirror code. And a lot of Media API was built expecting a number // Because the original code would return NaN on runtime // We are just make it explict now. // We may run a deep investagation on Media code to figure out a better fix. But, for now, we want to keep the current behavior. // TODO: ED-13910 prosemirror-bump leftovers return NaN; } return pos + childOffset + 1; }; _this.mediaNodes.push(item); if (updatedAttrs) { _this.updateNodeAttrs(props, item, getPos); } }); (_this$mediaPluginStat = _this.mediaPluginState) === null || _this$mediaPluginStat === void 0 || _this$mediaPluginStat.handleMediaGroupUpdate(oldMediaNodes, _this.mediaNodes); }); _defineProperty(_assertThisInitialized(_this), "getIdentifier", function (item) { if (item.attrs.type === 'external') { return { mediaItemType: 'external-image', dataURI: item.attrs.url }; } return { id: item.attrs.id, mediaItemType: 'file', collectionName: item.attrs.collection }; }); _defineProperty(_assertThisInitialized(_this), "isNodeSelected", function (nodePos) { var selected = isMediaGroupSelectedFromProps(_this.props); if (selected === SelectedState.selectedInRange) { return true; } if (selected === SelectedState.selectedInside && _this.props.anchorPos === nodePos) { return true; } return false; }); _defineProperty(_assertThisInitialized(_this), "renderChildNodes", function () { var viewMediaClientConfig = _this.state.viewMediaClientConfig; var _this$props = _this.props, getPos = _this$props.getPos, allowLazyLoading = _this$props.allowLazyLoading, disabled = _this$props.disabled, mediaOptions = _this$props.mediaOptions; var items = _this.mediaNodes.map(function (item, idx) { // We declared this to get a fresh position every time var getNodePos = function getNodePos() { var pos = getPos(); if (typeof pos !== 'number') { // That may seems weird, but the previous type wasn't match with the real ProseMirror code. And a lot of Media API was built expecting a number // Because the original code would return NaN on runtime // We are just make it explict now. // We may run a deep investagation on Media code to figure out a better fix. But, for now, we want to keep the current behavior. // TODO: ED-13910 prosemirror-bump leftovers return NaN; } return pos + idx + 1; }; // Media Inline creates a floating toolbar with the same options, excludes these options if enabled var mediaInlineOptions = function mediaInlineOptions() { var allowMediaInline = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; if (!allowMediaInline) { return { shouldEnableDownloadButton: mediaOptions.enableDownloadButton, actions: [{ handler: disabled || !_this.mediaPluginState ? function () {} : _this.mediaPluginState.handleMediaNodeRemoval.bind(null, undefined, getNodePos), icon: /*#__PURE__*/React.createElement(EditorCloseIcon, { label: _this.props.intl.formatMessage(messages.mediaGroupDeleteLabel) }) }] }; } }; return _objectSpread({ identifier: _this.getIdentifier(item), isLazy: allowLazyLoading, selected: _this.isNodeSelected(getNodePos()), onClick: function onClick() { setNodeSelection(_this.props.view, getNodePos()); } }, mediaInlineOptions(getMediaFeatureFlag('mediaInline', mediaOptions.featureFlags))); }); return /*#__PURE__*/React.createElement(Filmstrip, { items: items, mediaClientConfig: viewMediaClientConfig, featureFlags: mediaOptions.featureFlags }); }); _this.mediaNodes = []; _this.mediaPluginState = mediaStateKey.getState(_props.view.state); _this.setMediaItems(_props); _this.state = { viewMediaClientConfig: undefined }; return _this; } _createClass(MediaGroup, [{ key: "componentDidMount", value: function componentDidMount() { var _this2 = this; this.updateMediaClientConfig(); this.mediaNodes.forEach( /*#__PURE__*/function () { var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(node) { var _this2$props, view, mediaProvider, contextIdentifierProvider, mediaNodeUpdater, getPos, contextId, hasDifferentContextId; return _regeneratorRuntime.wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: if (!(node.attrs.type === 'external')) { _context.next = 2; break; } return _context.abrupt("return"); case 2: _this2$props = _this2.props, view = _this2$props.view, mediaProvider = _this2$props.mediaProvider, contextIdentifierProvider = _this2$props.contextIdentifierProvider; mediaNodeUpdater = new MediaNodeUpdater({ view: view, mediaProvider: mediaProvider, contextIdentifierProvider: contextIdentifierProvider, node: node, isMediaSingle: false }); getPos = function getPos() { var pos = _this2.props.getPos(); if (typeof pos !== 'number') { // That may seems weird, but the previous type wasn't match with the real ProseMirror code. And a lot of Media API was built expecting a number // Because the original code would return NaN on runtime // We are just make it explict now. // We may run a deep investagation on Media code to figure out a better fix. But, for now, we want to keep the current behavior. // TODO: ED-13910 prosemirror-bump leftovers return NaN; } return pos + 1; }; contextId = mediaNodeUpdater.getNodeContextId(); if (contextId) { _context.next = 9; break; } _context.next = 9; return mediaNodeUpdater.updateNodeContextId(getPos); case 9: _context.next = 11; return mediaNodeUpdater.hasDifferentContextId(); case 11: hasDifferentContextId = _context.sent; if (!hasDifferentContextId) { _context.next = 15; break; } _context.next = 15; return mediaNodeUpdater.copyNodeFromPos(getPos, { traceId: node.attrs.__mediaTraceId }); case 15: case "end": return _context.stop(); } }, _callee); })); return function (_x) { return _ref.apply(this, arguments); }; }()); } }, { key: "componentWillUnmount", value: function componentWillUnmount() { var _this$mediaPluginStat2; (_this$mediaPluginStat2 = this.mediaPluginState) === null || _this$mediaPluginStat2 === void 0 || _this$mediaPluginStat2.handleMediaGroupUpdate(this.mediaNodes, []); } }, { key: "UNSAFE_componentWillReceiveProps", value: function UNSAFE_componentWillReceiveProps(props) { this.updateMediaClientConfig(); this.setMediaItems(props, props.isCopyPasteEnabled || props.isCopyPasteEnabled === undefined); } }, { key: "shouldComponentUpdate", value: function shouldComponentUpdate(nextProps) { var _this$mediaPluginStat3; if (hasSelectionChanged(this.props, nextProps) || this.props.node !== nextProps.node || this.state.viewMediaClientConfig !== ((_this$mediaPluginStat3 = this.mediaPluginState) === null || _this$mediaPluginStat3 === void 0 ? void 0 : _this$mediaPluginStat3.mediaClientConfig)) { return true; } return false; } }, { key: "updateMediaClientConfig", value: function updateMediaClientConfig() { var viewMediaClientConfig = this.state.viewMediaClientConfig; var _ref2 = this.mediaPluginState || {}, mediaClientConfig = _ref2.mediaClientConfig; if (!viewMediaClientConfig && mediaClientConfig) { this.setState({ viewMediaClientConfig: mediaClientConfig }); } } }, { key: "render", value: function render() { return this.renderChildNodes(); } }]); return MediaGroup; }(React.Component); _defineProperty(MediaGroup, "displayName", 'MediaGroup'); var IntlMediaGroup = injectIntl(MediaGroup); export default IntlMediaGroup; function MediaGroupNodeViewInternal(_ref3) { var renderFn = _ref3.renderFn, pluginInjectionApi = _ref3.pluginInjectionApi; var _useSharedPluginState = useSharedPluginState(pluginInjectionApi, ['editorDisabled']), editorDisabledPlugin = _useSharedPluginState.editorDisabledState; return renderFn({ editorDisabledPlugin: editorDisabledPlugin }); } var MediaGroupNodeView = /*#__PURE__*/function (_ReactNodeView) { _inherits(MediaGroupNodeView, _ReactNodeView); var _super2 = _createSuper(MediaGroupNodeView); function MediaGroupNodeView() { _classCallCheck(this, MediaGroupNodeView); return _super2.apply(this, arguments); } _createClass(MediaGroupNodeView, [{ key: "render", value: function render(props, forwardRef) { var _this3 = this; var providerFactory = props.providerFactory, mediaOptions = props.mediaOptions, pluginInjectionApi = props.pluginInjectionApi; var getPos = this.getPos; return /*#__PURE__*/React.createElement(WithProviders, { providers: ['mediaProvider', 'contextIdentifierProvider'], providerFactory: providerFactory, renderNode: function renderNode(_ref4) { var mediaProvider = _ref4.mediaProvider, contextIdentifierProvider = _ref4.contextIdentifierProvider; var renderFn = function renderFn(_ref5) { var editorDisabledPlugin = _ref5.editorDisabledPlugin; if (!mediaProvider) { return null; } return /*#__PURE__*/React.createElement(IntlMediaGroup, { node: _this3.node, getPos: getPos, view: _this3.view, forwardRef: forwardRef, disabled: (editorDisabledPlugin || {}).editorDisabled, allowLazyLoading: mediaOptions.allowLazyLoading, mediaProvider: mediaProvider, contextIdentifierProvider: contextIdentifierProvider, isCopyPasteEnabled: mediaOptions.isCopyPasteEnabled, anchorPos: _this3.view.state.selection.$anchor.pos, headPos: _this3.view.state.selection.$head.pos, mediaOptions: mediaOptions }); }; return /*#__PURE__*/React.createElement(MediaGroupNodeViewInternal, { renderFn: renderFn, pluginInjectionApi: pluginInjectionApi }); } }); } }]); return MediaGroupNodeView; }(ReactNodeView); export var ReactMediaGroupNode = function ReactMediaGroupNode(portalProviderAPI, eventDispatcher, providerFactory) { var mediaOptions = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; var pluginInjectionApi = arguments.length > 4 ? arguments[4] : undefined; return function (node, view, getPos) { var hasIntlContext = true; return new MediaGroupNodeView(node, view, getPos, portalProviderAPI, eventDispatcher, { providerFactory: providerFactory, mediaOptions: mediaOptions, pluginInjectionApi: pluginInjectionApi }, undefined, undefined, undefined, hasIntlContext).init(); }; };