@hisptz/dhis2-analytics 1.0.49 → 1.0.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/.gitignore +5 -0
  2. package/build/cjs/components/ChartAnalytics/ChartAnalytics.test.js +1 -1
  3. package/build/cjs/components/ChartAnalytics/models/bar.js +24 -0
  4. package/build/cjs/components/ChartAnalytics/utils/chart.js +5 -0
  5. package/build/es/components/ChartAnalytics/ChartAnalytics.test.js +1 -1
  6. package/build/es/components/ChartAnalytics/models/bar.js +16 -0
  7. package/build/es/components/ChartAnalytics/utils/chart.js +5 -0
  8. package/build/types/components/ChartAnalytics/models/bar.d.ts +8 -0
  9. package/build/types/components/ChartAnalytics/types/props.d.ts +1 -1
  10. package/d2.config.js +8 -0
  11. package/i18n/en.pot +439 -0
  12. package/package.json +5 -5
  13. package/src/components/ChartAnalytics/ChartAnalytics.test.tsx +51 -0
  14. package/src/components/ChartAnalytics/components/DownloadMenu/components/Menu.tsx +48 -0
  15. package/src/components/ChartAnalytics/components/DownloadMenu/constants/menu.ts +38 -0
  16. package/src/components/ChartAnalytics/components/DownloadMenu/index.tsx +65 -0
  17. package/src/components/ChartAnalytics/components/DownloadMenu/interfaces/menu.ts +1 -0
  18. package/src/components/ChartAnalytics/hooks/useChart.ts +35 -0
  19. package/src/components/ChartAnalytics/index.tsx +28 -0
  20. package/src/components/ChartAnalytics/models/bar.ts +20 -0
  21. package/src/components/ChartAnalytics/models/column.ts +52 -0
  22. package/src/components/ChartAnalytics/models/index.ts +111 -0
  23. package/src/components/ChartAnalytics/models/line.ts +31 -0
  24. package/src/components/ChartAnalytics/models/multi-series.ts +115 -0
  25. package/src/components/ChartAnalytics/models/pie.ts +54 -0
  26. package/src/components/ChartAnalytics/services/export.ts +38 -0
  27. package/src/components/ChartAnalytics/styles/custom-highchart.css +48 -0
  28. package/src/components/ChartAnalytics/types/props.tsx +48 -0
  29. package/src/components/ChartAnalytics/utils/chart.ts +128 -0
  30. package/src/components/CircularProgressDashboard/CircularProgressIndicator.test.tsx +9 -0
  31. package/src/components/CircularProgressDashboard/index.tsx +36 -0
  32. package/src/components/CircularProgressDashboard/types/props.tsx +17 -0
  33. package/src/components/CustomPivotTable/components/Table/index.tsx +23 -0
  34. package/src/components/CustomPivotTable/components/TableBody/TableBody.module.css +12 -0
  35. package/src/components/CustomPivotTable/components/TableBody/index.tsx +96 -0
  36. package/src/components/CustomPivotTable/components/TableHeaders/TableHeaders.module.css +10 -0
  37. package/src/components/CustomPivotTable/components/TableHeaders/index.tsx +94 -0
  38. package/src/components/CustomPivotTable/index.tsx +63 -0
  39. package/src/components/CustomPivotTable/interfaces/index.ts +1 -0
  40. package/src/components/CustomPivotTable/services/engine.ts +102 -0
  41. package/src/components/CustomPivotTable/state/engine.tsx +22 -0
  42. package/src/components/Map/components/EarthEngineLayerConfiguration/EarthEngineLayerConfigModal.stories.tsx +28 -0
  43. package/src/components/Map/components/EarthEngineLayerConfiguration/EarthEngineLayerConfiguration.stories.tsx +34 -0
  44. package/src/components/Map/components/EarthEngineLayerConfiguration/index.tsx +412 -0
  45. package/src/components/Map/components/MapArea/index.tsx +83 -0
  46. package/src/components/Map/components/MapArea/interfaces/index.ts +39 -0
  47. package/src/components/Map/components/MapControls/components/CustomControl/index.tsx +24 -0
  48. package/src/components/Map/components/MapControls/components/DownloadControl/index.tsx +11 -0
  49. package/src/components/Map/components/MapControls/components/FullscreenControl/index.tsx +7 -0
  50. package/src/components/Map/components/MapControls/index.tsx +24 -0
  51. package/src/components/Map/components/MapLayer/components/BoundaryLayer/hooks/useBoundaryData.ts +7 -0
  52. package/src/components/Map/components/MapLayer/components/BoundaryLayer/index.tsx +55 -0
  53. package/src/components/Map/components/MapLayer/components/GoogleEngineLayer/components/EarthEngineLegend.tsx +74 -0
  54. package/src/components/Map/components/MapLayer/components/GoogleEngineLayer/constants/index.ts +430 -0
  55. package/src/components/Map/components/MapLayer/components/GoogleEngineLayer/hooks/index.ts +34 -0
  56. package/src/components/Map/components/MapLayer/components/GoogleEngineLayer/index.tsx +185 -0
  57. package/src/components/Map/components/MapLayer/components/GoogleEngineLayer/interfaces/index.ts +56 -0
  58. package/src/components/Map/components/MapLayer/components/GoogleEngineLayer/services/api.js +34241 -0
  59. package/src/components/Map/components/MapLayer/components/GoogleEngineLayer/services/engine.ts +431 -0
  60. package/src/components/Map/components/MapLayer/components/GoogleEngineLayer/utils/index.ts +105 -0
  61. package/src/components/Map/components/MapLayer/components/LegendArea/LegendArea.module.css +12 -0
  62. package/src/components/Map/components/MapLayer/components/LegendArea/components/LegendCardHeader/index.tsx +17 -0
  63. package/src/components/Map/components/MapLayer/components/LegendArea/index.tsx +167 -0
  64. package/src/components/Map/components/MapLayer/components/PointLayer/components/PointLegend/index.tsx +44 -0
  65. package/src/components/Map/components/MapLayer/components/PointLayer/hooks/index.ts +8 -0
  66. package/src/components/Map/components/MapLayer/components/PointLayer/index.tsx +36 -0
  67. package/src/components/Map/components/MapLayer/components/ThematicLayer/components/Bubble/components/BubbleLegend/components/Bubble.tsx +48 -0
  68. package/src/components/Map/components/MapLayer/components/ThematicLayer/components/Bubble/components/BubbleLegend/components/Bubbles.tsx +150 -0
  69. package/src/components/Map/components/MapLayer/components/ThematicLayer/components/Bubble/components/BubbleLegend/index.tsx +39 -0
  70. package/src/components/Map/components/MapLayer/components/ThematicLayer/components/Bubble/index.tsx +57 -0
  71. package/src/components/Map/components/MapLayer/components/ThematicLayer/components/Choropleth/components/ChoroplethLegend.tsx +43 -0
  72. package/src/components/Map/components/MapLayer/components/ThematicLayer/components/Choropleth/index.tsx +38 -0
  73. package/src/components/Map/components/MapLayer/components/ThematicLayer/components/CustomTooltip/index.tsx +26 -0
  74. package/src/components/Map/components/MapLayer/components/ThematicLayer/hooks/config.ts +10 -0
  75. package/src/components/Map/components/MapLayer/components/ThematicLayer/index.tsx +46 -0
  76. package/src/components/Map/components/MapLayer/components/ThematicLayer/styles/legends.css +62 -0
  77. package/src/components/Map/components/MapLayer/index.tsx +32 -0
  78. package/src/components/Map/components/MapLayer/interfaces/index.ts +139 -0
  79. package/src/components/Map/components/MapProvider/components/MapLayerProvider/hooks/index.tsx +368 -0
  80. package/src/components/Map/components/MapProvider/components/MapLayerProvider/index.tsx +105 -0
  81. package/src/components/Map/components/MapProvider/hooks/index.ts +14 -0
  82. package/src/components/Map/components/MapProvider/index.tsx +93 -0
  83. package/src/components/Map/components/MapUpdater/index.tsx +8 -0
  84. package/src/components/Map/components/ThematicLayerConfiguration/ThematicLayerConfigModal.stories.tsx +28 -0
  85. package/src/components/Map/components/ThematicLayerConfiguration/ThematicLayerConfiguration.stories.tsx +34 -0
  86. package/src/components/Map/components/ThematicLayerConfiguration/components/ColorScaleSelect/components/ColorScale/index.tsx +24 -0
  87. package/src/components/Map/components/ThematicLayerConfiguration/components/ColorScaleSelect/constants/colors.ts +433 -0
  88. package/src/components/Map/components/ThematicLayerConfiguration/components/ColorScaleSelect/index.tsx +50 -0
  89. package/src/components/Map/components/ThematicLayerConfiguration/components/ColorScaleSelect/styles/ColorScale.module.css +15 -0
  90. package/src/components/Map/components/ThematicLayerConfiguration/components/ColorScaleSelect/styles/ColorScaleSelect.module.css +12 -0
  91. package/src/components/Map/components/ThematicLayerConfiguration/components/ColorScaleSelect/utils/colors.ts +91 -0
  92. package/src/components/Map/components/ThematicLayerConfiguration/components/CustomLegend/index.tsx +45 -0
  93. package/src/components/Map/components/ThematicLayerConfiguration/components/IndicatorSelectorModal/index.tsx +47 -0
  94. package/src/components/Map/components/ThematicLayerConfiguration/components/LegendSetSelector/index.tsx +57 -0
  95. package/src/components/Map/components/ThematicLayerConfiguration/index.tsx +248 -0
  96. package/src/components/Map/constants/colors.ts +434 -0
  97. package/src/components/Map/constants/legendSet.ts +19 -0
  98. package/src/components/Map/hooks/map.ts +47 -0
  99. package/src/components/Map/index.tsx +65 -0
  100. package/src/components/Map/interfaces/index.ts +57 -0
  101. package/src/components/Map/state/index.tsx +31 -0
  102. package/src/components/Map/utils/colors.ts +95 -0
  103. package/src/components/Map/utils/helpers.ts +15 -0
  104. package/src/components/Map/utils/map.ts +150 -0
  105. package/src/components/SingleValueContainer/SingleValueContainer.test.tsx +24 -0
  106. package/src/components/SingleValueContainer/components/SingleValueItem/SingleValueItem.tsx +46 -0
  107. package/src/components/SingleValueContainer/components/SingleValueItem/SingleValuePercentage.tsx +12 -0
  108. package/src/components/SingleValueContainer/index.tsx +37 -0
  109. package/src/components/SingleValueContainer/styles/SingleValueContainer.module.css +39 -0
  110. package/src/components/SingleValueContainer/types/props.tsx +16 -0
  111. package/src/components/Visualization/components/AnalyticsDataProvider/index.tsx +76 -0
  112. package/src/components/Visualization/components/DimensionsProvider/index.tsx +51 -0
  113. package/src/components/Visualization/components/LayoutProvider/index.tsx +34 -0
  114. package/src/components/Visualization/components/VisualizationDimensionSelector/index.tsx +59 -0
  115. package/src/components/Visualization/components/VisualizationProvider/index.tsx +31 -0
  116. package/src/components/Visualization/components/VisualizationSelector/index.tsx +157 -0
  117. package/src/components/Visualization/components/VisualizationTypeProvider/index.tsx +40 -0
  118. package/src/components/Visualization/components/VisualizationTypeSelector/index.tsx +46 -0
  119. package/src/components/Visualization/index.tsx +103 -0
  120. package/src/index.ts +6 -0
  121. package/src/locales/en/translations.json +138 -0
  122. package/src/locales/index.js +16 -0
  123. package/tsconfig.build.json +46 -0
  124. package/tsconfig.json +51 -0
  125. package/LICENSE +0 -29
