@wordpress/block-editor
Version:
8 lines (7 loc) • 24.7 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../../../src/components/provider/use-block-sync.js"],
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { useContext, useEffect, useRef } from '@wordpress/element';\nimport { useRegistry } from '@wordpress/data';\nimport { cloneBlock } from '@wordpress/blocks';\n\n/**\n * Internal dependencies\n */\nimport { store as blockEditorStore } from '../../store';\nimport { SelectionContext } from './selection-context';\n\nconst noop = () => {};\n\n/**\n * Clones a block and its inner blocks, building a bidirectional mapping\n * between external (original) and internal (cloned) client IDs.\n *\n * This allows the block editor to use unique internal IDs while preserving\n * stable external IDs for features like real-time collaboration.\n *\n * @param {Object} block The block to clone.\n * @param {Object} mapping The mapping object with externalToInternal and internalToExternal Maps.\n * @return {Object} The cloned block with a new clientId.\n */\nfunction cloneBlockWithMapping( block, mapping ) {\n\tconst clonedBlock = cloneBlock( block );\n\n\t// Build bidirectional mapping\n\tmapping.externalToInternal.set( block.clientId, clonedBlock.clientId );\n\tmapping.internalToExternal.set( clonedBlock.clientId, block.clientId );\n\n\t// Recursively map inner blocks\n\tif ( block.innerBlocks?.length ) {\n\t\tclonedBlock.innerBlocks = block.innerBlocks.map( ( innerBlock ) => {\n\t\t\tconst clonedInner = cloneBlockWithMapping( innerBlock, mapping );\n\t\t\t// The clonedBlock already has cloned inner blocks from cloneBlock(),\n\t\t\t// but we need to use our mapped versions to maintain the mapping.\n\t\t\treturn clonedInner;\n\t\t} );\n\t}\n\n\treturn clonedBlock;\n}\n\n/**\n * Restores external (original) client IDs on blocks before passing them\n * to onChange/onInput callbacks.\n *\n * @param {Object[]} blocks The blocks with internal client IDs.\n * @param {Object} mapping The mapping object with internalToExternal Map.\n * @return {Object[]} Blocks with external client IDs restored.\n */\nfunction restoreExternalIds( blocks, mapping ) {\n\treturn blocks.map( ( block ) => {\n\t\tconst externalId = mapping.internalToExternal.get( block.clientId );\n\t\treturn {\n\t\t\t...block,\n\t\t\t// Use external ID if available, otherwise keep internal ID (for new blocks)\n\t\t\tclientId: externalId ?? block.clientId,\n\t\t\tinnerBlocks: restoreExternalIds( block.innerBlocks, mapping ),\n\t\t};\n\t} );\n}\n\n/**\n * Restores external client IDs in selection state.\n *\n * @param {Object} selectionState The selection state with internal client IDs.\n * @param {Object} mapping The mapping object with internalToExternal Map.\n * @return {Object} Selection state with external client IDs.\n */\nfunction restoreSelectionIds( selectionState, mapping ) {\n\tconst { selectionStart, selectionEnd, initialPosition } = selectionState;\n\n\tconst restoreClientId = ( sel ) => {\n\t\tif ( ! sel?.clientId ) {\n\t\t\treturn sel;\n\t\t}\n\t\tconst externalId = mapping.internalToExternal.get( sel.clientId );\n\t\treturn {\n\t\t\t...sel,\n\t\t\tclientId: externalId ?? sel.clientId,\n\t\t};\n\t};\n\n\treturn {\n\t\tselectionStart: restoreClientId( selectionStart ),\n\t\tselectionEnd: restoreClientId( selectionEnd ),\n\t\tinitialPosition,\n\t};\n}\n\n/**\n * A function to call when the block value has been updated in the block-editor\n * store.\n *\n * @callback onBlockUpdate\n * @param {Object[]} blocks The updated blocks.\n * @param {Object} options The updated block options, such as selectionStart\n * and selectionEnd.\n */\n\n/**\n * useBlockSync is a side effect which handles bidirectional sync between the\n * block-editor store and a controlling data source which provides blocks. This\n * is most commonly used by the BlockEditorProvider to synchronize the contents\n * of the block-editor store with the root entity, like a post.\n *\n * Another example would be the template part block, which provides blocks from\n * a separate entity data source than a root entity. This hook syncs edits to\n * the template part in the block editor back to the entity and vice-versa.\n *\n * Here are some of its basic functions:\n * - Initializes the block-editor store for the given clientID to the blocks\n * given via props.\n * - Adds incoming changes (like undo) to the block-editor store.\n * - Adds outgoing changes (like editing content) to the controlling entity,\n * determining if a change should be considered persistent or not.\n * - Handles edge cases and race conditions which occur in those operations.\n * - Ignores changes which happen to other entities (like nested inner block\n * controllers.\n * - Passes selection state from the block-editor store to the controlling entity.\n *\n * @param {Object} props Props for the block sync hook\n * @param {string} props.clientId The client ID of the inner block controller.\n * If none is passed, then it is assumed to be a\n * root controller rather than an inner block\n * controller.\n * @param {Object[]} props.value The control value for the blocks. This value\n * is used to initialize the block-editor store\n * and for resetting the blocks to incoming\n * changes like undo.\n * @param {onBlockUpdate} props.onChange Function to call when a persistent\n * change has been made in the block-editor blocks\n * for the given clientId. For example, after\n * this function is called, an entity is marked\n * dirty because it has changes to save.\n * @param {onBlockUpdate} props.onInput Function to call when a non-persistent\n * change has been made in the block-editor blocks\n * for the given clientId. When this is called,\n * controlling sources do not become dirty.\n */\nexport default function useBlockSync( {\n\tclientId = null,\n\tvalue: controlledBlocks,\n\tonChange = noop,\n\tonInput = noop,\n} ) {\n\tconst registry = useRegistry();\n\tconst { getSelection, onChangeSelection } = useContext( SelectionContext );\n\n\tconst {\n\t\tresetBlocks,\n\t\tresetSelection,\n\t\treplaceInnerBlocks,\n\t\tsetHasControlledInnerBlocks,\n\t\t__unstableMarkNextChangeAsNotPersistent,\n\t} = registry.dispatch( blockEditorStore );\n\tconst { getBlockName, getBlocks, getSelectionStart, getSelectionEnd } =\n\t\tregistry.select( blockEditorStore );\n\n\tconst pendingChangesRef = useRef( { incoming: null, outgoing: [] } );\n\tconst subscribedRef = useRef( false );\n\n\t// Mapping between external (original) and internal (cloned) client IDs.\n\t// This allows stable external IDs while using unique internal IDs.\n\tconst idMappingRef = useRef( {\n\t\texternalToInternal: new Map(),\n\t\tinternalToExternal: new Map(),\n\t} );\n\n\t// Tracks which context selection has already been applied, to avoid\n\t// duplicate restoration.\n\tconst appliedSelectionRef = useRef( null );\n\t// Flag to prevent the subscription from re-reporting a selection\n\t// change that was just restored from context (which would loop).\n\tconst isRestoringSelectionRef = useRef( false );\n\n\t// Restores selection from the SelectionContext using the current\n\t// idMapping. Called after blocks are (re-)cloned so that the\n\t// mapping is guaranteed to be fresh.\n\tconst restoreSelection = () => {\n\t\tconst selection = getSelection();\n\t\tif (\n\t\t\t! selection?.selectionStart?.clientId ||\n\t\t\tselection === appliedSelectionRef.current\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst startClientId = selection.selectionStart.clientId;\n\n\t\t// Check if this selection belongs to this controller.\n\t\t// Inner block controllers (clientId is set) own the block if\n\t\t// the external ID appears in their clone mapping.\n\t\t// The root controller (no clientId) owns it if the block\n\t\t// exists directly in the store.\n\t\tconst isOurs = clientId\n\t\t\t? idMappingRef.current.externalToInternal.has( startClientId )\n\t\t\t: !! getBlockName( startClientId );\n\n\t\tif ( isOurs ) {\n\t\t\tappliedSelectionRef.current = selection;\n\t\t\t// Inner block controllers need to convert external\u2192internal\n\t\t\t// IDs via the clone mapping; the root controller uses\n\t\t\t// external IDs directly (no mapping needed).\n\t\t\tconst convert = ( sel ) => {\n\t\t\t\tif ( ! sel?.clientId || ! clientId ) {\n\t\t\t\t\treturn sel;\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\t...sel,\n\t\t\t\t\tclientId:\n\t\t\t\t\t\tidMappingRef.current.externalToInternal.get(\n\t\t\t\t\t\t\tsel.clientId\n\t\t\t\t\t\t) ?? sel.clientId,\n\t\t\t\t};\n\t\t\t};\n\t\t\t// Flag prevents the subscription from re-reporting this\n\t\t\t// selection change back to the entity (which would cause\n\t\t\t// an infinite update loop).\n\t\t\tisRestoringSelectionRef.current = true;\n\t\t\tresetSelection(\n\t\t\t\tconvert( selection.selectionStart ),\n\t\t\t\tconvert( selection.selectionEnd ),\n\t\t\t\tselection.initialPosition\n\t\t\t);\n\t\t\tisRestoringSelectionRef.current = false;\n\t\t}\n\t};\n\n\tconst setControlledBlocks = () => {\n\t\tif ( ! controlledBlocks ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// We don't need to persist this change because we only replace\n\t\t// controlled inner blocks when the change was caused by an entity,\n\t\t// and so it would already be persisted.\n\t\tif ( clientId ) {\n\t\t\t// Batch so that the controlled flag and block replacement\n\t\t\t// are applied atomically \u2014 subscribers see a consistent state.\n\t\t\tregistry.batch( () => {\n\t\t\t\t// Clear previous mappings and build new ones during cloning.\n\t\t\t\t// This ensures the mapping stays in sync with the current blocks.\n\t\t\t\tidMappingRef.current.externalToInternal.clear();\n\t\t\t\tidMappingRef.current.internalToExternal.clear();\n\n\t\t\t\tconst storeBlocks = controlledBlocks.map( ( block ) =>\n\t\t\t\t\tcloneBlockWithMapping( block, idMappingRef.current )\n\t\t\t\t);\n\n\t\t\t\tsetHasControlledInnerBlocks( clientId, true );\n\n\t\t\t\tif ( subscribedRef.current ) {\n\t\t\t\t\tpendingChangesRef.current.incoming = storeBlocks;\n\t\t\t\t}\n\t\t\t\t__unstableMarkNextChangeAsNotPersistent();\n\t\t\t\treplaceInnerBlocks( clientId, storeBlocks );\n\n\t\t\t\t// Invalidate the applied-selection ref so that\n\t\t\t\t// restoreSelection() at the end of the\n\t\t\t\t// controlledBlocks effect re-applies with the\n\t\t\t\t// freshly-built mapping (new internal IDs).\n\t\t\t\tappliedSelectionRef.current = null;\n\t\t\t} );\n\t\t} else {\n\t\t\tif ( subscribedRef.current ) {\n\t\t\t\tpendingChangesRef.current.incoming = controlledBlocks;\n\t\t\t}\n\t\t\t__unstableMarkNextChangeAsNotPersistent();\n\t\t\tresetBlocks( controlledBlocks );\n\t\t}\n\t};\n\n\t// Clean up the changes made by setControlledBlocks() when the component\n\t// containing useBlockSync() unmounts.\n\tconst unsetControlledBlocks = () => {\n\t\t__unstableMarkNextChangeAsNotPersistent();\n\t\tif ( clientId ) {\n\t\t\tsetHasControlledInnerBlocks( clientId, false );\n\t\t\t__unstableMarkNextChangeAsNotPersistent();\n\t\t\treplaceInnerBlocks( clientId, [] );\n\t\t} else {\n\t\t\tresetBlocks( [] );\n\t\t}\n\t};\n\n\t// Add a subscription to the block-editor registry to detect when changes\n\t// have been made. This lets us inform the data source of changes. This\n\t// is an effect so that the subscriber can run synchronously without\n\t// waiting for React renders for changes.\n\tconst onInputRef = useRef( onInput );\n\tconst onChangeRef = useRef( onChange );\n\tuseEffect( () => {\n\t\tonInputRef.current = onInput;\n\t\tonChangeRef.current = onChange;\n\t}, [ onInput, onChange ] );\n\n\t// Determine if blocks need to be reset when they change.\n\t// Also restores selection from context after blocks are set.\n\tuseEffect( () => {\n\t\tconst isOutgoing =\n\t\t\tpendingChangesRef.current.outgoing.includes( controlledBlocks );\n\t\tconst storeMatch = getBlocks( clientId ) === controlledBlocks;\n\n\t\tif ( isOutgoing ) {\n\t\t\t// Skip block reset if the value matches expected outbound sync\n\t\t\t// triggered by this component by a preceding change detection.\n\t\t\t// Only skip if the value matches expectation, since a reset should\n\t\t\t// still occur if the value is modified (not equal by reference),\n\t\t\t// to allow that the consumer may apply modifications to reflect\n\t\t\t// back on the editor.\n\t\t\tif (\n\t\t\t\tpendingChangesRef.current.outgoing[\n\t\t\t\t\tpendingChangesRef.current.outgoing.length - 1\n\t\t\t\t] === controlledBlocks\n\t\t\t) {\n\t\t\t\tpendingChangesRef.current.outgoing = [];\n\t\t\t}\n\t\t} else if ( ! storeMatch ) {\n\t\t\t// Reset changing value in all other cases than the sync described\n\t\t\t// above. Since this can be reached in an update following an out-\n\t\t\t// bound sync, unset the outbound value to avoid considering it in\n\t\t\t// subsequent renders.\n\t\t\tpendingChangesRef.current.outgoing = [];\n\t\t\tsetControlledBlocks();\n\n\t\t\t// Restore selection from context if it targets our scope.\n\t\t\t// Only done when blocks were reset from an external source\n\t\t\t// (undo/redo, entity navigation) \u2014 NOT for outgoing changes,\n\t\t\t// because dispatching resetSelection between keystrokes breaks\n\t\t\t// the isUpdatingSameBlockAttribute chain and creates per-\n\t\t\t// character undo levels.\n\t\t\trestoreSelection();\n\t\t}\n\t}, [ controlledBlocks, clientId ] );\n\n\tuseEffect( () => {\n\t\tconst {\n\t\t\tgetSelectedBlocksInitialCaretPosition,\n\t\t\tisLastBlockChangePersistent,\n\t\t\t__unstableIsLastBlockChangeIgnored,\n\t\t\tareInnerBlocksControlled,\n\t\t\tgetBlockParents,\n\t\t} = registry.select( blockEditorStore );\n\n\t\tlet blocks = getBlocks( clientId );\n\t\tlet isPersistent = isLastBlockChangePersistent();\n\t\tlet previousAreBlocksDifferent = false;\n\t\tlet prevSelectionStart = getSelectionStart();\n\t\tlet prevSelectionEnd = getSelectionEnd();\n\n\t\tsubscribedRef.current = true;\n\t\tconst unsubscribe = registry.subscribe( () => {\n\t\t\t// Sometimes, when changing block lists, lingering subscriptions\n\t\t\t// might trigger before they are cleaned up. If the block for which\n\t\t\t// the subscription runs is no longer in the store, this would clear\n\t\t\t// its parent entity's block list. To avoid this, we bail out if\n\t\t\t// the subscription is triggering for a block (`clientId !== null`)\n\t\t\t// and its block name can't be found because it's not on the list.\n\t\t\t// (`getBlockName( clientId ) === null`).\n\t\t\tif ( clientId !== null && getBlockName( clientId ) === null ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst newIsPersistent = isLastBlockChangePersistent();\n\t\t\tconst newBlocks = getBlocks( clientId );\n\t\t\tconst areBlocksDifferent = newBlocks !== blocks;\n\t\t\tblocks = newBlocks;\n\t\t\tif (\n\t\t\t\tareBlocksDifferent &&\n\t\t\t\t( pendingChangesRef.current.incoming ||\n\t\t\t\t\t__unstableIsLastBlockChangeIgnored() )\n\t\t\t) {\n\t\t\t\tpendingChangesRef.current.incoming = null;\n\t\t\t\tisPersistent = newIsPersistent;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Since we often dispatch an action to mark the previous action as\n\t\t\t// persistent, we need to make sure that the blocks changed on the\n\t\t\t// previous action before committing the change.\n\t\t\tconst didPersistenceChange =\n\t\t\t\tpreviousAreBlocksDifferent &&\n\t\t\t\t! areBlocksDifferent &&\n\t\t\t\tnewIsPersistent &&\n\t\t\t\t! isPersistent;\n\n\t\t\tconst blocksChanged = areBlocksDifferent || didPersistenceChange;\n\n\t\t\t// Check if selection changed.\n\t\t\tconst newSelectionStart = getSelectionStart();\n\t\t\tconst newSelectionEnd = getSelectionEnd();\n\t\t\tconst selectionChanged =\n\t\t\t\tnewSelectionStart !== prevSelectionStart ||\n\t\t\t\tnewSelectionEnd !== prevSelectionEnd;\n\n\t\t\tif ( selectionChanged ) {\n\t\t\t\tprevSelectionStart = newSelectionStart;\n\t\t\t\tprevSelectionEnd = newSelectionEnd;\n\t\t\t}\n\n\t\t\tif ( blocksChanged || selectionChanged ) {\n\t\t\t\t// Batch block and selection updates so the entity\n\t\t\t\t// receives both changes atomically.\n\t\t\t\tregistry.batch( () => {\n\t\t\t\t\tif ( blocksChanged ) {\n\t\t\t\t\t\tisPersistent = newIsPersistent;\n\n\t\t\t\t\t\t// For inner block controllers (clientId is set), restore external IDs\n\t\t\t\t\t\t// before passing blocks to the parent.\n\t\t\t\t\t\tconst blocksForParent = clientId\n\t\t\t\t\t\t\t? restoreExternalIds( blocks, idMappingRef.current )\n\t\t\t\t\t\t\t: blocks;\n\n\t\t\t\t\t\t// Build selection state for the undo level.\n\t\t\t\t\t\tconst selectionInfo = {\n\t\t\t\t\t\t\tselectionStart: newSelectionStart,\n\t\t\t\t\t\t\tselectionEnd: newSelectionEnd,\n\t\t\t\t\t\t\tinitialPosition:\n\t\t\t\t\t\t\t\tgetSelectedBlocksInitialCaretPosition(),\n\t\t\t\t\t\t};\n\t\t\t\t\t\t// Restore external IDs in selection for inner block controllers.\n\t\t\t\t\t\tconst selectionForParent = clientId\n\t\t\t\t\t\t\t? restoreSelectionIds(\n\t\t\t\t\t\t\t\t\tselectionInfo,\n\t\t\t\t\t\t\t\t\tidMappingRef.current\n\t\t\t\t\t\t\t )\n\t\t\t\t\t\t\t: selectionInfo;\n\n\t\t\t\t\t\tpendingChangesRef.current.outgoing.push(\n\t\t\t\t\t\t\tblocksForParent\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tconst updateParent = isPersistent\n\t\t\t\t\t\t\t? onChangeRef.current\n\t\t\t\t\t\t\t: onInputRef.current;\n\t\t\t\t\t\tupdateParent( blocksForParent, {\n\t\t\t\t\t\t\tselection: selectionForParent,\n\t\t\t\t\t\t} );\n\t\t\t\t\t}\n\n\t\t\t\t\tif (\n\t\t\t\t\t\tselectionChanged &&\n\t\t\t\t\t\t! blocksChanged &&\n\t\t\t\t\t\tnewSelectionStart?.clientId &&\n\t\t\t\t\t\t! isRestoringSelectionRef.current\n\t\t\t\t\t) {\n\t\t\t\t\t\t// Report selection via onChangeSelection.\n\t\t\t\t\t\t// Each useBlockSync only reports if the selected block\n\t\t\t\t\t\t// is within its own scope.\n\t\t\t\t\t\t// Inner block controllers own the block if the internal\n\t\t\t\t\t\t// ID appears in their clone mapping.\n\t\t\t\t\t\t// The root controller owns it if the block is not inside\n\t\t\t\t\t\t// any controlled inner block.\n\t\t\t\t\t\tconst isOurs = clientId\n\t\t\t\t\t\t\t? idMappingRef.current.internalToExternal.has(\n\t\t\t\t\t\t\t\t\tnewSelectionStart.clientId\n\t\t\t\t\t\t\t )\n\t\t\t\t\t\t\t: ! getBlockParents(\n\t\t\t\t\t\t\t\t\tnewSelectionStart.clientId\n\t\t\t\t\t\t\t ).some( ( parentId ) =>\n\t\t\t\t\t\t\t\t\tareInnerBlocksControlled( parentId )\n\t\t\t\t\t\t\t );\n\n\t\t\t\t\t\tif ( isOurs ) {\n\t\t\t\t\t\t\tconst selectionInfo = {\n\t\t\t\t\t\t\t\tselectionStart: newSelectionStart,\n\t\t\t\t\t\t\t\tselectionEnd: newSelectionEnd,\n\t\t\t\t\t\t\t\tinitialPosition:\n\t\t\t\t\t\t\t\t\tgetSelectedBlocksInitialCaretPosition(),\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tonChangeSelection(\n\t\t\t\t\t\t\t\tclientId\n\t\t\t\t\t\t\t\t\t? restoreSelectionIds(\n\t\t\t\t\t\t\t\t\t\t\tselectionInfo,\n\t\t\t\t\t\t\t\t\t\t\tidMappingRef.current\n\t\t\t\t\t\t\t\t\t )\n\t\t\t\t\t\t\t\t\t: selectionInfo\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t\t}\n\t\t\tpreviousAreBlocksDifferent = areBlocksDifferent;\n\t\t}, blockEditorStore );\n\n\t\treturn () => {\n\t\t\tsubscribedRef.current = false;\n\t\t\tunsubscribe();\n\t\t};\n\t}, [ registry, clientId ] );\n\n\tuseEffect( () => {\n\t\treturn () => {\n\t\t\tunsetControlledBlocks();\n\t\t};\n\t}, [] );\n}\n"],
"mappings": "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",
"names": []
}