@mwater/visualization 5.0.1 → 5.2.0

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.
Files changed (172) hide show
  1. package/lib/GlobalFilter.d.ts +13 -0
  2. package/lib/GlobalFilter.js +2 -0
  3. package/lib/MWaterAddRelatedFormComponent.d.ts +1 -1
  4. package/lib/MWaterAddRelatedFormComponent.js +10 -17
  5. package/lib/MWaterCompleteTableSelectComponent.d.ts +2 -9
  6. package/lib/MWaterContextComponent.d.ts +31 -9
  7. package/lib/MWaterContextComponent.js +85 -76
  8. package/lib/MWaterCustomTablesetListComponent.js +9 -3
  9. package/lib/MWaterGlobalFiltersComponent.d.ts +6 -5
  10. package/lib/MWaterGlobalFiltersComponent.js +4 -4
  11. package/lib/MWaterLoaderComponent.d.ts +14 -4
  12. package/lib/MWaterLoaderComponent.js +10 -2
  13. package/lib/MWaterTableSelectComponent.d.ts +0 -1
  14. package/lib/MWaterTableSelectComponent.js +20 -41
  15. package/lib/axes/Axis.d.ts +20 -25
  16. package/lib/axes/AxisBuilder.js +9 -7
  17. package/lib/axes/AxisComponent.d.ts +4 -4
  18. package/lib/axes/RangesComponent.d.ts +12 -6
  19. package/lib/axes/RangesComponent.js +21 -10
  20. package/lib/dashboards/DashboardComponent.d.ts +1 -14
  21. package/lib/dashboards/DashboardComponent.js +18 -56
  22. package/lib/dashboards/DashboardDesign.d.ts +2 -17
  23. package/lib/dashboards/DashboardViewComponent.js +3 -4
  24. package/lib/dashboards/LayoutOptionsComponent.js +4 -3
  25. package/lib/dashboards/ServerDashboardDataSource.d.ts +1 -0
  26. package/lib/dashboards/ServerDashboardDataSource.js +3 -0
  27. package/lib/dashboards/SettingsModalComponent.d.ts +4 -15
  28. package/lib/dashboards/SettingsModalComponent.js +24 -38
  29. package/lib/datagrids/DatagridComponent.d.ts +10 -13
  30. package/lib/datagrids/DatagridComponent.js +27 -5
  31. package/lib/datagrids/DatagridDataSource.d.ts +3 -2
  32. package/lib/datagrids/DatagridDataSource.js +0 -11
  33. package/lib/datagrids/DatagridDesign.d.ts +2 -0
  34. package/lib/datagrids/DatagridDesignerComponent.d.ts +2 -93
  35. package/lib/datagrids/DatagridDesignerComponent.js +11 -8
  36. package/lib/datagrids/DatagridViewComponent.js +2 -2
  37. package/lib/datagrids/DirectDatagridDataSource.d.ts +1 -0
  38. package/lib/datagrids/DirectDatagridDataSource.js +26 -0
  39. package/lib/datagrids/FindReplaceModalComponent.d.ts +4 -20
  40. package/lib/datagrids/FindReplaceModalComponent.js +27 -13
  41. package/lib/datagrids/ServerDatagridDataSource.d.ts +2 -1
  42. package/lib/datagrids/ServerDatagridDataSource.js +16 -3
  43. package/lib/demo.js +1 -1
  44. package/lib/index.d.ts +1 -1
  45. package/lib/index.js +2 -4
  46. package/lib/layouts/blocks/BlocksDisplayComponent.d.ts +2 -1
  47. package/lib/layouts/blocks/BlocksDisplayComponent.js +2 -2
  48. package/lib/layouts/grid/GridLayoutManager.d.ts +2 -1
  49. package/lib/layouts/grid/LegoLayoutEngine.d.ts +1 -1
  50. package/lib/maps/BufferLayer.js +3 -1
  51. package/lib/maps/BufferLayerDesignerComponent.js +2 -2
  52. package/lib/maps/ChoroplethLayerDesigner.js +2 -2
  53. package/lib/maps/ClusterLayer.js +3 -1
  54. package/lib/maps/ClusterLayerDesignerComponent.js +2 -2
  55. package/lib/maps/DirectMapDataSource.js +1 -2
  56. package/lib/maps/GridLayer.js +5 -3
  57. package/lib/maps/GridLayerDesigner.js +2 -3
  58. package/lib/maps/LayerSwitcherComponent.js +1 -1
  59. package/lib/maps/MapComponent.d.ts +2 -7
  60. package/lib/maps/MapDesign.d.ts +2 -13
  61. package/lib/maps/MapDesignerComponent.d.ts +1 -12
  62. package/lib/maps/MapDesignerComponent.js +5 -12
  63. package/lib/maps/MapFiltersDesignerComponent.d.ts +0 -4
  64. package/lib/maps/MapFiltersDesignerComponent.js +4 -5
  65. package/lib/maps/MarkersLayerDesignerComponent.js +2 -2
  66. package/lib/maps/PopupFilterJoinsUtils.d.ts +6 -1
  67. package/lib/maps/PopupFilterJoinsUtils.js +4 -3
  68. package/lib/maps/RasterMapViewComponent.d.ts +2 -9
  69. package/lib/maps/RegionSelectComponent.d.ts +2 -1
  70. package/lib/maps/ServerMapDataSource.d.ts +1 -1
  71. package/lib/maps/UtfGridLayer.js +1 -1
  72. package/lib/maps/vectorMaps.d.ts +1 -0
  73. package/lib/maps/vectorMaps.js +10 -2
  74. package/lib/quickfilter/QuickfilterCompiler.d.ts +1 -1
  75. package/lib/widgets/IFrameWidgetComponent.d.ts +2 -9
  76. package/lib/widgets/ImageWidgetComponent.d.ts +6 -24
  77. package/lib/widgets/ImageWidgetComponent.js +2 -2
  78. package/lib/widgets/MapWidget.d.ts +2 -7
  79. package/lib/widgets/MarkdownWidget.d.ts +2 -7
  80. package/lib/widgets/TOCWidget.d.ts +2 -9
  81. package/lib/widgets/charts/ChartWidget.d.ts +3 -15
  82. package/lib/widgets/charts/calendar/CalendarChartDesignerComponent.js +2 -2
  83. package/lib/widgets/charts/imagemosaic/ImageMosaicChart.d.ts +1 -1
  84. package/lib/widgets/charts/imagemosaic/ImageMosaicChart.js +1 -1
  85. package/lib/widgets/charts/imagemosaic/ImageMosaicChartDesignerComponent.js +2 -2
  86. package/lib/widgets/charts/imagemosaic/ImagePopupComponent.d.ts +2 -7
  87. package/lib/widgets/charts/layered/LayeredChartDesignerComponent.d.ts +2 -31
  88. package/lib/widgets/charts/layered/LayeredChartLayerDesignerComponent.d.ts +2 -7
  89. package/lib/widgets/charts/layered/LayeredChartLayerDesignerComponent.js +2 -2
  90. package/lib/widgets/charts/pivot/IntersectionDesignerComponent.d.ts +73 -66
  91. package/lib/widgets/charts/pivot/PivotChartDesignerComponent.d.ts +10 -6
  92. package/lib/widgets/charts/pivot/PivotChartDesignerComponent.js +2 -2
  93. package/lib/widgets/charts/pivot/PivotChartViewComponent.d.ts +3 -22
  94. package/lib/widgets/charts/pivot/SegmentDesignerComponent.d.ts +55 -58
  95. package/lib/widgets/charts/table/TableChartDesignerComponent.js +2 -2
  96. package/lib/widgets/charts/table/TableChartViewComponent.js +21 -7
  97. package/lib/widgets/text/ExprInsertModalComponent.d.ts +2 -13
  98. package/lib/widgets/text/ExprItemEditorComponent.js +2 -2
  99. package/lib/widgets/text/ExprUpdateModalComponent.d.ts +2 -13
  100. package/lib/widgets/text/TextWidgetDesign.d.ts +3 -1
  101. package/package.json +1 -1
  102. package/src/GlobalFilter.ts +17 -0
  103. package/src/MWaterAddRelatedFormComponent.ts +15 -20
  104. package/src/MWaterContextComponent.tsx +158 -0
  105. package/src/MWaterCustomTablesetListComponent.tsx +21 -3
  106. package/src/MWaterGlobalFiltersComponent.ts +8 -8
  107. package/src/MWaterLoaderComponent.ts +10 -3
  108. package/src/{MWaterTableSelectComponent.ts → MWaterTableSelectComponent.tsx} +61 -66
  109. package/src/axes/Axis.ts +24 -25
  110. package/src/axes/AxisBuilder.ts +10 -9
  111. package/src/axes/RangesComponent.ts +27 -16
  112. package/src/dashboards/{DashboardComponent.ts → DashboardComponent.tsx} +39 -80
  113. package/src/dashboards/DashboardDesign.ts +2 -22
  114. package/src/dashboards/DashboardViewComponent.ts +4 -5
  115. package/src/dashboards/LayoutOptionsComponent.tsx +6 -4
  116. package/src/dashboards/ServerDashboardDataSource.ts +16 -12
  117. package/src/dashboards/SettingsModalComponent.tsx +170 -0
  118. package/src/datagrids/DatagridComponent.ts +45 -14
  119. package/src/datagrids/DatagridDataSource.ts +10 -8
  120. package/src/datagrids/DatagridDesign.ts +3 -0
  121. package/src/datagrids/DatagridDesignerComponent.tsx +31 -19
  122. package/src/datagrids/DatagridViewComponent.ts +4 -4
  123. package/src/datagrids/DirectDatagridDataSource.ts +35 -0
  124. package/src/datagrids/ExprCellComponent.ts +0 -1
  125. package/src/datagrids/FindReplaceModalComponent.ts +39 -22
  126. package/src/datagrids/ServerDatagridDataSource.ts +23 -6
  127. package/src/demo.ts +1 -1
  128. package/src/index.ts +1 -2
  129. package/src/layouts/blocks/BlocksDisplayComponent.ts +2 -2
  130. package/src/layouts/grid/LegoLayoutEngine.ts +2 -2
  131. package/src/layouts/grid/WidgetContainerComponent.ts +2 -2
  132. package/src/maps/BingLayer.ts +2 -2
  133. package/src/maps/BufferLayer.ts +3 -1
  134. package/src/maps/BufferLayerDesignerComponent.ts +1 -1
  135. package/src/maps/ChoroplethLayerDesigner.tsx +1 -1
  136. package/src/maps/ClusterLayer.ts +3 -1
  137. package/src/maps/ClusterLayerDesignerComponent.ts +1 -1
  138. package/src/maps/DirectMapDataSource.ts +1 -2
  139. package/src/maps/GridLayer.ts +5 -3
  140. package/src/maps/GridLayerDesigner.tsx +1 -2
  141. package/src/maps/LayerSwitcherComponent.tsx +1 -1
  142. package/src/maps/LegendGroup.ts +1 -1
  143. package/src/maps/MWaterServerLayer.ts +2 -2
  144. package/src/maps/MapDesign.ts +2 -17
  145. package/src/maps/{MapDesignerComponent.ts → MapDesignerComponent.tsx} +8 -16
  146. package/src/maps/{MapFiltersDesignerComponent.ts → MapFiltersDesignerComponent.tsx} +25 -25
  147. package/src/maps/MarkersLayerDesignerComponent.ts +1 -1
  148. package/src/maps/PopupFilterJoinsUtils.ts +4 -4
  149. package/src/maps/ServerMapDataSource.ts +7 -7
  150. package/src/maps/SwitchableTileUrlLayerDesigner.tsx +1 -13
  151. package/src/maps/UtfGridLayer.ts +4 -4
  152. package/src/maps/VectorMapViewComponent.tsx +0 -1
  153. package/src/maps/mapboxUtils.ts +2 -2
  154. package/src/maps/vectorMaps.tsx +10 -1
  155. package/src/quickfilter/QuickfilterCompiler.ts +1 -1
  156. package/src/richtext/ExprItemsHtmlConverter.ts +1 -1
  157. package/src/richtext/FontColorPaletteItem.ts +1 -1
  158. package/src/richtext/FontSizePaletteItem.ts +1 -1
  159. package/src/richtext/ItemsHtmlConverter.ts +2 -2
  160. package/src/widgets/ImageWidgetComponent.ts +1 -1
  161. package/src/widgets/charts/calendar/CalendarChartDesignerComponent.ts +1 -1
  162. package/src/widgets/charts/imagemosaic/ImageMosaicChart.ts +1 -1
  163. package/src/widgets/charts/imagemosaic/ImageMosaicChartDesignerComponent.ts +1 -1
  164. package/src/widgets/charts/layered/LayeredChartLayerDesignerComponent.tsx +1 -1
  165. package/src/widgets/charts/pivot/PivotChartDesignerComponent.tsx +1 -1
  166. package/src/widgets/charts/table/TableChartDesignerComponent.ts +1 -1
  167. package/src/widgets/charts/table/TableChartViewComponent.ts +21 -7
  168. package/src/widgets/text/ExprItemEditorComponent.tsx +1 -1
  169. package/src/widgets/text/TextWidgetDesign.ts +4 -1
  170. package/src/MWaterContextComponent.ts +0 -141
  171. package/src/TableSelectComponent.ts +0 -60
  172. package/src/dashboards/SettingsModalComponent.ts +0 -169
