5922 - UI Overlap and State Persistence in Filter Menus (#7270)

fixes #5922 


https://github.com/user-attachments/assets/07d088da-cefb-4d87-9016-e14cef18567d
This commit is contained in:
nitin
2024-09-27 17:50:21 +05:30
committed by GitHub
parent c4762c3921
commit ca906bbf6b
5 changed files with 154 additions and 117 deletions

View File

@ -2,6 +2,7 @@ import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdow
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { useCallback } from 'react';
import { MultipleFiltersButton } from './MultipleFiltersButton';
import { MultipleFiltersDropdownContent } from './MultipleFiltersDropdownContent';
@ -13,12 +14,18 @@ type MultipleFiltersDropdownButtonProps = {
export const MultipleFiltersDropdownButton = ({
hotkeyScope,
}: MultipleFiltersDropdownButtonProps) => {
const { resetFilter } = useFilterDropdown();
const { resetFilter, setIsObjectFilterDropdownOperandSelectUnfolded } =
useFilterDropdown();
const handleDropdownClose = useCallback(() => {
resetFilter();
setIsObjectFilterDropdownOperandSelectUnfolded(false);
}, [resetFilter, setIsObjectFilterDropdownOperandSelectUnfolded]);
return (
<Dropdown
dropdownId={OBJECT_FILTER_DROPDOWN_ID}
onClose={resetFilter}
onClose={handleDropdownClose}
clickableComponent={<MultipleFiltersButton />}
dropdownComponents={<MultipleFiltersDropdownContent />}
dropdownHotkeyScope={hotkeyScope}

View File

@ -1,11 +1,9 @@
import { useRecoilValue } from 'recoil';
import { ObjectFilterDropdownRatingInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownRatingInput';
import { ObjectFilterDropdownSearchInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownSearchInput';
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { ObjectFilterDropdownRatingInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownRatingInput';
import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
import { MultipleFiltersDropdownFilterOnFilterChangedEffect } from './MultipleFiltersDropdownFilterOnFilterChangedEffect';
import { ObjectFilterDropdownDateInput } from './ObjectFilterDropdownDateInput';
import { ObjectFilterDropdownFilterSelect } from './ObjectFilterDropdownFilterSelect';
@ -16,6 +14,21 @@ import { ObjectFilterDropdownOptionSelect } from './ObjectFilterDropdownOptionSe
import { ObjectFilterDropdownRecordSelect } from './ObjectFilterDropdownRecordSelect';
import { ObjectFilterDropdownTextSearchInput } from './ObjectFilterDropdownTextSearchInput';
const StyledContainer = styled.div`
position: relative;
`;
const StyledOperandSelectContainer = styled.div`
background: ${({ theme }) => theme.background.secondary};
box-shadow: ${({ theme }) => theme.boxShadow.light};
border-radius: ${({ theme }) => theme.border.radius.md};
left: 10px;
position: absolute;
top: 10px;
width: 100%;
z-index: 1000;
`;
type MultipleFiltersDropdownContentProps = {
filterDropdownId?: string;
};
@ -24,20 +37,23 @@ export const MultipleFiltersDropdownContent = ({
filterDropdownId,
}: MultipleFiltersDropdownContentProps) => {
const {
isObjectFilterDropdownOperandSelectUnfoldedState,
filterDefinitionUsedInDropdownState,
selectedOperandInDropdownState,
isObjectFilterDropdownOperandSelectUnfoldedState,
} = useFilterDropdown({ filterDropdownId });
const isObjectFilterDropdownOperandSelectUnfolded = useRecoilValue(
isObjectFilterDropdownOperandSelectUnfoldedState,
);
const filterDefinitionUsedInDropdown = useRecoilValue(
filterDefinitionUsedInDropdownState,
);
const selectedOperandInDropdown = useRecoilValue(
selectedOperandInDropdownState,
);
const isEmptyOperand =
selectedOperandInDropdown &&
[ViewFilterOperand.IsEmpty, ViewFilterOperand.IsNotEmpty].includes(
@ -45,64 +61,64 @@ export const MultipleFiltersDropdownContent = ({
);
return (
<>
<StyledContainer>
{!filterDefinitionUsedInDropdown ? (
<ObjectFilterDropdownFilterSelect />
) : isObjectFilterDropdownOperandSelectUnfolded ? (
<ObjectFilterDropdownOperandSelect />
) : isEmptyOperand ? (
<ObjectFilterDropdownOperandButton />
) : (
selectedOperandInDropdown && (
<>
<ObjectFilterDropdownOperandButton />
<DropdownMenuSeparator />
{[
'TEXT',
'EMAIL',
'EMAILS',
'PHONE',
'FULL_NAME',
'LINK',
'LINKS',
'ADDRESS',
'ACTOR',
'ARRAY',
'PHONES',
].includes(filterDefinitionUsedInDropdown.type) && (
<ObjectFilterDropdownTextSearchInput />
)}
{['NUMBER', 'CURRENCY'].includes(
filterDefinitionUsedInDropdown.type,
) && <ObjectFilterDropdownNumberInput />}
{filterDefinitionUsedInDropdown.type === 'RATING' && (
<ObjectFilterDropdownRatingInput />
)}
{['DATE_TIME', 'DATE'].includes(
filterDefinitionUsedInDropdown.type,
) && <ObjectFilterDropdownDateInput />}
{filterDefinitionUsedInDropdown.type === 'RELATION' && (
<>
<ObjectFilterDropdownSearchInput />
<DropdownMenuSeparator />
<ObjectFilterDropdownRecordSelect />
</>
)}
{filterDefinitionUsedInDropdown.type === 'SELECT' && (
<>
<ObjectFilterDropdownSearchInput />
<DropdownMenuSeparator />
<ObjectFilterDropdownOptionSelect />
</>
)}
</>
)
<>
<ObjectFilterDropdownOperandButton />
{isObjectFilterDropdownOperandSelectUnfolded && (
<StyledOperandSelectContainer>
<ObjectFilterDropdownOperandSelect />
</StyledOperandSelectContainer>
)}
{!isEmptyOperand && selectedOperandInDropdown && (
<>
{[
'TEXT',
'EMAIL',
'EMAILS',
'PHONE',
'FULL_NAME',
'LINK',
'LINKS',
'ADDRESS',
'ACTOR',
'ARRAY',
'PHONES',
].includes(filterDefinitionUsedInDropdown.type) && (
<ObjectFilterDropdownTextSearchInput />
)}
{['NUMBER', 'CURRENCY'].includes(
filterDefinitionUsedInDropdown.type,
) && <ObjectFilterDropdownNumberInput />}
{filterDefinitionUsedInDropdown.type === 'RATING' && (
<ObjectFilterDropdownRatingInput />
)}
{['DATE_TIME', 'DATE'].includes(
filterDefinitionUsedInDropdown.type,
) && <ObjectFilterDropdownDateInput />}
{filterDefinitionUsedInDropdown.type === 'RELATION' && (
<>
<ObjectFilterDropdownSearchInput />
<ObjectFilterDropdownRecordSelect />
</>
)}
{filterDefinitionUsedInDropdown.type === 'SELECT' && (
<>
<ObjectFilterDropdownSearchInput />
<ObjectFilterDropdownOptionSelect />
</>
)}
</>
)}
</>
)}
<MultipleFiltersDropdownFilterOnFilterChangedEffect
filterDefinitionUsedInDropdownType={
filterDefinitionUsedInDropdown?.type
}
/>
</>
</StyledContainer>
);
};

View File

@ -10,19 +10,11 @@ export const ObjectFilterDropdownOperandButton = () => {
const {
selectedOperandInDropdownState,
setIsObjectFilterDropdownOperandSelectUnfolded,
isObjectFilterDropdownOperandSelectUnfoldedState,
} = useFilterDropdown();
const selectedOperandInDropdown = useRecoilValue(
selectedOperandInDropdownState,
);
const isObjectFilterDropdownOperandSelectUnfolded = useRecoilValue(
isObjectFilterDropdownOperandSelectUnfoldedState,
);
if (isObjectFilterDropdownOperandSelectUnfolded) {
return null;
}
return (
<DropdownMenuHeader

View File

@ -39,6 +39,21 @@ export const StyledInput = styled.input`
}
`;
const StyledContainer = styled.div`
position: relative;
`;
const StyledSelectedSortDirectionContainer = styled.div`
background: ${({ theme }) => theme.background.secondary};
box-shadow: ${({ theme }) => theme.boxShadow.light};
border-radius: ${({ theme }) => theme.border.radius.md};
left: 10px;
position: absolute;
top: 10px;
width: 100%;
z-index: 1000;
`;
export type ObjectSortDropdownButtonProps = {
sortDropdownId: string;
hotkeyScope: HotkeyScope;
@ -95,60 +110,61 @@ export const ObjectSortDropdownButton = ({
}
dropdownComponents={
<>
{isSortDirectionMenuUnfolded ? (
<DropdownMenuItemsContainer>
{SORT_DIRECTIONS.map((sortOrder, index) => (
<MenuItem
key={index}
onClick={() => {
setSelectedSortDirection(sortOrder);
setIsSortDirectionMenuUnfolded(false);
}}
text={sortOrder === 'asc' ? 'Ascending' : 'Descending'}
/>
))}
</DropdownMenuItemsContainer>
) : (
<>
<DropdownMenuHeader
EndIcon={IconChevronDown}
onClick={() => setIsSortDirectionMenuUnfolded(true)}
>
{selectedSortDirection === 'asc' ? 'Ascending' : 'Descending'}
</DropdownMenuHeader>
<StyledInput
autoFocus
value={objectSortDropdownSearchInput}
placeholder="Search fields"
onChange={(event) =>
setObjectSortDropdownSearchInput(event.target.value)
}
/>
{isSortDirectionMenuUnfolded && (
<StyledSelectedSortDirectionContainer>
<DropdownMenuItemsContainer>
{[...availableSortDefinitions]
.sort((a, b) => a.label.localeCompare(b.label))
.filter((item) =>
item.label
.toLocaleLowerCase()
.includes(
objectSortDropdownSearchInput.toLocaleLowerCase(),
),
)
.map((availableSortDefinition, index) => (
<MenuItem
testId={`select-sort-${index}`}
key={index}
onClick={() => {
setObjectSortDropdownSearchInput('');
handleAddSort(availableSortDefinition);
}}
LeftIcon={getIcon(availableSortDefinition.iconName)}
text={availableSortDefinition.label}
/>
))}
{SORT_DIRECTIONS.map((sortOrder, index) => (
<MenuItem
key={index}
onClick={() => {
setSelectedSortDirection(sortOrder);
setIsSortDirectionMenuUnfolded(false);
}}
text={sortOrder === 'asc' ? 'Ascending' : 'Descending'}
/>
))}
</DropdownMenuItemsContainer>
</>
</StyledSelectedSortDirectionContainer>
)}
<StyledContainer>
<DropdownMenuHeader
EndIcon={IconChevronDown}
onClick={() => setIsSortDirectionMenuUnfolded(true)}
>
{selectedSortDirection === 'asc' ? 'Ascending' : 'Descending'}
</DropdownMenuHeader>
<StyledInput
autoFocus
value={objectSortDropdownSearchInput}
placeholder="Search fields"
onChange={(event) =>
setObjectSortDropdownSearchInput(event.target.value)
}
/>
<DropdownMenuItemsContainer>
{[...availableSortDefinitions]
.sort((a, b) => a.label.localeCompare(b.label))
.filter((item) =>
item.label
.toLocaleLowerCase()
.includes(
objectSortDropdownSearchInput.toLocaleLowerCase(),
),
)
.map((availableSortDefinition, index) => (
<MenuItem
testId={`select-sort-${index}`}
key={index}
onClick={() => {
setObjectSortDropdownSearchInput('');
handleAddSort(availableSortDefinition);
}}
LeftIcon={getIcon(availableSortDefinition.iconName)}
text={availableSortDefinition.label}
/>
))}
</DropdownMenuItemsContainer>
</StyledContainer>
</>
}
onClose={handleDropdownButtonClose}

View File

@ -29,6 +29,7 @@ export const EditableFilterDropdownButton = ({
setFilterDefinitionUsedInDropdown,
setSelectedOperandInDropdown,
setSelectedFilter,
setIsObjectFilterDropdownOperandSelectUnfolded,
} = useFilterDropdown({
filterDropdownId: viewFilterDropdownId,
});
@ -79,6 +80,10 @@ export const EditableFilterDropdownButton = ({
}
}, [viewFilter, deleteCombinedViewFilter]);
const handleDropdownClose = useCallback(() => {
setIsObjectFilterDropdownOperandSelectUnfolded(false);
}, [setIsObjectFilterDropdownOperandSelectUnfolded]);
return (
<Dropdown
dropdownId={viewFilterDropdownId}
@ -94,6 +99,7 @@ export const EditableFilterDropdownButton = ({
dropdownOffset={{ y: 8, x: 0 }}
dropdownPlacement="bottom-start"
onClickOutside={handleDropdownClickOutside}
onClose={handleDropdownClose}
/>
);
};