UNPKG

@atlaskit/editor-plugin-media

Version:

Media plugin for @atlaskit/editor-core

965 lines (959 loc) 48 kB
import _extends from "@babel/runtime/helpers/extends"; import React from 'react'; import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics'; import { alignmentIcons, buildLayoutButtons, buildLayoutDropdown, layoutToMessages, wrappingIcons } from '@atlaskit/editor-common/card'; import { withAnalytics } from '@atlaskit/editor-common/editor-analytics'; import { mediaInlineImagesEnabled } from '@atlaskit/editor-common/media-inline'; import commonMessages, { cardMessages, mediaAndEmbedToolbarMessages } from '@atlaskit/editor-common/messages'; import { areToolbarFlagsEnabled } from '@atlaskit/editor-common/toolbar-flag-check'; import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity'; import { NodeSelection } from '@atlaskit/editor-prosemirror/state'; import { contains, findParentNodeOfType, hasParentNodeOfType, removeSelectedNode } from '@atlaskit/editor-prosemirror/utils'; import { akEditorSelectedNodeClassName } from '@atlaskit/editor-shared-styles'; import ImageCropIcon from '@atlaskit/icon-lab/core/image-crop'; import CopyIcon from '@atlaskit/icon/core/copy'; import DeleteIcon from '@atlaskit/icon/core/delete'; import DownloadIcon from '@atlaskit/icon/core/download'; import GrowDiagonalIcon from '@atlaskit/icon/core/grow-diagonal'; import ImageFullscreenIcon from '@atlaskit/icon/core/image-fullscreen'; import ImageInlineIcon from '@atlaskit/icon/core/image-inline'; import MaximizeIcon from '@atlaskit/icon/core/maximize'; import SmartLinkCardIcon from '@atlaskit/icon/core/smart-link-card'; import { mediaFilmstripItemDOMSelector } from '@atlaskit/media-filmstrip'; import { messages } from '@atlaskit/media-ui'; import { fg } from '@atlaskit/platform-feature-flags'; import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals'; import { MediaSingleNodeSelector } from '../../nodeviews/styles'; import { getPluginState as getMediaAltTextPluginState } from '../../pm-plugins/alt-text'; import { showLinkingToolbar } from '../../pm-plugins/commands/linking'; import { getMediaLinkingState } from '../../pm-plugins/linking'; import { getPluginState as getMediaPixelResizingPluginState } from '../../pm-plugins/pixel-resizing'; import { FullWidthDisplay, PixelEntry } from '../../pm-plugins/pixel-resizing/ui'; import { stateKey } from '../../pm-plugins/plugin-key'; import { currentMediaOrInlineNodeBorderMark } from '../../pm-plugins/utils/current-media-node'; import { isVideo } from '../../pm-plugins/utils/media-single'; import ImageBorderItem from '../../ui/ImageBorder'; import { altTextButton, getAltTextDropdownOption, getAltTextToolbar } from './alt-text'; import { changeMediaCardToInline, changeMediaSingleToMediaInline, setBorderMark, toggleBorderMark } from './commands'; import { commentButton } from './comments'; import { shouldShowImageBorder } from './imageBorder'; import { LayoutGroup } from './layout-group'; import { getLinkingDropdownOptions, getLinkingToolbar, getOpenLinkToolbarButtonOption, shouldShowMediaLinkToolbar } from './linking'; import { LinkToolbarAppearance } from './linking-toolbar-appearance'; import { generateMediaInlineFloatingToolbar } from './mediaInline'; import { getPixelResizingToolbar, getResizeDropdownOption } from './pixel-resizing'; import { canShowSwitchButtons, downloadMedia, getIsDownloadDisabledByDataSecurityPolicy, getMaxToolbarWidth, getMediaSingleAndMediaInlineSwitcherDropdown, getSelectedLayoutIcon, getSelectedMediaSingle, getSelectedNearestMediaContainerNodeAttrs, removeMediaGroupNode, updateToFullHeightSeparator } from './utils'; const mediaTypeMessages = { image: messages.file_image_is_selected, video: messages.file_video_is_selected, audio: messages.file_audio_is_selected, doc: messages.file_doc_is_selected, archive: messages.file_archive_is_selected, unknown: messages.file_unknown_is_selected }; const getMediaActionSubject = nodeType => { switch (nodeType.name) { case 'mediaSingle': return ACTION_SUBJECT.MEDIA_SINGLE; case 'mediaInline': return ACTION_SUBJECT.MEDIA_INLINE; case 'mediaGroup': return ACTION_SUBJECT.MEDIA_GROUP; default: return ACTION_SUBJECT.MEDIA; } }; const removeWithAnalytics = (editorAnalyticsApi, nodeType) => { if (!nodeType) { return remove; } const mediaType = getMediaActionSubject(nodeType); return withAnalytics(editorAnalyticsApi, { action: ACTION.DELETED, actionSubject: mediaType, attributes: { inputMethod: INPUT_METHOD.FLOATING_TB }, eventType: EVENT_TYPE.TRACK })(remove); }; const remove = (state, dispatch) => { if (dispatch) { dispatch(removeSelectedNode(state.tr)); } return true; }; const handleRemoveMediaGroupWithAnalytics = editorAnalyticsApi => { return withAnalytics(editorAnalyticsApi, { action: ACTION.DELETED, actionSubject: ACTION_SUBJECT.MEDIA_GROUP, attributes: { inputMethod: INPUT_METHOD.FLOATING_TB }, eventType: EVENT_TYPE.TRACK })(handleRemoveMediaGroup); }; const handleRemoveMediaGroup = (state, dispatch) => { const tr = removeMediaGroupNode(state); if (dispatch) { dispatch(tr); } return true; }; export const handleShowMediaViewer = ({ api, mediaPluginState }) => { const selectedNodeAttrs = getSelectedNearestMediaContainerNodeAttrs(mediaPluginState); if (!selectedNodeAttrs) { return false; } api === null || api === void 0 ? void 0 : api.core.actions.execute(api === null || api === void 0 ? void 0 : api.media.commands.showMediaViewer(selectedNodeAttrs)); }; export const handleShowImageEditor = ({ api, mediaPluginState }) => { var _api$mediaEditing; const selectedNodeAttrs = getSelectedNearestMediaContainerNodeAttrs(mediaPluginState); if (!selectedNodeAttrs) { return false; } api === null || api === void 0 ? void 0 : api.core.actions.execute(api === null || api === void 0 ? void 0 : (_api$mediaEditing = api.mediaEditing) === null || _api$mediaEditing === void 0 ? void 0 : _api$mediaEditing.commands.showImageEditor(selectedNodeAttrs)); }; const generateMediaCardFloatingToolbar = (state, intl, mediaPluginState, hoverDecoration, pluginInjectionApi, editorAnalyticsAPI, forceFocusSelector, isViewOnly) => { var _pluginInjectionApi$c, _pluginInjectionApi$c2, _pluginInjectionApi$c3; const disableDownloadButton = getIsDownloadDisabledByDataSecurityPolicy(mediaPluginState); const areAnyNewToolbarFlagsEnabled = areToolbarFlagsEnabled(Boolean(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : pluginInjectionApi.toolbar)); const preview = { id: 'editor.media.viewer', testId: 'file-preview-toolbar-button', type: 'button', icon: areAnyNewToolbarFlagsEnabled ? GrowDiagonalIcon : MaximizeIcon, title: intl.formatMessage(messages.preview), onClick: () => { var _handleShowMediaViewe; return (_handleShowMediaViewe = handleShowMediaViewer({ mediaPluginState, api: pluginInjectionApi })) !== null && _handleShowMediaViewe !== void 0 ? _handleShowMediaViewe : false; }, disabled: isOfflineMode(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$c = pluginInjectionApi.connectivity) === null || _pluginInjectionApi$c === void 0 ? void 0 : (_pluginInjectionApi$c2 = _pluginInjectionApi$c.sharedState) === null || _pluginInjectionApi$c2 === void 0 ? void 0 : (_pluginInjectionApi$c3 = _pluginInjectionApi$c2.currentState()) === null || _pluginInjectionApi$c3 === void 0 ? void 0 : _pluginInjectionApi$c3.mode), supportsViewMode: true }; const download = { id: 'editor.media.card.download', type: 'button', icon: DownloadIcon, onClick: () => { downloadMedia(mediaPluginState); return true; }, title: intl.formatMessage(messages.download), disabled: disableDownloadButton, ...(areAnyNewToolbarFlagsEnabled && { supportsViewMode: true }) }; if (isViewOnly && !areAnyNewToolbarFlagsEnabled) { return []; } const { mediaGroup } = state.schema.nodes; const items = []; if (!areAnyNewToolbarFlagsEnabled) { items.push({ id: 'editor.media.view.switcher.inline', type: 'button', icon: ImageInlineIcon, selected: false, focusEditoronEnter: true, disabled: false, onClick: changeMediaCardToInline(editorAnalyticsAPI, forceFocusSelector), title: intl.formatMessage(cardMessages.inlineTitle), testId: 'inline-appearance', className: 'inline-appearance' // a11y. uses to force focus on item }, { id: 'editor.media.view.switcher.thumbnail', type: 'button', icon: SmartLinkCardIcon, selected: true, disabled: false, focusEditoronEnter: true, onClick: () => true, title: intl.formatMessage(cardMessages.blockTitle), testId: 'thumbnail-appearance', className: 'thumbnail-appearance' // a11y. uses to force focus on item }, { type: 'separator' }, preview, { type: 'separator' }, download, { type: 'separator' }, { type: 'copy-button', supportsViewMode: true, items: [{ state, formatMessage: intl.formatMessage, nodeType: mediaGroup }] }, { type: 'separator' }, { id: 'editor.media.delete', type: 'button', appearance: 'danger', focusEditoronEnter: true, icon: DeleteIcon, onMouseEnter: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(mediaGroup, true), onMouseLeave: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(mediaGroup, false), onFocus: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(mediaGroup, true), onBlur: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(mediaGroup, false), title: intl.formatMessage(commonMessages.remove), onClick: handleRemoveMediaGroupWithAnalytics(editorAnalyticsAPI), testId: 'media-toolbar-remove-button' }); } else { const options = [{ id: 'editor.media.view.switcher.inline', title: intl.formatMessage(cardMessages.inlineTitle), onClick: changeMediaCardToInline(editorAnalyticsAPI, forceFocusSelector), icon: /*#__PURE__*/React.createElement(ImageInlineIcon, { label: "", spacing: "spacious" }) }, { id: 'editor.media.view.switcher.thumbnail', title: intl.formatMessage(cardMessages.blockTitle), selected: true, onClick: () => true, icon: /*#__PURE__*/React.createElement(SmartLinkCardIcon, { label: "", spacing: "spacious" }) }]; const switcherDropdown = { title: intl.formatMessage(messages.fileDisplayOptions), id: 'media-group-inline-switcher-toolbar-item', testId: 'media-group-inline-switcher-dropdown', type: 'dropdown', options, icon: () => /*#__PURE__*/React.createElement(SmartLinkCardIcon, { label: "", spacing: "spacious" }) }; items.push(switcherDropdown, { type: 'separator', fullHeight: true }, download, { type: 'separator', supportsViewMode: true }, preview, { type: 'separator', fullHeight: true }); } return items; }; const generateMediaSingleFloatingToolbar = (state, intl, options, pluginState, mediaLinkingState, pluginInjectionApi) => { var _pluginInjectionApi$d, _pluginInjectionApi$d2; const { mediaSingle } = state.schema.nodes; const { allowResizing, allowLinking, allowAdvancedToolBarOptions, allowCommentsOnMedia, allowResizingInTables, allowAltTextOnImages, allowMediaInline, allowMediaInlineImages, allowImageEditing, allowImagePreview, isViewOnly, allowPixelResizing, onCommentButtonMount, createCommentExperience } = options; let toolbarButtons = []; const { hoverDecoration } = (_pluginInjectionApi$d = pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$d2 = pluginInjectionApi.decorations) === null || _pluginInjectionApi$d2 === void 0 ? void 0 : _pluginInjectionApi$d2.actions) !== null && _pluginInjectionApi$d !== void 0 ? _pluginInjectionApi$d : {}; const areAnyNewToolbarFlagsEnabled = areToolbarFlagsEnabled(Boolean(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : pluginInjectionApi.toolbar)); const disableDownloadButton = getIsDownloadDisabledByDataSecurityPolicy(pluginState); if (shouldShowImageBorder(state)) { toolbarButtons.push({ type: 'custom', fallback: [], render: editorView => { if (!editorView) { return null; } const { dispatch, state } = editorView; const borderMark = currentMediaOrInlineNodeBorderMark(state); return /*#__PURE__*/React.createElement(ImageBorderItem // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) , { toggleBorder: () => { var _pluginInjectionApi$a; toggleBorderMark(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$a = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a === void 0 ? void 0 : _pluginInjectionApi$a.actions)(state, dispatch); } // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) , setBorder: attrs => { var _pluginInjectionApi$a2; setBorderMark(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$a2 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a2 === void 0 ? void 0 : _pluginInjectionApi$a2.actions)(attrs)(state, dispatch); }, borderMark: borderMark, intl: intl }); } }); if (!areAnyNewToolbarFlagsEnabled) { toolbarButtons.push({ type: 'separator' }); } } if (allowAdvancedToolBarOptions) { var _pluginInjectionApi$a3; const widthPlugin = pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : pluginInjectionApi.width; let isChangingLayoutDisabled = false; const selectedNode = getSelectedMediaSingle(state); if (allowPixelResizing) { var _widthPlugin$sharedSt; const contentWidth = widthPlugin === null || widthPlugin === void 0 ? void 0 : (_widthPlugin$sharedSt = widthPlugin.sharedState.currentState()) === null || _widthPlugin$sharedSt === void 0 ? void 0 : _widthPlugin$sharedSt.lineLength; const selectedNodeMaxWidth = pluginState.currentMaxWidth || contentWidth; if (selectedNode && selectedNodeMaxWidth && selectedNode.node.attrs.width >= selectedNodeMaxWidth) { isChangingLayoutDisabled = true; } } const layoutButtons = buildLayoutButtons(state, intl, state.schema.nodes.mediaSingle, widthPlugin, pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$a3 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a3 === void 0 ? void 0 : _pluginInjectionApi$a3.actions, allowResizing, allowResizingInTables, true, true, isChangingLayoutDisabled, allowPixelResizing); const addLayoutDropdownToToolbar = () => { if (areAnyNewToolbarFlagsEnabled) { var _pluginInjectionApi$a4; const layoutDropdown = buildLayoutDropdown(state, intl, state.schema.nodes.mediaSingle, widthPlugin, pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$a4 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a4 === void 0 ? void 0 : _pluginInjectionApi$a4.actions, allowResizing, allowResizingInTables, true, true, isChangingLayoutDisabled, allowPixelResizing); toolbarButtons = [...toolbarButtons, ...layoutDropdown]; } else { const selectedLayoutIcon = getSelectedLayoutIcon([...alignmentIcons, ...wrappingIcons], // eslint-disable-next-line @typescript-eslint/no-non-null-assertion selectedNode.node); if (selectedLayoutIcon && layoutButtons.length) { const options = { render: props => { return /*#__PURE__*/React.createElement(LayoutGroup, _extends({ layoutButtons: layoutButtons // Ignored via go/ees005 // eslint-disable-next-line react/jsx-props-no-spreading }, props, { areAnyNewToolbarFlagsEnabled: areAnyNewToolbarFlagsEnabled })); }, width: 188, height: 32 }; const trigger = { id: 'media-single-layout', testId: 'media-single-layout-dropdown-trigger', type: 'dropdown', options: options, title: intl.formatMessage(layoutToMessages[selectedLayoutIcon.value]), icon: selectedLayoutIcon.icon }; toolbarButtons = [...toolbarButtons, trigger, ...(areAnyNewToolbarFlagsEnabled ? [] : [{ type: 'separator' }])]; } } }; if (fg('platform_editor_remove_media_inline_feature_flag')) { if (allowMediaInlineImages && selectedNode) { addLayoutDropdownToToolbar(); } else { toolbarButtons = [...toolbarButtons, ...layoutButtons]; if (layoutButtons.length) { toolbarButtons.push({ type: 'separator' }); } } } else { if (mediaInlineImagesEnabled(allowMediaInline, allowMediaInlineImages) && selectedNode) { addLayoutDropdownToToolbar(); } else { toolbarButtons = [...toolbarButtons, ...layoutButtons]; if (layoutButtons.length && !areAnyNewToolbarFlagsEnabled) { toolbarButtons.push({ type: 'separator' }); } } } // floating and inline switcher if (pluginState.allowInlineImages && selectedNode && canShowSwitchButtons(selectedNode.node)) { var _selectedNode$node$fi; // mediaInlne doesn't suppprt external images, so hiding buttons to prevent conversion from mediaSingle to mediaInline if (((_selectedNode$node$fi = selectedNode.node.firstChild) === null || _selectedNode$node$fi === void 0 ? void 0 : _selectedNode$node$fi.attrs.type) !== 'external') { const hasCaption = contains(selectedNode.node, state.schema.nodes.caption); const inlineSwitcherTitle = intl.formatMessage(hasCaption ? mediaAndEmbedToolbarMessages.changeToMediaInlineImageCaptionWarning : mediaAndEmbedToolbarMessages.changeToMediaInlineImage); const floatingSwitcherTitle = intl.formatMessage(mediaAndEmbedToolbarMessages.changeToMediaSingle); if (!areAnyNewToolbarFlagsEnabled) { var _pluginInjectionApi$a5; toolbarButtons.push({ type: 'button', id: 'editor.media.image.view.switcher.inline', title: inlineSwitcherTitle, icon: () => /*#__PURE__*/React.createElement(ImageInlineIcon, { color: "currentColor", spacing: "spacious", label: inlineSwitcherTitle }), onClick: changeMediaSingleToMediaInline(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$a5 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a5 === void 0 ? void 0 : _pluginInjectionApi$a5.actions), testId: 'image-inline-appearance', selected: false }, { type: 'button', id: 'editor.media.image.view.switcher.floating', title: floatingSwitcherTitle, icon: () => /*#__PURE__*/React.createElement(ImageFullscreenIcon, { color: "currentColor", spacing: "spacious", label: floatingSwitcherTitle }), onClick: () => { return true; }, testId: 'image-floating-appearance', selected: true }, { type: 'separator' }); } else { const switchFromBlockToInline = getMediaSingleAndMediaInlineSwitcherDropdown('block', intl, pluginInjectionApi, hasCaption); toolbarButtons.push(switchFromBlockToInline, { type: 'separator', fullHeight: true }); } } } // A separator is needed regardless switcher is enabled or not if (Boolean(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : pluginInjectionApi.toolbar)) { toolbarButtons.push({ type: 'separator', fullHeight: true }); } // Pixel Entry Toolbar Support const { selection } = state; const isWithinTable = hasParentNodeOfType([state.schema.nodes.table])(selection); if (allowResizing && (!isWithinTable || allowResizingInTables === true) && allowPixelResizing) { const selectedMediaSingleNode = getSelectedMediaSingle(state); const sizeInput = { type: 'custom', fallback: [], render: editorView => { if (!editorView || !selectedMediaSingleNode) { return null; } return /*#__PURE__*/React.createElement(PixelEntry, { editorView: editorView, intl: intl, selectedMediaSingleNode: selectedMediaSingleNode, pluginInjectionApi: pluginInjectionApi, pluginState: pluginState, hoverDecoration: hoverDecoration, isEditorFullWidthEnabled: options.fullWidthEnabled }); } }; if (pluginState.isResizing) { // If the image is resizing // then return pixel entry component or full width label as the only toolbar item if (!selectedMediaSingleNode) { return []; } const { layout } = selectedMediaSingleNode.node.attrs; if (layout === 'full-width') { const fullWidthLabel = { type: 'custom', fallback: [], render: () => { return /*#__PURE__*/React.createElement(FullWidthDisplay, { intl: intl }); } }; return [fullWidthLabel]; } return [sizeInput]; } if (!areAnyNewToolbarFlagsEnabled) { toolbarButtons.push(sizeInput); toolbarButtons.push({ type: 'separator' }); } } if (!areAnyNewToolbarFlagsEnabled) { if (allowCommentsOnMedia) { toolbarButtons.push(commentButton(intl, state, pluginInjectionApi, onCommentButtonMount, createCommentExperience), { type: 'separator', supportsViewMode: true }); } if (allowLinking && shouldShowMediaLinkToolbar(state)) { toolbarButtons.push({ type: 'custom', fallback: [], render: (editorView, idx) => { if (editorView !== null && editorView !== void 0 && editorView.state) { const editLink = () => { if (editorView) { const { state, dispatch } = editorView; showLinkingToolbar(state, dispatch); } }; const openLink = () => { if (editorView) { var _pluginInjectionApi$a6; const { state: { tr }, dispatch } = editorView; pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$a6 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a6 === void 0 ? void 0 : _pluginInjectionApi$a6.actions.attachAnalyticsEvent({ eventType: EVENT_TYPE.TRACK, action: ACTION.VISITED, actionSubject: ACTION_SUBJECT.MEDIA, actionSubjectId: ACTION_SUBJECT_ID.LINK })(tr); dispatch(tr); return true; } }; return /*#__PURE__*/React.createElement(LinkToolbarAppearance, { key: idx, editorState: editorView.state, intl: intl, mediaLinkingState: mediaLinkingState, onAddLink: editLink, onEditLink: editLink, onOpenLink: openLink, isViewOnly: isViewOnly, areAnyNewToolbarFlagsEnabled: false }); } return null; }, supportsViewMode: true }); } // Image Editing Support if (!!(pluginInjectionApi !== null && pluginInjectionApi !== void 0 && pluginInjectionApi.mediaEditing) && allowImageEditing && expValEquals('platform_editor_add_image_editing', 'isEnabled', true)) { var _mediaNode$attrs, _mediaNode$attrs2, _mediaNode$attrs3; const selectedMediaSingleNode = getSelectedMediaSingle(state); const mediaNode = selectedMediaSingleNode === null || selectedMediaSingleNode === void 0 ? void 0 : selectedMediaSingleNode.node.content.firstChild; // Disable image editing for external media, as we cannot save changes to external images per CORS policy const isExternal = (mediaNode === null || mediaNode === void 0 ? void 0 : (_mediaNode$attrs = mediaNode.attrs) === null || _mediaNode$attrs === void 0 ? void 0 : _mediaNode$attrs.type) === 'external'; // Disable image editing for gifs as CropperJS does not support gif editing const isGif = (mediaNode === null || mediaNode === void 0 ? void 0 : (_mediaNode$attrs2 = mediaNode.attrs) === null || _mediaNode$attrs2 === void 0 ? void 0 : _mediaNode$attrs2.__fileMimeType) === 'image/gif'; if (!isVideo(mediaNode === null || mediaNode === void 0 ? void 0 : (_mediaNode$attrs3 = mediaNode.attrs) === null || _mediaNode$attrs3 === void 0 ? void 0 : _mediaNode$attrs3.__fileMimeType) && !isExternal && !isGif) { toolbarButtons.push({ id: 'editor.media.edit', testId: 'image-edit-toolbar-button', type: 'button', icon: ImageCropIcon, title: intl.formatMessage(commonMessages.imageEdit), onClick: () => { var _handleShowImageEdito; return (_handleShowImageEdito = handleShowImageEditor({ api: pluginInjectionApi, mediaPluginState: pluginState })) !== null && _handleShowImageEdito !== void 0 ? _handleShowImageEdito : false; }, // Making the button more accessible supportsViewMode: false, isRadioButton: true, selected: false }); } } // Preview Support if (allowImagePreview) { var _mediaNode$attrs4; const selectedMediaSingleNode = getSelectedMediaSingle(state); const mediaNode = selectedMediaSingleNode === null || selectedMediaSingleNode === void 0 ? void 0 : selectedMediaSingleNode.node.content.firstChild; if (!isVideo(mediaNode === null || mediaNode === void 0 ? void 0 : (_mediaNode$attrs4 = mediaNode.attrs) === null || _mediaNode$attrs4 === void 0 ? void 0 : _mediaNode$attrs4.__fileMimeType)) { var _pluginInjectionApi$c4, _pluginInjectionApi$c5, _pluginInjectionApi$c6; toolbarButtons.push({ id: 'editor.media.viewer', testId: 'file-preview-toolbar-button', type: 'button', icon: MaximizeIcon, title: intl.formatMessage(messages.preview), onClick: () => { var _handleShowMediaViewe2; return (_handleShowMediaViewe2 = handleShowMediaViewer({ api: pluginInjectionApi, mediaPluginState: pluginState })) !== null && _handleShowMediaViewe2 !== void 0 ? _handleShowMediaViewe2 : false; }, disabled: isOfflineMode(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$c4 = pluginInjectionApi.connectivity) === null || _pluginInjectionApi$c4 === void 0 ? void 0 : (_pluginInjectionApi$c5 = _pluginInjectionApi$c4.sharedState) === null || _pluginInjectionApi$c5 === void 0 ? void 0 : (_pluginInjectionApi$c6 = _pluginInjectionApi$c5.currentState()) === null || _pluginInjectionApi$c6 === void 0 ? void 0 : _pluginInjectionApi$c6.mode), supportsViewMode: true }, { type: 'separator', supportsViewMode: true }); } } } } if (isViewOnly) { toolbarButtons.push({ id: 'editor.media.image.download', type: 'button', icon: DownloadIcon, onClick: () => { downloadMedia(pluginState, isViewOnly); return true; }, disabled: disableDownloadButton, title: intl.formatMessage(messages.download), supportsViewMode: true }, { type: 'separator', supportsViewMode: true }); } if (!areAnyNewToolbarFlagsEnabled) { var _pluginInjectionApi$a8; if (allowAltTextOnImages) { var _pluginInjectionApi$a7; toolbarButtons.push(altTextButton(intl, state, pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$a7 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a7 === void 0 ? void 0 : _pluginInjectionApi$a7.actions), { type: 'separator' }); } const removeButton = { id: 'editor.media.delete', type: 'button', appearance: 'danger', focusEditoronEnter: true, icon: DeleteIcon, onMouseEnter: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(mediaSingle, true), onMouseLeave: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(mediaSingle, false), onFocus: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(mediaSingle, true), onBlur: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(mediaSingle, false), title: intl.formatMessage(commonMessages.remove), onClick: removeWithAnalytics(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$a8 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a8 === void 0 ? void 0 : _pluginInjectionApi$a8.actions), testId: 'media-toolbar-remove-button', supportsViewMode: false }; const items = [...toolbarButtons, { type: 'copy-button', items: [{ state, formatMessage: intl.formatMessage, nodeType: mediaSingle }], supportsViewMode: true }]; items.push({ type: 'separator', supportsViewMode: false }); items.push(removeButton); return items; } else { // Preview Support if (allowAdvancedToolBarOptions && allowImagePreview) { var _mediaNode$attrs5; const selectedMediaSingleNode = getSelectedMediaSingle(state); const mediaNode = selectedMediaSingleNode === null || selectedMediaSingleNode === void 0 ? void 0 : selectedMediaSingleNode.node.content.firstChild; if (!isVideo(mediaNode === null || mediaNode === void 0 ? void 0 : (_mediaNode$attrs5 = mediaNode.attrs) === null || _mediaNode$attrs5 === void 0 ? void 0 : _mediaNode$attrs5.__fileMimeType)) { var _pluginInjectionApi$c7, _pluginInjectionApi$c8, _pluginInjectionApi$c9; toolbarButtons.push({ id: 'editor.media.viewer', testId: 'file-preview-toolbar-button', type: 'button', icon: GrowDiagonalIcon, title: intl.formatMessage(messages.preview), onClick: () => { var _handleShowMediaViewe3; return (_handleShowMediaViewe3 = handleShowMediaViewer({ api: pluginInjectionApi, mediaPluginState: pluginState })) !== null && _handleShowMediaViewe3 !== void 0 ? _handleShowMediaViewe3 : false; }, disabled: isOfflineMode(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$c7 = pluginInjectionApi.connectivity) === null || _pluginInjectionApi$c7 === void 0 ? void 0 : (_pluginInjectionApi$c8 = _pluginInjectionApi$c7.sharedState) === null || _pluginInjectionApi$c8 === void 0 ? void 0 : (_pluginInjectionApi$c9 = _pluginInjectionApi$c8.currentState()) === null || _pluginInjectionApi$c9 === void 0 ? void 0 : _pluginInjectionApi$c9.mode), supportsViewMode: true }, { type: 'separator', supportsViewMode: true }); } } if (allowAdvancedToolBarOptions && allowImageEditing && expValEquals('platform_editor_add_image_editing', 'isEnabled', true)) { var _mediaNode$attrs6, _mediaNode$attrs7, _mediaNode$attrs8; const selectedMediaSingleNode = getSelectedMediaSingle(state); const mediaNode = selectedMediaSingleNode === null || selectedMediaSingleNode === void 0 ? void 0 : selectedMediaSingleNode.node.content.firstChild; // Disable image editing for external media, as we cannot save changes to external images per CORS policy const isExternal = (mediaNode === null || mediaNode === void 0 ? void 0 : (_mediaNode$attrs6 = mediaNode.attrs) === null || _mediaNode$attrs6 === void 0 ? void 0 : _mediaNode$attrs6.type) === 'external'; // Disable image editing for gifs as CropperJS does not support gif editing const isGif = (mediaNode === null || mediaNode === void 0 ? void 0 : (_mediaNode$attrs7 = mediaNode.attrs) === null || _mediaNode$attrs7 === void 0 ? void 0 : _mediaNode$attrs7.__fileMimeType) === 'image/gif'; if (!isVideo(mediaNode === null || mediaNode === void 0 ? void 0 : (_mediaNode$attrs8 = mediaNode.attrs) === null || _mediaNode$attrs8 === void 0 ? void 0 : _mediaNode$attrs8.__fileMimeType) && !isExternal && !isGif) { toolbarButtons.push({ id: 'editor.media.edit', testId: 'image-edit-toolbar-button', type: 'button', icon: ImageCropIcon, title: intl.formatMessage(commonMessages.imageEdit), onClick: () => { var _handleShowImageEdito2; return (_handleShowImageEdito2 = handleShowImageEditor({ api: pluginInjectionApi, mediaPluginState: pluginState })) !== null && _handleShowImageEdito2 !== void 0 ? _handleShowImageEdito2 : false; }, supportsViewMode: false }, { type: 'separator', supportsViewMode: false }); } } // open link if (allowLinking && shouldShowMediaLinkToolbar(state) && mediaLinkingState && mediaLinkingState.editable) { toolbarButtons.push(getOpenLinkToolbarButtonOption(intl, mediaLinkingState, pluginInjectionApi), { type: 'separator', supportsViewMode: true }); } isViewOnly && toolbarButtons.push({ type: 'copy-button', items: [{ state, formatMessage: intl.formatMessage, nodeType: mediaSingle }], supportsViewMode: true }, { type: 'separator', supportsViewMode: true }); if (allowAdvancedToolBarOptions && allowCommentsOnMedia) { updateToFullHeightSeparator(toolbarButtons); toolbarButtons.push(commentButton(intl, state, pluginInjectionApi, onCommentButtonMount, createCommentExperience)); } return toolbarButtons; } }; const getMediaTypeMessage = selectedNodeTypeSingle => { const mediaType = Object.keys(mediaTypeMessages).find(key => selectedNodeTypeSingle === null || selectedNodeTypeSingle === void 0 ? void 0 : selectedNodeTypeSingle.includes(key)); return mediaType ? mediaTypeMessages[mediaType] : messages.file_unknown_is_selected; }; export const overflowDropdwonBtnTriggerTestId = 'media-overflow-dropdown-trigger'; const isMediaSelection = (selection, nodeType) => { if (selection instanceof NodeSelection) { return nodeType.includes(selection.node.type); } return false; }; export const floatingToolbar = (state, intl, options = {}, pluginInjectionApi) => { var _pluginInjectionApi$d3, _pluginInjectionApi$d4; const { media, mediaInline, mediaSingle, mediaGroup } = state.schema.nodes; const { altTextValidator, allowLinking, allowAltTextOnImages, providerFactory, allowMediaInlineImages, allowResizing, isViewOnly, allowResizingInTables, allowAdvancedToolBarOptions, allowPixelResizing } = options; let { allowMediaInline } = options; allowMediaInline = fg('platform_editor_remove_media_inline_feature_flag') ? allowMediaInlineImages : allowMediaInline; const mediaPluginState = stateKey.getState(state); const mediaLinkingState = getMediaLinkingState(state); const { hoverDecoration } = (_pluginInjectionApi$d3 = pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$d4 = pluginInjectionApi.decorations) === null || _pluginInjectionApi$d4 === void 0 ? void 0 : _pluginInjectionApi$d4.actions) !== null && _pluginInjectionApi$d3 !== void 0 ? _pluginInjectionApi$d3 : {}; if (!mediaPluginState) { return; } const nodeType = allowMediaInline ? [mediaInline, mediaSingle, media] : [mediaSingle]; const isSelectedNodeMediaSingle = state.selection instanceof NodeSelection && state.selection.node.type === mediaSingle; if (!isMediaSelection(state.selection, nodeType)) { return; } const baseToolbar = { title: 'Media floating controls', nodeType, getDomRef: () => { var _mediaPluginState$ele; const element = isSelectedNodeMediaSingle ? ((_mediaPluginState$ele = mediaPluginState.element) === null || _mediaPluginState$ele === void 0 ? void 0 : _mediaPluginState$ele.querySelector(`.${MediaSingleNodeSelector}`)) || mediaPluginState.element : mediaPluginState.element; return element; } }; if (allowLinking && mediaLinkingState && mediaLinkingState.visible && shouldShowMediaLinkToolbar(state)) { const linkingToolbar = getLinkingToolbar(baseToolbar, mediaLinkingState, state, intl, pluginInjectionApi, providerFactory); if (linkingToolbar) { return linkingToolbar; } } // testId is required to show focus on trigger button on ESC key press // see hideOnEsc in platform/packages/editor/editor-plugin-floating-toolbar/src/ui/Dropdown.tsx const overflowButtonSelector = areToolbarFlagsEnabled(Boolean(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : pluginInjectionApi.toolbar)) ? `[data-testid="${overflowDropdwonBtnTriggerTestId}"]` : undefined; if (allowAltTextOnImages) { const mediaAltTextPluginState = getMediaAltTextPluginState(state); if (mediaAltTextPluginState.isAltTextEditorOpen) { var _pluginInjectionApi$f, _pluginInjectionApi$f2; return getAltTextToolbar(baseToolbar, { altTextValidator, forceFocusSelector: pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$f = pluginInjectionApi.floatingToolbar) === null || _pluginInjectionApi$f === void 0 ? void 0 : (_pluginInjectionApi$f2 = _pluginInjectionApi$f.actions) === null || _pluginInjectionApi$f2 === void 0 ? void 0 : _pluginInjectionApi$f2.forceFocusSelector, triggerButtonSelector: overflowButtonSelector, areAnyNewToolbarFlagsEnabled: areToolbarFlagsEnabled(Boolean(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : pluginInjectionApi.toolbar)) }); } } const { selection } = state; const isWithinTable = hasParentNodeOfType([state.schema.nodes.table])(selection); if (allowAdvancedToolBarOptions && allowResizing && (!isWithinTable || allowResizingInTables === true) && allowPixelResizing && areToolbarFlagsEnabled(Boolean(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : pluginInjectionApi.toolbar))) { const mediaPixelResizingPluginState = getMediaPixelResizingPluginState(state); if (mediaPixelResizingPluginState !== null && mediaPixelResizingPluginState !== void 0 && mediaPixelResizingPluginState.isPixelEditorOpen) { return getPixelResizingToolbar(baseToolbar, { intl, pluginInjectionApi, pluginState: mediaPluginState, hoverDecoration, isEditorFullWidthEnabled: options.fullWidthEnabled, triggerButtonSelector: overflowButtonSelector }); } } let items = []; const parentMediaGroupNode = findParentNodeOfType(mediaGroup)(state.selection); let selectedNodeType; if (state.selection instanceof NodeSelection) { selectedNodeType = state.selection.node.type; } if (allowMediaInline && (parentMediaGroupNode === null || parentMediaGroupNode === void 0 ? void 0 : parentMediaGroupNode.node.type) === mediaGroup) { var _pluginInjectionApi$a9, _pluginInjectionApi$f3, _pluginInjectionApi$f4; const mediaOffset = state.selection.$from.parentOffset + 1; baseToolbar.getDomRef = () => { var _mediaPluginState$ele2; const selector = mediaFilmstripItemDOMSelector(mediaOffset); // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting return (_mediaPluginState$ele2 = mediaPluginState.element) === null || _mediaPluginState$ele2 === void 0 ? void 0 : _mediaPluginState$ele2.querySelector(selector); }; items = generateMediaCardFloatingToolbar(state, intl, mediaPluginState, hoverDecoration, pluginInjectionApi, pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$a9 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a9 === void 0 ? void 0 : _pluginInjectionApi$a9.actions, pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$f3 = pluginInjectionApi.floatingToolbar) === null || _pluginInjectionApi$f3 === void 0 ? void 0 : (_pluginInjectionApi$f4 = _pluginInjectionApi$f3.actions) === null || _pluginInjectionApi$f4 === void 0 ? void 0 : _pluginInjectionApi$f4.forceFocusSelector, isViewOnly); } else if (allowMediaInline && selectedNodeType && selectedNodeType === mediaInline) { items = generateMediaInlineFloatingToolbar(state, intl, mediaPluginState, hoverDecoration, pluginInjectionApi, options); } else { baseToolbar.getDomRef = () => { var _mediaPluginState$ele3; // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting const element = (_mediaPluginState$ele3 = mediaPluginState.element) === null || _mediaPluginState$ele3 === void 0 ? void 0 : _mediaPluginState$ele3.querySelector(`.${MediaSingleNodeSelector}`); return element || mediaPluginState.element; }; items = generateMediaSingleFloatingToolbar(state, intl, options, mediaPluginState, mediaLinkingState, pluginInjectionApi); } if (!mediaPluginState.isResizing && areToolbarFlagsEnabled(Boolean(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : pluginInjectionApi.toolbar))) { var _pluginInjectionApi$a0, _pluginInjectionApi$a1, _pluginInjectionApi$a10; updateToFullHeightSeparator(items); const customOptions = [...getLinkingDropdownOptions(state, intl, mediaLinkingState, allowMediaInline && selectedNodeType && selectedNodeType === mediaInline, allowLinking, isViewOnly), ...getAltTextDropdownOption(state, intl.formatMessage, allowAltTextOnImages, selectedNodeType, pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$a0 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a0 === void 0 ? void 0 : _pluginInjectionApi$a0.actions), ...getResizeDropdownOption(options, state, intl.formatMessage, selectedNodeType)]; if (customOptions.length) { customOptions.push({ type: 'separator' }); } const hoverDecorationProps = (nodeType, className) => ({ onMouseEnter: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(nodeType, true, className), onMouseLeave: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(nodeType, false, className), onFocus: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(nodeType, true, className), onBlur: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(nodeType, false, className) }); items.push({ type: 'overflow-dropdown', id: 'media', testId: overflowDropdwonBtnTriggerTestId, options: [...customOptions, { title: intl === null || intl === void 0 ? void 0 : intl.formatMessage(commonMessages.copyToClipboard), onClick: () => { var _pluginInjectionApi$c0, _pluginInjectionApi$f5; pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$c0 = pluginInjectionApi.core) === null || _pluginInjectionApi$c0 === void 0 ? void 0 : _pluginInjectionApi$c0.actions.execute( // @ts-ignore pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$f5 = pluginInjectionApi.floatingToolbar) === null || _pluginInjectionApi$f5 === void 0 ? void 0 : _pluginInjectionApi$f5.commands.copyNode(nodeType, INPUT_METHOD.FLOATING_TB)); return true; }, icon: /*#__PURE__*/React.createElement(CopyIcon, { label: "" }), ...hoverDecorationProps(nodeType, akEditorSelectedNodeClassName) }, { title: intl === null || intl === void 0 ? void 0 : intl.formatMessage(commonMessages.delete), onClick: (parentMediaGroupNode === null || parentMediaGroupNode === void 0 ? void 0 : parentMediaGroupNode.node.type) === mediaGroup ? handleRemoveMediaGroupWithAnalytics(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$a1 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a1 === void 0 ? void 0 : _pluginInjectionApi$a1.actions) : removeWithAnalytics(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$a10 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a10 === void 0 ? void 0 : _pluginInjectionApi$a10.actions, selectedNodeType), icon: /*#__PURE__*/React.createElement(DeleteIcon, { label: "" }), ...hoverDecorationProps(nodeType) }] }); } // Ignored via go/ees005 // eslint-disable-next-line no-var var assistiveMessage = ''; const selectedMediaSingleNode = getSelectedMediaSingle(state); if (selectedMediaSingleNode) { const selectedMediaNodeView = selectedMediaSingleNode === null || selectedMediaSingleNode === void 0 ? void 0 : selectedMediaSingleNode.node.content; if (selectedMediaNodeView) { const selectedMediaNode = selectedMediaNodeView.firstChild; const selectedNodeTypeSingle = selectedMediaNode === null || selectedMediaNode === void 0 ? void 0 : selectedMediaNode.attrs.__fileMimeType; const selectedMediaAlt = selectedMediaNode === null || selectedMediaNode === void 0 ? void 0 : selectedMediaNode.attrs.alt; assistiveMessage = intl === null || intl === void 0 ? void 0 : intl.formatMessage(getMediaTypeMessage(selectedNodeTypeSingle), { name: selectedMediaAlt }); } } const toolbarConfig = { ...baseToolbar, items, scrollable: true, mediaAssistiveMessage: assistiveMessage }; if (allowResizing && allowPixelResizing) { return { ...toolbarConfig, width: mediaPluginState.isResizing ? undefined : getMaxToolbarWidth() }; } return toolbarConfig; };