Feat/better hotkeys scope (#526)

* Working version

* fix

* Fixed console log

* Fix lint

* wip

* Fix

* Fix

* consolelog

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Lucas Bordeau
2023-07-08 03:53:05 +02:00
committed by GitHub
parent 611cda1f41
commit 66dcc9b2e1
77 changed files with 1240 additions and 454 deletions

View File

@ -0,0 +1,21 @@
import { useRecoilCallback } from 'recoil';
import { currentCellInEditModePositionState } from '../states/currentCellInEditModePositionState';
import { isCellInEditModeFamilyState } from '../states/isCellInEditModeFamilyState';
import { isSomeInputInEditModeState } from '../states/isSomeInputInEditModeState';
export function useCloseCurrentCellInEditMode() {
return useRecoilCallback(({ set, snapshot }) => {
return async () => {
const currentCellInEditModePosition = await snapshot.getPromise(
currentCellInEditModePositionState,
);
set(isCellInEditModeFamilyState(currentCellInEditModePosition), false);
await new Promise((resolve) => setTimeout(resolve, 20));
set(isSomeInputInEditModeState, false);
};
}, []);
}

View File

@ -0,0 +1,19 @@
import { useRecoilCallback } from 'recoil';
import { isSoftFocusActiveState } from '../states/isSoftFocusActiveState';
import { isSoftFocusOnCellFamilyState } from '../states/isSoftFocusOnCellFamilyState';
import { softFocusPositionState } from '../states/softFocusPositionState';
export function useDisableSoftFocus() {
return useRecoilCallback(({ set, snapshot }) => {
return () => {
const currentPosition = snapshot
.getLoadable(softFocusPositionState)
.valueOrThrow();
set(isSoftFocusActiveState, false);
set(isSoftFocusOnCellFamilyState(currentPosition), false);
};
}, []);
}

View File

@ -1,11 +1,9 @@
import { useEffect } from 'react';
import { useRecoilState } from 'recoil';
import { TABLE_MIN_COLUMN_NUMBER_BECAUSE_OF_CHECKBOX_COLUMN } from '../constants';
import { entityTableDimensionsState } from '../states/entityTableDimensionsState';
import { useResetTableRowSelection } from './useResetTableRowSelection';
import { useSetSoftFocusPosition } from './useSetSoftFocusPosition';
export type TableDimensions = {
numberOfRows: number;
@ -30,13 +28,4 @@ export function useInitializeEntityTable({
numberOfRows,
});
}, [numberOfRows, numberOfColumns, setTableDimensions]);
const setSoftFocusPosition = useSetSoftFocusPosition();
useEffect(() => {
setSoftFocusPosition({
row: 0,
column: TABLE_MIN_COLUMN_NUMBER_BECAUSE_OF_CHECKBOX_COLUMN,
});
}, [setSoftFocusPosition]);
}

View File

@ -0,0 +1,24 @@
import { useCurrentHotkeysScope } from '@/hotkeys/hooks/useCurrentHotkeysScope';
import { useResetHotkeysScopeStack } from '@/hotkeys/hooks/useResetHotkeysScopeStack';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { useCloseCurrentCellInEditMode } from './useClearCellInEditMode';
import { useDisableSoftFocus } from './useDisableSoftFocus';
export function useLeaveTableFocus() {
const resetHotkeysScopeStack = useResetHotkeysScopeStack();
const currentHotkeysScope = useCurrentHotkeysScope();
const disableSoftFocus = useDisableSoftFocus();
const closeCurrentCellInEditMode = useCloseCurrentCellInEditMode();
return async function leaveTableFocus() {
if (currentHotkeysScope?.scope === InternalHotkeysScope.Table) {
return;
}
closeCurrentCellInEditMode();
disableSoftFocus();
resetHotkeysScopeStack(InternalHotkeysScope.Table);
};
}

View File

