@geo2france/api-dashboard 1.8.0 → 1.10.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.
- package/dist/components/Attributions/Attributions.js +7 -7
- package/dist/components/Charts/Pie.js +3 -6
- package/dist/components/Charts/Statistics.d.ts +64 -0
- package/dist/components/Charts/Statistics.js +61 -0
- package/dist/components/Charts/YearSerie.js +3 -6
- package/dist/components/Control/Select.js +4 -3
- package/dist/components/DashboardElement/DashboardElement.js +2 -3
- package/dist/components/DashboardPage/Block.d.ts +1 -0
- package/dist/components/DashboardPage/Block.js +12 -5
- package/dist/components/DashboardPage/Page.d.ts +1 -0
- package/dist/components/Dataset/Dataset.js +1 -1
- package/dist/components/Dataset/hooks.d.ts +3 -0
- package/dist/components/Debug/Debug.js +2 -2
- package/dist/components/FlipCard/FlipCard.js +2 -2
- package/dist/components/Layout/Sider.js +2 -2
- package/dist/components/LoadingContainer/LoadingContainer.js +2 -2
- package/dist/components/Map/Map.d.ts +59 -0
- package/dist/components/Map/Map.js +147 -0
- package/dist/components/MapLegend/MapLegend.d.ts +6 -0
- package/dist/components/MapLegend/MapLegend.js +29 -0
- package/dist/components/NextPrevSelect/NextPrevSelect.js +1 -1
- package/dist/dsl/index.d.ts +4 -1
- package/dist/dsl/index.js +4 -1
- package/dist/utils/parsers.d.ts +2 -0
- package/dist/utils/parsers.js +14 -0
- package/dist/utils/useMapControl.d.ts +3 -0
- package/dist/utils/useMapControl.js +3 -0
- package/package.json +15 -6
- package/dist/data_providers/wfs/utils/mapOperator.d.ts +0 -1
- package/dist/data_providers/wfs/utils/mapOperator.js +0 -36
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Tooltip, Typography } from 'antd';
|
|
3
|
-
import {
|
|
3
|
+
import { Icon } from '@iconify/react';
|
|
4
4
|
const { Text, Link } = Typography;
|
|
5
5
|
const LogoLicence = ({ license, style }) => {
|
|
6
6
|
switch (license) {
|
|
7
7
|
case "CC":
|
|
8
|
-
return _jsx(
|
|
8
|
+
return _jsx(Icon, { icon: "cib:creative-commons", style: style });
|
|
9
9
|
case "BY":
|
|
10
|
-
return _jsx(
|
|
10
|
+
return _jsx(Icon, { icon: "cib:creative-commons-by", style: style });
|
|
11
11
|
case "NC":
|
|
12
|
-
return _jsx(
|
|
12
|
+
return _jsx(Icon, { icon: "cib:creative-commons-nc", style: style });
|
|
13
13
|
case "PD":
|
|
14
|
-
return _jsx(
|
|
14
|
+
return _jsx(Icon, { icon: "cib:creative-commons-pd", style: style });
|
|
15
15
|
case "SA":
|
|
16
|
-
return _jsx(
|
|
16
|
+
return _jsx(Icon, { icon: "cib:creative-commons-sa", style: style });
|
|
17
17
|
case "ZERO":
|
|
18
|
-
return _jsx(
|
|
18
|
+
return _jsx(Icon, { icon: "cib:creative-commons-zero", style: style });
|
|
19
19
|
}
|
|
20
20
|
};
|
|
21
21
|
const Attribution = ({ data, style, licenses }) => {
|
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useContext, useEffect } from "react";
|
|
3
2
|
import { useDataset } from "../Dataset/hooks";
|
|
4
|
-
import { ChartBlockContext } from "../DashboardPage/Block";
|
|
5
3
|
import { usePalette } from "../Palette/Palette";
|
|
6
4
|
import { from, op } from "arquero";
|
|
7
5
|
import { ChartEcharts } from "./ChartEcharts";
|
|
8
6
|
import { merge_others } from "../..";
|
|
7
|
+
import { useBlockConfig } from "../DashboardPage/Block";
|
|
9
8
|
export const ChartPie = ({ dataset: dataset_id, nameKey, dataKey, unit, title, donut = false, other = 5 }) => {
|
|
10
9
|
const dataset = useDataset(dataset_id);
|
|
11
|
-
const blockConfig = useContext(ChartBlockContext);
|
|
12
10
|
const data = dataset?.data;
|
|
13
|
-
|
|
11
|
+
useBlockConfig({
|
|
14
12
|
title: title,
|
|
15
13
|
dataExport: data
|
|
16
|
-
};
|
|
17
|
-
useEffect(() => blockConfig?.setConfig(block_config), [data]);
|
|
14
|
+
});
|
|
18
15
|
const chart_data1 = data && data.length > 0
|
|
19
16
|
? from(data)
|
|
20
17
|
.groupby(nameKey)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { ReactElement } from "react";
|
|
2
|
+
type comparwithType = "first" | "previous";
|
|
3
|
+
interface StatisticsProps {
|
|
4
|
+
/** Identifiant du jeu de données */
|
|
5
|
+
dataset: string;
|
|
6
|
+
/** Nom de la colonne qui contient les valeurs */
|
|
7
|
+
dataKey: string;
|
|
8
|
+
/** Texte à afficher après l'évolution */
|
|
9
|
+
evolutionSuffix?: string;
|
|
10
|
+
/** Afficher l'évolution en % (sinon dans la même unité que la valeur) */
|
|
11
|
+
relativeEvolution?: boolean;
|
|
12
|
+
/** Titre */
|
|
13
|
+
title?: string;
|
|
14
|
+
/** Couleur */
|
|
15
|
+
color?: string;
|
|
16
|
+
/** Unité de la valeur */
|
|
17
|
+
unit?: string;
|
|
18
|
+
/** Inverser les couleurs (rouge/vert) de l'évolution */
|
|
19
|
+
invertColor?: boolean;
|
|
20
|
+
/** Icône (composant ou nom de l'icône sur Iconify.js ) */
|
|
21
|
+
icon?: ReactElement | string;
|
|
22
|
+
/** Texte à afficher dans le tooltip d'aide */
|
|
23
|
+
help?: string;
|
|
24
|
+
/** Comparer la valeur avec la précédente ou la première du jeu de données */
|
|
25
|
+
compareWith?: comparwithType;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Composant `Statistics` affichant une valeur d'un dataset avec son évolution.
|
|
29
|
+
*
|
|
30
|
+
* Affiche :
|
|
31
|
+
* - La dernière valeur du dataset
|
|
32
|
+
* - Unité et picto
|
|
33
|
+
* - Évolution par rapport à la première ou l'avant-dernière valeur
|
|
34
|
+
* - Couleur selon évolution positive/négative
|
|
35
|
+
* - Tooltip d'aide si fourni
|
|
36
|
+
*
|
|
37
|
+
* @param {StatisticsProps} props - Propriétés du composant
|
|
38
|
+
* @returns {ReactElement} Carte statistique
|
|
39
|
+
*/
|
|
40
|
+
export declare const Statistics: React.FC<StatisticsProps>;
|
|
41
|
+
type StatisticsCollectionProps = {
|
|
42
|
+
/**
|
|
43
|
+
* Un ou plusieurs composants `<Statistics>`.
|
|
44
|
+
*/
|
|
45
|
+
children: ReactElement<typeof Statistics> | ReactElement<typeof Statistics>[];
|
|
46
|
+
/**
|
|
47
|
+
* Nombre de colonnes (défaut : 3)
|
|
48
|
+
*/
|
|
49
|
+
columns?: number;
|
|
50
|
+
/**
|
|
51
|
+
* Titre du bloc.
|
|
52
|
+
*/
|
|
53
|
+
title?: string;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* `StatisticsCollection` permet de regrouper plusieurs cartes statistiques
|
|
57
|
+
* dans un bloc
|
|
58
|
+
*
|
|
59
|
+
* @param {StatisticsProps} props - Propriétés du composant
|
|
60
|
+
* @returns {ReactElement} Collection de cartes statistiques
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export declare const StatisticsCollection: React.FC<StatisticsCollectionProps>;
|
|
64
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { QuestionCircleOutlined } from "@ant-design/icons";
|
|
3
|
+
import { Avatar, Card, Col, Flex, Row, Tooltip, Typography } from "antd";
|
|
4
|
+
import { Children } from "react";
|
|
5
|
+
import { useDataset } from "../Dataset/hooks";
|
|
6
|
+
import { Icon } from "@iconify/react";
|
|
7
|
+
import { useBlockConfig } from "../DashboardPage/Block";
|
|
8
|
+
const { Text, Paragraph } = Typography;
|
|
9
|
+
// DEV : modele cf https://bootstrapbrain.com/component/bootstrap-statistics-card-example/
|
|
10
|
+
/**
|
|
11
|
+
* Composant `Statistics` affichant une valeur d'un dataset avec son évolution.
|
|
12
|
+
*
|
|
13
|
+
* Affiche :
|
|
14
|
+
* - La dernière valeur du dataset
|
|
15
|
+
* - Unité et picto
|
|
16
|
+
* - Évolution par rapport à la première ou l'avant-dernière valeur
|
|
17
|
+
* - Couleur selon évolution positive/négative
|
|
18
|
+
* - Tooltip d'aide si fourni
|
|
19
|
+
*
|
|
20
|
+
* @param {StatisticsProps} props - Propriétés du composant
|
|
21
|
+
* @returns {ReactElement} Carte statistique
|
|
22
|
+
*/
|
|
23
|
+
export const Statistics = ({ dataset: dataset_id, dataKey, unit, evolutionSuffix, title, icon: icon_input, color, invertColor = false, help, compareWith, relativeEvolution = false }) => {
|
|
24
|
+
const icon = typeof icon_input === "string" ? _jsx(Icon, { icon: icon_input }) : icon_input;
|
|
25
|
+
const dataset = useDataset(dataset_id);
|
|
26
|
+
const value = dataset?.data?.slice(-1)[0][dataKey]; // Dernière valeur du dataset
|
|
27
|
+
const compare_value = compareWith === 'previous' ? dataset?.data?.slice(-2)[0][dataKey] : dataset?.data?.slice(0, 1)[0][dataKey]; //Première ou avant dernière
|
|
28
|
+
const evolution = relativeEvolution ? 100 * ((value - compare_value) / compare_value) : value - compare_value;
|
|
29
|
+
const evolution_unit = relativeEvolution ? '%' : unit;
|
|
30
|
+
const evolution_is_good = invertColor ? evolution < 0 : evolution > 0;
|
|
31
|
+
const tooltip = help && _jsx(Tooltip, { title: help, children: _jsx(QuestionCircleOutlined, {}) });
|
|
32
|
+
return (_jsx(Card, { title: title, style: {
|
|
33
|
+
borderLeft: `4px solid ${color}`,
|
|
34
|
+
height: "100%"
|
|
35
|
+
}, styles: {
|
|
36
|
+
body: {
|
|
37
|
+
padding: 16,
|
|
38
|
+
paddingTop: 8,
|
|
39
|
+
paddingBottom: 8
|
|
40
|
+
},
|
|
41
|
+
header: {
|
|
42
|
+
padding: "5px",
|
|
43
|
+
paddingLeft: "15px",
|
|
44
|
+
fontSize: 14,
|
|
45
|
+
minHeight: 35,
|
|
46
|
+
},
|
|
47
|
+
}, extra: tooltip, children: _jsxs(Flex, { vertical: true, children: [_jsxs(Flex, { justify: "space-between", align: "center", children: [_jsxs(Text, { style: { fontSize: "150%", paddingTop: 8, paddingBottom: 8, paddingLeft: 0 }, children: [value?.toLocaleString(), " ", unit] }), icon && _jsx(Avatar, { size: 32 + 8, icon: icon, style: { backgroundColor: color } })] }), evolution && _jsxs(Paragraph, { style: { marginBottom: "0.5rem" }, children: [_jsxs(Text, { strong: true, type: evolution_is_good ? "success" : "danger", style: { fontSize: "120%" }, children: [evolution < 0.1 ? '' : '+', evolution.toLocaleString(undefined, { maximumFractionDigits: 1 }), "\u00A0", evolution_unit] }), " ", _jsx(Text, { italic: true, type: "secondary", children: evolutionSuffix })] })] }) }));
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* `StatisticsCollection` permet de regrouper plusieurs cartes statistiques
|
|
51
|
+
* dans un bloc
|
|
52
|
+
*
|
|
53
|
+
* @param {StatisticsProps} props - Propriétés du composant
|
|
54
|
+
* @returns {ReactElement} Collection de cartes statistiques
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export const StatisticsCollection = ({ children, columns = 3, title }) => {
|
|
58
|
+
const arrayChildren = Children.toArray(children);
|
|
59
|
+
useBlockConfig({ title: title });
|
|
60
|
+
return (_jsx(Row, { gutter: [8, 8], children: arrayChildren.map((c, index) => (_jsx(Col, { xl: 24 / columns, xs: 24, children: c }, index))) }));
|
|
61
|
+
};
|
|
@@ -6,20 +6,17 @@ import { from, op } from "arquero";
|
|
|
6
6
|
import { useDataset } from "../Dataset/hooks";
|
|
7
7
|
import { usePalette } from "../Palette/Palette";
|
|
8
8
|
import { ChartEcharts } from "./ChartEcharts";
|
|
9
|
-
import {
|
|
10
|
-
import { ChartBlockContext } from "../DashboardPage/Block";
|
|
9
|
+
import { useBlockConfig } from "../DashboardPage/Block";
|
|
11
10
|
export const ChartYearSerie = ({ dataset: dataset_id, categoryKey, valueKey, yearKey, yearMark, stack: stack_input, title, type: chart_type = 'bar' }) => {
|
|
12
11
|
const stack = stack_input || chart_type == 'line' ? false : true; // Pas de stack par défaut pour le type line
|
|
13
12
|
const dataset = useDataset(dataset_id);
|
|
14
13
|
const data = dataset?.data;
|
|
15
14
|
let chart_data = [];
|
|
16
15
|
let distinct_cat = [];
|
|
17
|
-
|
|
18
|
-
const block_config = {
|
|
16
|
+
useBlockConfig({
|
|
19
17
|
title: title,
|
|
20
18
|
dataExport: data
|
|
21
|
-
};
|
|
22
|
-
useEffect(() => blockConfig?.setConfig(block_config), [title, data]);
|
|
19
|
+
});
|
|
23
20
|
if (data && data.length > 0) {
|
|
24
21
|
const grouped_data = categoryKey ? from(data).groupby(yearKey, categoryKey) //Somme par année et categorykey
|
|
25
22
|
.rollup({ [valueKey]: op.sum(valueKey) })
|
|
@@ -19,7 +19,8 @@ export const Select = ({ name, dataset: datasetSource, options: input_options =
|
|
|
19
19
|
if (data_options === undefined) {
|
|
20
20
|
return _jsx(_Fragment, {});
|
|
21
21
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
const value = initial_value == null || initial_value == false
|
|
23
|
+
? undefined
|
|
24
|
+
: initial_value;
|
|
25
|
+
return (_jsx(NextPrevSelect, { name: name, options: data_options, defaultValue: value, value: value, arrows: arrows, optionFilterProp: "label", ...rest }));
|
|
25
26
|
};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { DownloadOutlined, FileImageOutlined, FullscreenOutlined, MoreOutlined, } from "@ant-design/icons";
|
|
3
|
-
import { HiQuestionMarkCircle } from "react-icons/hi2";
|
|
2
|
+
import { DownloadOutlined, FileImageOutlined, FullscreenOutlined, MoreOutlined, QuestionCircleOutlined, } from "@ant-design/icons";
|
|
4
3
|
import { Card, theme, Modal, Dropdown, Flex, Button, Popover, Typography } from "antd";
|
|
5
4
|
import React, { createContext, useEffect, useState } from "react";
|
|
6
5
|
import Attribution from "../Attributions/Attributions";
|
|
@@ -103,6 +102,6 @@ const DashboardElement = ({ children, title, header = true, attributions, isFetc
|
|
|
103
102
|
}, variant: virtual ? 'borderless' : 'outlined', title: header && !virtual &&
|
|
104
103
|
_jsxs(Flex, { justify: "space-between", align: "center", children: [_jsx("span", { style: { overflow: "hidden", paddingRight: 15 }, children: title }), _jsx("div", { style: { marginRight: 5, fontSize: 16 }, children: toolbox && dropdown_toolbox })] }), children: _jsxs(Flex, { vertical: true, justify: "space-between", style: { height: "100%" }, children: [_jsx(chartContext.Provider, { value: { chartRef, setchartRef, setData, setNodata }, children: _jsx(LoadingContainer, { isFetching: isFetching, noData: nodata, children: children }) }), _jsxs(Flex, { justify: "flex-end", align: "flex-end", style: { marginRight: 5 }, children: [attributions && (_jsx("div", { style: { marginTop: "auto" }, children: _jsx(Attribution, { licenses: licenses, data: attributions }) })), description && (_jsx(Popover, { content: _jsx("div", { style: { maxWidth: 800 }, children: typeof description === "string" ?
|
|
105
104
|
_jsxs(Text, { italic: true, type: "secondary", children: [" ", description, " "] })
|
|
106
|
-
: _jsx(_Fragment, { children: description }) }), children: _jsx(Button, { type: "link", icon: _jsx(
|
|
105
|
+
: _jsx(_Fragment, { children: description }) }), children: _jsx(Button, { type: "link", icon: _jsx(QuestionCircleOutlined, {}), style: { fontSize: "150%" } }) }))] })] }) }), toolbox && fullscreen && (_jsxs(Modal, { forceRender: true, title: title, open: modalIsOpen, onCancel: () => setModalIsOpen(false), onOk: () => setModalIsOpen(false), footer: null, wrapClassName: "modal-fullscreen", children: [fullscreenChildren, attributions && _jsx(Attribution, { data: attributions })] }))] }));
|
|
107
106
|
};
|
|
108
107
|
export default DashboardElement;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Card, Dropdown, theme } from "antd";
|
|
3
|
-
import { createContext, useId, useState } from "react";
|
|
4
|
-
import {
|
|
5
|
-
import { AiOutlineMore } from "react-icons/ai";
|
|
3
|
+
import { createContext, useContext, useEffect, useId, useState } from "react";
|
|
4
|
+
import { Icon } from "@iconify/react";
|
|
6
5
|
import { ProducersFooter } from "../Dataset/Producer";
|
|
6
|
+
import { MoreOutlined } from '@ant-design/icons';
|
|
7
7
|
const { useToken } = theme;
|
|
8
8
|
export const ChartBlockContext = createContext(undefined);
|
|
9
9
|
export const DSL_ChartBlock = ({ children }) => {
|
|
@@ -13,12 +13,12 @@ export const DSL_ChartBlock = ({ children }) => {
|
|
|
13
13
|
const menu_items = [
|
|
14
14
|
{
|
|
15
15
|
key: "export_data_csv",
|
|
16
|
-
label: (_jsxs("a", { onClick: () => handleExportData(), children: [_jsx(
|
|
16
|
+
label: (_jsxs("a", { onClick: () => handleExportData(), children: [_jsx(Icon, { icon: "hugeicons:csv-01" }), " CSV"] })),
|
|
17
17
|
disabled: config.dataExport === undefined
|
|
18
18
|
},
|
|
19
19
|
];
|
|
20
20
|
const has_action = menu_items.some(item => !item.disabled);
|
|
21
|
-
const dropdown_toolbox = (_jsx(Dropdown, { menu: { items: menu_items }, children: _jsx("a", { style: { color: token.colorTextBase }, children: _jsx(
|
|
21
|
+
const dropdown_toolbox = (_jsx(Dropdown, { menu: { items: menu_items }, children: _jsx("a", { style: { color: token.colorTextBase }, children: _jsx(MoreOutlined, {}) }) }));
|
|
22
22
|
const handleExportData = () => {
|
|
23
23
|
console.log('datadl');
|
|
24
24
|
console.log(config.dataExport);
|
|
@@ -38,3 +38,10 @@ export const DSL_ChartBlock = ({ children }) => {
|
|
|
38
38
|
};
|
|
39
39
|
return (_jsx(ChartBlockContext.Provider, { value: { config: config, setConfig: (e) => setConfig(e) }, children: _jsxs(Card, { style: { height: '100%' }, extra: has_action && dropdown_toolbox, title: config.title, children: [children, _jsx(ProducersFooter, { component: children })] }) }));
|
|
40
40
|
};
|
|
41
|
+
export const useBlockConfig = ({ title, dataExport }) => {
|
|
42
|
+
const blockContext = useContext(ChartBlockContext);
|
|
43
|
+
useEffect(() => blockContext?.setConfig({
|
|
44
|
+
title: title,
|
|
45
|
+
dataExport: dataExport
|
|
46
|
+
}), [title, dataExport]);
|
|
47
|
+
};
|
|
@@ -86,7 +86,7 @@ export const DSL_Dataset = ({ children, id, provider: provider_input, type: prov
|
|
|
86
86
|
useEffect(() => {
|
|
87
87
|
const finalData = data?.data && transformers.reduce((datat, fn) => fn(datat), data.data);
|
|
88
88
|
if (datasetRegistryContext) {
|
|
89
|
-
datasetRegistryContext({ id: id, resource: resource, data: finalData, isFetching: isFetching, isError: isError, producers: producers });
|
|
89
|
+
datasetRegistryContext({ id: id, resource: resource, data: finalData, isFetching: isFetching, isError: isError, producers: producers, geojson: data?.geojson });
|
|
90
90
|
//Ajouter une info pour distinguer les erreurs du fourniseurs et celles des transformers ?
|
|
91
91
|
}
|
|
92
92
|
}, [resource, data, isFetching, someFetching, children]);
|
|
@@ -5,6 +5,7 @@ export declare const useDataset: (dataset_id?: string) => {
|
|
|
5
5
|
isFetching: boolean;
|
|
6
6
|
isError: boolean;
|
|
7
7
|
producers?: any[];
|
|
8
|
+
geojson?: any;
|
|
8
9
|
} | undefined;
|
|
9
10
|
export declare const useDatasets: (dataset_ids?: string[]) => {
|
|
10
11
|
id: string;
|
|
@@ -13,6 +14,7 @@ export declare const useDatasets: (dataset_ids?: string[]) => {
|
|
|
13
14
|
isFetching: boolean;
|
|
14
15
|
isError: boolean;
|
|
15
16
|
producers?: any[];
|
|
17
|
+
geojson?: any;
|
|
16
18
|
}[] | undefined;
|
|
17
19
|
export declare const useAllDatasets: () => {
|
|
18
20
|
id: string;
|
|
@@ -21,4 +23,5 @@ export declare const useAllDatasets: () => {
|
|
|
21
23
|
isFetching: boolean;
|
|
22
24
|
isError: boolean;
|
|
23
25
|
producers?: any[];
|
|
26
|
+
geojson?: any;
|
|
24
27
|
}[] | undefined;
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { Badge, Collapse, Divider, FloatButton, Modal, theme, Typography } from "antd";
|
|
3
3
|
import { DataPreview, PalettePreview } from "../../dsl";
|
|
4
4
|
import { useAllDatasets } from "../Dataset/hooks";
|
|
5
|
-
import {
|
|
5
|
+
import { BugOutlined } from "@ant-design/icons";
|
|
6
6
|
import { useState } from "react";
|
|
7
7
|
import { DatasetBadgeStatus } from "../Dataset/DataPreview";
|
|
8
8
|
import { ControlPreview } from "../Control/Control";
|
|
@@ -20,5 +20,5 @@ export const Debug = () => {
|
|
|
20
20
|
label: _jsxs("span", { children: [_jsx(DatasetBadgeStatus, { isError: dataset?.isError, isFetching: dataset?.isFetching }), " ", dataset.id, " ", " ", _jsxs(Text, { type: "secondary", children: [" ", dataset?.resource, " "] }), " ", _jsx(Badge, { color: token.colorInfo, overflowCount: 9999, count: dataset?.data?.length })] }),
|
|
21
21
|
children: _jsx(DataPreview, { dataset: dataset.id, pageSize: 3 })
|
|
22
22
|
}));
|
|
23
|
-
return (_jsxs(_Fragment, { children: [_jsx(FloatButton, { icon: _jsx(
|
|
23
|
+
return (_jsxs(_Fragment, { children: [_jsx(FloatButton, { icon: _jsx(BugOutlined, {}), type: "primary", onClick: () => setIsModalOpen(true), style: { top: 5 }, className: "debugFloatButton" }), ";", _jsxs(Modal, { title: "Information concepteur", width: "90%", centered: true, styles: { content: { 'width': "100%", padding: 36 } }, closable: { 'aria-label': 'Custom Close Button' }, open: isModalOpen, onCancel: () => setIsModalOpen(false), footer: null, children: [_jsx(Title, { level: 5, children: "Jeux de donn\u00E9es " }), _jsx(Collapse, { accordion: true, items: items }), _jsx(Divider, {}), _jsx(Title, { level: 5, children: "Contr\u00F4les utilisateur " }), _jsx(ControlPreview, {}), _jsx(Divider, {}), _jsx(Title, { level: 5, children: "Palette " }), _jsx(PalettePreview, {})] })] }));
|
|
24
24
|
};
|
|
@@ -2,8 +2,8 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import { useRef } from 'react';
|
|
3
3
|
import { Button, Card, Typography } from "antd";
|
|
4
4
|
import { useState } from "react";
|
|
5
|
-
import { BsInfoCircle, BsInfoCircleFill } from "react-icons/bs";
|
|
6
5
|
import { cardStyles } from "../../utils/cardStyles";
|
|
6
|
+
import { InfoCircleFilled, InfoCircleOutlined } from "@ant-design/icons";
|
|
7
7
|
const { Text } = Typography;
|
|
8
8
|
/**
|
|
9
9
|
* Une card qui peux se retourner et afficher des informations a son verso
|
|
@@ -19,7 +19,7 @@ const FlipCard = ({ title, information, children }) => {
|
|
|
19
19
|
width: "100%",
|
|
20
20
|
};
|
|
21
21
|
const InfoButton = ({ filled = false }) => {
|
|
22
|
-
return (_jsx(Button, { type: "text", shape: "circle", "aria-label": "info", onClick: toggleFlipped, children: filled ? _jsx(
|
|
22
|
+
return (_jsx(Button, { type: "text", shape: "circle", "aria-label": "info", onClick: toggleFlipped, children: filled ? _jsx(InfoCircleFilled, {}) : _jsx(InfoCircleOutlined, {}) }));
|
|
23
23
|
};
|
|
24
24
|
return (_jsxs("div", { style: { position: "relative", height: "100%" }, children: [_jsx(Card, { title: title, extra: _jsxs(_Fragment, { children: [information && _jsx(InfoButton, { filled: flipped }), " "] }), style: {
|
|
25
25
|
transform: flipped ? "rotateY(180deg)" : "",
|
|
@@ -2,9 +2,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useContext, useState } from "react";
|
|
3
3
|
import { Layout, Menu, theme, Row, Col, Button, Divider } from "antd";
|
|
4
4
|
import { NavLink, useLocation } from "react-router-dom";
|
|
5
|
-
import { MdOutlineKeyboardDoubleArrowLeft, MdOutlineKeyboardDoubleArrowRight } from "react-icons/md";
|
|
6
5
|
import { generateMenuItems } from "../../utils/route_utils";
|
|
7
6
|
import { AppContext } from "./DashboardApp";
|
|
7
|
+
import { Icon } from "@iconify/react";
|
|
8
8
|
const style_img = {
|
|
9
9
|
height: 52,
|
|
10
10
|
maxWidth: "100%",
|
|
@@ -41,7 +41,7 @@ const DashboardSider = ({ style, logo, route_config }) => {
|
|
|
41
41
|
}, children: [_jsx(NavLink, { to: "", style: {
|
|
42
42
|
display: collapsed ? 'none' : undefined,
|
|
43
43
|
marginTop: 8, marginLeft: 8
|
|
44
|
-
}, children: _jsx("img", { style: style_img, src: appLogo, alt: title }) }), _jsx(Divider, { style: { display: collapsed ? 'none' : undefined }, type: "vertical" }), _jsx(Button, { type: "text", onClick: () => setCollapsed(!collapsed), icon: collapsed ? _jsx(
|
|
44
|
+
}, children: _jsx("img", { style: style_img, src: appLogo, alt: title }) }), _jsx(Divider, { style: { display: collapsed ? 'none' : undefined }, type: "vertical" }), _jsx(Button, { type: "text", onClick: () => setCollapsed(!collapsed), icon: collapsed ? _jsx(Icon, { icon: "material-symbols:keyboard-double-arrow-right-rounded" }) : _jsx(Icon, { icon: "material-symbols:keyboard-double-arrow-left-rounded" }), style: {
|
|
45
45
|
fontSize: '28px',
|
|
46
46
|
width: 32,
|
|
47
47
|
height: 32,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef, useState } from "react";
|
|
3
3
|
import { Empty, Spin, theme } from 'antd';
|
|
4
|
-
import {
|
|
4
|
+
import { Icon } from '@iconify/react';
|
|
5
5
|
/**
|
|
6
6
|
* Composant qui ajoute un effet de flou pendant le chargement (si temps de chargement > delay) et affiche un spinner au centre du contenu.
|
|
7
7
|
* @param isLoading Indique si le chargement est en cours ou non
|
|
@@ -28,6 +28,6 @@ const LoadingContainer = ({ isFetching, children, blurRadius = '10px', delay = 5
|
|
|
28
28
|
return (_jsxs(_Fragment, { children: [_jsx("div", { style: {
|
|
29
29
|
filter: blur ? `blur(${blurRadius})` : undefined,
|
|
30
30
|
display: noData ? "none" : undefined
|
|
31
|
-
}, children: children }), noData && _jsx(Empty, { style: { position: "relative", top: "50%", right: "50%", marginBottom: 50, transform: "translate(50%, -50%)" }, description: "Pas de donn\u00E9es disponibles", image: _jsx(
|
|
31
|
+
}, children: children }), noData && _jsx(Empty, { style: { position: "relative", top: "50%", right: "50%", marginBottom: 50, transform: "translate(50%, -50%)" }, description: "Pas de donn\u00E9es disponibles", image: _jsx(Icon, { icon: "ph:empty-fill", width: 80, color: token.colorPrimary }) }), blur ? _jsx(Spin, { size: "large", style: { position: 'absolute', left: '50%', top: '50%' } }) : _jsx(_Fragment, {})] }));
|
|
32
32
|
};
|
|
33
33
|
export default LoadingContainer;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { AnyLayer } from 'react-map-gl/maplibre';
|
|
2
|
+
import { AnyPaint } from 'mapbox-gl';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import 'maplibre-gl/dist/maplibre-gl.css';
|
|
5
|
+
type LayerType = AnyLayer["type"];
|
|
6
|
+
export declare const map_locale: {
|
|
7
|
+
'CooperativeGesturesHandler.WindowsHelpText': string;
|
|
8
|
+
'CooperativeGesturesHandler.MacHelpText': string;
|
|
9
|
+
'CooperativeGesturesHandler.MobileHelpText': string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Une carto simple avec un layer
|
|
13
|
+
*
|
|
14
|
+
* */
|
|
15
|
+
interface MapProps extends MapLayerProps {
|
|
16
|
+
/** Afficher une popup après un click sur la carte */
|
|
17
|
+
popup?: boolean;
|
|
18
|
+
/** Titre du graphique */
|
|
19
|
+
title?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare const Map: React.FC<MapProps>;
|
|
22
|
+
interface MapLayerProps {
|
|
23
|
+
/** Identifiant du jeu de données */
|
|
24
|
+
dataset: string;
|
|
25
|
+
/** Couleur des symboles */
|
|
26
|
+
color?: string;
|
|
27
|
+
/** Layer Type */
|
|
28
|
+
type?: LayerType;
|
|
29
|
+
/** Les paint properties de maplibre cf. https://maplibre.org/maplibre-style-spec/layers/#paint */
|
|
30
|
+
paint?: AnyPaint;
|
|
31
|
+
/** Colonne contenant la variable à représenter */
|
|
32
|
+
categoryKey?: string;
|
|
33
|
+
/** Colonne contenant la coordonnée x / longitude */
|
|
34
|
+
xKey?: string;
|
|
35
|
+
/** Colonne contenant la coordonnées y / latitude */
|
|
36
|
+
yKey?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Composant à utiliser comme enfant d'une <Map>
|
|
40
|
+
* Ajoute une couche (layer) à partir d'un dataset
|
|
41
|
+
*
|
|
42
|
+
* @param { MapLayerProps } props
|
|
43
|
+
* @returns { ReactElement }
|
|
44
|
+
*/
|
|
45
|
+
export declare const MapLayer: React.FC<MapLayerProps>;
|
|
46
|
+
export interface IMapBaseLayerProps {
|
|
47
|
+
layer: 'osm' | 'ortho';
|
|
48
|
+
tileSize?: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Composant à utiliser comme enfant d'une <Map>
|
|
52
|
+
* Permet d'ajouter un fond de plan (OSM ou orthophoto)
|
|
53
|
+
* devnote : couches spécifiques Hauts-de-France
|
|
54
|
+
*
|
|
55
|
+
* @param { IMapBaseLayerProps } props
|
|
56
|
+
* @returns { ReactElement }
|
|
57
|
+
*/
|
|
58
|
+
export declare const BaseLayer: React.FC<IMapBaseLayerProps>;
|
|
59
|
+
export {};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
// Composant carto
|
|
3
|
+
import Maplibre, { Layer, Source, useMap, Popup } from 'react-map-gl/maplibre';
|
|
4
|
+
import { useEffect, useRef, useState } from "react";
|
|
5
|
+
import { useDataset } from '../Dataset/hooks';
|
|
6
|
+
import bbox from '@turf/bbox';
|
|
7
|
+
import { getType } from '@turf/invariant';
|
|
8
|
+
import { featureCollection, point } from '@turf/helpers';
|
|
9
|
+
import { usePalette } from '../Palette/Palette';
|
|
10
|
+
import { from, op } from 'arquero';
|
|
11
|
+
import 'maplibre-gl/dist/maplibre-gl.css';
|
|
12
|
+
import { LegendControl } from '../MapLegend/MapLegend';
|
|
13
|
+
import { useBlockConfig } from '../DashboardPage/Block';
|
|
14
|
+
import { parseNumber } from '../../utils/parsers';
|
|
15
|
+
export const map_locale = {
|
|
16
|
+
'CooperativeGesturesHandler.WindowsHelpText': 'Utilisez Ctrl + molette pour zommer sur la carte.',
|
|
17
|
+
'CooperativeGesturesHandler.MacHelpText': 'Utilisez ⌘ + molette pour zommer sur la carte.',
|
|
18
|
+
'CooperativeGesturesHandler.MobileHelpText': 'Utilisez deux doights pour déplacer la carte.',
|
|
19
|
+
};
|
|
20
|
+
/** Construire un geojson a partir d'un tableau de données*/
|
|
21
|
+
const build_geojson = (params) => {
|
|
22
|
+
const { data, xKey, yKey } = params;
|
|
23
|
+
const features_collection = featureCollection(data.map((e) => {
|
|
24
|
+
const [x, y] = [parseNumber(e[xKey]), parseNumber(e[yKey])];
|
|
25
|
+
return point([x, y], { ...e });
|
|
26
|
+
}));
|
|
27
|
+
return features_collection;
|
|
28
|
+
};
|
|
29
|
+
export const Map = ({ dataset, color, type, paint, categoryKey, popup = false, title, xKey, yKey }) => {
|
|
30
|
+
const mapRef = useRef(null);
|
|
31
|
+
const [clickedFeature, setClickedFeature] = useState(undefined);
|
|
32
|
+
useBlockConfig({ title: title });
|
|
33
|
+
const onClickMap = (evt) => {
|
|
34
|
+
setClickedFeature({ ...evt.features[0], ...{ lngLat: evt.lngLat } });
|
|
35
|
+
};
|
|
36
|
+
const onMouseMoveMap = (evt) => {
|
|
37
|
+
if (!mapRef.current) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (evt?.features.length > 0 && popup) {
|
|
41
|
+
mapRef.current.getCanvasContainer().style.cursor = 'pointer';
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
mapRef.current.getCanvasContainer().style.cursor = 'grab';
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
return (_jsxs(Maplibre, { cooperativeGestures: true, locale: map_locale, ref: mapRef, interactiveLayerIds: [dataset], onClick: onClickMap, onMouseMove: onMouseMoveMap, style: { width: '100%', height: '500px' }, children: [_jsx(BaseLayer, { layer: "osm" }), _jsx(MapLayer, { dataset: dataset, color: color, type: type, paint: paint, categoryKey: categoryKey, xKey: xKey, yKey: yKey }), clickedFeature?.properties && categoryKey && popup &&
|
|
48
|
+
_jsxs(Popup, { longitude: clickedFeature.lngLat.lng, latitude: clickedFeature.lngLat.lat, onClose: () => { setClickedFeature(null); }, children: [_jsxs("div", { children: [" ", clickedFeature.properties[categoryKey], " "] }), " "] })] }));
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Composant à utiliser comme enfant d'une <Map>
|
|
52
|
+
* Ajoute une couche (layer) à partir d'un dataset
|
|
53
|
+
*
|
|
54
|
+
* @param { MapLayerProps } props
|
|
55
|
+
* @returns { ReactElement }
|
|
56
|
+
*/
|
|
57
|
+
export const MapLayer = ({ dataset, categoryKey, color = 'red', type = 'circle', paint, xKey, yKey }) => {
|
|
58
|
+
const { current: map } = useMap();
|
|
59
|
+
const data = useDataset(dataset);
|
|
60
|
+
// src (lib proj4 pour convertir)
|
|
61
|
+
// Si x et y sont definie, on construit le geojson
|
|
62
|
+
const geojson = xKey && yKey && data?.data ?
|
|
63
|
+
build_geojson({ data: data.data, xKey: xKey, yKey: yKey })
|
|
64
|
+
: data?.geojson; // Sinon on utilise le geojson (fournisseur wfs)
|
|
65
|
+
const geom_type = geojson?.features?.[0] && getType(geojson?.features?.[0]);
|
|
66
|
+
/** Type de données dans categoryKey (string ou number) */
|
|
67
|
+
const type_value = categoryKey && typeof (data?.data?.[0][categoryKey]);
|
|
68
|
+
/** Valeurs distinctes (si type string) */
|
|
69
|
+
const values = (type_value === 'string') && categoryKey && data?.data && from(data?.data).rollup({ a: op.array_agg_distinct(categoryKey) }).get('a', 0) || undefined;
|
|
70
|
+
/** Couleurs de la palette */
|
|
71
|
+
const colors = usePalette({ nColors: Array.isArray(values) ? values?.length : 1 });
|
|
72
|
+
const match = Array.isArray(values) ? values?.map((v, i) => ({
|
|
73
|
+
val: v,
|
|
74
|
+
color: colors?.[i],
|
|
75
|
+
})) : undefined;
|
|
76
|
+
/** Expression mapLibre qui permet de mapper les valeurs et les couleurs de la palette */
|
|
77
|
+
const expression = match && categoryKey
|
|
78
|
+
? [
|
|
79
|
+
"match",
|
|
80
|
+
["get", categoryKey],
|
|
81
|
+
...match?.flatMap((s) => [s.val, s.color]),
|
|
82
|
+
"purple" // fallback
|
|
83
|
+
]
|
|
84
|
+
: undefined;
|
|
85
|
+
const legendItems = match?.map((e) => ({ color: e.color, label: e.val })).sort((a, b) => a.label.localeCompare(b.label)) || [];
|
|
86
|
+
const layers = [];
|
|
87
|
+
/** POINT */
|
|
88
|
+
if (geom_type === 'Point' || geom_type === 'MultiPoint') {
|
|
89
|
+
const default_paint = { "circle-color": expression ?? color ?? colors[0] };
|
|
90
|
+
type = 'circle';
|
|
91
|
+
layers.push(_jsx(Layer, { id: dataset, type: "circle", paint: (paint ?? default_paint) }, dataset));
|
|
92
|
+
}
|
|
93
|
+
/** POLYGON */
|
|
94
|
+
else if (geom_type === 'Polygon' || geom_type === 'MultiPolygon') {
|
|
95
|
+
const default_paint = { "fill-color": expression ?? color ?? colors[0] };
|
|
96
|
+
type = 'fill';
|
|
97
|
+
layers.push(_jsx(Layer, { id: dataset, type: "fill", paint: (paint ?? default_paint) }, dataset));
|
|
98
|
+
layers.push(_jsx(Layer, { id: dataset + '_line', type: 'line', paint: { "line-width": 0.5, "line-color": '#fff' } }, dataset + '_line'));
|
|
99
|
+
}
|
|
100
|
+
/** LINESTRING */
|
|
101
|
+
else if (geom_type === 'LineString' || geom_type === 'MultiLineString') {
|
|
102
|
+
const default_paint = { "line-color": expression ?? color ?? colors[0] };
|
|
103
|
+
type = 'line';
|
|
104
|
+
layers.push(_jsx(Layer, { id: dataset, type: "line", paint: (paint ?? default_paint) }, dataset));
|
|
105
|
+
}
|
|
106
|
+
//devnote : regarder la colonne contenant les valeurs pour proposer une représentation (catégorie ou choroplèthe)
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (geojson) {
|
|
109
|
+
const box = bbox(geojson).slice(0, 4);
|
|
110
|
+
map?.fitBounds(box, { padding: 20 });
|
|
111
|
+
}
|
|
112
|
+
}, [geojson, map]);
|
|
113
|
+
return (_jsxs(_Fragment, { children: [geojson &&
|
|
114
|
+
_jsx(Source, { type: "geojson", data: geojson, children: layers }), _jsx(LegendControl, { items: legendItems })] }));
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Composant à utiliser comme enfant d'une <Map>
|
|
118
|
+
* Permet d'ajouter un fond de plan (OSM ou orthophoto)
|
|
119
|
+
* devnote : couches spécifiques Hauts-de-France
|
|
120
|
+
*
|
|
121
|
+
* @param { IMapBaseLayerProps } props
|
|
122
|
+
* @returns { ReactElement }
|
|
123
|
+
*/
|
|
124
|
+
export const BaseLayer = ({ layer, tileSize = 256 }) => {
|
|
125
|
+
//TODO : ne pas utiliser par défaut le fond de plan geo2france ?
|
|
126
|
+
const t = (() => {
|
|
127
|
+
switch (layer) {
|
|
128
|
+
case 'osm':
|
|
129
|
+
return `https://osm.geo2france.fr/mapcache/?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&FORMAT=image%2Fpng&TRANSPARENT=false&LAYERS=grey&TILED=true&WIDTH=${tileSize}&HEIGHT=${tileSize}&SRS=EPSG%3A3857&STYLES=&BBOX={bbox-epsg-3857}`;
|
|
130
|
+
case 'ortho':
|
|
131
|
+
return `https://www.geo2france.fr/geoserver/geo2france/ows/?bbox={bbox-epsg-3857}&format=image/png&service=WMS&version=1.3.0&request=GetMap&srs=EPSG:3857&transparent=true&width=${tileSize}&height=${tileSize}&layers=ortho_regionale_2018_rvb`;
|
|
132
|
+
}
|
|
133
|
+
})();
|
|
134
|
+
const source_raster = {
|
|
135
|
+
type: 'raster',
|
|
136
|
+
attribution: 'OpenStreetMap', //fixme
|
|
137
|
+
tiles: [
|
|
138
|
+
t
|
|
139
|
+
],
|
|
140
|
+
tileSize: tileSize
|
|
141
|
+
};
|
|
142
|
+
const layer_raster = {
|
|
143
|
+
'type': 'raster',
|
|
144
|
+
'paint': {}
|
|
145
|
+
};
|
|
146
|
+
return (_jsx(Source, { ...source_raster, children: _jsx(Layer, { ...layer_raster }) }));
|
|
147
|
+
};
|
|
@@ -10,3 +10,9 @@ interface MapLegendProps {
|
|
|
10
10
|
}
|
|
11
11
|
declare const MapLegend: React.FC<MapLegendProps>;
|
|
12
12
|
export default MapLegend;
|
|
13
|
+
interface LegendControlProps {
|
|
14
|
+
/** Elements de légende */
|
|
15
|
+
items: LegendItem[];
|
|
16
|
+
}
|
|
17
|
+
/** Un control pour Maplibre qui permet d'afficher une légende */
|
|
18
|
+
export declare const LegendControl: React.FC<LegendControlProps>;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
import { createRoot } from "react-dom/client";
|
|
4
|
+
import { useControl } from "react-map-gl/maplibre";
|
|
2
5
|
const default_style = {
|
|
3
6
|
backgroundColor: 'rgba(256,256,256,0.8)',
|
|
4
7
|
padding: '10px',
|
|
@@ -17,3 +20,29 @@ const MapLegend = ({ items, style }) => {
|
|
|
17
20
|
} }), _jsx("span", { children: item.label })] }, index))) }));
|
|
18
21
|
};
|
|
19
22
|
export default MapLegend;
|
|
23
|
+
/** Un control pour Maplibre qui permet d'afficher une légende */
|
|
24
|
+
export const LegendControl = ({ items }) => {
|
|
25
|
+
const rootRef = useRef(null);
|
|
26
|
+
useControl(() => {
|
|
27
|
+
const container = document.createElement("div");
|
|
28
|
+
//container.className = "maplibregl-ctrl"; // pour hériter du style par défaut
|
|
29
|
+
const root = createRoot(container);
|
|
30
|
+
rootRef.current = root;
|
|
31
|
+
const control = {
|
|
32
|
+
onAdd: (_map) => {
|
|
33
|
+
root.render(_jsx(MapLegend, { items: items }));
|
|
34
|
+
return container;
|
|
35
|
+
},
|
|
36
|
+
onRemove: () => {
|
|
37
|
+
container.parentNode?.removeChild(container);
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
return control;
|
|
41
|
+
}, { position: "top-right" });
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (rootRef.current) {
|
|
44
|
+
rootRef.current.render(_jsx(MapLegend, { items: items }));
|
|
45
|
+
}
|
|
46
|
+
}, [items]);
|
|
47
|
+
return null;
|
|
48
|
+
};
|
|
@@ -27,7 +27,7 @@ const NextPrevSelect = ({ name, options: input_options = [], style, value, defau
|
|
|
27
27
|
const [current_value, setCurrent_value] = useState(value);
|
|
28
28
|
const form = Form.useFormInstance();
|
|
29
29
|
useEffect(() => {
|
|
30
|
-
|
|
30
|
+
value && handleChange(value);
|
|
31
31
|
}, [value]);
|
|
32
32
|
const options = list_to_options(input_options);
|
|
33
33
|
const current_index = options?.findIndex((o) => o.value == form?.getFieldValue(name) || o.value == current_value);
|
package/dist/dsl/index.d.ts
CHANGED
|
@@ -16,4 +16,7 @@ import { Palette, usePalette, PalettePreview } from "../components/Palette/Palet
|
|
|
16
16
|
import { Debug } from "../components/Debug/Debug";
|
|
17
17
|
import { Join } from "../components/Dataset/Join";
|
|
18
18
|
import { ChartEcharts } from "../components/Charts/ChartEcharts";
|
|
19
|
-
|
|
19
|
+
import { useBlockConfig } from "../components/DashboardPage/Block";
|
|
20
|
+
import { Statistics, StatisticsCollection } from "../components/Charts/Statistics";
|
|
21
|
+
import { MapLayer, Map } from "../components/Map/Map";
|
|
22
|
+
export { Dashboard, Dataset, Provider, Transform, Join, Filter, DataPreview, ChartEcharts, ChartPie, ChartYearSerie, Statistics, StatisticsCollection, useDataset, useDatasets, useAllDatasets, useBlockConfig, Producer, Control, useControl, useAllControls, Radio, Select, Input, Palette, usePalette, PalettePreview, Debug, Map, MapLayer };
|
package/dist/dsl/index.js
CHANGED
|
@@ -16,4 +16,7 @@ import { Palette, usePalette, PalettePreview } from "../components/Palette/Palet
|
|
|
16
16
|
import { Debug } from "../components/Debug/Debug";
|
|
17
17
|
import { Join } from "../components/Dataset/Join";
|
|
18
18
|
import { ChartEcharts } from "../components/Charts/ChartEcharts";
|
|
19
|
-
|
|
19
|
+
import { useBlockConfig } from "../components/DashboardPage/Block";
|
|
20
|
+
import { Statistics, StatisticsCollection } from "../components/Charts/Statistics";
|
|
21
|
+
import { MapLayer, Map } from "../components/Map/Map";
|
|
22
|
+
export { Dashboard, Dataset, Provider, Transform, Join, Filter, DataPreview, ChartEcharts, ChartPie, ChartYearSerie, Statistics, StatisticsCollection, useDataset, useDatasets, useAllDatasets, useBlockConfig, Producer, Control, useControl, useAllControls, Radio, Select, Input, Palette, usePalette, PalettePreview, Debug, Map, MapLayer };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Parser automatiquement un nombre au format : 0.6, "0.6" ou "0,6" */
|
|
2
|
+
export const parseNumber = (value) => {
|
|
3
|
+
if (typeof value === "number") {
|
|
4
|
+
return value;
|
|
5
|
+
}
|
|
6
|
+
if (typeof value === "string") {
|
|
7
|
+
const normalized = value.replace(",", ".");
|
|
8
|
+
const num = parseFloat(normalized);
|
|
9
|
+
if (!isNaN(num)) {
|
|
10
|
+
return num;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return NaN;
|
|
14
|
+
};
|
|
@@ -4,5 +4,8 @@ interface useMapControlProps {
|
|
|
4
4
|
legendElement: React.ReactElement;
|
|
5
5
|
position?: ControlPosition;
|
|
6
6
|
}
|
|
7
|
+
/** A déprécier ? utiliser plutôt le composant LegendControl ?
|
|
8
|
+
* Uniquement pour legacy
|
|
9
|
+
*/
|
|
7
10
|
export declare const useMapControl: ({ mapRef, legendElement, position }: useMapControlProps) => void;
|
|
8
11
|
export {};
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// Ajouter un control (element HTML) sur une carte
|
|
2
2
|
import { useEffect } from "react";
|
|
3
3
|
import { createRoot } from 'react-dom/client';
|
|
4
|
+
/** A déprécier ? utiliser plutôt le composant LegendControl ?
|
|
5
|
+
* Uniquement pour legacy
|
|
6
|
+
*/
|
|
4
7
|
export const useMapControl = ({ mapRef, legendElement, position = 'top-right' }) => {
|
|
5
8
|
useEffect(() => {
|
|
6
9
|
if (mapRef?.current) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geo2france/api-dashboard",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Build dashboards with JSX/TSX",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -45,18 +45,24 @@
|
|
|
45
45
|
"test": "jest --watchAll"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
+
"@iconify/react": "^6.0.1",
|
|
49
|
+
"@turf/bbox": "^7.2.0",
|
|
50
|
+
"@turf/helpers": "^7.2.0",
|
|
51
|
+
"@turf/invariant": "^7.2.0",
|
|
48
52
|
"alasql": "^4.6.6",
|
|
49
53
|
"arquero": "^8.0.3",
|
|
50
|
-
"axios": "^1.
|
|
54
|
+
"axios": "^1.12.2",
|
|
51
55
|
"chroma-js": "^3.1.2",
|
|
52
|
-
"echarts": "^
|
|
53
|
-
"echarts-for-react": "^3.0.
|
|
56
|
+
"echarts": "^6.0.0",
|
|
57
|
+
"echarts-for-react": "^3.0.4",
|
|
54
58
|
"query-string": "~7.1.3",
|
|
55
59
|
"react-helmet": "^6.1.0",
|
|
56
60
|
"react-helmet-async": "^2.0.5",
|
|
57
61
|
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz"
|
|
58
62
|
},
|
|
59
63
|
"devDependencies": {
|
|
64
|
+
"@ant-design/icons": "^6.0.2",
|
|
65
|
+
"@iconify/json": "^2.2.382",
|
|
60
66
|
"@tanstack/react-query": "^5.51.11",
|
|
61
67
|
"@testing-library/dom": "^10.4.0",
|
|
62
68
|
"@testing-library/jest-dom": "^6.5.0",
|
|
@@ -76,7 +82,9 @@
|
|
|
76
82
|
"antd": "^5.24.3",
|
|
77
83
|
"jest": "^29.7.0",
|
|
78
84
|
"jest-environment-jsdom": "^29.7.0",
|
|
85
|
+
"maplibre-gl": "^4.7.1",
|
|
79
86
|
"react": "^18.3.1",
|
|
87
|
+
"react-map-gl": "^7.1.9",
|
|
80
88
|
"react-router-dom": "^6.25.1",
|
|
81
89
|
"ts-jest": "^29.2.5",
|
|
82
90
|
"ts-node": "^10.9.2",
|
|
@@ -86,12 +94,13 @@
|
|
|
86
94
|
"vite-plugin-svgr": "^4.2.0"
|
|
87
95
|
},
|
|
88
96
|
"peerDependencies": {
|
|
97
|
+
"@ant-design/icons": "^6.0.2",
|
|
89
98
|
"@tanstack/react-query": "^5.51.11",
|
|
90
99
|
"antd": "^5.18.3",
|
|
100
|
+
"maplibre-gl": "^4.7.1",
|
|
91
101
|
"react": "^18.3.1",
|
|
92
102
|
"react-dom": "^18.3.1",
|
|
93
|
-
"react-
|
|
94
|
-
"react-map-gl": "^7.1.7",
|
|
103
|
+
"react-map-gl": "^7.1.9",
|
|
95
104
|
"react-router-dom": "^6.25.1"
|
|
96
105
|
}
|
|
97
106
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const mapOperator: (operator: any) => string;
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
export const mapOperator = (operator) => {
|
|
2
|
-
switch (operator) {
|
|
3
|
-
case "ne":
|
|
4
|
-
return '<>';
|
|
5
|
-
case "gte":
|
|
6
|
-
return '>=';
|
|
7
|
-
case "gt":
|
|
8
|
-
return '>';
|
|
9
|
-
case "lte":
|
|
10
|
-
return `<=`;
|
|
11
|
-
case "lt":
|
|
12
|
-
return `<`;
|
|
13
|
-
case "eq":
|
|
14
|
-
return "=";
|
|
15
|
-
case "contains":
|
|
16
|
-
case "startswith":
|
|
17
|
-
case "endswith":
|
|
18
|
-
return "ilike";
|
|
19
|
-
case "containss":
|
|
20
|
-
case "startswiths":
|
|
21
|
-
case "endswiths":
|
|
22
|
-
return "like";
|
|
23
|
-
case "ncontains":
|
|
24
|
-
case "nstartswith":
|
|
25
|
-
case "nendswith":
|
|
26
|
-
return "not ilike";
|
|
27
|
-
case "ncontainss":
|
|
28
|
-
case "nstartswiths":
|
|
29
|
-
case "nendswiths":
|
|
30
|
-
return "not like";
|
|
31
|
-
case "in":
|
|
32
|
-
return operator;
|
|
33
|
-
default:
|
|
34
|
-
return "";
|
|
35
|
-
}
|
|
36
|
-
};
|