use super::*;
use crate::ui::form::Row;
pub struct FormRow {
pub name: &'static str,
pub value: String,
pub unset: bool,
pub chips: Vec<(String, String)>,
pub link: Option<Row>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Mark {
Fine,
Waiting,
Problem,
}
pub struct TextBox<'a> {
pub title: String,
pub untitled: &'a str,
pub body: &'a str,
pub markdown: bool,
pub empty: &'a str,
pub hint: &'a str,
pub link: Link,
pub actions: Vec<(&'static str, Row)>,
}
#[derive(Default)]
pub struct WriteFormView<'a> {
pub heading: String,
pub base: Option<LinkBase>,
pub rows: Vec<FormRow>,
pub status: Vec<(String, Mark, Option<Row>)>,
pub boxes: Vec<TextBox<'a>>,
pub after: Vec<FormRow>,
pub write: &'a str,
pub save_key: &'a str,
pub button: String,
pub why_not: Option<String>,
pub error: Option<&'a str>,
}
const SHOWN: usize = 40;
pub fn write_form(page: &mut Page, v: &WriteFormView<'_>) {
page.line(vec![Seg::new(v.heading.clone(), Role::Title)]);
page.blank();
for row in &v.rows {
form_row(page, row);
}
for (text, mark, target) in &v.status {
let (sign, role) = match mark {
Mark::Fine => ("✓ ", Role::Success),
Mark::Waiting => ("… ", Role::Meta),
Mark::Problem => ("! ", Role::Error),
};
maybe_item(page, target.clone().map(Link::Form), |page, link| {
let text = Seg::linked(text.clone(), Role::Body, link);
page.wrapped(vec![Seg::new(sign, role), text], 0, Frame::None);
});
}
for b in &v.boxes {
text_box(page, b, v);
}
page.blank();
for row in &v.after {
form_row(page, row);
}
page.blank();
item(page, Link::Form(Row::Save), |page, link| {
let role = match v.why_not {
Some(_) => Role::Chip(Bg::ContainerHigh),
None => Role::Chip(Bg::SuccessContainer),
};
let button = Seg::linked(format!(" {} ", v.button), role, link);
page.line(vec![button, meta(format!(" {}", v.save_key))]);
if let Some(why) = &v.why_not {
say(page, why.clone(), Role::Meta);
}
});
if let Some(error) = v.error {
say(page, error.to_owned(), Role::Error);
}
}
fn form_row(page: &mut Page, row: &FormRow) {
let value = |link: Option<u32>| {
let role = if row.unset { Role::Meta } else { Role::Body };
Seg::linked(row.value.clone(), role, link)
};
let segs = |link| {
let mut segs = vec![strong(format!("{:<12}", row.name))];
segs.push(value(link));
for (name, color) in &row.chips {
segs.push(Seg::new(format!(" {name} "), Role::Label(color.clone())));
segs.push(space());
}
segs
};
let target = row.link.clone().map(Link::Form);
maybe_item(page, target, |page, link| {
page.wrapped(segs(link), 0, Frame::None);
});
}
fn text_box(page: &mut Page, b: &TextBox<'_>, v: &WriteFormView<'_>) {
let rename = b.actions.iter().find(|(name, _)| *name == "rename");
let right = match rename {
Some(_) => format!("↵ renames · {} writes", v.write),
None => format!("{} writes", v.write),
};
let right = vec![meta(right)];
let title_start = page.lines.len();
let title_link = match rename {
Some((_, target)) => page.link(Link::Form(target.clone())),
None => page.link(b.link.clone()),
};
let title = if b.title.is_empty() {
Seg::linked(b.untitled, Role::Meta, title_link)
} else {
Seg::linked(b.title.clone(), Role::Strong, title_link)
};
page.box_top(vec![title], right);
if rename.is_some() {
page.item(title_start, title_link);
}
let body_start = page.lines.len();
let link = page.link(b.link.clone());
if b.body.trim().is_empty() {
empty_row(page, b.empty);
} else if b.markdown {
markdown::render(page, b.body, v.base.as_ref(), Frame::Body);
} else {
let lines: Vec<&str> = b.body.lines().collect();
for line in lines.iter().take(SHOWN) {
body(page, vec![Seg::new((*line).to_owned(), Role::Code)]);
}
if lines.len() > SHOWN {
let more = lines.len() - SHOWN;
empty_row(page, &format!("and {}", plural(more as u64, "more line")));
}
}
page.box_gap();
body(page, vec![Seg::linked(b.hint.to_owned(), Role::Meta, link)]);
page.box_bottom();
page.item(
if rename.is_some() {
body_start
} else {
title_start
},
link,
);
for (name, target) in b.actions.iter().filter(|(name, _)| *name != "rename") {
let text = format!(" {name} this file");
item(page, Link::Form(target.clone()), |page, link| {
page.line(vec![Seg::linked(text, Role::Link, link)]);
});
}
}