@atlaskit/editor-plugin-media
Version:
Media plugin for @atlaskit/editor-core
427 lines (424 loc) • 23 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.mediaPlugin = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _react = _interopRequireWildcard(require("react"));
var _analytics = require("@atlaskit/editor-common/analytics");
var _hooks = require("@atlaskit/editor-common/hooks");
var _messages = require("@atlaskit/editor-common/messages");
var _quickInsert = require("@atlaskit/editor-common/quick-insert");
var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
var _toolbarFlagCheck = require("@atlaskit/editor-common/toolbar-flag-check");
var _state = require("@atlaskit/editor-prosemirror/state");
var _mediaCommon = require("@atlaskit/media-common");
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
var _lazyMediaGroup = require("./nodeviews/lazy-media-group");
var _lazyMediaInline = require("./nodeviews/lazy-media-inline");
var _mediaNodeView = require("./nodeviews/mediaNodeView");
var _mediaSingle = require("./nodeviews/mediaSingle");
var _media = require("./nodeviews/toDOM-fixes/media");
var _mediaGroup = require("./nodeviews/toDOM-fixes/mediaGroup");
var _mediaInline = require("./nodeviews/toDOM-fixes/mediaInline");
var _mediaSingle2 = require("./nodeviews/toDOM-fixes/mediaSingle");
var _aiGeneratingDecoration = require("./pm-plugins/ai-generating-decoration");
var _altText = require("./pm-plugins/alt-text");
var _keymap = _interopRequireDefault(require("./pm-plugins/alt-text/keymap"));
var _commands = require("./pm-plugins/commands");
var _keymap2 = _interopRequireDefault(require("./pm-plugins/keymap"));
var _keymapMedia = _interopRequireDefault(require("./pm-plugins/keymap-media"));
var _linking = _interopRequireDefault(require("./pm-plugins/linking"));
var _keymap3 = _interopRequireDefault(require("./pm-plugins/linking/keymap"));
var _main = require("./pm-plugins/main");
var _pixelResizing = require("./pm-plugins/pixel-resizing");
var _pluginKey = require("./pm-plugins/plugin-key");
var _mediaCommon2 = require("./pm-plugins/utils/media-common");
var _mediaSingle3 = require("./pm-plugins/utils/media-single");
var _MediaPicker = require("./ui/MediaPicker");
var _PortalWrapper = require("./ui/MediaViewer/PortalWrapper");
var _toolbar = require("./ui/toolbar");
var _ToolbarMedia = _interopRequireDefault(require("./ui/ToolbarMedia"));
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
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) { (0, _defineProperty2.default)(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; }
var selector = function selector(states) {
var _states$mediaState, _states$mediaState2;
return {
onPopupToggle: (_states$mediaState = states.mediaState) === null || _states$mediaState === void 0 ? void 0 : _states$mediaState.onPopupToggle,
setBrowseFn: (_states$mediaState2 = states.mediaState) === null || _states$mediaState2 === void 0 ? void 0 : _states$mediaState2.setBrowseFn
};
};
var MediaPickerFunctionalComponent = function MediaPickerFunctionalComponent(_ref) {
var api = _ref.api,
editorDomElement = _ref.editorDomElement,
appearance = _ref.appearance;
var _useSharedPluginState = (0, _hooks.useSharedPluginStateWithSelector)(api, ['media'], selector),
onPopupToggle = _useSharedPluginState.onPopupToggle,
setBrowseFn = _useSharedPluginState.setBrowseFn;
if (!onPopupToggle || !setBrowseFn) {
return null;
}
return /*#__PURE__*/_react.default.createElement(_MediaPicker.MediaPickerComponents, {
onPopupToggle: onPopupToggle,
setBrowseFn: setBrowseFn,
editorDomElement: editorDomElement,
appearance: appearance,
api: api
});
};
var mediaViewerStateSelector = function mediaViewerStateSelector(states) {
var _states$mediaState3, _states$mediaState4, _states$mediaState5;
return {
isMediaViewerVisible: (_states$mediaState3 = states.mediaState) === null || _states$mediaState3 === void 0 ? void 0 : _states$mediaState3.isMediaViewerVisible,
mediaViewerSelectedMedia: (_states$mediaState4 = states.mediaState) === null || _states$mediaState4 === void 0 ? void 0 : _states$mediaState4.mediaViewerSelectedMedia,
mediaClientConfig: (_states$mediaState5 = states.mediaState) === null || _states$mediaState5 === void 0 ? void 0 : _states$mediaState5.mediaClientConfig
};
};
var MediaViewerFunctionalComponent = function MediaViewerFunctionalComponent(_ref2) {
var api = _ref2.api,
editorView = _ref2.editorView,
mediaViewerExtensions = _ref2.mediaViewerExtensions;
// Only traverse document once when media viewer is visible, media viewer items will not update
// when document changes are made while media viewer is open
var _useSharedPluginState2 = (0, _hooks.useSharedPluginStateWithSelector)(api, ['media'], mediaViewerStateSelector),
isMediaViewerVisible = _useSharedPluginState2.isMediaViewerVisible,
mediaViewerSelectedMedia = _useSharedPluginState2.mediaViewerSelectedMedia,
mediaClientConfig = _useSharedPluginState2.mediaClientConfig;
var mediaItems = (0, _react.useMemo)(function () {
if (isMediaViewerVisible) {
var mediaNodes = (0, _mediaCommon2.extractMediaNodes)(editorView.state.doc);
return (0, _mediaCommon2.createMediaIdentifierArray)(mediaNodes);
}
// eslint-disable-next-line react-hooks/exhaustive-deps -- only update mediaItems once when media viewer is visible
}, [isMediaViewerVisible]);
// Viewer does not have required attributes to render the media viewer
if (!isMediaViewerVisible || !mediaViewerSelectedMedia || !mediaClientConfig) {
return null;
}
var handleOnClose = function handleOnClose() {
// Run Command to hide the media viewer
api === null || api === void 0 || api.core.actions.execute(api === null || api === void 0 ? void 0 : api.media.commands.hideMediaViewer);
};
return /*#__PURE__*/_react.default.createElement(_PortalWrapper.RenderMediaViewer, {
mediaClientConfig: mediaClientConfig,
onClose: handleOnClose,
selectedNodeAttrs: mediaViewerSelectedMedia,
items: mediaItems,
extensions: mediaViewerExtensions
});
};
var mediaPlugin = exports.mediaPlugin = function mediaPlugin(_ref3) {
var _api$analytics3;
var _ref3$config = _ref3.config,
options = _ref3$config === void 0 ? {} : _ref3$config,
api = _ref3.api;
var previousMediaProvider;
var mediaErrorLocalIds = new Set();
return {
name: 'media',
getSharedState: function getSharedState(editorState) {
if (!editorState) {
return null;
}
return _pluginKey.stateKey.getState(editorState) || null;
},
actions: {
handleMediaNodeRenderError: function handleMediaNodeRenderError(node, reason, nestedUnder) {
var _api$analytics;
var isDuplicateError = false;
if ((0, _expValEquals.expValEquals)('platform_editor_media_reliability_observability', 'isEnabled', true)) {
if (mediaErrorLocalIds.has(node.attrs.localId)) {
// we mark duplicate errors in the case of nested media nodes
// renderering to avoid firing multiple errored events for the same underlying issue,
// which can happen more often with nested media nodes
isDuplicateError = true;
} else {
mediaErrorLocalIds.add(node.attrs.localId);
}
}
api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || _api$analytics.actions.fireAnalyticsEvent({
action: _analytics.ACTION.ERRORED,
actionSubject: _analytics.ACTION_SUBJECT.EDITOR,
actionSubjectId: _analytics.ACTION_SUBJECT_ID.MEDIA,
eventType: _analytics.EVENT_TYPE.UI,
attributes: _objectSpread(_objectSpread({
reason: reason,
external: node.attrs.__external
}, nestedUnder && (0, _experiments.editorExperiment)('platform_synced_block', true) ? {
nestedUnder: nestedUnder
} : {}), (0, _experiments.editorExperiment)('platform_synced_block', true) ? {
isDuplicateError: isDuplicateError
} : {})
});
},
insertMediaAsMediaSingle: function insertMediaAsMediaSingle(view, node, inputMethod, insertMediaVia) {
var _api$analytics2;
return (0, _mediaSingle3.insertMediaAsMediaSingle)(view, node, inputMethod, api === null || api === void 0 || (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions, insertMediaVia, options === null || options === void 0 ? void 0 : options.allowPixelResizing);
},
setProvider: function setProvider(provider) {
var _api$core$actions$exe;
// Prevent someone trying to set the exact same provider twice for performance reasons
if (previousMediaProvider === provider) {
return false;
}
previousMediaProvider = provider;
return (_api$core$actions$exe = api === null || api === void 0 ? void 0 : api.core.actions.execute(function (_ref4) {
var tr = _ref4.tr;
return tr.setMeta(_pluginKey.stateKey, {
mediaProvider: provider
});
})) !== null && _api$core$actions$exe !== void 0 ? _api$core$actions$exe : false;
}
},
commands: {
showMediaViewer: _commands.showMediaViewer,
hideMediaViewer: _commands.hideMediaViewer,
trackMediaPaste: _commands.trackMediaPaste,
setAIGenerating: _commands.setAIGenerating,
clearAIGenerating: _commands.clearAIGenerating,
insertMediaSingle: (0, _commands.insertMediaAsMediaSingleCommand)(api === null || api === void 0 || (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : _api$analytics3.actions, options.allowPixelResizing)
},
nodes: function nodes() {
var _ref5 = options || {},
_ref5$allowMediaGroup = _ref5.allowMediaGroup,
allowMediaGroup = _ref5$allowMediaGroup === void 0 ? true : _ref5$allowMediaGroup,
_ref5$allowMediaSingl = _ref5.allowMediaSingle,
allowMediaSingle = _ref5$allowMediaSingl === void 0 ? false : _ref5$allowMediaSingl,
_ref5$allowPixelResiz = _ref5.allowPixelResizing,
allowPixelResizing = _ref5$allowPixelResiz === void 0 ? false : _ref5$allowPixelResiz,
allowCaptions = _ref5.allowCaptions,
allowMediaInlineImages = _ref5.allowMediaInlineImages,
mediaFeatureFlags = _ref5.featureFlags;
var allowMediaInline = (0, _platformFeatureFlags.fg)('platform_editor_remove_media_inline_feature_flag') ? allowMediaInlineImages : (0, _mediaCommon.getMediaFeatureFlag)('mediaInline', mediaFeatureFlags);
var mediaSingleOption = {
withCaption: allowCaptions,
withExtendedWidthTypes: allowPixelResizing
};
return [{
name: 'mediaGroup',
node: (0, _mediaGroup.mediaGroupSpecWithFixedToDOM)()
}, {
name: 'mediaSingle',
node: (0, _mediaSingle2.mediaSingleSpecWithFixedToDOM)(mediaSingleOption)
}, {
name: 'media',
node: (0, _media.mediaSpecWithFixedToDOM)()
}, {
name: 'mediaInline',
node: (0, _mediaInline.mediaInlineSpecWithFixedToDOM)()
}].filter(function (node) {
if (node.name === 'mediaGroup') {
return allowMediaGroup;
}
if (node.name === 'mediaSingle') {
return allowMediaSingle;
}
if (node.name === 'mediaInline') {
return allowMediaInline;
}
return true;
});
},
pmPlugins: function pmPlugins() {
var pmPlugins = [{
name: 'media',
plugin: function plugin(_ref6) {
var schema = _ref6.schema,
dispatch = _ref6.dispatch,
getIntl = _ref6.getIntl,
eventDispatcher = _ref6.eventDispatcher,
providerFactory = _ref6.providerFactory,
errorReporter = _ref6.errorReporter,
portalProviderAPI = _ref6.portalProviderAPI,
dispatchAnalyticsEvent = _ref6.dispatchAnalyticsEvent,
nodeViewPortalProviderAPI = _ref6.nodeViewPortalProviderAPI;
return (0, _main.createPlugin)(schema, {
providerFactory: providerFactory,
nodeViews: {
mediaGroup: (0, _lazyMediaGroup.lazyMediaGroupView)(portalProviderAPI, eventDispatcher, providerFactory, options, api),
mediaSingle: (0, _mediaSingle.ReactMediaSingleNode)(portalProviderAPI, eventDispatcher, providerFactory, api, dispatchAnalyticsEvent, options),
media: (0, _mediaNodeView.ReactMediaNode)(portalProviderAPI, eventDispatcher, providerFactory, options, api),
mediaInline: (0, _lazyMediaInline.lazyMediaInlineView)(portalProviderAPI, eventDispatcher, providerFactory, api, undefined, options === null || options === void 0 ? void 0 : options.fallbackMediaNameFetcher)
},
errorReporter: errorReporter,
uploadErrorHandler: options && options.uploadErrorHandler,
waitForMediaUpload: options && options.waitForMediaUpload,
customDropzoneContainer: options && options.customDropzoneContainer,
customMediaPicker: options && options.customMediaPicker,
allowResizing: !!(options && options.allowResizing)
}, getIntl, api, nodeViewPortalProviderAPI, dispatch, options);
}
}, {
name: 'mediaKeymap',
plugin: function plugin(_ref7) {
var _api$analytics4, _api$selection;
var getIntl = _ref7.getIntl;
return (0, _keymap2.default)(options, api === null || api === void 0 || (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 ? void 0 : _api$analytics4.actions, api === null || api === void 0 || (_api$selection = api.selection) === null || _api$selection === void 0 ? void 0 : _api$selection.actions, api === null || api === void 0 ? void 0 : api.width, getIntl);
}
}];
if (options && options.allowMediaSingle) {
pmPlugins.push({
name: 'mediaSingleKeymap',
plugin: function plugin(_ref8) {
var schema = _ref8.schema;
return (0, _keymapMedia.default)(schema);
}
});
}
if (options && options.allowAltTextOnImages) {
pmPlugins.push({
name: 'mediaAltText',
plugin: _altText.createPlugin
});
pmPlugins.push({
name: 'mediaAltTextKeymap',
plugin: function plugin(_ref9) {
var _api$analytics5;
var schema = _ref9.schema;
return (0, _keymap.default)(schema, api === null || api === void 0 || (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : _api$analytics5.actions);
}
});
}
if (options && options.allowLinking) {
pmPlugins.push({
name: 'mediaLinking',
plugin: function plugin(_ref0) {
var dispatch = _ref0.dispatch;
return (0, _linking.default)(dispatch);
}
});
pmPlugins.push({
name: 'mediaLinkingKeymap',
plugin: function plugin(_ref1) {
var schema = _ref1.schema;
return (0, _keymap3.default)(schema);
}
});
}
if (options && options.allowAdvancedToolBarOptions && options.allowResizing && (0, _toolbarFlagCheck.areToolbarFlagsEnabled)(Boolean(api === null || api === void 0 ? void 0 : api.toolbar)) && options.allowPixelResizing) {
pmPlugins.push({
name: 'mediaPixelResizing',
plugin: _pixelResizing.createPlugin
});
}
pmPlugins.push({
name: 'mediaAIGeneratingDecoration',
plugin: function plugin() {
return (0, _aiGeneratingDecoration.createAIGeneratingDecorationPlugin)();
}
});
pmPlugins.push({
name: 'mediaSelectionHandler',
plugin: function plugin() {
var mediaSelectionHandlerPlugin = new _safePlugin.SafePlugin({
key: new _state.PluginKey('mediaSelectionHandlerPlugin'),
props: {
handleScrollToSelection: function handleScrollToSelection(view) {
var selection = view.state.selection;
if (!(selection instanceof _state.NodeSelection) || selection.node.type.name !== 'media') {
return false;
}
var _view$domAtPos = view.domAtPos(selection.from),
node = _view$domAtPos.node,
offset = _view$domAtPos.offset;
if (
// Is the media element mounted already?
offset === node.childNodes.length) {
// Media is not ready, so stop the scroll request
return true;
}
// Media is ready, keep the scrolling request
return false;
}
}
});
return mediaSelectionHandlerPlugin;
}
});
return pmPlugins;
},
contentComponent: function contentComponent(_ref10) {
var editorView = _ref10.editorView,
appearance = _ref10.appearance;
if (!editorView) {
return null;
}
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(MediaViewerFunctionalComponent, {
api: api,
editorView: editorView,
mediaViewerExtensions: options === null || options === void 0 ? void 0 : options.mediaViewerExtensions
}), /*#__PURE__*/_react.default.createElement(MediaPickerFunctionalComponent, {
editorDomElement: editorView.dom,
appearance: appearance,
api: api
}));
},
secondaryToolbarComponent: function secondaryToolbarComponent(_ref11) {
var disabled = _ref11.disabled;
return /*#__PURE__*/_react.default.createElement(_ToolbarMedia.default, {
isDisabled: disabled,
isReducedSpacing: true,
api: api
});
},
pluginsOptions: {
quickInsert: function quickInsert(_ref12) {
var formatMessage = _ref12.formatMessage;
return !(api !== null && api !== void 0 && api.mediaInsert) ? [{
id: 'media',
title: formatMessage(_messages.toolbarInsertBlockMessages.mediaFiles),
description: formatMessage(_messages.toolbarInsertBlockMessages.mediaFilesDescription),
priority: 400,
keywords: ['attachment', 'gif', 'media', 'picture', 'image', 'video', 'file'],
icon: function icon() {
return /*#__PURE__*/_react.default.createElement(_quickInsert.IconImages, null);
},
isDisabledOffline: true,
action: function action(insert, state) {
var _api$analytics6;
var pluginState = _pluginKey.stateKey.getState(state);
pluginState === null || pluginState === void 0 || pluginState.showMediaPicker();
var tr = insert('');
api === null || api === void 0 || (_api$analytics6 = api.analytics) === null || _api$analytics6 === void 0 || _api$analytics6.actions.attachAnalyticsEvent({
action: _analytics.ACTION.OPENED,
actionSubject: _analytics.ACTION_SUBJECT.PICKER,
actionSubjectId: _analytics.ACTION_SUBJECT_ID.PICKER_CLOUD,
attributes: {
inputMethod: _analytics.INPUT_METHOD.QUICK_INSERT
},
eventType: _analytics.EVENT_TYPE.UI
})(tr);
return tr;
}
}] : [];
},
floatingToolbar: function floatingToolbar(state, intl, providerFactory) {
var _api$editorViewMode;
return (0, _toolbar.floatingToolbar)(state, intl, {
providerFactory: providerFactory,
allowMediaInline: options && (0, _mediaCommon.getMediaFeatureFlag)('mediaInline', options.featureFlags),
allowResizing: options && options.allowResizing,
allowResizingInTables: options && options.allowResizingInTables,
allowCommentsOnMedia: options && options.allowCommentsOnMedia,
allowLinking: options && options.allowLinking,
allowAdvancedToolBarOptions: options && options.allowAdvancedToolBarOptions,
allowAltTextOnImages: options && options.allowAltTextOnImages,
allowImageEditing: !!(api !== null && api !== void 0 && api.mediaEditing) && options && options.allowImageEditing,
allowImagePreview: options && options.allowImagePreview,
altTextValidator: options && options.altTextValidator,
fullWidthEnabled: options && options.fullWidthEnabled,
allowMediaInlineImages: options && options.allowMediaInlineImages,
isViewOnly: (api === null || api === void 0 || (_api$editorViewMode = api.editorViewMode) === null || _api$editorViewMode === void 0 || (_api$editorViewMode = _api$editorViewMode.sharedState.currentState()) === null || _api$editorViewMode === void 0 ? void 0 : _api$editorViewMode.mode) === 'view',
allowPixelResizing: options === null || options === void 0 ? void 0 : options.allowPixelResizing,
onCommentButtonMount: options === null || options === void 0 ? void 0 : options.onCommentButtonMount,
createCommentExperience: options === null || options === void 0 ? void 0 : options.createCommentExperience
}, api);
}
}
};
};