@mwater/visualization 5.4.5 → 5.5.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.
Files changed (132) hide show
  1. package/.storybook/head.html +0 -1
  2. package/lib/MWaterContextComponent.js +1 -1
  3. package/lib/MWaterLoaderComponent.d.ts +2 -2
  4. package/lib/dashboards/DashboardComponent.js +2 -1
  5. package/lib/dashboards/LayoutOptionsComponent.js +18 -11
  6. package/lib/dashboards/ServerDashboardDataSource.d.ts +10 -1
  7. package/lib/dashboards/ServerDashboardDataSource.js +29 -0
  8. package/lib/dashboards/layoutOptions.d.ts +5 -1
  9. package/lib/datagrids/DatagridComponent.js +1 -1
  10. package/lib/datagrids/ExprCellComponent.d.ts +1 -0
  11. package/lib/datagrids/ExprCellComponent.js +22 -20
  12. package/lib/maps/BufferLayer.d.ts +18 -0
  13. package/lib/maps/BufferLayer.js +24 -14
  14. package/lib/maps/ChoroplethLayer.d.ts +18 -0
  15. package/lib/maps/ChoroplethLayer.js +34 -25
  16. package/lib/maps/ChoroplethLayerDesign.d.ts +3 -2
  17. package/lib/maps/ChoroplethLayerDesigner.d.ts +11 -1
  18. package/lib/maps/DirectMapDataSource.js +17 -0
  19. package/lib/maps/EditHoverOver.d.ts +1 -1
  20. package/lib/maps/EditHoverOver.js +62 -33
  21. package/lib/maps/HoverContent.d.ts +10 -5
  22. package/lib/maps/HoverContent.js +6 -35
  23. package/lib/maps/Layer.d.ts +37 -0
  24. package/lib/maps/Layer.js +30 -4
  25. package/lib/maps/MWaterServerLayer.d.ts +2 -2
  26. package/lib/maps/MWaterServerLayer.js +6 -6
  27. package/lib/maps/MapLayerDataSource.d.ts +9 -0
  28. package/lib/maps/MapUtils.d.ts +19 -1
  29. package/lib/maps/MapUtils.js +71 -1
  30. package/lib/maps/MarkersLayer.d.ts +18 -0
  31. package/lib/maps/MarkersLayer.js +24 -24
  32. package/lib/maps/MarkersLayerDesignerComponent.d.ts +14 -1
  33. package/lib/maps/RasterMapViewComponent.js +1 -1
  34. package/lib/maps/ServerMapDataSource.d.ts +9 -0
  35. package/lib/maps/ServerMapDataSource.js +29 -0
  36. package/lib/maps/VectorMapViewComponent.js +6 -6
  37. package/lib/maps/maps.d.ts +4 -2
  38. package/lib/mwater_table_selection/FormsListComponent.d.ts +33 -0
  39. package/lib/mwater_table_selection/FormsListComponent.js +141 -0
  40. package/lib/mwater_table_selection/IndicatorsListComponent.d.ts +47 -0
  41. package/lib/mwater_table_selection/IndicatorsListComponent.js +182 -0
  42. package/lib/mwater_table_selection/IssuesListComponent.d.ts +29 -0
  43. package/lib/mwater_table_selection/IssuesListComponent.js +123 -0
  44. package/lib/mwater_table_selection/MWaterAccountingSystemListComponent.d.ts +20 -0
  45. package/lib/mwater_table_selection/MWaterAccountingSystemListComponent.js +157 -0
  46. package/lib/mwater_table_selection/MWaterAssetSystemsListComponent.d.ts +17 -0
  47. package/lib/mwater_table_selection/MWaterAssetSystemsListComponent.js +79 -0
  48. package/lib/mwater_table_selection/MWaterCompleteTableSelectComponent.d.ts +37 -0
  49. package/lib/mwater_table_selection/MWaterCompleteTableSelectComponent.js +275 -0
  50. package/lib/mwater_table_selection/MWaterCustomTablesetListComponent.d.ts +17 -0
  51. package/lib/mwater_table_selection/MWaterCustomTablesetListComponent.js +94 -0
  52. package/lib/mwater_table_selection/MWaterMetricsTableListComponent.d.ts +17 -0
  53. package/lib/mwater_table_selection/MWaterMetricsTableListComponent.js +80 -0
  54. package/lib/mwater_table_selection/MWaterTableSelectComponent.d.ts +32 -0
  55. package/lib/mwater_table_selection/MWaterTableSelectComponent.js +158 -0
  56. package/lib/widgets/charts/Chart.d.ts +11 -0
  57. package/lib/widgets/charts/Chart.js +15 -0
  58. package/lib/widgets/charts/ChartWidgetComponent.d.ts +1 -0
  59. package/lib/widgets/charts/ChartWidgetComponent.js +27 -1
  60. package/lib/widgets/charts/layered/LayeredChartDesign.d.ts +1 -1
  61. package/lib/widgets/charts/layered/LayeredChartDesignerComponent.d.ts +1 -1
  62. package/lib/widgets/charts/layered/LayeredChartDesignerComponent.js +5 -12
  63. package/lib/widgets/charts/layered/LayeredChartLayerDesignerComponent.d.ts +43 -57
  64. package/lib/widgets/charts/layered/LayeredChartLayerDesignerComponent.js +113 -110
  65. package/lib/widgets/charts/layered/LayeredChartUtils.d.ts +2 -1
  66. package/lib/widgets/charts/layered/LayeredChartUtils.js +0 -2
  67. package/lib/widgets/charts/pivot/PivotChart.d.ts +2 -0
  68. package/lib/widgets/charts/pivot/PivotChart.js +156 -0
  69. package/lib/widgets/charts/pivot/PivotChartDesignerComponent.d.ts +5 -20
  70. package/lib/widgets/charts/pivot/PivotChartDesignerComponent.js +31 -61
  71. package/lib/widgets/charts/pivot/PivotChartLayoutBuilder.d.ts +4 -0
  72. package/lib/widgets/charts/pivot/PivotChartLayoutBuilder.js +4 -2
  73. package/lib/widgets/charts/pivot/PivotChartLayoutComponent.d.ts +5 -44
  74. package/lib/widgets/charts/pivot/PivotChartLayoutComponent.js +38 -63
  75. package/lib/widgets/charts/pivot/SegmentDesignerComponent.d.ts +7 -68
  76. package/lib/widgets/charts/pivot/SegmentDesignerComponent.js +58 -106
  77. package/lib/widgets/charts/table/TableChart.d.ts +2 -0
  78. package/lib/widgets/charts/table/TableChart.js +172 -1
  79. package/lib/widgets/charts/table/TableChartDesignerComponent.d.ts +7 -17
  80. package/lib/widgets/charts/table/TableChartDesignerComponent.js +79 -95
  81. package/lib/widgets/charts/table/TableChartViewComponent.d.ts +1 -7
  82. package/lib/widgets/charts/table/TableChartViewComponent.js +19 -27
  83. package/package.json +3 -8
  84. package/src/MWaterContextComponent.tsx +1 -1
  85. package/src/MWaterLoaderComponent.ts +1 -1
  86. package/src/dashboards/DashboardComponent.tsx +2 -1
  87. package/src/dashboards/LayoutOptionsComponent.tsx +22 -10
  88. package/src/dashboards/ServerDashboardDataSource.ts +36 -1
  89. package/src/dashboards/layoutOptions.tsx +5 -1
  90. package/src/datagrids/DatagridComponent.tsx +1 -1
  91. package/src/datagrids/ExprCellComponent.tsx +23 -20
  92. package/src/maps/BufferLayer.ts +35 -20
  93. package/src/maps/ChoroplethLayer.ts +51 -33
  94. package/src/maps/ChoroplethLayerDesign.ts +3 -2
  95. package/src/maps/ChoroplethLayerDesigner.tsx +2 -2
  96. package/src/maps/DirectMapDataSource.ts +21 -1
  97. package/src/maps/EditHoverOver.tsx +91 -51
  98. package/src/maps/HoverContent.tsx +16 -47
  99. package/src/maps/Layer.ts +42 -4
  100. package/src/maps/MWaterServerLayer.ts +6 -6
  101. package/src/maps/MapLayerDataSource.ts +8 -0
  102. package/src/maps/MapUtils.ts +70 -3
  103. package/src/maps/MarkersLayer.ts +34 -24
  104. package/src/maps/RasterMapViewComponent.ts +1 -1
  105. package/src/maps/ServerMapDataSource.ts +35 -0
  106. package/src/maps/VectorMapViewComponent.tsx +6 -6
  107. package/src/maps/maps.ts +4 -2
  108. package/src/mwater_table_selection/FormsListComponent.tsx +188 -0
  109. package/src/mwater_table_selection/IndicatorsListComponent.tsx +283 -0
  110. package/src/mwater_table_selection/IssuesListComponent.tsx +167 -0
  111. package/src/mwater_table_selection/MWaterAccountingSystemListComponent.tsx +225 -0
  112. package/src/{MWaterAssetSystemsListComponent.tsx → mwater_table_selection/MWaterAssetSystemsListComponent.tsx} +2 -2
  113. package/src/mwater_table_selection/MWaterCompleteTableSelectComponent.tsx +377 -0
  114. package/src/{MWaterCustomTablesetListComponent.tsx → mwater_table_selection/MWaterCustomTablesetListComponent.tsx} +1 -1
  115. package/src/{MWaterMetricsTableListComponent.tsx → mwater_table_selection/MWaterMetricsTableListComponent.tsx} +1 -1
  116. package/src/{MWaterTableSelectComponent.tsx → mwater_table_selection/MWaterTableSelectComponent.tsx} +83 -86
  117. package/src/widgets/charts/Chart.ts +17 -0
  118. package/src/widgets/charts/ChartWidgetComponent.tsx +36 -1
  119. package/src/widgets/charts/layered/LayeredChartDesign.ts +1 -1
  120. package/src/widgets/charts/layered/LayeredChartDesignerComponent.tsx +23 -24
  121. package/src/widgets/charts/layered/LayeredChartLayerDesignerComponent.tsx +260 -211
  122. package/src/widgets/charts/layered/LayeredChartUtils.ts +7 -7
  123. package/src/widgets/charts/pivot/PivotChart.ts +191 -0
  124. package/src/widgets/charts/pivot/PivotChartDesignerComponent.tsx +124 -129
  125. package/src/widgets/charts/pivot/PivotChartLayoutBuilder.ts +4 -2
  126. package/src/widgets/charts/pivot/PivotChartLayoutComponent.tsx +120 -149
  127. package/src/widgets/charts/pivot/SegmentDesignerComponent.tsx +178 -198
  128. package/src/widgets/charts/table/TableChart.ts +177 -1
  129. package/src/widgets/charts/table/TableChartDesignerComponent.tsx +422 -0
  130. package/src/widgets/charts/table/{TableChartViewComponent.ts → TableChartViewComponent.tsx} +65 -60
  131. package/src/MWaterCompleteTableSelectComponent.tsx +0 -975
  132. package/src/widgets/charts/table/TableChartDesignerComponent.ts +0 -441
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.MWaterCustomTablesetListComponent = void 0;
7
+ const lodash_1 = __importDefault(require("lodash"));
8
+ const expressions_1 = require("@mwater/expressions");
9
+ const react_1 = require("react");
10
+ const react_2 = __importDefault(require("react"));
11
+ const UIComponents_1 = require("../UIComponents");
12
+ const bootstrap_1 = require("@mwater/react-library/lib/bootstrap");
13
+ /** Searchable list of custom tables */
14
+ const MWaterCustomTablesetListComponent = (props) => {
15
+ const [tablesets, setTablesets] = (0, react_1.useState)();
16
+ const [search, setSearch] = (0, react_1.useState)("");
17
+ const [extraTableNeeded, setExtraTableNeeded] = (0, react_1.useState)();
18
+ const [showSystem, setShowSystem] = (0, react_1.useState)(false);
19
+ // Get list of all tablesets
20
+ (0, react_1.useEffect)(() => {
21
+ fetch(`${props.apiUrl}custom_tablesets?client=${props.client || ""}`)
22
+ .then((response) => response.json())
23
+ .then((tablesets) => {
24
+ // Filter out non-normal
25
+ tablesets = tablesets.filter((ts) => ts.type === "normal");
26
+ // Put included ones first
27
+ setTablesets(lodash_1.default.sortByAll(tablesets, [
28
+ (ts) => (props.extraTables.some((t) => (t || "").startsWith(`custom.${ts.code}.`)) ? 0 : 1),
29
+ (ts) => expressions_1.ExprUtils.localizeString(ts.design.name, props.locale)
30
+ ]));
31
+ });
32
+ }, []);
33
+ (0, react_1.useEffect)(() => {
34
+ if (extraTableNeeded && props.schema.getTable(extraTableNeeded)) {
35
+ props.onChange(extraTableNeeded);
36
+ }
37
+ });
38
+ const selectTable = (ts, tableId) => {
39
+ const qualifiedTableId = `custom.${ts.code}.${tableId}`;
40
+ // If already included, select it
41
+ if (props.schema.getTable(qualifiedTableId)) {
42
+ props.onChange(qualifiedTableId);
43
+ return;
44
+ }
45
+ // Request extra tables as wildcard
46
+ setExtraTableNeeded(qualifiedTableId);
47
+ props.onExtraTableAdd(`custom.${ts.code}.*`);
48
+ };
49
+ const handleRemove = (ts) => {
50
+ // Remove from extra tables
51
+ const match = props.extraTables.find((t) => (t || "").startsWith(`custom.${ts.code}.`));
52
+ if (match) {
53
+ if (confirm(T `Remove this set of tables? Some widgets may not work correctly.`)) {
54
+ props.onChange(null);
55
+ props.onExtraTableRemove(match);
56
+ }
57
+ }
58
+ };
59
+ if (!tablesets || extraTableNeeded) {
60
+ return (react_2.default.createElement("div", null,
61
+ react_2.default.createElement("i", { className: "fa fa-spin fa-spinner" }),
62
+ " ",
63
+ T `Loading...`));
64
+ }
65
+ const renderTableset = (ts) => {
66
+ const name = expressions_1.ExprUtils.localizeString(ts.design.name, props.locale) || "";
67
+ // Check search
68
+ if (search &&
69
+ !name.toLowerCase().includes(search.toLowerCase()) &&
70
+ !ts.design.tables.some((t) => expressions_1.ExprUtils.localizeString(t.name).toLowerCase().includes(search.toLowerCase()))) {
71
+ return null;
72
+ }
73
+ const items = ts.design.tables
74
+ .filter((t) => !t.deprecated)
75
+ .map((t) => ({
76
+ name: expressions_1.ExprUtils.localizeString(t.name, props.locale),
77
+ onClick: () => selectTable(ts, t.id)
78
+ }));
79
+ const alreadyIncluded = props.extraTables.some((t) => (t || "").startsWith(`custom.${ts.code}.`));
80
+ return (react_2.default.createElement("div", { key: ts.code },
81
+ alreadyIncluded ? (react_2.default.createElement("div", { style: { float: "right" } },
82
+ react_2.default.createElement("button", { className: "btn btn-sm btn-link", type: "button", onClick: () => handleRemove(ts) },
83
+ react_2.default.createElement("i", { className: "fa fa-remove" })))) : null,
84
+ react_2.default.createElement("h4", { className: "text-muted" }, name),
85
+ react_2.default.createElement(UIComponents_1.OptionListComponent, { items: items })));
86
+ };
87
+ const visibleTablesets = tablesets.filter((ts) => (showSystem || !ts.design.system) && !ts.design.deprecated);
88
+ return (react_2.default.createElement("div", null,
89
+ react_2.default.createElement(bootstrap_1.TextInput, { value: search, onChange: setSearch, placeholder: T `Search...` }),
90
+ visibleTablesets.map((ts) => renderTableset(ts)),
91
+ react_2.default.createElement("div", null,
92
+ react_2.default.createElement("button", { className: "btn btn-link btn-sm", onClick: () => setShowSystem(!showSystem) }, showSystem ? T `Hide system tables` : T `Show system tables`))));
93
+ };
94
+ exports.MWaterCustomTablesetListComponent = MWaterCustomTablesetListComponent;
@@ -0,0 +1,17 @@
1
+ import { Schema } from "@mwater/expressions";
2
+ import React from "react";
3
+ /** Searchable list of metric tables */
4
+ export declare const MWaterMetricsTableListComponent: (props: {
5
+ apiUrl: string;
6
+ schema: Schema;
7
+ client?: string;
8
+ /** User id */
9
+ user?: string;
10
+ /** Called with table selected */
11
+ onChange: (tableId: string | null) => void;
12
+ extraTables: string[];
13
+ onExtraTableAdd: (tableId: string) => void;
14
+ onExtraTableRemove: (tableId: string) => void;
15
+ /** e.g. "en" */
16
+ locale?: string;
17
+ }) => React.JSX.Element;
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.MWaterMetricsTableListComponent = void 0;
7
+ const lodash_1 = __importDefault(require("lodash"));
8
+ const expressions_1 = require("@mwater/expressions");
9
+ const react_1 = require("react");
10
+ const react_2 = __importDefault(require("react"));
11
+ const UIComponents_1 = require("../UIComponents");
12
+ const bootstrap_1 = require("@mwater/react-library/lib/bootstrap");
13
+ /** Searchable list of metric tables */
14
+ const MWaterMetricsTableListComponent = (props) => {
15
+ const [metrics, setMetrics] = (0, react_1.useState)();
16
+ const [search, setSearch] = (0, react_1.useState)("");
17
+ const [extraTableNeeded, setExtraTableNeeded] = (0, react_1.useState)();
18
+ // Get list of all metrics
19
+ (0, react_1.useEffect)(() => {
20
+ fetch(`${props.apiUrl}metrics?client=${props.client || ""}`)
21
+ .then((response) => response.json())
22
+ .then((body) => {
23
+ // Put included ones first
24
+ setMetrics(lodash_1.default.sortByAll(body, [
25
+ (m) => (props.extraTables.some((t) => (t = `metrics:${m._id}`)) ? 0 : 1),
26
+ (m) => expressions_1.ExprUtils.localizeString(m.design.name, props.locale)
27
+ ]));
28
+ });
29
+ }, []);
30
+ (0, react_1.useEffect)(() => {
31
+ if (extraTableNeeded && props.schema.getTable(extraTableNeeded)) {
32
+ props.onChange(extraTableNeeded);
33
+ }
34
+ });
35
+ const selectTable = (metric) => {
36
+ const qualifiedTableId = `metrics:${metric._id}`;
37
+ // If already included, select it
38
+ if (props.schema.getTable(qualifiedTableId)) {
39
+ props.onChange(qualifiedTableId);
40
+ return;
41
+ }
42
+ // Request extra tables as wildcard
43
+ setExtraTableNeeded(qualifiedTableId);
44
+ props.onExtraTableAdd(qualifiedTableId);
45
+ };
46
+ const handleRemove = (metric) => {
47
+ // Remove from extra tables
48
+ const match = props.extraTables.find((t) => t == `metrics:${metric._id}`);
49
+ if (match) {
50
+ if (confirm(T `Remove this table? Some widgets may not work correctly.`)) {
51
+ props.onChange(null);
52
+ props.onExtraTableRemove(match);
53
+ }
54
+ }
55
+ };
56
+ if (!metrics || extraTableNeeded) {
57
+ return (react_2.default.createElement("div", null,
58
+ react_2.default.createElement("i", { className: "fa fa-spin fa-spinner" }),
59
+ " ",
60
+ T `Loading...`));
61
+ }
62
+ const renderMetrics = () => {
63
+ const items = metrics
64
+ .filter((m) => !m.design.deprecated)
65
+ .map((m) => {
66
+ const alreadyIncluded = props.extraTables.some((t) => t == `metrics:${m._id}`);
67
+ return {
68
+ name: expressions_1.ExprUtils.localizeString(m.design.name, props.locale) || "",
69
+ onClick: () => selectTable(m),
70
+ onRemove: alreadyIncluded ? handleRemove.bind(null, m) : undefined
71
+ };
72
+ })
73
+ .filter((item) => !search || !item.name.toLowerCase().includes(search.toLowerCase()));
74
+ return react_2.default.createElement(UIComponents_1.OptionListComponent, { items: items });
75
+ };
76
+ return (react_2.default.createElement("div", null,
77
+ react_2.default.createElement(bootstrap_1.TextInput, { value: search, onChange: setSearch, placeholder: T `Search...` }),
78
+ renderMetrics()));
79
+ };
80
+ exports.MWaterMetricsTableListComponent = MWaterMetricsTableListComponent;
@@ -0,0 +1,32 @@
1
+ import React from "react";
2
+ import { ToggleEditComponent } from "../UIComponents";
3
+ import { Schema } from "@mwater/expressions";
4
+ export interface MWaterTableSelectComponentProps {
5
+ /** Url to hit api */
6
+ apiUrl: string;
7
+ /** Optional client */
8
+ client?: string;
9
+ schema: Schema;
10
+ /** User id */
11
+ user?: string;
12
+ table?: string;
13
+ /** Called with table selected */
14
+ onChange: (table: string | null) => void;
15
+ extraTables: string[];
16
+ onExtraTablesChange: (tables: string[]) => void;
17
+ /** Can also perform filtering for some types. Include these props to enable this */
18
+ filter?: any;
19
+ onFilterChange?: (filter: any) => void;
20
+ }
21
+ interface MWaterTableSelectComponentState {
22
+ pendingExtraTable: string | null;
23
+ }
24
+ export default class MWaterTableSelectComponent extends React.Component<MWaterTableSelectComponentProps, MWaterTableSelectComponentState> {
25
+ toggleEdit: ToggleEditComponent | null;
26
+ constructor(props: MWaterTableSelectComponentProps);
27
+ componentDidUpdate(prevProps: MWaterTableSelectComponentProps): void;
28
+ handleChange: (tableId: string | null) => void;
29
+ handleTableChange: (tableId: string | null) => void;
30
+ render(): React.JSX.Element;
31
+ }
32
+ export {};
@@ -0,0 +1,158 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const lodash_1 = __importDefault(require("lodash"));
7
+ const react_1 = __importDefault(require("react"));
8
+ const UIComponents_1 = require("../UIComponents");
9
+ const expressions_1 = require("@mwater/expressions");
10
+ const MWaterResponsesFilterComponent_1 = __importDefault(require("../MWaterResponsesFilterComponent"));
11
+ const ModalPopupComponent_1 = __importDefault(require("@mwater/react-library/lib/ModalPopupComponent"));
12
+ const MWaterCompleteTableSelectComponent_1 = __importDefault(require("./MWaterCompleteTableSelectComponent"));
13
+ const expressions_ui_1 = require("@mwater/expressions-ui");
14
+ // Allows selection of a mwater-visualization table. Loads forms as well and calls event if modified
15
+ class MWaterTableSelectComponent extends react_1.default.Component {
16
+ toggleEdit = null;
17
+ constructor(props) {
18
+ super(props);
19
+ this.state = {
20
+ pendingExtraTable: null // Set when waiting for a table to load
21
+ };
22
+ }
23
+ componentDidUpdate(prevProps) {
24
+ // If received new schema with pending extra table, select it
25
+ let table = null;
26
+ if (this.state.pendingExtraTable) {
27
+ table = this.state.pendingExtraTable;
28
+ if (this.props.schema.getTable(table)) {
29
+ // No longer waiting
30
+ this.setState({ pendingExtraTable: null });
31
+ // Close toggle edit
32
+ this.toggleEdit?.close();
33
+ // Fire change
34
+ this.props.onChange(table);
35
+ }
36
+ }
37
+ // If table is newly selected and is a responses table and no filters, set filters to final only
38
+ if (this.props.table &&
39
+ this.props.table.match(/responses:/) &&
40
+ this.props.table !== prevProps.table &&
41
+ !this.props.filter &&
42
+ this.props.onFilterChange) {
43
+ this.props.onFilterChange({
44
+ type: "op",
45
+ op: "= any",
46
+ table: this.props.table,
47
+ exprs: [
48
+ { type: "field", table: this.props.table, column: "status" },
49
+ { type: "literal", valueType: "enumset", value: ["final"] }
50
+ ]
51
+ });
52
+ }
53
+ }
54
+ handleChange = (tableId) => {
55
+ // Close toggle edit
56
+ this.toggleEdit?.close();
57
+ // Call onChange if different
58
+ if (tableId !== this.props.table) {
59
+ return this.props.onChange(tableId);
60
+ }
61
+ };
62
+ handleTableChange = (tableId) => {
63
+ // If not part of extra tables, add it and wait for new schema
64
+ if (tableId && !this.props.schema.getTable(tableId)) {
65
+ return this.setState({ pendingExtraTable: tableId }, () => {
66
+ return this.props.onExtraTablesChange(lodash_1.default.union(this.props.extraTables, [tableId]));
67
+ });
68
+ }
69
+ else {
70
+ return this.handleChange(tableId);
71
+ }
72
+ };
73
+ render() {
74
+ const editor = react_1.default.createElement(EditModeTableSelectComponent, { apiUrl: this.props.apiUrl, client: this.props.client, schema: this.props.schema, user: this.props.user, table: this.props.table, onChange: this.handleTableChange, extraTables: this.props.extraTables, onExtraTablesChange: this.props.onExtraTablesChange });
75
+ return (react_1.default.createElement("div", null,
76
+ this.state.pendingExtraTable ? (react_1.default.createElement("div", { className: "alert alert-info", key: "pendingExtraTable" },
77
+ react_1.default.createElement("i", { className: "fa fa-spinner fa-spin" }),
78
+ "\u00A0",
79
+ T `Please wait...`)) : undefined,
80
+ react_1.default.createElement(UIComponents_1.ToggleEditComponent, { ref: (c) => {
81
+ this.toggleEdit = c;
82
+ }, forceOpen: !this.props.table, label: this.props.table
83
+ ? expressions_1.ExprUtils.localizeString(this.props.schema.getTable(this.props.table)?.name, T.locale)
84
+ : "", editor: editor, onRemove: () => {
85
+ this.props.onChange(null);
86
+ } }),
87
+ this.props.table &&
88
+ this.props.onFilterChange &&
89
+ this.props.table.match(/^responses:/) &&
90
+ this.props.schema.getTable(this.props.table) ? (react_1.default.createElement(MWaterResponsesFilterComponent_1.default, { schema: this.props.schema, table: this.props.table, filter: this.props.filter, onFilterChange: this.props.onFilterChange })) : undefined));
91
+ }
92
+ }
93
+ exports.default = MWaterTableSelectComponent;
94
+ // Is the table select component when in edit mode. Toggles between complete list and simplified list
95
+ class EditModeTableSelectComponent extends react_1.default.Component {
96
+ constructor(props) {
97
+ super(props);
98
+ this.state = {
99
+ // True when in popup mode that shows all tables
100
+ completeMode: false
101
+ };
102
+ }
103
+ handleShowMore = () => {
104
+ return this.setState({ completeMode: true });
105
+ };
106
+ // Get list of tables that should be included in shortlist
107
+ // This is all active tables and all responses tables in schema (so as to include rosters) and all extra tables
108
+ // Also includes current table
109
+ getTableShortlist(activeTables) {
110
+ let tables = activeTables;
111
+ // Remove dead tables
112
+ tables = tables.filter((t) => this.props.schema.getTable(t) != null && !this.props.schema.getTable(t)?.deprecated);
113
+ tables = lodash_1.default.union(tables, lodash_1.default.filter(lodash_1.default.pluck(this.props.schema.getTables(), "id"), (t) => t.match(/^responses:/)));
114
+ if (this.props.table) {
115
+ tables = lodash_1.default.union(tables, [this.props.table]);
116
+ }
117
+ for (let extraTable of this.props.extraTables || []) {
118
+ // Check if wildcard
119
+ if (extraTable.match(/\*$/)) {
120
+ for (let table of this.props.schema.getTables()) {
121
+ if (table.id.startsWith(extraTable.substr(0, extraTable.length - 1)) && !table.deprecated) {
122
+ tables = lodash_1.default.union(tables, [table.id]);
123
+ }
124
+ }
125
+ }
126
+ else {
127
+ // Add if exists
128
+ if (this.props.schema.getTable(extraTable) && !this.props.schema.getTable(extraTable)?.deprecated) {
129
+ tables = lodash_1.default.union(tables, [extraTable]);
130
+ }
131
+ }
132
+ }
133
+ // Sort by name
134
+ tables = lodash_1.default.sortBy(tables, (tableId) => expressions_1.ExprUtils.localizeString(this.props.schema.getTable(tableId)?.name, T.locale));
135
+ return tables;
136
+ }
137
+ handleCompleteChange = (tableId) => {
138
+ this.setState({ completeMode: false });
139
+ return this.props.onChange(tableId);
140
+ };
141
+ render() {
142
+ return (react_1.default.createElement(expressions_ui_1.ActiveTablesContext.Consumer, null, (activeTables) => (react_1.default.createElement("div", null,
143
+ this.state.completeMode ? (react_1.default.createElement(ModalPopupComponent_1.default, { header: T `Select Data Source`, onClose: () => this.setState({ completeMode: false }), showCloseX: true, size: "x-large" },
144
+ react_1.default.createElement(MWaterCompleteTableSelectComponent_1.default, { apiUrl: this.props.apiUrl, client: this.props.client, schema: this.props.schema, user: this.props.user, table: this.props.table, onChange: this.handleCompleteChange, extraTables: this.props.extraTables, onExtraTablesChange: this.props.onExtraTablesChange }))) : null,
145
+ this.getTableShortlist(activeTables).length > 0 ? (react_1.default.createElement(react_1.default.Fragment, null,
146
+ react_1.default.createElement("div", { className: "text-muted" }, T `Select Data Source:`),
147
+ react_1.default.createElement(UIComponents_1.OptionListComponent, { items: this.getTableShortlist(activeTables).map((tableId) => {
148
+ const table = this.props.schema.getTable(tableId);
149
+ return {
150
+ name: expressions_1.ExprUtils.localizeString(table?.name, T.locale),
151
+ desc: expressions_1.ExprUtils.localizeString(table?.desc, T.locale),
152
+ onClick: () => table && this.props.onChange(table.id)
153
+ };
154
+ }) }),
155
+ react_1.default.createElement("div", null,
156
+ react_1.default.createElement("button", { type: "button", className: "btn btn-link btn-sm", onClick: this.handleShowMore }, T `Show All Available Data Sources...`)))) : (react_1.default.createElement("button", { type: "button", className: "btn btn-link", onClick: this.handleShowMore }, T `Select Data Source...`))))));
157
+ }
158
+ }
@@ -62,4 +62,15 @@ export default class Chart {
62
62
  getTranslatableStrings(design: any, schema: Schema): string[];
63
63
  /** Translates the design. Override in subclasses. Design is already cleaned. */
64
64
  translateDesign(design: any, translate: (input: string) => string): any;
65
+ /** Determines if the chart supports exporting to XLSX format */
66
+ supportsXlsxExport(): boolean;
67
+ /** Creates a SheetJS workbook for the chart data
68
+ * @param design Chart design
69
+ * @param schema Schema to use
70
+ * @param dataSource Data source
71
+ * @param data Data from chart
72
+ * @param locale Locale to use
73
+ * @returns base64 encoded XLSX file
74
+ */
75
+ createXlsxWorkbook(design: any, schema: Schema, dataSource: DataSource, data: any, locale: string): string;
65
76
  }
