Skip to main content

things3_cloud/ui/components/
project_item.rs

1use iocraft::prelude::*;
2
3use crate::{
4    store::Task,
5    ui::components::{
6        details_container::DetailsContainer,
7        id::Id,
8        progress_badge::ProgressBadge,
9        task_line::TaskLine,
10        tasks::TaskOptions,
11    },
12};
13
14#[derive(Default, Props)]
15pub struct ProjectItemProps<'a> {
16    pub project: Option<&'a Task>,
17    pub options: TaskOptions,
18    pub id_prefix_len: usize,
19}
20
21#[component]
22pub fn ProjectItem<'a>(props: &ProjectItemProps<'a>) -> impl Into<AnyElement<'a>> {
23    let Some(project) = props.project else {
24        return element!(Fragment).into_any();
25    };
26
27    let details = if props.options.detailed {
28        element!(ProjectDetails(project: project)).into_any()
29    } else {
30        element!(Fragment).into_any()
31    };
32
33    element! {
34        View(flex_direction: FlexDirection::Row, gap: 1) {
35            Id(id: &project.uuid, length: props.id_prefix_len)
36            View(flex_direction: FlexDirection::Column) {
37                ProjectText(project: project, options: props.options)
38                #(details)
39            }
40        }
41    }
42    .into_any()
43}
44
45#[derive(Default, Props)]
46struct ProjectTextProps<'a> {
47    pub project: Option<&'a Task>,
48    pub options: TaskOptions,
49}
50
51#[component]
52fn ProjectText<'a>(props: &ProjectTextProps<'a>) -> impl Into<AnyElement<'a>> {
53    let Some(project) = props.project else {
54        return element!(Fragment).into_any();
55    };
56
57    element! {
58        View(flex_direction: FlexDirection::Row, gap: 1) {
59            ProgressBadge(project)
60            TaskLine(
61                task: project,
62                show_today_markers: props.options.show_today_markers,
63                show_staged_today_marker: props.options.show_staged_today_marker,
64                show_tags: true,
65                show_project: false,
66                show_area: props.options.show_area,
67            )
68        }
69    }
70    .into_any()
71}
72
73#[derive(Default, Props)]
74struct ProjectDetailsProps<'a> {
75    pub project: Option<&'a Task>,
76}
77
78#[component]
79fn ProjectDetails<'a>(props: &ProjectDetailsProps<'a>) -> impl Into<AnyElement<'a>> {
80    let Some(project) = props.project else {
81        return element!(Fragment).into_any();
82    };
83
84    let note_text = project.notes.as_deref().unwrap_or("").trim();
85    if note_text.is_empty() {
86        return element!(Fragment).into_any();
87    }
88
89    element! {
90        DetailsContainer {
91            Text(content: note_text, wrap: TextWrap::NoWrap, color: Color::DarkGrey)
92        }
93    }
94    .into_any()
95}