@mwater/visualization 5.4.1 → 5.4.2

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 (269) hide show
  1. package/lib/ColorComponent.js +2 -1
  2. package/lib/IdSelection.d.ts +16 -0
  3. package/lib/IdSelection.js +59 -0
  4. package/lib/MWaterAddRelatedIndicatorComponent.js +2 -2
  5. package/lib/MWaterCompleteTableSelectComponent.d.ts +3 -8
  6. package/lib/MWaterCompleteTableSelectComponent.js +36 -42
  7. package/lib/MWaterLoaderComponent.d.ts +11 -10
  8. package/lib/MWaterLoaderComponent.js +1 -1
  9. package/lib/MWaterResponsesFilterComponent.js +1 -1
  10. package/lib/MWaterTableSelectComponent.d.ts +0 -1
  11. package/lib/MWaterTableSelectComponent.js +4 -6
  12. package/lib/autotranslate.d.ts +20 -0
  13. package/lib/autotranslate.js +122 -0
  14. package/lib/axes/AxisBuilder.js +3 -3
  15. package/lib/axes/AxisColorEditorComponent.js +4 -0
  16. package/lib/axes/AxisComponent.d.ts +8 -12
  17. package/lib/axes/AxisComponent.js +32 -80
  18. package/lib/axes/CategoryMapComponent.js +4 -4
  19. package/lib/axes/RangesComponent.js +2 -2
  20. package/lib/dashboards/DashboardComponent.d.ts +6 -0
  21. package/lib/dashboards/DashboardComponent.js +44 -12
  22. package/lib/dashboards/DashboardDesign.d.ts +11 -2
  23. package/lib/dashboards/DashboardUtils.d.ts +5 -0
  24. package/lib/dashboards/DashboardUtils.js +30 -0
  25. package/lib/dashboards/DashboardViewComponent.d.ts +2 -0
  26. package/lib/dashboards/DashboardViewComponent.js +16 -3
  27. package/lib/dashboards/ServerDashboardDataSource.js +2 -1
  28. package/lib/dashboards/SettingsModalComponent.d.ts +1 -1
  29. package/lib/dashboards/SettingsModalComponent.js +256 -19
  30. package/lib/dashboards/WidgetComponent.d.ts +6 -3
  31. package/lib/dashboards/WidgetComponent.js +3 -1
  32. package/lib/datagrids/CellEditor.d.ts +19 -0
  33. package/lib/datagrids/CellEditor.js +223 -0
  34. package/lib/datagrids/DatagridComponent.d.ts +18 -87
  35. package/lib/datagrids/DatagridComponent.js +304 -222
  36. package/lib/datagrids/DatagridViewComponent.d.ts +15 -53
  37. package/lib/datagrids/DatagridViewComponent.js +256 -257
  38. package/lib/datagrids/DirectDatagridDataSource.js +2 -3
  39. package/lib/datagrids/ExprCellComponent.d.ts +8 -15
  40. package/lib/datagrids/ExprCellComponent.js +11 -15
  41. package/lib/datagrids/FindReplaceModalComponent.d.ts +4 -6
  42. package/lib/datagrids/FindReplaceModalComponent.js +38 -75
  43. package/lib/index.css +1 -1
  44. package/lib/index.d.ts +0 -1
  45. package/lib/index.js +0 -1
  46. package/lib/layouts/blocks/HorizontalBlockComponent.js +2 -2
  47. package/lib/mWaterLoader.d.ts +1 -1
  48. package/lib/maps/BufferLayer.d.ts +7 -5
  49. package/lib/maps/BufferLayer.js +69 -48
  50. package/lib/maps/BufferLayerDesign.d.ts +21 -14
  51. package/lib/maps/BufferLayerDesignerComponent.d.ts +16 -31
  52. package/lib/maps/BufferLayerDesignerComponent.js +68 -102
  53. package/lib/maps/ChoroplethLayer.d.ts +5 -4
  54. package/lib/maps/ChoroplethLayer.js +32 -9
  55. package/lib/maps/ChoroplethLayerDesign.d.ts +6 -2
  56. package/lib/maps/ChoroplethLayerDesigner.js +4 -2
  57. package/lib/maps/ClusterLayer.d.ts +3 -4
  58. package/lib/maps/ClusterLayer.js +2 -1
  59. package/lib/maps/DetailLevelSelectComponent.js +1 -1
  60. package/lib/maps/DirectMapDataSource.js +2 -1
  61. package/lib/maps/EditPopupComponent.js +5 -3
  62. package/lib/maps/GridLayer.d.ts +3 -4
  63. package/lib/maps/GridLayer.js +2 -1
  64. package/lib/maps/GridLayerDesigner.js +5 -3
  65. package/lib/maps/HoverContent.d.ts +11 -3
  66. package/lib/maps/HoverContent.js +25 -9
  67. package/lib/maps/Layer.d.ts +24 -3
  68. package/lib/maps/Layer.js +5 -1
  69. package/lib/maps/LayerFactory.js +0 -8
  70. package/lib/maps/LayerLegendComponent.js +0 -1
  71. package/lib/maps/LayerSwitcherComponent.d.ts +1 -0
  72. package/lib/maps/LayerSwitcherComponent.js +1 -1
  73. package/lib/maps/LeafletMapComponent.js +3 -1
  74. package/lib/maps/LegendComponent.d.ts +1 -0
  75. package/lib/maps/LegendComponent.js +9 -1
  76. package/lib/maps/MWaterServerLayer.d.ts +2 -2
  77. package/lib/maps/MWaterServerLayer.js +2 -2
  78. package/lib/maps/MapComponent.js +3 -3
  79. package/lib/maps/MapDesign.d.ts +2 -0
  80. package/lib/maps/MapDesignerComponent.d.ts +4 -3
  81. package/lib/maps/MapDesignerComponent.js +68 -74
  82. package/lib/maps/MapLayerViewDesignerComponent.js +2 -2
  83. package/lib/maps/MapUtils.d.ts +4 -0
  84. package/lib/maps/MapUtils.js +19 -0
  85. package/lib/maps/MapViewComponent.d.ts +8 -3
  86. package/lib/maps/MarkersLayer.d.ts +5 -4
  87. package/lib/maps/MarkersLayer.js +33 -7
  88. package/lib/maps/MarkersLayerDesign.d.ts +19 -16
  89. package/lib/maps/PopupFilterJoinsUtils.d.ts +6 -3
  90. package/lib/maps/PopupFilterJoinsUtils.js +0 -6
  91. package/lib/maps/RasterMapViewComponent.d.ts +3 -31
  92. package/lib/maps/RasterMapViewComponent.js +7 -2
  93. package/lib/maps/ServerMapDataSource.js +2 -1
  94. package/lib/maps/SwitchableTileUrlLayer.d.ts +3 -3
  95. package/lib/maps/SwitchableTileUrlLayer.js +2 -1
  96. package/lib/maps/TileUrlLayer.d.ts +4 -5
  97. package/lib/maps/TileUrlLayer.js +2 -1
  98. package/lib/maps/VectorMapViewComponent.d.ts +5 -37
  99. package/lib/maps/VectorMapViewComponent.js +19 -8
  100. package/lib/maps/maps.d.ts +3 -0
  101. package/lib/quickfilter/QuickfiltersComponent.d.ts +2 -0
  102. package/lib/quickfilter/QuickfiltersComponent.js +9 -7
  103. package/lib/quickfilter/QuickfiltersDesignComponent.d.ts +1 -1
  104. package/lib/quickfilter/QuickfiltersDesignComponent.js +19 -35
  105. package/lib/richtext/ExprItemsHtmlConverter.d.ts +5 -2
  106. package/lib/richtext/ExprItemsHtmlConverter.js +4 -4
  107. package/lib/richtext/ExprItemsTranslator.d.ts +5 -0
  108. package/lib/richtext/ExprItemsTranslator.js +149 -0
  109. package/lib/richtext/ItemsHtmlConverter.d.ts +1 -1
  110. package/lib/richtext/ItemsHtmlConverter.js +31 -15
  111. package/lib/wellknown.js +12 -9
  112. package/lib/widgets/IFrameWidget.d.ts +4 -4
  113. package/lib/widgets/ImageWidget.d.ts +7 -4
  114. package/lib/widgets/ImageWidget.js +9 -1
  115. package/lib/widgets/ImageWidgetComponent.d.ts +1 -0
  116. package/lib/widgets/ImageWidgetComponent.js +1 -1
  117. package/lib/widgets/MapWidget.d.ts +5 -48
  118. package/lib/widgets/MapWidget.js +26 -63
  119. package/lib/widgets/MarkdownWidget.d.ts +3 -0
  120. package/lib/widgets/MarkdownWidget.js +3 -0
  121. package/lib/widgets/TOCWidget.d.ts +15 -27
  122. package/lib/widgets/TOCWidget.js +107 -183
  123. package/lib/widgets/Widget.d.ts +18 -7
  124. package/lib/widgets/Widget.js +4 -0
  125. package/lib/widgets/WidgetScopesViewComponent.js +1 -1
  126. package/lib/widgets/charts/Chart.d.ts +10 -1
  127. package/lib/widgets/charts/Chart.js +22 -11
  128. package/lib/widgets/charts/ChartViewComponent.d.ts +4 -0
  129. package/lib/widgets/charts/ChartViewComponent.js +6 -3
  130. package/lib/widgets/charts/ChartWidget.d.ts +2 -0
  131. package/lib/widgets/charts/ChartWidget.js +9 -1
  132. package/lib/widgets/charts/ChartWidgetComponent.d.ts +4 -0
  133. package/lib/widgets/charts/ChartWidgetComponent.js +2 -2
  134. package/lib/widgets/charts/calendar/CalendarChart.d.ts +1 -0
  135. package/lib/widgets/charts/calendar/CalendarChart.js +26 -0
  136. package/lib/widgets/charts/calendar/CalendarChartViewComponent.js +3 -1
  137. package/lib/widgets/charts/imagemosaic/ImageMosaicChart.d.ts +1 -0
  138. package/lib/widgets/charts/imagemosaic/ImageMosaicChart.js +8 -0
  139. package/lib/widgets/charts/layered/LayeredChart.d.ts +2 -0
  140. package/lib/widgets/charts/layered/LayeredChart.js +63 -3
  141. package/lib/widgets/charts/layered/LayeredChartCompiler.d.ts +1 -1
  142. package/lib/widgets/charts/layered/LayeredChartCompiler.js +1 -1
  143. package/lib/widgets/charts/layered/LayeredChartDesignerComponent.js +2 -2
  144. package/lib/widgets/charts/layered/LayeredChartViewComponent.js +8 -3
  145. package/lib/widgets/charts/pivot/PivotChart.d.ts +1 -0
  146. package/lib/widgets/charts/pivot/PivotChart.js +63 -0
  147. package/lib/widgets/charts/pivot/PivotChartLayoutComponent.js +1 -1
  148. package/lib/widgets/charts/pivot/SegmentDesignerComponent.js +7 -4
  149. package/lib/widgets/charts/table/OrderingsComponent.js +1 -1
  150. package/lib/widgets/charts/table/TableChart.d.ts +1 -0
  151. package/lib/widgets/charts/table/TableChart.js +15 -0
  152. package/lib/widgets/text/TextComponent.d.ts +11 -4
  153. package/lib/widgets/text/TextComponent.js +11 -8
  154. package/lib/widgets/text/TextWidget.d.ts +6 -3
  155. package/lib/widgets/text/TextWidget.js +7 -1
  156. package/lib/widgets/text/TextWidgetComponent.d.ts +4 -0
  157. package/lib/widgets/text/TextWidgetComponent.js +7 -1
  158. package/lib/widgets/text/TextWidgetDesign.d.ts +2 -4
  159. package/lib/widgets/text/TextWidgetDesign.js +1 -1
  160. package/package.json +7 -8
  161. package/src/ColorComponent.tsx +1 -2
  162. package/src/IdSelection.ts +62 -0
  163. package/src/MWaterAddRelatedIndicatorComponent.ts +3 -2
  164. package/src/MWaterCompleteTableSelectComponent.tsx +36 -46
  165. package/src/MWaterLoaderComponent.ts +28 -26
  166. package/src/MWaterResponsesFilterComponent.ts +5 -2
  167. package/src/MWaterTableSelectComponent.tsx +5 -9
  168. package/src/autotranslate.ts +141 -0
  169. package/src/axes/AxisBuilder.ts +3 -3
  170. package/src/axes/AxisColorEditorComponent.tsx +5 -0
  171. package/src/axes/{AxisComponent.ts → AxisComponent.tsx} +106 -106
  172. package/src/axes/CategoryMapComponent.ts +4 -4
  173. package/src/axes/RangesComponent.ts +3 -2
  174. package/src/dashboards/DashboardComponent.tsx +79 -14
  175. package/src/dashboards/DashboardDesign.ts +9 -2
  176. package/src/dashboards/DashboardUtils.ts +39 -0
  177. package/src/dashboards/DashboardViewComponent.tsx +22 -3
  178. package/src/dashboards/ServerDashboardDataSource.ts +2 -1
  179. package/src/dashboards/SettingsModalComponent.tsx +450 -35
  180. package/src/dashboards/WidgetComponent.tsx +12 -6
  181. package/src/datagrids/CellEditor.tsx +354 -0
  182. package/src/datagrids/DatagridComponent.tsx +646 -0
  183. package/src/datagrids/DatagridViewComponent.tsx +539 -0
  184. package/src/datagrids/DirectDatagridDataSource.ts +2 -3
  185. package/src/datagrids/{ExprCellComponent.ts → ExprCellComponent.tsx} +28 -23
  186. package/src/datagrids/{FindReplaceModalComponent.ts → FindReplaceModalComponent.tsx} +109 -122
  187. package/src/index.css +1 -1
  188. package/src/index.ts +0 -1
  189. package/src/layouts/blocks/HorizontalBlockComponent.ts +2 -2
  190. package/src/mWaterLoader.ts +1 -1
  191. package/src/maps/BufferLayer.ts +83 -60
  192. package/src/maps/BufferLayerDesign.ts +20 -14
  193. package/src/maps/BufferLayerDesignerComponent.tsx +309 -0
  194. package/src/maps/ChoroplethLayer.ts +40 -19
  195. package/src/maps/ChoroplethLayerDesign.ts +4 -2
  196. package/src/maps/ChoroplethLayerDesigner.tsx +4 -2
  197. package/src/maps/ClusterLayer.ts +4 -10
  198. package/src/maps/DetailLevelSelectComponent.ts +1 -1
  199. package/src/maps/DirectMapDataSource.ts +2 -1
  200. package/src/maps/EditPopupComponent.ts +7 -3
  201. package/src/maps/GridLayer.ts +4 -10
  202. package/src/maps/GridLayerDesigner.tsx +5 -3
  203. package/src/maps/HoverContent.tsx +40 -16
  204. package/src/maps/Layer.ts +28 -10
  205. package/src/maps/LayerFactory.ts +0 -8
  206. package/src/maps/LayerLegendComponent.ts +2 -4
  207. package/src/maps/LayerSwitcherComponent.tsx +6 -2
  208. package/src/maps/LeafletMapComponent.tsx +3 -1
  209. package/src/maps/LegendComponent.tsx +10 -1
  210. package/src/maps/MWaterServerLayer.ts +3 -3
  211. package/src/maps/MapComponent.ts +3 -3
  212. package/src/maps/MapDesign.ts +3 -0
  213. package/src/maps/MapDesignerComponent.tsx +165 -162
  214. package/src/maps/MapLayerViewDesignerComponent.ts +2 -2
  215. package/src/maps/MapUtils.ts +24 -0
  216. package/src/maps/MapViewComponent.tsx +11 -3
  217. package/src/maps/MarkersLayer.ts +44 -18
  218. package/src/maps/MarkersLayerDesign.ts +19 -16
  219. package/src/maps/PopupFilterJoinsUtils.ts +6 -2
  220. package/src/maps/RasterMapViewComponent.ts +9 -45
  221. package/src/maps/ServerMapDataSource.ts +2 -2
  222. package/src/maps/SwitchableTileUrlLayer.tsx +4 -10
  223. package/src/maps/TileUrlLayer.tsx +4 -10
  224. package/src/maps/VectorMapViewComponent.tsx +28 -55
  225. package/src/maps/maps.ts +3 -0
  226. package/src/quickfilter/QuickfiltersComponent.ts +13 -7
  227. package/src/quickfilter/QuickfiltersDesignComponent.tsx +56 -74
  228. package/src/richtext/ExprItemsHtmlConverter.ts +9 -5
  229. package/src/richtext/ExprItemsTranslator.ts +176 -0
  230. package/src/richtext/ItemsHtmlConverter.ts +33 -18
  231. package/src/wellknown.ts +33 -30
  232. package/src/widgets/ImageWidget.ts +10 -1
  233. package/src/widgets/ImageWidgetComponent.ts +3 -2
  234. package/src/widgets/{MapWidget.ts → MapWidget.tsx} +90 -101
  235. package/src/widgets/MarkdownWidget.ts +3 -0
  236. package/src/widgets/TOCWidget.tsx +281 -0
  237. package/src/widgets/Widget.ts +25 -5
  238. package/src/widgets/WidgetScopesViewComponent.ts +2 -1
  239. package/src/widgets/charts/Chart.ts +31 -12
  240. package/src/widgets/charts/ChartViewComponent.ts +13 -3
  241. package/src/widgets/charts/ChartWidget.ts +11 -1
  242. package/src/widgets/charts/ChartWidgetComponent.tsx +9 -1
  243. package/src/widgets/charts/calendar/CalendarChart.ts +29 -0
  244. package/src/widgets/charts/calendar/CalendarChartViewComponent.tsx +3 -1
  245. package/src/widgets/charts/imagemosaic/ImageMosaicChart.ts +9 -0
  246. package/src/widgets/charts/layered/LayeredChart.ts +71 -3
  247. package/src/widgets/charts/layered/LayeredChartCompiler.ts +2 -2
  248. package/src/widgets/charts/layered/LayeredChartDesignerComponent.tsx +4 -2
  249. package/src/widgets/charts/layered/LayeredChartViewComponent.ts +10 -4
  250. package/src/widgets/charts/pivot/PivotChart.ts +73 -0
  251. package/src/widgets/charts/pivot/PivotChartLayoutComponent.tsx +1 -1
  252. package/src/widgets/charts/pivot/SegmentDesignerComponent.tsx +6 -4
  253. package/src/widgets/charts/table/OrderingsComponent.tsx +2 -1
  254. package/src/widgets/charts/table/TableChart.ts +17 -0
  255. package/src/widgets/text/TextComponent.tsx +22 -12
  256. package/src/widgets/text/TextWidget.ts +9 -2
  257. package/src/widgets/text/TextWidgetComponent.tsx +16 -1
  258. package/src/widgets/text/TextWidgetDesign.ts +4 -7
  259. package/test/IdSelectionTests.ts +54 -0
  260. package/test/LayeredChartCompilerTests.ts +0 -2
  261. package/test/richtext/ExprItemsTranslatorTests.ts +144 -0
  262. package/test/wellknownTests.ts +144 -0
  263. package/src/datagrids/DatagridComponent.ts +0 -478
  264. package/src/datagrids/DatagridViewComponent.ts +0 -464
  265. package/src/datagrids/EditExprCellComponent.tsx +0 -305
  266. package/src/datagrids/README.md +0 -3
  267. package/src/maps/BufferLayerDesignerComponent.ts +0 -311
  268. package/src/widgets/TOCWidget.ts +0 -326
  269. package/test/LegoLayoutEngineTests.ts +0 -69
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.translateStrings = translateStrings;
4
+ exports.translateLocalizedStrings = translateLocalizedStrings;
5
+ exports.canAutoTranslate = canAutoTranslate;
6
+ /** Translates an array of strings from a source language to a target language using the mWater translation API.
7
+ * @param strings Array of strings to translate
8
+ * @param sourceLanguage Source language code (e.g. "en")
9
+ * @param targetLanguage Target language code to translate to
10
+ * @returns Promise that resolves to array of translated strings in same order as input
11
+ * @throws Error if translation fails
12
+ */
13
+ async function translateStrings(strings, sourceLanguage, targetLanguage) {
14
+ // Call translation API
15
+ const response = await fetch(`https://api.mwater.co/v3/translate?source_language=${sourceLanguage}&target_language=${targetLanguage}`, {
16
+ method: "POST",
17
+ headers: {
18
+ "Content-Type": "application/json"
19
+ },
20
+ body: JSON.stringify({
21
+ phrases: strings
22
+ })
23
+ });
24
+ if (!response.ok) {
25
+ throw new Error("Translation failed");
26
+ }
27
+ const result = await response.json();
28
+ return result.phrases;
29
+ }
30
+ /** Ensures that all LocalizedString objects have translations for all specified locales.
31
+ * Only translates missing translations, preserving existing ones and base strings.
32
+ * Each string's _base property is used as the source language for translations.
33
+ * @param strings Array of LocalizedString objects to ensure are fully translated
34
+ * @param locales Array of language codes specifying which languages to ensure exist
35
+ * @param baseLanguage Language code of the _base strings (e.g. "en", "fr", etc)
36
+ * @returns Promise that resolves to array of fully translated LocalizedString objects
37
+ */
38
+ async function translateLocalizedStrings(strings, locales, baseLanguage) {
39
+ // Create copy of strings to modify
40
+ const translatedStrings = strings.map(str => ({ ...str }));
41
+ // For each locale
42
+ for (const locale of locales) {
43
+ // Skip if it's the base language
44
+ if (locale === baseLanguage) {
45
+ continue;
46
+ }
47
+ // Group strings by their current source language
48
+ // Start with base language strings
49
+ const stringsBySourceLang = new Map();
50
+ translatedStrings.forEach((str, index) => {
51
+ // Skip if translation already exists
52
+ if (str[locale]) {
53
+ return;
54
+ }
55
+ // Add to list to translate from base language
56
+ if (!stringsBySourceLang.has(baseLanguage)) {
57
+ stringsBySourceLang.set(baseLanguage, []);
58
+ }
59
+ stringsBySourceLang.get(baseLanguage).push({ str, index });
60
+ });
61
+ // Translate each group
62
+ for (const [sourceLang, stringsToTranslate] of stringsBySourceLang.entries()) {
63
+ try {
64
+ // Get source strings
65
+ const sourceStrings = stringsToTranslate.map(item => item.str[item.str._base]);
66
+ // Translate all strings at once
67
+ const translations = await translateStrings(sourceStrings, sourceLang, locale);
68
+ // Add translations back to strings
69
+ translations.forEach((translation, i) => {
70
+ translatedStrings[stringsToTranslate[i].index][locale] = translation;
71
+ });
72
+ }
73
+ catch (error) {
74
+ console.error(`Failed to translate from ${sourceLang} to ${locale}:`, error);
75
+ throw error;
76
+ }
77
+ }
78
+ }
79
+ return translatedStrings;
80
+ }
81
+ /** Returns true if the given language code can be automatically translated. */
82
+ function canAutoTranslate(languageCode) {
83
+ const languageCodes = [
84
+ "en", // English
85
+ "fr", // French
86
+ "es", // Spanish
87
+ "pt", // Portuguese
88
+ "sw", // Swahili
89
+ "id", // Indonesian
90
+ "hi", // Hindi
91
+ "ne", // Nepali
92
+ "rw", // Kinyarwanda
93
+ "sd", // Sindhi
94
+ "tet", // Tetum
95
+ "ur", // Urdu
96
+ "ja", // Japanese
97
+ "de", // German
98
+ "it", // Italian
99
+ "su", // Sundanese
100
+ "ar", // Arabic
101
+ "vi", // Vietnamese
102
+ "mg", // Malagasy
103
+ "tl", // Tagalog/Filipino
104
+ "ht", // Haitian Creole
105
+ "fi", // Finnish
106
+ "bn", // Bangla
107
+ "my", // Burmese
108
+ "km", // Khmer
109
+ "so", // Somali
110
+ "jv", // Javanese
111
+ "ko", // Korean
112
+ "zh", // Chinese
113
+ "ru", // Russian
114
+ "tr", // Turkish
115
+ "nl", // Dutch
116
+ "pl", // Polish
117
+ "hu", // Hungarian
118
+ "cs", // Czech
119
+ "sk" // Slovak
120
+ ];
121
+ return languageCodes.includes(languageCode);
122
+ }
@@ -495,7 +495,7 @@ class AxisBuilder {
495
495
  }
