Skip to content

Commit 6290f89

Browse files
feat: enhance catalog navigation and improve layout
- Added catalog link to FilterBar and SpecPage for easier navigation - Implemented breadcrumb navigation in CatalogPage - Enhanced layout responsiveness and styling across components
1 parent 974ed57 commit 6290f89

File tree

5 files changed

+160
-89
lines changed

5 files changed

+160
-89
lines changed

app/src/components/FilterBar.tsx

Lines changed: 77 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useState, useCallback, useRef, useEffect, useMemo } from 'react';
2+
import { Link } from 'react-router-dom';
23
import Box from '@mui/material/Box';
34
import Chip from '@mui/material/Chip';
45
import Menu from '@mui/material/Menu';
@@ -7,11 +8,13 @@ import ListItemText from '@mui/material/ListItemText';
78
import Typography from '@mui/material/Typography';
89
import InputBase from '@mui/material/InputBase';
910
import Divider from '@mui/material/Divider';
11+
import Tooltip from '@mui/material/Tooltip';
1012
import CloseIcon from '@mui/icons-material/Close';
1113
import SearchIcon from '@mui/icons-material/Search';
1214
import AddIcon from '@mui/icons-material/Add';
1315
import ViewAgendaIcon from '@mui/icons-material/ViewAgenda';
1416
import ViewModuleIcon from '@mui/icons-material/ViewModule';
17+
import ListIcon from '@mui/icons-material/List';
1518
import useMediaQuery from '@mui/material/useMediaQuery';
1619
import { useTheme } from '@mui/material/styles';
1720

@@ -343,28 +346,57 @@ export function FilterBar({
343346
{scrollPercent}% · {currentTotal}
344347
</Typography>
345348
)}
346-
{/* Grid size toggle - absolute right (desktop only) */}
349+
{/* Catalog icon + Grid size toggle - absolute right (desktop only) */}
347350
{!isMobile && (
348351
<Box
349-
onClick={() => onImageSizeChange(imageSize === 'normal' ? 'compact' : 'normal')}
350352
sx={{
351353
position: 'absolute',
352354
right: 0,
353355
display: 'flex',
354356
alignItems: 'center',
355-
justifyContent: 'center',
356-
width: 32,
357-
height: 32,
358-
cursor: 'pointer',
359-
color: '#9ca3af',
360-
'&:hover': { color: '#3776AB' },
357+
gap: 0.5,
361358
}}
362359
>
363-
{imageSize === 'normal' ? (
364-
<ViewAgendaIcon sx={{ fontSize: '1.25rem' }} />
365-
) : (
366-
<ViewModuleIcon sx={{ fontSize: '1.25rem' }} />
367-
)}
360+
{/* Catalog icon */}
361+
<Tooltip title="catalog">
362+
<Box
363+
component={Link}
364+
to="/catalog"
365+
sx={{
366+
display: 'flex',
367+
alignItems: 'center',
368+
justifyContent: 'center',
369+
width: 32,
370+
height: 32,
371+
color: '#9ca3af',
372+
'&:hover': { color: '#3776AB' },
373+
}}
374+
>
375+
<ListIcon sx={{ fontSize: '1.25rem' }} />
376+
</Box>
377+
</Tooltip>
378+
{/* Grid size toggle */}
379+
<Tooltip title={imageSize === 'normal' ? 'compact view' : 'normal view'}>
380+
<Box
381+
onClick={() => onImageSizeChange(imageSize === 'normal' ? 'compact' : 'normal')}
382+
sx={{
383+
display: 'flex',
384+
alignItems: 'center',
385+
justifyContent: 'center',
386+
width: 32,
387+
height: 32,
388+
cursor: 'pointer',
389+
color: '#9ca3af',
390+
'&:hover': { color: '#3776AB' },
391+
}}
392+
>
393+
{imageSize === 'normal' ? (
394+
<ViewAgendaIcon sx={{ fontSize: '1.25rem' }} />
395+
) : (
396+
<ViewModuleIcon sx={{ fontSize: '1.25rem' }} />
397+
)}
398+
</Box>
399+
</Tooltip>
368400
</Box>
369401
)}
370402
{/* Active filter chips */}
@@ -443,15 +475,17 @@ export function FilterBar({
443475
},
444476
}}
445477
>
446-
<SearchIcon
447-
className="search-icon"
448-
sx={{
449-
color: '#9ca3af',
450-
fontSize: isSearchExpanded ? '1rem' : '1.25rem',
451-
transition: 'all 0.2s ease',
452-
flexShrink: 0,
453-
}}
454-
/>
478+
<Tooltip title={isSearchExpanded ? '' : 'search'}>
479+
<SearchIcon
480+
className="search-icon"
481+
sx={{
482+
color: '#9ca3af',
483+
fontSize: isSearchExpanded ? '1rem' : '1.25rem',
484+
transition: 'all 0.2s ease',
485+
flexShrink: 0,
486+
}}
487+
/>
488+
</Tooltip>
455489
<InputBase
456490
inputRef={inputRef}
457491
id="filter-search"
@@ -531,25 +565,27 @@ export function FilterBar({
531565
) : (
532566
<Box />
533567
)}
534-
<Box
535-
onClick={() => onImageSizeChange(imageSize === 'normal' ? 'compact' : 'normal')}
536-
sx={{
537-
display: 'flex',
538-
alignItems: 'center',
539-
justifyContent: 'center',
540-
width: 32,
541-
height: 32,
542-
cursor: 'pointer',
543-
color: '#9ca3af',
544-
'&:hover': { color: '#3776AB' },
545-
}}
546-
>
547-
{imageSize === 'normal' ? (
548-
<ViewAgendaIcon sx={{ fontSize: '1.25rem' }} />
549-
) : (
550-
<ViewModuleIcon sx={{ fontSize: '1.25rem' }} />
551-
)}
552-
</Box>
568+
<Tooltip title={imageSize === 'normal' ? 'compact view' : 'normal view'}>
569+
<Box
570+
onClick={() => onImageSizeChange(imageSize === 'normal' ? 'compact' : 'normal')}
571+
sx={{
572+
display: 'flex',
573+
alignItems: 'center',
574+
justifyContent: 'center',
575+
width: 32,
576+
height: 32,
577+
cursor: 'pointer',
578+
color: '#9ca3af',
579+
'&:hover': { color: '#3776AB' },
580+
}}
581+
>
582+
{imageSize === 'normal' ? (
583+
<ViewAgendaIcon sx={{ fontSize: '1.25rem' }} />
584+
) : (
585+
<ViewModuleIcon sx={{ fontSize: '1.25rem' }} />
586+
)}
587+
</Box>
588+
</Tooltip>
553589
</Box>
554590
)}
555591

