mirror of
https://github.com/expressjs/expressjs.com.git
synced 2026-02-22 03:51:33 +00:00
feat : improve language picker component (#2040)
Signed-off-by: Shubham Oulkar <oulkarshubhu@gmail.com> Co-authored-by: Sebastian Beltran <bjohansebas@gmail.com>
This commit is contained in:
4
.github/workflows/lighthouse.yml
vendored
4
.github/workflows/lighthouse.yml
vendored
@@ -29,12 +29,12 @@ jobs:
|
||||
run: |
|
||||
PREVIEW_URL="https://deploy-preview-${{ github.event.pull_request.number }}--expressjscom-preview.netlify.app"
|
||||
echo "PREVIEW_URL=$PREVIEW_URL" >> "$GITHUB_ENV"
|
||||
MAX_RETRIES=12
|
||||
MAX_RETRIES=10
|
||||
DELAY=10
|
||||
|
||||
echo "Checking Netlify preview: $PREVIEW_URL"
|
||||
for i in $(seq 1 $MAX_RETRIES); do
|
||||
if curl -s --head --max-time 5 "$PREVIEW_URL" | grep "200 OK" > /dev/null; then
|
||||
if curl -s -I "$PREVIEW_URL" | grep "HTTP/.* 200" > /dev/null; then
|
||||
echo "✅ Preview is live!"
|
||||
echo "skip_lighthouse=false" >> "$GITHUB_ENV"
|
||||
exit 0
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
# Home
|
||||
home: Home
|
||||
|
||||
# Getting started
|
||||
getting_started: Getting started
|
||||
installing: Installing
|
||||
|
||||
@@ -12,11 +12,6 @@
|
||||
<nav id="navbar" aria-label="primary">
|
||||
<input id="q" placeholder="🔎 search">
|
||||
<ul id="navmenu">
|
||||
<li>
|
||||
<a href="/" id="home-menu" {% if page.menu=='home' %} class="active" {% endif %}>
|
||||
{{ site.data[page.lang].menu.home }}
|
||||
</a>
|
||||
</li>
|
||||
<li id="getting-started-menu" class="submenu">
|
||||
<a href="/{{ page.lang }}/starter/installing.html" {% if page.menu=='starter' %} class="active" {% endif%}>
|
||||
{{ site.data[page.lang].menu.getting_started }}
|
||||
@@ -252,6 +247,5 @@
|
||||
<span id="icon-sun">{% include icons/sun.svg %}</span>
|
||||
</button>
|
||||
{% include language-picker.html %}
|
||||
{% include language-picker-mobile.html %}
|
||||
</div>
|
||||
</header>
|
||||
@@ -1,20 +0,0 @@
|
||||
<div id="mobile-menu">
|
||||
<div id="language-picker-button" class="header-btn">
|
||||
{% include icons/i18n.svg %}
|
||||
</div>
|
||||
</div>
|
||||
<div id="language-picker-menu">
|
||||
<div id="navbar">
|
||||
<ul id="navmenu">
|
||||
{% assign url_parts = page.url | split: '/' %}
|
||||
|
||||
{% assign url_remainder = url_parts | slice: 2, url_parts.size | join: '/' %}
|
||||
|
||||
{% for lang in site.data.languages %}
|
||||
<li class="submenu">
|
||||
<a href="/{{ lang.code }}/{{ url_remainder }}">{{ lang.name }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,22 +1,21 @@
|
||||
<div class="desktop-lang-switcher">
|
||||
{% assign url_parts = page.url | split: '/' %}
|
||||
{% assign url_remainder = url_parts | slice: 2, url_parts.size | join: '/' %}
|
||||
{% assign current_lang = page.lang %}
|
||||
<button class="lang-btn" type="button" aria-haspopup="menu" aria-label="Change language">
|
||||
<span id="current-lang"></span>
|
||||
</button>
|
||||
<ul class="lang-list submenu-content" aria-labelledby="current-lang" >
|
||||
{% for lang in site.data.languages %}
|
||||
<li>
|
||||
<a href="/{{ lang.code }}/{{ url_remainder }}">
|
||||
{% if lang.code == current_lang %}
|
||||
<div class="desktop-lang-switcher">
|
||||
<button id="langBtn" class="lang-btn" type="button" aria-expanded="false" aria-haspopup="listbox" aria-label="Change language">
|
||||
{% include icons/i18n.svg %}
|
||||
</button>
|
||||
<ul id="langList" class="lang-list" role="listbox">
|
||||
{% for lang in site.data.languages %}
|
||||
<li>
|
||||
<a href="/{{ lang.code }}/{{ url_remainder }}">
|
||||
{% if lang.code == current_lang %}
|
||||
<strong>{{ lang.name }}</strong>
|
||||
{% else %}
|
||||
{% else %}
|
||||
{{ lang.name }}
|
||||
{% endif %}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<div id="languageData" data-languages='{{ site.data.languages | jsonify }}' style="display:none;"></div>
|
||||
{% endif %}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
102
css/style.css
102
css/style.css
@@ -802,42 +802,12 @@ button.lang-btn {
|
||||
cursor: pointer;
|
||||
color: var(--card-fg);
|
||||
padding: 0.2rem;
|
||||
font-size: inherit;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 0.8em;
|
||||
height: 0.5em;
|
||||
background-color: var(--card-fg);
|
||||
clip-path: polygon(100% 0%, 0 0%, 50% 100%);
|
||||
cursor: pointer;
|
||||
pointer-events: none;
|
||||
transition: transform 0.2s ease-in-out;
|
||||
}
|
||||
width: fit-content;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
div.desktop-lang-switcher {
|
||||
position: relative;
|
||||
> button.lang-btn {
|
||||
&:is(:focus, :hover) {
|
||||
& + ul {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* enable lang list tabbing on keyboard focus */
|
||||
&:focus-within .lang-list {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
> ul.lang-list {
|
||||
display: none;
|
||||
@@ -845,11 +815,14 @@ div.desktop-lang-switcher {
|
||||
position: absolute;
|
||||
list-style: none;
|
||||
visibility: hidden;
|
||||
&:is(:hover, :focus) {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
left: -75px;
|
||||
z-index: 100;
|
||||
background-color: var(--card-bg);
|
||||
border: 1px solid;
|
||||
border-radius: 10px;
|
||||
padding: 0px;
|
||||
min-width: max-content;
|
||||
|
||||
li a {
|
||||
display: block;
|
||||
padding: 5px 20px 5px 20px;
|
||||
@@ -859,11 +832,22 @@ div.desktop-lang-switcher {
|
||||
background: var(--hover-bg);
|
||||
}
|
||||
}
|
||||
|
||||
> li:first-child > a {
|
||||
border-start-start-radius: 10px;
|
||||
border-start-end-radius: 10px;
|
||||
}
|
||||
|
||||
> li:last-child > a {
|
||||
border-end-start-radius: 10px;
|
||||
border-end-end-radius: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
/* rotate arrow */
|
||||
&:is(:hover,:focus-within) button.lang-btn::after {
|
||||
transform: rotate(180deg);
|
||||
> ul.lang-list.open {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -953,9 +937,6 @@ div.desktop-lang-switcher {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#language-picker-menu {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* TOC side menu */
|
||||
|
||||
@@ -1320,26 +1301,6 @@ h2 a {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
#language-picker-menu #navmenu>li:first-child {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#language-picker-menu #navmenu {
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#language-picker-menu {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
}
|
||||
}
|
||||
|
||||
/* TOC responsive */
|
||||
@@ -1448,10 +1409,6 @@ h2 a {
|
||||
header {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#mobile-menu {
|
||||
display:none;
|
||||
}
|
||||
}
|
||||
|
||||
/* For image callouts in writing-middleware.md */
|
||||
@@ -1589,9 +1546,6 @@ h2 a {
|
||||
margin-right: 0;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#blog-doc .blog-details + p > img {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
@@ -1661,18 +1615,14 @@ blockquote {
|
||||
}
|
||||
|
||||
@media all and (max-width: 1110px) {
|
||||
.desktop-lang-switcher {
|
||||
display: none;
|
||||
.algolia-autocomplete {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
#mobile-menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.algolia-autocomplete {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
#navbar {
|
||||
padding: 0;
|
||||
top: 1px;
|
||||
|
||||
10
js/app.js
10
js/app.js
@@ -1,15 +1,5 @@
|
||||
const languageElement = document.getElementById('languageData');
|
||||
const languagesData = languageElement ? JSON.parse(languageElement.dataset.languages) : [];
|
||||
const langDisplay = document.getElementById('current-lang');
|
||||
const i18nMsgBox = document.getElementById("i18n-notice-box");
|
||||
|
||||
// display current language in language picker component
|
||||
if (langDisplay) {
|
||||
const currentLanguage = window.location.pathname.split('/')[1];
|
||||
const matchedLang = languagesData.find(lang => lang.code === currentLanguage);
|
||||
langDisplay.textContent = matchedLang ? matchedLang.name : 'English';
|
||||
}
|
||||
|
||||
// add/remove class 'scroll' on scroll by 5px
|
||||
const scrollTarget = document.querySelector('.logo-container');
|
||||
const scrollObserver = new IntersectionObserver(
|
||||
|
||||
73
js/menu.js
73
js/menu.js
@@ -65,15 +65,35 @@ for (const el of itemsMenu) {
|
||||
}
|
||||
|
||||
// Mobile Menu and Language Picker
|
||||
|
||||
const langBtn = document.getElementById("langBtn");
|
||||
const langList = document.getElementById("langList");
|
||||
const linkItemsMenu = document.querySelectorAll(".submenu > a");
|
||||
const languageItems = document.querySelectorAll("#language-picker-menu > #navbar > #navmenu > .submenu > a");
|
||||
const languagePickerMenu = document.querySelector("#language-picker-menu > #navbar > #navmenu");
|
||||
const menu = document.querySelector("#navmenu");
|
||||
const overlay = document.querySelector("#overlay");
|
||||
const navButton = document.querySelector("#nav-button");
|
||||
const languagePickerButton = document.querySelector("#language-picker-button");
|
||||
|
||||
function closeLangList() {
|
||||
langList.classList.remove("open");
|
||||
langBtn.setAttribute("aria-expanded", false);
|
||||
}
|
||||
|
||||
function toggleLangList() {
|
||||
const isOpen = langList.classList.toggle("open");
|
||||
langBtn.setAttribute("aria-expanded", isOpen);
|
||||
}
|
||||
|
||||
// toggle on button click
|
||||
langBtn.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
toggleLangList();
|
||||
});
|
||||
|
||||
// close on outside click except for lang btn
|
||||
document.body.addEventListener("click", (e) => {
|
||||
if (!langList.contains(e.target)) {
|
||||
closeLangList();
|
||||
}
|
||||
});
|
||||
|
||||
for (const el of linkItemsMenu) {
|
||||
el.addEventListener("click", (e) => {
|
||||
@@ -85,56 +105,23 @@ for (const el of linkItemsMenu) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
for (const el of languageItems) {
|
||||
el.addEventListener("click", (e) => {
|
||||
const href = el.getAttribute("href");
|
||||
|
||||
if (href && href !== "#") {
|
||||
languagePickerMenu?.classList.remove("opens");
|
||||
overlay?.classList.remove("blurs");
|
||||
document.body.classList.remove("no-scroll");
|
||||
|
||||
window.location.href = href;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
navButton?.addEventListener("click", () => {
|
||||
const isLanguageMenuOpen = languagePickerMenu?.classList.contains("opens");
|
||||
if (isLanguageMenuOpen) {
|
||||
languagePickerMenu?.classList.remove("opens");
|
||||
menu?.classList.toggle("opens");
|
||||
} else {
|
||||
menu?.classList.toggle("opens");
|
||||
overlay?.classList.toggle("blurs");
|
||||
document.body.classList.toggle("no-scroll");
|
||||
}
|
||||
});
|
||||
|
||||
languagePickerButton?.addEventListener("click", () => {
|
||||
const isMenuOpen = menu?.classList.contains("opens");
|
||||
if (isMenuOpen) {
|
||||
menu?.classList.remove("opens");
|
||||
languagePickerMenu?.classList.toggle("opens");
|
||||
} else {
|
||||
languagePickerMenu?.classList.toggle("opens");
|
||||
overlay?.classList.toggle("blurs");
|
||||
document.body.classList.toggle("no-scroll");
|
||||
}
|
||||
menu?.classList.toggle("opens");
|
||||
overlay?.classList.toggle("blurs");
|
||||
document.body.classList.toggle("no-scroll");
|
||||
});
|
||||
|
||||
overlay?.addEventListener("click", () => {
|
||||
if (menu?.classList.contains("opens")) {
|
||||
menu.classList.remove("opens");
|
||||
}
|
||||
if (languagePickerMenu?.classList.contains("opens")) {
|
||||
languagePickerMenu.classList.remove("opens");
|
||||
}
|
||||
overlay.classList.remove("blurs");
|
||||
document.body.classList.remove("no-scroll");
|
||||
// TODO : write helper function
|
||||
const isOpen = langList.classList.toggle("open");
|
||||
langBtn.setAttribute("aria-expanded", isOpen);
|
||||
});
|
||||
|
||||
document
|
||||
|
||||
Reference in New Issue
Block a user