update
All checks were successful
ci/woodpecker/push/test Pipeline was successful

This commit is contained in:
JMARyA 2025-02-23 00:53:33 +01:00
parent 1196f00ca7
commit a79da3d797
Signed by: jmarya
GPG key ID: 901B2ADDF27C2263
11 changed files with 84 additions and 76 deletions

View file

@ -52,15 +52,12 @@ pub trait AssetRoutes {
impl AssetRoutes for rocket::Rocket<Build> { impl AssetRoutes for rocket::Rocket<Build> {
fn mount_assets(self) -> Self { fn mount_assets(self) -> Self {
self.mount( self.mount("/", routes![
"/", crate::asset::htmx_script_route,
routes![ crate::asset::flowbite_css,
crate::asset::htmx_script_route, crate::asset::flowbite_js,
crate::asset::flowbite_css, crate::asset::material_css,
crate::asset::flowbite_js, crate::asset::material_font
crate::asset::material_css, ])
crate::asset::material_font
],
)
} }
} }

View file

@ -1,5 +1,3 @@
use maud::{Markup, Render};
use crate::auth::User; use crate::auth::User;
use crate::ui::primitives::Optional; use crate::ui::primitives::Optional;

View file

@ -4,12 +4,14 @@ use crate::ui::{UIWidget, color::UIColor};
use super::ColorCircle; use super::ColorCircle;
#[allow(non_snake_case)]
pub fn Indicator<C: UIColor + 'static>(color: C) -> PreEscaped<String> { pub fn Indicator<C: UIColor + 'static>(color: C) -> PreEscaped<String> {
html! { html! {
span class=(format!("flex w-3 h-3 me-3 bg-{} rounded-full", color.color_class())) {}; span class=(format!("flex w-3 h-3 me-3 bg-{} rounded-full", color.color_class())) {};
} }
} }
#[allow(non_snake_case)]
pub fn IndicatorLegend<C: UIColor + 'static>(color: C, legend: &str) -> PreEscaped<String> { pub fn IndicatorLegend<C: UIColor + 'static>(color: C, legend: &str) -> PreEscaped<String> {
html! { html! {
span class="flex items-center text-sm font-medium text-gray-900 dark:text-white me-3" { span class="flex items-center text-sm font-medium text-gray-900 dark:text-white me-3" {
@ -19,6 +21,7 @@ pub fn IndicatorLegend<C: UIColor + 'static>(color: C, legend: &str) -> PreEscap
} }
} }
#[allow(non_snake_case)]
pub fn NumberIndicator<T: UIWidget + 'static>(on: T, amount: u32) -> PreEscaped<String> { pub fn NumberIndicator<T: UIWidget + 'static>(on: T, amount: u32) -> PreEscaped<String> {
html! { html! {
div class="relative items-center max-w-fit" { div class="relative items-center max-w-fit" {
@ -28,6 +31,7 @@ pub fn NumberIndicator<T: UIWidget + 'static>(on: T, amount: u32) -> PreEscaped<
} }
} }
#[allow(non_snake_case)]
pub fn BadgeIndicator<C: UIColor + 'static + ColorCircle>( pub fn BadgeIndicator<C: UIColor + 'static + ColorCircle>(
color: C, color: C,
dark_color: C, dark_color: C,

View file

@ -15,7 +15,7 @@ pub mod prelude {
pub use super::appbar::AppBar; pub use super::appbar::AppBar;
pub use super::avatar::{Avatar, AvatarStack}; pub use super::avatar::{Avatar, AvatarStack};
pub use super::htmx::{ClickToLoad, InfinityScroll, LazyLoad}; pub use super::htmx::{ClickToLoad, InfinityScroll, LazyLoad};
pub use super::icon::MaterialIcon; pub use super::icon::{ColoredMaterialIcon, MaterialIcon};
pub use super::indicator::{BadgeIndicator, Indicator, IndicatorLegend, NumberIndicator}; pub use super::indicator::{BadgeIndicator, Indicator, IndicatorLegend, NumberIndicator};
pub use super::modal::{Modal, ModalCloseButton, ModalOpenButton}; pub use super::modal::{Modal, ModalCloseButton, ModalOpenButton};
pub use super::overlay::{ pub use super::overlay::{
@ -31,6 +31,7 @@ pub mod prelude {
Alignment, BottomNavigation, BottomNavigationTile, Classic, ClassicWidget, FetchToast, Alignment, BottomNavigation, BottomNavigationTile, Classic, ClassicWidget, FetchToast,
NavBar, Position, Shell, Toast, NavBar, Position, Shell, Toast,
}; };
pub use super::timeline::{ActivityLog, ActivityLogElement, Timeline, TimelineElement};
pub use super::{ pub use super::{
Accordion, Alert, Banner, Breadcrumb, Card, Carousel, CarouselMode, ColoredAlert, Accordion, Alert, Banner, Breadcrumb, Card, Carousel, CarouselMode, ColoredAlert,
ColoredSpinner, CopyText, FetchAlert, FnKey, HelpIcon, HorizontalLine, IconStepper, ColoredSpinner, CopyText, FetchAlert, FnKey, HelpIcon, HorizontalLine, IconStepper,
@ -56,12 +57,14 @@ pub fn HorizontalLine() -> PreEscaped<String> {
} }
} }
#[allow(non_snake_case)]
pub fn FnKey(key: &str) -> PreEscaped<String> { pub fn FnKey(key: &str) -> PreEscaped<String> {
html! { html! {
kbd class="px-2 py-1.5 text-xs font-semibold text-gray-800 bg-gray-100 border border-gray-200 rounded-lg dark:bg-gray-600 dark:text-gray-100 dark:border-gray-500" { (key) }; kbd class="px-2 py-1.5 text-xs font-semibold text-gray-800 bg-gray-100 border border-gray-200 rounded-lg dark:bg-gray-600 dark:text-gray-100 dark:border-gray-500" { (key) };
} }
} }
#[allow(non_snake_case)]
pub fn ColoredSpinner<T: UIColor + 'static>(color: T) -> PreEscaped<String> { pub fn ColoredSpinner<T: UIColor + 'static>(color: T) -> PreEscaped<String> {
let col = color.color_class(); let col = color.color_class();
html! { html! {
@ -80,6 +83,7 @@ pub fn Spinner() -> PreEscaped<String> {
ColoredSpinner(super::color::Blue::_600) ColoredSpinner(super::color::Blue::_600)
} }
#[allow(non_snake_case)]
pub fn CopyText(txt: &str) -> PreEscaped<String> { pub fn CopyText(txt: &str) -> PreEscaped<String> {
let id = uuid::Uuid::new_v4().to_string(); let id = uuid::Uuid::new_v4().to_string();
@ -739,12 +743,14 @@ impl UIWidget for StepperWidget {
pub struct TabWidget { pub struct TabWidget {
pub content: Vec<(PreEscaped<String>, String, Box<dyn UIWidget>)>, pub content: Vec<(PreEscaped<String>, String, Box<dyn UIWidget>)>,
pub active: String,
} }
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn Tabs() -> TabWidget { pub fn Tabs() -> TabWidget {
TabWidget { TabWidget {
content: Vec::new(), content: Vec::new(),
active: String::new(),
} }
} }
@ -759,6 +765,11 @@ impl TabWidget {
.push((tab.render(), id.to_string(), Box::new(body))); .push((tab.render(), id.to_string(), Box::new(body)));
self self
} }
pub fn active(mut self, id: &str) -> Self {
self.active = id.to_string();
self
}
} }
impl Render for TabWidget { impl Render for TabWidget {
@ -789,11 +800,11 @@ impl UIWidget for TabWidget {
@for (i, (head, id, _)) in self.content.iter().enumerate() { @for (i, (head, id, _)) in self.content.iter().enumerate() {
li class=(if i == self.content.len() { "" } else { "me-2" }) role="presentation" { li class=(if i == self.content.len() { "" } else { "me-2" }) role="presentation" {
button class="inline-block p-4 border-b-2 rounded-t-lg" button class="inline-block p-4 border-b-2 rounded-t-lg"
data-tabs-target=(format!("#{id}")) data-tabs-target=(format!("#{id}"))x
type="button" type="button"
role="tab" role="tab"
aria-controls=(id) aria-controls=(id)
aria-selected="false" { (head) }; aria-selected=(if *id == self.active { "true" } else { "false" }) { (head) };
} }
}; };
} }

View file

@ -1,7 +1,8 @@
use maud::{PreEscaped, Render, html}; use maud::{PreEscaped, html};
use crate::ui::UIWidget; use crate::ui::UIWidget;
#[allow(non_snake_case)]
pub fn ModalCloseButton<T: UIWidget + 'static>(modal: &str, inner: T) -> PreEscaped<String> { pub fn ModalCloseButton<T: UIWidget + 'static>(modal: &str, inner: T) -> PreEscaped<String> {
html! { html! {
button button
@ -10,6 +11,7 @@ pub fn ModalCloseButton<T: UIWidget + 'static>(modal: &str, inner: T) -> PreEsca
} }
} }
#[allow(non_snake_case)]
pub fn ModalOpenButton<T: UIWidget + 'static>(modal: &str, inner: T) -> PreEscaped<String> { pub fn ModalOpenButton<T: UIWidget + 'static>(modal: &str, inner: T) -> PreEscaped<String> {
html! { html! {
button button
@ -19,6 +21,7 @@ pub fn ModalOpenButton<T: UIWidget + 'static>(modal: &str, inner: T) -> PreEscap
} }
} }
#[allow(non_snake_case)]
pub fn Modal<T: UIWidget + 'static, E: UIWidget + 'static, F: FnOnce(String) -> E>( pub fn Modal<T: UIWidget + 'static, E: UIWidget + 'static, F: FnOnce(String) -> E>(
title: &str, title: &str,
body: T, body: T,
@ -26,32 +29,29 @@ pub fn Modal<T: UIWidget + 'static, E: UIWidget + 'static, F: FnOnce(String) ->
) -> (String, PreEscaped<String>) { ) -> (String, PreEscaped<String>) {
let id = uuid::Uuid::new_v4().to_string(); let id = uuid::Uuid::new_v4().to_string();
( (format!("modal-{id}"), html! {
format!("modal-{id}"), div id=(format!("modal-{id}")) tabindex="-1" aria-hidden="true" class="hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full" {
html! { div class="relative p-4 w-full max-w-2xl max-h-full" {
div id=(format!("modal-{id}")) tabindex="-1" aria-hidden="true" class="hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full" {
div class="relative p-4 w-full max-w-2xl max-h-full" {
div class="relative bg-white rounded-lg shadow dark:bg-gray-700" { div class="relative bg-white rounded-lg shadow dark:bg-gray-700" {
div class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600" { div class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600" {
h3 class="text-xl font-semibold text-gray-900 dark:text-white" { (title) } h3 class="text-xl font-semibold text-gray-900 dark:text-white" { (title) }
button type="button" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white" data-modal-hide=(format!("modal-{id}")) { button type="button" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white" data-modal-hide=(format!("modal-{id}")) {
svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14" { svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14" {
path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" {}; path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" {};
}; };
span class="sr-only" { "Close modal" }; span class="sr-only" { "Close modal" };
} }
};
div class="p-4 md:p-5 space-y-4" {
(body)
};
div class="flex items-center p-4 md:p-5 border-t border-gray-200 rounded-b dark:border-gray-600" {
(footer(format!("modal-{id}")))
};
}; };
}};
}, div class="p-4 md:p-5 space-y-4" {
) (body)
};
div class="flex items-center p-4 md:p-5 border-t border-gray-200 rounded-b dark:border-gray-600" {
(footer(format!("modal-{id}")))
};
};
}};
})
} }

View file

@ -278,13 +278,10 @@ pub fn BottomNavigationTile<T: UIWidget + 'static>(
) -> ClassicWidget<LinkWidget> { ) -> ClassicWidget<LinkWidget> {
Classic( Classic(
"inline-flex flex-col items-center justify-center px-5 hover:bg-gray-50 dark:hover:bg-gray-800 group", "inline-flex flex-col items-center justify-center px-5 hover:bg-gray-50 dark:hover:bg-gray-800 group",
Link( Link(reference, html! {
reference, (icon.map(|x| x.render()).unwrap_or_default());
html! { span class="text-sm text-gray-500 dark:text-gray-400 group-hover:text-blue-600 dark:group-hover:text-blue-500" { (text) };
(icon.map(|x| x.render()).unwrap_or_default()); }),
span class="text-sm text-gray-500 dark:text-gray-400 group-hover:text-blue-600 dark:group-hover:text-blue-500" { (text) };
},
),
) )
} }

