(function ( wpI18n, wpBlocks, wpElement, wpEditor, wpComponents, wpData, wpHooks ) { const { __ } = wpI18n; const { Component, Fragment } = wpElement; const { registerBlockType, getBlockContent, createBlock } = wpBlocks; const { BlockControls, InspectorControls, InspectorAdvancedControls, PanelColorSettings, BlockAlignmentToolbar } = wpEditor; const { IconButton, Placeholder, Button, Toolbar, ToggleControl, TextControl, PanelBody } = wpComponents; const { select, dispatch } = wpData; const { addFilter } = wpHooks; const summaryBlockIcon = ( ); const summaryBlockTitle = __( 'Summary' ); // Add button to insert summary inside table of contents component ( function () { jQuery( window ).on( 'load', function () { if (typeof dispatch( 'core/editor' ) === 'undefined') { return false; } const $ = jQuery; const { insertBlock } = dispatch( 'core/editor' ); const summaryBlock = createBlock( 'advgb/summary' ); $( '#editor' ).find( '.table-of-contents' ).click( function () { const allBlocks = select( 'core/editor' ).getBlocks(); const summaryBlockExist = !!allBlocks.filter( ( block ) => ( block.name === 'advgb/summary' ) ).length; setTimeout( function () { const summaryButton = $( '' ); $( '#editor' ).find( '.table-of-contents__popover' ).find( '.document-outline' ) .append( summaryButton ); summaryButton.unbind( 'click' ).click( function () { insertBlock( summaryBlock, 0 ); $('.table-of-contents__popover').hide(); } ); if (summaryBlockExist) { summaryButton.prop( 'disabled', true ); } }, 100 ) } ) } ); } )(); // Add notice for user to refresh summary if manually change heading anchor addFilter( 'editor.BlockEdit', 'advgb/addHeadingNotice', function ( BlockEdit ) { return ( props ) => { const { isSelected, name: blockType, attributes } = props; return ( [ , isSelected && blockType === 'core/heading' && attributes.nodeName !== 'H1' &&

{__( 'After manually changing the anchor, remember to refresh summary block to make the links work!' )}

, ] ) } } ); class SummaryBlock extends Component { constructor() { super( ...arguments ); this.updateSummary = this.updateSummary.bind( this ); } componentWillMount() { const { attributes, setAttributes } = this.props; const currentBlockConfig = advgbDefaultConfig['advgb-summary']; // No override attributes of blocks inserted before if (attributes.changed !== true) { if (typeof currentBlockConfig === 'object' && currentBlockConfig !== null) { Object.keys(currentBlockConfig).map((attribute) => { if (typeof attributes[attribute] === 'boolean') { attributes[attribute] = !!currentBlockConfig[attribute]; } else { attributes[attribute] = currentBlockConfig[attribute]; } }); } // Finally set changed attribute to true, so we don't modify anything again setAttributes( { changed: true } ); } } componentDidMount() { this.updateSummary(); }; /** * Function to get heading blocks from columns blocks * * @param block array Columns block to get data * @param storeData array Data array to store heading blocks * * @returns array array Heading blocks from block given */ static getHeadingBlocksFromColumns( block, storeData ) { if ( block.name === 'core/columns' || block.name === 'core/column' ) { block.innerBlocks.map(function ( bl ) { SummaryBlock.getHeadingBlocksFromColumns( bl, storeData ); return bl; } ) } else if ( block.name === 'core/heading' ) { storeData.push( block ); } return storeData; } updateSummary() { let headingDatas = []; let headingBlocks = []; const allBlocks = select( 'core/editor' ).getBlocks(); const filteredBlocks = allBlocks.filter( ( block ) => ( block.name === 'core/heading' || block.name === 'core/columns' ) ); filteredBlocks.map(function ( block ) { if (block.name === 'core/columns') { SummaryBlock.getHeadingBlocksFromColumns( block, headingBlocks ); } else { headingBlocks.push( block ); } return block; }); headingBlocks.map( ( heading ) => { let thisHead = {}; thisHead[ 'level' ] = parseInt( heading.attributes.level ); // We only get heading from h2 if (thisHead[ 'level' ] > 1) { thisHead[ 'level' ] -= 1; thisHead[ 'content' ] = heading.attributes.content.length ? getBlockContent( heading ).replace( /<(?:.|\n)*?>/gm, '' ) : ''; thisHead[ 'clientId' ] = heading.clientId; if (heading.attributes.anchor) { thisHead[ 'anchor' ] = heading.attributes.anchor; } else { // Generate a random anchor for headings without it thisHead[ 'anchor' ] = 'advgb-toc-' + heading.clientId; heading.attributes.anchor = thisHead[ 'anchor' ]; } headingDatas.push( thisHead ); } return heading; } ); this.props.setAttributes( { headings: headingDatas } ); } render() { const { attributes, isSelected, setAttributes } = this.props; const { headings, loadMinimized, anchorColor, align, headerTitle } = attributes; // No heading blocks let summaryContent = ( ); // Having heading blocks if (headings.length > 0) { const { selectBlock } = dispatch( 'core/editor' ); summaryContent = ( ) } return ( {!!headings.length && ( setAttributes( { align: align } ) } /> ) } setAttributes( { loadMinimized: !loadMinimized, postTitle: select('core/editor').getEditedPostAttribute('title') } ) } /> {loadMinimized && setAttributes( { headerTitle: value } ) } /> } setAttributes( { anchorColor: value } ), }, ] } /> {summaryContent} {anchorColor && } ) } } registerBlockType( 'advgb/summary', { title: summaryBlockTitle, description: __( 'Show the table of content of current post/page.' ), icon: { src: summaryBlockIcon, foreground: typeof advgbBlocks !== 'undefined' ? advgbBlocks.color : undefined, }, category: 'advgb-category', keywords: [ __( 'summary' ), __( 'table of content' ), __( 'list' ) ], attributes: { headings: { type: 'array', default: [], }, loadMinimized: { type: 'boolean', default: false, }, anchorColor: { type: 'string', }, align: { type: 'string', default: 'none', }, postTitle: { type: 'string', }, headerTitle: { type: 'string', }, changed: { type: 'boolean', default: false, }, }, supports: { multiple: false, }, edit: SummaryBlock, save: ( { attributes } ) => { const { headings, loadMinimized, anchorColor, align = 'none', postTitle, headerTitle } = attributes; // No heading blocks if (headings.length < 1) { return null; } let blockStyle = undefined; if (loadMinimized) blockStyle = { display: 'none' }; const summary = ( ); if ( loadMinimized ) { return (
{ headerTitle || postTitle }
{summary}
); } return summary; }, getEditWrapperProps( attributes ) { const { align } = attributes; const props = { 'data-resized': true }; if ( 'left' === align || 'right' === align || 'center' === align ) { props[ 'data-align' ] = align; } return props; }, } ); })( wp.i18n, wp.blocks, wp.element, wp.editor, wp.components, wp.data, wp.hooks );