dce-reactkit 3.9.4-beta-logreviewer.12 → 3.9.4-beta-logreviewer.13

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/dist/cjs/index.js CHANGED
@@ -216,6 +216,98 @@ const LogBuiltInMetadata = {
216
216
  },
217
217
  };
218
218
 
219
+ /**
220
+ * Loading spinner/indicator
221
+ * @author Gabe Abrams
222
+ */
223
+ /*------------------------------------------------------------------------*/
224
+ /* -------------------------------- Style ------------------------------- */
225
+ /*------------------------------------------------------------------------*/
226
+ const style$a = `
227
+ /* Container fades in */
228
+ .LoadingSpinner-container {
229
+ animation-name: LoadingSpinner-container-fade-in;
230
+ animation-duration: 0.3s;
231
+ animation-delay: 0.4s;
232
+ animation-fill-mode: both;
233
+ animation-timing-function: ease-out;
234
+ }
235
+ @keyframes LoadingSpinner-container-fade-in {
236
+ 0% {
237
+ opacity: 0;
238
+ }
239
+ 100% {
240
+ opacity: 1;
241
+ }
242
+ }
243
+
244
+ /* Blips */
245
+ .LoadingSpinner-blip-1,
246
+ .LoadingSpinner-blip-2,
247
+ .LoadingSpinner-blip-3,
248
+ .LoadingSpinner-blip-4 {
249
+ font-size: 1.8rem;
250
+ opacity: 0.6;
251
+ margin-top: 1rem;
252
+ margin-bottom: 1rem;
253
+ }
254
+
255
+ /* First Blip */
256
+ .LoadingSpinner-blip-1 {
257
+ animation: LoadingSpinner-pop-animation 2s infinite;
258
+ }
259
+
260
+ /* Second Blip */
261
+ .LoadingSpinner-blip-2 {
262
+ animation: LoadingSpinner-pop-animation 2s infinite;
263
+ -webkit-animation-delay: 0.1s;
264
+ animation-delay: 0.1s;
265
+ }
266
+
267
+ /* Third Blip */
268
+ .LoadingSpinner-blip-3 {
269
+ animation: LoadingSpinner-pop-animation 2s infinite;
270
+ animation-delay: 0.2s;
271
+ }
272
+
273
+ /* Fourth Blip */
274
+ .LoadingSpinner-blip-4 {
275
+ animation: LoadingSpinner-pop-animation 2s infinite;
276
+ animation-delay: 0.3s;
277
+ }
278
+
279
+ /* Pop Animation for Each Blip */
280
+ @keyframes LoadingSpinner-pop-animation {
281
+ 0% {
282
+ transform: scale(1.0);
283
+ }
284
+ 10% {
285
+ transform: scale(1.5);
286
+ }
287
+ 30% {
288
+ transform: scale(1.0);
289
+ }
290
+ 100% {
291
+ transform: scale(1.0);
292
+ }
293
+ }
294
+ `;
295
+ /*------------------------------------------------------------------------*/
296
+ /* ------------------------------ Component ----------------------------- */
297
+ /*------------------------------------------------------------------------*/
298
+ const LoadingSpinner = () => {
299
+ /*------------------------------------------------------------------------*/
300
+ /* ------------------------------- Render ------------------------------- */
301
+ /*------------------------------------------------------------------------*/
302
+ // Add all four blips to a container
303
+ return (React__default["default"].createElement("div", { className: "text-center LoadingSpinner LoadingSpinner-container" },
304
+ React__default["default"].createElement("style", null, style$a),
305
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCircle, className: "LoadingSpinner-blip-1 me-1" }),
306
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCircle, className: "LoadingSpinner-blip-2 me-1" }),
307
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCircle, className: "LoadingSpinner-blip-3 me-1" }),
308
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCircle, className: "LoadingSpinner-blip-4" })));
309
+ };
310
+
219
311
  /**
220
312
  * Wait for a certain number of ms
221
313
  * @author Gabe Abrams
@@ -348,6 +440,7 @@ const BASE_Z_INDEX = 1000000000;
348
440
  const BASE_Z_INDEX_ON_TOP = 2000000000;
349
441
  // Constants
350
442
  const MS_TO_ANIMATE = 200; // Animation duration
443
+ const MS_TO_WAIT_BEFORE_SHOWING_LOADING_INDICATOR = 1000;
351
444
  // Modal type to list of buttons
352
445
  const modalTypeToModalButtonTypes = {
353
446
  [ModalType$1.Okay]: [
@@ -461,7 +554,7 @@ const getNextUniqueId = () => {
461
554
  /*------------------------------------------------------------------------*/