496
496
  if (range.maxValue != null) {
497
497
  if (label) {
498
- label += T ` and `;
498
+ label += ` ${T `and`} `;
499
499
  }
500
500
  if (range.maxOpen) {
501
501
  label += `< ${range.maxValue}`;
@@ -920,7 +920,7 @@ class AxisBuilder {
920
920
  return this.formatCategory(axis, category);
921
921
  }
922
922
  else {
923
- return T `???`;
923
+ return `???`;
924
924
  }
925
925
  }).join(", ");
926
926
  }
@@ -930,7 +930,7 @@ class AxisBuilder {
930
930
  return this.formatCategory(axis, category);
931
931
  }
932
932
  else {
933
- return T `???`;
933
+ return `???`;
934
934
  }
935
935
  }
936
936
  }
@@ -48,6 +48,10 @@ class AxisColorEditorComponent extends react_1.default.Component {
48
48
  }
49
49
  const missingColorMap = ColorSchemeFactory_1.default.createColorMapForCategories(missingCategories, axisBuilder.isCategorical(this.props.axis));
50
50
  colorMap = (this.props.axis.colorMap || []).concat(missingColorMap);
51
+ // If there are categories present, remove any color map values that are not in the categories
52
+ if (this.props.categories.length > 0) {
53
+ colorMap = lodash_1.default.filter(colorMap, (cm) => this.props.categories.find((c) => c.value === cm.value));
54
+ }
51
55
  }
