dce-reactkit 4.0.0-beta.3 → 4.0.1

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 (38) hide show
  1. package/.vscode/settings.json +3 -1
  2. package/dist/cjs/index.js +392 -167
  3. package/dist/cjs/index.js.map +1 -1
  4. package/dist/cjs/types/components/ItemPicker/index.d.ts +1 -0
  5. package/dist/cjs/types/components/Modal/ModalProps.d.ts +2 -0
  6. package/dist/cjs/types/components/SimpleDateChooser.d.ts +3 -1
  7. package/dist/cjs/types/components/SimpleTimeChooser.d.ts +20 -0
  8. package/dist/cjs/types/components/TabBox.d.ts +1 -0
  9. package/dist/cjs/types/helpers/getWordCount.d.ts +10 -0
  10. package/dist/cjs/types/index.d.ts +3 -1
  11. package/dist/cjs/types/types/ReactKitErrorCode.d.ts +3 -1
  12. package/dist/esm/index.js +392 -169
  13. package/dist/esm/index.js.map +1 -1
  14. package/dist/esm/types/components/ItemPicker/index.d.ts +1 -0
  15. package/dist/esm/types/components/Modal/ModalProps.d.ts +2 -0
  16. package/dist/esm/types/components/SimpleDateChooser.d.ts +3 -1
  17. package/dist/esm/types/components/SimpleTimeChooser.d.ts +20 -0
  18. package/dist/esm/types/components/TabBox.d.ts +1 -0
  19. package/dist/esm/types/helpers/getWordCount.d.ts +10 -0
  20. package/dist/esm/types/index.d.ts +3 -1
  21. package/dist/esm/types/types/ReactKitErrorCode.d.ts +3 -1
  22. package/dist/index.d.ts +55 -17
  23. package/package.json +2 -3
  24. package/rollup.config.js +0 -2
  25. package/src/components/IntelliTable.tsx +1 -1
  26. package/src/components/ItemPicker/NestableItemList.tsx +1 -0
  27. package/src/components/ItemPicker/index.tsx +96 -0
  28. package/src/components/LogReviewer.tsx +2 -2
  29. package/src/components/Modal/ModalProps.ts +4 -0
  30. package/src/components/Modal/index.tsx +59 -20
  31. package/src/components/SimpleDateChooser.tsx +62 -26
  32. package/src/components/SimpleTimeChooser.tsx +196 -0
  33. package/src/components/TabBox.tsx +27 -9
  34. package/src/helpers/getWordCount.ts +26 -0
  35. package/src/index.ts +4 -0
  36. package/src/types/LogSource.ts +1 -1
  37. package/src/types/ReactKitErrorCode.tsx +3 -1
  38. package/genEncodedSecret.ts +0 -84
package/dist/cjs/index.js CHANGED
@@ -55,7 +55,7 @@ function __awaiter(thisArg, _arguments, P, generator) {
55
55
  });
56
56
  }
57
57
 
58
- // Highest error code = DRK34
58
+ // Highest error code = DRK36
59
59
  /**
60
60
  * List of error codes built into the react kit
61
61
  * @author Gabe Abrams
@@ -66,6 +66,8 @@ var ReactKitErrorCode;
66
66
  ReactKitErrorCode["NoCode"] = "DRK2";
67
67
  ReactKitErrorCode["SessionExpired"] = "DRK3";
68
68
  ReactKitErrorCode["NoCACCLSendRequestFunction"] = "DRK7";
69
+ ReactKitErrorCode["SimpleDateChooserInvalidDateRange"] = "DRK35";
70
+ ReactKitErrorCode["SimpleDateChooserInvalidNumMonths"] = "DRK36";
69
71
  })(ReactKitErrorCode || (ReactKitErrorCode = {}));
70
72
  var ReactKitErrorCode$1 = ReactKitErrorCode;
71
73
 
@@ -897,6 +899,98 @@ function requireReactDom_development () {
897
899
 
898
900
  var ReactDOM = /*@__PURE__*/getDefaultExportFromCjs(reactDom.exports);
899
901
 
