@mwater/visualization 5.5.0 → 5.6.0

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 (222) hide show
  1. package/lib/MWaterContextComponent.d.ts +1 -1
  2. package/lib/MWaterGlobalFiltersComponent.d.ts +2 -2
  3. package/lib/MWaterGlobalFiltersComponent.js +11 -20
  4. package/lib/MWaterLoaderComponent.d.ts +4 -13
  5. package/lib/MWaterLoaderComponent.js +2 -11
  6. package/lib/UndoStack.d.ts +2 -1
  7. package/lib/UndoStack.js +12 -6
  8. package/lib/dashboards/DashboardComponent.js +5 -4
  9. package/lib/dashboards/DashboardDesign.d.ts +1 -1
  10. package/lib/dashboards/ServerDashboardDataSource.js +0 -10
  11. package/lib/dashboards/SettingsModalComponent.js +1 -1
  12. package/lib/datagrids/DatagridComponent.js +22 -2
  13. package/lib/datagrids/DatagridDesignerComponent.d.ts +2 -3
  14. package/lib/datagrids/DatagridDesignerComponent.js +108 -120
  15. package/lib/datagrids/DatagridViewComponent.js +3 -2
  16. package/lib/datagrids/OrderBysDesignerComponent.d.ts +7 -7
  17. package/lib/datagrids/OrderBysDesignerComponent.js +19 -28
  18. package/lib/index.css +45 -2
  19. package/lib/index.d.ts +5 -5
  20. package/lib/index.js +2 -3
  21. package/lib/layouts/blocks/BlocksDisplayComponent.d.ts +8 -1
  22. package/lib/layouts/blocks/BlocksDisplayComponent.js +46 -4
  23. package/lib/maps/BufferLayer.js +12 -0
  24. package/lib/maps/BufferLayerDesign.d.ts +1 -1
  25. package/lib/maps/BufferLayerDesignerComponent.js +2 -2
  26. package/lib/maps/ChoroplethLayer.js +12 -0
  27. package/lib/maps/ChoroplethLayerDesign.d.ts +5 -2
  28. package/lib/maps/ChoroplethLayerDesigner.d.ts +10 -32
  29. package/lib/maps/ChoroplethLayerDesigner.js +58 -89
  30. package/lib/maps/DirectMapDataSource.js +0 -10
  31. package/lib/maps/EditHoverOver.d.ts +4 -3
  32. package/lib/maps/EditHoverOver.js +3 -3
  33. package/lib/maps/HoverContent.js +1 -1
  34. package/lib/maps/LeafletMapComponent.js +10 -19
  35. package/lib/maps/MapComponent.js +0 -1
  36. package/lib/maps/MapUtils.js +10 -1
  37. package/lib/maps/MarkersLayer.js +18 -2
  38. package/lib/maps/MarkersLayerDesign.d.ts +1 -1
  39. package/lib/maps/MarkersLayerDesignerComponent.d.ts +12 -41
  40. package/lib/maps/MarkersLayerDesignerComponent.js +81 -111
  41. package/lib/maps/ServerMapDataSource.js +0 -10
  42. package/lib/maps/VectorMapViewComponent.js +1 -9
  43. package/lib/maps/symbols/font-awesome/asterisk.png +0 -0
  44. package/lib/maps/symbols/font-awesome/ban.png +0 -0
  45. package/lib/maps/symbols/font-awesome/beer.png +0 -0
  46. package/lib/maps/symbols/font-awesome/bell.png +0 -0
  47. package/lib/maps/symbols/font-awesome/bolt.png +0 -0
  48. package/lib/maps/symbols/font-awesome/building.png +0 -0
  49. package/lib/maps/symbols/font-awesome/bullseye.png +0 -0
  50. package/lib/maps/symbols/font-awesome/bus.png +0 -0
  51. package/lib/maps/symbols/font-awesome/caret-up.png +0 -0
  52. package/lib/maps/symbols/font-awesome/certificate.png +0 -0
  53. package/lib/maps/symbols/font-awesome/check-circle.png +0 -0
  54. package/lib/maps/symbols/font-awesome/check.png +0 -0
  55. package/lib/maps/symbols/font-awesome/chevron-circle-down.png +0 -0
  56. package/lib/maps/symbols/font-awesome/chevron-circle-up.png +0 -0
  57. package/lib/maps/symbols/font-awesome/cloud-rain.png +0 -0
  58. package/lib/maps/symbols/font-awesome/cloud.png +0 -0
  59. package/lib/maps/symbols/font-awesome/comment.png +0 -0
  60. package/lib/maps/symbols/font-awesome/crosshairs.png +0 -0
  61. package/lib/maps/symbols/font-awesome/dot-circle-o.png +0 -0
  62. package/lib/maps/symbols/font-awesome/exclamation-circle.png +0 -0
  63. package/lib/maps/symbols/font-awesome/exclamation-triangle.png +0 -0
  64. package/lib/maps/symbols/font-awesome/female.png +0 -0
  65. package/lib/maps/symbols/font-awesome/file.png +0 -0
  66. package/lib/maps/symbols/font-awesome/flag.png +0 -0
  67. package/lib/maps/symbols/font-awesome/flask.png +0 -0
  68. package/lib/maps/symbols/font-awesome/h-square.png +0 -0
  69. package/lib/maps/symbols/font-awesome/home.png +0 -0
  70. package/lib/maps/symbols/font-awesome/info-circle.png +0 -0
  71. package/lib/maps/symbols/font-awesome/male.png +0 -0
  72. package/lib/maps/symbols/font-awesome/medkit.png +0 -0
  73. package/lib/maps/symbols/font-awesome/mobile.png +0 -0
  74. package/lib/maps/symbols/font-awesome/plus-circle.png +0 -0
  75. package/lib/maps/symbols/font-awesome/plus-square.png +0 -0
  76. package/lib/maps/symbols/font-awesome/plus.png +0 -0
  77. package/lib/maps/symbols/font-awesome/square.png +0 -0
  78. package/lib/maps/symbols/font-awesome/star.png +0 -0
  79. package/lib/maps/symbols/font-awesome/thumbs-down.png +0 -0
  80. package/lib/maps/symbols/font-awesome/thumbs-up.png +0 -0
  81. package/lib/maps/symbols/font-awesome/ticket.png +0 -0
  82. package/lib/maps/symbols/font-awesome/times-circle.png +0 -0
  83. package/lib/maps/symbols/font-awesome/times.png +0 -0
  84. package/lib/maps/symbols/font-awesome/tint.png +0 -0
  85. package/lib/maps/symbols/font-awesome/tree.png +0 -0
  86. package/lib/maps/symbols/font-awesome/university.png +0 -0
  87. package/lib/maps/symbols/font-awesome/usd.png +0 -0
  88. package/lib/maps/symbols/font-awesome/user.png +0 -0
  89. package/lib/maps/symbols/font-awesome/users.png +0 -0
  90. package/lib/maps/symbols/font-awesome/wheelchair.png +0 -0
  91. package/lib/maps/symbols/sdf-ize.sh +93 -0
  92. package/lib/maps/vectorMaps.d.ts +1 -0
  93. package/lib/maps/vectorMaps.js +20 -36
  94. package/lib/mwater_table_selection/IndicatorsListComponent.d.ts +4 -2
  95. package/lib/mwater_table_selection/IndicatorsListComponent.js +103 -34
  96. package/lib/mwater_table_selection/MWaterCalculatedDataSourcesListComponent.d.ts +18 -0
  97. package/lib/mwater_table_selection/MWaterCalculatedDataSourcesListComponent.js +80 -0
  98. package/lib/mwater_table_selection/MWaterCompleteTableSelectComponent.d.ts +26 -0
  99. package/lib/mwater_table_selection/MWaterCompleteTableSelectComponent.js +237 -51
  100. package/lib/mwater_table_selection/MWaterTableSelectComponent.d.ts +2 -2
  101. package/lib/mwater_table_selection/MWaterTableSelectComponent.js +9 -4
  102. package/lib/mwater_table_selection/MWaterWorkflowsSelectComponent.d.ts +19 -0
  103. package/lib/mwater_table_selection/MWaterWorkflowsSelectComponent.js +111 -0
  104. package/lib/quickfilter/QuickfiltersComponent.d.ts +3 -102
  105. package/lib/quickfilter/QuickfiltersComponent.js +53 -110
  106. package/lib/quickfilter/TextLiteralComponent.d.ts +23 -47
  107. package/lib/quickfilter/TextLiteralComponent.js +85 -82
  108. package/lib/widgets/MapWidget.js +4 -2
  109. package/lib/widgets/text/ExprItemEditorComponent.d.ts +3 -8
  110. package/lib/widgets/text/ExprItemEditorComponent.js +36 -33
  111. package/lib/widgets/text/ExprUpdateModalComponent.d.ts +1 -0
  112. package/package.json +2 -3
  113. package/src/MWaterContextComponent.tsx +1 -1
  114. package/src/{MWaterGlobalFiltersComponent.ts → MWaterGlobalFiltersComponent.tsx} +32 -33
  115. package/src/{MWaterLoaderComponent.ts → MWaterLoaderComponent.tsx} +17 -18
  116. package/src/UndoStack.ts +14 -6
  117. package/src/dashboards/DashboardComponent.tsx +5 -4
  118. package/src/dashboards/DashboardDesign.ts +1 -1
  119. package/src/dashboards/ServerDashboardDataSource.ts +0 -12
  120. package/src/dashboards/SettingsModalComponent.tsx +1 -1
  121. package/src/datagrids/DatagridComponent.tsx +30 -2
  122. package/src/datagrids/DatagridDesignerComponent.tsx +241 -229
  123. package/src/datagrids/DatagridViewComponent.tsx +3 -2
  124. package/src/datagrids/OrderBysDesignerComponent.tsx +61 -70
  125. package/src/index.css +45 -2
  126. package/src/index.ts +5 -11
  127. package/src/layouts/blocks/BlocksDisplayComponent.tsx +60 -5
  128. package/src/maps/BufferLayer.ts +14 -1
  129. package/src/maps/BufferLayerDesign.ts +1 -1
  130. package/src/maps/BufferLayerDesignerComponent.tsx +2 -1
  131. package/src/maps/ChoroplethLayer.ts +20 -7
  132. package/src/maps/ChoroplethLayerDesign.ts +5 -2
  133. package/src/maps/ChoroplethLayerDesigner.tsx +169 -165
  134. package/src/maps/DirectMapDataSource.ts +0 -12
  135. package/src/maps/EditHoverOver.tsx +9 -5
  136. package/src/maps/HoverContent.tsx +1 -1
  137. package/src/maps/LeafletMapComponent.tsx +10 -19
  138. package/src/maps/MapComponent.ts +0 -1
  139. package/src/maps/MapUtils.ts +13 -1
  140. package/src/maps/MarkersLayer.ts +22 -5
  141. package/src/maps/MarkersLayerDesign.ts +1 -1
  142. package/src/maps/MarkersLayerDesignerComponent.tsx +360 -0
  143. package/src/maps/ServerMapDataSource.ts +0 -12
  144. package/src/maps/VectorMapViewComponent.tsx +2 -13
  145. package/src/maps/symbols/font-awesome/asterisk.png +0 -0
  146. package/src/maps/symbols/font-awesome/ban.png +0 -0
  147. package/src/maps/symbols/font-awesome/beer.png +0 -0
  148. package/src/maps/symbols/font-awesome/bell.png +0 -0
  149. package/src/maps/symbols/font-awesome/bolt.png +0 -0
  150. package/src/maps/symbols/font-awesome/building.png +0 -0
  151. package/src/maps/symbols/font-awesome/bullseye.png +0 -0
  152. package/src/maps/symbols/font-awesome/bus.png +0 -0
  153. package/src/maps/symbols/font-awesome/caret-up.png +0 -0
  154. package/src/maps/symbols/font-awesome/certificate.png +0 -0
  155. package/src/maps/symbols/font-awesome/check-circle.png +0 -0
  156. package/src/maps/symbols/font-awesome/check.png +0 -0
  157. package/src/maps/symbols/font-awesome/chevron-circle-down.png +0 -0
  158. package/src/maps/symbols/font-awesome/chevron-circle-up.png +0 -0
  159. package/src/maps/symbols/font-awesome/cloud-rain.png +0 -0
  160. package/src/maps/symbols/font-awesome/cloud.png +0 -0
  161. package/src/maps/symbols/font-awesome/comment.png +0 -0
  162. package/src/maps/symbols/font-awesome/crosshairs.png +0 -0
  163. package/src/maps/symbols/font-awesome/dot-circle-o.png +0 -0
  164. package/src/maps/symbols/font-awesome/exclamation-circle.png +0 -0
  165. package/src/maps/symbols/font-awesome/exclamation-triangle.png +0 -0
  166. package/src/maps/symbols/font-awesome/female.png +0 -0
  167. package/src/maps/symbols/font-awesome/file.png +0 -0
  168. package/src/maps/symbols/font-awesome/flag.png +0 -0
  169. package/src/maps/symbols/font-awesome/flask.png +0 -0
  170. package/src/maps/symbols/font-awesome/h-square.png +0 -0
  171. package/src/maps/symbols/font-awesome/home.png +0 -0
  172. package/src/maps/symbols/font-awesome/info-circle.png +0 -0
  173. package/src/maps/symbols/font-awesome/male.png +0 -0
  174. package/src/maps/symbols/font-awesome/medkit.png +0 -0
  175. package/src/maps/symbols/font-awesome/mobile.png +0 -0
  176. package/src/maps/symbols/font-awesome/plus-circle.png +0 -0
  177. package/src/maps/symbols/font-awesome/plus-square.png +0 -0
  178. package/src/maps/symbols/font-awesome/plus.png +0 -0
  179. package/src/maps/symbols/font-awesome/square.png +0 -0
  180. package/src/maps/symbols/font-awesome/star.png +0 -0
  181. package/src/maps/symbols/font-awesome/thumbs-down.png +0 -0
  182. package/src/maps/symbols/font-awesome/thumbs-up.png +0 -0
  183. package/src/maps/symbols/font-awesome/ticket.png +0 -0
  184. package/src/maps/symbols/font-awesome/times-circle.png +0 -0
  185. package/src/maps/symbols/font-awesome/times.png +0 -0
  186. package/src/maps/symbols/font-awesome/tint.png +0 -0
  187. package/src/maps/symbols/font-awesome/tree.png +0 -0
  188. package/src/maps/symbols/font-awesome/university.png +0 -0
  189. package/src/maps/symbols/font-awesome/usd.png +0 -0
  190. package/src/maps/symbols/font-awesome/user.png +0 -0
  191. package/src/maps/symbols/font-awesome/users.png +0 -0
  192. package/src/maps/symbols/font-awesome/wheelchair.png +0 -0
  193. package/src/maps/symbols/sdf-ize.sh +93 -0
  194. package/src/maps/vectorMaps.tsx +20 -44
  195. package/src/mwater_table_selection/IndicatorsListComponent.tsx +165 -37
  196. package/src/mwater_table_selection/MWaterCalculatedDataSourcesListComponent.tsx +111 -0
  197. package/src/mwater_table_selection/MWaterCompleteTableSelectComponent.tsx +373 -37
  198. package/src/mwater_table_selection/MWaterTableSelectComponent.tsx +12 -8
  199. package/src/mwater_table_selection/MWaterWorkflowsSelectComponent.tsx +159 -0
  200. package/src/quickfilter/{QuickfiltersComponent.ts → QuickfiltersComponent.tsx} +165 -158
  201. package/src/quickfilter/TextLiteralComponent.tsx +197 -0
  202. package/src/widgets/MapWidget.tsx +9 -1
  203. package/src/widgets/text/ExprItemEditorComponent.tsx +83 -77
  204. package/src/widgets/text/ExprUpdateModalComponent.tsx +1 -0
  205. package/test/UndoStackTests.ts +52 -1
  206. package/.storybook/config.js +0 -7
  207. package/.storybook/head.html +0 -3
  208. package/.storybook/webpack.config.js +0 -15
  209. package/src/maps/BingLayer.ts +0 -146
  210. package/src/maps/MarkersLayerDesignerComponent.ts +0 -374
  211. package/src/quickfilter/TextLiteralComponent.ts +0 -165
  212. package/stories/UpdateableComponent.js +0 -29
  213. package/stories/consoles.js +0 -202
  214. package/stories/dashboards.js +0 -217
  215. package/stories/datagridDesign.js +0 -114
  216. package/stories/datagrids.js +0 -69
  217. package/stories/dates.js +0 -80
  218. package/stories/exprcomponent.js +0 -43
  219. package/stories/index.js +0 -18
  220. package/stories/leaflet.js +0 -59
  221. package/stories/maps.js +0 -24
  222. package/stories/pivotChart.js +0 -235
