@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
@@ -1,9 +1,9 @@
1
1
  import _ from "lodash"
2
2
  import React from "react"
3
- const R = React.createElement
4
- import update from "update-object"
3
+ import uuid from "uuid"
4
+ import { produce } from "immer"
5
5
  import { ExprComponent } from "@mwater/expressions-ui"
6
- import { DataSource, ExprUtils, Schema } from "@mwater/expressions"
6
+ import { DataSource, Expr, ExprUtils, FieldExpr, Schema } from "@mwater/expressions"
7
7
  import * as ui from "@mwater/react-library/lib/bootstrap"
8
8
  import { ListEditorComponent } from "@mwater/react-library/lib/ListEditorComponent"
9
9
  import { Quickfilter } from "./Quickfilter"
@@ -21,21 +21,29 @@ export interface QuickfiltersDesignComponentProps {
21
21
 
22
22
  // Displays quick filters and allows their value to be modified
23
23
  export default class QuickfiltersDesignComponent extends React.Component<QuickfiltersDesignComponentProps> {
24
- handleDesignChange = (design: any) => {
25
- design = design.slice()
24
+ handleDesignChange = (design: Quickfilter[]) => {
25
+ // Use immer produce to create immutable update
26
+ const newDesign = produce(design, draft => {
27
+ // Add UUID if missing
28
+ for (let i = 0; i < draft.length; i++) {
29
+ if (!draft[i].id) {
30
+ draft[i].id = uuid.v4()
31
+ }
32
+ }
26
33
 
27
- // Update merged, clearing if not mergeable
28
- for (let index = 0, end = design.length, asc = 0 <= end; asc ? index < end : index > end; asc ? index++ : index--) {
29
- if (design[index].merged && !this.isMergeable(design, index)) {
30
- design[index] = _.extend({}, design[index], { merged: false })
34
+ // Update merged flag, clearing if not mergeable
35
+ for (let i = 0; i < draft.length; i++) {
36
+ if (draft[i].merged && !this.isMergeable(draft, i)) {
37
+ draft[i].merged = false
38
+ }
31
39
  }
32
- }
40
+ })
33
41
 
34
- return this.props.onDesignChange(design)
42
+ this.props.onDesignChange(newDesign)
35
43
  }
36
44
 
37
45
  // Determine if quickfilter at index is mergeable with previous (same type, id table and enum values)
38
- isMergeable(design: any, index: any) {
46
+ isMergeable(design: Quickfilter[], index: number) {
39
47
  if (index === 0) {
40
48
  return false
41
49
  }
@@ -73,54 +81,51 @@ export default class QuickfiltersDesignComponent extends React.Component<Quickfi
73
81
  return true
74
82
  }
75
83
 
76
- renderQuickfilter = (item: any, index: any) => {
77
- return R(QuickfilterDesignComponent, {
78
- key: index,
79
- design: item,
80
- schema: this.props.schema,
81
- dataSource: this.props.dataSource,
82
- tables: this.props.tables,
83
- mergeable: this.isMergeable(this.props.design, index),
84
- onChange: (newItem: any) => {
84
+ renderQuickfilter = (item: Quickfilter, index: number) => {
85
+ return <QuickfilterDesignComponent
86
+ key={index}
87
+ design={item}
88
+ schema={this.props.schema}
89
+ dataSource={this.props.dataSource}
90
+ tables={this.props.tables}
91
+ mergeable={this.isMergeable(this.props.design, index)}
92
+ onChange={(newItem: Quickfilter) => {
85
93
  const design = this.props.design.slice()
86
94
  design[index] = newItem
87
95
  return this.handleDesignChange(design)
88
- },
89
-
90
- onRemove: this.handleRemove.bind(null, index)
91
- })
96
+ }}
97
+ onRemove={this.handleRemove.bind(null, index)}
98
+ />
92
99
  }
93
100
 
94
101
  handleAdd = () => {
95
102
  // Add blank to end
96
103
  const design = this.props.design.concat([{ expr: null }])
97
- return this.props.onDesignChange(design)
104
+ this.props.onDesignChange(design)
98
105
  }
99
106
 
100
- handleRemove = (index: any) => {
107
+ handleRemove = (index: number) => {
101
108
  const design = this.props.design.slice()
102
109
  design.splice(index, 1)
103
- return this.props.onDesignChange(design)
110
+ this.props.onDesignChange(design)
104
111
  }
105
112
 
106
113
  render() {
107
- return R(
108
- "div",
109
- null,
110
- <ListEditorComponent
111
- items={this.props.design}
112
- onItemsChange={this.handleDesignChange}
113
- renderItem={this.renderQuickfilter}
114
- getReorderableKey={(item, index) => index}
115
- />,
116
- this.props.tables.length > 0
117
- ? R(
118
- "button",
119
- { type: "button", className: "btn btn-sm btn-link", onClick: this.handleAdd },
120
- R("span", { className: "fas fa-plus" }),
121
- T`Add Quick Filter`
122
- )
123
- : undefined
114
+ return (
115
+ <div>
116
+ <ListEditorComponent
117
+ items={this.props.design}
118
+ onItemsChange={this.handleDesignChange}
119
+ renderItem={this.renderQuickfilter}
120
+ getReorderableKey={(item, index) => item.id || index}
121
+ />
122
+ {this.props.tables.length > 0 ? (
123
+ <button type="button" className="btn btn-sm btn-link" onClick={this.handleAdd}>
124
+ <span className="fas fa-plus me-1" />
125
+ {T`Add Quick Filter`}
126
+ </button>
127
+ ) : undefined}
128
+ </div>
124
129
  )
125
130
  }
126
131
  }
@@ -138,7 +143,7 @@ interface QuickfilterDesignComponentProps {
138
143
  }
139
144
 
140
145
  interface QuickfilterDesignComponentState {
141
- table: any
146
+ table: string
142
147
  }
143
148
 
144
149
  /** Single quickfilter design component */
@@ -146,16 +151,16 @@ class QuickfilterDesignComponent extends React.Component<
146
151
  QuickfilterDesignComponentProps,
147
152
  QuickfilterDesignComponentState
148
153
  > {
149
- constructor(props: any) {
154
+ constructor(props: QuickfilterDesignComponentProps) {
150
155
  super(props)
151
156
 
152
157
  // Store table to allow selecting table first, then expression
153
158
  this.state = {
154
- table: props.design.expr?.table || props.tables[0]
159
+ table: (props.design.expr as FieldExpr)?.table || props.tables[0]
155
160
  }
156
161
  }
157
162
 
158
- handleTableChange = (table: any) => {
163
+ handleTableChange = (table: string) => {
159
164
  this.setState({ table })
160
165
  const design = {
161
166
  expr: null
@@ -163,95 +168,89 @@ class QuickfilterDesignComponent extends React.Component<
163
168
  return this.props.onChange(design)
164
169
  }
165
170
 
166
- handleExprChange = (expr: any) => {
167
- return this.props.onChange(update(this.props.design, { expr: { $set: expr } }))
171
+ handleExprChange = (expr: Expr) => {
172
+ this.props.onChange(produce(this.props.design, draft => {
173
+ draft.expr = expr
174
+ }))
168
175
  }
176
+
169
177
  handleLabelChange = (ev: any) => {
170
- return this.props.onChange(update(this.props.design, { label: { $set: ev.target.value } }))
178
+ this.props.onChange(produce(this.props.design, draft => {
179
+ draft.label = ev.target.value
180
+ }))
171
181
  }
172
- handleMergedChange = (merged: any) => {
173
- return this.props.onChange(update(this.props.design, { merged: { $set: merged } }))
182
+
183
+ handleMergedChange = (merged: boolean) => {
184
+ this.props.onChange(produce(this.props.design, draft => {
185
+ draft.merged = merged
186
+ }))
174
187
  }
175
- handleMultiChange = (multi: any) => {
176
- return this.props.onChange(update(this.props.design, { multi: { $set: multi } }))
188
+
189
+ handleMultiChange = (multi: boolean) => {
190
+ this.props.onChange(produce(this.props.design, draft => {
191
+ draft.multi = multi
192
+ }))
177
193
  }
178
194
 
179
195
  render() {
180
196
  // Determine type of expression
181
197
  const exprType = new ExprUtils(this.props.schema).getExprType(this.props.design.expr)
182
198
 
183
- return R(
184
- "div", {},
185
- R(
186
- "div",
187
- { className: "mb-3 mt-1", key: "table" },
188
- R("label", { className: "text-muted" }, T`Data Source`),
189
- R(ui.Select, {
190
- value: this.state.table,
191
- options: _.map(this.props.tables, (table) => ({
192
- value: table,
193
- label: ExprUtils.localizeString(this.props.schema.getTable(table)!.name)
194
- })),
195
- onChange: this.handleTableChange,
196
- nullLabel: T`Select...`
197
- })
198
- ),
199
-
200
- R(
201
- "div",
202
- { className: "mb-3", key: "expr" },
203
- R("label", { className: "text-muted" }, T`Filter By`),
204
- R(
205
- "div",
206
- null,
207
- R(ExprComponent, {
208
- schema: this.props.schema,
209
- dataSource: this.props.dataSource,
210
- table: this.state.table,
211
- value: this.props.design.expr,
212
- onChange: this.handleExprChange,
213
- types: ["enum", "text", "enumset", "date", "datetime", "id[]", "text[]"],
214
- })
215
- )
216
- ),
217
-
218
- this.props.design.expr
219
- ? R(
220
- "div",
221
- { className: "mb-3", key: "label" },
222
- R("label", { className: "text-muted" }, T`Label`),
223
- R("input", {
224
- type: "text",
225
- className: "form-control form-control-sm",
226
- value: this.props.design.label || "",
227
- onChange: this.handleLabelChange,
228
- placeholder: T`Optional Label`
229
- })
230
- )
231
- : undefined,
232
-
233
- this.props.mergeable
234
- ? R(
235
- ui.Checkbox,
236
- {
237
- value: this.props.design.merged,
238
- onChange: this.handleMergedChange
239
- },
240
- T`Merge with previous quickfilter `,
241
- R("span", { className: "text-muted" }, T`- displays as one single control that filters both`)
242
- )
243
- : undefined,
244
-
245
- ["enum", "text", "enumset", "id[]", "text[]"].includes(exprType!)
246
- ? R(
247
- ui.Checkbox,
248
- {
249
- value: this.props.design.multi,
250
- onChange: this.handleMultiChange
251
- },
252
- T`Allow multiple selections`
253
- )
254
- : undefined
199
+ return (
200
+ <div>
201
+ <div className="mb-3 mt-1" key="table">
202
+ <label className="text-muted">{T`Data Source`}</label>
203
+ <ui.Select
204
+ value={this.state.table}
205
+ options={_.map(this.props.tables, (table) => ({
206
+ value: table,
207
+ label: ExprUtils.localizeString(this.props.schema.getTable(table)!.name)
208
+ }))}
209
+ onChange={this.handleTableChange}
210
+ nullLabel={T`Select...`}
211
+ />
212
+ </div>
213
+
214
+ <div className="mb-3" key="expr">
215
+ <label className="text-muted">{T`Filter By`}</label>
216
+ <div>
217
+ <ExprComponent
218
+ schema={this.props.schema}
219
+ dataSource={this.props.dataSource}
220
+ table={this.state.table}
221
+ value={this.props.design.expr}
222
+ onChange={this.handleExprChange}
223
+ types={["enum", "text", "enumset", "date", "datetime", "id[]", "text[]"]}
224
+ />
225
+ </div>
226
+ </div>
227
+
228
+ {this.props.design.expr ? (
229
+ <div className="mb-3" key="label">
230
+ <label className="text-muted">{T`Label`}</label>
231
+ <input
232
+ type="text"
233
+ className="form-control form-control-sm"
234
+ value={this.props.design.label || ""}
235
+ onChange={this.handleLabelChange}
236
+ placeholder={T`Optional Label`}
237
+ />
238
+ </div>
239
+ ) : undefined}
240
+
241
+ {this.props.mergeable ? (
242
+ <ui.Checkbox value={this.props.design.merged} onChange={this.handleMergedChange}>
243
+ {T`Merge with previous quickfilter `}
244
+ <span className="text-muted">{`- ${T`displays as one single control that filters both`}`}</span>
245
+ </ui.Checkbox>
246
+ ) : undefined}
247
+
248
+ {["enum", "text", "enumset", "id[]", "text[]"].includes(exprType!) ? (
249
+ <ui.Checkbox value={this.props.design.multi} onChange={this.handleMultiChange}>
250
+ {T`Allow multiple selections`}
251
+ </ui.Checkbox>
252
+ ) : undefined}
253
+ </div>
255
254
  )
256
255
  }
257
256
  }
@@ -1,10 +1,11 @@
1
1
  import _ from "lodash"
2
- import ItemsHtmlConverter, { HtmlItemBase } from "./ItemsHtmlConverter"
2
+ import ItemsHtmlConverter, { HtmlItem, HtmlItemBase } from "./ItemsHtmlConverter"
3
3
  import { Expr, ExprUtils, Schema } from "@mwater/expressions"
4
4
  import uuid from "uuid"
5
5
  import { formatValue } from "../valueFormatter"
6
6
  import { canFormatType } from "../valueFormatter"
7
7
 
8
+ /** Html item that is an expression */
8
9
  export interface HtmlItemExpr extends HtmlItemBase {
9
10
  type: "expr"
10
11
 
@@ -24,6 +25,9 @@ export interface HtmlItemExpr extends HtmlItemBase {
24
25
  format?: string
25
26
  }
26
27
 
28
+ /** Html item that is an expression or a normal item */
29
+ export type HtmlItemOrExpr = HtmlItem | HtmlItemExpr
30
+
27
31
  /**
28
32
  * ItemsHtmlConverter that supports embedded mwater expressions
29
33
  * Converts items (JSON contents of rich text) to HTML and back to allow editing
@@ -66,7 +70,7 @@ export default class ExprItemsHtmlConverter extends ItemsHtmlConverter {
66
70
  if (this.summarizeExprs) {
67
71
  text = new ExprUtils(this.schema).summarizeExpr(exprItem.expr, this.locale)
68
72
  if (text.length > 30) {
69
- text = text.substr(0, 30) + T`...`
73
+ text = text.substr(0, 30) + `...`
70
74
  }
71
75
 
72
76
  exprHtml = _.escape(text)
@@ -88,7 +92,7 @@ export default class ExprItemsHtmlConverter extends ItemsHtmlConverter {
88
92
 
89
93
  exprHtml = _.escape(text)
90
94
  } else {
91
- exprHtml = `<span style="color: #DDD">${T`---`}</span>`
95
+ exprHtml = `<span style="color: #DDD">${`---`}</span>`
92
96
  }
93
97
  } else {
94
98
  // Placeholder
@@ -97,14 +101,14 @@ export default class ExprItemsHtmlConverter extends ItemsHtmlConverter {
97
101
 
98
102
  // Add label
99
103
  if (exprItem.includeLabel) {
100
- const label = exprItem.labelText || new ExprUtils(this.schema).summarizeExpr(exprItem.expr, this.locale) + T`:\u00A0`
104
+ const label = exprItem.labelText || new ExprUtils(this.schema).summarizeExpr(exprItem.expr, this.locale) + `:\u00A0`
101
105
  exprHtml = `<span class="text-muted">${_.escape(label)}</span>` + exprHtml
102
106
  }
103
107
 
104
108
  if (this.designMode) {
105
109
  html +=
106
110
  `\u2060<span data-embed="${_.escape(JSON.stringify(item))}" class="mwater-visualization-text-widget-expr">` +
107
- (exprHtml || T`\u00A0`) +
111
+ (exprHtml || `\u00A0`) +
108
112
  `</span>\u2060`
109
113
  } else {
110
114
  // View mode
@@ -0,0 +1,176 @@
1
+ import _ from "lodash"
2
+ import { HtmlItemExpr, HtmlItemOrExpr } from "./ExprItemsHtmlConverter"
3
+ import { produce } from "immer"
4
+ import ItemsHtmlConverter, { HtmlItem, HtmlItemBase, HtmlItemElement } from "./ItemsHtmlConverter"
5
+
6
+ /** Gets all unique strings from html items */
7
+ export function getHtmlItemsStrings(items: HtmlItemOrExpr[]): string[] {
8
+ const strings = new Set<string>()
9
+
10
+ // Create collector function that just saves strings and returns them unchanged
11
+ const collectStrings = (str: string) => {
12
+ // Only collect non-empty strings
13
+ if (str) {
14
+ strings.add(str)
15
+ }
16
+ return str
17
+ }
18
+
19
+ // Use existing translation function but with collector
20
+ translateHtmlItems(items, collectStrings)
21
+
22
+ return Array.from(strings)
23
+ }
24
+
25
+ /** Translates items in an html items list with expressions */
26
+ export function translateHtmlItems(items: HtmlItemOrExpr[], translate: (input: string) => string): HtmlItemOrExpr[] {
27
+ return produce(items, draft => {
28
+ function translateItems(itemList: HtmlItemOrExpr[]) {
29
+ // Accumulate simple items
30
+ let simpleItems: HtmlItemOrExpr[] = []
31
+ let simpleItemStartIndex: number | null = null
32
+
33
+ /** Process accumulated simple nodes which are simple inline elements */
34
+ function processSimpleNodes() {
35
+ if (simpleItems.length === 0) {
36
+ return
37
+ }
38
+
39
+ // Create html string with {0}, {1}, etc. for expressions and html for other items
40
+ const expressions: HtmlItemExpr[] = []
41
+
42
+ const converter = new TranslationItemsHtmlConverter(item => {
43
+ if (item.type === "expr") {
44
+ const expr = item as HtmlItemExpr
45
+ if (expr.labelText) {
46
+ expr.labelText = translate(expr.labelText)
47
+ }
48
+ expressions.push(expr)
49
+ return `{${expressions.length - 1}}`
50
+ }
51
+ return ""
52
+ })
53
+
54
+ // Convert items to html which has the side effect of translating expressions
55
+ let html = converter.convertItemsToHtml(simpleItems)
56
+
57
+ // If there was no text, do nothing except translate expressions
58
+ if (!simpleItems.some(doesItemContainText)) {
59
+ return
60
+ }
61
+
62
+ // Remove leading and trailing whitespace from the HTML
63
+ const leftWhitespace = html.slice(0, html.length - html.trimStart().length)
64
+ const rightWhitespace = html.slice(html.trimEnd().length)
65
+ html = html.trim()
66
+
67
+ if (!html) {
68
+ return
69
+ }
70
+
71
+ let translatedHtml = leftWhitespace + translate(html) + rightWhitespace
72
+
73
+ // Parse translatedHtml into items by first replacing {0}, {1}, etc. with html that can be converted to items
74
+ for (let i = 0; i < expressions.length; i++) {
75
+ const expr = expressions[i]
76
+ translatedHtml = translatedHtml.replace(new RegExp(`\\{${i}\\}`, "g"), `<span data-embed="${_.escape(JSON.stringify(expr))}"></span>`)
77
+ }
78
+
79
+ // Wrap in a div to prevent whitespace from being trimmed.
80
+ const translatedItems = (converter.convertElemToItems(new DOMParser().parseFromString("<div>" + translatedHtml + "</div>", "text/html").body.firstChild as HTMLElement))
81
+
82
+ // Replace the simple items with the translated items
83
+ itemList.splice(simpleItemStartIndex!, simpleItems.length, ...translatedItems)
84
+
85
+ simpleItems = []
86
+ simpleItemStartIndex = null
87
+ }
88
+
89
+ for (let i = 0; i < itemList.length; i++) {
90
+ const item = itemList[i]
91
+
92
+ // If the child is a simple node, add it to the list
93
+ if (isItemSimple(item)) {
94
+ simpleItems.push(item)
95
+ if (simpleItemStartIndex == null) {
96
+ simpleItemStartIndex = i
97
+ }
98
+ } else {
99
+ // If we have gathered simple nodes, we need to return/replace them
100
+ processSimpleNodes()
101
+ simpleItemStartIndex = null
102
+ simpleItems = []
103
+
104
+ // Handle the current node
105
+ if (typeof item !== "string" && item.type === "element" && item.items) {
106
+ translateItems(item.items)
107
+ }
108
+ }
109
+ }
110
+
111
+ if (simpleItems.length > 0) {
112
+ processSimpleNodes()
113
+ simpleItemStartIndex = null
114
+ simpleItems = []
115
+ }
116
+ }
117
+
118
+ translateItems(draft)
119
+ })
120
+ }
121
+
122
+ /** ItemsHtmlConverter that has a callback for handling special items */
123
+ class TranslationItemsHtmlConverter extends ItemsHtmlConverter {
124
+ handleSpecialItem: (item: HtmlItemBase) => string
125
+
126
+ constructor(handleSpecialItem: (item: HtmlItemBase) => string) {
127
+ super()
128
+ this.handleSpecialItem = handleSpecialItem
129
+ }
130
+
131
+ convertSpecialItemToHtml(item: HtmlItemBase) {
132
+ return this.handleSpecialItem(item)
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Check if an html item is simple.
138
+ * A node is simple if it is:
139
+ * - a string
140
+ * - a tag that is a "b", "i", "u", "a", "strong", "em" tag with no children or only simple children
141
+ * Only sequences of simple nodes are exported to translation entries as HTML.
142
+ */
143
+ function isItemSimple(node: HtmlItemOrExpr): boolean {
144
+ if (typeof node === "string") {
145
+ return true
146
+ }
147
+
148
+ if (node.type === "expr") {
149
+ return true
150
+ }
151
+
152
+ if (node.type === "element") {
153
+ const element = node as HtmlItemElement
154
+
155
+ if (["b", "i", "u", "a", "strong", "em"].indexOf(element.tag) === -1) {
156
+ return false
157
+ }
158
+
159
+ return (element.items || []).every(child => isItemSimple(child))
160
+ }
161
+
162
+ return false
163
+ }
164
+
165
+ /** Determines if an item contains any non-empty text strings */
166
+ function doesItemContainText(item: HtmlItemOrExpr): boolean {
167
+ if (typeof item === "string") {
168
+ return item.trim().length > 0
169
+ }
170
+
171
+ if (item.type === "element") {
172
+ return (item.items || []).some(child => doesItemContainText(child))
173
+ }
174
+
175
+ return false
176
+ }