dce-reactkit 2.0.11 → 3.0.0-beta.10

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 (129) hide show
  1. package/dist/cjs/index.js +921 -0
  2. package/dist/cjs/index.js.map +1 -0
  3. package/{lib → dist/cjs/types}/components/AppWrapper.d.ts +9 -4
  4. package/{lib → dist/cjs/types}/components/ErrorBox.d.ts +0 -0
  5. package/{lib → dist/cjs/types}/components/LoadingSpinner.d.ts +1 -0
  6. package/{lib → dist/cjs/types}/components/Modal.d.ts +5 -36
  7. package/{lib → dist/cjs/types}/components/TabBox.d.ts +0 -0
  8. package/{lib → dist/cjs/types}/errors/ErrorWithCode.d.ts +0 -0
  9. package/{lib → dist/cjs/types}/helpers/abbreviate.d.ts +1 -0
  10. package/{lib → dist/cjs/types}/helpers/avg.d.ts +0 -0
  11. package/{lib → dist/cjs/types}/helpers/ceilToNumDecimals.d.ts +0 -0
  12. package/{lib → dist/cjs/types}/helpers/floorToNumDecimals.d.ts +0 -0
  13. package/{lib → dist/cjs/types}/helpers/forceNumIntoBounds.d.ts +0 -0
  14. package/{lib → dist/cjs/types}/helpers/padDecimalZeros.d.ts +0 -0
  15. package/{lib → dist/cjs/types}/helpers/padZerosLeft.d.ts +0 -0
  16. package/{lib → dist/cjs/types}/helpers/roundToNumDecimals.d.ts +0 -0
  17. package/{lib → dist/cjs/types}/helpers/sum.d.ts +0 -0
  18. package/{lib → dist/cjs/types}/helpers/waitMs.d.ts +0 -0
  19. package/dist/cjs/types/index.d.ts +22 -0
  20. package/dist/cjs/types/types/ModalButtonType.d.ts +17 -0
  21. package/dist/cjs/types/types/ModalSize.d.ts +11 -0
  22. package/dist/cjs/types/types/ModalType.d.ts +16 -0
  23. package/{lib → dist/cjs/types}/types/ReactKitErrorCode.d.ts +0 -0
  24. package/{lib → dist/cjs/types}/types/Variant.d.ts +0 -0
  25. package/dist/esm/index.js +890 -0
  26. package/dist/esm/index.js.map +1 -0
  27. package/dist/esm/types/components/AppWrapper.d.ts +40 -0
  28. package/dist/esm/types/components/ErrorBox.d.ts +12 -0
  29. package/dist/esm/types/components/LoadingSpinner.d.ts +7 -0
  30. package/dist/esm/types/components/Modal.d.ts +40 -0
  31. package/dist/esm/types/components/TabBox.d.ts +11 -0
  32. package/dist/esm/types/errors/ErrorWithCode.d.ts +9 -0
  33. package/dist/esm/types/helpers/abbreviate.d.ts +10 -0
  34. package/dist/esm/types/helpers/avg.d.ts +8 -0
  35. package/dist/esm/types/helpers/ceilToNumDecimals.d.ts +9 -0
  36. package/dist/esm/types/helpers/floorToNumDecimals.d.ts +9 -0
  37. package/dist/esm/types/helpers/forceNumIntoBounds.d.ts +10 -0
  38. package/dist/esm/types/helpers/padDecimalZeros.d.ts +10 -0
  39. package/dist/esm/types/helpers/padZerosLeft.d.ts +9 -0
  40. package/dist/esm/types/helpers/roundToNumDecimals.d.ts +9 -0
  41. package/dist/esm/types/helpers/sum.d.ts +8 -0
  42. package/dist/esm/types/helpers/waitMs.d.ts +7 -0
  43. package/dist/esm/types/index.d.ts +22 -0
  44. package/dist/esm/types/types/ModalButtonType.d.ts +17 -0
  45. package/dist/esm/types/types/ModalSize.d.ts +11 -0
  46. package/dist/esm/types/types/ModalType.d.ts +16 -0
  47. package/dist/esm/types/types/ReactKitErrorCode.d.ts +13 -0
  48. package/dist/esm/types/types/Variant.d.ts +15 -0
  49. package/dist/index.d.ts +274 -0
  50. package/package.json +23 -15
  51. package/rollup.config.js +45 -0
  52. package/src/components/AppWrapper.tsx +38 -37
  53. package/src/components/LoadingSpinner.tsx +3 -0
  54. package/src/components/Modal.tsx +86 -173
  55. package/src/helpers/abbreviate.tsx +2 -0
  56. package/src/helpers/padDecimalZeros.tsx +1 -1
  57. package/src/index.ts +61 -0
  58. package/src/types/ModalButtonType.tsx +18 -0
  59. package/src/types/ModalSize.tsx +12 -0
  60. package/src/types/ModalType.tsx +17 -0
  61. package/tsconfig.json +16 -17
  62. package/lib/components/AppWrapper.js +0 -277
  63. package/lib/components/AppWrapper.js.map +0 -1
  64. package/lib/components/ErrorBox.js +0 -65
  65. package/lib/components/ErrorBox.js.map +0 -1
  66. package/lib/components/LoadingSpinner.js +0 -41
  67. package/lib/components/LoadingSpinner.js.map +0 -1
  68. package/lib/components/Modal.js +0 -321
  69. package/lib/components/Modal.js.map +0 -1
  70. package/lib/components/TabBox.js +0 -42
  71. package/lib/components/TabBox.js.map +0 -1
  72. package/lib/errors/ErrorWithCode.js +0 -33
  73. package/lib/errors/ErrorWithCode.js.map +0 -1
  74. package/lib/helpers/abbreviate.js +0 -21
  75. package/lib/helpers/abbreviate.js.map +0 -1
  76. package/lib/helpers/alert.d.ts +0 -2
  77. package/lib/helpers/alert.js +0 -5
  78. package/lib/helpers/alert.js.map +0 -1
  79. package/lib/helpers/avg.js +0 -24
  80. package/lib/helpers/avg.js.map +0 -1
  81. package/lib/helpers/ceilToNumDecimals.js +0 -15
  82. package/lib/helpers/ceilToNumDecimals.js.map +0 -1
  83. package/lib/helpers/confirm.d.ts +0 -2
  84. package/lib/helpers/confirm.js +0 -5
  85. package/lib/helpers/confirm.js.map +0 -1
  86. package/lib/helpers/floorToNumDecimals.js +0 -15
  87. package/lib/helpers/floorToNumDecimals.js.map +0 -1
  88. package/lib/helpers/forceNumIntoBounds.js +0 -15
  89. package/lib/helpers/forceNumIntoBounds.js.map +0 -1
  90. package/lib/helpers/handleError.d.ts +0 -14
  91. package/lib/helpers/handleError.js +0 -54
  92. package/lib/helpers/handleError.js.map +0 -1
  93. package/lib/helpers/handleSuccess.d.ts +0 -8
  94. package/lib/helpers/handleSuccess.js +0 -20
  95. package/lib/helpers/handleSuccess.js.map +0 -1
  96. package/lib/helpers/padDecimalZeros.js +0 -30
  97. package/lib/helpers/padDecimalZeros.js.map +0 -1
  98. package/lib/helpers/padZerosLeft.js +0 -21
  99. package/lib/helpers/padZerosLeft.js.map +0 -1
  100. package/lib/helpers/parseRequest.d.ts +0 -21
  101. package/lib/helpers/parseRequest.js +0 -240
  102. package/lib/helpers/parseRequest.js.map +0 -1
  103. package/lib/helpers/roundToNumDecimals.js +0 -15
  104. package/lib/helpers/roundToNumDecimals.js.map +0 -1
  105. package/lib/helpers/showFatalError.d.ts +0 -2
  106. package/lib/helpers/showFatalError.js +0 -5
  107. package/lib/helpers/showFatalError.js.map +0 -1
  108. package/lib/helpers/sum.js +0 -15
  109. package/lib/helpers/sum.js.map +0 -1
  110. package/lib/helpers/visitServerEndpoint.d.ts +0 -23
  111. package/lib/helpers/visitServerEndpoint.js +0 -135
  112. package/lib/helpers/visitServerEndpoint.js.map +0 -1
  113. package/lib/helpers/waitMs.js +0 -55
  114. package/lib/helpers/waitMs.js.map +0 -1
  115. package/lib/types/ParamType.d.ts +0 -17
  116. package/lib/types/ParamType.js +0 -21
  117. package/lib/types/ParamType.js.map +0 -1
  118. package/lib/types/ReactKitErrorCode.js +0 -18
  119. package/lib/types/ReactKitErrorCode.js.map +0 -1
  120. package/lib/types/Variant.js +0 -19
  121. package/lib/types/Variant.js.map +0 -1
  122. package/src/helpers/alert.tsx +0 -3
  123. package/src/helpers/confirm.tsx +0 -3
  124. package/src/helpers/handleError.ts +0 -65
  125. package/src/helpers/handleSuccess.ts +0 -18
  126. package/src/helpers/parseRequest.ts +0 -299
  127. package/src/helpers/showFatalError.tsx +0 -3
  128. package/src/helpers/visitServerEndpoint.tsx +0 -110
  129. package/src/types/ParamType.ts +0 -18