@@ -90,5 +90,20 @@ class Chart {
90
90
  translateDesign(design, translate) {
91
91
  return design;
92
92
  }
93
+ /** Determines if the chart supports exporting to XLSX format */
94
+ supportsXlsxExport() {
95
+ return false;
96
+ }
97
+ /** Creates a SheetJS workbook for the chart data
98
+ * @param design Chart design
99
+ * @param schema Schema to use
100
+ * @param dataSource Data source
101
+ * @param data Data from chart
102
+ * @param locale Locale to use
103
+ * @returns base64 encoded XLSX file
104
+ */
105
+ createXlsxWorkbook(design, schema, dataSource, data, locale) {
106
+ throw new Error("Not implemented");
107
+ }
93
108
  }
94
109
  exports.default = Chart;
@@ -44,6 +44,7 @@ export declare class ChartWidgetComponent extends React.PureComponent<ChartWidge
44
44
  static contextType: React.Context<string>;
45
45
  constructor(props: any);
46
46
  handleSaveCsvFile: () => void | AbortController | undefined;
47
+ handleSaveXlsxFile: () => void;
47
48
  handleStartEditing: () => void;
48
49
  handleEndEditing: () => void;
49
50
  handleCancelEditing: () => void;
@@ -69,6 +69,28 @@ class ChartWidgetComponent extends react_1.default.PureComponent {
69
69
  return FileSaver.saveAs(blob, "Exported Data.csv");
70
70
  });
