mirror of
https://gitee.com/baidu/amis.git
synced 2024-12-02 03:58:07 +08:00
feat : dialog支持拖拽移动位置
This commit is contained in:
parent
e3ebd970a2
commit
764a155e9f
@ -261,6 +261,11 @@ export class DialogPlugin extends BasePlugin {
|
||||
name: 'showLoading',
|
||||
value: true
|
||||
}),
|
||||
getSchemaTpl('switch', {
|
||||
label: '是否可拖拽',
|
||||
name: 'draggable',
|
||||
value: false
|
||||
}),
|
||||
getSchemaTpl('dataMap')
|
||||
]
|
||||
}
|
||||
|
@ -65,6 +65,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
&-draggable > &-header {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
&-overlay {
|
||||
transition: ease-in-out opacity var(--animation-duration);
|
||||
position: fixed;
|
||||
|
@ -17,6 +17,13 @@ import {ClassNamesFn, themeable, ThemeProps} from 'amis-core';
|
||||
import {Icon} from './icons';
|
||||
import {LocaleProps, localeable} from 'amis-core';
|
||||
import {autobind, getScrollbarWidth} from 'amis-core';
|
||||
import {
|
||||
DraggableCore,
|
||||
type DraggableBounds,
|
||||
type DraggableData,
|
||||
type DraggableEvent
|
||||
} from 'react-draggable';
|
||||
import isNumber from 'lodash/isNumber';
|
||||
|
||||
export const getContainerWithFullscreen =
|
||||
(container?: () => HTMLElement | HTMLElement | null) => () => {
|
||||
@ -53,8 +60,14 @@ export interface ModalProps extends ThemeProps, LocaleProps {
|
||||
children?: React.ReactNode | Array<React.ReactNode>;
|
||||
modalClassName?: string;
|
||||
modalMaskClassName?: string;
|
||||
draggable?: boolean;
|
||||
}
|
||||
export interface ModalState {}
|
||||
|
||||
export interface ModalState {
|
||||
bounds?: DraggableBounds;
|
||||
dragPos?: {x: number; y: number};
|
||||
}
|
||||
|
||||
const fadeStyles: {
|
||||
[propName: string]: string;
|
||||
} = {
|
||||
@ -62,6 +75,7 @@ const fadeStyles: {
|
||||
[ENTERED]: 'in',
|
||||
[EXITING]: 'out'
|
||||
};
|
||||
|
||||
const contentFadeStyles: {
|
||||
[propName: string]: string;
|
||||
} = {
|
||||
@ -69,11 +83,13 @@ const contentFadeStyles: {
|
||||
[ENTERED]: '',
|
||||
[EXITING]: 'out'
|
||||
};
|
||||
|
||||
export class Modal extends React.Component<ModalProps, ModalState> {
|
||||
static defaultProps = {
|
||||
container: document.body,
|
||||
size: '',
|
||||
overlay: true
|
||||
overlay: true,
|
||||
draggable: false
|
||||
};
|
||||
|
||||
isRootClosed = false;
|
||||
@ -173,6 +189,8 @@ export class Modal extends React.Component<ModalProps, ModalState> {
|
||||
)
|
||||
);
|
||||
|
||||
state: Readonly<ModalState> = {dragPos: undefined};
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.show) {
|
||||
this.handleEnter();
|
||||
@ -283,6 +301,87 @@ export class Modal extends React.Component<ModalProps, ModalState> {
|
||||
this.isRootClosed && !e.defaultPrevented && onHide(e);
|
||||
}
|
||||
|
||||
// #region 处理dialog拖动
|
||||
|
||||
handleDragStart = (_event: DraggableEvent, uiData: DraggableData) => {
|
||||
const node = uiData.node;
|
||||
const {offsetParent} = node;
|
||||
if (!node || !offsetParent) {
|
||||
return;
|
||||
}
|
||||
const {clientWidth, clientHeight} = window.document.documentElement;
|
||||
const nodeStyle = getComputedStyle(node);
|
||||
const marginTop = parseInt(nodeStyle.marginTop, 10);
|
||||
const nodeWidth = parseInt(nodeStyle.width, 10);
|
||||
const nodeHeight = parseInt(nodeStyle.height, 10);
|
||||
const bounds = {
|
||||
left: 0,
|
||||
right: clientWidth - nodeWidth,
|
||||
top: -marginTop,
|
||||
bottom: clientHeight - nodeHeight - marginTop
|
||||
};
|
||||
const parentRect = offsetParent.getBoundingClientRect();
|
||||
const clientRect = node.getBoundingClientRect();
|
||||
const cLeft = clientRect.left;
|
||||
const pLeft = parentRect.left;
|
||||
const cTop = clientRect.top;
|
||||
const pTop = parentRect.top;
|
||||
const left = cLeft - pLeft + offsetParent.scrollLeft;
|
||||
const top = cTop - pTop + offsetParent.scrollTop - marginTop;
|
||||
this.setState({dragPos: {x: left, y: top}, bounds});
|
||||
// 阻止冒泡 存在弹窗里面套弹窗
|
||||
_event.stopPropagation();
|
||||
};
|
||||
|
||||
handleDrag = (e: DraggableEvent, {deltaX, deltaY}: DraggableData) => {
|
||||
e.stopPropagation();
|
||||
if (!this.state.dragPos) {
|
||||
return;
|
||||
}
|
||||
const {
|
||||
dragPos: {x, y},
|
||||
bounds
|
||||
} = this.state;
|
||||
|
||||
let calcY = y + deltaY;
|
||||
let calcX = x + deltaX;
|
||||
|
||||
// 防止拖动到屏幕外 处理边界
|
||||
if (isNumber(bounds?.right)) {
|
||||
calcX = Math.min(calcX, bounds!.right);
|
||||
}
|
||||
if (isNumber(bounds?.bottom)) {
|
||||
calcY = Math.min(calcY, bounds!.bottom);
|
||||
}
|
||||
if (isNumber(bounds?.left)) {
|
||||
calcX = Math.max(calcX, bounds!.left);
|
||||
}
|
||||
if (isNumber(bounds?.top)) {
|
||||
calcY = Math.max(calcY, bounds!.top);
|
||||
}
|
||||
this.setState({dragPos: {x: calcX, y: calcY}});
|
||||
};
|
||||
|
||||
handleDragStop = (e: DraggableEvent) => {
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
getDragStyle = (): React.CSSProperties => {
|
||||
const {draggable} = this.props;
|
||||
const {dragPos} = this.state;
|
||||
if (!dragPos || !draggable) {
|
||||
return {};
|
||||
}
|
||||
const {x, y} = dragPos;
|
||||
return {
|
||||
top: `${y}px`,
|
||||
left: `${x}px`,
|
||||
position: 'absolute'
|
||||
};
|
||||
};
|
||||
|
||||
// #endregion
|
||||
|
||||
render() {
|
||||
const {
|
||||
className,
|
||||
@ -297,7 +396,10 @@ export class Modal extends React.Component<ModalProps, ModalState> {
|
||||
height,
|
||||
modalClassName,
|
||||
modalMaskClassName,
|
||||
classnames: cx
|
||||
classnames: cx,
|
||||
mobileUI,
|
||||
draggable,
|
||||
classPrefix
|
||||
} = this.props;
|
||||
|
||||
let _style = {
|
||||
@ -338,18 +440,27 @@ export class Modal extends React.Component<ModalProps, ModalState> {
|
||||
)}
|
||||
/>
|
||||
) : null}
|
||||
<div
|
||||
className={cx(
|
||||
`Modal-content`,
|
||||
size === 'custom' ? 'Modal-content-custom' : '',
|
||||
contentClassName,
|
||||
modalClassName,
|
||||
contentFadeStyles[status]
|
||||
)}
|
||||
style={_style}
|
||||
<DraggableCore
|
||||
disabled={!draggable || mobileUI}
|
||||
onStart={this.handleDragStart}
|
||||
onDrag={this.handleDrag}
|
||||
onStop={this.handleDragStop}
|
||||
handle={`.${classPrefix}Modal-header`}
|
||||
>
|
||||
{status === EXITED ? null : children}
|
||||
</div>
|
||||
<div
|
||||
className={cx(
|
||||
`Modal-content`,
|
||||
draggable && !mobileUI ? 'Modal-draggable' : '',
|
||||
size === 'custom' ? 'Modal-content-custom' : '',
|
||||
contentClassName,
|
||||
modalClassName,
|
||||
contentFadeStyles[status]
|
||||
)}
|
||||
style={{..._style, ...this.getDragStyle()}}
|
||||
>
|
||||
{status === EXITED ? null : children}
|
||||
</div>
|
||||
</DraggableCore>
|
||||
</div>
|
||||
</Portal>
|
||||
)}
|
||||
|
@ -123,6 +123,10 @@ export interface DialogSchema extends BaseSchema {
|
||||
* 弹框类型 confirm 确认弹框
|
||||
*/
|
||||
dialogType?: 'confirm';
|
||||
/**
|
||||
* 可拖拽
|
||||
*/
|
||||
draggable?: boolean;
|
||||
}
|
||||
|
||||
export type DialogSchemaBase = Omit<DialogSchema, 'type'>;
|
||||
@ -164,7 +168,8 @@ export default class Dialog extends React.Component<DialogProps> {
|
||||
'showErrorMsg',
|
||||
'actions',
|
||||
'popOverContainer',
|
||||
'overlay'
|
||||
'overlay',
|
||||
'draggable'
|
||||
];
|
||||
static defaultProps = {
|
||||
title: 'Dialog.title',
|
||||
|
Loading…
Reference in New Issue
Block a user