dce-reactkit 4.0.0-beta.3 → 4.0.0

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.
@@ -14,6 +14,7 @@ type Props = {
14
14
  */
15
15
  onChanged: (updatedItems: PickableItem[]) => void;
16
16
  noBottomMargin?: boolean;
17
+ hideSelectAllOrNoneButtons?: boolean;
17
18
  };
18
19
  declare const ItemPicker: React.FC<Props>;
19
20
  export default ItemPicker;
@@ -38,5 +38,7 @@ type ModalProps = {
38
38
  confirmLabel?: string;
39
39
  confirmVariant?: Variant;
40
40
  onTopOfOtherModals?: boolean;
41
+ isLoading?: boolean;
42
+ isLoadingCancelable?: boolean;
41
43
  };
42
44
  export default ModalProps;
@@ -6,6 +6,7 @@ import React from 'react';
6
6
  type Props = {
7
7
  title: React.ReactNode;
8
8
  children: React.ReactNode;
9
+ topRightChildren?: React.ReactNode;
9
10
  noBottomMargin?: boolean;
10
11
  noBottomPadding?: boolean;
11
12
  };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Get number of words in string
3
+ * @author Gardenia Liu
4
+ * @author Allison Zhang
5
+ * @author Gabe Abrams
6
+ * @param text the string to check
7
+ * @returns number of words in the string
8
+ */
9
+ declare const getWordCount: (text: string) => number;
10
+ export default getWordCount;
@@ -75,6 +75,7 @@ import mapAsync from './helpers/asyncArrayFunctions/mapAsync';
75
75
  import someAsync from './helpers/asyncArrayFunctions/someAsync';
76
76
  import capitalize from './helpers/capitalize';
77
77
  import shuffleArray from './helpers/shuffleArray';
78
+ import getWordCount from './helpers/getWordCount';
78
79
  import ParamType from './types/ParamType';
79
80
  import ModalButtonType from './types/ModalButtonType';
80
81
  import ModalSize from './types/ModalSize';
@@ -99,4 +100,4 @@ import PickableItem from './components/ItemPicker/types/PickableItem';
99
100
  import DBEntry from './components/DBEntryManagerPanel/types/DBEntry';
100
101
  import DBEntryField from './components/DBEntryManagerPanel/types/DBEntryField';
101
102
  import DBEntryFieldType from './components/DBEntryManagerPanel/types/DBEntryFieldType';
