mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-23 20:13:21 +03:00
3886 - Shortcut Sort/Filter (#3901)
Closes #3886 --------- Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
parent
b65d82c274
commit
bcf5268f7f
@ -23,6 +23,14 @@ export const useColumnDefinitionsFromFieldMetadata = (
|
||||
[objectMetadataItem],
|
||||
);
|
||||
|
||||
const filterDefinitions = formatFieldMetadataItemsAsFilterDefinitions({
|
||||
fields: activeFieldMetadataItems,
|
||||
});
|
||||
|
||||
const sortDefinitions = formatFieldMetadataItemsAsSortDefinitions({
|
||||
fields: activeFieldMetadataItems,
|
||||
});
|
||||
|
||||
const columnDefinitions: ColumnDefinition<FieldMetadata>[] = useMemo(
|
||||
() =>
|
||||
objectMetadataItem
|
||||
@ -35,18 +43,30 @@ export const useColumnDefinitionsFromFieldMetadata = (
|
||||
}),
|
||||
)
|
||||
.filter(filterAvailableTableColumns)
|
||||
.map((column) => {
|
||||
const existsInFilterDefinitions = filterDefinitions.some(
|
||||
(filter) => filter.fieldMetadataId === column.fieldMetadataId,
|
||||
);
|
||||
|
||||
const existsInSortDefinitions = sortDefinitions.some(
|
||||
(sort) => sort.fieldMetadataId === column.fieldMetadataId,
|
||||
);
|
||||
|
||||
return {
|
||||
...column,
|
||||
isFilterable: existsInFilterDefinitions,
|
||||
isSortable: existsInSortDefinitions,
|
||||
};
|
||||
})
|
||||
: [],
|
||||
[activeFieldMetadataItems, objectMetadataItem],
|
||||
[
|
||||
activeFieldMetadataItems,
|
||||
objectMetadataItem,
|
||||
filterDefinitions,
|
||||
sortDefinitions,
|
||||
],
|
||||
);
|
||||
|
||||
const filterDefinitions = formatFieldMetadataItemsAsFilterDefinitions({
|
||||
fields: activeFieldMetadataItems,
|
||||
});
|
||||
|
||||
const sortDefinitions = formatFieldMetadataItemsAsSortDefinitions({
|
||||
fields: activeFieldMetadataItems,
|
||||
});
|
||||
|
||||
return {
|
||||
columnDefinitions,
|
||||
filterDefinitions,
|
||||
|
@ -53,10 +53,10 @@ export const formatFieldMetadataItemAsFilterDefinition = ({
|
||||
field.toRelationMetadata?.fromObjectMetadata.namePlural,
|
||||
relationObjectMetadataNameSingular:
|
||||
field.toRelationMetadata?.fromObjectMetadata.nameSingular,
|
||||
type: getFilterType(field.type),
|
||||
type: getFilterTypeFromFieldType(field.type),
|
||||
});
|
||||
|
||||
const getFilterType = (fieldType: FieldMetadataType) => {
|
||||
export const getFilterTypeFromFieldType = (fieldType: FieldMetadataType) => {
|
||||
switch (fieldType) {
|
||||
case FieldMetadataType.DateTime:
|
||||
return 'DATE_TIME';
|
||||
|
@ -1,9 +1,7 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { IconChevronDown } from 'twenty-ui';
|
||||
|
||||
import { OBJECT_SORT_DROPDOWN_ID } from '@/object-record/object-sort-dropdown/constants/ObjectSortDropdownId';
|
||||
import { useSortDropdown } from '@/object-record/object-sort-dropdown/hooks/useSortDropdown';
|
||||
import { useObjectSortDropdown } from '@/object-record/object-sort-dropdown/hooks/useObjectSortDropdown';
|
||||
import { ObjectSortDropdownScope } from '@/object-record/object-sort-dropdown/scopes/ObjectSortDropdownScope';
|
||||
import { useIcons } from '@/ui/display/icon/hooks/useIcons';
|
||||
import { LightButton } from '@/ui/input/button/components/LightButton';
|
||||
@ -11,12 +9,10 @@ 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 { 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 { SortDefinition } from '../types/SortDefinition';
|
||||
import { SORT_DIRECTIONS, SortDirection } from '../types/SortDirection';
|
||||
import { SORT_DIRECTIONS } from '../types/SortDirection';
|
||||
|
||||
export type ObjectSortDropdownButtonProps = {
|
||||
sortDropdownId: string;
|
||||
@ -27,45 +23,20 @@ export const ObjectSortDropdownButton = ({
|
||||
sortDropdownId,
|
||||
hotkeyScope,
|
||||
}: ObjectSortDropdownButtonProps) => {
|
||||
const [isSortDirectionMenuUnfolded, setIsSortDirectionMenuUnfolded] =
|
||||
useState(false);
|
||||
|
||||
const [selectedSortDirection, setSelectedSortDirection] =
|
||||
useState<SortDirection>('asc');
|
||||
|
||||
const resetState = useCallback(() => {
|
||||
setIsSortDirectionMenuUnfolded(false);
|
||||
setSelectedSortDirection('asc');
|
||||
}, []);
|
||||
|
||||
const { toggleDropdown } = useDropdown(OBJECT_SORT_DROPDOWN_ID);
|
||||
const {
|
||||
isSortDirectionMenuUnfolded,
|
||||
setIsSortDirectionMenuUnfolded,
|
||||
selectedSortDirection,
|
||||
setSelectedSortDirection,
|
||||
toggleSortDropdown,
|
||||
resetState,
|
||||
isSortSelected,
|
||||
availableSortDefinitions,
|
||||
handleAddSort,
|
||||
} = useObjectSortDropdown();
|
||||
|
||||
const handleButtonClick = () => {
|
||||
toggleDropdown();
|
||||
resetState();
|
||||
};
|
||||
|
||||
const {
|
||||
availableSortDefinitionsState,
|
||||
onSortSelectState,
|
||||
isSortSelectedState,
|
||||
} = useSortDropdown({
|
||||
sortDropdownId: sortDropdownId,
|
||||
});
|
||||
|
||||
const isSortSelected = useRecoilValue(isSortSelectedState);
|
||||
const availableSortDefinitions = useRecoilValue(
|
||||
availableSortDefinitionsState,
|
||||
);
|
||||
const onSortSelect = useRecoilValue(onSortSelectState);
|
||||
|
||||
const handleAddSort = (selectedSortDefinition: SortDefinition) => {
|
||||
toggleDropdown();
|
||||
onSortSelect?.({
|
||||
fieldMetadataId: selectedSortDefinition.fieldMetadataId,
|
||||
direction: selectedSortDirection,
|
||||
definition: selectedSortDefinition,
|
||||
});
|
||||
toggleSortDropdown();
|
||||
};
|
||||
|
||||
const handleDropdownButtonClose = () => {
|
||||
|
@ -1 +1,5 @@
|
||||
export const OBJECT_SORT_DROPDOWN_ID = 'sort-dropdown';
|
||||
/* eslint-disable @nx/workspace-max-consts-per-file */
|
||||
const OBJECT_SORT_DROPDOWN_ID = 'sort-dropdown';
|
||||
const VIEW_SORT_DROPDOWN_ID = 'view-sort';
|
||||
|
||||
export { OBJECT_SORT_DROPDOWN_ID, VIEW_SORT_DROPDOWN_ID };
|
||||
|
@ -0,0 +1,77 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useSortDropdown } from '@/object-record/object-sort-dropdown/hooks/useSortDropdown';
|
||||
import isSortDirectionMenuUnfoldedState from '@/object-record/object-sort-dropdown/states/isSortDirectionMenuUnfoldedState';
|
||||
import selectedSortDirectionState from '@/object-record/object-sort-dropdown/states/selectedSortDirectionState';
|
||||
import { SortDefinition } from '@/object-record/object-sort-dropdown/types/SortDefinition';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
|
||||
import {
|
||||
OBJECT_SORT_DROPDOWN_ID,
|
||||
VIEW_SORT_DROPDOWN_ID,
|
||||
} from '../constants/ObjectSortDropdownId';
|
||||
|
||||
// TODO: merge this with useSortDropdown
|
||||
export const useObjectSortDropdown = () => {
|
||||
const [isSortDirectionMenuUnfolded, setIsSortDirectionMenuUnfolded] =
|
||||
useRecoilState(isSortDirectionMenuUnfoldedState);
|
||||
|
||||
const [selectedSortDirection, setSelectedSortDirection] = useRecoilState(
|
||||
selectedSortDirectionState,
|
||||
);
|
||||
|
||||
const resetState = useCallback(() => {
|
||||
setIsSortDirectionMenuUnfolded(false);
|
||||
setSelectedSortDirection('asc');
|
||||
}, [setIsSortDirectionMenuUnfolded, setSelectedSortDirection]);
|
||||
|
||||
const { toggleDropdown, closeDropdown } = useDropdown(
|
||||
OBJECT_SORT_DROPDOWN_ID,
|
||||
);
|
||||
|
||||
const toggleSortDropdown = () => {
|
||||
toggleDropdown();
|
||||
resetState();
|
||||
};
|
||||
|
||||
const closeSortDropdown = () => {
|
||||
closeDropdown();
|
||||
resetState();
|
||||
};
|
||||
|
||||
const {
|
||||
availableSortDefinitionsState,
|
||||
onSortSelectState,
|
||||
isSortSelectedState,
|
||||
} = useSortDropdown({
|
||||
sortDropdownId: VIEW_SORT_DROPDOWN_ID,
|
||||
});
|
||||
|
||||
const isSortSelected = useRecoilValue(isSortSelectedState);
|
||||
const availableSortDefinitions = useRecoilValue(
|
||||
availableSortDefinitionsState,
|
||||
);
|
||||
const onSortSelect = useRecoilValue(onSortSelectState);
|
||||
|
||||
const handleAddSort = (selectedSortDefinition: SortDefinition) => {
|
||||
closeSortDropdown();
|
||||
onSortSelect?.({
|
||||
fieldMetadataId: selectedSortDefinition.fieldMetadataId,
|
||||
direction: selectedSortDirection,
|
||||
definition: selectedSortDefinition,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
isSortDirectionMenuUnfolded,
|
||||
setIsSortDirectionMenuUnfolded,
|
||||
selectedSortDirection,
|
||||
setSelectedSortDirection,
|
||||
toggleSortDropdown,
|
||||
resetState,
|
||||
isSortSelected,
|
||||
availableSortDefinitions,
|
||||
handleAddSort,
|
||||
};
|
||||
};
|
@ -0,0 +1,8 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
const isSortDirectionMenuUnfoldedState = atom({
|
||||
key: 'isSortDirectionMenuUnfoldedState',
|
||||
default: false,
|
||||
});
|
||||
|
||||
export default isSortDirectionMenuUnfoldedState;
|
@ -0,0 +1,10 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
import { SortDirection } from '@/object-record/object-sort-dropdown/types/SortDirection';
|
||||
|
||||
const selectedSortDirectionState = atom<SortDirection>({
|
||||
key: 'selectedSortDirectionState',
|
||||
default: 'asc',
|
||||
});
|
||||
|
||||
export default selectedSortDirectionState;
|
@ -4,6 +4,8 @@ import { useRecoilValue } from 'recoil';
|
||||
import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useRecordActionBar } from '@/object-record/record-action-bar/hooks/useRecordActionBar';
|
||||
import { useHandleToggleColumnFilter } from '@/object-record/record-index/hooks/useHandleToggleColumnFilter';
|
||||
import { useHandleToggleColumnSort } from '@/object-record/record-index/hooks/useHandleToggleColumnSort';
|
||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||
import { useSetRecordCountInCurrentView } from '@/views/hooks/useSetRecordCountInCurrentView';
|
||||
|
||||
@ -23,6 +25,8 @@ export const RecordIndexTableContainerEffect = ({
|
||||
setOnEntityCountChange,
|
||||
resetTableRowSelection,
|
||||
selectedRowIdsSelector,
|
||||
setOnToggleColumnFilter,
|
||||
setOnToggleColumnSort,
|
||||
} = useRecordTable({
|
||||
recordTableId,
|
||||
});
|
||||
@ -49,6 +53,30 @@ export const RecordIndexTableContainerEffect = ({
|
||||
callback: resetTableRowSelection,
|
||||
});
|
||||
|
||||
const handleToggleColumnFilter = useHandleToggleColumnFilter({
|
||||
objectNameSingular,
|
||||
viewBarId,
|
||||
});
|
||||
|
||||
const handleToggleColumnSort = useHandleToggleColumnSort({
|
||||
objectNameSingular,
|
||||
viewBarId,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setOnToggleColumnFilter(
|
||||
() => (fieldMetadataId: string) =>
|
||||
handleToggleColumnFilter(fieldMetadataId),
|
||||
);
|
||||
}, [setOnToggleColumnFilter, handleToggleColumnFilter]);
|
||||
|
||||
useEffect(() => {
|
||||
setOnToggleColumnSort(
|
||||
() => (fieldMetadataId: string) =>
|
||||
handleToggleColumnSort(fieldMetadataId),
|
||||
);
|
||||
}, [setOnToggleColumnSort, handleToggleColumnSort]);
|
||||
|
||||
useEffect(() => {
|
||||
setActionBarEntries?.();
|
||||
setContextMenuEntries?.();
|
||||
|
@ -0,0 +1,71 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
||||
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
|
||||
import { getOperandsForFilterType } from '@/object-record/object-filter-dropdown/utils/getOperandsForFilterType';
|
||||
import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2';
|
||||
import { useCombinedViewFilters } from '@/views/hooks/useCombinedViewFilters';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
type UseHandleToggleColumnFilterProps = {
|
||||
objectNameSingular: string;
|
||||
viewBarId: string;
|
||||
};
|
||||
|
||||
export const useHandleToggleColumnFilter = ({
|
||||
viewBarId,
|
||||
objectNameSingular,
|
||||
}: UseHandleToggleColumnFilterProps) => {
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const { columnDefinitions } =
|
||||
useColumnDefinitionsFromFieldMetadata(objectMetadataItem);
|
||||
|
||||
const { upsertCombinedViewFilter } = useCombinedViewFilters(viewBarId);
|
||||
const { openDropdown } = useDropdownV2();
|
||||
|
||||
const handleToggleColumnFilter = useCallback(
|
||||
(fieldMetadataId: string) => {
|
||||
const correspondingColumnDefinition = columnDefinitions.find(
|
||||
(columnDefinition) =>
|
||||
columnDefinition.fieldMetadataId === fieldMetadataId,
|
||||
);
|
||||
|
||||
if (!isDefined(correspondingColumnDefinition)) return;
|
||||
|
||||
const filterType = getFilterTypeFromFieldType(
|
||||
correspondingColumnDefinition?.type,
|
||||
);
|
||||
|
||||
const availableOperandsForFilter = getOperandsForFilterType(filterType);
|
||||
|
||||
const defaultOperand = availableOperandsForFilter[0];
|
||||
|
||||
const newFilter: Filter = {
|
||||
fieldMetadataId,
|
||||
operand: defaultOperand,
|
||||
displayValue: '',
|
||||
definition: {
|
||||
label: correspondingColumnDefinition.label,
|
||||
iconName: correspondingColumnDefinition.iconName,
|
||||
fieldMetadataId,
|
||||
type: filterType,
|
||||
},
|
||||
value: '',
|
||||
};
|
||||
|
||||
upsertCombinedViewFilter(newFilter);
|
||||
|
||||
openDropdown(fieldMetadataId, {
|
||||
scope: fieldMetadataId,
|
||||
});
|
||||
},
|
||||
[columnDefinitions, upsertCombinedViewFilter, openDropdown],
|
||||
);
|
||||
|
||||
return handleToggleColumnFilter;
|
||||
};
|
@ -0,0 +1,52 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { Sort } from '@/object-record/object-sort-dropdown/types/Sort';
|
||||
import { useCombinedViewSorts } from '@/views/hooks/useCombinedViewSorts';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
type UseHandleToggleColumnSortProps = {
|
||||
objectNameSingular: string;
|
||||
viewBarId: string;
|
||||
};
|
||||
|
||||
export const useHandleToggleColumnSort = ({
|
||||
viewBarId,
|
||||
objectNameSingular,
|
||||
}: UseHandleToggleColumnSortProps) => {
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const { columnDefinitions } =
|
||||
useColumnDefinitionsFromFieldMetadata(objectMetadataItem);
|
||||
|
||||
const { upsertCombinedViewSort } = useCombinedViewSorts(viewBarId);
|
||||
|
||||
const handleToggleColumnSort = useCallback(
|
||||
(fieldMetadataId: string) => {
|
||||
const correspondingColumnDefinition = columnDefinitions.find(
|
||||
(columnDefinition) =>
|
||||
columnDefinition.fieldMetadataId === fieldMetadataId,
|
||||
);
|
||||
|
||||
if (!isDefined(correspondingColumnDefinition)) return;
|
||||
|
||||
const newSort: Sort = {
|
||||
fieldMetadataId,
|
||||
definition: {
|
||||
fieldMetadataId,
|
||||
label: correspondingColumnDefinition.label,
|
||||
iconName: correspondingColumnDefinition.iconName,
|
||||
},
|
||||
direction: 'asc',
|
||||
};
|
||||
|
||||
upsertCombinedViewSort(newSort);
|
||||
},
|
||||
[columnDefinitions, upsertCombinedViewSort],
|
||||
);
|
||||
|
||||
return handleToggleColumnSort;
|
||||
};
|
@ -1,9 +1,16 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { IconArrowLeft, IconArrowRight, IconEyeOff } from 'twenty-ui';
|
||||
import {
|
||||
IconArrowLeft,
|
||||
IconArrowRight,
|
||||
IconEyeOff,
|
||||
IconFilter,
|
||||
IconSortDescending,
|
||||
} from 'twenty-ui';
|
||||
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
|
||||
@ -17,7 +24,11 @@ export type RecordTableColumnDropdownMenuProps = {
|
||||
export const RecordTableColumnDropdownMenu = ({
|
||||
column,
|
||||
}: RecordTableColumnDropdownMenuProps) => {
|
||||
const { visibleTableColumnsSelector } = useRecordTableStates();
|
||||
const {
|
||||
visibleTableColumnsSelector,
|
||||
onToggleColumnFilterState,
|
||||
onToggleColumnSortState,
|
||||
} = useRecordTableStates();
|
||||
|
||||
const visibleTableColumns = useRecoilValue(visibleTableColumnsSelector());
|
||||
|
||||
@ -55,8 +66,42 @@ export const RecordTableColumnDropdownMenu = ({
|
||||
handleColumnVisibilityChange(column);
|
||||
};
|
||||
|
||||
const onToggleColumnFilter = useRecoilValue(onToggleColumnFilterState);
|
||||
const onToggleColumnSort = useRecoilValue(onToggleColumnSortState);
|
||||
|
||||
const handleSortClick = () => {
|
||||
closeDropdown();
|
||||
|
||||
onToggleColumnSort?.(column.fieldMetadataId);
|
||||
};
|
||||
|
||||
const handleFilterClick = () => {
|
||||
closeDropdown();
|
||||
|
||||
onToggleColumnFilter?.(column.fieldMetadataId);
|
||||
};
|
||||
|
||||
const isSortable = column.isSortable === true;
|
||||
const isFilterable = column.isFilterable === true;
|
||||
const showSeparator = isFilterable || isSortable;
|
||||
|
||||
return (
|
||||
<DropdownMenuItemsContainer>
|
||||
{isFilterable && (
|
||||
<MenuItem
|
||||
LeftIcon={IconFilter}
|
||||
onClick={handleFilterClick}
|
||||
text="Filter"
|
||||
/>
|
||||
)}
|
||||
{isSortable && (
|
||||
<MenuItem
|
||||
LeftIcon={IconSortDescending}
|
||||
onClick={handleSortClick}
|
||||
text="Sort"
|
||||
/>
|
||||
)}
|
||||
{showSeparator && <DropdownMenuSeparator />}
|
||||
{canMoveLeft && (
|
||||
<MenuItem
|
||||
LeftIcon={IconArrowLeft}
|
||||
|
@ -10,6 +10,8 @@ import { isTableCellInEditModeComponentFamilyState } from '@/object-record/recor
|
||||
import { numberOfTableRowsComponentState } from '@/object-record/record-table/states/numberOfTableRowsComponentState';
|
||||
import { onColumnsChangeComponentState } from '@/object-record/record-table/states/onColumnsChangeComponentState';
|
||||
import { onEntityCountChangeComponentState } from '@/object-record/record-table/states/onEntityCountChangeComponentState';
|
||||
import { onToggleColumnFilterComponentState } from '@/object-record/record-table/states/onToggleColumnFilterComponentState';
|
||||
import { onToggleColumnSortComponentState } from '@/object-record/record-table/states/onToggleColumnSortComponentState';
|
||||
import { resizeFieldOffsetComponentState } from '@/object-record/record-table/states/resizeFieldOffsetComponentState';
|
||||
import { allRowsSelectedStatusComponentSelector } from '@/object-record/record-table/states/selectors/allRowsSelectedStatusComponentSelector';
|
||||
import { hiddenTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/hiddenTableColumnsComponentSelector';
|
||||
@ -49,6 +51,14 @@ export const useRecordTableStates = (recordTableId?: string) => {
|
||||
tableColumnsComponentState,
|
||||
scopeId,
|
||||
),
|
||||
onToggleColumnFilterState: extractComponentState(
|
||||
onToggleColumnFilterComponentState,
|
||||
scopeId,
|
||||
),
|
||||
onToggleColumnSortState: extractComponentState(
|
||||
onToggleColumnSortComponentState,
|
||||
scopeId,
|
||||
),
|
||||
onColumnsChangeState: extractComponentState(
|
||||
onColumnsChangeComponentState,
|
||||
scopeId,
|
||||
|
@ -42,6 +42,8 @@ export const useRecordTable = (props?: useRecordTableProps) => {
|
||||
isRecordTableInitialLoadingState,
|
||||
tableLastRowVisibleState,
|
||||
selectedRowIdsSelector,
|
||||
onToggleColumnFilterState,
|
||||
onToggleColumnSortState,
|
||||
} = useRecordTableStates(recordTableId);
|
||||
|
||||
const setAvailableTableColumns = useRecoilCallback(
|
||||
@ -70,6 +72,9 @@ export const useRecordTable = (props?: useRecordTableProps) => {
|
||||
|
||||
const setOnColumnsChange = useSetRecoilState(onColumnsChangeState);
|
||||
|
||||
const setOnToggleColumnFilter = useSetRecoilState(onToggleColumnFilterState);
|
||||
const setOnToggleColumnSort = useSetRecoilState(onToggleColumnSortState);
|
||||
|
||||
const setIsRecordTableInitialLoading = useSetRecoilState(
|
||||
isRecordTableInitialLoadingState,
|
||||
);
|
||||
@ -215,5 +220,7 @@ export const useRecordTable = (props?: useRecordTableProps) => {
|
||||
isSomeCellInEditModeState,
|
||||
selectedRowIdsSelector,
|
||||
setHasUserSelectedAllRows,
|
||||
setOnToggleColumnFilter,
|
||||
setOnToggleColumnSort,
|
||||
};
|
||||
};
|
||||
|
@ -0,0 +1,8 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
export const onToggleColumnFilterComponentState = createComponentState<
|
||||
((fieldMetadataId: string) => void) | undefined
|
||||
>({
|
||||
key: 'onToggleColumnFilterComponentState',
|
||||
defaultValue: undefined,
|
||||
});
|
@ -0,0 +1,8 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
export const onToggleColumnSortComponentState = createComponentState<
|
||||
((fieldMetadataId: string) => void) | undefined
|
||||
>({
|
||||
key: 'onToggleColumnSortComponentState',
|
||||
defaultValue: undefined,
|
||||
});
|
@ -7,4 +7,6 @@ export type ColumnDefinition<T extends FieldMetadata> = FieldDefinition<T> & {
|
||||
isLabelIdentifier?: boolean;
|
||||
isVisible?: boolean;
|
||||
viewFieldId?: string;
|
||||
isFilterable?: boolean;
|
||||
isSortable?: boolean;
|
||||
};
|
||||
|
@ -14,6 +14,7 @@ import { HotkeyEffect } from '@/ui/utilities/hotkey/components/HotkeyEffect';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
import { useDropdown } from '../hooks/useDropdown';
|
||||
@ -92,7 +93,7 @@ export const Dropdown = ({
|
||||
});
|
||||
|
||||
useInternalHotkeyScopeManagement({
|
||||
dropdownScopeId: `${dropdownId}-scope`,
|
||||
dropdownScopeId: getScopeIdFromComponentId(dropdownId),
|
||||
dropdownHotkeyScopeFromParent: dropdownHotkeyScope,
|
||||
});
|
||||
|
||||
@ -106,7 +107,7 @@ export const Dropdown = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<DropdownScope dropdownScopeId={`${dropdownId}-scope`}>
|
||||
<DropdownScope dropdownScopeId={getScopeIdFromComponentId(dropdownId)}>
|
||||
<div ref={containerRef} className={className}>
|
||||
{clickableComponent && (
|
||||
<div
|
||||
|
@ -0,0 +1,85 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { dropdownHotkeyComponentState } from '@/ui/layout/dropdown/states/dropdownHotkeyComponentState';
|
||||
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
|
||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
export const useDropdownV2 = () => {
|
||||
const {
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
goBackToPreviousHotkeyScope,
|
||||
} = usePreviousHotkeyScope();
|
||||
|
||||
const closeDropdown = useRecoilCallback(
|
||||
({ set }) =>
|
||||
(specificComponentId: string) => {
|
||||
const scopeId = getScopeIdFromComponentId(specificComponentId);
|
||||
|
||||
goBackToPreviousHotkeyScope();
|
||||
set(
|
||||
isDropdownOpenComponentState({
|
||||
scopeId,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
},
|
||||
[goBackToPreviousHotkeyScope],
|
||||
);
|
||||
|
||||
const openDropdown = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
(specificComponentId: string, customHotkeyScope?: HotkeyScope) => {
|
||||
const scopeId = getScopeIdFromComponentId(specificComponentId);
|
||||
|
||||
const dropdownHotkeyScope = snapshot
|
||||
.getLoadable(dropdownHotkeyComponentState({ scopeId }))
|
||||
.getValue();
|
||||
|
||||
set(
|
||||
isDropdownOpenComponentState({
|
||||
scopeId,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
|
||||
if (isDefined(customHotkeyScope)) {
|
||||
setHotkeyScopeAndMemorizePreviousScope(
|
||||
customHotkeyScope.scope,
|
||||
customHotkeyScope.customScopes,
|
||||
);
|
||||
} else if (isDefined(dropdownHotkeyScope)) {
|
||||
setHotkeyScopeAndMemorizePreviousScope(
|
||||
dropdownHotkeyScope.scope,
|
||||
dropdownHotkeyScope.customScopes,
|
||||
);
|
||||
}
|
||||
},
|
||||
[setHotkeyScopeAndMemorizePreviousScope],
|
||||
);
|
||||
|
||||
const toggleDropdown = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
(specificComponentId: string) => {
|
||||
const scopeId = getScopeIdFromComponentId(specificComponentId);
|
||||
const isDropdownOpen = snapshot
|
||||
.getLoadable(isDropdownOpenComponentState({ scopeId }))
|
||||
.getValue();
|
||||
|
||||
if (isDropdownOpen) {
|
||||
closeDropdown(specificComponentId);
|
||||
} else {
|
||||
openDropdown(specificComponentId);
|
||||
}
|
||||
},
|
||||
[closeDropdown, openDropdown],
|
||||
);
|
||||
|
||||
return {
|
||||
closeDropdown,
|
||||
openDropdown,
|
||||
toggleDropdown,
|
||||
};
|
||||
};
|
@ -74,7 +74,7 @@ export const EditableFilterDropdownButton = ({
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
dropdownId={viewFilter.fieldMetadataId}
|
||||
dropdownId={viewFilterDropdownId}
|
||||
clickableComponent={
|
||||
<EditableFilterChip viewFilter={viewFilter} onRemove={handleRemove} />
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { AddObjectFilterFromDetailsButton } from '@/object-record/object-filter-dropdown/components/AddObjectFilterFromDetailsButton';
|
||||
import { ObjectFilterDropdownScope } from '@/object-record/object-filter-dropdown/scopes/ObjectFilterDropdownScope';
|
||||
import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope';
|
||||
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
|
||||
import { EditableFilterDropdownButton } from '@/views/components/EditableFilterDropdownButton';
|
||||
import { EditableSortChip } from '@/views/components/EditableSortChip';
|
||||
@ -161,7 +160,7 @@ export const ViewBarDetails = ({
|
||||
<EditableFilterDropdownButton
|
||||
viewFilter={viewFilter}
|
||||
hotkeyScope={{
|
||||
scope: FiltersHotkeyScope.ObjectFilterDropdownButton,
|
||||
scope: viewFilter.fieldMetadataId,
|
||||
}}
|
||||
viewFilterDropdownId={viewFilter.fieldMetadataId}
|
||||
/>
|
||||
|
@ -47,7 +47,9 @@ export const mapViewFieldsToColumnDefinitions = ({
|
||||
isLabelIdentifier,
|
||||
isVisible: isLabelIdentifier || viewField.isVisible,
|
||||
viewFieldId: viewField.id,
|
||||
};
|
||||
isSortable: correspondingColumnDefinition.isSortable,
|
||||
isFilterable: correspondingColumnDefinition.isFilterable,
|
||||
} as ColumnDefinition<FieldMetadata>;
|
||||
})
|
||||
.filter(isDefined);
|
||||
|
||||
|
@ -67,6 +67,7 @@ export {
|
||||
IconFileText,
|
||||
IconFileUpload,
|
||||
IconFileZip,
|
||||
IconFilter,
|
||||
IconFilterOff,
|
||||
IconFocusCentered,
|
||||
IconForbid,
|
||||
@ -119,6 +120,7 @@ export {
|
||||
IconSearch,
|
||||
IconSend,
|
||||
IconSettings,
|
||||
IconSortDescending,
|
||||
IconTable,
|
||||
IconTag,
|
||||
IconTarget,
|
||||
|
Loading…
Reference in New Issue
Block a user