mirror of
				https://github.com/lingble/twenty.git
				synced 2025-10-30 20:27:55 +00:00 
			
		
		
		
	refactor: rely on recoil and scoped hotkeys to select all items and reset selection
This commit is contained in:
		| @@ -61,6 +61,7 @@ export const RecordBoard = () => { | |||||||
|     columnIdsState, |     columnIdsState, | ||||||
|     columnsFamilySelector, |     columnsFamilySelector, | ||||||
|     recordIdsByColumnIdFamilyState, |     recordIdsByColumnIdFamilyState, | ||||||
|  |     allRecordIdsSelector, | ||||||
|   } = useRecordBoardStates(recordBoardId); |   } = useRecordBoardStates(recordBoardId); | ||||||
|  |  | ||||||
|   const columnIds = useRecoilValue(columnIdsState); |   const columnIds = useRecoilValue(columnIdsState); | ||||||
| @@ -80,6 +81,20 @@ export const RecordBoard = () => { | |||||||
|     callback: resetRecordSelection, |     callback: resetRecordSelection, | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|  |   const selectAll = useRecoilCallback( | ||||||
|  |     ({ snapshot }) => | ||||||
|  |       () => { | ||||||
|  |         const allRecordIds = snapshot | ||||||
|  |           .getLoadable(allRecordIdsSelector()) | ||||||
|  |           .getValue(); | ||||||
|  |  | ||||||
|  |         for (const recordId of allRecordIds) { | ||||||
|  |           setRecordAsSelected(recordId, true); | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |     [allRecordIdsSelector, setRecordAsSelected], | ||||||
|  |   ); | ||||||
|  |  | ||||||
|   useScopedHotkeys([Key.Escape], resetRecordSelection, TableHotkeyScope.Table); |   useScopedHotkeys([Key.Escape], resetRecordSelection, TableHotkeyScope.Table); | ||||||
|  |  | ||||||
|   const handleDragEnd: OnDragEndResponder = useRecoilCallback( |   const handleDragEnd: OnDragEndResponder = useRecoilCallback( | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ import { recordBoardObjectSingularNameComponentState } from '@/object-record/rec | |||||||
| import { recordBoardRecordIdsByColumnIdComponentFamilyState } from '@/object-record/record-board/states/recordBoardRecordIdsByColumnIdComponentFamilyState'; | import { recordBoardRecordIdsByColumnIdComponentFamilyState } from '@/object-record/record-board/states/recordBoardRecordIdsByColumnIdComponentFamilyState'; | ||||||
| import { recordBoardShouldFetchMoreInColumnComponentFamilyState } from '@/object-record/record-board/states/recordBoardShouldFetchMoreInColumnComponentFamilyState'; | import { recordBoardShouldFetchMoreInColumnComponentFamilyState } from '@/object-record/record-board/states/recordBoardShouldFetchMoreInColumnComponentFamilyState'; | ||||||
| import { recordBoardSortsComponentState } from '@/object-record/record-board/states/recordBoardSortsComponentState'; | 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 { recordBoardColumnsComponentFamilySelector } from '@/object-record/record-board/states/selectors/recordBoardColumnsComponentFamilySelector'; | ||||||
| import { recordBoardSelectedRecordIdsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardSelectedRecordIdsComponentSelector'; | import { recordBoardSelectedRecordIdsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardSelectedRecordIdsComponentSelector'; | ||||||
| import { recordBoardShouldFetchMoreComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardShouldFetchMoreComponentFamilySelector'; | import { recordBoardShouldFetchMoreComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardShouldFetchMoreComponentFamilySelector'; | ||||||
| @@ -76,6 +77,10 @@ export const useRecordBoardStates = (recordBoardId?: string) => { | |||||||
|       isRecordBoardCardSelectedComponentFamilyState, |       isRecordBoardCardSelectedComponentFamilyState, | ||||||
|       scopeId, |       scopeId, | ||||||
|     ), |     ), | ||||||
|  |     allRecordIdsSelector: extractComponentReadOnlySelector( | ||||||
|  |       recordBoardAllRecordIdsComponentSelector, | ||||||
|  |       scopeId, | ||||||
|  |     ), | ||||||
|     selectedRecordIdsSelector: extractComponentReadOnlySelector( |     selectedRecordIdsSelector: extractComponentReadOnlySelector( | ||||||
|       recordBoardSelectedRecordIdsComponentSelector, |       recordBoardSelectedRecordIdsComponentSelector, | ||||||
|       scopeId, |       scopeId, | ||||||
|   | |||||||
| @@ -0,0 +1,26 @@ | |||||||
|  | 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; | ||||||
|  |       }, | ||||||
|  |   }); | ||||||
| @@ -11,6 +11,12 @@ import { useSaveCurrentViewFields } from '@/views/hooks/useSaveCurrentViewFields | |||||||
| import { mapColumnDefinitionsToViewFields } from '@/views/utils/mapColumnDefinitionToViewField'; | import { mapColumnDefinitionsToViewFields } from '@/views/utils/mapColumnDefinitionToViewField'; | ||||||
|  |  | ||||||
| import { RecordUpdateContext } from '../contexts/EntityUpdateMutationHookContext'; | import { RecordUpdateContext } from '../contexts/EntityUpdateMutationHookContext'; | ||||||
|  | import { useRecordTable } from '../hooks/useRecordTable'; | ||||||
|  |  | ||||||
|  | import { ActionBarHotkeyScope } from '@/action-menu/types/ActionBarHotKeyScope'; | ||||||
|  | import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope'; | ||||||
|  | import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; | ||||||
|  | import { Key } from 'ts-key-enum'; | ||||||
|  |  | ||||||
| const StyledTableWithHeader = styled.div` | const StyledTableWithHeader = styled.div` | ||||||
|   height: 100%; |   height: 100%; | ||||||
| @@ -40,6 +46,23 @@ export const RecordTableWithWrappers = ({ | |||||||
|   recordTableId, |   recordTableId, | ||||||
|   viewBarId, |   viewBarId, | ||||||
| }: RecordTableWithWrappersProps) => { | }: RecordTableWithWrappersProps) => { | ||||||
|  |   const { resetTableRowSelection, selectAllRows } = useRecordTable({ | ||||||
|  |     recordTableId, | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   useScopedHotkeys( | ||||||
|  |     Key.Escape, | ||||||
|  |     resetTableRowSelection, | ||||||
|  |     ActionBarHotkeyScope.ActionBar, | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   useScopedHotkeys('ctrl+a,meta+a', selectAllRows, TableHotkeyScope.Table); | ||||||
|  |   useScopedHotkeys( | ||||||
|  |     'ctrl+a,meta+a', | ||||||
|  |     selectAllRows, | ||||||
|  |     TableHotkeyScope.TableSoftFocus, | ||||||
|  |   ); | ||||||
|  |  | ||||||
|   const { saveViewFields } = useSaveCurrentViewFields(viewBarId); |   const { saveViewFields } = useSaveCurrentViewFields(viewBarId); | ||||||
|  |  | ||||||
|   const { deleteOneRecord } = useDeleteOneRecord({ objectNameSingular }); |   const { deleteOneRecord } = useDeleteOneRecord({ objectNameSingular }); | ||||||
|   | |||||||
| @@ -1,4 +1,3 @@ | |||||||
| import { RefObject, useCallback, useEffect } from 'react'; |  | ||||||
| import { | import { | ||||||
|   boxesIntersect, |   boxesIntersect, | ||||||
|   useSelectionContainer, |   useSelectionContainer, | ||||||
| @@ -22,53 +21,10 @@ export const DragSelect = ({ | |||||||
|   onDragSelectionStart, |   onDragSelectionStart, | ||||||
|   onDragSelectionEnd, |   onDragSelectionEnd, | ||||||
| }: DragSelectProps) => { | }: DragSelectProps) => { | ||||||
|   const handleKeyDown = useCallback( |  | ||||||
|     (event: KeyboardEvent) => { |  | ||||||
|       if (event.key === 'a' && (event.ctrlKey || event.metaKey)) { |  | ||||||
|         event.preventDefault(); |  | ||||||
|         const items = dragSelectable.current?.querySelectorAll<HTMLElement>( |  | ||||||
|           '[data-selectable-id]', |  | ||||||
|         ); |  | ||||||
|         if (!items) return; |  | ||||||
|  |  | ||||||
|         queueMicrotask(() => { |  | ||||||
|           items.forEach((item) => { |  | ||||||
|             const id = item.getAttribute('data-selectable-id'); |  | ||||||
|             if (id !== null) { |  | ||||||
|               onDragSelectionChange(id, true); |  | ||||||
|             } |  | ||||||
|           }); |  | ||||||
|         }); |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
|       if (event.key === 'Escape') { |  | ||||||
|         event.preventDefault(); |  | ||||||
|  |  | ||||||
|         const items = dragSelectable.current?.querySelectorAll<HTMLElement>( |  | ||||||
|           '[data-selectable-id]', |  | ||||||
|         ); |  | ||||||
|         if (!items) return; |  | ||||||
|  |  | ||||||
|         queueMicrotask(() => { |  | ||||||
|           items.forEach((item) => { |  | ||||||
|             const id = item.getAttribute('data-selectable-id'); |  | ||||||
|             if (id !== null) { |  | ||||||
|               onDragSelectionChange(id, false); |  | ||||||
|             } |  | ||||||
|           }); |  | ||||||
|         }); |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     [dragSelectable, onDragSelectionChange], |  | ||||||
|   ); |  | ||||||
|  |  | ||||||
|   useEffect(() => { |  | ||||||
|     document.addEventListener('keydown', handleKeyDown); |  | ||||||
|     return () => document.removeEventListener('keydown', handleKeyDown); |  | ||||||
|   }, [handleKeyDown]); |  | ||||||
|  |  | ||||||
|   const theme = useTheme(); |   const theme = useTheme(); | ||||||
|  |  | ||||||
|   const { isDragSelectionStartEnabled } = useDragSelect(); |   const { isDragSelectionStartEnabled } = useDragSelect(); | ||||||
|  |  | ||||||
|   const { DragSelection } = useSelectionContainer({ |   const { DragSelection } = useSelectionContainer({ | ||||||
|     shouldStartSelecting: (target) => { |     shouldStartSelecting: (target) => { | ||||||
|       if (!isDragSelectionStartEnabled()) { |       if (!isDragSelectionStartEnabled()) { | ||||||
|   | |||||||
| @@ -88,6 +88,8 @@ export const useSetHotkeyScope = () => | |||||||
|  |  | ||||||
|         set(internalHotkeysEnabledScopesState, scopesToSet); |         set(internalHotkeysEnabledScopesState, scopesToSet); | ||||||
|         set(currentHotkeyScopeState, newHotkeyScope); |         set(currentHotkeyScopeState, newHotkeyScope); | ||||||
|  |  | ||||||
|  |         console.log(scopesToSet); | ||||||
|       }, |       }, | ||||||
|     [], |     [], | ||||||
|   ); |   ); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Devessier
					Devessier