dce-reactkit 4.0.0-beta.2 → 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.
@@ -1,10 +1,12 @@
1
1
  {
2
2
  "cSpell.words": [
3
3
  "Copiable",
4
+ "Gabe",
4
5
  "Intelli",
5
6
  "pickable",
6
7
  "Subcontext",
7
8
  "subcontexts",
8
- "Uncategorized"
9
+ "Uncategorized",
10
+ "Zhang"
9
11
  ]
10
12
  }
package/README.md CHANGED
@@ -14,23 +14,3 @@ npm install dce-reactkit --save
14
14
  ```js
15
15
  import { LoadingSpinner } from 'dce-reactkit';
16
16
  ```
17
-
18
- ## Cross-server Requests
19
-
20
- If your server will be _accepting_ cross-server requests, include a `REACTKIT_CRED_ENCODING_SALT` environment variable with secure, secret text.
21
-
22
- To generate new encoded secrets, use `npm run gen-reactkit-cross-server-secret`. You'll need to put credentials into a collection. Create that collection in your `mongo.ts` file:
23
-
24
- ```ts
25
- // Import dce-reactkit
26
- import { initCrossServerCredentialCollection, CrossServerCredential } from 'dce-reactkit';
27
-
28
- ...
29
-
30
- // Cross-server Credentials
31
- export const crossServerCredentialCollection = initCrossServerCredentialCollection(Collection) as Collection<CrossServerCredential>;
32
- ```
33
-
34
- In that collection, manually insert into the database individual credentials, one for each service. You'll need to add a description, list of allowed scopes, and you'll need to come up with a public key.
35
-
36
- If your server will be _sending_ cross-server requests, after creating a secret and key using the process above, in the server that will be _sending_ the cross-server requests, add the credential to the environment as `REACTKIT_CROSS_SERVER_KEY` and `REACTKIT_CROSS_SERVER_SECRET`.
package/dist/cjs/index.js CHANGED
@@ -897,6 +897,98 @@ function requireReactDom_development () {
897
897
 
898
898
  var ReactDOM = /*@__PURE__*/getDefaultExportFromCjs(reactDom.exports);
899
899
 
900
+ /**
901
+ * Loading spinner/indicator
902
+ * @author Gabe Abrams
903
+ */
904
+ /*------------------------------------------------------------------------*/
905
+ /* -------------------------------- Style ------------------------------- */
906
+ /*------------------------------------------------------------------------*/
907
+ const style$a = `
908
+ /* Container fades in */
909
+ .LoadingSpinner-container {
910
+ animation-name: LoadingSpinner-container-fade-in;
911
+ animation-duration: 0.3s;
912
+ animation-delay: 0.4s;
913
+ animation-fill-mode: both;
914
+ animation-timing-function: ease-out;
915
+ }
916
+ @keyframes LoadingSpinner-container-fade-in {
917
+ 0% {
918
+ opacity: 0;
919
+ }
920
+ 100% {
921
+ opacity: 1;
922
+ }
923
+ }
924
+
925
+ /* Blips */
926
+ .LoadingSpinner-blip-1,
927
+ .LoadingSpinner-blip-2,
928
+ .LoadingSpinner-blip-3,
929
+ .LoadingSpinner-blip-4 {
930
+ font-size: 1.8rem;
931
+ opacity: 0.6;
932
+ margin-top: 1rem;
933
+ margin-bottom: 1rem;
934
+ }
935
+
936
+ /* First Blip */
937
+ .LoadingSpinner-blip-1 {
938
+ animation: LoadingSpinner-pop-animation 2s infinite;
939
+ }
940
+
941
+ /* Second Blip */
942
+ .LoadingSpinner-blip-2 {
943
+ animation: LoadingSpinner-pop-animation 2s infinite;
944
+ -webkit-animation-delay: 0.1s;
945
+ animation-delay: 0.1s;
946
+ }
947
+
948
+ /* Third Blip */
949
+ .LoadingSpinner-blip-3 {
950
+ animation: LoadingSpinner-pop-animation 2s infinite;
951
+ animation-delay: 0.2s;
952
+ }
953
+
954
+ /* Fourth Blip */
955
+ .LoadingSpinner-blip-4 {
956
+ animation: LoadingSpinner-pop-animation 2s infinite;
957
+ animation-delay: 0.3s;
958
+ }
959
+
960
+ /* Pop Animation for Each Blip */
961
+ @keyframes LoadingSpinner-pop-animation {
962
+ 0% {
963
+ transform: scale(1.0);
964
+ }
965
+ 10% {
966
+ transform: scale(1.5);
967
+ }
968
+ 30% {
969
+ transform: scale(1.0);
970
+ }
971
+ 100% {
972
+ transform: scale(1.0);
973
+ }
974
+ }
975
+ `;
976
+ /*------------------------------------------------------------------------*/
977
+ /* ------------------------------ Component ----------------------------- */
978
+ /*------------------------------------------------------------------------*/
979
+ const LoadingSpinner = () => {
980
+ /*------------------------------------------------------------------------*/
981
+ /* ------------------------------- Render ------------------------------- */
982
+ /*------------------------------------------------------------------------*/
983
+ // Add all four blips to a container
984
+ return (React__default["default"].createElement("div", { className: "text-center LoadingSpinner LoadingSpinner-container" },
985
+ React__default["default"].createElement("style", null, style$a),
986
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCircle, className: "LoadingSpinner-blip-1 me-1" }),
987
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCircle, className: "LoadingSpinner-blip-2 me-1" }),
988
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCircle, className: "LoadingSpinner-blip-3 me-1" }),
989
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCircle, className: "LoadingSpinner-blip-4" })));
990
+ };
991
+
900
992
  /**
901
993
  * Wait for a certain number of ms
902
994
  * @author Gabe Abrams
@@ -1040,6 +1132,7 @@ const BASE_Z_INDEX = 1000000000;
1040
1132
  const BASE_Z_INDEX_ON_TOP = 2000000000;
1041
1133
  // Constants
1042
1134
  const MS_TO_ANIMATE = 200; // Animation duration
1135
+ const MS_TO_WAIT_BEFORE_SHOWING_LOADING_INDICATOR = 1000;
1043
1136
  // Modal type to list of buttons
1044
1137
  const modalTypeToModalButtonTypes = {
1045
1138
  [ModalType$1.Okay]: [
@@ -1153,7 +1246,7 @@ const getNextUniqueId = () => {
1153
1246
  /*------------------------------------------------------------------------*/
