@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.
Files changed (136) hide show
  1. package/.storybook/head.html +0 -1
  2. package/lib/MWaterContextComponent.js +1 -1
  3. package/lib/MWaterLoaderComponent.d.ts +2 -2
  4. package/lib/dashboards/DashboardComponent.js +2 -1
  5. package/lib/dashboards/LayoutOptionsComponent.js +18 -11
  6. package/lib/dashboards/ServerDashboardDataSource.d.ts +10 -1
  7. package/lib/dashboards/ServerDashboardDataSource.js +29 -0
  8. package/lib/dashboards/layoutOptions.d.ts +5 -1
  9. package/lib/datagrids/DatagridComponent.js +1 -1
  10. package/lib/datagrids/ExprCellComponent.d.ts +1 -0
  11. package/lib/datagrids/ExprCellComponent.js +22 -20
  12. package/lib/maps/BufferLayer.d.ts +18 -0
  13. package/lib/maps/BufferLayer.js +24 -14
  14. package/lib/maps/ChoroplethLayer.d.ts +18 -0
  15. package/lib/maps/ChoroplethLayer.js +34 -25
  16. package/lib/maps/ChoroplethLayerDesign.d.ts +3 -2
  17. package/lib/maps/ChoroplethLayerDesigner.d.ts +11 -1
  18. package/lib/maps/DirectMapDataSource.js +17 -0
  19. package/lib/maps/EditHoverOver.d.ts +1 -1
  20. package/lib/maps/EditHoverOver.js +62 -33
  21. package/lib/maps/HoverContent.d.ts +10 -5
  22. package/lib/maps/HoverContent.js +6 -35
  23. package/lib/maps/Layer.d.ts +37 -0
  24. package/lib/maps/Layer.js +30 -4
  25. package/lib/maps/MWaterServerLayer.d.ts +2 -2
  26. package/lib/maps/MWaterServerLayer.js +6 -6
  27. package/lib/maps/MapLayerDataSource.d.ts +9 -0
  28. package/lib/maps/MapUtils.d.ts +19 -1
  29. package/lib/maps/MapUtils.js +71 -1
  30. package/lib/maps/MarkersLayer.d.ts +18 -0
  31. package/lib/maps/MarkersLayer.js +24 -24
  32. package/lib/maps/MarkersLayerDesignerComponent.d.ts +14 -1
  33. package/lib/maps/RasterMapViewComponent.js +1 -1
  34. package/lib/maps/ServerMapDataSource.d.ts +9 -0
  35. package/lib/maps/ServerMapDataSource.js +29 -0
  36. package/lib/maps/VectorMapViewComponent.js +6 -6
  37. package/lib/maps/maps.d.ts +4 -2
  38. package/lib/mwater_table_selection/FormsListComponent.d.ts +33 -0
  39. package/lib/mwater_table_selection/FormsListComponent.js +141 -0
  40. package/lib/mwater_table_selection/IndicatorsListComponent.d.ts +47 -0
  41. package/lib/mwater_table_selection/IndicatorsListComponent.js +182 -0
  42. package/lib/mwater_table_selection/IssuesListComponent.d.ts +29 -0
  43. package/lib/mwater_table_selection/IssuesListComponent.js +123 -0
  44. package/lib/mwater_table_selection/MWaterAccountingSystemListComponent.d.ts +20 -0
  45. package/lib/mwater_table_selection/MWaterAccountingSystemListComponent.js +157 -0
  46. package/lib/mwater_table_selection/MWaterAssetSystemsListComponent.d.ts +17 -0
  47. package/lib/mwater_table_selection/MWaterAssetSystemsListComponent.js +79 -0
  48. package/lib/mwater_table_selection/MWaterCompleteTableSelectComponent.d.ts +37 -0
  49. package/lib/mwater_table_selection/MWaterCompleteTableSelectComponent.js +275 -0
  50. package/lib/mwater_table_selection/MWaterCustomTablesetListComponent.d.ts +17 -0
  51. package/lib/mwater_table_selection/MWaterCustomTablesetListComponent.js +94 -0
  52. package/lib/mwater_table_selection/MWaterMetricsTableListComponent.d.ts +17 -0
  53. package/lib/mwater_table_selection/MWaterMetricsTableListComponent.js +80 -0
  54. package/lib/mwater_table_selection/MWaterTableSelectComponent.d.ts +32 -0
  55. package/lib/mwater_table_selection/MWaterTableSelectComponent.js +158 -0
  56. package/lib/quickfilter/Quickfilter.d.ts +2 -0
  57. package/lib/quickfilter/QuickfiltersDesignComponent.js +18 -10
  58. package/lib/widgets/charts/Chart.d.ts +11 -0
  59. package/lib/widgets/charts/Chart.js +15 -0
  60. package/lib/widgets/charts/ChartWidgetComponent.d.ts +1 -0
  61. package/lib/widgets/charts/ChartWidgetComponent.js +27 -1
  62. package/lib/widgets/charts/layered/LayeredChartDesign.d.ts +1 -1
  63. package/lib/widgets/charts/layered/LayeredChartDesignerComponent.d.ts +1 -1
  64. package/lib/widgets/charts/layered/LayeredChartDesignerComponent.js +5 -12
  65. package/lib/widgets/charts/layered/LayeredChartLayerDesignerComponent.d.ts +43 -57
  66. package/lib/widgets/charts/layered/LayeredChartLayerDesignerComponent.js +113 -110
  67. package/lib/widgets/charts/layered/LayeredChartUtils.d.ts +2 -1
  68. package/lib/widgets/charts/layered/LayeredChartUtils.js +0 -2
  69. package/lib/widgets/charts/pivot/PivotChart.d.ts +2 -0
  70. package/lib/widgets/charts/pivot/PivotChart.js +156 -0
  71. package/lib/widgets/charts/pivot/PivotChartDesignerComponent.d.ts +5 -20
  72. package/lib/widgets/charts/pivot/PivotChartDesignerComponent.js +31 -61
  73. package/lib/widgets/charts/pivot/PivotChartLayoutBuilder.d.ts +4 -0
  74. package/lib/widgets/charts/pivot/PivotChartLayoutBuilder.js +4 -2
  75. package/lib/widgets/charts/pivot/PivotChartLayoutComponent.d.ts +5 -44
  76. package/lib/widgets/charts/pivot/PivotChartLayoutComponent.js +38 -63
  77. package/lib/widgets/charts/pivot/SegmentDesignerComponent.d.ts +7 -68
  78. package/lib/widgets/charts/pivot/SegmentDesignerComponent.js +58 -106
  79. package/lib/widgets/charts/table/TableChart.d.ts +2 -0
  80. package/lib/widgets/charts/table/TableChart.js +172 -1
  81. package/lib/widgets/charts/table/TableChartDesignerComponent.d.ts +7 -17
  82. package/lib/widgets/charts/table/TableChartDesignerComponent.js +79 -95
  83. package/lib/widgets/charts/table/TableChartViewComponent.d.ts +1 -7
  84. package/lib/widgets/charts/table/TableChartViewComponent.js +19 -27
  85. package/package.json +3 -8
  86. package/src/MWaterContextComponent.tsx +1 -1
  87. package/src/MWaterLoaderComponent.ts +1 -1
  88. package/src/dashboards/DashboardComponent.tsx +2 -1
  89. package/src/dashboards/LayoutOptionsComponent.tsx +22 -10
  90. package/src/dashboards/ServerDashboardDataSource.ts +36 -1
  91. package/src/dashboards/layoutOptions.tsx +5 -1
  92. package/src/datagrids/DatagridComponent.tsx +1 -1
  93. package/src/datagrids/ExprCellComponent.tsx +23 -20
  94. package/src/maps/BufferLayer.ts +35 -20
  95. package/src/maps/ChoroplethLayer.ts +51 -33
  96. package/src/maps/ChoroplethLayerDesign.ts +3 -2
  97. package/src/maps/ChoroplethLayerDesigner.tsx +2 -2
  98. package/src/maps/DirectMapDataSource.ts +21 -1
  99. package/src/maps/EditHoverOver.tsx +91 -51
  100. package/src/maps/HoverContent.tsx +16 -47
  101. package/src/maps/Layer.ts +42 -4
  102. package/src/maps/MWaterServerLayer.ts +6 -6
  103. package/src/maps/MapLayerDataSource.ts +8 -0
  104. package/src/maps/MapUtils.ts +70 -3
  105. package/src/maps/MarkersLayer.ts +34 -24
  106. package/src/maps/RasterMapViewComponent.ts +1 -1
  107. package/src/maps/ServerMapDataSource.ts +35 -0
  108. package/src/maps/VectorMapViewComponent.tsx +6 -6
  109. package/src/maps/maps.ts +4 -2
  110. package/src/mwater_table_selection/FormsListComponent.tsx +188 -0
  111. package/src/mwater_table_selection/IndicatorsListComponent.tsx +283 -0
  112. package/src/mwater_table_selection/IssuesListComponent.tsx +167 -0
  113. package/src/mwater_table_selection/MWaterAccountingSystemListComponent.tsx +225 -0
  114. package/src/{MWaterAssetSystemsListComponent.tsx → mwater_table_selection/MWaterAssetSystemsListComponent.tsx} +2 -2
  115. package/src/mwater_table_selection/MWaterCompleteTableSelectComponent.tsx +377 -0
  116. package/src/{MWaterCustomTablesetListComponent.tsx → mwater_table_selection/MWaterCustomTablesetListComponent.tsx} +1 -1
  117. package/src/{MWaterMetricsTableListComponent.tsx → mwater_table_selection/MWaterMetricsTableListComponent.tsx} +1 -1
  118. package/src/{MWaterTableSelectComponent.tsx → mwater_table_selection/MWaterTableSelectComponent.tsx} +83 -86
  119. package/src/quickfilter/Quickfilter.ts +3 -0
  120. package/src/quickfilter/QuickfiltersDesignComponent.tsx +19 -14
  121. package/src/widgets/charts/Chart.ts +17 -0
  122. package/src/widgets/charts/ChartWidgetComponent.tsx +36 -1
  123. package/src/widgets/charts/layered/LayeredChartDesign.ts +1 -1
  124. package/src/widgets/charts/layered/LayeredChartDesignerComponent.tsx +23 -24
  125. package/src/widgets/charts/layered/LayeredChartLayerDesignerComponent.tsx +260 -211
  126. package/src/widgets/charts/layered/LayeredChartUtils.ts +7 -7
  127. package/src/widgets/charts/pivot/PivotChart.ts +191 -0
  128. package/src/widgets/charts/pivot/PivotChartDesignerComponent.tsx +124 -129
  129. package/src/widgets/charts/pivot/PivotChartLayoutBuilder.ts +4 -2
  130. package/src/widgets/charts/pivot/PivotChartLayoutComponent.tsx +120 -149
  131. package/src/widgets/charts/pivot/SegmentDesignerComponent.tsx +178 -198
  132. package/src/widgets/charts/table/TableChart.ts +177 -1
  133. package/src/widgets/charts/table/TableChartDesignerComponent.tsx +422 -0
  134. package/src/widgets/charts/table/{TableChartViewComponent.ts → TableChartViewComponent.tsx} +65 -60
  135. package/src/MWaterCompleteTableSelectComponent.tsx +0 -975
  136. package/src/widgets/charts/table/TableChartDesignerComponent.ts +0 -441
