mirror of
https://github.com/twentyhq/twenty.git
synced 2025-01-03 17:53:58 +03:00
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 <charles@twenty.com>
This commit is contained in:
parent
e8d77833a7
commit
b5de2abd48
@ -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 (
|
||||
<FilterDropdownEntitySearchSelect entitiesForSelect={usersForSelect} />
|
||||
<FilterDropdownEntitySearchSelect
|
||||
entitiesForSelect={usersForSelect}
|
||||
context={TableContext}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -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],
|
||||
);
|
||||
|
@ -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<string | null>;
|
||||
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 ? (
|
||||
<FilterDropdownFilterSelect />
|
||||
{!filterDefinitionUsedInDropdown ? (
|
||||
<FilterDropdownFilterSelect context={context} />
|
||||
) : isFilterDropdownOperandSelectUnfolded ? (
|
||||
<FilterDropdownOperandSelect />
|
||||
<FilterDropdownOperandSelect context={context} />
|
||||
) : (
|
||||
selectedOperandInDropdown && (
|
||||
<>
|
||||
<FilterDropdownOperandButton />
|
||||
<FilterDropdownOperandButton context={context} />
|
||||
<DropdownButton.StyledSearchField autoFocus key={'search-filter'}>
|
||||
{tableFilterDefinitionUsedInDropdown.type === 'text' && (
|
||||
<FilterDropdownTextSearchInput />
|
||||
{filterDefinitionUsedInDropdown.type === 'text' && (
|
||||
<FilterDropdownTextSearchInput context={context} />
|
||||
)}
|
||||
{tableFilterDefinitionUsedInDropdown.type === 'number' && (
|
||||
<FilterDropdownNumberSearchInput />
|
||||
{filterDefinitionUsedInDropdown.type === 'number' && (
|
||||
<FilterDropdownNumberSearchInput context={context} />
|
||||
)}
|
||||
{tableFilterDefinitionUsedInDropdown.type === 'date' && (
|
||||
<FilterDropdownDateSearchInput />
|
||||
{filterDefinitionUsedInDropdown.type === 'date' && (
|
||||
<FilterDropdownDateSearchInput context={context} />
|
||||
)}
|
||||
{tableFilterDefinitionUsedInDropdown.type === 'entity' && (
|
||||
<FilterDropdownEntitySearchInput />
|
||||
{filterDefinitionUsedInDropdown.type === 'entity' && (
|
||||
<FilterDropdownEntitySearchInput context={context} />
|
||||
)}
|
||||
</DropdownButton.StyledSearchField>
|
||||
{tableFilterDefinitionUsedInDropdown.type === 'entity' && (
|
||||
<FilterDropdownEntitySelect />
|
||||
{filterDefinitionUsedInDropdown.type === 'entity' && (
|
||||
<FilterDropdownEntitySelect context={context} />
|
||||
)}
|
||||
</>
|
||||
)
|
@ -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<string | null>;
|
||||
}) {
|
||||
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(),
|
@ -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<string | null>;
|
||||
}) {
|
||||
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 && (
|
||||
<input
|
||||
type="text"
|
||||
value={filterDropdownSearchInput}
|
||||
placeholder={tableFilterDefinitionUsedInDropdown.label}
|
||||
placeholder={filterDefinitionUsedInDropdown.label}
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||
setFilterDropdownSearchInput(event.target.value);
|
||||
}}
|
@ -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<EntityForSelect>;
|
||||
context: React.Context<string | null>;
|
||||
}) {
|
||||
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,
|
||||
});
|
||||
}
|
@ -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<string | null>;
|
||||
}) {
|
||||
const [filterDefinitionUsedInDropdown] = useRecoilScopedState(
|
||||
filterDefinitionUsedInDropdownScopedState,
|
||||
TableContext,
|
||||
context,
|
||||
);
|
||||
|
||||
if (tableFilterDefinitionUsedInDropdown?.type !== 'entity') {
|
||||
if (filterDefinitionUsedInDropdown?.type !== 'entity') {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -19,7 +23,7 @@ export function FilterDropdownEntitySelect() {
|
||||
<>
|
||||
<DropdownMenuSeparator />
|
||||
<RecoilScope>
|
||||
{tableFilterDefinitionUsedInDropdown.entitySelectComponent}
|
||||
{filterDefinitionUsedInDropdown.entitySelectComponent}
|
||||
</RecoilScope>
|
||||
</>
|
||||
);
|
@ -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<string | null>;
|
||||
}) {
|
||||
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 (
|
||||
<DropdownMenuItemContainer style={{ maxHeight: '300px' }}>
|
||||
{availableTableFilters.map((availableTableFilter, index) => (
|
||||
{availableFilters.map((availableFilter, index) => (
|
||||
<DropdownMenuSelectableItem
|
||||
key={`select-filter-${index}`}
|
||||
onClick={() => {
|
||||
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('');
|
||||
}}
|
||||
>
|
||||
<DropdownButton.StyledIcon>
|
||||
{availableTableFilter.icon}
|
||||
{availableFilter.icon}
|
||||
</DropdownButton.StyledIcon>
|
||||
{availableTableFilter.label}
|
||||
{availableFilter.label}
|
||||
</DropdownMenuSelectableItem>
|
||||
))}
|
||||
</DropdownMenuItemContainer>
|
@ -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<string | null>;
|
||||
}) {
|
||||
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 && (
|
||||
<input
|
||||
type="number"
|
||||
placeholder={tableFilterDefinitionUsedInDropdown.label}
|
||||
placeholder={filterDefinitionUsedInDropdown.label}
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||
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,
|
@ -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<string | null>;
|
||||
}) {
|
||||
const [selectedOperandInDropdown] = useRecoilScopedState(
|
||||
selectedOperandInDropdownScopedState,
|
||||
TableContext,
|
||||
context,
|
||||
);
|
||||
|
||||
const [isOperandSelectionUnfolded, setIsOperandSelectionUnfolded] =
|
||||
useRecoilScopedState(
|
||||
isFilterDropdownOperandSelectUnfoldedScopedState,
|
||||
TableContext,
|
||||
context,
|
||||
);
|
||||
|
||||
if (isOperandSelectionUnfolded) {
|
@ -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<string | null>;
|
||||
}) {
|
||||
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,
|
||||
});
|
||||
}
|
||||
}
|
@ -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<string | null>;
|
||||
}) {
|
||||
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 && (
|
||||
<input
|
||||
type="text"
|
||||
placeholder={tableFilterDefinitionUsedInDropdown.label}
|
||||
placeholder={filterDefinitionUsedInDropdown.label}
|
||||
value={filterCurrentlyEdited?.value ?? filterDropdownSearchInput}
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||
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,
|
@ -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<SortField> = {
|
||||
context: Context<string | null>;
|
||||
sorts: Array<SelectedSortType<SortField>>;
|
||||
onRemoveSort: (sortId: SelectedSortType<SortField>['key']) => void;
|
||||
onCancelClick: () => void;
|
||||
@ -60,6 +60,7 @@ const StyledCancelButton = styled.button`
|
||||
`;
|
||||
|
||||
function SortAndFilterBar<SortField>({
|
||||
context,
|
||||
sorts,
|
||||
onRemoveSort,
|
||||
onCancelClick,
|
||||
@ -68,26 +69,26 @@ function SortAndFilterBar<SortField>({
|
||||
|
||||
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([]);
|
@ -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<SortField> = {
|
||||
isSortSelected: boolean;
|
||||
onSortSelect: (sort: SelectedSortType<SortField>) => void;
|
||||
availableSorts: SortType<SortField>[];
|
||||
hotkeysScope: InternalHotkeysScope;
|
||||
};
|
||||
|
||||
const options: Array<SelectedSortType<any>['order']> = ['asc', 'desc'];
|
||||
@ -19,6 +21,7 @@ export function SortDropdownButton<SortField>({
|
||||
isSortSelected,
|
||||
availableSorts,
|
||||
onSortSelect,
|
||||
hotkeysScope,
|
||||
}: OwnProps<SortField>) {
|
||||
const [isUnfolded, setIsUnfolded] = useState(false);
|
||||
|
||||
@ -54,6 +57,7 @@ export function SortDropdownButton<SortField>({
|
||||
isActive={isSortSelected}
|
||||
isUnfolded={isUnfolded}
|
||||
onIsUnfoldedChange={handleIsUnfoldedChange}
|
||||
hotkeysScope={hotkeysScope}
|
||||
>
|
||||
{isOptionUnfolded
|
||||
? options.map((option, index) => (
|
@ -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<SortField> = {
|
||||
viewName: string;
|
||||
@ -89,15 +90,20 @@ export function TableHeader<SortField>({
|
||||
{viewName}
|
||||
</StyledViewSection>
|
||||
<StyledFilters>
|
||||
<FilterDropdownButton />
|
||||
<FilterDropdownButton
|
||||
context={TableContext}
|
||||
hotkeysScope={InternalHotkeysScope.TableHeaderDropdownButton}
|
||||
/>
|
||||
<SortDropdownButton<SortField>
|
||||
isSortSelected={sorts.length > 0}
|
||||
availableSorts={availableSorts || []}
|
||||
onSortSelect={sortSelect}
|
||||
hotkeysScope={InternalHotkeysScope.TableHeaderDropdownButton}
|
||||
/>
|
||||
</StyledFilters>
|
||||
</StyledTableHeader>
|
||||
<SortAndFilterBar
|
||||
context={TableContext}
|
||||
sorts={sorts}
|
||||
onRemoveSort={sortUnselect}
|
||||
onCancelClick={() => {
|
||||
|
@ -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 (
|
||||
<FilterDropdownEntitySearchSelect entitiesForSelect={usersForSelect} />
|
||||
<FilterDropdownEntitySearchSelect
|
||||
entitiesForSelect={usersForSelect}
|
||||
context={TableContext}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user