UNPKG

@atlaskit/editor-plugin-synced-block

Version:

SyncedBlock plugin for @atlaskit/editor-core

214 lines (211 loc) 13.1 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.syncedBlockPlugin = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _react = _interopRequireDefault(require("react")); var _adfSchema = require("@atlaskit/adf-schema"); var _hooks = require("@atlaskit/editor-common/hooks"); var _editorSyncedBlockProvider = require("@atlaskit/editor-synced-block-provider"); var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled"); var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals"); var _editorActions = require("./editor-actions"); var _editorCommands = require("./editor-commands"); var _main = require("./pm-plugins/main"); var _menuAndToolbarExperiences = require("./pm-plugins/menu-and-toolbar-experiences"); var _blockMenuComponents = require("./ui/block-menu-components"); var _DeleteConfirmationModal = require("./ui/DeleteConfirmationModal"); var _Flag = require("./ui/Flag"); var _floatingToolbar = require("./ui/floating-toolbar"); var _quickInsert = require("./ui/quick-insert"); var _getSyncedBlockQuickInsertComponents = require("./ui/quick-insert/getSyncedBlockQuickInsertComponents"); var _SourceSyncBlockPlaceholder = require("./ui/SourceSyncBlockPlaceholder"); var _SyncBlockRefresher = require("./ui/SyncBlockRefresher"); var _toolbarComponents = require("./ui/toolbar-components"); 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; } /** * EDITOR-6929 / PR-G: Guard contentComponent rendering. * When `hasSyncedBlocks` is false return null * to avoid mounting SyncBlockRefresher, DeleteConfirmationModal, and Flag — * their hooks (useSharedPluginStateWithSelector) would execute selectors on * every transaction for no benefit on the ~99.98% of pages with zero synced * blocks. */ var LazySyncedBlockUI = function LazySyncedBlockUI(_ref) { var syncBlockStoreManager = _ref.syncBlockStore, api = _ref.api, editorView = _ref.editorView, onFeedbackPromptShown = _ref.onFeedbackPromptShown, onGiveFeedback = _ref.onGiveFeedback; var hasSyncBlocks = (0, _hooks.useSharedPluginStateWithSelector)(api, ['syncedBlock'], function (states) { var _states$syncedBlockSt; return (_states$syncedBlockSt = states.syncedBlockState) === null || _states$syncedBlockSt === void 0 ? void 0 : _states$syncedBlockSt.hasSyncedBlocks; }); if (!hasSyncBlocks) { return null; } return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_SyncBlockRefresher.SyncBlockRefresher, { syncBlockStoreManager: syncBlockStoreManager, api: api }), /*#__PURE__*/_react.default.createElement(_DeleteConfirmationModal.DeleteConfirmationModal, { syncBlockStoreManager: syncBlockStoreManager, api: api, editorView: editorView }), /*#__PURE__*/_react.default.createElement(_Flag.Flag, { api: api, onFeedbackPromptShown: onFeedbackPromptShown, onGiveFeedback: onGiveFeedback })); }; var syncedBlockPlugin = exports.syncedBlockPlugin = function syncedBlockPlugin(_ref2) { var _api$editorViewMode, _api$analytics, _api$blockMenu, _config$enableSourceC, _api$toolbar, _config$enableSourceC2; var config = _ref2.config, api = _ref2.api; var refs = {}; var viewMode = 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; var syncBlockStore = new _editorSyncedBlockProvider.SyncBlockStoreManager(config === null || config === void 0 ? void 0 : config.syncBlockDataProvider, viewMode, config === null || config === void 0 ? void 0 : config.__livePage); syncBlockStore.setFireAnalyticsEvent(api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || (_api$analytics = _api$analytics.actions) === null || _api$analytics === void 0 ? void 0 : _api$analytics.fireAnalyticsEvent); // --- Memoized getSharedState (EDITOR-6929 / PR-F) --- // Cache the last returned shared state object. On each call, perform a // shallow comparison of all fields against the cached value. If nothing // changed, return the cached reference so SharedStateAPI subscribers // (React components) skip re-rendering. var cachedSharedState; api === null || api === void 0 || (_api$blockMenu = api.blockMenu) === null || _api$blockMenu === void 0 || _api$blockMenu.actions.registerBlockMenuComponents((0, _blockMenuComponents.getBlockMenuComponents)(api, (_config$enableSourceC = config === null || config === void 0 ? void 0 : config.enableSourceCreation) !== null && _config$enableSourceC !== void 0 ? _config$enableSourceC : false)); api === null || api === void 0 || (_api$toolbar = api.toolbar) === null || _api$toolbar === void 0 || _api$toolbar.actions.registerComponents((0, _toolbarComponents.getToolbarComponents)(api, (_config$enableSourceC2 = config === null || config === void 0 ? void 0 : config.enableSourceCreation) !== null && _config$enableSourceC2 !== void 0 ? _config$enableSourceC2 : false)); var isRegisteredSlashCommandEnabled = (0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_slash_command'); if (isRegisteredSlashCommandEnabled && config !== null && config !== void 0 && config.enableSourceCreation) { var _api$uiControlRegistr; api === null || api === void 0 || (_api$uiControlRegistr = api.uiControlRegistry) === null || _api$uiControlRegistr === void 0 || _api$uiControlRegistr.actions.register((0, _getSyncedBlockQuickInsertComponents.getSyncedBlockQuickInsertComponents)({ api: api, syncBlockStore: syncBlockStore })); } return { name: 'syncedBlock', nodes: function nodes() { return [{ name: 'syncBlock', node: _adfSchema.syncBlock }, { name: 'bodiedSyncBlock', node: _adfSchema.bodiedSyncBlock }]; }, pmPlugins: function pmPlugins() { return [{ name: 'syncedBlockPlugin', plugin: function plugin(params) { return (0, _main.createPlugin)(config, params, syncBlockStore, api); } }, { name: 'menuAndToolbarExperiencesPlugin', plugin: function plugin() { return (0, _menuAndToolbarExperiences.getMenuAndToolbarExperiencesPlugin)({ refs: refs, dispatchAnalyticsEvent: function dispatchAnalyticsEvent(payload) { var _api$analytics2; return api === null || api === void 0 || (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 || (_api$analytics2 = _api$analytics2.actions) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.fireAnalyticsEvent(payload); } }); } }]; }, commands: { copySyncedBlockReferenceToClipboard: function copySyncedBlockReferenceToClipboard(inputMethod) { return (0, _editorCommands.copySyncedBlockReferenceToClipboardEditorCommand)(syncBlockStore, inputMethod, api, config === null || config === void 0 ? void 0 : config.__livePage); }, insertSyncedBlock: function insertSyncedBlock(inputMethod) { return function (_ref3) { var _api$analytics3; var tr = _ref3.tr; if (!(config !== null && config !== void 0 && config.enableSourceCreation)) { return null; } return (0, _editorCommands.createSyncedBlock)({ tr: tr, syncBlockStore: syncBlockStore, fireAnalyticsEvent: api === null || api === void 0 || (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : _api$analytics3.actions.fireAnalyticsEvent, inputMethod: inputMethod }) || null; }; } }, actions: { flushBodiedSyncBlocks: function flushBodiedSyncBlocks() { return (0, _editorActions.flushBodiedSyncBlocks)(syncBlockStore); }, flushSyncedBlocks: function flushSyncedBlocks() { return (0, _editorActions.flushSyncBlocks)(syncBlockStore); }, discardUnpublishedSyncBlocks: function discardUnpublishedSyncBlocks() { return (0, _editorActions.discardUnpublishedSyncBlocks)(syncBlockStore); } }, pluginsOptions: _objectSpread(_objectSpread({}, !isRegisteredSlashCommandEnabled && { quickInsert: (0, _quickInsert.getQuickInsertConfig)(config, api, syncBlockStore) }), {}, { floatingToolbar: function floatingToolbar(state, intl) { var _syncedBlockPluginKey; // When registered slash-command support is on and the document has no synced blocks, // skip the toolbar config entirely to avoid the per-selection-change // cost of findSyncBlockOrBodiedSyncBlock (EDITOR-6931). if (!((_syncedBlockPluginKey = _main.syncedBlockPluginKey.getState(state)) !== null && _syncedBlockPluginKey !== void 0 && _syncedBlockPluginKey.hasSyncedBlocks)) { return undefined; } return (0, _floatingToolbar.getToolbarConfig)(state, intl, api, syncBlockStore, config === null || config === void 0 ? void 0 : config.__livePage, config === null || config === void 0 ? void 0 : config.onGiveFeedback); } }), contentComponent: function contentComponent(_ref4) { var containerElement = _ref4.containerElement, wrapperElement = _ref4.wrapperElement, popupsMountPoint = _ref4.popupsMountPoint, editorView = _ref4.editorView; refs.containerElement = containerElement || undefined; refs.popupsMountPoint = popupsMountPoint || undefined; refs.wrapperElement = wrapperElement || undefined; return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, (0, _expValEquals.expValEquals)('platform_editor_sync_block_activation', 'isEnabled', true) && /*#__PURE__*/_react.default.createElement(_SourceSyncBlockPlaceholder.SourceSyncBlockPlaceholder, null), /*#__PURE__*/_react.default.createElement(LazySyncedBlockUI, { syncBlockStore: syncBlockStore, api: api, editorView: editorView, onFeedbackPromptShown: config === null || config === void 0 ? void 0 : config.onFeedbackPromptShown, onGiveFeedback: config === null || config === void 0 ? void 0 : config.onGiveFeedback })); }, getSharedState: function getSharedState(editorState) { if (!editorState) { return; } var pluginState = _main.syncedBlockPluginKey.getState(editorState); var activeFlag = pluginState.activeFlag, currentSyncBlockStore = pluginState.syncBlockStore, bodiedSyncBlockDeletionStatus = pluginState.bodiedSyncBlockDeletionStatus, retryCreationPosMap = pluginState.retryCreationPosMap, hasSyncedBlocks = pluginState.hasSyncedBlocks, hasUnsavedBodiedSyncBlockChanges = pluginState.hasUnsavedBodiedSyncBlockChanges; // --- EDITOR-6929 / PR-F: return a stable reference when all // fields are unchanged to prevent unnecessary React re-renders. --- if (cachedSharedState !== undefined && cachedSharedState.activeFlag === activeFlag && cachedSharedState.syncBlockStore === currentSyncBlockStore && cachedSharedState.bodiedSyncBlockDeletionStatus === bodiedSyncBlockDeletionStatus && cachedSharedState.retryCreationPosMap === retryCreationPosMap && cachedSharedState.hasSyncedBlocks === hasSyncedBlocks && cachedSharedState.hasUnsavedBodiedSyncBlockChanges === hasUnsavedBodiedSyncBlockChanges) { return cachedSharedState; } var nextSharedState = { activeFlag: activeFlag, syncBlockStore: currentSyncBlockStore, bodiedSyncBlockDeletionStatus: bodiedSyncBlockDeletionStatus, retryCreationPosMap: retryCreationPosMap, hasSyncedBlocks: hasSyncedBlocks, hasUnsavedBodiedSyncBlockChanges: hasUnsavedBodiedSyncBlockChanges }; cachedSharedState = nextSharedState; return nextSharedState; }, // Destroy the SyncBlockStoreManager on editor unmount to cancel // pending timers, subscriptions, and in-flight fetches. destroy: function destroy() { syncBlockStore.destroy(); } }; };