462
555
  /* -------------------------------- Style ------------------------------- */
463
556
  /*------------------------------------------------------------------------*/
464
- const style$a = `
557
+ const style$9 = `
465
558
  .Modal-backdrop {
466
559
  position: fixed;
467
560
  top: 0;
@@ -533,13 +626,14 @@ const Modal = (props) => {
533
626
  /*------------------------------------------------------------------------*/
534
627
  var _a;
535
628
  /* -------------- Props ------------- */
536
- const { type = ModalType$1.NoButtons, size = ModalSize$1.Large, title, largeTitle, children, onClose, dontAllowBackdropExit, dontShowXButton, onTopOfOtherModals, } = props;
629
+ const { type = ModalType$1.NoButtons, size = ModalSize$1.Large, title, largeTitle, children, onClose, dontAllowBackdropExit, dontShowXButton, onTopOfOtherModals, isLoading, isLoadingCancelable, } = props;
537
630
  // Determine if no header either
538
631
  const noHeader = (!title && type === ModalType$1.NoButtons);
539
632
  /* -------------- State ------------- */
540
633
  // True if animation is in use
541
634
  const [animatingIn, setAnimatingIn] = React.useState(true);
542
635
  const [animatingPop, setAnimatingPop] = React.useState(false);
636
+ const [showModal, setShowModal] = React.useState(false);
543
637
  /* -------------- Refs -------------- */
544
638
  // Keep track of whether modal is still mounted
545
639
  const mounted = React.useRef(false);
@@ -566,6 +660,16 @@ const Modal = (props) => {
566
660
  if (mounted.current) {
567
661
  setAnimatingIn(false);
568
662
  }
663
+ if (isLoading) {
664
+ // wait before showing modal
665
+ yield waitMs(MS_TO_WAIT_BEFORE_SHOWING_LOADING_INDICATOR);
666
+ if (mounted.current) {
667
+ setShowModal(true);
668
+ }
669
+ }
670
+ else {
671
+ setShowModal(true);
672
+ }
569
673
  }))();
