ag-grid-rs 0.2.2

Wasm bindings for AG Grid
Documentation
//! Types pertaining to defining and constructing a `Grid`.

use std::{collections::HashMap, future::Future};

use ag_grid_core::imports::log;
use ag_grid_derive::FieldSetter;
use js_sys::Function;
use wasm_bindgen::{prelude::*, JsCast};
use wasm_bindgen_futures::spawn_local;
use web_sys::HtmlElement;

pub use crate::shared::SortMethod;
use crate::{
    callbacks::{GetRowsParams, IGetRowsParams},
    column::ColumnDef,
    convert::ToJsValue,
    grid::AgGrid,
    types::OneOrMany,
    Grid, ToJsValue as ToJsValueMacro,
};
/// An instance of an AG Grid [`GridOptions`].
///
/// With this struct, users can specify the initial options for their grid,
/// before calling the [`GridOptions::build()`] method to receive an instance of
/// [`Grid`]. The various options are fully customisable using the builder
/// pattern, so you only need to specify what you need. The options mirror those
/// used in the AG Grid library.
///
/// [`GridOptions`]: https://www.ag-grid.com/javascript-data-grid/grid-options/
#[derive(FieldSetter, ToJsValueMacro)]
#[js_value(skip_serializing_none)]
pub struct GridOptions<T>
where
    T: ToJsValue,
{
    // Accessories
    // TODO

    // Clipboard
    // TODO

    // Column Definitions
    /// Set the column definitions. Fields set here take precedence over those
    /// set in `default_col_def`.
    column_defs: Option<Vec<ColumnDef>>,
    /// Set the default column definition. Fields set here have lower precedence
    /// than fields set on a per-column basis in `column_defs`.
    default_col_def: Option<ColumnDef>,
    // default_col_group_def
    // column_types
    /// Keeps the order of Columns maintained after new Column Definitions are
    /// updated.
    maintain_column_order: Option<bool>,

    /// If `true`, then dots in field names (e.g. 'address.firstLine') are not
    /// treated as deep references. Allows you to use dots in your field name if
    /// you prefer.
    suppress_dot_field_notation: Option<bool>,

    // Column Headers
    /// The height in pixels for the row containing the column label header. If
    /// not specified, it uses the theme value of `header-height`.
    header_height: Option<u32>,

    /// The height in pixels for the rows containing header column groups. If
    /// not specified, it uses [`GridOptions::header_height`].
    group_header_height: Option<u32>,

    /// The height in pixels for the row containing the floating filters. If not
    /// specified, it uses the theme value of `header-height`.
    floating_filters_height: Option<u32>,

    // Column Moving
    /// Set to `true` to suppress column moving, i.e. to make the columns fixed
    /// position.
    suppress_movable_columns: Option<bool>,

    /// If `true`, the `ag-column-moving` class is not added to the grid while
    /// columns are moving. In the default themes, this results in no animation
    /// when moving columns.
    suppress_column_move_animation: Option<bool>,

    /// If `true`, when you drag a column out of the grid (e.g. to the group
    /// zone) the column is not hidden.
    suppress_drag_leave_hides_columns: Option<bool>,

    /// If `true`, when you drag a column into a row group panel the column is
    /// not hidden.
    suppress_row_group_hides_columns: Option<bool>,

    // Column Sizing
    /// Set to 'Shift' to have shift-resize as the default resize operation
    /// (same as user holding down `Shift` while resizing).
    col_resize_default: Option<ResizeMethod>,

    /// Suppresses auto-sizing columns for columns. In other words, double
    /// clicking a column's header's edge will not auto-size.
    suppress_auto_size: Option<bool>,

    /// Number of pixels to add to a column width after the auto-sizing
    /// calculation. Set this if you want to add extra room to accommodate (for
    /// example) sort icons, or some other dynamic nature of the header.
    auto_size_padding: Option<u32>,

    /// Set this to `true` to skip the `header_name` when `auto_size` is called
    /// by default.
    skip_header_on_auto_size: Option<bool>,

    // Components
    // TODO

    // Editing
    /// Set to 'FullRow' to enable Full Row Editing. Otherwise leave blank to
    /// edit one cell at a time.
    edit_type: Option<EditType>,

    /// Set to `true` to enable Single Click Editing for cells, to start editing
    /// with a single click.
    single_click_edit: Option<bool>,

    /// Set to `true` so that neither single nor double click starts editing.
    suppress_click_edit: Option<bool>,

    /// Set to `true` to stop cell editing when grid loses focus. The default is
    /// that the grid stays editing until focus goes onto another cell.
    stop_editing_when_cells_lose_focus: Option<bool>,

    /// Set to `true` along with [`GridOptions::enter_moves_down_after_edit`] to
    /// have Excel-style behaviour for the `Enter` key, i.e. pressing the
    /// `Enter` key will move down to the cell beneath.
    enter_moves_down: Option<bool>,

    /// Set to `true` along with [`GridOptions::enter_moves_down`] to have
    /// Excel-style behaviour for the `Enter` key, i.e. pressing the `Enter` key
    /// will move down to the cell beneath.
    enter_moves_down_after_edit: Option<bool>,

    /// Set to `true` to enable Undo / Redo while editing.
    undo_redo_cell_editing: Option<bool>,

    /// Set the size of the undo / redo stack.
    undo_redo_cell_editing_limit: Option<u32>,

    /// Set to `true` to stop the grid updating data after and edit. When this
    /// is set, it is intended the application will update the data, e.g. in an
    /// external immutable store, and then pass the new dataset to the grid.
    read_only_edit: Option<bool>,

    // Export
    /// Prevent the user from exporting the grid to CSV.
    suppress_csv_export: Option<bool>,

    /// Prevent the user from exporting the grid to Excel.
    suppress_excel_export: Option<bool>,
    // TODO

    // Filtering
    // TODO

    // Integrated Charts
    // TODO

    // Keyboard Navigation
    // TODO

    // Loading Cell Renderers
    // TODO

    // Localisation
    // TODO

    // Master Detail
    // TODO

    // Miscellaneous
    /// Provide a context object that is provided to different callbacks the
    /// grid uses. Used for passing additional information to the callbacks by
    /// your application.
    context: Option<HashMap<String, String>>,

    /// Change this value to set the tabIndex order of the Grid within your
    /// application.
    tab_index: Option<u32>,

    /// The number of rows rendered outside the viewable area the grid renders.
    /// Having a buffer means the grid will have rows ready to show as the user
    /// slowly scrolls vertically.
    row_buffer: Option<u32>,

    /// Set to `true` to enable debug information from the grid and related
    /// components. Will result in additional logging being output, but very
    /// useful when investigating problems.
    debug: Option<bool>,

    // Overlays
    // TODO

    // Pagination
    /// Set whether pagination is enabled.
    pagination: Option<bool>,

    /// How many rows to load per page. If
    /// [`GridOptions::pagination_auto_page_size`] is specified, this property
    /// is ignored.
    pagination_page_size: Option<u32>,

    /// Set to `true` so that the number of rows to load per page is
    /// automatically adjusted by the grid so each page shows enough rows to
    /// just fill the area designated for the grid. If `false`,
    /// [#GridOption::pagination_page_size`] is used.
    pagination_auto_page_size: Option<bool>,

    /// Set to `true` to have pages split children of groups when using Row
    /// Grouping or detail rows with Master Detail.
    paginate_child_rows: Option<bool>,

    /// If `true`, the default grid controls for navigation are hidden. This is
    /// useful if `pagination=true` and you want to provide your own pagination
    /// controls. Otherwise, when `pagination=true` the grid automatically shows
    /// the necessary controls at the bottom so that the user can navigate
    /// through the different pages.
    suppress_pagination_panel: Option<bool>,

    // Pivot and Aggregation
    // TODO

    // Rendering
    /// Set to `true` to enable Row Animation.
    animate_rows: Option<bool>,

    /// Set to `true` to have cells flash after data changes.
    enable_cell_change_flash: Option<bool>,

    /// To be used in combination with
    /// [`GridOptions::enable_cell_change_flash`], this configuration
    /// will set the delay in milliseconds of how long a cell should remain in
    /// its "flashed" state.
    cell_flash_delay: Option<u32>,

    /// To be used in combination with
    /// [`GridOptions::enable_cell_change_flash`], this configuration
    /// will set the delay in milliseconds of how long the "flashed" state
    /// animation takes to fade away after the timer set by
    /// [`GridOptions::cell_flash_delay`] has completed.
    cell_fade_delay: Option<u32>,

    /// Set to `true` to have cells flash after data changes even when the
    /// change is due to filtering.
    allow_show_change_after_filter: Option<bool>,

    /// Switch between layout options.
    dom_layout: Option<DomLayout>,

    /// When `true`, the order of rows and columns in the DOM are consistent
    /// with what is on screen.
    ensure_dom_order: Option<bool>,

    /// Set to `true` to operate the grid in RTL (Right to Left) mode.
    enable_rtl: Option<bool>,

    /// Set to `true` so that the grid doesn't virtualise the columns. For
    /// example, if you have 100 columns, but only 10 visible due to scrolling,
    /// all 100 will always be rendered.
    suppress_column_virtualisation: Option<bool>,

    /// Set to `true` so that the grid doesn't virtualise the rows. For example,
    /// if you have 100 rows, but only 10 visible due to scrolling, all 100 will
    /// always be rendered.
    suppress_row_virtualisation: Option<bool>,

    /// By default the grid has a limit of rendering a maximum of 500 rows at
    /// once (remember the grid only renders rows you can see, so unless your
    /// display shows more than 500 rows without vertically scrolling this will
    /// never be an issue). This is only relevant if you are manually setting
    /// [`GridOptions::row_buffer`] to a high value (rendering more rows than
    /// can be seen) or if your grid height is able to display more than 500
    /// rows at once.
    suppress_max_rendered_row_restriction: Option<bool>,

    // Row Drag and Drop
    /// Set to `true` to enable Managed Row Dragging.
    row_drag_managed: Option<bool>,

    /// Set to `true` to enable clicking and dragging anywhere on the row
    /// without the need for a drag handle.
    row_drag_entire_row: Option<bool>,

    /// Set to `true` to enable dragging multiple rows at the same time.
    row_drag_multi_row: Option<bool>,

    /// Set to `true` to suppress row dragging.
    suppress_row_drag: Option<bool>,

    /// Set to `true` to suppress moving rows while dragging the row drag
    /// waffle. This option highlights the position where the row will be
    /// placed and it will only move the row on mouse up.
    suppress_move_when_row_dragging: Option<bool>,

    // Row Full Width
    // TODO

    // Row Pinning
    // TODO

    // RowModel
    /// Sets the row model type.
    row_model_type: Option<RowModelType>,
    // get_row_id

    // RowModel: Client Side
    /// Set the row data.
    row_data: Option<Vec<T>>,

    // RowModel: Infinite
    datasource: Option<DataSource>,
    /// How many extra blank rows to display to the user at the end of the
    /// dataset, which sets the vertical scroll and then allows the grid to
    /// request viewing more rows of data.
    cache_overflow_size: Option<u32>,

    /// How many requests to hit the server with concurrently. If the max is
    /// reached, requests are queued. Set to `-1` for no maximum restriction on
    /// requests.
    max_concurrent_datasource_requests: Option<i32>,

    /// How many rows for each block in the store, i.e. how many rows returned
    /// from the server at a time.
    cache_block_size: Option<u32>,

    /// How many blocks to keep in the store. Default is no limit, so every
    /// requested block is kept. Use this if you have memory concerns, and
    /// blocks that were least recently viewed will be purged when the limit is
    /// hit. The grid will additionally make sure it has all the blocks needed
    /// to display what is currently visible, in case this property is set to a
    /// low value.
    max_blocks_in_cache: Option<u32>,

    /// How many extra blank rows to display to the user at the end of the
    /// dataset, which sets the vertical scroll and then allows the grid to
    /// request viewing more rows of data.
    infinite_initial_row_count: Option<u32>,
    // TODO

    // RowModel: Server Side
    // TODO

    // RowModel: Viewport
    // TODO

    // Scrolling
    /// Set to `true` to always show the horizontal scrollbar.
    always_show_horizontal_scroll: Option<bool>,

    /// Set to `true` to always show the vertical scrollbar.
    always_show_vertical_scroll: Option<bool>,

    /// Set to `true` to debounce the vertical scrollbar. Can provide smoother
    /// scrolling on slow machines.
    debounce_vertical_scrollbar: Option<bool>,

    /// Set to `true` to never show the horizontal scroll. This is useful if the
    /// grid is aligned with another grid and will scroll when the other grid
    /// scrolls. (Should not be used in combination with
    /// [`GridOptions::always_show_horizontal_scroll`].)
    suppress_horizontal_scrollbar: Option<bool>,

    /// When `true`, the grid will not scroll to the top when new row data is
    /// provided. Use this if you don't want the default behaviour of scrolling
    /// to the top every time you load new data.
    suppress_scroll_on_new_data: Option<bool>,

    /// When `true`, the grid will not allow mousewheel / touchpad scroll when
    /// popup elements are present.
    suppress_scroll_when_popups_are_open: Option<bool>,

    /// When `true`, the grid will not use animation frames when drawing rows
    /// while scrolling. Use this if the grid is working fast enough that you
    /// don't need animation frames and you don't want the grid to flicker.
    suppress_animation_frame: Option<bool>,

    /// When `true`, middle clicks will result in click events for cells and
    /// rows. Otherwise the browser will use middle click to scroll the grid.
    /// Note: Not all browsers fire click events with the middle button. Most
    /// will fire only mousedown and mouseup events, which can be used to focus
    /// a cell, but will not work to call the onCellClicked function.
    suppress_middle_click_scrolls: Option<bool>,

    /// When `true`, mouse wheel events will be passed to the browser. Useful if
    /// your grid has no vertical scrolls and you want the mouse to scroll the
    /// browser page.
    suppress_prevent_default_on_mouse_wheel: Option<bool>,

    /// Tell the grid how wide in pixels the scrollbar is, which is used in grid
    /// width calculations. Set only if using non-standard browser-provided
    /// scrollbars, so the grid can use the non-standard size in its
    /// calculations.
    scrollbar_width: Option<u32>,

    // Selection
    /// Type of row selection.
    row_selection: Option<RowSelection>,

    /// Set to `true` to allow multiple rows to be selected using single click.
    row_multi_select_with_click: Option<bool>,

    // is_row_selectable
    /// If `true`, rows will not be deselected if you hold down `Ctrl` and click
    /// the row or press `Space`.
    suppress_row_deselection: Option<bool>,

    /// If `true`, row selection won't happen when rows are clicked. Use when
    /// you only want checkbox selection.
    suppress_row_click_selection: Option<bool>,

    /// If `true`, cells won't be focusable. This means keyboard navigation will
    /// be disabled for grid cells, but remain enabled in other elements of the
    /// grid such as column headers, floating filters, tool panels.
    suppress_cell_focus: Option<bool>,

    /// Set to `true` to be able to select the text within cells. Note: When
    /// this is set to true, the clipboard service is disabled.
    enable_cell_text_selection: Option<bool>,

    // Sorting
    /// Vector defining the order in which sorting occurs (if sorting is
    /// enabled).
    sorting_order: Option<Vec<SortMethod>>,

    /// Set to `true` to specify that the sort should take accented characters
    /// into account. If this feature is turned on the sort will be slower.
    accented_sort: Option<bool>,

    /// Set to `true` to show the 'no sort' icon.
    #[js_value(rename = "unSortIcon")]
    unsort_icon: Option<bool>,

    /// Set to `true` to suppress multi-sort when the user shift-clicks a column
    /// header.
    suppress_multi_sort: Option<bool>,

    /// Set to `true` to always multi-sort when the user clicks a column header,
    /// regardless of key presses.
    always_multi_sort: Option<bool>,

    /// Set to 'Ctrl' to have multi sorting work using the `Ctrl` (or `Command
    /// ⌘` for Mac) key.
    multi_sort_key: Option<MultiSortKey>,

    /// Set to `true` to suppress sorting of un-sorted data to match original
    /// row data.
    suppress_maintain_unsorted_order: Option<bool>,

    /// When enabled, sorts only the rows added/updated by a transaction.
    delta_sort: Option<bool>,

    // Styling
    /// Default row height in pixels.
    row_height: Option<u32>,

    /// CSS class(es) for all rows. Provide either a string (class name) or
    /// vector of strings (vector of class names).
    row_class: Option<OneOrMany<String>>,

    /// Set to `true` to not highlight rows by adding the `ag-row-hover` CSS
    /// class.
    suppress_row_hover_highlight: Option<bool>,

    /// Uses CSS `top` instead of CSS `transform` for positioning rows. Useful
    /// if the transform function is causing issues such as used in `row
    /// spanning`.
    suppress_row_transform: Option<bool>,

    /// Set to `true` to highlight columns by adding the `ag-column-hover` CSS
    /// class.
    column_hover_highlight: Option<bool>,

    // Tooltips
    /// Set to `true` to use the browser's default tooltip instead of using the
    /// grid's Tooltip Component.
    enable_browser_tooltips: Option<bool>,

    /// The delay in milliseconds that it takes for tooltips to show up once an
    /// element is hovered over. Note: This property does not work if
    /// [`GridOptions::enable_browser_tooltips`] is `true`.
    tooltip_show_delay: Option<u32>,

    /// The delay in milliseconds that it takes for tooltips to hide once they
    /// have been displayed. Note: This property does not work if
    /// [`GridOptions::enable_browser_tooltips`] is `true`.
    tooltip_hide_delay: Option<u32>,

    /// Set to `true` to have tooltips follow the cursor once they are
    /// displayed.
    tooltip_mouse_track: Option<bool>,
}

