@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,105 @@
1
+ import i18n from "@dhis2/d2-i18n";
2
+ import {CenteredContent, CircularLoader} from "@dhis2/ui";
3
+ import {LayersControlEvent} from "leaflet";
4
+ import {compact, find, head, set} from "lodash";
5
+ import React, {useCallback, useEffect, useState} from "react";
6
+ import {useMapEvents} from "react-leaflet";
7
+ import {MapLayersContext} from "../../../../state";
8
+ import {MapLayerConfig} from "../../../MapArea/interfaces";
9
+ import {
10
+ CustomBoundaryLayer,
11
+ CustomGoogleEngineLayer,
12
+ CustomMapLayer,
13
+ CustomPointLayer,
14
+ CustomThematicLayer,
15
+ ThematicLayerConfig,
16
+ } from "../../../MapLayer/interfaces";
17
+ import {useMapOrganisationUnit, useMapPeriods} from "../../hooks";
18
+ import {useGoogleEngineLayers, usePointLayer, useThematicLayers} from "./hooks";
19
+
20
+ export function MapLayersProvider({ layers, children }: { layers: MapLayerConfig; children: React.ReactNode }) {
21
+ const period = useMapPeriods();
22
+ const orgUnit = useMapOrganisationUnit();
23
+ const [updatedLayers, setUpdatedLayers] = useState<Array<CustomThematicLayer | CustomBoundaryLayer | CustomPointLayer | CustomGoogleEngineLayer>>([]);
24
+ const { sanitizeLayers: sanitizeThematicLayers, error } = useThematicLayers();
25
+ const { sanitizeLayer: sanitizePointLayer } = usePointLayer();
26
+ const { sanitizeLayers: sanitizeEarthEngineLayers } = useGoogleEngineLayers();
27
+ const [loading, setLoading] = useState(false);
28
+
29
+ useMapEvents({
30
+ overlayremove: (event) => {
31
+ setupLayerListeners("remove", event);
32
+ },
33
+ overlayadd: (event) => {
34
+ setupLayerListeners("add", event);
35
+ },
36
+ });
37
+
38
+ const sanitizeLayers = async () => {
39
+ setLoading(true);
40
+ try {
41
+ const { boundaryLayers, thematicLayers, pointLayers, earthEngineLayers } = layers;
42
+ const sanitizedThematicLayers = await sanitizeThematicLayers([...(thematicLayers ?? [])] as ThematicLayerConfig[]);
43
+ const sanitizedBoundaryLayers = (boundaryLayers ?? []) as CustomBoundaryLayer[];
44
+ const sanitizedPointLayer = head(pointLayers ?? []) ? await sanitizePointLayer(head(pointLayers) as CustomPointLayer) : undefined;
45
+ const sanitizedEarthEngineLayers = await sanitizeEarthEngineLayers([...(earthEngineLayers ?? [])] as unknown as CustomGoogleEngineLayer[]);
46
+ setUpdatedLayers(
47
+ compact([...(sanitizedBoundaryLayers ?? []), ...(sanitizedThematicLayers ?? []), sanitizedPointLayer, ...(sanitizedEarthEngineLayers ?? [])])
48
+ );
49
+ } catch (e: any) {
50
+ console.error(`Error sanitizing layers`, e.toString());
51
+ }
52
+ setLoading(false);
53
+ };
54
+
55
+ const updateLayer = useCallback((id: string, updatedLayer: CustomMapLayer) => {
56
+ setUpdatedLayers((prevLayers) => {
57
+ const updatedLayers = [...prevLayers];
58
+ const layerIndex = updatedLayers.findIndex((layer) => layer.id === updatedLayer.id);
59
+ if (layerIndex < 0) {
60
+ return prevLayers;
61
+ }
62
+ set(updatedLayers, layerIndex, updatedLayer);
63
+ return updatedLayers;
64
+ });
65
+ }, []);
66
+
67
+ useEffect(() => {
68
+ sanitizeLayers().catch(console.error);
69
+ }, [period, orgUnit]);
70
+
71
+ const setupLayerListeners = (type: "add" | "remove", event: LayersControlEvent) => {
72
+ const name = event.name;
73
+
74
+ const layerConfig = find(updatedLayers, (layer: any) => {
75
+ const nameFromConfig = layer?.name ?? layer?.dataItem?.displayname ?? layer?.label;
76
+ return nameFromConfig === name;
77
+ });
78
+
79
+ if (layerConfig) {
80
+ updateLayer(layerConfig.id, { ...layerConfig, enabled: type === "add" });
81
+ }
82
+ };
83
+
84
+ if (loading) {
85
+ return (
86
+ <div style={{ height: "100%", width: "100%" }}>
87
+ <CenteredContent>
88
+ <CircularLoader small />
89
+ </CenteredContent>
90
+ </div>
91
+ );
92
+ }
93
+ if (error) {
94
+ return (
95
+ <div style={{ height: "100%", width: "100%" }}>
96
+ <CenteredContent>
97
+ <h4>
98
+ {i18n.t("Error")}: {error.message}
99
+ </h4>
100
+ </CenteredContent>
101
+ </div>
102
+ );
103
+ }
104
+ return <MapLayersContext.Provider value={{ layers: updatedLayers, updateLayer }}>{children}</MapLayersContext.Provider>;
105
+ }
@@ -0,0 +1,14 @@
1
+ import {useContext} from "react";
2
+ import {MapLayersContext, MapOrgUnitContext, MapPeriodContext} from "../../../state";
3
+
4
+ export function useMapOrganisationUnit() {
5
+ return useContext(MapOrgUnitContext);
6
+ }
7
+
8
+ export function useMapPeriods() {
9
+ return useContext(MapPeriodContext);
10
+ }
11
+
12
+ export function useMapLayers() {
13
+ return useContext(MapLayersContext);
14
+ }
@@ -0,0 +1,93 @@
1
+ import {useDataQuery} from "@dhis2/app-runtime";
2
+ import i18n from "@dhis2/d2-i18n";
3
+ import {CenteredContent, CircularLoader} from "@dhis2/ui";
4
+ import {compact, isEmpty} from "lodash";
5
+ import React, {useEffect, useState} from "react";
6
+ import {MapOrgUnit, MapProviderProps} from "../../interfaces";
7
+ import {MapOrgUnitContext, MapPeriodContext} from "../../state";
8
+ import {getOrgUnitsSelection, sanitizeOrgUnits, toGeoJson} from "../../utils/map";
9
+ import {BasePeriod, PeriodUtility} from "@hisptz/dhis2-utils";
10
+
11
+ const boundaryQuery = {
12
+ boundaries: {
13
+ resource: "geoFeatures",
14
+ params: ({ orgUnitIds }: any) => ({
15
+ ou: `ou:${orgUnitIds?.join(";")}`,
16
+ }),
17
+ },
18
+ analytics: {
19
+ resource: "analytics",
20
+ params: ({ orgUnitIds }: any) => ({
21
+ dimension: [`ou:${orgUnitIds.join(";")}`, `pe:${new Date().getFullYear()}`],
22
+ skipData: true,
23
+ hierarchyMeta: true,
24
+ }),
25
+ },
26
+ };
27
+
28
+ export function MapProvider({ children, orgUnitSelection, periodSelection }: MapProviderProps) {
29
+ const [orgUnits, setOrgUnits] = useState<MapOrgUnit[]>([]);
30
+ const { refetch, loading, error } = useDataQuery(boundaryQuery, { lazy: true });
31
+
32
+ useEffect(() => {
33
+ async function getOrgUnits() {
34
+ const rawOrgUnitIds = getOrgUnitsSelection(orgUnitSelection);
35
+ const data = await refetch({ orgUnitIds: rawOrgUnitIds });
36
+ const { analytics, boundaries } = (data as any) ?? {};
37
+ const rawOrgUnits = sanitizeOrgUnits(analytics?.metaData);
38
+ const geoJSONObjects = toGeoJson(boundaries.filter((bound: any) => bound.co));
39
+ const orgUnits: MapOrgUnit[] = compact(
40
+ rawOrgUnits.map((orgUnit: any) => {
41
+ const geoJSONObject: any = geoJSONObjects?.find((geoJSON: any) => geoJSON.properties.id === orgUnit.id);
42
+
43
+ if (!geoJSONObject) {
44
+ return;
45
+ }
46
+ return {
47
+ ...orgUnit,
48
+ geoJSON: geoJSONObject,
49
+ bounds: [],
50
+ level: geoJSONObject.properties.level,
51
+ };
52
+ })
53
+ );
54
+ setOrgUnits(orgUnits);
55
+ }
56
+
57
+ getOrgUnits().catch((error) => console.log(error));
58
+ }, [orgUnitSelection, refetch]);
59
+
60
+ if (loading) {
61
+ return (
62
+ <div style={{ height: "100%", width: "100%" }}>
63
+ <CenteredContent>
64
+ <CircularLoader small />
65
+ </CenteredContent>
66
+ </div>
67
+ );
68
+ }
69
+
70
+ if (error) {
71
+ return (
72
+ <div style={{ height: "100%", width: "100%" }}>
73
+ <CenteredContent>
74
+ <h4>
75
+ {i18n.t("Error")}: {error.message}
76
+ </h4>
77
+ </CenteredContent>
78
+ </div>
79
+ );
80
+ }
81
+
82
+ const periods: BasePeriod[] = compact(periodSelection?.periods?.map((pe) => PeriodUtility.getPeriodById(pe)));
83
+
84
+ if (!isEmpty(orgUnits)) {
85
+ return (
86
+ <MapOrgUnitContext.Provider value={{ orgUnitSelection, orgUnits }}>
87
+ <MapPeriodContext.Provider value={{ ...periodSelection, periods }}>{children}</MapPeriodContext.Provider>
88
+ </MapOrgUnitContext.Provider>
89
+ );
90
+ }
91
+
92
+ return null;
93
+ }
@@ -0,0 +1,8 @@
1
+ import {useCenterMap} from "../../hooks/map";
2
+ import React from "react";
3
+ import {LatLngTuple} from "leaflet";
4
+
5
+ export default function MapUpdater({ bounds }: { bounds: LatLngTuple[] }) {
6
+ const ref = useCenterMap({ bounds });
7
+ return <div style={{ width: "100%", height: "100%" }} ref={ref}></div>;
8
+ }
@@ -0,0 +1,28 @@
1
+ import {Story} from "@storybook/react";
2
+ import {ThematicLayerConfigModal, ThematicLayerConfigModalProps} from "./index";
3
+ import React from "react";
4
+
5
+ const Template: Story<ThematicLayerConfigModalProps> = (args) => {
6
+ return <ThematicLayerConfigModal {...args} />;
7
+ };
8
+
9
+ export const Basic = Template.bind({});
10
+ Basic.args = {
11
+ onChange: console.info,
12
+ onClose: () => {},
13
+ open: true,
14
+ };
15
+
16
+ export default {
17
+ title: "Analytics/Map/Thematic Layer Config Modal",
18
+ component: ThematicLayerConfigModal,
19
+ decorators: [
20
+ (MapStory: any) => {
21
+ return (
22
+ <div style={{ width: "50%", height: "50%" }}>
23
+ <MapStory />
24
+ </div>
25
+ );
26
+ },
27
+ ],
28
+ };
@@ -0,0 +1,34 @@
1
+ import {Story} from "@storybook/react";
2
+ import {ThematicLayerConfiguration, ThematicLayerConfigurationProps} from "./index";
3
+ import React from "react";
4
+ import {useForm} from "react-hook-form";
5
+ import {ThematicLayerConfig} from "../MapLayer/interfaces";
6
+ import i18n from "@dhis2/d2-i18n";
7
+ import {Button} from "@dhis2/ui";
8
+
9
+ const Template: Story<ThematicLayerConfigurationProps> = (args) => {
10
+ const form = useForm<ThematicLayerConfig>();
11
+ return (
12
+ <form className="column gap-16" onSubmit={form.handleSubmit(console.log)}>
13
+ <ThematicLayerConfiguration {...args} form={form} />
14
+ <Button type="submit">{i18n.t("Submit")}</Button>
15
+ </form>
16
+ );
17
+ };
18
+
19
+ export const Basic = Template.bind({});
20
+ Basic.args = {};
21
+
22
+ export default {
23
+ title: "Analytics/Map/Thematic Layer Configuration",
24
+ component: ThematicLayerConfiguration,
25
+ decorators: [
26
+ (MapStory: any) => {
27
+ return (
28
+ <div style={{ width: "50%", height: "50%" }}>
29
+ <MapStory />
30
+ </div>
31
+ );
32
+ },
33
+ ],
34
+ };
@@ -0,0 +1,24 @@
1
+ import React from "react";
2
+ import styles from "../../styles/ColorScale.module.css";
3
+ import {COLOR_PALETTES} from "../../constants/colors";
4
+
5
+ // Returns one color scale based on a code and number of classes
6
+ const ColorScale = ({ scale, bins, width, onClick }: { scale: string; bins: number; width: number; onClick: (scale: string) => void }) => {
7
+ const colors = (COLOR_PALETTES as any)?.[scale]?.[bins];
8
+ const itemWidth = width ? width / bins : 36;
9
+
10
+ return (
11
+ <ul
12
+ onClick={() => onClick(scale)}
13
+ className={styles.colorScale}
14
+ style={{
15
+ ...(width && { width }),
16
+ }}>
17
+ {colors.map((color: string, index: number) => (
18
+ <li key={index} className={styles.item} style={{ backgroundColor: color, width: itemWidth }} />
19
+ ))}
20
+ </ul>
21
+ );
22
+ };
23
+
24
+ export default ColorScale;