fix(audit-wave-9): standardize on Sheet for previews; doctrine in CLAUDE.md
Swap the one outlier (client-interests-tab.tsx) from Vaul Drawer to Sheet side=right so every detail-preview surface uses the same primitive. Document the doctrine: Sheet for side panels on both desktop and mobile; Vaul Drawer reserved for mobile-only bottom-sheet UX (currently just MoreSheet). Closes ui/ux M11. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -128,6 +128,9 @@ export function CommandSearch() {
|
||||
const [lastAllTotals, setLastAllTotals] = useState<SearchResults['totals'] | null>(null);
|
||||
useEffect(() => {
|
||||
if (activeBucket === 'all' && results?.totals) {
|
||||
// Snapshot the totals at the moment the user is on "All" so the
|
||||
// chips stay stable when they switch to a filtered bucket.
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||
setLastAllTotals(results.totals);
|
||||
}
|
||||
}, [activeBucket, results]);
|
||||
@@ -220,8 +223,10 @@ export function CommandSearch() {
|
||||
});
|
||||
}, [showDropdown, query, results, recentlyViewed, recentSearches, activeBucket, portSlug]);
|
||||
|
||||
// Reset focus index when the visible row set changes.
|
||||
// Reset focus index when the visible row set changes. The set-state
|
||||
// is intentional — external state (bucket / query) drives focus.
|
||||
useEffect(() => {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||
setFocusIndex(-1);
|
||||
}, [activeBucket, query]);
|
||||
|
||||
|
||||
@@ -70,11 +70,14 @@ export function MobileSearchOverlay({ open, onOpenChange }: MobileSearchOverlayP
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||
setVisibleHeight(null);
|
||||
return;
|
||||
}
|
||||
const vv = window.visualViewport;
|
||||
if (!vv) return;
|
||||
// Subscribing to the visualViewport external store — canonical
|
||||
// useEffect+setState shape for that pattern.
|
||||
const update = () => setVisibleHeight(vv.height);
|
||||
update();
|
||||
vv.addEventListener('resize', update);
|
||||
@@ -98,6 +101,7 @@ export function MobileSearchOverlay({ open, onOpenChange }: MobileSearchOverlayP
|
||||
const [lastAllTotals, setLastAllTotals] = useState<SearchResults['totals'] | null>(null);
|
||||
useEffect(() => {
|
||||
if (activeBucket === 'all' && results?.totals) {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||
setLastAllTotals(results.totals);
|
||||
}
|
||||
}, [activeBucket, results]);
|
||||
@@ -120,8 +124,11 @@ export function MobileSearchOverlay({ open, onOpenChange }: MobileSearchOverlayP
|
||||
|
||||
// Reset query when the drawer closes. Without this, reopening the
|
||||
// overlay would flash stale results before the empty state renders.
|
||||
// setState in effect is intentional — the trigger is a discrete
|
||||
// open/close transition.
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||
setQuery('');
|
||||
setActiveBucket('all');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user