Skip to main content

sqlly_datatable/
lib.rs

1//! Configurable virtualized data grid for the [GPUI] toolkit.
2//!
3//! `sqlly-datatable` provides a self-contained `Entity<GridState>` widget that
4//! you can drop into a GPUI application. It supports:
5//!
6//! * Virtualized rendering for large datasets.
7//! * Cell, row, column and rectangular drag selection.
8//! * Sorting on column headers, with a cycle through ascending, descending,
9//!   and unsorted.
10//! * Per-column filtering via a rich filter panel (operator predicates,
11//!   value checklist, search) opened from the built-in context menu.
12//! * Expandable row sections created by grouping on any column from its
13//!   built-in context menu or through [`GridState::set_grouped_column`].
14//! * Configurable column resizing, mouse-driven scrollbars, and edge-scroll
15//!   during drag selection.
16//! * Clipboard copy of any selection (with or without headers).
17//! * A restrained native motion layer: transient surfaces (context menus,
18//!   filter panels, popovers, dialogs, the busy scrim, the pivot drag ghost)
19//!   fade in on appear, while the data surface stays instant. On by default;
20//!   set [`config::GridConfig::animations`] to `false` to honor a system
21//!   reduce-motion preference.
22//! * An optional **pivot tab** ([`SqllyDataTableBuilder::pivot`]): a
23//!   cross-tabulation view with a drag-and-drop field sidebar (rows /
24//!   columns / values / filters), count/sum/avg/min/max aggregation,
25//!   expandable row and column groups, subtotals and grand totals, sorting
26//!   on labels or values, source-value filters, and CSV export. The pivot
27//!   reads a shared snapshot of the grid's rows and never mutates them;
28//!   switching tabs preserves both views' state. Configure it
29//!   programmatically via [`pivot::PivotConfig`] and read the live layout
30//!   back from [`pivot::PivotState::config`]. Pivot row height and column
31//!   width can be initialized on the builder, resized in the view, and read
32//!   or updated through [`pivot::PivotState`]. Right-clicks surface to a
33//!   [`pivot::PivotContextMenuProvider`] with full context (grouping paths,
34//!   aggregated value, driving source rows), and double-clicking any value
35//!   cell drills through: the flat grid is filtered to exactly the rows
36//!   behind that cell and brought to the front.
37//!
38//! The crate is intentionally GPUI-only on the UI side; the pure formatter in
39//! [`mod@format`] is usable in any context (export pipelines, server-side preview,
40//! etc.). All formatting is configurable per column by composing the
41//! [`config::GridConfig`] defaults with [`config::ColumnOverride`] entries.
42//!
43//! # Quick start
44//!
45//! ```no_run
46//! use gpui::App;
47//! use sqlly_datatable::{
48//!     CellValue, Column, ColumnKind, GridConfig, GridData, SqllyDataTable,
49//! };
50//!
51//! let data = GridData::new(
52//!     vec![Column { name: "id".into(), kind: ColumnKind::Integer, width: 80.0 }],
53//!     vec![vec![CellValue::Integer(1)], vec![CellValue::Integer(2)]],
54//! ).expect("rectangular data");
55//! let app = gpui::Application::new();
56//! app.run(|cx: &mut App| {
57//!     let _view = SqllyDataTable::builder(data)
58//!         .config(GridConfig::default())
59//!         .build(cx);
60//! });
61//! ```
62//!
63//! See `crates/sqlly-datatable-sample` for a runnable demo.
64//!
65//! [GPUI]: https://github.com/zed-industries/gpui
66
67// `missing_docs` is intentionally not enabled at the crate level. The public
68// surfaces documented here are stable; private internals will get docs as the
69// `grid::` modules mature. Run clippy with
70// `#![warn(missing_docs)]` in scope when cleaning up a module.
71
72pub mod config;
73pub mod data;
74pub mod filter;
75pub mod format;
76pub mod grid;
77pub mod pivot;
78
79pub use config::{
80    BooleanFormat, ColumnOverride, DateFormat, GridConfig, KeyBinding, KeyBindings, NullFormat,
81    NumberFormat, RelativeDateFormat, RelativeUnit, ReplacementRule, ReplacementTiming,
82    ResolvedColumnFormat, StringFormat, TextAlignment, TextCase, TruncationBehavior,
83};
84pub use data::{
85    compare_cells, sample_data, CellValue, Column, ColumnKind, GridData, GridDataError,
86};
87pub use filter::{ColumnFilter, FilterPredicate, NumberOp, TextOp};
88pub use grid::{
89    BusyState, ColumnContext, ContextMenu, ContextMenuItem, ContextMenuProvider,
90    ContextMenuRequest, ContextMenuSelection, ContextMenuTarget, FilterPanel, GridState, GridTab,
91    GridTheme, GridThemePair, HitResult, MenuAction, MenuItem, PivotSidebarPosition, RowGroup,
92    RowWindow, ScrollbarAxis, SelectedCellContext, SelectedRowContext, Selection, SortDirection,
93    SqllyDataTable, SqllyDataTableBuilder,
94};
95pub use pivot::{
96    AggregationFn, PivotCellContext, PivotConfig, PivotContextMenuProvider,
97    PivotContextMenuRequest, PivotFormatDialog, PivotGrid, PivotMenuItem, PivotMenuTarget,
98    PivotPathComponent, PivotResult, PivotSaveConfigHandler, PivotSidebar, PivotSortKey,
99    PivotState, PivotZone, DEFAULT_PIVOT_COLUMN_WIDTH, DEFAULT_PIVOT_ROW_HEIGHT,
100    DEFAULT_PIVOT_SIDEBAR_WIDTH, MIN_PIVOT_COLUMN_WIDTH, MIN_PIVOT_ROW_HEIGHT,
101};