0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-03-31 22:51:25 -05:00

refactor(console): support searching in add language selector (#2069)

This commit is contained in:
Xiao Yijun 2022-10-09 22:12:52 +08:00 committed by GitHub
parent 81f9fbc483
commit 8417fc851a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 131 additions and 48 deletions

View file

@ -1,24 +1,58 @@
@use '@/scss/underscore' as _;
.addLanguageButton {
width: 100%;
border-color: var(--color-outline);
color: var(--color-text);
background: unset;
.languageSelector {
.input {
position: relative;
.iconPlus {
color: var(--color-outline);
.addLanguageButton {
height: 38px;
width: 100%;
border-color: var(--color-outline);
color: var(--color-text);
background: unset;
}
.buttonIcon {
color: var(--color-outline);
}
}
.dropDown {
position: absolute;
width: 168px;
margin: _.unit(1) 0;
padding: _.unit(1);
background: var(--color-float);
border: 1px solid var(--color-divider);
border-radius: 8px;
max-height: 288px;
overflow-y: auto;
.dropDownItem {
width: 100%;
border-radius: _.unit(2);
padding: _.unit(2);
list-style: none;
cursor: pointer;
&:hover {
background: var(--color-hover);
}
.languageName {
font: var(--font-label-large);
color: var(--color-text);
}
.languageTag {
font: var(--font-body-medium);
color: var(--color-caption);
}
}
}
.hidden {
display: none;
}
}
.dropDownItem {
.languageName {
font: var(--font-label-large);
color: var(--color-text);
}
.languageTag {
font: var(--font-body-medium);
color: var(--color-caption);
}
}

View file

@ -1,9 +1,12 @@
import { languages, LanguageTag } from '@logto/language-kit';
import { useRef, useState } from 'react';
import { LanguageTag, languages as uiLanguageNameMapping } from '@logto/language-kit';
import classNames from 'classnames';
import { ChangeEvent, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import Button from '@/components/Button';
import Dropdown, { DropdownItem } from '@/components/Dropdown';
import TextInput from '@/components/TextInput';
import Plus from '@/icons/Plus';
import SearchIcon from '@/icons/Search';
import * as style from './AddLanguageSelector.module.scss';
@ -12,17 +15,51 @@ type Props = {
onSelect: (languageTag: LanguageTag) => void;
};
// TODO:(LOG-4147) Support Instant Search In Manage Language Editor Dropdown
const AddLanguageSelector = ({ options, onSelect }: Props) => {
const anchorRef = useRef<HTMLDivElement>(null);
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const selectorRef = useRef<HTMLDivElement>(null);
const searchInputRef = useRef<HTMLInputElement>(null);
const [isDropDownOpen, setIsDropDownOpen] = useState(false);
const [searchInputValue, setSearchInputValue] = useState('');
const filteredOptions = searchInputValue
? options.filter(
(languageTag) =>
languageTag.toLocaleLowerCase().includes(searchInputValue.toLocaleLowerCase()) ||
uiLanguageNameMapping[languageTag]
.toLocaleLowerCase()
.includes(searchInputValue.toLocaleLowerCase())
)
: options;
const clickOutsideHandler = ({ target }: MouseEvent) => {
if (target instanceof HTMLElement && !selectorRef.current?.contains(target)) {
setIsDropDownOpen(false);
setSearchInputValue('');
}
};
useEffect(() => {
if (isDropDownOpen) {
searchInputRef.current?.focus();
document.addEventListener('mousedown', clickOutsideHandler);
} else {
document.removeEventListener('mousedown', clickOutsideHandler);
}
return () => {
if (isDropDownOpen) {
document.removeEventListener('mousedown', clickOutsideHandler);
}
};
}, [isDropDownOpen, searchInputRef]);
return (
<div>
<div ref={anchorRef}>
<div ref={selectorRef} className={style.languageSelector}>
<div className={style.input}>
<Button
className={style.addLanguageButton}
icon={<Plus className={style.iconPlus} />}
className={classNames(style.addLanguageButton, isDropDownOpen && style.hidden)}
icon={<Plus className={style.buttonIcon} />}
title="sign_in_exp.others.manage_language.add_language"
type="outline"
size="medium"
@ -30,29 +67,35 @@ const AddLanguageSelector = ({ options, onSelect }: Props) => {
setIsDropDownOpen(true);
}}
/>
<TextInput
ref={searchInputRef}
icon={<SearchIcon className={style.buttonIcon} />}
className={classNames(!isDropDownOpen && style.hidden)}
placeholder={t('general.type_to_search')}
value={searchInputValue}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
setSearchInputValue(event.target.value);
}}
/>
</div>
<Dropdown
isFullWidth
anchorRef={anchorRef}
isOpen={isDropDownOpen}
onClose={() => {
setIsDropDownOpen(false);
}}
>
{options.map((languageTag) => (
<DropdownItem
key={languageTag}
onClick={() => {
onSelect(languageTag);
}}
>
<div className={style.dropDownItem}>
<div className={style.languageName}>{languages[languageTag]}</div>
{isDropDownOpen && filteredOptions.length > 0 && (
<ul className={style.dropDown}>
{filteredOptions.map((languageTag) => (
<li
key={languageTag}
className={style.dropDownItem}
onClick={() => {
onSelect(languageTag);
setIsDropDownOpen(false);
setSearchInputValue('');
}}
>
<div className={style.languageName}>{uiLanguageNameMapping[languageTag]}</div>
<div className={style.languageTag}>{languageTag}</div>
</div>
</DropdownItem>
))}
</Dropdown>
</li>
))}
</ul>
)}
</div>
);
};

View file

@ -35,6 +35,7 @@ const general = {
unsaved_changes_warning: 'You have made some changes. Are you sure you want to leave this page?',
leave_page: 'Leave Page',
stay_on_page: 'Stay on Page',
type_to_search: 'Type to search',
};
export default general;

View file

@ -36,6 +36,7 @@ const general = {
'Vous avez effectué des changements. Êtes-vous sûr de vouloir quitter cette page ?',
leave_page: 'Quittez la page',
stay_on_page: 'Rester sur la page',
type_to_search: 'Type to search', // UNTRANSLATED
};
export default general;

View file

@ -35,6 +35,7 @@ const general = {
unsaved_changes_warning: '수정된 내용이 있어요. 정말로 현재 페이지를 벗어날까요?',
leave_page: '페이지 나가기',
stay_on_page: '페이지 유지하기',
type_to_search: 'Type to search', // UNTRANSLATED
};
export default general;

View file

@ -35,6 +35,7 @@ const general = {
unsaved_changes_warning: 'Fez algumas alterações. Tem a certeza que deseja sair desta página?',
leave_page: 'Sair da página',
stay_on_page: 'Ficar na página',
type_to_search: 'Type to search', // UNTRANSLATED
};
export default general;

View file

@ -36,6 +36,7 @@ const general = {
'Bazı değişiklikler yaptınız. Bu sayfadan ayrılmak istediğine emin misin?',
leave_page: 'Sayfayı terk et',
stay_on_page: 'Bu sayfada kal',
type_to_search: 'Type to search', // UNTRANSLATED
};
export default general;

View file

@ -35,6 +35,7 @@ const general = {
unsaved_changes_warning: '还有未保存的变更, 确定要离开吗?',
leave_page: '离开此页',
stay_on_page: '留在此页',
type_to_search: 'Type to search', // UNTRANSLATED
};
export default general;