Skip to content

Commit eef1cb8

Browse files
committed
fix: vocab page filtered entries
1 parent 4fefc70 commit eef1cb8

File tree

1 file changed

+133
-130
lines changed

1 file changed

+133
-130
lines changed

src/pages/VocabPage.jsx

Lines changed: 133 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
XCircleIcon,
77
ArrowUpRightIcon,
88
} from '@phosphor-icons/react';
9-
import { motion, AnimatePresence } from 'framer-motion';
9+
import { motion } from 'framer-motion';
1010
import { vocabulary } from '../data/vocabulary';
1111
import Seo from '../components/Seo';
1212
import { useSidePanel } from '../context/SidePanelContext';
@@ -75,10 +75,14 @@ const VocabPage = () => {
7575
})).sort((a, b) => a.title.localeCompare(b.title)),
7676
[]);
7777

78-
const filteredEntries = vocabEntries.filter((entry) =>
79-
entry.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
80-
entry.slug.toLowerCase().includes(searchQuery.toLowerCase())
81-
);
78+
const filteredEntries = useMemo(() => {
79+
const query = searchQuery.toLowerCase().trim();
80+
if (!query) return vocabEntries;
81+
return vocabEntries.filter((entry) =>
82+
entry.title.toLowerCase().includes(query) ||
83+
entry.slug.toLowerCase().includes(query)
84+
);
85+
}, [vocabEntries, searchQuery]);
8286

