From 539dc9506d1d9c9dc503849d1e52fd90715eca00 Mon Sep 17 00:00:00 2001 From: Nabhag Motivaras <65061890+Nabhag8848@users.noreply.github.com> Date: Thu, 10 Oct 2024 20:35:22 +0530 Subject: [PATCH] fix: filter and sort options to match order of table columns (#7392) ### ISSUE - Closes #5960 ### Demo https://github.com/user-attachments/assets/279b19cf-6841-4a63-82ed-423bc0eb4395 --------- Co-authored-by: Lucas Bordeau --- .../MultipleFiltersDropdownContent.tsx | 130 +++--------- .../components/ObjectFilterDropdownButton.tsx | 19 +- .../ObjectFilterDropdownFilterInput.tsx | 132 ++++++++++++ .../ObjectFilterDropdownFilterSelect.tsx | 193 ++++++++---------- ...pdownFilterSelectCompositeFieldSubMenu.tsx | 94 ++++++--- ...jectFilterDropdownFilterSelectMenuItem.tsx | 70 +++++++ .../MultipleFiltersDropdownButton.stories.tsx | 66 +++++- .../hooks/useFilterDropdown.ts | 22 ++ ...tFilterDropdownComponentInstanceContext.ts | 4 + ...rDropdownFilterIsSelectedComponentState.ts | 9 + ...irstLevelFilterDefinitionComponentState.ts | 10 + ...IsSelectingCompositeFieldComponentState.ts | 9 + ...rDropdownSubMenuFieldTypeComponentState.ts | 10 + .../components/ObjectSortDropdownButton.tsx | 82 ++++++-- 14 files changed, 573 insertions(+), 277 deletions(-) create mode 100644 packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterInput.tsx create mode 100644 packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext.ts create mode 100644 packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState.ts create mode 100644 packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/objectFilterDropdownFirstLevelFilterDefinitionComponentState.ts create mode 100644 packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState.ts create mode 100644 packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/objectFilterDropdownSubMenuFieldTypeComponentState.ts diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/MultipleFiltersDropdownContent.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/MultipleFiltersDropdownContent.tsx index 81ee172b3545..1e019b6c77e5 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/MultipleFiltersDropdownContent.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/MultipleFiltersDropdownContent.tsx @@ -1,38 +1,19 @@ -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 { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; -import { ObjectFilterDropdownRecordSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordSelect'; -import { ObjectFilterDropdownSourceSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownSourceSelect'; -import { ObjectFilterDropdownTextSearchInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownTextSearchInput'; -import { isActorSourceCompositeFilter } from '@/object-record/object-filter-dropdown/utils/isActorSourceCompositeFilter'; -import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; +import { ObjectFilterDropdownFilterInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterInput'; +import { ObjectFilterDropdownFilterSelectCompositeFieldSubMenu } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectCompositeFieldSubMenu'; +import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState'; +import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState'; +import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; import styled from '@emotion/styled'; import { useRecoilValue } from 'recoil'; import { MultipleFiltersDropdownFilterOnFilterChangedEffect } from './MultipleFiltersDropdownFilterOnFilterChangedEffect'; -import { ObjectFilterDropdownDateInput } from './ObjectFilterDropdownDateInput'; import { ObjectFilterDropdownFilterSelect } from './ObjectFilterDropdownFilterSelect'; -import { ObjectFilterDropdownNumberInput } from './ObjectFilterDropdownNumberInput'; -import { ObjectFilterDropdownOperandButton } from './ObjectFilterDropdownOperandButton'; -import { ObjectFilterDropdownOperandSelect } from './ObjectFilterDropdownOperandSelect'; -import { ObjectFilterDropdownOptionSelect } from './ObjectFilterDropdownOptionSelect'; 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; }; @@ -40,99 +21,38 @@ type MultipleFiltersDropdownContentProps = { export const MultipleFiltersDropdownContent = ({ filterDropdownId, }: MultipleFiltersDropdownContentProps) => { - const { - filterDefinitionUsedInDropdownState, - selectedOperandInDropdownState, - isObjectFilterDropdownOperandSelectUnfoldedState, - } = useFilterDropdown({ filterDropdownId }); + const { filterDefinitionUsedInDropdownState } = useFilterDropdown({ + filterDropdownId, + }); + + const [objectFilterDropdownIsSelectingCompositeField] = + useRecoilComponentStateV2( + objectFilterDropdownIsSelectingCompositeFieldComponentState, + filterDropdownId, + ); - const isObjectFilterDropdownOperandSelectUnfolded = useRecoilValue( - isObjectFilterDropdownOperandSelectUnfoldedState, + const [objectFilterDropdownFilterIsSelected] = useRecoilComponentStateV2( + objectFilterDropdownFilterIsSelectedComponentState, + filterDropdownId, ); const filterDefinitionUsedInDropdown = useRecoilValue( filterDefinitionUsedInDropdownState, ); - const selectedOperandInDropdown = useRecoilValue( - selectedOperandInDropdownState, - ); + const shouldShowCompositeSelectionSubMenu = + objectFilterDropdownIsSelectingCompositeField; - const isConfigurable = - selectedOperandInDropdown && - [ - ViewFilterOperand.Is, - ViewFilterOperand.IsNotNull, - ViewFilterOperand.IsNot, - ViewFilterOperand.LessThan, - ViewFilterOperand.GreaterThan, - ViewFilterOperand.IsBefore, - ViewFilterOperand.IsAfter, - ViewFilterOperand.Contains, - ViewFilterOperand.DoesNotContain, - ViewFilterOperand.IsRelative, - ].includes(selectedOperandInDropdown); + const shoudShowFilterInput = objectFilterDropdownFilterIsSelected; return ( - {!filterDefinitionUsedInDropdown ? ( - + {shoudShowFilterInput ? ( + + ) : shouldShowCompositeSelectionSubMenu ? ( + ) : ( - <> - - {isObjectFilterDropdownOperandSelectUnfolded && ( - - - - )} - {isConfigurable && selectedOperandInDropdown && ( - <> - {[ - 'TEXT', - 'EMAIL', - 'EMAILS', - 'PHONE', - 'FULL_NAME', - 'LINK', - 'LINKS', - 'ADDRESS', - 'ACTOR', - 'ARRAY', - 'PHONES', - ].includes(filterDefinitionUsedInDropdown.type) && - !isActorSourceCompositeFilter( - filterDefinitionUsedInDropdown, - ) && } - {['NUMBER', 'CURRENCY'].includes( - filterDefinitionUsedInDropdown.type, - ) && } - {filterDefinitionUsedInDropdown.type === 'RATING' && ( - - )} - {['DATE_TIME', 'DATE'].includes( - filterDefinitionUsedInDropdown.type, - ) && } - {filterDefinitionUsedInDropdown.type === 'RELATION' && ( - <> - - - - )} - {isActorSourceCompositeFilter(filterDefinitionUsedInDropdown) && ( - <> - - - - )} - {filterDefinitionUsedInDropdown.type === 'SELECT' && ( - <> - - - - )} - - )} - + )} - {hasOnlyOneEntityFilter ? ( - - ) : ( - - )} - + + + {hasOnlyOneEntityFilter ? ( + + ) : ( + + )} + + ); }; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterInput.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterInput.tsx new file mode 100644 index 000000000000..a630286ffadb --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterInput.tsx @@ -0,0 +1,132 @@ +import { useRecoilValue } from 'recoil'; + +import { ObjectFilterDropdownDateInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownDateInput'; +import { ObjectFilterDropdownNumberInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownNumberInput'; +import { ObjectFilterDropdownOperandButton } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownOperandButton'; +import { ObjectFilterDropdownOperandSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownOperandSelect'; +import { ObjectFilterDropdownOptionSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownOptionSelect'; +import { ObjectFilterDropdownRatingInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownRatingInput'; +import { ObjectFilterDropdownRecordSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordSelect'; +import { ObjectFilterDropdownSearchInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownSearchInput'; +import { ObjectFilterDropdownSourceSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownSourceSelect'; +import { ObjectFilterDropdownTextSearchInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownTextSearchInput'; +import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown'; +import { isActorSourceCompositeFilter } from '@/object-record/object-filter-dropdown/utils/isActorSourceCompositeFilter'; +import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; +import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; +import styled from '@emotion/styled'; +import { isDefined } from 'twenty-ui'; + +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 ObjectFilterDropdownFilterInputProps = { + filterDropdownId?: string; +}; + +export const ObjectFilterDropdownFilterInput = ({ + filterDropdownId, +}: ObjectFilterDropdownFilterInputProps) => { + const { + filterDefinitionUsedInDropdownState, + selectedOperandInDropdownState, + isObjectFilterDropdownOperandSelectUnfoldedState, + } = useFilterDropdown({ filterDropdownId }); + + const isObjectFilterDropdownOperandSelectUnfolded = useRecoilValue( + isObjectFilterDropdownOperandSelectUnfoldedState, + ); + + const filterDefinitionUsedInDropdown = useRecoilValue( + filterDefinitionUsedInDropdownState, + ); + + const selectedOperandInDropdown = useRecoilValue( + selectedOperandInDropdownState, + ); + + const isConfigurable = + selectedOperandInDropdown && + [ + ViewFilterOperand.Is, + ViewFilterOperand.IsNotNull, + ViewFilterOperand.IsNot, + ViewFilterOperand.LessThan, + ViewFilterOperand.GreaterThan, + ViewFilterOperand.IsBefore, + ViewFilterOperand.IsAfter, + ViewFilterOperand.Contains, + ViewFilterOperand.DoesNotContain, + ViewFilterOperand.IsRelative, + ].includes(selectedOperandInDropdown); + + if (!isDefined(filterDefinitionUsedInDropdown)) { + return null; + } + + return ( + <> + + {isObjectFilterDropdownOperandSelectUnfolded && ( + + + + )} + {isConfigurable && selectedOperandInDropdown && ( + <> + {[ + 'TEXT', + 'EMAIL', + 'EMAILS', + 'PHONE', + 'FULL_NAME', + 'LINK', + 'LINKS', + 'ADDRESS', + 'ACTOR', + 'ARRAY', + 'PHONES', + ].includes(filterDefinitionUsedInDropdown.type) && + !isActorSourceCompositeFilter(filterDefinitionUsedInDropdown) && ( + + )} + {['NUMBER', 'CURRENCY'].includes( + filterDefinitionUsedInDropdown.type, + ) && } + {filterDefinitionUsedInDropdown.type === 'RATING' && ( + + )} + {['DATE_TIME', 'DATE'].includes( + filterDefinitionUsedInDropdown.type, + ) && } + {filterDefinitionUsedInDropdown.type === 'RELATION' && ( + <> + + + + )} + {isActorSourceCompositeFilter(filterDefinitionUsedInDropdown) && ( + <> + + + + )} + {filterDefinitionUsedInDropdown.type === 'SELECT' && ( + <> + + + + )} + + )} + + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx index 5eb45abae62b..9c815948dc28 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx @@ -1,27 +1,23 @@ import styled from '@emotion/styled'; -import { useState } from 'react'; +import { useContext } from 'react'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; -import { ObjectFilterDropdownFilterSelectCompositeFieldSubMenu } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectCompositeFieldSubMenu'; +import { ObjectFilterDropdownFilterSelectMenuItem } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItem'; import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdown/constants/ObjectFilterDropdownId'; import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown'; import { useSelectFilter } from '@/object-record/object-filter-dropdown/hooks/useSelectFilter'; -import { CompositeFilterableFieldType } from '@/object-record/object-filter-dropdown/types/CompositeFilterableFieldType'; -import { FilterDefinition } from '@/object-record/object-filter-dropdown/types/FilterDefinition'; import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope'; -import { isCompositeField } from '@/object-record/object-filter-dropdown/utils/isCompositeField'; -import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope'; +import { RecordIndexRootPropsContext } from '@/object-record/record-index/contexts/RecordIndexRootPropsContext'; +import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates'; +import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem'; import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; -import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState'; import { useRecoilValue } from 'recoil'; -import { isDefined, useIcons } from 'twenty-ui'; -import { getOperandsForFilterDefinition } from '../utils/getOperandsForFilterType'; +import { isDefined } from 'twenty-ui'; export const StyledInput = styled.input` background: transparent; @@ -50,15 +46,7 @@ export const StyledInput = styled.input` `; export const ObjectFilterDropdownFilterSelect = () => { - const [subMenuFieldType, setSubMenuFieldType] = - useState(null); - - const [firstLevelFilterDefinition, setFirstLevelFilterDefinition] = - useState(null); - const { - setFilterDefinitionUsedInDropdown, - setSelectedOperandInDropdown, setObjectFilterDropdownSearchInput, objectFilterDropdownSearchInputState, } = useFilterDropdown(); @@ -70,16 +58,41 @@ export const ObjectFilterDropdownFilterSelect = () => { const availableFilterDefinitions = useRecoilComponentValueV2( availableFilterDefinitionsComponentState, ); + const { recordIndexId } = useContext(RecordIndexRootPropsContext); + const { hiddenTableColumnsSelector, visibleTableColumnsSelector } = + useRecordTableStates(recordIndexId); - const sortedAvailableFilterDefinitions = [...availableFilterDefinitions] - .sort((a, b) => a.label.localeCompare(b.label)) - .filter((item) => + const visibleTableColumns = useRecoilValue(visibleTableColumnsSelector()); + const visibleColumnsIds = visibleTableColumns.map( + (column) => column.fieldMetadataId, + ); + const hiddenTableColumns = useRecoilValue(hiddenTableColumnsSelector()); + const hiddenColumnIds = hiddenTableColumns.map( + (column) => column.fieldMetadataId, + ); + + const filteredSearchInputFilterDefinitions = + availableFilterDefinitions.filter((item) => item.label .toLocaleLowerCase() .includes(objectFilterDropdownSearchInput.toLocaleLowerCase()), ); - const selectableListItemIds = sortedAvailableFilterDefinitions.map( + const visibleColumnsFilterDefinitions = filteredSearchInputFilterDefinitions + + .sort((a, b) => { + return ( + visibleColumnsIds.indexOf(a.fieldMetadataId) - + visibleColumnsIds.indexOf(b.fieldMetadataId) + ); + }) + .filter((item) => visibleColumnsIds.includes(item.fieldMetadataId)); + + const hiddenColumnsFilterDefinitions = filteredSearchInputFilterDefinitions + .sort((a, b) => a.label.localeCompare(b.label)) + .filter((item) => hiddenColumnIds.includes(item.fieldMetadataId)); + + const selectableListItemIds = availableFilterDefinitions.map( (item) => item.fieldMetadataId, ); @@ -88,7 +101,7 @@ export const ObjectFilterDropdownFilterSelect = () => { const { resetSelectedItem } = useSelectableList(OBJECT_FILTER_DROPDOWN_ID); const handleEnter = (itemId: string) => { - const selectedFilterDefinition = sortedAvailableFilterDefinitions.find( + const selectedFilterDefinition = availableFilterDefinitions.find( (item) => item.fieldMetadataId === itemId, ); @@ -101,96 +114,56 @@ export const ObjectFilterDropdownFilterSelect = () => { selectFilter({ filterDefinition: selectedFilterDefinition }); }; - const setHotkeyScope = useSetHotkeyScope(); - const { getIcon } = useIcons(); - - const handleSelectFilter = (availableFilterDefinition: FilterDefinition) => { - setFilterDefinitionUsedInDropdown(availableFilterDefinition); - - if ( - availableFilterDefinition.type === 'RELATION' || - availableFilterDefinition.type === 'SELECT' - ) { - setHotkeyScope(RelationPickerHotkeyScope.RelationPicker); - } - - setSelectedOperandInDropdown( - getOperandsForFilterDefinition(availableFilterDefinition)[0], - ); - - setObjectFilterDropdownSearchInput(''); - }; - - const handleSubMenuBack = () => { - setSubMenuFieldType(null); - setFirstLevelFilterDefinition(null); - }; - - const shouldShowFirstLevelMenu = !isDefined(subMenuFieldType); + const shoudShowSeparator = + visibleColumnsFilterDefinitions.length > 0 && + hiddenColumnsFilterDefinitions.length > 0; return ( <> - {shouldShowFirstLevelMenu ? ( - <> - ) => - setObjectFilterDropdownSearchInput(event.target.value) - } - /> - - - {[...availableFilterDefinitions] - .sort((a, b) => a.label.localeCompare(b.label)) - .filter((item) => - item.label - .toLocaleLowerCase() - .includes( - objectFilterDropdownSearchInput.toLocaleLowerCase(), - ), - ) - .map((availableFilterDefinition, index) => ( - - { - if (isCompositeField(availableFilterDefinition.type)) { - setSubMenuFieldType(availableFilterDefinition.type); - setFirstLevelFilterDefinition( - availableFilterDefinition, - ); - } else { - handleSelectFilter(availableFilterDefinition); - } - }} - LeftIcon={getIcon(availableFilterDefinition.iconName)} - text={availableFilterDefinition.label} - hasSubMenu={isCompositeField( - availableFilterDefinition.type, - )} - /> - - ))} - - - - ) : ( - - )} + ) => + setObjectFilterDropdownSearchInput(event.target.value) + } + /> + + + {visibleColumnsFilterDefinitions.map( + (visibleFilterDefinition, index) => ( + + + + ), + )} + + {shoudShowSeparator && } + + {hiddenColumnsFilterDefinitions.map( + (hiddenFilterDefinition, index) => ( + + + + ), + )} + + ); }; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectCompositeFieldSubMenu.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectCompositeFieldSubMenu.tsx index a84046570924..84442b51a812 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectCompositeFieldSubMenu.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectCompositeFieldSubMenu.tsx @@ -1,6 +1,8 @@ import { StyledInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect'; import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown'; -import { CompositeFilterableFieldType } from '@/object-record/object-filter-dropdown/types/CompositeFilterableFieldType'; +import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState'; +import { objectFilterDropdownFirstLevelFilterDefinitionComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFirstLevelFilterDefinitionComponentState'; +import { objectFilterDropdownSubMenuFieldTypeComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSubMenuFieldTypeComponentState'; import { FilterDefinition } from '@/object-record/object-filter-dropdown/types/FilterDefinition'; import { getCompositeSubFieldLabel } from '@/object-record/object-filter-dropdown/utils/getCompositeSubFieldLabel'; import { getFilterableFieldTypeLabel } from '@/object-record/object-filter-dropdown/utils/getFilterableFieldTypeLabel'; @@ -9,24 +11,33 @@ import { SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS } from '@/settings/data-model/con import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; +import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; import { useState } from 'react'; -import { IconApps, IconChevronLeft, useIcons } from 'twenty-ui'; +import { IconApps, IconChevronLeft, isDefined, useIcons } from 'twenty-ui'; -type ObjectFilterDropdownFilterSelectCompositeFieldSubMenuProps = { - fieldType: CompositeFilterableFieldType; - firstLevelFieldDefinition: FilterDefinition | null; - onBack: () => void; -}; - -export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = ({ - fieldType, - firstLevelFieldDefinition, - onBack, -}: ObjectFilterDropdownFilterSelectCompositeFieldSubMenuProps) => { +export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = () => { const [searchText, setSearchText] = useState(''); const { getIcon } = useIcons(); + const [ + objectFilterDropdownFirstLevelFilterDefinition, + setObjectFilterDropdownFirstLevelFilterDefinition, + ] = useRecoilComponentStateV2( + objectFilterDropdownFirstLevelFilterDefinitionComponentState, + ); + + const [, setObjectFilterDropdownFilterIsSelected] = useRecoilComponentStateV2( + objectFilterDropdownFilterIsSelectedComponentState, + ); + + const [ + objectFilterDropdownSubMenuFieldType, + setObjectFilterDropdownSubMenuFieldType, + ] = useRecoilComponentStateV2( + objectFilterDropdownSubMenuFieldTypeComponentState, + ); + const { setFilterDefinitionUsedInDropdown, setSelectedOperandInDropdown, @@ -42,11 +53,26 @@ export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = ({ ); setObjectFilterDropdownSearchInput(''); + + setObjectFilterDropdownFilterIsSelected(true); } }; + const handleSubMenuBack = () => { + setFilterDefinitionUsedInDropdown(null); + setObjectFilterDropdownSubMenuFieldType(null); + setObjectFilterDropdownFirstLevelFilterDefinition(null); + }; + + if ( + !isDefined(objectFilterDropdownSubMenuFieldType) || + !isDefined(objectFilterDropdownFirstLevelFilterDefinition) + ) { + return null; + } + const options = SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS[ - fieldType + objectFilterDropdownSubMenuFieldType ].filterableSubFields .sort((a, b) => a.localeCompare(b)) .filter((item) => @@ -55,8 +81,11 @@ export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = ({ return ( <> - - {getFilterableFieldTypeLabel(fieldType)} + + {getFilterableFieldTypeLabel(objectFilterDropdownSubMenuFieldType)} { - handleSelectFilter(firstLevelFieldDefinition); + handleSelectFilter(objectFilterDropdownFirstLevelFilterDefinition); }} LeftIcon={IconApps} - text={`Any ${getFilterableFieldTypeLabel(fieldType)} field`} + text={`Any ${getFilterableFieldTypeLabel(objectFilterDropdownSubMenuFieldType)} field`} /> {options.map((subFieldName, index) => ( - firstLevelFieldDefinition && - handleSelectFilter({ - ...firstLevelFieldDefinition, - label: getCompositeSubFieldLabel(fieldType, subFieldName), - compositeFieldName: subFieldName, - }) - } - text={getCompositeSubFieldLabel(fieldType, subFieldName)} - LeftIcon={getIcon(firstLevelFieldDefinition?.iconName)} + onClick={() => { + if (isDefined(objectFilterDropdownFirstLevelFilterDefinition)) { + handleSelectFilter({ + ...objectFilterDropdownFirstLevelFilterDefinition, + label: getCompositeSubFieldLabel( + objectFilterDropdownSubMenuFieldType, + subFieldName, + ), + compositeFieldName: subFieldName, + }); + } + }} + text={getCompositeSubFieldLabel( + objectFilterDropdownSubMenuFieldType, + subFieldName, + )} + LeftIcon={getIcon( + objectFilterDropdownFirstLevelFilterDefinition?.iconName, + )} /> ))} diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItem.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItem.tsx index a1a12802fd1c..7d94eaaf0e35 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItem.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItem.tsx @@ -1,9 +1,20 @@ import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdown/constants/ObjectFilterDropdownId'; +import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown'; import { useSelectFilter } from '@/object-record/object-filter-dropdown/hooks/useSelectFilter'; +import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState'; +import { objectFilterDropdownFirstLevelFilterDefinitionComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFirstLevelFilterDefinitionComponentState'; +import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState'; +import { objectFilterDropdownSubMenuFieldTypeComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSubMenuFieldTypeComponentState'; +import { CompositeFilterableFieldType } from '@/object-record/object-filter-dropdown/types/CompositeFilterableFieldType'; import { FilterDefinition } from '@/object-record/object-filter-dropdown/types/FilterDefinition'; +import { getOperandsForFilterDefinition } from '@/object-record/object-filter-dropdown/utils/getOperandsForFilterType'; +import { isCompositeField } from '@/object-record/object-filter-dropdown/utils/isCompositeField'; +import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; import { MenuItemSelect } from '@/ui/navigation/menu-item/components/MenuItemSelect'; +import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; +import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; import { useRecoilValue } from 'recoil'; import { useIcons } from 'twenty-ui'; @@ -16,6 +27,24 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({ }: ObjectFilterDropdownFilterSelectMenuItemProps) => { const { selectFilter } = useSelectFilter(); + const [, setObjectFilterDropdownFirstLevelFilterDefinition] = + useRecoilComponentStateV2( + objectFilterDropdownFirstLevelFilterDefinitionComponentState, + ); + + const [, setObjectFilterDropdownSubMenuFieldType] = useRecoilComponentStateV2( + objectFilterDropdownSubMenuFieldTypeComponentState, + ); + + const [, setObjectFilterDropdownIsSelectingCompositeField] = + useRecoilComponentStateV2( + objectFilterDropdownIsSelectingCompositeFieldComponentState, + ); + + const [, setObjectFilterDropdownFilterIsSelected] = useRecoilComponentStateV2( + objectFilterDropdownFilterIsSelectedComponentState, + ); + const { isSelectedItemIdSelector, resetSelectedItem } = useSelectableList( OBJECT_FILTER_DROPDOWN_ID, ); @@ -24,12 +53,52 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({ isSelectedItemIdSelector(filterDefinition.fieldMetadataId), ); + const isACompositeField = isCompositeField(filterDefinition.type); + + const { + setFilterDefinitionUsedInDropdown, + setSelectedOperandInDropdown, + setObjectFilterDropdownSearchInput, + } = useFilterDropdown(); + + const setHotkeyScope = useSetHotkeyScope(); + + const handleSelectFilter = (availableFilterDefinition: FilterDefinition) => { + setFilterDefinitionUsedInDropdown(availableFilterDefinition); + + if ( + availableFilterDefinition.type === 'RELATION' || + availableFilterDefinition.type === 'SELECT' + ) { + setHotkeyScope(RelationPickerHotkeyScope.RelationPicker); + } + + setSelectedOperandInDropdown( + getOperandsForFilterDefinition(availableFilterDefinition)[0], + ); + + setObjectFilterDropdownSearchInput(''); + + setObjectFilterDropdownFilterIsSelected(true); + }; + const { getIcon } = useIcons(); const handleClick = () => { resetSelectedItem(); selectFilter({ filterDefinition }); + + if (isACompositeField) { + // TODO: create isCompositeFilterableFieldType type guard + setObjectFilterDropdownSubMenuFieldType( + filterDefinition.type as CompositeFilterableFieldType, + ); + setObjectFilterDropdownFirstLevelFilterDefinition(filterDefinition); + setObjectFilterDropdownIsSelectingCompositeField(true); + } else { + handleSelectFilter(filterDefinition); + } }; return ( @@ -39,6 +108,7 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({ onClick={handleClick} LeftIcon={getIcon(filterDefinition.iconName)} text={filterDefinition.label} + hasSubMenu={isACompositeField} /> ); }; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/__stories__/MultipleFiltersDropdownButton.stories.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/__stories__/MultipleFiltersDropdownButton.stories.tsx index 2c43f6a5aa5e..df9044a82f21 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/__stories__/MultipleFiltersDropdownButton.stories.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/__stories__/MultipleFiltersDropdownButton.stories.tsx @@ -3,10 +3,15 @@ import { Meta, StoryObj } from '@storybook/react'; import { TaskGroups } from '@/activities/tasks/components/TaskGroups'; import { MultipleFiltersDropdownButton } from '@/object-record/object-filter-dropdown/components/MultipleFiltersDropdownButton'; import { ObjectFilterDropdownScope } from '@/object-record/object-filter-dropdown/scopes/ObjectFilterDropdownScope'; +import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext'; +import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates'; +import { RecordTableScopeInternalContext } from '@/object-record/record-table/scopes/scope-internal-context/RecordTableScopeInternalContext'; +import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState'; import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; import { within } from '@storybook/test'; +import { useSetRecoilState } from 'recoil'; import { ComponentDecorator } from 'twenty-ui'; import { FieldMetadataType } from '~/generated/graphql'; import { IconsProviderDecorator } from '~/testing/decorators/IconsProviderDecorator'; @@ -25,18 +30,49 @@ const meta: Meta = { instanceId, ); - setAvailableFilterDefinitions([ + const { tableColumnsState } = useRecordTableStates(instanceId); + + const setTableColumns = useSetRecoilState(tableColumnsState); + + setTableColumns([ { fieldMetadataId: '1', iconName: 'IconUser', label: 'Text', type: FieldMetadataType.Text, - }, + isVisible: true, + metadata: { + fieldName: 'text', + }, + } as ColumnDefinition, + { + fieldMetadataId: '3', + iconName: 'IconNumber', + label: 'Number', + type: FieldMetadataType.Number, + isVisible: true, + metadata: { + fieldName: 'number', + }, + } as ColumnDefinition, + { + fieldMetadataId: '4', + iconName: 'IconCalendar', + label: 'Date', + type: FieldMetadataType.DateTime, + isVisible: true, + metadata: { + fieldName: 'date', + }, + } as ColumnDefinition, + ]); + + setAvailableFilterDefinitions([ { - fieldMetadataId: '2', - iconName: 'Icon123', - label: 'Email', - type: FieldMetadataType.Emails, + fieldMetadataId: '1', + iconName: 'IconUser', + label: 'Text', + type: FieldMetadataType.Text, }, { fieldMetadataId: '3', @@ -52,11 +88,19 @@ const meta: Meta = { }, ]); return ( - - - - - + + {} }} + > + + + + + + + ); }, ObjectMetadataItemsDecorator, diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useFilterDropdown.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useFilterDropdown.ts index b09d33a38396..c8a801336647 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useFilterDropdown.ts +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useFilterDropdown.ts @@ -4,6 +4,9 @@ import { useFilterDropdownStates } from '@/object-record/object-filter-dropdown/ import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; +import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState'; +import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState'; +import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { ObjectFilterDropdownScopeInternalContext } from '../scopes/scope-internal-context/ObjectFilterDropdownScopeInternalContext'; import { Filter } from '../types/Filter'; @@ -54,6 +57,18 @@ export const useFilterDropdown = (props?: UseFilterDropdownProps) => { ], ); + const setObjectFilterDropdownFilterIsSelectedCallbackState = + useRecoilComponentCallbackStateV2( + objectFilterDropdownFilterIsSelectedComponentState, + props?.filterDropdownId, + ); + + const setObjectFilterDropdownIsSelectingCompositeFieldCallbackState = + useRecoilComponentCallbackStateV2( + objectFilterDropdownIsSelectingCompositeFieldComponentState, + props?.filterDropdownId, + ); + const resetFilter = useRecoilCallback( ({ set }) => () => { @@ -62,6 +77,11 @@ export const useFilterDropdown = (props?: UseFilterDropdownProps) => { set(selectedFilterState, undefined); set(filterDefinitionUsedInDropdownState, null); set(selectedOperandInDropdownState, null); + set(setObjectFilterDropdownFilterIsSelectedCallbackState, false); + set( + setObjectFilterDropdownIsSelectingCompositeFieldCallbackState, + false, + ); }, [ filterDefinitionUsedInDropdownState, @@ -69,6 +89,8 @@ export const useFilterDropdown = (props?: UseFilterDropdownProps) => { objectFilterDropdownSelectedRecordIdsState, selectedFilterState, selectedOperandInDropdownState, + setObjectFilterDropdownFilterIsSelectedCallbackState, + setObjectFilterDropdownIsSelectingCompositeFieldCallbackState, ], ); diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext.ts new file mode 100644 index 000000000000..44305191b8d9 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext.ts @@ -0,0 +1,4 @@ +import { createComponentInstanceContext } from '@/ui/utilities/state/component-state/utils/createComponentInstanceContext'; + +export const ObjectFilterDropdownComponentInstanceContext = + createComponentInstanceContext(); diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState.ts new file mode 100644 index 000000000000..051f5b9e7b69 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState.ts @@ -0,0 +1,9 @@ +import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext'; +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; + +export const objectFilterDropdownFilterIsSelectedComponentState = + createComponentStateV2({ + key: 'objectFilterDropdownFilterIsSelectedComponentState', + defaultValue: false, + componentInstanceContext: ObjectFilterDropdownComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/objectFilterDropdownFirstLevelFilterDefinitionComponentState.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/objectFilterDropdownFirstLevelFilterDefinitionComponentState.ts new file mode 100644 index 000000000000..b686bb1bbd03 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/objectFilterDropdownFirstLevelFilterDefinitionComponentState.ts @@ -0,0 +1,10 @@ +import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext'; +import { FilterDefinition } from '@/object-record/object-filter-dropdown/types/FilterDefinition'; +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; + +export const objectFilterDropdownFirstLevelFilterDefinitionComponentState = + createComponentStateV2({ + key: 'objectFilterDropdownFirstLevelFilterDefinitionComponentState', + defaultValue: null, + componentInstanceContext: ObjectFilterDropdownComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState.ts new file mode 100644 index 000000000000..68a141fd88ad --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState.ts @@ -0,0 +1,9 @@ +import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext'; +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; + +export const objectFilterDropdownIsSelectingCompositeFieldComponentState = + createComponentStateV2({ + key: 'objectFilterDropdownIsSelectingCompositeFieldComponentState', + defaultValue: false, + componentInstanceContext: ObjectFilterDropdownComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/objectFilterDropdownSubMenuFieldTypeComponentState.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/objectFilterDropdownSubMenuFieldTypeComponentState.ts new file mode 100644 index 000000000000..f00347b30ea6 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/objectFilterDropdownSubMenuFieldTypeComponentState.ts @@ -0,0 +1,10 @@ +import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext'; +import { CompositeFilterableFieldType } from '@/object-record/object-filter-dropdown/types/CompositeFilterableFieldType'; +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; + +export const objectFilterDropdownSubMenuFieldTypeComponentState = + createComponentStateV2({ + key: 'objectFilterDropdownSubMenuFieldTypeComponentState', + defaultValue: null, + componentInstanceContext: ObjectFilterDropdownComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/components/ObjectSortDropdownButton.tsx b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/components/ObjectSortDropdownButton.tsx index dbbc8d829745..f1e399c1a0b9 100644 --- a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/components/ObjectSortDropdownButton.tsx +++ b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/components/ObjectSortDropdownButton.tsx @@ -5,14 +5,17 @@ import { IconChevronDown, useIcons } from 'twenty-ui'; import { OBJECT_SORT_DROPDOWN_ID } from '@/object-record/object-sort-dropdown/constants/ObjectSortDropdownId'; import { useObjectSortDropdown } from '@/object-record/object-sort-dropdown/hooks/useObjectSortDropdown'; import { ObjectSortDropdownScope } from '@/object-record/object-sort-dropdown/scopes/ObjectSortDropdownScope'; +import { RecordIndexRootPropsContext } from '@/object-record/record-index/contexts/RecordIndexRootPropsContext'; +import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; +import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; - +import { useContext } from 'react'; import { SORT_DIRECTIONS } from '../types/SortDirection'; export const StyledInput = styled.input` @@ -94,6 +97,43 @@ export const ObjectSortDropdownButton = ({ const { getIcon } = useIcons(); + const { recordIndexId } = useContext(RecordIndexRootPropsContext); + const { hiddenTableColumnsSelector, visibleTableColumnsSelector } = + useRecordTableStates(recordIndexId); + + const visibleTableColumns = useRecoilValue(visibleTableColumnsSelector()); + const visibleColumnsIds = visibleTableColumns.map( + (column) => column.fieldMetadataId, + ); + const hiddenTableColumns = useRecoilValue(hiddenTableColumnsSelector()); + const hiddenColumnIds = hiddenTableColumns.map( + (column) => column.fieldMetadataId, + ); + + const filteredSearchInputSortDefinitions = availableSortDefinitions.filter( + (item) => + item.label + .toLocaleLowerCase() + .includes(objectSortDropdownSearchInput.toLocaleLowerCase()), + ); + + const visibleColumnsSortDefinitions = filteredSearchInputSortDefinitions + .sort((a, b) => { + return ( + visibleColumnsIds.indexOf(a.fieldMetadataId) - + visibleColumnsIds.indexOf(b.fieldMetadataId) + ); + }) + .filter((item) => visibleColumnsIds.includes(item.fieldMetadataId)); + + const hiddenColumnsSortDefinitions = filteredSearchInputSortDefinitions + .sort((a, b) => a.label.localeCompare(b.label)) + .filter((item) => hiddenColumnIds.includes(item.fieldMetadataId)); + + const shoudShowSeparator = + visibleColumnsSortDefinitions.length > 0 && + hiddenColumnsSortDefinitions.length > 0; + return ( - {[...availableSortDefinitions] - .sort((a, b) => a.label.localeCompare(b.label)) - .filter((item) => - item.label - .toLocaleLowerCase() - .includes( - objectSortDropdownSearchInput.toLocaleLowerCase(), - ), - ) - .map((availableSortDefinition, index) => ( + {visibleColumnsSortDefinitions.map( + (visibleSortDefinition, index) => ( { setObjectSortDropdownSearchInput(''); - handleAddSort(availableSortDefinition); + handleAddSort(visibleSortDefinition); }} - LeftIcon={getIcon(availableSortDefinition.iconName)} - text={availableSortDefinition.label} + LeftIcon={getIcon(visibleSortDefinition.iconName)} + text={visibleSortDefinition.label} /> - ))} + ), + )} + + {shoudShowSeparator && } + + {hiddenColumnsSortDefinitions.map( + (hiddenSortDefinition, index) => ( + { + setObjectSortDropdownSearchInput(''); + handleAddSort(hiddenSortDefinition); + }} + LeftIcon={getIcon(hiddenSortDefinition.iconName)} + text={hiddenSortDefinition.label} + /> + ), + )}