From 53a3bea234905b3163f7536648f3e6d927fce7eb Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Fri, 15 Jul 2022 12:25:20 +0400 Subject: [PATCH 01/12] Template Parts: Add search to replacement modal --- .../src/template-part/edit/selection-modal.js | 104 +++++++++++------- .../src/template-part/edit/utils/search.js | 20 ++++ 2 files changed, 85 insertions(+), 39 deletions(-) create mode 100644 packages/block-library/src/template-part/edit/utils/search.js diff --git a/packages/block-library/src/template-part/edit/selection-modal.js b/packages/block-library/src/template-part/edit/selection-modal.js index a9e0ed68255ced..bcac2523923296 100644 --- a/packages/block-library/src/template-part/edit/selection-modal.js +++ b/packages/block-library/src/template-part/edit/selection-modal.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useCallback, useMemo } from '@wordpress/element'; +import { useCallback, useMemo, useState } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; import { store as noticesStore } from '@wordpress/notices'; import { useDispatch } from '@wordpress/data'; @@ -11,6 +11,10 @@ import { __experimentalBlockPatternsList as BlockPatternsList, store as blockEditorStore, } from '@wordpress/block-editor'; +import { + SearchControl, + __experimentalHStack as HStack, +} from '@wordpress/components'; /** * Internal dependencies @@ -21,6 +25,7 @@ import { useCreateTemplatePartFromBlocks, } from './utils/hooks'; import { createTemplatePartId } from './utils/create-template-part-id'; +import { searchItems } from './utils/search'; export default function TemplatePartSelectionModal( { setAttributes, @@ -29,6 +34,8 @@ export default function TemplatePartSelectionModal( { area, clientId, } ) { + const [ searchValue, setSearchValue ] = useState( '' ); + // When the templatePartId is undefined, // it means the user is creating a new one from the placeholder. const isReplacingTemplatePartContent = !! templatePartId; @@ -37,18 +44,24 @@ export default function TemplatePartSelectionModal( { templatePartId ); // We can map template parts to block patters to reuse the BlockPatternsList UI - const templartPartsAsBlockPatterns = useMemo( () => { - return templateParts.map( ( templatePart ) => ( { + const filteredTemplateParts = useMemo( () => { + const partsAsPatterns = templateParts.map( ( templatePart ) => ( { name: createTemplatePartId( templatePart.theme, templatePart.slug ), title: templatePart.title.rendered, blocks: parse( templatePart.content.raw ), templatePart, } ) ); - }, [ templateParts ] ); - const shownTemplateParts = useAsyncList( templartPartsAsBlockPatterns ); - const { createSuccessNotice } = useDispatch( noticesStore ); + + return searchItems( partsAsPatterns, searchValue ); + }, [ templateParts, searchValue ] ); + const shownTemplateParts = useAsyncList( filteredTemplateParts ); const blockPatterns = useAlternativeBlockPatterns( area, clientId ); - const shownBlockPatterns = useAsyncList( blockPatterns ); + const filteredBlockPatterns = useMemo( () => { + return searchItems( blockPatterns, searchValue ); + }, [ blockPatterns, searchValue ] ); + const shownBlockPatterns = useAsyncList( filteredBlockPatterns ); + + const { createSuccessNotice } = useDispatch( noticesStore ); const { replaceInnerBlocks } = useDispatch( blockEditorStore ); const onTemplatePartSelect = useCallback( ( templatePart ) => { @@ -76,40 +89,53 @@ export default function TemplatePartSelectionModal( { ); return ( - <> -
- { !! templartPartsAsBlockPatterns.length && ( -
-

{ __( 'Existing template parts' ) }

- { - onTemplatePartSelect( pattern.templatePart ); - } } - /> -
- ) } +
+
+ +
+ { !! filteredTemplateParts.length && ( +
+

{ __( 'Existing template parts' ) }

+ { + onTemplatePartSelect( pattern.templatePart ); + } } + /> +
+ ) } - { !! blockPatterns.length && ( -
-

{ __( 'Patterns' ) }

- { - if ( isReplacingTemplatePartContent ) { - replaceInnerBlocks( clientId, blocks ); - } else { - createFromBlocks( blocks, pattern.title ); - } + { !! filteredBlockPatterns.length && ( +
+

{ __( 'Patterns' ) }

+ { + if ( isReplacingTemplatePartContent ) { + replaceInnerBlocks( clientId, blocks ); + } else { + createFromBlocks( blocks, pattern.title ); + } - onClose(); - } } - /> -
+ onClose(); + } } + /> +
+ ) } + + { ! filteredTemplateParts.length && + ! filteredBlockPatterns.length && ( + +

{ __( 'No results found.' ) }

+
) } -
- +
); } diff --git a/packages/block-library/src/template-part/edit/utils/search.js b/packages/block-library/src/template-part/edit/utils/search.js new file mode 100644 index 00000000000000..fc0ea7755f3b03 --- /dev/null +++ b/packages/block-library/src/template-part/edit/utils/search.js @@ -0,0 +1,20 @@ +/** + * Filters an item list given a search term. + * + * @param {Array} items Item list + * @param {string} searchValue Search input. + * + * @return {Array} Filtered item list. + */ +export function searchItems( items, searchValue ) { + if ( ! searchValue ) { + return items; + } + + const normalizedSearchValue = searchValue.toLowerCase(); + return items.filter( ( item ) => { + const normalizedTitle = item.title.toLowerCase(); + + return normalizedTitle.includes( normalizedSearchValue ); + } ); +} From f91d9aae4725ce25e48978afcb0630b727671a89 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 18 Jul 2022 11:30:07 +0400 Subject: [PATCH 02/12] Nitpick --- .../src/template-part/edit/selection-modal.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/block-library/src/template-part/edit/selection-modal.js b/packages/block-library/src/template-part/edit/selection-modal.js index bcac2523923296..528055e66c3ef1 100644 --- a/packages/block-library/src/template-part/edit/selection-modal.js +++ b/packages/block-library/src/template-part/edit/selection-modal.js @@ -88,6 +88,9 @@ export default function TemplatePartSelectionModal( { setAttributes ); + const hasTemplateParts = !! filteredTemplateParts.length; + const hasBlockPatterns = !! filteredBlockPatterns.length; + return (
@@ -98,7 +101,7 @@ export default function TemplatePartSelectionModal( { placeholder={ __( 'Search' ) } />
- { !! filteredTemplateParts.length && ( + { hasTemplateParts && (

{ __( 'Existing template parts' ) }

) } - { !! filteredBlockPatterns.length && ( + { hasBlockPatterns && (

{ __( 'Patterns' ) }

) } - { ! filteredTemplateParts.length && - ! filteredBlockPatterns.length && ( - -

{ __( 'No results found.' ) }

-
- ) } + { ! hasTemplateParts && ! hasBlockPatterns && ( + +

{ __( 'No results found.' ) }

+
+ ) }
); } From af6ef39576af02451f4b5046c85a4d884d4b0943 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 18 Jul 2022 13:42:36 +0400 Subject: [PATCH 03/12] Make search sticky --- packages/base-styles/_z-index.scss | 1 + packages/block-library/src/template-part/editor.scss | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/packages/base-styles/_z-index.scss b/packages/base-styles/_z-index.scss index 76cfad0b424a1a..f5b6331e6fa589 100644 --- a/packages/base-styles/_z-index.scss +++ b/packages/base-styles/_z-index.scss @@ -18,6 +18,7 @@ $z-layers: ( ".block-editor-inserter__tabs .components-tab-panel__tab-content": 0, // lower scrolling content ".block-editor-inserter__tabs .components-tab-panel__tabs": 1, // higher sticky element ".block-editor-inserter__search": 1, // higher sticky element + ".block-library-template-part__selection-search": 1, // higher sticky element // These next two share a stacking context ".interface-complementary-area .components-panel" : 0, // lower scrolling content diff --git a/packages/block-library/src/template-part/editor.scss b/packages/block-library/src/template-part/editor.scss index ca1011bf5522dd..11057806b9faf4 100644 --- a/packages/block-library/src/template-part/editor.scss +++ b/packages/block-library/src/template-part/editor.scss @@ -16,3 +16,11 @@ } } } + +.block-library-template-part__selection-search { + background: $white; + padding-top: $grid-unit-20; + position: sticky; + top: 0; + z-index: z-index(".block-library-template-part__selection-search"); +} From d16c616c1b25f9a9d2f02bab03eacecc672ded82 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 18 Jul 2022 14:33:30 +0400 Subject: [PATCH 04/12] Improve search --- .../src/template-part/edit/selection-modal.js | 6 +- .../src/template-part/edit/utils/search.js | 76 ++++++++++++++++--- 2 files changed, 69 insertions(+), 13 deletions(-) diff --git a/packages/block-library/src/template-part/edit/selection-modal.js b/packages/block-library/src/template-part/edit/selection-modal.js index 528055e66c3ef1..68a4c488fa875e 100644 --- a/packages/block-library/src/template-part/edit/selection-modal.js +++ b/packages/block-library/src/template-part/edit/selection-modal.js @@ -25,7 +25,7 @@ import { useCreateTemplatePartFromBlocks, } from './utils/hooks'; import { createTemplatePartId } from './utils/create-template-part-id'; -import { searchItems } from './utils/search'; +import { searchPatterns } from './utils/search'; export default function TemplatePartSelectionModal( { setAttributes, @@ -52,12 +52,12 @@ export default function TemplatePartSelectionModal( { templatePart, } ) ); - return searchItems( partsAsPatterns, searchValue ); + return searchPatterns( partsAsPatterns, searchValue ); }, [ templateParts, searchValue ] ); const shownTemplateParts = useAsyncList( filteredTemplateParts ); const blockPatterns = useAlternativeBlockPatterns( area, clientId ); const filteredBlockPatterns = useMemo( () => { - return searchItems( blockPatterns, searchValue ); + return searchPatterns( blockPatterns, searchValue ); }, [ blockPatterns, searchValue ] ); const shownBlockPatterns = useAsyncList( filteredBlockPatterns ); diff --git a/packages/block-library/src/template-part/edit/utils/search.js b/packages/block-library/src/template-part/edit/utils/search.js index fc0ea7755f3b03..e0f3c50763f620 100644 --- a/packages/block-library/src/template-part/edit/utils/search.js +++ b/packages/block-library/src/template-part/edit/utils/search.js @@ -1,20 +1,76 @@ /** - * Filters an item list given a search term. + * External dependencies + */ +import removeAccents from 'remove-accents'; + +/** + * Sanitizes the search input string. + * + * @param {string} input The search input to normalize. + * + * @return {string} The normalized search input. + */ +function normalizeSearchInput( input = '' ) { + // Disregard diacritics. + input = removeAccents( input ); + + // Trim & Lowercase. + input = input.trim().toLowerCase(); + + return input; +} + +/** + * Get the search rank for a given pattern and a specific search term. + * + * @param {Object} pattern Pattern to rank + * @param {string} searchValue Search term + * @return {number} A pattern search rank + */ +function getSearchPatternRank( pattern, searchValue ) { + const normalizedSearchValue = normalizeSearchInput( searchValue ); + const normalizedTitle = normalizeSearchInput( pattern.title ); + + let rank = 0; + + if ( normalizedSearchValue === normalizedTitle ) { + rank += 30; + } else if ( normalizedTitle.startsWith( normalizedSearchValue ) ) { + rank += 20; + } else { + const searchTerms = normalizedSearchValue.split( ' ' ); + const hasMatchedTerms = searchTerms.every( ( searchTerm ) => + normalizedTitle.includes( searchTerm ) + ); + + // Prefer pattern with every search word in the title. + if ( hasMatchedTerms ) { + rank += 10; + } + } + + return rank; +} + +/** + * Filters an pattern list given a search term. * - * @param {Array} items Item list + * @param {Array} patterns Item list * @param {string} searchValue Search input. * - * @return {Array} Filtered item list. + * @return {Array} Filtered pattern list. */ -export function searchItems( items, searchValue ) { +export function searchPatterns( patterns = [], searchValue = '' ) { if ( ! searchValue ) { - return items; + return patterns; } - const normalizedSearchValue = searchValue.toLowerCase(); - return items.filter( ( item ) => { - const normalizedTitle = item.title.toLowerCase(); + const rankedPatterns = patterns + .map( ( pattern ) => { + return [ pattern, getSearchPatternRank( pattern, searchValue ) ]; + } ) + .filter( ( [ , rank ] ) => rank > 0 ); - return normalizedTitle.includes( normalizedSearchValue ); - } ); + rankedPatterns.sort( ( [ , rank1 ], [ , rank2 ] ) => rank2 - rank1 ); + return rankedPatterns.map( ( [ pattern ] ) => pattern ); } From c393c6eeff02cac9b785ee73d17b6d5ad4454789 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 18 Jul 2022 14:37:54 +0400 Subject: [PATCH 05/12] Update function name --- packages/block-library/src/template-part/edit/utils/search.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/block-library/src/template-part/edit/utils/search.js b/packages/block-library/src/template-part/edit/utils/search.js index e0f3c50763f620..46db0a5ad14a72 100644 --- a/packages/block-library/src/template-part/edit/utils/search.js +++ b/packages/block-library/src/template-part/edit/utils/search.js @@ -27,7 +27,7 @@ function normalizeSearchInput( input = '' ) { * @param {string} searchValue Search term * @return {number} A pattern search rank */ -function getSearchPatternRank( pattern, searchValue ) { +function getPatternSearchRank( pattern, searchValue ) { const normalizedSearchValue = normalizeSearchInput( searchValue ); const normalizedTitle = normalizeSearchInput( pattern.title ); @@ -67,7 +67,7 @@ export function searchPatterns( patterns = [], searchValue = '' ) { const rankedPatterns = patterns .map( ( pattern ) => { - return [ pattern, getSearchPatternRank( pattern, searchValue ) ]; + return [ pattern, getPatternSearchRank( pattern, searchValue ) ]; } ) .filter( ( [ , rank ] ) => rank > 0 ); From d1a78e514c5bbcb3aed7fe7fbe949e8f1b45684d Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 18 Jul 2022 16:53:36 +0400 Subject: [PATCH 06/12] Try absolute position --- packages/base-styles/_z-index.scss | 1 - packages/block-library/src/template-part/editor.scss | 9 +++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/base-styles/_z-index.scss b/packages/base-styles/_z-index.scss index f5b6331e6fa589..76cfad0b424a1a 100644 --- a/packages/base-styles/_z-index.scss +++ b/packages/base-styles/_z-index.scss @@ -18,7 +18,6 @@ $z-layers: ( ".block-editor-inserter__tabs .components-tab-panel__tab-content": 0, // lower scrolling content ".block-editor-inserter__tabs .components-tab-panel__tabs": 1, // higher sticky element ".block-editor-inserter__search": 1, // higher sticky element - ".block-library-template-part__selection-search": 1, // higher sticky element // These next two share a stacking context ".interface-complementary-area .components-panel" : 0, // lower scrolling content diff --git a/packages/block-library/src/template-part/editor.scss b/packages/block-library/src/template-part/editor.scss index 11057806b9faf4..0aa4b83038ace1 100644 --- a/packages/block-library/src/template-part/editor.scss +++ b/packages/block-library/src/template-part/editor.scss @@ -19,8 +19,9 @@ .block-library-template-part__selection-search { background: $white; - padding-top: $grid-unit-20; - position: sticky; - top: 0; - z-index: z-index(".block-library-template-part__selection-search"); + position: absolute; + top: $grid-unit-20; + // Gap + close button size + header padding. + right: $grid-unit-10 + $button-size + $grid-unit-40; + z-index: z-index(".block-editor-template-part__selection-modal"); } From c7ebe8c7fa4407b96b51be076782a9428a250172 Mon Sep 17 00:00:00 2001 From: James Koster Date: Mon, 18 Jul 2022 14:33:16 +0100 Subject: [PATCH 07/12] Search input height --- packages/block-library/src/template-part/editor.scss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/block-library/src/template-part/editor.scss b/packages/block-library/src/template-part/editor.scss index 0aa4b83038ace1..a40457de2601db 100644 --- a/packages/block-library/src/template-part/editor.scss +++ b/packages/block-library/src/template-part/editor.scss @@ -24,4 +24,10 @@ // Gap + close button size + header padding. right: $grid-unit-10 + $button-size + $grid-unit-40; z-index: z-index(".block-editor-template-part__selection-modal"); + + .components-search-control { + input[type=search].components-search-control__input { + height: $grid-unit * 5.5; + } + } } From bfcc67142e9344eaff9aff5af31a5c3886845554 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Tue, 19 Jul 2022 09:58:10 +0400 Subject: [PATCH 08/12] Fix ESLint error --- packages/block-library/src/template-part/editor.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/template-part/editor.scss b/packages/block-library/src/template-part/editor.scss index a40457de2601db..67a8de64f598f1 100644 --- a/packages/block-library/src/template-part/editor.scss +++ b/packages/block-library/src/template-part/editor.scss @@ -26,7 +26,7 @@ z-index: z-index(".block-editor-template-part__selection-modal"); .components-search-control { - input[type=search].components-search-control__input { + input[type="search"].components-search-control__input { height: $grid-unit * 5.5; } } From a44c265ea5e914387622cb589581689c0d040559 Mon Sep 17 00:00:00 2001 From: James Koster Date: Tue, 19 Jul 2022 10:23:11 +0100 Subject: [PATCH 09/12] Search input width on small screens --- packages/block-library/src/template-part/editor.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/block-library/src/template-part/editor.scss b/packages/block-library/src/template-part/editor.scss index 67a8de64f598f1..30aa92d345c284 100644 --- a/packages/block-library/src/template-part/editor.scss +++ b/packages/block-library/src/template-part/editor.scss @@ -28,6 +28,11 @@ .components-search-control { input[type="search"].components-search-control__input { height: $grid-unit * 5.5; + width: $grid-unit * 16; + + @include break-small() { + width: auto; + } } } } From 2f1ee80e83ae87d4e397261399d4c59fd3c509b6 Mon Sep 17 00:00:00 2001 From: James Koster Date: Tue, 19 Jul 2022 10:39:33 +0100 Subject: [PATCH 10/12] Position search absolutely on larger screens only --- .../src/template-part/editor.scss | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/block-library/src/template-part/editor.scss b/packages/block-library/src/template-part/editor.scss index 30aa92d345c284..49b013cea5b53d 100644 --- a/packages/block-library/src/template-part/editor.scss +++ b/packages/block-library/src/template-part/editor.scss @@ -19,19 +19,23 @@ .block-library-template-part__selection-search { background: $white; - position: absolute; - top: $grid-unit-20; - // Gap + close button size + header padding. - right: $grid-unit-10 + $button-size + $grid-unit-40; z-index: z-index(".block-editor-template-part__selection-modal"); + position: sticky; + top: 0; + padding: $grid-unit-20 0; + + @include break-small() { + position: absolute; + top: $grid-unit-20; + // Gap + close button size + header padding. + right: $grid-unit-10 + $button-size + $grid-unit-40; + padding: 0; + } .components-search-control { input[type="search"].components-search-control__input { - height: $grid-unit * 5.5; - width: $grid-unit * 16; - @include break-small() { - width: auto; + height: $grid-unit * 5.5; } } } From 22f82785618c9bc1859b2d6e48c055e2a9f1ad0d Mon Sep 17 00:00:00 2001 From: James Koster Date: Tue, 19 Jul 2022 11:49:09 +0100 Subject: [PATCH 11/12] Remove absolute positioning --- .../block-library/src/template-part/editor.scss | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/packages/block-library/src/template-part/editor.scss b/packages/block-library/src/template-part/editor.scss index 49b013cea5b53d..622c0a741fe63e 100644 --- a/packages/block-library/src/template-part/editor.scss +++ b/packages/block-library/src/template-part/editor.scss @@ -23,20 +23,4 @@ position: sticky; top: 0; padding: $grid-unit-20 0; - - @include break-small() { - position: absolute; - top: $grid-unit-20; - // Gap + close button size + header padding. - right: $grid-unit-10 + $button-size + $grid-unit-40; - padding: 0; - } - - .components-search-control { - input[type="search"].components-search-control__input { - @include break-small() { - height: $grid-unit * 5.5; - } - } - } } From fa3bfca0cb0652793e1d810646dc7beb8c9c7340 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Tue, 19 Jul 2022 15:10:45 +0400 Subject: [PATCH 12/12] Use correct z-index --- packages/base-styles/_z-index.scss | 1 + packages/block-library/src/template-part/editor.scss | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/base-styles/_z-index.scss b/packages/base-styles/_z-index.scss index 76cfad0b424a1a..f5b6331e6fa589 100644 --- a/packages/base-styles/_z-index.scss +++ b/packages/base-styles/_z-index.scss @@ -18,6 +18,7 @@ $z-layers: ( ".block-editor-inserter__tabs .components-tab-panel__tab-content": 0, // lower scrolling content ".block-editor-inserter__tabs .components-tab-panel__tabs": 1, // higher sticky element ".block-editor-inserter__search": 1, // higher sticky element + ".block-library-template-part__selection-search": 1, // higher sticky element // These next two share a stacking context ".interface-complementary-area .components-panel" : 0, // lower scrolling content diff --git a/packages/block-library/src/template-part/editor.scss b/packages/block-library/src/template-part/editor.scss index 622c0a741fe63e..c17d37a6078e2d 100644 --- a/packages/block-library/src/template-part/editor.scss +++ b/packages/block-library/src/template-part/editor.scss @@ -19,8 +19,8 @@ .block-library-template-part__selection-search { background: $white; - z-index: z-index(".block-editor-template-part__selection-modal"); position: sticky; top: 0; padding: $grid-unit-20 0; + z-index: z-index(".block-library-template-part__selection-search"); }