From b5de2abd4812a5081ebf76fad0e46c621a932b21 Mon Sep 17 00:00:00 2001 From: Emilien Chauvet Date: Tue, 11 Jul 2023 20:42:15 -0700 Subject: [PATCH] Move filter and sort compoenets in a separate lib (#612) * Move filter and sort compoenets in a separate lib * Add SortAndFilterBar to the filter lib * Abstract hotkeys scopes * Fix hotkeys on filters --------- Co-authored-by: Charles Bochet --- .../FilterDropdownCompanySearchSelect.tsx | 7 +- .../components}/DropdownButton.tsx | 7 +- .../components}/FilterDropdownButton.tsx | 71 +++++++++---------- .../FilterDropdownDateSearchInput.tsx | 28 ++++---- .../FilterDropdownEntitySearchInput.tsx | 21 +++--- .../FilterDropdownEntitySearchSelect.tsx | 30 ++++---- .../FilterDropdownEntitySelect.tsx | 20 +++--- .../FilterDropdownFilterSelect.tsx | 38 +++++----- .../FilterDropdownNumberSearchInput.tsx | 31 ++++---- .../FilterDropdownOperandButton.tsx | 13 ++-- .../FilterDropdownOperandSelect.tsx | 42 +++++------ .../FilterDropdownTextSearchInput.tsx | 35 ++++----- .../components}/SortAndFilterBar.tsx | 17 ++--- .../components}/SortDropdownButton.tsx | 4 ++ .../components}/SortOrFilterChip.tsx | 0 .../table/table-header/TableHeader.tsx | 16 +++-- .../FilterDropdownUserSearchSelect.tsx | 7 +- 17 files changed, 213 insertions(+), 174 deletions(-) rename front/src/modules/{ui/components/table/table-header => lib/filters-and-sorts/components}/DropdownButton.tsx (97%) rename front/src/modules/{ui/components/table/table-header => lib/filters-and-sorts/components}/FilterDropdownButton.tsx (65%) rename front/src/modules/{ui/components/table/table-header => lib/filters-and-sorts/components}/FilterDropdownDateSearchInput.tsx (63%) rename front/src/modules/{ui/components/table/table-header => lib/filters-and-sorts/components}/FilterDropdownEntitySearchInput.tsx (68%) rename front/src/modules/{ui/components/table/table-header => lib/filters-and-sorts/components}/FilterDropdownEntitySearchSelect.tsx (75%) rename front/src/modules/{ui/components/table/table-header => lib/filters-and-sorts/components}/FilterDropdownEntitySelect.tsx (52%) rename front/src/modules/{ui/components/table/table-header => lib/filters-and-sorts/components}/FilterDropdownFilterSelect.tsx (69%) rename front/src/modules/{ui/components/table/table-header => lib/filters-and-sorts/components}/FilterDropdownNumberSearchInput.tsx (59%) rename front/src/modules/{ui/components/table/table-header => lib/filters-and-sorts/components}/FilterDropdownOperandButton.tsx (87%) rename front/src/modules/{ui/components/table/table-header => lib/filters-and-sorts/components}/FilterDropdownOperandSelect.tsx (70%) rename front/src/modules/{ui/components/table/table-header => lib/filters-and-sorts/components}/FilterDropdownTextSearchInput.tsx (62%) rename front/src/modules/{ui/components/table/table-header => lib/filters-and-sorts/components}/SortAndFilterBar.tsx (92%) rename front/src/modules/{ui/components/table/table-header => lib/filters-and-sorts/components}/SortDropdownButton.tsx (94%) rename front/src/modules/{ui/components/table/table-header => lib/filters-and-sorts/components}/SortOrFilterChip.tsx (100%) diff --git a/front/src/modules/companies/components/FilterDropdownCompanySearchSelect.tsx b/front/src/modules/companies/components/FilterDropdownCompanySearchSelect.tsx index 4c1706c008..21ee171b8c 100644 --- a/front/src/modules/companies/components/FilterDropdownCompanySearchSelect.tsx +++ b/front/src/modules/companies/components/FilterDropdownCompanySearchSelect.tsx @@ -1,10 +1,10 @@ +import { FilterDropdownEntitySearchSelect } from '@/lib/filters-and-sorts/components/FilterDropdownEntitySearchSelect'; import { filterDropdownSearchInputScopedState } from '@/lib/filters-and-sorts/states/filterDropdownSearchInputScopedState'; import { filterDropdownSelectedEntityIdScopedState } from '@/lib/filters-and-sorts/states/filterDropdownSelectedEntityIdScopedState'; import { useRecoilScopedState } from '@/recoil-scope/hooks/useRecoilScopedState'; import { useRecoilScopedValue } from '@/recoil-scope/hooks/useRecoilScopedValue'; import { useFilteredSearchEntityQuery } from '@/relation-picker/hooks/useFilteredSearchEntityQuery'; import { Entity } from '@/relation-picker/types/EntityTypeForSelect'; -import { FilterDropdownEntitySearchSelect } from '@/ui/components/table/table-header/FilterDropdownEntitySearchSelect'; import { TableContext } from '@/ui/tables/states/TableContext'; import { getLogoUrlFromDomainName } from '@/utils/utils'; import { useSearchCompanyQuery } from '~/generated/graphql'; @@ -38,6 +38,9 @@ export function FilterDropdownCompanySearchSelect() { }); return ( - + ); } diff --git a/front/src/modules/ui/components/table/table-header/DropdownButton.tsx b/front/src/modules/lib/filters-and-sorts/components/DropdownButton.tsx similarity index 97% rename from front/src/modules/ui/components/table/table-header/DropdownButton.tsx rename to front/src/modules/lib/filters-and-sorts/components/DropdownButton.tsx index f7dbe89bc1..9178c49249 100644 --- a/front/src/modules/ui/components/table/table-header/DropdownButton.tsx +++ b/front/src/modules/lib/filters-and-sorts/components/DropdownButton.tsx @@ -4,11 +4,10 @@ import { Key } from 'ts-key-enum'; import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys'; import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope'; +import { useOutsideAlerter } from '@/ui/hooks/useOutsideAlerter'; import { IconChevronDown } from '@/ui/icons/index'; import { overlayBackground, textInputStyle } from '@/ui/themes/effects'; -import { useOutsideAlerter } from '../../../hooks/useOutsideAlerter'; - type OwnProps = { label: string; isActive: boolean; @@ -16,6 +15,7 @@ type OwnProps = { isUnfolded?: boolean; onIsUnfoldedChange?: (newIsUnfolded: boolean) => void; resetState?: () => void; + hotkeysScope: InternalHotkeysScope; }; const StyledDropdownButtonContainer = styled.div` @@ -160,13 +160,14 @@ function DropdownButton({ children, isUnfolded = false, onIsUnfoldedChange, + hotkeysScope, }: OwnProps) { useScopedHotkeys( [Key.Enter, Key.Escape], () => { onIsUnfoldedChange?.(false); }, - InternalHotkeysScope.TableHeaderDropdownButton, + hotkeysScope, [onIsUnfoldedChange], ); diff --git a/front/src/modules/ui/components/table/table-header/FilterDropdownButton.tsx b/front/src/modules/lib/filters-and-sorts/components/FilterDropdownButton.tsx similarity index 65% rename from front/src/modules/ui/components/table/table-header/FilterDropdownButton.tsx rename to front/src/modules/lib/filters-and-sorts/components/FilterDropdownButton.tsx index c212f375bf..3aa02ca7ed 100644 --- a/front/src/modules/ui/components/table/table-header/FilterDropdownButton.tsx +++ b/front/src/modules/lib/filters-and-sorts/components/FilterDropdownButton.tsx @@ -1,4 +1,4 @@ -import { useCallback, useState } from 'react'; +import { Context, useCallback, useState } from 'react'; import { Key } from 'ts-key-enum'; import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys'; @@ -10,7 +10,6 @@ import { filtersScopedState } from '@/lib/filters-and-sorts/states/filtersScoped import { isFilterDropdownOperandSelectUnfoldedScopedState } from '@/lib/filters-and-sorts/states/isFilterDropdownOperandSelectUnfoldedScopedState'; import { selectedOperandInDropdownScopedState } from '@/lib/filters-and-sorts/states/selectedOperandInDropdownScopedState'; import { useRecoilScopedState } from '@/recoil-scope/hooks/useRecoilScopedState'; -import { TableContext } from '@/ui/tables/states/TableContext'; import DropdownButton from './DropdownButton'; import { FilterDropdownDateSearchInput } from './FilterDropdownDateSearchInput'; @@ -22,7 +21,13 @@ import { FilterDropdownOperandButton } from './FilterDropdownOperandButton'; import { FilterDropdownOperandSelect } from './FilterDropdownOperandSelect'; import { FilterDropdownTextSearchInput } from './FilterDropdownTextSearchInput'; -export function FilterDropdownButton() { +export function FilterDropdownButton({ + context, + hotkeysScope, +}: { + context: Context; + hotkeysScope: InternalHotkeysScope; +}) { const [isUnfolded, setIsUnfolded] = useState(false); const [ @@ -30,52 +35,45 @@ export function FilterDropdownButton() { setIsFilterDropdownOperandSelectUnfolded, ] = useRecoilScopedState( isFilterDropdownOperandSelectUnfoldedScopedState, - TableContext, + context, ); - const [ - tableFilterDefinitionUsedInDropdown, - setTableFilterDefinitionUsedInDropdown, - ] = useRecoilScopedState( - filterDefinitionUsedInDropdownScopedState, - TableContext, - ); + const [filterDefinitionUsedInDropdown, setFilterDefinitionUsedInDropdown] = + useRecoilScopedState(filterDefinitionUsedInDropdownScopedState, context); const [, setFilterDropdownSearchInput] = useRecoilScopedState( filterDropdownSearchInputScopedState, - TableContext, + context, ); - const [activeTableFilters] = useRecoilScopedState( - filtersScopedState, - TableContext, - ); + const [filters] = useRecoilScopedState(filtersScopedState, context); const [selectedOperandInDropdown, setSelectedOperandInDropdown] = - useRecoilScopedState(selectedOperandInDropdownScopedState, TableContext); + useRecoilScopedState(selectedOperandInDropdownScopedState, context); const resetState = useCallback(() => { setIsFilterDropdownOperandSelectUnfolded(false); - setTableFilterDefinitionUsedInDropdown(null); + setFilterDefinitionUsedInDropdown(null); setSelectedOperandInDropdown(null); setFilterDropdownSearchInput(''); }, [ - setTableFilterDefinitionUsedInDropdown, + setFilterDefinitionUsedInDropdown, setSelectedOperandInDropdown, setFilterDropdownSearchInput, setIsFilterDropdownOperandSelectUnfolded, ]); - const isFilterSelected = (activeTableFilters?.length ?? 0) > 0; + const isFilterSelected = (filters?.length ?? 0) > 0; const setHotkeysScope = useSetHotkeysScope(); function handleIsUnfoldedChange(newIsUnfolded: boolean) { if (newIsUnfolded) { + setHotkeysScope(hotkeysScope); setIsUnfolded(true); } else { - if (tableFilterDefinitionUsedInDropdown?.type === 'entity') { - setHotkeysScope(InternalHotkeysScope.Table); + if (filterDefinitionUsedInDropdown?.type === 'entity') { + setHotkeysScope(hotkeysScope); } setIsUnfolded(false); resetState(); @@ -97,31 +95,32 @@ export function FilterDropdownButton() { isActive={isFilterSelected} isUnfolded={isUnfolded} onIsUnfoldedChange={handleIsUnfoldedChange} + hotkeysScope={hotkeysScope} > - {!tableFilterDefinitionUsedInDropdown ? ( - + {!filterDefinitionUsedInDropdown ? ( + ) : isFilterDropdownOperandSelectUnfolded ? ( - + ) : ( selectedOperandInDropdown && ( <> - + - {tableFilterDefinitionUsedInDropdown.type === 'text' && ( - + {filterDefinitionUsedInDropdown.type === 'text' && ( + )} - {tableFilterDefinitionUsedInDropdown.type === 'number' && ( - + {filterDefinitionUsedInDropdown.type === 'number' && ( + )} - {tableFilterDefinitionUsedInDropdown.type === 'date' && ( - + {filterDefinitionUsedInDropdown.type === 'date' && ( + )} - {tableFilterDefinitionUsedInDropdown.type === 'entity' && ( - + {filterDefinitionUsedInDropdown.type === 'entity' && ( + )} - {tableFilterDefinitionUsedInDropdown.type === 'entity' && ( - + {filterDefinitionUsedInDropdown.type === 'entity' && ( + )} ) diff --git a/front/src/modules/ui/components/table/table-header/FilterDropdownDateSearchInput.tsx b/front/src/modules/lib/filters-and-sorts/components/FilterDropdownDateSearchInput.tsx similarity index 63% rename from front/src/modules/ui/components/table/table-header/FilterDropdownDateSearchInput.tsx rename to front/src/modules/lib/filters-and-sorts/components/FilterDropdownDateSearchInput.tsx index c8b3743aa4..5151d79f24 100644 --- a/front/src/modules/ui/components/table/table-header/FilterDropdownDateSearchInput.tsx +++ b/front/src/modules/lib/filters-and-sorts/components/FilterDropdownDateSearchInput.tsx @@ -1,33 +1,35 @@ +import { Context } from 'react'; import styled from '@emotion/styled'; import { useUpsertFilter } from '@/lib/filters-and-sorts/hooks/useUpsertFilter'; import { filterDefinitionUsedInDropdownScopedState } from '@/lib/filters-and-sorts/states/filterDefinitionUsedInDropdownScopedState'; import { selectedOperandInDropdownScopedState } from '@/lib/filters-and-sorts/states/selectedOperandInDropdownScopedState'; import { useRecoilScopedState } from '@/recoil-scope/hooks/useRecoilScopedState'; -import { TableContext } from '@/ui/tables/states/TableContext'; +import DatePicker from '@/ui/components/form/DatePicker'; -import DatePicker from '../../form/DatePicker'; - -export function FilterDropdownDateSearchInput() { - const [tableFilterDefinitionUsedInDropdown] = useRecoilScopedState( +export function FilterDropdownDateSearchInput({ + context, +}: { + context: Context; +}) { + const [filterDefinitionUsedInDropdown] = useRecoilScopedState( filterDefinitionUsedInDropdownScopedState, - TableContext, + context, ); const [selectedOperandInDropdown] = useRecoilScopedState( selectedOperandInDropdownScopedState, - TableContext, + context, ); - const upsertActiveTableFilter = useUpsertFilter(TableContext); + const upsertFilter = useUpsertFilter(context); function handleChange(date: Date) { - if (!tableFilterDefinitionUsedInDropdown || !selectedOperandInDropdown) - return; + if (!filterDefinitionUsedInDropdown || !selectedOperandInDropdown) return; - upsertActiveTableFilter({ - field: tableFilterDefinitionUsedInDropdown.field, - type: tableFilterDefinitionUsedInDropdown.type, + upsertFilter({ + field: filterDefinitionUsedInDropdown.field, + type: filterDefinitionUsedInDropdown.type, value: date.toISOString(), operand: selectedOperandInDropdown, displayValue: date.toLocaleDateString(), diff --git a/front/src/modules/ui/components/table/table-header/FilterDropdownEntitySearchInput.tsx b/front/src/modules/lib/filters-and-sorts/components/FilterDropdownEntitySearchInput.tsx similarity index 68% rename from front/src/modules/ui/components/table/table-header/FilterDropdownEntitySearchInput.tsx rename to front/src/modules/lib/filters-and-sorts/components/FilterDropdownEntitySearchInput.tsx index 3e24a4ac53..ee486121cb 100644 --- a/front/src/modules/ui/components/table/table-header/FilterDropdownEntitySearchInput.tsx +++ b/front/src/modules/lib/filters-and-sorts/components/FilterDropdownEntitySearchInput.tsx @@ -1,32 +1,35 @@ -import { ChangeEvent } from 'react'; +import { ChangeEvent, Context } from 'react'; import { filterDefinitionUsedInDropdownScopedState } from '@/lib/filters-and-sorts/states/filterDefinitionUsedInDropdownScopedState'; import { filterDropdownSearchInputScopedState } from '@/lib/filters-and-sorts/states/filterDropdownSearchInputScopedState'; import { selectedOperandInDropdownScopedState } from '@/lib/filters-and-sorts/states/selectedOperandInDropdownScopedState'; import { useRecoilScopedState } from '@/recoil-scope/hooks/useRecoilScopedState'; -import { TableContext } from '@/ui/tables/states/TableContext'; -export function FilterDropdownEntitySearchInput() { - const [tableFilterDefinitionUsedInDropdown] = useRecoilScopedState( +export function FilterDropdownEntitySearchInput({ + context, +}: { + context: Context; +}) { + const [filterDefinitionUsedInDropdown] = useRecoilScopedState( filterDefinitionUsedInDropdownScopedState, - TableContext, + context, ); const [selectedOperandInDropdown] = useRecoilScopedState( selectedOperandInDropdownScopedState, - TableContext, + context, ); const [filterDropdownSearchInput, setFilterDropdownSearchInput] = - useRecoilScopedState(filterDropdownSearchInputScopedState, TableContext); + useRecoilScopedState(filterDropdownSearchInputScopedState, context); return ( - tableFilterDefinitionUsedInDropdown && + filterDefinitionUsedInDropdown && selectedOperandInDropdown && ( ) => { setFilterDropdownSearchInput(event.target.value); }} diff --git a/front/src/modules/ui/components/table/table-header/FilterDropdownEntitySearchSelect.tsx b/front/src/modules/lib/filters-and-sorts/components/FilterDropdownEntitySearchSelect.tsx similarity index 75% rename from front/src/modules/ui/components/table/table-header/FilterDropdownEntitySearchSelect.tsx rename to front/src/modules/lib/filters-and-sorts/components/FilterDropdownEntitySearchSelect.tsx index 7056fa61e0..3927576fff 100644 --- a/front/src/modules/ui/components/table/table-header/FilterDropdownEntitySearchSelect.tsx +++ b/front/src/modules/lib/filters-and-sorts/components/FilterDropdownEntitySearchSelect.tsx @@ -10,36 +10,34 @@ import { useRecoilScopedState } from '@/recoil-scope/hooks/useRecoilScopedState' import { EntitiesForMultipleEntitySelect } from '@/relation-picker/components/MultipleEntitySelect'; import { SingleEntitySelectBase } from '@/relation-picker/components/SingleEntitySelectBase'; import { EntityForSelect } from '@/relation-picker/types/EntityForSelect'; -import { TableContext } from '@/ui/tables/states/TableContext'; export function FilterDropdownEntitySearchSelect({ entitiesForSelect, + context, }: { entitiesForSelect: EntitiesForMultipleEntitySelect; + context: React.Context; }) { const [filterDropdownSelectedEntityId, setFilterDropdownSelectedEntityId] = - useRecoilScopedState( - filterDropdownSelectedEntityIdScopedState, - TableContext, - ); + useRecoilScopedState(filterDropdownSelectedEntityIdScopedState, context); const [selectedOperandInDropdown] = useRecoilScopedState( selectedOperandInDropdownScopedState, - TableContext, + context, ); - const [tableFilterDefinitionUsedInDropdown] = useRecoilScopedState( + const [filterDefinitionUsedInDropdown] = useRecoilScopedState( filterDefinitionUsedInDropdownScopedState, - TableContext, + context, ); - const upsertActiveTableFilter = useUpsertFilter(TableContext); - const removeActiveTableFilter = useRemoveFilter(TableContext); + const upsertFilter = useUpsertFilter(context); + const removeFilter = useRemoveFilter(context); - const filterCurrentlyEdited = useFilterCurrentlyEdited(TableContext); + const filterCurrentlyEdited = useFilterCurrentlyEdited(context); function handleUserSelected(selectedEntity: EntityForSelect) { - if (!tableFilterDefinitionUsedInDropdown || !selectedOperandInDropdown) { + if (!filterDefinitionUsedInDropdown || !selectedOperandInDropdown) { return; } @@ -47,16 +45,16 @@ export function FilterDropdownEntitySearchSelect({ selectedEntity.id === filterDropdownSelectedEntityId; if (clickedOnAlreadySelectedEntity) { - removeActiveTableFilter(tableFilterDefinitionUsedInDropdown.field); + removeFilter(filterDefinitionUsedInDropdown.field); setFilterDropdownSelectedEntityId(null); } else { setFilterDropdownSelectedEntityId(selectedEntity.id); - upsertActiveTableFilter({ + upsertFilter({ displayValue: selectedEntity.name, - field: tableFilterDefinitionUsedInDropdown.field, + field: filterDefinitionUsedInDropdown.field, operand: selectedOperandInDropdown, - type: tableFilterDefinitionUsedInDropdown.type, + type: filterDefinitionUsedInDropdown.type, value: selectedEntity.id, }); } diff --git a/front/src/modules/ui/components/table/table-header/FilterDropdownEntitySelect.tsx b/front/src/modules/lib/filters-and-sorts/components/FilterDropdownEntitySelect.tsx similarity index 52% rename from front/src/modules/ui/components/table/table-header/FilterDropdownEntitySelect.tsx rename to front/src/modules/lib/filters-and-sorts/components/FilterDropdownEntitySelect.tsx index 935ebdc116..b5fcbabeda 100644 --- a/front/src/modules/ui/components/table/table-header/FilterDropdownEntitySelect.tsx +++ b/front/src/modules/lib/filters-and-sorts/components/FilterDropdownEntitySelect.tsx @@ -1,17 +1,21 @@ +import { Context } from 'react'; + import { filterDefinitionUsedInDropdownScopedState } from '@/lib/filters-and-sorts/states/filterDefinitionUsedInDropdownScopedState'; import { RecoilScope } from '@/recoil-scope/components/RecoilScope'; import { useRecoilScopedState } from '@/recoil-scope/hooks/useRecoilScopedState'; -import { TableContext } from '@/ui/tables/states/TableContext'; +import { DropdownMenuSeparator } from '@/ui/components/menu/DropdownMenuSeparator'; -import { DropdownMenuSeparator } from '../../menu/DropdownMenuSeparator'; - -export function FilterDropdownEntitySelect() { - const [tableFilterDefinitionUsedInDropdown] = useRecoilScopedState( +export function FilterDropdownEntitySelect({ + context, +}: { + context: Context; +}) { + const [filterDefinitionUsedInDropdown] = useRecoilScopedState( filterDefinitionUsedInDropdownScopedState, - TableContext, + context, ); - if (tableFilterDefinitionUsedInDropdown?.type !== 'entity') { + if (filterDefinitionUsedInDropdown?.type !== 'entity') { return null; } @@ -19,7 +23,7 @@ export function FilterDropdownEntitySelect() { <> - {tableFilterDefinitionUsedInDropdown.entitySelectComponent} + {filterDefinitionUsedInDropdown.entitySelectComponent} ); diff --git a/front/src/modules/ui/components/table/table-header/FilterDropdownFilterSelect.tsx b/front/src/modules/lib/filters-and-sorts/components/FilterDropdownFilterSelect.tsx similarity index 69% rename from front/src/modules/ui/components/table/table-header/FilterDropdownFilterSelect.tsx rename to front/src/modules/lib/filters-and-sorts/components/FilterDropdownFilterSelect.tsx index eda80438c1..080ad9aa4c 100644 --- a/front/src/modules/ui/components/table/table-header/FilterDropdownFilterSelect.tsx +++ b/front/src/modules/lib/filters-and-sorts/components/FilterDropdownFilterSelect.tsx @@ -1,3 +1,5 @@ +import { Context } from 'react'; + import { useSetHotkeysScope } from '@/hotkeys/hooks/useSetHotkeysScope'; import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope'; import { availableFiltersScopedState } from '@/lib/filters-and-sorts/states/availableFiltersScopedState'; @@ -7,59 +9,61 @@ import { selectedOperandInDropdownScopedState } from '@/lib/filters-and-sorts/st import { getOperandsForFilterType } from '@/lib/filters-and-sorts/utils/getOperandsForFilterType'; import { useRecoilScopedState } from '@/recoil-scope/hooks/useRecoilScopedState'; import { useRecoilScopedValue } from '@/recoil-scope/hooks/useRecoilScopedValue'; -import { TableContext } from '@/ui/tables/states/TableContext'; - -import { DropdownMenuItemContainer } from '../../menu/DropdownMenuItemContainer'; -import { DropdownMenuSelectableItem } from '../../menu/DropdownMenuSelectableItem'; +import { DropdownMenuItemContainer } from '@/ui/components/menu/DropdownMenuItemContainer'; +import { DropdownMenuSelectableItem } from '@/ui/components/menu/DropdownMenuSelectableItem'; import DropdownButton from './DropdownButton'; -export function FilterDropdownFilterSelect() { - const [, setTableFilterDefinitionUsedInDropdown] = useRecoilScopedState( +export function FilterDropdownFilterSelect({ + context, +}: { + context: Context; +}) { + const [, setFilterDefinitionUsedInDropdown] = useRecoilScopedState( filterDefinitionUsedInDropdownScopedState, - TableContext, + context, ); const [, setSelectedOperandInDropdown] = useRecoilScopedState( selectedOperandInDropdownScopedState, - TableContext, + context, ); const [, setFilterDropdownSearchInput] = useRecoilScopedState( filterDropdownSearchInputScopedState, - TableContext, + context, ); - const availableTableFilters = useRecoilScopedValue( + const availableFilters = useRecoilScopedValue( availableFiltersScopedState, - TableContext, + context, ); const setHotkeysScope = useSetHotkeysScope(); return ( - {availableTableFilters.map((availableTableFilter, index) => ( + {availableFilters.map((availableFilter, index) => ( { - setTableFilterDefinitionUsedInDropdown(availableTableFilter); + setFilterDefinitionUsedInDropdown(availableFilter); - if (availableTableFilter.type === 'entity') { + if (availableFilter.type === 'entity') { setHotkeysScope(InternalHotkeysScope.RelationPicker); } setSelectedOperandInDropdown( - getOperandsForFilterType(availableTableFilter.type)?.[0], + getOperandsForFilterType(availableFilter.type)?.[0], ); setFilterDropdownSearchInput(''); }} > - {availableTableFilter.icon} + {availableFilter.icon} - {availableTableFilter.label} + {availableFilter.label} ))} diff --git a/front/src/modules/ui/components/table/table-header/FilterDropdownNumberSearchInput.tsx b/front/src/modules/lib/filters-and-sorts/components/FilterDropdownNumberSearchInput.tsx similarity index 59% rename from front/src/modules/ui/components/table/table-header/FilterDropdownNumberSearchInput.tsx rename to front/src/modules/lib/filters-and-sorts/components/FilterDropdownNumberSearchInput.tsx index caa3850c10..abd14d10b3 100644 --- a/front/src/modules/ui/components/table/table-header/FilterDropdownNumberSearchInput.tsx +++ b/front/src/modules/lib/filters-and-sorts/components/FilterDropdownNumberSearchInput.tsx @@ -1,39 +1,42 @@ -import { ChangeEvent } from 'react'; +import { ChangeEvent, Context } from 'react'; import { useRemoveFilter } from '@/lib/filters-and-sorts/hooks/useRemoveFilter'; import { useUpsertFilter } from '@/lib/filters-and-sorts/hooks/useUpsertFilter'; import { filterDefinitionUsedInDropdownScopedState } from '@/lib/filters-and-sorts/states/filterDefinitionUsedInDropdownScopedState'; import { selectedOperandInDropdownScopedState } from '@/lib/filters-and-sorts/states/selectedOperandInDropdownScopedState'; import { useRecoilScopedState } from '@/recoil-scope/hooks/useRecoilScopedState'; -import { TableContext } from '@/ui/tables/states/TableContext'; -export function FilterDropdownNumberSearchInput() { - const [tableFilterDefinitionUsedInDropdown] = useRecoilScopedState( +export function FilterDropdownNumberSearchInput({ + context, +}: { + context: Context; +}) { + const [filterDefinitionUsedInDropdown] = useRecoilScopedState( filterDefinitionUsedInDropdownScopedState, - TableContext, + context, ); const [selectedOperandInDropdown] = useRecoilScopedState( selectedOperandInDropdownScopedState, - TableContext, + context, ); - const upsertActiveTableFilter = useUpsertFilter(TableContext); - const removeActiveTableFilter = useRemoveFilter(TableContext); + const upsertFilter = useUpsertFilter(context); + const removeFilter = useRemoveFilter(context); return ( - tableFilterDefinitionUsedInDropdown && + filterDefinitionUsedInDropdown && selectedOperandInDropdown && ( ) => { if (event.target.value === '') { - removeActiveTableFilter(tableFilterDefinitionUsedInDropdown.field); + removeFilter(filterDefinitionUsedInDropdown.field); } else { - upsertActiveTableFilter({ - field: tableFilterDefinitionUsedInDropdown.field, - type: tableFilterDefinitionUsedInDropdown.type, + upsertFilter({ + field: filterDefinitionUsedInDropdown.field, + type: filterDefinitionUsedInDropdown.type, value: event.target.value, operand: selectedOperandInDropdown, displayValue: event.target.value, diff --git a/front/src/modules/ui/components/table/table-header/FilterDropdownOperandButton.tsx b/front/src/modules/lib/filters-and-sorts/components/FilterDropdownOperandButton.tsx similarity index 87% rename from front/src/modules/ui/components/table/table-header/FilterDropdownOperandButton.tsx rename to front/src/modules/lib/filters-and-sorts/components/FilterDropdownOperandButton.tsx index 8c96441e0d..faf637d023 100644 --- a/front/src/modules/ui/components/table/table-header/FilterDropdownOperandButton.tsx +++ b/front/src/modules/lib/filters-and-sorts/components/FilterDropdownOperandButton.tsx @@ -1,21 +1,26 @@ +import { Context } from 'react'; + import { isFilterDropdownOperandSelectUnfoldedScopedState } from '@/lib/filters-and-sorts/states/isFilterDropdownOperandSelectUnfoldedScopedState'; import { selectedOperandInDropdownScopedState } from '@/lib/filters-and-sorts/states/selectedOperandInDropdownScopedState'; import { getOperandLabel } from '@/lib/filters-and-sorts/utils/getOperandLabel'; import { useRecoilScopedState } from '@/recoil-scope/hooks/useRecoilScopedState'; -import { TableContext } from '@/ui/tables/states/TableContext'; import DropdownButton from './DropdownButton'; -export function FilterDropdownOperandButton() { +export function FilterDropdownOperandButton({ + context, +}: { + context: Context; +}) { const [selectedOperandInDropdown] = useRecoilScopedState( selectedOperandInDropdownScopedState, - TableContext, + context, ); const [isOperandSelectionUnfolded, setIsOperandSelectionUnfolded] = useRecoilScopedState( isFilterDropdownOperandSelectUnfoldedScopedState, - TableContext, + context, ); if (isOperandSelectionUnfolded) { diff --git a/front/src/modules/ui/components/table/table-header/FilterDropdownOperandSelect.tsx b/front/src/modules/lib/filters-and-sorts/components/FilterDropdownOperandSelect.tsx similarity index 70% rename from front/src/modules/ui/components/table/table-header/FilterDropdownOperandSelect.tsx rename to front/src/modules/lib/filters-and-sorts/components/FilterDropdownOperandSelect.tsx index a45aecf81b..e703dcb43b 100644 --- a/front/src/modules/ui/components/table/table-header/FilterDropdownOperandSelect.tsx +++ b/front/src/modules/lib/filters-and-sorts/components/FilterDropdownOperandSelect.tsx @@ -1,3 +1,5 @@ +import { Context } from 'react'; + import { useFilterCurrentlyEdited } from '@/lib/filters-and-sorts/hooks/useFilterCurrentlyEdited'; import { useUpsertFilter } from '@/lib/filters-and-sorts/hooks/useUpsertFilter'; import { filterDefinitionUsedInDropdownScopedState } from '@/lib/filters-and-sorts/states/filterDefinitionUsedInDropdownScopedState'; @@ -7,52 +9,50 @@ import { FilterOperand } from '@/lib/filters-and-sorts/types/FilterOperand'; import { getOperandLabel } from '@/lib/filters-and-sorts/utils/getOperandLabel'; import { getOperandsForFilterType } from '@/lib/filters-and-sorts/utils/getOperandsForFilterType'; import { useRecoilScopedState } from '@/recoil-scope/hooks/useRecoilScopedState'; -import { TableContext } from '@/ui/tables/states/TableContext'; - -import { DropdownMenuItemContainer } from '../../menu/DropdownMenuItemContainer'; +import { DropdownMenuItemContainer } from '@/ui/components/menu/DropdownMenuItemContainer'; import DropdownButton from './DropdownButton'; -export function FilterDropdownOperandSelect() { - const [tableFilterDefinitionUsedInDropdown] = useRecoilScopedState( +export function FilterDropdownOperandSelect({ + context, +}: { + context: Context; +}) { + const [filterDefinitionUsedInDropdown] = useRecoilScopedState( filterDefinitionUsedInDropdownScopedState, - TableContext, + context, ); const [, setSelectedOperandInDropdown] = useRecoilScopedState( selectedOperandInDropdownScopedState, - TableContext, + context, ); const operandsForFilterType = getOperandsForFilterType( - tableFilterDefinitionUsedInDropdown?.type, + filterDefinitionUsedInDropdown?.type, ); const [isOperandSelectionUnfolded, setIsOperandSelectionUnfolded] = useRecoilScopedState( isFilterDropdownOperandSelectUnfoldedScopedState, - TableContext, + context, ); - const activeTableFilterCurrentlyEdited = - useFilterCurrentlyEdited(TableContext); + const filterCurrentlyEdited = useFilterCurrentlyEdited(context); - const upsertActiveTableFilter = useUpsertFilter(TableContext); + const upsertFilter = useUpsertFilter(context); function handleOperangeChange(newOperand: FilterOperand) { setSelectedOperandInDropdown(newOperand); setIsOperandSelectionUnfolded(false); - if ( - tableFilterDefinitionUsedInDropdown && - activeTableFilterCurrentlyEdited - ) { - upsertActiveTableFilter({ - field: activeTableFilterCurrentlyEdited.field, - displayValue: activeTableFilterCurrentlyEdited.displayValue, + if (filterDefinitionUsedInDropdown && filterCurrentlyEdited) { + upsertFilter({ + field: filterCurrentlyEdited.field, + displayValue: filterCurrentlyEdited.displayValue, operand: newOperand, - type: activeTableFilterCurrentlyEdited.type, - value: activeTableFilterCurrentlyEdited.value, + type: filterCurrentlyEdited.type, + value: filterCurrentlyEdited.value, }); } } diff --git a/front/src/modules/ui/components/table/table-header/FilterDropdownTextSearchInput.tsx b/front/src/modules/lib/filters-and-sorts/components/FilterDropdownTextSearchInput.tsx similarity index 62% rename from front/src/modules/ui/components/table/table-header/FilterDropdownTextSearchInput.tsx rename to front/src/modules/lib/filters-and-sorts/components/FilterDropdownTextSearchInput.tsx index b0b7d31f93..057035627b 100644 --- a/front/src/modules/ui/components/table/table-header/FilterDropdownTextSearchInput.tsx +++ b/front/src/modules/lib/filters-and-sorts/components/FilterDropdownTextSearchInput.tsx @@ -1,4 +1,4 @@ -import { ChangeEvent } from 'react'; +import { ChangeEvent, Context } from 'react'; import { useFilterCurrentlyEdited } from '@/lib/filters-and-sorts/hooks/useFilterCurrentlyEdited'; import { useRemoveFilter } from '@/lib/filters-and-sorts/hooks/useRemoveFilter'; @@ -7,43 +7,46 @@ import { filterDefinitionUsedInDropdownScopedState } from '@/lib/filters-and-sor import { filterDropdownSearchInputScopedState } from '@/lib/filters-and-sorts/states/filterDropdownSearchInputScopedState'; import { selectedOperandInDropdownScopedState } from '@/lib/filters-and-sorts/states/selectedOperandInDropdownScopedState'; import { useRecoilScopedState } from '@/recoil-scope/hooks/useRecoilScopedState'; -import { TableContext } from '@/ui/tables/states/TableContext'; -export function FilterDropdownTextSearchInput() { - const [tableFilterDefinitionUsedInDropdown] = useRecoilScopedState( +export function FilterDropdownTextSearchInput({ + context, +}: { + context: Context; +}) { + const [filterDefinitionUsedInDropdown] = useRecoilScopedState( filterDefinitionUsedInDropdownScopedState, - TableContext, + context, ); const [selectedOperandInDropdown] = useRecoilScopedState( selectedOperandInDropdownScopedState, - TableContext, + context, ); const [filterDropdownSearchInput, setFilterDropdownSearchInput] = - useRecoilScopedState(filterDropdownSearchInputScopedState, TableContext); + useRecoilScopedState(filterDropdownSearchInputScopedState, context); - const upsertActiveTableFilter = useUpsertFilter(TableContext); - const removeActiveTableFilter = useRemoveFilter(TableContext); + const upsertFilter = useUpsertFilter(context); + const removeFilter = useRemoveFilter(context); - const filterCurrentlyEdited = useFilterCurrentlyEdited(TableContext); + const filterCurrentlyEdited = useFilterCurrentlyEdited(context); return ( - tableFilterDefinitionUsedInDropdown && + filterDefinitionUsedInDropdown && selectedOperandInDropdown && ( ) => { setFilterDropdownSearchInput(event.target.value); if (event.target.value === '') { - removeActiveTableFilter(tableFilterDefinitionUsedInDropdown.field); + removeFilter(filterDefinitionUsedInDropdown.field); } else { - upsertActiveTableFilter({ - field: tableFilterDefinitionUsedInDropdown.field, - type: tableFilterDefinitionUsedInDropdown.type, + upsertFilter({ + field: filterDefinitionUsedInDropdown.field, + type: filterDefinitionUsedInDropdown.type, value: event.target.value, operand: selectedOperandInDropdown, displayValue: event.target.value, diff --git a/front/src/modules/ui/components/table/table-header/SortAndFilterBar.tsx b/front/src/modules/lib/filters-and-sorts/components/SortAndFilterBar.tsx similarity index 92% rename from front/src/modules/ui/components/table/table-header/SortAndFilterBar.tsx rename to front/src/modules/lib/filters-and-sorts/components/SortAndFilterBar.tsx index 77dacc4eff..92c8641cb1 100644 --- a/front/src/modules/ui/components/table/table-header/SortAndFilterBar.tsx +++ b/front/src/modules/lib/filters-and-sorts/components/SortAndFilterBar.tsx @@ -1,6 +1,8 @@ +import { Context } from 'react'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; +import SortOrFilterChip from '@/lib/filters-and-sorts/components/SortOrFilterChip'; import { useRemoveFilter } from '@/lib/filters-and-sorts/hooks/useRemoveFilter'; import { SelectedSortType } from '@/lib/filters-and-sorts/interfaces/sorts/interface'; import { availableFiltersScopedState } from '@/lib/filters-and-sorts/states/availableFiltersScopedState'; @@ -8,11 +10,9 @@ import { filtersScopedState } from '@/lib/filters-and-sorts/states/filtersScoped import { getOperandLabel } from '@/lib/filters-and-sorts/utils/getOperandLabel'; import { useRecoilScopedState } from '@/recoil-scope/hooks/useRecoilScopedState'; import { IconArrowNarrowDown, IconArrowNarrowUp } from '@/ui/icons/index'; -import { TableContext } from '@/ui/tables/states/TableContext'; - -import SortOrFilterChip from './SortOrFilterChip'; type OwnProps = { + context: Context; sorts: Array>; onRemoveSort: (sortId: SelectedSortType['key']) => void; onCancelClick: () => void; @@ -60,6 +60,7 @@ const StyledCancelButton = styled.button` `; function SortAndFilterBar({ + context, sorts, onRemoveSort, onCancelClick, @@ -68,26 +69,26 @@ function SortAndFilterBar({ const [filters, setFilters] = useRecoilScopedState( filtersScopedState, - TableContext, + context, ); const [availableFilters] = useRecoilScopedState( availableFiltersScopedState, - TableContext, + context, ); const filtersWithDefinition = filters.map((filter) => { - const tableFilterDefinition = availableFilters.find((availableFilter) => { + const filterDefinition = availableFilters.find((availableFilter) => { return availableFilter.field === filter.field; }); return { ...filter, - ...tableFilterDefinition, + ...filterDefinition, }; }); - const removeFilter = useRemoveFilter(TableContext); + const removeFilter = useRemoveFilter(context); function handleCancelClick() { setFilters([]); diff --git a/front/src/modules/ui/components/table/table-header/SortDropdownButton.tsx b/front/src/modules/lib/filters-and-sorts/components/SortDropdownButton.tsx similarity index 94% rename from front/src/modules/ui/components/table/table-header/SortDropdownButton.tsx rename to front/src/modules/lib/filters-and-sorts/components/SortDropdownButton.tsx index 351e854b5e..86df00271f 100644 --- a/front/src/modules/ui/components/table/table-header/SortDropdownButton.tsx +++ b/front/src/modules/lib/filters-and-sorts/components/SortDropdownButton.tsx @@ -1,5 +1,6 @@ import { useCallback, useState } from 'react'; +import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope'; import { SelectedSortType, SortType, @@ -11,6 +12,7 @@ type OwnProps = { isSortSelected: boolean; onSortSelect: (sort: SelectedSortType) => void; availableSorts: SortType[]; + hotkeysScope: InternalHotkeysScope; }; const options: Array['order']> = ['asc', 'desc']; @@ -19,6 +21,7 @@ export function SortDropdownButton({ isSortSelected, availableSorts, onSortSelect, + hotkeysScope, }: OwnProps) { const [isUnfolded, setIsUnfolded] = useState(false); @@ -54,6 +57,7 @@ export function SortDropdownButton({ isActive={isSortSelected} isUnfolded={isUnfolded} onIsUnfoldedChange={handleIsUnfoldedChange} + hotkeysScope={hotkeysScope} > {isOptionUnfolded ? options.map((option, index) => ( diff --git a/front/src/modules/ui/components/table/table-header/SortOrFilterChip.tsx b/front/src/modules/lib/filters-and-sorts/components/SortOrFilterChip.tsx similarity index 100% rename from front/src/modules/ui/components/table/table-header/SortOrFilterChip.tsx rename to front/src/modules/lib/filters-and-sorts/components/SortOrFilterChip.tsx diff --git a/front/src/modules/ui/components/table/table-header/TableHeader.tsx b/front/src/modules/ui/components/table/table-header/TableHeader.tsx index 785aad6452..ee5ae01713 100644 --- a/front/src/modules/ui/components/table/table-header/TableHeader.tsx +++ b/front/src/modules/ui/components/table/table-header/TableHeader.tsx @@ -1,14 +1,15 @@ import { ReactNode, useCallback, useState } from 'react'; import styled from '@emotion/styled'; +import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope'; +import { FilterDropdownButton } from '@/lib/filters-and-sorts/components/FilterDropdownButton'; +import SortAndFilterBar from '@/lib/filters-and-sorts/components/SortAndFilterBar'; +import { SortDropdownButton } from '@/lib/filters-and-sorts/components/SortDropdownButton'; import { SelectedSortType, SortType, } from '@/lib/filters-and-sorts/interfaces/sorts/interface'; - -import { FilterDropdownButton } from './FilterDropdownButton'; -import SortAndFilterBar from './SortAndFilterBar'; -import { SortDropdownButton } from './SortDropdownButton'; +import { TableContext } from '@/ui/tables/states/TableContext'; type OwnProps = { viewName: string; @@ -89,15 +90,20 @@ export function TableHeader({ {viewName} - + isSortSelected={sorts.length > 0} availableSorts={availableSorts || []} onSortSelect={sortSelect} + hotkeysScope={InternalHotkeysScope.TableHeaderDropdownButton} /> { diff --git a/front/src/modules/users/components/FilterDropdownUserSearchSelect.tsx b/front/src/modules/users/components/FilterDropdownUserSearchSelect.tsx index 0ccf1d3f95..b91b084b9e 100644 --- a/front/src/modules/users/components/FilterDropdownUserSearchSelect.tsx +++ b/front/src/modules/users/components/FilterDropdownUserSearchSelect.tsx @@ -1,10 +1,10 @@ +import { FilterDropdownEntitySearchSelect } from '@/lib/filters-and-sorts/components/FilterDropdownEntitySearchSelect'; import { filterDropdownSearchInputScopedState } from '@/lib/filters-and-sorts/states/filterDropdownSearchInputScopedState'; import { filterDropdownSelectedEntityIdScopedState } from '@/lib/filters-and-sorts/states/filterDropdownSelectedEntityIdScopedState'; import { useRecoilScopedState } from '@/recoil-scope/hooks/useRecoilScopedState'; import { useRecoilScopedValue } from '@/recoil-scope/hooks/useRecoilScopedValue'; import { useFilteredSearchEntityQuery } from '@/relation-picker/hooks/useFilteredSearchEntityQuery'; import { Entity } from '@/relation-picker/types/EntityTypeForSelect'; -import { FilterDropdownEntitySearchSelect } from '@/ui/components/table/table-header/FilterDropdownEntitySearchSelect'; import { TableContext } from '@/ui/tables/states/TableContext'; import { useSearchUserQuery } from '~/generated/graphql'; @@ -36,6 +36,9 @@ export function FilterDropdownUserSearchSelect() { }); return ( - + ); }