diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/DateTimeFieldInput.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/DateTimeFieldInput.stories.tsx
index c21553579..010523e8f 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/DateTimeFieldInput.stories.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/DateTimeFieldInput.stories.tsx
@@ -6,12 +6,12 @@ import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope
import { FieldMetadataType } from '~/generated/graphql';
import { FieldContextProvider } from '@/object-record/record-field/meta-types/components/FieldContextProvider';
+import { StorybookFieldInputDropdownFocusIdSetterEffect } from '~/testing/components/StorybookFieldInputDropdownFocusIdSetterEffect';
import { useDateTimeField } from '../../../hooks/useDateTimeField';
import {
DateTimeFieldInput,
DateTimeFieldInputProps,
} from '../DateTimeFieldInput';
-
const formattedDate = new Date(2022, 0, 1, 2, 0, 0);
const DateFieldValueSetterEffect = ({ value }: { value: Date }) => {
@@ -81,6 +81,7 @@ const DateFieldInputWithContext = ({
}}
recordId={recordId}
>
+
+
+
({
@@ -25,9 +30,32 @@ export const useRegisterInputEvents = ({
onClickOutside?: (event: MouseEvent | TouchEvent, inputValue: T) => void;
hotkeyScope: string;
}) => {
+ const activeDropdownFocusId = useRecoilValue(activeDropdownFocusIdState);
+
+ const { recordId, fieldDefinition } = useContext(FieldContext);
+
useListenClickOutside({
refs: [inputRef, copyRef].filter(isDefined),
callback: (event) => {
+ const fieldDropdownFocusIdTableCell = getDropdownFocusIdForRecordField(
+ recordId,
+ fieldDefinition.fieldMetadataId,
+ 'table-cell',
+ );
+
+ const fieldDropdownFocusIdInlineCell = getDropdownFocusIdForRecordField(
+ recordId,
+ fieldDefinition.fieldMetadataId,
+ 'inline-cell',
+ );
+
+ if (
+ activeDropdownFocusId !== fieldDropdownFocusIdTableCell &&
+ activeDropdownFocusId !== fieldDropdownFocusIdInlineCell
+ ) {
+ return;
+ }
+
onClickOutside?.(event, inputValue);
},
enabled: isDefined(onClickOutside),
diff --git a/packages/twenty-front/src/modules/ui/utilities/pointer-event/hooks/useListenClickOutside.ts b/packages/twenty-front/src/modules/ui/utilities/pointer-event/hooks/useListenClickOutside.ts
index a57d6ef92..60c45d49e 100644
--- a/packages/twenty-front/src/modules/ui/utilities/pointer-event/hooks/useListenClickOutside.ts
+++ b/packages/twenty-front/src/modules/ui/utilities/pointer-event/hooks/useListenClickOutside.ts
@@ -4,6 +4,8 @@ import { useRecoilCallback } from 'recoil';
import { internalHotkeysEnabledScopesState } from '@/ui/utilities/hotkey/states/internal/internalHotkeysEnabledScopesState';
import { useClickOustideListenerStates } from '@/ui/utilities/pointer-event/hooks/useClickOustideListenerStates';
+const CLICK_OUTSIDE_DEBUG_MODE = false;
+
export enum ClickOutsideMode {
comparePixels = 'comparePixels',
compareHTMLRef = 'compareHTMLRef',
@@ -60,52 +62,60 @@ export const useListenClickOutside = ({
return;
}
- if (mode === ClickOutsideMode.compareHTMLRef) {
- const clickedOnAtLeastOneRef = refs
- .filter((ref) => !!ref.current)
- .some((ref) => ref.current?.contains(event.target as Node));
+ switch (mode) {
+ case ClickOutsideMode.compareHTMLRef: {
+ const clickedOnAtLeastOneRef = refs
+ .filter((ref) => !!ref.current)
+ .some((ref) => ref.current?.contains(event.target as Node));
- set(
- getClickOutsideListenerIsMouseDownInsideState,
- clickedOnAtLeastOneRef,
- );
- }
+ set(
+ getClickOutsideListenerIsMouseDownInsideState,
+ clickedOnAtLeastOneRef,
+ );
+ break;
+ }
- if (mode === ClickOutsideMode.comparePixels) {
- const clickedOnAtLeastOneRef = refs
- .filter((ref) => !!ref.current)
- .some((ref) => {
- if (!ref.current) {
- return false;
- }
+ case ClickOutsideMode.comparePixels: {
+ const clickedOnAtLeastOneRef = refs
+ .filter((ref) => !!ref.current)
+ .some((ref) => {
+ if (!ref.current) {
+ return false;
+ }
- const { x, y, width, height } =
- ref.current.getBoundingClientRect();
+ const { x, y, width, height } =
+ ref.current.getBoundingClientRect();
- const clientX =
- 'clientX' in event
- ? event.clientX
- : event.changedTouches[0].clientX;
- const clientY =
- 'clientY' in event
- ? event.clientY
- : event.changedTouches[0].clientY;
+ const clientX =
+ 'clientX' in event
+ ? event.clientX
+ : event.changedTouches[0].clientX;
+ const clientY =
+ 'clientY' in event
+ ? event.clientY
+ : event.changedTouches[0].clientY;
- if (
- clientX < x ||
- clientX > x + width ||
- clientY < y ||
- clientY > y + height
- ) {
- return false;
- }
- return true;
- });
+ if (
+ clientX < x ||
+ clientX > x + width ||
+ clientY < y ||
+ clientY > y + height
+ ) {
+ return false;
+ }
+ return true;
+ });
- set(
- getClickOutsideListenerIsMouseDownInsideState,
- clickedOnAtLeastOneRef,
- );
+ set(
+ getClickOutsideListenerIsMouseDownInsideState,
+ clickedOnAtLeastOneRef,
+ );
+ break;
+ }
+
+ default: {
+ break;
+ }
}
},
[
@@ -171,13 +181,30 @@ export const useListenClickOutside = ({
.filter((ref) => !!ref.current)
.some((ref) => ref.current?.contains(event.target as Node));
- if (
+ const shouldTrigger =
isListening &&
hasMouseDownHappened &&
!clickedOnAtLeastOneRef &&
!isMouseDownInside &&
- !isClickedOnExcluded
- ) {
+ !isClickedOnExcluded;
+
+ if (CLICK_OUTSIDE_DEBUG_MODE) {
+ // eslint-disable-next-line no-console
+ console.log('click outside compare refs', {
+ listenerId,
+ shouldTrigger,
+ isListening,
+ hasMouseDownHappened,
+ clickedOnAtLeastOneRef,
+ isMouseDownInside,
+ isClickedOnExcluded,
+ hotkeyScope,
+ enabled,
+ event,
+ });
+ }
+
+ if (shouldTrigger) {
callback(event);
}
}
@@ -213,12 +240,28 @@ export const useListenClickOutside = ({
return true;
});
- if (
+ const shouldTrigger =
!clickedOnAtLeastOneRef &&
!isMouseDownInside &&
isListening &&
- hasMouseDownHappened
- ) {
+ hasMouseDownHappened;
+
+ if (CLICK_OUTSIDE_DEBUG_MODE) {
+ // eslint-disable-next-line no-console
+ console.log('click outside compare pixel', {
+ listenerId,
+ shouldTrigger,
+ clickedOnAtLeastOneRef,
+ isMouseDownInside,
+ isListening,
+ hasMouseDownHappened,
+ hotkeyScope,
+ enabled,
+ event,
+ });
+ }
+
+ if (shouldTrigger) {
callback(event);
}
}
@@ -233,6 +276,7 @@ export const useListenClickOutside = ({
refs,
excludeClassNames,
callback,
+ listenerId,
],
);
diff --git a/packages/twenty-front/src/testing/components/StorybookFieldInputDropdownFocusIdSetterEffect.tsx b/packages/twenty-front/src/testing/components/StorybookFieldInputDropdownFocusIdSetterEffect.tsx
new file mode 100644
index 000000000..380d99370
--- /dev/null
+++ b/packages/twenty-front/src/testing/components/StorybookFieldInputDropdownFocusIdSetterEffect.tsx
@@ -0,0 +1,26 @@
+import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
+import { getDropdownFocusIdForRecordField } from '@/object-record/utils/getDropdownFocusIdForRecordField';
+import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious';
+import { useContext, useEffect } from 'react';
+
+export const StorybookFieldInputDropdownFocusIdSetterEffect = () => {
+ const { recordId, fieldDefinition } = useContext(FieldContext);
+
+ const { setActiveDropdownFocusIdAndMemorizePrevious } =
+ useSetActiveDropdownFocusIdAndMemorizePrevious();
+
+ const fieldDropdownFocusIdTableCell = getDropdownFocusIdForRecordField(
+ recordId,
+ fieldDefinition.fieldMetadataId,
+ 'table-cell',
+ );
+
+ useEffect(() => {
+ setActiveDropdownFocusIdAndMemorizePrevious(fieldDropdownFocusIdTableCell);
+ }, [
+ setActiveDropdownFocusIdAndMemorizePrevious,
+ fieldDropdownFocusIdTableCell,
+ ]);
+
+ return null;
+};