@mwater/visualization 5.6.0 → 5.6.1

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 (92) hide show
  1. package/lib/ColorComponent.js +2 -2
  2. package/lib/TranslationsTabComponent.d.ts +34 -0
  3. package/lib/TranslationsTabComponent.js +256 -0
  4. package/lib/dashboards/DashboardComponent.js +1 -1
  5. package/lib/dashboards/ServerDashboardDataSource.d.ts +0 -1
  6. package/lib/dashboards/ServerDashboardDataSource.js +0 -15
  7. package/lib/dashboards/SettingsModalComponent.js +9 -233
  8. package/lib/datagrids/DatagridComponent.js +5 -0
  9. package/lib/datagrids/DatagridViewComponent.js +30 -4
  10. package/lib/maps/BufferLayer.d.ts +0 -13
  11. package/lib/maps/BufferLayer.js +12 -237
  12. package/lib/maps/BufferLayerDesignerComponent.d.ts +1 -1
  13. package/lib/maps/BufferLayerDesignerComponent.js +0 -5
  14. package/lib/maps/ChoroplethLayer.d.ts +1 -16
  15. package/lib/maps/ChoroplethLayer.js +13 -358
  16. package/lib/maps/ClusterLayer.d.ts +0 -9
  17. package/lib/maps/ClusterLayer.js +0 -250
  18. package/lib/maps/DirectMapDataSource.js +1 -38
  19. package/lib/maps/GridLayer.d.ts +0 -15
  20. package/lib/maps/GridLayer.js +0 -212
  21. package/lib/maps/Layer.d.ts +1 -26
  22. package/lib/maps/Layer.js +0 -13
  23. package/lib/maps/MapComponent.d.ts +19 -35
  24. package/lib/maps/MapComponent.js +135 -76
  25. package/lib/maps/MapControlComponent.d.ts +4 -5
  26. package/lib/maps/MapControlComponent.js +5 -12
  27. package/lib/maps/MapDesign.d.ts +8 -0
  28. package/lib/maps/MapDesignerComponent.d.ts +2 -0
  29. package/lib/maps/MapDesignerComponent.js +7 -2
  30. package/lib/maps/MapLayerDataSource.d.ts +0 -4
  31. package/lib/maps/MapLayerViewDesignerComponent.d.ts +3 -1
  32. package/lib/maps/MapLayerViewDesignerComponent.js +5 -1
  33. package/lib/maps/MapLayersDesignerComponent.d.ts +2 -0
  34. package/lib/maps/MapLayersDesignerComponent.js +2 -1
  35. package/lib/maps/MapTranslationsTab.d.ts +15 -0
  36. package/lib/maps/MapTranslationsTab.js +47 -0
  37. package/lib/maps/MapUtils.d.ts +11 -0
  38. package/lib/maps/MapUtils.js +47 -0
  39. package/lib/maps/MapViewComponent.d.ts +1 -1
  40. package/lib/maps/MapViewComponent.js +1 -8
  41. package/lib/maps/MarkersLayer.d.ts +1 -14
  42. package/lib/maps/MarkersLayer.js +71 -252
  43. package/lib/maps/MarkersLayerDesign.d.ts +4 -0
  44. package/lib/maps/MarkersLayerDesignerComponent.d.ts +20 -16
  45. package/lib/maps/MarkersLayerDesignerComponent.js +77 -23
  46. package/lib/maps/ServerMapDataSource.d.ts +0 -1
  47. package/lib/maps/ServerMapDataSource.js +0 -15
  48. package/lib/maps/SwitchableTileUrlLayer.d.ts +0 -2
  49. package/lib/maps/SwitchableTileUrlLayer.js +0 -9
  50. package/lib/maps/TileUrlLayer.d.ts +0 -1
  51. package/lib/maps/TileUrlLayer.js +0 -5
  52. package/lib/maps/VectorMapViewComponent.js +12 -1
  53. package/lib/maps/vectorMaps.d.ts +5 -6
  54. package/lib/maps/vectorMaps.js +13 -9
  55. package/lib/widgets/MapWidget.js +2 -1
  56. package/package.json +2 -2
  57. package/src/ColorComponent.tsx +2 -2
  58. package/src/TranslationsTabComponent.tsx +429 -0
  59. package/src/dashboards/DashboardComponent.tsx +1 -1
  60. package/src/dashboards/ServerDashboardDataSource.ts +0 -19
  61. package/src/dashboards/SettingsModalComponent.tsx +27 -383
  62. package/src/datagrids/DatagridComponent.tsx +6 -0
  63. package/src/datagrids/DatagridViewComponent.tsx +41 -5
  64. package/src/maps/BufferLayer.ts +16 -262
  65. package/src/maps/BufferLayerDesignerComponent.tsx +0 -6
  66. package/src/maps/ChoroplethLayer.ts +16 -393
  67. package/src/maps/ClusterLayer.ts +0 -274
  68. package/src/maps/DirectMapDataSource.ts +2 -49
  69. package/src/maps/GridLayer.ts +0 -224
  70. package/src/maps/Layer.ts +1 -35
  71. package/src/maps/MapComponent.tsx +448 -0
  72. package/src/maps/MapControlComponent.tsx +41 -0
  73. package/src/maps/MapDesign.ts +6 -0
  74. package/src/maps/MapDesignerComponent.tsx +18 -1
  75. package/src/maps/MapLayerDataSource.ts +0 -5
  76. package/src/maps/MapLayerViewDesignerComponent.ts +9 -2
  77. package/src/maps/MapLayersDesignerComponent.ts +4 -1
  78. package/src/maps/MapTranslationsTab.tsx +53 -0
  79. package/src/maps/MapUtils.ts +48 -0
  80. package/src/maps/MapViewComponent.tsx +2 -8
  81. package/src/maps/MarkersLayer.ts +79 -270
  82. package/src/maps/MarkersLayerDesign.ts +6 -0
  83. package/src/maps/MarkersLayerDesignerComponent.tsx +114 -38
  84. package/src/maps/ServerMapDataSource.ts +0 -19
  85. package/src/maps/SwitchableTileUrlLayer.tsx +0 -11
  86. package/src/maps/TileUrlLayer.tsx +0 -6
  87. package/src/maps/VectorMapViewComponent.tsx +13 -2
  88. package/src/maps/vectorMaps.tsx +12 -9
  89. package/src/widgets/MapWidget.tsx +2 -0
  90. package/src/maps/MapComponent.ts +0 -311
  91. package/src/maps/MapControlComponent.ts +0 -46
  92. package/src/maps/RasterMapViewComponent.ts +0 -345
