dce-reactkit 3.0.0-beta.4 → 3.0.0-beta.40

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 (60) hide show
  1. package/.eslintrc.js +95 -0
  2. package/README.md +1 -546
  3. package/dist/cjs/index.js +784 -85
  4. package/dist/cjs/index.js.map +1 -1
  5. package/dist/cjs/types/components/AppWrapper.d.ts +25 -2
  6. package/dist/cjs/types/components/ButtonInputGroup.d.ts +12 -0
  7. package/dist/cjs/types/components/CheckboxButton.d.ts +20 -0
  8. package/dist/cjs/types/components/RadioButton.d.ts +20 -0
  9. package/dist/cjs/types/components/SimpleDateChooser.d.ts +21 -0
  10. package/dist/cjs/types/components/TabBox.d.ts +1 -0
  11. package/dist/cjs/types/helpers/genRouteHandler.d.ts +30 -0
  12. package/dist/cjs/types/helpers/getOrdinal.d.ts +8 -0
  13. package/dist/cjs/types/helpers/getTimeInfoInET.d.ts +17 -0
  14. package/dist/cjs/types/helpers/handleError.d.ts +18 -0
  15. package/dist/cjs/types/helpers/handleSuccess.d.ts +8 -0
  16. package/dist/cjs/types/helpers/visitServerEndpoint.d.ts +23 -0
  17. package/dist/cjs/types/index.d.ts +10 -1
  18. package/dist/cjs/types/server/initServer.d.ts +21 -0
  19. package/dist/cjs/types/types/ParamType.d.ts +17 -0
  20. package/dist/cjs/types/types/ReactKitErrorCode.d.ts +3 -1
  21. package/dist/esm/index.js +776 -86
  22. package/dist/esm/index.js.map +1 -1
  23. package/dist/esm/types/components/AppWrapper.d.ts +25 -2
  24. package/dist/esm/types/components/ButtonInputGroup.d.ts +12 -0
  25. package/dist/esm/types/components/CheckboxButton.d.ts +20 -0
  26. package/dist/esm/types/components/RadioButton.d.ts +20 -0
  27. package/dist/esm/types/components/SimpleDateChooser.d.ts +21 -0
  28. package/dist/esm/types/components/TabBox.d.ts +1 -0
  29. package/dist/esm/types/helpers/genRouteHandler.d.ts +30 -0
  30. package/dist/esm/types/helpers/getOrdinal.d.ts +8 -0
  31. package/dist/esm/types/helpers/getTimeInfoInET.d.ts +17 -0
  32. package/dist/esm/types/helpers/handleError.d.ts +18 -0
  33. package/dist/esm/types/helpers/handleSuccess.d.ts +8 -0
  34. package/dist/esm/types/helpers/visitServerEndpoint.d.ts +23 -0
  35. package/dist/esm/types/index.d.ts +10 -1
  36. package/dist/esm/types/server/initServer.d.ts +21 -0
  37. package/dist/esm/types/types/ParamType.d.ts +17 -0
  38. package/dist/esm/types/types/ReactKitErrorCode.d.ts +3 -1
  39. package/dist/index.d.ts +182 -10
  40. package/package.json +13 -1
  41. package/rollup.config.js +0 -1
  42. package/src/components/AppWrapper.tsx +73 -15
  43. package/src/components/ButtonInputGroup.tsx +89 -0
  44. package/src/components/CheckboxButton.tsx +100 -0
  45. package/src/components/ErrorBox.tsx +4 -4
  46. package/src/components/LoadingSpinner.tsx +3 -3
  47. package/src/components/Modal.tsx +185 -71
  48. package/src/components/RadioButton.tsx +102 -0
  49. package/src/components/SimpleDateChooser.tsx +217 -0
  50. package/src/components/TabBox.tsx +65 -58
  51. package/src/helpers/genRouteHandler.ts +326 -0
  52. package/src/helpers/getOrdinal.tsx +13 -0
  53. package/src/helpers/getTimeInfoInET.tsx +56 -0
  54. package/src/helpers/handleError.ts +65 -0
  55. package/src/helpers/handleSuccess.ts +18 -0
  56. package/src/helpers/visitServerEndpoint.tsx +110 -0
  57. package/src/index.ts +21 -0
  58. package/src/server/initServer.ts +53 -0
  59. package/src/types/ParamType.ts +18 -0
  60. package/src/types/ReactKitErrorCode.tsx +3 -1
package/dist/esm/index.js CHANGED
@@ -1,8 +1,7 @@
1
1
  import React, { useState, useEffect } from 'react';
2
- import 'bootstrap/dist/css/bootstrap.min.css';
3
- import 'bootstrap/dist/js/bootstrap.min';
4
2
  import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
5
- import { faExclamationTriangle, faCircle } from '@fortawesome/free-solid-svg-icons';
3
+ import { faExclamationTriangle, faCircle, faDotCircle, faCheckSquare } from '@fortawesome/free-solid-svg-icons';
4
+ import { faCircle as faCircle$1, faSquare } from '@fortawesome/free-regular-svg-icons';
6
5
 
7
6
  /******************************************************************************
8
7
  Copyright (c) Microsoft Corporation.
@@ -29,7 +28,7 @@ function __awaiter(thisArg, _arguments, P, generator) {
29
28
  });
30
29
  }
31
30
 
32
- // Highest error code = DRK2
31
+ // Highest error code = DRK8
33
32
  /**
34
33
  * List of error codes built into the react kit
35
34
  * @author Gabe Abrams
@@ -42,6 +41,8 @@ var ReactKitErrorCode;
42
41
  ReactKitErrorCode["MissingParameter"] = "DRK4";
43
42
  ReactKitErrorCode["InvalidParameter"] = "DRK5";
44
43
  ReactKitErrorCode["WrongCourse"] = "DRK6";
44
+ ReactKitErrorCode["NoCACCLSendRequestFunction"] = "DRK7";
45
+ ReactKitErrorCode["NoCACCLGetLaunchInfoFunction"] = "DRK8";
45
46
  })(ReactKitErrorCode || (ReactKitErrorCode = {}));
46
47
  var ReactKitErrorCode$1 = ReactKitErrorCode;
47
48
 
@@ -73,9 +74,9 @@ const ErrorBox = (props) => {
73
74
  ' ',
74
75
  React.createElement("span", { style: {
75
76
  backgroundColor: 'white',
76
- borderRadius: '5px',
77
- paddingLeft: '3px',
78
- paddingRight: '3px',
77
+ borderRadius: '0.3rem',
78
+ paddingLeft: '0.2rem',
79
+ paddingRight: '0.2rem',
79
80
  color: '#DC4150',
80
81
  fontVariant: 'small-caps',
81
82
  fontSize: '80%',
@@ -87,7 +88,7 @@ const ErrorBox = (props) => {
87
88
  }
88
89
  // Main UI
89
90
  return (React.createElement("div", { className: "alert alert-danger text-center", style: {
90
- maxWidth: '650px',
91
+ maxWidth: '40rem',
91
92
  margin: 'auto',
92
93
  } },
93
94
  React.createElement("h4", { className: "mb-1" },
@@ -186,9 +187,7 @@ var ModalSize$1 = ModalSize;
186
187
  /* Constants */
