@axa-fr/slimfaas-planet-saver 0.37.0-pr.1061102 → 0.37.0-pr.1111153

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/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ![SlimFaas.png](https://github.com/AxaFrance/SlimFaas/blob/main/documentation/SlimFaas.png)
6
6
 
7
- A Vanilla JS project to save the planet. SlimFaas (https://github.com/SlimPlanet/slimfaas) is the slimest and simplest Function As A Service on Kubernetes.
7
+ A Vanilla JS project to save the planet. SlimFaas (https://github.com/SlimPlanet/slimfaas) is the slimmest and simplest Function As A Service on Kubernetes.
8
8
  It works as a proxy that you can be deployed in your namespace.
9
9
 
10
10
  SlimFaas API can give to the frontend information about the infrastructure state. **It is a mind changer !**
@@ -12,7 +12,7 @@ SlimFaas API can give to the frontend information about the infrastructure state
12
12
  **Why?**
13
13
 
14
14
  Because in production instead of setting up 2 replicas of your API backend, you can set up 0 replicas and use an UX that will show the user that the backend is down instead !
15
- **@axa-fr/slimfaas-planet-saver** is here to for doing that easily.
15
+ **@axa-fr/slimfaas-planet-saver** is here to for doing that easy.
16
16
 
17
17
  ![SlimFaasPlanetSaver.gif](https://github.com/AxaFrance/SlimFaas/blob/main/documentation/SlimfaasPlanetSaver.gif)
18
18
 
@@ -25,9 +25,9 @@ npm install @axa-fr/slimfaas-planet-saver
25
25
  Example usage with react :
26
26
  ```javascript
27
27
  import React, { useState, useEffect, useRef } from 'react';
28
- import { SlimFaasPlanetSaver } from "@axa-fr/slimfaas-planet-saver";
28
+ import { SlimFaasPlanetSaver } from '@axa-fr/slimfaas-planet-saver';
29
29
 
30
- const PlanetSaver = ({ children, baseUrl, fetch }) => {
30
+ const PlanetSaver = ({ children, baseUrl, fetch, noActivityTimeout=60000, behavior={} }) => {
31
31
  const [isFirstStart, setIsFirstStart] = useState(true);
32
32
  const environmentStarterRef = useRef(null);
33
33
 
@@ -39,9 +39,18 @@ const PlanetSaver = ({ children, baseUrl, fetch }) => {
39
39
  const instance = new SlimFaasPlanetSaver(baseUrl, {
40
40
  interval: 2000,
41
41
  fetch,
42
+ behavior,
42
43
  updateCallback: (data) => {
43
- const allReady = data.every((item) => item.NumberReady >= 1);
44
- if (allReady && isFirstStart) {
44
+ // Filter only the items that block the UI (WakeUp+BlockUI)
45
+ const blockingItems = data.filter(
46
+ (item) => instance.getBehavior(item.Name) === 'WakeUp+BlockUI'
47
+ );
48
+
49
+ // If all blocking items are ready, set isFirstStart to false
50
+ const allBlockingReady = blockingItems.every(
51
+ (item) => item.NumberReady >= 1
52
+ );
53
+ if (allBlockingReady && isFirstStart) {
45
54
  setIsFirstStart(false);
46
55
  }
47
56
  },
@@ -53,7 +62,8 @@ const PlanetSaver = ({ children, baseUrl, fetch }) => {
53
62
  overlayErrorMessage: 'An error occurred when starting environment. Please contact an administrator.',
54
63
  overlaySecondaryMessage: 'Startup should be fast, but if no machines are available it can take several minutes.',
55
64
  overlayLoadingIcon: '🌍',
56
- overlayErrorSecondaryMessage: 'If the error persists, please contact an administrator.'
65
+ overlayErrorSecondaryMessage: 'If the error persists, please contact an administrator.',
66
+ noActivityTimeout
57
67
  });
58
68
 
59
69
  environmentStarterRef.current = instance;
@@ -78,8 +88,25 @@ const PlanetSaver = ({ children, baseUrl, fetch }) => {
78
88
  export default PlanetSaver;
79
89
 
80
90
 
91
+
92
+ ```
93
+
94
+ ### Usage:
95
+
96
+ ```jsx
97
+ const behavior: {
98
+ "api-speech-to-text": "WakeUp",
99
+ "heavy-pdf-service": "WakeUp+BlockUI",
100
+ "deprecated-service": "None"
101
+ }
102
+
103
+ <PlanetSaver baseUrl="http://slimfaas.mycompany.com" fetch={window.fetch} behavior={behavior}>
104
+ <App />
105
+ </PlanetSaver>
81
106
  ```
82
107
 
108
+ ---
109
+
83
110
  ## Run the demo
84
111
 
85
112
  ```javascript
@@ -88,3 +115,43 @@ cd slimfaas/src/SlimFaasPlanetSaver
88
115
  npm i
89
116
  npm run dev
90
117
  ```
118
+ This will launch a local dev server, letting you see `SlimFaasPlanetSaver` in action.
119
+
120
+ ---
121
+
122
+ ## Configuration Options
123
+ When you create a `new SlimFaasPlanetSaver(baseUrl, options)`, you can provide the following optional properties in the `options` object:
124
+
125
+ | Property | Type | Default | Description |
126
+ |---------------------------|----------------------------------------------------------------------|-------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
127
+ | updateCallback | `(data: any[]) => void` | `() => {}` | Function called after a successful fetch of the functions’ status. The array data includes objects with info about each function, for example: `[{ Name: 'myFunc', NumberReady: 1 }, ...]`. |
128
+ | errorCallback | `(errorMessage: string) => void` | `() => {}` | Function called if an error occurs during the status fetch (e.g., network error). Receives an errorMessage string. |
129
+ | interval | `number` | `5000` | How frequently (in ms) the polling should run. |
130
+ | overlayStartingMessage | `string` | `"🌳 Starting the environment.... 🌳"` | Main message shown on the overlay when the environment is waking up. |
131
+ | overlayNoActivityMessage | `string` | `"Waiting activity to start environment..."` | Message shown if there is no user activity (mouse movement) for too long, but the environment is not ready yet. |
132
+ | overlayErrorMessage | `string` | `"An error occurred while starting the environment."` | Main message shown on the overlay if an error occurs (e.g., network error). |
133
+ | overlaySecondaryMessage | `string` | `"Startup should be fast, but if no machines are available it can take several minutes."` | Secondary message shown on the overlay when the environment is waking up. |
134
+ | overlayErrorSecondaryMessage | `string` | `"If the error persists, please contact an administrator."` | Secondary message shown on the overlay when an error occurs. |
135
+ | overlayLoadingIcon | `string` | `"🌍"` | Text or icon shown on the overlay. By default, it is animated to spin. |
136
+ | noActivityTimeout | `number` | `60000` | How long (in ms) to wait for mouse movement before concluding there is no activity. If no activity is detected, a different overlay message is displayed. |
137
+ | wakeUpTimeout | `number` | `60000` | If a function was recently “woken up,” we’ll skip re-calling wake-up for that function within this timeout window (in ms). |
138
+ | fetch | `typeof fetch` | Global fetch | Custom fetch function if you want to provide your own (e.g., for SSR, or if your environment doesn't have a global fetch). |
139
+ | behavior | `{ [functionName: string]: 'WakeUp+BlockUI' \| 'WakeUp' \| 'None' }` | *Not set; defaults each function to "WakeUp+BlockUI" if unspecified* | Allows you to override how each function is handled: 1. `"WakeUp+BlockUI"`: wakes the function and blocks the UI with the overlay until it’s ready. 2. `"WakeUp"`: wakes without blocking the UI. 3. `"None"`: no wake-up call. |
140
+
141
+ ### Notes on Behavior
142
+ If a function is **not** specified in the behavior map, it defaults to `"WakeUp+BlockUI"`.
143
+
144
+ - `"WakeUp+BlockUI"` means the overlay will be shown until that function is `NumberReady >= 1`.
145
+ - `"WakeUp"` means we attempt to wake up the function, but do not keep the overlay shown specifically for that function.
146
+ - `"None"` means the function will neither be woken up nor block the UI.
147
+
148
+ ### Lifecycle
149
+
150
+ 1. **Initialize**
151
+ Call `instance.initialize()` to create the overlay elements, inject styles, and bind event listeners (e.g., for mouse movement).
152
+
153
+ 2. **Start Polling**
154
+ Call `instance.startPolling()` to begin the periodic checks of the environment. If the environment is not ready, the overlay will appear.
155
+
156
+ 3. **Stop Polling / Cleanup**
157
+ When your component unmounts or you no longer need to monitor the environment, call `instance.cleanup()`. This removes the overlay, styles, and any timers or event listeners.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@axa-fr/slimfaas-planet-saver",
3
3
  "private": false,
4
- "version": "0.37.0-pr.1061102",
4
+ "version": "0.37.0-pr.1111153",
5
5
  "type": "module",
6
6
  "main": "./src/SlimFaasPlanetSaver.js",
7
7
  "module": "./src/SlimFaasPlanetSaver.js",
@@ -1,56 +1,37 @@
1
- declare class SlimFaasPlanetSaver {
2
- private baseUrl: string;
3
- private updateCallback: (data: any) => void;
4
- private errorCallback: (error: any) => void;
5
- private interval: number;
6
- private overlayStartingMessage: string;
7
- private overlayNoActivityMessage: string;
8
- private overlayErrorMessage: string;
9
- private overlaySecondaryMessage: string;
10
- private overlayErrorSecondaryMessage: string;
11
- private overlayLoadingIcon: string;
12
- private noActivityTimeout: number;
13
- private wakeUpTimeout: number;
14
- private fetch: typeof fetch;
15
- private intervalId: number | null;
16
- private isDocumentVisible: boolean;
17
- private overlayElement: HTMLElement | null;
18
- private spanElement: HTMLElement | null;
19
- private styleElement: HTMLElement | null;
20
- private isReady: boolean;
21
- private id: number;
22
- private cleanned: boolean;
23
- private lastMouseMoveTime: number;
24
- private handleMouseMove: () => void;
25
- private handleVisibilityChange: () => void;
1
+ export type BehaviorValue = 'WakeUp+BlockUI' | 'WakeUp' | 'None';
26
2
 
27
- constructor(baseUrl: string, options?: {
28
- updateCallback?: (data: any) => void,
29
- errorCallback?: (error: any) => void,
30
- interval?: number,
31
- overlayStartingMessage?: string,
32
- overlayNoActivityMessage?: string,
33
- overlayErrorMessage?: string,
34
- overlaySecondaryMessage?: string,
35
- overlayErrorSecondaryMessage?: string,
36
- overlayLoadingIcon?: string,
37
- fetch?: typeof fetch
38
- noActivityTimeout?: number
39
- wakeUpTimeout?: number
40
- });
3
+ export interface BehaviorMap {
4
+ [functionName: string]: BehaviorValue;
5
+ }
41
6
 
42
- initialize(): void;
43
- wakeUpPods(data: Array<{ Name: string, NumberReady: number }>): Promise<void>;
44
- fetchStatus(): Promise<void>;
45
- setReadyState(isReady: boolean): void;
46
- startPolling(): void;
47
- stopPolling(): void;
48
- injectStyles(): void;
49
- createOverlay(): void;
50
- showOverlay(): void;
51
- hideOverlay(): void;
52
- updateOverlayMessage(newMessage: string, status?: 'waiting' | 'waiting-action' | 'error', secondaryMessage?: string | null): void;
53
- cleanup(): void;
7
+ export interface SlimFaasPlanetSaverOptions {
8
+ updateCallback?: (data: any[]) => void;
9
+ errorCallback?: (errorMessage: string) => void;
10
+ interval?: number;
11
+ overlayStartingMessage?: string;
12
+ overlayNoActivityMessage?: string;
13
+ overlayErrorMessage?: string;
14
+ overlaySecondaryMessage?: string;
15
+ overlayErrorSecondaryMessage?: string;
16
+ overlayLoadingIcon?: string;
17
+ noActivityTimeout?: number;
18
+ wakeUpTimeout?: number;
19
+ fetch?: typeof fetch;
20
+ behavior?: BehaviorMap;
54
21
  }
55
22
 
56
- export default SlimFaasPlanetSaver;
23
+ export default class SlimFaasPlanetSaver {
24
+ constructor(baseUrl: string, options?: SlimFaasPlanetSaverOptions);
25
+ initialize(): void;
26
+ startPolling(): void;
27
+ stopPolling(): void;
28
+ protected fetchStatus(): Promise<void>;
29
+ protected wakeUpPods(data: any[], lastWakeUpTime: number | null): Promise<boolean>;
30
+ protected setReadyState(isReady: boolean): void;
31
+ protected createOverlay(): void;
32
+ protected injectStyles(): void;
33
+ protected showOverlay(): void;
34
+ protected hideOverlay(): void;
35
+ protected updateOverlayMessage(newMessage: string, status?: string, secondaryMessage?: string | null): void;
36
+ cleanup(): void;
37
+ }
@@ -4,7 +4,7 @@
4
4
  return tempUrl;
5
5
  }
6
6
 
7
- let id =1;
7
+ let id = 1;
8
8
 
9
9
  export default class SlimFaasPlanetSaver {
10
10
  constructor(baseUrl, options = {}) {
@@ -21,6 +21,11 @@ export default class SlimFaasPlanetSaver {
21
21
  this.noActivityTimeout = options.noActivityTimeout || 60000;
22
22
  this.wakeUpTimeout = options.wakeUpTimeout || 60000;
23
23
  this.fetch = options.fetch || fetch;
24
+
25
+ // Ajout de la configuration de comportement
26
+ // Les valeurs possibles sont "WakeUp+BlockUI", "WakeUp", "None"
27
+ this.behavior = options.behavior || {};
28
+
24
29
  this.intervalId = null;
25
30
  this.isDocumentVisible = !document.hidden;
26
31
  this.overlayElement = null;
@@ -32,6 +37,14 @@ export default class SlimFaasPlanetSaver {
32
37
  this.lastWakeUpTime = null;
33
38
  }
34
39
 
40
+ /**
41
+ * Retourne le comportement à appliquer pour une fonction donnée
42
+ * S'il n'est pas renseigné, renvoie "WakeUp+BlockUI" par défaut
43
+ */
44
+ getBehavior(name) {
45
+ return this.behavior[name] || 'WakeUp+BlockUI';
46
+ }
47
+
35
48
  initialize() {
36
49
  this.cleanned = false;
37
50
  this.lastMouseMoveTime = Date.now();
@@ -43,8 +56,8 @@ export default class SlimFaasPlanetSaver {
43
56
 
44
57
  this.createOverlay();
45
58
  this.injectStyles();
46
-
47
59
  }
60
+
48
61
  handleMouseMove() {
49
62
  this.lastMouseMoveTime = Date.now();
50
63
  }
@@ -53,11 +66,21 @@ export default class SlimFaasPlanetSaver {
53
66
  this.isDocumentVisible = !document.hidden;
54
67
  }
55
68
 
69
+ /**
70
+ * Appelle le wake-up sur les fonctions dont le comportement n'est pas "None"
71
+ * et qui ne sont pas prêtes (NumberReady === 0), sauf si on a déjà fait
72
+ * un wake-up trop récemment (selon wakeUpTimeout).
73
+ */
56
74
  async wakeUpPods(data, lastWakeUpTime) {
57
75
  const currentTime = Date.now();
58
76
  let isWakeUpCallMade = false;
77
+
78
+ // On évite de rappeler trop souvent la même fonction
59
79
  const shouldFilter = lastWakeUpTime && (currentTime - lastWakeUpTime) <= this.wakeUpTimeout;
80
+
81
+ // On ne fait un wake-up que pour les fonctions dont le comportement est "WakeUp" ou "WakeUp+BlockUI"
60
82
  const wakePromises = data
83
+ .filter((item) => this.getBehavior(item.Name) !== 'None')
61
84
  .filter((item) => item.NumberReady === 0 || !shouldFilter)
62
85
  .map(async (item) => {
63
86
  const response = await this.fetch(`${this.baseUrl}/wake-function/${item.Name}`, {
@@ -66,7 +89,7 @@ export default class SlimFaasPlanetSaver {
66
89
  if (response.status >= 400) {
67
90
  throw new Error(`HTTP Error! status: ${response.status} for function ${item.Name}`);
68
91
  }
69
- isWakeUpCallMade = true
92
+ isWakeUpCallMade = true;
70
93
  return response;
71
94
  });
72
95
 
@@ -79,6 +102,9 @@ export default class SlimFaasPlanetSaver {
79
102
  return isWakeUpCallMade;
80
103
  }
81
104
 
105
+ /**
106
+ * Récupère le status de chaque fonction et met à jour l'UI et l'overlay
107
+ */
82
108
  async fetchStatus() {
83
109
  try {
84
110
  const response = await this.fetch(`${this.baseUrl}/status-functions`);
@@ -87,38 +113,48 @@ export default class SlimFaasPlanetSaver {
87
113
  }
88
114
  const data = await response.json();
89
115
 
90
- const allReady = data.every((item) => item.NumberReady >= 1);
91
- this.setReadyState(allReady);
116
+ // On ne considère comme bloquantes que les fonctions dont le comportement est "WakeUp+BlockUI"
117
+ const blockingItems = data.filter((item) => this.getBehavior(item.Name) === 'WakeUp+BlockUI');
118
+ const allBlockingReady = blockingItems.every((item) => item.NumberReady >= 1);
119
+
120
+ // Si toutes les fonctions "bloquantes" sont prêtes, on estime que c'est "ready".
121
+ this.setReadyState(allBlockingReady);
92
122
 
123
+ // Callback pour l'extérieur
93
124
  this.updateCallback(data);
94
125
 
126
+ // On vérifie l'activité de la souris pour "réveiller" (wakeUp) les fonctions
95
127
  const now = Date.now();
96
- const mouseMovedRecently = now - this.lastMouseMoveTime <= this.noActivityTimeout; // 1 minute
128
+ const mouseMovedRecently = now - this.lastMouseMoveTime <= this.noActivityTimeout;
97
129
 
98
- if (!allReady && this.isDocumentVisible && !mouseMovedRecently) {
130
+ if (!allBlockingReady && this.isDocumentVisible && !mouseMovedRecently) {
131
+ // Pas de mouvement de souris, document visible => message "no activity"
99
132
  this.updateOverlayMessage(this.overlayNoActivityMessage, 'waiting-action');
100
- } else if (mouseMovedRecently && this.isDocumentVisible) {
101
- if(!allReady) {
133
+ } else if (mouseMovedRecently) {
134
+ // Il y a une activité de souris
135
+ if (!allBlockingReady && this.isDocumentVisible) {
102
136
  this.updateOverlayMessage(this.overlayStartingMessage, 'waiting');
103
137
  }
104
- if(!this.lastWakeUpTime) {
138
+ if (!this.lastWakeUpTime) {
105
139
  this.lastWakeUpTime = Date.now();
106
140
  }
107
141
  const isWakeUpCallMade = await this.wakeUpPods(data, this.lastWakeUpTime);
108
- if(isWakeUpCallMade) {
142
+ if (isWakeUpCallMade) {
109
143
  this.lastWakeUpTime = Date.now();
110
144
  }
111
- } else if(!this.isDocumentVisible && !allReady) {
145
+ } else if (!this.isDocumentVisible && !allBlockingReady) {
146
+ // Document caché, pas prêt => message "no activity"
112
147
  this.updateOverlayMessage(this.overlayNoActivityMessage, 'waiting');
113
148
  }
114
149
  } catch (error) {
115
150
  const errorMessage = error.message;
151
+ // On garde l'état précédent de readiness en cas d'erreur
116
152
  this.setReadyState(this.isReady);
117
153
  this.updateOverlayMessage(this.overlayErrorMessage, 'error', this.overlayErrorSecondaryMessage);
118
154
  this.errorCallback(errorMessage);
119
155
  console.error('Error fetching slimfaas data:', errorMessage);
120
156
  } finally {
121
- if(this.intervalId) {
157
+ if (this.intervalId) {
122
158
  this.intervalId = setTimeout(() => {
123
159
  this.fetchStatus();
124
160
  }, this.interval);
@@ -126,6 +162,9 @@ export default class SlimFaasPlanetSaver {
126
162
  }
127
163
  }
128
164
 
165
+ /**
166
+ * Met à jour l'état ready. Si ready = true => on cache l'overlay
167
+ */
129
168
  setReadyState(isReady) {
130
169
  this.isReady = isReady;
131
170
  if (isReady) {
@@ -135,16 +174,20 @@ export default class SlimFaasPlanetSaver {
135
174
  }
136
175
  }
137
176
 
177
+ /**
178
+ * Lance le polling régulier
179
+ */
138
180
  startPolling() {
139
181
  if (this.intervalId || !this.baseUrl || this.cleanned) return;
140
-
141
182
  this.fetchStatus();
142
-
143
183
  this.intervalId = setTimeout(() => {
144
184
  this.fetchStatus();
145
185
  }, this.interval);
146
186
  }
147
187
 
188
+ /**
189
+ * Arrête le polling
190
+ */
148
191
  stopPolling() {
149
192
  if (this.intervalId) {
150
193
  clearTimeout(this.intervalId);
@@ -152,6 +195,9 @@ export default class SlimFaasPlanetSaver {
152
195
  }
153
196
  }
154
197
 
198
+ /**
199
+ * Injecte dans le DOM le style pour l'overlay
200
+ */
155
201
  injectStyles() {
156
202
  const cssString = `
157
203
  .slimfaas-environment-overlay {
@@ -222,48 +268,57 @@ export default class SlimFaasPlanetSaver {
222
268
  document.head.appendChild(this.styleElement);
223
269
  }
224
270
 
271
+ /**
272
+ * Crée dans le DOM l'overlay en lui-même (sans l'ajouter encore)
273
+ */
225
274
  createOverlay() {
226
275
  this.overlayElement = document.createElement('div');
227
276
  this.overlayElement.className = 'slimfaas-environment-overlay';
228
277
  this.overlayElement.id = `slimfaas-environment-overlay-${this.id}`;
229
278
 
230
- // Créer l'élément icône
279
+ // Élément icône
231
280
  this.iconElement = document.createElement('div');
232
281
  this.iconElement.className = 'slimfaas-environment-overlay__icon';
233
282
  this.iconElement.innerText = this.overlayLoadingIcon;
234
283
 
235
- // Créer l'élément du message principal
284
+ // Message principal
236
285
  this.spanElement = document.createElement('span');
237
286
  this.spanElement.className = 'slimfaas-environment-overlay__main-message';
238
287
  this.spanElement.innerHTML = `${this.overlayStartingMessage}`;
239
288
 
240
- // Créer l'élément du message secondaire
289
+ // Message secondaire
241
290
  this.secondarySpanElement = document.createElement('span');
242
291
  this.secondarySpanElement.className = 'slimfaas-environment-overlay__secondary-message';
243
292
  this.secondarySpanElement.innerText = this.overlaySecondaryMessage;
244
293
 
245
- // Ajouter les éléments à l'overlay
294
+ // Ajout à l'overlay
246
295
  this.overlayElement.appendChild(this.iconElement);
247
296
  this.overlayElement.appendChild(this.spanElement);
248
297
  this.overlayElement.appendChild(this.secondarySpanElement);
249
-
250
- // Ne pas ajouter l'overlay au DOM ici
251
- // document.body.appendChild(this.overlayElement);
252
298
  }
253
299
 
300
+ /**
301
+ * Affiche l'overlay dans la page si nécessaire
302
+ */
254
303
  showOverlay() {
255
- if(this.cleanned) return;
304
+ if (this.cleanned) return;
256
305
  if (this.overlayElement && !document.body.contains(this.overlayElement)) {
257
306
  document.body.appendChild(this.overlayElement);
258
307
  }
259
308
  }
260
309
 
310
+ /**
311
+ * Cache l'overlay
312
+ */
261
313
  hideOverlay() {
262
314
  if (this.overlayElement && document.body.contains(this.overlayElement)) {
263
315
  document.body.removeChild(this.overlayElement);
264
316
  }
265
317
  }
266
318
 
319
+ /**
320
+ * Met à jour le message affiché dans l'overlay
321
+ */
267
322
  updateOverlayMessage(newMessage, status = 'waiting', secondaryMessage = null) {
268
323
  if (this.spanElement) {
269
324
  this.spanElement.innerHTML = `${newMessage}`;
@@ -283,6 +338,9 @@ export default class SlimFaasPlanetSaver {
283
338
  }
284
339
  }
285
340
 
341
+ /**
342
+ * Nettoie le composant : retire listeners, styles et overlay
343
+ */
286
344
  cleanup() {
287
345
  this.cleanned = true;
288
346
  this.stopPolling();