register(); // Enqueue the block's assets. add_action( 'enqueue_block_assets', [ $this, 'enqueueBlockAssets' ] ); } /** * Register the block. * * @since 4.2.3 * * @return void */ public function register() { aioseo()->blocks->registerBlock( 'table-of-contents', [ 'render_callback' => [ $this, 'render' ] ] ); } /** * Enqueues the block's assets. * * @since 4.9.0 * * @return void */ public function enqueueBlockAssets() { // Only enqueue if the block is present in the content. if ( ! is_singular() ) { return; } $post = get_post(); if ( ! $post || ! has_block( 'aioseo/table-of-contents', $post ) ) { return; } aioseo()->core->assets->load( 'src/vue/standalone/blocks/table-of-contents/frontend.js' ); } /** * Get the default attributes for the block. * * @since 4.9.0 * * @return array */ private function getDefaultAttributes() { return [ 'listStyle' => 'ul', 'collapsibleType' => 'off', 'collapsed' => false, 'collapsedTitle' => __( 'Show Table of Contents', 'all-in-one-seo-pack' ), 'expandedTitle' => __( 'Hide Table of Contents', 'all-in-one-seo-pack' ), 'mode' => null, 'headings' => [], 'reOrdered' => false ]; } /** * Get the nested headings for the block. * * @since 4.9.0 * * @param array $headings The headings to get. * @param string $listStyle The list style to use. * * @return string */ private function getNestedHeadings( $headings, $listStyle ) { $htmlString = '<' . $listStyle . '>'; foreach ( $headings as $heading ) { if ( $heading['hidden'] ) { continue; } $listItem = '
  • '; $content = empty( $heading['editedContent'] ) ? $heading['content'] : $heading['editedContent']; $listItem .= '' . esc_html( $content ) . ''; if ( ! empty( $heading['headings'] ) ) { $listItem .= $this->getNestedHeadings( $heading['headings'], $listStyle ); } $listItem .= '
  • '; $htmlString .= $listItem; } $htmlString .= ''; return $htmlString; } /** * Get the collapsed icon for the block. * * @since 4.9.0 * * @return string */ private function getCollapsedIcon() { return ' '; } /** * Get the expanded icon for the block. * * @since 4.9.0 * * @return string */ private function getExpandedIcon() { return ' '; } /** * Get the HTML for the block. * * @since 4.9.0 * * @param array $attributes The attributes for the block. * * @return string */ private function getHtml( $attributes ) { $htmlString = $this->getNestedHeadings( $attributes['headings'], $attributes['listStyle'] ); $class1 = 'open' === $attributes['collapsibleType'] ? 'aioseo-toc-collapsed' : ''; $class2 = 'closed' === $attributes['collapsibleType'] ? 'aioseo-toc-collapsed' : ''; $class3 = 'closed' === $attributes['collapsibleType'] ? 'aioseo-toc-collapsed' : ''; $blockCustomClass = isset( $attributes['className'] ) ? $attributes['className'] : ''; $fullHtmlString = '
    ' . $this->getCollapsedIcon() . '
    ' . esc_html( $attributes['collapsedTitle'] ) . '
    ' . $this->getExpandedIcon() . '
    ' . esc_html( $attributes['expandedTitle'] ) . '
    ' . $htmlString . '
    '; $htmlString = '
    ' . $htmlString . '
    '; $fullHtmlString = 'off' === $attributes['collapsibleType'] ? $htmlString : $fullHtmlString; return $fullHtmlString; } /** * Render the block. * * @since 4.9.0 * * @param array $attributes The attributes for the block. * * @return string */ public function render( $attributes ) { if ( empty( $attributes['headings'] ) ) { return null; } return $this->getHtml( array_merge( $this->getDefaultAttributes(), $attributes ) ); } }