@mwater/visualization 5.6.0 → 5.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/lib/ColorComponent.js +2 -2
  2. package/lib/TranslationsTabComponent.d.ts +34 -0
  3. package/lib/TranslationsTabComponent.js +256 -0
  4. package/lib/dashboards/DashboardComponent.js +1 -1
  5. package/lib/dashboards/ServerDashboardDataSource.d.ts +0 -1
  6. package/lib/dashboards/ServerDashboardDataSource.js +0 -15
  7. package/lib/dashboards/SettingsModalComponent.js +9 -233
  8. package/lib/datagrids/DatagridComponent.js +5 -0
  9. package/lib/datagrids/DatagridViewComponent.js +30 -4
  10. package/lib/maps/BufferLayer.d.ts +0 -13
  11. package/lib/maps/BufferLayer.js +12 -237
  12. package/lib/maps/BufferLayerDesignerComponent.d.ts +1 -1
  13. package/lib/maps/BufferLayerDesignerComponent.js +0 -5
  14. package/lib/maps/ChoroplethLayer.d.ts +1 -16
  15. package/lib/maps/ChoroplethLayer.js +13 -358
  16. package/lib/maps/ClusterLayer.d.ts +0 -9
  17. package/lib/maps/ClusterLayer.js +0 -250
  18. package/lib/maps/DirectMapDataSource.js +1 -38
  19. package/lib/maps/GridLayer.d.ts +0 -15
  20. package/lib/maps/GridLayer.js +0 -212
  21. package/lib/maps/Layer.d.ts +1 -26
  22. package/lib/maps/Layer.js +0 -13
  23. package/lib/maps/MapComponent.d.ts +19 -35
  24. package/lib/maps/MapComponent.js +135 -76
  25. package/lib/maps/MapControlComponent.d.ts +4 -5
  26. package/lib/maps/MapControlComponent.js +5 -12
  27. package/lib/maps/MapDesign.d.ts +8 -0
  28. package/lib/maps/MapDesignerComponent.d.ts +2 -0
  29. package/lib/maps/MapDesignerComponent.js +7 -2
  30. package/lib/maps/MapLayerDataSource.d.ts +0 -4
  31. package/lib/maps/MapLayerViewDesignerComponent.d.ts +3 -1
  32. package/lib/maps/MapLayerViewDesignerComponent.js +5 -1
  33. package/lib/maps/MapLayersDesignerComponent.d.ts +2 -0
  34. package/lib/maps/MapLayersDesignerComponent.js +2 -1
  35. package/lib/maps/MapTranslationsTab.d.ts +15 -0
  36. package/lib/maps/MapTranslationsTab.js +47 -0
  37. package/lib/maps/MapUtils.d.ts +11 -0
  38. package/lib/maps/MapUtils.js +47 -0
  39. package/lib/maps/MapViewComponent.d.ts +1 -1
  40. package/lib/maps/MapViewComponent.js +1 -8
  41. package/lib/maps/MarkersLayer.d.ts +1 -14
  42. package/lib/maps/MarkersLayer.js +71 -252
  43. package/lib/maps/MarkersLayerDesign.d.ts +4 -0
  44. package/lib/maps/MarkersLayerDesignerComponent.d.ts +20 -16
  45. package/lib/maps/MarkersLayerDesignerComponent.js +77 -23
  46. package/lib/maps/ServerMapDataSource.d.ts +0 -1
  47. package/lib/maps/ServerMapDataSource.js +0 -15
  48. package/lib/maps/SwitchableTileUrlLayer.d.ts +0 -2
  49. package/lib/maps/SwitchableTileUrlLayer.js +0 -9
  50. package/lib/maps/TileUrlLayer.d.ts +0 -1
  51. package/lib/maps/TileUrlLayer.js +0 -5
  52. package/lib/maps/VectorMapViewComponent.js +12 -1
  53. package/lib/maps/vectorMaps.d.ts +5 -6
  54. package/lib/maps/vectorMaps.js +13 -9
  55. package/lib/widgets/MapWidget.js +2 -1
  56. package/package.json +2 -2
  57. package/src/ColorComponent.tsx +2 -2
  58. package/src/TranslationsTabComponent.tsx +429 -0
  59. package/src/dashboards/DashboardComponent.tsx +1 -1
  60. package/src/dashboards/ServerDashboardDataSource.ts +0 -19
  61. package/src/dashboards/SettingsModalComponent.tsx +27 -383
  62. package/src/datagrids/DatagridComponent.tsx +6 -0
  63. package/src/datagrids/DatagridViewComponent.tsx +41 -5
  64. package/src/maps/BufferLayer.ts +16 -262
  65. package/src/maps/BufferLayerDesignerComponent.tsx +0 -6
  66. package/src/maps/ChoroplethLayer.ts +16 -393
  67. package/src/maps/ClusterLayer.ts +0 -274
  68. package/src/maps/DirectMapDataSource.ts +2 -49
  69. package/src/maps/GridLayer.ts +0 -224
  70. package/src/maps/Layer.ts +1 -35
  71. package/src/maps/MapComponent.tsx +448 -0
  72. package/src/maps/MapControlComponent.tsx +41 -0
  73. package/src/maps/MapDesign.ts +6 -0
  74. package/src/maps/MapDesignerComponent.tsx +18 -1
  75. package/src/maps/MapLayerDataSource.ts +0 -5
  76. package/src/maps/MapLayerViewDesignerComponent.ts +9 -2
  77. package/src/maps/MapLayersDesignerComponent.ts +4 -1
  78. package/src/maps/MapTranslationsTab.tsx +53 -0
  79. package/src/maps/MapUtils.ts +48 -0
  80. package/src/maps/MapViewComponent.tsx +2 -8
  81. package/src/maps/MarkersLayer.ts +79 -270
  82. package/src/maps/MarkersLayerDesign.ts +6 -0
  83. package/src/maps/MarkersLayerDesignerComponent.tsx +114 -38
  84. package/src/maps/ServerMapDataSource.ts +0 -19
  85. package/src/maps/SwitchableTileUrlLayer.tsx +0 -11
  86. package/src/maps/TileUrlLayer.tsx +0 -6
  87. package/src/maps/VectorMapViewComponent.tsx +13 -2
  88. package/src/maps/vectorMaps.tsx +12 -9
  89. package/src/widgets/MapWidget.tsx +2 -0
  90. package/src/maps/MapComponent.ts +0 -311
  91. package/src/maps/MapControlComponent.ts +0 -46
  92. package/src/maps/RasterMapViewComponent.ts +0 -345