@@ -39,51 +39,54 @@ export default class ExprCellComponent extends React.Component<ExprCellComponent
39
39
  return <a href={url} key={id} target="_blank" style={{ paddingLeft: 5, paddingRight: 5 }}>{T`Image`}</a>
40
40
  }
41
41
 
42
- render() {
43
- let node
44
- const exprUtils = new ExprUtils(this.props.schema)
42
+ renderCellContent() {
45
43
  let { value } = this.props
46
44
 
45
+ const exprUtils = new ExprUtils(this.props.schema)
46
+
47
47
  if (value == null || !this.props.expr) {
48
- node = null
48
+ return null
49
49
  } else {
50
50
  // Parse if should be JSON
51
51
  if (["image", "imagelist", "geometry", "text[]"].includes(this.props.exprType) && _.isString(value)) {
52
- value = JSON.parse(value)
52
+ try {
53
+ value = JSON.parse(value)
54
+ } catch (e) {
55
+ console.error("Error parsing JSON", e)
56
+ return "???"
57
+ }
53
58
  }
54
59
 
55
60
  // Format if possible
56
61
  if (canFormatType(this.props.exprType)) {
57
- node = formatValue(this.props.exprType, value, this.props.format)
62
+ return formatValue(this.props.exprType, value, this.props.format)
58
63
  } else {
59
64
  // Convert to node based on type
60
65
  switch (this.props.exprType) {
61
66
  case "text":
62
- node = <Linkify properties={{ target: "_blank" }}>{value}</Linkify>
63
- break
67
+ return <Linkify properties={{ target: "_blank" }}>{value}</Linkify>
64
68
  case "boolean":
65
69
  case "enum":
66
70
  case "enumset":
67
71
  case "text[]":
68
- node = exprUtils.stringifyExprLiteral(this.props.expr, value, this.props.locale)
69
- break
72
+ return exprUtils.stringifyExprLiteral(this.props.expr, value, this.props.locale)
70
73
  case "date":
71
- node = moment(value, "YYYY-MM-DD").format("ll")
72
- break
74
+ return moment(value, "YYYY-MM-DD").format("ll")
73
75
  case "datetime":
74
- node = moment(value, moment.ISO_8601).format("lll")
75
- break
76
+ return moment(value, moment.ISO_8601).format("lll")
76
77
  case "image":
77
- node = this.renderImage(value.id)
78
- break
78
+ return this.renderImage(value.id)
79
79
  case "imagelist":
80
- node = _.map(value, (v: any) => this.renderImage(v.id))
81
- break
80
+ return _.map(value, (v: any) => this.renderImage(v.id))
82
81
  default:
83
- node = "" + value
82
+ return "" + value
84
83
  }
85
84
  }
86
85
  }
86
+ }
87
+
88
+ render() {
89
+ const content = this.renderCellContent()
87
90
 
88
91
  return (
89
92
  <div
@@ -101,7 +104,7 @@ export default class ExprCellComponent extends React.Component<ExprCellComponent
101
104
  onClick={this.props.onClick}
102
105
  onDoubleClick={this.props.onDoubleClick}
103
106
  >
104
- {node}
107
+ {content}
105
108
  </div>
106
109
  )
107
110
  }
@@ -18,6 +18,7 @@ import { getDefaultLayoutOptions } from "../dashboards/layoutOptions"
18
18
  import Widget from "../widgets/Widget"
19
19
  import BlocksLayoutManager from "../layouts/blocks/BlocksLayoutManager"
20
20
  import { getTranslatableStringsFromLayoutManager } from "../dashboards/DashboardUtils"
21
+ import { getSimpleHoverOverData } from "./MapUtils"
21
22
 
22
23
  /** Layer which draws a buffer around geometries (i.e. a radius circle around points) */
23
24
  export default class BufferLayer extends Layer<BufferLayerDesign> {
@@ -696,29 +697,16 @@ marker-fill: ` +
696
697
  ev: { data: any; event: any },
697
698
  hoverOptions: OnGridHoverOptions<BufferLayerDesign>
698
699
  ): OnGridHoverResults {
699
- if (ev.data && ev.data.id) {
700
- const { table } = hoverOptions.design
701
- const results: OnGridHoverResults = {}
702
-
703
- // Popup
704
- if (hoverOptions.design.hoverOver) {
705
- // Create filter using popupFilterJoins
706
- const popupFilterJoins =
707
- hoverOptions.design.popupFilterJoins || PopupFilterJoinsUtils.createDefaultPopupFilterJoins(table)
708
- const popupFilters = PopupFilterJoinsUtils.createPopupFilters(
709
- popupFilterJoins,
710
- hoverOptions.schema,
711
- table,
712
- ev.data.id
713
- )
714
-
715
- results.hoverOver = React.createElement(HoverContent, {
700
+ if (ev.data && ev.data.id && hoverOptions.design.hoverOver && hoverOptions.design.hoverOver.items.length > 0) {
701
+ const results: OnGridHoverResults = {
702
+ hoverOver: React.createElement(HoverContent, {
716
703
  key: ev.data.id,
717
704
  schema: hoverOptions.schema,
718
- dataSource: hoverOptions.dataSource,
719
- table,
705
+ data: ev.data,
706
+ mapLayerDataSource: hoverOptions.layerDataSource,
707
+ design: hoverOptions.design,
720
708
  items: hoverOptions.design.hoverOver.items,
721
- filters: popupFilters,
709
+ filters: hoverOptions.filters,
722
710
  locale: hoverOptions.locale,
723
711
  translate: hoverOptions.translate
724
712
  })
@@ -950,4 +938,31 @@ marker-fill: ` +
950
938
  // Remove duplicates
951
939
  return _.uniq(strings)
952
940
  }
941
+
942
+
943
+ /** Gets hover over data for hover over items. This should be implemented by layers that have hover over items.
944
+ * It will be called on the server side if using a server map data source, or on the client side if using a direct
945
+ * map data source.
946
+ */
947
+ getHoverOverData(options: {
948
+ /** Design of the layer */
949
+ design: BufferLayerDesign,
950
+ /** Data of the current item being hovered over. e.g. { id: 123 } */
951
+ data: any,
952
+ /** Filters to apply to the hover over data, not including filtering down to the current item */
953
+ filters: JsonQLFilter[],
954
+ /** Schema to use */
955
+ schema: Schema,
956
+ /** Data source to use */
957
+ dataSource: DataSource,
958
+ }): Promise<{ [key: string]: any }> {
959
+ return getSimpleHoverOverData({
960
+ id: options.data.id,
961
+ table: options.design.table,
962
+ filters: options.filters,
963
+ schema: options.schema,
964
+ dataSource: options.dataSource,
965
+ hoverOverItems: options.design.hoverOver.items,
966
+ })
967
+ }
953
968
  }
