use std::sync::Arc;
use arrow_array::RecordBatch;
use leptos::prelude::Callback;
pub const MIN_COL_WIDTH_PX: f64 = 40.0;
pub const DEFAULT_COL_WIDTH_PX: f64 = 120.0;
pub const ROW_NUM_WIDTH_PX: f64 = 72.0;
#[derive(Clone, Debug)]
pub struct GridPage {
pub start: u64,
pub row_count: usize,
pub batch: Arc<RecordBatch>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SortDirection {
Asc,
Desc,
}
impl SortDirection {
#[must_use]
pub fn next(self) -> Self {
match self {
Self::Asc => Self::Desc,
Self::Desc => Self::Asc,
}
}
pub fn arrow(self) -> &'static str {
match self {
Self::Asc => "\u{2191}", Self::Desc => "\u{2193}", }
}
}
#[derive(Clone, Debug, Default)]
pub struct SortState {
pub active: Vec<(usize, SortDirection)>,
}
pub fn cycle_sort_multi(
current: &SortState,
col_idx: usize,
additive: bool,
) -> Vec<(usize, SortDirection)> {
if additive {
let mut result = current.active.clone();
if let Some(pos) = result.iter().position(|(i, _)| *i == col_idx) {
match result[pos].1 {
SortDirection::Asc => result[pos].1 = SortDirection::Desc,
SortDirection::Desc => {
result.remove(pos);
}
}
} else {
result.push((col_idx, SortDirection::Asc));
}
result
} else {
match current.active.iter().find(|(i, _)| *i == col_idx) {
Some((_, SortDirection::Asc)) => vec![(col_idx, SortDirection::Desc)],
Some((_, SortDirection::Desc)) => vec![],
None => vec![(col_idx, SortDirection::Asc)],
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum FilterKind {
Contains(String),
StartsWith(String),
Regex(String),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FilterMode {
Contains,
StartsWith,
Regex,
}
impl FilterMode {
pub fn label(self) -> &'static str {
match self {
Self::Contains => "Contains",
Self::StartsWith => "Starts with",
Self::Regex => "Regex",
}
}
}
pub fn build_filter(mode: FilterMode, text: String) -> FilterKind {
match mode {
FilterMode::Contains => FilterKind::Contains(text),
FilterMode::StartsWith => FilterKind::StartsWith(text),
FilterMode::Regex => FilterKind::Regex(text),
}
}
#[derive(Clone)]
pub struct MenuItem {
pub label: String,
pub disabled: bool,
pub on_click: Callback<()>,
}
pub fn format_row_number(n: u64) -> String {
if n == 0 {
return "0".to_owned();
}
let s = n.to_string();
let mut result = String::with_capacity(s.len() + s.len() / 3);
for (i, ch) in s.chars().rev().enumerate() {
if i > 0 && i % 3 == 0 {
result.push('\u{2009}'); }
result.push(ch);
}
result.chars().rev().collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn format_row_number_basic() {
assert_eq!(format_row_number(0), "0");
assert_eq!(format_row_number(1), "1");
assert_eq!(format_row_number(999), "999");
assert_eq!(format_row_number(1000), "1\u{2009}000");
assert_eq!(format_row_number(1_000_000), "1\u{2009}000\u{2009}000");
assert_eq!(format_row_number(12_345_678), "12\u{2009}345\u{2009}678");
}
#[test]
fn sort_direction_next() {
assert_eq!(SortDirection::Asc.next(), SortDirection::Desc);
assert_eq!(SortDirection::Desc.next(), SortDirection::Asc);
}
#[test]
fn sort_direction_arrow() {
assert_eq!(SortDirection::Asc.arrow(), "\u{2191}");
assert_eq!(SortDirection::Desc.arrow(), "\u{2193}");
}
#[test]
fn cycle_sort_unsorted_starts_asc() {
let state = SortState::default();
assert_eq!(
cycle_sort_multi(&state, 2, false),
vec![(2, SortDirection::Asc)]
);
}
#[test]
fn cycle_sort_asc_goes_desc() {
let state = SortState {
active: vec![(3, SortDirection::Asc)],
};
assert_eq!(
cycle_sort_multi(&state, 3, false),
vec![(3, SortDirection::Desc)]
);
}
#[test]
fn cycle_sort_desc_clears() {
let state = SortState {
active: vec![(3, SortDirection::Desc)],
};
assert_eq!(cycle_sort_multi(&state, 3, false), vec![]);
}
#[test]
fn cycle_sort_different_column_starts_asc() {
let state = SortState {
active: vec![(1, SortDirection::Desc)],
};
assert_eq!(
cycle_sort_multi(&state, 5, false),
vec![(5, SortDirection::Asc)]
);
}
#[test]
fn cycle_sort_multi_additive_adds_new_column() {
let state = SortState {
active: vec![(0, SortDirection::Asc)],
};
let result = cycle_sort_multi(&state, 2, true);
assert_eq!(
result,
vec![(0, SortDirection::Asc), (2, SortDirection::Asc)]
);
}
#[test]
fn cycle_sort_multi_additive_cycles_existing_asc_to_desc() {
let state = SortState {
active: vec![(0, SortDirection::Asc), (2, SortDirection::Desc)],
};
let result = cycle_sort_multi(&state, 0, true);
assert_eq!(
result,
vec![(0, SortDirection::Desc), (2, SortDirection::Desc)]
);
}
#[test]
fn cycle_sort_multi_additive_removes_desc_column() {
let state = SortState {
active: vec![(0, SortDirection::Asc), (2, SortDirection::Desc)],
};
let result = cycle_sort_multi(&state, 2, true);
assert_eq!(result, vec![(0, SortDirection::Asc)]);
}
#[test]
fn cycle_sort_multi_non_additive_replaces_multi() {
let state = SortState {
active: vec![(0, SortDirection::Asc), (2, SortDirection::Desc)],
};
assert_eq!(
cycle_sort_multi(&state, 5, false),
vec![(5, SortDirection::Asc)]
);
}
}