Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add navigation block #443

Merged
merged 6 commits into from
Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions mu-plugins/blocks/navigation/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
<?php
/**
* Update the Navigation block to work with a `menuSlug`
adamwoodnz marked this conversation as resolved.
Show resolved Hide resolved
*
* @package wporg
*/

namespace WordPressdotorg\MU_Plugins\Navigation_Block;

use WP_Block_List, WP_Block_Supports;

defined( 'WPINC' ) || die();

/**
* Actions and filters.
*/
add_action( 'init', __NAMESPACE__ . '\init' );
add_filter( 'block_core_navigation_render_inner_blocks', __NAMESPACE__ . '\update_navigation_items' );

/**
* Add the JS script to update the navigation block.
*
* The dependencies are autogenerated in block.json, and can be read with
* `wp_json_file_decode` & `register_block_script_handle.
*/
function init() {
$metadata_file = dirname( dirname( __DIR__ ) ) . '/blocks/navigation/build/block.json';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Path changed from main due to different file structure

$metadata = wp_json_file_decode( $metadata_file, array( 'associative' => true ) );
$metadata['file'] = $metadata_file;
$editor_script_handle = register_block_script_handle( $metadata, 'editorScript', 0 );
add_action(
'enqueue_block_assets',
function() use ( $editor_script_handle ) {
if ( is_admin() && wp_should_load_block_editor_scripts_and_styles() ) {
wp_localize_script(
Copy link
Contributor Author

@adamwoodnz adamwoodnz Sep 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pass menus to block editor frontend

$editor_script_handle,
'wporgLocalNavigationMenus',
apply_filters( 'wporg_block_navigation_menus', array() )
);
wp_enqueue_script( $editor_script_handle );
}
}
);

// Hide the menu selection when a dynamic menu is selected.
add_action(
'admin_print_styles',
function() {
global $hook_suffix;
if ( ! in_array( $hook_suffix, array( 'post.php', 'post-new.php' ) ) ) {
return;
}
echo '<style>.wporg-nav-hide-next-panel + .components-panel__body { display: none; }</style>';
}
);
}

/**
* Update the inner blocks (menu items) in a navigation block.
*
* @param WP_Block_List $inner_blocks
*/
function update_navigation_items( $inner_blocks ) {
// This contains the parent block, so that we can check that we're in the correct Navigation.
$block = WP_Block_Supports::$block_to_render;
if (
isset( $block['blockName'] ) &&
'core/navigation' === $block['blockName'] &&
isset( $block['attrs']['menuSlug'] ) &&
$block['attrs']['menuSlug']
) {
$menu_content = get_menu_content( $block['attrs']['menuSlug'] );
$parsed_blocks = parse_blocks( $menu_content );
$compacted_blocks = block_core_navigation_filter_out_empty_blocks( $parsed_blocks );
$inner_blocks = new WP_Block_List( $compacted_blocks, $block['attrs'] );
}
return $inner_blocks;
}

/**
* Get the navigation menu content as blocks.
*
* @param string $menu_slug
*
* @return string Menu items in block syntax.
*/
function get_menu_content( $menu_slug ) {
$menu_items = get_menu_items( $menu_slug );
if ( ! $menu_items ) {
return '';
}

$menu_content = '';
foreach ( $menu_items as $item ) {
$block_code = '<!-- wp:navigation-link {"label":"%1$s","url":"%2$s","kind":"custom"} /-->';

// If this is a relative link, convert it to absolute and try to find
// the corresponding ID, so that the `current` attributes are used.
if ( str_starts_with( $item['url'], '/' ) ) {
$page_obj = get_page_by_path( $item['url'] );
$item['url'] = home_url( $item['url'] );
if ( $page_obj ) {
// A page was found, so use the post-type link.
$block_code = '<!-- wp:navigation-link {"label":"%1$s","url":"%2$s","kind":"post-type","id":"%3$s"} /-->';
$item['id'] = $page_obj->ID;
}
}

$menu_content .= sprintf(
$block_code,
esc_html( $item['label'] ),
esc_url( $item['url'], ),
isset( $item['id'] ) ? intval( $item['id'] ) : ''
);
}

return $menu_content;
}

