Replace hotkey scopes by focus stack (Part 5 - Form field Inputs, Pages, Dialog ...) (#13106)

# Replace hotkey scopes by focus stack (Part 5 - Form field Inputs,
Pages, Dialog ...)

This PR is the 5th part of a refactoring aiming to deprecate the hotkey
scopes api in favor of the new focus stack api which is more robust.
Part 1: https://github.com/twentyhq/twenty/pull/12673
Part 2: https://github.com/twentyhq/twenty/pull/12798
Part 3: https://github.com/twentyhq/twenty/pull/12910
Part 4: https://github.com/twentyhq/twenty/pull/12933

In this part, all the last components using hotkey scopes were
refactored.
In the 6th and final part of this refactoring we will be able to
completely remove the hotkey scopes from the codebase.
This commit is contained in:
Raphaël Bosi
2025-07-08 20:18:32 +02:00
committed by GitHub
parent 66b633e08e
commit 9eaa8ad517
50 changed files with 590 additions and 678 deletions
@@ -4,8 +4,8 @@ import { Key } from 'ts-key-enum';
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
import { useRecordBoardCardNavigation } from '@/object-record/record-board/hooks/useRecordBoardCardNavigation';
import { useRecordBoardSelectAllHotkeys } from '@/object-record/record-board/hooks/useRecordBoardSelectAllHotkeys';
import { RECORD_INDEX_FOCUS_ID } from '@/object-record/record-index/constants/RecordIndexFocusId';
import { RecordIndexHotkeyScope } from '@/object-record/record-index/types/RecordIndexHotkeyScope';
import { PageFocusId } from '@/types/PageFocusId';
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
export const RecordBoardHotkeyEffect = () => {
@@ -18,7 +18,7 @@ export const RecordBoardHotkeyEffect = () => {
callback: () => {
move('down');
},
focusId: RECORD_INDEX_FOCUS_ID,
focusId: PageFocusId.RecordIndex,
scope: RecordIndexHotkeyScope.RecordIndex,
dependencies: [move],
});
@@ -1,65 +1,28 @@
import styled from '@emotion/styled';
import { useCallback, useRef } from 'react';
import { useRecordGroupActions } from '@/object-record/record-group/hooks/useRecordGroupActions';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { ViewType } from '@/views/types/ViewType';
import { MenuItem } from 'twenty-ui/navigation';
const StyledMenuContainer = styled.div`
position: absolute;
top: ${({ theme }) => theme.spacing(10)};
width: 200px;
z-index: 1;
`;
type RecordBoardColumnDropdownMenuProps = {
onClose: () => void;
onDelete?: (id: string) => void;
stageId: string;
};
export const RecordBoardColumnDropdownMenu = ({
onClose,
}: RecordBoardColumnDropdownMenuProps) => {
const boardColumnMenuRef = useRef<HTMLDivElement>(null);
export const RecordBoardColumnDropdownMenu = () => {
const recordGroupActions = useRecordGroupActions({
viewType: ViewType.Kanban,
});
const closeMenu = useCallback(() => {
onClose();
}, [onClose]);
useListenClickOutside({
refs: [boardColumnMenuRef],
callback: closeMenu,
listenerId: 'record-board-column-dropdown-menu',
});
return (
<StyledMenuContainer ref={boardColumnMenuRef}>
<OverlayContainer>
<DropdownContent selectDisabled>
<DropdownMenuItemsContainer>
{recordGroupActions.map((action) => (
<MenuItem
key={action.id}
onClick={() => {
action.callback();
closeMenu();
}}
LeftIcon={action.icon}
text={action.label}
/>
))}
</DropdownMenuItemsContainer>
</DropdownContent>
</OverlayContainer>
</StyledMenuContainer>
<DropdownContent selectDisabled>
<DropdownMenuItemsContainer>
{recordGroupActions.map((action) => (
<MenuItem
key={action.id}
onClick={() => {
action.callback();
}}
LeftIcon={action.icon}
text={action.label}
/>
))}
</DropdownMenuItemsContainer>
</DropdownContent>
);
};
@@ -7,10 +7,10 @@ import { RecordBoardColumnDropdownMenu } from '@/object-record/record-board/reco
import { RecordBoardColumnHeaderAggregateDropdown } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdown';
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
import { useAggregateRecordsForRecordBoardColumn } from '@/object-record/record-board/record-board-column/hooks/useAggregateRecordsForRecordBoardColumn';
import { RecordBoardColumnHotkeyScope } from '@/object-record/record-board/types/BoardColumnHotkeyScope';
import { RecordGroupDefinitionType } from '@/object-record/record-group/types/RecordGroupDefinition';
import { useCreateNewIndexRecord } from '@/object-record/record-table/hooks/useCreateNewIndexRecord';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { useToggleDropdown } from '@/ui/layout/dropdown/hooks/useToggleDropdown';
import { Tag } from 'twenty-ui/components';
import { IconDotsVertical, IconPlus } from 'twenty-ui/display';
import { LightIconButton } from 'twenty-ui/input';
@@ -66,32 +66,11 @@ const StyledTag = styled(Tag)`
export const RecordBoardColumnHeader = () => {
const { columnDefinition } = useContext(RecordBoardColumnContext);
const [isBoardColumnMenuOpen, setIsBoardColumnMenuOpen] = useState(false);
const [isHeaderHovered, setIsHeaderHovered] = useState(false);
const { objectMetadataItem, selectFieldMetadataItem } =
useContext(RecordBoardContext);
const {
setHotkeyScopeAndMemorizePreviousScope,
goBackToPreviousHotkeyScope,
} = usePreviousHotkeyScope();
const handleBoardColumnMenuOpen = () => {
setIsBoardColumnMenuOpen(true);
setHotkeyScopeAndMemorizePreviousScope({
scope: RecordBoardColumnHotkeyScope.BoardColumn,
customScopes: {
goto: false,
},
});
};
const handleBoardColumnMenuClose = () => {
goBackToPreviousHotkeyScope();
setIsBoardColumnMenuOpen(false);
};
const { aggregateValue, aggregateLabel } =
useAggregateRecordsForRecordBoardColumn();
@@ -105,6 +84,10 @@ export const RecordBoardColumnHeader = () => {
objectMetadataItem: objectMetadataItem,
});
const { toggleDropdown } = useToggleDropdown();
const dropdownId = `record-board-column-dropdown-${columnDefinition.id}`;
return (
<StyledColumn>
<StyledHeader
@@ -113,25 +96,36 @@ export const RecordBoardColumnHeader = () => {
>
<StyledHeaderContainer>
<StyledLeftContainer>
<StyledTag
onClick={handleBoardColumnMenuOpen}
variant={
columnDefinition.type === RecordGroupDefinitionType.Value
? 'solid'
: 'outline'
}
color={
columnDefinition.type === RecordGroupDefinitionType.Value
? columnDefinition.color
: 'transparent'
}
text={columnDefinition.title}
weight={
columnDefinition.type === RecordGroupDefinitionType.Value
? 'regular'
: 'medium'
<Dropdown
dropdownId={dropdownId}
dropdownPlacement="bottom-start"
dropdownOffset={{
x: 0,
y: 10,
}}
clickableComponent={
<StyledTag
variant={
columnDefinition.type === RecordGroupDefinitionType.Value
? 'solid'
: 'outline'
}
color={
columnDefinition.type === RecordGroupDefinitionType.Value
? columnDefinition.color
: 'transparent'
}
text={columnDefinition.title}
weight={
columnDefinition.type === RecordGroupDefinitionType.Value
? 'regular'
: 'medium'
}
/>
}
dropdownComponents={<RecordBoardColumnDropdownMenu />}
/>
<RecordBoardColumnHeaderAggregateDropdown
aggregateValue={aggregateValue}
dropdownId={`record-board-column-aggregate-dropdown-${columnDefinition.id}`}
@@ -145,7 +139,11 @@ export const RecordBoardColumnHeader = () => {
<LightIconButton
accent="tertiary"
Icon={IconDotsVertical}
onClick={handleBoardColumnMenuOpen}
onClick={() => {
toggleDropdown({
dropdownComponentInstanceIdFromProps: dropdownId,
});
}}
/>
{hasObjectUpdatePermissions && (
<LightIconButton
@@ -164,12 +162,6 @@ export const RecordBoardColumnHeader = () => {
</StyledRightContainer>
</StyledHeaderContainer>
</StyledHeader>
{isBoardColumnMenuOpen && (
<RecordBoardColumnDropdownMenu
onClose={handleBoardColumnMenuClose}
stageId={columnDefinition.id}
/>
)}
</StyledColumn>
);
};
@@ -1,36 +1,24 @@
import { Key } from 'ts-key-enum';
import { useDropdownContextStateManagement } from '@/dropdown-context-state-management/hooks/useDropdownContextStateManagement';
import {
RecordBoardColumnHeaderAggregateDropdownContext,
RecordBoardColumnHeaderAggregateDropdownContextValue,
} from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownContext';
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useLingui } from '@lingui/react/macro';
import { MenuItem } from 'twenty-ui/navigation';
export const RecordBoardColumnHeaderAggregateDropdownMenuContent = () => {
const { t } = useLingui();
const { onContentChange, closeDropdown } =
const { onContentChange } =
useDropdownContextStateManagement<RecordBoardColumnHeaderAggregateDropdownContextValue>(
{
context: RecordBoardColumnHeaderAggregateDropdownContext,
},
);
useScopedHotkeys(
[Key.Escape],
() => {
closeDropdown();
},
TableOptionsHotkeyScope.Dropdown,
);
return (
<DropdownContent>
<DropdownMenuItemsContainer>
@@ -10,18 +10,15 @@ import { getAggregateOperationLabel } from '@/object-record/record-board/record-
import { recordIndexKanbanAggregateOperationState } from '@/object-record/record-index/states/recordIndexKanbanAggregateOperationState';
import { AggregateOperations } from '@/object-record/record-table/constants/AggregateOperations';
import { ExtendedAggregateOperations } from '@/object-record/record-table/types/ExtendedAggregateOperations';
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
import { AvailableFieldsForAggregateOperation } from '@/object-record/types/AvailableFieldsForAggregateOperation';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { useUpdateViewAggregate } from '@/views/hooks/useUpdateViewAggregate';
import isEmpty from 'lodash.isempty';
import { useRecoilValue } from 'recoil';
import { Key } from 'ts-key-enum';
import { IconCheck, IconChevronLeft } from 'twenty-ui/display';
export const RecordBoardColumnHeaderAggregateDropdownOptionsContent = ({
@@ -38,14 +35,6 @@ export const RecordBoardColumnHeaderAggregateDropdownOptionsContent = ({
},
);
useScopedHotkeys(
[Key.Escape],
() => {
closeDropdown();
},
TableOptionsHotkeyScope.Dropdown,
);
const setAggregateOperation = useSetRecoilComponentStateV2(
aggregateOperationComponentState,
);
@@ -85,6 +85,7 @@ export const FormBooleanFieldInput = ({
<FormFieldInputRowContainer>
<FormFieldInputInnerContainer
formFieldInputInstanceId={instanceId}
hasRightElement={isDefined(VariablePicker) && !readonly}
>
{draftValue.type === 'static' ? (
@@ -296,6 +296,7 @@ export const FormDateTimeFieldInput = ({
<FormFieldInputRowContainer>
<StyledInputContainer
formFieldInputInstanceId={instanceId}
ref={datePickerWrapperRef}
hasRightElement={isDefined(VariablePicker) && !readonly}
>
@@ -1,5 +1,7 @@
import { FormFieldInputHotKeyScope } from '@/object-record/record-field/form-types/constants/FormFieldInputHotKeyScope';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById';
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import { forwardRef, HTMLAttributes, Ref } from 'react';
@@ -9,9 +11,12 @@ type FormFieldInputInnerContainerProps = {
multiline?: boolean;
readonly?: boolean;
preventSetHotkeyScope?: boolean;
formFieldInputInstanceId: string;
};
const StyledFormFieldInputInnerContainer = styled.div<FormFieldInputInnerContainerProps>`
const StyledFormFieldInputInnerContainer = styled.div<
Omit<FormFieldInputInnerContainerProps, 'formFieldInputInstanceId'>
>`
background-color: ${({ theme }) => theme.background.transparent.lighter};
border: 1px solid ${({ theme }) => theme.border.color.medium};
border-top-left-radius: ${({ theme }) => theme.border.radius.sm};
@@ -48,20 +53,25 @@ export const FormFieldInputInnerContainer = forwardRef(
readonly,
preventSetHotkeyScope = false,
onClick,
formFieldInputInstanceId,
}: HTMLAttributes<HTMLDivElement> & FormFieldInputInnerContainerProps,
ref: Ref<HTMLDivElement>,
) => {
const {
goBackToPreviousHotkeyScope,
setHotkeyScopeAndMemorizePreviousScope,
} = usePreviousHotkeyScope();
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
const { removeFocusItemFromFocusStackById } =
useRemoveFocusItemFromFocusStackById();
const handleFocus = (e: React.FocusEvent<HTMLDivElement>) => {
onFocus?.(e);
if (!preventSetHotkeyScope) {
setHotkeyScopeAndMemorizePreviousScope({
scope: FormFieldInputHotKeyScope.FormFieldInput,
pushFocusItemToFocusStack({
focusId: formFieldInputInstanceId,
component: {
type: FocusComponentType.FORM_FIELD_INPUT,
instanceId: formFieldInputInstanceId,
},
hotkeyScope: { scope: FormFieldInputHotKeyScope.FormFieldInput },
});
}
};
@@ -70,7 +80,9 @@ export const FormFieldInputInnerContainer = forwardRef(
onBlur?.(e);
if (!preventSetHotkeyScope) {
goBackToPreviousHotkeyScope();
removeFocusItemFromFocusStackById({
focusId: formFieldInputInstanceId,
});
}
};
@@ -14,7 +14,9 @@ import { MultiSelectInput } from '@/ui/field/input/components/MultiSelectInput';
import { InputLabel } from '@/ui/input/components/InputLabel';
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById';
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
import { isStandaloneVariableString } from '@/workflow/utils/isStandaloneVariableString';
import { useTheme } from '@emotion/react';
import { isArray } from '@sniptt/guards';
@@ -88,10 +90,9 @@ export const FormMultiSelectFieldInput = ({
const hotkeyScope =
FormMultiSelectFieldInputHotKeyScope.FormMultiSelectFieldInput;
const {
setHotkeyScopeAndMemorizePreviousScope,
goBackToPreviousHotkeyScope,
} = usePreviousHotkeyScope();
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
const { removeFocusItemFromFocusStackById } =
useRemoveFocusItemFromFocusStackById();
const [draftValue, setDraftValue] = useState<
| {
@@ -128,8 +129,13 @@ export const FormMultiSelectFieldInput = ({
editingMode: 'edit',
});
setHotkeyScopeAndMemorizePreviousScope({
scope: hotkeyScope,
pushFocusItemToFocusStack({
focusId: instanceId,
component: {
type: FocusComponentType.FORM_FIELD_INPUT,
instanceId,
},
hotkeyScope: { scope: hotkeyScope },
});
};
@@ -157,7 +163,7 @@ export const FormMultiSelectFieldInput = ({
editingMode: 'view',
});
goBackToPreviousHotkeyScope();
removeFocusItemFromFocusStackById({ focusId: instanceId });
};
const handleVariableTagInsert = (variableName: string) => {
@@ -201,6 +207,7 @@ export const FormMultiSelectFieldInput = ({
<FormFieldInputRowContainer>
<FormFieldInputInnerContainer
formFieldInputInstanceId={instanceId}
hasRightElement={isDefined(VariablePicker) && !readonly}
>
{draftValue.type === 'static' ? (
@@ -121,6 +121,7 @@ export const FormNumberFieldInput = ({
<FormFieldInputRowContainer>
<FormFieldInputInnerContainer
formFieldInputInstanceId={instanceId}
hasRightElement={isDefined(VariablePicker) && !readonly}
onBlur={onBlur}
>
@@ -71,6 +71,7 @@ export const FormRawJsonFieldInput = ({
<FormFieldInputRowContainer multiline>
<FormFieldInputInnerContainer
formFieldInputInstanceId={instanceId}
hasRightElement={isDefined(VariablePicker) && !readonly}
multiline
onBlur={onBlur}
@@ -7,8 +7,8 @@ import { InlineCellHotkeyScope } from '@/object-record/record-inline-cell/types/
import { InputLabel } from '@/ui/input/components/InputLabel';
import { Select } from '@/ui/input/components/Select';
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById';
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
import { isStandaloneVariableString } from '@/workflow/utils/isStandaloneVariableString';
import { useTheme } from '@emotion/react';
import { useId, useState } from 'react';
@@ -40,7 +40,8 @@ export const FormSelectFieldInput = ({
const hotkeyScope = InlineCellHotkeyScope.InlineCell;
const { goBackToPreviousHotkeyScope } = usePreviousHotkeyScope();
const { removeFocusItemFromFocusStackById } =
useRemoveFocusItemFromFocusStackById();
const [draftValue, setDraftValue] = useState<
| {
@@ -72,7 +73,7 @@ export const FormSelectFieldInput = ({
editingMode: 'view',
});
goBackToPreviousHotkeyScope();
removeFocusItemFromFocusStackById({ focusId: instanceId });
onChange(option);
};
@@ -87,7 +88,7 @@ export const FormSelectFieldInput = ({
editingMode: 'view',
});
goBackToPreviousHotkeyScope();
removeFocusItemFromFocusStackById({ focusId: instanceId });
};
const selectedOption = options.find(
@@ -119,14 +120,13 @@ export const FormSelectFieldInput = ({
onChange(variableName);
};
useScopedHotkeys(
Key.Escape,
() => {
onCancel();
},
hotkeyScope,
[onCancel],
);
useHotkeysOnFocusedElement({
keys: Key.Escape,
callback: onCancel,
focusId: instanceId,
scope: hotkeyScope,
dependencies: [onCancel],
});
return (
<FormFieldInputContainer>
@@ -149,6 +149,7 @@ export const FormSelectFieldInput = ({
/>
) : (
<FormFieldInputInnerContainer
formFieldInputInstanceId={instanceId}
hasRightElement={isDefined(VariablePicker) && !readonly}
>
<VariableChipStandalone
@@ -150,7 +150,11 @@ export const FormSingleRecordPicker = ({
{label ? <InputLabel>{label}</InputLabel> : null}
<FormFieldInputRowContainer>
{disabled ? (
<StyledFormSelectContainer hasRightElement={false} readonly>
<StyledFormSelectContainer
formFieldInputInstanceId={componentId}
hasRightElement={false}
readonly
>
<FormSingleRecordFieldChip
draftValue={draftValue}
selectedRecord={selectedRecord}
@@ -169,6 +173,7 @@ export const FormSingleRecordPicker = ({
dropdownOffset={{ y: parseInt(theme.spacing(1), 10) }}
clickableComponent={
<StyledFormSelectContainer
formFieldInputInstanceId={componentId}
hasRightElement={isDefined(VariablePicker) && !disabled}
preventSetHotkeyScope={true}
>
@@ -71,6 +71,7 @@ export const FormTextFieldInput = ({
<FormFieldInputRowContainer multiline={multiline}>
<FormFieldInputInnerContainer
formFieldInputInstanceId={instanceId}
hasRightElement={isDefined(VariablePicker) && !readonly}
multiline={multiline}
onBlur={onBlur}
@@ -95,6 +95,7 @@ export const FormUuidFieldInput = ({
<FormFieldInputRowContainer>
<FormFieldInputInnerContainer
formFieldInputInstanceId={instanceId}
hasRightElement={isDefined(VariablePicker) && !readonly}
>
{draftValue.type === 'static' ? (
@@ -1 +0,0 @@
export const RECORD_INDEX_FOCUS_ID = 'record-index';
@@ -1,5 +1,5 @@
import { RECORD_INDEX_FOCUS_ID } from '@/object-record/record-index/constants/RecordIndexFocusId';
import { RecordIndexHotkeyScope } from '@/object-record/record-index/types/RecordIndexHotkeyScope';
import { PageFocusId } from '@/types/PageFocusId';
import { useResetFocusStackToFocusItem } from '@/ui/utilities/focus/hooks/useResetFocusStackToFocusItem';
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
@@ -9,10 +9,10 @@ export const useResetFocusStackToRecordIndex = () => {
const resetFocusStackToRecordIndex = () => {
resetFocusStackToFocusItem({
focusStackItem: {
focusId: RECORD_INDEX_FOCUS_ID,
focusId: PageFocusId.RecordIndex,
componentInstance: {
componentType: FocusComponentType.PAGE,
componentInstanceId: RECORD_INDEX_FOCUS_ID,
componentInstanceId: PageFocusId.RecordIndex,
},
globalHotkeysConfig: {
enableGlobalHotkeysWithModifiers: true,
@@ -14,7 +14,6 @@ import { RecordIndexHotkeyScope } from '@/object-record/record-index/types/Recor
import { RecordTableComponentInstance } from '@/object-record/record-table/components/RecordTableComponentInstance';
import { RecordTableContextProvider } from '@/object-record/record-table/components/RecordTableContextProvider';
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { RecordUpdateContext } from '../contexts/EntityUpdateMutationHookContext';
import { useRecordTable } from '../hooks/useRecordTable';
@@ -48,16 +47,6 @@ export const RecordTableWithWrappers = ({
selectAllRows();
};
useScopedHotkeys(
'ctrl+a,meta+a',
handleSelectAllRows,
RecordIndexHotkeyScope.RecordIndex,
[],
{
enableOnFormTags: false,
},
);
useHotkeysOnFocusedElement({
keys: ['ctrl+a,meta+a'],
callback: handleSelectAllRows,
@@ -1,10 +1,10 @@
import { Key } from 'ts-key-enum';
import { RECORD_INDEX_FOCUS_ID } from '@/object-record/record-index/constants/RecordIndexFocusId';
import { RecordIndexHotkeyScope } from '@/object-record/record-index/types/RecordIndexHotkeyScope';
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
import { isAtLeastOneTableRowSelectedSelector } from '@/object-record/record-table/record-table-row/states/isAtLeastOneTableRowSelectedSelector';
import { PageFocusId } from '@/types/PageFocusId';
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
@@ -28,7 +28,7 @@ export const RecordTableBodyEscapeHotkeyEffect = () => {
useHotkeysOnFocusedElement({
keys: [Key.Escape],
callback: handleEscape,
focusId: RECORD_INDEX_FOCUS_ID,
focusId: PageFocusId.RecordIndex,
scope: RecordIndexHotkeyScope.RecordIndex,
dependencies: [handleEscape],
options: {
@@ -1,10 +1,10 @@
import { RECORD_INDEX_FOCUS_ID } from '@/object-record/record-index/constants/RecordIndexFocusId';
import { RecordIndexHotkeyScope } from '@/object-record/record-index/types/RecordIndexHotkeyScope';
import { useRecordTableRowFocusHotkeys } from '@/object-record/record-table/hooks/useRecordTableRowFocusHotkeys';
import { PageFocusId } from '@/types/PageFocusId';
export const RecordTableBodyFocusKeyboardEffect = () => {
useRecordTableRowFocusHotkeys({
focusId: RECORD_INDEX_FOCUS_ID,
focusId: PageFocusId.RecordIndex,
hotkeyScope: RecordIndexHotkeyScope.RecordIndex,
});
@@ -1,15 +1,11 @@
import { RecordTableColumnAggregateFooterAggregateOperationMenuItems } from '@/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateFooterAggregateOperationMenuItems';
import { RecordTableColumnAggregateFooterDropdownContext } from '@/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateFooterDropdownContext';
import { ExtendedAggregateOperations } from '@/object-record/record-table/types/ExtendedAggregateOperations';
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useContext } from 'react';
import { Key } from 'ts-key-enum';
import { IconChevronLeft } from 'twenty-ui/display';
export const RecordTableColumnAggregateFooterDropdownSubmenuContent = ({
@@ -19,19 +15,10 @@ export const RecordTableColumnAggregateFooterDropdownSubmenuContent = ({
aggregateOperations: ExtendedAggregateOperations[];
title: string;
}) => {
const { dropdownId, resetContent } = useContext(
const { resetContent } = useContext(
RecordTableColumnAggregateFooterDropdownContext,
);
const { closeDropdown } = useCloseDropdown();
useScopedHotkeys(
[Key.Escape],
() => {
resetContent();
closeDropdown(dropdownId);
},
TableOptionsHotkeyScope.Dropdown,
);
return (
<DropdownContent>
<DropdownMenuHeader
@@ -4,14 +4,11 @@ import { RecordTableColumnAggregateFooterDropdownContext } from '@/object-record
import { NON_STANDARD_AGGREGATE_OPERATION_OPTIONS } from '@/object-record/record-table/record-table-footer/constants/nonStandardAggregateOperationsOptions';
import { useViewFieldAggregateOperation } from '@/object-record/record-table/record-table-footer/hooks/useViewFieldAggregateOperation';
import { getAvailableAggregateOperationsForFieldMetadataType } from '@/object-record/record-table/record-table-footer/utils/getAvailableAggregateOperationsForFieldMetadataType';
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { t } from '@lingui/core/macro';
import { useContext, useMemo } from 'react';
import { Key } from 'ts-key-enum';
import { isDefined, isFieldMetadataDateKind } from 'twenty-shared/utils';
import { IconCheck } from 'twenty-ui/display';
import { MenuItem } from 'twenty-ui/navigation';
@@ -28,14 +25,6 @@ export const RecordTableColumnAggregateFooterMenuContent = () => {
const { closeDropdown } = useCloseDropdown();
const { objectMetadataItem } = useRecordTableContextOrThrow();
useScopedHotkeys(
[Key.Escape],
() => {
closeDropdown(dropdownId);
},
TableOptionsHotkeyScope.Dropdown,
);
const availableAggregateOperation = useMemo(
() =>
getAvailableAggregateOperationsForFieldMetadataType({