use std::cell::OnceCell;
use blinc_layout::div::{FontFamily, GenericFont};
use blinc_layout::prelude::*;
use blinc_theme::{ColorToken, RadiusToken, ThemeState};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum KbdSize {
Small,
#[default]
Medium,
Large,
}
#[derive(Clone, Debug)]
struct KbdConfig {
text: String,
size: KbdSize,
}
pub struct KbdBuilder {
config: KbdConfig,
built: OnceCell<Kbd>,
}
impl std::fmt::Debug for KbdBuilder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("KbdBuilder")
.field("text", &self.config.text)
.field("size", &self.config.size)
.finish()
}
}
impl KbdBuilder {
pub fn new(text: impl Into<String>) -> Self {
Self {
config: KbdConfig {
text: text.into(),
size: KbdSize::Medium,
},
built: OnceCell::new(),
}
}
pub fn size(mut self, size: KbdSize) -> Self {
self.config.size = size;
self
}
fn get_or_build(&self) -> &Kbd {
self.built.get_or_init(|| self.build_component())
}
fn build_component(&self) -> Kbd {
let theme = ThemeState::get();
let bg = theme.color(ColorToken::Surface);
let border_color = theme.color(ColorToken::Border);
let text_color = theme.color(ColorToken::TextSecondary);
let (font_size, px, py, radius) = match self.config.size {
KbdSize::Small => (10.0, 4.0, 2.0, theme.radius(RadiusToken::Sm)),
KbdSize::Medium => (12.0, 6.0, 2.0, theme.radius(RadiusToken::Sm)),
KbdSize::Large => (14.0, 8.0, 4.0, theme.radius(RadiusToken::Sm)),
};
let inner = div()
.class("cn-kbd")
.items_center()
.justify_center()
.w_fit()
.bg(bg)
.border(1.0, border_color)
.border_bottom(3.0, border_color)
.rounded(radius)
.padding_x_px(px)
.padding_y_px(py)
.shadow_sm()
.child(
text(&self.config.text)
.size(font_size)
.color(text_color)
.font_family(FontFamily::generic(GenericFont::Monospace))
.weight(FontWeight::SemiBold)
.medium()
.no_wrap(),
);
Kbd { inner }
}
}
pub struct Kbd {
inner: Div,
}
impl Kbd {
pub fn class(mut self, name: impl Into<String>) -> Self {
self.inner = self.inner.class(name);
self
}
pub fn id(mut self, id: &str) -> Self {
self.inner = self.inner.id(id);
self
}
}
impl std::fmt::Debug for Kbd {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Kbd").finish()
}
}
impl ElementBuilder for KbdBuilder {
fn build(&self, tree: &mut blinc_layout::tree::LayoutTree) -> blinc_layout::tree::LayoutNodeId {
self.get_or_build().inner.build(tree)
}
fn render_props(&self) -> blinc_layout::element::RenderProps {
self.get_or_build().inner.render_props()
}
fn children_builders(&self) -> &[Box<dyn ElementBuilder>] {
self.get_or_build().inner.children_builders()
}
fn element_type_id(&self) -> blinc_layout::div::ElementTypeId {
self.get_or_build().inner.element_type_id()
}
fn layout_style(&self) -> Option<&taffy::Style> {
self.get_or_build().inner.layout_style()
}
fn event_handlers(&self) -> Option<&blinc_layout::event_handler::EventHandlers> {
ElementBuilder::event_handlers(&self.get_or_build().inner)
}
fn element_classes(&self) -> &[String] {
self.get_or_build().inner.element_classes()
}
}
impl ElementBuilder for Kbd {
fn build(&self, tree: &mut blinc_layout::tree::LayoutTree) -> blinc_layout::tree::LayoutNodeId {
self.inner.build(tree)
}
fn render_props(&self) -> blinc_layout::element::RenderProps {
self.inner.render_props()
}
fn children_builders(&self) -> &[Box<dyn ElementBuilder>] {
self.inner.children_builders()
}
fn element_type_id(&self) -> blinc_layout::div::ElementTypeId {
self.inner.element_type_id()
}
fn layout_style(&self) -> Option<&taffy::Style> {
self.inner.layout_style()
}
fn event_handlers(&self) -> Option<&blinc_layout::event_handler::EventHandlers> {
ElementBuilder::event_handlers(&self.inner)
}
fn element_classes(&self) -> &[String] {
self.inner.element_classes()
}
}
pub fn kbd(text: impl Into<String>) -> KbdBuilder {
KbdBuilder::new(text)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_kbd_sizes() {
assert_eq!(KbdSize::default(), KbdSize::Medium);
}
}