Skip to content

Commit

Permalink
Navigation: Implement redesign of Navigation Editor
Browse files Browse the repository at this point in the history
  • Loading branch information
noisysocks committed Sep 9, 2020
1 parent 458277c commit 31e2196
Show file tree
Hide file tree
Showing 37 changed files with 896 additions and 1,108 deletions.
2 changes: 2 additions & 0 deletions packages/components/src/select-control/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ function SelectControl(
options = [],
size = 'default',
value: valueProp,
labelPosition,
...props
},
ref
Expand Down Expand Up @@ -100,6 +101,7 @@ function SelectControl(
<Icon icon={ chevronDown } size={ 18 } />
</DownArrowWrapper>
}
labelPosition={ labelPosition }
>
<Select
{ ...props }
Expand Down

This file was deleted.

This file was deleted.

14 changes: 14 additions & 0 deletions packages/edit-navigation/src/components/editor/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Internal dependencies
*/
import ListView from './list-view';
import VisualView from './visual-view';

export default function Editor( { isPending, blocks } ) {
return (
<div className="edit-navigation-editor">
<ListView isPending={ isPending } blocks={ blocks } />
<VisualView isPending={ isPending } />
</div>
);
}
32 changes: 32 additions & 0 deletions packages/edit-navigation/src/components/editor/list-view.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* WordPress dependencies
*/
import { useState } from '@wordpress/element';
import { Spinner } from '@wordpress/components';
import { __experimentalBlockNavigationTree } from '@wordpress/block-editor';

export default function ListView( { isPending, blocks } ) {
const [ selectedBlockId, setSelectedBlockId ] = useState(
blocks[ 0 ]?.clientId
);

return (
<div className="edit-navigation-editor__list-view">
{ isPending ? (
<Spinner />
) : (
<__experimentalBlockNavigationTree
blocks={ blocks }
selectedBlockClientId={ selectedBlockId }
selectBlock={ ( id ) => {
setSelectedBlockId( id );
} }
__experimentalFeatures
showNestedBlocks
showAppender
showBlockMovers
/>
) }
</div>
);
}
43 changes: 43 additions & 0 deletions packages/edit-navigation/src/components/editor/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
.edit-navigation-editor {
margin-top: $grid-unit-20;

@include break-medium() {
align-items: flex-start;
display: flex;
}
}

.edit-navigation-editor__list-view,
.edit-navigation-editor__visual-view {
background: $white;
border-radius: 3px;
border: $border-width solid $gray-200;
}

.edit-navigation-editor__list-view {
padding: $grid-unit-10;

@include break-medium() {
width: 300px;
}

.components-spinner {
display: block;
margin: 100px auto;
}
}

.edit-navigation-editor__visual-view {
margin-top: $grid-unit-20;

@include break-medium() {
flex-grow: 1;
margin-left: $grid-unit-20;
margin-top: 0;
}

.components-spinner {
display: block;
margin: 10px auto;
}
}
38 changes: 38 additions & 0 deletions packages/edit-navigation/src/components/editor/visual-view.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* WordPress dependencies
*/
import { useSelect, useDispatch } from '@wordpress/data';
import { useEffect } from '@wordpress/element';
import { WritingFlow, ObserveTyping, BlockList } from '@wordpress/block-editor';
import { Spinner } from '@wordpress/components';

export default function VisualView( { isPending } ) {
const rootClientId = useSelect(
( select ) => select( 'core/block-editor' ).getBlocks()[ 0 ]?.clientId
);

const { selectBlock } = useDispatch( 'core/block-editor' );

// Select the root Navigation block when it becomes available.
useEffect( () => {
if ( rootClientId ) {
selectBlock( rootClientId );
}
}, [ rootClientId ] );

return (
<div className="edit-navigation-editor__visual-view">
{ isPending ? (
<Spinner />
) : (
<div className="editor-styles-wrapper">
<WritingFlow>
<ObserveTyping>
<BlockList />
</ObserveTyping>
</WritingFlow>
</div>
) }
</div>
);
}
41 changes: 41 additions & 0 deletions packages/edit-navigation/src/components/header/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { Button, Dropdown } from '@wordpress/components';

