UNPKG

@wordpress/core-data

Version:
131 lines (127 loc) 4.06 kB
/** * WordPress dependencies */ import { useCallback, useMemo } from '@wordpress/element'; import { useDispatch, useSelect } from '@wordpress/data'; import { parse, __unstableSerializeAndClean } from '@wordpress/blocks'; /** * Internal dependencies */ import { STORE_NAME } from '../name'; import useEntityId from './use-entity-id'; import { updateFootnotesFromMeta } from '../footnotes'; const EMPTY_ARRAY = []; const parsedBlocksCache = new WeakMap(); /** * Hook that returns block content getters and setters for * the nearest provided entity of the specified type. * * The return value has the shape `[ blocks, onInput, onChange ]`. * `onInput` is for block changes that don't create undo levels * or dirty the post, non-persistent changes, and `onChange` is for * persistent changes. They map directly to the props of a * `BlockEditorProvider` and are intended to be used with it, * or similar components or hooks. * * @param {string} kind The entity kind. * @param {string} name The entity name. * @param {Object} options * @param {string} [options.id] An entity ID to use instead of the context-provided one. * * @return {[unknown[], Function, Function]} The block array and setters. */ export default function useEntityBlockEditor(kind, name, { id: _id } = {}) { const providerId = useEntityId(kind, name); const id = _id !== null && _id !== void 0 ? _id : providerId; const { getEntityRecord, getEntityRecordEdits } = useSelect(STORE_NAME); const { content, editedBlocks, meta } = useSelect(select => { if (!id) { return {}; } const { getEditedEntityRecord } = select(STORE_NAME); const editedRecord = getEditedEntityRecord(kind, name, id); return { editedBlocks: editedRecord.blocks, content: editedRecord.content, meta: editedRecord.meta }; }, [kind, name, id]); const { __unstableCreateUndoLevel, editEntityRecord } = useDispatch(STORE_NAME); const blocks = useMemo(() => { if (!id) { return undefined; } if (editedBlocks) { return editedBlocks; } if (!content || typeof content !== 'string') { return EMPTY_ARRAY; } // If there's an edit, cache the parsed blocks by the edit. // If not, cache by the original entity record. const edits = getEntityRecordEdits(kind, name, id); const isUnedited = !edits || !Object.keys(edits).length; const cackeKey = isUnedited ? getEntityRecord(kind, name, id) : edits; let _blocks = parsedBlocksCache.get(cackeKey); if (!_blocks) { _blocks = parse(content); parsedBlocksCache.set(cackeKey, _blocks); } return _blocks; }, [kind, name, id, editedBlocks, content, getEntityRecord, getEntityRecordEdits]); const onChange = useCallback((newBlocks, options) => { const noChange = blocks === newBlocks; if (noChange) { return __unstableCreateUndoLevel(kind, name, id); } const { selection, ...rest } = options; // We create a new function here on every persistent edit // to make sure the edit makes the post dirty and creates // a new undo level. const edits = { selection, content: ({ blocks: blocksForSerialization = [] }) => __unstableSerializeAndClean(blocksForSerialization), ...updateFootnotesFromMeta(newBlocks, meta) }; editEntityRecord(kind, name, id, edits, { isCached: false, ...rest }); }, [kind, name, id, blocks, meta, __unstableCreateUndoLevel, editEntityRecord]); const onInput = useCallback((newBlocks, options) => { const { selection, ...rest } = options; const footnotesChanges = updateFootnotesFromMeta(newBlocks, meta); const edits = { selection, ...footnotesChanges }; editEntityRecord(kind, name, id, edits, { isCached: true, ...rest }); }, [kind, name, id, meta, editEntityRecord]); return [blocks, onInput, onChange]; } //# sourceMappingURL=use-entity-block-editor.js.map