@gridsuite/commons-ui 0.73.1 → 0.75.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 (31) hide show
  1. package/dist/components/filter/constants/FilterConstants.d.ts +2 -2
  2. package/dist/components/filter/constants/FilterConstants.js +2 -2
  3. package/dist/components/filter/criteriaBased/CriteriaBasedFilterEditionDialog.d.ts +4 -4
  4. package/dist/components/filter/criteriaBased/CriteriaBasedFilterEditionDialog.js +7 -9
  5. package/dist/components/filter/expert/ExpertFilterEditionDialog.d.ts +4 -4
  6. package/dist/components/filter/expert/ExpertFilterEditionDialog.js +7 -9
  7. package/dist/components/filter/explicitNaming/ExplicitNamingFilterEditionDialog.d.ts +4 -4
  8. package/dist/components/filter/explicitNaming/ExplicitNamingFilterEditionDialog.js +7 -9
  9. package/dist/components/filter/filter.type.d.ts +6 -7
  10. package/dist/components/filter/index.js +2 -2
  11. package/dist/components/index.d.ts +1 -0
  12. package/dist/components/index.js +10 -2
  13. package/dist/components/notifications/NotificationsProvider.d.ts +6 -0
  14. package/dist/components/notifications/NotificationsProvider.js +54 -0
  15. package/dist/components/notifications/contexts/NotificationsContext.d.ts +23 -0
  16. package/dist/components/notifications/contexts/NotificationsContext.js +14 -0
  17. package/dist/components/notifications/hooks/useListenerManager.d.ts +7 -0
  18. package/dist/components/notifications/hooks/useListenerManager.js +50 -0
  19. package/dist/components/notifications/hooks/useNotificationsListener.d.ts +11 -0
  20. package/dist/components/notifications/hooks/useNotificationsListener.js +33 -0
  21. package/dist/components/notifications/index.d.ts +4 -0
  22. package/dist/components/notifications/index.js +10 -0
  23. package/dist/index.js +10 -2
  24. package/dist/services/appsMetadata.d.ts +1 -1
  25. package/dist/services/appsMetadata.js +2 -2
  26. package/dist/translations/en/networkModificationsEn.d.ts +1 -1
  27. package/dist/translations/en/networkModificationsEn.js +1 -1
  28. package/dist/utils/mapper/getFileIcon.js +3 -1
  29. package/dist/utils/types/elementType.d.ts +2 -1
  30. package/dist/utils/types/elementType.js +1 -0
  31. package/package.json +2 -1
@@ -1,4 +1,4 @@
1
- import { SelectionForCopy } from '../filter.type';
1
+ import { ItemSelectionForCopy } from '../filter.type';
2
2
 
3
3
  export declare const DISTRIBUTION_KEY = "distributionKey";
4
4
  export declare const FilterType: {
@@ -15,4 +15,4 @@ export declare const FilterType: {
15
15
  label: string;
16
16
  };
17
17
  };
18
- export declare const NO_SELECTION_FOR_COPY: SelectionForCopy;
18
+ export declare const NO_ITEM_SELECTION_FOR_COPY: ItemSelectionForCopy;
@@ -4,7 +4,7 @@ const FilterType = {
4
4
  EXPLICIT_NAMING: { id: "IDENTIFIER_LIST", label: "filter.explicitNaming" },
5
5
  EXPERT: { id: "EXPERT", label: "filter.expert" }
6
6
  };
