diff --git a/packages/modules/data-widgets/src/themesource/datawidgets/web/_datagrid.scss b/packages/modules/data-widgets/src/themesource/datawidgets/web/_datagrid.scss
index 8ca532f190..e72d3f0a9f 100644
--- a/packages/modules/data-widgets/src/themesource/datawidgets/web/_datagrid.scss
+++ b/packages/modules/data-widgets/src/themesource/datawidgets/web/_datagrid.scss
@@ -395,6 +395,11 @@ $root: ".widget-datagrid";
display: grid !important;
min-width: fit-content;
margin-bottom: 0;
+ &.infinite-loading {
+ // in order to restrict the scroll to row area
+ // we need to prevent table itself to expanding beyond available position
+ min-width: 0;
+ }
}
}
@@ -530,24 +535,57 @@ $root: ".widget-datagrid";
margin: var(--spacing-small, 8px) 0;
}
-.infinite-loading.widget-datagrid-grid-body {
- // when virtual scroll is enabled we make area that holds rows scrollable
- // (while the area that holds column headers still stays in place)
- overflow-y: auto;
+.infinite-loading {
+ .widget-datagrid-grid-head {
+ width: calc(var(--widgets-grid-width) - var(--widgets-grid-scrollbar-size));
+ overflow-x: hidden;
+ }
+ .widget-datagrid-grid-head[data-scrolled-y="true"] {
+ box-shadow: 0 5px 5px -5px gray;
+ }
+
+ .widget-datagrid-grid-body {
+ width: var(--widgets-grid-width);
+ overflow-y: auto;
+ max-height: var(--widgets-grid-body-height);
+ }
+
+ .widget-datagrid-grid-head[data-scrolled-x="true"]:after {
+ content: "";
+ position: absolute;
+ left: 0px;
+ width: 10px;
+ box-shadow: inset 5px 0 5px -5px gray;
+ top: 0;
+ bottom: 0;
+ }
}
-.widget-datagrid-grid-head,
+.widget-datagrid-grid-head {
+ display: grid;
+
+ // this head is not part of the grid, so it has dedicated column template --widgets-grid-template-columns-head
+ // but it might not be available at the initial render, so we use template from the grid --widgets-grid-template-columns
+ // using template from the grid might to misalignment from the grid itself,
+ // but in practice:
+ // - grid has no data at that moment, so misalignment is not visible.
+ // - as soon as the grid itself gets rendered --widgets-grid-template-columns-head gets calculated
+ // and everything looks like it should.
+ grid-template-columns: var(--widgets-grid-template-columns-head, var(--widgets-grid-template-columns));
+}
.widget-datagrid-grid-body {
// this element has to position their children (columns or headers)
// as grid and have those aligned with the parent grid
display: grid;
// this property makes sure we align our own grid columns
// to the columns defined in the global grid
- grid-template-columns: subgrid;
+ grid-template-columns: var(--widgets-grid-template-columns);
+}
- // ensure that we cover all columns of original top level grid
- // so our own columns get aligned with the parent
+.grid-mock-header {
+ grid-template-columns: subgrid;
grid-column: 1 / -1;
+ display: grid;
}
:where(#{$root}-paging-bottom, #{$root}-padding-top) {
diff --git a/packages/pluggableWidgets/datagrid-web/CHANGELOG.md b/packages/pluggableWidgets/datagrid-web/CHANGELOG.md
index 1f7339aa51..721bfa9705 100644
--- a/packages/pluggableWidgets/datagrid-web/CHANGELOG.md
+++ b/packages/pluggableWidgets/datagrid-web/CHANGELOG.md
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
## [Unreleased]
+### Changed
+
+- We improved virtual scrolling behavior when horizontal scrolling is present due to grid size.
+
### Added
- We added a new property for export to excel. The new property allows to set the cell export type and also the format for type number and date.
diff --git a/packages/pluggableWidgets/datagrid-web/src/components/Grid.tsx b/packages/pluggableWidgets/datagrid-web/src/components/Grid.tsx
index 015fb3f539..9c3135bc33 100644
--- a/packages/pluggableWidgets/datagrid-web/src/components/Grid.tsx
+++ b/packages/pluggableWidgets/datagrid-web/src/components/Grid.tsx
@@ -1,16 +1,22 @@
+import classNames from "classnames";
import { observer } from "mobx-react-lite";
import { PropsWithChildren, ReactElement } from "react";
-import { useDatagridConfig, useGridStyle } from "../model/hooks/injection-hooks";
+import { useDatagridConfig, useGridSizeStore, useGridStyle } from "../model/hooks/injection-hooks";
export const Grid = observer(function Grid(props: PropsWithChildren): ReactElement {
const config = useDatagridConfig();
+ const gridSizeStore = useGridSizeStore();
+
const style = useGridStyle().get();
return (
{props.children}
diff --git a/packages/pluggableWidgets/datagrid-web/src/components/GridBody.tsx b/packages/pluggableWidgets/datagrid-web/src/components/GridBody.tsx
index a8ededee80..c27b6337c6 100644
--- a/packages/pluggableWidgets/datagrid-web/src/components/GridBody.tsx
+++ b/packages/pluggableWidgets/datagrid-web/src/components/GridBody.tsx
@@ -1,8 +1,8 @@
-import classNames from "classnames";
import { observer } from "mobx-react-lite";
import { Fragment, PropsWithChildren, ReactElement, ReactNode } from "react";
import {
useDatagridConfig,
+ useGridSizeStore,
useItemCount,
useLoaderViewModel,
usePaginationService,
@@ -14,14 +14,14 @@ import { SpinnerLoader } from "./loader/SpinnerLoader";
export const GridBody = observer(function GridBody(props: PropsWithChildren): ReactElement {
const { children } = props;
- const { bodySize, containerRef, isInfinite, handleScroll } = useBodyScroll();
+ const gridSizeStore = useGridSizeStore();
+ const { handleScroll } = useBodyScroll();
return (
0 ? { maxHeight: `${bodySize}px` } : {}}
+ className={"widget-datagrid-grid-body table-content"}
role="rowgroup"
- ref={containerRef}
+ ref={gridSizeStore.gridBodyRef}
onScroll={handleScroll}
>
{children}
diff --git a/packages/pluggableWidgets/datagrid-web/src/components/GridHeader.tsx b/packages/pluggableWidgets/datagrid-web/src/components/GridHeader.tsx
index 39b86c6d3a..a378cd42b7 100644
--- a/packages/pluggableWidgets/datagrid-web/src/components/GridHeader.tsx
+++ b/packages/pluggableWidgets/datagrid-web/src/components/GridHeader.tsx
@@ -1,5 +1,5 @@
import { ReactElement, useState } from "react";
-import { useColumnsStore, useDatagridConfig } from "../model/hooks/injection-hooks";
+import { useColumnsStore, useDatagridConfig, useGridSizeStore } from "../model/hooks/injection-hooks";
import { ColumnId } from "../typings/GridColumn";
import { CheckboxColumnHeader } from "./CheckboxColumnHeader";
import { ColumnProvider } from "./ColumnProvider";
@@ -11,6 +11,7 @@ import { HeaderSkeletonLoader } from "./loader/HeaderSkeletonLoader";
export function GridHeader(): ReactElement {
const { columnsHidable, id: gridId } = useDatagridConfig();
const columnsStore = useColumnsStore();
+ const gridSizeStore = useGridSizeStore();
const columns = columnsStore.visibleColumns;
const [dragOver, setDragOver] = useState<[ColumnId, "before" | "after"] | undefined>(undefined);
const [isDragging, setIsDragging] = useState<[ColumnId | undefined, ColumnId, ColumnId | undefined] | undefined>();
@@ -20,7 +21,7 @@ export function GridHeader(): ReactElement {
}
return (
-
+
{columns.map(column => (
diff --git a/packages/pluggableWidgets/datagrid-web/src/components/Header.tsx b/packages/pluggableWidgets/datagrid-web/src/components/Header.tsx
index ed334d2ad9..1c396b3b75 100644
--- a/packages/pluggableWidgets/datagrid-web/src/components/Header.tsx
+++ b/packages/pluggableWidgets/datagrid-web/src/components/Header.tsx
@@ -60,7 +60,6 @@ export function Header(props: HeaderProps): ReactElement {
role="columnheader"
style={!canSort ? { cursor: "unset" } : undefined}
title={caption}
- ref={ref => column.setHeaderElementRef(ref)}
data-column-id={column.columnId}
onDrop={draggableProps.onDrop}
onDragEnter={draggableProps.onDragEnter}
diff --git a/packages/pluggableWidgets/datagrid-web/src/components/MockHeader.tsx b/packages/pluggableWidgets/datagrid-web/src/components/MockHeader.tsx
new file mode 100644
index 0000000000..9035326a1a
--- /dev/null
+++ b/packages/pluggableWidgets/datagrid-web/src/components/MockHeader.tsx
@@ -0,0 +1,56 @@
+import { ReactNode, useCallback, useEffect, useRef } from "react";
+import { useColumnsStore, useDatagridConfig, useGridSizeStore } from "../model/hooks/injection-hooks";
+
+function getColumnSizes(container: HTMLDivElement | null): Map
{
+ const sizes = new Map();
+ if (container) {
+ container.querySelectorAll("[data-column-id]").forEach(c => {
+ const columnId = c.dataset.columnId;
+ if (!columnId) {
+ console.debug("getColumnSizes: can't find id on:", c);
+ return;
+ }
+ sizes.set(columnId, c.offsetWidth);
+ });
+ }
+
+ return sizes;
+}
+
+export function MockHeader(): ReactNode {
+ const columnsStore = useColumnsStore();
+ const config = useDatagridConfig();
+ const gridSizeStore = useGridSizeStore();
+ const headerRef = useRef(null);
+ const resizeCallback = useCallback(() => {
+ gridSizeStore.updateColumnSizes(getColumnSizes(headerRef.current).values().toArray());
+ }, [headerRef, gridSizeStore]);
+
+ useEffect(() => {
+ const observer = new ResizeObserver(resizeCallback);
+
+ if (headerRef.current) {
+ observer.observe(headerRef.current);
+ }
+ return () => {
+ observer.disconnect();
+ };
+ }, [resizeCallback, headerRef]);
+
+ return (
+
+ {config.checkboxColumnEnabled &&
}
+ {columnsStore.visibleColumns.map(c => (
+
c.setHeaderElementRef(ref)}
+ >
+ ))}
+ {config.selectorColumnEnabled &&
}
+
+ );
+}
diff --git a/packages/pluggableWidgets/datagrid-web/src/components/Widget.tsx b/packages/pluggableWidgets/datagrid-web/src/components/Widget.tsx
index bce68e5755..9d11e7b5d7 100644
--- a/packages/pluggableWidgets/datagrid-web/src/components/Widget.tsx
+++ b/packages/pluggableWidgets/datagrid-web/src/components/Widget.tsx
@@ -13,6 +13,7 @@ import { WidgetFooter } from "./WidgetFooter";
import { WidgetHeader } from "./WidgetHeader";
import { WidgetRoot } from "./WidgetRoot";
import { WidgetTopBar } from "./WidgetTopBar";
+import { MockHeader } from "./MockHeader";
export function Widget(props: { onExportCancel?: () => void }): ReactElement {
return (
@@ -25,6 +26,7 @@ export function Widget(props: { onExportCancel?: () => void }): ReactElement {
+
diff --git a/packages/pluggableWidgets/datagrid-web/src/components/__tests__/__snapshots__/Grid.spec.tsx.snap b/packages/pluggableWidgets/datagrid-web/src/components/__tests__/__snapshots__/Grid.spec.tsx.snap
index a6e74b1f27..3751635dc2 100644
--- a/packages/pluggableWidgets/datagrid-web/src/components/__tests__/__snapshots__/Grid.spec.tsx.snap
+++ b/packages/pluggableWidgets/datagrid-web/src/components/__tests__/__snapshots__/Grid.spec.tsx.snap
@@ -5,7 +5,7 @@ exports[`Grid renders without crashing 1`] = `
Test
@@ -17,7 +17,7 @@ exports[`Grid renders without selector column 1`] = `
Test
diff --git a/packages/pluggableWidgets/datagrid-web/src/helpers/state/column/ColumnStore.tsx b/packages/pluggableWidgets/datagrid-web/src/helpers/state/column/ColumnStore.tsx
index 7a48f5b404..9ee3cf83ee 100644
--- a/packages/pluggableWidgets/datagrid-web/src/helpers/state/column/ColumnStore.tsx
+++ b/packages/pluggableWidgets/datagrid-web/src/helpers/state/column/ColumnStore.tsx
@@ -28,6 +28,8 @@ export class ColumnStore implements GridColumn {
private baseInfo: BaseColumnInfo;
private parentStore: IColumnParentStore;
+ // this holds size of the column that it had prior to resizing started
+ // this is needed to prevent personalization being saved while resizing
private frozenSize: number | undefined;
// dynamic props from PW API
diff --git a/packages/pluggableWidgets/datagrid-web/src/model/containers/Datagrid.container.ts b/packages/pluggableWidgets/datagrid-web/src/model/containers/Datagrid.container.ts
index fd99730e77..172275ab3e 100644
--- a/packages/pluggableWidgets/datagrid-web/src/model/containers/Datagrid.container.ts
+++ b/packages/pluggableWidgets/datagrid-web/src/model/containers/Datagrid.container.ts
@@ -32,6 +32,7 @@ import { DerivedLoaderController } from "../services/DerivedLoaderController";
import { PaginationController } from "../services/PaginationController";
import { SelectionGate } from "../services/SelectionGate.service";
import { CORE_TOKENS as CORE, DG_TOKENS as DG, SA_TOKENS } from "../tokens";
+import { GridSizeStore } from "../stores/GridSize.store";
// base
injected(ColumnGroupStore, CORE.setupService, CORE.mainGate, CORE.config, DG.filterHost);
@@ -40,6 +41,7 @@ injected(DatasourceService, CORE.setupService, DG.queryGate, DG.refreshInterval.
injected(PaginationController, CORE.setupService, DG.paginationConfig, DG.query);
injected(GridBasicData, CORE.mainGate);
injected(WidgetRootViewModel, CORE.mainGate, CORE.config, DG.exportProgressService, SA_TOKENS.selectionDialogVM);
+injected(GridSizeStore, DG.paginationService);
// loader
injected(DerivedLoaderController, DG.query, DG.exportProgressService, CORE.columnsStore, DG.loaderConfig);
@@ -58,7 +60,7 @@ injected(GridPersonalizationStore, CORE.setupService, CORE.mainGate, CORE.column
// selection
injected(SelectionGate, CORE.mainGate);
injected(createSelectionHelper, CORE.setupService, DG.selectionGate, CORE.config.optional);
-injected(gridStyleAtom, CORE.columnsStore, CORE.config);
+injected(gridStyleAtom, CORE.columnsStore, CORE.config, DG.gridSizeStore);
injected(rowClassProvider, CORE.mainGate);
// row-interaction
@@ -98,6 +100,8 @@ export class DatagridContainer extends Container {
this.bind(DG.query).toInstance(DatasourceService).inSingletonScope();
// Pagination service
this.bind(DG.paginationService).toInstance(PaginationController).inSingletonScope();
+ // Grid sizing and scrolling store
+ this.bind(DG.gridSizeStore).toInstance(GridSizeStore).inSingletonScope();
// Datasource params service
this.bind(DG.paramsService).toInstance(DatasourceParamsController).inSingletonScope();
// FilterAPI
@@ -229,5 +233,7 @@ export class DatagridContainer extends Container {
// Hydrate filters from props
this.get(DG.combinedFilter).hydrate(props.datasource.filter);
+
+ this.get(DG.gridSizeStore);
}
}
diff --git a/packages/pluggableWidgets/datagrid-web/src/model/hooks/injection-hooks.ts b/packages/pluggableWidgets/datagrid-web/src/model/hooks/injection-hooks.ts
index 337a03637f..77bf97a510 100644
--- a/packages/pluggableWidgets/datagrid-web/src/model/hooks/injection-hooks.ts
+++ b/packages/pluggableWidgets/datagrid-web/src/model/hooks/injection-hooks.ts
@@ -9,6 +9,7 @@ export const [useExportProgressService] = createInjectionHooks(DG.exportProgress
export const [useLoaderViewModel] = createInjectionHooks(DG.loaderVM);
export const [useMainGate] = createInjectionHooks(CORE.mainGate);
export const [usePaginationService] = createInjectionHooks(DG.paginationService);
+export const [useGridSizeStore] = createInjectionHooks(DG.gridSizeStore);
export const [useSelectionHelper] = createInjectionHooks(DG.selectionHelper);
export const [useGridStyle] = createInjectionHooks(DG.gridColumnsStyle);
export const [useQueryService] = createInjectionHooks(DG.query);
diff --git a/packages/pluggableWidgets/datagrid-web/src/model/hooks/useBodyScroll.ts b/packages/pluggableWidgets/datagrid-web/src/model/hooks/useBodyScroll.ts
index e5fd2eed3a..eb98f0e965 100644
--- a/packages/pluggableWidgets/datagrid-web/src/model/hooks/useBodyScroll.ts
+++ b/packages/pluggableWidgets/datagrid-web/src/model/hooks/useBodyScroll.ts
@@ -1,27 +1,12 @@
-import { useInfiniteControl } from "@mendix/widget-plugin-grid/components/InfiniteBody";
-import { RefObject, UIEventHandler, useCallback } from "react";
-import { usePaginationService } from "./injection-hooks";
+import { UIEventHandler } from "react";
+import { useInfiniteControl } from "./useInfiniteControl";
export function useBodyScroll(): {
handleScroll: UIEventHandler | undefined;
- bodySize: number;
- containerRef: RefObject;
- isInfinite: boolean;
} {
- const paging = usePaginationService();
- const setPage = useCallback((cb: (n: number) => number) => paging.setPage(cb), [paging]);
-
- const isInfinite = paging.pagination === "virtualScrolling";
- const [trackScrolling, bodySize, containerRef] = useInfiniteControl({
- hasMoreItems: paging.hasMoreItems,
- isInfinite,
- setPage
- });
+ const [trackBodyScrolling] = useInfiniteControl();
return {
- handleScroll: isInfinite ? trackScrolling : undefined,
- bodySize,
- containerRef,
- isInfinite
+ handleScroll: trackBodyScrolling
};
}
diff --git a/packages/pluggableWidgets/datagrid-web/src/model/hooks/useInfiniteControl.tsx b/packages/pluggableWidgets/datagrid-web/src/model/hooks/useInfiniteControl.tsx
new file mode 100644
index 0000000000..c9f7fa9b8d
--- /dev/null
+++ b/packages/pluggableWidgets/datagrid-web/src/model/hooks/useInfiniteControl.tsx
@@ -0,0 +1,66 @@
+import { RefObject, UIEvent, useCallback, useEffect, useLayoutEffect } from "react";
+import { useOnScreen } from "@mendix/widget-plugin-hooks/useOnScreen";
+import { useGridSizeStore } from "@mendix/datagrid-web/src/model/hooks/injection-hooks";
+import { VIRTUAL_SCROLLING_OFFSET } from "../stores/GridSize.store";
+
+export function useInfiniteControl(): [trackBodyScrolling: ((e: any) => void) | undefined] {
+ const gridSizeStore = useGridSizeStore();
+
+ const isVisible = useOnScreen(gridSizeStore.gridBodyRef as RefObject);
+
+ const trackBodyScrolling = useCallback(
+ (e: UIEvent) => {
+ const target = e.target as HTMLElement;
+ const head = gridSizeStore.gridHeaderRef.current;
+ if (head) {
+ // synchronize header position to the body as they are decoupled
+ // we don't use state to optimize speed as we
+ // don't want a re-render.
+ head.scrollTo({ left: target.scrollLeft });
+
+ // this is cosmetic, needed to provide nice shadows when body is scrolled
+ head.dataset.scrolledY = target.scrollTop > 0 ? "true" : "false";
+ head.dataset.scrolledX = target.scrollLeft > 0 ? "true" : "false";
+ }
+
+ // we need to determine scrollbar width to calculate header size correctly in css
+ gridSizeStore.setScrollBarSize(target.offsetWidth - target.clientWidth);
+
+ /**
+ * In Windows OS the result of first expression returns a non integer and result in never loading more, require floor to solve.
+ * note: Math floor sometimes result in incorrect integer value,
+ * causing mismatch by 1 pixel point, thus, add magic number 2 as buffer.
+ */
+ const bottom =
+ Math.floor(target.scrollHeight - VIRTUAL_SCROLLING_OFFSET - target.scrollTop) <=
+ Math.floor(target.clientHeight) + 2;
+ if (bottom) {
+ gridSizeStore.bumpPage();
+ }
+ },
+ [gridSizeStore]
+ );
+
+ useEffect(() => {
+ setTimeout(() => isVisible && gridSizeStore.lockGridBodyHeight(), 100);
+ }, [isVisible, gridSizeStore]);
+
+ useLayoutEffect(() => {
+ const observeTarget = gridSizeStore.gridContainerRef.current;
+ if (!gridSizeStore.hasVirtualScrolling || !observeTarget) return;
+
+ const resizeObserver = new ResizeObserver(entries => {
+ for (const entry of entries) {
+ gridSizeStore.setGridWidth(entry.target.clientWidth ? entry.target.clientWidth : undefined);
+ }
+ });
+
+ resizeObserver.observe(observeTarget);
+
+ return () => {
+ resizeObserver.unobserve(observeTarget);
+ };
+ }, [gridSizeStore]);
+
+ return [gridSizeStore.hasVirtualScrolling ? trackBodyScrolling : undefined];
+}
diff --git a/packages/pluggableWidgets/datagrid-web/src/model/models/grid.model.ts b/packages/pluggableWidgets/datagrid-web/src/model/models/grid.model.ts
index 559b2bf366..e8c99f06a3 100644
--- a/packages/pluggableWidgets/datagrid-web/src/model/models/grid.model.ts
+++ b/packages/pluggableWidgets/datagrid-web/src/model/models/grid.model.ts
@@ -3,23 +3,44 @@ import { computed } from "mobx";
import { CSSProperties } from "react";
import { ColumnGroupStore } from "../../helpers/state/ColumnGroupStore";
import { DatagridConfig } from "../configs/Datagrid.config";
+import { GridColumn } from "../../typings/GridColumn";
+import { GridSizeStore } from "../stores/GridSize.store";
-export function gridStyleAtom(columns: ColumnGroupStore, config: DatagridConfig): ComputedAtom {
- return computed(() => {
- return gridStyle(columns.visibleColumns, {
- checkboxColumn: config.checkboxColumnEnabled,
- selectorColumn: config.selectorColumnEnabled
- });
- });
+export function gridStyleAtom(
+ columns: ColumnGroupStore,
+ config: DatagridConfig,
+ gridSizeStore: GridSizeStore
+): ComputedAtom {
+ return computed(
+ () =>
+ ({
+ "--widgets-grid-template-columns": templateColumns(columns.visibleColumns, {
+ checkboxColumn: config.checkboxColumnEnabled,
+ selectorColumn: config.selectorColumnEnabled
+ }),
+ "--widgets-grid-template-columns-head": gridSizeStore.templateColumnsHead,
+ "--widgets-grid-body-height": asPx(gridSizeStore.gridBodyHeight),
+ "--widgets-grid-width": asPx(gridSizeStore.gridWidth),
+ "--widgets-grid-scrollbar-size": asPx(gridSizeStore.scrollBarSize)
+ }) as CSSProperties
+ );
}
-function gridStyle(
- columns: Array<{ getCssWidth(): string }>,
+function asPx(v: number | undefined): string | undefined {
+ if (v === undefined) {
+ return undefined;
+ }
+
+ return `${v}px`;
+}
+
+function templateColumns(
+ columns: GridColumn[],
optional: {
- checkboxColumn?: boolean;
- selectorColumn?: boolean;
+ checkboxColumn: boolean;
+ selectorColumn: boolean;
}
-): CSSProperties {
+): string {
const columnSizes = columns.map(c => c.getCssWidth());
const sizes: string[] = [];
@@ -34,7 +55,5 @@ function gridStyle(
sizes.push("54px");
}
- return {
- gridTemplateColumns: sizes.join(" ")
- };
+ return sizes.join(" ");
}
diff --git a/packages/pluggableWidgets/datagrid-web/src/model/stores/GridSize.store.ts b/packages/pluggableWidgets/datagrid-web/src/model/stores/GridSize.store.ts
new file mode 100644
index 0000000000..fee206c8df
--- /dev/null
+++ b/packages/pluggableWidgets/datagrid-web/src/model/stores/GridSize.store.ts
@@ -0,0 +1,80 @@
+import { action, computed, makeAutoObservable, observable } from "mobx";
+import { createRef } from "react";
+import { PaginationController } from "../services/PaginationController";
+
+export const VIRTUAL_SCROLLING_OFFSET = 30;
+
+export class GridSizeStore {
+ gridContainerRef = createRef();
+ gridBodyRef = createRef();
+ gridHeaderRef = createRef();
+
+ scrollBarSize?: number;
+ gridWidth?: number;
+ gridBodyHeight?: number;
+ columnSizes?: number[];
+
+ paging: PaginationController;
+
+ constructor(paging: PaginationController) {
+ this.paging = paging;
+
+ makeAutoObservable(this, {
+ gridContainerRef: false,
+ gridBodyRef: false,
+ gridHeaderRef: false,
+
+ scrollBarSize: observable,
+ setScrollBarSize: action,
+
+ gridWidth: observable,
+ setGridWidth: action,
+
+ gridBodyHeight: observable,
+ lockGridBodyHeight: action,
+
+ columnSizes: observable,
+ updateColumnSizes: action,
+
+ templateColumnsHead: computed
+ });
+ }
+
+ get hasVirtualScrolling(): boolean {
+ return this.paging.pagination === "virtualScrolling";
+ }
+
+ get templateColumnsHead(): string | undefined {
+ return this.columnSizes?.map(s => `${s}px`).join(" ");
+ }
+
+ bumpPage(): void {
+ if (this.paging.hasMoreItems) {
+ return this.paging.setPage(page => page + 1);
+ }
+ }
+
+ setScrollBarSize(size: number): void {
+ this.scrollBarSize = size;
+ }
+
+ setGridWidth(size: number | undefined): void {
+ this.gridWidth = size;
+ }
+
+ updateColumnSizes(sizes: number[]): void {
+ this.columnSizes = sizes;
+ }
+
+ lockGridBodyHeight(): void {
+ if (!this.hasVirtualScrolling || !this.paging.hasMoreItems) {
+ return;
+ }
+ const gridBody = this.gridBodyRef.current;
+ if (!gridBody || this.gridBodyHeight !== undefined) {
+ return;
+ }
+
+ this.gridBodyHeight = gridBody.clientHeight - VIRTUAL_SCROLLING_OFFSET;
+ }
+}
diff --git a/packages/pluggableWidgets/datagrid-web/src/model/tokens.ts b/packages/pluggableWidgets/datagrid-web/src/model/tokens.ts
index 26b3e38201..b9ffd203c9 100644
--- a/packages/pluggableWidgets/datagrid-web/src/model/tokens.ts
+++ b/packages/pluggableWidgets/datagrid-web/src/model/tokens.ts
@@ -42,6 +42,7 @@ import { DerivedLoaderController, DerivedLoaderControllerConfig } from "./servic
import { PaginationConfig, PaginationController } from "./services/PaginationController";
import { TextsService } from "./services/Texts.service";
import { PageSizeStore } from "./stores/PageSize.store";
+import { GridSizeStore } from "./stores/GridSize.store";
/** Tokens to resolve dependencies from the container. */
@@ -104,6 +105,8 @@ export const DG_TOKENS = {
paginationConfig: token("PaginationConfig"),
paginationService: token("PaginationService"),
+ gridSizeStore: token("@store:GridSizeStore"),
+
parentChannelName: token("parentChannelName"),
refreshInterval: token("refreshInterval"),