💄(frontend) adapt language picker design
Adapt the language picker design to to fit with the new design of the app.
This commit is contained in:
@@ -381,6 +381,7 @@ const config = {
|
||||
'color-active': 'var(--c--theme--colors--primary-100)',
|
||||
},
|
||||
'color-hover': 'var(--c--theme--colors--primary-text)',
|
||||
color: 'var(--c--theme--colors--primary-800)',
|
||||
},
|
||||
secondary: {
|
||||
background: {
|
||||
|
||||
@@ -8,17 +8,20 @@ import {
|
||||
import { Button, Popover } from 'react-aria-components';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { BoxProps } from './Box';
|
||||
|
||||
const StyledPopover = styled(Popover)`
|
||||
background-color: white;
|
||||
border-radius: 4px;
|
||||
box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1);
|
||||
|
||||
border: 1px solid #dddddd;
|
||||
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
`;
|
||||
|
||||
const StyledButton = styled(Button)`
|
||||
interface StyledButtonProps {
|
||||
$css?: BoxProps['$css'];
|
||||
}
|
||||
const StyledButton = styled(Button)<StyledButtonProps>`
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
background: none;
|
||||
@@ -29,10 +32,12 @@ const StyledButton = styled(Button)`
|
||||
font-size: 0.938rem;
|
||||
padding: 0;
|
||||
text-wrap: nowrap;
|
||||
${({ $css }) => $css};
|
||||
`;
|
||||
|
||||
export interface DropButtonProps {
|
||||
button: ReactNode;
|
||||
buttonCss?: BoxProps['$css'];
|
||||
isOpen?: boolean;
|
||||
onOpenChange?: (isOpen: boolean) => void;
|
||||
label?: string;
|
||||
@@ -40,6 +45,7 @@ export interface DropButtonProps {
|
||||
|
||||
export const DropButton = ({
|
||||
button,
|
||||
buttonCss,
|
||||
isOpen = false,
|
||||
onOpenChange,
|
||||
children,
|
||||
@@ -64,6 +70,7 @@ export const DropButton = ({
|
||||
ref={triggerRef}
|
||||
onPress={() => onOpenChangeHandler(true)}
|
||||
aria-label={label}
|
||||
$css={buttonCss}
|
||||
>
|
||||
{button}
|
||||
</StyledButton>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PropsWithChildren, useState } from 'react';
|
||||
import { PropsWithChildren, useRef, useState } from 'react';
|
||||
import { css } from 'styled-components';
|
||||
|
||||
import { Box, BoxButton, BoxProps, DropButton, Icon, Text } from '@/components';
|
||||
@@ -20,6 +20,7 @@ export type DropdownMenuProps = {
|
||||
showArrow?: boolean;
|
||||
label?: string;
|
||||
arrowCss?: BoxProps['$css'];
|
||||
buttonCss?: BoxProps['$css'];
|
||||
disabled?: boolean;
|
||||
topMessage?: string;
|
||||
};
|
||||
@@ -30,6 +31,7 @@ export const DropdownMenu = ({
|
||||
disabled = false,
|
||||
showArrow = false,
|
||||
arrowCss,
|
||||
buttonCss,
|
||||
label,
|
||||
topMessage,
|
||||
}: PropsWithChildren<DropdownMenuProps>) => {
|
||||
@@ -37,6 +39,7 @@ export const DropdownMenu = ({
|
||||
const spacings = theme.spacingsTokens();
|
||||
const colors = theme.colorsTokens();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const blockButtonRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const onOpenChange = (isOpen: boolean) => {
|
||||
setIsOpen(isOpen);
|
||||
@@ -51,10 +54,17 @@ export const DropdownMenu = ({
|
||||
isOpen={isOpen}
|
||||
onOpenChange={onOpenChange}
|
||||
label={label}
|
||||
buttonCss={buttonCss}
|
||||
button={
|
||||
showArrow ? (
|
||||
<Box $direction="row" $align="center">
|
||||
<div>{children}</div>
|
||||
<Box
|
||||
ref={blockButtonRef}
|
||||
$direction="row"
|
||||
$align="center"
|
||||
$position="relative"
|
||||
aria-controls="menu"
|
||||
>
|
||||
<Box>{children}</Box>
|
||||
<Icon
|
||||
$variation="600"
|
||||
$css={
|
||||
@@ -67,11 +77,17 @@ export const DropdownMenu = ({
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
children
|
||||
<Box ref={blockButtonRef} aria-controls="menu">
|
||||
{children}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
>
|
||||
<Box $maxWidth="320px">
|
||||
<Box
|
||||
$maxWidth="320px"
|
||||
$minWidth={`${blockButtonRef.current?.clientWidth}px`}
|
||||
role="menu"
|
||||
>
|
||||
{topMessage && (
|
||||
<Text
|
||||
$variation="700"
|
||||
@@ -90,6 +106,7 @@ export const DropdownMenu = ({
|
||||
const isDisabled = option.disabled !== undefined && option.disabled;
|
||||
return (
|
||||
<BoxButton
|
||||
role="menuitem"
|
||||
aria-label={option.label}
|
||||
data-testid={option.testId}
|
||||
$direction="row"
|
||||
|
||||
@@ -25,7 +25,9 @@ export interface TextProps extends BoxProps {
|
||||
$size?: TextSizes | (string & {});
|
||||
$theme?:
|
||||
| 'primary'
|
||||
| 'primary-text'
|
||||
| 'secondary'
|
||||
| 'secondary-text'
|
||||
| 'info'
|
||||
| 'success'
|
||||
| 'warning'
|
||||
|
||||
@@ -406,6 +406,10 @@ input:-webkit-autofill:focus {
|
||||
);
|
||||
}
|
||||
|
||||
.c__button--primary-text {
|
||||
color: var(--c--components--button--primary-text--color);
|
||||
}
|
||||
|
||||
.c__button--primary-text:hover,
|
||||
.c__button--primary-text:focus-visible {
|
||||
background-color: var(
|
||||
|
||||
@@ -505,6 +505,9 @@
|
||||
--c--components--button--primary-text--color-hover: var(
|
||||
--c--theme--colors--primary-text
|
||||
);
|
||||
--c--components--button--primary-text--color: var(
|
||||
--c--theme--colors--primary-800
|
||||
);
|
||||
--c--components--button--secondary--background--color-hover: #f6f6f6;
|
||||
--c--components--button--secondary--background--color-active: #ededed;
|
||||
--c--components--button--secondary--border--color: var(
|
||||
|
||||
@@ -505,6 +505,7 @@ export const tokens = {
|
||||
'color-active': 'var(--c--theme--colors--primary-100)',
|
||||
},
|
||||
'color-hover': 'var(--c--theme--colors--primary-text)',
|
||||
color: 'var(--c--theme--colors--primary-800)',
|
||||
},
|
||||
secondary: {
|
||||
background: { 'color-hover': '#F6F6F6', 'color-active': '#EDEDED' },
|
||||
|
||||
@@ -1,84 +1,61 @@
|
||||
import { Select } from '@openfun/cunningham-react';
|
||||
import { Settings } from 'luxon';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styled from 'styled-components';
|
||||
import { css } from 'styled-components';
|
||||
|
||||
import { Box, Text } from '@/components/';
|
||||
import { DropdownMenu, Text } from '@/components/';
|
||||
import { LANGUAGES_ALLOWED } from '@/i18n/conf';
|
||||
|
||||
const SelectStyled = styled(Select)<{ $isSmall?: boolean }>`
|
||||
flex-shrink: 0;
|
||||
width: auto;
|
||||
|
||||
.c__select__wrapper {
|
||||
min-height: 2rem;
|
||||
height: auto;
|
||||
border-color: transparent;
|
||||
padding: 0 0.15rem 0 0.45rem;
|
||||
border-radius: 1px;
|
||||
|
||||
.labelled-box .labelled-box__children {
|
||||
padding-right: 2rem;
|
||||
|
||||
.c_select__render .typo-text {
|
||||
${({ $isSmall }) => $isSmall && `display: none;`}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const LanguagePicker = () => {
|
||||
const { t, i18n } = useTranslation();
|
||||
const { preload: languages } = i18n.options;
|
||||
Settings.defaultLocale = i18n.language;
|
||||
const language = i18n.language;
|
||||
Settings.defaultLocale = language;
|
||||
|
||||
const optionsPicker = useMemo(() => {
|
||||
return (languages || []).map((lang) => ({
|
||||
value: lang,
|
||||
label: lang,
|
||||
render: () => (
|
||||
<Box
|
||||
className="c_select__render"
|
||||
$direction="row"
|
||||
$gap="0.7rem"
|
||||
$align="center"
|
||||
>
|
||||
<Text
|
||||
$isMaterialIcon
|
||||
$size="1rem"
|
||||
$theme="primary"
|
||||
$weight="bold"
|
||||
$variation="800"
|
||||
>
|
||||
translate
|
||||
</Text>
|
||||
<Text $theme="primary" $weight="500" $variation="800">
|
||||
{LANGUAGES_ALLOWED[lang]}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
}));
|
||||
}, [languages]);
|
||||
|
||||
return (
|
||||
<SelectStyled
|
||||
label={t('Language')}
|
||||
showLabelWhenSelected={false}
|
||||
clearable={false}
|
||||
hideLabel
|
||||
defaultValue={i18n.language}
|
||||
className="c_select__no_bg"
|
||||
options={optionsPicker}
|
||||
onChange={(e) => {
|
||||
i18n.changeLanguage(e.target.value as string).catch((err) => {
|
||||
label: LANGUAGES_ALLOWED[lang],
|
||||
isSelected: language === lang,
|
||||
callback: () => {
|
||||
i18n.changeLanguage(lang).catch((err) => {
|
||||
console.error('Error changing language', err);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
},
|
||||
}));
|
||||
}, [i18n, language, languages]);
|
||||
|
||||
return (
|
||||
<DropdownMenu
|
||||
options={optionsPicker}
|
||||
showArrow
|
||||
buttonCss={css`
|
||||
&:hover {
|
||||
background-color: var(
|
||||
--c--components--button--primary-text--background--color-hover
|
||||
);
|
||||
}
|
||||
border-radius: 4px;
|
||||
padding: 0.5rem 0.6rem;
|
||||
& > div {
|
||||
gap: 0.2rem;
|
||||
display: flex;
|
||||
}
|
||||
& .material-icons {
|
||||
color: var(--c--components--button--primary-text--color) !important;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<Text
|
||||
$theme="primary"
|
||||
aria-label={t('Language')}
|
||||
$direction="row"
|
||||
$gap="0.5rem"
|
||||
>
|
||||
<Text $isMaterialIcon $color="inherit" $size="xl">
|
||||
translate
|
||||
</Text>
|
||||
{LANGUAGES_ALLOWED[language]}
|
||||
</Text>
|
||||
</DropdownMenu>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user