From 17da489229099d224f505773fbfaa043e37552ed Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Sat, 28 Sep 2024 11:18:18 -0700 Subject: [PATCH] web: WIP tailwind integration --- web/index.html | 1 + web/src/tailwind.css | 3 + web/src/view/mod.rs | 276 ++++++++++++++++++++++++----------------- web/tailwind.config.js | 9 ++ 4 files changed, 177 insertions(+), 112 deletions(-) create mode 100644 web/src/tailwind.css create mode 100644 web/tailwind.config.js diff --git a/web/index.html b/web/index.html index 3ae058a..24460e1 100644 --- a/web/index.html +++ b/web/index.html @@ -23,6 +23,7 @@ + diff --git a/web/src/tailwind.css b/web/src/tailwind.css new file mode 100644 index 0000000..b5c61c9 --- /dev/null +++ b/web/src/tailwind.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/web/src/view/mod.rs b/web/src/view/mod.rs index 3126e66..bc2bb75 100644 --- a/web/src/view/mod.rs +++ b/web/src/view/mod.rs @@ -425,7 +425,11 @@ fn render_avatar(avatar: Option, from: &str) -> Node { let h = 64; let from_color = compute_color(from); svg![ - attrs! {At::ViewBox=>format!("0 0 {w} {h}") }, + attrs! { + At::Width=>w, + At::Height=>h, + At::ViewBox=>format!("0 0 {w} {h}") + }, style! { St::Display => "block", St::FontFamily => "Poppins", @@ -460,6 +464,16 @@ fn render_avatar(avatar: Option, from: &str) -> Node { } } +fn copy_text_widget(text: &str) -> Node { + let text = text.to_string(); + span![ + i![C!["far", "fa-clone"]], + ev(Ev::Click, move |e| { + e.stop_propagation(); + Msg::CopyToClipboard(text) + }) + ] +} fn render_open_header(msg: &ShowThreadQueryThreadOnEmailThreadMessages) -> Node { let (from, from_detail) = match &msg.from { Some(ShowThreadQueryThreadOnEmailThreadMessagesFrom { @@ -477,127 +491,166 @@ fn render_open_header(msg: &ShowThreadQueryThreadOnEmailThreadMessages) -> Node< let id = msg.id.clone(); let is_unread = has_unread(&msg.tags); let img = render_avatar(avatar, &from); - article![ - C!["media"], - figure![C!["media-left"], p![C!["image", "is-64x64"], img]], - div![ - C!["media-content"], + div![ + figure![ + C!["flex"], + div![C!["w-16", "h-16", "mx-auto", "h-auto"], &img], div![ - C!["content"], - p![ - strong![from], - br![], - small![ - &from_detail, + C!["px-4"], + &from, + " ", + from_detail.as_ref().map(|text| copy_text_widget(&text)), + IF!(!msg.to.is_empty() =>div![ + "To: ", + msg.to.iter().map(|to| { + let ShowThreadQueryThreadOnEmailThreadMessagesTo { name, addr } = to; + span![ + C!["text-nowrap"], + &name, " ", - from_detail.map(|detail| span![ + span![ i![C!["far", "fa-clone"]], - ev(Ev::Click, move |e| { + addr.clone().map(|addr| ev(Ev::Click, move |e| { e.stop_propagation(); - Msg::CopyToClipboard(detail.to_string()) - }) - ]) - ], - table![ - IF!(!msg.to.is_empty() => - tr![ - td![ "To:" ], - //td![ if i==0 { "To" }else { "" } ], - td![ - msg.to.iter().enumerate().map(|(i, to)| - small![ - if i>0 { ", " }else { "" }, - { - let to = match to { - ShowThreadQueryThreadOnEmailThreadMessagesTo { - name: Some(name), - addr:Some(addr), - } => format!("{name} <{addr}>"), - ShowThreadQueryThreadOnEmailThreadMessagesTo { - name: Some(name), - addr:None - } => format!("{name}"), - ShowThreadQueryThreadOnEmailThreadMessagesTo { - addr: Some(addr), .. - } => format!("{addr}"), - _ => String::from("UNKNOWN"), - }; - span![ - &to, " ", + Msg::CopyToClipboard(addr) + })) + ], + " " + ] + })]), + IF!(!msg.cc.is_empty() =>div![ + "CC: ", + msg.cc.iter().map(|cc| { + let ShowThreadQueryThreadOnEmailThreadMessagesCc { name, addr } = cc; + span![ + C!["text-nowrap"], + &name, + " ", + span![ + i![C!["far", "fa-clone"]], + addr.clone().map(|addr| ev(Ev::Click, move |e| { + e.stop_propagation(); + Msg::CopyToClipboard(addr) + })) + ], + " " + ] + })]), + msg.timestamp.map(|ts| span![C!["header"], human_age(ts)]), + span![ + C!["read-status"], + i![C![ + "far", + if is_unread { + "fa-envelope" + } else { + "fa-envelope-open" + }, + ]], + { + // TODO remove clone and code block when removing article![] below + let id = id.clone(); + ev(Ev::Click, move |e| { + e.stop_propagation(); + Msg::SetUnread(id, !is_unread) + }) + } + ] + ] + ], + hr![], + article![ + C!["media"], + figure![C!["media-left"], p![C!["image", "is-64x64"], img]], + div![ + C!["media-content"], + div![ + C!["content"], + p![ + //C!["text-nowrap"], + strong![from], + " ", + from_detail.map(|text| copy_text_widget(&text)), + table![ + IF!(!msg.to.is_empty() => + tr![ + td![ "To:" ], + //td![ if i==0 { "To" }else { "" } ], + td![ + msg.to.iter().map(|to| + small![ + " ", + { + let ShowThreadQueryThreadOnEmailThreadMessagesTo{name, addr} = to; span![ - i![C!["far", "fa-clone"]], - ev(Ev::Click, move |e| { - e.stop_propagation(); - Msg::CopyToClipboard(to) - }) + //C!["text-nowrap"], + &name, " ", + span![ + i![C!["far", "fa-clone"]], + addr.clone().map(|addr| ev(Ev::Click, + move |e| { + e.stop_propagation(); + Msg::CopyToClipboard(addr) + } + )) + ] ] - ] - } - - ]) - ] - ]), - IF!(!msg.cc.is_empty() => - tr![ - td![ "CC:" ], - td![ - msg.cc.iter().enumerate().map(|(i, cc)| - small![ - if i>0 { ", " }else { "" }, - { - let cc = match cc { - ShowThreadQueryThreadOnEmailThreadMessagesCc { - name: Some(name), - addr:Some(addr), - } => format!("{name} <{addr}>"), - ShowThreadQueryThreadOnEmailThreadMessagesCc { - name: Some(name), - addr:None - } => format!("{name}"), - ShowThreadQueryThreadOnEmailThreadMessagesCc { - addr: Some(addr), .. - } => format!("<{addr}>"), - _ => String::from("UNKNOWN"), - }; - span![ - &cc, " ", + } + ]) + ] + ]), + IF!(!msg.cc.is_empty() => + tr![ + td![ "CC:" ], + td![ + msg.cc.iter().map(|cc| + small![ + " ", + { + let ShowThreadQueryThreadOnEmailThreadMessagesCc{name, addr} = cc; span![ - i![C!["far", "fa-clone"]], - ev(Ev::Click, move |e| { - e.stop_propagation(); - Msg::CopyToClipboard(cc) - }) + //C!["text-nowrap"], + &name, " ", + span![ + i![C!["far", "fa-clone"]], + addr.clone().map(|addr| ev(Ev::Click, + move |e| { + e.stop_propagation(); + Msg::CopyToClipboard(addr) + } + )) + ] ] - ] - } - ]) - ] - ]), - tr![td![ - attrs! {At::ColSpan=>2}, - msg.timestamp.map(|ts| span![C!["header"], human_age(ts)]) - ]] + } + ]) + ] + ]), + tr![td![ + attrs! {At::ColSpan=>2}, + msg.timestamp.map(|ts| span![C!["header"], human_age(ts)]) + ]] + ], ], ], ], - ], - div![ - C!["media-right"], - span![ - C!["read-status"], - i![C![ - "far", - if is_unread { - "fa-envelope" - } else { - "fa-envelope-open" - }, - ]] - ], - ev(Ev::Click, move |e| { - e.stop_propagation(); - Msg::SetUnread(id, !is_unread) - }) + div![ + C!["media-right"], + span![ + C!["read-status"], + i![C![ + "far", + if is_unread { + "fa-envelope" + } else { + "fa-envelope-open" + }, + ]] + ], + ev(Ev::Click, move |e| { + e.stop_propagation(); + Msg::SetUnread(id, !is_unread) + }) + ] ] ] } @@ -1127,7 +1180,6 @@ fn news_post( } fn render_news_post_header(post: &ShowThreadQueryThreadOnNewsPost) -> Node { let from = &post.site; - let from_detail = post.url.clone(); let avatar: Option = None; //let avatar: Option = Some(String::from("https://bulma.io/images/placeholders/64x64.png")); let id = post.thread_id.clone(); diff --git a/web/tailwind.config.js b/web/tailwind.config.js new file mode 100644 index 0000000..c3481c0 --- /dev/null +++ b/web/tailwind.config.js @@ -0,0 +1,9 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ['./src/**/*.rs'], + theme: { + extend: {}, + }, + plugins: [], +} +