Fix article row layout: button overlapping title, uneven summary gaps
article-row had no layout structure at all — title link, score badge, and the dismiss button were plain inline siblings in a block <li>, so a long title pushed the button flush against (or past) the row's edge. Group title+score into a header row and make the row itself a flex column so summary/topics/button always stack on their own line below, regardless of title length. Also stopped rendering the summary <p> and topics div at all when there's no content (many RSS entries have no description), instead of emitting empty elements — spacing was uneven because those still reserved a line's worth of height. Left this as an explicit Rust-side branch rather than CSS :empty: an empty <p> still contains a zero-length text node, and whether :empty matches that is inconsistent across engines, whereas the topics div (an empty `for` loop) has zero children and would be a safe :empty candidate — but kept both on the same explicit mechanism for consistency. Swapped the dismiss button from a raw <button> to the dx Button component, matching the rest of the row and the project's preference for the component library over raw elements. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PqTJmazHBQK878vjQ4JnnF
This commit is contained in:
parent
8edffe8973
commit
21103b1f25
2 changed files with 21 additions and 11 deletions
|
|
@ -4,9 +4,10 @@ body { font-family: system-ui, sans-serif; margin: 0; color: #1a1a1a; height: 10
|
||||||
.content-header h2 { margin: 0; }
|
.content-header h2 { margin: 0; }
|
||||||
.article-scroll-area { flex: 1; min-height: 0; padding: 0 1.5rem 1.5rem; }
|
.article-scroll-area { 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; }
|
.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 a { font-weight: 600; text-decoration: none; color: #0b5fff; }
|
.article-row a { font-weight: 600; text-decoration: none; color: #0b5fff; }
|
||||||
.summary { color: #444; margin: 0.4rem 0; }
|
.summary { color: #444; margin: 0; }
|
||||||
.topics { display: flex; flex-wrap: wrap; gap: 0.3rem; }
|
.topics { display: flex; flex-wrap: wrap; gap: 0.3rem; }
|
||||||
.error { color: #b00020; }
|
.error { color: #b00020; }
|
||||||
.subscribe-form { display: flex; gap: 0.5rem; margin: 1rem 0; }
|
.subscribe-form { display: flex; gap: 0.5rem; margin: 1rem 0; }
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::api;
|
use crate::api;
|
||||||
use crate::api::ArticleView;
|
use crate::api::ArticleView;
|
||||||
use crate::components::badge::{Badge, BadgeVariant};
|
use crate::components::badge::{Badge, BadgeVariant};
|
||||||
|
use crate::components::button::{Button, ButtonSize, ButtonVariant};
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
|
|
@ -8,17 +9,25 @@ pub fn ArticleRow(article: ArticleView) -> Element {
|
||||||
let score_pct = article.final_score.map(|s| (s * 100.0).round() as i32);
|
let score_pct = article.final_score.map(|s| (s * 100.0).round() as i32);
|
||||||
rsx! {
|
rsx! {
|
||||||
li { class: "article-row",
|
li { class: "article-row",
|
||||||
a { href: "{article.url}", target: "_blank", "{article.title}" }
|
div { class: "article-row-header",
|
||||||
if let Some(pct) = score_pct {
|
a { href: "{article.url}", target: "_blank", "{article.title}" }
|
||||||
Badge { variant: BadgeVariant::Secondary, "{pct}%" }
|
if let Some(pct) = score_pct {
|
||||||
}
|
Badge { variant: BadgeVariant::Secondary, "{pct}%" }
|
||||||
p { class: "summary", "{article.summary}" }
|
|
||||||
div { class: "topics",
|
|
||||||
for topic in article.topics.iter() {
|
|
||||||
Badge { variant: BadgeVariant::Outline, "{topic}" }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
button {
|
if !article.summary.trim().is_empty() {
|
||||||
|
p { class: "summary", "{article.summary}" }
|
||||||
|
}
|
||||||
|
if !article.topics.is_empty() {
|
||||||
|
div { class: "topics",
|
||||||
|
for topic in article.topics.iter() {
|
||||||
|
Badge { variant: BadgeVariant::Outline, "{topic}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
variant: ButtonVariant::Outline,
|
||||||
|
size: ButtonSize::Sm,
|
||||||
onclick: move |_| {
|
onclick: move |_| {
|
||||||
let id = article.id.clone();
|
let id = article.id.clone();
|
||||||
async move {
|
async move {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue