Skip to content

Commit

Permalink
Merge pull request #1059 from Tencent/feat/drawer-size-drag
Browse files Browse the repository at this point in the history
feat(Drawer): 新增sizeDraggable 支持通过拖拽改变抽屉宽度/高度
  • Loading branch information
honkinglin authored Jul 8, 2022
2 parents dd1ebe8 + 0574b5c commit 7376e6a
Show file tree
Hide file tree
Showing 6 changed files with 298 additions and 15 deletions.
37 changes: 23 additions & 14 deletions src/drawer/Drawer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { forwardRef, useState, useEffect, useImperativeHandle, useRef } from 'react';
import React, { forwardRef, useState, useEffect, useImperativeHandle, useRef, useCallback } from 'react';
import classnames from 'classnames';
import { CloseIcon } from 'tdesign-icons-react';

Expand All @@ -12,6 +12,7 @@ import DrawerWrapper from './DrawerWrapper';
import Button from '../button';
import useConfig from '../_util/useConfig';
import { drawerDefaultProps } from './defaultProps';
import useDrag from './hooks/useDrag';

export const CloseTriggerType: { [key: string]: DrawerEventSource } = {
CLICK_OVERLAY: 'overlay',
Expand All @@ -22,17 +23,6 @@ export const CloseTriggerType: { [key: string]: DrawerEventSource } = {

export interface DrawerProps extends TdDrawerProps, StyledProps {}

const getSizeValue = (size: string): string => {
const defaultSize = isNaN(Number(size)) ? size : `${size}px`;
return (
{
small: '300px',
medium: '500px',
large: '760px',
}[size] || defaultSize
);
};

const Drawer = forwardRef((props: DrawerProps, ref: React.Ref<HTMLDivElement>) => {
const {
className,
Expand Down Expand Up @@ -62,6 +52,7 @@ const Drawer = forwardRef((props: DrawerProps, ref: React.Ref<HTMLDivElement>) =
destroyOnClose,
mode,
preventScrollThrough = true,
sizeDraggable,
} = props;

// 国际化文本初始化
Expand All @@ -79,7 +70,7 @@ const Drawer = forwardRef((props: DrawerProps, ref: React.Ref<HTMLDivElement>) =

const transform = visible ? 'translate(0px)' : '';
const closeIcon = React.isValidElement(closeBtn) ? closeBtn : <CloseIcon />;

const { dragSizeValue, enableDrag, draggableLineStyles } = useDrag(placement, sizeDraggable);
const [isDestroyOnClose, setIsDestroyOnClose] = useState(false);

useImperativeHandle(ref, () => containerRef.current);
Expand All @@ -100,6 +91,22 @@ const Drawer = forwardRef((props: DrawerProps, ref: React.Ref<HTMLDivElement>) =
setIsDestroyOnClose(false);
}, [visible, destroyOnClose]);

const getSizeValue = useCallback(
(size: string): string => {
if (dragSizeValue) return dragSizeValue;

const defaultSize = isNaN(Number(size)) ? size : `${size}px`;
return (
{
small: '300px',
medium: '500px',
large: '760px',
}[size] || defaultSize
);
},
[dragSizeValue],
);

useEffect(() => {
let documentBodyCssText = '';

Expand Down Expand Up @@ -139,7 +146,7 @@ const Drawer = forwardRef((props: DrawerProps, ref: React.Ref<HTMLDivElement>) =

contentWrapperRef.current.style.transform = transform;
}
}, [attach, mode, transform, visible, placement, size]);
}, [attach, mode, transform, visible, placement, size, getSizeValue]);

function onMaskClick(e: React.MouseEvent<HTMLDivElement>) {
onOverlayClick?.({ e });
Expand Down Expand Up @@ -186,6 +193,7 @@ const Drawer = forwardRef((props: DrawerProps, ref: React.Ref<HTMLDivElement>) =
`${prefixCls}__content-wrapper`,
`${prefixCls}__content-wrapper--${placement}`,
);

const contentWrapperStyle = {
transform: visible ? 'translateX(0)' : undefined,
width: ['left', 'right'].includes(placement) ? getSizeValue(size) : '',
Expand Down Expand Up @@ -252,6 +260,7 @@ const Drawer = forwardRef((props: DrawerProps, ref: React.Ref<HTMLDivElement>) =
{renderHeader}
{renderBody}
{renderFooter}
{sizeDraggable && <div style={draggableLineStyles} onMouseDown={enableDrag}></div>}
</div>
</div>
</DrawerWrapper>
Expand Down
180 changes: 180 additions & 0 deletions src/drawer/__tests__/__snapshots__/drawer.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1197,3 +1197,183 @@ exports[`size.jsx 1`] = `
</div>
</DocumentFragment>
`;

exports[`size-draggable.jsx 1`] = `
<DocumentFragment>
<div
class="t-space t-space-horizontal"
style="gap: 16px;"
>
<div
class="t-space-item"
>
<div
class="t-radio-group t-size-m t-radio-group__outline"
>
<label
class="t-radio-button"
>
<input
class="t-radio-button__former"
type="radio"
value="left"
/>
<span
class="t-radio-button__input"
/>
<span
class="t-radio-button__label"
>
从左侧拖拽抽屉
</span>
</label>
<label
class="t-radio-button t-is-checked"
>
<input
checked=""
class="t-radio-button__former"
type="radio"
value="right"
/>
<span
class="t-radio-button__input"
/>
<span
class="t-radio-button__label"
>
从右侧拖拽抽屉
</span>
</label>
<label
class="t-radio-button"
>
<input
class="t-radio-button__former"
type="radio"
value="top"
/>
<span
class="t-radio-button__input"
/>
<span
class="t-radio-button__label"
>
从上方拖拽抽屉
</span>
</label>
<label
class="t-radio-button"
>
<input
class="t-radio-button__former"
type="radio"
value="bottom"
/>
<span
class="t-radio-button__input"
/>
<span
class="t-radio-button__label"
>
从下方拖拽抽屉
</span>
</label>
</div>
</div>
<div
class="t-space-item"
>
<div>
<button
class="t-button t-button--theme-primary t-button--variant-base"
type="button"
>
<span
class="t-button__text"
>
打开抽屉
</span>
</button>
</div>
</div>
<div
class="t-space-item"
>
<div>
<div
class="t-drawer t-drawer--right"
tabindex="-1"
>
<div
class="t-drawer__mask"
/>
<div
class="t-drawer__content-wrapper t-drawer__content-wrapper--right"
style="width: 300px;"
>
<div
class="t-drawer__close-btn"
>
<svg
class="t-icon t-icon-close"
fill="none"
height="1em"
viewBox="0 0 16 16"
width="1em"
>
<path
d="M8 8.92L11.08 12l.92-.92L8.92 8 12 4.92 11.08 4 8 7.08 4.92 4 4 4.92 7.08 8 4 11.08l.92.92L8 8.92z"
fill="currentColor"
fill-opacity="0.9"
/>
</svg>
</div>
<div
class="t-drawer__header"
/>
<div
class="t-drawer__body"
>
<p>
抽屉的内容
</p>
</div>
<div
class="t-drawer__footer"
>
<div
style="display: flex; justify-content: flex-start;"
>
<button
class="t-drawer__confirm t-button t-button--theme-primary t-button--variant-base"
type="button"
>
<span
class="t-button__text"
>
确认
</span>
</button>
<button
class="t-drawer__cancel t-button t-button--theme-default t-button--variant-base"
type="button"
>
<span
class="t-button__text"
>
取消
</span>
</button>
</div>
</div>
<div
style="z-index: 1; position: absolute; background: transparent; left: 0px; width: 16px; height: 100%; cursor: col-resize;"
/>
</div>
</div>
</div>
</div>
</div>
</DocumentFragment>
`;
2 changes: 1 addition & 1 deletion src/drawer/_example/placement.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default function () {
打开抽屉
</Button>
</div>
<Drawer placement={placement} key={placement} title="Drawer" visible={visible} onClose={handleClose}>
<Drawer placement={placement} key={placement} visible={visible} onClose={handleClose}>
<p>抽屉的内容</p>
</Drawer>
</Space>
Expand Down
33 changes: 33 additions & 0 deletions src/drawer/_example/size-draggable.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React, { useState } from 'react';
import { Drawer, Radio, Button, Space } from 'tdesign-react';

export default function () {
const [visible, setVisible] = useState(false);
const [placement, setPlacement] = useState('right');

const handleClick = () => {
setVisible(true);
};
const handleClose = () => {
setVisible(false);
};
return (
<Space>
<Radio.Group value={placement} onChange={(value) => setPlacement(value)}>
<Radio.Button value="left">从左侧拖拽抽屉</Radio.Button>
<Radio.Button value="right">从右侧拖拽抽屉</Radio.Button>
<Radio.Button value="top">从上方拖拽抽屉</Radio.Button>
<Radio.Button value="bottom">从下方拖拽抽屉</Radio.Button>
</Radio.Group>

<div>
<Button theme="primary" onClick={handleClick}>
打开抽屉
</Button>
</div>
<Drawer placement={placement} key={placement} visible={visible} onClose={handleClose} sizeDraggable={true}>
<p>抽屉的内容</p>
</Drawer>
</Space>
);
}
59 changes: 59 additions & 0 deletions src/drawer/hooks/useDrag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { useMemo, useState } from 'react';
import { TdDrawerProps } from '../type';
import { Styles } from '../../common';

const useDrag = (placement: TdDrawerProps['placement'], sizeDraggable: TdDrawerProps['sizeDraggable']) => {
const [dragSizeValue, changeDragSizeValue] = useState<string>(null);

const handleMousemove = (e: MouseEvent) => {
// 鼠标移动时计算draggedSizeValue的值
const { x, y } = e;
if (sizeDraggable) {
if (placement === 'right') {
changeDragSizeValue(`${document.documentElement.clientWidth - x + 8}px`);
}
if (placement === 'left') {
changeDragSizeValue(`${x + 8}px`);
}
if (placement === 'top') {
changeDragSizeValue(`${y + 8}px`);
}
if (placement === 'bottom') {
changeDragSizeValue(`${document.documentElement.clientHeight - y + 8}px`);
}
}
};
const handleMouseup = () => {
document.removeEventListener('mouseup', handleMouseup, true);
document.removeEventListener('mousemove', handleMousemove, true);
};
const draggableLineStyles: Styles = useMemo(() => {
// 设置拖拽control的样式
const isHorizontal = ['right', 'left'].includes(placement);
const oppositeMap = {
left: 'right',
right: 'left',
top: 'bottom',
bottom: 'top',
};
return {
zIndex: 1,
position: 'absolute',
background: 'transparent',
[oppositeMap[placement]]: 0,
width: isHorizontal ? '16px' : '100%',
height: isHorizontal ? '100%' : '16px',
cursor: isHorizontal ? 'col-resize' : 'row-resize',
};
}, [placement]);

const enableDrag = () => {
// mousedown绑定mousemove和mouseup事件
document.addEventListener('mouseup', handleMouseup, true);
document.addEventListener('mousemove', handleMousemove, true);
};

return { dragSizeValue, enableDrag, draggableLineStyles };
};

export default useDrag;
2 changes: 2 additions & 0 deletions test/ssr/__snapshots__/ssr.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,8 @@ exports[`ssr snapshot test renders ./src/drawer/_example/popup.jsx correctly 1`]

exports[`ssr snapshot test renders ./src/drawer/_example/size.jsx correctly 1`] = `"<div style=\\"gap:16px\\" class=\\"t-space t-space-horizontal\\"><div class=\\"t-space-item\\"><div class=\\"t-radio-group t-size-m t-radio-group__outline\\"><label class=\\"t-radio-button t-is-checked\\" checked=\\"\\"><input type=\\"radio\\" class=\\"t-radio-button__former\\" checked=\\"\\" value=\\"small\\"/><span class=\\"t-radio-button__input\\"></span><span class=\\"t-radio-button__label\\">small(300px)</span></label><label class=\\"t-radio-button\\"><input type=\\"radio\\" class=\\"t-radio-button__former\\" value=\\"medium\\"/><span class=\\"t-radio-button__input\\"></span><span class=\\"t-radio-button__label\\">medium(500px)</span></label><label class=\\"t-radio-button\\"><input type=\\"radio\\" class=\\"t-radio-button__former\\" value=\\"large\\"/><span class=\\"t-radio-button__input\\"></span><span class=\\"t-radio-button__label\\">large(760px)</span></label><label class=\\"t-radio-button\\"><input type=\\"radio\\" class=\\"t-radio-button__former\\" value=\\"200\\"/><span class=\\"t-radio-button__input\\"></span><span class=\\"t-radio-button__label\\">200</span></label><label class=\\"t-radio-button\\"><input type=\\"radio\\" class=\\"t-radio-button__former\\" value=\\"400px\\"/><span class=\\"t-radio-button__input\\"></span><span class=\\"t-radio-button__label\\">400px</span></label><label class=\\"t-radio-button\\"><input type=\\"radio\\" class=\\"t-radio-button__former\\" value=\\"50%\\"/><span class=\\"t-radio-button__input\\"></span><span class=\\"t-radio-button__label\\">50%</span></label></div></div><div class=\\"t-space-item\\"><div><button type=\\"button\\" class=\\"t-button t-button--theme-primary t-button--variant-base\\"><span class=\\"t-button__text\\">打开抽屉</span></button></div></div><div class=\\"t-space-item\\"><div><div class=\\"t-drawer t-drawer--right\\" tabindex=\\"-1\\"><div class=\\"t-drawer__mask\\"></div><div class=\\"t-drawer__content-wrapper t-drawer__content-wrapper--right\\" style=\\"width:300px;height:\\"><div class=\\"t-drawer__close-btn\\"><svg fill=\\"none\\" viewBox=\\"0 0 16 16\\" width=\\"1em\\" height=\\"1em\\" class=\\"t-icon t-icon-close\\"><path fill=\\"currentColor\\" d=\\"M8 8.92L11.08 12l.92-.92L8.92 8 12 4.92 11.08 4 8 7.08 4.92 4 4 4.92 7.08 8 4 11.08l.92.92L8 8.92z\\" fill-opacity=\\"0.9\\"></path></svg></div><div class=\\"t-drawer__header\\"></div><div class=\\"t-drawer__body\\"><p>抽屉的内容</p></div><div class=\\"t-drawer__footer\\"><div style=\\"display:flex;justify-content:flex-start\\"><button type=\\"button\\" class=\\"t-drawer__confirm t-button t-button--theme-primary t-button--variant-base\\"><span class=\\"t-button__text\\">确认</span></button><button type=\\"button\\" class=\\"t-drawer__cancel t-button t-button--theme-default t-button--variant-base\\"><span class=\\"t-button__text\\">取消</span></button></div></div></div></div></div></div></div>"`;

exports[`ssr snapshot test renders ./src/drawer/_example/size-draggable.jsx correctly 1`] = `"<div style=\\"gap:16px\\" class=\\"t-space t-space-horizontal\\"><div class=\\"t-space-item\\"><div class=\\"t-radio-group t-size-m t-radio-group__outline\\"><label class=\\"t-radio-button\\"><input type=\\"radio\\" class=\\"t-radio-button__former\\" value=\\"left\\"/><span class=\\"t-radio-button__input\\"></span><span class=\\"t-radio-button__label\\">从左侧拖拽抽屉</span></label><label class=\\"t-radio-button t-is-checked\\" checked=\\"\\"><input type=\\"radio\\" class=\\"t-radio-button__former\\" checked=\\"\\" value=\\"right\\"/><span class=\\"t-radio-button__input\\"></span><span class=\\"t-radio-button__label\\">从右侧拖拽抽屉</span></label><label class=\\"t-radio-button\\"><input type=\\"radio\\" class=\\"t-radio-button__former\\" value=\\"top\\"/><span class=\\"t-radio-button__input\\"></span><span class=\\"t-radio-button__label\\">从上方拖拽抽屉</span></label><label class=\\"t-radio-button\\"><input type=\\"radio\\" class=\\"t-radio-button__former\\" value=\\"bottom\\"/><span class=\\"t-radio-button__input\\"></span><span class=\\"t-radio-button__label\\">从下方拖拽抽屉</span></label></div></div><div class=\\"t-space-item\\"><div><button type=\\"button\\" class=\\"t-button t-button--theme-primary t-button--variant-base\\"><span class=\\"t-button__text\\">打开抽屉</span></button></div></div><div class=\\"t-space-item\\"><div><div class=\\"t-drawer t-drawer--right\\" tabindex=\\"-1\\"><div class=\\"t-drawer__mask\\"></div><div class=\\"t-drawer__content-wrapper t-drawer__content-wrapper--right\\" style=\\"width:300px;height:\\"><div class=\\"t-drawer__close-btn\\"><svg fill=\\"none\\" viewBox=\\"0 0 16 16\\" width=\\"1em\\" height=\\"1em\\" class=\\"t-icon t-icon-close\\"><path fill=\\"currentColor\\" d=\\"M8 8.92L11.08 12l.92-.92L8.92 8 12 4.92 11.08 4 8 7.08 4.92 4 4 4.92 7.08 8 4 11.08l.92.92L8 8.92z\\" fill-opacity=\\"0.9\\"></path></svg></div><div class=\\"t-drawer__header\\"></div><div class=\\"t-drawer__body\\"><p>抽屉的内容</p></div><div class=\\"t-drawer__footer\\"><div style=\\"display:flex;justify-content:flex-start\\"><button type=\\"button\\" class=\\"t-drawer__confirm t-button t-button--theme-primary t-button--variant-base\\"><span class=\\"t-button__text\\">确认</span></button><button type=\\"button\\" class=\\"t-drawer__cancel t-button t-button--theme-default t-button--variant-base\\"><span class=\\"t-button__text\\">取消</span></button></div></div><div style=\\"z-index:1;position:absolute;background:transparent;left:0;width:16px;height:100%;cursor:col-resize\\"></div></div></div></div></div></div>"`;

exports[`ssr snapshot test renders ./src/dropdown/_example/base.jsx correctly 1`] = `"<div class=\\"t-popup__reference\\"><button type=\\"button\\" class=\\"t-button t-button--theme-default t-button--variant-text\\"><span class=\\"t-button__text\\"><span style=\\"display:inline-flex\\">更多<svg class=\\"t-icon t-icon-chevron-down\\" style=\\"font-size:16\\"><use xlink:href=\\"#t-icon-chevron-down\\"></use></svg></span></span></button></div>"`;

exports[`ssr snapshot test renders ./src/dropdown/_example/button.jsx correctly 1`] = `"<div class=\\"t-popup__reference\\"><button type=\\"button\\" class=\\"t-button t-button--theme-default t-button--variant-outline\\"><span class=\\"t-button__text\\"><svg class=\\"t-icon t-icon-ellipsis\\" style=\\"font-size:16\\"><use xlink:href=\\"#t-icon-ellipsis\\"></use></svg></span></button></div>"`;
Expand Down

0 comments on commit 7376e6a

Please sign in to comment.