Convert DashboardRow to component
This commit is contained in:
parent
ae8a3d0ade
commit
4fe76fe596
@ -2,117 +2,113 @@ use leptos::html::Ul;
|
||||
use leptos::leptos_dom::*;
|
||||
use leptos::*;
|
||||
use leptos_use::{use_element_size, UseElementSizeReturn, use_scroll, UseScrollReturn};
|
||||
use crate::components::dashboard_tile::DashboardTile;
|
||||
use crate::components::dashboard_tile::*;
|
||||
use leptos_icons::*;
|
||||
|
||||
/// A row of dashboard tiles, with a title
|
||||
pub struct DashboardRow {
|
||||
pub title: String,
|
||||
pub tiles: Vec<Box<dyn DashboardTile>>,
|
||||
}
|
||||
#[component]
|
||||
pub fn DashboardRow(
|
||||
#[prop(into)] title: TextProp,
|
||||
/*#[prop(default=vec![])]*/ tiles: Vec<DashboardTile>,
|
||||
) -> impl IntoView {
|
||||
let list_ref = create_node_ref::<Ul>();
|
||||
|
||||
impl DashboardRow {
|
||||
pub fn new(title: String, tiles: Vec<Box<dyn DashboardTile>>) -> Self {
|
||||
Self {
|
||||
title,
|
||||
tiles,
|
||||
// Scroll functions attempt to align the left edge of the scroll area with the left edge of a tile
|
||||
// This is done by scrolling to the nearest multiple of the tile width, plus some for padding
|
||||
|
||||
let scroll_left = move |_| {
|
||||
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 {
|
||||
fn into_view(self) -> View {
|
||||
let list_ref = create_node_ref::<Ul>();
|
||||
|
||||
// Scroll functions attempt to align the left edge of the scroll area with the left edge of a tile
|
||||
// This is done by scrolling to the nearest multiple of the tile width, plus some for padding
|
||||
|
||||
let scroll_left = move |_| {
|
||||
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);
|
||||
}
|
||||
let scroll_right = move |_| {
|
||||
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 - (desired_pos % tile_width);
|
||||
scroll_element.scroll_to_with_x_and_y(scroll_pos, 0.0);
|
||||
} 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 |_| {
|
||||
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 - (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 UseScrollReturn { x: scroll_x, .. } = use_scroll(list_ref);
|
||||
|
||||
let UseElementSizeReturn { width: scroll_element_width, .. } = use_element_size(list_ref);
|
||||
let UseScrollReturn { x: scroll_x, .. } = use_scroll(list_ref);
|
||||
|
||||
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 {
|
||||
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 {
|
||||
""
|
||||
}
|
||||
});
|
||||
|
||||
view! {
|
||||
<div class="dashboard-tile-row">
|
||||
<div class="dashboard-tile-row-title-row">
|
||||
<h2>{self.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>
|
||||
let scroll_left_hidden = Signal::derive(move || {
|
||||
if scroll_x.get() <= 0.0 {
|
||||
"visibility: hidden"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
});
|
||||
|
||||
view! {
|
||||
<div class="dashboard-tile-row">
|
||||
<div class="dashboard-tile-row-title-row">
|
||||
<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>
|
||||
<ul _ref={list_ref}>
|
||||
{self.tiles.into_iter().map(|tile_info| {
|
||||
view! {
|
||||
<li>
|
||||
{ tile_info.into_view() }
|
||||
</li>
|
||||
}
|
||||
}).collect::<Vec<_>>()}
|
||||
</ul>
|
||||
</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()
|
||||
}
|
||||
|
@ -1,27 +1,13 @@
|
||||
use leptos::leptos_dom::*;
|
||||
use leptos::*;
|
||||
|
||||
pub trait DashboardTile {
|
||||
fn image_path(&self) -> String;
|
||||
fn title(&self) -> String;
|
||||
fn link(&self) -> String;
|
||||
fn description(&self) -> Option<String> { None }
|
||||
}
|
||||
|
||||
impl IntoView for &dyn DashboardTile {
|
||||
fn into_view(self) -> View {
|
||||
let link = self.link();
|
||||
|
||||
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()
|
||||
}
|
||||
#[slot]
|
||||
pub struct DashboardTile {
|
||||
#[prop(into)]
|
||||
image_path: TextProp,
|
||||
#[prop(into)]
|
||||
title: TextProp,
|
||||
#[prop(into)]
|
||||
link: TextProp,
|
||||
#[prop(into, optional)]
|
||||
description: Option<TextProp>,
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user