@geo2france/api-dashboard 1.16.0 → 1.17.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.
@@ -25,12 +25,12 @@ export const Statistics = ({ dataset: dataset_id, dataKey, unit, evolutionSuffix
25
25
  const dataset = useDataset(dataset_id);
26
26
  const row = dataset?.data?.slice(-1)[0];
27
27
  const value = row?.[dataKey]; // Dernière valeur du dataset. Caster en Number ?
28
- 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 compare_value = compareWith === 'previous' ? dataset?.data?.slice(-2)?.[0]?.[dataKey] : dataset?.data?.slice(0, 1)?.[0]?.[dataKey]; //Première ou avant dernière
29
29
  const evolution = relativeEvolution ? 100 * ((value - compare_value) / compare_value) : value - compare_value;
30
30
  const evolution_unit = relativeEvolution ? '%' : unit;
31
31
  const evolution_is_good = invertColor ? evolution < 0 : evolution > 0;
32
32
  const tooltip = help && _jsx(Tooltip, { title: help, children: _jsx(QuestionCircleOutlined, {}) });
33
- const CallbackParams = { value: value || NaN, compareValue: compare_value || NaN, data: dataset?.data || [], row: row };
33
+ const CallbackParams = { value: value ?? NaN, compareValue: compare_value ?? NaN, data: dataset?.data ?? [], row: row };
34
34
  let subtitle;
35
35
  if (annotation !== undefined) {
36
36
  subtitle = typeof annotation === 'function' ? annotation(CallbackParams) : annotation;
@@ -40,6 +40,7 @@ export const ChartYearSerie = ({ dataset: dataset_id, categoryKey, valueKey, sec
40
40
  .derive({ part: d => 100 * d.value / op.sum(d.value) }) // Data for normalized view
41
41
  .rename({ value: valueKey, part: `${valueKey}_pct`, secondaryValue: secondaryValueKey || '' }) // Rename to original var name
42
42
  ).objects();
43
+ chart_data.sort((a, b) => a[yearKey] - b[yearKey]);
43
44
  }
44
45
  const COLORS = usePalette({ nColors: distinct_cat?.length }) || [];
45
46
  const colors_labels = usePaletteLabels();
@@ -7,15 +7,31 @@ declare const Control: React.FC<IControlProps>;
7
7
  export default Control;
8
8
  export declare const useControl: (name: string) => string | undefined;
9
9
  export declare const useAllControls: () => Record<string, any>;
10
- export declare const list_to_options: (input?: string[] | number[] | {
11
- label: string | number;
12
- value: string | number;
10
+ export declare const list_to_options: (input?: string[] | {
11
+ label: string;
12
+ value: string;
13
13
  }[]) => {
14
- label: string | number;
15
- value: string | number;
14
+ label: string;
15
+ value: string;
16
16
  }[];
17
17
  interface IControlProps {
18
18
  children: ReactElement | ReactElement[];
19
19
  }
20
20
  export declare const DSL_Control: React.FC<IControlProps>;
21
21
  export declare const ControlPreview: React.FC;
22
+ type ControlContextType = {
23
+ values: Record<string, any>;
24
+ register: (control: {
25
+ name: string;
26
+ value: any;
27
+ }) => void;
28
+ clear: () => void;
29
+ getAll: () => Record<string, any>;
30
+ };
31
+ export declare const ControlContext: React.Context<ControlContextType | undefined>;
32
+ export declare const CreateControlesRegistry: () => {
33
+ register: (c: Record<string, any>) => void;
34
+ clear: () => void;
35
+ getAll: () => Record<string, any>;
36
+ values: Record<string, any>;
37
+ };
@@ -1,7 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Descriptions, Form, Layout } from "antd";
3
- import React, { useContext, useEffect } from "react";
4
- import { ControlContext } from "../DashboardPage/Page";
3
+ import React, { createContext, useCallback, useContext, useEffect, useState } from "react";
5
4
  const { Header } = Layout;
6
5
  /*
7
6
  * Composant destiné à recevoir un Form avec les contrôles de la page
@@ -28,7 +27,7 @@ export const useControl = (name) => {
28
27
  if (!context_controls) {
29
28
  throw new Error("useControl must be used within a ControlProvider");
30
29
  }
31
- const { values } = context_controls;
30
+ const values = context_controls.getAll();
32
31
  const value = values[name];
33
32
  return value;
34
33
  };
@@ -49,8 +48,8 @@ export const list_to_options = (input = []) => {
49
48
  return [];
50
49
  }
51
50
  return input.map((o) => {
52
- if (typeof o == "string" || typeof o == "number") {
53
- return { label: String(o), value: o };
51
+ if (typeof o == "string") {
52
+ return { label: String(o), value: String(o) };
54
53
  }
55
54
  return o;
56
55
  });
@@ -64,7 +63,7 @@ export const DSL_Control = ({ children }) => {
64
63
  if (!context_controls) { //Le contexte peut être nul ?
65
64
  throw new Error("useControl must be used within a ControlProvider");
66
65
  }
67
- const { values: _control, pushValue: pushControl } = context_controls;
66
+ const { values: _control, register: pushControl } = context_controls;
68
67
  const childrenArray = React.Children.toArray(children).filter((child) => React.isValidElement(child));
69
68
  //Ajout des nouvelles valeurs de controles dans le contexte de la page
70
69
  const handleChange = (changed_value) => {
@@ -83,3 +82,26 @@ export const ControlPreview = ({}) => {
83
82
  }));
84
83
  return (_jsx(Descriptions, { items: items }));
85
84
  };
85
+ export const ControlContext = createContext(undefined);
86
+ export const CreateControlesRegistry = () => {
87
+ /* CONTROLS */
88
+ const [controls, setControles] = useState({});
89
+ const pushControl = useCallback((c) => {
90
+ setControles(prev => ({
91
+ ...prev,
92
+ ...c
93
+ }));
94
+ }, []);
95
+ const clear = useCallback(() => {
96
+ setControles({});
97
+ }, []);
98
+ const getAll = useCallback(() => {
99
+ return controls;
100
+ }, [controls]);
101
+ return ({
102
+ register: pushControl,
103
+ clear,
104
+ getAll,
105
+ values: controls
106
+ });
107
+ };
@@ -2,18 +2,18 @@ import type { RadioGroupProps } from 'antd';
2
2
  import { SimpleRecord } from '../../types';
3
3
  export declare const buildOptionsFromData: (data: SimpleRecord[], labelField?: string, valueField?: string) => {
4
4
  label: string;
5
- value: string | number;
5
+ value: string;
6
6
  }[];
7
7
  type ExtendedRadioGroupProps = RadioGroupProps & {
8
8
  name?: string;
9
9
  dataset?: string;
10
10
  options?: {
11
11
  label: string;
12
- value: string | number;
13
- }[] | string[] | number[];
12
+ value: string;
13
+ }[] | string[];
14
14
  labelField?: string;
15
15
  valueField?: string;
16
- initalValue?: string | number;
16
+ initalValue?: string;
17
17
  };
18
18
  export declare const Radio: React.FC<ExtendedRadioGroupProps>;
19
19
  export {};
@@ -6,6 +6,9 @@ import { list_to_options } from './Control';
6
6
  // On construit les options depuis le tableau de données, utiliser pour Radio et Select
7
7
  export const buildOptionsFromData = (data, labelField = 'label', valueField = 'value') => {
8
8
  const t = from(data);
9
+ if (data.length <= 0) { //Avoir arquero error on empty data
10
+ return [];
11
+ }
9
12
  return (t.select(labelField, valueField)
10
13
  .dedupe(valueField)
11
14
  .objects()
@@ -3,13 +3,15 @@ type ExtendedSelectProps = Omit<SelectProps<any>, 'options'> & {
3
3
  dataset?: string;
4
4
  options?: {
5
5
  label: string;
6
- value: string | number;
7
- }[] | string[] | number[];
8
- initial_value?: string | number;
6
+ value: string;
7
+ }[] | string[];
8
+ initial_value?: string;
9
9
  labelField?: string;
10
10
  valueField?: string;
11
11
  name?: string;
12
+ label?: string;
12
13
  arrows?: boolean;
14
+ reverse?: boolean;
13
15
  };
14
16
  export declare const Select: React.FC<ExtendedSelectProps>;
15
17
  export {};
@@ -5,13 +5,13 @@ import NextPrevSelect from '../NextPrevSelect/NextPrevSelect';
5
5
  import { list_to_options } from './Control';
6
6
  // TODO : a fusionner avec NextPrevSelect pour n'avoir qu'un seul composant
7
7
  // Actuellement, Select apporte seulement le fait de choisir les valeurs depuis un
8
- export const Select = ({ name, dataset: datasetSource, options: input_options = [], labelField = 'label', valueField = 'value', arrows = false, initial_value: initial_value_in, ...rest }) => {
8
+ export const Select = ({ name, dataset: datasetSource, options: input_options = [], labelField = 'label', valueField = 'value', arrows = false, reverse = false, label, initial_value: initial_value_in, ...rest }) => {
9
9
  const options = list_to_options(input_options);
10
10
  const data = useDataset(datasetSource)?.data;
11
11
  const data_options = datasetSource ? (data && buildOptionsFromData(data, labelField, valueField)) : options;
12
12
  const myOptions = data_options && data_options.map((o) => {
13
- if (typeof o == "string" || typeof o == "number") {
14
- return { label: o, value: o };
13
+ if (typeof o == "string") {
14
+ return { label: o, value: String(o) };
15
15
  }
16
16
  return o;
17
17
  });
@@ -22,5 +22,5 @@ export const Select = ({ name, dataset: datasetSource, options: input_options =
22
22
  const value = initial_value == null || initial_value == false
23
23
  ? undefined
24
24
  : initial_value;
25
- return (_jsx(NextPrevSelect, { name: name, options: data_options, defaultValue: value, value: value, arrows: arrows, optionFilterProp: "label", ...rest }));
25
+ return (_jsx(NextPrevSelect, { name: name, label: label ?? name, options: data_options, defaultValue: value, value: value, arrows: arrows, reverse: reverse, optionFilterProp: "label", ...rest }));
26
26
  };
@@ -17,14 +17,6 @@ interface IDashboardPageProps {
17
17
  }
18
18
  declare const DashboardPage: React.FC<IDashboardPageProps>;
19
19
  export default DashboardPage;
20
- type ControlContextType = {
21
- values: Record<string, any>;
22
- pushValue: (control: {
23
- name: string;
24
- value: any;
25
- }) => void;
26
- };
27
- export declare const ControlContext: React.Context<ControlContextType | undefined>;
28
20
  interface IDSLDashboardPageProps {
29
21
  children: React.ReactNode;
30
22
  name?: string;
@@ -1,14 +1,14 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Button, Col, Dropdown, Flex, Grid, Layout, Radio, Row, Tabs, theme } from "antd";
3
- import React, { isValidElement, useState, createContext, useEffect } from "react";
3
+ import React, { isValidElement, useState, useEffect, useContext } from "react";
4
4
  import { Helmet } from "react-helmet-async";
5
5
  import { useSearchParamsState } from "../../utils/useSearchParamsState";
6
- import Control, { DSL_Control } from "../Control/Control";
6
+ import Control, { ControlContext, DSL_Control } from "../Control/Control";
7
7
  import { Dataset, Debug, Provider } from "../../dsl";
8
8
  import { DEFAULT_PALETTE, Palette, PaletteContext } from "../Palette/Palette";
9
9
  import { Section } from "./Section";
10
10
  import { Icon } from "@iconify/react";
11
- import { useDatasetRegistry } from "../Dataset/hooks";
11
+ import { DatasetRegistryContext } from "../Dataset/context";
12
12
  const { Header } = Layout;
13
13
  const { useToken } = theme;
14
14
  const getSection = (child) => React.isValidElement(child) ? child.props.section : undefined;
@@ -40,14 +40,15 @@ const DashboardPage = ({ children: children_input, control, row_gutter = [8, 8],
40
40
  })), value: activeTab, onChange: (e) => setActiveTab(e.target.value) }), control] }) }), _jsx(Row, { gutter: row_gutter, style: { margin: 16 }, children: children.map((child, idx) => ({ child, idx })).filter(({ child }) => (getSection(child) ?? 'Autres') == activeTab).map(({ child, idx }) => _jsx(Col, { xl: 12, xs: 24, children: child }, idx)) })] }));
