UNPKG

@atlaskit/editor-plugin-media

Version:

Media plugin for @atlaskit/editor-core

209 lines (207 loc) 9.26 kB
import React from 'react'; import memoizeOne from 'memoize-one'; import { wrappedLayouts } from '@atlaskit/editor-common/media-single'; import { mediaAndEmbedToolbarMessages } from '@atlaskit/editor-common/messages'; import { nonWrappedLayouts } from '@atlaskit/editor-common/utils'; import { findParentNodeOfType, findSelectedNodeOfType, removeParentNodeOfType, removeSelectedNode } from '@atlaskit/editor-prosemirror/utils'; import { akEditorFullWidthLayoutWidth } from '@atlaskit/editor-shared-styles'; import ImageInlineIcon from '@atlaskit/icon/core/image-inline'; import MaximizeIcon from '@atlaskit/icon/core/maximize'; import { getMediaClient } from '@atlaskit/media-client-react'; import { messages } from '@atlaskit/media-ui'; import { isVideo } from '../../pm-plugins/utils/media-single'; import { changeMediaInlineToMediaSingle, changeMediaSingleToMediaInline } from './commands'; export const isExternalMedia = attrs => { return attrs.type === 'external'; }; const getSelectedMediaContainerNodeAttrs = mediaPluginState => { var _mediaPluginState$sel; const selectedNode = (_mediaPluginState$sel = mediaPluginState.selectedMediaContainerNode) === null || _mediaPluginState$sel === void 0 ? void 0 : _mediaPluginState$sel.call(mediaPluginState); if (selectedNode && selectedNode.attrs) { return selectedNode.attrs; } return null; }; export const getSelectedNearestMediaContainerNodeAttrsFunction = selectedMediaContainerNode => { const selectedNode = selectedMediaContainerNode === null || selectedMediaContainerNode === void 0 ? void 0 : selectedMediaContainerNode(); if (selectedNode) { switch (selectedNode.type.name) { case 'mediaSingle': { const childNode = selectedNode.firstChild; return childNode === null || childNode === void 0 ? void 0 : childNode.attrs; } default: return selectedNode.attrs; } } return null; }; export const getSelectedNearestMediaContainerNodeAttrs = mediaPluginState => { return getSelectedNearestMediaContainerNodeAttrsFunction(mediaPluginState.selectedMediaContainerNode); }; export const getIsDownloadDisabledByDataSecurityPolicy = mediaPluginState => { var _mediaPluginState$med; const enforceMediaDataSecurityPolicy = mediaPluginState === null || mediaPluginState === void 0 ? void 0 : (_mediaPluginState$med = mediaPluginState.mediaClientConfig) === null || _mediaPluginState$med === void 0 ? void 0 : _mediaPluginState$med.enforceDataSecurityPolicy; return typeof enforceMediaDataSecurityPolicy === 'boolean' ? enforceMediaDataSecurityPolicy : false; }; export const downloadMedia = async (mediaPluginState, isViewMode) => { try { const selectedNodeAttrs = isViewMode ? getSelectedNearestMediaContainerNodeAttrs(mediaPluginState) : getSelectedMediaContainerNodeAttrs(mediaPluginState); if (selectedNodeAttrs && mediaPluginState.mediaClientConfig && !isExternalMedia(selectedNodeAttrs)) { const { id, collection = '' } = selectedNodeAttrs; const mediaClient = getMediaClient(mediaPluginState.mediaClientConfig); const fileState = await mediaClient.file.getCurrentState(id, { collectionName: collection }); const fileName = fileState.status === 'error' ? undefined : fileState.name; mediaClient.file.downloadBinary(id, fileName, collection); } return true; } catch (err) { return false; } }; export const removeMediaGroupNode = state => { const { mediaGroup } = state.schema.nodes; const mediaGroupParent = findParentNodeOfType(mediaGroup)(state.selection); let tr = state.tr; // If it is the last media group in filmstrip, remove the entire filmstrip if (mediaGroupParent && mediaGroupParent.node.childCount === 1) { tr = removeParentNodeOfType(mediaGroup)(tr); } else { tr = removeSelectedNode(tr); } return tr; }; export const getSelectedMediaSingle = state => { const { mediaSingle } = state.schema.nodes; return findSelectedNodeOfType(mediaSingle)(state.selection) || findParentNodeOfType(mediaSingle)(state.selection); }; export const getPixelWidthOfElement = memoizeOne((editorView, pos, mediaWidth) => { const domNode = editorView.nodeDOM(pos); if (domNode instanceof HTMLElement) { return domNode.offsetWidth; } return mediaWidth; }); export const calcNewLayout = (width, layout, contentWidth, fullWidthMode = false, isNested = false) => { const isWrappedLayout = wrappedLayouts.indexOf(layout) > -1; //See flowchart for layout logic: https://hello.atlassian.net/wiki/spaces/TWPCP/whiteboard/2969594044 if (width >= akEditorFullWidthLayoutWidth) { // If width is greater than or equal to full editor width return 'full-width'; } if (fullWidthMode) { // If under editor full width mode return isWrappedLayout ? layout : 'center'; } if (width > contentWidth && !isNested) { // If width is greater than content length and not nested return 'wide'; } return isNested || isWrappedLayout && width !== contentWidth ? layout : 'center'; }; let maxToolbarFitWidth = 0; export const getMaxToolbarWidth = () => { // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting const toolbar = document.querySelector(`div[aria-label="Media floating controls"]`); const toolbarWidth = toolbar === null || toolbar === void 0 ? void 0 : toolbar.getBoundingClientRect().width; if (!toolbar) { maxToolbarFitWidth = 0; } if (toolbarWidth && toolbarWidth > maxToolbarFitWidth) { maxToolbarFitWidth = toolbarWidth; } return maxToolbarFitWidth; }; export const getSelectedLayoutIcon = (layoutIcons, selectedNode) => { const selectedLayout = selectedNode.attrs.layout; return layoutIcons.find(icon => icon.value === (nonWrappedLayouts.includes(selectedLayout) ? 'center' : selectedLayout)); }; /** * Check if 'original size' and 'inline' buttons can be shown in the toolbar for a given mediaSingle node. * @param mediaSingleNode node to be checked */ export const canShowSwitchButtons = mediaSingleNode => { if (mediaSingleNode) { const mediaNode = mediaSingleNode.content.firstChild; return mediaNode && !isVideo(mediaNode.attrs.__fileMimeType); } return false; }; export const updateToFullHeightSeparator = items => { const lastItem = items.at(-1); if ((lastItem === null || lastItem === void 0 ? void 0 : lastItem.type) === 'separator') { lastItem.fullHeight = true; } else if (items.length) { items.push({ type: 'separator', fullHeight: true }); } }; export const getMediaSingleAndMediaInlineSwitcherDropdown = (nodeType, intl, pluginInjectionApi, hasCaption = false) => { var _pluginInjectionApi$a, _pluginInjectionApi$a2; const mediaInlineImageTitle = intl.formatMessage(mediaAndEmbedToolbarMessages.changeToMediaInlineImage); const mediaSingleTitle = intl.formatMessage(mediaAndEmbedToolbarMessages.changeToMediaSingle); const inlineSwitcherTitle = intl.formatMessage(hasCaption ? mediaAndEmbedToolbarMessages.changeToMediaInlineImageCaptionWarning : mediaAndEmbedToolbarMessages.changeToMediaInlineImage); const InlineIcon = () => /*#__PURE__*/React.createElement(ImageInlineIcon, { color: "currentColor", spacing: "spacious", label: "" }); const BlockIcon = () => /*#__PURE__*/React.createElement(MaximizeIcon, { color: "currentColor", spacing: "spacious", label: "" }); const dropdownConfig = { inline: { handleInlineButtonClick: () => { return true; }, handleBlockButtonClick: changeMediaInlineToMediaSingle(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$a = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a === void 0 ? void 0 : _pluginInjectionApi$a.actions, pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : pluginInjectionApi.width.sharedState.currentState()), id: 'media-inline-to-block-toolbar-item', icon: InlineIcon }, block: { handleInlineButtonClick: changeMediaSingleToMediaInline(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$a2 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a2 === void 0 ? void 0 : _pluginInjectionApi$a2.actions), handleBlockButtonClick: () => { return true; }, id: 'media-block-to-inline-toolbar-item', icon: BlockIcon } }; const options = [{ id: 'editor.media.convert.mediainline', title: mediaInlineImageTitle, onClick: dropdownConfig[nodeType].handleInlineButtonClick, selected: nodeType === 'inline', icon: /*#__PURE__*/React.createElement(InlineIcon, null), tooltip: hasCaption ? inlineSwitcherTitle : undefined }, { id: 'editor.media.convert.mediasingle', title: mediaSingleTitle, onClick: dropdownConfig[nodeType].handleBlockButtonClick, selected: nodeType === 'block', icon: /*#__PURE__*/React.createElement(BlockIcon, null) }]; return { id: dropdownConfig[nodeType].id, testId: 'media-inline-to-block-dropdown', title: intl.formatMessage(messages.sizeOptions), type: 'dropdown', options, icon: dropdownConfig[nodeType].icon }; };