graphix-package-tui 0.9.0

A dataflow language for UIs and network programming, TUI package
Documentation
let browser = |
    #selected_style: Style = style(#bg:`Indexed(20), #fg:`Indexed(227)),
    #header_style: Style = style(#fg:`Indexed(111)),
    #style: Style = style(#fg:`Indexed(227), #bg:`Indexed(234)),
    #cursor: MoveCursor = never(),
    #selected_row: &string = &never(),
    #selected_path: &string = &never(),
    #flex: Flex = `SpaceBetween,
    #rate: duration = duration:0.5s,
    #size: Size,
    path: string
| -> Tui {
    let selected: {x: i64, y: i64} = { x: 1, y: 0 };
    let viewport: {x: i64, y: i64} = { x: 0, y: 0 };
    select uniq(path) {
        p => {
            selected <- p ~ { x: 1, y: 0 };
            viewport <- p ~ { x: 0, y: 0 }
        }
    };
    let (mode, structure) = select net::list_table(path)$ {
        { columns, rows } => {
            let mode = uniq(select columns {
                [] => `List,
                _ => `Table
            });
            let columns: Array<string> = select columns {
                [] => ["Value"],
                cols => array::sort(#numeric:true, cols)
            };
            let rows: Array<string> = {
                let base = select str::dirname(rows[0]$) {
                    null as _ => "/",
                    s => s
                };
                let names = array::filter_map(rows, |r| str::basename(r));
                let names = array::sort(#numeric:true, names);
                array::map(names, |name| select base {
                    "/" => "/[name]",
                    base => "[base]/[name]"
                })
            };
            (mode, { columns, rows })
        }
    };
    let visible_rows = select structure.rows {
        [] => [],
        rows => {
            let len = array::len(rows);
            let start = min(len - 1, viewport.y);
            let end = min(len, viewport.y + size.height - 1);
            (rows)[start .. end]$
        }
    };
    let content_widths: Array<i64> = [];
    let get_width = |i| try any(content_widths[i]?, i ~ 0) catch(e) => never(e);
    let { total: _, cols: visible_columns, widths }:
        { total: i64, cols: Array<string>, widths: Array<Constraint> } = {
        let name_width = max(4, get_width(0));
        let max_width = size.width / 2;
        let init = {
            total: size.width - name_width,
            cols: [],
            widths: [`Min(name_width)]
        };
        let candidates = array::enumerate((structure.columns)[viewport.x ..]$);
        let candidates = any(content_widths ~ candidates, candidates);
        array::fold(candidates, init, |{total, cols, widths}, (i, col)| {
            let len = min(max_width, max(str::len(col), get_width(i + 1)));
            select total {
                x if x > 0 => {
                    total: x - len - 4,
                    cols: array::push(cols, col),
                    widths: array::push(widths, `Min(len))
                },
                x => { total: x - len - 4, cols, widths }
            }
        })
    };
    let visible_columns: Array<string> = uniq(visible_columns);
    *selected_row <- visible_rows[selected.y]$;
    *selected_path <- select mode {
        `List => "[*selected_row]",
        `Table => "[*selected_row]/[visible_columns[selected.x - 1]$]"
    };
    let header = select mode {
        `List => row(#style:header_style, [cell(line("Name")), cell(line("Value"))]),
        `Table => {
            let cols = array::map(visible_columns, |col| cell(line(col)));
            row(#style:header_style, array::push_front(cols, cell(line("Name"))))
        }
    };
    let rows = array::map(visible_rows, |row| {
        let name = select str::basename(row) { null as _ => "", s => s };
        select mode {
            `List => {
                let v: Result<Primitive, [`InvalidCast(string), `SubscribeError(string)]> =
                    throttle(#rate, net::subscribe("[row]"));
                [name, "[any(uniq(row) ~ "#SUB", v)]"]
            },
            `Table => {
                let a = array::map(visible_columns, |col| {
                    let path = "[row]/[col]";
                    let v: Result<Primitive, [`InvalidCast(string), `SubscribeError(string)]> =
                        throttle(#rate, net::subscribe(path));
                    "[any(uniq(path) ~ "#SUB", v)]"
                });
                array::push_front(a, name)
            }
        }
    });
    content_widths <- throttle(#rate:duration:1.0s,
        array::fold(rows, [], |acc, row| {
            let row = array::map(row, |s| str::len(s));
            select acc {
                [] => row,
                acc => array::map(array::zip(row, acc), |(l0, l1)| max(l0, l1))
            }
        }));
    let rows = array::map(rows, |row|
        &tui::table::row(array::map(array::enumerate(row), |(i, s)| select i {
            0 => cell(#style:header_style, line(s)),
            _ => cell(line(s))
        })));
    select cursor {
        `Left(n) => select n ~ selected.x {
            e if e <= 1 =>
                viewport <- uniq(e ~ { viewport with x: max(0, viewport.x - n) }),
            e => selected <- e ~ { selected with x: max(1, selected.x - n) }
        },
        `Right(n) => {
            let limit = array::len(visible_columns);
            let clim = array::len(structure.columns) - array::len(visible_columns);
            select n ~ selected.x {
                x if x >= limit =>
                    viewport <- uniq(x ~ { viewport with x: min(clim, viewport.x + n) }),
                e => selected <- e ~ {selected with x: min(limit, selected.x + n)}
            }
        },
        `Down(n) => {
            let rlim = array::len(structure.rows) - array::len(visible_rows);
            let limit = array::len(visible_rows) - 1;
            select n ~ selected.y {
                x if x >= limit =>
                    viewport <- uniq(x ~ { viewport with y: min(rlim, viewport.y + n) }),
                e => selected <- e ~ { selected with y: min(limit, selected.y + n) }
            }
        },
        `Up(n) => select n ~ selected.y {
            y if y <= 0 =>
                viewport <- uniq(y ~ { viewport with y: max(0, viewport.y - n) }),
            y => selected <- y ~ { selected with y: max(0, selected.y - n) }
        }
    };
    table(
        #cell_highlight_style:&selected_style,
        #header:&header,
        #widths:&widths,
        #flex:&flex,
        #selected_cell: &selected,
        #style:&style,
        &rows
    )
}