Skip to content

Commit eae5be7

Browse files
committed
WIP
1 parent 6041549 commit eae5be7

File tree

19 files changed

+212
-220
lines changed

19 files changed

+212
-220
lines changed

packages/pluggableWidgets/datagrid-web/src/Datagrid.tsx

Lines changed: 2 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import { useClickActionHelper } from "@mendix/widget-plugin-grid/helpers/ClickActionHelper";
22
import { useFocusTargetController } from "@mendix/widget-plugin-grid/keyboard-navigation/useFocusTargetController";
33
import { useConst } from "@mendix/widget-plugin-mobx-kit/react/useConst";
4-
import { generateUUID } from "@mendix/widget-plugin-platform/framework/generate-uuid";
54
import { ContainerProvider } from "brandi-react";
65
import { observer } from "mobx-react-lite";
7-
import { ReactElement, useCallback, useMemo } from "react";
6+
import { ReactElement } from "react";
87
import { DatagridContainerProps } from "../typings/DatagridProps";
9-
import { Cell } from "./components/Cell";
108
import { Widget } from "./components/Widget";
119
import { useDataExport } from "./features/data-export/useDataExport";
1210
import { useCellEventsController } from "./features/row-interaction/CellEventsController";
@@ -17,7 +15,6 @@ import { useDataGridJSActions } from "./helpers/useDataGridJSActions";
1715
import {
1816
useColumnsStore,
1917
useExportProgressService,
20-
useLoaderViewModel,
2118
useMainGate,
2219
useSelectionHelper
2320
} from "./model/hooks/injection-hooks";
@@ -27,7 +24,6 @@ const DatagridRoot = observer((props: DatagridContainerProps): ReactElement => {
2724
const gate = useMainGate();
2825
const columnsStore = useColumnsStore();
2926
const exportProgress = useExportProgressService();
30-
const loaderVM = useLoaderViewModel();
3127
const items = gate.props.datasource.items ?? [];
3228

3329
const [abortExport] = useDataExport(props, columnsStore, exportProgress);
@@ -67,43 +63,7 @@ const DatagridRoot = observer((props: DatagridContainerProps): ReactElement => {
6763
focusController
6864
})}
6965
>
70-
<Widget
71-
className={props.class}
72-
CellComponent={Cell}
73-
columnsDraggable={props.columnsDraggable}
74-
columnsFilterable={props.columnsFilterable}
75-
columnsHidable={props.columnsHidable}
76-
columnsResizable={props.columnsResizable}
77-
columnsSortable={props.columnsSortable}
78-
data={items}
79-
filterRenderer={useCallback(
80-
(renderWrapper, columnIndex) => {
81-
const columnFilter = columnsStore.columnFilters[columnIndex];
82-
return renderWrapper(columnFilter.renderFilterWidgets());
83-
},
84-
[columnsStore.columnFilters]
85-
)}
86-
headerTitle={props.filterSectionTitle?.value}
87-
headerContent={props.filtersPlaceholder}
88-
id={useMemo(() => `DataGrid${generateUUID()}`, [])}
89-
numberOfItems={props.datasource.totalCount}
90-
onExportCancel={abortExport}
91-
paginationType={props.pagination}
92-
loadMoreButtonCaption={props.loadMoreButtonCaption?.value}
93-
rowClass={useCallback((value: any) => props.rowClass?.get(value)?.value ?? "", [props.rowClass])}
94-
styles={props.style}
95-
exporting={exportProgress.inProgress}
96-
processedRows={exportProgress.loaded}
97-
selectActionHelper={selectActionHelper}
98-
cellEventsController={cellEventsController}
99-
checkboxEventsController={checkboxEventsController}
100-
focusController={focusController}
101-
isFirstLoad={loaderVM.isFirstLoad}
102-
isFetchingNextBatch={loaderVM.isFetchingNextBatch}
103-
showRefreshIndicator={loaderVM.showRefreshIndicator}
104-
loadingType={props.loadingType}
105-
columnsLoading={!columnsStore.loaded}
106-
/>
66+
<Widget onExportCancel={abortExport} />
10767
</LegacyContext.Provider>
10868
);
10969
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { RefreshIndicator } from "@mendix/widget-plugin-component-kit/RefreshIndicator";
2+
import { ReactNode } from "react";
3+
import { useLoaderViewModel } from "../model/hooks/injection-hooks";
4+
5+
export const RefreshStatus = function RefreshStatus(): ReactNode {
6+
const loaderVM = useLoaderViewModel();
7+
8+
if (!loaderVM.showRefreshIndicator) return null;
9+
10+
return loaderVM.isRefreshing ? <RefreshIndicator /> : null;
11+
};