7
- const NO_SELECTION_FOR_COPY = {
7
+ const NO_ITEM_SELECTION_FOR_COPY = {
8
8
  sourceItemUuid: null,
9
9
  nameItem: null,
10
10
  descriptionItem: null,
@@ -15,5 +15,5 @@ const NO_SELECTION_FOR_COPY = {
15
15
  export {
16
16
  DISTRIBUTION_KEY,
17
17
  FilterType,
18
- NO_SELECTION_FOR_COPY
18
+ NO_ITEM_SELECTION_FOR_COPY
19
19
  };
@@ -1,4 +1,4 @@
1
- import { SelectionForCopy } from '../filter.type';
1
+ import { ItemSelectionForCopy } from '../filter.type';
2
2
  import { ElementExistsType } from '../../../utils/types/elementType';
3
3
  import { UUID } from 'crypto';
4
4
 
@@ -10,10 +10,10 @@ export interface CriteriaBasedFilterEditionDialogProps {
10
10
  onClose: () => void;
11
11
  broadcastChannel: BroadcastChannel;
12
12
  getFilterById: (id: string) => Promise<any>;
13
- selectionForCopy: SelectionForCopy;
14
- setSelectionForCopy: (selection: SelectionForCopy) => void;
13
+ itemSelectionForCopy: ItemSelectionForCopy;
14
+ setItemSelectionForCopy: (selection: ItemSelectionForCopy) => void;
15
15
  activeDirectory?: UUID;
16
16
  elementExists?: ElementExistsType;
17
17
  language?: string;
18
18
  }
19
- export declare function CriteriaBasedFilterEditionDialog({ id, name, titleId, open, onClose, broadcastChannel, getFilterById, selectionForCopy, setSelectionForCopy, activeDirectory, elementExists, language, }: Readonly<CriteriaBasedFilterEditionDialogProps>): import("react/jsx-runtime").JSX.Element;
19
+ export declare function CriteriaBasedFilterEditionDialog({ id, name, titleId, open, onClose, broadcastChannel, getFilterById, itemSelectionForCopy, setItemSelectionForCopy, activeDirectory, elementExists, language, }: Readonly<CriteriaBasedFilterEditionDialogProps>): import("react/jsx-runtime").JSX.Element;
@@ -9,7 +9,7 @@ import { FieldConstants } from "../../../utils/constants/fieldConstants.js";
9
9
  import "../../../utils/yupConfig.js";
10
10
  import { CustomMuiDialog } from "../../dialogs/customMuiDialog/CustomMuiDialog.js";
11
11
  import { FilterForm } from "../FilterForm.js";
12
- import { FilterType, NO_SELECTION_FOR_COPY } from "../constants/FilterConstants.js";
12
+ import { FilterType, NO_ITEM_SELECTION_FOR_COPY } from "../constants/FilterConstants.js";
13
13
  import { criteriaBasedFilterSchema } from "./CriteriaBasedFilterForm.js";
14
14
  import { backToFrontTweak, frontToBackTweak } from "./criteriaBasedFilterUtils.js";
15
15
  import * as yup from "yup";
@@ -27,8 +27,8 @@ function CriteriaBasedFilterEditionDialog({
27
27
  onClose,
28
28
  broadcastChannel,
29
29
  getFilterById,
30
- selectionForCopy,
31
- setSelectionForCopy,
30
+ itemSelectionForCopy,
31
+ setItemSelectionForCopy,
32
32
  activeDirectory,
33
33
  elementExists,
34
34
  language
@@ -67,11 +67,9 @@ function CriteriaBasedFilterEditionDialog({
67
67
  const onSubmit = useCallback(
68
68
  (filterForm) => {
69
69
  saveFilter(frontToBackTweak(id, filterForm), filterForm[FieldConstants.NAME]).then(() => {
70
- if (selectionForCopy.sourceItemUuid === id) {
71
- setSelectionForCopy(NO_SELECTION_FOR_COPY);
72
- broadcastChannel.postMessage({
73
- NO_SELECTION_FOR_COPY
74
- });
70
+ if (itemSelectionForCopy.sourceItemUuid === id) {
71
+ setItemSelectionForCopy(NO_ITEM_SELECTION_FOR_COPY);
72
+ broadcastChannel.postMessage({ NO_SELECTION_FOR_COPY: NO_ITEM_SELECTION_FOR_COPY });
75
73
  }
76
74
  }).catch((error) => {
77
75
  snackError({
@@ -79,7 +77,7 @@ function CriteriaBasedFilterEditionDialog({
79
77
  });
80
78
  });
81
79
  },
82
- [broadcastChannel, id, selectionForCopy.sourceItemUuid, snackError, setSelectionForCopy]
80
+ [broadcastChannel, id, itemSelectionForCopy.sourceItemUuid, snackError, setItemSelectionForCopy]
83
81
  );
84
82
  const isDataReady = dataFetchStatus === FetchStatus.FETCH_SUCCESS;
85
83
  return /* @__PURE__ */ jsx(
@@ -1,4 +1,4 @@
1
- import { SelectionForCopy } from '../filter.type';
1
+ import { ItemSelectionForCopy } from '../filter.type';
2
2
  import { ElementExistsType } from '../../../utils/types/elementType';
3
3
  import { UUID } from 'crypto';
4
4
 
@@ -9,13 +9,13 @@ export interface ExpertFilterEditionDialogProps {
9
9
  open: boolean;
10
10
  onClose: () => void;
11
11
  broadcastChannel: BroadcastChannel;
12
- selectionForCopy: SelectionForCopy;
12
+ itemSelectionForCopy: ItemSelectionForCopy;
13
+ setItemSelectionForCopy: (selection: ItemSelectionForCopy) => void;
13
14
  getFilterById: (id: string) => Promise<{
14
15
  [prop: string]: any;
15
16
  }>;
16
- setSelectionForCopy: (selection: SelectionForCopy) => void;
17
17
  activeDirectory?: UUID;
18
18
  elementExists?: ElementExistsType;
19
19
  language?: string;
20
20
  }
21
- export declare function ExpertFilterEditionDialog({ id, name, titleId, open, onClose, broadcastChannel, selectionForCopy, getFilterById, setSelectionForCopy, activeDirectory, elementExists, language, }: ExpertFilterEditionDialogProps): import("react/jsx-runtime").JSX.Element;
21
+ export declare function ExpertFilterEditionDialog({ id, name, titleId, open, onClose, broadcastChannel, itemSelectionForCopy, getFilterById, setItemSelectionForCopy, activeDirectory, elementExists, language, }: Readonly<ExpertFilterEditionDialogProps>): import("react/jsx-runtime").JSX.Element;
@@ -7,7 +7,7 @@ import { FetchStatus } from "../../../utils/constants/fetchStatus.js";
7
7
  import { FieldConstants } from "../../../utils/constants/fieldConstants.js";
8
8
  import "../../../utils/yupConfig.js";
9
9
  import { CustomMuiDialog } from "../../dialogs/customMuiDialog/CustomMuiDialog.js";
10
- import { FilterType, NO_SELECTION_FOR_COPY } from "../constants/FilterConstants.js";
10
+ import { FilterType, NO_ITEM_SELECTION_FOR_COPY } from "../constants/FilterConstants.js";
11
11
  import { FilterForm } from "../FilterForm.js";
12
12
  import { saveExpertFilter } from "../utils/filterApi.js";
13
13
  import { expertFilterSchema, EXPERT_FILTER_QUERY } from "./ExpertFilterForm.js";
@@ -26,9 +26,9 @@ function ExpertFilterEditionDialog({
26
26
  open,
27
27
  onClose,
28
28
  broadcastChannel,
29
- selectionForCopy,
29
+ itemSelectionForCopy,
30
30
  getFilterById,
31
- setSelectionForCopy,
31
+ setItemSelectionForCopy,
32
32
  activeDirectory,
33
33
  elementExists,
34
34
  language
@@ -83,14 +83,12 @@ function ExpertFilterEditionDialog({
83
83
  });
84
84
  }
85
85
  );
86
- if (selectionForCopy.sourceItemUuid === id) {
87
- setSelectionForCopy(NO_SELECTION_FOR_COPY);
88
- broadcastChannel.postMessage({
89
- NO_SELECTION_FOR_COPY
90
- });
86
+ if (itemSelectionForCopy.sourceItemUuid === id) {
87
+ setItemSelectionForCopy(NO_ITEM_SELECTION_FOR_COPY);
88
+ broadcastChannel.postMessage({ NO_SELECTION_FOR_COPY: NO_ITEM_SELECTION_FOR_COPY });
91
89
  }
92
90
  },
93
- [broadcastChannel, id, onClose, selectionForCopy.sourceItemUuid, snackError, setSelectionForCopy]
91
+ [broadcastChannel, id, onClose, itemSelectionForCopy.sourceItemUuid, snackError, setItemSelectionForCopy]
94
92
  );
95
93
  const isDataReady = dataFetchStatus === FetchStatus.FETCH_SUCCESS;
96
94
  return /* @__PURE__ */ jsx(
@@ -1,4 +1,4 @@
1
- import { SelectionForCopy } from '../filter.type';
1
+ import { ItemSelectionForCopy } from '../filter.type';
2
2
  import { ElementExistsType } from '../../../utils/types/elementType';
3
3
  import { UUID } from 'crypto';
4
4
 
@@ -9,11 +9,11 @@ export interface ExplicitNamingFilterEditionDialogProps {
9
9
  open: boolean;
10
10
  onClose: () => void;
11
11
  broadcastChannel: BroadcastChannel;
12
- selectionForCopy: SelectionForCopy;
13
- setSelectionForCopy: (selection: SelectionForCopy) => void;
12
+ itemSelectionForCopy: ItemSelectionForCopy;
13
+ setItemSelectionForCopy: (selection: ItemSelectionForCopy) => void;
14
14
  getFilterById: (id: string) => Promise<any>;
15
15
  activeDirectory?: UUID;
16
16
  elementExists?: ElementExistsType;
17
17
  language?: string;
18
18
  }
19
- export declare function ExplicitNamingFilterEditionDialog({ id, name, titleId, open, onClose, broadcastChannel, selectionForCopy, setSelectionForCopy, getFilterById, activeDirectory, elementExists, language, }: Readonly<ExplicitNamingFilterEditionDialogProps>): import("react/jsx-runtime").JSX.Element;
19
+ export declare function ExplicitNamingFilterEditionDialog({ id, name, titleId, open, onClose, broadcastChannel, itemSelectionForCopy, setItemSelectionForCopy, getFilterById, activeDirectory, elementExists, language, }: Readonly<ExplicitNamingFilterEditionDialogProps>): import("react/jsx-runtime").JSX.Element;
@@ -12,7 +12,7 @@ import { saveExplicitNamingFilter } from "../utils/filterApi.js";
12
12
  import { explicitNamingFilterSchema, FILTER_EQUIPMENTS_ATTRIBUTES } from "./ExplicitNamingFilterForm.js";
13
13
  import { FetchStatus } from "../../../utils/constants/fetchStatus.js";
14
14
  import { FilterForm } from "../FilterForm.js";
15
- import { FilterType, NO_SELECTION_FOR_COPY } from "../constants/FilterConstants.js";
15
+ import { FilterType, NO_ITEM_SELECTION_FOR_COPY } from "../constants/FilterConstants.js";
16
16
  import * as yup from "yup";
17
17
  const formSchema = yup.object().shape({
18
18
  [FieldConstants.NAME]: yup.string().trim().required("nameEmpty"),
@@ -27,8 +27,8 @@ function ExplicitNamingFilterEditionDialog({
27
27
  open,
28
28
  onClose,
29
29
  broadcastChannel,
30
- selectionForCopy,
31
- setSelectionForCopy,
30
+ itemSelectionForCopy,
31
+ setItemSelectionForCopy,
32
32
  getFilterById,
33
33
  activeDirectory,
34
34
  elementExists,
@@ -86,14 +86,12 @@ function ExplicitNamingFilterEditionDialog({
86
86
  },
87
87
  onClose
88
88
  );
89
- if (selectionForCopy.sourceItemUuid === id) {
90
- setSelectionForCopy(NO_SELECTION_FOR_COPY);
91
- broadcastChannel.postMessage({
92
- NO_SELECTION_FOR_COPY
93
- });
89
+ if (itemSelectionForCopy.sourceItemUuid === id) {
90
+ setItemSelectionForCopy(NO_ITEM_SELECTION_FOR_COPY);
91
+ broadcastChannel.postMessage({ NO_SELECTION_FOR_COPY: NO_ITEM_SELECTION_FOR_COPY });
94
92
  }
95
93
  },
96
- [broadcastChannel, id, selectionForCopy, onClose, snackError, setSelectionForCopy]
94
+ [broadcastChannel, id, itemSelectionForCopy, onClose, snackError, setItemSelectionForCopy]
97
95
  );
98
96
  const isDataReady = dataFetchStatus === FetchStatus.FETCH_SUCCESS;
99
97
  return /* @__PURE__ */ jsx(
@@ -1,14 +1,13 @@
1
+ import { UUID } from 'crypto';
2
+
1
3
  /**
2
- * Copyright (c) 2024, RTE (http://www.rte-france.com)
3
- * This Source Code Form is subject to the terms of the Mozilla Public
4
- * License, v. 2.0. If a copy of the MPL was not distributed with this
5
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
+ * Represent an item/object in directories.
6
5
  */
7
- export type SelectionForCopy = {
8
- sourceItemUuid: string | null;
6
+ export type ItemSelectionForCopy = {
7
+ sourceItemUuid: UUID | null;
9
8
  typeItem: string | null;
10
9
  nameItem: string | null;
11
10
  descriptionItem: string | null;
12
- parentDirectoryUuid: string | null;
11
+ parentDirectoryUuid: UUID | null;
13
12
  specificTypeItem: string | null;
14
13
  };
@@ -1,6 +1,6 @@
1
1
  import { FilterCreationDialog } from "./FilterCreationDialog.js";
2
2
  import { FilterForm } from "./FilterForm.js";
3
- import { DISTRIBUTION_KEY, FilterType, NO_SELECTION_FOR_COPY } from "./constants/FilterConstants.js";
3
+ import { DISTRIBUTION_KEY, FilterType, NO_ITEM_SELECTION_FOR_COPY } from "./constants/FilterConstants.js";
4
4
  import { CriteriaBasedFilterEditionDialog } from "./criteriaBased/CriteriaBasedFilterEditionDialog.js";
5
5
  import { CriteriaBasedFilterForm, criteriaBasedFilterEmptyFormData, criteriaBasedFilterSchema } from "./criteriaBased/CriteriaBasedFilterForm.js";
6
6
  import { CriteriaBasedForm } from "./criteriaBased/CriteriaBasedForm.js";
@@ -47,7 +47,7 @@ export {
47
47
  FilterType,
48
48
  FreePropertiesTypes,
49
49
  LOAD_TYPE_OPTIONS,
50
- NO_SELECTION_FOR_COPY,
50
+ NO_ITEM_SELECTION_FOR_COPY,
51
51
  OPERATOR_OPTIONS,
52
52
  OperatorType,
53
53
  PHASE_REGULATION_MODE_OPTIONS,
@@ -20,3 +20,4 @@ export * from './overflowableText';
20
20
  export * from './snackbarProvider';
21
21
  export * from './topBar';
22
22
  export * from './treeViewFinder';
23
+ export * from './notifications';
@@ -25,7 +25,7 @@ import { useElementSearch } from "./elementSearch/hooks/useElementSearch.js";
25
25
  import { TagRenderer } from "./elementSearch/tagRenderer/TagRenderer.js";
26
26
  import { FilterCreationDialog } from "./filter/FilterCreationDialog.js";
27
27
  import { FilterForm } from "./filter/FilterForm.js";
28
- import { DISTRIBUTION_KEY, FilterType, NO_SELECTION_FOR_COPY } from "./filter/constants/FilterConstants.js";
28
+ import { DISTRIBUTION_KEY, FilterType, NO_ITEM_SELECTION_FOR_COPY } from "./filter/constants/FilterConstants.js";
29
29
  import { CriteriaBasedFilterEditionDialog } from "./filter/criteriaBased/CriteriaBasedFilterEditionDialog.js";
30
30
  import { CriteriaBasedFilterForm, criteriaBasedFilterEmptyFormData, criteriaBasedFilterSchema } from "./filter/criteriaBased/CriteriaBasedFilterForm.js";
31
31
  import { CriteriaBasedForm } from "./filter/criteriaBased/CriteriaBasedForm.js";
@@ -104,6 +104,10 @@ import { TopBar } from "./topBar/TopBar.js";
104
104
  import { GridLogo, LogoText } from "./topBar/GridLogo.js";
105
105
  import { AboutDialog } from "./topBar/AboutDialog.js";
106
106
  import { TreeViewFinder, generateTreeViewFinderClass } from "./treeViewFinder/TreeViewFinder.js";
107
+ import { NotificationsProvider } from "./notifications/NotificationsProvider.js";
108
+ import { NotificationsContext } from "./notifications/contexts/NotificationsContext.js";
109
+ import { useNotificationsListener } from "./notifications/hooks/useNotificationsListener.js";
110
+ import { useListenerManager } from "./notifications/hooks/useListenerManager.js";
107
111
  export {
108
112
  AboutDialog,
109
113
  AddButton,
@@ -193,7 +197,9 @@ export {
193
197
  MultipleAutocompleteInput,
194
198
  MultipleSelectionDialog,
195
199
  NAME,
196
- NO_SELECTION_FOR_COPY,
200
+ NO_ITEM_SELECTION_FOR_COPY,
201
+ NotificationsContext,
202
+ NotificationsProvider,
197
203
  NumericEditor,
198
204
  OPERATOR_OPTIONS,
199
205
  OperatorType,
@@ -289,5 +295,7 @@ export {
289
295
  useConvertValue,
290
296
  useCustomFormContext,
291
297
  useElementSearch,
298
+ useListenerManager,
299
+ useNotificationsListener,
292
300
  useValid
293
301
  };
@@ -0,0 +1,6 @@
1
+ import { PropsWithChildren } from 'react';
2
+
3
+ export type NotificationsProviderProps = {
4
+ urls: Record<string, string>;
5
+ };
6
+ export declare function NotificationsProvider({ urls, children }: PropsWithChildren<NotificationsProviderProps>): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,54 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useEffect, useMemo } from "react";
3
+ import ReconnectingWebSocket from "reconnecting-websocket";
4
+ import { NotificationsContext } from "./contexts/NotificationsContext.js";
5
+ import { useListenerManager } from "./hooks/useListenerManager.js";
6
+ const DELAY_BEFORE_WEBSOCKET_CONNECTED = 12e3;
7
+ function NotificationsProvider({ urls, children }) {
8
+ const {
9
+ broadcast: broadcastMessage,
10
+ addListener: addListenerMessage,
11
+ removeListener: removeListenerMessage
12
+ } = useListenerManager(urls);
13
+ const {
14
+ broadcast: broadcastOnReopen,
15
+ addListener: addListenerOnReopen,
16
+ removeListener: removeListenerOnReopen
17
+ } = useListenerManager(urls);
18
+ useEffect(() => {
19
+ const connections = Object.keys(urls).filter((u) => urls[u] != null).map((urlKey) => {
20
+ const rws = new ReconnectingWebSocket(() => urls[urlKey], [], {
21
+ // this option set the minimum duration being connected before reset the retry count to 0
22
+ minUptime: DELAY_BEFORE_WEBSOCKET_CONNECTED
23
+ });
24
+ rws.onmessage = broadcastMessage(urlKey);
25
+ rws.onclose = (event) => {
26
+ console.error(`Unexpected ${urlKey} Notification WebSocket closed`, event);
27
+ };
28
+ rws.onerror = (event) => {
29
+ console.error(`Unexpected ${urlKey} Notification WebSocket error`, event);
30
+ };
31
+ rws.onopen = () => {
32
+ console.info(`${urlKey} Notification Websocket connected`);
33
+ broadcastOnReopen(urlKey);
34
+ };
35
+ return rws;
36
+ });
37
+ return () => {
38
+ connections.forEach((c) => c.close());
39
+ };
40
+ }, [broadcastMessage, broadcastOnReopen, urls]);
41
+ const contextValue = useMemo(
42
+ () => ({
43
+ addListenerEvent: addListenerMessage,
44
+ removeListenerEvent: removeListenerMessage,
45
+ addListenerOnReopen,
46
+ removeListenerOnReopen
47
+ }),
48
+ [addListenerMessage, removeListenerMessage, addListenerOnReopen, removeListenerOnReopen]
49
+ );
50
+ return /* @__PURE__ */ jsx(NotificationsContext.Provider, { value: contextValue, children });
51
+ }
52
+ export {
53
+ NotificationsProvider
54
+ };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Copyright (c) 2024, RTE (http://www.rte-france.com)
3
+ * This Source Code Form is subject to the terms of the Mozilla Public
4
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
5
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
+ */
7
+ /// <reference types="react" />
8
+ export type ListenerEventWS = {
9
+ id: string;
10
+ callback: (event: MessageEvent) => void;
11
+ };
12
+ export type ListenerOnReopen = {
13
+ id: string;
14
+ callback: () => void;
15
+ };
16
+ export type NotificationsContextType = {
17
+ addListenerEvent: (urlKey: string, l: ListenerEventWS) => void;
18
+ removeListenerEvent: (urlKey: string, idListener: string) => void;
19
+ addListenerOnReopen: (urlKey: string, l: ListenerOnReopen) => void;
20
+ removeListenerOnReopen: (urlKey: string, idListener: string) => void;
21
+ };
22
+ export type NotificationsContextRecordType = Record<string, NotificationsContextType>;
23
+ export declare const NotificationsContext: import('react').Context<NotificationsContextType>;
@@ -0,0 +1,14 @@
1
+ import { createContext } from "react";
2
+ const NotificationsContext = createContext({
3
+ addListenerEvent: () => {
4
+ },
5
+ removeListenerEvent: () => {
6
+ },
7
+ addListenerOnReopen: () => {
8
+ },
9
+ removeListenerOnReopen: () => {
10
+ }
11
+ });
12
+ export {
13
+ NotificationsContext
14
+ };
@@ -0,0 +1,7 @@
1
+ import { ListenerEventWS, ListenerOnReopen } from '../contexts/NotificationsContext';
2
+
3
+ export declare const useListenerManager: <TListener extends ListenerEventWS | ListenerOnReopen, TMessage extends MessageEvent<any>>(urls: Record<string, string>) => {
4
+ addListener: (urlKey: string, listener: TListener) => void;
5
+ removeListener: (urlKey: string, id: string) => void;
6
+ broadcast: (urlKey: string) => (event: TMessage) => void;
7
+ };
@@ -0,0 +1,50 @@
1
+ import { useRef, useEffect, useCallback } from "react";
2
+ const useListenerManager = (urls) => {
3
+ const urlsListenersRef = useRef({});
4
+ useEffect(() => {
5
+ urlsListenersRef.current = Object.keys(urls).reduce((acc, urlKey) => {
6
+ acc[urlKey] = urlsListenersRef.current[urlKey] ?? [];
7
+ return acc;
8
+ }, {});
9
+ }, [urls]);
10
+ const addListenerEvent = useCallback((urlKey, listener) => {
11
+ const urlsListeners = urlsListenersRef.current;
12
+ if (urlKey in urlsListeners) {
13
+ urlsListeners[urlKey].push(listener);
14
+ } else {
15
+ urlsListeners[urlKey] = [listener];
16
+ }
17
+ urlsListenersRef.current = urlsListeners;
18
+ }, []);
19
+ const removeListenerEvent = useCallback((urlKey, id) => {
20
+ var _a;
21
+ const listeners = (_a = urlsListenersRef.current) == null ? void 0 : _a[urlKey];
22
+ if (listeners) {
23
+ const newListerners = listeners.filter((l) => l.id !== id);
24
+ urlsListenersRef.current = {
25
+ ...urlsListenersRef.current,
26
+ [urlKey]: newListerners
27
+ };
28
+ }
29
+ }, []);
30
+ const broadcast = useCallback(
31
+ (urlKey) => (event) => {
32
+ var _a;
33
+ const listeners = (_a = urlsListenersRef.current) == null ? void 0 : _a[urlKey];
34
+ if (listeners) {
35
+ listeners.forEach(({ callback }) => {
36
+ callback(event);
37
+ });
38
+ }
39
+ },
40
+ []
41
+ );
42
+ return {
43
+ addListener: addListenerEvent,
44
+ removeListener: removeListenerEvent,
45
+ broadcast
46
+ };
47
+ };
48
+ export {
49
+ useListenerManager
50
+ };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Copyright (c) 2024, RTE (http://www.rte-france.com)
3
+ * This Source Code Form is subject to the terms of the Mozilla Public
4
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
5
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
+ */
7
+ export declare const useNotificationsListener: (listenerKey: string, { listenerCallbackMessage, listenerCallbackOnReopen, propsId, }: {
8
+ listenerCallbackMessage?: ((event: MessageEvent<any>) => void) | undefined;
9
+ listenerCallbackOnReopen?: (() => void) | undefined;
10
+ propsId?: string | undefined;
11
+ }) => void;
@@ -0,0 +1,33 @@
1
+ import { useContext, useEffect } from "react";
2
+ import { v4 } from "uuid";
3
+ import { NotificationsContext } from "../contexts/NotificationsContext.js";
4
+ const useNotificationsListener = (listenerKey, {
5
+ listenerCallbackMessage,
6
+ listenerCallbackOnReopen,
7
+ propsId
8
+ }) => {
9
+ const { addListenerEvent, removeListenerEvent, addListenerOnReopen, removeListenerOnReopen } = useContext(NotificationsContext);
10
+ useEffect(() => {
11
+ const id = propsId ?? v4();
12
+ if (listenerCallbackMessage) {
13
+ addListenerEvent(listenerKey, {
14
+ id,
15
+ callback: listenerCallbackMessage
16
+ });
17
+ }
18
+ return () => removeListenerEvent(listenerKey, id);
19
+ }, [addListenerEvent, removeListenerEvent, listenerKey, listenerCallbackMessage, propsId]);
20
+ useEffect(() => {
21
+ const id = propsId ?? v4();
22
+ if (listenerCallbackOnReopen) {
23
+ addListenerOnReopen(listenerKey, {
24
+ id,
25
+ callback: listenerCallbackOnReopen
26
+ });
27
+ }
28
+ return () => removeListenerOnReopen(listenerKey, id);
29
+ }, [addListenerOnReopen, removeListenerOnReopen, listenerKey, listenerCallbackOnReopen, propsId]);
30
+ };
31
+ export {
32
+ useNotificationsListener
33
+ };
@@ -0,0 +1,4 @@
1
+ export * from './NotificationsProvider';
2
+ export * from './contexts/NotificationsContext';
3
+ export * from './hooks/useNotificationsListener';
4
+ export * from './hooks/useListenerManager';
@@ -0,0 +1,10 @@
1
+ import { NotificationsProvider } from "./NotificationsProvider.js";
2
+ import { NotificationsContext } from "./contexts/NotificationsContext.js";
3
+ import { useNotificationsListener } from "./hooks/useNotificationsListener.js";
4
+ import { useListenerManager } from "./hooks/useListenerManager.js";
5
+ export {
6
+ NotificationsContext,
7
+ NotificationsProvider,
8
+ useListenerManager,
9
+ useNotificationsListener
10
+ };
package/dist/index.js CHANGED
@@ -26,7 +26,7 @@ import { useElementSearch } from "./components/elementSearch/hooks/useElementSea
26
26
  import { TagRenderer } from "./components/elementSearch/tagRenderer/TagRenderer.js";
27
27
  import { FilterCreationDialog } from "./components/filter/FilterCreationDialog.js";
28
28
  import { FilterForm } from "./components/filter/FilterForm.js";
29
- import { DISTRIBUTION_KEY, FilterType, NO_SELECTION_FOR_COPY } from "./components/filter/constants/FilterConstants.js";
29
+ import { DISTRIBUTION_KEY, FilterType, NO_ITEM_SELECTION_FOR_COPY } from "./components/filter/constants/FilterConstants.js";
30
30
  import { CriteriaBasedFilterEditionDialog } from "./components/filter/criteriaBased/CriteriaBasedFilterEditionDialog.js";
31
31
  import { CriteriaBasedFilterForm, criteriaBasedFilterEmptyFormData, criteriaBasedFilterSchema } from "./components/filter/criteriaBased/CriteriaBasedFilterForm.js";
32
32
  import { CriteriaBasedForm } from "./components/filter/criteriaBased/CriteriaBasedForm.js";
@@ -105,6 +105,10 @@ import { TopBar } from "./components/topBar/TopBar.js";
105
105
  import { GridLogo, LogoText } from "./components/topBar/GridLogo.js";
106
106
  import { AboutDialog } from "./components/topBar/AboutDialog.js";
107
107
  import { TreeViewFinder, generateTreeViewFinderClass } from "./components/treeViewFinder/TreeViewFinder.js";
108
+ import { NotificationsProvider } from "./components/notifications/NotificationsProvider.js";
109
+ import { NotificationsContext } from "./components/notifications/contexts/NotificationsContext.js";
110
+ import { useNotificationsListener } from "./components/notifications/hooks/useNotificationsListener.js";
111
+ import { useListenerManager } from "./components/notifications/hooks/useListenerManager.js";
108
112
  import { useStateBoolean } from "./hooks/customStates/useStateBoolean.js";
109
113
  import { useStateNumber } from "./hooks/customStates/useStateNumber.js";
110
114
  import { useModificationLabelComputer } from "./hooks/useModificationLabelComputer.js";
@@ -290,7 +294,9 @@ export {
290
294
  MultipleAutocompleteInput,
291
295
  MultipleSelectionDialog,
292
296
  NAME,
293
- NO_SELECTION_FOR_COPY,
297
+ NO_ITEM_SELECTION_FOR_COPY,
298
+ NotificationsContext,
299
+ NotificationsProvider,
294
300
  NumericEditor,
295
301
  OPERATOR_OPTIONS,
296
302
  OperatingStatus,
@@ -494,8 +500,10 @@ export {
494
500
  useDebounce,
495
501
  useElementSearch,
496
502
  useIntlRef,
503
+ useListenerManager,
497
504
  useLocalizedCountries,
498
505
  useModificationLabelComputer,
506
+ useNotificationsListener,
499
507
  usePredefinedProperties,
500
508
  usePrevious,
501
509
  useSnackMessage,
@@ -8,7 +8,7 @@ export type Env = {
8
8
  };
9
9
  export declare function fetchEnv(): Promise<Env>;
10
10
  export declare function fetchAppsMetadata(): Promise<Metadata[]>;
11
- export declare const isStudyMetadata: (metadata: Metadata) => metadata is StudyMetadata;
11
+ export declare function isStudyMetadata(metadata: Metadata): metadata is StudyMetadata;
12
12
  export declare function fetchStudyMetadata(): Promise<StudyMetadata>;
13
13
  export declare function fetchFavoriteAndDefaultCountries(): Promise<{
14
14
  favoriteCountries: string[];
@@ -7,9 +7,9 @@ async function fetchAppsMetadata() {
7
7
  const res = await fetch(`${env.appsMetadataServerUrl}/apps-metadata.json`);
8
8
  return res.json();
9
9
  }
10
- const isStudyMetadata = (metadata) => {
10
+ function isStudyMetadata(metadata) {
11
11
  return metadata.name === "Study";
12
- };
12
+ }
13
13
  async function fetchStudyMetadata() {
14
14
  console.info(`Fetching study metadata...`);
15
15
  const studyMetadata = (await fetchAppsMetadata()).filter(isStudyMetadata);
@@ -58,5 +58,5 @@ export declare const networkModificationsEn: {
58
58
  'network_modifications.TABULAR_CREATION': string;
59
59
  'network_modifications.tabular.GENERATOR_CREATION': string;
60
60
  'network_modifications.LCC_CREATION': string;
61
- 'network_modifications.CSPR_CREATION': string;
61
+ 'network_modifications.STATIC_VAR_COMPENSATOR_CREATION': string;
62
62
  };
@@ -52,7 +52,7 @@ const networkModificationsEn = {
52
52
  "network_modifications.TABULAR_CREATION": "Tabular creation - {computedLabel}",
53
53
  "network_modifications.tabular.GENERATOR_CREATION": "generator creations",
54
54
  "network_modifications.LCC_CREATION": "Creating HVDC (LCC) {computedLabel}",
55
- "network_modifications.CSPR_CREATION": "Creating CSPR {computedLabel}"
55
+ "network_modifications.STATIC_VAR_COMPENSATOR_CREATION": "Creating static var compensator {computedLabel}"
56
56
  };
57
57
  export {
58
58
  networkModificationsEn
@@ -1,5 +1,5 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
- import { Calculate, Settings, Article, NoteAlt, OfflineBolt, Photo, PhotoLibrary } from "@mui/icons-material";
2
+ import { TableView, Calculate, Settings, Article, NoteAlt, OfflineBolt, Photo, PhotoLibrary } from "@mui/icons-material";
3
3
  import { ElementType } from "../types/elementType.js";
4
4
  function getFileIcon(type, style) {
5
5
  switch (type) {
@@ -22,6 +22,8 @@ function getFileIcon(type, style) {
22
22
  return /* @__PURE__ */ jsx(Settings, { sx: style });
23
23
  case ElementType.SPREADSHEET_CONFIG:
24
24
  return /* @__PURE__ */ jsx(Calculate, { sx: style });
25
+ case ElementType.SPREADSHEET_CONFIG_COLLECTION:
26
+ return /* @__PURE__ */ jsx(TableView, { sx: style });
25
27
  case ElementType.DIRECTORY:
26
28
  return void 0;
27
29
  default:
@@ -13,6 +13,7 @@ export declare enum ElementType {
13
13
  LOADFLOW_PARAMETERS = "LOADFLOW_PARAMETERS",
14
14
  SENSITIVITY_PARAMETERS = "SENSITIVITY_PARAMETERS",
15
15
  SHORT_CIRCUIT_PARAMETERS = "SHORT_CIRCUIT_PARAMETERS",
16
- SPREADSHEET_CONFIG = "SPREADSHEET_CONFIG"
16
+ SPREADSHEET_CONFIG = "SPREADSHEET_CONFIG",
17
+ SPREADSHEET_CONFIG_COLLECTION = "SPREADSHEET_CONFIG_COLLECTION"
17
18
  }
18
19
  export type ElementExistsType = (directory: UUID, value: string, elementType: ElementType) => Promise<boolean>;
@@ -12,6 +12,7 @@ var ElementType = /* @__PURE__ */ ((ElementType2) => {
12
12
  ElementType2["SENSITIVITY_PARAMETERS"] = "SENSITIVITY_PARAMETERS";
13
13
  ElementType2["SHORT_CIRCUIT_PARAMETERS"] = "SHORT_CIRCUIT_PARAMETERS";
14
14
  ElementType2["SPREADSHEET_CONFIG"] = "SPREADSHEET_CONFIG";
15
+ ElementType2["SPREADSHEET_CONFIG_COLLECTION"] = "SPREADSHEET_CONFIG_COLLECTION";
15
16
  return ElementType2;
16
17
  })(ElementType || {});
17
18
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gridsuite/commons-ui",
3
- "version": "0.73.1",
3
+ "version": "0.75.0",
4
4
  "description": "common react components for gridsuite applications",
5
5
  "engines": {
6
6
  "npm": ">=9",
@@ -43,6 +43,7 @@
43
43
  "react-dnd-html5-backend": "^16.0.1",
44
44
  "react-querybuilder": "^7.2.0",
45
45
  "react-virtualized": "^9.22.5",
46
+ "reconnecting-websocket": "^4.4.0",
46
47
  "uuid": "^9.0.1"
47
48
  },
48
49
  "peerDependencies": {