feat: redesign Toolbox navigation and personalization

This commit is contained in:
2026-07-23 00:33:03 +02:00
parent 587aadb79b
commit 67ba5e3eca
25 changed files with 1075 additions and 427 deletions

View File

@@ -0,0 +1,109 @@
import { useEffect, useRef } from 'react';
interface HelpPanelProps {
open: boolean;
onClose: () => void;
}
export function HelpPanel({ open, onClose }: HelpPanelProps) {
const panel = useRef<HTMLElement>(null);
useEffect(() => {
if (!open) return;
const returnFocus =
document.activeElement instanceof HTMLElement
? document.activeElement
: null;
const dialog = panel.current;
const focusable = () =>
dialog
? [...dialog.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)]
: [];
focusable()[0]?.focus();
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
event.preventDefault();
onClose();
return;
}
if (event.key !== 'Tab') return;
const elements = focusable();
const first = elements[0];
const last = elements.at(-1);
if (!first || !last) return;
if (event.shiftKey && document.activeElement === first) {
event.preventDefault();
last.focus();
} else if (!event.shiftKey && document.activeElement === last) {
event.preventDefault();
first.focus();
}
};
document.addEventListener('keydown', handleKeyDown);
return () => {
document.removeEventListener('keydown', handleKeyDown);
returnFocus?.focus();
};
}, [open, onClose]);
if (!open) return null;
return (
<div
className="dialog-backdrop"
role="presentation"
onMouseDown={(event) => event.target === event.currentTarget && onClose()}
>
<section
ref={panel}
className="help-panel"
role="dialog"
aria-modal="true"
aria-labelledby="help-title"
tabIndex={-1}
>
<header>
<div>
<p className="eyebrow">Toolbox guide</p>
<h2 id="help-title">Choose and arrange your tools</h2>
</div>
<button
className="close-button"
type="button"
aria-label="Close help"
onClick={onClose}
>
×
</button>
</header>
<div className="help-steps">
<p>
<strong>Open:</strong> click anywhere on a tool tile that is not a
control.
</p>
<p>
<strong>Filter:</strong> search, choose a category, or click a tag.
Click the selected tag again to clear it.
</p>
<p>
<strong>Arrange:</strong> pin frequently used tools into their own
section, then drag the grip to reorder. The grip also supports
keyboard dragging with Space, arrow keys, and Escape.
</p>
<p>
<strong>Privacy:</strong> each tile reports its declared processing,
uploads, telemetry, and browser requirements before you open it.
</p>
</div>
</section>
</div>
);
}
const FOCUSABLE_SELECTOR = [
'a[href]',
'button:not([disabled])',
'input:not([disabled])',
'select:not([disabled])',
'[tabindex]:not([tabindex="-1"])',
].join(',');