Skip to the content

Capsid menu

components/switch/switch.ts

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

Functions

The functions of switch.ts
FunctionLinesComplexityCoveredCRAPMutation
stateWord4 to 6233%OkCRAP 3.2No data: mutation testing has not run on this repo yet
syncWord9 to 14317%WatchCRAP 8.2No data: mutation testing has not run on this repo yet
setSwitch18 to 23217%OkCRAP 4.3No data: mutation testing has not run on this repo yet
setBusy28 to 33317%WatchCRAP 8.2No data: mutation testing has not run on this repo yet
enhance37 to 50213%OkCRAP 4.7No data: mutation testing has not run on this repo yet
onChange42 to 4210%OkCRAP 2No data: mutation testing has not run on this repo yet
<anonymous:44>44 to 4710%OkCRAP 2No data: mutation testing has not run on this repo yet
<anonymous:49>49 to 4910%OkCRAP 2No data: mutation testing has not run on this repo yet
<anonymous:49>49 to 491100%OkCRAP 1No data: mutation testing has not run on this repo yet

Imports

It imports no other module of this repo. Imported by components/authorship/authorship.ts, components/switch-reason/switch-reason.react.tsx, components/switch-reason/switch-reason.ts, components/switch/switch.react.tsx.

Source

// The switch's behaviour: keeps the state word beside it true. The word is "On" and "Off"
// unless the .cap-switch-state element sets its own in data-on and data-off.

export function stateWord(checked: boolean, on = "On", off = "Off"): string {
  return checked ? on : off;
}

// Writes the word for one switch's current state.
export function syncWord(label: Element): void {
  const input = label.querySelector<HTMLInputElement>("input[role='switch']");
  const word = label.querySelector<HTMLElement>(".cap-switch-state");
  if (!input || !word) return;
  word.textContent = stateWord(input.checked, word.dataset.on, word.dataset.off);
}

// Moves a switch from code (an Undo, a value from the server) and keeps its word true.
// Fires no change event, so it cannot loop back into the app's own handler.
export function setSwitch(label: Element, checked: boolean): void {
  const input = label.querySelector<HTMLInputElement>("input[role='switch']");
  if (!input) return;
  input.checked = checked;
  syncWord(label);
}

// Marks a switch pending while a request for its change runs (aria-busy, drawn as a dashed
// outline), and clears it when the request has answered. The switch is not disabled, so
// focus stays where it is.
export function setBusy(label: Element, busy: boolean): void {
  const input = label.querySelector<HTMLInputElement>("input[role='switch']");
  if (!input) return;
  if (busy) input.setAttribute("aria-busy", "true");
  else input.removeAttribute("aria-busy");
}

// Attaches to every [data-cap="switch"] under root that is not attached yet. Returns a
// function that detaches them all.
export function enhance(root: ParentNode = document): () => void {
  const undo: Array<() => void> = [];
  for (const label of root.querySelectorAll<HTMLElement>("[data-cap='switch']:not([data-cap-ready])")) {
    label.dataset.capReady = "";
    syncWord(label);
    const onChange = () => syncWord(label);
    label.addEventListener("change", onChange);
    undo.push(() => {
      label.removeEventListener("change", onChange);
      delete label.dataset.capReady;
    });
  }
  return () => undo.forEach((f) => f());
}