Skip to content

Commit

Permalink
Add: Modal to choose a start pattern on new templates.
Browse files Browse the repository at this point in the history
  • Loading branch information
jorgefilipecosta committed Jan 16, 2023
1 parent c75db83 commit 48a1569
Show file tree
Hide file tree
Showing 7 changed files with 279 additions and 20 deletions.
12 changes: 12 additions & 0 deletions lib/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -352,3 +352,15 @@ function gutenberg_register_legacy_social_link_blocks() {
}

add_action( 'init', 'gutenberg_register_legacy_social_link_blocks' );

register_block_pattern(
'custom-pattern',
array(
'title' => _x( 'Start post pattern', 'Block pattern title', 'gutenberg' ),
'blockTypes' => array( 'core/paragraph', 'core/post-content' ),
// 'postTypes' => array( 'page' ),
'content' => '<!-- wp:paragraph -->
<p>A start post pattern</p>
<!-- /wp:paragraph -->',
)
);
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ class Gutenberg_REST_Templates_Controller_6_2 extends Gutenberg_REST_Templates_C
* @return void
*/
public function register_routes() {
parent::register_routes();
// Get fallback template content.

register_rest_route(
$this->namespace,
'/' . $this->rest_base . '/lookup',
Expand All @@ -41,10 +40,17 @@ public function register_routes() {
'description' => __( 'The template prefix for the created template. This is used to extract the main template type ex. in `taxonomy-books` we extract the `taxonomy`', 'gutenberg' ),
'type' => 'string',
),
'ignore_empty' => array(
'description' => __( 'If true templates with empty content are ignored.', 'gutenberg' ),
'type' => 'boolean',
'default' => false,
),
),
),
)
);
parent::register_routes();
// Get fallback template content.
}

