react-draggable icon indicating copy to clipboard operation
react-draggable copied to clipboard

positionOffset effects the bounds

Open liubin915249126 opened this issue 4 years ago • 3 comments
trafficstars

<Draggable enableUserSelectHack={false} bounds="body" positionOffset={{ x: '-50%', y: '-50%' }} >

after I check the getBoundPosition function , I think node.offsetLeft does not contains css3 transform translate that positionOffset makes. referer How do I get the position of an element after css3 translation in JavaScript?

liubin915249126 avatar Dec 03 '20 08:12 liubin915249126

Maybe It is the same problem I've had recently. If you set values for 'positionOffset', then the positionOffset affects the entire draggable boundary so that the bounds you set will be also moved. I wanted the draggable element to be center of body, so I set values 'positionOffset={{ x: -50%, y: -50%}}' and css 'position: absolute; top: -50%; left: -50%;'. I don't know the reason but the css 'transform: translate(x, y)' value I set is not working. Maybe I guess the reason is that the transform value is variable with my dragging.

This is how I get through this problem.


const [bounds, setBounds] = useState({ left: null, right: null, top: null, bottom: null })

const handleStart = (e, data) => {
  if(data) {
    const x = data.node.offsetLeft - (data.node.offsetWidth / 2)
    const top = data.node.offsetTop
    const bottom = window.innerHeight - data.node.offsetHeight - data.note.offsetTop
    setBounds({ left: -x, right: x, top: top, bottom: bottom })
  }
}

<Draggable
  positionOffset={{ x: '-50%', y: '-50%' })
  onStart={handleStart}
  bounds={bounds}
...
>
<div
  style={{ position: 'absolute', top: '50%', left: '50% }}
...>
...
</div>
</Draggable>

then the Draggable element is placed in the center of the browser, and the 'bounds' works.

but It is the case when you want that the 'bounds' value is equal to the entire browser screen.

Anyway, I hope my comment was helpful.

seungha-0709 avatar Apr 30 '21 06:04 seungha-0709

@seungha-0709 Thanks for the idea. I had to adapt your code a bit to work with typescript. Plus, it looks like we only need to set the bounds once and draggable handles the rest.

import React from "react";
import Draggable from "react-draggable";
import s from "./modal.module.scss";

export interface Props {
    header: string,
    content: string,
    draggable?: boolean,
    boundingElement?: string,
    dragPadding?: number
}

export interface State {
    bounds: Object
}

export default class Modal extends React.PureComponent<Props, State> {

    private hasSetBounds = false;

    constructor(props: Props) {
        super(props);

        // Setup draggable
        if (this.props.draggable) {
            // Track the bounds state
            this.state = {
                bounds: { left: null, top: null, right: null, bottom: null}
            }
        }

    }

    handleDragStartOnce = (e: any, data: any) => {

        // Handles fixing the draggable bounds
        // Ref: https://github.com/react-grid-layout/react-draggable/issues/535
        if (this.hasSetBounds
            || !data
            || !this.props.draggable
            || !this.props.boundingElement) return;

        // Set our run-once flag
        this.hasSetBounds = true;

        // Padding applied to edges of bounding box
        const dragPadding = this.props.dragPadding || 10;

        // Grab the bounding element's bounding box data
        const boundingBox = document.querySelector(this.props.boundingElement)?.getBoundingClientRect();
        if (!boundingBox) {
            console.warn('Could not find bounding box of specified bounding element', this.props.boundingElement);
            return;
        }

        // Grab the node's bounding box data
        const nodeBox = data.node.getBoundingClientRect();

        // Set the state
        this.setState({
            bounds: {
                left: (nodeBox.left - boundingBox.left - dragPadding) * -1,
                right: boundingBox.right - nodeBox.right - dragPadding,
                top: (nodeBox.top - boundingBox.top - dragPadding) * -1,
                bottom: boundingBox.bottom - nodeBox.bottom - dragPadding,
            }
        });
    }

    render() {

        // Build our inner section we may wrap
        let childSection = (
            <div className={s.container}>
                <div className={s.titleBar} />
                <div className={s.mainBody}>
                    <h3 className={s.header}>{this.props.header}</h3>
                    <div className={s.content}>{this.props.content}</div>
                </div>
            </div>
        );

        // Wrap with draggable
        if (this.props.draggable) {
            const onStart = (this.props.boundingElement) ? this.handleDragStartOnce : undefined

            childSection = (
                <Draggable
                    bounds={this.state.bounds}
                    onStart={onStart}
                    handle={'.' + s.titleBar}
                    positionOffset={{ x: 0, y: 'calc(40vh - 50%)' }}>
                    {childSection}
                </Draggable>
            )
        }

        // Compile the final block
        return (
            <div className={s.modalWrapper}>
                {childSection}
            </div>
        );
    }
}

duckboy81 avatar Feb 20 '22 16:02 duckboy81