8387
const groupedEntries = useMemo(() => {
8488
const groups = {};
@@ -90,7 +94,7 @@ const VocabPage = () => {
9094
return groups;
9195
}, [filteredEntries]);
9296

93-
const alphabet = Object.keys(groupedEntries).sort();
97+
const alphabet = useMemo(() => Object.keys(groupedEntries).sort(), [groupedEntries]);
9498

9599
const handleOpenVocab = (entry) => {
96100
const LazyComponent = React.lazy(entry.loader);
@@ -113,134 +117,133 @@ const VocabPage = () => {
113117
}
114118
};
115119

116-
return (
117-
<div className="min-h-screen bg-[#050505] text-[#f4f4f4] selection:bg-emerald-500/30 font-sans">
118-
<Seo
119-
title="Glossary | Fezcodex"
120-
description="A dictionary of technical concepts and patterns."
121-
keywords={['Fezcodex', 'vocabulary', 'glossary', 'definitions']}
122-
/>
123-
124-
{/* Bauhaus Grid Background Pattern */}
125-
<div className="fixed inset-0 pointer-events-none opacity-[0.02] z-0"
126-
style={{
127-
backgroundImage: 'linear-gradient(#ffffff 1px, transparent 1px), linear-gradient(90deg, #ffffff 1px, transparent 1px)',
128-
backgroundSize: '40px 40px'
129-
}}
130-
/>
131-
<div className="relative z-10 mx-auto max-w-7xl px-6 py-24 md:px-12">
132-
{/* Header Section */}
133-
<header className="mb-24 flex flex-col items-start">
134-
<Link
135-
to="/"
136-
className="mb-12 inline-flex items-center gap-3 text-md font-instr-sans text-gray-500 hover:text-white transition-colors"
137-
>
138-
<ArrowLeftIcon size={16} />
139-
<span>Fezcodex Index</span>
140-
</Link>
141-
142-
<h1 className="text-7xl md:text-9xl font-instr-serif italic tracking-tight mb-6 text-white">
143-
Glossary
144-
</h1>
145-
<p className="text-xl font-light text-gray-400 max-w-2xl font-instr-sans">
146-
A curated collection of technical concepts, design patterns, and terminology.
147-
</p>
148-
</header>
149-
150-
{/* Sticky Search & Nav */}
151-
<div className="sticky top-6 z-30 mb-20">
152-
<div className="bg-[#0a0a0a]/80 backdrop-blur-xl border border-white/10 shadow-lg rounded-2xl p-2 flex flex-col md:flex-row items-center gap-4">
153-
<div className="relative w-full md:w-96">
154-
<MagnifyingGlassIcon size={18} className="absolute left-4 top-1/2 -translate-y-1/2 text-gray-500" />
155-
<input
156-
type="text"
157-
placeholder="Search terms..."
158-
value={searchQuery}
159-
onChange={(e) => setSearchQuery(e.target.value)}
160-
className="w-full bg-transparent text-lg font-instr-sans placeholder-gray-600 focus:outline-none py-3 pl-12 pr-4 text-white"
161-
/>
162-
{searchQuery && (
163-
<button onClick={() => setSearchQuery('')} className="absolute right-4 top-1/2 -translate-y-1/2 text-gray-500 hover:text-red-500">
164-
<XCircleIcon size={18} weight="fill" />
165-
</button>
166-
)}
167-
</div>
168-
169-
<div className="h-8 w-px bg-white/10 hidden md:block" />
170-
171-
<div className="flex flex-wrap gap-1 justify-center md:justify-start px-2 py-2 md:py-0 w-full overflow-x-auto no-scrollbar">
172-
{alphabet.map(letter => (
173-
<button
174-
key={letter}
175-
onClick={() => scrollToLetter(letter)}
176-
className="w-8 h-8 flex items-center justify-center rounded-full text-xs font-bold text-gray-500 hover:bg-white hover:text-black transition-all font-mono"
177-
>
178-
{letter}
120+
return (
121+
<div className="min-h-screen bg-[#050505] text-[#f4f4f4] selection:bg-emerald-500/30 font-sans relative overflow-x-hidden">
122+
<Seo
123+
title="Glossary | Fezcodex"
124+
description="A dictionary of technical concepts and patterns."
125+
keywords={['Fezcodex', 'vocabulary', 'glossary', 'definitions']}
126+
/>
127+
128+
{/* Bauhaus Grid Background Pattern */}
129+
<div className="fixed inset-0 pointer-events-none opacity-[0.02] z-0"
130+
style={{
131+
backgroundImage: 'linear-gradient(#ffffff 1px, transparent 1px), linear-gradient(90deg, #ffffff 1px, transparent 1px)',
132+
backgroundSize: '40px 40px'
133+
}}
134+
/>
135+
136+
<div className="relative z-10 mx-auto max-w-7xl px-6 py-24 md:px-12 flex flex-col">
137+
{/* Header Section */}
138+
<header className="mb-24 flex flex-col items-start shrink-0">
139+
<Link
140+
to="/"
141+
className="mb-12 inline-flex items-center gap-3 text-md font-instr-sans text-gray-500 hover:text-white transition-colors"
142+
>
143+
<ArrowLeftIcon size={16} />
144+
<span>Fezcodex Index</span>
145+
</Link>
146+
147+
<h1 className="text-7xl md:text-9xl font-instr-serif italic tracking-tight mb-6 text-white leading-none">
148+
Glossary
149+
</h1>
150+
<p className="text-xl font-light text-gray-400 max-w-2xl font-instr-sans">
151+
A curated collection of technical concepts, design patterns, and terminology.
152+
</p>
153+
</header>
154+
155+
{/* Sticky Search & Nav */}
156+
<div className="sticky top-6 z-30 mb-20 shrink-0">
157+
<div className="bg-[#0a0a0a]/80 backdrop-blur-xl border border-white/10 shadow-2xl rounded-2xl p-2 flex flex-col md:flex-row items-center gap-4">
158+
<div className="relative w-full md:w-96">
159+
<MagnifyingGlassIcon size={18} className="absolute left-4 top-1/2 -translate-y-1/2 text-gray-500" />
160+
<input
161+
type="text"
162+
placeholder="Search terms..."
163+
value={searchQuery}
164+
onChange={(e) => setSearchQuery(e.target.value)}
165+
className="w-full bg-transparent text-lg font-instr-sans placeholder-gray-600 focus:outline-none py-3 pl-12 pr-4 text-white"
166+
/>
167+
{searchQuery && (
168+
<button onClick={() => setSearchQuery('')} className="absolute right-4 top-1/2 -translate-y-1/2 text-gray-500 hover:text-red-500">
169+
<XCircleIcon size={18} weight="fill" />
179170
</button>
180-
))}
181-
</div>
182-
</div>
183-
</div>
184-
185-
{/* Content */}
186-
<div className="pb-48 space-y-20">
187-
<AnimatePresence mode="popLayout">
188-
{alphabet.map((letter) => (
189-
<motion.section
190-
key={letter}
191-
id={`letter-${letter}`}
192-
initial={{ opacity: 0, y: 20 }}
193-
whileInView={{ opacity: 1, y: 0 }}
194-
viewport={{ once: true, margin: "-100px" }}
195-
transition={{ duration: 0.5, ease: "easeOut" }}
196-
>
197-
<div className="flex items-baseline gap-6 mb-10 border-b border-white/10 pb-4">
198-
<h2 className="text-6xl font-instr-serif italic text-white/20">{letter}</h2>
199-
</div>
200-
201-
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-8 gap-y-8">
202-
{groupedEntries[letter].map((entry) => (
203-
<motion.button
204-
key={entry.slug}
205-
layout
206-
onClick={() => handleOpenVocab(entry)}
207-
className="group flex flex-col text-left p-8 bg-[#0a0a0a] border border-white/5 hover:border-white/20 hover:shadow-2xl hover:shadow-emerald-900/10 hover:-translate-y-1 transition-all duration-300 rounded-xl relative overflow-hidden"
208-
>
209-
{/* Procedural Bauhaus Background */}
210-
<div className="absolute inset-0 opacity-40 group-hover:opacity-100 transition-opacity duration-700">
211-
<BauhausShapes seed={entry.slug} />
212-
</div>
213-
214-
{/* Decorative Bauhaus Shape Overlay */}
215-
<div className="absolute top-0 right-0 w-24 h-24 bg-gradient-to-bl from-white/5 to-transparent rounded-bl-full opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
216-
217-
<div className="flex justify-between items-start w-full mb-6 relative z-10"> <span className="font-mono text-[10px] text-gray-500 uppercase tracking-widest group-hover:text-emerald-400 transition-colors">
218-
{entry.slug}
219-
</span>
220-
<ArrowUpRightIcon
221-
size={18}
222-
className="text-gray-600 group-hover:text-white transition-colors"
223-
/>
224-
</div>
225-
226-
<h3 className="text-2xl font-instr-serif text-white mb-3 group-hover:underline decoration-1 underline-offset-4 decoration-white/30 relative z-10">
227-
{entry.title}
228-
</h3>
229-
</motion.button>
171+
)}
172+
</div>
173+
174+
<div className="h-8 w-px bg-white/10 hidden md:block" />
175+
176+
<div className="flex flex-wrap gap-1 justify-center md:justify-start px-2 py-2 md:py-0 w-full overflow-x-auto no-scrollbar">
177+
{alphabet.map(letter => (
178+
<button
179+
key={letter}
180+
onClick={() => scrollToLetter(letter)}
181+
className="w-8 h-8 flex items-center justify-center rounded-full text-xs font-bold text-gray-500 hover:bg-white hover:text-black transition-all font-mono"
182+
>
183+
{letter}
184+
</button>
230185
))}
231-
</div>
232-
</motion.section>
233-
))}
234-
</AnimatePresence>
235-
236-
{filteredEntries.length === 0 && (
237-
<div className="py-32 text-center">
238-
<p className="font-instr-serif italic text-2xl text-gray-600">No definitions found for "{searchQuery}"</p>
186+
</div>
187+
</div>
188+
</div>
189+
190+
{/* Content Container - Shrinks with content */}
191+
<div className="flex-1 space-y-24 mb-32">
192+
{alphabet.map((letter) => (
193+
<motion.section
194+
key={letter}
195+
id={`letter-${letter}`}
196+
initial={{ opacity: 0 }}
197+
animate={{ opacity: 1 }}
198+
className="scroll-mt-40"
199+
>
200+
<div className="flex items-baseline gap-6 mb-10 border-b border-white/10 pb-4">
201+
<h2 className="text-6xl font-instr-serif italic text-white/20">{letter}</h2>
239202
</div>
240-
)}
241-
</div>
203+
204+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
205+
{groupedEntries[letter].map((entry) => (
206+
<button
207+
key={entry.slug}
208+
onClick={() => handleOpenVocab(entry)}
209+
className="group flex flex-col text-left p-8 bg-[#0a0a0a] border border-white/5 hover:border-white/20 hover:shadow-2xl hover:shadow-emerald-900/10 transition-all duration-300 rounded-xl relative overflow-hidden h-full"
210+
>
211+
{/* Procedural Bauhaus Background */}
212+
<div className="absolute inset-0 opacity-40 group-hover:opacity-100 transition-opacity duration-700 pointer-events-none">
213+
<BauhausShapes seed={entry.slug} />
214+
</div>
215+
216+
{/* Decorative Bauhaus Shape Overlay */}
217+
<div className="absolute top-0 right-0 w-24 h-24 bg-gradient-to-bl from-white/5 to-transparent rounded-bl-full opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
218+
219+
<div className="flex justify-between items-start w-full mb-6 relative z-10">
220+
<span className="font-mono text-[10px] text-gray-500 uppercase tracking-widest group-hover:text-emerald-400 transition-colors">
221+
{entry.slug}
222+
</span>
223+
<ArrowUpRightIcon
224+
size={18}
225+
className="text-gray-600 group-hover:text-white transition-colors"
226+
/>
227+
</div>
228+
229+
<h3 className="text-2xl font-instr-serif text-white mb-3 group-hover:underline decoration-1 underline-offset-4 decoration-white/30 relative z-10">
230+
{entry.title}
231+
</h3>
232+
</button>
233+
))}
234+
</div>
235+
</motion.section>
236+
))}
237+
238+
{filteredEntries.length === 0 && (
239+
<div className="py-32 text-center">
240+
<p className="font-instr-serif italic text-2xl text-gray-600">No definitions found for "{searchQuery}"</p>
241+
</div>
242+
)}
242243
</div>
243244
</div>
244-
);};
245+
</div>
246+
);
247+
};
245248

246249
export default VocabPage;

0 commit comments

Comments
 (0)