Skip to content

Commit

Permalink
Merge pull request react-bootstrap#1172 from react-bootstrap/dom-utils
Browse files Browse the repository at this point in the history
Dom utils
  • Loading branch information
jquense committed Aug 20, 2015
2 parents 136d442 + 653d2ff commit 6f13e04
Show file tree
Hide file tree
Showing 10 changed files with 61 additions and 211 deletions.
7 changes: 4 additions & 3 deletions docs/src/ComponentsPage.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/* eslint react/no-did-mount-set-state: 0 */

import React from 'react';
import getOffset from 'dom-helpers/query/offset';
import css from 'dom-helpers/style';

import Affix from '../../src/Affix';
import Nav from '../../src/Nav';
Expand Down Expand Up @@ -33,9 +35,8 @@ const ComponentsPage = React.createClass({

componentDidMount() {
let elem = React.findDOMNode(this.refs.sideNav);
let domUtils = Affix.domUtils;
let sideNavOffsetTop = domUtils.getOffset(elem).top;
let sideNavMarginTop = parseInt(domUtils.getComputedStyles(elem.firstChild).marginTop, 10);
let sideNavOffsetTop = getOffset(elem).top;
let sideNavMarginTop = parseInt(css(elem.firstChild, 'marginTop'), 10);
let topNavHeight = React.findDOMNode(this.refs.topNav).offsetHeight;

this.setState({
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@
},
"dependencies": {
"babel-runtime": "^5.8.19",
"lodash": "^3.10.0",
"classnames": "^2.1.3"
"classnames": "^2.1.3",
"dom-helpers": "^2.2.4",
"lodash": "^3.10.0"
}
}
4 changes: 0 additions & 4 deletions src/Affix.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import React from 'react';
import classNames from 'classnames';
import AffixMixin from './AffixMixin';
import domUtils from './utils/domUtils';

const Affix = React.createClass({
statics: {
domUtils
},

mixins: [AffixMixin],

Expand Down
5 changes: 2 additions & 3 deletions src/Collapse.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@ const MARGINS = {

function getDimensionValue(dimension, elem){
let value = elem[`offset${capitalize(dimension)}`];
let computedStyles = domUtils.getComputedStyles(elem);
let margins = MARGINS[dimension];

return (value +
parseInt(computedStyles[margins[0]], 10) +
parseInt(computedStyles[margins[1]], 10)
parseInt(domUtils.css(elem, margins[0]), 10) +
parseInt(domUtils.css(elem, margins[1]), 10)
);
}

Expand Down
33 changes: 5 additions & 28 deletions src/Modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import React, { cloneElement } from 'react';
import classNames from 'classnames';
import domUtils from './utils/domUtils';
import getScrollbarSize from 'dom-helpers/util/scrollbarSize';
import EventListener from './utils/EventListener';
import createChainedFunction from './utils/createChainedFunction';
import CustomPropTypes from './utils/CustomPropTypes';
Expand Down Expand Up @@ -67,28 +68,6 @@ function onFocus(context, handler) {
return currentFocusListener;
}

let scrollbarSize;

function getScrollbarSize() {
if (scrollbarSize !== undefined) {
return scrollbarSize;
}

let scrollDiv = document.createElement('div');

scrollDiv.style.position = 'absolute';
scrollDiv.style.top = '-9999px';
scrollDiv.style.width = '50px';
scrollDiv.style.height = '50px';
scrollDiv.style.overflow = 'scroll';

document.body.appendChild(scrollDiv);
scrollbarSize = scrollDiv.offsetWidth - scrollDiv.clientWidth;
document.body.removeChild(scrollDiv);

scrollDiv = null;
return scrollbarSize;
}

const Modal = React.createClass({
propTypes: {
Expand Down Expand Up @@ -377,18 +356,16 @@ const Modal = React.createClass({

checkForFocus() {
if (domUtils.canUseDom) {
try {
this.lastFocus = document.activeElement;
}
catch (e) {} // eslint-disable-line no-empty
this.lastFocus = domUtils.activeElement(document);
}
},

focusModalContent() {
let modalContent = React.findDOMNode(this.refs.dialog);
let current = domUtils.activeElement(this);
let current = domUtils.activeElement(domUtils.ownerDocument(this));
let focusInModal = current && domUtils.contains(modalContent, current);


if (modalContent && this.props.autoFocus && !focusInModal) {
this.lastFocus = current;
modalContent.focus();
Expand All @@ -407,7 +384,7 @@ const Modal = React.createClass({
return;
}

let active = domUtils.activeElement(this);
let active = domUtils.activeElement(domUtils.ownerDocument(this));
let modal = React.findDOMNode(this.refs.dialog);

if (modal && modal !== active && !domUtils.contains(modal, active)) {
Expand Down
28 changes: 25 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import deprecationWarning from './utils/deprecationWarning';

export Accordion from './Accordion';
export Affix from './Affix';
export AffixMixin from './AffixMixin';
Expand Down Expand Up @@ -70,16 +72,36 @@ export Fade from './Collapse';

export * as FormControls from './FormControls';

import domUtils from './utils/domUtils';
import childrenValueInputValidation from './utils/childrenValueInputValidation';
import createChainedFunction from './utils/createChainedFunction';
import domUtils from './utils/domUtils';
import ValidComponentChildren from './utils/ValidComponentChildren';
import CustomPropTypes from './utils/CustomPropTypes';

export const utils = {
childrenValueInputValidation,
createChainedFunction,
domUtils,
ValidComponentChildren,
CustomPropTypes
CustomPropTypes,
domUtils: createDeprecationWrapper(domUtils, 'utils/domUtils', 'npm install dom-helpers'),
};

function createDeprecationWrapper(obj, deprecated, instead, link){
let wrapper = {};

if (process.env.NODE_ENV === 'production'){
return obj;
}

Object.keys(obj).forEach(key => {
Object.defineProperty(wrapper, key, {
get(){
deprecationWarning(deprecated, instead, link);
return obj[key];
},
set(x){ obj[key] = x; }
});
});

return wrapper;
}
184 changes: 17 additions & 167 deletions src/utils/domUtils.js
Original file line number Diff line number Diff line change
@@ -1,142 +1,33 @@
import React from 'react';
import canUseDom from 'dom-helpers/util/inDOM';

import getOwnerDocument from 'dom-helpers/ownerDocument';
import getOwnerWindow from 'dom-helpers/ownerWindow';

let canUseDom = !!(
typeof window !== 'undefined' &&
window.document &&
window.document.createElement
);
import contains from 'dom-helpers/query/contains';
import activeElement from 'dom-helpers/activeElement';

import getOffset from 'dom-helpers/query/offset';
import offsetParent from 'dom-helpers/query/offsetParent';
import getPosition from 'dom-helpers/query/position';

import css from 'dom-helpers/style';

/**
* Get elements owner document
*
* @param {ReactComponent|HTMLElement} componentOrElement
* @returns {HTMLElement}
*/
function ownerDocument(componentOrElement) {
let elem = React.findDOMNode(componentOrElement);
return (elem && elem.ownerDocument) || document;
return getOwnerDocument((elem && elem.ownerDocument) || document);
}

function ownerWindow(componentOrElement) {
let doc = ownerDocument(componentOrElement);
return doc.defaultView
? doc.defaultView
: doc.parentWindow;
}

/**
* get the active element, safe in IE
* @return {HTMLElement}
*/
function getActiveElement(componentOrElement){
let doc = ownerDocument(componentOrElement);

try {
return doc.activeElement || doc.body;
} catch (e) {
return doc.body;
}
return getOwnerWindow(doc);
}

/**
* Shortcut to compute element style
*
* @param {HTMLElement} elem
* @returns {CssStyle}
*/
//TODO remove in 0.26
function getComputedStyles(elem) {
return ownerDocument(elem).defaultView.getComputedStyle(elem, null);
}

/**
* Get elements offset
*
* TODO: REMOVE JQUERY!
*
* @param {HTMLElement} DOMNode
* @returns {{top: number, left: number}}
*/
function getOffset(DOMNode) {
if (window.jQuery) {
return window.jQuery(DOMNode).offset();
}

let docElem = ownerDocument(DOMNode).documentElement;
let box = { top: 0, left: 0 };

// If we don't have gBCR, just use 0,0 rather than error
// BlackBerry 5, iOS 3 (original iPhone)
if ( typeof DOMNode.getBoundingClientRect !== 'undefined' ) {
box = DOMNode.getBoundingClientRect();
}

return {
top: box.top + window.pageYOffset - docElem.clientTop,
left: box.left + window.pageXOffset - docElem.clientLeft
};
}

/**
* Get elements position
*
* TODO: REMOVE JQUERY!
*
* @param {HTMLElement} elem
* @param {HTMLElement?} offsetParent
* @returns {{top: number, left: number}}
*/
function getPosition(elem, offsetParent) {
let offset,
parentOffset;

if (window.jQuery) {
if (!offsetParent) {
return window.jQuery(elem).position();
}

offset = window.jQuery(elem).offset();
parentOffset = window.jQuery(offsetParent).offset();

// Get element offset relative to offsetParent
return {
top: offset.top - parentOffset.top,
left: offset.left - parentOffset.left
};
}

parentOffset = {top: 0, left: 0};

// Fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is its only offset parent
if (getComputedStyles(elem).position === 'fixed' ) {
// We assume that getBoundingClientRect is available when computed position is fixed
offset = elem.getBoundingClientRect();

} else {
if (!offsetParent) {
// Get *real* offsetParent
offsetParent = offsetParentFunc(elem);
}

// Get correct offsets
offset = getOffset(elem);
if ( offsetParent.nodeName !== 'HTML') {
parentOffset = getOffset(offsetParent);
}

// Add offsetParent borders
parentOffset.top += parseInt(getComputedStyles(offsetParent).borderTopWidth, 10);
parentOffset.left += parseInt(getComputedStyles(offsetParent).borderLeftWidth, 10);
}

// Subtract parent offsets and element margins
return {
top: offset.top - parentOffset.top - parseInt(getComputedStyles(elem).marginTop, 10),
left: offset.left - parentOffset.left - parseInt(getComputedStyles(elem).marginLeft, 10)
};
}

/**
* Get an element's size
*
Expand All @@ -156,57 +47,16 @@ function getSize(elem) {
return rect;
}

/**
* Get parent element
*
* @param {HTMLElement?} elem
* @returns {HTMLElement}
*/
function offsetParentFunc(elem) {
let docElem = ownerDocument(elem).documentElement;
let offsetParent = elem.offsetParent || docElem;

while ( offsetParent && ( offsetParent.nodeName !== 'HTML' &&
getComputedStyles(offsetParent).position === 'static' ) ) {
offsetParent = offsetParent.offsetParent;
}

return offsetParent || docElem;
}

/**
* Cross browser .contains() polyfill
* @param {HTMLElement} elem
* @param {HTMLElement} inner
* @return {bool}
*/
function contains(elem, inner){
function ie8Contains(root, node) {
while (node) {
if (node === root) {
return true;
}
node = node.parentNode;
}
return false;
}

return (elem && elem.contains)
? elem.contains(inner)
: (elem && elem.compareDocumentPosition)
? elem === inner || !!(elem.compareDocumentPosition(inner) & 16)
: ie8Contains(elem, inner);
}

export default {
canUseDom,
css,
getComputedStyles,
contains,
ownerWindow,
ownerDocument,
getComputedStyles,
getOffset,
getPosition,
getSize,
activeElement: getActiveElement,
offsetParent: offsetParentFunc
activeElement,
offsetParent
};
Loading

0 comments on commit 6f13e04

Please sign in to comment.