Skip to content

Commit f33352e

Browse files
committed
Merge branch 'dherbolt-user-select'
2 parents 55f701e + 28be6da commit f33352e

File tree

3 files changed

+113
-98
lines changed

3 files changed

+113
-98
lines changed

lib/DraggableCore.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ export default class DraggableCore extends React.Component<DraggableCoreProps, D
223223
removeEvent(ownerDocument, eventsFor.touch.move, this.handleDrag);
224224
removeEvent(ownerDocument, eventsFor.mouse.stop, this.handleDragStop);
225225
removeEvent(ownerDocument, eventsFor.touch.stop, this.handleDragStop);
226-
if (this.props.enableUserSelectHack) removeUserSelectStyles(ownerDocument.body);
226+
if (this.props.enableUserSelectHack) removeUserSelectStyles(ownerDocument);
227227
}
228228
}
229229

@@ -272,7 +272,7 @@ export default class DraggableCore extends React.Component<DraggableCoreProps, D
272272

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

277277
// Initiate dragging. Set the current x and y as offsets
278278
// so we know how much we've moved during the drag. This allows us
@@ -343,10 +343,11 @@ export default class DraggableCore extends React.Component<DraggableCoreProps, D
343343
if (position == null) return;
344344
const {x, y} = position;
345345
const coreEvent = createCoreData(this, x, y);
346+
346347
const thisNode = ReactDOM.findDOMNode(this);
347348
if (thisNode) {
348349
// Remove user-select hack
349-
if (this.props.enableUserSelectHack) removeUserSelectStyles(thisNode.ownerDocument.body);
350+
if (this.props.enableUserSelectHack) removeUserSelectStyles(thisNode.ownerDocument);
350351
}
351352

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

lib/utils/domFns.js

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// @flow
22
import {findInArray, isFunction, int} from './shims';
3-
import browserPrefix, {getPrefix, browserPrefixToStyle, browserPrefixToKey} from './getPrefix';
3+
import browserPrefix, {browserPrefixToKey} from './getPrefix';
44

55
import type {ControlPosition} from './types';
66