902
+ /**
903
+ * Loading spinner/indicator
904
+ * @author Gabe Abrams
905
+ */
906
+ /*------------------------------------------------------------------------*/
907
+ /* -------------------------------- Style ------------------------------- */
908
+ /*------------------------------------------------------------------------*/
909
+ const style$a = `
910
+ /* Container fades in */
911
+ .LoadingSpinner-container {
912
+ animation-name: LoadingSpinner-container-fade-in;
913
+ animation-duration: 0.3s;
914
+ animation-delay: 0.4s;
915
+ animation-fill-mode: both;
916
+ animation-timing-function: ease-out;
917
+ }
918
+ @keyframes LoadingSpinner-container-fade-in {
919
+ 0% {
920
+ opacity: 0;
921
+ }
922
+ 100% {
923
+ opacity: 1;
924
+ }
925
+ }
926
+
927
+ /* Blips */
928
+ .LoadingSpinner-blip-1,
929
+ .LoadingSpinner-blip-2,
930
+ .LoadingSpinner-blip-3,
931
+ .LoadingSpinner-blip-4 {
932
+ font-size: 1.8rem;
933
+ opacity: 0.6;
934
+ margin-top: 1rem;
935
+ margin-bottom: 1rem;
936
+ }
937
+
938
+ /* First Blip */
939
+ .LoadingSpinner-blip-1 {
940
+ animation: LoadingSpinner-pop-animation 2s infinite;
941
+ }
942
+
943
+ /* Second Blip */
944
+ .LoadingSpinner-blip-2 {
945
+ animation: LoadingSpinner-pop-animation 2s infinite;
946
+ -webkit-animation-delay: 0.1s;
947
+ animation-delay: 0.1s;
948
+ }
949
+
950
+ /* Third Blip */
951
+ .LoadingSpinner-blip-3 {
952
+ animation: LoadingSpinner-pop-animation 2s infinite;
953
+ animation-delay: 0.2s;
954
+ }
955
+
956
+ /* Fourth Blip */
957
+ .LoadingSpinner-blip-4 {
958
+ animation: LoadingSpinner-pop-animation 2s infinite;
959
+ animation-delay: 0.3s;
960
+ }
961
+
962
+ /* Pop Animation for Each Blip */
963
+ @keyframes LoadingSpinner-pop-animation {
964
+ 0% {
965
+ transform: scale(1.0);
966
+ }
967
+ 10% {
968
+ transform: scale(1.5);
969
+ }
970
+ 30% {
971
+ transform: scale(1.0);
972
+ }
973
+ 100% {
974
+ transform: scale(1.0);
975
+ }
976
+ }
977
+ `;
978
+ /*------------------------------------------------------------------------*/
979
+ /* ------------------------------ Component ----------------------------- */
980
+ /*------------------------------------------------------------------------*/
981
+ const LoadingSpinner = () => {
982
+ /*------------------------------------------------------------------------*/
983
+ /* ------------------------------- Render ------------------------------- */
984
+ /*------------------------------------------------------------------------*/
985
+ // Add all four blips to a container
986
+ return (React__default["default"].createElement("div", { className: "text-center LoadingSpinner LoadingSpinner-container" },
987
+ React__default["default"].createElement("style", null, style$a),
988
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCircle, className: "LoadingSpinner-blip-1 me-1" }),
989
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCircle, className: "LoadingSpinner-blip-2 me-1" }),
990
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCircle, className: "LoadingSpinner-blip-3 me-1" }),
991
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCircle, className: "LoadingSpinner-blip-4" })));
992
+ };
993
+
900
994
  /**
901
995
  * Wait for a certain number of ms
902
996
  * @author Gabe Abrams
@@ -1040,6 +1134,7 @@ const BASE_Z_INDEX = 1000000000;
1040
1134
  const BASE_Z_INDEX_ON_TOP = 2000000000;
1041
1135
  // Constants
1042
1136
  const MS_TO_ANIMATE = 200; // Animation duration
1137
+ const MS_TO_WAIT_BEFORE_SHOWING_LOADING_INDICATOR = 1000;
1043
1138
  // Modal type to list of buttons
1044
1139
  const modalTypeToModalButtonTypes = {
1045
1140
  [ModalType$1.Okay]: [
@@ -1153,7 +1248,7 @@ const getNextUniqueId = () => {
1153
1248
  /*------------------------------------------------------------------------*/
1154
1249
  /* -------------------------------- Style ------------------------------- */
1155
1250
  /*------------------------------------------------------------------------*/
1156
- const style$a = `
1251
+ const style$9 = `
1157
1252
  .Modal-backdrop {
1158
1253
  position: fixed;
1159
1254
  top: 0;
@@ -1225,13 +1320,14 @@ const Modal = (props) => {
1225
1320
  /*------------------------------------------------------------------------*/
1226
1321
  var _a;
1227
1322
  /* -------------- Props ------------- */
1228
- const { type = ModalType$1.NoButtons, size = ModalSize$1.Large, title, largeTitle, children, onClose, dontAllowBackdropExit, dontShowXButton, onTopOfOtherModals, } = props;
1323
+ const { type = ModalType$1.NoButtons, size = ModalSize$1.Large, title, largeTitle, children, onClose, dontAllowBackdropExit, dontShowXButton, onTopOfOtherModals, isLoading, isLoadingCancelable, } = props;
1229
1324
  // Determine if no header either
1230
1325
  const noHeader = (!title && type === ModalType$1.NoButtons);
1231
1326
  /* -------------- State ------------- */
1232
1327
  // True if animation is in use
1233
1328
  const [animatingIn, setAnimatingIn] = React.useState(true);
1234
1329
  const [animatingPop, setAnimatingPop] = React.useState(false);
1330
+ const [showModal, setShowModal] = React.useState(false);
1235
1331
  /* -------------- Refs -------------- */
1236
1332
  // Keep track of whether modal is still mounted
1237
1333
  const mounted = React.useRef(false);
@@ -1258,6 +1354,16 @@ const Modal = (props) => {
1258
1354
  if (mounted.current) {
1259
1355
  setAnimatingIn(false);
1260
1356
  }
1357
+ if (isLoading) {
1358
+ // wait before showing modal
1359
+ yield waitMs(MS_TO_WAIT_BEFORE_SHOWING_LOADING_INDICATOR);
1360
+ if (mounted.current) {
1361
+ setShowModal(true);
1362
+ }
1363
+ }
1364
+ else {
1365
+ setShowModal(true);
1366
+ }
1261
1367
  }))();
