diff --git a/epictrack-web/src/components/shared/filterSelect/FilterSelect.tsx b/epictrack-web/src/components/shared/filterSelect/FilterSelect.tsx index 37ad60b98..c8f003da0 100644 --- a/epictrack-web/src/components/shared/filterSelect/FilterSelect.tsx +++ b/epictrack-web/src/components/shared/filterSelect/FilterSelect.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect, useRef } from "react"; import Select from "react-select"; import Menu from "./components/Menu"; import Option from "./components/Option"; @@ -21,9 +21,11 @@ const FilterSelect = (props: SelectProps) => { const [selectValue, setSelectValue] = React.useState( defaultValue ?? standardDefault ); + const menuRef = useRef(null); const [menuIsOpen, setMenuIsOpen] = React.useState( !!props.menuIsOpen ); + const [menuStyle, setMenuStyle] = React.useState({}); // eslint-disable-line const selectRef = React.useRef(null); const selectAllOption = React.useMemo( @@ -34,19 +36,19 @@ const FilterSelect = (props: SelectProps) => { [] ); + useEffect(() => { + if (menuIsOpen) { + adjustDropdownPosition(); + updateSelectedOptions(); + } + }, [menuIsOpen]); + const isSelectAllSelected = () => selectedOptions.includes(selectAllOption.value); const isOptionSelected = (o: OptionType) => isMulti ? selectedOptions.includes(o.value) : selectedOptions === o.value; - React.useEffect(() => { - const currentValues = isMulti - ? selectValue.map((v: OptionType) => v.value) - : selectValue.value; - setSelectedOptions(currentValues); - }, [menuIsOpen]); - const handleChange = (newValue: any, actionMeta: any) => { if (!isMulti) { if (isOptionSelected(newValue)) { @@ -126,6 +128,32 @@ const FilterSelect = (props: SelectProps) => { selectRef.current?.blur(); }; + const adjustDropdownPosition = () => { + if (menuRef?.current) { + const menuRect = menuRef.current.getBoundingClientRect(); + const windowWidth = window.innerWidth - 50; + const rightEdgeOfMenu = menuRect.left + menuRect.width; + + if (rightEdgeOfMenu > windowWidth) { + const overflow = rightEdgeOfMenu - windowWidth; + const newPosition = { + transform: `translateX(${-(overflow + 80)}px)`, + }; + setMenuStyle(newPosition); + } else { + setMenuStyle({}); + } + } + }; + + // Function to update selected options + const updateSelectedOptions = () => { + const currentValues = isMulti + ? selectValue.map((v: OptionType) => v.value) + : selectValue.value; + setSelectedOptions(currentValues); + }; + React.useEffect(() => { let filterOptions = props.options as OptionType[]; if (isMulti) filterOptions = [selectAllOption, ...filterOptions]; @@ -153,108 +181,113 @@ const FilterSelect = (props: SelectProps) => { }, [props.value]); return ( - null, + DropdownIndicator, + }} + filterProps={{ + applyFilters, + clearFilters, + selectedOptions, + onCancel, + variant: props.variant || "inline", + }} + menuIsOpen={menuIsOpen} + closeMenuOnSelect={false} + hideSelectedOptions={false} + onFocus={() => setMenuIsOpen(true)} + onBlur={() => setMenuIsOpen(false)} + ref={selectRef} + styles={{ + option: (base, provided) => ({ + ...base, + whiteSpace: "normal", + overflow: "hidden", + textOverflow: "ellipsis", + display: "flex", + alignItems: "center", + padding: ".5rem .75rem .5rem 0px", + fontWeight: "normal", + fontSize: "1rem", + maxWidth: props.maxWidth ?? "100%", + background: provided.isFocused + ? Palette.neutral.bg.main : "transparent", + color: provided.isSelected + ? Palette.primary.accent.main + : Palette.neutral.accent.dark, + cursor: provided.isFocused ? "pointer" : "default", + }), + control: (base, props) => ({ + ...base, + background: props.hasValue + ? Palette.primary.bg.light + : Palette.white, + borderWidth: "2px", + borderStyle: props.hasValue ? "none" : "solid", + borderColor: + props.isFocused || props.menuIsOpen + ? Palette.primary.accent.light + : Palette.neutral.accent.light, + boxShadow: "none", + ...(props.selectProps.filterProps?.variant === "bar" && { + borderColor: props.isFocused + ? Palette.primary.accent.light + : "transparent", + }), + }), + menu: (base, props) => ({ + ...base, + position: "relative", + marginBlock: "0px", + border: `1px solid ${Palette.neutral.accent.light}`, + borderRadius: "4px", + ...menuStyle, + }), + placeholder: (base, props) => ({ + ...base, + fontWeight: MET_Header_Font_Weight_Regular, + color: Palette.neutral.light, + fontSize: INPUT_SIZE, + lineHeight: "1rem", + ...(props.selectProps.filterProps?.variant == "bar" && { + color: Palette.primary.accent.main, + fontWeight: 700, + }), + }), + menuPortal: (base, props) => ({ + ...base, + zIndex: theme.zIndex.modal, + marginTop: "4px", }), - }), - menu: (base, props) => ({ - ...base, - position: "relative", - marginBlock: "0px", - border: `1px solid ${Palette.neutral.accent.light}`, - borderRadius: "4px", - }), - placeholder: (base, props) => ({ - ...base, - fontWeight: MET_Header_Font_Weight_Regular, - color: Palette.neutral.light, - fontSize: INPUT_SIZE, - lineHeight: "1rem", - ...(props.selectProps.filterProps?.variant == "bar" && { - color: Palette.primary.accent.main, - fontWeight: 700, + input: (base, props) => ({ + ...base, + fontWeight: "400", + fontSize: INPUT_SIZE, }), - }), - menuPortal: (base, props) => ({ - ...base, - zIndex: theme.zIndex.modal, - marginTop: "4px", - }), - input: (base, props) => ({ - ...base, - fontWeight: "400", - fontSize: INPUT_SIZE, - }), - }} - isClearable={false} - menuPortalTarget={document.body} - controlShouldRenderValue={props.controlShouldRenderValue} - isLoading={props.isLoading} - loadingMessage={() => "Loading..."} - isDisabled={props.isDisabled} - isSearchable={isSearchable()} - /> + }} + isClearable={false} + menuPortalTarget={document.body} + controlShouldRenderValue={props.controlShouldRenderValue} + isLoading={props.isLoading} + loadingMessage={() => "Loading..."} + isDisabled={props.isDisabled} + isSearchable={isSearchable()} + /> + ); };