Subhadip007 commited on
Commit
87bd3e8
·
1 Parent(s): 8b5b25e

feat(ui): implement custom premium glassmorphic dropdowns for configuration

Browse files
Files changed (1) hide show
  1. frontend-next/app/page.tsx +133 -33
frontend-next/app/page.tsx CHANGED
@@ -227,6 +227,100 @@ const FeedbackRow = ({ query, time, citationsCount, model }: { query: string, ti
227
  );
228
  };
229
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  export default function App() {
231
  const [sessions, setSessions] = useState<ChatSession[]>([]);
232
  const [activeSessionId, setActiveSessionId] = useState<string | null>(null);
@@ -756,39 +850,45 @@ export default function App() {
756
  boxShadow: '0 10px 40px rgba(0,0,0,0.5)'
757
  }}
758
  >
759
- <div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
760
- <label style={{ fontSize: '0.7rem', color: 'var(--text-muted)', fontWeight: 700, marginLeft: '4px' }}>RETRIEVAL DEPTH</label>
761
- <select style={{ background: 'rgba(0,0,0,0.4)', border: '1px solid rgba(255,255,255,0.1)', color: '#fff', padding: '8px 12px', borderRadius: '8px', cursor: 'pointer', outline: 'none', minWidth: '100px' }} value={topK} onChange={(e) => setTopK(Number(e.target.value))}>
762
- <option value={3}>Fast (3 Papers)</option>
763
- <option value={5}>Balanced (5 Papers)</option>
764
- <option value={10}>Deep (10 Papers)</option>
765
- </select>
766
- </div>
767
-
768
- <div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
769
- <label style={{ fontSize: '0.7rem', color: 'var(--text-muted)', fontWeight: 700, marginLeft: '4px' }}>RESEARCH DOMAIN</label>
770
- <select style={{ background: 'rgba(0,0,0,0.4)', border: '1px solid rgba(255,255,255,0.1)', color: '#fff', padding: '8px 12px', borderRadius: '8px', cursor: 'pointer', outline: 'none' }} value={category} onChange={(e) => setCategory(e.target.value)}>
771
- <option value="All">Global Search</option>
772
- <option value="cs.LG">cs.LG (Machine Learning)</option>
773
- <option value="cs.AI">cs.AI (Artificial Intelligence)</option>
774
- <option value="stat.ML">stat.ML (ML Stats)</option>
775
- <option value="cs.CL">cs.CL (NLP/Language)</option>
776
- <option value="cs.CV">cs.CV (Vision)</option>
777
- <option value="cs.RO">cs.RO (Robotics)</option>
778
- </select>
779
- </div>
780
-
781
- <div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
782
- <label style={{ fontSize: '0.7rem', color: 'var(--text-muted)', fontWeight: 700, marginLeft: '4px' }}>RECENCY FILTER</label>
783
- <select style={{ background: 'rgba(0,0,0,0.4)', border: '1px solid rgba(255,255,255,0.1)', color: '#fff', padding: '8px 12px', borderRadius: '8px', cursor: 'pointer', outline: 'none' }} value={filterYear} onChange={(e) => setFilterYear(e.target.value)}>
784
- <option value="All">Legacy & Modern</option>
785
- <option value="2024">2024 (Latest)</option>
786
- <option value="2023">2023+</option>
787
- <option value="2022">2022+</option>
788
- <option value="2021">2021+</option>
789
- <option value="2020">2020+</option>
790
- </select>
791
- </div>
 
 
 
 
 
 
792
  </motion.div>
793
  )}
794
  </AnimatePresence>
 
227
  );
228
  };
229
 