187
188
  /*------------------------------------------------------------------------*/
188
189
  // Constants
189
- const MS_TO_ANIMATE = 400; // Time to animate in/out (defined by bootstrap)
190
- const MS_ANIMATE_IN_DELAY = 10;
191
- // Time to wait before animating in (must be >0 or animation won't trigger)
190
+ const MS_TO_ANIMATE = 200; // Animation duration
192
191
  // Modal type to list of buttons
193
192
  const modalTypeToModalButtonTypes = {
194
193
  [ModalType$1.Okay]: [
@@ -268,6 +267,114 @@ const ModalButtonTypeToLabelAndVariant = {
268
267
  },
269
268
  };
270
269
  /*------------------------------------------------------------------------*/
270
+ /* Style */
271
+ /*------------------------------------------------------------------------*/
272
+ const style$2 = `
273
+ .Modal-backdrop {
274
+ position: fixed;
275
+ top: 0;
276
+ left: 0;
277
+ width: 100vw;
278
+ height: 100vw;
279
+ background-color: rgba(0, 0, 0, 0.7);
280
+ }
281
+
282
+ .Modal-fading-in {
283
+ animation-name: Modal-fading-in;
284
+ animation-duration: ${Math.floor(MS_TO_ANIMATE * 2)}ms;
285
+ animation-iteration-count: 1;
286
+ animation-fill-mode: both;
287
+ animation-timing-function: ease-out;
288
+ }
289
+
290
+ @keyframes Modal-fading-in {
291
+ 0% {
292
+ opacity: 0;
293
+ }
294
+ 100% {
295
+ opacity: 1;
296
+ }
297
+ }
298
+
299
+ .Modal-fading-out {
300
+ animation-name: Modal-fading-out;
301
+ animation-duration: ${MS_TO_ANIMATE}ms;
302
+ animation-iteration-count: 1;
303
+ animation-fill-mode: both;
304
+ animation-timing-function: ease-in;
305
+ }
306
+
307
+ @keyframes Modal-fading-out {
308
+ 0% {
309
+ opacity: 1;
310
+ }
311
+ 100% {
312
+ opacity: 0;
313
+ }
314
+ }
315
+
316
+ .Modal-animating-in {
317
+ animation-name: Modal-animating-in;
318
+ animation-duration: ${MS_TO_ANIMATE}ms;
319
+ animation-iteration-count: 1;
320
+ animation-fill-mode: both;
321
+ animation-timing-function: ease-out;
322
+ }
323
+
324
+ @keyframes Modal-animating-in {
325
+ 0% {
326
+ transform: scale(1.05) translate(0, -1.5rem);
327
+ opacity: 0;
328
+ }
329
+ 100% {
330
+ transform: scale(1) translate(0, 0);
331
+ opacity: 1;
332
+ }
333
+ }
334
+
335
+ .Modal-animating-pop {
336
+ animation-name: Modal-animating-pop;
337
+ animation-duration: ${MS_TO_ANIMATE}ms;
338
+ animation-iteration-count: 1;
339
+ animation-fill-mode: both;
340
+ animation-timing-function: ease-in-out;
341
+ }
342
+
343
+ @keyframes Modal-animating-pop {
344
+ 0% {
345
+ transform: scale(1);
346
+ opacity: 1;
347
+ }
348
+ 50% {
349
+ transform: scale(1.05);
350
+ opacity: 0.9;
351
+ }
352
+ 100% {
353
+ transform: scale(1);
354
+ opacity: 1;
355
+ }
356
+ }
357
+
358
+ .Modal-animating-out {
359
+ animation-name: Modal-animating-out;
360
+ animation-duration: ${MS_TO_ANIMATE}ms;
361
+ animation-iteration-count: 1;
362
+ animation-fill-mode: both;
363
+ animation-timing-function: ease-in;
364
+ }
365
+
366
+ @keyframes Modal-animating-out {
367
+ 0% {
368
+ transform: scale(1) translate(0, 0);
369
+ opacity: 1;
370
+ }
371
+ 100% {
372
+ transform: scale(1.05) translate(0, -1.5rem);
373
+ opacity: 0;
374
+ }
375
+ }
376
+ `;
377
+ /*------------------------------------------------------------------------*/
271
378
  /* Component */
272
379
  /*------------------------------------------------------------------------*/
273
380
  const Modal = (props) => {
@@ -278,10 +385,15 @@ const Modal = (props) => {
278
385
  /* -------------- Props ------------- */
279
386
  const { type = ModalType$1.NoButtons, size = ModalSize$1.Large, title, children, onClose, dontAllowBackdropExit, onTopOfOtherModals, } = props;
280
387
  /* -------------- State ------------- */
388
+ // If true, the modal is completely gone
389
+ // (not just invisible, also removed from the dom)
390
+ const [gone, setGone] = useState(false);
281
391
  // If true, the modal is shown
282
392
  const [visible, setVisible] = useState(false);
283
- // True if currently animating in
284
- const [animatingIn, setAnimatingIn] = useState(false);
393
+ // True if animation is in use
394
+ const [animatingIn, setAnimatingIn] = useState(true);
395
+ const [animatingPop, setAnimatingPop] = useState(false);
396
+ const [animatingOut, setAnimatingOut] = useState(false);
285
397
  /*------------------------------------------------------------------------*/
286
398
  /* Lifecycle Functions */
287
399
  /*------------------------------------------------------------------------*/
@@ -291,12 +403,10 @@ const Modal = (props) => {
291
403
  */
292
404
  useEffect(() => {
293
405
  (() => __awaiter(void 0, void 0, void 0, function* () {
294
- // Start the component animating in
295
- yield waitMs(MS_ANIMATE_IN_DELAY);
296
- setAnimatingIn(true);
297
406
  // Wait and then set visible to true
298
407
  yield waitMs(MS_TO_ANIMATE);
299
408
  setVisible(true);
409
+ setAnimatingIn(false);
300
410
  }))();
301
411
  }, []);
302
412
  /*------------------------------------------------------------------------*/
@@ -323,13 +433,19 @@ const Modal = (props) => {
323
433
  }
324
434
  // Update the state
325
435
  setVisible(false);
436
+ setAnimatingOut(true);
326
437
  // Call the handler after the modal has animated out
327
438
  yield waitMs(MS_TO_ANIMATE);
328
439
  onClose(ModalButtonType);
440
+ setGone(true);
329
441
  });
330
442
  /*------------------------------------------------------------------------*/
331
443
  /* Render */
332
444
  /*------------------------------------------------------------------------*/
445
+ // Return nothing if gone
446
+ if (gone) {
447
+ return null;
448
+ }
333
449
  /*----------------------------------------*/
334
450
  /* Footer */
335
451
  /*----------------------------------------*/
@@ -351,7 +467,7 @@ const Modal = (props) => {
351
467
  // Check if this button is last
352
468
  const last = (i === ModalButtonTypes.length - 1);
353
469
  // Create the button
354
- return (React.createElement("button", { type: "button", className: `Modal-${ModalButtonType}-button btn btn-${variant} ${last ? '' : 'mr-1'}`, onClick: () => {
470
+ return (React.createElement("button", { key: ModalButtonType, type: "button", className: `Modal-${ModalButtonType}-button btn btn-${variant} ${last ? '' : 'me-1'}`, onClick: () => {
355
471
  handleClose(ModalButtonType);
356
472
  } }, label));
357
473
  });
@@ -359,21 +475,62 @@ const Modal = (props) => {
359
475
  const footer = ((buttons && buttons.length)
360
476
  ? (React.createElement("div", null, buttons))
361
477
  : undefined);
478
+ // Choose an animation
479
+ let animationClass = '';
480
+ let backdropAnimationClass = '';
481
+ if (animatingIn) {
482
+ animationClass = 'Modal-animating-in';
483
+ backdropAnimationClass = 'Modal-fading-in';
484
+ }
485
+ else if (animatingOut) {
486
+ animationClass = 'Modal-animating-out';
487
+ backdropAnimationClass = 'Modal-fading-out';
488
+ }
489
+ else if (animatingPop) {
490
+ animationClass = 'Modal-animating-pop';
491
+ }
362
492
  // Render the modal
363
- return (React.createElement("div", { className: `modal fade modal-${size}`, tabIndex: -1, style: {
493
+ return (React.createElement("div", { className: `modal show modal-dialog-scrollable modal-dialog-centered modal-${size}`, tabIndex: -1, style: {
364
494
  zIndex: (onTopOfOtherModals
365
- ? 6000000000
495
+ ? 5000000001
366
496
  : 5000000000),
497
+ display: 'block',
498
+ margin: 'auto',
499
+ left: 0,
500
+ right: 0,
367
501
  } },
368
- React.createElement("div", { className: "modal-dialog" },
502
+ React.createElement("style", null, style$2),
503
+ React.createElement("div", { className: `Modal-backdrop ${backdropAnimationClass}`, style: {
504
+ zIndex: 5000000003,
505
+ }, onClick: () => __awaiter(void 0, void 0, void 0, function* () {
506
+ // Skip if exit via backdrop not allowed
507
+ if (dontAllowBackdropExit || !onClose) {
508
+ // Show pop animation
509
+ if (!animatingPop) {
510
+ setAnimatingPop(true);
511
+ // Wait then stop pop animation
512
+ yield waitMs(MS_TO_ANIMATE);
513
+ setAnimatingPop(false);
514
+ }
515
+ return;
516
+ }
517
+ // Handle close
518
+ handleClose(ModalButtonType$1.Cancel);
519
+ }) }),
520
+ React.createElement("div", { className: `modal-dialog ${animationClass}`, style: {
521
+ zIndex: 5000000002,
522
+ } },
369
523
  React.createElement("div", { className: "modal-content" },
370
524
  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: () => {
525
+ React.createElement("h5", { className: "modal-title", style: {
526
+ fontWeight: 'bold',
527
+ } }, title),
528
+ onClose && (React.createElement("button", { type: "button", className: "btn-close", "aria-label": "Close", onClick: () => {
529
+ // Handle close
373
530
  handleClose(ModalButtonType$1.Cancel);
374
531
  } }))),
375
532
  children && (React.createElement("div", { className: "modal-body" }, children)),
376
- footer && (React.createElement("div", { className: "modal-footer" }, footer))))));
533
+ footer && (React.createElement("div", { className: "modal-footer pt-1 pb-1" }, footer))))));
377
534
  };
378
535
 
379
536
  /**
@@ -397,6 +554,24 @@ class ErrorWithCode extends Error {
397
554
  /* Static Helpers */
398
555
  /*------------------------------------------------------------------------*/
399
556
  /*----------------------------------------*/
557
+ /* Send Request */
558
+ /*----------------------------------------*/
559
+ // Store copy of caccl send request
560
+ let _cacclSendRequest;
561
+ /**
562
+ * Send a request using caccl's send request feature
563
+ * @author Gabe Abrams
564
+ * @param opts send request options
565
+ * @returns send request response
566
+ */
567
+ const cacclSendRequest = (opts) => __awaiter(void 0, void 0, void 0, function* () {
568
+ // Make sure send request has been passed in
569
+ if (!_cacclSendRequest) {
570
+ throw new ErrorWithCode('The request could not be sent because the AppWrapper component does not have a copy of sendRequest from CACCL.', ReactKitErrorCode$1.NoCACCLSendRequestFunction);
571
+ }
572
+ return _cacclSendRequest(opts);
573
+ });
574
+ /*----------------------------------------*/
400
575
  /* Alert */
401
576
  /*----------------------------------------*/
402
577
  // Stored copies of setters
@@ -408,7 +583,7 @@ let onAlertClosed;
408
583
  * @param title the title text to display at the top of the alert
409
584
  * @param text the text to display in the alert
410
585
  */
411
- const alert = (title, text) => __awaiter(void 0, void 0, void 0, function* () {
586
+ const alert$1 = (title, text) => __awaiter(void 0, void 0, void 0, function* () {
412
587
  // Fallback if alert not available
413
588
  if (!setAlertInfo) {
414
589
  window.alert(`${title}\n\n${text}`);
@@ -482,7 +657,7 @@ const showFatalError = (error, errorTitle = 'An Error Occurred') => {
482
657
  : String((_b = error.code) !== null && _b !== void 0 ? _b : ReactKitErrorCode$1.NoCode));
483
658
  // Handle case where app hasn't loaded
484
659
  if (!setFatalErrorMessage || !setFatalErrorCode) {
485
- alert(errorTitle, `${message} (code: ${code}). Please contact support.`);
660
+ alert$1(errorTitle, `${message} (code: ${code}). Please contact support.`);
486
661
  return undefined;
487
662
  }
488
663
  // Use setters
@@ -499,7 +674,9 @@ const AppWrapper = (props) => {
499
674
  /* Setup */
500
675
  /*------------------------------------------------------------------------*/
501
676
  /* -------------- Props ------------- */
502
- const { children, dark, sessionExpiredMessage = 'Your session has expired. Please go back to Canvas and start over.', } = props;
677
+ const { children, sendRequest, dark, sessionExpiredMessage = 'Your session has expired. Please go back to Canvas and start over.', } = props;
678
+ // Store copy of send request
679
+ _cacclSendRequest = sendRequest;
503
680
  /* -------------- State ------------- */
504
681
  // Fatal error
505
682
  const [fatalErrorMessage, setFatalErrorMessageInner,] = useState();
@@ -509,10 +686,10 @@ const AppWrapper = (props) => {
509
686
  const [fatalErrorTitle, setFatalErrorTitleInner,] = useState();
510
687
  setFatalErrorTitle = setFatalErrorTitleInner;
511
688
  // Alert
512
- const [alertInfo, setAlertInfoInner,] = useState();
689
+ const [alertInfo, setAlertInfoInner,] = useState(undefined);
513
690
  setAlertInfo = setAlertInfoInner;
514
691
  // Confirm
515
- const [confirmInfo, setConfirmInfoInner,] = useState();
692
+ const [confirmInfo, setConfirmInfoInner,] = useState(undefined);
516
693
  setConfirmInfo = setConfirmInfoInner;
517
694
  // Session expired
518
695
  const [sessionHasExpired, setSessionHasExpiredInner,] = useState(false);
@@ -526,8 +703,9 @@ const AppWrapper = (props) => {
526
703
  let modal;
527
704
  /* -------------- Alert ------------- */
528
705
  if (alertInfo) {
529
- modal = (React.createElement(Modal, { title: alertInfo.title, type: ModalType$1.Okay, onClose: () => {
706
+ modal = (React.createElement(Modal, { key: `alert-${alertInfo.title}-${alertInfo.text}`, title: alertInfo.title, type: ModalType$1.Okay, onClose: () => {
530
707
  // Alert closed
708
+ setAlertInfo(undefined);
531
709
  if (onAlertClosed) {
532
710
  onAlertClosed();
533
711
  }
@@ -535,11 +713,12 @@ const AppWrapper = (props) => {
535
713
  }
536
714
  /* ------------- Confirm ------------ */
537
715
  if (confirmInfo) {
538
- modal = (React.createElement(Modal, { title: confirmInfo.title, type: ModalType$1.OkayCancel, onClose: (buttonType) => {
716
+ modal = (React.createElement(Modal, { key: `confirm-${confirmInfo.title}-${confirmInfo.text}`, title: confirmInfo.title, type: ModalType$1.OkayCancel, onClose: (buttonType) => {
717
+ setConfirmInfo(undefined);
539
718
  if (onConfirmClosed) {
540
719
  onConfirmClosed(buttonType === ModalButtonType$1.Okay);
541
720
  }
542
- }, dontAllowBackdropExit: true }));
721
+ }, dontAllowBackdropExit: true }, confirmInfo.text));
543
722
  }
544
723
  /*----------------------------------------*/
545
724
  /* Views */
@@ -591,10 +770,10 @@ const style$1 = `
591
770
  .LoadingSpinner-blip-2,
592
771
  .LoadingSpinner-blip-3,
593
772
  .LoadingSpinner-blip-4 {
594
- font-size: 25px;
773
+ font-size: 1.8rem;
595
774
  opacity: 0.6;
596
- margin-top: 20px;
597
- margin-bottom: 20px;
775
+ margin-top: 1rem;
776
+ margin-bottom: 1rem;
598
777
  }
599
778
 
600
779
  /* First Blip */
@@ -661,63 +840,67 @@ const LoadingSpinner = () => {
661
840
  /* Style */
662
841
  /*------------------------------------------------------------------------*/
663
842
  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
- }
843
+ /* Tab Box */
844
+ .TabBox-box {
845
+ /* Light Border */
846
+ border: 0.15rem solid #dedede;
847
+
848
+ /* Rounded Corners (except top-left) */
849
+ border-bottom-right-radius: 0.3rem;
850
+ border-bottom-left-radius: 0.3rem;
851
+ border-top-right-radius: 0.3rem;
680
852
 
681
- /* Container for Title */
682
- .TabBox-title-container {
683
- /* Place on Left */
684
- position: relative;
685
- left: 0;
686
- text-align: left;
687
- }
853
+ /* Very Light Gray Border */
854
+ background: #fdfdfd;
855
+
856
+ /* Align Contents on Left */
857
+ text-align: left;
688
858
 
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 */
859
+ /* Add Text Padding */
860
+ padding-left: 0.3rem;
861
+ padding-right: 0.3rem;
862
+ }
695
863
 
696
- /* Title-sized Font */
697
- font-size: 25px;
864
+ /* Container for Title */
865
+ .TabBox-title-container {
866
+ /* Place on Left */
867
+ position: relative;
868
+ left: 0;
869
+ text-align: left;
870
+ }
698
871
 
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;
872
+ /* Tab-style Title */
873
+ .TabBox-title {
874
+ /* Place so it Barely Overlaps the Box Border */
875
+ display: inline-block;
876
+ position: relative;
877
+ top: 0.15rem; /* Gives Illusion that Border Doesn't Exist Below Tab */
703
878
 
704
- /* Round the Top Corners */
705
- border-top-left-radius: 5px;
706
- border-top-right-radius: 5px;
879
+ /* Title-sized Font */
880
+ font-size: 1.5rem;
707
881
 
708
- /* Add Text Padding */
709
- padding-left: 12px;
710
- padding-right: 12px;
882
+ /* Add Border on Top and Sides */
883
+ border-top: 0.15rem solid #dedede;
884
+ border-left: 0.15rem solid #dedede;
885
+ border-right: 0.15rem solid #dedede;
711
886
 
712
- /* Match Background Color of Box */
713
- background: #fdfdfd;
714
- }
887
+ /* Round the Top Corners */
888
+ border-top-left-radius: 0.3rem;
889
+ border-top-right-radius: 0.3rem;
715
890
 
716
- /* Make the TabBox's Children Appear Above Title if Overlap Occurs */
717
- .TabBox-children {
718
- position: relative;
719
- z-index: 1;
720
- }
891
+ /* Add Text Padding */
892
+ padding-left: 0.5rem;
893
+ padding-right: 0.5rem;
894
+
895
+ /* Match Background Color of Box */
896
+ background: #fdfdfd;
897
+ }
898
+
899
+ /* Make the TabBox's Children Appear Above Title if Overlap Occurs */
900
+ .TabBox-children {
901
+ position: relative;
902
+ z-index: 1;
903
+ }
721
904
  `;
722
905
  /*------------------------------------------------------------------------*/
723
906
  /* Component */
@@ -727,7 +910,7 @@ const TabBox = (props) => {
727
910
  /* Setup */
728
911
  /*------------------------------------------------------------------------*/
729
912
  /* -------------- Props ------------- */
730
- const { title, children, } = props;
913
+ const { title, children, noBottomPadding, } = props;
731
914
  /*------------------------------------------------------------------------*/
732
915
  /* Render */
733
916
  /*------------------------------------------------------------------------*/
@@ -735,7 +918,7 @@ const TabBox = (props) => {
735
918
  /* Main UI */
736
919
  /*----------------------------------------*/
737
920
  // Full UI
738
- return (React.createElement("div", null,
921
+ return (React.createElement("div", { className: `TabBox-container ${noBottomPadding ? '' : 'mb-2'}` },
739
922
  React.createElement("style", null, style),
740
923
  React.createElement("div", { className: "TabBox-title-container" },
741
924
  React.createElement("div", { className: "TabBox-title" }, title)),
@@ -743,6 +926,95 @@ const TabBox = (props) => {
743
926
  React.createElement("div", { className: "TabBox-children" }, children))));
744
927
  };
745
928
 
929
+ /**
930
+ * A radio selection button
931
+ * @author Gabe Abrams
932
+ */
933
+ /*------------------------------------------------------------------------*/
934
+ /* Component */
935
+ /*------------------------------------------------------------------------*/
936
+ const RadioButton = (props) => {
937
+ /*------------------------------------------------------------------------*/
938
+ /* Setup */
939
+ /*------------------------------------------------------------------------*/
940
+ /* -------------- Props ------------- */
941
+ const { text, onSelected, ariaLabel, title, selected, id, noMarginOnRight, selectedVariant = Variant$1.Secondary, unselectedVariant = Variant$1.Light, small, } = props;
942
+ /*------------------------------------------------------------------------*/
943
+ /* Render */
944
+ /*------------------------------------------------------------------------*/
945
+ /*----------------------------------------*/
946
+ /* Main UI */
947
+ /*----------------------------------------*/
948
+ return (React.createElement("button", { type: "button", id: id, title: title, className: `btn btn-${selected ? selectedVariant : unselectedVariant}${selected ? ' selected' : ''}${small ? ' btn-sm' : ''} m-0${noMarginOnRight ? '' : ' me-1'}`, "aria-label": `${ariaLabel}${selected ? ': currently selected' : ''}`, onClick: () => {
949
+ if (!selected) {
950
+ onSelected();
951
+ }
952
+ } },
953
+ React.createElement(FontAwesomeIcon, { icon: selected ? faDotCircle : faCircle$1, className: "me-1" }),
954
+ text));
955
+ };
956
+
957
+ /**
958
+ * A checkbox button
959
+ * @author Gabe Abrams
960
+ */
961
+ /*------------------------------------------------------------------------*/
962
+ /* Component */
963
+ /*------------------------------------------------------------------------*/
964
+ const CheckboxButton = (props) => {
965
+ /*------------------------------------------------------------------------*/
966
+ /* Setup */
967
+ /*------------------------------------------------------------------------*/
968
+ /* -------------- Props ------------- */
969
+ const { text, onChanged, ariaLabel, title, checked, id, noMarginOnRight, checkedVariant = Variant$1.Secondary, uncheckedVariant = Variant$1.Light, small, } = props;
970
+ /*------------------------------------------------------------------------*/
971
+ /* Render */
972
+ /*------------------------------------------------------------------------*/
973
+ /*----------------------------------------*/
974
+ /* Main UI */
975
+ /*----------------------------------------*/
976
+ return (React.createElement("button", { type: "button", id: id, title: title, className: `btn btn-${checked ? checkedVariant : uncheckedVariant}${checked ? ' selected' : ''}${small ? ' btn-sm' : ''} m-0${noMarginOnRight ? '' : ' me-1'}`, "aria-label": `${ariaLabel}${checked ? ': currently checked' : ''}`, onClick: () => {
977
+ onChanged(!checked);
978
+ } },
979
+ React.createElement(FontAwesomeIcon, { icon: checked ? faCheckSquare : faSquare, className: "me-1" }),
980
+ text));
981
+ };
982
+
983
+ /**
984
+ * Input group with a title and space for buttons
985
+ * @author Gabe Abrams
986
+ */
987
+ /*------------------------------------------------------------------------*/
988
+ /* Component */
989
+ /*------------------------------------------------------------------------*/
990
+ const ButtonInputGroup = (props) => {
991
+ /*------------------------------------------------------------------------*/
992
+ /* Setup */
993
+ /*------------------------------------------------------------------------*/
994
+ /* -------------- Props ------------- */
995
+ // Destructure all props
996
+ const { label, minLabelWidth, children, } = props;
997
+ /*------------------------------------------------------------------------*/
998
+ /* Render */
999
+ /*------------------------------------------------------------------------*/
1000
+ /*----------------------------------------*/
1001
+ /* Main UI */
1002
+ /*----------------------------------------*/
1003
+ return (React.createElement("div", { className: "input-group" },
1004
+ React.createElement("div", { className: "input-group-prepend d-flex w-100" },
1005
+ React.createElement("span", { className: "input-group-text", style: {
1006
+ minWidth: (minLabelWidth !== null && minLabelWidth !== void 0 ? minLabelWidth : undefined),
1007
+ borderTopRightRadius: 0,
1008
+ borderBottomRightRadius: 0,
1009
+ } }, label),
1010
+ React.createElement("span", { className: "input-group-text flex-grow-1 rounded-right", style: {
1011
+ backgroundColor: 'white',
1012
+ borderTopLeftRadius: 0,
1013
+ borderBottomLeftRadius: 0,
1014
+ borderLeftWidth: 0,
1015
+ } }, children))));
1016
+ };
1017
+
746
1018
  /**
747
1019
  * Shorten text so it fits into a certain number of chars
748
1020
  * @author Gabe Abrams
@@ -886,5 +1158,423 @@ const roundToNumDecimals = (num, numDecimals) => {
886
1158
  return (Math.round(num * rounder) / rounder);
887
1159
  };
888
1160
 
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 };
1161
+ // Keep track of whether or not session expiry has already been handled
1162
+ let sessionAlreadyExpired = false;
1163
+ /*------------------------------------------------------------------------*/
1164
+ /* Main */
1165
+ /*------------------------------------------------------------------------*/
1166
+ /**
1167
+ * Visit an endpoint on the server [for client only]
1168
+ * @author Gabe Abrams
1169
+ * @param opts object containing all arguments
1170
+ * @param opts.path - the path of the server endpoint
1171
+ * @param [opts.method=GET] - the method of the endpoint
1172
+ * @param [opts.params] - query/body parameters to include
1173
+ * @returns response from server
1174
+ */
1175
+ const visitServerEndpoint = (opts) => __awaiter(void 0, void 0, void 0, function* () {
1176
+ var _a;
1177
+ // Send the request
1178
+ const response = yield cacclSendRequest({
1179
+ path: opts.path,
1180
+ method: (_a = opts.method) !== null && _a !== void 0 ? _a : 'GET',
1181
+ params: opts.params,
1182
+ });
1183
+ // Check for failure
1184
+ if (!response || !response.body) {
1185
+ throw new ErrorWithCode('We didn\'t get a response from the server. Please check your internet connection.', ReactKitErrorCode$1.NoResponse);
1186
+ }
1187
+ if (!response.body.success) {
1188
+ // Session expired
1189
+ if (response.body.code === ReactKitErrorCode$1.SessionExpired) {
1190
+ // Skip notice if session was already expired
1191
+ if (sessionAlreadyExpired) {
1192
+ // Never return (browser is already reloading)
1193
+ yield new Promise(() => {
1194
+ // Promise that never returns
1195
+ });
1196
+ }
1197
+ sessionAlreadyExpired = true;
1198
+ // Show session expiration message
1199
+ {
1200
+ // Fallback to alert
1201
+ // eslint-disable-next-line no-alert
1202
+ alert('Your session has expired. Please start over.');
1203
+ }
1204
+ // Never return (don't continue execution)
1205
+ yield new Promise(() => {
1206
+ // Promise that never returns
1207
+ });
1208
+ }
1209
+ // Other errors
1210
+ throw new ErrorWithCode((response.body.message
1211
+ || 'An unknown error occurred. Please contact an admin.'), (response.body.code
1212
+ || ReactKitErrorCode$1.NoCode));
1213
+ }
1214
+ // Success! Extract the body
1215
+ const { body } = response.body;
1216
+ // Return
1217
+ return body;
1218
+ });
1219
+
1220
+ // Import custom error
1221
+ // Stored copy of caccl functions
1222
+ let _cacclGetLaunchInfo;
1223
+ /*------------------------------------------------------------------------*/
1224
+ /* Helpers */
1225
+ /*------------------------------------------------------------------------*/
1226
+ /**
1227
+ * Get launch info via CACCL
1228
+ * @author Gabe Abrams
1229
+ * @param req express request object
1230
+ * @returns object { launched, launchInfo }
1231
+ */
1232
+ const cacclGetLaunchInfo = (req) => {
1233
+ if (!_cacclGetLaunchInfo) {
1234
+ throw new ErrorWithCode('Could not get launch info because server was not initialized with dce-reactkit\'s initServer function', ReactKitErrorCode$1.NoCACCLGetLaunchInfoFunction);
1235
+ }
1236
+ return _cacclGetLaunchInfo(req);
1237
+ };
1238
+ /*------------------------------------------------------------------------*/
1239
+ /* Main */
1240
+ /*------------------------------------------------------------------------*/
1241
+ /**
1242
+ * Prepare dce-reactkit to run on the server
1243
+ * @author Gabe Abrams
1244
+ * @param opts object containing all arguments
1245
+ * @param opts.getLaunchInfo CACCL LTI's get launch info function
1246
+ */
1247
+ const initServer = (opts) => {
1248
+ _cacclGetLaunchInfo = opts.getLaunchInfo;
1249
+ };
1250
+
1251
+ /**
1252
+ * Server-side API param types
1253
+ * @author Gabe Abrams
1254
+ */
1255
+ var ParamType;
1256
+ (function (ParamType) {
1257
+ ParamType["Boolean"] = "boolean";
1258
+ ParamType["BooleanOptional"] = "boolean-optional";
1259
+ ParamType["Float"] = "float";
1260
+ ParamType["FloatOptional"] = "float-optional";
1261
+ ParamType["Int"] = "int";
1262
+ ParamType["IntOptional"] = "int-optional";
1263
+ ParamType["JSON"] = "json";
1264
+ ParamType["JSONOptional"] = "json-optional";
1265
+ ParamType["String"] = "string";
1266
+ ParamType["StringOptional"] = "string-optional";
1267
+ })(ParamType || (ParamType = {}));
1268
+ var ParamType$1 = ParamType;
1269
+
1270
+ // Import shared types
1271
+ /**
1272
+ * Handle an error and respond to the client
1273
+ * @author Gabe Abrams
1274
+ * @param res express response
1275
+ * @param error error info
1276
+ * @param opts.err the error to send to the client
1277
+ * or the error message
1278
+ * @param [opts.code] an error code (only used if err.code is not
1279
+ * included)
1280
+ * @param [opts.status=500] the https status code to use
1281
+ * defined)
1282
+ */
1283
+ const handleError = (res, error) => {
1284
+ // Get the error message
1285
+ let message;
1286
+ if (error && error.message) {
1287
+ message = (error.message || 'An unknown error occurred.');
1288
+ }
1289
+ else if (typeof error === 'string') {
1290
+ message = (error.trim().length > 0
1291
+ ? error
1292
+ : 'An unknown error occurred.');
1293
+ }
1294
+ else {
1295
+ message = 'An unknown error occurred.';
1296
+ }
1297
+ // Get the error code
1298
+ const code = (error.code || ReactKitErrorCode$1.NoCode);
1299
+ // Get the status code
1300
+ const status = (error.status || 500);
1301
+ // Respond to user
1302
+ res
1303
+ // Set the http status code
1304
+ .status(status)
1305
+ // Send a JSON response
1306
+ .json({
1307
+ // Error message
1308
+ message,
1309
+ // Error code
1310
+ code,
1311
+ // Success = false flag so client can detect server-side errors
1312
+ success: false,
1313
+ });
1314
+ return undefined;
1315
+ };
1316
+
1317
+ /**
1318
+ * Send successful API response
1319
+ * @author Gabe Abrams
1320
+ * @param res express response
1321
+ * @param body the body of the response to send to the client
1322
+ */
1323
+ const handleSuccess = (res, body) => {
1324
+ // Send a http 200 json response
1325
+ res.json({
1326
+ // Include the body as a parameter
1327
+ body,
1328
+ // Success = true flag so client can detect successful responses
1329
+ success: true,
1330
+ });
1331
+ return undefined;
1332
+ };
1333
+
1334
+ /**
1335
+ * Generate an express API route handler
1336
+ * @author Gabe Abrams
1337
+ * @param opts object containing all arguments
1338
+ * @param opts.paramTypes map containing the types for each parameter that is
1339
+ * included in the request (map: param name => type)
1340
+ * @param opts.handler function that processes the request
1341
+ * @returns express route handler that takes the following arguments:
1342
+ * params (map: param name => value), handleSuccess (function for handling
1343
+ * successful requests), handleError (function for handling failed requests),
1344
+ * req (express request object), res (express response object)
1345
+ */
1346
+ const genRouteHandler = (opts) => {
1347
+ // Return a route handler
1348
+ return (req, res) => __awaiter(void 0, void 0, void 0, function* () {
1349
+ var _a;
1350
+ // Output params
1351
+ const output = {};
1352
+ /*----------------------------------------*/
1353
+ /* Parse Params */
1354
+ /*----------------------------------------*/
1355
+ // Process items one by one
1356
+ const paramList = Object.entries((_a = opts.paramTypes) !== null && _a !== void 0 ? _a : {});
1357
+ for (let i = 0; i < paramList.length; i++) {
1358
+ const [name, type] = paramList[i];
1359
+ // Find the value as a string
1360
+ const value = (req.params[name]
1361
+ || req.query[name]
1362
+ || req.body[name]);
1363
+ // Parse
1364
+ if (type === ParamType$1.Boolean || type === ParamType$1.BooleanOptional) {
1365
+ // Boolean
1366
+ // Handle case where value doesn't exist
1367
+ if (value === undefined) {
1368
+ if (type === ParamType$1.BooleanOptional) {
1369
+ output[name] = undefined;
1370
+ }
1371
+ else {
1372
+ return handleError(res, {
1373
+ message: `Parameter ${name} is required, but it was not included.`,
1374
+ code: ReactKitErrorCode$1.MissingParameter,
1375
+ status: 422,
1376
+ });
1377
+ }
1378
+ }
1379
+ else {
1380
+ // Value exists
1381
+ // Simplify value
1382
+ const simpleVal = (String(value)
1383
+ .trim()
1384
+ .toLowerCase());
1385
+ // Parse
1386
+ output[name] = ([
1387
+ 'true',
1388
+ 'yes',
1389
+ 'y',
1390
+ '1',
1391
+ 't',
1392
+ ].indexOf(simpleVal) >= 0);
1393
+ }
1394
+ }
1395
+ else if (type === ParamType$1.Float || type === ParamType$1.FloatOptional) {
1396
+ // Float
1397
+ // Handle case where value doesn't exist
1398
+ if (value === undefined) {
1399
+ if (type === ParamType$1.FloatOptional) {
1400
+ output[name] = undefined;
1401
+ }
1402
+ else {
1403
+ return handleError(res, {
1404
+ message: `Parameter ${name} is required, but it was not included.`,
1405
+ code: ReactKitErrorCode$1.MissingParameter,
1406
+ status: 422,
1407
+ });
1408
+ }
1409
+ }
1410
+ else if (!Number.isNaN(Number.parseFloat(String(value)))) {
1411
+ // Value is a number
1412
+ output[name] = Number.parseFloat(String(value));
1413
+ }
1414
+ else {
1415
+ // Issue!
1416
+ return handleError(res, {
1417
+ message: `Request data was malformed: ${name} was not a valid float.`,
1418
+ code: ReactKitErrorCode$1.InvalidParameter,
1419
+ status: 422,
1420
+ });
1421
+ }
1422
+ }
1423
+ else if (type === ParamType$1.Int || type === ParamType$1.IntOptional) {
1424
+ // Int
1425
+ // Handle case where value doesn't exist
1426
+ if (value === undefined) {
1427
+ if (type === ParamType$1.IntOptional) {
1428
+ output[name] = undefined;
1429
+ }
1430
+ else {
1431
+ return handleError(res, {
1432
+ message: `Parameter ${name} is required, but it was not included.`,
1433
+ code: ReactKitErrorCode$1.MissingParameter,
1434
+ status: 422,
1435
+ });
1436
+ }
1437
+ }
1438
+ else if (!Number.isNaN(Number.parseInt(String(value), 10))) {
1439
+ // Value is a number
1440
+ output[name] = Number.parseInt(String(value), 10);
1441
+ }
1442
+ else {
1443
+ // Issue!
1444
+ return handleError(res, {
1445
+ message: `Request data was malformed: ${name} was not a valid int.`,
1446
+ code: ReactKitErrorCode$1.InvalidParameter,
1447
+ status: 422,
1448
+ });
1449
+ }
1450
+ }
1451
+ else if (type === ParamType$1.JSON || type === ParamType$1.JSONOptional) {
1452
+ // Stringified JSON
1453
+ // Handle case where value doesn't exist
1454
+ if (value === undefined) {
1455
+ if (type === ParamType$1.JSONOptional) {
1456
+ output[name] = undefined;
1457
+ }
1458
+ else {
1459
+ return handleError(res, {
1460
+ message: `Parameter ${name} is required, but it was not included.`,
1461
+ code: ReactKitErrorCode$1.MissingParameter,
1462
+ status: 422,
1463
+ });
1464
+ }
1465
+ }
1466
+ else {
1467
+ // Value exists
1468
+ // Parse
1469
+ try {
1470
+ output[name] = JSON.parse(String(value));
1471
+ }
1472
+ catch (err) {
1473
+ return handleError(res, {
1474
+ message: `Request data was malformed: ${name} was not a valid JSON payload.`,
1475
+ code: ReactKitErrorCode$1.InvalidParameter,
1476
+ status: 422,
1477
+ });
1478
+ }
1479
+ }
1480
+ }
1481
+ else if (type === ParamType$1.String || type === ParamType$1.StringOptional) {
1482
+ // String
1483
+ // Handle case where value doesn't exist
1484
+ if (value === undefined) {
1485
+ if (type === ParamType$1.StringOptional) {
1486
+ output[name] = undefined;
1487
+ }
1488
+ else {
1489
+ return handleError(res, {
1490
+ message: `Parameter ${name} is required, but it was not included.`,
1491
+ code: ReactKitErrorCode$1.MissingParameter,
1492
+ status: 422,
1493
+ });
1494
+ }
1495
+ }
1496
+ else {
1497
+ // Value exists
1498
+ // Leave as is
1499
+ output[name] = value;
1500
+ }
1501
+ }
1502
+ else {
1503
+ // No valid data type
1504
+ return handleError(res, {
1505
+ message: `An internal error occurred: we could not determine the type of ${name}.`,
1506
+ code: ReactKitErrorCode$1.InvalidParameter,
1507
+ status: 422,
1508
+ });
1509
+ }
1510
+ }
1511
+ /*----------------------------------------*/
1512
+ /* Launch Info */
1513
+ /*----------------------------------------*/
1514
+ // Get launch info
1515
+ const { launched, launchInfo } = cacclGetLaunchInfo(req);
1516
+ if (!launched || !launchInfo) {
1517
+ return handleError(res, {
1518
+ message: 'Your session has expired. Please refresh the page and try again.',
1519
+ code: ReactKitErrorCode$1.SessionExpired,
1520
+ status: 440,
1521
+ });
1522
+ }
1523
+ // Error if user info cannot be found
1524
+ if (!launchInfo.userId
1525
+ || !launchInfo.userFirstName
1526
+ || !launchInfo.userLastName
1527
+ || (launchInfo.notInCourse
1528
+ && !launchInfo.isAdmin)
1529
+ || (!launchInfo.isTTM
1530
+ && !launchInfo.isLearner
1531
+ && !launchInfo.isAdmin)) {
1532
+ return handleError(res, {
1533
+ message: 'Your session was invalid. Please refresh the page and try again.',
1534
+ code: ReactKitErrorCode$1.SessionExpired,
1535
+ status: 440,
1536
+ });
1537
+ }
1538
+ // Add launch info to output
1539
+ output.userId = launchInfo.userId;
1540
+ output.userFirstName = launchInfo.userFirstName;
1541
+ output.userLastName = launchInfo.userLastName;
1542
+ output.isLearner = !!launchInfo.isLearner;
1543
+ output.isTTM = !!launchInfo.isTTM;
1544
+ output.isAdmin = !!launchInfo.isAdmin;
1545
+ output.isWatchingInPrivate = !!(req.session.isWatchingInPrivate);
1546
+ /*----------------------------------------*/
1547
+ /* Require Course Consistency */
1548
+ /*----------------------------------------*/
1549
+ // Make sure the user actually launched from the appropriate course
1550
+ if (output.courseId
1551
+ && launchInfo.courseId
1552
+ && output.courseId !== launchInfo.courseId
1553
+ && !output.isTTM
1554
+ && !output.isAdmin) {
1555
+ // Course of interest is not the launch course
1556
+ return handleError(res, {
1557
+ message: 'You switched sessions by opening Immersive Classroom in another tab. Please refresh the page and try again.',
1558
+ code: ReactKitErrorCode$1.WrongCourse,
1559
+ status: 401,
1560
+ });
1561
+ }
1562
+ /*------------------------------------------------------------------------*/
1563
+ /* Call handler */
1564
+ /*------------------------------------------------------------------------*/
1565
+ opts.handler({
1566
+ params: output,
1567
+ handleSuccess: (body) => {
1568
+ return handleSuccess(res, body);
1569
+ },
1570
+ handleError: (error) => {
1571
+ return handleError(res, error);
1572
+ },
1573
+ req,
1574
+ res,
1575
+ });
1576
+ });
1577
+ };
1578
+
1579
+ export { AppWrapper, ButtonInputGroup, CheckboxButton, ErrorBox, ErrorWithCode, LoadingSpinner, Modal, ModalButtonType$1 as ModalButtonType, ModalSize$1 as ModalSize, ModalType$1 as ModalType, ParamType$1 as ParamType, RadioButton, ReactKitErrorCode$1 as ReactKitErrorCode, TabBox, Variant$1 as Variant, abbreviate, alert$1 as alert, avg, ceilToNumDecimals, confirm, floorToNumDecimals, forceNumIntoBounds, genRouteHandler, handleError, handleSuccess, initServer, padDecimalZeros, padZerosLeft, roundToNumDecimals, showFatalError, sum, visitServerEndpoint, waitMs };
890
1580
  //# sourceMappingURL=index.js.map