@@ -1,46 +0,0 @@
1
- import PropTypes from "prop-types"
2
- import React from "react"
3
- const R = React.createElement
4
-
5
- import MapLayersDesignerComponent from "./MapLayersDesignerComponent"
6
- import BaseLayerDesignerComponent from "./BaseLayerDesignerComponent"
7
- import { DataSource, Schema } from "@mwater/expressions"
8
-
9
- export interface MapControlComponentProps {
10
- /** Schema to use */
11
- schema: Schema
12
- dataSource: DataSource
13
- /** See Map Design.md */
14
- design: any
15
- onDesignChange: any
16
- }
17
-
18
- // Allows controlling readonly map
19
- export default class MapControlComponent extends React.Component<MapControlComponentProps> {
20
- render() {
21
- return R(
22
- "div",
23
- { style: { padding: 5 } },
24
- R(MapLayersDesignerComponent, {
25
- schema: this.props.schema,
26
- dataSource: this.props.dataSource,
27
- design: this.props.design,
28
- onDesignChange: this.props.onDesignChange,
29
- allowEditingLayers: false
30
- }),
31
-
32
- R("br"),
33
-
34
- R(
35
- "div",
36
- { className: "mb-3" },
37
- R("label", { className: "text-muted" }, T`Map Style`),
38
-
39
- R(BaseLayerDesignerComponent, {
40
- design: this.props.design,
41
- onDesignChange: this.props.onDesignChange
42
- })
43
- )
44
- )
45
- }
46
- }
@@ -1,345 +0,0 @@
1
- import _ from "lodash"
2
- import React, { ReactNode } from "react"
3
- const R = React.createElement
4
- import LeafletMapComponent, { TileLayer } from "./LeafletMapComponent"
5
- import { ExprUtils, Schema, DataSource } from "@mwater/expressions"
6
- import LayerFactory from "./LayerFactory"
7
- import ModalPopupComponent from "@mwater/react-library/lib/ModalPopupComponent"
8
- import * as MapUtils from "./MapUtils"
9
- import { LayerSwitcherComponent } from "./LayerSwitcherComponent"
10
- import { default as LegendComponent } from "./LegendComponent"
11
- import { JsonQLFilter } from "../JsonQLFilter"
12
- import { MapDesign } from "./MapDesign"
13
- import { LocaleContext } from "@mwater/expressions-ui"
14
- import { MapViewComponentProps } from "./MapViewComponent"
15
-
16
- export interface RasterMapViewComponentProps extends MapViewComponentProps {
17
- /** Called with underlying leaflet map component */
18
- leafletMapRef?: (map: LeafletMapComponent | null) => void
19
- }
20
-
21
- /** Component that displays just the map, using raster tile technology */
22
- export default class RasterMapViewComponent extends React.Component<
23
- RasterMapViewComponentProps,
24
- {
25
- popupContents: ReactNode
26
- legendHidden: boolean
27
- }
28
- > {
29
- static contextType = LocaleContext
30
-
31
- leafletMap?: LeafletMapComponent | null
32
-
33
- constructor(props: any) {
34
- super(props)
35
-
36
- const initialLegendDisplay = props.design.initialLegendDisplay || "open"
37
-
38
- this.state = {
39
- popupContents: null, // Element in the popup
40
- legendHidden: initialLegendDisplay === "closed" || (props.width < 500 && initialLegendDisplay === "closedIfSmall")
41
- }
42
- }
43
-
44
- componentDidMount() {
45
- // Autozoom
46
- if (this.props.design.autoBounds) {
47
- return this.performAutoZoom()
48
- }
49
- }
50
-
51
- componentDidUpdate(prevProps: any) {
52
- if (this.props.design.autoBounds) {
53
- // Autozoom if filters or autozoom changed
54
- if (
55
- !_.isEqual(this.props.design.filters, prevProps.design.filters) ||
56
- !_.isEqual(this.props.design.globalFilters, prevProps.design.globalFilters) ||
57
- !_.isEqual(this.props.extraFilters, prevProps.extraFilters) ||
58
- !prevProps.design.autoBounds
59
- ) {
60
- return this.performAutoZoom()
61
- }
62
- } else {
63
- // Update bounds
64
- if (!_.isEqual(this.props.design.bounds, prevProps.design.bounds)) {
65
- return this.leafletMap?.setBounds(this.props.design.bounds)
66
- }
67
- }
68
- }
69
-
70
- performAutoZoom() {
71
- return this.props.mapDataSource.getBounds(
72
- this.props.design,
73
- this.getCompiledFilters(),
74
- (error: any, bounds: any) => {
75
- if (bounds) {
76
- this.leafletMap?.setBounds(bounds, 0.2)
77
-
78
- // Also record if editable as part of bounds
79
- if (this.props.onDesignChange != null) {
80
- return this.props.onDesignChange(_.extend({}, this.props.design, { bounds }))
81
- }
82
- }
83
- }
84
- )
85
- }
86
-
87
- handleBoundsChange = (bounds: any) => {
88
- // Ignore if readonly
89
- if (this.props.onDesignChange == null) {
90
- return
91
- }
92
-
93
- if (this.props.zoomLocked) {
94
- return
95
- }
96
-
97
- // Ignore if autoBounds
98
- if (this.props.design.autoBounds) {
99
- return
100
- }
101
-
102
- const design = _.extend({}, this.props.design, { bounds }) as MapDesign
103
- return this.props.onDesignChange(design)
104
- }
105
-
106
- handleGridClick = (layerViewId: any, ev: any) => {
107
- const layerView = _.findWhere(this.props.design.layerViews, { id: layerViewId })!
108
-
109
- // Create layer
110
- const layer = LayerFactory.createLayer(layerView.type)
111
-
112
- // Clean design (prevent ever displaying invalid/legacy designs)
113
- const design = layer.cleanDesign(layerView.design, this.props.schema)
114
-
115
- // Handle click of layer
116
- // TODO not translated
117
- const results = layer.onGridClick(ev, {
118
- design,
119
- schema: this.props.schema,
120
- dataSource: this.props.dataSource,
121
- layerDataSource: this.props.mapDataSource.getLayerDataSource(layerViewId),
122
- scopeData: this.props.scope?.data?.layerViewId === layerViewId ? this.props.scope!.data.data : undefined,
123
- filters: this.getCompiledFilters(),
124
- locale: this.context,
125
- translate: (input: string) => input
126
- })
127
-
128
- if (!results) {
129
- return
130
- }
131
-
132
- // Handle popup first
133
- if (results.popup) {
134
- this.setState({ popupContents: results.popup })
135
- }
136
-
137
- // Handle onRowClick case
138
- if (results.row && this.props.onRowClick) {
139
- this.props.onRowClick(results.row.tableId, results.row.primaryKey)
140
- }
141
-
142
- // Handle scoping
143
- if (this.props.onScopeChange && _.has(results, "scope")) {
144
- let scope
145
- if (results.scope) {
146
- // Encode layer view id into scope
147
- scope = {
148
- name: results.scope.name,
149
- filter: results.scope.filter,
150
- data: { layerViewId, data: results.scope.data }
151
- }
152
- } else {
153
- scope = null
154
- }
155
-
156
- return this.props.onScopeChange(scope)
157
- }
158
- }
159
-
160
- // Get filters from extraFilters combined with map filters
161
- getCompiledFilters() {
162
- return (this.props.extraFilters || []).concat(
163
- MapUtils.getCompiledFilters(
164
- this.props.design,
165
- this.props.schema,
166
- MapUtils.getFilterableTables(this.props.design, this.props.schema)
167
- )
168
- )
169
- }
170
-
171
- renderLegend() {
172
- if (this.state.legendHidden) {
173
- return R(HiddenLegend, { onShow: () => this.setState({ legendHidden: false }) })
174
- } else {
175
- return R(LegendComponent, {
176
- schema: this.props.schema,
177
- layerViews: this.props.design.layerViews,
178
- filters: this.getCompiledFilters(),
179
- dataSource: this.props.dataSource,
180
- locale: this.context,
181
- translate: this.props.translate ?? ((input: string) => input),
182
- onHide: () => this.setState({ legendHidden: true }),
183
- zoom: null
184
- })
185
- }
186
- }
187
-
188
- renderPopup() {
189
- if (!this.state.popupContents) {
190
- return null
191
- }
192
-
193
- return R(
194
- ModalPopupComponent,
195
- {
196
- onClose: () => this.setState({ popupContents: null }),
197
- showCloseX: true,
198
- size: "x-large"
199
- },
200
- // Render in fixed height div so that dashboard doesn't collapse to nothing
201
- R("div", { style: { height: "80vh" } }, this.state.popupContents),
202
- R(
203
- "div",
204
- { style: { textAlign: "right", marginTop: 10 } },
205
- R("button", { className: "btn btn-secondary", onClick: () => this.setState({ popupContents: null }) }, T`Close`)
206
- )
207
- )
208
- }
209
-
210
- render() {
211
- let design, scopedCompiledFilters
212
- const compiledFilters = this.getCompiledFilters()
213
-
214
- // Determine scoped filters
215
- if (this.props.scope) {
216
- scopedCompiledFilters = compiledFilters.concat([this.props.scope.filter])
217
- } else {
218
- scopedCompiledFilters = compiledFilters
219
- }
220
-
221
- // Convert to leaflet layers, if layers are valid
222
- const leafletLayers = []
223
- for (let index = 0; index < this.props.design.layerViews.length; index++) {
224
- // Create layer
225
- const layerView = this.props.design.layerViews[index]
226
- const layer = LayerFactory.createLayer(layerView.type)
227
-
228
- // Clean design (prevent ever displaying invalid/legacy designs)
229
- design = layer.cleanDesign(layerView.design, this.props.schema)
230
-
231
- // Ignore if invalid
232
- if (layer.validateDesign(design, this.props.schema)) {
233
- continue
234
- }
235
-
236
- // Get layer data source
237
- const layerDataSource = this.props.mapDataSource.getLayerDataSource(layerView.id)
238
-
239
- // If layer is scoping, fade opacity and add extra filtered version
240
- const isScoping = this.props.scope && this.props.scope.data.layerViewId === layerView.id
241
-
242
- // Create leafletLayer
243
- let leafletLayer: TileLayer = {
244
- tileUrl: layerDataSource.getTileUrl(design, isScoping ? compiledFilters : scopedCompiledFilters)!,
245
- utfGridUrl: this.props.disableInteraction
246
- ? undefined
247
- : layerDataSource.getUtfGridUrl(design, isScoping ? compiledFilters : scopedCompiledFilters) ?? undefined,
248
- visible: layerView.visible,
249
- opacity: isScoping ? layerView.opacity * 0.3 : layerView.opacity,
250
- minZoom: layer.getMinZoom(design) ?? undefined,
251
- maxZoom: layer.getMaxZoom(design) ?? undefined,
252
- onGridClick: this.handleGridClick.bind(null, layerView.id)
253
- }
254
-
255
- leafletLayers.push(leafletLayer)
256
-
257
- // Add scoped layer if scoping
258
- if (isScoping) {
259
- leafletLayer = {
260
- tileUrl: layerDataSource.getTileUrl(design, scopedCompiledFilters)!,
261
- utfGridUrl: this.props.disableInteraction
262
- ? undefined
263
- : layerDataSource.getUtfGridUrl(design, scopedCompiledFilters) ?? undefined,
264
- visible: layerView.visible,
265
- opacity: layerView.opacity,
266
- minZoom: layer.getMinZoom(design) ?? undefined,
267
- maxZoom: layer.getMaxZoom(design) ?? undefined,
268
- onGridClick: this.handleGridClick.bind(null, layerView.id)
269
- }
270
-
271
- leafletLayers.push(leafletLayer)
272
- }
273
- }
274
-
275
- return R(
276
- "div",
277
- { style: { width: this.props.width, height: this.props.height, position: "relative" } },
278
- this.renderPopup(),
279
- R(
280
- "div",
281
- { style: { position: "absolute", maxWidth: 250, top: 10, right: 10, zIndex: 999, userSelect: "none" } },
282
- R(
283
- "div",
284
- { style: { display: "flex", gap: 6, position: "relative", flexDirection: "column", alignItems: "right" } },
285
- this.props.onDesignChange && this.props.design.showLayerSwitcher
286
- ? R(LayerSwitcherComponent, {
287
- design: this.props.design,
288
- onDesignChange: this.props.onDesignChange,
289
- translate: this.props.translate ?? (() => "")
290
- })
291
- : undefined
292
- )
293
- ),
294
-
295
- R(LeafletMapComponent, {
296
- ref: (c: LeafletMapComponent | null) => {
297
- this.leafletMap = c
298
- if (this.props.leafletMapRef) {
299
- this.props.leafletMapRef(c)
300
- }
301
- },
302
- initialBounds: this.props.design.bounds,
303
- baseLayerId: this.props.design.baseLayer,
304
- baseLayerOpacity: this.props.design.baseLayerOpacity ?? undefined,
305
- layers: leafletLayers,
306
- width: this.props.width,
307
- height: this.props.height,
308
- legend: this.renderLegend(),
309
- dragging: this.props.dragging,
310
- touchZoom: this.props.touchZoom,
311
- scrollWheelZoom: this.props.scrollWheelZoom,
312
- onBoundsChange: this.handleBoundsChange,
313
- extraAttribution: this.props.design.attribution,
314
- loadingSpinner: true,
315
- maxZoom: this.props.design.maxZoom ?? undefined,
316
- showZoomControlOnHover: true
317
- })
318
- )
319
- }
320
- }
321
-
322
- // Legend display tab at bottom right
323
- class HiddenLegend extends React.Component<{ onShow: () => void }> {
324
- render() {
325
- const style = {
326
- zIndex: 1000,
327
- backgroundColor: "white",
328
- position: "absolute",
329
- bottom: 34,
330
- right: 0,
331
- fontSize: 14,
332
- color: "#337ab7",
333
- cursor: "pointer",
334
- paddingTop: 4,
335
- paddingBottom: 3,
336
- paddingLeft: 3,
337
- paddingRight: 3,
338
- borderRadius: "4px 0px 0px 4px",
339
- border: "solid 1px #AAA",
340
- borderRight: "none"
341
- }
342
-
343
- return R("div", { style, onClick: this.props.onShow }, R("i", { className: "fa fa-angle-double-left" }))
344
- }
345
- }