Add feed sidebar navigation #8

Merged
schaefera merged 14 commits from worktree-feed-sidebar-nav into main 2026-09-15 07:20:41 +00:00
2 changed files with 35 additions and 29 deletions
Showing only changes of commit f0e42e2ad5 - Show all commits

View file

@ -2,7 +2,14 @@ body { font-family: system-ui, sans-serif; margin: 0; color: #1a1a1a; height: 10
.app-shell { display: flex; height: 100vh; } .app-shell { display: flex; height: 100vh; }
.content-header { display: flex; align-items: center; gap: 0.75rem; padding: 1.5rem 1.5rem 0.5rem; } .content-header { display: flex; align-items: center; gap: 0.75rem; padding: 1.5rem 1.5rem 0.5rem; }
.content-header h2 { margin: 0; } .content-header h2 { margin: 0; }
.article-scroll-area { flex: 1; min-height: 0; padding: 0 1.5rem 1.5rem; } /* Target the primitive's own stable data attribute, not a class we pass in:
ScrollArea sets its own `class` after spreading our attributes, so a
caller-supplied class never reaches the DOM (confirmed by inspecting the
live element it only ever carries "dx-scroll-area-auto-hide"). Without
this, the scroll div has no bounded height, so it just grows to fit every
article (nothing to scroll) while its ancestor's overflow: hidden clips
everything past the viewport instead. */
main > [data-scroll-direction] { flex: 1; min-height: 0; padding: 0 1.5rem 1.5rem; }
.article-list { list-style: none; padding: 0; margin: 0; } .article-list { list-style: none; padding: 0; margin: 0; }
.article-row { border-bottom: 1px solid #ddd; padding: 1rem 0; display: flex; flex-direction: column; align-items: flex-start; gap: 0.5rem; } .article-row { border-bottom: 1px solid #ddd; padding: 1rem 0; display: flex; flex-direction: column; align-items: flex-start; gap: 0.5rem; }
.article-row-header { display: flex; align-items: baseline; flex-wrap: wrap; gap: 0.5rem; } .article-row-header { display: flex; align-items: baseline; flex-wrap: wrap; gap: 0.5rem; }

View file

@ -85,37 +85,36 @@ pub fn App() -> Element {
} }
} }
SidebarInset { SidebarInset {
main { div { class: "content-header",
div { class: "content-header", SidebarTrigger {}
SidebarTrigger {} h2 {
h2 { {
{ let heading = match selected_feed.read().as_ref() {
let heading = match selected_feed.read().as_ref() { None => "All articles".to_string(),
None => "All articles".to_string(), Some(id) => feeds
Some(id) => feeds .read()
.read() .as_ref()
.as_ref() .and_then(|r| r.as_ref().ok())
.and_then(|r| r.as_ref().ok()) .and_then(|feeds| feeds.iter().find(|f| &f.id == id))
.and_then(|feeds| feeds.iter().find(|f| &f.id == id)) .map(|f| f.title.clone())
.map(|f| f.title.clone()) .unwrap_or_else(|| "Feed".to_string()),
.unwrap_or_else(|| "Feed".to_string()), };
}; rsx! { "{heading}" }
rsx! { "{heading}" }
}
} }
} }
ScrollArea { class: "article-scroll-area", }
match articles.read().as_ref() { ScrollArea {
Some(Ok(articles)) => rsx! { direction: dioxus_primitives::scroll_area::ScrollDirection::Vertical,
ul { class: "article-list", match articles.read().as_ref() {
for article in articles.iter() { Some(Ok(articles)) => rsx! {
ArticleRow { article: article.clone() } ul { class: "article-list",
} for article in articles.iter() {
ArticleRow { article: article.clone() }
} }
}, }
Some(Err(err)) => rsx! { p { class: "error", "Failed to load articles: {err}" } }, },
None => rsx! { p { "Loading..." } }, Some(Err(err)) => rsx! { p { class: "error", "Failed to load articles: {err}" } },
} None => rsx! { p { "Loading..." } },
} }
} }
} }