@@ -1,8 +1,7 @@
1
1
  import _ from "lodash"
2
2
  import React from "react"
3
- const R = React.createElement
4
3
  import { default as ReactSelect } from "react-select"
5
- import { DataSource, ExprUtils, LocalizedString, Schema } from "@mwater/expressions"
4
+ import { DataSource, Expr, ExprUtils, LocalizedString, Schema } from "@mwater/expressions"
6
5
  import { ExprCleaner } from "@mwater/expressions"
7
6
  import TextLiteralComponent from "./TextLiteralComponent"
8
7
  import DateExprComponent from "./DateExprComponent"
@@ -45,7 +44,7 @@ export interface QuickfiltersComponentProps {
45
44
 
46
45
  /** Displays quick filters and allows their value to be modified */
47
46
  export default class QuickfiltersComponent extends React.Component<QuickfiltersComponentProps> {
48
- renderQuickfilter(item: any, index: any) {
47
+ renderQuickfilter(item: Quickfilter, index: number) {
49
48
  // Skip if merged
50
49
  let onValueChange
51
50
  if (item.merged) {
@@ -56,10 +55,12 @@ export default class QuickfiltersComponent extends React.Component<QuickfiltersC
56
55
  let itemValue = values[index]
57
56
 
58
57
  // Translate label
59
- const label = this.props.translate?.(item.label) ?? item.label
58
+ const label = this.props.translate?.(item.label ?? "") ?? item.label ?? ""
59
+
60
+ const exprCleaner = new ExprCleaner(this.props.schema)
60
61
 
61
62
  // Clean expression first
62
- const expr = new ExprCleaner(this.props.schema).cleanExpr(item.expr)
63
+ const expr = exprCleaner.cleanExpr(item.expr)
63
64
 
64
65
  // Do not render if nothing
65
66
  if (!expr) {
@@ -72,6 +73,22 @@ export default class QuickfiltersComponent extends React.Component<QuickfiltersC
72
73
  return null
73
74
  }
74
75
 
76
+ // If this quickfilter is text or text[] and has subsequent merged quickfilters, gather their expressions
77
+ let exprsToPass: Expr[] = [expr]
78
+ if (["text", "text[]"].includes(type)) {
79
+ for (let i2 = index + 1; i2 < this.props.design.length; i2++) {
80
+ const nextItem = this.props.design[i2]
81
+ if (nextItem.merged) {
82
+ const cleaned = exprCleaner.cleanExpr(nextItem.expr)
83
+ if (cleaned) {
84
+ exprsToPass.push(cleaned)
85
+ }
86
+ } else {
87
+ break
88
+ }
89
+ }
90
+ }
91
+
75
92
  // Determine if locked
76
93
  const lock = _.find(this.props.locks || [], (lock) => _.isEqual(lock.expr, expr))
77
94
 
@@ -110,71 +127,71 @@ export default class QuickfiltersComponent extends React.Component<QuickfiltersC
110
127
  const filters = (this.props.filters || []).concat(otherQuickfilterFilters)
111
128
 
112
129
  if (["enum", "enumset"].includes(type)) {
113
- return R(EnumQuickfilterComponent, {
114
- key: JSON.stringify(item),
115
- label,
116
- schema: this.props.schema,
117
- options: new ExprUtils(this.props.schema).getExprEnumValues(expr)!,
118
- value: itemValue,
119
- onValueChange,
120
- multi: item.multi
121
- })
130
+ return <EnumQuickfilterComponent
131
+ key={JSON.stringify(item)}
132
+ label={label}
133
+ schema={this.props.schema}
134
+ options={new ExprUtils(this.props.schema).getExprEnumValues(expr)!}
135
+ value={itemValue}
136
+ onValueChange={onValueChange}
137
+ multi={item.multi}
138
+ />
122
139
  }
123
140
 
124
141
  if (type === "text") {
125
- return R(TextQuickfilterComponent, {
126
- key: JSON.stringify(item),
127
- index,
128
- label,
129
- expr,
130
- schema: this.props.schema,
131
- quickfiltersDataSource: this.props.quickfiltersDataSource,
132
- value: itemValue,
133
- onValueChange,
134
- filters,
135
- multi: item.multi
136
- })
142
+ return <TextQuickfilterComponent
143
+ key={JSON.stringify(item)}
144
+ index={index}
145
+ label={label}
146
+ expr={exprsToPass}
147
+ schema={this.props.schema}
148
+ quickfiltersDataSource={this.props.quickfiltersDataSource}
149
+ value={itemValue}
150
+ onValueChange={onValueChange}
151
+ filters={filters}
152
+ multi={item.multi}
153
+ />
137
154
  }
138
155
 
139
156
  if (["date", "datetime"].includes(type)) {
140
- return R(DateQuickfilterComponent, {
141
- key: JSON.stringify(item),
142
- label,
143
- expr,
144
- schema: this.props.schema,
145
- value: itemValue,
146
- onValueChange
147
- })
157
+ return <DateQuickfilterComponent
158
+ key={JSON.stringify(item)}
159
+ label={label}
160
+ expr={expr}
161
+ schema={this.props.schema}
162
+ value={itemValue}
163
+ onValueChange={onValueChange}
164
+ />
148
165
  }
149
166
 
150
167
  if (type === "id[]") {
151
- return R(IdArrayQuickfilterComponent, {
152
- key: JSON.stringify(item),
153
- index,
154
- label,
155
- expr,
156
- schema: this.props.schema,
157
- dataSource: this.props.dataSource,
158
- value: itemValue,
159
- onValueChange,
160
- filters,
161
- multi: item.multi
162
- })
168
+ return <IdArrayQuickfilterComponent
169
+ key={JSON.stringify(item)}
170
+ index={index}
171
+ label={label}
172
+ expr={expr}
173
+ schema={this.props.schema}
174
+ dataSource={this.props.dataSource}
175
+ value={itemValue}
176
+ onValueChange={onValueChange}
177
+ filters={filters}
178
+ multi={item.multi}
179
+ />
163
180
  }
164
181
 
165
182
  if (type === "text[]") {
166
- return R(TextArrayQuickfilterComponent, {
167
- key: JSON.stringify(item),
168
- index,
169
- label,
170
- expr,
171
- schema: this.props.schema,
172
- quickfiltersDataSource: this.props.quickfiltersDataSource,
173
- value: itemValue,
174
- onValueChange,
175
- filters,
176
- multi: item.multi
177
- })
183
+ return <TextArrayQuickfilterComponent
184
+ key={JSON.stringify(item)}
185
+ index={index}
186
+ label={label}
187
+ expr={exprsToPass}
188
+ schema={this.props.schema}
189
+ quickfiltersDataSource={this.props.quickfiltersDataSource}
190
+ value={itemValue}
191
+ onValueChange={onValueChange}
192
+ filters={filters}
193
+ multi={item.multi}
194
+ />
178
195
  }
179
196
 
180
197
  return null
@@ -185,23 +202,21 @@ export default class QuickfiltersComponent extends React.Component<QuickfiltersC
185
202
  return null
186
203
  }
187
204
 
188
- return R(
189
- "div",
190
- {
191
- style: {
205
+ return (
206
+ <div
207
+ style={{
192
208
  borderTop: !this.props.hideTopBorder ? "solid 1px #E8E8E8" : undefined,
193
209
  borderBottom: "solid 1px #E8E8E8",
194
210
  padding: 5
195
- }
196
- },
197
- _.map(this.props.design, (item, i) => this.renderQuickfilter(item, i)),
198
- this.props.onHide
199
- ? R(
200
- "button",
201
- { className: "btn btn-sm btn-link", onClick: this.props.onHide },
202
- R("i", { className: "fa fa-angle-double-up" })
203
- )
204
- : undefined
211
+ }}
212
+ >
213
+ {_.map(this.props.design, (item, i) => this.renderQuickfilter(item, i))}
214
+ {this.props.onHide ? (
215
+ <button className="btn btn-sm btn-link" onClick={this.props.onHide}>
216
+ <i className="fa fa-angle-double-up" />
217
+ </button>
218
+ ) : undefined}
219
+ </div>
205
220
  )
206
221
  }
207
222
  }
@@ -244,38 +259,38 @@ class EnumQuickfilterComponent extends React.Component<EnumQuickfilterComponentP
244
259
  }
245
260
 
246
261
  renderSingleSelect(options: any[]) {
247
- return R(ReactSelect, {
248
- placeholder: T`All`,
249
- value: _.findWhere(options, { value: this.props.value }) || null,
250
- options,
251
- isClearable: true,
252
- onChange: (value: any) => {
262
+ return <ReactSelect
263
+ placeholder={T`All`}
264
+ value={_.findWhere(options, { value: this.props.value }) || null}
265
+ options={options}
266
+ isClearable={true}
267
+ onChange={(value: any) => {
253
268
  if (this.props.onValueChange) {
254
269
  return this.handleSingleChange(value?.value)
255
270
  }
256
- },
257
- isDisabled: this.props.onValueChange == null,
258
- styles: {
271
+ }}
272
+ isDisabled={this.props.onValueChange == null}
273
+ styles={{
259
274
  // Keep menu above fixed data table headers
260
275
  menu: (style) => _.extend({}, style, { zIndex: 2000 })
261
- }
262
- })
276
+ }}
277
+ />
263
278
  }
264
279
 
265
280
  renderMultiSelect(options: any[]) {
266
- return R(ReactSelect, {
267
- placeholder: T`All`,
268
- value: _.map(this.props.value, (v) => _.find(options, (o) => o.value === v)),
269
- isClearable: true,
270
- isMulti: true,
271
- options,
272
- onChange: this.props.onValueChange ? this.handleMultiChange : undefined,
273
- isDisabled: this.props.onValueChange == null,
274
- styles: {
281
+ return <ReactSelect
282
+ placeholder={T`All`}
283
+ value={_.map(this.props.value, (v) => _.find(options, (o) => o.value === v))}
284
+ isClearable={true}
285
+ isMulti={true}
286
+ options={options}
287
+ onChange={this.props.onValueChange ? this.handleMultiChange : undefined}
288
+ isDisabled={this.props.onValueChange == null}
289
+ styles={{
275
290
  // Keep menu above fixed data table headers
276
291
  menu: (style) => _.extend({}, style, { zIndex: 2000 })
277
- }
278
- })
292
+ }}
293
+ />
279
294
  }
280
295
 
281
296
  render() {
@@ -289,16 +304,14 @@ class EnumQuickfilterComponent extends React.Component<EnumQuickfilterComponentP
289
304
  width = width ? width * 8 + 120 : 280
290
305
  const minWidth = width > 280 || this.props.multi ? "280px" : `${width}px`
291
306
 
292
- return R(
293
- "div",
294
- { style: { display: "inline-block", paddingRight: 10 } },
295
- this.props.label ? R("span", { style: { color: "gray" } }, this.props.label + ":\u00a0") : undefined,
296
- R(
297
- "div",
298
- { style: { display: "inline-block", minWidth, verticalAlign: "middle" } },
299
- this.props.multi ? this.renderMultiSelect(options) : this.renderSingleSelect(options)
300
- ),
301
- !this.props.onValueChange ? R("i", { className: "text-warning fa fa-fw fa-lock" }) : undefined
307
+ return (
308
+ <div style={{ display: "inline-block", paddingRight: 10 }}>
309
+ {this.props.label ? <span style={{ color: "gray" }}>{this.props.label + ":\u00a0"}</span> : undefined}
310
+ <div style={{ display: "inline-block", minWidth, verticalAlign: "middle" }}>
311
+ {this.props.multi ? this.renderMultiSelect(options) : this.renderSingleSelect(options)}
312
+ </div>
313
+ {!this.props.onValueChange ? <i className="text-warning fa fa-fw fa-lock" /> : undefined}
314
+ </div>
302
315
  )
303
316
  }
304
317
  }
@@ -308,7 +321,7 @@ interface TextQuickfilterComponentProps {
308
321
  schema: Schema
309
322
  /** See QuickfiltersDataSource */
310
323
  quickfiltersDataSource: QuickfiltersDataSource
311
- expr: any
324
+ expr: Expr[]
312
325
  index: number
313
326
  /** Current value of quickfilter (state of filter selected) */
314
327
  value?: any
@@ -324,25 +337,23 @@ interface TextQuickfilterComponentProps {
324
337
  // Quickfilter for a text value
325
338
  class TextQuickfilterComponent extends React.Component<TextQuickfilterComponentProps> {
326
339
  render() {
327
- return R(
328
- "div",
329
- { style: { display: "inline-block", paddingRight: 10 } },
330
- this.props.label ? R("span", { style: { color: "gray" } }, this.props.label + ":\u00a0") : undefined,
331
- R(
332
- "div",
333
- { style: { display: "inline-block", minWidth: "280px", verticalAlign: "middle" } },
334
- R(TextLiteralComponent, {
335
- value: this.props.value,
336
- onChange: this.props.onValueChange,
337
- schema: this.props.schema,
338
- expr: this.props.expr,
339
- index: this.props.index,
340
- multi: this.props.multi,
341
- quickfiltersDataSource: this.props.quickfiltersDataSource,
342
- filters: this.props.filters
343
- })
344
- ),
345
- !this.props.onValueChange ? R("i", { className: "text-warning fa fa-fw fa-lock" }) : undefined
340
+ return (
341
+ <div style={{ display: "inline-block", paddingRight: 10 }}>
342
+ {this.props.label ? <span style={{ color: "gray" }}>{this.props.label + ":\u00a0"}</span> : undefined}
343
+ <div style={{ display: "inline-block", minWidth: "280px", verticalAlign: "middle" }}>
344
+ <TextLiteralComponent
345
+ value={this.props.value}
346
+ onChange={this.props.onValueChange}
347
+ schema={this.props.schema}
348
+ expr={this.props.expr}
349
+ index={this.props.index}
350
+ multi={this.props.multi}
351
+ quickfiltersDataSource={this.props.quickfiltersDataSource}
352
+ filters={this.props.filters}
353
+ />
354
+ </div>
355
+ {!this.props.onValueChange ? <i className="text-warning fa fa-fw fa-lock" /> : undefined}
356
+ </div>
346
357
  )
347
358
  }
348
359
  }
@@ -359,20 +370,18 @@ interface DateQuickfilterComponentProps {
359
370
  // Quickfilter for a date value
360
371
  class DateQuickfilterComponent extends React.Component<DateQuickfilterComponentProps> {
361
372
  render() {
362
- return R(
363
- "div",
364
- { style: { display: "inline-block", paddingRight: 10 } },
365
- this.props.label ? R("span", { style: { color: "gray" } }, this.props.label + ":\u00a0") : undefined,
366
- R(
367
- "div",
368
- { style: { display: "inline-block", minWidth: "280px", verticalAlign: "middle" } },
369
- R(DateExprComponent, {
370
- datetime: new ExprUtils(this.props.schema).getExprType(this.props.expr) === "datetime",
371
- value: this.props.value,
372
- onChange: this.props.onValueChange
373
- })
374
- ),
375
- !this.props.onValueChange ? R("i", { className: "text-warning fa fa-fw fa-lock" }) : undefined
373
+ return (
374
+ <div style={{ display: "inline-block", paddingRight: 10 }}>
375
+ {this.props.label ? <span style={{ color: "gray" }}>{this.props.label + ":\u00a0"}</span> : undefined}
376
+ <div style={{ display: "inline-block", minWidth: "280px", verticalAlign: "middle" }}>
377
+ <DateExprComponent
378
+ datetime={new ExprUtils(this.props.schema).getExprType(this.props.expr) === "datetime"}
379
+ value={this.props.value}
380
+ onChange={this.props.onValueChange}
381
+ />
382
+ </div>
383
+ {!this.props.onValueChange ? <i className="text-warning fa fa-fw fa-lock" /> : undefined}
384
+ </div>
376
385
  )
377
386
  }
378
387
  }
@@ -382,7 +391,7 @@ interface TextArrayQuickfilterComponentProps {
382
391
  schema: Schema
383
392
  /** See QuickfiltersDataSource */
384
393
  quickfiltersDataSource: QuickfiltersDataSource
385
- expr: any
394
+ expr: Expr[]
386
395
  index: number
387
396
  /** Current value of quickfilter (state of filter selected) */
388
397
  value?: any
@@ -398,25 +407,23 @@ interface TextArrayQuickfilterComponentProps {
398
407
  /** Quickfilter for a text value */
399
408
  class TextArrayQuickfilterComponent extends React.Component<TextArrayQuickfilterComponentProps> {
400
409
  render() {
401
- return R(
402
- "div",
403
- { style: { display: "inline-block", paddingRight: 10 } },
404
- this.props.label ? R("span", { style: { color: "gray" } }, this.props.label + ":\u00a0") : undefined,
405
- R(
406
- "div",
407
- { style: { display: "inline-block", minWidth: "280px", verticalAlign: "middle" } },
408
- R(TextLiteralComponent, {
409
- value: this.props.value,
410
- onChange: this.props.onValueChange,
411
- schema: this.props.schema,
412
- expr: this.props.expr,
413
- index: this.props.index,
414
- multi: this.props.multi,
415
- quickfiltersDataSource: this.props.quickfiltersDataSource,
416
- filters: this.props.filters
417
- })
418
- ),
419
- !this.props.onValueChange ? R("i", { className: "text-warning fa fa-fw fa-lock" }) : undefined
410
+ return (
411
+ <div style={{ display: "inline-block", paddingRight: 10 }}>
412
+ {this.props.label ? <span style={{ color: "gray" }}>{this.props.label + ":\u00a0"}</span> : undefined}
413
+ <div style={{ display: "inline-block", minWidth: "280px", verticalAlign: "middle" }}>
414
+ <TextLiteralComponent
415
+ value={this.props.value}
416
+ onChange={this.props.onValueChange}
417
+ schema={this.props.schema}
418
+ expr={this.props.expr}
419
+ index={this.props.index}
420
+ multi={this.props.multi}
421
+ quickfiltersDataSource={this.props.quickfiltersDataSource}
422
+ filters={this.props.filters}
423
+ />
424
+ </div>
425
+ {!this.props.onValueChange ? <i className="text-warning fa fa-fw fa-lock" /> : undefined}
426
+ </div>
420
427
  )
421
428
  }
422
429
  }
@@ -0,0 +1,197 @@
1
+ import _ from "lodash"
2
+ import React from "react"
3
+ import { default as AsyncReactSelect } from "react-select/async"
4
+ import { Expr, ExprCompiler, Schema } from "@mwater/expressions"
5
+ import { ExprUtils } from "@mwater/expressions"
6
+ import { JsonQLFilter } from "../JsonQLFilter"
7
+ import { QuickfiltersDataSource } from "./QuickfiltersDataSource"
8
+
9
+ export interface TextLiteralComponentProps {
10
+ value?: string | string[] | null
11
+ onChange?: (value: string | string[] | null) => void
12
+ schema: Schema
13
+ /** See QuickfiltersDataSource */
14
+ quickfiltersDataSource: QuickfiltersDataSource
15
+ /**
16
+ * One or more expressions whose values should be combined into a single option list.
17
+ * Each subsequent expression represents another quickfilter that is merged with this one.
18
+ * The first expression is the main quickfilter, and the subsequent expressions are merged with it.
19
+ */
20
+ expr: Expr[]
21
+ index: number
22
+ /** true to display multiple values */
23
+ multi?: boolean
24
+
25
+ /** Filters to add to restrict quick filter data to */
26
+ filters?: JsonQLFilter[]
27
+ }
28
+
29
+ /** Displays a combo box that allows selecting single or multiple text values from an expression
30
+ * The expression can be type `text` or `text[]`
31
+ */
32
+ export default class TextLiteralComponent extends React.Component<TextLiteralComponentProps> {
33
+ handleSingleChange = (val: any) => {
34
+ const value = val ? val.value || null : null // Blank is null
35
+ return this.props.onChange?.(value)
36
+ }
37
+
38
+ handleMultipleChange = (val: any) => {
39
+ const value = val ? _.pluck(val, "value") : []
40
+
41
+ if (value.length > 0) {
42
+ return this.props.onChange?.(value)
43
+ } else {
44
+ return this.props.onChange?.(null)
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Load the options for the select. If multiple expressions are supplied, each
50
+ * is queried independently and the union of the results (unique values) is
51
+ * returned.
52
+ */
53
+ getOptions = (input: string, cb: (rows: { value: string; label: string }[]) => void) => {
54
+ // Normalise to array so we can treat single and multiple uniformly
55
+ const exprs = this.props.expr
56
+
57
+ // Keep the unique set of text values that come back from each expression
58
+ const uniqueValues = new Set<string>()
59
+
60
+ // Early exit if no expressions
61
+ if (exprs.length === 0) {
62
+ cb([])
63
+ return
64
+ }
65
+
66
+ let remaining = exprs.length
67
+
68
+ // Helper that is called after each async query completes
69
+ const maybeFinish = () => {
70
+ remaining -= 1
71
+ if (remaining === 0) {
72
+ const result = Array.from(uniqueValues).map((value) => ({ value, label: value }))
73
+ cb(result)
74
+ }
75
+ }
76
+
77
+ // Execute a query for each expression
78
+ for (let i = 0; i < exprs.length; i++) {
79
+ const expr = exprs[i]
80
+ // Clone base filters for this expression so that each expression gets its own copy
81
+ const filters = (this.props.filters || []).slice()
82
+
83
+ const exprUtils = new ExprUtils(this.props.schema)
84
+ const exprType = exprUtils.getExprType(expr)
85
+
86
+ // Create query to get matches
87
+ const exprCompiler = new ExprCompiler(this.props.schema)
88
+ const exprTable = exprUtils.getExprTable(expr)
89
+
90
+ if (!exprTable) {
91
+ maybeFinish()
92
+ return
93
+ }
94
+
95
+ // Add filter for the input text depending on expression type
96
+ if (exprType === "text") {
97
+ if (input) {
98
+ filters.push({
99
+ table: exprTable,
100
+ jsonql: {
101
+ type: "op",
102
+ op: "~*",
103
+ exprs: [
104
+ exprCompiler.compileExpr({ expr, tableAlias: "{alias}" }),
105
+ escapeRegex(input) // Avoid _.escapeRegExp to prevent extra backslashes that Postgres dislikes
106
+ ]
107
+ }
108
+ })
109
+ }
110
+ } else if (exprType === "text[]") {
111
+ if (input) {
112
+ filters.push({
113
+ table: "values",
114
+ jsonql: {
115
+ type: "op",
116
+ op: "~*",
117
+ exprs: [
118
+ { type: "field", tableAlias: "{alias}", column: "value" },
119
+ "^" + escapeRegex(input) // Avoid _.escapeRegExp to prevent extra backslashes that Postgres dislikes
120
+ ]
121
+ }
122
+ })
123
+ }
124
+ } else {
125
+ // Unsupported type – skip this expression
126
+ maybeFinish()
127
+ return
128
+ }
129
+
130
+ // Execute the data source query for this expression
131
+ this.props.quickfiltersDataSource.getValues(
132
+ this.props.index + i, // Index of this quickfilter in the design
133
+ expr,
134
+ filters,
135
+ null,
136
+ 250,
137
+ (err: any, values: any[] = []) => {
138
+ if (!err && values) {
139
+ // Filter out null / blank and add to unique set
140
+ _.filter(values, (v) => v).forEach((v) => uniqueValues.add(v))
141
+ }
142
+
143
+ maybeFinish()
144
+ }
145
+ )
146
+ }
147
+ }
148
+
149
+ renderSingle() {
150
+ const currentValue = this.props.value ? { value: this.props.value, label: this.props.value } : null
151
+
152
+ return <AsyncReactSelect
153
+ key={JSON.stringify(this.props.filters)} // Include to force a change when filters change
154
+ placeholder={T`All`}
155
+ value={currentValue}
156
+ loadOptions={this.getOptions}
157
+ onChange={this.props.onChange ? this.handleSingleChange : undefined}
158
+ isClearable={true}
159
+ defaultOptions={true}
160
+ isDisabled={this.props.onChange == null}
161
+ noOptionsMessage={() => T`Type to search`}
162
+ styles={{
163
+ // Keep menu above fixed data table headers
164
+ menu: (style) => _.extend({}, style, { zIndex: 2000 })
165
+ }}
166
+ />
167
+ }
168
+
169
+ renderMultiple() {
170
+ const currentValue = this.props.value ? _.map(this.props.value, (v) => ({ value: v, label: v })) : null
171
+
172
+ return <AsyncReactSelect
173
+ placeholder={T`All`}
174
+ value={currentValue}
175
+ key={JSON.stringify(this.props.filters)} // Include to force a change when filters change
176
+ isMulti={true}
177
+ loadOptions={this.getOptions}
178
+ defaultOptions={true}
179
+ onChange={this.props.onChange ? this.handleMultipleChange : undefined}
180
+ isClearable={true}
181
+ isDisabled={this.props.onChange == null}
182
+ noOptionsMessage={() => T`Type to search`}
183
+ styles={{
184
+ // Keep menu above fixed data table headers
185
+ menu: (style) => _.extend({}, style, { zIndex: 2000 })
186
+ }}
187
+ />
188
+ }
189
+
190
+ render() {
191
+ return <div style={{ width: "100%" }}>{this.props.multi ? this.renderMultiple() : this.renderSingle()}</div>
192
+ }
193
+ }
194
+
195
+ function escapeRegex(s: any) {
196
+ return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&")
197
+ }
@@ -196,8 +196,15 @@ class MapWidgetComponent extends React.Component<MapWidgetComponentProps, MapWid
196
196
 
197
197
  const handleDesignChange = (d: any) => this.setState({ transientDesign: d })
198
198
 
199
+ // Only allow single click to edit if there are no layers
200
+ const handleClick = this.props.onDesignChange != null && this.state.editDesign === null && this.props.design.layerViews.length == 0 ? this.handleStartEditing : undefined
201
+
199
202
  // Wrap in a simple widget
200
- return <div>
203
+ return (
204
+ <div
205
+ onClick={handleClick}
206
+ style={{ position: "relative", width: this.props.width }}
207
+ >
201
208
  {this.props.onDesignChange != null ? this.renderEditor() : undefined}
202
209
  <DropdownWidgetComponent
203
210
  width={this.props.width}
@@ -207,5 +214,6 @@ class MapWidgetComponent extends React.Component<MapWidgetComponentProps, MapWid
207
214
  {this.renderContent(this.state.transientDesign, handleDesignChange, this.props.width, this.props.height)}
208
215
  </DropdownWidgetComponent>
209
216
  </div>
217
+ )
210
218
  }
211
219
  }