things3_cloud/ui/views/
find.rs1use std::sync::Arc;
2
3use iocraft::prelude::*;
4
5use crate::{
6 common::ICONS,
7 store::{Task, ThingsStore},
8 ui::components::{
9 empty_text::EmptyText,
10 project_item::ProjectItem,
11 task_item::TaskItem,
12 tasks::TaskOptions,
13 },
14};
15
16#[derive(Clone)]
17pub struct FindRow<'a> {
18 pub task: &'a Task,
19 pub force_detailed: bool,
20}
21
22#[derive(Default, Props)]
23pub struct FindViewProps<'a> {
24 pub rows: Vec<FindRow<'a>>,
25 pub detailed: bool,
26}
27
28#[component]
29pub fn FindView<'a>(hooks: Hooks, props: &FindViewProps<'a>) -> impl Into<AnyElement<'a>> {
30 let store = hooks.use_context::<Arc<ThingsStore>>().clone();
31
32 if props.rows.is_empty() {
33 return element! { EmptyText(content: "No matching tasks.") }.into_any();
34 }
35
36 let id_prefix_len = store.unique_prefix_length(
37 &props
38 .rows
39 .iter()
40 .map(|row| row.task.uuid.clone())
41 .collect::<Vec<_>>(),
42 );
43
44 let count = props.rows.len();
45 let label = if count == 1 { "task" } else { "tasks" };
46
47 let mut body: Vec<AnyElement<'a>> = Vec::new();
48 for row in &props.rows {
49 let options = TaskOptions {
50 detailed: props.detailed || row.force_detailed,
51 show_project: true,
52 show_area: false,
53 show_today_markers: true,
54 show_staged_today_marker: false,
55 };
56 let line = if row.task.is_project() {
57 element! {
58 View(flex_direction: FlexDirection::Column, padding_left: 2) {
59 ProjectItem(project: row.task, options, id_prefix_len)
60 }
61 }
62 .into_any()
63 } else {
64 element! {
65 View(flex_direction: FlexDirection::Column, padding_left: 2) {
66 TaskItem(task: row.task, options, id_prefix_len)
67 }
68 }
69 .into_any()
70 };
71 body.push(line);
72 }
73
74 element! {
75 View(flex_direction: FlexDirection::Column) {
76 Text(
77 content: format!("{} Find ({} {})", ICONS.find, count, label),
78 wrap: TextWrap::NoWrap,
79 color: Color::Cyan,
80 weight: Weight::Bold,
81 )
82 Text(content: "", wrap: TextWrap::NoWrap)
83 #(body)
84 }
85 }
86 .into_any()
87}