@ -1,72 +1,74 @@
import { useHotkeys } from 'react-hotkeys-hook';
import { useRecoilState } from 'recoil';
import { Key } from 'ts-key-enum';
import { useRemoveFromHotkeysScopeStack } from '@/hotkeys/hooks/useRemoveFromHotkeysScopeStack';
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { isSomeInputInEditModeState } from '../states/isSomeInputInEditModeState';
import { useDisableSoftFocus } from './useDisableSoftFocus';
import { useMoveSoftFocus } from './useMoveSoftFocus';
export function useMapKeyboardToSoftFocus() {
const { moveDown, moveLeft, moveRight, moveUp } = useMoveSoftFocus();
const removeFromHotkeysScopedStack = useRemoveFromHotkeysScopeStack();
const disableSoftFocus = useDisableSoftFocus();
const [isSomeInputInEditMode] = useRecoilState(isSomeInputInEditModeState);
useHotkeys(
'up, shift+enter',
useScopedHotkeys(
[Key.ArrowUp, `${Key.Shift}+${Key.Enter}`],
() => {
if (!isSomeInputInEditMode) {
moveUp();
}
},
InternalHotkeysScope.TableSoftFocus,
[moveUp, isSomeInputInEditMode],
{
preventDefault: true,
enableOnContentEditable: true,
enableOnFormTags: true,
},
);
useHotkeys(
'down',
useScopedHotkeys(
Key.ArrowDown,
() => {
if (!isSomeInputInEditMode) {
moveDown();
}
},
InternalHotkeysScope.TableSoftFocus,
[moveDown, isSomeInputInEditMode],
{
preventDefault: true,
enableOnContentEditable: true,
enableOnFormTags: true,
},
);
useHotkeys(
['left', 'shift+tab'],
useScopedHotkeys(
[Key.ArrowLeft, `${Key.Shift}+${Key.Tab}`],
() => {
if (!isSomeInputInEditMode) {
moveLeft();
}
},
InternalHotkeysScope.TableSoftFocus,
[moveLeft, isSomeInputInEditMode],
{
preventDefault: true,
enableOnContentEditable: true,
enableOnFormTags: true,
},
);
useHotkeys(
['right', 'tab'],
useScopedHotkeys(
[Key.ArrowRight, Key.Tab],
() => {
if (!isSomeInputInEditMode) {
moveRight();
}
},
InternalHotkeysScope.TableSoftFocus,
[moveRight, isSomeInputInEditMode],
{
preventDefault: true,
enableOnContentEditable: true,
enableOnFormTags: true,
);
useScopedHotkeys(
[Key.Escape],
() => {
removeFromHotkeysScopedStack(InternalHotkeysScope.TableSoftFocus);
disableSoftFocus();
},
InternalHotkeysScope.TableSoftFocus,
[removeFromHotkeysScopedStack, disableSoftFocus],
);
}

View File

@ -0,0 +1,21 @@
import { useRecoilCallback } from 'recoil';
import { currentCellInEditModePositionState } from '../states/currentCellInEditModePositionState';
import { isCellInEditModeFamilyState } from '../states/isCellInEditModeFamilyState';
import { CellPosition } from '../types/CellPosition';
export function useMoveEditModeToCellPosition() {
return useRecoilCallback(({ set, snapshot }) => {
return (newPosition: CellPosition) => {
const currentCellInEditModePosition = snapshot
.getLoadable(currentCellInEditModePositionState)
.valueOrThrow();
set(isCellInEditModeFamilyState(currentCellInEditModePosition), false);
set(currentCellInEditModePositionState, newPosition);
set(isCellInEditModeFamilyState(newPosition), true);
};
}, []);
}

View File

@ -1,16 +1,19 @@
import { useRecoilCallback } from 'recoil';
import { isSoftFocusActiveState } from '../states/isSoftFocusActiveState';
import { isSoftFocusOnCellFamilyState } from '../states/isSoftFocusOnCellFamilyState';
import { softFocusPositionState } from '../states/softFocusPositionState';
import { TablePosition } from '../types/TablePosition';
import { CellPosition } from '../types/CellPosition';
export function useSetSoftFocusPosition() {
return useRecoilCallback(({ set, snapshot }) => {
return (newPosition: TablePosition) => {
return (newPosition: CellPosition) => {
const currentPosition = snapshot
.getLoadable(softFocusPositionState)
.valueOrThrow();
set(isSoftFocusActiveState, true);
set(isSoftFocusOnCellFamilyState(currentPosition), false);
set(softFocusPositionState, newPosition);

View File

@ -0,0 +1,11 @@
import { atom } from 'recoil';
import { CellPosition } from '../types/CellPosition';
export const currentCellInEditModePositionState = atom<CellPosition>({
key: 'currentCellInEditModePositionState',
default: {
row: 0,
column: 1,
},
});

View File

@ -0,0 +1,8 @@
import { atomFamily } from 'recoil';
import { CellPosition } from '../types/CellPosition';
export const isCellInEditModeFamilyState = atomFamily<boolean, CellPosition>({
key: 'isCellInEditModeFamilyState',
default: false,
});

View File

@ -0,0 +1,6 @@
import { atom } from 'recoil';
export const isSoftFocusActiveState = atom<boolean>({
key: 'isSoftFocusActiveState',
default: false,
});

View File

@ -1,8 +1,8 @@
import { atomFamily } from 'recoil';
import { TablePosition } from '../types/TablePosition';
import { CellPosition } from '../types/CellPosition';
export const isSoftFocusOnCellFamilyState = atomFamily<boolean, TablePosition>({
export const isSoftFocusOnCellFamilyState = atomFamily<boolean, CellPosition>({
key: 'isSoftFocusOnCellFamilyState',
default: false,
});

View File

@ -1,8 +1,8 @@
import { atom } from 'recoil';
import { TablePosition } from '../types/TablePosition';
import { CellPosition } from '../types/CellPosition';
export const softFocusPositionState = atom<TablePosition>({
export const softFocusPositionState = atom<CellPosition>({
key: 'softFocusPositionState',
default: {
row: 0,

View File

@ -1,4 +1,4 @@
export type TablePosition = {
export type CellPosition = {
row: number;
column: number;
};

View File

@ -1,6 +1,6 @@
import { TablePosition } from '../TablePosition';
import { CellPosition } from '../CellPosition';
export function isTablePosition(value: any): value is TablePosition {
export function isTablePosition(value: any): value is CellPosition {
return (
value && typeof value.row === 'number' && typeof value.column === 'number'
);