@@ -0,0 +1,45 @@
1
+ import {Controller, useWatch} from "react-hook-form";
2
+ import {Field, SingleSelectField, SingleSelectOption} from "@dhis2/ui";
3
+ import {defaultClasses, defaultColorScaleName} from "../../../../utils/colors";
4
+ import i18n from "@dhis2/d2-i18n";
5
+ import ColorScaleSelect from "../ColorScaleSelect";
6
+ import React from "react";
7
+
8
+ export function CustomLegend() {
9
+ const scale = useWatch({
10
+ name: "dataItem.legendConfig.scale",
11
+ });
12
+
13
+ return (
14
+ <div className="row gap-16 space-between">
15
+ <div style={{ width: "30%" }}>
16
+ <Controller
17
+ name="dataItem.legendConfig.scale"
18
+ render={({ field, fieldState }) => (
19
+ <SingleSelectField
20
+ validationText={fieldState.error?.message}
21
+ error={Boolean(fieldState.error)}
22
+ selected={field.value?.toString() ?? defaultClasses.toString()}
23
+ label={i18n.t("Classes")}
24
+ onChange={({ selected }: { selected: string }) => field.onChange(parseInt(selected))}
25
+ name="scale">
26
+ {[3, 4, 5, 6, 7, 8, 9].map((value) => (
27
+ <SingleSelectOption key={`${value}-classes-option`} label={`${value}`} value={value?.toString()} />
28
+ ))}
29
+ </SingleSelectField>
30
+ )}
31
+ />
32
+ </div>
33
+ <div style={{ width: "70%" }}>
34
+ <Controller
35
+ name="dataItem.legendConfig.colorClass"
36
+ render={({ field }) => (
37
+ <Field label={i18n.t("Colors")}>
38
+ <ColorScaleSelect count={scale ?? defaultClasses} colorClass={field.value ?? defaultColorScaleName} width={300} onChange={field.onChange} />
39
+ </Field>
40
+ )}
41
+ />
42
+ </div>
43
+ </div>
44
+ );
45
+ }
@@ -0,0 +1,47 @@
1
+ import i18n from "@dhis2/d2-i18n";
2
+ import {Button, ButtonStrip, Modal, ModalActions, ModalContent, ModalTitle} from "@dhis2/ui";
3
+ import React, {useCallback, useState} from "react";
4
+ import {DataSourceSelector} from "@hisptz/dhis2-ui";
5
+
6
+ export default function IndicatorSelectorModal({
7
+ onUpdate,
8
+ onClose,
9
+ hide,
10
+ selected,
11
+ disabled,
12
+ ...props
13
+ }: {
14
+ onUpdate: (data: any) => void;
15
+ onClose: () => void;
16
+ hide: boolean;
17
+ selected: any;
18
+ disabled?: string[];
19
+ }) {
20
+ const [selectedIndicators, setSelectedIndicators] = useState(selected);
21
+
22
+ const onUpdateClick = useCallback(() => {
23
+ onUpdate(selectedIndicators);
24
+ onClose();
25
+ }, [onUpdate, selectedIndicators]);
26
+
27
+ const onSelect = useCallback((indicators: any) => {
28
+ setSelectedIndicators(indicators);
29
+ }, []);
30
+
31
+ return (
32
+ <Modal placement="middle" hide={hide} onClose={onClose}>
33
+ <ModalTitle>{i18n.t("Select Data Item")}</ModalTitle>
34
+ <ModalContent>
35
+ <DataSourceSelector {...props} disabled={disabled} maxSelections={1} selected={selectedIndicators} onSelect={onSelect} />
36
+ </ModalContent>
37
+ <ModalActions>
38
+ <ButtonStrip>
39
+ <Button onClick={onClose}>{i18n.t("Cancel")}</Button>
40
+ <Button primary onClick={onUpdateClick}>
41
+ {i18n.t("Update")}
42
+ </Button>
43
+ </ButtonStrip>
44
+ </ModalActions>
45
+ </Modal>
46
+ );
47
+ }
@@ -0,0 +1,57 @@
1
+ import {useDataQuery} from "@dhis2/app-runtime";
2
+ import React, {useMemo} from "react";
3
+ import {LegendSet} from "@hisptz/dhis2-utils";
4
+ import {SingleSelectField, SingleSelectOption} from "@dhis2/ui";
5
+ import i18n from "@dhis2/d2-i18n";
6
+ import {isEmpty} from "lodash";
7
+
8
+ const legendSetQuery = {
9
+ legendSets: {
10
+ resource: "legendSets",
11
+ params: {
12
+ fields: ["displayName", "id"],
13
+ },
14
+ },
15
+ };
16
+
17
+ export function LegendSetSelector({
18
+ selected,
19
+ onChange,
20
+ error,
21
+ required,
22
+ ...props
23
+ }: {
24
+ selected?: string;
25
+ onChange: (value: string) => void;
26
+ error?: { message?: string };
27
+ required?: boolean;
28
+ }) {
29
+ const { loading, data } = useDataQuery(legendSetQuery);
30
+ const options = useMemo(() => {
31
+ if (data) {
32
+ return (data?.legendSets as { legendSets?: LegendSet[] })?.legendSets?.map(({ displayName, id }) => ({
33
+ label: displayName,
34
+ value: id,
35
+ }));
36
+ }
37
+ return [];
38
+ }, [data]);
39
+
40
+ return (
41
+ <SingleSelectField
42
+ required={required}
43
+ error={Boolean(error)}
44
+ validationText={error?.message}
45
+ {...props}
46
+ label={i18n.t("Legend set")}
47
+ filterable
48
+ selected={!isEmpty(options) ? selected : undefined}
49
+ loadingText={i18n.t("Please wait...")}
50
+ onChange={({ selected }: { selected: string }) => onChange(selected)}
51
+ loading={loading}>
52
+ {options?.map(({ label, value }) => (
53
+ <SingleSelectOption key={`${label}-legend-option`} label={label} value={value} />
54
+ ))}
55
+ </SingleSelectField>
56
+ );
57
+ }
@@ -0,0 +1,248 @@
1
+ import i18n from "@dhis2/d2-i18n";
2
+ import {
3
+ Button,
4
+ ButtonStrip,
5
+ Field,
6
+ InputField,
7
+ Modal,
8
+ ModalActions,
9
+ ModalContent,
10
+ ModalTitle,
11
+ Radio,
12
+ SingleSelectField,
13
+ SingleSelectOption
14
+ } from "@dhis2/ui";
15
+ import {Controller, FormProvider, useForm, useFormContext, UseFormReturn, useWatch} from "react-hook-form";
16
+ import React, {useMemo, useState} from "react";
17
+ import {compact} from "lodash";
18
+ import {defaultClasses, defaultColorScaleName} from "../../utils/colors";
19
+ import {ThematicLayerConfig} from "../MapLayer/interfaces";
20
+ import IndicatorSelectorModal from "./components/IndicatorSelectorModal";
21
+ import {LegendSetSelector} from "./components/LegendSetSelector";
22
+ import {CustomLegend} from "./components/CustomLegend";
23
+
24
+ export interface ThematicLayerConfigurationProps {
25
+ exclude?: string[];
26
+ form: UseFormReturn<ThematicLayerConfig>;
27
+ [key: string]: any;
28
+ }
29
+
30
+ export function RadiusField() {
31
+ return (
32
+ <div className="row gap-8">
33
+ <Controller
34
+ render={({ field, fieldState }) => (
35
+ <InputField
36
+ {...field}
37
+ error={Boolean(fieldState.error)}
38
+ validationText={fieldState.error?.message}
39
+ value={field.value?.toString()}
40
+ onChange={({ value }: { value: string }) => field.onChange(parseInt(value))}
41
+ label={i18n.t("Min")}
42
+ type="number"
43
+ />
44
+ )}
45
+ name={"radius.min"}
46
+ />
47
+ <Controller
48
+ render={({ field, fieldState }) => (
49
+ <InputField
50
+ value={field.value?.toString()}
51
+ onChange={({ value }: { value: string }) => field.onChange(parseInt(value))}
52
+ label={i18n.t("Max")}
53
+ type="number"
54
+ />
55
+ )}
56
+ name={"radius.max"}
57
+ />
58
+ </div>
59
+ );
60
+ }
61
+
62
+ function TypeField() {
63
+ const { setValue } = useFormContext();
64
+ const resetFields = (type: string) => {
65
+ if (type === "bubble") {
66
+ setValue(`radius`, {
67
+ min: 5,
68
+ max: 30,
69
+ });
70
+ } else {
71
+ setValue(`radius`, undefined);
72
+ }
73
+ };
74
+
75
+ return (
76
+ <Controller
77
+ rules={{
78
+ required: i18n.t("Layer type is required"),
79
+ }}
80
+ render={({ field, fieldState }) => {
81
+ return (
82
+ <SingleSelectField
83
+ label={i18n.t("Layer type")}
84
+ required
85
+ error={Boolean(fieldState.error)}
86
+ validationText={fieldState.error?.message}
87
+ onChange={({ selected }: { selected: string }) => {
88
+ resetFields(selected);
89
+ field.onChange(selected);
90
+ }}
91
+ selected={field.value}>
92
+ <SingleSelectOption value={"choropleth"} label={i18n.t("Choropleth")} />
93
+ <SingleSelectOption value={"bubble"} label={i18n.t("Bubble")} />
94
+ </SingleSelectField>
95
+ );
96
+ }}
97
+ name={"type"}
98
+ />
99
+ );
100
+ }
101
+
102
+ export function ThematicLayerConfiguration({ exclude, form }: ThematicLayerConfigurationProps) {
103
+ const [type, legendSet, dataItemId] = useWatch({
104
+ control: form.control,
105
+ name: ["type", "dataItem.legendSet", "dataItem.id"],
106
+ });
107
+ const [legendType, setLegendType] = useState(legendSet ? "legendSet" : "custom");
108
+ const [dataSelectorOpen, setDataSelectorOpen] = useState(false);
109
+
110
+ const onLegendTypeChange =
111
+ (type: string) =>
112
+ ({ value }: { value: string }) => {
113
+ if (type === "custom") {
114
+ form.setValue("dataItem.legendSet", undefined);
115
+ form.setValue("dataItem.legendConfig.scale", defaultClasses);
116
+ form.setValue("dataItem.legendConfig.colorClass", defaultColorScaleName);
117
+ } else {
118
+ form.setValue("dataItem.legendConfig", undefined);
119
+ }
120
+ setLegendType(value);
121
+ };
122
+ const disabled = useMemo(() => exclude?.filter((indicator) => indicator !== dataItemId) ?? [], [dataItemId, exclude]);
123
+ return (
124
+ <FormProvider {...form}>
125
+ <div className="column gap-16">
126
+ <TypeField />
127
+ <Controller
128
+ rules={{
129
+ validate: {
130
+ required: (value: { id: string; name: string }) => {
131
+ return Boolean(value?.id) || i18n.t("A data item is required");
132
+ },
133
+ },
134
+ }}
135
+ render={({ field, fieldState }) => (
136
+ <>
137
+ <div style={{ alignItems: "flex-end" }} className="row w-100 gap-16 align-end">
138
+ <div onClick={() => setDataSelectorOpen(true)} style={{ flex: 1 }}>
139
+ <InputField
140
+ required
141
+ error={Boolean(fieldState.error)}
142
+ validationText={fieldState.error?.message}
143
+ disabled
144
+ inputWidth="100%"
145
+ label={i18n.t("Data Item")}
146
+ value={field.value?.displayName}
147
+ />
148
+ </div>
149
+ <Button onClick={() => setDataSelectorOpen(true)}>{field.value?.id ? i18n.t("Change") : i18n.t("Select")}</Button>
150
+ </div>
151
+ {dataSelectorOpen && (
152
+ <IndicatorSelectorModal
153
+ disabled={disabled}
154
+ onUpdate={(values: any[]) => {
155
+ const [indicator] = values ?? [];
156
+ field.onChange({
157
+ id: indicator.id,
158
+ displayName: indicator.displayName,
159
+ type: "indicator",
160
+ });
161
+ }}
162
+ onClose={() => setDataSelectorOpen(false)}
163
+ hide={!dataSelectorOpen}
164
+ selected={compact([
165
+ {
166
+ id: field.value?.id,
167
+ displayName: field.value?.displayName,
168
+ },
169
+ ])}
170
+ />
171
+ )}
172
+ </>
173
+ )}
174
+ name={"dataItem"}
175
+ />
176
+ <div>
177
+ <Field label={i18n.t("Legend")}>
178
+ <div className="column gap-8">
179
+ <div className="row gap-16">
180
+ <Radio
181
+ checked={legendType === "legendSet"}
182
+ label={i18n.t("Legend set")}
183
+ name="legendSet"
184
+ value="legendSet"
185
+ onChange={onLegendTypeChange("legendSet")}
186
+ />
187
+ <Radio checked={legendType === "custom"} label={i18n.t("Custom legend")} name="custom" value="custom" onChange={onLegendTypeChange("custom")} />
188
+ </div>
189
+ <div>
190
+ {legendType === "legendSet" && (
191
+ <Controller
192
+ rules={{
193
+ required: i18n.t("Legend set is required"),
194
+ }}
195
+ name="dataItem.legendSet"
196
+ render={({ field, fieldState }) => <LegendSetSelector required selected={field.value} {...field} {...fieldState} />}
197
+ />
198
+ )}
199
+ {legendType === "custom" && <CustomLegend />}
200
+ </div>
201
+ </div>
202
+ </Field>
203
+ {type === "bubble" && (
204
+ <Field label={i18n.t("Radius")}>
205
+ <RadiusField />
206
+ </Field>
207
+ )}
208
+ </div>
209
+ </div>
210
+ </FormProvider>
211
+ );
212
+ }
213
+
214
+ export interface ThematicLayerConfigModalProps {
215
+ open: boolean;
216
+ config?: ThematicLayerConfig;
217
+ exclude?: string[];
218
+ onClose: () => void;
219
+ onChange: (config: ThematicLayerConfig) => void;
220
+ }
221
+
222
+ export function ThematicLayerConfigModal({ open, exclude, config, onClose, onChange, ...props }: ThematicLayerConfigModalProps) {
223
+ const form = useForm<ThematicLayerConfig>({
224
+ defaultValues: config,
225
+ });
226
+
227
+ const onSubmitClick = (values: ThematicLayerConfig) => {
228
+ onChange(values);
229
+ onClose();
230
+ };
231
+
232
+ return (
233
+ <Modal {...props} open={open} onClose={onClose}>
234
+ <ModalTitle>{i18n.t("Configure Thematic Layer")}</ModalTitle>
235
+ <ModalContent>
236
+ <ThematicLayerConfiguration form={form} exclude={exclude} />
237
+ </ModalContent>
238
+ <ModalActions>
239
+ <ButtonStrip>
240
+ <Button onClick={onClose}>{i18n.t("Cancel")}</Button>
241
+ <Button primary onClick={form.handleSubmit(onSubmitClick)}>
242
+ {i18n.t("Save")}
243
+ </Button>
244
+ </ButtonStrip>
245
+ </ModalActions>
246
+ </Modal>
247
+ );
248
+ }