Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 100 additions & 89 deletions src/components/TopNavClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,16 @@ import platformSelectorStyles from './platformSelector/style.module.scss';

const mainSections = mainSectionsWithDropdowns;

// Add a helper hook for portal dropdown positioning
// Helper hook for dropdown positioning
// Returns both absolute (for portals) and fixed (for inline) coordinates
function useDropdownPosition(triggerRef, open) {
const [position, setPosition] = useState({top: 0, left: 0, width: 0});
const [position, setPosition] = useState<{
top: number;
left: number;
width: number;
fixedTop: number;
fixedLeft: number;
} | null>(null);
useEffect(() => {
function updatePosition() {
if (triggerRef.current && open) {
Expand All @@ -22,6 +29,8 @@ function useDropdownPosition(triggerRef, open) {
top: rect.bottom + window.scrollY,
left: rect.left + window.scrollX,
width: rect.width,
fixedTop: rect.bottom,
fixedLeft: rect.left,
});
}
}
Expand Down Expand Up @@ -139,8 +148,29 @@ export default function TopNavClient({platforms}: {platforms: Platform[]}) {
}
}
}
function handleKeyDown(e: KeyboardEvent) {
if (e.key === 'Escape') {
if (conceptsDropdownOpen) {
setConceptsDropdownOpen(false);
conceptsBtnRef.current?.focus();
}
if (moreDropdownOpen) {
setMoreDropdownOpen(false);
moreBtnRef.current?.focus();
}
if (platformDropdownOpen) {
setPlatformDropdownOpen(false);
setPlatformDropdownByClick(false);
platformBtnRef.current?.focus();
}
}
}
document.addEventListener('mousedown', handleClick);
return () => document.removeEventListener('mousedown', handleClick);
document.addEventListener('keydown', handleKeyDown);
return () => {
document.removeEventListener('mousedown', handleClick);
document.removeEventListener('keydown', handleKeyDown);
};
}, [
platformDropdownOpen,
platformDropdownByClick,
Expand Down Expand Up @@ -298,6 +328,39 @@ export default function TopNavClient({platforms}: {platforms: Platform[]}) {
/>
</svg>
</button>
{conceptsDropdownOpen && conceptsPosition && (
<div
ref={conceptsDropdownRef}
className="bg-white dark:bg-black border border-[var(--gray-a3)] dark:border-[var(--gray-7)] shadow-lg z-50 min-w-[220px] p-2 rounded-b-md rounded-t-none"
style={{
position: 'fixed',
top: conceptsPosition.fixedTop,
left: conceptsPosition.fixedLeft,
minWidth: conceptsPosition.width,
overflowY: 'auto',
scrollbarWidth: 'none',
msOverflowStyle: 'none',
}}
onClick={e => e.stopPropagation()}
Comment thread
sentry[bot] marked this conversation as resolved.
>
<style>{`
.dark .concepts-dropdown-link {
color: #fff !important;
}
`}</style>
{mainSections
.find(s => s.label === 'Concepts')
?.dropdown?.map(dropdown => (
<Link
key={dropdown.href}
href={dropdown.href}
className="concepts-dropdown-link block px-4 py-2 text-[var(--gray-12)] dark:text-white hover:bg-[var(--gray-3)] dark:hover:bg-[var(--gray-8)] rounded text-[0.875rem] font-normal font-sans no-underline"
>
{dropdown.label}
</Link>
))}
</div>
)}
</div>
) : section.label === 'Manage' ? (
<div
Expand Down Expand Up @@ -350,6 +413,39 @@ export default function TopNavClient({platforms}: {platforms: Platform[]}) {
/>
</svg>
</button>
{moreDropdownOpen && morePosition && (
<div
ref={moreDropdownRef}
className="bg-white dark:bg-black border border-[var(--gray-a3)] dark:border-[var(--gray-7)] shadow-lg z-50 min-w-[220px] p-2 rounded-b-md rounded-t-none"
style={{
position: 'fixed',
top: morePosition.fixedTop,
left: morePosition.fixedLeft,
minWidth: morePosition.width,
overflowY: 'auto',
scrollbarWidth: 'none',
msOverflowStyle: 'none',
}}
onClick={e => e.stopPropagation()}
>
<style>{`
.dark .more-dropdown-link {
color: #fff !important;
}
`}</style>
{mainSections
.find(s => s.label === 'Manage')
?.dropdown?.map(dropdown => (
<Link
key={dropdown.href}
href={dropdown.href}
className="more-dropdown-link block px-4 py-2 text-[var(--gray-12)] dark:text-white hover:bg-[var(--gray-3)] dark:hover:bg-[var(--gray-8)] rounded text-[0.875rem] font-normal font-sans no-underline"
>
{dropdown.label}
</Link>
))}
</div>
)}
</div>
) : section.label === 'SDKs' ? (
<a
Expand Down Expand Up @@ -382,6 +478,7 @@ export default function TopNavClient({platforms}: {platforms: Platform[]}) {
</div>
{/* Portal-based dropdowns */}
{platformDropdownOpen &&
sdksPosition &&
ReactDOM.createPortal(
<div
ref={platformDropdownRef}
Expand Down Expand Up @@ -501,92 +598,6 @@ export default function TopNavClient({platforms}: {platforms: Platform[]}) {
</div>,
document.body
)}
{conceptsDropdownOpen &&
ReactDOM.createPortal(
<div
ref={conceptsDropdownRef}
className="absolute left-0 bg-white dark:bg-black border border-[var(--gray-a3)] dark:border-[var(--gray-7)] shadow-lg z-50 min-w-[220px] p-2 rounded-b-md rounded-t-none"
style={{
position: 'absolute',
top: conceptsPosition.top,
left: conceptsPosition.left,
minWidth: conceptsPosition.width,
overflowY: 'auto',
scrollbarWidth: 'none',
msOverflowStyle: 'none',
}}
onClick={e => e.stopPropagation()}
onMouseEnter={() => {
clearTimeout(closeTimers.current.concepts);
}}
onMouseLeave={() => {
closeTimers.current.concepts = setTimeout(() => {
setConceptsDropdownOpen(false);
}, 150);
}}
>
<style>{`
.dark .concepts-dropdown-link {
color: #fff !important;
}
`}</style>
{mainSections
.find(s => s.label === 'Concepts')
?.dropdown?.map(dropdown => (
<Link
key={dropdown.href}
href={dropdown.href}
className="concepts-dropdown-link block px-4 py-2 text-[var(--gray-12)] dark:text-white hover:bg-[var(--gray-3)] dark:hover:bg-[var(--gray-8)] rounded text-[0.875rem] font-normal font-sans no-underline"
>
{dropdown.label}
</Link>
))}
</div>,
document.body
)}
{moreDropdownOpen &&
ReactDOM.createPortal(
<div
ref={moreDropdownRef}
className="absolute left-0 bg-white dark:bg-black border border-[var(--gray-a3)] dark:border-[var(--gray-7)] shadow-lg z-50 min-w-[220px] p-2 rounded-b-md rounded-t-none"
style={{
position: 'absolute',
top: morePosition.top,
left: morePosition.left,
minWidth: morePosition.width,
overflowY: 'auto',
scrollbarWidth: 'none',
msOverflowStyle: 'none',
}}
onClick={e => e.stopPropagation()}
onMouseEnter={() => {
clearTimeout(closeTimers.current.more);
}}
onMouseLeave={() => {
closeTimers.current.more = setTimeout(() => {
setMoreDropdownOpen(false);
}, 150);
}}
>
<style>{`
.dark .more-dropdown-link {
color: #fff !important;
}
`}</style>
{mainSections
.find(s => s.label === 'Manage')
?.dropdown?.map(dropdown => (
<Link
key={dropdown.href}
href={dropdown.href}
className="more-dropdown-link block px-4 py-2 text-[var(--gray-12)] dark:text-white hover:bg-[var(--gray-3)] dark:hover:bg-[var(--gray-8)] rounded text-[0.875rem] font-normal font-sans no-underline"
>
{dropdown.label}
</Link>
))}
</div>,
document.body
)}
<style>{`
.scrollbar-hide::-webkit-scrollbar {
display: none;
Expand Down
Loading