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:
@ -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({
|
||||
|
||||
Reference in New Issue
Block a user