/**
* Internal dependencies
*/
import ManageLocations from './manage-locations';

export default function Header( { onBeginAddingMenu } ) {
return (
<div className="edit-navigation-header">
<h1>{ __( 'Navigation' ) }</h1>

<Button
className="edit-navigation-header__add-new-button"
isSecondary
onClick={ onBeginAddingMenu }
>
{ __( 'Add new' ) }
</Button>

<Dropdown
contentClassName="edit-navigation-header__manage-locations"
position="bottom center"
renderToggle={ ( { isOpen, onToggle } ) => (
<Button
isLink
aria-expanded={ isOpen }
onClick={ onToggle }
>
{ __( 'Manage locations' ) }
</Button>
) }
renderContent={ () => <ManageLocations /> }
/>
</div>
);
}
48 changes: 48 additions & 0 deletions packages/edit-navigation/src/components/header/manage-locations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
import { Spinner, SelectControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import useMenuLocations from './use-menu-locations';

export default function ManageLocations() {
const menus = useSelect( ( select ) => select( 'core' ).getMenus() );

const [ menuLocations, assignMenuToLocation ] = useMenuLocations();

if ( ! menus || ! menuLocations ) {
return <Spinner />;
}

if ( ! menus.length ) {
return <p>{ __( 'There are no available menus.' ) }</p>;
}

if ( ! menuLocations.length ) {
return <p>{ __( 'There are no available menu locations.' ) }</p>;
}

return menuLocations.map( ( menuLocation ) => (
<SelectControl
key={ menuLocation.name }
label={ menuLocation.description }
labelPosition="top"
value={ menuLocation.menu }
options={ [
{ value: 0, label: __( '— Select navigation —' ) },
...menus.map( ( menu ) => ( {
value: menu.id,
label: menu.name,
} ) ),
] }
onChange={ ( menuId ) => {
assignMenuToLocation( menuLocation.name, Number( menuId ) );
} }
/>
) );
}
25 changes: 25 additions & 0 deletions packages/edit-navigation/src/components/header/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.edit-navigation-header {
align-items: center;
display: flex;
margin-top: $grid-unit-10;
}

.edit-navigation-header__add-new-button {
margin: 0 $grid-unit-20;
}

.edit-navigation-header__manage-locations {
text-align: center;

.components-popover__content > div {
padding: $grid-unit-20;
}

.components-base-control {
margin-bottom: $grid-unit-20;

&:last-child {
margin-bottom: 0;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* WordPress dependencies
*/
import { useState, useEffect } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';
import { useDispatch } from '@wordpress/data';

export default function useMenuLocations() {
const [ menuLocationsByName, setMenuLocationsByName ] = useState( null );

useEffect( () => {
const fetchMenuLocationsByName = async () => {
const newMenuLocationsByName = await apiFetch( {
method: 'GET',
path: '/__experimental/menu-locations',
} );
setMenuLocationsByName( newMenuLocationsByName );
};
fetchMenuLocationsByName();
}, [] );

const { saveMenu } = useDispatch( 'core' );

const assignMenuToLocation = async ( locationName, newMenuId ) => {
const oldMenuId = menuLocationsByName[ locationName ].menu;

const newMenuLocationsByName = {
...menuLocationsByName,
[ locationName ]: {
...menuLocationsByName[ locationName ],
menu: newMenuId,
},
};

setMenuLocationsByName( newMenuLocationsByName );

const promises = [];

if ( oldMenuId ) {
promises.push(
saveMenu( {
id: oldMenuId,
locations: Object.values( newMenuLocationsByName )
.filter( ( { menu } ) => menu === oldMenuId )
.map( ( { name } ) => name ),
} )
);
}

if ( newMenuId ) {
promises.push(
saveMenu( {
id: newMenuId,
locations: Object.values( newMenuLocationsByName )
.filter( ( { menu } ) => menu === newMenuId )
.map( ( { name } ) => name ),
} )
);
}

await Promise.all( promises );
};

return [
menuLocationsByName ? Object.values( menuLocationsByName ) : null,
assignMenuToLocation,
];
}
Loading

0 comments on commit 31e2196

Please sign in to comment.