-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Try adding 'Template Parts' as a tab on the inserter. #22612
Closed
Closed
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
821a1eb
initial commit
Addison-Stavlo 43f1b51
conditioned behind experiment
Addison-Stavlo 22a1158
create TP component
Addison-Stavlo 7f1b30a
show previews of each template part
Addison-Stavlo 5372af6
remove early return
Addison-Stavlo ff8bcff
kind of inserting
Addison-Stavlo 932e355
styles
Addison-Stavlo 37ee5ae
better inserting / bug fix
Addison-Stavlo 17d7b63
group by theme
Addison-Stavlo 49dce60
added success notice
Addison-Stavlo 9b8ca36
set up for search section
Addison-Stavlo 6892fcd
filtering working
Addison-Stavlo d52e5ab
placeholders and async list
Addison-Stavlo 2de5f38
comment and var name change
Addison-Stavlo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
197 changes: 197 additions & 0 deletions
197
packages/block-editor/src/components/inserter/template-parts.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { useSelect, useDispatch } from '@wordpress/data'; | ||
import { parse, createBlock } from '@wordpress/blocks'; | ||
import { useMemo, useCallback } from '@wordpress/element'; | ||
import { ENTER, SPACE } from '@wordpress/keycodes'; | ||
import { __, sprintf } from '@wordpress/i18n'; | ||
/** | ||
* Internal dependencies | ||
*/ | ||
import BlockPreview from '../block-preview'; | ||
import InserterPanel from './panel'; | ||
import useAsyncList from './use-async-list'; | ||
|
||
/** | ||
* External dependencies | ||
*/ | ||
import { groupBy } from 'lodash'; | ||
|
||
function TemplatePartPlaceholder() { | ||
return ( | ||
<div className="block-editor-inserter__template-part-item is-placeholder" /> | ||
); | ||
} | ||
|
||
function TemplatePartItem( { templatePart, onInsert } ) { | ||
const { id, slug, theme } = templatePart; | ||
// The 'raw' property is not defined for a brief period in the save cycle. | ||
// The fallback prevents an error in the parse function while saving. | ||
const content = templatePart.content.raw || ''; | ||
const blocks = useMemo( () => parse( content ), [ content ] ); | ||
const { createSuccessNotice } = useDispatch( 'core/notices' ); | ||
|
||
const onClick = useCallback( () => { | ||
const templatePartBlock = createBlock( 'core/template-part', { | ||
postId: id, | ||
slug, | ||
theme, | ||
} ); | ||
onInsert( templatePartBlock ); | ||
createSuccessNotice( | ||
sprintf( | ||
/* translators: %s: template part title. */ | ||
__( 'Template Part "%s" inserted.' ), | ||
slug | ||
), | ||
{ | ||
type: 'snackbar', | ||
} | ||
); | ||
}, [ id, slug, theme ] ); | ||
|
||
return ( | ||
<div | ||
className="block-editor-inserter__template-part-item" | ||
role="button" | ||
onClick={ onClick } | ||
onKeyDown={ ( event ) => { | ||
if ( ENTER === event.keyCode || SPACE === event.keyCode ) { | ||
onClick(); | ||
} | ||
} } | ||
tabIndex={ 0 } | ||
aria-label={ templatePart.slug } | ||
> | ||
<BlockPreview blocks={ blocks } /> | ||
<div className="block-editor-inserter__template-part-item-title"> | ||
{ templatePart.slug } | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
function TemplatePartsByTheme( { templateParts, onInsert } ) { | ||
const templatePartsByTheme = useMemo( () => { | ||
return Object.values( groupBy( templateParts, 'meta.theme' ) ); | ||
}, [ templateParts ] ); | ||
const currentShownTPs = useAsyncList( templateParts ); | ||
|
||
return ( | ||
<> | ||
{ templatePartsByTheme.length && | ||
templatePartsByTheme.map( ( templatePartList ) => ( | ||
<InserterPanel | ||
key={ templatePartList[ 0 ].meta.theme } | ||
title={ templatePartList[ 0 ].meta.theme } | ||
> | ||
{ templatePartList.map( ( templatePart ) => { | ||
return currentShownTPs.includes( templatePart ) ? ( | ||
<TemplatePartItem | ||
key={ templatePart.id } | ||
templatePart={ templatePart } | ||
onInsert={ onInsert } | ||
/> | ||
) : ( | ||
<TemplatePartPlaceholder | ||
key={ templatePart.id } | ||
/> | ||
); | ||
} ) } | ||
</InserterPanel> | ||
) ) } | ||
</> | ||
); | ||
} | ||
|
||
function TemplatePartSearchResults( { templateParts, onInsert, filterValue } ) { | ||
const filteredTPs = useMemo( () => { | ||
// Filter based on value. | ||
const lowerFilterValue = filterValue.toLowerCase(); | ||
const searchResults = templateParts.filter( | ||
( { slug, meta: { theme } } ) => | ||
slug.toLowerCase().includes( lowerFilterValue ) || | ||
theme.toLowerCase().includes( lowerFilterValue ) | ||
); | ||
// Order based on value location. | ||
searchResults.sort( ( a, b ) => { | ||
// First prioritize index found in slug. | ||
const indexInSlugA = a.slug | ||
.toLowerCase() | ||
.indexOf( lowerFilterValue ); | ||
const indexInSlugB = b.slug | ||
.toLowerCase() | ||
.indexOf( lowerFilterValue ); | ||
if ( indexInSlugA !== -1 && indexInSlugB !== -1 ) { | ||
return indexInSlugA - indexInSlugB; | ||
} else if ( indexInSlugA !== -1 ) { | ||
return -1; | ||
} else if ( indexInSlugB !== -1 ) { | ||
return 1; | ||
} | ||
// Second prioritize index found in theme. | ||
return ( | ||
a.meta.theme.toLowerCase().indexOf( lowerFilterValue ) - | ||
b.meta.theme.toLowerCase().indexOf( lowerFilterValue ) | ||
); | ||
} ); | ||
return searchResults; | ||
}, [ filterValue, templateParts ] ); | ||
|
||
const currentShownTPs = useAsyncList( filteredTPs ); | ||
|
||
return ( | ||
<> | ||
{ filteredTPs.map( ( templatePart ) => ( | ||
<InserterPanel | ||
key={ templatePart.id } | ||
title={ templatePart.meta.theme } | ||
> | ||
{ currentShownTPs.includes( templatePart ) ? ( | ||
<TemplatePartItem | ||
key={ templatePart.id } | ||
templatePart={ templatePart } | ||
onInsert={ onInsert } | ||
/> | ||
) : ( | ||
<TemplatePartPlaceholder key={ templatePart.id } /> | ||
) } | ||
</InserterPanel> | ||
) ) } | ||
</> | ||
); | ||
} | ||
|
||
export default function TemplateParts( { onInsert, filterValue } ) { | ||
const templateParts = useSelect( ( select ) => { | ||
return select( 'core' ).getEntityRecords( | ||
'postType', | ||
'wp_template_part', | ||
{ | ||
status: [ 'publish', 'auto-draft' ], | ||
} | ||
); | ||
}, [] ); | ||
|
||
if ( ! templateParts || ! templateParts.length ) { | ||
return null; | ||
} | ||
|
||
if ( filterValue ) { | ||
return ( | ||
<TemplatePartSearchResults | ||
templateParts={ templateParts } | ||
onInsert={ onInsert } | ||
filterValue={ filterValue } | ||
/> | ||
); | ||
} | ||
|
||
return ( | ||
<TemplatePartsByTheme | ||
templateParts={ templateParts } | ||
onInsert={ onInsert } | ||
/> | ||
); | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we add
to only show template parts for the current theme? (This would mean we'd need to get
theme
via thegetCurrentThemeSelector
.)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so. The theme is arbitrary, and user created template parts will be saved under a theme tag that does not match the current theme. Similarly, if we have the template part saved, the theme it originated from shouldn't be a restriction on if we can use it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. Just to make sure though, could that be a problem when exporting a theme?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To put on my devil's advocate's hat, I'm not even 100% convinced that we should allow inserting template parts from a different theme TBH 🤔 E.g. I'd expect a theme's header template part to kind of reflect its overall look and feel, so using a different theme's template part might give a rather inconsistent look and feel.
Furthermore, if we make available the template parts from all installed themes, it could lead to an overwhelming number of template parts; and it could be confusing to find the current theme's ones (e.g. to pick the right header). (Clearly, the latter is mitigated by grouping template parts by theme, as this PR does.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Template parts from non-active themes will only show if you have customized them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It definitely could, but it allows users to choose from other template parts they have created or customized. If they want to use them over the theme's supplied header template part, they can choose to do so. If the user still feels it doesn't fully match the style, they can be further customized.
Maybe in the future we may need to take steps to make the template part supplied by the current theme to be more prominent (default to the top of the list or something). But since "Template parts from non-active themes will only show if you have customized them.", this may not be necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That could be confusing enough tho, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, it could be confusing between base template parts supplied by the theme vs. ones you have customized from that theme? In which case maybe we could end up presenting it as 'Customized from 'theme-name''. as opposed to just 'theme-name'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should show the non-customized ones if they have a customized counterpart.