@mwater/visualization 5.4.4 → 5.5.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.
- package/.storybook/head.html +0 -1
- package/lib/MWaterContextComponent.js +1 -1
- package/lib/MWaterLoaderComponent.d.ts +2 -2
- package/lib/dashboards/DashboardComponent.js +2 -1
- package/lib/dashboards/LayoutOptionsComponent.js +18 -11
- package/lib/dashboards/ServerDashboardDataSource.d.ts +10 -1
- package/lib/dashboards/ServerDashboardDataSource.js +29 -0
- package/lib/dashboards/layoutOptions.d.ts +5 -1
- package/lib/datagrids/DatagridComponent.js +1 -1
- package/lib/datagrids/ExprCellComponent.d.ts +1 -0
- package/lib/datagrids/ExprCellComponent.js +22 -20
- package/lib/maps/BufferLayer.d.ts +18 -0
- package/lib/maps/BufferLayer.js +24 -14
- package/lib/maps/ChoroplethLayer.d.ts +18 -0
- package/lib/maps/ChoroplethLayer.js +34 -25
- package/lib/maps/ChoroplethLayerDesign.d.ts +3 -2
- package/lib/maps/ChoroplethLayerDesigner.d.ts +11 -1
- package/lib/maps/DirectMapDataSource.js +17 -0
- package/lib/maps/EditHoverOver.d.ts +1 -1
- package/lib/maps/EditHoverOver.js +62 -33
- package/lib/maps/HoverContent.d.ts +10 -5
- package/lib/maps/HoverContent.js +6 -35
- package/lib/maps/Layer.d.ts +37 -0
- package/lib/maps/Layer.js +30 -4
- package/lib/maps/MWaterServerLayer.d.ts +2 -2
- package/lib/maps/MWaterServerLayer.js +6 -6
- package/lib/maps/MapLayerDataSource.d.ts +9 -0
- package/lib/maps/MapUtils.d.ts +19 -1
- package/lib/maps/MapUtils.js +71 -1
- package/lib/maps/MarkersLayer.d.ts +18 -0
- package/lib/maps/MarkersLayer.js +24 -24
- package/lib/maps/MarkersLayerDesignerComponent.d.ts +14 -1
- package/lib/maps/RasterMapViewComponent.js +1 -1
- package/lib/maps/ServerMapDataSource.d.ts +9 -0
- package/lib/maps/ServerMapDataSource.js +29 -0
- package/lib/maps/VectorMapViewComponent.js +6 -6
- package/lib/maps/maps.d.ts +4 -2
- package/lib/mwater_table_selection/FormsListComponent.d.ts +33 -0
- package/lib/mwater_table_selection/FormsListComponent.js +141 -0
- package/lib/mwater_table_selection/IndicatorsListComponent.d.ts +47 -0
- package/lib/mwater_table_selection/IndicatorsListComponent.js +182 -0
- package/lib/mwater_table_selection/IssuesListComponent.d.ts +29 -0
- package/lib/mwater_table_selection/IssuesListComponent.js +123 -0
- package/lib/mwater_table_selection/MWaterAccountingSystemListComponent.d.ts +20 -0
- package/lib/mwater_table_selection/MWaterAccountingSystemListComponent.js +157 -0
- package/lib/mwater_table_selection/MWaterAssetSystemsListComponent.d.ts +17 -0
- package/lib/mwater_table_selection/MWaterAssetSystemsListComponent.js +79 -0
- package/lib/mwater_table_selection/MWaterCompleteTableSelectComponent.d.ts +37 -0
- package/lib/mwater_table_selection/MWaterCompleteTableSelectComponent.js +275 -0
- package/lib/mwater_table_selection/MWaterCustomTablesetListComponent.d.ts +17 -0
- package/lib/mwater_table_selection/MWaterCustomTablesetListComponent.js +94 -0
- package/lib/mwater_table_selection/MWaterMetricsTableListComponent.d.ts +17 -0
- package/lib/mwater_table_selection/MWaterMetricsTableListComponent.js +80 -0
- package/lib/mwater_table_selection/MWaterTableSelectComponent.d.ts +32 -0
- package/lib/mwater_table_selection/MWaterTableSelectComponent.js +158 -0
- package/lib/quickfilter/Quickfilter.d.ts +2 -0
- package/lib/quickfilter/QuickfiltersDesignComponent.js +18 -10
- package/lib/widgets/charts/Chart.d.ts +11 -0
- package/lib/widgets/charts/Chart.js +15 -0
- package/lib/widgets/charts/ChartWidgetComponent.d.ts +1 -0
- package/lib/widgets/charts/ChartWidgetComponent.js +27 -1
- package/lib/widgets/charts/layered/LayeredChartDesign.d.ts +1 -1
- package/lib/widgets/charts/layered/LayeredChartDesignerComponent.d.ts +1 -1
- package/lib/widgets/charts/layered/LayeredChartDesignerComponent.js +5 -12
- package/lib/widgets/charts/layered/LayeredChartLayerDesignerComponent.d.ts +43 -57
- package/lib/widgets/charts/layered/LayeredChartLayerDesignerComponent.js +113 -110
- package/lib/widgets/charts/layered/LayeredChartUtils.d.ts +2 -1
- package/lib/widgets/charts/layered/LayeredChartUtils.js +0 -2
- package/lib/widgets/charts/pivot/PivotChart.d.ts +2 -0
- package/lib/widgets/charts/pivot/PivotChart.js +156 -0
- package/lib/widgets/charts/pivot/PivotChartDesignerComponent.d.ts +5 -20
- package/lib/widgets/charts/pivot/PivotChartDesignerComponent.js +31 -61
- package/lib/widgets/charts/pivot/PivotChartLayoutBuilder.d.ts +4 -0
- package/lib/widgets/charts/pivot/PivotChartLayoutBuilder.js +4 -2
- package/lib/widgets/charts/pivot/PivotChartLayoutComponent.d.ts +5 -44
- package/lib/widgets/charts/pivot/PivotChartLayoutComponent.js +38 -63
- package/lib/widgets/charts/pivot/SegmentDesignerComponent.d.ts +7 -68
- package/lib/widgets/charts/pivot/SegmentDesignerComponent.js +58 -106
- package/lib/widgets/charts/table/TableChart.d.ts +2 -0
- package/lib/widgets/charts/table/TableChart.js +172 -1
- package/lib/widgets/charts/table/TableChartDesignerComponent.d.ts +7 -17
- package/lib/widgets/charts/table/TableChartDesignerComponent.js +79 -95
- package/lib/widgets/charts/table/TableChartViewComponent.d.ts +1 -7
- package/lib/widgets/charts/table/TableChartViewComponent.js +19 -27
- package/package.json +3 -8
- package/src/MWaterContextComponent.tsx +1 -1
- package/src/MWaterLoaderComponent.ts +1 -1
- package/src/dashboards/DashboardComponent.tsx +2 -1
- package/src/dashboards/LayoutOptionsComponent.tsx +22 -10
- package/src/dashboards/ServerDashboardDataSource.ts +36 -1
- package/src/dashboards/layoutOptions.tsx +5 -1
- package/src/datagrids/DatagridComponent.tsx +1 -1
- package/src/datagrids/ExprCellComponent.tsx +23 -20
- package/src/maps/BufferLayer.ts +35 -20
- package/src/maps/ChoroplethLayer.ts +51 -33
- package/src/maps/ChoroplethLayerDesign.ts +3 -2
- package/src/maps/ChoroplethLayerDesigner.tsx +2 -2
- package/src/maps/DirectMapDataSource.ts +21 -1
- package/src/maps/EditHoverOver.tsx +91 -51
- package/src/maps/HoverContent.tsx +16 -47
- package/src/maps/Layer.ts +42 -4
- package/src/maps/MWaterServerLayer.ts +6 -6
- package/src/maps/MapLayerDataSource.ts +8 -0
- package/src/maps/MapUtils.ts +70 -3
- package/src/maps/MarkersLayer.ts +34 -24
- package/src/maps/RasterMapViewComponent.ts +1 -1
- package/src/maps/ServerMapDataSource.ts +35 -0
- package/src/maps/VectorMapViewComponent.tsx +6 -6
- package/src/maps/maps.ts +4 -2
- package/src/mwater_table_selection/FormsListComponent.tsx +188 -0
- package/src/mwater_table_selection/IndicatorsListComponent.tsx +283 -0
- package/src/mwater_table_selection/IssuesListComponent.tsx +167 -0
- package/src/mwater_table_selection/MWaterAccountingSystemListComponent.tsx +225 -0
- package/src/{MWaterAssetSystemsListComponent.tsx → mwater_table_selection/MWaterAssetSystemsListComponent.tsx} +2 -2
- package/src/mwater_table_selection/MWaterCompleteTableSelectComponent.tsx +377 -0
- package/src/{MWaterCustomTablesetListComponent.tsx → mwater_table_selection/MWaterCustomTablesetListComponent.tsx} +1 -1
- package/src/{MWaterMetricsTableListComponent.tsx → mwater_table_selection/MWaterMetricsTableListComponent.tsx} +1 -1
- package/src/{MWaterTableSelectComponent.tsx → mwater_table_selection/MWaterTableSelectComponent.tsx} +83 -86
- package/src/quickfilter/Quickfilter.ts +3 -0
- package/src/quickfilter/QuickfiltersDesignComponent.tsx +19 -14
- package/src/widgets/charts/Chart.ts +17 -0
- package/src/widgets/charts/ChartWidgetComponent.tsx +36 -1
- package/src/widgets/charts/layered/LayeredChartDesign.ts +1 -1
- package/src/widgets/charts/layered/LayeredChartDesignerComponent.tsx +23 -24
- package/src/widgets/charts/layered/LayeredChartLayerDesignerComponent.tsx +260 -211
- package/src/widgets/charts/layered/LayeredChartUtils.ts +7 -7
- package/src/widgets/charts/pivot/PivotChart.ts +191 -0
- package/src/widgets/charts/pivot/PivotChartDesignerComponent.tsx +124 -129
- package/src/widgets/charts/pivot/PivotChartLayoutBuilder.ts +4 -2
- package/src/widgets/charts/pivot/PivotChartLayoutComponent.tsx +120 -149
- package/src/widgets/charts/pivot/SegmentDesignerComponent.tsx +178 -198
- package/src/widgets/charts/table/TableChart.ts +177 -1
- package/src/widgets/charts/table/TableChartDesignerComponent.tsx +422 -0
- package/src/widgets/charts/table/{TableChartViewComponent.ts → TableChartViewComponent.tsx} +65 -60
- package/src/MWaterCompleteTableSelectComponent.tsx +0 -975
- package/src/widgets/charts/table/TableChartDesignerComponent.ts +0 -441
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import PropTypes from "prop-types"
|
|
2
2
|
import _ from "lodash"
|
|
3
3
|
import React from "react"
|
|
4
|
-
const R = React.createElement
|
|
5
4
|
|
|
6
5
|
import * as ui from "@mwater/react-library/lib/bootstrap"
|
|
7
6
|
import AxisComponent from "../../../axes/AxisComponent"
|
|
@@ -86,45 +85,42 @@ export default class SegmentDesignerComponent extends React.Component<
|
|
|
86
85
|
}
|
|
87
86
|
|
|
88
87
|
renderMode() {
|
|
89
|
-
return
|
|
90
|
-
ui.FormGroup
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
{` - `}
|
|
99
|
-
{T`used for summary ${this.props.segmentType}s and empty ${this.props.segmentType}s`}
|
|
100
|
-
</span>
|
|
101
|
-
</ui.Radio>,
|
|
88
|
+
return (
|
|
89
|
+
<ui.FormGroup labelMuted={true} label={T`Type`}>
|
|
90
|
+
<ui.Radio key="single" value={this.state.mode} radioValue={"single"} onChange={this.handleMode}>
|
|
91
|
+
{T`Single ${this.props.segmentType}`}
|
|
92
|
+
<span className="text-muted">
|
|
93
|
+
{` - `}
|
|
94
|
+
{T`used for summary ${this.props.segmentType}s and empty ${this.props.segmentType}s`}
|
|
95
|
+
</span>
|
|
96
|
+
</ui.Radio>
|
|
102
97
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
98
|
+
<ui.Radio key="multiple" value={this.state.mode} radioValue={"multiple"} onChange={this.handleMode}>
|
|
99
|
+
{T`Multiple ${this.props.segmentType}s`}
|
|
100
|
+
{" - "}
|
|
101
|
+
<span className="text-muted">{T`disaggregate data by a field`}</span>
|
|
102
|
+
</ui.Radio>
|
|
103
|
+
</ui.FormGroup>
|
|
108
104
|
)
|
|
109
105
|
}
|
|
110
106
|
|
|
111
107
|
renderLabel() {
|
|
112
|
-
return
|
|
113
|
-
ui.FormGroup
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
label:
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
108
|
+
return (
|
|
109
|
+
<ui.FormGroup
|
|
110
|
+
labelMuted={true}
|
|
111
|
+
label={T`Label`}
|
|
112
|
+
help={this.state.mode === "multiple" ? T`Optional label for the ${this.props.segmentType}s` : undefined}
|
|
113
|
+
>
|
|
114
|
+
<input
|
|
115
|
+
ref={(elem: HTMLInputElement | null) => {
|
|
116
|
+
this.labelElem = elem
|
|
117
|
+
}}
|
|
118
|
+
type="text"
|
|
119
|
+
className="form-control"
|
|
120
|
+
value={this.props.segment.label || ""}
|
|
121
|
+
onChange={this.handleLabelChange}
|
|
122
|
+
/>
|
|
123
|
+
</ui.FormGroup>
|
|
128
124
|
)
|
|
129
125
|
}
|
|
130
126
|
|
|
@@ -137,20 +133,18 @@ export default class SegmentDesignerComponent extends React.Component<
|
|
|
137
133
|
|
|
138
134
|
return <ui.FormGroup labelMuted label={T`Field`} help={T`Field to disaggregate data by`}>
|
|
139
135
|
<div style={{ marginLeft: 8 }}>
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
})
|
|
153
|
-
}
|
|
136
|
+
<AxisComponent
|
|
137
|
+
schema={this.props.schema}
|
|
138
|
+
dataSource={this.props.dataSource}
|
|
139
|
+
table={this.props.table}
|
|
140
|
+
types={["enum", "enumset", "text", "boolean", "date"]}
|
|
141
|
+
aggrNeed="none"
|
|
142
|
+
value={this.props.segment.valueAxis}
|
|
143
|
+
onChange={this.handleValueAxisChange}
|
|
144
|
+
allowExcludedValues={true}
|
|
145
|
+
filters={this.props.filters}
|
|
146
|
+
collapseCategories={true}
|
|
147
|
+
/>
|
|
154
148
|
{ allowValueAxisOnlyValuesPresent ?
|
|
155
149
|
<ui.Checkbox
|
|
156
150
|
value={this.props.segment.valueAxisOnlyValuesPresent}
|
|
@@ -168,158 +162,144 @@ export default class SegmentDesignerComponent extends React.Component<
|
|
|
168
162
|
}
|
|
169
163
|
|
|
170
164
|
renderFilter() {
|
|
171
|
-
return
|
|
172
|
-
ui.FormGroup
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
165
|
+
return (
|
|
166
|
+
<ui.FormGroup
|
|
167
|
+
labelMuted={true}
|
|
168
|
+
label={<><ui.Icon id="glyphicon-filter" /> {T`Filters`}</>}
|
|
169
|
+
hint={T`Filters all data associated with this ${this.props.segmentType}`}
|
|
170
|
+
>
|
|
171
|
+
<FilterExprComponent
|
|
172
|
+
schema={this.props.schema}
|
|
173
|
+
dataSource={this.props.dataSource}
|
|
174
|
+
onChange={this.handleFilterChange}
|
|
175
|
+
table={this.props.table}
|
|
176
|
+
value={this.props.segment.filter}
|
|
177
|
+
/>
|
|
178
|
+
</ui.FormGroup>
|
|
185
179
|
)
|
|
186
180
|
}
|
|
187
181
|
|
|
188
182
|
renderStyling() {
|
|
189
|
-
return
|
|
190
|
-
ui.FormGroup
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
onChange={(value) => this.update({ valueLabelBold: value })}
|
|
218
|
-
>
|
|
219
|
-
{T`Header Bold`}
|
|
220
|
-
</ui.Checkbox>
|
|
221
|
-
: undefined}
|
|
222
|
-
{this.props.segment.valueAxis && this.props.segment.label
|
|
223
|
-
? R(
|
|
224
|
-
"div",
|
|
225
|
-
{ style: { paddingTop: 5 } },
|
|
226
|
-
T`Shade filler cells: `,
|
|
227
|
-
R(ColorComponent, {
|
|
228
|
-
color: this.props.segment.fillerColor,
|
|
229
|
-
onChange: (color: any) => this.update({ fillerColor: color })
|
|
230
|
-
})
|
|
231
|
-
)
|
|
183
|
+
return (
|
|
184
|
+
<ui.FormGroup labelMuted={true} label={T`Styling`}>
|
|
185
|
+
<div>
|
|
186
|
+
<ui.Checkbox
|
|
187
|
+
key="bold"
|
|
188
|
+
inline
|
|
189
|
+
value={this.props.segment.bold === true}
|
|
190
|
+
onChange={(value) => this.update({ bold: value })}
|
|
191
|
+
>
|
|
192
|
+
{T`Bold`}
|
|
193
|
+
</ui.Checkbox>
|
|
194
|
+
<ui.Checkbox
|
|
195
|
+
key="italic"
|
|
196
|
+
inline
|
|
197
|
+
value={this.props.segment.italic === true}
|
|
198
|
+
onChange={(value) => this.update({ italic: value })}
|
|
199
|
+
>
|
|
200
|
+
{T`Italic`}
|
|
201
|
+
</ui.Checkbox>
|
|
202
|
+
{ this.props.segment.valueAxis && this.props.segment.label ?
|
|
203
|
+
<ui.Checkbox
|
|
204
|
+
key="valueLabelBold"
|
|
205
|
+
inline
|
|
206
|
+
value={this.props.segment.valueLabelBold === true}
|
|
207
|
+
onChange={(value) => this.update({ valueLabelBold: value })}
|
|
208
|
+
>
|
|
209
|
+
{T`Header Bold`}
|
|
210
|
+
</ui.Checkbox>
|
|
232
211
|
: undefined}
|
|
233
|
-
|
|
212
|
+
{this.props.segment.valueAxis && this.props.segment.label ? (
|
|
213
|
+
<div style={{ paddingTop: 5 }}>
|
|
214
|
+
{T`Shade filler cells: `}
|
|
215
|
+
<ColorComponent
|
|
216
|
+
color={this.props.segment.fillerColor}
|
|
217
|
+
onChange={(color: any) => this.update({ fillerColor: color })}
|
|
218
|
+
/>
|
|
219
|
+
</div>
|
|
220
|
+
) : undefined}
|
|
221
|
+
</div>
|
|
222
|
+
</ui.FormGroup>
|
|
234
223
|
)
|
|
235
224
|
}
|
|
236
225
|
|
|
237
226
|
renderBorders() {
|
|
238
|
-
return
|
|
239
|
-
ui.FormGroup
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
defaultValue: 2,
|
|
260
|
-
onChange: (value: any) => this.update({ borderAfter: value })
|
|
261
|
-
})
|
|
227
|
+
return (
|
|
228
|
+
<ui.FormGroup labelMuted={true} label={T`Borders`}>
|
|
229
|
+
<div key="before">{this.props.segmentType === "row" ? T`Top: ` : T`Left: `}</div>
|
|
230
|
+
<BorderComponent
|
|
231
|
+
value={this.props.segment.borderBefore}
|
|
232
|
+
defaultValue={2}
|
|
233
|
+
onChange={(value: any) => this.update({ borderBefore: value })}
|
|
234
|
+
/>
|
|
235
|
+
<div key="within">{T`Within: `}</div>
|
|
236
|
+
<BorderComponent
|
|
237
|
+
value={this.props.segment.borderWithin}
|
|
238
|
+
defaultValue={1}
|
|
239
|
+
onChange={(value: any) => this.update({ borderWithin: value })}
|
|
240
|
+
/>
|
|
241
|
+
<div key="after">{this.props.segmentType === "row" ? T`Bottom: ` : T`Right: `}</div>
|
|
242
|
+
<BorderComponent
|
|
243
|
+
value={this.props.segment.borderAfter}
|
|
244
|
+
defaultValue={2}
|
|
245
|
+
onChange={(value: any) => this.update({ borderAfter: value })}
|
|
246
|
+
/>
|
|
247
|
+
</ui.FormGroup>
|
|
262
248
|
)
|
|
263
249
|
}
|
|
264
250
|
|
|
265
251
|
renderOrderExpr() {
|
|
266
|
-
return
|
|
267
|
-
ui.FormGroup
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}),
|
|
252
|
+
return (
|
|
253
|
+
<ui.FormGroup
|
|
254
|
+
labelMuted={true}
|
|
255
|
+
label={<><ui.Icon id="fa-sort-amount-asc" /> {T`Sort`}</>}
|
|
256
|
+
hint={T`Sorts the display of this ${this.props.segmentType}`}
|
|
257
|
+
>
|
|
258
|
+
<ExprComponent
|
|
259
|
+
schema={this.props.schema}
|
|
260
|
+
dataSource={this.props.dataSource}
|
|
261
|
+
onChange={this.handleOrderExprChange}
|
|
262
|
+
table={this.props.table}
|
|
263
|
+
types={["enum", "text", "boolean", "date", "datetime", "number"]}
|
|
264
|
+
aggrStatuses={["aggregate"]}
|
|
265
|
+
value={this.props.segment.orderExpr ?? null}
|
|
266
|
+
/>
|
|
282
267
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
{
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
{
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
},
|
|
305
|
-
T`Descending`
|
|
306
|
-
)
|
|
307
|
-
)
|
|
308
|
-
: undefined
|
|
268
|
+
{this.props.segment.orderExpr ? (
|
|
269
|
+
<div>
|
|
270
|
+
<ui.Radio
|
|
271
|
+
value={this.props.segment.orderDir || "asc"}
|
|
272
|
+
radioValue="asc"
|
|
273
|
+
onChange={this.handleOrderDirChange}
|
|
274
|
+
inline={true}
|
|
275
|
+
>
|
|
276
|
+
{T`Ascending`}
|
|
277
|
+
</ui.Radio>
|
|
278
|
+
<ui.Radio
|
|
279
|
+
value={this.props.segment.orderDir || "asc"}
|
|
280
|
+
radioValue="desc"
|
|
281
|
+
onChange={this.handleOrderDirChange}
|
|
282
|
+
inline={true}
|
|
283
|
+
>
|
|
284
|
+
{T`Descending`}
|
|
285
|
+
</ui.Radio>
|
|
286
|
+
</div>
|
|
287
|
+
) : undefined}
|
|
288
|
+
</ui.FormGroup>
|
|
309
289
|
)
|
|
310
290
|
}
|
|
311
291
|
|
|
312
292
|
render() {
|
|
313
|
-
return
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
293
|
+
return (
|
|
294
|
+
<div>
|
|
295
|
+
{this.renderMode()}
|
|
296
|
+
{this.state.mode ? this.renderLabel() : undefined}
|
|
297
|
+
{this.state.mode === "multiple" ? this.renderValueAxis() : undefined}
|
|
298
|
+
{this.state.mode ? this.renderFilter() : undefined}
|
|
299
|
+
{this.state.mode === "multiple" ? this.renderOrderExpr() : undefined}
|
|
300
|
+
{this.state.mode ? this.renderStyling() : undefined}
|
|
301
|
+
{this.state.mode ? this.renderBorders() : undefined}
|
|
302
|
+
</div>
|
|
323
303
|
)
|
|
324
304
|
}
|
|
325
305
|
}
|
|
@@ -335,21 +315,21 @@ class BorderComponent extends React.Component<BorderComponentProps> {
|
|
|
335
315
|
render() {
|
|
336
316
|
const value = this.props.value != null ? this.props.value : this.props.defaultValue
|
|
337
317
|
|
|
338
|
-
return
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
</
|
|
318
|
+
return (
|
|
319
|
+
<span>
|
|
320
|
+
<Radio inline value={value} radioValue={0} onChange={() => this.props.onChange(0)}>
|
|
321
|
+
{T`None`}
|
|
322
|
+
</Radio>
|
|
323
|
+
<Radio inline value={value} radioValue={1} onChange={() => this.props.onChange(1)}>
|
|
324
|
+
{T`Light`}
|
|
325
|
+
</Radio>
|
|
326
|
+
<Radio inline value={value} radioValue={2} onChange={() => this.props.onChange(2)}>
|
|
327
|
+
{T`Medium`}
|
|
328
|
+
</Radio>
|
|
329
|
+
<Radio inline value={value} radioValue={3} onChange={() => this.props.onChange(3)}>
|
|
330
|
+
{T`Heavy`}
|
|
331
|
+
</Radio>
|
|
332
|
+
</span>
|
|
353
333
|
)
|
|
354
334
|
}
|
|
355
335
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import _ from "lodash"
|
|
2
2
|
import React from "react"
|
|
3
|
-
const R = React.createElement
|
|
4
3
|
import uuid from "uuid"
|
|
5
4
|
import { default as produce } from "immer"
|
|
6
5
|
import { original } from "immer"
|
|
@@ -15,6 +14,8 @@ import { JsonQLSelectQuery } from "@mwater/jsonql"
|
|
|
15
14
|
import { Axis } from "../../../axes/Axis"
|
|
16
15
|
import { JsonQLFilter } from "../../../JsonQLFilter"
|
|
17
16
|
import { Image } from "@mwater/forms/lib/RotationAwareImageComponent"
|
|
17
|
+
import * as XLSX from "xlsx-js-style"
|
|
18
|
+
import Color from "color"
|
|
18
19
|
|
|
19
20
|
export interface TableChartDesign {
|
|
20
21
|
version?: number
|
|
@@ -448,4 +449,179 @@ export default class TableChart extends Chart {
|
|
|
448
449
|
}
|
|
449
450
|
})
|
|
450
451
|
}
|
|
452
|
+
|
|
453
|
+
// Override to indicate this chart supports XLSX export
|
|
454
|
+
supportsXlsxExport(): boolean {
|
|
455
|
+
return true
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Creates a SheetJS workbook for the chart data
|
|
459
|
+
createXlsxWorkbook(design: TableChartDesign, schema: Schema, dataSource: DataSource, data: any, locale: string): any {
|
|
460
|
+
const exprUtils = new ExprUtils(schema)
|
|
461
|
+
const axisBuilder = new AxisBuilder({ schema })
|
|
462
|
+
const workbook = XLSX.utils.book_new()
|
|
463
|
+
|
|
464
|
+
// Create array for headers
|
|
465
|
+
const headers = design.columns.map(column => column.headerText || exprUtils.summarizeExpr(column.textAxis?.expr || null, locale))
|
|
466
|
+
|
|
467
|
+
// Create arrays for data
|
|
468
|
+
const rows = data.main.map((record: any) => {
|
|
469
|
+
return design.columns.map((column: any, columnIndex: number) => {
|
|
470
|
+
const value = record[`c${columnIndex}`]
|
|
471
|
+
if (value == null) {
|
|
472
|
+
return ""
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const exprType = exprUtils.getExprType(column.textAxis?.expr)
|
|
476
|
+
|
|
477
|
+
// Handle images as URLs
|
|
478
|
+
if (exprType === "image" && value) {
|
|
479
|
+
return dataSource.getImageUrl(value.id)
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (exprType === "imagelist" && value) {
|
|
483
|
+
return value.map((img: Image) => dataSource.getImageUrl(img.id)).join(", ")
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Handle dates properly for Excel
|
|
487
|
+
if (exprType === "date" && value) {
|
|
488
|
+
// Convert date string to JavaScript Date object
|
|
489
|
+
const date = new Date(value)
|
|
490
|
+
// Return a date that Excel can properly handle
|
|
491
|
+
return date
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Handle datetime properly for Excel
|
|
495
|
+
if (exprType === "datetime" && value) {
|
|
496
|
+
// Convert datetime string to JavaScript Date object
|
|
497
|
+
const date = new Date(value)
|
|
498
|
+
// Return a date that Excel can properly handle
|
|
499
|
+
return date
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
return exprUtils.stringifyExprLiteral(column.textAxis?.expr, value, locale)
|
|
503
|
+
})
|
|
504
|
+
})
|
|
505
|
+
|
|
506
|
+
// Add summary row if exists
|
|
507
|
+
if (data.summary) {
|
|
508
|
+
const summaryRow = design.columns.map((column: any, columnIndex: number) => {
|
|
509
|
+
if (data.summary[`c${columnIndex}`] != null) {
|
|
510
|
+
return data.summary[`c${columnIndex}`]
|
|
511
|
+
}
|
|
512
|
+
return ""
|
|
513
|
+
})
|
|
514
|
+
rows.push(summaryRow)
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// Create worksheet from data
|
|
518
|
+
const worksheet = XLSX.utils.aoa_to_sheet([headers, ...rows])
|
|
519
|
+
|
|
520
|
+
// Set column widths based on content
|
|
521
|
+
const colWidths = headers.map((header, colIndex) => {
|
|
522
|
+
// Start with header width
|
|
523
|
+
let maxWidth = header.length
|
|
524
|
+
|
|
525
|
+
// Check data rows
|
|
526
|
+
rows.forEach((row: any[]) => {
|
|
527
|
+
const cellValue = row[colIndex]
|
|
528
|
+
if (cellValue) {
|
|
529
|
+
maxWidth = Math.max(maxWidth, String(cellValue).length)
|
|
530
|
+
}
|
|
531
|
+
})
|
|
532
|
+
|
|
533
|
+
// Add some padding and cap at 50 characters
|
|
534
|
+
return Math.min(maxWidth + 2, 50)
|
|
535
|
+
})
|
|
536
|
+
|
|
537
|
+
worksheet['!cols'] = colWidths.map(width => ({ wch: width }))
|
|
538
|
+
|
|
539
|
+
// Apply cell styles and background colors
|
|
540
|
+
const range = XLSX.utils.decode_range(worksheet['!ref']!)
|
|
541
|
+
|
|
542
|
+
for (let rowIndex = 0; rowIndex <= range.e.r; rowIndex++) {
|
|
543
|
+
for (let colIndex = 0; colIndex <= range.e.c; colIndex++) {
|
|
544
|
+
const cellAddress = XLSX.utils.encode_cell({ r: rowIndex, c: colIndex })
|
|
545
|
+
const cell = worksheet[cellAddress]
|
|
546
|
+
|
|
547
|
+
// Skip empty cells
|
|
548
|
+
if (!cell) continue
|
|
549
|
+
|
|
550
|
+
// Initialize style object if needed
|
|
551
|
+
if (!cell.s) cell.s = {}
|
|
552
|
+
|
|
553
|
+
// Apply header styles
|
|
554
|
+
if (rowIndex === 0) {
|
|
555
|
+
cell.s = {
|
|
556
|
+
...cell.s,
|
|
557
|
+
font: { bold: true },
|
|
558
|
+
alignment: { horizontal: "center" },
|
|
559
|
+
border: {
|
|
560
|
+
bottom: { style: "thin" }
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// Apply background colors for data cells
|
|
566
|
+
if (rowIndex > 0 && rowIndex <= data.main.length && colIndex < design.columns.length) {
|
|
567
|
+
const record = data.main[rowIndex - 1]
|
|
568
|
+
const column = design.columns[colIndex]
|
|
569
|
+
|
|
570
|
+
// Check if this column has a background color axis and the bc property exists
|
|
571
|
+
if (column?.backgroundColorAxis && record[`bc${colIndex}`] !== undefined) {
|
|
572
|
+
// Use the Color library to handle the color value safely
|
|
573
|
+
try {
|
|
574
|
+
// Call getValueColor to get the actual color
|
|
575
|
+
const axisBuilder = new AxisBuilder({ schema })
|
|
576
|
+
const bgColor = axisBuilder.getValueColor(column.backgroundColorAxis, record[`bc${colIndex}`])
|
|
577
|
+
|
|
578
|
+
if (bgColor) {
|
|
579
|
+
const color = Color(bgColor)
|
|
580
|
+
const hex = color.hex().substring(1) // Remove #
|
|
581
|
+
|
|
582
|
+
// Apply the background color
|
|
583
|
+
cell.s.fill = {
|
|
584
|
+
patternType: "solid",
|
|
585
|
+
fgColor: { rgb: hex },
|
|
586
|
+
bgColor: { rgb: hex }
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// Adjust text color based on background lightness
|
|
590
|
+
const lightness = color.luminosity()
|
|
591
|
+
cell.s.font = {
|
|
592
|
+
...cell.s.font,
|
|
593
|
+
color: { rgb: lightness < 0.3 ? "CCCCCC" : "000000" }
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
} catch (e) {
|
|
597
|
+
// Silently ignore color errors
|
|
598
|
+
console.error("Error applying background color", e)
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// Apply summary row styling
|
|
604
|
+
if (data.summary && rowIndex === rows.length) {
|
|
605
|
+
cell.s = {
|
|
606
|
+
...cell.s,
|
|
607
|
+
font: { bold: true },
|
|
608
|
+
border: {
|
|
609
|
+
top: { style: "thin" }
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
console.log(worksheet)
|
|
617
|
+
|
|
618
|
+
// Add the worksheet to the workbook
|
|
619
|
+
XLSX.utils.book_append_sheet(workbook, worksheet, "Data")
|
|
620
|
+
|
|
621
|
+
// Convert workbook to blob
|
|
622
|
+
const workbookBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' })
|
|
623
|
+
const blob = new Blob([workbookBuffer], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" })
|
|
624
|
+
|
|
625
|
+
return blob
|
|
626
|
+
}
|
|
451
627
|
}
|