41
41
  };
42
42
  export default DashboardPage;
43
- export const ControlContext = createContext(undefined);
44
43
  export const DSL_DashboardPage = ({ name = 'Tableau de bord', columns = 2, children, debug = false }) => {
45
44
  const { token } = useToken();
46
45
  const [palette, setPalette] = useState(DEFAULT_PALETTE);
47
- const datasetRegistry = useDatasetRegistry();
46
+ const datasetRegistry = useContext(DatasetRegistryContext);
47
+ const controlesRegistry = useContext(ControlContext);
48
48
  useEffect(() => {
49
49
  return () => {
50
50
  datasetRegistry.clear();
51
+ controlesRegistry?.clear();
51
52
  };
52
53
  }, []);
53
54
  //const allDatasetLoaded = Object.values(datasets).every(d => !d.isFetching);
@@ -1,10 +1,9 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useContext, useEffect } from "react";
3
3
  import { useApi } from "../..";
4
- import { ControlContext } from "../DashboardPage/Page";
5
4
  import { Producer } from "./Producer";
6
5
  import React from "react";
7
- import { Filter, Transform, useAllDatasets, useDatasets } from "../../dsl";
6
+ import { Filter, Transform, useAllControls, useAllDatasets, useDatasets } from "../../dsl";
8
7
  import alasql from "alasql";