1262
1368
  return () => {
1263
1369
  mounted.current = false;
@@ -1324,6 +1430,15 @@ const Modal = (props) => {
1324
1430
  else if (animatingPop) {
1325
1431
  animationClass = 'Modal-animating-pop';
1326
1432
  }
1433
+ // default to show close button when not loading and not show close button when loading
1434
+ // unless downShowXButton or isLoadingCancelable
1435
+ const showCloseButton = (
1436
+ // The modal must have an onClose function
1437
+ onClose
1438
+ // ...and the close button is allowed to be visible
1439
+ && !dontShowXButton
1440
+ // ...and should not be shown if the modal is loading and not cancelable
1441
+ && !(isLoading && !isLoadingCancelable));
1327
1442
  // Render the modal
1328
1443
  const contentToRender = (React__default["default"].createElement("div", { className: "modal show", tabIndex: -1, style: {
1329
1444
  zIndex: baseZIndex,
@@ -1332,7 +1447,7 @@ const Modal = (props) => {
1332
1447
  left: 0,
1333
1448
  right: 0,
1334
1449
  } },
1335
- React__default["default"].createElement("style", null, style$a),
1450
+ React__default["default"].createElement("style", null, style$9),
1336
1451
  React__default["default"].createElement("div", { className: `Modal-backdrop ${backdropAnimationClass}`, style: {
1337
1452
  zIndex: baseZIndex + 1,
1338
1453
  }, onClick: () => __awaiter(void 0, void 0, void 0, function* () {
@@ -1350,7 +1465,7 @@ const Modal = (props) => {
1350
1465
  // Handle close
1351
1466
  handleClose(ModalButtonType$1.Cancel);
1352
1467
  }) }),
1353
- React__default["default"].createElement("div", { className: `modal-dialog modal-${size} ${animationClass} modal-dialog-scrollable modal-dialog-centered`, style: {
1468
+ showModal && (React__default["default"].createElement("div", { className: `modal-dialog modal-${size} ${animationClass} modal-dialog-scrollable modal-dialog-centered`, style: {
1354
1469
  zIndex: baseZIndex + 2,
1355
1470
  // Override sizes for even larger for XL
1356
1471
  width: (size === ModalSize$1.ExtraLarge
@@ -1382,7 +1497,7 @@ const Modal = (props) => {
1382
1497
  ? '1.6rem'
1383
1498
  : undefined),
1384
1499
  } }, title),
1385
- (onClose && !dontShowXButton) && (React__default["default"].createElement("button", { type: "button", className: "Modal-x-button btn-close", "aria-label": "Close", style: {
1500
+ showCloseButton && (React__default["default"].createElement("button", { type: "button", className: "Modal-x-button btn-close", "aria-label": "Close", style: {
1386
1501
  backgroundColor: (isDarkModeOn()
1387
1502
  ? 'white'
1388
1503
  : undefined),
@@ -1390,14 +1505,18 @@ const Modal = (props) => {
1390
1505
  // Handle close
1391
1506
  handleClose(ModalButtonType$1.Cancel);
1392
1507
  } })))),
1393
- children && (React__default["default"].createElement("div", { className: "modal-body", style: {
1508
+ React__default["default"].createElement("div", { className: "modal-body", style: {
1394
1509
  color: (isDarkModeOn()
1395
1510
  ? 'white'
1396
1511
  : undefined),
1397
1512
  backgroundColor: (isDarkModeOn()
1398
1513
  ? '#444'
1399
1514
  : undefined),
1400
- } }, children)),
1515
+ } }, isLoading
1516
+ ? (React__default["default"].createElement(React__default["default"].Fragment, null,
1517
+ React__default["default"].createElement(LoadingSpinner, null),
1518
+ React__default["default"].createElement("span", { className: "sr-only" }, "Content loading")))
1519
+ : children),
1401
1520
  footer && (React__default["default"].createElement("div", { className: "modal-footer pt-1 pb-1", style: {
1402
1521
  color: (isDarkModeOn()
1403
1522
  ? 'white'
@@ -1408,7 +1527,7 @@ const Modal = (props) => {
1408
1527
  borderTop: (isDarkModeOn()
1409
1528
  ? '0.1rem solid gray'
1410
1529
  : undefined),
1411
- } }, footer))))));
1530
+ } }, footer)))))));
1412
1531
  // Determine which portal to use
1413
1532
  const portalNumber = id.current % NUM_MODAL_PORTALS;
1414
1533
  // Render in a portal
@@ -1979,7 +2098,7 @@ const showSessionExpiredMessage = () => __awaiter(void 0, void 0, void 0, functi
1979
2098
  /*------------------------------------------------------------------------*/
1980
2099
  /* -------------------------------- Style ------------------------------- */
1981
2100
  /*------------------------------------------------------------------------*/
1982
- const style$9 = `
2101
+ const style$8 = `
1983
2102
  .AppWrapper-leave-to-url-notice {
1984
2103
  opacity: 0;
1985
2104
 
@@ -2193,105 +2312,13 @@ const AppWrapper = (props) => {
2193
2312
  /* --------------- Main UI -------------- */
2194
2313
  /*----------------------------------------*/
2195
2314
  return (React__default["default"].createElement(React__default["default"].Fragment, null,
2196
- React__default["default"].createElement("style", null, style$9),
2315
+ React__default["default"].createElement("style", null, style$8),
2197
2316
  React__default["default"].createElement("style", null, tooltipStyle),
2198
2317
  modal,
2199
2318
  customModalPortals,
2200
2319
  body));
2201
2320
  };
2202
2321
 
2203
- /**
2204
- * Loading spinner/indicator
2205
- * @author Gabe Abrams
2206
- */
2207
- /*------------------------------------------------------------------------*/
2208
- /* -------------------------------- Style ------------------------------- */
2209
- /*------------------------------------------------------------------------*/
2210
- const style$8 = `
2211
- /* Container fades in */
2212
- .LoadingSpinner-container {
2213
- animation-name: LoadingSpinner-container-fade-in;
2214
- animation-duration: 0.3s;
2215
- animation-delay: 0.4s;
2216
- animation-fill-mode: both;
2217
- animation-timing-function: ease-out;
2218
- }
2219
- @keyframes LoadingSpinner-container-fade-in {
2220
- 0% {
2221
- opacity: 0;
2222
- }
2223
- 100% {
2224
- opacity: 1;
2225
- }
2226
- }
2227
-
2228
- /* Blips */
2229
- .LoadingSpinner-blip-1,
2230
- .LoadingSpinner-blip-2,
2231
- .LoadingSpinner-blip-3,
2232
- .LoadingSpinner-blip-4 {
2233
- font-size: 1.8rem;
2234
- opacity: 0.6;
2235
- margin-top: 1rem;
2236
- margin-bottom: 1rem;
2237
- }
2238
-
2239
- /* First Blip */
2240
- .LoadingSpinner-blip-1 {
2241
- animation: LoadingSpinner-pop-animation 2s infinite;
2242
- }
2243
-
2244
- /* Second Blip */
2245
- .LoadingSpinner-blip-2 {
2246
- animation: LoadingSpinner-pop-animation 2s infinite;
2247
- -webkit-animation-delay: 0.1s;
2248
- animation-delay: 0.1s;
2249
- }
2250
-
2251
- /* Third Blip */
2252
- .LoadingSpinner-blip-3 {
2253
- animation: LoadingSpinner-pop-animation 2s infinite;
2254
- animation-delay: 0.2s;
2255
- }
2256
-
2257
- /* Fourth Blip */
2258
- .LoadingSpinner-blip-4 {
2259
- animation: LoadingSpinner-pop-animation 2s infinite;
2260
- animation-delay: 0.3s;
2261
- }
2262
-
2263
- /* Pop Animation for Each Blip */
2264
- @keyframes LoadingSpinner-pop-animation {
2265
- 0% {
2266
- transform: scale(1.0);
2267
- }
2268
- 10% {
2269
- transform: scale(1.5);
2270
- }
2271
- 30% {
2272
- transform: scale(1.0);
2273
- }
2274
- 100% {
2275
- transform: scale(1.0);
2276
- }
2277
- }
2278
- `;
2279
- /*------------------------------------------------------------------------*/
2280
- /* ------------------------------ Component ----------------------------- */
2281
- /*------------------------------------------------------------------------*/
2282
- const LoadingSpinner = () => {
2283
- /*------------------------------------------------------------------------*/
2284
- /* ------------------------------- Render ------------------------------- */
2285
- /*------------------------------------------------------------------------*/
2286
- // Add all four blips to a container
2287
- return (React__default["default"].createElement("div", { className: "text-center LoadingSpinner LoadingSpinner-container" },
2288
- React__default["default"].createElement("style", null, style$8),
2289
- React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCircle, className: "LoadingSpinner-blip-1 me-1" }),
2290
- React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCircle, className: "LoadingSpinner-blip-2 me-1" }),
2291
- React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCircle, className: "LoadingSpinner-blip-3 me-1" }),
2292
- React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCircle, className: "LoadingSpinner-blip-4" })));
2293
- };
2294
-
2295
2322
  /**
2296
2323
  * A box with a tab on the top that holds buttons and other content
2297
2324
  * @author Gabe Abrams
@@ -2323,7 +2350,9 @@ const style$7 = `
2323
2350
 
2324
2351
  /* Container for Title */