1154
1247
  /* -------------------------------- Style ------------------------------- */
1155
1248
  /*------------------------------------------------------------------------*/
1156
- const style$a = `
1249
+ const style$9 = `
1157
1250
  .Modal-backdrop {
1158
1251
  position: fixed;
1159
1252
  top: 0;
@@ -1225,13 +1318,14 @@ const Modal = (props) => {
1225
1318
  /*------------------------------------------------------------------------*/
1226
1319
  var _a;
1227
1320
  /* -------------- Props ------------- */
1228
- const { type = ModalType$1.NoButtons, size = ModalSize$1.Large, title, largeTitle, children, onClose, dontAllowBackdropExit, dontShowXButton, onTopOfOtherModals, } = props;
1321
+ const { type = ModalType$1.NoButtons, size = ModalSize$1.Large, title, largeTitle, children, onClose, dontAllowBackdropExit, dontShowXButton, onTopOfOtherModals, isLoading, isLoadingCancelable, } = props;
1229
1322
  // Determine if no header either
1230
1323
  const noHeader = (!title && type === ModalType$1.NoButtons);
1231
1324
  /* -------------- State ------------- */
1232
1325
  // True if animation is in use
1233
1326
  const [animatingIn, setAnimatingIn] = React.useState(true);
1234
1327
  const [animatingPop, setAnimatingPop] = React.useState(false);
1328
+ const [showModal, setShowModal] = React.useState(false);
1235
1329
  /* -------------- Refs -------------- */
1236
1330
  // Keep track of whether modal is still mounted
1237
1331
  const mounted = React.useRef(false);
@@ -1258,6 +1352,16 @@ const Modal = (props) => {
1258
1352
  if (mounted.current) {
1259
1353
  setAnimatingIn(false);
1260
1354
  }
1355
+ if (isLoading) {
1356
+ // wait before showing modal
1357
+ yield waitMs(MS_TO_WAIT_BEFORE_SHOWING_LOADING_INDICATOR);
1358
+ if (mounted.current) {
1359
+ setShowModal(true);
1360
+ }
1361
+ }
1362
+ else {
1363
+ setShowModal(true);
1364
+ }
1261
1365
  }))();