/**
* Get a list of menu items for a given menu slug.
*
* This is used to build up the navigation menu. Relative links should
* correspond to pages on the site, while absolute URLs can be used to
* navigate off-site.
*
* @param string $menu_slug
*
* @return array|boolean List of menu items with label, url, or false if the
* slug does not match a navigation list.
*/
function get_menu_items( $menu_slug ) {
$menus = apply_filters( 'wporg_block_navigation_menus', array() );
adamwoodnz marked this conversation as resolved.
Show resolved Hide resolved

if ( empty( $menus ) ) {
adamwoodnz marked this conversation as resolved.
Show resolved Hide resolved
return false;
}

return $menus[ $menu_slug ];
}
4 changes: 4 additions & 0 deletions mu-plugins/blocks/navigation/src/block.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"name": "wporg/navigation",
"editorScript": "file:./index.js"
}
86 changes: 86 additions & 0 deletions mu-plugins/blocks/navigation/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { addFilter } from '@wordpress/hooks';
import { InspectorControls } from '@wordpress/block-editor';
import { PanelBody, SelectControl } from '@wordpress/components';

const BLOCK_TYPE = 'core/navigation';
const menus = window.wporgLocalNavigationMenus || [];

/**
* Add the `menuSlug` attribute to the core/navigation block type.
*
* @param {Object} settings The settings as defined in block.json.
* @param {string} name Current block type
*
* @return {Object}
*/
const addNavigationMenuSlugAttr = ( settings, name ) => {
if ( name === BLOCK_TYPE ) {
return { ...settings, attributes: { ...settings.attributes, menuSlug: { type: 'string' } } };
}
return settings;
};
addFilter( 'blocks.registerBlockType', 'wporg/navigation-menu-slug', addNavigationMenuSlugAttr );

/**
* Inject a control for the `menuSlug` attribute in the editor.
*
* This is done by wrapping the existing edit function and adding
* a new `InspectorControls` panel.
*
* @param {Function} BlockEdit
*
* @return {Function}
*/
const withNavigationMenuSlug = ( BlockEdit ) => ( props ) => {
const { name, attributes, setAttributes } = props;

if ( name !== BLOCK_TYPE ) {
return <BlockEdit { ...props } />;
}

const options = Object.keys( menus ).map( ( value ) => {
// Create label by converting hyphenated value to title case with ` — ` separator
const label = value
.replace( /-/g, ' — ' )
.replace( /\w\S*/g, ( word ) => word.charAt( 0 ).toUpperCase() + word.slice( 1 ).toLowerCase() );

return { label, value };
} );

return (
<>
<InspectorControls group="list">
<PanelBody className={ attributes.menuSlug ? 'wporg-nav-hide-next-panel' : '' }>
<SelectControl
label={ __( 'Dynamic Menu', 'wporg' ) }
value={ attributes.menuSlug }
options={ [ { label: __( 'Custom menu', 'wporg' ), value: '' }, ...options ] }
onChange={ ( newValue ) => setAttributes( { menuSlug: newValue } ) }
__nextHasNoMarginBottom
/>
{ attributes.menuSlug ? (
<p>
{ __(
'This menu will display a hard-coded navigation, relative to the current site.',
'wporg'
) }
</p>
) : (
<p>
{ __(
'This menu will display the content below. Note that on locale sites (for example, es.wordpress.org) this menu will probably not exist.',
'wporg'
) }
</p>
) }
</PanelBody>
</InspectorControls>
<BlockEdit { ...props } />
</>
);
};
addFilter( 'editor.BlockEdit', 'wporg/navigation-menu-slug', withNavigationMenuSlug );
1 change: 1 addition & 0 deletions mu-plugins/loader.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
require_once __DIR__ . '/blocks/local-navigation-bar/index.php';
require_once __DIR__ . '/blocks/latest-news/latest-news.php';
require_once __DIR__ . '/blocks/link-wrapper/index.php';
require_once __DIR__ . '/blocks/navigation/index.php';
require_once __DIR__ . '/blocks/notice/index.php';
require_once __DIR__ . '/blocks/query-total/index.php';
require_once __DIR__ . '/blocks/sidebar-container/index.php';
Expand Down