@gridsuite/commons-ui 0.74.0 → 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.
@@ -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';
@@ -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,
@@ -194,6 +198,8 @@ export {
194
198
  MultipleSelectionDialog,
195
199
  NAME,
196
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
@@ -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";
@@ -291,6 +295,8 @@ export {
291
295
  MultipleSelectionDialog,
292
296
  NAME,
293
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,
@@ -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.74.0",
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": {