@mwater/visualization 5.4.0 → 5.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/ColorComponent.js +2 -1
- package/lib/IdSelection.d.ts +16 -0
- package/lib/IdSelection.js +59 -0
- package/lib/MWaterAddRelatedIndicatorComponent.js +2 -2
- package/lib/MWaterCompleteTableSelectComponent.d.ts +3 -8
- package/lib/MWaterCompleteTableSelectComponent.js +36 -42
- package/lib/MWaterLoaderComponent.d.ts +11 -10
- package/lib/MWaterLoaderComponent.js +1 -1
- package/lib/MWaterResponsesFilterComponent.js +1 -1
- package/lib/MWaterTableSelectComponent.d.ts +0 -1
- package/lib/MWaterTableSelectComponent.js +4 -6
- package/lib/autotranslate.d.ts +20 -0
- package/lib/autotranslate.js +122 -0
- package/lib/axes/AxisBuilder.js +3 -3
- package/lib/axes/AxisColorEditorComponent.js +4 -0
- package/lib/axes/AxisComponent.d.ts +8 -12
- package/lib/axes/AxisComponent.js +32 -80
- package/lib/axes/CategoryMapComponent.js +4 -4
- package/lib/axes/RangesComponent.js +2 -2
- package/lib/dashboards/DashboardComponent.d.ts +6 -0
- package/lib/dashboards/DashboardComponent.js +44 -12
- package/lib/dashboards/DashboardDesign.d.ts +11 -2
- package/lib/dashboards/DashboardUtils.d.ts +5 -0
- package/lib/dashboards/DashboardUtils.js +30 -0
- package/lib/dashboards/DashboardViewComponent.d.ts +2 -0
- package/lib/dashboards/DashboardViewComponent.js +16 -3
- package/lib/dashboards/ServerDashboardDataSource.js +2 -1
- package/lib/dashboards/SettingsModalComponent.d.ts +1 -1
- package/lib/dashboards/SettingsModalComponent.js +256 -19
- package/lib/dashboards/WidgetComponent.d.ts +6 -3
- package/lib/dashboards/WidgetComponent.js +3 -1
- package/lib/datagrids/CellEditor.d.ts +19 -0
- package/lib/datagrids/CellEditor.js +223 -0
- package/lib/datagrids/DatagridComponent.d.ts +18 -87
- package/lib/datagrids/DatagridComponent.js +304 -222
- package/lib/datagrids/DatagridViewComponent.d.ts +15 -53
- package/lib/datagrids/DatagridViewComponent.js +256 -257
- package/lib/datagrids/DirectDatagridDataSource.js +2 -3
- package/lib/datagrids/ExprCellComponent.d.ts +8 -15
- package/lib/datagrids/ExprCellComponent.js +11 -15
- package/lib/datagrids/FindReplaceModalComponent.d.ts +4 -6
- package/lib/datagrids/FindReplaceModalComponent.js +38 -75
- package/lib/index.css +1 -1
- package/lib/index.d.ts +0 -1
- package/lib/index.js +0 -1
- package/lib/layouts/blocks/HorizontalBlockComponent.js +2 -2
- package/lib/mWaterLoader.d.ts +1 -1
- package/lib/maps/BufferLayer.d.ts +7 -5
- package/lib/maps/BufferLayer.js +69 -48
- package/lib/maps/BufferLayerDesign.d.ts +21 -14
- package/lib/maps/BufferLayerDesignerComponent.d.ts +16 -31
- package/lib/maps/BufferLayerDesignerComponent.js +68 -102
- package/lib/maps/ChoroplethLayer.d.ts +5 -4
- package/lib/maps/ChoroplethLayer.js +32 -9
- package/lib/maps/ChoroplethLayerDesign.d.ts +6 -2
- package/lib/maps/ChoroplethLayerDesigner.js +4 -2
- package/lib/maps/ClusterLayer.d.ts +3 -4
- package/lib/maps/ClusterLayer.js +2 -1
- package/lib/maps/DetailLevelSelectComponent.js +1 -1
- package/lib/maps/DirectMapDataSource.js +2 -1
- package/lib/maps/EditPopupComponent.js +5 -3
- package/lib/maps/GridLayer.d.ts +3 -4
- package/lib/maps/GridLayer.js +2 -1
- package/lib/maps/GridLayerDesigner.js +5 -3
- package/lib/maps/HoverContent.d.ts +11 -3
- package/lib/maps/HoverContent.js +25 -9
- package/lib/maps/Layer.d.ts +24 -3
- package/lib/maps/Layer.js +5 -1
- package/lib/maps/LayerFactory.js +0 -8
- package/lib/maps/LayerLegendComponent.js +0 -1
- package/lib/maps/LayerSwitcherComponent.d.ts +1 -0
- package/lib/maps/LayerSwitcherComponent.js +1 -1
- package/lib/maps/LeafletMapComponent.js +3 -1
- package/lib/maps/LegendComponent.d.ts +1 -0
- package/lib/maps/LegendComponent.js +9 -1
- package/lib/maps/MWaterServerLayer.d.ts +2 -2
- package/lib/maps/MWaterServerLayer.js +2 -2
- package/lib/maps/MapComponent.js +3 -3
- package/lib/maps/MapDesign.d.ts +2 -0
- package/lib/maps/MapDesignerComponent.d.ts +4 -3
- package/lib/maps/MapDesignerComponent.js +68 -74
- package/lib/maps/MapLayerViewDesignerComponent.js +2 -2
- package/lib/maps/MapUtils.d.ts +4 -0
- package/lib/maps/MapUtils.js +19 -0
- package/lib/maps/MapViewComponent.d.ts +8 -3
- package/lib/maps/MarkersLayer.d.ts +5 -4
- package/lib/maps/MarkersLayer.js +33 -7
- package/lib/maps/MarkersLayerDesign.d.ts +19 -16
- package/lib/maps/PopupFilterJoinsUtils.d.ts +6 -3
- package/lib/maps/PopupFilterJoinsUtils.js +0 -6
- package/lib/maps/RasterMapViewComponent.d.ts +3 -31
- package/lib/maps/RasterMapViewComponent.js +7 -2
- package/lib/maps/ServerMapDataSource.js +2 -1
- package/lib/maps/SwitchableTileUrlLayer.d.ts +3 -3
- package/lib/maps/SwitchableTileUrlLayer.js +2 -1
- package/lib/maps/TileUrlLayer.d.ts +4 -5
- package/lib/maps/TileUrlLayer.js +2 -1
- package/lib/maps/VectorMapViewComponent.d.ts +5 -37
- package/lib/maps/VectorMapViewComponent.js +19 -8
- package/lib/maps/maps.d.ts +3 -0
- package/lib/quickfilter/QuickfiltersComponent.d.ts +2 -0
- package/lib/quickfilter/QuickfiltersComponent.js +9 -7
- package/lib/quickfilter/QuickfiltersDesignComponent.d.ts +1 -1
- package/lib/quickfilter/QuickfiltersDesignComponent.js +19 -35
- package/lib/richtext/ExprItemsHtmlConverter.d.ts +5 -2
- package/lib/richtext/ExprItemsHtmlConverter.js +4 -4
- package/lib/richtext/ExprItemsTranslator.d.ts +5 -0
- package/lib/richtext/ExprItemsTranslator.js +149 -0
- package/lib/richtext/ItemsHtmlConverter.d.ts +1 -1
- package/lib/richtext/ItemsHtmlConverter.js +31 -15
- package/lib/wellknown.js +12 -9
- package/lib/widgets/IFrameWidget.d.ts +4 -4
- package/lib/widgets/ImageWidget.d.ts +7 -4
- package/lib/widgets/ImageWidget.js +9 -1
- package/lib/widgets/ImageWidgetComponent.d.ts +1 -0
- package/lib/widgets/ImageWidgetComponent.js +1 -1
- package/lib/widgets/MapWidget.d.ts +5 -48
- package/lib/widgets/MapWidget.js +26 -63
- package/lib/widgets/MarkdownWidget.d.ts +3 -0
- package/lib/widgets/MarkdownWidget.js +3 -0
- package/lib/widgets/TOCWidget.d.ts +15 -27
- package/lib/widgets/TOCWidget.js +107 -183
- package/lib/widgets/Widget.d.ts +18 -7
- package/lib/widgets/Widget.js +4 -0
- package/lib/widgets/WidgetScopesViewComponent.js +1 -1
- package/lib/widgets/charts/Chart.d.ts +10 -1
- package/lib/widgets/charts/Chart.js +22 -11
- package/lib/widgets/charts/ChartViewComponent.d.ts +4 -0
- package/lib/widgets/charts/ChartViewComponent.js +6 -3
- package/lib/widgets/charts/ChartWidget.d.ts +2 -0
- package/lib/widgets/charts/ChartWidget.js +9 -1
- package/lib/widgets/charts/ChartWidgetComponent.d.ts +4 -0
- package/lib/widgets/charts/ChartWidgetComponent.js +2 -2
- package/lib/widgets/charts/calendar/CalendarChart.d.ts +1 -0
- package/lib/widgets/charts/calendar/CalendarChart.js +26 -0
- package/lib/widgets/charts/calendar/CalendarChartViewComponent.js +3 -1
- package/lib/widgets/charts/imagemosaic/ImageMosaicChart.d.ts +1 -0
- package/lib/widgets/charts/imagemosaic/ImageMosaicChart.js +8 -0
- package/lib/widgets/charts/layered/LayeredChart.d.ts +2 -0
- package/lib/widgets/charts/layered/LayeredChart.js +63 -3
- package/lib/widgets/charts/layered/LayeredChartCompiler.d.ts +1 -1
- package/lib/widgets/charts/layered/LayeredChartCompiler.js +3 -3
- package/lib/widgets/charts/layered/LayeredChartDesignerComponent.js +2 -2
- package/lib/widgets/charts/layered/LayeredChartViewComponent.js +8 -3
- package/lib/widgets/charts/pivot/PivotChart.d.ts +1 -0
- package/lib/widgets/charts/pivot/PivotChart.js +63 -0
- package/lib/widgets/charts/pivot/PivotChartDesignerComponent.d.ts +1 -0
- package/lib/widgets/charts/pivot/PivotChartLayoutComponent.js +1 -1
- package/lib/widgets/charts/pivot/SegmentDesignerComponent.d.ts +6 -0
- package/lib/widgets/charts/pivot/SegmentDesignerComponent.js +7 -4
- package/lib/widgets/charts/table/OrderingsComponent.js +1 -1
- package/lib/widgets/charts/table/TableChart.d.ts +1 -0
- package/lib/widgets/charts/table/TableChart.js +15 -0
- package/lib/widgets/text/TextComponent.d.ts +11 -4
- package/lib/widgets/text/TextComponent.js +11 -8
- package/lib/widgets/text/TextWidget.d.ts +6 -3
- package/lib/widgets/text/TextWidget.js +7 -1
- package/lib/widgets/text/TextWidgetComponent.d.ts +4 -0
- package/lib/widgets/text/TextWidgetComponent.js +7 -1
- package/lib/widgets/text/TextWidgetDesign.d.ts +2 -4
- package/lib/widgets/text/TextWidgetDesign.js +1 -1
- package/package.json +7 -8
- package/src/ColorComponent.tsx +1 -2
- package/src/IdSelection.ts +62 -0
- package/src/MWaterAddRelatedIndicatorComponent.ts +3 -2
- package/src/MWaterCompleteTableSelectComponent.tsx +36 -46
- package/src/MWaterLoaderComponent.ts +28 -26
- package/src/MWaterResponsesFilterComponent.ts +5 -2
- package/src/MWaterTableSelectComponent.tsx +5 -9
- package/src/autotranslate.ts +141 -0
- package/src/axes/AxisBuilder.ts +3 -3
- package/src/axes/AxisColorEditorComponent.tsx +5 -0
- package/src/axes/{AxisComponent.ts → AxisComponent.tsx} +106 -106
- package/src/axes/CategoryMapComponent.ts +4 -4
- package/src/axes/RangesComponent.ts +3 -2
- package/src/dashboards/DashboardComponent.tsx +79 -14
- package/src/dashboards/DashboardDesign.ts +9 -2
- package/src/dashboards/DashboardUtils.ts +39 -0
- package/src/dashboards/DashboardViewComponent.tsx +22 -3
- package/src/dashboards/ServerDashboardDataSource.ts +2 -1
- package/src/dashboards/SettingsModalComponent.tsx +450 -35
- package/src/dashboards/WidgetComponent.tsx +12 -6
- package/src/datagrids/CellEditor.tsx +354 -0
- package/src/datagrids/DatagridComponent.tsx +646 -0
- package/src/datagrids/DatagridViewComponent.tsx +539 -0
- package/src/datagrids/DirectDatagridDataSource.ts +2 -3
- package/src/datagrids/{ExprCellComponent.ts → ExprCellComponent.tsx} +28 -23
- package/src/datagrids/{FindReplaceModalComponent.ts → FindReplaceModalComponent.tsx} +109 -122
- package/src/index.css +1 -1
- package/src/index.ts +0 -1
- package/src/layouts/blocks/HorizontalBlockComponent.ts +2 -2
- package/src/mWaterLoader.ts +1 -1
- package/src/maps/BufferLayer.ts +83 -60
- package/src/maps/BufferLayerDesign.ts +20 -14
- package/src/maps/BufferLayerDesignerComponent.tsx +309 -0
- package/src/maps/ChoroplethLayer.ts +40 -19
- package/src/maps/ChoroplethLayerDesign.ts +4 -2
- package/src/maps/ChoroplethLayerDesigner.tsx +4 -2
- package/src/maps/ClusterLayer.ts +4 -10
- package/src/maps/DetailLevelSelectComponent.ts +1 -1
- package/src/maps/DirectMapDataSource.ts +2 -1
- package/src/maps/EditPopupComponent.ts +7 -3
- package/src/maps/GridLayer.ts +4 -10
- package/src/maps/GridLayerDesigner.tsx +5 -3
- package/src/maps/HoverContent.tsx +40 -16
- package/src/maps/Layer.ts +28 -10
- package/src/maps/LayerFactory.ts +0 -8
- package/src/maps/LayerLegendComponent.ts +2 -4
- package/src/maps/LayerSwitcherComponent.tsx +6 -2
- package/src/maps/LeafletMapComponent.tsx +3 -1
- package/src/maps/LegendComponent.tsx +10 -1
- package/src/maps/MWaterServerLayer.ts +3 -3
- package/src/maps/MapComponent.ts +3 -3
- package/src/maps/MapDesign.ts +3 -0
- package/src/maps/MapDesignerComponent.tsx +165 -162
- package/src/maps/MapLayerViewDesignerComponent.ts +2 -2
- package/src/maps/MapUtils.ts +24 -0
- package/src/maps/MapViewComponent.tsx +11 -3
- package/src/maps/MarkersLayer.ts +44 -18
- package/src/maps/MarkersLayerDesign.ts +19 -16
- package/src/maps/PopupFilterJoinsUtils.ts +6 -2
- package/src/maps/RasterMapViewComponent.ts +9 -45
- package/src/maps/ServerMapDataSource.ts +2 -2
- package/src/maps/SwitchableTileUrlLayer.tsx +4 -10
- package/src/maps/TileUrlLayer.tsx +4 -10
- package/src/maps/VectorMapViewComponent.tsx +28 -55
- package/src/maps/maps.ts +3 -0
- package/src/quickfilter/QuickfiltersComponent.ts +13 -7
- package/src/quickfilter/QuickfiltersDesignComponent.tsx +56 -74
- package/src/richtext/ExprItemsHtmlConverter.ts +9 -5
- package/src/richtext/ExprItemsTranslator.ts +176 -0
- package/src/richtext/ItemsHtmlConverter.ts +33 -18
- package/src/wellknown.ts +33 -30
- package/src/widgets/ImageWidget.ts +10 -1
- package/src/widgets/ImageWidgetComponent.ts +3 -2
- package/src/widgets/{MapWidget.ts → MapWidget.tsx} +90 -101
- package/src/widgets/MarkdownWidget.ts +3 -0
- package/src/widgets/TOCWidget.tsx +281 -0
- package/src/widgets/Widget.ts +25 -5
- package/src/widgets/WidgetScopesViewComponent.ts +2 -1
- package/src/widgets/charts/Chart.ts +31 -12
- package/src/widgets/charts/ChartViewComponent.ts +13 -3
- package/src/widgets/charts/ChartWidget.ts +11 -1
- package/src/widgets/charts/ChartWidgetComponent.tsx +9 -1
- package/src/widgets/charts/calendar/CalendarChart.ts +29 -0
- package/src/widgets/charts/calendar/CalendarChartViewComponent.tsx +3 -1
- package/src/widgets/charts/imagemosaic/ImageMosaicChart.ts +9 -0
- package/src/widgets/charts/layered/LayeredChart.ts +71 -3
- package/src/widgets/charts/layered/LayeredChartCompiler.ts +4 -4
- package/src/widgets/charts/layered/LayeredChartDesignerComponent.tsx +4 -2
- package/src/widgets/charts/layered/LayeredChartViewComponent.ts +10 -4
- package/src/widgets/charts/pivot/PivotChart.ts +73 -0
- package/src/widgets/charts/pivot/PivotChartLayoutComponent.tsx +1 -1
- package/src/widgets/charts/pivot/SegmentDesignerComponent.tsx +6 -4
- package/src/widgets/charts/table/OrderingsComponent.tsx +2 -1
- package/src/widgets/charts/table/TableChart.ts +17 -0
- package/src/widgets/text/TextComponent.tsx +22 -12
- package/src/widgets/text/TextWidget.ts +9 -2
- package/src/widgets/text/TextWidgetComponent.tsx +16 -1
- package/src/widgets/text/TextWidgetDesign.ts +4 -7
- package/test/IdSelectionTests.ts +54 -0
- package/test/LayeredChartCompilerTests.ts +0 -2
- package/test/richtext/ExprItemsTranslatorTests.ts +144 -0
- package/test/wellknownTests.ts +144 -0
- package/src/datagrids/DatagridComponent.ts +0 -478
- package/src/datagrids/DatagridViewComponent.ts +0 -464
- package/src/datagrids/EditExprCellComponent.tsx +0 -305
- package/src/datagrids/README.md +0 -3
- package/src/maps/BufferLayerDesignerComponent.ts +0 -311
- package/src/widgets/TOCWidget.ts +0 -326
- package/test/LegoLayoutEngineTests.ts +0 -69
|
@@ -0,0 +1,646 @@
|
|
|
1
|
+
import _ from "lodash"
|
|
2
|
+
import React, { ReactNode, useEffect, useState, useRef, forwardRef, useImperativeHandle } from "react"
|
|
3
|
+
|
|
4
|
+
import AutoSizeComponent from "@mwater/react-library/lib/AutoSizeComponent"
|
|
5
|
+
import ActionCancelModalComponent from "@mwater/react-library/lib/ActionCancelModalComponent"
|
|
6
|
+
import { DataSource, Expr, ExprUtils, injectTableAlias, Schema } from "@mwater/expressions"
|
|
7
|
+
import { ExprCompiler } from "@mwater/expressions"
|
|
8
|
+
import { ExprCleaner } from "@mwater/expressions"
|
|
9
|
+
import DatagridViewComponent, { DatagridViewComponentRef, RowUpdate } from "./DatagridViewComponent"
|
|
10
|
+
import DatagridDesignerComponent from "./DatagridDesignerComponent"
|
|
11
|
+
import DatagridUtils from "./DatagridUtils"
|
|
12
|
+
import { Checkbox } from "@mwater/react-library/lib/bootstrap"
|
|
13
|
+
|
|
14
|
+
import QuickfiltersComponent from "../quickfilter/QuickfiltersComponent"
|
|
15
|
+
import QuickfilterCompiler from "../quickfilter/QuickfilterCompiler"
|
|
16
|
+
import FindReplaceModalComponent from "./FindReplaceModalComponent"
|
|
17
|
+
import DatagridDataSource from "./DatagridDataSource"
|
|
18
|
+
import { DatagridDesign, JsonQLFilter } from ".."
|
|
19
|
+
import { format as d3Format } from "d3-format"
|
|
20
|
+
import { LocaleContext } from "@mwater/expressions-ui"
|
|
21
|
+
import IdSelection from "../IdSelection"
|
|
22
|
+
import { useStableCallback } from "@mwater/react-library/lib/useStableCallback"
|
|
23
|
+
import DatagridQueryBuilder from "./DatagridQueryBuilder"
|
|
24
|
+
import produce from "immer"
|
|
25
|
+
|
|
26
|
+
export interface DatagridComponentRef {
|
|
27
|
+
getQuickfilterValues: () => any[]
|
|
28
|
+
getQuickfilterFilters: () => JsonQLFilter[]
|
|
29
|
+
reload: () => void
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface DatagridComponentProps {
|
|
33
|
+
/** Schema to use */
|
|
34
|
+
schema: Schema
|
|
35
|
+
|
|
36
|
+
/** Data source to use */
|
|
37
|
+
dataSource: DataSource
|
|
38
|
+
|
|
39
|
+
/** Datagrid data source to use */
|
|
40
|
+
datagridDataSource: DatagridDataSource
|
|
41
|
+
|
|
42
|
+
/** Design of datagrid to use */
|
|
43
|
+
design: DatagridDesign
|
|
44
|
+
|
|
45
|
+
/** Called when design changes */
|
|
46
|
+
onDesignChange?: (design: DatagridDesign) => void
|
|
47
|
+
|
|
48
|
+
/** Extra element to include in title at left */
|
|
49
|
+
titleElem?: ReactNode
|
|
50
|
+
|
|
51
|
+
/** Extra elements to add to right */
|
|
52
|
+
extraTitleButtonsElem?: ReactNode
|
|
53
|
+
|
|
54
|
+
/** Check if a value is editable by testing if underlying expression is editable */
|
|
55
|
+
canEditExpr?: (tableId: string, rowId: any, expr: Expr) => Promise<boolean>
|
|
56
|
+
|
|
57
|
+
/** Update cell values by updating set of expressions and values */
|
|
58
|
+
updateExprValues?: (tableId: string, rowUpdates: RowUpdate[]) => Promise<void>
|
|
59
|
+
|
|
60
|
+
/** Called when row is clicked with (tableId, rowId) */
|
|
61
|
+
onRowClick?: (tableId: string, rowId: any) => void
|
|
62
|
+
|
|
63
|
+
/** Called when row is double-clicked with (tableId, rowId) */
|
|
64
|
+
onRowDoubleClick?: (tableId: string, rowId: any) => void
|
|
65
|
+
|
|
66
|
+
/** Called when a set of rows is deleted with (tableId, rowIds). Can be cancelled via the abort signal */
|
|
67
|
+
onRowsDelete?: (tableId: string, rowIds: any[], signal: AbortSignal) => Promise<void>
|
|
68
|
+
|
|
69
|
+
/** Locked quickfilter values. See README in quickfilters */
|
|
70
|
+
quickfilterLocks?: any[]
|
|
71
|
+
|
|
72
|
+
// Filters to add to the datagrid
|
|
73
|
+
filters?: JsonQLFilter[]
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Datagrid with decorations
|
|
78
|
+
* Design should be cleaned already before being passed in (see DatagridUtils)
|
|
79
|
+
*/
|
|
80
|
+
export default forwardRef<DatagridComponentRef, DatagridComponentProps>(function DatagridComponent(props: DatagridComponentProps, ref) {
|
|
81
|
+
const { schema, dataSource, datagridDataSource, design, onDesignChange, titleElem, extraTitleButtonsElem, canEditExpr, updateExprValues, onRowClick, onRowDoubleClick, quickfilterLocks, filters = [] } = props
|
|
82
|
+
|
|
83
|
+
// State management
|
|
84
|
+
/** is design being edited */
|
|
85
|
+
const [editingDesign, setEditingDesign] = useState(false)
|
|
86
|
+
|
|
87
|
+
/** True if cells can be edited directly */
|
|
88
|
+
const [dataEditingEnabled, setDataEditingEnabled] = useState(false)
|
|
89
|
+
|
|
90
|
+
/** Height of quickfilters */
|
|
91
|
+
const [quickfiltersHeight, setQuickfiltersHeight] = useState<number | null>(null)
|
|
92
|
+
|
|
93
|
+
/** Values of quickfilters */
|
|
94
|
+
const [quickfiltersValues, setQuickfiltersValues] = useState<any[] | null>(null)
|
|
95
|
+
|
|
96
|
+
/** Refresh key */
|
|
97
|
+
const [refreshKey, setRefreshKey] = useState(1)
|
|
98
|
+
|
|
99
|
+
/** Number of rows */
|
|
100
|
+
const [numRows, setNumRows] = useState<number>()
|
|
101
|
+
|
|
102
|
+
/** Selected rows */
|
|
103
|
+
const [selectedRows, setSelectedRows] = useState(new IdSelection())
|
|
104
|
+
|
|
105
|
+
/** True if delete confirmation modal is open */
|
|
106
|
+
const [showDeleteConfirmation, setShowDeleteConfirmation] = useState(false)
|
|
107
|
+
|
|
108
|
+
/** IDs of rows to be deleted */
|
|
109
|
+
const [rowIdsToDelete, setRowIdsToDelete] = useState<string[]>()
|
|
110
|
+
|
|
111
|
+
/** True when loading IDs of rows to delete */
|
|
112
|
+
const [loadingRowIdsToDelete, setLoadingRowIdsToDelete] = useState(false)
|
|
113
|
+
|
|
114
|
+
// Refs
|
|
115
|
+
const datagridViewRef = React.useRef<DatagridViewComponentRef | null>(null)
|
|
116
|
+
const quickfiltersRef = React.useRef<HTMLDivElement | null>(null)
|
|
117
|
+
const findReplaceModalRef = React.useRef<FindReplaceModalComponent | null>(null)
|
|
118
|
+
|
|
119
|
+
const reload = () => {
|
|
120
|
+
datagridViewRef.current?.reload()
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const loadRowCount = useStableCallback(() => {
|
|
124
|
+
if (!design.showNumRows) {
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
let activeFilters = filters || []
|
|
128
|
+
|
|
129
|
+
// Compile quickfilters
|
|
130
|
+
activeFilters = activeFilters.concat(getQuickfilterFilters())
|
|
131
|
+
datagridDataSource.countRows(design,
|
|
132
|
+
activeFilters,
|
|
133
|
+
(error: any, count?: number) => {
|
|
134
|
+
if (error) {
|
|
135
|
+
console.error(error)
|
|
136
|
+
alert(T`Error loading data`)
|
|
137
|
+
return
|
|
138
|
+
}
|
|
139
|
+
setNumRows(count)
|
|
140
|
+
})
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
useEffect(() => {
|
|
144
|
+
loadRowCount()
|
|
145
|
+
}, [JSON.stringify(design), JSON.stringify(quickfiltersValues), refreshKey])
|
|
146
|
+
|
|
147
|
+
useEffect(() => {
|
|
148
|
+
// Calculate quickfilters height
|
|
149
|
+
if (quickfiltersRef.current) {
|
|
150
|
+
if (quickfiltersHeight !== quickfiltersRef.current.offsetHeight) {
|
|
151
|
+
setQuickfiltersHeight(quickfiltersRef.current.offsetHeight)
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
setQuickfiltersHeight(0)
|
|
155
|
+
}
|
|
156
|
+
}, [quickfiltersRef.current, JSON.stringify(design.quickfilters)])
|
|
157
|
+
|
|
158
|
+
const handleRefreshData = () => {
|
|
159
|
+
dataSource.clearCache?.()
|
|
160
|
+
setRefreshKey(refreshKey + 1)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const handleDeleteRows = async (signal: AbortSignal) => {
|
|
164
|
+
try {
|
|
165
|
+
if (!rowIdsToDelete) {
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
await props.onRowsDelete!(design.table!, rowIdsToDelete, signal)
|
|
170
|
+
setShowDeleteConfirmation(false)
|
|
171
|
+
|
|
172
|
+
// If not aborted, clear selected rows
|
|
173
|
+
if (!signal.aborted) {
|
|
174
|
+
setSelectedRows(new IdSelection())
|
|
175
|
+
handleRefreshData()
|
|
176
|
+
}
|
|
177
|
+
setRowIdsToDelete(undefined)
|
|
178
|
+
} catch (error) {
|
|
179
|
+
if (error.name !== "AbortError") {
|
|
180
|
+
alert(`Error deleting rows: ${error}`)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const handleShowDeleteConfirmation = async () => {
|
|
186
|
+
setShowDeleteConfirmation(true)
|
|
187
|
+
setLoadingRowIdsToDelete(true)
|
|
188
|
+
setRowIdsToDelete(undefined)
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
// Get row ids
|
|
192
|
+
let rowIds = Object.keys(selectedRows.ids)
|
|
193
|
+
|
|
194
|
+
// If inverted, get all ids and remove selected ids
|
|
195
|
+
if (selectedRows.inverted) {
|
|
196
|
+
// Create modified design to just get ids
|
|
197
|
+
const idsDesign = produce(design, (draft) => {
|
|
198
|
+
draft.columns = []
|
|
199
|
+
draft.subtables = []
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
// Create query builder
|
|
203
|
+
const queryBuilder = new DatagridQueryBuilder(schema)
|
|
204
|
+
const query = queryBuilder.createQuery(idsDesign, {
|
|
205
|
+
extraFilters: filters
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
// Perform query
|
|
209
|
+
const rows = await dataSource.performQuery(query)
|
|
210
|
+
rowIds = rows.map((row: any) => row.id)
|
|
211
|
+
|
|
212
|
+
// Remove rows from rowIds
|
|
213
|
+
rowIds = rowIds.filter(id => !selectedRows.ids[id])
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
setRowIdsToDelete(rowIds)
|
|
217
|
+
} catch (error) {
|
|
218
|
+
alert(`Error loading rows to delete: ${error}`)
|
|
219
|
+
setShowDeleteConfirmation(false)
|
|
220
|
+
} finally {
|
|
221
|
+
setLoadingRowIdsToDelete(false)
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Get the values of the quick filters
|
|
226
|
+
const getQuickfilterValues = () => {
|
|
227
|
+
return quickfiltersValues || []
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** Get filters that are applied by the quickfilters */
|
|
231
|
+
const getQuickfilterFilters = () => {
|
|
232
|
+
return new QuickfilterCompiler(schema).compile(
|
|
233
|
+
design.quickfilters,
|
|
234
|
+
quickfiltersValues,
|
|
235
|
+
quickfilterLocks
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const handleDataEditingToggle = () => {
|
|
240
|
+
if (dataEditingEnabled) {
|
|
241
|
+
setDataEditingEnabled(false)
|
|
242
|
+
} else {
|
|
243
|
+
if (confirm(T`Turn on data editing? This will allow you to edit the live data and is an advanced feature.`)) {
|
|
244
|
+
setDataEditingEnabled(true)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const handleEdit = () => {
|
|
250
|
+
setEditingDesign(true)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** Get datagrid filter compiled for quickfilter filtering */
|
|
254
|
+
const getCompiledFilters = (): JsonQLFilter[] => {
|
|
255
|
+
let jsonql
|
|
256
|
+
const exprCompiler = new ExprCompiler(schema)
|
|
257
|
+
const exprUtils = new ExprUtils(schema)
|
|
258
|
+
const exprCleaner = new ExprCleaner(schema)
|
|
259
|
+
|
|
260
|
+
const compiledFilters: JsonQLFilter[] = []
|
|
261
|
+
|
|
262
|
+
if (design.filter) {
|
|
263
|
+
// First clean the filter
|
|
264
|
+
const cleanExpr = exprCleaner.cleanExpr(design.filter, { table: design.table! })
|
|
265
|
+
|
|
266
|
+
jsonql = exprCompiler.compileExpr({ expr: cleanExpr, tableAlias: "{alias}" })
|
|
267
|
+
if (jsonql) {
|
|
268
|
+
compiledFilters.push({
|
|
269
|
+
table: design.table!,
|
|
270
|
+
jsonql
|
|
271
|
+
})
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Add global filters
|
|
276
|
+
for (let filter of design.globalFilters || []) {
|
|
277
|
+
// Check if exists and is correct type
|
|
278
|
+
const column = schema.getColumn(design.table!, filter.columnId)
|
|
279
|
+
if (!column) {
|
|
280
|
+
continue
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const columnExpr: Expr = { type: "field", table: design.table!, column: column.id }
|
|
284
|
+
if (exprUtils.getExprType(columnExpr) !== filter.columnType) {
|
|
285
|
+
continue
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Create expr
|
|
289
|
+
let expr: Expr = {
|
|
290
|
+
type: "op",
|
|
291
|
+
op: filter.op,
|
|
292
|
+
table: design.table!,
|
|
293
|
+
exprs: [columnExpr as Expr].concat(filter.exprs)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Clean expr
|
|
297
|
+
expr = exprCleaner.cleanExpr(expr, { table: design.table! })
|
|
298
|
+
|
|
299
|
+
jsonql = exprCompiler.compileExpr({ expr, tableAlias: "{alias}" })
|
|
300
|
+
if (jsonql) {
|
|
301
|
+
compiledFilters.push({
|
|
302
|
+
table: design.table!,
|
|
303
|
+
jsonql
|
|
304
|
+
})
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return compiledFilters
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const renderDeleteRows = () => {
|
|
312
|
+
if (!props.onRowsDelete || selectedRows.isNone()) {
|
|
313
|
+
return null
|
|
314
|
+
}
|
|
315
|
+
return <button className="btn btn-link btn-sm" onClick={handleShowDeleteConfirmation}>
|
|
316
|
+
<i className="fas fa-times" />
|
|
317
|
+
{" "}
|
|
318
|
+
{T`Delete Selected Rows`}
|
|
319
|
+
</button>
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/** Toggle to allow cell editing */
|
|
323
|
+
const renderCellEdit = () => {
|
|
324
|
+
if (!canEditExpr) {
|
|
325
|
+
return null
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const label = <>
|
|
329
|
+
<i className={dataEditingEnabled ? "fa fa-fw fa-check-square" : "fa fa-fw fa-square-o"} />
|
|
330
|
+
{" "}
|
|
331
|
+
{T`Data Editing`}
|
|
332
|
+
</>
|
|
333
|
+
|
|
334
|
+
return (
|
|
335
|
+
<a
|
|
336
|
+
key="cell-edit"
|
|
337
|
+
className="btn btn-link btn-sm"
|
|
338
|
+
onClick={handleDataEditingToggle}
|
|
339
|
+
>
|
|
340
|
+
{label}
|
|
341
|
+
</a>
|
|
342
|
+
)
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const renderEditButton = () => {
|
|
346
|
+
if (!onDesignChange) {
|
|
347
|
+
return null
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return (
|
|
351
|
+
<button
|
|
352
|
+
type="button"
|
|
353
|
+
className="btn btn-primary"
|
|
354
|
+
onClick={handleEdit}
|
|
355
|
+
>
|
|
356
|
+
<span className="fas fa-cog" />
|
|
357
|
+
{" "}
|
|
358
|
+
{T`Settings`}
|
|
359
|
+
</button>
|
|
360
|
+
)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const renderFindReplace = () => {
|
|
364
|
+
if (!dataEditingEnabled) {
|
|
365
|
+
return null
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return (
|
|
369
|
+
<a
|
|
370
|
+
key="findreplace"
|
|
371
|
+
className="btn btn-link btn-sm"
|
|
372
|
+
onClick={() => findReplaceModalRef.current!.show()}
|
|
373
|
+
>
|
|
374
|
+
{T`Find/Replace`}
|
|
375
|
+
</a>
|
|
376
|
+
)
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const renderTitleBar = () => {
|
|
380
|
+
return (
|
|
381
|
+
<div style={{ position: "absolute", top: 0, left: 0, right: 0, height: 40, padding: 4 }}>
|
|
382
|
+
<div style={{ float: "right" }}>
|
|
383
|
+
{design.showNumRows && numRows ? <small className='text-muted text-sm'>{`${d3Format(',')(numRows)} rows`}</small> : undefined}
|
|
384
|
+
{renderDeleteRows()}
|
|
385
|
+
{renderFindReplace()}
|
|
386
|
+
{renderCellEdit()}
|
|
387
|
+
{renderEditButton()}
|
|
388
|
+
<a key="refresh" className="btn btn-link btn-sm" onClick={handleRefreshData}>
|
|
389
|
+
<span className="fas fa-sync" />
|
|
390
|
+
{" "}
|
|
391
|
+
<span className="hide-600px">{T`Refresh`}</span>
|
|
392
|
+
</a>
|
|
393
|
+
{extraTitleButtonsElem}
|
|
394
|
+
</div>
|
|
395
|
+
{titleElem}
|
|
396
|
+
</div>
|
|
397
|
+
)
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const renderQuickfilter = () => {
|
|
401
|
+
return (
|
|
402
|
+
<div
|
|
403
|
+
style={{ position: "absolute", top: 40, left: 0, right: 0 }}
|
|
404
|
+
ref={quickfiltersRef}
|
|
405
|
+
>
|
|
406
|
+
<QuickfiltersComponent
|
|
407
|
+
design={design.quickfilters!}
|
|
408
|
+
schema={schema}
|
|
409
|
+
dataSource={dataSource}
|
|
410
|
+
quickfiltersDataSource={datagridDataSource.getQuickfiltersDataSource()}
|
|
411
|
+
values={quickfiltersValues || []}
|
|
412
|
+
onValuesChange={(values: any) => setQuickfiltersValues(values)}
|
|
413
|
+
locks={quickfilterLocks}
|
|
414
|
+
filters={getCompiledFilters()}
|
|
415
|
+
/>
|
|
416
|
+
</div>
|
|
417
|
+
)
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Renders the editor modal
|
|
421
|
+
const renderEditor = () => {
|
|
422
|
+
if (!editingDesign) {
|
|
423
|
+
return null
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return (
|
|
427
|
+
<DatagridEditorComponent
|
|
428
|
+
schema={schema}
|
|
429
|
+
dataSource={dataSource}
|
|
430
|
+
design={design}
|
|
431
|
+
onDesignChange={(newDesign: any) => {
|
|
432
|
+
// If quickfilters have changed, reset values
|
|
433
|
+
if (!_.isEqual(design.quickfilters, newDesign.quickfilters)) {
|
|
434
|
+
setQuickfiltersValues(null)
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
onDesignChange!(newDesign)
|
|
438
|
+
setEditingDesign(false)
|
|
439
|
+
}}
|
|
440
|
+
onCancel={() => setEditingDesign(false)}
|
|
441
|
+
/>
|
|
442
|
+
)
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const renderFindReplaceModal = (filters: any) => {
|
|
446
|
+
return (
|
|
447
|
+
<FindReplaceModalComponent
|
|
448
|
+
ref={findReplaceModalRef}
|
|
449
|
+
schema={schema}
|
|
450
|
+
dataSource={dataSource}
|
|
451
|
+
design={design}
|
|
452
|
+
filters={filters}
|
|
453
|
+
updateExprValues={updateExprValues!}
|
|
454
|
+
onUpdate={() => {
|
|
455
|
+
// Reload
|
|
456
|
+
datagridViewRef.current?.reload()
|
|
457
|
+
}}
|
|
458
|
+
/>
|
|
459
|
+
)
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
let activeFilters = filters || []
|
|
463
|
+
|
|
464
|
+
// Compile quickfilters
|
|
465
|
+
activeFilters = activeFilters.concat(getQuickfilterFilters())
|
|
466
|
+
|
|
467
|
+
useImperativeHandle(
|
|
468
|
+
ref,
|
|
469
|
+
(): DatagridComponentRef => ({
|
|
470
|
+
getQuickfilterValues,
|
|
471
|
+
getQuickfilterFilters,
|
|
472
|
+
reload
|
|
473
|
+
})
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
return (
|
|
477
|
+
<LocaleContext.Provider value={design.locale || "en"}>
|
|
478
|
+
<div
|
|
479
|
+
style={{
|
|
480
|
+
width: "100%",
|
|
481
|
+
height: "100%",
|
|
482
|
+
position: "relative",
|
|
483
|
+
paddingTop: 40 + (quickfiltersHeight || 0)
|
|
484
|
+
}}
|
|
485
|
+
>
|
|
486
|
+
{renderTitleBar()}
|
|
487
|
+
{renderQuickfilter()}
|
|
488
|
+
|
|
489
|
+
{renderEditor()}
|
|
490
|
+
{renderFindReplaceModal(activeFilters)}
|
|
491
|
+
{showDeleteConfirmation && (
|
|
492
|
+
<DeleteConfirmationModal
|
|
493
|
+
onConfirm={handleDeleteRows}
|
|
494
|
+
onCancel={() => {
|
|
495
|
+
setShowDeleteConfirmation(false)
|
|
496
|
+
setRowIdsToDelete(undefined)
|
|
497
|
+
}}
|
|
498
|
+
numRows={rowIdsToDelete?.length}
|
|
499
|
+
loadingNumRows={loadingRowIdsToDelete}
|
|
500
|
+
/>
|
|
501
|
+
)}
|
|
502
|
+
|
|
503
|
+
<AutoSizeComponent injectWidth injectHeight>
|
|
504
|
+
{(size: any) => {
|
|
505
|
+
// Clean before displaying
|
|
506
|
+
const cleanedDesign = new DatagridUtils(schema).cleanDesign(design)
|
|
507
|
+
|
|
508
|
+
if (!new DatagridUtils(schema).validateDesign(cleanedDesign)) {
|
|
509
|
+
return (
|
|
510
|
+
<DatagridViewComponent
|
|
511
|
+
ref={datagridViewRef}
|
|
512
|
+
width={size.width - 1} // minus 1 px to test if it solves the jitter with scroll
|
|
513
|
+
height={size.height - 1}
|
|
514
|
+
pageSize={100}
|
|
515
|
+
schema={schema}
|
|
516
|
+
dataSource={dataSource}
|
|
517
|
+
datagridDataSource={datagridDataSource}
|
|
518
|
+
design={cleanedDesign}
|
|
519
|
+
filters={activeFilters}
|
|
520
|
+
onDesignChange={onDesignChange}
|
|
521
|
+
onRowClick={onRowClick}
|
|
522
|
+
onRowDoubleClick={onRowDoubleClick}
|
|
523
|
+
selectedRows={dataEditingEnabled && props.onRowsDelete != null ? selectedRows : undefined}
|
|
524
|
+
onSelectedRowsChange={dataEditingEnabled && props.onRowsDelete != null ? setSelectedRows : undefined}
|
|
525
|
+
canEditExpr={dataEditingEnabled ? canEditExpr : undefined}
|
|
526
|
+
updateExprValues={dataEditingEnabled ? updateExprValues : undefined}
|
|
527
|
+
refreshKey={refreshKey}
|
|
528
|
+
/>
|
|
529
|
+
)
|
|
530
|
+
} else if (onDesignChange) {
|
|
531
|
+
return (
|
|
532
|
+
<div style={{ textAlign: "center", marginTop: size.height / 2 }}>
|
|
533
|
+
<a className="btn btn-link" onClick={handleEdit}>{T`Click Here to Configure`}</a>
|
|
534
|
+
</div>
|
|
535
|
+
)
|
|
536
|
+
} else {
|
|
537
|
+
return null
|
|
538
|
+
}
|
|
539
|
+
}}
|
|
540
|
+
</AutoSizeComponent>
|
|
541
|
+
</div>
|
|
542
|
+
</LocaleContext.Provider>
|
|
543
|
+
)
|
|
544
|
+
})
|
|
545
|
+
|
|
546
|
+
interface DatagridEditorComponentProps {
|
|
547
|
+
schema: Schema
|
|
548
|
+
dataSource: DataSource
|
|
549
|
+
design: DatagridDesign
|
|
550
|
+
onDesignChange: (design: DatagridDesign) => void
|
|
551
|
+
onCancel: () => void
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/** Popup editor */
|
|
555
|
+
function DatagridEditorComponent(props: DatagridEditorComponentProps) {
|
|
556
|
+
const { schema, dataSource, design, onDesignChange, onCancel } = props
|
|
557
|
+
const [localDesign, setLocalDesign] = useState(design)
|
|
558
|
+
|
|
559
|
+
return (
|
|
560
|
+
<ActionCancelModalComponent
|
|
561
|
+
onAction={() => {
|
|
562
|
+
onDesignChange(localDesign)
|
|
563
|
+
setLocalDesign(design)
|
|
564
|
+
}}
|
|
565
|
+
onCancel={onCancel}
|
|
566
|
+
size="large"
|
|
567
|
+
>
|
|
568
|
+
<DatagridDesignerComponent
|
|
569
|
+
schema={schema}
|
|
570
|
+
dataSource={dataSource}
|
|
571
|
+
design={localDesign}
|
|
572
|
+
onDesignChange={(design: any) => setLocalDesign(design)}
|
|
573
|
+
/>
|
|
574
|
+
</ActionCancelModalComponent>
|
|
575
|
+
)
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
interface DeleteConfirmationModalProps {
|
|
579
|
+
/** Called when deletion is confirmed */
|
|
580
|
+
onConfirm: (signal: AbortSignal) => Promise<void>
|
|
581
|
+
/** Called when modal is cancelled */
|
|
582
|
+
onCancel: () => void
|
|
583
|
+
/** Number of rows to be deleted */
|
|
584
|
+
numRows?: number
|
|
585
|
+
/** True when loading number of rows */
|
|
586
|
+
loadingNumRows: boolean
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/** Modal that confirms deletion of rows */
|
|
590
|
+
function DeleteConfirmationModal(props: DeleteConfirmationModalProps) {
|
|
591
|
+
const { onConfirm, onCancel, numRows, loadingNumRows } = props
|
|
592
|
+
const [confirmed, setConfirmed] = useState(false)
|
|
593
|
+
const [deleting, setDeleting] = useState(false)
|
|
594
|
+
const abortControllerRef = useRef<AbortController>()
|
|
595
|
+
|
|
596
|
+
// If more than 1000 rows, show an error
|
|
597
|
+
const tooManyRows = numRows && numRows > 1000
|
|
598
|
+
|
|
599
|
+
const handleConfirm = async () => {
|
|
600
|
+
setDeleting(true)
|
|
601
|
+
abortControllerRef.current = new AbortController()
|
|
602
|
+
try {
|
|
603
|
+
await onConfirm(abortControllerRef.current.signal)
|
|
604
|
+
}
|
|
605
|
+
finally {
|
|
606
|
+
setDeleting(false)
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
const handleCancel = () => {
|
|
611
|
+
if (deleting) {
|
|
612
|
+
abortControllerRef.current?.abort()
|
|
613
|
+
}
|
|
614
|
+
onCancel()
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
return (
|
|
618
|
+
<ActionCancelModalComponent
|
|
619
|
+
title={T`Confirm Delete`}
|
|
620
|
+
onAction={handleConfirm}
|
|
621
|
+
onCancel={handleCancel}
|
|
622
|
+
actionLabel={T`Delete`}
|
|
623
|
+
size="normal"
|
|
624
|
+
actionBusy={deleting}
|
|
625
|
+
actionDisabled={!confirmed || loadingNumRows}
|
|
626
|
+
>
|
|
627
|
+
<div>
|
|
628
|
+
{loadingNumRows ? (
|
|
629
|
+
<div className="text-muted"><i className="fa fa-spinner fa-spin" /> {T`Loading rows to delete...`}</div>
|
|
630
|
+
) : (
|
|
631
|
+
<div>{T`This will permanently delete ${d3Format(",")(numRows || 0)} row(s).`}</div>
|
|
632
|
+
)}
|
|
633
|
+
{tooManyRows && (
|
|
634
|
+
<div className="alert alert-danger">{T`Cannot delete more than 1000 rows at a time.`}</div>
|
|
635
|
+
)}
|
|
636
|
+
{!tooManyRows && numRows && numRows > 0 && (
|
|
637
|
+
<div className="mt-3">
|
|
638
|
+
<Checkbox value={confirmed} onChange={setConfirmed}>
|
|
639
|
+
{T`I understand that this action cannot be undone`}
|
|
640
|
+
</Checkbox>
|
|
641
|
+
</div>
|
|
642
|
+
)}
|
|
643
|
+
</div>
|
|
644
|
+
</ActionCancelModalComponent>
|
|
645
|
+
)
|
|
646
|
+
}
|