Skip to content

Commit 87b1020

Browse files
committed
Add initial dropdown component.
Signed-off-by: bgravenorst <byron.gravenorst@consensys.net>
1 parent ff8d86b commit 87b1020

8 files changed

Lines changed: 319 additions & 106 deletions

File tree

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
.dropdown {
2+
position: relative;
3+
padding-left: var(--ifm-menu-link-padding-horizontal);
4+
margin-top: 0.5rem;
5+
font-family: var(--font-mm-sans);
6+
border-bottom: 1px solid var(--ifm-toc-border-color);
7+
padding-bottom: 1rem;
8+
margin-bottom: 1rem;
9+
}
10+
11+
.versionItem {
12+
display: flex;
13+
align-items: center;
14+
font-size: 1.4rem;
15+
font-weight: 500;
16+
cursor: pointer;
17+
color: var(--color-text);
18+
user-select: none;
19+
width: 100%;
20+
}
21+
22+
.versionItem:hover {
23+
color: var(--ifm-color-primary);
24+
}
25+
26+
.chevron {
27+
font-size: 2.2rem;
28+
line-height: 1;
29+
display: inline-block;
30+
margin-left: 0.5rem;
31+
}
32+
33+
.label {
34+
flex: 1;
35+
line-height: 1.5;
36+
}
37+
38+
.menu {
39+
position: absolute;
40+
top: 100%;
41+
left: 0;
42+
background: var(--general-white);
43+
border: 1px solid var(--general-gray-light);
44+
border-radius: 4px;
45+
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.1);
46+
margin-top: 0.3rem;
47+
z-index: 100;
48+
width: 100%;
49+
min-width: 180px;
50+
}
51+
52+
.menuItem {
53+
padding: 0.75rem 1rem;
54+
font-size: 1.4rem;
55+
cursor: pointer;
56+
color: var(--color-text);
57+
background: var(--general-white);
58+
}
59+
60+
.menuItem:hover {
61+
background: var(--ifm-hover-overlay);
62+
}
63+
64+
/* Dark mode support */
65+
[data-theme='dark'] .menu {
66+
background: var(--general-black-light);
67+
border-color: var(--general-white);
68+
}
69+
70+
[data-theme='dark'] .menuItem {
71+
color: var(--general-white);
72+
}
73+
74+
[data-theme='dark'] .menuItem:hover {
75+
background: var(--general-black-mid);
76+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import React, { useState, useRef, useEffect } from 'react';
2+
import { useHistory, useLocation } from '@docusaurus/router';
3+
import versions from '@site/gator_versions.json';
4+
import clsx from 'clsx';
5+
import styles from './SidebarVersionDropdown.module.css';
6+
7+
export default function SidebarVersionDropdown({ pluginId = 'gator' }: { pluginId?: string }) {
8+
const history = useHistory();
9+
const location = useLocation();
10+
const dropdownRef = useRef<HTMLDivElement>(null);
11+
const [open, setOpen] = useState(false);
12+
13+
const latestVersion = versions[0];
14+
const allVersions = [latestVersion, 'current', ...versions.filter(v => v !== latestVersion)];
15+
16+
const getVersionLabel = (version: string) => {
17+
if (version === 'current') return 'development';
18+
if (version === latestVersion) return `latest (${version})`;
19+
return version;
20+
};
21+
22+
const getActiveVersion = () => {
23+
const segments = location.pathname.split('/');
24+
const versionSegment = segments[2];
25+
26+
if (versionSegment === 'development') return 'current';
27+
if (!versionSegment || versionSegment === '' || versionSegment === latestVersion) return latestVersion;
28+
if (versions.includes(versionSegment)) return versionSegment;
29+
return latestVersion;
30+
};
31+
32+
const currentVersion = getActiveVersion();
33+
34+
const handleSelect = (version: string) => {
35+
let versionPath = '';
36+
if (version === 'current') {
37+
versionPath = `/${pluginId}/development/`;
38+
} else if (version === latestVersion) {
39+
versionPath = `/${pluginId}/`;
40+
} else {
41+
versionPath = `/${pluginId}/${version}/`;
42+
}
43+
44+
history.push(versionPath);
45+
setOpen(false);
46+
};
47+
48+
const toggleDropdown = () => setOpen((prev) => !prev);
49+
50+
useEffect(() => {
51+
const handleClickOutside = (e: MouseEvent) => {
52+
if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {
53+
setOpen(false);
54+
}
55+
};
56+
document.addEventListener('mousedown', handleClickOutside);
57+
return () => document.removeEventListener('mousedown', handleClickOutside);
58+
}, []);
59+
60+
return (
61+
<div ref={dropdownRef} className={styles.dropdown}>
62+
<div
63+
className={clsx('menu__link', styles.versionItem)}
64+
onClick={toggleDropdown}
65+
role="button"
66+
tabIndex={0}
67+
onKeyDown={(e) => e.key === 'Enter' && toggleDropdown()}
68+
>
69+
<span className={styles.label}>Version: {getVersionLabel(currentVersion)}</span>
70+
<span className={clsx('menu__caret', styles.chevron)} aria-hidden></span>
71+
</div>
72+
{open && (
73+
<ul className={styles.menu} role="menu">
74+
{allVersions.map((version) => (
75+
<li
76+
key={version}
77+
className={styles.menuItem}
78+
onClick={() => handleSelect(version)}
79+
role="menuitem"
80+
>
81+
{getVersionLabel(version)}
82+
</li>
83+
))}
84+
</ul>
85+
)}
86+
</div>
87+
);
88+
}

src/theme/DocSidebar/Desktop/Content/index.tsx

Lines changed: 0 additions & 24 deletions
This file was deleted.

src/theme/DocSidebar/Desktop/Content/styles.module.css

Lines changed: 0 additions & 82 deletions
This file was deleted.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import React from 'react';
2+
import clsx from 'clsx';
3+
import { useThemeConfig } from '@docusaurus/theme-common';
4+
import { useLocation } from '@docusaurus/router';
5+
import Logo from '@theme/Logo';
6+
import CollapseButton from '@theme-original/DocSidebar/Desktop/CollapseButton';
7+
import Content from '@theme-original/DocSidebar/Desktop/Content';
8+
import SidebarVersionDropdown from '@site/src/components/SidebarVersionDropdown/SidebarVersionDropdown';
9+
import type { PropSidebar } from '@docusaurus/plugin-content-docs';
10+
import styles from './styles.module.css';
11+
12+
type Props = {
13+
path: string;
14+
sidebar: PropSidebar;
15+
onCollapse: () => void;
16+
isHidden: boolean;
17+
};
18+
19+
function DocSidebarDesktop({ path, sidebar, onCollapse, isHidden }: Props) {
20+
const {
21+
navbar: { hideOnScroll },
22+
docs: {
23+
sidebar: { hideable },
24+
},
25+
} = useThemeConfig();
26+
27+
const location = useLocation();
28+
const isGatorDocs = location.pathname.startsWith('/gator');
29+
30+
let versionDropdown = null;
31+
try {
32+
if (isGatorDocs) {
33+
versionDropdown = (
34+
<div className={styles.versionDropdownContainer}>
35+
<SidebarVersionDropdown pluginId="gator" />
36+
</div>
37+
);
38+
}
39+
} catch (e) {
40+
console.error('Failed to render version dropdown:', e);
41+
}
42+
43+
return (
44+
<div
45+
className={clsx(
46+
styles.sidebar,
47+
hideOnScroll && styles.sidebarWithHideableNavbar,
48+
isHidden && styles.sidebarHidden
49+
)}
50+
>
51+
{hideOnScroll && <Logo tabIndex={-1} className={styles.sidebarLogo} />}
52+
{versionDropdown}
53+
<Content path={path} sidebar={sidebar} />
54+
{hideable && <CollapseButton onClick={onCollapse} />}
55+
</div>
56+
);
57+
}
58+
59+
export default DocSidebarDesktop;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
@media (min-width: 997px) {
2+
.sidebar {
3+
display: flex;
4+
flex-direction: column;
5+
height: 100%;
6+
padding-top: var(--ifm-navbar-height);
7+
width: var(--doc-sidebar-width);
8+
}
9+
10+
.sidebarWithHideableNavbar {
11+
padding-top: 0;
12+
}
13+
14+
.sidebarHidden {
15+
opacity: 0;
16+
visibility: hidden;
17+
}
18+
19+
.sidebarLogo {
20+
display: flex !important;
21+
align-items: center;
22+
margin: 0 var(--ifm-navbar-padding-horizontal);
23+
min-height: var(--ifm-navbar-height);
24+
max-height: var(--ifm-navbar-height);
25+
color: inherit !important;
26+
text-decoration: none !important;
27+
}
28+
29+
.sidebarLogo img {
30+
margin-right: 0.5rem;
31+
height: 2rem;
32+
}
33+
}
34+
35+
.sidebarLogo {
36+
display: none;
37+
}

0 commit comments

Comments
 (0)