61 lines · 1.9 KB · TypeScript
RawDownload
import { useMemo, useState } from "react"; import { cn } from "../../lib/utils";import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip"; const REDACTED_TEXT_ALPHABET = "abcdefghjkmnpqrstuvwxyz23456789"; function redactedPlaceholder(value: string): string {  let state = 0x811c9dc5;  for (let index = 0; index < value.length; index += 1) {    state ^= value.charCodeAt(index);    state = Math.imul(state, 0x01000193);  }   const nextChar = () => {    state = Math.imul(state ^ (state >>> 13), 0x85ebca6b);    state = Math.imul(state ^ (state >>> 16), 0xc2b2ae35);    return REDACTED_TEXT_ALPHABET[Math.abs(state) % REDACTED_TEXT_ALPHABET.length] ?? "x";  };   return Array.from(value, (char) => {    if (char === "@" || char === "." || char === "-" || char === "_") return char;    return nextChar();  }).join("");} export function RedactedSensitiveText(props: {  readonly value: string | null | undefined;  readonly ariaLabel: string;  readonly revealTooltip: string;  readonly hideTooltip: string;  readonly className?: string;}) {  const [revealed, setRevealed] = useState(false);  const value = props.value?.trim();  const redacted = useMemo(() => (value ? redactedPlaceholder(value) : ""), [value]);   if (!value) return null;   return (    <Tooltip>      <TooltipTrigger        render={          <button            type="button"            className={cn(              "min-w-0 cursor-pointer rounded-sm font-mono text-[11px] leading-none transition hover:text-foreground",              revealed ? "text-muted-foreground" : "select-none text-muted-foreground blur-[2px]",              props.className,            )}            onClick={() => setRevealed((current) => !current)}            aria-label={props.ariaLabel}          >            {revealed ? value : redacted}          </button>        }      />      <TooltipPopup side="top">{revealed ? props.hideTooltip : props.revealTooltip}</TooltipPopup>    </Tooltip>  );}