9
8
  import { DataProviderContext, getProviderFromType } from "./Provider";
10
9
  import { Join } from "./Join";
@@ -58,8 +57,7 @@ export const DSL_Dataset = ({ children, id, provider: provider_input, type: prov
58
57
  };
59
58
  const datasetRegistryContext = useContext(DatasetRegistryContext);
60
59
  const allDatasets = useAllDatasets();
61
- const controlContext = useContext(ControlContext);
62
- const controls = controlContext?.values;
60
+ const controls = useAllControls();
63
61
  const providerContext = useContext(DataProviderContext);
64
62
  const provider = (providerUrl && getProviderFromType(providerType)(providerUrl)) || providerContext || provider_input;
65
63
  if (provider === undefined) {
@@ -1,7 +1,7 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useDatasets } from "./hooks";
3
3
  import { Typography } from "antd";
4
- const { Link } = Typography;
4
+ const { Link, Text } = Typography;
5
5
  /**
6
6
  * Les props sont récupérées par le parent (Dataset) et enregistrés avec le dataset
7
7
  */
@@ -21,7 +21,7 @@ export const ProducersFooter = ({ component }) => {
21
21
  const uniqueProducers = Array.from(new Map(producers?.map(p => [p.nom, p]) // clé unique = nom
22
22
  ).values());
23
23
  uniqueProducers.sort((a, b) => a.nom.localeCompare(b.nom));
24
- return datasets ? (_jsxs(_Fragment, { children: ["Source des donn\u00E9es :", " ", uniqueProducers?.map((p, idx, arr) => (_jsxs("span", { children: [_jsx(Link, { href: p.url, children: p.nom }), idx < arr.length - 1 ? ', ' : ''] }, idx)))] })) : null;
24
+ return (producers?.length > 0) ? (_jsxs(Text, { type: "secondary", style: { padding: 4 }, children: ["Source des donn\u00E9es :", " ", uniqueProducers?.map((p, idx, arr) => (_jsxs("span", { children: [_jsx(Link, { href: p.url, children: p.nom }), idx < arr.length - 1 ? ', ' : ''] }, idx)))] })) : null;
25
25
  }
26
26
  return _jsx(_Fragment, {});
27
27
  };
