bento-charts 2.3.0 → 2.4.0

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 (62) hide show
  1. package/README.md +94 -23
  2. package/dist/ChartConfigProvider.d.ts +2 -2
  3. package/dist/ChartConfigProvider.js.map +1 -1
  4. package/dist/Components/Charts/BentoBarChart.js.map +1 -1
  5. package/dist/Components/Charts/BentoPie.js +33 -31
  6. package/dist/Components/Charts/BentoPie.js.map +1 -1
  7. package/dist/Components/Maps/BentoChoroplethMap.d.ts +3 -0
  8. package/dist/Components/Maps/BentoChoroplethMap.js +90 -0
  9. package/dist/Components/Maps/BentoChoroplethMap.js.map +1 -0
  10. package/dist/Components/Maps/BentoMapContainer.d.ts +7 -0
  11. package/dist/Components/Maps/BentoMapContainer.js +33 -0
  12. package/dist/Components/Maps/BentoMapContainer.js.map +1 -0
  13. package/dist/Components/Maps/BentoOSMTileLayer.d.ts +2 -0
  14. package/dist/Components/Maps/BentoOSMTileLayer.js +6 -0
  15. package/dist/Components/Maps/BentoOSMTileLayer.js.map +1 -0
  16. package/dist/Components/Maps/BentoPointMap.d.ts +3 -0
  17. package/dist/Components/Maps/BentoPointMap.js +25 -0
  18. package/dist/Components/Maps/BentoPointMap.js.map +1 -0
  19. package/dist/Components/Maps/controls/MapLegendContinuous.d.ts +10 -0
  20. package/dist/Components/Maps/controls/MapLegendContinuous.js +19 -0
  21. package/dist/Components/Maps/controls/MapLegendContinuous.js.map +1 -0
  22. package/dist/Components/Maps/controls/MapLegendDiscrete.d.ts +8 -0
  23. package/dist/Components/Maps/controls/MapLegendDiscrete.js +22 -0
  24. package/dist/Components/Maps/controls/MapLegendDiscrete.js.map +1 -0
  25. package/dist/Components/Maps/controls/utils.d.ts +4 -0
  26. package/dist/Components/Maps/controls/utils.js +7 -0
  27. package/dist/Components/Maps/controls/utils.js.map +1 -0
  28. package/dist/constants/mapConstants.d.ts +2 -0
  29. package/dist/constants/mapConstants.js +3 -0
  30. package/dist/constants/mapConstants.js.map +1 -0
  31. package/dist/index.js +5 -0
  32. package/dist/index.js.map +1 -1
  33. package/dist/maps.d.ts +3 -0
  34. package/dist/maps.js +5 -0
  35. package/dist/maps.js.map +1 -0
  36. package/dist/types/chartTypes.d.ts +11 -9
  37. package/dist/types/geoJSONTypes.d.ts +18 -0
  38. package/dist/types/geoJSONTypes.js +2 -0
  39. package/dist/types/geoJSONTypes.js.map +1 -0
  40. package/dist/types/mapTypes.d.ts +43 -0
  41. package/dist/types/mapTypes.js +2 -0
  42. package/dist/types/mapTypes.js.map +1 -0
  43. package/package.json +30 -4
  44. package/src/ChartConfigProvider.tsx +9 -2
  45. package/src/Components/Charts/BentoBarChart.tsx +2 -2
  46. package/src/Components/Charts/BentoPie.tsx +55 -53
  47. package/src/Components/Maps/BentoChoroplethMap.tsx +137 -0
  48. package/src/Components/Maps/BentoMapContainer.tsx +35 -0
  49. package/src/Components/Maps/BentoOSMTileLayer.tsx +7 -0
  50. package/src/Components/Maps/BentoPointMap.tsx +30 -0
  51. package/src/Components/Maps/controls/MapLegendContinuous.tsx +32 -0
  52. package/src/Components/Maps/controls/MapLegendDiscrete.tsx +31 -0
  53. package/src/Components/Maps/controls/utils.ts +8 -0
  54. package/src/constants/mapConstants.ts +4 -0
  55. package/src/index.ts +7 -0
  56. package/src/maps.ts +4 -0
  57. package/src/react-app-env.d.ts +1 -0
  58. package/src/styles.css +48 -0
  59. package/src/types/chartTypes.ts +12 -9
  60. package/src/types/geoJSONTypes.ts +21 -0
  61. package/src/types/mapTypes.ts +52 -0
  62. package/webpack.config.js +60 -0
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import { Marker, Popup } from 'react-leaflet';
3
+ import BentoMapContainer from './BentoMapContainer';
4
+ import type { PointMapProps } from '../../types/mapTypes';
5
+
6
+ const BentoPointMap = ({ height, center, zoom, tileLayer, data, onClick, renderPopupBody }: PointMapProps) => {
7
+ return (
8
+ <BentoMapContainer height={height} center={center} zoom={zoom} tileLayer={tileLayer}>
9
+ {data.map((point, i) => {
10
+ const { coordinates, title } = point;
11
+
12
+ // We expect points in [long, lat] order (consistent with GeoJSON), but Leaflet wants them in [lat, long].
13
+ const coordinatesLatLongOrder: [number, number] = [coordinates[1], coordinates[0]];
14
+
15
+ return (
16
+ <Marker key={i} position={coordinatesLatLongOrder}>
17
+ <Popup>
18
+ <h4 style={{ marginBottom: renderPopupBody ? 6 : 0 }}>
19
+ {onClick ? <a onClick={() => onClick(point)}>{title}</a> : <>{title}</>}
20
+ </h4>
21
+ {renderPopupBody ? renderPopupBody(point) : null}
22
+ </Popup>
23
+ </Marker>
24
+ );
25
+ })}
26
+ </BentoMapContainer>
27
+ );
28
+ };
29
+
30
+ export default BentoPointMap;
@@ -0,0 +1,32 @@
1
+ import React from 'react';
2
+ import type { ControlPosition } from 'leaflet';
3
+ import { controlPositionClasses } from './utils';
4
+
5
+ export interface MapLegendDiscreteProps {
6
+ position: ControlPosition;
7
+ minValue: number;
8
+ minColor: string;
9
+ maxValue: number;
10
+ maxColor: string;
11
+ }
12
+
13
+ const MapLegendContinuous = ({ position, minValue, minColor, maxValue, maxColor }: MapLegendDiscreteProps) => {
14
+ return (
15
+ <div className={controlPositionClasses[position]}>
16
+ <div className="leaflet-control bento-charts--map--legend">
17
+ <div className="bento-charts--map--legend--scale">
18
+ <div
19
+ className="bento-charts--continuous-scale"
20
+ style={{ background: `linear-gradient(0deg, ${minColor} 0%, ${maxColor} 100%)` }}
21
+ />
22
+ <div className="bento-charts--map--legend--values">
23
+ <span>{maxValue}</span>
24
+ <span>{minValue}</span>
25
+ </div>
26
+ </div>
27
+ </div>
28
+ </div>
29
+ );
30
+ };
31
+
32
+ export default MapLegendContinuous;
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ import type { ControlPosition } from 'leaflet';
3
+ import type { MapDiscreteLegendItem } from '../../../types/mapTypes';
4
+ import { controlPositionClasses } from './utils';
5
+
6
+ export interface MapLegendDiscreteProps {
7
+ position: ControlPosition;
8
+ legendItems: MapDiscreteLegendItem[];
9
+ }
10
+
11
+ const MapLegendDiscrete = ({ position, legendItems }: MapLegendDiscreteProps) => {
12
+ return (
13
+ <div className={controlPositionClasses[position]}>
14
+ <div className="leaflet-control bento-charts--map--legend">
15
+ <ul>
16
+ {legendItems.map(({ label, color }, i) => (
17
+ <li key={i}>
18
+ <span
19
+ className="bento-charts--map--legend--patch"
20
+ style={{ backgroundColor: color ?? `rgba(255, 255, 255, 0)` }}
21
+ />
22
+ {label}
23
+ </li>
24
+ ))}
25
+ </ul>
26
+ </div>
27
+ </div>
28
+ );
29
+ };
30
+
31
+ export default MapLegendDiscrete;
@@ -0,0 +1,8 @@
1
+ import type { ControlPosition } from 'leaflet';
2
+
3
+ export const controlPositionClasses: { [x in ControlPosition]: string } = {
4
+ bottomleft: 'leaflet-bottom leaflet-left',
5
+ bottomright: 'leaflet-bottom leaflet-right',
6
+ topleft: 'leaflet-top leaflet-left',
7
+ topright: 'leaflet-bottom leaflet-right',
8
+ };
@@ -0,0 +1,4 @@
1
+ export const OSM_TILE_LAYER_ATTRIBUTION = `
2
+ &copy; <a href='https://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors
3
+ `;
4
+ export const OSM_TILE_LAYER_TEMPLATE = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
package/src/index.ts CHANGED
@@ -1,5 +1,12 @@
1
+ // Disable unused item linting in WebStorm:
2
+ // noinspection JSUnusedGlobalSymbols
3
+
4
+ // Categorical charts
1
5
  export { default as BarChart } from './Components/Charts/BentoBarChart';
