dce-reactkit 3.0.0-beta.4 → 3.0.0-beta.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +95 -0
- package/README.md +1 -546
- package/dist/cjs/index.js +944 -85
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/components/AppWrapper.d.ts +25 -2
- package/dist/cjs/types/components/ButtonInputGroup.d.ts +12 -0
- package/dist/cjs/types/components/CheckboxButton.d.ts +20 -0
- package/dist/cjs/types/components/RadioButton.d.ts +20 -0
- package/dist/cjs/types/components/SimpleDateChooser.d.ts +22 -0
- package/dist/cjs/types/components/TabBox.d.ts +1 -0
- package/dist/cjs/types/helpers/genRouteHandler.d.ts +30 -0
- package/dist/cjs/types/helpers/getOrdinal.d.ts +8 -0
- package/dist/cjs/types/helpers/getTimeInfoInET.d.ts +17 -0
- package/dist/cjs/types/helpers/handleError.d.ts +18 -0
- package/dist/cjs/types/helpers/handleSuccess.d.ts +8 -0
- package/dist/cjs/types/helpers/visitServerEndpoint.d.ts +23 -0
- package/dist/cjs/types/index.d.ts +13 -1
- package/dist/cjs/types/server/initServer.d.ts +21 -0
- package/dist/cjs/types/types/ParamType.d.ts +17 -0
- package/dist/cjs/types/types/ReactKitErrorCode.d.ts +3 -1
- package/dist/esm/index.js +933 -86
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/components/AppWrapper.d.ts +25 -2
- package/dist/esm/types/components/ButtonInputGroup.d.ts +12 -0
- package/dist/esm/types/components/CheckboxButton.d.ts +20 -0
- package/dist/esm/types/components/RadioButton.d.ts +20 -0
- package/dist/esm/types/components/SimpleDateChooser.d.ts +22 -0
- package/dist/esm/types/components/TabBox.d.ts +1 -0
- package/dist/esm/types/helpers/genRouteHandler.d.ts +30 -0
- package/dist/esm/types/helpers/getOrdinal.d.ts +8 -0
- package/dist/esm/types/helpers/getTimeInfoInET.d.ts +17 -0
- package/dist/esm/types/helpers/handleError.d.ts +18 -0
- package/dist/esm/types/helpers/handleSuccess.d.ts +8 -0
- package/dist/esm/types/helpers/visitServerEndpoint.d.ts +23 -0
- package/dist/esm/types/index.d.ts +13 -1
- package/dist/esm/types/server/initServer.d.ts +21 -0
- package/dist/esm/types/types/ParamType.d.ts +17 -0
- package/dist/esm/types/types/ReactKitErrorCode.d.ts +3 -1
- package/dist/index.d.ts +229 -10
- package/package.json +13 -1
- package/rollup.config.js +0 -1
- package/src/components/AppWrapper.tsx +73 -15
- package/src/components/ButtonInputGroup.tsx +89 -0
- package/src/components/CheckboxButton.tsx +100 -0
- package/src/components/ErrorBox.tsx +4 -4
- package/src/components/LoadingSpinner.tsx +3 -3
- package/src/components/Modal.tsx +185 -71
- package/src/components/RadioButton.tsx +102 -0
- package/src/components/SimpleDateChooser.tsx +222 -0
- package/src/components/TabBox.tsx +65 -58
- package/src/helpers/genRouteHandler.ts +326 -0
- package/src/helpers/getOrdinal.tsx +13 -0
- package/src/helpers/getTimeInfoInET.tsx +56 -0
- package/src/helpers/handleError.ts +65 -0
- package/src/helpers/handleSuccess.ts +18 -0
- package/src/helpers/visitServerEndpoint.tsx +110 -0
- package/src/index.ts +27 -0
- package/src/server/initServer.ts +53 -0
- package/src/types/ParamType.ts +18 -0
- package/src/types/ReactKitErrorCode.tsx +3 -1
package/dist/cjs/index.js
CHANGED
|
@@ -3,10 +3,9 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var React = require('react');
|
|
6
|
-
require('bootstrap/dist/css/bootstrap.min.css');
|
|
7
|
-
require('bootstrap/dist/js/bootstrap.min');
|
|
8
6
|
var reactFontawesome = require('@fortawesome/react-fontawesome');
|
|
9
7
|
var freeSolidSvgIcons = require('@fortawesome/free-solid-svg-icons');
|
|
8
|
+
var freeRegularSvgIcons = require('@fortawesome/free-regular-svg-icons');
|
|
10
9
|
|
|
11
10
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
12
11
|
|
|
@@ -37,7 +36,7 @@ function __awaiter(thisArg, _arguments, P, generator) {
|
|
|
37
36
|
});
|
|
38
37
|
}
|
|
39
38
|
|
|
40
|
-
// Highest error code =
|
|
39
|
+
// Highest error code = DRK8
|
|
41
40
|
/**
|
|
42
41
|
* List of error codes built into the react kit
|
|
43
42
|
* @author Gabe Abrams
|
|
@@ -50,6 +49,8 @@ var ReactKitErrorCode;
|
|
|
50
49
|
ReactKitErrorCode["MissingParameter"] = "DRK4";
|
|
51
50
|
ReactKitErrorCode["InvalidParameter"] = "DRK5";
|
|
52
51
|
ReactKitErrorCode["WrongCourse"] = "DRK6";
|
|
52
|
+
ReactKitErrorCode["NoCACCLSendRequestFunction"] = "DRK7";
|
|
53
|
+
ReactKitErrorCode["NoCACCLGetLaunchInfoFunction"] = "DRK8";
|
|
53
54
|
})(ReactKitErrorCode || (ReactKitErrorCode = {}));
|
|
54
55
|
var ReactKitErrorCode$1 = ReactKitErrorCode;
|
|
55
56
|
|
|
@@ -81,9 +82,9 @@ const ErrorBox = (props) => {
|
|
|
81
82
|
' ',
|
|
82
83
|
React__default["default"].createElement("span", { style: {
|
|
83
84
|
backgroundColor: 'white',
|
|
84
|
-
borderRadius: '
|
|
85
|
-
paddingLeft: '
|
|
86
|
-
paddingRight: '
|
|
85
|
+
borderRadius: '0.3rem',
|
|
86
|
+
paddingLeft: '0.2rem',
|
|
87
|
+
paddingRight: '0.2rem',
|
|
87
88
|
color: '#DC4150',
|
|
88
89
|
fontVariant: 'small-caps',
|
|
89
90
|
fontSize: '80%',
|
|
@@ -95,7 +96,7 @@ const ErrorBox = (props) => {
|
|
|
95
96
|
}
|
|
96
97
|
// Main UI
|
|
97
98
|
return (React__default["default"].createElement("div", { className: "alert alert-danger text-center", style: {
|
|
98
|
-
maxWidth: '
|
|
99
|
+
maxWidth: '40rem',
|
|
99
100
|
margin: 'auto',
|
|
100
101
|
} },
|
|
101
102
|
React__default["default"].createElement("h4", { className: "mb-1" },
|
|
@@ -194,9 +195,7 @@ var ModalSize$1 = ModalSize;
|
|
|
194
195
|
/* Constants */
|
|
195
196
|
/*------------------------------------------------------------------------*/
|
|
196
197
|
// Constants
|
|
197
|
-
const MS_TO_ANIMATE =
|
|
198
|
-
const MS_ANIMATE_IN_DELAY = 10;
|
|
199
|
-
// Time to wait before animating in (must be >0 or animation won't trigger)
|
|
198
|
+
const MS_TO_ANIMATE = 200; // Animation duration
|
|
200
199
|
// Modal type to list of buttons
|
|
201
200
|
const modalTypeToModalButtonTypes = {
|
|
202
201
|
[ModalType$1.Okay]: [
|
|
@@ -276,6 +275,114 @@ const ModalButtonTypeToLabelAndVariant = {
|
|
|
276
275
|
},
|
|
277
276
|
};
|
|
278
277
|
/*------------------------------------------------------------------------*/
|
|
278
|
+
/* Style */
|
|
279
|
+
/*------------------------------------------------------------------------*/
|
|
280
|
+
const style$2 = `
|
|
281
|
+
.Modal-backdrop {
|
|
282
|
+
position: fixed;
|
|
283
|
+
top: 0;
|
|
284
|
+
left: 0;
|
|
285
|
+
width: 100vw;
|
|
286
|
+
height: 100vw;
|
|
287
|
+
background-color: rgba(0, 0, 0, 0.7);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.Modal-fading-in {
|
|
291
|
+
animation-name: Modal-fading-in;
|
|
292
|
+
animation-duration: ${Math.floor(MS_TO_ANIMATE * 2)}ms;
|
|
293
|
+
animation-iteration-count: 1;
|
|
294
|
+
animation-fill-mode: both;
|
|
295
|
+
animation-timing-function: ease-out;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
@keyframes Modal-fading-in {
|
|
299
|
+
0% {
|
|
300
|
+
opacity: 0;
|
|
301
|
+
}
|
|
302
|
+
100% {
|
|
303
|
+
opacity: 1;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
.Modal-fading-out {
|
|
308
|
+
animation-name: Modal-fading-out;
|
|
309
|
+
animation-duration: ${MS_TO_ANIMATE}ms;
|
|
310
|
+
animation-iteration-count: 1;
|
|
311
|
+
animation-fill-mode: both;
|
|
312
|
+
animation-timing-function: ease-in;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
@keyframes Modal-fading-out {
|
|
316
|
+
0% {
|
|
317
|
+
opacity: 1;
|
|
318
|
+
}
|
|
319
|
+
100% {
|
|
320
|
+
opacity: 0;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.Modal-animating-in {
|
|
325
|
+
animation-name: Modal-animating-in;
|
|
326
|
+
animation-duration: ${MS_TO_ANIMATE}ms;
|
|
327
|
+
animation-iteration-count: 1;
|
|
328
|
+
animation-fill-mode: both;
|
|
329
|
+
animation-timing-function: ease-out;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
@keyframes Modal-animating-in {
|
|
333
|
+
0% {
|
|
334
|
+
transform: scale(1.05) translate(0, -1.5rem);
|
|
335
|
+
opacity: 0;
|
|
336
|
+
}
|
|
337
|
+
100% {
|
|
338
|
+
transform: scale(1) translate(0, 0);
|
|
339
|
+
opacity: 1;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.Modal-animating-pop {
|
|
344
|
+
animation-name: Modal-animating-pop;
|
|
345
|
+
animation-duration: ${MS_TO_ANIMATE}ms;
|
|
346
|
+
animation-iteration-count: 1;
|
|
347
|
+
animation-fill-mode: both;
|
|
348
|
+
animation-timing-function: ease-in-out;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
@keyframes Modal-animating-pop {
|
|
352
|
+
0% {
|
|
353
|
+
transform: scale(1);
|
|
354
|
+
opacity: 1;
|
|
355
|
+
}
|
|
356
|
+
50% {
|
|
357
|
+
transform: scale(1.05);
|
|
358
|
+
opacity: 0.9;
|
|
359
|
+
}
|
|
360
|
+
100% {
|
|
361
|
+
transform: scale(1);
|
|
362
|
+
opacity: 1;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
.Modal-animating-out {
|
|
367
|
+
animation-name: Modal-animating-out;
|
|
368
|
+
animation-duration: ${MS_TO_ANIMATE}ms;
|
|
369
|
+
animation-iteration-count: 1;
|
|
370
|
+
animation-fill-mode: both;
|
|
371
|
+
animation-timing-function: ease-in;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
@keyframes Modal-animating-out {
|
|
375
|
+
0% {
|
|
376
|
+
transform: scale(1) translate(0, 0);
|
|
377
|
+
opacity: 1;
|
|
378
|
+
}
|
|
379
|
+
100% {
|
|
380
|
+
transform: scale(1.05) translate(0, -1.5rem);
|
|
381
|
+
opacity: 0;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
`;
|
|
385
|
+
/*------------------------------------------------------------------------*/
|
|
279
386
|
/* Component */
|
|
280
387
|
/*------------------------------------------------------------------------*/
|
|
281
388
|
const Modal = (props) => {
|
|
@@ -286,10 +393,15 @@ const Modal = (props) => {
|
|
|
286
393
|
/* -------------- Props ------------- */
|
|
287
394
|
const { type = ModalType$1.NoButtons, size = ModalSize$1.Large, title, children, onClose, dontAllowBackdropExit, onTopOfOtherModals, } = props;
|
|
288
395
|
/* -------------- State ------------- */
|
|
396
|
+
// If true, the modal is completely gone
|
|
397
|
+
// (not just invisible, also removed from the dom)
|
|
398
|
+
const [gone, setGone] = React.useState(false);
|
|
289
399
|
// If true, the modal is shown
|
|
290
400
|
const [visible, setVisible] = React.useState(false);
|
|
291
|
-
// True if
|
|
292
|
-
const [animatingIn, setAnimatingIn] = React.useState(
|
|
401
|
+
// True if animation is in use
|
|
402
|
+
const [animatingIn, setAnimatingIn] = React.useState(true);
|
|
403
|
+
const [animatingPop, setAnimatingPop] = React.useState(false);
|
|
404
|
+
const [animatingOut, setAnimatingOut] = React.useState(false);
|
|
293
405
|
/*------------------------------------------------------------------------*/
|
|
294
406
|
/* Lifecycle Functions */
|
|
295
407
|
/*------------------------------------------------------------------------*/
|
|
@@ -299,12 +411,10 @@ const Modal = (props) => {
|
|
|
299
411
|
*/
|
|
300
412
|
React.useEffect(() => {
|
|
301
413
|
(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
302
|
-
// Start the component animating in
|
|
303
|
-
yield waitMs(MS_ANIMATE_IN_DELAY);
|
|
304
|
-
setAnimatingIn(true);
|
|
305
414
|
// Wait and then set visible to true
|
|
306
415
|
yield waitMs(MS_TO_ANIMATE);
|
|
307
416
|
setVisible(true);
|
|
417
|
+
setAnimatingIn(false);
|
|
308
418
|
}))();
|
|
309
419
|
}, []);
|
|
310
420
|
/*------------------------------------------------------------------------*/
|
|
@@ -331,13 +441,19 @@ const Modal = (props) => {
|
|
|
331
441
|
}
|
|
332
442
|
// Update the state
|
|
333
443
|
setVisible(false);
|
|
444
|
+
setAnimatingOut(true);
|
|
334
445
|
// Call the handler after the modal has animated out
|
|
335
446
|
yield waitMs(MS_TO_ANIMATE);
|
|
336
447
|
onClose(ModalButtonType);
|
|
448
|
+
setGone(true);
|
|
337
449
|
});
|
|
338
450
|
/*------------------------------------------------------------------------*/
|
|
339
451
|
/* Render */
|
|
340
452
|
/*------------------------------------------------------------------------*/
|
|
453
|
+
// Return nothing if gone
|
|
454
|
+
if (gone) {
|
|
455
|
+
return null;
|
|
456
|
+
}
|
|
341
457
|
/*----------------------------------------*/
|
|
342
458
|
/* Footer */
|
|
343
459
|
/*----------------------------------------*/
|
|
@@ -359,7 +475,7 @@ const Modal = (props) => {
|
|
|
359
475
|
// Check if this button is last
|
|
360
476
|
const last = (i === ModalButtonTypes.length - 1);
|
|
361
477
|
// Create the button
|
|
362
|
-
return (React__default["default"].createElement("button", { type: "button", className: `Modal-${ModalButtonType}-button btn btn-${variant} ${last ? '' : '
|
|
478
|
+
return (React__default["default"].createElement("button", { key: ModalButtonType, type: "button", className: `Modal-${ModalButtonType}-button btn btn-${variant} ${last ? '' : 'me-1'}`, onClick: () => {
|
|
363
479
|
handleClose(ModalButtonType);
|
|
364
480
|
} }, label));
|
|
365
481
|
});
|
|
@@ -367,21 +483,62 @@ const Modal = (props) => {
|
|
|
367
483
|
const footer = ((buttons && buttons.length)
|
|
368
484
|
? (React__default["default"].createElement("div", null, buttons))
|
|
369
485
|
: undefined);
|
|
486
|
+
// Choose an animation
|
|
487
|
+
let animationClass = '';
|
|
488
|
+
let backdropAnimationClass = '';
|
|
489
|
+
if (animatingIn) {
|
|
490
|
+
animationClass = 'Modal-animating-in';
|
|
491
|
+
backdropAnimationClass = 'Modal-fading-in';
|
|
492
|
+
}
|
|
493
|
+
else if (animatingOut) {
|
|
494
|
+
animationClass = 'Modal-animating-out';
|
|
495
|
+
backdropAnimationClass = 'Modal-fading-out';
|
|
496
|
+
}
|
|
497
|
+
else if (animatingPop) {
|
|
498
|
+
animationClass = 'Modal-animating-pop';
|
|
499
|
+
}
|
|
370
500
|
// Render the modal
|
|
371
|
-
return (React__default["default"].createElement("div", { className: `modal
|
|
501
|
+
return (React__default["default"].createElement("div", { className: `modal show modal-dialog-scrollable modal-dialog-centered modal-${size}`, tabIndex: -1, style: {
|
|
372
502
|
zIndex: (onTopOfOtherModals
|
|
373
|
-
?
|
|
503
|
+
? 5000000001
|
|
374
504
|
: 5000000000),
|
|
505
|
+
display: 'block',
|
|
506
|
+
margin: 'auto',
|
|
507
|
+
left: 0,
|
|
508
|
+
right: 0,
|
|
375
509
|
} },
|
|
376
|
-
React__default["default"].createElement("
|
|
510
|
+
React__default["default"].createElement("style", null, style$2),
|
|
511
|
+
React__default["default"].createElement("div", { className: `Modal-backdrop ${backdropAnimationClass}`, style: {
|
|
512
|
+
zIndex: 5000000003,
|
|
513
|
+
}, onClick: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
514
|
+
// Skip if exit via backdrop not allowed
|
|
515
|
+
if (dontAllowBackdropExit || !onClose) {
|
|
516
|
+
// Show pop animation
|
|
517
|
+
if (!animatingPop) {
|
|
518
|
+
setAnimatingPop(true);
|
|
519
|
+
// Wait then stop pop animation
|
|
520
|
+
yield waitMs(MS_TO_ANIMATE);
|
|
521
|
+
setAnimatingPop(false);
|
|
522
|
+
}
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
// Handle close
|
|
526
|
+
handleClose(ModalButtonType$1.Cancel);
|
|
527
|
+
}) }),
|
|
528
|
+
React__default["default"].createElement("div", { className: `modal-dialog ${animationClass}`, style: {
|
|
529
|
+
zIndex: 5000000002,
|
|
530
|
+
} },
|
|
377
531
|
React__default["default"].createElement("div", { className: "modal-content" },
|
|
378
532
|
React__default["default"].createElement("div", { className: "modal-header" },
|
|
379
|
-
React__default["default"].createElement("h5", { className: "modal-title"
|
|
380
|
-
|
|
533
|
+
React__default["default"].createElement("h5", { className: "modal-title", style: {
|
|
534
|
+
fontWeight: 'bold',
|
|
535
|
+
} }, title),
|
|
536
|
+
onClose && (React__default["default"].createElement("button", { type: "button", className: "btn-close", "aria-label": "Close", onClick: () => {
|
|
537
|
+
// Handle close
|
|
381
538
|
handleClose(ModalButtonType$1.Cancel);
|
|
382
539
|
} }))),
|
|
383
540
|
children && (React__default["default"].createElement("div", { className: "modal-body" }, children)),
|
|
384
|
-
footer && (React__default["default"].createElement("div", { className: "modal-footer" }, footer))))));
|
|
541
|
+
footer && (React__default["default"].createElement("div", { className: "modal-footer pt-1 pb-1" }, footer))))));
|
|
385
542
|
};
|
|
386
543
|
|
|
387
544
|
/**
|
|
@@ -405,6 +562,24 @@ class ErrorWithCode extends Error {
|
|
|
405
562
|
/* Static Helpers */
|
|
406
563
|
/*------------------------------------------------------------------------*/
|
|
407
564
|
/*----------------------------------------*/
|
|
565
|
+
/* Send Request */
|
|
566
|
+
/*----------------------------------------*/
|
|
567
|
+
// Store copy of caccl send request
|
|
568
|
+
let _cacclSendRequest;
|
|
569
|
+
/**
|
|
570
|
+
* Send a request using caccl's send request feature
|
|
571
|
+
* @author Gabe Abrams
|
|
572
|
+
* @param opts send request options
|
|
573
|
+
* @returns send request response
|
|
574
|
+
*/
|
|
575
|
+
const cacclSendRequest = (opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
576
|
+
// Make sure send request has been passed in
|
|
577
|
+
if (!_cacclSendRequest) {
|
|
578
|
+
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);
|
|
579
|
+
}
|
|
580
|
+
return _cacclSendRequest(opts);
|
|
581
|
+
});
|
|
582
|
+
/*----------------------------------------*/
|
|
408
583
|
/* Alert */
|
|
409
584
|
/*----------------------------------------*/
|
|
410
585
|
// Stored copies of setters
|
|
@@ -416,7 +591,7 @@ let onAlertClosed;
|
|
|
416
591
|
* @param title the title text to display at the top of the alert
|
|
417
592
|
* @param text the text to display in the alert
|
|
418
593
|
*/
|
|
419
|
-
const alert = (title, text) => __awaiter(void 0, void 0, void 0, function* () {
|
|
594
|
+
const alert$1 = (title, text) => __awaiter(void 0, void 0, void 0, function* () {
|
|
420
595
|
// Fallback if alert not available
|
|
421
596
|
if (!setAlertInfo) {
|
|
422
597
|
window.alert(`${title}\n\n${text}`);
|
|
@@ -490,7 +665,7 @@ const showFatalError = (error, errorTitle = 'An Error Occurred') => {
|
|
|
490
665
|
: String((_b = error.code) !== null && _b !== void 0 ? _b : ReactKitErrorCode$1.NoCode));
|
|
491
666
|
// Handle case where app hasn't loaded
|
|
492
667
|
if (!setFatalErrorMessage || !setFatalErrorCode) {
|
|
493
|
-
alert(errorTitle, `${message} (code: ${code}). Please contact support.`);
|
|
668
|
+
alert$1(errorTitle, `${message} (code: ${code}). Please contact support.`);
|
|
494
669
|
return undefined;
|
|
495
670
|
}
|
|
496
671
|
// Use setters
|
|
@@ -507,7 +682,9 @@ const AppWrapper = (props) => {
|
|
|
507
682
|
/* Setup */
|
|
508
683
|
/*------------------------------------------------------------------------*/
|
|
509
684
|
/* -------------- Props ------------- */
|
|
510
|
-
const { children, dark, sessionExpiredMessage = 'Your session has expired. Please go back to Canvas and start over.', } = props;
|
|
685
|
+
const { children, sendRequest, dark, sessionExpiredMessage = 'Your session has expired. Please go back to Canvas and start over.', } = props;
|
|
686
|
+
// Store copy of send request
|
|
687
|
+
_cacclSendRequest = sendRequest;
|
|
511
688
|
/* -------------- State ------------- */
|
|
512
689
|
// Fatal error
|
|
513
690
|
const [fatalErrorMessage, setFatalErrorMessageInner,] = React.useState();
|
|
@@ -517,10 +694,10 @@ const AppWrapper = (props) => {
|
|
|
517
694
|
const [fatalErrorTitle, setFatalErrorTitleInner,] = React.useState();
|
|
518
695
|
setFatalErrorTitle = setFatalErrorTitleInner;
|
|
519
696
|
// Alert
|
|
520
|
-
const [alertInfo, setAlertInfoInner,] = React.useState();
|
|
697
|
+
const [alertInfo, setAlertInfoInner,] = React.useState(undefined);
|
|
521
698
|
setAlertInfo = setAlertInfoInner;
|
|
522
699
|
// Confirm
|
|
523
|
-
const [confirmInfo, setConfirmInfoInner,] = React.useState();
|
|
700
|
+
const [confirmInfo, setConfirmInfoInner,] = React.useState(undefined);
|
|
524
701
|
setConfirmInfo = setConfirmInfoInner;
|
|
525
702
|
// Session expired
|
|
526
703
|
const [sessionHasExpired, setSessionHasExpiredInner,] = React.useState(false);
|
|
@@ -534,8 +711,9 @@ const AppWrapper = (props) => {
|
|
|
534
711
|
let modal;
|
|
535
712
|
/* -------------- Alert ------------- */
|
|
536
713
|
if (alertInfo) {
|
|
537
|
-
modal = (React__default["default"].createElement(Modal, { title: alertInfo.title, type: ModalType$1.Okay, onClose: () => {
|
|
714
|
+
modal = (React__default["default"].createElement(Modal, { key: `alert-${alertInfo.title}-${alertInfo.text}`, title: alertInfo.title, type: ModalType$1.Okay, onClose: () => {
|
|
538
715
|
// Alert closed
|
|
716
|
+
setAlertInfo(undefined);
|
|
539
717
|
if (onAlertClosed) {
|
|
540
718
|
onAlertClosed();
|
|
541
719
|
}
|
|
@@ -543,11 +721,12 @@ const AppWrapper = (props) => {
|
|
|
543
721
|
}
|
|
544
722
|
/* ------------- Confirm ------------ */
|
|
545
723
|
if (confirmInfo) {
|
|
546
|
-
modal = (React__default["default"].createElement(Modal, { title: confirmInfo.title, type: ModalType$1.OkayCancel, onClose: (buttonType) => {
|
|
724
|
+
modal = (React__default["default"].createElement(Modal, { key: `confirm-${confirmInfo.title}-${confirmInfo.text}`, title: confirmInfo.title, type: ModalType$1.OkayCancel, onClose: (buttonType) => {
|
|
725
|
+
setConfirmInfo(undefined);
|
|
547
726
|
if (onConfirmClosed) {
|
|
548
727
|
onConfirmClosed(buttonType === ModalButtonType$1.Okay);
|
|
549
728
|
}
|
|
550
|
-
}, dontAllowBackdropExit: true }));
|
|
729
|
+
}, dontAllowBackdropExit: true }, confirmInfo.text));
|
|
551
730
|
}
|
|
552
731
|
/*----------------------------------------*/
|
|
553
732
|
/* Views */
|
|
@@ -599,10 +778,10 @@ const style$1 = `
|
|
|
599
778
|
.LoadingSpinner-blip-2,
|
|
600
779
|
.LoadingSpinner-blip-3,
|
|
601
780
|
.LoadingSpinner-blip-4 {
|
|
602
|
-
font-size:
|
|
781
|
+
font-size: 1.8rem;
|
|
603
782
|
opacity: 0.6;
|
|
604
|
-
margin-top:
|
|
605
|
-
margin-bottom:
|
|
783
|
+
margin-top: 1rem;
|
|
784
|
+
margin-bottom: 1rem;
|
|
606
785
|
}
|
|
607
786
|
|
|
608
787
|
/* First Blip */
|
|
@@ -669,63 +848,67 @@ const LoadingSpinner = () => {
|
|
|
669
848
|
/* Style */
|
|
670
849
|
/*------------------------------------------------------------------------*/
|
|
671
850
|
const style = `
|
|
672
|
-
/* Tab Box */
|
|
673
|
-
.TabBox-box {
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
/* Very Light Gray Border */
|
|
683
|
-
background: #fdfdfd;
|
|
684
|
-
|
|
685
|
-
/* Align Contents on Left */
|
|
686
|
-
text-align: left;
|
|
687
|
-
}
|
|
851
|
+
/* Tab Box */
|
|
852
|
+
.TabBox-box {
|
|
853
|
+
/* Light Border */
|
|
854
|
+
border: 0.15rem solid #dedede;
|
|
855
|
+
|
|
856
|
+
/* Rounded Corners (except top-left) */
|
|
857
|
+
border-bottom-right-radius: 0.3rem;
|
|
858
|
+
border-bottom-left-radius: 0.3rem;
|
|
859
|
+
border-top-right-radius: 0.3rem;
|
|
688
860
|
|
|
689
|
-
/*
|
|
690
|
-
|
|
691
|
-
/* Place on Left */
|
|
692
|
-
position: relative;
|
|
693
|
-
left: 0;
|
|
694
|
-
text-align: left;
|
|
695
|
-
}
|
|
861
|
+
/* Very Light Gray Border */
|
|
862
|
+
background: #fdfdfd;
|
|
696
863
|
|
|
697
|
-
/*
|
|
698
|
-
|
|
699
|
-
/* Place so it Barely Overlaps the Box Border */
|
|
700
|
-
display: inline-block;
|
|
701
|
-
position: relative;
|
|
702
|
-
top: 2px; /* Gives Illusion that Border Doesn't Exist Below Tab */
|
|
864
|
+
/* Align Contents on Left */
|
|
865
|
+
text-align: left;
|
|
703
866
|
|
|
704
|
-
|
|
705
|
-
|
|
867
|
+
/* Add Text Padding */
|
|
868
|
+
padding-left: 0.3rem;
|
|
869
|
+
padding-right: 0.3rem;
|
|
870
|
+
}
|
|
706
871
|
|
|
707
|
-
/*
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
872
|
+
/* Container for Title */
|
|
873
|
+
.TabBox-title-container {
|
|
874
|
+
/* Place on Left */
|
|
875
|
+
position: relative;
|
|
876
|
+
left: 0;
|
|
877
|
+
text-align: left;
|
|
878
|
+
}
|
|
711
879
|
|
|
712
|
-
/*
|
|
713
|
-
|
|
714
|
-
|
|
880
|
+
/* Tab-style Title */
|
|
881
|
+
.TabBox-title {
|
|
882
|
+
/* Place so it Barely Overlaps the Box Border */
|
|
883
|
+
display: inline-block;
|
|
884
|
+
position: relative;
|
|
885
|
+
top: 0.15rem; /* Gives Illusion that Border Doesn't Exist Below Tab */
|
|
715
886
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
padding-right: 12px;
|
|
887
|
+
/* Title-sized Font */
|
|
888
|
+
font-size: 1.5rem;
|
|
719
889
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
890
|
+
/* Add Border on Top and Sides */
|
|
891
|
+
border-top: 0.15rem solid #dedede;
|
|
892
|
+
border-left: 0.15rem solid #dedede;
|
|
893
|
+
border-right: 0.15rem solid #dedede;
|
|
723
894
|
|
|
724
|
-
/*
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
895
|
+
/* Round the Top Corners */
|
|
896
|
+
border-top-left-radius: 0.3rem;
|
|
897
|
+
border-top-right-radius: 0.3rem;
|
|
898
|
+
|
|
899
|
+
/* Add Text Padding */
|
|
900
|
+
padding-left: 0.5rem;
|
|
901
|
+
padding-right: 0.5rem;
|
|
902
|
+
|
|
903
|
+
/* Match Background Color of Box */
|
|
904
|
+
background: #fdfdfd;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
/* Make the TabBox's Children Appear Above Title if Overlap Occurs */
|
|
908
|
+
.TabBox-children {
|
|
909
|
+
position: relative;
|
|
910
|
+
z-index: 1;
|
|
911
|
+
}
|
|
729
912
|
`;
|
|
730
913
|
/*------------------------------------------------------------------------*/
|
|
731
914
|
/* Component */
|
|
@@ -735,7 +918,7 @@ const TabBox = (props) => {
|
|
|
735
918
|
/* Setup */
|
|
736
919
|
/*------------------------------------------------------------------------*/
|
|
737
920
|
/* -------------- Props ------------- */
|
|
738
|
-
const { title, children, } = props;
|
|
921
|
+
const { title, children, noBottomPadding, } = props;
|
|
739
922
|
/*------------------------------------------------------------------------*/
|
|
740
923
|
/* Render */
|
|
741
924
|
/*------------------------------------------------------------------------*/
|
|
@@ -743,7 +926,7 @@ const TabBox = (props) => {
|
|
|
743
926
|
/* Main UI */
|
|
744
927
|
/*----------------------------------------*/
|
|
745
928
|
// Full UI
|
|
746
|
-
return (React__default["default"].createElement("div",
|
|
929
|
+
return (React__default["default"].createElement("div", { className: `TabBox-container ${noBottomPadding ? '' : 'mb-2'}` },
|
|
747
930
|
React__default["default"].createElement("style", null, style),
|
|
748
931
|
React__default["default"].createElement("div", { className: "TabBox-title-container" },
|
|
749
932
|
React__default["default"].createElement("div", { className: "TabBox-title" }, title)),
|
|
@@ -751,6 +934,252 @@ const TabBox = (props) => {
|
|
|
751
934
|
React__default["default"].createElement("div", { className: "TabBox-children" }, children))));
|
|
752
935
|
};
|
|
753
936
|
|
|
937
|
+
/**
|
|
938
|
+
* A radio selection button
|
|
939
|
+
* @author Gabe Abrams
|
|
940
|
+
*/
|
|
941
|
+
/*------------------------------------------------------------------------*/
|
|
942
|
+
/* Component */
|
|
943
|
+
/*------------------------------------------------------------------------*/
|
|
944
|
+
const RadioButton = (props) => {
|
|
945
|
+
/*------------------------------------------------------------------------*/
|
|
946
|
+
/* Setup */
|
|
947
|
+
/*------------------------------------------------------------------------*/
|
|
948
|
+
/* -------------- Props ------------- */
|
|
949
|
+
const { text, onSelected, ariaLabel, title, selected, id, noMarginOnRight, selectedVariant = Variant$1.Secondary, unselectedVariant = Variant$1.Light, small, } = props;
|
|
950
|
+
/*------------------------------------------------------------------------*/
|
|
951
|
+
/* Render */
|
|
952
|
+
/*------------------------------------------------------------------------*/
|
|
953
|
+
/*----------------------------------------*/
|
|
954
|
+
/* Main UI */
|
|
955
|
+
/*----------------------------------------*/
|
|
956
|
+
return (React__default["default"].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: () => {
|
|
957
|
+
if (!selected) {
|
|
958
|
+
onSelected();
|
|
959
|
+
}
|
|
960
|
+
} },
|
|
961
|
+
React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: selected ? freeSolidSvgIcons.faDotCircle : freeRegularSvgIcons.faCircle, className: "me-1" }),
|
|
962
|
+
text));
|
|
963
|
+
};
|
|
964
|
+
|
|
965
|
+
/**
|
|
966
|
+
* A checkbox button
|
|
967
|
+
* @author Gabe Abrams
|
|
968
|
+
*/
|
|
969
|
+
/*------------------------------------------------------------------------*/
|
|
970
|
+
/* Component */
|
|
971
|
+
/*------------------------------------------------------------------------*/
|
|
972
|
+
const CheckboxButton = (props) => {
|
|
973
|
+
/*------------------------------------------------------------------------*/
|
|
974
|
+
/* Setup */
|
|
975
|
+
/*------------------------------------------------------------------------*/
|
|
976
|
+
/* -------------- Props ------------- */
|
|
977
|
+
const { text, onChanged, ariaLabel, title, checked, id, noMarginOnRight, checkedVariant = Variant$1.Secondary, uncheckedVariant = Variant$1.Light, small, } = props;
|
|
978
|
+
/*------------------------------------------------------------------------*/
|
|
979
|
+
/* Render */
|
|
980
|
+
/*------------------------------------------------------------------------*/
|
|
981
|
+
/*----------------------------------------*/
|
|
982
|
+
/* Main UI */
|
|
983
|
+
/*----------------------------------------*/
|
|
984
|
+
return (React__default["default"].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: () => {
|
|
985
|
+
onChanged(!checked);
|
|
986
|
+
} },
|
|
987
|
+
React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: checked ? freeSolidSvgIcons.faCheckSquare : freeRegularSvgIcons.faSquare, className: "me-1" }),
|
|
988
|
+
text));
|
|
989
|
+
};
|
|
990
|
+
|
|
991
|
+
/**
|
|
992
|
+
* Input group with a title and space for buttons
|
|
993
|
+
* @author Gabe Abrams
|
|
994
|
+
*/
|
|
995
|
+
/*------------------------------------------------------------------------*/
|
|
996
|
+
/* Component */
|
|
997
|
+
/*------------------------------------------------------------------------*/
|
|
998
|
+
const ButtonInputGroup = (props) => {
|
|
999
|
+
/*------------------------------------------------------------------------*/
|
|
1000
|
+
/* Setup */
|
|
1001
|
+
/*------------------------------------------------------------------------*/
|
|
1002
|
+
/* -------------- Props ------------- */
|
|
1003
|
+
// Destructure all props
|
|
1004
|
+
const { label, minLabelWidth, children, } = props;
|
|
1005
|
+
/*------------------------------------------------------------------------*/
|
|
1006
|
+
/* Render */
|
|
1007
|
+
/*------------------------------------------------------------------------*/
|
|
1008
|
+
/*----------------------------------------*/
|
|
1009
|
+
/* Main UI */
|
|
1010
|
+
/*----------------------------------------*/
|
|
1011
|
+
return (React__default["default"].createElement("div", { className: "input-group" },
|
|
1012
|
+
React__default["default"].createElement("div", { className: "input-group-prepend d-flex w-100" },
|
|
1013
|
+
React__default["default"].createElement("span", { className: "input-group-text", style: {
|
|
1014
|
+
minWidth: (minLabelWidth !== null && minLabelWidth !== void 0 ? minLabelWidth : undefined),
|
|
1015
|
+
borderTopRightRadius: 0,
|
|
1016
|
+
borderBottomRightRadius: 0,
|
|
1017
|
+
} }, label),
|
|
1018
|
+
React__default["default"].createElement("span", { className: "input-group-text flex-grow-1 rounded-right", style: {
|
|
1019
|
+
backgroundColor: 'white',
|
|
1020
|
+
borderTopLeftRadius: 0,
|
|
1021
|
+
borderBottomLeftRadius: 0,
|
|
1022
|
+
borderLeftWidth: 0,
|
|
1023
|
+
} }, children))));
|
|
1024
|
+
};
|
|
1025
|
+
|
|
1026
|
+
const ORDINALS = ['th', 'st', 'nd', 'rd'];
|
|
1027
|
+
/**
|
|
1028
|
+
* Get a number's ordinal
|
|
1029
|
+
* @author Gabe Abrams
|
|
1030
|
+
* @param num the number being analyzed
|
|
1031
|
+
* @returns ordinal
|
|
1032
|
+
*/
|
|
1033
|
+
const getOrdinal = (num) => {
|
|
1034
|
+
var _a, _b;
|
|
1035
|
+
return ((_b = (_a = ORDINALS[(num - 20) % 10]) !== null && _a !== void 0 ? _a : ORDINALS[num]) !== null && _b !== void 0 ? _b : ORDINALS[0]);
|
|
1036
|
+
};
|
|
1037
|
+
|
|
1038
|
+
/**
|
|
1039
|
+
* Get current time info in US Boston Eastern Time, independent of machine
|
|
1040
|
+
* timezone
|
|
1041
|
+
* @author Gabe Abrams
|
|
1042
|
+
* @param {Date} [date=now] the date to get info on
|
|
1043
|
+
* @returns object with timestamp (ms since epoch) and numbers
|
|
1044
|
+
* corresponding to ET time values for year, month, day, hour, minute
|
|
1045
|
+
*/
|
|
1046
|
+
const getTimeInfoInET = (date) => {
|
|
1047
|
+
// Create a time string
|
|
1048
|
+
const d = (date || new Date());
|
|
1049
|
+
const str = d.toLocaleString('en-US', // Using US encoding (it's the only one installed on containers)
|
|
1050
|
+
{ timeZone: 'America/New_York' } // Force EST timezone
|
|
1051
|
+
);
|
|
1052
|
+
// Parse the string for the date/time info
|
|
1053
|
+
const [dateStr, timeStr] = str.split(', '); // Format: MM/DD/YYYY, HH:MM:SS AM
|
|
1054
|
+
const [monthStr, dayStr, yearStr] = dateStr.split('/'); // Format: MM/DD/YYYY
|
|
1055
|
+
const [hourStr, minStr, ending] = timeStr.split(':'); // Format: HH:MM:SS AM
|
|
1056
|
+
// Create all time numbers
|
|
1057
|
+
const timestamp = d.getTime();
|
|
1058
|
+
const year = Number.parseInt(yearStr, 10);
|
|
1059
|
+
const month = Number.parseInt(monthStr, 10);
|
|
1060
|
+
const day = Number.parseInt(dayStr, 10);
|
|
1061
|
+
const minute = Number.parseInt(minStr, 10);
|
|
1062
|
+
let hour = Number.parseInt(hourStr, 10);
|
|
1063
|
+
// Convert from am/pm to 24hr
|
|
1064
|
+
const isAM = ending.toLowerCase().includes('am');
|
|
1065
|
+
const isPM = !isAM;
|
|
1066
|
+
if (isPM && hour !== 12) {
|
|
1067
|
+
hour += 12;
|
|
1068
|
+
}
|
|
1069
|
+
else if (isAM && hour === 12) {
|
|
1070
|
+
hour = 0;
|
|
1071
|
+
}
|
|
1072
|
+
// Return
|
|
1073
|
+
return {
|
|
1074
|
+
timestamp,
|
|
1075
|
+
year,
|
|
1076
|
+
month,
|
|
1077
|
+
day,
|
|
1078
|
+
hour,
|
|
1079
|
+
minute,
|
|
1080
|
+
};
|
|
1081
|
+
};
|
|
1082
|
+
|
|
1083
|
+
/**
|
|
1084
|
+
* A very simple, lightweight date chooser
|
|
1085
|
+
* @author Gabe Abrams
|
|
1086
|
+
*/
|
|
1087
|
+
/*------------------------------------------------------------------------*/
|
|
1088
|
+
/* Constants */
|
|
1089
|
+
/*------------------------------------------------------------------------*/
|
|
1090
|
+
// Constants
|
|
1091
|
+
const MONTH_NAMES = [
|
|
1092
|
+
'January',
|
|
1093
|
+
'February',
|
|
1094
|
+
'March',
|
|
1095
|
+
'April',
|
|
1096
|
+
'May',
|
|
1097
|
+
'June',
|
|
1098
|
+
'July',
|
|
1099
|
+
'August',
|
|
1100
|
+
'September',
|
|
1101
|
+
'October',
|
|
1102
|
+
'November',
|
|
1103
|
+
'December',
|
|
1104
|
+
];
|
|
1105
|
+
/*------------------------------------------------------------------------*/
|
|
1106
|
+
/* Component */
|
|
1107
|
+
/*------------------------------------------------------------------------*/
|
|
1108
|
+
const SimpleDateChooser = (props) => {
|
|
1109
|
+
/*------------------------------------------------------------------------*/
|
|
1110
|
+
/* Setup */
|
|
1111
|
+
/*------------------------------------------------------------------------*/
|
|
1112
|
+
var _a;
|
|
1113
|
+
/* -------------- Props ------------- */
|
|
1114
|
+
const { ariaLabel, name, month, day, year, onChange, } = props;
|
|
1115
|
+
const numMonthsToShow = Math.min((_a = props.numMonthsToShow) !== null && _a !== void 0 ? _a : 6, 12);
|
|
1116
|
+
/*------------------------------------------------------------------------*/
|
|
1117
|
+
/* Render */
|
|
1118
|
+
/*------------------------------------------------------------------------*/
|
|
1119
|
+
/*----------------------------------------*/
|
|
1120
|
+
/* Main UI */
|
|
1121
|
+
/*----------------------------------------*/
|
|
1122
|
+
// Determine the set of choices allowed
|
|
1123
|
+
const today = getTimeInfoInET();
|
|
1124
|
+
const choices = [];
|
|
1125
|
+
for (let i = 0; i < numMonthsToShow; i++) {
|
|
1126
|
+
// Get month and year info
|
|
1127
|
+
const unmoddedMonth = (today.month + i);
|
|
1128
|
+
const month = (unmoddedMonth > 12
|
|
1129
|
+
? unmoddedMonth - 12
|
|
1130
|
+
: unmoddedMonth);
|
|
1131
|
+
const monthName = MONTH_NAMES[month - 1];
|
|
1132
|
+
const year = (unmoddedMonth > 12
|
|
1133
|
+
? today.year + 1
|
|
1134
|
+
: today.year);
|
|
1135
|
+
// Figure out which days are allowed
|
|
1136
|
+
const days = [];
|
|
1137
|
+
const numDaysInMonth = (new Date(year, month, 0)).getDate();
|
|
1138
|
+
const firstDay = (month === today.month
|
|
1139
|
+
? today.day // Current month: start at current date
|
|
1140
|
+
: 1 // Future month: start at beginning of month
|
|
1141
|
+
);
|
|
1142
|
+
for (let day = firstDay; day <= numDaysInMonth; day++) {
|
|
1143
|
+
days.push(day);
|
|
1144
|
+
}
|
|
1145
|
+
choices.push({
|
|
1146
|
+
choiceName: `${monthName} ${year}`,
|
|
1147
|
+
month,
|
|
1148
|
+
year,
|
|
1149
|
+
days,
|
|
1150
|
+
});
|
|
1151
|
+
}
|
|
1152
|
+
// Create choice options
|
|
1153
|
+
const monthOptions = [];
|
|
1154
|
+
const dayOptions = [];
|
|
1155
|
+
choices.forEach((choice) => {
|
|
1156
|
+
// Create month option
|
|
1157
|
+
monthOptions.push(React__default["default"].createElement("option", { key: choice.month, value: choice.month, "aria-label": `choose ${choice.choiceName}`, onSelect: () => {
|
|
1158
|
+
onChange(choice.month, choice.days[0], choice.year);
|
|
1159
|
+
} }, choice.choiceName));
|
|
1160
|
+
if (month === choice.month) {
|
|
1161
|
+
// This is the currently selected month
|
|
1162
|
+
// Create day options
|
|
1163
|
+
choice.days.forEach((dayChoice) => {
|
|
1164
|
+
const ordinal = getOrdinal(dayChoice);
|
|
1165
|
+
dayOptions.push(React__default["default"].createElement("option", { key: dayChoice, value: dayChoice, "aria-label": `choose date ${dayChoice}` },
|
|
1166
|
+
dayChoice,
|
|
1167
|
+
ordinal));
|
|
1168
|
+
});
|
|
1169
|
+
}
|
|
1170
|
+
});
|
|
1171
|
+
return (React__default["default"].createElement("div", { className: "SimpleDateChooser d-inline-block", "aria-label": `date chooser with selected date: ${month} ${day}, ${year}` },
|
|
1172
|
+
React__default["default"].createElement("select", { "aria-label": `month for ${ariaLabel}`, className: "custom-select d-inline-block mr-1", style: { width: 'auto' }, id: `SimpleDateChooser-${name}-month`, value: month, onChange: (e) => {
|
|
1173
|
+
const choice = choices[e.target.selectedIndex];
|
|
1174
|
+
// Change day, month, and year
|
|
1175
|
+
onChange(choice.month, choice.days[0], choice.year);
|
|
1176
|
+
} }, monthOptions),
|
|
1177
|
+
React__default["default"].createElement("select", { "aria-label": `day for ${ariaLabel}`, className: "custom-select d-inline-block", style: { width: 'auto' }, id: `SimpleDateChooser-${name}-day`, value: day, onChange: (e) => {
|
|
1178
|
+
// Only change the day
|
|
1179
|
+
onChange(month, Number.parseInt(e.target.value, 10), year);
|
|
1180
|
+
} }, dayOptions)));
|
|
1181
|
+
};
|
|
1182
|
+
|
|
754
1183
|
/**
|
|
755
1184
|
* Shorten text so it fits into a certain number of chars
|
|
756
1185
|
* @author Gabe Abrams
|
|
@@ -894,7 +1323,427 @@ const roundToNumDecimals = (num, numDecimals) => {
|
|
|
894
1323
|
return (Math.round(num * rounder) / rounder);
|
|
895
1324
|
};
|
|
896
1325
|
|
|
1326
|
+
// Keep track of whether or not session expiry has already been handled
|
|
1327
|
+
let sessionAlreadyExpired = false;
|
|
1328
|
+
/*------------------------------------------------------------------------*/
|
|
1329
|
+
/* Main */
|
|
1330
|
+
/*------------------------------------------------------------------------*/
|
|
1331
|
+
/**
|
|
1332
|
+
* Visit an endpoint on the server [for client only]
|
|
1333
|
+
* @author Gabe Abrams
|
|
1334
|
+
* @param opts object containing all arguments
|
|
1335
|
+
* @param opts.path - the path of the server endpoint
|
|
1336
|
+
* @param [opts.method=GET] - the method of the endpoint
|
|
1337
|
+
* @param [opts.params] - query/body parameters to include
|
|
1338
|
+
* @returns response from server
|
|
1339
|
+
*/
|
|
1340
|
+
const visitServerEndpoint = (opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1341
|
+
var _a;
|
|
1342
|
+
// Send the request
|
|
1343
|
+
const response = yield cacclSendRequest({
|
|
1344
|
+
path: opts.path,
|
|
1345
|
+
method: (_a = opts.method) !== null && _a !== void 0 ? _a : 'GET',
|
|
1346
|
+
params: opts.params,
|
|
1347
|
+
});
|
|
1348
|
+
// Check for failure
|
|
1349
|
+
if (!response || !response.body) {
|
|
1350
|
+
throw new ErrorWithCode('We didn\'t get a response from the server. Please check your internet connection.', ReactKitErrorCode$1.NoResponse);
|
|
1351
|
+
}
|
|
1352
|
+
if (!response.body.success) {
|
|
1353
|
+
// Session expired
|
|
1354
|
+
if (response.body.code === ReactKitErrorCode$1.SessionExpired) {
|
|
1355
|
+
// Skip notice if session was already expired
|
|
1356
|
+
if (sessionAlreadyExpired) {
|
|
1357
|
+
// Never return (browser is already reloading)
|
|
1358
|
+
yield new Promise(() => {
|
|
1359
|
+
// Promise that never returns
|
|
1360
|
+
});
|
|
1361
|
+
}
|
|
1362
|
+
sessionAlreadyExpired = true;
|
|
1363
|
+
// Show session expiration message
|
|
1364
|
+
{
|
|
1365
|
+
// Fallback to alert
|
|
1366
|
+
// eslint-disable-next-line no-alert
|
|
1367
|
+
alert('Your session has expired. Please start over.');
|
|
1368
|
+
}
|
|
1369
|
+
// Never return (don't continue execution)
|
|
1370
|
+
yield new Promise(() => {
|
|
1371
|
+
// Promise that never returns
|
|
1372
|
+
});
|
|
1373
|
+
}
|
|
1374
|
+
// Other errors
|
|
1375
|
+
throw new ErrorWithCode((response.body.message
|
|
1376
|
+
|| 'An unknown error occurred. Please contact an admin.'), (response.body.code
|
|
1377
|
+
|| ReactKitErrorCode$1.NoCode));
|
|
1378
|
+
}
|
|
1379
|
+
// Success! Extract the body
|
|
1380
|
+
const { body } = response.body;
|
|
1381
|
+
// Return
|
|
1382
|
+
return body;
|
|
1383
|
+
});
|
|
1384
|
+
|
|
1385
|
+
// Import custom error
|
|
1386
|
+
// Stored copy of caccl functions
|
|
1387
|
+
let _cacclGetLaunchInfo;
|
|
1388
|
+
/*------------------------------------------------------------------------*/
|
|
1389
|
+
/* Helpers */
|
|
1390
|
+
/*------------------------------------------------------------------------*/
|
|
1391
|
+
/**
|
|
1392
|
+
* Get launch info via CACCL
|
|
1393
|
+
* @author Gabe Abrams
|
|
1394
|
+
* @param req express request object
|
|
1395
|
+
* @returns object { launched, launchInfo }
|
|
1396
|
+
*/
|
|
1397
|
+
const cacclGetLaunchInfo = (req) => {
|
|
1398
|
+
if (!_cacclGetLaunchInfo) {
|
|
1399
|
+
throw new ErrorWithCode('Could not get launch info because server was not initialized with dce-reactkit\'s initServer function', ReactKitErrorCode$1.NoCACCLGetLaunchInfoFunction);
|
|
1400
|
+
}
|
|
1401
|
+
return _cacclGetLaunchInfo(req);
|
|
1402
|
+
};
|
|
1403
|
+
/*------------------------------------------------------------------------*/
|
|
1404
|
+
/* Main */
|
|
1405
|
+
/*------------------------------------------------------------------------*/
|
|
1406
|
+
/**
|
|
1407
|
+
* Prepare dce-reactkit to run on the server
|
|
1408
|
+
* @author Gabe Abrams
|
|
1409
|
+
* @param opts object containing all arguments
|
|
1410
|
+
* @param opts.getLaunchInfo CACCL LTI's get launch info function
|
|
1411
|
+
*/
|
|
1412
|
+
const initServer = (opts) => {
|
|
1413
|
+
_cacclGetLaunchInfo = opts.getLaunchInfo;
|
|
1414
|
+
};
|
|
1415
|
+
|
|
1416
|
+
/**
|
|
1417
|
+
* Server-side API param types
|
|
1418
|
+
* @author Gabe Abrams
|
|
1419
|
+
*/
|
|
1420
|
+
var ParamType;
|
|
1421
|
+
(function (ParamType) {
|
|
1422
|
+
ParamType["Boolean"] = "boolean";
|
|
1423
|
+
ParamType["BooleanOptional"] = "boolean-optional";
|
|
1424
|
+
ParamType["Float"] = "float";
|
|
1425
|
+
ParamType["FloatOptional"] = "float-optional";
|
|
1426
|
+
ParamType["Int"] = "int";
|
|
1427
|
+
ParamType["IntOptional"] = "int-optional";
|
|
1428
|
+
ParamType["JSON"] = "json";
|
|
1429
|
+
ParamType["JSONOptional"] = "json-optional";
|
|
1430
|
+
ParamType["String"] = "string";
|
|
1431
|
+
ParamType["StringOptional"] = "string-optional";
|
|
1432
|
+
})(ParamType || (ParamType = {}));
|
|
1433
|
+
var ParamType$1 = ParamType;
|
|
1434
|
+
|
|
1435
|
+
// Import shared types
|
|
1436
|
+
/**
|
|
1437
|
+
* Handle an error and respond to the client
|
|
1438
|
+
* @author Gabe Abrams
|
|
1439
|
+
* @param res express response
|
|
1440
|
+
* @param error error info
|
|
1441
|
+
* @param opts.err the error to send to the client
|
|
1442
|
+
* or the error message
|
|
1443
|
+
* @param [opts.code] an error code (only used if err.code is not
|
|
1444
|
+
* included)
|
|
1445
|
+
* @param [opts.status=500] the https status code to use
|
|
1446
|
+
* defined)
|
|
1447
|
+
*/
|
|
1448
|
+
const handleError = (res, error) => {
|
|
1449
|
+
// Get the error message
|
|
1450
|
+
let message;
|
|
1451
|
+
if (error && error.message) {
|
|
1452
|
+
message = (error.message || 'An unknown error occurred.');
|
|
1453
|
+
}
|
|
1454
|
+
else if (typeof error === 'string') {
|
|
1455
|
+
message = (error.trim().length > 0
|
|
1456
|
+
? error
|
|
1457
|
+
: 'An unknown error occurred.');
|
|
1458
|
+
}
|
|
1459
|
+
else {
|
|
1460
|
+
message = 'An unknown error occurred.';
|
|
1461
|
+
}
|
|
1462
|
+
// Get the error code
|
|
1463
|
+
const code = (error.code || ReactKitErrorCode$1.NoCode);
|
|
1464
|
+
// Get the status code
|
|
1465
|
+
const status = (error.status || 500);
|
|
1466
|
+
// Respond to user
|
|
1467
|
+
res
|
|
1468
|
+
// Set the http status code
|
|
1469
|
+
.status(status)
|
|
1470
|
+
// Send a JSON response
|
|
1471
|
+
.json({
|
|
1472
|
+
// Error message
|
|
1473
|
+
message,
|
|
1474
|
+
// Error code
|
|
1475
|
+
code,
|
|
1476
|
+
// Success = false flag so client can detect server-side errors
|
|
1477
|
+
success: false,
|
|
1478
|
+
});
|
|
1479
|
+
return undefined;
|
|
1480
|
+
};
|
|
1481
|
+
|
|
1482
|
+
/**
|
|
1483
|
+
* Send successful API response
|
|
1484
|
+
* @author Gabe Abrams
|
|
1485
|
+
* @param res express response
|
|
1486
|
+
* @param body the body of the response to send to the client
|
|
1487
|
+
*/
|
|
1488
|
+
const handleSuccess = (res, body) => {
|
|
1489
|
+
// Send a http 200 json response
|
|
1490
|
+
res.json({
|
|
1491
|
+
// Include the body as a parameter
|
|
1492
|
+
body,
|
|
1493
|
+
// Success = true flag so client can detect successful responses
|
|
1494
|
+
success: true,
|
|
1495
|
+
});
|
|
1496
|
+
return undefined;
|
|
1497
|
+
};
|
|
1498
|
+
|
|
1499
|
+
/**
|
|
1500
|
+
* Generate an express API route handler
|
|
1501
|
+
* @author Gabe Abrams
|
|
1502
|
+
* @param opts object containing all arguments
|
|
1503
|
+
* @param opts.paramTypes map containing the types for each parameter that is
|
|
1504
|
+
* included in the request (map: param name => type)
|
|
1505
|
+
* @param opts.handler function that processes the request
|
|
1506
|
+
* @returns express route handler that takes the following arguments:
|
|
1507
|
+
* params (map: param name => value), handleSuccess (function for handling
|
|
1508
|
+
* successful requests), handleError (function for handling failed requests),
|
|
1509
|
+
* req (express request object), res (express response object)
|
|
1510
|
+
*/
|
|
1511
|
+
const genRouteHandler = (opts) => {
|
|
1512
|
+
// Return a route handler
|
|
1513
|
+
return (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1514
|
+
var _a;
|
|
1515
|
+
// Output params
|
|
1516
|
+
const output = {};
|
|
1517
|
+
/*----------------------------------------*/
|
|
1518
|
+
/* Parse Params */
|
|
1519
|
+
/*----------------------------------------*/
|
|
1520
|
+
// Process items one by one
|
|
1521
|
+
const paramList = Object.entries((_a = opts.paramTypes) !== null && _a !== void 0 ? _a : {});
|
|
1522
|
+
for (let i = 0; i < paramList.length; i++) {
|
|
1523
|
+
const [name, type] = paramList[i];
|
|
1524
|
+
// Find the value as a string
|
|
1525
|
+
const value = (req.params[name]
|
|
1526
|
+
|| req.query[name]
|
|
1527
|
+
|| req.body[name]);
|
|
1528
|
+
// Parse
|
|
1529
|
+
if (type === ParamType$1.Boolean || type === ParamType$1.BooleanOptional) {
|
|
1530
|
+
// Boolean
|
|
1531
|
+
// Handle case where value doesn't exist
|
|
1532
|
+
if (value === undefined) {
|
|
1533
|
+
if (type === ParamType$1.BooleanOptional) {
|
|
1534
|
+
output[name] = undefined;
|
|
1535
|
+
}
|
|
1536
|
+
else {
|
|
1537
|
+
return handleError(res, {
|
|
1538
|
+
message: `Parameter ${name} is required, but it was not included.`,
|
|
1539
|
+
code: ReactKitErrorCode$1.MissingParameter,
|
|
1540
|
+
status: 422,
|
|
1541
|
+
});
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
else {
|
|
1545
|
+
// Value exists
|
|
1546
|
+
// Simplify value
|
|
1547
|
+
const simpleVal = (String(value)
|
|
1548
|
+
.trim()
|
|
1549
|
+
.toLowerCase());
|
|
1550
|
+
// Parse
|
|
1551
|
+
output[name] = ([
|
|
1552
|
+
'true',
|
|
1553
|
+
'yes',
|
|
1554
|
+
'y',
|
|
1555
|
+
'1',
|
|
1556
|
+
't',
|
|
1557
|
+
].indexOf(simpleVal) >= 0);
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
else if (type === ParamType$1.Float || type === ParamType$1.FloatOptional) {
|
|
1561
|
+
// Float
|
|
1562
|
+
// Handle case where value doesn't exist
|
|
1563
|
+
if (value === undefined) {
|
|
1564
|
+
if (type === ParamType$1.FloatOptional) {
|
|
1565
|
+
output[name] = undefined;
|
|
1566
|
+
}
|
|
1567
|
+
else {
|
|
1568
|
+
return handleError(res, {
|
|
1569
|
+
message: `Parameter ${name} is required, but it was not included.`,
|
|
1570
|
+
code: ReactKitErrorCode$1.MissingParameter,
|
|
1571
|
+
status: 422,
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
else if (!Number.isNaN(Number.parseFloat(String(value)))) {
|
|
1576
|
+
// Value is a number
|
|
1577
|
+
output[name] = Number.parseFloat(String(value));
|
|
1578
|
+
}
|
|
1579
|
+
else {
|
|
1580
|
+
// Issue!
|
|
1581
|
+
return handleError(res, {
|
|
1582
|
+
message: `Request data was malformed: ${name} was not a valid float.`,
|
|
1583
|
+
code: ReactKitErrorCode$1.InvalidParameter,
|
|
1584
|
+
status: 422,
|
|
1585
|
+
});
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
else if (type === ParamType$1.Int || type === ParamType$1.IntOptional) {
|
|
1589
|
+
// Int
|
|
1590
|
+
// Handle case where value doesn't exist
|
|
1591
|
+
if (value === undefined) {
|
|
1592
|
+
if (type === ParamType$1.IntOptional) {
|
|
1593
|
+
output[name] = undefined;
|
|
1594
|
+
}
|
|
1595
|
+
else {
|
|
1596
|
+
return handleError(res, {
|
|
1597
|
+
message: `Parameter ${name} is required, but it was not included.`,
|
|
1598
|
+
code: ReactKitErrorCode$1.MissingParameter,
|
|
1599
|
+
status: 422,
|
|
1600
|
+
});
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
else if (!Number.isNaN(Number.parseInt(String(value), 10))) {
|
|
1604
|
+
// Value is a number
|
|
1605
|
+
output[name] = Number.parseInt(String(value), 10);
|
|
1606
|
+
}
|
|
1607
|
+
else {
|
|
1608
|
+
// Issue!
|
|
1609
|
+
return handleError(res, {
|
|
1610
|
+
message: `Request data was malformed: ${name} was not a valid int.`,
|
|
1611
|
+
code: ReactKitErrorCode$1.InvalidParameter,
|
|
1612
|
+
status: 422,
|
|
1613
|
+
});
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
else if (type === ParamType$1.JSON || type === ParamType$1.JSONOptional) {
|
|
1617
|
+
// Stringified JSON
|
|
1618
|
+
// Handle case where value doesn't exist
|
|
1619
|
+
if (value === undefined) {
|
|
1620
|
+
if (type === ParamType$1.JSONOptional) {
|
|
1621
|
+
output[name] = undefined;
|
|
1622
|
+
}
|
|
1623
|
+
else {
|
|
1624
|
+
return handleError(res, {
|
|
1625
|
+
message: `Parameter ${name} is required, but it was not included.`,
|
|
1626
|
+
code: ReactKitErrorCode$1.MissingParameter,
|
|
1627
|
+
status: 422,
|
|
1628
|
+
});
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
else {
|
|
1632
|
+
// Value exists
|
|
1633
|
+
// Parse
|
|
1634
|
+
try {
|
|
1635
|
+
output[name] = JSON.parse(String(value));
|
|
1636
|
+
}
|
|
1637
|
+
catch (err) {
|
|
1638
|
+
return handleError(res, {
|
|
1639
|
+
message: `Request data was malformed: ${name} was not a valid JSON payload.`,
|
|
1640
|
+
code: ReactKitErrorCode$1.InvalidParameter,
|
|
1641
|
+
status: 422,
|
|
1642
|
+
});
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
else if (type === ParamType$1.String || type === ParamType$1.StringOptional) {
|
|
1647
|
+
// String
|
|
1648
|
+
// Handle case where value doesn't exist
|
|
1649
|
+
if (value === undefined) {
|
|
1650
|
+
if (type === ParamType$1.StringOptional) {
|
|
1651
|
+
output[name] = undefined;
|
|
1652
|
+
}
|
|
1653
|
+
else {
|
|
1654
|
+
return handleError(res, {
|
|
1655
|
+
message: `Parameter ${name} is required, but it was not included.`,
|
|
1656
|
+
code: ReactKitErrorCode$1.MissingParameter,
|
|
1657
|
+
status: 422,
|
|
1658
|
+
});
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
else {
|
|
1662
|
+
// Value exists
|
|
1663
|
+
// Leave as is
|
|
1664
|
+
output[name] = value;
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
else {
|
|
1668
|
+
// No valid data type
|
|
1669
|
+
return handleError(res, {
|
|
1670
|
+
message: `An internal error occurred: we could not determine the type of ${name}.`,
|
|
1671
|
+
code: ReactKitErrorCode$1.InvalidParameter,
|
|
1672
|
+
status: 422,
|
|
1673
|
+
});
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
/*----------------------------------------*/
|
|
1677
|
+
/* Launch Info */
|
|
1678
|
+
/*----------------------------------------*/
|
|
1679
|
+
// Get launch info
|
|
1680
|
+
const { launched, launchInfo } = cacclGetLaunchInfo(req);
|
|
1681
|
+
if (!launched || !launchInfo) {
|
|
1682
|
+
return handleError(res, {
|
|
1683
|
+
message: 'Your session has expired. Please refresh the page and try again.',
|
|
1684
|
+
code: ReactKitErrorCode$1.SessionExpired,
|
|
1685
|
+
status: 440,
|
|
1686
|
+
});
|
|
1687
|
+
}
|
|
1688
|
+
// Error if user info cannot be found
|
|
1689
|
+
if (!launchInfo.userId
|
|
1690
|
+
|| !launchInfo.userFirstName
|
|
1691
|
+
|| !launchInfo.userLastName
|
|
1692
|
+
|| (launchInfo.notInCourse
|
|
1693
|
+
&& !launchInfo.isAdmin)
|
|
1694
|
+
|| (!launchInfo.isTTM
|
|
1695
|
+
&& !launchInfo.isLearner
|
|
1696
|
+
&& !launchInfo.isAdmin)) {
|
|
1697
|
+
return handleError(res, {
|
|
1698
|
+
message: 'Your session was invalid. Please refresh the page and try again.',
|
|
1699
|
+
code: ReactKitErrorCode$1.SessionExpired,
|
|
1700
|
+
status: 440,
|
|
1701
|
+
});
|
|
1702
|
+
}
|
|
1703
|
+
// Add launch info to output
|
|
1704
|
+
output.userId = launchInfo.userId;
|
|
1705
|
+
output.userFirstName = launchInfo.userFirstName;
|
|
1706
|
+
output.userLastName = launchInfo.userLastName;
|
|
1707
|
+
output.isLearner = !!launchInfo.isLearner;
|
|
1708
|
+
output.isTTM = !!launchInfo.isTTM;
|
|
1709
|
+
output.isAdmin = !!launchInfo.isAdmin;
|
|
1710
|
+
output.isWatchingInPrivate = !!(req.session.isWatchingInPrivate);
|
|
1711
|
+
/*----------------------------------------*/
|
|
1712
|
+
/* Require Course Consistency */
|
|
1713
|
+
/*----------------------------------------*/
|
|
1714
|
+
// Make sure the user actually launched from the appropriate course
|
|
1715
|
+
if (output.courseId
|
|
1716
|
+
&& launchInfo.courseId
|
|
1717
|
+
&& output.courseId !== launchInfo.courseId
|
|
1718
|
+
&& !output.isTTM
|
|
1719
|
+
&& !output.isAdmin) {
|
|
1720
|
+
// Course of interest is not the launch course
|
|
1721
|
+
return handleError(res, {
|
|
1722
|
+
message: 'You switched sessions by opening Immersive Classroom in another tab. Please refresh the page and try again.',
|
|
1723
|
+
code: ReactKitErrorCode$1.WrongCourse,
|
|
1724
|
+
status: 401,
|
|
1725
|
+
});
|
|
1726
|
+
}
|
|
1727
|
+
/*------------------------------------------------------------------------*/
|
|
1728
|
+
/* Call handler */
|
|
1729
|
+
/*------------------------------------------------------------------------*/
|
|
1730
|
+
opts.handler({
|
|
1731
|
+
params: output,
|
|
1732
|
+
handleSuccess: (body) => {
|
|
1733
|
+
return handleSuccess(res, body);
|
|
1734
|
+
},
|
|
1735
|
+
handleError: (error) => {
|
|
1736
|
+
return handleError(res, error);
|
|
1737
|
+
},
|
|
1738
|
+
req,
|
|
1739
|
+
res,
|
|
1740
|
+
});
|
|
1741
|
+
});
|
|
1742
|
+
};
|
|
1743
|
+
|
|
897
1744
|
exports.AppWrapper = AppWrapper;
|
|
1745
|
+
exports.ButtonInputGroup = ButtonInputGroup;
|
|
1746
|
+
exports.CheckboxButton = CheckboxButton;
|
|
898
1747
|
exports.ErrorBox = ErrorBox;
|
|
899
1748
|
exports.ErrorWithCode = ErrorWithCode;
|
|
900
1749
|
exports.LoadingSpinner = LoadingSpinner;
|
|
@@ -902,20 +1751,30 @@ exports.Modal = Modal;
|
|
|
902
1751
|
exports.ModalButtonType = ModalButtonType$1;
|
|
903
1752
|
exports.ModalSize = ModalSize$1;
|
|
904
1753
|
exports.ModalType = ModalType$1;
|
|
1754
|
+
exports.ParamType = ParamType$1;
|
|
1755
|
+
exports.RadioButton = RadioButton;
|
|
905
1756
|
exports.ReactKitErrorCode = ReactKitErrorCode$1;
|
|
1757
|
+
exports.SimpleDateChooser = SimpleDateChooser;
|
|
906
1758
|
exports.TabBox = TabBox;
|
|
907
1759
|
exports.Variant = Variant$1;
|
|
908
1760
|
exports.abbreviate = abbreviate;
|
|
909
|
-
exports.alert = alert;
|
|
1761
|
+
exports.alert = alert$1;
|
|
910
1762
|
exports.avg = avg;
|
|
911
1763
|
exports.ceilToNumDecimals = ceilToNumDecimals;
|
|
912
1764
|
exports.confirm = confirm;
|
|
913
1765
|
exports.floorToNumDecimals = floorToNumDecimals;
|
|
914
1766
|
exports.forceNumIntoBounds = forceNumIntoBounds;
|
|
1767
|
+
exports.genRouteHandler = genRouteHandler;
|
|
1768
|
+
exports.getOrdinal = getOrdinal;
|
|
1769
|
+
exports.getTimeInfoInET = getTimeInfoInET;
|
|
1770
|
+
exports.handleError = handleError;
|
|
1771
|
+
exports.handleSuccess = handleSuccess;
|
|
1772
|
+
exports.initServer = initServer;
|
|
915
1773
|
exports.padDecimalZeros = padDecimalZeros;
|
|
916
1774
|
exports.padZerosLeft = padZerosLeft;
|
|
917
1775
|
exports.roundToNumDecimals = roundToNumDecimals;
|
|
918
1776
|
exports.showFatalError = showFatalError;
|
|
919
1777
|
exports.sum = sum;
|
|
1778
|
+
exports.visitServerEndpoint = visitServerEndpoint;
|
|
920
1779
|
exports.waitMs = waitMs;
|
|
921
1780
|
//# sourceMappingURL=index.js.map
|