Fix/scope hotkeys (#581)

* WIP

* asd

* Fix

* Fix lint

* Removed console log

* asd

* Removed isDefined

* Fix/debounce company card onchange (#580)

* Add internal state and debounce for editable text card

* Use debounce for date fields too

* Update refetch

* Nit

* Removed comments

* Ménage

---------

Co-authored-by: Emilien Chauvet <emilien.chauvet.enpc@gmail.com>
This commit is contained in:
Lucas Bordeau
2023-07-11 03:53:46 +02:00
committed by GitHub
parent 1c8aaff39d
commit 5f98b70c6a
71 changed files with 581 additions and 509 deletions

View File

@ -1,11 +1,11 @@
import { ReactElement } from 'react';
import styled from '@emotion/styled';
import { HotkeysScopeStackItem } from '@/hotkeys/types/internal/HotkeysScopeStackItems';
import { HotkeysScope } from '@/hotkeys/types/internal/HotkeysScope';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { useEditableCell } from './hooks/useCloseEditableCell';
import { useCurrentCellEditMode } from './hooks/useCurrentCellEditMode';
import { useEditableCell } from './hooks/useEditableCell';
import { useIsSoftFocusOnCurrentCell } from './hooks/useIsSoftFocusOnCurrentCell';
import { useSetSoftFocusOnCurrentCell } from './hooks/useSetSoftFocusOnCurrentCell';
import { EditableCellDisplayMode } from './EditableCellDisplayMode';
@ -28,7 +28,7 @@ type OwnProps = {
nonEditModeContent: ReactElement;
editModeHorizontalAlign?: 'left' | 'right';
editModeVerticalPosition?: 'over' | 'below';
editHotkeysScope?: HotkeysScopeStackItem;
editHotkeysScope?: HotkeysScope;
};
export function EditableCell({
@ -60,9 +60,9 @@ export function EditableCell({
scope: InternalHotkeysScope.CellEditMode,
},
);
} else {
setSoftFocusOnCurrentCell();
}
setSoftFocusOnCurrentCell();
}
return (

View File

@ -7,7 +7,7 @@ import { useListenClickOutsideArrayOfRef } from '@/ui/hooks/useListenClickOutsid
import { useMoveSoftFocus } from '@/ui/tables/hooks/useMoveSoftFocus';
import { overlayBackground } from '@/ui/themes/effects';
import { useEditableCell } from './hooks/useCloseEditableCell';
import { useEditableCell } from './hooks/useEditableCell';
export const EditableCellEditModeContainer = styled.div<OwnProps>`
align-items: center;

View File

@ -1,17 +1,17 @@
import React from 'react';
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
import { HotkeysScopeStackItem } from '@/hotkeys/types/internal/HotkeysScopeStackItems';
import { HotkeysScope } from '@/hotkeys/types/internal/HotkeysScope';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { isNonTextWritingKey } from '@/utils/hotkeys/isNonTextWritingKey';
import { useEditableCell } from './hooks/useCloseEditableCell';
import { useEditableCell } from './hooks/useEditableCell';
import { EditableCellDisplayMode } from './EditableCellDisplayMode';
export function EditableCellSoftFocusMode({
children,
editHotkeysScope,
}: React.PropsWithChildren<{ editHotkeysScope?: HotkeysScopeStackItem }>) {
}: React.PropsWithChildren<{ editHotkeysScope?: HotkeysScope }>) {
const { closeEditableCell, openEditableCell } = useEditableCell();
useScopedHotkeys(

View File

@ -1,8 +1,8 @@
import { useRecoilCallback } from 'recoil';
import { useAddToHotkeysScopeStack } from '@/hotkeys/hooks/useAddToHotkeysScopeStack';
import { useRemoveHighestHotkeysScopeStackItem } from '@/hotkeys/hooks/useRemoveHighestHotkeysScopeStackItem';
import { HotkeysScopeStackItem } from '@/hotkeys/types/internal/HotkeysScopeStackItems';
import { useSetHotkeysScope } from '@/hotkeys/hooks/useSetHotkeysScope';
import { HotkeysScope } from '@/hotkeys/types/internal/HotkeysScope';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { useCloseCurrentCellInEditMode } from '@/ui/tables/hooks/useClearCellInEditMode';
import { isSoftFocusActiveState } from '@/ui/tables/states/isSoftFocusActiveState';
import { isSomeInputInEditModeState } from '@/ui/tables/states/isSomeInputInEditModeState';
@ -12,21 +12,18 @@ import { useCurrentCellEditMode } from './useCurrentCellEditMode';
export function useEditableCell() {
const { setCurrentCellInEditMode } = useCurrentCellEditMode();
const addToHotkeysScopeStack = useAddToHotkeysScopeStack();
const setHotkeysScope = useSetHotkeysScope();
const closeCurrentCellInEditMode = useCloseCurrentCellInEditMode();
const removeHighestHotkeysScopedStackItem =
useRemoveHighestHotkeysScopeStackItem();
function closeEditableCell() {
closeCurrentCellInEditMode();
removeHighestHotkeysScopedStackItem();
setHotkeysScope(InternalHotkeysScope.TableSoftFocus);
}
const openEditableCell = useRecoilCallback(
({ snapshot, set }) =>
(hotkeysScopeStackItem: HotkeysScopeStackItem) => {
(hotkeysScope: HotkeysScope) => {
const isSomeInputInEditMode = snapshot
.getLoadable(isSomeInputInEditModeState)
.valueOrThrow();
@ -37,10 +34,10 @@ export function useEditableCell() {
setCurrentCellInEditMode();
addToHotkeysScopeStack(hotkeysScopeStackItem);
setHotkeysScope(hotkeysScope.scope);
}
},
[setCurrentCellInEditMode, addToHotkeysScopeStack],
[setCurrentCellInEditMode, setHotkeysScope],
);
return {

View File

@ -1,7 +1,7 @@
import { useCallback, useMemo } from 'react';
import { useRecoilState } from 'recoil';
import { useAddToHotkeysScopeStack } from '@/hotkeys/hooks/useAddToHotkeysScopeStack';
import { useSetHotkeysScope } from '@/hotkeys/hooks/useSetHotkeysScope';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { useRecoilScopedState } from '@/recoil-scope/hooks/useRecoilScopedState';
import { useSetSoftFocusPosition } from '@/ui/tables/hooks/useSetSoftFocusPosition';
@ -34,16 +34,16 @@ export function useSetSoftFocusOnCurrentCell() {
const [, setIsSoftFocusActive] = useRecoilState(isSoftFocusActiveState);
const addToHotkeysScopeStack = useAddToHotkeysScopeStack();
const setHotkeysScope = useSetHotkeysScope();
return useCallback(() => {
setSoftFocusPosition(currentTablePosition);
setIsSoftFocusActive(true);
addToHotkeysScopeStack({ scope: InternalHotkeysScope.TableSoftFocus });
setHotkeysScope(InternalHotkeysScope.TableSoftFocus);
}, [
setSoftFocusPosition,
currentTablePosition,
setIsSoftFocusActive,
addToHotkeysScopeStack,
setHotkeysScope,
]);
}

View File

@ -1,6 +1,6 @@
import { InplaceInputDateEditMode } from '@/ui/inplace-inputs/components/InplaceInputDateEditMode';
import { useEditableCell } from '../hooks/useCloseEditableCell';
import { useEditableCell } from '../hooks/useEditableCell';
export type EditableDateProps = {
value: Date;

View File

@ -7,7 +7,7 @@ import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysSc
import { InplaceInputTextEditMode } from '@/ui/inplace-inputs/components/InplaceInputTextEditMode';
import { useMoveSoftFocus } from '@/ui/tables/hooks/useMoveSoftFocus';
import { useEditableCell } from '../hooks/useCloseEditableCell';
import { useEditableCell } from '../hooks/useEditableCell';
type OwnProps = {
firstValue: string;

View File

@ -1,5 +1,10 @@
import { ChangeEvent } from 'react';
import { ChangeEvent, useRef } from 'react';
import styled from '@emotion/styled';
import { Key } from 'ts-key-enum';
import { usePreviousHotkeysScope } from '@/hotkeys/hooks/internal/usePreviousHotkeysScope';
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
type OwnProps = Omit<
React.InputHTMLAttributes<HTMLInputElement>,
@ -52,10 +57,37 @@ export function TextInput({
fullWidth,
...props
}: OwnProps): JSX.Element {
const inputRef = useRef<HTMLInputElement>(null);
const {
goBackToPreviousHotkeysScope,
setHotkeysScopeAndMemorizePreviousScope,
} = usePreviousHotkeysScope();
function handleFocus() {
setHotkeysScopeAndMemorizePreviousScope(InternalHotkeysScope.TextInput);
}
function handleBlur() {
goBackToPreviousHotkeysScope();
}
useScopedHotkeys(
[Key.Enter, Key.Escape],
() => {
inputRef.current?.blur();
},
InternalHotkeysScope.TextInput,
);
return (
<StyledContainer>
{label && <StyledLabel>{label}</StyledLabel>}
<StyledInput
ref={inputRef}
tabIndex={props.tabIndex ?? 0}
onFocus={handleFocus}
onBlur={handleBlur}
fullWidth={fullWidth ?? false}
value={value}
onChange={(event: ChangeEvent<HTMLInputElement>) => {

View File

@ -1,11 +1,13 @@
import { useCallback, useState } from 'react';
import { Key } from 'ts-key-enum';
import { activeTableFiltersScopedState } from '@/filters-and-sorts/states/activeTableFiltersScopedState';
import { filterDropdownSearchInputScopedState } from '@/filters-and-sorts/states/filterDropdownSearchInputScopedState';
import { isFilterDropdownOperandSelectUnfoldedScopedState } from '@/filters-and-sorts/states/isFilterDropdownOperandSelectUnfoldedScopedState';
import { selectedOperandInDropdownScopedState } from '@/filters-and-sorts/states/selectedOperandInDropdownScopedState';
import { tableFilterDefinitionUsedInDropdownScopedState } from '@/filters-and-sorts/states/tableFilterDefinitionUsedInDropdownScopedState';
import { useHotkeysScopeOnBooleanState } from '@/hotkeys/hooks/useHotkeysScopeOnBooleanState';
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
import { useSetHotkeysScope } from '@/hotkeys/hooks/useSetHotkeysScope';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { useRecoilScopedState } from '@/recoil-scope/hooks/useRecoilScopedState';
import { TableContext } from '@/ui/tables/states/TableContext';
@ -23,11 +25,6 @@ import { FilterDropdownTextSearchInput } from './FilterDropdownTextSearchInput';
export function FilterDropdownButton() {
const [isUnfolded, setIsUnfolded] = useState(false);
useHotkeysScopeOnBooleanState(
{ scope: InternalHotkeysScope.TableHeaderDropdownButton },
isUnfolded,
);
const [
isFilterDropdownOperandSelectUnfolded,
setIsFilterDropdownOperandSelectUnfolded,
@ -71,18 +68,27 @@ export function FilterDropdownButton() {
const isFilterSelected = (activeTableFilters?.length ?? 0) > 0;
const setHotkeysScope = useSetHotkeysScope();
function handleIsUnfoldedChange(newIsUnfolded: boolean) {
if (newIsUnfolded) {
setIsUnfolded(true);
} else {
if (tableFilterDefinitionUsedInDropdown?.type === 'entity') {
setHotkeysScope(InternalHotkeysScope.Table);
}
setIsUnfolded(false);
resetState();
}
}
useHotkeysScopeOnBooleanState(
{ scope: InternalHotkeysScope.RelationPicker },
tableFilterDefinitionUsedInDropdown?.type === 'entity',
useScopedHotkeys(
[Key.Escape],
() => {
handleIsUnfoldedChange(false);
},
InternalHotkeysScope.RelationPicker,
[handleIsUnfoldedChange],
);
return (

View File

@ -3,6 +3,8 @@ import { filterDropdownSearchInputScopedState } from '@/filters-and-sorts/states
import { selectedOperandInDropdownScopedState } from '@/filters-and-sorts/states/selectedOperandInDropdownScopedState';
import { tableFilterDefinitionUsedInDropdownScopedState } from '@/filters-and-sorts/states/tableFilterDefinitionUsedInDropdownScopedState';
import { getOperandsForFilterType } from '@/filters-and-sorts/utils/getOperandsForFilterType';
import { useSetHotkeysScope } from '@/hotkeys/hooks/useSetHotkeysScope';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { useRecoilScopedState } from '@/recoil-scope/hooks/useRecoilScopedState';
import { useRecoilScopedValue } from '@/recoil-scope/hooks/useRecoilScopedValue';
import { TableContext } from '@/ui/tables/states/TableContext';
@ -33,6 +35,8 @@ export function FilterDropdownFilterSelect() {
TableContext,
);
const setHotkeysScope = useSetHotkeysScope();
return (
<DropdownMenuItemContainer style={{ maxHeight: '300px' }}>
{availableTableFilters.map((availableTableFilter, index) => (
@ -40,6 +44,11 @@ export function FilterDropdownFilterSelect() {
key={`select-filter-${index}`}
onClick={() => {
setTableFilterDefinitionUsedInDropdown(availableTableFilter);
if (availableTableFilter.type === 'entity') {
setHotkeysScope(InternalHotkeysScope.RelationPicker);
}
setSelectedOperandInDropdown(
getOperandsForFilterType(availableTableFilter.type)?.[0],
);