Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/facett

facett-card/examples/card_demo.rs

//! **card_demo** — a YouTrack/Jira-style surveillance **issue card**, live. A
//! parent case with a status enum, an assignee (avatar initial), priority, opened
//! / due dates, tags, a progress bar, and a ticket hyperlink — plus three nested
//! **sub-task** cards, each in its own collapsible header (recursively a card).
//!
//! A read/edit toggle swaps the parent's field grid into a bound facett-form (the
//! card *is* the form model). A theme switcher proves every chip / avatar / bar is
//! palette-driven, and Ctrl-C copies the whole card (incl. children) as JSON,
//! Ctrl-V fills it — routed exactly the way a [`facett_core::FacetDeck`] does.
//!
//! Run: `cargo run -p facett-card --example card_demo`

use eframe::egui;
use facett_card::{Card, CardField, CardView, FieldValue};
use facett_core::{Facet, Theme, clipboard, set_theme};

fn demo_card() -> Card {
    let subtask = |id: &str, title: &str, state: &str, who: &str, prog: f32| {
        Card::new(title)
            .with_id(id)
            .field(CardField::new(
                "Status",
                FieldValue::Enum {
                    state: state.into(),
                    options: vec!["open".into(), "in_progress".into(), "done".into()],
                },
            ))
            .field(CardField::new("Assignee", FieldValue::User(who.into())))
            .field(CardField::new("Progress", FieldValue::Progress(prog)))
    };

    Card::new("Investigate vessel Nordkapp")
        .with_id("CASE-42")
        .field(CardField::new(
            "Status",
            FieldValue::Enum {
                state: "escalated".into(),
                options: vec!["open".into(), "escalated".into(), "closed".into()],
            },
        ))
        .field(CardField::new("Assignee", FieldValue::User("hugin".into())))
        .field(CardField::new("Priority", FieldValue::Number(2.0)))
        .field(CardField::new("Opened", FieldValue::Date("2026-06-01".into())))
        .field(CardField::new("Due", FieldValue::Date("2026-06-30".into())))
        .field(CardField::new(
            "Tags",
            FieldValue::Tags(vec!["ais-gap".into(), "port-call".into(), "nordkapp".into()]),
        ))
        .field(CardField::new("Progress", FieldValue::Progress(0.4)))
        .field(CardField::new(
            "Ticket",
            FieldValue::Link { label: "open in tracker".into(), url: "https://example.test/CASE-42".into() },
        ))
        .child(subtask("CASE-42-1", "Pull AIS track", "done", "munin", 1.0))
        .child(subtask("CASE-42-2", "Cross-ref port calls", "in_progress", "hugin", 0.5))
        .child(subtask("CASE-42-3", "Draft surveillance report", "open", "hugin", 0.0))
}

struct Demo {
    theme_idx: usize,
    view: CardView,
}

impl Default for Demo {
    fn default() -> Self {
        Self { theme_idx: 2, view: CardView::new("CASE-42", demo_card()) }
    }
}

impl eframe::App for Demo {
    fn ui(&mut self, root: &mut egui::Ui, _frame: &mut eframe::Frame) {
        let ctx = root.ctx().clone();
        set_theme(&ctx, Theme::ALL[self.theme_idx]());

        egui::Panel::top("controls").show(root, |ui| {
            ui.horizontal_wrapped(|ui| {
                ui.label("Theme:");
                for (i, ctor) in Theme::ALL.iter().enumerate() {
                    ui.selectable_value(&mut self.theme_idx, i, ctor().name);
                }
            });
            ui.label("Toggle Edit to form-bind the parent's fields · expand a sub-task · Ctrl-C copies the card as JSON · Ctrl-V fills it");
        });

        // Deck-style clipboard routing, gated by the facet's caps.
        let caps = self.view.caps();
        for action in clipboard::poll(&ctx) {
            match action {
                clipboard::ClipAction::Copy if caps.copyable => {
                    if let Some(t) = self.view.copy() {
                        clipboard::put(&ctx, t);
                    }
                }
                clipboard::ClipAction::Paste(s) if caps.pasteable => {
                    self.view.paste(&s);
                }
                _ => {}
            }
        }

        egui::CentralPanel::default().show(root, |ui| {
            egui::ScrollArea::vertical().show(ui, |ui| {
                self.view.ui(ui);
            });
        });
    }
}

fn main() -> eframe::Result {
    let options = eframe::NativeOptions {
        viewport: egui::ViewportBuilder::default().with_inner_size(egui::vec2(560.0, 720.0)),
        ..Default::default()
    };
    eframe::run_native("facett · card_demo", options, Box::new(|_cc| Ok(Box::<Demo>::default())))
}