Skip to content

Commit 07378a7

Browse files
Zsbyqx20claude
andcommitted
feat(gallery): add active filter chips with dismissible UI
Replace redundant sidebar clear button with interactive filter chips that show active filters for search, platform, difficulty, and tags. Each chip can be individually dismissed or all filters can be cleared at once. Add results counter showing filtered vs total environments. Improve UX by making filter state more visible and accessible while maintaining consistent newspaper editorial design system. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent a6ffeea commit 07378a7

1 file changed

Lines changed: 135 additions & 13 deletions

File tree

src/pages/Gallery.tsx

Lines changed: 135 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -489,23 +489,11 @@ const Gallery: React.FC = () => {
489489
<input
490490
type="text"
491491
placeholder="Search environments..."
492-
className="w-full px-4 py-2 mb-4 border-2 border-gray-300 text-sm focus:outline-none focus:border-gray-400"
492+
className="w-full px-4 py-2 mb-6 border-2 border-gray-300 text-sm focus:outline-none focus:border-gray-400"
493493
value={searchInput}
494494
onChange={e => setSearchInput(e.target.value)}
495495
/>
496496

497-
<button
498-
onClick={() => {
499-
setSelectedPlatform('all');
500-
setSelectedDifficulty('all');
501-
setSearchInput('');
502-
setSelectedTags([]);
503-
}}
504-
className="w-full px-4 py-2 border-2 border-gray-300 text-gray-700 text-sm font-bold uppercase tracking-wide hover:bg-gray-100 transition-colors mb-6"
505-
>
506-
Clear All Filters
507-
</button>
508-
509497
<div className="space-y-6">
510498
<div>
511499
<h3 className="text-sm font-bold uppercase text-gray-700 mb-3">
@@ -604,6 +592,140 @@ const Gallery: React.FC = () => {
604592

605593
{/* Right Content Area */}
606594
<div className="flex-1">
595+
{/* Active Filter Chips */}
596+
{(selectedPlatform !== 'all' ||
597+
selectedDifficulty !== 'all' ||
598+
debouncedSearch ||
599+
selectedTags.length > 0) && (
600+
<div className="mb-6 bg-gray-50 border-2 border-gray-300 p-4">
601+
<div className="flex flex-wrap items-center gap-2">
602+
<span className="text-xs font-bold uppercase text-gray-700 tracking-wide">
603+
Active Filters:
604+
</span>
605+
606+
{/* Search chip */}
607+
{debouncedSearch && (
608+
<button
609+
onClick={() => setSearchInput('')}
610+
className="inline-flex items-center px-3 py-1 bg-gray-900 text-white text-xs font-medium uppercase tracking-wide hover:bg-gray-800 transition-colors group"
611+
>
612+
<span className="mr-2">
613+
Search: &quot;{debouncedSearch}&quot;
614+
</span>
615+
<svg
616+
className="w-3 h-3 group-hover:scale-110 transition-transform"
617+
fill="none"
618+
stroke="currentColor"
619+
viewBox="0 0 24 24"
620+
>
621+
<path
622+
strokeLinecap="round"
623+
strokeLinejoin="round"
624+
strokeWidth="2"
625+
d="M6 18L18 6M6 6l12 12"
626+
/>
627+
</svg>
628+
</button>
629+
)}
630+
631+
{/* Platform chip */}
632+
{selectedPlatform !== 'all' && (
633+
<button
634+
onClick={() => setSelectedPlatform('all')}
635+
className="inline-flex items-center px-3 py-1 bg-gray-900 text-white text-xs font-medium uppercase tracking-wide hover:bg-gray-800 transition-colors group"
636+
>
637+
<span className="mr-2">
638+
Platform: {selectedPlatform}
639+
</span>
640+
<svg
641+
className="w-3 h-3 group-hover:scale-110 transition-transform"
642+
fill="none"
643+
stroke="currentColor"
644+
viewBox="0 0 24 24"
645+
>
646+
<path
647+
strokeLinecap="round"
648+
strokeLinejoin="round"
649+
strokeWidth="2"
650+
d="M6 18L18 6M6 6l12 12"
651+
/>
652+
</svg>
653+
</button>
654+
)}
655+
656+
{/* Difficulty chip */}
657+
{selectedDifficulty !== 'all' && (
658+
<button
659+
onClick={() => setSelectedDifficulty('all')}
660+
className="inline-flex items-center px-3 py-1 bg-gray-900 text-white text-xs font-medium uppercase tracking-wide hover:bg-gray-800 transition-colors group"
661+
>
662+
<span className="mr-2">
663+
Difficulty: {selectedDifficulty}
664+
</span>
665+
<svg
666+
className="w-3 h-3 group-hover:scale-110 transition-transform"
667+
fill="none"
668+
stroke="currentColor"
669+
viewBox="0 0 24 24"
670+
>
671+
<path
672+
strokeLinecap="round"
673+
strokeLinejoin="round"
674+
strokeWidth="2"
675+
d="M6 18L18 6M6 6l12 12"
676+
/>
677+
</svg>
678+
</button>
679+
)}
680+
681+
{/* Tag chips */}
682+
{selectedTags.map(tag => (
683+
<button
684+
key={tag}
685+
onClick={() => toggleTag(tag)}
686+
className="inline-flex items-center px-3 py-1 bg-gray-900 text-white text-xs font-medium uppercase tracking-wide hover:bg-gray-800 transition-colors group"
687+
>
688+
<span className="mr-2">Tag: {tag}</span>
689+
<svg
690+
className="w-3 h-3 group-hover:scale-110 transition-transform"
691+
fill="none"
692+
stroke="currentColor"
693+
viewBox="0 0 24 24"
694+
>
695+
<path
696+
strokeLinecap="round"
697+
strokeLinejoin="round"
698+
strokeWidth="2"
699+
d="M6 18L18 6M6 6l12 12"
700+
/>
701+
</svg>
702+
</button>
703+
))}
704+
705+
{/* Clear all button */}
706+
<button
707+
onClick={() => {
708+
setSelectedPlatform('all');
709+
setSelectedDifficulty('all');
710+
setSearchInput('');
711+
setSelectedTags([]);
712+
}}
713+
className="inline-flex items-center px-3 py-1 border-2 border-gray-800 text-gray-800 text-xs font-bold uppercase tracking-wide hover:bg-gray-100 transition-colors ml-auto"
714+
>
715+
Clear All
716+
</button>
717+
</div>
718+
719+
{/* Results count */}
720+
<div className="mt-3 pt-3 border-t border-gray-300">
721+
<span className="text-sm font-semibold text-gray-700">
722+
Showing {filteredAndPaginatedEnvironments.totalCount} of{' '}
723+
{environmentsWithIcons.length} environments
724+
</span>
725+
</div>
726+
</div>
727+
)}
728+
607729
{/* Environment List - Newspaper Style */}
608730
<div className="space-y-4 md:space-y-6">
609731
{filteredAndPaginatedEnvironments.items.map(

0 commit comments

Comments
 (0)