feat: record board component state refactor (#8779)

Fix #8758 

This PR is migrating the recoil component state from v1 to v2 for board.
It also now share some states and logics between board and table,
further can be done later.
Lastly this PR fix an issue since the PR #8613 that was treating
no-value as a normal record-group.
This commit is contained in:
Jérémy M
2024-11-28 13:44:21 +01:00
committed by GitHub
parent e96ad9a1f2
commit 812ed6ed69
95 changed files with 1355 additions and 1316 deletions

View File

@ -10,46 +10,50 @@ import { useObjectNamePluralFromSingular } from '@/object-metadata/hooks/useObje
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown'; import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown';
import { RecordGroupsVisibilityDropdownSection } from '@/object-record/record-group/components/RecordGroupsVisibilityDropdownSection'; import { RecordGroupsVisibilityDropdownSection } from '@/object-record/record-group/components/RecordGroupsVisibilityDropdownSection';
import { useRecordGroups } from '@/object-record/record-group/hooks/useRecordGroups';
import { useRecordGroupVisibility } from '@/object-record/record-group/hooks/useRecordGroupVisibility'; import { useRecordGroupVisibility } from '@/object-record/record-group/hooks/useRecordGroupVisibility';
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector';
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
import { SettingsPath } from '@/types/SettingsPath'; import { SettingsPath } from '@/types/SettingsPath';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState'; import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
import { useSetRecoilState } from 'recoil'; import { useSetRecoilState } from 'recoil';
export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => { export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => {
const { const {
currentContentId, currentContentId,
viewType,
recordIndexId, recordIndexId,
objectMetadataItem, objectMetadataItem,
onContentChange, onContentChange,
closeDropdown, closeDropdown,
} = useOptionsDropdown(); } = useOptionsDropdown();
const { objectNamePlural } = useObjectNamePluralFromSingular({ const recordGroupFieldMetadata = useRecoilComponentValueV2(
objectNameSingular: objectMetadataItem.nameSingular, recordGroupFieldMetadataComponentState,
}); );
const { hiddenRecordGroups, viewGroupFieldMetadataItem } = useRecordGroups({ const hiddenRecordGroupIds = useRecoilComponentValueV2(
hiddenRecordGroupIdsComponentSelector,
);
const { objectNamePlural } = useObjectNamePluralFromSingular({
objectNameSingular: objectMetadataItem.nameSingular, objectNameSingular: objectMetadataItem.nameSingular,
}); });
const { handleVisibilityChange: handleRecordGroupVisibilityChange } = const { handleVisibilityChange: handleRecordGroupVisibilityChange } =
useRecordGroupVisibility({ useRecordGroupVisibility({
viewBarId: recordIndexId, viewBarId: recordIndexId,
viewType,
}); });
const viewGroupSettingsUrl = getSettingsPagePath( const viewGroupSettingsUrl = getSettingsPagePath(
SettingsPath.ObjectFieldEdit, SettingsPath.ObjectFieldEdit,
{ {
objectSlug: objectNamePlural, objectSlug: objectNamePlural,
fieldSlug: viewGroupFieldMetadataItem?.name ?? '', fieldSlug: recordGroupFieldMetadata?.name ?? '',
}, },
); );
@ -61,11 +65,11 @@ export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => {
useEffect(() => { useEffect(() => {
if ( if (
currentContentId === 'hiddenRecordGroups' && currentContentId === 'hiddenRecordGroups' &&
hiddenRecordGroups.length === 0 hiddenRecordGroupIds.length === 0
) { ) {
onContentChange('recordGroups'); onContentChange('recordGroups');
} }
}, [hiddenRecordGroups, currentContentId, onContentChange]); }, [hiddenRecordGroupIds, currentContentId, onContentChange]);
return ( return (
<> <>
@ -74,13 +78,13 @@ export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => {
StartIcon={IconChevronLeft} StartIcon={IconChevronLeft}
onClick={() => onContentChange('recordGroups')} onClick={() => onContentChange('recordGroups')}
> >
Hidden {viewGroupFieldMetadataItem?.label} Hidden {recordGroupFieldMetadata?.label}
</DropdownMenuHeader> </DropdownMenuHeader>
</DropdownMenuItemsContainer> </DropdownMenuItemsContainer>
<RecordGroupsVisibilityDropdownSection <RecordGroupsVisibilityDropdownSection
title={`Hidden ${viewGroupFieldMetadataItem?.label}`} title={`Hidden ${recordGroupFieldMetadata?.label}`}
recordGroups={hiddenRecordGroups} recordGroupIds={hiddenRecordGroupIds}
onVisibilityChange={handleRecordGroupVisibilityChange} onVisibilityChange={handleRecordGroupVisibilityChange}
isDraggable={false} isDraggable={false}
showSubheader={false} showSubheader={false}

View File

@ -15,7 +15,7 @@ import { useHandleToggleTrashColumnFilter } from '@/object-record/record-index/h
import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard'; import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard';
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown'; import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown';
import { useRecordGroups } from '@/object-record/record-group/hooks/useRecordGroups'; import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
import { import {
displayedExportProgress, displayedExportProgress,
useExportRecords, useExportRecords,
@ -26,6 +26,7 @@ import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenu
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { ViewType } from '@/views/types/ViewType'; import { ViewType } from '@/views/types/ViewType';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
@ -44,6 +45,10 @@ export const ObjectOptionsDropdownMenuContent = () => {
objectNameSingular: objectMetadataItem.nameSingular, objectNameSingular: objectMetadataItem.nameSingular,
}); });
const recordGroupFieldMetadata = useRecoilComponentValueV2(
recordGroupFieldMetadataComponentState,
);
useScopedHotkeys( useScopedHotkeys(
[Key.Escape], [Key.Escape],
() => { () => {
@ -64,10 +69,6 @@ export const ObjectOptionsDropdownMenuContent = () => {
viewBarId: recordIndexId, viewBarId: recordIndexId,
}); });
const { viewGroupFieldMetadataItem } = useRecordGroups({
objectNameSingular: objectMetadataItem.nameSingular,
});
const { openObjectRecordsSpreasheetImportDialog } = const { openObjectRecordsSpreasheetImportDialog } =
useOpenObjectRecordsSpreadsheetImportDialog( useOpenObjectRecordsSpreadsheetImportDialog(
objectMetadataItem.nameSingular, objectMetadataItem.nameSingular,
@ -113,7 +114,7 @@ export const ObjectOptionsDropdownMenuContent = () => {
onClick={() => onContentChange('recordGroups')} onClick={() => onContentChange('recordGroups')}
LeftIcon={IconLayoutList} LeftIcon={IconLayoutList}
text="Group by" text="Group by"
contextualText={viewGroupFieldMetadataItem?.label} contextualText={recordGroupFieldMetadata?.label}
hasSubMenu hasSubMenu
/> />
)} )}

View File

@ -12,7 +12,7 @@ import { useObjectNamePluralFromSingular } from '@/object-metadata/hooks/useObje
import { StyledInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect'; import { StyledInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect';
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown'; import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown';
import { useSearchRecordGroupField } from '@/object-record/object-options-dropdown/hooks/useSearchRecordGroupField'; import { useSearchRecordGroupField } from '@/object-record/object-options-dropdown/hooks/useSearchRecordGroupField';
import { useRecordGroups } from '@/object-record/record-group/hooks/useRecordGroups'; import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector';
import { useHandleRecordGroupField } from '@/object-record/record-index/hooks/useHandleRecordGroupField'; import { useHandleRecordGroupField } from '@/object-record/record-index/hooks/useHandleRecordGroupField';
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
import { SettingsPath } from '@/types/SettingsPath'; import { SettingsPath } from '@/types/SettingsPath';
@ -20,6 +20,7 @@ import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenu
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState'; import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
import { useSetRecoilState } from 'recoil'; import { useSetRecoilState } from 'recoil';
@ -38,9 +39,9 @@ export const ObjectOptionsDropdownRecordGroupFieldsContent = () => {
objectNameSingular: objectMetadataItem.nameSingular, objectNameSingular: objectMetadataItem.nameSingular,
}); });
const { hiddenRecordGroups } = useRecordGroups({ const hiddenRecordGroupIds = useRecoilComponentValueV2(
objectNameSingular: objectMetadataItem.nameSingular, hiddenRecordGroupIdsComponentSelector,
}); );
const { const {
recordGroupFieldSearchInput, recordGroupFieldSearchInput,
@ -68,11 +69,11 @@ export const ObjectOptionsDropdownRecordGroupFieldsContent = () => {
useEffect(() => { useEffect(() => {
if ( if (
currentContentId === 'hiddenRecordGroups' && currentContentId === 'hiddenRecordGroups' &&
hiddenRecordGroups.length === 0 hiddenRecordGroupIds.length === 0
) { ) {
onContentChange('recordGroups'); onContentChange('recordGroups');
} }
}, [hiddenRecordGroups, currentContentId, onContentChange]); }, [hiddenRecordGroupIds, currentContentId, onContentChange]);
return ( return (
<> <>

View File

@ -8,24 +8,21 @@ import {
} from 'twenty-ui'; } from 'twenty-ui';
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown'; import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown';
import { useRecordGroups } from '@/object-record/record-group/hooks/useRecordGroups'; import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector';
import { RecordGroupSort } from '@/object-record/record-group/types/RecordGroupSort'; import { RecordGroupSort } from '@/object-record/record-group/types/RecordGroupSort';
import { recordIndexRecordGroupSortComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupSortComponentState'; import { recordIndexRecordGroupSortComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupSortComponentState';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
export const ObjectOptionsDropdownRecordGroupSortContent = () => { export const ObjectOptionsDropdownRecordGroupSortContent = () => {
const { const { currentContentId, onContentChange, closeDropdown } =
currentContentId, useOptionsDropdown();
objectMetadataItem,
onContentChange,
closeDropdown,
} = useOptionsDropdown();
const { hiddenRecordGroups } = useRecordGroups({ const hiddenRecordGroupIds = useRecoilComponentValueV2(
objectNameSingular: objectMetadataItem.nameSingular, hiddenRecordGroupIdsComponentSelector,
}); );
const setRecordGroupSort = useSetRecoilComponentStateV2( const setRecordGroupSort = useSetRecoilComponentStateV2(
recordIndexRecordGroupSortComponentState, recordIndexRecordGroupSortComponentState,
@ -39,11 +36,11 @@ export const ObjectOptionsDropdownRecordGroupSortContent = () => {
useEffect(() => { useEffect(() => {
if ( if (
currentContentId === 'hiddenRecordGroups' && currentContentId === 'hiddenRecordGroups' &&
hiddenRecordGroups.length === 0 hiddenRecordGroupIds.length === 0
) { ) {
onContentChange('recordGroups'); onContentChange('recordGroups');
} }
}, [hiddenRecordGroups, currentContentId, onContentChange]); }, [hiddenRecordGroupIds, currentContentId, onContentChange]);
return ( return (
<> <>

View File

@ -13,8 +13,10 @@ import {
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown'; import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown';
import { RecordGroupsVisibilityDropdownSection } from '@/object-record/record-group/components/RecordGroupsVisibilityDropdownSection'; import { RecordGroupsVisibilityDropdownSection } from '@/object-record/record-group/components/RecordGroupsVisibilityDropdownSection';
import { useRecordGroupReorder } from '@/object-record/record-group/hooks/useRecordGroupReorder'; import { useRecordGroupReorder } from '@/object-record/record-group/hooks/useRecordGroupReorder';
import { useRecordGroups } from '@/object-record/record-group/hooks/useRecordGroups';
import { useRecordGroupVisibility } from '@/object-record/record-group/hooks/useRecordGroupVisibility'; import { useRecordGroupVisibility } from '@/object-record/record-group/hooks/useRecordGroupVisibility';
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector';
import { visibleRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector';
import { recordIndexRecordGroupHideComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupHideComponentState'; import { recordIndexRecordGroupHideComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupHideComponentState';
import { recordIndexRecordGroupIsDraggableSortComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexRecordGroupIsDraggableSortComponentSelector'; import { recordIndexRecordGroupIsDraggableSortComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexRecordGroupIsDraggableSortComponentSelector';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
@ -26,22 +28,20 @@ import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
export const ObjectOptionsDropdownRecordGroupsContent = () => { export const ObjectOptionsDropdownRecordGroupsContent = () => {
const isViewGroupEnabled = useIsFeatureEnabled('IS_VIEW_GROUPS_ENABLED'); const isViewGroupEnabled = useIsFeatureEnabled('IS_VIEW_GROUPS_ENABLED');
const { const { currentContentId, recordIndexId, onContentChange, resetContent } =
currentContentId, useOptionsDropdown();
viewType,
recordIndexId,
objectMetadataItem,
onContentChange,
resetContent,
} = useOptionsDropdown();
const { const recordGroupFieldMetadata = useRecoilComponentValueV2(
hiddenRecordGroups, recordGroupFieldMetadataComponentState,
visibleRecordGroups, );
viewGroupFieldMetadataItem,
} = useRecordGroups({ const visibleRecordGroupIds = useRecoilComponentValueV2(
objectNameSingular: objectMetadataItem.nameSingular, visibleRecordGroupIdsComponentSelector,
}); );
const hiddenRecordGroupIds = useRecoilComponentValueV2(
hiddenRecordGroupIdsComponentSelector,
);
const isDragableSortRecordGroup = useRecoilComponentValueV2( const isDragableSortRecordGroup = useRecoilComponentValueV2(
recordIndexRecordGroupIsDraggableSortComponentSelector, recordIndexRecordGroupIsDraggableSortComponentSelector,
@ -56,23 +56,21 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => {
handleHideEmptyRecordGroupChange, handleHideEmptyRecordGroupChange,
} = useRecordGroupVisibility({ } = useRecordGroupVisibility({
viewBarId: recordIndexId, viewBarId: recordIndexId,
viewType,
}); });
const { handleOrderChange: handleRecordGroupOrderChange } = const { handleOrderChange: handleRecordGroupOrderChange } =
useRecordGroupReorder({ useRecordGroupReorder({
objectNameSingular: objectMetadataItem.nameSingular,
viewBarId: recordIndexId, viewBarId: recordIndexId,
}); });
useEffect(() => { useEffect(() => {
if ( if (
currentContentId === 'hiddenRecordGroups' && currentContentId === 'hiddenRecordGroups' &&
hiddenRecordGroups.length === 0 hiddenRecordGroupIds.length === 0
) { ) {
onContentChange('recordGroups'); onContentChange('recordGroups');
} }
}, [hiddenRecordGroups, currentContentId, onContentChange]); }, [hiddenRecordGroupIds, currentContentId, onContentChange]);
return ( return (
<> <>
@ -86,9 +84,9 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => {
onClick={() => onContentChange('recordGroupFields')} onClick={() => onContentChange('recordGroupFields')}
LeftIcon={IconLayoutList} LeftIcon={IconLayoutList}
text={ text={
!viewGroupFieldMetadataItem !recordGroupFieldMetadata
? 'Group by' ? 'Group by'
: `Group by "${viewGroupFieldMetadataItem.label}"` : `Group by "${recordGroupFieldMetadata.label}"`
} }
hasSubMenu hasSubMenu
/> />
@ -108,12 +106,12 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => {
toggleSize="small" toggleSize="small"
/> />
</DropdownMenuItemsContainer> </DropdownMenuItemsContainer>
{visibleRecordGroups.length > 0 && ( {visibleRecordGroupIds.length > 0 && (
<> <>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<RecordGroupsVisibilityDropdownSection <RecordGroupsVisibilityDropdownSection
title="Visible groups" title="Visible groups"
recordGroups={visibleRecordGroups} recordGroupIds={visibleRecordGroupIds}
onDragEnd={handleRecordGroupOrderChange} onDragEnd={handleRecordGroupOrderChange}
onVisibilityChange={handleRecordGroupVisibilityChange} onVisibilityChange={handleRecordGroupVisibilityChange}
isDraggable={isDragableSortRecordGroup} isDraggable={isDragableSortRecordGroup}
@ -121,14 +119,14 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => {
/> />
</> </>
)} )}
{hiddenRecordGroups.length > 0 && ( {hiddenRecordGroupIds.length > 0 && (
<> <>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItemsContainer> <DropdownMenuItemsContainer>
<MenuItemNavigate <MenuItemNavigate
onClick={() => onContentChange('hiddenRecordGroups')} onClick={() => onContentChange('hiddenRecordGroups')}
LeftIcon={IconEyeOff} LeftIcon={IconEyeOff}
text={`Hidden ${viewGroupFieldMetadataItem?.label ?? ''}`} text={`Hidden ${recordGroupFieldMetadata?.label ?? ''}`}
/> />
</DropdownMenuItemsContainer> </DropdownMenuItemsContainer>
</> </>

View File

@ -4,10 +4,11 @@ import { useRecoilState } from 'recoil';
import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata'; import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard'; import { isRecordBoardCompactModeActiveComponentState } from '@/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState';
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'; import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState'; import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState';
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition'; import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
import { useSaveCurrentViewFields } from '@/views/hooks/useSaveCurrentViewFields'; import { useSaveCurrentViewFields } from '@/views/hooks/useSaveCurrentViewFields';
import { useUpdateCurrentView } from '@/views/hooks/useUpdateCurrentView'; import { useUpdateCurrentView } from '@/views/hooks/useUpdateCurrentView';
import { GraphQLView } from '@/views/types/GraphQLView'; import { GraphQLView } from '@/views/types/GraphQLView';
@ -32,11 +33,12 @@ export const useObjectOptionsForBoard = ({
const { saveViewFields } = useSaveCurrentViewFields(viewBarId); const { saveViewFields } = useSaveCurrentViewFields(viewBarId);
const { updateCurrentView } = useUpdateCurrentView(viewBarId); const { updateCurrentView } = useUpdateCurrentView(viewBarId);
const { isCompactModeActiveState } = useRecordBoard(recordBoardId);
const [isCompactModeActive, setIsCompactModeActive] = useRecoilState( const [isCompactModeActive, setIsCompactModeActive] =
isCompactModeActiveState, useRecoilComponentStateV2(
); isRecordBoardCompactModeActiveComponentState,
recordBoardId,
);
const { objectMetadataItem } = useObjectMetadataItem({ const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular, objectNameSingular,

View File

@ -1,7 +1,7 @@
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { DragDropContext, OnDragEndResponder } from '@hello-pangea/dnd'; // Atlassian dnd does not support StrictMode from RN 18, so we use a fork @hello-pangea/dnd https://github.com/atlassian/react-beautiful-dnd/issues/2350 import { DragDropContext, OnDragEndResponder } from '@hello-pangea/dnd'; // Atlassian dnd does not support StrictMode from RN 18, so we use a fork @hello-pangea/dnd https://github.com/atlassian/react-beautiful-dnd/issues/2350
import { useContext, useRef } from 'react'; import { useContext, useRef } from 'react';
import { useRecoilCallback, useRecoilValue } from 'recoil'; import { useRecoilCallback } from 'recoil';
import { Key } from 'ts-key-enum'; import { Key } from 'ts-key-enum';
import { ActionBarHotkeyScope } from '@/action-menu/types/ActionBarHotKeyScope'; import { ActionBarHotkeyScope } from '@/action-menu/types/ActionBarHotKeyScope';
@ -9,11 +9,15 @@ import { RecordBoardHeader } from '@/object-record/record-board/components/Recor
import { RecordBoardStickyHeaderEffect } from '@/object-record/record-board/components/RecordBoardStickyHeaderEffect'; import { RecordBoardStickyHeaderEffect } from '@/object-record/record-board/components/RecordBoardStickyHeaderEffect';
import { RECORD_BOARD_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-board/constants/RecordBoardClickOutsideListenerId'; import { RECORD_BOARD_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-board/constants/RecordBoardClickOutsideListenerId';
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext'; import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates';
import { useRecordBoardSelection } from '@/object-record/record-board/hooks/useRecordBoardSelection'; import { useRecordBoardSelection } from '@/object-record/record-board/hooks/useRecordBoardSelection';
import { RecordBoardColumn } from '@/object-record/record-board/record-board-column/components/RecordBoardColumn'; import { RecordBoardColumn } from '@/object-record/record-board/record-board-column/components/RecordBoardColumn';
import { RecordBoardScope } from '@/object-record/record-board/scopes/RecordBoardScope'; import { RecordBoardScope } from '@/object-record/record-board/scopes/RecordBoardScope';
import { RecordBoardComponentInstanceContext } from '@/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext';
import { getDraggedRecordPosition } from '@/object-record/record-board/utils/getDraggedRecordPosition'; import { getDraggedRecordPosition } from '@/object-record/record-board/utils/getDraggedRecordPosition';
import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
import { visibleRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector';
import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState';
import { recordIndexRowIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRowIdsByGroupComponentFamilyState';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope'; import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect'; import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
@ -21,6 +25,9 @@ import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useListenClickOutsideV2 } from '@/ui/utilities/pointer-event/hooks/useListenClickOutsideV2'; import { useListenClickOutsideV2 } from '@/ui/utilities/pointer-event/hooks/useListenClickOutsideV2';
import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId'; import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
import { useScrollRestoration } from '~/hooks/useScrollRestoration'; import { useScrollRestoration } from '~/hooks/useScrollRestoration';
const StyledContainer = styled.div` const StyledContainer = styled.div`
@ -58,14 +65,17 @@ export const RecordBoard = () => {
useContext(RecordBoardContext); useContext(RecordBoardContext);
const boardRef = useRef<HTMLDivElement>(null); const boardRef = useRef<HTMLDivElement>(null);
const { const visibleRecordGroupIds = useRecoilComponentValueV2(
columnIdsState, visibleRecordGroupIdsComponentSelector,
columnsFamilySelector, );
recordIdsByColumnIdFamilyState,
allRecordIdsSelector,
} = useRecordBoardStates(recordBoardId);
const columnIds = useRecoilValue(columnIdsState); const recordIndexRowIdsByGroupFamilyState = useRecoilComponentCallbackStateV2(
recordIndexRowIdsByGroupComponentFamilyState,
);
const recordIndexAllRowIdsState = useRecoilComponentCallbackStateV2(
recordIndexAllRowIdsComponentState,
);
const { resetRecordSelection, setRecordAsSelected } = const { resetRecordSelection, setRecordAsSelected } =
useRecordBoardSelection(recordBoardId); useRecordBoardSelection(recordBoardId);
@ -85,15 +95,16 @@ export const RecordBoard = () => {
const selectAll = useRecoilCallback( const selectAll = useRecoilCallback(
({ snapshot }) => ({ snapshot }) =>
() => { () => {
const allRecordIds = snapshot const allRecordIds = getSnapshotValue(
.getLoadable(allRecordIdsSelector()) snapshot,
.getValue(); recordIndexAllRowIdsState,
);
for (const recordId of allRecordIds) { for (const recordId of allRecordIds) {
setRecordAsSelected(recordId, true); setRecordAsSelected(recordId, true);
} }
}, },
[allRecordIdsSelector, setRecordAsSelected], [recordIndexAllRowIdsState, setRecordAsSelected],
); );
useScopedHotkeys('ctrl+a,meta+a', selectAll, TableHotkeyScope.Table); useScopedHotkeys('ctrl+a,meta+a', selectAll, TableHotkeyScope.Table);
@ -111,42 +122,40 @@ export const RecordBoard = () => {
if (!result.destination) return; if (!result.destination) return;
const draggedRecordId = result.draggableId; const draggedRecordId = result.draggableId;
const sourceColumnId = result.source.droppableId; const sourceRecordGroupId = result.source.droppableId;
const destinationColumnId = result.destination.droppableId; const destinationRecordGroupId = result.destination.droppableId;
const destinationIndexInColumn = result.destination.index; const destinationIndexInColumn = result.destination.index;
if (!destinationColumnId || !selectFieldMetadataItem) return; if (!destinationRecordGroupId || !selectFieldMetadataItem) return;
const column = snapshot const recordGroup = getSnapshotValue(
.getLoadable(columnsFamilySelector(destinationColumnId)) snapshot,
.getValue(); recordGroupDefinitionFamilyState(destinationRecordGroupId),
);
if (!column) return; if (!recordGroup) return;
const destinationColumnRecordIds = snapshot const destinationRecordByGroupIds = getSnapshotValue(
.getLoadable(recordIdsByColumnIdFamilyState(destinationColumnId)) snapshot,
.getValue(); recordIndexRowIdsByGroupFamilyState(destinationRecordGroupId),
const otherRecordsInDestinationColumn = );
sourceColumnId === destinationColumnId const otherRecordIdsInDestinationColumn =
? destinationColumnRecordIds.filter( sourceRecordGroupId === destinationRecordGroupId
? destinationRecordByGroupIds.filter(
(recordId) => recordId !== draggedRecordId, (recordId) => recordId !== draggedRecordId,
) )
: destinationColumnRecordIds; : destinationRecordByGroupIds;
const recordBeforeId = const recordBeforeId =
otherRecordsInDestinationColumn[destinationIndexInColumn - 1]; otherRecordIdsInDestinationColumn[destinationIndexInColumn - 1];
const recordBefore = recordBeforeId const recordBefore = recordBeforeId
? snapshot ? getSnapshotValue(snapshot, recordStoreFamilyState(recordBeforeId))
.getLoadable(recordStoreFamilyState(recordBeforeId))
.getValue()
: null; : null;
const recordAfterId = const recordAfterId =
otherRecordsInDestinationColumn[destinationIndexInColumn]; otherRecordIdsInDestinationColumn[destinationIndexInColumn];
const recordAfter = recordAfterId const recordAfter = recordAfterId
? snapshot ? getSnapshotValue(snapshot, recordStoreFamilyState(recordAfterId))
.getLoadable(recordStoreFamilyState(recordAfterId))
.getValue()
: null; : null;
const draggedRecordPosition = getDraggedRecordPosition( const draggedRecordPosition = getDraggedRecordPosition(
@ -157,14 +166,13 @@ export const RecordBoard = () => {
updateOneRecord({ updateOneRecord({
idToUpdate: draggedRecordId, idToUpdate: draggedRecordId,
updateOneRecordInput: { updateOneRecordInput: {
[selectFieldMetadataItem.name]: column.value, [selectFieldMetadataItem.name]: recordGroup.value,
position: draggedRecordPosition, position: draggedRecordPosition,
}, },
}); });
}, },
[ [
columnsFamilySelector, recordIndexRowIdsByGroupFamilyState,
recordIdsByColumnIdFamilyState,
selectFieldMetadataItem, selectFieldMetadataItem,
updateOneRecord, updateOneRecord,
], ],
@ -182,32 +190,36 @@ export const RecordBoard = () => {
onColumnsChange={() => {}} onColumnsChange={() => {}}
onFieldsChange={() => {}} onFieldsChange={() => {}}
> >
<ScrollWrapper contextProviderName="recordBoard"> <RecordBoardComponentInstanceContext.Provider
<RecordBoardStickyHeaderEffect /> value={{ instanceId: recordBoardId }}
<StyledContainerContainer> >
<RecordBoardHeader /> <ScrollWrapper contextProviderName="recordBoard">
<StyledBoardContentContainer> <RecordBoardStickyHeaderEffect />
<StyledContainer ref={boardRef}> <StyledContainerContainer>
<DragDropContext onDragEnd={handleDragEnd}> <RecordBoardHeader />
<StyledColumnContainer> <StyledBoardContentContainer>
{columnIds.map((columnId) => ( <StyledContainer ref={boardRef}>
<RecordBoardColumn <DragDropContext onDragEnd={handleDragEnd}>
key={columnId} <StyledColumnContainer>
recordBoardColumnId={columnId} {visibleRecordGroupIds.map((recordGroupId) => (
/> <RecordBoardColumn
))} key={recordGroupId}
</StyledColumnContainer> recordBoardColumnId={recordGroupId}
</DragDropContext> />
</StyledContainer> ))}
<RecordBoardScrollRestoreEffect /> </StyledColumnContainer>
<DragSelect </DragDropContext>
dragSelectable={boardRef} </StyledContainer>
onDragSelectionStart={resetRecordSelection} <RecordBoardScrollRestoreEffect />
onDragSelectionChange={setRecordAsSelected} <DragSelect
/> dragSelectable={boardRef}
</StyledBoardContentContainer> onDragSelectionStart={resetRecordSelection}
</StyledContainerContainer> onDragSelectionChange={setRecordAsSelected}
</ScrollWrapper> />
</StyledBoardContentContainer>
</StyledContainerContainer>
</ScrollWrapper>
</RecordBoardComponentInstanceContext.Provider>
</RecordBoardScope> </RecordBoardScope>
); );
}; };

View File

@ -1,7 +1,6 @@
import { useRecoilValue } from 'recoil';
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates';
import { RecordBoardColumnHeaderWrapper } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderWrapper'; import { RecordBoardColumnHeaderWrapper } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderWrapper';
import { visibleRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
const StyledHeaderContainer = styled.div` const StyledHeaderContainer = styled.div`
@ -24,14 +23,17 @@ const StyledHeaderContainer = styled.div`
`; `;
export const RecordBoardHeader = () => { export const RecordBoardHeader = () => {
const { columnIdsState } = useRecordBoardStates(); const visibleRecordGroupIds = useRecoilComponentValueV2(
visibleRecordGroupIdsComponentSelector,
const columnIds = useRecoilValue(columnIdsState); );
return ( return (
<StyledHeaderContainer id="record-board-header"> <StyledHeaderContainer id="record-board-header">
{columnIds.map((columnId) => ( {visibleRecordGroupIds.map((recordGroupId) => (
<RecordBoardColumnHeaderWrapper columnId={columnId} key={columnId} /> <RecordBoardColumnHeaderWrapper
columnId={recordGroupId}
key={recordGroupId}
/>
))} ))}
</StyledHeaderContainer> </StyledHeaderContainer>
); );

View File

@ -1,103 +0,0 @@
import { RecordBoardScopeInternalContext } from '@/object-record/record-board/scopes/scope-internal-context/RecordBoardScopeInternalContext';
import { isRecordBoardCardSelectedComponentFamilyState } from '@/object-record/record-board/states/isRecordBoardCardSelectedComponentFamilyState';
import { isRecordBoardCompactModeActiveComponentState } from '@/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState';
import { isRecordBoardFetchingRecordsByColumnFamilyState } from '@/object-record/record-board/states/isRecordBoardFetchingRecordsByColumnFamilyState';
import { isRecordBoardFetchingRecordsComponentState } from '@/object-record/record-board/states/isRecordBoardFetchingRecordsComponentState';
import { recordBoardColumnIdsComponentState } from '@/object-record/record-board/states/recordBoardColumnIdsComponentState';
import { recordBoardFieldDefinitionsComponentState } from '@/object-record/record-board/states/recordBoardFieldDefinitionsComponentState';
import { recordBoardFiltersComponentState } from '@/object-record/record-board/states/recordBoardFiltersComponentState';
import { recordBoardKanbanFieldMetadataNameComponentState } from '@/object-record/record-board/states/recordBoardKanbanFieldMetadataNameComponentState';
import { recordBoardObjectSingularNameComponentState } from '@/object-record/record-board/states/recordBoardObjectSingularNameComponentState';
import { recordBoardRecordIdsByColumnIdComponentFamilyState } from '@/object-record/record-board/states/recordBoardRecordIdsByColumnIdComponentFamilyState';
import { recordBoardShouldFetchMoreInColumnComponentFamilyState } from '@/object-record/record-board/states/recordBoardShouldFetchMoreInColumnComponentFamilyState';
import { recordBoardSortsComponentState } from '@/object-record/record-board/states/recordBoardSortsComponentState';
import { recordBoardAllRecordIdsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardAllRecordIdsComponentSelector';
import { recordBoardColumnsComponentFamilySelector } from '@/object-record/record-board/states/selectors/recordBoardColumnsComponentFamilySelector';
import { recordBoardSelectedRecordIdsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardSelectedRecordIdsComponentSelector';
import { recordBoardShouldFetchMoreComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardShouldFetchMoreComponentFamilySelector';
import { recordBoardVisibleFieldDefinitionsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector';
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
import { getScopeIdOrUndefinedFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdOrUndefinedFromComponentId';
import { extractComponentFamilyState } from '@/ui/utilities/state/component-state/utils/extractComponentFamilyState';
import { extractComponentReadOnlySelector } from '@/ui/utilities/state/component-state/utils/extractComponentReadOnlySelector';
import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState';
export const useRecordBoardStates = (recordBoardId?: string) => {
const scopeId = useAvailableScopeIdOrThrow(
RecordBoardScopeInternalContext,
getScopeIdOrUndefinedFromComponentId(recordBoardId),
);
return {
scopeId,
objectSingularNameState: extractComponentState(
recordBoardObjectSingularNameComponentState,
scopeId,
),
kanbanFieldMetadataNameState: extractComponentState(
recordBoardKanbanFieldMetadataNameComponentState,
scopeId,
),
isFetchingRecordState: extractComponentState(
isRecordBoardFetchingRecordsComponentState,
scopeId,
),
isFetchingRecordsByColumnState: extractComponentFamilyState(
isRecordBoardFetchingRecordsByColumnFamilyState,
scopeId,
),
columnIdsState: extractComponentState(
recordBoardColumnIdsComponentState,
scopeId,
),
columnsFamilySelector: extractComponentFamilyState(
recordBoardColumnsComponentFamilySelector,
scopeId,
),
filtersState: extractComponentState(
recordBoardFiltersComponentState,
scopeId,
),
sortsState: extractComponentState(recordBoardSortsComponentState, scopeId),
fieldDefinitionsState: extractComponentState(
recordBoardFieldDefinitionsComponentState,
scopeId,
),
visibleFieldDefinitionsState: extractComponentReadOnlySelector(
recordBoardVisibleFieldDefinitionsComponentSelector,
scopeId,
),
recordIdsByColumnIdFamilyState: extractComponentFamilyState(
recordBoardRecordIdsByColumnIdComponentFamilyState,
scopeId,
),
isRecordBoardCardSelectedFamilyState: extractComponentFamilyState(
isRecordBoardCardSelectedComponentFamilyState,
scopeId,
),
allRecordIdsSelector: extractComponentReadOnlySelector(
recordBoardAllRecordIdsComponentSelector,
scopeId,
),
selectedRecordIdsSelector: extractComponentReadOnlySelector(
recordBoardSelectedRecordIdsComponentSelector,
scopeId,
),
isCompactModeActiveState: extractComponentState(
isRecordBoardCompactModeActiveComponentState,
scopeId,
),
shouldFetchMoreInColumnFamilyState: extractComponentFamilyState(
recordBoardShouldFetchMoreInColumnComponentFamilyState,
scopeId,
),
shouldFetchMoreSelector: extractComponentReadOnlySelector(
recordBoardShouldFetchMoreComponentSelector,
scopeId,
),
};
};

View File

@ -1,58 +0,0 @@
import { useRecoilCallback } from 'recoil';
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates';
import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition';
import { sortRecordGroupDefinitions } from '@/object-record/record-group/utils/sortRecordGroupDefinitions';
import { recordIndexRecordGroupSortComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupSortComponentState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
export const useSetRecordBoardColumns = (recordBoardId?: string) => {
const { scopeId, columnIdsState, columnsFamilySelector } =
useRecordBoardStates(recordBoardId);
const recordGroupSort = useRecoilComponentValueV2(
recordIndexRecordGroupSortComponentState,
recordBoardId,
);
const setColumns = useRecoilCallback(
({ set, snapshot }) =>
(columns: RecordGroupDefinition[]) => {
const currentColumnsIds = snapshot
.getLoadable(columnIdsState)
.getValue();
const sortedColumns = sortRecordGroupDefinitions(
columns,
recordGroupSort,
);
const columnIds = sortedColumns
.filter(({ isVisible }) => isVisible)
.map(({ id }) => id);
if (!isDeeplyEqual(currentColumnsIds, columnIds)) {
set(columnIdsState, columnIds);
}
columns.forEach((column) => {
const currentColumn = snapshot
.getLoadable(columnsFamilySelector(column.id))
.getValue();
if (isDeeplyEqual(currentColumn, column)) {
return;
}
set(columnsFamilySelector(column.id), column);
});
},
[columnIdsState, recordGroupSort, columnsFamilySelector],
);
return {
scopeId,
setColumns,
};
};

View File

@ -1,63 +0,0 @@
import { useRecoilCallback } from 'recoil';
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { sortRecordsByPosition } from '@/object-record/utils/sortRecordsByPosition';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
export const useSetRecordBoardRecordIds = (recordBoardId?: string) => {
const {
scopeId,
recordIdsByColumnIdFamilyState,
columnsFamilySelector,
columnIdsState,
kanbanFieldMetadataNameState,
} = useRecordBoardStates(recordBoardId);
const setRecordIds = useRecoilCallback(
({ set, snapshot }) =>
(records: ObjectRecord[]) => {
const columnIds = snapshot.getLoadable(columnIdsState).getValue();
columnIds.forEach((columnId) => {
const column = snapshot
.getLoadable(columnsFamilySelector(columnId))
.getValue();
const existingColumnRecordIds = snapshot
.getLoadable(recordIdsByColumnIdFamilyState(columnId))
.getValue();
const kanbanFieldMetadataName = snapshot
.getLoadable(kanbanFieldMetadataNameState)
.getValue();
if (!kanbanFieldMetadataName) {
return;
}
const columnRecordIds = records
.filter(
(record) => record[kanbanFieldMetadataName] === column?.value,
)
.sort(sortRecordsByPosition)
.map((record) => record.id);
if (!isDeeplyEqual(existingColumnRecordIds, columnRecordIds)) {
set(recordIdsByColumnIdFamilyState(columnId), columnRecordIds);
}
});
},
[
columnIdsState,
columnsFamilySelector,
recordIdsByColumnIdFamilyState,
kanbanFieldMetadataNameState,
],
);
return {
scopeId,
setRecordIds,
};
};

View File

@ -1,55 +0,0 @@
import { useRecoilCallback } from 'recoil';
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { sortRecordsByPosition } from '@/object-record/utils/sortRecordsByPosition';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
export const useSetRecordIdsForColumn = (recordBoardId?: string) => {
const {
scopeId,
recordIdsByColumnIdFamilyState,
columnsFamilySelector,
kanbanFieldMetadataNameState,
} = useRecordBoardStates(recordBoardId);
const setRecordIdsForColumn = useRecoilCallback(
({ set, snapshot }) =>
(columnId: string, records: ObjectRecord[]) => {
const column = snapshot
.getLoadable(columnsFamilySelector(columnId))
.getValue();
const existingColumnRecordIds = snapshot
.getLoadable(recordIdsByColumnIdFamilyState(columnId))
.getValue();
const kanbanFieldMetadataName = snapshot
.getLoadable(kanbanFieldMetadataNameState)
.getValue();
if (!kanbanFieldMetadataName) {
return;
}
const columnRecordIds = records
.filter((record) => record[kanbanFieldMetadataName] === column?.value)
.sort(sortRecordsByPosition)
.map((record) => record.id);
if (!isDeeplyEqual(existingColumnRecordIds, columnRecordIds)) {
set(recordIdsByColumnIdFamilyState(columnId), columnRecordIds);
}
},
[
columnsFamilySelector,
recordIdsByColumnIdFamilyState,
kanbanFieldMetadataNameState,
],
);
return {
scopeId,
setRecordIdsForColumn,
};
};

View File

@ -1,43 +0,0 @@
import { useSetRecoilState } from 'recoil';
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates';
import { useSetRecordBoardColumns } from '@/object-record/record-board/hooks/internal/useSetRecordBoardColumns';
import { useSetRecordBoardRecordIds } from '@/object-record/record-board/hooks/internal/useSetRecordBoardRecordIds';
import { useSetRecordIdsForColumn } from '@/object-record/record-board/hooks/internal/useSetRecordIdsForColumn';
export const useRecordBoard = (recordBoardId?: string) => {
const {
scopeId,
fieldDefinitionsState,
objectSingularNameState,
selectedRecordIdsSelector,
isCompactModeActiveState,
kanbanFieldMetadataNameState,
shouldFetchMoreSelector,
isFetchingRecordsByColumnState,
} = useRecordBoardStates(recordBoardId);
const { setColumns } = useSetRecordBoardColumns(recordBoardId);
const { setRecordIds } = useSetRecordBoardRecordIds(recordBoardId);
const { setRecordIdsForColumn } = useSetRecordIdsForColumn(recordBoardId);
const setFieldDefinitions = useSetRecoilState(fieldDefinitionsState);
const setObjectSingularName = useSetRecoilState(objectSingularNameState);
const setKanbanFieldMetadataName = useSetRecoilState(
kanbanFieldMetadataNameState,
);
return {
scopeId,
setColumns,
setRecordIds,
setFieldDefinitions,
setObjectSingularName,
setKanbanFieldMetadataName,
selectedRecordIdsSelector,
isCompactModeActiveState,
shouldFetchMoreSelector,
setRecordIdsForColumn,
isFetchingRecordsByColumnState,
};
};

View File

@ -2,13 +2,25 @@ import { useRecoilCallback } from 'recoil';
import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId'; import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId';
import { getActionMenuIdFromRecordIndexId } from '@/action-menu/utils/getActionMenuIdFromRecordIndexId'; import { getActionMenuIdFromRecordIndexId } from '@/action-menu/utils/getActionMenuIdFromRecordIndexId';
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; import { isRecordBoardCardSelectedComponentFamilyState } from '@/object-record/record-board/states/isRecordBoardCardSelectedComponentFamilyState';
import { recordBoardSelectedRecordIdsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardSelectedRecordIdsComponentSelector';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState';
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
export const useRecordBoardSelection = (recordBoardId: string) => { export const useRecordBoardSelection = (recordBoardId: string) => {
const { selectedRecordIdsSelector, isRecordBoardCardSelectedFamilyState } = const isRecordBoardCardSelectedFamilyState =
useRecordBoardStates(recordBoardId); useRecoilComponentCallbackStateV2(
isRecordBoardCardSelectedComponentFamilyState,
recordBoardId,
);
const recordBoardSelectedRecordIdsSelector =
useRecoilComponentCallbackStateV2(
recordBoardSelectedRecordIdsComponentSelector,
recordBoardId,
);
const isActionMenuDropdownOpenState = extractComponentState( const isActionMenuDropdownOpenState = extractComponentState(
isDropdownOpenComponentState, isDropdownOpenComponentState,
@ -22,9 +34,10 @@ export const useRecordBoardSelection = (recordBoardId: string) => {
() => { () => {
set(isActionMenuDropdownOpenState, false); set(isActionMenuDropdownOpenState, false);
const recordIds = snapshot const recordIds = getSnapshotValue(
.getLoadable(selectedRecordIdsSelector()) snapshot,
.getValue(); recordBoardSelectedRecordIdsSelector,
);
for (const recordId of recordIds) { for (const recordId of recordIds) {
set(isRecordBoardCardSelectedFamilyState(recordId), false); set(isRecordBoardCardSelectedFamilyState(recordId), false);
@ -32,7 +45,7 @@ export const useRecordBoardSelection = (recordBoardId: string) => {
}, },
[ [
isActionMenuDropdownOpenState, isActionMenuDropdownOpenState,
selectedRecordIdsSelector, recordBoardSelectedRecordIdsSelector,
isRecordBoardCardSelectedFamilyState, isRecordBoardCardSelectedFamilyState,
], ],
); );

View File

@ -0,0 +1,110 @@
import { useRecoilCallback } from 'recoil';
import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
import { visibleRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector';
import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState';
import { recordIndexRowIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRowIdsByGroupComponentFamilyState';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { sortRecordsByPosition } from '@/object-record/utils/sortRecordsByPosition';
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { isDefined } from '~/utils/isDefined';
export const useSetRecordBoardRecordIds = (recordBoardId?: string) => {
const visibleRecordGroupIdsSelector = useRecoilComponentCallbackStateV2(
visibleRecordGroupIdsComponentSelector,
);
const recordGroupFieldMetadataState = useRecoilComponentCallbackStateV2(
recordGroupFieldMetadataComponentState,
recordBoardId,
);
const recordIndexAllRowIdsState = useRecoilComponentCallbackStateV2(
recordIndexAllRowIdsComponentState,
recordBoardId,
);
const recordIndexRowIdsByGroupFamilyState = useRecoilComponentCallbackStateV2(
recordIndexRowIdsByGroupComponentFamilyState,
recordBoardId,
);
const setRecordIds = useRecoilCallback(
({ set, snapshot }) =>
(records: ObjectRecord[]) => {
const existingAllRowIds = getSnapshotValue(
snapshot,
recordIndexAllRowIdsState,
);
const recordGroupIds = getSnapshotValue(
snapshot,
visibleRecordGroupIdsSelector,
);
for (const recordGroupId of recordGroupIds) {
const recordGroup = getSnapshotValue(
snapshot,
recordGroupDefinitionFamilyState(recordGroupId),
);
const existingRecordGroupRowIds = getSnapshotValue(
snapshot,
recordIndexRowIdsByGroupFamilyState(recordGroupId),
);
const recordGroupFieldMetadata = getSnapshotValue(
snapshot,
recordGroupFieldMetadataState,
);
if (!isDefined(recordGroupFieldMetadata)) {
return;
}
const recordGroupRowIds = records
.filter(
(record) =>
record[recordGroupFieldMetadata.name] === recordGroup?.value,
)
.sort(sortRecordsByPosition)
.map((record) => record.id);
if (!isDeeplyEqual(existingRecordGroupRowIds, recordGroupRowIds)) {
set(
recordIndexRowIdsByGroupFamilyState(recordGroupId),
recordGroupRowIds,
);
}
}
const allRowIds: string[] = [];
for (const recordGroupId of recordGroupIds) {
const tableRowIdsByGroup = getSnapshotValue(
snapshot,
recordIndexRowIdsByGroupFamilyState(recordGroupId),
);
allRowIds.push(...tableRowIdsByGroup);
}
if (!isDeeplyEqual(existingAllRowIds, allRowIds)) {
set(recordIndexAllRowIdsState, allRowIds);
}
},
[
visibleRecordGroupIdsSelector,
recordIndexRowIdsByGroupFamilyState,
recordGroupFieldMetadataState,
recordIndexAllRowIdsState,
],
);
return {
setRecordIds,
};
};

View File

@ -0,0 +1,109 @@
import { useRecoilCallback } from 'recoil';
import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
import { recordGroupIdsComponentState } from '@/object-record/record-group/states/recordGroupIdsComponentState';
import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState';
import { recordIndexRowIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRowIdsByGroupComponentFamilyState';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { sortRecordsByPosition } from '@/object-record/utils/sortRecordsByPosition';
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { isDefined } from '~/utils/isDefined';
export const useSetRecordIdsForColumn = (recordBoardId?: string) => {
const recordGroupIdsState = useRecoilComponentCallbackStateV2(
recordGroupIdsComponentState,
recordBoardId,
);
const recordGroupFieldMetadataState = useRecoilComponentCallbackStateV2(
recordGroupFieldMetadataComponentState,
recordBoardId,
);
const recordIndexAllRowIdsState = useRecoilComponentCallbackStateV2(
recordIndexAllRowIdsComponentState,
recordBoardId,
);
const recordIndexRowIdsByGroupFamilyState = useRecoilComponentCallbackStateV2(
recordIndexRowIdsByGroupComponentFamilyState,
recordBoardId,
);
const setRecordIdsForColumn = useRecoilCallback(
({ set, snapshot }) =>
(currentRecordGroupId: string, records: ObjectRecord[]) => {
const existingAllRowIds = getSnapshotValue(
snapshot,
recordIndexAllRowIdsState,
);
const recordGroupIds = getSnapshotValue(snapshot, recordGroupIdsState);
const recordGroup = getSnapshotValue(
snapshot,
recordGroupDefinitionFamilyState(currentRecordGroupId),
);
const existingRecordGroupRowIds = getSnapshotValue(
snapshot,
recordIndexRowIdsByGroupFamilyState(currentRecordGroupId),
);
const recordGroupFieldMetadata = getSnapshotValue(
snapshot,
recordGroupFieldMetadataState,
);
if (!isDefined(recordGroupFieldMetadata)) {
return;
}
const recordGroupRowIds = records
.filter(
(record) =>
record[recordGroupFieldMetadata.name] === recordGroup?.value,
)
.sort(sortRecordsByPosition)
.map((record) => record.id);
if (!isDeeplyEqual(existingRecordGroupRowIds, recordGroupRowIds)) {
set(
recordIndexRowIdsByGroupFamilyState(currentRecordGroupId),
recordGroupRowIds,
);
}
const allRowIds: string[] = [];
for (const recordGroupId of recordGroupIds) {
const tableRowIdsByGroup =
recordGroupId !== currentRecordGroupId
? getSnapshotValue(
snapshot,
recordIndexRowIdsByGroupFamilyState(recordGroupId),
)
: recordGroupRowIds;
allRowIds.push(...tableRowIdsByGroup);
}
if (!isDeeplyEqual(existingAllRowIds, allRowIds)) {
set(recordIndexAllRowIdsState, allRowIds);
}
},
[
recordGroupIdsState,
recordIndexRowIdsByGroupFamilyState,
recordGroupFieldMetadataState,
recordIndexAllRowIdsState,
],
);
return {
setRecordIdsForColumn,
};
};

View File

@ -3,9 +3,11 @@ import { recordIndexActionMenuDropdownPositionComponentState } from '@/action-me
import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId'; import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId';
import { getActionMenuIdFromRecordIndexId } from '@/action-menu/utils/getActionMenuIdFromRecordIndexId'; import { getActionMenuIdFromRecordIndexId } from '@/action-menu/utils/getActionMenuIdFromRecordIndexId';
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext'; import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates';
import { RecordBoardCardContext } from '@/object-record/record-board/record-board-card/contexts/RecordBoardCardContext'; import { RecordBoardCardContext } from '@/object-record/record-board/record-board-card/contexts/RecordBoardCardContext';
import { RecordBoardScopeInternalContext } from '@/object-record/record-board/scopes/scope-internal-context/RecordBoardScopeInternalContext'; import { RecordBoardScopeInternalContext } from '@/object-record/record-board/scopes/scope-internal-context/RecordBoardScopeInternalContext';
import { isRecordBoardCardSelectedComponentFamilyState } from '@/object-record/record-board/states/isRecordBoardCardSelectedComponentFamilyState';
import { isRecordBoardCompactModeActiveComponentState } from '@/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState';
import { recordBoardVisibleFieldDefinitionsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector';
import { import {
FieldContext, FieldContext,
RecordUpdateHook, RecordUpdateHook,
@ -22,11 +24,13 @@ import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { TextInput } from '@/ui/input/components/TextInput'; import { TextInput } from '@/ui/input/components/TextInput';
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
import { RecordBoardScrollWrapperContext } from '@/ui/utilities/scroll/contexts/ScrollWrapperContexts'; import { RecordBoardScrollWrapperContext } from '@/ui/utilities/scroll/contexts/ScrollWrapperContexts';
import { useRecoilComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyStateV2';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { ReactNode, useContext, useState } from 'react'; import { ReactNode, useContext, useState } from 'react';
import { InView, useInView } from 'react-intersection-observer'; import { InView, useInView } from 'react-intersection-observer';
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; import { useRecoilValue, useSetRecoilState } from 'recoil';
import { import {
AnimatedEaseInOut, AnimatedEaseInOut,
AvatarChipVariant, AvatarChipVariant,
@ -157,27 +161,30 @@ export const RecordBoardCard = ({
onCreateSuccess?: () => void; onCreateSuccess?: () => void;
position?: 'first' | 'last'; position?: 'first' | 'last';
}) => { }) => {
const [newLabelValue, setNewLabelValue] = useState('');
const { handleBlur, handleInputEnter } = useAddNewCard();
const { recordId } = useContext(RecordBoardCardContext); const { recordId } = useContext(RecordBoardCardContext);
const [newLabelValue, setNewLabelValue] = useState('');
const { handleBlur, handleInputEnter } = useAddNewCard();
const { updateOneRecord, objectMetadataItem } = const { updateOneRecord, objectMetadataItem } =
useContext(RecordBoardContext); useContext(RecordBoardContext);
const {
isCompactModeActiveState, const visibleFieldDefinitions = useRecoilComponentValueV2(
isRecordBoardCardSelectedFamilyState, recordBoardVisibleFieldDefinitionsComponentSelector,
visibleFieldDefinitionsState, );
} = useRecordBoardStates();
const isCompactModeActive = useRecoilValue(isCompactModeActiveState); const isCompactModeActive = useRecoilComponentValueV2(
isRecordBoardCompactModeActiveComponentState,
);
const [isCardExpanded, setIsCardExpanded] = useState(false); const [isCardExpanded, setIsCardExpanded] = useState(false);
const [isCurrentCardSelected, setIsCurrentCardSelected] = useRecoilState( const [isCurrentCardSelected, setIsCurrentCardSelected] =
isRecordBoardCardSelectedFamilyState(recordId), useRecoilComponentFamilyStateV2(
); isRecordBoardCardSelectedComponentFamilyState,
recordId,
const visibleFieldDefinitions = useRecoilValue( );
visibleFieldDefinitionsState(),
);
const record = useRecoilValue(recordStoreFamilyState(recordId)); const record = useRecoilValue(recordStoreFamilyState(recordId));

View File

@ -1,10 +1,12 @@
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { Droppable } from '@hello-pangea/dnd'; import { Droppable } from '@hello-pangea/dnd';
import { useRecoilValue } from 'recoil';
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates';
import { RecordBoardColumnCardsContainer } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnCardsContainer'; import { RecordBoardColumnCardsContainer } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnCardsContainer';
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext'; import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
import { recordIndexRowIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRowIdsByGroupComponentFamilyState';
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';
import { useRecoilValue } from 'recoil';
const StyledColumn = styled.div` const StyledColumn = styled.div`
background-color: ${({ theme }) => theme.background.primary}; background-color: ${({ theme }) => theme.background.primary};
@ -25,27 +27,26 @@ type RecordBoardColumnProps = {
export const RecordBoardColumn = ({ export const RecordBoardColumn = ({
recordBoardColumnId, recordBoardColumnId,
}: RecordBoardColumnProps) => { }: RecordBoardColumnProps) => {
const { columnsFamilySelector, recordIdsByColumnIdFamilyState } = const recordGroupDefinition = useRecoilValue(
useRecordBoardStates(); recordGroupDefinitionFamilyState(recordBoardColumnId),
const columnDefinition = useRecoilValue(
columnsFamilySelector(recordBoardColumnId),
); );
const recordIds = useRecoilValue( const recordRowIdsByGroup = useRecoilComponentFamilyValueV2(
recordIdsByColumnIdFamilyState(recordBoardColumnId), recordIndexRowIdsByGroupComponentFamilyState,
recordBoardColumnId,
); );
if (!columnDefinition) { if (!recordGroupDefinition) {
return null; return null;
} }
return ( return (
<RecordBoardColumnContext.Provider <RecordBoardColumnContext.Provider
value={{ value={{
columnDefinition: columnDefinition, columnDefinition: recordGroupDefinition,
recordCount: recordIds.length, recordCount: recordRowIdsByGroup.length,
columnId: recordBoardColumnId, columnId: recordBoardColumnId,
recordIds, recordIds: recordRowIdsByGroup,
}} }}
> >
<Droppable droppableId={recordBoardColumnId}> <Droppable droppableId={recordBoardColumnId}>
@ -53,7 +54,7 @@ export const RecordBoardColumn = ({
<StyledColumn> <StyledColumn>
<RecordBoardColumnCardsContainer <RecordBoardColumnCardsContainer
droppableProvided={droppableProvided} droppableProvided={droppableProvided}
recordIds={recordIds} recordIds={recordRowIdsByGroup}
/> />
</StyledColumn> </StyledColumn>
)} )}

View File

@ -5,7 +5,6 @@ import { useRecoilValue } from 'recoil';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext'; import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates';
import { RecordBoardColumnCardContainerSkeletonLoader } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnCardContainerSkeletonLoader'; import { RecordBoardColumnCardContainerSkeletonLoader } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnCardContainerSkeletonLoader';
import { RecordBoardColumnCardsMemo } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnCardsMemo'; import { RecordBoardColumnCardsMemo } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnCardsMemo';
import { RecordBoardColumnFetchMoreLoader } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnFetchMoreLoader'; import { RecordBoardColumnFetchMoreLoader } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnFetchMoreLoader';
@ -15,7 +14,10 @@ import { RecordBoardColumnNewRecordButton } from '@/object-record/record-board/r
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext'; import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
import { useIsOpportunitiesCompanyFieldDisabled } from '@/object-record/record-board/record-board-column/hooks/useIsOpportunitiesCompanyFieldDisabled'; import { useIsOpportunitiesCompanyFieldDisabled } from '@/object-record/record-board/record-board-column/hooks/useIsOpportunitiesCompanyFieldDisabled';
import { getNumberOfCardsPerColumnForSkeletonLoading } from '@/object-record/record-board/record-board-column/utils/getNumberOfCardsPerColumnForSkeletonLoading'; import { getNumberOfCardsPerColumnForSkeletonLoading } from '@/object-record/record-board/record-board-column/utils/getNumberOfCardsPerColumnForSkeletonLoading';
import { isRecordBoardCompactModeActiveComponentState } from '@/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState';
import { recordBoardVisibleFieldDefinitionsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector';
import { isRecordIndexBoardColumnLoadingFamilyState } from '@/object-record/states/isRecordBoardColumnLoadingFamilyState'; import { isRecordIndexBoardColumnLoadingFamilyState } from '@/object-record/states/isRecordBoardColumnLoadingFamilyState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
const StyledColumnCardsContainer = styled.div` const StyledColumnCardsContainer = styled.div`
display: flex; display: flex;
@ -56,16 +58,16 @@ export const RecordBoardColumnCardsContainer = ({
isRecordIndexBoardColumnLoadingFamilyState(columnId), isRecordIndexBoardColumnLoadingFamilyState(columnId),
); );
const { isCompactModeActiveState, visibleFieldDefinitionsState } = const visibleFieldDefinitions = useRecoilComponentValueV2(
useRecordBoardStates(); recordBoardVisibleFieldDefinitionsComponentSelector,
const visibleFieldDefinitions = useRecoilValue(
visibleFieldDefinitionsState(),
); );
const numberOfFields = visibleFieldDefinitions.length; const numberOfFields = visibleFieldDefinitions.length;
const isCompactModeActive = useRecoilValue(isCompactModeActiveState); const isCompactModeActive = useRecoilComponentValueV2(
isRecordBoardCompactModeActiveComponentState,
);
const { isOpportunitiesCompanyFieldDisabled } = const { isOpportunitiesCompanyFieldDisabled } =
useIsOpportunitiesCompanyFieldDisabled(); useIsOpportunitiesCompanyFieldDisabled();

View File

@ -5,7 +5,6 @@ import { useRecordGroupActions } from '@/object-record/record-group/hooks/useRec
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { ViewType } from '@/views/types/ViewType';
import { MenuItem } from 'twenty-ui'; import { MenuItem } from 'twenty-ui';
const StyledMenuContainer = styled.div` const StyledMenuContainer = styled.div`
@ -26,9 +25,7 @@ export const RecordBoardColumnDropdownMenu = ({
}: RecordBoardColumnDropdownMenuProps) => { }: RecordBoardColumnDropdownMenuProps) => {
const boardColumnMenuRef = useRef<HTMLDivElement>(null); const boardColumnMenuRef = useRef<HTMLDivElement>(null);
const recordGroupActions = useRecordGroupActions({ const recordGroupActions = useRecordGroupActions();
viewType: ViewType.Kanban,
});
const closeMenu = useCallback(() => { const closeMenu = useCallback(() => {
onClose(); onClose();

View File

@ -1,11 +1,13 @@
import styled from '@emotion/styled';
import { useContext, useEffect } from 'react'; import { useContext, useEffect } from 'react';
import { useInView } from 'react-intersection-observer'; import { useInView } from 'react-intersection-observer';
import styled from '@emotion/styled'; import { useRecoilValue } from 'recoil';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { GRAY_SCALE } from 'twenty-ui'; import { GRAY_SCALE } from 'twenty-ui';
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates';
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext'; import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
import { isRecordBoardFetchingRecordsByColumnFamilyState } from '@/object-record/record-board/states/isRecordBoardFetchingRecordsByColumnFamilyState';
import { recordBoardShouldFetchMoreInColumnComponentFamilyState } from '@/object-record/record-board/states/recordBoardShouldFetchMoreInColumnComponentFamilyState';
import { useSetRecoilComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentFamilyStateV2';
const StyledText = styled.div` const StyledText = styled.div`
align-items: center; align-items: center;
@ -19,15 +21,14 @@ const StyledText = styled.div`
export const RecordBoardColumnFetchMoreLoader = () => { export const RecordBoardColumnFetchMoreLoader = () => {
const { columnDefinition } = useContext(RecordBoardColumnContext); const { columnDefinition } = useContext(RecordBoardColumnContext);
const { shouldFetchMoreInColumnFamilyState, isFetchingRecordsByColumnState } =
useRecordBoardStates();
const isFetchingRecord = useRecoilValue( const isFetchingRecord = useRecoilValue(
isFetchingRecordsByColumnState({ columnId: columnDefinition.id }), isRecordBoardFetchingRecordsByColumnFamilyState(columnDefinition.id),
); );
const setShouldFetchMore = useSetRecoilState( const setShouldFetchMore = useSetRecoilComponentFamilyStateV2(
shouldFetchMoreInColumnFamilyState(columnDefinition.id), recordBoardShouldFetchMoreInColumnComponentFamilyState,
columnDefinition.id,
); );
const { ref, inView } = useInView(); const { ref, inView } = useInView();

View File

@ -1,8 +1,10 @@
import { isDefined } from 'twenty-ui'; import { isDefined } from 'twenty-ui';
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates';
import { RecordBoardColumnHeader } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeader'; import { RecordBoardColumnHeader } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeader';
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext'; import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
import { recordIndexRowIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRowIdsByGroupComponentFamilyState';
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
type RecordBoardColumnHeaderWrapperProps = { type RecordBoardColumnHeaderWrapperProps = {
@ -12,14 +14,16 @@ type RecordBoardColumnHeaderWrapperProps = {
export const RecordBoardColumnHeaderWrapper = ({ export const RecordBoardColumnHeaderWrapper = ({
columnId, columnId,
}: RecordBoardColumnHeaderWrapperProps) => { }: RecordBoardColumnHeaderWrapperProps) => {
const { columnsFamilySelector, recordIdsByColumnIdFamilyState } = const recordGroupDefinition = useRecoilValue(
useRecordBoardStates(); recordGroupDefinitionFamilyState(columnId),
);
const columnDefinition = useRecoilValue(columnsFamilySelector(columnId)); const recordRowIdsByGroup = useRecoilComponentFamilyValueV2(
recordIndexRowIdsByGroupComponentFamilyState,
columnId,
);
const recordIds = useRecoilValue(recordIdsByColumnIdFamilyState(columnId)); if (!isDefined(recordGroupDefinition)) {
if (!isDefined(columnDefinition)) {
return null; return null;
} }
@ -27,9 +31,9 @@ export const RecordBoardColumnHeaderWrapper = ({
<RecordBoardColumnContext.Provider <RecordBoardColumnContext.Provider
value={{ value={{
columnId, columnId,
columnDefinition: columnDefinition, columnDefinition: recordGroupDefinition,
recordCount: recordIds.length, recordCount: recordRowIdsByGroup.length,
recordIds, recordIds: recordRowIdsByGroup,
}} }}
> >
<RecordBoardColumnHeader /> <RecordBoardColumnHeader />

View File

@ -1,12 +1,12 @@
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates';
import { useAddNewCard } from '@/object-record/record-board/record-board-column/hooks/useAddNewCard'; import { useAddNewCard } from '@/object-record/record-board/record-board-column/hooks/useAddNewCard';
import { useRecoilValue } from 'recoil'; import { recordBoardVisibleFieldDefinitionsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
export const useColumnNewCardActions = (columnId: string) => { export const useColumnNewCardActions = (columnId: string) => {
const { visibleFieldDefinitionsState } = useRecordBoardStates(); const visibleFieldDefinitions = useRecoilComponentValueV2(
const visibleFieldDefinitions = useRecoilValue( recordBoardVisibleFieldDefinitionsComponentSelector,
visibleFieldDefinitionsState(),
); );
const labelIdentifierField = visibleFieldDefinitions.find( const labelIdentifierField = visibleFieldDefinitions.find(
(field) => field.isLabelIdentifier, (field) => field.isLabelIdentifier,
); );

View File

@ -0,0 +1,4 @@
import { createComponentInstanceContext } from '@/ui/utilities/state/component-state/utils/createComponentInstanceContext';
export const RecordBoardComponentInstanceContext =
createComponentInstanceContext();

View File

@ -1,7 +1,9 @@
import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState'; import { RecordBoardComponentInstanceContext } from '@/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext';
import { createComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentFamilyStateV2';
export const isRecordBoardCardSelectedComponentFamilyState = export const isRecordBoardCardSelectedComponentFamilyState =
createComponentFamilyState<boolean, string>({ createComponentFamilyStateV2<boolean, string>({
key: 'isRecordBoardCardSelectedComponentFamilyState', key: 'isRecordBoardCardSelectedComponentFamilyState',
defaultValue: false, defaultValue: false,
componentInstanceContext: RecordBoardComponentInstanceContext,
}); });

View File

@ -1,7 +1,9 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; import { RecordBoardComponentInstanceContext } from '@/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
export const isRecordBoardCompactModeActiveComponentState = export const isRecordBoardCompactModeActiveComponentState =
createComponentState<boolean>({ createComponentStateV2<boolean>({
key: 'isRecordBoardCompactModeActiveComponentState', key: 'isRecordBoardCompactModeActiveComponentState',
defaultValue: false, defaultValue: false,
componentInstanceContext: RecordBoardComponentInstanceContext,
}); });

View File

@ -1,7 +1,10 @@
import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState'; import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition';
import { atomFamily } from 'recoil';
export const isRecordBoardFetchingRecordsByColumnFamilyState = export const isRecordBoardFetchingRecordsByColumnFamilyState = atomFamily<
createComponentFamilyState<boolean, { columnId: string }>({ boolean,
key: 'isRecordBoardFetchingRecordsByColumnFamilyState', RecordGroupDefinition['id']
defaultValue: false, >({
}); key: 'isRecordBoardFetchingRecordsByColumnFamilyState',
default: false,
});

View File

@ -1,7 +0,0 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
export const isRecordBoardFetchingRecordsComponentState =
createComponentState<boolean>({
key: 'isRecordBoardFetchingRecordsComponentState',
defaultValue: false,
});

View File

@ -1,8 +0,0 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
export const recordBoardColumnIdsComponentState = createComponentState<
string[]
>({
key: 'recordBoardColumnIdsComponentState',
defaultValue: [],
});

View File

@ -1,8 +0,0 @@
import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition';
import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState';
export const recordBoardColumnsComponentFamilyState =
createComponentFamilyState<RecordGroupDefinition | undefined, string>({
key: 'recordBoardColumnsComponentFamilyState',
defaultValue: undefined,
});

View File

@ -1,10 +1,12 @@
import { RecordBoardComponentInstanceContext } from '@/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext';
import { RecordBoardFieldDefinition } from '@/object-record/record-board/types/RecordBoardFieldDefinition'; import { RecordBoardFieldDefinition } from '@/object-record/record-board/types/RecordBoardFieldDefinition';
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'; import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
export const recordBoardFieldDefinitionsComponentState = createComponentState< export const recordBoardFieldDefinitionsComponentState = createComponentStateV2<
RecordBoardFieldDefinition<FieldMetadata>[] RecordBoardFieldDefinition<FieldMetadata>[]
>({ >({
key: 'recordBoardFieldDefinitionsComponentState', key: 'recordBoardFieldDefinitionsComponentState',
defaultValue: [], defaultValue: [],
componentInstanceContext: RecordBoardComponentInstanceContext,
}); });

View File

@ -1,7 +0,0 @@
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
export const recordBoardFiltersComponentState = createComponentState<Filter[]>({
key: 'recordBoardFiltersComponentState',
defaultValue: [],
});

View File

@ -1,7 +0,0 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
export const recordBoardKanbanFieldMetadataNameComponentState =
createComponentState<string | undefined>({
key: 'recordBoardKanbanFieldMetadataNameComponentState',
defaultValue: undefined,
});

View File

@ -1,7 +0,0 @@
import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState';
export const recordBoardRecordIdsByColumnIdComponentFamilyState =
createComponentFamilyState<string[], string>({
key: 'recordBoardRecordIdsByColumnIdComponentFamilyState',
defaultValue: [],
});

View File

@ -1,7 +1,9 @@
import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState'; import { RecordBoardComponentInstanceContext } from '@/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext';
import { createComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentFamilyStateV2';
export const recordBoardShouldFetchMoreInColumnComponentFamilyState = export const recordBoardShouldFetchMoreInColumnComponentFamilyState =
createComponentFamilyState<boolean, string>({ createComponentFamilyStateV2<boolean, string>({
key: 'onRecordBoardFetchMoreIrecordBoardShouldFetchMoreInColumnComponentFamilyStatesVisibleComponentFamilyState', key: 'recordBoardShouldFetchMoreInColumnComponentFamilyState',
defaultValue: false, defaultValue: false,
componentInstanceContext: RecordBoardComponentInstanceContext,
}); });

View File

@ -1,7 +0,0 @@
import { Sort } from '@/object-record/object-sort-dropdown/types/Sort';
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
export const recordBoardSortsComponentState = createComponentState<Sort[]>({
key: 'recordBoardSortsComponentState',
defaultValue: [],
});

View File

@ -1,26 +0,0 @@
import { recordBoardColumnIdsComponentState } from '@/object-record/record-board/states/recordBoardColumnIdsComponentState';
import { recordBoardRecordIdsByColumnIdComponentFamilyState } from '@/object-record/record-board/states/recordBoardRecordIdsByColumnIdComponentFamilyState';
import { createComponentReadOnlySelector } from '@/ui/utilities/state/component-state/utils/createComponentReadOnlySelector';
export const recordBoardAllRecordIdsComponentSelector =
createComponentReadOnlySelector<string[]>({
key: 'recordBoardAllRecordIdsComponentSelector',
get:
({ scopeId }) =>
({ get }) => {
const columnIds = get(recordBoardColumnIdsComponentState({ scopeId }));
const recordIdsByColumn = columnIds.map((columnId) =>
get(
recordBoardRecordIdsByColumnIdComponentFamilyState({
scopeId,
familyKey: columnId,
}),
),
);
const recordIds = recordIdsByColumn.flat();
return recordIds;
},
});

View File

@ -1,41 +0,0 @@
import { recordBoardColumnsComponentFamilyState } from '@/object-record/record-board/states/recordBoardColumnsComponentFamilyState';
import { createComponentFamilySelector } from '@/ui/utilities/state/component-state/utils/createComponentFamilySelector';
import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition';
export const recordBoardColumnsComponentFamilySelector =
createComponentFamilySelector<RecordGroupDefinition | undefined, string>({
key: 'recordBoardColumnsComponentFamilySelector',
get:
({
scopeId,
familyKey: columnId,
}: {
scopeId: string;
familyKey: string;
}) =>
({ get }) => {
return get(
recordBoardColumnsComponentFamilyState({
scopeId,
familyKey: columnId,
}),
);
},
set:
({
scopeId,
familyKey: columnId,
}: {
scopeId: string;
familyKey: string;
}) =>
({ set }, newColumn) => {
set(
recordBoardColumnsComponentFamilyState({
scopeId,
familyKey: columnId,
}),
newColumn,
);
},
});

View File

@ -1,32 +1,24 @@
import { RecordBoardComponentInstanceContext } from '@/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext';
import { isRecordBoardCardSelectedComponentFamilyState } from '@/object-record/record-board/states/isRecordBoardCardSelectedComponentFamilyState'; import { isRecordBoardCardSelectedComponentFamilyState } from '@/object-record/record-board/states/isRecordBoardCardSelectedComponentFamilyState';
import { recordBoardColumnIdsComponentState } from '@/object-record/record-board/states/recordBoardColumnIdsComponentState'; import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState';
import { recordBoardRecordIdsByColumnIdComponentFamilyState } from '@/object-record/record-board/states/recordBoardRecordIdsByColumnIdComponentFamilyState'; import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2';
import { createComponentReadOnlySelector } from '@/ui/utilities/state/component-state/utils/createComponentReadOnlySelector';
export const recordBoardSelectedRecordIdsComponentSelector = export const recordBoardSelectedRecordIdsComponentSelector =
createComponentReadOnlySelector<string[]>({ createComponentSelectorV2<string[]>({
key: 'recordBoardSelectedRecordIdsSelector', key: 'recordBoardSelectedRecordIdsSelector',
componentInstanceContext: RecordBoardComponentInstanceContext,
get: get:
({ scopeId }) => ({ instanceId }) =>
({ get }) => { ({ get }) => {
const columnIds = get(recordBoardColumnIdsComponentState({ scopeId })); const allRowIds = get(
recordIndexAllRowIdsComponentState.atomFamily({ instanceId }),
const recordIdsByColumn = columnIds.map((columnId) =>
get(
recordBoardRecordIdsByColumnIdComponentFamilyState({
scopeId,
familyKey: columnId,
}),
),
); );
const recordIds = recordIdsByColumn.flat(); return allRowIds.filter(
return recordIds.filter(
(recordId) => (recordId) =>
get( get(
isRecordBoardCardSelectedComponentFamilyState({ isRecordBoardCardSelectedComponentFamilyState.atomFamily({
scopeId, instanceId,
familyKey: recordId, familyKey: recordId,
}), }),
) === true, ) === true,

View File

@ -1,28 +0,0 @@
import { recordBoardColumnIdsComponentState } from '@/object-record/record-board/states/recordBoardColumnIdsComponentState';
import { recordBoardShouldFetchMoreInColumnComponentFamilyState } from '@/object-record/record-board/states/recordBoardShouldFetchMoreInColumnComponentFamilyState';
import { createComponentReadOnlySelector } from '@/ui/utilities/state/component-state/utils/createComponentReadOnlySelector';
export const recordBoardShouldFetchMoreComponentSelector =
createComponentReadOnlySelector<boolean>({
key: 'recordBoardShouldFetchMoreComponentSelector',
get:
({ scopeId }: { scopeId: string }) =>
({ get }) => {
const columnIds = get(
recordBoardColumnIdsComponentState({
scopeId,
}),
);
const shouldFetchMoreInColumns = columnIds.map((columnId) => {
return get(
recordBoardShouldFetchMoreInColumnComponentFamilyState({
scopeId,
familyKey: columnId,
}),
);
});
return shouldFetchMoreInColumns.some(Boolean);
},
});

View File

@ -1,13 +1,17 @@
import { RecordBoardComponentInstanceContext } from '@/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext';
import { recordBoardFieldDefinitionsComponentState } from '@/object-record/record-board/states/recordBoardFieldDefinitionsComponentState'; import { recordBoardFieldDefinitionsComponentState } from '@/object-record/record-board/states/recordBoardFieldDefinitionsComponentState';
import { createComponentReadOnlySelector } from '@/ui/utilities/state/component-state/utils/createComponentReadOnlySelector'; import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2';
export const recordBoardVisibleFieldDefinitionsComponentSelector = export const recordBoardVisibleFieldDefinitionsComponentSelector =
createComponentReadOnlySelector({ createComponentSelectorV2({
key: 'recordBoardVisibleFieldDefinitionsComponentSelector', key: 'recordBoardVisibleFieldDefinitionsComponentSelector',
get: get:
({ scopeId }) => ({ instanceId }) =>
({ get }) => ({ get }) =>
get(recordBoardFieldDefinitionsComponentState({ scopeId })) get(
recordBoardFieldDefinitionsComponentState.atomFamily({ instanceId }),
)
.filter((field) => field.isVisible) .filter((field) => field.isVisible)
.sort((a, b) => a.position - b.position), .sort((a, b) => a.position - b.position),
componentInstanceContext: RecordBoardComponentInstanceContext,
}); });

View File

@ -1,31 +1,45 @@
import { IconEye, IconEyeOff, MenuItemDraggable, Tag } from 'twenty-ui'; import { IconEye, IconEyeOff, MenuItemDraggable, Tag } from 'twenty-ui';
import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
import { import {
RecordGroupDefinition, RecordGroupDefinition,
RecordGroupDefinitionType, RecordGroupDefinitionType,
} from '@/object-record/record-group/types/RecordGroupDefinition'; } from '@/object-record/record-group/types/RecordGroupDefinition';
import { useRecoilValue } from 'recoil';
import { isDefined } from '~/utils/isDefined'; import { isDefined } from '~/utils/isDefined';
type RecordGroupMenuItemDraggableProps = { type RecordGroupMenuItemDraggableProps = {
recordGroup: RecordGroupDefinition; recordGroupId: string;
showDragGrip?: boolean; showDragGrip?: boolean;
isDraggable?: boolean; isDraggable?: boolean;
onVisibilityChange: (viewGroup: RecordGroupDefinition) => void; onVisibilityChange: (recordGroup: RecordGroupDefinition) => void;
}; };
export const RecordGroupMenuItemDraggable = ({ export const RecordGroupMenuItemDraggable = ({
recordGroup, recordGroupId,
showDragGrip, showDragGrip,
isDraggable, isDraggable,
onVisibilityChange, onVisibilityChange,
}: RecordGroupMenuItemDraggableProps) => { }: RecordGroupMenuItemDraggableProps) => {
const recordGroup = useRecoilValue(
recordGroupDefinitionFamilyState(recordGroupId),
);
if (!isDefined(recordGroup)) {
return null;
}
const isNoValue = recordGroup.type === RecordGroupDefinitionType.NoValue; const isNoValue = recordGroup.type === RecordGroupDefinitionType.NoValue;
const getIconButtons = (recordGroup: RecordGroupDefinition) => { const getIconButtons = (recordGroup: RecordGroupDefinition) => {
const iconButtons = [ const iconButtons = [
{ {
Icon: recordGroup.isVisible ? IconEyeOff : IconEye, Icon: recordGroup.isVisible ? IconEyeOff : IconEye,
onClick: () => onVisibilityChange(recordGroup), onClick: () =>
onVisibilityChange({
...recordGroup,
isVisible: !recordGroup.isVisible,
}),
}, },
].filter(isDefined); ].filter(isDefined);

View File

@ -13,17 +13,17 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop
import { StyledDropdownMenuSubheader } from '@/ui/layout/dropdown/components/StyledDropdownMenuSubheader'; import { StyledDropdownMenuSubheader } from '@/ui/layout/dropdown/components/StyledDropdownMenuSubheader';
type RecordGroupsVisibilityDropdownSectionProps = { type RecordGroupsVisibilityDropdownSectionProps = {
recordGroups: RecordGroupDefinition[]; recordGroupIds: string[];
isDraggable: boolean; isDraggable: boolean;
onDragEnd?: OnDragEndResponder; onDragEnd?: OnDragEndResponder;
onVisibilityChange: (viewGroup: RecordGroupDefinition) => void; onVisibilityChange: (recordGroup: RecordGroupDefinition) => void;
title: string; title: string;
showSubheader?: boolean; showSubheader?: boolean;
showDragGrip: boolean; showDragGrip: boolean;
}; };
export const RecordGroupsVisibilityDropdownSection = ({ export const RecordGroupsVisibilityDropdownSection = ({
recordGroups, recordGroupIds,
isDraggable, isDraggable,
onDragEnd, onDragEnd,
onVisibilityChange, onVisibilityChange,
@ -43,12 +43,13 @@ export const RecordGroupsVisibilityDropdownSection = ({
<StyledDropdownMenuSubheader>{title}</StyledDropdownMenuSubheader> <StyledDropdownMenuSubheader>{title}</StyledDropdownMenuSubheader>
)} )}
<DropdownMenuItemsContainer> <DropdownMenuItemsContainer>
{!!recordGroups.length && ( {recordGroupIds.length > 0 && (
<> <>
{!isDraggable ? ( {!isDraggable ? (
recordGroups.map((recordGroup) => ( recordGroupIds.map((recordGroupId) => (
<RecordGroupMenuItemDraggable <RecordGroupMenuItemDraggable
recordGroup={recordGroup} key={recordGroupId}
recordGroupId={recordGroupId}
onVisibilityChange={onVisibilityChange} onVisibilityChange={onVisibilityChange}
showDragGrip={showDragGrip} showDragGrip={showDragGrip}
isDraggable={isDraggable} isDraggable={isDraggable}
@ -59,14 +60,14 @@ export const RecordGroupsVisibilityDropdownSection = ({
onDragEnd={handleOnDrag} onDragEnd={handleOnDrag}
draggableItems={ draggableItems={
<> <>
{recordGroups.map((recordGroup, index) => ( {recordGroupIds.map((recordGroupId, index) => (
<DraggableItem <DraggableItem
key={recordGroup.id} key={recordGroupId}
draggableId={recordGroup.id} draggableId={recordGroupId}
index={index + 1} index={index + 1}
itemComponent={ itemComponent={
<RecordGroupMenuItemDraggable <RecordGroupMenuItemDraggable
recordGroup={recordGroup} recordGroupId={recordGroupId}
onVisibilityChange={onVisibilityChange} onVisibilityChange={onVisibilityChange}
showDragGrip={showDragGrip} showDragGrip={showDragGrip}
isDraggable={isDraggable} isDraggable={isDraggable}

View File

@ -1,37 +1,14 @@
import { RecordGroupContext } from '@/object-record/record-group/states/context/RecordGroupContext'; import { RecordGroupContext } from '@/object-record/record-group/states/context/RecordGroupContext';
import { hasRecordGroupDefinitionsComponentSelector } from '@/object-record/record-group/states/hasRecordGroupDefinitionsComponentSelector'; import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
import { recordGroupDefinitionsComponentState } from '@/object-record/record-group/states/recordGroupDefinitionsComponentState'; import { useContext } from 'react';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilValue } from 'recoil';
import { useContext, useMemo } from 'react';
export const useCurrentRecordGroupDefinition = (recordTableId?: string) => { export const useCurrentRecordGroupDefinition = () => {
const context = useContext(RecordGroupContext); const context = useContext(RecordGroupContext);
const hasRecordGroups = useRecoilComponentValueV2( const recordGroupDefinition = useRecoilValue(
hasRecordGroupDefinitionsComponentSelector, recordGroupDefinitionFamilyState(context?.recordGroupId),
recordTableId,
); );
const recordGroupDefinitions = useRecoilComponentValueV2(
recordGroupDefinitionsComponentState,
recordTableId,
);
const recordGroupDefinition = useMemo(() => {
if (!hasRecordGroups) {
return undefined;
}
if (!context) {
throw new Error(
'useCurrentRecordGroupDefinition must be used within a RecordGroupContextProvider.',
);
}
return recordGroupDefinitions.find(
({ id }) => id === context.recordGroupId,
);
}, [context, hasRecordGroups, recordGroupDefinitions]);
return recordGroupDefinition; return recordGroupDefinition;
}; };

View File

@ -2,24 +2,18 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
import { getFieldSlug } from '@/object-metadata/utils/getFieldSlug'; import { getFieldSlug } from '@/object-metadata/utils/getFieldSlug';
import { getObjectSlug } from '@/object-metadata/utils/getObjectSlug'; import { getObjectSlug } from '@/object-metadata/utils/getObjectSlug';
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext'; import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
import { useRecordGroups } from '@/object-record/record-group/hooks/useRecordGroups';
import { useRecordGroupVisibility } from '@/object-record/record-group/hooks/useRecordGroupVisibility'; import { useRecordGroupVisibility } from '@/object-record/record-group/hooks/useRecordGroupVisibility';
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
import { RecordGroupAction } from '@/object-record/record-group/types/RecordGroupActions'; import { RecordGroupAction } from '@/object-record/record-group/types/RecordGroupActions';
import { RecordIndexRootPropsContext } from '@/object-record/record-index/contexts/RecordIndexRootPropsContext'; import { RecordIndexRootPropsContext } from '@/object-record/record-index/contexts/RecordIndexRootPropsContext';
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState'; import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
import { ViewType } from '@/views/types/ViewType'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useCallback, useContext, useMemo } from 'react'; import { useCallback, useContext, useMemo } from 'react';
import { useLocation, useNavigate } from 'react-router-dom'; import { useLocation, useNavigate } from 'react-router-dom';
import { useSetRecoilState } from 'recoil'; import { useSetRecoilState } from 'recoil';
import { IconEyeOff, IconSettings, isDefined } from 'twenty-ui'; import { IconEyeOff, IconSettings, isDefined } from 'twenty-ui';
type UseRecordGroupActionsParams = { export const useRecordGroupActions = () => {
viewType: ViewType;
};
export const useRecordGroupActions = ({
viewType,
}: UseRecordGroupActionsParams) => {
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
@ -35,14 +29,13 @@ export const useRecordGroupActions = ({
objectNameSingular, objectNameSingular,
}); });
const { viewGroupFieldMetadataItem } = useRecordGroups({ const recordGroupFieldMetadata = useRecoilComponentValueV2(
objectNameSingular, recordGroupFieldMetadataComponentState,
}); );
const { handleVisibilityChange: handleRecordGroupVisibilityChange } = const { handleVisibilityChange: handleRecordGroupVisibilityChange } =
useRecordGroupVisibility({ useRecordGroupVisibility({
viewBarId: recordIndexId, viewBarId: recordIndexId,
viewType,
}); });
const setNavigationMemorizedUrl = useSetRecoilState( const setNavigationMemorizedUrl = useSetRecoilState(
@ -52,11 +45,11 @@ export const useRecordGroupActions = ({
const navigateToSelectSettings = useCallback(() => { const navigateToSelectSettings = useCallback(() => {
setNavigationMemorizedUrl(location.pathname + location.search); setNavigationMemorizedUrl(location.pathname + location.search);
if (!isDefined(viewGroupFieldMetadataItem)) { if (!isDefined(recordGroupFieldMetadata)) {
throw new Error('viewGroupFieldMetadataItem is not a non-empty string'); throw new Error('recordGroupFieldMetadata is not a non-empty string');
} }
const settingsPath = `/settings/objects/${getObjectSlug(objectMetadataItem)}/${getFieldSlug(viewGroupFieldMetadataItem)}`; const settingsPath = `/settings/objects/${getObjectSlug(objectMetadataItem)}/${getFieldSlug(recordGroupFieldMetadata)}`;
navigate(settingsPath); navigate(settingsPath);
}, [ }, [
@ -65,7 +58,7 @@ export const useRecordGroupActions = ({
location.search, location.search,
navigate, navigate,
objectMetadataItem, objectMetadataItem,
viewGroupFieldMetadataItem, recordGroupFieldMetadata,
]); ]);
const recordGroupActions: RecordGroupAction[] = useMemo( const recordGroupActions: RecordGroupAction[] = useMemo(

View File

@ -1,59 +1,89 @@
import { OnDragEndResponder } from '@hello-pangea/dnd'; import { OnDragEndResponder } from '@hello-pangea/dnd';
import { useCallback } from 'react';
import { useRecordGroups } from '@/object-record/record-group/hooks/useRecordGroups'; import { useSetRecordGroup } from '@/object-record/record-group/hooks/useSetRecordGroup';
import { recordGroupDefinitionsComponentState } from '@/object-record/record-group/states/recordGroupDefinitionsComponentState'; import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { visibleRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector';
import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition';
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
import { useSaveCurrentViewGroups } from '@/views/hooks/useSaveCurrentViewGroups'; import { useSaveCurrentViewGroups } from '@/views/hooks/useSaveCurrentViewGroups';
import { mapRecordGroupDefinitionsToViewGroups } from '@/views/utils/mapRecordGroupDefinitionsToViewGroups'; import { mapRecordGroupDefinitionsToViewGroups } from '@/views/utils/mapRecordGroupDefinitionsToViewGroups';
import { useRecoilCallback } from 'recoil';
import { moveArrayItem } from '~/utils/array/moveArrayItem'; import { moveArrayItem } from '~/utils/array/moveArrayItem';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { isDefined } from '~/utils/isDefined';
type UseRecordGroupHandlersParams = { type UseRecordGroupHandlersParams = {
objectNameSingular: string;
viewBarId: string; viewBarId: string;
}; };
export const useRecordGroupReorder = ({ export const useRecordGroupReorder = ({
objectNameSingular,
viewBarId, viewBarId,
}: UseRecordGroupHandlersParams) => { }: UseRecordGroupHandlersParams) => {
const setRecordGroupDefinitions = useSetRecoilComponentStateV2( const setRecordGroup = useSetRecordGroup(viewBarId);
recordGroupDefinitionsComponentState,
);
const { visibleRecordGroups } = useRecordGroups({ const visibleRecordGroupIdsSelector = useRecoilComponentCallbackStateV2(
objectNameSingular: objectNameSingular, visibleRecordGroupIdsComponentSelector,
}); );
const { saveViewGroups } = useSaveCurrentViewGroups(viewBarId); const { saveViewGroups } = useSaveCurrentViewGroups(viewBarId);
const handleOrderChange: OnDragEndResponder = useCallback( const handleOrderChange: OnDragEndResponder = useRecoilCallback(
(result) => { ({ snapshot }) =>
if (!result.destination) { (result) => {
return; if (!result.destination) {
} return;
}
const reorderedVisibleBoardGroups = moveArrayItem(visibleRecordGroups, { const visibleRecordGroupIds = getSnapshotValue(
fromIndex: result.source.index - 1, snapshot,
toIndex: result.destination.index - 1, visibleRecordGroupIdsSelector,
}); );
if (isDeeplyEqual(visibleRecordGroups, reorderedVisibleBoardGroups)) const reorderedVisibleRecordGroupIds = moveArrayItem(
return; visibleRecordGroupIds,
{
fromIndex: result.source.index - 1,
toIndex: result.destination.index - 1,
},
);
const updatedGroups = [...reorderedVisibleBoardGroups].map( if (
(group, index) => ({ ...group, position: index }), isDeeplyEqual(visibleRecordGroupIds, reorderedVisibleRecordGroupIds)
); ) {
return;
}
setRecordGroupDefinitions(updatedGroups); const updatedRecordGroups = reorderedVisibleRecordGroupIds.reduce<
saveViewGroups(mapRecordGroupDefinitionsToViewGroups(updatedGroups)); RecordGroupDefinition[]
}, >((acc, recordGroupId, index) => {
[saveViewGroups, setRecordGroupDefinitions, visibleRecordGroups], const recordGroupDefinition = getSnapshotValue(
snapshot,
recordGroupDefinitionFamilyState(recordGroupId),
);
if (!isDefined(recordGroupDefinition)) {
return acc;
}
return [
...acc,
{
...recordGroupDefinition,
position: index,
},
];
}, []);
setRecordGroup(updatedRecordGroups);
saveViewGroups(
mapRecordGroupDefinitionsToViewGroups(updatedRecordGroups),
);
},
[saveViewGroups, setRecordGroup, visibleRecordGroupIdsSelector],
); );
return { return {
visibleRecordGroups,
handleOrderChange, handleOrderChange,
}; };
}; };

View File

@ -1,125 +1,113 @@
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
import { recordGroupDefinitionsComponentState } from '@/object-record/record-group/states/recordGroupDefinitionsComponentState'; import { recordGroupIdsComponentState } from '@/object-record/record-group/states/recordGroupIdsComponentState';
import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition';
import { recordIndexRecordGroupHideComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupHideComponentState'; import { recordIndexRecordGroupHideComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupHideComponentState';
import { tableRowIdsByGroupComponentFamilyState } from '@/object-record/record-table/states/tableRowIdsByGroupComponentFamilyState'; import { recordIndexRowIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRowIdsByGroupComponentFamilyState';
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
import { useSaveCurrentViewGroups } from '@/views/hooks/useSaveCurrentViewGroups'; import { useSaveCurrentViewGroups } from '@/views/hooks/useSaveCurrentViewGroups';
import { ViewType } from '@/views/types/ViewType';
import { mapRecordGroupDefinitionsToViewGroups } from '@/views/utils/mapRecordGroupDefinitionsToViewGroups'; import { mapRecordGroupDefinitionsToViewGroups } from '@/views/utils/mapRecordGroupDefinitionsToViewGroups';
import { recordGroupDefinitionToViewGroup } from '@/views/utils/recordGroupDefinitionToViewGroup';
import { useRecoilCallback } from 'recoil'; import { useRecoilCallback } from 'recoil';
import { isDefined } from '~/utils/isDefined';
type UseRecordGroupVisibilityParams = { type UseRecordGroupVisibilityParams = {
viewBarId: string; viewBarId: string;
viewType: ViewType;
}; };
export const useRecordGroupVisibility = ({ export const useRecordGroupVisibility = ({
viewBarId, viewBarId,
viewType,
}: UseRecordGroupVisibilityParams) => { }: UseRecordGroupVisibilityParams) => {
const recordGroupDefinitionsState = useRecoilComponentCallbackStateV2( const recordIndexRecordGroupIdsState = useRecoilComponentCallbackStateV2(
recordGroupDefinitionsComponentState, recordGroupIdsComponentState,
); );
const tableRowIdsByGroupFamilyState = useRecoilComponentCallbackStateV2( const recordIndexRowIdsByGroupFamilyState = useRecoilComponentCallbackStateV2(
tableRowIdsByGroupComponentFamilyState, recordIndexRowIdsByGroupComponentFamilyState,
viewBarId, viewBarId,
); );
const { recordIdsByColumnIdFamilyState } = useRecordBoardStates(viewBarId);
const objectOptionsDropdownRecordGroupHideState = const objectOptionsDropdownRecordGroupHideState =
useRecoilComponentCallbackStateV2(recordIndexRecordGroupHideComponentState); useRecoilComponentCallbackStateV2(recordIndexRecordGroupHideComponentState);
const { saveViewGroups } = useSaveCurrentViewGroups(viewBarId); const { saveViewGroup, saveViewGroups } = useSaveCurrentViewGroups(viewBarId);
const handleVisibilityChange = useRecoilCallback( const handleVisibilityChange = useRecoilCallback(
({ snapshot, set }) => ({ set }) =>
async (updatedRecordGroupDefinition: RecordGroupDefinition) => { async (updatedRecordGroup: RecordGroupDefinition) => {
const recordGroupDefinitions = getSnapshotValue( set(
snapshot, recordGroupDefinitionFamilyState(updatedRecordGroup.id),
recordGroupDefinitionsState, updatedRecordGroup,
); );
const updatedRecordGroupDefinitions = recordGroupDefinitions.map( saveViewGroup(recordGroupDefinitionToViewGroup(updatedRecordGroup));
(groupDefinition) =>
groupDefinition.id === updatedRecordGroupDefinition.id
? {
...groupDefinition,
isVisible: !groupDefinition.isVisible,
}
: groupDefinition,
);
set(recordGroupDefinitionsState, updatedRecordGroupDefinitions);
saveViewGroups(
mapRecordGroupDefinitionsToViewGroups(updatedRecordGroupDefinitions),
);
// If visibility is manually toggled, we should reset the hideEmptyRecordGroup state // If visibility is manually toggled, we should reset the hideEmptyRecordGroup state
set(objectOptionsDropdownRecordGroupHideState, false); set(objectOptionsDropdownRecordGroupHideState, false);
}, },
[ [saveViewGroup, objectOptionsDropdownRecordGroupHideState],
objectOptionsDropdownRecordGroupHideState,
recordGroupDefinitionsState,
saveViewGroups,
],
); );
const handleHideEmptyRecordGroupChange = useRecoilCallback( const handleHideEmptyRecordGroupChange = useRecoilCallback(
({ snapshot, set }) => ({ snapshot, set }) =>
async () => { async () => {
const recordGroupDefinitions = getSnapshotValue( const updatedRecordGroupDefinitions: RecordGroupDefinition[] = [];
const recordGroupIds = getSnapshotValue(
snapshot, snapshot,
recordGroupDefinitionsState, recordIndexRecordGroupIdsState,
); );
const currentHideState = getSnapshotValue( const currentHideState = getSnapshotValue(
snapshot, snapshot,
objectOptionsDropdownRecordGroupHideState, objectOptionsDropdownRecordGroupHideState,
); );
const newHideState = !currentHideState;
set(objectOptionsDropdownRecordGroupHideState, !currentHideState); set(objectOptionsDropdownRecordGroupHideState, newHideState);
const updatedRecordGroupDefinitions = recordGroupDefinitions.map( for (const recordGroupId of recordGroupIds) {
(recordGroup) => { const recordGroup = getSnapshotValue(
// TODO: Maybe we can improve that and only use one state for both table and board snapshot,
const recordGroupRowIds = recordGroupDefinitionFamilyState(recordGroupId),
viewType === ViewType.Table );
? getSnapshotValue(
snapshot,
tableRowIdsByGroupFamilyState(recordGroup.id),
)
: getSnapshotValue(
snapshot,
recordIdsByColumnIdFamilyState(recordGroup.id),
);
if (recordGroupRowIds.length > 0) { if (!isDefined(recordGroup)) {
return recordGroup; throw new Error(
} `Record group with id ${recordGroupId} not found in snapshot`,
);
}
return { const recordGroupRowIds = getSnapshotValue(
...recordGroup, snapshot,
isVisible: currentHideState, recordIndexRowIdsByGroupFamilyState(recordGroupId),
}; );
},
); if (recordGroupRowIds.length > 0) {
continue;
}
const updatedRecordGroup = {
...recordGroup,
isVisible: !newHideState,
};
set(
recordGroupDefinitionFamilyState(recordGroupId),
updatedRecordGroup,
);
updatedRecordGroupDefinitions.push(updatedRecordGroup);
}
saveViewGroups( saveViewGroups(
mapRecordGroupDefinitionsToViewGroups(updatedRecordGroupDefinitions), mapRecordGroupDefinitionsToViewGroups(updatedRecordGroupDefinitions),
); );
}, },
[ [
recordGroupDefinitionsState, recordIndexRecordGroupIdsState,
objectOptionsDropdownRecordGroupHideState, objectOptionsDropdownRecordGroupHideState,
saveViewGroups, saveViewGroups,
viewType, recordIndexRowIdsByGroupFamilyState,
tableRowIdsByGroupFamilyState,
recordIdsByColumnIdFamilyState,
], ],
); );

View File

@ -1,58 +0,0 @@
import { useMemo } from 'react';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { recordGroupDefinitionsComponentState } from '@/object-record/record-group/states/recordGroupDefinitionsComponentState';
import { sortRecordGroupDefinitions } from '@/object-record/record-group/utils/sortRecordGroupDefinitions';
import { recordIndexRecordGroupSortComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupSortComponentState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
type UseRecordGroupsParams = {
objectNameSingular: string;
};
export const useRecordGroups = ({
objectNameSingular,
}: UseRecordGroupsParams) => {
const recordGroupDefinitions = useRecoilComponentValueV2(
recordGroupDefinitionsComponentState,
);
const recordGroupSort = useRecoilComponentValueV2(
recordIndexRecordGroupSortComponentState,
);
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
});
const viewGroupFieldMetadataItem = useMemo(() => {
if (recordGroupDefinitions.length === 0) return null;
// We're assuming that all groups have the same fieldMetadataId for now
const fieldMetadataId =
'fieldMetadataId' in recordGroupDefinitions[0]
? recordGroupDefinitions[0].fieldMetadataId
: null;
if (!fieldMetadataId) return null;
return objectMetadataItem.fields.find(
(field) => field.id === fieldMetadataId,
);
}, [objectMetadataItem, recordGroupDefinitions]);
const visibleRecordGroups = useMemo(
() => sortRecordGroupDefinitions(recordGroupDefinitions, recordGroupSort),
[recordGroupDefinitions, recordGroupSort],
);
const hiddenRecordGroups = useMemo(
() => recordGroupDefinitions.filter((boardGroup) => !boardGroup.isVisible),
[recordGroupDefinitions],
);
return {
hiddenRecordGroups,
visibleRecordGroups,
viewGroupFieldMetadataItem,
};
};

View File

@ -0,0 +1,83 @@
import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
import { recordGroupIdsComponentState } from '@/object-record/record-group/states/recordGroupIdsComponentState';
import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition';
import { RecordIndexRootPropsContext } from '@/object-record/record-index/contexts/RecordIndexRootPropsContext';
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
import { useContext } from 'react';
import { useRecoilCallback } from 'recoil';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { isDefined } from '~/utils/isDefined';
export const useSetRecordGroup = (viewId?: string) => {
const { objectMetadataItem } = useContext(RecordIndexRootPropsContext);
const recordIndexRecordGroupIdsState = useRecoilComponentCallbackStateV2(
recordGroupIdsComponentState,
viewId,
);
const recordGroupFieldMetadataState = useRecoilComponentCallbackStateV2(
recordGroupFieldMetadataComponentState,
viewId,
);
return useRecoilCallback(
({ snapshot, set }) =>
(recordGroups: RecordGroupDefinition[]) => {
if (recordGroups.length === 0) {
return;
}
const currentRecordGroupId = getSnapshotValue(
snapshot,
recordIndexRecordGroupIdsState,
);
const fieldMetadataId = recordGroups[0].fieldMetadataId;
const fieldMetadata = objectMetadataItem.fields.find(
(field) => field.id === fieldMetadataId,
);
const currentFieldMetadata = getSnapshotValue(
snapshot,
recordGroupFieldMetadataState,
);
// Set the field metadata linked to the record groups
if (
isDefined(fieldMetadata) &&
!isDeeplyEqual(fieldMetadata, currentFieldMetadata)
) {
set(recordGroupFieldMetadataState, fieldMetadata);
}
// Set the record groups by id
recordGroups.forEach((recordGroup) => {
const existingRecordGroup = getSnapshotValue(
snapshot,
recordGroupDefinitionFamilyState(recordGroup.id),
);
if (isDeeplyEqual(existingRecordGroup, recordGroup)) {
return;
}
set(recordGroupDefinitionFamilyState(recordGroup.id), recordGroup);
});
const recordGroupIds = recordGroups.map(({ id }) => id);
if (isDeeplyEqual(currentRecordGroupId, recordGroupIds)) {
return;
}
// Set the record group ids
set(recordIndexRecordGroupIdsState, recordGroupIds);
},
[
objectMetadataItem.fields,
recordGroupFieldMetadataState,
recordIndexRecordGroupIdsState,
],
);
};

View File

@ -0,0 +1,10 @@
import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition';
import { atomFamily } from 'recoil';
export const recordGroupDefinitionFamilyState = atomFamily<
RecordGroupDefinition | undefined,
RecordGroupDefinition['id']
>({
key: 'recordGroupDefinitionFamilyState',
default: undefined,
});

View File

@ -0,0 +1,11 @@
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
export const recordGroupFieldMetadataComponentState = createComponentStateV2<
FieldMetadataItem | undefined
>({
key: 'recordGroupFieldMetadataComponentState',
defaultValue: undefined,
componentInstanceContext: ViewComponentInstanceContext,
});

View File

@ -2,10 +2,10 @@ import { RecordGroupDefinition } from '@/object-record/record-group/types/Record
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
export const recordGroupDefinitionsComponentState = createComponentStateV2< export const recordGroupIdsComponentState = createComponentStateV2<
RecordGroupDefinition[] RecordGroupDefinition['id'][]
>({ >({
key: 'recordGroupDefinitionsComponentState', key: 'recordGroupIdsComponentState',
defaultValue: [], defaultValue: [],
componentInstanceContext: ViewComponentInstanceContext, componentInstanceContext: ViewComponentInstanceContext,
}); });

View File

@ -1,21 +1,21 @@
import { recordGroupDefinitionsComponentState } from '@/object-record/record-group/states/recordGroupDefinitionsComponentState'; import { recordGroupIdsComponentState } from '@/object-record/record-group/states/recordGroupIdsComponentState';
import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
export const hasRecordGroupDefinitionsComponentSelector = export const hasRecordGroupsComponentSelector =
createComponentSelectorV2<boolean>({ createComponentSelectorV2<boolean>({
key: 'hasRecordGroupDefinitionsComponentSelector', key: 'hasRecordGroupsComponentSelector',
componentInstanceContext: ViewComponentInstanceContext, componentInstanceContext: ViewComponentInstanceContext,
get: get:
({ instanceId }) => ({ instanceId }) =>
({ get }) => { ({ get }) => {
const recordGroupDefinitions = get( const recordGroupIds = get(
recordGroupDefinitionsComponentState.atomFamily({ recordGroupIdsComponentState.atomFamily({
instanceId, instanceId,
}), }),
); );
return recordGroupDefinitions.length > 0; return recordGroupIds.length > 0;
}, },
}); });

View File

@ -0,0 +1,34 @@
import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
import { recordGroupIdsComponentState } from '@/object-record/record-group/states/recordGroupIdsComponentState';
import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
import { isDefined } from '~/utils/isDefined';
export const hiddenRecordGroupIdsComponentSelector = createComponentSelectorV2<
string[]
>({
key: 'hiddenRecordGroupIdsComponentSelector',
componentInstanceContext: ViewComponentInstanceContext,
get:
({ instanceId }) =>
({ get }) => {
const recordGroupIds = get(
recordGroupIdsComponentState.atomFamily({
instanceId,
}),
);
return recordGroupIds.filter((recordGroupId) => {
const recordGroupDefinition = get(
recordGroupDefinitionFamilyState(recordGroupId),
);
if (!isDefined(recordGroupDefinition)) {
return false;
}
return !recordGroupDefinition.isVisible;
});
},
});

View File

@ -0,0 +1,37 @@
import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
import { recordGroupIdsComponentState } from '@/object-record/record-group/states/recordGroupIdsComponentState';
import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition';
import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
import { isDefined } from '~/utils/isDefined';
export const recordGroupDefinitionsComponentSelector =
createComponentSelectorV2<RecordGroupDefinition[]>({
key: 'recordGroupDefinitionsComponentSelector',
componentInstanceContext: ViewComponentInstanceContext,
get:
({ instanceId }) =>
({ get }) => {
const recordGroupIds = get(
recordGroupIdsComponentState.atomFamily({
instanceId,
}),
);
return recordGroupIds.reduce<RecordGroupDefinition[]>(
(acc, recordGroupId) => {
const recordGroupDefinition = get(
recordGroupDefinitionFamilyState(recordGroupId),
);
if (!isDefined(recordGroupDefinition)) {
return acc;
}
return [...acc, recordGroupDefinition];
},
[],
);
},
});

View File

@ -0,0 +1,63 @@
import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
import { recordGroupIdsComponentState } from '@/object-record/record-group/states/recordGroupIdsComponentState';
import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition';
import { RecordGroupSort } from '@/object-record/record-group/types/RecordGroupSort';
import { sortedInsert } from '@/object-record/record-group/utils/sortedInsert';
import { recordIndexRecordGroupSortComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupSortComponentState';
import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
import { isDefined } from '~/utils/isDefined';
export const visibleRecordGroupIdsComponentSelector = createComponentSelectorV2<
string[]
>({
key: 'visibleRecordGroupIdsComponentSelector',
componentInstanceContext: ViewComponentInstanceContext,
get:
({ instanceId }) =>
({ get }) => {
const recordGroupSort = get(
recordIndexRecordGroupSortComponentState.atomFamily({
instanceId,
}),
);
const recordGroupIds = get(
recordGroupIdsComponentState.atomFamily({
instanceId,
}),
);
const result: RecordGroupDefinition[] = [];
const comparator = (
a: RecordGroupDefinition,
b: RecordGroupDefinition,
) => {
switch (recordGroupSort) {
case RecordGroupSort.Alphabetical:
return a.title.localeCompare(b.title);
case RecordGroupSort.ReverseAlphabetical:
return b.title.localeCompare(a.title);
case RecordGroupSort.Manual:
default:
return a.position - b.position;
}
};
for (const recordGroupId of recordGroupIds) {
const recordGroupDefinition = get(
recordGroupDefinitionFamilyState(recordGroupId),
);
if (
isDefined(recordGroupDefinition) &&
recordGroupDefinition.isVisible
) {
sortedInsert(result, recordGroupDefinition, comparator);
}
}
return result.map(({ id }) => id);
},
});

View File

@ -5,7 +5,7 @@ export const sortRecordGroupDefinitions = (
recordGroupDefinitions: RecordGroupDefinition[], recordGroupDefinitions: RecordGroupDefinition[],
recordGroupSort: RecordGroupSort, recordGroupSort: RecordGroupSort,
) => { ) => {
const visibleGroups = recordGroupDefinitions.filter( const visibleRecordGroups = recordGroupDefinitions.filter(
(boardGroup) => boardGroup.isVisible, (boardGroup) => boardGroup.isVisible,
); );
@ -17,15 +17,15 @@ export const sortRecordGroupDefinitions = (
switch (recordGroupSort) { switch (recordGroupSort) {
case RecordGroupSort.Alphabetical: case RecordGroupSort.Alphabetical:
return visibleGroups.sort((a, b) => return visibleRecordGroups.sort((a, b) =>
compareAlphabetical(a.title.toLowerCase(), b.title.toLowerCase()), compareAlphabetical(a.title.toLowerCase(), b.title.toLowerCase()),
); );
case RecordGroupSort.ReverseAlphabetical: case RecordGroupSort.ReverseAlphabetical:
return visibleGroups.sort((a, b) => return visibleRecordGroups.sort((a, b) =>
compareAlphabetical(a.title.toLowerCase(), b.title.toLowerCase(), true), compareAlphabetical(a.title.toLowerCase(), b.title.toLowerCase(), true),
); );
case RecordGroupSort.Manual: case RecordGroupSort.Manual:
default: default:
return visibleGroups.sort((a, b) => a.position - b.position); return visibleRecordGroups.sort((a, b) => a.position - b.position);
} }
}; };

View File

@ -0,0 +1,20 @@
export const sortedInsert = <T>(
array: T[],
item: T,
comparator: (a: T, b: T) => number,
) => {
let low = 0;
let high = array.length;
while (low < high) {
const mid = Math.floor((low + high) / 2);
if (comparator(item, array[mid]) < 0) {
high = mid;
} else {
low = mid + 1;
}
}
array.splice(low, 0, item);
};

View File

@ -5,7 +5,7 @@ import { isRecordBoardFetchingRecordsByColumnFamilyState } from '@/object-record
import { recordBoardShouldFetchMoreInColumnComponentFamilyState } from '@/object-record/record-board/states/recordBoardShouldFetchMoreInColumnComponentFamilyState'; import { recordBoardShouldFetchMoreInColumnComponentFamilyState } from '@/object-record/record-board/states/recordBoardShouldFetchMoreInColumnComponentFamilyState';
import { useLoadRecordIndexBoardColumn } from '@/object-record/record-index/hooks/useLoadRecordIndexBoardColumn'; import { useLoadRecordIndexBoardColumn } from '@/object-record/record-index/hooks/useLoadRecordIndexBoardColumn';
import { isRecordIndexBoardColumnLoadingFamilyState } from '@/object-record/states/isRecordBoardColumnLoadingFamilyState'; import { isRecordIndexBoardColumnLoadingFamilyState } from '@/object-record/states/isRecordBoardColumnLoadingFamilyState';
import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId'; import { useRecoilComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyStateV2';
export const RecordIndexBoardColumnLoaderEffect = ({ export const RecordIndexBoardColumnLoaderEffect = ({
objectNameSingular, objectNameSingular,
@ -18,20 +18,14 @@ export const RecordIndexBoardColumnLoaderEffect = ({
boardFieldMetadataId: string | null; boardFieldMetadataId: string | null;
columnId: string; columnId: string;
}) => { }) => {
const [shouldFetchMore, setShouldFetchMore] = useRecoilState( const [shouldFetchMore, setShouldFetchMore] = useRecoilComponentFamilyStateV2(
recordBoardShouldFetchMoreInColumnComponentFamilyState({ recordBoardShouldFetchMoreInColumnComponentFamilyState,
scopeId: getScopeIdFromComponentId(recordBoardId), columnId,
familyKey: columnId, recordBoardId,
}),
); );
const [loadingRecordsForThisColumn, setLoadingRecordsForThisColumn] = const [loadingRecordsForThisColumn, setLoadingRecordsForThisColumn] =
useRecoilState( useRecoilState(isRecordBoardFetchingRecordsByColumnFamilyState(columnId));
isRecordBoardFetchingRecordsByColumnFamilyState({
scopeId: getScopeIdFromComponentId(recordBoardId),
familyKey: { columnId },
}),
);
const { fetchMoreRecords, loading, records, hasNextPage } = const { fetchMoreRecords, loading, records, hasNextPage } =
useLoadRecordIndexBoardColumn({ useLoadRecordIndexBoardColumn({

View File

@ -1,9 +1,10 @@
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; import { visibleRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector';
import { RecordIndexBoardColumnLoaderEffect } from '@/object-record/record-index/components/RecordIndexBoardColumnLoaderEffect'; import { RecordIndexBoardColumnLoaderEffect } from '@/object-record/record-index/components/RecordIndexBoardColumnLoaderEffect';
import { recordIndexKanbanFieldMetadataIdState } from '@/object-record/record-index/states/recordIndexKanbanFieldMetadataIdState'; import { recordIndexKanbanFieldMetadataIdState } from '@/object-record/record-index/states/recordIndexKanbanFieldMetadataIdState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
type RecordIndexBoardDataLoaderProps = { type RecordIndexBoardDataLoaderProps = {
objectNameSingular: string; objectNameSingular: string;
@ -18,6 +19,10 @@ export const RecordIndexBoardDataLoader = ({
objectNameSingular, objectNameSingular,
}); });
const visibleRecordGroupIds = useRecoilComponentValueV2(
visibleRecordGroupIdsComponentSelector,
);
const recordIndexKanbanFieldMetadataId = useRecoilValue( const recordIndexKanbanFieldMetadataId = useRecoilValue(
recordIndexKanbanFieldMetadataIdState, recordIndexKanbanFieldMetadataIdState,
); );
@ -26,18 +31,14 @@ export const RecordIndexBoardDataLoader = ({
(field) => field.id === recordIndexKanbanFieldMetadataId, (field) => field.id === recordIndexKanbanFieldMetadataId,
); );
const { columnIdsState } = useRecordBoardStates(recordBoardId);
const columnIds = useRecoilValue(columnIdsState);
return ( return (
<> <>
{columnIds.map((columnId, index) => ( {visibleRecordGroupIds.map((recordGroupId, index) => (
<RecordIndexBoardColumnLoaderEffect <RecordIndexBoardColumnLoaderEffect
objectNameSingular={objectNameSingular} objectNameSingular={objectNameSingular}
boardFieldMetadataId={recordIndexKanbanFieldMetadataId} boardFieldMetadataId={recordIndexKanbanFieldMetadataId}
recordBoardId={recordBoardId} recordBoardId={recordBoardId}
columnId={columnId} columnId={recordGroupId}
key={index} key={index}
/> />
))} ))}

View File

@ -1,101 +1,52 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useRecoilValue, useSetRecoilState } from 'recoil'; import { useRecoilValue } from 'recoil';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { isRecordBoardCompactModeActiveComponentState } from '@/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState';
import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard'; import { recordBoardFieldDefinitionsComponentState } from '@/object-record/record-board/states/recordBoardFieldDefinitionsComponentState';
import { recordGroupDefinitionsComponentState } from '@/object-record/record-group/states/recordGroupDefinitionsComponentState'; import { recordBoardSelectedRecordIdsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardSelectedRecordIdsComponentSelector';
import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState'; import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState';
import { recordIndexIsCompactModeActiveState } from '@/object-record/record-index/states/recordIndexIsCompactModeActiveState'; import { recordIndexIsCompactModeActiveState } from '@/object-record/record-index/states/recordIndexIsCompactModeActiveState';
import { recordIndexKanbanFieldMetadataIdState } from '@/object-record/record-index/states/recordIndexKanbanFieldMetadataIdState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { isDefined } from '~/utils/isDefined';
type RecordIndexBoardDataLoaderEffectProps = { type RecordIndexBoardDataLoaderEffectProps = {
objectNameSingular: string;
recordBoardId: string; recordBoardId: string;
}; };
export const RecordIndexBoardDataLoaderEffect = ({ export const RecordIndexBoardDataLoaderEffect = ({
objectNameSingular,
recordBoardId, recordBoardId,
}: RecordIndexBoardDataLoaderEffectProps) => { }: RecordIndexBoardDataLoaderEffectProps) => {
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
});
const recordIndexFieldDefinitions = useRecoilValue( const recordIndexFieldDefinitions = useRecoilValue(
recordIndexFieldDefinitionsState, recordIndexFieldDefinitionsState,
); );
const recordGroupDefinitions = useRecoilComponentValueV2(
recordGroupDefinitionsComponentState,
);
const recordIndexKanbanFieldMetadataId = useRecoilValue(
recordIndexKanbanFieldMetadataIdState,
);
const recordIndexIsCompactModeActive = useRecoilValue( const recordIndexIsCompactModeActive = useRecoilValue(
recordIndexIsCompactModeActiveState, recordIndexIsCompactModeActiveState,
); );
const { isCompactModeActiveState } = useRecordBoard(recordBoardId); const setRecordBoardFieldDefinitions = useSetRecoilComponentStateV2(
recordBoardFieldDefinitionsComponentState,
recordBoardId,
);
const setIsCompactModeActive = useSetRecoilState(isCompactModeActiveState); const selectedRecordIds = useRecoilComponentValueV2(
recordBoardSelectedRecordIdsComponentSelector,
recordBoardId,
);
const setIsCompactModeActive = useSetRecoilComponentStateV2(
isRecordBoardCompactModeActiveComponentState,
recordBoardId,
);
useEffect(() => { useEffect(() => {
setIsCompactModeActive(recordIndexIsCompactModeActive); setIsCompactModeActive(recordIndexIsCompactModeActive);
}, [recordIndexIsCompactModeActive, setIsCompactModeActive]); }, [recordIndexIsCompactModeActive, setIsCompactModeActive]);
const {
setColumns,
setObjectSingularName,
selectedRecordIdsSelector,
setFieldDefinitions,
setKanbanFieldMetadataName,
} = useRecordBoard(recordBoardId);
useEffect(() => { useEffect(() => {
setFieldDefinitions(recordIndexFieldDefinitions); setRecordBoardFieldDefinitions(recordIndexFieldDefinitions);
}, [recordIndexFieldDefinitions, setFieldDefinitions]); }, [recordIndexFieldDefinitions, setRecordBoardFieldDefinitions]);
useEffect(() => {
setObjectSingularName(objectNameSingular);
}, [objectNameSingular, setObjectSingularName]);
useEffect(() => {
setColumns(recordGroupDefinitions);
}, [recordGroupDefinitions, setColumns]);
// TODO: Remove this duplicate useEffect by ensuring it's not here because
// We want it to be triggered by a change of objectMetadataItem, which would be an anti-pattern
// As it is an unnecessary dependency
useEffect(() => {
setFieldDefinitions(recordIndexFieldDefinitions);
}, [objectMetadataItem, setFieldDefinitions, recordIndexFieldDefinitions]);
useEffect(() => {
if (isDefined(recordIndexKanbanFieldMetadataId)) {
const kanbanFieldMetadataName = objectMetadataItem?.fields.find(
(field) =>
field.type === FieldMetadataType.Select &&
field.id === recordIndexKanbanFieldMetadataId,
)?.name;
if (isDefined(kanbanFieldMetadataName)) {
setKanbanFieldMetadataName(kanbanFieldMetadataName);
}
}
}, [
objectMetadataItem,
recordIndexKanbanFieldMetadataId,
setKanbanFieldMetadataName,
]);
const selectedRecordIds = useRecoilValue(selectedRecordIdsSelector());
const setContextStoreTargetedRecords = useSetRecoilComponentStateV2( const setContextStoreTargetedRecords = useSetRecoilComponentStateV2(
contextStoreTargetedRecordsRuleComponentState, contextStoreTargetedRecordsRuleComponentState,

View File

@ -24,11 +24,9 @@ import { SpreadsheetImportProvider } from '@/spreadsheet-import/provider/compone
import { RecordIndexActionMenu } from '@/action-menu/components/RecordIndexActionMenu'; import { RecordIndexActionMenu } from '@/action-menu/components/RecordIndexActionMenu';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard'; import { useSetRecordGroup } from '@/object-record/record-group/hooks/useSetRecordGroup';
import { recordGroupDefinitionsComponentState } from '@/object-record/record-group/states/recordGroupDefinitionsComponentState';
import { RecordIndexFiltersToContextStoreEffect } from '@/object-record/record-index/components/RecordIndexFiltersToContextStoreEffect'; import { RecordIndexFiltersToContextStoreEffect } from '@/object-record/record-index/components/RecordIndexFiltersToContextStoreEffect';
import { recordIndexViewFilterGroupsState } from '@/object-record/record-index/states/recordIndexViewFilterGroupsState'; import { recordIndexViewFilterGroupsState } from '@/object-record/record-index/states/recordIndexViewFilterGroupsState';
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { ViewBar } from '@/views/components/ViewBar'; import { ViewBar } from '@/views/components/ViewBar';
import { ViewField } from '@/views/types/ViewField'; import { ViewField } from '@/views/types/ViewField';
@ -38,7 +36,7 @@ import { mapViewFieldsToColumnDefinitions } from '@/views/utils/mapViewFieldsToC
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters'; import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
import { mapViewGroupsToRecordGroupDefinitions } from '@/views/utils/mapViewGroupsToRecordGroupDefinitions'; import { mapViewGroupsToRecordGroupDefinitions } from '@/views/utils/mapViewGroupsToRecordGroupDefinitions';
import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts'; import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts';
import { useContext } from 'react'; import { useCallback, useContext } from 'react';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
const StyledContainer = styled.div` const StyledContainer = styled.div`
@ -68,9 +66,7 @@ export const RecordIndexContainer = () => {
objectNameSingular, objectNameSingular,
} = useContext(RecordIndexRootPropsContext); } = useContext(RecordIndexRootPropsContext);
const recordGroupDefinitionsCallbackState = useRecoilComponentCallbackStateV2( const setRecordGroup = useSetRecordGroup(recordIndexId);
recordGroupDefinitionsComponentState,
);
const { columnDefinitions, filterDefinitions, sortDefinitions } = const { columnDefinitions, filterDefinitions, sortDefinitions } =
useColumnDefinitionsFromFieldMetadata(objectMetadataItem); useColumnDefinitionsFromFieldMetadata(objectMetadataItem);
@ -96,8 +92,6 @@ export const RecordIndexContainer = () => {
recordTableId: recordIndexId, recordTableId: recordIndexId,
}); });
const { setColumns } = useRecordBoard(recordIndexId);
const onViewFieldsChange = useRecoilCallback( const onViewFieldsChange = useRecoilCallback(
({ set, snapshot }) => ({ set, snapshot }) =>
(viewFields: ViewField[]) => { (viewFields: ViewField[]) => {
@ -124,30 +118,16 @@ export const RecordIndexContainer = () => {
[columnDefinitions, setTableColumns], [columnDefinitions, setTableColumns],
); );
const onViewGroupsChange = useRecoilCallback( const onViewGroupsChange = useCallback(
({ set, snapshot }) => (viewGroups: ViewGroup[]) => {
(viewGroups: ViewGroup[]) => { const newGroupDefinitions = mapViewGroupsToRecordGroupDefinitions({
const newGroupDefinitions = mapViewGroupsToRecordGroupDefinitions({ objectMetadataItem,
objectMetadataItem, viewGroups,
viewGroups, });
});
setColumns(newGroupDefinitions); setRecordGroup(newGroupDefinitions);
},
const existingRecordIndexGroupDefinitions = snapshot [objectMetadataItem, setRecordGroup],
.getLoadable(recordGroupDefinitionsCallbackState)
.getValue();
if (
!isDeeplyEqual(
existingRecordIndexGroupDefinitions,
newGroupDefinitions,
)
) {
set(recordGroupDefinitionsCallbackState, newGroupDefinitions);
}
},
[objectMetadataItem, recordGroupDefinitionsCallbackState, setColumns],
); );
const setContextStoreTargetedRecordsRule = useSetRecoilComponentStateV2( const setContextStoreTargetedRecordsRule = useSetRecoilComponentStateV2(
@ -229,10 +209,7 @@ export const RecordIndexContainer = () => {
objectNameSingular={objectNameSingular} objectNameSingular={objectNameSingular}
recordBoardId={recordIndexId} recordBoardId={recordIndexId}
/> />
<RecordIndexBoardDataLoaderEffect <RecordIndexBoardDataLoaderEffect recordBoardId={recordIndexId} />
objectNameSingular={objectNameSingular}
recordBoardId={recordIndexId}
/>
</StyledContainerWithPadding> </StyledContainerWithPadding>
)} )}
<RecordIndexActionMenu /> <RecordIndexActionMenu />

View File

@ -1,8 +1,9 @@
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates';
import { useAddNewCard } from '@/object-record/record-board/record-board-column/hooks/useAddNewCard'; import { useAddNewCard } from '@/object-record/record-board/record-board-column/hooks/useAddNewCard';
import { useIsOpportunitiesCompanyFieldDisabled } from '@/object-record/record-board/record-board-column/hooks/useIsOpportunitiesCompanyFieldDisabled'; import { useIsOpportunitiesCompanyFieldDisabled } from '@/object-record/record-board/record-board-column/hooks/useIsOpportunitiesCompanyFieldDisabled';
import { recordBoardVisibleFieldDefinitionsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector';
import { visibleRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector';
import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition';
import { RecordIndexPageKanbanAddMenuItem } from '@/object-record/record-index/components/RecordIndexPageKanbanAddMenuItem'; import { RecordIndexPageKanbanAddMenuItem } from '@/object-record/record-index/components/RecordIndexPageKanbanAddMenuItem';
import { RecordIndexRootPropsContext } from '@/object-record/record-index/contexts/RecordIndexRootPropsContext'; import { RecordIndexRootPropsContext } from '@/object-record/record-index/contexts/RecordIndexRootPropsContext';
@ -11,6 +12,7 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useCallback, useContext } from 'react'; import { useCallback, useContext } from 'react';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
@ -32,6 +34,10 @@ export const RecordIndexPageKanbanAddButton = () => {
); );
const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular }); const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular });
const visibleRecordGroupIds = useRecoilComponentValueV2(
visibleRecordGroupIdsComponentSelector,
);
const recordIndexKanbanFieldMetadataId = useRecoilValue( const recordIndexKanbanFieldMetadataId = useRecoilValue(
recordIndexKanbanFieldMetadataIdState, recordIndexKanbanFieldMetadataIdState,
); );
@ -42,12 +48,11 @@ export const RecordIndexPageKanbanAddButton = () => {
const isOpportunity = const isOpportunity =
objectMetadataItem.nameSingular === CoreObjectNameSingular.Opportunity; objectMetadataItem.nameSingular === CoreObjectNameSingular.Opportunity;
const { columnIdsState, visibleFieldDefinitionsState } = const visibleFieldDefinitions = useRecoilComponentValueV2(
useRecordBoardStates(recordIndexId); recordBoardVisibleFieldDefinitionsComponentSelector,
const columnIds = useRecoilValue(columnIdsState); recordIndexId,
const visibleFieldDefinitions = useRecoilValue(
visibleFieldDefinitionsState(),
); );
const labelIdentifierField = visibleFieldDefinitions.find( const labelIdentifierField = visibleFieldDefinitions.find(
(field) => field.isLabelIdentifier, (field) => field.isLabelIdentifier,
); );
@ -101,11 +106,10 @@ export const RecordIndexPageKanbanAddButton = () => {
dropdownComponents={ dropdownComponents={
<StyledDropDownMenu> <StyledDropDownMenu>
<StyledDropdownMenuItemsContainer> <StyledDropdownMenuItemsContainer>
{columnIds.map((columnId) => ( {visibleRecordGroupIds.map((recordGroupId) => (
<RecordIndexPageKanbanAddMenuItem <RecordIndexPageKanbanAddMenuItem
key={columnId} key={recordGroupId}
columnId={columnId} columnId={recordGroupId}
recordIndexId={recordIndexId}
onItemClick={handleItemClick} onItemClick={handleItemClick}
/> />
))} ))}

View File

@ -1,7 +1,9 @@
import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
import { RecordGroupDefinitionType } from '@/object-record/record-group/types/RecordGroupDefinition'; import { RecordGroupDefinitionType } from '@/object-record/record-group/types/RecordGroupDefinition';
import { useRecordIndexPageKanbanAddMenuItem } from '@/object-record/record-index/hooks/useRecordIndexPageKanbanAddMenuItem';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
import { MenuItem, Tag } from 'twenty-ui'; import { MenuItem, Tag } from 'twenty-ui';
import { isDefined } from '~/utils/isDefined';
const StyledMenuItem = styled(MenuItem)` const StyledMenuItem = styled(MenuItem)`
width: calc(100% - 2 * var(--horizontal-padding)); width: calc(100% - 2 * var(--horizontal-padding));
@ -9,20 +11,18 @@ const StyledMenuItem = styled(MenuItem)`
type RecordIndexPageKanbanAddMenuItemProps = { type RecordIndexPageKanbanAddMenuItemProps = {
columnId: string; columnId: string;
recordIndexId: string;
onItemClick: (columnDefinition: any) => void; onItemClick: (columnDefinition: any) => void;
}; };
export const RecordIndexPageKanbanAddMenuItem = ({ export const RecordIndexPageKanbanAddMenuItem = ({
columnId, columnId,
recordIndexId,
onItemClick, onItemClick,
}: RecordIndexPageKanbanAddMenuItemProps) => { }: RecordIndexPageKanbanAddMenuItemProps) => {
const { columnDefinition } = useRecordIndexPageKanbanAddMenuItem( const recordGroupDefinition = useRecoilValue(
recordIndexId, recordGroupDefinitionFamilyState(columnId),
columnId,
); );
if (!columnDefinition) {
if (!isDefined(recordGroupDefinition)) {
return null; return null;
} }
@ -31,24 +31,24 @@ export const RecordIndexPageKanbanAddMenuItem = ({
text={ text={
<Tag <Tag
variant={ variant={
columnDefinition.type === RecordGroupDefinitionType.Value recordGroupDefinition.type === RecordGroupDefinitionType.Value
? 'solid' ? 'solid'
: 'outline' : 'outline'
} }
color={ color={
columnDefinition.type === RecordGroupDefinitionType.Value recordGroupDefinition.type === RecordGroupDefinitionType.Value
? columnDefinition.color ? recordGroupDefinition.color
: 'transparent' : 'transparent'
} }
text={columnDefinition.title} text={recordGroupDefinition.title}
weight={ weight={
columnDefinition.type === RecordGroupDefinitionType.Value recordGroupDefinition.type === RecordGroupDefinitionType.Value
? 'regular' ? 'regular'
: 'medium' : 'medium'
} }
/> />
} }
onClick={() => onItemClick(columnDefinition)} onClick={() => onItemClick(recordGroupDefinition)}
/> />
); );
}; };

View File

@ -8,14 +8,12 @@ import {
import { PERSON_FRAGMENT_WITH_DEPTH_ZERO_RELATIONS } from '@/object-record/hooks/__mocks__/personFragments'; import { PERSON_FRAGMENT_WITH_DEPTH_ZERO_RELATIONS } from '@/object-record/hooks/__mocks__/personFragments';
import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard'; import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard';
import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard'; import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
import { recordBoardKanbanFieldMetadataNameComponentState } from '@/object-record/record-board/states/recordBoardKanbanFieldMetadataNameComponentState'; import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState';
import { ViewType } from '@/views/types/ViewType'; import { ViewType } from '@/views/types/ViewType';
import { MockedResponse } from '@apollo/client/testing'; import { MockedResponse } from '@apollo/client/testing';
import { expect } from '@storybook/test'; import { expect } from '@storybook/test';
import gql from 'graphql-tag'; import gql from 'graphql-tag';
import { useRecoilValue } from 'recoil';
import { getJestMetadataAndApolloMocksAndContextStoreWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndContextStoreWrapper'; import { getJestMetadataAndApolloMocksAndContextStoreWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndContextStoreWrapper';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems'; import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
@ -232,10 +230,12 @@ describe('useRecordData', () => {
const callback = jest.fn(); const callback = jest.fn();
const { result } = renderHook( const { result } = renderHook(
() => { () => {
const kanbanFieldNameState = extractComponentState( const [recordGroupFieldMetadata, setRecordGroupFieldMetadata] =
recordBoardKanbanFieldMetadataNameComponentState, useRecoilComponentStateV2(
recordIndexId, recordGroupFieldMetadataComponentState,
); recordIndexId,
);
return { return {
tableData: useExportFetchRecords({ tableData: useExportFetchRecords({
recordIndexId, recordIndexId,
@ -246,8 +246,8 @@ describe('useRecordData', () => {
delayMs: 0, delayMs: 0,
viewType: ViewType.Kanban, viewType: ViewType.Kanban,
}), }),
useRecordBoardHook: useRecordBoard(recordIndexId), kanbanFieldName: recordGroupFieldMetadata?.name,
kanbanFieldName: useRecoilValue(kanbanFieldNameState), setRecordGroupFieldMetadata,
kanbanData: useObjectOptionsForBoard({ kanbanData: useObjectOptionsForBoard({
objectNameSingular: objectMetadataItem.nameSingular, objectNameSingular: objectMetadataItem.nameSingular,
recordBoardId: recordIndexId, recordBoardId: recordIndexId,
@ -269,9 +269,7 @@ describe('useRecordData', () => {
); );
await act(async () => { await act(async () => {
result.current.useRecordBoardHook.setKanbanFieldMetadataName( result.current.setRecordGroupFieldMetadata(updatedAtFieldMetadataItem);
updatedAtFieldMetadataItem?.name,
);
}); });
await act(async () => { await act(async () => {
@ -322,10 +320,12 @@ describe('useRecordData', () => {
const callback = jest.fn(); const callback = jest.fn();
const { result } = renderHook( const { result } = renderHook(
() => { () => {
const kanbanFieldNameState = extractComponentState( const [recordGroupFieldMetadata, setRecordGroupFieldMetadata] =
recordBoardKanbanFieldMetadataNameComponentState, useRecoilComponentStateV2(
recordIndexId, recordGroupFieldMetadataComponentState,
); recordIndexId,
);
return { return {
tableData: useExportFetchRecords({ tableData: useExportFetchRecords({
recordIndexId, recordIndexId,
@ -336,8 +336,9 @@ describe('useRecordData', () => {
delayMs: 0, delayMs: 0,
viewType: ViewType.Table, viewType: ViewType.Table,
}), }),
setKanbanFieldName: useRecordBoard(recordIndexId), objectMetadataItem,
kanbanFieldName: useRecoilValue(kanbanFieldNameState), kanbanFieldName: recordGroupFieldMetadata?.name,
setRecordGroupFieldMetadata,
kanbanData: useObjectOptionsForBoard({ kanbanData: useObjectOptionsForBoard({
objectNameSingular: objectMetadataItem.nameSingular, objectNameSingular: objectMetadataItem.nameSingular,
recordBoardId: recordIndexId, recordBoardId: recordIndexId,
@ -351,9 +352,14 @@ describe('useRecordData', () => {
); );
await act(async () => { await act(async () => {
result.current.setKanbanFieldName.setKanbanFieldMetadataName( const fieldMetadataItem =
result.current.kanbanData.hiddenBoardFields[0].metadata.fieldName, result.current.objectMetadataItem?.fields.find(
); (fieldMetadata) =>
fieldMetadata.id ===
result.current.kanbanData.hiddenBoardFields[0].fieldMetadataId,
);
result.current.setRecordGroupFieldMetadata(fieldMetadataItem);
}); });
await act(async () => { await act(async () => {

View File

@ -1,5 +1,4 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useRecoilValue } from 'recoil';
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'; import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition'; import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
@ -13,7 +12,7 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useLazyFindManyRecords } from '@/object-record/hooks/useLazyFindManyRecords'; import { useLazyFindManyRecords } from '@/object-record/hooks/useLazyFindManyRecords';
import { EXPORT_TABLE_DATA_DEFAULT_PAGE_SIZE } from '@/object-record/object-options-dropdown/constants/ExportTableDataDefaultPageSize'; import { EXPORT_TABLE_DATA_DEFAULT_PAGE_SIZE } from '@/object-record/object-options-dropdown/constants/ExportTableDataDefaultPageSize';
import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard'; import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard';
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
import { useFindManyParams } from '@/object-record/record-index/hooks/useLoadRecordIndexTable'; import { useFindManyParams } from '@/object-record/record-index/hooks/useLoadRecordIndexTable';
import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector'; import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
@ -68,10 +67,13 @@ export const useExportFetchRecords = ({
viewBarId: recordIndexId, viewBarId: recordIndexId,
}); });
const { kanbanFieldMetadataNameState } = useRecordBoardStates(recordIndexId); const recordGroupFieldMetadata = useRecoilComponentValueV2(
const kanbanFieldMetadataName = useRecoilValue(kanbanFieldMetadataNameState); recordGroupFieldMetadataComponentState,
recordIndexId,
);
const hiddenKanbanFieldColumn = hiddenBoardFields.find( const hiddenKanbanFieldColumn = hiddenBoardFields.find(
(column) => column.metadata.fieldName === kanbanFieldMetadataName, (column) => column.metadata.fieldName === recordGroupFieldMetadata?.name,
); );
const columns = useRecoilComponentValueV2( const columns = useRecoilComponentValueV2(
visibleTableColumnsComponentSelector, visibleTableColumnsComponentSelector,

View File

@ -1,12 +1,13 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useRecoilValue, useSetRecoilState } from 'recoil'; import { useRecoilValue } from 'recoil';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy'; import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard'; import { useSetRecordBoardRecordIds } from '@/object-record/record-board/hooks/useSetRecordBoardRecordIds';
import { isRecordBoardCompactModeActiveComponentState } from '@/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState';
import { recordBoardFieldDefinitionsComponentState } from '@/object-record/record-board/states/recordBoardFieldDefinitionsComponentState';
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter'; import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
import { recordGroupDefinitionsComponentState } from '@/object-record/record-group/states/recordGroupDefinitionsComponentState';
import { useRecordBoardRecordGqlFields } from '@/object-record/record-index/hooks/useRecordBoardRecordGqlFields'; import { useRecordBoardRecordGqlFields } from '@/object-record/record-index/hooks/useRecordBoardRecordGqlFields';
import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState'; import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState';
import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState'; import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState';
@ -14,7 +15,7 @@ import { recordIndexIsCompactModeActiveState } from '@/object-record/record-inde
import { recordIndexSortsState } from '@/object-record/record-index/states/recordIndexSortsState'; import { recordIndexSortsState } from '@/object-record/record-index/states/recordIndexSortsState';
import { recordIndexViewFilterGroupsState } from '@/object-record/record-index/states/recordIndexViewFilterGroupsState'; import { recordIndexViewFilterGroupsState } from '@/object-record/record-index/states/recordIndexViewFilterGroupsState';
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore'; import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { useSetRecordCountInCurrentView } from '@/views/hooks/useSetRecordCountInCurrentView'; import { useSetRecordCountInCurrentView } from '@/views/hooks/useSetRecordCountInCurrentView';
type UseLoadRecordIndexBoardProps = { type UseLoadRecordIndexBoardProps = {
@ -31,33 +32,28 @@ export const useLoadRecordIndexBoard = ({
const { objectMetadataItem } = useObjectMetadataItem({ const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular, objectNameSingular,
}); });
const {
setRecordIds: setRecordIdsInBoard, const setRecordBoardFieldDefinitions = useSetRecoilComponentStateV2(
setFieldDefinitions, recordBoardFieldDefinitionsComponentState,
setColumns, recordBoardId,
isCompactModeActiveState, );
} = useRecordBoard(recordBoardId);
const { setRecordIds: setRecordIdsInBoard } =
useSetRecordBoardRecordIds(recordBoardId);
const { upsertRecords: upsertRecordsInStore } = useUpsertRecordsInStore(); const { upsertRecords: upsertRecordsInStore } = useUpsertRecordsInStore();
const recordIndexFieldDefinitions = useRecoilValue( const recordIndexFieldDefinitions = useRecoilValue(
recordIndexFieldDefinitionsState, recordIndexFieldDefinitionsState,
); );
useEffect(() => { useEffect(() => {
setFieldDefinitions(recordIndexFieldDefinitions); setRecordBoardFieldDefinitions(recordIndexFieldDefinitions);
}, [recordIndexFieldDefinitions, setFieldDefinitions]); }, [recordIndexFieldDefinitions, setRecordBoardFieldDefinitions]);
const recordIndexViewFilterGroups = useRecoilValue( const recordIndexViewFilterGroups = useRecoilValue(
recordIndexViewFilterGroupsState, recordIndexViewFilterGroupsState,
); );
const recordGroupDefinitions = useRecoilComponentValueV2(
recordGroupDefinitionsComponentState,
);
useEffect(() => {
setColumns(recordGroupDefinitions);
}, [recordGroupDefinitions, setColumns]);
const recordIndexFilters = useRecoilValue(recordIndexFiltersState); const recordIndexFilters = useRecoilValue(recordIndexFiltersState);
const recordIndexSorts = useRecoilValue(recordIndexSortsState); const recordIndexSorts = useRecoilValue(recordIndexSortsState);
const requestFilters = computeViewRecordGqlOperationFilter( const requestFilters = computeViewRecordGqlOperationFilter(
@ -92,7 +88,10 @@ export const useLoadRecordIndexBoard = ({
const { setRecordCountInCurrentView } = const { setRecordCountInCurrentView } =
useSetRecordCountInCurrentView(viewBarId); useSetRecordCountInCurrentView(viewBarId);
const setIsCompactModeActive = useSetRecoilState(isCompactModeActiveState); const setIsCompactModeActive = useSetRecoilComponentStateV2(
isRecordBoardCompactModeActiveComponentState,
recordBoardId,
);
useEffect(() => { useEffect(() => {
setRecordIdsInBoard(records); setRecordIdsInBoard(records);

View File

@ -4,9 +4,9 @@ import { useRecoilValue } from 'recoil';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy'; import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; import { useSetRecordIdsForColumn } from '@/object-record/record-board/hooks/useSetRecordIdsForColumn';
import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard';
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter'; import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
import { useRecordBoardRecordGqlFields } from '@/object-record/record-index/hooks/useRecordBoardRecordGqlFields'; import { useRecordBoardRecordGqlFields } from '@/object-record/record-index/hooks/useRecordBoardRecordGqlFields';
import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState'; import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState';
import { recordIndexSortsState } from '@/object-record/record-index/states/recordIndexSortsState'; import { recordIndexSortsState } from '@/object-record/record-index/states/recordIndexSortsState';
@ -30,16 +30,18 @@ export const useLoadRecordIndexBoardColumn = ({
const { objectMetadataItem } = useObjectMetadataItem({ const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular, objectNameSingular,
}); });
const { setRecordIdsForColumn } = useRecordBoard(recordBoardId); const { setRecordIdsForColumn } = useSetRecordIdsForColumn(recordBoardId);
const { columnsFamilySelector } = useRecordBoardStates(recordBoardId);
const { upsertRecords: upsertRecordsInStore } = useUpsertRecordsInStore(); const { upsertRecords: upsertRecordsInStore } = useUpsertRecordsInStore();
const recordGroupDefinition = useRecoilValue(
recordGroupDefinitionFamilyState(columnId),
);
const recordIndexViewFilterGroups = useRecoilValue( const recordIndexViewFilterGroups = useRecoilValue(
recordIndexViewFilterGroupsState, recordIndexViewFilterGroupsState,
); );
const recordIndexFilters = useRecoilValue(recordIndexFiltersState); const recordIndexFilters = useRecoilValue(recordIndexFiltersState);
const recordIndexSorts = useRecoilValue(recordIndexSortsState); const recordIndexSorts = useRecoilValue(recordIndexSortsState);
const columnDefinition = useRecoilValue(columnsFamilySelector(columnId));
const requestFilters = computeViewRecordGqlOperationFilter( const requestFilters = computeViewRecordGqlOperationFilter(
recordIndexFilters, recordIndexFilters,
@ -60,9 +62,9 @@ export const useLoadRecordIndexBoardColumn = ({
const filter = { const filter = {
...requestFilters, ...requestFilters,
[recordIndexKanbanFieldMetadataItem?.name ?? '']: isDefined( [recordIndexKanbanFieldMetadataItem?.name ?? '']: isDefined(
columnDefinition?.value, recordGroupDefinition?.value,
) )
? { in: [columnDefinition?.value] } ? { in: [recordGroupDefinition?.value] }
: { is: 'NULL' }, : { is: 'NULL' },
}; };

View File

@ -27,8 +27,7 @@ export const useFindManyParams = (
objectNameSingular, objectNameSingular,
}); });
const currentRecordGroupDefinition = const currentRecordGroupDefinition = useCurrentRecordGroupDefinition();
useCurrentRecordGroupDefinition(recordTableId);
const tableViewFilterGroups = useRecoilComponentValueV2( const tableViewFilterGroups = useRecoilComponentValueV2(
tableViewFilterGroupsComponentState, tableViewFilterGroupsComponentState,

View File

@ -1,9 +1,9 @@
import { useRecoilValue } from 'recoil';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { getObjectMetadataIdentifierFields } from '@/object-metadata/utils/getObjectMetadataIdentifierFields'; import { getObjectMetadataIdentifierFields } from '@/object-metadata/utils/getObjectMetadataIdentifierFields';
import { hasPositionField } from '@/object-metadata/utils/hasPositionField'; import { hasPositionField } from '@/object-metadata/utils/hasPositionField';
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; import { recordBoardVisibleFieldDefinitionsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector';
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { isDefined } from '~/utils/isDefined'; import { isDefined } from '~/utils/isDefined';
export const useRecordBoardRecordGqlFields = ({ export const useRecordBoardRecordGqlFields = ({
@ -13,15 +13,17 @@ export const useRecordBoardRecordGqlFields = ({
recordBoardId: string; recordBoardId: string;
objectMetadataItem: ObjectMetadataItem; objectMetadataItem: ObjectMetadataItem;
}) => { }) => {
const { kanbanFieldMetadataNameState, visibleFieldDefinitionsState } = const visibleFieldDefinitions = useRecoilComponentValueV2(
useRecordBoardStates(recordBoardId); recordBoardVisibleFieldDefinitionsComponentSelector,
recordBoardId,
);
const { imageIdentifierFieldMetadataItem, labelIdentifierFieldMetadataItem } = const { imageIdentifierFieldMetadataItem, labelIdentifierFieldMetadataItem } =
getObjectMetadataIdentifierFields({ objectMetadataItem }); getObjectMetadataIdentifierFields({ objectMetadataItem });
const kanbanFieldMetadataName = useRecoilValue(kanbanFieldMetadataNameState); const recordGroupFieldMetadata = useRecoilComponentValueV2(
const visibleFieldDefinitions = useRecoilValue( recordGroupFieldMetadataComponentState,
visibleFieldDefinitionsState(), recordBoardId,
); );
const identifierQueryFields: Record<string, boolean> = {}; const identifierQueryFields: Record<string, boolean> = {};
@ -59,8 +61,8 @@ export const useRecordBoardRecordGqlFields = ({
}, },
}; };
if (isDefined(kanbanFieldMetadataName)) { if (isDefined(recordGroupFieldMetadata?.name)) {
recordGqlFields[kanbanFieldMetadataName] = true; recordGqlFields[recordGroupFieldMetadata.name] = true;
} }
return recordGqlFields; return recordGqlFields;

View File

@ -1,12 +0,0 @@
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates';
import { useRecoilValue } from 'recoil';
export const useRecordIndexPageKanbanAddMenuItem = (
recordIndexId: string,
columnId: string,
) => {
const { columnsFamilySelector } = useRecordBoardStates(recordIndexId);
const columnDefinition = useRecoilValue(columnsFamilySelector(columnId));
return { columnDefinition };
};

View File

@ -0,0 +1,10 @@
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
export const recordIndexAllRowIdsComponentState = createComponentStateV2<
string[]
>({
key: 'recordIndexAllRowIdsComponentState',
defaultValue: [],
componentInstanceContext: ViewComponentInstanceContext,
});

View File

@ -1,10 +1,10 @@
import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition';
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
import { createComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentFamilyStateV2'; import { createComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentFamilyStateV2';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
export const tableRowIdsByGroupComponentFamilyState = export const recordIndexRowIdsByGroupComponentFamilyState =
createComponentFamilyStateV2<string[], RecordGroupDefinition['id']>({ createComponentFamilyStateV2<string[], RecordGroupDefinition['id']>({
key: 'tableRowIdsByGroupComponentFamilyState', key: 'recordIndexRowIdsByGroupComponentFamilyState',
defaultValue: [], defaultValue: [],
componentInstanceContext: RecordTableComponentInstanceContext, componentInstanceContext: ViewComponentInstanceContext,
}); });

View File

@ -1,7 +1,8 @@
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { isNonEmptyString, isNull } from '@sniptt/guards'; import { isNonEmptyString, isNull } from '@sniptt/guards';
import { hasRecordGroupDefinitionsComponentSelector } from '@/object-record/record-group/states/hasRecordGroupDefinitionsComponentSelector'; import { hasRecordGroupsComponentSelector } from '@/object-record/record-group/states/selectors/hasRecordGroupsComponentSelector';
import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState';
import { RecordTableComponentInstance } from '@/object-record/record-table/components/RecordTableComponentInstance'; import { RecordTableComponentInstance } from '@/object-record/record-table/components/RecordTableComponentInstance';
import { RecordTableContextProvider } from '@/object-record/record-table/components/RecordTableContextProvider'; import { RecordTableContextProvider } from '@/object-record/record-table/components/RecordTableContextProvider';
import { RecordTableStickyEffect } from '@/object-record/record-table/components/RecordTableStickyEffect'; import { RecordTableStickyEffect } from '@/object-record/record-table/components/RecordTableStickyEffect';
@ -16,7 +17,6 @@ import { RecordTableRecordGroupsBody } from '@/object-record/record-table/record
import { RecordTableHeader } from '@/object-record/record-table/record-table-header/components/RecordTableHeader'; import { RecordTableHeader } from '@/object-record/record-table/record-table-header/components/RecordTableHeader';
import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState'; import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState';
import { recordTablePendingRecordIdComponentState } from '@/object-record/record-table/states/recordTablePendingRecordIdComponentState'; import { recordTablePendingRecordIdComponentState } from '@/object-record/record-table/states/recordTablePendingRecordIdComponentState';
import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState';
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect'; import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener'; import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
@ -53,8 +53,8 @@ export const RecordTable = ({
recordTableId, recordTableId,
); );
const tableRowIds = useRecoilComponentValueV2( const allRowIds = useRecoilComponentValueV2(
tableAllRowIdsComponentState, recordIndexAllRowIdsComponentState,
recordTableId, recordTableId,
); );
@ -64,13 +64,13 @@ export const RecordTable = ({
); );
const hasRecordGroups = useRecoilComponentValueV2( const hasRecordGroups = useRecoilComponentValueV2(
hasRecordGroupDefinitionsComponentSelector, hasRecordGroupsComponentSelector,
recordTableId, recordTableId,
); );
const recordTableIsEmpty = const recordTableIsEmpty =
!isRecordTableInitialLoading && !isRecordTableInitialLoading &&
tableRowIds.length === 0 && allRowIds.length === 0 &&
isNull(pendingRecordId); isNull(pendingRecordId);
const { resetTableRowSelection, setRowSelected } = useRecordTable({ const { resetTableRowSelection, setRowSelected } = useRecordTable({
@ -109,9 +109,7 @@ export const RecordTable = ({
{!hasRecordGroups ? ( {!hasRecordGroups ? (
<RecordTableNoRecordGroupBody /> <RecordTableNoRecordGroupBody />
) : ( ) : (
<RecordTableRecordGroupsBody <RecordTableRecordGroupsBody />
objectNameSingular={objectNameSingular}
/>
)} )}
<RecordTableStickyEffect /> <RecordTableStickyEffect />
</StyledTable> </StyledTable>

View File

@ -1,14 +1,16 @@
import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState';
import { RecordTableBodyFetchMoreLoader } from '@/object-record/record-table/record-table-body/components/RecordTableBodyFetchMoreLoader'; import { RecordTableBodyFetchMoreLoader } from '@/object-record/record-table/record-table-body/components/RecordTableBodyFetchMoreLoader';
import { RecordTableRow } from '@/object-record/record-table/record-table-row/components/RecordTableRow'; import { RecordTableRow } from '@/object-record/record-table/record-table-row/components/RecordTableRow';
import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
export const RecordTableNoRecordGroupRows = () => { export const RecordTableNoRecordGroupRows = () => {
const rowIds = useRecoilComponentValueV2(tableAllRowIdsComponentState); const allRowIds = useRecoilComponentValueV2(
recordIndexAllRowIdsComponentState,
);
return ( return (
<> <>
{rowIds.map((recordId, rowIndex) => { {allRowIds.map((recordId, rowIndex) => {
return ( return (
<RecordTableRow <RecordTableRow
key={recordId} key={recordId}

View File

@ -1,7 +1,7 @@
import { useCurrentRecordGroupId } from '@/object-record/record-group/hooks/useCurrentRecordGroupId'; import { useCurrentRecordGroupId } from '@/object-record/record-group/hooks/useCurrentRecordGroupId';
import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState';
import { recordIndexRowIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRowIdsByGroupComponentFamilyState';
import { RecordTableRow } from '@/object-record/record-table/record-table-row/components/RecordTableRow'; import { RecordTableRow } from '@/object-record/record-table/record-table-row/components/RecordTableRow';
import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState';
import { tableRowIdsByGroupComponentFamilyState } from '@/object-record/record-table/states/tableRowIdsByGroupComponentFamilyState';
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useMemo } from 'react'; import { useMemo } from 'react';
@ -9,10 +9,12 @@ import { useMemo } from 'react';
export const RecordTableRecordGroupRows = () => { export const RecordTableRecordGroupRows = () => {
const recordGroupId = useCurrentRecordGroupId(); const recordGroupId = useCurrentRecordGroupId();
const allRowIds = useRecoilComponentValueV2(tableAllRowIdsComponentState); const allRowIds = useRecoilComponentValueV2(
recordIndexAllRowIdsComponentState,
);
const recordGroupRowIds = useRecoilComponentFamilyValueV2( const recordGroupRowIds = useRecoilComponentFamilyValueV2(
tableRowIdsByGroupComponentFamilyState, recordIndexRowIdsByGroupComponentFamilyState,
recordGroupId, recordGroupId,
); );

View File

@ -1,9 +1,9 @@
import { isNull } from '@sniptt/guards'; import { isNull } from '@sniptt/guards';
import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState';
import { RecordTableEmptyState } from '@/object-record/record-table/empty-state/components/RecordTableEmptyState'; import { RecordTableEmptyState } from '@/object-record/record-table/empty-state/components/RecordTableEmptyState';
import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState'; import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState';
import { recordTablePendingRecordIdComponentState } from '@/object-record/record-table/states/recordTablePendingRecordIdComponentState'; import { recordTablePendingRecordIdComponentState } from '@/object-record/record-table/states/recordTablePendingRecordIdComponentState';
import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
type RecordTableEmptyHandlerProps = { type RecordTableEmptyHandlerProps = {
@ -20,8 +20,8 @@ export const RecordTableEmptyHandler = ({
recordTableId, recordTableId,
); );
const tableRowIds = useRecoilComponentValueV2( const allRowIds = useRecoilComponentValueV2(
tableAllRowIdsComponentState, recordIndexAllRowIdsComponentState,
recordTableId, recordTableId,
); );
@ -32,7 +32,7 @@ export const RecordTableEmptyHandler = ({
const recordTableIsEmpty = const recordTableIsEmpty =
!isRecordTableInitialLoading && !isRecordTableInitialLoading &&
tableRowIds.length === 0 && allRowIds.length === 0 &&
isNull(pendingRecordId); isNull(pendingRecordId);
if (recordTableIsEmpty) { if (recordTableIsEmpty) {

View File

@ -2,10 +2,10 @@ import { useRecoilCallback } from 'recoil';
import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId'; import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId';
import { getActionMenuIdFromRecordIndexId } from '@/action-menu/utils/getActionMenuIdFromRecordIndexId'; import { getActionMenuIdFromRecordIndexId } from '@/action-menu/utils/getActionMenuIdFromRecordIndexId';
import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState';
import { hasUserSelectedAllRowsComponentState } from '@/object-record/record-table/record-table-row/states/hasUserSelectedAllRowsFamilyState'; import { hasUserSelectedAllRowsComponentState } from '@/object-record/record-table/record-table-row/states/hasUserSelectedAllRowsFamilyState';
import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState';
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
@ -18,8 +18,8 @@ export const useResetTableRowSelection = (recordTableId?: string) => {
recordTableId, recordTableId,
); );
const tableAllRowIdsState = useRecoilComponentCallbackStateV2( const recordIndexAllRowIdsState = useRecoilComponentCallbackStateV2(
tableAllRowIdsComponentState, recordIndexAllRowIdsComponentState,
recordTableIdFromContext, recordTableIdFromContext,
); );
@ -43,9 +43,9 @@ export const useResetTableRowSelection = (recordTableId?: string) => {
return useRecoilCallback( return useRecoilCallback(
({ set, snapshot }) => ({ set, snapshot }) =>
() => { () => {
const tableRowIds = getSnapshotValue(snapshot, tableAllRowIdsState); const allRowIds = getSnapshotValue(snapshot, recordIndexAllRowIdsState);
for (const rowId of tableRowIds) { for (const rowId of allRowIds) {
set(isRowSelectedFamilyState(rowId), false); set(isRowSelectedFamilyState(rowId), false);
} }
@ -54,7 +54,7 @@ export const useResetTableRowSelection = (recordTableId?: string) => {
set(isActionMenuDropdownOpenState, false); set(isActionMenuDropdownOpenState, false);
}, },
[ [
tableAllRowIdsState, recordIndexAllRowIdsState,
hasUserSelectedAllRowsState, hasUserSelectedAllRowsState,
isActionMenuDropdownOpenState, isActionMenuDropdownOpenState,
isRowSelectedFamilyState, isRowSelectedFamilyState,

View File

@ -1,8 +1,8 @@
import { useRecoilCallback } from 'recoil'; import { useRecoilCallback } from 'recoil';
import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState';
import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState';
import { allRowsSelectedStatusComponentSelector } from '@/object-record/record-table/states/selectors/allRowsSelectedStatusComponentSelector'; import { allRowsSelectedStatusComponentSelector } from '@/object-record/record-table/states/selectors/allRowsSelectedStatusComponentSelector';
import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState';
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
@ -15,8 +15,8 @@ export const useSelectAllRows = (recordTableId?: string) => {
isRowSelectedComponentFamilyState, isRowSelectedComponentFamilyState,
recordTableId, recordTableId,
); );
const tableAllRowIdsState = useRecoilComponentCallbackStateV2( const recordIndexAllRowIdsState = useRecoilComponentCallbackStateV2(
tableAllRowIdsComponentState, recordIndexAllRowIdsComponentState,
recordTableId, recordTableId,
); );
@ -28,24 +28,24 @@ export const useSelectAllRows = (recordTableId?: string) => {
allRowsSelectedStatusSelector, allRowsSelectedStatusSelector,
); );
const tableRowIds = getSnapshotValue(snapshot, tableAllRowIdsState); const allRowIds = getSnapshotValue(snapshot, recordIndexAllRowIdsState);
if ( if (
allRowsSelectedStatus === 'none' || allRowsSelectedStatus === 'none' ||
allRowsSelectedStatus === 'some' allRowsSelectedStatus === 'some'
) { ) {
for (const rowId of tableRowIds) { for (const rowId of allRowIds) {
set(isRowSelectedFamilyState(rowId), true); set(isRowSelectedFamilyState(rowId), true);
} }
} else { } else {
for (const rowId of tableRowIds) { for (const rowId of allRowIds) {
set(isRowSelectedFamilyState(rowId), false); set(isRowSelectedFamilyState(rowId), false);
} }
} }
}, },
[ [
allRowsSelectedStatusSelector, allRowsSelectedStatusSelector,
tableAllRowIdsState, recordIndexAllRowIdsState,
isRowSelectedFamilyState, isRowSelectedFamilyState,
], ],
); );

View File

@ -1,11 +1,11 @@
import { useRecoilCallback } from 'recoil'; import { useRecoilCallback } from 'recoil';
import { recordGroupDefinitionsComponentState } from '@/object-record/record-group/states/recordGroupDefinitionsComponentState'; import { recordGroupIdsComponentState } from '@/object-record/record-group/states/recordGroupIdsComponentState';
import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState';
import { recordIndexRowIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRowIdsByGroupComponentFamilyState';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { hasUserSelectedAllRowsComponentState } from '@/object-record/record-table/record-table-row/states/hasUserSelectedAllRowsFamilyState'; import { hasUserSelectedAllRowsComponentState } from '@/object-record/record-table/record-table-row/states/hasUserSelectedAllRowsFamilyState';
import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState';
import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState';
import { tableRowIdsByGroupComponentFamilyState } from '@/object-record/record-table/states/tableRowIdsByGroupComponentFamilyState';
import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
@ -21,12 +21,12 @@ export const useSetRecordTableData = ({
recordTableId, recordTableId,
onEntityCountChange, onEntityCountChange,
}: useSetRecordTableDataProps) => { }: useSetRecordTableDataProps) => {
const tableRowIdsByGroupFamilyState = useRecoilComponentCallbackStateV2( const recordIndexRowIdsByGroupFamilyState = useRecoilComponentCallbackStateV2(
tableRowIdsByGroupComponentFamilyState, recordIndexRowIdsByGroupComponentFamilyState,
recordTableId, recordTableId,
); );
const tableAllRowIdsState = useRecoilComponentCallbackStateV2( const recordIndexAllRowIdsState = useRecoilComponentCallbackStateV2(
tableAllRowIdsComponentState, recordIndexAllRowIdsComponentState,
recordTableId, recordTableId,
); );
const isRowSelectedFamilyState = useRecoilComponentCallbackStateV2( const isRowSelectedFamilyState = useRecoilComponentCallbackStateV2(
@ -37,8 +37,8 @@ export const useSetRecordTableData = ({
hasUserSelectedAllRowsComponentState, hasUserSelectedAllRowsComponentState,
recordTableId, recordTableId,
); );
const recordGroupDefinitionsState = useRecoilComponentCallbackStateV2( const recordIndexRecordGroupIdsState = useRecoilComponentCallbackStateV2(
recordGroupDefinitionsComponentState, recordGroupIdsComponentState,
recordTableId, recordTableId,
); );
@ -46,11 +46,11 @@ export const useSetRecordTableData = ({
({ set, snapshot }) => ({ set, snapshot }) =>
<T extends ObjectRecord>({ <T extends ObjectRecord>({
records, records,
recordGroupId, currentRecordGroupId,
totalCount, totalCount,
}: { }: {
records: T[]; records: T[];
recordGroupId?: string; currentRecordGroupId?: string;
totalCount?: number; totalCount?: number;
}) => { }) => {
for (const record of records) { for (const record of records) {
@ -66,9 +66,9 @@ export const useSetRecordTableData = ({
const currentRowIds = getSnapshotValue( const currentRowIds = getSnapshotValue(
snapshot, snapshot,
recordGroupId currentRecordGroupId
? tableRowIdsByGroupFamilyState(recordGroupId) ? recordIndexRowIdsByGroupFamilyState(currentRecordGroupId)
: tableAllRowIdsState, : recordIndexAllRowIdsState,
); );
const hasUserSelectedAllRows = getSnapshotValue( const hasUserSelectedAllRows = getSnapshotValue(
@ -76,9 +76,9 @@ export const useSetRecordTableData = ({
hasUserSelectedAllRowsState, hasUserSelectedAllRowsState,
); );
const recordGroupDefinitions = getSnapshotValue( const recordGroupIds = getSnapshotValue(
snapshot, snapshot,
recordGroupDefinitionsState, recordIndexRecordGroupIdsState,
); );
const recordIds = records.map((record) => record.id); const recordIds = records.map((record) => record.id);
@ -90,39 +90,42 @@ export const useSetRecordTableData = ({
} }
} }
if (isDefined(recordGroupId)) { if (isDefined(currentRecordGroupId)) {
// TODO: Hack to store all ids in the same order as the record group definitions // TODO: Hack to store all ids in the same order as the record group definitions
// Should be replaced by something more efficient // Should be replaced by something more efficient
const allRowIds: string[] = []; const allRowIds: string[] = [];
set(tableRowIdsByGroupFamilyState(recordGroupId), recordIds); set(
recordIndexRowIdsByGroupFamilyState(currentRecordGroupId),
recordIds,
);
for (const recordGroupDefinition of recordGroupDefinitions) { for (const recordGroupId of recordGroupIds) {
const tableRowIdsByGroup = const tableRowIdsByGroup =
recordGroupDefinition.id !== recordGroupId recordGroupId !== currentRecordGroupId
? getSnapshotValue( ? getSnapshotValue(
snapshot, snapshot,
tableRowIdsByGroupFamilyState(recordGroupDefinition.id), recordIndexRowIdsByGroupFamilyState(recordGroupId),
) )
: recordIds; : recordIds;
allRowIds.push(...tableRowIdsByGroup); allRowIds.push(...tableRowIdsByGroup);
} }
set(tableAllRowIdsState, allRowIds); set(recordIndexAllRowIdsState, allRowIds);
} else { } else {
set(tableAllRowIdsState, recordIds); set(recordIndexAllRowIdsState, recordIds);
} }
onEntityCountChange(totalCount); onEntityCountChange(totalCount);
} }
}, },
[ [
tableRowIdsByGroupFamilyState, recordIndexRowIdsByGroupFamilyState,
tableAllRowIdsState, recordIndexAllRowIdsState,
recordGroupDefinitionsState, hasUserSelectedAllRowsState,
recordIndexRecordGroupIdsState,
onEntityCountChange, onEntityCountChange,
isRowSelectedFamilyState, isRowSelectedFamilyState,
hasUserSelectedAllRowsState,
], ],
); );
}; };

View File

@ -3,9 +3,9 @@ import { useRecoilCallback } from 'recoil';
import { MoveFocusDirection } from '@/object-record/record-table/types/MoveFocusDirection'; import { MoveFocusDirection } from '@/object-record/record-table/types/MoveFocusDirection';
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState';
import { numberOfTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/numberOfTableColumnsComponentSelector'; import { numberOfTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/numberOfTableColumnsComponentSelector';
import { softFocusPositionComponentState } from '@/object-record/record-table/states/softFocusPositionComponentState'; import { softFocusPositionComponentState } from '@/object-record/record-table/states/softFocusPositionComponentState';
import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState';
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
import { useSetSoftFocusPosition } from './internal/useSetSoftFocusPosition'; import { useSetSoftFocusPosition } from './internal/useSetSoftFocusPosition';
@ -17,8 +17,8 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => {
recordTableId, recordTableId,
); );
const tableAllRowIdsState = useRecoilComponentCallbackStateV2( const recordIndexAllRowIdsState = useRecoilComponentCallbackStateV2(
tableAllRowIdsComponentState, recordIndexAllRowIdsComponentState,
recordTableId, recordTableId,
); );
@ -47,7 +47,7 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => {
const moveDown = useRecoilCallback( const moveDown = useRecoilCallback(
({ snapshot }) => ({ snapshot }) =>
() => { () => {
const allRowIds = getSnapshotValue(snapshot, tableAllRowIdsState); const allRowIds = getSnapshotValue(snapshot, recordIndexAllRowIdsState);
const softFocusPosition = getSnapshotValue( const softFocusPosition = getSnapshotValue(
snapshot, snapshot,
softFocusPositionState, softFocusPositionState,
@ -64,7 +64,7 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => {
row: newRowIndex, row: newRowIndex,
}); });
}, },
[tableAllRowIdsState, setSoftFocusPosition, softFocusPositionState], [recordIndexAllRowIdsState, setSoftFocusPosition, softFocusPositionState],
); );
const numberOfTableColumnsSelector = useRecoilComponentCallbackStateV2( const numberOfTableColumnsSelector = useRecoilComponentCallbackStateV2(
@ -75,7 +75,7 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => {
const moveRight = useRecoilCallback( const moveRight = useRecoilCallback(
({ snapshot }) => ({ snapshot }) =>
() => { () => {
const allRowIds = getSnapshotValue(snapshot, tableAllRowIdsState); const allRowIds = getSnapshotValue(snapshot, recordIndexAllRowIdsState);
const softFocusPosition = getSnapshotValue( const softFocusPosition = getSnapshotValue(
snapshot, snapshot,
softFocusPositionState, softFocusPositionState,
@ -116,7 +116,7 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => {
} }
}, },
[ [
tableAllRowIdsState, recordIndexAllRowIdsState,
softFocusPositionState, softFocusPositionState,
numberOfTableColumnsSelector, numberOfTableColumnsSelector,
setSoftFocusPosition, setSoftFocusPosition,

View File

@ -3,10 +3,10 @@ import { ReactNode, useContext } from 'react';
import { useSetRecoilState } from 'recoil'; import { useSetRecoilState } from 'recoil';
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState';
import { RecordTableContext } from '@/object-record/record-table/contexts/RecordTableContext'; import { RecordTableContext } from '@/object-record/record-table/contexts/RecordTableContext';
import { useComputeNewRowPosition } from '@/object-record/record-table/hooks/useComputeNewRowPosition'; import { useComputeNewRowPosition } from '@/object-record/record-table/hooks/useComputeNewRowPosition';
import { isRemoveSortingModalOpenState } from '@/object-record/record-table/states/isRemoveSortingModalOpenState'; import { isRemoveSortingModalOpenState } from '@/object-record/record-table/states/isRemoveSortingModalOpenState';
import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
import { isDefined } from '~/utils/isDefined'; import { isDefined } from '~/utils/isDefined';
@ -22,8 +22,8 @@ export const RecordTableBodyDragDropContext = ({
objectNameSingular, objectNameSingular,
}); });
const tableAllRowIds = useRecoilComponentValueV2( const allRowIds = useRecoilComponentValueV2(
tableAllRowIdsComponentState, recordIndexAllRowIdsComponentState,
); );
const { currentViewWithCombinedFiltersAndSorts } = const { currentViewWithCombinedFiltersAndSorts } =
@ -43,7 +43,7 @@ export const RecordTableBodyDragDropContext = ({
return; return;
} }
const computeResult = computeNewRowPosition(result, tableAllRowIds); const computeResult = computeNewRowPosition(result, allRowIds);
if (!isDefined(computeResult)) { if (!isDefined(computeResult)) {
return; return;

View File

@ -1,22 +1,22 @@
import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState';
import { RecordTableNoRecordGroupRows } from '@/object-record/record-table/components/RecordTableNoRecordGroupRows'; import { RecordTableNoRecordGroupRows } from '@/object-record/record-table/components/RecordTableNoRecordGroupRows';
import { RecordTableBodyDragDropContext } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDragDropContext'; import { RecordTableBodyDragDropContext } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDragDropContext';
import { RecordTableBodyDroppable } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDroppable'; import { RecordTableBodyDroppable } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDroppable';
import { RecordTableBodyLoading } from '@/object-record/record-table/record-table-body/components/RecordTableBodyLoading'; import { RecordTableBodyLoading } from '@/object-record/record-table/record-table-body/components/RecordTableBodyLoading';
import { RecordTablePendingRow } from '@/object-record/record-table/record-table-row/components/RecordTablePendingRow'; import { RecordTablePendingRow } from '@/object-record/record-table/record-table-row/components/RecordTablePendingRow';
import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState'; import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState';
import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
export const RecordTableNoRecordGroupBody = () => { export const RecordTableNoRecordGroupBody = () => {
const tableAllRowIds = useRecoilComponentValueV2( const allRowIds = useRecoilComponentValueV2(
tableAllRowIdsComponentState, recordIndexAllRowIdsComponentState,
); );
const isRecordTableInitialLoading = useRecoilComponentValueV2( const isRecordTableInitialLoading = useRecoilComponentValueV2(
isRecordTableInitialLoadingComponentState, isRecordTableInitialLoadingComponentState,
); );
if (isRecordTableInitialLoading && tableAllRowIds.length === 0) { if (isRecordTableInitialLoading && allRowIds.length === 0) {
return <RecordTableBodyLoading />; return <RecordTableBodyLoading />;
} }

View File

@ -56,7 +56,7 @@ export const RecordTableRecordGroupBodyEffect = () => {
if (!loading) { if (!loading) {
setRecordTableData({ setRecordTableData({
records, records,
recordGroupId, currentRecordGroupId: recordGroupId,
totalCount, totalCount,
}); });
} }

View File

@ -1,18 +1,15 @@
import { RecordGroupContext } from '@/object-record/record-group/states/context/RecordGroupContext'; import { RecordGroupContext } from '@/object-record/record-group/states/context/RecordGroupContext';
import { recordGroupDefinitionsComponentState } from '@/object-record/record-group/states/recordGroupDefinitionsComponentState'; import { recordGroupIdsComponentState } from '@/object-record/record-group/states/recordGroupIdsComponentState';
import { RecordTableRecordGroupBodyEffect } from '@/object-record/record-table/record-table-body/components/RecordTableRecordGroupBodyEffect'; import { RecordTableRecordGroupBodyEffect } from '@/object-record/record-table/record-table-body/components/RecordTableRecordGroupBodyEffect';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
export const RecordTableRecordGroupBodyEffects = () => { export const RecordTableRecordGroupBodyEffects = () => {
const recordGroupDefinitions = useRecoilComponentValueV2( const recordGroupIds = useRecoilComponentValueV2(
recordGroupDefinitionsComponentState, recordGroupIdsComponentState,
); );
return recordGroupDefinitions.map((recordGroupDefinition) => ( return recordGroupIds.map((recordGroupId) => (
<RecordGroupContext.Provider <RecordGroupContext.Provider key={recordGroupId} value={{ recordGroupId }}>
key={recordGroupDefinition.id}
value={{ recordGroupId: recordGroupDefinition.id }}
>
<RecordTableRecordGroupBodyEffect /> <RecordTableRecordGroupBodyEffect />
</RecordGroupContext.Provider> </RecordGroupContext.Provider>
)); ));

View File

@ -1,32 +1,28 @@
import { useRecordGroups } from '@/object-record/record-group/hooks/useRecordGroups';
import { RecordGroupContext } from '@/object-record/record-group/states/context/RecordGroupContext'; import { RecordGroupContext } from '@/object-record/record-group/states/context/RecordGroupContext';
import { visibleRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector';
import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState';
import { RecordTableRecordGroupRows } from '@/object-record/record-table/components/RecordTableRecordGroupRows'; import { RecordTableRecordGroupRows } from '@/object-record/record-table/components/RecordTableRecordGroupRows';
import { RecordTableBodyDragDropContext } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDragDropContext'; import { RecordTableBodyDragDropContext } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDragDropContext';
import { RecordTableBodyDroppable } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDroppable'; import { RecordTableBodyDroppable } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDroppable';
import { RecordTableBodyLoading } from '@/object-record/record-table/record-table-body/components/RecordTableBodyLoading'; import { RecordTableBodyLoading } from '@/object-record/record-table/record-table-body/components/RecordTableBodyLoading';
import { RecordTablePendingRow } from '@/object-record/record-table/record-table-row/components/RecordTablePendingRow'; import { RecordTablePendingRow } from '@/object-record/record-table/record-table-row/components/RecordTablePendingRow';
import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState'; import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState';
import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
type RecordTableRecordGroupsBodyProps = { export const RecordTableRecordGroupsBody = () => {
objectNameSingular: string; const allRowIds = useRecoilComponentValueV2(
}; recordIndexAllRowIdsComponentState,
export const RecordTableRecordGroupsBody = ({
objectNameSingular,
}: RecordTableRecordGroupsBodyProps) => {
const tableAllRowIds = useRecoilComponentValueV2(
tableAllRowIdsComponentState,
); );
const isRecordTableInitialLoading = useRecoilComponentValueV2( const isRecordTableInitialLoading = useRecoilComponentValueV2(
isRecordTableInitialLoadingComponentState, isRecordTableInitialLoadingComponentState,
); );
const { visibleRecordGroups } = useRecordGroups({ objectNameSingular }); const visibleRecordGroupIds = useRecoilComponentValueV2(
visibleRecordGroupIdsComponentSelector,
);
if (isRecordTableInitialLoading && tableAllRowIds.length === 0) { if (isRecordTableInitialLoading && allRowIds.length === 0) {
return <RecordTableBodyLoading />; return <RecordTableBodyLoading />;
} }
@ -34,10 +30,10 @@ export const RecordTableRecordGroupsBody = ({
<RecordTableBodyDragDropContext> <RecordTableBodyDragDropContext>
<RecordTableBodyDroppable> <RecordTableBodyDroppable>
<RecordTablePendingRow /> <RecordTablePendingRow />
{visibleRecordGroups.map((recordGroupDefinition) => ( {visibleRecordGroupIds.map((recordGroupId) => (
<RecordGroupContext.Provider <RecordGroupContext.Provider
key={recordGroupDefinition.id} key={recordGroupId}
value={{ recordGroupId: recordGroupDefinition.id }} value={{ recordGroupId }}
> >
<RecordTableRecordGroupRows /> <RecordTableRecordGroupRows />
</RecordGroupContext.Provider> </RecordGroupContext.Provider>

View File

@ -1,7 +1,7 @@
import { selectedRowIdsComponentSelector } from '@/object-record/record-table/states/selectors/selectedRowIdsComponentSelector'; import { selectedRowIdsComponentSelector } from '@/object-record/record-table/states/selectors/selectedRowIdsComponentSelector';
import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState';
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState';
import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2';
import { AllRowsSelectedStatus } from '../../types/AllRowSelectedStatus'; import { AllRowsSelectedStatus } from '../../types/AllRowSelectedStatus';
@ -12,8 +12,9 @@ export const allRowsSelectedStatusComponentSelector =
get: get:
({ instanceId }) => ({ instanceId }) =>
({ get }) => { ({ get }) => {
const tableRowIds = get( const allRowIds = get(
tableAllRowIdsComponentState.atomFamily({ // TODO: Working because instanceId is the same, but we're not in the same context, should be changed !
recordIndexAllRowIdsComponentState.atomFamily({
instanceId, instanceId,
}), }),
); );
@ -29,7 +30,7 @@ export const allRowsSelectedStatusComponentSelector =
const allRowsSelectedStatus = const allRowsSelectedStatus =
numberOfSelectedRows === 0 numberOfSelectedRows === 0
? 'none' ? 'none'
: selectedRowIds.length === tableRowIds.length : selectedRowIds.length === allRowIds.length
? 'all' ? 'all'
: 'some'; : 'some';

View File

@ -1,6 +1,6 @@
import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState';
import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState';
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState';
import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2';
export const selectedRowIdsComponentSelector = createComponentSelectorV2< export const selectedRowIdsComponentSelector = createComponentSelectorV2<
@ -11,13 +11,14 @@ export const selectedRowIdsComponentSelector = createComponentSelectorV2<
get: get:
({ instanceId }) => ({ instanceId }) =>
({ get }) => { ({ get }) => {
const rowIds = get( const allRowIds = get(
tableAllRowIdsComponentState.atomFamily({ // TODO: Working because instanceId is the same, but we're not in the same context, should be changed !
recordIndexAllRowIdsComponentState.atomFamily({
instanceId, instanceId,
}), }),
); );
return rowIds.filter( return allRowIds.filter(
(rowId) => (rowId) =>
get( get(
isRowSelectedComponentFamilyState.atomFamily({ isRowSelectedComponentFamilyState.atomFamily({

View File

@ -1,6 +1,6 @@
import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState';
import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState';
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState';
import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2';
export const unselectedRowIdsComponentSelector = createComponentSelectorV2< export const unselectedRowIdsComponentSelector = createComponentSelectorV2<
@ -12,7 +12,8 @@ export const unselectedRowIdsComponentSelector = createComponentSelectorV2<
({ instanceId }) => ({ instanceId }) =>
({ get }) => { ({ get }) => {
const rowIds = get( const rowIds = get(
tableAllRowIdsComponentState.atomFamily({ // TODO: Working because instanceId is the same, but we're not in the same context, should be changed !
recordIndexAllRowIdsComponentState.atomFamily({
instanceId, instanceId,
}), }),
); );

View File

@ -1,8 +0,0 @@
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
export const tableAllRowIdsComponentState = createComponentStateV2<string[]>({
key: 'tableAllRowIdsComponentState',
defaultValue: [],
componentInstanceContext: RecordTableComponentInstanceContext,
});

View File

@ -0,0 +1,30 @@
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { ComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/types/ComponentFamilyStateV2';
import { globalComponentInstanceContextMap } from '@/ui/utilities/state/component-state/utils/globalComponentInstanceContextMap';
import { SerializableParam, useRecoilState } from 'recoil';
export const useRecoilComponentFamilyStateV2 = <
StateType,
FamilyKey extends SerializableParam,
>(
componentState: ComponentFamilyStateV2<StateType, FamilyKey>,
familyKey: FamilyKey,
instanceIdFromProps?: string,
) => {
const componentInstanceContext = globalComponentInstanceContextMap.get(
componentState.key,
);
if (!componentInstanceContext) {
throw new Error(
`Instance context for key "${componentState.key}" is not defined`,
);
}
const instanceId = useAvailableComponentInstanceIdOrThrow(
componentInstanceContext,
instanceIdFromProps,
);
return useRecoilState(componentState.atomFamily({ instanceId, familyKey }));
};

View File

@ -20,6 +20,56 @@ export const useSaveCurrentViewGroups = (viewBarComponentId?: string) => {
viewBarComponentId, viewBarComponentId,
); );
const saveViewGroup = useRecoilCallback(
({ snapshot }) =>
async (viewGroupToSave: ViewGroup) => {
const currentViewId = snapshot
.getLoadable(currentViewIdCallbackState)
.getValue();
if (!currentViewId) {
return;
}
const view = await getViewFromCache(currentViewId);
if (isUndefinedOrNull(view)) {
return;
}
const currentViewGroups = view.viewGroups;
const existingField = currentViewGroups.find(
(currentViewGroup) =>
currentViewGroup.fieldValue === viewGroupToSave.fieldValue,
);
if (isUndefinedOrNull(existingField)) {
return;
}
if (
isDeeplyEqual(
{
position: existingField.position,
isVisible: existingField.isVisible,
},
{
position: viewGroupToSave.position,
isVisible: viewGroupToSave.isVisible,
},
)
) {
return;
}
await updateViewGroupRecords([
{ ...viewGroupToSave, id: existingField.id },
]);
},
[currentViewIdCallbackState, getViewFromCache, updateViewGroupRecords],
);
const saveViewGroups = useRecoilCallback( const saveViewGroups = useRecoilCallback(
({ snapshot }) => ({ snapshot }) =>
async (viewGroupsToSave: ViewGroup[]) => { async (viewGroupsToSave: ViewGroup[]) => {
@ -91,6 +141,7 @@ export const useSaveCurrentViewGroups = (viewBarComponentId?: string) => {
); );
return { return {
saveViewGroup,
saveViewGroups, saveViewGroups,
}; };
}; };

View File

@ -1,17 +1,9 @@
import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition';
import { ViewGroup } from '@/views/types/ViewGroup'; import { ViewGroup } from '@/views/types/ViewGroup';
import { recordGroupDefinitionToViewGroup } from '@/views/utils/recordGroupDefinitionToViewGroup';
export const mapRecordGroupDefinitionsToViewGroups = ( export const mapRecordGroupDefinitionsToViewGroups = (
groupDefinitions: RecordGroupDefinition[], groupDefinitions: RecordGroupDefinition[],
): ViewGroup[] => { ): ViewGroup[] => {
return groupDefinitions.map( return groupDefinitions.map(recordGroupDefinitionToViewGroup);
(groupDefinition): ViewGroup => ({
__typename: 'ViewGroup',
id: groupDefinition.id,
fieldMetadataId: groupDefinition.fieldMetadataId,
position: groupDefinition.position,
isVisible: groupDefinition.isVisible ?? true,
fieldValue: groupDefinition.value ?? '',
}),
);
}; };

View File

@ -1,3 +1,4 @@
import { v4 } from 'uuid';
import { isDefined } from '~/utils/isDefined'; import { isDefined } from '~/utils/isDefined';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
@ -42,16 +43,7 @@ export const mapViewGroupsToRecordGroupDefinitions = ({
); );
if (!selectedOption) { if (!selectedOption) {
return { return null;
id: 'no-value',
title: 'No Value',
type: RecordGroupDefinitionType.NoValue,
value: null,
position: viewGroup.position,
isVisible: viewGroup.isVisible,
fieldMetadataId: selectFieldMetadataItem.id,
color: 'transparent',
} satisfies RecordGroupDefinition;
} }
return { return {
@ -65,8 +57,32 @@ export const mapViewGroupsToRecordGroupDefinitions = ({
isVisible: viewGroup.isVisible, isVisible: viewGroup.isVisible,
} as RecordGroupDefinition; } as RecordGroupDefinition;
}) })
.filter(isDefined) .filter(isDefined);
.sort((a, b) => a.position - b.position);
return recordGroupDefinitionsFromViewGroups; if (selectFieldMetadataItem.isNullable === true) {
const viewGroup = viewGroups.find(
(viewGroup) => viewGroup.fieldValue === '',
);
const noValueColumn = {
id: viewGroup?.id ?? v4(),
title: 'No Value',
type: RecordGroupDefinitionType.NoValue,
value: null,
position:
viewGroup?.position ??
recordGroupDefinitionsFromViewGroups
.map((option) => option.position)
.reduce((a, b) => Math.max(a, b), 0) + 1,
isVisible: viewGroup?.isVisible ?? true,
fieldMetadataId: selectFieldMetadataItem.id,
color: 'transparent',
} satisfies RecordGroupDefinition;
return [...recordGroupDefinitionsFromViewGroups, noValueColumn];
}
return recordGroupDefinitionsFromViewGroups.sort(
(a, b) => a.position - b.position,
);
}; };

View File

@ -0,0 +1,15 @@
import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition';
import { ViewGroup } from '@/views/types/ViewGroup';
export const recordGroupDefinitionToViewGroup = (
recordGroup: RecordGroupDefinition,
): ViewGroup => {
return {
__typename: 'ViewGroup',
id: recordGroup.id,
fieldMetadataId: recordGroup.fieldMetadataId,
position: recordGroup.position,
isVisible: recordGroup.isVisible ?? true,
fieldValue: recordGroup.value ?? '',
};
};