@mwater/visualization 5.1.0 → 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/MWaterCompleteTableSelectComponent.d.ts +2 -9
- package/lib/MWaterContextComponent.d.ts +15 -3
- package/lib/MWaterContextComponent.js +38 -13
- package/lib/MWaterCustomTablesetListComponent.js +9 -3
- package/lib/MWaterGlobalFiltersComponent.d.ts +6 -5
- package/lib/MWaterGlobalFiltersComponent.js +4 -4
- package/lib/MWaterLoaderComponent.d.ts +12 -2
- package/lib/MWaterLoaderComponent.js +9 -1
- 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/dashboards/DashboardComponent.d.ts +0 -5
- package/lib/dashboards/DashboardComponent.js +2 -29
- 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/SettingsModalComponent.d.ts +4 -15
- package/lib/dashboards/SettingsModalComponent.js +24 -38
- package/lib/datagrids/DatagridComponent.d.ts +2 -9
- package/lib/datagrids/DatagridDataSource.d.ts +3 -3
- package/lib/datagrids/DatagridDataSource.js +0 -14
- package/lib/datagrids/DatagridDesignerComponent.d.ts +2 -93
- package/lib/datagrids/DatagridDesignerComponent.js +8 -6
- package/lib/datagrids/DatagridViewComponent.js +1 -1
- package/lib/datagrids/FindReplaceModalComponent.d.ts +4 -20
- package/lib/datagrids/FindReplaceModalComponent.js +27 -13
- package/lib/datagrids/ServerDatagridDataSource.d.ts +1 -1
- package/lib/datagrids/ServerDatagridDataSource.js +1 -3
- package/lib/demo.js +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/layouts/blocks/BlocksDisplayComponent.d.ts +2 -1
- package/lib/layouts/grid/GridLayoutManager.d.ts +2 -1
- package/lib/maps/BufferLayer.js +3 -1
- package/lib/maps/ClusterLayer.js +3 -1
- package/lib/maps/GridLayer.js +5 -3
- package/lib/maps/GridLayerDesigner.js +0 -1
- 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/MapFiltersDesignerComponent.d.ts +0 -4
- package/lib/maps/MapFiltersDesignerComponent.js +4 -5
- 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/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/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/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/pivot/IntersectionDesignerComponent.d.ts +73 -66
- package/lib/widgets/charts/pivot/PivotChartDesignerComponent.d.ts +10 -6
- 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/TableChartViewComponent.js +21 -7
- package/lib/widgets/text/ExprInsertModalComponent.d.ts +2 -13
- 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/MWaterContextComponent.tsx +37 -19
- package/src/MWaterCustomTablesetListComponent.tsx +21 -3
- package/src/MWaterGlobalFiltersComponent.ts +8 -8
- package/src/MWaterLoaderComponent.ts +8 -1
- package/src/axes/Axis.ts +24 -25
- package/src/axes/AxisBuilder.ts +9 -8
- package/src/dashboards/DashboardComponent.tsx +2 -40
- package/src/dashboards/DashboardDesign.ts +2 -22
- package/src/dashboards/DashboardViewComponent.ts +4 -5
- package/src/dashboards/LayoutOptionsComponent.tsx +6 -4
- package/src/dashboards/SettingsModalComponent.tsx +170 -0
- package/src/datagrids/DatagridDataSource.ts +6 -12
- package/src/datagrids/DatagridDesignerComponent.tsx +22 -18
- package/src/datagrids/DatagridViewComponent.ts +3 -3
- package/src/datagrids/ExprCellComponent.ts +0 -1
- package/src/datagrids/FindReplaceModalComponent.ts +39 -22
- package/src/datagrids/ServerDatagridDataSource.ts +1 -2
- package/src/demo.ts +1 -1
- package/src/index.ts +1 -0
- package/src/maps/BufferLayer.ts +3 -1
- package/src/maps/ClusterLayer.ts +3 -1
- package/src/maps/GridLayer.ts +5 -3
- package/src/maps/GridLayerDesigner.tsx +0 -1
- package/src/maps/LayerSwitcherComponent.tsx +1 -1
- package/src/maps/MapDesign.ts +2 -17
- package/src/maps/{MapFiltersDesignerComponent.ts → MapFiltersDesignerComponent.tsx} +25 -25
- package/src/maps/ServerMapDataSource.ts +1 -1
- package/src/maps/VectorMapViewComponent.tsx +0 -1
- package/src/maps/vectorMaps.tsx +10 -1
- package/src/quickfilter/QuickfilterCompiler.ts +1 -1
- package/src/widgets/charts/table/TableChartViewComponent.ts +21 -7
- package/src/widgets/text/TextWidgetDesign.ts +4 -1
- package/src/dashboards/SettingsModalComponent.ts +0 -169
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Expr, LiteralType } from "@mwater/expressions";
|
|
2
|
+
/** Global filters apply to multiple tables at once if a certain column is present. User-interface to set them
|
|
3
|
+
* is application-specific and the default (non-mWater) dashboard applies them but does not allow editing. */
|
|
4
|
+
export interface GlobalFilter {
|
|
5
|
+
/** id of column to filter */
|
|
6
|
+
columnId: string;
|
|
7
|
+
/** type of column to filter (to ensure that consistent) */
|
|
8
|
+
columnType: LiteralType;
|
|
9
|
+
/** op of expression for filtering */
|
|
10
|
+
op: string;
|
|
11
|
+
/** array of expressions to use for filtering. field expression for column will be injected as expression 0 in the resulting filter expression */
|
|
12
|
+
exprs: Expr[];
|
|
13
|
+
}
|
|
@@ -3,6 +3,7 @@ import PropTypes from "prop-types";
|
|
|
3
3
|
import React from "react";
|
|
4
4
|
import * as uiComponents from "./UIComponents";
|
|
5
5
|
import { Schema } from "@mwater/expressions";
|
|
6
|
+
import ModalPopupComponent from "@mwater/react-library/lib/ModalPopupComponent";
|
|
6
7
|
interface MWaterCompleteTableSelectComponentProps {
|
|
7
8
|
/** Url to hit api */
|
|
8
9
|
apiUrl: string;
|
|
@@ -180,15 +181,7 @@ declare class AddIndicatorConfirmPopupComponent extends React.Component<AddIndic
|
|
|
180
181
|
renderContents(): React.DetailedReactHTMLElement<React.HTMLAttributes<HTMLElement>, HTMLElement> | React.DetailedReactHTMLElement<{
|
|
181
182
|
className: string;
|
|
182
183
|
}, HTMLElement>;
|
|
183
|
-
render(): React.CElement<
|
|
184
|
-
showCloseX: boolean;
|
|
185
|
-
onClose: () => void;
|
|
186
|
-
header: string;
|
|
187
|
-
}, React.Component<{
|
|
188
|
-
showCloseX: boolean;
|
|
189
|
-
onClose: () => void;
|
|
190
|
-
header: string;
|
|
191
|
-
}, any, any>> | null;
|
|
184
|
+
render(): React.CElement<import("@mwater/react-library/lib/ModalPopupComponent").ModalPopupComponentProps, ModalPopupComponent> | null;
|
|
192
185
|
}
|
|
193
186
|
interface IssuesListComponentProps {
|
|
194
187
|
/** Url to hit api */
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import PropTypes from "prop-types";
|
|
2
2
|
import React from "react";
|
|
3
|
-
import { Schema, Section } from "@mwater/expressions";
|
|
3
|
+
import { DataSource, Schema, Section } from "@mwater/expressions";
|
|
4
4
|
import { CustomTableSelectComponentFactoryOptions, DecorateScalarExprTreeSectionChildrenOptions } from "@mwater/expressions-ui";
|
|
5
|
+
import { GlobalFilter } from "./GlobalFilter";
|
|
6
|
+
export interface GlobalFiltersElementFactoryProps {
|
|
7
|
+
schema: Schema;
|
|
8
|
+
dataSource: DataSource;
|
|
9
|
+
filterableTables: string[];
|
|
10
|
+
globalFilters?: GlobalFilter[];
|
|
11
|
+
onChange: (globalFilters: GlobalFilter[]) => void;
|
|
12
|
+
/** If true, return null element if not applicable to filterableTables */
|
|
13
|
+
nullIfIrrelevant?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export type GlobalFiltersElementFactory = (props: GlobalFiltersElementFactoryProps) => React.ReactElement | null;
|
|
16
|
+
export declare const GlobalFiltersElementFactoryContext: React.Context<GlobalFiltersElementFactory | null>;
|
|
5
17
|
/**
|
|
6
18
|
* Creates several contexts to allow selecting of a table in an mWater-friendly way
|
|
7
19
|
* and several other context items
|
|
@@ -9,7 +21,7 @@ import { CustomTableSelectComponentFactoryOptions, DecorateScalarExprTreeSection
|
|
|
9
21
|
export default class MWaterContextComponent extends React.Component<{
|
|
10
22
|
apiUrl: string;
|
|
11
23
|
client?: string;
|
|
12
|
-
/**
|
|
24
|
+
/** user id of logged in user */
|
|
13
25
|
user?: string;
|
|
14
26
|
schema: Schema;
|
|
15
27
|
/** Extra tables to load in schema. Forms are not loaded by default as they are too many */
|
|
@@ -21,10 +33,10 @@ export default class MWaterContextComponent extends React.Component<{
|
|
|
21
33
|
}> {
|
|
22
34
|
static childContextTypes: {
|
|
23
35
|
addLayerElementFactory: PropTypes.Requireable<(...args: any[]) => any>;
|
|
24
|
-
globalFiltersElementFactory: PropTypes.Requireable<(...args: any[]) => any>;
|
|
25
36
|
};
|
|
26
37
|
createTableSelectElementFactory: (options: CustomTableSelectComponentFactoryOptions) => React.JSX.Element;
|
|
27
38
|
getChildContext(): any;
|
|
39
|
+
createGlobalFiltersElementFactory: (props: GlobalFiltersElementFactoryProps) => React.JSX.Element | null;
|
|
28
40
|
isScalarExprTreeSectionMatch: (options: {
|
|
29
41
|
tableId: string;
|
|
30
42
|
section: Section;
|
|
@@ -1,11 +1,35 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
2
25
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
27
|
};
|
|
5
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.GlobalFiltersElementFactoryContext = void 0;
|
|
6
30
|
const prop_types_1 = __importDefault(require("prop-types"));
|
|
7
31
|
const lodash_1 = __importDefault(require("lodash"));
|
|
8
|
-
const react_1 =
|
|
32
|
+
const react_1 = __importStar(require("react"));
|
|
9
33
|
const R = react_1.default.createElement;
|
|
10
34
|
const MWaterTableSelectComponent_1 = __importDefault(require("./MWaterTableSelectComponent"));
|
|
11
35
|
const MWaterAddRelatedFormComponent_1 = __importDefault(require("./MWaterAddRelatedFormComponent"));
|
|
@@ -13,15 +37,14 @@ const MWaterAddRelatedIndicatorComponent_1 = __importDefault(require("./MWaterAd
|
|
|
13
37
|
const MWaterGlobalFiltersComponent_1 = __importDefault(require("./MWaterGlobalFiltersComponent"));
|
|
14
38
|
const expressions_ui_1 = require("@mwater/expressions-ui");
|
|
15
39
|
const expressions_ui_2 = require("@mwater/expressions-ui");
|
|
40
|
+
exports.GlobalFiltersElementFactoryContext = (0, react_1.createContext)(null);
|
|
16
41
|
/**
|
|
17
42
|
* Creates several contexts to allow selecting of a table in an mWater-friendly way
|
|
18
43
|
* and several other context items
|
|
19
44
|
*/
|
|
20
45
|
class MWaterContextComponent extends react_1.default.Component {
|
|
21
46
|
static childContextTypes = {
|
|
22
|
-
addLayerElementFactory: prop_types_1.default.func,
|
|
23
|
-
globalFiltersElementFactory: prop_types_1.default.func, // Call with props { schema, dataSource, filterableTables, globalFilters, onChange, nullIfIrrelevant }.
|
|
24
|
-
// Displays a component to edit global filters. nullIfIrrelevant causes null element if not applicable to filterableTables
|
|
47
|
+
addLayerElementFactory: prop_types_1.default.func, // Call with props of AddLayerComponent
|
|
25
48
|
};
|
|
26
49
|
createTableSelectElementFactory = (options) => {
|
|
27
50
|
return (react_1.default.createElement(MWaterTableSelectComponent_1.default, { apiUrl: this.props.apiUrl, client: this.props.client, schema: options.schema, user: this.props.user, table: options.value ?? undefined, onChange: options.onChange, extraTables: this.props.extraTables, onExtraTablesChange: this.props.onExtraTablesChange, filter: options.filter, onFilterChange: options.onFilterChange }));
|
|
@@ -31,14 +54,14 @@ class MWaterContextComponent extends react_1.default.Component {
|
|
|
31
54
|
if (this.props.addLayerElementFactory) {
|
|
32
55
|
context.addLayerElementFactory = this.props.addLayerElementFactory;
|
|
33
56
|
}
|
|
34
|
-
context.globalFiltersElementFactory = (props) => {
|
|
35
|
-
if (props.nullIfIrrelevant && !lodash_1.default.any(props.filterableTables, (t) => t.match(/^entities./))) {
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
return react_1.default.createElement(MWaterGlobalFiltersComponent_1.default, props);
|
|
39
|
-
};
|
|
40
57
|
return context;
|
|
41
58
|
}
|
|
59
|
+
createGlobalFiltersElementFactory = (props) => {
|
|
60
|
+
if (props.nullIfIrrelevant && !lodash_1.default.any(props.filterableTables, (t) => t.match(/^entities./))) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
return react_1.default.createElement(MWaterGlobalFiltersComponent_1.default, { ...props });
|
|
64
|
+
};
|
|
42
65
|
isScalarExprTreeSectionMatch = (options) => {
|
|
43
66
|
if (options.tableId.match(/^entities\./) && options.section.id === "!indicators") {
|
|
44
67
|
return true;
|
|
@@ -84,9 +107,11 @@ class MWaterContextComponent extends react_1.default.Component {
|
|
|
84
107
|
};
|
|
85
108
|
render() {
|
|
86
109
|
return react_1.default.createElement(expressions_ui_1.CustomTableSelectComponentFactoryContext.Provider, { value: this.createTableSelectElementFactory },
|
|
87
|
-
react_1.default.createElement(
|
|
88
|
-
react_1.default.createElement(expressions_ui_2.
|
|
89
|
-
react_1.default.createElement(
|
|
110
|
+
react_1.default.createElement(exports.GlobalFiltersElementFactoryContext.Provider, { value: this.createGlobalFiltersElementFactory },
|
|
111
|
+
react_1.default.createElement(expressions_ui_2.IsScalarExprTreeSectionMatchContext.Provider, { value: this.isScalarExprTreeSectionMatch },
|
|
112
|
+
react_1.default.createElement(expressions_ui_2.IsScalarExprTreeSectionInitiallyOpenContext.Provider, { value: this.isScalarExprTreeSectionInitiallyOpen },
|
|
113
|
+
react_1.default.createElement(expressions_ui_1.DecorateScalarExprTreeSectionChildrenContext.Provider, { value: this.decorateScalarExprTreeSectionChildren }, this.props.children)))),
|
|
114
|
+
" ");
|
|
90
115
|
}
|
|
91
116
|
}
|
|
92
117
|
exports.default = MWaterContextComponent;
|
|
@@ -15,13 +15,16 @@ const MWaterCustomTablesetListComponent = (props) => {
|
|
|
15
15
|
const [tablesets, setTablesets] = (0, react_1.useState)();
|
|
16
16
|
const [search, setSearch] = (0, react_1.useState)("");
|
|
17
17
|
const [extraTableNeeded, setExtraTableNeeded] = (0, react_1.useState)();
|
|
18
|
+
const [showSystem, setShowSystem] = (0, react_1.useState)(false);
|
|
18
19
|
// Get list of all tablesets
|
|
19
20
|
(0, react_1.useEffect)(() => {
|
|
20
21
|
fetch(`${props.apiUrl}custom_tablesets?client=${props.client || ""}`)
|
|
21
22
|
.then((response) => response.json())
|
|
22
|
-
.then((
|
|
23
|
+
.then((tablesets) => {
|
|
24
|
+
// Filter out non-normal
|
|
25
|
+
tablesets = tablesets.filter((ts) => ts.type === "normal");
|
|
23
26
|
// Put included ones first
|
|
24
|
-
setTablesets(lodash_1.default.sortByAll(
|
|
27
|
+
setTablesets(lodash_1.default.sortByAll(tablesets, [
|
|
25
28
|
(ts) => (props.extraTables.some((t) => (t || "").startsWith(`custom.${ts.code}.`)) ? 0 : 1),
|
|
26
29
|
(ts) => expressions_1.ExprUtils.localizeString(ts.design.name, props.locale)
|
|
27
30
|
]));
|
|
@@ -80,8 +83,11 @@ const MWaterCustomTablesetListComponent = (props) => {
|
|
|
80
83
|
react_2.default.createElement("h4", { className: "text-muted" }, name),
|
|
81
84
|
react_2.default.createElement(UIComponents_1.OptionListComponent, { items: items })));
|
|
82
85
|
};
|
|
86
|
+
const visibleTablesets = tablesets.filter((ts) => (showSystem || !ts.design.system) && !ts.design.deprecated);
|
|
83
87
|
return (react_2.default.createElement("div", null,
|
|
84
88
|
react_2.default.createElement(bootstrap_1.TextInput, { value: search, onChange: setSearch, placeholder: "Search..." }),
|
|
85
|
-
|
|
89
|
+
visibleTablesets.map((ts) => renderTableset(ts)),
|
|
90
|
+
react_2.default.createElement("div", null,
|
|
91
|
+
react_2.default.createElement("button", { className: "btn btn-link btn-sm", onClick: () => setShowSystem(!showSystem) }, showSystem ? "Hide system tables" : "Show system tables"))));
|
|
86
92
|
};
|
|
87
93
|
exports.MWaterCustomTablesetListComponent = MWaterCustomTablesetListComponent;
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { DataSource, Schema } from "@mwater/expressions";
|
|
3
|
+
import { GlobalFilter } from "./GlobalFilter";
|
|
3
4
|
export interface MWaterGlobalFiltersComponentProps {
|
|
4
5
|
/** Schema of the database */
|
|
5
6
|
schema: Schema;
|
|
6
7
|
/** Data source to use to get values */
|
|
7
8
|
dataSource: DataSource;
|
|
8
|
-
filterableTables:
|
|
9
|
-
globalFilters?:
|
|
10
|
-
onChange:
|
|
9
|
+
filterableTables: string[];
|
|
10
|
+
globalFilters?: GlobalFilter[];
|
|
11
|
+
onChange: (globalFilters: GlobalFilter[]) => void;
|
|
11
12
|
}
|
|
12
13
|
export default class MWaterGlobalFiltersComponent extends React.Component<MWaterGlobalFiltersComponentProps> {
|
|
13
|
-
handleRegionsChange: (regions: any) =>
|
|
14
|
-
handleManagedByChange: (managedBy: any) =>
|
|
14
|
+
handleRegionsChange: (regions: any) => void;
|
|
15
|
+
handleManagedByChange: (managedBy: any) => void;
|
|
15
16
|
render(): React.DetailedReactHTMLElement<React.HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
16
17
|
}
|
|
@@ -45,7 +45,7 @@ class MWaterGlobalFiltersComponent extends react_1.default.Component {
|
|
|
45
45
|
exprs: [{ type: "literal", valueType: "id[]", idTable: "admin_regions", value: regions }]
|
|
46
46
|
});
|
|
47
47
|
}
|
|
48
|
-
|
|
48
|
+
this.props.onChange(globalFilters);
|
|
49
49
|
};
|
|
50
50
|
handleManagedByChange = (managedBy) => {
|
|
51
51
|
// Remove existing filter
|
|
@@ -59,12 +59,12 @@ class MWaterGlobalFiltersComponent extends react_1.default.Component {
|
|
|
59
59
|
exprs: [{ type: "literal", valueType: "id", idTable: "subjects", value: "group:" + managedBy }]
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
|
-
|
|
62
|
+
this.props.onChange(globalFilters);
|
|
63
63
|
};
|
|
64
64
|
render() {
|
|
65
65
|
// Find managed by
|
|
66
66
|
let adminRegions, managedBy;
|
|
67
|
-
const managedByEntry = lodash_1.default.find(this.props.globalFilters, (gf) => gf.op === "within" && gf.columnId === "_managed_by");
|
|
67
|
+
const managedByEntry = lodash_1.default.find(this.props.globalFilters || [], (gf) => gf.op === "within" && gf.columnId === "_managed_by");
|
|
68
68
|
if (managedByEntry) {
|
|
69
69
|
managedBy = managedByEntry.exprs[0].value.split(":")[1];
|
|
70
70
|
}
|
|
@@ -72,7 +72,7 @@ class MWaterGlobalFiltersComponent extends react_1.default.Component {
|
|
|
72
72
|
managedBy = null;
|
|
73
73
|
}
|
|
74
74
|
// Find admin region
|
|
75
|
-
const adminRegionEntry = lodash_1.default.find(this.props.globalFilters, (gf) => gf.op === "within any" && gf.columnId === "admin_region");
|
|
75
|
+
const adminRegionEntry = lodash_1.default.find(this.props.globalFilters || [], (gf) => gf.op === "within any" && gf.columnId === "admin_region");
|
|
76
76
|
if (adminRegionEntry) {
|
|
77
77
|
adminRegions = adminRegionEntry.exprs[0].value;
|
|
78
78
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { ReactElement } from "react";
|
|
1
|
+
import React, { ReactElement } from "react";
|
|
2
2
|
import { DataSource, Schema } from "@mwater/expressions";
|
|
3
3
|
import AsyncLoadComponent from "@mwater/react-library/lib/AsyncLoadComponent";
|
|
4
|
+
import LoadingComponent from "@mwater/react-library/lib/LoadingComponent";
|
|
5
|
+
import MWaterContextComponent from "./MWaterContextComponent";
|
|
4
6
|
/**
|
|
5
7
|
* Loads an mWater schema from the server and creates child with schema and dataSource
|
|
6
8
|
* Also creates context to allow selecting of a table in an mWater-friendly way
|
|
@@ -38,5 +40,13 @@ export default class MWaterLoaderComponent extends AsyncLoadComponent<{
|
|
|
38
40
|
constructor(props: any);
|
|
39
41
|
isLoadNeeded(newProps: any, oldProps: any): boolean;
|
|
40
42
|
load(props: any, prevProps: any, callback: any): void;
|
|
41
|
-
render(): any
|
|
43
|
+
render(): React.ReactElement<any, string | React.JSXElementConstructor<any>> | React.CElement<import("@mwater/react-library/lib/LoadingComponent").LoadingComponentProps, LoadingComponent> | React.CElement<{
|
|
44
|
+
apiUrl: string;
|
|
45
|
+
client?: string | undefined;
|
|
46
|
+
user?: string | undefined;
|
|
47
|
+
schema: Schema;
|
|
48
|
+
extraTables?: string[] | undefined;
|
|
49
|
+
onExtraTablesChange?: ((extraTables: string[]) => void) | undefined;
|
|
50
|
+
addLayerElementFactory?: any;
|
|
51
|
+
}, MWaterContextComponent>;
|
|
42
52
|
}
|
|
@@ -45,7 +45,15 @@ class MWaterLoaderComponent extends AsyncLoadComponent_1.default {
|
|
|
45
45
|
if (error) {
|
|
46
46
|
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}`;
|
|
47
47
|
if (this.props.errorFormatter) {
|
|
48
|
-
|
|
48
|
+
try {
|
|
49
|
+
const parsedError = JSON.parse(error.message);
|
|
50
|
+
if (parsedError) {
|
|
51
|
+
return callback({ error: this.props.errorFormatter(parsedError, defaultError) });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
// Ignore
|
|
56
|
+
}
|
|
49
57
|
}
|
|
50
58
|
return callback({ error: defaultError });
|
|
51
59
|
}
|
package/lib/axes/Axis.d.ts
CHANGED
|
@@ -4,35 +4,15 @@ import { Expr } from "@mwater/expressions";
|
|
|
4
4
|
An axis is an expression with optional aggregation, transform and color mapping
|
|
5
5
|
In ggplot2 parlance, an "aesthetic"
|
|
6
6
|
|
|
7
|
-
It contains:
|
|
8
|
-
expr: expression
|
|
9
|
-
aggr: DEPRECATED: optional aggregation (e.g. sum)
|
|
10
|
-
xform: optional transformation to be applied. object with `type` field. See below
|
|
11
|
-
colorMap: optional array of color mappings. See below
|
|
12
|
-
excludedValues: Array of post-xform values to exclude when displaying.
|
|
13
|
-
format: optional d3-format format for numeric values. Default if null is ","
|
|
14
|
-
|
|
15
|
-
## Xforms
|
|
16
|
-
|
|
17
|
-
types:
|
|
18
|
-
|
|
19
|
-
|
|
20
7
|
*/
|
|
21
8
|
export interface Axis {
|
|
9
|
+
/** Expression to be displayed on axis */
|
|
22
10
|
expr: Expr;
|
|
23
|
-
/**
|
|
24
|
-
*
|
|
25
|
-
* `date`: convert to complete date e.g. `2015-02-08`. type date
|
|
26
|
-
* `year`: year only e.g. `2015-01-01`. type date
|
|
27
|
-
* `yearmonth`: year and month only e.g. `2015-02-01`. type date
|
|
28
|
-
* `yearquarter`: year and quarter only e.g. `2015-01`. type enum
|
|
29
|
-
* `yearweek`: year and week (ISO) only e.g. `2015-31`. type enum
|
|
30
|
-
* `month`: month only e.g. `02`. type enum
|
|
31
|
-
* `week`: ISO week of year e.g. `01` for the first week that contains January 4th
|
|
32
|
-
* `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 <=) }
|
|
33
|
-
* `floor`: convert to floor. type enum
|
|
11
|
+
/** Optional transformation to be applied. If a date is used, for example, it's generally
|
|
12
|
+
* better to use a transform to convert to a year or month rather than creating an expression.
|
|
34
13
|
*/
|
|
35
14
|
xform?: AxisXform;
|
|
15
|
+
/** optional array of color mappings */
|
|
36
16
|
colorMap?: ColorMap;
|
|
37
17
|
/** optional array of category values which define the order in which categories should be rendered */
|
|
38
18
|
drawOrder?: any[];
|
|
@@ -42,9 +22,11 @@ export interface Axis {
|
|
|
42
22
|
};
|
|
43
23
|
/** optional string for null category */
|
|
44
24
|
nullLabel?: string;
|
|
25
|
+
/** Array of post-xform values to exclude when displaying. */
|
|
45
26
|
excludedValues?: any[];
|
|
27
|
+
/** optional d3-format format for numeric values. Default if null is "," */
|
|
46
28
|
format?: string;
|
|
47
|
-
/** @deprecated */
|
|
29
|
+
/** @deprecated optional aggregation (e.g. sum) */
|
|
48
30
|
aggr?: string;
|
|
49
31
|
}
|
|
50
32
|
/**
|
|
@@ -63,6 +45,19 @@ export interface AxisCategory {
|
|
|
63
45
|
value: any;
|
|
64
46
|
}
|
|
65
47
|
export interface AxisXform {
|
|
48
|
+
/**
|
|
49
|
+
* Type of transformation
|
|
50
|
+
* `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
|
|
51
|
+
* `date`: convert to complete date e.g. `2015-02-08`. type date
|
|
52
|
+
* `year`: year only e.g. `2015-01-01`. type date
|
|
53
|
+
* `yearmonth`: year and month only e.g. `2015-02-01`. type date
|
|
54
|
+
* `yearquarter`: year and quarter only e.g. `2015-01`. type enum
|
|
55
|
+
* `yearweek`: year and week (ISO) only e.g. `2015-31`. type enum
|
|
56
|
+
* `month`: month only e.g. `02`. type enum
|
|
57
|
+
* `week`: ISO week of year e.g. `01` for the first week that contains January 4th
|
|
58
|
+
* `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 <=) }
|
|
59
|
+
* `floor`: convert to floor. type enum
|
|
60
|
+
*/
|
|
66
61
|
type: "bin" | "date" | "year" | "yearmonth" | "month" | "week" | "ranges" | "yearweek" | "yearquarter" | "floor";
|
|
67
62
|
numBins?: number;
|
|
68
63
|
min?: number;
|
package/lib/axes/AxisBuilder.js
CHANGED
|
@@ -38,6 +38,7 @@ const react_1 = __importDefault(require("react"));
|
|
|
38
38
|
const R = react_1.default.createElement;
|
|
39
39
|
const immer_1 = __importDefault(require("immer"));
|
|
40
40
|
const valueFormatter_1 = require("../valueFormatter");
|
|
41
|
+
const dayjs_1 = __importDefault(require("../dayjs"));
|
|
41
42
|
const xforms = [
|
|
42
43
|
{ type: "bin", input: "number", output: "enum" },
|
|
43
44
|
{ type: "ranges", input: "number", output: "enum" },
|
|
@@ -827,7 +828,7 @@ class AxisBuilder {
|
|
|
827
828
|
}
|
|
828
829
|
if (options.onlyValuesPresent) {
|
|
829
830
|
// Sort and take only present
|
|
830
|
-
categories = lodash_1.default.sortBy(lodash_1.default.uniq(values), item => item).map(value => ({ value, label: (0,
|
|
831
|
+
categories = lodash_1.default.sortBy(lodash_1.default.uniq(values), item => item).map(value => ({ value, label: (0, valueFormatter_1.formatValue)("date", value, axis.format) }));
|
|
831
832
|
if (hasNone) {
|
|
832
833
|
categories.push(noneCategory);
|
|
833
834
|
}
|
|
@@ -837,12 +838,12 @@ class AxisBuilder {
|
|
|
837
838
|
min = values.sort()[0];
|
|
838
839
|
max = values.sort().slice(-1)[0];
|
|
839
840
|
// Use moment to get range
|
|
840
|
-
current = (0,
|
|
841
|
-
end = (0,
|
|
841
|
+
current = (0, dayjs_1.default)(min);
|
|
842
|
+
end = (0, dayjs_1.default)(max);
|
|
842
843
|
categories = [];
|
|
843
844
|
while (!current.isAfter(end)) {
|
|
844
|
-
categories.push({ value: current.format("YYYY-MM-DD"), label: current.format("ll") });
|
|
845
|
-
current.add(1, "days");
|
|
845
|
+
categories.push({ value: current.format("YYYY-MM-DD"), label: current.format(axis.format ?? "ll") });
|
|
846
|
+
current = current.add(1, "days");
|
|
846
847
|
if (categories.length >= 1000) {
|
|
847
848
|
break;
|
|
848
849
|
}
|
|
@@ -947,9 +948,10 @@ class AxisBuilder {
|
|
|
947
948
|
}
|
|
948
949
|
return R("div", null, lodash_1.default.map(value, (v, i) => R("div", { key: i }, v)));
|
|
949
950
|
case "date":
|
|
950
|
-
|
|
951
|
+
console.log(axis);
|
|
952
|
+
return (0, valueFormatter_1.formatValue)(type, value, axis.format);
|
|
951
953
|
case "datetime":
|
|
952
|
-
return (0,
|
|
954
|
+
return (0, valueFormatter_1.formatValue)(type, value, axis.format);
|
|
953
955
|
}
|
|
954
956
|
return "" + value;
|
|
955
957
|
}
|
|
@@ -50,10 +50,10 @@ export default class AxisComponent extends AsyncLoadComponent<AxisComponentProps
|
|
|
50
50
|
constructor(props: any);
|
|
51
51
|
isLoadNeeded(newProps: any, oldProps: any): boolean;
|
|
52
52
|
load(props: any, prevProps: any, callback: any): any;
|
|
53
|
-
handleExprChange: (expr: any) =>
|
|
54
|
-
handleFormatChange: (ev: any) =>
|
|
55
|
-
handleXformTypeChange: (type: any) =>
|
|
56
|
-
handleXformChange: (xform: any) =>
|
|
53
|
+
handleExprChange: (expr: any) => void;
|
|
54
|
+
handleFormatChange: (ev: any) => void;
|
|
55
|
+
handleXformTypeChange: (type: any) => void;
|
|
56
|
+
handleXformChange: (xform: any) => void;
|
|
57
57
|
cleanAxis(axis: any): Axis | null;
|
|
58
58
|
renderXform(axis: any): React.DetailedReactHTMLElement<React.HTMLAttributes<HTMLElement>, HTMLElement> | React.CElement<ui.RadioToggleComponentProps, ui.RadioToggleComponent> | null;
|
|
59
59
|
renderColorMap(axis: any): (React.DetailedReactHTMLElement<React.HTMLAttributes<HTMLElement>, HTMLElement> | React.CElement<any, AxisColorEditorComponent>)[] | null;
|
|
@@ -83,11 +83,6 @@ export default class DashboardComponent extends React.Component<DashboardCompone
|
|
|
83
83
|
className: string;
|
|
84
84
|
onClick: () => void;
|
|
85
85
|
}, HTMLElement>;
|
|
86
|
-
renderStyleItem(style: any): React.DetailedReactHTMLElement<{
|
|
87
|
-
key: any;
|
|
88
|
-
className: string;
|
|
89
|
-
onClick: () => void;
|
|
90
|
-
}, HTMLElement>;
|
|
91
86
|
renderStyle(): React.DetailedReactHTMLElement<{
|
|
92
87
|
type: string;
|
|
93
88
|
key: string;
|
|
@@ -160,34 +160,6 @@ class DashboardComponent extends react_1.default.Component {
|
|
|
160
160
|
onClick: this.handleToggleEditing
|
|
161
161
|
}, R("span", { className: "fas fa-pencil-alt" }), this.state.editing ? " Editing" : " Edit");
|
|
162
162
|
}
|
|
163
|
-
renderStyleItem(style) {
|
|
164
|
-
const isActive = (this.props.design.style || "default") === style;
|
|
165
|
-
const content = (() => {
|
|
166
|
-
switch (style) {
|
|
167
|
-
case "default":
|
|
168
|
-
return [
|
|
169
|
-
R("h4", { key: "name", className: "list-group-item-heading" }, "Classic Dashboard"),
|
|
170
|
-
R("p", { key: "description", className: "" }, "Ideal for data display with minimal text")
|
|
171
|
-
];
|
|
172
|
-
case "greybg":
|
|
173
|
-
return [
|
|
174
|
-
R("h4", { key: "name", className: "list-group-item-heading" }, "Framed Dashboard"),
|
|
175
|
-
R("p", { key: "description", className: "" }, "Each widget is white on a grey background")
|
|
176
|
-
];
|
|
177
|
-
case "story":
|
|
178
|
-
return [
|
|
179
|
-
R("h4", { key: "name", className: "list-group-item-heading" }, "Story"),
|
|
180
|
-
R("p", { key: "description", className: "" }, "Ideal for data-driven storytelling with lots of text. Responsive and mobile-friendly")
|
|
181
|
-
];
|
|
182
|
-
}
|
|
183
|
-
return null;
|
|
184
|
-
})();
|
|
185
|
-
return R("a", {
|
|
186
|
-
key: style,
|
|
187
|
-
className: `list-group-item ${isActive ? "active" : ""}`,
|
|
188
|
-
onClick: this.handleStyleChange.bind(null, style)
|
|
189
|
-
}, content);
|
|
190
|
-
}
|
|
191
163
|
renderStyle() {
|
|
192
164
|
return R("button", { type: "button", key: "style", className: "btn btn-link btn-sm", onClick: this.handleOpenLayoutOptions }, R("span", { className: "fa fa-mobile" }), R("span", { className: "hide-600px" }, " Layout "));
|
|
193
165
|
}
|
|
@@ -232,7 +204,8 @@ class DashboardComponent extends react_1.default.Component {
|
|
|
232
204
|
locks: this.props.quickfilterLocks,
|
|
233
205
|
filters: this.getCompiledFilters(),
|
|
234
206
|
hideTopBorder: this.props.hideTitleBar,
|
|
235
|
-
|
|
207
|
+
// Don't hide if title bar is hidden as it can't be shown again
|
|
208
|
+
onHide: () => !this.props.hideTitleBar ? this.setState({ hideQuickfilters: true }) : undefined
|
|
236
209
|
});
|
|
237
210
|
}
|
|
238
211
|
refDashboardView = (el) => {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Quickfilter } from "../quickfilter/Quickfilter";
|
|
2
|
-
import {
|
|
2
|
+
import { Expr } from "@mwater/expressions";
|
|
3
3
|
import { BlocksLayoutOptions, DashboardTheme } from "./layoutOptions";
|
|
4
|
+
import { GlobalFilter } from "../GlobalFilter";
|
|
4
5
|
/** Dashboard design
|
|
5
6
|
* Each understands enough of the dashboard design to create widgets.
|
|
6
7
|
* Widget refers to the widget itself, where *item* refers also to the layout and id that it has in the dashboard.
|
|
@@ -27,19 +28,3 @@ export interface DashboardDesign {
|
|
|
27
28
|
/** array of global filters. See below. */
|
|
28
29
|
globalFilters?: GlobalFilter[];
|
|
29
30
|
}
|
|
30
|
-
/** Global Filters:
|
|
31
|
-
|
|
32
|
-
Global filters apply to multiple tables at once if a certain column is present. User-interface to set them is application-specific
|
|
33
|
-
and the default (non-mWater) dashboard applies them but does not allow editing.
|
|
34
|
-
|
|
35
|
-
*/
|
|
36
|
-
export interface GlobalFilter {
|
|
37
|
-
/** id of column to filter */
|
|
38
|
-
columnId: string;
|
|
39
|
-
/** type of column to filter (to ensure that consistent) */
|
|
40
|
-
columnType: LiteralType;
|
|
41
|
-
/** op of expression for filtering */
|
|
42
|
-
op: string;
|
|
43
|
-
/** array of expressions to use for filtering. field expression for column will be injected as expression 0 in the resulting filter expression */
|
|
44
|
-
exprs: Expr[];
|
|
45
|
-
}
|
|
@@ -114,17 +114,16 @@ class DashboardViewComponent extends react_1.default.Component {
|
|
|
114
114
|
}
|
|
115
115
|
// Call to print the dashboard
|
|
116
116
|
print = async () => {
|
|
117
|
-
// Temporarily
|
|
118
|
-
const mapTilerAPIKey = (0, vectorMaps_1.getMapTilerApiKey)();
|
|
117
|
+
// Temporarily enable print mode for vector maps
|
|
119
118
|
try {
|
|
120
|
-
(0, vectorMaps_1.
|
|
119
|
+
(0, vectorMaps_1.setPrintingModeEnabled)(true);
|
|
121
120
|
// Create element at 1080 wide (use as standard printing width)
|
|
122
121
|
const elem = R("div", { style: { width: 1080 } }, R(DashboardViewComponent, lodash_1.default.extend({}, this.props, { onDesignChange: null, printMode: true })));
|
|
123
122
|
const printer = new ReactElementPrinter_1.default();
|
|
124
123
|
await printer.print(elem, { delay: 5000 });
|
|
125
124
|
}
|
|
126
125
|
finally {
|
|
127
|
-
(0, vectorMaps_1.
|
|
126
|
+
(0, vectorMaps_1.setPrintingModeEnabled)(false);
|
|
128
127
|
}
|
|
129
128
|
};
|
|
130
129
|
// Get filters from props filters combined with dashboard filters
|
|
@@ -103,9 +103,10 @@ function ThemeToggle(props) {
|
|
|
103
103
|
return null;
|
|
104
104
|
}
|
|
105
105
|
return (react_1.default.createElement(bootstrap_1.FormGroup, { label: "Theme" },
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
106
|
+
react_1.default.createElement("div", { className: "list-group" },
|
|
107
|
+
renderStyleItem("default"),
|
|
108
|
+
renderStyleItem("greybg"),
|
|
109
|
+
renderStyleItem("story"))));
|
|
109
110
|
}
|
|
110
111
|
function WidthSelector(props) {
|
|
111
112
|
return (react_1.default.createElement(bootstrap_1.Select, { value: props.value, onChange: props.onChange, nullLabel: "N/A", options: [
|
|
@@ -1,33 +1,22 @@
|
|
|
1
|
-
import PropTypes from "prop-types";
|
|
2
1
|
import React from "react";
|
|
3
2
|
import { DataSource, Schema } from "@mwater/expressions";
|
|
3
|
+
import { DashboardDesign } from "./DashboardDesign";
|
|
4
4
|
export interface SettingsModalComponentProps {
|
|
5
5
|
onDesignChange: any;
|
|
6
6
|
schema: Schema;
|
|
7
7
|
dataSource: DataSource;
|
|
8
8
|
}
|
|
9
9
|
interface SettingsModalComponentState {
|
|
10
|
-
design:
|
|
10
|
+
design: DashboardDesign | null;
|
|
11
11
|
}
|
|
12
12
|
export default class SettingsModalComponent extends React.Component<SettingsModalComponentProps, SettingsModalComponentState> {
|
|
13
|
-
|
|
14
|
-
globalFiltersElementFactory: PropTypes.Requireable<(...args: any[]) => any>;
|
|
15
|
-
};
|
|
16
|
-
constructor(props: any);
|
|
13
|
+
constructor(props: SettingsModalComponentProps);
|
|
17
14
|
show(design: any): void;
|
|
18
15
|
handleSave: () => void;
|
|
19
16
|
handleCancel: () => void;
|
|
20
17
|
handleDesignChange: (design: any) => void;
|
|
21
18
|
handleFiltersChange: (filters: any) => void;
|
|
22
19
|
handleGlobalFiltersChange: (globalFilters: any) => void;
|
|
23
|
-
render(): React.
|
|
24
|
-
size: string;
|
|
25
|
-
onCancel: () => void;
|
|
26
|
-
onAction: () => void;
|
|
27
|
-
}, React.Component<{
|
|
28
|
-
size: string;
|
|
29
|
-
onCancel: () => void;
|
|
30
|
-
onAction: () => void;
|
|
31
|
-
}, any, any>> | null;
|
|
20
|
+
render(): React.JSX.Element | null;
|
|
32
21
|
}
|
|
33
22
|
export {};
|