orbital-base-components 0.1.1

Headless base primitives for Orbital — structure, semantics, and accessibility
Documentation
use std::cmp::min;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PaginationItem {
    DotLeft,
    DotRight,
    Number(usize),
}

pub fn pagination_items(page: usize, count: usize, sibling_count: usize) -> Vec<PaginationItem> {
    let total_page_numbers = sibling_count + 5;
    if total_page_numbers >= count {
        return range(1, count);
    }

    let left_sibling_index = if page > sibling_count + 1 {
        page - sibling_count
    } else {
        1
    };
    let right_sibling_index = min(page + sibling_count, count);
    let should_show_left_dots = left_sibling_index > 2;
    let should_show_right_dots = right_sibling_index < count - 2;

    if !should_show_left_dots && should_show_right_dots {
        let left_item_count = 3 + 2 * sibling_count;
        let mut left_range = range(1, left_item_count);
        left_range.push(PaginationItem::DotRight);
        left_range.push(PaginationItem::Number(count));
        left_range
    } else if should_show_left_dots && !should_show_right_dots {
        let right_item_count = 3 + 2 * sibling_count;
        let mut right_range = range(count - right_item_count + 1, count);
        let mut ret = vec![PaginationItem::Number(1), PaginationItem::DotLeft];
        ret.append(&mut right_range);
        ret
    } else {
        let mut middle_range = range(left_sibling_index, right_sibling_index);
        let mut items = vec![PaginationItem::Number(1), PaginationItem::DotLeft];
        items.append(&mut middle_range);
        items.append(&mut vec![
            PaginationItem::DotRight,
            PaginationItem::Number(count),
        ]);
        items
    }
}

fn range(start: usize, end: usize) -> Vec<PaginationItem> {
    (start..=end).map(PaginationItem::Number).collect()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn small_page_count_returns_full_range() {
        assert_eq!(
            pagination_items(1, 5, 1),
            vec![
                PaginationItem::Number(1),
                PaginationItem::Number(2),
                PaginationItem::Number(3),
                PaginationItem::Number(4),
                PaginationItem::Number(5),
            ]
        );
    }

    #[test]
    fn middle_page_shows_both_dots() {
        let items = pagination_items(10, 20, 1);
        assert!(items.contains(&PaginationItem::DotLeft));
        assert!(items.contains(&PaginationItem::DotRight));
        assert!(items.contains(&PaginationItem::Number(1)));
        assert!(items.contains(&PaginationItem::Number(20)));
    }

    #[test]
    fn early_page_shows_right_dots_only() {
        let items = pagination_items(2, 20, 1);
        assert!(!items.contains(&PaginationItem::DotLeft));
        assert!(items.contains(&PaginationItem::DotRight));
    }

    #[test]
    fn late_page_shows_left_dots_only() {
        let items = pagination_items(19, 20, 1);
        assert!(items.contains(&PaginationItem::DotLeft));
        assert!(!items.contains(&PaginationItem::DotRight));
    }
}