mirror of
https://github.com/nexu-io/open-design.git
synced 2026-06-01 03:14:35 +07:00
* fix: language dropdown overflow in settings modal (#281) Smart-place the Settings language menu: open downward when there is enough space below the trigger, otherwise fall back to opening upward. Expose the available height to CSS via --menu-available-h so the menu is bounded in either direction, scrolls internally, and prevents long labels from overflowing item rows. * fix(SettingsDialog): address PR review feedback for #281 - Document the 200px threshold used to prefer downward placement. - Close the language menu on window resize so the placement (computed once at open) cannot become stale. - Cap the menu max-height at 60vh so it stays comfortable on short viewports (mobile landscape ~400-500px tall).
This commit is contained in:
parent
0c00f241e7
commit
6805628458
2 changed files with 29 additions and 7 deletions
|
|
@ -103,6 +103,15 @@ export function SettingsDialog({
|
|||
};
|
||||
}, [languageOpen]);
|
||||
|
||||
// Close the language menu on window resize so its placement (computed on
|
||||
// open) cannot end up stale relative to the new viewport dimensions.
|
||||
useEffect(() => {
|
||||
if (!languageOpen) return;
|
||||
const handleResize = () => setLanguageOpen(false);
|
||||
window.addEventListener('resize', handleResize);
|
||||
return () => window.removeEventListener('resize', handleResize);
|
||||
}, [languageOpen]);
|
||||
|
||||
const installedCount = useMemo(
|
||||
() => agents.filter((a) => a.available).length,
|
||||
[agents],
|
||||
|
|
@ -531,15 +540,23 @@ export function SettingsDialog({
|
|||
</span>
|
||||
<Icon name="chevron-down" size={16} />
|
||||
</button>
|
||||
{languageOpen && languageMenuRect ? (
|
||||
{languageOpen && languageMenuRect ? (() => {
|
||||
const spaceBelow = window.innerHeight - languageMenuRect.bottom;
|
||||
const spaceAbove = languageMenuRect.top;
|
||||
// Prefer downward if at least 200px available (enough for ~5 options)
|
||||
const openDownward = spaceBelow >= spaceAbove || spaceBelow >= 200;
|
||||
return (
|
||||
<div
|
||||
className="settings-language-menu"
|
||||
role="menu"
|
||||
style={{
|
||||
bottom: window.innerHeight - languageMenuRect.top + 6,
|
||||
top: openDownward ? languageMenuRect.bottom + 6 : undefined,
|
||||
bottom: openDownward
|
||||
? undefined
|
||||
: window.innerHeight - languageMenuRect.top + 6,
|
||||
left: languageMenuRect.left,
|
||||
width: languageMenuRect.width,
|
||||
'--menu-available-h': `${languageMenuRect.top - 6}px`,
|
||||
'--menu-available-h': `${(openDownward ? spaceBelow : spaceAbove) - 6}px`,
|
||||
} as React.CSSProperties}
|
||||
>
|
||||
{LOCALES.map((code) => {
|
||||
|
|
@ -569,7 +586,8 @@ export function SettingsDialog({
|
|||
);
|
||||
})}
|
||||
</div>
|
||||
) : null}
|
||||
);
|
||||
})() : null}
|
||||
</div>
|
||||
</section>
|
||||
) : null}
|
||||
|
|
|
|||
|
|
@ -1355,9 +1355,12 @@ code {
|
|||
z-index: 1000;
|
||||
max-width: calc(100vw - 24px);
|
||||
/* 360px = 9 locales × ~40px; --menu-available-h is set by JS to the
|
||||
distance between the trigger and the viewport top, so the menu never
|
||||
overflows above the viewport regardless of trigger placement. */
|
||||
max-height: min(360px, var(--menu-available-h, 360px));
|
||||
distance between the trigger and whichever viewport edge the menu
|
||||
opens toward, so the menu never overflows the viewport regardless of
|
||||
placement direction (above or below the trigger). The 60vh cap keeps
|
||||
the menu from feeling cramped on short viewports (mobile landscape,
|
||||
~400–500px tall). */
|
||||
max-height: min(360px, 60vh, var(--menu-available-h, 360px));
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
display: flex;
|
||||
|
|
@ -1375,6 +1378,7 @@ code {
|
|||
align-items: center;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
min-height: 40px;
|
||||
padding: 10px 12px;
|
||||
border: 0;
|
||||
border-radius: 9px;
|
||||
|
|
|
|||
Loading…
Reference in a new issue