@mwater/visualization 5.4.1 → 5.4.3

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 (273) 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 +12 -20
  21. package/lib/dashboards/DashboardComponent.js +109 -69
  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/languages.js +6 -1
  47. package/lib/layouts/blocks/HorizontalBlockComponent.js +2 -2
  48. package/lib/mWaterLoader.d.ts +1 -1
  49. package/lib/maps/BufferLayer.d.ts +7 -5
  50. package/lib/maps/BufferLayer.js +69 -48
  51. package/lib/maps/BufferLayerDesign.d.ts +21 -14
  52. package/lib/maps/BufferLayerDesignerComponent.d.ts +16 -31
  53. package/lib/maps/BufferLayerDesignerComponent.js +68 -102
  54. package/lib/maps/ChoroplethLayer.d.ts +5 -4
  55. package/lib/maps/ChoroplethLayer.js +32 -9
  56. package/lib/maps/ChoroplethLayerDesign.d.ts +6 -2
  57. package/lib/maps/ChoroplethLayerDesigner.js +4 -2
  58. package/lib/maps/ClusterLayer.d.ts +3 -4
  59. package/lib/maps/ClusterLayer.js +2 -1
  60. package/lib/maps/DetailLevelSelectComponent.js +1 -1
  61. package/lib/maps/DirectMapDataSource.js +2 -1
  62. package/lib/maps/EditPopupComponent.js +5 -3
  63. package/lib/maps/GridLayer.d.ts +3 -4
  64. package/lib/maps/GridLayer.js +2 -1
  65. package/lib/maps/GridLayerDesigner.js +5 -3
  66. package/lib/maps/HoverContent.d.ts +11 -3
  67. package/lib/maps/HoverContent.js +25 -9
  68. package/lib/maps/Layer.d.ts +24 -3
  69. package/lib/maps/Layer.js +5 -1
  70. package/lib/maps/LayerFactory.js +0 -8
  71. package/lib/maps/LayerLegendComponent.js +0 -1
  72. package/lib/maps/LayerSwitcherComponent.d.ts +1 -0
  73. package/lib/maps/LayerSwitcherComponent.js +1 -1
  74. package/lib/maps/LeafletMapComponent.js +3 -1
  75. package/lib/maps/LegendComponent.d.ts +1 -0
  76. package/lib/maps/LegendComponent.js +9 -1
  77. package/lib/maps/MWaterServerLayer.d.ts +2 -2
  78. package/lib/maps/MWaterServerLayer.js +2 -2
  79. package/lib/maps/MapComponent.js +3 -3
  80. package/lib/maps/MapDesign.d.ts +2 -0
  81. package/lib/maps/MapDesignerComponent.d.ts +4 -3
  82. package/lib/maps/MapDesignerComponent.js +68 -74
  83. package/lib/maps/MapLayerViewDesignerComponent.js +2 -2
  84. package/lib/maps/MapUtils.d.ts +4 -0
  85. package/lib/maps/MapUtils.js +19 -0
  86. package/lib/maps/MapViewComponent.d.ts +8 -3
  87. package/lib/maps/MarkersLayer.d.ts +5 -4
  88. package/lib/maps/MarkersLayer.js +33 -7
  89. package/lib/maps/MarkersLayerDesign.d.ts +19 -16
  90. package/lib/maps/PopupFilterJoinsUtils.d.ts +6 -3
  91. package/lib/maps/PopupFilterJoinsUtils.js +0 -6
  92. package/lib/maps/RasterMapViewComponent.d.ts +3 -31
  93. package/lib/maps/RasterMapViewComponent.js +7 -2
  94. package/lib/maps/ServerMapDataSource.js +2 -1
  95. package/lib/maps/SwitchableTileUrlLayer.d.ts +3 -3
  96. package/lib/maps/SwitchableTileUrlLayer.js +2 -1
  97. package/lib/maps/TileUrlLayer.d.ts +4 -5
  98. package/lib/maps/TileUrlLayer.js +2 -1
  99. package/lib/maps/VectorMapViewComponent.d.ts +5 -37
  100. package/lib/maps/VectorMapViewComponent.js +19 -8
  101. package/lib/maps/maps.d.ts +3 -0
  102. package/lib/quickfilter/Quickfilter.d.ts +2 -0
  103. package/lib/quickfilter/QuickfiltersComponent.d.ts +2 -0
  104. package/lib/quickfilter/QuickfiltersComponent.js +9 -7
  105. package/lib/quickfilter/QuickfiltersDesignComponent.d.ts +5 -30
  106. package/lib/quickfilter/QuickfiltersDesignComponent.js +56 -63
  107. package/lib/richtext/ExprItemsHtmlConverter.d.ts +5 -2
  108. package/lib/richtext/ExprItemsHtmlConverter.js +4 -4
  109. package/lib/richtext/ExprItemsTranslator.d.ts +5 -0
  110. package/lib/richtext/ExprItemsTranslator.js +149 -0
  111. package/lib/richtext/ItemsHtmlConverter.d.ts +1 -1
  112. package/lib/richtext/ItemsHtmlConverter.js +31 -15
  113. package/lib/wellknown.js +12 -9
  114. package/lib/widgets/IFrameWidget.d.ts +4 -4
  115. package/lib/widgets/ImageWidget.d.ts +7 -4
  116. package/lib/widgets/ImageWidget.js +9 -1
  117. package/lib/widgets/ImageWidgetComponent.d.ts +1 -0
  118. package/lib/widgets/ImageWidgetComponent.js +1 -1
  119. package/lib/widgets/MapWidget.d.ts +5 -48
  120. package/lib/widgets/MapWidget.js +26 -63
  121. package/lib/widgets/MarkdownWidget.d.ts +3 -0
  122. package/lib/widgets/MarkdownWidget.js +3 -0
  123. package/lib/widgets/TOCWidget.d.ts +15 -27
  124. package/lib/widgets/TOCWidget.js +107 -183
  125. package/lib/widgets/Widget.d.ts +18 -7
  126. package/lib/widgets/Widget.js +4 -0
  127. package/lib/widgets/WidgetScopesViewComponent.js +1 -1
  128. package/lib/widgets/charts/Chart.d.ts +10 -1
  129. package/lib/widgets/charts/Chart.js +22 -11
  130. package/lib/widgets/charts/ChartViewComponent.d.ts +4 -0
  131. package/lib/widgets/charts/ChartViewComponent.js +6 -3
  132. package/lib/widgets/charts/ChartWidget.d.ts +2 -0
  133. package/lib/widgets/charts/ChartWidget.js +9 -1
  134. package/lib/widgets/charts/ChartWidgetComponent.d.ts +4 -0
  135. package/lib/widgets/charts/ChartWidgetComponent.js +2 -2
  136. package/lib/widgets/charts/calendar/CalendarChart.d.ts +1 -0
  137. package/lib/widgets/charts/calendar/CalendarChart.js +26 -0
  138. package/lib/widgets/charts/calendar/CalendarChartViewComponent.js +3 -1
  139. package/lib/widgets/charts/imagemosaic/ImageMosaicChart.d.ts +1 -0
  140. package/lib/widgets/charts/imagemosaic/ImageMosaicChart.js +8 -0
  141. package/lib/widgets/charts/layered/LayeredChart.d.ts +2 -0
  142. package/lib/widgets/charts/layered/LayeredChart.js +63 -3
  143. package/lib/widgets/charts/layered/LayeredChartCompiler.d.ts +1 -1
  144. package/lib/widgets/charts/layered/LayeredChartCompiler.js +1 -1
  145. package/lib/widgets/charts/layered/LayeredChartDesignerComponent.js +2 -2
  146. package/lib/widgets/charts/layered/LayeredChartViewComponent.js +8 -3
  147. package/lib/widgets/charts/pivot/PivotChart.d.ts +1 -0
  148. package/lib/widgets/charts/pivot/PivotChart.js +63 -0
  149. package/lib/widgets/charts/pivot/PivotChartLayoutComponent.js +1 -1
  150. package/lib/widgets/charts/pivot/SegmentDesignerComponent.js +7 -4
  151. package/lib/widgets/charts/table/OrderingsComponent.js +1 -1
  152. package/lib/widgets/charts/table/TableChart.d.ts +1 -0
  153. package/lib/widgets/charts/table/TableChart.js +15 -0
  154. package/lib/widgets/text/TextComponent.d.ts +11 -4
  155. package/lib/widgets/text/TextComponent.js +11 -8
  156. package/lib/widgets/text/TextWidget.d.ts +6 -3
  157. package/lib/widgets/text/TextWidget.js +7 -1
  158. package/lib/widgets/text/TextWidgetComponent.d.ts +4 -0
  159. package/lib/widgets/text/TextWidgetComponent.js +7 -1
  160. package/lib/widgets/text/TextWidgetDesign.d.ts +2 -4
  161. package/lib/widgets/text/TextWidgetDesign.js +1 -1
  162. package/package.json +7 -8
  163. package/src/ColorComponent.tsx +1 -2
  164. package/src/IdSelection.ts +62 -0
  165. package/src/MWaterAddRelatedIndicatorComponent.ts +3 -2
  166. package/src/MWaterCompleteTableSelectComponent.tsx +36 -46
  167. package/src/MWaterLoaderComponent.ts +28 -26
  168. package/src/MWaterResponsesFilterComponent.ts +5 -2
  169. package/src/MWaterTableSelectComponent.tsx +5 -9
  170. package/src/autotranslate.ts +141 -0
  171. package/src/axes/AxisBuilder.ts +3 -3
  172. package/src/axes/AxisColorEditorComponent.tsx +5 -0
  173. package/src/axes/{AxisComponent.ts → AxisComponent.tsx} +106 -106
  174. package/src/axes/CategoryMapComponent.ts +4 -4
  175. package/src/axes/RangesComponent.ts +3 -2
  176. package/src/dashboards/DashboardComponent.tsx +189 -125
  177. package/src/dashboards/DashboardDesign.ts +9 -2
  178. package/src/dashboards/DashboardUtils.ts +39 -0
  179. package/src/dashboards/DashboardViewComponent.tsx +22 -3
  180. package/src/dashboards/ServerDashboardDataSource.ts +2 -1
  181. package/src/dashboards/SettingsModalComponent.tsx +450 -35
  182. package/src/dashboards/WidgetComponent.tsx +12 -6
  183. package/src/datagrids/CellEditor.tsx +354 -0
  184. package/src/datagrids/DatagridComponent.tsx +646 -0
  185. package/src/datagrids/DatagridViewComponent.tsx +539 -0
  186. package/src/datagrids/DirectDatagridDataSource.ts +2 -3
  187. package/src/datagrids/{ExprCellComponent.ts → ExprCellComponent.tsx} +28 -23
  188. package/src/datagrids/{FindReplaceModalComponent.ts → FindReplaceModalComponent.tsx} +109 -122
  189. package/src/index.css +1 -1
  190. package/src/index.ts +0 -1
  191. package/src/languages.ts +6 -1
  192. package/src/layouts/blocks/HorizontalBlockComponent.ts +2 -2
  193. package/src/mWaterLoader.ts +1 -1
  194. package/src/maps/BufferLayer.ts +83 -60
  195. package/src/maps/BufferLayerDesign.ts +20 -14
  196. package/src/maps/BufferLayerDesignerComponent.tsx +309 -0
  197. package/src/maps/ChoroplethLayer.ts +40 -19
  198. package/src/maps/ChoroplethLayerDesign.ts +4 -2
  199. package/src/maps/ChoroplethLayerDesigner.tsx +4 -2
  200. package/src/maps/ClusterLayer.ts +4 -10
  201. package/src/maps/DetailLevelSelectComponent.ts +1 -1
  202. package/src/maps/DirectMapDataSource.ts +2 -1
  203. package/src/maps/EditPopupComponent.ts +7 -3
  204. package/src/maps/GridLayer.ts +4 -10
  205. package/src/maps/GridLayerDesigner.tsx +5 -3
  206. package/src/maps/HoverContent.tsx +40 -16
  207. package/src/maps/Layer.ts +28 -10
  208. package/src/maps/LayerFactory.ts +0 -8
  209. package/src/maps/LayerLegendComponent.ts +2 -4
  210. package/src/maps/LayerSwitcherComponent.tsx +6 -2
  211. package/src/maps/LeafletMapComponent.tsx +3 -1
  212. package/src/maps/LegendComponent.tsx +10 -1
  213. package/src/maps/MWaterServerLayer.ts +3 -3
  214. package/src/maps/MapComponent.ts +3 -3
  215. package/src/maps/MapDesign.ts +3 -0
  216. package/src/maps/MapDesignerComponent.tsx +165 -162
  217. package/src/maps/MapLayerViewDesignerComponent.ts +2 -2
  218. package/src/maps/MapUtils.ts +24 -0
  219. package/src/maps/MapViewComponent.tsx +11 -3
  220. package/src/maps/MarkersLayer.ts +44 -18
  221. package/src/maps/MarkersLayerDesign.ts +19 -16
  222. package/src/maps/PopupFilterJoinsUtils.ts +6 -2
  223. package/src/maps/RasterMapViewComponent.ts +9 -45
  224. package/src/maps/ServerMapDataSource.ts +2 -2
  225. package/src/maps/SwitchableTileUrlLayer.tsx +4 -10
  226. package/src/maps/TileUrlLayer.tsx +4 -10
  227. package/src/maps/VectorMapViewComponent.tsx +28 -55
  228. package/src/maps/maps.ts +3 -0
  229. package/src/quickfilter/Quickfilter.ts +3 -0
  230. package/src/quickfilter/QuickfiltersComponent.ts +13 -7
  231. package/src/quickfilter/QuickfiltersDesignComponent.tsx +127 -128
  232. package/src/richtext/ExprItemsHtmlConverter.ts +9 -5
  233. package/src/richtext/ExprItemsTranslator.ts +176 -0
  234. package/src/richtext/ItemsHtmlConverter.ts +33 -18
  235. package/src/wellknown.ts +33 -30
  236. package/src/widgets/ImageWidget.ts +10 -1
  237. package/src/widgets/ImageWidgetComponent.ts +3 -2
  238. package/src/widgets/{MapWidget.ts → MapWidget.tsx} +90 -101
  239. package/src/widgets/MarkdownWidget.ts +3 -0
  240. package/src/widgets/TOCWidget.tsx +281 -0
  241. package/src/widgets/Widget.ts +25 -5
  242. package/src/widgets/WidgetScopesViewComponent.ts +2 -1
  243. package/src/widgets/charts/Chart.ts +31 -12
  244. package/src/widgets/charts/ChartViewComponent.ts +13 -3
  245. package/src/widgets/charts/ChartWidget.ts +11 -1
  246. package/src/widgets/charts/ChartWidgetComponent.tsx +9 -1
  247. package/src/widgets/charts/calendar/CalendarChart.ts +29 -0
  248. package/src/widgets/charts/calendar/CalendarChartViewComponent.tsx +3 -1
  249. package/src/widgets/charts/imagemosaic/ImageMosaicChart.ts +9 -0
  250. package/src/widgets/charts/layered/LayeredChart.ts +71 -3
  251. package/src/widgets/charts/layered/LayeredChartCompiler.ts +2 -2
  252. package/src/widgets/charts/layered/LayeredChartDesignerComponent.tsx +4 -2
  253. package/src/widgets/charts/layered/LayeredChartViewComponent.ts +10 -4
  254. package/src/widgets/charts/pivot/PivotChart.ts +73 -0
  255. package/src/widgets/charts/pivot/PivotChartLayoutComponent.tsx +1 -1
  256. package/src/widgets/charts/pivot/SegmentDesignerComponent.tsx +6 -4
  257. package/src/widgets/charts/table/OrderingsComponent.tsx +2 -1
  258. package/src/widgets/charts/table/TableChart.ts +17 -0
  259. package/src/widgets/text/TextComponent.tsx +22 -12
  260. package/src/widgets/text/TextWidget.ts +9 -2
  261. package/src/widgets/text/TextWidgetComponent.tsx +16 -1
  262. package/src/widgets/text/TextWidgetDesign.ts +4 -7
  263. package/test/IdSelectionTests.ts +54 -0
  264. package/test/LayeredChartCompilerTests.ts +0 -2
  265. package/test/richtext/ExprItemsTranslatorTests.ts +144 -0
  266. package/test/wellknownTests.ts +144 -0
  267. package/src/datagrids/DatagridComponent.ts +0 -478
  268. package/src/datagrids/DatagridViewComponent.ts +0 -464
  269. package/src/datagrids/EditExprCellComponent.tsx +0 -305
  270. package/src/datagrids/README.md +0 -3
  271. package/src/maps/BufferLayerDesignerComponent.ts +0 -311
  272. package/src/widgets/TOCWidget.ts +0 -326
  273. package/test/LegoLayoutEngineTests.ts +0 -69
