Skip to content

Commit 00e199a

Browse files
feat : improve language picker component (#2040)
Signed-off-by: Shubham Oulkar <[email protected]> Co-authored-by: Sebastian Beltran <[email protected]>
1 parent 72ff6ba commit 00e199a

File tree

8 files changed

+74
-177
lines changed

8 files changed

+74
-177
lines changed

.github/workflows/lighthouse.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ jobs:
2929
run: |
3030
PREVIEW_URL="https://deploy-preview-${{ github.event.pull_request.number }}--expressjscom-preview.netlify.app"
3131
echo "PREVIEW_URL=$PREVIEW_URL" >> "$GITHUB_ENV"
32-
MAX_RETRIES=12
32+
MAX_RETRIES=10
3333
DELAY=10
3434
3535
echo "Checking Netlify preview: $PREVIEW_URL"
3636
for i in $(seq 1 $MAX_RETRIES); do
37-
if curl -s --head --max-time 5 "$PREVIEW_URL" | grep "200 OK" > /dev/null; then
37+
if curl -s -I "$PREVIEW_URL" | grep "HTTP/.* 200" > /dev/null; then
3838
echo "✅ Preview is live!"
3939
echo "skip_lighthouse=false" >> "$GITHUB_ENV"
4040
exit 0

_data/en/menu.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
# Home
2-
home: Home
3-
41
# Getting started
52
getting_started: Getting started
63
installing: Installing

_includes/header.html

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,6 @@
1212
<nav id="navbar" aria-label="primary">
1313
<input id="q" placeholder="🔎 search">
1414
<ul id="navmenu">
15-
<li>
16-
<a href="/" id="home-menu" {% if page.menu=='home' %} class="active" {% endif %}>
17-
{{ site.data[page.lang].menu.home }}
18-
</a>
19-
</li>
2015
<li id="getting-started-menu" class="submenu">
2116
<a href="/{{ page.lang }}/starter/installing.html" {% if page.menu=='starter' %} class="active" {% endif%}>
2217
{{ site.data[page.lang].menu.getting_started }}
@@ -252,6 +247,5 @@
252247
<span id="icon-sun">{% include icons/sun.svg %}</span>
253248
</button>
254249
{% include language-picker.html %}
255-
{% include language-picker-mobile.html %}
256250
</div>
257251
</header>

_includes/language-picker-mobile.html

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

_includes/language-picker.html

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
1-
<div class="desktop-lang-switcher">
21
{% assign url_parts = page.url | split: '/' %}
32
{% assign url_remainder = url_parts | slice: 2, url_parts.size | join: '/' %}
43
{% assign current_lang = page.lang %}
5-
<button class="lang-btn" type="button" aria-haspopup="menu" aria-label="Change language">
6-
<span id="current-lang"></span>
7-
</button>
8-
<ul class="lang-list submenu-content" aria-labelledby="current-lang" >
9-
{% for lang in site.data.languages %}
10-
<li>
11-
<a href="/{{ lang.code }}/{{ url_remainder }}">
12-
{% if lang.code == current_lang %}
4+
<div class="desktop-lang-switcher">
5+
<button id="langBtn" class="lang-btn" type="button" aria-expanded="false" aria-haspopup="listbox" aria-label="Change language">
6+
{% include icons/i18n.svg %}
7+
</button>
8+
<ul id="langList" class="lang-list" role="listbox">
9+
{% for lang in site.data.languages %}
10+
<li>
11+
<a href="/{{ lang.code }}/{{ url_remainder }}">
12+
{% if lang.code == current_lang %}
1313
<strong>{{ lang.name }}</strong>
14-
{% else %}
14+
{% else %}
1515
{{ lang.name }}
16-
{% endif %}
17-
</a>
18-
</li>
19-
{% endfor %}
20-
</ul>
21-
<div id="languageData" data-languages='{{ site.data.languages | jsonify }}' style="display:none;"></div>
22-
</div>
16+
{% endif %}
17+
</a>
18+
</li>
19+
{% endfor %}
20+
</ul>
21+
</div>

css/style.css

Lines changed: 26 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -802,54 +802,27 @@ button.lang-btn {
802802
cursor: pointer;
803803
color: var(--card-fg);
804804
padding: 0.2rem;
805-
font-size: inherit;
806-
display: flex;
807-
justify-content: center;
808-
align-items: center;
809-
gap: 1rem;
810-
811-
&::after {
812-
content: "";
813-
display: block;
814-
width: 0.8em;
815-
height: 0.5em;
816-
background-color: var(--card-fg);
817-
clip-path: polygon(100% 0%, 0 0%, 50% 100%);
818-
cursor: pointer;
819-
pointer-events: none;
820-
transition: transform 0.2s ease-in-out;
821-
}
805+
width: fit-content;
806+
aspect-ratio: 1;
822807
}
823808

824809
div.desktop-lang-switcher {
825810
position: relative;
826-
> button.lang-btn {
827-
&:is(:focus, :hover) {
828-
& + ul {
829-
display: block;
830-
opacity: 1;
831-
visibility: visible;
832-
}
833-
}
834-
}
835-
/* enable lang list tabbing on keyboard focus */
836-
&:focus-within .lang-list {
837-
display: block;
838-
opacity: 1;
839-
visibility: visible;
840-
}
841811

842812
> ul.lang-list {
843813
display: none;
844814
opacity: 0;
845815
position: absolute;
846816
list-style: none;
847817
visibility: hidden;
848-
&:is(:hover, :focus) {
849-
display: block;
850-
opacity: 1;
851-
visibility: visible;
852-
}
818+
left: -75px;
819+
z-index: 100;
820+
background-color: var(--card-bg);
821+
border: 1px solid;
822+
border-radius: 10px;
823+
padding: 0px;
824+
min-width: max-content;
825+
853826
li a {
854827
display: block;
855828
padding: 5px 20px 5px 20px;
@@ -859,11 +832,22 @@ div.desktop-lang-switcher {
859832
background: var(--hover-bg);
860833
}
861834
}
835+
836+
> li:first-child > a {
837+
border-start-start-radius: 10px;
838+
border-start-end-radius: 10px;
839+
}
840+
841+
> li:last-child > a {
842+
border-end-start-radius: 10px;
843+
border-end-end-radius: 10px;
844+
}
862845
}
863846

864-
/* rotate arrow */
865-
&:is(:hover,:focus-within) button.lang-btn::after {
866-
transform: rotate(180deg);
847+
> ul.lang-list.open {
848+
display: block;
849+
opacity: 1;
850+
visibility: visible;
867851
}
868852
}
869853

@@ -953,9 +937,6 @@ div.desktop-lang-switcher {
953937
text-decoration: none;
954938
}
955939

956-
#language-picker-menu {
957-
display: none;
958-
}
959940

960941
/* TOC side menu */
961942

@@ -1320,26 +1301,6 @@ h2 a {
13201301
display: flex;
13211302
gap: 8px;
13221303
}
1323-
1324-
#language-picker-menu #navmenu>li:first-child {
1325-
display: flex;
1326-
}
1327-
1328-
#language-picker-menu #navmenu {
1329-
max-height: 70vh;
1330-
overflow-y: auto;
1331-
scrollbar-width: thin;
1332-
text-align: center;
1333-
}
1334-
1335-
#language-picker-menu {
1336-
display: block;
1337-
position: absolute;
1338-
top: 0;
1339-
right: 0;
1340-
width: 100%;
1341-
z-index: 1000;
1342-
}
13431304
}
13441305

