2422 refactor scope components to improve dev experience (#2736)

* move scope inside record table

* fix imports

* update mock

* recordTable scope done

* RecordTable done

* fix board

* fix typo

* wip

* filter is working

* sort is working

* Tasks working

* Fix according to PR

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
bosiraphael
2023-11-28 18:24:18 +01:00
committed by GitHub
parent 9d3e000055
commit ade41c916d
60 changed files with 651 additions and 529 deletions

View File

@ -2,10 +2,18 @@ import { isNonEmptyString } from '@sniptt/guards';
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
import { PageAddButton } from '@/ui/layout/page/PageAddButton';
import { useFilter } from '@/ui/object/object-filter-dropdown/hooks/useFilter';
import { useFilterDropdown } from '@/ui/object/object-filter-dropdown/hooks/useFilterDropdown';
export const PageAddTaskButton = () => {
const { selectedFilter } = useFilter();
type PageAddTaskButtonProps = {
filterDropdownId: string;
};
export const PageAddTaskButton = ({
filterDropdownId,
}: PageAddTaskButtonProps) => {
const { selectedFilter } = useFilterDropdown({
filterDropdownId: filterDropdownId,
});
const openCreateActivity = useOpenCreateActivityDrawer();
const handleClick = () => {

View File

@ -13,6 +13,7 @@ import { AddTaskButton } from './AddTaskButton';
import { TaskList } from './TaskList';
type TaskGroupsProps = {
filterDropdownId?: string;
entity?: ActivityTargetableEntity;
showAddButton?: boolean;
};
@ -51,13 +52,17 @@ const StyledContainer = styled.div`
flex-direction: column;
`;
export const TaskGroups = ({ entity, showAddButton }: TaskGroupsProps) => {
export const TaskGroups = ({
filterDropdownId,
entity,
showAddButton,
}: TaskGroupsProps) => {
const {
todayOrPreviousTasks,
upcomingTasks,
unscheduledTasks,
completedTasks,
} = useTasks(entity);
} = useTasks({ filterDropdownId: filterDropdownId, entity });
const openCreateActivity = useOpenCreateActivityDrawer();

View File

@ -5,12 +5,21 @@ import { undefined } from 'zod';
import { Activity } from '@/activities/types/Activity';
import { ActivityTargetableEntity } from '@/activities/types/ActivityTargetableEntity';
import { useFindManyObjectRecords } from '@/object-record/hooks/useFindManyObjectRecords';
import { useFilter } from '@/ui/object/object-filter-dropdown/hooks/useFilter';
import { useFilterDropdown } from '@/ui/object/object-filter-dropdown/hooks/useFilterDropdown';
import { parseDate } from '~/utils/date-utils';
import { isDefined } from '~/utils/isDefined';
export const useTasks = (entity?: ActivityTargetableEntity) => {
const { selectedFilter } = useFilter();
type UseTasksProps = {
filterDropdownId?: string;
entity?: ActivityTargetableEntity;
};
export const useTasks = (props?: UseTasksProps) => {
const { filterDropdownId, entity } = props ?? {};
const { selectedFilter } = useFilterDropdown({
filterDropdownId: filterDropdownId,
});
const { objects: activityTargets } = useFindManyObjectRecords({
objectNamePlural: 'activityTargets',

View File

@ -12,7 +12,6 @@ import { RecordBoardContextMenu } from '@/ui/object/record-board/context-menu/co
import { BoardOptionsDropdown } from '@/ui/object/record-board/options/components/BoardOptionsDropdown';
import { ViewBar } from '@/views/components/ViewBar';
import { useViewFields } from '@/views/hooks/internal/useViewFields';
import { ViewScope } from '@/views/scopes/ViewScope';
import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions';
import { HooksCompanyBoardEffect } from '../../components/HooksCompanyBoardEffect';
@ -36,41 +35,35 @@ export const CompanyBoard = ({
onColumnDelete,
onEditColumnTitle,
}: CompanyBoardProps) => {
const viewScopeId = 'company-board-view';
const viewBarId = 'company-board-view';
const { persistViewFields } = useViewFields(viewScopeId);
const { persistViewFields } = useViewFields(viewBarId);
return (
<ViewScope
viewScopeId={viewScopeId}
onViewFieldsChange={() => {}}
onViewFiltersChange={() => {}}
onViewSortsChange={() => {}}
>
<StyledContainer>
<BoardContext.Provider
value={{
BoardRecoilScopeContext: CompanyBoardRecoilScopeContext,
onFieldsChange: (fields) => {
persistViewFields(mapBoardFieldDefinitionsToViewFields(fields));
},
}}
>
<ViewBar
optionsDropdownButton={<BoardOptionsDropdown />}
optionsDropdownScopeId={BoardOptionsDropdownId}
/>
<HooksCompanyBoardEffect />
<RecordBoard
boardOptions={opportunitiesBoardOptions}
onColumnAdd={onColumnAdd}
onColumnDelete={onColumnDelete}
onEditColumnTitle={onEditColumnTitle}
/>
<RecordBoardActionBar />
<RecordBoardContextMenu />
</BoardContext.Provider>
</StyledContainer>
</ViewScope>
<StyledContainer>
<BoardContext.Provider
value={{
BoardRecoilScopeContext: CompanyBoardRecoilScopeContext,
onFieldsChange: (fields) => {
persistViewFields(mapBoardFieldDefinitionsToViewFields(fields));
},
}}
>
<ViewBar
viewBarId={viewBarId}
optionsDropdownButton={<BoardOptionsDropdown />}
optionsDropdownScopeId={BoardOptionsDropdownId}
/>
<HooksCompanyBoardEffect viewBarId={viewBarId} />
<RecordBoard
boardOptions={opportunitiesBoardOptions}
onColumnAdd={onColumnAdd}
onColumnDelete={onColumnDelete}
onEditColumnTitle={onEditColumnTitle}
/>
<RecordBoardActionBar />
<RecordBoardContextMenu />
</BoardContext.Provider>
</StyledContainer>
);
};

View File

@ -20,7 +20,7 @@ import { isBoardLoadedState } from '@/ui/object/record-board/states/isBoardLoade
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { useSetRecoilScopedStateV2 } from '@/ui/utilities/recoil-scope/hooks/useSetRecoilScopedStateV2';
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
import { useView } from '@/views/hooks/useView';
import { useViewBar } from '@/views/hooks/useViewBar';
import { ViewType } from '@/views/types/ViewType';
import { mapViewFieldsToBoardFieldDefinitions } from '@/views/utils/mapViewFieldsToBoardFieldDefinitions';
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
@ -30,7 +30,13 @@ import { isDefined } from '~/utils/isDefined';
import { useUpdateCompanyBoardCardIds } from '../hooks/useUpdateBoardCardIds';
import { useUpdateCompanyBoard } from '../hooks/useUpdateCompanyBoardColumns';
export const HooksCompanyBoardEffect = () => {
type HooksCompanyBoardEffectProps = {
viewBarId: string;
};
export const HooksCompanyBoardEffect = ({
viewBarId,
}: HooksCompanyBoardEffectProps) => {
const {
setAvailableFilterDefinitions,
setAvailableSortDefinitions,
@ -38,13 +44,13 @@ export const HooksCompanyBoardEffect = () => {
setEntityCountInCurrentView,
setViewObjectMetadataId,
setViewType,
} = useView();
} = useViewBar({ viewBarId: viewBarId });
const {
currentViewFieldsState,
currentViewFiltersState,
currentViewSortsState,
} = useViewScopedStates();
} = useViewScopedStates({ viewScopeId: viewBarId });
const [pipelineSteps, setPipelineSteps] = useState<PipelineStep[]>([]);
const [opportunities, setOpportunities] = useState<Opportunity[]>([]);

View File

@ -1,5 +1,4 @@
import styled from '@emotion/styled';
import { useRecoilCallback } from 'recoil';
import { useComputeDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useComputeDefinitionsFromFieldMetadata';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
@ -7,11 +6,7 @@ import { RecordTable } from '@/ui/object/record-table/components/RecordTable';
import { TableOptionsDropdownId } from '@/ui/object/record-table/constants/TableOptionsDropdownId';
import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable';
import { TableOptionsDropdown } from '@/ui/object/record-table/options/components/TableOptionsDropdown';
import { RecordTableScope } from '@/ui/object/record-table/scopes/RecordTableScope';
import { ViewBar } from '@/views/components/ViewBar';
import { useViewFields } from '@/views/hooks/internal/useViewFields';
import { ViewScope } from '@/views/scopes/ViewScope';
import { mapColumnDefinitionsToViewFields } from '@/views/utils/mapColumnDefinitionToViewField';
import { mapViewFieldsToColumnDefinitions } from '@/views/utils/mapViewFieldsToColumnDefinitions';
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts';
@ -45,13 +40,11 @@ export const RecordTableContainer = ({
objectNameSingular: foundObjectMetadataItem?.nameSingular,
});
const tableScopeId = objectNamePlural ?? '';
const viewScopeId = objectNamePlural ?? '';
const { persistViewFields } = useViewFields(viewScopeId);
const recordTableId = objectNamePlural ?? '';
const viewBarId = objectNamePlural ?? '';
const { setTableFilters, setTableSorts, setTableColumns } = useRecordTable({
recordTableScopeId: tableScopeId,
recordTableScopeId: recordTableId,
});
const updateEntity = ({
@ -71,35 +64,31 @@ export const RecordTableContainer = ({
};
return (
<ViewScope
viewScopeId={viewScopeId}
onViewFieldsChange={(viewFields) => {
setTableColumns(
mapViewFieldsToColumnDefinitions(viewFields, columnDefinitions),
);
}}
onViewFiltersChange={(viewFilters) => {
setTableFilters(mapViewFiltersToFilters(viewFilters));
}}
onViewSortsChange={(viewSorts) => {
setTableSorts(mapViewSortsToSorts(viewSorts));
}}
>
<StyledContainer>
<RecordTableScope
recordTableScopeId={tableScopeId}
onColumnsChange={useRecoilCallback(() => (columns) => {
persistViewFields(mapColumnDefinitionsToViewFields(columns));
})}
>
<ViewBar
optionsDropdownButton={<TableOptionsDropdown />}
optionsDropdownScopeId={TableOptionsDropdownId}
/>
<RecordTableEffect />
<RecordTable updateEntityMutation={updateEntity} />
</RecordTableScope>
</StyledContainer>
</ViewScope>
<StyledContainer>
<ViewBar
viewBarId={viewBarId}
optionsDropdownButton={
<TableOptionsDropdown recordTableId={recordTableId} />
}
optionsDropdownScopeId={TableOptionsDropdownId}
onViewFieldsChange={(viewFields) => {
setTableColumns(
mapViewFieldsToColumnDefinitions(viewFields, columnDefinitions),
);
}}
onViewFiltersChange={(viewFilters) => {
setTableFilters(mapViewFiltersToFilters(viewFilters));
}}
onViewSortsChange={(viewSorts) => {
setTableSorts(mapViewSortsToSorts(viewSorts));
}}
/>
<RecordTableEffect recordTableId={recordTableId} viewBarId={viewBarId} />
<RecordTable
recordTableId={recordTableId}
viewBarId={viewBarId}
updateEntityMutation={updateEntity}
/>
</StyledContainer>
);
};

View File

@ -5,16 +5,22 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
import { useRecordTableContextMenuEntries } from '@/object-record/hooks/useRecordTableContextMenuEntries';
import { filterAvailableTableColumns } from '@/object-record/utils/filterAvailableTableColumns';
import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable';
import { useView } from '@/views/hooks/useView';
import { useViewBar } from '@/views/hooks/useViewBar';
import { ViewType } from '@/views/types/ViewType';
export const RecordTableEffect = () => {
export const RecordTableEffect = ({
recordTableId,
viewBarId,
}: {
recordTableId: string;
viewBarId: string;
}) => {
const {
scopeId: objectNamePlural,
setAvailableTableColumns,
setOnEntityCountChange,
setObjectMetadataConfig,
} = useRecordTable();
} = useRecordTable({ recordTableScopeId: recordTableId });
const {
objectMetadataItem,
@ -34,7 +40,7 @@ export const RecordTableEffect = () => {
setViewType,
setViewObjectMetadataId,
setEntityCountInCurrentView,
} = useView();
} = useViewBar({ viewBarId });
useEffect(() => {
if (basePathToShowPage && labelIdentifierFieldMetadataId) {
@ -80,7 +86,9 @@ export const RecordTableEffect = () => {
]);
const { setActionBarEntries, setContextMenuEntries } =
useRecordTableContextMenuEntries();
useRecordTableContextMenuEntries({
recordTableScopeId: recordTableId,
});
useEffect(() => {
setActionBarEntries?.();

View File

@ -9,19 +9,33 @@ import { IconHeart, IconHeartOff, IconTrash } from '@/ui/display/icon';
import { actionBarEntriesState } from '@/ui/navigation/action-bar/states/actionBarEntriesState';
import { contextMenuEntriesState } from '@/ui/navigation/context-menu/states/contextMenuEntriesState';
import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable';
import { RecordTableScopeInternalContext } from '@/ui/object/record-table/scopes/scope-internal-context/RecordTableScopeInternalContext';
import { selectedRowIdsSelector } from '@/ui/object/record-table/states/selectors/selectedRowIdsSelector';
import { tableRowIdsState } from '@/ui/object/record-table/states/tableRowIdsState';
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
type useRecordTableContextMenuEntriesProps = {
recordTableScopeId?: string;
};
// TODO: refactor this
export const useRecordTableContextMenuEntries = () => {
export const useRecordTableContextMenuEntries = (
props?: useRecordTableContextMenuEntriesProps,
) => {
const scopeId = useAvailableScopeIdOrThrow(
RecordTableScopeInternalContext,
props?.recordTableScopeId,
);
const setContextMenuEntries = useSetRecoilState(contextMenuEntriesState);
const setActionBarEntriesState = useSetRecoilState(actionBarEntriesState);
const setTableRowIds = useSetRecoilState(tableRowIdsState);
const selectedRowIds = useRecoilValue(selectedRowIdsSelector);
const { scopeId: objectNamePlural, resetTableRowSelection } =
useRecordTable();
const { scopeId: objectNamePlural, resetTableRowSelection } = useRecordTable({
recordTableScopeId: scopeId,
});
const { objectMetadataItem: foundObjectMetadataItem } = useObjectMetadataItem(
{

View File

@ -5,11 +5,11 @@ import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable';
import { isFetchingRecordTableDataState } from '@/ui/object/record-table/states/isFetchingRecordTableDataState';
import { numberOfTableRowsState } from '@/ui/object/record-table/states/numberOfTableRowsState';
import { tableRowIdsState } from '@/ui/object/record-table/states/tableRowIdsState';
import { useView } from '@/views/hooks/useView';
import { useViewBar } from '@/views/hooks/useViewBar';
export const useSetRecordTableData = () => {
const { resetTableRowSelection } = useRecordTable();
const { setEntityCountInCurrentView } = useView();
const { setEntityCountInCurrentView } = useViewBar();
return useRecoilCallback(
({ set, snapshot }) =>

View File

@ -4,9 +4,7 @@ import { SignInBackgroundMockContainerEffect } from '@/sign-in-background-mock/c
import { RecordTable } from '@/ui/object/record-table/components/RecordTable';
import { TableOptionsDropdownId } from '@/ui/object/record-table/constants/TableOptionsDropdownId';
import { TableOptionsDropdown } from '@/ui/object/record-table/options/components/TableOptionsDropdown';
import { RecordTableScope } from '@/ui/object/record-table/scopes/RecordTableScope';
import { ViewBar } from '@/views/components/ViewBar';
import { ViewScope } from '@/views/scopes/ViewScope';
const StyledContainer = styled.div`
display: flex;
@ -16,29 +14,27 @@ const StyledContainer = styled.div`
`;
export const SignInBackgroundMockContainer = () => {
const tableScopeId = 'sign-in-background-mock-table';
const viewScopeId = 'sign-in-background-mock-view';
const recordTableId = 'sign-in-background-mock-table';
const viewBarId = 'sign-in-background-mock-view';
return (
<ViewScope
viewScopeId={viewScopeId}
onViewFieldsChange={() => {}}
onViewFiltersChange={() => {}}
onViewSortsChange={() => {}}
>
<StyledContainer>
<RecordTableScope
recordTableScopeId={tableScopeId}
onColumnsChange={() => {}}
>
<ViewBar
optionsDropdownButton={<TableOptionsDropdown />}
optionsDropdownScopeId={TableOptionsDropdownId}
/>
<SignInBackgroundMockContainerEffect />
<RecordTable updateEntityMutation={() => {}} />
</RecordTableScope>
</StyledContainer>
</ViewScope>
<StyledContainer>
<ViewBar
viewBarId={viewBarId}
optionsDropdownButton={
<TableOptionsDropdown recordTableId={recordTableId} />
}
optionsDropdownScopeId={TableOptionsDropdownId}
/>
<SignInBackgroundMockContainerEffect
recordTableId={recordTableId}
viewId={viewBarId}
/>
<RecordTable
recordTableId={recordTableId}
viewBarId={viewBarId}
updateEntityMutation={() => {}}
/>
</StyledContainer>
);
};

View File

@ -11,11 +11,19 @@ import {
} from '@/sign-in-background-mock/constants/signInBackgroundMockDefinitions';
import { signInBackgroundMockViewFields } from '@/sign-in-background-mock/constants/signInBackgroundMockViewFields';
import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable';
import { useView } from '@/views/hooks/useView';
import { useViewBar } from '@/views/hooks/useViewBar';
import { ViewType } from '@/views/types/ViewType';
import { mapViewFieldsToColumnDefinitions } from '@/views/utils/mapViewFieldsToColumnDefinitions';
export const SignInBackgroundMockContainerEffect = () => {
type SignInBackgroundMockContainerEffectProps = {
recordTableId: string;
viewId: string;
};
export const SignInBackgroundMockContainerEffect = ({
recordTableId,
viewId,
}: SignInBackgroundMockContainerEffectProps) => {
const {
scopeId: objectNamePlural,
setAvailableTableColumns,
@ -23,7 +31,9 @@ export const SignInBackgroundMockContainerEffect = () => {
setRecordTableData,
setTableColumns,
setObjectMetadataConfig,
} = useRecordTable();
} = useRecordTable({
recordTableScopeId: recordTableId,
});
const { objectMetadataItem } = useObjectMetadataItem({
objectNamePlural,
@ -36,7 +46,7 @@ export const SignInBackgroundMockContainerEffect = () => {
setViewType,
setViewObjectMetadataId,
setEntityCountInCurrentView,
} = useView();
} = useViewBar({ viewBarId: viewId });
useEffect(() => {
setViewObjectMetadataId?.('company-mock-object-metadata-id');
@ -76,7 +86,9 @@ export const SignInBackgroundMockContainerEffect = () => {
}, [setObjectMetadataConfig]);
const { setActionBarEntries, setContextMenuEntries } =
useRecordTableContextMenuEntries();
useRecordTableContextMenuEntries({
recordTableScopeId: recordTableId,
});
useEffect(() => {
setActionBarEntries?.();

View File

@ -1,16 +1,24 @@
import { IconPlus } from '@/ui/display/icon';
import { LightButton } from '@/ui/input/button/components/LightButton';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { useFilter } from '@/ui/object/object-filter-dropdown/hooks/useFilter';
import { useFilterDropdown } from '@/ui/object/object-filter-dropdown/hooks/useFilterDropdown';
import { ObjectFilterDropdownId } from '../constants/ObjectFilterDropdownId';
export const AddObjectFilterFromDetailsButton = () => {
type AddObjectFilterFromDetailsButtonProps = {
filterDropdownId?: string;
};
export const AddObjectFilterFromDetailsButton = ({
filterDropdownId,
}: AddObjectFilterFromDetailsButtonProps) => {
const { toggleDropdown } = useDropdown({
dropdownScopeId: ObjectFilterDropdownId,
});
const { resetFilter } = useFilter();
const { resetFilter } = useFilterDropdown({
filterDropdownId: filterDropdownId,
});
const handleClick = () => {
resetFilter();

View File

@ -1,11 +1,11 @@
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { useFilterDropdown } from '@/ui/object/object-filter-dropdown/hooks/useFilterDropdown';
import { ObjectFilterDropdownId } from '../constants/ObjectFilterDropdownId';
import { useFilter } from '../hooks/useFilter';
export const MultipleFiltersButton = () => {
const { resetFilter } = useFilter();
const { resetFilter } = useFilterDropdown();
const { isDropdownOpen, toggleDropdown } = useDropdown({
dropdownScopeId: ObjectFilterDropdownId,

View File

@ -1,6 +1,6 @@
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
import { useFilter } from '@/ui/object/object-filter-dropdown/hooks/useFilter';
import { useFilterDropdown } from '@/ui/object/object-filter-dropdown/hooks/useFilterDropdown';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { ObjectFilterDropdownId } from '../constants/ObjectFilterDropdownId';
@ -15,7 +15,7 @@ type MultipleFiltersDropdownButtonProps = {
export const MultipleFiltersDropdownButton = ({
hotkeyScope,
}: MultipleFiltersDropdownButtonProps) => {
const { resetFilter } = useFilter();
const { resetFilter } = useFilterDropdown();
return (
<DropdownScope dropdownScopeId={ObjectFilterDropdownId}>

View File

@ -1,6 +1,5 @@
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { useFilter } from '../hooks/useFilter';
import { useFilterDropdown } from '@/ui/object/object-filter-dropdown/hooks/useFilterDropdown';
import { MultipleFiltersDropdownFilterOnFilterChangedEffect } from './MultipleFiltersDropdownFilterOnFilterChangedEffect';
import { ObjectFilterDropdownDateSearchInput } from './ObjectFilterDropdownDateSearchInput';
@ -17,7 +16,7 @@ export const MultipleFiltersDropdownContent = () => {
isObjectFilterDropdownOperandSelectUnfolded,
filterDefinitionUsedInDropdown,
selectedOperandInDropdown,
} = useFilter();
} = useFilterDropdown();
return (
<>

View File

@ -1,19 +1,22 @@
import { useFilterDropdown } from '@/ui/object/object-filter-dropdown/hooks/useFilterDropdown';
import { ObjectFilterDropdownScope } from '@/ui/object/object-filter-dropdown/scopes/ObjectFilterDropdownScope';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { useFilter } from '../hooks/useFilter';
import { MultipleFiltersDropdownButton } from './MultipleFiltersDropdownButton';
import { SingleEntityObjectFilterDropdownButton } from './SingleEntityObjectFilterDropdownButton';
type ObjectFilterDropdownButtonProps = {
filterDropdownId: string;
hotkeyScope: HotkeyScope;
};
export const ObjectFilterDropdownButton = ({
filterDropdownId,
hotkeyScope,
}: ObjectFilterDropdownButtonProps) => {
const { availableFilterDefinitions } = useFilter();
const { availableFilterDefinitions } = useFilterDropdown({
filterDropdownId: filterDropdownId,
});
const hasOnlyOneEntityFilter =
availableFilterDefinitions.length === 1 &&
availableFilterDefinitions[0].type === 'RELATION';
@ -22,9 +25,13 @@ export const ObjectFilterDropdownButton = ({
return <></>;
}
return hasOnlyOneEntityFilter ? (
<SingleEntityObjectFilterDropdownButton hotkeyScope={hotkeyScope} />
) : (
<MultipleFiltersDropdownButton hotkeyScope={hotkeyScope} />
return (
<ObjectFilterDropdownScope filterScopeId={filterDropdownId}>
{hasOnlyOneEntityFilter ? (
<SingleEntityObjectFilterDropdownButton hotkeyScope={hotkeyScope} />
) : (
<MultipleFiltersDropdownButton hotkeyScope={hotkeyScope} />
)}
</ObjectFilterDropdownScope>
);
};

View File

@ -1,6 +1,5 @@
import { InternalDatePicker } from '@/ui/input/components/internal/date/components/InternalDatePicker';
import { useFilter } from '../hooks/useFilter';
import { useFilterDropdown } from '@/ui/object/object-filter-dropdown/hooks/useFilterDropdown';
export const ObjectFilterDropdownDateSearchInput = () => {
const {
@ -8,7 +7,7 @@ export const ObjectFilterDropdownDateSearchInput = () => {
selectedOperandInDropdown,
setIsObjectFilterDropdownUnfolded,
selectFilter,
} = useFilter();
} = useFilterDropdown();
const handleChange = (date: Date) => {
if (!filterDefinitionUsedInDropdown || !selectedOperandInDropdown) return;

View File

@ -1,8 +1,7 @@
import { ChangeEvent } from 'react';
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { useFilter } from '../hooks/useFilter';
import { useFilterDropdown } from '@/ui/object/object-filter-dropdown/hooks/useFilterDropdown';
export const ObjectFilterDropdownEntitySearchInput = () => {
const {
@ -10,7 +9,7 @@ export const ObjectFilterDropdownEntitySearchInput = () => {
selectedOperandInDropdown,
objectFilterDropdownSearchInput,
setObjectFilterDropdownSearchInput,
} = useFilter();
} = useFilterDropdown();
return (
filterDefinitionUsedInDropdown &&

View File

@ -4,10 +4,9 @@ import { EntitiesForMultipleEntitySelect } from '@/ui/input/relation-picker/comp
import { SingleEntitySelectBase } from '@/ui/input/relation-picker/components/SingleEntitySelectBase';
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { useFilterDropdown } from '@/ui/object/object-filter-dropdown/hooks/useFilterDropdown';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { useFilter } from '../hooks/useFilter';
export const ObjectFilterDropdownEntitySearchSelect = ({
entitiesForSelect,
}: {
@ -20,7 +19,7 @@ export const ObjectFilterDropdownEntitySearchSelect = ({
objectFilterDropdownSearchInput,
selectedFilter,
selectFilter,
} = useFilter();
} = useFilterDropdown();
const { closeDropdown } = useDropdown();

View File

@ -4,15 +4,14 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
import { useRelationPicker } from '@/ui/input/components/internal/relation-picker/hooks/useRelationPicker';
import { ObjectFilterDropdownEntitySearchSelect } from '@/ui/object/object-filter-dropdown/components/ObjectFilterDropdownEntitySearchSelect';
import { useFilter } from '../hooks/useFilter';
import { useFilterDropdown } from '@/ui/object/object-filter-dropdown/hooks/useFilterDropdown';
export const ObjectFilterDropdownEntitySelect = () => {
const {
filterDefinitionUsedInDropdown,
objectFilterDropdownSearchInput,
objectFilterDropdownSelectedEntityId,
} = useFilter();
} = useFilterDropdown();
const objectMetadataNameSingular =
filterDefinitionUsedInDropdown?.relationObjectMetadataNameSingular ?? '';

View File

@ -2,9 +2,9 @@ import { useLazyLoadIcons } from '@/ui/input/hooks/useLazyLoadIcons';
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { useFilterDropdown } from '@/ui/object/object-filter-dropdown/hooks/useFilterDropdown';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { useFilter } from '../hooks/useFilter';
import { getOperandsForFilterType } from '../utils/getOperandsForFilterType';
export const ObjectFilterDropdownFilterSelect = () => {
@ -13,7 +13,7 @@ export const ObjectFilterDropdownFilterSelect = () => {
setSelectedOperandInDropdown,
setObjectFilterDropdownSearchInput,
availableFilterDefinitions,
} = useFilter();
} = useFilterDropdown();
const { icons } = useLazyLoadIcons();

View File

@ -1,15 +1,14 @@
import { ChangeEvent } from 'react';
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { useFilter } from '../hooks/useFilter';
import { useFilterDropdown } from '@/ui/object/object-filter-dropdown/hooks/useFilterDropdown';
export const ObjectFilterDropdownNumberSearchInput = () => {
const {
selectedOperandInDropdown,
filterDefinitionUsedInDropdown,
selectFilter,
} = useFilter();
} = useFilterDropdown();
return (
filterDefinitionUsedInDropdown &&

View File

@ -1,7 +1,7 @@
import { IconChevronDown } from '@/ui/display/icon';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
import { useFilterDropdown } from '@/ui/object/object-filter-dropdown/hooks/useFilterDropdown';
import { useFilter } from '../hooks/useFilter';
import { getOperandLabel } from '../utils/getOperandLabel';
export const ObjectFilterDropdownOperandButton = () => {
@ -9,7 +9,7 @@ export const ObjectFilterDropdownOperandButton = () => {
selectedOperandInDropdown,
setIsObjectFilterDropdownOperandSelectUnfolded,
isObjectFilterDropdownOperandSelectUnfolded,
} = useFilter();
} = useFilterDropdown();
if (isObjectFilterDropdownOperandSelectUnfolded) {
return null;

View File

@ -1,8 +1,8 @@
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { useFilterDropdown } from '@/ui/object/object-filter-dropdown/hooks/useFilterDropdown';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { useFilter } from '../hooks/useFilter';
import { getOperandLabel } from '../utils/getOperandLabel';
import { getOperandsForFilterType } from '../utils/getOperandsForFilterType';
@ -14,7 +14,7 @@ export const ObjectFilterDropdownOperandSelect = () => {
setIsObjectFilterDropdownOperandSelectUnfolded,
selectedFilter,
selectFilter,
} = useFilter();
} = useFilterDropdown();
const operandsForFilterType = getOperandsForFilterType(
filterDefinitionUsedInDropdown?.type,

View File

@ -1,8 +1,7 @@
import { ChangeEvent } from 'react';
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { useFilter } from '../hooks/useFilter';
import { useFilterDropdown } from '@/ui/object/object-filter-dropdown/hooks/useFilterDropdown';
export const ObjectFilterDropdownTextSearchInput = () => {
const {
@ -12,7 +11,7 @@ export const ObjectFilterDropdownTextSearchInput = () => {
setObjectFilterDropdownSearchInput,
selectedFilter,
selectFilter,
} = useFilter();
} = useFilterDropdown();
return (
filterDefinitionUsedInDropdown &&

View File

@ -5,10 +5,10 @@ import { IconChevronDown } from '@/ui/display/icon/index';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
import { useFilterDropdown } from '@/ui/object/object-filter-dropdown/hooks/useFilterDropdown';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { useFilter } from '../hooks/useFilter';
import { getOperandsForFilterType } from '../utils/getOperandsForFilterType';
import { GenericEntityFilterChip } from './GenericEntityFilterChip';
@ -25,7 +25,7 @@ export const SingleEntityObjectFilterDropdownButton = ({
selectedFilter,
setFilterDefinitionUsedInDropdown,
setSelectedOperandInDropdown,
} = useFilter();
} = useFilterDropdown();
const availableFilter = availableFilterDefinitions[0];

View File

@ -1,22 +1,21 @@
import { useCallback } from 'react';
import { useFilterDropdownStates } from '@/ui/object/object-filter-dropdown/hooks/useFilterDropdownStates';
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
import { useScopeInternalContextOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useScopeInternalContextOrThrow';
import { ObjectFilterDropdownScopeInternalContext } from '../scopes/scope-internal-context/ObjectFilterDropdownScopeInternalContext';
import { Filter } from '../types/Filter';
import { useFilterStates } from './useFilterStates';
type UseFilterProps = {
filterScopeId?: string;
type UseFilterDropdownProps = {
filterDropdownId?: string;
};
export const useFilter = (props?: UseFilterProps) => {
export const useFilterDropdown = (props?: UseFilterDropdownProps) => {
const scopeId = useAvailableScopeIdOrThrow(
ObjectFilterDropdownScopeInternalContext,
props?.filterScopeId,
props?.filterDropdownId,
);
const {
availableFilterDefinitions,
setAvailableFilterDefinitions,
@ -34,11 +33,9 @@ export const useFilter = (props?: UseFilterProps) => {
setSelectedFilter,
selectedOperandInDropdown,
setSelectedOperandInDropdown,
} = useFilterStates(scopeId);
const { onFilterSelect } = useScopeInternalContextOrThrow(
ObjectFilterDropdownScopeInternalContext,
);
onFilterSelect,
setOnFilterSelect,
} = useFilterDropdownStates(scopeId);
const selectFilter = useCallback(
(filter: Filter) => {
@ -82,5 +79,7 @@ export const useFilter = (props?: UseFilterProps) => {
setSelectedOperandInDropdown,
selectFilter,
resetFilter,
onFilterSelect,
setOnFilterSelect,
};
};

View File

@ -1,3 +1,4 @@
import { onFilterSelectScopedState } from '@/ui/object/object-filter-dropdown/states/onFilterSelectScopedState';
import { useRecoilScopedStateV2 } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedStateV2';
import { availableFilterDefinitionsScopedState } from '../states/availableFilterDefinitionsScopedState';
@ -9,7 +10,7 @@ import { objectFilterDropdownSelectedEntityIdScopedState } from '../states/objec
import { selectedFilterScopedState } from '../states/selectedFilterScopedState';
import { selectedOperandInDropdownScopedState } from '../states/selectedOperandInDropdownScopedState';
export const useFilterStates = (scopeId: string) => {
export const useFilterDropdownStates = (scopeId: string) => {
const [availableFilterDefinitions, setAvailableFilterDefinitions] =
useRecoilScopedStateV2(availableFilterDefinitionsScopedState, scopeId);
@ -46,6 +47,11 @@ export const useFilterStates = (scopeId: string) => {
const [selectedOperandInDropdown, setSelectedOperandInDropdown] =
useRecoilScopedStateV2(selectedOperandInDropdownScopedState, scopeId);
const [onFilterSelect, setOnFilterSelect] = useRecoilScopedStateV2(
onFilterSelectScopedState,
scopeId,
);
return {
availableFilterDefinitions,
setAvailableFilterDefinitions,
@ -63,5 +69,7 @@ export const useFilterStates = (scopeId: string) => {
setSelectedFilter,
selectedOperandInDropdown,
setSelectedOperandInDropdown,
onFilterSelect,
setOnFilterSelect,
};
};

View File

@ -1,33 +1,20 @@
import { ReactNode } from 'react';
import { FilterDefinition } from '@/ui/object/object-filter-dropdown/types/FilterDefinition';
import { Filter } from '../types/Filter';
import { ObjectFilterDropdownScopeInitEffect } from './init-effect/ObjectFilterDropdownScopeInitEffect';
import { ObjectFilterDropdownScopeInternalContext } from './scope-internal-context/ObjectFilterDropdownScopeInternalContext';
type ObjectFilterDropdownScopeProps = {
children: ReactNode;
filterScopeId: string;
availableFilterDefinitions?: FilterDefinition[];
onFilterSelect?: (filter: Filter) => void;
};
export const ObjectFilterDropdownScope = ({
children,
filterScopeId,
availableFilterDefinitions,
onFilterSelect,
}: ObjectFilterDropdownScopeProps) => {
return (
<ObjectFilterDropdownScopeInternalContext.Provider
value={{ scopeId: filterScopeId, onFilterSelect }}
value={{ scopeId: filterScopeId }}
>
<ObjectFilterDropdownScopeInitEffect
filterScopeId={filterScopeId}
availableFilterDefinitions={availableFilterDefinitions}
/>
{children}
</ObjectFilterDropdownScopeInternalContext.Provider>
);

View File

@ -1,25 +0,0 @@
import { useEffect } from 'react';
import { FilterDefinition } from '@/ui/object/object-filter-dropdown/types/FilterDefinition';
import { useFilterStates } from '../../hooks/useFilterStates';
type ObjectFilterDropdownScopeInitEffectProps = {
filterScopeId: string;
availableFilterDefinitions?: FilterDefinition[];
};
export const ObjectFilterDropdownScopeInitEffect = ({
filterScopeId,
availableFilterDefinitions,
}: ObjectFilterDropdownScopeInitEffectProps) => {
const { setAvailableFilterDefinitions } = useFilterStates(filterScopeId);
useEffect(() => {
if (availableFilterDefinitions) {
setAvailableFilterDefinitions(availableFilterDefinitions);
}
}, [availableFilterDefinitions, setAvailableFilterDefinitions]);
return <></>;
};

View File

@ -1,11 +1,7 @@
import { ScopedStateKey } from '@/ui/utilities/recoil-scope/scopes-internal/types/ScopedStateKey';
import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext';
import { Filter } from '../../types/Filter';
type ObjectFilterDropdownScopeInternalContextProps = ScopedStateKey & {
onFilterSelect?: (sort: Filter) => void;
};
type ObjectFilterDropdownScopeInternalContextProps = ScopedStateKey;
export const ObjectFilterDropdownScopeInternalContext =
createScopeInternalContext<ObjectFilterDropdownScopeInternalContextProps>();

View File

@ -0,0 +1,10 @@
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
import { Filter } from '../types/Filter';
export const onFilterSelectScopedState = createScopedState<
((filter: Filter) => void) | undefined
>({
key: 'onFilterSelectScopedState',
defaultValue: undefined,
});

View File

@ -10,19 +10,21 @@ import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownM
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { useSortDropdown } from '@/ui/object/object-sort-dropdown/hooks/useSortDropdown';
import { ObjectSortDropdownScope } from '@/ui/object/object-sort-dropdown/scopes/ObjectSortDropdownScope';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { ObjectSortDropdownId } from '../constants/ObjectSortDropdownId';
import { useObjectSortDropdown } from '../hooks/useObjectSortDropdown';
import { SortDefinition } from '../types/SortDefinition';
import { SORT_DIRECTIONS, SortDirection } from '../types/SortDirection';
export type ObjectSortDropdownButtonProps = {
sortDropdownId: string;
hotkeyScope: HotkeyScope;
isPrimaryButton?: boolean;
};
export const ObjectSortDropdownButton = ({
sortDropdownId,
hotkeyScope,
}: ObjectSortDropdownButtonProps) => {
const [isSortDirectionMenuUnfolded, setIsSortDirectionMenuUnfolded] =
@ -36,8 +38,9 @@ export const ObjectSortDropdownButton = ({
setSelectedSortDirection('asc');
}, []);
const { availableSortDefinitions, onSortSelect, isSortSelected } =
useObjectSortDropdown();
const { isSortSelected } = useSortDropdown({
sortDropdownId: sortDropdownId,
});
const { toggleDropdown } = useDropdown({
dropdownScopeId: ObjectSortDropdownId,
@ -48,6 +51,10 @@ export const ObjectSortDropdownButton = ({
resetState();
};
const { availableSortDefinitions, onSortSelect } = useSortDropdown({
sortDropdownId: sortDropdownId,
});
const handleAddSort = (selectedSortDefinition: SortDefinition) => {
toggleDropdown();
onSortSelect?.({
@ -64,60 +71,64 @@ export const ObjectSortDropdownButton = ({
const { icons } = useLazyLoadIcons();
return (
<DropdownScope dropdownScopeId={ObjectSortDropdownId}>
<Dropdown
dropdownHotkeyScope={hotkeyScope}
dropdownOffset={{ y: 8 }}
clickableComponent={
<LightButton
title="Sort"
active={isSortSelected}
onClick={handleButtonClick}
/>
}
dropdownComponents={
<>
{isSortDirectionMenuUnfolded ? (
<DropdownMenuItemsContainer>
{SORT_DIRECTIONS.map((sortOrder, index) => (
<MenuItem
key={index}
onClick={() => {
setSelectedSortDirection(sortOrder);
setIsSortDirectionMenuUnfolded(false);
}}
text={sortOrder === 'asc' ? 'Ascending' : 'Descending'}
/>
))}
</DropdownMenuItemsContainer>
) : (
<>
<DropdownMenuHeader
EndIcon={IconChevronDown}
onClick={() => setIsSortDirectionMenuUnfolded(true)}
>
{selectedSortDirection === 'asc' ? 'Ascending' : 'Descending'}
</DropdownMenuHeader>
<DropdownMenuSeparator />
<ObjectSortDropdownScope sortScopeId={sortDropdownId}>
<DropdownScope dropdownScopeId={ObjectSortDropdownId}>
<Dropdown
dropdownHotkeyScope={hotkeyScope}
dropdownOffset={{ y: 8 }}
clickableComponent={
<LightButton
title="Sort"
active={isSortSelected}
onClick={handleButtonClick}
/>
}
dropdownComponents={
<>
{isSortDirectionMenuUnfolded ? (
<DropdownMenuItemsContainer>
{[...availableSortDefinitions]
.sort((a, b) => a.label.localeCompare(b.label))
.map((availableSortDefinition, index) => (
<MenuItem
testId={`select-sort-${index}`}
key={index}
onClick={() => handleAddSort(availableSortDefinition)}
LeftIcon={icons[availableSortDefinition.iconName]}
text={availableSortDefinition.label}
/>
))}
{SORT_DIRECTIONS.map((sortOrder, index) => (
<MenuItem
key={index}
onClick={() => {
setSelectedSortDirection(sortOrder);
setIsSortDirectionMenuUnfolded(false);
}}
text={sortOrder === 'asc' ? 'Ascending' : 'Descending'}
/>
))}
</DropdownMenuItemsContainer>
</>
)}
</>
}
onClose={handleDropdownButtonClose}
/>
</DropdownScope>
) : (
<>
<DropdownMenuHeader
EndIcon={IconChevronDown}
onClick={() => setIsSortDirectionMenuUnfolded(true)}
>
{selectedSortDirection === 'asc'
? 'Ascending'
: 'Descending'}
</DropdownMenuHeader>
<DropdownMenuSeparator />
<DropdownMenuItemsContainer>
{[...availableSortDefinitions]
.sort((a, b) => a.label.localeCompare(b.label))
.map((availableSortDefinition, index) => (
<MenuItem
testId={`select-sort-${index}`}
key={index}
onClick={() => handleAddSort(availableSortDefinition)}
LeftIcon={icons[availableSortDefinition.iconName]}
text={availableSortDefinition.label}
/>
))}
</DropdownMenuItemsContainer>
</>
)}
</>
}
onClose={handleDropdownButtonClose}
/>
</DropdownScope>
</ObjectSortDropdownScope>
);
};

View File

@ -1,36 +1,33 @@
import { useSortDropdownStates } from '@/ui/object/object-sort-dropdown/hooks/useSortDropdownStates';
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
import { useScopeInternalContextOrThrow } from '../../../utilities/recoil-scope/scopes-internal/hooks/useScopeInternalContextOrThrow';
import { ObjectSortDropdownScopeInternalContext } from '../scopes/scope-internal-context/ObjectSortDropdownScopeInternalContext';
import { useObjectSortDropdownStates } from './useObjectSortDropdownStates';
type UseSortProps = {
sortScopeId?: string;
sortDropdownId?: string;
};
export const useObjectSortDropdown = (props?: UseSortProps) => {
export const useSortDropdown = (props?: UseSortProps) => {
const scopeId = useAvailableScopeIdOrThrow(
ObjectSortDropdownScopeInternalContext,
props?.sortScopeId,
props?.sortDropdownId,
);
const {
availableSortDefinitions,
setAvailableSortDefinitions,
isSortSelected,
setIsSortSelected,
} = useObjectSortDropdownStates(scopeId);
const { onSortSelect } = useScopeInternalContextOrThrow(
ObjectSortDropdownScopeInternalContext,
);
onSortSelect,
setOnSortSelect,
} = useSortDropdownStates(scopeId);
return {
onSortSelect,
scopeId,
availableSortDefinitions,
isSortSelected,
setIsSortSelected,
setAvailableSortDefinitions,
onSortSelect,
setOnSortSelect,
};
};

View File

@ -1,9 +1,10 @@
import { onSortSelectScopedState } from '@/ui/object/object-sort-dropdown/states/onSortSelectScopedState';
import { useRecoilScopedStateV2 } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedStateV2';
import { availableSortDefinitionsScopedState } from '../states/availableSortDefinitionsScopedState';
import { isSortSelectedScopedState } from '../states/isSortSelectedScopedState';
export const useObjectSortDropdownStates = (scopeId: string) => {
export const useSortDropdownStates = (scopeId: string) => {
const [availableSortDefinitions, setAvailableSortDefinitions] =
useRecoilScopedStateV2(availableSortDefinitionsScopedState, scopeId);
@ -12,10 +13,17 @@ export const useObjectSortDropdownStates = (scopeId: string) => {
scopeId,
);
const [onSortSelect, setOnSortSelect] = useRecoilScopedStateV2(
onSortSelectScopedState,
scopeId,
);
return {
availableSortDefinitions,
setAvailableSortDefinitions,
isSortSelected,
setIsSortSelected,
onSortSelect,
setOnSortSelect,
};
};

View File

@ -1,32 +1,20 @@
import { ReactNode } from 'react';
import { Sort } from '../types/Sort';
import { SortDefinition } from '../types/SortDefinition';
import { ObjectSortDropdownScopeInitEffect } from './init-effect/ObjectSortDropdownScopeInitEffect';
import { ObjectSortDropdownScopeInternalContext } from './scope-internal-context/ObjectSortDropdownScopeInternalContext';
type ObjectSortDropdownScopeProps = {
children: ReactNode;
sortScopeId: string;
availableSortDefinitions?: SortDefinition[];
onSortSelect?: (sort: Sort) => void | Promise<void>;
};
export const ObjectSortDropdownScope = ({
children,
sortScopeId,
availableSortDefinitions,
onSortSelect,
}: ObjectSortDropdownScopeProps) => {
return (
<ObjectSortDropdownScopeInternalContext.Provider
value={{ scopeId: sortScopeId, onSortSelect }}
value={{ scopeId: sortScopeId }}
>
<ObjectSortDropdownScopeInitEffect
sortScopeId={sortScopeId}
availableSortDefinitions={availableSortDefinitions}
/>
{children}
</ObjectSortDropdownScopeInternalContext.Provider>
);

View File

@ -1,26 +0,0 @@
import { useEffect } from 'react';
import { SortDefinition } from '@/ui/object/object-sort-dropdown/types/SortDefinition';
import { useObjectSortDropdownStates } from '../../hooks/useObjectSortDropdownStates';
type ObjectSortDropdownScopeInitEffectProps = {
sortScopeId: string;
availableSortDefinitions?: SortDefinition[];
};
export const ObjectSortDropdownScopeInitEffect = ({
sortScopeId,
availableSortDefinitions,
}: ObjectSortDropdownScopeInitEffectProps) => {
const { setAvailableSortDefinitions } =
useObjectSortDropdownStates(sortScopeId);
useEffect(() => {
if (availableSortDefinitions) {
setAvailableSortDefinitions(availableSortDefinitions);
}
}, [availableSortDefinitions, setAvailableSortDefinitions]);
return <></>;
};

View File

@ -0,0 +1,10 @@
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
import { Sort } from '../types/Sort';
export const onSortSelectScopedState = createScopedState<
((sort: Sort) => void) | undefined
>({
key: 'onSortSelectScopedState',
defaultValue: undefined,
});

View File

@ -14,6 +14,11 @@ export const turnSortsIntoOrderBy = (
createdAt: 'DescNullsFirst',
};
}
if (!fields.length) {
return {};
}
return {
[fields[0].name]: 'DescNullsFirst',
};

View File

@ -1,4 +1,4 @@
import { useView } from '@/views/hooks/useView';
import { useViewBar } from '@/views/hooks/useViewBar';
import { Dropdown } from '../../../../layout/dropdown/components/Dropdown';
import { DropdownScope } from '../../../../layout/dropdown/scopes/DropdownScope';
@ -19,7 +19,7 @@ type BoardOptionsDropdownProps = Pick<
export const BoardOptionsDropdown = ({
onStageAdd,
}: BoardOptionsDropdownProps) => {
const { setViewEditMode } = useView();
const { setViewEditMode } = useViewBar();
return (
<DropdownScope dropdownScopeId={BoardOptionsDropdownId}>

View File

@ -26,7 +26,7 @@ import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection';
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
import { useView } from '@/views/hooks/useView';
import { useViewBar } from '@/views/hooks/useViewBar';
import { useBoardCardFields } from '../../hooks/useBoardCardFields';
import { boardColumnsState } from '../../states/boardColumnsState';
@ -52,7 +52,7 @@ type ColumnForCreate = {
export const BoardOptionsDropdownContent = ({
onStageAdd,
}: BoardOptionsDropdownContentProps) => {
const { setViewEditMode, handleViewNameSubmit } = useView();
const { setViewEditMode, handleViewNameSubmit } = useViewBar();
const { viewEditModeState, currentViewSelector } = useViewScopedStates();
const { BoardRecoilScopeContext } = useContext(BoardContext);

View File

@ -1,10 +1,14 @@
import { useRef } from 'react';
import styled from '@emotion/styled';
import { useRecoilCallback } from 'recoil';
import { RecordTableInternalEffect } from '@/ui/object/record-table/components/RecordTableInternalEffect';
import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable';
import { RecordTableScope } from '@/ui/object/record-table/scopes/RecordTableScope';
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import { useViewFields } from '@/views/hooks/internal/useViewFields';
import { mapColumnDefinitionsToViewFields } from '@/views/utils/mapColumnDefinitionToViewField';
import { EntityUpdateMutationContext } from '../contexts/EntityUpdateMutationHookContext';
@ -71,34 +75,50 @@ const StyledTableContainer = styled.div`
`;
type RecordTableProps = {
recordTableId: string;
viewBarId: string;
updateEntityMutation: (params: any) => void;
};
export const RecordTable = ({ updateEntityMutation }: RecordTableProps) => {
export const RecordTable = ({
recordTableId,
viewBarId,
updateEntityMutation,
}: RecordTableProps) => {
const tableBodyRef = useRef<HTMLDivElement>(null);
const { resetTableRowSelection, setRowSelectedState } = useRecordTable();
const { resetTableRowSelection, setRowSelectedState } = useRecordTable({
recordTableScopeId: recordTableId,
});
const { persistViewFields } = useViewFields(viewBarId);
return (
<ScrollWrapper>
<EntityUpdateMutationContext.Provider value={updateEntityMutation}>
<StyledTableWithHeader>
<StyledTableContainer>
<div ref={tableBodyRef}>
<StyledTable className="entity-table-cell">
<RecordTableHeader />
<RecordTableBody />
</StyledTable>
<DragSelect
dragSelectable={tableBodyRef}
onDragSelectionStart={resetTableRowSelection}
onDragSelectionChange={setRowSelectedState}
/>
</div>
<RecordTableInternalEffect tableBodyRef={tableBodyRef} />
</StyledTableContainer>
</StyledTableWithHeader>
</EntityUpdateMutationContext.Provider>
</ScrollWrapper>
<RecordTableScope
recordTableScopeId={recordTableId}
onColumnsChange={useRecoilCallback(() => (columns) => {
persistViewFields(mapColumnDefinitionsToViewFields(columns));
})}
>
<ScrollWrapper>
<EntityUpdateMutationContext.Provider value={updateEntityMutation}>
<StyledTableWithHeader>
<StyledTableContainer>
<div ref={tableBodyRef}>
<StyledTable className="entity-table-cell">
<RecordTableHeader />
<RecordTableBody />
</StyledTable>
<DragSelect
dragSelectable={tableBodyRef}
onDragSelectionStart={resetTableRowSelection}
onDragSelectionChange={setRowSelectedState}
/>
</div>
<RecordTableInternalEffect tableBodyRef={tableBodyRef} />
</StyledTableContainer>
</StyledTableWithHeader>
</EntityUpdateMutationContext.Provider>
</ScrollWrapper>
</RecordTableScope>
);
};

View File

@ -1,32 +1,39 @@
import { useCallback } from 'react';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { useRecoilValue } from 'recoil';
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable';
import { RecordTableScopeInternalContext } from '@/ui/object/record-table/scopes/scope-internal-context/RecordTableScopeInternalContext';
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
import { useMoveViewColumns } from '@/views/hooks/useMoveViewColumns';
import { useView } from '@/views/hooks/useView';
import { savedTableColumnsFamilyState } from '../states/savedTableColumnsFamilyState';
import { ColumnDefinition } from '../types/ColumnDefinition';
import { useRecordTableScopedStates } from './internal/useRecordTableScopedStates';
export const useTableColumns = () => {
const { onColumnsChange, setTableColumns } = useRecordTable();
type useRecordTableProps = {
recordTableScopeId?: string;
};
export const useTableColumns = (props?: useRecordTableProps) => {
const scopeId = useAvailableScopeIdOrThrow(
RecordTableScopeInternalContext,
props?.recordTableScopeId,
);
const { onColumnsChange, setTableColumns } = useRecordTable({
recordTableScopeId: scopeId,
});
const {
availableTableColumnsState,
tableColumnsState,
visibleTableColumnsSelector,
} = useRecordTableScopedStates();
} = useRecordTableScopedStates({
customRecordTableScopeId: scopeId,
});
const availableTableColumns = useRecoilValue(availableTableColumnsState);
const { currentViewId } = useView();
const setSavedTableColumns = useSetRecoilState(
savedTableColumnsFamilyState(currentViewId),
);
const tableColumns = useRecoilValue(tableColumnsState);
const visibleTableColumns = useRecoilValue(visibleTableColumnsSelector);
@ -34,12 +41,11 @@ export const useTableColumns = () => {
const handleColumnsChange = useCallback(
async (columns: ColumnDefinition<FieldMetadata>[]) => {
setSavedTableColumns(columns);
setTableColumns(columns);
await onColumnsChange?.(columns);
},
[onColumnsChange, setSavedTableColumns, setTableColumns],
[onColumnsChange, setTableColumns],
);
const handleColumnVisibilityChange = useCallback(

View File

@ -1,6 +1,6 @@
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
import { useView } from '@/views/hooks/useView';
import { useViewBar } from '@/views/hooks/useViewBar';
import { TableOptionsDropdownId } from '../../constants/TableOptionsDropdownId';
import { TableOptionsHotkeyScope } from '../../types/TableOptionsHotkeyScope';
@ -10,10 +10,12 @@ import { TableOptionsDropdownContent } from './TableOptionsDropdownContent';
export const TableOptionsDropdown = ({
onImport,
recordTableId,
}: {
onImport?: () => void;
recordTableId: string;
}) => {
const { setViewEditMode } = useView();
const { setViewEditMode } = useViewBar();
return (
<DropdownScope dropdownScopeId={TableOptionsDropdownId}>
@ -21,7 +23,12 @@ export const TableOptionsDropdown = ({
clickableComponent={<TableOptionsDropdownButton />}
dropdownHotkeyScope={{ scope: TableOptionsHotkeyScope.Dropdown }}
dropdownOffset={{ y: 8 }}
dropdownComponents={<TableOptionsDropdownContent onImport={onImport} />}
dropdownComponents={
<TableOptionsDropdownContent
onImport={onImport}
recordTableId={recordTableId}
/>
}
onClickOutside={() => setViewEditMode('none')}
/>
</DropdownScope>

View File

@ -13,7 +13,7 @@ import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection';
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
import { useView } from '@/views/hooks/useView';
import { useViewBar } from '@/views/hooks/useViewBar';
import { useRecordTableScopedStates } from '../../hooks/internal/useRecordTableScopedStates';
import { useTableColumns } from '../../hooks/useTableColumns';
@ -23,10 +23,12 @@ type TableOptionsMenu = 'fields';
export const TableOptionsDropdownContent = ({
onImport,
recordTableId,
}: {
onImport?: () => void;
recordTableId: string;
}) => {
const { setViewEditMode, handleViewNameSubmit } = useView();
const { setViewEditMode, handleViewNameSubmit } = useViewBar();
const { viewEditModeState, currentViewSelector } = useViewScopedStates();
const viewEditMode = useRecoilValue(viewEditModeState);
@ -40,13 +42,14 @@ export const TableOptionsDropdownContent = ({
const viewEditInputRef = useRef<HTMLInputElement>(null);
const { hiddenTableColumnsSelector, visibleTableColumnsSelector } =
useRecordTableScopedStates();
useRecordTableScopedStates({ customRecordTableScopeId: recordTableId });
const hiddenTableColumns = useRecoilValue(hiddenTableColumnsSelector);
const visibleTableColumns = useRecoilValue(visibleTableColumnsSelector);
const { handleColumnVisibilityChange, handleColumnReorder } =
useTableColumns();
const { handleColumnVisibilityChange, handleColumnReorder } = useTableColumns(
{ recordTableScopeId: recordTableId },
);
const handleSelectMenu = (option: TableOptionsMenu) => {
const name = viewEditInputRef.current?.value;

View File

@ -1,13 +0,0 @@
import { atomFamily } from 'recoil';
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
import { ColumnDefinition } from '../types/ColumnDefinition';
export const savedTableColumnsFamilyState = atomFamily<
ColumnDefinition<FieldMetadata>[],
string | undefined
>({
key: 'savedTableColumnsFamilyState',
default: [],
});

View File

@ -1,19 +0,0 @@
import { selectorFamily } from 'recoil';
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
import { ColumnDefinition } from '../../types/ColumnDefinition';
import { savedTableColumnsFamilyState } from '../savedTableColumnsFamilyState';
export const savedTableColumnsByKeyFamilySelector = selectorFamily({
key: 'savedTableColumnsByKeyFamilySelector',
get:
(viewId: string | undefined) =>
({ get }) =>
get(savedTableColumnsFamilyState(viewId)).reduce<
Record<string, ColumnDefinition<FieldMetadata>>
>(
(result, column) => ({ ...result, [column.fieldMetadataId]: column }),
{},
),
});

View File

@ -9,7 +9,7 @@ import { ButtonGroup } from '@/ui/input/button/components/ButtonGroup';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useView } from '@/views/hooks/useView';
import { useViewBar } from '@/views/hooks/useViewBar';
import { useViewScopedStates } from '../hooks/internal/useViewScopedStates';
@ -29,7 +29,7 @@ export const UpdateViewButtonGroup = ({
onViewEditModeChange,
}: UpdateViewButtonGroupProps) => {
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const { updateCurrentView, setViewEditMode } = useView();
const { updateCurrentView, setViewEditMode } = useViewBar();
const { canPersistFiltersSelector, canPersistSortsSelector } =
useViewScopedStates();

View File

@ -1,16 +1,18 @@
import { ReactNode } from 'react';
import { useRecoilValue } from 'recoil';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { TopBar } from '@/ui/layout/top-bar/TopBar';
import { ObjectFilterDropdownButton } from '@/ui/object/object-filter-dropdown/components/ObjectFilterDropdownButton';
import { ObjectFilterDropdownScope } from '@/ui/object/object-filter-dropdown/scopes/ObjectFilterDropdownScope';
import { FiltersHotkeyScope } from '@/ui/object/object-filter-dropdown/types/FiltersHotkeyScope';
import { ObjectSortDropdownButton } from '@/ui/object/object-sort-dropdown/components/ObjectSortDropdownButton';
import { ObjectSortDropdownScope } from '@/ui/object/object-sort-dropdown/scopes/ObjectSortDropdownScope';
import { ViewBarFilterEffect } from '@/views/components/ViewBarFilterEffect';
import { ViewBarSortEffect } from '@/views/components/ViewBarSortEffect';
import { useViewBar } from '@/views/hooks/useViewBar';
import { ViewScope } from '@/views/scopes/ViewScope';
import { ViewField } from '@/views/types/ViewField';
import { ViewFilter } from '@/views/types/ViewFilter';
import { ViewSort } from '@/views/types/ViewSort';
import { useViewScopedStates } from '../hooks/internal/useViewScopedStates';
import { useView } from '../hooks/useView';
import { ViewsHotkeyScope } from '../types/ViewsHotkeyScope';
import { UpdateViewButtonGroup } from './UpdateViewButtonGroup';
@ -19,82 +21,91 @@ import { ViewBarEffect } from './ViewBarEffect';
import { ViewsDropdownButton } from './ViewsDropdownButton';
export type ViewBarProps = {
viewBarId: string;
className?: string;
optionsDropdownButton: ReactNode;
optionsDropdownScopeId: string;
onViewSortsChange?: (sorts: ViewSort[]) => void | Promise<void>;
onViewFiltersChange?: (filters: ViewFilter[]) => void | Promise<void>;
onViewFieldsChange?: (fields: ViewField[]) => void | Promise<void>;
};
export const ViewBar = ({
viewBarId,
className,
optionsDropdownButton,
optionsDropdownScopeId,
onViewFieldsChange,
onViewFiltersChange,
onViewSortsChange,
}: ViewBarProps) => {
const { openDropdown: openOptionsDropdownButton } = useDropdown({
dropdownScopeId: optionsDropdownScopeId,
});
const { upsertViewSort, upsertViewFilter } = useView();
const { upsertViewSort, upsertViewFilter } = useViewBar({
viewBarId: viewBarId,
});
const { availableFilterDefinitionsState, availableSortDefinitionsState } =
useViewScopedStates();
const availableFilterDefinitions = useRecoilValue(
availableFilterDefinitionsState,
);
const availableSortDefinitions = useRecoilValue(
availableSortDefinitionsState,
);
const filterDropdownId = 'view-filter';
const sortDropdownId = 'view-sort';
return (
<ObjectFilterDropdownScope
filterScopeId="view-filter"
availableFilterDefinitions={availableFilterDefinitions}
onFilterSelect={upsertViewFilter}
<ViewScope
viewScopeId={viewBarId}
onViewFieldsChange={onViewFieldsChange}
onViewFiltersChange={onViewFiltersChange}
onViewSortsChange={onViewSortsChange}
>
<ObjectSortDropdownScope
sortScopeId="view-sort"
availableSortDefinitions={availableSortDefinitions}
<ViewBarEffect />
<ViewBarFilterEffect
filterDropdownId={filterDropdownId}
onFilterSelect={upsertViewFilter}
/>
<ViewBarSortEffect
sortDropdownId={sortDropdownId}
onSortSelect={upsertViewSort}
>
<ViewBarEffect />
<TopBar
className={className}
leftComponent={
<ViewsDropdownButton
onViewEditModeChange={openOptionsDropdownButton}
hotkeyScope={{ scope: ViewsHotkeyScope.ListDropdown }}
optionsDropdownScopeId={optionsDropdownScopeId}
/>
<TopBar
className={className}
leftComponent={
<ViewsDropdownButton
onViewEditModeChange={openOptionsDropdownButton}
hotkeyScope={{ scope: ViewsHotkeyScope.ListDropdown }}
optionsDropdownScopeId={optionsDropdownScopeId}
/>
}
displayBottomBorder={false}
rightComponent={
<>
<ObjectFilterDropdownButton
filterDropdownId={filterDropdownId}
hotkeyScope={{
scope: FiltersHotkeyScope.ObjectFilterDropdownButton,
}}
/>
}
displayBottomBorder={false}
rightComponent={
<>
<ObjectFilterDropdownButton
hotkeyScope={{
scope: FiltersHotkeyScope.ObjectFilterDropdownButton,
}}
/>
<ObjectSortDropdownButton
hotkeyScope={{
scope: FiltersHotkeyScope.ObjectSortDropdownButton,
}}
isPrimaryButton
/>
{optionsDropdownButton}
</>
}
bottomComponent={
<ViewBarDetails
hasFilterButton
rightComponent={
<UpdateViewButtonGroup
onViewEditModeChange={openOptionsDropdownButton}
hotkeyScope={ViewsHotkeyScope.CreateDropdown}
/>
}
<ObjectSortDropdownButton
sortDropdownId={sortDropdownId}
hotkeyScope={{
scope: FiltersHotkeyScope.ObjectSortDropdownButton,
}}
/>
}
/>
</ObjectSortDropdownScope>
</ObjectFilterDropdownScope>
{optionsDropdownButton}
</>
}
bottomComponent={
<ViewBarDetails
filterDropdownId={filterDropdownId}
hasFilterButton
rightComponent={
<UpdateViewButtonGroup
onViewEditModeChange={openOptionsDropdownButton}
hotkeyScope={ViewsHotkeyScope.CreateDropdown}
/>
}
/>
}
/>
</ViewScope>
);
};

View File

@ -6,15 +6,16 @@ import { IconArrowDown, IconArrowUp } from '@/ui/display/icon/index';
import { useLazyLoadIcons } from '@/ui/input/hooks/useLazyLoadIcons';
import { AddObjectFilterFromDetailsButton } from '@/ui/object/object-filter-dropdown/components/AddObjectFilterFromDetailsButton';
import { getOperandLabelShort } from '@/ui/object/object-filter-dropdown/utils/getOperandLabel';
import { useViewBar } from '@/views/hooks/useViewBar';
import { useViewScopedStates } from '../hooks/internal/useViewScopedStates';
import { useView } from '../hooks/useView';
import SortOrFilterChip from './SortOrFilterChip';
export type ViewBarDetailsProps = {
hasFilterButton?: boolean;
rightComponent?: ReactNode;
filterDropdownId?: string;
};
const StyledBar = styled.div`
@ -88,6 +89,7 @@ const StyledAddFilterContainer = styled.div`
export const ViewBarDetails = ({
hasFilterButton = false,
rightComponent,
filterDropdownId,
}: ViewBarDetailsProps) => {
const {
currentViewSortsState,
@ -104,7 +106,7 @@ export const ViewBarDetails = ({
const canPersistSorts = useRecoilValue(canPersistSortsSelector);
const isViewBarExpanded = useRecoilValue(isViewBarExpandedState);
const { resetViewBar, removeViewSort, removeViewFilter } = useView();
const { resetViewBar, removeViewSort, removeViewFilter } = useViewBar();
const canPersistView = canPersistFilters || canPersistSorts;
@ -161,7 +163,9 @@ export const ViewBarDetails = ({
</StyledChipcontainer>
{hasFilterButton && (
<StyledAddFilterContainer>
<AddObjectFilterFromDetailsButton />
<AddObjectFilterFromDetailsButton
filterDropdownId={filterDropdownId}
/>
</StyledAddFilterContainer>
)}
</StyledFilterContainer>

View File

@ -5,11 +5,11 @@ import { useRecoilCallback, useRecoilValue } from 'recoil';
import { useFindManyObjectRecords } from '@/object-record/hooks/useFindManyObjectRecords';
import { PaginatedObjectTypeResults } from '@/object-record/types/PaginatedObjectTypeResults';
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
import { useViewBar } from '@/views/hooks/useViewBar';
import { GraphQLView } from '@/views/types/GraphQLView';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { useViewScopedStates } from '../hooks/internal/useViewScopedStates';
import { useView } from '../hooks/useView';
import { getViewScopedStatesFromSnapshot } from '../utils/getViewScopedStatesFromSnapshot';
export const ViewBarEffect = () => {
@ -20,7 +20,7 @@ export const ViewBarEffect = () => {
loadViewFields,
loadViewFilters,
loadViewSorts,
} = useView();
} = useViewBar();
const [searchParams] = useSearchParams();
const currentViewIdFromUrl = searchParams.get('view');

View File

@ -0,0 +1,41 @@
import { useEffect } from 'react';
import { useRecoilValue } from 'recoil';
import { useFilterDropdown } from '@/ui/object/object-filter-dropdown/hooks/useFilterDropdown';
import { Filter } from '@/ui/object/object-filter-dropdown/types/Filter';
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
type ViewBarFilterEffectProps = {
filterDropdownId: string;
onFilterSelect?: ((filter: Filter) => void) | undefined;
};
export const ViewBarFilterEffect = ({
filterDropdownId,
onFilterSelect,
}: ViewBarFilterEffectProps) => {
const { availableFilterDefinitionsState } = useViewScopedStates();
const availableFilterDefinitions = useRecoilValue(
availableFilterDefinitionsState,
);
const { setAvailableFilterDefinitions, setOnFilterSelect } =
useFilterDropdown({ filterDropdownId: filterDropdownId });
useEffect(() => {
if (availableFilterDefinitions) {
setAvailableFilterDefinitions(availableFilterDefinitions);
}
if (onFilterSelect) {
setOnFilterSelect(() => onFilterSelect);
}
}, [
availableFilterDefinitions,
onFilterSelect,
setAvailableFilterDefinitions,
setOnFilterSelect,
]);
return <></>;
};

View File

@ -0,0 +1,42 @@
import { useEffect } from 'react';
import { useRecoilValue } from 'recoil';
import { useSortDropdown } from '@/ui/object/object-sort-dropdown/hooks/useSortDropdown';
import { Sort } from '@/ui/object/object-sort-dropdown/types/Sort';
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
type ViewBarSortEffectProps = {
sortDropdownId: string;
onSortSelect?: ((sort: Sort) => void) | undefined;
};
export const ViewBarSortEffect = ({
sortDropdownId,
onSortSelect,
}: ViewBarSortEffectProps) => {
const { availableSortDefinitionsState } = useViewScopedStates();
const availableSortDefinitions = useRecoilValue(
availableSortDefinitionsState,
);
const { setAvailableSortDefinitions, setOnSortSelect } = useSortDropdown({
sortDropdownId,
});
useEffect(() => {
if (availableSortDefinitions) {
setAvailableSortDefinitions(availableSortDefinitions);
}
if (onSortSelect) {
setOnSortSelect(() => onSortSelect);
}
}, [
availableSortDefinitions,
onSortSelect,
setAvailableSortDefinitions,
setOnSortSelect,
]);
return <></>;
};

View File

@ -19,11 +19,11 @@ import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { MOBILE_VIEWPORT } from '@/ui/theme/constants/theme';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { useViewBar } from '@/views/hooks/useViewBar';
import { assertNotNull } from '~/utils/assert';
import { ViewsDropdownId } from '../constants/ViewsDropdownId';
import { useViewScopedStates } from '../hooks/internal/useViewScopedStates';
import { useView } from '../hooks/useView';
const StyledBoldDropdownMenuItemsContainer = styled(DropdownMenuItemsContainer)`
font-weight: ${({ theme }) => theme.font.weight.regular};
@ -68,7 +68,7 @@ export const ViewsDropdownButton = ({
optionsDropdownScopeId,
}: ViewsDropdownButtonProps) => {
const theme = useTheme();
const { removeView, changeViewInUrl } = useView();
const { removeView, changeViewInUrl } = useViewBar();
const { viewsState, currentViewSelector, entityCountInCurrentViewState } =
useViewScopedStates();
@ -79,7 +79,7 @@ export const ViewsDropdownButton = ({
entityCountInCurrentViewState,
);
const { setViewEditMode, setCurrentViewId, loadView } = useView();
const { setViewEditMode, setCurrentViewId, loadView } = useViewBar();
const {
isDropdownOpen: isViewsDropdownOpen,

View File

@ -23,7 +23,7 @@ export const useViewFilters = (viewScopeId: string) => {
const apolloClient = useApolloClient();
const { currentViewFiltersState } = useViewScopedStates({
customViewScopeId: viewScopeId,
viewScopeId: viewScopeId,
});
const persistViewFilters = useRecoilCallback(

View File

@ -8,12 +8,12 @@ import { ViewScopeInternalContext } from '../../scopes/scope-internal-context/Vi
import { currentViewIdScopedState } from '../../states/currentViewIdScopedState';
import { getViewScopedStates } from '../../utils/internal/getViewScopedStates';
export const useViewScopedStates = (args?: { customViewScopeId?: string }) => {
const { customViewScopeId } = args ?? {};
export const useViewScopedStates = (args?: { viewScopeId?: string }) => {
const { viewScopeId } = args ?? {};
const scopeId = useAvailableScopeIdOrThrow(
ViewScopeInternalContext,
customViewScopeId,
viewScopeId,
);
// View

View File

@ -22,7 +22,7 @@ export const useViewSorts = (viewScopeId: string) => {
const apolloClient = useApolloClient();
const { currentViewSortsState } = useViewScopedStates({
customViewScopeId: viewScopeId,
viewScopeId: viewScopeId,
});
const persistViewSorts = useRecoilCallback(

View File

@ -25,13 +25,13 @@ import { useViewScopedStates } from './internal/useViewScopedStates';
import { useViewSorts } from './internal/useViewSorts';
type UseViewProps = {
viewScopeId?: string;
viewBarId?: string;
};
export const useView = (props?: UseViewProps) => {
export const useViewBar = (props?: UseViewProps) => {
const scopeId = useAvailableScopeIdOrThrow(
ViewScopeInternalContext,
props?.viewScopeId,
props?.viewBarId,
);
const {
@ -46,7 +46,7 @@ export const useView = (props?: UseViewProps) => {
viewObjectMetadataIdState,
viewTypeState,
} = useViewScopedStates({
customViewScopeId: scopeId,
viewScopeId: scopeId,
});
const { persistViewSorts, upsertViewSort, removeViewSort } =

View File

@ -11,7 +11,6 @@ import { PageHeader } from '@/ui/layout/page/PageHeader';
import { TabList } from '@/ui/layout/tab/components/TabList';
import { TopBar } from '@/ui/layout/top-bar/TopBar';
import { ObjectFilterDropdownButton } from '@/ui/object/object-filter-dropdown/components/ObjectFilterDropdownButton';
import { ObjectFilterDropdownScope } from '@/ui/object/object-filter-dropdown/scopes/ObjectFilterDropdownScope';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { TasksEffect } from './TasksEffect';
@ -45,38 +44,36 @@ export const Tasks = () => {
},
];
const filterDropdownId = 'tasks-assignee-filter';
return (
<PageContainer>
<RecoilScope CustomRecoilScopeContext={TasksRecoilScopeContext}>
<ObjectFilterDropdownScope filterScopeId="tasks-filter-scope">
<TasksEffect />
<PageHeader title="Tasks" Icon={IconCheckbox}>
<PageAddTaskButton />
</PageHeader>
<PageBody>
<StyledTasksContainer>
<TopBar
leftComponent={
<StyledTabListContainer>
<TabList
context={TasksRecoilScopeContext}
tabs={TASK_TABS}
/>
</StyledTabListContainer>
}
rightComponent={
<ObjectFilterDropdownButton
key="tasks-filter-dropdown-button"
hotkeyScope={{
scope: RelationPickerHotkeyScope.RelationPicker,
}}
/>
}
/>
<TaskGroups />
</StyledTasksContainer>
</PageBody>
</ObjectFilterDropdownScope>
<TasksEffect filterDropdownId={filterDropdownId} />
<PageHeader title="Tasks" Icon={IconCheckbox}>
<PageAddTaskButton filterDropdownId={filterDropdownId} />
</PageHeader>
<PageBody>
<StyledTasksContainer>
<TopBar
leftComponent={
<StyledTabListContainer>
<TabList context={TasksRecoilScopeContext} tabs={TASK_TABS} />
</StyledTabListContainer>
}
rightComponent={
<ObjectFilterDropdownButton
filterDropdownId={filterDropdownId}
key="tasks-filter-dropdown-button"
hotkeyScope={{
scope: RelationPickerHotkeyScope.RelationPicker,
}}
/>
}
/>
<TaskGroups filterDropdownId={filterDropdownId} />
</StyledTasksContainer>
</PageBody>
</RecoilScope>
</PageContainer>
);

View File

@ -2,14 +2,21 @@ import { useEffect } from 'react';
import { useRecoilValue } from 'recoil';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { useFilter } from '@/ui/object/object-filter-dropdown/hooks/useFilter';
import { useFilterDropdown } from '@/ui/object/object-filter-dropdown/hooks/useFilterDropdown';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { tasksFilterDefinitions } from './tasks-filter-definitions';
export const TasksEffect = () => {
type TasksEffectProps = {
filterDropdownId: string;
};
export const TasksEffect = ({ filterDropdownId }: TasksEffectProps) => {
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
const { setSelectedFilter, setAvailableFilterDefinitions } = useFilter();
const { setSelectedFilter, setAvailableFilterDefinitions } =
useFilterDropdown({
filterDropdownId: filterDropdownId,
});
useEffect(() => {
setAvailableFilterDefinitions(tasksFilterDefinitions);