# All files / src/components/display FloatingActionBar.tsx

0% Statements 0/73
100% Branches 1/1
100% Functions 1/1
0% Lines 0/73

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122

     /**
     * Floating action bar shown at the bottom of EventStream during multi-select mode.
     *
     * Displays selection count, select all/deselect all toggle, Copy button,
     * Forward button, and Cancel.
     * Pure presentational component -- no useGrackle().
     */

    import { type JSX } from "react";
    import { Copy, Forward, X } from "lucide-react";
    import { motion } from "motion/react";
    import { ICON_SM } from "../../utils/iconSize.js";
    import { Tooltip } from "./Tooltip.js";
    import styles from "./FloatingActionBar.module.scss";

    /** Props for the FloatingActionBar component. */
    export interface FloatingActionBarProps {
      /** Number of currently selected events. */
      selectedCount: number;
      /** Total number of selectable (content-bearing) events. */
      totalSelectable: number;
      /** Called when "Select all" is clicked. */
      onSelectAll: () => void;
      /** Called when "Deselect all" is clicked. */
      onDeselectAll: () => void;
      /** Called when "Copy" is clicked. */
      onCopy: () => void;
      /** Called when "Forward" is clicked. Omit to hide the Forward button. */
      onForward?: () => void;
      /** When true, the Forward button is rendered but disabled. */
      forwardDisabled?: boolean;
      /** Called when "Cancel" (X) is clicked. */
      onCancel: () => void;
    }

    /**
     * Floating action bar for multi-select mode in EventStream.
     *
     * Positioned absolutely at the bottom of the EventStream wrapper.
     * Uses motion.div for animated entrance/exit.
     */
    export function FloatingActionBar({
      selectedCount,
      totalSelectable,
      onSelectAll,
      onDeselectAll,
      onCopy,
      onForward,
      forwardDisabled = false,
      onCancel,
    }: FloatingActionBarProps): JSX.Element {
      const allSelected = selectedCount > 0 && selectedCount >= totalSelectable;

      return (
        <motion.div
          className={styles.bar}
          initial={{ opacity: 0, y: 12 }}
          animate={{ opacity: 1, y: 0 }}
          exit={{ opacity: 0, y: 12 }}
          transition={{ duration: 0.15 }}
          data-testid="floating-action-bar"
        >
          <div className={styles.left}>
            <span className={styles.count} data-testid="floating-bar-count">
              {selectedCount} selected
            </span>
            <button
              type="button"
              className={styles.toggleButton}
              onClick={allSelected ? onDeselectAll : onSelectAll}
              data-testid="floating-bar-select-all"
            >
              {allSelected ? "Deselect all" : "Select all"}
            </button>
          </div>
          <div className={styles.right}>
            <button
              type="button"
              className={styles.copyButton}
              onClick={onCopy}
              disabled={selectedCount === 0}
              data-testid="floating-bar-copy"
            >
              <Copy size={ICON_SM} aria-hidden="true" />
              Copy
            </button>
            {onForward !== undefined && (
              <Tooltip
                text={
                  forwardDisabled ? "No active sessions to forward to" : "Forward to another session"
                }
              >
                <button
                  type="button"
                  className={styles.forwardButton}
                  aria-disabled={forwardDisabled || selectedCount === 0}
                  onClick={() => {
                    if (!forwardDisabled && selectedCount > 0) {
                      onForward();
                    }
                  }}
                  data-testid="floating-bar-forward"
                >
                  <Forward size={ICON_SM} aria-hidden="true" />
                  Forward
                </button>
              </Tooltip>
            )}
            <button
              type="button"
              className={styles.cancelButton}
              onClick={onCancel}
              aria-label="Cancel selection"
              data-testid="floating-bar-cancel"
            >
              <X size={ICON_SM} aria-hidden="true" />
            </button>
          </div>
        </motion.div>
      );
    }