mirror of
https://github.com/twentyhq/twenty.git
synced 2025-01-09 02:11:55 +03:00
Fix filter multi select (#8682)
- Created a dropdown inside a dropdown for the `ObjectFilterDropdownOperandDropdown` so the operand can be opened over the selection with an offset - Refactored dropdown component and introduced `DropdownUnmountEffect` to close the dropdown when the component unmounts - Removed old logic Before: <img width="216" alt="Capture d’écran 2024-11-22 à 14 03 58" src="https://github.com/user-attachments/assets/3c1bba03-af03-4993-a070-f009b8dc876f"> After: <img width="222" alt="Capture d’écran 2024-11-22 à 14 03 40" src="https://github.com/user-attachments/assets/a8a784b4-8672-4b02-bb21-e4a749682f2e">
This commit is contained in:
parent
ac9cf737fb
commit
f44e2935df
@ -62,7 +62,7 @@ export const RightDrawerActionMenuDropdown = () => {
|
||||
clickableComponent={<Button title="Actions" shortcut="⌘O" />}
|
||||
dropdownPlacement="top-end"
|
||||
dropdownOffset={{
|
||||
y: parseInt(theme.spacing(2)),
|
||||
y: parseInt(theme.spacing(2), 10),
|
||||
}}
|
||||
dropdownComponents={
|
||||
<DropdownMenuItemsContainer>
|
||||
|
@ -22,7 +22,6 @@ export const AdvancedFilterViewFilterValueInput = ({
|
||||
const {
|
||||
setFilterDefinitionUsedInDropdown,
|
||||
setSelectedOperandInDropdown,
|
||||
setIsObjectFilterDropdownOperandSelectUnfolded,
|
||||
setSelectedFilter,
|
||||
} = useFilterDropdown();
|
||||
|
||||
@ -53,7 +52,6 @@ export const AdvancedFilterViewFilterValueInput = ({
|
||||
onOpen={() => {
|
||||
setFilterDefinitionUsedInDropdown(filter.definition);
|
||||
setSelectedOperandInDropdown(filter.operand);
|
||||
setIsObjectFilterDropdownOperandSelectUnfolded(true);
|
||||
setSelectedFilter(filter);
|
||||
}}
|
||||
dropdownComponents={
|
||||
|
@ -14,13 +14,11 @@ type MultipleFiltersDropdownButtonProps = {
|
||||
export const MultipleFiltersDropdownButton = ({
|
||||
hotkeyScope,
|
||||
}: MultipleFiltersDropdownButtonProps) => {
|
||||
const { resetFilter, setIsObjectFilterDropdownOperandSelectUnfolded } =
|
||||
useFilterDropdown();
|
||||
const { resetFilter } = useFilterDropdown();
|
||||
|
||||
const handleDropdownClose = useCallback(() => {
|
||||
resetFilter();
|
||||
setIsObjectFilterDropdownOperandSelectUnfolded(false);
|
||||
}, [resetFilter, setIsObjectFilterDropdownOperandSelectUnfolded]);
|
||||
}, [resetFilter]);
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
|
@ -21,7 +21,6 @@ export const ObjectFilterDropdownDateInput = () => {
|
||||
filterDefinitionUsedInDropdownState,
|
||||
selectedOperandInDropdownState,
|
||||
selectedFilterState,
|
||||
setIsObjectFilterDropdownUnfolded,
|
||||
selectFilter,
|
||||
} = useFilterDropdown();
|
||||
|
||||
@ -65,8 +64,6 @@ export const ObjectFilterDropdownDateInput = () => {
|
||||
definition: filterDefinitionUsedInDropdown,
|
||||
viewFilterGroupId: selectedFilter?.viewFilterGroupId,
|
||||
});
|
||||
|
||||
setIsObjectFilterDropdownUnfolded(false);
|
||||
};
|
||||
|
||||
const handleRelativeDateChange = (
|
||||
@ -95,8 +92,6 @@ export const ObjectFilterDropdownDateInput = () => {
|
||||
definition: filterDefinitionUsedInDropdown,
|
||||
viewFilterGroupId: selectedFilter?.viewFilterGroupId,
|
||||
});
|
||||
|
||||
setIsObjectFilterDropdownUnfolded(false);
|
||||
};
|
||||
|
||||
const isRelativeOperand =
|
||||
|
@ -28,13 +28,8 @@ export const ObjectFilterDropdownFilterInput = ({
|
||||
const {
|
||||
filterDefinitionUsedInDropdownState,
|
||||
selectedOperandInDropdownState,
|
||||
isObjectFilterDropdownOperandSelectUnfoldedState,
|
||||
} = useFilterDropdown({ filterDropdownId });
|
||||
|
||||
const isObjectFilterDropdownOperandSelectUnfolded = useRecoilValue(
|
||||
isObjectFilterDropdownOperandSelectUnfoldedState,
|
||||
);
|
||||
|
||||
const filterDefinitionUsedInDropdown = useRecoilValue(
|
||||
filterDefinitionUsedInDropdownState,
|
||||
);
|
||||
@ -58,9 +53,7 @@ export const ObjectFilterDropdownFilterInput = ({
|
||||
ViewFilterOperand.IsRelative,
|
||||
].includes(selectedOperandInDropdown);
|
||||
|
||||
const shouldHide = isObjectFilterDropdownOperandSelectUnfolded;
|
||||
|
||||
if (shouldHide || !isDefined(filterDefinitionUsedInDropdown)) {
|
||||
if (!isDefined(filterDefinitionUsedInDropdown)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -84,6 +77,7 @@ export const ObjectFilterDropdownFilterInput = ({
|
||||
{filterDefinitionUsedInDropdown.type === 'RELATION' && (
|
||||
<>
|
||||
<ObjectFilterDropdownSearchInput />
|
||||
<DropdownMenuSeparator />
|
||||
<ObjectFilterDropdownRecordSelect />
|
||||
</>
|
||||
)}
|
||||
@ -98,6 +92,7 @@ export const ObjectFilterDropdownFilterInput = ({
|
||||
) && (
|
||||
<>
|
||||
<ObjectFilterDropdownSearchInput />
|
||||
<DropdownMenuSeparator />
|
||||
<ObjectFilterDropdownOptionSelect />
|
||||
</>
|
||||
)}
|
||||
|
@ -1,38 +0,0 @@
|
||||
import { ObjectFilterDropdownOperandButton } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownOperandButton';
|
||||
import { ObjectFilterDropdownOperandSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownOperandSelect';
|
||||
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
const StyledOperandSelectContainer = styled.div`
|
||||
background: ${({ theme }) => theme.background.secondary};
|
||||
box-shadow: ${({ theme }) => theme.boxShadow.light};
|
||||
border-radius: ${({ theme }) => theme.border.radius.md};
|
||||
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
`;
|
||||
|
||||
export const ObjectFilterDropdownFilterOperandSelect = ({
|
||||
filterDropdownId,
|
||||
}: {
|
||||
filterDropdownId?: string;
|
||||
}) => {
|
||||
const { isObjectFilterDropdownOperandSelectUnfoldedState } =
|
||||
useFilterDropdown({ filterDropdownId });
|
||||
|
||||
const isObjectFilterDropdownOperandSelectUnfolded = useRecoilValue(
|
||||
isObjectFilterDropdownOperandSelectUnfoldedState,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ObjectFilterDropdownOperandButton />
|
||||
{isObjectFilterDropdownOperandSelectUnfolded && (
|
||||
<StyledOperandSelectContainer>
|
||||
<ObjectFilterDropdownOperandSelect />
|
||||
</StyledOperandSelectContainer>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,39 +0,0 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { IconChevronDown } from 'twenty-ui';
|
||||
|
||||
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
|
||||
|
||||
import { getOperandLabel } from '../utils/getOperandLabel';
|
||||
|
||||
export const ObjectFilterDropdownOperandButton = () => {
|
||||
const {
|
||||
selectedOperandInDropdownState,
|
||||
setIsObjectFilterDropdownOperandSelectUnfolded,
|
||||
isObjectFilterDropdownOperandSelectUnfoldedState,
|
||||
} = useFilterDropdown();
|
||||
|
||||
const selectedOperandInDropdown = useRecoilValue(
|
||||
selectedOperandInDropdownState,
|
||||
);
|
||||
|
||||
const isObjectFilterDropdownOperandSelectUnfolded = useRecoilValue(
|
||||
isObjectFilterDropdownOperandSelectUnfoldedState,
|
||||
);
|
||||
|
||||
const handleButtonClick = () => {
|
||||
setIsObjectFilterDropdownOperandSelectUnfolded(
|
||||
!isObjectFilterDropdownOperandSelectUnfolded,
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<DropdownMenuHeader
|
||||
key={'selected-filter-operand'}
|
||||
EndIcon={IconChevronDown}
|
||||
onClick={handleButtonClick}
|
||||
>
|
||||
{getOperandLabel(selectedOperandInDropdown)}
|
||||
</DropdownMenuHeader>
|
||||
);
|
||||
};
|
@ -0,0 +1,51 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { IconChevronDown } from 'twenty-ui';
|
||||
|
||||
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
|
||||
|
||||
import { ObjectFilterDropdownOperandSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownOperandSelect';
|
||||
import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { getOperandLabel } from '../utils/getOperandLabel';
|
||||
|
||||
const StyledDropdownMenuHeader = styled(DropdownMenuHeader)`
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
export const ObjectFilterDropdownOperandDropdown = ({
|
||||
filterDropdownId,
|
||||
}: {
|
||||
filterDropdownId?: string;
|
||||
}) => {
|
||||
const { selectedOperandInDropdownState } = useFilterDropdown();
|
||||
|
||||
const selectedOperandInDropdown = useRecoilValue(
|
||||
selectedOperandInDropdownState,
|
||||
);
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
dropdownId={`${filterDropdownId}-operand-dropdown`}
|
||||
clickableComponent={
|
||||
<StyledDropdownMenuHeader
|
||||
key={'selected-filter-operand'}
|
||||
EndIcon={IconChevronDown}
|
||||
>
|
||||
{getOperandLabel(selectedOperandInDropdown)}
|
||||
</StyledDropdownMenuHeader>
|
||||
}
|
||||
dropdownComponents={<ObjectFilterDropdownOperandSelect />}
|
||||
dropdownHotkeyScope={{
|
||||
scope: FiltersHotkeyScope.ObjectFilterDropdownOperandDropdown,
|
||||
}}
|
||||
dropdownOffset={{
|
||||
x: parseInt(theme.spacing(2), 10),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
@ -2,33 +2,35 @@ import { useRecoilValue } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
|
||||
import { getInitialFilterValue } from '@/object-record/object-filter-dropdown/utils/getInitialFilterValue';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import styled from '@emotion/styled';
|
||||
import { MenuItem } from 'twenty-ui';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
import { getInitialFilterValue } from '@/object-record/object-filter-dropdown/utils/getInitialFilterValue';
|
||||
import { getOperandLabel } from '../utils/getOperandLabel';
|
||||
import { getOperandsForFilterDefinition } from '../utils/getOperandsForFilterType';
|
||||
|
||||
const StyledDropdownMenuItemsContainer = styled(DropdownMenuItemsContainer)`
|
||||
background-color: ${({ theme }) => theme.background.primary};
|
||||
border-radius: ${({ theme }) => theme.border.radius.md};
|
||||
`;
|
||||
|
||||
export const ObjectFilterDropdownOperandSelect = () => {
|
||||
const {
|
||||
filterDefinitionUsedInDropdownState,
|
||||
setSelectedOperandInDropdown,
|
||||
isObjectFilterDropdownOperandSelectUnfoldedState,
|
||||
setIsObjectFilterDropdownOperandSelectUnfolded,
|
||||
selectedFilterState,
|
||||
selectFilter,
|
||||
} = useFilterDropdown();
|
||||
|
||||
const { closeDropdown } = useDropdown();
|
||||
|
||||
const filterDefinitionUsedInDropdown = useRecoilValue(
|
||||
filterDefinitionUsedInDropdownState,
|
||||
);
|
||||
|
||||
const isObjectFilterDropdownOperandSelectUnfolded = useRecoilValue(
|
||||
isObjectFilterDropdownOperandSelectUnfoldedState,
|
||||
);
|
||||
|
||||
const selectedFilter = useRecoilValue(selectedFilterState);
|
||||
|
||||
const operandsForFilterType = isDefined(filterDefinitionUsedInDropdown)
|
||||
@ -45,7 +47,6 @@ export const ObjectFilterDropdownOperandSelect = () => {
|
||||
].includes(newOperand);
|
||||
|
||||
setSelectedOperandInDropdown(newOperand);
|
||||
setIsObjectFilterDropdownOperandSelectUnfolded(false);
|
||||
|
||||
if (isValuelessOperand && isDefined(filterDefinitionUsedInDropdown)) {
|
||||
selectFilter?.({
|
||||
@ -81,21 +82,18 @@ export const ObjectFilterDropdownOperandSelect = () => {
|
||||
}
|
||||
};
|
||||
|
||||
if (!isObjectFilterDropdownOperandSelectUnfolded) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownMenuItemsContainer>
|
||||
<StyledDropdownMenuItemsContainer>
|
||||
{operandsForFilterType.map((filterOperand, index) => (
|
||||
<MenuItem
|
||||
key={`select-filter-operand-${index}`}
|
||||
onClick={() => {
|
||||
handleOperandChange(filterOperand);
|
||||
closeDropdown();
|
||||
}}
|
||||
text={getOperandLabel(filterOperand)}
|
||||
/>
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
</StyledDropdownMenuItemsContainer>
|
||||
);
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { ObjectFilterDropdownFilterInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterInput';
|
||||
import { ObjectFilterDropdownFilterOperandSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterOperandSelect';
|
||||
import { ObjectFilterDropdownOperandDropdown } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownOperandDropdown';
|
||||
|
||||
type ObjectFilterOperandSelectAndInputProps = {
|
||||
filterDropdownId?: string;
|
||||
@ -10,7 +10,7 @@ export const ObjectFilterOperandSelectAndInput = ({
|
||||
}: ObjectFilterOperandSelectAndInputProps) => {
|
||||
return (
|
||||
<>
|
||||
<ObjectFilterDropdownFilterOperandSelect
|
||||
<ObjectFilterDropdownOperandDropdown
|
||||
filterDropdownId={filterDropdownId}
|
||||
/>
|
||||
<ObjectFilterDropdownFilterInput filterDropdownId={filterDropdownId} />
|
||||
|
@ -259,64 +259,6 @@ describe('useFilterDropdown', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should set isObjectFilterDropdownOperandSelectUnfolded', async () => {
|
||||
const { result } = renderHook(() => {
|
||||
useFilterDropdown({ filterDropdownId });
|
||||
const { isObjectFilterDropdownOperandSelectUnfoldedState } =
|
||||
useFilterDropdownStates(filterDropdownId);
|
||||
|
||||
const [
|
||||
isObjectFilterDropdownOperandSelectUnfolded,
|
||||
setIsObjectFilterDropdownOperandSelectUnfolded,
|
||||
] = useRecoilState(isObjectFilterDropdownOperandSelectUnfoldedState);
|
||||
return {
|
||||
isObjectFilterDropdownOperandSelectUnfolded,
|
||||
setIsObjectFilterDropdownOperandSelectUnfolded,
|
||||
};
|
||||
}, renderHookConfig);
|
||||
|
||||
expect(result.current.isObjectFilterDropdownOperandSelectUnfolded).toBe(
|
||||
false,
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.setIsObjectFilterDropdownOperandSelectUnfolded(true);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isObjectFilterDropdownOperandSelectUnfolded).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should set isObjectFilterDropdownUnfolded', async () => {
|
||||
const { result } = renderHook(() => {
|
||||
useFilterDropdown({ filterDropdownId });
|
||||
const { isObjectFilterDropdownUnfoldedState } =
|
||||
useFilterDropdownStates(filterDropdownId);
|
||||
|
||||
const [
|
||||
isObjectFilterDropdownUnfolded,
|
||||
setIsObjectFilterDropdownUnfolded,
|
||||
] = useRecoilState(isObjectFilterDropdownUnfoldedState);
|
||||
return {
|
||||
isObjectFilterDropdownUnfolded,
|
||||
setIsObjectFilterDropdownUnfolded,
|
||||
};
|
||||
}, renderHookConfig);
|
||||
|
||||
expect(result.current.isObjectFilterDropdownUnfolded).toBe(false);
|
||||
|
||||
act(() => {
|
||||
result.current.setIsObjectFilterDropdownUnfolded(true);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isObjectFilterDropdownUnfolded).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should reset filter', async () => {
|
||||
const { result } = renderHook(() => {
|
||||
const { resetFilter, selectFilter } = useFilterDropdown({
|
||||
|
@ -28,8 +28,6 @@ export const useFilterDropdown = (props?: UseFilterDropdownProps) => {
|
||||
objectFilterDropdownSearchInputState,
|
||||
objectFilterDropdownSelectedRecordIdsState,
|
||||
objectFilterDropdownSelectedOptionValuesState,
|
||||
isObjectFilterDropdownOperandSelectUnfoldedState,
|
||||
isObjectFilterDropdownUnfoldedState,
|
||||
selectedFilterState,
|
||||
selectedOperandInDropdownState,
|
||||
onFilterSelectState,
|
||||
@ -121,12 +119,7 @@ export const useFilterDropdown = (props?: UseFilterDropdownProps) => {
|
||||
const setObjectFilterDropdownSelectedOptionValues = useSetRecoilState(
|
||||
objectFilterDropdownSelectedOptionValuesState,
|
||||
);
|
||||
const setIsObjectFilterDropdownOperandSelectUnfolded = useSetRecoilState(
|
||||
isObjectFilterDropdownOperandSelectUnfoldedState,
|
||||
);
|
||||
const setIsObjectFilterDropdownUnfolded = useSetRecoilState(
|
||||
isObjectFilterDropdownUnfoldedState,
|
||||
);
|
||||
|
||||
const setOnFilterSelect = useSetRecoilState(onFilterSelectState);
|
||||
const setAdvancedFilterViewFilterGroupId = useSetRecoilState(
|
||||
advancedFilterViewFilterGroupIdState,
|
||||
@ -143,22 +136,16 @@ export const useFilterDropdown = (props?: UseFilterDropdownProps) => {
|
||||
setSelectedOperandInDropdown,
|
||||
setFilterDefinitionUsedInDropdown,
|
||||
setObjectFilterDropdownSearchInput,
|
||||
// setObjectFilterDropdownSelectedEntityId,
|
||||
setObjectFilterDropdownSelectedRecordIds,
|
||||
setObjectFilterDropdownSelectedOptionValues,
|
||||
setIsObjectFilterDropdownOperandSelectUnfolded,
|
||||
setIsObjectFilterDropdownUnfolded,
|
||||
setOnFilterSelect,
|
||||
setAdvancedFilterViewFilterGroupId,
|
||||
setAdvancedFilterViewFilterId,
|
||||
emptyFilterButKeepDefinition,
|
||||
filterDefinitionUsedInDropdownState,
|
||||
objectFilterDropdownSearchInputState,
|
||||
// objectFilterDropdownSelectedEntityIdState,
|
||||
objectFilterDropdownSelectedRecordIdsState,
|
||||
objectFilterDropdownSelectedOptionValuesState,
|
||||
isObjectFilterDropdownOperandSelectUnfoldedState,
|
||||
isObjectFilterDropdownUnfoldedState,
|
||||
selectedFilterState,
|
||||
selectedOperandInDropdownState,
|
||||
onFilterSelectState,
|
||||
|
@ -1,8 +1,6 @@
|
||||
import { advancedFilterViewFilterGroupIdComponentState } from '@/object-record/object-filter-dropdown/states/advancedFilterViewFilterGroupIdComponentState';
|
||||
import { advancedFilterViewFilterIdComponentState } from '@/object-record/object-filter-dropdown/states/advancedFilterViewFilterIdComponentState';
|
||||
import { filterDefinitionUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/filterDefinitionUsedInDropdownComponentState';
|
||||
import { isObjectFilterDropdownOperandSelectUnfoldedComponentState } from '@/object-record/object-filter-dropdown/states/isObjectFilterDropdownOperandSelectUnfoldedComponentState';
|
||||
import { isObjectFilterDropdownUnfoldedComponentState } from '@/object-record/object-filter-dropdown/states/isObjectFilterDropdownUnfoldedComponentState';
|
||||
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
|
||||
import { objectFilterDropdownSelectedOptionValuesComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedOptionValuesComponentState';
|
||||
import { objectFilterDropdownSelectedRecordIdsComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedRecordIdsComponentState';
|
||||
@ -32,17 +30,6 @@ export const useFilterDropdownStates = (scopeId: string) => {
|
||||
scopeId,
|
||||
);
|
||||
|
||||
const isObjectFilterDropdownOperandSelectUnfoldedState =
|
||||
extractComponentState(
|
||||
isObjectFilterDropdownOperandSelectUnfoldedComponentState,
|
||||
scopeId,
|
||||
);
|
||||
|
||||
const isObjectFilterDropdownUnfoldedState = extractComponentState(
|
||||
isObjectFilterDropdownUnfoldedComponentState,
|
||||
scopeId,
|
||||
);
|
||||
|
||||
const selectedFilterState = extractComponentState(
|
||||
selectedFilterComponentState,
|
||||
scopeId,
|
||||
@ -73,8 +60,6 @@ export const useFilterDropdownStates = (scopeId: string) => {
|
||||
objectFilterDropdownSearchInputState,
|
||||
objectFilterDropdownSelectedRecordIdsState,
|
||||
objectFilterDropdownSelectedOptionValuesState,
|
||||
isObjectFilterDropdownOperandSelectUnfoldedState,
|
||||
isObjectFilterDropdownUnfoldedState,
|
||||
selectedFilterState,
|
||||
selectedOperandInDropdownState,
|
||||
onFilterSelectState,
|
||||
|
@ -1,4 +1,5 @@
|
||||
export enum FiltersHotkeyScope {
|
||||
ObjectFilterDropdownButton = 'filter-dropdown-button',
|
||||
ObjectSortDropdownButton = 'sort-dropdown-button',
|
||||
ObjectFilterDropdownOperandDropdown = 'filter-dropdown-operand-dropdown',
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import { isDefined } from '~/utils/isDefined';
|
||||
import { useDropdown } from '../hooks/useDropdown';
|
||||
import { useInternalHotkeyScopeManagement } from '../hooks/useInternalHotkeyScopeManagement';
|
||||
|
||||
import { DropdownUnmountEffect } from '@/ui/layout/dropdown/components/DropdownUnmountEffect';
|
||||
import { useListenClickOutsideV2 } from '@/ui/utilities/pointer-event/hooks/useListenClickOutsideV2';
|
||||
import { DropdownMenu } from './DropdownMenu';
|
||||
import { DropdownOnToggleEffect } from './DropdownOnToggleEffect';
|
||||
@ -149,25 +150,38 @@ export const Dropdown = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<DropdownScope dropdownScopeId={getScopeIdFromComponentId(dropdownId)}>
|
||||
<div ref={containerRef} className={className}>
|
||||
{clickableComponent && (
|
||||
<div
|
||||
ref={refs.setReference}
|
||||
onClick={handleClickableComponentClick}
|
||||
className={className}
|
||||
>
|
||||
{clickableComponent}
|
||||
</div>
|
||||
)}
|
||||
{hotkey && (
|
||||
<HotkeyEffect
|
||||
hotkey={hotkey}
|
||||
onHotkeyTriggered={handleHotkeyTriggered}
|
||||
/>
|
||||
)}
|
||||
{isDropdownOpen && usePortal && (
|
||||
<FloatingPortal>
|
||||
<>
|
||||
<DropdownScope dropdownScopeId={getScopeIdFromComponentId(dropdownId)}>
|
||||
<div ref={containerRef} className={className}>
|
||||
{clickableComponent && (
|
||||
<div
|
||||
ref={refs.setReference}
|
||||
onClick={handleClickableComponentClick}
|
||||
className={className}
|
||||
>
|
||||
{clickableComponent}
|
||||
</div>
|
||||
)}
|
||||
{hotkey && (
|
||||
<HotkeyEffect
|
||||
hotkey={hotkey}
|
||||
onHotkeyTriggered={handleHotkeyTriggered}
|
||||
/>
|
||||
)}
|
||||
{isDropdownOpen && usePortal && (
|
||||
<FloatingPortal>
|
||||
<DropdownMenu
|
||||
disableBlur={disableBlur}
|
||||
width={dropdownMenuWidth ?? dropdownWidth}
|
||||
data-select-disable
|
||||
ref={refs.setFloating}
|
||||
style={floatingStyles}
|
||||
>
|
||||
{dropdownComponents}
|
||||
</DropdownMenu>
|
||||
</FloatingPortal>
|
||||
)}
|
||||
{isDropdownOpen && !usePortal && (
|
||||
<DropdownMenu
|
||||
disableBlur={disableBlur}
|
||||
width={dropdownMenuWidth ?? dropdownWidth}
|
||||
@ -177,24 +191,14 @@ export const Dropdown = ({
|
||||
>
|
||||
{dropdownComponents}
|
||||
</DropdownMenu>
|
||||
</FloatingPortal>
|
||||
)}
|
||||
{isDropdownOpen && !usePortal && (
|
||||
<DropdownMenu
|
||||
disableBlur={disableBlur}
|
||||
width={dropdownMenuWidth ?? dropdownWidth}
|
||||
data-select-disable
|
||||
ref={refs.setFloating}
|
||||
style={floatingStyles}
|
||||
>
|
||||
{dropdownComponents}
|
||||
</DropdownMenu>
|
||||
)}
|
||||
<DropdownOnToggleEffect
|
||||
onDropdownClose={onClose}
|
||||
onDropdownOpen={onOpen}
|
||||
/>
|
||||
</div>
|
||||
</DropdownScope>
|
||||
)}
|
||||
<DropdownOnToggleEffect
|
||||
onDropdownClose={onClose}
|
||||
onDropdownOpen={onOpen}
|
||||
/>
|
||||
</div>
|
||||
</DropdownScope>
|
||||
<DropdownUnmountEffect dropdownId={dropdownId} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -49,6 +49,7 @@ type DropdownMenuHeaderProps = ComponentProps<'li'> & {
|
||||
EndIcon?: IconComponent;
|
||||
onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
|
||||
testId?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export const DropdownMenuHeader = ({
|
||||
@ -57,12 +58,17 @@ export const DropdownMenuHeader = ({
|
||||
EndIcon,
|
||||
onClick,
|
||||
testId,
|
||||
className,
|
||||
}: DropdownMenuHeaderProps) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<>
|
||||
{EndIcon && (
|
||||
<StyledHeader data-testid={testId} onClick={onClick}>
|
||||
<StyledHeader
|
||||
data-testid={testId}
|
||||
onClick={onClick}
|
||||
className={className}
|
||||
>
|
||||
<StyledChildrenWrapper>{children}</StyledChildrenWrapper>
|
||||
<StyledEndIcon>
|
||||
<EndIcon size={theme.icon.size.md} />
|
||||
@ -70,7 +76,7 @@ export const DropdownMenuHeader = ({
|
||||
</StyledHeader>
|
||||
)}
|
||||
{StartIcon && (
|
||||
<StyledHeader data-testid={testId}>
|
||||
<StyledHeader data-testid={testId} className={className}>
|
||||
<LightIconButton
|
||||
testId="dropdown-menu-header-end-icon"
|
||||
Icon={StartIcon}
|
||||
|
@ -37,12 +37,17 @@ const StyledDropdownMenuItemsInternalContainer = styled.div`
|
||||
export const DropdownMenuItemsContainer = ({
|
||||
children,
|
||||
hasMaxHeight,
|
||||
className,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
hasMaxHeight?: boolean;
|
||||
className?: string;
|
||||
}) => {
|
||||
return (
|
||||
<StyledDropdownMenuItemsExternalContainer hasMaxHeight={hasMaxHeight}>
|
||||
<StyledDropdownMenuItemsExternalContainer
|
||||
hasMaxHeight={hasMaxHeight}
|
||||
className={className}
|
||||
>
|
||||
{hasMaxHeight ? (
|
||||
<StyledScrollWrapper contextProviderName="dropdownMenuItemsContainer">
|
||||
<StyledDropdownMenuItemsInternalContainer>
|
||||
|
@ -0,0 +1,18 @@
|
||||
import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export const DropdownUnmountEffect = ({
|
||||
dropdownId,
|
||||
}: {
|
||||
dropdownId: string;
|
||||
}) => {
|
||||
const { closeDropdown } = useDropdownV2();
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
closeDropdown(dropdownId);
|
||||
};
|
||||
}, [closeDropdown, dropdownId]);
|
||||
|
||||
return null;
|
||||
};
|
@ -30,7 +30,6 @@ export const EditableFilterDropdownButton = ({
|
||||
setFilterDefinitionUsedInDropdown,
|
||||
setSelectedOperandInDropdown,
|
||||
setSelectedFilter,
|
||||
setIsObjectFilterDropdownOperandSelectUnfolded,
|
||||
} = useFilterDropdown({
|
||||
filterDropdownId: viewFilterDropdownId,
|
||||
});
|
||||
@ -87,10 +86,6 @@ export const EditableFilterDropdownButton = ({
|
||||
}
|
||||
}, [viewFilter, deleteCombinedViewFilter]);
|
||||
|
||||
const handleDropdownClose = useCallback(() => {
|
||||
setIsObjectFilterDropdownOperandSelectUnfolded(false);
|
||||
}, [setIsObjectFilterDropdownOperandSelectUnfolded]);
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
dropdownId={viewFilterDropdownId}
|
||||
@ -106,7 +101,6 @@ export const EditableFilterDropdownButton = ({
|
||||
dropdownOffset={{ y: 8, x: 0 }}
|
||||
dropdownPlacement="bottom-start"
|
||||
onClickOutside={handleDropdownClickOutside}
|
||||
onClose={handleDropdownClose}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user