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 (