570
674
  return () => {
571
675
  mounted.current = false;
@@ -632,6 +736,15 @@ const Modal = (props) => {
632
736
  else if (animatingPop) {
633
737
  animationClass = 'Modal-animating-pop';
634
738
  }
739
+ // default to show close button when not loading and not show close button when loading
740
+ // unless downShowXButton or isLoadingCancelable
741
+ const showCloseButton = (
742
+ // The modal must have an onClose function
743
+ onClose
744
+ // ...and the close button is allowed to be visible
745
+ && !dontShowXButton
746
+ // ...and should not be shown if the modal is loading and not cancelable
747
+ && !(isLoading && !isLoadingCancelable));
635
748
  // Render the modal
636
749
  const contentToRender = (React__default["default"].createElement("div", { className: "modal show", tabIndex: -1, style: {
637
750
  zIndex: baseZIndex,
@@ -640,7 +753,7 @@ const Modal = (props) => {
640
753
  left: 0,
641
754
  right: 0,
642
755
  } },
643
- React__default["default"].createElement("style", null, style$a),
756
+ React__default["default"].createElement("style", null, style$9),
644
757
  React__default["default"].createElement("div", { className: `Modal-backdrop ${backdropAnimationClass}`, style: {
645
758
  zIndex: baseZIndex + 1,
646
759
  }, onClick: () => __awaiter(void 0, void 0, void 0, function* () {
@@ -658,7 +771,7 @@ const Modal = (props) => {
658
771
  // Handle close
659
772
  handleClose(ModalButtonType$1.Cancel);
660
773
  }) }),
661
- React__default["default"].createElement("div", { className: `modal-dialog modal-${size} ${animationClass} modal-dialog-scrollable modal-dialog-centered`, style: {
774
+ showModal && (React__default["default"].createElement("div", { className: `modal-dialog modal-${size} ${animationClass} modal-dialog-scrollable modal-dialog-centered`, style: {
662
775
  zIndex: baseZIndex + 2,
663
776
  // Override sizes for even larger for XL
664
777
  width: (size === ModalSize$1.ExtraLarge
@@ -690,7 +803,7 @@ const Modal = (props) => {
690
803
  ? '1.6rem'
691
804
  : undefined),
692
805
  } }, title),
693
- (onClose && !dontShowXButton) && (React__default["default"].createElement("button", { type: "button", className: "Modal-x-button btn-close", "aria-label": "Close", style: {
806
+ showCloseButton && (React__default["default"].createElement("button", { type: "button", className: "Modal-x-button btn-close", "aria-label": "Close", style: {
694
807
  backgroundColor: (isDarkModeOn()
695
808
  ? 'white'
696
809
  : undefined),
@@ -698,14 +811,18 @@ const Modal = (props) => {
698
811
  // Handle close
699
812
  handleClose(ModalButtonType$1.Cancel);
700
813
  } })))),
701
- children && (React__default["default"].createElement("div", { className: "modal-body", style: {
814
+ React__default["default"].createElement("div", { className: "modal-body", style: {
702
815
  color: (isDarkModeOn()
703
816
  ? 'white'
704
817
  : undefined),
705
818
  backgroundColor: (isDarkModeOn()
706
819
  ? '#444'
707
820
  : undefined),
708
- } }, children)),
821
+ } }, isLoading
822
+ ? (React__default["default"].createElement(React__default["default"].Fragment, null,
823
+ React__default["default"].createElement(LoadingSpinner, null),
824
+ React__default["default"].createElement("span", { className: "sr-only" }, "Content loading")))
825
+ : children),
709
826
  footer && (React__default["default"].createElement("div", { className: "modal-footer pt-1 pb-1", style: {
710
827
  color: (isDarkModeOn()
711
828
  ? 'white'
@@ -716,7 +833,7 @@ const Modal = (props) => {
716
833
  borderTop: (isDarkModeOn()
717
834
  ? '0.1rem solid gray'
718
835
  : undefined),
719
- } }, footer))))));
836
+ } }, footer)))))));
720
837
  // Determine which portal to use
721
838
  const portalNumber = id.current % NUM_MODAL_PORTALS;
722
839
  // Render in a portal
@@ -1283,7 +1400,7 @@ const showSessionExpiredMessage = () => __awaiter(void 0, void 0, void 0, functi
1283
1400
  /*------------------------------------------------------------------------*/
1284
1401
  /* -------------------------------- Style ------------------------------- */
1285
1402
  /*------------------------------------------------------------------------*/
1286
- const style$9 = `
1403
+ const style$8 = `
1287
1404
  .AppWrapper-leave-to-url-notice {
1288
1405
  opacity: 0;
1289
1406
 
@@ -1497,105 +1614,13 @@ const AppWrapper = (props) => {
1497
1614
  /* --------------- Main UI -------------- */
1498
1615
  /*----------------------------------------*/
1499
1616
  return (React__default["default"].createElement(React__default["default"].Fragment, null,
1500
- React__default["default"].createElement("style", null, style$9),
1617
+ React__default["default"].createElement("style", null, style$8),
1501
1618
  React__default["default"].createElement("style", null, tooltipStyle),
1502
1619
  modal,
1503
1620
  customModalPortals,
1504
1621
  body));
1505
1622
  };
1506
1623
 
1507
- /**
1508
- * Loading spinner/indicator
1509
- * @author Gabe Abrams
1510
- */
1511
- /*------------------------------------------------------------------------*/
1512
- /* -------------------------------- Style ------------------------------- */
1513
- /*------------------------------------------------------------------------*/
1514
- const style$8 = `
1515
- /* Container fades in */
1516
- .LoadingSpinner-container {
1517
- animation-name: LoadingSpinner-container-fade-in;
1518
- animation-duration: 0.3s;
1519
- animation-delay: 0.4s;
1520
- animation-fill-mode: both;
1521
- animation-timing-function: ease-out;
1522
- }
1523
- @keyframes LoadingSpinner-container-fade-in {
1524
- 0% {
1525
- opacity: 0;
1526
- }
1527
- 100% {
1528
- opacity: 1;
1529
- }
1530
- }
1531
-
1532
- /* Blips */
1533
- .LoadingSpinner-blip-1,
1534
- .LoadingSpinner-blip-2,
1535
- .LoadingSpinner-blip-3,
1536
- .LoadingSpinner-blip-4 {
1537
- font-size: 1.8rem;
1538
- opacity: 0.6;
1539
- margin-top: 1rem;
1540
- margin-bottom: 1rem;
1541
- }
1542
-
1543
- /* First Blip */
1544
- .LoadingSpinner-blip-1 {
1545
- animation: LoadingSpinner-pop-animation 2s infinite;
1546
- }
1547
-
1548
- /* Second Blip */
1549
- .LoadingSpinner-blip-2 {
1550
- animation: LoadingSpinner-pop-animation 2s infinite;
1551
- -webkit-animation-delay: 0.1s;
1552
- animation-delay: 0.1s;
1553
- }
1554
-
1555
- /* Third Blip */
1556
- .LoadingSpinner-blip-3 {
1557
- animation: LoadingSpinner-pop-animation 2s infinite;
1558
- animation-delay: 0.2s;
1559
- }
1560
-
1561
- /* Fourth Blip */
1562
- .LoadingSpinner-blip-4 {
1563
- animation: LoadingSpinner-pop-animation 2s infinite;
1564
- animation-delay: 0.3s;
1565
- }
1566
-
1567
- /* Pop Animation for Each Blip */
1568
- @keyframes LoadingSpinner-pop-animation {
1569
- 0% {
1570
- transform: scale(1.0);
1571
- }
1572
- 10% {
1573
- transform: scale(1.5);
1574
- }
1575
- 30% {
1576
- transform: scale(1.0);
1577
- }
1578
- 100% {
1579
- transform: scale(1.0);
1580
- }
1581
- }
1582
- `;
1583
- /*------------------------------------------------------------------------*/
1584
- /* ------------------------------ Component ----------------------------- */
1585
- /*------------------------------------------------------------------------*/
1586
- const LoadingSpinner = () => {
1587
- /*------------------------------------------------------------------------*/
1588
- /* ------------------------------- Render ------------------------------- */
1589
- /*------------------------------------------------------------------------*/
1590
- // Add all four blips to a container
1591
- return (React__default["default"].createElement("div", { className: "text-center LoadingSpinner LoadingSpinner-container" },
1592
- React__default["default"].createElement("style", null, style$8),
1593
- React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCircle, className: "LoadingSpinner-blip-1 me-1" }),
1594
- React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCircle, className: "LoadingSpinner-blip-2 me-1" }),
1595
- React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCircle, className: "LoadingSpinner-blip-3 me-1" }),
1596
- React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCircle, className: "LoadingSpinner-blip-4" })));
1597
- };
1598
-
1599
1624
  /**
1600
1625
  * A box with a tab on the top that holds buttons and other content
1601
1626
  * @author Gabe Abrams
@@ -1627,7 +1652,9 @@ const style$7 = `
1627
1652
 
1628
1653
  /* Container for Title */
