Skip to main content

liora_components/
textarea.rs

1use crate::Input;
2use gpui::{
3    App, Context, Entity, FocusHandle, Focusable, Render, SharedString, Window, prelude::*, px,
4};
5use liora_core::Config;
6
7pub struct Textarea {
8    input: Entity<Input>,
9    rows: usize,
10    max_length: Option<usize>,
11    focus_handle: FocusHandle,
12}
13
14impl Textarea {
15    pub fn new(value: impl Into<SharedString>, cx: &mut Context<Self>) -> Self {
16        let value = value.into();
17        let rows = 1;
18        let input = cx.new(|cx| Input::new(value, cx).min_rows(rows));
19
20        Self {
21            input,
22            rows,
23            max_length: None,
24            focus_handle: cx.focus_handle(),
25        }
26    }
27
28    pub fn rows(mut self, rows: usize) -> Self {
29        self.rows = rows;
30        self
31    }
32
33    pub fn placeholder(self, p: impl Into<SharedString>, cx: &mut Context<Self>) -> Self {
34        self.input.update(cx, |input, cx| {
35            input.set_placeholder(p, cx);
36        });
37        self
38    }
39
40    pub fn disabled(self, d: bool, cx: &mut Context<Self>) -> Self {
41        self.input.update(cx, |input, cx| {
42            input.set_disabled(d, cx);
43        });
44        self
45    }
46
47    pub fn max_length(mut self, max: usize) -> Self {
48        self.max_length = Some(max);
49        self
50    }
51}
52
53impl Focusable for Textarea {
54    fn focus_handle(&self, _cx: &App) -> FocusHandle {
55        self.focus_handle.clone()
56    }
57}
58
59impl Render for Textarea {
60    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
61        let theme = cx.global::<Config>().theme.clone();
62        let value = self.input.read(cx).value();
63        let len = value.chars().count();
64        let rows = self.rows;
65
66        // Sync rows to inner input if changed
67        self.input.update(cx, |input, cx| {
68            if input.min_rows != rows {
69                input.set_min_rows(rows, cx);
70            }
71        });
72
73        gpui::div()
74            .flex()
75            .flex_col()
76            .gap_1()
77            .child(self.input.clone())
78            .when_some(self.max_length, |this, max| {
79                this.child(
80                    gpui::div()
81                        .flex()
82                        .justify_end()
83                        .px(px(4.0))
84                        .text_size(px(theme.font_size.sm))
85                        .text_color(if len > max {
86                            theme.danger.base
87                        } else {
88                            theme.neutral.text_3
89                        })
90                        .child(format!("{}/{}", len, max)),
91                )
92            })
93    }
94}