@mwater/visualization 5.0.1 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/GlobalFilter.d.ts +13 -0
- package/lib/GlobalFilter.js +2 -0
- package/lib/MWaterAddRelatedFormComponent.d.ts +1 -1
- package/lib/MWaterAddRelatedFormComponent.js +10 -17
- package/lib/MWaterCompleteTableSelectComponent.d.ts +2 -9
- package/lib/MWaterContextComponent.d.ts +31 -9
- package/lib/MWaterContextComponent.js +85 -76
- package/lib/MWaterCustomTablesetListComponent.js +9 -3
- package/lib/MWaterGlobalFiltersComponent.d.ts +6 -5
- package/lib/MWaterGlobalFiltersComponent.js +4 -4
- package/lib/MWaterLoaderComponent.d.ts +14 -4
- package/lib/MWaterLoaderComponent.js +10 -2
- package/lib/MWaterTableSelectComponent.d.ts +0 -1
- package/lib/MWaterTableSelectComponent.js +20 -41
- package/lib/axes/Axis.d.ts +20 -25
- package/lib/axes/AxisBuilder.js +9 -7
- package/lib/axes/AxisComponent.d.ts +4 -4
- package/lib/axes/RangesComponent.d.ts +12 -6
- package/lib/axes/RangesComponent.js +21 -10
- package/lib/dashboards/DashboardComponent.d.ts +1 -14
- package/lib/dashboards/DashboardComponent.js +18 -56
- package/lib/dashboards/DashboardDesign.d.ts +2 -17
- package/lib/dashboards/DashboardViewComponent.js +3 -4
- package/lib/dashboards/LayoutOptionsComponent.js +4 -3
- package/lib/dashboards/ServerDashboardDataSource.d.ts +1 -0
- package/lib/dashboards/ServerDashboardDataSource.js +3 -0
- package/lib/dashboards/SettingsModalComponent.d.ts +4 -15
- package/lib/dashboards/SettingsModalComponent.js +24 -38
- package/lib/datagrids/DatagridComponent.d.ts +10 -13
- package/lib/datagrids/DatagridComponent.js +27 -5
- package/lib/datagrids/DatagridDataSource.d.ts +3 -2
- package/lib/datagrids/DatagridDataSource.js +0 -11
- package/lib/datagrids/DatagridDesign.d.ts +2 -0
- package/lib/datagrids/DatagridDesignerComponent.d.ts +2 -93
- package/lib/datagrids/DatagridDesignerComponent.js +11 -8
- package/lib/datagrids/DatagridViewComponent.js +2 -2
- package/lib/datagrids/DirectDatagridDataSource.d.ts +1 -0
- package/lib/datagrids/DirectDatagridDataSource.js +26 -0
- package/lib/datagrids/FindReplaceModalComponent.d.ts +4 -20
- package/lib/datagrids/FindReplaceModalComponent.js +27 -13
- package/lib/datagrids/ServerDatagridDataSource.d.ts +2 -1
- package/lib/datagrids/ServerDatagridDataSource.js +16 -3
- package/lib/demo.js +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +2 -4
- package/lib/layouts/blocks/BlocksDisplayComponent.d.ts +2 -1
- package/lib/layouts/blocks/BlocksDisplayComponent.js +2 -2
- package/lib/layouts/grid/GridLayoutManager.d.ts +2 -1
- package/lib/layouts/grid/LegoLayoutEngine.d.ts +1 -1
- package/lib/maps/BufferLayer.js +3 -1
- package/lib/maps/BufferLayerDesignerComponent.js +2 -2
- package/lib/maps/ChoroplethLayerDesigner.js +2 -2
- package/lib/maps/ClusterLayer.js +3 -1
- package/lib/maps/ClusterLayerDesignerComponent.js +2 -2
- package/lib/maps/DirectMapDataSource.js +1 -2
- package/lib/maps/GridLayer.js +5 -3
- package/lib/maps/GridLayerDesigner.js +2 -3
- package/lib/maps/LayerSwitcherComponent.js +1 -1
- package/lib/maps/MapComponent.d.ts +2 -7
- package/lib/maps/MapDesign.d.ts +2 -13
- package/lib/maps/MapDesignerComponent.d.ts +1 -12
- package/lib/maps/MapDesignerComponent.js +5 -12
- package/lib/maps/MapFiltersDesignerComponent.d.ts +0 -4
- package/lib/maps/MapFiltersDesignerComponent.js +4 -5
- package/lib/maps/MarkersLayerDesignerComponent.js +2 -2
- package/lib/maps/PopupFilterJoinsUtils.d.ts +6 -1
- package/lib/maps/PopupFilterJoinsUtils.js +4 -3
- package/lib/maps/RasterMapViewComponent.d.ts +2 -9
- package/lib/maps/RegionSelectComponent.d.ts +2 -1
- package/lib/maps/ServerMapDataSource.d.ts +1 -1
- package/lib/maps/UtfGridLayer.js +1 -1
- package/lib/maps/vectorMaps.d.ts +1 -0
- package/lib/maps/vectorMaps.js +10 -2
- package/lib/quickfilter/QuickfilterCompiler.d.ts +1 -1
- package/lib/widgets/IFrameWidgetComponent.d.ts +2 -9
- package/lib/widgets/ImageWidgetComponent.d.ts +6 -24
- package/lib/widgets/ImageWidgetComponent.js +2 -2
- package/lib/widgets/MapWidget.d.ts +2 -7
- package/lib/widgets/MarkdownWidget.d.ts +2 -7
- package/lib/widgets/TOCWidget.d.ts +2 -9
- package/lib/widgets/charts/ChartWidget.d.ts +3 -15
- package/lib/widgets/charts/calendar/CalendarChartDesignerComponent.js +2 -2
- package/lib/widgets/charts/imagemosaic/ImageMosaicChart.d.ts +1 -1
- package/lib/widgets/charts/imagemosaic/ImageMosaicChart.js +1 -1
- package/lib/widgets/charts/imagemosaic/ImageMosaicChartDesignerComponent.js +2 -2
- package/lib/widgets/charts/imagemosaic/ImagePopupComponent.d.ts +2 -7
- package/lib/widgets/charts/layered/LayeredChartDesignerComponent.d.ts +2 -31
- package/lib/widgets/charts/layered/LayeredChartLayerDesignerComponent.d.ts +2 -7
- package/lib/widgets/charts/layered/LayeredChartLayerDesignerComponent.js +2 -2
- package/lib/widgets/charts/pivot/IntersectionDesignerComponent.d.ts +73 -66
- package/lib/widgets/charts/pivot/PivotChartDesignerComponent.d.ts +10 -6
- package/lib/widgets/charts/pivot/PivotChartDesignerComponent.js +2 -2
- package/lib/widgets/charts/pivot/PivotChartViewComponent.d.ts +3 -22
- package/lib/widgets/charts/pivot/SegmentDesignerComponent.d.ts +55 -58
- package/lib/widgets/charts/table/TableChartDesignerComponent.js +2 -2
- package/lib/widgets/charts/table/TableChartViewComponent.js +21 -7
- package/lib/widgets/text/ExprInsertModalComponent.d.ts +2 -13
- package/lib/widgets/text/ExprItemEditorComponent.js +2 -2
- package/lib/widgets/text/ExprUpdateModalComponent.d.ts +2 -13
- package/lib/widgets/text/TextWidgetDesign.d.ts +3 -1
- package/package.json +1 -1
- package/src/GlobalFilter.ts +17 -0
- package/src/MWaterAddRelatedFormComponent.ts +15 -20
- package/src/MWaterContextComponent.tsx +158 -0
- package/src/MWaterCustomTablesetListComponent.tsx +21 -3
- package/src/MWaterGlobalFiltersComponent.ts +8 -8
- package/src/MWaterLoaderComponent.ts +10 -3
- package/src/{MWaterTableSelectComponent.ts → MWaterTableSelectComponent.tsx} +61 -66
- package/src/axes/Axis.ts +24 -25
- package/src/axes/AxisBuilder.ts +10 -9
- package/src/axes/RangesComponent.ts +27 -16
- package/src/dashboards/{DashboardComponent.ts → DashboardComponent.tsx} +39 -80
- package/src/dashboards/DashboardDesign.ts +2 -22
- package/src/dashboards/DashboardViewComponent.ts +4 -5
- package/src/dashboards/LayoutOptionsComponent.tsx +6 -4
- package/src/dashboards/ServerDashboardDataSource.ts +16 -12
- package/src/dashboards/SettingsModalComponent.tsx +170 -0
- package/src/datagrids/DatagridComponent.ts +45 -14
- package/src/datagrids/DatagridDataSource.ts +10 -8
- package/src/datagrids/DatagridDesign.ts +3 -0
- package/src/datagrids/DatagridDesignerComponent.tsx +31 -19
- package/src/datagrids/DatagridViewComponent.ts +4 -4
- package/src/datagrids/DirectDatagridDataSource.ts +35 -0
- package/src/datagrids/ExprCellComponent.ts +0 -1
- package/src/datagrids/FindReplaceModalComponent.ts +39 -22
- package/src/datagrids/ServerDatagridDataSource.ts +23 -6
- package/src/demo.ts +1 -1
- package/src/index.ts +1 -2
- package/src/layouts/blocks/BlocksDisplayComponent.ts +2 -2
- package/src/layouts/grid/LegoLayoutEngine.ts +2 -2
- package/src/layouts/grid/WidgetContainerComponent.ts +2 -2
- package/src/maps/BingLayer.ts +2 -2
- package/src/maps/BufferLayer.ts +3 -1
- package/src/maps/BufferLayerDesignerComponent.ts +1 -1
- package/src/maps/ChoroplethLayerDesigner.tsx +1 -1
- package/src/maps/ClusterLayer.ts +3 -1
- package/src/maps/ClusterLayerDesignerComponent.ts +1 -1
- package/src/maps/DirectMapDataSource.ts +1 -2
- package/src/maps/GridLayer.ts +5 -3
- package/src/maps/GridLayerDesigner.tsx +1 -2
- package/src/maps/LayerSwitcherComponent.tsx +1 -1
- package/src/maps/LegendGroup.ts +1 -1
- package/src/maps/MWaterServerLayer.ts +2 -2
- package/src/maps/MapDesign.ts +2 -17
- package/src/maps/{MapDesignerComponent.ts → MapDesignerComponent.tsx} +8 -16
- package/src/maps/{MapFiltersDesignerComponent.ts → MapFiltersDesignerComponent.tsx} +25 -25
- package/src/maps/MarkersLayerDesignerComponent.ts +1 -1
- package/src/maps/PopupFilterJoinsUtils.ts +4 -4
- package/src/maps/ServerMapDataSource.ts +7 -7
- package/src/maps/SwitchableTileUrlLayerDesigner.tsx +1 -13
- package/src/maps/UtfGridLayer.ts +4 -4
- package/src/maps/VectorMapViewComponent.tsx +0 -1
- package/src/maps/mapboxUtils.ts +2 -2
- package/src/maps/vectorMaps.tsx +10 -1
- package/src/quickfilter/QuickfilterCompiler.ts +1 -1
- package/src/richtext/ExprItemsHtmlConverter.ts +1 -1
- package/src/richtext/FontColorPaletteItem.ts +1 -1
- package/src/richtext/FontSizePaletteItem.ts +1 -1
- package/src/richtext/ItemsHtmlConverter.ts +2 -2
- package/src/widgets/ImageWidgetComponent.ts +1 -1
- package/src/widgets/charts/calendar/CalendarChartDesignerComponent.ts +1 -1
- package/src/widgets/charts/imagemosaic/ImageMosaicChart.ts +1 -1
- package/src/widgets/charts/imagemosaic/ImageMosaicChartDesignerComponent.ts +1 -1
- package/src/widgets/charts/layered/LayeredChartLayerDesignerComponent.tsx +1 -1
- package/src/widgets/charts/pivot/PivotChartDesignerComponent.tsx +1 -1
- package/src/widgets/charts/table/TableChartDesignerComponent.ts +1 -1
- package/src/widgets/charts/table/TableChartViewComponent.ts +21 -7
- package/src/widgets/text/ExprItemEditorComponent.tsx +1 -1
- package/src/widgets/text/TextWidgetDesign.ts +4 -1
- package/src/MWaterContextComponent.ts +0 -141
- package/src/TableSelectComponent.ts +0 -60
- package/src/dashboards/SettingsModalComponent.ts +0 -169
|
@@ -2,19 +2,19 @@ import _ from "lodash"
|
|
|
2
2
|
import React from "react"
|
|
3
3
|
const R = React.createElement
|
|
4
4
|
|
|
5
|
-
import querystring from "querystring"
|
|
6
5
|
import * as ui from "@mwater/react-library/lib/bootstrap"
|
|
7
6
|
import { IdLiteralComponent } from "@mwater/expressions-ui"
|
|
8
7
|
import { DataSource, Schema } from "@mwater/expressions"
|
|
8
|
+
import { GlobalFilter } from "./GlobalFilter"
|
|
9
9
|
|
|
10
10
|
export interface MWaterGlobalFiltersComponentProps {
|
|
11
11
|
/** Schema of the database */
|
|
12
12
|
schema: Schema
|
|
13
13
|
/** Data source to use to get values */
|
|
14
14
|
dataSource: DataSource
|
|
15
|
-
filterableTables:
|
|
16
|
-
globalFilters?:
|
|
17
|
-
onChange:
|
|
15
|
+
filterableTables: string[]
|
|
16
|
+
globalFilters?: GlobalFilter[]
|
|
17
|
+
onChange: (globalFilters: GlobalFilter[]) => void
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
// Control to edit the global filters (_managed_by and admin_region)
|
|
@@ -36,7 +36,7 @@ export default class MWaterGlobalFiltersComponent extends React.Component<MWater
|
|
|
36
36
|
})
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
this.props.onChange(globalFilters)
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
handleManagedByChange = (managedBy: any) => {
|
|
@@ -56,13 +56,13 @@ export default class MWaterGlobalFiltersComponent extends React.Component<MWater
|
|
|
56
56
|
})
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
this.props.onChange(globalFilters)
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
render() {
|
|
63
63
|
// Find managed by
|
|
64
64
|
let adminRegions, managedBy
|
|
65
|
-
const managedByEntry = _.find(this.props.globalFilters, (gf) => gf.op === "within" && gf.columnId === "_managed_by")
|
|
65
|
+
const managedByEntry = _.find(this.props.globalFilters || [], (gf) => gf.op === "within" && gf.columnId === "_managed_by")
|
|
66
66
|
if (managedByEntry) {
|
|
67
67
|
managedBy = managedByEntry.exprs[0].value.split(":")[1]
|
|
68
68
|
} else {
|
|
@@ -71,7 +71,7 @@ export default class MWaterGlobalFiltersComponent extends React.Component<MWater
|
|
|
71
71
|
|
|
72
72
|
// Find admin region
|
|
73
73
|
const adminRegionEntry = _.find(
|
|
74
|
-
this.props.globalFilters,
|
|
74
|
+
this.props.globalFilters || [],
|
|
75
75
|
(gf) => gf.op === "within any" && gf.columnId === "admin_region"
|
|
76
76
|
)
|
|
77
77
|
if (adminRegionEntry) {
|
|
@@ -10,7 +10,7 @@ import MWaterContextComponent from "./MWaterContextComponent"
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Loads an mWater schema from the server and creates child with schema and dataSource
|
|
13
|
-
* Also creates
|
|
13
|
+
* Also creates context to allow selecting of a table in an mWater-friendly way
|
|
14
14
|
* and several other context items
|
|
15
15
|
*/
|
|
16
16
|
export default class MWaterLoaderComponent extends AsyncLoadComponent<
|
|
@@ -32,7 +32,7 @@ export default class MWaterLoaderComponent extends AsyncLoadComponent<
|
|
|
32
32
|
addLayerElementFactory?: any
|
|
33
33
|
children: (error: any, config?: { schema: Schema; dataSource: DataSource }) => ReactElement<any>
|
|
34
34
|
/** Custom error formatter that returns React node or string, gets passed the error response from server */
|
|
35
|
-
errorFormatter
|
|
35
|
+
errorFormatter?: (data: any, defaultError: string) => string
|
|
36
36
|
},
|
|
37
37
|
{
|
|
38
38
|
error: any
|
|
@@ -79,7 +79,14 @@ export default class MWaterLoaderComponent extends AsyncLoadComponent<
|
|
|
79
79
|
if (error) {
|
|
80
80
|
const defaultError = `Cannot load one of the forms that this depends on. Perhaps the administrator has not shared the form with you? Details: ${error.message}`
|
|
81
81
|
if (this.props.errorFormatter) {
|
|
82
|
-
|
|
82
|
+
try {
|
|
83
|
+
const parsedError = JSON.parse(error.message)
|
|
84
|
+
if (parsedError) {
|
|
85
|
+
return callback({ error: this.props.errorFormatter(parsedError, defaultError) })
|
|
86
|
+
}
|
|
87
|
+
} catch (e) {
|
|
88
|
+
// Ignore
|
|
89
|
+
}
|
|
83
90
|
}
|
|
84
91
|
return callback({ error: defaultError })
|
|
85
92
|
}
|
|
@@ -7,6 +7,7 @@ import { ExprUtils, Schema } from "@mwater/expressions"
|
|
|
7
7
|
import MWaterResponsesFilterComponent from "./MWaterResponsesFilterComponent"
|
|
8
8
|
import ModalPopupComponent from "@mwater/react-library/lib/ModalPopupComponent"
|
|
9
9
|
import MWaterCompleteTableSelectComponent from "./MWaterCompleteTableSelectComponent"
|
|
10
|
+
import { ActiveTablesContext } from "@mwater/expressions-ui"
|
|
10
11
|
|
|
11
12
|
export interface MWaterTableSelectComponentProps {
|
|
12
13
|
/** Url to hit api */
|
|
@@ -37,10 +38,8 @@ export default class MWaterTableSelectComponent extends React.Component<
|
|
|
37
38
|
> {
|
|
38
39
|
static contextTypes = {
|
|
39
40
|
locale: PropTypes.string, // e.g. "en"
|
|
40
|
-
|
|
41
|
-
// Optional list of tables (ids) being used. Use this to present an initially short list to select from
|
|
42
|
-
activeTables: PropTypes.arrayOf(PropTypes.string.isRequired)
|
|
43
41
|
}
|
|
42
|
+
|
|
44
43
|
toggleEdit: any
|
|
45
44
|
|
|
46
45
|
constructor(props: any) {
|
|
@@ -187,9 +186,6 @@ class EditModeTableSelectComponent extends React.Component<
|
|
|
187
186
|
> {
|
|
188
187
|
static contextTypes = {
|
|
189
188
|
locale: PropTypes.string, // e.g. "en"
|
|
190
|
-
|
|
191
|
-
// Optional list of tables (ids) being used. Use this to present an initially short list to select from
|
|
192
|
-
activeTables: PropTypes.arrayOf(PropTypes.string.isRequired)
|
|
193
189
|
}
|
|
194
190
|
|
|
195
191
|
constructor(props: any) {
|
|
@@ -208,8 +204,8 @@ class EditModeTableSelectComponent extends React.Component<
|
|
|
208
204
|
// Get list of tables that should be included in shortlist
|
|
209
205
|
// This is all active tables and all responses tables in schema (so as to include rosters) and all extra tables
|
|
210
206
|
// Also includes current table
|
|
211
|
-
getTableShortlist(): string[] {
|
|
212
|
-
let tables: string[] =
|
|
207
|
+
getTableShortlist(activeTables: string[]): string[] {
|
|
208
|
+
let tables: string[] = activeTables
|
|
213
209
|
|
|
214
210
|
// Remove dead tables
|
|
215
211
|
tables = tables.filter(
|
|
@@ -253,64 +249,63 @@ class EditModeTableSelectComponent extends React.Component<
|
|
|
253
249
|
}
|
|
254
250
|
|
|
255
251
|
render() {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
)
|
|
252
|
+
return (
|
|
253
|
+
<ActiveTablesContext.Consumer>
|
|
254
|
+
{activeTables => (
|
|
255
|
+
<div>
|
|
256
|
+
{this.state.completeMode ? (
|
|
257
|
+
<ModalPopupComponent
|
|
258
|
+
header="Select Data Source"
|
|
259
|
+
onClose={() => this.setState({ completeMode: false })}
|
|
260
|
+
showCloseX={true}
|
|
261
|
+
size="x-large"
|
|
262
|
+
>
|
|
263
|
+
<MWaterCompleteTableSelectComponent
|
|
264
|
+
apiUrl={this.props.apiUrl}
|
|
265
|
+
client={this.props.client}
|
|
266
|
+
schema={this.props.schema}
|
|
267
|
+
user={this.props.user}
|
|
268
|
+
table={this.props.table}
|
|
269
|
+
onChange={this.handleCompleteChange}
|
|
270
|
+
extraTables={this.props.extraTables}
|
|
271
|
+
onExtraTablesChange={this.props.onExtraTablesChange}
|
|
272
|
+
/>
|
|
273
|
+
</ModalPopupComponent>
|
|
274
|
+
) : null}
|
|
275
|
+
|
|
276
|
+
{this.getTableShortlist(activeTables).length > 0 ? (
|
|
277
|
+
<>
|
|
278
|
+
<div className="text-muted">Select Data Source:</div>
|
|
279
|
+
<OptionListComponent items={this.getTableShortlist(activeTables).map((tableId) => {
|
|
280
|
+
const table = this.props.schema.getTable(tableId)!
|
|
281
|
+
return {
|
|
282
|
+
name: ExprUtils.localizeString(table.name, this.context.locale),
|
|
283
|
+
desc: ExprUtils.localizeString(table.desc, this.context.locale),
|
|
284
|
+
onClick: () => this.props.onChange(table.id)
|
|
285
|
+
}
|
|
286
|
+
})} />
|
|
287
|
+
<div>
|
|
288
|
+
<button
|
|
289
|
+
type="button"
|
|
290
|
+
className="btn btn-link btn-sm"
|
|
291
|
+
onClick={this.handleShowMore}
|
|
292
|
+
>
|
|
293
|
+
Show All Available Data Sources...
|
|
294
|
+
</button>
|
|
295
|
+
</div>
|
|
296
|
+
</>
|
|
297
|
+
) : (
|
|
298
|
+
<button
|
|
299
|
+
type="button"
|
|
300
|
+
className="btn btn-link"
|
|
301
|
+
onClick={this.handleShowMore}
|
|
302
|
+
>
|
|
303
|
+
Select Data Source...
|
|
304
|
+
</button>
|
|
305
|
+
)}
|
|
306
|
+
</div>
|
|
307
|
+
)}
|
|
308
|
+
</ActiveTablesContext.Consumer>
|
|
314
309
|
)
|
|
315
310
|
}
|
|
316
311
|
}
|
package/src/axes/Axis.ts
CHANGED
|
@@ -5,37 +5,19 @@ import { Expr } from "@mwater/expressions"
|
|
|
5
5
|
An axis is an expression with optional aggregation, transform and color mapping
|
|
6
6
|
In ggplot2 parlance, an "aesthetic"
|
|
7
7
|
|
|
8
|
-
It contains:
|
|
9
|
-
expr: expression
|
|
10
|
-
aggr: DEPRECATED: optional aggregation (e.g. sum)
|
|
11
|
-
xform: optional transformation to be applied. object with `type` field. See below
|
|
12
|
-
colorMap: optional array of color mappings. See below
|
|
13
|
-
excludedValues: Array of post-xform values to exclude when displaying.
|
|
14
|
-
format: optional d3-format format for numeric values. Default if null is ","
|
|
15
|
-
|
|
16
|
-
## Xforms
|
|
17
|
-
|
|
18
|
-
types:
|
|
19
|
-
|
|
20
|
-
|
|
21
8
|
*/
|
|
22
9
|
export interface Axis {
|
|
10
|
+
/** Expression to be displayed on axis */
|
|
23
11
|
expr: Expr
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
*
|
|
27
|
-
* `year`: year only e.g. `2015-01-01`. type date
|
|
28
|
-
* `yearmonth`: year and month only e.g. `2015-02-01`. type date
|
|
29
|
-
* `yearquarter`: year and quarter only e.g. `2015-01`. type enum
|
|
30
|
-
* `yearweek`: year and week (ISO) only e.g. `2015-31`. type enum
|
|
31
|
-
* `month`: month only e.g. `02`. type enum
|
|
32
|
-
* `week`: ISO week of year e.g. `01` for the first week that contains January 4th
|
|
33
|
-
* `ranges`: convert to ranges. type enum. `ranges` is array of { id (unique id), label (optional label), minValue (null for none), maxValue (null for none), minOpen (true for >, false for >=), maxOpen (true for <, false for <=) }
|
|
34
|
-
* `floor`: convert to floor. type enum
|
|
12
|
+
|
|
13
|
+
/** Optional transformation to be applied. If a date is used, for example, it's generally
|
|
14
|
+
* better to use a transform to convert to a year or month rather than creating an expression.
|
|
35
15
|
*/
|
|
36
16
|
xform?: AxisXform
|
|
37
17
|
|
|
18
|
+
/** optional array of color mappings */
|
|
38
19
|
colorMap?: ColorMap
|
|
20
|
+
|
|
39
21
|
/** optional array of category values which define the order in which categories should be rendered */
|
|
40
22
|
drawOrder?: any[]
|
|
41
23
|
|
|
@@ -45,12 +27,16 @@ export interface Axis {
|
|
|
45
27
|
/** optional string for null category */
|
|
46
28
|
nullLabel?: string
|
|
47
29
|
|
|
30
|
+
/** Array of post-xform values to exclude when displaying. */
|
|
48
31
|
excludedValues?: any[]
|
|
32
|
+
|
|
33
|
+
/** optional d3-format format for numeric values. Default if null is "," */
|
|
49
34
|
format?: string
|
|
50
35
|
|
|
51
|
-
/** @deprecated */
|
|
36
|
+
/** @deprecated optional aggregation (e.g. sum) */
|
|
52
37
|
aggr?: string
|
|
53
38
|
}
|
|
39
|
+
|
|
54
40
|
/**
|
|
55
41
|
* Color map
|
|
56
42
|
* Array of { value: post-transform value of expression, color: html color }
|
|
@@ -70,6 +56,19 @@ export interface AxisCategory {
|
|
|
70
56
|
}
|
|
71
57
|
|
|
72
58
|
export interface AxisXform {
|
|
59
|
+
/**
|
|
60
|
+
* Type of transformation
|
|
61
|
+
* `bin`: convert into bins. always has `numBins` integer and `min` and `max`. Can have `excludeUpper` and/or `excludeLower` to remove open bin on top or bottem. type enum
|
|
62
|
+
* `date`: convert to complete date e.g. `2015-02-08`. type date
|
|
63
|
+
* `year`: year only e.g. `2015-01-01`. type date
|
|
64
|
+
* `yearmonth`: year and month only e.g. `2015-02-01`. type date
|
|
65
|
+
* `yearquarter`: year and quarter only e.g. `2015-01`. type enum
|
|
66
|
+
* `yearweek`: year and week (ISO) only e.g. `2015-31`. type enum
|
|
67
|
+
* `month`: month only e.g. `02`. type enum
|
|
68
|
+
* `week`: ISO week of year e.g. `01` for the first week that contains January 4th
|
|
69
|
+
* `ranges`: convert to ranges. type enum. `ranges` is array of { id (unique id), label (optional label), minValue (null for none), maxValue (null for none), minOpen (true for >, false for >=), maxOpen (true for <, false for <=) }
|
|
70
|
+
* `floor`: convert to floor. type enum
|
|
71
|
+
*/
|
|
73
72
|
type: "bin" | "date" | "year" | "yearmonth" | "month" | "week" | "ranges" | "yearweek" | "yearquarter" | "floor"
|
|
74
73
|
numBins?: number
|
|
75
74
|
min?: number
|
package/src/axes/AxisBuilder.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { default as produce } from "immer"
|
|
|
12
12
|
import { formatValue } from "../valueFormatter"
|
|
13
13
|
import { Axis, AxisCategory } from "./Axis"
|
|
14
14
|
import { JsonQLExpr, JsonQLSelectQuery } from "@mwater/jsonql"
|
|
15
|
+
import dayjs from '../dayjs'
|
|
15
16
|
|
|
16
17
|
const xforms: { type: string; input: LiteralType; output: LiteralType }[] = [
|
|
17
18
|
{ type: "bin", input: "number", output: "enum" },
|
|
@@ -253,7 +254,7 @@ export default class AxisBuilder {
|
|
|
253
254
|
if (options.axis.xform.excludeUpper) {
|
|
254
255
|
const thresholds = _.map(
|
|
255
256
|
_.range(0, options.axis.xform.numBins),
|
|
256
|
-
(bin) => min + ((max - min) * bin) / options.axis
|
|
257
|
+
(bin) => min + ((max - min) * bin) / options.axis!.xform!.numBins!
|
|
257
258
|
)
|
|
258
259
|
thresholds.push(max + epsilon)
|
|
259
260
|
compiledExpr = {
|
|
@@ -913,10 +914,9 @@ export default class AxisBuilder {
|
|
|
913
914
|
if (values.length === 0) {
|
|
914
915
|
return [noneCategory]
|
|
915
916
|
}
|
|
916
|
-
|
|
917
917
|
if (options.onlyValuesPresent) {
|
|
918
918
|
// Sort and take only present
|
|
919
|
-
categories = _.sortBy(_.uniq(values), item => item).map(value => ({ value, label:
|
|
919
|
+
categories = _.sortBy(_.uniq(values), item => item).map(value => ({ value, label: formatValue("date", value, axis.format)}))
|
|
920
920
|
|
|
921
921
|
if (hasNone) {
|
|
922
922
|
categories.push(noneCategory)
|
|
@@ -930,12 +930,12 @@ export default class AxisBuilder {
|
|
|
930
930
|
max = values.sort().slice(-1)[0]
|
|
931
931
|
|
|
932
932
|
// Use moment to get range
|
|
933
|
-
current =
|
|
934
|
-
end =
|
|
933
|
+
current = dayjs(min)
|
|
934
|
+
end = dayjs(max)
|
|
935
935
|
categories = []
|
|
936
936
|
while (!current.isAfter(end)) {
|
|
937
|
-
categories.push({ value: current.format("YYYY-MM-DD"), label: current.format("ll") })
|
|
938
|
-
current.add(1, "days")
|
|
937
|
+
categories.push({ value: current.format("YYYY-MM-DD"), label: current.format(axis.format ?? "ll") })
|
|
938
|
+
current = current.add(1, "days")
|
|
939
939
|
if (categories.length >= 1000) {
|
|
940
940
|
break
|
|
941
941
|
}
|
|
@@ -1055,9 +1055,10 @@ export default class AxisBuilder {
|
|
|
1055
1055
|
_.map(value as string[], (v, i) => R("div", { key: i }, v))
|
|
1056
1056
|
)
|
|
1057
1057
|
case "date":
|
|
1058
|
-
|
|
1058
|
+
console.log(axis)
|
|
1059
|
+
return formatValue(type, value, axis.format)
|
|
1059
1060
|
case "datetime":
|
|
1060
|
-
return
|
|
1061
|
+
return formatValue(type, value, axis.format)
|
|
1061
1062
|
}
|
|
1062
1063
|
|
|
1063
1064
|
return "" + value
|
|
@@ -10,6 +10,7 @@ import NumberInputComponent from "@mwater/react-library/lib/NumberInputComponent
|
|
|
10
10
|
import ReorderableListComponent from "@mwater/react-library/lib/reorderable/ReorderableListComponent"
|
|
11
11
|
import { AxisXform, AxisXformRange } from "./Axis"
|
|
12
12
|
import { Expr, Schema } from "@mwater/expressions"
|
|
13
|
+
import produce from "immer"
|
|
13
14
|
|
|
14
15
|
export interface RangesComponentProps {
|
|
15
16
|
schema: Schema
|
|
@@ -102,9 +103,9 @@ export default class RangesComponent extends React.Component<RangesComponentProp
|
|
|
102
103
|
|
|
103
104
|
interface RangeComponentProps {
|
|
104
105
|
/** Range to edit */
|
|
105
|
-
range:
|
|
106
|
-
onChange:
|
|
107
|
-
onRemove:
|
|
106
|
+
range: AxisXformRange
|
|
107
|
+
onChange: (range: AxisXformRange) => void
|
|
108
|
+
onRemove: () => void
|
|
108
109
|
/** reorderable connector */
|
|
109
110
|
connectDragSource: any
|
|
110
111
|
/** reorderable connector */
|
|
@@ -114,14 +115,24 @@ interface RangeComponentProps {
|
|
|
114
115
|
|
|
115
116
|
// Single range (row)
|
|
116
117
|
class RangeComponent extends React.Component<RangeComponentProps> {
|
|
117
|
-
|
|
118
|
-
|
|
118
|
+
/**
|
|
119
|
+
* Handles change in minimum open value
|
|
120
|
+
*/
|
|
121
|
+
handleMinOpenChange = (minOpen: boolean) => {
|
|
122
|
+
return this.props.onChange(produce(this.props.range, draft => {
|
|
123
|
+
draft.minOpen = minOpen
|
|
124
|
+
}))
|
|
119
125
|
}
|
|
120
126
|
|
|
121
|
-
|
|
122
|
-
|
|
127
|
+
/**
|
|
128
|
+
* Handles change in maximum open value
|
|
129
|
+
*/
|
|
130
|
+
handleMaxOpenChange = (maxOpen: boolean) => {
|
|
131
|
+
return this.props.onChange(produce(this.props.range, draft => {
|
|
132
|
+
draft.maxOpen = maxOpen
|
|
133
|
+
}))
|
|
123
134
|
}
|
|
124
|
-
|
|
135
|
+
|
|
125
136
|
render() {
|
|
126
137
|
let placeholder = ""
|
|
127
138
|
if (this.props.range.minValue != null) {
|
|
@@ -156,10 +167,10 @@ class RangeComponent extends React.Component<RangeComponentProps> {
|
|
|
156
167
|
LinkComponent,
|
|
157
168
|
{
|
|
158
169
|
dropdownItems: [
|
|
159
|
-
{ id: true, name: "greater than" },
|
|
160
|
-
{ id: false, name: "greater than or equal to" }
|
|
170
|
+
{ id: "true", name: "greater than" },
|
|
171
|
+
{ id: "false", name: "greater than or equal to" }
|
|
161
172
|
],
|
|
162
|
-
onDropdownItemClicked: this.handleMinOpenChange
|
|
173
|
+
onDropdownItemClicked: (id: string) => this.handleMinOpenChange(id === "true")
|
|
163
174
|
},
|
|
164
175
|
this.props.range.minOpen ? "greater than" : "greater than or equal to"
|
|
165
176
|
)
|
|
@@ -169,7 +180,7 @@ class RangeComponent extends React.Component<RangeComponentProps> {
|
|
|
169
180
|
"td",
|
|
170
181
|
{ key: "minValue" },
|
|
171
182
|
R(NumberInputComponent, {
|
|
172
|
-
value: this.props.range.minValue,
|
|
183
|
+
value: this.props.range.minValue ?? undefined,
|
|
173
184
|
placeholder: "None",
|
|
174
185
|
small: true,
|
|
175
186
|
onChange: (v: any) => this.props.onChange(update(this.props.range, { minValue: { $set: v } }))
|
|
@@ -185,10 +196,10 @@ class RangeComponent extends React.Component<RangeComponentProps> {
|
|
|
185
196
|
LinkComponent,
|
|
186
197
|
{
|
|
187
198
|
dropdownItems: [
|
|
188
|
-
{ id: true, name: "less than" },
|
|
189
|
-
{ id: false, name: "less than or equal to" }
|
|
199
|
+
{ id: "true", name: "less than" },
|
|
200
|
+
{ id: "false", name: "less than or equal to" }
|
|
190
201
|
],
|
|
191
|
-
onDropdownItemClicked: this.handleMaxOpenChange
|
|
202
|
+
onDropdownItemClicked: (id: string) => this.handleMaxOpenChange(id === "true")
|
|
192
203
|
},
|
|
193
204
|
this.props.range.maxOpen ? "less than" : "less than or equal to"
|
|
194
205
|
)
|
|
@@ -198,7 +209,7 @@ class RangeComponent extends React.Component<RangeComponentProps> {
|
|
|
198
209
|
"td",
|
|
199
210
|
{ key: "maxValue" },
|
|
200
211
|
R(NumberInputComponent, {
|
|
201
|
-
value: this.props.range.maxValue,
|
|
212
|
+
value: this.props.range.maxValue ?? undefined,
|
|
202
213
|
placeholder: "None",
|
|
203
214
|
small: true,
|
|
204
215
|
onChange: (v: any) => this.props.onChange(update(this.props.range, { maxValue: { $set: v } }))
|
|
@@ -19,6 +19,7 @@ import { getLayoutOptions } from "./layoutOptions"
|
|
|
19
19
|
import { DashboardDesign } from "./DashboardDesign"
|
|
20
20
|
import DashboardDataSource from "./DashboardDataSource"
|
|
21
21
|
import { JsonQLFilter } from ".."
|
|
22
|
+
import { ActiveTablesContext } from "@mwater/expressions-ui"
|
|
22
23
|
|
|
23
24
|
export interface DashboardComponentProps {
|
|
24
25
|
design: DashboardDesign
|
|
@@ -80,7 +81,6 @@ export default class DashboardComponent extends React.Component<DashboardCompone
|
|
|
80
81
|
|
|
81
82
|
static childContextTypes = {
|
|
82
83
|
locale: PropTypes.string,
|
|
83
|
-
activeTables: PropTypes.arrayOf(PropTypes.string.isRequired)
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
settings: SettingsModalComponent | null
|
|
@@ -89,9 +89,6 @@ export default class DashboardComponent extends React.Component<DashboardCompone
|
|
|
89
89
|
return {
|
|
90
90
|
// Pass locale down. Both here and DashboardViewComponent to ensure that quickfilters also get context
|
|
91
91
|
locale: this.props.design.locale,
|
|
92
|
-
|
|
93
|
-
// Pass active tables down to table select components so they can present a shorter list
|
|
94
|
-
activeTables: DashboardUtils.getFilterableTables(this.props.design, this.props.schema)
|
|
95
92
|
}
|
|
96
93
|
}
|
|
97
94
|
|
|
@@ -240,45 +237,6 @@ export default class DashboardComponent extends React.Component<DashboardCompone
|
|
|
240
237
|
)
|
|
241
238
|
}
|
|
242
239
|
|
|
243
|
-
renderStyleItem(style: any) {
|
|
244
|
-
const isActive = (this.props.design.style || "default") === style
|
|
245
|
-
|
|
246
|
-
const content = (() => {
|
|
247
|
-
switch (style) {
|
|
248
|
-
case "default":
|
|
249
|
-
return [
|
|
250
|
-
R("h4", { key: "name", className: "list-group-item-heading" }, "Classic Dashboard"),
|
|
251
|
-
R("p", { key: "description", className: "" }, "Ideal for data display with minimal text")
|
|
252
|
-
]
|
|
253
|
-
case "greybg":
|
|
254
|
-
return [
|
|
255
|
-
R("h4", { key: "name", className: "list-group-item-heading" }, "Framed Dashboard"),
|
|
256
|
-
R("p", { key: "description", className: "" }, "Each widget is white on a grey background")
|
|
257
|
-
]
|
|
258
|
-
case "story":
|
|
259
|
-
return [
|
|
260
|
-
R("h4", { key: "name", className: "list-group-item-heading" }, "Story"),
|
|
261
|
-
R(
|
|
262
|
-
"p",
|
|
263
|
-
{ key: "description", className: "" },
|
|
264
|
-
"Ideal for data-driven storytelling with lots of text. Responsive and mobile-friendly"
|
|
265
|
-
)
|
|
266
|
-
]
|
|
267
|
-
}
|
|
268
|
-
return null
|
|
269
|
-
})()
|
|
270
|
-
|
|
271
|
-
return R(
|
|
272
|
-
"a",
|
|
273
|
-
{
|
|
274
|
-
key: style,
|
|
275
|
-
className: `list-group-item ${isActive ? "active" : ""}`,
|
|
276
|
-
onClick: this.handleStyleChange.bind(null, style)
|
|
277
|
-
},
|
|
278
|
-
content
|
|
279
|
-
)
|
|
280
|
-
}
|
|
281
|
-
|
|
282
240
|
renderStyle() {
|
|
283
241
|
return R(
|
|
284
242
|
"button",
|
|
@@ -382,7 +340,8 @@ export default class DashboardComponent extends React.Component<DashboardCompone
|
|
|
382
340
|
locks: this.props.quickfilterLocks,
|
|
383
341
|
filters: this.getCompiledFilters(),
|
|
384
342
|
hideTopBorder: this.props.hideTitleBar,
|
|
385
|
-
|
|
343
|
+
// Don't hide if title bar is hidden as it can't be shown again
|
|
344
|
+
onHide: () => !this.props.hideTitleBar ? this.setState({ hideQuickfilters: true }) : undefined
|
|
386
345
|
})
|
|
387
346
|
}
|
|
388
347
|
|
|
@@ -428,41 +387,41 @@ export default class DashboardComponent extends React.Component<DashboardCompone
|
|
|
428
387
|
hideScopes: this.state.hideQuickfilters
|
|
429
388
|
})
|
|
430
389
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
390
|
+
|
|
391
|
+
// Pass active tables down to table select components so they can present a shorter list
|
|
392
|
+
return <ActiveTablesContext.Provider
|
|
393
|
+
value={DashboardUtils.getFilterableTables(this.props.design, this.props.schema)}>
|
|
394
|
+
|
|
395
|
+
<div style={{
|
|
396
|
+
display: "grid",
|
|
397
|
+
gridTemplateRows: this.props.hideTitleBar ? "auto 1fr" : "auto auto 1fr",
|
|
398
|
+
height: "100%"
|
|
399
|
+
}}>
|
|
400
|
+
{!this.props.hideTitleBar ? this.renderTitleBar() : undefined}
|
|
401
|
+
<div>{!this.state.hideQuickfilters ? this.renderQuickfilter() : undefined}</div>
|
|
402
|
+
{dashboardView}
|
|
403
|
+
{this.props.onDesignChange != null && (
|
|
404
|
+
<SettingsModalComponent
|
|
405
|
+
onDesignChange={this.handleDesignChange}
|
|
406
|
+
schema={this.props.schema}
|
|
407
|
+
dataSource={this.props.dataSource}
|
|
408
|
+
ref={(c: SettingsModalComponent | null) => {
|
|
409
|
+
this.settings = c
|
|
410
|
+
}}
|
|
411
|
+
/>
|
|
412
|
+
)}
|
|
413
|
+
{this.state.layoutOptionsOpen && (
|
|
414
|
+
<ModalWindowComponent isOpen={true} outerPadding={10} innerPadding={10}>
|
|
415
|
+
<LayoutOptionsComponent
|
|
416
|
+
design={this.props.design}
|
|
417
|
+
onDesignChange={this.props.onDesignChange!}
|
|
418
|
+
onClose={() => this.setState({ layoutOptionsOpen: false })}
|
|
419
|
+
dashboardView={readonlyDashboardView}
|
|
420
|
+
quickfiltersView={this.renderQuickfilter()}
|
|
421
|
+
/>
|
|
422
|
+
</ModalWindowComponent>
|
|
423
|
+
)}
|
|
424
|
+
</div>
|
|
425
|
+
</ActiveTablesContext.Provider>
|
|
467
426
|
}
|
|
468
427
|
}
|