UNPKG

@wordpress/block-editor

Version:
495 lines (467 loc) 14.9 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.__experimentalUpdateSettings = __experimentalUpdateSettings; exports.clearBlockRemovalPrompt = clearBlockRemovalPrompt; exports.deleteStyleOverride = deleteStyleOverride; exports.ensureDefaultBlock = void 0; exports.expandBlock = expandBlock; exports.hideBlockInterface = hideBlockInterface; exports.privateRemoveBlocks = exports.modifyContentLockBlock = void 0; exports.resetZoomLevel = resetZoomLevel; exports.setBlockRemovalRules = setBlockRemovalRules; exports.setInsertionPoint = setInsertionPoint; exports.setLastFocus = setLastFocus; exports.setOpenedBlockSettingsMenu = setOpenedBlockSettingsMenu; exports.setStyleOverride = setStyleOverride; exports.setZoomLevel = void 0; exports.showBlockInterface = showBlockInterface; exports.startDragging = startDragging; exports.stopDragging = stopDragging; exports.stopEditingAsBlocks = stopEditingAsBlocks; var _element = require("@wordpress/element"); var _deprecated = _interopRequireDefault(require("@wordpress/deprecated")); var _a11y = require("@wordpress/a11y"); var _i18n = require("@wordpress/i18n"); var _index = require("./index"); var _lockUnlock = require("../lock-unlock"); /** * WordPress dependencies */ /** * Internal dependencies */ const castArray = maybeArray => Array.isArray(maybeArray) ? maybeArray : [maybeArray]; /** * A list of private/experimental block editor settings that * should not become a part of the WordPress public API. * BlockEditorProvider will remove these settings from the * settings object it receives. * * @see https://github.com/WordPress/gutenberg/pull/46131 */ const privateSettings = ['inserterMediaCategories', 'blockInspectorAnimation', 'mediaSideload']; /** * Action that updates the block editor settings and * conditionally preserves the experimental ones. * * @param {Object} settings Updated settings * @param {Object} options Options object. * @param {boolean} options.stripExperimentalSettings Whether to strip experimental settings. * @param {boolean} options.reset Whether to reset the settings. * @return {Object} Action object */ function __experimentalUpdateSettings(settings, { stripExperimentalSettings = false, reset = false } = {}) { let incomingSettings = settings; if (Object.hasOwn(incomingSettings, '__unstableIsPreviewMode')) { (0, _deprecated.default)("__unstableIsPreviewMode argument in wp.data.dispatch('core/block-editor').updateSettings", { since: '6.8', alternative: 'isPreviewMode' }); incomingSettings = { ...incomingSettings }; incomingSettings.isPreviewMode = incomingSettings.__unstableIsPreviewMode; delete incomingSettings.__unstableIsPreviewMode; } let cleanSettings = incomingSettings; // There are no plugins in the mobile apps, so there is no // need to strip the experimental settings: if (stripExperimentalSettings && _element.Platform.OS === 'web') { cleanSettings = {}; for (const key in incomingSettings) { if (!privateSettings.includes(key)) { cleanSettings[key] = incomingSettings[key]; } } } return { type: 'UPDATE_SETTINGS', settings: cleanSettings, reset }; } /** * Hides the block interface (eg. toolbar, outline, etc.) * * @return {Object} Action object. */ function hideBlockInterface() { return { type: 'HIDE_BLOCK_INTERFACE' }; } /** * Shows the block interface (eg. toolbar, outline, etc.) * * @return {Object} Action object. */ function showBlockInterface() { return { type: 'SHOW_BLOCK_INTERFACE' }; } /** * Yields action objects used in signalling that the blocks corresponding to * the set of specified client IDs are to be removed. * * Compared to `removeBlocks`, this private interface exposes an additional * parameter; see `forceRemove`. * * @param {string|string[]} clientIds Client IDs of blocks to remove. * @param {boolean} selectPrevious True if the previous block * or the immediate parent * (if no previous block exists) * should be selected * when a block is removed. * @param {boolean} forceRemove Whether to force the operation, * bypassing any checks for certain * block types. */ const privateRemoveBlocks = (clientIds, selectPrevious = true, forceRemove = false) => ({ select, dispatch, registry }) => { if (!clientIds || !clientIds.length) { return; } clientIds = castArray(clientIds); const canRemoveBlocks = select.canRemoveBlocks(clientIds); if (!canRemoveBlocks) { return; } // In certain editing contexts, we'd like to prevent accidental removal // of important blocks. For example, in the site editor, the Query Loop // block is deemed important. In such cases, we'll ask the user for // confirmation that they intended to remove such block(s). However, // the editor instance is responsible for presenting those confirmation // prompts to the user. Any instance opting into removal prompts must // register using `setBlockRemovalRules()`. // // @see https://github.com/WordPress/gutenberg/pull/51145 const rules = !forceRemove && select.getBlockRemovalRules(); if (rules) { function flattenBlocks(blocks) { const result = []; const stack = [...blocks]; while (stack.length) { const { innerBlocks, ...block } = stack.shift(); stack.push(...innerBlocks); result.push(block); } return result; } const blockList = clientIds.map(select.getBlock); const flattenedBlocks = flattenBlocks(blockList); // Find the first message and use it. let message; for (const rule of rules) { message = rule.callback(flattenedBlocks); if (message) { dispatch(displayBlockRemovalPrompt(clientIds, selectPrevious, message)); return; } } } if (selectPrevious) { dispatch.selectPreviousBlock(clientIds[0], selectPrevious); } // We're batching these two actions because an extra `undo/redo` step can // be created, based on whether we insert a default block or not. registry.batch(() => { dispatch({ type: 'REMOVE_BLOCKS', clientIds }); // To avoid a focus loss when removing the last block, assure there is // always a default block if the last of the blocks have been removed. dispatch(ensureDefaultBlock()); }); }; /** * Action which will insert a default block insert action if there * are no other blocks at the root of the editor. This action should be used * in actions which may result in no blocks remaining in the editor (removal, * replacement, etc). */ exports.privateRemoveBlocks = privateRemoveBlocks; const ensureDefaultBlock = () => ({ select, dispatch }) => { // To avoid a focus loss when removing the last block, assure there is // always a default block if the last of the blocks have been removed. const count = select.getBlockCount(); if (count > 0) { return; } // If there's an custom appender, don't insert default block. // We have to remember to manually move the focus elsewhere to // prevent it from being lost though. const { __unstableHasCustomAppender } = select.getSettings(); if (__unstableHasCustomAppender) { return; } dispatch.insertDefaultBlock(); }; /** * Returns an action object used in signalling that a block removal prompt must * be displayed. * * Contrast with `setBlockRemovalRules`. * * @param {string|string[]} clientIds Client IDs of blocks to remove. * @param {boolean} selectPrevious True if the previous block or the * immediate parent (if no previous * block exists) should be selected * when a block is removed. * @param {string} message Message to display in the prompt. * * @return {Object} Action object. */ exports.ensureDefaultBlock = ensureDefaultBlock; function displayBlockRemovalPrompt(clientIds, selectPrevious, message) { return { type: 'DISPLAY_BLOCK_REMOVAL_PROMPT', clientIds, selectPrevious, message }; } /** * Returns an action object used in signalling that a block removal prompt must * be cleared, either be cause the user has confirmed or canceled the request * for removal. * * @return {Object} Action object. */ function clearBlockRemovalPrompt() { return { type: 'CLEAR_BLOCK_REMOVAL_PROMPT' }; } /** * Returns an action object used to set up any rules that a block editor may * provide in order to prevent a user from accidentally removing certain * blocks. These rules are then used to display a confirmation prompt to the * user. For instance, in the Site Editor, the Query Loop block is important * enough to warrant such confirmation. * * IMPORTANT: Registering rules implicitly signals to the `privateRemoveBlocks` * action that the editor will be responsible for displaying block removal * prompts and confirming deletions. This action is meant to be used by * component `BlockRemovalWarningModal` only. * * The data is a record whose keys are block types (e.g. 'core/query') and * whose values are the explanation to be shown to users (e.g. 'Query Loop * displays a list of posts or pages.'). * * Contrast with `displayBlockRemovalPrompt`. * * @param {Record<string,string>|false} rules Block removal rules. * @return {Object} Action object. */ function setBlockRemovalRules(rules = false) { return { type: 'SET_BLOCK_REMOVAL_RULES', rules }; } /** * Sets the client ID of the block settings menu that is currently open. * * @param {?string} clientId The block client ID. * @return {Object} Action object. */ function setOpenedBlockSettingsMenu(clientId) { return { type: 'SET_OPENED_BLOCK_SETTINGS_MENU', clientId }; } function setStyleOverride(id, style) { return { type: 'SET_STYLE_OVERRIDE', id, style }; } function deleteStyleOverride(id) { return { type: 'DELETE_STYLE_OVERRIDE', id }; } /** * Action that sets the element that had focus when focus leaves the editor canvas. * * @param {Object} lastFocus The last focused element. * * * @return {Object} Action object. */ function setLastFocus(lastFocus = null) { return { type: 'LAST_FOCUS', lastFocus }; } /** * Action that stops temporarily editing as blocks. * * @param {string} clientId The block's clientId. */ function stopEditingAsBlocks(clientId) { return ({ select, dispatch, registry }) => { const focusModeToRevert = (0, _lockUnlock.unlock)(registry.select(_index.store)).getTemporarilyEditingFocusModeToRevert(); dispatch.__unstableMarkNextChangeAsNotPersistent(); dispatch.updateBlockAttributes(clientId, { templateLock: 'contentOnly' }); dispatch.updateBlockListSettings(clientId, { ...select.getBlockListSettings(clientId), templateLock: 'contentOnly' }); dispatch.updateSettings({ focusMode: focusModeToRevert }); dispatch.__unstableSetTemporarilyEditingAsBlocks(); }; } /** * Returns an action object used in signalling that the user has begun to drag. * * @return {Object} Action object. */ function startDragging() { return { type: 'START_DRAGGING' }; } /** * Returns an action object used in signalling that the user has stopped dragging. * * @return {Object} Action object. */ function stopDragging() { return { type: 'STOP_DRAGGING' }; } /** * @param {string|null} clientId The block's clientId, or `null` to clear. * * @return {Object} Action object. */ function expandBlock(clientId) { return { type: 'SET_BLOCK_EXPANDED_IN_LIST_VIEW', clientId }; } /** * @param {Object} value * @param {string} value.rootClientId The root client ID to insert at. * @param {number} value.index The index to insert at. * * @return {Object} Action object. */ function setInsertionPoint(value) { return { type: 'SET_INSERTION_POINT', value }; } /** * Temporarily modify/unlock the content-only block for editions. * * @param {string} clientId The client id of the block. */ const modifyContentLockBlock = clientId => ({ select, dispatch }) => { dispatch.selectBlock(clientId); dispatch.__unstableMarkNextChangeAsNotPersistent(); dispatch.updateBlockAttributes(clientId, { templateLock: undefined }); dispatch.updateBlockListSettings(clientId, { ...select.getBlockListSettings(clientId), templateLock: false }); const focusModeToRevert = select.getSettings().focusMode; dispatch.updateSettings({ focusMode: true }); dispatch.__unstableSetTemporarilyEditingAsBlocks(clientId, focusModeToRevert); }; /** * Sets the zoom level. * * @param {number} zoom the new zoom level * @return {Object} Action object. */ exports.modifyContentLockBlock = modifyContentLockBlock; const setZoomLevel = (zoom = 100) => ({ select, dispatch }) => { // When switching to zoom-out mode, we need to select the parent section if (zoom !== 100) { const firstSelectedClientId = select.getBlockSelectionStart(); const sectionRootClientId = select.getSectionRootClientId(); if (firstSelectedClientId) { let sectionClientId; if (sectionRootClientId) { const sectionClientIds = select.getBlockOrder(sectionRootClientId); // If the selected block is a section block, use it. if (sectionClientIds?.includes(firstSelectedClientId)) { sectionClientId = firstSelectedClientId; } else { // If the selected block is not a section block, find // the parent section that contains the selected block. sectionClientId = select.getBlockParents(firstSelectedClientId).find(parent => sectionClientIds.includes(parent)); } } else { sectionClientId = select.getBlockHierarchyRootClientId(firstSelectedClientId); } if (sectionClientId) { dispatch.selectBlock(sectionClientId); } else { dispatch.clearSelectedBlock(); } (0, _a11y.speak)((0, _i18n.__)('You are currently in zoom-out mode.')); } } dispatch({ type: 'SET_ZOOM_LEVEL', zoom }); }; /** * Resets the Zoom state. * @return {Object} Action object. */ exports.setZoomLevel = setZoomLevel; function resetZoomLevel() { return { type: 'RESET_ZOOM_LEVEL' }; } //# sourceMappingURL=private-actions.js.map