From 472b33688d373b1325b9ae4184eba83207a327db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Volf?= Date: Tue, 27 Jan 2026 11:03:06 +0100 Subject: [PATCH] temp --- assets/manifest.json | 4 +- assets/styles/input_range.css | 52 ++---- assets/styles/select_arrow.css | 9 + src/components/app.rs | 4 +- src/components/bottom_panel.rs | 6 +- src/components/category_input.rs | 117 ++++--------- src/components/category_today_task_list.rs | 32 ++-- src/components/error_boundary_message.rs | 1 + src/components/form_open_button.rs | 14 +- src/components/input.rs | 42 +++++ src/components/input_label.rs | 21 +++ src/components/mod.rs | 5 +- src/components/navigation.rs | 24 +-- src/components/navigation_item.rs | 18 +- src/components/project_select.rs | 2 +- src/components/reoccurrence_input.rs | 86 ---------- src/components/reoccurrence_interval_input.rs | 44 +++++ src/components/select_button.rs | 33 ++++ src/components/task_form.rs | 157 ++++++------------ src/components/task_list.rs | 17 +- src/layouts/main.rs | 4 +- src/views/category_inbox_page.rs | 5 + tailwind.css | 4 + 23 files changed, 332 insertions(+), 369 deletions(-) create mode 100644 assets/styles/select_arrow.css create mode 100644 src/components/input.rs create mode 100644 src/components/input_label.rs delete mode 100644 src/components/reoccurrence_input.rs create mode 100644 src/components/reoccurrence_interval_input.rs create mode 100644 src/components/select_button.rs diff --git a/assets/manifest.json b/assets/manifest.json index ee671f6..a0062c6 100644 --- a/assets/manifest.json +++ b/assets/manifest.json @@ -3,8 +3,8 @@ "short_name": "Todo Baggins", "start_url": "/", "display": "standalone", - "background_color": "#27272a", - "theme_color": "#27272a", + "background_color": "#101828", + "theme_color": "#b89a2e", "icons": [ { "src": "/assets/images/icon.png", diff --git a/assets/styles/input_range.css b/assets/styles/input_range.css index 555e054..a9338cd 100644 --- a/assets/styles/input_range.css +++ b/assets/styles/input_range.css @@ -8,56 +8,32 @@ input[type="range"] { background: transparent; } -input[type="range"]::-moz-range-thumb { +input[type="range"]::-moz-range-thumb, input[type="range"]::-webkit-slider-thumb { width: 1.25rem; height: 1.25rem; - background: rgba(228 228 231); + background: var(--color-gray-400); + filter: drop-shadow(0 var(--spacing) 0 var(--color-gray-500)); border: 0; border-radius: 0.5rem; } -input[type="range"]::-moz-range-progress { - background: #525259; +input[type="range"]::-webkit-slider-thumb { + position: relative; + top: -9px; +} + +input[type="range"]::-moz-range-track, input[type="range"]::-webkit-slider-runnable-track { + background: var(--color-gray-800-muted); height: 0.5rem; + filter: drop-shadow(0 calc(0px - var(--spacing)) 0 var(--color-gray-900-muted)); border-radius: 0.25rem; } input[type="range"]::-moz-range-track { - background: rgba(39 39 42 / 50%); - height: 0.5rem; - border-radius: 0.25rem; -} - -input[type="range"].input-range-reverse::-moz-range-progress { - background: #2d2d31; - height: 0.5rem; - border-radius: 0.25rem; -} - -input[type="range"].input-range-reverse::-moz-range-track { - background: rgba(113 113 122 / 50%); - height: 0.5rem; - border-radius: 0.25rem; -} - -input[type="range"]::-webkit-slider-thumb { - width: 1.25rem; - height: 1.25rem; - background: rgba(228 228 231); - border: 0; - border-radius: 0.5rem; - position: relative; - top: -0.4rem; + transform: translateY(3px); } input[type="range"]::-webkit-slider-runnable-track { - background: rgba(39 39 42 / 50%); - height: 0.5rem; - border-radius: 0.25rem; -} - -input[type="range"].input-range-reverse::-webkit-slider-runnable-track { - background: rgba(39 39 42 / 50%); - height: 0.5rem; - border-radius: 0.25rem; + position: relative; + top: 3px; } diff --git a/assets/styles/select_arrow.css b/assets/styles/select_arrow.css new file mode 100644 index 0000000..cbb88f0 --- /dev/null +++ b/assets/styles/select_arrow.css @@ -0,0 +1,9 @@ +select { + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 640'%3E%3Cpath fill='%239ca3af' d='M300.3 440.8C312.9 451 331.4 450.3 343.1 438.6L471.1 310.6C480.3 301.4 483 287.7 478 275.7C473 263.7 461.4 256 448.5 256L192.5 256C179.6 256 167.9 263.8 162.9 275.8C157.9 287.8 160.7 301.5 169.9 310.6L297.9 438.6L300.3 440.8z'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-size: 2rem; + background-position: right .5rem center; +} diff --git a/src/components/app.rs b/src/components/app.rs index 553ee51..9bbf89b 100644 --- a/src/components/app.rs +++ b/src/components/app.rs @@ -15,6 +15,7 @@ static FONTS_DIRECTORY: Asset = asset!( const TAILWIND_CSS: Asset = asset!("/assets/tailwind.css"); const INPUT_NUMBER_ARROWS_CSS: Asset = asset!("/assets/styles/input_number_arrows.css"); const INPUT_RANGE_CSS: Asset = asset!("/assets/styles/input_range.css"); +const SELECT_ARROW_CSS: Asset = asset!("/assets/styles/select_arrow.css"); const MANIFEST: Asset = asset!("/assets/manifest.json"); #[component] @@ -36,10 +37,11 @@ pub(crate) fn App() -> Element { document::Stylesheet { href: TAILWIND_CSS } document::Stylesheet { href: INPUT_NUMBER_ARROWS_CSS } document::Stylesheet { href: INPUT_RANGE_CSS } + document::Stylesheet { href: SELECT_ARROW_CSS } document::Link { rel: "manifest", href: MANIFEST, crossorigin: "use-credentials" } div { - class: "min-h-screen pt-4 pb-36 flex flex-col text-zinc-200 bg-zinc-800", + class: "min-h-screen pt-4 pb-36 flex flex-col text-gray-300 bg-gray-900", Router:: {} } } diff --git a/src/components/bottom_panel.rs b/src/components/bottom_panel.rs index 7738415..6d2f0b0 100644 --- a/src/components/bottom_panel.rs +++ b/src/components/bottom_panel.rs @@ -38,9 +38,9 @@ pub(crate) fn BottomPanel(display_form: Signal) -> Element { rsx! { div { class: format!( - "flex flex-col pointer-events-auto bg-zinc-700/50 rounded-t-xl border-t-zinc-600 border-t backdrop-blur drop-shadow-[0_-5px_10px_rgba(0,0,0,0.2)] transition-[height] duration-[500ms] ease-[cubic-bezier(0.79,0.14,0.15,0.86)] overflow-y-scroll {}", + "flex flex-col pointer-events-auto bg-gray-800 transition-[height] duration-[500ms] ease-[cubic-bezier(0.79,0.14,0.15,0.86)] overflow-y-scroll {}", match (display_form(), current_route, navigation_expanded()) { - (false, _, false) => "h-[64px]", + (false, _, false) => "h-[66px]", (false, _, true) => "h-[130px]", (true, Route::ProjectsPage, _) => "h-[130px]", (true, _, _) => "h-[506px]", @@ -71,7 +71,7 @@ pub(crate) fn BottomPanel(display_form: Signal) -> Element { } } else { Navigation { - expanded: navigation_expanded, + is_expanded: navigation_expanded, } } } diff --git a/src/components/category_input.rs b/src/components/category_input.rs index b86f805..128a60a 100644 --- a/src/components/category_input.rs +++ b/src/components/category_input.rs @@ -1,10 +1,11 @@ +use crate::components::select_button::SelectButton; use crate::models::category::Category; use dioxus::core_macro::rsx; use dioxus::dioxus_core::Element; use dioxus::prelude::*; use dioxus_free_icons::Icon; use dioxus_free_icons::icons::fa_solid_icons::{ - FaCalendarDays, FaForward, FaHourglassHalf, FaInbox, FaQuestion, FaWater, + FaCalendarDays, FaForward, FaHourglassHalf, FaInbox, FaQuestion, FaSignsPost, FaWater }; #[component] @@ -14,105 +15,51 @@ pub(crate) fn CategoryInput( ) -> Element { rsx! { div { - class: format!("flex flex-row gap-2 {}", class.unwrap_or("")), - button { - r#type: "button", - class: format!( - "py-3 flex flex-row justify-center items-center rounded-lg grow basis-0 {} cursor-pointer", - if selected_category() == Category::SomedayMaybe { "bg-zinc-500/50" } - else { "bg-zinc-800/50" } - ), - onclick: move |_| { + class: format!("grid grid-cols-3 gap-3 {}", class.unwrap_or("")), + SelectButton { + icon: FaQuestion, + is_selected: matches!(selected_category(), Category::SomedayMaybe), + on_select: move |_| { selected_category.set(Category::SomedayMaybe); - }, - Icon { - icon: FaQuestion, - height: 16, - width: 16 } - }, - button { - r#type: "button", - class: format!( - "py-3 flex flex-row justify-center items-center rounded-lg grow basis-0 {} cursor-pointer", - if selected_category() == Category::LongTerm { "bg-zinc-500/50" } - else { "bg-zinc-800/50" } - ), - onclick: move |_| { + } + SelectButton { + icon: FaWater, + is_selected: matches!(selected_category(), Category::LongTerm), + on_select: move |_| { selected_category.set(Category::LongTerm); - }, - Icon { - icon: FaWater, - height: 16, - width: 16 } - }, - button { - r#type: "button", - class: format!( - "py-3 flex flex-row justify-center items-center rounded-lg grow basis-0 {} cursor-pointer", - if let Category::WaitingFor(_) = selected_category() { "bg-zinc-500/50" } - else { "bg-zinc-800/50" } - ), - onclick: move |_| { + } + SelectButton { + icon: FaHourglassHalf, + is_selected: matches!(selected_category(), Category::WaitingFor(_)), + on_select: move |_| { selected_category.set(Category::WaitingFor(String::new())); - }, - Icon { - icon: FaHourglassHalf, - height: 16, - width: 16 } - }, - button { - r#type: "button", - class: format!( - "py-3 flex flex-row justify-center items-center rounded-lg grow basis-0 {} cursor-pointer", - if selected_category() == Category::NextSteps { "bg-zinc-500/50" } - else { "bg-zinc-800/50" } - ), - onclick: move |_| { + } + SelectButton { + icon: FaSignsPost, + is_selected: matches!(selected_category(), Category::NextSteps), + on_select: move |_| { selected_category.set(Category::NextSteps); - }, - Icon { - icon: FaForward, - height: 16, - width: 16 } - }, - button { - r#type: "button", - class: format!( - "py-3 flex flex-row justify-center items-center rounded-lg grow basis-0 {} cursor-pointer", - if let Category::Calendar { .. } = selected_category() { "bg-zinc-500/50" } - else { "bg-zinc-800/50" } - ), - onclick: move |_| { + } + SelectButton { + icon: FaCalendarDays, + is_selected: matches!(selected_category(), Category::Calendar { .. }), + on_select: move |_| { selected_category.set(Category::Calendar { date: chrono::Local::now().date_naive(), reoccurrence: None, time: None, }); - }, - Icon { - icon: FaCalendarDays, - height: 16, - width: 16 } - }, - button { - r#type: "button", - class: format!( - "py-3 flex flex-row justify-center items-center rounded-lg grow basis-0 {} cursor-pointer", - if selected_category() == Category::Inbox { "bg-zinc-500/50" } - else { "bg-zinc-800/50" } - ), - onclick: move |_| { + } + SelectButton { + icon: FaInbox, + is_selected: matches!(selected_category(), Category::Inbox), + on_select: move |_| { selected_category.set(Category::Inbox); - }, - Icon { - icon: FaInbox, - height: 16, - width: 16 } } } diff --git a/src/components/category_today_task_list.rs b/src/components/category_today_task_list.rs index 0cb6e0e..772847c 100644 --- a/src/components/category_today_task_list.rs +++ b/src/components/category_today_task_list.rs @@ -46,29 +46,31 @@ pub(crate) fn CategoryTodayTaskList() -> Element { rsx! { div { class: "pt-4 flex flex-col gap-8", - div { - class: "flex flex-col gap-4", + if !long_term_tasks.is_empty() { div { - class: "px-7 flex flex-row items-center gap-2 font-bold", - Icon { - class: "mx-1", - icon: FaWater - } + class: "flex flex-col gap-4", div { - {t!("long-term")._upper_first()} + class: "px-7 flex flex-row items-center gap-2 text-gray-500 font-bold", + Icon { + class: "mx-1.5", + icon: FaWater + } + div { + {t!("long-term")._upper_first()} + } + } + TaskList { + tasks: long_term_tasks } - } - TaskList { - tasks: long_term_tasks } } if !overdue_tasks.is_empty() { div { class: "flex flex-col gap-4", div { - class: "px-7 flex flex-row items-center gap-2 font-bold", + class: "px-7 flex flex-row items-center gap-2 text-gray-500 font-bold", Icon { - class: "mx-1", + class: "mx-1.25", height: 22, width: 22, icon: FaCalendarXmark @@ -86,9 +88,9 @@ pub(crate) fn CategoryTodayTaskList() -> Element { div { class: "flex flex-col gap-4", div { - class: "px-7 flex flex-row items-center gap-2 font-bold", + class: "px-7 flex flex-row items-center gap-2 text-gray-500 font-bold", Icon { - class: "mx-1", + class: "mx-1.25", height: 22, width: 22, icon: FaCalendarCheck diff --git a/src/components/error_boundary_message.rs b/src/components/error_boundary_message.rs index 4beb4d4..6beb3ac 100644 --- a/src/components/error_boundary_message.rs +++ b/src/components/error_boundary_message.rs @@ -14,6 +14,7 @@ pub(crate) fn ErrorBoundaryMessage(children: Element, class: Option) -> class: "grow flex flex-col justify-center items-center", div { Icon { + class: "text-gray-500", icon: FaTriangleExclamation, height: 32, width: 32 diff --git a/src/components/form_open_button.rs b/src/components/form_open_button.rs index 253e1c4..6142867 100644 --- a/src/components/form_open_button.rs +++ b/src/components/form_open_button.rs @@ -3,25 +3,25 @@ use crate::models::task::Task; use dioxus::prelude::*; use dioxus_free_icons::{ Icon, - icons::fa_solid_icons::{FaPlus, FaXmark}, + icons::fa_solid_icons::{FaGavel, FaPlus, FaXmark}, }; #[component] -pub(crate) fn FormOpenButton(opened: Signal) -> Element { +pub(crate) fn FormOpenButton(is_opened: Signal) -> Element { let mut project_being_edited = use_context::>>(); let mut task_being_edited = use_context::>>(); rsx! { button { - class: "pointer-events-auto m-4 py-4 px-5 self-end text-center bg-zinc-300/50 rounded-xl border-t-zinc-200 border-t backdrop-blur drop-shadow-[0_-5px_10px_rgba(0,0,0,0.2)] text-2xl text-zinc-200 cursor-pointer", + class: "pointer-events-auto m-4 p-4 self-end bg-amber-300-muted drop-shadow-[0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted)] rounded-full text-amber-700-muted cursor-pointer", onclick: move |_| { - if opened() { + if is_opened() { project_being_edited.set(None); task_being_edited.set(None); } - opened.set(!opened()); + is_opened.set(!is_opened()); }, - if opened() { + if is_opened() { Icon { icon: FaXmark, height: 24, @@ -29,7 +29,7 @@ pub(crate) fn FormOpenButton(opened: Signal) -> Element { } } else { Icon { - icon: FaPlus, + icon: FaGavel, height: 24, width: 24 } diff --git a/src/components/input.rs b/src/components/input.rs new file mode 100644 index 0000000..72e434a --- /dev/null +++ b/src/components/input.rs @@ -0,0 +1,42 @@ +use dioxus::prelude::*; + +#[component] +pub(crate) fn Input( + name: String, + required: Option, + disabled: Option, + initial_value: Option, + class: Option, + r#type: String, + inputmode: Option, + min: Option, + autofocus: Option, + oninput: Option>>, +) -> Element { + rsx! { + input { + name: name.clone(), + required, + disabled, + initial_value, + class: format!( + "pt-3 pb-2.25 {} grow bg-gray-800-muted drop-shadow-[0_calc(0px_-_var(--spacing))_0_var(--color-gray-900-muted)] rounded-xl {}", + match r#type.as_str() { + "date" => "ps-3.25 pe-3", + _ => "px-4" + }, + class.unwrap_or("".to_owned()) + ), + r#type, + inputmode, + min, + autofocus, + id: "input_{name}", + oninput: move |event| { + if let Some(oninput) = oninput { + oninput.call(event); + } + } + } + } +} diff --git a/src/components/input_label.rs b/src/components/input_label.rs new file mode 100644 index 0000000..6f5952c --- /dev/null +++ b/src/components/input_label.rs @@ -0,0 +1,21 @@ +use dioxus::prelude::*; +use dioxus_free_icons::{Icon, IconShape}; + +#[component] +pub(crate) fn InputLabel( + icon: I, + r#for: Option +) -> Element { + rsx! { + label { + r#for, + class: "mt-0.5 min-w-6 flex flex-row justify-center items-center", + Icon { + class: "text-gray-600", + icon, + height: 16, + width: 16 + } + } + } +} diff --git a/src/components/mod.rs b/src/components/mod.rs index 81d8ae0..6deb488 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -5,12 +5,15 @@ pub(crate) mod category_input; pub(crate) mod category_today_task_list; pub(crate) mod error_boundary_message; pub(crate) mod form_open_button; +pub(crate) mod input; +pub(crate) mod input_label; pub(crate) mod navigation; pub(crate) mod navigation_item; pub(crate) mod project_form; pub(crate) mod project_list; pub(crate) mod project_select; -pub(crate) mod reoccurrence_input; +pub(crate) mod reoccurrence_interval_input; +pub(crate) mod select_button; pub(crate) mod sticky_bottom; pub(crate) mod subtasks_form; pub(crate) mod task_form; diff --git a/src/components/navigation.rs b/src/components/navigation.rs index 4c01216..caeb75d 100644 --- a/src/components/navigation.rs +++ b/src/components/navigation.rs @@ -8,21 +8,25 @@ use dioxus_free_icons::icons::fa_solid_icons::{ }; #[component] -pub(crate) fn Navigation(expanded: Signal) -> Element { +pub(crate) fn Navigation(is_expanded: Signal) -> Element { rsx! { div { class: "grid grid-cols-5 justify-stretch", button { class: format!( - "py-5 flex flex-row justify-center items-center {} cursor-pointer", - if expanded() { "text-zinc-200" } - else { "text-zinc-500" } + "py-2 flex flex-row justify-center items-center cursor-pointer", ), - onclick: move |_| expanded.set(!expanded()), - Icon { - icon: FaBars, - height: 24, - width: 24 + onclick: move |_| is_expanded.set(!is_expanded()), + div { + class: format!("pt-2.5 px-4 {} transition-all duration-150", + if is_expanded() { "pb-2 mt-1 bg-gray-900 text-gray-400 rounded-xl drop-shadow-[0_calc(0px_-_var(--spacing))_0_var(--color-gray-950)]" } + else { "pb-3 bg-gray-800 rounded-xl drop-shadow-[0_0_0_var(--color-gray-950)] text-gray-600" } + ), + Icon { + icon: FaBars, + height: 24, + width: 24 + } } }, NavigationItem { @@ -41,7 +45,7 @@ pub(crate) fn Navigation(expanded: Signal) -> Element { route: Route::CategoryInboxPage, icon: FaInbox }, - {if expanded() { + {if is_expanded() { rsx! { NavigationItem { route: Route::ProjectsPage, diff --git a/src/components/navigation_item.rs b/src/components/navigation_item.rs index 39c139e..f14674e 100644 --- a/src/components/navigation_item.rs +++ b/src/components/navigation_item.rs @@ -13,14 +13,18 @@ pub(crate) fn NavigationItem( Link { to: route.clone(), class: format!( - "py-5 flex flex-row justify-center items-center {}", - if current_route == route { "text-zinc-200" } - else { "text-zinc-500" } + "py-2.5 flex flex-row justify-center items-center", ), - Icon { - icon, - height: 24, - width: 24 + div { + class: format!("pt-2.5 px-4 {} transition-all duration-150", + if current_route == route { "pb-2 mt-1 bg-gray-900 text-gray-400 rounded-xl drop-shadow-[0_calc(0px_-_var(--spacing))_0_var(--color-gray-950)]" } + else { "pb-3 bg-gray-800 rounded-xl drop-shadow-[0_0_0_var(--color-gray-950)] text-gray-600" } + ), + Icon { + icon, + height: 24, + width: 24 + } } } } diff --git a/src/components/project_select.rs b/src/components/project_select.rs index fc3021e..ff39aaa 100644 --- a/src/components/project_select.rs +++ b/src/components/project_select.rs @@ -10,7 +10,7 @@ pub(crate) fn ProjectSelect(initial_selected_id: Option) -> Element { rsx! { select { name: "project_id", - class: "px-3.5 py-2.5 bg-zinc-800/50 rounded-lg grow cursor-pointer", + class: "px-4 pt-3 pb-2.25 bg-gray-800-muted drop-shadow-[0_calc(0px_-_var(--spacing))_0_var(--color-gray-900-muted)] rounded-xl grow cursor-pointer", id: "input_project", option { value: 0, diff --git a/src/components/reoccurrence_input.rs b/src/components/reoccurrence_input.rs deleted file mode 100644 index 9f84b86..0000000 --- a/src/components/reoccurrence_input.rs +++ /dev/null @@ -1,86 +0,0 @@ -use crate::models::category::ReoccurrenceInterval; -use dioxus::core_macro::rsx; -use dioxus::dioxus_core::Element; -use dioxus::prelude::*; -use dioxus_free_icons::Icon; -use dioxus_free_icons::icons::fa_solid_icons::{FaBan, FaEarthEurope, FaMoon, FaSun}; - -#[component] -pub(crate) fn ReoccurrenceIntervalInput( - reoccurrence_interval: Signal>, - class_buttons: Option<&'static str>, -) -> Element { - rsx! { - button { - r#type: "button", - class: format!( - "py-2 flex flex-row justify-center items-center rounded-lg {} {} cursor-pointer", - class_buttons.unwrap_or(""), - if reoccurrence_interval().is_none() { "bg-zinc-500/50" } - else { "bg-zinc-800/50" } - ), - onclick: move |_| { - reoccurrence_interval.set(None); - }, - Icon { - icon: FaBan, - height: 16, - width: 16 - } - }, - button { - r#type: "button", - class: format!( - "py-2 flex flex-row justify-center items-center rounded-lg {} {} cursor-pointer", - class_buttons.unwrap_or(""), - if let Some(ReoccurrenceInterval::Day) = reoccurrence_interval() - { "bg-zinc-500/50" } - else { "bg-zinc-800/50" } - ), - onclick: move |_| { - reoccurrence_interval.set(Some(ReoccurrenceInterval::Day)) - }, - Icon { - icon: FaSun, - height: 16, - width: 16 - } - }, - button { - r#type: "button", - class: format!( - "py-2 flex flex-row justify-center items-center rounded-lg {} {} cursor-pointer", - class_buttons.unwrap_or(""), - if let Some(ReoccurrenceInterval::Month) = reoccurrence_interval() - { "bg-zinc-500/50" } - else { "bg-zinc-800/50" } - ), - onclick: move |_| { - reoccurrence_interval.set(Some(ReoccurrenceInterval::Month)) - }, - Icon { - icon: FaMoon, - height: 16, - width: 16 - } - }, - button { - r#type: "button", - class: format!( - "py-2 flex flex-row justify-center items-center rounded-lg {} {} cursor-pointer", - class_buttons.unwrap_or(""), - if let Some(ReoccurrenceInterval::Year) = reoccurrence_interval() - { "bg-zinc-500/50" } - else { "bg-zinc-800/50" } - ), - onclick: move |_| { - reoccurrence_interval.set(Some(ReoccurrenceInterval::Year)) - }, - Icon { - icon: FaEarthEurope, - height: 16, - width: 16 - } - } - } -} diff --git a/src/components/reoccurrence_interval_input.rs b/src/components/reoccurrence_interval_input.rs new file mode 100644 index 0000000..a4b00dd --- /dev/null +++ b/src/components/reoccurrence_interval_input.rs @@ -0,0 +1,44 @@ +use crate::components::select_button::SelectButton; +use crate::models::category::ReoccurrenceInterval; +use dioxus::core_macro::rsx; +use dioxus::dioxus_core::Element; +use dioxus::prelude::*; +use dioxus_free_icons::icons::fa_solid_icons::{FaBan, FaEarthEurope, FaMoon, FaSun}; + +#[component] +pub(crate) fn ReoccurrenceIntervalInput( + reoccurrence_interval: Signal>, + class_buttons: Option<&'static str>, +) -> Element { + rsx! { + // TODO: Abstract into SelectButton. Make it sank into the surface by default, like other inputs (abstract those too haha), and rise it up on selection (rationale: it will influence what is on the surface). + SelectButton { + icon: FaBan, + is_selected: reoccurrence_interval().is_none(), + on_select: move |_| { + reoccurrence_interval.set(None); + } + } + SelectButton { + icon: FaSun, + is_selected: matches!(reoccurrence_interval(), Some(ReoccurrenceInterval::Day)), + on_select: move |_| { + reoccurrence_interval.set(Some(ReoccurrenceInterval::Day)) + } + } + SelectButton { + icon: FaMoon, + is_selected: matches!(reoccurrence_interval(), Some(ReoccurrenceInterval::Month)), + on_select: move |_| { + reoccurrence_interval.set(Some(ReoccurrenceInterval::Month)); + } + } + SelectButton { + icon: FaEarthEurope, + is_selected: matches!(reoccurrence_interval(), Some(ReoccurrenceInterval::Year)), + on_select: move |_| { + reoccurrence_interval.set(Some(ReoccurrenceInterval::Year)); + } + } + } +} diff --git a/src/components/select_button.rs b/src/components/select_button.rs new file mode 100644 index 0000000..b858537 --- /dev/null +++ b/src/components/select_button.rs @@ -0,0 +1,33 @@ +use crate::models::project::Project; +use crate::models::task::Task; +use dioxus::prelude::*; +use dioxus_free_icons::{ + Icon, IconShape, + icons::fa_solid_icons::{FaGavel, FaPlus, FaXmark}, +}; + +#[component] +pub(crate) fn SelectButton( + icon: I, + is_selected: bool, + on_select: Callback, +) -> Element { + rsx! { + button { + r#type: "button", + class: format!( + "pt-4.5 flex flex-row justify-center items-center {} rounded-xl transition-all duration-150", + if is_selected { "pb-3.75 bg-gray-900 drop-shadow-[0_0_0_var(--color-gray-900-muted)]" } + else { "pb-2.75 mt-1 bg-gray-800-muted drop-shadow-[0_calc(0px_-_var(--spacing))_0_var(--color-gray-900-muted)] text-gray-400 cursor-pointer" } + ), + onclick: move |_| { + on_select.call(()); + }, + Icon { + icon, + height: 16, + width: 16 + } + }, + } +} diff --git a/src/components/task_form.rs b/src/components/task_form.rs index 7d85bd3..3de6c92 100644 --- a/src/components/task_form.rs +++ b/src/components/task_form.rs @@ -1,6 +1,8 @@ use crate::components::category_input::CategoryInput; +use crate::components::input::Input; +use crate::components::input_label::InputLabel; use crate::components::project_select::ProjectSelect; -use crate::components::reoccurrence_input::ReoccurrenceIntervalInput; +use crate::components::reoccurrence_interval_input::ReoccurrenceIntervalInput; use crate::components::subtasks_form::SubtasksForm; use crate::models::category::{CalendarTime, Category, Reoccurrence}; use crate::models::task::NewTask; @@ -13,8 +15,8 @@ use dioxus::dioxus_core::Element; use dioxus::prelude::*; use dioxus_free_icons::Icon; use dioxus_free_icons::icons::fa_solid_icons::{ - FaBell, FaBomb, FaClock, FaFloppyDisk, FaHourglassEnd, FaLayerGroup, FaList, FaPenClip, - FaRepeat, FaTrashCan, + FaBell, FaBomb, FaClock, FaFeatherPointed, FaFloppyDisk, FaHourglassEnd, FaLayerGroup, FaList, + FaPenClip, FaRepeat, FaScroll, FaTrashCan, FaVolcano, FaXmark, }; use dioxus_i18n::t; use serde::{Deserialize, Serialize}; @@ -108,9 +110,9 @@ pub(crate) fn TaskForm(task: Option, on_successful_submit: EventHandler<() rsx! { div { - class: "p-4 flex flex-col gap-4", + class: "p-4 flex flex-col gap-10", form { - class: "flex flex-col gap-4", + class: "flex flex-col gap-8", id: "form_task", onsubmit: move |event| { event.prevent_default(); @@ -162,46 +164,33 @@ pub(crate) fn TaskForm(task: Option, on_successful_submit: EventHandler<() }, div { class: "flex flex-row items-center gap-3", - label { + InputLabel { r#for: "input_title", - class: "min-w-6 flex flex-row justify-center items-center", - Icon { - class: "text-zinc-400/50", - icon: FaPenClip, - height: 16, - width: 16 - } + icon: FaFeatherPointed }, - input { + Input { name: "title", required: true, initial_value: task.as_ref().map(|task| task.title.clone()), r#type: "text", - class: "py-2 px-3 grow bg-zinc-800/50 rounded-lg", - id: "input_title" - }, + autofocus: true + } }, div { class: "flex flex-row items-center gap-3", - label { + InputLabel { r#for: "input_project", - class: "min-w-6 flex flex-row justify-center items-center", - Icon { - class: "text-zinc-400/50", - icon: FaList, - height: 16, - width: 16 - } + icon: FaList }, SuspenseBoundary { fallback: |_| { rsx ! { select { - class: "px-3.5 py-2.5 bg-zinc-800/50 rounded-lg grow cursor-pointer", + class: "px-4.5 pt-3.5 pb-2.75 bg-gray-800-muted drop-shadow-[0_calc(0px_-_var(--spacing))_0_var(--color-gray-900-muted)] rounded-xl grow cursor-pointer", option { value: 0, {t!("none")} - }, + } } } }, @@ -212,35 +201,22 @@ pub(crate) fn TaskForm(task: Option, on_successful_submit: EventHandler<() }, div { class: "flex flex-row items-center gap-3", - label { + InputLabel { r#for: "input_deadline", - class: "min-w-6 flex flex-row justify-center items-center", - Icon { - class: "text-zinc-400/50", - icon: FaBomb, - height: 16, - width: 16 - } + icon: FaBomb }, - input { + Input { name: "deadline", initial_value: task.as_ref().and_then(|task| task.deadline) .map(|deadline| deadline.format("%Y-%m-%d").to_string()), r#type: "date", - class: "py-2 px-3 bg-zinc-800/50 rounded-lg grow basis-0 cursor-pointer", - id: "input_deadline" + class: "grow basis-0 cursor-pointer" } }, div { class: "flex flex-row items-center gap-3", - label { - class: "min-w-6 flex flex-row justify-center items-center", - Icon { - class: "text-zinc-400/50", - icon: FaLayerGroup, - height: 16, - width: 16 - } + InputLabel { + icon: FaScroll, }, CategoryInput { selected_category: selected_category, @@ -251,59 +227,40 @@ pub(crate) fn TaskForm(task: Option, on_successful_submit: EventHandler<() Category::WaitingFor(waiting_for) => rsx! { div { class: "flex flex-row items-center gap-3", - label { - r#for: "input_waiting_for", - class: "min-w-6 flex flex-row justify-center items-center", - Icon { - class: "text-zinc-400/50", - icon: FaHourglassEnd, - height: 16, - width: 16 - } + InputLabel { + r#for: "input_category_waiting_for", + icon: FaHourglassEnd, }, - input { + Input { name: "category_waiting_for", required: true, initial_value: waiting_for, r#type: "text", - class: "py-2 px-3 bg-zinc-800/50 rounded-lg grow", - id: "input_category_waiting_for" - }, + } } }, Category::Calendar { date, reoccurrence, time } => rsx! { div { class: "flex flex-row items-center gap-3", - label { + InputLabel { r#for: "input_category_calendar_date", - class: "min-w-6 flex flex-row justify-center items-center", - Icon { - class: "text-zinc-400/50", - icon: FaClock, - height: 16, - width: 16 - } + icon: FaClock }, div { - class: "grow flex flex-row gap-2", - input { + class: "grow flex flex-row gap-3", + Input { r#type: "date", name: "category_calendar_date", required: true, initial_value: date.format("%Y-%m-%d").to_string(), - class: - "py-2 px-3 bg-zinc-800/50 rounded-lg grow cursor-pointer", - id: "input_category_calendar_date" }, - input { + Input { r#type: "time", name: "category_calendar_time", initial_value: time.map(|calendar_time| calendar_time.time.format("%H:%M").to_string() ), - class: "py-2 px-3 bg-zinc-800/50 rounded-lg grow cursor-pointer", - id: "input_category_calendar_time", - oninput: move |event| { + oninput: move |event: Event| { category_calendar_has_time.set(!event.value().is_empty()); } } @@ -311,50 +268,36 @@ pub(crate) fn TaskForm(task: Option, on_successful_submit: EventHandler<() }, div { class: "flex flex-row items-center gap-3", - label { + InputLabel { r#for: "category_calendar_reoccurrence_length", - class: "min-w-6 flex flex-row justify-center items-center", - Icon { - class: "text-zinc-400/50", - icon: FaRepeat, - height: 16, - width: 16 - } + icon: FaRepeat }, div { - class: "grow grid grid-cols-6 gap-2", + class: "grow grid grid-cols-5 items-end gap-3", ReoccurrenceIntervalInput { reoccurrence_interval: category_calendar_reoccurrence_interval }, - input { + Input { r#type: "number", inputmode: "numeric", name: "category_calendar_reoccurrence_length", disabled: category_calendar_reoccurrence_interval().is_none(), required: true, - min: 1, + min: "1", initial_value: category_calendar_reoccurrence_interval().map_or( String::new(), |_| reoccurrence.map_or(1, |reoccurrence| reoccurrence.length).to_string() ), - class: "py-2 px-3 bg-zinc-800/50 rounded-lg col-span-2 text-right", - id: "category_calendar_reoccurrence_length" } } }, if category_calendar_has_time() { div { class: "flex flex-row items-center gap-3", - label { + InputLabel { r#for: "category_calendar_reminder_offset_index", - class: "min-w-6 flex flex-row justify-center items-center", - Icon { - class: "text-zinc-400/50", - icon: FaBell, - height: 16, - width: 16 - } + icon: FaBell }, input { r#type: "range", @@ -363,7 +306,7 @@ pub(crate) fn TaskForm(task: Option, on_successful_submit: EventHandler<() max: REMINDER_OFFSETS.len() as i64 - 1, initial_value: category_calendar_reminder_offset_index() .to_string(), - class: "grow input-range-reverse cursor-pointer", + class: "grow cursor-pointer", id: "category_calendar_has_reminder", oninput: move |event| { category_calendar_reminder_offset_index.set( @@ -400,10 +343,10 @@ pub(crate) fn TaskForm(task: Option, on_successful_submit: EventHandler<() } } div { - class: "flex flex-row justify-between mt-auto", + class: "flex flex-row gap-3 mt-auto", button { r#type: "button", - class: "py-3 px-4 bg-zinc-300/50 rounded-lg cursor-pointer", + class: "grow py-3 px-4 flex flex-row justify-center items-center bg-gray-600 drop-shadow-[0_6px_0_var(--color-gray-800)] rounded-xl cursor-pointer", onclick: move |_| { let task = task.clone(); async move { @@ -424,7 +367,17 @@ pub(crate) fn TaskForm(task: Option, on_successful_submit: EventHandler<() } }, Icon { - icon: FaTrashCan, + icon: FaVolcano, + height: 16, + width: 16 + } + } + button { + r#type: "button", + class: "grow py-3 px-4 flex flex-row justify-center items-center bg-gray-600 drop-shadow-[0_6px_0_var(--color-gray-800)] rounded-xl cursor-pointer", + onclick: move |_| {}, + Icon { + icon: FaXmark, height: 16, width: 16 } @@ -432,7 +385,7 @@ pub(crate) fn TaskForm(task: Option, on_successful_submit: EventHandler<() button { form: "form_task", r#type: "submit", - class: "py-3 px-4 flex flex-row justify-center items-center bg-zinc-300/50 rounded-lg cursor-pointer", + class: "grow py-3.5 px-4 flex flex-row justify-center items-center bg-amber-300-muted drop-shadow-[0_6px_0_var(--color-amber-700-muted)] text-amber-700-muted rounded-xl cursor-pointer", Icon { icon: FaFloppyDisk, height: 16, diff --git a/src/components/task_list.rs b/src/components/task_list.rs index 98a4931..c13233d 100644 --- a/src/components/task_list.rs +++ b/src/components/task_list.rs @@ -6,7 +6,7 @@ use dioxus::core_macro::rsx; use dioxus::dioxus_core::Element; use dioxus::prelude::*; use dioxus_free_icons::Icon; -use dioxus_free_icons::icons::fa_regular_icons::FaSquare; +use dioxus_free_icons::icons::fa_regular_icons::{FaCircle, FaSquare}; use dioxus_free_icons::icons::fa_solid_icons::FaSquareCheck; #[component] @@ -40,7 +40,6 @@ pub(crate) fn TaskList(tasks: Vec, class: Option<&'static str> move |_| task_being_edited.set(Some(task.task.clone())) }, button { - class: "text-zinc-500", onclick: { move |event: Event| { // To prevent editing the task. @@ -57,16 +56,16 @@ pub(crate) fn TaskList(tasks: Vec, class: Option<&'static str> width: 30 } } else { - Icon { - class: "cursor-pointer", - icon: FaSquare, - height: 30, - width: 30 + div { + class: "h-8 w-8 border-3 border-amber-300-muted drop-shadow-[0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted)] rounded-full cursor-pointer" } } }, - TaskListItem { - task: task.clone() + div { + class: "mt-1", + TaskListItem { + task: task.clone() + } } } } diff --git a/src/layouts/main.rs b/src/layouts/main.rs index f0800ad..ac6e7cb 100644 --- a/src/layouts/main.rs +++ b/src/layouts/main.rs @@ -28,7 +28,7 @@ pub(crate) fn Main() -> Element { div { class: "grow flex flex-col justify-center items-center", Icon { - class: "animate-[spin_2000ms_linear_infinite]", + class: "text-gray-500 animate-[spin_2000ms_linear_infinite]", icon: FaCog, height: 32, width: 32 @@ -40,7 +40,7 @@ pub(crate) fn Main() -> Element { } StickyBottom { FormOpenButton { - opened: display_form, + is_opened: display_form, } BottomPanel { display_form: display_form, diff --git a/src/views/category_inbox_page.rs b/src/views/category_inbox_page.rs index 643ed6d..a14ac79 100644 --- a/src/views/category_inbox_page.rs +++ b/src/views/category_inbox_page.rs @@ -1,4 +1,5 @@ use crate::components::error_boundary_message::ErrorBoundaryMessage; +use crate::components::task_form::TaskForm; use crate::models::category::Category; use crate::views::category_page::CategoryPage; use dioxus::core_macro::rsx; @@ -12,6 +13,10 @@ pub(crate) fn CategoryInboxPage() -> Element { CategoryPage { category: Category::Inbox, } + TaskForm { + task: None, + on_successful_submit: move |_| {} + } } } } diff --git a/tailwind.css b/tailwind.css index 664bee6..225f3b1 100644 --- a/tailwind.css +++ b/tailwind.css @@ -20,4 +20,8 @@ /* stylelint-disable-next-line */ @theme { --font-sans: "Inter", "sans"; + --color-amber-300-muted: #b89a2e; + --color-amber-700-muted: #80390b; + --color-gray-800-muted: #141d2d; + --color-gray-900-muted: #0b111f; }