From 95188199c8b8045322d7f75a2666d47ea6504ad2 Mon Sep 17 00:00:00 2001 From: Jacopo Tomasone Date: Fri, 23 Oct 2020 14:05:35 +0100 Subject: [PATCH] Site Editor: Add Dropdown to Create Generic Templates (#26284) Add a very basic interface to create generic templates from the Site Editor navigation sidebar. Clicking on the + button will open a dropdown listing all missing generic templates, filling them with the hierarchically closest template content, plus a "General" template that creates a blank template. --- .../components/src/navigation/menu/index.js | 4 +- .../src/navigation/menu/menu-title.js | 33 ++++--- .../navigation/styles/navigation-styles.js | 6 +- .../navigation-panel/menus/templates.js | 2 + .../navigation-panel/new-template-dropdown.js | 91 +++++++++++++++++++ .../utils/get-closest-available-template.js | 23 +++++ 6 files changed, 146 insertions(+), 13 deletions(-) create mode 100644 packages/edit-site/src/components/navigation-sidebar/navigation-panel/new-template-dropdown.js create mode 100644 packages/edit-site/src/utils/get-closest-available-template.js diff --git a/packages/components/src/navigation/menu/index.js b/packages/components/src/navigation/menu/index.js index ef98f33b410520..4eadb9d05a8e2b 100644 --- a/packages/components/src/navigation/menu/index.js +++ b/packages/components/src/navigation/menu/index.js @@ -27,11 +27,12 @@ export default function NavigationMenu( props ) { className, hasSearch, menu = ROOT_MENU, + onBackButtonClick, onSearch: setControlledSearch, parentMenu, search: controlledSearch, title, - onBackButtonClick, + titleAction, } = props; const [ uncontrolledSearch, setUncontrolledSearch ] = useState( '' ); useNavigationTreeMenu( props ); @@ -76,6 +77,7 @@ export default function NavigationMenu( props ) { onSearch={ onSearch } search={ search } title={ title } + titleAction={ titleAction } /> diff --git a/packages/components/src/navigation/menu/menu-title.js b/packages/components/src/navigation/menu/menu-title.js index 5e8ced22683b26..6f1174e943b859 100644 --- a/packages/components/src/navigation/menu/menu-title.js +++ b/packages/components/src/navigation/menu/menu-title.js @@ -11,7 +11,11 @@ import { Icon, search as searchIcon } from '@wordpress/icons'; import Animate from '../../animate'; import Button from '../../button'; import MenuTitleSearch from './menu-title-search'; -import { MenuTitleHeadingUI, MenuTitleUI } from '../styles/navigation-styles'; +import { + MenuTitleActionsUI, + MenuTitleHeadingUI, + MenuTitleUI, +} from '../styles/navigation-styles'; import { useNavigationMenuContext } from './context'; import { SEARCH_FOCUS_DELAY } from '../constants'; @@ -20,6 +24,7 @@ export default function NavigationMenuTitle( { onSearch, search, title, + titleAction, } ) { const [ isSearching, setIsSearching ] = useState( false ); const { menu } = useNavigationMenuContext(); @@ -53,16 +58,22 @@ export default function NavigationMenuTitle( { > { title } - { hasSearch && ( - + { ( hasSearch || titleAction ) && ( + + { titleAction } + + { hasSearch && ( + + ) } + ) } ) } diff --git a/packages/components/src/navigation/styles/navigation-styles.js b/packages/components/src/navigation/styles/navigation-styles.js index 43bc022454377e..8e5a8df3721bba 100644 --- a/packages/components/src/navigation/styles/navigation-styles.js +++ b/packages/components/src/navigation/styles/navigation-styles.js @@ -62,6 +62,10 @@ export const MenuTitleHeadingUI = styled( Text )` justify-content: space-between; margin-bottom: ${ space( 1 ) }; padding: ${ space( 0.5 ) } 0 ${ space( 0.5 ) } ${ space( 2 ) }; +`; + +export const MenuTitleActionsUI = styled.span` + height: ${ space( 3 ) }; // 24px, same height as the buttons inside .components-button.is-small { color: ${ G2.lightGray.ui }; @@ -84,7 +88,7 @@ export const MenuTitleSearchUI = styled.div` position: relative; input { - height: 36px; // Same height as MenuTitle + height: ${ space( 4.5 ) }; // 36px, same height as MenuTitle margin-bottom: ${ space( 1 ) }; padding-left: 30px; // Leave room for the search icon padding-right: 30px; // Leave room for the close search button diff --git a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates.js b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates.js index 8a242ea83323d8..e4b79762c204b6 100644 --- a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates.js +++ b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates.js @@ -23,6 +23,7 @@ import { } from '../constants'; import { useSelect } from '@wordpress/data'; import TemplatesAllMenu from './templates-all'; +import NewTemplateDropdown from '../new-template-dropdown'; export default function TemplatesMenu( { onActivateItem } ) { const templates = useSelect( @@ -42,6 +43,7 @@ export default function TemplatesMenu( { onActivateItem } ) { } parentMenu={ MENU_ROOT } > + select( 'core' ).getEntityRecords( 'postType', 'wp_template', { + status: [ 'publish', 'auto-draft' ], + per_page: -1, + } ), + [] + ); + const { addTemplate } = useDispatch( 'core/edit-site' ); + + const createTemplate = ( slug ) => { + const closestAvailableTemplate = getClosestAvailableTemplate( + slug, + templates + ); + addTemplate( { + content: closestAvailableTemplate.content.raw, + slug, + title: slug, + status: 'draft', + } ); + }; + + const missingTemplates = omit( + TEMPLATES_DEFAULT_DETAILS, + map( templates, 'slug' ) + ); + + return ( + , + isSmall: true, + isTertiary: true, + } } + > + { ( { onClose } ) => ( + + + { map( + missingTemplates, + ( { title, description }, slug ) => ( + { + createTemplate( slug ); + onClose(); + } } + > + { title } + + ) + ) } + + + ) } + + ); +} diff --git a/packages/edit-site/src/utils/get-closest-available-template.js b/packages/edit-site/src/utils/get-closest-available-template.js new file mode 100644 index 00000000000000..a12bbbe96d944e --- /dev/null +++ b/packages/edit-site/src/utils/get-closest-available-template.js @@ -0,0 +1,23 @@ +/** + * External dependencies + */ +import { find } from 'lodash'; + +export default function getClosestAvailableTemplate( slug, templates ) { + if ( 'front-page' === slug ) { + const homeTemplate = find( templates, { slug: 'home' } ); + if ( homeTemplate ) { + return homeTemplate; + } + } + + if ( 'single' === slug || 'page' === slug ) { + const singularTemplate = find( templates, { slug: 'singular' } ); + if ( singularTemplate ) { + return singularTemplate; + } + } + + const indexTemplate = find( templates, { slug: 'index' } ); + return indexTemplate; +}