@wordpress/block-library
Version:
Block library for the WordPress editor.
8 lines (7 loc) • 8.29 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../../src/table-of-contents/hooks.js"],
"sourcesContent": ["/**\n * External dependencies\n */\nimport fastDeepEqual from 'fast-deep-equal/es6/index.js';\n\n/**\n * WordPress dependencies\n */\nimport { useRegistry } from '@wordpress/data';\nimport { __unstableStripHTML as stripHTML } from '@wordpress/dom';\nimport { useEffect } from '@wordpress/element';\nimport { addQueryArgs, removeQueryArgs } from '@wordpress/url';\nimport { store as blockEditorStore } from '@wordpress/block-editor';\n\nfunction getLatestHeadings( select, clientId ) {\n\tconst {\n\t\tgetBlockAttributes,\n\t\tgetBlockName,\n\t\tgetBlocksByName,\n\t\tgetClientIdsOfDescendants,\n\t} = select( blockEditorStore );\n\n\t// FIXME: @wordpress/block-library should not depend on @wordpress/editor.\n\t// Blocks can be loaded into a *non-post* block editor, so to avoid\n\t// declaring @wordpress/editor as a dependency, we must access its\n\t// store by string. When the store is not available, editorSelectors\n\t// will be null, and the block's saved markup will lack permalinks.\n\tconst permalink = select( 'core/editor' ).getPermalink() ?? null;\n\n\tconst isPaginated = getBlocksByName( 'core/nextpage' ).length !== 0;\n\tconst { onlyIncludeCurrentPage, maxLevel } =\n\t\tgetBlockAttributes( clientId ) ?? {};\n\n\t// Get post-content block client ID.\n\tconst [ postContentClientId = '' ] = getBlocksByName( 'core/post-content' );\n\n\t// Get the client ids of all blocks in the editor.\n\tconst allBlockClientIds = getClientIdsOfDescendants( postContentClientId );\n\n\t// If onlyIncludeCurrentPage is true, calculate the page (of a paginated post) this block is part of, so we know which headings to include; otherwise, skip the calculation.\n\tlet tocPage = 1;\n\n\tif ( isPaginated && onlyIncludeCurrentPage ) {\n\t\t// We can't use getBlockIndex because it only returns the index\n\t\t// relative to sibling blocks.\n\t\tconst tocIndex = allBlockClientIds.indexOf( clientId );\n\n\t\tfor ( const [\n\t\t\tblockIndex,\n\t\t\tblockClientId,\n\t\t] of allBlockClientIds.entries() ) {\n\t\t\t// If we've reached blocks after the Table of Contents, we've\n\t\t\t// finished calculating which page the block is on.\n\t\t\tif ( blockIndex >= tocIndex ) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif ( getBlockName( blockClientId ) === 'core/nextpage' ) {\n\t\t\t\ttocPage++;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst latestHeadings = [];\n\n\t/** The page (of a paginated post) a heading will be part of. */\n\tlet headingPage = 1;\n\tlet headingPageLink = null;\n\n\t// If the core/editor store is available, we can add permalinks to the\n\t// generated table of contents.\n\tif ( typeof permalink === 'string' ) {\n\t\theadingPageLink = isPaginated\n\t\t\t? addQueryArgs( permalink, { page: headingPage } )\n\t\t\t: permalink;\n\t}\n\n\tfor ( const blockClientId of allBlockClientIds ) {\n\t\tconst blockName = getBlockName( blockClientId );\n\t\tif ( blockName === 'core/nextpage' ) {\n\t\t\theadingPage++;\n\n\t\t\t// If we're only including headings from the current page (of\n\t\t\t// a paginated post), then exit the loop if we've reached the\n\t\t\t// pages after the one with the Table of Contents block.\n\t\t\tif ( onlyIncludeCurrentPage && headingPage > tocPage ) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif ( typeof permalink === 'string' ) {\n\t\t\t\theadingPageLink = addQueryArgs(\n\t\t\t\t\tremoveQueryArgs( permalink, [ 'page' ] ),\n\t\t\t\t\t{ page: headingPage }\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\t// If we're including all headings or we've reached headings on\n\t\t// the same page as the Table of Contents block, add them to the\n\t\t// list.\n\t\telse if ( ! onlyIncludeCurrentPage || headingPage === tocPage ) {\n\t\t\tif ( blockName === 'core/heading' ) {\n\t\t\t\tconst headingAttributes = getBlockAttributes( blockClientId );\n\n\t\t\t\t// Skip headings that are deeper than maxLevel\n\t\t\t\tif ( maxLevel && headingAttributes.level > maxLevel ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst canBeLinked =\n\t\t\t\t\ttypeof headingPageLink === 'string' &&\n\t\t\t\t\ttypeof headingAttributes.anchor === 'string' &&\n\t\t\t\t\theadingAttributes.anchor !== '';\n\n\t\t\t\tlatestHeadings.push( {\n\t\t\t\t\t// Convert line breaks to spaces, and get rid of HTML tags in the headings.\n\t\t\t\t\tcontent: stripHTML(\n\t\t\t\t\t\theadingAttributes.content.replace(\n\t\t\t\t\t\t\t/(<br *\\/?>)+/g,\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\tlevel: headingAttributes.level,\n\t\t\t\t\tlink: canBeLinked\n\t\t\t\t\t\t? `${ headingPageLink }#${ headingAttributes.anchor }`\n\t\t\t\t\t\t: null,\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\t}\n\n\treturn latestHeadings;\n}\n\nfunction observeCallback( select, dispatch, clientId ) {\n\tconst { getBlockAttributes } = select( blockEditorStore );\n\tconst { updateBlockAttributes, __unstableMarkNextChangeAsNotPersistent } =\n\t\tdispatch( blockEditorStore );\n\n\t/**\n\t * If the block no longer exists in the store, skip the update.\n\t * The \"undo\" action recreates the block and provides a new `clientId`.\n\t * The hook still might be observing the changes while the old block unmounts.\n\t */\n\tconst attributes = getBlockAttributes( clientId );\n\tif ( attributes === null ) {\n\t\treturn;\n\t}\n\n\tconst headings = getLatestHeadings( select, clientId );\n\tif ( ! fastDeepEqual( headings, attributes.headings ) ) {\n\t\t// Executing the update in a microtask ensures that the non-persistent marker doesn't affect an attribute triggering the change.\n\t\twindow.queueMicrotask( () => {\n\t\t\t__unstableMarkNextChangeAsNotPersistent();\n\t\t\tupdateBlockAttributes( clientId, { headings } );\n\t\t} );\n\t}\n}\n\nexport function useObserveHeadings( clientId ) {\n\tconst registry = useRegistry();\n\tuseEffect( () => {\n\t\t// Todo: Limit subscription to block editor store when data no longer depends on `getPermalink`.\n\t\t// See: https://github.com/WordPress/gutenberg/pull/45513\n\t\treturn registry.subscribe( () =>\n\t\t\tobserveCallback( registry.select, registry.dispatch, clientId )\n\t\t);\n\t}, [ registry, clientId ] );\n}\n"],
"mappings": "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",
"names": ["blockEditorStore", "stripHTML", "fastDeepEqual"]
}