102
- export { AppWrapper, LoadingSpinner, ErrorBox, Modal, TabBox, RadioButton, CheckboxButton, ButtonInputGroup, SimpleDateChooser, Drawer, PopSuccessMark, PopFailureMark, PopPendingMark, CopiableBox, ItemPicker, LogReviewer, IntelliTable, CSVDownloadButton, DBEntryManagerPanel, Tooltip, ToggleSwitch, AutoscrollToBottomContainer, MultiSwitch, Dropdown, alert, prompt, confirm, showFatalError, ErrorWithCode, MINUTE_IN_MS, HOUR_IN_MS, DAY_IN_MS, LOG_REVIEW_ROUTE_PATH_PREFIX, LOG_ROUTE_PATH, LOG_REVIEW_STATUS_ROUTE, DynamicWord, abbreviate, avg, ceilToNumDecimals, floorToNumDecimals, forceNumIntoBounds, padDecimalZeros, padZerosLeft, roundToNumDecimals, sum, waitMs, getOrdinal, getTimeInfoInET, stubServerEndpoint, startMinWait, getHumanReadableDate, getPartOfDay, stringsToHumanReadableList, onlyKeepLetters, parallelLimit, getMonthName, genCSV, canReviewLogs, isMobileOrTablet, extractProp, compareArraysByProp, genCommaList, validateEmail, validatePhoneNumber, validateString, getLocalTimeInfo, idify, makeLinksClickable, prefixWithAOrAn, everyAsync, filterAsync, forEachAsync, mapAsync, someAsync, capitalize, shuffleArray, initClient, visitServerEndpoint, logClientEvent, addFatalErrorHandler, leaveToURL, combineClassNames, useForceRender, setClientEventMetadataPopulator, ParamType, ModalButtonType, ModalSize, ModalType, ReactKitErrorCode, Variant, DayOfWeek, Log, LogType, LogSource, LogAction, LogBuiltInMetadata, LogMetadataType, LogFunction, LogTypeSpecificInfo, LogMainInfo, LogSourceSpecificInfo, LogLevel, IntelliTableColumn, DropdownItemType, PickableItem, DBEntry, DBEntryField, DBEntryFieldType, };
103
+ export { AppWrapper, LoadingSpinner, ErrorBox, Modal, TabBox, RadioButton, CheckboxButton, ButtonInputGroup, SimpleDateChooser, Drawer, PopSuccessMark, PopFailureMark, PopPendingMark, CopiableBox, ItemPicker, LogReviewer, IntelliTable, CSVDownloadButton, DBEntryManagerPanel, Tooltip, ToggleSwitch, AutoscrollToBottomContainer, MultiSwitch, Dropdown, alert, prompt, confirm, showFatalError, ErrorWithCode, MINUTE_IN_MS, HOUR_IN_MS, DAY_IN_MS, LOG_REVIEW_ROUTE_PATH_PREFIX, LOG_ROUTE_PATH, LOG_REVIEW_STATUS_ROUTE, DynamicWord, abbreviate, avg, ceilToNumDecimals, floorToNumDecimals, forceNumIntoBounds, padDecimalZeros, padZerosLeft, roundToNumDecimals, sum, waitMs, getOrdinal, getTimeInfoInET, stubServerEndpoint, startMinWait, getHumanReadableDate, getPartOfDay, stringsToHumanReadableList, onlyKeepLetters, parallelLimit, getMonthName, genCSV, canReviewLogs, isMobileOrTablet, extractProp, compareArraysByProp, genCommaList, validateEmail, validatePhoneNumber, validateString, getLocalTimeInfo, idify, makeLinksClickable, prefixWithAOrAn, everyAsync, filterAsync, forEachAsync, mapAsync, someAsync, capitalize, shuffleArray, getWordCount, initClient, visitServerEndpoint, logClientEvent, addFatalErrorHandler, leaveToURL, combineClassNames, useForceRender, setClientEventMetadataPopulator, ParamType, ModalButtonType, ModalSize, ModalType, ReactKitErrorCode, Variant, DayOfWeek, Log, LogType, LogSource, LogAction, LogBuiltInMetadata, LogMetadataType, LogFunction, LogTypeSpecificInfo, LogMainInfo, LogSourceSpecificInfo, LogLevel, IntelliTableColumn, DropdownItemType, PickableItem, DBEntry, DBEntryField, DBEntryFieldType, };
package/dist/esm/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import React__default, { useState, useRef, useEffect, useReducer, forwardRef, useContext, useLayoutEffect, createContext, useMemo, useCallback, Component, Fragment } from 'react';
3
- import { faExclamationTriangle, faHourglassEnd, faCircle, faDotCircle, faCheckSquare, faHourglass, faClipboard, faChevronDown, faChevronRight, faCloudDownloadAlt, faMinus, faCheckCircle, faXmarkCircle, faSort, faSortDown, faSortUp, faCalendar, faTag, faHammer, faList, faTimes, faSave, faTrash, faCog, faPlus } from '@fortawesome/free-solid-svg-icons';
3
+ import { faExclamationTriangle, faCircle, faHourglassEnd, faDotCircle, faCheckSquare, faHourglass, faClipboard, faChevronDown, faChevronRight, faCloudDownloadAlt, faMinus, faCheckCircle, faXmarkCircle, faSort, faSortDown, faSortUp, faCalendar, faTag, faHammer, faList, faTimes, faSave, faTrash, faCog, faPlus } from '@fortawesome/free-solid-svg-icons';
4
4
  import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
5
5
  import { faCircle as faCircle$1, faSquareMinus, faSquare } from '@fortawesome/free-regular-svg-icons';
6
6
 
