FavoriteGrid.svelte RawHistoryFolder
1<script lang="ts">
2 import type { Favorite } from '../types';
3 import { homepageState } from '../state.svelte';
4 import FavoriteCard from './FavoriteCard.svelte';
5
6 interface Props {
7 favorites: Favorite[];
8 onSelectFavorite?: (fav: Favorite) => void;
9 onEditFavorite?: (fav: Favorite) => void;
10 }
11
12 let { favorites, onSelectFavorite, onEditFavorite }: Props = $props();
13
14 let containerRef = $state<HTMLElement | null>(null);
15
16 const getFocusedFavorite = () => {
17 return favorites[homepageState.focusedIndex] ?? null;
18 };
19
20 // Track previous focused index to only scroll on actual navigation
21 let prevFocusedIndex = $state(-1);
22
23 // Scroll focused item into view when focusedIndex changes via keyboard navigation
24 $effect(() => {
25 // Skip if panel not active or command palette is open
26 if (!containerRef || homepageState.activePanel !== 'favorites') return;
27 if (homepageState.commandPaletteOpen) return;
28
29 const currentIndex = homepageState.focusedIndex;
30
31 // Only scroll if index actually changed (not on initial render or modal close)
32 if (prevFocusedIndex === currentIndex) return;
33 if (prevFocusedIndex === -1) {
34 // First render - just track, don't scroll
35 prevFocusedIndex = currentIndex;
36 return;
37 }
38
39 prevFocusedIndex = currentIndex;
40
41 const focusedFav = getFocusedFavorite();
42 if (!focusedFav) return;
43
44 // Find the focused element by favorite ID
45 const el = containerRef.querySelector(`[data-favorite-id="${focusedFav.id}"]`);
46 if (el) {
47 el.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
48 }
49 });
50</script>
51
52<div class="favorite-grid" bind:this={containerRef}>
53 {#if favorites.length === 0}
54 <div class="empty-state">
55 <span class="empty-icon"></span>
56 <span class="empty-text">NO FAVORITES</span>
57 <span class="empty-hint">Pin sites from history to add them here</span>
58 </div>
59 {:else}
60 {#each favorites as favorite (favorite.id)}
61 <FavoriteCard
62 {favorite}
63 focused={getFocusedFavorite()?.id === favorite.id}
64 onSelect={onSelectFavorite}
65 onEdit={onEditFavorite}
66 />
67 {/each}
68 {/if}
69</div>
70
71<style>
72 .favorite-grid {
73 display: flex;
74 flex-direction: column;
75 gap: 0.5rem;
76 }
77
78 .empty-state {
79 flex: 1;
80 display: flex;
81 flex-direction: column;
82 align-items: center;
83 justify-content: center;
84 gap: 0.5rem;
85 padding: 2rem;
86 color: var(--muted-foreground);
87 text-align: center;
88 border: 1px dashed var(--border);
89 background: color-mix(in srgb, var(--background) 95%, var(--card) 5%);
90 }
91
92 .empty-icon {
93 font-size: 1.5rem;
94 opacity: 0.5;
95 }
96
97 .empty-text {
98 font-size: 0.75rem;
99 font-weight: 600;
100 letter-spacing: 0.12em;
101 }
102
103 .empty-hint {
104 font-size: 0.68rem;
105 letter-spacing: 0.06em;
106 opacity: 0.7;
107 }
108</style>