app/src/components/Header.tsx

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { memo, useState, useEffect } from 'react';
2-
import { Link as RouterLink } from 'react-router-dom';
32
import Box from '@mui/material/Box';
43
import Typography from '@mui/material/Typography';
54
import Link from '@mui/material/Link';
@@ -8,7 +7,6 @@ import ClickAwayListener from '@mui/material/ClickAwayListener';
87
import useMediaQuery from '@mui/material/useMediaQuery';
98
import { useTheme } from '@mui/material/styles';
109
import ShuffleIcon from '@mui/icons-material/Shuffle';
11-
import ListIcon from '@mui/icons-material/List';
1210

1311
interface HeaderProps {
1412
stats?: { specs: number; plots: number; libraries: number } | null;
@@ -190,32 +188,6 @@ export const Header = memo(function Header({ stats, onRandom }: HeaderProps) {
190188
{isXs ? '. copy. create.' : '. grab the code. make it yours.'}
191189
</Typography>
192190

193-
{/* Catalog Link */}
194-
<Box
195-
component={RouterLink}
196-
to="/catalog"
197-
sx={{
198-
display: 'inline-flex',
199-
alignItems: 'center',
200-
gap: 0.5,
201-
mt: 2,
202-
px: 1.5,
203-
py: 0.5,
204-
borderRadius: 1,
205-
fontFamily: '"MonoLisa", monospace',
206-
fontSize: '0.85rem',
207-
color: '#6b7280',
208-
textDecoration: 'none',
209-
transition: 'all 0.2s',
210-
'&:hover': {
211-
color: '#3776AB',
212-
bgcolor: 'rgba(55, 118, 171, 0.08)',
213-
},
214-
}}
215-
>
216-
<ListIcon sx={{ fontSize: '1rem' }} />
217-
catalog
218-
</Box>
219191
</Box>
220192
);
221193
});

app/src/pages/CatalogPage.tsx

Lines changed: 58 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,12 @@ import { Link } from 'react-router-dom';
33
import { Helmet } from 'react-helmet-async';
44
import Box from '@mui/material/Box';
55
import Typography from '@mui/material/Typography';
6-
import Button from '@mui/material/Button';
76
import Skeleton from '@mui/material/Skeleton';
8-
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
97

108
import { API_URL } from '../constants';
119
import { useAnalytics } from '../hooks';
12-
import { useAppData } from '../components/Layout';
10+
import { useAppData, useHomeState } from '../components/Layout';
11+
import { Footer } from '../components';
1312
import type { PlotImage } from '../types';
1413

