Skip to content

Commit

Permalink
Merge pull request #254 from psychlone77/dropdown-css
Browse files Browse the repository at this point in the history
Improve Language Selector dropdowns
  • Loading branch information
psychlone77 authored Feb 6, 2025
2 parents 9924f4d + 26c6091 commit 595186b
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 63 deletions.
82 changes: 42 additions & 40 deletions src/components/LanguageSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,13 @@ const LanguageSelector = () => {

useEffect(() => {
if (isOpen && focusedIndex >= 0) {
const element = document.querySelector(
`.selector__item:nth-child(${focusedIndex + 1})`
) as HTMLElement;
const elements = Array.from(
document.querySelectorAll(".selector__item")
) as HTMLElement[];
const focusableElements = elements.filter(
(el) => el.getAttribute("tabIndex") !== "-1"
);
const element = focusableElements[focusedIndex];
element?.focus();
}
}, [isOpen, focusedIndex]);
Expand Down Expand Up @@ -186,43 +190,41 @@ const LanguageSelector = () => {
</div>
<span className="selector__arrow" />
</button>
{isOpen && (
<ul
className="selector__dropdown"
role="listbox"
onKeyDown={handleKeyDown}
tabIndex={-1}
>
{fetchedLanguages.map((lang, index) =>
lang.subLanguages.length > 0 ? (
<SubLanguageSelector
key={lang.name}
opened={openedLanguages.includes(lang)}
parentLanguage={lang}
onDropdownToggle={handleToggleSubLanguage}
handleParentSelect={handleSelect}
afterSelect={afterSelect}
/>
) : (
<li
key={lang.name}
role="option"
tabIndex={-1}
onClick={() => handleSelect(lang)}
className={`selector__item ${
language.name === lang.name ? "selected" : ""
} ${focusedIndex === index ? "focused" : ""}`}
aria-selected={language.name === lang.name}
>
<label>
<img src={lang.icon} alt="" />
<span>{lang.name}</span>
</label>
</li>
)
)}
</ul>
)}
<ul
className={`selector__dropdown ${isOpen ? "" : " hidden"}`}
role="listbox"
onKeyDown={handleKeyDown}
tabIndex={0}
>
{fetchedLanguages.map((lang, index) =>
lang.subLanguages.length > 0 ? (
<SubLanguageSelector
key={lang.name}
opened={openedLanguages.includes(lang)}
parentLanguage={lang}
onDropdownToggle={handleToggleSubLanguage}
handleParentSelect={handleSelect}
afterSelect={afterSelect}
/>
) : (
<li
key={lang.name}
role="option"
tabIndex={0}
onClick={() => handleSelect(lang)}
className={`selector__item ${
language.name === lang.name ? "selected" : ""
} ${focusedIndex === index ? "focused" : ""}`}
aria-selected={language.name === lang.name}
>
<label>
<img src={lang.icon} alt="" />
<span>{lang.name}</span>
</label>
</li>
)
)}
</ul>
</div>
);
};
Expand Down
38 changes: 19 additions & 19 deletions src/components/SubLanguageSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const SubLanguageSelector = ({
<>
<li
role="option"
tabIndex={-1}
tabIndex={0}
className={`selector__item ${
subLanguage === defaultSlugifiedSubLanguageName &&
language.name === parentLanguage.name
Expand Down Expand Up @@ -78,24 +78,24 @@ const SubLanguageSelector = ({
</label>
</li>

{opened &&
parentLanguage.subLanguages.map((sl) => (
<li
key={sl.name}
role="option"
tabIndex={-1}
className={`selector__item sublanguage__item ${
slugify(subLanguage) === slugify(sl.name) ? "selected" : ""
}`}
aria-selected={slugify(subLanguage) === slugify(sl.name)}
onClick={handleSubLanguageSelect(sl)}
>
<label>
<img src={sl.icon} alt={sl.name} />
<span>{sl.name}</span>
</label>
</li>
))}
{parentLanguage.subLanguages.map((sl) => (
<li
key={sl.name}
role="option"
tabIndex={opened ? 0 : -1}
aria-disabled={!opened}
className={`selector__item sublanguage__item ${opened ? "" : "hidden"} ${
slugify(subLanguage) === slugify(sl.name) ? "selected" : ""
}`}
aria-selected={slugify(subLanguage) === slugify(sl.name)}
onClick={handleSubLanguageSelect(sl)}
>
<label>
<img src={sl.icon} alt={sl.name} />
<span>{sl.name}</span>
</label>
</li>
))}
</>
);
};
Expand Down
44 changes: 40 additions & 4 deletions src/styles/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ abbr {
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-top: 7px solid var(--clr-text-primary); /* [1] */
transition: transform 100ms ease;
transition: transform 300ms ease;
}

.selector--open .selector__arrow {
Expand All @@ -414,7 +414,7 @@ abbr {

position: absolute;
width: 100%;
max-height: 20rem;
height: 50vh;
overflow-y: auto;

background-color: var(--clr-bg-secondary);
Expand All @@ -427,10 +427,28 @@ abbr {

box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
z-index: 1;
transition: all 300ms ease;
}

.selector__dropdown:focus-within {
border-color: var(--clr-accent);
.selector__dropdown.hidden {
border: none;
padding: 0;
height: 0;
opacity: 0;
overflow: hidden;
}

.selector__dropdown::-webkit-scrollbar {
width: 8px;
}

.selector__dropdown::-webkit-scrollbar-thumb {
background: var(--scrollbar-thumb);
border-radius: 4px;
}

.selector__dropdown::-webkit-scrollbar-thumb:hover {
background: var(--scrollbar-thumb);
}

.selector__item {
Expand All @@ -440,10 +458,19 @@ abbr {
gap: 1rem;
align-items: center;
border-radius: var(--br-md);
transition: all 300ms ease;
}

.sublanguage__item {
height: 3rem;
margin-left: 1.5rem;
transition: all 300ms ease;
}

.sublanguage__item.hidden {
height: 0;
opacity: 0;
overflow: hidden;
}

.sublanguage__button {
Expand All @@ -463,6 +490,11 @@ abbr {
transform: rotate(-90deg);
transition: transform 100ms ease;
cursor: pointer;
transition: all 200ms ease;
}

.sublanguage__arrow:hover {
border-top-color: var(--clr-accent);
}

[aria-expanded="true"] .sublanguage__arrow {
Expand All @@ -473,6 +505,10 @@ abbr {
border-top-color: var(--clr-text-tertiary);
}

.selector__item.selected .sublanguage__arrow:hover {
border-top-color: var(--clr-text-primary);
}

.selector__item label {
width: 100%;
padding: 0.25em 0.75em;
Expand Down

0 comments on commit 595186b

Please sign in to comment.