🎨 style: adjust the popover position based on the user screen
This commit is contained in:
parent
b7fe301c81
commit
c950bd8d93
@ -5,6 +5,8 @@ import React, {
|
|||||||
useMemo,
|
useMemo,
|
||||||
useCallback,
|
useCallback,
|
||||||
MouseEvent,
|
MouseEvent,
|
||||||
|
useRef,
|
||||||
|
useEffect,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { useMultipleSelection, useCombobox } from 'downshift';
|
import { useMultipleSelection, useCombobox } from 'downshift';
|
||||||
import { SelectTheme, selectTheme } from './Select.theme';
|
import { SelectTheme, selectTheme } from './Select.theme';
|
||||||
@ -62,6 +64,34 @@ export const MultiSelect: React.FC<MultiSelectProps> = ({
|
|||||||
const [inputValue, setInputValue] = useState('');
|
const [inputValue, setInputValue] = useState('');
|
||||||
const [selectedItem, setSelectedItem] = useState<DropdownItem | null>(null);
|
const [selectedItem, setSelectedItem] = useState<DropdownItem | null>(null);
|
||||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||||
|
const [dropdownPosition, setDropdownPosition] = useState<'top' | 'bottom'>(
|
||||||
|
'bottom',
|
||||||
|
);
|
||||||
|
const popoverRef = useRef(null); // Ref for the popover
|
||||||
|
const inputWrapperRef = useRef(null); // Ref for the input wrapper
|
||||||
|
|
||||||
|
// Calculate and update popover position
|
||||||
|
useEffect(() => {
|
||||||
|
if (dropdownOpen && popoverRef.current && inputWrapperRef.current) {
|
||||||
|
const popover = popoverRef.current;
|
||||||
|
// @ts-expect-error – we know it's not null
|
||||||
|
const input = inputWrapperRef.current.getBoundingClientRect();
|
||||||
|
const spaceBelow = window.innerHeight - input.bottom;
|
||||||
|
const spaceAbove = input.top;
|
||||||
|
// @ts-expect-error – we know it's not null
|
||||||
|
const popoverHeight = popover.offsetHeight;
|
||||||
|
|
||||||
|
// Determine if there's enough space below
|
||||||
|
if (spaceBelow >= popoverHeight) {
|
||||||
|
setDropdownPosition('bottom');
|
||||||
|
} else if (spaceAbove >= popoverHeight) {
|
||||||
|
setDropdownPosition('top');
|
||||||
|
} else {
|
||||||
|
// Default to bottom if neither has enough space, but you could also set logic to choose the side with more space
|
||||||
|
setDropdownPosition('bottom');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [dropdownOpen]); // Re-calculate whenever the dropdown is opened
|
||||||
|
|
||||||
const handleSelectedItemChange = (selectedItem: DropdownItem | null) => {
|
const handleSelectedItemChange = (selectedItem: DropdownItem | null) => {
|
||||||
setSelectedItem(selectedItem);
|
setSelectedItem(selectedItem);
|
||||||
@ -211,6 +241,7 @@ export const MultiSelect: React.FC<MultiSelectProps> = ({
|
|||||||
<div className={theme.container()}>
|
<div className={theme.container()}>
|
||||||
{renderLabels}
|
{renderLabels}
|
||||||
<div
|
<div
|
||||||
|
ref={inputWrapperRef}
|
||||||
className={theme.inputWrapper({
|
className={theme.inputWrapper({
|
||||||
hasValue: multiple && selectedItems.length > 0,
|
hasValue: multiple && selectedItems.length > 0,
|
||||||
})}
|
})}
|
||||||
@ -250,7 +281,15 @@ export const MultiSelect: React.FC<MultiSelectProps> = ({
|
|||||||
{renderRightIcon}
|
{renderRightIcon}
|
||||||
</div>
|
</div>
|
||||||
{renderHelperText}
|
{renderHelperText}
|
||||||
<ul {...getMenuProps()} className={theme.popover({ isOpen })}>
|
<ul
|
||||||
|
{...getMenuProps()}
|
||||||
|
id="popover"
|
||||||
|
ref={popoverRef}
|
||||||
|
className={cn(theme.popover({ isOpen }), {
|
||||||
|
'top-1/4': dropdownPosition === 'bottom',
|
||||||
|
'bottom-[95%]': dropdownPosition === 'top',
|
||||||
|
})}
|
||||||
|
>
|
||||||
{isOpen && filteredItems.length !== 0 ? (
|
{isOpen && filteredItems.length !== 0 ? (
|
||||||
filteredItems.map((item, index) => (
|
filteredItems.map((item, index) => (
|
||||||
<li
|
<li
|
||||||
|
Loading…
Reference in New Issue
Block a user