UNPKG

@atlaskit/editor-plugin-media-insert

Version:

Media Insert plugin for @atlaskit/editor-core

368 lines (366 loc) 18.7 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _typeof = require("@babel/runtime/helpers/typeof"); Object.defineProperty(exports, "__esModule", { value: true }); exports.MediaInsertPicker = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _react = _interopRequireDefault(require("react")); var _reactIntl = require("react-intl"); var _analytics = require("@atlaskit/editor-common/analytics"); var _getDomRefFromSelection = require("@atlaskit/editor-common/get-dom-ref-from-selection"); var _hooks = require("@atlaskit/editor-common/hooks"); var _messages = require("@atlaskit/editor-common/messages"); var _ui = require("@atlaskit/editor-common/ui"); var _editorSharedStyles = require("@atlaskit/editor-shared-styles"); var _compiled = require("@atlaskit/primitives/compiled"); var _tabs = _interopRequireWildcard(require("@atlaskit/tabs")); var _expValEqualsNoExposure = require("@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure"); var _useFocusEditor = require("./hooks/use-focus-editor"); var _useUnholyAutofocus2 = require("./hooks/use-unholy-autofocus"); var _LocalMedia = require("./LocalMedia"); var _MediaFromURL = require("./MediaFromURL"); var _MediaInsertWrapper = require("./MediaInsertWrapper"); 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 PopupWithListeners = (0, _ui.withOuterListeners)(_ui.Popup); var MEDIA_INSERT_PICKER_ANALYTICS_SOURCE = 'MediaInsertPicker'; var EMPTY_REGISTERED_TABS = []; var getMediaInsertPickerTabSource = function getMediaInsertPickerTabSource(selectedTab) { return "".concat(MEDIA_INSERT_PICKER_ANALYTICS_SOURCE, " - ").concat(selectedTab); }; var getNextTabIndexForKey = function getNextTabIndexForKey(currentTabIndex, tabCount, key) { if (key === 'Home') { return 0; } if (key === 'End') { return tabCount - 1; } if (key === 'ArrowRight') { return currentTabIndex === tabCount - 1 ? 0 : currentTabIndex + 1; } if (key === 'ArrowLeft') { return currentTabIndex === 0 ? tabCount - 1 : currentTabIndex - 1; } return undefined; }; var getLinkTabIndex = function getLinkTabIndex(registeredTabCount, isOnlyExternalLinks) { return registeredTabCount + (isOnlyExternalLinks ? 0 : 1); }; var TabWithAnalytics = function TabWithAnalytics(_ref) { var children = _ref.children, onSelectTabForAnalytics = _ref.onSelectTabForAnalytics, selectedTabIndex = _ref.selectedTabIndex, tabCount = _ref.tabCount; var _useTab = (0, _tabs.useTab)(), onClick = _useTab.onClick, id = _useTab.id, ariaControls = _useTab['aria-controls'], ariaPosinset = _useTab['aria-posinset'], ariaSelected = _useTab['aria-selected'], ariaSetsize = _useTab['aria-setsize'], onKeyDown = _useTab.onKeyDown, role = _useTab.role, tabIndex = _useTab.tabIndex; var handleClick = _react.default.useCallback(function () { onSelectTabForAnalytics(selectedTabIndex); onClick(); }, [onClick, onSelectTabForAnalytics, selectedTabIndex]); var handleKeyDown = _react.default.useCallback(function (event) { var nextTabIndex = getNextTabIndexForKey(selectedTabIndex, tabCount, event.key); if (nextTabIndex !== undefined) { onSelectTabForAnalytics(nextTabIndex); } onKeyDown(event); }, [onKeyDown, onSelectTabForAnalytics, selectedTabIndex, tabCount]); return /*#__PURE__*/_react.default.createElement(_compiled.Focusable, { as: "div", isInset: true, onClick: handleClick, id: id, "aria-controls": ariaControls, "aria-posinset": ariaPosinset, "aria-selected": ariaSelected, "aria-setsize": ariaSetsize, onKeyDown: handleKeyDown, role: role, tabIndex: tabIndex }, /*#__PURE__*/_react.default.createElement(_compiled.Text, { weight: "medium", color: "inherit", maxLines: 1 }, children)); }; /** * A custom TabPanel that is non-focusable. */ var CustomTabPanel = function CustomTabPanel(_ref2) { var children = _ref2.children, _ref2$disablePaddingB = _ref2.disablePaddingBlockEnd, disablePaddingBlockEnd = _ref2$disablePaddingB === void 0 ? false : _ref2$disablePaddingB; var tabPanelAttributes = (0, _tabs.useTabPanel)(); return /*#__PURE__*/_react.default.createElement(_compiled.Box, (0, _extends2.default)({ paddingBlockEnd: disablePaddingBlockEnd ? 'space.0' : 'space.150' // Ignored via go/ees005 // eslint-disable-next-line react/jsx-props-no-spreading }, tabPanelAttributes, { tabIndex: -1 }), children); }; var MediaInsertPicker = exports.MediaInsertPicker = function MediaInsertPicker(_ref3) { var _api$mediaInsert$acti, _api$mediaInsert, _api$mediaInsert$getI; var api = _ref3.api, editorView = _ref3.editorView, dispatchAnalyticsEvent = _ref3.dispatchAnalyticsEvent, popupsMountPoint = _ref3.popupsMountPoint, popupsBoundariesElement = _ref3.popupsBoundariesElement, popupsScrollableElement = _ref3.popupsScrollableElement, _closeMediaInsertPicker = _ref3.closeMediaInsertPicker, insertMediaSingle = _ref3.insertMediaSingle, insertExternalMediaSingle = _ref3.insertExternalMediaSingle, insertFile = _ref3.insertFile, _ref3$isOnlyExternalL = _ref3.isOnlyExternalLinks, isOnlyExternalLinks = _ref3$isOnlyExternalL === void 0 ? false : _ref3$isOnlyExternalL, customizedUrlValidation = _ref3.customizedUrlValidation, customizedHelperMessage = _ref3.customizedHelperMessage; // Tabs registered by other plugins via `api.mediaInsert.actions.registerInsertTab(...)`. // Read once per render; the registry is mutated only at plugin setup time so this is stable // for the lifetime of an editor instance. var registeredTabs = (_api$mediaInsert$acti = api === null || api === void 0 || (_api$mediaInsert = api.mediaInsert) === null || _api$mediaInsert === void 0 || (_api$mediaInsert = _api$mediaInsert.actions) === null || _api$mediaInsert === void 0 || (_api$mediaInsert$getI = _api$mediaInsert.getInsertTabs) === null || _api$mediaInsert$getI === void 0 ? void 0 : _api$mediaInsert$getI.call(_api$mediaInsert)) !== null && _api$mediaInsert$acti !== void 0 ? _api$mediaInsert$acti : EMPTY_REGISTERED_TABS; var _useSharedPluginState = (0, _hooks.useSharedPluginStateWithSelector)(api, ['media', 'mediaInsert'], function (states) { var _states$mediaState, _states$mediaInsertSt, _states$mediaInsertSt2; return { mediaProvider: (_states$mediaState = states.mediaState) === null || _states$mediaState === void 0 ? void 0 : _states$mediaState.mediaProvider, isOpen: (_states$mediaInsertSt = states.mediaInsertState) === null || _states$mediaInsertSt === void 0 ? void 0 : _states$mediaInsertSt.isOpen, mountInfo: (_states$mediaInsertSt2 = states.mediaInsertState) === null || _states$mediaInsertSt2 === void 0 ? void 0 : _states$mediaInsertSt2.mountInfo }; }), mediaProvider = _useSharedPluginState.mediaProvider, isOpen = _useSharedPluginState.isOpen, mountInfo = _useSharedPluginState.mountInfo; var targetRef; var mountPoint; if (mountInfo) { targetRef = mountInfo.ref; mountPoint = mountInfo.mountPoint; } else { var _api$analytics; // If targetRef is undefined, target the selection in the editor targetRef = (0, _getDomRefFromSelection.getDomRefFromSelection)(editorView, _analytics.ACTION_SUBJECT_ID.PICKER_MEDIA, api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions); mountPoint = popupsMountPoint; } var intl = (0, _reactIntl.useIntl)(); var focusEditor = (0, _useFocusEditor.useFocusEditor)({ editorView: editorView }); var _useUnholyAutofocus = (0, _useUnholyAutofocus2.useUnholyAutofocus)(), autofocusRef = _useUnholyAutofocus.autofocusRef, onPositionCalculated = _useUnholyAutofocus.onPositionCalculated; var tabCount = registeredTabs.length + (isOnlyExternalLinks ? 1 : 2); var linkTabIndex = getLinkTabIndex(registeredTabs.length, isOnlyExternalLinks); var getTabAnalyticsMetadata = _react.default.useCallback(function (selectedTabIndex) { var registeredTab = registeredTabs[selectedTabIndex]; if (registeredTab) { return { selectedTab: registeredTab.key, selectedTabIndex: selectedTabIndex }; } var uploadTabIndex = registeredTabs.length; if (!isOnlyExternalLinks && selectedTabIndex === uploadTabIndex) { return { selectedTab: _analytics.MEDIA_INSERT_TAB.UPLOAD, selectedTabIndex: selectedTabIndex }; } if (selectedTabIndex === linkTabIndex) { return { selectedTab: _analytics.MEDIA_INSERT_TAB.LINK, selectedTabIndex: selectedTabIndex }; } return { selectedTab: 'unknown', selectedTabIndex: selectedTabIndex }; }, [isOnlyExternalLinks, linkTabIndex, registeredTabs]); var selectedTabAnalyticsMetadataRef = _react.default.useRef(getTabAnalyticsMetadata(0)); var tabsAnalyticsContext = _react.default.useMemo(function () { return { get source() { return getMediaInsertPickerTabSource(selectedTabAnalyticsMetadataRef.current.selectedTab); } }; }, []); var setSelectedTabAnalyticsMetadata = _react.default.useCallback(function (selectedTabIndex) { selectedTabAnalyticsMetadataRef.current = getTabAnalyticsMetadata(selectedTabIndex); }, [getTabAnalyticsMetadata]); var hasDispatchedInitialTabViewedEventRef = _react.default.useRef(false); // Atlaskit Tabs only calls `onChange` after the user changes tabs, so the // initially opened tab needs its viewed analytics event dispatched from an // effect. The ref keeps this to once per picker open without setting state. _react.default.useEffect(function () { if (!isOpen) { hasDispatchedInitialTabViewedEventRef.current = false; return; } if (!mediaProvider || !dispatchAnalyticsEvent || hasDispatchedInitialTabViewedEventRef.current) { return; } var selectedTabMetadata = getTabAnalyticsMetadata(0); selectedTabAnalyticsMetadataRef.current = selectedTabMetadata; var payload = { action: _analytics.ACTION.VIEWED, actionSubject: _analytics.ACTION_SUBJECT.PICKER, actionSubjectId: _analytics.ACTION_SUBJECT_ID.PICKER_MEDIA, eventType: _analytics.EVENT_TYPE.UI, attributes: { selectedTab: selectedTabMetadata.selectedTab, selectedTabIndex: selectedTabMetadata.selectedTabIndex } }; dispatchAnalyticsEvent(payload); hasDispatchedInitialTabViewedEventRef.current = true; }, [dispatchAnalyticsEvent, getTabAnalyticsMetadata, isOpen, mediaProvider]); var handleTabChange = _react.default.useCallback(function (selectedTabIndex, analyticsEvent) { var selectedTabMetadata = getTabAnalyticsMetadata(selectedTabIndex); selectedTabAnalyticsMetadataRef.current = selectedTabMetadata; analyticsEvent.update(function (payload) { return _objectSpread(_objectSpread({}, payload), {}, { attributes: _objectSpread(_objectSpread({}, payload.attributes), {}, { selectedTab: selectedTabMetadata.selectedTab, selectedTabIndex: selectedTabIndex }) }); }).fire(); if (dispatchAnalyticsEvent) { var payload = { action: _analytics.ACTION.VIEWED, actionSubject: _analytics.ACTION_SUBJECT.PICKER, actionSubjectId: _analytics.ACTION_SUBJECT_ID.PICKER_MEDIA, eventType: _analytics.EVENT_TYPE.UI, attributes: { selectedTab: selectedTabMetadata.selectedTab, selectedTabIndex: selectedTabIndex } }; dispatchAnalyticsEvent(payload); } }, [dispatchAnalyticsEvent, getTabAnalyticsMetadata]); if (!isOpen || !mediaProvider) { return null; } var handleClose = function handleClose(exitMethod) { return function (event) { // Same as AIImageGenerationPopup: react-select can detach the option // before `click` fires, so withOuterListeners treats it as outside. if (exitMethod === _analytics.INPUT_METHOD.MOUSE && event.target instanceof Node && !event.target.isConnected) { return; } event.preventDefault(); if (dispatchAnalyticsEvent) { var payload = { action: _analytics.ACTION.CLOSED, actionSubject: _analytics.ACTION_SUBJECT.PICKER, actionSubjectId: _analytics.ACTION_SUBJECT_ID.PICKER_MEDIA, eventType: _analytics.EVENT_TYPE.UI, attributes: { exitMethod: exitMethod } }; dispatchAnalyticsEvent(payload); } _closeMediaInsertPicker(); focusEditor(); }; }; var fileTabTitle = (0, _expValEqualsNoExposure.expValEqualsNoExposure)('cc_page_experiences_editor_image_generation', 'isEnabled', true) ? intl.formatMessage(_messages.mediaInsertMessages.uploadTabTitle) : intl.formatMessage(_messages.mediaInsertMessages.fileTabTitle); return /*#__PURE__*/_react.default.createElement(PopupWithListeners, { ariaLabel: intl.formatMessage(_messages.mediaInsertMessages.mediaPickerPopupAriaLabel) // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) , offset: [0, 12], target: targetRef, zIndex: _editorSharedStyles.akEditorFloatingDialogZIndex, fitHeight: 390, fitWidth: 340, mountTo: mountPoint, boundariesElement: popupsBoundariesElement, handleClickOutside: handleClose(_analytics.INPUT_METHOD.MOUSE), handleEscapeKeydown: handleClose(_analytics.INPUT_METHOD.KEYBOARD), scrollableElement: popupsScrollableElement, preventOverflow: true, onPositionCalculated: onPositionCalculated, focusTrap: true }, /*#__PURE__*/_react.default.createElement(_ui.PlainOutsideClickTargetRefContext.Consumer, null, function (setOutsideClickTargetRef) { return /*#__PURE__*/_react.default.createElement(_MediaInsertWrapper.MediaInsertWrapper, { ref: setOutsideClickTargetRef }, /*#__PURE__*/_react.default.createElement(_tabs.default, { id: "media-insert-tab-navigation", analyticsContext: tabsAnalyticsContext, onChange: handleTabChange }, /*#__PURE__*/_react.default.createElement(_compiled.Box, { paddingBlockEnd: "space.150" }, /*#__PURE__*/_react.default.createElement(_tabs.TabList, null, registeredTabs.map(function (tab, index) { return /*#__PURE__*/_react.default.createElement(TabWithAnalytics, { key: tab.key, onSelectTabForAnalytics: setSelectedTabAnalyticsMetadata, selectedTabIndex: index, tabCount: tabCount }, tab.label); }), !isOnlyExternalLinks && /*#__PURE__*/_react.default.createElement(TabWithAnalytics, { onSelectTabForAnalytics: setSelectedTabAnalyticsMetadata, selectedTabIndex: registeredTabs.length, tabCount: tabCount }, fileTabTitle), /*#__PURE__*/_react.default.createElement(TabWithAnalytics, { onSelectTabForAnalytics: setSelectedTabAnalyticsMetadata, selectedTabIndex: linkTabIndex, tabCount: tabCount }, intl.formatMessage(_messages.mediaInsertMessages.linkTabTitle)))), registeredTabs.map(function (_ref4) { var key = _ref4.key, TabComponent = _ref4.component; return /*#__PURE__*/_react.default.createElement(CustomTabPanel, { key: key, disablePaddingBlockEnd: true }, /*#__PURE__*/_react.default.createElement(TabComponent // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) , { closeMediaInsertPicker: function closeMediaInsertPicker() { _closeMediaInsertPicker(); focusEditor(); }, dispatchAnalyticsEvent: dispatchAnalyticsEvent, insertMediaSingle: insertMediaSingle, mediaProvider: mediaProvider })); }), !isOnlyExternalLinks && /*#__PURE__*/_react.default.createElement(CustomTabPanel, null, /*#__PURE__*/_react.default.createElement(_LocalMedia.LocalMedia, { ref: autofocusRef, mediaProvider: mediaProvider // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) , closeMediaInsertPicker: function closeMediaInsertPicker() { _closeMediaInsertPicker(); focusEditor(); }, dispatchAnalyticsEvent: dispatchAnalyticsEvent, insertFile: insertFile })), /*#__PURE__*/_react.default.createElement(CustomTabPanel, null, /*#__PURE__*/_react.default.createElement(_MediaFromURL.MediaFromURL, { mediaProvider: mediaProvider, dispatchAnalyticsEvent: dispatchAnalyticsEvent // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) , closeMediaInsertPicker: function closeMediaInsertPicker() { _closeMediaInsertPicker(); focusEditor(); }, insertMediaSingle: insertMediaSingle, insertExternalMediaSingle: insertExternalMediaSingle, isOnlyExternalLinks: isOnlyExternalLinks, customizedUrlValidation: customizedUrlValidation, customizedHelperMessage: customizedHelperMessage })))); })); };