@@ -37,6 +37,10 @@ const QuickfiltersDesignComponent_1 = __importDefault(require("../quickfilter/Qu
37
37
  const FiltersDesignerComponent_1 = __importDefault(require("../FiltersDesignerComponent"));
38
38
  const MWaterContextComponent_1 = require("../MWaterContextComponent");
39
39
  const immer_1 = __importDefault(require("immer"));
40
+ const TabbedComponent_1 = __importDefault(require("@mwater/react-library/lib/TabbedComponent"));
41
+ const file_saver_1 = __importDefault(require("file-saver"));
42
+ const localizeUtils = __importStar(require("ez-localize/lib/utils"));
43
+ const autotranslate_1 = require("../autotranslate");
40
44
  /** Popup with settings for dashboard */
41
45
  class SettingsModalComponent extends react_1.default.Component {
42
46
  constructor(props) {
@@ -71,33 +75,41 @@ exports.default = SettingsModalComponent;
71
75
  /** Settings component for dashboard that is used in the settings modal */
72
76
  function SettingsComponent(props) {
73
77
  const { design, onDesignChange, schema, dataSource } = props;
74
- // Get filterable tables
75
- const filterableTables = DashboardUtils.getFilterableTables(design, schema);
76
- const localeOptions = (0, react_1.useMemo)(() => {
77
- return lodash_1.default.map(languages_1.languages, (language) => {
78
- return {
79
- value: language.code,
80
- label: language.en + " (" + language.name + ")"
81
- };
82
- });
83
- }, [languages_1.languages]);
78
+ const tabs = [
79
+ {
80
+ id: "filters",
81
+ label: T `Filters`,
82
+ elem: (react_1.default.createElement(FiltersTab, { design: design, onDesignChange: onDesignChange, schema: schema, dataSource: dataSource }))
83
+ },
84
+ {
85
+ id: "language",
86
+ label: T `Translation`,
87
+ elem: (react_1.default.createElement(LanguageTab, { design: design, onDesignChange: onDesignChange, schema: schema }))
88
+ }
89
+ ];
84
90
  return (react_1.default.createElement("div", { style: { paddingBottom: 200 } },
91
+ react_1.default.createElement(TabbedComponent_1.default, { tabs: tabs, initialTabId: "filters" })));
92
+ }
93
+ function FiltersTab({ design, onDesignChange, schema, dataSource }) {
94
+ // Get filterable tables
95
+ const filterableTables = (0, react_1.useMemo)(() => DashboardUtils.getFilterableTables(design, schema), [design, schema]);
96
+ return (react_1.default.createElement(react_1.default.Fragment, null,
85
97
  react_1.default.createElement("h4", null, T `Quick Filters`),
86
- react_1.default.createElement("div", { className: "text-muted" }, T `Quick filters are shown to the user as a dropdown at the top of the dashboard and can be used to filter data of widgets.`),
98
+ react_1.default.createElement("div", { className: "mb-2" }, T `Quick filters are shown to the user as a dropdown at the top of the dashboard and can be used to filter data of widgets.`),
87
99
  filterableTables.length > 0 ? (react_1.default.createElement(QuickfiltersDesignComponent_1.default, { design: design.quickfilters || [], onDesignChange: (quickfilters) => {
88
100
  onDesignChange((0, immer_1.default)(design, draft => {
89
101
  draft.quickfilters = quickfilters;
90
102
  }));
91
103
  }, schema: schema, dataSource: dataSource, tables: filterableTables })) : (T `Nothing to quickfilter. Add widgets to the dashboard`),
92
- react_1.default.createElement("h4", { style: { paddingTop: 10 } }, T `Filters`),
93
- react_1.default.createElement("div", { className: "text-muted" }, T `Filters are built in to the dashboard and cannot be changed by viewers of the dashboard.`),
104
+ react_1.default.createElement("h4", { style: { paddingTop: 15 } }, T `Filters`),
105
+ react_1.default.createElement("div", { className: "mb-2" }, T `Filters are built in to the dashboard and cannot be changed by viewers of the dashboard.`),
94
106
  filterableTables.length > 0 ? (react_1.default.createElement(FiltersDesignerComponent_1.default, { schema: schema, dataSource: dataSource, filters: design.filters, onFiltersChange: (filters) => {
95
107
  onDesignChange((0, immer_1.default)(design, draft => {
96
108
  draft.filters = filters;
97
109
  }));
98
110
  }, filterableTables: filterableTables })) : (T `Nothing to filter. Add widgets to the dashboard`),
99
111
  react_1.default.createElement(MWaterContextComponent_1.GlobalFiltersElementFactoryContext.Consumer, null, globalFiltersElementFactory => globalFiltersElementFactory ? (react_1.default.createElement("div", null,
100
- react_1.default.createElement("h4", { style: { paddingTop: 10 } }, T `Global Filters`),
112
+ react_1.default.createElement("h4", { style: { paddingTop: 15 } }, T `Global Filters`),
101
113
  globalFiltersElementFactory({
102
114
  schema,
103
115
  dataSource,
@@ -109,14 +121,239 @@ function SettingsComponent(props) {
109
121
  }));
110
122
  }
111
123
  }))) : undefined),
112
- react_1.default.createElement("h4", { style: { paddingTop: 10 } }, T `Language`),
113
- react_1.default.createElement("div", { className: "text-muted" }, T `Controls the preferred language of widgets and uses specified language when available`),
114
- react_1.default.createElement(react_select_1.default, { value: lodash_1.default.findWhere(localeOptions, { value: design.locale || "en" }) || null, options: localeOptions, onChange: (locale) => onDesignChange((0, immer_1.default)(design, draft => {
115
- draft.locale = locale.value;
116
- })) }),
117
124
  design.implicitFiltersEnabled && (react_1.default.createElement(react_1.default.Fragment, null,
118
125
  react_1.default.createElement("h4", { style: { paddingTop: 10 } }, T `Advanced`),
119
126
  react_1.default.createElement(ui.Checkbox, { value: design.implicitFiltersEnabled != null ? design.implicitFiltersEnabled : true, onChange: (value) => onDesignChange((0, immer_1.default)(design, draft => {
120
127
  draft.implicitFiltersEnabled = value;
121
128
  })) }, T `Enable Implicit Filtering (leave unchecked for new dashboards)`)))));
122
129
  }
130
+ function LanguageTab({ design, onDesignChange, schema }) {
131
+ const fileInputRef = (0, react_1.useRef)(null);
132
+ const locale = design.locale || "en";
133
+ const localeOptions = (0, react_1.useMemo)(() => {
134
+ return lodash_1.default.sortBy(lodash_1.default.map(languages_1.languages, (language) => ({
135
+ value: language.code,
136
+ label: `${language.en} (${language.name})`
137
+ })), 'label');
138
+ }, [languages_1.languages]);
139
+ // Get available languages that aren't already selected
140
+ const availableLocaleOptions = (0, react_1.useMemo)(() => {
141
+ const selectedLocales = new Set([design.locale, ...(design.otherLocales || [])]);
142
+ return localeOptions.filter(opt => !selectedLocales.has(opt.value));
143
+ }, [localeOptions, design.locale, design.otherLocales]);
144
+ const translatableStrings = (0, react_1.useMemo)(() => DashboardUtils.getTranslatableStringsFromDashboard(design, schema), [design, schema]);
145
+ // Calculate percentage of strings translated for each locale
146
+ const translationPercentages = (0, react_1.useMemo)(() => {
147
+ const percentages = {};
148
+ const totalStrings = translatableStrings.length;
149
+ for (const locale of design.otherLocales || []) {
150
+ const translatedCount = translatableStrings.filter(str => design.translations?.[locale]?.[str] != null).length;
151
+ // Round down to nearest percent
152
+ percentages[locale] = Math.floor((translatedCount / totalStrings) * 100);
153
+ }
154
+ return percentages;
155
+ }, [design.translations, design.otherLocales, translatableStrings]);
156
+ const handleAddLocale = (locale) => {
157
+ onDesignChange((0, immer_1.default)(design, draft => {
158
+ draft.otherLocales = [...(draft.otherLocales || []), locale.value];
159
+ // Initialize empty translations object if needed
160
+ if (!draft.translations) {
161
+ draft.translations = {};
162
+ }
163
+ if (!draft.translations[locale.value]) {
164
+ draft.translations[locale.value] = {};
165
+ }
166
+ }));
167
+ };
168
+ const handleRemoveLocale = (localeToRemove) => {
169
+ onDesignChange((0, immer_1.default)(design, draft => {
170
+ draft.otherLocales = (draft.otherLocales || []).filter(locale => locale !== localeToRemove);
171
+ // Remove translations for this locale
172
+ if (draft.translations) {
173
+ delete draft.translations[localeToRemove];
174
+ }
175
+ }));
176
+ };
177
+ // Convert dashboard translations to LocalizedString format
178
+ const getLocalizedStrings = () => {
179
+ // For each string, create a localized string
180
+ const localizedStrings = [];
181
+ for (const str of translatableStrings) {
182
+ const localizedString = { _base: locale };
183
+ localizedString[locale] = str;
184
+ // Only add translations for other locales if they exist
185
+ for (const otherLocale of design.otherLocales || []) {
186
+ if (design.translations?.[otherLocale]?.[str]) {
187
+ localizedString[otherLocale] = design.translations?.[otherLocale]?.[str];
188
+ }
189
+ }
190
+ localizedStrings.push(localizedString);
191
+ }
192
+ return localizedStrings;
193
+ };
194
+ // Convert LocalizedString format back to dashboard translations
195
+ const updateFromLocalizedStrings = (localizedStrings) => {
196
+ const newTranslations = {};
197
+ // Get all locales except base
198
+ const locales = design.otherLocales || [];
199
+ // Initialize translations object
200
+ for (const locale of locales) {
201
+ newTranslations[locale] = {};
202
+ }
203
+ // Add all translations
204
+ for (const str of localizedStrings) {
205
+ for (const locale of locales) {
206
+ if (str[locale]) {
207
+ newTranslations[locale][str[str._base]] = str[locale];
208
+ }
209
+ }
210
+ }
211
+ return newTranslations;
212
+ };
213
+ const handleDownload = () => {
214
+ // Get strings in LocalizedString format
215
+ const strings = getLocalizedStrings();
216
+ // Create xlsx base64 using all locales (primary + other)
217
+ const locales = [{ code: locale, name: locale }]
218
+ .concat((design.otherLocales || []).map(code => ({ code, name: code })));
219
+ const base64 = localizeUtils.exportXlsx(locales, strings);
220
+ // Download
221
+ file_saver_1.default.saveAs(b64toBlob(base64, "application/octet-stream"), "Dashboard Translations.xlsx");
222
+ };
223
+ const handleUpload = () => {
224
+ fileInputRef.current?.click();
225
+ };
226
+ const handleUploadChange = (evt) => {
227
+ const reader = new FileReader();
228
+ reader.onload = (file) => {
229
+ if (!file.target?.result) {
230
+ return;
231
+ }
232
+ const base64 = file.target.result.split(",")[1];
233
+ try {
234
+ // Create locales array for import
235
+ const locales = [{ code: locale, name: locale }]
236
+ .concat((design.otherLocales || []).map(code => ({ code, name: code })));
237
+ // Import updates
238
+ const updates = localizeUtils.importXlsx(locales, base64);
239
+ // If nothing localized
240
+ if (updates.length === 0) {
241
+ alert(T `No translation data found in file`);
242
+ return;
243
+ }
244
+ // Convert back to dashboard format and update
245
+ const newTranslations = updateFromLocalizedStrings(updates);
246
+ onDesignChange((0, immer_1.default)(design, draft => {
247
+ draft.translations = newTranslations;
248
+ }));
249
+ alert(T `${updates.length} translations applied`);
250
+ }
251
+ catch (error) {
252
+ console.error("Invalid xlsx file:", error);
253
+ alert(T `Invalid xlsx file`);
254
+ }
255
+ };
256
+ if (evt.target.files?.[0]) {
257
+ reader.readAsDataURL(evt.target.files[0]);
258
+ }
259
+ };
260
+ return (react_1.default.createElement(react_1.default.Fragment, null,
261
+ react_1.default.createElement("h4", null, T `Base Language`),
262
+ react_1.default.createElement("div", { className: "mb-2" }, T `This is the language that the dashboard is written in.`),
263
+ react_1.default.createElement(react_select_1.default, { value: lodash_1.default.findWhere(localeOptions, { value: design.locale || "en" }) || null, options: localeOptions, onChange: (locale) => onDesignChange((0, immer_1.default)(design, draft => {
264
+ draft.locale = locale.value;
265
+ })) }),
266
+ react_1.default.createElement("h4", { className: "mt-4" }, T `Additional Languages`),
267
+ react_1.default.createElement("div", { className: "mb-2" }, T `Add languages that this dashboard will be translated into`),
268
+ react_1.default.createElement("table", null,
269
+ react_1.default.createElement("tbody", null, (design.otherLocales || []).map(locale => {
270
+ const localeOption = lodash_1.default.findWhere(localeOptions, { value: locale });
271
+ if (!localeOption) {
272
+ return null;
273
+ }
274
+ return (react_1.default.createElement("tr", { key: locale },
275
+ react_1.default.createElement("td", { style: { paddingRight: 10 } }, localeOption.label),
276
+ react_1.default.createElement("td", { className: translationPercentages[locale] === 100 ? "text-success" : "text-warning", style: { textAlign: "right" } }, T `${translationPercentages[locale]}% translated`),
277
+ react_1.default.createElement("td", null, translationPercentages[locale] < 100 && (react_1.default.createElement(AutoTranslateLink, { locale: locale, baseLocale: design.locale || "en", strings: translatableStrings, existingTranslations: design.translations?.[locale] || {}, onTranslationsChange: (newTranslations) => {
278
+ onDesignChange((0, immer_1.default)(design, draft => {
279
+ if (!draft.translations) {
280
+ draft.translations = {};
281
+ }
282
+ draft.translations[locale] = newTranslations;
283
+ }));
284
+ } }))),
285
+ react_1.default.createElement("td", null,
286
+ react_1.default.createElement("button", { type: "button", className: "btn btn-sm btn-link", onClick: () => handleRemoveLocale(locale) },
287
+ react_1.default.createElement("i", { className: "fa fa-times" })))));
288
+ }))),
289
+ availableLocaleOptions.length > 0 && (react_1.default.createElement("div", { className: "mt-3" },
290
+ react_1.default.createElement(react_select_1.default, { value: null, options: availableLocaleOptions, onChange: handleAddLocale, placeholder: T `Add language...` }))),
291
+ (design.otherLocales || []).length > 0 && (react_1.default.createElement(react_1.default.Fragment, null,
292
+ react_1.default.createElement("h4", { className: "mt-4" }, T `Manage Translations`),
293
+ react_1.default.createElement("p", null, T `Download and re-upload an Excel spreadsheet of text to translate:`),
294
+ react_1.default.createElement("div", null,
295
+ react_1.default.createElement("button", { type: "button", className: "btn btn-secondary", onClick: handleDownload },
296
+ react_1.default.createElement("i", { className: "fas fa-download me-2" }),
297
+ T `Download XLSX`)),
298
+ react_1.default.createElement("div", { className: "text-muted mt-2" }, T `This creates a spreadsheet that can be sent to a translator. Please do not change the first column or first row of the spreadsheet.`),
299
+ react_1.default.createElement("br", null),
300
+ react_1.default.createElement("p", null, T `Once translation is complete, upload the file back using the button below:`),
301
+ react_1.default.createElement("div", null,
302
+ react_1.default.createElement("button", { type: "button", className: "btn btn-secondary", onClick: handleUpload },
303
+ react_1.default.createElement("i", { className: "fas fa-upload me-2" }),
304
+ T `Upload Translated XLSX`)),
305
+ react_1.default.createElement("input", { type: "file", ref: fileInputRef, style: { display: "none" }, onChange: handleUploadChange, accept: ".xlsx" })))));
306
+ }
307
+ /** Helper function for base64 to blob conversion */
308
+ function b64toBlob(b64Data, contentType = "", sliceSize = 512) {
309
+ const byteCharacters = atob(b64Data);
310
+ const byteArrays = [];
311
+ for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
312
+ const slice = byteCharacters.slice(offset, offset + sliceSize);
313
+ const byteNumbers = new Array(slice.length);
314
+ for (let i = 0; i < slice.length; i++) {
315
+ byteNumbers[i] = slice.charCodeAt(i);
316
+ }
317
+ const byteArray = new Uint8Array(byteNumbers);
318
+ byteArrays.push(byteArray);
319
+ }
320
+ return new Blob(byteArrays, { type: contentType });
321
+ }
322
+ function AutoTranslateLink(props) {
323
+ const { locale, baseLocale, strings, existingTranslations, onTranslationsChange } = props;
324
+ const [isTranslating, setIsTranslating] = (0, react_1.useState)(false);
325
+ const untranslatedStrings = (0, react_1.useMemo)(() => {
326
+ return strings.filter(str => !existingTranslations[str]);
327
+ }, [strings, existingTranslations]);
328
+ const handleClick = async () => {
329
+ if (isTranslating) {
330
+ return;
331
+ }
332
+ setIsTranslating(true);
333
+ try {
334
+ const translatedStrings = await (0, autotranslate_1.translateStrings)(untranslatedStrings, baseLocale, locale);
335
+ const newTranslations = { ...existingTranslations };
336
+ for (let i = 0; i < untranslatedStrings.length; i++) {
337
+ newTranslations[untranslatedStrings[i]] = translatedStrings[i];
338
+ }
339
+ onTranslationsChange(newTranslations);
340
+ }
341
+ catch (error) {
342
+ console.error("Error translating strings:", error);
343
+ alert(T `Error translating strings`);
344
+ }
345
+ finally {
346
+ setIsTranslating(false);
347
+ }
348
+ };
349
+ if (untranslatedStrings.length === 0) {
350
+ return null;
351
+ }
352
+ if (!(0, autotranslate_1.canAutoTranslate)(locale)) {
353
+ return null;
354
+ }
355
+ return (react_1.default.createElement("button", { type: "button", className: "btn btn-sm btn-link", onClick: handleClick, disabled: isTranslating, style: { marginLeft: 5, padding: "0 5px" } }, isTranslating ? (react_1.default.createElement("span", null,
356
+ react_1.default.createElement("i", { className: "fa fa-spinner fa-spin" }),
357
+ " ",
358
+ T `Translating...`)) : (T `Autotranslate`)));
359
+ }
@@ -2,13 +2,12 @@ import { Schema, DataSource } from "@mwater/expressions";
2
2
  import { JsonQLFilter } from "../JsonQLFilter";
