Skip to the content

Capsid menu

app/components/admin/media-bulk-bar.tsx

69 lines and 11 functions in DrDustinEdwards/dustinedwards-info, at commit 1590ba4 on 2026-10-24. This file on GitHub.

Functions

The functions of media-bulk-bar.tsx
FunctionLinesComplexityCoveredCRAPMutation
BulkBar11 to 721No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code
<anonymous:32>32 to 321No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code
<anonymous:33>33 to 331No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code
<anonymous:42>42 to 521No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code
<anonymous:44>44 to 441No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code
<anonymous:45>45 to 451No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code
<anonymous:48>48 to 492No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code
<anonymous:51>51 to 511No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code
<anonymous:56>56 to 561No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code
<anonymous:58>58 to 581No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code
<anonymous:66>66 to 661No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code

Imports

It imports app/components/admin/bulk-tag-controls.tsx, app/components/admin/toast.tsx, app/lib/clipboard.ts, app/lib/media/byte-size.mjs. Imported by app/components/admin/media-grid.tsx.

Source

import { BulkTagControls } from "~/components/admin/bulk-tag-controls";
import { toast } from "~/components/admin/toast";
import { copyText } from "~/lib/clipboard";
import { byteSize } from "~/lib/media/byte-size.mjs";

import type { Route } from "../../routes/+types/admin.media._index";

type Listing = Extract<Route.ComponentProps["loaderData"], { detail: unknown }>;

/** The bar over a selection: count and size, copy addresses, bulk tags, trash, clear. */
export function BulkBar({
  objects,
  chosen,
  setSelected,
  setConfirmingTrash,
  tagCounts,
}: {
  objects: Listing["objects"];
  chosen: string[];
  setSelected: (keys: string[]) => void;
  setConfirmingTrash: (value: boolean) => void;
  tagCounts: Listing["tagCounts"];
}) {
  return (
    <div className="cap-bulk" role="group" aria-label="Bulk actions">
      {/* Not a live region: it mounts with its number, and the grid's own status region announces it. */}
      <p className="cap-bulk-count">
        {chosen.length} selected{" "}
        <span className="cap-bulk-detail">
          {byteSize(
            objects
              .filter((o) => chosen.includes(o.key))
              .reduce((sum, o) => sum + o.size, 0),
          )}
        </span>
      </p>
      <div className="cap-bulk-actions">
        {/* `type="button"` so it never submits the form it sits in. One address per line: that pastes usefully. */}
        <button
          type="button"
          className="cap-btn"
          onClick={() => {
            const addresses = objects
              .filter((o) => chosen.includes(o.key))
              .map((o) => o.url)
              .join("\n");
            copyText(addresses)
              .then(() =>
                toast(`Copied ${chosen.length} address${chosen.length === 1 ? "" : "es"}`),
              )
              .catch(() => toast("The clipboard refused. Open a file to copy its address."));
          }}
        >
          Copy addresses
        </button>
        <BulkTagControls listId="media-bulk-tags" options={tagCounts.map((t) => t.tag)} />
        {/* A `type="button"` opens the modal, because submitting from here would skip it. */}
        <button type="button" className="cap-btn" onClick={() => setConfirmingTrash(true)}>
          Move to trash
        </button>
      </div>
      <button
        type="button"
        className="cap-btn cap-bulk-clear"
        data-variant="quiet"
        onClick={() => setSelected([])}
      >
        Clear
      </button>
    </div>
  );
}