Skip to content

Commit

Permalink
fix: 修复 popover 无法打开问题 close #540 (#541)
Browse files Browse the repository at this point in the history
  • Loading branch information
3lang3 authored Aug 1, 2022
1 parent 1151c2c commit 4ef57a3
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 63 deletions.
15 changes: 15 additions & 0 deletions packages/react-vant/src/components/hooks/use-lazy-effect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useEffect, useState } from 'react'

const useLazyEffect: typeof useEffect = (effect, deps) => {
const [c, setC] = useState(0)

useEffect(() => {
setC(v => v + 1)
}, deps)

useEffect(() => {
return effect()
}, [c])
}

export default useLazyEffect
136 changes: 73 additions & 63 deletions packages/react-vant/src/components/popover/Popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ import React, {
useImperativeHandle,
useRef,
useState,
} from 'react';
import cls from 'clsx';
import { Instance, createPopper, offsetModifier } from '@vant/popperjs';
import { PopoverAction, PopoverInstance, PopoverProps } from './PropsType';
import { createNamespace, extend, pick } from '../utils';
import { PopupInstanceType } from '../popup/PropsType';
import { BORDER_BOTTOM } from '../utils/constant';
import useClickAway from '../hooks/use-click-away';
import Popup from '../popup';
} from 'react'
import cls from 'clsx'
import { Instance, createPopper, offsetModifier } from '@vant/popperjs'
import { PopoverAction, PopoverInstance, PopoverProps } from './PropsType'
import { createNamespace, extend, pick } from '../utils'
import { PopupInstanceType } from '../popup/PropsType'
import { BORDER_BOTTOM } from '../utils/constant'
import useClickAway from '../hooks/use-click-away'
import Popup from '../popup'
import useLazyEffect from '../hooks/use-lazy-effect'

const popupProps = [
'overlay',
Expand All @@ -26,17 +27,16 @@ const popupProps = [
'onClosed',
'onOpened',
'onClickOverlay',
] as const;
] as const

const [bem] = createNamespace('popover');
const [bem] = createNamespace('popover')

const Popover = forwardRef<PopoverInstance, PopoverProps>(
({ children, className, ...props }, ref) => {
const [visible, updateShow] = useState(false);

const popper = useRef<Instance>(null);
const wrapperRef = useRef<HTMLElement>();
const popoverRef = useRef<PopupInstanceType>();
const [visible, updateShow] = useState(false)
const popper = useRef<Instance>(null)
const wrapperRef = useRef<HTMLElement>()
const popoverRef = useRef<PopupInstanceType>()

const createPopperInstance = () =>
createPopper(wrapperRef.current, popoverRef.current.popupRef.current, {
Expand All @@ -55,53 +55,59 @@ const Popover = forwardRef<PopoverInstance, PopoverProps>(
},
}),
],
});
})

const updateLocation = () => {
if (!visible) {
return;
return
}

if (!popper.current) {
popper.current = createPopperInstance();
popper.current = createPopperInstance()
} else {
popper.current?.setOptions({
placement: props.placement,
});
})
}
};
}

const onClickWrapper = () => {
if (props.trigger === 'click') {
updateShow(!visible);
updateShow(!visible)
}
};
}

const onClickAction = (action: PopoverAction, index: number) => {
if (action.disabled) {
return;
return
}

props.onSelect?.(action, index);
props.onSelect?.(action, index)

if (props.closeOnClickAction) {
updateShow(false);
updateShow(false)
}
};
}

const onClickAway = () => {
if (props.closeOnClickOutside && (!props.overlay || props.closeOnClickOverlay)) {
updateShow(false);
if (
props.closeOnClickOutside &&
(!props.overlay || props.closeOnClickOverlay)
) {
updateShow(false)
}
};
}

const renderAction = (action: PopoverAction, index: number) => {
const { icon, text, color, disabled, className: actionClassname } = action;
const { icon, text, color, disabled, className: actionClassname } = action
return (
<div
// role="menuitem"
key={index}
className={cls(bem('action', { disabled, 'with-icon': icon }), actionClassname)}
className={cls(
bem('action', { disabled, 'with-icon': icon }),
actionClassname
)}
style={{ color }}
onClick={() => onClickAction(action, index)}
>
Expand All @@ -112,71 +118,75 @@ const Popover = forwardRef<PopoverInstance, PopoverProps>(
: null}
<div className={cls(bem('action-text'), BORDER_BOTTOM)}>{text}</div>
</div>
);
};
)
}

useEffect(() => {
return () => {
if (popper.current) {
popper.current?.destroy();
popper.current = null;
popper.current?.destroy()
popper.current = null
}
};
}, []);
}
}, [])

useEffect(() => {
updateLocation();
}, [visible, props.placement]);
useLazyEffect(() => {
updateLocation()
}, [visible, props.placement])

useEffect(() => {
let popupTarget;
const prevent = (e) => e.stopPropagation();
let popupTarget
const prevent = e => e.stopPropagation()
if (popoverRef.current && popoverRef.current.popupRef.current) {
popupTarget = popoverRef.current.popupRef.current;
popupTarget.addEventListener('touchstart', prevent);
popupTarget = popoverRef.current.popupRef.current
popupTarget.addEventListener('touchstart', prevent)
}
return () => {
if (popupTarget) popupTarget.removeEventListener('touchstart', prevent);
};
}, [popoverRef.current]);
if (popupTarget) popupTarget.removeEventListener('touchstart', prevent)
}
}, [popoverRef.current])

useImperativeHandle(ref, () => ({
show: () => {
if (visible) {
updateShow(false);
setTimeout(() => updateShow(true), 0);
return;
updateShow(false)
setTimeout(() => updateShow(true), 0)
return
}
updateShow(true);
updateShow(true)
},
hide: () => updateShow(false),
}));
}))

useClickAway(wrapperRef, onClickAway, 'touchstart');
useClickAway(wrapperRef, onClickAway, 'touchstart')

return (
<>
<span ref={wrapperRef} className={cls(bem('wrapper'))} onClick={onClickWrapper}>
<span
ref={wrapperRef}
className={cls(bem('wrapper'))}
onClick={onClickWrapper}
>
{props.reference}
</span>
<Popup
ref={popoverRef}
visible={visible}
className={cls(className, bem([props.theme]))}
position=""
transition="rv-zoom"
position=''
transition='rv-zoom'
lockScroll={false}
{...pick(props, popupProps)}
>
<div className={cls(bem('arrow'))} />
<div role="menu" className={cls(bem('content'))}>
<div role='menu' className={cls(bem('content'))}>
{children || props.actions.map(renderAction)}
</div>
</Popup>
</>
);
},
);
)
}
)

Popover.defaultProps = {
overlay: false,
Expand All @@ -189,6 +199,6 @@ Popover.defaultProps = {
trigger: 'click',
actions: [],
placement: 'bottom',
};
}

export default Popover;
export default Popover

0 comments on commit 4ef57a3

Please sign in to comment.