1514
interface CatalogSpec {
@@ -21,11 +20,13 @@ interface CatalogSpec {
2120

2221
export function CatalogPage() {
2322
const { specsData } = useAppData();
23+
const { saveScrollPosition } = useHomeState();
2424
const { trackEvent } = useAnalytics();
2525

2626
const [allImages, setAllImages] = useState<PlotImage[]>([]);
2727
const [loading, setLoading] = useState(true);
2828
const [rotationIndex, setRotationIndex] = useState<Record<string, number>>({});
29+
const [expandedDescs, setExpandedDescs] = useState<Record<string, boolean>>({});
2930

3031
// Fetch all images
3132
useEffect(() => {
@@ -127,28 +128,48 @@ export function CatalogPage() {
127128
return (
128129
<>
129130
<Helmet>
130-
<title>Catalog | pyplots.ai</title>
131-
<meta name="description" content="Browse all Python plotting examples alphabetically" />
132-
<meta property="og:title" content="Catalog | pyplots.ai" />
133-
<meta property="og:description" content="Browse all Python plotting examples alphabetically" />
131+
<title>catalog | pyplots.ai</title>
132+
<meta name="description" content="Browse all Python plotting specifications alphabetically" />
133+
<meta property="og:title" content="catalog | pyplots.ai" />
134+
<meta property="og:description" content="Browse all Python plotting specifications alphabetically" />
134135
</Helmet>
135136

136137
<Box sx={{ pb: 4 }}>
137-
{/* Back Button */}
138-
<Button
139-
component={Link}
140-
to="/"
141-
startIcon={<ArrowBackIcon />}
138+
{/* Breadcrumb navigation */}
139+
<Box
142140
sx={{
143-
color: '#6b7280',
141+
display: 'flex',
142+
alignItems: 'center',
143+
mx: { xs: -2, sm: -4, md: -8, lg: -12 },
144+
mt: -5,
145+
px: 2,
146+
py: 1,
144147
mb: 3,
148+
bgcolor: '#f3f4f6',
149+
borderBottom: '1px solid #e5e7eb',
145150
fontFamily: '"MonoLisa", monospace',
146-
textTransform: 'none',
147-
'&:hover': { color: '#3776AB', bgcolor: 'transparent' },
151+
fontSize: '0.85rem',
152+
position: 'sticky',
153+
top: 0,
154+
zIndex: 100,
148155
}}
149156
>
150-
Back
151-
</Button>
157+
<Box
158+
component={Link}
159+
to="/"
160+
sx={{
161+
color: '#3776AB',
162+
textDecoration: 'none',
163+
'&:hover': { textDecoration: 'underline' },
164+
}}
165+
>
166+
pyplots.ai
167+
</Box>
168+
<Box component="span" sx={{ mx: 1, color: '#9ca3af' }}></Box>
169+
<Box component="span" sx={{ color: '#4b5563' }}>
170+
catalog
171+
</Box>
172+
</Box>
152173

153174
{/* Title */}
154175
<Typography
@@ -161,7 +182,7 @@ export function CatalogPage() {
161182
color: '#1f2937',
162183
}}
163184
>
164-
Catalog
185+
catalog
165186
<Typography
166187
component="span"
167188
sx={{
@@ -171,7 +192,7 @@ export function CatalogPage() {
171192
color: '#9ca3af',
172193
}}
173194
>
174-
{catalogSpecs.length} examples
195+
{catalogSpecs.length} specifications
175196
</Typography>
176197
</Typography>
177198

@@ -281,6 +302,7 @@ export function CatalogPage() {
281302
<Box
282303
component={Link}
283304
to={`/${spec.id}/${currentImage?.library || ''}`}
305+
onClick={saveScrollPosition}
284306
sx={{
285307
flex: 1,
286308
textDecoration: 'none',
@@ -304,11 +326,25 @@ export function CatalogPage() {
304326
</Typography>
305327
{spec.description && (
306328
<Typography
329+
onClick={(e) => {
330+
if (!expandedDescs[spec.id]) {
331+
e.preventDefault();
332+
e.stopPropagation();
333+
setExpandedDescs((prev) => ({ ...prev, [spec.id]: true }));
334+
}
335+
}}
307336
sx={{
308337
fontFamily: '"MonoLisa", monospace',
309338
fontSize: '0.85rem',
310339
color: '#6b7280',
311340
lineHeight: 1.6,
341+
cursor: expandedDescs[spec.id] ? 'default' : 'pointer',
342+
...(!expandedDescs[spec.id] && {
343+
display: '-webkit-box',
344+
WebkitLineClamp: 5,
345+
WebkitBoxOrient: 'vertical',
346+
overflow: 'hidden',
347+
}),
312348
}}
313349
>
314350
{spec.description}
@@ -319,6 +355,9 @@ export function CatalogPage() {
319355
);
320356
})}
321357
</Box>
358+
359+
{/* Footer */}
360+
<Footer onTrackEvent={trackEvent} />
322361
</Box>
323362
</>
324363
);

app/src/pages/InteractivePage.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,18 @@ export function InteractivePage() {
213213
pyplots.ai
214214
</Box>
215215
<Box component="span" sx={{ mx: 1, color: '#9ca3af' }}></Box>
216+
<Box
217+
component={Link}
218+
to="/catalog"
219+
sx={{
220+
color: '#3776AB',
221+
textDecoration: 'none',
222+
'&:hover': { textDecoration: 'underline' },
223+
}}
224+
>
225+
catalog
226+
</Box>
227+
<Box component="span" sx={{ mx: 1, color: '#9ca3af' }}></Box>
216228
<Box
217229
component={Link}
218230
to={`/${specId}`}

0 commit comments

Comments
 (0)