Skip to the content

Capsid menu

components/permission-matrix/permission-matrix.react.tsx

133 lines and 12 functions in DrDustinEdwards/capsomer, at commit d3966cc on 2026-10-24. This file on GitHub.

Functions

The functions of permission-matrix.react.tsx
FunctionLinesComplexityCoveredCRAPMutation
Check15 to 232No data: no coverage report in this mapNo data: no coverage report in this mapNo data: mutation testing has not run on this repo yet
Names25 to 361No data: no coverage report in this mapNo data: no coverage report in this mapNo data: mutation testing has not run on this repo yet
<anonymous:28>28 to 332No data: no coverage report in this mapNo data: no coverage report in this mapNo data: mutation testing has not run on this repo yet
PermissionMatrix38 to 1403No data: no coverage report in this mapNo data: no coverage report in this mapNo data: mutation testing has not run on this repo yet
choose43 to 461No data: no coverage report in this mapNo data: no coverage report in this mapNo data: mutation testing has not run on this repo yet
<anonymous:55>55 to 602No data: no coverage report in this mapNo data: no coverage report in this mapNo data: mutation testing has not run on this repo yet
<anonymous:57>57 to 571No data: no coverage report in this mapNo data: no coverage report in this mapNo data: mutation testing has not run on this repo yet
<anonymous:72>72 to 761No data: no coverage report in this mapNo data: no coverage report in this mapNo data: mutation testing has not run on this repo yet
<anonymous:80>80 to 951No data: no coverage report in this mapNo data: no coverage report in this mapNo data: mutation testing has not run on this repo yet
<anonymous:85>85 to 933No data: no coverage report in this mapNo data: no coverage report in this mapNo data: mutation testing has not run on this repo yet
<anonymous:102>102 to 1152No data: no coverage report in this mapNo data: no coverage report in this mapNo data: mutation testing has not run on this repo yet
<anonymous:122>122 to 1352No data: no coverage report in this mapNo data: no coverage report in this mapNo data: mutation testing has not run on this repo yet

Imports

It imports components/permission-matrix/permission-matrix.ts. No module of this repo imports it.

Source

import { useId, useState } from "react";
import { holders, holdings, type PermMatrix, type PermView } from "./permission-matrix.ts";

export interface PermissionMatrixProps {
  matrix: PermMatrix;
  // The table region's name and the agent cards' list name.
  label?: string;
  defaultView?: PermView;
  // Controlled view, for an app that keeps it in the address.
  view?: PermView;
  onViewChange?: (view: PermView) => void;
  note?: string;
}

const Check = ({ held }: { held: boolean }) => (
  <span className="cap-perms-check" aria-hidden="true">
    {held ? (
      <svg viewBox="0 0 16 16" focusable="false">
        <path fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" d="m3.5 8.5 3 3 6-7" />
      </svg>
    ) : null}
  </span>
);

function Names({ names }: { names: string[] }) {
  return (
    <>
      {names.map((n, i) => (
        <span key={n}>
          {i > 0 ? ", " : ""}
          <span className="cap-mono">{n}</span>
        </span>
      ))}
    </>
  );
}

export function PermissionMatrix(props: PermissionMatrixProps) {
  const { matrix, label = "Permissions by agent", defaultView = "agent", note } = props;
  const id = useId();
  const [own, setOwn] = useState<PermView>(defaultView);
  const view = props.view ?? own;
  const choose = (v: PermView) => {
    setOwn(v);
    props.onViewChange?.(v);
  };
  const byAgent = holdings(matrix);
  const byPerm = holders(matrix);

  return (
    <div className="cap-perms" data-cap="permission-matrix" data-cap-ready="" data-current={view}>
      <div className="cap-perms-bar">
        <fieldset className="cap-seg">
          <legend className="cap-sr-only">View</legend>
          {(["agent", "permission"] as const).map((v) => (
            <label key={v}>
              <input type="radio" name={`${id}-view`} value={v} checked={view === v} onChange={() => choose(v)} />
              {v === "agent" ? "By agent" : "By permission"}
            </label>
          ))}
        </fieldset>
        {note ? <p className="cap-perms-note">{note}</p> : null}
      </div>

      <div className="cap-perms-view" data-view="agent" hidden={view !== "agent"}>
        <div className="cap-perms-wide">
          <div className="cap-table-wrap" role="region" aria-label={label} tabIndex={0}>
            <table className="cap-table">
              <thead>
                <tr>
                  <th scope="col">Agent</th>
                  {matrix.permissions.map((p) => (
                    <th scope="col" className="cap-perms-col" key={p}>
                      {p}
                    </th>
                  ))}
                </tr>
              </thead>
              <tbody>
                {matrix.agents.map((a) => (
                  <tr key={a.name}>
                    <th scope="row" className="cap-perms-agent cap-mono">
                      {a.name}
                    </th>
                    {matrix.permissions.map((p) => {
                      const held = a.holds.includes(p);
                      return (
                        <td className="cap-perms-cell" data-held={held ? "yes" : "no"} key={p}>
                          <Check held={held} />
                          <span className="cap-sr-only">{held ? "Yes" : "No"}</span>
                        </td>
                      );
                    })}
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
        <div className="cap-perms-narrow">
          <ul className="cap-perms-cards" aria-label={label}>
            {byAgent.map((h) => (
              <li className="cap-perms-card" key={h.agent}>
                <span className="cap-perms-name cap-mono">{h.agent}</span>
                {h.held.length ? (
                  <div className="cap-perms-holds">
                    <span className="cap-perms-key">Holds:</span> {h.held.join(", ")}
                  </div>
                ) : (
                  <div className="cap-perms-holds" data-empty="">
                    Holds no permissions
                  </div>
                )}
              </li>
            ))}
          </ul>
        </div>
      </div>

      <div className="cap-perms-view" data-view="permission" hidden={view !== "permission"}>
        <ul className="cap-perms-cards" aria-label="Agents by permission">
          {byPerm.map((h) => (
            <li className="cap-perms-card" key={h.permission}>
              <span className="cap-perms-name">{h.permission}</span>
              {h.agents.length ? (
                <div className="cap-perms-holds">
                  <span className="cap-perms-key">Held by:</span> <Names names={h.agents} />
                </div>
              ) : (
                <div className="cap-perms-holds" data-empty="">
                  Nobody holds this
                </div>
              )}
            </li>
          ))}
        </ul>
      </div>
    </div>
  );
}