Convert DashboardRow to component

This commit is contained in:
Ethan Girouard 2024-12-24 16:41:36 -05:00
parent ae8a3d0ade
commit 4fe76fe596
Signed by: eta357
GPG Key ID: 7BCDC36DFD11C146
2 changed files with 102 additions and 120 deletions

View File

@ -2,117 +2,113 @@ use leptos::html::Ul;
use leptos::leptos_dom::*; use leptos::leptos_dom::*;
use leptos::*; use leptos::*;
use leptos_use::{use_element_size, UseElementSizeReturn, use_scroll, UseScrollReturn}; use leptos_use::{use_element_size, UseElementSizeReturn, use_scroll, UseScrollReturn};
use crate::components::dashboard_tile::DashboardTile; use crate::components::dashboard_tile::*;
use leptos_icons::*; use leptos_icons::*;
/// A row of dashboard tiles, with a title /// A row of dashboard tiles, with a title
pub struct DashboardRow { #[component]
pub title: String, pub fn DashboardRow(
pub tiles: Vec<Box<dyn DashboardTile>>, #[prop(into)] title: TextProp,
} /*#[prop(default=vec![])]*/ tiles: Vec<DashboardTile>,
) -> impl IntoView {
let list_ref = create_node_ref::<Ul>();
impl DashboardRow { // Scroll functions attempt to align the left edge of the scroll area with the left edge of a tile
pub fn new(title: String, tiles: Vec<Box<dyn DashboardTile>>) -> Self { // This is done by scrolling to the nearest multiple of the tile width, plus some for padding
Self {
title, let scroll_left = move |_| {
tiles, if let Some(scroll_element) = list_ref.get_untracked() {
let client_width = scroll_element.client_width() as f64;
let current_pos = scroll_element.scroll_left() as f64;
let desired_pos = current_pos - client_width;
if let Some(first_tile) = scroll_element.first_element_child() {
let tile_width = first_tile.client_width() as f64;
let scroll_pos = desired_pos + (tile_width - (desired_pos % tile_width));
scroll_element.scroll_to_with_x_and_y(scroll_pos, 0.0);
} else {
warn!("Could not get first tile to scroll left");
// Fall back to scrolling by the client width if we can't get the tile width
scroll_element.scroll_to_with_x_and_y(desired_pos, 0.0);
}
} else {
warn!("Could not get scroll element to scroll left");
} }
} };
}
impl IntoView for DashboardRow { let scroll_right = move |_| {
fn into_view(self) -> View { if let Some(scroll_element) = list_ref.get_untracked() {
let list_ref = create_node_ref::<Ul>(); let client_width = scroll_element.client_width() as f64;
let current_pos = scroll_element.scroll_left() as f64;
// Scroll functions attempt to align the left edge of the scroll area with the left edge of a tile let desired_pos = current_pos + client_width;
// This is done by scrolling to the nearest multiple of the tile width, plus some for padding
if let Some(first_tile) = scroll_element.first_element_child() {
let scroll_left = move |_| { let tile_width = first_tile.client_width() as f64;
if let Some(scroll_element) = list_ref.get_untracked() { let scroll_pos = desired_pos - (desired_pos % tile_width);
let client_width = scroll_element.client_width() as f64; scroll_element.scroll_to_with_x_and_y(scroll_pos, 0.0);
let current_pos = scroll_element.scroll_left() as f64;
let desired_pos = current_pos - client_width;
if let Some(first_tile) = scroll_element.first_element_child() {
let tile_width = first_tile.client_width() as f64;
let scroll_pos = desired_pos + (tile_width - (desired_pos % tile_width));
scroll_element.scroll_to_with_x_and_y(scroll_pos, 0.0);
} else {
warn!("Could not get first tile to scroll left");
// Fall back to scrolling by the client width if we can't get the tile width
scroll_element.scroll_to_with_x_and_y(desired_pos, 0.0);
}
} else { } else {
warn!("Could not get scroll element to scroll left"); warn!("Could not get first tile to scroll right");
// Fall back to scrolling by the client width if we can't get the tile width
scroll_element.scroll_to_with_x_and_y(desired_pos, 0.0);
} }
}; } else {
warn!("Could not get scroll element to scroll right");
}
};
let scroll_right = move |_| { let UseElementSizeReturn { width: scroll_element_width, .. } = use_element_size(list_ref);
if let Some(scroll_element) = list_ref.get_untracked() { let UseScrollReturn { x: scroll_x, .. } = use_scroll(list_ref);
let client_width = scroll_element.client_width() as f64;
let current_pos = scroll_element.scroll_left() as f64;
let desired_pos = current_pos + client_width;
if let Some(first_tile) = scroll_element.first_element_child() {
let tile_width = first_tile.client_width() as f64;
let scroll_pos = desired_pos - (desired_pos % tile_width);
scroll_element.scroll_to_with_x_and_y(scroll_pos, 0.0);
} else {
warn!("Could not get first tile to scroll right");
// Fall back to scrolling by the client width if we can't get the tile width
scroll_element.scroll_to_with_x_and_y(desired_pos, 0.0);
}
} else {
warn!("Could not get scroll element to scroll right");
}
};
let UseElementSizeReturn { width: scroll_element_width, .. } = use_element_size(list_ref); let scroll_right_hidden = Signal::derive(move || {
let UseScrollReturn { x: scroll_x, .. } = use_scroll(list_ref); if let Some(scroll_element) = list_ref.get() {
if scroll_element.scroll_width() as f64 - scroll_element_width.get() <= scroll_x.get() {
let scroll_right_hidden = Signal::derive(move || {
if let Some(scroll_element) = list_ref.get() {
if scroll_element.scroll_width() as f64 - scroll_element_width.get() <= scroll_x.get() {
"visibility: hidden"
} else {
""
}
} else {
""
}
});
let scroll_left_hidden = Signal::derive(move || {
if scroll_x.get() <= 0.0 {
"visibility: hidden" "visibility: hidden"
} else { } else {
"" ""
} }
}); } else {
""
}
});
view! { let scroll_left_hidden = Signal::derive(move || {
<div class="dashboard-tile-row"> if scroll_x.get() <= 0.0 {
<div class="dashboard-tile-row-title-row"> "visibility: hidden"
<h2>{self.title}</h2> } else {
<div class="dashboard-tile-row-scroll-btn"> ""
<button on:click=scroll_left tabindex=-1 style=scroll_left_hidden> }
<Icon class="dashboard-tile-row-scroll" icon=icondata::FiChevronLeft /> });
</button>
<button on:click=scroll_right tabindex=-1 style=scroll_right_hidden> view! {
<Icon class="dashboard-tile-row-scroll" icon=icondata::FiChevronRight /> <div class="dashboard-tile-row">
</button> <div class="dashboard-tile-row-title-row">
</div> <h2>{title}</h2>
<div class="dashboard-tile-row-scroll-btn">
<button on:click=scroll_left tabindex=-1 style=scroll_left_hidden>
<Icon class="dashboard-tile-row-scroll" icon=icondata::FiChevronLeft />
</button>
<button on:click=scroll_right tabindex=-1 style=scroll_right_hidden>
<Icon class="dashboard-tile-row-scroll" icon=icondata::FiChevronRight />
</button>
</div> </div>
<ul _ref={list_ref}>
{self.tiles.into_iter().map(|tile_info| {
view! {
<li>
{ tile_info.into_view() }
</li>
}
}).collect::<Vec<_>>()}
</ul>
</div> </div>
}.into_view() <ul _ref={list_ref}>
} {tiles.into_iter().map(|tile| {
view! {
<li>
<div class="dashboard-tile">
<a href={tile.link}>
<img src={tile.image_path} alt="dashboard-tile" />
<p class="dashboard-tile-title">{tile.title}</p>
<p class="dashboard-tile-description">
{tile.description}
</p>
</a>
</div>
</li>
}
}).collect::<Vec<_>>()}
</ul>
</div>
}.into_view()
} }

View File

@ -1,27 +1,13 @@
use leptos::leptos_dom::*;
use leptos::*; use leptos::*;
pub trait DashboardTile { #[slot]
fn image_path(&self) -> String; pub struct DashboardTile {
fn title(&self) -> String; #[prop(into)]
fn link(&self) -> String; image_path: TextProp,
fn description(&self) -> Option<String> { None } #[prop(into)]
} title: TextProp,
#[prop(into)]
impl IntoView for &dyn DashboardTile { link: TextProp,
fn into_view(self) -> View { #[prop(into, optional)]
let link = self.link(); description: Option<TextProp>,
view! {
<div class="dashboard-tile">
<a href={link}>
<img src={self.image_path()} alt="dashboard-tile" />
<p class="dashboard-tile-title">{self.title()}</p>
<p class="dashboard-tile-description">
{self.description().unwrap_or_default()}
</p>
</a>
</div>
}.into_view()
}
} }