1629
1654
  .TabBox-title-container {
1630
- /* Place on Left */
1655
+ display: flex;
1656
+ justify-content: space-between;
1657
+ align-items: center;
1631
1658
  position: relative;
1632
1659
  left: 0;
1633
1660
  text-align: left;
@@ -1660,6 +1687,19 @@ const style$7 = `
1660
1687
  background: #fdfdfd;
1661
1688
  }
1662
1689
 
1690
+ .TabBox-title-right-container {
1691
+ display: flex;
1692
+ flex-direction: row;
1693
+ align-items: bottom;
1694
+ height: 2.4rem;
1695
+ overflow: visible;
1696
+ }
1697
+
1698
+ .TabBox-title-right-contents {
1699
+ margin-right: 0.5rem;
1700
+ margin-bottom: 0.2rem;
1701
+ }
1702
+
1663
1703
  /* Make the TabBox's Children Appear Above Title if Overlap Occurs */
1664
1704
  .TabBox-children {
1665
1705
  position: relative;
@@ -1673,19 +1713,16 @@ const TabBox = (props) => {
1673
1713
  /*------------------------------------------------------------------------*/
1674
1714
  /* -------------------------------- Setup ------------------------------- */
1675
1715
  /*------------------------------------------------------------------------*/
1676
- /* -------------- Props ------------- */
1677
- const { title, children, noBottomPadding, noBottomMargin, } = props;
1716
+ const { title, children, topRightChildren, noBottomPadding, noBottomMargin, } = props;
1678
1717
  /*------------------------------------------------------------------------*/
1679
1718
  /* ------------------------------- Render ------------------------------- */
1680
1719
  /*------------------------------------------------------------------------*/
1681
- /*----------------------------------------*/
1682
- /* --------------- Main UI -------------- */
1683
- /*----------------------------------------*/
1684
- // Full UI
1685
1720
  return (React__default["default"].createElement("div", { className: `TabBox-container ${noBottomMargin ? '' : 'mb-2'}` },
1686
1721
  React__default["default"].createElement("style", null, style$7),
1687
1722
  React__default["default"].createElement("div", { className: "TabBox-title-container" },
1688
- React__default["default"].createElement("div", { className: "TabBox-title" }, title)),
1723
+ React__default["default"].createElement("div", { className: "TabBox-title" }, title),
1724
+ topRightChildren && (React__default["default"].createElement("div", { className: "TabBox-title-right-container" },
1725
+ React__default["default"].createElement("div", { className: "TabBox-title-right-contents" }, topRightChildren)))),
1689
1726
  React__default["default"].createElement("div", { className: `TabBox-box ps-2 pt-2 pe-2 ${noBottomPadding ? '' : 'pb-2'}` },
1690
1727
  React__default["default"].createElement("div", { className: "TabBox-children" }, children))));
1691
1728
  };
@@ -1722,7 +1759,7 @@ const RadioButton = (props) => {
1722
1759
  React__default["default"].createElement("div", { className: "me-1" },
1723
1760
  React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: selected ? freeSolidSvgIcons.faDotCircle : freeRegularSvgIcons.faCircle })),
1724
1761
  useComplexFormatting
1725
- ? (React__default["default"].createElement("pre", { className: "ps-1 text-start text-break", style: {
1762
+ ? (React__default["default"].createElement("pre", { className: "ps-1 text-start text-break m-0", style: {
1726
1763
  whiteSpace: 'pre-wrap',
1727
1764
  tabSize: 2,
1728
1765
  } }, text))
@@ -1770,7 +1807,7 @@ const CheckboxButton = (props) => {
1770
1807
  React__default["default"].createElement("div", { className: "me-1" },
1771
1808
  React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: icon })),
1772
1809
  useComplexFormatting
1773
- ? (React__default["default"].createElement("pre", { className: "ps-1 text-start text-break", style: {
1810
+ ? (React__default["default"].createElement("pre", { className: "ps-1 text-start text-break m-0", style: {
1774
1811
  whiteSpace: 'pre-wrap',
1775
1812
  tabSize: 2,
1776
1813
  } }, text))
@@ -2672,7 +2709,7 @@ const NestableItemList = (props) => {
2672
2709
  React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: childExpanded[item.id] ? freeSolidSvgIcons.faChevronDown : freeSolidSvgIcons.faChevronRight })))),
2673
2710
  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) => {
2674
2711
  onChanged(changeChecked(item.id, checked, items));
2675
- }, ariaLabel: `Select ${item.name}`, checkedVariant: Variant$1.Light }),
2712
+ }, ariaLabel: `Select ${item.name}`, checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Light }),
2676
2713
  (item.isGroup && childExpanded[item.id]) && (React__default["default"].createElement("div", { className: "NestableItemList-children-container", style: {
2677
2714
  paddingLeft: '2.2rem',
2678
2715
  } },
@@ -2695,17 +2732,62 @@ const ItemPicker = (props) => {
2695
2732
  /*------------------------------------------------------------------------*/
2696
2733
  /* -------------- Props ------------- */
2697
2734
  // Destructure all props
2698
- const { title, items, onChanged, noBottomMargin, } = props;
2735
+ const { title, items, onChanged, noBottomMargin, hideSelectAllOrNoneButtons, } = props;
2699
2736
  /*------------------------------------------------------------------------*/
2700
2737
  /* ------------------------- Component Functions ------------------------ */
2701
2738
  /*------------------------------------------------------------------------*/
2739
+ /**
2740
+ * Updates the checked state of an item (including all children)
2741
+ * @author Yuen Ler Chow
2742
+ * @param item the item to update
2743
+ * @param checked the new checked state
2744
+ * @returns the updated item
2745
+ */
2746
+ const updateItemChecked = (item, checked) => {
2747
+ if (item.isGroup) {
2748
+ return Object.assign(Object.assign({}, item), { children: item.children.map((child) => {
2749
+ return updateItemChecked(child, checked);
2750
+ }) });
2751
+ }
2752
+ return Object.assign(Object.assign({}, item), { checked });
2753
+ };
2754
+ /**
2755
+ * Selects all items in the list
2756
+ * @author Yuen Ler Chow
2757
+ */
2758
+ const handleSelectAll = () => {
2759
+ const updatedItems = items.map((item) => {
2760
+ return updateItemChecked(item, true);
2761
+ });
2762
+ onChanged(updatedItems);
2763
+ };
2764
+ /**
2765
+ * Deselects all items in the list
2766
+ * @author Yuen Ler Chow
2767
+ */
2768
+ const handleDeselectAll = () => {
2769
+ const updatedItems = items.map((item) => {
2770
+ return updateItemChecked(item, false);
2771
+ });
2772
+ onChanged(updatedItems);
2773
+ };
2702
2774
  /*------------------------------------------------------------------------*/
2703
2775
  /* ------------------------------- Render ------------------------------- */
2704
2776
  /*------------------------------------------------------------------------*/
2705
2777
  /*----------------------------------------*/
2706
2778
  /* --------------- Main UI -------------- */
2707
2779
  /*----------------------------------------*/
2708
- return (React__default["default"].createElement(TabBox, { title: title, noBottomMargin: noBottomMargin },
2780
+ // Select all/none
2781
+ const selectAllOrNone = (!hideSelectAllOrNoneButtons
2782
+ ? (React__default["default"].createElement("div", { className: "d-flex h-100 align-items-end flex-row" },
2783
+ React__default["default"].createElement("div", { className: "d-flex justify-content-end" },
2784
+ React__default["default"].createElement("div", { className: "me-2", style: { fontSize: '1.2rem' } }, "Select"),
2785
+ React__default["default"].createElement("div", { className: "btn-group", role: "group" },
2786
+ 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"),
2787
+ React__default["default"].createElement("button", { type: "button", "aria-label": "Deselect all contexts", className: "btn btn-secondary py-0", onClick: handleDeselectAll }, "None")))))
2788
+ : undefined);
2789
+ // Main UI
2790
+ return (React__default["default"].createElement(TabBox, { title: title, noBottomMargin: noBottomMargin, topRightChildren: selectAllOrNone },
2709
2791
  React__default["default"].createElement("div", { style: { overflowX: 'auto' } },
2710
2792
  React__default["default"].createElement(NestableItemList, { items: items, onChanged: onChanged }))));
2711
2793
  };
@@ -3077,7 +3159,7 @@ const IntelliTable = (props) => {
3077
3159
  param: column.param,
3078
3160
  visible: checked,
3079
3161
  });
3080
- }, checked: columnVisibilityMap[column.param], ariaLabel: `show "${column.title}" column in the ${title} table`, checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Secondary }));
3162
+ }, checked: columnVisibilityMap[column.param], ariaLabel: `show "${column.title}" column in the ${title} table`, checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Light }));
3081
3163
  }),
3082
3164
  React__default["default"].createElement("div", { className: "mt-3" }, "Or you can:"),
3083
3165
  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: () => {
@@ -4266,6 +4348,7 @@ const LogReviewer = (props) => {
4266
4348
  // Create filter UI
4267
4349
  filterDrawer = (React__default["default"].createElement(ItemPicker, { title: "Context", items: pickableItems, onChanged: (updatedItems) => {
4268
4350
  // Update our state
4351
+ const newContextFilterState = Object.assign({}, pendingContextFilterState);
4269
4352
  updatedItems.forEach((pickableItem) => {
4270
4353
  if (pickableItem.isGroup) {
4271
4354
  // Has subcontexts
@@ -4273,36 +4356,29 @@ const LogReviewer = (props) => {
4273
4356
  // Built-in
4274
4357
  // Treat as if these were top-level contexts
4275
4358
  pickableItem.children.forEach((subcontextItem) => {
4276
- const newContextFilterState = Object.assign(Object.assign({}, pendingContextFilterState), { [subcontextItem.id]: 'checked' in subcontextItem
4277
- && subcontextItem.checked });
4278
- dispatch({
4279
- type: ActionType$6.UpdateContextFilterState,
4280
- contextFilterState: newContextFilterState,
4281
- });
4359
+ newContextFilterState[subcontextItem.id] = ('checked' in subcontextItem
4360
+ && subcontextItem.checked);
4282
4361
  });
4283
4362
  }
4284
4363
  else {
4285
4364
  // Not built-in
4286
4365
  pickableItem.children.forEach((subcontextItem) => {
4287
4366
  if (!subcontextItem.isGroup) {
4288
- const newContextFilterState = Object.assign(Object.assign({}, pendingContextFilterState), { [pickableItem.id]: Object.assign(Object.assign({}, pendingContextFilterState[pickableItem.id]), { [subcontextItem.id]: subcontextItem.checked }) });
4289
- dispatch({
4290
- type: ActionType$6.UpdateContextFilterState,
4291
- contextFilterState: newContextFilterState,
4292
- });
4367
+ newContextFilterState[pickableItem.id][subcontextItem.id] = (subcontextItem.checked);
4293
4368
  }
4294
4369
  });
4295
4370
  }
4296
4371
  }
4297
4372
  else {
4298
4373
  // No subcontexts
4299
- const newContextFilterState = Object.assign(Object.assign({}, pendingContextFilterState), { [pickableItem.id]: pickableItem.checked });
4300
- dispatch({
4301
- type: ActionType$6.UpdateContextFilterState,
4302
- contextFilterState: newContextFilterState,
4303
- });
4374
+ newContextFilterState[pickableItem.id] = (pickableItem.checked);
4304
4375
  }
4305
4376
  });
4377
+ // Update state
4378
+ dispatch({
4379
+ type: ActionType$6.UpdateContextFilterState,
4380
+ contextFilterState: newContextFilterState,
4381
+ });
4306
4382
  } }));
4307
4383
  }
4308
4384
  else if (expandedFilterDrawer === FilterDrawer.Tag) {
@@ -16419,6 +16495,27 @@ const visitEndpointOnAnotherServer = (opts) => __awaiter(void 0, void 0, void 0,
16419
16495
  return body;
16420
16496
  });
16421
16497
 
16498
+ const punctuationRegex = /[!@#$%^&*(),.?\/;:'"\-\[\]]/g;
16499
+ /**
16500
+ * Get number of words in string
16501
+ * @author Gardenia Liu
16502
+ * @author Allison Zhang
16503
+ * @author Gabe Abrams
16504
+ * @param text the string to check
16505
+ * @returns number of words in the string
16506
+ */
16507
+ const getWordCount = (text) => {
16508
+ const trimmedTextWithoutPunctuation = (text
16509
+ // Remove leading and trailing whitespace
16510
+ .trim()
16511
+ // Remove punctuation
16512
+ .replace(punctuationRegex, ''));
16513
+ if (trimmedTextWithoutPunctuation.length === 0) {
16514
+ return 0;
16515
+ }
16516
+ return trimmedTextWithoutPunctuation.split(/\s+/g).length;
16517
+ };
16518
+
16422
16519
  /**
16423
16520
  * Days of the week
16424
16521
  * @author Gabe Abrams
@@ -16503,6 +16600,7 @@ exports.getMonthName = getMonthName;
16503
16600
  exports.getOrdinal = getOrdinal;
16504
16601
  exports.getPartOfDay = getPartOfDay;
16505
16602
  exports.getTimeInfoInET = getTimeInfoInET;
16603
+ exports.getWordCount = getWordCount;
16506
16604
  exports.handleError = handleError;
16507
16605
  exports.handleSuccess = handleSuccess;
16508
16606
  exports.idify = idify;