From d16c616c1b25f9a9d2f02bab03eacecc672ded82 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 18 Jul 2022 14:33:30 +0400 Subject: [PATCH] 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 ); }