feat: modify popper

This commit is contained in:
QiShaoXuan 2022-11-03 18:59:54 +08:00
parent d9205bb405
commit 75f05cb399
4 changed files with 70 additions and 147 deletions

View File

@ -1,65 +0,0 @@
import { useState } from 'react';
import type { CSSProperties, PropsWithChildren, ReactNode } from 'react';
import Grow from '@mui/material/Grow';
import ClickAwayListener from '@mui/base/ClickAwayListener';
import { styled } from '@/styles';
type PopoverProps = {
popoverContent?: ReactNode;
style?: CSSProperties;
};
const StyledPopoverContainer = styled('div')({
position: 'relative',
cursor: 'pointer',
});
const StyledPopoverWrapper = styled('div')({
position: 'absolute',
bottom: '0',
right: '0',
paddingTop: '46px',
zIndex: 1000,
});
const StyledPopover = styled('div')(({ theme }) => {
return {
width: '248px',
background: theme.colors.popoverBackground,
boxShadow: theme.shadow.popover,
color: theme.colors.popoverColor,
borderRadius: '10px 0px 10px 10px',
padding: '8px 4px',
position: 'absolute',
top: '46px',
right: '0',
};
});
export const Popover = ({
children,
popoverContent,
style = {},
}: PropsWithChildren<PopoverProps>) => {
const [show, setShow] = useState(false);
return (
<ClickAwayListener
onClickAway={() => {
setShow(false);
}}
>
<StyledPopoverContainer
onClick={() => {
setShow(!show);
}}
style={style}
>
{children}
<Grow in={show}>
<StyledPopoverWrapper>
<StyledPopover>{popoverContent}</StyledPopover>
</StyledPopoverWrapper>
</Grow>
</StyledPopoverContainer>
</ClickAwayListener>
);
};

View File