3
3
  import { WidgetScope } from "../WidgetScope";
4
4
  import { WidgetDataSource } from "../widgets/WidgetDataSource";
5
+ import { TOCEntry } from "../widgets/Widget";
5
6
  /**
6
7
  * Component which renders a widget and ensures that props do not change
7
8
  * unnecessarily.
8
9
  */
9
10
  export declare function WidgetComponent(props: {
10
- /** Widget id */
11
- id: string;
12
11
  /** Widget type */
13
12
  type: string;
14
13
  /** Widget design */
@@ -42,11 +41,15 @@ export declare function WidgetComponent(props: {
42
41
  [key: string]: string;
43
42
  };
44
43
  /** Entries in the table of content */
45
- tocEntries?: string[];
44
+ tocEntries?: TOCEntry[];
46
45
  /** the widget callback ref */
47
46
  widgetRef: (widget: any) => void;
48
47
  /** called with (widgetId, tocEntryId) to scroll to TOC entry */
49
48
  onScrollToTOCEntry?: (widgetId: string, tocEntryId: string) => void;
50
49
  /** Change to force a refresh */
51
50
  refreshKey?: any;
51
+ /** Locale to use for display */
52
+ locale: string;
53
+ /** Translate function to use for display. Returns same string when editing. */
54
+ translate: (input: string) => string;
52
55
  }): import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>>;
@@ -37,7 +37,9 @@ function WidgetComponent(props) {
37
37
  tocEntries: props.tocEntries,
38
38
  onScrollToTOCEntry: props.onScrollToTOCEntry,
39
39
  widgetRef,
40
- refreshKey: props.refreshKey
40
+ refreshKey: props.refreshKey,
41
+ locale: props.locale,
42
+ translate: props.translate
41
43
  });
