Skip to content

Commit

Permalink
feat(popup): popup对齐vue mobile (#449)
Browse files Browse the repository at this point in the history
* feat(popup): 对齐vue mobile

fix #407

* refactor(popup): demo调整

* refactor(popup): useDefaultProps 使用

* fix(popup): 修复button编译报错问题,popup demo改tsx

* refactor(popup): 更新overlay 使用;动画改用 CSSTransition

* refactor(popup): portal 应用

* test: update csr and ssr snap

---------

Co-authored-by: anlyyao <anly_yaw@163.com>
  • Loading branch information
hkaikai and anlyyao authored Aug 21, 2024
1 parent 7e07f83 commit ee54e86
Show file tree
Hide file tree
Showing 22 changed files with 848 additions and 287 deletions.
2 changes: 1 addition & 1 deletion site/mobile/mobile.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export default {
{
title: 'Popup 弹出层',
name: 'popup',
component: () => import('tdesign-mobile-react/popup/_example/base.jsx'),
component: () => import('tdesign-mobile-react/popup/_example/index.tsx'),
},
{
title: 'Progress 进度条',
Expand Down
2 changes: 1 addition & 1 deletion site/web/main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

import '@common/style/mobile/index.less';
// import '@common/style/mobile/index.less';

import 'tdesign-site-components';
import 'tdesign-site-components/lib/styles/style.css';
Expand Down
2 changes: 1 addition & 1 deletion src/_common
Submodule _common updated 0 files
156 changes: 97 additions & 59 deletions src/popup/Popup.tsx
Original file line number Diff line number Diff line change
@@ -1,92 +1,130 @@
import React, { FC, useState } from 'react';
import React, { forwardRef, useMemo, useRef, useState } from 'react';
import classnames from 'classnames';
import { useSpring, animated } from 'react-spring';
import { CloseIcon } from 'tdesign-icons-react';
import { CSSTransition } from 'react-transition-group';
import { CSSTransitionClassNames } from 'react-transition-group/CSSTransition';
import Overlay from '../overlay';
import useDefault from '../_util/useDefault';
import withNativeProps, { NativeProps } from '../_util/withNativeProps';
import { TdPopupProps } from './type';
import useConfig from '../_util/useConfig';
import { popupDefaultProps } from './defaultProps';
import useDefaultProps from '../hooks/useDefaultProps';
import parseTNode from '../_util/parseTNode';
import { usePrefixClass } from '../hooks/useClass';
import Portal from '../common/Portal';

export interface PopupProps extends TdPopupProps, NativeProps {}

enum PopupSourceEnum {
OVERLAY = 'overlay',
CLOSEBTN = 'close-btn',
}

enum PlacementEnum {
TOP = 'top',
BOTTOM = 'bottom',
LEFT = 'left',
RIGHT = 'right',
CENTER = 'center',
}
const Popup = forwardRef<HTMLDivElement, PopupProps>((props) => {
const {
children,
placement,
showOverlay,
visible,
defaultVisible,
zIndex,
overlayProps,
preventScrollThrough,
attach,
destroyOnClose,
closeBtn,
closeOnOverlayClick,
onClose,
onClosed,
onOpen,
onOpened,
onVisibleChange,
} = useDefaultProps(props, popupDefaultProps);

const Popup: FC<PopupProps> = (props) => {
const { children, placement, showOverlay, visible, defaultVisible, zIndex, overlayProps, onVisibleChange } = props;
const duration = 300;

const { classPrefix } = useConfig();
const name = usePrefixClass('popup');

const name = `${classPrefix}-popup`;
const contentRef = useRef<HTMLDivElement>(null);

const [show, setShow] = useDefault<boolean, any>(visible, defaultVisible, onVisibleChange);

const [active, setActive] = useState(show);

const handleOverlayClick = () => {
const handleOverlayClick = (e) => {
if (!closeOnOverlayClick) return;
onClose?.(e);
setShow(false, PopupSourceEnum.OVERLAY);
};

const { progress, opacity } = useSpring({
progress: show ? 0 : 100,
opacity: show ? 1 : 0,
onStart: () => {
setActive(true);
},
onRest: () => {
setActive(show);
},
});

const contentStyle = {
transform: progress.to((p) => {
if (placement === PlacementEnum.BOTTOM) {
return `translateY(${p}%)`;
}
if (placement === PlacementEnum.TOP) {
return `translateY(-${p}%)`;
}
if (placement === PlacementEnum.LEFT) {
return `translateX(-${p}%)`;
}
if (placement === PlacementEnum.RIGHT) {
return `translateX(${p}%)`;
}
const handleCloseClick = (e) => {
onClose?.(e);
setShow(false, PopupSourceEnum.CLOSEBTN);
};

const contentStyle = useMemo<React.CSSProperties>(
() => ({
zIndex,
display: active ? null : 'none',
animationFillMode: 'forwards',
}),
opacity: opacity.to((o) => {
if (placement === PlacementEnum.CENTER) {
return o;
}
[zIndex, active],
);

const classNames = useMemo<CSSTransitionClassNames>(
() => ({
enterActive: placement === 'center' ? 'fade-zoom-enter-active' : `slide-${placement}-enter-active`,
exitActive: placement === 'center' ? 'fade-zoom-leave-active' : `slide-${placement}-leave-active`,
}),
};
[placement],
);

const rootStyles = {
zIndex,
display: active ? 'block' : 'none',
};
const closeBtnNode = !closeBtn ? null : (
<div className={`${name}__close`} onClick={handleCloseClick}>
{parseTNode(closeBtn, props, <CloseIcon size={24} />)}
</div>
);

return withNativeProps(
props,
<div className={`${name}`} style={rootStyles}>
{showOverlay && <Overlay visible={show} onClick={handleOverlayClick} {...overlayProps} />}
<animated.div className={classnames([`${name}--content`, `${name}--content-${placement}`])} style={contentStyle}>
{active && children}
</animated.div>
</div>,
const node = (
<>
<Overlay
visible={show && showOverlay}
onClick={handleOverlayClick}
preventScrollThrough={preventScrollThrough}
duration={duration}
{...overlayProps}
/>
<CSSTransition
in={show}
timeout={duration}
nodeRef={contentRef}
unmountOnExit={destroyOnClose}
classNames={classNames}
onEnter={() => {
onOpen?.();
setActive(true);
}}
onEntered={() => {
onOpened?.();
}}
onExited={() => {
onClosed?.();
setActive(false);
}}
>
{withNativeProps(
props,
<div ref={contentRef} className={classnames([name, `${name}--${placement}`])} style={contentStyle}>
{closeBtnNode}
{parseTNode(children)}
</div>,
)}
</CSSTransition>
</>
);
};
return <Portal attach={attach}>{node}</Portal>;
});

Popup.displayName = 'Popup';
Popup.defaultProps = popupDefaultProps;

export default Popup;
65 changes: 0 additions & 65 deletions src/popup/_example/base.jsx

This file was deleted.

48 changes: 48 additions & 0 deletions src/popup/_example/base.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React, { useState } from 'react';
import Button from '../../button';
import Popup from '..';

const placementList = [
{ value: 'top', text: '顶部弹出' },
{ value: 'left', text: '左侧弹出' },
{ value: 'center', text: '中间弹出' },
{ value: 'bottom', text: '底部弹出' },
{ value: 'right', text: '右侧弹出' },
] as const;

type PlacementValue = (typeof placementList)[number]['value'];

export default function Base() {
const [visible, setVisible] = useState(false);
const [placement, setPlacement] = useState<PlacementValue>('top');
const onClick = (value) => {
setPlacement(value);
setVisible(true);
};
const handleVisibleChange = (visible) => {
setVisible(visible);
};
return (
<div style={{ padding: '0 16px' }}>
{placementList.map((item) => (
<Button
key={item.value}
block={true}
variant="outline"
theme="primary"
size="large"
onClick={() => onClick(item.value)}
style={{ margin: '16px 0' }}
>
{item.text}
</Button>
))}
<Popup
visible={visible}
placement={placement}
style={{ padding: '100px' }}
onVisibleChange={handleVisibleChange}
></Popup>
</div>
);
}
35 changes: 35 additions & 0 deletions src/popup/_example/custom-close.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React, { useState } from 'react';
import { CloseCircleIcon } from 'tdesign-icons-react';
import { Popup, Button } from 'tdesign-mobile-react';

export default function CustomClose() {
const [visible, setVisible] = useState(false);

const handleVisibleChange = (visible) => {
setVisible(visible);
};

const onHide = () => setVisible(false);

return (
<>
<Button variant="outline" block={true} theme="primary" size="large" onClick={() => setVisible(true)}>
居中弹出层-带自定义关闭按钮
</Button>

<Popup
visible={visible}
onVisibleChange={handleVisibleChange}
placement="center"
style={{ width: '240px', height: '240px' }}
>
<CloseCircleIcon
className="design-mobile-popup-demo__custom-close close-btn"
size={32}
color="#fff"
onClick={onHide}
/>
</Popup>
</>
);
}
Loading

0 comments on commit ee54e86

Please sign in to comment.