Skip to content

Commit

Permalink
implemented useOuterClick - mantine calendar modal issue (#199)
Browse files Browse the repository at this point in the history
  • Loading branch information
balaji-jr authored Feb 15, 2024
1 parent bc04d81 commit 6315d41
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 21 deletions.
72 changes: 51 additions & 21 deletions src/components/Header/TimeRange.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import { DateTimePicker } from '@mantine/dates';
import { IconClock } from '@tabler/icons-react';
import dayjs from 'dayjs';
import type { FC } from 'react';
import { Fragment, useEffect, useMemo } from 'react';
import { Fragment, useCallback, useEffect, useMemo } from 'react';
import { FIXED_DURATIONS } from '@/constants/timeConstants';
import logQueryStyles from './styles/LogQuery.module.css'
import logQueryStyles from './styles/LogQuery.module.css';
import { useOuterClick } from '@/hooks/useOuterClick';

type FixedDurations = (typeof FIXED_DURATIONS)[number];

Expand All @@ -16,9 +17,31 @@ const TimeRange: FC = () => {
state: { subLogQuery, subLogSelectedTimeRange },
} = useHeaderContext();

const handleOuterClick = (event: any) => {
const targetClassNames: string[] = event.target?.classList || [];
const maybeSubmitBtnClassNames: string[] = event.target.closest('button')?.classList || [];
const classNames: string[] = [
...(typeof targetClassNames[Symbol.iterator] === 'function' ? [...targetClassNames] : []),
...(typeof maybeSubmitBtnClassNames[Symbol.iterator] === 'function' ? [...maybeSubmitBtnClassNames] : []),
];
const shouldIgnoreClick = classNames.some((className) => {
return (
className.startsWith('mantine-DateTimePicker') ||
className.startsWith('mantine-TimeInput') ||
className === 'mantine-Popover-dropdown'
);
});
!shouldIgnoreClick && setOpened(false);
};

const innerRef = useOuterClick(handleOuterClick);
const [opened, setOpened] = useMountedState(false);
const [selectedRange, setSelectedRange] = useMountedState<string>(subLogSelectedTimeRange.get().value);

const toggleMenu = useCallback(() => {
setOpened((prev) => !prev);
}, []);

useEffect(() => {
const listener = subLogSelectedTimeRange.subscribe((state) => {
setSelectedRange(state.value);
Expand Down Expand Up @@ -51,31 +74,38 @@ const TimeRange: FC = () => {
} = logQueryStyles;

return (
<Menu withArrow position="top" opened={opened} onChange={setOpened}>
<Menu withArrow position="top" opened={opened}>
<Menu.Target>
<Button className={timeRangeBTn} leftSection={<IconClock size={px('1.2rem')} stroke={1.5} />}>
<Button
className={timeRangeBTn}
leftSection={<IconClock size={px('1.2rem')} stroke={1.5} />}
onClick={toggleMenu}>
{selectedRange}
</Button>
</Menu.Target>
<Menu.Dropdown>
<Box className={timeRangeContainer}>
<Box className={fixedRangeContainer}>
{FIXED_DURATIONS.map((duration) => {
return (
<UnstyledButton
disabled={selectedRange === duration.name}
className={[fixedRangeBtn, selectedRange === duration.name && fixedRangeBtnSelected].filter(Boolean).join(' ')}
key={duration.name}
onClick={() => onDurationSelect(duration)}>
{duration.name}
</UnstyledButton>
);
})}
</Box>
<Box className={customRangeContainer}>
<CustomTimeRange setOpened={setOpened} />
<div ref={innerRef}>
<Box className={timeRangeContainer}>
<Box className={fixedRangeContainer}>
{FIXED_DURATIONS.map((duration) => {
return (
<UnstyledButton
disabled={selectedRange === duration.name}
className={[fixedRangeBtn, selectedRange === duration.name && fixedRangeBtnSelected]
.filter(Boolean)
.join(' ')}
key={duration.name}
onClick={() => onDurationSelect(duration)}>
{duration.name}
</UnstyledButton>
);
})}
</Box>
<Box className={customRangeContainer}>
<CustomTimeRange setOpened={setOpened} />
</Box>
</Box>
</Box>
</div>
</Menu.Dropdown>
</Menu>
);
Expand Down
21 changes: 21 additions & 0 deletions src/hooks/useOuterClick.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { MutableRefObject } from 'react';
import { useEffect, useRef } from 'react';

export const useOuterClick = (callback: (event: any) => void): MutableRefObject<HTMLDivElement | null> => {
const innerRef: MutableRefObject<HTMLDivElement | null> = useRef(null);
useEffect(() => {
const handleClickOutside = (event: any) => {
if (innerRef.current && !(innerRef.current as any).contains(event.target)) {
callback(event);
}
};

document.addEventListener('click', handleClickOutside);

return () => {
document.removeEventListener('click', handleClickOutside);
};
}, [callback]);

return innerRef;
};

0 comments on commit 6315d41

Please sign in to comment.