Improved dropdown menu headers for filter and sorts (#13177)
This PR improves dropdown menu headers for filter and sort dropdown in view bar and editable filter chips. It adds what's necessary to navigate back or close the dropdown, so that we don't rely solely on click outside to exit the dropdown. This PR also refactors the components so that we clearly identify the two code paths that can use filter dropdowns : view bar and filter chip, everything that can be DRY stays in the object-filter-dropdown module but we try to have our wrapping components in each distinct module instead of blending everything with ternaries inside object-filter-dropdown module. The vector search input value wasn't correctly handled across the different dropdowns, due to a wrong component instance management, since the dropdown menu header improvement put this into light, I also refactored the state management of the vector search input. @Bonapara please check the QA video and tell me if it's ok, I didn't add dropdown menu header on the advanced filter field list dropdown because it's a select more than a standalone dropdown, what do you think ? QA : https://github.com/user-attachments/assets/17080f32-f302-436c-937b-3577715b7e84 QA Vector search fix : https://github.com/user-attachments/assets/6367bbf6-8a98-4b53-86cf-6ba92be130eb Fixes https://github.com/twentyhq/core-team-issues/issues/640 Fixes https://github.com/twentyhq/core-team-issues/issues/1206
This commit is contained in:
@ -0,0 +1,38 @@
|
||||
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
||||
import { DATE_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/DateFilterTypes';
|
||||
import { DATE_PICKER_DROPDOWN_CONTENT_WIDTH } from '@/object-record/object-filter-dropdown/constants/DatePickerDropdownContentWidth';
|
||||
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const ObjectFilterDropdownContentWrapper = ({
|
||||
children,
|
||||
}: React.PropsWithChildren) => {
|
||||
const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2(
|
||||
fieldMetadataItemUsedInDropdownComponentSelector,
|
||||
);
|
||||
|
||||
if (!isDefined(fieldMetadataItemUsedInDropdown)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const filterType = getFilterTypeFromFieldType(
|
||||
fieldMetadataItemUsedInDropdown.type,
|
||||
);
|
||||
|
||||
const isDateFilter = DATE_FILTER_TYPES.includes(filterType);
|
||||
|
||||
return (
|
||||
<DropdownContent
|
||||
widthInPixels={
|
||||
isDateFilter
|
||||
? DATE_PICKER_DROPDOWN_CONTENT_WIDTH
|
||||
: GenericDropdownContentWidth.ExtraLarge
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
@ -5,22 +5,19 @@ import { ObjectFilterDropdownRatingInput } from '@/object-record/object-filter-d
|
||||
import { ObjectFilterDropdownRecordSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordSelect';
|
||||
import { ObjectFilterDropdownSearchInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownSearchInput';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { ViewBarFilterDropdownVectorSearchInput } from '@/views/components/ViewBarFilterDropdownVectorSearchInput';
|
||||
|
||||
import { ViewFilterOperand } from 'twenty-shared/src/types/ViewFilterOperand';
|
||||
|
||||
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
||||
import { ObjectFilterDropdownBooleanSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownBooleanSelect';
|
||||
import { ObjectFilterDropdownFilterInputHeader } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterInputHeader';
|
||||
import { ObjectFilterDropdownInnerSelectOperandDropdown } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownInnerSelectOperandDropdown';
|
||||
import { ObjectFilterDropdownTextInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownTextInput';
|
||||
import { ObjectFilterDropdownVectorSearchInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownVectorSearchInput';
|
||||
import { DATE_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/DateFilterTypes';
|
||||
import { DATE_PICKER_DROPDOWN_CONTENT_WIDTH } from '@/object-record/object-filter-dropdown/constants/DatePickerDropdownContentWidth';
|
||||
import { NUMBER_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/NumberFilterTypes';
|
||||
import { TEXT_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/TextFilterTypes';
|
||||
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
|
||||
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
@ -62,11 +59,7 @@ export const ObjectFilterDropdownFilterInput = ({
|
||||
selectedOperandInDropdown === ViewFilterOperand.VectorSearch;
|
||||
|
||||
if (isVectorSearchFilter && isDefined(filterDropdownId)) {
|
||||
return (
|
||||
<ViewBarFilterDropdownVectorSearchInput
|
||||
filterDropdownId={filterDropdownId}
|
||||
/>
|
||||
);
|
||||
return <ObjectFilterDropdownVectorSearchInput />;
|
||||
}
|
||||
|
||||
if (!isDefined(fieldMetadataItemUsedInDropdown)) {
|
||||
@ -82,24 +75,21 @@ export const ObjectFilterDropdownFilterInput = ({
|
||||
|
||||
if (isOnlyOperand) {
|
||||
return (
|
||||
<DropdownContent widthInPixels={GenericDropdownContentWidth.ExtraLarge}>
|
||||
<ObjectFilterDropdownFilterInputHeader />
|
||||
<>
|
||||
<ObjectFilterDropdownInnerSelectOperandDropdown />
|
||||
</DropdownContent>
|
||||
</>
|
||||
);
|
||||
} else if (isDateFilter) {
|
||||
return (
|
||||
<DropdownContent widthInPixels={DATE_PICKER_DROPDOWN_CONTENT_WIDTH}>
|
||||
<ObjectFilterDropdownFilterInputHeader />
|
||||
<>
|
||||
<ObjectFilterDropdownInnerSelectOperandDropdown />
|
||||
<DropdownMenuSeparator />
|
||||
<ObjectFilterDropdownDateInput />
|
||||
</DropdownContent>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<DropdownContent widthInPixels={GenericDropdownContentWidth.ExtraLarge}>
|
||||
<ObjectFilterDropdownFilterInputHeader />
|
||||
<>
|
||||
<ObjectFilterDropdownInnerSelectOperandDropdown />
|
||||
<DropdownMenuSeparator />
|
||||
{TEXT_FILTER_TYPES.includes(filterType) && (
|
||||
@ -130,7 +120,7 @@ export const ObjectFilterDropdownFilterInput = ({
|
||||
</>
|
||||
)}
|
||||
{filterType === 'BOOLEAN' && <ObjectFilterDropdownBooleanSelect />}
|
||||
</DropdownContent>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,16 +1,56 @@
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
||||
|
||||
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
|
||||
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext';
|
||||
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { ViewBarFilterDropdownFilterInputMenuHeader } from '@/views/components/ViewBarFilterDropdownFilterInputMenuHeader';
|
||||
import { VIEW_BAR_FILTER_DROPDOWN_ID } from '@/views/constants/ViewBarFilterDropdownId';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useContext } from 'react';
|
||||
import { ViewFilterOperand } from 'twenty-shared/types';
|
||||
import { IconX } from 'twenty-ui/display';
|
||||
|
||||
export const ObjectFilterDropdownFilterInputHeader = () => {
|
||||
const { t } = useLingui();
|
||||
|
||||
const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2(
|
||||
fieldMetadataItemUsedInDropdownComponentSelector,
|
||||
);
|
||||
|
||||
return (
|
||||
<DropdownMenuHeader>
|
||||
{fieldMetadataItemUsedInDropdown?.label}
|
||||
</DropdownMenuHeader>
|
||||
const selectedOperandInDropdown = useRecoilComponentValueV2(
|
||||
selectedOperandInDropdownComponentState,
|
||||
);
|
||||
|
||||
const { closeDropdown } = useCloseDropdown();
|
||||
|
||||
const dropdownInstanceId = useContext(
|
||||
DropdownComponentInstanceContext,
|
||||
)?.instanceId;
|
||||
|
||||
const isInViewBarFilterDropdown =
|
||||
dropdownInstanceId === VIEW_BAR_FILTER_DROPDOWN_ID;
|
||||
|
||||
const isVectorSearchFilter =
|
||||
selectedOperandInDropdown === ViewFilterOperand.VectorSearch;
|
||||
|
||||
if (isInViewBarFilterDropdown) {
|
||||
return <ViewBarFilterDropdownFilterInputMenuHeader />;
|
||||
} else {
|
||||
return (
|
||||
<DropdownMenuHeader
|
||||
StartComponent={
|
||||
<DropdownMenuHeaderLeftComponent
|
||||
onClick={() => closeDropdown(dropdownInstanceId)}
|
||||
Icon={IconX}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{isVectorSearchFilter
|
||||
? t`Search`
|
||||
: fieldMetadataItemUsedInDropdown?.label}
|
||||
</DropdownMenuHeader>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
||||
import { DATE_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/DateFilterTypes';
|
||||
import { DATE_PICKER_DROPDOWN_CONTENT_WIDTH } from '@/object-record/object-filter-dropdown/constants/DatePickerDropdownContentWidth';
|
||||
import { useApplyObjectFilterDropdownOperand } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownOperand';
|
||||
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
|
||||
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
||||
@ -7,6 +9,7 @@ import { getOperandLabel } from '@/object-record/object-filter-dropdown/utils/ge
|
||||
import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand';
|
||||
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
|
||||
import { DropdownMenuInnerSelect } from '@/ui/layout/dropdown/components/DropdownMenuInnerSelect';
|
||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { SelectOption } from 'twenty-ui/input';
|
||||
@ -54,16 +57,30 @@ export const ObjectFilterDropdownInnerSelectOperandDropdown = () => {
|
||||
);
|
||||
};
|
||||
|
||||
if (!isDefined(selectedOperandInDropdown)) {
|
||||
if (
|
||||
!isDefined(selectedOperandInDropdown) ||
|
||||
!isDefined(fieldMetadataItemUsedInDropdown)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const filterType = getFilterTypeFromFieldType(
|
||||
fieldMetadataItemUsedInDropdown.type,
|
||||
);
|
||||
|
||||
const isDateFilter = DATE_FILTER_TYPES.includes(filterType);
|
||||
|
||||
const widthInPixels = isDateFilter
|
||||
? DATE_PICKER_DROPDOWN_CONTENT_WIDTH
|
||||
: GenericDropdownContentWidth.ExtraLarge;
|
||||
|
||||
return (
|
||||
<DropdownMenuInnerSelect
|
||||
dropdownId={OBJECT_FILTER_DROPDOWN_INNER_SELECT_OPERAND_DROPDOWN_ID}
|
||||
selectedOption={selectedOption}
|
||||
onChange={handleOperandChange}
|
||||
options={options}
|
||||
widthInPixels={widthInPixels}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -0,0 +1,38 @@
|
||||
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
||||
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
|
||||
import { useVectorSearchFilterActions } from '@/views/hooks/useVectorSearchFilterActions';
|
||||
import { vectorSearchInputComponentState } from '@/views/states/vectorSearchInputComponentState';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
|
||||
export const ObjectFilterDropdownVectorSearchInput = () => {
|
||||
const { t } = useLingui();
|
||||
|
||||
const [vectorSearchInputValue, setVectorSearchInputValue] =
|
||||
useRecoilComponentStateV2(vectorSearchInputComponentState);
|
||||
|
||||
const { applyVectorSearchFilter } = useVectorSearchFilterActions();
|
||||
|
||||
const debouncedApplyVectorSearchFilter = useDebouncedCallback(
|
||||
(value: string) => {
|
||||
applyVectorSearchFilter(value);
|
||||
},
|
||||
500,
|
||||
);
|
||||
|
||||
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const inputValue = e.target.value;
|
||||
setVectorSearchInputValue(inputValue);
|
||||
debouncedApplyVectorSearchFilter(inputValue);
|
||||
};
|
||||
|
||||
return (
|
||||
<DropdownMenuSearchInput
|
||||
autoFocus
|
||||
type="text"
|
||||
value={vectorSearchInputValue}
|
||||
placeholder={t`Search`}
|
||||
onChange={handleSearchChange}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,5 +1,3 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { availableFieldMetadataItemsForSortFamilySelector } from '@/object-metadata/states/availableFieldMetadataItemsForSortFamilySelector';
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { OBJECT_SORT_DROPDOWN_ID } from '@/object-record/object-sort-dropdown/constants/ObjectSortDropdownId';
|
||||
@ -20,7 +18,10 @@ import { visibleTableColumnsComponentSelector } from '@/object-record/record-tab
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||
import { DropdownMenuInnerSelect } from '@/ui/layout/dropdown/components/DropdownMenuInnerSelect';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
||||
import { DropdownMenuSectionLabel } from '@/ui/layout/dropdown/components/DropdownMenuSectionLabel';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
|
||||
@ -32,54 +33,12 @@ import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states
|
||||
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { Trans, useLingui } from '@lingui/react/macro';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { IconChevronDown, useIcons } from 'twenty-ui/display';
|
||||
import { IconX, useIcons } from 'twenty-ui/display';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
export const StyledInput = styled.input`
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-top: none;
|
||||
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
border-radius: 0;
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
margin: 0;
|
||||
outline: none;
|
||||
padding: ${({ theme }) => theme.spacing(2)};
|
||||
height: 19px;
|
||||
font-family: inherit;
|
||||
font-size: ${({ theme }) => theme.font.size.sm};
|
||||
|
||||
font-weight: inherit;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-decoration: none;
|
||||
|
||||
&::placeholder {
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledSelectedSortDirectionContainer = styled.div`
|
||||
background: ${({ theme }) => theme.background.secondary};
|
||||
box-shadow: ${({ theme }) => theme.boxShadow.light};
|
||||
border-radius: ${({ theme }) => theme.border.radius.md};
|
||||
|
||||
position: absolute;
|
||||
top: 32px;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
`;
|
||||
|
||||
const StyledDropdownMenuHeaderEndComponent = styled.div`
|
||||
padding: ${({ theme }) => theme.spacing(1)};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
export const ObjectSortDropdownButton = () => {
|
||||
const { resetRecordSortDropdownSearchInput } =
|
||||
useResetRecordSortDropdownSearchInput();
|
||||
@ -88,10 +47,6 @@ export const ObjectSortDropdownButton = () => {
|
||||
objectSortDropdownSearchInputComponentState,
|
||||
);
|
||||
|
||||
const isRecordSortDirectionMenuUnfolded = useRecoilComponentValueV2(
|
||||
isRecordSortDirectionDropdownMenuUnfoldedComponentState,
|
||||
);
|
||||
|
||||
const { resetSortDropdown } = useResetSortDropdown();
|
||||
|
||||
const { recordIndexId, objectMetadataItem } = useRecordIndexContextOrThrow();
|
||||
@ -195,8 +150,6 @@ export const ObjectSortDropdownButton = () => {
|
||||
|
||||
const { t } = useLingui();
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const selectableItemIdArray = [
|
||||
...visibleFieldMetadataItems.map((item) => item.id),
|
||||
...hiddenFieldMetadataItems.map((item) => item.id),
|
||||
@ -227,51 +180,50 @@ export const ObjectSortDropdownButton = () => {
|
||||
}
|
||||
dropdownComponents={
|
||||
<DropdownContent widthInPixels={GenericDropdownContentWidth.ExtraLarge}>
|
||||
<DropdownMenuHeader
|
||||
StartComponent={
|
||||
<DropdownMenuHeaderLeftComponent
|
||||
onClick={() => closeSortDropdown()}
|
||||
Icon={IconX}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{t`Sort`}
|
||||
</DropdownMenuHeader>
|
||||
<DropdownMenuInnerSelect
|
||||
dropdownId="record-sort-direction-dropdown"
|
||||
options={RECORD_SORT_DIRECTIONS.map((sortDirection) => ({
|
||||
value: sortDirection,
|
||||
label: sortDirection === 'asc' ? t`Ascending` : t`Descending`,
|
||||
}))}
|
||||
selectedOption={{
|
||||
value: selectedRecordSortDirection,
|
||||
label:
|
||||
selectedRecordSortDirection === 'asc'
|
||||
? t`Ascending`
|
||||
: t`Descending`,
|
||||
}}
|
||||
onChange={(sortDirection) =>
|
||||
handleSortDirectionClick(
|
||||
sortDirection.value as RecordSortDirection,
|
||||
)
|
||||
}
|
||||
widthInPixels={GenericDropdownContentWidth.ExtraLarge}
|
||||
/>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuSearchInput
|
||||
autoFocus
|
||||
value={objectSortDropdownSearchInput}
|
||||
placeholder={t`Search fields`}
|
||||
onChange={(event) =>
|
||||
setObjectSortDropdownSearchInput(event.target.value)
|
||||
}
|
||||
/>
|
||||
<SelectableList
|
||||
selectableListInstanceId={OBJECT_SORT_DROPDOWN_ID}
|
||||
selectableItemIdArray={selectableItemIdArray}
|
||||
focusId={OBJECT_SORT_DROPDOWN_ID}
|
||||
>
|
||||
{isRecordSortDirectionMenuUnfolded && (
|
||||
<StyledSelectedSortDirectionContainer>
|
||||
<DropdownMenuItemsContainer>
|
||||
{RECORD_SORT_DIRECTIONS.map((sortDirection, index) => (
|
||||
<MenuItem
|
||||
key={index}
|
||||
focused={selectedItemId === sortDirection}
|
||||
onClick={() => handleSortDirectionClick(sortDirection)}
|
||||
text={
|
||||
sortDirection === 'asc' ? t`Ascending` : t`Descending`
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
</StyledSelectedSortDirectionContainer>
|
||||
)}
|
||||
<DropdownMenuHeader
|
||||
onClick={() =>
|
||||
setIsRecordSortDirectionMenuUnfolded(
|
||||
!isRecordSortDirectionMenuUnfolded,
|
||||
)
|
||||
}
|
||||
EndComponent={
|
||||
<StyledDropdownMenuHeaderEndComponent>
|
||||
<IconChevronDown size={theme.icon.size.md} />
|
||||
</StyledDropdownMenuHeaderEndComponent>
|
||||
}
|
||||
>
|
||||
{selectedRecordSortDirection === 'asc'
|
||||
? t`Ascending`
|
||||
: t`Descending`}
|
||||
</DropdownMenuHeader>
|
||||
<StyledInput
|
||||
autoFocus
|
||||
value={objectSortDropdownSearchInput}
|
||||
placeholder={t`Search fields`}
|
||||
onChange={(event) =>
|
||||
setObjectSortDropdownSearchInput(event.target.value)
|
||||
}
|
||||
/>
|
||||
{shouldShowVisibleFields && (
|
||||
<>
|
||||
<DropdownMenuSectionLabel label={t`Visible fields`} />
|
||||
|
||||
Reference in New Issue
Block a user