2325
2352
  .TabBox-title-container {
2326
- /* Place on Left */
2353
+ display: flex;
2354
+ justify-content: space-between;
2355
+ align-items: center;
2327
2356
  position: relative;
2328
2357
  left: 0;
2329
2358
  text-align: left;
@@ -2356,6 +2385,19 @@ const style$7 = `
2356
2385
  background: #fdfdfd;
2357
2386
  }
2358
2387
 
2388
+ .TabBox-title-right-container {
2389
+ display: flex;
2390
+ flex-direction: row;
2391
+ align-items: bottom;
2392
+ height: 2.4rem;
2393
+ overflow: visible;
2394
+ }
2395
+
2396
+ .TabBox-title-right-contents {
2397
+ margin-right: 0.5rem;
2398
+ margin-bottom: 0.2rem;
2399
+ }
2400
+
2359
2401
  /* Make the TabBox's Children Appear Above Title if Overlap Occurs */
2360
2402
  .TabBox-children {
2361
2403
  position: relative;
@@ -2369,19 +2411,16 @@ const TabBox = (props) => {
2369
2411
  /*------------------------------------------------------------------------*/
2370
2412
  /* -------------------------------- Setup ------------------------------- */
2371
2413
  /*------------------------------------------------------------------------*/
2372
- /* -------------- Props ------------- */
2373
- const { title, children, noBottomPadding, noBottomMargin, } = props;
2414
+ const { title, children, topRightChildren, noBottomPadding, noBottomMargin, } = props;
2374
2415
  /*------------------------------------------------------------------------*/
2375
2416
  /* ------------------------------- Render ------------------------------- */
2376
2417
  /*------------------------------------------------------------------------*/
2377
- /*----------------------------------------*/
2378
- /* --------------- Main UI -------------- */
2379
- /*----------------------------------------*/
2380
- // Full UI
2381
2418
  return (React__default["default"].createElement("div", { className: `TabBox-container ${noBottomMargin ? '' : 'mb-2'}` },
2382
2419
  React__default["default"].createElement("style", null, style$7),
2383
2420
  React__default["default"].createElement("div", { className: "TabBox-title-container" },
2384
- React__default["default"].createElement("div", { className: "TabBox-title" }, title)),
2421
+ React__default["default"].createElement("div", { className: "TabBox-title" }, title),
2422
+ topRightChildren && (React__default["default"].createElement("div", { className: "TabBox-title-right-container" },
2423
+ React__default["default"].createElement("div", { className: "TabBox-title-right-contents" }, topRightChildren)))),
2385
2424
  React__default["default"].createElement("div", { className: `TabBox-box ps-2 pt-2 pe-2 ${noBottomPadding ? '' : 'pb-2'}` },
2386
2425
  React__default["default"].createElement("div", { className: "TabBox-children" }, children))));
2387
2426
  };
@@ -2510,6 +2549,19 @@ const ButtonInputGroup = (props) => {
2510
2549
  : children))));
2511
2550
  };
2512
2551
 
2552
+ // Constants
2553
+ const ORDINALS = ['th', 'st', 'nd', 'rd'];
2554
+ /**
2555
+ * Get a number's ordinal
2556
+ * @author Gabe Abrams
2557
+ * @param num the number being analyzed
2558
+ * @returns ordinal
2559
+ */
2560
+ const getOrdinal = (num) => {
2561
+ var _a, _b;
2562
+ return ((_b = (_a = ORDINALS[(num - 20) % 10]) !== null && _a !== void 0 ? _a : ORDINALS[num]) !== null && _b !== void 0 ? _b : ORDINALS[0]);
2563
+ };
2564
+
2513
2565
  const monthNames = [
2514
2566
  { short: 'Jan', full: 'January' },
2515
2567
  { short: 'Feb', full: 'February' },
@@ -2538,19 +2590,6 @@ const getMonthName = (month) => {
2538
2590
  return ((_a = monthNames[month - 1]) !== null && _a !== void 0 ? _a : monthNames[0]);
2539
2591
  };
2540
2592
 
2541
- // Constants
2542
- const ORDINALS = ['th', 'st', 'nd', 'rd'];
2543
- /**
2544
- * Get a number's ordinal
2545
- * @author Gabe Abrams
2546
- * @param num the number being analyzed
2547
- * @returns ordinal
2548
- */
2549
- const getOrdinal = (num) => {
2550
- var _a, _b;
2551
- return ((_b = (_a = ORDINALS[(num - 20) % 10]) !== null && _a !== void 0 ? _a : ORDINALS[num]) !== null && _b !== void 0 ? _b : ORDINALS[0]);
2552
- };
2553
-
2554
2593
  /**
2555
2594
  * Get current time info in US Boston Eastern Time, independent of machine
2556
2595
  * timezone
@@ -2614,6 +2653,7 @@ const getTimeInfoInET = (dateOrTimestamp) => {
2614
2653
  /**
2615
2654
  * A very simple, lightweight date chooser
2616
2655
  * @author Gabe Abrams
2656
+ * @author Gardenia Liu
2617
2657
  */
2618
2658
  /*------------------------------------------------------------------------*/
2619
2659
  /* ------------------------------ Component ----------------------------- */
@@ -2623,7 +2663,7 @@ const SimpleDateChooser = (props) => {
2623
2663
  /* -------------------------------- Setup ------------------------------- */
2624
2664
  /*------------------------------------------------------------------------*/
2625
2665
  /* -------------- Props ------------- */
2626
- const { ariaLabel, name, onChange, chooseFromPast, numMonthsToShow = 6, } = props;
2666
+ const { ariaLabel, name, onChange, numMonthsToShow = 6, dontAllowFuture, dontAllowPast, } = props;
2627
2667
  /*------------------------------------------------------------------------*/
2628
2668
  /* ------------------------------- Render ------------------------------- */
2629
2669
  /*------------------------------------------------------------------------*/
@@ -2635,14 +2675,28 @@ const SimpleDateChooser = (props) => {
2635
2675
  const choices = [];
2636
2676
  let startYear = today.year;
2637
2677
  let startMonth = today.month;
2638
- if (chooseFromPast) {
2678
+ // Don't allow past or future dates
2679
+ if (dontAllowPast && dontAllowFuture) {
2680
+ throw new ErrorWithCode('No past or future dates allowed', ReactKitErrorCode$1.SimpleDateChooserInvalidDateRange);
2681
+ }
2682
+ // Require numMonthsToShow to be positive
2683
+ if (numMonthsToShow <= 0) {
2684
+ throw new ErrorWithCode('numMonthsToShow must be positive', ReactKitErrorCode$1.SimpleDateChooserInvalidNumMonths);
2685
+ }
2686
+ // Recalculate startMonth and startYear when allowing past dates
2687
+ if (!dontAllowPast) {
2639
2688
  startMonth -= Math.max(0, numMonthsToShow - 1);
2640
2689
  while (startMonth <= 0) {
2641
2690
  startMonth += 12;
2642
2691
  startYear -= 1;
2643
2692
  }
2644
2693
  }
2645
- for (let i = 0; i < numMonthsToShow; i++) {
2694
+ // Calculate total number of months to show
2695
+ let totalMonthsToShow = numMonthsToShow;
2696
+ if (!dontAllowPast && !dontAllowFuture) {
2697
+ totalMonthsToShow = totalMonthsToShow * 2 - 1;
2698
+ }
2699
+ for (let i = 0; i < totalMonthsToShow; i++) {
2646
2700
  // Get month and year info
2647
2701
  const unmoddedMonth = (startMonth + i);
2648
2702
  let month = unmoddedMonth;
@@ -2661,23 +2715,25 @@ const SimpleDateChooser = (props) => {
2661
2715
  // Figure out which days are allowed
2662
2716
  const days = [];
2663
2717
  const numDaysInMonth = (new Date(year, month, 0)).getDate();
2664
- if (chooseFromPast) {
2665
- // Past selection
2666
- const numDaysToAdd = ((month === today.month)
2667
- ? today.day // Current month, only add up to today
2668
- : numDaysInMonth // Past month, add all days
2669
- );
2670
- for (let day = 1; day <= numDaysToAdd; day++) {
2671
- days.push(day);
2718
+ // Current month
2719
+ if (month === today.month && year === today.year) {
2720
+ // Past selection: add all previous days of the month
2721
+ if (!dontAllowPast) {
2722
+ for (let day = 1; day < today.day; day++) {
2723
+ days.push(day);
2724
+ }
2672
2725
  }
2673
- }
2674
- else {
2726
+ days.push(today.day); // Add current day
2675
2727
  // Future selection: add all remaining days of the month
2676
- const firstDay = (month === today.month
2677
- ? today.day // Current month: start at current date
2678
- : 1 // Future month: start at beginning of month
2679
- );
2680
- for (let day = firstDay; day <= numDaysInMonth; day++) {
2728
+ if (!dontAllowFuture) {
2729
+ for (let day = today.day + 1; day <= numDaysInMonth; day++) {
2730
+ days.push(day);
2731
+ }
2732
+ }
2733
+ }
2734
+ else { // Past or future month
2735
+ // Include all days in the month
2736
+ for (let day = 1; day <= numDaysInMonth; day++) {
2681
2737
  days.push(day);
2682
2738
  }
2683
2739
  }
@@ -2697,8 +2753,8 @@ const SimpleDateChooser = (props) => {
2697
2753
  monthOptions.push(React__default["default"].createElement("option", { key: `${choice.year}-${choice.month}`, value: `${choice.month}-${choice.year}`, "aria-label": `choose ${choice.choiceName}`, onSelect: () => {
2698
2754
  onChange(choice.month, choice.days[0], choice.year);
2699
2755
  } }, choice.choiceName));
2756
+ // This is the currently selected month
2700
2757
  if (month === choice.month) {
2701
- // This is the currently selected month
2702
2758
  // Create day options
2703
2759
  choice.days.forEach((dayChoice) => {
2704
2760
  const ordinal = getOrdinal(dayChoice);
@@ -2720,6 +2776,125 @@ const SimpleDateChooser = (props) => {
2720
2776
  } }, dayOptions)));
2721
2777
  };
2722
2778
 
2779
+ /**
2780
+ * Pad a number with zeros on the left (e.g. 5 becomes 05 with 2 digit padding)
2781
+ * @author Gabe Abrams
2782
+ * @param num the number to pad
2783
+ * @param numDigits the minimum number of digits before the decimal
2784
+ * @returns padded number
2785
+ */
2786
+ const padZerosLeft = (num, numDigits) => {
2787
+ // Convert to string
2788
+ let out = String(num);
2789
+ // Add zeros
2790
+ while (out.split('.')[0].length < numDigits) {
2791
+ out = `0${out}`;
2792
+ }
2793
+ // Return
2794
+ return out;
2795
+ };
2796
+
2797
+ /**
2798
+ * A very simple, lightweight time chooser
2799
+ * @author Gardenia Liu
2800
+ */
2801
+ /*------------------------------------------------------------------------*/
2802
+ /* ------------------------------ Constants ----------------------------- */
2803
+ /*------------------------------------------------------------------------*/
2804
+ // Allowed intervals between options
2805
+ const ALLOWED_INTERVALS = [15, 30, 60]; // min
2806
+ // Default interval to use if an unsupported interval is passed in
2807
+ const DEFAULT_INTERVAL = ALLOWED_INTERVALS[0]; // min
2808
+ /*------------------------------------------------------------------------*/
2809
+ /* ------------------------------ Component ----------------------------- */
2810
+ /*------------------------------------------------------------------------*/
2811
+ const SimpleTimeChooser = (props) => {
2812
+ /*------------------------------------------------------------------------*/
2813
+ /* -------------------------------- Setup ------------------------------- */
2814
+ /*------------------------------------------------------------------------*/
2815
+ /* -------------- Props ------------- */
2816
+ const { ariaLabel, name, hour, minute, onChange, } = props;
2817
+ let { intervalMin = DEFAULT_INTERVAL, } = props;
2818
+ // Use default interval if not supported
2819
+ if (!ALLOWED_INTERVALS.includes(intervalMin)) {
2820
+ intervalMin = DEFAULT_INTERVAL;
2821
+ }
2822
+ /*------------------------------------------------------------------------*/
2823
+ /* ------------------------- Component Functions ------------------------ */
2824
+ /*------------------------------------------------------------------------*/
2825
+ /**
2826
+ * Convert number of minutes since midnight into 24hour and minute format
2827
+ * @author Gabe Abrams
2828
+ * @param minSinceMidnight total minutes since midnight
2829
+ * @returns hours (24) and minutes
2830
+ */
2831
+ const convertMinSinceMidnightToHoursAndMin = (minSinceMidnight) => {
2832
+ return {
2833
+ hours: Math.floor(minSinceMidnight / 60),
2834
+ minutes: minSinceMidnight % 60,
2835
+ };
2836
+ };
2837
+ /**
2838
+ * Convert time in minutes into HH:MM format
2839
+ * @author Gardenia Liu
2840
+ * @param totalMinutes total minutes since midnight
2841
+ * @returns formatted time string
2842
+ */
2843
+ const formatTime = (totalMinutes) => {
2844
+ // Handle special cases
2845
+ if (totalMinutes === 0) {
2846
+ return '12:00 Midnight';
2847
+ }
2848
+ if (totalMinutes === 12 * 60) {
2849
+ return '12:00 Noon';
2850
+ }
2851
+ // All normal cases:
2852
+ const timeInfo = convertMinSinceMidnightToHoursAndMin(totalMinutes);
2853
+ let { hours } = timeInfo;
2854
+ const { minutes } = timeInfo;
2855
+ // Process 24hr -> 12hr
2856
+ const isAM = (hours < 12);
2857
+ if (hours === 0) {
2858
+ hours = 12;
2859
+ }
2860
+ else if (hours > 12) {
2861
+ hours %= 12;
2862
+ }
2863
+ // Pad with zeros
2864
+ const paddedMinutes = padZerosLeft(minutes, 2);
2865
+ // Assemble time string
2866
+ return `${hours}:${paddedMinutes} ${isAM ? 'AM' : 'PM'}`;
2867
+ };
2868
+ /*------------------------------------------------------------------------*/
2869
+ /* ------------------------------- Render ------------------------------- */
2870
+ /*------------------------------------------------------------------------*/
2871
+ /*----------------------------------------*/
2872
+ /* --------------- Main UI -------------- */
2873
+ /*----------------------------------------*/
2874
+ // Generate list of time options
2875
+ const times = [];
2876
+ for (let time = 0; time < 24 * 60; time += intervalMin) {
2877
+ times.push(formatTime(time));
2878
+ }
2879
+ // Currently selected time in minutes since midnight
2880
+ const selectedTimeMin = hour * 60 + minute;
2881
+ // Create choice options
2882
+ const timeOptions = times.map((timeString, timeIndex) => {
2883
+ const numMinutesForChoice = timeIndex * intervalMin;
2884
+ // Render the option
2885
+ return (React__default["default"].createElement("option", { key: numMinutesForChoice, value: numMinutesForChoice, "aria-label": `choose ${timeString}` }, timeString));
2886
+ });
2887
+ return (React__default["default"].createElement("div", { className: "SimpleTimeChooser-container", "aria-label": `time chooser with selected time: ${formatTime(selectedTimeMin)}` },
2888
+ React__default["default"].createElement("select", { "aria-label": `time for ${ariaLabel}`, className: "custom-select d-inline-block", style: { width: 'auto' }, id: `SimpleTimeChooser-${name}-time`, value: selectedTimeMin, onChange: (e) => {
2889
+ // Parse selector value (string)
2890
+ const newTime = Number.parseInt(e.target.value, 10);
2891
+ // Convert minutes since midnight to hour and minute
2892
+ const timeInfo = convertMinSinceMidnightToHoursAndMin(newTime);
2893
+ // Notify parent
2894
+ onChange(timeInfo.hours, timeInfo.minutes);
2895
+ } }, timeOptions)));
2896
+ };
2897
+
2723
2898
  /**
2724
2899
  * Drawer container
2725
2900
  * @author Gabe Abrams
@@ -3368,7 +3543,7 @@ const NestableItemList = (props) => {
3368
3543
  React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: childExpanded[item.id] ? freeSolidSvgIcons.faChevronDown : freeSolidSvgIcons.faChevronRight })))),
3369
3544
  React__default["default"].createElement(CheckboxButton, { className: `NestableItemList-CheckboxButton-${item.id}`, text: item.name, checked: item.isGroup ? allChecked(item.children) : item.checked, dashed: item.isGroup ? !noneChecked(item.children) : false, onChanged: (checked) => {
3370
3545
  onChanged(changeChecked(item.id, checked, items));
3371
- }, ariaLabel: `Select ${item.name}`, checkedVariant: Variant$1.Light }),
3546
+ }, ariaLabel: `Select ${item.name}`, checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Light }),
3372
3547
  (item.isGroup && childExpanded[item.id]) && (React__default["default"].createElement("div", { className: "NestableItemList-children-container", style: {
3373
3548
  paddingLeft: '2.2rem',
3374
3549
  } },
@@ -3391,17 +3566,62 @@ const ItemPicker = (props) => {
3391
3566
  /*------------------------------------------------------------------------*/
3392
3567
  /* -------------- Props ------------- */
3393
3568
  // Destructure all props
3394
- const { title, items, onChanged, noBottomMargin, } = props;
3569
+ const { title, items, onChanged, noBottomMargin, hideSelectAllOrNoneButtons, } = props;
3395
3570
  /*------------------------------------------------------------------------*/
3396
3571
  /* ------------------------- Component Functions ------------------------ */
3397
3572
  /*------------------------------------------------------------------------*/
3573
+ /**
3574
+ * Updates the checked state of an item (including all children)
3575
+ * @author Yuen Ler Chow
3576
+ * @param item the item to update
3577
+ * @param checked the new checked state
3578
+ * @returns the updated item
3579
+ */
3580
+ const updateItemChecked = (item, checked) => {
3581
+ if (item.isGroup) {
3582
+ return Object.assign(Object.assign({}, item), { children: item.children.map((child) => {
3583
+ return updateItemChecked(child, checked);
3584
+ }) });
3585
+ }
3586
+ return Object.assign(Object.assign({}, item), { checked });
3587
+ };
3588
+ /**
3589
+ * Selects all items in the list
3590
+ * @author Yuen Ler Chow
3591
+ */
3592
+ const handleSelectAll = () => {
3593
+ const updatedItems = items.map((item) => {
3594
+ return updateItemChecked(item, true);
3595
+ });
3596
+ onChanged(updatedItems);
3597
+ };
3598
+ /**
3599
+ * Deselects all items in the list
3600
+ * @author Yuen Ler Chow
3601
+ */
3602
+ const handleDeselectAll = () => {
3603
+ const updatedItems = items.map((item) => {
3604
+ return updateItemChecked(item, false);
3605
+ });
3606
+ onChanged(updatedItems);
3607
+ };
3398
3608
  /*------------------------------------------------------------------------*/
3399
3609
  /* ------------------------------- Render ------------------------------- */
3400
3610
  /*------------------------------------------------------------------------*/
3401
3611
  /*----------------------------------------*/
3402
3612
  /* --------------- Main UI -------------- */
3403
3613
  /*----------------------------------------*/
3404
- return (React__default["default"].createElement(TabBox, { title: title, noBottomMargin: noBottomMargin },
3614
+ // Select all/none
3615
+ const selectAllOrNone = (!hideSelectAllOrNoneButtons
3616
+ ? (React__default["default"].createElement("div", { className: "d-flex h-100 align-items-end flex-row" },
3617
+ React__default["default"].createElement("div", { className: "d-flex justify-content-end" },
3618
+ React__default["default"].createElement("div", { className: "me-2", style: { fontSize: '1.2rem' } }, "Select"),
3619
+ React__default["default"].createElement("div", { className: "btn-group", role: "group" },
3620
+ React__default["default"].createElement("button", { type: "button", style: { borderRight: '0.1rem solid white' }, "aria-label": "Select all contexts", className: "btn btn-secondary py-0", onClick: handleSelectAll }, "All"),
3621
+ React__default["default"].createElement("button", { type: "button", "aria-label": "Deselect all contexts", className: "btn btn-secondary py-0", onClick: handleDeselectAll }, "None")))))
3622
+ : undefined);
3623
+ // Main UI
3624
+ return (React__default["default"].createElement(TabBox, { title: title, noBottomMargin: noBottomMargin, topRightChildren: selectAllOrNone },
3405
3625
  React__default["default"].createElement("div", { style: { overflowX: 'auto' } },
3406
3626
  React__default["default"].createElement(NestableItemList, { items: items, onChanged: onChanged }))));
3407
3627
  };
@@ -3773,7 +3993,7 @@ const IntelliTable = (props) => {
3773
3993
  param: column.param,
3774
3994
  visible: checked,
3775
3995
  });
3776
- }, checked: columnVisibilityMap[column.param], ariaLabel: `show "${column.title}" column in the ${title} table`, checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Secondary }));
3996
+ }, checked: columnVisibilityMap[column.param], ariaLabel: `show "${column.title}" column in the ${title} table`, checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Light }));
3777
3997
  }),
3778
3998
  React__default["default"].createElement("div", { className: "mt-3" }, "Or you can:"),
3779
3999
  React__default["default"].createElement("button", { type: "button", id: `IntelliTable-${id}-select-all-columns`, className: "btn btn-secondary me-2", "aria-label": `show all columns in the ${title} table`, onClick: () => {
@@ -4769,14 +4989,14 @@ const LogReviewer = (props) => {
4769
4989
  if (expandedFilterDrawer) {
4770
4990
  if (expandedFilterDrawer === FilterDrawer.Date) {
4771
4991
  filterDrawer = (React__default["default"].createElement(TabBox, { title: "Date" },
4772
- React__default["default"].createElement(SimpleDateChooser, { ariaLabel: "filter start date", name: "filter-start-date", year: dateFilterState.startDate.year, month: dateFilterState.startDate.month, day: dateFilterState.startDate.day, chooseFromPast: true, numMonthsToShow: 36, onChange: (month, day, year) => {
4992
+ React__default["default"].createElement(SimpleDateChooser, { ariaLabel: "filter start date", name: "filter-start-date", year: dateFilterState.startDate.year, month: dateFilterState.startDate.month, day: dateFilterState.startDate.day, dontAllowFuture: true, numMonthsToShow: 36, onChange: (month, day, year) => {
4773
4993
  dateFilterState.startDate = { month, day, year };
4774
4994
  handleDateRangeUpdated(dateFilterState);
4775
4995
  } }),
4776
4996
  ' ',
4777
4997
  "to",
4778
4998
  ' ',
4779
- React__default["default"].createElement(SimpleDateChooser, { ariaLabel: "filter end date", name: "filter-end-date", year: dateFilterState.endDate.year, month: dateFilterState.endDate.month, day: dateFilterState.endDate.day, chooseFromPast: true, numMonthsToShow: 12, onChange: (month, day, year) => {
4999
+ React__default["default"].createElement(SimpleDateChooser, { ariaLabel: "filter end date", name: "filter-end-date", year: dateFilterState.endDate.year, month: dateFilterState.endDate.month, day: dateFilterState.endDate.day, dontAllowFuture: true, numMonthsToShow: 12, onChange: (month, day, year) => {
4780
5000
  if (year < dateFilterState.startDate.year
4781
5001
  || (year === dateFilterState.startDate.year
4782
5002
  && month < dateFilterState.startDate.month)
@@ -15256,24 +15476,6 @@ const padDecimalZeros = (num, numDigits) => {
15256
15476
  return out;
15257
15477
  };
15258
15478
 
15259
- /**
15260
- * Pad a number with zeros on the left (e.g. 5 becomes 05 with 2 digit padding)
15261
- * @author Gabe Abrams
15262
- * @param num the number to pad
15263
- * @param numDigits the minimum number of digits before the decimal
15264
- * @returns padded number
15265
- */
15266
- const padZerosLeft = (num, numDigits) => {
15267
- // Convert to string
15268
- let out = String(num);
15269
- // Add zeros
15270
- while (out.split('.')[0].length < numDigits) {
15271
- out = `0${out}`;
15272
- }
15273
- // Return
15274
- return out;
15275
- };
15276
-
15277
15479
  /**
15278
15480
  * Stub a server endpoint response
15279
15481
  * @author Gabe Abrams
@@ -16107,6 +16309,27 @@ const shuffleArray = (arr) => {
16107
16309
  return newArr;
16108
16310
  };
16109
16311
 
16312
+ const punctuationRegex = /[!@#$%^&*(),.?\/;:'"\-\[\]]/g;
16313
+ /**
16314
+ * Get number of words in string
16315
+ * @author Gardenia Liu
16316
+ * @author Allison Zhang
16317
+ * @author Gabe Abrams
16318
+ * @param text the string to check
16319
+ * @returns number of words in the string
16320
+ */
16321
+ const getWordCount = (text) => {
16322
+ const trimmedTextWithoutPunctuation = (text
16323
+ // Remove leading and trailing whitespace
16324
+ .trim()
16325
+ // Remove punctuation
16326
+ .replace(punctuationRegex, ''));
16327
+ if (trimmedTextWithoutPunctuation.length === 0) {
16328
+ return 0;
16329
+ }
16330
+ return trimmedTextWithoutPunctuation.split(/\s+/g).length;
16331
+ };
16332
+
16110
16333
  /**
16111
16334
  * Days of the week
16112
16335
  * @author Gabe Abrams
@@ -16165,6 +16388,7 @@ exports.PopSuccessMark = PopSuccessMark;
16165
16388
  exports.RadioButton = RadioButton;
16166
16389
  exports.ReactKitErrorCode = ReactKitErrorCode$1;
16167
16390
  exports.SimpleDateChooser = SimpleDateChooser;
16391
+ exports.SimpleTimeChooser = SimpleTimeChooser;
16168
16392
  exports.TabBox = TabBox;
16169
16393
  exports.ToggleSwitch = ToggleSwitch;
16170
16394
  exports.Tooltip = Tooltip;
@@ -16193,6 +16417,7 @@ exports.getMonthName = getMonthName;
16193
16417
  exports.getOrdinal = getOrdinal;
16194
16418
  exports.getPartOfDay = getPartOfDay;
16195
16419
  exports.getTimeInfoInET = getTimeInfoInET;
16420
+ exports.getWordCount = getWordCount;
16196
16421
  exports.idify = idify;
16197
16422
  exports.initClient = initClient;
16198
16423
  exports.isMobileOrTablet = isMobileOrTablet;