@@ -5,28 +5,37 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const lodash_1 = __importDefault(require("lodash"));
7
7
  const react_1 = __importDefault(require("react"));
8
- const R = react_1.default.createElement;
9
8
  const MapViewComponent_1 = require("./MapViewComponent");
10
9
  const MapDesignerComponent_1 = __importDefault(require("./MapDesignerComponent"));
11
10
  const MapControlComponent_1 = __importDefault(require("./MapControlComponent"));
12
11
  const AutoSizeComponent_1 = __importDefault(require("@mwater/react-library/lib/AutoSizeComponent"));
13
12
  const UndoStack_1 = __importDefault(require("../UndoStack"));
14
13
  const PopoverHelpComponent_1 = __importDefault(require("@mwater/react-library/lib/PopoverHelpComponent"));
14
+ const __1 = require("..");
15
15
  const QuickfilterCompiler_1 = __importDefault(require("../quickfilter/QuickfilterCompiler"));
16
16
  const QuickfiltersComponent_1 = __importDefault(require("../quickfilter/QuickfiltersComponent"));
17
17
  const MapUtils_1 = require("./MapUtils");
18
- const expressions_ui_1 = require("@mwater/expressions-ui");
19
18
  /** Map with designer on right */
20
19
  class MapComponent extends react_1.default.Component {
21
- static contextType = expressions_ui_1.LocaleContext;
22
20
  constructor(props) {
23
21
  super(props);
22
+ // Prefer the T.locale if available when loading the map
23
+ let initialLocale = props.design.locale || "en";
24
+ const otherLocales = props.design.otherLocales || [];
25
+ if (props.preferredLocale && otherLocales.includes(props.preferredLocale)) {
26
+ initialLocale = props.preferredLocale;
27
+ }
28
+ else if (otherLocales.includes(T.locale)) {
29
+ initialLocale = T.locale;
30
+ }
24
31
  this.state = {
25
32
  undoStack: new UndoStack_1.default().push(props.design),
26
33
  transientDesign: props.design, // Temporary design for read-only maps
27
34
  zoomLocked: true,
28
35
  quickfiltersValues: props.quickfiltersValues ?? null,
29
- hideQuickfilters: false
36
+ hideQuickfilters: false,
37
+ locale: initialLocale,
38
+ previewLocale: null
30
39
  };
31
40
  }
32
41
  componentDidUpdate(prevProps) {
@@ -54,39 +63,93 @@ class MapComponent extends react_1.default.Component {
54
63
  handleZoomLockClick = () => {
55
64
  return this.setState({ zoomLocked: !this.state.zoomLocked });
56
65
  };
66
+ /** Handle locale selection from dropdown */
67
+ handleLocaleSelect = (locale) => {
68
+ const baseLocale = this.props.design.locale || "en";
69
+ const isEditing = this.props.onDesignChange != null;
70
+ if (isEditing) {
71
+ // In edit mode, selecting base language exits preview mode
72
+ // Selecting other language enters preview mode
73
+ if (locale === baseLocale) {
74
+ this.setState({ previewLocale: null });
75
+ }
76
+ else {
77
+ this.setState({ previewLocale: locale });
78
+ }
79
+ }
80
+ else {
81
+ // In readonly mode, just change the locale
82
+ this.setState({ locale: locale });
83
+ }
84
+ };
85
+ /** Render the language selector dropdown */
86
+ renderLanguageDropdown() {
87
+ const baseLocale = this.props.design.locale || "en";
88
+ const otherLocales = this.props.design.otherLocales || [];
89
+ const isEditing = this.props.onDesignChange != null;
90
+ const isPreviewMode = isEditing && this.state.previewLocale != null;
91
+ // Determine which locale to display in the dropdown button
92
+ let displayedLocale;
93
+ if (isEditing) {
94
+ displayedLocale = this.state.previewLocale || baseLocale;
95
+ }
96
+ else {
97
+ displayedLocale = this.state.locale;
98
+ }
99
+ return (react_1.default.createElement("div", { key: "translations", className: "dropdown d-inline-block" },
100
+ react_1.default.createElement("a", { className: `btn btn-link btn-sm dropdown-toggle ${isPreviewMode ? "text-info" : ""}`, "data-bs-toggle": "dropdown" },
101
+ react_1.default.createElement("span", { className: "fal fa-globe" }),
102
+ " ",
103
+ __1.languages.find(l => l.code === displayedLocale)?.name || displayedLocale,
104
+ isPreviewMode && react_1.default.createElement("span", { className: "badge bg-info ms-1" }, T `Preview`)),
105
+ react_1.default.createElement("ul", { className: "dropdown-menu dropdown-menu-end" },
106
+ react_1.default.createElement("li", { key: baseLocale },
107
+ react_1.default.createElement("a", { className: `dropdown-item ${displayedLocale === baseLocale ? "active" : ""}`, onClick: () => this.handleLocaleSelect(baseLocale) }, __1.languages.find(l => l.code === baseLocale)?.name || baseLocale)),
108
+ isEditing && otherLocales.length > 0 && (react_1.default.createElement(react_1.default.Fragment, null,
109
+ react_1.default.createElement("li", null,
110
+ react_1.default.createElement("hr", { className: "dropdown-divider" })),
111
+ react_1.default.createElement("li", null,
112
+ react_1.default.createElement("span", { className: "dropdown-header" }, T `Preview translations:`)))),
113
+ otherLocales.filter(loc => loc !== baseLocale).map(locale => react_1.default.createElement("li", { key: locale },
114
+ react_1.default.createElement("a", { className: `dropdown-item ${displayedLocale === locale ? "active" : ""}`, onClick: () => this.handleLocaleSelect(locale) }, __1.languages.find(l => l.code === locale)?.name || locale))))));
115
+ }
57
116
  renderActionLinks() {
58
- return R("div", null, this.props.onDesignChange != null
59
- ? [
60
- R("a", { key: "lock", className: "btn btn-link btn-sm", onClick: this.handleZoomLockClick }, R("span", {
61
- className: `fa ${this.state.zoomLocked ? "fa-lock red" : "fa-unlock green"}`,
62
- style: { marginRight: 5 }
63
- }), R(PopoverHelpComponent_1.default, { placement: "bottom" }, T `Changes to zoom level wont be saved in locked mode`)),
64
- R("a", {
65
- key: "undo",
66
- className: `btn btn-link btn-sm ${!this.state.undoStack.canUndo() ? "disabled" : ""}`,
67
- onClick: this.handleUndo
68
- }, R("span", { className: "fas fa-caret-left" }), R("span", { className: "hide-600px" }, " ", T `Undo`)),
117
+ return (react_1.default.createElement("div", null,
118
+ this.props.onDesignChange != null && [
119
+ react_1.default.createElement("a", { key: "lock", className: "btn btn-link btn-sm", onClick: this.handleZoomLockClick },
120
+ react_1.default.createElement("span", { className: `fa ${this.state.zoomLocked ? "fa-lock red" : "fa-unlock green"}`, style: { marginRight: 5 } }),
121
+ react_1.default.createElement(PopoverHelpComponent_1.default, { placement: "bottom" }, T `Changes to zoom level wont be saved in locked mode`)),
122
+ react_1.default.createElement("a", { key: "undo", className: `btn btn-link btn-sm ${!this.state.undoStack.canUndo() ? "disabled" : ""}`, onClick: this.handleUndo },
123
+ react_1.default.createElement("span", { className: "fas fa-caret-left" }),
124
+ react_1.default.createElement("span", { className: "hide-600px" },
125
+ " ",
126
+ T `Undo`)),
69
127
  " ",
70
- R("a", {
71
- key: "redo",
72
- className: `btn btn-link btn-sm ${!this.state.undoStack.canRedo() ? "disabled" : ""}`,
73
- onClick: this.handleRedo
74
- }, R("span", { className: "fas fa-caret-right" }), R("span", { className: "hide-600px" }, " ", T `Redo`))
75
- ]
76
- : undefined, this.state.hideQuickfilters && this.props.design.quickfilters && this.props.design.quickfilters.length > 0
77
- ? R("a", { key: "showQuickfilters", className: "btn btn-link btn-sm", onClick: this.handleShowQuickfilters }, R("span", { className: "fa fa-filter" }), R("span", { className: "hide-600px" }, " ", T `Show Quickfilters`))
78
- : undefined, this.props.extraTitleButtonsElem, R("a", { key: "toggleDesign", className: "btn btn-link btn-sm", onClick: this.handleToggleDesignPanel, alt: T `Toggle design panel` }, this.getDesign().hideDesignPanel ?
79
- R("span", { className: "fas fa-angle-double-left" })
80
- : R("span", { className: "fas fa-angle-double-right" })));
128
+ react_1.default.createElement("a", { key: "redo", className: `btn btn-link btn-sm ${!this.state.undoStack.canRedo() ? "disabled" : ""}`, onClick: this.handleRedo },
129
+ react_1.default.createElement("span", { className: "fas fa-caret-right" }),
130
+ react_1.default.createElement("span", { className: "hide-600px" },
131
+ " ",
132
+ T `Redo`))
133
+ ],
134
+ this.props.design.otherLocales && this.props.design.otherLocales.length > 0
135
+ ? this.renderLanguageDropdown()
136
+ : undefined,
137
+ this.state.hideQuickfilters && this.props.design.quickfilters && this.props.design.quickfilters.length > 0 && (react_1.default.createElement("a", { key: "showQuickfilters", className: "btn btn-link btn-sm", onClick: this.handleShowQuickfilters },
138
+ react_1.default.createElement("span", { className: "fa fa-filter" }),
139
+ react_1.default.createElement("span", { className: "hide-600px" },
140
+ " ",
141
+ T `Show Quickfilters`))),
142
+ this.props.extraTitleButtonsElem,
143
+ react_1.default.createElement("a", { key: "toggleDesign", className: "btn btn-link btn-sm", onClick: this.handleToggleDesignPanel, title: T `Toggle design panel` }, this.getDesign().hideDesignPanel ? (react_1.default.createElement("span", { className: "fas fa-angle-double-left" })) : (react_1.default.createElement("span", { className: "fas fa-angle-double-right" })))));
81
144
  }
82
145
  renderHeader() {
83
- return R("div", {
84
- style: {
146
+ return (react_1.default.createElement("div", { style: {
85
147
  padding: 4,
86
148
  borderBottom: "solid 1px #e8e8e8",
87
149
  gridArea: "header"
88
- }
89
- }, R("div", { style: { float: "right" } }, this.renderActionLinks()), this.props.titleElem);
150
+ } },
151
+ react_1.default.createElement("div", { style: { float: "right" } }, this.renderActionLinks()),
152
+ this.props.titleElem));
90
153
  }
91
154
  handleDesignChange = (design) => {
92
155
  if (this.props.onDesignChange) {
@@ -115,21 +178,19 @@ class MapComponent extends react_1.default.Component {
115
178
  let filters = this.props.extraFilters || [];
116
179
  // Compile quickfilters
117
180
  filters = filters.concat(new QuickfilterCompiler_1.default(this.props.schema).compile(this.props.design.quickfilters || [], this.state.quickfiltersValues, this.props.quickfilterLocks));
118
- return react_1.default.createElement(AutoSizeComponent_1.default, { injectWidth: true, injectHeight: true }, (size) => {
119
- return react_1.default.createElement(MapViewComponent_1.MapViewComponent, {
120
- mapDataSource: this.props.mapDataSource,
121
- schema: this.props.schema,
122
- dataSource: this.props.dataSource,
123
- design: this.getDesign(),
124
- onDesignChange: this.handleDesignChange,
125
- zoomLocked: this.state.zoomLocked,
126
- onRowClick: this.props.onRowClick,
127
- extraFilters: filters,
128
- locale: this.context,
129
- width: size.width,
130
- height: size.height
131
- });
132
- });
181
+ const isPreviewMode = this.props.onDesignChange != null && this.state.previewLocale != null;
182
+ // Determine display locale:
183
+ // - In preview mode: use previewLocale
184
+ // - In edit mode (no preview): use design locale
185
+ // - In readonly mode: use state locale
186
+ const displayLocale = isPreviewMode
187
+ ? this.state.previewLocale
188
+ : (this.props.onDesignChange != null
189
+ ? this.props.design.locale || "en"
190
+ : this.state.locale);
191
+ return (react_1.default.createElement(AutoSizeComponent_1.default, { injectWidth: true, injectHeight: true }, (size) => {
192
+ return (react_1.default.createElement(MapViewComponent_1.MapViewComponent, { mapDataSource: this.props.mapDataSource, schema: this.props.schema, dataSource: this.props.dataSource, design: this.getDesign(), onDesignChange: isPreviewMode ? undefined : this.handleDesignChange, zoomLocked: this.state.zoomLocked, onRowClick: this.props.onRowClick, extraFilters: filters, locale: displayLocale, width: size.width, height: size.height }));
193
+ }));
133
194
  }
134
195
  // Get filters from props filters combined with maps filters
135
196
  getCompiledFilters() {
@@ -138,48 +199,46 @@ class MapComponent extends react_1.default.Component {
138
199
  return compiledFilters;
139
200
  }
140
201
  renderQuickfilter() {
141
- return R("div", { style: { gridArea: "quickfilters", borderBottom: "solid 1px #e8e8e8" } }, R(QuickfiltersComponent_1.default, {
142
- design: this.props.design.quickfilters || [],
143
- schema: this.props.schema,
144
- dataSource: this.props.dataSource,
145
- quickfiltersDataSource: this.props.mapDataSource.getQuickfiltersDataSource(),
146
- values: this.state.quickfiltersValues || undefined,
147
- onValuesChange: (values) => this.setState({ quickfiltersValues: values }),
148
- locks: this.props.quickfilterLocks,
149
- filters: this.getCompiledFilters(),
150
- hideTopBorder: this.props.hideTitleBar,
151
- onHide: () => this.setState({ hideQuickfilters: true })
152
- }));
202
+ return (react_1.default.createElement("div", { style: { gridArea: "quickfilters", borderBottom: "solid 1px #e8e8e8" } },
203
+ react_1.default.createElement(QuickfiltersComponent_1.default, { design: this.props.design.quickfilters || [], schema: this.props.schema, dataSource: this.props.dataSource, quickfiltersDataSource: this.props.mapDataSource.getQuickfiltersDataSource(), values: this.state.quickfiltersValues || undefined, onValuesChange: (values) => this.setState({ quickfiltersValues: values }), locks: this.props.quickfilterLocks, filters: this.getCompiledFilters(), hideTopBorder: this.props.hideTitleBar, onHide: () => this.setState({ hideQuickfilters: true }) })));
153
204
  }
205
+ /** Translate function to use for display based on current locale */
206
+ translate = (input) => {
207
+ const design = this.getDesign();
208
+ const designLocale = design.locale || "en";
209
+ const isPreviewMode = this.props.onDesignChange != null && this.state.previewLocale != null;
210
+ // Determine display locale:
211
+ // - In preview mode: use previewLocale
212
+ // - In edit mode (no preview): use design locale
213
+ // - In readonly mode: use state locale
214
+ const displayLocale = isPreviewMode
215
+ ? this.state.previewLocale
216
+ : (this.props.onDesignChange != null
217
+ ? designLocale
218
+ : this.state.locale);
219
+ if (designLocale === displayLocale) {
220
+ return input;
221
+ }
222
+ return design.translations?.[displayLocale]?.[input] ?? input;
223
+ };
154
224
  renderDesigner() {
155
- return R("div", { style: { gridArea: "designer", borderLeft: "solid 2px #e8e8e8", overflowY: 'scroll' } }, this.props.onDesignChange ?
156
- react_1.default.createElement(MapDesignerComponent_1.default, {
157
- schema: this.props.schema,
158
- dataSource: this.props.dataSource,
159
- design: this.getDesign(),
160
- onDesignChange: this.handleDesignChange,
161
- enableQuickfilters: this.props.enableQuickfilters
162
- })
163
- :
164
- react_1.default.createElement(MapControlComponent_1.default, {
165
- schema: this.props.schema,
166
- dataSource: this.props.dataSource,
167
- design: this.getDesign(),
168
- onDesignChange: this.handleDesignChange
169
- }));
225
+ const isPreviewMode = this.props.onDesignChange != null && this.state.previewLocale != null;
226
+ return (react_1.default.createElement("div", { style: { gridArea: "designer", borderLeft: "solid 2px #e8e8e8", overflowY: 'scroll' } }, this.props.onDesignChange && !isPreviewMode ? (react_1.default.createElement(MapDesignerComponent_1.default, { schema: this.props.schema, dataSource: this.props.dataSource, design: this.getDesign(), onDesignChange: this.handleDesignChange, enableQuickfilters: this.props.enableQuickfilters })) : (react_1.default.createElement(MapControlComponent_1.default, { schema: this.props.schema, dataSource: this.props.dataSource, design: this.getDesign(), onDesignChange: this.handleDesignChange, translate: this.translate }))));
170
227
  }
171
228
  render() {
172
229
  const designerVisible = !this.getDesign().hideDesignPanel;
173
- return R("div", {
174
- style: {
230
+ return (react_1.default.createElement("div", { style: {
175
231
  width: "100%",
176
232
  height: "100%",
177
233
  display: "grid",
178
234
  gridTemplateColumns: designerVisible ? "70% 30%" : "100%",
179
235
  gridTemplateRows: "auto auto 1fr",
180
236
  gridTemplateAreas: `"header designer" "quickfilters designer" "view designer"`
181
- }
182
- }, this.props.hideTitleBar != true ? this.renderHeader() : null, this.state.hideQuickfilters ? null : this.renderQuickfilter(), R("div", { style: { width: "100%", height: "100%", gridArea: "view", overflow: "hidden" } }, this.renderView()), designerVisible ? this.renderDesigner() : null);
237
+ } },
238
+ this.props.hideTitleBar != true ? this.renderHeader() : null,
239
+ this.state.hideQuickfilters ? null : this.renderQuickfilter(),
240
+ react_1.default.createElement("div", { style: { width: "100%", height: "100%", gridArea: "view", overflow: "hidden" } }, this.renderView()),
241
+ designerVisible ? this.renderDesigner() : null));
183
242
  }
184
243
  }
185
244
  exports.default = MapComponent;
@@ -7,11 +7,10 @@ export interface MapControlComponentProps {
7
7
  /** See Map Design.md */
8
8
  design: any;
9
9
  onDesignChange: any;
10
+ /** Translate function to use for display */
11
+ translate?: (input: string) => string;
10
12
  }
13
+ /** Allows controlling readonly map */
11
14
  export default class MapControlComponent extends React.Component<MapControlComponentProps> {
12
- render(): React.DetailedReactHTMLElement<{
13
- style: {
14
- padding: number;
15
- };
16
- }, HTMLElement>;
15
+ render(): React.JSX.Element;
17
16
  }
@@ -4,22 +4,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const react_1 = __importDefault(require("react"));
7
- const R = react_1.default.createElement;
8
7
  const MapLayersDesignerComponent_1 = __importDefault(require("./MapLayersDesignerComponent"));
9
8
  const BaseLayerDesignerComponent_1 = __importDefault(require("./BaseLayerDesignerComponent"));
10
- // Allows controlling readonly map
9
+ /** Allows controlling readonly map */
11
10
  class MapControlComponent extends react_1.default.Component {
12
11
  render() {
13
- return R("div", { style: { padding: 5 } }, R(MapLayersDesignerComponent_1.default, {
14
- schema: this.props.schema,
15
- dataSource: this.props.dataSource,
16
- design: this.props.design,
17
- onDesignChange: this.props.onDesignChange,
18
- allowEditingLayers: false
19
- }), R("br"), R("div", { className: "mb-3" }, R("label", { className: "text-muted" }, T `Map Style`), R(BaseLayerDesignerComponent_1.default, {
20
- design: this.props.design,
21
- onDesignChange: this.props.onDesignChange
22
- })));
12
+ return (react_1.default.createElement("div", { style: { padding: 5 } },
13
+ react_1.default.createElement(MapLayersDesignerComponent_1.default, { schema: this.props.schema, dataSource: this.props.dataSource, design: this.props.design, onDesignChange: this.props.onDesignChange, allowEditingLayers: false, translate: this.props.translate }),
14
+ react_1.default.createElement("br", null),
15
+ react_1.default.createElement(BaseLayerDesignerComponent_1.default, { design: this.props.design, onDesignChange: this.props.onDesignChange })));
23
16
  }
24
17
  }
25
18
  exports.default = MapControlComponent;
@@ -35,6 +35,14 @@ export interface MapDesign {
35
35
  initialLegendDisplay?: "open" | "closed" | "closedIfSmall";
36
36
  /** optional locale (e.g. "fr") to use for display */
37
37
  locale?: string;
38
+ /** Other locales that the map is available in. */
39
+ otherLocales?: string[];
40
+ /** Translation map for map. Maps locale to translation map. Does not include default locale. */
41
+ translations?: {
42
+ [locale: string]: {
43
+ [key: string]: string;
44
+ };
45
+ };
38
46
  /** True if design panel is hidden */
39
47
  hideDesignPanel?: boolean;
40
48
  }
@@ -15,6 +15,8 @@ export interface MapDesignerComponentProps {
15
15
  filters?: JsonQLFilter[];
16
16
  /** True to enable quickfilters */
17
17
  enableQuickfilters?: boolean;
18
+ /** True to enable translations tab. Defaults to true. Set to false when map is embedded in a dashboard. */
19
+ enableTranslations?: boolean;
18
20
  }
19
21
  export default class MapDesignerComponent extends React.Component<MapDesignerComponentProps> {
20
22
  handleAttributionChange: (text: string | undefined) => void;
@@ -41,6 +41,7 @@ const ui = __importStar(require("@mwater/react-library/lib/bootstrap"));
41
41
  const QuickfiltersDesignComponent_1 = __importDefault(require("../quickfilter/QuickfiltersDesignComponent"));
42
42
  const immer_1 = __importDefault(require("immer"));
43
43
  const expressions_ui_1 = require("@mwater/expressions-ui");
44
+ const MapTranslationsTab_1 = require("./MapTranslationsTab");
44
45
  class MapDesignerComponent extends react_1.default.Component {
45
46
  handleAttributionChange = (text) => {
46
47
  const design = { ...this.props.design, attribution: text };
@@ -69,7 +70,7 @@ class MapDesignerComponent extends react_1.default.Component {
69
70
  return this.props.onDesignChange(design);
70
71
  };
71
72
  renderOptionsTab() {
72
- return (react_1.default.createElement("div", null,
73
+ return (react_1.default.createElement("div", { style: { marginBottom: 200 } },
73
74
  react_1.default.createElement(BaseLayerDesignerComponent_1.default, { design: this.props.design, onDesignChange: this.props.onDesignChange }),
74
75
  react_1.default.createElement(CheckboxComponent_1.default, { checked: this.props.design.autoBounds, onChange: this.handleAutoBoundsChange },
75
76
  react_1.default.createElement("span", { className: "text-muted" },
@@ -94,7 +95,11 @@ class MapDesignerComponent extends react_1.default.Component {
94
95
  react_1.default.createElement("a", { onClick: this.handleConvertToMarkersMap, className: "btn btn-link btn-sm" }, T `Convert to markers map`))) : undefined,
95
96
  react_1.default.createElement(AttributionComponent, { text: this.props.design.attribution, onTextChange: this.handleAttributionChange }),
96
97
  react_1.default.createElement("br", null),
97
- react_1.default.createElement(AdvancedOptionsComponent, { design: this.props.design, onDesignChange: this.props.onDesignChange })));
98
+ react_1.default.createElement(AdvancedOptionsComponent, { design: this.props.design, onDesignChange: this.props.onDesignChange }),
99
+ this.props.enableTranslations !== false && (react_1.default.createElement(react_1.default.Fragment, null,
100
+ react_1.default.createElement("hr", null),
101
+ react_1.default.createElement("h5", null, T `Translations`),
102
+ react_1.default.createElement(MapTranslationsTab_1.MapTranslationsTab, { schema: this.props.schema, design: this.props.design, onDesignChange: this.props.onDesignChange })))));
98
103
  }
99
104
  render() {
100
105
  const filterableTables = MapUtils.getFilterableTables(this.props.design, this.props.schema);
@@ -6,10 +6,6 @@ export interface MapLayerDataSource {
6
6
  * Called with (design, filters) where design is the design of the layer and filters are filters to apply. Returns URL
7
7
  * Only called on layers that are valid */
8
8
  getTileUrl(design: any, filters: JsonQLFilter[]): string | null;
9
- /** Get the url for the interactivity tiles with the specified filters applied
10
- * Called with (design, filters) where design is the design of the layer and filters are filters to apply. Returns URL
11
- * Only called on layers that are valid */
12
- getUtfGridUrl(design: any, filters: JsonQLFilter[]): string | null;
13
9
  /** Get the url for vector tile source with an expiry time. Only for layers of type "VectorTile"
14
10
  * @param createdAfter ISO 8601 timestamp requiring that tile source on server is created after specified datetime
15
11
  */
@@ -18,12 +18,14 @@ export interface MapLayerViewDesignerComponentProps {
18
18
  connectDropTarget?: any;
19
19
  allowEditingLayer: boolean;
20
20
  filters?: any;
21
+ /** Translate function to use for display */
22
+ translate?: (input: string) => string;
21
23
  }
22
24
  interface MapLayerViewDesignerComponentState {
23
25
  editing: any;
24
26
  }
25
27
  export default class MapLayerViewDesignerComponent extends React.Component<MapLayerViewDesignerComponentProps, MapLayerViewDesignerComponentState> {
26
- constructor(props: any);
28
+ constructor(props: MapLayerViewDesignerComponentProps);
27
29
  update(updates: any): any;
28
30
  handleVisibleClick: () => any;
29
31
  handleHideLegend: (hideLegend: any) => any;
@@ -97,7 +97,11 @@ class MapLayerViewDesignerComponent extends react_1.default.Component {
97
97
  }), R("div", null, R(PopoverHelpComponent_1.default, { placement: "top", key: "help" }, T `Layers in the same group can only be selected one at a time`)));
98
98
  }
99
99
  renderName() {
100
- return R("span", { className: "hover-display-parent", onClick: this.handleRename, style: { cursor: "pointer" } }, this.props.layerView.name, " ", R("span", { className: "hover-display-child fas fa-pencil-alt text-muted" }));
100
+ // Translate the name if a translate function is provided
101
+ const displayName = this.props.translate
102
+ ? this.props.translate(this.props.layerView.name)
103
+ : this.props.layerView.name;
104
+ return R("span", { className: "hover-display-parent", onClick: this.handleRename, style: { cursor: "pointer" } }, displayName, " ", R("span", { className: "hover-display-child fas fa-pencil-alt text-muted" }));
101
105
  }
102
106
  renderEditor() {
103
107
  const layer = LayerFactory_1.default.createLayer(this.props.layerView.type);
@@ -12,6 +12,8 @@ export interface MapLayersDesignerComponentProps {
12
12
  /** True to allow editing layers */
13
13
  allowEditingLayers: boolean;
14
14
  filters?: any;
15
+ /** Translate function to use for display */
16
+ translate?: (input: string) => string;
15
17
  }
16
18
  export default class MapLayersDesignerComponent extends React.Component<MapLayersDesignerComponentProps> {
17
19
  updateDesign(changes: any): void;
@@ -70,7 +70,8 @@ class MapLayersDesignerComponent extends react_1.default.Component {
70
70
  connectDragPreview,
71
71
  connectDropTarget,
72
72
  allowEditingLayer: this.props.allowEditingLayers,
73
- filters: lodash_1.default.compact(filters)
73
+ filters: lodash_1.default.compact(filters),
74
+ translate: this.props.translate
74
75
  }));
75
76
  };
76
77
  render() {
@@ -0,0 +1,15 @@
1
+ import React from "react";
2
+ import { Schema } from "@mwater/expressions";
3
+ import { MapDesign } from "./MapDesign";
4
+ export interface MapTranslationsTabProps {
5
+ /** Schema to use */
6
+ schema: Schema;
7
+ /** Map design */
8
+ design: MapDesign;
9
+ /** Called with new design */
10
+ onDesignChange: (design: MapDesign) => void;
11
+ }
12
+ /**
13
+ * Translations tab for map designer that wraps the reusable TranslationsTabComponent
14
+ */
15
+ export declare function MapTranslationsTab(props: MapTranslationsTabProps): React.JSX.Element;
@@ -0,0 +1,47 @@
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
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.MapTranslationsTab = MapTranslationsTab;
30
+ const react_1 = __importStar(require("react"));
31
+ const TranslationsTabComponent_1 = require("../TranslationsTabComponent");
32
+ const MapUtils = __importStar(require("./MapUtils"));
33
+ const immer_1 = __importDefault(require("immer"));
34
+ /**
35
+ * Translations tab for map designer that wraps the reusable TranslationsTabComponent
36
+ */
37
+ function MapTranslationsTab(props) {
38
+ const { design, onDesignChange, schema } = props;
39
+ const translatableStrings = (0, react_1.useMemo)(() => MapUtils.getTranslatableStrings(design, schema), [design, schema]);
40
+ return (react_1.default.createElement(TranslationsTabComponent_1.TranslationsTabComponent, { locale: design.locale || "en", otherLocales: design.otherLocales || [], translations: design.translations || {}, translatableStrings: translatableStrings, onLocaleChange: (locale) => onDesignChange((0, immer_1.default)(design, draft => {
41
+ draft.locale = locale;
42
+ })), onOtherLocalesChange: (otherLocales) => onDesignChange((0, immer_1.default)(design, draft => {
43
+ draft.otherLocales = otherLocales;
44
+ })), onTranslationsChange: (translations) => onDesignChange((0, immer_1.default)(design, draft => {
45
+ draft.translations = translations;
46
+ })), downloadFilename: "Map Translations.xlsx", baseLanguageDescription: T `This is the language that the map is written in.` }));
47
+ }
@@ -3,6 +3,7 @@ import { DataSource, Schema } from "@mwater/expressions";
3
3
  import { JsonQLFilter } from "../JsonQLFilter";
4
4
  import { MapDesign } from "./MapDesign";
5
5
  import { HoverOverItem } from "./maps";
6
+ import { Axis } from "../axes/Axis";
6
7
  export interface MapScope {
7
8
  name: string;
8
9
  filter: JsonQLFilter;
@@ -24,6 +25,16 @@ export declare function getCompiledFilters(design: MapDesign, schema: Schema, fi
24
25
  * Get a list of translatable strings in the map design
25
26
  */
26
27
  export declare function getTranslatableStrings(design: MapDesign, schema: Schema): string[];
28
+ /**
29
+ * Get translatable strings from an axis's categoryLabels and nullLabel.
30
+ * Always includes "None" since it's the default label for null values in AxisBuilder.
31
+ */
32
+ export declare function getTranslatableStringsFromAxis(axis: Axis | null | undefined): string[];
33
+ /**
34
+ * Translate an axis's category labels and null label.
35
+ * If no nullLabel is set, translates the default "None" and sets it so AxisBuilder uses it.
36
+ */
37
+ export declare function translateAxis(axis: Axis | null | undefined, translate: (input: string) => string): Axis | null | undefined;
27
38
  /**
28
39
  * Convenience function to get hover over data for a map given an id and a list of hover over items
29
40
  */
@@ -34,10 +34,13 @@ exports.convertToMarkersMap = convertToMarkersMap;
34
34
  exports.getFilterableTables = getFilterableTables;
35
35
  exports.getCompiledFilters = getCompiledFilters;
36
36
  exports.getTranslatableStrings = getTranslatableStrings;
37
+ exports.getTranslatableStringsFromAxis = getTranslatableStringsFromAxis;
38
+ exports.translateAxis = translateAxis;
37
39
  exports.getSimpleHoverOverData = getSimpleHoverOverData;
38
40
  const lodash_1 = __importStar(require("lodash"));
39
41
  const expressions_1 = require("@mwater/expressions");
40
42
  const LayerFactory_1 = __importDefault(require("./LayerFactory"));
43
+ const immer_1 = require("immer");
41
44
  // Check if can convert to a cluster map. Only maps containing marker views can be
42
45
  function canConvertToClusterMap(design) {
43
46
  return lodash_1.default.any(design.layerViews, (lv) => lv.type === "Markers");
@@ -159,6 +162,50 @@ function getTranslatableStrings(design, schema) {
159
162
  // Remove duplicates
160
163
  return lodash_1.default.uniq(strings);
161
164
  }
165
+ /**
166
+ * Get translatable strings from an axis's categoryLabels and nullLabel.
167
+ * Always includes "None" since it's the default label for null values in AxisBuilder.
168
+ */
169
+ function getTranslatableStringsFromAxis(axis) {
170
+ const strings = [];
171
+ if (axis?.categoryLabels) {
172
+ strings.push(...Object.values(axis.categoryLabels));
173
+ }
174
+ if (axis?.nullLabel) {
175
+ strings.push(axis.nullLabel);
176
+ }
177
+ else {
178
+ // Always include "None" as it's the default nullLabel in AxisBuilder
179
+ strings.push("None");
180
+ }
181
+ return strings;
182
+ }
183
+ /**
184
+ * Translate an axis's category labels and null label.
185
+ * If no nullLabel is set, translates the default "None" and sets it so AxisBuilder uses it.
186
+ */
187
+ function translateAxis(axis, translate) {
188
+ if (!axis)
189
+ return axis;
190
+ return (0, immer_1.produce)(axis, draft => {
191
+ if (draft.categoryLabels) {
192
+ for (const key in draft.categoryLabels) {
193
+ draft.categoryLabels[key] = translate(draft.categoryLabels[key]);
194
+ }
195
+ }
196
+ if (draft.nullLabel) {
197
+ draft.nullLabel = translate(draft.nullLabel);
198
+ }
199
+ else {
200
+ // Translate the default "None" and set it so AxisBuilder uses it
201
+ // instead of falling back to T`None` which uses the global locale
202
+ const translatedNone = translate("None");
203
+ if (translatedNone !== "None") {
204
+ draft.nullLabel = translatedNone;
205
+ }
206
+ }
207
+ });
208
+ }
162
209
  /**
163
210
  * Convenience function to get hover over data for a map given an id and a list of hover over items
164
211
  */
@@ -32,7 +32,7 @@ export interface MapViewComponentProps {
32
32
  zoomLocked?: boolean;
33
33
  /** Locale to use. Overrides map design locale */
34
34
  locale?: string;
35
- /** Translate function to use for display. TODO: implement this */
35
+ /** Translate function to use for display */
36
36
  translate?: (input: string) => string;
37
37
  /** Increment to force refresh */
38
38
  refreshTrigger?: number;
@@ -6,14 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.MapViewComponent = MapViewComponent;
7
7
  const react_1 = __importDefault(require("react"));
8
8
  const VectorMapViewComponent_1 = require("./VectorMapViewComponent");
9
- const RasterMapViewComponent_1 = __importDefault(require("./RasterMapViewComponent"));
10
- const vectorMaps_1 = require("./vectorMaps");
11
9
  /** Component that displays just the map */
12
10
  function MapViewComponent(props) {
13
- if ((0, vectorMaps_1.areVectorMapsEnabled)()) {
14
- return react_1.default.createElement(VectorMapViewComponent_1.VectorMapViewComponent, { ...props });
15
- }
16
- else {
17
- return react_1.default.createElement(RasterMapViewComponent_1.default, { ...props });
18
- }
11
+ return react_1.default.createElement(VectorMapViewComponent_1.VectorMapViewComponent, { ...props });
19
12
  }