@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
@@ -0,0 +1,281 @@
1
+ import React, { useState } from "react"
2
+ import _ from "lodash"
3
+ import * as ui from "@mwater/react-library/lib/bootstrap"
4
+ import Widget, { CreateViewElementOptions, TOCEntry } from "./Widget"
5
+ import DropdownWidgetComponent from "./DropdownWidgetComponent"
6
+ import ModalPopupComponent from "@mwater/react-library/lib/ModalPopupComponent"
7
+ import produce from "immer"
8
+ import { Schema } from "@mwater/expressions"
9
+
10
+ export interface TOCWidgetDesign {
11
+ /** Header text. Defaults to blank */
12
+ header?: string
13
+ /** Border weight. Defaults to 0=None. 1=light, 2=medium, 3=heavy */
14
+ borderWeight?: number
15
+ /** True/false for prepending numbering to entries (e.g. 3.4.1) */
16
+ numbering?: boolean
17
+ }
18
+
19
+ /**
20
+ * Table of contents widget that displays the h1, h2, etc entries from all text fields in one widget
21
+ */
22
+ export default class TOCWidget extends Widget {
23
+ /** Creates a React element that is a view of the widget */
24
+ createViewElement(options: CreateViewElementOptions) {
25
+ return <TOCWidgetComponent
26
+ design={options.design}
27
+ onDesignChange={options.onDesignChange ?? undefined}
28
+ width={options.width}
29
+ height={options.height}
30
+ tocEntries={options.tocEntries}
31
+ onScrollToTOCEntry={options.onScrollToTOCEntry}
32
+ translate={options.translate}
33
+ />
34
+ }
35
+
36
+ // Determine if widget is auto-height, which means that a vertical height is not required.
37
+ isAutoHeight() {
38
+ return true
39
+ }
40
+
41
+ getTranslatableStrings(design: TOCWidgetDesign, schema: Schema): string[] {
42
+ const strings: string[] = []
43
+ if (design.header) {
44
+ strings.push(design.header)
45
+ }
46
+ return strings
47
+ }
48
+ }
49
+
50
+ interface TOCWidgetComponentProps {
51
+ design: TOCWidgetDesign
52
+ /** Called with new design. null/undefined for readonly */
53
+ onDesignChange?: (design: TOCWidgetDesign) => void
54
+ width?: number
55
+ height?: number
56
+ tocEntries?: TOCEntry[]
57
+ onScrollToTOCEntry?: (widgetId: string, id: any) => void
58
+ translate: (input: string) => string
59
+ }
60
+
61
+ function TOCWidgetComponent(props: TOCWidgetComponentProps) {
62
+ const { design, onDesignChange, width, height, tocEntries, onScrollToTOCEntry, translate } = props
63
+ const [editing, setEditing] = useState(false)
64
+
65
+ const handleStartEditing = () => {
66
+ setEditing(true)
67
+ }
68
+
69
+ const handleEndEditing = () => {
70
+ setEditing(false)
71
+ }
72
+
73
+ const renderEditor = () => {
74
+ if (!editing) {
75
+ return null
76
+ }
77
+
78
+ // Create editor
79
+ const editor = <TOCWidgetDesignerComponent
80
+ design={design}
81
+ onDesignChange={onDesignChange!}
82
+ />
83
+
84
+ return <ModalPopupComponent
85
+ showCloseX={true}
86
+ header={T`Table of Contents Options`}
87
+ onClose={handleEndEditing}
88
+ >
89
+ {editor}
90
+ </ModalPopupComponent>
91
+ }
92
+
93
+ const renderContent = () => {
94
+ return <TOCWidgetViewComponent
95
+ design={design}
96
+ onDesignChange={onDesignChange}
97
+ width={width}
98
+ height={height}
99
+ tocEntries={tocEntries}
100
+ onScrollToTOCEntry={onScrollToTOCEntry}
101
+ translate={translate}
102
+ />
103
+ }
104
+
105
+ const dropdownItems = []
106
+ if (onDesignChange != null) {
107
+ dropdownItems.push({ label: T`Edit`, icon: "pencil", onClick: handleStartEditing })
108
+ }
109
+
110
+ // Wrap in a simple widget
111
+ return <div onDoubleClick={handleStartEditing}>
112
+ {onDesignChange != null ? renderEditor() : undefined}
113
+ <DropdownWidgetComponent
114
+ width={width}
115
+ height={height}
116
+ dropdownItems={dropdownItems}
117
+ >
118
+ {renderContent()}
119
+ </DropdownWidgetComponent>
120
+ </div>
121
+ }
122
+
123
+ interface TOCWidgetViewComponentProps {
124
+ design: TOCWidgetDesign
125
+ onDesignChange?: (design: TOCWidgetDesign) => void
126
+ width?: number
127
+ height?: number
128
+ tocEntries?: TOCEntry[]
129
+ onScrollToTOCEntry?: (widgetId: string, id: any) => void
130
+ translate: (input: string) => string
131
+ }
132
+
133
+ // Displays the contents of the widget
134
+ function TOCWidgetViewComponent(props: TOCWidgetViewComponentProps) {
135
+ const { design, onDesignChange, width, height, tocEntries, onScrollToTOCEntry, translate } = props
136
+
137
+ const handleEntryClick = (tocEntry: TOCEntry) => {
138
+ return onScrollToTOCEntry?.(tocEntry.widgetId, tocEntry.id)
139
+ }
140
+
141
+ const renderTOCEntry = (tocEntry: TOCEntry, index: number) => {
142
+ // Find indentation number (e.g "1.3.2") by counting # backwards that are same level with no level lower
143
+ let level
144
+ let indentation = ""
145
+ if (design.numbering) {
146
+ // For each level up to the current entry's level
147
+ for (let level = 1; level <= tocEntry.level; level++) {
148
+ let value = 0
149
+ // Count entries at this level up to current index
150
+ for (let i = 0; i <= index; i++) {
151
+ const entry = tocEntries?.[i]
152
+ if (!entry) {
153
+ continue
154
+ }
155
+ if (entry.level === level) {
156
+ value++
157
+ } else if (entry.level < level) {
158
+ // Reset counter when we hit a higher level
159
+ value = 0
160
+ }
161
+ }
162
+ indentation += `${value}.`
163
+ }
164
+ indentation += " "
165
+ }
166
+
167
+ return <div key={index} style={{ paddingLeft: tocEntry.level * 8 - 8 }}>
168
+ <a className="link-plain" onClick={() => handleEntryClick(tocEntry)}>
169
+ {indentation}
170
+ <span>{translate(tocEntry.text)}</span>
171
+ </a>
172
+ </div>
173
+ }
174
+
175
+ // Get border
176
+ const border = (() => {
177
+ switch (design.borderWeight) {
178
+ case 0:
179
+ return "none"
180
+ case 1:
181
+ return "solid 1px #f4f4f4"
182
+ case 2:
183
+ return "solid 1px #ccc"
184
+ case 3:
185
+ return "solid 1px #888"
186
+ }
187
+ })()
188
+
189
+ return <div
190
+ style={{
191
+ width,
192
+ height,
193
+ border,
194
+ padding: 5,
195
+ margin: 1
196
+ }}
197
+ >
198
+ {/* Render header */}
199
+ <div style={{ fontWeight: "bold" }}>{design.header ? props.translate(design.header) : ""}</div>
200
+ {_.map(tocEntries || [], (tocEntry, i) => {
201
+ return renderTOCEntry(tocEntry, i)
202
+ })}
203
+
204
+ {/* Add placeholder if none and editable */}
205
+ {onDesignChange && (tocEntries || []).length === 0
206
+ ? <div className="text-muted">
207
+ {T`Table of Contents will appear here as text blocks with headings are added to the dashboard`}
208
+ </div>
209
+ : undefined}
210
+ </div>
211
+ }
212
+
213
+ interface TOCWidgetDesignerProps {
214
+ design: TOCWidgetDesign
215
+ onDesignChange: (design: TOCWidgetDesign) => void
216
+ }
217
+
218
+ // Designer for TOC widget options
219
+ function TOCWidgetDesignerComponent(props: TOCWidgetDesignerProps) {
220
+ const { design, onDesignChange } = props
221
+
222
+ // Updates design with the specified changes
223
+ const handleUpdate = (field: keyof TOCWidgetDesign) => (value: any) => {
224
+ onDesignChange(produce(design, draft => {
225
+ draft[field] = value
226
+ }))
227
+ }
228
+
229
+ return <div>
230
+ <ui.FormGroup label={T`Header`}>
231
+ <ui.TextInput
232
+ value={design.header || ""}
233
+ onChange={handleUpdate("header")}
234
+ placeholder={T`None`}
235
+ />
236
+ </ui.FormGroup>
237
+ <ui.FormGroup label={T`Border`}>
238
+ <BorderComponent
239
+ value={design.borderWeight || 0}
240
+ onChange={handleUpdate("borderWeight")}
241
+ />
242
+ </ui.FormGroup>
243
+ <ui.FormGroup label={T`Numbering`}>
244
+ <ui.Radio
245
+ inline={true}
246
+ value={design.numbering || false}
247
+ radioValue={true}
248
+ onChange={handleUpdate("numbering")}
249
+ >
250
+ {T`On`}
251
+ </ui.Radio>
252
+ <ui.Radio
253
+ inline={true}
254
+ value={design.numbering || false}
255
+ radioValue={false}
256
+ onChange={handleUpdate("numbering")}
257
+ >
258
+ {T`Off`}
259
+ </ui.Radio>
260
+ </ui.FormGroup>
261
+ </div>
262
+ }
263
+
264
+ interface BorderComponentProps {
265
+ value?: number
266
+ defaultValue?: number
267
+ onChange: (value: number) => void
268
+ }
269
+
270
+ // Allows setting border heaviness
271
+ function BorderComponent(props: BorderComponentProps) {
272
+ const { value, defaultValue, onChange } = props
273
+ const currentValue = value != null ? value : defaultValue
274
+
275
+ return <div>
276
+ <ui.Radio inline={true} value={currentValue} radioValue={0} onChange={onChange}>{T`None`}</ui.Radio>
277
+ <ui.Radio inline={true} value={currentValue} radioValue={1} onChange={onChange}>{T`Light`}</ui.Radio>
278
+ <ui.Radio inline={true} value={currentValue} radioValue={2} onChange={onChange}>{T`Medium`}</ui.Radio>
279
+ <ui.Radio inline={true} value={currentValue} radioValue={3} onChange={onChange}>{T`Heavy`}</ui.Radio>
280
+ </div>
281
+ }
@@ -10,16 +10,16 @@ export interface CreateViewElementOptions {
10
10
  dataSource: DataSource
11
11
  /** Gives data to the widget in a way that allows client-server separation and secure sharing. See definition in WidgetDataSource. **/
12
12
  widgetDataSource: WidgetDataSource
13
- /** widget design **/
14
- design: object
13
+ /** widget design */
14
+ design: any
15
15
  /** scope of the widget (when the widget self-selects a particular scope) **/
16
16
  scope?: WidgetScope | null
17
17
  /** array of filters to apply.**/
18
18
  filters: JsonQLFilter[]
19
19
  /** called with scope of widget **/
20
- onScopeChange: (scope: WidgetScope | null) => void
20
+ onScopeChange?: (scope: WidgetScope | null) => void
21
21
  /** called with new design. null/undefined for readonly **/
22
- onDesignChange?: { (design: object): void } | null
22
+ onDesignChange?: ((design: any) => void) | null
23
23
  /** width in pixels on screen **/
24
24
  width?: number
25
25
  /** height in pixels on screen **/
@@ -32,7 +32,7 @@ export interface CreateViewElementOptions {
32
32
  namedStrings?: { [key: string]: string }
33
33
 
34
34
  /** Entries in the table of content */
35
- tocEntries?: string[]
35
+ tocEntries?: TOCEntry[]
36
36
 
37
37
  /** the widget callback ref */
38
38
  widgetRef?: (widget: any) => void
@@ -42,8 +42,23 @@ export interface CreateViewElementOptions {
42
42
 
43
43
  /** A key that changes when the widget should be refreshed */
44
44
  refreshKey?: any
45
+
46
+ /** Locale to use for display */
47
+ locale: string
48
+
49
+ /** Translate function to use for display. Returns same string when editing. */
50
+ translate: (input: string) => string
51
+ }
52
+
53
+ /** An entry in the table of contents */
54
+ export interface TOCEntry {
55
+ id: any
56
+ widgetId: string
57
+ level: number
58
+ text: string
45
59
  }
46
60
 
61
+
47
62
  export default class Widget {
48
63
  /** Creates a React element that is a view of the widget */
49
64
  createViewElement(options: CreateViewElementOptions): ReactElement<any> {
@@ -83,4 +98,9 @@ export default class Widget {
83
98
  getTOCEntries(design: any, namedStrings?: { [key: string]: string }): { id: string; text: string; level: number }[] {
84
99
  return []
85
100
  }
101
+
102
+ /** Gets any strings that should be translated */
103
+ getTranslatableStrings(design: any, schema: Schema): string[] {
104
+ return []
105
+ }
86
106
  }
@@ -47,7 +47,8 @@ export default class WidgetScopesViewComponent extends React.Component<WidgetSco
47
47
  "div",
48
48
  { className: "alert alert-info" },
49
49
  R("span", { className: "fas fa-filter" }),
50
- T` Filters: `,
50
+ " ",
51
+ T`Filters: `,
51
52
  _.map(_.keys(scopes), (id) => this.renderScope(id, scopes[id]))
52
53
  )
53
54
  }
@@ -11,7 +11,7 @@ export interface ChartCreateViewElementOptions {
11
11
  /** data source to use. Only used when designing, for display uses data **/
12
12
  dataSource: DataSource
13
13
 
14
- /** Chart design **/
14
+ /** Chart design. Should already be translated. */
15
15
  design: any
16
16
 
17
17
  /** called with new design. null/undefined for readonly **/
@@ -37,6 +37,9 @@ export interface ChartCreateViewElementOptions {
37
37
 
38
38
  /** Called with (tableId, rowId) when item is clicked **/
39
39
  onRowClick?: (tableId: string, rowId: any) => void
40
+
41
+ /** Locale to use for display */
42
+ locale: string
40
43
  }
41
44
 
42
45
  export default class Chart {
@@ -98,17 +101,9 @@ export default class Chart {
98
101
  throw new Error("Not implemented")
99
102
  }
100
103
 
101
- // Create a view element for the chart
102
- // Options include:
103
- // schema: schema to use
104
- // dataSource: dataSource to use
105
- // design: design of the chart
106
- // onDesignChange: when design changes
107
- // data: results from queries
108
- // width, height: size of the chart view
109
- // scope: current scope of the view element
110
- // onScopeChange: called when scope changes with new scope
111
- // onRowClick: Called with (tableId, rowId) when item is clicked
104
+ /** Create a view element for the chart.
105
+ * Chart design should already be translated. This is handled by ChartViewComponent.
106
+ */
112
107
  createViewElement(options: ChartCreateViewElementOptions): ReactNode {
113
108
  throw new Error("Not implemented")
114
109
  }
@@ -137,4 +132,28 @@ export default class Chart {
137
132
  getPlaceholderIcon() {
138
133
  return ""
139
134
  }
135
+
136
+ /** Get a list of translatable strings in the design. No need to override in subclasses. Design should already be cleaned. */
137
+ getTranslatableStrings(design: any, schema: Schema): string[] {
138
+ const strings = new Set<string>()
139
+
140
+ // Create collector function that just saves strings and returns them unchanged
141
+ const collectStrings = (str: string) => {
142
+ // Only collect non-empty strings
143
+ if (str) {
144
+ strings.add(str)
145
+ }
146
+ return str
147
+ }
148
+
149
+ // Use existing translation function but with collector
150
+ this.translateDesign(design, collectStrings)
151
+
152
+ return Array.from(strings)
153
+ }
154
+
155
+ /** Translates the design. Override in subclasses. Design is already cleaned. */
156
+ translateDesign(design: any, translate: (input: string) => string): any {
157
+ return design
158
+ }
140
159
  }
@@ -38,6 +38,12 @@ export interface ChartViewComponentProps {
38
38
 
39
39
  /** A key that changes when the widget should be refreshed */
40
40
  refreshKey?: any
41
+
42
+ /** Locale to use for display */
43
+ locale: string
44
+
45
+ /** Translate function to use for display */
46
+ translate: (input: string) => string
41
47
  }
42
48
 
43
49
  interface ChartViewComponentState {
@@ -197,15 +203,18 @@ export default class ChartViewComponent extends React.Component<ChartViewCompone
197
203
  if (this.state.dataError) {
198
204
  return this.renderError()
199
205
  }
206
+
207
+ // Translate design if needed
208
+ const translatedDesign = this.props.onDesignChange || !this.props.translate || !this.state.validDesign ? this.state.validDesign : this.props.chart.translateDesign(this.state.validDesign, this.props.translate)
200
209
 
201
210
  return R(
202
211
  "div",
203
212
  { style },
204
- this.state.validDesign
213
+ translatedDesign
205
214
  ? this.props.chart.createViewElement({
206
215
  schema: this.props.schema,
207
216
  dataSource: this.props.dataSource,
208
- design: this.state.validDesign,
217
+ design: translatedDesign,
209
218
  onDesignChange: this.props.onDesignChange,
210
219
  data: this.state.data,
211
220
  scope: this.props.scope,
@@ -213,7 +222,8 @@ export default class ChartViewComponent extends React.Component<ChartViewCompone
213
222
  width: this.props.width,
214
223
  height: this.props.height,
215
224
  onRowClick: this.props.onRowClick,
216
- filters: this.props.filters
225
+ filters: this.props.filters,
226
+ locale: this.props.locale,
217
227
  })
218
228
  : undefined,
219
229
  this.state.dataLoading ? this.renderSpinner() : undefined
@@ -41,7 +41,9 @@ export default class ChartWidget extends Widget {
41
41
  width: options.width,
42
42
  height: options.height,
43
43
  onRowClick: options.onRowClick,
44
- refreshKey: options.refreshKey
44
+ refreshKey: options.refreshKey,
45
+ locale: options.locale,
46
+ translate: options.translate
45
47
  })
46
48
  }
47
49
 
@@ -70,4 +72,12 @@ export default class ChartWidget extends Widget {
70
72
  isAutoHeight() {
71
73
  return this.chart.isAutoHeight()
72
74
  }
75
+
76
+ /** Get a list of translatable strings in the design */
77
+ getTranslatableStrings(design: any, schema: Schema) {
78
+ // Clean design first
79
+ const cleanDesign = this.chart.cleanDesign(design, schema)
80
+
81
+ return this.chart.getTranslatableStrings(cleanDesign, schema)
82
+ }
73
83
  }
@@ -48,6 +48,12 @@ export interface ChartWidgetComponentProps {
48
48
 
49
49
  /** A key that changes when the widget should be refreshed */
50
50
  refreshKey?: any
51
+
52
+ /** Locale to use for display */
53
+ locale: string
54
+
55
+ /** Translate function to use for display */
56
+ translate: (input: string) => string
51
57
  }
52
58
 
53
59
  // Complete chart widget
@@ -134,6 +140,8 @@ export class ChartWidgetComponent extends React.PureComponent<ChartWidgetCompone
134
140
  onScopeChange={this.props.onScopeChange}
135
141
  onRowClick={this.props.onRowClick}
136
142
  refreshKey={this.props.refreshKey}
143
+ locale={this.props.locale}
144
+ translate={this.props.translate}
137
145
  />
138
146
  }
139
147
 
@@ -251,7 +259,7 @@ export class ChartWidgetComponent extends React.PureComponent<ChartWidgetCompone
251
259
  this.props.filters
252
260
  )
253
261
  if (!designError) {
254
- dropdownItems.push({ label: T`Export Data`, icon: "save-file", onClick: this.handleSaveCsvFile })
262
+ dropdownItems.push({ label: T`Export Data`, onClick: this.handleSaveCsvFile })
255
263
  }
256
264
  if (this.props.onDesignChange != null) {
257
265
  dropdownItems.unshift({
@@ -224,4 +224,33 @@ export default class CalendarChart extends Chart {
224
224
  getPlaceholderIcon() {
225
225
  return "fa-calendar"
226
226
  }
227
+
228
+ translateDesign(design: CalendarChartDesign, translate: (input: string) => string) {
229
+ return produce(design, draft => {
230
+ // Translate title text
231
+ if (draft.titleText) {
232
+ draft.titleText = translate(draft.titleText)
233
+ }
234
+
235
+ // Translate axis category labels
236
+ if (draft.dateAxis?.categoryLabels) {
237
+ for (const key in draft.dateAxis.categoryLabels) {
238
+ draft.dateAxis.categoryLabels[key] = translate(draft.dateAxis.categoryLabels[key])
239
+ }
240
+ }
241
+ if (draft.valueAxis?.categoryLabels) {
242
+ for (const key in draft.valueAxis.categoryLabels) {
243
+ draft.valueAxis.categoryLabels[key] = translate(draft.valueAxis.categoryLabels[key])
244
+ }
245
+ }
246
+
247
+ // Translate null labels
248
+ if (draft.dateAxis?.nullLabel) {
249
+ draft.dateAxis.nullLabel = translate(draft.dateAxis.nullLabel)
250
+ }
251
+ if (draft.valueAxis?.nullLabel) {
252
+ draft.valueAxis.nullLabel = translate(draft.valueAxis.nullLabel)
253
+ }
254
+ })
255
+ }
227
256
  }
@@ -113,7 +113,9 @@ export default class CalendarChartViewComponent extends React.Component<Calendar
113
113
  const scopeData: WidgetScope = {
114
114
  name:
115
115
  this.axisBuilder.summarizeAxis(this.props.design.dateAxis!, this.context) +
116
- T` is ` +
116
+ " " +
117
+ T`is` +
118
+ " " +
117
119
  this.axisBuilder.formatValue(this.props.design.dateAxis!, data, this.context),
118
120
  filter: {
119
121
  jsonql: this.axisBuilder.createValueFilter(this.props.design.dateAxis!, data),
@@ -227,4 +227,13 @@ export default class ImageMosaicChart extends Chart {
227
227
  getPlaceholderIcon() {
228
228
  return "fa-th"
229
229
  }
230
+
231
+ translateDesign(design: ImageMosaicChartDesign, translate: (input: string) => string) {
232
+ return produce(design, draft => {
233
+ // Translate title text
234
+ if (draft.titleText) {
235
+ draft.titleText = translate(draft.titleText)
236
+ }
237
+ })
238
+ }
230
239
  }
@@ -1,3 +1,4 @@
1
+
1
2
  import _ from "lodash"
2
3
  import React from "react"
3
4
  import async from "async"
@@ -16,8 +17,8 @@ import { JsonQLFilter } from "../../.."
16
17
  import { JsonQLQuery } from "@mwater/jsonql"
17
18
  import LayeredChartViewComponent from "./LayeredChartViewComponent"
18
19
  import LayeredChartDesignerComponent from "./LayeredChartDesignerComponent"
20
+ import { translateHtmlItems } from "../../../richtext/ExprItemsTranslator"
19
21
 
20
- // See LayeredChart Design.md for the design
21
22
  export default class LayeredChart extends Chart {
22
23
  cleanDesign(design: LayeredChartDesign, schema: Schema): LayeredChartDesign {
23
24
  const exprCleaner = new ExprCleaner(schema)
@@ -130,7 +131,7 @@ export default class LayeredChart extends Chart {
130
131
  return T`Missing Y Axis`
131
132
  }
132
133
 
133
- if (!layer.axes.x && compiler.isXAxisRequired(design, layerId)) {
134
+ if (!layer.axes.x && compiler.isXAxisRequired(design)) {
134
135
  return T`Missing X Axis`
135
136
  }
136
137
  if (!layer.axes.color && compiler.isColorAxisRequired(design, layerId)) {
@@ -246,7 +247,9 @@ export default class LayeredChart extends Chart {
246
247
  height: options.height,
247
248
 
248
249
  scope: options.scope,
249
- onScopeChange: options.onScopeChange
250
+ onScopeChange: options.onScopeChange,
251
+
252
+ locale: options.locale,
250
253
  }
251
254
 
252
255
  return React.createElement(LayeredChartViewComponent, props)
@@ -356,4 +359,69 @@ export default class LayeredChart extends Chart {
356
359
  getPlaceholderIcon() {
357
360
  return "fa-bar-chart"
358
361
  }
362
+
363
+ /** Translates the design */
364
+ translateDesign(design: LayeredChartDesign, translate: (input: string) => string) {
365
+ return produce(design, draft => {
366
+ // Translate axis labels
367
+ if (draft.xAxisLabelText) {
368
+ draft.xAxisLabelText = translate(draft.xAxisLabelText)
369
+ }
370
+ if (draft.yAxisLabelText) {
371
+ draft.yAxisLabelText = translate(draft.yAxisLabelText)
372
+ }
373
+
374
+ // Translate layer names
375
+ for (const layer of draft.layers) {
376
+ if (layer.name) {
377
+ layer.name = translate(layer.name)
378
+ }
379
+
380
+ // Translate axis category labels
381
+ if (layer.axes.x?.categoryLabels) {
382
+ for (const key in layer.axes.x.categoryLabels) {
383
+ layer.axes.x.categoryLabels[key] = translate(layer.axes.x.categoryLabels[key])
384
+ }
385
+ }
386
+ if (layer.axes.y?.categoryLabels) {
387
+ for (const key in layer.axes.y.categoryLabels) {
388
+ layer.axes.y.categoryLabels[key] = translate(layer.axes.y.categoryLabels[key])
389
+ }
390
+ }
391
+ if (layer.axes.color?.categoryLabels) {
392
+ for (const key in layer.axes.color.categoryLabels) {
393
+ layer.axes.color.categoryLabels[key] = translate(layer.axes.color.categoryLabels[key])
394
+ }
395
+ }
396
+
397
+ // Translate null labels
398
+ if (layer.axes.x?.nullLabel) {
399
+ layer.axes.x.nullLabel = translate(layer.axes.x.nullLabel)
400
+ }
401
+ if (layer.axes.y?.nullLabel) {
402
+ layer.axes.y.nullLabel = translate(layer.axes.y.nullLabel)
403
+ }
404
+ if (layer.axes.color?.nullLabel) {
405
+ layer.axes.color.nullLabel = translate(layer.axes.color.nullLabel)
406
+ }
407
+ }
408
+
409
+ // Translate y thresholds
410
+ if (draft.yThresholds) {
411
+ for (const threshold of draft.yThresholds) {
412
+ if (threshold.label) {
413
+ threshold.label = translate(threshold.label)
414
+ }
415
+ }
416
+ }
417
+
418
+ // Translate header and footer using ExprItemsTranslator
419
+ if (draft.header) {
420
+ draft.header.items = translateHtmlItems(draft.header.items || [], translate)
421
+ }
422
+ if (draft.footer) {
423
+ draft.footer.items = translateHtmlItems(draft.footer.items || [], translate)
424
+ }
425
+ })
426
+ }
359
427
  }