dce-reactkit 3.5.3 → 3.5.5

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.
@@ -44,6 +44,11 @@ export declare const confirm: (title: string, text: string, opts?: {
44
44
  * @param [errorTitle] title of the error box
45
45
  */
46
46
  export declare const showFatalError: (error: any, errorTitle?: string) => undefined;
47
+ /**
48
+ * Add a handler for when a fatal error occurs
49
+ * @author Gabe Abrams
50
+ */
51
+ export declare const addFatalErrorHandler: (handler: () => void) => void;
47
52
  /**
48
53
  * Show the "session expired" message
49
54
  * @author Gabe Abrams
@@ -0,0 +1,20 @@
1
+ /**
2
+ * A toggle switch that toggles on or off
3
+ * @author Alessandra De Lucas
4
+ * @author Gabe Abrams
5
+ */
6
+ import React from 'react';
7
+ import Variant from '../types/Variant';
8
+ type Props = {
9
+ isOn: boolean;
10
+ /**
11
+ * A handler to call when the switch is toggled
12
+ * @param isOn Updated value for isOn
13
+ */
14
+ onToggle: (isOn: boolean) => void;
15
+ id?: string;
16
+ description: string;
17
+ backgroundVariantWhenOn?: Variant;
18
+ };
19
+ declare const ToggleSwitch: React.FC<Props>;
20
+ export default ToggleSwitch;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Simple tooltip component
3
+ * @author Gabe Abrams
4
+ */
5
+ import React from 'react';
6
+ import { IconProp } from '@fortawesome/fontawesome-svg-core';
7
+ type Props = {
8
+ icon?: IconProp;
9
+ text: string;
10
+ children: React.ReactNode;
11
+ wide?: boolean;
12
+ thin?: boolean;
13
+ };
14
+ declare const Tooltip: React.FC<Props>;
15
+ export default Tooltip;
@@ -1,4 +1,4 @@
1
- import AppWrapper, { alert, confirm, showFatalError } from './components/AppWrapper';
1
+ import AppWrapper, { alert, confirm, showFatalError, addFatalErrorHandler } from './components/AppWrapper';
2
2
  import LoadingSpinner from './components/LoadingSpinner';
3
3
  import ErrorBox from './components/ErrorBox';
4
4
  import Modal from './components/Modal';
@@ -17,6 +17,8 @@ import LogReviewer from './components/LogReviewer';
17
17
  import IntelliTable from './components/IntelliTable';
18
18
  import CSVDownloadButton from './components/CSVDownloadButton';
19
19
  import DBEntryManagerPanel from './components/DBEntryManagerPanel';
20
+ import Tooltip from './components/Tooltip';
21
+ import ToggleSwitch from './components/ToggleSwitch';
20
22
  import ErrorWithCode from './errors/ErrorWithCode';
21
23
  import MINUTE_IN_MS from './constants/MINUTE_IN_MS';
22
24
  import HOUR_IN_MS from './constants/HOUR_IN_MS';
@@ -79,4 +81,4 @@ import PickableItem from './components/ItemPicker/types/PickableItem';
79
81
  import DBEntry from './components/DBEntryManagerPanel/types/DBEntry';
80
82
  import DBEntryField from './components/DBEntryManagerPanel/types/DBEntryField';
81
83
  import DBEntryFieldType from './components/DBEntryManagerPanel/types/DBEntryFieldType';
82
- export { AppWrapper, LoadingSpinner, ErrorBox, Modal, TabBox, RadioButton, CheckboxButton, ButtonInputGroup, SimpleDateChooser, Drawer, PopSuccessMark, PopFailureMark, PopPendingMark, CopiableBox, ItemPicker, LogReviewer, IntelliTable, CSVDownloadButton, DBEntryManagerPanel, alert, confirm, showFatalError, ErrorWithCode, MINUTE_IN_MS, HOUR_IN_MS, DAY_IN_MS, DynamicWord, abbreviate, avg, ceilToNumDecimals, floorToNumDecimals, forceNumIntoBounds, padDecimalZeros, padZerosLeft, roundToNumDecimals, sum, waitMs, getOrdinal, getTimeInfoInET, stubServerEndpoint, startMinWait, getHumanReadableDate, getPartOfDay, stringsToHumanReadableList, onlyKeepLetters, parallelLimit, getMonthName, genCSV, canReviewLogs, isMobileOrTablet, extractProp, compareArraysByProp, genCommaList, validateEmail, validatePhoneNumber, validateString, getLocalTimeInfo, initClient, visitServerEndpoint, logClientEvent, initServer, genRouteHandler, handleError, handleSuccess, initLogCollection, addDBEditorEndpoints, ModalButtonType, ModalSize, ModalType, ReactKitErrorCode, Variant, DayOfWeek, Log, LogType, LogSource, LogAction, LogBuiltInMetadata, LogMetadataType, IntelliTableColumn, PickableItem, DBEntry, DBEntryField, DBEntryFieldType, ParamType, };
84
+ export { AppWrapper, LoadingSpinner, ErrorBox, Modal, TabBox, RadioButton, CheckboxButton, ButtonInputGroup, SimpleDateChooser, Drawer, PopSuccessMark, PopFailureMark, PopPendingMark, CopiableBox, ItemPicker, LogReviewer, IntelliTable, CSVDownloadButton, DBEntryManagerPanel, Tooltip, ToggleSwitch, alert, confirm, showFatalError, ErrorWithCode, MINUTE_IN_MS, HOUR_IN_MS, DAY_IN_MS, DynamicWord, abbreviate, avg, ceilToNumDecimals, floorToNumDecimals, forceNumIntoBounds, padDecimalZeros, padZerosLeft, roundToNumDecimals, sum, waitMs, getOrdinal, getTimeInfoInET, stubServerEndpoint, startMinWait, getHumanReadableDate, getPartOfDay, stringsToHumanReadableList, onlyKeepLetters, parallelLimit, getMonthName, genCSV, canReviewLogs, isMobileOrTablet, extractProp, compareArraysByProp, genCommaList, validateEmail, validatePhoneNumber, validateString, getLocalTimeInfo, initClient, visitServerEndpoint, logClientEvent, addFatalErrorHandler, initServer, genRouteHandler, handleError, handleSuccess, initLogCollection, addDBEditorEndpoints, ModalButtonType, ModalSize, ModalType, ReactKitErrorCode, Variant, DayOfWeek, Log, LogType, LogSource, LogAction, LogBuiltInMetadata, LogMetadataType, IntelliTableColumn, PickableItem, DBEntry, DBEntryField, DBEntryFieldType, ParamType, };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  /// <reference types="react" />
2
2
  import React from 'react';
3
+ import { IconProp } from '@fortawesome/fontawesome-svg-core';
3
4
  import express from 'express';
4
5
 
5
6
  /**
@@ -23,7 +24,7 @@ declare enum Variant {
23
24
  * @author Gabe Abrams
24
25
  */
25
26
 
26
- type Props$h = {
27
+ type Props$j = {
27
28
  children: React.ReactNode;
28
29
  dark?: boolean;
29
30
  };
@@ -62,7 +63,12 @@ declare const confirm: (title: string, text: string, opts?: {
62
63
  * @param [errorTitle] title of the error box
63
64
  */
64
65
  declare const showFatalError: (error: any, errorTitle?: string) => undefined;
65
- declare const AppWrapper: React.FC<Props$h>;
66
+ /**
67
+ * Add a handler for when a fatal error occurs
68
+ * @author Gabe Abrams
69
+ */
70
+ declare const addFatalErrorHandler: (handler: () => void) => void;
71
+ declare const AppWrapper: React.FC<Props$j>;
66
72
 
67
73
  /**
68
74
  * Loading spinner/indicator
@@ -75,12 +81,12 @@ declare const LoadingSpinner: () => JSX.Element;
75
81
  * @author Gabe Abrams
76
82
  */
77
83
 
78
- type Props$g = {
84
+ type Props$i = {
79
85
  error: any;
80
86
  title?: string;
81
87
  onClose?: () => void;
82
88
  };
83
- declare const ErrorBox: React.FC<Props$g>;
89
+ declare const ErrorBox: React.FC<Props$i>;
84
90
 
85
91
  /**
86
92
  * Types of buttons in the modal
@@ -131,7 +137,7 @@ declare enum ModalType {
131
137
  * @author Gabe Abrams
132
138
  */
133
139
 
134
- type Props$f = {
140
+ type Props$h = {
135
141
  type?: ModalType;
136
142
  size?: ModalSize;
137
143
  title?: React.ReactNode;
@@ -160,27 +166,27 @@ type Props$f = {
160
166
  confirmVariant?: Variant;
161
167
  onTopOfOtherModals?: boolean;
162
168
  };
163
- declare const Modal: React.FC<Props$f>;
169
+ declare const Modal: React.FC<Props$h>;
164
170
 
165
171
  /**
166
172
  * A box with a tab on the top that holds buttons and other content
167
173
  * @author Gabe Abrams
168
174
  */
169
175
 
170
- type Props$e = {
176
+ type Props$g = {
171
177
  title: React.ReactNode;
172
178
  children: React.ReactNode;
173
179
  noBottomMargin?: boolean;
174
180
  noBottomPadding?: boolean;
175
181
  };
176
- declare const TabBox: React.FC<Props$e>;
182
+ declare const TabBox: React.FC<Props$g>;
177
183
 
178
184
  /**
179
185
  * A radio selection button
180
186
  * @author Gabe Abrams
181
187
  */
182
188
 
183
- type Props$d = {
189
+ type Props$f = {
184
190
  text: string;
185
191
  onSelected: () => void;
186
192
  ariaLabel: string;
@@ -192,14 +198,14 @@ type Props$d = {
192
198
  unselectedVariant?: Variant;
193
199
  small?: boolean;
194
200
  };
195
- declare const RadioButton: React.FC<Props$d>;
201
+ declare const RadioButton: React.FC<Props$f>;
196
202
 
197
203
  /**
198
204
  * A checkbox button
199
205
  * @author Gabe Abrams
200
206
  */
201
207
 
202
- type Props$c = {
208
+ type Props$e = {
203
209
  text: string;
204
210
  onChanged: (checked: boolean) => void;
205
211
  ariaLabel: string;
@@ -213,28 +219,28 @@ type Props$c = {
213
219
  small?: boolean;
214
220
  dashed?: boolean;
215
221
  };
216
- declare const CheckboxButton: React.FC<Props$c>;
222
+ declare const CheckboxButton: React.FC<Props$e>;
217
223
 
218
224
  /**
219
225
  * Input group with a title and space for buttons
220
226
  * @author Gabe Abrams
221
227
  */
222
228
 
223
- type Props$b = {
229
+ type Props$d = {
224
230
  label: string;
225
231
  minLabelWidth?: string;
226
232
  children: React.ReactNode;
227
233
  className?: string;
228
234
  wrapButtonsAndAddGaps?: boolean;
229
235
  };
230
- declare const ButtonInputGroup: React.FC<Props$b>;
236
+ declare const ButtonInputGroup: React.FC<Props$d>;
231
237
 
232
238
  /**
233
239
  * A very simple, lightweight date chooser
234
240
  * @author Gabe Abrams
235
241
  */
236
242
 
237
- type Props$a = {
243
+ type Props$c = {
238
244
  ariaLabel: string;
239
245
  name: string;
240
246
  month: number;
@@ -250,61 +256,61 @@ type Props$a = {
250
256
  numMonthsToShow?: number;
251
257
  chooseFromPast?: boolean;
252
258
  };
253
- declare const SimpleDateChooser: React.FC<Props$a>;
259
+ declare const SimpleDateChooser: React.FC<Props$c>;
254
260
 
255
261
  /**
256
262
  * Drawer container
257
263
  * @author Gabe Abrams
258
264
  */
259
265
 
260
- type Props$9 = {
266
+ type Props$b = {
261
267
  customBackgroundColor?: string;
262
268
  children: React.ReactNode;
263
269
  };
264
- declare const Drawer: React.FC<Props$9>;
270
+ declare const Drawer: React.FC<Props$b>;
265
271
 
266
272
  /**
267
273
  * Success checkmark that pops into view
268
274
  * @author Gabe Abrams
269
275
  */
270
276
 
271
- type Props$8 = {
277
+ type Props$a = {
272
278
  sizeRem?: number;
273
279
  circleVariant?: string;
274
280
  checkVariant?: string;
275
281
  };
276
- declare const PopSuccessMark: React.FC<Props$8>;
282
+ declare const PopSuccessMark: React.FC<Props$a>;
277
283
 
278
284
  /**
279
285
  * Failure x mark that pops into view
280
286
  * @author Gabe Abrams
281
287
  */
282
288
 
283
- type Props$7 = {
289
+ type Props$9 = {
284
290
  sizeRem?: number;
285
291
  circleVariant?: string;
286
292
  xVariant?: string;
287
293
  };
288
- declare const PopFailureMark: React.FC<Props$7>;
294
+ declare const PopFailureMark: React.FC<Props$9>;
289
295
 
290
296
  /**
291
297
  * Failure pending that pops into view
292
298
  * @author Gabe Abrams
293
299
  */
294
300
 
295
- type Props$6 = {
301
+ type Props$8 = {
296
302
  sizeRem?: number;
297
303
  circleVariant?: string;
298
304
  hourglassVariant?: string;
299
305
  };
300
- declare const PopPendingMark: React.FC<Props$6>;
306
+ declare const PopPendingMark: React.FC<Props$8>;
301
307
 
302
308
  /**
303
309
  * Copiable text box
304
310
  * @author Gabe Abrams
305
311
  */
306
312
 
307
- type Props$5 = {
313
+ type Props$7 = {
308
314
  text: string;
309
315
  maxTextWidthRem?: number;
310
316
  label?: string;
@@ -316,7 +322,7 @@ type Props$5 = {
316
322
  textAreaId?: string;
317
323
  copyButtonId?: string;
318
324
  };
319
- declare const CopiableBox: React.FC<Props$5>;
325
+ declare const CopiableBox: React.FC<Props$7>;
320
326
 
321
327
  /**
322
328
  * An item that can be chosen (for use within ItemPicker)
@@ -339,7 +345,7 @@ type PickableItem = ({
339
345
  * @author Yuen Ler Chow
340
346
  */
341
347
 
342
- type Props$4 = {
348
+ type Props$6 = {
343
349
  title: string;
344
350
  items: PickableItem[];
345
351
  /**
@@ -350,7 +356,7 @@ type Props$4 = {
350
356
  onChanged: (updatedItems: PickableItem[]) => void;
351
357
  noBottomMargin?: boolean;
352
358
  };
353
- declare const ItemPicker: React.FC<Props$4>;
359
+ declare const ItemPicker: React.FC<Props$6>;
354
360
 
355
361
  /**
356
362
  * Type of the context map in a LogMetadata file
@@ -395,11 +401,11 @@ type LogMetadataType = {
395
401
  * @author Gabe Abrams
396
402
  */
397
403
 
398
- type Props$3 = {
404
+ type Props$5 = {
399
405
  LogMetadata: LogMetadataType;
400
406
  onClose: () => void;
401
407
  };
402
- declare const LogReviewer: React.FC<Props$3>;
408
+ declare const LogReviewer: React.FC<Props$5>;
403
409
 
404
410
  /**
405
411
  * Server-side API param types
@@ -434,7 +440,7 @@ type IntelliTableColumn = {
434
440
  * @author Gabe Abrams
435
441
  */
436
442
 
437
- type Props$2 = {
443
+ type Props$4 = {
438
444
  title: string;
439
445
  id: string;
440
446
  data: {
@@ -444,14 +450,14 @@ type Props$2 = {
444
450
  columns: IntelliTableColumn[];
445
451
  csvName?: string;
446
452
  };
447
- declare const IntelliTable: React.FC<Props$2>;
453
+ declare const IntelliTable: React.FC<Props$4>;
448
454
 
449
455
  /**
450
456
  * Button for downloading a csv file
451
457
  * @author Gabe Abrams
452
458
  */
453
459
 
454
- type Props$1 = {
460
+ type Props$3 = {
455
461
  filename: string;
456
462
  csv: string;
457
463
  id?: string;
@@ -463,7 +469,7 @@ type Props$1 = {
463
469
  onClick?: () => void;
464
470
  children?: React.ReactNode;
465
471
  };
466
- declare const CSVDownloadButton: React.FC<Props$1>;
472
+ declare const CSVDownloadButton: React.FC<Props$3>;
467
473
 
468
474
  /**
469
475
  * Generic type for an object
@@ -539,7 +545,7 @@ type DBEntryField = ({
539
545
  * @author Gabe Abrams
540
546
  */
541
547
 
542
- type Props = {
548
+ type Props$2 = {
543
549
  entryFields: DBEntryField[];
544
550
  idPropName: string;
545
551
  titlePropName: string;
@@ -565,7 +571,40 @@ type Props = {
565
571
  [k: string]: any;
566
572
  };
567
573
  };
568
- declare const DBEntryManagerPanel: React.FC<Props>;
574
+ declare const DBEntryManagerPanel: React.FC<Props$2>;
575
+
576
+ /**
577
+ * Simple tooltip component
578
+ * @author Gabe Abrams
579
+ */
580
+
581
+ type Props$1 = {
582
+ icon?: IconProp;
583
+ text: string;
584
+ children: React.ReactNode;
585
+ wide?: boolean;
586
+ thin?: boolean;
587
+ };
588
+ declare const Tooltip: React.FC<Props$1>;
589
+
590
+ /**
591
+ * A toggle switch that toggles on or off
592
+ * @author Alessandra De Lucas
593
+ * @author Gabe Abrams
594
+ */
595
+
596
+ type Props = {
597
+ isOn: boolean;
598
+ /**
599
+ * A handler to call when the switch is toggled
600
+ * @param isOn Updated value for isOn
601
+ */
602
+ onToggle: (isOn: boolean) => void;
603
+ id?: string;
604
+ description: string;
605
+ backgroundVariantWhenOn?: Variant;
606
+ };
607
+ declare const ToggleSwitch: React.FC<Props>;
569
608
 
570
609
  /**
571
610
  * An error with a code
@@ -1344,4 +1383,4 @@ declare const LogBuiltInMetadata: {
1344
1383
  };
1345
1384
  };
1346
1385
 
1347
- export { AppWrapper, ButtonInputGroup, CSVDownloadButton, CheckboxButton, CopiableBox, DAY_IN_MS, DBEntry, DBEntryField, DBEntryFieldType, DBEntryManagerPanel, DayOfWeek, Drawer, DynamicWord, ErrorBox, ErrorWithCode, HOUR_IN_MS, IntelliTable, IntelliTableColumn, ItemPicker, LoadingSpinner, Log, LogAction, LogBuiltInMetadata, LogMetadataType, LogReviewer, LogSource, LogType, MINUTE_IN_MS, Modal, ModalButtonType, ModalSize, ModalType, ParamType, PickableItem, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode, SimpleDateChooser, TabBox, Variant, abbreviate, addDBEditorEndpoints, alert, avg, canReviewLogs, ceilToNumDecimals, compareArraysByProp, confirm, extractProp, floorToNumDecimals, forceNumIntoBounds, genCSV, genCommaList, genRouteHandler, getHumanReadableDate, getLocalTimeInfo, getMonthName, getOrdinal, getPartOfDay, getTimeInfoInET, handleError, handleSuccess, initClient, initLogCollection, initServer, isMobileOrTablet, logClientEvent, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, roundToNumDecimals, showFatalError, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, validateEmail, validatePhoneNumber, validateString, visitServerEndpoint, waitMs };
1386
+ export { AppWrapper, ButtonInputGroup, CSVDownloadButton, CheckboxButton, CopiableBox, DAY_IN_MS, DBEntry, DBEntryField, DBEntryFieldType, DBEntryManagerPanel, DayOfWeek, Drawer, DynamicWord, ErrorBox, ErrorWithCode, HOUR_IN_MS, IntelliTable, IntelliTableColumn, ItemPicker, LoadingSpinner, Log, LogAction, LogBuiltInMetadata, LogMetadataType, LogReviewer, LogSource, LogType, MINUTE_IN_MS, Modal, ModalButtonType, ModalSize, ModalType, ParamType, PickableItem, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode, SimpleDateChooser, TabBox, ToggleSwitch, Tooltip, Variant, abbreviate, addDBEditorEndpoints, addFatalErrorHandler, alert, avg, canReviewLogs, ceilToNumDecimals, compareArraysByProp, confirm, extractProp, floorToNumDecimals, forceNumIntoBounds, genCSV, genCommaList, genRouteHandler, getHumanReadableDate, getLocalTimeInfo, getMonthName, getOrdinal, getPartOfDay, getTimeInfoInET, handleError, handleSuccess, initClient, initLogCollection, initServer, isMobileOrTablet, logClientEvent, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, roundToNumDecimals, showFatalError, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, validateEmail, validatePhoneNumber, validateString, visitServerEndpoint, waitMs };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dce-reactkit",
3
- "version": "3.5.3",
3
+ "version": "3.5.5",
4
4
  "description": "Shared components for Harvard DCE apps",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -29,6 +29,7 @@
29
29
  "peerDependencies": {
30
30
  "@fortawesome/free-regular-svg-icons": "^x.x.x",
31
31
  "@fortawesome/free-solid-svg-icons": "^x.x.x",
32
+ "@fortawesome/fontawesome-svg-core": "^x.x.x",
32
33
  "@fortawesome/react-fontawesome": "^x.x.x",
33
34
  "bootstrap": "^5.x.x",
34
35
  "react": "^x.x.x",
@@ -30,7 +30,7 @@ import {
30
30
  } from '../client/initClient';
31
31
 
32
32
  /*------------------------------------------------------------------------*/
33
- /* Props */
33
+ /* -------------------------------- Props ------------------------------- */
34
34
  /*------------------------------------------------------------------------*/
35
35
 
36
36
  type Props = {
@@ -41,11 +41,11 @@ type Props = {
41
41
  };
42
42
 
43
43
  /*------------------------------------------------------------------------*/
44
- /* Static Helpers */
44
+ /* --------------------------- Static Helpers --------------------------- */
45
45
  /*------------------------------------------------------------------------*/
46
46
 
47
47
  /*----------------------------------------*/
48
- /* Alert */
48
+ /* ---------------- Alert --------------- */
49
49
  /*----------------------------------------*/
50
50
 
51
51
  // Stored copies of setters
@@ -61,6 +61,7 @@ let onAlertClosed: () => void;
61
61
  export const alert = async (title: string, text: string): Promise<undefined> => {
62
62
  // Fallback if alert not available
63
63
  if (!setAlertInfo) {
64
+ // eslint-disable-next-line no-alert
64
65
  window.alert(`${title}\n\n${text}`);
65
66
  return undefined;
66
67
  }
@@ -81,7 +82,7 @@ export const alert = async (title: string, text: string): Promise<undefined> =>
81
82
  };
82
83
 
83
84
  /*----------------------------------------*/
84
- /* Confirm */
85
+ /* --------------- Confirm -------------- */
85
86
  /*----------------------------------------*/
86
87
 
87
88
  // Stored copies of setters
@@ -129,6 +130,7 @@ export const confirm = async (
129
130
  ): Promise<boolean> => {
130
131
  // Fallback if confirm is not available
131
132
  if (!setConfirmInfo) {
133
+ // eslint-disable-next-line no-alert
132
134
  return window.confirm(`${title}\n\n${text}`);
133
135
  }
134
136
 
@@ -136,7 +138,7 @@ export const confirm = async (
136
138
  return new Promise((resolve) => {
137
139
  // Setup handler
138
140
  onConfirmClosed = (confirmed: boolean) => {
139
- resolve(confirmed)
141
+ resolve(confirmed);
140
142
  };
141
143
 
142
144
  // Show the confirm
@@ -149,7 +151,7 @@ export const confirm = async (
149
151
  };
150
152
 
151
153
  /*----------------------------------------*/
152
- /* Fatal Error */
154
+ /* ------------- Fatal Error ------------ */
153
155
  /*----------------------------------------*/
154
156
 
155
157
  // Stored copies of setters
@@ -157,6 +159,9 @@ let setFatalErrorMessage: (message: string) => void;
157
159
  let setFatalErrorCode: (code: string) => void;
158
160
  let setFatalErrorTitle: (title: string) => void;
159
161
 
162
+ // Fatal error listeners
163
+ const fatalErrorHandlers: (() => void)[] = [];
164
+
160
165
  /**
161
166
  * Show a fatal error message
162
167
  * @author Gabe Abrams
@@ -179,6 +184,15 @@ export const showFatalError = (
179
184
  : String(error.code ?? ReactKitErrorCode.NoCode)
180
185
  );
181
186
 
187
+ // Call all fatal error listeners
188
+ try {
189
+ fatalErrorHandlers.forEach((handler) => {
190
+ handler();
191
+ });
192
+ } catch (err) {
193
+ // Ignore listener errors
194
+ }
195
+
182
196
  // Add log
183
197
  logClientEvent({
184
198
  context: LogBuiltInMetadata.Context.ClientFatalError,
@@ -209,8 +223,16 @@ export const showFatalError = (
209
223
  return undefined;
210
224
  };
211
225
 
226
+ /**
227
+ * Add a handler for when a fatal error occurs
228
+ * @author Gabe Abrams
229
+ */
230
+ export const addFatalErrorHandler = (handler: () => void) => {
231
+ fatalErrorHandlers.push(handler);
232
+ };
233
+
212
234
  /*----------------------------------------*/
213
- /* Session Expired */
235
+ /* ----------- Session Expired ---------- */
214
236
  /*----------------------------------------*/
215
237
 
216
238
  // Stored copies of setters
@@ -226,12 +248,12 @@ export const showSessionExpiredMessage = (): undefined => {
226
248
  };
227
249
 
228
250
  /*------------------------------------------------------------------------*/
229
- /* Component */
251
+ /* ------------------------------ Component ----------------------------- */
230
252
  /*------------------------------------------------------------------------*/
231
253
 
232
254
  const AppWrapper: React.FC<Props> = (props: Props): React.ReactElement => {
233
255
  /*------------------------------------------------------------------------*/
234
- /* Setup */
256
+ /* -------------------------------- Setup ------------------------------- */
235
257
  /*------------------------------------------------------------------------*/
236
258
 
237
259
  /* -------------- Props ------------- */
@@ -265,11 +287,11 @@ const AppWrapper: React.FC<Props> = (props: Props): React.ReactElement => {
265
287
  alertInfo,
266
288
  setAlertInfoInner,
267
289
  ] = useState<
268
- undefined
269
- | {
270
- title: string,
271
- text: string,
272
- }
290
+ undefined
291
+ | {
292
+ title: string,
293
+ text: string,
294
+ }
273
295
  >(undefined);
274
296
  setAlertInfo = setAlertInfoInner;
275
297
 
@@ -278,17 +300,17 @@ const AppWrapper: React.FC<Props> = (props: Props): React.ReactElement => {
278
300
  confirmInfo,
279
301
  setConfirmInfoInner,
280
302
  ] = useState<
281
- undefined
282
- | {
283
- title: string,
284
- text: string,
285
- opts: {
286
- confirmButtonText?: string,
287
- confirmButtonVariant?: Variant,
288
- cancelButtonText?: string,
289
- cancelButtonVariant?: Variant,
290
- },
291
- }
303
+ undefined
304
+ | {
305
+ title: string,
306
+ text: string,
307
+ opts: {
308
+ confirmButtonText?: string,
309
+ confirmButtonVariant?: Variant,
310
+ cancelButtonText?: string,
311
+ cancelButtonVariant?: Variant,
312
+ },
313
+ }
292
314
  >(undefined);
293
315
  setConfirmInfo = setConfirmInfoInner;
294
316
 
@@ -300,11 +322,11 @@ const AppWrapper: React.FC<Props> = (props: Props): React.ReactElement => {
300
322
  setSessionHasExpired = setSessionHasExpiredInner;
301
323
 
302
324
  /*------------------------------------------------------------------------*/
303
- /* Render */
325
+ /* ------------------------------- Render ------------------------------- */
304
326
  /*------------------------------------------------------------------------*/
305
327
 
306
328
  /*----------------------------------------*/
307
- /* Modal */
329
+ /* ---------------- Modal --------------- */
308
330
  /*----------------------------------------*/
309
331
 
310
332
  // Modal that may be defined
@@ -358,7 +380,7 @@ const AppWrapper: React.FC<Props> = (props: Props): React.ReactElement => {
358
380
  }
359
381
 
360
382
  /*----------------------------------------*/
361
- /* Views */
383
+ /* ---------------- Views --------------- */
362
384
  /*----------------------------------------*/
363
385
 
364
386
  // Body that will be filled with the current view
@@ -410,11 +432,7 @@ const AppWrapper: React.FC<Props> = (props: Props): React.ReactElement => {
410
432
  /* --------------- App -------------- */
411
433
 
412
434
  if (!body) {
413
- body = (
414
- <>
415
- {children}
416
- </>
417
- );
435
+ body = children;
418
436
  }
419
437
 
420
438
  /*----------------------------------------*/
@@ -434,4 +452,4 @@ const AppWrapper: React.FC<Props> = (props: Props): React.ReactElement => {
434
452
  /*------------------------------------------------------------------------*/
435
453
 
436
454
  // Export component
437
- export default AppWrapper;
455
+ export default AppWrapper;
@@ -6,13 +6,14 @@
6
6
  // Import React
7
7
  import React, { useReducer } from 'react';
8
8
 
9
- // Import other reactkit functions
10
- import { alert, waitMs } from '..';
11
-
12
9
  // Import FontAwesome
13
10
  import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
14
11
  import { faClipboard } from '@fortawesome/free-solid-svg-icons';
15
12
 
13
+ // Import other reactkit functions
14
+ import { alert } from './AppWrapper';
15
+ import waitMs from '../helpers/waitMs';
16
+
16
17
  /*------------------------------------------------------------------------*/
17
18
  /* Types */
18
19
  /*------------------------------------------------------------------------*/
@@ -260,7 +261,7 @@ const CopiableBox: React.FC<Props> = (props) => {
260
261
  />
261
262
  )
262
263
  }
263
-
264
+
264
265
  <button
265
266
  id={copyButtonId}
266
267
  className="btn btn-secondary"