From 9e7b90a551f071437db75306e66d12fa29b68828 Mon Sep 17 00:00:00 2001 From: Harshit Singh <73997189+harshit078@users.noreply.github.com> Date: Fri, 15 Nov 2024 21:45:39 +0530 Subject: [PATCH] fix: Added keyboard functionality for esc and Ctrl + a (#8282) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description - This PR fixes #8247 #8206 - Adds keyboard functionality for esc (deselect all records) and Ctrl + a (select records) ## Changes https://github.com/user-attachments/assets/7b2718d6-dbe5-4cd8-8ead-b6baed1fc357 Merged during stream 🎉 --------- Co-authored-by: Devessier --- .../record-board/components/RecordBoard.tsx | 25 +++++++++++++++++- .../hooks/internal/useRecordBoardStates.ts | 5 ++++ ...ecordBoardAllRecordIdsComponentSelector.ts | 26 +++++++++++++++++++ .../components/RecordTableWithWrappers.tsx | 23 ++++++++++++++++ .../drag-select/components/DragSelect.tsx | 2 ++ 5 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 packages/twenty-front/src/modules/object-record/record-board/states/selectors/recordBoardAllRecordIdsComponentSelector.ts diff --git a/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoard.tsx b/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoard.tsx index 34f70dffcaba..4c5076d9f8af 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoard.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoard.tsx @@ -4,6 +4,7 @@ import { useContext, useRef } from 'react'; import { useRecoilCallback, useRecoilValue } from 'recoil'; import { Key } from 'ts-key-enum'; +import { ActionBarHotkeyScope } from '@/action-menu/types/ActionBarHotKeyScope'; import { RecordBoardHeader } from '@/object-record/record-board/components/RecordBoardHeader'; import { RecordBoardStickyHeaderEffect } from '@/object-record/record-board/components/RecordBoardStickyHeaderEffect'; import { RECORD_BOARD_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-board/constants/RecordBoardClickOutsideListenerId'; @@ -61,6 +62,7 @@ export const RecordBoard = () => { columnIdsState, columnsFamilySelector, recordIdsByColumnIdFamilyState, + allRecordIdsSelector, } = useRecordBoardStates(recordBoardId); const columnIds = useRecoilValue(columnIdsState); @@ -80,7 +82,28 @@ export const RecordBoard = () => { callback: resetRecordSelection, }); - useScopedHotkeys([Key.Escape], resetRecordSelection, TableHotkeyScope.Table); + const selectAll = useRecoilCallback( + ({ snapshot }) => + () => { + const allRecordIds = snapshot + .getLoadable(allRecordIdsSelector()) + .getValue(); + + for (const recordId of allRecordIds) { + setRecordAsSelected(recordId, true); + } + }, + [allRecordIdsSelector, setRecordAsSelected], + ); + + useScopedHotkeys('ctrl+a,meta+a', selectAll, TableHotkeyScope.Table); + useScopedHotkeys('ctrl+a,meta+a', selectAll, ActionBarHotkeyScope.ActionBar); + + useScopedHotkeys( + Key.Escape, + resetRecordSelection, + ActionBarHotkeyScope.ActionBar, + ); const handleDragEnd: OnDragEndResponder = useRecoilCallback( ({ snapshot }) => diff --git a/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useRecordBoardStates.ts b/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useRecordBoardStates.ts index 65428a0197ca..a26506254b67 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useRecordBoardStates.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useRecordBoardStates.ts @@ -11,6 +11,7 @@ import { recordBoardObjectSingularNameComponentState } from '@/object-record/rec 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'; @@ -76,6 +77,10 @@ export const useRecordBoardStates = (recordBoardId?: string) => { isRecordBoardCardSelectedComponentFamilyState, scopeId, ), + allRecordIdsSelector: extractComponentReadOnlySelector( + recordBoardAllRecordIdsComponentSelector, + scopeId, + ), selectedRecordIdsSelector: extractComponentReadOnlySelector( recordBoardSelectedRecordIdsComponentSelector, scopeId, diff --git a/packages/twenty-front/src/modules/object-record/record-board/states/selectors/recordBoardAllRecordIdsComponentSelector.ts b/packages/twenty-front/src/modules/object-record/record-board/states/selectors/recordBoardAllRecordIdsComponentSelector.ts new file mode 100644 index 000000000000..154da9313fa2 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-board/states/selectors/recordBoardAllRecordIdsComponentSelector.ts @@ -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({ + 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; + }, + }); diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableWithWrappers.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableWithWrappers.tsx index d2a82737d08f..3dd91a2f5fdc 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableWithWrappers.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableWithWrappers.tsx @@ -11,6 +11,12 @@ import { useSaveCurrentViewFields } from '@/views/hooks/useSaveCurrentViewFields import { mapColumnDefinitionsToViewFields } from '@/views/utils/mapColumnDefinitionToViewField'; 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` height: 100%; @@ -40,6 +46,23 @@ export const RecordTableWithWrappers = ({ recordTableId, viewBarId, }: RecordTableWithWrappersProps) => { + const { resetTableRowSelection, selectAllRows } = useRecordTable({ + recordTableId, + }); + + useScopedHotkeys('ctrl+a,meta+a', selectAllRows, TableHotkeyScope.Table); + useScopedHotkeys( + 'ctrl+a,meta+a', + selectAllRows, + ActionBarHotkeyScope.ActionBar, + ); + + useScopedHotkeys( + Key.Escape, + resetTableRowSelection, + ActionBarHotkeyScope.ActionBar, + ); + const { saveViewFields } = useSaveCurrentViewFields(viewBarId); const { deleteOneRecord } = useDeleteOneRecord({ objectNameSingular }); diff --git a/packages/twenty-front/src/modules/ui/utilities/drag-select/components/DragSelect.tsx b/packages/twenty-front/src/modules/ui/utilities/drag-select/components/DragSelect.tsx index c298b3e8e1bf..5ed5bd32f294 100644 --- a/packages/twenty-front/src/modules/ui/utilities/drag-select/components/DragSelect.tsx +++ b/packages/twenty-front/src/modules/ui/utilities/drag-select/components/DragSelect.tsx @@ -22,7 +22,9 @@ export const DragSelect = ({ onDragSelectionEnd, }: DragSelectProps) => { const theme = useTheme(); + const { isDragSelectionStartEnabled } = useDragSelect(); + const { DragSelection } = useSelectionContainer({ shouldStartSelecting: (target) => { if (!isDragSelectionStartEnabled()) {