2
6
  export { default as PieChart } from './Components/Charts/BentoPie';
3
7
 
8
+ // Maps are not included in index.ts - instead, they need to be included from `bento-charts/maps`.
9
+ // This way, we can have optional peer dependencies.
10
+
4
11
  export { default as ChartConfigProvider } from './ChartConfigProvider';
5
12
  export * from './types/chartTypes';
package/src/maps.ts ADDED
@@ -0,0 +1,4 @@
1
+ // Maps
2
+ export { default as PointMap } from './Components/Maps/BentoPointMap';
3
+ export { default as ChoroplethMap } from './Components/Maps/BentoChoroplethMap';
4
+ export * from './types/mapTypes';
@@ -0,0 +1 @@
1
+ declare module '*.png';
package/src/styles.css ADDED
@@ -0,0 +1,48 @@
1
+ .bento-charts--map--legend {
2
+ background-color: white;
3
+ padding: 12px;
4
+ border-radius: 12px;
5
+ border: 1px solid rgba(0, 0, 0, 0.2);
6
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.07);
7
+ }
8
+ .leaflet-bottom .bento-charts--map--legend {
9
+ margin-bottom: 28px;
10
+ }
11
+
12
+ .bento-charts--map--legend ul {
13
+ list-style: none;
14
+ margin: 0;
15
+ padding: 0;
16
+ display: flex;
17
+ flex-direction: column;
18
+ gap: 6px;
19
+ }
20
+
21
+ .bento-charts--map--legend--patch {
22
+ display: inline-block;
23
+ width: 1rem;
24
+ height: 1rem;
25
+ border: 1px solid white;
26
+ vertical-align: top;
27
+ margin-right: 12px;
28
+ border-radius: 3px;
29
+ }
30
+
31
+ .bento-charts--map--legend--scale {
32
+ height: 100px;
33
+ display: flex;
34
+ gap: 12px;
35
+ }
36
+
37
+ .bento-charts--continuous-scale {
38
+ width: 1rem;
39
+ height: 100%;
40
+ }
41
+
42
+ .bento-charts--map--legend--values {
43
+ display: flex;
44
+ flex-direction: column;
45
+ }
46
+ .bento-charts--map--legend--values > span:first-of-type {
47
+ flex: 1;
48
+ }
@@ -1,8 +1,8 @@
1
- import { PieProps, BarProps } from 'recharts';
1
+ import type { PieProps, BarProps } from 'recharts';
2
2
 