1262
1366
  return () => {
1263
1367
  mounted.current = false;
@@ -1324,6 +1428,15 @@ const Modal = (props) => {
1324
1428
  else if (animatingPop) {
1325
1429
  animationClass = 'Modal-animating-pop';
1326
1430
  }
1431
+ // default to show close button when not loading and not show close button when loading
1432
+ // unless downShowXButton or isLoadingCancelable
1433
+ const showCloseButton = (
1434
+ // The modal must have an onClose function
1435
+ onClose
1436
+ // ...and the close button is allowed to be visible
1437
+ && !dontShowXButton
1438
+ // ...and should not be shown if the modal is loading and not cancelable
1439
+ && !(isLoading && !isLoadingCancelable));
1327
1440
  // Render the modal
1328
1441
  const contentToRender = (React__default["default"].createElement("div", { className: "modal show", tabIndex: -1, style: {
1329
1442
  zIndex: baseZIndex,
@@ -1332,7 +1445,7 @@ const Modal = (props) => {
1332
1445
  left: 0,
1333
1446
  right: 0,
1334
1447
  } },
1335
- React__default["default"].createElement("style", null, style$a),
1448
+ React__default["default"].createElement("style", null, style$9),
1336
1449
  React__default["default"].createElement("div", { className: `Modal-backdrop ${backdropAnimationClass}`, style: {
1337
1450
  zIndex: baseZIndex + 1,
1338
1451
  }, onClick: () => __awaiter(void 0, void 0, void 0, function* () {
@@ -1350,7 +1463,7 @@ const Modal = (props) => {
1350
1463
  // Handle close
1351
1464
  handleClose(ModalButtonType$1.Cancel);
1352
1465
  }) }),
1353
- React__default["default"].createElement("div", { className: `modal-dialog modal-${size} ${animationClass} modal-dialog-scrollable modal-dialog-centered`, style: {
1466
+ showModal && (React__default["default"].createElement("div", { className: `modal-dialog modal-${size} ${animationClass} modal-dialog-scrollable modal-dialog-centered`, style: {
1354
1467
  zIndex: baseZIndex + 2,
1355
1468
  // Override sizes for even larger for XL
1356
1469
  width: (size === ModalSize$1.ExtraLarge
@@ -1382,7 +1495,7 @@ const Modal = (props) => {
1382
1495
  ? '1.6rem'
1383
1496
  : undefined),
1384
1497
  } }, title),
1385
- (onClose && !dontShowXButton) && (React__default["default"].createElement("button", { type: "button", className: "Modal-x-button btn-close", "aria-label": "Close", style: {
1498
+ showCloseButton && (React__default["default"].createElement("button", { type: "button", className: "Modal-x-button btn-close", "aria-label": "Close", style: {
1386
1499
  backgroundColor: (isDarkModeOn()
1387
1500
  ? 'white'
1388
1501
  : undefined),
@@ -1390,14 +1503,18 @@ const Modal = (props) => {
1390
1503
  // Handle close
1391
1504
  handleClose(ModalButtonType$1.Cancel);
1392
1505
  } })))),
1393
- children && (React__default["default"].createElement("div", { className: "modal-body", style: {
1506
+ React__default["default"].createElement("div", { className: "modal-body", style: {
1394
1507
  color: (isDarkModeOn()
1395
1508
  ? 'white'
1396
1509
  : undefined),
1397
1510
  backgroundColor: (isDarkModeOn()
1398
1511
  ? '#444'
1399
1512
  : undefined),
1400
- } }, children)),
1513
+ } }, isLoading
1514
+ ? (React__default["default"].createElement(React__default["default"].Fragment, null,
1515
+ React__default["default"].createElement(LoadingSpinner, null),
1516
+ React__default["default"].createElement("span", { className: "sr-only" }, "Content loading")))
1517
+ : children),
1401
1518
  footer && (React__default["default"].createElement("div", { className: "modal-footer pt-1 pb-1", style: {
1402
1519
  color: (isDarkModeOn()
1403
1520
  ? 'white'
@@ -1408,7 +1525,7 @@ const Modal = (props) => {
1408
1525
  borderTop: (isDarkModeOn()
1409
1526
  ? '0.1rem solid gray'
1410
1527
  : undefined),
1411
- } }, footer))))));
1528
+ } }, footer)))))));
1412
1529
  // Determine which portal to use
1413
1530
  const portalNumber = id.current % NUM_MODAL_PORTALS;
1414
1531
  // Render in a portal
@@ -1979,7 +2096,7 @@ const showSessionExpiredMessage = () => __awaiter(void 0, void 0, void 0, functi
1979
2096
  /*------------------------------------------------------------------------*/
1980
2097
  /* -------------------------------- Style ------------------------------- */
1981
2098
  /*------------------------------------------------------------------------*/
1982
- const style$9 = `
2099
+ const style$8 = `
1983
2100
  .AppWrapper-leave-to-url-notice {
1984
2101
  opacity: 0;
1985
2102
 
@@ -2193,105 +2310,13 @@ const AppWrapper = (props) => {
2193
2310
  /* --------------- Main UI -------------- */
2194
2311
  /*----------------------------------------*/
2195
2312
  return (React__default["default"].createElement(React__default["default"].Fragment, null,
2196
- React__default["default"].createElement("style", null, style$9),
2313
+ React__default["default"].createElement("style", null, style$8),
2197
2314
  React__default["default"].createElement("style", null, tooltipStyle),
2198
2315
  modal,
2199
2316
  customModalPortals,
2200
2317
  body));
2201
2318
  };
2202
2319
 
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
2320
  /**
2296
2321
  * A box with a tab on the top that holds buttons and other content
2297
2322
  * @author Gabe Abrams
@@ -2323,7 +2348,9 @@ const style$7 = `
2323
2348
 
2324
2349
  /* Container for Title */
2325
2350
  .TabBox-title-container {
2326
- /* Place on Left */
2351
+ display: flex;
2352
+ justify-content: space-between;
2353
+ align-items: center;
2327
2354
  position: relative;
2328
2355
  left: 0;
2329
2356
  text-align: left;
@@ -2356,6 +2383,19 @@ const style$7 = `
2356
2383
  background: #fdfdfd;
2357
2384
  }
2358
2385
 
2386
+ .TabBox-title-right-container {
2387
+ display: flex;
2388
+ flex-direction: row;
2389
+ align-items: bottom;
2390
+ height: 2.4rem;
2391
+ overflow: visible;
2392
+ }
2393
+
2394
+ .TabBox-title-right-contents {
2395
+ margin-right: 0.5rem;
2396
+ margin-bottom: 0.2rem;
2397
+ }
2398
+
2359
2399
  /* Make the TabBox's Children Appear Above Title if Overlap Occurs */
2360
2400
  .TabBox-children {
2361
2401
  position: relative;
@@ -2369,19 +2409,16 @@ const TabBox = (props) => {
2369
2409
  /*------------------------------------------------------------------------*/
2370
2410
  /* -------------------------------- Setup ------------------------------- */
2371
2411
  /*------------------------------------------------------------------------*/
2372
- /* -------------- Props ------------- */
2373
- const { title, children, noBottomPadding, noBottomMargin, } = props;
2412
+ const { title, children, topRightChildren, noBottomPadding, noBottomMargin, } = props;
2374
2413
  /*------------------------------------------------------------------------*/
2375
2414
  /* ------------------------------- Render ------------------------------- */
2376
2415
  /*------------------------------------------------------------------------*/
2377
- /*----------------------------------------*/
2378
- /* --------------- Main UI -------------- */
2379
- /*----------------------------------------*/
2380
- // Full UI
2381
2416
  return (React__default["default"].createElement("div", { className: `TabBox-container ${noBottomMargin ? '' : 'mb-2'}` },
2382
2417
  React__default["default"].createElement("style", null, style$7),
2383
2418
  React__default["default"].createElement("div", { className: "TabBox-title-container" },
2384
- React__default["default"].createElement("div", { className: "TabBox-title" }, title)),
2419
+ React__default["default"].createElement("div", { className: "TabBox-title" }, title),
2420
+ topRightChildren && (React__default["default"].createElement("div", { className: "TabBox-title-right-container" },
2421
+ React__default["default"].createElement("div", { className: "TabBox-title-right-contents" }, topRightChildren)))),
2385
2422
  React__default["default"].createElement("div", { className: `TabBox-box ps-2 pt-2 pe-2 ${noBottomPadding ? '' : 'pb-2'}` },
2386
2423
  React__default["default"].createElement("div", { className: "TabBox-children" }, children))));
2387
2424
  };
@@ -3368,7 +3405,7 @@ const NestableItemList = (props) => {
3368
3405
  React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: childExpanded[item.id] ? freeSolidSvgIcons.faChevronDown : freeSolidSvgIcons.faChevronRight })))),
3369
3406
  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
3407
  onChanged(changeChecked(item.id, checked, items));
3371
- }, ariaLabel: `Select ${item.name}`, checkedVariant: Variant$1.Light }),
3408
+ }, ariaLabel: `Select ${item.name}`, checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Light }),
3372
3409
  (item.isGroup && childExpanded[item.id]) && (React__default["default"].createElement("div", { className: "NestableItemList-children-container", style: {
3373
3410
  paddingLeft: '2.2rem',
3374
3411
  } },
@@ -3391,17 +3428,62 @@ const ItemPicker = (props) => {
3391
3428
  /*------------------------------------------------------------------------*/
3392
3429
  /* -------------- Props ------------- */
3393
3430
  // Destructure all props
3394
- const { title, items, onChanged, noBottomMargin, } = props;
3431
+ const { title, items, onChanged, noBottomMargin, hideSelectAllOrNoneButtons, } = props;
3395
3432
  /*------------------------------------------------------------------------*/
3396
3433
  /* ------------------------- Component Functions ------------------------ */
3397
3434
  /*------------------------------------------------------------------------*/
3435
+ /**
3436
+ * Updates the checked state of an item (including all children)
3437
+ * @author Yuen Ler Chow
3438
+ * @param item the item to update
3439
+ * @param checked the new checked state
3440
+ * @returns the updated item
3441
+ */
3442
+ const updateItemChecked = (item, checked) => {
3443
+ if (item.isGroup) {
3444
+ return Object.assign(Object.assign({}, item), { children: item.children.map((child) => {
3445
+ return updateItemChecked(child, checked);
3446
+ }) });
3447
+ }
3448
+ return Object.assign(Object.assign({}, item), { checked });
3449
+ };
3450
+ /**
3451
+ * Selects all items in the list
3452
+ * @author Yuen Ler Chow
3453
+ */
3454
+ const handleSelectAll = () => {
3455
+ const updatedItems = items.map((item) => {
3456
+ return updateItemChecked(item, true);
3457
+ });
3458
+ onChanged(updatedItems);
3459
+ };
3460
+ /**
3461
+ * Deselects all items in the list
3462
+ * @author Yuen Ler Chow
3463
+ */
3464
+ const handleDeselectAll = () => {
3465
+ const updatedItems = items.map((item) => {
3466
+ return updateItemChecked(item, false);
3467
+ });
3468
+ onChanged(updatedItems);
3469
+ };
3398
3470
  /*------------------------------------------------------------------------*/
3399
3471
  /* ------------------------------- Render ------------------------------- */
3400
3472
  /*------------------------------------------------------------------------*/
3401
3473
  /*----------------------------------------*/
3402
3474
  /* --------------- Main UI -------------- */
3403
3475
  /*----------------------------------------*/
3404
- return (React__default["default"].createElement(TabBox, { title: title, noBottomMargin: noBottomMargin },
3476
+ // Select all/none
3477
+ const selectAllOrNone = (!hideSelectAllOrNoneButtons
3478
+ ? (React__default["default"].createElement("div", { className: "d-flex h-100 align-items-end flex-row" },
3479
+ React__default["default"].createElement("div", { className: "d-flex justify-content-end" },
3480
+ React__default["default"].createElement("div", { className: "me-2", style: { fontSize: '1.2rem' } }, "Select"),
3481
+ React__default["default"].createElement("div", { className: "btn-group", role: "group" },
3482
+ 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"),
3483
+ React__default["default"].createElement("button", { type: "button", "aria-label": "Deselect all contexts", className: "btn btn-secondary py-0", onClick: handleDeselectAll }, "None")))))
3484
+ : undefined);
3485
+ // Main UI
3486
+ return (React__default["default"].createElement(TabBox, { title: title, noBottomMargin: noBottomMargin, topRightChildren: selectAllOrNone },
3405
3487
  React__default["default"].createElement("div", { style: { overflowX: 'auto' } },
3406
3488
  React__default["default"].createElement(NestableItemList, { items: items, onChanged: onChanged }))));
3407
3489
  };
@@ -3773,7 +3855,7 @@ const IntelliTable = (props) => {
3773
3855
  param: column.param,
3774
3856
  visible: checked,
3775
3857
  });
3776
- }, checked: columnVisibilityMap[column.param], ariaLabel: `show "${column.title}" column in the ${title} table`, checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Secondary }));
3858
+ }, checked: columnVisibilityMap[column.param], ariaLabel: `show "${column.title}" column in the ${title} table`, checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Light }));
3777
3859
  }),
3778
3860
  React__default["default"].createElement("div", { className: "mt-3" }, "Or you can:"),
3779
3861
  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: () => {
@@ -16107,6 +16189,27 @@ const shuffleArray = (arr) => {
16107
16189
  return newArr;
16108
16190
  };
16109
16191
 
16192
+ const punctuationRegex = /[!@#$%^&*(),.?\/;:'"\-\[\]]/g;
16193
+ /**
16194
+ * Get number of words in string
16195
+ * @author Gardenia Liu
16196
+ * @author Allison Zhang
16197
+ * @author Gabe Abrams
16198
+ * @param text the string to check
16199
+ * @returns number of words in the string
16200
+ */
16201
+ const getWordCount = (text) => {
16202
+ const trimmedTextWithoutPunctuation = (text
16203
+ // Remove leading and trailing whitespace
16204
+ .trim()
16205
+ // Remove punctuation
16206
+ .replace(punctuationRegex, ''));
16207
+ if (trimmedTextWithoutPunctuation.length === 0) {
16208
+ return 0;
16209
+ }
16210
+ return trimmedTextWithoutPunctuation.split(/\s+/g).length;
16211
+ };
16212
+
16110
16213
  /**
16111
16214
  * Days of the week
16112
16215
  * @author Gabe Abrams
@@ -16193,6 +16296,7 @@ exports.getMonthName = getMonthName;
16193
16296
  exports.getOrdinal = getOrdinal;
16194
16297
  exports.getPartOfDay = getPartOfDay;
16195
16298
  exports.getTimeInfoInET = getTimeInfoInET;
16299
+ exports.getWordCount = getWordCount;
16196
16300
  exports.idify = idify;
16197
16301
  exports.initClient = initClient;
16198
16302
  exports.isMobileOrTablet = isMobileOrTablet;