diff --git a/src/components/app/NavigationBar.tsx b/src/components/app/NavigationBar.tsx index 0b16d2ed0..8659c231c 100644 --- a/src/components/app/NavigationBar.tsx +++ b/src/components/app/NavigationBar.tsx @@ -33,6 +33,7 @@ import { userHasBudibaseTools } from '../../api/apiGetTools'; import { browserNotificationsSettings } from '../../utils/notificationHelpers'; import useIsFirstVisit from '../../utils/useIsFirstVisit'; import { useResponsive } from '../../hooks/useResponsive'; +import { MENUPLACEMENT_RIGHT } from '../select/SelectDropdown'; export interface NavigationBarProps { onLogout: any; @@ -351,7 +352,7 @@ export const NavigationBar = ({ vertical iconSize={32} label={translate('navigation.language')} - menuPlacement="right" + menuPlacement={MENUPLACEMENT_RIGHT} selectRef={(el) => (ref_select.current = el)} isInsideMenu={true} /> diff --git a/src/components/error/Error.tsx b/src/components/error/Error.tsx index 022963b2a..4b1eedb36 100644 --- a/src/components/error/Error.tsx +++ b/src/components/error/Error.tsx @@ -15,6 +15,7 @@ import { useAppConfig } from '../../hooks/useAppConfig'; import { LocaleProvider, AppConfigProvider } from '../../globalState'; import { AppConfigInterface } from '../../globalState/interfaces'; import { useResponsive } from '../../hooks/useResponsive'; +import { MENUPLACEMENT_BOTTOM_LEFT } from '../select/SelectDropdown'; const getStatusCode = () => { const errorRoot = document.getElementById('errorRoot'); @@ -72,7 +73,7 @@ export const ErrorContent = () => { return (
- +
diff --git a/src/components/header/Header.tsx b/src/components/header/Header.tsx index 14fff83b3..b470d4422 100644 --- a/src/components/header/Header.tsx +++ b/src/components/header/Header.tsx @@ -5,11 +5,10 @@ import { useContext } from 'react'; import { TenantContext } from '../../globalState'; import './header.styles'; import { useTranslation } from 'react-i18next'; -import { LocaleSwitch } from '../localeSwitch/LocaleSwitch'; import { useAtomValue } from 'jotai'; import { agencyLogoAtom } from '../../store/agencyLogoAtom'; -export const Header = ({ showLocaleSwitch = false }) => { +export const Header = () => { const { t: translate } = useTranslation(); const { tenant } = useContext(TenantContext); const agencyLogo = useAtomValue(agencyLogoAtom); @@ -29,7 +28,6 @@ export const Header = ({ showLocaleSwitch = false }) => { text={tenant?.content?.claim || translate('app.claim')} /> )} - {showLocaleSwitch && }
); diff --git a/src/components/localeSwitch/LocaleSwitch.tsx b/src/components/localeSwitch/LocaleSwitch.tsx index c461f8189..49bd451b5 100644 --- a/src/components/localeSwitch/LocaleSwitch.tsx +++ b/src/components/localeSwitch/LocaleSwitch.tsx @@ -6,7 +6,12 @@ import { useTranslation } from 'react-i18next'; import { useContext, useEffect, useState } from 'react'; import { UserDataContext, LocaleContext } from '../../globalState'; import { apiPatchUserData } from '../../api/apiPatchUserData'; -import { SelectDropdown, SelectDropdownItem } from '../select/SelectDropdown'; +import { + MENUPLACEMENT, + MENUPLACEMENT_BOTTOM, + SelectDropdown, + SelectDropdownItem +} from '../select/SelectDropdown'; import { setValueInCookie } from '../sessionCookie/accessSessionCookie'; export interface LocaleSwitchProp { @@ -16,7 +21,7 @@ export interface LocaleSwitchProp { className?: string; iconSize?: number; label?: string; - menuPlacement?: 'top' | 'bottom' | 'right'; + menuPlacement?: MENUPLACEMENT; selectRef?: any; isInsideMenu?: boolean; } @@ -27,7 +32,7 @@ export const LocaleSwitch: React.FC = ({ showIcon = true, vertical, iconSize = 20, - menuPlacement = 'bottom', + menuPlacement = MENUPLACEMENT_BOTTOM, label, selectRef, isInsideMenu = false diff --git a/src/components/select/SelectDropdown.tsx b/src/components/select/SelectDropdown.tsx index dda34c9bd..671aa3981 100644 --- a/src/components/select/SelectDropdown.tsx +++ b/src/components/select/SelectDropdown.tsx @@ -1,6 +1,6 @@ import clsx from 'clsx'; import * as React from 'react'; -import Select, { defaultStyles } from 'react-select'; +import Select, { defaultStyles, MenuPlacement } from 'react-select'; import { components } from 'react-select'; import { CloseCircle } from '../../resources/img/icons'; import { ReactComponent as ArrowDownIcon } from '../../resources/img/icons/arrow-down-light.svg'; @@ -10,7 +10,7 @@ import './select.react.styles'; import './select.styles'; import { useResponsive } from '../../hooks/useResponsive'; import { useTranslation } from 'react-i18next'; -import { ReactNode } from 'react'; +import { ReactNode, useMemo } from 'react'; export interface SelectOption { value: string; @@ -26,6 +26,17 @@ export interface SelectOptionsMulti { option?: SelectOption; } +export const MENUPLACEMENT_TOP = 'top'; +export const MENUPLACEMENT_BOTTOM = 'bottom'; +export const MENUPLACEMENT_RIGHT = 'right'; +export const MENUPLACEMENT_BOTTOM_LEFT = 'bottomLeft'; + +export type MENUPLACEMENT = + | typeof MENUPLACEMENT_TOP + | typeof MENUPLACEMENT_BOTTOM + | typeof MENUPLACEMENT_RIGHT + | typeof MENUPLACEMENT_BOTTOM_LEFT; + export interface SelectDropdownItem { className?: string; id: string; @@ -37,7 +48,7 @@ export interface SelectDropdownItem { isSearchable?: boolean; isMulti?: boolean; isClearable?: boolean; - menuPlacement: 'top' | 'bottom' | 'right'; + menuPlacement: MENUPLACEMENT; menuPosition?: 'absolute' | 'fixed'; defaultValue?: SelectOption | SelectOption[]; hasError?: boolean; @@ -51,7 +62,7 @@ export interface SelectDropdownItem { const colourStyles = ( fromL, - menuPlacement, + menuPlacement: MENUPLACEMENT, { control, singleValue, @@ -141,9 +152,9 @@ const colourStyles = ( }), menu: (styles, state) => ({ ...styles, - 'marginTop': state.menuPlacement === 'top' ? '0' : '16px', + 'marginTop': state.menuPlacement === MENUPLACEMENT_TOP ? '0' : '16px', 'fontWeight': 'normal', - ...(menuPlacement === 'right' + ...(menuPlacement === MENUPLACEMENT_RIGHT ? { bottom: '0', left: '100%', @@ -153,7 +164,12 @@ const colourStyles = ( width: 'auto' } : { - marginBottom: state.menuPlacement === 'top' ? '16px' : '0' + marginBottom: + state.menuPlacement === MENUPLACEMENT_TOP + ? '16px' + : '0', + right: + menuPlacement === MENUPLACEMENT_BOTTOM_LEFT ? 0 : 'auto' }), 'boxShadow': undefined, '&:after, &:before': { @@ -161,10 +177,8 @@ const colourStyles = ( position: 'absolute', marginTop: '-1px', marginLeft: '-12px', - bottom: state.menuPlacement === 'top' ? '-9px' : 'auto', - top: state.menuPlacement === 'top' ? 'auto' : '-8px', zIndex: 2, - ...(menuPlacement === 'right' + ...(menuPlacement === MENUPLACEMENT_RIGHT ? { left: '0', bottom: '5%', @@ -177,21 +191,33 @@ const colourStyles = ( width: '12px' } : { + left: + menuPlacement === MENUPLACEMENT_BOTTOM_LEFT + ? '75%' + : '50%', + bottom: + state.menuPlacement === MENUPLACEMENT_TOP + ? '-9px' + : 'auto', + top: + state.menuPlacement === MENUPLACEMENT_TOP + ? 'auto' + : '-8px', borderLeft: '10px solid transparent', borderRight: '10px solid transparent', borderTop: - state.menuPlacement === 'top' + state.menuPlacement === MENUPLACEMENT_TOP ? '10px solid #fff' : 'none', borderBottom: - state.menuPlacement === 'top' + state.menuPlacement === MENUPLACEMENT_TOP ? 'none' : '10px solid #fff' }) }, '&:before': { zIndex: 1, - ...(menuPlacement === 'right' + ...(menuPlacement === MENUPLACEMENT_RIGHT ? { left: '0', bottom: '5%', @@ -202,16 +228,20 @@ const colourStyles = ( borderRight: '10px solid rgba(0,0,0,0.1)' } : { - left: '50%', bottom: - state.menuPlacement === 'top' ? '-14px' : 'auto', - top: state.menuPlacement === 'top' ? 'auto' : '-10px', + state.menuPlacement === MENUPLACEMENT_TOP + ? '-14px' + : 'auto', + top: + state.menuPlacement === MENUPLACEMENT_TOP + ? 'auto' + : '-10px', borderTop: - state.menuPlacement === 'top' + state.menuPlacement === MENUPLACEMENT_TOP ? '10px solid rgba(0,0,0,0.1)' : 'none', borderBottom: - state.menuPlacement === 'top' + state.menuPlacement === MENUPLACEMENT_TOP ? 'none' : '10px solid rgba(0,0,0,0.1)' }) @@ -351,6 +381,16 @@ export const SelectDropdown = (props: SelectDropdownItem) => { ); }; + const menuPlacement = useMemo(() => { + switch (props.menuPlacement) { + case MENUPLACEMENT_BOTTOM_LEFT: + case MENUPLACEMENT_RIGHT: + return MENUPLACEMENT_BOTTOM; + default: + return props.menuPlacement; + } + }, [props.menuPlacement]); + return (