hac-client 0.2.0

your handy API client, on your terminal!
Documentation
use crate::pages::input::Input;

use ratatui::buffer::Buffer;
use ratatui::layout::{Constraint, Direction, Flex, Layout, Rect};
use ratatui::style::{Style, Stylize};
use ratatui::text::Line;
use ratatui::widgets::{
    Block, BorderType, Borders, Clear, Padding, Paragraph, StatefulWidget, Widget,
};

#[derive(Debug, Default, PartialEq, Eq)]
pub enum FormFocus {
    #[default]
    Name,
    Description,
    Confirm,
    Cancel,
}

struct FormLayout {
    name_input: Rect,
    desc_input: Rect,
    confirm_button: Rect,
    cancel_button: Rect,
    hint: Rect,
}

#[derive(Debug, Default)]
pub struct FormState {
    pub name: String,
    pub description: String,
    pub focused_field: FormFocus,
}

impl FormState {
    pub fn reset(&mut self) {
        self.name = String::default();
        self.description = String::default();
        self.focused_field = FormFocus::Name;
    }
}

#[derive(Debug)]
pub struct NewCollectionForm<'a> {
    colors: &'a hac_colors::Colors,
}

impl<'a> NewCollectionForm<'a> {
    pub fn new(colors: &'a hac_colors::Colors) -> Self {
        NewCollectionForm { colors }
    }

    fn build_layout(&self, size: &Rect) -> FormLayout {
        let size = Rect {
            x: size.x + 2,
            y: size.y + 1,
            width: size.width.saturating_sub(4),
            height: size.height.saturating_sub(2),
        };
        let [name_input, desc_input, _, buttons, _, hint] = Layout::default()
            .direction(Direction::Vertical)
            .constraints([
                Constraint::Length(3),
                Constraint::Length(3),
                Constraint::Length(1),
                Constraint::Length(3),
                Constraint::Length(1),
                Constraint::Length(1),
            ])
            .areas(size);

        let [confirm_button, _, cancel_button] = Layout::default()
            .direction(Direction::Horizontal)
            .constraints([
                Constraint::Length(10),
                Constraint::Length(1),
                Constraint::Length(10),
            ])
            .flex(Flex::Center)
            .areas(buttons);

        FormLayout {
            name_input,
            desc_input,
            confirm_button,
            cancel_button,
            hint,
        }
    }
}

impl StatefulWidget for NewCollectionForm<'_> {
    type State = FormState;

    fn render(self, size: Rect, buf: &mut Buffer, state: &mut Self::State) {
        let layout = self.build_layout(&size);
        Clear.render(size, buf);

        let mut name_input =
            Input::new(self.colors, "Name".into()).placeholder("My awesome API".into());

        let mut desc_input =
            Input::new(self.colors, "Description".into()).placeholder("Request testing".into());

        match state.focused_field {
            FormFocus::Name => name_input.focus(),
            FormFocus::Description => desc_input.focus(),
            _ => {}
        };

        let cancel_text = if state.focused_field.eq(&FormFocus::Cancel) {
            "Cancel"
                .fg(self.colors.normal.white)
                .bg(self.colors.normal.red)
        } else {
            "Cancel".fg(self.colors.normal.white)
        };

        let cancel_button = Paragraph::new(Line::from(cancel_text).centered()).block(
            Block::default()
                .borders(Borders::ALL)
                .border_style(Style::default().fg(self.colors.bright.red))
                .border_type(BorderType::Rounded),
        );

        let confirm_text = if state.focused_field.eq(&FormFocus::Confirm) {
            "Create"
                .fg(self.colors.normal.white)
                .bg(self.colors.normal.magenta)
        } else {
            "Create".fg(self.colors.normal.white)
        };

        let confirm_button = Paragraph::new(Line::from(confirm_text).centered()).block(
            Block::default()
                .borders(Borders::ALL)
                .border_style(Style::default().fg(self.colors.bright.magenta))
                .border_type(BorderType::Rounded),
        );

        let full_block = Block::default()
            .padding(Padding::uniform(1))
            .style(Style::default().bg(self.colors.primary.background));

        let hint = Paragraph::new("[Tab] to switch focus [Enter] to select a button")
            .centered()
            .fg(self.colors.normal.magenta);

        full_block.render(size, buf);
        name_input.render(layout.name_input, buf, &mut state.name);
        desc_input.render(layout.desc_input, buf, &mut state.description);
        cancel_button.render(layout.cancel_button, buf);
        confirm_button.render(layout.confirm_button, buf);
        hint.render(layout.hint, buf);
    }
}