71
71
  };
72
+ // Saves an xlsx file to disk
73
+ handleSaveXlsxFile = () => {
74
+ // Get the data
75
+ this.props.widgetDataSource.getData(this.props.design, this.props.filters, (err, data) => {
76
+ if (err) {
77
+ console.error(err);
78
+ alert(T `Failed to get data: ${err.message}`);
79
+ return;
80
+ }
81
+ try {
82
+ // Create workbook
83
+ const blob = this.props.chart.createXlsxWorkbook(this.props.design, this.props.schema, this.props.dataSource, data, this.context);
84
+ // Require at use as causes server problems
85
+ const FileSaver = require("file-saver");
86
+ FileSaver.saveAs(blob, "Exported Data.xlsx");
87
+ }
88
+ catch (ex) {
89
+ console.error(ex);
90
+ alert(T `Failed to export data: ${ex.message}`);
91
+ }
92
+ });
93
+ };
72
94
  handleStartEditing = () => {
73
95
  // Can't edit if already editing
74
96
  if (this.state.editDesign) {
@@ -149,7 +171,11 @@ class ChartWidgetComponent extends react_1.default.PureComponent {
149
171
  // Create dropdown items
150
172
  const dropdownItems = this.props.chart.createDropdownItems(design, this.props.schema, this.props.widgetDataSource, this.props.filters);
151
173
  if (!designError) {
152
- dropdownItems.push({ label: T `Export Data`, onClick: this.handleSaveCsvFile });
174
+ dropdownItems.push({ label: T `Export Data (CSV)`, onClick: this.handleSaveCsvFile });
175
+ // Add XLSX export option if supported
176
+ if (this.props.chart.supportsXlsxExport()) {
177
+ dropdownItems.push({ label: T `Export Data (XLSX)`, onClick: this.handleSaveXlsxFile });
178
+ }
153
179
  }
154
180
  if (this.props.onDesignChange != null) {
155
181
  dropdownItems.unshift({
@@ -45,7 +45,7 @@ export interface LayeredChartDesign {
45
45
  }
46
46
  export interface LayeredChartDesignLayer {
47
47
  /** bar/line/spline/scatter/area/pie/donut (overrides main one) */
48
- type: "bar" | "line" | "spline" | "scatter" | "area" | "pie" | "donut";
48
+ type?: "bar" | "line" | "spline" | "scatter" | "area" | "pie" | "donut";
49
49
  /** table of layer */
50
50
  table: string;
51
51
  /** label for layer (optional) */
@@ -36,7 +36,7 @@ export default class LayeredChartDesignerComponent extends React.Component<Layer
36
36
  handleXAxisTickMultilineChange: (value: boolean) => void;
37
37
  renderLabels(): React.DetailedReactHTMLElement<React.HTMLAttributes<HTMLElement>, HTMLElement> | undefined;
38
38
  renderChartOptions(): React.JSX.Element;
39
- renderType(): React.CElement<uiComponents.SectionComponentProps, uiComponents.SectionComponent>;
39
+ renderType(): React.JSX.Element;
40
40
  renderLayer: (index: any) => React.DetailedReactHTMLElement<{
41
41
  style: {
42
42
  paddingTop: number;
@@ -154,24 +154,17 @@ class LayeredChartDesignerComponent extends react_1.default.Component {
154
154
  { id: "scatter", name: T `Scatter`, desc: T `Show correlation between two number variables` },
155
155
  { id: "area", name: T `Area`, desc: T `For cumulative data over time` }
156
156
  ];
157
- const current = lodash_1.default.findWhere(chartTypes, { id: this.props.design.type });
158
- return R(uiComponents.SectionComponent, { icon: "glyphicon-th", label: T `Chart Type` }, R(uiComponents.ToggleEditComponent, {
159
- forceOpen: !this.props.design.type,
160
- label: current ? current.name : "",
161
- editor: (onClose) => {
162
- return R(uiComponents.OptionListComponent, {
163
- hint: T `Select a Chart Type`,
164
- items: lodash_1.default.map(chartTypes, ct => ({
157
+ const current = chartTypes.find(ct => ct.id === this.props.design.type);
158
+ return (react_1.default.createElement(uiComponents.SectionComponent, { label: T `Chart Type` },
159
+ react_1.default.createElement(uiComponents.ToggleEditComponent, { forceOpen: !this.props.design.type, label: current ? current.name : "", editor: (onClose) => (react_1.default.createElement(uiComponents.OptionListComponent, { hint: T `Select a Chart Type`, items: chartTypes.map(ct => ({
165
160
  name: ct.name,
166
161
  desc: ct.desc,
167
162
  onClick: () => {
168
163
  onClose(); // Close editor first
169
164
  return this.handleTypeChange(ct.id);
170
165
  }
171
- }))
172
- });
173
- }
174
- }), this.renderOptions());
166
+ })) })) }),
167
+ this.renderOptions()));
175
168
  }
176
169
  renderLayer = (index) => {
177
170
  const style = {
@@ -1,66 +1,52 @@
1
1
  import React from "react";
2
- import { DataSource, Schema } from "@mwater/expressions";
3
- import * as uiComponents from "../../../UIComponents";
2
+ import { DataSource, Schema, LiteralType, Expr } from "@mwater/expressions";
3
+ import { LayeredChartDesignLayer, LayeredChartDesign } from "./LayeredChartDesign";
4
+ import { Axis } from "../../../axes/Axis";
5
+ import { JsonQLFilter } from "@mwater/visualization";
4
6
  export interface LayeredChartLayerDesignerComponentProps {
5
- design: any;
7
+ design: LayeredChartDesign;
6
8
  schema: Schema;
7
9
  dataSource: DataSource;
8
10
  index: number;
9
- onChange: any;
10
- onRemove: any;
11
- filters?: any;
11
+ onChange: (layer: LayeredChartDesignLayer) => void;
12
+ onRemove: () => void;
13
+ filters?: JsonQLFilter[];
12
14
  }
13
15
  export default class LayeredChartLayerDesignerComponent extends React.Component<LayeredChartLayerDesignerComponentProps> {
14
- isLayerPolar(layer: any): boolean;
15
- doesLayerNeedGrouping(layer: any): boolean;
16
- isXAxisRequired(layer: any): boolean;
17
- getAxisTypes(layer: any, axisKey: any): string[] | null;
18
- getAxisLabel(icon: any, label: any): React.DetailedReactHTMLElement<React.HTMLAttributes<HTMLElement>, HTMLElement>;
19
- getXAxisLabel(layer: any): React.DetailedReactHTMLElement<React.HTMLAttributes<HTMLElement>, HTMLElement>;
20
- getYAxisLabel(layer: any): React.DetailedReactHTMLElement<React.HTMLAttributes<HTMLElement>, HTMLElement>;
21
- getColorAxisLabel(layer: any): React.DetailedReactHTMLElement<React.HTMLAttributes<HTMLElement>, HTMLElement>;
22
- updateLayer(changes: any): any;
23
- updateAxes(changes: any): any;
24
- handleNameChange: (ev: any) => any;
25
- handleTableChange: (table: any) => any;
26
- handleXAxisChange: (axis: any) => any;
27
- handleXColorMapChange: (xColorMap: any) => any;
28
- handleColorAxisChange: (axis: any) => any;
29
- handleYAxisChange: (axis: any) => any;
30
- handleFilterChange: (filter: any) => any;
31
- handleColorChange: (color: any) => any;
32
- handleCumulativeChange: (value: any) => any;
33
- handleTrendlineChange: (value: any) => any;
34
- handleStackedChange: (value: any) => any;
35
- renderName(): React.DetailedReactHTMLElement<{
36
- type: string;
37
- className: string;
38
- value: any;
39
- onChange: (ev: any) => any;
40
- placeholder: string;
41
- }, HTMLElement>;
42
- renderRemove(): React.DetailedReactHTMLElement<{
43
- className: string;
44
- type: string;
45
- onClick: any;
46
- }, HTMLElement> | null;
47
- renderTable(): React.CElement<uiComponents.SectionComponentProps, uiComponents.SectionComponent>;
16
+ isLayerPolar(layer: LayeredChartDesignLayer): boolean;
17
+ doesLayerNeedGrouping(layer: LayeredChartDesignLayer): boolean;
18
+ isXAxisRequired(layer: LayeredChartDesignLayer): boolean;
19
+ getAxisTypes(layer: LayeredChartDesignLayer, axisKey: any): LiteralType[] | undefined;
20
+ getAxisLabel(icon: any, label: any): React.JSX.Element;
21
+ getXAxisLabel(layer: LayeredChartDesignLayer): React.JSX.Element;
22
+ getYAxisLabel(layer: LayeredChartDesignLayer): React.JSX.Element;
23
+ getColorAxisLabel(layer: LayeredChartDesignLayer): React.JSX.Element;
24
+ updateLayer(changes: Partial<LayeredChartDesignLayer>): void;
25
+ updateAxes(changes: Partial<LayeredChartDesignLayer["axes"]>): void;
26
+ handleNameChange: (ev: any) => void;
27
+ handleLayerTypeChange: (type: LayeredChartDesignLayer["type"]) => void;
28
+ handleTableChange: (table: string) => void;
29
+ handleXAxisChange: (axis: Axis) => void;
30
+ handleXColorMapChange: (xColorMap: boolean) => void;
31
+ handleColorAxisChange: (axis: Axis) => void;
32
+ handleYAxisChange: (axis: Axis) => void;
33
+ handleFilterChange: (filter: Expr) => void;
34
+ handleColorChange: (color: string) => void;
35
+ handleCumulativeChange: (value: boolean) => void;
36
+ handleTrendlineChange: (value: boolean) => void;
37
+ handleStackedChange: (value: boolean) => void;
38
+ renderName(): React.JSX.Element;
39
+ renderRemove(): React.JSX.Element | undefined;
40
+ renderTable(): React.JSX.Element;
41
+ renderLayerTypeSelector(): React.JSX.Element | null;
48
42
  handlexAxisOnlyValuesPresentChange: (xAxisOnlyValuesPresent: boolean) => void;
49
- renderXAxis(): (React.JSX.Element | null)[] | undefined;
50
- renderColorAxis(): React.DetailedReactHTMLElement<{
51
- className: string;
52
- }, HTMLElement> | undefined;
53
- renderYAxis(): React.DetailedReactHTMLElement<{
54
- className: string;
55
- }, HTMLElement> | undefined;
56
- renderCumulative(): React.DetailedReactHTMLElement<React.HTMLAttributes<HTMLElement>, HTMLElement> | undefined;
57
- renderTrendline(): React.DetailedReactHTMLElement<React.HTMLAttributes<HTMLElement>, HTMLElement> | undefined;
58
- renderStacked(): React.DetailedReactHTMLElement<React.HTMLAttributes<HTMLElement>, HTMLElement> | null;
59
- renderColor(): React.DetailedReactHTMLElement<{
60
- className: string;
61
- }, HTMLElement> | undefined;
62
- renderFilter(): React.DetailedReactHTMLElement<{
63
- className: string;
64
- }, HTMLElement> | null;
65
- render(): React.DetailedReactHTMLElement<React.HTMLAttributes<HTMLElement>, HTMLElement>;
43
+ renderXAxis(): React.ReactNode[] | undefined;
44
+ renderColorAxis(): React.JSX.Element | undefined;
45
+ renderYAxis(): React.JSX.Element | undefined;
46
+ renderCumulative(): React.JSX.Element | undefined;
47
+ renderTrendline(): React.JSX.Element | undefined;
48
+ renderStacked(): React.JSX.Element | undefined;
49
+ renderColor(): React.JSX.Element | undefined;
50
+ renderFilter(): React.JSX.Element | undefined;
51
+ render(): React.JSX.Element;
66
52
  }