-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix bug reoder on table in view groups mode #8894
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,15 @@ | ||
import { DragDropContext, DropResult } from '@hello-pangea/dnd'; | ||
import { ReactNode, useContext } from 'react'; | ||
import { useSetRecoilState } from 'recoil'; | ||
import { useRecoilCallback, useSetRecoilState } from 'recoil'; | ||
|
||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; | ||
import { getDraggedRecordPosition } from '@/object-record/record-board/utils/getDraggedRecordPosition'; | ||
import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; | ||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; | ||
import { RecordTableContext } from '@/object-record/record-table/contexts/RecordTableContext'; | ||
import { useComputeNewRowPosition } from '@/object-record/record-table/hooks/useComputeNewRowPosition'; | ||
import { isRemoveSortingModalOpenState } from '@/object-record/record-table/states/isRemoveSortingModalOpenState'; | ||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; | ||
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; | ||
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; | ||
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; | ||
import { isDefined } from '~/utils/isDefined'; | ||
|
||
|
@@ -22,7 +24,7 @@ export const RecordTableBodyDragDropContextProvider = ({ | |
objectNameSingular, | ||
}); | ||
|
||
const allRecordIds = useRecoilComponentValueV2( | ||
const recordIndexAllRecordIdsSelector = useRecoilComponentCallbackStateV2( | ||
recordIndexAllRecordIdsComponentSelector, | ||
); | ||
|
||
|
@@ -35,27 +37,75 @@ export const RecordTableBodyDragDropContextProvider = ({ | |
isRemoveSortingModalOpenState, | ||
); | ||
|
||
const computeNewRowPosition = useComputeNewRowPosition(); | ||
const handleDragEnd = useRecoilCallback( | ||
({ snapshot }) => | ||
(result: DropResult) => { | ||
if (viewSorts.length > 0) { | ||
setIsRemoveSortingModalOpenState(true); | ||
return; | ||
} | ||
|
||
const handleDragEnd = (result: DropResult) => { | ||
if (viewSorts.length > 0) { | ||
setIsRemoveSortingModalOpenState(true); | ||
return; | ||
} | ||
if (!isDefined(result.destination)) { | ||
throw new Error('Drop Destination is not defined'); | ||
} | ||
|
||
const computeResult = computeNewRowPosition(result, allRecordIds); | ||
const allRecordIds = getSnapshotValue( | ||
snapshot, | ||
recordIndexAllRecordIdsSelector, | ||
); | ||
|
||
if (!isDefined(computeResult)) { | ||
return; | ||
} | ||
const isSourceIndexBeforeDestinationIndex = | ||
result.source.index < result.destination.index; | ||
|
||
updateOneRow({ | ||
idToUpdate: computeResult.draggedRecordId, | ||
updateOneRecordInput: { | ||
position: computeResult.newPosition, | ||
const recordBeforeDestinationId = | ||
allRecordIds[ | ||
isSourceIndexBeforeDestinationIndex | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is subtle: if the previous record is before or after, we need to shift the records before and after by 1 |
||
? result.destination.index | ||
: result.destination.index - 1 | ||
]; | ||
|
||
const recordBeforeDestination = recordBeforeDestinationId | ||
? snapshot | ||
.getLoadable(recordStoreFamilyState(recordBeforeDestinationId)) | ||
.getValue() | ||
: null; | ||
|
||
const recordAfterDestinationId = | ||
allRecordIds[ | ||
isSourceIndexBeforeDestinationIndex | ||
? result.destination.index + 1 | ||
: result.destination.index | ||
]; | ||
|
||
const recordAfterDestination = recordAfterDestinationId | ||
? snapshot | ||
.getLoadable(recordStoreFamilyState(recordAfterDestinationId)) | ||
.getValue() | ||
: null; | ||
|
||
const newPosition = getDraggedRecordPosition( | ||
recordBeforeDestination?.position, | ||
recordAfterDestination?.position, | ||
); | ||
|
||
if (!isDefined(newPosition)) { | ||
return; | ||
} | ||
|
||
updateOneRow({ | ||
idToUpdate: result.draggableId, | ||
updateOneRecordInput: { | ||
position: newPosition, | ||
}, | ||
}); | ||
}, | ||
}); | ||
}; | ||
[ | ||
recordIndexAllRecordIdsSelector, | ||
setIsRemoveSortingModalOpenState, | ||
updateOneRow, | ||
viewSorts.length, | ||
], | ||
); | ||
|
||
return ( | ||
<DragDropContext onDragEnd={handleDragEnd}>{children}</DragDropContext> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,10 +3,11 @@ import { ReactNode, useContext } from 'react'; | |
import { useRecoilCallback, useSetRecoilState } from 'recoil'; | ||
|
||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; | ||
import { getDraggedRecordPosition } from '@/object-record/record-board/utils/getDraggedRecordPosition'; | ||
import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState'; | ||
import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; | ||
import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState'; | ||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; | ||
import { RecordTableContext } from '@/object-record/record-table/contexts/RecordTableContext'; | ||
import { useComputeNewRowPosition } from '@/object-record/record-table/hooks/useComputeNewRowPosition'; | ||
import { isRemoveSortingModalOpenState } from '@/object-record/record-table/states/isRemoveSortingModalOpenState'; | ||
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; | ||
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; | ||
|
@@ -25,10 +26,6 @@ export const RecordTableBodyRecordGroupDragDropContextProvider = ({ | |
objectNameSingular, | ||
}); | ||
|
||
const recordIndexAllRecordIdsSelector = useRecoilComponentCallbackStateV2( | ||
recordIndexAllRecordIdsComponentSelector, | ||
); | ||
|
||
const { currentViewWithCombinedFiltersAndSorts } = | ||
useGetCurrentView(recordTableId); | ||
|
||
|
@@ -38,33 +35,34 @@ export const RecordTableBodyRecordGroupDragDropContextProvider = ({ | |
isRemoveSortingModalOpenState, | ||
); | ||
|
||
const computeNewRowPosition = useComputeNewRowPosition(); | ||
const recordIdsByGroupFamilyState = useRecoilComponentCallbackStateV2( | ||
recordIndexRecordIdsByGroupComponentFamilyState, | ||
); | ||
|
||
const handleDragEnd = useRecoilCallback( | ||
({ snapshot }) => | ||
(result: DropResult) => { | ||
const tableAllRecordIds = getSnapshotValue( | ||
snapshot, | ||
recordIndexAllRecordIdsSelector, | ||
); | ||
const destinationRecordGroupId = result.destination?.droppableId; | ||
|
||
const recordGroupId = result.destination?.droppableId; | ||
if (!isDefined(result.destination)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same logic but in case of viewGroups |
||
throw new Error('Drop Destination is not defined'); | ||
} | ||
|
||
if (!isDefined(recordGroupId)) { | ||
if (!isDefined(destinationRecordGroupId)) { | ||
throw new Error('Record group id is not defined'); | ||
} | ||
Comment on lines
+51
to
53
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: Throwing an error here might be too aggressive - consider gracefully handling the case where drag operation is cancelled |
||
|
||
const recordGroup = getSnapshotValue( | ||
const destinationRecordGroup = getSnapshotValue( | ||
snapshot, | ||
recordGroupDefinitionFamilyState(recordGroupId), | ||
recordGroupDefinitionFamilyState(destinationRecordGroupId), | ||
); | ||
|
||
if (!isDefined(recordGroup)) { | ||
if (!isDefined(destinationRecordGroup)) { | ||
throw new Error('Record group is not defined'); | ||
} | ||
|
||
const fieldMetadata = objectMetadataItem.fields.find( | ||
(field) => field.id === recordGroup.fieldMetadataId, | ||
(field) => field.id === destinationRecordGroup.fieldMetadataId, | ||
); | ||
|
||
if (!isDefined(fieldMetadata)) { | ||
|
@@ -76,25 +74,62 @@ export const RecordTableBodyRecordGroupDragDropContextProvider = ({ | |
return; | ||
} | ||
|
||
const computeResult = computeNewRowPosition(result, tableAllRecordIds); | ||
const isSourceIndexBeforeDestinationIndexInSameGroup = | ||
result.source.index < result.destination.index && | ||
result.source.droppableId === result.destination.droppableId; | ||
|
||
const destinationGroupRecordIds = getSnapshotValue( | ||
snapshot, | ||
recordIdsByGroupFamilyState(destinationRecordGroupId), | ||
); | ||
|
||
const recordBeforeDestinationId = | ||
destinationGroupRecordIds[ | ||
isSourceIndexBeforeDestinationIndexInSameGroup | ||
? result.destination.index | ||
: result.destination.index - 1 | ||
]; | ||
|
||
const recordBeforeDestination = recordBeforeDestinationId | ||
? snapshot | ||
.getLoadable(recordStoreFamilyState(recordBeforeDestinationId)) | ||
.getValue() | ||
: null; | ||
|
||
const recordAfterDestinationId = | ||
destinationGroupRecordIds[ | ||
isSourceIndexBeforeDestinationIndexInSameGroup | ||
? result.destination.index + 1 | ||
: result.destination.index | ||
]; | ||
|
||
const recordAfterDestination = recordAfterDestinationId | ||
? snapshot | ||
.getLoadable(recordStoreFamilyState(recordAfterDestinationId)) | ||
.getValue() | ||
: null; | ||
|
||
const newPosition = getDraggedRecordPosition( | ||
recordBeforeDestination?.position, | ||
recordAfterDestination?.position, | ||
); | ||
|
||
if (!isDefined(computeResult)) { | ||
if (!isDefined(newPosition)) { | ||
return; | ||
} | ||
|
||
updateOneRow({ | ||
idToUpdate: computeResult.draggedRecordId, | ||
idToUpdate: result.draggableId, | ||
updateOneRecordInput: { | ||
position: computeResult.newPosition, | ||
[fieldMetadata.name]: recordGroup.value, | ||
position: newPosition, | ||
[fieldMetadata.name]: destinationRecordGroup.value, | ||
}, | ||
}); | ||
}, | ||
[ | ||
recordIndexAllRecordIdsSelector, | ||
objectMetadataItem.fields, | ||
viewSorts.length, | ||
computeNewRowPosition, | ||
recordIdsByGroupFamilyState, | ||
updateOneRow, | ||
setIsRemoveSortingModalOpenState, | ||
], | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm introducing two rowIndexes:
Right now the index used in focus / cell opening is handled at table level so it's a global index, the index use drag is at group level. I think long term we want to have the index for focus / cell opening at group level too and handle the jump between groups properly
I prefer introducing these two naming ForFocus and ForDrag because it's clear when to use which one in underlying components. Alternatively I could have introduced rowIndexInGroup and rowIndexInTable but this will make underlying components know about recordGroups and make if conditions which I think we should avoid to reduce complexity