diff --git a/docs/manifest.json b/docs/manifest.json index 41fbf7da9dae29..0c8ae73274ed07 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -1043,6 +1043,12 @@ "markdown_source": "../packages/components/src/navigable-container/README.md", "parent": "components" }, + { + "title": "Navigation", + "slug": "navigation", + "markdown_source": "../packages/components/src/navigation/README.md", + "parent": "components" + }, { "title": "Notice", "slug": "notice", diff --git a/packages/components/src/navigation/README.md b/packages/components/src/navigation/README.md new file mode 100644 index 00000000000000..87bab075e53e8b --- /dev/null +++ b/packages/components/src/navigation/README.md @@ -0,0 +1,43 @@ +# Navigation + +Navigation is a component which renders a component which renders a heirarchy of menu items. + +## Usage + +```jsx +import { MenuItem } from '@wordpress/components'; +import { withState } from '@wordpress/compose'; + +const data = [ + { title: 'My Navigation', slug: 'root', back: 'Back' }, + { title: 'Home', slug: 'home', parent: 'root', menu: 'primary' }, + { title: 'Option one', slug: 'option_one', parent: 'root', menu: 'primary' }, + { title: 'Option two', slug: 'option_two', parent: 'root', menu: 'primary' }, + { title: 'Option three', slug: 'option_three', parent: 'root', menu: 'secondary' }, + { title: 'Child one', slug: 'child_one', parent: 'option_three', menu: 'primary' }, + { title: 'Child two', slug: 'child_two', parent: 'option_three', menu: 'primary' }, + { title: 'Child three', slug: 'child_three', parent: 'option_three', menu: 'primary' }, +]; + +const MyNavigation = () => { + return ; +}; +``` + +## Props + +Navigation supports the following props. + +### `data` + +- Type: `array` +- Required: Yes + +An array of config objects for each menu item. + +### `initial` + +- Type: `string` +- Required: Yes + +The active slug. \ No newline at end of file diff --git a/packages/components/src/navigation/index.js b/packages/components/src/navigation/index.js new file mode 100644 index 00000000000000..bf5199edc2cb9e --- /dev/null +++ b/packages/components/src/navigation/index.js @@ -0,0 +1,76 @@ +/** + * WordPress dependencies + */ +import { useState } from '@wordpress/element'; +import { Icon, arrowLeft } from '@wordpress/icons'; + +/** + * Internal dependencies + */ +import Button from '../button'; +import Text from '../text'; +import Item from './item'; + +const Navigation = ( { data, initial } ) => { + const initialActive = data.find( ( item ) => item.slug === initial ); + const [ active, setActive ] = useState( initialActive ); + const parent = data.find( ( item ) => item.slug === active.parent ); + const items = data.filter( ( item ) => item.parent === active.parent ); + + const goBack = () => { + if ( ! parent.parent ) { + // We are at top level, will need to handle this case. + return; + } + const parentalSiblings = data.filter( + ( item ) => item.parent === parent.parent + ); + if ( parentalSiblings.length ) { + setActive( parentalSiblings[ 0 ] ); + } + }; + + return ( +
+ +
+ { parent.title } +
+
+ { items.map( ( item ) => + item.menu !== 'secondary' ? ( + + ) : null + ) } +
+
+ { items.map( ( item ) => + item.menu === 'secondary' ? ( + + ) : null + ) } +
+
+ ); +}; + +export default Navigation; diff --git a/packages/components/src/navigation/item.js b/packages/components/src/navigation/item.js new file mode 100644 index 00000000000000..b3453dbbf99793 --- /dev/null +++ b/packages/components/src/navigation/item.js @@ -0,0 +1,36 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { Icon, chevronRight } from '@wordpress/icons'; + +/** + * Internal dependencies + */ +import Button from '../button'; +import Text from '../text'; + +const Item = ( { data, item, setActive, isActive } ) => { + const children = data.filter( ( d ) => d.parent === item.slug ); + const onSelect = () => { + const next = children.length ? children[ 0 ] : item; + setActive( next ); + }; + const classes = classnames( 'components-navigation__menu-item', { + 'is-active': isActive, + } ); + return ( + + ); +}; + +export default Item; diff --git a/packages/components/src/navigation/stories/index.js b/packages/components/src/navigation/stories/index.js new file mode 100644 index 00000000000000..ede16f3046136f --- /dev/null +++ b/packages/components/src/navigation/stories/index.js @@ -0,0 +1,90 @@ +/** + * Internal dependencies + */ +import Navigation from '../'; + +export default { + title: 'Components/Navigation', + component: Navigation, +}; + +const data = [ + { title: 'WooCommerce', slug: 'root', back: 'Dashboard' }, + { title: 'Home', slug: 'home', parent: 'root', menu: 'primary' }, + { + title: 'Analytics', + slug: 'analytics', + parent: 'root', + back: 'WooCommerce Home', + menu: 'primary', + }, + { + title: 'Orders', + slug: 'orders', + parent: 'root', + back: 'WooCommerce Home', + menu: 'primary', + }, + { + title: 'Overview', + slug: 'overview', + parent: 'analytics', + }, + { + title: 'Products report', + slug: 'products', + parent: 'analytics', + }, + { + title: 'All orders', + slug: 'all_orders', + parent: 'orders', + }, + { + title: 'Payouts', + slug: 'payouts', + parent: 'orders', + }, + { + title: 'Settings', + slug: 'settings', + parent: 'root', + back: 'WooCommerce Home', + menu: 'secondary', + }, + { + title: 'Extensions', + slug: 'extensions', + parent: 'root', + back: 'WooCommerce Home', + menu: 'secondary', + }, + { + title: 'General', + slug: 'general', + parent: 'settings', + }, + { + title: 'Tax', + slug: 'tax', + parent: 'settings', + }, + { + title: 'My extensions', + slug: 'my_extensions', + parent: 'extensions', + }, + { + title: 'Marketplace', + slug: 'marketplace', + parent: 'extensions', + }, +]; + +function Example() { + return ; +} + +export const _default = () => { + return ; +}; diff --git a/packages/components/src/navigation/style.scss b/packages/components/src/navigation/style.scss new file mode 100644 index 00000000000000..1c90f0573f0439 --- /dev/null +++ b/packages/components/src/navigation/style.scss @@ -0,0 +1,30 @@ +.components-navigation { + width: 272px; + padding: 35px 30px; +} + +.components-navigation__back { + margin-bottom: 64px; +} + +.components-navigation__title { + margin-bottom: 40px; +} + +.components-navigation__menu-items { + display: flex; + flex-direction: column; + + &.is-secondary { + margin-top: 24px; + } +} + +.components-navigation__menu-item { + display: flex; + justify-content: space-between; + + &.is-active span { + border-bottom: 2px solid var(--wp-admin-theme-color); + } +} diff --git a/packages/components/src/style.scss b/packages/components/src/style.scss index 36c406c3c7a414..3066b604428cc5 100644 --- a/packages/components/src/style.scss +++ b/packages/components/src/style.scss @@ -29,6 +29,7 @@ @import "./menu-item/style.scss"; @import "./menu-items-choice/style.scss"; @import "./modal/style.scss"; +@import "./navigation/style.scss"; @import "./notice/style.scss"; @import "./panel/style.scss"; @import "./placeholder/style.scss";