13451306
/* TOC responsive */
@@ -1448,10 +1409,6 @@ h2 a {
14481409
header {
14491410
position: absolute;
14501411
}
1451-
1452-
#mobile-menu {
1453-
display:none;
1454-
}
14551412
}
14561413

14571414
/* For image callouts in writing-middleware.md */
@@ -1589,9 +1546,6 @@ h2 a {
15891546
margin-right: 0;
15901547
padding-right: 10px;
15911548
}
1592-
1593-
1594-
15951549
#blog-doc .blog-details + p > img {
15961550
margin-bottom: 15px;
15971551
}
@@ -1661,18 +1615,14 @@ blockquote {
16611615
}
16621616

16631617
@media all and (max-width: 1110px) {
1664-
.desktop-lang-switcher {
1665-
display: none;
1618+
.algolia-autocomplete {
1619+
display: none !important;
16661620
}
16671621

16681622
#mobile-menu {
16691623
display: block;
16701624
}
16711625

1672-
.algolia-autocomplete {
1673-
display: none !important;
1674-
}
1675-
16761626
#navbar {
16771627
padding: 0;
16781628
top: 1px;

js/app.js

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,5 @@
1-
const languageElement = document.getElementById('languageData');
2-
const languagesData = languageElement ? JSON.parse(languageElement.dataset.languages) : [];
3-
const langDisplay = document.getElementById('current-lang');
41
const i18nMsgBox = document.getElementById("i18n-notice-box");
52

6-
// display current language in language picker component
7-
if (langDisplay) {
8-
const currentLanguage = window.location.pathname.split('/')[1];
9-
const matchedLang = languagesData.find(lang => lang.code === currentLanguage);
10-
langDisplay.textContent = matchedLang ? matchedLang.name : 'English';
11-
}
12-
133
// add/remove class 'scroll' on scroll by 5px
144
const scrollTarget = document.querySelector('.logo-container');
155
const scrollObserver = new IntersectionObserver(

js/menu.js

Lines changed: 30 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,35 @@ for (const el of itemsMenu) {
6565
}
6666

6767
// Mobile Menu and Language Picker
68-
68+
const langBtn = document.getElementById("langBtn");
69+
const langList = document.getElementById("langList");
6970
const linkItemsMenu = document.querySelectorAll(".submenu > a");
70-
const languageItems = document.querySelectorAll("#language-picker-menu > #navbar > #navmenu > .submenu > a");
71-
const languagePickerMenu = document.querySelector("#language-picker-menu > #navbar > #navmenu");
7271
const menu = document.querySelector("#navmenu");
7372
const overlay = document.querySelector("#overlay");
7473
const navButton = document.querySelector("#nav-button");
75-
const languagePickerButton = document.querySelector("#language-picker-button");
7674

75+
function closeLangList() {
76+
langList.classList.remove("open");
77+
langBtn.setAttribute("aria-expanded", false);
78+
}
79+
80+
function toggleLangList() {
81+
const isOpen = langList.classList.toggle("open");
82+
langBtn.setAttribute("aria-expanded", isOpen);
83+
}
84+
85+
// toggle on button click
86+
langBtn.addEventListener("click", (e) => {
87+
e.stopPropagation();
88+
toggleLangList();
89+
});
90+
91+
// close on outside click except for lang btn
92+
document.body.addEventListener("click", (e) => {
93+
if (!langList.contains(e.target)) {
94+
closeLangList();
95+
}
96+
});
7797

7898
for (const el of linkItemsMenu) {
7999
el.addEventListener("click", (e) => {
@@ -85,56 +105,23 @@ for (const el of linkItemsMenu) {
85105
e.preventDefault();
86106
}
87107
});
88-
89-
}
90-
91-
for (const el of languageItems) {
92-
el.addEventListener("click", (e) => {
93-
const href = el.getAttribute("href");
94-
95-
if (href && href !== "#") {
96-
languagePickerMenu?.classList.remove("opens");
97-
overlay?.classList.remove("blurs");
98-
document.body.classList.remove("no-scroll");
99-
100-
window.location.href = href;
101-
}
102-
});
103108
}
104109

105110
navButton?.addEventListener("click", () => {
106-
const isLanguageMenuOpen = languagePickerMenu?.classList.contains("opens");
107-
if (isLanguageMenuOpen) {
108-
languagePickerMenu?.classList.remove("opens");
109-
menu?.classList.toggle("opens");
110-
} else {
111-
menu?.classList.toggle("opens");
112-
overlay?.classList.toggle("blurs");
113-
document.body.classList.toggle("no-scroll");
114-
}
115-
});
116-
117-
languagePickerButton?.addEventListener("click", () => {
118-
const isMenuOpen = menu?.classList.contains("opens");
119-
if (isMenuOpen) {
120-
menu?.classList.remove("opens");
121-
languagePickerMenu?.classList.toggle("opens");
122-
} else {
123-
languagePickerMenu?.classList.toggle("opens");
124-
overlay?.classList.toggle("blurs");
125-
document.body.classList.toggle("no-scroll");
126-
}
111+
menu?.classList.toggle("opens");
112+
overlay?.classList.toggle("blurs");
113+
document.body.classList.toggle("no-scroll");
127114
});
128115

129116
overlay?.addEventListener("click", () => {
130117
if (menu?.classList.contains("opens")) {
131118
menu.classList.remove("opens");
132119
}
133-
if (languagePickerMenu?.classList.contains("opens")) {
134-
languagePickerMenu.classList.remove("opens");
135-
}
136120
overlay.classList.remove("blurs");
137121
document.body.classList.remove("no-scroll");
122+
// TODO : write helper function
123+
const isOpen = langList.classList.toggle("open");
124+
langBtn.setAttribute("aria-expanded", isOpen);
138125
});
139126

140127
document

0 commit comments

Comments
 (0)