diff --git a/packages/twenty-front/.storybook/preview.tsx b/packages/twenty-front/.storybook/preview.tsx index aaaf7405d..b869153f4 100644 --- a/packages/twenty-front/.storybook/preview.tsx +++ b/packages/twenty-front/.storybook/preview.tsx @@ -7,10 +7,10 @@ import { useDarkMode } from 'storybook-dark-mode'; import { RootDecorator } from '../src/testing/decorators/RootDecorator'; import { mockedUserJWT } from '../src/testing/mock-data/jwt'; +import { ClickOutsideListenerContext } from '@/ui/utilities/pointer-event/contexts/ClickOutsideListenerContext'; import 'react-loading-skeleton/dist/skeleton.css'; import 'twenty-ui/style.css'; import { THEME_DARK, THEME_LIGHT, ThemeContextProvider } from 'twenty-ui/theme'; -import { ClickOutsideListenerContext } from '@/ui/utilities/pointer-event/contexts/ClickOutsideListenerContext'; initialize({ onUnhandledRequest: async (request: Request) => { @@ -48,7 +48,7 @@ const preview: Preview = { diff --git a/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx b/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx index 6fc83086d..e2aa47b19 100644 --- a/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx +++ b/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx @@ -1,6 +1,7 @@ import { ActionComponent } from '@/action-menu/actions/display/components/ActionComponent'; import { ActionScope } from '@/action-menu/actions/types/ActionScope'; import { ActionType } from '@/action-menu/actions/types/ActionType'; +import { ACTION_MENU_DROPDOWN_CLICK_OUTSIDE_ID } from '@/action-menu/constants/ActionMenuDropdownClickOutsideId'; import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext'; import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; import { recordIndexActionMenuDropdownPositionComponentState } from '@/action-menu/states/recordIndexActionMenuDropdownPositionComponentState'; @@ -81,7 +82,9 @@ export const RecordIndexActionMenuDropdown = () => { y: actionMenuDropdownPosition.y ?? 0, }} dropdownComponents={ - + { diff --git a/packages/twenty-front/src/modules/app/effect-components/PageChangeEffect.tsx b/packages/twenty-front/src/modules/app/effect-components/PageChangeEffect.tsx index 30d1850ba..079be631f 100644 --- a/packages/twenty-front/src/modules/app/effect-components/PageChangeEffect.tsx +++ b/packages/twenty-front/src/modules/app/effect-components/PageChangeEffect.tsx @@ -15,6 +15,7 @@ import { useExecuteTasksOnAnyLocationChange } from '@/app/hooks/useExecuteTasksO import { useRequestFreshCaptchaToken } from '@/captcha/hooks/useRequestFreshCaptchaToken'; import { isCaptchaScriptLoadedState } from '@/captcha/states/isCaptchaScriptLoadedState'; import { isCaptchaRequiredForPath } from '@/captcha/utils/isCaptchaRequiredForPath'; +import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId'; import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState'; import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState'; @@ -89,6 +90,12 @@ export const PageChangeEffect = () => { const { executeTasksOnAnyLocationChange } = useExecuteTasksOnAnyLocationChange(); + const { closeCommandMenu } = useCommandMenu(); + + useEffect(() => { + closeCommandMenu(); + }, [location.pathname, closeCommandMenu]); + useEffect(() => { if (!previousLocation || previousLocation !== location.pathname) { setPreviousLocation(location.pathname); diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuOpenContainer.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuOpenContainer.tsx index 8183807bd..073f87c71 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuOpenContainer.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuOpenContainer.tsx @@ -1,8 +1,10 @@ import { COMMAND_MENU_ANIMATION_VARIANTS } from '@/command-menu/constants/CommandMenuAnimationVariants'; +import { COMMAND_MENU_CLICK_OUTSIDE_ID } from '@/command-menu/constants/CommandMenuClickOutsideId'; import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; import { CommandMenuAnimationVariant } from '@/command-menu/types/CommandMenuAnimationVariant'; import { CommandMenuHotkeyScope } from '@/command-menu/types/CommandMenuHotkeyScope'; import { RootStackingContextZIndices } from '@/ui/layout/constants/RootStackingContextZIndices'; +import { PAGE_HEADER_COMMAND_MENU_BUTTON_CLICK_OUTSIDE_ID } from '@/ui/layout/page-header/constants/PageHeaderCommandMenuButtonClickOutsideId'; import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { useTheme } from '@emotion/react'; @@ -62,14 +64,14 @@ export const CommandMenuOpenContainer = ({ refs: [commandMenuRef], callback: handleClickOutside, listenerId: 'COMMAND_MENU_LISTENER_ID', - excludeClassNames: ['page-header-command-menu-button'], + excludedClickOutsideIds: [PAGE_HEADER_COMMAND_MENU_BUTTON_CLICK_OUTSIDE_ID], }); return ( { ); useListenClickOutside({ - excludeClassNames: [ - 'bottom-bar', - 'action-menu-dropdown', - 'command-menu', - 'modal-backdrop', - 'page-action-container', - 'record-board-card', + excludedClickOutsideIds: [ + ACTION_MENU_DROPDOWN_CLICK_OUTSIDE_ID, + COMMAND_MENU_CLICK_OUTSIDE_ID, + MODAL_BACKDROP_CLICK_OUTSIDE_ID, + PAGE_ACTION_CONTAINER_CLICK_OUTSIDE_ID, + RECORD_BOARD_CARD_CLICK_OUTSIDE_ID, ], listenerId: RECORD_BOARD_CLICK_OUTSIDE_LISTENER_ID, refs: [], diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx index 94437960a..8353f2262 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx @@ -13,6 +13,7 @@ import { ActionMenuDropdownHotkeyScope } from '@/action-menu/types/ActionMenuDro import { useOpenRecordInCommandMenu } from '@/command-menu/hooks/useOpenRecordInCommandMenu'; import { RecordBoardCardBody } from '@/object-record/record-board/record-board-card/components/RecordBoardCardBody'; import { RecordBoardCardHeader } from '@/object-record/record-board/record-board-card/components/RecordBoardCardHeader'; +import { RECORD_BOARD_CARD_CLICK_OUTSIDE_ID } from '@/object-record/record-board/record-board-card/constants/RecordBoardCardClickOutsideId'; import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext'; import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState'; import { AppPath } from '@/types/AppPath'; @@ -197,7 +198,7 @@ export const RecordBoardCard = () => { return ( diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCardDraggableContainer.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCardDraggableContainer.tsx index 7219f60b1..60b20a51c 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCardDraggableContainer.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCardDraggableContainer.tsx @@ -50,7 +50,6 @@ export const RecordBoardCardDraggableContainer = ({ {...draggableProvided?.dragHandleProps} // eslint-disable-next-line react/jsx-props-no-spreading {...draggableProvided?.draggableProps} - className="record-board-card" data-selectable-id={recordId} data-select-disable > diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/constants/RecordBoardCardClickOutsideId.ts b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/constants/RecordBoardCardClickOutsideId.ts new file mode 100644 index 000000000..0666cffdb --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/constants/RecordBoardCardClickOutsideId.ts @@ -0,0 +1 @@ +export const RECORD_BOARD_CARD_CLICK_OUTSIDE_ID = 'record-board-card'; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyFocusClickOutsideEffect.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyFocusClickOutsideEffect.tsx index d5ea707d9..0b617d834 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyFocusClickOutsideEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyFocusClickOutsideEffect.tsx @@ -1,8 +1,12 @@ +import { ACTION_MENU_DROPDOWN_CLICK_OUTSIDE_ID } from '@/action-menu/constants/ActionMenuDropdownClickOutsideId'; +import { COMMAND_MENU_CLICK_OUTSIDE_ID } from '@/command-menu/constants/CommandMenuClickOutsideId'; import { RecordIndexHotkeyScope } from '@/object-record/record-index/types/RecordIndexHotkeyScope'; import { RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-table/constants/RecordTableClickOutsideListenerId'; import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext'; import { useLeaveTableFocus } from '@/object-record/record-table/hooks/internal/useLeaveTableFocus'; import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope'; +import { MODAL_BACKDROP_CLICK_OUTSIDE_ID } from '@/ui/layout/modal/constants/ModalBackdropClickOutsideId'; +import { PAGE_ACTION_CONTAINER_CLICK_OUTSIDE_ID } from '@/ui/layout/page/constants/PageActionContainerClickOutsideId'; import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; @@ -23,12 +27,11 @@ export const RecordTableBodyFocusClickOutsideEffect = ({ const setHotkeyScope = useSetHotkeyScope(); useListenClickOutside({ - excludeClassNames: [ - 'bottom-bar', - 'action-menu-dropdown', - 'command-menu', - 'modal-backdrop', - 'page-action-container', + excludedClickOutsideIds: [ + ACTION_MENU_DROPDOWN_CLICK_OUTSIDE_ID, + COMMAND_MENU_CLICK_OUTSIDE_ID, + PAGE_ACTION_CONTAINER_CLICK_OUTSIDE_ID, + MODAL_BACKDROP_CLICK_OUTSIDE_ID, ], listenerId: RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID, refs: [tableBodyRef], diff --git a/packages/twenty-front/src/modules/ui/feedback/dialog-manager/components/Dialog.tsx b/packages/twenty-front/src/modules/ui/feedback/dialog-manager/components/Dialog.tsx index 9c2ea43fc..dae53c85c 100644 --- a/packages/twenty-front/src/modules/ui/feedback/dialog-manager/components/Dialog.tsx +++ b/packages/twenty-front/src/modules/ui/feedback/dialog-manager/components/Dialog.tsx @@ -4,6 +4,7 @@ import { Key } from 'ts-key-enum'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; +import { DIALOG_CLICK_OUTSIDE_ID } from '@/ui/feedback/dialog-manager/constants/DialogClickOutsideId'; import { DIALOG_LISTENER_ID } from '@/ui/feedback/dialog-manager/constants/DialogListenerId'; import { RootStackingContextZIndices } from '@/ui/layout/constants/RootStackingContextZIndices'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; @@ -138,6 +139,7 @@ export const Dialog = ({ animate="open" exit="closed" className={className} + data-click-outside-id={DIALOG_CLICK_OUTSIDE_ID} > { {dialogInternal.queue.map(({ buttons, children, id, message, title }) => ( closeDialog(id)} /> diff --git a/packages/twenty-front/src/modules/ui/feedback/dialog-manager/constants/DialogClickOutsideId.ts b/packages/twenty-front/src/modules/ui/feedback/dialog-manager/constants/DialogClickOutsideId.ts new file mode 100644 index 000000000..91ebfbf3b --- /dev/null +++ b/packages/twenty-front/src/modules/ui/feedback/dialog-manager/constants/DialogClickOutsideId.ts @@ -0,0 +1 @@ +export const DIALOG_CLICK_OUTSIDE_ID = 'dialog-manager-dialog'; diff --git a/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/components/SnackBar.tsx b/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/components/SnackBar.tsx index dbd068cce..815920ed2 100644 --- a/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/components/SnackBar.tsx +++ b/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/components/SnackBar.tsx @@ -203,6 +203,7 @@ export const SnackBar = ({ onMouseLeave={handleMouseLeave} title={message || defaultAriaLabelByVariant[variant]} {...{ className, id, role, variant }} + data-globally-prevent-click-outside > { if (activeDropdownFocusId !== dropdownId) return; @@ -118,7 +117,7 @@ export const DropdownContent = ({ maxWidth: dropdownMaxWidth, }; - const { excludeClassName } = useContext(ClickOutsideListenerContext); + const { excludedClickOutsideId } = useContext(ClickOutsideListenerContext); return ( <> @@ -127,24 +126,23 @@ export const DropdownContent = ({ )} -
- - - - {dropdownComponents} - - - -
+ + + + {dropdownComponents} + + +
); diff --git a/packages/twenty-front/src/modules/ui/layout/modal/components/ConfirmationModal.tsx b/packages/twenty-front/src/modules/ui/layout/modal/components/ConfirmationModal.tsx index 8ebf60edf..3fb21adf2 100644 --- a/packages/twenty-front/src/modules/ui/layout/modal/components/ConfirmationModal.tsx +++ b/packages/twenty-front/src/modules/ui/layout/modal/components/ConfirmationModal.tsx @@ -119,7 +119,7 @@ export const ConfirmationModal = ({ isClosable={true} padding="large" modalVariant={modalVariant} - className="confirmation-modal" + data-globally-prevent-click-outside > diff --git a/packages/twenty-front/src/modules/ui/layout/modal/components/Modal.tsx b/packages/twenty-front/src/modules/ui/layout/modal/components/Modal.tsx index a956a4447..451f80568 100644 --- a/packages/twenty-front/src/modules/ui/layout/modal/components/Modal.tsx +++ b/packages/twenty-front/src/modules/ui/layout/modal/components/Modal.tsx @@ -4,7 +4,8 @@ import { ModalHotkeyScope } from '@/ui/layout/modal/components/types/ModalHotkey import { ModalComponentInstanceContext } from '@/ui/layout/modal/contexts/ModalComponentInstanceContext'; import { isModalOpenedComponentState } from '@/ui/layout/modal/states/isModalOpenedComponentState'; -import { MODAL_CLICK_OUTSIDE_LISTENER_EXCLUDED_CLASS_NAME } from '@/ui/layout/modal/constants/ModalClickOutsideListenerExcludedClassName'; +import { MODAL_BACKDROP_CLICK_OUTSIDE_ID } from '@/ui/layout/modal/constants/ModalBackdropClickOutsideId'; +import { MODAL_CLICK_OUTSIDE_LISTENER_EXCLUDED_ID } from '@/ui/layout/modal/constants/ModalClickOutsideListenerExcludedClassName'; import { useModal } from '@/ui/layout/modal/hooks/useModal'; import { ClickOutsideListenerContext } from '@/ui/utilities/pointer-event/contexts/ClickOutsideListenerContext'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; @@ -229,8 +230,7 @@ export const Modal = ({ > diff --git a/packages/twenty-front/src/modules/ui/layout/modal/components/ModalHotkeysAndClickOutsideEffect.tsx b/packages/twenty-front/src/modules/ui/layout/modal/components/ModalHotkeysAndClickOutsideEffect.tsx index 18f8df02a..db8cccdf9 100644 --- a/packages/twenty-front/src/modules/ui/layout/modal/components/ModalHotkeysAndClickOutsideEffect.tsx +++ b/packages/twenty-front/src/modules/ui/layout/modal/components/ModalHotkeysAndClickOutsideEffect.tsx @@ -1,5 +1,6 @@ +import { DIALOG_CLICK_OUTSIDE_ID } from '@/ui/feedback/dialog-manager/constants/DialogClickOutsideId'; import { ModalHotkeyScope } from '@/ui/layout/modal/components/types/ModalHotkeyScope'; -import { MODAL_CLICK_OUTSIDE_LISTENER_EXCLUDED_CLASS_NAME } from '@/ui/layout/modal/constants/ModalClickOutsideListenerExcludedClassName'; +import { MODAL_CLICK_OUTSIDE_LISTENER_EXCLUDED_ID } from '@/ui/layout/modal/constants/ModalClickOutsideListenerExcludedClassName'; import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { Key } from 'ts-key-enum'; @@ -45,9 +46,9 @@ export const ModalHotkeysAndClickOutsideEffect = ({ useListenClickOutside({ refs: [modalRef], - excludeClassNames: [ - MODAL_CLICK_OUTSIDE_LISTENER_EXCLUDED_CLASS_NAME, - 'dialog-manager-dialog', + excludedClickOutsideIds: [ + MODAL_CLICK_OUTSIDE_LISTENER_EXCLUDED_ID, + DIALOG_CLICK_OUTSIDE_ID, ], listenerId: `MODAL_CLICK_OUTSIDE_LISTENER_ID_${modalId}`, callback: () => { diff --git a/packages/twenty-front/src/modules/ui/layout/modal/constants/ModalBackdropClickOutsideId.ts b/packages/twenty-front/src/modules/ui/layout/modal/constants/ModalBackdropClickOutsideId.ts new file mode 100644 index 000000000..7f6a41428 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/layout/modal/constants/ModalBackdropClickOutsideId.ts @@ -0,0 +1 @@ +export const MODAL_BACKDROP_CLICK_OUTSIDE_ID = 'modal-backdrop'; diff --git a/packages/twenty-front/src/modules/ui/layout/modal/constants/ModalClickOutsideListenerExcludedClassName.ts b/packages/twenty-front/src/modules/ui/layout/modal/constants/ModalClickOutsideListenerExcludedClassName.ts index 848b5a6ea..bc8f72189 100644 --- a/packages/twenty-front/src/modules/ui/layout/modal/constants/ModalClickOutsideListenerExcludedClassName.ts +++ b/packages/twenty-front/src/modules/ui/layout/modal/constants/ModalClickOutsideListenerExcludedClassName.ts @@ -1,2 +1,2 @@ -export const MODAL_CLICK_OUTSIDE_LISTENER_EXCLUDED_CLASS_NAME = +export const MODAL_CLICK_OUTSIDE_LISTENER_EXCLUDED_ID = 'modal-click-outside-listener-excluded'; diff --git a/packages/twenty-front/src/modules/ui/layout/page-header/components/PageHeaderToggleCommandMenuButton.tsx b/packages/twenty-front/src/modules/ui/layout/page-header/components/PageHeaderToggleCommandMenuButton.tsx index 59da40d5b..2df69e11b 100644 --- a/packages/twenty-front/src/modules/ui/layout/page-header/components/PageHeaderToggleCommandMenuButton.tsx +++ b/packages/twenty-front/src/modules/ui/layout/page-header/components/PageHeaderToggleCommandMenuButton.tsx @@ -1,6 +1,7 @@ import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState'; import { RootStackingContextZIndices } from '@/ui/layout/constants/RootStackingContextZIndices'; +import { PAGE_HEADER_COMMAND_MENU_BUTTON_CLICK_OUTSIDE_ID } from '@/ui/layout/page-header/constants/PageHeaderCommandMenuButtonClickOutsideId'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { i18n } from '@lingui/core'; @@ -124,7 +125,9 @@ export const PageHeaderToggleCommandMenuButton = () => { animatedSvg={ } - className="page-header-command-menu-button" + data-click-outside-id={ + PAGE_HEADER_COMMAND_MENU_BUTTON_CLICK_OUTSIDE_ID + } dataTestId="page-header-command-menu-button" size={isMobile ? 'medium' : 'small'} variant="secondary" diff --git a/packages/twenty-front/src/modules/ui/layout/page-header/constants/PageHeaderCommandMenuButtonClickOutsideId.ts b/packages/twenty-front/src/modules/ui/layout/page-header/constants/PageHeaderCommandMenuButtonClickOutsideId.ts new file mode 100644 index 000000000..c1420a466 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/layout/page-header/constants/PageHeaderCommandMenuButtonClickOutsideId.ts @@ -0,0 +1,2 @@ +export const PAGE_HEADER_COMMAND_MENU_BUTTON_CLICK_OUTSIDE_ID = + 'page-header-command-menu-button'; diff --git a/packages/twenty-front/src/modules/ui/layout/page/components/PageHeader.tsx b/packages/twenty-front/src/modules/ui/layout/page/components/PageHeader.tsx index 39a85be26..b1a0d2f05 100644 --- a/packages/twenty-front/src/modules/ui/layout/page/components/PageHeader.tsx +++ b/packages/twenty-front/src/modules/ui/layout/page/components/PageHeader.tsx @@ -5,6 +5,7 @@ import { useRecoilValue } from 'recoil'; import { NavigationDrawerCollapseButton } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerCollapseButton'; +import { PAGE_ACTION_CONTAINER_CLICK_OUTSIDE_ID } from '@/ui/layout/page/constants/PageActionContainerClickOutsideId'; import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import { @@ -144,7 +145,9 @@ export const PageHeader = ({ - + {children} diff --git a/packages/twenty-front/src/modules/ui/layout/page/constants/PageActionContainerClickOutsideId.ts b/packages/twenty-front/src/modules/ui/layout/page/constants/PageActionContainerClickOutsideId.ts new file mode 100644 index 000000000..4113337b4 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/layout/page/constants/PageActionContainerClickOutsideId.ts @@ -0,0 +1 @@ +export const PAGE_ACTION_CONTAINER_CLICK_OUTSIDE_ID = 'page-action-container'; diff --git a/packages/twenty-front/src/modules/ui/utilities/pointer-event/contexts/ClickOutsideListenerContext.tsx b/packages/twenty-front/src/modules/ui/utilities/pointer-event/contexts/ClickOutsideListenerContext.tsx index a2bfb9f46..414617142 100644 --- a/packages/twenty-front/src/modules/ui/utilities/pointer-event/contexts/ClickOutsideListenerContext.tsx +++ b/packages/twenty-front/src/modules/ui/utilities/pointer-event/contexts/ClickOutsideListenerContext.tsx @@ -1,10 +1,10 @@ import { createContext } from 'react'; type ClickOutsideListenerContextType = { - excludeClassName: string | undefined; + excludedClickOutsideId: string | undefined; }; export const ClickOutsideListenerContext = createContext({ - excludeClassName: undefined, + excludedClickOutsideId: undefined, }); 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 371b2a397..8178ee40a 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,12 +4,13 @@ import { clickOutsideListenerMouseDownHappenedComponentState } from '@/ui/utilit import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import React, { useEffect } from 'react'; import { useRecoilCallback } from 'recoil'; +import { isDefined } from 'twenty-shared/utils'; const CLICK_OUTSIDE_DEBUG_MODE = false; export type ClickOutsideListenerProps = { refs: Array>; - excludeClassNames?: string[]; + excludedClickOutsideIds?: string[]; callback: (event: MouseEvent | TouchEvent) => void; listenerId: string; enabled?: boolean; @@ -17,7 +18,7 @@ export type ClickOutsideListenerProps = { export const useListenClickOutside = ({ refs, - excludeClassNames, + excludedClickOutsideIds, callback, listenerId, enabled = true, @@ -90,12 +91,18 @@ export const useListenClickOutside = ({ let currentElement: HTMLElement | null = clickedElement; while (currentElement) { - const currentClassList = currentElement.classList; + const currentDataAttributes = currentElement.dataset; + + const isGloballyExcluded = + currentDataAttributes?.globallyPreventClickOutside === 'true'; + + const clickOutsideId = currentDataAttributes?.clickOutsideId; isClickedOnExcluded = - excludeClassNames?.some((className) => - currentClassList.contains(className), - ) ?? false; + isGloballyExcluded || + (isDefined(clickOutsideId) && + isDefined(excludedClickOutsideIds) && + excludedClickOutsideIds.includes(clickOutsideId)); if (isClickedOnExcluded) { break; @@ -140,7 +147,7 @@ export const useListenClickOutside = ({ clickOutsideListenerIsMouseDownInsideState, clickOutsideListenerMouseDownHappenedState, refs, - excludeClassNames, + excludedClickOutsideIds, callback, listenerId, ],