diff --git a/quotesdb/src/bin/ui/pages/browse.rs b/quotesdb/src/bin/ui/pages/browse.rs index 21d4472..429cec3 100644 --- a/quotesdb/src/bin/ui/pages/browse.rs +++ b/quotesdb/src/bin/ui/pages/browse.rs @@ -14,6 +14,9 @@ use yew::prelude::*; /// Displays a paginated list of quotes. Supports filtering by author name, /// tag, and an optional date range (year granularity). Fetches from the API /// whenever page, author, tag, or date state changes. +/// +/// Filter controls are hidden behind a collapsible "Filters" toggle button. +/// The panel is collapsed by default when the page loads. #[function_component(BrowsePage)] pub fn browse_page() -> Html { let page = use_state(|| 1u32); @@ -25,6 +28,8 @@ pub fn browse_page() -> Html { let quotes: UseStateHandle> = use_state(Vec::new); let error: UseStateHandle> = use_state(|| None); let loading = use_state(|| true); + // Filter panel is collapsed by default. + let filters_open = use_state(|| false); // Fetch quotes whenever page, author, tag, or date bounds change { @@ -143,51 +148,85 @@ pub fn browse_page() -> Html { }) }; + let on_toggle_filters = { + let filters_open = filters_open.clone(); + Callback::from(move |_: MouseEvent| { + filters_open.set(!*filters_open); + }) + }; + + let toggle_label = if *filters_open { + "Filters \u{25b2}" + } else { + "Filters \u{25bc}" + }; + html! {

{ "Browse Quotes" }

-
- - -
- - -
-
- - -
+
+
+ if *filters_open { +
+
+ + +
+
+ + +
+
+ +
+ { "after" } + + { "before" } + +
+
+
+ } + if *loading {

{ "Loading..." }

} else if let Some(err) = (*error).clone() { diff --git a/quotesdb/src/bin/ui/style.css b/quotesdb/src/bin/ui/style.css index ee61e6d..d036afb 100644 --- a/quotesdb/src/bin/ui/style.css +++ b/quotesdb/src/bin/ui/style.css @@ -419,15 +419,69 @@ code { margin-bottom: 1.5rem; } +/* Row containing the toggle button, sits above the collapsible panel */ +.page-browse__filter-toggle-row { + margin-bottom: 0.75rem; +} + +/* Toggle button — inherits .btn base styles */ +.page-browse__filter-toggle { + font-size: 0.9rem; +} + +/* Collapsible filter panel — shown only when open */ .page-browse__filters { display: flex; - gap: 1rem; - margin-bottom: 2rem; - flex-wrap: wrap; + flex-direction: column; + gap: 0.75rem; + background: var(--color-surface); + border: 1px solid var(--color-border); + border-radius: var(--radius); + padding: 1rem 1.25rem; + margin-bottom: 1.5rem; } +/* A single filter row: label + input(s) on one line */ +.page-browse__filter-row { + display: flex; + align-items: center; + gap: 0.75rem; +} + +/* Fixed-width label so all inputs line up vertically */ +.page-browse__filter-label { + flex-shrink: 0; + width: 4.5rem; + font-size: 0.875rem; + font-weight: 600; + color: var(--color-text); + text-align: right; +} + +/* Text / number inputs inside the filter panel */ .page-browse__filter-input { - max-width: 240px; + flex: 1; + max-width: 260px; +} + +/* Narrower year inputs for the date range row */ +.page-browse__filter-input--year { + max-width: 120px; +} + +/* Inner group for the date range row: "after [input] before [input]" */ +.page-browse__filter-date-group { + display: flex; + align-items: center; + gap: 0.5rem; + flex-wrap: wrap; +} + +/* Small inline text labels ("after" / "before") */ +.page-browse__filter-date-label { + font-size: 0.875rem; + color: var(--color-text-muted); + white-space: nowrap; } .page-browse__loading, @@ -611,15 +665,21 @@ code { align-items: center; } - .page-browse__filters { - flex-direction: column; + .page-browse__filter-row { + flex-wrap: wrap; } .page-browse__filter-input, + .page-browse__filter-input--year, .page-author__filter-input { max-width: 100%; } + .page-browse__filter-date-group { + flex-direction: column; + align-items: flex-start; + } + .nav { padding: 0.75rem 1rem; }