@wordpress/block-editor
Version:
158 lines (157 loc) • 5.27 kB
JavaScript
// packages/block-editor/src/components/provider/use-block-sync.js
import { useEffect, useRef } from "@wordpress/element";
import { useRegistry, useSelect } from "@wordpress/data";
import { cloneBlock } from "@wordpress/blocks";
import { store as blockEditorStore } from "../../store";
var noop = () => {
};
function useBlockSync({
clientId = null,
value: controlledBlocks,
selection: controlledSelection,
onChange = noop,
onInput = noop
}) {
const registry = useRegistry();
const {
resetBlocks,
resetSelection,
replaceInnerBlocks,
setHasControlledInnerBlocks,
__unstableMarkNextChangeAsNotPersistent
} = registry.dispatch(blockEditorStore);
const { getBlockName, getBlocks, getSelectionStart, getSelectionEnd } = registry.select(blockEditorStore);
const isControlled = useSelect(
(select) => {
return !clientId || select(blockEditorStore).areInnerBlocksControlled(clientId);
},
[clientId]
);
const pendingChangesRef = useRef({ incoming: null, outgoing: [] });
const subscribedRef = useRef(false);
const setControlledBlocks = () => {
if (!controlledBlocks) {
return;
}
__unstableMarkNextChangeAsNotPersistent();
if (clientId) {
registry.batch(() => {
setHasControlledInnerBlocks(clientId, true);
const storeBlocks = controlledBlocks.map(
(block) => cloneBlock(block)
);
if (subscribedRef.current) {
pendingChangesRef.current.incoming = storeBlocks;
}
__unstableMarkNextChangeAsNotPersistent();
replaceInnerBlocks(clientId, storeBlocks);
});
} else {
if (subscribedRef.current) {
pendingChangesRef.current.incoming = controlledBlocks;
}
resetBlocks(controlledBlocks);
}
};
const unsetControlledBlocks = () => {
__unstableMarkNextChangeAsNotPersistent();
if (clientId) {
setHasControlledInnerBlocks(clientId, false);
__unstableMarkNextChangeAsNotPersistent();
replaceInnerBlocks(clientId, []);
} else {
resetBlocks([]);
}
};
const onInputRef = useRef(onInput);
const onChangeRef = useRef(onChange);
useEffect(() => {
onInputRef.current = onInput;
onChangeRef.current = onChange;
}, [onInput, onChange]);
useEffect(() => {
if (pendingChangesRef.current.outgoing.includes(controlledBlocks)) {
if (pendingChangesRef.current.outgoing[pendingChangesRef.current.outgoing.length - 1] === controlledBlocks) {
pendingChangesRef.current.outgoing = [];
}
} else if (getBlocks(clientId) !== controlledBlocks) {
pendingChangesRef.current.outgoing = [];
setControlledBlocks();
if (controlledSelection) {
resetSelection(
controlledSelection.selectionStart,
controlledSelection.selectionEnd,
controlledSelection.initialPosition
);
}
}
}, [controlledBlocks, clientId]);
const isMountedRef = useRef(false);
useEffect(() => {
if (!isMountedRef.current) {
isMountedRef.current = true;
return;
}
if (!isControlled) {
pendingChangesRef.current.outgoing = [];
setControlledBlocks();
}
}, [isControlled]);
useEffect(() => {
const {
getSelectedBlocksInitialCaretPosition,
isLastBlockChangePersistent,
__unstableIsLastBlockChangeIgnored,
areInnerBlocksControlled
} = registry.select(blockEditorStore);
let blocks = getBlocks(clientId);
let isPersistent = isLastBlockChangePersistent();
let previousAreBlocksDifferent = false;
subscribedRef.current = true;
const unsubscribe = registry.subscribe(() => {
if (clientId !== null && getBlockName(clientId) === null) {
return;
}
const isStillControlled = !clientId || areInnerBlocksControlled(clientId);
if (!isStillControlled) {
return;
}
const newIsPersistent = isLastBlockChangePersistent();
const newBlocks = getBlocks(clientId);
const areBlocksDifferent = newBlocks !== blocks;
blocks = newBlocks;
if (areBlocksDifferent && (pendingChangesRef.current.incoming || __unstableIsLastBlockChangeIgnored())) {
pendingChangesRef.current.incoming = null;
isPersistent = newIsPersistent;
return;
}
const didPersistenceChange = previousAreBlocksDifferent && !areBlocksDifferent && newIsPersistent && !isPersistent;
if (areBlocksDifferent || didPersistenceChange) {
isPersistent = newIsPersistent;
pendingChangesRef.current.outgoing.push(blocks);
const updateParent = isPersistent ? onChangeRef.current : onInputRef.current;
updateParent(blocks, {
selection: {
selectionStart: getSelectionStart(),
selectionEnd: getSelectionEnd(),
initialPosition: getSelectedBlocksInitialCaretPosition()
}
});
}
previousAreBlocksDifferent = areBlocksDifferent;
}, blockEditorStore);
return () => {
subscribedRef.current = false;
unsubscribe();
};
}, [registry, clientId]);
useEffect(() => {
return () => {
unsetControlledBlocks();
};
}, []);
}
export {
useBlockSync as default
};
//# sourceMappingURL=use-block-sync.js.map