impl<T> GridOptions<T>
where
    T: ToJsValue,
{
    pub fn new() -> Self {
        Default::default()
    }

    /// A finaliser method for the [`GridOptions`] struct. This method
    /// constructs the underlying JavaScript grid and returns a handle,
    /// [`Grid`], which provides access to the grid APIs.
    pub fn build(self, div: HtmlElement) -> Grid {
        let grid_options = self.to_js_value();

        let js_grid = AgGrid::new(div, grid_options);

        Grid {
            api: js_grid.gridOptions().api(),
            column_api: js_grid.gridOptions().columnApi(),
        }
    }
}

/// Allowed values for [`GridOptions::multi_sort_key`].
#[derive(ToJsValueMacro)]
pub enum MultiSortKey {
    Ctrl,
}

/// Allowed values for [`GridOptions::dom_layout`].
#[derive(ToJsValueMacro)]
pub enum DomLayout {
    Normal,
    Print,
    AutoHeight,
}

/// Allowed values for [`GridOptions::edit_type`].
#[derive(ToJsValueMacro)]
pub enum EditType {
    FullRow,
}

/// Allowed values for [`GridOptions::col_resize_default`].
#[derive(ToJsValueMacro)]
pub enum ResizeMethod {
    Shift,
}

/// Allowed values for [`GridOptions::row_selection`].
#[derive(ToJsValueMacro)]
pub enum RowSelection {
    Single,
    Multiple,
}

