@mwater/visualization 5.4.1 → 5.4.2
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/lib/ColorComponent.js +2 -1
- package/lib/IdSelection.d.ts +16 -0
- package/lib/IdSelection.js +59 -0
- package/lib/MWaterAddRelatedIndicatorComponent.js +2 -2
- package/lib/MWaterCompleteTableSelectComponent.d.ts +3 -8
- package/lib/MWaterCompleteTableSelectComponent.js +36 -42
- package/lib/MWaterLoaderComponent.d.ts +11 -10
- package/lib/MWaterLoaderComponent.js +1 -1
- package/lib/MWaterResponsesFilterComponent.js +1 -1
- package/lib/MWaterTableSelectComponent.d.ts +0 -1
- package/lib/MWaterTableSelectComponent.js +4 -6
- package/lib/autotranslate.d.ts +20 -0
- package/lib/autotranslate.js +122 -0
- package/lib/axes/AxisBuilder.js +3 -3
- package/lib/axes/AxisColorEditorComponent.js +4 -0
- package/lib/axes/AxisComponent.d.ts +8 -12
- package/lib/axes/AxisComponent.js +32 -80
- package/lib/axes/CategoryMapComponent.js +4 -4
- package/lib/axes/RangesComponent.js +2 -2
- package/lib/dashboards/DashboardComponent.d.ts +6 -0
- package/lib/dashboards/DashboardComponent.js +44 -12
- package/lib/dashboards/DashboardDesign.d.ts +11 -2
- package/lib/dashboards/DashboardUtils.d.ts +5 -0
- package/lib/dashboards/DashboardUtils.js +30 -0
- package/lib/dashboards/DashboardViewComponent.d.ts +2 -0
- package/lib/dashboards/DashboardViewComponent.js +16 -3
- package/lib/dashboards/ServerDashboardDataSource.js +2 -1
- package/lib/dashboards/SettingsModalComponent.d.ts +1 -1
- package/lib/dashboards/SettingsModalComponent.js +256 -19
- package/lib/dashboards/WidgetComponent.d.ts +6 -3
- package/lib/dashboards/WidgetComponent.js +3 -1
- package/lib/datagrids/CellEditor.d.ts +19 -0
- package/lib/datagrids/CellEditor.js +223 -0
- package/lib/datagrids/DatagridComponent.d.ts +18 -87
- package/lib/datagrids/DatagridComponent.js +304 -222
- package/lib/datagrids/DatagridViewComponent.d.ts +15 -53
- package/lib/datagrids/DatagridViewComponent.js +256 -257
- package/lib/datagrids/DirectDatagridDataSource.js +2 -3
- package/lib/datagrids/ExprCellComponent.d.ts +8 -15
- package/lib/datagrids/ExprCellComponent.js +11 -15
- package/lib/datagrids/FindReplaceModalComponent.d.ts +4 -6
- package/lib/datagrids/FindReplaceModalComponent.js +38 -75
- package/lib/index.css +1 -1
- package/lib/index.d.ts +0 -1
- package/lib/index.js +0 -1
- package/lib/layouts/blocks/HorizontalBlockComponent.js +2 -2
- package/lib/mWaterLoader.d.ts +1 -1
- package/lib/maps/BufferLayer.d.ts +7 -5
- package/lib/maps/BufferLayer.js +69 -48
- package/lib/maps/BufferLayerDesign.d.ts +21 -14
- package/lib/maps/BufferLayerDesignerComponent.d.ts +16 -31
- package/lib/maps/BufferLayerDesignerComponent.js +68 -102
- package/lib/maps/ChoroplethLayer.d.ts +5 -4
- package/lib/maps/ChoroplethLayer.js +32 -9
- package/lib/maps/ChoroplethLayerDesign.d.ts +6 -2
- package/lib/maps/ChoroplethLayerDesigner.js +4 -2
- package/lib/maps/ClusterLayer.d.ts +3 -4
- package/lib/maps/ClusterLayer.js +2 -1
- package/lib/maps/DetailLevelSelectComponent.js +1 -1
- package/lib/maps/DirectMapDataSource.js +2 -1
- package/lib/maps/EditPopupComponent.js +5 -3
- package/lib/maps/GridLayer.d.ts +3 -4
- package/lib/maps/GridLayer.js +2 -1
- package/lib/maps/GridLayerDesigner.js +5 -3
- package/lib/maps/HoverContent.d.ts +11 -3
- package/lib/maps/HoverContent.js +25 -9
- package/lib/maps/Layer.d.ts +24 -3
- package/lib/maps/Layer.js +5 -1
- package/lib/maps/LayerFactory.js +0 -8
- package/lib/maps/LayerLegendComponent.js +0 -1
- package/lib/maps/LayerSwitcherComponent.d.ts +1 -0
- package/lib/maps/LayerSwitcherComponent.js +1 -1
- package/lib/maps/LeafletMapComponent.js +3 -1
- package/lib/maps/LegendComponent.d.ts +1 -0
- package/lib/maps/LegendComponent.js +9 -1
- package/lib/maps/MWaterServerLayer.d.ts +2 -2
- package/lib/maps/MWaterServerLayer.js +2 -2
- package/lib/maps/MapComponent.js +3 -3
- package/lib/maps/MapDesign.d.ts +2 -0
- package/lib/maps/MapDesignerComponent.d.ts +4 -3
- package/lib/maps/MapDesignerComponent.js +68 -74
- package/lib/maps/MapLayerViewDesignerComponent.js +2 -2
- package/lib/maps/MapUtils.d.ts +4 -0
- package/lib/maps/MapUtils.js +19 -0
- package/lib/maps/MapViewComponent.d.ts +8 -3
- package/lib/maps/MarkersLayer.d.ts +5 -4
- package/lib/maps/MarkersLayer.js +33 -7
- package/lib/maps/MarkersLayerDesign.d.ts +19 -16
- package/lib/maps/PopupFilterJoinsUtils.d.ts +6 -3
- package/lib/maps/PopupFilterJoinsUtils.js +0 -6
- package/lib/maps/RasterMapViewComponent.d.ts +3 -31
- package/lib/maps/RasterMapViewComponent.js +7 -2
- package/lib/maps/ServerMapDataSource.js +2 -1
- package/lib/maps/SwitchableTileUrlLayer.d.ts +3 -3
- package/lib/maps/SwitchableTileUrlLayer.js +2 -1
- package/lib/maps/TileUrlLayer.d.ts +4 -5
- package/lib/maps/TileUrlLayer.js +2 -1
- package/lib/maps/VectorMapViewComponent.d.ts +5 -37
- package/lib/maps/VectorMapViewComponent.js +19 -8
- package/lib/maps/maps.d.ts +3 -0
- package/lib/quickfilter/QuickfiltersComponent.d.ts +2 -0
- package/lib/quickfilter/QuickfiltersComponent.js +9 -7
- package/lib/quickfilter/QuickfiltersDesignComponent.d.ts +1 -1
- package/lib/quickfilter/QuickfiltersDesignComponent.js +19 -35
- package/lib/richtext/ExprItemsHtmlConverter.d.ts +5 -2
- package/lib/richtext/ExprItemsHtmlConverter.js +4 -4
- package/lib/richtext/ExprItemsTranslator.d.ts +5 -0
- package/lib/richtext/ExprItemsTranslator.js +149 -0
- package/lib/richtext/ItemsHtmlConverter.d.ts +1 -1
- package/lib/richtext/ItemsHtmlConverter.js +31 -15
- package/lib/wellknown.js +12 -9
- package/lib/widgets/IFrameWidget.d.ts +4 -4
- package/lib/widgets/ImageWidget.d.ts +7 -4
- package/lib/widgets/ImageWidget.js +9 -1
- package/lib/widgets/ImageWidgetComponent.d.ts +1 -0
- package/lib/widgets/ImageWidgetComponent.js +1 -1
- package/lib/widgets/MapWidget.d.ts +5 -48
- package/lib/widgets/MapWidget.js +26 -63
- package/lib/widgets/MarkdownWidget.d.ts +3 -0
- package/lib/widgets/MarkdownWidget.js +3 -0
- package/lib/widgets/TOCWidget.d.ts +15 -27
- package/lib/widgets/TOCWidget.js +107 -183
- package/lib/widgets/Widget.d.ts +18 -7
- package/lib/widgets/Widget.js +4 -0
- package/lib/widgets/WidgetScopesViewComponent.js +1 -1
- package/lib/widgets/charts/Chart.d.ts +10 -1
- package/lib/widgets/charts/Chart.js +22 -11
- package/lib/widgets/charts/ChartViewComponent.d.ts +4 -0
- package/lib/widgets/charts/ChartViewComponent.js +6 -3
- package/lib/widgets/charts/ChartWidget.d.ts +2 -0
- package/lib/widgets/charts/ChartWidget.js +9 -1
- package/lib/widgets/charts/ChartWidgetComponent.d.ts +4 -0
- package/lib/widgets/charts/ChartWidgetComponent.js +2 -2
- package/lib/widgets/charts/calendar/CalendarChart.d.ts +1 -0
- package/lib/widgets/charts/calendar/CalendarChart.js +26 -0
- package/lib/widgets/charts/calendar/CalendarChartViewComponent.js +3 -1
- package/lib/widgets/charts/imagemosaic/ImageMosaicChart.d.ts +1 -0
- package/lib/widgets/charts/imagemosaic/ImageMosaicChart.js +8 -0
- package/lib/widgets/charts/layered/LayeredChart.d.ts +2 -0
- package/lib/widgets/charts/layered/LayeredChart.js +63 -3
- package/lib/widgets/charts/layered/LayeredChartCompiler.d.ts +1 -1
- package/lib/widgets/charts/layered/LayeredChartCompiler.js +1 -1
- package/lib/widgets/charts/layered/LayeredChartDesignerComponent.js +2 -2
- package/lib/widgets/charts/layered/LayeredChartViewComponent.js +8 -3
- package/lib/widgets/charts/pivot/PivotChart.d.ts +1 -0
- package/lib/widgets/charts/pivot/PivotChart.js +63 -0
- package/lib/widgets/charts/pivot/PivotChartLayoutComponent.js +1 -1
- package/lib/widgets/charts/pivot/SegmentDesignerComponent.js +7 -4
- package/lib/widgets/charts/table/OrderingsComponent.js +1 -1
- package/lib/widgets/charts/table/TableChart.d.ts +1 -0
- package/lib/widgets/charts/table/TableChart.js +15 -0
- package/lib/widgets/text/TextComponent.d.ts +11 -4
- package/lib/widgets/text/TextComponent.js +11 -8
- package/lib/widgets/text/TextWidget.d.ts +6 -3
- package/lib/widgets/text/TextWidget.js +7 -1
- package/lib/widgets/text/TextWidgetComponent.d.ts +4 -0
- package/lib/widgets/text/TextWidgetComponent.js +7 -1
- package/lib/widgets/text/TextWidgetDesign.d.ts +2 -4
- package/lib/widgets/text/TextWidgetDesign.js +1 -1
- package/package.json +7 -8
- package/src/ColorComponent.tsx +1 -2
- package/src/IdSelection.ts +62 -0
- package/src/MWaterAddRelatedIndicatorComponent.ts +3 -2
- package/src/MWaterCompleteTableSelectComponent.tsx +36 -46
- package/src/MWaterLoaderComponent.ts +28 -26
- package/src/MWaterResponsesFilterComponent.ts +5 -2
- package/src/MWaterTableSelectComponent.tsx +5 -9
- package/src/autotranslate.ts +141 -0
- package/src/axes/AxisBuilder.ts +3 -3
- package/src/axes/AxisColorEditorComponent.tsx +5 -0
- package/src/axes/{AxisComponent.ts → AxisComponent.tsx} +106 -106
- package/src/axes/CategoryMapComponent.ts +4 -4
- package/src/axes/RangesComponent.ts +3 -2
- package/src/dashboards/DashboardComponent.tsx +79 -14
- package/src/dashboards/DashboardDesign.ts +9 -2
- package/src/dashboards/DashboardUtils.ts +39 -0
- package/src/dashboards/DashboardViewComponent.tsx +22 -3
- package/src/dashboards/ServerDashboardDataSource.ts +2 -1
- package/src/dashboards/SettingsModalComponent.tsx +450 -35
- package/src/dashboards/WidgetComponent.tsx +12 -6
- package/src/datagrids/CellEditor.tsx +354 -0
- package/src/datagrids/DatagridComponent.tsx +646 -0
- package/src/datagrids/DatagridViewComponent.tsx +539 -0
- package/src/datagrids/DirectDatagridDataSource.ts +2 -3
- package/src/datagrids/{ExprCellComponent.ts → ExprCellComponent.tsx} +28 -23
- package/src/datagrids/{FindReplaceModalComponent.ts → FindReplaceModalComponent.tsx} +109 -122
- package/src/index.css +1 -1
- package/src/index.ts +0 -1
- package/src/layouts/blocks/HorizontalBlockComponent.ts +2 -2
- package/src/mWaterLoader.ts +1 -1
- package/src/maps/BufferLayer.ts +83 -60
- package/src/maps/BufferLayerDesign.ts +20 -14
- package/src/maps/BufferLayerDesignerComponent.tsx +309 -0
- package/src/maps/ChoroplethLayer.ts +40 -19
- package/src/maps/ChoroplethLayerDesign.ts +4 -2
- package/src/maps/ChoroplethLayerDesigner.tsx +4 -2
- package/src/maps/ClusterLayer.ts +4 -10
- package/src/maps/DetailLevelSelectComponent.ts +1 -1
- package/src/maps/DirectMapDataSource.ts +2 -1
- package/src/maps/EditPopupComponent.ts +7 -3
- package/src/maps/GridLayer.ts +4 -10
- package/src/maps/GridLayerDesigner.tsx +5 -3
- package/src/maps/HoverContent.tsx +40 -16
- package/src/maps/Layer.ts +28 -10
- package/src/maps/LayerFactory.ts +0 -8
- package/src/maps/LayerLegendComponent.ts +2 -4
- package/src/maps/LayerSwitcherComponent.tsx +6 -2
- package/src/maps/LeafletMapComponent.tsx +3 -1
- package/src/maps/LegendComponent.tsx +10 -1
- package/src/maps/MWaterServerLayer.ts +3 -3
- package/src/maps/MapComponent.ts +3 -3
- package/src/maps/MapDesign.ts +3 -0
- package/src/maps/MapDesignerComponent.tsx +165 -162
- package/src/maps/MapLayerViewDesignerComponent.ts +2 -2
- package/src/maps/MapUtils.ts +24 -0
- package/src/maps/MapViewComponent.tsx +11 -3
- package/src/maps/MarkersLayer.ts +44 -18
- package/src/maps/MarkersLayerDesign.ts +19 -16
- package/src/maps/PopupFilterJoinsUtils.ts +6 -2
- package/src/maps/RasterMapViewComponent.ts +9 -45
- package/src/maps/ServerMapDataSource.ts +2 -2
- package/src/maps/SwitchableTileUrlLayer.tsx +4 -10
- package/src/maps/TileUrlLayer.tsx +4 -10
- package/src/maps/VectorMapViewComponent.tsx +28 -55
- package/src/maps/maps.ts +3 -0
- package/src/quickfilter/QuickfiltersComponent.ts +13 -7
- package/src/quickfilter/QuickfiltersDesignComponent.tsx +56 -74
- package/src/richtext/ExprItemsHtmlConverter.ts +9 -5
- package/src/richtext/ExprItemsTranslator.ts +176 -0
- package/src/richtext/ItemsHtmlConverter.ts +33 -18
- package/src/wellknown.ts +33 -30
- package/src/widgets/ImageWidget.ts +10 -1
- package/src/widgets/ImageWidgetComponent.ts +3 -2
- package/src/widgets/{MapWidget.ts → MapWidget.tsx} +90 -101
- package/src/widgets/MarkdownWidget.ts +3 -0
- package/src/widgets/TOCWidget.tsx +281 -0
- package/src/widgets/Widget.ts +25 -5
- package/src/widgets/WidgetScopesViewComponent.ts +2 -1
- package/src/widgets/charts/Chart.ts +31 -12
- package/src/widgets/charts/ChartViewComponent.ts +13 -3
- package/src/widgets/charts/ChartWidget.ts +11 -1
- package/src/widgets/charts/ChartWidgetComponent.tsx +9 -1
- package/src/widgets/charts/calendar/CalendarChart.ts +29 -0
- package/src/widgets/charts/calendar/CalendarChartViewComponent.tsx +3 -1
- package/src/widgets/charts/imagemosaic/ImageMosaicChart.ts +9 -0
- package/src/widgets/charts/layered/LayeredChart.ts +71 -3
- package/src/widgets/charts/layered/LayeredChartCompiler.ts +2 -2
- package/src/widgets/charts/layered/LayeredChartDesignerComponent.tsx +4 -2
- package/src/widgets/charts/layered/LayeredChartViewComponent.ts +10 -4
- package/src/widgets/charts/pivot/PivotChart.ts +73 -0
- package/src/widgets/charts/pivot/PivotChartLayoutComponent.tsx +1 -1
- package/src/widgets/charts/pivot/SegmentDesignerComponent.tsx +6 -4
- package/src/widgets/charts/table/OrderingsComponent.tsx +2 -1
- package/src/widgets/charts/table/TableChart.ts +17 -0
- package/src/widgets/text/TextComponent.tsx +22 -12
- package/src/widgets/text/TextWidget.ts +9 -2
- package/src/widgets/text/TextWidgetComponent.tsx +16 -1
- package/src/widgets/text/TextWidgetDesign.ts +4 -7
- package/test/IdSelectionTests.ts +54 -0
- package/test/LayeredChartCompilerTests.ts +0 -2
- package/test/richtext/ExprItemsTranslatorTests.ts +144 -0
- package/test/wellknownTests.ts +144 -0
- package/src/datagrids/DatagridComponent.ts +0 -478
- package/src/datagrids/DatagridViewComponent.ts +0 -464
- package/src/datagrids/EditExprCellComponent.tsx +0 -305
- package/src/datagrids/README.md +0 -3
- package/src/maps/BufferLayerDesignerComponent.ts +0 -311
- package/src/widgets/TOCWidget.ts +0 -326
- package/test/LegoLayoutEngineTests.ts +0 -69
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import _ from "lodash"
|
|
2
|
-
import React, { useMemo } from "react"
|
|
2
|
+
import React, { useMemo, useRef, useState } from "react"
|
|
3
3
|
import { languages } from "../languages"
|
|
4
4
|
import * as ui from "@mwater/react-library/lib/bootstrap"
|
|
5
5
|
import { default as ReactSelect } from "react-select"
|
|
@@ -7,13 +7,17 @@ import * as DashboardUtils from "./DashboardUtils"
|
|
|
7
7
|
import ActionCancelModalComponent from "@mwater/react-library/lib/ActionCancelModalComponent"
|
|
8
8
|
import QuickfiltersDesignComponent from "../quickfilter/QuickfiltersDesignComponent"
|
|
9
9
|
import FiltersDesignerComponent from "../FiltersDesignerComponent"
|
|
10
|
-
import { DataSource, Schema } from "@mwater/expressions"
|
|
10
|
+
import { DataSource, LocalizedString, Schema } from "@mwater/expressions"
|
|
11
11
|
import { DashboardDesign } from "./DashboardDesign"
|
|
12
12
|
import { GlobalFiltersElementFactoryContext } from "../MWaterContextComponent"
|
|
13
13
|
import produce from "immer"
|
|
14
|
+
import TabbedComponent from "@mwater/react-library/lib/TabbedComponent"
|
|
15
|
+
import FileSaver from "file-saver"
|
|
16
|
+
import * as localizeUtils from "ez-localize/lib/utils"
|
|
17
|
+
import { canAutoTranslate, translateStrings } from "../autotranslate"
|
|
14
18
|
|
|
15
19
|
export interface SettingsModalComponentProps {
|
|
16
|
-
onDesignChange:
|
|
20
|
+
onDesignChange: (design: DashboardDesign) => void
|
|
17
21
|
schema: Schema
|
|
18
22
|
dataSource: DataSource
|
|
19
23
|
}
|
|
@@ -39,7 +43,7 @@ export default class SettingsModalComponent extends React.Component<
|
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
handleSave = () => {
|
|
42
|
-
this.props.onDesignChange(this.state.design)
|
|
46
|
+
this.props.onDesignChange(this.state.design!)
|
|
43
47
|
return this.setState({ design: null })
|
|
44
48
|
}
|
|
45
49
|
|
|
@@ -85,22 +89,57 @@ interface SettingsComponentProps {
|
|
|
85
89
|
function SettingsComponent(props: SettingsComponentProps) {
|
|
86
90
|
const { design, onDesignChange, schema, dataSource } = props
|
|
87
91
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
92
|
+
const tabs = [
|
|
93
|
+
{
|
|
94
|
+
id: "filters",
|
|
95
|
+
label: T`Filters`,
|
|
96
|
+
elem: (
|
|
97
|
+
<FiltersTab
|
|
98
|
+
design={design}
|
|
99
|
+
onDesignChange={onDesignChange}
|
|
100
|
+
schema={schema}
|
|
101
|
+
dataSource={dataSource}
|
|
102
|
+
/>
|
|
103
|
+
)
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: "language",
|
|
107
|
+
label: T`Translation`,
|
|
108
|
+
elem: (
|
|
109
|
+
<LanguageTab
|
|
110
|
+
design={design}
|
|
111
|
+
onDesignChange={onDesignChange}
|
|
112
|
+
schema={schema}
|
|
113
|
+
/>
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
]
|
|
99
117
|
|
|
100
118
|
return (
|
|
101
119
|
<div style={{ paddingBottom: 200 }}>
|
|
120
|
+
<TabbedComponent
|
|
121
|
+
tabs={tabs}
|
|
122
|
+
initialTabId="filters"
|
|
123
|
+
/>
|
|
124
|
+
</div>
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
interface FiltersTabProps {
|
|
129
|
+
design: DashboardDesign
|
|
130
|
+
onDesignChange: (design: DashboardDesign) => void
|
|
131
|
+
schema: Schema
|
|
132
|
+
dataSource: DataSource
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function FiltersTab({ design, onDesignChange, schema, dataSource }: FiltersTabProps) {
|
|
136
|
+
// Get filterable tables
|
|
137
|
+
const filterableTables = useMemo(() => DashboardUtils.getFilterableTables(design, schema), [design, schema])
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<>
|
|
102
141
|
<h4>{T`Quick Filters`}</h4>
|
|
103
|
-
<div className="
|
|
142
|
+
<div className="mb-2">
|
|
104
143
|
{T`Quick filters are shown to the user as a dropdown at the top of the dashboard and can be used to filter data of widgets.`}
|
|
105
144
|
</div>
|
|
106
145
|
|
|
@@ -120,8 +159,8 @@ function SettingsComponent(props: SettingsComponentProps) {
|
|
|
120
159
|
T`Nothing to quickfilter. Add widgets to the dashboard`
|
|
121
160
|
)}
|
|
122
161
|
|
|
123
|
-
<h4 style={{ paddingTop:
|
|
124
|
-
<div className="
|
|
162
|
+
<h4 style={{ paddingTop: 15 }}>{T`Filters`}</h4>
|
|
163
|
+
<div className="mb-2">
|
|
125
164
|
{T`Filters are built in to the dashboard and cannot be changed by viewers of the dashboard.`}
|
|
126
165
|
</div>
|
|
127
166
|
|
|
@@ -145,7 +184,7 @@ function SettingsComponent(props: SettingsComponentProps) {
|
|
|
145
184
|
{globalFiltersElementFactory =>
|
|
146
185
|
globalFiltersElementFactory ? (
|
|
147
186
|
<div>
|
|
148
|
-
<h4 style={{ paddingTop:
|
|
187
|
+
<h4 style={{ paddingTop: 15 }}>{T`Global Filters`}</h4>
|
|
149
188
|
{globalFiltersElementFactory({
|
|
150
189
|
schema,
|
|
151
190
|
dataSource,
|
|
@@ -161,10 +200,207 @@ function SettingsComponent(props: SettingsComponentProps) {
|
|
|
161
200
|
) : undefined
|
|
162
201
|
}
|
|
163
202
|
</GlobalFiltersElementFactoryContext.Consumer>
|
|
203
|
+
{design.implicitFiltersEnabled && (
|
|
204
|
+
<>
|
|
205
|
+
<h4 style={{ paddingTop: 10 }}>{T`Advanced`}</h4>
|
|
206
|
+
<ui.Checkbox
|
|
207
|
+
value={design.implicitFiltersEnabled != null ? design.implicitFiltersEnabled : true}
|
|
208
|
+
onChange={(value: boolean) =>
|
|
209
|
+
onDesignChange(produce(design, draft => {
|
|
210
|
+
draft.implicitFiltersEnabled = value
|
|
211
|
+
}))
|
|
212
|
+
}
|
|
213
|
+
>
|
|
214
|
+
{T`Enable Implicit Filtering (leave unchecked for new dashboards)`}
|
|
215
|
+
</ui.Checkbox>
|
|
216
|
+
</>
|
|
217
|
+
)}
|
|
218
|
+
</>
|
|
219
|
+
)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
interface LanguageTabProps {
|
|
223
|
+
design: DashboardDesign
|
|
224
|
+
onDesignChange: (design: DashboardDesign) => void
|
|
225
|
+
schema: Schema
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function LanguageTab({ design, onDesignChange, schema }: LanguageTabProps) {
|
|
229
|
+
const fileInputRef = useRef<HTMLInputElement>(null)
|
|
230
|
+
|
|
231
|
+
const locale = design.locale || "en"
|
|
232
|
+
|
|
233
|
+
const localeOptions = useMemo(() => {
|
|
234
|
+
return _.sortBy(
|
|
235
|
+
_.map(languages, (language) => ({
|
|
236
|
+
value: language.code,
|
|
237
|
+
label: `${language.en} (${language.name})`
|
|
238
|
+
})),
|
|
239
|
+
'label'
|
|
240
|
+
)
|
|
241
|
+
}, [languages])
|
|
242
|
+
|
|
243
|
+
// Get available languages that aren't already selected
|
|
244
|
+
const availableLocaleOptions = useMemo(() => {
|
|
245
|
+
const selectedLocales = new Set([design.locale, ...(design.otherLocales || [])])
|
|
246
|
+
return localeOptions.filter(opt => !selectedLocales.has(opt.value))
|
|
247
|
+
}, [localeOptions, design.locale, design.otherLocales])
|
|
248
|
+
|
|
249
|
+
const translatableStrings = useMemo(() => DashboardUtils.getTranslatableStringsFromDashboard(design, schema), [design, schema])
|
|
250
|
+
|
|
251
|
+
// Calculate percentage of strings translated for each locale
|
|
252
|
+
const translationPercentages = useMemo(() => {
|
|
253
|
+
const percentages: { [locale: string]: number } = {}
|
|
254
|
+
const totalStrings = translatableStrings.length
|
|
255
|
+
|
|
256
|
+
for (const locale of design.otherLocales || []) {
|
|
257
|
+
const translatedCount = translatableStrings.filter(str =>
|
|
258
|
+
design.translations?.[locale]?.[str] != null
|
|
259
|
+
).length
|
|
260
|
+
|
|
261
|
+
// Round down to nearest percent
|
|
262
|
+
percentages[locale] = Math.floor((translatedCount / totalStrings) * 100)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return percentages
|
|
266
|
+
}, [design.translations, design.otherLocales, translatableStrings])
|
|
267
|
+
|
|
268
|
+
const handleAddLocale = (locale: any) => {
|
|
269
|
+
onDesignChange(produce(design, draft => {
|
|
270
|
+
draft.otherLocales = [...(draft.otherLocales || []), locale.value]
|
|
271
|
+
// Initialize empty translations object if needed
|
|
272
|
+
if (!draft.translations) {
|
|
273
|
+
draft.translations = {}
|
|
274
|
+
}
|
|
275
|
+
if (!draft.translations[locale.value]) {
|
|
276
|
+
draft.translations[locale.value] = {}
|
|
277
|
+
}
|
|
278
|
+
}))
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const handleRemoveLocale = (localeToRemove: string) => {
|
|
282
|
+
onDesignChange(produce(design, draft => {
|
|
283
|
+
draft.otherLocales = (draft.otherLocales || []).filter(locale => locale !== localeToRemove)
|
|
284
|
+
// Remove translations for this locale
|
|
285
|
+
if (draft.translations) {
|
|
286
|
+
delete draft.translations[localeToRemove]
|
|
287
|
+
}
|
|
288
|
+
}))
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Convert dashboard translations to LocalizedString format
|
|
292
|
+
const getLocalizedStrings = (): LocalizedString[] => {
|
|
293
|
+
// For each string, create a localized string
|
|
294
|
+
const localizedStrings: LocalizedString[] = []
|
|
295
|
+
for (const str of translatableStrings) {
|
|
296
|
+
const localizedString: LocalizedString = { _base: locale }
|
|
297
|
+
localizedString[locale] = str
|
|
298
|
+
|
|
299
|
+
// Only add translations for other locales if they exist
|
|
300
|
+
for (const otherLocale of design.otherLocales || []) {
|
|
301
|
+
if (design.translations?.[otherLocale]?.[str]) {
|
|
302
|
+
localizedString[otherLocale] = design.translations?.[otherLocale]?.[str]
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
localizedStrings.push(localizedString)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return localizedStrings
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Convert LocalizedString format back to dashboard translations
|
|
313
|
+
const updateFromLocalizedStrings = (localizedStrings: LocalizedString[]) => {
|
|
314
|
+
const newTranslations: { [locale: string]: { [key: string]: string } } = {}
|
|
315
|
+
|
|
316
|
+
// Get all locales except base
|
|
317
|
+
const locales = design.otherLocales || []
|
|
318
|
+
|
|
319
|
+
// Initialize translations object
|
|
320
|
+
for (const locale of locales) {
|
|
321
|
+
newTranslations[locale] = {}
|
|
322
|
+
}
|
|
164
323
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
324
|
+
// Add all translations
|
|
325
|
+
for (const str of localizedStrings) {
|
|
326
|
+
for (const locale of locales) {
|
|
327
|
+
if (str[locale]) {
|
|
328
|
+
newTranslations[locale][str[str._base]] = str[locale]
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return newTranslations
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const handleDownload = () => {
|
|
337
|
+
// Get strings in LocalizedString format
|
|
338
|
+
const strings = getLocalizedStrings()
|
|
339
|
+
|
|
340
|
+
// Create xlsx base64 using all locales (primary + other)
|
|
341
|
+
const locales = [{ code: locale, name: locale }]
|
|
342
|
+
.concat((design.otherLocales || []).map(code => ({ code, name: code })))
|
|
343
|
+
|
|
344
|
+
const base64 = localizeUtils.exportXlsx(locales, strings)
|
|
345
|
+
|
|
346
|
+
// Download
|
|
347
|
+
FileSaver.saveAs(
|
|
348
|
+
b64toBlob(base64, "application/octet-stream"),
|
|
349
|
+
"Dashboard Translations.xlsx"
|
|
350
|
+
)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const handleUpload = () => {
|
|
354
|
+
fileInputRef.current?.click()
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const handleUploadChange = (evt: React.ChangeEvent<HTMLInputElement>) => {
|
|
358
|
+
const reader = new FileReader()
|
|
359
|
+
|
|
360
|
+
reader.onload = (file) => {
|
|
361
|
+
if (!file.target?.result) {
|
|
362
|
+
return
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const base64 = (file.target.result as string).split(",")[1]
|
|
366
|
+
|
|
367
|
+
try {
|
|
368
|
+
// Create locales array for import
|
|
369
|
+
const locales = [{ code: locale, name: locale }]
|
|
370
|
+
.concat((design.otherLocales || []).map(code => ({ code, name: code })))
|
|
371
|
+
|
|
372
|
+
// Import updates
|
|
373
|
+
const updates = localizeUtils.importXlsx(locales, base64)
|
|
374
|
+
|
|
375
|
+
// If nothing localized
|
|
376
|
+
if (updates.length === 0) {
|
|
377
|
+
alert(T`No translation data found in file`)
|
|
378
|
+
return
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Convert back to dashboard format and update
|
|
382
|
+
const newTranslations = updateFromLocalizedStrings(updates)
|
|
383
|
+
onDesignChange(produce(design, draft => {
|
|
384
|
+
draft.translations = newTranslations
|
|
385
|
+
}))
|
|
386
|
+
|
|
387
|
+
alert(T`${updates.length} translations applied`)
|
|
388
|
+
} catch (error) {
|
|
389
|
+
console.error("Invalid xlsx file:", error)
|
|
390
|
+
alert(T`Invalid xlsx file`)
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (evt.target.files?.[0]) {
|
|
395
|
+
reader.readAsDataURL(evt.target.files[0])
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return (
|
|
400
|
+
<>
|
|
401
|
+
<h4>{T`Base Language`}</h4>
|
|
402
|
+
<div className="mb-2">
|
|
403
|
+
{T`This is the language that the dashboard is written in.`}
|
|
168
404
|
</div>
|
|
169
405
|
|
|
170
406
|
<ReactSelect
|
|
@@ -177,21 +413,200 @@ function SettingsComponent(props: SettingsComponentProps) {
|
|
|
177
413
|
}
|
|
178
414
|
/>
|
|
179
415
|
|
|
180
|
-
{
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
416
|
+
<h4 className="mt-4">{T`Additional Languages`}</h4>
|
|
417
|
+
<div className="mb-2">
|
|
418
|
+
{T`Add languages that this dashboard will be translated into`}
|
|
419
|
+
</div>
|
|
420
|
+
|
|
421
|
+
{/* Show current additional languages */}
|
|
422
|
+
<table>
|
|
423
|
+
<tbody>
|
|
424
|
+
{(design.otherLocales || []).map(locale => {
|
|
425
|
+
const localeOption = _.findWhere(localeOptions, { value: locale })
|
|
426
|
+
if (!localeOption) {
|
|
427
|
+
return null
|
|
189
428
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
429
|
+
|
|
430
|
+
return (
|
|
431
|
+
<tr key={locale}>
|
|
432
|
+
<td style={{ paddingRight: 10 }}>{localeOption.label}</td>
|
|
433
|
+
<td className={translationPercentages[locale] === 100 ? "text-success" : "text-warning"} style={{ textAlign: "right" }}>
|
|
434
|
+
{T`${translationPercentages[locale]}% translated`}
|
|
435
|
+
</td>
|
|
436
|
+
<td>
|
|
437
|
+
{translationPercentages[locale] < 100 && (
|
|
438
|
+
<AutoTranslateLink
|
|
439
|
+
locale={locale}
|
|
440
|
+
baseLocale={design.locale || "en"}
|
|
441
|
+
strings={translatableStrings}
|
|
442
|
+
existingTranslations={design.translations?.[locale] || {}}
|
|
443
|
+
onTranslationsChange={(newTranslations) => {
|
|
444
|
+
onDesignChange(produce(design, draft => {
|
|
445
|
+
if (!draft.translations) {
|
|
446
|
+
draft.translations = {}
|
|
447
|
+
}
|
|
448
|
+
draft.translations[locale] = newTranslations
|
|
449
|
+
}))
|
|
450
|
+
}}
|
|
451
|
+
/>
|
|
452
|
+
)}
|
|
453
|
+
</td>
|
|
454
|
+
<td>
|
|
455
|
+
<button
|
|
456
|
+
type="button"
|
|
457
|
+
className="btn btn-sm btn-link"
|
|
458
|
+
onClick={() => handleRemoveLocale(locale)}
|
|
459
|
+
>
|
|
460
|
+
<i className="fa fa-times" />
|
|
461
|
+
</button>
|
|
462
|
+
</td>
|
|
463
|
+
</tr>
|
|
464
|
+
)
|
|
465
|
+
})}
|
|
466
|
+
</tbody>
|
|
467
|
+
</table>
|
|
468
|
+
|
|
469
|
+
{/* Add new language dropdown */}
|
|
470
|
+
{availableLocaleOptions.length > 0 && (
|
|
471
|
+
<div className="mt-3">
|
|
472
|
+
<ReactSelect
|
|
473
|
+
value={null}
|
|
474
|
+
options={availableLocaleOptions}
|
|
475
|
+
onChange={handleAddLocale}
|
|
476
|
+
placeholder={T`Add language...`}
|
|
477
|
+
/>
|
|
478
|
+
</div>
|
|
479
|
+
)}
|
|
480
|
+
|
|
481
|
+
{/* Add translation management section if there are additional languages */}
|
|
482
|
+
{(design.otherLocales || []).length > 0 && (
|
|
483
|
+
<>
|
|
484
|
+
<h4 className="mt-4">{T`Manage Translations`}</h4>
|
|
485
|
+
<p>{T`Download and re-upload an Excel spreadsheet of text to translate:`}</p>
|
|
486
|
+
|
|
487
|
+
<div>
|
|
488
|
+
<button
|
|
489
|
+
type="button"
|
|
490
|
+
className="btn btn-secondary"
|
|
491
|
+
onClick={handleDownload}
|
|
492
|
+
>
|
|
493
|
+
<i className="fas fa-download me-2"/>{T`Download XLSX`}
|
|
494
|
+
</button>
|
|
495
|
+
</div>
|
|
496
|
+
<div className="text-muted mt-2">
|
|
497
|
+
{T`This creates a spreadsheet that can be sent to a translator. Please do not change the first column or first row of the spreadsheet.`}
|
|
498
|
+
</div>
|
|
499
|
+
|
|
500
|
+
<br />
|
|
501
|
+
<p>
|
|
502
|
+
{T`Once translation is complete, upload the file back using the button below:`}
|
|
503
|
+
</p>
|
|
504
|
+
<div>
|
|
505
|
+
<button
|
|
506
|
+
type="button"
|
|
507
|
+
className="btn btn-secondary"
|
|
508
|
+
onClick={handleUpload}
|
|
509
|
+
>
|
|
510
|
+
<i className="fas fa-upload me-2"/>{T`Upload Translated XLSX`}
|
|
511
|
+
</button>
|
|
512
|
+
</div>
|
|
513
|
+
<input
|
|
514
|
+
type="file"
|
|
515
|
+
ref={fileInputRef}
|
|
516
|
+
style={{ display: "none" }}
|
|
517
|
+
onChange={handleUploadChange}
|
|
518
|
+
accept=".xlsx"
|
|
519
|
+
/>
|
|
193
520
|
</>
|
|
194
521
|
)}
|
|
195
|
-
|
|
522
|
+
</>
|
|
523
|
+
)
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/** Helper function for base64 to blob conversion */
|
|
527
|
+
function b64toBlob(b64Data: string, contentType: string = "", sliceSize: number = 512) {
|
|
528
|
+
const byteCharacters = atob(b64Data)
|
|
529
|
+
const byteArrays = []
|
|
530
|
+
|
|
531
|
+
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
|
|
532
|
+
const slice = byteCharacters.slice(offset, offset + sliceSize)
|
|
533
|
+
const byteNumbers = new Array(slice.length)
|
|
534
|
+
|
|
535
|
+
for (let i = 0; i < slice.length; i++) {
|
|
536
|
+
byteNumbers[i] = slice.charCodeAt(i)
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const byteArray = new Uint8Array(byteNumbers)
|
|
540
|
+
byteArrays.push(byteArray)
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
return new Blob(byteArrays, { type: contentType })
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
interface AutoTranslateLinkProps {
|
|
547
|
+
locale: string
|
|
548
|
+
baseLocale: string
|
|
549
|
+
strings: string[]
|
|
550
|
+
existingTranslations: { [key: string]: string }
|
|
551
|
+
onTranslationsChange: (newTranslations: { [key: string]: string }) => void
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
function AutoTranslateLink(props: AutoTranslateLinkProps) {
|
|
555
|
+
const { locale, baseLocale, strings, existingTranslations, onTranslationsChange } = props
|
|
556
|
+
const [isTranslating, setIsTranslating] = useState(false)
|
|
557
|
+
|
|
558
|
+
const untranslatedStrings = useMemo(() => {
|
|
559
|
+
return strings.filter(str => !existingTranslations[str])
|
|
560
|
+
}, [strings, existingTranslations])
|
|
561
|
+
|
|
562
|
+
const handleClick = async () => {
|
|
563
|
+
if (isTranslating) {
|
|
564
|
+
return
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
setIsTranslating(true)
|
|
568
|
+
try {
|
|
569
|
+
const translatedStrings = await translateStrings(untranslatedStrings, baseLocale, locale)
|
|
570
|
+
|
|
571
|
+
const newTranslations = { ...existingTranslations }
|
|
572
|
+
for (let i = 0; i < untranslatedStrings.length; i++) {
|
|
573
|
+
newTranslations[untranslatedStrings[i]] = translatedStrings[i]
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
onTranslationsChange(newTranslations)
|
|
577
|
+
} catch (error) {
|
|
578
|
+
console.error("Error translating strings:", error)
|
|
579
|
+
alert(T`Error translating strings`)
|
|
580
|
+
} finally {
|
|
581
|
+
setIsTranslating(false)
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
if (untranslatedStrings.length === 0) {
|
|
586
|
+
return null
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
if (!canAutoTranslate(locale)) {
|
|
590
|
+
return null
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
return (
|
|
594
|
+
<button
|
|
595
|
+
type="button"
|
|
596
|
+
className="btn btn-sm btn-link"
|
|
597
|
+
onClick={handleClick}
|
|
598
|
+
disabled={isTranslating}
|
|
599
|
+
style={{ marginLeft: 5, padding: "0 5px" }}
|
|
600
|
+
>
|
|
601
|
+
{isTranslating ? (
|
|
602
|
+
<span>
|
|
603
|
+
<i className="fa fa-spinner fa-spin" />
|
|
604
|
+
{" "}
|
|
605
|
+
{T`Translating...`}
|
|
606
|
+
</span>
|
|
607
|
+
) : (
|
|
608
|
+
T`Autotranslate`
|
|
609
|
+
)}
|
|
610
|
+
</button>
|
|
196
611
|
)
|
|
197
612
|
}
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import { Schema, DataSource } from "@mwater/expressions"
|
|
2
|
-
import {
|
|
2
|
+
import { useRef } from "react"
|
|
3
3
|
import { JsonQLFilter } from "../JsonQLFilter"
|
|
4
4
|
import WidgetFactory from "../widgets/WidgetFactory"
|
|
5
5
|
import { WidgetScope } from "../WidgetScope"
|
|
6
|
-
import DashboardDataSource from "./DashboardDataSource"
|
|
7
6
|
import { WidgetDataSource } from "../widgets/WidgetDataSource"
|
|
7
|
+
import { TOCEntry } from "../widgets/Widget"
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Component which renders a widget and ensures that props do not change
|
|
11
11
|
* unnecessarily.
|
|
12
12
|
*/
|
|
13
13
|
export function WidgetComponent(props: {
|
|
14
|
-
/** Widget id */
|
|
15
|
-
id: string
|
|
16
14
|
/** Widget type */
|
|
17
15
|
type: string
|
|
18
16
|
|
|
@@ -56,7 +54,7 @@ export function WidgetComponent(props: {
|
|
|
56
54
|
namedStrings?: { [key: string]: string }
|
|
57
55
|
|
|
58
56
|
/** Entries in the table of content */
|
|
59
|
-
tocEntries?:
|
|
57
|
+
tocEntries?: TOCEntry[]
|
|
60
58
|
|
|
61
59
|
/** the widget callback ref */
|
|
62
60
|
widgetRef: (widget: any) => void
|
|
@@ -66,6 +64,12 @@ export function WidgetComponent(props: {
|
|
|
66
64
|
|
|
67
65
|
/** Change to force a refresh */
|
|
68
66
|
refreshKey?: any
|
|
67
|
+
|
|
68
|
+
/** Locale to use for display */
|
|
69
|
+
locale: string
|
|
70
|
+
|
|
71
|
+
/** Translate function to use for display. Returns same string when editing. */
|
|
72
|
+
translate: (input: string) => string
|
|
69
73
|
}) {
|
|
70
74
|
const widget = WidgetFactory.createWidget(props.type)
|
|
71
75
|
|
|
@@ -96,7 +100,9 @@ export function WidgetComponent(props: {
|
|
|
96
100
|
tocEntries: props.tocEntries,
|
|
97
101
|
onScrollToTOCEntry: props.onScrollToTOCEntry,
|
|
98
102
|
widgetRef,
|
|
99
|
-
refreshKey: props.refreshKey
|
|
103
|
+
refreshKey: props.refreshKey,
|
|
104
|
+
locale: props.locale,
|
|
105
|
+
translate: props.translate
|
|
100
106
|
})
|
|
101
107
|
}
|
|
102
108
|
|