42
44
  }
43
45
  /** Always returns the same function to prevent unnecessary re-rendering. Forwards to the real function */
@@ -0,0 +1,19 @@
1
+ import React from "react";
2
+ import { EnumValue, LiteralType, Schema, DataSource } from "@mwater/expressions";
3
+ import { RenderCellEditorProps } from "@mwater/react-library/lib/GridComponent";
4
+ import "react-datepicker/dist/react-datepicker.css";
5
+ /** Determine if type can be edited */
6
+ export declare function canEditType(type: LiteralType): boolean;
7
+ /** Cell editor for a cell of the grid */
8
+ export declare const CellEditor: (props: {
9
+ type: LiteralType;
10
+ enumValues?: EnumValue[];
11
+ idTable?: string;
12
+ initialValue: any;
13
+ editorOptions: RenderCellEditorProps;
14
+ /** Update the cell value. Returns true if successful, false if cancelled */
15
+ updateCellValue: (value: any) => Promise<boolean>;
16
+ locale?: string;
17
+ schema: Schema;
18
+ dataSource: DataSource;
19
+ }) => React.JSX.Element;
@@ -0,0 +1,223 @@
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.CellEditor = void 0;
30
+ exports.canEditType = canEditType;
31
+ const lodash_1 = __importDefault(require("lodash"));
32
+ const react_1 = __importStar(require("react"));
33
+ const expressions_1 = require("@mwater/expressions");
34
+ const react_select_1 = __importDefault(require("react-select"));
35
+ const react_datepicker_1 = __importDefault(require("react-datepicker"));
36
+ require("react-datepicker/dist/react-datepicker.css");
37
+ const moment_1 = __importDefault(require("moment"));
38
+ const expressions_ui_1 = require("@mwater/expressions-ui");
39
+ /** Determine if type can be edited */
40
+ function canEditType(type) {
41
+ return ["text", "number", "enum", "enumset", "boolean", "date", "datetime", "id", "id[]"].includes(type);
42
+ }
43
+ /** Cell editor for a cell of the grid */
44
+ const CellEditor = (props) => {
45
+ const editorOptions = props.editorOptions;
46
+ const [value, setValue] = (0, react_1.useState)(props.initialValue);
47
+ const [valid, setValid] = (0, react_1.useState)(true);
48
+ // Hook save function
49
+ editorOptions.setSaveEdit(() => {
50
+ if (!valid) {
51
+ return Promise.resolve(false);
52
+ }
53
+ if (value == props.initialValue) {
54
+ return Promise.resolve(true);
55
+ }
56
+ return props.updateCellValue(value);
57
+ });
58
+ if (editorOptions.saving) {
59
+ return (react_1.default.createElement("div", { style: { backgroundColor: "#EEE", width: editorOptions.width, height: editorOptions.height, padding: 10 } },
60
+ react_1.default.createElement("i", { className: "fa fa-spinner fa-spin" })));
61
+ }
62
+ if (props.type == "text") {
63
+ return (react_1.default.createElement("input", { type: "text", value: value || "", className: "form-control", onChange: (ev) => {
64
+ setValue(ev.target.value || null);
65
+ }, onKeyDown: (ev) => {
66
+ if (ev.keyCode == 13) {
67
+ editorOptions.onEndEdit();
68
+ }
69
+ }, style: { height: editorOptions.height }, ref: (node) => {
70
+ if (node) {
71
+ setTimeout(() => {
72
+ if (node) {
73
+ node.focus();
74
+ }
75
+ }, 0);
76
+ }
77
+ } }));
78
+ }
79
+ if (props.type == "number") {
80
+ return (react_1.default.createElement(NumberInput, { value: value, onChange: setValue, onValid: setValid, height: editorOptions.height, onEnter: editorOptions.onEndEdit }));
81
+ }
82
+ if (props.type == "enum") {
83
+ return react_1.default.createElement(EnumEditor, { value: value, onChange: setValue, enumValues: props.enumValues, locale: props.locale });
84
+ }
85
+ if (props.type == "enumset") {
86
+ return react_1.default.createElement(EnumsetEditor, { value: value, onChange: setValue, enumValues: props.enumValues, locale: props.locale });
87
+ }
88
+ if (props.type == "boolean") {
89
+ return react_1.default.createElement(BooleanEditor, { value: value, onChange: setValue });
90
+ }
91
+ if (props.type == "date") {
92
+ return react_1.default.createElement(DateEditor, { value: value, onChange: setValue, locale: props.locale });
93
+ }
94
+ if (props.type == "datetime") {
95
+ return react_1.default.createElement(DatetimeEditor, { value: value, onChange: setValue, locale: props.locale });
96
+ }
97
+ if (props.type == "id") {
98
+ return (react_1.default.createElement(expressions_ui_1.IdLiteralComponent, { schema: props.schema, dataSource: props.dataSource, idTable: props.idTable, value: value, onChange: setValue }));
99
+ }
100
+ if (props.type == "id[]") {
101
+ return (react_1.default.createElement(expressions_ui_1.IdLiteralComponent, { schema: props.schema, dataSource: props.dataSource, idTable: props.idTable, value: value, onChange: setValue, multi: true }));
102
+ }
103
+ // if (props.type == "geometry") {
104
+ // const str = value.type == "Point" ? `${value.coordinates[1].toFixed(6)} ${value.coordinates[0].toFixed(6)}` : value.type
105
+ // return <div className="custom-table-grid-cell-text">
106
+ // { str }
107
+ // </div>
108
+ // }
109
+ // TODO
110
+ // text[]
111
+ // image
112
+ // imagelist
113
+ // json
114
+ return react_1.default.createElement("div", null);
115
+ };
116
+ exports.CellEditor = CellEditor;
117
+ /** Number input that calls onChange when number is valid and updates valid status */
118
+ const NumberInput = (props) => {
119
+ const [str, setStr] = (0, react_1.useState)(props.value != null ? props.value + "" : "");
120
+ const handleChange = (ev) => {
121
+ const text = ev.target.value;
122
+ setStr(text);
123
+ if (!text) {
124
+ props.onChange(null);
125
+ props.onValid(true);
126
+ }
127
+ else {
128
+ const num = parseFloat(text);
129
+ if (!isNaN(num)) {
130
+ props.onChange(num);
131
+ props.onValid(true);
132
+ }
133
+ else {
134
+ props.onValid(false);
135
+ }
136
+ }
137
+ };
138
+ return (react_1.default.createElement("input", { type: "number", value: str, className: "form-control", onChange: handleChange, style: { height: props.height }, onKeyDown: (ev) => {
139
+ if (ev.keyCode == 13) {
140
+ props.onEnter();
141
+ }
142
+ }, ref: (node) => {
143
+ if (node) {
144
+ setTimeout(() => {
145
+ if (node) {
146
+ node.focus();
147
+ }
148
+ }, 0);
149
+ }
150
+ } }));
151
+ };
152
+ /** Editor for an enum value */
153
+ const EnumEditor = (props) => {
154
+ const options = props.enumValues.map((v) => ({ label: expressions_1.ExprUtils.localizeString(v.name, props.locale), value: v.id }));
155
+ return (react_1.default.createElement(react_select_1.default, { options: options, isClearable: true, onChange: (opt) => props.onChange(opt ? opt.value : null), value: options.find((opt) => opt.value == props.value) || null, styles: {
156
+ // Keep menu above modal
157
+ menu: (style) => ({ ...style, zIndex: 2000 })
158
+ }, ref: (node) => {
159
+ if (node) {
160
+ setTimeout(() => {
161
+ if (node) {
162
+ node.focus();
163
+ }
164
+ }, 0);
165
+ }
166
+ } }));
167
+ };
168
+ /** Editor for an enumset value */
169
+ const EnumsetEditor = (props) => {
170
+ const options = props.enumValues.map((v) => ({ label: expressions_1.ExprUtils.localizeString(v.name, props.locale), value: v.id }));
171
+ const value = lodash_1.default.map(props.value || [], (v) => lodash_1.default.find(options, (o) => o.value == v)) || [];
172
+ return (react_1.default.createElement(react_select_1.default, { options: options, isClearable: true, isMulti: true, onChange: (opt) => {
173
+ props.onChange(opt && opt.length > 0 ? lodash_1.default.pluck(opt, "value") : null);
174
+ }, value: value, styles: {
175
+ // Keep menu above modal
176
+ menu: (style) => ({ ...style, zIndex: 2000 })
177
+ }, ref: (node) => {
178
+ if (node) {
179
+ setTimeout(() => {
180
+ if (node) {
181
+ node.focus();
182
+ }
183
+ }, 0);
184
+ }
185
+ } }));
186
+ };
187
+ /** Editor for a boolean value */
188
+ const BooleanEditor = (props) => {
189
+ const options = [
190
+ { label: T `True`, value: true },
191
+ { label: T `False`, value: false }
192
+ ];
193
+ return (react_1.default.createElement(react_select_1.default, { options: options, isClearable: true, onChange: (opt) => props.onChange(opt ? opt.value : null), value: options.find((opt) => opt.value == props.value) || null, styles: {
194
+ // Keep menu above modal
195
+ menu: (style) => ({ ...style, zIndex: 2000 })
196
+ }, ref: (node) => {
197
+ if (node) {
198
+ setTimeout(() => {
199
+ if (node) {
200
+ node.focus();
201
+ }
202
+ }, 0);
203
+ }
204
+ } }));
205
+ };
206
+ /** Editor for a date */
207
+ const DateEditor = (props) => {
208
+ const momentDate = props.value ? (0, moment_1.default)(props.value, "YYYY-MM-DD") : null;
209
+ const handleChange = (0, react_1.useCallback)((value) => {
210
+ props.onChange(value ? value.format("YYYY-MM-DD") : null);
211
+ }, [props.onChange]);
212
+ return (react_1.default.createElement("div", { style: { padding: 2 } },
213
+ react_1.default.createElement(react_datepicker_1.default, { isClearable: true, selected: momentDate, onChange: handleChange, dateFormat: "YYYY-MM-DD", className: "form-control", locale: props.locale })));
214
+ };
215
+ /** Editor for a date */
216
+ const DatetimeEditor = (props) => {
217
+ const momentDate = props.value ? (0, moment_1.default)(props.value, moment_1.default.ISO_8601) : null;
218
+ const handleChange = (0, react_1.useCallback)((value) => {
219
+ props.onChange(value ? value.toISOString() : null);
220
+ }, [props.onChange]);
221
+ return (react_1.default.createElement("div", { style: { padding: 2 } },
222
+ react_1.default.createElement(react_datepicker_1.default, { fixedHeight: true, isClearable: true, selected: momentDate, onChange: handleChange, dateFormat: "YYYY-MM-DD HH:mm", className: "form-control", showTimeSelect: true, timeFormat: "HH:mm", locale: props.locale })));
223
+ };