/// Allowed values for [`GridOptions::row_model_type`].
#[derive(ToJsValueMacro)]
pub enum RowModelType {
    Infinite,
    Viewport,
    ClientSide,
    ServerSide,
}

/// A struct passed to the JavaScript grid which is used by AG Grid to fetch the
/// requested data from the server.
#[wasm_bindgen]
#[derive(ToJsValueMacro)]
pub struct DataSource {
    #[wasm_bindgen(readonly, getter_with_clone, js_name = getRows)]
    pub get_rows: Function,
}

/// Builder for the [`DataSource`].
pub struct DataSourceBuilder {
    // Callback the grid calls that the user implements to fetch rows from the
    // server.
    get_rows: Closure<dyn FnMut(IGetRowsParams)>,
    // row_count is deprecated. Use GridOptions.infiniteInitialRowCount instead:
    // https://github.com/ag-grid/ag-grid/blob/7358e4286fd52946c4fe24bd26b5fbe7fd3b22d4/community-modules/core/src/ts/interfaces/iDatasource.ts#L7-L9
    //row_count: Option<u32>,
}

impl DataSourceBuilder {
    /// Start constructing a new `DataSourceBuilder` by providing a callback
    /// function which will receive [`GetRowsParams`]. This callback is
    /// called by AG Grid to request new rows from the server.
    pub fn new<F, Fut, T>(mut get_rows: F) -> Self
    where
        F: FnMut(GetRowsParams) -> Fut + 'static,
        Fut: Future<Output = Result<(Vec<T>, Option<u32>), Box<dyn std::error::Error>>> + 'static,
        T: ToJsValue,
    {
        let get_rows =
            Closure::<dyn FnMut(IGetRowsParams)>::new(move |js_params: IGetRowsParams| {
                let params = (&js_params).into();
                let fut = get_rows(params);

                let wrapper = async move {
                    match fut.await {
                        Ok((data, last_row_index)) => {
                            let data = data.to_js_value();
                            let last_row_index = last_row_index.to_js_value();
                            js_params
                                .success_callback()
                                .call2(&JsValue::null(), &data, &last_row_index)
                                .expect("failed calling success callback");
                        }
                        Err(e) => {
                            log(&format!("Error calling get_rows callback: {e:?}"));
                            js_params
                                .fail_callback()
                                .call0(&JsValue::null())
                                .expect("failed calling failure callback");
                        }
                    };
                };

                spawn_local(wrapper)
            });

        Self { get_rows }
    }

    /// Finalise construction of a [`DataSource`].
    pub fn build(self) -> DataSource {
        DataSource {
            get_rows: self.get_rows.into_js_value().unchecked_into(),
        }
    }
}