@@ -871,6 +871,98 @@ function requireReactDom_development () {
871
871
 
872
872
  var ReactDOM = /*@__PURE__*/getDefaultExportFromCjs(reactDom.exports);
873
873
 
874
+ /**
875
+ * Loading spinner/indicator
876
+ * @author Gabe Abrams
877
+ */
878
+ /*------------------------------------------------------------------------*/
879
+ /* -------------------------------- Style ------------------------------- */
880
+ /*------------------------------------------------------------------------*/
881
+ const style$a = `
882
+ /* Container fades in */
883
+ .LoadingSpinner-container {
884
+ animation-name: LoadingSpinner-container-fade-in;
885
+ animation-duration: 0.3s;
886
+ animation-delay: 0.4s;
887
+ animation-fill-mode: both;
888
+ animation-timing-function: ease-out;
889
+ }
890
+ @keyframes LoadingSpinner-container-fade-in {
891
+ 0% {
892
+ opacity: 0;
893
+ }
894
+ 100% {
895
+ opacity: 1;
896
+ }
897
+ }
898
+
899
+ /* Blips */
900
+ .LoadingSpinner-blip-1,
901
+ .LoadingSpinner-blip-2,
902
+ .LoadingSpinner-blip-3,
903
+ .LoadingSpinner-blip-4 {
904
+ font-size: 1.8rem;
905
+ opacity: 0.6;
906
+ margin-top: 1rem;
907
+ margin-bottom: 1rem;
908
+ }
909
+
910
+ /* First Blip */
911
+ .LoadingSpinner-blip-1 {
912
+ animation: LoadingSpinner-pop-animation 2s infinite;
913
+ }
914
+
915
+ /* Second Blip */
916
+ .LoadingSpinner-blip-2 {
917
+ animation: LoadingSpinner-pop-animation 2s infinite;
918
+ -webkit-animation-delay: 0.1s;
919
+ animation-delay: 0.1s;
920
+ }
921
+
922
+ /* Third Blip */
923
+ .LoadingSpinner-blip-3 {
924
+ animation: LoadingSpinner-pop-animation 2s infinite;
925
+ animation-delay: 0.2s;
926
+ }
927
+
928
+ /* Fourth Blip */
929
+ .LoadingSpinner-blip-4 {
930
+ animation: LoadingSpinner-pop-animation 2s infinite;
931
+ animation-delay: 0.3s;
932
+ }
933
+
934
+ /* Pop Animation for Each Blip */
935
+ @keyframes LoadingSpinner-pop-animation {
936
+ 0% {
937
+ transform: scale(1.0);
938
+ }
939
+ 10% {
940
+ transform: scale(1.5);
941
+ }
942
+ 30% {
943
+ transform: scale(1.0);
944
+ }
945
+ 100% {
946
+ transform: scale(1.0);
947
+ }
948
+ }
949
+ `;
950
+ /*------------------------------------------------------------------------*/
951
+ /* ------------------------------ Component ----------------------------- */
952
+ /*------------------------------------------------------------------------*/
953
+ const LoadingSpinner = () => {
954
+ /*------------------------------------------------------------------------*/
955
+ /* ------------------------------- Render ------------------------------- */
956
+ /*------------------------------------------------------------------------*/
957
+ // Add all four blips to a container
958
+ return (React__default.createElement("div", { className: "text-center LoadingSpinner LoadingSpinner-container" },
959
+ React__default.createElement("style", null, style$a),
960
+ React__default.createElement(FontAwesomeIcon, { icon: faCircle, className: "LoadingSpinner-blip-1 me-1" }),
961
+ React__default.createElement(FontAwesomeIcon, { icon: faCircle, className: "LoadingSpinner-blip-2 me-1" }),
962
+ React__default.createElement(FontAwesomeIcon, { icon: faCircle, className: "LoadingSpinner-blip-3 me-1" }),
963
+ React__default.createElement(FontAwesomeIcon, { icon: faCircle, className: "LoadingSpinner-blip-4" })));
964
+ };
965
+
874
966
  /**
875
967
  * Wait for a certain number of ms
876
968
  * @author Gabe Abrams
@@ -1014,6 +1106,7 @@ const BASE_Z_INDEX = 1000000000;
1014
1106
  const BASE_Z_INDEX_ON_TOP = 2000000000;
1015
1107
  // Constants
1016
1108
  const MS_TO_ANIMATE = 200; // Animation duration
1109
+ const MS_TO_WAIT_BEFORE_SHOWING_LOADING_INDICATOR = 1000;
1017
1110
  // Modal type to list of buttons
1018
1111
  const modalTypeToModalButtonTypes = {
1019
1112
  [ModalType$1.Okay]: [
@@ -1127,7 +1220,7 @@ const getNextUniqueId = () => {
1127
1220
  /*------------------------------------------------------------------------*/
1128
1221
  /* -------------------------------- Style ------------------------------- */
1129
1222
  /*------------------------------------------------------------------------*/
1130
- const style$a = `
1223
+ const style$9 = `
1131
1224
  .Modal-backdrop {
1132
1225
  position: fixed;
1133
1226
  top: 0;
@@ -1199,13 +1292,14 @@ const Modal = (props) => {
1199
1292
  /*------------------------------------------------------------------------*/
1200
1293
  var _a;
1201
1294
  /* -------------- Props ------------- */
1202
- const { type = ModalType$1.NoButtons, size = ModalSize$1.Large, title, largeTitle, children, onClose, dontAllowBackdropExit, dontShowXButton, onTopOfOtherModals, } = props;
1295
+ const { type = ModalType$1.NoButtons, size = ModalSize$1.Large, title, largeTitle, children, onClose, dontAllowBackdropExit, dontShowXButton, onTopOfOtherModals, isLoading, isLoadingCancelable, } = props;
1203
1296
  // Determine if no header either
1204
1297
  const noHeader = (!title && type === ModalType$1.NoButtons);
1205
1298
  /* -------------- State ------------- */
1206
1299
  // True if animation is in use
1207
1300
  const [animatingIn, setAnimatingIn] = useState(true);
1208
1301
  const [animatingPop, setAnimatingPop] = useState(false);
1302
+ const [showModal, setShowModal] = useState(false);
1209
1303
  /* -------------- Refs -------------- */
1210
1304
  // Keep track of whether modal is still mounted
1211
1305
  const mounted = useRef(false);
@@ -1232,6 +1326,16 @@ const Modal = (props) => {
1232
1326
  if (mounted.current) {
1233
1327
  setAnimatingIn(false);
1234
1328
  }
1329
+ if (isLoading) {
1330
+ // wait before showing modal
1331
+ yield waitMs(MS_TO_WAIT_BEFORE_SHOWING_LOADING_INDICATOR);
1332
+ if (mounted.current) {
1333
+ setShowModal(true);
1334
+ }
1335
+ }
1336
+ else {
1337
+ setShowModal(true);
1338
+ }
1235
1339
  }))();
1236
1340
  return () => {
1237
1341
  mounted.current = false;
@@ -1298,6 +1402,15 @@ const Modal = (props) => {
1298
1402
  else if (animatingPop) {
1299
1403
  animationClass = 'Modal-animating-pop';
1300
1404
  }
1405
+ // default to show close button when not loading and not show close button when loading
1406
+ // unless downShowXButton or isLoadingCancelable
1407
+ const showCloseButton = (
1408
+ // The modal must have an onClose function
1409
+ onClose
1410
+ // ...and the close button is allowed to be visible
1411
+ && !dontShowXButton
1412
+ // ...and should not be shown if the modal is loading and not cancelable
1413
+ && !(isLoading && !isLoadingCancelable));
1301
1414
  // Render the modal
1302
1415
  const contentToRender = (React__default.createElement("div", { className: "modal show", tabIndex: -1, style: {
1303
1416
  zIndex: baseZIndex,
@@ -1306,7 +1419,7 @@ const Modal = (props) => {
1306
1419
  left: 0,
1307
1420
  right: 0,
1308
1421
  } },
1309
- React__default.createElement("style", null, style$a),
1422
+ React__default.createElement("style", null, style$9),
1310
1423
  React__default.createElement("div", { className: `Modal-backdrop ${backdropAnimationClass}`, style: {
1311
1424
  zIndex: baseZIndex + 1,
1312
1425
  }, onClick: () => __awaiter(void 0, void 0, void 0, function* () {
@@ -1324,7 +1437,7 @@ const Modal = (props) => {
1324
1437
  // Handle close
1325
1438
  handleClose(ModalButtonType$1.Cancel);
1326
1439
  }) }),
1327
- React__default.createElement("div", { className: `modal-dialog modal-${size} ${animationClass} modal-dialog-scrollable modal-dialog-centered`, style: {
1440
+ showModal && (React__default.createElement("div", { className: `modal-dialog modal-${size} ${animationClass} modal-dialog-scrollable modal-dialog-centered`, style: {
1328
1441
  zIndex: baseZIndex + 2,
1329
1442
  // Override sizes for even larger for XL
1330
1443
  width: (size === ModalSize$1.ExtraLarge
@@ -1356,7 +1469,7 @@ const Modal = (props) => {
1356
1469
  ? '1.6rem'
1357
1470
  : undefined),
1358
1471
  } }, title),
1359
- (onClose && !dontShowXButton) && (React__default.createElement("button", { type: "button", className: "Modal-x-button btn-close", "aria-label": "Close", style: {
1472
+ showCloseButton && (React__default.createElement("button", { type: "button", className: "Modal-x-button btn-close", "aria-label": "Close", style: {
1360
1473
  backgroundColor: (isDarkModeOn()
1361
1474
  ? 'white'
1362
1475
  : undefined),
@@ -1364,14 +1477,18 @@ const Modal = (props) => {
1364
1477
  // Handle close
1365
1478
  handleClose(ModalButtonType$1.Cancel);
1366
1479
  } })))),
1367
- children && (React__default.createElement("div", { className: "modal-body", style: {
1480
+ React__default.createElement("div", { className: "modal-body", style: {
1368
1481
  color: (isDarkModeOn()
1369
1482
  ? 'white'
1370
1483
  : undefined),
1371
1484
  backgroundColor: (isDarkModeOn()
1372
1485
  ? '#444'
1373
1486
  : undefined),
1374
- } }, children)),
1487
+ } }, isLoading
1488
+ ? (React__default.createElement(React__default.Fragment, null,
1489
+ React__default.createElement(LoadingSpinner, null),
1490
+ React__default.createElement("span", { className: "sr-only" }, "Content loading")))
1491
+ : children),
1375
1492
  footer && (React__default.createElement("div", { className: "modal-footer pt-1 pb-1", style: {
1376
1493
  color: (isDarkModeOn()
1377
1494
  ? 'white'
@@ -1382,7 +1499,7 @@ const Modal = (props) => {
1382
1499
  borderTop: (isDarkModeOn()
1383
1500
  ? '0.1rem solid gray'
1384
1501
  : undefined),
1385
- } }, footer))))));
1502
+ } }, footer)))))));
1386
1503
  // Determine which portal to use
1387
1504
  const portalNumber = id.current % NUM_MODAL_PORTALS;
1388
1505
  // Render in a portal
@@ -1953,7 +2070,7 @@ const showSessionExpiredMessage = () => __awaiter(void 0, void 0, void 0, functi
1953
2070
  /*------------------------------------------------------------------------*/
1954
2071
  /* -------------------------------- Style ------------------------------- */
1955
2072
  /*------------------------------------------------------------------------*/
1956
- const style$9 = `
2073
+ const style$8 = `
1957
2074
  .AppWrapper-leave-to-url-notice {
1958
2075
  opacity: 0;
1959
2076
 
@@ -2167,105 +2284,13 @@ const AppWrapper = (props) => {
2167
2284
  /* --------------- Main UI -------------- */
2168
2285
  /*----------------------------------------*/
2169
2286
  return (React__default.createElement(React__default.Fragment, null,
2170
- React__default.createElement("style", null, style$9),
2287
+ React__default.createElement("style", null, style$8),
2171
2288
  React__default.createElement("style", null, tooltipStyle),
2172
2289
  modal,
2173
2290
  customModalPortals,
2174
2291
  body));
2175
2292
  };
2176
2293
 
2177
- /**
2178
- * Loading spinner/indicator
2179
- * @author Gabe Abrams
2180
- */
2181
- /*------------------------------------------------------------------------*/
2182
- /* -------------------------------- Style ------------------------------- */
2183
- /*------------------------------------------------------------------------*/
2184
- const style$8 = `
2185
- /* Container fades in */
2186
- .LoadingSpinner-container {
2187
- animation-name: LoadingSpinner-container-fade-in;
2188
- animation-duration: 0.3s;
2189
- animation-delay: 0.4s;
2190
- animation-fill-mode: both;
2191
- animation-timing-function: ease-out;
2192
- }
2193
- @keyframes LoadingSpinner-container-fade-in {
2194
- 0% {
2195
- opacity: 0;
2196
- }
2197
- 100% {
2198
- opacity: 1;
2199
- }
2200
- }
2201
-
2202
- /* Blips */
2203
- .LoadingSpinner-blip-1,
2204
- .LoadingSpinner-blip-2,
2205
- .LoadingSpinner-blip-3,
2206
- .LoadingSpinner-blip-4 {
2207
- font-size: 1.8rem;
2208
- opacity: 0.6;
2209
- margin-top: 1rem;
2210
- margin-bottom: 1rem;
2211
- }
2212
-
2213
- /* First Blip */
2214
- .LoadingSpinner-blip-1 {
2215
- animation: LoadingSpinner-pop-animation 2s infinite;
2216
- }
2217
-
2218
- /* Second Blip */
2219
- .LoadingSpinner-blip-2 {
2220
- animation: LoadingSpinner-pop-animation 2s infinite;
2221
- -webkit-animation-delay: 0.1s;
2222
- animation-delay: 0.1s;
2223
- }
2224
-
2225
- /* Third Blip */
2226
- .LoadingSpinner-blip-3 {
2227
- animation: LoadingSpinner-pop-animation 2s infinite;
2228
- animation-delay: 0.2s;
2229
- }
2230
-
2231
- /* Fourth Blip */
2232
- .LoadingSpinner-blip-4 {
2233
- animation: LoadingSpinner-pop-animation 2s infinite;
2234
- animation-delay: 0.3s;
2235
- }
2236
-
2237
- /* Pop Animation for Each Blip */
2238
- @keyframes LoadingSpinner-pop-animation {
2239
- 0% {
2240
- transform: scale(1.0);
2241
- }
2242
- 10% {
2243
- transform: scale(1.5);
2244
- }
2245
- 30% {
2246
- transform: scale(1.0);
2247
- }
2248
- 100% {
2249
- transform: scale(1.0);
2250
- }
2251
- }
2252
- `;
2253
- /*------------------------------------------------------------------------*/
2254
- /* ------------------------------ Component ----------------------------- */
2255
- /*------------------------------------------------------------------------*/
2256
- const LoadingSpinner = () => {
2257
- /*------------------------------------------------------------------------*/
2258
- /* ------------------------------- Render ------------------------------- */
2259
- /*------------------------------------------------------------------------*/
2260
- // Add all four blips to a container
2261
- return (React__default.createElement("div", { className: "text-center LoadingSpinner LoadingSpinner-container" },
2262
- React__default.createElement("style", null, style$8),
2263
- React__default.createElement(FontAwesomeIcon, { icon: faCircle, className: "LoadingSpinner-blip-1 me-1" }),
2264
- React__default.createElement(FontAwesomeIcon, { icon: faCircle, className: "LoadingSpinner-blip-2 me-1" }),
2265
- React__default.createElement(FontAwesomeIcon, { icon: faCircle, className: "LoadingSpinner-blip-3 me-1" }),
2266
- React__default.createElement(FontAwesomeIcon, { icon: faCircle, className: "LoadingSpinner-blip-4" })));
2267
- };
2268
-
2269
2294
  /**
2270
2295
  * A box with a tab on the top that holds buttons and other content
2271
2296
  * @author Gabe Abrams
@@ -2297,7 +2322,9 @@ const style$7 = `
2297
2322
 
2298
2323
  /* Container for Title */
2299
2324
  .TabBox-title-container {
2300
- /* Place on Left */
2325
+ display: flex;
2326
+ justify-content: space-between;
2327
+ align-items: center;
2301
2328
  position: relative;
2302
2329
  left: 0;
2303
2330
  text-align: left;
@@ -2330,6 +2357,19 @@ const style$7 = `
2330
2357
  background: #fdfdfd;
2331
2358
  }
2332
2359
 
2360
+ .TabBox-title-right-container {
2361
+ display: flex;
2362
+ flex-direction: row;
2363
+ align-items: bottom;
2364
+ height: 2.4rem;
2365
+ overflow: visible;
2366
+ }
2367
+
2368
+ .TabBox-title-right-contents {
2369
+ margin-right: 0.5rem;
2370
+ margin-bottom: 0.2rem;
2371
+ }
2372
+
2333
2373
  /* Make the TabBox's Children Appear Above Title if Overlap Occurs */
2334
2374
  .TabBox-children {
2335
2375
  position: relative;
@@ -2343,19 +2383,16 @@ const TabBox = (props) => {
2343
2383
  /*------------------------------------------------------------------------*/
2344
2384
  /* -------------------------------- Setup ------------------------------- */
2345
2385
  /*------------------------------------------------------------------------*/
2346
- /* -------------- Props ------------- */
2347
- const { title, children, noBottomPadding, noBottomMargin, } = props;
2386
+ const { title, children, topRightChildren, noBottomPadding, noBottomMargin, } = props;
2348
2387
  /*------------------------------------------------------------------------*/
2349
2388
  /* ------------------------------- Render ------------------------------- */
2350
2389
  /*------------------------------------------------------------------------*/
2351
- /*----------------------------------------*/
2352
- /* --------------- Main UI -------------- */
2353
- /*----------------------------------------*/
2354
- // Full UI
2355
2390
  return (React__default.createElement("div", { className: `TabBox-container ${noBottomMargin ? '' : 'mb-2'}` },
2356
2391
  React__default.createElement("style", null, style$7),
2357
2392
  React__default.createElement("div", { className: "TabBox-title-container" },
2358
- React__default.createElement("div", { className: "TabBox-title" }, title)),
2393
+ React__default.createElement("div", { className: "TabBox-title" }, title),
2394
+ topRightChildren && (React__default.createElement("div", { className: "TabBox-title-right-container" },
2395
+ React__default.createElement("div", { className: "TabBox-title-right-contents" }, topRightChildren)))),
2359
2396
  React__default.createElement("div", { className: `TabBox-box ps-2 pt-2 pe-2 ${noBottomPadding ? '' : 'pb-2'}` },
2360
2397
  React__default.createElement("div", { className: "TabBox-children" }, children))));
2361
2398
  };
@@ -3342,7 +3379,7 @@ const NestableItemList = (props) => {
3342
3379
  React__default.createElement(FontAwesomeIcon, { icon: childExpanded[item.id] ? faChevronDown : faChevronRight })))),
3343
3380
  React__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) => {
3344
3381
  onChanged(changeChecked(item.id, checked, items));
3345
- }, ariaLabel: `Select ${item.name}`, checkedVariant: Variant$1.Light }),
3382
+ }, ariaLabel: `Select ${item.name}`, checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Light }),
3346
3383
  (item.isGroup && childExpanded[item.id]) && (React__default.createElement("div", { className: "NestableItemList-children-container", style: {
3347
3384
  paddingLeft: '2.2rem',
3348
3385
  } },
@@ -3365,17 +3402,62 @@ const ItemPicker = (props) => {
3365
3402
  /*------------------------------------------------------------------------*/
3366
3403
  /* -------------- Props ------------- */
3367
3404
  // Destructure all props
3368
- const { title, items, onChanged, noBottomMargin, } = props;
3405
+ const { title, items, onChanged, noBottomMargin, hideSelectAllOrNoneButtons, } = props;
3369
3406
  /*------------------------------------------------------------------------*/
3370
3407
  /* ------------------------- Component Functions ------------------------ */
3371
3408
  /*------------------------------------------------------------------------*/
3409
+ /**
3410
+ * Updates the checked state of an item (including all children)
3411
+ * @author Yuen Ler Chow
3412
+ * @param item the item to update
3413
+ * @param checked the new checked state
3414
+ * @returns the updated item
3415
+ */
3416
+ const updateItemChecked = (item, checked) => {
3417
+ if (item.isGroup) {
3418
+ return Object.assign(Object.assign({}, item), { children: item.children.map((child) => {
3419
+ return updateItemChecked(child, checked);
3420
+ }) });
3421
+ }
3422
+ return Object.assign(Object.assign({}, item), { checked });
3423
+ };
3424
+ /**
3425
+ * Selects all items in the list
3426
+ * @author Yuen Ler Chow
3427
+ */
3428
+ const handleSelectAll = () => {
3429
+ const updatedItems = items.map((item) => {
3430
+ return updateItemChecked(item, true);
3431
+ });
3432
+ onChanged(updatedItems);
3433
+ };
3434
+ /**
3435
+ * Deselects all items in the list
3436
+ * @author Yuen Ler Chow
3437
+ */
3438
+ const handleDeselectAll = () => {
3439
+ const updatedItems = items.map((item) => {
3440
+ return updateItemChecked(item, false);
3441
+ });
3442
+ onChanged(updatedItems);
3443
+ };
3372
3444
  /*------------------------------------------------------------------------*/
3373
3445
  /* ------------------------------- Render ------------------------------- */
3374
3446
  /*------------------------------------------------------------------------*/
3375
3447
  /*----------------------------------------*/
3376
3448
  /* --------------- Main UI -------------- */
3377
3449
  /*----------------------------------------*/
3378
- return (React__default.createElement(TabBox, { title: title, noBottomMargin: noBottomMargin },
3450
+ // Select all/none
3451
+ const selectAllOrNone = (!hideSelectAllOrNoneButtons
3452
+ ? (React__default.createElement("div", { className: "d-flex h-100 align-items-end flex-row" },
3453
+ React__default.createElement("div", { className: "d-flex justify-content-end" },
3454
+ React__default.createElement("div", { className: "me-2", style: { fontSize: '1.2rem' } }, "Select"),
3455
+ React__default.createElement("div", { className: "btn-group", role: "group" },
3456
+ React__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"),
3457
+ React__default.createElement("button", { type: "button", "aria-label": "Deselect all contexts", className: "btn btn-secondary py-0", onClick: handleDeselectAll }, "None")))))
3458
+ : undefined);
3459
+ // Main UI
3460
+ return (React__default.createElement(TabBox, { title: title, noBottomMargin: noBottomMargin, topRightChildren: selectAllOrNone },
3379
3461
  React__default.createElement("div", { style: { overflowX: 'auto' } },
3380
3462
  React__default.createElement(NestableItemList, { items: items, onChanged: onChanged }))));
3381
3463
  };
@@ -3747,7 +3829,7 @@ const IntelliTable = (props) => {
3747
3829
  param: column.param,
3748
3830
  visible: checked,
3749
3831
  });
3750
- }, checked: columnVisibilityMap[column.param], ariaLabel: `show "${column.title}" column in the ${title} table`, checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Secondary }));
3832
+ }, checked: columnVisibilityMap[column.param], ariaLabel: `show "${column.title}" column in the ${title} table`, checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Light }));
3751
3833
  }),
3752
3834
  React__default.createElement("div", { className: "mt-3" }, "Or you can:"),
3753
3835
  React__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: () => {
@@ -16081,6 +16163,27 @@ const shuffleArray = (arr) => {
16081
16163
  return newArr;
16082
16164
  };
16083
16165
 
16166
+ const punctuationRegex = /[!@#$%^&*(),.?\/;:'"\-\[\]]/g;
16167
+ /**
16168
+ * Get number of words in string
16169
+ * @author Gardenia Liu
16170
+ * @author Allison Zhang
16171
+ * @author Gabe Abrams
16172
+ * @param text the string to check
16173
+ * @returns number of words in the string
16174
+ */
16175
+ const getWordCount = (text) => {
16176
+ const trimmedTextWithoutPunctuation = (text
16177
+ // Remove leading and trailing whitespace
16178
+ .trim()
16179
+ // Remove punctuation
16180
+ .replace(punctuationRegex, ''));
16181
+ if (trimmedTextWithoutPunctuation.length === 0) {
16182
+ return 0;
16183
+ }
16184
+ return trimmedTextWithoutPunctuation.split(/\s+/g).length;
16185
+ };
16186
+
16084
16187
  /**
16085
16188
  * Days of the week
16086
16189
  * @author Gabe Abrams
@@ -16097,5 +16200,5 @@ var DayOfWeek;
16097
16200
  })(DayOfWeek || (DayOfWeek = {}));
16098
16201
  var DayOfWeek$1 = DayOfWeek;
16099
16202
 
16100
- export { AppWrapper, AutoscrollToBottomContainer, ButtonInputGroup, CSVDownloadButton, CheckboxButton, CopiableBox, DAY_IN_MS, DBEntryFieldType$1 as DBEntryFieldType, DBEntryManagerPanel, DayOfWeek$1 as DayOfWeek, Drawer, Dropdown, DropdownItemType$1 as DropdownItemType, DynamicWord, ErrorBox, ErrorWithCode, HOUR_IN_MS, IntelliTable, ItemPicker, LOG_REVIEW_ROUTE_PATH_PREFIX, LOG_REVIEW_STATUS_ROUTE, LOG_ROUTE_PATH, LoadingSpinner, LogAction$1 as LogAction, LogBuiltInMetadata, LogLevel$1 as LogLevel, LogReviewer, LogSource$1 as LogSource, LogType$1 as LogType, MINUTE_IN_MS, Modal, ModalButtonType$1 as ModalButtonType, ModalSize$1 as ModalSize, ModalType$1 as ModalType, MultiSwitch, ParamType$1 as ParamType, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode$1 as ReactKitErrorCode, SimpleDateChooser, TabBox, ToggleSwitch, Tooltip, Variant$1 as Variant, abbreviate, addFatalErrorHandler, alert, avg, canReviewLogs, capitalize, ceilToNumDecimals, combineClassNames, compareArraysByProp, confirm, everyAsync, extractProp, filterAsync, floorToNumDecimals, forEachAsync, forceNumIntoBounds, genCSV, genCommaList, getHumanReadableDate, getLocalTimeInfo, getMonthName, getOrdinal, getPartOfDay, getTimeInfoInET, idify, initClient, isMobileOrTablet, leaveToURL, logClientEvent, makeLinksClickable, mapAsync, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, prefixWithAOrAn, prompt, roundToNumDecimals, setClientEventMetadataPopulator, showFatalError, shuffleArray, someAsync, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, useForceRender, validateEmail, validatePhoneNumber, validateString, visitServerEndpoint, waitMs };
16203
+ export { AppWrapper, AutoscrollToBottomContainer, ButtonInputGroup, CSVDownloadButton, CheckboxButton, CopiableBox, DAY_IN_MS, DBEntryFieldType$1 as DBEntryFieldType, DBEntryManagerPanel, DayOfWeek$1 as DayOfWeek, Drawer, Dropdown, DropdownItemType$1 as DropdownItemType, DynamicWord, ErrorBox, ErrorWithCode, HOUR_IN_MS, IntelliTable, ItemPicker, LOG_REVIEW_ROUTE_PATH_PREFIX, LOG_REVIEW_STATUS_ROUTE, LOG_ROUTE_PATH, LoadingSpinner, LogAction$1 as LogAction, LogBuiltInMetadata, LogLevel$1 as LogLevel, LogReviewer, LogSource$1 as LogSource, LogType$1 as LogType, MINUTE_IN_MS, Modal, ModalButtonType$1 as ModalButtonType, ModalSize$1 as ModalSize, ModalType$1 as ModalType, MultiSwitch, ParamType$1 as ParamType, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode$1 as ReactKitErrorCode, SimpleDateChooser, TabBox, ToggleSwitch, Tooltip, Variant$1 as Variant, abbreviate, addFatalErrorHandler, alert, avg, canReviewLogs, capitalize, ceilToNumDecimals, combineClassNames, compareArraysByProp, confirm, everyAsync, extractProp, filterAsync, floorToNumDecimals, forEachAsync, forceNumIntoBounds, genCSV, genCommaList, getHumanReadableDate, getLocalTimeInfo, getMonthName, getOrdinal, getPartOfDay, getTimeInfoInET, getWordCount, idify, initClient, isMobileOrTablet, leaveToURL, logClientEvent, makeLinksClickable, mapAsync, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, prefixWithAOrAn, prompt, roundToNumDecimals, setClientEventMetadataPopulator, showFatalError, shuffleArray, someAsync, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, useForceRender, validateEmail, validatePhoneNumber, validateString, visitServerEndpoint, waitMs };
16101
16204
  //# sourceMappingURL=index.js.map