Skip to main content

ui_grid_wasm/
lib.rs

1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize, de::DeserializeOwned};
4use serde_json::Value;
5use wasm_bindgen::prelude::*;
6
7use ui_grid_core::{
8    display::format_grid_cell_display_value,
9    edit::{
10        GridMoveDirection, begin_grid_edit_session, build_grid_focus_cell_result,
11        clear_grid_edit_session, find_next_grid_cell, is_grid_cell_position, is_printable_grid_key,
12        parse_grid_edited_value, should_grid_edit_on_focus, stringify_grid_editor_value,
13    },
14    export::{export_csv_rows, header_label},
15    filtering::{clear_grid_filter_reasons, matches_grid_row_filters},
16    grouping::build_grid_display_items,
17    identity::{build_grid_sort_state, find_grid_row_by_id, resolve_grid_row_id},
18    infinite_scroll::{
19        MaybeRequestInfiniteScrollDataContext, complete_infinite_scroll_data_load,
20        maybe_request_infinite_scroll_data, reset_infinite_scroll_state,
21        save_infinite_scroll_percentage, set_infinite_scroll_directions_state,
22    },
23    models::{
24        BuildGridPipelineContext, GridCellPosition, GridColumnDef, GridLabels, GridOptions,
25        GridRecord, GridRow, SortState,
26    },
27    pagination::{
28        get_current_page_value, get_effective_page_size, get_first_row_index_value,
29        get_last_row_index_value, get_total_pages_value, is_virtualization_enabled,
30        paginate_grid_rows, resolve_grid_page_size, seek_grid_page,
31    },
32    pinning::{
33        PinDirection, PinnedColumnState, build_initial_pinned_state, compute_pinned_offset,
34        get_column_pin_direction, is_column_pinnable, is_pinning_enabled, pin_column_state,
35        pinning_button_label,
36    },
37    pipeline::build_grid_pipeline,
38    row_state::{
39        add_grid_row_invisible_reason, are_all_grid_rows_expanded, clear_grid_row_invisible_reason,
40        expand_all_grid_rows, expand_all_grid_tree_rows, get_grid_tree_row_children,
41        set_grid_tree_row_expanded, toggle_grid_row_expanded, toggle_grid_tree_row_expanded,
42    },
43    sorting::sort_grid_rows,
44    state::{
45        BuildGridSavedStateContext, build_grid_saved_state, is_safe_state_key,
46        normalize_boolean_map, normalize_grid_saved_state, sanitize_download_filename,
47    },
48    tree::{build_grid_rows, filter_and_flatten_grid_tree_rows, is_tree_enabled},
49    utils::{get_cell_value, get_path_value, stringify_cell_value, titleize},
50    viewmodel::{
51        can_grid_expand_rows, can_grid_move_columns, grid_cell_indent, grid_column_width,
52        grid_editor_input_type, grid_expand_toggle_label, grid_expand_toggle_label_for_row,
53        grid_filter_placeholder, grid_group_disclosure_label, grid_grouping_button_label,
54        grid_sort_aria_sort, grid_sort_button_label, grid_tree_toggle_label,
55        grid_tree_toggle_label_for_row, is_grid_column_filterable, is_grid_column_grouped,
56        is_grid_column_sortable, is_grid_filtering_enabled, is_grid_grouping_enabled,
57        is_grid_infinite_scroll_enabled, is_grid_pagination_enabled, is_grid_primary_column,
58        is_grid_sorting_enabled, is_grid_tree_enabled, is_grid_tree_row_expanded,
59        resolve_grid_labels, should_show_grid_expand_toggle, should_show_grid_pagination_controls,
60        should_show_grid_tree_toggle,
61    },
62};
63use ui_grid_virtualization::{VirtualWindowRequest, calculate_virtual_window};
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
66#[serde(rename_all = "camelCase")]
67struct OptionsColumnInput {
68    options: GridOptions,
69    column: GridColumnDef,
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize)]
73#[serde(rename_all = "camelCase")]
74struct VisibleColumnsColumnInput {
75    visible_columns: Vec<GridColumnDef>,
76    column: GridColumnDef,
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize)]
80#[serde(rename_all = "camelCase")]
81struct OptionsVisibleColumnsRowColumnInput {
82    options: GridOptions,
83    visible_columns: Vec<GridColumnDef>,
84    row: GridRow,
85    column: GridColumnDef,
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize)]
89#[serde(rename_all = "camelCase")]
90struct OptionsVisibleColumnsColumnInput {
91    options: GridOptions,
92    visible_columns: Vec<GridColumnDef>,
93    column: GridColumnDef,
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize)]
97#[serde(rename_all = "camelCase")]
98struct DirectionLabelsInput {
99    direction: ui_grid_core::constants::SortDirection,
100    labels: GridLabels,
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
104#[serde(rename_all = "camelCase")]
105struct BooleanLabelsInput {
106    value: bool,
107    labels: GridLabels,
108}
109
110#[derive(Debug, Clone, Serialize, Deserialize)]
111#[serde(rename_all = "camelCase")]
112struct GroupByColumnInput {
113    group_by_columns: Vec<String>,
114    column: GridColumnDef,
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize)]
118#[serde(rename_all = "camelCase")]
119struct ExpandedTreeRowsRowInput {
120    expanded_tree_rows: BTreeMap<String, bool>,
121    row: GridRow,
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
125#[serde(rename_all = "camelCase")]
126struct ExpandedTreeRowsRowLabelsInput {
127    expanded_tree_rows: BTreeMap<String, bool>,
128    row: GridRow,
129    labels: GridLabels,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
133#[serde(rename_all = "camelCase")]
134struct RowLabelsInput {
135    row: GridRow,
136    labels: GridLabels,
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize)]
140#[serde(rename_all = "camelCase")]
141struct PinnedColumnsColumnInput {
142    pinned_columns: PinnedColumnState,
143    column: GridColumnDef,
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
147#[serde(rename_all = "camelCase")]
148struct PinColumnStateInput {
149    current: PinnedColumnState,
150    column_name: String,
151    direction: PinDirection,
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize)]
155#[serde(rename_all = "camelCase")]
156struct VisibleColumnsPinnedColumnsColumnInput {
157    visible_columns: Vec<GridColumnDef>,
158    pinned_columns: PinnedColumnState,
159    column: GridColumnDef,
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize)]
163#[serde(rename_all = "camelCase")]
164struct PinnedColumnsColumnLabelsInput {
165    pinned_columns: PinnedColumnState,
166    column: GridColumnDef,
167    labels: GridLabels,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize)]
171#[serde(rename_all = "camelCase")]
172struct CellPositionMatchInput {
173    position: Option<GridCellPosition>,
174    row_id: String,
175    column_name: String,
176}
177
178#[derive(Debug, Clone, Serialize, Deserialize)]
179#[serde(rename_all = "camelCase")]
180struct BeginEditSessionInput {
181    row_id: String,
182    column_name: String,
183    editing_value: String,
184}
185
186#[derive(Debug, Clone, Serialize, Deserialize)]
187#[serde(rename_all = "camelCase")]
188struct BuildGridFocusCellResultInput {
189    current_focused_cell: Option<GridCellPosition>,
190    current_editing_cell: Option<GridCellPosition>,
191    row_id: String,
192    column_name: String,
193    should_edit_on_focus: bool,
194    is_cell_editable: bool,
195}
196
197#[derive(Debug, Clone, Serialize, Deserialize)]
198#[serde(rename_all = "camelCase")]
199struct FindNextGridCellInput {
200    rows: Vec<GridRow>,
201    columns: Vec<GridColumnDef>,
202    row_id: String,
203    column_name: String,
204    direction: GridMoveDirection,
205}
206
207#[derive(Debug, Clone, Serialize, Deserialize)]
208#[serde(rename_all = "camelCase")]
209struct ParseGridEditedValueInput {
210    column: GridColumnDef,
211    value: String,
212    old_value: Value,
213}
214
215#[derive(Debug, Clone, Serialize, Deserialize)]
216#[serde(rename_all = "camelCase")]
217struct PrintableGridKeyInput {
218    key: String,
219    ctrl_key: bool,
220    meta_key: bool,
221    alt_key: bool,
222}
223
224#[derive(Debug, Clone, Serialize, Deserialize)]
225#[serde(rename_all = "camelCase")]
226struct InfiniteScrollLoadInput {
227    state: ui_grid_core::models::GridInfiniteScrollState,
228    scroll_up: bool,
229    scroll_down: bool,
230}
231
232#[derive(Debug, Clone, Serialize, Deserialize)]
233#[serde(rename_all = "camelCase")]
234struct InfiniteScrollVisibleRowsInput {
235    state: ui_grid_core::models::GridInfiniteScrollState,
236    visible_rows: usize,
237}
238
239#[derive(Debug, Clone, Serialize, Deserialize)]
240#[serde(rename_all = "camelCase")]
241struct InfiniteScrollDirectionsInput {
242    state: ui_grid_core::models::GridInfiniteScrollState,
243    scroll_up: bool,
244    scroll_down: bool,
245}
246
247#[derive(Debug, Clone, Serialize, Deserialize)]
248#[serde(rename_all = "camelCase")]
249struct ExpandedRowsRowIdInput {
250    expanded_rows: BTreeMap<String, bool>,
251    row_id: String,
252}
253
254#[derive(Debug, Clone, Serialize, Deserialize)]
255#[serde(rename_all = "camelCase")]
256struct ExpandedTreeRowsRowIdExpandedInput {
257    expanded_tree_rows: BTreeMap<String, bool>,
258    row_id: String,
259    expanded: bool,
260}
261
262#[derive(Debug, Clone, Serialize, Deserialize)]
263#[serde(rename_all = "camelCase")]
264struct RowsExpandedRowsInput {
265    rows: Vec<GridRow>,
266    expanded_rows: BTreeMap<String, bool>,
267}
268
269#[derive(Debug, Clone, Serialize, Deserialize)]
270#[serde(rename_all = "camelCase")]
271struct HiddenRowReasonInput {
272    hidden_row_reasons: BTreeMap<String, Vec<String>>,
273    row_id: String,
274    reason: String,
275}
276
277#[derive(Debug, Clone, Serialize, Deserialize)]
278#[serde(rename_all = "camelCase")]
279struct PaginationInput {
280    options: GridOptions,
281    page_size: usize,
282    total_items: usize,
283}
284
285#[derive(Debug, Clone, Serialize, Deserialize)]
286#[serde(rename_all = "camelCase")]
287struct PaginationPageInput {
288    options: GridOptions,
289    current_page: usize,
290    total_items: usize,
291    page_size: usize,
292}
293
294#[derive(Debug, Clone, Serialize, Deserialize)]
295#[serde(rename_all = "camelCase")]
296struct PaginateRowsInput {
297    rows: Vec<GridRow>,
298    options: GridOptions,
299    current_page: usize,
300    page_size: usize,
301    total_items: usize,
302}
303
304#[derive(Debug, Clone, Serialize, Deserialize)]
305#[serde(rename_all = "camelCase")]
306struct VirtualizationInput {
307    options: GridOptions,
308    item_count: usize,
309}
310
311#[derive(Debug, Clone, Serialize, Deserialize)]
312#[serde(rename_all = "camelCase")]
313struct SeekGridPageInput {
314    page: usize,
315    total_pages: usize,
316}
317
318#[derive(Debug, Clone, Serialize, Deserialize)]
319#[serde(rename_all = "camelCase")]
320struct SortRowsInput {
321    rows: Vec<GridRow>,
322    columns: Vec<GridColumnDef>,
323    options: GridOptions,
324    sort_state: SortState,
325}
326
327#[derive(Debug, Clone, Serialize, Deserialize)]
328#[serde(rename_all = "camelCase")]
329struct FindGridRowByIdInput {
330    rows: Vec<GridRow>,
331    row_id: String,
332}
333
334#[derive(Debug, Clone, Serialize, Deserialize)]
335#[serde(rename_all = "camelCase")]
336struct BuildGridSortStateInput {
337    column_name: String,
338    direction: Option<ui_grid_core::constants::SortDirection>,
339}
340
341#[derive(Debug, Clone, Serialize, Deserialize)]
342#[serde(rename_all = "camelCase")]
343struct ResolveGridRowIdInput {
344    options: GridOptions,
345    row: Value,
346}
347
348#[derive(Debug, Clone, Serialize, Deserialize)]
349#[serde(rename_all = "camelCase")]
350struct FormatGridCellDisplayValueInput {
351    row: GridRow,
352    column: GridColumnDef,
353}
354
355#[derive(Debug, Clone, Serialize, Deserialize)]
356#[serde(rename_all = "camelCase")]
357struct HeaderLabelInput {
358    column: GridColumnDef,
359}
360
361#[derive(Debug, Clone, Serialize, Deserialize)]
362#[serde(rename_all = "camelCase")]
363struct MatchesGridRowFiltersInput {
364    row: GridRow,
365    columns: Vec<GridColumnDef>,
366    options: GridOptions,
367    active_filters: BTreeMap<String, String>,
368}
369
370#[derive(Debug, Clone, Serialize, Deserialize)]
371#[serde(rename_all = "camelCase")]
372struct MatchesGridRowFiltersResult {
373    row: GridRow,
374    matches: bool,
375}
376
377#[derive(Debug, Clone, Serialize, Deserialize)]
378#[serde(rename_all = "camelCase")]
379struct BuildGridRowsInput {
380    options: GridOptions,
381    row_size: usize,
382    hidden_row_reasons: BTreeMap<String, Vec<String>>,
383    expanded_rows: BTreeMap<String, bool>,
384}
385
386#[derive(Debug, Clone, Serialize, Deserialize)]
387#[serde(rename_all = "camelCase")]
388struct FilterFlattenTreeRowsInput {
389    rows: Vec<GridRow>,
390    columns: Vec<GridColumnDef>,
391    options: GridOptions,
392    active_filters: BTreeMap<String, String>,
393    expanded_tree_rows: BTreeMap<String, bool>,
394    sort_state: SortState,
395}
396
397#[derive(Debug, Clone, Serialize, Deserialize)]
398#[serde(rename_all = "camelCase")]
399struct PathValueInput {
400    record: GridRecord,
401    path: String,
402}
403
404#[derive(Debug, Clone, Serialize, Deserialize)]
405#[serde(rename_all = "camelCase")]
406struct CellValueInput {
407    row: GridRecord,
408    column: GridColumnDef,
409}
410
411#[derive(Debug, Clone, Serialize, Deserialize)]
412#[serde(rename_all = "camelCase")]
413struct StringifyCellValueInput {
414    value: Value,
415}
416
417#[derive(Debug, Clone, Serialize, Deserialize)]
418#[serde(rename_all = "camelCase")]
419struct TitleizeInput {
420    value: String,
421}
422
423#[derive(Debug, Clone, Serialize, Deserialize)]
424#[serde(rename_all = "camelCase")]
425struct BuildGridDisplayItemsInput {
426    rows: Vec<GridRow>,
427    columns: Vec<GridColumnDef>,
428    options: GridOptions,
429    group_by: Vec<String>,
430    collapsed_groups: BTreeMap<String, bool>,
431}
432
433#[derive(Debug, Clone, Serialize, Deserialize)]
434#[serde(rename_all = "camelCase")]
435struct NormalizeBooleanMapInput {
436    value: serde_json::Map<String, Value>,
437}
438
439fn from_js<T: DeserializeOwned>(value: JsValue) -> Result<T, JsValue> {
440    serde_wasm_bindgen::from_value(value)
441        .map_err(|error| JsValue::from_str(&format!("failed to decode wasm input: {error}")))
442}
443
444fn to_js<T: serde::Serialize>(value: &T) -> Result<JsValue, JsValue> {
445    let serializer = serde_wasm_bindgen::Serializer::json_compatible();
446    value
447        .serialize(&serializer)
448        .map_err(|error| JsValue::from_str(&format!("failed to encode wasm output: {error}")))
449}
450
451#[wasm_bindgen(start)]
452pub fn start() {
453    console_error_panic_hook::set_once();
454}
455
456#[wasm_bindgen]
457pub fn version() -> String {
458    env!("CARGO_PKG_VERSION").to_string()
459}
460
461// Pipeline
462
463#[wasm_bindgen]
464pub fn build_pipeline_js(context: JsValue) -> Result<JsValue, JsValue> {
465    let context: BuildGridPipelineContext = from_js(context)?;
466    let result = build_grid_pipeline(&context);
467    to_js(&result)
468}
469
470#[wasm_bindgen]
471pub fn build_grid_pipeline_js(context: JsValue) -> Result<JsValue, JsValue> {
472    build_pipeline_js(context)
473}
474
475// Viewmodel
476
477#[wasm_bindgen]
478pub fn resolve_grid_labels_js(overrides: JsValue) -> Result<JsValue, JsValue> {
479    if overrides.is_null() || overrides.is_undefined() {
480        return to_js(&resolve_grid_labels(None));
481    }
482
483    let overrides: GridLabels = from_js(overrides)?;
484    to_js(&resolve_grid_labels(Some(&overrides)))
485}
486
487#[wasm_bindgen]
488pub fn is_grid_tree_enabled_js(options: JsValue) -> Result<bool, JsValue> {
489    let options: GridOptions = from_js(options)?;
490    Ok(is_grid_tree_enabled(&options))
491}
492
493#[wasm_bindgen]
494pub fn is_grid_grouping_enabled_js(options: JsValue) -> Result<bool, JsValue> {
495    let options: GridOptions = from_js(options)?;
496    Ok(is_grid_grouping_enabled(&options))
497}
498
499#[wasm_bindgen]
500pub fn can_grid_expand_rows_js(options: JsValue) -> Result<bool, JsValue> {
501    let options: GridOptions = from_js(options)?;
502    Ok(can_grid_expand_rows(&options))
503}
504
505#[wasm_bindgen]
506pub fn is_grid_pagination_enabled_js(options: JsValue) -> Result<bool, JsValue> {
507    let options: GridOptions = from_js(options)?;
508    Ok(is_grid_pagination_enabled(&options))
509}
510
511#[wasm_bindgen]
512pub fn should_show_grid_pagination_controls_js(options: JsValue) -> Result<bool, JsValue> {
513    let options: GridOptions = from_js(options)?;
514    Ok(should_show_grid_pagination_controls(&options))
515}
516
517#[wasm_bindgen]
518pub fn is_grid_infinite_scroll_enabled_js(options: JsValue) -> Result<bool, JsValue> {
519    let options: GridOptions = from_js(options)?;
520    Ok(is_grid_infinite_scroll_enabled(&options))
521}
522
523#[wasm_bindgen]
524pub fn is_grid_sorting_enabled_js(options: JsValue) -> Result<bool, JsValue> {
525    let options: GridOptions = from_js(options)?;
526    Ok(is_grid_sorting_enabled(&options))
527}
528
529#[wasm_bindgen]
530pub fn is_grid_filtering_enabled_js(options: JsValue) -> Result<bool, JsValue> {
531    let options: GridOptions = from_js(options)?;
532    Ok(is_grid_filtering_enabled(&options))
533}
534
535#[wasm_bindgen]
536pub fn can_grid_move_columns_js(options: JsValue) -> Result<bool, JsValue> {
537    let options: GridOptions = from_js(options)?;
538    Ok(can_grid_move_columns(&options))
539}
540
541#[wasm_bindgen]
542pub fn is_grid_primary_column_js(input: JsValue) -> Result<bool, JsValue> {
543    let input: VisibleColumnsColumnInput = from_js(input)?;
544    Ok(is_grid_primary_column(
545        &input.visible_columns,
546        &input.column,
547    ))
548}
549
550#[wasm_bindgen]
551pub fn is_grid_column_sortable_js(input: JsValue) -> Result<bool, JsValue> {
552    let input: OptionsColumnInput = from_js(input)?;
553    Ok(is_grid_column_sortable(&input.options, &input.column))
554}
555
556#[wasm_bindgen]
557pub fn is_grid_column_filterable_js(input: JsValue) -> Result<bool, JsValue> {
558    let input: OptionsColumnInput = from_js(input)?;
559    Ok(is_grid_column_filterable(&input.options, &input.column))
560}
561
562#[wasm_bindgen]
563pub fn should_show_grid_tree_toggle_js(input: JsValue) -> Result<bool, JsValue> {
564    let input: OptionsVisibleColumnsRowColumnInput = from_js(input)?;
565    Ok(should_show_grid_tree_toggle(
566        &input.options,
567        &input.visible_columns,
568        &input.row,
569        &input.column,
570    ))
571}
572
573#[wasm_bindgen]
574pub fn should_show_grid_expand_toggle_js(input: JsValue) -> Result<bool, JsValue> {
575    let input: OptionsVisibleColumnsColumnInput = from_js(input)?;
576    Ok(should_show_grid_expand_toggle(
577        &input.options,
578        &input.visible_columns,
579        &input.column,
580    ))
581}
582
583#[wasm_bindgen]
584pub fn grid_sort_button_label_js(input: JsValue) -> Result<String, JsValue> {
585    let input: DirectionLabelsInput = from_js(input)?;
586    Ok(grid_sort_button_label(input.direction, &input.labels))
587}
588
589#[wasm_bindgen]
590pub fn grid_sort_aria_sort_js(direction: JsValue) -> Result<String, JsValue> {
591    let direction: ui_grid_core::constants::SortDirection = from_js(direction)?;
592    Ok(grid_sort_aria_sort(direction))
593}
594
595#[wasm_bindgen]
596pub fn grid_grouping_button_label_js(input: JsValue) -> Result<String, JsValue> {
597    let input: BooleanLabelsInput = from_js(input)?;
598    Ok(grid_grouping_button_label(input.value, &input.labels))
599}
600
601#[wasm_bindgen]
602pub fn grid_filter_placeholder_js(input: JsValue) -> Result<String, JsValue> {
603    let input: BooleanLabelsInput = from_js(input)?;
604    Ok(grid_filter_placeholder(input.value, &input.labels))
605}
606
607#[wasm_bindgen]
608pub fn grid_group_disclosure_label_js(input: JsValue) -> Result<String, JsValue> {
609    let input: BooleanLabelsInput = from_js(input)?;
610    Ok(grid_group_disclosure_label(input.value, &input.labels))
611}
612
613#[wasm_bindgen]
614pub fn grid_editor_input_type_js(column: JsValue) -> Result<String, JsValue> {
615    let column: GridColumnDef = from_js(column)?;
616    Ok(grid_editor_input_type(&column))
617}
618
619#[wasm_bindgen]
620pub fn grid_column_width_js(column: JsValue) -> Result<String, JsValue> {
621    let column: GridColumnDef = from_js(column)?;
622    Ok(grid_column_width(&column))
623}
624
625#[wasm_bindgen]
626pub fn grid_cell_indent_js(input: JsValue) -> Result<String, JsValue> {
627    let input: OptionsVisibleColumnsRowColumnInput = from_js(input)?;
628    Ok(grid_cell_indent(
629        &input.options,
630        &input.visible_columns,
631        &input.row,
632        &input.column,
633    ))
634}
635
636#[wasm_bindgen]
637pub fn grid_tree_toggle_label_js(input: JsValue) -> Result<String, JsValue> {
638    let input: BooleanLabelsInput = from_js(input)?;
639    Ok(grid_tree_toggle_label(input.value, &input.labels))
640}
641
642#[wasm_bindgen]
643pub fn grid_expand_toggle_label_js(input: JsValue) -> Result<String, JsValue> {
644    let input: BooleanLabelsInput = from_js(input)?;
645    Ok(grid_expand_toggle_label(input.value, &input.labels))
646}
647
648#[wasm_bindgen]
649pub fn is_grid_column_grouped_js(input: JsValue) -> Result<bool, JsValue> {
650    let input: GroupByColumnInput = from_js(input)?;
651    Ok(is_grid_column_grouped(
652        &input.group_by_columns,
653        &input.column,
654    ))
655}
656
657#[wasm_bindgen]
658pub fn is_grid_tree_row_expanded_js(input: JsValue) -> Result<bool, JsValue> {
659    let input: ExpandedTreeRowsRowInput = from_js(input)?;
660    Ok(is_grid_tree_row_expanded(
661        &input.expanded_tree_rows,
662        &input.row,
663    ))
664}
665
666#[wasm_bindgen]
667pub fn grid_tree_toggle_label_for_row_js(input: JsValue) -> Result<String, JsValue> {
668    let input: ExpandedTreeRowsRowLabelsInput = from_js(input)?;
669    Ok(grid_tree_toggle_label_for_row(
670        &input.expanded_tree_rows,
671        &input.row,
672        &input.labels,
673    ))
674}
675
676#[wasm_bindgen]
677pub fn grid_expand_toggle_label_for_row_js(input: JsValue) -> Result<String, JsValue> {
678    let input: RowLabelsInput = from_js(input)?;
679    Ok(grid_expand_toggle_label_for_row(&input.row, &input.labels))
680}
681
682// Pinning
683
684#[wasm_bindgen]
685pub fn is_pinning_enabled_js(options: JsValue) -> Result<bool, JsValue> {
686    let options: GridOptions = from_js(options)?;
687    Ok(is_pinning_enabled(&options))
688}
689
690#[wasm_bindgen]
691pub fn is_column_pinnable_js(input: JsValue) -> Result<bool, JsValue> {
692    let input: OptionsColumnInput = from_js(input)?;
693    Ok(is_column_pinnable(&input.options, &input.column))
694}
695
696#[wasm_bindgen]
697pub fn get_column_pin_direction_js(input: JsValue) -> Result<JsValue, JsValue> {
698    let input: PinnedColumnsColumnInput = from_js(input)?;
699    to_js(&get_column_pin_direction(
700        &input.pinned_columns,
701        &input.column,
702    ))
703}
704
705#[wasm_bindgen]
706pub fn pin_column_state_js(input: JsValue) -> Result<JsValue, JsValue> {
707    let input: PinColumnStateInput = from_js(input)?;
708    to_js(&pin_column_state(
709        &input.current,
710        &input.column_name,
711        input.direction,
712    ))
713}
714
715#[wasm_bindgen]
716pub fn build_initial_pinned_state_js(columns: JsValue) -> Result<JsValue, JsValue> {
717    let columns: Vec<GridColumnDef> = from_js(columns)?;
718    to_js(&build_initial_pinned_state(&columns))
719}
720
721#[wasm_bindgen]
722pub fn compute_pinned_offset_js(input: JsValue) -> Result<JsValue, JsValue> {
723    let input: VisibleColumnsPinnedColumnsColumnInput = from_js(input)?;
724    to_js(&compute_pinned_offset(
725        &input.visible_columns,
726        &input.pinned_columns,
727        &input.column,
728    ))
729}
730
731#[wasm_bindgen]
732pub fn pinning_button_label_js(input: JsValue) -> Result<String, JsValue> {
733    let input: PinnedColumnsColumnLabelsInput = from_js(input)?;
734    Ok(pinning_button_label(
735        &input.pinned_columns,
736        &input.column,
737        &input.labels,
738    ))
739}
740
741// Edit
742
743#[wasm_bindgen]
744pub fn is_grid_cell_position_js(input: JsValue) -> Result<bool, JsValue> {
745    let input: CellPositionMatchInput = from_js(input)?;
746    Ok(is_grid_cell_position(
747        input.position.as_ref(),
748        &input.row_id,
749        &input.column_name,
750    ))
751}
752
753#[wasm_bindgen]
754pub fn begin_grid_edit_session_js(input: JsValue) -> Result<JsValue, JsValue> {
755    let input: BeginEditSessionInput = from_js(input)?;
756    to_js(&begin_grid_edit_session(
757        input.row_id,
758        input.column_name,
759        input.editing_value,
760    ))
761}
762
763#[wasm_bindgen]
764pub fn should_grid_edit_on_focus_js(input: JsValue) -> Result<bool, JsValue> {
765    let input: OptionsColumnInput = from_js(input)?;
766    Ok(should_grid_edit_on_focus(&input.options, &input.column))
767}
768
769#[wasm_bindgen]
770pub fn build_grid_focus_cell_result_js(input: JsValue) -> Result<JsValue, JsValue> {
771    let input: BuildGridFocusCellResultInput = from_js(input)?;
772    to_js(&build_grid_focus_cell_result(
773        input.current_focused_cell.as_ref(),
774        input.current_editing_cell.as_ref(),
775        input.row_id,
776        input.column_name,
777        input.should_edit_on_focus,
778        input.is_cell_editable,
779    ))
780}
781
782#[wasm_bindgen]
783pub fn clear_grid_edit_session_js() -> Result<JsValue, JsValue> {
784    to_js(&clear_grid_edit_session())
785}
786
787#[wasm_bindgen]
788pub fn find_next_grid_cell_js(input: JsValue) -> Result<JsValue, JsValue> {
789    let input: FindNextGridCellInput = from_js(input)?;
790    to_js(
791        &find_next_grid_cell::<fn(&GridRow, &GridColumnDef) -> bool>(
792            &input.rows,
793            &input.columns,
794            &input.row_id,
795            &input.column_name,
796            input.direction,
797            None,
798        ),
799    )
800}
801
802#[wasm_bindgen]
803pub fn stringify_grid_editor_value_js(value: JsValue) -> Result<String, JsValue> {
804    let value: Value = from_js(value)?;
805    Ok(stringify_grid_editor_value(&value))
806}
807
808#[wasm_bindgen]
809pub fn parse_grid_edited_value_js(input: JsValue) -> Result<JsValue, JsValue> {
810    let input: ParseGridEditedValueInput = from_js(input)?;
811    to_js(&parse_grid_edited_value(
812        &input.column,
813        &input.value,
814        &input.old_value,
815    ))
816}
817
818#[wasm_bindgen]
819pub fn is_printable_grid_key_js(input: JsValue) -> Result<bool, JsValue> {
820    let input: PrintableGridKeyInput = from_js(input)?;
821    Ok(is_printable_grid_key(
822        &input.key,
823        input.ctrl_key,
824        input.meta_key,
825        input.alt_key,
826    ))
827}
828
829// Row state
830
831#[wasm_bindgen]
832pub fn toggle_grid_row_expanded_js(input: JsValue) -> Result<JsValue, JsValue> {
833    let input: ExpandedRowsRowIdInput = from_js(input)?;
834    to_js(&toggle_grid_row_expanded(
835        &input.expanded_rows,
836        &input.row_id,
837    ))
838}
839
840#[wasm_bindgen]
841pub fn expand_all_grid_rows_js(rows: JsValue) -> Result<JsValue, JsValue> {
842    let rows: Vec<GridRow> = from_js(rows)?;
843    to_js(&expand_all_grid_rows(&rows))
844}
845
846#[wasm_bindgen]
847pub fn are_all_grid_rows_expanded_js(input: JsValue) -> Result<bool, JsValue> {
848    let input: RowsExpandedRowsInput = from_js(input)?;
849    Ok(are_all_grid_rows_expanded(
850        &input.rows,
851        &input.expanded_rows,
852    ))
853}
854
855#[wasm_bindgen]
856pub fn set_grid_tree_row_expanded_js(input: JsValue) -> Result<JsValue, JsValue> {
857    let input: ExpandedTreeRowsRowIdExpandedInput = from_js(input)?;
858    to_js(&set_grid_tree_row_expanded(
859        &input.expanded_tree_rows,
860        &input.row_id,
861        input.expanded,
862    ))
863}
864
865#[wasm_bindgen]
866pub fn toggle_grid_tree_row_expanded_js(input: JsValue) -> Result<JsValue, JsValue> {
867    let input: ExpandedRowsRowIdInput = from_js(input)?;
868    to_js(&toggle_grid_tree_row_expanded(
869        &input.expanded_rows,
870        &input.row_id,
871    ))
872}
873
874#[wasm_bindgen]
875pub fn expand_all_grid_tree_rows_js(rows: JsValue) -> Result<JsValue, JsValue> {
876    let rows: Vec<GridRow> = from_js(rows)?;
877    to_js(&expand_all_grid_tree_rows(&rows))
878}
879
880#[wasm_bindgen]
881pub fn get_grid_tree_row_children_js(input: JsValue) -> Result<JsValue, JsValue> {
882    let input: FindGridRowByIdInput = from_js(input)?;
883    to_js(&get_grid_tree_row_children(&input.rows, &input.row_id))
884}
885
886#[wasm_bindgen]
887pub fn add_grid_row_invisible_reason_js(input: JsValue) -> Result<JsValue, JsValue> {
888    let input: HiddenRowReasonInput = from_js(input)?;
889    to_js(&add_grid_row_invisible_reason(
890        &input.hidden_row_reasons,
891        &input.row_id,
892        &input.reason,
893    ))
894}
895
896#[wasm_bindgen]
897pub fn clear_grid_row_invisible_reason_js(input: JsValue) -> Result<JsValue, JsValue> {
898    let input: HiddenRowReasonInput = from_js(input)?;
899    to_js(&clear_grid_row_invisible_reason(
900        &input.hidden_row_reasons,
901        &input.row_id,
902        &input.reason,
903    ))
904}
905
906// Pagination
907
908#[wasm_bindgen]
909pub fn get_effective_page_size_js(input: JsValue) -> Result<usize, JsValue> {
910    let input: PaginationInput = from_js(input)?;
911    Ok(get_effective_page_size(
912        &input.options,
913        input.page_size,
914        input.total_items,
915    ))
916}
917
918#[wasm_bindgen]
919pub fn get_total_pages_value_js(input: JsValue) -> Result<usize, JsValue> {
920    let input: PaginationInput = from_js(input)?;
921    Ok(get_total_pages_value(
922        &input.options,
923        input.total_items,
924        input.page_size,
925    ))
926}
927
928#[wasm_bindgen]
929pub fn get_current_page_value_js(input: JsValue) -> Result<usize, JsValue> {
930    let input: PaginationPageInput = from_js(input)?;
931    Ok(get_current_page_value(
932        &input.options,
933        input.current_page,
934        input.total_items,
935        input.page_size,
936    ))
937}
938
939#[wasm_bindgen]
940pub fn get_first_row_index_value_js(input: JsValue) -> Result<usize, JsValue> {
941    let input: PaginationPageInput = from_js(input)?;
942    Ok(get_first_row_index_value(
943        &input.options,
944        input.current_page,
945        input.total_items,
946        input.page_size,
947    ))
948}
949
950#[wasm_bindgen]
951pub fn get_last_row_index_value_js(input: JsValue) -> Result<usize, JsValue> {
952    let input: PaginationPageInput = from_js(input)?;
953    Ok(get_last_row_index_value(
954        &input.options,
955        input.current_page,
956        input.total_items,
957        input.page_size,
958    ))
959}
960
961#[wasm_bindgen]
962pub fn paginate_grid_rows_js(input: JsValue) -> Result<JsValue, JsValue> {
963    let input: PaginateRowsInput = from_js(input)?;
964    to_js(&paginate_grid_rows(
965        &input.rows,
966        &input.options,
967        input.current_page,
968        input.page_size,
969        input.total_items,
970    ))
971}
972
973#[wasm_bindgen]
974pub fn is_virtualization_enabled_js(input: JsValue) -> Result<bool, JsValue> {
975    let input: VirtualizationInput = from_js(input)?;
976    Ok(is_virtualization_enabled(&input.options, input.item_count))
977}
978
979#[wasm_bindgen]
980pub fn seek_grid_page_js(input: JsValue) -> Result<usize, JsValue> {
981    let input: SeekGridPageInput = from_js(input)?;
982    Ok(seek_grid_page(input.page, input.total_pages))
983}
984
985#[wasm_bindgen]
986pub fn resolve_grid_page_size_js(page_size: usize) -> Result<JsValue, JsValue> {
987    to_js(&resolve_grid_page_size(page_size))
988}
989
990// State
991
992#[wasm_bindgen]
993pub fn export_csv_rows_js(columns: JsValue, rows: JsValue) -> Result<String, JsValue> {
994    let columns: Vec<GridColumnDef> = from_js(columns)?;
995    let rows: Vec<GridRow> = from_js(rows)?;
996    Ok(export_csv_rows(&columns, &rows))
997}
998
999#[wasm_bindgen]
1000pub fn build_saved_state_js(context: JsValue) -> Result<JsValue, JsValue> {
1001    let context: BuildGridSavedStateContext = from_js(context)?;
1002    let result = build_grid_saved_state(context);
1003    to_js(&result)
1004}
1005
1006#[wasm_bindgen]
1007pub fn build_grid_saved_state_js(context: JsValue) -> Result<JsValue, JsValue> {
1008    build_saved_state_js(context)
1009}
1010
1011#[wasm_bindgen]
1012pub fn normalize_saved_state_js(state: JsValue) -> Result<JsValue, JsValue> {
1013    let state: serde_json::Value = from_js(state)?;
1014    let result = normalize_grid_saved_state(&state);
1015    to_js(&result)
1016}
1017
1018#[wasm_bindgen]
1019pub fn normalize_grid_saved_state_js(state: JsValue) -> Result<JsValue, JsValue> {
1020    normalize_saved_state_js(state)
1021}
1022
1023#[wasm_bindgen]
1024pub fn sanitize_download_filename_js(value: String) -> String {
1025    sanitize_download_filename(&value)
1026}
1027
1028#[wasm_bindgen]
1029pub fn normalize_boolean_map_js(input: JsValue) -> Result<JsValue, JsValue> {
1030    let input: NormalizeBooleanMapInput = from_js(input)?;
1031    to_js(&normalize_boolean_map(&input.value))
1032}
1033
1034#[wasm_bindgen]
1035pub fn is_safe_state_key_js(value: String) -> bool {
1036    is_safe_state_key(&value)
1037}
1038
1039// Identity
1040
1041#[wasm_bindgen]
1042pub fn find_grid_row_by_id_js(input: JsValue) -> Result<JsValue, JsValue> {
1043    let input: FindGridRowByIdInput = from_js(input)?;
1044    to_js(&find_grid_row_by_id(&input.rows, &input.row_id))
1045}
1046
1047#[wasm_bindgen]
1048pub fn build_grid_sort_state_js(input: JsValue) -> Result<JsValue, JsValue> {
1049    let input: BuildGridSortStateInput = from_js(input)?;
1050    to_js(&build_grid_sort_state(input.column_name, input.direction))
1051}
1052
1053#[wasm_bindgen]
1054pub fn resolve_grid_row_id_js(input: JsValue) -> Result<String, JsValue> {
1055    let input: ResolveGridRowIdInput = from_js(input)?;
1056    Ok(resolve_grid_row_id(&input.options, &input.row))
1057}
1058
1059// Infinite scroll
1060
1061#[wasm_bindgen]
1062pub fn maybe_request_infinite_scroll_data_js(input: JsValue) -> Result<JsValue, JsValue> {
1063    let input: MaybeRequestInfiniteScrollDataContext = from_js(input)?;
1064    to_js(&maybe_request_infinite_scroll_data(&input))
1065}
1066
1067#[wasm_bindgen]
1068pub fn complete_infinite_scroll_data_load_js(input: JsValue) -> Result<JsValue, JsValue> {
1069    let input: InfiniteScrollLoadInput = from_js(input)?;
1070    to_js(&complete_infinite_scroll_data_load(
1071        &input.state,
1072        input.scroll_up,
1073        input.scroll_down,
1074    ))
1075}
1076
1077#[wasm_bindgen]
1078pub fn reset_infinite_scroll_state_js(input: JsValue) -> Result<JsValue, JsValue> {
1079    let input: InfiniteScrollDirectionsInput = from_js(input)?;
1080    to_js(&reset_infinite_scroll_state(
1081        input.scroll_up,
1082        input.scroll_down,
1083    ))
1084}
1085
1086#[wasm_bindgen]
1087pub fn save_infinite_scroll_percentage_js(input: JsValue) -> Result<JsValue, JsValue> {
1088    let input: InfiniteScrollVisibleRowsInput = from_js(input)?;
1089    to_js(&save_infinite_scroll_percentage(
1090        &input.state,
1091        input.visible_rows,
1092    ))
1093}
1094
1095#[wasm_bindgen]
1096pub fn set_infinite_scroll_directions_state_js(input: JsValue) -> Result<JsValue, JsValue> {
1097    let input: InfiniteScrollDirectionsInput = from_js(input)?;
1098    to_js(&set_infinite_scroll_directions_state(
1099        &input.state,
1100        input.scroll_up,
1101        input.scroll_down,
1102    ))
1103}
1104
1105// Display/export/filter/sort/tree/utils/grouping
1106
1107#[wasm_bindgen]
1108pub fn format_grid_cell_display_value_js(input: JsValue) -> Result<String, JsValue> {
1109    let input: FormatGridCellDisplayValueInput = from_js(input)?;
1110    Ok(format_grid_cell_display_value(&input.row, &input.column))
1111}
1112
1113#[wasm_bindgen]
1114pub fn header_label_js(input: JsValue) -> Result<String, JsValue> {
1115    let input: HeaderLabelInput = from_js(input)?;
1116    Ok(header_label(&input.column))
1117}
1118
1119#[wasm_bindgen]
1120pub fn clear_grid_filter_reasons_js(row: JsValue) -> Result<JsValue, JsValue> {
1121    let mut row: GridRow = from_js(row)?;
1122    clear_grid_filter_reasons(&mut row);
1123    to_js(&row)
1124}
1125
1126#[wasm_bindgen]
1127pub fn matches_grid_row_filters_js(input: JsValue) -> Result<JsValue, JsValue> {
1128    let mut input: MatchesGridRowFiltersInput = from_js(input)?;
1129    let matches = matches_grid_row_filters(
1130        &mut input.row,
1131        &input.columns,
1132        &input.options,
1133        &input.active_filters,
1134    );
1135    to_js(&MatchesGridRowFiltersResult {
1136        row: input.row,
1137        matches,
1138    })
1139}
1140
1141#[wasm_bindgen]
1142pub fn sort_grid_rows_js(input: JsValue) -> Result<JsValue, JsValue> {
1143    let input: SortRowsInput = from_js(input)?;
1144    to_js(&sort_grid_rows(
1145        &input.rows,
1146        &input.columns,
1147        &input.options,
1148        &input.sort_state,
1149    ))
1150}
1151
1152#[wasm_bindgen]
1153pub fn build_grid_rows_js(input: JsValue) -> Result<JsValue, JsValue> {
1154    let input: BuildGridRowsInput = from_js(input)?;
1155    to_js(&build_grid_rows(
1156        &input.options,
1157        input.row_size,
1158        &input.hidden_row_reasons,
1159        &input.expanded_rows,
1160    ))
1161}
1162
1163#[wasm_bindgen]
1164pub fn is_tree_enabled_js(options: JsValue) -> Result<bool, JsValue> {
1165    let options: GridOptions = from_js(options)?;
1166    Ok(is_tree_enabled(&options))
1167}
1168
1169#[wasm_bindgen]
1170pub fn filter_and_flatten_grid_tree_rows_js(input: JsValue) -> Result<JsValue, JsValue> {
1171    let input: FilterFlattenTreeRowsInput = from_js(input)?;
1172    to_js(&filter_and_flatten_grid_tree_rows(
1173        &input.rows,
1174        &input.columns,
1175        &input.options,
1176        &input.active_filters,
1177        &input.expanded_tree_rows,
1178        &input.sort_state,
1179    ))
1180}
1181
1182#[wasm_bindgen]
1183pub fn get_path_value_js(input: JsValue) -> Result<JsValue, JsValue> {
1184    let input: PathValueInput = from_js(input)?;
1185    to_js(&get_path_value(&input.record, &input.path))
1186}
1187
1188#[wasm_bindgen]
1189pub fn get_cell_value_js(input: JsValue) -> Result<JsValue, JsValue> {
1190    let input: CellValueInput = from_js(input)?;
1191    to_js(&get_cell_value(&input.row, &input.column))
1192}
1193
1194#[wasm_bindgen]
1195pub fn stringify_cell_value_js(input: JsValue) -> Result<String, JsValue> {
1196    let input: StringifyCellValueInput = from_js(input)?;
1197    Ok(stringify_cell_value(&input.value))
1198}
1199
1200#[wasm_bindgen]
1201pub fn titleize_js(input: JsValue) -> Result<String, JsValue> {
1202    let input: TitleizeInput = from_js(input)?;
1203    Ok(titleize(&input.value))
1204}
1205
1206#[wasm_bindgen]
1207pub fn build_grid_display_items_js(input: JsValue) -> Result<JsValue, JsValue> {
1208    let input: BuildGridDisplayItemsInput = from_js(input)?;
1209    to_js(&build_grid_display_items(
1210        &input.rows,
1211        &input.columns,
1212        &input.options,
1213        &input.group_by,
1214        &input.collapsed_groups,
1215    ))
1216}
1217
1218// Virtualization
1219
1220#[wasm_bindgen]
1221pub fn calculate_virtual_window_js(request: JsValue) -> Result<JsValue, JsValue> {
1222    let request: VirtualWindowRequest = from_js(request)?;
1223    let result = calculate_virtual_window(&request);
1224    to_js(&result)
1225}