@mwater/visualization 5.5.0 → 5.6.1
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 -2
- package/lib/MWaterContextComponent.d.ts +1 -1
- package/lib/MWaterGlobalFiltersComponent.d.ts +2 -2
- package/lib/MWaterGlobalFiltersComponent.js +11 -20
- package/lib/MWaterLoaderComponent.d.ts +4 -13
- package/lib/MWaterLoaderComponent.js +2 -11
- package/lib/TranslationsTabComponent.d.ts +34 -0
- package/lib/TranslationsTabComponent.js +256 -0
- package/lib/UndoStack.d.ts +2 -1
- package/lib/UndoStack.js +12 -6
- package/lib/dashboards/DashboardComponent.js +6 -5
- package/lib/dashboards/DashboardDesign.d.ts +1 -1
- package/lib/dashboards/ServerDashboardDataSource.d.ts +0 -1
- package/lib/dashboards/ServerDashboardDataSource.js +0 -25
- package/lib/dashboards/SettingsModalComponent.js +9 -233
- package/lib/datagrids/DatagridComponent.js +27 -2
- package/lib/datagrids/DatagridDesignerComponent.d.ts +2 -3
- package/lib/datagrids/DatagridDesignerComponent.js +108 -120
- package/lib/datagrids/DatagridViewComponent.js +33 -6
- package/lib/datagrids/OrderBysDesignerComponent.d.ts +7 -7
- package/lib/datagrids/OrderBysDesignerComponent.js +19 -28
- package/lib/index.css +45 -2
- package/lib/index.d.ts +5 -5
- package/lib/index.js +2 -3
- package/lib/layouts/blocks/BlocksDisplayComponent.d.ts +8 -1
- package/lib/layouts/blocks/BlocksDisplayComponent.js +46 -4
- package/lib/maps/BufferLayer.d.ts +0 -13
- package/lib/maps/BufferLayer.js +24 -237
- package/lib/maps/BufferLayerDesign.d.ts +1 -1
- package/lib/maps/BufferLayerDesignerComponent.d.ts +1 -1
- package/lib/maps/BufferLayerDesignerComponent.js +2 -7
- package/lib/maps/ChoroplethLayer.d.ts +1 -16
- package/lib/maps/ChoroplethLayer.js +25 -358
- package/lib/maps/ChoroplethLayerDesign.d.ts +5 -2
- package/lib/maps/ChoroplethLayerDesigner.d.ts +10 -32
- package/lib/maps/ChoroplethLayerDesigner.js +58 -89
- package/lib/maps/ClusterLayer.d.ts +0 -9
- package/lib/maps/ClusterLayer.js +0 -250
- package/lib/maps/DirectMapDataSource.js +1 -48
- package/lib/maps/EditHoverOver.d.ts +4 -3
- package/lib/maps/EditHoverOver.js +3 -3
- package/lib/maps/GridLayer.d.ts +0 -15
- package/lib/maps/GridLayer.js +0 -212
- package/lib/maps/HoverContent.js +1 -1
- package/lib/maps/Layer.d.ts +1 -26
- package/lib/maps/Layer.js +0 -13
- package/lib/maps/LeafletMapComponent.js +10 -19
- package/lib/maps/MapComponent.d.ts +19 -35
- package/lib/maps/MapComponent.js +135 -77
- package/lib/maps/MapControlComponent.d.ts +4 -5
- package/lib/maps/MapControlComponent.js +5 -12
- package/lib/maps/MapDesign.d.ts +8 -0
- package/lib/maps/MapDesignerComponent.d.ts +2 -0
- package/lib/maps/MapDesignerComponent.js +7 -2
- package/lib/maps/MapLayerDataSource.d.ts +0 -4
- package/lib/maps/MapLayerViewDesignerComponent.d.ts +3 -1
- package/lib/maps/MapLayerViewDesignerComponent.js +5 -1
- package/lib/maps/MapLayersDesignerComponent.d.ts +2 -0
- package/lib/maps/MapLayersDesignerComponent.js +2 -1
- package/lib/maps/MapTranslationsTab.d.ts +15 -0
- package/lib/maps/MapTranslationsTab.js +47 -0
- package/lib/maps/MapUtils.d.ts +11 -0
- package/lib/maps/MapUtils.js +57 -1
- package/lib/maps/MapViewComponent.d.ts +1 -1
- package/lib/maps/MapViewComponent.js +1 -8
- package/lib/maps/MarkersLayer.d.ts +1 -14
- package/lib/maps/MarkersLayer.js +89 -254
- package/lib/maps/MarkersLayerDesign.d.ts +5 -1
- package/lib/maps/MarkersLayerDesignerComponent.d.ts +32 -57
- package/lib/maps/MarkersLayerDesignerComponent.js +158 -134
- package/lib/maps/ServerMapDataSource.d.ts +0 -1
- package/lib/maps/ServerMapDataSource.js +0 -25
- package/lib/maps/SwitchableTileUrlLayer.d.ts +0 -2
- package/lib/maps/SwitchableTileUrlLayer.js +0 -9
- package/lib/maps/TileUrlLayer.d.ts +0 -1
- package/lib/maps/TileUrlLayer.js +0 -5
- package/lib/maps/VectorMapViewComponent.js +13 -10
- package/lib/maps/symbols/font-awesome/asterisk.png +0 -0
- package/lib/maps/symbols/font-awesome/ban.png +0 -0
- package/lib/maps/symbols/font-awesome/beer.png +0 -0
- package/lib/maps/symbols/font-awesome/bell.png +0 -0
- package/lib/maps/symbols/font-awesome/bolt.png +0 -0
- package/lib/maps/symbols/font-awesome/building.png +0 -0
- package/lib/maps/symbols/font-awesome/bullseye.png +0 -0
- package/lib/maps/symbols/font-awesome/bus.png +0 -0
- package/lib/maps/symbols/font-awesome/caret-up.png +0 -0
- package/lib/maps/symbols/font-awesome/certificate.png +0 -0
- package/lib/maps/symbols/font-awesome/check-circle.png +0 -0
- package/lib/maps/symbols/font-awesome/check.png +0 -0
- package/lib/maps/symbols/font-awesome/chevron-circle-down.png +0 -0
- package/lib/maps/symbols/font-awesome/chevron-circle-up.png +0 -0
- package/lib/maps/symbols/font-awesome/cloud-rain.png +0 -0
- package/lib/maps/symbols/font-awesome/cloud.png +0 -0
- package/lib/maps/symbols/font-awesome/comment.png +0 -0
- package/lib/maps/symbols/font-awesome/crosshairs.png +0 -0
- package/lib/maps/symbols/font-awesome/dot-circle-o.png +0 -0
- package/lib/maps/symbols/font-awesome/exclamation-circle.png +0 -0
- package/lib/maps/symbols/font-awesome/exclamation-triangle.png +0 -0
- package/lib/maps/symbols/font-awesome/female.png +0 -0
- package/lib/maps/symbols/font-awesome/file.png +0 -0
- package/lib/maps/symbols/font-awesome/flag.png +0 -0
- package/lib/maps/symbols/font-awesome/flask.png +0 -0
- package/lib/maps/symbols/font-awesome/h-square.png +0 -0
- package/lib/maps/symbols/font-awesome/home.png +0 -0
- package/lib/maps/symbols/font-awesome/info-circle.png +0 -0
- package/lib/maps/symbols/font-awesome/male.png +0 -0
- package/lib/maps/symbols/font-awesome/medkit.png +0 -0
- package/lib/maps/symbols/font-awesome/mobile.png +0 -0
- package/lib/maps/symbols/font-awesome/plus-circle.png +0 -0
- package/lib/maps/symbols/font-awesome/plus-square.png +0 -0
- package/lib/maps/symbols/font-awesome/plus.png +0 -0
- package/lib/maps/symbols/font-awesome/square.png +0 -0
- package/lib/maps/symbols/font-awesome/star.png +0 -0
- package/lib/maps/symbols/font-awesome/thumbs-down.png +0 -0
- package/lib/maps/symbols/font-awesome/thumbs-up.png +0 -0
- package/lib/maps/symbols/font-awesome/ticket.png +0 -0
- package/lib/maps/symbols/font-awesome/times-circle.png +0 -0
- package/lib/maps/symbols/font-awesome/times.png +0 -0
- package/lib/maps/symbols/font-awesome/tint.png +0 -0
- package/lib/maps/symbols/font-awesome/tree.png +0 -0
- package/lib/maps/symbols/font-awesome/university.png +0 -0
- package/lib/maps/symbols/font-awesome/usd.png +0 -0
- package/lib/maps/symbols/font-awesome/user.png +0 -0
- package/lib/maps/symbols/font-awesome/users.png +0 -0
- package/lib/maps/symbols/font-awesome/wheelchair.png +0 -0
- package/lib/maps/symbols/sdf-ize.sh +93 -0
- package/lib/maps/vectorMaps.d.ts +6 -6
- package/lib/maps/vectorMaps.js +33 -45
- package/lib/mwater_table_selection/IndicatorsListComponent.d.ts +4 -2
- package/lib/mwater_table_selection/IndicatorsListComponent.js +103 -34
- package/lib/mwater_table_selection/MWaterCalculatedDataSourcesListComponent.d.ts +18 -0
- package/lib/mwater_table_selection/MWaterCalculatedDataSourcesListComponent.js +80 -0
- package/lib/mwater_table_selection/MWaterCompleteTableSelectComponent.d.ts +26 -0
- package/lib/mwater_table_selection/MWaterCompleteTableSelectComponent.js +237 -51
- package/lib/mwater_table_selection/MWaterTableSelectComponent.d.ts +2 -2
- package/lib/mwater_table_selection/MWaterTableSelectComponent.js +9 -4
- package/lib/mwater_table_selection/MWaterWorkflowsSelectComponent.d.ts +19 -0
- package/lib/mwater_table_selection/MWaterWorkflowsSelectComponent.js +111 -0
- package/lib/quickfilter/QuickfiltersComponent.d.ts +3 -102
- package/lib/quickfilter/QuickfiltersComponent.js +53 -110
- package/lib/quickfilter/TextLiteralComponent.d.ts +23 -47
- package/lib/quickfilter/TextLiteralComponent.js +85 -82
- package/lib/widgets/MapWidget.js +6 -3
- package/lib/widgets/text/ExprItemEditorComponent.d.ts +3 -8
- package/lib/widgets/text/ExprItemEditorComponent.js +36 -33
- package/lib/widgets/text/ExprUpdateModalComponent.d.ts +1 -0
- package/package.json +3 -4
- package/src/ColorComponent.tsx +2 -2
- package/src/MWaterContextComponent.tsx +1 -1
- package/src/{MWaterGlobalFiltersComponent.ts → MWaterGlobalFiltersComponent.tsx} +32 -33
- package/src/{MWaterLoaderComponent.ts → MWaterLoaderComponent.tsx} +17 -18
- package/src/TranslationsTabComponent.tsx +429 -0
- package/src/UndoStack.ts +14 -6
- package/src/dashboards/DashboardComponent.tsx +6 -5
- package/src/dashboards/DashboardDesign.ts +1 -1
- package/src/dashboards/ServerDashboardDataSource.ts +0 -31
- package/src/dashboards/SettingsModalComponent.tsx +27 -383
- package/src/datagrids/DatagridComponent.tsx +36 -2
- package/src/datagrids/DatagridDesignerComponent.tsx +241 -229
- package/src/datagrids/DatagridViewComponent.tsx +44 -7
- package/src/datagrids/OrderBysDesignerComponent.tsx +61 -70
- package/src/index.css +45 -2
- package/src/index.ts +5 -11
- package/src/layouts/blocks/BlocksDisplayComponent.tsx +60 -5
- package/src/maps/BufferLayer.ts +30 -263
- package/src/maps/BufferLayerDesign.ts +1 -1
- package/src/maps/BufferLayerDesignerComponent.tsx +2 -7
- package/src/maps/ChoroplethLayer.ts +30 -394
- package/src/maps/ChoroplethLayerDesign.ts +5 -2
- package/src/maps/ChoroplethLayerDesigner.tsx +169 -165
- package/src/maps/ClusterLayer.ts +0 -274
- package/src/maps/DirectMapDataSource.ts +2 -61
- package/src/maps/EditHoverOver.tsx +9 -5
- package/src/maps/GridLayer.ts +0 -224
- package/src/maps/HoverContent.tsx +1 -1
- package/src/maps/Layer.ts +1 -35
- package/src/maps/LeafletMapComponent.tsx +10 -19
- package/src/maps/MapComponent.tsx +448 -0
- package/src/maps/MapControlComponent.tsx +41 -0
- package/src/maps/MapDesign.ts +6 -0
- package/src/maps/MapDesignerComponent.tsx +18 -1
- package/src/maps/MapLayerDataSource.ts +0 -5
- package/src/maps/MapLayerViewDesignerComponent.ts +9 -2
- package/src/maps/MapLayersDesignerComponent.ts +4 -1
- package/src/maps/MapTranslationsTab.tsx +53 -0
- package/src/maps/MapUtils.ts +61 -1
- package/src/maps/MapViewComponent.tsx +2 -8
- package/src/maps/MarkersLayer.ts +101 -275
- package/src/maps/MarkersLayerDesign.ts +7 -1
- package/src/maps/MarkersLayerDesignerComponent.tsx +436 -0
- package/src/maps/ServerMapDataSource.ts +0 -31
- package/src/maps/SwitchableTileUrlLayer.tsx +0 -11
- package/src/maps/TileUrlLayer.tsx +0 -6
- package/src/maps/VectorMapViewComponent.tsx +15 -15
- package/src/maps/symbols/font-awesome/asterisk.png +0 -0
- package/src/maps/symbols/font-awesome/ban.png +0 -0
- package/src/maps/symbols/font-awesome/beer.png +0 -0
- package/src/maps/symbols/font-awesome/bell.png +0 -0
- package/src/maps/symbols/font-awesome/bolt.png +0 -0
- package/src/maps/symbols/font-awesome/building.png +0 -0
- package/src/maps/symbols/font-awesome/bullseye.png +0 -0
- package/src/maps/symbols/font-awesome/bus.png +0 -0
- package/src/maps/symbols/font-awesome/caret-up.png +0 -0
- package/src/maps/symbols/font-awesome/certificate.png +0 -0
- package/src/maps/symbols/font-awesome/check-circle.png +0 -0
- package/src/maps/symbols/font-awesome/check.png +0 -0
- package/src/maps/symbols/font-awesome/chevron-circle-down.png +0 -0
- package/src/maps/symbols/font-awesome/chevron-circle-up.png +0 -0
- package/src/maps/symbols/font-awesome/cloud-rain.png +0 -0
- package/src/maps/symbols/font-awesome/cloud.png +0 -0
- package/src/maps/symbols/font-awesome/comment.png +0 -0
- package/src/maps/symbols/font-awesome/crosshairs.png +0 -0
- package/src/maps/symbols/font-awesome/dot-circle-o.png +0 -0
- package/src/maps/symbols/font-awesome/exclamation-circle.png +0 -0
- package/src/maps/symbols/font-awesome/exclamation-triangle.png +0 -0
- package/src/maps/symbols/font-awesome/female.png +0 -0
- package/src/maps/symbols/font-awesome/file.png +0 -0
- package/src/maps/symbols/font-awesome/flag.png +0 -0
- package/src/maps/symbols/font-awesome/flask.png +0 -0
- package/src/maps/symbols/font-awesome/h-square.png +0 -0
- package/src/maps/symbols/font-awesome/home.png +0 -0
- package/src/maps/symbols/font-awesome/info-circle.png +0 -0
- package/src/maps/symbols/font-awesome/male.png +0 -0
- package/src/maps/symbols/font-awesome/medkit.png +0 -0
- package/src/maps/symbols/font-awesome/mobile.png +0 -0
- package/src/maps/symbols/font-awesome/plus-circle.png +0 -0
- package/src/maps/symbols/font-awesome/plus-square.png +0 -0
- package/src/maps/symbols/font-awesome/plus.png +0 -0
- package/src/maps/symbols/font-awesome/square.png +0 -0
- package/src/maps/symbols/font-awesome/star.png +0 -0
- package/src/maps/symbols/font-awesome/thumbs-down.png +0 -0
- package/src/maps/symbols/font-awesome/thumbs-up.png +0 -0
- package/src/maps/symbols/font-awesome/ticket.png +0 -0
- package/src/maps/symbols/font-awesome/times-circle.png +0 -0
- package/src/maps/symbols/font-awesome/times.png +0 -0
- package/src/maps/symbols/font-awesome/tint.png +0 -0
- package/src/maps/symbols/font-awesome/tree.png +0 -0
- package/src/maps/symbols/font-awesome/university.png +0 -0
- package/src/maps/symbols/font-awesome/usd.png +0 -0
- package/src/maps/symbols/font-awesome/user.png +0 -0
- package/src/maps/symbols/font-awesome/users.png +0 -0
- package/src/maps/symbols/font-awesome/wheelchair.png +0 -0
- package/src/maps/symbols/sdf-ize.sh +93 -0
- package/src/maps/vectorMaps.tsx +32 -53
- package/src/mwater_table_selection/IndicatorsListComponent.tsx +165 -37
- package/src/mwater_table_selection/MWaterCalculatedDataSourcesListComponent.tsx +111 -0
- package/src/mwater_table_selection/MWaterCompleteTableSelectComponent.tsx +373 -37
- package/src/mwater_table_selection/MWaterTableSelectComponent.tsx +12 -8
- package/src/mwater_table_selection/MWaterWorkflowsSelectComponent.tsx +159 -0
- package/src/quickfilter/{QuickfiltersComponent.ts → QuickfiltersComponent.tsx} +165 -158
- package/src/quickfilter/TextLiteralComponent.tsx +197 -0
- package/src/widgets/MapWidget.tsx +11 -1
- package/src/widgets/text/ExprItemEditorComponent.tsx +83 -77
- package/src/widgets/text/ExprUpdateModalComponent.tsx +1 -0
- package/test/UndoStackTests.ts +52 -1
- package/.storybook/config.js +0 -7
- package/.storybook/head.html +0 -3
- package/.storybook/webpack.config.js +0 -15
- package/src/maps/BingLayer.ts +0 -146
- package/src/maps/MapComponent.ts +0 -312
- package/src/maps/MapControlComponent.ts +0 -46
- package/src/maps/MarkersLayerDesignerComponent.ts +0 -374
- package/src/maps/RasterMapViewComponent.ts +0 -345
- package/src/quickfilter/TextLiteralComponent.ts +0 -165
- package/stories/UpdateableComponent.js +0 -29
- package/stories/consoles.js +0 -202
- package/stories/dashboards.js +0 -217
- package/stories/datagridDesign.js +0 -114
- package/stories/datagrids.js +0 -69
- package/stories/dates.js +0 -80
- package/stories/exprcomponent.js +0 -43
- package/stories/index.js +0 -18
- package/stories/leaflet.js +0 -59
- package/stories/maps.js +0 -24
- package/stories/pivotChart.js +0 -235
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
import _ from "lodash"
|
|
2
|
-
import React, { useMemo
|
|
3
|
-
import { languages } from "../languages"
|
|
2
|
+
import React, { useMemo } from "react"
|
|
4
3
|
import * as ui from "@mwater/react-library/lib/bootstrap"
|
|
5
|
-
import { default as ReactSelect } from "react-select"
|
|
6
4
|
import * as DashboardUtils from "./DashboardUtils"
|
|
7
5
|
import ActionCancelModalComponent from "@mwater/react-library/lib/ActionCancelModalComponent"
|
|
8
6
|
import QuickfiltersDesignComponent from "../quickfilter/QuickfiltersDesignComponent"
|
|
9
7
|
import FiltersDesignerComponent from "../FiltersDesignerComponent"
|
|
10
|
-
import { DataSource,
|
|
8
|
+
import { DataSource, Schema } from "@mwater/expressions"
|
|
11
9
|
import { DashboardDesign } from "./DashboardDesign"
|
|
12
10
|
import { GlobalFiltersElementFactoryContext } from "../MWaterContextComponent"
|
|
13
11
|
import produce from "immer"
|
|
14
12
|
import TabbedComponent from "@mwater/react-library/lib/TabbedComponent"
|
|
15
|
-
import
|
|
16
|
-
import * as localizeUtils from "ez-localize/lib/utils"
|
|
17
|
-
import { canAutoTranslate, translateStrings } from "../autotranslate"
|
|
13
|
+
import { TranslationsTabComponent } from "../TranslationsTabComponent"
|
|
18
14
|
|
|
19
15
|
export interface SettingsModalComponentProps {
|
|
20
16
|
onDesignChange: (design: DashboardDesign) => void
|
|
@@ -225,388 +221,36 @@ interface LanguageTabProps {
|
|
|
225
221
|
schema: Schema
|
|
226
222
|
}
|
|
227
223
|
|
|
224
|
+
/** Wrapper around TranslationsTabComponent for dashboard-specific usage */
|
|
228
225
|
function LanguageTab({ design, onDesignChange, schema }: LanguageTabProps) {
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
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
|
-
}
|
|
323
|
-
|
|
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()
|
|
226
|
+
const translatableStrings = useMemo(
|
|
227
|
+
() => DashboardUtils.getTranslatableStringsFromDashboard(design, schema),
|
|
228
|
+
[design, schema]
|
|
229
|
+
)
|
|
359
230
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
231
|
+
return (
|
|
232
|
+
<TranslationsTabComponent
|
|
233
|
+
locale={design.locale || "en"}
|
|
234
|
+
otherLocales={design.otherLocales || []}
|
|
235
|
+
translations={design.translations || {}}
|
|
236
|
+
translatableStrings={translatableStrings}
|
|
237
|
+
onLocaleChange={(locale) =>
|
|
238
|
+
onDesignChange(produce(design, draft => {
|
|
239
|
+
draft.locale = locale
|
|
240
|
+
}))
|
|
363
241
|
}
|
|
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)
|
|
242
|
+
onOtherLocalesChange={(otherLocales) =>
|
|
383
243
|
onDesignChange(produce(design, draft => {
|
|
384
|
-
draft.
|
|
244
|
+
draft.otherLocales = otherLocales
|
|
385
245
|
}))
|
|
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
246
|
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
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.`}
|
|
404
|
-
</div>
|
|
405
|
-
|
|
406
|
-
<ReactSelect
|
|
407
|
-
value={_.findWhere(localeOptions, { value: design.locale || "en" }) || null}
|
|
408
|
-
options={localeOptions}
|
|
409
|
-
onChange={(locale: any) =>
|
|
410
|
-
onDesignChange(produce(design, draft => {
|
|
411
|
-
draft.locale = locale.value
|
|
412
|
-
}))
|
|
413
|
-
}
|
|
414
|
-
/>
|
|
415
|
-
|
|
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
|
|
428
|
-
}
|
|
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
|
-
/>
|
|
520
|
-
</>
|
|
521
|
-
)}
|
|
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]
|
|
247
|
+
onTranslationsChange={(translations) =>
|
|
248
|
+
onDesignChange(produce(design, draft => {
|
|
249
|
+
draft.translations = translations
|
|
250
|
+
}))
|
|
574
251
|
}
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
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>
|
|
252
|
+
downloadFilename="Dashboard Translations.xlsx"
|
|
253
|
+
baseLanguageDescription={T`This is the language that the dashboard is written in.`}
|
|
254
|
+
/>
|
|
611
255
|
)
|
|
612
256
|
}
|
|
@@ -126,6 +126,12 @@ export default forwardRef<DatagridComponentRef, DatagridComponentProps>(function
|
|
|
126
126
|
}
|
|
127
127
|
let activeFilters = filters || []
|
|
128
128
|
|
|
129
|
+
// Clean before counting
|
|
130
|
+
const cleanedDesign = new DatagridUtils(schema).cleanDesign(design)
|
|
131
|
+
if (new DatagridUtils(schema).validateDesign(cleanedDesign)) {
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
|
|
129
135
|
// Compile quickfilters
|
|
130
136
|
activeFilters = activeFilters.concat(getQuickfilterFilters())
|
|
131
137
|
datagridDataSource.countRows(design,
|
|
@@ -529,8 +535,36 @@ export default forwardRef<DatagridComponentRef, DatagridComponentProps>(function
|
|
|
529
535
|
)
|
|
530
536
|
} else if (onDesignChange) {
|
|
531
537
|
return (
|
|
532
|
-
<div style={{
|
|
533
|
-
|
|
538
|
+
<div style={{
|
|
539
|
+
display: "flex",
|
|
540
|
+
flexDirection: "column",
|
|
541
|
+
alignItems: "center",
|
|
542
|
+
justifyContent: "center",
|
|
543
|
+
height: "100%",
|
|
544
|
+
padding: "40px"
|
|
545
|
+
}}>
|
|
546
|
+
<div style={{
|
|
547
|
+
textAlign: "center",
|
|
548
|
+
marginBottom: "24px"
|
|
549
|
+
}}>
|
|
550
|
+
<i className="fas fa-table text-muted" style={{
|
|
551
|
+
fontSize: "48px",
|
|
552
|
+
marginBottom: "16px"
|
|
553
|
+
}} />
|
|
554
|
+
<div style={{
|
|
555
|
+
fontSize: "14px"
|
|
556
|
+
}} className="text-muted">
|
|
557
|
+
{T`Configure a data source and columns to get started.`}
|
|
558
|
+
</div>
|
|
559
|
+
</div>
|
|
560
|
+
|
|
561
|
+
<button
|
|
562
|
+
className="btn btn-primary"
|
|
563
|
+
onClick={handleEdit}
|
|
564
|
+
>
|
|
565
|
+
<i className="fas fa-cog" style={{ marginRight: "8px" }} />
|
|
566
|
+
{T`Configure`}
|
|
567
|
+
</button>
|
|
534
568
|
</div>
|
|
535
569
|
)
|
|
536
570
|
} else {
|