add better keyboard nav and fix sonar lint

This commit is contained in:
Eduard Gert
2026-06-18 13:56:41 +02:00
parent 79e7dce47e
commit 3175b880e4
15 changed files with 473 additions and 312 deletions
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState, type ReactNode } from "react";
import { useEffect, useRef, useState, type KeyboardEvent, type ReactNode } from "react";
import { useTranslation } from "react-i18next";
import { Check, Copy } from "lucide-react";
import { cn } from "@/lib/cn";
@@ -21,6 +21,7 @@ type CopyToClipboardProps = {
variant?: CopyToClipboardVariant;
"aria-label"?: string;
tabIndex?: number;
onKeyDown?: (e: KeyboardEvent<HTMLButtonElement>) => void;
};
export const CopyToClipboard = ({
@@ -34,6 +35,7 @@ export const CopyToClipboard = ({
variant = "default",
"aria-label": ariaLabel,
tabIndex = 0,
onKeyDown,
}: CopyToClipboardProps) => {
const { t } = useTranslation();
const wrapperRef = useRef<HTMLButtonElement>(null);
@@ -69,6 +71,7 @@ export const CopyToClipboard = ({
type="button"
ref={wrapperRef}
onClick={handleClick}
onKeyDown={onKeyDown}
tabIndex={tabIndex}
aria-label={resolvedLabel}
aria-live="polite"
@@ -13,12 +13,13 @@ export const ConfirmDialog = forwardRef<HTMLDivElement, ConfirmDialogProps>(func
ref,
) {
return (
<div
role="dialog"
aria-modal="true"
<dialog
open
aria-label={ariaLabel}
aria-labelledby={ariaLabelledBy}
className={"wails-draggable select-none flex flex-col items-center"}
className={
"wails-draggable select-none flex flex-col items-center static bg-transparent text-inherit p-0 m-0 max-w-none max-h-none border-0 w-full"
}
>
<div
ref={ref}
@@ -29,6 +30,6 @@ export const ConfirmDialog = forwardRef<HTMLDivElement, ConfirmDialogProps>(func
>
{children}
</div>
</div>
</dialog>
);
});
@@ -33,8 +33,6 @@ export default function FancyToggleSwitch({
}: Readonly<Props>) {
const switchId = React.useId();
const descriptionId = React.useId();
const childrenRef = React.useRef<HTMLDivElement>(null);
const switchRef = React.useRef<HTMLButtonElement>(null);
if (loading) {
const shimmer =
@@ -68,21 +66,8 @@ export default function FancyToggleSwitch({
);
}
const fromChildren = (target: EventTarget | null) =>
target instanceof Node && childrenRef.current?.contains(target);
const handleClick = (event: React.MouseEvent) => {
if (disabled || fromChildren(event.target)) return;
const target = event.target as HTMLElement;
// Let the switch own its own click so focus + state stay together.
if (target.closest("button,input,a,[role=switch]")) return;
switchRef.current?.click();
switchRef.current?.focus();
};
return (
<div
onClick={handleClick}
{...(disabled ? { inert: "" } : {})}
className={cn(
"cursor-default transition-all duration-300 relative z-[1]",
@@ -93,10 +78,8 @@ export default function FancyToggleSwitch({
>
<div className={"flex justify-between gap-10"}>
<div className={cn(textWrapperClassName)}>
<Label as="div" className={labelClassName}>
<label htmlFor={switchId} className={"cursor-default"}>
{label}
</label>
<Label htmlFor={switchId} className={labelClassName}>
{label}
</Label>
<HelpText margin={false}>
<span id={descriptionId}>{helpText}</span>
@@ -104,7 +87,6 @@ export default function FancyToggleSwitch({
</div>
<div className={"mt-2 pr-1"}>
<ToggleSwitch
ref={switchRef}
id={switchId}
checked={value}
onCheckedChange={onChange}
@@ -113,11 +95,7 @@ export default function FancyToggleSwitch({
/>
</div>
</div>
{children && value ? (
<div className="mt-4" ref={childrenRef}>
{children}
</div>
) : null}
{children && value ? <div className="mt-4">{children}</div> : null}
</div>
);
}