mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-11-21 19:07:46 +01:00
[Rework RelativeModal] calculate available window height (#6000)
* overflow-auto * removed the restrict menu height from #5601. * remove top from the equations due to scroll height * calculate available height
This commit is contained in:
parent
e3eae53cb9
commit
dee471e9e9
@ -7,54 +7,65 @@ import Button from './Button';
|
|||||||
import CameraIcon from '../icons/Camera';
|
import CameraIcon from '../icons/Camera';
|
||||||
|
|
||||||
export default function MultiSelect({ className, title, options, selection, onToggle, onShowAll, onSelectSingle }) {
|
export default function MultiSelect({ className, title, options, selection, onToggle, onShowAll, onSelectSingle }) {
|
||||||
|
|
||||||
const popupRef = useRef(null);
|
const popupRef = useRef(null);
|
||||||
|
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
showMenu: false,
|
showMenu: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const isOptionSelected = (item) => { return selection == "all" || selection.split(',').indexOf(item) > -1; }
|
const isOptionSelected = (item) => {
|
||||||
|
return selection == 'all' || selection.split(',').indexOf(item) > -1;
|
||||||
|
};
|
||||||
|
|
||||||
const menuHeight = Math.round(window.innerHeight * 0.55);
|
const menuHeight = Math.round(window.innerHeight * 0.55);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${className} p-2`} ref={popupRef}>
|
<div className={`${className} p-2`} ref={popupRef}>
|
||||||
<div
|
<div className="flex justify-between min-w-[120px]" onClick={() => setState({ showMenu: true })}>
|
||||||
className="flex justify-between min-w-[120px]"
|
|
||||||
onClick={() => setState({ showMenu: true })}
|
|
||||||
>
|
|
||||||
<label>{title}</label>
|
<label>{title}</label>
|
||||||
<ArrowDropdown className="w-6" />
|
<ArrowDropdown className="w-6" />
|
||||||
</div>
|
</div>
|
||||||
{state.showMenu ? (
|
{state.showMenu ? (
|
||||||
<Menu className={`max-h-[${menuHeight}px] overflow-scroll`} relativeTo={popupRef} onDismiss={() => setState({ showMenu: false })}>
|
<Menu
|
||||||
|
className={`max-h-[${menuHeight}px] overflow-auto`}
|
||||||
|
relativeTo={popupRef}
|
||||||
|
onDismiss={() => setState({ showMenu: false })}
|
||||||
|
>
|
||||||
<div className="flex flex-wrap justify-between items-center">
|
<div className="flex flex-wrap justify-between items-center">
|
||||||
<Heading className="p-4 justify-center" size="md">{title}</Heading>
|
<Heading className="p-4 justify-center" size="md">
|
||||||
<Button tabindex="false" className="mx-4" onClick={() => onShowAll() }>
|
{title}
|
||||||
|
</Heading>
|
||||||
|
<Button tabindex="false" className="mx-4" onClick={() => onShowAll()}>
|
||||||
Show All
|
Show All
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{options.map((item) => (
|
{options.map((item) => (
|
||||||
<div className="flex flex-grow" key={item}>
|
<div className="flex flex-grow" key={item}>
|
||||||
<label
|
<label
|
||||||
className={`flex flex-shrink space-x-2 p-1 my-1 min-w-[176px] hover:bg-gray-200 dark:hover:bg-gray-800 dark:hover:text-white cursor-pointer capitalize text-sm`}>
|
className={`flex flex-shrink space-x-2 p-1 my-1 min-w-[176px] hover:bg-gray-200 dark:hover:bg-gray-800 dark:hover:text-white cursor-pointer capitalize text-sm`}
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
className="mx-4 m-0 align-middle"
|
className="mx-4 m-0 align-middle"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={isOptionSelected(item)}
|
checked={isOptionSelected(item)}
|
||||||
onChange={() => onToggle(item)} />
|
onChange={() => onToggle(item)}
|
||||||
{item.replaceAll("_", " ")}
|
/>
|
||||||
|
{item.replaceAll('_', ' ')}
|
||||||
</label>
|
</label>
|
||||||
<div className="justify-right">
|
<div className="justify-right">
|
||||||
<Button color={isOptionSelected(item) ? "blue" : "black"} type="text" className="max-h-[35px] mx-2" onClick={() => onSelectSingle(item)}>
|
<Button
|
||||||
|
color={isOptionSelected(item) ? 'blue' : 'black'}
|
||||||
|
type="text"
|
||||||
|
className="max-h-[35px] mx-2"
|
||||||
|
onClick={() => onSelectSingle(item)}
|
||||||
|
>
|
||||||
<CameraIcon />
|
<CameraIcon />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</Menu>
|
</Menu>
|
||||||
): null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ export default function RelativeModal({
|
|||||||
x: relativeToX,
|
x: relativeToX,
|
||||||
y: relativeToY,
|
y: relativeToY,
|
||||||
width: relativeToWidth,
|
width: relativeToWidth,
|
||||||
// height: relativeToHeight,
|
height: relativeToHeight,
|
||||||
} = relativeTo.current.getBoundingClientRect();
|
} = relativeTo.current.getBoundingClientRect();
|
||||||
|
|
||||||
const _width = widthRelative ? relativeToWidth : menuWidth;
|
const _width = widthRelative ? relativeToWidth : menuWidth;
|
||||||
@ -78,10 +78,13 @@ export default function RelativeModal({
|
|||||||
newLeft = windowWidth - width - WINDOW_PADDING;
|
newLeft = windowWidth - width - WINDOW_PADDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
// too close to bottom
|
// This condition checks if the menu overflows the bottom of the page and
|
||||||
if (top + menuHeight > windowHeight - WINDOW_PADDING + window.scrollY) {
|
// if there's enough space to position the menu above the clicked icon.
|
||||||
// If the pop-up modal would extend beyond the bottom of the visible window,
|
// If both conditions are met, the menu will be positioned above the clicked icon
|
||||||
// reposition the modal to appear above the clicked icon instead
|
if (
|
||||||
|
top + menuHeight > windowHeight - WINDOW_PADDING + window.scrollY &&
|
||||||
|
top - menuHeight - relativeToHeight >= WINDOW_PADDING
|
||||||
|
) {
|
||||||
newTop = top - menuHeight;
|
newTop = top - menuHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +92,13 @@ export default function RelativeModal({
|
|||||||
newTop = WINDOW_PADDING;
|
newTop = WINDOW_PADDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxHeight = windowHeight - WINDOW_PADDING * 2 > menuHeight ? null : windowHeight - WINDOW_PADDING * 2;
|
// This calculation checks if there's enough space below the clicked icon for the menu to fit.
|
||||||
|
// If there is, it sets the maxHeight to null(meaning no height constraint). If not, it calculates the maxHeight based on the remaining space in the window
|
||||||
|
const maxHeight =
|
||||||
|
windowHeight - WINDOW_PADDING * 2 - top > menuHeight
|
||||||
|
? null
|
||||||
|
: windowHeight - WINDOW_PADDING * 2 - top + window.scrollY;
|
||||||
|
|
||||||
const newPosition = { left: newLeft, top: newTop, maxHeight };
|
const newPosition = { left: newLeft, top: newTop, maxHeight };
|
||||||
if (widthRelative) {
|
if (widthRelative) {
|
||||||
newPosition.width = relativeToWidth;
|
newPosition.width = relativeToWidth;
|
||||||
@ -115,7 +124,7 @@ export default function RelativeModal({
|
|||||||
<div data-testid="scrim" key="scrim" className="fixed inset-0 z-10" onClick={handleDismiss} />
|
<div data-testid="scrim" key="scrim" className="fixed inset-0 z-10" onClick={handleDismiss} />
|
||||||
<div
|
<div
|
||||||
key="menu"
|
key="menu"
|
||||||
className={`z-10 bg-white dark:bg-gray-700 dark:text-white absolute shadow-lg rounded w-auto h-auto transition-transform transition-opacity duration-75 transform scale-90 opacity-0 overflow-x-hidden overflow-y-auto ${
|
className={`z-10 bg-white dark:bg-gray-700 dark:text-white absolute shadow-lg rounded w-auto h-auto transition-transform duration-75 transform scale-90 opacity-0 overflow-x-hidden overflow-y-auto ${
|
||||||
show ? 'scale-100 opacity-100' : ''
|
show ? 'scale-100 opacity-100' : ''
|
||||||
} ${className}`}
|
} ${className}`}
|
||||||
onKeyDown={handleKeydown}
|
onKeyDown={handleKeydown}
|
||||||
|
@ -111,7 +111,7 @@ export default function System() {
|
|||||||
|
|
||||||
{state.showFfprobe && (
|
{state.showFfprobe && (
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<div className="p-4 mb-2 max-h-96 whitespace-pre-line overflow-scroll">
|
<div className="p-4 mb-2 max-h-96 whitespace-pre-line overflow-auto">
|
||||||
<Heading size="lg">Ffprobe Output</Heading>
|
<Heading size="lg">Ffprobe Output</Heading>
|
||||||
{state.ffprobe != '' ? (
|
{state.ffprobe != '' ? (
|
||||||
<div>
|
<div>
|
||||||
@ -175,7 +175,7 @@ export default function System() {
|
|||||||
|
|
||||||
{state.showVainfo && (
|
{state.showVainfo && (
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<div className="p-4 overflow-scroll whitespace-pre-line">
|
<div className="p-4 overflow-auto whitespace-pre-line">
|
||||||
<Heading size="lg">Vainfo Output</Heading>
|
<Heading size="lg">Vainfo Output</Heading>
|
||||||
{state.vainfo != '' ? (
|
{state.vainfo != '' ? (
|
||||||
<div className="mb-2 max-h-96 whitespace-pre-line">
|
<div className="mb-2 max-h-96 whitespace-pre-line">
|
||||||
|
Loading…
Reference in New Issue
Block a user