feat: add compact Toolbox tiles and details overlay

This commit is contained in:
2026-07-23 13:02:29 +02:00
parent 803397ff52
commit 2c13c56362
20 changed files with 1009 additions and 486 deletions
+38 -96
View File
@@ -1,4 +1,4 @@
import { useEffect, useRef } from 'react';
import { ModalPanel } from './ModalPanel';
interface HelpPanelProps {
open: boolean;
@@ -6,104 +6,46 @@ interface HelpPanelProps {
}
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()}
<ModalPanel
className="help-panel"
labelledBy="help-title"
onClose={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>
<header>
<div>
<p className="eyebrow">Toolbox guide</p>
<h2 id="help-title">Choose and arrange your tools</h2>
</div>
</section>
</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>Details:</strong> use the light bulb to review privacy,
requirements, categories, tags, version, and source before opening.
</p>
<p>
<strong>Filter:</strong> search, choose a category, or select a tag
from a tool's information panel. Select the 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>
</div>
</ModalPanel>
);
}
const FOCUSABLE_SELECTOR = [
'a[href]',
'button:not([disabled])',
'input:not([disabled])',
'select:not([disabled])',
'[tabindex]:not([tabindex="-1"])',
].join(',');