@@ -1,7 +1,7 @@
1
- import { JsonQLFilter } from "../JsonQLFilter"
2
1
  import { Quickfilter } from "../quickfilter/Quickfilter"
3
- import { LiteralType, Expr } from "@mwater/expressions"
2
+ import { Expr } from "@mwater/expressions"
4
3
  import { BlocksLayoutOptions, DashboardTheme } from "./layoutOptions"
4
+ import { GlobalFilter } from "../GlobalFilter"
5
5
 
6
6
  /** Dashboard design
7
7
  * Each understands enough of the dashboard design to create widgets.
@@ -35,23 +35,3 @@ export interface DashboardDesign {
35
35
  /** array of global filters. See below. */
36
36
  globalFilters?: GlobalFilter[]
37
37
  }
38
-
39
- /** Global Filters:
40
-
41
- Global filters apply to multiple tables at once if a certain column is present. User-interface to set them is application-specific
42
- and the default (non-mWater) dashboard applies them but does not allow editing.
43
-
44
- */
45
- export interface GlobalFilter {
46
- /** id of column to filter */
47
- columnId: string
48
-
49
- /** type of column to filter (to ensure that consistent) */
50
- columnType: LiteralType
51
-
52
- /** op of expression for filtering */
53
- op: string
54
-
55
- /** array of expressions to use for filtering. field expression for column will be injected as expression 0 in the resulting filter expression */
56
- exprs: Expr[]
57
- }
@@ -14,7 +14,7 @@ import WidgetScopesViewComponent from "../widgets/WidgetScopesViewComponent"
14
14
  import { getLayoutOptions } from "./layoutOptions"
