Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9f346fb753 | |||
| e15f1b4688 |
@@ -10,6 +10,7 @@ import { mockedUserJWT } from '../src/testing/mock-data/jwt';
|
||||
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) => {
|
||||
@@ -46,7 +47,11 @@ const preview: Preview = {
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<ThemeContextProvider theme={theme}>
|
||||
<Story />
|
||||
<ClickOutsideListenerContext.Provider
|
||||
value={{ excludeClassName: undefined }}
|
||||
>
|
||||
<Story />
|
||||
</ClickOutsideListenerContext.Provider>
|
||||
</ThemeContextProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
||||
@@ -5,6 +5,7 @@ import { AppErrorBoundary } from '@/error-handler/components/AppErrorBoundary';
|
||||
import { AppRootErrorFallback } from '@/error-handler/components/AppRootErrorFallback';
|
||||
import { ExceptionHandlerProvider } from '@/error-handler/components/ExceptionHandlerProvider';
|
||||
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
|
||||
import { ClickOutsideListenerContext } from '@/ui/utilities/pointer-event/contexts/ClickOutsideListenerContext';
|
||||
import { i18n } from '@lingui/core';
|
||||
import { I18nProvider } from '@lingui/react';
|
||||
import { HelmetProvider } from 'react-helmet-async';
|
||||
@@ -28,7 +29,11 @@ export const App = () => {
|
||||
<IconsProvider>
|
||||
<ExceptionHandlerProvider>
|
||||
<HelmetProvider>
|
||||
<AppRouter />
|
||||
<ClickOutsideListenerContext.Provider
|
||||
value={{ excludeClassName: undefined }}
|
||||
>
|
||||
<AppRouter />
|
||||
</ClickOutsideListenerContext.Provider>
|
||||
</HelmetProvider>
|
||||
</ExceptionHandlerProvider>
|
||||
</IconsProvider>
|
||||
|
||||
+1
-6
@@ -1,4 +1,3 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useCallback, useContext } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
@@ -35,10 +34,6 @@ type RecordDetailRelationSectionDropdownProps = {
|
||||
loading: boolean;
|
||||
};
|
||||
|
||||
const StyledAddDropdown = styled(Dropdown)`
|
||||
margin-left: auto;
|
||||
`;
|
||||
|
||||
export const RecordDetailRelationSectionDropdown = ({
|
||||
loading,
|
||||
}: RecordDetailRelationSectionDropdownProps) => {
|
||||
@@ -196,7 +191,7 @@ export const RecordDetailRelationSectionDropdown = ({
|
||||
|
||||
return (
|
||||
<DropdownScope dropdownScopeId={dropdownId}>
|
||||
<StyledAddDropdown
|
||||
<Dropdown
|
||||
dropdownId={dropdownId}
|
||||
dropdownPlacement="left-start"
|
||||
onClose={handleCloseRelationPickerDropdown}
|
||||
|
||||
+7
-2
@@ -8,6 +8,7 @@ import { onToggleColumnSortComponentState } from '@/object-record/record-table/s
|
||||
import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector';
|
||||
import { useToggleScrollWrapper } from '@/ui/utilities/scroll/hooks/useToggleScrollWrapper';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import {
|
||||
IconArrowLeft,
|
||||
@@ -24,6 +25,10 @@ export type RecordTableColumnHeadDropdownMenuProps = {
|
||||
column: ColumnDefinition<FieldMetadata>;
|
||||
};
|
||||
|
||||
const StyledDropdownMenuItemsContainer = styled(DropdownMenuItemsContainer)`
|
||||
z-index: ${({ theme }) => theme.lastLayerZIndex};
|
||||
`;
|
||||
|
||||
export const RecordTableColumnHeadDropdownMenu = ({
|
||||
column,
|
||||
}: RecordTableColumnHeadDropdownMenuProps) => {
|
||||
@@ -102,7 +107,7 @@ export const RecordTableColumnHeadDropdownMenu = ({
|
||||
const canHide = column.isLabelIdentifier !== true;
|
||||
|
||||
return (
|
||||
<DropdownMenuItemsContainer>
|
||||
<StyledDropdownMenuItemsContainer>
|
||||
{isFilterable && (
|
||||
<MenuItem
|
||||
LeftIcon={IconFilter}
|
||||
@@ -139,6 +144,6 @@ export const RecordTableColumnHeadDropdownMenu = ({
|
||||
text={t`Hide`}
|
||||
/>
|
||||
)}
|
||||
</DropdownMenuItemsContainer>
|
||||
</StyledDropdownMenuItemsContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+1
-8
@@ -2,7 +2,6 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'
|
||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { useToggleScrollWrapper } from '@/ui/utilities/scroll/hooks/useToggleScrollWrapper';
|
||||
import styled from '@emotion/styled';
|
||||
import { useCallback } from 'react';
|
||||
import { RecordTableColumnHead } from './RecordTableColumnHead';
|
||||
import { RecordTableColumnHeadDropdownMenu } from './RecordTableColumnHeadDropdownMenu';
|
||||
@@ -11,12 +10,6 @@ type RecordTableColumnHeadWithDropdownProps = {
|
||||
column: ColumnDefinition<FieldMetadata>;
|
||||
};
|
||||
|
||||
const StyledDropdown = styled(Dropdown)`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
z-index: ${({ theme }) => theme.lastLayerZIndex};
|
||||
`;
|
||||
|
||||
export const RecordTableColumnHeadWithDropdown = ({
|
||||
column,
|
||||
}: RecordTableColumnHeadWithDropdownProps) => {
|
||||
@@ -34,7 +27,7 @@ export const RecordTableColumnHeadWithDropdown = ({
|
||||
}, [toggleScrollXWrapper, toggleScrollYWrapper]);
|
||||
|
||||
return (
|
||||
<StyledDropdown
|
||||
<Dropdown
|
||||
onOpen={handleDropdownOpen}
|
||||
onClose={handleDropdownClose}
|
||||
dropdownId={column.fieldMetadataId + '-header'}
|
||||
|
||||
@@ -40,7 +40,6 @@ const StyledClickableComponent = styled.div<{
|
||||
`;
|
||||
|
||||
export type DropdownProps = {
|
||||
className?: string;
|
||||
clickableComponent?: ReactNode;
|
||||
clickableComponentWidth?: Width;
|
||||
dropdownComponents: ReactNode;
|
||||
@@ -57,11 +56,9 @@ export type DropdownProps = {
|
||||
onClickOutside?: () => void;
|
||||
onClose?: () => void;
|
||||
onOpen?: () => void;
|
||||
avoidPortal?: boolean;
|
||||
};
|
||||
|
||||
export const Dropdown = ({
|
||||
className,
|
||||
clickableComponent,
|
||||
dropdownComponents,
|
||||
dropdownWidth,
|
||||
@@ -74,7 +71,6 @@ export const Dropdown = ({
|
||||
onClickOutside,
|
||||
onClose,
|
||||
onOpen,
|
||||
avoidPortal,
|
||||
clickableComponentWidth = 'auto',
|
||||
}: DropdownProps) => {
|
||||
const { isDropdownOpen, toggleDropdown } = useDropdown(dropdownId);
|
||||
@@ -174,7 +170,6 @@ export const Dropdown = ({
|
||||
)}
|
||||
{isDropdownOpen && (
|
||||
<DropdownContent
|
||||
className={className}
|
||||
floatingStyles={floatingStyles}
|
||||
dropdownWidth={dropdownWidth}
|
||||
dropdownComponents={dropdownComponents}
|
||||
@@ -185,7 +180,6 @@ export const Dropdown = ({
|
||||
hotkey={hotkey}
|
||||
onClickOutside={onClickOutside}
|
||||
onHotkeyTriggered={toggleDropdown}
|
||||
avoidPortal={avoidPortal}
|
||||
/>
|
||||
)}
|
||||
<DropdownOnToggleEffect
|
||||
|
||||
+9
-27
@@ -9,6 +9,7 @@ import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContaine
|
||||
import { HotkeyEffect } from '@/ui/utilities/hotkey/components/HotkeyEffect';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
import { ClickOutsideListenerContext } from '@/ui/utilities/pointer-event/contexts/ClickOutsideListenerContext';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import styled from '@emotion/styled';
|
||||
@@ -17,7 +18,7 @@ import {
|
||||
Placement,
|
||||
UseFloatingReturn,
|
||||
} from '@floating-ui/react';
|
||||
import { useEffect } from 'react';
|
||||
import { useContext, useEffect } from 'react';
|
||||
import { Keys } from 'react-hotkeys-hook';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
@@ -28,7 +29,6 @@ export const StyledDropdownContentContainer = styled.div`
|
||||
`;
|
||||
|
||||
export type DropdownContentProps = {
|
||||
className?: string;
|
||||
dropdownId: string;
|
||||
dropdownPlacement: Placement;
|
||||
floatingUiRefs: UseFloatingReturn['refs'];
|
||||
@@ -43,11 +43,9 @@ export type DropdownContentProps = {
|
||||
dropdownWidth?: `${string}px` | `${number}%` | 'auto' | number;
|
||||
dropdownComponents: React.ReactNode;
|
||||
parentDropdownId?: string;
|
||||
avoidPortal?: boolean;
|
||||
};
|
||||
|
||||
export const DropdownContent = ({
|
||||
className,
|
||||
dropdownId,
|
||||
dropdownPlacement,
|
||||
floatingUiRefs,
|
||||
@@ -58,7 +56,6 @@ export const DropdownContent = ({
|
||||
onHotkeyTriggered,
|
||||
dropdownWidth,
|
||||
dropdownComponents,
|
||||
avoidPortal,
|
||||
}: DropdownContentProps) => {
|
||||
const { isDropdownOpen, closeDropdown, setDropdownPlacement } =
|
||||
useDropdown(dropdownId);
|
||||
@@ -121,30 +118,16 @@ export const DropdownContent = ({
|
||||
maxWidth: dropdownMaxWidth,
|
||||
};
|
||||
|
||||
const { excludeClassName } = useContext(ClickOutsideListenerContext);
|
||||
|
||||
return (
|
||||
<>
|
||||
{hotkey && onHotkeyTriggered && (
|
||||
<HotkeyEffect hotkey={hotkey} onHotkeyTriggered={onHotkeyTriggered} />
|
||||
)}
|
||||
{avoidPortal ? (
|
||||
<StyledDropdownContentContainer
|
||||
ref={floatingUiRefs.setFloating}
|
||||
style={dropdownMenuStyles}
|
||||
role="listbox"
|
||||
id={`${dropdownId}-options`}
|
||||
>
|
||||
<OverlayContainer>
|
||||
<DropdownMenu
|
||||
className={className}
|
||||
width={dropdownWidth}
|
||||
data-select-disable
|
||||
>
|
||||
{dropdownComponents}
|
||||
</DropdownMenu>
|
||||
</OverlayContainer>
|
||||
</StyledDropdownContentContainer>
|
||||
) : (
|
||||
<FloatingPortal>
|
||||
|
||||
<FloatingPortal>
|
||||
<div className={excludeClassName}>
|
||||
<StyledDropdownContentContainer
|
||||
ref={floatingUiRefs.setFloating}
|
||||
style={dropdownMenuStyles}
|
||||
@@ -154,7 +137,6 @@ export const DropdownContent = ({
|
||||
<OverlayContainer>
|
||||
<DropdownMenu
|
||||
id={dropdownId}
|
||||
className={className}
|
||||
width={dropdownWidth}
|
||||
data-select-disable
|
||||
>
|
||||
@@ -162,8 +144,8 @@ export const DropdownContent = ({
|
||||
</DropdownMenu>
|
||||
</OverlayContainer>
|
||||
</StyledDropdownContentContainer>
|
||||
</FloatingPortal>
|
||||
)}
|
||||
</div>
|
||||
</FloatingPortal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,7 +4,9 @@ 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 { useModal } from '@/ui/layout/modal/hooks/useModal';
|
||||
import { ClickOutsideListenerContext } from '@/ui/utilities/pointer-event/contexts/ClickOutsideListenerContext';
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { css, useTheme } from '@emotion/react';
|
||||
@@ -225,36 +227,43 @@ export const Modal = ({
|
||||
instanceId: modalId,
|
||||
}}
|
||||
>
|
||||
<ModalHotkeysAndClickOutsideEffect
|
||||
modalId={modalId}
|
||||
modalRef={modalRef}
|
||||
onEnter={onEnter}
|
||||
isClosable={isClosable}
|
||||
onClose={handleClose}
|
||||
/>
|
||||
<StyledBackDrop
|
||||
data-testid="modal-backdrop"
|
||||
className="modal-backdrop"
|
||||
onMouseDown={stopEventPropagation}
|
||||
modalVariant={modalVariant}
|
||||
<ClickOutsideListenerContext.Provider
|
||||
value={{
|
||||
excludeClassName:
|
||||
MODAL_CLICK_OUTSIDE_LISTENER_EXCLUDED_CLASS_NAME,
|
||||
}}
|
||||
>
|
||||
<StyledModalDiv
|
||||
ref={modalRef}
|
||||
size={size}
|
||||
padding={padding}
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
exit="exit"
|
||||
layout
|
||||
<ModalHotkeysAndClickOutsideEffect
|
||||
modalId={modalId}
|
||||
modalRef={modalRef}
|
||||
onEnter={onEnter}
|
||||
isClosable={isClosable}
|
||||
onClose={handleClose}
|
||||
/>
|
||||
<StyledBackDrop
|
||||
data-testid="modal-backdrop"
|
||||
className="modal-backdrop"
|
||||
onMouseDown={stopEventPropagation}
|
||||
modalVariant={modalVariant}
|
||||
variants={modalAnimation}
|
||||
transition={{ duration: theme.animation.duration.normal }}
|
||||
className={className}
|
||||
isMobile={isMobile}
|
||||
>
|
||||
{children}
|
||||
</StyledModalDiv>
|
||||
</StyledBackDrop>
|
||||
<StyledModalDiv
|
||||
ref={modalRef}
|
||||
size={size}
|
||||
padding={padding}
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
exit="exit"
|
||||
layout
|
||||
modalVariant={modalVariant}
|
||||
variants={modalAnimation}
|
||||
transition={{ duration: theme.animation.duration.normal }}
|
||||
className={className}
|
||||
isMobile={isMobile}
|
||||
>
|
||||
{children}
|
||||
</StyledModalDiv>
|
||||
</StyledBackDrop>
|
||||
</ClickOutsideListenerContext.Provider>
|
||||
</ModalComponentInstanceContext.Provider>
|
||||
)}
|
||||
</>
|
||||
|
||||
+3
-5
@@ -1,9 +1,7 @@
|
||||
import { ModalHotkeyScope } from '@/ui/layout/modal/components/types/ModalHotkeyScope';
|
||||
import { MODAL_CLICK_OUTSIDE_LISTENER_EXCLUDED_CLASS_NAME } from '@/ui/layout/modal/constants/ModalClickOutsideListenerExcludedClassName';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import {
|
||||
ClickOutsideMode,
|
||||
useListenClickOutside,
|
||||
} from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
import { Key } from 'ts-key-enum';
|
||||
|
||||
type ModalHotkeysAndClickOutsideEffectProps = {
|
||||
@@ -41,13 +39,13 @@ export const ModalHotkeysAndClickOutsideEffect = ({
|
||||
|
||||
useListenClickOutside({
|
||||
refs: [modalRef],
|
||||
excludeClassNames: [MODAL_CLICK_OUTSIDE_LISTENER_EXCLUDED_CLASS_NAME],
|
||||
listenerId: `MODAL_CLICK_OUTSIDE_LISTENER_ID_${modalId}`,
|
||||
callback: () => {
|
||||
if (isClosable && onClose !== undefined) {
|
||||
onClose();
|
||||
}
|
||||
},
|
||||
mode: ClickOutsideMode.compareHTMLRef,
|
||||
});
|
||||
|
||||
return null;
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
export const MODAL_CLICK_OUTSIDE_LISTENER_EXCLUDED_CLASS_NAME =
|
||||
'modal-click-outside-listener-excluded';
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
type ClickOutsideListenerContextType = {
|
||||
excludeClassName: string | undefined;
|
||||
};
|
||||
|
||||
export const ClickOutsideListenerContext =
|
||||
createContext<ClickOutsideListenerContextType>({
|
||||
excludeClassName: undefined,
|
||||
});
|
||||
+2
-53
@@ -1,16 +1,11 @@
|
||||
import { fireEvent, renderHook } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import React, { act } from 'react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import {
|
||||
ClickOutsideMode,
|
||||
useListenClickOutside,
|
||||
} from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
const containerRef = React.createRef<HTMLDivElement>();
|
||||
const nullRef = React.createRef<HTMLDivElement>();
|
||||
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<RecoilRoot>
|
||||
@@ -41,28 +36,6 @@ describe('useListenClickOutside', () => {
|
||||
expect(callback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should trigger the callback when clicking outside the specified ref with pixel comparison', async () => {
|
||||
const callback = jest.fn();
|
||||
|
||||
renderHook(
|
||||
() =>
|
||||
useListenClickOutside({
|
||||
refs: [nullRef],
|
||||
callback,
|
||||
mode: ClickOutsideMode.comparePixels,
|
||||
listenerId,
|
||||
}),
|
||||
{ wrapper: Wrapper },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
fireEvent.mouseDown(document);
|
||||
fireEvent.click(document);
|
||||
});
|
||||
|
||||
expect(callback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not call the callback when clicking inside the specified refs using default comparison', () => {
|
||||
const callback = jest.fn();
|
||||
|
||||
@@ -85,28 +58,4 @@ describe('useListenClickOutside', () => {
|
||||
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not call the callback when clicking inside the specified refs using pixel comparison', () => {
|
||||
const callback = jest.fn();
|
||||
|
||||
renderHook(
|
||||
() =>
|
||||
useListenClickOutside({
|
||||
refs: [containerRef],
|
||||
callback,
|
||||
mode: ClickOutsideMode.comparePixels,
|
||||
listenerId,
|
||||
}),
|
||||
{ wrapper: Wrapper },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
if (isDefined(containerRef.current)) {
|
||||
fireEvent.mouseDown(containerRef.current);
|
||||
fireEvent.click(containerRef.current);
|
||||
}
|
||||
});
|
||||
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
+29
-148
@@ -7,16 +7,10 @@ import { useRecoilCallback } from 'recoil';
|
||||
|
||||
const CLICK_OUTSIDE_DEBUG_MODE = false;
|
||||
|
||||
export enum ClickOutsideMode {
|
||||
comparePixels = 'comparePixels',
|
||||
compareHTMLRef = 'compareHTMLRef',
|
||||
}
|
||||
|
||||
export type ClickOutsideListenerProps<T extends Element> = {
|
||||
refs: Array<React.RefObject<T>>;
|
||||
excludeClassNames?: string[];
|
||||
callback: (event: MouseEvent | TouchEvent) => void;
|
||||
mode?: ClickOutsideMode;
|
||||
listenerId: string;
|
||||
enabled?: boolean;
|
||||
};
|
||||
@@ -25,7 +19,6 @@ export const useListenClickOutside = <T extends Element>({
|
||||
refs,
|
||||
excludeClassNames,
|
||||
callback,
|
||||
mode = ClickOutsideMode.compareHTMLRef,
|
||||
listenerId,
|
||||
enabled = true,
|
||||
}: ClickOutsideListenerProps<T>) => {
|
||||
@@ -60,67 +53,16 @@ export const useListenClickOutside = <T extends Element>({
|
||||
return;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case ClickOutsideMode.compareHTMLRef: {
|
||||
const clickedOnAtLeastOneRef = refs
|
||||
.filter((ref) => !!ref.current)
|
||||
.some((ref) => ref.current?.contains(event.target as Node));
|
||||
const clickedOnAtLeastOneRef = refs
|
||||
.filter((ref) => !!ref.current)
|
||||
.some((ref) => ref.current?.contains(event.target as Node));
|
||||
|
||||
set(
|
||||
clickOutsideListenerIsMouseDownInsideState,
|
||||
clickedOnAtLeastOneRef,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
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 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;
|
||||
});
|
||||
|
||||
set(
|
||||
clickOutsideListenerIsMouseDownInsideState,
|
||||
clickedOnAtLeastOneRef,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
set(clickOutsideListenerIsMouseDownInsideState, clickedOnAtLeastOneRef);
|
||||
},
|
||||
[
|
||||
clickOutsideListenerIsActivatedState,
|
||||
clickOutsideListenerMouseDownHappenedState,
|
||||
enabled,
|
||||
mode,
|
||||
refs,
|
||||
clickOutsideListenerIsMouseDownInsideState,
|
||||
],
|
||||
@@ -162,94 +104,34 @@ export const useListenClickOutside = <T extends Element>({
|
||||
currentElement = currentElement.parentElement;
|
||||
}
|
||||
|
||||
if (mode === ClickOutsideMode.compareHTMLRef) {
|
||||
const clickedOnAtLeastOneRef = refs
|
||||
.filter((ref) => !!ref.current)
|
||||
.some((ref) => ref.current?.contains(event.target as Node));
|
||||
const clickedOnAtLeastOneRef = refs
|
||||
.filter((ref) => !!ref.current)
|
||||
.some((ref) => ref.current?.contains(event.target as Node));
|
||||
|
||||
const shouldTrigger =
|
||||
isListening &&
|
||||
hasMouseDownHappened &&
|
||||
!clickedOnAtLeastOneRef &&
|
||||
!isMouseDownInside &&
|
||||
!isClickedOnExcluded;
|
||||
const shouldTrigger =
|
||||
isListening &&
|
||||
hasMouseDownHappened &&
|
||||
!clickedOnAtLeastOneRef &&
|
||||
!isMouseDownInside &&
|
||||
!isClickedOnExcluded;
|
||||
|
||||
if (CLICK_OUTSIDE_DEBUG_MODE) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('click outside compare ref', {
|
||||
listenerId,
|
||||
shouldTrigger,
|
||||
clickedOnAtLeastOneRef,
|
||||
isMouseDownInside,
|
||||
isListening,
|
||||
hasMouseDownHappened,
|
||||
isClickedOnExcluded,
|
||||
enabled,
|
||||
event,
|
||||
});
|
||||
}
|
||||
|
||||
if (shouldTrigger) {
|
||||
callback(event);
|
||||
}
|
||||
if (CLICK_OUTSIDE_DEBUG_MODE) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('click outside compare ref', {
|
||||
listenerId,
|
||||
shouldTrigger,
|
||||
clickedOnAtLeastOneRef,
|
||||
isMouseDownInside,
|
||||
isListening,
|
||||
hasMouseDownHappened,
|
||||
isClickedOnExcluded,
|
||||
enabled,
|
||||
event,
|
||||
});
|
||||
}
|
||||
|
||||
if (mode === 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 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;
|
||||
});
|
||||
|
||||
const shouldTrigger =
|
||||
!clickedOnAtLeastOneRef &&
|
||||
!isMouseDownInside &&
|
||||
isListening &&
|
||||
hasMouseDownHappened &&
|
||||
!isClickedOnExcluded;
|
||||
|
||||
if (CLICK_OUTSIDE_DEBUG_MODE) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('click outside compare pixel', {
|
||||
listenerId,
|
||||
shouldTrigger,
|
||||
clickedOnAtLeastOneRef,
|
||||
isMouseDownInside,
|
||||
isListening,
|
||||
hasMouseDownHappened,
|
||||
isClickedOnExcluded,
|
||||
enabled,
|
||||
event,
|
||||
});
|
||||
}
|
||||
|
||||
if (shouldTrigger) {
|
||||
callback(event);
|
||||
}
|
||||
if (shouldTrigger) {
|
||||
callback(event);
|
||||
}
|
||||
},
|
||||
[
|
||||
@@ -257,7 +139,6 @@ export const useListenClickOutside = <T extends Element>({
|
||||
enabled,
|
||||
clickOutsideListenerIsMouseDownInsideState,
|
||||
clickOutsideListenerMouseDownHappenedState,
|
||||
mode,
|
||||
refs,
|
||||
excludeClassNames,
|
||||
callback,
|
||||
@@ -291,5 +172,5 @@ export const useListenClickOutside = <T extends Element>({
|
||||
capture: true,
|
||||
});
|
||||
};
|
||||
}, [refs, callback, mode, handleClickOutside, handleMouseDown]);
|
||||
}, [refs, callback, handleClickOutside, handleMouseDown]);
|
||||
};
|
||||
|
||||
@@ -51,7 +51,6 @@ export const ViewBarFilterDropdown = ({
|
||||
dropdownHotkeyScope={hotkeyScope}
|
||||
dropdownOffset={{ y: 8 }}
|
||||
onClickOutside={handleDropdownClickOutside}
|
||||
dropdownWidth={280}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
+1
-1
@@ -79,7 +79,7 @@ export const ViewBarFilterDropdownFieldSelectMenu = () => {
|
||||
selectableItemIdArray={selectableFieldMetadataItemIds}
|
||||
selectableListInstanceId={FILTER_FIELD_LIST_ID}
|
||||
>
|
||||
<DropdownMenuItemsContainer width="auto">
|
||||
<DropdownMenuItemsContainer width={200}>
|
||||
{selectableVisibleFieldMetadataItems.map(
|
||||
(visibleFieldMetadataItem) => (
|
||||
<ViewBarFilterDropdownFieldSelectMenuItem
|
||||
|
||||
Reference in New Issue
Block a user