@mwater/visualization 5.0.0 → 5.0.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/MWaterContextComponent.js +1 -4
- package/lib/datagrids/DatagridComponent.d.ts +2 -0
- package/lib/datagrids/DatagridComponent.js +8 -2
- package/lib/datagrids/DatagridViewComponent.d.ts +2 -0
- package/lib/datagrids/DatagridViewComponent.js +3 -2
- package/lib/datagrids/LabeledExprGenerator.js +15 -0
- package/lib/dayjs.d.ts +2 -0
- package/lib/dayjs.js +9 -0
- package/lib/languages.js +5 -0
- package/lib/maps/DetailLevelSelectComponent.d.ts +1 -93
- package/lib/maps/Layer.js +7 -18
- package/lib/maps/MapComponent.js +1 -1
- package/lib/maps/RegionSelectComponent.d.ts +1 -33
- package/lib/maps/VectorMapViewComponent.js +21 -29
- package/lib/quickfilter/QuickfiltersComponent.d.ts +2 -186
- package/lib/quickfilter/QuickfiltersDesignComponent.js +1 -1
- package/lib/quickfilter/TextLiteralComponent.d.ts +2 -186
- package/lib/quickfilter/TextLiteralComponent.js +3 -0
- package/lib/valueFormatter.js +52 -1
- package/lib/widgets/charts/layered/LayeredChartCompiler.d.ts +1 -1
- package/lib/widgets/charts/pivot/PivotChartLayout.d.ts +3 -2
- package/lib/widgets/charts/pivot/PivotChartLayoutComponent.js +4 -1
- package/lib/widgets/charts/pivot/PivotChartQueryBuilder.js +1 -1
- package/lib/widgets/charts/table/TableChart.js +15 -4
- package/lib/widgets/charts/table/TableChartViewComponent.d.ts +2 -1
- package/lib/widgets/charts/table/TableChartViewComponent.js +9 -4
- package/package.json +8 -8
- package/src/MWaterAddRelatedIndicatorComponent.ts +1 -1
- package/src/MWaterContextComponent.ts +1 -4
- package/src/datagrids/DatagridComponent.ts +15 -1
- package/src/datagrids/DatagridViewComponent.ts +6 -2
- package/src/datagrids/LabeledExprGenerator.ts +15 -0
- package/src/dayjs.ts +5 -0
- package/src/languages.ts +5 -0
- package/src/maps/Layer.ts +6 -16
- package/src/maps/MapComponent.ts +1 -1
- package/src/maps/RasterMapViewComponent.ts +0 -1
- package/src/maps/VectorMapViewComponent.tsx +23 -36
- package/src/quickfilter/QuickfiltersDesignComponent.tsx +1 -1
- package/src/quickfilter/TextLiteralComponent.ts +4 -0
- package/src/valueFormatter.ts +54 -1
- package/src/widgets/charts/pivot/PivotChartLayout.ts +3 -2
- package/src/widgets/charts/pivot/PivotChartLayoutBuilder.ts +2 -2
- package/src/widgets/charts/pivot/PivotChartLayoutComponent.tsx +7 -3
- package/src/widgets/charts/pivot/PivotChartQueryBuilder.ts +1 -1
- package/src/widgets/charts/table/TableChart.ts +24 -14
- package/src/widgets/charts/table/TableChartViewComponent.ts +10 -5
- package/stories/dashboards.js +3 -3
|
@@ -7,7 +7,6 @@ const prop_types_1 = __importDefault(require("prop-types"));
|
|
|
7
7
|
const lodash_1 = __importDefault(require("lodash"));
|
|
8
8
|
const react_1 = __importDefault(require("react"));
|
|
9
9
|
const R = react_1.default.createElement;
|
|
10
|
-
const moment_1 = __importDefault(require("moment"));
|
|
11
10
|
const react_linkify_1 = __importDefault(require("react-linkify"));
|
|
12
11
|
const AxisBuilder_1 = __importDefault(require("../../../axes/AxisBuilder"));
|
|
13
12
|
const expressions_1 = require("@mwater/expressions");
|
|
@@ -149,7 +148,13 @@ class TableContentsComponent extends react_1.default.Component {
|
|
|
149
148
|
else {
|
|
150
149
|
// Parse if should be JSON
|
|
151
150
|
if (["image", "imagelist", "geometry", "text[]"].includes(exprType || "") && lodash_1.default.isString(value)) {
|
|
152
|
-
|
|
151
|
+
try {
|
|
152
|
+
value = JSON.parse(value);
|
|
153
|
+
}
|
|
154
|
+
catch (e) {
|
|
155
|
+
// Ignore as can happen when re-arranging columns
|
|
156
|
+
value = null;
|
|
157
|
+
}
|
|
153
158
|
}
|
|
154
159
|
if (column.backgroundColorAxis && row[`bc${columnIndex}`] != null) {
|
|
155
160
|
backgroundColor = axisBuilder.getValueColor(column.backgroundColorAxis, row[`bc${columnIndex}`]) ?? "#fff";
|
|
@@ -170,10 +175,10 @@ class TableContentsComponent extends react_1.default.Component {
|
|
|
170
175
|
node = exprUtils.stringifyExprLiteral(column.textAxis?.expr, value, this.context.locale);
|
|
171
176
|
break;
|
|
172
177
|
case "date":
|
|
173
|
-
node = (0,
|
|
178
|
+
node = (0, valueFormatter_1.formatValue)(exprType, value, column.format);
|
|
174
179
|
break;
|
|
175
180
|
case "datetime":
|
|
176
|
-
node = (0,
|
|
181
|
+
node = (0, valueFormatter_1.formatValue)(exprType, value, column.format);
|
|
177
182
|
break;
|
|
178
183
|
case "image":
|
|
179
184
|
node = this.renderImage(value.id);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mwater/visualization",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.1",
|
|
4
4
|
"description": "Visualization library",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -20,6 +20,11 @@
|
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@babel/runtime": "^7.12.5",
|
|
23
|
+
"@mwater/expressions": "^2.49.3",
|
|
24
|
+
"@mwater/expressions-ui": "^4.13.0",
|
|
25
|
+
"@mwater/forms": "^11.2.7",
|
|
26
|
+
"@mwater/jsonql": "^1.14.0",
|
|
27
|
+
"@mwater/react-library": "^2.0.0",
|
|
23
28
|
"@turf/bbox": "^6.0.1",
|
|
24
29
|
"async": "^1.2.1",
|
|
25
30
|
"async-latest": "^1.0.0",
|
|
@@ -34,6 +39,7 @@
|
|
|
34
39
|
"d3-scale": "^1.0.3",
|
|
35
40
|
"d3-scale-chromatic": "^1.5.0",
|
|
36
41
|
"d3-tip": "^0.9.0",
|
|
42
|
+
"dayjs": "^1.11.10",
|
|
37
43
|
"dompurify": "^1.0.11",
|
|
38
44
|
"ez-localize": "^2.5.0",
|
|
39
45
|
"file-saver": "^2.0.5",
|
|
@@ -42,16 +48,12 @@
|
|
|
42
48
|
"immutable-setter": "^1.1.1",
|
|
43
49
|
"jquery": "^3.6.3",
|
|
44
50
|
"js-yaml": "^3.14.0",
|
|
45
|
-
"@mwater/jsonql": "^1.14.0",
|
|
46
51
|
"leaflet": "^1.8.0",
|
|
47
52
|
"lodash": "^3.1.0",
|
|
48
53
|
"lru-cache": "^6.0.0",
|
|
49
54
|
"maplibre-gl": "^3.3.1",
|
|
50
55
|
"markdown-it": "^12.0.4",
|
|
51
56
|
"moment": "^2.29.1",
|
|
52
|
-
"@mwater/expressions": "^2.49.3",
|
|
53
|
-
"@mwater/expressions-ui": "^4.13.0",
|
|
54
|
-
"@mwater/forms": "^11.2.7",
|
|
55
57
|
"pako": "^1.0.11",
|
|
56
58
|
"prop-types": "^15.7.2",
|
|
57
59
|
"querystring": "^0.2.0",
|
|
@@ -65,14 +67,12 @@
|
|
|
65
67
|
"react-dropzone": "^14.2.2",
|
|
66
68
|
"react-float-affixed": "^1.0.0",
|
|
67
69
|
"react-lazy-load-image-component": "^1.5.1",
|
|
68
|
-
"@mwater/react-library": "^2.0.0",
|
|
69
70
|
"react-linkify": "^0.2.2",
|
|
70
71
|
"react-motion": "^0.5.2",
|
|
71
72
|
"react-onclickout": "^2.0.4",
|
|
72
73
|
"react-select": "^5.1.0",
|
|
73
74
|
"save-svg-as-png": "^1.4.17",
|
|
74
75
|
"shallowequal": "^0.2.2",
|
|
75
|
-
"ui-builder": "^2.0.1",
|
|
76
76
|
"update-object": "^1.0.0",
|
|
77
77
|
"utm": "^1.1.1",
|
|
78
78
|
"uuid": "^3.4.0"
|
|
@@ -108,6 +108,6 @@
|
|
|
108
108
|
"react-addons-test-utils": "^0.14.0",
|
|
109
109
|
"react-test-renderer": "^16.14.0",
|
|
110
110
|
"sinon": "^4.2.0",
|
|
111
|
-
"typescript": "^
|
|
111
|
+
"typescript": "^5.2.2"
|
|
112
112
|
}
|
|
113
113
|
}
|
|
@@ -43,7 +43,7 @@ export default class MWaterAddRelatedIndicatorComponent extends React.Component<
|
|
|
43
43
|
|
|
44
44
|
componentDidMount() {
|
|
45
45
|
// Get all response-type indicators
|
|
46
|
-
const query = {}
|
|
46
|
+
const query: any = {}
|
|
47
47
|
query.selector = JSON.stringify({ type: "response" })
|
|
48
48
|
query.fields = JSON.stringify({
|
|
49
49
|
"design.name": 1,
|
|
@@ -122,11 +122,8 @@ export default class MWaterContextComponent extends React.Component<{
|
|
|
122
122
|
return null
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
//
|
|
125
|
+
// Nothing initially open
|
|
126
126
|
context.isScalarExprTreeSectionInitiallyOpen = (options: any) => {
|
|
127
|
-
if (options.tableId.match(/^entities\./) && options.section.id === "!indicators") {
|
|
128
|
-
return true
|
|
129
|
-
}
|
|
130
127
|
return null
|
|
131
128
|
}
|
|
132
129
|
|
|
@@ -68,6 +68,7 @@ export default class DatagridComponent extends React.Component<
|
|
|
68
68
|
/** Height of quickfilters */
|
|
69
69
|
quickfiltersHeight: number | null
|
|
70
70
|
quickfiltersValues: null | any[]
|
|
71
|
+
refreshKey: number
|
|
71
72
|
}
|
|
72
73
|
> {
|
|
73
74
|
datagridView?: DatagridViewComponent | null
|
|
@@ -81,7 +82,8 @@ export default class DatagridComponent extends React.Component<
|
|
|
81
82
|
editingDesign: false, // is design being edited
|
|
82
83
|
cellEditingEnabled: false, // True if cells can be edited directly
|
|
83
84
|
quickfiltersHeight: null, // Height of quickfilters
|
|
84
|
-
quickfiltersValues: null
|
|
85
|
+
quickfiltersValues: null,
|
|
86
|
+
refreshKey: 1
|
|
85
87
|
}
|
|
86
88
|
}
|
|
87
89
|
|
|
@@ -97,6 +99,11 @@ export default class DatagridComponent extends React.Component<
|
|
|
97
99
|
return this.updateHeight()
|
|
98
100
|
}
|
|
99
101
|
|
|
102
|
+
handleRefreshData = () => {
|
|
103
|
+
this.props.dataSource.clearCache?.()
|
|
104
|
+
this.setState({ refreshKey: this.state.refreshKey + 1 })
|
|
105
|
+
}
|
|
106
|
+
|
|
100
107
|
updateHeight() {
|
|
101
108
|
// Calculate quickfilters height
|
|
102
109
|
if (this.quickfilters) {
|
|
@@ -261,6 +268,12 @@ export default class DatagridComponent extends React.Component<
|
|
|
261
268
|
this.renderFindReplace(),
|
|
262
269
|
this.renderCellEdit(),
|
|
263
270
|
this.renderEditButton(),
|
|
271
|
+
R(
|
|
272
|
+
"a",
|
|
273
|
+
{ key: "refresh", className: "btn btn-link btn-sm", onClick: this.handleRefreshData },
|
|
274
|
+
R("span", { className: "fas fa-sync" }),
|
|
275
|
+
R("span", { className: "hide-600px" }, " Refresh")
|
|
276
|
+
),
|
|
264
277
|
this.props.extraTitleButtonsElem
|
|
265
278
|
),
|
|
266
279
|
this.props.titleElem
|
|
@@ -375,6 +388,7 @@ export default class DatagridComponent extends React.Component<
|
|
|
375
388
|
onRowDoubleClick: this.props.onRowDoubleClick,
|
|
376
389
|
canEditExpr: this.state.cellEditingEnabled ? this.props.canEditExpr : undefined,
|
|
377
390
|
updateExprValues: this.state.cellEditingEnabled ? this.props.updateExprValues : undefined,
|
|
391
|
+
refreshKey: this.state.refreshKey
|
|
378
392
|
})
|
|
379
393
|
} else if (this.props.onDesignChange) {
|
|
380
394
|
return R(
|
|
@@ -38,6 +38,9 @@ export interface DatagridViewComponentProps {
|
|
|
38
38
|
|
|
39
39
|
/** Called when a row is clicked with (tableId, rowId, rowIndex) */
|
|
40
40
|
onRowClick?: (tableId: string, rowId: any, rowIndex: number) => void
|
|
41
|
+
|
|
42
|
+
/** Change to force a refresh */
|
|
43
|
+
refreshKey?: any
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
/** Update to one row expression value */
|
|
@@ -91,7 +94,7 @@ export default class DatagridViewComponent extends React.Component<
|
|
|
91
94
|
componentWillReceiveProps(nextProps: DatagridViewComponentProps) {
|
|
92
95
|
// If design or filters changed, delete all rows
|
|
93
96
|
// TODO won't this reload on column resize?
|
|
94
|
-
if (!_.isEqual(nextProps.design, this.props.design) || !_.isEqual(nextProps.filters, this.props.filters)) {
|
|
97
|
+
if (!_.isEqual(nextProps.design, this.props.design) || !_.isEqual(nextProps.filters, this.props.filters) || nextProps.refreshKey !== this.props.refreshKey) {
|
|
95
98
|
return this.setState({ rows: [], entirelyLoaded: false })
|
|
96
99
|
}
|
|
97
100
|
}
|
|
@@ -452,7 +455,8 @@ export default class DatagridViewComponent extends React.Component<
|
|
|
452
455
|
onRowDoubleClick: this.handleRowDoubleClick,
|
|
453
456
|
onRowClick: this.handleRowClick,
|
|
454
457
|
isColumnResizing: false,
|
|
455
|
-
onColumnResizeEndCallback: this.handleColumnResize
|
|
458
|
+
onColumnResizeEndCallback: this.handleColumnResize,
|
|
459
|
+
touchScrollEnabled: true
|
|
456
460
|
},
|
|
457
461
|
this.renderColumns()
|
|
458
462
|
)
|
|
@@ -140,6 +140,21 @@ export default class LabeledExprGenerator {
|
|
|
140
140
|
joins
|
|
141
141
|
}
|
|
142
142
|
]
|
|
143
|
+
} else if (column.idTable!.match(/^custom./)) { // Support cascading ref question
|
|
144
|
+
return this.schema
|
|
145
|
+
.getColumns(column.idTable!)
|
|
146
|
+
.filter((c: any) => c.id[0] !== "_")
|
|
147
|
+
.map((c: any) => ({
|
|
148
|
+
expr: {
|
|
149
|
+
type: "scalar",
|
|
150
|
+
table,
|
|
151
|
+
joins: [column.id],
|
|
152
|
+
expr: { type: "field", table: column.idTable, column: c.id }
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
label: `${createLabel(column)} > ${createLabel(c)}`,
|
|
156
|
+
joins
|
|
157
|
+
}))
|
|
143
158
|
} else {
|
|
144
159
|
// Use label, code, full name, or name of dest table
|
|
145
160
|
const destTable = this.schema.getTable(column.idTable!)
|
package/src/dayjs.ts
ADDED
package/src/languages.ts
CHANGED
package/src/maps/Layer.ts
CHANGED
|
@@ -312,22 +312,12 @@ export default class Layer<LayerDesign> {
|
|
|
312
312
|
|
|
313
313
|
if (results[0].bounds) {
|
|
314
314
|
const [w, s, e, n] = bbox(results[0].bounds)
|
|
315
|
-
// Pad to
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
n: n + 0.1
|
|
322
|
-
}
|
|
323
|
-
// Pad bounds to prevent too small box (10m)
|
|
324
|
-
} else {
|
|
325
|
-
bounds = {
|
|
326
|
-
w: w - 0.001,
|
|
327
|
-
s: s - 0.001,
|
|
328
|
-
e: e + 0.001,
|
|
329
|
-
n: n + 0.001
|
|
330
|
-
}
|
|
315
|
+
// Pad bounds to prevent too small box (100m)
|
|
316
|
+
bounds = {
|
|
317
|
+
w: w - 0.001,
|
|
318
|
+
s: s - 0.001,
|
|
319
|
+
e: e + 0.001,
|
|
320
|
+
n: n + 0.001
|
|
331
321
|
}
|
|
332
322
|
}
|
|
333
323
|
|
package/src/maps/MapComponent.ts
CHANGED
|
@@ -303,7 +303,7 @@ export default class MapComponent extends React.Component<MapComponentProps, Map
|
|
|
303
303
|
gridTemplateAreas: `"header designer" "quickfilters designer" "view designer"`
|
|
304
304
|
}
|
|
305
305
|
},
|
|
306
|
-
this.renderHeader(),
|
|
306
|
+
this.props.hideTitleBar != true ? this.renderHeader() : null,
|
|
307
307
|
this.state.hideQuickfilters ? null : this.renderQuickfilter(),
|
|
308
308
|
R("div", { style: { width: "100%", height: "100%", gridArea: "view", overflow: "hidden" } }, this.renderView()),
|
|
309
309
|
designerVisible ? this.renderDesigner() : null
|
|
@@ -13,7 +13,6 @@ import { JsonQLFilter } from "../JsonQLFilter"
|
|
|
13
13
|
import { MapDesign } from "./MapDesign"
|
|
14
14
|
import { MapDataSource } from "./MapDataSource"
|
|
15
15
|
import { MapScope } from "./MapUtils"
|
|
16
|
-
import { defaultProps } from "react-select/src/Select"
|
|
17
16
|
|
|
18
17
|
export interface RasterMapViewComponentProps {
|
|
19
18
|
schema: Schema
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import _ from "lodash"
|
|
1
|
+
import _, { find } from "lodash"
|
|
2
2
|
import { LayerSpecification, MapLayerMouseEvent } from "maplibre-gl"
|
|
3
3
|
import { DataSource, Schema } from "@mwater/expressions"
|
|
4
4
|
import React, { CSSProperties, ReactNode, useEffect, useMemo, useState } from "react"
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
getFilterableTables as utilsGetFilterableTables,
|
|
15
15
|
MapScope
|
|
16
16
|
} from "./MapUtils"
|
|
17
|
-
import {
|
|
17
|
+
import {Color} from '@maplibre/maplibre-gl-style-spec';
|
|
18
18
|
|
|
19
19
|
import "maplibre-gl/dist/maplibre-gl.css"
|
|
20
20
|
import "./VectorMapViewComponent.css"
|
|
@@ -115,7 +115,7 @@ export function VectorMapViewComponent(props: {
|
|
|
115
115
|
)
|
|
116
116
|
|
|
117
117
|
// Last feature that mouse entered
|
|
118
|
-
const lastFeature = useRef()
|
|
118
|
+
const lastFeature = useRef<string>()
|
|
119
119
|
|
|
120
120
|
// Load map
|
|
121
121
|
const map = useVectorMap({
|
|
@@ -162,6 +162,7 @@ export function VectorMapViewComponent(props: {
|
|
|
162
162
|
})
|
|
163
163
|
|
|
164
164
|
if (!results) {
|
|
165
|
+
setHoverContents(null)
|
|
165
166
|
return
|
|
166
167
|
}
|
|
167
168
|
|
|
@@ -171,15 +172,6 @@ export function VectorMapViewComponent(props: {
|
|
|
171
172
|
}
|
|
172
173
|
})
|
|
173
174
|
|
|
174
|
-
const handleLayerHoverDebounced = memoizedDebounce(
|
|
175
|
-
(layerViewId: string, ev: { data: any; event: any }) => {
|
|
176
|
-
handleLayerHover(layerViewId, ev)
|
|
177
|
-
},
|
|
178
|
-
250,
|
|
179
|
-
{ leading: true, trailing: false },
|
|
180
|
-
(layerViewId, ev) => ev.data.id
|
|
181
|
-
)
|
|
182
|
-
|
|
183
175
|
/** Handle a click on a layer */
|
|
184
176
|
const handleLayerClick = useStableCallback((layerViewId: string, ev: { data: any; event: any }) => {
|
|
185
177
|
const layerView = props.design.layerViews.find(lv => lv.id == layerViewId)!
|
|
@@ -514,7 +506,7 @@ export function VectorMapViewComponent(props: {
|
|
|
514
506
|
setBusy(b => b - 1)
|
|
515
507
|
|
|
516
508
|
if (bounds) {
|
|
517
|
-
map!.fitBounds([bounds.w, bounds.s, bounds.e, bounds.n], { padding: 20, duration:
|
|
509
|
+
map!.fitBounds([bounds.w, bounds.s, bounds.e, bounds.n], { padding: 20, duration: 2500 })
|
|
518
510
|
|
|
519
511
|
// Also record if editable as part of bounds
|
|
520
512
|
setBounds(bounds)
|
|
@@ -598,42 +590,37 @@ export function VectorMapViewComponent(props: {
|
|
|
598
590
|
return
|
|
599
591
|
}
|
|
600
592
|
|
|
601
|
-
const removes: { (): void }[] = []
|
|
602
593
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
594
|
+
const layers = layerClickHandlers.map(clickHandler => clickHandler.mapLayerId)
|
|
595
|
+
|
|
596
|
+
const onEnter = (ev: MapLayerMouseEvent) => {
|
|
597
|
+
let f = map.queryRenderedFeatures(ev.point, { layers });
|
|
598
|
+
if (f.length) {
|
|
599
|
+
if(f[0].layer.type === 'fill' && f[0].layer.paint?.['fill-color']?.toString() == 'rgba(0,0,0,0)') {
|
|
606
600
|
lastFeature.current = undefined
|
|
607
601
|
setHoverContents(null)
|
|
608
602
|
return
|
|
609
603
|
}
|
|
610
|
-
|
|
611
|
-
|
|
604
|
+
if(lastFeature.current !== f[0].properties.id) {
|
|
605
|
+
lastFeature.current = f[0].properties.id
|
|
612
606
|
setHoverContents(null)
|
|
613
|
-
|
|
607
|
+
const handler = find(layerClickHandlers, {mapLayerId: f[0].layer.id})
|
|
608
|
+
handleLayerHover(handler!.layerViewId, {
|
|
609
|
+
data: f[0].properties,
|
|
610
|
+
event: ev
|
|
611
|
+
})
|
|
614
612
|
}
|
|
615
|
-
|
|
616
|
-
handleLayerHoverDebounced(clickHandler.layerViewId, {
|
|
617
|
-
data: ev.features![0].properties,
|
|
618
|
-
event: ev
|
|
619
|
-
})
|
|
620
|
-
}
|
|
621
|
-
const onLeave = (ev: MapLayerMouseEvent) => {
|
|
613
|
+
} else {
|
|
622
614
|
lastFeature.current = undefined
|
|
623
615
|
setHoverContents(null)
|
|
624
616
|
}
|
|
625
|
-
map.on("mousemove", clickHandler.mapLayerId, onEnter)
|
|
626
|
-
map.on("mouseleave", clickHandler.mapLayerId, onLeave)
|
|
627
|
-
removes.push(() => {
|
|
628
|
-
map.off("mousemove", clickHandler.mapLayerId, onEnter)
|
|
629
|
-
map.off("mouseleave", clickHandler.mapLayerId, onLeave)
|
|
630
|
-
})
|
|
631
617
|
}
|
|
618
|
+
map.on("mousemove", onEnter)
|
|
619
|
+
|
|
632
620
|
return () => {
|
|
633
|
-
|
|
634
|
-
remove()
|
|
635
|
-
}
|
|
621
|
+
map.off("mousemove", onEnter)
|
|
636
622
|
}
|
|
623
|
+
|
|
637
624
|
}, [map, layerClickHandlers])
|
|
638
625
|
|
|
639
626
|
function renderLegend() {
|
|
@@ -210,7 +210,7 @@ class QuickfilterDesignComponent extends React.Component<
|
|
|
210
210
|
table: this.state.table,
|
|
211
211
|
value: this.props.design.expr,
|
|
212
212
|
onChange: this.handleExprChange,
|
|
213
|
-
types: ["enum", "text", "enumset", "date", "datetime", "id[]", "text[]"]
|
|
213
|
+
types: ["enum", "text", "enumset", "date", "datetime", "id[]", "text[]"],
|
|
214
214
|
})
|
|
215
215
|
)
|
|
216
216
|
),
|
|
@@ -51,6 +51,10 @@ export default class TextLiteralComponent extends React.Component<TextLiteralCom
|
|
|
51
51
|
// Create query to get matches
|
|
52
52
|
const exprCompiler = new ExprCompiler(this.props.schema)
|
|
53
53
|
|
|
54
|
+
if(!this.props.expr.table) {
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
|
|
54
58
|
// Add filter for input (simple if just text)
|
|
55
59
|
if (exprType === "text") {
|
|
56
60
|
if (input) {
|
package/src/valueFormatter.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { LiteralType } from "@mwater/expressions"
|
|
2
2
|
import { format as d3Format } from "d3-format"
|
|
3
3
|
import { fromLatLon } from "utm"
|
|
4
|
+
import dayjs from './dayjs'
|
|
4
5
|
|
|
5
6
|
/** Option for list of format options */
|
|
6
7
|
export interface FormatOption {
|
|
@@ -10,7 +11,7 @@ export interface FormatOption {
|
|
|
10
11
|
|
|
11
12
|
/** Determine if can format type */
|
|
12
13
|
export function canFormatType(type: LiteralType): boolean {
|
|
13
|
-
return type == "number" || type == "geometry"
|
|
14
|
+
return type == "number" || type == "geometry" || type === "date" || type === "datetime"
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
/** Get available options for formatting a type. Null if not available */
|
|
@@ -35,6 +36,43 @@ export function getFormatOptions(type: LiteralType): FormatOption[] | null {
|
|
|
35
36
|
]
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
if (type == "date") {
|
|
40
|
+
return [
|
|
41
|
+
{ value: "ll", label: dayjs().format("ll") },
|
|
42
|
+
{ value: "YYYY-MM-DD", label: dayjs().format("YYYY-MM-DD") },
|
|
43
|
+
{ value: "YYYY-MMM-DD", label: dayjs().format("YYYY-MMM-DD") },
|
|
44
|
+
{ value: "YYYY-MM", label: dayjs().format("YYYY-MM") },
|
|
45
|
+
{ value: "YYYY", label: dayjs().format("YYYY") },
|
|
46
|
+
{ value: "MMM YYYY", label: dayjs().format("MMM YYYY") },
|
|
47
|
+
{ value: "MMM DD, YYYY", label: dayjs().format("MMM DD, YYYY") },
|
|
48
|
+
{ value: "MMMM D, YYYY", label: dayjs().format("MMMM D, YYYY") },
|
|
49
|
+
{ value: "MM/DD/YYYY", label: dayjs().format("MM/DD/YYYY") },
|
|
50
|
+
{ value: "DD/MM/YYYY", label: dayjs().format("DD/MM/YYYY") },
|
|
51
|
+
{ value: "DD-MM-YYYY", label: dayjs().format("DD-MM-YYYY") },
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (type == "datetime") {
|
|
56
|
+
return [
|
|
57
|
+
{ value: "lll", label: dayjs().format("lll") },
|
|
58
|
+
{ value: "llll", label: dayjs().format("llll") },
|
|
59
|
+
{ value: "YYYY-MM-DD", label: dayjs().format("YYYY-MM-DD") },
|
|
60
|
+
{ value: "YYYY-MMM-DD", label: dayjs().format("YYYY-MMM-DD") },
|
|
61
|
+
{ value: "YYYY-MM", label: dayjs().format("YYYY-MM") },
|
|
62
|
+
{ value: "YYYY", label: dayjs().format("YYYY") },
|
|
63
|
+
{ value: "MMM YYYY", label: dayjs().format("MMM YYYY") },
|
|
64
|
+
{ value: "MMM DD, YYYY h:mm A", label: dayjs().format("MMM DD, YYYY h:mm A") },
|
|
65
|
+
{ value: "MMMM D, YYYY h:mm A", label: dayjs().format("MMMM D, YYYY h:mm A") },
|
|
66
|
+
{ value: "MMM DD, YYYY", label: dayjs().format("MMM DD, YYYY") },
|
|
67
|
+
{ value: "MMMM D, YYYY", label: dayjs().format("MMMM D, YYYY") },
|
|
68
|
+
{ value: "MM/DD/YYYY", label: dayjs().format("MM/DD/YYYY") },
|
|
69
|
+
{ value: "DD/MM/YYYY", label: dayjs().format("DD/MM/YYYY") },
|
|
70
|
+
{ value: "DD-MM-YYYY", label: dayjs().format("DD-MM-YYYY") },
|
|
71
|
+
{ value: "YYYY-MM-DD HH:mm:ss", label: dayjs().format("YYYY-MM-DD HH:mm:ss") },
|
|
72
|
+
{ value: "ISO 8601", label: dayjs().toISOString() },
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
|
|
38
76
|
return null
|
|
39
77
|
}
|
|
40
78
|
|
|
@@ -48,6 +86,14 @@ export function getDefaultFormat(type: LiteralType): string {
|
|
|
48
86
|
return "lat, lng"
|
|
49
87
|
}
|
|
50
88
|
|
|
89
|
+
if (type == "date") {
|
|
90
|
+
return "ll"
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (type == "datetime") {
|
|
94
|
+
return "lll"
|
|
95
|
+
}
|
|
96
|
+
|
|
51
97
|
throw new Error("Not supported")
|
|
52
98
|
}
|
|
53
99
|
|
|
@@ -78,6 +124,13 @@ export function formatValue(
|
|
|
78
124
|
return d3Format(format)(value)
|
|
79
125
|
}
|
|
80
126
|
|
|
127
|
+
if (["date", "datetime"].includes(type)) {
|
|
128
|
+
if (format == "ISO 8601") {
|
|
129
|
+
return dayjs(value).toISOString()
|
|
130
|
+
}
|
|
131
|
+
return dayjs(value).format(format)
|
|
132
|
+
}
|
|
133
|
+
|
|
81
134
|
if (type == "geometry") {
|
|
82
135
|
if (format == "UTM") {
|
|
83
136
|
if (value.type == "Point") {
|
|
@@ -40,8 +40,9 @@ export interface PivotChartLayoutCell {
|
|
|
40
40
|
align: "left" | "center" | "right"
|
|
41
41
|
|
|
42
42
|
/** section id of either a segment or intersection or blank area. Always a rectangle.
|
|
43
|
-
* Id is like intersection id if intersection, id of segment if segment
|
|
44
|
-
|
|
43
|
+
* Id is like intersection id if intersection, id of segment if segment. Undefined if doesn't
|
|
44
|
+
* exist yet. */
|
|
45
|
+
section?: string
|
|
45
46
|
|
|
46
47
|
/** true if cell is on top edge of section */
|
|
47
48
|
sectionTop?: boolean
|
|
@@ -373,7 +373,7 @@ export default class PivotChartLayoutBuilder {
|
|
|
373
373
|
const intersectionData = dataIndexed[intersectionId]
|
|
374
374
|
|
|
375
375
|
// Create key to lookup value
|
|
376
|
-
const key = {}
|
|
376
|
+
const key: { [rNcN: string]: any } = {}
|
|
377
377
|
for (i = 0; i < row.length; i++) {
|
|
378
378
|
part = row[i]
|
|
379
379
|
key[`r${i}`] = part.value
|
|
@@ -727,7 +727,7 @@ export default class PivotChartLayoutBuilder {
|
|
|
727
727
|
// Sort categories if segment is sorted
|
|
728
728
|
if (segment.orderExpr) {
|
|
729
729
|
// Index the ordering by the JSON.stringify to make it O(n)
|
|
730
|
-
const orderIndex = {}
|
|
730
|
+
const orderIndex: { [key: string]: number } = {}
|
|
731
731
|
const iterable = _.pluck(data[segment.id], "value")
|
|
732
732
|
for (let index = 0; index < iterable.length; index++) {
|
|
733
733
|
value = iterable[index]
|
|
@@ -5,7 +5,7 @@ const R = React.createElement
|
|
|
5
5
|
import Color from "color"
|
|
6
6
|
import * as ui from "@mwater/react-library/lib/bootstrap"
|
|
7
7
|
import classNames from "classnames"
|
|
8
|
-
import { PivotChartLayout, PivotChartLayoutRow } from "./PivotChartLayout"
|
|
8
|
+
import { PivotChartLayout, PivotChartLayoutCell, PivotChartLayoutRow } from "./PivotChartLayout"
|
|
9
9
|
|
|
10
10
|
export interface PivotChartLayoutComponentProps {
|
|
11
11
|
layout: PivotChartLayout
|
|
@@ -71,7 +71,7 @@ export default class PivotChartLayoutComponent extends React.Component<PivotChar
|
|
|
71
71
|
layout: this.props.layout,
|
|
72
72
|
rowIndex,
|
|
73
73
|
columnIndex,
|
|
74
|
-
onHover: this.props.editable ? () => this.setState({ hoverSection: cell.section }) : undefined,
|
|
74
|
+
onHover: this.props.editable ? () => this.setState({ hoverSection: cell.section ?? null }) : undefined,
|
|
75
75
|
hoverSection: this.props.editable ? this.state.hoverSection : undefined,
|
|
76
76
|
onEditSection: this.props.onEditSection ? this.props.onEditSection.bind(null, cell.section) : undefined,
|
|
77
77
|
onSummarizeSegment: this.props.onSummarizeSegment
|
|
@@ -383,7 +383,11 @@ class LayoutCellComponent extends React.Component<LayoutCellComponentProps> {
|
|
|
383
383
|
}
|
|
384
384
|
|
|
385
385
|
// Render an unconfigured cell
|
|
386
|
-
renderUnconfigured(cell:
|
|
386
|
+
renderUnconfigured(cell: PivotChartLayoutCell) {
|
|
387
|
+
if (!cell.section) {
|
|
388
|
+
return null
|
|
389
|
+
}
|
|
390
|
+
|
|
387
391
|
return R(
|
|
388
392
|
"span",
|
|
389
393
|
{ style: { fontSize: "90%" } },
|
|
@@ -108,7 +108,7 @@ export default class PivotChartQueryBuilder {
|
|
|
108
108
|
const columnSegment = columnPath[i]
|
|
109
109
|
query.selects.push({
|
|
110
110
|
type: "select",
|
|
111
|
-
expr:
|
|
111
|
+
expr: compileSegmentAxis(columnSegment.valueAxis),
|
|
112
112
|
alias: `c${i}`
|
|
113
113
|
})
|
|
114
114
|
query.groupBy!.push(i + 1 + rowPath.length)
|