/**
Expand All @@ -56,8 +62,16 @@ public function register_routes() {
*/
public function get_template_fallback( $request ) {
$hierarchy = get_template_hierarchy( $request['slug'], $request['is_custom'], $request['template_prefix'] );
$fallback_template = resolve_block_template( $request['slug'], $hierarchy, '' );
$response = $this->prepare_item_for_response( $fallback_template, $request );
$fallback_template = null;
if ( true === $request['ignore_empty'] ) {
do {
$fallback_template = resolve_block_template( $request['slug'], $hierarchy, '' );
array_shift( $hierarchy );
} while ( ! empty( $hierarchy ) && empty( $fallback_template->content ) );
} else {
$fallback_template = resolve_block_template( $request['slug'], $hierarchy, '' );
}
$response = $this->prepare_item_for_response( $fallback_template, $request );
return rest_ensure_response( $response );
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
/**
* WordPress dependencies
*/
import apiFetch from '@wordpress/api-fetch';
import { addQueryArgs } from '@wordpress/url';
import {
DropdownMenu,
MenuGroup,
Expand Down Expand Up @@ -107,19 +105,7 @@ export default function NewTemplate( {
}
setIsCreatingTemplate( true );
try {
const { title, description, slug, templatePrefix } = template;
let templateContent = template.content;
// Try to find fallback content from existing templates.
if ( ! templateContent ) {
const fallbackTemplate = await apiFetch( {
path: addQueryArgs( '/wp/v2/templates/lookup', {
slug,
is_custom: ! isWPSuggestion,
template_prefix: templatePrefix,
} ),
} );
templateContent = fallbackTemplate.content.raw;
}
const { title, description, slug } = template;
const newTemplate = await saveEntityRecord(
'postType',
'wp_template',
Expand All @@ -129,7 +115,6 @@ export default function NewTemplate( {
slug: slug.toString(),
status: 'publish',
title,
content: templateContent,
// This adds a post meta field in template that is part of `is_custom` value calculation.
is_wp_suggestion: isWPSuggestion,
},
Expand Down
2 changes: 2 additions & 0 deletions packages/edit-site/src/components/editor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import KeyboardShortcuts from '../keyboard-shortcuts';
import InserterSidebar from '../secondary-sidebar/inserter-sidebar';
import ListViewSidebar from '../secondary-sidebar/list-view-sidebar';
import WelcomeGuide from '../welcome-guide';
import StartTemplateOptions from '../start-template-options';
import { store as editSiteStore } from '../../store';
import { GlobalStylesRenderer } from '../global-styles-renderer';
import { GlobalStylesProvider } from '../global-styles/global-styles-provider';
Expand Down Expand Up @@ -166,6 +167,7 @@ export default function Editor() {
<GlobalStylesProvider>
<BlockContextProvider value={ blockContext }>
<SidebarComplementaryAreaFills />
<StartTemplateOptions />
<InterfaceSkeleton
enableRegionNavigation={ false }
className={
Expand Down
189 changes: 189 additions & 0 deletions packages/edit-site/src/components/start-template-options/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/**
* WordPress dependencies
*/
import { Modal } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { useState, useEffect, useMemo } from '@wordpress/element';
import { __experimentalBlockPatternsList as BlockPatternsList } from '@wordpress/block-editor';
import { useSelect } from '@wordpress/data';
import { useAsyncList, useResizeObserver } from '@wordpress/compose';
import { store as preferencesStore } from '@wordpress/preferences';
import { parse } from '@wordpress/blocks';

/**
* Internal dependencies
*/
import { store as editSiteStore } from '../../store';
import { store as coreStore, useEntityBlockEditor } from '@wordpress/core-data';
import apiFetch from '@wordpress/api-fetch';
import { addQueryArgs } from '@wordpress/url';

function useFallbackTemplateContent( slug, isCustom = false ) {
const [ templateContent, setTemplateContent ] = useState( '' );

useEffect( () => {
apiFetch( {
path: addQueryArgs( '/wp/v2/templates/lookup', {
slug,
is_custom: isCustom,
ignore_empty: true,
} ),
} ).then( ( { content } ) => setTemplateContent( content.raw ) );
}, [ slug ] );
return templateContent;
}

const START_BLANK_TITLE = __( 'Start blank' );

function PatternSelection( { fallbackContent, onChoosePattern, postType } ) {
const [ resizeObserver, sizes ] = useResizeObserver();
const [ gridHeight, setGridHeight ] = useState( '320px' );
const [ , , onChange ] = useEntityBlockEditor( 'postType', postType );
const blockPatterns = useMemo(
() => [
{
name: 'fallback',
blocks: parse( fallbackContent ),
title: __( 'Fallback content' ),
},
{
name: 'start-blank',
blocks: parse(
'<!-- wp:paragraph --><p></p><!-- /wp:paragraph -->'
),
title: START_BLANK_TITLE,
},
],
[ fallbackContent ]
);
const shownBlockPatterns = useAsyncList( blockPatterns );
useEffect( () => {
const elementOffSetWidth = window?.document?.querySelector(
'.edit-site-start-template-options__pattern-container .block-editor-block-patterns-list__list-item'
)?.offsetWidth;
if ( elementOffSetWidth ) {
setGridHeight( `${ ( elementOffSetWidth * 4 ) / 3 }px` );
}
}, [ blockPatterns, sizes.width ] );
return (
<div
className="edit-site-start-template-options__pattern-container"
style={ {
'--wp-edit-site-start-template-options-start-blank': `"${ START_BLANK_TITLE }"`,
'--wp-edit-site-start-template-options-grid-height': gridHeight,
} }
>
{ resizeObserver }
<BlockPatternsList
blockPatterns={ blockPatterns }
shownPatterns={ shownBlockPatterns }
showTitlesAsTooltip
onClickPattern={ ( pattern, blocks ) => {
onChange( 'start-blank' === pattern.name ? [] : blocks, {
selection: undefined,
} );
onChoosePattern();
} }
/>
</div>
);
}

function StartModal( { slug, isCustom, onClose, postType } ) {
const fallbackContent = useFallbackTemplateContent( slug, isCustom );
if ( ! fallbackContent ) {
return null;
}
return (
<Modal
className="edit-site-start-template-options__modal"
title={ __( 'Choose a pattern' ) }
closeLabel={ __( 'Cancel' ) }
focusOnMount="firstElement"
onRequestClose={ onClose }
>
<div className="edit-site-start-template-options__modal-content">
<PatternSelection
fallbackContent={ fallbackContent }
slug={ slug }
isCustom={ isCustom }
postType={ postType }
onChoosePattern={ () => {
onClose();
} }
/>
</div>
</Modal>
);
}

const START_TEMPLATE_MODAL_STATES = {
INITIAL: 'INITIAL',
PATTERN: 'PATTERN',
CLOSED: 'CLOSED',
};

export default function StartTemplateOptions() {
const [ modalState, setModalState ] = useState(
START_TEMPLATE_MODAL_STATES.INITIAL
);
const { shouldOpenModel, slug, isCustom, postType } = useSelect(
( select ) => {
const { getEditedPostType, getEditedPostId } =
select( editSiteStore );
const _postType = getEditedPostType();
const postId = getEditedPostId();
const {
__experimentalGetDirtyEntityRecords,
getEditedEntityRecord,
} = select( coreStore );
const templateRecord = getEditedEntityRecord(
'postType',
_postType,
postId
);

const hasDirtyEntityRecords =
__experimentalGetDirtyEntityRecords().length > 0;

return {
shouldOpenModel:
modalState === START_TEMPLATE_MODAL_STATES.INITIAL &&
! hasDirtyEntityRecords &&
'' === templateRecord.content &&
'wp_template' === _postType &&
! select( preferencesStore ).get(
'core/edit-site',
'welcomeGuide'
),
slug: templateRecord.slug,
isCustom: templateRecord.is_custom,
postType: _postType,
};
},
[ modalState ]
);

useEffect( () => {
if ( shouldOpenModel ) {
setModalState( START_TEMPLATE_MODAL_STATES.PATTERN );
}
}, [ shouldOpenModel ] );

if (
modalState === START_TEMPLATE_MODAL_STATES.INITIAL ||
modalState === START_TEMPLATE_MODAL_STATES.CLOSED
) {
return null;
}
return (
<StartModal
slug={ slug }
isCustom={ isCustom }
postType={ postType }
onClose={ () =>
setModalState( START_TEMPLATE_MODAL_STATES.CLOSED )
}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
.edit-site-start-template-options__modal.components-modal__frame {
// To keep modal dimensions consistent as subsections are navigated, width
// and height are used instead of max-(width/height).
@include break-small() {
width: calc(100% - #{ $grid-unit-20 * 2 });
height: calc(100% - #{ $header-height * 2 });
}
@include break-medium() {
width: 70%;
}
@include break-large() {
height: fit-content;
}
}

.edit-site-start-template-options__modal-content .block-editor-block-patterns-list {
display: grid;
width: 100%;
margin-top: $grid-unit-05;
gap: $grid-unit-10;
grid-template-columns: repeat(auto-fit, minmax(min(100%/2, max(240px, 100%/10)), 1fr));
grid-auto-rows: var(--wp-edit-site-start-template-options-grid-height);
.block-editor-block-patterns-list__list-item {
break-inside: avoid-column;
margin-bottom: $grid-unit-30;
width: 100%;

.block-editor-block-preview__container {
height: 100%;
}

.block-editor-block-preview__content {
width: 100%;
position: absolute;
}
}

// The start blank pattern is the last and we are selecting it.
.block-editor-block-patterns-list__list-item:nth-last-child(2) {
.block-editor-block-preview__container {
position: absolute;
padding: 0;
background: #f0f0f0;
&::after {
width: 100%;
top: 50%;
margin-top: -1em;
content: var(--wp-edit-site-start-template-options-start-blank);
text-align: center;
}
}
iframe {
display: none;
}
}
}
1 change: 1 addition & 0 deletions packages/edit-site/src/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
@import "./components/create-template-part-modal/style.scss";
@import "./components/secondary-sidebar/style.scss";
@import "./components/welcome-guide/style.scss";
@import "./components/start-template-options/style.scss";
@import "./components/keyboard-shortcut-help-modal/style.scss";
@import "./components/layout/style.scss";
@import "./components/sidebar/style.scss";
Expand Down

0 comments on commit 48a1569

Please sign in to comment.