(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 = (
{headings.map( ( heading, index ) => {
return (
-
{heading.content}
)
} )}
{ anchorColor &&
}
);
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 );