@@ -2,17 +2,20 @@ import {
22 __experimentalVStack as VStack ,
33 __experimentalHeading as Heading ,
44 __experimentalText as Text ,
5+ SearchControl as SearchControlWp ,
56 Spinner ,
67} from '@wordpress/components' ;
78import { useI18n } from '@wordpress/react-i18n' ;
8- import { useCallback } from 'react' ;
9+ import { useCallback , useMemo , useState } from 'react' ;
910import { ArrowIcon } from 'src/components/arrow-icon' ;
1011import StudioButton from 'src/components/button' ;
1112import { EMPTY_SITE_PLAYGROUND_URL } from 'src/constants' ;
1213import { cx } from 'src/lib/cx' ;
1314import { getIpcApi } from 'src/lib/get-ipc-api' ;
1415import { GalleryBlueprint } from 'src/stores/gallery-blueprints-api' ;
1516
17+ const SearchControl = process . env . NODE_ENV === 'test' ? ( ) => null : SearchControlWp ;
18+
1619interface Blueprint {
1720 slug : string ;
1821 title : string ;
@@ -279,6 +282,22 @@ export function NewSiteOptions( {
279282 gallerySelectionError,
280283} : NewSiteOptionsProps ) {
281284 const { __ } = useI18n ( ) ;
285+ const [ searchQuery , setSearchQuery ] = useState ( '' ) ;
286+
287+ const filteredGalleryBlueprints = useMemo ( ( ) => {
288+ const query = searchQuery . toLowerCase ( ) . trim ( ) ;
289+ if ( ! query ) {
290+ return galleryBlueprints ;
291+ }
292+ return galleryBlueprints . filter ( ( blueprint ) => {
293+ const titleMatch = blueprint . title . toLowerCase ( ) . includes ( query ) ;
294+ const descriptionMatch = blueprint . description . toLowerCase ( ) . includes ( query ) ;
295+ const categoryMatch = blueprint . categories . some ( ( category ) =>
296+ category . toLowerCase ( ) . includes ( query )
297+ ) ;
298+ return titleMatch || descriptionMatch || categoryMatch ;
299+ } ) ;
300+ } , [ galleryBlueprints , searchQuery ] ) ;
282301
283302 const handleEmptyClick = useCallback ( ( ) => {
284303 onBlueprintChange ( 'empty' ) ;
@@ -335,12 +354,20 @@ export function NewSiteOptions( {
335354
336355 { onGalleryBlueprintSelect && (
337356 < >
338- < Heading
339- className = "text-[18px] text-frame-text mt-8 mb-4 w-full max-w-2xl mx-auto"
340- weight = { 500 }
341- >
342- { __ ( 'Explore more blueprints' ) }
343- </ Heading >
357+ < div className = "flex items-center justify-between w-full max-w-2xl mx-auto mt-8 mb-4" >
358+ < Heading className = "text-[18px] text-frame-text" weight = { 500 } >
359+ { __ ( 'Explore more blueprints' ) }
360+ </ Heading >
361+ { ! isLoadingGallery && ! galleryErrorMessage && (
362+ < SearchControl
363+ className = "!w-48 text-frame-text"
364+ placeholder = { __ ( 'Search blueprints' ) }
365+ onChange = { setSearchQuery }
366+ value = { searchQuery }
367+ __nextHasNoMarginBottom = { true }
368+ />
369+ ) }
370+ </ div >
344371
345372 { gallerySelectionError && (
346373 < div className = "bg-red-50 dark:bg-red-900/20 text-red-800 dark:text-red-200 text-sm rounded-lg px-4 py-3 mb-4 max-w-2xl mx-auto w-full" >
@@ -356,13 +383,17 @@ export function NewSiteOptions( {
356383 < div className = "flex items-center justify-center text-sm text-frame-text-secondary py-8" >
357384 { galleryErrorMessage }
358385 </ div >
386+ ) : filteredGalleryBlueprints . length === 0 ? (
387+ < div className = "flex items-center justify-center text-sm text-frame-text-secondary py-8 max-w-2xl mx-auto" >
388+ { __ ( 'No blueprints found.' ) }
389+ </ div >
359390 ) : (
360391 < div className = "grid grid-cols-1 sm:grid-cols-2 gap-4 w-full max-w-2xl mx-auto pb-1" >
361- { galleryBlueprints . map ( ( bp ) => (
392+ { filteredGalleryBlueprints . map ( ( blueprint ) => (
362393 < GalleryBlueprintCard
363- key = { bp . slug }
364- blueprint = { bp }
365- onClick = { ( ) => onGalleryBlueprintSelect ( bp ) }
394+ key = { blueprint . slug }
395+ blueprint = { blueprint }
396+ onClick = { ( ) => onGalleryBlueprintSelect ( blueprint ) }
366397 disabled = { isSelectingGalleryBlueprint }
367398 />
368399 ) ) }
0 commit comments