View file

@ -38,8 +38,8 @@ pub mod prelude {
MixBlendMode, Opacity, Saturate, Sepia, MixBlendMode, Opacity, Saturate, Sepia,
}; };
pub use super::primitives::flex::{ pub use super::primitives::flex::{
AlignContent, AlignItems, Direction, DivideStyle, DivideWidth, Flex, FlexBasis, FlexGrow, AlignContent, AlignItems, Column, Direction, DivideStyle, DivideWidth, Flex, FlexBasis,
Justify, JustifyItems, Order, Strategy, Wrap, FlexGrow, Justify, JustifyItems, Order, Row, Strategy, Wrap,
}; };
pub use super::primitives::grid::{ pub use super::primitives::grid::{
Columns, Grid, GridAmount, GridAutoFlow, GridAutoSize, GridElementColumn, GridElementRow, Columns, Grid, GridAmount, GridAutoFlow, GridAutoSize, GridElementColumn, GridElementRow,
@ -53,7 +53,7 @@ pub mod prelude {
Range, Select, TextArea, TextInput, TimePicker, Toggle, Range, Select, TextArea, TextInput, TimePicker, Toggle,
}; };
pub use super::primitives::link::Link; pub use super::primitives::link::Link;
pub use super::primitives::list::{OrderedList, UnorderedList}; pub use super::primitives::list::{HorizontalList, ListEntry, OrderedList, UnorderedList};
pub use super::primitives::margin::Margin; pub use super::primitives::margin::Margin;
pub use super::primitives::padding::Padding; pub use super::primitives::padding::Padding;
pub use super::primitives::position::{ pub use super::primitives::position::{
@ -79,7 +79,9 @@ pub mod prelude {
}; };
pub use super::primitives::visibility::Visibility; pub use super::primitives::visibility::Visibility;
pub use super::primitives::width::{MaxWidth, MinWidth, Width}; pub use super::primitives::width::{MaxWidth, MinWidth, Width};
pub use super::primitives::{Context, NoBrowserAppearance, Nothing, Side, Size, script}; pub use super::primitives::{
Context, NoBrowserAppearance, Nothing, Optional, Side, Size, script,
};
pub use super::wrapper::{ pub use super::wrapper::{
_2XLScreen, Hover, LargeScreen, MediumScreen, Screen, SmallScreen, XLScreen, _2XLScreen, Hover, LargeScreen, MediumScreen, Screen, SmallScreen, XLScreen,
}; };

View file

@ -274,25 +274,21 @@ impl GridElement {
} }
pub fn span(mut self, value: GridElementValue) -> Self { pub fn span(mut self, value: GridElementValue) -> Self {
self.1.push(format!( self.1.push(format!("{}-span-{}", self.2, match value {
"{}-span-{}", GridElementValue::_1 => "1",
self.2, GridElementValue::_2 => "2",
match value { GridElementValue::_3 => "3",
GridElementValue::_1 => "1", GridElementValue::_4 => "4",
GridElementValue::_2 => "2", GridElementValue::_5 => "5",
GridElementValue::_3 => "3", GridElementValue::_6 => "6",
GridElementValue::_4 => "4", GridElementValue::_7 => "7",
GridElementValue::_5 => "5", GridElementValue::_8 => "8",
GridElementValue::_6 => "6", GridElementValue::_9 => "9",
GridElementValue::_7 => "7", GridElementValue::_10 => "10",
GridElementValue::_8 => "8", GridElementValue::_11 => "11",
GridElementValue::_9 => "9", GridElementValue::_12 => "12",
GridElementValue::_10 => "10", GridElementValue::Auto => "full",
GridElementValue::_11 => "11", }));
GridElementValue::_12 => "12",
GridElementValue::Auto => "full",
}
));
self self
} }

View file

@ -1,6 +1,4 @@
use std::{collections::HashMap, fmt::Write}; use maud::{Markup, Render, html};
use maud::{Markup, PreEscaped, Render, html};
use crate::{ use crate::{
auth::{User, csrf::CSRF}, auth::{User, csrf::CSRF},

View file

@ -93,7 +93,7 @@ impl UIWidget for TextInputWidget {
Vec::new() Vec::new()
} }
fn render_with_class(&self, class: &str) -> Markup { fn render_with_class(&self, _: &str) -> Markup {
let mut attrs = self.attrs.clone(); let mut attrs = self.attrs.clone();
if self.password { if self.password {
@ -303,6 +303,7 @@ pub struct NumberInputWidget {
buttons: bool, buttons: bool,
} }
#[allow(non_snake_case)]
pub fn NumberInput() -> NumberInputWidget { pub fn NumberInput() -> NumberInputWidget {
NumberInputWidget { NumberInputWidget {
inner: None, inner: None,

View file

@ -116,6 +116,7 @@ impl UIWidget for ListWidget {
// TODO : List data backed list + reorderable + add + remove + crud // TODO : List data backed list + reorderable + add + remove + crud
#[allow(non_snake_case)]
pub fn CheckIconRounded() -> PreEscaped<String> { pub fn CheckIconRounded() -> PreEscaped<String> {
html! { html! {
svg class="w-3.5 h-3.5 me-2 text-green-500 dark:text-green-400 shrink-0" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20" { svg class="w-3.5 h-3.5 me-2 text-green-500 dark:text-green-400 shrink-0" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20" {
@ -124,6 +125,7 @@ pub fn CheckIconRounded() -> PreEscaped<String> {
} }
} }
#[allow(non_snake_case)]
pub fn CheckIconRoundedGray() -> PreEscaped<String> { pub fn CheckIconRoundedGray() -> PreEscaped<String> {
html! { html! {
svg class="w-3.5 h-3.5 me-2 text-gray-500 dark:text-gray-400 shrink-0" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20" { svg class="w-3.5 h-3.5 me-2 text-gray-500 dark:text-gray-400 shrink-0" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20" {
@ -132,6 +134,7 @@ pub fn CheckIconRoundedGray() -> PreEscaped<String> {
} }
} }
#[allow(non_snake_case)]
pub fn ListEntry<T: UIWidget + 'static, I: UIWidget + 'static>( pub fn ListEntry<T: UIWidget + 'static, I: UIWidget + 'static>(
icon: I, icon: I,
inner: T, inner: T,
@ -144,6 +147,7 @@ pub fn ListEntry<T: UIWidget + 'static, I: UIWidget + 'static>(
} }
} }
#[allow(non_snake_case)]
pub fn CheckIcon() -> PreEscaped<String> { pub fn CheckIcon() -> PreEscaped<String> {
html! { html! {
svg class="shrink-0 w-3.5 h-3.5 text-green-500 dark:text-green-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 12" { svg class="shrink-0 w-3.5 h-3.5 text-green-500 dark:text-green-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 12" {