@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
|
@@ -2,13 +2,14 @@ import { Quickfilter } from "../quickfilter/Quickfilter";
|
|
|
2
2
|
import { Expr } from "@mwater/expressions";
|
|
3
3
|
import { BlocksLayoutOptions, DashboardTheme } from "./layoutOptions";
|
|
4
4
|
import { GlobalFilter } from "../GlobalFilter";
|
|
5
|
+
import { LayoutBlock } from "../layouts/blocks/blockUtils";
|
|
5
6
|
/** Dashboard design
|
|
6
7
|
* Each understands enough of the dashboard design to create widgets.
|
|
7
8
|
* Widget refers to the widget itself, where *item* refers also to the layout and id that it has in the dashboard.
|
|
8
9
|
*/
|
|
9
10
|
export interface DashboardDesign {
|
|
10
11
|
/** dashboard items. Format depends on layout of dashboard. See layouts/.../README.md */
|
|
11
|
-
items:
|
|
12
|
+
items: LayoutBlock;
|
|
12
13
|
/** array of quick filters (user-selectable filters). See quickfilter/README.md */
|
|
13
14
|
quickfilters?: Quickfilter[];
|
|
14
15
|
/** layout engine to use (`blocks` is new default) */
|
|
@@ -21,8 +22,16 @@ export interface DashboardDesign {
|
|
|
21
22
|
filters?: {
|
|
22
23
|
[tableId: string]: Expr;
|
|
23
24
|
};
|
|
24
|
-
/** optional locale (e.g. "fr") to use for display */
|
|
25
|
+
/** optional locale (e.g. "fr") to use for display. Defaults to "en" */
|
|
25
26
|
locale?: string;
|
|
27
|
+
/** Other locales that the dashboard is available in. */
|
|
28
|
+
otherLocales?: string[];
|
|
29
|
+
/** Translation map for dashboard. Maps locale to translation map. Does not include default locale. */
|
|
30
|
+
translations?: {
|
|
31
|
+
[locale: string]: {
|
|
32
|
+
[key: string]: string;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
26
35
|
/** true to enable implicit filtering (see ImplicitFilterBuilder). Defaults to true for older dashboards. */
|
|
27
36
|
implicitFiltersEnabled?: boolean;
|
|
28
37
|
/** array of global filters. See below. */
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { Schema } from "@mwater/expressions";
|
|
2
2
|
import { JsonQLFilter } from "..";
|
|
3
|
+
import LayoutManager from "../layouts/LayoutManager";
|
|
3
4
|
import { DashboardDesign } from "./DashboardDesign";
|
|
4
5
|
/** Gets filterable tables for a dashboard */
|
|
5
6
|
export declare function getFilterableTables(design: DashboardDesign, schema: Schema): string[];
|
|
7
|
+
/** Gets all translatable strings from a dashboard */
|
|
8
|
+
export declare function getTranslatableStringsFromDashboard(design: DashboardDesign, schema: Schema): string[];
|
|
9
|
+
/** Gets translatable strings from a layout manager and items */
|
|
10
|
+
export declare function getTranslatableStringsFromLayoutManager(layoutManager: LayoutManager, items: any, schema: Schema): string[];
|
|
6
11
|
/** Get filters from props filters combined with dashboard filters */
|
|
7
12
|
export declare function getCompiledFilters(design: DashboardDesign, schema: Schema, filterableTables: string[]): JsonQLFilter[];
|
|
@@ -4,6 +4,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.getFilterableTables = getFilterableTables;
|
|
7
|
+
exports.getTranslatableStringsFromDashboard = getTranslatableStringsFromDashboard;
|
|
8
|
+
exports.getTranslatableStringsFromLayoutManager = getTranslatableStringsFromLayoutManager;
|
|
7
9
|
exports.getCompiledFilters = getCompiledFilters;
|
|
8
10
|
const lodash_1 = __importDefault(require("lodash"));
|
|
9
11
|
const expressions_1 = require("@mwater/expressions");
|
|
@@ -24,6 +26,34 @@ function getFilterableTables(design, schema) {
|
|
|
24
26
|
filterableTables = lodash_1.default.filter(lodash_1.default.uniq(filterableTables), (table) => schema.getTable(table));
|
|
25
27
|
return filterableTables;
|
|
26
28
|
}
|
|
29
|
+
/** Gets all translatable strings from a dashboard */
|
|
30
|
+
function getTranslatableStringsFromDashboard(design, schema) {
|
|
31
|
+
const layoutManager = LayoutManager_1.default.createLayoutManager(design.layout);
|
|
32
|
+
// Get translatable strings from layout manager
|
|
33
|
+
let strings = getTranslatableStringsFromLayoutManager(layoutManager, design.items, schema);
|
|
34
|
+
// Get translatable strings from quickfilters
|
|
35
|
+
if (design.quickfilters) {
|
|
36
|
+
for (let quickfilter of design.quickfilters) {
|
|
37
|
+
if (quickfilter.label) {
|
|
38
|
+
strings.push(quickfilter.label);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// Remove duplicates
|
|
43
|
+
return lodash_1.default.uniq(strings);
|
|
44
|
+
}
|
|
45
|
+
/** Gets translatable strings from a layout manager and items */
|
|
46
|
+
function getTranslatableStringsFromLayoutManager(layoutManager, items, schema) {
|
|
47
|
+
let strings = [];
|
|
48
|
+
for (let widgetItem of layoutManager.getAllWidgets(items)) {
|
|
49
|
+
// Create widget
|
|
50
|
+
const widget = WidgetFactory_1.default.createWidget(widgetItem.type);
|
|
51
|
+
// Get translatable strings
|
|
52
|
+
strings = strings.concat(widget.getTranslatableStrings(widgetItem.design, schema));
|
|
53
|
+
}
|
|
54
|
+
// Remove duplicates
|
|
55
|
+
return lodash_1.default.uniq(strings);
|
|
56
|
+
}
|
|
27
57
|
/** Get filters from props filters combined with dashboard filters */
|
|
28
58
|
function getCompiledFilters(design, schema, filterableTables) {
|
|
29
59
|
let expr, jsonql, table;
|
|
@@ -30,6 +30,8 @@ export interface DashboardViewComponentProps {
|
|
|
30
30
|
printMode?: boolean;
|
|
31
31
|
/** Change to force a refresh */
|
|
32
32
|
refreshKey?: any;
|
|
33
|
+
/** Locale to use for display. Defaults to dashboard design locale. */
|
|
34
|
+
locale?: string;
|
|
33
35
|
}
|
|
34
36
|
export interface DashboardViewComponentHandle {
|
|
35
37
|
print: () => Promise<void>;
|
|
@@ -154,6 +154,17 @@ const DashboardViewComponent = react_1.default.forwardRef((props, ref) => {
|
|
|
154
154
|
const renderScopes = () => {
|
|
155
155
|
return (react_1.default.createElement(WidgetScopesViewComponent_1.default, { scopes: widgetScoper.getScopes(), onRemoveScope: handleRemoveScope }));
|
|
156
156
|
};
|
|
157
|
+
/** Locale that the dashboard design is in */
|
|
158
|
+
const designLocale = props.design.locale ?? "en";
|
|
159
|
+
/** Locale to display the dashboard in. If editing, always use design locale. */
|
|
160
|
+
const displayLocale = props.onDesignChange != null ? designLocale : (props.locale ?? designLocale ?? "en");
|
|
161
|
+
/** Translate function to use for display. Do not use when editing. */
|
|
162
|
+
const translate = (0, react_1.useCallback)((input) => {
|
|
163
|
+
if (designLocale === displayLocale) {
|
|
164
|
+
return input;
|
|
165
|
+
}
|
|
166
|
+
return props.design.translations?.[displayLocale]?.[input] ?? input;
|
|
167
|
+
}, [props.design.translations, designLocale, displayLocale]);
|
|
157
168
|
const compRef = (widgetId, comp) => {
|
|
158
169
|
return (widgetComps.current[widgetId] = comp);
|
|
159
170
|
};
|
|
@@ -180,9 +191,11 @@ const DashboardViewComponent = react_1.default.forwardRef((props, ref) => {
|
|
|
180
191
|
const implicitFilterBuilder = new ImplicitFilterBuilder_1.default(props.schema);
|
|
181
192
|
filters = implicitFilterBuilder.extendFilters(filterableTables, filters);
|
|
182
193
|
}
|
|
183
|
-
const widgetElem = (react_1.default.createElement(WidgetComponent_1.WidgetComponent, { key: options.id,
|
|
194
|
+
const widgetElem = (react_1.default.createElement(WidgetComponent_1.WidgetComponent, { key: options.id, type: options.type, schema: props.schema, dataSource: props.dataSource, widgetDataSource: widgetDataSource, design: options.design, scope: widgetScoper.getScope(options.id), filters: filters, onScopeChange: handleScopeChange.bind(null, options.id), onDesignChange: options.onDesignChange, width: options.width, height: options.height, onRowClick: props.onRowClick, namedStrings: props.namedStrings, tocEntries: tocEntries, onScrollToTOCEntry: handleScrollToTOCEntry,
|
|
184
195
|
// Keep references to widget elements
|
|
185
|
-
widgetRef: compRef.bind(null, options.id), refreshKey: props.refreshKey
|
|
196
|
+
widgetRef: compRef.bind(null, options.id), refreshKey: props.refreshKey,
|
|
197
|
+
// Use locale from props, design, or default to en
|
|
198
|
+
locale: displayLocale, translate: translate }));
|
|
186
199
|
return widgetElem;
|
|
187
200
|
};
|
|
188
201
|
const style = {
|
|
@@ -195,7 +208,7 @@ const DashboardViewComponent = react_1.default.forwardRef((props, ref) => {
|
|
|
195
208
|
style.overflowX = "auto";
|
|
196
209
|
}
|
|
197
210
|
// Render widget container
|
|
198
|
-
return (react_1.default.createElement(expressions_ui_1.LocaleContext.Provider, { value:
|
|
211
|
+
return (react_1.default.createElement(expressions_ui_1.LocaleContext.Provider, { value: displayLocale },
|
|
199
212
|
react_1.default.createElement("div", { style: style },
|
|
200
213
|
!props.hideScopes ? renderScopes() : undefined,
|
|
201
214
|
layoutManager.renderLayout({
|
|
@@ -273,7 +273,8 @@ class ServerWidgetLayerDataSource {
|
|
|
273
273
|
throw new Error("Error getting tiles token");
|
|
274
274
|
}
|
|
275
275
|
const { token, expires } = await response.json();
|
|
276
|
-
|
|
276
|
+
// Client isn't necessary, but allows tracking of usage easier
|
|
277
|
+
return { url: this.options.apiUrl + `vector_tiles/tiles/{z}/{x}/{y}?token=${token}&client=${this.options.client ?? ""}`, expires };
|
|
277
278
|
}
|
|
278
279
|
// Gets widget data source for a popup widget
|
|
279
280
|
getPopupWidgetDataSource(design, widgetId) {
|
|
@@ -2,7 +2,7 @@ import React from "react";
|
|
|
2
2
|
import { DataSource, Schema } from "@mwater/expressions";
|
|
3
3
|
import { DashboardDesign } from "./DashboardDesign";
|
|
4
4
|
export interface SettingsModalComponentProps {
|
|
5
|
-
onDesignChange:
|
|
5
|
+
onDesignChange: (design: DashboardDesign) => void;
|
|
6
6
|
schema: Schema;
|
|
7
7
|
dataSource: DataSource;
|
|
8
8
|
}
|
|
@@ -37,6 +37,10 @@ const QuickfiltersDesignComponent_1 = __importDefault(require("../quickfilter/Qu
|
|
|
37
37
|
const FiltersDesignerComponent_1 = __importDefault(require("../FiltersDesignerComponent"));
|
|
38
38
|
const MWaterContextComponent_1 = require("../MWaterContextComponent");
|
|
39
39
|
const immer_1 = __importDefault(require("immer"));
|
|
40
|
+
const TabbedComponent_1 = __importDefault(require("@mwater/react-library/lib/TabbedComponent"));
|
|
41
|
+
const file_saver_1 = __importDefault(require("file-saver"));
|
|
42
|
+
const localizeUtils = __importStar(require("ez-localize/lib/utils"));
|
|
43
|
+
const autotranslate_1 = require("../autotranslate");
|
|
40
44
|
/** Popup with settings for dashboard */
|
|
41
45
|
class SettingsModalComponent extends react_1.default.Component {
|
|
42
46
|
constructor(props) {
|
|
@@ -71,33 +75,41 @@ exports.default = SettingsModalComponent;
|
|
|
71
75
|
/** Settings component for dashboard that is used in the settings modal */
|
|
72
76
|
function SettingsComponent(props) {
|
|
73
77
|
const { design, onDesignChange, schema, dataSource } = props;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
78
|
+
const tabs = [
|
|
79
|
+
{
|
|
80
|
+
id: "filters",
|
|
81
|
+
label: T `Filters`,
|
|
82
|
+
elem: (react_1.default.createElement(FiltersTab, { design: design, onDesignChange: onDesignChange, schema: schema, dataSource: dataSource }))
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: "language",
|
|
86
|
+
label: T `Translation`,
|
|
87
|
+
elem: (react_1.default.createElement(LanguageTab, { design: design, onDesignChange: onDesignChange, schema: schema }))
|
|
88
|
+
}
|
|
89
|
+
];
|
|
84
90
|
return (react_1.default.createElement("div", { style: { paddingBottom: 200 } },
|
|
91
|
+
react_1.default.createElement(TabbedComponent_1.default, { tabs: tabs, initialTabId: "filters" })));
|
|
92
|
+
}
|
|
93
|
+
function FiltersTab({ design, onDesignChange, schema, dataSource }) {
|
|
94
|
+
// Get filterable tables
|
|
95
|
+
const filterableTables = (0, react_1.useMemo)(() => DashboardUtils.getFilterableTables(design, schema), [design, schema]);
|
|
96
|
+
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
85
97
|
react_1.default.createElement("h4", null, T `Quick Filters`),
|
|
86
|
-
react_1.default.createElement("div", { className: "
|
|
98
|
+
react_1.default.createElement("div", { className: "mb-2" }, 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.`),
|
|
87
99
|
filterableTables.length > 0 ? (react_1.default.createElement(QuickfiltersDesignComponent_1.default, { design: design.quickfilters || [], onDesignChange: (quickfilters) => {
|
|
88
100
|
onDesignChange((0, immer_1.default)(design, draft => {
|
|
89
101
|
draft.quickfilters = quickfilters;
|
|
90
102
|
}));
|
|
91
103
|
}, schema: schema, dataSource: dataSource, tables: filterableTables })) : (T `Nothing to quickfilter. Add widgets to the dashboard`),
|
|
92
|
-
react_1.default.createElement("h4", { style: { paddingTop:
|
|
93
|
-
react_1.default.createElement("div", { className: "
|
|
104
|
+
react_1.default.createElement("h4", { style: { paddingTop: 15 } }, T `Filters`),
|
|
105
|
+
react_1.default.createElement("div", { className: "mb-2" }, T `Filters are built in to the dashboard and cannot be changed by viewers of the dashboard.`),
|
|
94
106
|
filterableTables.length > 0 ? (react_1.default.createElement(FiltersDesignerComponent_1.default, { schema: schema, dataSource: dataSource, filters: design.filters, onFiltersChange: (filters) => {
|
|
95
107
|
onDesignChange((0, immer_1.default)(design, draft => {
|
|
96
108
|
draft.filters = filters;
|
|
97
109
|
}));
|
|
98
110
|
}, filterableTables: filterableTables })) : (T `Nothing to filter. Add widgets to the dashboard`),
|
|
99
111
|
react_1.default.createElement(MWaterContextComponent_1.GlobalFiltersElementFactoryContext.Consumer, null, globalFiltersElementFactory => globalFiltersElementFactory ? (react_1.default.createElement("div", null,
|
|
100
|
-
react_1.default.createElement("h4", { style: { paddingTop:
|
|
112
|
+
react_1.default.createElement("h4", { style: { paddingTop: 15 } }, T `Global Filters`),
|
|
101
113
|
globalFiltersElementFactory({
|
|
102
114
|
schema,
|
|
103
115
|
dataSource,
|
|
@@ -109,14 +121,239 @@ function SettingsComponent(props) {
|
|
|
109
121
|
}));
|
|
110
122
|
}
|
|
111
123
|
}))) : undefined),
|
|
112
|
-
react_1.default.createElement("h4", { style: { paddingTop: 10 } }, T `Language`),
|
|
113
|
-
react_1.default.createElement("div", { className: "text-muted" }, T `Controls the preferred language of widgets and uses specified language when available`),
|
|
114
|
-
react_1.default.createElement(react_select_1.default, { value: lodash_1.default.findWhere(localeOptions, { value: design.locale || "en" }) || null, options: localeOptions, onChange: (locale) => onDesignChange((0, immer_1.default)(design, draft => {
|
|
115
|
-
draft.locale = locale.value;
|
|
116
|
-
})) }),
|
|
117
124
|
design.implicitFiltersEnabled && (react_1.default.createElement(react_1.default.Fragment, null,
|
|
118
125
|
react_1.default.createElement("h4", { style: { paddingTop: 10 } }, T `Advanced`),
|
|
119
126
|
react_1.default.createElement(ui.Checkbox, { value: design.implicitFiltersEnabled != null ? design.implicitFiltersEnabled : true, onChange: (value) => onDesignChange((0, immer_1.default)(design, draft => {
|
|
120
127
|
draft.implicitFiltersEnabled = value;
|
|
121
128
|
})) }, T `Enable Implicit Filtering (leave unchecked for new dashboards)`)))));
|
|
122
129
|
}
|
|
130
|
+
function LanguageTab({ design, onDesignChange, schema }) {
|
|
131
|
+
const fileInputRef = (0, react_1.useRef)(null);
|
|
132
|
+
const locale = design.locale || "en";
|
|
133
|
+
const localeOptions = (0, react_1.useMemo)(() => {
|
|
134
|
+
return lodash_1.default.sortBy(lodash_1.default.map(languages_1.languages, (language) => ({
|
|
135
|
+
value: language.code,
|
|
136
|
+
label: `${language.en} (${language.name})`
|
|
137
|
+
})), 'label');
|
|
138
|
+
}, [languages_1.languages]);
|
|
139
|
+
// Get available languages that aren't already selected
|
|
140
|
+
const availableLocaleOptions = (0, react_1.useMemo)(() => {
|
|
141
|
+
const selectedLocales = new Set([design.locale, ...(design.otherLocales || [])]);
|
|
142
|
+
return localeOptions.filter(opt => !selectedLocales.has(opt.value));
|
|
143
|
+
}, [localeOptions, design.locale, design.otherLocales]);
|
|
144
|
+
const translatableStrings = (0, react_1.useMemo)(() => DashboardUtils.getTranslatableStringsFromDashboard(design, schema), [design, schema]);
|
|
145
|
+
// Calculate percentage of strings translated for each locale
|
|
146
|
+
const translationPercentages = (0, react_1.useMemo)(() => {
|
|
147
|
+
const percentages = {};
|
|
148
|
+
const totalStrings = translatableStrings.length;
|
|
149
|
+
for (const locale of design.otherLocales || []) {
|
|
150
|
+
const translatedCount = translatableStrings.filter(str => design.translations?.[locale]?.[str] != null).length;
|
|
151
|
+
// Round down to nearest percent
|
|
152
|
+
percentages[locale] = Math.floor((translatedCount / totalStrings) * 100);
|
|
153
|
+
}
|
|
154
|
+
return percentages;
|
|
155
|
+
}, [design.translations, design.otherLocales, translatableStrings]);
|
|
156
|
+
const handleAddLocale = (locale) => {
|
|
157
|
+
onDesignChange((0, immer_1.default)(design, draft => {
|
|
158
|
+
draft.otherLocales = [...(draft.otherLocales || []), locale.value];
|
|
159
|
+
// Initialize empty translations object if needed
|
|
160
|
+
if (!draft.translations) {
|
|
161
|
+
draft.translations = {};
|
|
162
|
+
}
|
|
163
|
+
if (!draft.translations[locale.value]) {
|
|
164
|
+
draft.translations[locale.value] = {};
|
|
165
|
+
}
|
|
166
|
+
}));
|
|
167
|
+
};
|
|
168
|
+
const handleRemoveLocale = (localeToRemove) => {
|
|
169
|
+
onDesignChange((0, immer_1.default)(design, draft => {
|
|
170
|
+
draft.otherLocales = (draft.otherLocales || []).filter(locale => locale !== localeToRemove);
|
|
171
|
+
// Remove translations for this locale
|
|
172
|
+
if (draft.translations) {
|
|
173
|
+
delete draft.translations[localeToRemove];
|
|
174
|
+
}
|
|
175
|
+
}));
|
|
176
|
+
};
|
|
177
|
+
// Convert dashboard translations to LocalizedString format
|
|
178
|
+
const getLocalizedStrings = () => {
|
|
179
|
+
// For each string, create a localized string
|
|
180
|
+
const localizedStrings = [];
|
|
181
|
+
for (const str of translatableStrings) {
|
|
182
|
+
const localizedString = { _base: locale };
|
|
183
|
+
localizedString[locale] = str;
|
|
184
|
+
// Only add translations for other locales if they exist
|
|
185
|
+
for (const otherLocale of design.otherLocales || []) {
|
|
186
|
+
if (design.translations?.[otherLocale]?.[str]) {
|
|
187
|
+
localizedString[otherLocale] = design.translations?.[otherLocale]?.[str];
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
localizedStrings.push(localizedString);
|
|
191
|
+
}
|
|
192
|
+
return localizedStrings;
|
|
193
|
+
};
|
|
194
|
+
// Convert LocalizedString format back to dashboard translations
|
|
195
|
+
const updateFromLocalizedStrings = (localizedStrings) => {
|
|
196
|
+
const newTranslations = {};
|
|
197
|
+
// Get all locales except base
|
|
198
|
+
const locales = design.otherLocales || [];
|
|
199
|
+
// Initialize translations object
|
|
200
|
+
for (const locale of locales) {
|
|
201
|
+
newTranslations[locale] = {};
|
|
202
|
+
}
|
|
203
|
+
// Add all translations
|
|
204
|
+
for (const str of localizedStrings) {
|
|
205
|
+
for (const locale of locales) {
|
|
206
|
+
if (str[locale]) {
|
|
207
|
+
newTranslations[locale][str[str._base]] = str[locale];
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return newTranslations;
|
|
212
|
+
};
|
|
213
|
+
const handleDownload = () => {
|
|
214
|
+
// Get strings in LocalizedString format
|
|
215
|
+
const strings = getLocalizedStrings();
|
|
216
|
+
// Create xlsx base64 using all locales (primary + other)
|
|
217
|
+
const locales = [{ code: locale, name: locale }]
|
|
218
|
+
.concat((design.otherLocales || []).map(code => ({ code, name: code })));
|
|
219
|
+
const base64 = localizeUtils.exportXlsx(locales, strings);
|
|
220
|
+
// Download
|
|
221
|
+
file_saver_1.default.saveAs(b64toBlob(base64, "application/octet-stream"), "Dashboard Translations.xlsx");
|
|
222
|
+
};
|
|
223
|
+
const handleUpload = () => {
|
|
224
|
+
fileInputRef.current?.click();
|
|
225
|
+
};
|
|
226
|
+
const handleUploadChange = (evt) => {
|
|
227
|
+
const reader = new FileReader();
|
|
228
|
+
reader.onload = (file) => {
|
|
229
|
+
if (!file.target?.result) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const base64 = file.target.result.split(",")[1];
|
|
233
|
+
try {
|
|
234
|
+
// Create locales array for import
|
|
235
|
+
const locales = [{ code: locale, name: locale }]
|
|
236
|
+
.concat((design.otherLocales || []).map(code => ({ code, name: code })));
|
|
237
|
+
// Import updates
|
|
238
|
+
const updates = localizeUtils.importXlsx(locales, base64);
|
|
239
|
+
// If nothing localized
|
|
240
|
+
if (updates.length === 0) {
|
|
241
|
+
alert(T `No translation data found in file`);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
// Convert back to dashboard format and update
|
|
245
|
+
const newTranslations = updateFromLocalizedStrings(updates);
|
|
246
|
+
onDesignChange((0, immer_1.default)(design, draft => {
|
|
247
|
+
draft.translations = newTranslations;
|
|
248
|
+
}));
|
|
249
|
+
alert(T `${updates.length} translations applied`);
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
console.error("Invalid xlsx file:", error);
|
|
253
|
+
alert(T `Invalid xlsx file`);
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
if (evt.target.files?.[0]) {
|
|
257
|
+
reader.readAsDataURL(evt.target.files[0]);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
261
|
+
react_1.default.createElement("h4", null, T `Base Language`),
|
|
262
|
+
react_1.default.createElement("div", { className: "mb-2" }, T `This is the language that the dashboard is written in.`),
|
|
263
|
+
react_1.default.createElement(react_select_1.default, { value: lodash_1.default.findWhere(localeOptions, { value: design.locale || "en" }) || null, options: localeOptions, onChange: (locale) => onDesignChange((0, immer_1.default)(design, draft => {
|
|
264
|
+
draft.locale = locale.value;
|
|
265
|
+
})) }),
|
|
266
|
+
react_1.default.createElement("h4", { className: "mt-4" }, T `Additional Languages`),
|
|
267
|
+
react_1.default.createElement("div", { className: "mb-2" }, T `Add languages that this dashboard will be translated into`),
|
|
268
|
+
react_1.default.createElement("table", null,
|
|
269
|
+
react_1.default.createElement("tbody", null, (design.otherLocales || []).map(locale => {
|
|
270
|
+
const localeOption = lodash_1.default.findWhere(localeOptions, { value: locale });
|
|
271
|
+
if (!localeOption) {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
return (react_1.default.createElement("tr", { key: locale },
|
|
275
|
+
react_1.default.createElement("td", { style: { paddingRight: 10 } }, localeOption.label),
|
|
276
|
+
react_1.default.createElement("td", { className: translationPercentages[locale] === 100 ? "text-success" : "text-warning", style: { textAlign: "right" } }, T `${translationPercentages[locale]}% translated`),
|
|
277
|
+
react_1.default.createElement("td", null, translationPercentages[locale] < 100 && (react_1.default.createElement(AutoTranslateLink, { locale: locale, baseLocale: design.locale || "en", strings: translatableStrings, existingTranslations: design.translations?.[locale] || {}, onTranslationsChange: (newTranslations) => {
|
|
278
|
+
onDesignChange((0, immer_1.default)(design, draft => {
|
|
279
|
+
if (!draft.translations) {
|
|
280
|
+
draft.translations = {};
|
|
281
|
+
}
|
|
282
|
+
draft.translations[locale] = newTranslations;
|
|
283
|
+
}));
|
|
284
|
+
} }))),
|
|
285
|
+
react_1.default.createElement("td", null,
|
|
286
|
+
react_1.default.createElement("button", { type: "button", className: "btn btn-sm btn-link", onClick: () => handleRemoveLocale(locale) },
|
|
287
|
+
react_1.default.createElement("i", { className: "fa fa-times" })))));
|
|
288
|
+
}))),
|
|
289
|
+
availableLocaleOptions.length > 0 && (react_1.default.createElement("div", { className: "mt-3" },
|
|
290
|
+
react_1.default.createElement(react_select_1.default, { value: null, options: availableLocaleOptions, onChange: handleAddLocale, placeholder: T `Add language...` }))),
|
|
291
|
+
(design.otherLocales || []).length > 0 && (react_1.default.createElement(react_1.default.Fragment, null,
|
|
292
|
+
react_1.default.createElement("h4", { className: "mt-4" }, T `Manage Translations`),
|
|
293
|
+
react_1.default.createElement("p", null, T `Download and re-upload an Excel spreadsheet of text to translate:`),
|
|
294
|
+
react_1.default.createElement("div", null,
|
|
295
|
+
react_1.default.createElement("button", { type: "button", className: "btn btn-secondary", onClick: handleDownload },
|
|
296
|
+
react_1.default.createElement("i", { className: "fas fa-download me-2" }),
|
|
297
|
+
T `Download XLSX`)),
|
|
298
|
+
react_1.default.createElement("div", { className: "text-muted mt-2" }, 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.`),
|
|
299
|
+
react_1.default.createElement("br", null),
|
|
300
|
+
react_1.default.createElement("p", null, T `Once translation is complete, upload the file back using the button below:`),
|
|
301
|
+
react_1.default.createElement("div", null,
|
|
302
|
+
react_1.default.createElement("button", { type: "button", className: "btn btn-secondary", onClick: handleUpload },
|
|
303
|
+
react_1.default.createElement("i", { className: "fas fa-upload me-2" }),
|
|
304
|
+
T `Upload Translated XLSX`)),
|
|
305
|
+
react_1.default.createElement("input", { type: "file", ref: fileInputRef, style: { display: "none" }, onChange: handleUploadChange, accept: ".xlsx" })))));
|
|
306
|
+
}
|
|
307
|
+
/** Helper function for base64 to blob conversion */
|
|
308
|
+
function b64toBlob(b64Data, contentType = "", sliceSize = 512) {
|
|
309
|
+
const byteCharacters = atob(b64Data);
|
|
310
|
+
const byteArrays = [];
|
|
311
|
+
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
|
|
312
|
+
const slice = byteCharacters.slice(offset, offset + sliceSize);
|
|
313
|
+
const byteNumbers = new Array(slice.length);
|
|
314
|
+
for (let i = 0; i < slice.length; i++) {
|
|
315
|
+
byteNumbers[i] = slice.charCodeAt(i);
|
|
316
|
+
}
|
|
317
|
+
const byteArray = new Uint8Array(byteNumbers);
|
|
318
|
+
byteArrays.push(byteArray);
|
|
319
|
+
}
|
|
320
|
+
return new Blob(byteArrays, { type: contentType });
|
|
321
|
+
}
|
|
322
|
+
function AutoTranslateLink(props) {
|
|
323
|
+
const { locale, baseLocale, strings, existingTranslations, onTranslationsChange } = props;
|
|
324
|
+
const [isTranslating, setIsTranslating] = (0, react_1.useState)(false);
|
|
325
|
+
const untranslatedStrings = (0, react_1.useMemo)(() => {
|
|
326
|
+
return strings.filter(str => !existingTranslations[str]);
|
|
327
|
+
}, [strings, existingTranslations]);
|
|
328
|
+
const handleClick = async () => {
|
|
329
|
+
if (isTranslating) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
setIsTranslating(true);
|
|
333
|
+
try {
|
|
334
|
+
const translatedStrings = await (0, autotranslate_1.translateStrings)(untranslatedStrings, baseLocale, locale);
|
|
335
|
+
const newTranslations = { ...existingTranslations };
|
|
336
|
+
for (let i = 0; i < untranslatedStrings.length; i++) {
|
|
337
|
+
newTranslations[untranslatedStrings[i]] = translatedStrings[i];
|
|
338
|
+
}
|
|
339
|
+
onTranslationsChange(newTranslations);
|
|
340
|
+
}
|
|
341
|
+
catch (error) {
|
|
342
|
+
console.error("Error translating strings:", error);
|
|
343
|
+
alert(T `Error translating strings`);
|
|
344
|
+
}
|
|
345
|
+
finally {
|
|
346
|
+
setIsTranslating(false);
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
if (untranslatedStrings.length === 0) {
|
|
350
|
+
return null;
|
|
351
|
+
}
|
|
352
|
+
if (!(0, autotranslate_1.canAutoTranslate)(locale)) {
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
return (react_1.default.createElement("button", { type: "button", className: "btn btn-sm btn-link", onClick: handleClick, disabled: isTranslating, style: { marginLeft: 5, padding: "0 5px" } }, isTranslating ? (react_1.default.createElement("span", null,
|
|
356
|
+
react_1.default.createElement("i", { className: "fa fa-spinner fa-spin" }),
|
|
357
|
+
" ",
|
|
358
|
+
T `Translating...`)) : (T `Autotranslate`)));
|
|
359
|
+
}
|
|
@@ -2,13 +2,12 @@ import { Schema, DataSource } from "@mwater/expressions";
|
|
|
2
2
|
import { JsonQLFilter } from "../JsonQLFilter";
|
|
3
3
|
import { WidgetScope } from "../WidgetScope";
|
|
4
4
|
import { WidgetDataSource } from "../widgets/WidgetDataSource";
|
|
5
|
+
import { TOCEntry } from "../widgets/Widget";
|
|
5
6
|
/**
|
|
6
7
|
* Component which renders a widget and ensures that props do not change
|
|
7
8
|
* unnecessarily.
|
|
8
9
|
*/
|
|
9
10
|
export declare function WidgetComponent(props: {
|
|
10
|
-
/** Widget id */
|
|
11
|
-
id: string;
|
|
12
11
|
/** Widget type */
|
|
13
12
|
type: string;
|
|
14
13
|
/** Widget design */
|
|
@@ -42,11 +41,15 @@ export declare function WidgetComponent(props: {
|
|
|
42
41
|
[key: string]: string;
|
|
43
42
|
};
|
|
44
43
|
/** Entries in the table of content */
|
|
45
|
-
tocEntries?:
|
|
44
|
+
tocEntries?: TOCEntry[];
|
|
46
45
|
/** the widget callback ref */
|
|
47
46
|
widgetRef: (widget: any) => void;
|
|
48
47
|
/** called with (widgetId, tocEntryId) to scroll to TOC entry */
|
|
49
48
|
onScrollToTOCEntry?: (widgetId: string, tocEntryId: string) => void;
|
|
50
49
|
/** Change to force a refresh */
|
|
51
50
|
refreshKey?: any;
|
|
51
|
+
/** Locale to use for display */
|
|
52
|
+
locale: string;
|
|
53
|
+
/** Translate function to use for display. Returns same string when editing. */
|
|
54
|
+
translate: (input: string) => string;
|
|
52
55
|
}): import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>>;
|
|
@@ -37,7 +37,9 @@ function WidgetComponent(props) {
|
|
|
37
37
|
tocEntries: props.tocEntries,
|
|
38
38
|
onScrollToTOCEntry: props.onScrollToTOCEntry,
|
|
39
39
|
widgetRef,
|
|
40
|
-
refreshKey: props.refreshKey
|
|
40
|
+
refreshKey: props.refreshKey,
|
|
41
|
+
locale: props.locale,
|
|
42
|
+
translate: props.translate
|
|
41
43
|
});
|
|
42
44
|
}
|
|
43
45
|
/** Always returns the same function to prevent unnecessary re-rendering. Forwards to the real function */
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { EnumValue, LiteralType, Schema, DataSource } from "@mwater/expressions";
|
|
3
|
+
import { RenderCellEditorProps } from "@mwater/react-library/lib/GridComponent";
|
|
4
|
+
import "react-datepicker/dist/react-datepicker.css";
|
|
5
|
+
/** Determine if type can be edited */
|
|
6
|
+
export declare function canEditType(type: LiteralType): boolean;
|
|
7
|
+
/** Cell editor for a cell of the grid */
|
|
8
|
+
export declare const CellEditor: (props: {
|
|
9
|
+
type: LiteralType;
|
|
10
|
+
enumValues?: EnumValue[];
|
|
11
|
+
idTable?: string;
|
|
12
|
+
initialValue: any;
|
|
13
|
+
editorOptions: RenderCellEditorProps;
|
|
14
|
+
/** Update the cell value. Returns true if successful, false if cancelled */
|
|
15
|
+
updateCellValue: (value: any) => Promise<boolean>;
|
|
16
|
+
locale?: string;
|
|
17
|
+
schema: Schema;
|
|
18
|
+
dataSource: DataSource;
|
|
19
|
+
}) => React.JSX.Element;
|