@@ -127,23 +127,24 @@ export function getTouchIdentifier(e: MouseTouchEvent): ?number {
127127
// User-select Hacks:
128128
//
129129
// Useful for preventing blue highlights all over everything when dragging.
130-
const userSelectPrefix = getPrefix('user-select');
131-
const userSelect = browserPrefixToStyle('user-select', userSelectPrefix);
132-
const userSelectStyle = `;${userSelect}: none;`;
133-
const userSelectReplaceRegExp = new RegExp(`;?${userSelect}: none;`); // leading ; not present on IE
134130

135131
// Note we're passing `document` b/c we could be iframed
136-
export function addUserSelectStyles(body: ?HTMLElement) {
137-
if (!body) return;
138-
const style = body.getAttribute('style') || '';
139-
if (userSelectReplaceRegExp.test(style)) return; // don't add twice
140-
body.setAttribute('style', style + userSelectStyle);
132+
export function addUserSelectStyles(doc: Document) {
133+
let styleEl = doc.getElementById('react-draggable-style-el');
134+
if (!styleEl) {
135+
styleEl = doc.createElement('style');
136+
styleEl.type = 'text/css';
137+
styleEl.id = 'react-draggable-style-el';
138+
styleEl.innerHTML = '.react-draggable-transparent-selection *::-moz-selection {background: transparent;}\n';
139+
styleEl.innerHTML += '.react-draggable-transparent-selection *::selection {background: transparent;}\n';
140+
doc.getElementsByTagName('head')[0].appendChild(styleEl);
141+
}
142+
if (doc.body) addClassName(doc.body, 'react-draggable-transparent-selection');
141143
}
142144

143-
export function removeUserSelectStyles(body: ?HTMLElement) {
144-
if (!body) return;
145-
const style = body.getAttribute('style') || '';
146-
body.setAttribute('style', style.replace(userSelectReplaceRegExp, ''));
145+
export function removeUserSelectStyles(doc: Document) {
146+
if (doc.body) removeClassName(doc.body, 'react-draggable-transparent-selection');
147+
window.getSelection().removeAllRanges(); // remove selection caused by scroll
147148
}
148149

149150
export function styleHacks(childStyle: Object = {}): Object {
@@ -154,3 +155,21 @@ export function styleHacks(childStyle: Object = {}): Object {
154155
...childStyle
155156
};
156157
}
158+
159+
export function addClassName(el: HTMLElement, className: string) {
160+
if (el.classList) {
161+
el.classList.add(className);
162+
} else {
163+
if (!el.className.match(new RegExp(`(?:^|\\s)${className}(?!\\S)`))) {
164+
el.className += ` ${className}`;
165+
}
166+
}
167+
}
168+
169+
export function removeClassName(el: HTMLElement, className: string) {
170+
if (el.classList) {
171+
el.classList.remove(className);
172+
} else {
173+
el.className = el.className.replace(new RegExp(`(?:^|\\s)${className}(?!\\S)`, 'g'), '');
174+
}
175+
}

specs/draggable.spec.jsx

Lines changed: 76 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -320,58 +320,56 @@ describe('react-draggable', function () {
320320
assert(transform.indexOf('translate(100,100)') >= 0);
321321
});
322322

323-
it('should add and remove user-select styles', function () {
324-
const userSelectStyleStr = `${userSelectStyle}: none;`;
325-
326-
drag = TestUtils.renderIntoDocument(
327-
<Draggable>
328-
<div />
329-
</Draggable>
330-
);
331-
332-
const node = ReactDOM.findDOMNode(drag);
333-
334-
assert(!document.body.getAttribute('style'));
335-
TestUtils.Simulate.mouseDown(node, {clientX: 0, clientY: 0});
336-
assert(document.body.getAttribute('style').indexOf(userSelectStyleStr) !== -1);
337-
TestUtils.Simulate.mouseUp(node);
338-
assert(!document.body.getAttribute('style'));
339-
});
340-
341-
it('should not add and remove user-select styles when disabled', function () {
342-
343-
drag = TestUtils.renderIntoDocument(
344-
<Draggable enableUserSelectHack={false}>
345-
<div />
346-
</Draggable>
347-
);
348-
349-
const node = ReactDOM.findDOMNode(drag);
350-
351-
assert(!document.body.getAttribute('style'));
352-
TestUtils.Simulate.mouseDown(node, {clientX: 0, clientY: 0});
353-
assert(!document.body.getAttribute('style'));
354-
TestUtils.Simulate.mouseUp(node);
355-
assert(!document.body.getAttribute('style'));
356-
});
357-
358-
it('should not add and remove user-select styles when onStart returns false', function () {
359-
function onStart() { return false; }
360-
361-
drag = TestUtils.renderIntoDocument(
362-
<Draggable onStart={onStart}>
363-
<div />
364-
</Draggable>
365-
);
366-
367-
const node = ReactDOM.findDOMNode(drag);
368-
369-
assert(!document.body.getAttribute('style'));
370-
TestUtils.Simulate.mouseDown(node, {clientX: 0, clientY: 0});
371-
assert(!document.body.getAttribute('style'));
372-
TestUtils.Simulate.mouseUp(node);
373-
assert(!document.body.getAttribute('style'));
374-
});
323+
it('should add and remove transparent selection class', function () {
324+
drag = TestUtils.renderIntoDocument(
325+
<Draggable>
326+
<div />
327+
</Draggable>
328+
);
329+
330+
const node = ReactDOM.findDOMNode(drag);
331+
332+
assert(!document.body.classList.contains('react-draggable-transparent-selection'));
333+
TestUtils.Simulate.mouseDown(node, {clientX: 0, clientY: 0});
334+
assert(document.body.classList.contains('react-draggable-transparent-selection'));
335+
TestUtils.Simulate.mouseUp(node);
336+
assert(!document.body.classList.contains('react-draggable-transparent-selection'));
337+
});
338+
339+
it('should not add and remove transparent selection class when disabled', function () {
340+
341+
drag = TestUtils.renderIntoDocument(
342+
<Draggable enableUserSelectHack={false}>
343+
<div />
344+
</Draggable>
345+
);
346+
347+
const node = ReactDOM.findDOMNode(drag);
348+
349+
assert(!document.body.classList.contains('react-draggable-transparent-selection'));
350+
TestUtils.Simulate.mouseDown(node, {clientX: 0, clientY: 0});
351+
assert(!document.body.classList.contains('react-draggable-transparent-selection'));
352+
TestUtils.Simulate.mouseUp(node);
353+
assert(!document.body.classList.contains('react-draggable-transparent-selection'));
354+
});
355+
356+
it('should not add and remove transparent selection class when onStart returns false', function () {
357+
function onStart() { return false; }
358+
359+
drag = TestUtils.renderIntoDocument(
360+
<Draggable onStart={onStart}>
361+
<div />
362+
</Draggable>
363+
);
364+
365+
const node = ReactDOM.findDOMNode(drag);
366+
367+
assert(!document.body.classList.contains('react-draggable-transparent-selection'));
368+
TestUtils.Simulate.mouseDown(node, {clientX: 0, clientY: 0});
369+
assert(!document.body.classList.contains('react-draggable-transparent-selection'));
370+
TestUtils.Simulate.mouseUp(node);
371+
assert(!document.body.classList.contains('react-draggable-transparent-selection'));
372+
});
375373

376374
it('should be draggable when in an iframe', function (done) {
377375
let dragged = false;
@@ -397,35 +395,32 @@ describe('react-draggable', function () {
397395
}, 50);
398396
});
399397

400-
it('should add and remove user-select styles to iframe’s body when in an iframe', function (done) {
401-
const userSelectStyleStr = `${userSelectStyle}: none;`;
402-
403-
const dragElement = (
404-
<Draggable>
405-
<div />
406-
</Draggable>
407-
);
408-
const renderRoot = document.body.appendChild(document.createElement('div'));
409-
const frame = ReactDOM.render(<FrameComponent>{ dragElement }</FrameComponent>, renderRoot);
410-
411-
setTimeout(() => {
412-
const iframeDoc = ReactDOM.findDOMNode(frame).contentDocument;
413-
const node = iframeDoc.querySelector('.react-draggable');
414-
iframeDoc.body.setAttribute('style', '');
415-
416-
assert(!iframeDoc.body.getAttribute('style'));
417-
assert(!document.body.getAttribute('style'));
418-
TestUtils.Simulate.mouseDown(node, {clientX: 0, clientY: 0});
419-
assert(iframeDoc.body.getAttribute('style').indexOf(userSelectStyleStr) !== -1);
420-
assert(!document.body.getAttribute('style'));
421-
TestUtils.Simulate.mouseUp(node);
422-
assert(!iframeDoc.body.getAttribute('style'));
423-
assert(!document.body.getAttribute('style'));
424-
425-
renderRoot.parentNode.removeChild(renderRoot);
426-
done();
427-
}, 50);
428-
});
398+
it('should add and remove transparent selection class to iframe’s body when in an iframe', function (done) {
399+
const dragElement = (
400+
<Draggable>
401+
<div />
402+
</Draggable>
403+
);
404+
const renderRoot = document.body.appendChild(document.createElement('div'));
405+
const frame = ReactDOM.render(<FrameComponent>{ dragElement }</FrameComponent>, renderRoot);
406+
407+
setTimeout(() => {
408+
const iframeDoc = ReactDOM.findDOMNode(frame).contentDocument;
409+
const node = iframeDoc.querySelector('.react-draggable');
410+
411+
assert(!document.body.classList.contains('react-draggable-transparent-selection'));
412+
assert(!iframeDoc.body.classList.contains('react-draggable-transparent-selection'));
413+
TestUtils.Simulate.mouseDown(node, {clientX: 0, clientY: 0});
414+
assert(!document.body.classList.contains('react-draggable-transparent-selection'));
415+
assert(iframeDoc.body.classList.contains('react-draggable-transparent-selection'));
416+
TestUtils.Simulate.mouseUp(node);
417+
assert(!document.body.classList.contains('react-draggable-transparent-selection'));
418+
assert(!iframeDoc.body.classList.contains('react-draggable-transparent-selection'));
419+
420+
renderRoot.parentNode.removeChild(renderRoot);
421+
done();
422+
}, 50);
423+
});
429424
});
430425

431426
describe('interaction', function () {

0 commit comments

Comments
 (0)