@@ -1,6 +1,16 @@
1
1
  import { SimpleRecord } from "../..";
2
- interface ITransformProps<T = SimpleRecord[]> {
3
- children: string | ((data: T) => T);
2
+ interface ITransformProps {
3
+ /**
4
+ * Contenu du Transform :
5
+ * - **string** : SQL (interprété par Alasql)
6
+ * - **fonction** `(data: any) => SimpleRecord[]` : transforme les données brutes ou déjà transformées
7
+ * en un tableau de `SimpleRecord`.
8
+ */
9
+ children: string | ((data: any) => SimpleRecord[]);
4
10
  }
5
- export declare const DSL_Transform: React.FC<ITransformProps>;
11
+ /**
12
+ * Composant permettant d'appliquer une transformation à un jeu de données.
13
+ * La transformation est appliquée via une fonction ou une chaîne SQL.
14
+ */
15
+ export declare const Transform: React.FC<ITransformProps>;
6
16
  export {};
@@ -1,7 +1,8 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
- /*
3
- * Les props sont utilisées dans le Dataset parent pour appliquer une (ou plusieurs fonction de transformation)
4
- */
5
- export const DSL_Transform = () => {
2
+ /**
3
+ * Composant permettant d'appliquer une transformation à un jeu de données.
4
+ * La transformation est appliquée via une fonction ou une chaîne SQL.
5
+ */
6
+ export const Transform = () => {
6
7
  return _jsx(_Fragment, {});
7
8
  };
@@ -2,7 +2,11 @@ import { dataset } from "./Dataset";
2
2
  export declare const useDataset: (dataset_id?: string) => dataset | undefined;
3
3
  export declare const useAllDatasets: () => dataset[];
4
4
  export declare const useDatasets: (dataset_ids?: string[]) => dataset[];
5
- export declare const useDatasetRegistry: () => {
5
+ /**
6
+ * Fonction permettant de créer le registre de dataset
7
+ * avec les méthodes nécessaires.
8
+ */
9
+ export declare const createDatasetRegistry: () => {
6
10
  register: (d: dataset) => void;
7
11
  clear: () => void;
8
12
  get: (dataset_id?: string) => dataset | undefined;
@@ -17,7 +17,11 @@ export const useDatasets = (dataset_ids) => {
17
17
  const datasets = useAllDatasets();
18
18
  return (datasets.filter(d => dataset_ids?.includes(d.id)));
19
19
  };
20
- export const useDatasetRegistry = () => {
20
+ /**
21
+ * Fonction permettant de créer le registre de dataset
22
+ * avec les méthodes nécessaires.
23
+ */
24
+ export const createDatasetRegistry = () => {
21
25
  /* DATASET */
22
26
  const [datasets, setdatasets] = useState({});
23
27
  const pushDataset = useCallback((d) => {
@@ -7,11 +7,11 @@ import DashboardSider from "./Sider";
7
7
  import { Content } from "antd/es/layout/layout";
8
8
  import { ErrorComponent } from "./Error";
9
9
  import { DasbhoardFooter } from "./Footer";
10
- import { createContext, useState } from "react";
11
- import { ControlContext } from "../DashboardPage/Page";
10
+ import { createContext } from "react";
12
11
  import { HelmetProvider } from "react-helmet-async";
13
- import { useDatasetRegistry } from "../Dataset/hooks";
12
+ import { createDatasetRegistry } from "../Dataset/hooks";
14
13
  import { DatasetRegistryContext } from "../Dataset/context";
14
+ import { ControlContext, CreateControlesRegistry } from "../Control/Control";
15
15
  //import '../../index.css' //TODO a intégrer en jsx
16
16
  const queryClient = new QueryClient();
17
17
  const default_theme = {
@@ -35,14 +35,6 @@ const default_theme = {
35
35
  export const AppContext = createContext({});
36
36
  const DashboardApp = ({ routes, theme, logo, brands, footerSlider, title, subtitle }) => {
37
37
  const context_values = { title, subtitle, logo };
38
- /* CONTROLS */
39
- const [controls, setControles] = useState({});
40
- const pushControl = (c) => {
41
- setControles(prev => ({
42
- ...prev,
43
- ...c
44
- }));
45
- };
46
- return (_jsx(QueryClientProvider, { client: queryClient, children: _jsx(ConfigProvider, { theme: theme || default_theme /* Merger plutôt ?*/, children: _jsx(HelmetProvider, { children: _jsx(AppContext.Provider, { value: context_values, children: _jsx(DatasetRegistryContext.Provider, { value: useDatasetRegistry(), children: _jsx(ControlContext.Provider, { value: { values: controls, pushValue: pushControl }, children: _jsx(HashRouter, { children: _jsx(Routes, { children: _jsxs(Route, { element: _jsxs(Layout, { hasSider: true, style: { minHeight: '100vh' }, children: [_jsx(DashboardSider, { route_config: routes }), _jsxs(Layout, { children: [_jsx(Content, { style: { width: "100%" }, children: _jsx(Outlet, {}) }), _jsx(DasbhoardFooter, { brands: brands, slider: footerSlider })] })] }), children: [generateRoutes(routes), _jsx(Route, { path: "*", element: _jsx(ErrorComponent, {}) })] }) }) }) }) }) }) }) }) }));
38
+ return (_jsx(QueryClientProvider, { client: queryClient, children: _jsx(ConfigProvider, { theme: theme || default_theme /* Merger plutôt ?*/, children: _jsx(HelmetProvider, { children: _jsx(AppContext.Provider, { value: context_values, children: _jsx(DatasetRegistryContext.Provider, { value: createDatasetRegistry(), children: _jsx(ControlContext.Provider, { value: CreateControlesRegistry(), children: _jsx(HashRouter, { children: _jsx(Routes, { children: _jsxs(Route, { element: _jsxs(Layout, { hasSider: true, style: { minHeight: '100vh' }, children: [_jsx(DashboardSider, { route_config: routes }), _jsxs(Layout, { children: [_jsx(Content, { style: { width: "100%" }, children: _jsx(Outlet, {}) }), _jsx(DasbhoardFooter, { brands: brands, slider: footerSlider })] })] }), children: [generateRoutes(routes), _jsx(Route, { path: "*", element: _jsx(ErrorComponent, {}) })] }) }) }) }) }) }) }) }) }));
47
39
  };
48
40
  export default DashboardApp;
@@ -1,14 +1,39 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Button, Layout, Typography } from "antd";
3
- import { useContext, useState } from "react";
2
+ import { Button, Layout, theme, Typography } from "antd";
3
+ import { useContext, useEffect, useState } from "react";
4
4
  import Slider from "@ant-design/react-slick";
5
5
  import { UpOutlined, DownOutlined } from "@ant-design/icons";
6
6
  import { AppContext } from "./DashboardApp";
7
7
  import "slick-carousel/slick/slick.css";
8
8
  import "slick-carousel/slick/slick-theme.css";
9
+ import { Icon } from "@iconify/react";
9
10
  const { Text } = Typography;
11
+ const { useToken } = theme;
10
12
  export const DasbhoardFooter = ({ brands, slider = true }) => {
11
13
  const [isCollapsed, setIsCollapsed] = useState(window.innerWidth < 768 ? true : false);
14
+ const [showScrollIndicator, setShowScrollIndicator] = useState(true);
15
+ const { token } = useToken();
16
+ /* 🤖 IA Generated effect
17
+ * Permet d'afficher ou non le scrollIndicator
18
+ */
19
+ useEffect(() => {
20
+ const checkShadow = () => {
21
+ const scrollTop = window.scrollY;
22
+ const windowHeight = window.innerHeight;
23
+ const docHeight = document.documentElement.scrollHeight;
24
+ setShowScrollIndicator(scrollTop + windowHeight < docHeight - 1);
25
+ };
26
+ // scroll listener
27
+ window.addEventListener("scroll", checkShadow);
28
+ // observer pour changements dynamiques du contenu
29
+ const observer = new ResizeObserver(checkShadow);
30
+ observer.observe(document.body);
31
+ checkShadow(); // initial
32
+ return () => {
33
+ window.removeEventListener("scroll", checkShadow);
34
+ observer.disconnect();
35
+ };
36
+ }, []);
12
37
  const toggleCollapse = () => {
13
38
  setIsCollapsed(!isCollapsed);
14
39
  };
@@ -40,10 +65,23 @@ export const DasbhoardFooter = ({ brands, slider = true }) => {
40
65
  height: "auto",
41
66
  minHeight: "40px",
42
67
  transition: "height 0.5s ease-in-out",
43
- overflow: "hidden",
68
+ overflow: "visible",
44
69
  borderTop: "1px solid #ccc",
45
70
  zIndex: 600, // maplibre top zIndex if 500
46
- }, children: [isCollapsed && (_jsxs(Text, { type: "secondary", children: [app_context?.title, " - ", app_context?.subtitle] })), _jsx("div", { style: { display: isCollapsed ? "none" : "block", padding: "10px 0" }, children: slider
71
+ }, children: [showScrollIndicator &&
72
+ /* Shaddow + chevron : show the user that remaing content is avaible downside */
73
+ _jsx("div", { className: "scroll-indicator", style: {
74
+ position: "absolute",
75
+ top: -40,
76
+ left: 0,
77
+ right: 0,
78
+ height: 40,
79
+ pointerEvents: "none",
80
+ display: "flex",
81
+ justifyContent: "center",
82
+ alignContent: "flex-end",
83
+ background: "linear-gradient(to bottom, rgba(255,255,255,0), rgba(0,0,0,0.1))",
84
+ }, children: _jsx(Icon, { icon: "fa6-solid:chevron-down", fontSize: 35, color: token.colorPrimary }) }), isCollapsed && (_jsxs(Text, { type: "secondary", children: [app_context?.title, " - ", app_context?.subtitle] })), _jsx("div", { style: { display: isCollapsed ? "none" : "block", padding: "10px 0" }, children: slider
47
85
  // Logos avec défilement (choix par défaut)
48
86
  ? _jsx(Slider
49
87
  // Défilement auto si plus de logos que la lagreur de l'écran ne peut en afficher
@@ -2,16 +2,17 @@ import React from 'react';
2
2
  import { CSSProperties } from "react";
3
3
  type NextPrevSelectProps = {
4
4
  options?: {
5
- label: string | number;
6
- value: string | number;
7
- }[] | string[] | number[];
5
+ label: string;
6
+ value: string;
7
+ }[] | string[];
8
8
  style?: CSSProperties;
9
- defaultValue?: string | number;
10
- value?: string | number;
11
- onChange?: (value: string | number) => void;
9
+ defaultValue?: string;
10
+ value?: string;
11
+ onChange?: (value: string) => void;
12
12
  reverse?: boolean;
13
13
  name?: string;
14
14
  arrows?: boolean;
15
+ label?: string;
15
16
  };
16
17
  declare const NextPrevSelect: React.FC<NextPrevSelectProps>;
17
18
  export default NextPrevSelect;
@@ -1,8 +1,10 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { CaretLeftOutlined, CaretRightOutlined } from "@ant-design/icons";
3
- import { Button, ConfigProvider, Flex, Form, Select } from "antd";
4
- import { useEffect, useState } from "react";
3
+ import { Button, ConfigProvider, Flex, Form, Select, theme, Typography } from "antd";
4
+ import { useEffect } from "react";
5
5
  import { list_to_options } from '../Control/Control';
6
+ import { useSearchParamsState } from '../../utils/useSearchParamsState';
7
+ const { Text } = Typography;
6
8
  // Update field and trigger form OnValueChange, thanks to : https://github.com/ant-design/ant-design/issues/23782#issuecomment-2114700558
7
9
  const updateFieldValue = (form, name, value) => {
8
10
  form.getInternalHooks('RC_FORM_INTERNAL_HOOKS').dispatch({
@@ -23,20 +25,22 @@ const style_button_right = {
23
25
  borderBottomRightRadius: 4,
24
26
  borderLeft: 0,
25
27
  };
26
- const NextPrevSelect = ({ name, options: input_options = [], style, value, defaultValue, onChange, reverse = false, arrows = true, ...rest }) => {
27
- const [current_value, setCurrent_value] = useState(value);
28
+ const NextPrevSelect = ({ name = 'sansnom', options: input_options = [], style, value, defaultValue, onChange, reverse = false, arrows = true, label, ...rest }) => {
29
+ const [current_value, setCurrent_value] = useSearchParamsState(name, String(defaultValue) || '');
30
+ const { token } = theme.useToken();
28
31
  const form = Form.useFormInstance();
32
+ const labelApplied = label ?? name;
29
33
  useEffect(() => {
30
- value && handleChange(value);
31
- }, [value]);
34
+ current_value && handleChange(current_value);
35
+ }, [value, current_value]);
32
36
  const options = list_to_options(input_options);
33
37
  const current_index = options?.findIndex((o) => o.value == form?.getFieldValue(name) || o.value == current_value);
34
- const next = () => reverse
38
+ const next = () => String(reverse
35
39
  ? options[current_index - 1].value
36
- : options[current_index + 1].value;
37
- const previous = () => reverse
40
+ : options[current_index + 1].value);
41
+ const previous = () => String(reverse
38
42
  ? options[current_index + 1].value
39
- : options[current_index - 1].value;
43
+ : options[current_index - 1].value);
40
44
  const isFirst = () => reverse ? current_index == options.length - 1 : current_index == 0;
41
45
  const isLast = () => reverse ? current_index == 0 : current_index == options.length - 1;
42
46
  const handleChange = (v) => {
@@ -44,6 +48,6 @@ const NextPrevSelect = ({ name, options: input_options = [], style, value, defau
44
48
  name && form && updateFieldValue(form, name, v);
45
49
  onChange && onChange(v);
46
50
  };
47
- return (_jsxs(Flex, { style: style, name: name, children: [arrows && _jsx(Button, { style: style_button_left, onClick: () => handleChange(previous()), disabled: isFirst(), children: _jsx(CaretLeftOutlined, {}) }), _jsxs(ConfigProvider, { theme: arrows ? { components: { Select: { borderRadius: 0, }, }, } : undefined, children: [" ", _jsx(Form.Item, { name: name, label: name, noStyle: arrows, initialValue: defaultValue, shouldUpdate: true, children: _jsx(Select, { className: "nextPrevSelect", options: options, style: { ...style }, value: current_value, defaultValue: defaultValue, onChange: handleChange, ...rest }) })] }), arrows && _jsx(Button, { style: style_button_right, onClick: () => handleChange(next()), disabled: isLast(), children: _jsx(CaretRightOutlined, {}) })] }));
51
+ return (_jsxs(Flex, { style: style, align: 'center', name: name, children: [" ", arrows && _jsxs(Text, { type: 'secondary', style: { color: token.colorTextLabel }, children: [" ", labelApplied, "\u00A0:\u00A0"] }), arrows && _jsx(Button, { style: style_button_left, onClick: () => handleChange(previous()), disabled: isFirst(), children: _jsx(CaretLeftOutlined, {}) }), _jsxs(ConfigProvider, { theme: arrows ? { components: { Select: { borderRadius: 0, }, }, } : undefined, children: [" ", _jsx(Form.Item, { name: name, label: labelApplied, noStyle: arrows, initialValue: defaultValue, shouldUpdate: true, children: _jsx(Select, { className: "nextPrevSelect", options: options, style: { ...style }, value: current_value, defaultValue: defaultValue, onChange: handleChange, ...rest }) })] }), arrows && _jsx(Button, { style: style_button_right, onClick: () => handleChange(next()), disabled: isLast(), children: _jsx(CaretRightOutlined, {}) })] }));
48
52
  };
49
53
  export default NextPrevSelect;
@@ -1,7 +1,7 @@
1
1
  import { DSL_DashboardPage as Dashboard } from "../components/DashboardPage/Page";
2
2
  import { DSL_Dataset as Dataset } from "../components/Dataset/Dataset";
3
3
  import { DSL_DataPreview as DataPreview } from "../components/Dataset/DataPreview";
4
- import { DSL_Transform as Transform } from "../components/Dataset/Transform";
4
+ import { Transform } from "../components/Dataset/Transform";
5
5
  import { DSL_Filter as Filter } from "../components/Dataset/Filter";
6
6
  import { Provider } from "../components/Dataset/Provider";
7
7
  import { useAllDatasets, useDataset, useDatasets } from "../components/Dataset/hooks";
package/dist/dsl/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { DSL_DashboardPage as Dashboard } from "../components/DashboardPage/Page";
2
2
  import { DSL_Dataset as Dataset } from "../components/Dataset/Dataset";
3
3
  import { DSL_DataPreview as DataPreview } from "../components/Dataset/DataPreview";
4
- import { DSL_Transform as Transform } from "../components/Dataset/Transform";
4
+ import { Transform } from "../components/Dataset/Transform";
5
5
  import { DSL_Filter as Filter } from "../components/Dataset/Filter";
6
6
  import { Provider } from "../components/Dataset/Provider";
7
7
  import { useAllDatasets, useDataset, useDatasets } from "../components/Dataset/hooks";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geo2france/api-dashboard",
3
- "version": "1.16.0",
3
+ "version": "1.17.0",
4
4
  "private": false,
5
5
  "description": "Build dashboards with JSX/TSX",
6
6
  "main": "dist/index.js",