52
56
  else {
53
57
  // Keep existing
@@ -1,10 +1,8 @@
1
1
  import React from "react";
2
2
  import AsyncLoadComponent from "@mwater/react-library/lib/AsyncLoadComponent";
3
3
  import { DataSource, LiteralType, Schema } from "@mwater/expressions";
4
- import * as ui from "../UIComponents";
5
- import CategoryMapComponent from "./CategoryMapComponent";
6
4
  import { JsonQLFilter } from "../JsonQLFilter";
7
- import { Axis } from "./Axis";
5
+ import { Axis, AxisCategory } from "./Axis";
8
6
  export interface AxisComponentProps {
9
7
  schema: Schema;
10
8
  dataSource: DataSource;
@@ -34,7 +32,7 @@ export interface AxisComponentProps {
34
32
  collapseCategories?: boolean;
35
33
  }
36
34
  export default class AxisComponent extends AsyncLoadComponent<AxisComponentProps, {
37
- categories: any;
35
+ categories: AxisCategory[] | null;
38
36
  loading: boolean;
39
37
  }> {
40
38
  static defaultProps: {
@@ -45,17 +43,15 @@ export default class AxisComponent extends AsyncLoadComponent<AxisComponentProps
45
43
  static contextType: React.Context<string>;
46
44
  constructor(props: any);
47
45
  isLoadNeeded(newProps: any, oldProps: any): boolean;
48
- load(props: any, prevProps: any, callback: any): any;
46
+ load(props: any, prevProps: any, callback: any): void;
49
47
  handleExprChange: (expr: any) => void;
50
48
  handleFormatChange: (ev: any) => void;
51
49
  handleXformTypeChange: (type: any) => void;
52
50
  handleXformChange: (xform: any) => void;
53
51
  cleanAxis(axis: any): Axis | null;
54
- renderXform(axis: any): React.DetailedReactHTMLElement<React.HTMLAttributes<HTMLElement>, HTMLElement> | React.CElement<ui.RadioToggleComponentProps, ui.RadioToggleComponent> | null;
55
- renderColorMap(axis: any): React.DetailedReactHTMLElement<React.HTMLAttributes<HTMLElement>, HTMLElement>[] | null;
56
- renderExcludedValues(axis: any): (React.DetailedReactHTMLElement<React.HTMLAttributes<HTMLElement>, HTMLElement> | React.CElement<import("./CategoryMapComponent").CategoryMapComponentProps, CategoryMapComponent>)[] | null;
57
- renderFormat(axis: any): React.DetailedReactHTMLElement<{
58
- className: string;
59
- }, HTMLElement> | null;
60
- render(): React.DetailedReactHTMLElement<React.HTMLAttributes<HTMLElement>, HTMLElement>;
52
+ renderXform(axis: any): React.JSX.Element | null;
53
+ renderColorMap(axis: any): React.JSX.Element | null;
54
+ renderExcludedValues(axis: any): React.JSX.Element | null;
55
+ renderFormat(axis: any): React.JSX.Element | null;
56
+ render(): React.JSX.Element;
61
57
  }
@@ -28,7 +28,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  const lodash_1 = __importDefault(require("lodash"));
30
30
  const react_1 = __importDefault(require("react"));
31
- const R = react_1.default.createElement;
32
31
  const uuid_1 = __importDefault(require("uuid"));
33
32
  const AsyncLoadComponent_1 = __importDefault(require("@mwater/react-library/lib/AsyncLoadComponent"));
34
33
  const expressions_ui_1 = require("@mwater/expressions-ui");
@@ -118,13 +117,13 @@ class AxisComponent extends AsyncLoadComponent_1.default {
118
117
  else {
119
118
  valuesQuery.where = whereClauses[0];
120
119
  }
121
- return props.dataSource.performQuery(valuesQuery, (error, rows) => {
120
+ props.dataSource.performQuery(valuesQuery, (error, rows) => {
122
121
  if (error) {
123
122
  return; // Ignore errors
124
123
  }
125
124
  // Get categories (value + label)
126
125
  categories = axisBuilder.getCategories(axis, lodash_1.default.pluck(rows, "val"));
127
- return callback({ categories });
126
+ callback({ categories });
128
127
  });
129
128
  }
130
129
  handleExprChange = (expr) => {
@@ -204,42 +203,27 @@ class AxisComponent extends AsyncLoadComponent_1.default {
204
203
  if (axis.xform && ["bin", "ranges", "floor"].includes(axis.xform.type)) {
205
204
  let comp;
206
205
  if (axis.xform.type === "ranges") {
207
- comp = R(RangesComponent_1.default, {
208
- schema: this.props.schema,
209
- expr: axis.expr,
210
- xform: axis.xform,
211
- onChange: this.handleXformChange
212
- });
206
+ comp = react_1.default.createElement(RangesComponent_1.default, { schema: this.props.schema, expr: axis.expr, xform: axis.xform, onChange: this.handleXformChange });
213
207
  }
214
208
  else if (axis.xform.type === "bin") {
215
- comp = R(BinsComponent_1.default, {
216
- schema: this.props.schema,
217
- dataSource: this.props.dataSource,
218
- expr: axis.expr,
219
- xform: axis.xform,
220
- onChange: this.handleXformChange
221
- });
209
+ comp = react_1.default.createElement(BinsComponent_1.default, { schema: this.props.schema, dataSource: this.props.dataSource, expr: axis.expr, xform: axis.xform, onChange: this.handleXformChange });
222
210
  }
223
211
  else {
224
212
  comp = null;
225
213
  }
226
- return R("div", null, R(ui.RadioToggleComponent, {
227
- value: axis.xform ? axis.xform.type : null,
228
- options: [
229
- { value: "bin", label: T `Equal Bins` },
230
- { value: "ranges", label: T `Custom Ranges` },
231
- { value: "floor", label: T `Whole Numbers` }
232
- ],
233
- onChange: this.handleXformTypeChange
234
- }), comp);
214
+ return (react_1.default.createElement("div", null,
215
+ react_1.default.createElement(ui.RadioToggleComponent, { value: axis.xform ? axis.xform.type : null, options: [
216
+ { value: "bin", label: T `Equal Bins` },
217
+ { value: "ranges", label: T `Custom Ranges` },
218
+ { value: "floor", label: T `Whole Numbers` }
219
+ ], onChange: this.handleXformTypeChange }),
220
+ comp));
235
221
  }
236
222
  const exprUtils = new expressions_1.ExprUtils(this.props.schema);
237
223
  const exprType = exprUtils.getExprType(axis.expr);
238
224
  switch (exprType) {
239
225
  case "date":
240
- return R(ui.RadioToggleComponent, {
241
- value: axis.xform ? axis.xform.type : null,
242
- options: [
226
+ return react_1.default.createElement(ui.RadioToggleComponent, { value: axis.xform ? axis.xform.type : null, options: [
243
227
  { value: null, label: T `Exact Date` },
244
228
  { value: "year", label: T `Year` },
245
229
  { value: "yearmonth", label: T `Year/Month` },
@@ -247,13 +231,9 @@ class AxisComponent extends AsyncLoadComponent_1.default {
247
231
  { value: "week", label: T `Week` },
248
232
  { value: "yearweek", label: T `Year/Week` },
249
233
  { value: "yearquarter", label: T `Year/Quarter` }
250
- ],
251
- onChange: this.handleXformTypeChange
252
- });
234
+ ], onChange: this.handleXformTypeChange });
253
235
  case "datetime":
254
- return R(ui.RadioToggleComponent, {
255
- value: axis.xform ? axis.xform.type : null,
256
- options: [
236
+ return react_1.default.createElement(ui.RadioToggleComponent, { value: axis.xform ? axis.xform.type : null, options: [
257
237
  { value: "date", label: T `Date` },
258
238
  { value: "year", label: T `Year` },
259
239
  { value: "yearmonth", label: T `Year/Month` },
@@ -261,9 +241,7 @@ class AxisComponent extends AsyncLoadComponent_1.default {
261
241
  { value: "week", label: T `Week` },
262
242
  { value: "yearweek", label: T `Year/Week` },
263
243
  { value: "yearquarter", label: T `Year/Quarter` }
264
- ],
265
- onChange: this.handleXformTypeChange
266
- });
244
+ ], onChange: this.handleXformTypeChange });
267
245
  }
268
246
  return null;
269
247
  }
@@ -271,20 +249,9 @@ class AxisComponent extends AsyncLoadComponent_1.default {
271
249
  if (!this.props.showColorMap || !axis || !axis.expr) {
272
250
  return null;
273
251
  }
274
- return [
275
- R("br"),
276
- R(AxisColorEditorComponent_1.default, {
277
- schema: this.props.schema,
278
- axis,
279
- categories: this.state.categories,
280
- onChange: this.props.onChange,
281
- reorderable: this.props.reorderable,
282
- defaultColor: this.props.defaultColor,
283
- allowExcludedValues: this.props.allowExcludedValues,
284
- autosetColors: this.props.autosetColors,
285
- initiallyExpanded: this.props.collapseCategories !== true
286
- })
287
- ];
252
+ return (react_1.default.createElement(react_1.default.Fragment, null,
253
+ react_1.default.createElement("br", null),
254
+ react_1.default.createElement(AxisColorEditorComponent_1.default, { schema: this.props.schema, axis: axis, categories: this.state.categories ?? undefined, onChange: this.props.onChange, reorderable: this.props.reorderable, defaultColor: this.props.defaultColor ?? undefined, allowExcludedValues: this.props.allowExcludedValues, autosetColors: this.props.autosetColors, initiallyExpanded: this.props.collapseCategories !== true })));
288
255
  }
289
256
  renderExcludedValues(axis) {
290
257
  // Only if no color map and allows excluded values
@@ -295,19 +262,9 @@ class AxisComponent extends AsyncLoadComponent_1.default {
295
262
  if (!this.state.categories || this.state.categories.length < 1) {
296
263
  return null;
297
264
  }
298
- return [
299
- R("br"),
300
- R(CategoryMapComponent_1.default, {
301
- schema: this.props.schema,
302
- axis,
303
- onChange: this.props.onChange,
304
- categories: this.state.categories,
305
- reorderable: false,
306
- showColorMap: false,
307
- allowExcludedValues: true,
308
- initiallyExpanded: this.props.collapseCategories !== true
309
- })
310
- ];
265
+ return (react_1.default.createElement(react_1.default.Fragment, null,
266
+ react_1.default.createElement("br", null),
267
+ react_1.default.createElement(CategoryMapComponent_1.default, { schema: this.props.schema, axis: axis, onChange: this.props.onChange, categories: this.state.categories, reorderable: false, showColorMap: false, allowExcludedValues: true, initiallyExpanded: this.props.collapseCategories !== true })));
311
268
  }
312
269
  renderFormat(axis) {
313
270
  const axisBuilder = new AxisBuilder_1.default({ schema: this.props.schema });
@@ -319,12 +276,10 @@ class AxisComponent extends AsyncLoadComponent_1.default {
319
276
  if (!formats) {
320
277
  return null;
321
278
  }
322
- return R("div", { className: "mb-3" }, R("label", { className: "text-muted" }, T `Format`), ": ", R("select", {
323
- value: axis.format != null ? axis.format : (0, valueFormatter_2.getDefaultFormat)(valueType),
324
- className: "form-select",
325
- style: { width: "auto", display: "inline-block" },
326
- onChange: this.handleFormatChange
327
- }, lodash_1.default.map(formats, (format) => R("option", { key: format.value, value: format.value }, format.label))));
279
+ return (react_1.default.createElement("div", { className: "mb-3" },
280
+ react_1.default.createElement("label", { className: "text-muted" }, T `Format`),
281
+ ": ",
282
+ react_1.default.createElement("select", { value: axis.format != null ? axis.format : (0, valueFormatter_2.getDefaultFormat)(valueType), className: "form-select", style: { width: "auto", display: "inline-block" }, onChange: this.handleFormatChange }, lodash_1.default.map(formats, (format) => (react_1.default.createElement("option", { key: format.value, value: format.value }, format.label))))));
328
283
  }
329
284
  render() {
330
285
  let aggrStatuses;
@@ -343,16 +298,13 @@ class AxisComponent extends AsyncLoadComponent_1.default {
343
298
  aggrStatuses = ["literal", "aggregate"];
344
299
  break;
345
300
  }
346
- return R("div", null, R("div", null, R(expressions_ui_1.ExprComponent, {
347
- schema: this.props.schema,
348
- dataSource: this.props.dataSource,
349
- table: this.props.table,
350
- types: axisBuilder.getExprTypes(this.props.types),
351
- // preventRemove: @props.required
352
- onChange: this.handleExprChange,
353
- value: this.props.value ? this.props.value.expr : null,
354
- aggrStatuses
355
- })), this.renderXform(axis), this.props.showFormat ? this.renderFormat(axis) : undefined, this.renderColorMap(axis), this.renderExcludedValues(axis));
301
+ return (react_1.default.createElement("div", null,
302
+ react_1.default.createElement("div", null,
303
+ react_1.default.createElement(expressions_ui_1.ExprComponent, { schema: this.props.schema, dataSource: this.props.dataSource, table: this.props.table, types: axisBuilder.getExprTypes(this.props.types), onChange: this.handleExprChange, value: this.props.value ? this.props.value.expr : null, aggrStatuses: aggrStatuses })),
304
+ this.renderXform(axis),
305
+ this.props.showFormat ? this.renderFormat(axis) : undefined,
306
+ this.renderColorMap(axis),
307
+ this.renderExcludedValues(axis)));
356
308
  }
357
309
  }
358
310
  exports.default = AxisComponent;
@@ -23,7 +23,7 @@ class CategoryMapComponent extends react_1.default.Component {
23
23
  }
24
24
  handleReorder = (map) => {
25
25
  const order = lodash_1.default.pluck(map, "value");
26
- return this.props.onChange((0, update_object_1.default)(this.props.axis, { drawOrder: { $set: order } }));
26
+ this.props.onChange((0, update_object_1.default)(this.props.axis, { drawOrder: { $set: order } }));
27
27
  };
28
28
  handleColorChange = (value, color) => {
29
29
  // Delete if present for value
@@ -32,7 +32,7 @@ class CategoryMapComponent extends react_1.default.Component {
32
32
  if (color) {
33
33
  colorMap.push({ value, color });
34
34
  }
35
- return this.props.onChange((0, update_object_1.default)(this.props.axis, { colorMap: { $set: colorMap } }));
35
+ this.props.onChange((0, update_object_1.default)(this.props.axis, { colorMap: { $set: colorMap } }));
36
36
  };
37
37
  handleExcludeChange = (value, ev) => {
38
38
  let excludedValues;
@@ -42,7 +42,7 @@ class CategoryMapComponent extends react_1.default.Component {
42
42
  else {
43
43
  excludedValues = lodash_1.default.union(this.props.axis.excludedValues || [], [value]);
44
44
  }
45
- return this.props.onChange((0, update_object_1.default)(this.props.axis, { excludedValues: { $set: excludedValues } }));
45
+ this.props.onChange((0, update_object_1.default)(this.props.axis, { excludedValues: { $set: excludedValues } }));
46
46
  };
47
47
  // Gets the current color value if known
48
48
  lookupColor(value) {
@@ -55,7 +55,7 @@ class CategoryMapComponent extends react_1.default.Component {
55
55
  handleNullLabelChange = (e) => {
56
56
  const name = prompt(T `Enter label for none value`, this.props.axis.nullLabel || T `None`);
57
57
  if (name) {
58
- return this.props.onChange((0, update_object_1.default)(this.props.axis, { nullLabel: { $set: name } }));
58
+ this.props.onChange((0, update_object_1.default)(this.props.axis, { nullLabel: { $set: name } }));
59
59
  }
60
60
  };
61
61
  handleCategoryLabelChange = (category, e) => {
@@ -54,7 +54,7 @@ class RangesComponent extends react_1.default.Component {
54
54
  })),
55
55
  // _.map @props.xform.ranges, (range, i) =>
56
56
  // R RangeComponent, key: range.id, range: range, onChange: @handleRangeChange.bind(null, i), onRemove: @handleRemoveRange.bind(null, i)
57
- R("button", { className: "btn btn-link btn-sm", type: "button", onClick: this.handleAddRange }, R("span", { className: "fas fa-plus" }), T ` Add Range`));
57
+ R("button", { className: "btn btn-link btn-sm", type: "button", onClick: this.handleAddRange }, R("span", { className: "fas fa-plus" }), " ", T `Add Range`));
58
58
  }
59
59
  }
60
60
  exports.default = RangesComponent;
@@ -88,7 +88,7 @@ class RangeComponent extends react_1.default.Component {
88
88
  }
89
89
  if (this.props.range.maxValue != null) {
90
90
  if (placeholder) {
91
- placeholder += T ` and `;
91
+ placeholder += ` ${T `and`} `;
92
92
  }
93
93
  if (this.props.range.maxOpen) {
94
94
  placeholder += `< ${this.props.range.maxValue}`;
@@ -39,6 +39,8 @@ export interface DashboardComponentProps {
39
39
  hideTitleBar?: boolean;
40
40
  /** Called when the edit mode changes */
41
41
  onEditModeChange?: (editing: boolean) => void;
42
+ /** Locale to prefer if available */
43
+ preferredLocale?: string;
42
44
  }
43
45
  export interface DashboardComponentState {
44
46
  undoStack: UndoStack;
@@ -47,6 +49,8 @@ export interface DashboardComponentState {
47
49
  layoutOptionsOpen: boolean;
48
50
  hideQuickfilters: boolean;
49
51
  refreshKey: number;
52
+ /** Locale to use for display. Ignored if in edit mode */
53
+ locale: string;
50
54
  }
51
55
  /** Dashboard component that includes an action bar at the top
52
56
  * Manages undo stack and quickfilter value
@@ -72,6 +76,8 @@ export default class DashboardComponent extends React.Component<DashboardCompone
72
76
  handleDesignChange: (design: any) => void;
73
77
  handleShowQuickfilters: () => void;
74
78
  getCompiledFilters(): JsonQLFilter[];
79
+ /** Translate function to use for display. Do not use when editing. */
80
+ translate: (input: string) => string;
75
81
  renderEditingSwitch(): React.DetailedReactHTMLElement<{
76
82
  key: string;
77
83
  className: string;
@@ -39,6 +39,7 @@ const LayoutManager_1 = __importDefault(require("../layouts/LayoutManager"));
39
39
  const LayoutOptionsComponent_1 = require("./LayoutOptionsComponent");
40
40
  const ModalWindowComponent_1 = __importDefault(require("@mwater/react-library/lib/ModalWindowComponent"));
41
41
  const layoutOptions_1 = require("./layoutOptions");
42
+ const __1 = require("..");
42
43
  const expressions_ui_1 = require("@mwater/expressions-ui");
43
44
  /** Dashboard component that includes an action bar at the top
44
45
  * Manages undo stack and quickfilter value
@@ -50,6 +51,15 @@ class DashboardComponent extends react_1.default.Component {
50
51
  constructor(props) {
51
52
  super(props);
52
53
  const layoutOptions = (0, layoutOptions_1.getLayoutOptions)(props.design);
54
+ // Prefer the T.en locale if available when loading the dashboard
55
+ let initialLocale = props.design.locale || "en";
56
+ const otherLocales = props.design.otherLocales || [];
57
+ if (props.preferredLocale && otherLocales.includes(props.preferredLocale)) {
58
+ initialLocale = props.preferredLocale;
59
+ }
60
+ else if (otherLocales.includes(T.locale)) {
61
+ initialLocale = T.locale;
62
+ }
53
63
  this.state = {
54
64
  undoStack: new UndoStack_1.default().push(props.design),
55
65
  quickfiltersValues: props.quickfiltersValues || null,
@@ -57,7 +67,8 @@ class DashboardComponent extends react_1.default.Component {
57
67
  props.onDesignChange != null,
58
68
  layoutOptionsOpen: false,
59
69
  hideQuickfilters: layoutOptions.hideQuickfiltersWidth != null && layoutOptions.hideQuickfiltersWidth > document.body.clientWidth,
60
- refreshKey: 1
70
+ refreshKey: 1,
71
+ locale: initialLocale
61
72
  };
62
73
  if (props.onEditModeChange) {
63
74
  props.onEditModeChange(this.state.editing);
@@ -141,15 +152,24 @@ class DashboardComponent extends react_1.default.Component {
141
152
  compiledFilters = compiledFilters.concat(this.props.filters || []);
142
153
  return compiledFilters;
143
154
  }
155
+ /** Translate function to use for display. Do not use when editing. */
156
+ translate = (input) => {
157
+ const designLocale = this.props.design.locale ?? "en";
158
+ const displayLocale = this.state.editing ? designLocale : (this.state.locale ?? designLocale ?? "en");
159
+ if (designLocale === displayLocale) {
160
+ return input;
161
+ }
162
+ return this.props.design.translations?.[displayLocale]?.[input] ?? input;
163
+ };
144
164
  renderEditingSwitch() {
145
165
  return R("a", {
146
166
  key: "edit",
147
167
  className: `btn btn-primary btn-sm ${this.state.editing ? "active" : ""}`,
148
168
  onClick: this.handleToggleEditing
149
- }, R("span", { className: "fas fa-pencil-alt" }), this.state.editing ? T ` Editing` : T ` Edit`);
169
+ }, R("span", { className: "fas fa-pencil-alt" }), " ", this.state.editing ? T `Editing` : T `Edit`);
150
170
  }
151
171
  renderStyle() {
152
- 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" }, T ` Layout `));
172
+ 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" }, " ", T `Layout`));
153
173
  }
154
174
  renderActionLinks() {
155
175
  return R("div", null, this.state.editing
@@ -158,22 +178,30 @@ class DashboardComponent extends react_1.default.Component {
158
178
  key: "undo",
159
179
  className: `btn btn-link btn-sm ${!this.state.undoStack.canUndo() ? "disabled" : ""}`,
160
180
  onClick: this.handleUndo
161
- }, R("span", { className: "fas fa-caret-left" }), R("span", { className: "hide-600px" }, T ` Undo`)),
181
+ }, R("span", { className: "fas fa-caret-left" }), R("span", { className: "hide-600px" }, " ", T `Undo`)),
162
182
  " ",
163
183
  R("a", {
164
184
  key: "redo",
165
185
  className: `btn btn-link btn-sm ${!this.state.undoStack.canRedo() ? "disabled" : ""}`,
166
186
  onClick: this.handleRedo
167
- }, R("span", { className: "fas fa-caret-right" }), R("span", { className: "hide-600px" }, T ` Redo`))
187
+ }, R("span", { className: "fas fa-caret-right" }), R("span", { className: "hide-600px" }, " ", T `Redo`))
168
188
  ]
169
- : undefined, R("a", { key: "print", className: "btn btn-link btn-sm", onClick: this.handlePrint }, R("span", { className: "fas fa-print" }), R("span", { className: "hide-600px" }, T ` Print`)), R("a", { key: "refresh", className: "btn btn-link btn-sm", onClick: this.handleRefreshData }, R("span", { className: "fas fa-sync" }), R("span", { className: "hide-600px" }, T ` Refresh`)), this.state.hideQuickfilters && this.props.design.quickfilters && this.props.design.quickfilters.length > 0
170
- ? 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`))
189
+ : undefined, !this.state.editing && this.props.design.otherLocales && this.props.design.otherLocales.length > 0
190
+ ? R("div", { key: "translations", className: "dropdown d-inline-block" }, R("a", {
191
+ className: "btn btn-link btn-sm dropdown-toggle",
192
+ "data-bs-toggle": "dropdown"
193
+ }, R("span", { className: "fal fa-globe" }), " ", this.state.locale), R("ul", { className: "dropdown-menu dropdown-menu-end" }, [this.props.design.locale || "en", ...this.props.design.otherLocales].map(locale => R("li", { key: locale }, R("a", {
194
+ className: "dropdown-item",
195
+ onClick: () => this.setState({ locale: locale })
196
+ }, __1.languages.find(l => l.code === locale)?.name || locale)))))
197
+ : undefined, R("a", { key: "print", className: "btn btn-link btn-sm", onClick: this.handlePrint }, R("span", { className: "fas fa-print" }), R("span", { className: "hide-600px" }, " ", T `Print`)), R("a", { key: "refresh", className: "btn btn-link btn-sm", onClick: this.handleRefreshData }, R("span", { className: "fas fa-sync" }), R("span", { className: "hide-600px" }, " ", T `Refresh`)), this.state.hideQuickfilters && this.props.design.quickfilters && this.props.design.quickfilters.length > 0
198
+ ? 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`))
171
199
  : undefined,
172
200
  // R 'a', key: "export", className: "btn btn-link btn-sm", onClick: @handleSaveDesignFile,
173
201
  // R('span', className: "glyphicon glyphicon-download-alt")
174
202
  // " Export"
175
203
  this.state.editing
176
- ? R("a", { key: "settings", className: "btn btn-link btn-sm", onClick: this.handleSettings }, R("span", { className: "fas fa-cog" }), R("span", { className: "hide-600px" }, T ` Settings`))
204
+ ? R("a", { key: "settings", className: "btn btn-link btn-sm", onClick: this.handleSettings }, R("span", { className: "fas fa-cog" }), R("span", { className: "hide-600px" }, " ", T `Settings`))
177
205
  : undefined, this.state.editing ? this.renderStyle() : undefined, this.props.extraTitleButtonsElem, this.props.onDesignChange != null ? this.renderEditingSwitch() : undefined);
178
206
  }
179
207
  renderTitleBar() {
@@ -191,7 +219,8 @@ class DashboardComponent extends react_1.default.Component {
191
219
  filters: this.getCompiledFilters(),
192
220
  hideTopBorder: this.props.hideTitleBar,
193
221
  // Don't hide if title bar is hidden as it can't be shown again
194
- onHide: () => !this.props.hideTitleBar ? this.setState({ hideQuickfilters: true }) : undefined
222
+ onHide: () => !this.props.hideTitleBar ? this.setState({ hideQuickfilters: true }) : undefined,
223
+ translate: this.translate
195
224
  });
196
225
  }
197
226
  refDashboardView = (el) => {
@@ -201,6 +230,7 @@ class DashboardComponent extends react_1.default.Component {
201
230
  let filters = this.props.filters || [];
202
231
  // Compile quickfilters
203
232
  filters = filters.concat(new QuickfilterCompiler_1.default(this.props.schema).compile(this.props.design.quickfilters || [], this.state.quickfiltersValues, this.props.quickfilterLocks));
233
+ const displayLocale = this.state.editing ? this.props.design.locale || "en" : this.state.locale;
204
234
  const dashboardView = R(DashboardViewComponent_1.default, {
205
235
  schema: this.props.schema,
206
236
  dataSource: this.props.dataSource,
@@ -212,7 +242,8 @@ class DashboardComponent extends react_1.default.Component {
212
242
  onRowClick: this.props.onRowClick,
213
243
  namedStrings: this.props.namedStrings,
214
244
  hideScopes: this.state.hideQuickfilters,
215
- refreshKey: this.state.refreshKey
245
+ refreshKey: this.state.refreshKey,
246
+ locale: displayLocale
216
247
  });
217
248
  const readonlyDashboardView = R(DashboardViewComponent_1.default, {
218
249
  schema: this.props.schema,
@@ -223,11 +254,12 @@ class DashboardComponent extends react_1.default.Component {
223
254
  filters,
224
255
  onRowClick: this.props.onRowClick,
225
256
  namedStrings: this.props.namedStrings,
226
- hideScopes: this.state.hideQuickfilters
257
+ hideScopes: this.state.hideQuickfilters,
258
+ locale: displayLocale
227
259
  });
228
260
  // Pass active tables down to table select components so they can present a shorter list
229
261
  return react_1.default.createElement(expressions_ui_1.ActiveTablesContext.Provider, { value: DashboardUtils.getFilterableTables(this.props.design, this.props.schema) },
230
- react_1.default.createElement(expressions_ui_1.LocaleContext.Provider, { value: this.props.design.locale ?? "en" },
262
+ react_1.default.createElement(expressions_ui_1.LocaleContext.Provider, { value: displayLocale },
231
263
  react_1.default.createElement("div", { style: {
232
264
  display: "grid",
233
265
  gridTemplateRows: this.props.hideTitleBar ? "auto 1fr" : "auto auto 1fr",