Skip to content

Use window/document relative to DOM element #180

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 19, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 20 additions & 14 deletions lib/DraggableCore.es6
Original file line number Diff line number Diff line change
Expand Up @@ -182,11 +182,12 @@ export default class DraggableCore extends React.Component {
componentWillUnmount() {
// Remove any leftover event handlers. Remove both touch and mouse handlers in case
// some browser quirk caused a touch event to fire during a mouse move, or vice versa.
removeEvent(document, eventsFor.mouse.move, this.handleDrag);
removeEvent(document, eventsFor.touch.move, this.handleDrag);
removeEvent(document, eventsFor.mouse.stop, this.handleDragStop);
removeEvent(document, eventsFor.touch.stop, this.handleDragStop);
if (this.props.enableUserSelectHack) removeUserSelectStyles();
const {ownerDocument} = ReactDOM.findDOMNode(this);
removeEvent(ownerDocument, eventsFor.mouse.move, this.handleDrag);
removeEvent(ownerDocument, eventsFor.touch.move, this.handleDrag);
removeEvent(ownerDocument, eventsFor.mouse.stop, this.handleDragStop);
removeEvent(ownerDocument, eventsFor.touch.stop, this.handleDragStop);
if (this.props.enableUserSelectHack) removeUserSelectStyles(ownerDocument.body);
}

handleDragStart: EventHandler<MouseEvent> = (e) => {
Expand All @@ -196,11 +197,15 @@ export default class DraggableCore extends React.Component {
// Only accept left-clicks.
if (!this.props.allowAnyClick && typeof e.button === 'number' && e.button !== 0) return false;

// Get nodes. Be sure to grab relative document (could be iframed)
const domNode = ReactDOM.findDOMNode(this);
const {ownerDocument} = domNode;

// Short circuit if handle or cancel prop was provided and selector doesn't match.
if (this.props.disabled ||
(!(e.target instanceof Node)) ||
(this.props.handle && !matchesSelectorAndParentsTo(e.target, this.props.handle, ReactDOM.findDOMNode(this))) ||
(this.props.cancel && matchesSelectorAndParentsTo(e.target, this.props.cancel, ReactDOM.findDOMNode(this)))) {
(!(e.target instanceof ownerDocument.defaultView.Node)) ||
(this.props.handle && !matchesSelectorAndParentsTo(e.target, this.props.handle, domNode)) ||
(this.props.cancel && matchesSelectorAndParentsTo(e.target, this.props.cancel, domNode))) {
return;
}

Expand All @@ -227,7 +232,7 @@ export default class DraggableCore extends React.Component {

// Add a style to the body to disable user-select. This prevents text from
// being selected all over the page.
if (this.props.enableUserSelectHack) addUserSelectStyles();
if (this.props.enableUserSelectHack) addUserSelectStyles(ownerDocument.body);

// Initiate dragging. Set the current x and y as offsets
// so we know how much we've moved during the drag. This allows us
Expand All @@ -242,8 +247,8 @@ export default class DraggableCore extends React.Component {
// Add events to the document directly so we catch when the user's mouse/touch moves outside of
// this element. We use different events depending on whether or not we have detected that this
// is a touch-capable device.
addEvent(document, dragEventFor.move, this.handleDrag);
addEvent(document, dragEventFor.stop, this.handleDragStop);
addEvent(ownerDocument, dragEventFor.move, this.handleDrag);
addEvent(ownerDocument, dragEventFor.stop, this.handleDragStop);
};

handleDrag: EventHandler<MouseEvent> = (e) => {
Expand Down Expand Up @@ -298,7 +303,7 @@ export default class DraggableCore extends React.Component {
const coreEvent = createCoreData(this, x, y);

// Remove user-select hack
if (this.props.enableUserSelectHack) removeUserSelectStyles();
if (this.props.enableUserSelectHack) removeUserSelectStyles(ReactDOM.findDOMNode(this).ownerDocument.body);

log('DraggableCore: handleDragStop: %j', coreEvent);

Expand All @@ -313,9 +318,10 @@ export default class DraggableCore extends React.Component {
this.props.onStop(e, coreEvent);

// Remove event handlers
const {ownerDocument} = ReactDOM.findDOMNode(this);
log('DraggableCore: Removing handlers');
removeEvent(document, dragEventFor.move, this.handleDrag);
removeEvent(document, dragEventFor.stop, this.handleDragStop);
removeEvent(ownerDocument, dragEventFor.move, this.handleDrag);
removeEvent(ownerDocument, dragEventFor.stop, this.handleDragStop);
};

onMouseDown: EventHandler<MouseEvent> = (e) => {
Expand Down
24 changes: 12 additions & 12 deletions lib/utils/domFns.es6
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export function outerHeight(node: HTMLElement): number {
// This is deliberately excluding margin for our calculations, since we are using
// offsetTop which is including margin. See getBoundPosition
let height = node.clientHeight;
const computedStyle = window.getComputedStyle(node);
const computedStyle = node.ownerDocument.defaultView.getComputedStyle(node);
height += int(computedStyle.borderTopWidth);
height += int(computedStyle.borderBottomWidth);
return height;
Expand All @@ -73,30 +73,29 @@ export function outerWidth(node: HTMLElement): number {
// This is deliberately excluding margin for our calculations, since we are using
// offsetLeft which is including margin. See getBoundPosition
let width = node.clientWidth;
const computedStyle = window.getComputedStyle(node);
const computedStyle = node.ownerDocument.defaultView.getComputedStyle(node);
width += int(computedStyle.borderLeftWidth);
width += int(computedStyle.borderRightWidth);
return width;
}
export function innerHeight(node: HTMLElement): number {
let height = node.clientHeight;
const computedStyle = window.getComputedStyle(node);
const computedStyle = node.ownerDocument.defaultView.getComputedStyle(node);
height -= int(computedStyle.paddingTop);
height -= int(computedStyle.paddingBottom);
return height;
}

export function innerWidth(node: HTMLElement): number {
let width = node.clientWidth;
const computedStyle = window.getComputedStyle(node);
const computedStyle = node.ownerDocument.defaultView.getComputedStyle(node);
width -= int(computedStyle.paddingLeft);
width -= int(computedStyle.paddingRight);
return width;
}

// Get from offsetParent
export function offsetXYFromParent(evt: {clientX: number, clientY: number}, offsetParent: ?HTMLElement): ControlPosition {
if (!offsetParent) offsetParent = document.body;
export function offsetXYFromParent(evt: {clientX: number, clientY: number}, offsetParent: HTMLElement): ControlPosition {
const isBody = offsetParent === offsetParent.ownerDocument.body;
const offsetParentRect = isBody ? {left: 0, top: 0} : offsetParent.getBoundingClientRect();

Expand Down Expand Up @@ -132,14 +131,15 @@ const userSelectPrefix = getPrefix('user-select');
const userSelect = browserPrefixToStyle('user-select', userSelectPrefix);
const userSelectStyle = `;${userSelect}: none;`;

export function addUserSelectStyles() {
const style = document.body.getAttribute('style') || '';
document.body.setAttribute('style', style + userSelectStyle);
// Note we're passing `document` b/c we could be iframed
export function addUserSelectStyles(body: HTMLElement) {
const style = body.getAttribute('style') || '';
body.setAttribute('style', style + userSelectStyle);
}

export function removeUserSelectStyles() {
const style = document.body.getAttribute('style') || '';
document.body.setAttribute('style', style.replace(userSelectStyle, ''));
export function removeUserSelectStyles(body: HTMLElement) {
const style = body.getAttribute('style') || '';
body.setAttribute('style', style.replace(userSelectStyle, ''));
}

export function styleHacks(childStyle: Object = {}): Object {
Expand Down
13 changes: 7 additions & 6 deletions lib/utils/positionFns.es6
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,17 @@ export function getBoundPosition(draggable: Draggable, x: number, y: number): [n
const node = ReactDOM.findDOMNode(draggable);

if (typeof bounds === 'string') {
const {currentDocument} = node;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currentDocument is a thing? Didn’t know about that one.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, it's actually not. Gap in our test coverage to be sure

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const currentWindow = node.defaultView;
let boundNode;
if (bounds === 'parent') {
boundNode = node.parentNode;
} else {
boundNode = document.querySelector(bounds);
boundNode = currentDocument.querySelector(bounds);
if (!boundNode) throw new Error('Bounds selector "' + bounds + '" could not find an element.');
}
const nodeStyle = window.getComputedStyle(node);
const boundNodeStyle = window.getComputedStyle(boundNode);
const nodeStyle = currentWindow.getComputedStyle(node);
const boundNodeStyle = currentWindow.getComputedStyle(boundNode);
// Compute bounds. This is a pain with padding and offsets but this gets it exactly right.
bounds = {
left: -node.offsetLeft + int(boundNodeStyle.paddingLeft) +
Expand Down Expand Up @@ -66,10 +68,9 @@ export function canDragY(draggable: Draggable): boolean {
export function getControlPosition(e: MouseEvent, touchIdentifier: ?number, draggableCore: DraggableCore): ?ControlPosition {
const touchObj = typeof touchIdentifier === 'number' ? getTouch(e, touchIdentifier) : null;
if (typeof touchIdentifier === 'number' && !touchObj) return null; // not the right touch
const node = ReactDOM.findDOMNode(draggableCore);
// User can provide an offsetParent if desired.
const offsetParent = draggableCore.props.offsetParent ||
ReactDOM.findDOMNode(draggableCore).offsetParent ||
document.body;
const offsetParent = draggableCore.props.offsetParent || node.offsetParent || node.ownerDocument.body;
return offsetXYFromParent(touchObj || e, offsetParent);
}

Expand Down