15
15
  import { WidgetComponent } from "./WidgetComponent"
16
16
  import { DashboardDataSource, DashboardDesign, JsonQLFilter } from ".."
17
- import { getMapTilerApiKey, setMapTilerApiKey } from "../maps/vectorMaps"
17
+ import { setPrintingModeEnabled } from "../maps/vectorMaps"
18
18
 
19
19
  export interface DashboardViewComponentProps {
20
20
  /** schema to use */
@@ -147,10 +147,9 @@ export default class DashboardViewComponent extends React.Component<
147
147
 
148
148
  // Call to print the dashboard
149
149
  print = async () => {
150
- // Temporarily disable vector maps as WebGL is not supported in print mode
151
- const mapTilerAPIKey = getMapTilerApiKey()
150
+ // Temporarily enable print mode for vector maps
152
151
  try {
153
- setMapTilerApiKey("")
152
+ setPrintingModeEnabled(true)
154
153
 
155
154
  // Create element at 1080 wide (use as standard printing width)
156
155
  const elem = R(
@@ -162,7 +161,7 @@ export default class DashboardViewComponent extends React.Component<
162
161
  const printer = new ReactElementPrinter()
163
162
  await printer.print(elem, { delay: 5000 })
164
163
  } finally {
165
- setMapTilerApiKey(mapTilerAPIKey)
164
+ setPrintingModeEnabled(false)
166
165
  }
167
166
  }
168
167
 
@@ -137,7 +137,7 @@ export function LayoutOptionsComponent(props: {
137
137
  <div style={{ height: "100%", display: "grid", gridTemplateRows: "auto 1fr" }}>
138
138
  <div key="quickfilters">
139
139
  {layoutOptions.hideQuickfiltersWidth == null ||
140
- sizeOptions[previewSize].value.width > layoutOptions.hideQuickfiltersWidth
140
+ sizeOptions[previewSize].value.width > layoutOptions.hideQuickfiltersWidth
141
141
  ? props.quickfiltersView
142
142
  : null}
143
143
  </div>
@@ -196,9 +196,11 @@ function ThemeToggle(props: { theme?: DashboardTheme; onChange: (theme: Dashboar
196
196
 
197
197
  return (
198
198
  <FormGroup label="Theme">
199
- {renderStyleItem("default")}
200
- {renderStyleItem("greybg")}
201
- {renderStyleItem("story")}
199
+ <div className="list-group">
200
+ {renderStyleItem("default")}
201
+ {renderStyleItem("greybg")}
202
+ {renderStyleItem("story")}
203
+ </div>
202
204
  </FormGroup>
203
205
  )
204
206
  }
@@ -97,7 +97,7 @@ class ServerQuickfilterDataSource implements QuickfiltersDataSource {
97
97
  `dashboards/${this.options.dashboardId}/quickfilters/${index}/values?` +
98
98
  querystring.stringify(query)
99
99
 
100
- const headers = {}
100
+ const headers: any = {}
101
101
  const cacheExpiry = this.options.dataSource.getCacheExpiry()
102
102
  if (cacheExpiry) {
103
103
  const seconds = Math.floor((new Date().getTime() - cacheExpiry) / 1000)
@@ -110,10 +110,10 @@ class ServerQuickfilterDataSource implements QuickfiltersDataSource {
110
110
  url,
111
111
  headers
112
112
  })
113
- .done((data) => {
113
+ .done((data: any) => {
114
114
  return callback(null, data)
115
115
  })
116
- .fail((xhr) => {
116
+ .fail((xhr: any) => {
117
117
  console.log(xhr.responseText)
118
118
  return callback(new Error(xhr.responseText))
119
119
  })
@@ -151,7 +151,7 @@ class ServerWidgetDataSource {
151
151
  `dashboards/${this.options.dashboardId}/widgets/${this.options.widgetId}/data?` +
152
152
  querystring.stringify(query)
153
153
 
154
- const headers = {}
154
+ const headers: any = {}
155
155
  const cacheExpiry = this.options.dataSource.getCacheExpiry()
156
156
  if (cacheExpiry) {
157
157
  const seconds = Math.floor((new Date().getTime() - cacheExpiry) / 1000)
@@ -164,10 +164,10 @@ class ServerWidgetDataSource {
164
164
  url,
165
165
  headers
166
166
  })
167
- .done((data) => {
167
+ .done((data: any) => {
168
168
  return callback(null, data)
169
169
  })
170
- .fail((xhr) => {
170
+ .fail((xhr: any) => {
171
171
  console.log(xhr.responseText)
172
172
  return callback(new Error(xhr.responseText))
173
173
  })
@@ -235,7 +235,7 @@ class ServerWidgetMapDataSource implements MapDataSource {
235
235
  `dashboards/${this.options.dashboardId}/widgets/${this.options.widgetId}/bounds?` +
236
236
  querystring.stringify(query)
237
237
 
238
- const headers = {}
238
+ const headers: any = {}
239
239
  const cacheExpiry = this.options.dataSource.getCacheExpiry()
240
240
  if (cacheExpiry) {
241
241
  const seconds = Math.floor((new Date().getTime() - cacheExpiry) / 1000)
@@ -248,14 +248,18 @@ class ServerWidgetMapDataSource implements MapDataSource {
248
248
  url,
249
249
  headers
250
250
  })
251
- .done((data) => {
251
+ .done((data: any) => {
252
252
  return callback(null, data)
253
253
  })
254
- .fail((xhr) => {
254
+ .fail((xhr: any) => {
255
255
  console.log(xhr.responseText)
256
256
  return callback(new Error(xhr.responseText))
257
257
  })
258
258
  }
259
+
260
+ getQuickfiltersDataSource(): QuickfiltersDataSource {
261
+ return new ServerQuickfilterDataSource(this.options)
262
+ }
259
263
  }
260
264
 
261
265
  interface ServerWidgetLayerDataSourceOptions extends ServerDashboardDataSourceOptions {
@@ -480,7 +484,7 @@ class ServerWidgetLayerPopupWidgetDataSource implements WidgetDataSource {
480
484
  `dashboards/${this.options.dashboardId}/widgets/${this.options.widgetId}/layers/${this.options.layerView.id}/widgets/${this.options.popupWidgetId}/data?` +
481
485
  querystring.stringify(query)
482
486
 
483
- const headers = {}
487
+ const headers: any = {}
484
488
  const cacheExpiry = this.options.dataSource.getCacheExpiry()
485
489
  if (cacheExpiry) {
486
490
  const seconds = Math.floor((new Date().getTime() - cacheExpiry) / 1000)
@@ -493,10 +497,10 @@ class ServerWidgetLayerPopupWidgetDataSource implements WidgetDataSource {
493
497
  url,
494
498
  headers
495
499
  })
496
- .done((data) => {
500
+ .done((data: any) => {
497
501
  return callback(null, data)
498
502
  })
499
- .fail((xhr) => {
503
+ .fail((xhr: any) => {
500
504
  console.log(xhr.responseText)
501
505
  return callback(new Error(xhr.responseText))
502
506
  })
@@ -0,0 +1,170 @@
1
+ import _ from "lodash"
2
+ import React from "react"
3
+ const R = React.createElement
4
+ import update from "update-object"
5
+ import { languages } from "../languages"
6
+ import * as ui from "@mwater/react-library/lib/bootstrap"
7
+ import { default as ReactSelect } from "react-select"
8
+ import * as DashboardUtils from "./DashboardUtils"
9
+ import ActionCancelModalComponent from "@mwater/react-library/lib/ActionCancelModalComponent"
10
+ import QuickfiltersDesignComponent from "../quickfilter/QuickfiltersDesignComponent"
11
+ import FiltersDesignerComponent from "../FiltersDesignerComponent"
12
+ import { DataSource, Schema } from "@mwater/expressions"
13
+ import { DashboardDesign } from "./DashboardDesign"
14
+ import { GlobalFiltersElementFactoryContext } from "../MWaterContextComponent"
15
+
16
+ export interface SettingsModalComponentProps {
17
+ onDesignChange: any
18
+ schema: Schema
19
+ dataSource: DataSource
20
+ }
21
+
22
+ interface SettingsModalComponentState {
23
+ design: DashboardDesign | null
24
+ }
25
+
26
+ // Popup with settings for dashboard
27
+ export default class SettingsModalComponent extends React.Component<
28
+ SettingsModalComponentProps,
29
+ SettingsModalComponentState
30
+ > {
31
+ constructor(props: SettingsModalComponentProps) {
32
+ super(props)
33
+ this.state = {
34
+ design: null // Set when being edited
35
+ }
36
+ }
37
+
38
+ show(design: any) {
39
+ return this.setState({ design })
40
+ }
41
+
42
+ handleSave = () => {
43
+ this.props.onDesignChange(this.state.design)
44
+ return this.setState({ design: null })
45
+ }
46
+
47
+ handleCancel = () => {
48
+ return this.setState({ design: null })
49
+ }
50
+ handleDesignChange = (design: any) => {
51
+ return this.setState({ design })
52
+ }
53
+
54
+ handleFiltersChange = (filters: any) => {
55
+ const design = _.extend({}, this.state.design, { filters })
56
+ return this.handleDesignChange(design)
57
+ }
58
+
59
+ handleGlobalFiltersChange = (globalFilters: any) => {
60
+ const design = _.extend({}, this.state.design, { globalFilters })
61
+ return this.handleDesignChange(design)
62
+ }
63
+
64
+ render() {
65
+ // Don't show if not editing
66
+ if (!this.state.design) {
67
+ return null
68
+ }
69
+
70
+ // Get filterable tables
71
+ const filterableTables = DashboardUtils.getFilterableTables(this.state.design, this.props.schema)
72
+
73
+ const localeOptions = _.map(languages, (language) => {
74
+ return {
75
+ value: language.code,
76
+ label: language.en + " (" + language.name + ")"
77
+ }
78
+ })
79
+
80
+ return (
81
+ <ActionCancelModalComponent
82
+ size="large"
83
+ onCancel={this.handleCancel}
84
+ onAction={this.handleSave}
85
+ >
86
+ <div style={{ paddingBottom: 200 }}>
87
+ <h4>Quick Filters</h4>
88
+ <div className="text-muted">
89
+ Quick filters are shown to the user as a dropdown at the top of the dashboard and can be used to filter data of widgets.
90
+ </div>
91
+
92
+ {filterableTables.length > 0 ? (
93
+ <QuickfiltersDesignComponent
94
+ design={this.state.design.quickfilters || []}
95
+ onDesignChange={(design: any) =>
96
+ this.handleDesignChange(update(this.state.design, { quickfilters: { $set: design } }))
97
+ }
98
+ schema={this.props.schema}
99
+ dataSource={this.props.dataSource}
100
+ tables={filterableTables}
101
+ />
102
+ ) : (
103
+ "Nothing to quickfilter. Add widgets to the dashboard"
104
+ )}
105
+
106
+ <h4 style={{ paddingTop: 10 }}>Filters</h4>
107
+ <div className="text-muted">
108
+ Filters are built in to the dashboard and cannot be changed by viewers of the dashboard.
109
+ </div>
110
+
111
+ {filterableTables.length > 0 ? (
112
+ <FiltersDesignerComponent
113
+ schema={this.props.schema}
114
+ dataSource={this.props.dataSource}
115
+ filters={this.state.design.filters}
116
+ onFiltersChange={this.handleFiltersChange}
117
+ filterableTables={filterableTables}
118
+ />
119
+ ) : (
120
+ "Nothing to filter. Add widgets to the dashboard"
121
+ )}
122
+
123
+ <GlobalFiltersElementFactoryContext.Consumer>
124
+ {globalFiltersElementFactory =>
125
+ globalFiltersElementFactory ? (
126
+ <div>
127
+ <h4 style={{ paddingTop: 10 }}>Global Filters</h4>
128
+ {globalFiltersElementFactory({
129
+ schema: this.props.schema,
130
+ dataSource: this.props.dataSource,
131
+ filterableTables,
132
+ globalFilters: this.state.design!.globalFilters || [],
133
+ onChange: this.handleGlobalFiltersChange
134
+ })}
135
+ </div>
136
+ ) : undefined
137
+ }
138
+ </GlobalFiltersElementFactoryContext.Consumer>
139
+
140
+ <h4 style={{ paddingTop: 10 }}>Language</h4>
141
+ <div className="text-muted">
142
+ Controls the preferred language of widgets and uses specified language when available
143
+ </div>
144
+
145
+ <ReactSelect
146
+ value={_.findWhere(localeOptions, { value: this.state.design.locale || "en" }) || null}
147
+ options={localeOptions}
148
+ onChange={(locale: any) =>
149
+ this.handleDesignChange(update(this.state.design, { locale: { $set: locale.value } }))
150
+ }
151
+ />
152
+
153
+ {this.state.design.implicitFiltersEnabled && (
154
+ <>
155
+ <h4 style={{ paddingTop: 10 }}>Advanced</h4>
156
+ <ui.Checkbox
157
+ value={this.state.design.implicitFiltersEnabled != null ? this.state.design.implicitFiltersEnabled : true}
158
+ onChange={(value: boolean) =>
159
+ this.handleDesignChange(update(this.state.design, { implicitFiltersEnabled: { $set: value } }))
160
+ }
161
+ >
162
+ Enable Implicit Filtering (leave unchecked for new dashboards)
163
+ </ui.Checkbox>
164
+ </>
165
+ )}
166
+ </div>
167
+ </ActionCancelModalComponent>
168
+ )
169
+ }
170
+ }
@@ -16,6 +16,7 @@ import QuickfilterCompiler from "../quickfilter/QuickfilterCompiler"
16
16
  import FindReplaceModalComponent from "./FindReplaceModalComponent"
17
17
  import DatagridDataSource from "./DatagridDataSource"
18
18
  import { DatagridDesign, JsonQLFilter } from ".."
19
+ import { format as d3Format } from "d3-format"
19
20
 
20
21
  export interface DatagridComponentProps {
21
22
  /** schema to use */
@@ -55,21 +56,25 @@ export interface DatagridComponentProps {
55
56
  filters?: JsonQLFilter[]
56
57
  }
57
58
 
59
+ interface DatagridComponentState {
60
+ /** is design being edited */
61
+ editingDesign: boolean
62
+ /** True if cells can be edited directly */
63
+ cellEditingEnabled: boolean
64
+ /** Height of quickfilters */
65
+ quickfiltersHeight: number | null
66
+ quickfiltersValues: null | any[]
67
+ refreshKey: number
68
+ /** Number of rows */
69
+ numRows?: number
70
+ }
71
+
58
72
  // Datagrid with decorations
59
73
  // See README.md for description of datagrid format
60
74
  // Design should be cleaned already before being passed in (see DatagridUtils)
61
75
  export default class DatagridComponent extends React.Component<
62
76
  DatagridComponentProps,
63
- {
64
- /** is design being edited */
65
- editingDesign: boolean
66
- /** True if cells can be edited directly */
67
- cellEditingEnabled: boolean
68
- /** Height of quickfilters */
69
- quickfiltersHeight: number | null
70
- quickfiltersValues: null | any[]
71
- refreshKey: number
72
- }
77
+ DatagridComponentState
73
78
  > {
74
79
  datagridView?: DatagridViewComponent | null
75
80
  quickfilters?: HTMLElement | null
@@ -88,15 +93,40 @@ export default class DatagridComponent extends React.Component<
88
93
  }
89
94
 
90
95
  reload() {
91
- return this.datagridView?.reload()
96
+ this.datagridView?.reload()
92
97
  }
93
98
 
94
99
  componentDidMount() {
95
- return this.updateHeight()
100
+ this.loadRowCount()
101
+ this.updateHeight()
96
102
  }
97
103
 
98
- componentDidUpdate() {
99
- return this.updateHeight()
104
+ componentDidUpdate(prevProps: DatagridComponentProps, prevState: DatagridComponentState) {
105
+ if (!_.isEqual(prevProps.design, this.props.design) || !_.isEqual(prevState.quickfiltersValues, this.state.quickfiltersValues) || prevState.refreshKey !== this.state.refreshKey) {
106
+ this.loadRowCount()
107
+ }
108
+ this.updateHeight()
109
+ }
110
+
111
+ loadRowCount() {
112
+ if(!this.props.design.showNumRows) {
113
+ return
114
+ }
115
+ let filters = this.props.filters || []
116
+
117
+ // Compile quickfilters
118
+ filters = filters.concat(this.getQuickfilterFilters())
119
+ this.props.datagridDataSource.countRows(this.props.design,
120
+ filters,
121
+ (error: any, numRows: any) => {
122
+ if (error) {
123
+ console.error(error)
124
+ alert(T("Error loading data"))
125
+ return
126
+ }
127
+ console.log(numRows)
128
+ this.setState({numRows})
129
+ })
100
130
  }
101
131
 
102
132
  handleRefreshData = () => {
@@ -265,6 +295,7 @@ export default class DatagridComponent extends React.Component<
265
295
  R(
266
296
  "div",
267
297
  { style: { float: "right" } },
298
+ this.props.design.showNumRows && this.state.numRows ? R("small", {className: 'text-muted text-sm'}, `${d3Format(',')(this.state.numRows)} rows`): undefined,
268
299
  this.renderFindReplace(),
269
300
  this.renderCellEdit(),
270
301
  this.renderEditButton(),
@@ -2,20 +2,22 @@ import { Row } from "@mwater/expressions"
2
2
  import { DatagridDesign, JsonQLFilter } from ".."
3
3
  import { QuickfiltersDataSource } from "../quickfilter/QuickfiltersDataSource"
4
4
 
5
- export default class DatagridDataSource {
5
+ export default interface DatagridDataSource {
6
6
  /** Gets the rows specified */
7
7
  getRows(
8
8
  design: DatagridDesign,
9
9
  offset: number,
10
10
  limit: number,
11
11
  filters: JsonQLFilter[] | undefined,
12
- callback: (error: any, rows: Row[]) => void
13
- ): void {
14
- throw new Error("Not implemented")
15
- }
12
+ callback: (error: any, rows?: Row[]) => void
13
+ ): void
14
+
15
+ countRows(
16
+ design: DatagridDesign,
17
+ filters: JsonQLFilter[] | undefined,
18
+ callback: (error: any, numRows?: number) => void
19
+ ): void
16
20
 
17
21
  /** Gets the quickfilters data source */
18
- getQuickfiltersDataSource(): QuickfiltersDataSource {
19
- throw new Error("Not implemented")
20
- }
22
+ getQuickfiltersDataSource(): QuickfiltersDataSource
21
23
  }
@@ -29,6 +29,9 @@ export interface DatagridDesign {
29
29
 
30
30
  /** array of global filters. See below. */
31
31
  globalFilters?: DatagridDesignGlobalFilter[]
32
+
33
+ /** true to show number of rows */
34
+ showNumRows?: boolean
32
35
  }
33
36
 
34
37
  export interface DatagridDesignColumn {
@@ -1,4 +1,3 @@
1
- import PropTypes from "prop-types"
2
1
  import _ from "lodash"
3
2
  import React, { useMemo } from "react"
4
3
  const R = React.createElement
@@ -14,7 +13,7 @@ import OrderBysDesignerComponent from "./OrderBysDesignerComponent"
14
13
  import ReorderableListComponent from "@mwater/react-library/lib/reorderable/ReorderableListComponent"
15
14
  import QuickfiltersDesignComponent from "../quickfilter/QuickfiltersDesignComponent"
16
15
  import LabeledExprGenerator from "./LabeledExprGenerator"
17
- import TableSelectComponent from "../TableSelectComponent"
16
+ import { TableSelectComponent } from "@mwater/expressions-ui"
18
17
  import uuid from "uuid"
19
18
  import update from "update-object"
20
19
  import * as ui from "@mwater/react-library/lib/bootstrap"
@@ -22,6 +21,7 @@ import { getFormatOptions } from "../valueFormatter"
22
21
  import { getDefaultFormat } from "../valueFormatter"
23
22
  import { DatagridDesignColumn } from "./DatagridDesign"
24
23
  import { DatagridDesign } from ".."
24
+ import { GlobalFiltersElementFactoryContext } from "../MWaterContextComponent"
25
25
 
26
26
  export interface DatagridDesignerComponentProps {
27
27
  /** schema to use */
@@ -36,8 +36,6 @@ export interface DatagridDesignerComponentProps {
36
36
 
37
37
  // Designer for the datagrid. Currenly allows only single-table designs (no subtable rows)
38
38
  export default class DatagridDesignerComponent extends React.Component<DatagridDesignerComponentProps> {
39
- static contextTypes = { globalFiltersElementFactory: PropTypes.func }
40
-
41
39
  handleTableChange = (table: any) => {
42
40
  const design = {
43
41
  table,
@@ -93,20 +91,24 @@ export default class DatagridDesignerComponent extends React.Component<DatagridD
93
91
  value: this.props.design.filter,
94
92
  onChange: this.handleFilterChange
95
93
  }),
96
- this.context.globalFiltersElementFactory
97
- ? R(
98
- "div",
99
- { style: { marginTop: 20 } },
100
- this.context.globalFiltersElementFactory({
101
- schema: this.props.schema,
102
- dataSource: this.props.dataSource,
103
- filterableTables: [this.props.design.table],
104
- globalFilters: this.props.design.globalFilters,
105
- onChange: this.handleGlobalFiltersChange,
106
- nullIfIrrelevant: true
107
- })
108
- )
109
- : undefined
94
+ <GlobalFiltersElementFactoryContext.Consumer>
95
+ {(globalFiltersElementFactory) =>
96
+ globalFiltersElementFactory
97
+ ? R(
98
+ "div",
99
+ { style: { marginTop: 20 } },
100
+ globalFiltersElementFactory({
101
+ schema: this.props.schema,
102
+ dataSource: this.props.dataSource,
103
+ filterableTables: [this.props.design.table!],
104
+ globalFilters: this.props.design.globalFilters,
105
+ onChange: this.handleGlobalFiltersChange,
106
+ nullIfIrrelevant: true
107
+ })
108
+ )
109
+ : undefined
110
+ }
111
+ </GlobalFiltersElementFactoryContext.Consumer>
110
112
  )
111
113
  },
112
114
  {
@@ -196,6 +198,14 @@ function DatagridOptionsComponent(props: DatagridOptionsComponentProps) {
196
198
  >
197
199
  {T("Show row numbers")}
198
200
  </ui.Checkbox>
201
+ <ui.Checkbox
202
+ value={props.design.showNumRows}
203
+ onChange={(showNumRows) =>
204
+ props.onDesignChange({ ...props.design, showNumRows })
205
+ }
206
+ >
207
+ {T("Show number of rows")}
208
+ </ui.Checkbox>
199
209
  <ui.FormGroup label={T("Language")} hint={T("Preferred language of the datagrid")}>
200
210
  <ReactSelect
201
211
  value={localeOptions.find(opt => opt.value == (props.design.locale || "en")) || null}
@@ -554,7 +564,9 @@ class ColumnDesignerComponent extends React.Component<ColumnDesignerComponentPro
554
564
  }),
555
565
  this.renderSplit(),
556
566
  this.renderFormat(),
557
- error ? R("i", { className: "fa fa-exclamation-circle text-danger" }) : undefined
567
+ error ? <span className="text-danger">
568
+ <i className="fa fa-exclamation-circle" /> {error}
569
+ </span> : undefined
558
570
  ),
559
571
 
560
572
  R(
@@ -95,7 +95,7 @@ export default class DatagridViewComponent extends React.Component<
95
95
  // If design or filters changed, delete all rows
96
96
  // TODO won't this reload on column resize?
97
97
  if (!_.isEqual(nextProps.design, this.props.design) || !_.isEqual(nextProps.filters, this.props.filters) || nextProps.refreshKey !== this.props.refreshKey) {
98
- return this.setState({ rows: [], entirelyLoaded: false })
98
+ this.setState({ rows: [], entirelyLoaded: false })
99
99
  }
100
100
  }
101
101
 
@@ -191,8 +191,8 @@ export default class DatagridViewComponent extends React.Component<
191
191
  }
192
192
 
193
193
  const newRows = produce(this.state.rows, draft => {
194
- if (rows[0]) {
195
- draft[rowIndex] = rows[0]
194
+ if (rows![0]) {
195
+ draft[rowIndex] = rows![0]
196
196
  }
197
197
  else {
198
198
  // If row missing, remove it from list
@@ -308,7 +308,7 @@ export default class DatagridViewComponent extends React.Component<
308
308
  }
309
309
 
310
310
  handleRowDoubleClick = (ev: any, rowIndex: any) => {
311
- if (this.props.onRowDoubleClick != null && this.state.rows[rowIndex].id) {
311
+ if (this.props.onRowDoubleClick != null && this.state.rows[rowIndex]?.id) {
312
312
  this.props.onRowDoubleClick(this.props.design.table!, this.state.rows[rowIndex].id, rowIndex)
313
313
  }
314
314
  }
@@ -4,6 +4,8 @@ import * as QuickfilterUtils from "../quickfilter/QuickfilterUtils"
4
4
  import { DataSource, Row, Schema } from "@mwater/expressions"
5
5
  import { DatagridDesign } from "./DatagridDesign"
6
6
  import { JsonQLFilter } from "../JsonQLFilter"
7
+ import _ from "lodash"
8
+ import { JsonQLSelectQuery } from "@mwater/jsonql"
7
9
 
8
10
  /** Uses direct DataSource queries */
9
11
  export default class DirectDatagridDataSource implements DatagridDataSource {
@@ -37,6 +39,39 @@ export default class DirectDatagridDataSource implements DatagridDataSource {
37
39
  return this.options.dataSource.performQuery(query, callback)
38
40
  }
39
41
 
42
+ countRows(
43
+ design: DatagridDesign,
44
+ filters: JsonQLFilter[] | undefined,
45
+ callback: (error: any, numRows: number) => void
46
+ ): void {
47
+ const queryBuilder = new DatagridQueryBuilder(this.options.schema)
48
+
49
+ // Create query to get the page of rows at the specific offset
50
+ const query = queryBuilder.createQuery(design, {
51
+ extraFilters: filters
52
+ })
53
+
54
+ const countQuery: JsonQLSelectQuery = {
55
+ ..._.omit(query, 'orderBy'),
56
+ selects: [
57
+ {
58
+ type: 'select',
59
+ expr: {
60
+ type: 'op',
61
+ op: 'count',
62
+ exprs: []
63
+ },
64
+ alias: 'cnt'
65
+ }
66
+ ]
67
+ };
68
+
69
+ console.log(countQuery)
70
+ return this.options.dataSource.performQuery(countQuery, (error, rows) => {
71
+ callback(error, rows?.[0]?.cnt)
72
+ })
73
+ }
74
+
40
75
  // Gets the quickfilters data source
41
76
  getQuickfiltersDataSource() {
42
77
  return {
@@ -1,4 +1,3 @@
1
- import PropTypes from "prop-types"
2
1
  import _ from "lodash"
3
2
  import React from "react"
4
3
  const R = React.createElement