11import { useState , useCallback , useRef , useEffect , useMemo } from 'react' ;
2- import { Link } from 'react-router-dom' ;
32import Box from '@mui/material/Box' ;
43import Chip from '@mui/material/Chip' ;
54import Menu from '@mui/material/Menu' ;
@@ -12,16 +11,14 @@ import Tooltip from '@mui/material/Tooltip';
1211import CloseIcon from '@mui/icons-material/Close' ;
1312import SearchIcon from '@mui/icons-material/Search' ;
1413import AddIcon from '@mui/icons-material/Add' ;
15- import ViewAgendaIcon from '@mui/icons-material/ViewAgenda' ;
16- import ViewModuleIcon from '@mui/icons-material/ViewModule' ;
17- import ListIcon from '@mui/icons-material/List' ;
1814import useMediaQuery from '@mui/material/useMediaQuery' ;
1915import { useTheme } from '@mui/material/styles' ;
2016
2117import type { FilterCategory , ActiveFilters , FilterCounts } from '../types' ;
2218import { FILTER_LABELS , FILTER_TOOLTIPS , FILTER_CATEGORIES } from '../types' ;
2319import type { ImageSize } from '../constants' ;
2420import { getAvailableValues , getAvailableValuesForGroup , getSearchResults , type SearchResult } from '../utils' ;
21+ import { ToolbarActions } from './ToolbarActions' ;
2522
2623interface FilterBarProps {
2724 activeFilters : ActiveFilters ;
@@ -250,8 +247,18 @@ export function FilterBar({
250247 return available . length > 0 ;
251248 } )
252249 . map ( ( cat ) => ( { type : 'category' as const , category : cat } ) ) ;
250+ } else if ( selectedCategory && ! hasQuery ) {
251+ // Category selected but no query - show all available values for this category
252+ const available = getAvailableValues ( filterCounts , activeFilters , selectedCategory ) ;
253+ return available . map ( ( [ value , count ] ) => ( {
254+ type : 'value' as const ,
255+ category : selectedCategory ,
256+ value,
257+ count,
258+ matchType : 'exact' as const ,
259+ } ) ) ;
253260 } else {
254- // Search results or category values
261+ // Search results (with query)
255262 return searchResults . map ( ( r ) => ( { type : 'value' as const , ...r } ) ) ;
256263 }
257264 } , [ selectedCategory , hasQuery , filterCounts , activeFilters , searchResults ] ) ;
@@ -348,73 +355,14 @@ export function FilterBar({
348355 { scrollPercent } % · { currentTotal }
349356 </ Typography >
350357 ) }
351- { /* Catalog icon + Grid size toggle - absolute right (desktop only) */ }
358+ { /* Toolbar actions - absolute right (desktop only) */ }
352359 { ! isMobile && (
353- < Box
354- sx = { {
355- position : 'absolute' ,
356- right : 0 ,
357- display : 'flex' ,
358- alignItems : 'center' ,
359- gap : 0.5 ,
360- } }
361- >
362- { /* Catalog icon */ }
363- < Tooltip title = "catalog" >
364- < Box
365- component = { Link }
366- to = "/catalog"
367- sx = { {
368- display : 'flex' ,
369- alignItems : 'center' ,
370- justifyContent : 'center' ,
371- width : 32 ,
372- height : 32 ,
373- color : '#9ca3af' ,
374- '&:hover' : { color : '#3776AB' } ,
375- } }
376- >
377- < ListIcon sx = { { fontSize : '1.25rem' } } />
378- </ Box >
379- </ Tooltip >
380- { /* Grid size toggle */ }
381- < Tooltip title = { imageSize === 'normal' ? 'compact view' : 'normal view' } >
382- < Box
383- role = "button"
384- tabIndex = { 0 }
385- aria-label = { imageSize === 'normal' ? 'Switch to compact view' : 'Switch to normal view' }
386- onClick = { ( ) => {
387- const newSize = imageSize === 'normal' ? 'compact' : 'normal' ;
388- onImageSizeChange ( newSize ) ;
389- onTrackEvent ( 'toggle_grid_size' , { size : newSize } ) ;
390- } }
391- onKeyDown = { ( e ) => {
392- if ( e . key === 'Enter' || e . key === ' ' ) {
393- e . preventDefault ( ) ;
394- const newSize = imageSize === 'normal' ? 'compact' : 'normal' ;
395- onImageSizeChange ( newSize ) ;
396- onTrackEvent ( 'toggle_grid_size' , { size : newSize } ) ;
397- }
398- } }
399- sx = { {
400- display : 'flex' ,
401- alignItems : 'center' ,
402- justifyContent : 'center' ,
403- width : 32 ,
404- height : 32 ,
405- cursor : 'pointer' ,
406- color : '#9ca3af' ,
407- '&:hover' : { color : '#3776AB' } ,
408- '&:focus' : { outline : '2px solid #3776AB' , outlineOffset : 2 } ,
409- } }
410- >
411- { imageSize === 'normal' ? (
412- < ViewAgendaIcon sx = { { fontSize : '1.25rem' } } />
413- ) : (
414- < ViewModuleIcon sx = { { fontSize : '1.25rem' } } />
415- ) }
416- </ Box >
417- </ Tooltip >
360+ < Box sx = { { position : 'absolute' , right : 0 } } >
361+ < ToolbarActions
362+ imageSize = { imageSize }
363+ onImageSizeChange = { onImageSizeChange }
364+ onTrackEvent = { onTrackEvent }
365+ />
418366 </ Box >
419367 ) }
420368 { /* Active filter chips */ }
@@ -594,64 +542,11 @@ export function FilterBar({
594542 ) : (
595543 < Box />
596544 ) }
597- < Box sx = { { display : 'flex' , alignItems : 'center' , gap : 0.5 } } >
598- { /* Catalog icon */ }
599- < Tooltip title = "catalog" >
600- < Box
601- component = { Link }
602- to = "/catalog"
603- sx = { {
604- display : 'flex' ,
605- alignItems : 'center' ,
606- justifyContent : 'center' ,
607- width : 32 ,
608- height : 32 ,
609- color : '#9ca3af' ,
610- '&:hover' : { color : '#3776AB' } ,
611- } }
612- >
613- < ListIcon sx = { { fontSize : '1.25rem' } } />
614- </ Box >
615- </ Tooltip >
616- { /* Grid size toggle */ }
617- < Tooltip title = { imageSize === 'normal' ? 'compact view' : 'normal view' } >
618- < Box
619- role = "button"
620- tabIndex = { 0 }
621- aria-label = { imageSize === 'normal' ? 'Switch to compact view' : 'Switch to normal view' }
622- onClick = { ( ) => {
623- const newSize = imageSize === 'normal' ? 'compact' : 'normal' ;
624- onImageSizeChange ( newSize ) ;
625- onTrackEvent ( 'toggle_grid_size' , { size : newSize } ) ;
626- } }
627- onKeyDown = { ( e ) => {
628- if ( e . key === 'Enter' || e . key === ' ' ) {
629- e . preventDefault ( ) ;
630- const newSize = imageSize === 'normal' ? 'compact' : 'normal' ;
631- onImageSizeChange ( newSize ) ;
632- onTrackEvent ( 'toggle_grid_size' , { size : newSize } ) ;
633- }
634- } }
635- sx = { {
636- display : 'flex' ,
637- alignItems : 'center' ,
638- justifyContent : 'center' ,
639- width : 32 ,
640- height : 32 ,
641- cursor : 'pointer' ,
642- color : '#9ca3af' ,
643- '&:hover' : { color : '#3776AB' } ,
644- '&:focus' : { outline : '2px solid #3776AB' , outlineOffset : 2 } ,
645- } }
646- >
647- { imageSize === 'normal' ? (
648- < ViewAgendaIcon sx = { { fontSize : '1.25rem' } } />
649- ) : (
650- < ViewModuleIcon sx = { { fontSize : '1.25rem' } } />
651- ) }
652- </ Box >
653- </ Tooltip >
654- </ Box >
545+ < ToolbarActions
546+ imageSize = { imageSize }
547+ onImageSizeChange = { onImageSizeChange }
548+ onTrackEvent = { onTrackEvent }
549+ />
655550 </ Box >
656551 ) }
657552
@@ -734,85 +629,97 @@ export function FilterBar({
734629 < Divider key = "divider" /> ,
735630 ]
736631 : [ ] ) ,
737- ...( searchResults . length > 0
738- ? ( ( ) => {
739- // Split results into exact and fuzzy matches
740- const exactResults = searchResults . filter ( ( r ) => r . matchType === 'exact' ) ;
741- const fuzzyResults = searchResults . filter ( ( r ) => r . matchType === 'fuzzy' ) ;
742-
743- const renderMenuItem = ( result : SearchResult , idx : number ) => {
744- const { category, value, count } = result ;
745- const specTitle = category === 'spec' ? specTitles [ value ] : undefined ;
746- const menuItem = (
747- < MenuItem
748- key = { `${ category } -${ value } ` }
749- onClick = { ( ) => handleValueSelect ( category , value ) }
750- selected = { idx === highlightedIndex }
751- sx = { { fontFamily : '"MonoLisa", "MonoLisa Fallback", monospace' } }
632+ ...( ( ( ) => {
633+ // Use searchResults if query exists, otherwise show all available values for selected category
634+ const resultsToShow : SearchResult [ ] = hasQuery
635+ ? searchResults
636+ : selectedCategory
637+ ? getAvailableValues ( filterCounts , activeFilters , selectedCategory ) . map ( ( [ value , count ] ) => ( {
638+ category : selectedCategory ,
639+ value,
640+ count,
641+ matchType : 'exact' as const ,
642+ } ) )
643+ : [ ] ;
644+
645+ if ( resultsToShow . length > 0 ) {
646+ // Split results into exact and fuzzy matches
647+ const exactResults = resultsToShow . filter ( ( r ) => r . matchType === 'exact' ) ;
648+ const fuzzyResults = resultsToShow . filter ( ( r ) => r . matchType === 'fuzzy' ) ;
649+
650+ const renderMenuItem = ( result : SearchResult , idx : number ) => {
651+ const { category, value, count } = result ;
652+ const specTitle = category === 'spec' ? specTitles [ value ] : undefined ;
653+ const menuItem = (
654+ < MenuItem
655+ key = { `${ category } -${ value } ` }
656+ onClick = { ( ) => handleValueSelect ( category , value ) }
657+ selected = { idx === highlightedIndex }
658+ sx = { { fontFamily : '"MonoLisa", "MonoLisa Fallback", monospace' } }
659+ >
660+ < ListItemText
661+ primary = { value }
662+ secondary = { ! selectedCategory ? FILTER_LABELS [ category ] : undefined }
663+ primaryTypographyProps = { {
664+ fontFamily : '"MonoLisa", "MonoLisa Fallback", monospace' ,
665+ fontSize : '0.85rem' ,
666+ } }
667+ secondaryTypographyProps = { {
668+ fontFamily : '"MonoLisa", "MonoLisa Fallback", monospace' ,
669+ fontSize : '0.7rem' ,
670+ color : '#9ca3af' ,
671+ } }
672+ />
673+ < Typography
674+ sx = { {
675+ fontFamily : '"MonoLisa", "MonoLisa Fallback", monospace' ,
676+ fontSize : '0.75rem' ,
677+ color : '#9ca3af' ,
678+ ml : 2 ,
679+ } }
752680 >
753- < ListItemText
754- primary = { value }
755- secondary = { ! selectedCategory ? FILTER_LABELS [ category ] : undefined }
756- primaryTypographyProps = { {
757- fontFamily : '"MonoLisa", "MonoLisa Fallback", monospace' ,
758- fontSize : '0.85rem' ,
759- } }
760- secondaryTypographyProps = { {
761- fontFamily : '"MonoLisa", "MonoLisa Fallback", monospace' ,
762- fontSize : '0.7rem' ,
763- color : '#9ca3af' ,
764- } }
765- />
766- < Typography
767- sx = { {
768- fontFamily : '"MonoLisa", "MonoLisa Fallback", monospace' ,
769- fontSize : '0.75rem' ,
770- color : '#9ca3af' ,
771- ml : 2 ,
772- } }
773- >
774- ({ count } )
775- </ Typography >
776- </ MenuItem >
777- ) ;
778- return specTitle ? (
779- < Tooltip key = { `${ category } -${ value } ` } title = { specTitle } placement = "right" arrow >
780- < span > { menuItem } </ span >
781- </ Tooltip >
782- ) : (
783- menuItem
784- ) ;
785- } ;
786-
787- const items : React . ReactNode [ ] = [ ] ;
788- // Add exact matches
789- exactResults . forEach ( ( result , i ) => {
790- items . push ( renderMenuItem ( result , i ) ) ;
791- } ) ;
792- // Add fuzzy label/divider if there are fuzzy results
793- if ( fuzzyResults . length > 0 ) {
794- items . push (
795- < Divider key = "exact-fuzzy-divider" sx = { { my : 0.5 } } >
796- < Typography
797- sx = { {
798- fontSize : '0.65rem' ,
799- color : '#9ca3af' ,
800- fontFamily : '"MonoLisa", "MonoLisa Fallback", monospace' ,
801- px : 1 ,
802- } }
803- >
804- fuzzy
805- </ Typography >
806- </ Divider >
807- ) ;
808- }
809- // Add fuzzy matches
810- fuzzyResults . forEach ( ( result , i ) => {
811- items . push ( renderMenuItem ( result , exactResults . length + i ) ) ;
812- } ) ;
813- return items ;
814- } ) ( )
815- : [
681+ ({ count } )
682+ </ Typography >
683+ </ MenuItem >
684+ ) ;
685+ return specTitle ? (
686+ < Tooltip key = { `${ category } -${ value } ` } title = { specTitle } placement = "right" arrow >
687+ < span > { menuItem } </ span >
688+ </ Tooltip >
689+ ) : (
690+ menuItem
691+ ) ;
692+ } ;
693+
694+ const items : React . ReactNode [ ] = [ ] ;
695+ // Add exact matches
696+ exactResults . forEach ( ( result , i ) => {
697+ items . push ( renderMenuItem ( result , i ) ) ;
698+ } ) ;
699+ // Add fuzzy label/divider if there are fuzzy results
700+ if ( fuzzyResults . length > 0 ) {
701+ items . push (
702+ < Divider key = "exact-fuzzy-divider" sx = { { my : 0.5 } } >
703+ < Typography
704+ sx = { {
705+ fontSize : '0.65rem' ,
706+ color : '#9ca3af' ,
707+ fontFamily : '"MonoLisa", "MonoLisa Fallback", monospace' ,
708+ px : 1 ,
709+ } }
710+ >
711+ fuzzy
712+ </ Typography >
713+ </ Divider >
714+ ) ;
715+ }
716+ // Add fuzzy matches
717+ fuzzyResults . forEach ( ( result , i ) => {
718+ items . push ( renderMenuItem ( result , exactResults . length + i ) ) ;
719+ } ) ;
720+ return items ;
721+ } else {
722+ return [
816723 < MenuItem key = "no-results" disabled >
817724 < Typography
818725 sx = { {
@@ -824,7 +731,9 @@ export function FilterBar({
824731 no matches
825732 </ Typography >
826733 </ MenuItem > ,
827- ] ) ,
734+ ] ;
735+ }
736+ } ) ( ) ) ,
828737 ] }
829738 </ Menu >
830739
0 commit comments