@@ -0,0 +1,890 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
3
+ import { faExclamationTriangle, faCircle } from '@fortawesome/free-solid-svg-icons';
4
+
5
+ /******************************************************************************
6
+ Copyright (c) Microsoft Corporation.
7
+
8
+ Permission to use, copy, modify, and/or distribute this software for any
9
+ purpose with or without fee is hereby granted.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
12
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
14
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
16
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17
+ PERFORMANCE OF THIS SOFTWARE.
18
+ ***************************************************************************** */
19
+
20
+ function __awaiter(thisArg, _arguments, P, generator) {
21
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
22
+ return new (P || (P = Promise))(function (resolve, reject) {
23
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
24
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
25
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
26
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
27
+ });
28
+ }
29
+
30
+ // Highest error code = DRK2
31
+ /**
32
+ * List of error codes built into the react kit
33
+ * @author Gabe Abrams
34
+ */
35
+ var ReactKitErrorCode;
36
+ (function (ReactKitErrorCode) {
37
+ ReactKitErrorCode["NoResponse"] = "DRK1";
38
+ ReactKitErrorCode["NoCode"] = "DRK2";
39
+ ReactKitErrorCode["SessionExpired"] = "DRK3";
40
+ ReactKitErrorCode["MissingParameter"] = "DRK4";
41
+ ReactKitErrorCode["InvalidParameter"] = "DRK5";
42
+ ReactKitErrorCode["WrongCourse"] = "DRK6";
43
+ })(ReactKitErrorCode || (ReactKitErrorCode = {}));
44
+ var ReactKitErrorCode$1 = ReactKitErrorCode;
45
+
46
+ /**
47
+ * Displays an error
48
+ * @author Gabe Abrams
49
+ */
50
+ /*------------------------------------------------------------------------*/
51
+ /* Component */
52
+ /*------------------------------------------------------------------------*/
53
+ const ErrorBox = (props) => {
54
+ /*------------------------------------------------------------------------*/
55
+ /* Setup */
56
+ /*------------------------------------------------------------------------*/
57
+ var _a;
58
+ /* -------------- Props ------------- */
59
+ const { error, title = 'An Error Occurred', onClose, } = props;
60
+ /*------------------------------------------------------------------------*/
61
+ /* Render */
62
+ /*------------------------------------------------------------------------*/
63
+ // Determine error text
64
+ const errorText = (typeof error === 'string'
65
+ ? error.trim()
66
+ : String(error.message || 'An unknown error occurred. Please contact support.'));
67
+ // Error code box
68
+ let errorCodeBox;
69
+ if (error && error.code) {
70
+ errorCodeBox = (React.createElement("span", null,
71
+ ' ',
72
+ React.createElement("span", { style: {
73
+ backgroundColor: 'white',
74
+ borderRadius: '5px',
75
+ paddingLeft: '3px',
76
+ paddingRight: '3px',
77
+ color: '#DC4150',
78
+ fontVariant: 'small-caps',
79
+ fontSize: '80%',
80
+ whiteSpace: 'nowrap',
81
+ } },
82
+ "code:",
83
+ ' ',
84
+ String((_a = error.code) !== null && _a !== void 0 ? _a : ReactKitErrorCode$1.NoCode).toUpperCase())));
85
+ }
86
+ // Main UI
87
+ return (React.createElement("div", { className: "alert alert-danger text-center", style: {
88
+ maxWidth: '650px',
89
+ margin: 'auto',
90
+ } },
91
+ React.createElement("h4", { className: "mb-1" },
92
+ React.createElement(FontAwesomeIcon, { icon: faExclamationTriangle, className: "me-2" }),
93
+ title),
94
+ React.createElement("div", null,
95
+ errorText,
96
+ errorCodeBox),
97
+ onClose && (React.createElement("div", { className: "mt-2" },
98
+ React.createElement("button", { type: "button", className: "btn btn-light", "aria-label": "dismiss error and close this activity or view", onClick: onClose }, "Close")))));
99
+ };
100
+
101
+ /**
102
+ * Types of buttons in the modal
103
+ * @author Gabe Abrams
104
+ */
105
+ var ModalButtonType;
106
+ (function (ModalButtonType) {
107
+ ModalButtonType["Okay"] = "okay";
108
+ ModalButtonType["Cancel"] = "cancel";
109
+ ModalButtonType["Yes"] = "yes";
110
+ ModalButtonType["No"] = "no";
111
+ ModalButtonType["Abandon"] = "abandon";
112
+ ModalButtonType["GoBack"] = "goBack";
113
+ ModalButtonType["Continue"] = "continue";
114
+ ModalButtonType["ImSure"] = "imSure";
115
+ ModalButtonType["Delete"] = "delete";
116
+ ModalButtonType["Confirm"] = "confirm";
117
+ })(ModalButtonType || (ModalButtonType = {}));
118
+ var ModalButtonType$1 = ModalButtonType;
119
+
120
+ /**
121
+ * Types of modals
122
+ * @author Gabe Abrams
123
+ */
124
+ var ModalType;
125
+ (function (ModalType) {
126
+ ModalType["Okay"] = "okay";
127
+ ModalType["OkayCancel"] = "okay-cancel";
128
+ ModalType["YesNo"] = "yes-no";
129
+ ModalType["YesNoCancel"] = "yes-no-cancel";
130
+ ModalType["AbandonGoBack"] = "abandon-goBack";
131
+ ModalType["ImSureCancel"] = "imSure-cancel";
132
+ ModalType["DeleteCancel"] = "delete-cancel";
133
+ ModalType["ConfirmCancel"] = "confirm-cancel";
134
+ ModalType["NoButtons"] = "-";
135
+ })(ModalType || (ModalType = {}));
136
+ var ModalType$1 = ModalType;
137
+
138
+ /**
139
+ * Wait for a certain number of ms
140
+ * @author Gabe Abrams
141
+ * @param ms number of ms to wait
142
+ */
143
+ const waitMs = (ms = 0) => __awaiter(void 0, void 0, void 0, function* () {
144
+ return new Promise((resolve) => {
145
+ setTimeout(resolve, ms);
146
+ });
147
+ });
148
+
149
+ /**
150
+ * Bootstrap variants
151
+ * @author Gabe Abrams
152
+ */
153
+ var Variant;
154
+ (function (Variant) {
155
+ Variant["Primary"] = "primary";
156
+ Variant["Secondary"] = "secondary";
157
+ Variant["Success"] = "success";
158
+ Variant["Warning"] = "warning";
159
+ Variant["Info"] = "info";
160
+ Variant["Danger"] = "danger";
161
+ Variant["Light"] = "light";
162
+ Variant["Dark"] = "dark";
163
+ })(Variant || (Variant = {}));
164
+ var Variant$1 = Variant;
165
+
166
+ /**
167
+ * Modal sizes
168
+ * @author Gabe Abrams
169
+ */
170
+ var ModalSize;
171
+ (function (ModalSize) {
172
+ ModalSize["Small"] = "sm";
173
+ ModalSize["Medium"] = "md";
174
+ ModalSize["Large"] = "lg";
175
+ ModalSize["ExtraLarge"] = "xl";
176
+ })(ModalSize || (ModalSize = {}));
177
+ var ModalSize$1 = ModalSize;
178
+
179
+ /**
180
+ * A generic popup modal
181
+ * @author Gabe Abrams
182
+ */
183
+ /*------------------------------------------------------------------------*/
184
+ /* Constants */
185
+ /*------------------------------------------------------------------------*/
186
+ // Constants
187
+ const MS_TO_ANIMATE = 400; // Time to animate in/out (defined by bootstrap)
188
+ const MS_ANIMATE_IN_DELAY = 10;
189
+ // Time to wait before animating in (must be >0 or animation won't trigger)
190
+ // Modal type to list of buttons
191
+ const modalTypeToModalButtonTypes = {
192
+ [ModalType$1.Okay]: [
193
+ ModalButtonType$1.Okay,
194
+ ],
195
+ [ModalType$1.OkayCancel]: [
196
+ ModalButtonType$1.Okay,
197
+ ModalButtonType$1.Cancel,
198
+ ],
199
+ [ModalType$1.YesNo]: [
200
+ ModalButtonType$1.Yes,
201
+ ModalButtonType$1.No,
202
+ ],
203
+ [ModalType$1.YesNoCancel]: [
204
+ ModalButtonType$1.Yes,
205
+ ModalButtonType$1.No,
206
+ ModalButtonType$1.Cancel,
207
+ ],
208
+ [ModalType$1.AbandonGoBack]: [
209
+ ModalButtonType$1.Abandon,
210
+ ModalButtonType$1.GoBack,
211
+ ],
212
+ [ModalType$1.ImSureCancel]: [
213
+ ModalButtonType$1.ImSure,
214
+ ModalButtonType$1.Cancel,
215
+ ],
216
+ [ModalType$1.DeleteCancel]: [
217
+ ModalButtonType$1.Delete,
218
+ ModalButtonType$1.Cancel,
219
+ ],
220
+ [ModalType$1.ConfirmCancel]: [
221
+ ModalButtonType$1.Confirm,
222
+ ModalButtonType$1.Cancel,
223
+ ],
224
+ };
225
+ // Button type styling and labels
226
+ const ModalButtonTypeToLabelAndVariant = {
227
+ [ModalButtonType$1.Okay]: {
228
+ label: 'Okay',
229
+ variant: Variant$1.Dark,
230
+ },
231
+ [ModalButtonType$1.Cancel]: {
232
+ label: 'Cancel',
233
+ variant: Variant$1.Secondary,
234
+ },
235
+ [ModalButtonType$1.Yes]: {
236
+ label: 'Yes',
237
+ variant: Variant$1.Dark,
238
+ },
239
+ [ModalButtonType$1.No]: {
240
+ label: 'No',
241
+ variant: Variant$1.Secondary,
242
+ },
243
+ [ModalButtonType$1.Abandon]: {
244
+ label: 'Abandon Changes',
245
+ variant: Variant$1.Warning,
246
+ },
247
+ [ModalButtonType$1.GoBack]: {
248
+ label: 'Go Back',
249
+ variant: Variant$1.Secondary,
250
+ },
251
+ [ModalButtonType$1.Continue]: {
252
+ label: 'Continue',
253
+ variant: Variant$1.Dark,
254
+ },
255
+ [ModalButtonType$1.ImSure]: {
256
+ label: 'I am sure',
257
+ variant: Variant$1.Warning,
258
+ },
259
+ [ModalButtonType$1.Delete]: {
260
+ label: 'Yes, Delete',
261
+ variant: Variant$1.Danger,
262
+ },
263
+ [ModalButtonType$1.Confirm]: {
264
+ label: 'Confirm',
265
+ variant: Variant$1.Dark,
266
+ },
267
+ };
268
+ /*------------------------------------------------------------------------*/
269
+ /* Component */
270
+ /*------------------------------------------------------------------------*/
271
+ const Modal = (props) => {
272
+ /*------------------------------------------------------------------------*/
273
+ /* Setup */
274
+ /*------------------------------------------------------------------------*/
275
+ var _a;
276
+ /* -------------- Props ------------- */
277
+ const { type = ModalType$1.NoButtons, size = ModalSize$1.Large, title, children, onClose, dontAllowBackdropExit, onTopOfOtherModals, } = props;
278
+ /* -------------- State ------------- */
279
+ // If true, the modal is shown
280
+ const [visible, setVisible] = useState(false);
281
+ // True if currently animating in
282
+ const [animatingIn, setAnimatingIn] = useState(false);
283
+ /*------------------------------------------------------------------------*/
284
+ /* Lifecycle Functions */
285
+ /*------------------------------------------------------------------------*/
286
+ /**
287
+ * Mount
288
+ * @author Gabe Abrams
289
+ */
290
+ useEffect(() => {
291
+ (() => __awaiter(void 0, void 0, void 0, function* () {
292
+ // Start the component animating in
293
+ yield waitMs(MS_ANIMATE_IN_DELAY);
294
+ setAnimatingIn(true);
295
+ // Wait and then set visible to true
296
+ yield waitMs(MS_TO_ANIMATE);
297
+ setVisible(true);
298
+ }))();
299
+ }, []);
300
+ /*------------------------------------------------------------------------*/
301
+ /* Component Functions */
302
+ /*------------------------------------------------------------------------*/
303
+ /**
304
+ * Handles the closing of the modal
305
+ * @author Gabe Abrams
306
+ * @param ModalButtonType the button that was clicked when closing the
307
+ * modal
308
+ */
309
+ const handleClose = (ModalButtonType) => __awaiter(void 0, void 0, void 0, function* () {
310
+ // Don't close if no handler
311
+ if (!onClose) {
312
+ return;
313
+ }
314
+ // Don't close if animating in
315
+ if (animatingIn) {
316
+ return;
317
+ }
318
+ // Don't close if already closed
319
+ if (!visible) {
320
+ return;
321
+ }
322
+ // Update the state
323
+ setVisible(false);
324
+ // Call the handler after the modal has animated out
325
+ yield waitMs(MS_TO_ANIMATE);
326
+ onClose(ModalButtonType);
327
+ });
328
+ /*------------------------------------------------------------------------*/
329
+ /* Render */
330
+ /*------------------------------------------------------------------------*/
331
+ /*----------------------------------------*/
332
+ /* Footer */
333
+ /*----------------------------------------*/
334
+ // Get list of buttons for this modal type
335
+ const ModalButtonTypes = (_a = modalTypeToModalButtonTypes[type]) !== null && _a !== void 0 ? _a : [];
336
+ // Create buttons
337
+ const buttons = ModalButtonTypes.map((ModalButtonType, i) => {
338
+ // Get default style
339
+ let { label, variant, } = ModalButtonTypeToLabelAndVariant[ModalButtonType];
340
+ // Override with customizations
341
+ const newLabel = props[`${ModalButtonType}Label`];
342
+ if (newLabel) {
343
+ label = newLabel;
344
+ }
345
+ const newVariant = props[`${ModalButtonType}Variant`];
346
+ if (newVariant) {
347
+ variant = newVariant;
348
+ }
349
+ // Check if this button is last
350
+ const last = (i === ModalButtonTypes.length - 1);
351
+ // Create the button
352
+ return (React.createElement("button", { type: "button", className: `Modal-${ModalButtonType}-button btn btn-${variant} ${last ? '' : 'mr-1'}`, onClick: () => {
353
+ handleClose(ModalButtonType);
354
+ } }, label));
355
+ });
356
+ // Put all buttons in a footer
357
+ const footer = ((buttons && buttons.length)
358
+ ? (React.createElement("div", null, buttons))
359
+ : undefined);
360
+ // Render the modal
361
+ return (React.createElement("div", { className: `modal show modal-dialog-scrollable modal-dialog-centered modal-${size}`, tabIndex: -1, style: {
362
+ zIndex: (onTopOfOtherModals
363
+ ? 6000000000
364
+ : 5000000000),
365
+ display: 'block',
366
+ margin: 'auto',
367
+ } },
368
+ React.createElement("div", { className: "modal-dialog" },
369
+ React.createElement("div", { className: "modal-content" },
370
+ React.createElement("div", { className: "modal-header" },
371
+ React.createElement("h5", { className: "modal-title" }, title),
372
+ onClose && (React.createElement("button", { type: "button", className: "btn-close", "data-bs-dismiss": "modal", "aria-label": "Close", onClick: () => {
373
+ handleClose(ModalButtonType$1.Cancel);
374
+ } }))),
375
+ children && (React.createElement("div", { className: "modal-body" }, children)),
376
+ footer && (React.createElement("div", { className: "modal-footer" }, footer))))));
377
+ };
378
+
379
+ /**
380
+ * An error with a code
381
+ * @author Gabe Abrams
382
+ */
383
+ class ErrorWithCode extends Error {
384
+ constructor(message, code) {
385
+ super(message);
386
+ this.name = 'ErrorWithCode';
387
+ this.code = code;
388
+ }
389
+ }
390
+
391
+ /**
392
+ * A wrapper for the entire React app that adds global functionality like
393
+ * handling for fatal error messages, adds bootstrap support
394
+ * @author Gabe Abrams
395
+ */
396
+ /*------------------------------------------------------------------------*/
397
+ /* Static Helpers */
398
+ /*------------------------------------------------------------------------*/
399
+ /*----------------------------------------*/
400
+ /* Alert */
401
+ /*----------------------------------------*/
402
+ // Stored copies of setters
403
+ let setAlertInfo;
404
+ let onAlertClosed;
405
+ /**
406
+ * Show an alert modal with an "Okay" button
407
+ * @author Gabe Abrams
408
+ * @param title the title text to display at the top of the alert
409
+ * @param text the text to display in the alert
410
+ */
411
+ const alert = (title, text) => __awaiter(void 0, void 0, void 0, function* () {
412
+ // Fallback if alert not available
413
+ if (!setAlertInfo) {
414
+ window.alert(`${title}\n\n${text}`);
415
+ return undefined;
416
+ }
417
+ // Return promise that resolves when alert is closed
418
+ return new Promise((resolve) => {
419
+ // Setup handler
420
+ onAlertClosed = () => {
421
+ resolve(undefined);
422
+ };
423
+ // Show the alert
424
+ setAlertInfo({
425
+ title,
426
+ text,
427
+ });
428
+ });
429
+ });
430
+ /*----------------------------------------*/
431
+ /* Confirm */
432
+ /*----------------------------------------*/
433
+ // Stored copies of setters
434
+ let setConfirmInfo;
435
+ let onConfirmClosed;
436
+ /**
437
+ * Show a confirmation modal with an "Okay" and a "Cancel" button
438
+ * @author Gabe Abrams
439
+ * @param title the title text to display at the top of the alert
440
+ * @param text the text to display in the alert
441
+ * @returns true if the user confirmed
442
+ */
443
+ const confirm = (title, text) => __awaiter(void 0, void 0, void 0, function* () {
444
+ // Fallback if confirm is not available
445
+ if (!setConfirmInfo) {
446
+ return window.confirm(`${title}\n\n${text}`);
447
+ }
448
+ // Return promise that resolves with result of confirmation
449
+ return new Promise((resolve) => {
450
+ // Setup handler
451
+ onConfirmClosed = (confirmed) => {
452
+ resolve(confirmed);
453
+ };
454
+ // Show the confirm
455
+ setConfirmInfo({
456
+ title,
457
+ text,
458
+ });
459
+ });
460
+ });
461
+ /*----------------------------------------*/
462
+ /* Fatal Error */
463
+ /*----------------------------------------*/
464
+ // Stored copies of setters
465
+ let setFatalErrorMessage;
466
+ let setFatalErrorCode;
467
+ let setFatalErrorTitle;
468
+ /**
469
+ * Show a fatal error message
470
+ * @author Gabe Abrams
471
+ * @param error the error to show
472
+ * @param [errorTitle] title of the error box
473
+ */
474
+ const showFatalError = (error, errorTitle = 'An Error Occurred') => {
475
+ var _a, _b;
476
+ // Determine message and code
477
+ const message = (typeof error === 'string'
478
+ ? error.trim()
479
+ : String((_a = error.message) !== null && _a !== void 0 ? _a : 'An unknown error occurred.'));
480
+ const code = (typeof error === 'string'
481
+ ? ReactKitErrorCode$1.NoCode
482
+ : String((_b = error.code) !== null && _b !== void 0 ? _b : ReactKitErrorCode$1.NoCode));
483
+ // Handle case where app hasn't loaded
484
+ if (!setFatalErrorMessage || !setFatalErrorCode) {
485
+ alert(errorTitle, `${message} (code: ${code}). Please contact support.`);
486
+ return undefined;
487
+ }
488
+ // Use setters
489
+ setFatalErrorMessage(message);
490
+ setFatalErrorCode(code);
491
+ setFatalErrorTitle(errorTitle);
492
+ return undefined;
493
+ };
494
+ /*------------------------------------------------------------------------*/
495
+ /* Component */
496
+ /*------------------------------------------------------------------------*/
497
+ const AppWrapper = (props) => {
498
+ /*------------------------------------------------------------------------*/
499
+ /* Setup */
500
+ /*------------------------------------------------------------------------*/
501
+ /* -------------- Props ------------- */
502
+ const { children, dark, sessionExpiredMessage = 'Your session has expired. Please go back to Canvas and start over.', } = props;
503
+ /* -------------- State ------------- */
504
+ // Fatal error
505
+ const [fatalErrorMessage, setFatalErrorMessageInner,] = useState();
506
+ setFatalErrorMessage = setFatalErrorMessageInner;
507
+ const [fatalErrorCode, setFatalErrorCodeInner,] = useState();
508
+ setFatalErrorCode = setFatalErrorCodeInner;
509
+ const [fatalErrorTitle, setFatalErrorTitleInner,] = useState();
510
+ setFatalErrorTitle = setFatalErrorTitleInner;
511
+ // Alert
512
+ const [alertInfo, setAlertInfoInner,] = useState();
513
+ setAlertInfo = setAlertInfoInner;
514
+ // Confirm
515
+ const [confirmInfo, setConfirmInfoInner,] = useState();
516
+ setConfirmInfo = setConfirmInfoInner;
517
+ // Session expired
518
+ const [sessionHasExpired, setSessionHasExpiredInner,] = useState(false);
519
+ /*------------------------------------------------------------------------*/
520
+ /* Render */
521
+ /*------------------------------------------------------------------------*/
522
+ /*----------------------------------------*/
523
+ /* Modal */
524
+ /*----------------------------------------*/
525
+ // Modal that may be defined
526
+ let modal;
527
+ /* -------------- Alert ------------- */
528
+ if (alertInfo) {
529
+ modal = (React.createElement(Modal, { title: alertInfo.title, type: ModalType$1.Okay, onClose: () => {
530
+ // Alert closed
531
+ if (onAlertClosed) {
532
+ onAlertClosed();
533
+ }
534
+ }, onTopOfOtherModals: true }, alertInfo.text));
535
+ }
536
+ /* ------------- Confirm ------------ */
537
+ if (confirmInfo) {
538
+ modal = (React.createElement(Modal, { title: confirmInfo.title, type: ModalType$1.OkayCancel, onClose: (buttonType) => {
539
+ if (onConfirmClosed) {
540
+ onConfirmClosed(buttonType === ModalButtonType$1.Okay);
541
+ }
542
+ }, dontAllowBackdropExit: true }));
543
+ }
544
+ /*----------------------------------------*/
545
+ /* Views */
546
+ /*----------------------------------------*/
547
+ // Body that will be filled with the current view
548
+ let body;
549
+ /* ----------- Fatal Error ---------- */
550
+ if (fatalErrorMessage || fatalErrorCode || sessionHasExpired) {
551
+ // Re-encapsulate in an error
552
+ const error = (sessionHasExpired
553
+ ? new ErrorWithCode((fatalErrorMessage !== null && fatalErrorMessage !== void 0 ? fatalErrorMessage : 'An unknown error has occurred. Please contact support.'), (fatalErrorCode !== null && fatalErrorCode !== void 0 ? fatalErrorCode : ReactKitErrorCode$1.NoCode))
554
+ : new ErrorWithCode(sessionExpiredMessage, ReactKitErrorCode$1.SessionExpired));
555
+ // Build error screen
556
+ body = (React.createElement("div", { style: {
557
+ display: 'block',
558
+ width: '100vw',
559
+ minHeight: '100vh',
560
+ paddingTop: '2rem',
561
+ backgroundColor: (dark
562
+ ? '#222'
563
+ : '#fff'),
564
+ } },
565
+ React.createElement(ErrorBox, { title: (sessionHasExpired
566
+ ? 'Session Expired'
567
+ : fatalErrorTitle), error: error })));
568
+ }
569
+ /* --------------- App -------------- */
570
+ if (!body) {
571
+ body = (React.createElement(React.Fragment, null, children));
572
+ }
573
+ /*----------------------------------------*/
574
+ /* Main UI */
575
+ /*----------------------------------------*/
576
+ return (React.createElement(React.Fragment, null,
577
+ modal,
578
+ body));
579
+ };
580
+
581
+ /**
582
+ * Loading spinner/indicator
583
+ * @author Gabe Abrams
584
+ */
585
+ /*------------------------------------------------------------------------*/
586
+ /* Style */
587
+ /*------------------------------------------------------------------------*/
588
+ const style$1 = `
589
+ /* Blips */
590
+ .LoadingSpinner-blip-1,
591
+ .LoadingSpinner-blip-2,
592
+ .LoadingSpinner-blip-3,
593
+ .LoadingSpinner-blip-4 {
594
+ font-size: 25px;
595
+ opacity: 0.6;
596
+ margin-top: 20px;
597
+ margin-bottom: 20px;
598
+ }
599
+
600
+ /* First Blip */
601
+ .LoadingSpinner-blip-1 {
602
+ animation: LoadingSpinner-pop-animation 2s infinite;
603
+ }
604
+
605
+ /* Second Blip */
606
+ .LoadingSpinner-blip-2 {
607
+ animation: LoadingSpinner-pop-animation 2s infinite;
608
+ -webkit-animation-delay: 0.1s;
609
+ animation-delay: 0.1s;
610
+ }
611
+
612
+ /* Third Blip */
613
+ .LoadingSpinner-blip-3 {
614
+ animation: LoadingSpinner-pop-animation 2s infinite;
615
+ animation-delay: 0.2s;
616
+ }
617
+
618
+ /* Fourth Blip */
619
+ .LoadingSpinner-blip-4 {
620
+ animation: LoadingSpinner-pop-animation 2s infinite;
621
+ animation-delay: 0.3s;
622
+ }
623
+
624
+ /* Pop Animation for Each Blip */
625
+ @keyframes LoadingSpinner-pop-animation {
626
+ 0% {
627
+ transform: scale(1.0);
628
+ }
629
+ 10% {
630
+ transform: scale(1.5);
631
+ }
632
+ 30% {
633
+ transform: scale(1.0);
634
+ }
635
+ 100% {
636
+ transform: scale(1.0);
637
+ }
638
+ }
639
+ `;
640
+ /*------------------------------------------------------------------------*/
641
+ /* Component */
642
+ /*------------------------------------------------------------------------*/
643
+ const LoadingSpinner = () => {
644
+ /*------------------------------------------------------------------------*/
645
+ /* Render */
646
+ /*------------------------------------------------------------------------*/
647
+ // Add all four blips to a container
648
+ return (React.createElement("div", { className: "text-center LoadingSpinner LoadingSpinner-container" },
649
+ React.createElement("style", null, style$1),
650
+ React.createElement(FontAwesomeIcon, { icon: faCircle, className: "LoadingSpinner-blip-1 me-1" }),
651
+ React.createElement(FontAwesomeIcon, { icon: faCircle, className: "LoadingSpinner-blip-2 me-1" }),
652
+ React.createElement(FontAwesomeIcon, { icon: faCircle, className: "LoadingSpinner-blip-3 me-1" }),
653
+ React.createElement(FontAwesomeIcon, { icon: faCircle, className: "LoadingSpinner-blip-4" })));
654
+ };
655
+
656
+ /**
657
+ * A box with a tab on the top that holds buttons and other content
658
+ * @author Gabe Abrams
659
+ */
660
+ /*------------------------------------------------------------------------*/
661
+ /* Style */
662
+ /*------------------------------------------------------------------------*/
663
+ const style = `
664
+ /* Tab Box */
665
+ .TabBox-box {
666
+ /* Light Border */
667
+ border: 2px solid #dedede;
668
+
669
+ /* Rounded Corners (except top-left) */
670
+ border-bottom-right-radius: 5px;
671
+ border-bottom-left-radius: 5px;
672
+ border-top-right-radius: 5px;
673
+
674
+ /* Very Light Gray Border */
675
+ background: #fdfdfd;
676
+
677
+ /* Align Contents on Left */
678
+ text-align: left;
679
+ }
680
+
681
+ /* Container for Title */
682
+ .TabBox-title-container {
683
+ /* Place on Left */
684
+ position: relative;
685
+ left: 0;
686
+ text-align: left;
687
+ }
688
+
689
+ /* Tab-style Title */
690
+ .TabBox-title {
691
+ /* Place so it Barely Overlaps the Box Border */
692
+ display: inline-block;
693
+ position: relative;
694
+ top: 2px; /* Gives Illusion that Border Doesn't Exist Below Tab */
695
+
696
+ /* Title-sized Font */
697
+ font-size: 25px;
698
+
699
+ /* Add Border on Top and Sides */
700
+ border-top: 2px solid #dedede;
701
+ border-left: 2px solid #dedede;
702
+ border-right: 2px solid #dedede;
703
+
704
+ /* Round the Top Corners */
705
+ border-top-left-radius: 5px;
706
+ border-top-right-radius: 5px;
707
+
708
+ /* Add Text Padding */
709
+ padding-left: 12px;
710
+ padding-right: 12px;
711
+
712
+ /* Match Background Color of Box */
713
+ background: #fdfdfd;
714
+ }
715
+
716
+ /* Make the TabBox's Children Appear Above Title if Overlap Occurs */
717
+ .TabBox-children {
718
+ position: relative;
719
+ z-index: 1;
720
+ }
721
+ `;
722
+ /*------------------------------------------------------------------------*/
723
+ /* Component */
724
+ /*------------------------------------------------------------------------*/
725
+ const TabBox = (props) => {
726
+ /*------------------------------------------------------------------------*/
727
+ /* Setup */
728
+ /*------------------------------------------------------------------------*/
729
+ /* -------------- Props ------------- */
730
+ const { title, children, } = props;
731
+ /*------------------------------------------------------------------------*/
732
+ /* Render */
733
+ /*------------------------------------------------------------------------*/
734
+ /*----------------------------------------*/
735
+ /* Main UI */
736
+ /*----------------------------------------*/
737
+ // Full UI
738
+ return (React.createElement("div", null,
739
+ React.createElement("style", null, style),
740
+ React.createElement("div", { className: "TabBox-title-container" },
741
+ React.createElement("div", { className: "TabBox-title" }, title)),
742
+ React.createElement("div", { className: "TabBox-box p-2" },
743
+ React.createElement("div", { className: "TabBox-children" }, children))));
744
+ };
745
+
746
+ /**
747
+ * Shorten text so it fits into a certain number of chars
748
+ * @author Gabe Abrams
749
+ * @param text the text to abbreviate
750
+ * @param maxChars the maximum number of chars to include
751
+ * @returns abbreviated text with length no greater than maxChars
752
+ * (including ellipses if applicable)
753
+ */
754
+ const abbreviate = (text, maxChars) => {
755
+ // Check if already short enough
756
+ if (text.trim().length < maxChars) {
757
+ return text.trim();
758
+ }
759
+ // Abbreviate
760
+ const shortenedText = (text
761
+ .trim()
762
+ .substring(0, maxChars - 3)
763
+ .trim());
764
+ return `${shortenedText}...`;
765
+ };
766
+
767
+ /**
768
+ * Sum the numbers in an array
769
+ * @author Gabe Abrams
770
+ * @param nums the numbers to sum
771
+ * @returns the sum of the numbers
772
+ */
773
+ const sum = (nums) => {
774
+ return nums.reduce((a, b) => {
775
+ return (a + b);
776
+ }, 0);
777
+ };
778
+
779
+ /**
780
+ * Get the average of a set of numbers
781
+ * @author Gabe Abrams
782
+ * @param nums the numbers to average
783
+ * @returns average value or 0 if no numbers
784
+ */
785
+ const avg = (nums) => {
786
+ // Handle empty array case
787
+ if (nums.length === 0) {
788
+ return 0;
789
+ }
790
+ // Get the total value
791
+ const total = sum(nums);
792
+ // Get average
793
+ return (total / nums.length);
794
+ };
795
+
796
+ /**
797
+ * Round a number (ceiling) to a certain number of decimals
798
+ * @author Gabe Abrams
799
+ * @param num the number to round
800
+ * @param numDecimals the number of decimals to round to
801
+ * @returns rounded number
802
+ */
803
+ const ceilToNumDecimals = (num, numDecimals) => {
804
+ const rounder = 10 ** numDecimals;
805
+ return (Math.ceil(num * rounder) / rounder);
806
+ };
807
+
808
+ /**
809
+ * Round a number (floor) to a certain number of decimals
810
+ * @author Gabe Abrams
811
+ * @param num the number to round
812
+ * @param numDecimals the number of decimals to round to
813
+ * @returns rounded number
814
+ */
815
+ const floorToNumDecimals = (num, numDecimals) => {
816
+ const rounder = 10 ** numDecimals;
817
+ return (Math.floor(num * rounder) / rounder);
818
+ };
819
+
820
+ /**
821
+ * Force a number to stay within specific bounds
822
+ * @author Gabe Abrams
823
+ * @param num the number to move into the bounds
824
+ * @param min the minimum number in the bound
825
+ * @param max the maximum number in the bound
826
+ * @returns bounded number
827
+ */
828
+ const forceNumIntoBounds = (num, min, max) => {
829
+ return Math.max(min, Math.min(max, num));
830
+ };
831
+
832
+ /**
833
+ * Pad a number's decimal with zeros on the right
834
+ * (e.g. 5.2 becomes 5.20 with 2 digit padding)
835
+ * @author Gabe Abrams
836
+ * @param num the number to pad
837
+ * @param numDigits the minimum number of digits after the decimal
838
+ * @returns padded number
839
+ */
840
+ const padDecimalZeros = (num, numDigits) => {
841
+ // Skip if nothing to do
842
+ if (numDigits < 1) {
843
+ return String(num);
844
+ }
845
+ // Convert to string
846
+ let out = String(num);
847
+ // Add a decimal point if there isn't one
848
+ if (!out.includes('.')) {
849
+ out += '.';
850
+ }
851
+ // Add zeros
852
+ while (out.split('.')[1].length < numDigits) {
853
+ out = `${out}0`;
854
+ }
855
+ // Return
856
+ return out;
857
+ };
858
+
859
+ /**
860
+ * Pad a number with zeros on the left (e.g. 5 becomes 05 with 2 digit padding)
861
+ * @author Gabe Abrams
862
+ * @param num the number to pad
863
+ * @param numDigits the minimum number of digits before the decimal
864
+ * @returns padded number
865
+ */
866
+ const padZerosLeft = (num, numDigits) => {
867
+ // Convert to string
868
+ let out = String(num);
869
+ // Add zeros
870
+ while (out.split('.')[0].length < numDigits) {
871
+ out = `0${out}`;
872
+ }
873
+ // Return
874
+ return out;
875
+ };
876
+
877
+ /**
878
+ * Round a number to a certain number of decimals
879
+ * @author Gabe Abrams
880
+ * @param num the number to round
881
+ * @param numDecimals the number of decimals to round to
882
+ * @returns rounded number
883
+ */
884
+ const roundToNumDecimals = (num, numDecimals) => {
885
+ const rounder = 10 ** numDecimals;
886
+ return (Math.round(num * rounder) / rounder);
887
+ };
888
+
889
+ export { AppWrapper, ErrorBox, ErrorWithCode, LoadingSpinner, Modal, ModalButtonType$1 as ModalButtonType, ModalSize$1 as ModalSize, ModalType$1 as ModalType, ReactKitErrorCode$1 as ReactKitErrorCode, TabBox, Variant$1 as Variant, abbreviate, alert, avg, ceilToNumDecimals, confirm, floorToNumDecimals, forceNumIntoBounds, padDecimalZeros, padZerosLeft, roundToNumDecimals, showFatalError, sum, waitMs };
890
+ //# sourceMappingURL=index.js.map