@ -4,6 +4,7 @@ import {
useMemo,
useRef,
useState,
cloneElement,
} from 'react';
import PopperUnstyled from '@mui/base/PopperUnstyled';
import ClickAwayListener from '@mui/base/ClickAwayListener';
@ -26,7 +27,6 @@ export const Popper = ({
onVisibleChange,
popoverStyle,
popoverClassName,
anchorStyle,
anchorClassName,
zIndex,
offset = [0, 5],
@ -36,12 +36,9 @@ export const Popper = ({
onClickAway,
...popperProps
}: PopperProps) => {
// @ts-ignore
const [anchorEl, setAnchorEl] = useState<VirtualElement>(null);
const [anchorEl, setAnchorEl] = useState<VirtualElement>();
const [visible, setVisible] = useState(defaultVisible);
// @ts-ignore
const [arrowRef, setArrowRef] = useState<HTMLElement>(null);
const popperRef = useRef();
const [arrowRef, setArrowRef] = useState<HTMLElement>();
const pointerLeaveTimer = useRef<number>();
const pointerEnterTimer = useRef<number>();
@ -95,7 +92,6 @@ export const Popper = ({
};
});
// @ts-ignore
return (
<ClickAwayListener
onClickAway={() => {
@ -107,66 +103,63 @@ export const Popper = ({
}}
>
<Container>
{isAnchorCustom ? null : (
<div
ref={(dom: HTMLDivElement) => setAnchorEl(dom)}
onClick={e => {
if (!hasClickTrigger || visibleControlledByParent) {
onClick?.(e);
return;
}
setVisible(!visible);
}}
onPointerEnter={onPointerEnterHandler}
onPointerLeave={onPointerLeaveHandler}
style={anchorStyle}
className={anchorClassName}
{cloneElement(children, {
ref: (dom: HTMLDivElement) => setAnchorEl(dom),
onClick: (e: MouseEvent) => {
if (!hasClickTrigger || visibleControlledByParent) {
// @ts-ignore
onClick?.(e);
return;
}
setVisible(!visible);
},
onPointerEnter: onPointerEnterHandler,
onPointerLeave: onPointerLeaveHandler,
className: anchorClassName,
popperVisible: visible,
})}
{content && (
<BasicStyledPopper
open={visibleControlledByParent ? propsVisible : visible}
zIndex={zIndex}
anchorEl={isAnchorCustom ? propsAnchorEl : anchorEl}
placement={placement}
transition
modifiers={[
{
name: 'offset',
options: {
offset,
},
},
{
name: 'arrow',
enabled: showArrow,
options: {
element: arrowRef,
},
},
]}
{...popperProps}
>
{children}
</div>
{({ TransitionProps }) => (
<Grow {...TransitionProps}>
<div
onPointerEnter={onPointerEnterHandler}
onPointerLeave={onPointerLeaveHandler}
style={popoverStyle}
className={popoverClassName}
>
{showArrow && (
// @ts-ignore
<PopperArrow placement={placement} ref={setArrowRef} />
)}
{content}
</div>
</Grow>
)}
</BasicStyledPopper>
)}
<BasicStyledPopper
// @ts-ignore
popperRef={popperRef}
open={visibleControlledByParent ? propsVisible : visible}
zIndex={zIndex}
anchorEl={isAnchorCustom ? propsAnchorEl : anchorEl}
placement={placement}
transition
modifiers={[
{
name: 'offset',
options: {
offset,
},
},
{
name: 'arrow',
enabled: showArrow,
options: {
element: arrowRef,
},
},
]}
{...popperProps}
>
{({ TransitionProps }) => (
<Grow {...TransitionProps}>
<div
onPointerEnter={onPointerEnterHandler}
onPointerLeave={onPointerLeaveHandler}
style={popoverStyle}
className={popoverClassName}
>
{showArrow && (
// @ts-ignore
<PopperArrow placement={placement} ref={setArrowRef} />
)}
{content}
</div>
</Grow>
)}
</BasicStyledPopper>
</Container>
</ClickAwayListener>
);
@ -184,6 +177,6 @@ const BasicStyledPopper = styled(PopperUnstyled, {
zIndex?: number;
}>(({ zIndex, theme }) => {
return {
zIndex: zIndex,
zIndex: zIndex ?? theme.zIndex.popover,
};
});

View File

@ -1,4 +1,4 @@
import type { CSSProperties, ReactNode, Ref } from 'react';
import type { CSSProperties, ReactNode, Ref, ReactElement } from 'react';
import {
type PopperPlacementType,
type PopperUnstyledProps,
@ -18,10 +18,10 @@ export type PopperArrowProps = {
export type PopperProps = {
// Popover content
content: ReactNode;
content?: ReactNode;
// Popover trigger
children?: ReactNode;
children: ReactElement;
// Whether the default is implicit
defaultVisible?: boolean;
@ -47,9 +47,6 @@ export type PopperProps = {
// Popover container class name
popoverClassName?: string;
// Anchor style
anchorStyle?: CSSProperties;
// Anchor class name
anchorClassName?: string;
@ -63,4 +60,4 @@ export type PopperProps = {
popperHandlerRef?: Ref<PopperHandler>;
onClickAway?: () => void;
} & Omit<PopperUnstyledProps, 'open' | 'ref'>;
} & Omit<PopperUnstyledProps, 'open'>;

View File

@ -1,4 +1,3 @@
import { type PropsWithChildren } from 'react';
import StyledPopperContainer from '../shared/Container';
import { Popper, type PopperProps } from '../popper';
import { styled } from '@/styles';
@ -14,16 +13,15 @@ const StyledTooltip = styled(StyledPopperContainer)(({ theme }) => {
};
});
export const Tooltip = (
props: PropsWithChildren<PopperProps & Omit<TooltipProps, 'title'>>
) => {
const { content, placement = 'top-start' } = props;
// If there is no content, hide forever
return content ? (
export const Tooltip = (props: PopperProps & Omit<TooltipProps, 'title'>) => {
const { content, placement = 'top-start', children } = props;
return (
<Popper
{...props}
showArrow={false}
content={<StyledTooltip placement={placement}>{content}</StyledTooltip>}
/>
) : null;
>
{children}
</Popper>
);
};