packages/pluggableWidgets/datagrid-web/src/components/RowsRenderer.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
import { KeyNavProvider } from "@mendix/widget-plugin-grid/keyboard-navigation/context";
2-
import { ObjectItem } from "mendix";
32
import { observer } from "mobx-react-lite";
43
import { ReactElement } from "react";
54
import { useLegacyContext } from "../helpers/root-context";
6-
import { useColumnsStore, useDatagridConfig, useRowClass } from "../model/hooks/injection-hooks";
5+
import { useColumnsStore, useDatagridConfig, useRowClass, useRows } from "../model/hooks/injection-hooks";
76
import { Row } from "./Row";
87

9-
interface RowsRendererProps {
10-
rows: ObjectItem[];
11-
}
12-
13-
export const RowsRenderer = observer(function RowsRenderer({ rows }: RowsRendererProps): ReactElement {
8+
export const RowsRenderer = observer(function RowsRenderer(): ReactElement {
9+
const rows = useRows().get();
1410
const config = useDatagridConfig();
1511
const { visibleColumns } = useColumnsStore();
1612
const rowClass = useRowClass();
Lines changed: 13 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,110 +1,38 @@
1-
import { RefreshIndicator } from "@mendix/widget-plugin-component-kit/RefreshIndicator";
2-
import { FocusTargetController } from "@mendix/widget-plugin-grid/keyboard-navigation/FocusTargetController";
3-
import { ListActionValue, ObjectItem } from "mendix";
4-
import { observer } from "mobx-react-lite";
5-
import { CSSProperties, Fragment, ReactElement, ReactNode } from "react";
6-
import { LoadingTypeEnum, PaginationEnum } from "../../typings/DatagridProps";
7-
1+
import { ReactNode } from "react";
2+
import { ExportProgressDialog } from "../features/data-export/ExportProgressDialog";
83
import { EmptyPlaceholder } from "../features/empty-message/EmptyPlaceholder";
94
import { SelectAllBar } from "../features/select-all/SelectAllBar";
105
import { SelectionProgressDialog } from "../features/select-all/SelectionProgressDialog";
11-
import { SelectActionHelper } from "../helpers/SelectActionHelper";
12-
import { useBasicData } from "../model/hooks/injection-hooks";
13-
import { EventsController } from "../typings/CellComponent";
14-
import { ExportWidget } from "./ExportWidget";
156
import { Grid } from "./Grid";
167
import { GridBody } from "./GridBody";
178
import { GridHeader } from "./GridHeader";
9+
import { RefreshStatus } from "./RefreshStatus";
1810
import { RowsRenderer } from "./RowsRenderer";
1911
import { WidgetContent } from "./WidgetContent";
2012
import { WidgetFooter } from "./WidgetFooter";
2113
import { WidgetHeader } from "./WidgetHeader";
2214
import { WidgetRoot } from "./WidgetRoot";
2315
import { WidgetTopBar } from "./WidgetTopBar";
2416

25-
export interface WidgetProps {
26-
className: string;
27-
columnsDraggable: boolean;
28-
columnsFilterable: boolean;
29-
columnsHidable: boolean;
30-
columnsResizable: boolean;
31-
columnsSortable: boolean;
32-
data: ObjectItem[];
33-
exporting: boolean;
34-
headerContent?: ReactNode;
35-
headerTitle?: string;
36-
id: string;
37-
numberOfItems?: number;
38-
onExportCancel?: () => void;
39-
paginationType: PaginationEnum;
40-
loadMoreButtonCaption?: string;
41-
42-
processedRows: number;
43-
44-
styles?: CSSProperties;
45-
rowAction?: ListActionValue;
46-
isFirstLoad: boolean;
47-
isFetchingNextBatch: boolean;
48-
loadingType: LoadingTypeEnum;
49-
columnsLoading: boolean;
50-
showRefreshIndicator: boolean;
51-
52-
// Helpers
53-
cellEventsController: EventsController;
54-
checkboxEventsController: EventsController;
55-
selectActionHelper: SelectActionHelper;
56-
focusController: FocusTargetController;
57-
}
58-
59-
export const Widget = observer(function Widget(props: WidgetProps) {
60-
const { className, exporting, numberOfItems, onExportCancel, selectActionHelper } = props;
61-
const basicData = useBasicData();
62-
const selectionEnabled = selectActionHelper.selectionType !== "None";
63-
64-
return (
65-
<WidgetRoot
66-
className={className}
67-
selectionMethod={selectActionHelper.selectionMethod}
68-
selection={selectionEnabled}
69-
style={props.styles}
70-
exporting={exporting}
71-
>
72-
<Main {...props} data={exporting ? [] : props.data} />
73-
<SelectionProgressDialog />
74-
{exporting && (
75-
<ExportWidget
76-
alertLabel={basicData.exportDialogLabel ?? "Export progress"}
77-
cancelLabel={basicData.cancelExportLabel ?? "Cancel data export"}
78-
failed={false}
79-
onCancel={onExportCancel}
80-
open={exporting}
81-
progress={props.processedRows}
82-
total={numberOfItems}
83-
/>
84-
)}
85-
</WidgetRoot>
86-
);
87-
});
88-
89-
const Main = observer((props: WidgetProps): ReactElement => {
90-
const { data: rows, headerContent, headerTitle, loadMoreButtonCaption, showRefreshIndicator } = props;
91-
17+
export function Widget(props: { onExportCancel?: () => void }): ReactNode {
9218
return (
93-
<Fragment>
19+
<WidgetRoot>
9420
<WidgetTopBar />
95-
<WidgetHeader headerTitle={headerTitle} headerContent={headerContent} />
21+
<WidgetHeader />
9622
<WidgetContent>
9723
<Grid>
9824
<GridHeader />
9925
<SelectAllBar />
100-
{showRefreshIndicator ? <RefreshIndicator /> : null}
26+
<RefreshStatus />
10127
<GridBody>
102-
<RowsRenderer rows={rows} />
28+
<RowsRenderer />
10329
<EmptyPlaceholder />
10430
</GridBody>
10531
</Grid>
10632
</WidgetContent>
107-
<WidgetFooter loadMoreButtonCaption={loadMoreButtonCaption} />
108-
</Fragment>
33+
<WidgetFooter />
34+
<SelectionProgressDialog />
35+
<ExportProgressDialog onExportCancel={props.onExportCancel} />
36+
</WidgetRoot>
10937
);
110-
});
38+
}

packages/pluggableWidgets/datagrid-web/src/components/WidgetFooter.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,13 @@ import { observer } from "mobx-react-lite";
33
import { ReactElement } from "react";
44
import { SelectionCounter } from "../features/selection-counter/SelectionCounter";
55
import { useSelectionCounterViewModel } from "../features/selection-counter/injection-hooks";
6-
import { useDatagridConfig, usePaginationService } from "../model/hooks/injection-hooks";
6+
import { useDatagridConfig, usePaginationService, useTexts } from "../model/hooks/injection-hooks";
77
import { Pagination } from "./Pagination";
88

9-
type WidgetFooterProps = {
10-
loadMoreButtonCaption?: string;
11-
};
12-
13-
export const WidgetFooter = observer(function WidgetFooter(props: WidgetFooterProps): ReactElement | null {
9+
export const WidgetFooter = observer(function WidgetFooter(): ReactElement | null {
1410
const config = useDatagridConfig();
1511
const paging = usePaginationService();
16-
const { loadMoreButtonCaption } = props;
12+
const { loadMoreButtonCaption } = useTexts();
1713
const selectionCounterVM = useSelectionCounterViewModel();
1814

1915
return (
Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
import { getGlobalFilterContextObject } from "@mendix/widget-plugin-filtering/context";
22
import { getGlobalSelectionContext, useCreateSelectionContextValue } from "@mendix/widget-plugin-grid/selection";
33
import { PropsWithChildren, ReactElement, ReactNode } from "react";
4-
import { useDatagridFilterAPI, useSelectionHelper } from "../model/hooks/injection-hooks";
5-
6-
type WidgetHeaderProps = {
7-
headerTitle?: string;
8-
headerContent?: ReactNode;
9-
};
4+
import { useDatagridFilterAPI, useMainGate, useSelectionHelper, useTexts } from "../model/hooks/injection-hooks";
105

116
const Selection = getGlobalSelectionContext();
127
const FilterAPI = getGlobalFilterContextObject();
@@ -21,14 +16,15 @@ function HeaderContainer(props: PropsWithChildren): ReactElement {
2116
);
2217
}
2318

24-
export function WidgetHeader(props: WidgetHeaderProps): ReactNode {
25-
const { headerContent, headerTitle } = props;
19+
export const WidgetHeader = function WidgetHeader(): ReactNode {
20+
const { headerAriaLabel } = useTexts();
21+
const { filtersPlaceholder } = useMainGate().props;
2622

27-
if (!headerContent) return null;
23+
if (!filtersPlaceholder) return null;
2824

2925
return (
30-
<div className="widget-datagrid-header header-filters" aria-label={headerTitle || undefined}>
31-
<HeaderContainer>{headerContent}</HeaderContainer>
26+
<div className="widget-datagrid-header header-filters" aria-label={headerAriaLabel}>
27+
<HeaderContainer>{filtersPlaceholder}</HeaderContainer>
3228
</div>
3329
);
34-
}
30+
};

packages/pluggableWidgets/datagrid-web/src/components/WidgetRoot.tsx

Lines changed: 12 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,21 @@
11
import classNames from "classnames";
22
import { observer } from "mobx-react-lite";
3-
import { ComponentPropsWithoutRef, ReactElement, useMemo, useRef } from "react";
4-
import { useSelectionDialogViewModel } from "../features/select-all/injection-hooks";
5-
import { SelectionMethod } from "../helpers/SelectActionHelper";
3+
import { PropsWithChildren, ReactElement } from "react";
4+
import { useDatagridRootVM } from "../model/hooks/injection-hooks";
65

7-
type P = ComponentPropsWithoutRef<"div">;
8-
9-
export interface WidgetRootProps extends P {
10-
className?: string;
11-
selection?: boolean;
12-
selectionMethod: SelectionMethod;
13-
exporting?: boolean;
14-
}
15-
16-
export const WidgetRoot = observer(function WidgetRoot(props: WidgetRootProps): ReactElement {
17-
const ref = useRef<HTMLDivElement>(null);
18-
const { className, selectionMethod, selection, exporting, children, ...rest } = props;
19-
const { isOpen: selectingAllPages } = useSelectionDialogViewModel();
20-
const style = useMemo(() => {
21-
const s = { ...props.style };
22-
if ((exporting || selectingAllPages) && ref.current) {
23-
s.height = ref.current.offsetHeight;
24-
}
25-
return s;
26-
}, [props.style, exporting, selectingAllPages]);
6+
export const WidgetRoot = observer(function WidgetRoot({ children }: PropsWithChildren): ReactElement {
7+
const vm = useDatagridRootVM();
278

289
return (
2910
<div
30-
{...rest}
31-
ref={ref}
32-
style={style}
33-
className={classNames(className, "widget-datagrid", {
34-
"widget-datagrid-exporting": exporting,
35-
"widget-datagrid-selecting-all-pages": selectingAllPages,
36-
"widget-datagrid-selectable-rows": selection,
37-
"widget-datagrid-selection-method-checkbox": selection && selectionMethod === "checkbox",
38-
"widget-datagrid-selection-method-click": selection && selectionMethod === "rowClick"
11+
ref={vm.ref}
12+
style={vm.style}
13+
className={classNames(vm.className, "widget-datagrid", {
14+
"widget-datagrid-exporting": vm.exporting,
15+
"widget-datagrid-selecting-all-pages": vm.selecting,
16+
"widget-datagrid-selectable-rows": vm.selectable,
17+
"widget-datagrid-selection-method-checkbox": vm.selectable && vm.selectionMethod === "checkbox",
18+
"widget-datagrid-selection-method-click": vm.selectable && vm.selectionMethod === "rowClick"
3919
})}
4020
>
4121
{children}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { TaskProgressService } from "@mendix/widget-plugin-grid/main";
2+
import { DerivedPropsGate } from "@mendix/widget-plugin-mobx-kit/main";
3+
import { makeAutoObservable } from "mobx";
4+
import { createRef, CSSProperties } from "react";
5+
import { ItemSelectionMethodEnum } from "../../../typings/DatagridProps";
6+
7+
export class WidgetRootViewModel {
8+
ref = createRef<HTMLDivElement>();
9+
10+
constructor(
11+
private gate: DerivedPropsGate<{
12+
style?: CSSProperties;
13+
class?: string;
14+
}>,
15+
private config: { selectionEnabled: boolean; selectionMethod: ItemSelectionMethodEnum },
16+
private exportTask: TaskProgressService,
17+
private selectAllTask: TaskProgressService
18+
) {
19+
makeAutoObservable(this);
20+
}
21+
22+
get className(): string | undefined {
23+
return this.gate.props.class;
24+
}
25+
26+
get exporting(): boolean {
27+
return this.exportTask.inProgress;
28+
}
29+
30+
get selecting(): boolean {
31+
return this.selectAllTask.inProgress;
32+
}
33+
34+
get selectable(): boolean {
35+
return this.config.selectionEnabled;
36+
}
37+
38+
get selectionMethod(): ItemSelectionMethodEnum {
39+
return this.config.selectionMethod;
40+
}
41+
42+
get style(): CSSProperties {
43+
const style = { ...this.gate.props.style };
44+
45+
if (!this.ref.current) return style;
46+
47+
if (this.exporting || this.selecting) {
48+
return {
49+
...style,
50+
height: this.ref.current.offsetHeight
51+
};
52+
}
53+
54+
return style;
55+
}
56+
}

0 commit comments

Comments
 (0)