All checks were successful
CI / Lint, typecheck, test (push) Successful in 3m35s
CI / Build container images (push) Has been skipped
CD / Build and push images (push) Successful in 3m44s
CD / Deploy to Test (push) Successful in 15s
CD / Smoke tests against Test (push) Successful in 1m17s
CD / Promote to Int (push) Successful in 11s
CI / Auth e2e pack (push) Successful in 5m36s
CI / Import/export fidelity gate (push) Successful in 46s
- content footer: the collab status is an icon (wifi/off/refresh, localized tooltip + visually-hidden text, class/data-status hooks kept for e2e) on the left, the legal links right-aligned; read mode drops the editor frame and its inner padding, edit mode keeps it - menus (page overflow, user, notifications bell, pond switcher) close on outside click and Escape via a shared useDismissable hook; the bell got its missing tooltip - side panels (labels, history) stack vertically in one column - edit mode gains a Save-version icon (prompt for the name, POST /pages/:id/versions); the history panel lists contributors by display name — more than three collapse to two plus an expandable ellipsis (PageVersionView.contributors resolved server-side, deleted users drop out) - search finds partial words via a LIKE fallback next to the tsquery (FTS matches still rank first; regression-pinned in the db pack), and the recent-searches list has a clear button - pond owners create labels directly in the label picker (plus a permanent link to the full manager); add/remove/delete buttons across the pond settings (members, access rules, labels, files) and the watch/unwatch toggles in pond/user settings are icon buttons now — class hooks and accessible names unchanged for the e2e packs Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1
29 lines
982 B
TypeScript
29 lines
982 B
TypeScript
import { useEffect, type RefObject } from 'react';
|
|
|
|
/**
|
|
* Closes a dropdown/menu when the user clicks anywhere outside `ref` or
|
|
* presses Escape (M10 follow-up). Pass the menu's *container* (trigger +
|
|
* popup) so clicks on the trigger keep working as a toggle.
|
|
*/
|
|
export function useDismissable(
|
|
ref: RefObject<HTMLElement | null>,
|
|
open: boolean,
|
|
onClose: () => void,
|
|
): void {
|
|
useEffect(() => {
|
|
if (!open) return undefined;
|
|
function onPointerDown(event: PointerEvent): void {
|
|
if (ref.current && !ref.current.contains(event.target as Node)) onClose();
|
|
}
|
|
function onKeyDown(event: KeyboardEvent): void {
|
|
if (event.key === 'Escape') onClose();
|
|
}
|
|
document.addEventListener('pointerdown', onPointerDown);
|
|
document.addEventListener('keydown', onKeyDown);
|
|
return () => {
|
|
document.removeEventListener('pointerdown', onPointerDown);
|
|
document.removeEventListener('keydown', onKeyDown);
|
|
};
|
|
}, [ref, open, onClose]);
|
|
}
|