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

nordisk/facett

facett-grid/examples/grid_demo.rs

//! **grid_demo** — the flagship `facett-grid`, live: a **50,000-row** Arrow-backed
//! grid with a frozen sticky header, two pinned leading columns, a sticky Σ totals
//! footer, multi-column sort (shift-click a 2nd header), Excel-style cell
//! selection (click · shift-range · ctrl-multi · Ctrl-A), keyboard nav
//! (arrows/page/home/end), type-aware cells (grouped ints, float heat, enum chips,
//! booleans, in-cell progress bars, sparklines), conditional formatting, a density
//! toggle, a live filter, and a **palette switcher** proving every colour is
//! theme-driven. **Ctrl-C** copies the selection as TSV (routed exactly the way a
//! [`FacetDeck`] does).
//!
//! Run: `cargo run -p facett-grid --example grid_demo`

use std::sync::Arc;

use arrow_array::{BooleanArray, Date32Array, Float64Array, Int64Array, RecordBatch, StringArray};
use arrow_schema::{DataType, Field, Schema};
use eframe::egui;

use facett_core::{FacetDeck, Theme, clipboard, set_theme};
use facett_grid::{CondFmt, Density, Grid};

const ROWS: usize = 50_000;

fn demo_batch(n: usize) -> RecordBatch {
    let id: Vec<i64> = (0..n as i64).map(|i| 100_000 + i).collect();
    let name: Vec<String> = (0..n).map(|i| format!("vessel-{i:06}")).collect();
    let region: Vec<&str> = (0..n).map(|i| ["Nordsjøn", "Skagerrak", "Kattegat", "Østersjön", "Barents"][i % 5]).collect();
    let class: Vec<&str> = (0..n).map(|i| ["cargo", "tanker", "fishing", "passenger"][i % 4]).collect();
    let speed: Vec<f64> = (0..n).map(|i| ((i * 53) % 320) as f64 / 10.0).collect();
    let risk: Vec<f64> = (0..n).map(|i| ((i * 71 + 13) % 100) as f64).collect();
    let progress: Vec<f64> = (0..n).map(|i| ((i * 29) % 100) as f64 / 100.0).collect();
    let trend: Vec<f64> = (0..n).map(|i| (i as f64 * 0.3).sin() * 40.0 + 50.0).collect();
    let flagged: Vec<bool> = (0..n).map(|i| i % 7 == 0).collect();
    let seen: Vec<i32> = (0..n).map(|i| 19000 + (i as i32 % 365)).collect();

    RecordBatch::try_new(
        Arc::new(Schema::new(vec![
            Field::new("id", DataType::Int64, false),
            Field::new("name", DataType::Utf8, false),
            Field::new("region", DataType::Utf8, false),
            Field::new("class", DataType::Utf8, false),
            Field::new("speed_kn", DataType::Float64, false),
            Field::new("risk", DataType::Float64, false),
            Field::new("scan", DataType::Float64, false),
            Field::new("trend", DataType::Float64, false),
            Field::new("flagged", DataType::Boolean, false),
            Field::new("last_seen", DataType::Date32, false),
        ])),
        vec![
            Arc::new(Int64Array::from(id)),
            Arc::new(StringArray::from(name)),
            Arc::new(StringArray::from(region)),
            Arc::new(StringArray::from(class)),
            Arc::new(Float64Array::from(speed)),
            Arc::new(Float64Array::from(risk)),
            Arc::new(Float64Array::from(progress)),
            Arc::new(Float64Array::from(trend)),
            Arc::new(BooleanArray::from(flagged)),
            Arc::new(Date32Array::from(seen)),
        ],
    )
    .unwrap()
}

fn build_grid() -> Grid {
    let mut g = Grid::from_record_batch(format!("{ROWS} vessels"), &demo_batch(ROWS))
        .pin_columns(2) // freeze id + name
        .with_totals(true)
        .with_density(Density::Comfortable)
        .with_cond_fmt(5, CondFmt::ColorScale) // risk → heat
        .with_progress(6) // scan → in-cell bar
        .with_sparkline(7); // trend → sparkline
    // Pre-seed a multi-sort so the header shows ▲1 / ▲2.
    g.click_header(2, false); // region asc
    g.click_header(5, true); // then risk asc
    g
}

struct DemoApp {
    deck: FacetDeck,
}

impl Default for DemoApp {
    fn default() -> Self {
        let deck = FacetDeck::new(vec![Box::new(build_grid())]).with_fx(Default::default());
        Self { deck }
    }
}

impl eframe::App for DemoApp {
    fn ui(&mut self, root: &mut egui::Ui, _frame: &mut eframe::Frame) {
        let ctx = root.ctx().clone();
        // Apply the deck's chosen palette (the picker below pins it), else a
        // striking default so the demo opens looking great.
        if let Some(t) = self.deck.fx().theme() {
            set_theme(&ctx, t);
        } else {
            set_theme(&ctx, Theme::nordic_aurora());
        }
        egui::Panel::top("bar").show(root, |ui| {
            ui.horizontal(|ui| {
                ui.heading("facett-grid");
                ui.separator();
                self.deck.palette_picker(ui);
            });
            ui.label("click=cell · shift=range · ctrl=multi · Ctrl-A=all · Ctrl-C=copy TSV · arrows/Page/Home/End=nav");
        });
        egui::CentralPanel::default().show(root, |ui| {
            // The deck routes Ctrl-C / scale shortcuts to the grid for free.
            self.deck.ui(ui);
        });
        // Make the routed Ctrl-C visible in the terminal too.
        for action in clipboard::poll(&ctx) {
            if let clipboard::ClipAction::Copy = action {
                eprintln!("grid_demo: Ctrl-C → selection copied to clipboard as TSV");
            }
        }
    }
}

fn main() -> eframe::Result {
    let options = eframe::NativeOptions {
        viewport: egui::ViewportBuilder::default().with_inner_size([1280.0, 760.0]),
        ..Default::default()
    };
    eframe::run_native("facett-grid demo", options, Box::new(|_cc| Ok(Box::<DemoApp>::default())))
}