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