things3_cloud/ui/views/
area.rs1use crate::common::ICONS;
2use crate::store::{Area, Task, ThingsStore};
3use crate::ui::components::tags_badge::TagsBadge;
4use crate::ui::components::tasks::{TaskList, TaskOptions};
5use iocraft::prelude::*;
6use std::sync::Arc;
7
8const LIST_INDENT: u32 = 2;
9
10#[derive(Default, Props)]
11pub struct AreaViewProps<'a> {
12 pub area: Option<&'a Area>,
13 pub tasks: Vec<&'a Task>,
14 pub projects: Vec<&'a Task>,
15 pub detailed: bool,
16}
17
18#[component]
19pub fn AreaView<'a>(hooks: Hooks, props: &AreaViewProps<'a>) -> impl Into<AnyElement<'a>> {
20 let store = hooks.use_context::<Arc<ThingsStore>>().clone();
21 let Some(area) = props.area else {
22 return element! { Text(content: "") }.into_any();
23 };
24
25 let project_count = props.projects.len();
26 let task_count = props.tasks.len();
27
28 let mut parts = Vec::new();
29 if project_count > 0 {
30 parts.push(format!(
31 "{} project{}",
32 project_count,
33 if project_count == 1 { "" } else { "s" }
34 ));
35 }
36 if task_count > 0 {
37 parts.push(format!(
38 "{} task{}",
39 task_count,
40 if task_count == 1 { "" } else { "s" }
41 ));
42 }
43 let count_str = if parts.is_empty() {
44 String::new()
45 } else {
46 format!(" ({})", parts.join(", "))
47 };
48
49 let mut item_uuids = props
50 .projects
51 .iter()
52 .map(|p| p.uuid.clone())
53 .collect::<Vec<_>>();
54 item_uuids.extend(props.tasks.iter().map(|t| t.uuid.clone()));
55 let id_prefix_len = store.unique_prefix_length(&item_uuids);
56
57 let task_options = TaskOptions {
58 detailed: props.detailed,
59 show_project: false,
60 show_area: false,
61 show_today_markers: true,
62 show_staged_today_marker: false,
63 };
64 let project_options = TaskOptions {
65 detailed: props.detailed,
66 show_project: false,
67 show_area: false,
68 show_today_markers: false,
69 show_staged_today_marker: false,
70 };
71
72 element! {
73 View(flex_direction: FlexDirection::Column) {
74 View(flex_direction: FlexDirection::Row, gap: 1) {
75 Text(
76 content: format!("{} {}{}", ICONS.area, area.title, count_str),
77 wrap: TextWrap::NoWrap,
78 color: Color::Magenta,
79 weight: Weight::Bold,
80 )
81 TagsBadge(tags: area.tags.clone())
82 }
83
84 #(if !props.tasks.is_empty() {
85 Some(element! {
86 View(flex_direction: FlexDirection::Column) {
87 Text(content: "", wrap: TextWrap::NoWrap)
88 View(flex_direction: FlexDirection::Column, padding_left: LIST_INDENT) {
89 TaskList(items: props.tasks.clone(), id_prefix_len, options: task_options)
90 }
91 }
92 })
93 } else { None })
94
95 #(if !props.projects.is_empty() {
96 Some(element! {
97 View(flex_direction: FlexDirection::Column) {
98 Text(content: "", wrap: TextWrap::NoWrap)
99 View(flex_direction: FlexDirection::Column, padding_left: LIST_INDENT) {
100 TaskList(items: props.projects.clone(), id_prefix_len, options: project_options)
101 }
102 }
103 })
104 } else { None })
105 }
106 }
107 .into_any()
108}