@@ -29,6 +29,7 @@ import { getDefaultLayoutOptions } from "../dashboards/layoutOptions"
29
29
  import Widget from "../widgets/Widget"
30
30
  import BlocksLayoutManager from "../layouts/blocks/BlocksLayoutManager"
31
31
  import { getTranslatableStringsFromLayoutManager } from "../dashboards/DashboardUtils"
32
+ import { getSimpleHoverOverData } from "./MapUtils"
32
33
 
33
34
  export default class ChoroplethLayer extends Layer<ChoroplethLayerDesign> {
34
35
  /** Gets the type of layer definition */
@@ -1377,13 +1378,9 @@ export default class ChoroplethLayer extends Layer<ChoroplethLayerDesign> {
1377
1378
  ev: { data: any; event: any },
1378
1379
  hoverOptions: OnGridHoverOptions<ChoroplethLayerDesign>
1379
1380
  ): OnGridHoverResults {
1380
- const regionsTable = hoverOptions.design.regionsTable || "admin_regions"
1381
-
1382
- // Row only if mode is "plain" or "direct"
1383
- if (hoverOptions.design.regionMode == "plain" || hoverOptions.design.regionMode == "direct") {
1384
- if (!ev.data || !ev.data.id) {
1385
- return null
1386
- }
1381
+ // Only applicable to indirect mode
1382
+ if (hoverOptions.design.regionMode !== "indirect") {
1383
+ return null
1387
1384
  }
1388
1385
 
1389
1386
  // Ignore if indirect with no table
@@ -1391,35 +1388,16 @@ export default class ChoroplethLayer extends Layer<ChoroplethLayerDesign> {
1391
1388
  return null
1392
1389
  }
1393
1390
 
1394
- if (ev.data && ev.data.id) {
1395
- const { table } = hoverOptions.design
1396
- const results: OnGridHoverResults = {}
1397
-
1398
- // Popup
1399
- if (hoverOptions.design.hoverOver) {
1400
- // Create default popup filter joins
1401
- const defaultPopupFilterJoins: { [tableId: string]: Expr } = {}
1402
- if (hoverOptions.design.adminRegionExpr) {
1403
- defaultPopupFilterJoins[hoverOptions.design.table] = hoverOptions.design.adminRegionExpr
1404
- }
1405
-
1406
- // Create filter using popupFilterJoins
1407
- const popupFilterJoins = hoverOptions.design.popupFilterJoins || defaultPopupFilterJoins
1408
- const popupFilters = PopupFilterJoinsUtils.createPopupFilters(
1409
- popupFilterJoins,
1410
- hoverOptions.schema,
1411
- table,
1412
- ev.data.id,
1413
- true
1414
- )
1415
-
1416
- results.hoverOver = React.createElement(HoverContent, {
1391
+ if (ev.data && ev.data.id && hoverOptions.design.hoverOver && hoverOptions.design.hoverOver.items.length > 0) {
1392
+ const results: OnGridHoverResults = {
1393
+ hoverOver: React.createElement(HoverContent, {
1417
1394
  key: ev.data.id,
1418
1395
  schema: hoverOptions.schema,
1419
- dataSource: hoverOptions.dataSource,
1420
- table,
1396
+ mapLayerDataSource: hoverOptions.layerDataSource,
1397
+ design: hoverOptions.design,
1421
1398
  items: hoverOptions.design.hoverOver.items,
1422
- filters: popupFilters,
1399
+ data: ev.data,
1400
+ filters: hoverOptions.filters,
1423
1401
  locale: hoverOptions.locale,
1424
1402
  translate: hoverOptions.translate
1425
1403
  })
@@ -1731,4 +1709,44 @@ export default class ChoroplethLayer extends Layer<ChoroplethLayerDesign> {
1731
1709
  // Remove duplicates
1732
1710
  return _.uniq(strings)
1733
1711
  }
1712
+
1713
+ /** Gets hover over data for hover over items. This should be implemented by layers that have hover over items.
1714
+ * It will be called on the server side if using a server map data source, or on the client side if using a direct
1715
+ * map data source.
1716
+ */
1717
+ getHoverOverData(options: {
1718
+ /** Design of the layer */
1719
+ design: ChoroplethLayerDesign,
1720
+ /** Data of the current item being hovered over. e.g. { id: 123 } */
1721
+ data: any,
1722
+ /** Filters to apply to the hover over data, not including filtering down to the current item */
1723
+ filters: JsonQLFilter[],
1724
+ /** Schema to use */
1725
+ schema: Schema,
1726
+ /** Data source to use */
1727
+ dataSource: DataSource,
1728
+ }): Promise<{ [key: string]: any }> {
1729
+ // Use popup filter joins utilities to create filters
1730
+ const popupFilterJoins: { [tableId: string]: Expr } = {}
1731
+ if (options.design.adminRegionExpr) {
1732
+ popupFilterJoins[options.design.table!] = options.design.adminRegionExpr
1733
+ }
1734
+ const popupFilters = PopupFilterJoinsUtils.createPopupFilters(
1735
+ popupFilterJoins,
1736
+ options.schema,
1737
+ options.design.table!,
1738
+ options.data.id,
1739
+ true
1740
+ )
1741
+
1742
+ return getSimpleHoverOverData({
1743
+ // Filter using popup filters instead of id since will return multiple rows
1744
+ id: null,
1745
+ table: options.design.table!,
1746
+ filters: popupFilters,
1747
+ schema: options.schema,
1748
+ dataSource: options.dataSource,
1749
+ hoverOverItems: options.design.hoverOver.items,
1750
+ })
1751
+ }
1734
1752
  }
@@ -62,11 +62,12 @@ export default interface ChoroplethLayerDesign {
62
62
  /** contains items: which is BlocksLayoutManager items. Will be displayed when the region is clicked. Only when region mode is "indirect" */
63
63
  popup: { items: LayoutBlock }
64
64
 
65
- hoverOver: { items: HoverOverItem[] }
66
-
67
65
  /** customizable filtering for popup. See PopupFilterJoins.md. Only when region mode is "indirect" */
68
66
  popupFilterJoins: PopupFilterJoins
69
67
 
68
+ /** Hover over items to display when hovering over a region. Only when region mode is "indirect" */
69
+ hoverOver: { items: HoverOverItem[] }
70
+
70
71
  /** minimum zoom level */
71
72
  minZoom?: number
72
73
 
@@ -497,7 +497,7 @@ export default class ChoroplethLayerDesigner extends React.Component<{
497
497
 
498
498
  const regionsTable = this.props.design.regionsTable || "admin_regions"
499
499
 
500
- const defaultPopupFilterJoins = {}
500
+ const defaultPopupFilterJoins: { [tableId: string]: Expr } = {}
501
501
  if (this.props.design.adminRegionExpr) {
502
502
  defaultPopupFilterJoins[this.props.design.table] = this.props.design.adminRegionExpr
503
503
  }
@@ -521,7 +521,7 @@ export default class ChoroplethLayerDesigner extends React.Component<{
521
521
 
522
522
  const regionsTable = this.props.design.regionsTable || "admin_regions"
523
523
 
524
- const defaultPopupFilterJoins = {}
524
+ const defaultPopupFilterJoins: { [tableId: string]: Expr } = {}
525
525
  if (this.props.design.adminRegionExpr) {
526
526
  defaultPopupFilterJoins[this.props.design.table] = this.props.design.adminRegionExpr
527
527
  }
@@ -1,5 +1,5 @@
1
1
  import _ from "lodash"
2
- import { DataSource, Expr, injectTableAlias, Schema } from "@mwater/expressions"
2
+ import { DataSource, Expr, ExprCompiler, injectTableAlias, Schema } from "@mwater/expressions"
3
3
  import { JsonQLFilter } from "../JsonQLFilter"
4
4
  import BlocksLayoutManager from "../layouts/blocks/BlocksLayoutManager"
5
5
  import WidgetFactory from "../widgets/WidgetFactory"
@@ -16,6 +16,7 @@ import TileUrlLayer from "./TileUrlLayer"
16
16
  import * as QuickfilterUtils from "../quickfilter/QuickfilterUtils"
17
17
  import { useState, useEffect } from 'react'
18
18
  import { useStableCallback } from "@mwater/react-library/lib/useStableCallback"
19
+ import { JsonQLSelectQuery } from "@mwater/jsonql"
19
20
 
20
21
  export interface DirectMapDataSourceOptions {
21
22
  /** schema to use */
@@ -392,4 +393,23 @@ class DirectLayerDataSource implements MapLayerDataSource {
392
393
 
393
394
  return url
394
395
  }
396
+
397
+ /** Gets hover over data for hover over items
398
+ * @param design The design of the layer
399
+ * @param data The data of the current item being hovered over. e.g. { id: 123 }
400
+ * @param filters The filters to apply to the hover over data, not including filtering down to the current item
401
+ * @returns A promise that resolves to the hover over data, indexed by the id of the hover over item
402
+ */
403
+ getHoverOverData(design: any, data: any, filters: JsonQLFilter[]): Promise<{ [key: string]: any }> {
404
+ // Create layer
405
+ const layer = LayerFactory.createLayer(this.options.layerView.type)
406
+
407
+ return layer.getHoverOverData({
408
+ design,
409
+ data,
410
+ filters,
411
+ schema: this.options.schema,
412
+ dataSource: this.options.dataSource,
413
+ })
414
+ }
395
415
  }
@@ -1,11 +1,13 @@
1
1
  import { omit } from "lodash"
2
- import { DataSource, Expr, Schema } from "@mwater/expressions"
2
+ import { DataSource, Expr, ExprUtils, Schema } from "@mwater/expressions"
3
3
  import { ExprComponent } from "@mwater/expressions-ui"
4
4
  import React, { useState } from "react"
5
- import ModalWindowComponent from "@mwater/react-library/lib/ModalWindowComponent"
5
+ import ActionCancelModalComponent from "@mwater/react-library/lib/ActionCancelModalComponent"
6
6
  import * as ui from "@mwater/react-library/lib/bootstrap"
7
7
  import uuid from "uuid"
8
8
  import { HoverOverItem } from "./maps"
9
+ import { canFormatType, getDefaultFormat } from "../valueFormatter"
10
+ import { getFormatOptions } from "../valueFormatter"
9
11
 
10
12
  export interface EditHoverOverProps {
11
13
  /** Schema to use */
@@ -22,63 +24,82 @@ export interface EditHoverOverProps {
22
24
  defaultPopupFilterJoins: any
23
25
  }
24
26
 
25
- const EditHoverOver: React.FC<EditHoverOverProps> = props => {
27
+ function EditHoverOver(props: EditHoverOverProps) {
28
+ const { schema, dataSource, design, onDesignChange, table } = props
26
29
  const [editing, setEditing] = useState(false)
30
+ const [draftItems, setDraftItems] = useState<HoverOverItem[] | undefined>(undefined)
27
31
 
28
32
  const handleRemovePopup = () => {
29
- const design = omit(props.design, "hoverOver")
30
- props.onDesignChange(design)
33
+ const newDesign = omit(design, "hoverOver")
34
+ onDesignChange(newDesign)
31
35
  }
32
36
 
33
- const handleDesignChange = (items: HoverOverItem[]) => {
34
- const hoverOver = { ...(props.design.hoverOver ?? {}), items }
35
- const design = { ...props.design, hoverOver }
37
+ const handleItemChange = (item: HoverOverItem) => {
38
+ setDraftItems(draftItems?.map((i) => (item.id === i.id ? item : i)))
39
+ }
36
40
 
37
- return props.onDesignChange(design)
41
+ const handleItemDelete = (item: HoverOverItem) => {
42
+ setDraftItems(draftItems?.filter((i) => item.id !== i.id))
38
43
  }
39
44
 
40
- const handleItemChange = (item: HoverOverItem) => {
41
- const items = (props.design.hoverOver?.items ?? []).map((i: HoverOverItem) => (item.id === i.id ? item : i))
42
- const design = { ...props.design, hoverOver: { ...props.design.hoverOver, items } }
45
+ const handleAddItem = () => {
46
+ setDraftItems([...(draftItems ?? []), { id: uuid().replace(/-/g, ""), label: "" }])
47
+ }
43
48
 
44
- return props.onDesignChange(design)
49
+ const handleSave = () => {
50
+ const hoverOver = { ...(design.hoverOver ?? {}), items: draftItems ?? [] }
51
+ const newDesign = { ...design, hoverOver }
52
+ onDesignChange(newDesign)
53
+ setEditing(false)
54
+ setDraftItems(undefined)
45
55
  }
46
56
 
47
- const handleItemDelete = (item: HoverOverItem) => {
48
- const items = (props.design.hoverOver?.items ?? []).filter((i: HoverOverItem) => item.id !== i.id)
49
- const design = { ...props.design, hoverOver: { ...props.design.hoverOver, items } }
57
+ const handleCancel = () => {
58
+ setEditing(false)
59
+ setDraftItems(undefined)
60
+ }
50
61
 
51
- return props.onDesignChange(design)
62
+ const handleOpen = () => {
63
+ setDraftItems(design.hoverOver?.items ?? [])
64
+ setEditing(true)
52
65
  }
53
66
 
54
67
  return (
55
68
  <>
56
- <button className="btn btn-link" onClick={() => setEditing(true)}>
69
+ <button className="btn btn-link" onClick={handleOpen}>
57
70
  <span className="fa fa-pencil" /> {T`Customize Hoverover`}
58
71
  </button>
59
- {props.design.hoverOver && (
72
+ {design.hoverOver && (
60
73
  <button className="btn btn-link" onClick={handleRemovePopup}>
61
74
  <span className="fa fa-times" /> {T`Remove Hover over`}
62
75
  </button>
63
76
  )}
64
77
 
65
78
  {editing && (
66
- <ModalWindowComponent isOpen onRequestClose={() => setEditing(false)}>
67
- {(props.design.hoverOver?.items ?? []).length > 0 && (
79
+ <ActionCancelModalComponent
80
+ onAction={handleSave}
81
+ onCancel={handleCancel}
82
+ actionLabel={T`Save`}
83
+ title={T`Customize Hoverover`}
84
+ size="x-large"
85
+ >
86
+ {(draftItems ?? []).length > 0 && (
68
87
  <table className="table">
69
88
  <thead>
70
89
  <tr>
71
90
  <th>{T`Label`}</th>
72
91
  <th>{T`Value`}</th>
73
- <th></th>
92
+ <th style={{ width: "1%", whiteSpace: "nowrap" }}>{T`Format`}</th>
93
+ <th style={{ width: "1%", whiteSpace: "nowrap" }}></th>
74
94
  </tr>
75
95
  </thead>
76
96
  <tbody>
77
- {props.design.hoverOver?.items.map((item: HoverOverItem) => (
97
+ {draftItems?.map((item: HoverOverItem) => (
78
98
  <HoverOverItemEditor
79
- schema={props.schema}
80
- dataSource={props.dataSource}
81
- table={props.design.table}
99
+ key={item.id}
100
+ schema={schema}
101
+ dataSource={dataSource}
102
+ table={table}
82
103
  onItemChange={handleItemChange}
83
104
  onItemDelete={handleItemDelete}
84
105
  item={item}
@@ -88,20 +109,14 @@ const EditHoverOver: React.FC<EditHoverOverProps> = props => {
88
109
  </table>
89
110
  )}
90
111
 
91
- {(props.design.hoverOver?.items ?? []).length < 3 && (
92
- <button
93
- className="btn btn-link"
94
- onClick={() =>
95
- handleDesignChange([
96
- ...(props.design.hoverOver?.items ?? []),
97
- { id: uuid().replace(/-/g, ""), label: "" }
98
- ])
99
- }>
100
- <span className="fa fa-plus" />
101
- {T`Add item`}
102
- </button>
103
- )}
104
- </ModalWindowComponent>
112
+ <button
113
+ className="btn btn-link"
114
+ onClick={handleAddItem}
115
+ >
116
+ <span className="fas fa-plus me-1" />
117
+ {T`Add Item`}
118
+ </button>
119
+ </ActionCancelModalComponent>
105
120
  )}
106
121
  </>
107
122
  )
@@ -115,20 +130,42 @@ interface HoverOverItemEditorProps {
115
130
  onItemDelete: (item: HoverOverItem) => void
116
131
  table: string
117
132
  }
118
- const HoverOverItemEditor: React.FC<HoverOverItemEditorProps> = ({
119
- schema,
120
- dataSource,
121
- table,
122
- item,
123
- onItemChange,
124
- onItemDelete
125
- }) => {
133
+
134
+ function HoverOverItemEditor(props: HoverOverItemEditorProps) {
135
+ const { schema, dataSource, table, item, onItemChange, onItemDelete } = props
136
+
137
+ function renderFormat() {
138
+ const exprUtils = new ExprUtils(schema)
139
+ const exprType = exprUtils.getExprType(item.value ?? null)
140
+ if (!exprType) {
141
+ return null
142
+ }
143
+
144
+ if (!canFormatType(exprType)) {
145
+ return null
146
+ }
147
+
148
+ const formats = getFormatOptions(exprType)
149
+ if (!formats) {
150
+ return null
151
+ }
152
+
153
+ return (
154
+ <ui.Select
155
+ options={formats.map(f => ({ value: f.value, label: f.label }))}
156
+ value={item.format ?? getDefaultFormat(exprType)}
157
+ onChange={value => onItemChange({ ...item, format: value ?? undefined })}
158
+ style={{ width: "auto", display: "inline-block" }}
159
+ />
160
+ )
161
+ }
162
+
126
163
  return (
127
164
  <tr>
128
- <td>
165
+ <td className="align-middle">
129
166
  <ui.TextInput value={item.label} onChange={value => onItemChange({ ...item, label: value })} />
130
167
  </td>
131
- <td>
168
+ <td className="align-middle">
132
169
  <ExprComponent
133
170
  schema={schema}
134
171
  dataSource={dataSource}
@@ -139,7 +176,10 @@ const HoverOverItemEditor: React.FC<HoverOverItemEditorProps> = ({
139
176
  aggrStatuses={["individual", "literal", "aggregate"]}
140
177
  />
141
178
  </td>
142
- <td>
179
+ <td className="align-middle" style={{ width: "1%", whiteSpace: "nowrap" }}>
180
+ {renderFormat()}
181
+ </td>
182
+ <td className="align-middle" style={{ width: "1%", whiteSpace: "nowrap" }}>
143
183
  <button className="btn btn-link" onClick={() => onItemDelete(item)}>
144
184
  <span className="fa fa-close" />
145
185
  </button>
@@ -1,19 +1,22 @@
1
1
  import React, { useEffect, useState } from "react"
2
- import { DataSource, ExprCompiler, ExprUtils, Schema, injectTableAlias } from "@mwater/expressions"
3
- import { JsonQLSelectQuery } from "@mwater/jsonql"
2
+ import { ExprUtils, Schema } from "@mwater/expressions"
4
3
  import { JsonQLFilter } from ".."
5
- import { compact } from "lodash"
6
4
  import { HoverOverItem } from "./maps"
7
5
  import { canFormatType } from "../valueFormatter"
8
6
  import { formatValue } from "../valueFormatter"
7
+ import { MapLayerDataSource } from "./MapLayerDataSource"
9
8
 
10
9
  export interface HoverContentProps {
11
10
  /** Schema to use */
12
11
  schema: Schema
13
- dataSource: DataSource
14
- filters?: JsonQLFilter[]
15
- /** Table that hover over is for */
16
- table: string
12
+ /** Map data source */
13
+ mapLayerDataSource: MapLayerDataSource
14
+ /** Design of the layer */
15
+ design: any
16
+ /** Data of the current item being hovered over. e.g. { id: 123 } */
17
+ data: any
18
+ /** Additional filters to apply to the hover over data */
19
+ filters: JsonQLFilter[]
17
20
  /** Hover over items */
18
21
  items: HoverOverItem[]
19
22
  /** Locale to use */
@@ -33,48 +36,14 @@ const HoverContent = (props: HoverContentProps) => {
33
36
  const items = props.items
34
37
 
35
38
  if (items.length > 0) {
36
- const exprCompiler = new ExprCompiler(props.schema)
37
- const query: JsonQLSelectQuery = {
38
- type: "query",
39
- selects: [],
40
- from: exprCompiler.compileTable(props.table, "main"),
41
- limit: 1
42
- }
43
-
44
- items.forEach((item: HoverOverItem) => {
45
- if (item.value) {
46
- query.selects.push({
47
- type: "select",
48
- expr: exprCompiler.compileExpr({ expr: item.value, tableAlias: "main" }),
49
- alias: item.id
50
- })
39
+ props.mapLayerDataSource.getHoverOverData(props.design, props.data, props.filters).then((data) => {
40
+ if (mounted) {
41
+ setValues(data)
51
42
  }
52
- })
53
-
54
- if (props.filters) {
55
- let whereClauses = props.filters.map(f => injectTableAlias(f.jsonql, "main"))
56
-
57
- whereClauses = compact(whereClauses)
58
-
59
- // Wrap if multiple
60
- if (whereClauses.length > 1) {
61
- query.where = { type: "op", op: "and", exprs: whereClauses }
62
- } else {
63
- query.where = whereClauses[0]
43
+ }).catch((error) => {
44
+ if (mounted) {
45
+ setError(error.message)
64
46
  }
65
- }
66
-
67
- props.dataSource.performQuery(query, (error: any, data: any) => {
68
- if (!mounted) {
69
- return
70
- }
71
-
72
- if (error) {
73
- setError(props.translate("Error loading hover data"))
74
- return
75
- }
76
-
77
- setValues(data?.[0] ?? {})
78
47
  })
79
48
  }
80
49