import React, { useRef } from 'react' import { observer } from '@formily/reactive-react' import { Engine, DragStartEvent, DragMoveEvent, DragStopEvent, ScreenStatus, } from '@designable/core' import { calcSpeedFactor, createUniformSpeedAnimation, } from '@designable/shared' import { useScreen, useDesigner, usePrefix } from '../../hooks' import { IconWidget } from '../../widgets' import { ResizeHandle, ResizeHandleType } from './handle' import cls from 'classnames' import './styles.less' const useResizeEffect = ( container: React.MutableRefObject, content: React.MutableRefObject, engine: Engine ) => { let status: ResizeHandleType = null let startX = 0 let startY = 0 let startWidth = 0 let startHeight = 0 let animationX = null let animationY = null const getStyle = (status: ResizeHandleType) => { if (status === ResizeHandleType.Resize) return 'nwse-resize' if (status === ResizeHandleType.ResizeHeight) return 'ns-resize' if (status === ResizeHandleType.ResizeWidth) return 'ew-resize' } const updateSize = (deltaX: number, deltaY: number) => { const containerRect = container.current?.getBoundingClientRect() if (status === ResizeHandleType.Resize) { engine.screen.setSize(startWidth + deltaX, startHeight + deltaY) container.current.scrollBy( containerRect.width + deltaX, containerRect.height + deltaY ) } else if (status === ResizeHandleType.ResizeHeight) { engine.screen.setSize(startWidth, startHeight + deltaY) container.current.scrollBy( container.current.scrollLeft, containerRect.height + deltaY ) } else if (status === ResizeHandleType.ResizeWidth) { engine.screen.setSize(startWidth + deltaX, startHeight) container.current.scrollBy( containerRect.width + deltaX, container.current.scrollTop ) } } engine.subscribeTo(DragStartEvent, (e) => { if (!engine.workbench.currentWorkspace?.viewport) return const target = e.data.target as HTMLElement if (target?.closest('*[data-designer-resize-handle]')) { const rect = content.current?.getBoundingClientRect() if (!rect) return status = target.getAttribute( 'data-designer-resize-handle' ) as ResizeHandleType engine.cursor.setStyle(getStyle(status)) startX = e.data.topClientX startY = e.data.topClientY startWidth = rect.width startHeight = rect.height engine.screen.setStatus(ScreenStatus.Resizing) } }) engine.subscribeTo(DragMoveEvent, (e) => { if (!engine.workbench.currentWorkspace?.viewport) return if (!status) return const deltaX = e.data.topClientX - startX const deltaY = e.data.topClientY - startY const containerRect = container.current?.getBoundingClientRect() const distanceX = Math.floor(containerRect.right - e.data.topClientX) const distanceY = Math.floor(containerRect.bottom - e.data.topClientY) const factorX = calcSpeedFactor(distanceX, 10) const factorY = calcSpeedFactor(distanceY, 10) updateSize(deltaX, deltaY) if (distanceX <= 10) { if (!animationX) { animationX = createUniformSpeedAnimation(1000 * factorX, (delta) => { updateSize(deltaX + delta, deltaY) }) } } else { if (animationX) { animationX = animationX() } } if (distanceY <= 10) { if (!animationY) { animationY = createUniformSpeedAnimation(300 * factorY, (delta) => { updateSize(deltaX, deltaY + delta) }) } } else { if (animationY) { animationY = animationY() } } }) engine.subscribeTo(DragStopEvent, () => { if (!status) return status = null engine.cursor.setStyle('') engine.screen.setStatus(ScreenStatus.Normal) if (animationX) { animationX = animationX() } if (animationY) { animationY = animationY() } }) } export interface IResponsiveSimulatorProps extends React.HTMLAttributes { className?: string style?: React.CSSProperties } export const ResponsiveSimulator: React.FC = observer((props) => { const container = useRef() const content = useRef() const prefix = usePrefix('responsive-simulator') const screen = useScreen() useDesigner((engine) => { useResizeEffect(container, content, engine) }) return (
{props.children}
) })