@mwater/visualization 5.4.4 → 5.5.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.
- package/.storybook/head.html +0 -1
- package/lib/MWaterContextComponent.js +1 -1
- package/lib/MWaterLoaderComponent.d.ts +2 -2
- package/lib/dashboards/DashboardComponent.js +2 -1
- package/lib/dashboards/LayoutOptionsComponent.js +18 -11
- package/lib/dashboards/ServerDashboardDataSource.d.ts +10 -1
- package/lib/dashboards/ServerDashboardDataSource.js +29 -0
- package/lib/dashboards/layoutOptions.d.ts +5 -1
- package/lib/datagrids/DatagridComponent.js +1 -1
- package/lib/datagrids/ExprCellComponent.d.ts +1 -0
- package/lib/datagrids/ExprCellComponent.js +22 -20
- package/lib/maps/BufferLayer.d.ts +18 -0
- package/lib/maps/BufferLayer.js +24 -14
- package/lib/maps/ChoroplethLayer.d.ts +18 -0
- package/lib/maps/ChoroplethLayer.js +34 -25
- package/lib/maps/ChoroplethLayerDesign.d.ts +3 -2
- package/lib/maps/ChoroplethLayerDesigner.d.ts +11 -1
- package/lib/maps/DirectMapDataSource.js +17 -0
- package/lib/maps/EditHoverOver.d.ts +1 -1
- package/lib/maps/EditHoverOver.js +62 -33
- package/lib/maps/HoverContent.d.ts +10 -5
- package/lib/maps/HoverContent.js +6 -35
- package/lib/maps/Layer.d.ts +37 -0
- package/lib/maps/Layer.js +30 -4
- package/lib/maps/MWaterServerLayer.d.ts +2 -2
- package/lib/maps/MWaterServerLayer.js +6 -6
- package/lib/maps/MapLayerDataSource.d.ts +9 -0
- package/lib/maps/MapUtils.d.ts +19 -1
- package/lib/maps/MapUtils.js +71 -1
- package/lib/maps/MarkersLayer.d.ts +18 -0
- package/lib/maps/MarkersLayer.js +24 -24
- package/lib/maps/MarkersLayerDesignerComponent.d.ts +14 -1
- package/lib/maps/RasterMapViewComponent.js +1 -1
- package/lib/maps/ServerMapDataSource.d.ts +9 -0
- package/lib/maps/ServerMapDataSource.js +29 -0
- package/lib/maps/VectorMapViewComponent.js +6 -6
- package/lib/maps/maps.d.ts +4 -2
- package/lib/mwater_table_selection/FormsListComponent.d.ts +33 -0
- package/lib/mwater_table_selection/FormsListComponent.js +141 -0
- package/lib/mwater_table_selection/IndicatorsListComponent.d.ts +47 -0
- package/lib/mwater_table_selection/IndicatorsListComponent.js +182 -0
- package/lib/mwater_table_selection/IssuesListComponent.d.ts +29 -0
- package/lib/mwater_table_selection/IssuesListComponent.js +123 -0
- package/lib/mwater_table_selection/MWaterAccountingSystemListComponent.d.ts +20 -0
- package/lib/mwater_table_selection/MWaterAccountingSystemListComponent.js +157 -0
- package/lib/mwater_table_selection/MWaterAssetSystemsListComponent.d.ts +17 -0
- package/lib/mwater_table_selection/MWaterAssetSystemsListComponent.js +79 -0
- package/lib/mwater_table_selection/MWaterCompleteTableSelectComponent.d.ts +37 -0
- package/lib/mwater_table_selection/MWaterCompleteTableSelectComponent.js +275 -0
- package/lib/mwater_table_selection/MWaterCustomTablesetListComponent.d.ts +17 -0
- package/lib/mwater_table_selection/MWaterCustomTablesetListComponent.js +94 -0
- package/lib/mwater_table_selection/MWaterMetricsTableListComponent.d.ts +17 -0
- package/lib/mwater_table_selection/MWaterMetricsTableListComponent.js +80 -0
- package/lib/mwater_table_selection/MWaterTableSelectComponent.d.ts +32 -0
- package/lib/mwater_table_selection/MWaterTableSelectComponent.js +158 -0
- package/lib/quickfilter/Quickfilter.d.ts +2 -0
- package/lib/quickfilter/QuickfiltersDesignComponent.js +18 -10
- package/lib/widgets/charts/Chart.d.ts +11 -0
- package/lib/widgets/charts/Chart.js +15 -0
- package/lib/widgets/charts/ChartWidgetComponent.d.ts +1 -0
- package/lib/widgets/charts/ChartWidgetComponent.js +27 -1
- package/lib/widgets/charts/layered/LayeredChartDesign.d.ts +1 -1
- package/lib/widgets/charts/layered/LayeredChartDesignerComponent.d.ts +1 -1
- package/lib/widgets/charts/layered/LayeredChartDesignerComponent.js +5 -12
- package/lib/widgets/charts/layered/LayeredChartLayerDesignerComponent.d.ts +43 -57
- package/lib/widgets/charts/layered/LayeredChartLayerDesignerComponent.js +113 -110
- package/lib/widgets/charts/layered/LayeredChartUtils.d.ts +2 -1
- package/lib/widgets/charts/layered/LayeredChartUtils.js +0 -2
- package/lib/widgets/charts/pivot/PivotChart.d.ts +2 -0
- package/lib/widgets/charts/pivot/PivotChart.js +156 -0
- package/lib/widgets/charts/pivot/PivotChartDesignerComponent.d.ts +5 -20
- package/lib/widgets/charts/pivot/PivotChartDesignerComponent.js +31 -61
- package/lib/widgets/charts/pivot/PivotChartLayoutBuilder.d.ts +4 -0
- package/lib/widgets/charts/pivot/PivotChartLayoutBuilder.js +4 -2
- package/lib/widgets/charts/pivot/PivotChartLayoutComponent.d.ts +5 -44
- package/lib/widgets/charts/pivot/PivotChartLayoutComponent.js +38 -63
- package/lib/widgets/charts/pivot/SegmentDesignerComponent.d.ts +7 -68
- package/lib/widgets/charts/pivot/SegmentDesignerComponent.js +58 -106
- package/lib/widgets/charts/table/TableChart.d.ts +2 -0
- package/lib/widgets/charts/table/TableChart.js +172 -1
- package/lib/widgets/charts/table/TableChartDesignerComponent.d.ts +7 -17
- package/lib/widgets/charts/table/TableChartDesignerComponent.js +79 -95
- package/lib/widgets/charts/table/TableChartViewComponent.d.ts +1 -7
- package/lib/widgets/charts/table/TableChartViewComponent.js +19 -27
- package/package.json +3 -8
- package/src/MWaterContextComponent.tsx +1 -1
- package/src/MWaterLoaderComponent.ts +1 -1
- package/src/dashboards/DashboardComponent.tsx +2 -1
- package/src/dashboards/LayoutOptionsComponent.tsx +22 -10
- package/src/dashboards/ServerDashboardDataSource.ts +36 -1
- package/src/dashboards/layoutOptions.tsx +5 -1
- package/src/datagrids/DatagridComponent.tsx +1 -1
- package/src/datagrids/ExprCellComponent.tsx +23 -20
- package/src/maps/BufferLayer.ts +35 -20
- package/src/maps/ChoroplethLayer.ts +51 -33
- package/src/maps/ChoroplethLayerDesign.ts +3 -2
- package/src/maps/ChoroplethLayerDesigner.tsx +2 -2
- package/src/maps/DirectMapDataSource.ts +21 -1
- package/src/maps/EditHoverOver.tsx +91 -51
- package/src/maps/HoverContent.tsx +16 -47
- package/src/maps/Layer.ts +42 -4
- package/src/maps/MWaterServerLayer.ts +6 -6
- package/src/maps/MapLayerDataSource.ts +8 -0
- package/src/maps/MapUtils.ts +70 -3
- package/src/maps/MarkersLayer.ts +34 -24
- package/src/maps/RasterMapViewComponent.ts +1 -1
- package/src/maps/ServerMapDataSource.ts +35 -0
- package/src/maps/VectorMapViewComponent.tsx +6 -6
- package/src/maps/maps.ts +4 -2
- package/src/mwater_table_selection/FormsListComponent.tsx +188 -0
- package/src/mwater_table_selection/IndicatorsListComponent.tsx +283 -0
- package/src/mwater_table_selection/IssuesListComponent.tsx +167 -0
- package/src/mwater_table_selection/MWaterAccountingSystemListComponent.tsx +225 -0
- package/src/{MWaterAssetSystemsListComponent.tsx → mwater_table_selection/MWaterAssetSystemsListComponent.tsx} +2 -2
- package/src/mwater_table_selection/MWaterCompleteTableSelectComponent.tsx +377 -0
- package/src/{MWaterCustomTablesetListComponent.tsx → mwater_table_selection/MWaterCustomTablesetListComponent.tsx} +1 -1
- package/src/{MWaterMetricsTableListComponent.tsx → mwater_table_selection/MWaterMetricsTableListComponent.tsx} +1 -1
- package/src/{MWaterTableSelectComponent.tsx → mwater_table_selection/MWaterTableSelectComponent.tsx} +83 -86
- package/src/quickfilter/Quickfilter.ts +3 -0
- package/src/quickfilter/QuickfiltersDesignComponent.tsx +19 -14
- package/src/widgets/charts/Chart.ts +17 -0
- package/src/widgets/charts/ChartWidgetComponent.tsx +36 -1
- package/src/widgets/charts/layered/LayeredChartDesign.ts +1 -1
- package/src/widgets/charts/layered/LayeredChartDesignerComponent.tsx +23 -24
- package/src/widgets/charts/layered/LayeredChartLayerDesignerComponent.tsx +260 -211
- package/src/widgets/charts/layered/LayeredChartUtils.ts +7 -7
- package/src/widgets/charts/pivot/PivotChart.ts +191 -0
- package/src/widgets/charts/pivot/PivotChartDesignerComponent.tsx +124 -129
- package/src/widgets/charts/pivot/PivotChartLayoutBuilder.ts +4 -2
- package/src/widgets/charts/pivot/PivotChartLayoutComponent.tsx +120 -149
- package/src/widgets/charts/pivot/SegmentDesignerComponent.tsx +178 -198
- package/src/widgets/charts/table/TableChart.ts +177 -1
- package/src/widgets/charts/table/TableChartDesignerComponent.tsx +422 -0
- package/src/widgets/charts/table/{TableChartViewComponent.ts → TableChartViewComponent.tsx} +65 -60
- package/src/MWaterCompleteTableSelectComponent.tsx +0 -975
- package/src/widgets/charts/table/TableChartDesignerComponent.ts +0 -441
package/src/maps/Layer.ts
CHANGED
|
@@ -169,6 +169,25 @@ export default class Layer<LayerDesign> {
|
|
|
169
169
|
return null
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
+
/**
|
|
173
|
+
* Called when the interactivity grid is hovered over.
|
|
174
|
+
* arguments:
|
|
175
|
+
* ev: { data: interactivty data e.g. `{ id: 123 }` }
|
|
176
|
+
* options:
|
|
177
|
+
* design: design of layer
|
|
178
|
+
* schema: schema to use
|
|
179
|
+
* dataSource: data source to use
|
|
180
|
+
* layerDataSource: layer data source
|
|
181
|
+
* scopeData: current scope data if layer is scoping
|
|
182
|
+
* filters: compiled filters to apply to the popup
|
|
183
|
+
*
|
|
184
|
+
* Returns:
|
|
185
|
+
* null
|
|
186
|
+
* or
|
|
187
|
+
* {
|
|
188
|
+
* hoverOver: React element to put into a hover over
|
|
189
|
+
* }
|
|
190
|
+
*/
|
|
172
191
|
onGridHoverOver(ev: { data: any; event: any }, options: OnGridHoverOptions<LayerDesign>): OnGridHoverResults {
|
|
173
192
|
return null
|
|
174
193
|
}
|
|
@@ -316,10 +335,10 @@ export default class Layer<LayerDesign> {
|
|
|
316
335
|
const [w, s, e, n] = bbox(results[0].bounds)
|
|
317
336
|
// Pad bounds to prevent too small box (100m)
|
|
318
337
|
bounds = {
|
|
319
|
-
w: w - 0.001,
|
|
320
|
-
s: s - 0.001,
|
|
321
|
-
e: e + 0.001,
|
|
322
|
-
n: n + 0.001
|
|
338
|
+
w: Math.max(w - 0.001, -180),
|
|
339
|
+
s: Math.max(s - 0.001, -90),
|
|
340
|
+
e: Math.min(e + 0.001, 180),
|
|
341
|
+
n: Math.min(n + 0.001, 90)
|
|
323
342
|
}
|
|
324
343
|
}
|
|
325
344
|
|
|
@@ -332,6 +351,25 @@ export default class Layer<LayerDesign> {
|
|
|
332
351
|
getTranslatableStrings(design: LayerDesign, schema: Schema): string[] {
|
|
333
352
|
return []
|
|
334
353
|
}
|
|
354
|
+
|
|
355
|
+
/** Gets hover over data for hover over items. This should be implemented by layers that have hover over items.
|
|
356
|
+
* It will be called on the server side if using a server map data source, or on the client side if using a direct
|
|
357
|
+
* map data source.
|
|
358
|
+
*/
|
|
359
|
+
getHoverOverData(options: {
|
|
360
|
+
/** Design of the layer */
|
|
361
|
+
design: LayerDesign,
|
|
362
|
+
/** Data of the current item being hovered over. e.g. { id: 123 } */
|
|
363
|
+
data: any,
|
|
364
|
+
/** Filters to apply to the hover over data, not including filtering down to the current item */
|
|
365
|
+
filters: JsonQLFilter[],
|
|
366
|
+
/** Schema to use */
|
|
367
|
+
schema: Schema,
|
|
368
|
+
/** Data source to use */
|
|
369
|
+
dataSource: DataSource,
|
|
370
|
+
}): Promise<{ [key: string]: any }> {
|
|
371
|
+
return Promise.resolve({})
|
|
372
|
+
}
|
|
335
373
|
}
|
|
336
374
|
|
|
337
375
|
export interface LegendOptions<LayerDesign> {
|
|
@@ -94,15 +94,15 @@ class LoadingLegend extends React.Component<LoadingLegendProps, LoadingLegendSta
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
componentDidMount() {
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
$.get(this.props.url).done((data: any) => {
|
|
98
|
+
this.setState({ html: data })
|
|
99
99
|
})
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
if (
|
|
104
|
-
|
|
105
|
-
|
|
102
|
+
componentDidUpdate(prevProps: any) {
|
|
103
|
+
if (prevProps.url !== this.props.url) {
|
|
104
|
+
$.get(this.props.url).done((data: any) => {
|
|
105
|
+
this.setState({ html: data })
|
|
106
106
|
})
|
|
107
107
|
}
|
|
108
108
|
}
|
|
@@ -24,4 +24,12 @@ export interface MapLayerDataSource {
|
|
|
24
24
|
|
|
25
25
|
/** Gets widget data source for a popup widget */
|
|
26
26
|
getPopupWidgetDataSource(design: any, widgetId: string): WidgetDataSource
|
|
27
|
+
|
|
28
|
+
/** Gets hover over data for hover over items
|
|
29
|
+
* @param design The design of the layer
|
|
30
|
+
* @param data The data of the current item being hovered over. e.g. { id: 123 }
|
|
31
|
+
* @param filters The filters to apply to the layer does not include filters that narrow down to a specific item
|
|
32
|
+
* @returns A promise that resolves to the hover over data, indexed by the id of the hover over item
|
|
33
|
+
*/
|
|
34
|
+
getHoverOverData(design: any, data: any, filters: JsonQLFilter[]): Promise<{ [key: string]: any }>
|
|
27
35
|
}
|
package/src/maps/MapUtils.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
// General utilities for a map
|
|
2
2
|
|
|
3
|
-
import { JsonQLExpr } from "@mwater/jsonql"
|
|
4
|
-
import _ from "lodash"
|
|
5
|
-
import { Expr, ExprCleaner, ExprCompiler, ExprUtils, FieldExpr, Schema } from "@mwater/expressions"
|
|
3
|
+
import { JsonQLExpr, JsonQLSelectQuery } from "@mwater/jsonql"
|
|
4
|
+
import _, { compact } from "lodash"
|
|
5
|
+
import { DataSource, Expr, ExprCleaner, ExprCompiler, ExprUtils, FieldExpr, injectTableAlias, Schema } from "@mwater/expressions"
|
|
6
6
|
import { JsonQLFilter } from "../JsonQLFilter"
|
|
7
7
|
import LayerFactory from "./LayerFactory"
|
|
8
8
|
import { MapDesign } from "./MapDesign"
|
|
9
9
|
import { produce } from "immer"
|
|
10
|
+
import { HoverOverItem } from "./maps"
|
|
10
11
|
|
|
11
12
|
export interface MapScope {
|
|
12
13
|
name: string
|
|
@@ -170,3 +171,69 @@ export function getTranslatableStrings(design: MapDesign, schema: Schema): strin
|
|
|
170
171
|
// Remove duplicates
|
|
171
172
|
return _.uniq(strings)
|
|
172
173
|
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Convenience function to get hover over data for a map given an id and a list of hover over items
|
|
177
|
+
*/
|
|
178
|
+
export async function getSimpleHoverOverData(options: {
|
|
179
|
+
/** Id of the item to get hover over data for. If null, will not filter by id */
|
|
180
|
+
id: any,
|
|
181
|
+
/** Table of the item to get hover over data for */
|
|
182
|
+
table: string,
|
|
183
|
+
/** Extra filters to apply to the hover over data, not including filtering down to the current item */
|
|
184
|
+
filters: JsonQLFilter[],
|
|
185
|
+
/** Schema to use */
|
|
186
|
+
schema: Schema,
|
|
187
|
+
/** Data source to use */
|
|
188
|
+
dataSource: DataSource,
|
|
189
|
+
/** Hover over items */
|
|
190
|
+
hoverOverItems: HoverOverItem[]
|
|
191
|
+
}) {
|
|
192
|
+
const { id, table, filters, schema, dataSource, hoverOverItems } = options
|
|
193
|
+
|
|
194
|
+
const exprCompiler = new ExprCompiler(schema)
|
|
195
|
+
const query: JsonQLSelectQuery = {
|
|
196
|
+
type: "query",
|
|
197
|
+
selects: [],
|
|
198
|
+
from: exprCompiler.compileTable(table, "main"),
|
|
199
|
+
limit: 1
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
for (const item of hoverOverItems) {
|
|
203
|
+
if (item.value) {
|
|
204
|
+
query.selects.push({
|
|
205
|
+
type: "select",
|
|
206
|
+
expr: exprCompiler.compileExpr({ expr: item.value, tableAlias: "main" }),
|
|
207
|
+
alias: item.id
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (filters) {
|
|
213
|
+
let whereClauses = filters.filter(f => f.table === table).map(f => injectTableAlias(f.jsonql, "main"))
|
|
214
|
+
|
|
215
|
+
// Add id filter
|
|
216
|
+
if (id != null) {
|
|
217
|
+
whereClauses.push({
|
|
218
|
+
type: "op",
|
|
219
|
+
op: "=",
|
|
220
|
+
exprs: [
|
|
221
|
+
exprCompiler.compileExpr({ expr: { type: "id", table }, tableAlias: "main" }),
|
|
222
|
+
{ type: "literal", value: id }
|
|
223
|
+
]
|
|
224
|
+
})
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
whereClauses = compact(whereClauses)
|
|
228
|
+
|
|
229
|
+
// Wrap if multiple
|
|
230
|
+
if (whereClauses.length > 1) {
|
|
231
|
+
query.where = { type: "op", op: "and", exprs: whereClauses }
|
|
232
|
+
} else {
|
|
233
|
+
query.where = whereClauses[0]
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const rows = await dataSource.performQuery(query)
|
|
238
|
+
return rows?.[0] ?? {}
|
|
239
|
+
}
|
package/src/maps/MarkersLayer.ts
CHANGED
|
@@ -20,6 +20,7 @@ import Widget from "../widgets/Widget"
|
|
|
20
20
|
import { WidgetDataSource } from "../widgets/WidgetDataSource"
|
|
21
21
|
import BlocksLayoutManager from "../layouts/blocks/BlocksLayoutManager"
|
|
22
22
|
import { getTranslatableStringsFromLayoutManager } from "../dashboards/DashboardUtils"
|
|
23
|
+
import { getSimpleHoverOverData } from "./MapUtils"
|
|
23
24
|
|
|
24
25
|
export default class MarkersLayer extends Layer<MarkersLayerDesign> {
|
|
25
26
|
/** Gets the type of layer definition */
|
|
@@ -471,33 +472,16 @@ ${design.polygonBorderColor ? "line-color: " + design.polygonBorderColor + ";" :
|
|
|
471
472
|
ev: { data: any; event: any },
|
|
472
473
|
hoverOptions: OnGridHoverOptions<MarkersLayerDesign>
|
|
473
474
|
): OnGridHoverResults {
|
|
474
|
-
if (ev.data && ev.data.id) {
|
|
475
|
-
const
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
// Popup
|
|
479
|
-
if (hoverOptions.design.hoverOver) {
|
|
480
|
-
const exprCompiler = new ExprCompiler(hoverOptions.schema)
|
|
481
|
-
|
|
482
|
-
results.hoverOver = React.createElement(HoverContent, {
|
|
475
|
+
if (ev.data && ev.data.id && hoverOptions.design.hoverOver && hoverOptions.design.hoverOver.items.length > 0) {
|
|
476
|
+
const results: OnGridHoverResults = {
|
|
477
|
+
hoverOver: React.createElement(HoverContent, {
|
|
483
478
|
key: ev.data.id,
|
|
484
479
|
schema: hoverOptions.schema,
|
|
485
|
-
|
|
486
|
-
table,
|
|
480
|
+
mapLayerDataSource: hoverOptions.layerDataSource,
|
|
487
481
|
items: hoverOptions.design.hoverOver.items,
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
jsonql: {
|
|
492
|
-
type: "op",
|
|
493
|
-
op: "=",
|
|
494
|
-
exprs: [
|
|
495
|
-
exprCompiler.compileExpr({ expr: { type: "id", table }, tableAlias: "{alias}" }),
|
|
496
|
-
{ type: "literal", value: ev.data.id }
|
|
497
|
-
]
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
],
|
|
482
|
+
design: hoverOptions.design,
|
|
483
|
+
data: ev.data,
|
|
484
|
+
filters: hoverOptions.filters,
|
|
501
485
|
locale: hoverOptions.locale,
|
|
502
486
|
translate: hoverOptions.translate
|
|
503
487
|
})
|
|
@@ -818,6 +802,32 @@ ${design.polygonBorderColor ? "line-color: " + design.polygonBorderColor + ";" :
|
|
|
818
802
|
// Remove duplicates
|
|
819
803
|
return _.uniq(strings)
|
|
820
804
|
}
|
|
805
|
+
|
|
806
|
+
/** Gets hover over data for hover over items. This should be implemented by layers that have hover over items.
|
|
807
|
+
* It will be called on the server side if using a server map data source, or on the client side if using a direct
|
|
808
|
+
* map data source.
|
|
809
|
+
*/
|
|
810
|
+
getHoverOverData(options: {
|
|
811
|
+
/** Design of the layer */
|
|
812
|
+
design: MarkersLayerDesign,
|
|
813
|
+
/** Data of the current item being hovered over. e.g. { id: 123 } */
|
|
814
|
+
data: any,
|
|
815
|
+
/** Filters to apply to the hover over data, not including filtering down to the current item */
|
|
816
|
+
filters: JsonQLFilter[],
|
|
817
|
+
/** Schema to use */
|
|
818
|
+
schema: Schema,
|
|
819
|
+
/** Data source to use */
|
|
820
|
+
dataSource: DataSource,
|
|
821
|
+
}): Promise<{ [key: string]: any }> {
|
|
822
|
+
return getSimpleHoverOverData({
|
|
823
|
+
id: options.data.id,
|
|
824
|
+
table: options.design.table,
|
|
825
|
+
filters: options.filters,
|
|
826
|
+
schema: options.schema,
|
|
827
|
+
dataSource: options.dataSource,
|
|
828
|
+
hoverOverItems: options.design.hoverOver.items,
|
|
829
|
+
})
|
|
830
|
+
}
|
|
821
831
|
}
|
|
822
832
|
|
|
823
833
|
/**
|
|
@@ -178,7 +178,7 @@ export default class RasterMapViewComponent extends React.Component<
|
|
|
178
178
|
filters: this.getCompiledFilters(),
|
|
179
179
|
dataSource: this.props.dataSource,
|
|
180
180
|
locale: this.context,
|
|
181
|
-
translate: this.props.translate ?? (() =>
|
|
181
|
+
translate: this.props.translate ?? ((input: string) => input),
|
|
182
182
|
onHide: () => this.setState({ legendHidden: true }),
|
|
183
183
|
zoom: null
|
|
184
184
|
})
|
|
@@ -261,6 +261,41 @@ class ServerLayerDataSource implements MapLayerDataSource {
|
|
|
261
261
|
|
|
262
262
|
return url
|
|
263
263
|
}
|
|
264
|
+
|
|
265
|
+
/** Gets hover over data for hover over items
|
|
266
|
+
* @param design The design of the layer
|
|
267
|
+
* @param data The data of the current item being hovered over. e.g. { id: 123 }
|
|
268
|
+
* @param filters The filters to apply to the layer does not include filters that narrow down to a specific item
|
|
269
|
+
* @returns A promise that resolves to the hover over data, indexed by the id of the hover over item
|
|
270
|
+
*/
|
|
271
|
+
async getHoverOverData(design: any, data: any, filters: JsonQLFilter[]): Promise<{ [key: string]: any }> {
|
|
272
|
+
const query = {
|
|
273
|
+
client: this.options.client,
|
|
274
|
+
share: this.options.share,
|
|
275
|
+
filters: compressJson(filters || []),
|
|
276
|
+
data: compressJson(data),
|
|
277
|
+
rev: this.options.rev
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const url =
|
|
281
|
+
`${this.options.apiUrl}maps/${this.options.mapId}/layers/${this.options.layerView.id}/hoverdata?` +
|
|
282
|
+
querystring.stringify(query)
|
|
283
|
+
|
|
284
|
+
const response = await fetch(url, {
|
|
285
|
+
method: "GET",
|
|
286
|
+
headers: {
|
|
287
|
+
Accept: "application/json"
|
|
288
|
+
}
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
if (!response.ok) {
|
|
292
|
+
const errorText = await response.text()
|
|
293
|
+
console.error(errorText)
|
|
294
|
+
throw new Error(`Error fetching hover data: ${response.statusText}`)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return await response.json()
|
|
298
|
+
}
|
|
264
299
|
}
|
|
265
300
|
|
|
266
301
|
interface ServerMapLayerPopupWidgetDataSourceOptions {
|
|
@@ -76,7 +76,7 @@ export function VectorMapViewComponent(props: VectorMapViewComponentProps) {
|
|
|
76
76
|
// Locale to use
|
|
77
77
|
const locale = props.locale || props.design.locale || "en"
|
|
78
78
|
|
|
79
|
-
// Translate function to use
|
|
79
|
+
// Translate function to use
|
|
80
80
|
const translate = props.translate || ((input: string) => input)
|
|
81
81
|
|
|
82
82
|
// Last feature that mouse entered
|
|
@@ -505,13 +505,13 @@ export function VectorMapViewComponent(props: VectorMapViewComponentProps) {
|
|
|
505
505
|
}
|
|
506
506
|
|
|
507
507
|
if (!props.design.autoBounds && props.design.bounds) {
|
|
508
|
-
// If we set the new bounds, do not update map bounds
|
|
508
|
+
// If we set the new bounds, do not update map bounds unless they differ by more than 0.0001 degrees (roughly 10m)
|
|
509
509
|
if (
|
|
510
510
|
boundsRef.current == null ||
|
|
511
|
-
props.design.bounds.n
|
|
512
|
-
props.design.bounds.e
|
|
513
|
-
props.design.bounds.s
|
|
514
|
-
props.design.bounds.w
|
|
511
|
+
Math.abs(props.design.bounds.n - boundsRef.current.n) > 0.0001 ||
|
|
512
|
+
Math.abs(props.design.bounds.e - boundsRef.current.e) > 0.0001 ||
|
|
513
|
+
Math.abs(props.design.bounds.s - boundsRef.current.s) > 0.0001 ||
|
|
514
|
+
Math.abs(props.design.bounds.w - boundsRef.current.w) > 0.0001
|
|
515
515
|
) {
|
|
516
516
|
map.fitBounds([props.design.bounds.w, props.design.bounds.s, props.design.bounds.e, props.design.bounds.n])
|
|
517
517
|
boundsRef.current = props.design.bounds
|
package/src/maps/maps.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { JsonQLQuery } from "@mwater/jsonql"
|
|
2
2
|
import { Expr } from "@mwater/expressions"
|
|
3
|
+
import { ReactNode } from "react"
|
|
3
4
|
|
|
4
5
|
export interface LayerDefinition {
|
|
5
6
|
layers: Array<{
|
|
@@ -20,15 +21,16 @@ export interface LayerDefinition {
|
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
23
|
|
|
24
|
+
/** Results from onGridClick that can be used to display a popup */
|
|
23
25
|
export type OnGridClickResults = {
|
|
24
26
|
scope?: any
|
|
25
27
|
row?: { tableId: string; primaryKey: any }
|
|
26
28
|
popup?: React.ReactElement<{}>
|
|
27
29
|
} | null
|
|
28
30
|
|
|
31
|
+
/** Results from onGridHover that can be used to display a hover over */
|
|
29
32
|
export type OnGridHoverResults = {
|
|
30
|
-
|
|
31
|
-
hoverOver?: React.ReactElement<{}>
|
|
33
|
+
hoverOver?: ReactNode
|
|
32
34
|
} | null
|
|
33
35
|
|
|
34
36
|
/** Item in hover over */
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import _ from "lodash"
|
|
2
|
+
import $ from "jquery"
|
|
3
|
+
import React from "react"
|
|
4
|
+
import querystring from "querystring"
|
|
5
|
+
import * as uiComponents from "../UIComponents"
|
|
6
|
+
import { ExprUtils, Schema } from "@mwater/expressions"
|
|
7
|
+
import moment from "moment"
|
|
8
|
+
import { Form } from "@mwater/forms"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
interface FormsListComponentProps {
|
|
12
|
+
/** Url to hit api */
|
|
13
|
+
apiUrl: string
|
|
14
|
+
/** Optional client */
|
|
15
|
+
client?: string
|
|
16
|
+
schema: Schema
|
|
17
|
+
/** User id */
|
|
18
|
+
user?: string
|
|
19
|
+
/** Called with table selected */
|
|
20
|
+
onChange: any
|
|
21
|
+
extraTables: any
|
|
22
|
+
onExtraTableAdd: any
|
|
23
|
+
onExtraTableRemove: any
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface FormsListComponentState {
|
|
27
|
+
error?: any
|
|
28
|
+
search: any
|
|
29
|
+
forms: { id: string, name: string, desc?: string }[] | null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Searchable list of forms
|
|
33
|
+
export class FormsListComponent extends React.Component<FormsListComponentProps, FormsListComponentState> {
|
|
34
|
+
constructor(props: any) {
|
|
35
|
+
super(props)
|
|
36
|
+
this.state = {
|
|
37
|
+
forms: null,
|
|
38
|
+
search: ""
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
componentDidMount() {
|
|
43
|
+
// Get names and basic of forms
|
|
44
|
+
const query: any = {}
|
|
45
|
+
query.fields = JSON.stringify({
|
|
46
|
+
"design.name": 1,
|
|
47
|
+
"design.description": 1,
|
|
48
|
+
roles: 1,
|
|
49
|
+
created: 1,
|
|
50
|
+
modified: 1,
|
|
51
|
+
state: 1,
|
|
52
|
+
isMaster: 1
|
|
53
|
+
})
|
|
54
|
+
query.selector = JSON.stringify({ design: { $exists: true }, state: { $ne: "deleted" } })
|
|
55
|
+
query.client = this.props.client
|
|
56
|
+
|
|
57
|
+
// Get list of all form names
|
|
58
|
+
$.getJSON(this.props.apiUrl + "forms?" + querystring.stringify(query), (forms: Form[]) => {
|
|
59
|
+
// Sort by modified.on desc but first by user
|
|
60
|
+
forms = _.sortByOrder(
|
|
61
|
+
forms,
|
|
62
|
+
[
|
|
63
|
+
(form: Form) => ((this.props.extraTables || []).includes("responses:" + form._id) ? 1 : 0),
|
|
64
|
+
(form: Form) => (form.created.by === this.props.user ? 1 : 0),
|
|
65
|
+
(form: Form) => form.modified?.on
|
|
66
|
+
],
|
|
67
|
+
["desc", "desc", "desc"]
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
// TODO use name instead of design.name
|
|
71
|
+
this.setState({
|
|
72
|
+
forms: _.map(forms, (form) => {
|
|
73
|
+
let desc = ExprUtils.localizeString(form.design.description, null) || ""
|
|
74
|
+
if (desc) {
|
|
75
|
+
desc += " - "
|
|
76
|
+
}
|
|
77
|
+
desc += T`Modified ${moment(form.modified?.on, moment.ISO_8601).format("ll")}`
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
id: form._id,
|
|
81
|
+
name: ExprUtils.localizeString(form.design.name, null),
|
|
82
|
+
desc
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
})
|
|
86
|
+
}).fail((xhr: any) => {
|
|
87
|
+
this.setState({ error: xhr.responseText })
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
handleTableRemove = (table: any) => {
|
|
92
|
+
if (
|
|
93
|
+
confirm(
|
|
94
|
+
T`Remove ${ExprUtils.localizeString(
|
|
95
|
+
table.name,
|
|
96
|
+
T.locale
|
|
97
|
+
)}? Any widgets that depend on it will no longer work properly.`
|
|
98
|
+
)
|
|
99
|
+
) {
|
|
100
|
+
return this.props.onExtraTableRemove(table.id)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
searchRef = (comp: any) => {
|
|
105
|
+
// Focus
|
|
106
|
+
if (comp) {
|
|
107
|
+
return comp.focus()
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
render() {
|
|
112
|
+
let forms
|
|
113
|
+
if (this.state.error) {
|
|
114
|
+
return <div className="alert alert-danger">{this.state.error}</div>
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Filter forms
|
|
118
|
+
if (this.state.search) {
|
|
119
|
+
const escapeRegExp = (s: any) => s.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&")
|
|
120
|
+
|
|
121
|
+
const searchStringRegExp = new RegExp(escapeRegExp(this.state.search), "i")
|
|
122
|
+
|
|
123
|
+
forms = _.filter(this.state.forms!, (form) => form.name.match(searchStringRegExp))
|
|
124
|
+
} else {
|
|
125
|
+
;({ forms } = this.state)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Remove if already included
|
|
129
|
+
forms = _.filter(forms || [], (f) => !(this.props.extraTables || []).includes(`responses:${f.id}`))
|
|
130
|
+
|
|
131
|
+
let tables = _.filter(
|
|
132
|
+
this.props.schema.getTables(),
|
|
133
|
+
(table) => (table.id.match(/^responses:/) || table.id.match(/^master_responses:/)) && !table.deprecated
|
|
134
|
+
)
|
|
135
|
+
tables = _.sortBy(tables, (t) => t.name.en)
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
<div>
|
|
139
|
+
<label>{T`Included Surveys:`}</label>
|
|
140
|
+
{tables.length > 0 ? (
|
|
141
|
+
<uiComponents.OptionListComponent
|
|
142
|
+
items={_.map(tables, (table) => {
|
|
143
|
+
return {
|
|
144
|
+
name: ExprUtils.localizeString(table.name, T.locale),
|
|
145
|
+
desc: ExprUtils.localizeString(table.desc, T.locale),
|
|
146
|
+
onClick: this.props.onChange.bind(null, table.id),
|
|
147
|
+
onRemove: this.handleTableRemove.bind(null, table)
|
|
148
|
+
}
|
|
149
|
+
})}
|
|
150
|
+
/>
|
|
151
|
+
) : (
|
|
152
|
+
<div>{T`None`}</div>
|
|
153
|
+
)}
|
|
154
|
+
|
|
155
|
+
<br />
|
|
156
|
+
|
|
157
|
+
<label>{T`All Surveys:`}</label>
|
|
158
|
+
{!this.state.forms || this.state.forms.length === 0 ? (
|
|
159
|
+
<div className="alert alert-info">
|
|
160
|
+
<i className="fa fa-spinner fa-spin" />
|
|
161
|
+
{T`Loading...`}
|
|
162
|
+
</div>
|
|
163
|
+
) : (
|
|
164
|
+
<>
|
|
165
|
+
<input
|
|
166
|
+
type="text"
|
|
167
|
+
className="form-control form-control-sm"
|
|
168
|
+
placeholder={T`Search...`}
|
|
169
|
+
key="search"
|
|
170
|
+
ref={this.searchRef}
|
|
171
|
+
style={{ maxWidth: "20em", marginBottom: 10 }}
|
|
172
|
+
value={this.state.search}
|
|
173
|
+
onChange={(ev: any) => this.setState({ search: ev.target.value })}
|
|
174
|
+
/>
|
|
175
|
+
|
|
176
|
+
<uiComponents.OptionListComponent
|
|
177
|
+
items={_.map(forms, (form) => ({
|
|
178
|
+
name: form.name,
|
|
179
|
+
desc: form.desc,
|
|
180
|
+
onClick: this.props.onChange.bind(null, "responses:" + form.id)
|
|
181
|
+
}))}
|
|
182
|
+
/>
|
|
183
|
+
</>
|
|
184
|
+
)}
|
|
185
|
+
</div>
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
}
|