mirror of
				https://github.com/blakeblackshear/frigate.git
				synced 2025-10-27 10:52:11 +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