230
+ // --- Custom Dropdown Component ---
231
+ const CustomDropdown = ({ label, options, value, onChange }: { label: string, options: {value: any, label: string}[], value: any, onChange: (val: any) => void }) => {
232
+ const [isOpen, setIsOpen] = useState(false);
233
+ const containerRef = useRef<HTMLDivElement>(null);
234
+
235
+ useEffect(() => {
236
+ const handleClickOutside = (event: MouseEvent) => {
237
+ if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
238
+ setIsOpen(false);
239
+ }
240
+ };
241
+ document.addEventListener("mousedown", handleClickOutside);
242
+ return () => document.removeEventListener("mousedown", handleClickOutside);
243
+ }, []);
244
+
245
+ const selectedOption = options.find(opt => opt.value === value) || options[0];
246
+
247
+ return (
248
+ <div ref={containerRef} style={{ display: 'flex', flexDirection: 'column', gap: '4px', position: 'relative', minWidth: '160px' }}>
249
+ <label style={{ fontSize: '0.65rem', color: 'var(--text-muted)', fontWeight: 800, marginLeft: '4px', letterSpacing: '0.1em', textTransform: 'uppercase' }}>{label}</label>
250
+ <div
251
+ onClick={() => setIsOpen(!isOpen)}
252
+ style={{
253
+ background: 'rgba(255, 255, 255, 0.03)',
254
+ border: '1px solid rgba(255, 255, 255, 0.1)',
255
+ color: '#fff',
256
+ padding: '10px 14px',
257
+ borderRadius: '10px',
258
+ cursor: 'pointer',
259
+ display: 'flex',
260
+ justifyContent: 'space-between',
261
+ alignItems: 'center',
262
+ fontSize: '0.9rem',
263
+ fontWeight: 500,
264
+ transition: '0.2s',
265
+ userSelect: 'none'
266
+ }}
267
+ onMouseOver={e => e.currentTarget.style.borderColor = 'rgba(0, 240, 255, 0.4)'}
268
+ onMouseOut={e => e.currentTarget.style.borderColor = 'rgba(255, 255, 255, 0.1)'}
269
+ >
270
+ {selectedOption.label}
271
+ <motion.div animate={{ rotate: isOpen ? 180 : 0 }} style={{ display: 'flex' }}>
272
+ <ArrowDown size={14} opacity={0.5} />
273
+ </motion.div>
274
+ </div>
275
+
276
+ <AnimatePresence>
277
+ {isOpen && (
278
+ <motion.div
279
+ initial={{ opacity: 0, y: -10, scale: 0.95 }}
280
+ animate={{ opacity: 1, y: 5, scale: 1 }}
281
+ exit={{ opacity: 0, y: -10, scale: 0.95 }}
282
+ style={{
283
+ position: 'absolute',
284
+ bottom: '100%',
285
+ left: 0,
286
+ right: 0,
287
+ background: 'rgba(15, 20, 30, 0.98)',
288
+ backdropFilter: 'blur(20px)',
289
+ border: '1px solid rgba(255, 255, 255, 0.15)',
290
+ borderRadius: '12px',
291
+ zIndex: 1000,
292
+ padding: '6px',
293
+ boxShadow: '0 15px 40px rgba(0,0,0,0.6)',
294
+ marginBottom: '8px'
295
+ }}
296
+ >
297
+ {options.map((opt) => (
298
+ <div
299
+ key={opt.value}
300
+ onClick={() => { onChange(opt.value); setIsOpen(false); }}
301
+ style={{
302
+ padding: '10px 12px',
303
+ borderRadius: '8px',
304
+ cursor: 'pointer',
305
+ fontSize: '0.85rem',
306
+ fontWeight: opt.value === value ? 600 : 400,
307
+ color: opt.value === value ? 'var(--accent)' : '#fff',
308
+ background: opt.value === value ? 'rgba(0, 240, 255, 0.05)' : 'transparent',
309
+ transition: '0.2s'
310
+ }}
311
+ onMouseOver={e => { if(opt.value !== value) e.currentTarget.style.background = 'rgba(255, 255, 255, 0.05)'; }}
312
+ onMouseOut={e => { if(opt.value !== value) e.currentTarget.style.background = 'transparent'; }}
313
+ >
314
+ {opt.label}
315
+ </div>
316
+ ))}
317
+ </motion.div>
318
+ )}
319
+ </AnimatePresence>
320
+ </div>
321
+ );
322
+ };
323
+
324
  export default function App() {
325
  const [sessions, setSessions] = useState<ChatSession[]>([]);
326
  const [activeSessionId, setActiveSessionId] = useState<string | null>(null);
 
850
  boxShadow: '0 10px 40px rgba(0,0,0,0.5)'
851
  }}
852
  >
853
+ <CustomDropdown
854
+ label="RETRIEVAL DEPTH"
855
+ value={topK}
856
+ onChange={setTopK}
857
+ options={[
858
+ { value: 3, label: 'Fast (3 Papers)' },
859
+ { value: 5, label: 'Balanced (5 Papers)' },
860
+ { value: 10, label: 'Deep (10 Papers)' }
861
+ ]}
862
+ />
863
+
864
+ <CustomDropdown
865
+ label="RESEARCH DOMAIN"
866
+ value={category}
867
+ onChange={setCategory}
868
+ options={[
869
+ { value: 'All', label: 'Global Search' },
870
+ { value: 'cs.LG', label: 'cs.LG (Machine Learning)' },
871
+ { value: 'cs.AI', label: 'cs.AI (Artificial Intelligence)' },
872
+ { value: 'stat.ML', label: 'stat.ML (ML Stats)' },
873
+ { value: 'cs.CL', label: 'cs.CL (NLP/Language)' },
874
+ { value: 'cs.CV', label: 'cs.CV (Vision)' },
875
+ { value: 'cs.RO', label: 'cs.RO (Robotics)' }
876
+ ]}
877
+ />
878
+
879
+ <CustomDropdown
880
+ label="RECENCY FILTER"
881
+ value={filterYear}
882
+ onChange={setFilterYear}
883
+ options={[
884
+ { value: 'All', label: 'Legacy & Modern' },
885
+ { value: '2024', label: '2024 (Latest)' },
886
+ { value: '2023', label: '2023+' },
887
+ { value: '2022', label: '2022+' },
888
+ { value: '2021', label: '2021+' },
889
+ { value: '2020', label: '2020+' }
890
+ ]}
891
+ />
892
  </motion.div>
893
  )}
894
  </AnimatePresence>