@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.
- package/lib/ColorComponent.js +2 -2
- package/lib/TranslationsTabComponent.d.ts +34 -0
- package/lib/TranslationsTabComponent.js +256 -0
- package/lib/dashboards/DashboardComponent.js +1 -1
- package/lib/dashboards/ServerDashboardDataSource.d.ts +0 -1
- package/lib/dashboards/ServerDashboardDataSource.js +0 -15
- package/lib/dashboards/SettingsModalComponent.js +9 -233
- package/lib/datagrids/DatagridComponent.js +5 -0
- package/lib/datagrids/DatagridViewComponent.js +30 -4
- package/lib/maps/BufferLayer.d.ts +0 -13
- package/lib/maps/BufferLayer.js +12 -237
- package/lib/maps/BufferLayerDesignerComponent.d.ts +1 -1
- package/lib/maps/BufferLayerDesignerComponent.js +0 -5
- package/lib/maps/ChoroplethLayer.d.ts +1 -16
- package/lib/maps/ChoroplethLayer.js +13 -358
- package/lib/maps/ClusterLayer.d.ts +0 -9
- package/lib/maps/ClusterLayer.js +0 -250
- package/lib/maps/DirectMapDataSource.js +1 -38
- package/lib/maps/GridLayer.d.ts +0 -15
- package/lib/maps/GridLayer.js +0 -212
- package/lib/maps/Layer.d.ts +1 -26
- package/lib/maps/Layer.js +0 -13
- package/lib/maps/MapComponent.d.ts +19 -35
- package/lib/maps/MapComponent.js +135 -76
- package/lib/maps/MapControlComponent.d.ts +4 -5
- package/lib/maps/MapControlComponent.js +5 -12
- package/lib/maps/MapDesign.d.ts +8 -0
- package/lib/maps/MapDesignerComponent.d.ts +2 -0
- package/lib/maps/MapDesignerComponent.js +7 -2
- package/lib/maps/MapLayerDataSource.d.ts +0 -4
- package/lib/maps/MapLayerViewDesignerComponent.d.ts +3 -1
- package/lib/maps/MapLayerViewDesignerComponent.js +5 -1
- package/lib/maps/MapLayersDesignerComponent.d.ts +2 -0
- package/lib/maps/MapLayersDesignerComponent.js +2 -1
- package/lib/maps/MapTranslationsTab.d.ts +15 -0
- package/lib/maps/MapTranslationsTab.js +47 -0
- package/lib/maps/MapUtils.d.ts +11 -0
- package/lib/maps/MapUtils.js +47 -0
- package/lib/maps/MapViewComponent.d.ts +1 -1
- package/lib/maps/MapViewComponent.js +1 -8
- package/lib/maps/MarkersLayer.d.ts +1 -14
- package/lib/maps/MarkersLayer.js +71 -252
- package/lib/maps/MarkersLayerDesign.d.ts +4 -0
- package/lib/maps/MarkersLayerDesignerComponent.d.ts +20 -16
- package/lib/maps/MarkersLayerDesignerComponent.js +77 -23
- package/lib/maps/ServerMapDataSource.d.ts +0 -1
- package/lib/maps/ServerMapDataSource.js +0 -15
- package/lib/maps/SwitchableTileUrlLayer.d.ts +0 -2
- package/lib/maps/SwitchableTileUrlLayer.js +0 -9
- package/lib/maps/TileUrlLayer.d.ts +0 -1
- package/lib/maps/TileUrlLayer.js +0 -5
- package/lib/maps/VectorMapViewComponent.js +12 -1
- package/lib/maps/vectorMaps.d.ts +5 -6
- package/lib/maps/vectorMaps.js +13 -9
- package/lib/widgets/MapWidget.js +2 -1
- package/package.json +2 -2
- package/src/ColorComponent.tsx +2 -2
- package/src/TranslationsTabComponent.tsx +429 -0
- package/src/dashboards/DashboardComponent.tsx +1 -1
- package/src/dashboards/ServerDashboardDataSource.ts +0 -19
- package/src/dashboards/SettingsModalComponent.tsx +27 -383
- package/src/datagrids/DatagridComponent.tsx +6 -0
- package/src/datagrids/DatagridViewComponent.tsx +41 -5
- package/src/maps/BufferLayer.ts +16 -262
- package/src/maps/BufferLayerDesignerComponent.tsx +0 -6
- package/src/maps/ChoroplethLayer.ts +16 -393
- package/src/maps/ClusterLayer.ts +0 -274
- package/src/maps/DirectMapDataSource.ts +2 -49
- package/src/maps/GridLayer.ts +0 -224
- package/src/maps/Layer.ts +1 -35
- package/src/maps/MapComponent.tsx +448 -0
- package/src/maps/MapControlComponent.tsx +41 -0
- package/src/maps/MapDesign.ts +6 -0
- package/src/maps/MapDesignerComponent.tsx +18 -1
- package/src/maps/MapLayerDataSource.ts +0 -5
- package/src/maps/MapLayerViewDesignerComponent.ts +9 -2
- package/src/maps/MapLayersDesignerComponent.ts +4 -1
- package/src/maps/MapTranslationsTab.tsx +53 -0
- package/src/maps/MapUtils.ts +48 -0
- package/src/maps/MapViewComponent.tsx +2 -8
- package/src/maps/MarkersLayer.ts +79 -270
- package/src/maps/MarkersLayerDesign.ts +6 -0
- package/src/maps/MarkersLayerDesignerComponent.tsx +114 -38
- package/src/maps/ServerMapDataSource.ts +0 -19
- package/src/maps/SwitchableTileUrlLayer.tsx +0 -11
- package/src/maps/TileUrlLayer.tsx +0 -6
- package/src/maps/VectorMapViewComponent.tsx +13 -2
- package/src/maps/vectorMaps.tsx +12 -9
- package/src/widgets/MapWidget.tsx +2 -0
- package/src/maps/MapComponent.ts +0 -311
- package/src/maps/MapControlComponent.ts +0 -46
- package/src/maps/RasterMapViewComponent.ts +0 -345
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import _ from "lodash"
|
|
2
2
|
import React from "react"
|
|
3
3
|
import { FilterExprComponent } from "@mwater/expressions-ui"
|
|
4
|
-
import { DataSource, ExprUtils, OpExpr, Schema } from "@mwater/expressions"
|
|
4
|
+
import { DataSource, Expr, ExprUtils, OpExpr, Schema } from "@mwater/expressions"
|
|
5
5
|
import { ExprCompiler } from "@mwater/expressions"
|
|
6
6
|
import AxisComponent from "../axes/AxisComponent"
|
|
7
7
|
import ColorComponent from "../ColorComponent"
|
|
@@ -14,6 +14,9 @@ import * as ui from "@mwater/react-library/lib/bootstrap"
|
|
|
14
14
|
import { EditHoverOver } from "./EditHoverOver"
|
|
15
15
|
import { MarkersLayerDesign } from "./MarkersLayerDesign"
|
|
16
16
|
import { default as Rcslider } from "rc-slider"
|
|
17
|
+
import { produce } from "immer"
|
|
18
|
+
import { Axis } from "../axes/Axis"
|
|
19
|
+
import { JsonQLFilter } from "../JsonQLFilter"
|
|
17
20
|
|
|
18
21
|
export interface MarkersLayerDesignerComponentProps {
|
|
19
22
|
/** Schema to use */
|
|
@@ -22,55 +25,71 @@ export interface MarkersLayerDesignerComponentProps {
|
|
|
22
25
|
/** Design of the marker layer */
|
|
23
26
|
design: MarkersLayerDesign
|
|
24
27
|
/** Called with new design */
|
|
25
|
-
onDesignChange:
|
|
26
|
-
filters?:
|
|
28
|
+
onDesignChange: (design: MarkersLayerDesign) => void
|
|
29
|
+
filters?: JsonQLFilter[]
|
|
27
30
|
}
|
|
28
31
|
|
|
29
|
-
|
|
32
|
+
/** Designer for a markers layer */
|
|
30
33
|
export default class MarkersLayerDesignerComponent extends React.Component<MarkersLayerDesignerComponentProps> {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
handleTableChange = (table: string) => {
|
|
35
|
+
this.props.onDesignChange(produce(this.props.design, draft => {
|
|
36
|
+
draft.table = table
|
|
37
|
+
}))
|
|
34
38
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return this.update({ axes })
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
handleTableChange = (table: any) => {
|
|
43
|
-
return this.update({ table })
|
|
39
|
+
handleGeometryAxisChange = (axis: Axis) => {
|
|
40
|
+
this.props.onDesignChange(produce(this.props.design, draft => {
|
|
41
|
+
draft.axes.geometry = axis
|
|
42
|
+
}))
|
|
44
43
|
}
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
handleColorAxisChange = (axis: Axis | null) => {
|
|
45
|
+
this.props.onDesignChange(produce(this.props.design, draft => {
|
|
46
|
+
draft.axes.color = axis ?? undefined
|
|
47
|
+
}))
|
|
47
48
|
}
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
handleFilterChange = (expr: Expr) => {
|
|
50
|
+
this.props.onDesignChange(produce(this.props.design, draft => {
|
|
51
|
+
draft.filter = expr
|
|
52
|
+
}))
|
|
50
53
|
}
|
|
51
|
-
|
|
52
|
-
|
|
54
|
+
handleColorChange = (color: string) => {
|
|
55
|
+
this.props.onDesignChange(produce(this.props.design, draft => {
|
|
56
|
+
draft.color = color
|
|
57
|
+
}))
|
|
53
58
|
}
|
|
54
|
-
|
|
55
|
-
|
|
59
|
+
handlePolygonBorderColorChange = (polygonBorderColor: string) => {
|
|
60
|
+
this.props.onDesignChange(produce(this.props.design, draft => {
|
|
61
|
+
draft.polygonBorderColor = polygonBorderColor
|
|
62
|
+
}))
|
|
56
63
|
}
|
|
57
|
-
|
|
58
|
-
|
|
64
|
+
handlePolygonFillOpacityChange = (polygonFillOpacity: number) => {
|
|
65
|
+
this.props.onDesignChange(produce(this.props.design, draft => {
|
|
66
|
+
draft.polygonFillOpacity = polygonFillOpacity / 100
|
|
67
|
+
}))
|
|
59
68
|
}
|
|
60
|
-
|
|
61
|
-
|
|
69
|
+
handleSymbolChange = (symbol: string) => {
|
|
70
|
+
this.props.onDesignChange(produce(this.props.design, draft => {
|
|
71
|
+
draft.symbol = symbol
|
|
72
|
+
}))
|
|
62
73
|
}
|
|
63
|
-
|
|
64
|
-
|
|
74
|
+
handleMarkerSizeChange = (markerSize: number) => {
|
|
75
|
+
this.props.onDesignChange(produce(this.props.design, draft => {
|
|
76
|
+
draft.markerSize = markerSize
|
|
77
|
+
}))
|
|
65
78
|
}
|
|
66
|
-
|
|
67
|
-
|
|
79
|
+
handleLineWidthChange = (lineWidth: number) => {
|
|
80
|
+
this.props.onDesignChange(produce(this.props.design, draft => {
|
|
81
|
+
draft.lineWidth = lineWidth
|
|
82
|
+
}))
|
|
68
83
|
}
|
|
69
|
-
|
|
70
|
-
|
|
84
|
+
handleLabelAxisChange = (axis: Axis | null) => {
|
|
85
|
+
this.props.onDesignChange(produce(this.props.design, draft => {
|
|
86
|
+
draft.axes.label = axis ?? undefined
|
|
87
|
+
}))
|
|
71
88
|
}
|
|
72
|
-
|
|
73
|
-
|
|
89
|
+
handleLabelPositionChange = (labelPosition: "top" | "bottom" | "left" | "right") => {
|
|
90
|
+
this.props.onDesignChange(produce(this.props.design, draft => {
|
|
91
|
+
draft.labelPosition = labelPosition
|
|
92
|
+
}))
|
|
74
93
|
}
|
|
75
94
|
|
|
76
95
|
renderTable() {
|
|
@@ -103,7 +122,7 @@ export default class MarkersLayerDesignerComponent extends React.Component<Marke
|
|
|
103
122
|
const exprCompiler = new ExprCompiler(this.props.schema)
|
|
104
123
|
const jsonql = exprCompiler.compileExpr({ expr: this.props.design.filter, tableAlias: "{alias}" })
|
|
105
124
|
if (jsonql) {
|
|
106
|
-
filters.push({ table: (this.props.design.filter as OpExpr).table
|
|
125
|
+
filters.push({ table: (this.props.design.filter as OpExpr).table!, jsonql })
|
|
107
126
|
}
|
|
108
127
|
}
|
|
109
128
|
|
|
@@ -137,7 +156,7 @@ export default class MarkersLayerDesignerComponent extends React.Component<Marke
|
|
|
137
156
|
const exprCompiler = new ExprCompiler(this.props.schema)
|
|
138
157
|
const jsonql = exprCompiler.compileExpr({ expr: this.props.design.filter, tableAlias: "{alias}" })
|
|
139
158
|
if (jsonql) {
|
|
140
|
-
filters.push({ table: (this.props.design.filter as OpExpr).table
|
|
159
|
+
filters.push({ table: (this.props.design.filter as OpExpr).table!, jsonql })
|
|
141
160
|
}
|
|
142
161
|
}
|
|
143
162
|
|
|
@@ -295,6 +314,61 @@ export default class MarkersLayerDesignerComponent extends React.Component<Marke
|
|
|
295
314
|
)
|
|
296
315
|
}
|
|
297
316
|
|
|
317
|
+
renderLabelAxis() {
|
|
318
|
+
if (!this.props.design.axes.geometry) {
|
|
319
|
+
return
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const filters = _.clone(this.props.filters) || []
|
|
323
|
+
|
|
324
|
+
if (this.props.design.filter != null) {
|
|
325
|
+
const exprCompiler = new ExprCompiler(this.props.schema)
|
|
326
|
+
const jsonql = exprCompiler.compileExpr({ expr: this.props.design.filter, tableAlias: "{alias}" })
|
|
327
|
+
if (jsonql) {
|
|
328
|
+
filters.push({ table: (this.props.design.filter as OpExpr).table!, jsonql })
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return (
|
|
333
|
+
<div className="mb-3">
|
|
334
|
+
<label className="text-muted"><span className="fas fa-tag" /> {T`Label (points only)`}</label>
|
|
335
|
+
<AxisComponent
|
|
336
|
+
schema={this.props.schema}
|
|
337
|
+
dataSource={this.props.dataSource}
|
|
338
|
+
table={this.props.design.table}
|
|
339
|
+
types={["text", "number"]}
|
|
340
|
+
aggrNeed="none"
|
|
341
|
+
value={this.props.design.axes.label}
|
|
342
|
+
onChange={this.handleLabelAxisChange}
|
|
343
|
+
filters={filters}
|
|
344
|
+
/>
|
|
345
|
+
</div>
|
|
346
|
+
)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
renderLabelPosition() {
|
|
350
|
+
// Only show if label axis is set
|
|
351
|
+
if (!this.props.design.axes.label) {
|
|
352
|
+
return
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return (
|
|
356
|
+
<div className="mb-3">
|
|
357
|
+
<label className="text-muted">{T`Label Position`}</label>
|
|
358
|
+
<ui.Select
|
|
359
|
+
value={this.props.design.labelPosition || "bottom"}
|
|
360
|
+
options={[
|
|
361
|
+
{ value: "top", label: T`Top` },
|
|
362
|
+
{ value: "bottom", label: T`Bottom` },
|
|
363
|
+
{ value: "left", label: T`Left` },
|
|
364
|
+
{ value: "right", label: T`Right` }
|
|
365
|
+
]}
|
|
366
|
+
onChange={this.handleLabelPositionChange}
|
|
367
|
+
/>
|
|
368
|
+
</div>
|
|
369
|
+
)
|
|
370
|
+
}
|
|
371
|
+
|
|
298
372
|
renderPopup() {
|
|
299
373
|
if (!this.props.design.table) {
|
|
300
374
|
return null
|
|
@@ -340,6 +414,8 @@ export default class MarkersLayerDesignerComponent extends React.Component<Marke
|
|
|
340
414
|
{this.renderColor()}
|
|
341
415
|
{this.renderSymbol()}
|
|
342
416
|
{this.renderMarkerSize()}
|
|
417
|
+
{this.renderLabelAxis()}
|
|
418
|
+
{this.renderLabelPosition()}
|
|
343
419
|
<ui.CollapsibleSection
|
|
344
420
|
label={T`Shape Options`}
|
|
345
421
|
labelMuted={true}
|
|
@@ -126,25 +126,6 @@ class ServerLayerDataSource implements MapLayerDataSource {
|
|
|
126
126
|
return this.createUrl(filters, "png")
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
// Get the url for the interactivity tiles with the specified filters applied
|
|
130
|
-
// Called with (design, filters) where design is the design of the layer and filters are filters to apply. Returns URL
|
|
131
|
-
getUtfGridUrl(design: any, filters: JsonQLFilter[]) {
|
|
132
|
-
// Handle special cases
|
|
133
|
-
if (this.options.layerView.type === "MWaterServer") {
|
|
134
|
-
return this.createLegacyUrl(design, "grid.json", filters)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Create layer
|
|
138
|
-
const layer = LayerFactory.createLayer(this.options.layerView.type)
|
|
139
|
-
|
|
140
|
-
// If layer has tiles url directly available
|
|
141
|
-
if (layer.getLayerDefinitionType() === "TileUrl") {
|
|
142
|
-
return layer.getUtfGridUrl(this.options.layerView.design, filters)
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return this.createUrl(filters, "grid.json")
|
|
146
|
-
}
|
|
147
|
-
|
|
148
129
|
/** Get the url for vector tile source with an expiry time. Only for layers of type "VectorTile"
|
|
149
130
|
* @param createdAfter ISO 8601 timestamp requiring that tile source on server is created after specified datetime
|
|
150
131
|
*/
|
|
@@ -68,17 +68,6 @@ export default class SwitchableTileUrlLayer extends Layer<SwitchableTileUrlLayer
|
|
|
68
68
|
return option.tileUrl || null
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
/** Gets the utf grid url for definition type "TileUrl" */
|
|
72
|
-
getUtfGridUrl(design: SwitchableTileUrlLayerDesign, filters: JsonQLFilter[]): string | null {
|
|
73
|
-
// Find active option
|
|
74
|
-
const option = design.options.find((d) => d.id === design.activeOption)
|
|
75
|
-
if (!option) {
|
|
76
|
-
return null
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return option.utfGridUrl || null
|
|
80
|
-
}
|
|
81
|
-
|
|
82
71
|
getLegend(options: LegendOptions<SwitchableTileUrlLayerDesign>): ReactNode {
|
|
83
72
|
const { design, name } = options
|
|
84
73
|
// Find active option
|
|
@@ -31,7 +31,6 @@ Design is:
|
|
|
31
31
|
legendUrl:
|
|
32
32
|
*/
|
|
33
33
|
export default class TileUrlLayer extends Layer<TileUrlLayerDesign> {
|
|
34
|
-
// Gets the type of layer definition ("JsonQLCss"/"TileUrl")
|
|
35
34
|
getLayerDefinitionType(): "TileUrl" {
|
|
36
35
|
return "TileUrl"
|
|
37
36
|
}
|
|
@@ -41,11 +40,6 @@ export default class TileUrlLayer extends Layer<TileUrlLayerDesign> {
|
|
|
41
40
|
return design.tileUrl
|
|
42
41
|
}
|
|
43
42
|
|
|
44
|
-
// Gets the utf grid url for definition type "TileUrl"
|
|
45
|
-
getUtfGridUrl(design: any, filters: any) {
|
|
46
|
-
return null
|
|
47
|
-
}
|
|
48
|
-
|
|
49
43
|
// Get min and max zoom levels
|
|
50
44
|
getMinZoom(design: any) {
|
|
51
45
|
return design.minZoom
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import _, { find } from "lodash"
|
|
2
2
|
import { LayerSpecification, MapLayerMouseEvent } from "maplibre-gl"
|
|
3
|
-
import React, { CSSProperties, ReactNode, useEffect, useMemo, useState } from "react"
|
|
3
|
+
import React, { CSSProperties, ReactNode, useCallback, useEffect, useMemo, useState } from "react"
|
|
4
4
|
import { useRef } from "react"
|
|
5
5
|
import { JsonQLFilter } from "../JsonQLFilter"
|
|
6
6
|
import { default as LayerFactory } from "./LayerFactory"
|
|
@@ -75,7 +75,18 @@ export function VectorMapViewComponent(props: VectorMapViewComponentProps) {
|
|
|
75
75
|
const locale = props.locale || props.design.locale || "en"
|
|
76
76
|
|
|
77
77
|
// Translate function to use
|
|
78
|
-
const translate =
|
|
78
|
+
const translate = useCallback((input: string) => {
|
|
79
|
+
// Use passed in translate function if present
|
|
80
|
+
if (props.translate) {
|
|
81
|
+
return props.translate(input)
|
|
82
|
+
}
|
|
83
|
+
// If locale is the same as the design locale, don't translate
|
|
84
|
+
if (locale === props.design.locale) {
|
|
85
|
+
return input
|
|
86
|
+
}
|
|
87
|
+
// Otherwise, use translation from design
|
|
88
|
+
return props.design.translations?.[locale]?.[input] ?? input
|
|
89
|
+
}, [props.translate, props.design.translations, props.design.locale, locale])
|
|
79
90
|
|
|
80
91
|
// Last feature that mouse entered
|
|
81
92
|
const lastFeature = useRef<string>()
|
package/src/maps/vectorMaps.tsx
CHANGED
|
@@ -7,6 +7,9 @@ import "maplibre-gl/dist/maplibre-gl.css"
|
|
|
7
7
|
import "./VectorMapViewComponent.css"
|
|
8
8
|
import React from "react"
|
|
9
9
|
|
|
10
|
+
// Re-export maplibregl for consumers
|
|
11
|
+
export { default as maplibregl } from "maplibre-gl"
|
|
12
|
+
|
|
10
13
|
/** Set to true to enable printing by preserving the drawing buffer */
|
|
11
14
|
let printingModeEnabled = false
|
|
12
15
|
|
|
@@ -29,11 +32,6 @@ export function getMapTilerApiKey(): string {
|
|
|
29
32
|
return mapTilerApiKey
|
|
30
33
|
}
|
|
31
34
|
|
|
32
|
-
/** Check if vector maps are enabled by setting API key */
|
|
33
|
-
export function areVectorMapsEnabled() {
|
|
34
|
-
return mapTilerApiKey !== ""
|
|
35
|
-
}
|
|
36
|
-
|
|
37
35
|
export type BaseLayer = "bing_road" | "bing_aerial" | "cartodb_positron" | "cartodb_dark_matter" | "blank"
|
|
38
36
|
|
|
39
37
|
/** Loads a vector map, refreshing the WebGL context as needed */
|
|
@@ -106,7 +104,10 @@ export function useVectorMap(options: {
|
|
|
106
104
|
[-179.9, -85], // Southwest coordinates
|
|
107
105
|
[179.9, 85] // Northeast coordinates
|
|
108
106
|
],
|
|
109
|
-
|
|
107
|
+
// In maplibre-gl v5+, WebGL context options must be in canvasContextAttributes
|
|
108
|
+
canvasContextAttributes: {
|
|
109
|
+
preserveDrawingBuffer: printingModeEnabled
|
|
110
|
+
}
|
|
110
111
|
}
|
|
111
112
|
|
|
112
113
|
if (bounds) {
|
|
@@ -143,10 +144,12 @@ export function useVectorMap(options: {
|
|
|
143
144
|
// Check if known
|
|
144
145
|
const mapSymbol = getMapSymbols().find((s) => s.value == ev.id)
|
|
145
146
|
if (mapSymbol) {
|
|
146
|
-
m.loadImage(mapSymbol.url
|
|
147
|
-
if (
|
|
148
|
-
m.addImage(mapSymbol.value,
|
|
147
|
+
m.loadImage(mapSymbol.url).then((response) => {
|
|
148
|
+
if (response && response.data && !m.hasImage(mapSymbol.value)) {
|
|
149
|
+
m.addImage(mapSymbol.value, response.data, { sdf: true })
|
|
149
150
|
}
|
|
151
|
+
}).catch((err) => {
|
|
152
|
+
console.error("Error loading map symbol:", err)
|
|
150
153
|
})
|
|
151
154
|
}
|
|
152
155
|
})
|
|
@@ -123,12 +123,14 @@ class MapWidgetComponent extends React.Component<MapWidgetComponentProps, MapWid
|
|
|
123
123
|
const MapDesignerComponent = require("../maps/MapDesignerComponent").default
|
|
124
124
|
|
|
125
125
|
// Create editor
|
|
126
|
+
// Note: enableTranslations is false because translations are managed at the dashboard level
|
|
126
127
|
const editor = <MapDesignerComponent
|
|
127
128
|
schema={this.props.schema}
|
|
128
129
|
dataSource={this.props.dataSource}
|
|
129
130
|
design={this.state.editDesign}
|
|
130
131
|
onDesignChange={this.handleEditDesignChange}
|
|
131
132
|
filters={this.props.filters}
|
|
133
|
+
enableTranslations={false}
|
|
132
134
|
/>
|
|
133
135
|
|
|
134
136
|
// Create map (maxing out at half of width of screen)
|
package/src/maps/MapComponent.ts
DELETED
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
import _ from "lodash"
|
|
2
|
-
import React, { ReactNode } from "react"
|
|
3
|
-
const R = React.createElement
|
|
4
|
-
|
|
5
|
-
import { MapViewComponent } from "./MapViewComponent"
|
|
6
|
-
import MapDesignerComponent from "./MapDesignerComponent"
|
|
7
|
-
import MapControlComponent from "./MapControlComponent"
|
|
8
|
-
import AutoSizeComponent from "@mwater/react-library/lib/AutoSizeComponent"
|
|
9
|
-
import UndoStack from "../UndoStack"
|
|
10
|
-
import PopoverHelpComponent from "@mwater/react-library/lib/PopoverHelpComponent"
|
|
11
|
-
import { DataSource, Schema } from "@mwater/expressions"
|
|
12
|
-
import { MapDataSource } from "./MapDataSource"
|
|
13
|
-
import { MapDesign } from "./MapDesign"
|
|
14
|
-
import { JsonQLFilter } from "../JsonQLFilter"
|
|
15
|
-
import QuickfilterCompiler from "../quickfilter/QuickfilterCompiler"
|
|
16
|
-
import QuickfiltersComponent from "../quickfilter/QuickfiltersComponent"
|
|
17
|
-
import { getCompiledFilters, getFilterableTables } from "./MapUtils"
|
|
18
|
-
import { LocaleContext } from "@mwater/expressions-ui"
|
|
19
|
-
|
|
20
|
-
export interface MapComponentProps {
|
|
21
|
-
schema: Schema
|
|
22
|
-
dataSource: DataSource
|
|
23
|
-
mapDataSource: MapDataSource
|
|
24
|
-
|
|
25
|
-
design: MapDesign
|
|
26
|
-
onDesignChange?: (design: MapDesign) => void
|
|
27
|
-
|
|
28
|
-
/** Called with (tableId, rowId) when item is clicked */
|
|
29
|
-
onRowClick?: (tableId: string, rowId: any) => void
|
|
30
|
-
|
|
31
|
-
/** Extra filters to apply to view */
|
|
32
|
-
extraFilters?: JsonQLFilter[]
|
|
33
|
-
|
|
34
|
-
/** Extra element to include in title at left */
|
|
35
|
-
titleElem?: ReactNode
|
|
36
|
-
|
|
37
|
-
/** Extra elements to add to right */
|
|
38
|
-
extraTitleButtonsElem?: ReactNode
|
|
39
|
-
|
|
40
|
-
/** True to enable quickfilters */
|
|
41
|
-
enableQuickfilters?: boolean
|
|
42
|
-
|
|
43
|
-
/** Locked quickfilter values. See README in quickfilters */
|
|
44
|
-
quickfilterLocks?: any[]
|
|
45
|
-
|
|
46
|
-
/** Initial quickfilter values */
|
|
47
|
-
quickfiltersValues?: any[]
|
|
48
|
-
|
|
49
|
-
/** True to hide title bar and related controls */
|
|
50
|
-
hideTitleBar?: boolean
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
interface MapComponentState {
|
|
54
|
-
undoStack: UndoStack
|
|
55
|
-
transientDesign: MapDesign
|
|
56
|
-
zoomLocked: boolean
|
|
57
|
-
|
|
58
|
-
/** Values of quickfilters */
|
|
59
|
-
quickfiltersValues: any[] | null
|
|
60
|
-
|
|
61
|
-
/** True to hide quickfilters */
|
|
62
|
-
hideQuickfilters: boolean
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/** Map with designer on right */
|
|
66
|
-
export default class MapComponent extends React.Component<MapComponentProps, MapComponentState> {
|
|
67
|
-
static contextType = LocaleContext
|
|
68
|
-
|
|
69
|
-
constructor(props: MapComponentProps) {
|
|
70
|
-
super(props)
|
|
71
|
-
this.state = {
|
|
72
|
-
undoStack: new UndoStack().push(props.design),
|
|
73
|
-
transientDesign: props.design, // Temporary design for read-only maps
|
|
74
|
-
zoomLocked: true,
|
|
75
|
-
quickfiltersValues: props.quickfiltersValues ?? null,
|
|
76
|
-
hideQuickfilters: false
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
componentDidUpdate(prevProps: MapComponentProps) {
|
|
81
|
-
// If design changes, save on stack and update transient design
|
|
82
|
-
if (!_.isEqual(prevProps.design, this.props.design)) {
|
|
83
|
-
// Save on stack
|
|
84
|
-
this.setState({ undoStack: this.state.undoStack.push(this.props.design) })
|
|
85
|
-
|
|
86
|
-
// Update transient design
|
|
87
|
-
this.setState({ transientDesign: this.props.design })
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
handleUndo = () => {
|
|
92
|
-
const undoStack = this.state.undoStack.undo()
|
|
93
|
-
|
|
94
|
-
// We need to use callback as state is applied later
|
|
95
|
-
return this.setState({ undoStack }, () => this.props.onDesignChange!(undoStack.getValue()))
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
handleRedo = () => {
|
|
99
|
-
const undoStack = this.state.undoStack.redo()
|
|
100
|
-
|
|
101
|
-
// We need to use callback as state is applied later
|
|
102
|
-
return this.setState({ undoStack }, () => this.props.onDesignChange!(undoStack.getValue()))
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
handleShowQuickfilters = () => {
|
|
106
|
-
return this.setState({ hideQuickfilters: false })
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
handleZoomLockClick = () => {
|
|
110
|
-
return this.setState({ zoomLocked: !this.state.zoomLocked })
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
renderActionLinks() {
|
|
114
|
-
return R(
|
|
115
|
-
"div",
|
|
116
|
-
null,
|
|
117
|
-
this.props.onDesignChange != null
|
|
118
|
-
? [
|
|
119
|
-
R(
|
|
120
|
-
"a",
|
|
121
|
-
{ key: "lock", className: "btn btn-link btn-sm", onClick: this.handleZoomLockClick },
|
|
122
|
-
R("span", {
|
|
123
|
-
className: `fa ${this.state.zoomLocked ? "fa-lock red" : "fa-unlock green"}`,
|
|
124
|
-
style: { marginRight: 5 }
|
|
125
|
-
}),
|
|
126
|
-
R(PopoverHelpComponent, { placement: "bottom" }, T`Changes to zoom level wont be saved in locked mode`)
|
|
127
|
-
),
|
|
128
|
-
R(
|
|
129
|
-
"a",
|
|
130
|
-
{
|
|
131
|
-
key: "undo",
|
|
132
|
-
className: `btn btn-link btn-sm ${!this.state.undoStack.canUndo() ? "disabled" : ""}`,
|
|
133
|
-
onClick: this.handleUndo
|
|
134
|
-
},
|
|
135
|
-
R("span", { className: "fas fa-caret-left" }),
|
|
136
|
-
R("span", { className: "hide-600px" }, " ", T`Undo`)
|
|
137
|
-
),
|
|
138
|
-
" ",
|
|
139
|
-
R(
|
|
140
|
-
"a",
|
|
141
|
-
{
|
|
142
|
-
key: "redo",
|
|
143
|
-
className: `btn btn-link btn-sm ${!this.state.undoStack.canRedo() ? "disabled" : ""}`,
|
|
144
|
-
onClick: this.handleRedo
|
|
145
|
-
},
|
|
146
|
-
R("span", { className: "fas fa-caret-right" }),
|
|
147
|
-
R("span", { className: "hide-600px" }, " ", T`Redo`)
|
|
148
|
-
)
|
|
149
|
-
]
|
|
150
|
-
: undefined,
|
|
151
|
-
this.state.hideQuickfilters && this.props.design.quickfilters && this.props.design.quickfilters.length > 0
|
|
152
|
-
? R(
|
|
153
|
-
"a",
|
|
154
|
-
{ key: "showQuickfilters", className: "btn btn-link btn-sm", onClick: this.handleShowQuickfilters },
|
|
155
|
-
R("span", { className: "fa fa-filter" }),
|
|
156
|
-
R("span", { className: "hide-600px" }, " ", T`Show Quickfilters`)
|
|
157
|
-
)
|
|
158
|
-
: undefined,
|
|
159
|
-
this.props.extraTitleButtonsElem,
|
|
160
|
-
R("a", { key: "toggleDesign", className: "btn btn-link btn-sm", onClick: this.handleToggleDesignPanel, alt: T`Toggle design panel` },
|
|
161
|
-
this.getDesign().hideDesignPanel ?
|
|
162
|
-
R("span", { className: "fas fa-angle-double-left" })
|
|
163
|
-
: R("span", { className: "fas fa-angle-double-right" }))
|
|
164
|
-
)
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
renderHeader() {
|
|
168
|
-
return R(
|
|
169
|
-
"div",
|
|
170
|
-
{
|
|
171
|
-
style: {
|
|
172
|
-
padding: 4,
|
|
173
|
-
borderBottom: "solid 1px #e8e8e8",
|
|
174
|
-
gridArea: "header"
|
|
175
|
-
}
|
|
176
|
-
},
|
|
177
|
-
R("div", { style: { float: "right" } }, this.renderActionLinks()),
|
|
178
|
-
this.props.titleElem
|
|
179
|
-
)
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
handleDesignChange = (design: any) => {
|
|
183
|
-
if (this.props.onDesignChange) {
|
|
184
|
-
return this.props.onDesignChange(design)
|
|
185
|
-
} else {
|
|
186
|
-
return this.setState({ transientDesign: design })
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
getDesign() {
|
|
191
|
-
if (this.props.onDesignChange) {
|
|
192
|
-
return this.props.design
|
|
193
|
-
} else {
|
|
194
|
-
return this.state.transientDesign || this.props.design
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
handleToggleDesignPanel = () => {
|
|
199
|
-
this.handleDesignChange({ ...this.getDesign(), hideDesignPanel: !this.getDesign().hideDesignPanel })
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Get the values of the quick filters
|
|
203
|
-
getQuickfilterValues = () => {
|
|
204
|
-
return this.state.quickfiltersValues || []
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
renderView() {
|
|
208
|
-
let filters = this.props.extraFilters || []
|
|
209
|
-
|
|
210
|
-
// Compile quickfilters
|
|
211
|
-
filters = filters.concat(
|
|
212
|
-
new QuickfilterCompiler(this.props.schema).compile(
|
|
213
|
-
this.props.design.quickfilters || [],
|
|
214
|
-
this.state.quickfiltersValues,
|
|
215
|
-
this.props.quickfilterLocks
|
|
216
|
-
)
|
|
217
|
-
)
|
|
218
|
-
|
|
219
|
-
return React.createElement(
|
|
220
|
-
AutoSizeComponent,
|
|
221
|
-
{ injectWidth: true, injectHeight: true },
|
|
222
|
-
(size: {
|
|
223
|
-
width?: number;
|
|
224
|
-
height?: number;
|
|
225
|
-
}) => {
|
|
226
|
-
return React.createElement(MapViewComponent, {
|
|
227
|
-
mapDataSource: this.props.mapDataSource,
|
|
228
|
-
schema: this.props.schema,
|
|
229
|
-
dataSource: this.props.dataSource,
|
|
230
|
-
design: this.getDesign(),
|
|
231
|
-
onDesignChange: this.handleDesignChange,
|
|
232
|
-
zoomLocked: this.state.zoomLocked,
|
|
233
|
-
onRowClick: this.props.onRowClick,
|
|
234
|
-
extraFilters: filters,
|
|
235
|
-
locale: this.context,
|
|
236
|
-
width: size.width!,
|
|
237
|
-
height: size.height!
|
|
238
|
-
})
|
|
239
|
-
}
|
|
240
|
-
)
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Get filters from props filters combined with maps filters
|
|
244
|
-
getCompiledFilters() {
|
|
245
|
-
let compiledFilters = getCompiledFilters(
|
|
246
|
-
this.props.design,
|
|
247
|
-
this.props.schema,
|
|
248
|
-
getFilterableTables(this.props.design, this.props.schema)
|
|
249
|
-
)
|
|
250
|
-
compiledFilters = compiledFilters.concat(this.props.extraFilters || [])
|
|
251
|
-
return compiledFilters
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
renderQuickfilter() {
|
|
255
|
-
return R("div", { style: { gridArea: "quickfilters", borderBottom: "solid 1px #e8e8e8" } },
|
|
256
|
-
R(QuickfiltersComponent, {
|
|
257
|
-
design: this.props.design.quickfilters || [],
|
|
258
|
-
schema: this.props.schema,
|
|
259
|
-
dataSource: this.props.dataSource,
|
|
260
|
-
quickfiltersDataSource: this.props.mapDataSource.getQuickfiltersDataSource(),
|
|
261
|
-
values: this.state.quickfiltersValues || undefined,
|
|
262
|
-
onValuesChange: (values: any) => this.setState({ quickfiltersValues: values }),
|
|
263
|
-
locks: this.props.quickfilterLocks,
|
|
264
|
-
filters: this.getCompiledFilters(),
|
|
265
|
-
hideTopBorder: this.props.hideTitleBar,
|
|
266
|
-
onHide: () => this.setState({ hideQuickfilters: true })
|
|
267
|
-
})
|
|
268
|
-
)
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
renderDesigner() {
|
|
272
|
-
return R("div", { style: { gridArea: "designer", borderLeft: "solid 2px #e8e8e8", overflowY: 'scroll' } },
|
|
273
|
-
this.props.onDesignChange ?
|
|
274
|
-
React.createElement(MapDesignerComponent, {
|
|
275
|
-
schema: this.props.schema,
|
|
276
|
-
dataSource: this.props.dataSource,
|
|
277
|
-
design: this.getDesign(),
|
|
278
|
-
onDesignChange: this.handleDesignChange,
|
|
279
|
-
enableQuickfilters: this.props.enableQuickfilters
|
|
280
|
-
})
|
|
281
|
-
:
|
|
282
|
-
React.createElement(MapControlComponent, {
|
|
283
|
-
schema: this.props.schema,
|
|
284
|
-
dataSource: this.props.dataSource,
|
|
285
|
-
design: this.getDesign(),
|
|
286
|
-
onDesignChange: this.handleDesignChange
|
|
287
|
-
})
|
|
288
|
-
)
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
render() {
|
|
292
|
-
const designerVisible = !this.getDesign().hideDesignPanel
|
|
293
|
-
return R(
|
|
294
|
-
"div",
|
|
295
|
-
{
|
|
296
|
-
style: {
|
|
297
|
-
width: "100%",
|
|
298
|
-
height: "100%",
|
|
299
|
-
display: "grid",
|
|
300
|
-
gridTemplateColumns: designerVisible ? "70% 30%" : "100%",
|
|
301
|
-
gridTemplateRows: "auto auto 1fr",
|
|
302
|
-
gridTemplateAreas: `"header designer" "quickfilters designer" "view designer"`
|
|
303
|
-
}
|
|
304
|
-
},
|
|
305
|
-
this.props.hideTitleBar != true ? this.renderHeader() : null,
|
|
306
|
-
this.state.hideQuickfilters ? null : this.renderQuickfilter(),
|
|
307
|
-
R("div", { style: { width: "100%", height: "100%", gridArea: "view", overflow: "hidden" } }, this.renderView()),
|
|
308
|
-
designerVisible ? this.renderDesigner() : null
|
|
309
|
-
)
|
|
310
|
-
}
|
|
311
|
-
}
|