Skip to content

Commit e55cbab

Browse files
committed
minor layout changes
1 parent 8d13e18 commit e55cbab

File tree

4 files changed

+583
-439
lines changed

4 files changed

+583
-439
lines changed

src/components/SearchAndFilter.jsx

Lines changed: 191 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ import {
77
Box,
88
Chip,
99
Stack,
10+
Collapse,
11+
Typography,
12+
Tooltip,
1013
} from '@mui/material';
11-
import { Search, Clear } from '@mui/icons-material';
14+
import { Search, Clear, FilterList } from '@mui/icons-material';
1215
import { trackSearch, trackFilter } from '../utils/analytics.js';
1316

1417
const SearchAndFilter = ({
@@ -20,6 +23,7 @@ const SearchAndFilter = ({
2023
specialLabels = [],
2124
}) => {
2225
const [localSearchQuery, setLocalSearchQuery] = useState(searchQuery);
26+
const [isKeywordSelectOpen, setIsKeywordSelectOpen] = useState(false);
2327

2428
// Update local state when external searchQuery changes
2529
useEffect(() => {
@@ -50,7 +54,7 @@ const SearchAndFilter = ({
5054
onSearchChange('');
5155
};
5256
return (
53-
<Box sx={{ mb: 2 }}>
57+
<Box>
5458
<Stack spacing={1.5}>
5559
{/* Compact Search Bar */}
5660
<TextField
@@ -68,11 +72,43 @@ const SearchAndFilter = ({
6872
),
6973
endAdornment: (
7074
<InputAdornment position="end">
71-
{localSearchQuery && (
72-
<IconButton edge="end" onClick={handleClear} size="small">
73-
<Clear sx={{ fontSize: 18 }} />
74-
</IconButton>
75-
)}
75+
<Stack direction="row" spacing={0.5} alignItems="center">
76+
{/* Quick Keyword Select Toggle - Now visible on all devices */}
77+
{(availableLabels.length > 0 || specialLabels.length > 0) && (
78+
<Tooltip
79+
title={
80+
isKeywordSelectOpen
81+
? 'Hide keyword filters'
82+
: 'Show keyword filters'
83+
}
84+
>
85+
<IconButton
86+
edge="end"
87+
onClick={() =>
88+
setIsKeywordSelectOpen(!isKeywordSelectOpen)
89+
}
90+
size="small"
91+
sx={{
92+
color: isKeywordSelectOpen
93+
? 'primary.main'
94+
: 'action.active',
95+
'&:hover': {
96+
color: 'primary.main',
97+
bgcolor: 'action.hover',
98+
},
99+
}}
100+
>
101+
<FilterList sx={{ fontSize: 20 }} />
102+
</IconButton>
103+
</Tooltip>
104+
)}
105+
106+
{localSearchQuery && (
107+
<IconButton edge="end" onClick={handleClear} size="small">
108+
<Clear sx={{ fontSize: 18 }} />
109+
</IconButton>
110+
)}
111+
</Stack>
76112
</InputAdornment>
77113
),
78114
}}
@@ -90,95 +126,156 @@ const SearchAndFilter = ({
90126
}}
91127
/>
92128

93-
{/* Topic Filter Labels */}
129+
{/* Topic Filter Labels - Now visible on all devices when toggled */}
94130
{(availableLabels.length > 0 || specialLabels.length > 0) && (
95-
<Box
96-
sx={{
97-
p: 1.5,
98-
bgcolor: 'background.paper',
99-
borderRadius: 2,
100-
border: theme => `1px solid ${theme.palette.divider}`,
101-
boxShadow: 1,
102-
}}
103-
>
104-
<Stack
105-
direction="row"
106-
spacing={0.5}
107-
flexWrap="wrap"
108-
sx={{ gap: 0.5 }}
109-
>
110-
{/* Special Labels First */}
111-
{specialLabels.map(label => {
112-
const isSelected = selectedLabels.includes(label);
113-
const labelColor =
114-
label === 'prior / related work' ? 'default' : 'typeLabels';
115-
return (
116-
<Chip
117-
key={label}
118-
label={label}
119-
size="small"
120-
clickable
121-
color={labelColor}
122-
variant={isSelected ? 'filled' : 'outlined'}
123-
onClick={() => {
124-
if (isSelected) {
125-
onLabelsChange(selectedLabels.filter(l => l !== label));
126-
trackFilter('special_label', `remove_${label}`);
127-
} else {
128-
onLabelsChange([...selectedLabels, label]);
129-
trackFilter('special_label', `add_${label}`);
130-
}
131-
}}
132-
sx={{
133-
height: 26,
134-
fontSize: '0.7rem',
135-
borderRadius: 2,
136-
'& .MuiChip-label': { px: 1.5 },
137-
transition: 'all 0.2s ease',
138-
'&:hover': {
139-
transform: 'translateY(-1px)',
140-
boxShadow: 2,
141-
},
142-
}}
143-
/>
144-
);
145-
})}
131+
<Box>
132+
<Collapse in={isKeywordSelectOpen}>
133+
<Box
134+
sx={{
135+
p: 2,
136+
bgcolor: 'background.paper',
137+
borderRadius: 2,
138+
border: theme => `1px solid ${theme.palette.divider}`,
139+
boxShadow: 1,
140+
}}
141+
>
142+
<Stack spacing={1.5}>
143+
{/* Special Labels Section */}
144+
{specialLabels.length > 0 && (
145+
<Box>
146+
<Typography
147+
variant="caption"
148+
sx={{
149+
display: 'block',
150+
mb: 0.75,
151+
color: 'text.secondary',
152+
fontWeight: 600,
153+
textTransform: 'uppercase',
154+
fontSize: '0.65rem',
155+
letterSpacing: '0.5px',
156+
}}
157+
>
158+
Paper Types
159+
</Typography>
160+
<Stack
161+
direction="row"
162+
spacing={0.5}
163+
flexWrap="wrap"
164+
sx={{ gap: 0.5 }}
165+
>
166+
{specialLabels.map(label => {
167+
const isSelected = selectedLabels.includes(label);
168+
const labelColor =
169+
label === 'prior / related work'
170+
? 'default'
171+
: 'typeLabels';
172+
return (
173+
<Chip
174+
key={label}
175+
label={label}
176+
size="small"
177+
clickable
178+
color={labelColor}
179+
variant={isSelected ? 'filled' : 'outlined'}
180+
onClick={() => {
181+
if (isSelected) {
182+
onLabelsChange(
183+
selectedLabels.filter(l => l !== label)
184+
);
185+
trackFilter(
186+
'special_label',
187+
`remove_${label}`
188+
);
189+
} else {
190+
onLabelsChange([...selectedLabels, label]);
191+
trackFilter('special_label', `add_${label}`);
192+
}
193+
}}
194+
sx={{
195+
height: 28,
196+
fontSize: '0.75rem',
197+
borderRadius: 2,
198+
'& .MuiChip-label': { px: 1.5 },
199+
transition: 'all 0.2s ease',
200+
'&:hover': {
201+
transform: 'translateY(-1px)',
202+
boxShadow: 2,
203+
},
204+
}}
205+
/>
206+
);
207+
})}
208+
</Stack>
209+
</Box>
210+
)}
146211

147-
{/* Regular Labels - Show All */}
148-
{availableLabels.map(label => {
149-
const isSelected = selectedLabels.includes(label);
150-
return (
151-
<Chip
152-
key={label}
153-
label={label}
154-
size="small"
155-
clickable
156-
color="labels"
157-
variant={isSelected ? 'filled' : 'outlined'}
158-
onClick={() => {
159-
if (isSelected) {
160-
onLabelsChange(selectedLabels.filter(l => l !== label));
161-
trackFilter('regular_label', `remove_${label}`);
162-
} else {
163-
onLabelsChange([...selectedLabels, label]);
164-
trackFilter('regular_label', `add_${label}`);
165-
}
166-
}}
167-
sx={{
168-
height: 26,
169-
fontSize: '0.7rem',
170-
borderRadius: 2,
171-
'& .MuiChip-label': { px: 1.5 },
172-
transition: 'all 0.2s ease',
173-
'&:hover': {
174-
transform: 'translateY(-1px)',
175-
boxShadow: 2,
176-
},
177-
}}
178-
/>
179-
);
180-
})}
181-
</Stack>
212+
{/* Regular Labels Section */}
213+
{availableLabels.length > 0 && (
214+
<Box>
215+
<Typography
216+
variant="caption"
217+
sx={{
218+
display: 'block',
219+
mb: 0.75,
220+
color: 'text.secondary',
221+
fontWeight: 600,
222+
textTransform: 'uppercase',
223+
fontSize: '0.65rem',
224+
letterSpacing: '0.5px',
225+
}}
226+
>
227+
Topics & Keywords
228+
</Typography>
229+
<Stack
230+
direction="row"
231+
spacing={0.5}
232+
flexWrap="wrap"
233+
sx={{ gap: 0.5 }}
234+
>
235+
{availableLabels.map(label => {
236+
const isSelected = selectedLabels.includes(label);
237+
return (
238+
<Chip
239+
key={label}
240+
label={label}
241+
size="small"
242+
clickable
243+
color="labels"
244+
variant={isSelected ? 'filled' : 'outlined'}
245+
onClick={() => {
246+
if (isSelected) {
247+
onLabelsChange(
248+
selectedLabels.filter(l => l !== label)
249+
);
250+
trackFilter(
251+
'regular_label',
252+
`remove_${label}`
253+
);
254+
} else {
255+
onLabelsChange([...selectedLabels, label]);
256+
trackFilter('regular_label', `add_${label}`);
257+
}
258+
}}
259+
sx={{
260+
height: 28,
261+
fontSize: '0.75rem',
262+
borderRadius: 2,
263+
'& .MuiChip-label': { px: 1.5 },
264+
transition: 'all 0.2s ease',
265+
'&:hover': {
266+
transform: 'translateY(-1px)',
267+
boxShadow: 2,
268+
},
269+
}}
270+
/>
271+
);
272+
})}
273+
</Stack>
274+
</Box>
275+
)}
276+
</Stack>
277+
</Box>
278+
</Collapse>
182279
</Box>
183280
)}
184281
</Stack>

0 commit comments

Comments
 (0)