use crate::{
AnyElement, ComponentTheme, Hooks, Palette, UsePreviousSize, UseTheme,
components::theme::resolve_style,
prelude::{Fragment, Positioned, Text},
};
use ratatui::{style::Style, text::Span};
use ratatui_kit_macros::{Props, component, element};
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InputTheme {
pub cursor_style: Style,
pub placeholder_style: Style,
pub style: Style,
}
impl ComponentTheme for InputTheme {
fn from_palette(palette: &Palette) -> Self {
Self {
cursor_style: Style::new().bg(palette.accent).fg(palette.on_accent),
placeholder_style: Style::new().fg(palette.placeholder),
style: Style::new().fg(palette.fg),
}
}
}
impl Default for InputTheme {
fn default() -> Self {
Self::from_palette(&Palette::default())
}
}
#[derive(Debug, Clone, Props, Default)]
pub struct InputProps {
pub input: tui_input::Input,
pub cursor_style: Option<Style>,
pub placeholder: String,
pub placeholder_style: Option<Style>,
pub style: Option<Style>,
pub hide_cursor: bool,
}
#[component]
pub fn Input(props: &InputProps, mut hooks: Hooks) -> impl Into<AnyElement<'static>> {
let theme = hooks.use_component_theme::<InputTheme>();
let cursor_style = resolve_style(theme.cursor_style, props.cursor_style);
let placeholder_style = resolve_style(theme.placeholder_style, props.placeholder_style);
let style = resolve_style(theme.style, props.style);
let size = hooks.use_previous_size();
let input = &props.input;
let input_width = size.width.saturating_sub(1) as usize;
let scroll = if props.hide_cursor || input_width == 0 {
0
} else {
input.visual_scroll(input_width)
};
let text = if input.value().is_empty() {
props.placeholder.clone()
} else {
input.value().to_string()
};
let x = input.visual_cursor().max(scroll) - scroll;
let position = (size.x + x as u16, size.y);
element!(Fragment {
Text(
text:text,
style: if input.value().is_empty() {
placeholder_style
}else{
style
},
scroll:(0, scroll as u16),
)
if !props.hide_cursor {
Positioned(
x: position.0.min(size.x + size.width.saturating_sub(1)),
y: position.1.min(size.y + size.height),
width: 1u16,
height: 1u16,
){
widget(Span::from(" ").style(cursor_style))
}
}
})
}