3
- export type ChartDataType = ChartDataItem[];
3
+ export type CategoricalChartDataType = CategoricalChartDataItem[];
4
4
 
5
- export interface ChartDataItem {
5
+ export interface CategoricalChartDataItem {
6
6
  x: string;
7
7
  y: number;
8
8
  }
@@ -36,7 +36,7 @@ export type FilterCallback<T> = (value: T, index: number, array: T[]) => boolean
36
36
  export type UnitaryMapCallback<T> = (value: T, index: number, array: T[]) => T;
37
37
  // export type BinaryMapCallback<T, U> = (value: T, index: number, array: T[]) => U;
38
38
 
39
- export type ChartFilterCallback = FilterCallback<ChartDataItem>;
39
+ export type ChartFilterCallback = FilterCallback<CategoricalChartDataItem>;
40
40
 
41
41
  export type SupportedLng = 'en' | 'fr';
42
42
 
@@ -51,16 +51,19 @@ export type TranslationObject = {
51
51
  };
52
52
 
53
53
  // ################### COMPONENT PROPS #####################
54
- interface BaseChartProps {
55
- data: ChartDataType;
54
+ export interface BaseChartComponentProps {
56
55
  height: number;
57
56
  preFilter?: ChartFilterCallback;
58
- dataMap?: UnitaryMapCallback<ChartDataItem>;
57
+ dataMap?: UnitaryMapCallback<CategoricalChartDataItem>;
59
58
  postFilter?: ChartFilterCallback;
59
+ }
60
+
61
+ interface BaseCategoricalChartProps extends BaseChartComponentProps {
62
+ data: CategoricalChartDataType;
60
63
  removeEmpty?: boolean;
61
64
  }
62
65
 
63
- export interface PieChartProps extends BaseChartProps {
66
+ export interface PieChartProps extends BaseCategoricalChartProps {
64
67
  colorTheme?: keyof ChartTheme['pie'];
65
68
  sort?: boolean;
66
69
  onClick?: PieProps['onClick'];
@@ -68,7 +71,7 @@ export interface PieChartProps extends BaseChartProps {
68
71
  maxLabelChars?: number;
69
72
  }
70
73
 
71
- export interface BarChartProps extends BaseChartProps {
74
+ export interface BarChartProps extends BaseCategoricalChartProps {
72
75
  colorTheme?: keyof ChartTheme['bar'];
73
76
  title?: string;
74
77
  units: string;
@@ -0,0 +1,21 @@
1
+ export interface GeoJSONGeomPolygon {
2
+ type: 'Polygon';
3
+ coordinates: number[][][];
4
+ }
5
+
6
+ export interface BentoGeoJSONProperties {
7
+ title: string;
8
+ [x: string]: unknown;
9
+ }
10
+
11
+ export interface GeoJSONPolygonFeature {
12
+ type: 'Feature';
13
+ geometry: GeoJSONGeomPolygon;
14
+ properties: BentoGeoJSONProperties;
15
+ }
16
+
17
+ export interface GeoJSONPolygonOnlyFeatureCollection {
18
+ type: 'FeatureCollection';
19
+ features: GeoJSONPolygonFeature[];
20
+ [x: string]: unknown;
21
+ }
@@ -0,0 +1,52 @@
1
+ import { ReactElement, ReactNode } from 'react';
2
+ import type { Feature as GeoJSONFeatureType } from 'geojson';
3
+
4
+ import { BaseChartComponentProps, CategoricalChartDataType } from './chartTypes';
5
+ import type { GeoJSONPolygonOnlyFeatureCollection } from './geoJSONTypes';
6
+
7
+ export interface GeoPointDataItem {
8
+ coordinates: [number, number];
9
+ title: string;
10
+ }
11
+
12
+ type PointMapOnClick = (point: GeoPointDataItem) => void;
13
+
14
+ type GeoJSONShapeOnClick = (shape: GeoJSONFeatureType) => void;
15
+
16
+ export interface BaseMapProps extends BaseChartComponentProps {
17
+ center: [number, number];
18
+ zoom: number;
19
+ tileLayer?: ReactElement;
20
+ }
21
+
22
+ export interface PointMapProps extends BaseMapProps {
23
+ data: GeoPointDataItem[];
24
+ onClick?: PointMapOnClick;
25
+ renderPopupBody?: (p: GeoPointDataItem) => ReactNode;
26
+ }
27
+
28
+ export interface MapDiscreteLegendItem {
29
+ color: string | undefined;
30
+ label: string;
31
+ }
32
+
33
+ export interface ChoroplethMapColorModeContinuous {
34
+ mode: 'continuous';
35
+ minColor: string;
36
+ maxColor: string;
37
+ }
38
+
39
+ export interface ChoroplethMapColorModeDiscrete {
40
+ mode: 'discrete';
41
+ colorFunction: (x: number | undefined) => string;
42
+ legendItems: MapDiscreteLegendItem[];
43
+ }
44
+
45
+ export interface ChoroplethMapProps extends BaseMapProps {
46
+ data: CategoricalChartDataType; // heatmaps are 'categorical' + geographical
47
+ features: GeoJSONPolygonOnlyFeatureCollection;
48
+ colorMode: ChoroplethMapColorModeContinuous | ChoroplethMapColorModeDiscrete;
49
+ categoryProp: string;
50
+ onClick?: GeoJSONShapeOnClick;
51
+ renderPopupBody?: (f: GeoJSONFeatureType, d: number | undefined) => ReactNode;
52
+ }
@@ -0,0 +1,60 @@
1
+ const path = require('path');
2
+
3
+ const HtmlWebpackPlugin = require('html-webpack-plugin');
4
+
5
+ const config = {
6
+ mode: 'development',
7
+ entry: './test/js/index.tsx',
8
+ output: {
9
+ path: __dirname + '/test/dist',
10
+ publicPath: '',
11
+ filename: 'js/[name][chunkhash].js',
12
+ },
13
+ module: {
14
+ rules: [
15
+ { test: /\.[tj](sx|s)?$/, use: { loader: 'ts-loader' }, exclude: /node_modules/ },
16
+ {
17
+ test: /\.html$/i,
18
+ loader: 'html-loader',
19
+ },
20
+ {
21
+ test: /\.css$/i,
22
+ use: ['style-loader', 'css-loader'],
23
+ },
24
+ {
25
+ test: /\.(png|jpe?g|gif)$/i,
26
+ loader: 'file-loader',
27
+ },
28
+ ],
29
+ },
30
+ watchOptions: {
31
+ poll: 1000,
32
+ },
33
+ plugins: [
34
+ new HtmlWebpackPlugin({
35
+ title: 'Development',
36
+ inject: false,
37
+ template: 'test/index.ejs',
38
+ }),
39
+ ],
40
+ optimization: {
41
+ runtimeChunk: 'single',
42
+ },
43
+ devtool: 'source-map',
44
+ devServer: {
45
+ static: './test/dist',
46
+ },
47
+ resolve: {
48
+ alias: {
49
+ '@': path.resolve(__dirname, 'src'),
50
+ },
51
+ extensions: ['.tsx', '.ts', '.js'],
52
+ },
53
+ };
54
+
55
+ module.exports = (_env, argv) => {
56
+ if (argv.mode === 'development') {
57
+ config.devtool = 'inline-source-map';
58
+ }
59
+ return config;
60
+ };