rc-align

rc-align模块基于dom-align模块实现,用于以参照节点调整事件源节点的位置。

Align.js

'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = require('react');
var _react2 = _interopRequireDefault(_react);

var _reactDom = require('react-dom');
var _reactDom2 = _interopRequireDefault(_reactDom);

// domAlign(sourceNode,refNode,options)根据参照节点refNode和配置options调整事件源节点的位置
var _domAlign = require('dom-align');
var _domAlign2 = _interopRequireDefault(_domAlign);

var _addEventListener = require('rc-util/lib/Dom/addEventListener');
var _addEventListener2 = _interopRequireDefault(_addEventListener);

var _isWindow = require('./isWindow');
var _isWindow2 = _interopRequireDefault(_isWindow);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }

// 设置定时器,执行fn;调用返回值启动执行定时器;调用返回值的clear方法,清除定时器
function buffer(fn, ms) {
  var timer = void 0;

  function clear() {
    if (timer) {
      clearTimeout(timer);
      timer = null;
    }
  }

  function bufferFn() {
    clear();
    timer = setTimeout(fn, ms);
  }

  bufferFn.clear = clear;

  return bufferFn;
}

var Align = _react2["default"].createClass({
  displayName: 'Align',

  propTypes: {
    childrenProps: _react.PropTypes.object,// 为props.children提供props的映射,属性值为this.props的属性名
    align: _react.PropTypes.object.isRequired,// 位置调整配置项
    target: _react.PropTypes.func,// 获取参照节点,同时用于判断组件重绘时是否调整事件源节点的位置
    onAlign: _react.PropTypes.func,// 事件源节点位置调整后执行的回调函数
    monitorBufferTime: _react.PropTypes.number,// 监听resize事件的定时事件
    monitorWindowResize: _react.PropTypes.bool,// 浏览器缩放时是否调整事件源节点的位置
    disabled: _react.PropTypes.bool,// 是否启用位置调整
    children: _react.PropTypes.any// 实际显示用的待渲染节点
  },

  getDefaultProps: function getDefaultProps() {
    return {
      target: function target() {
        return window;
      },
      onAlign: function onAlign(sourceNode,offsetInfo) {},
      monitorBufferTime: 50,
      monitorWindowResize: false,
      disabled: false
    };
  },
  componentDidMount: function componentDidMount() {
    var props = this.props;
    // 调整事件源节点的位置
    this.forceAlign();

    // 监听窗口缩放resize事件
    if (!props.disabled && props.monitorWindowResize) {
      this.startMonitorWindowResize();
    }
  },
  componentDidUpdate: function componentDidUpdate(prevProps) {
    var reAlign = false;
    var props = this.props;

    // props.align或props.target返回值变更,重新调整事件源节点的位置
    if (!props.disabled) {
      if (prevProps.disabled || prevProps.align !== props.align) {
        reAlign = true;
      } else {
        var lastTarget = prevProps.target();
        var currentTarget = props.target();
        if ((0, _isWindow2["default"])(lastTarget) && (0, _isWindow2["default"])(currentTarget)) {
          reAlign = false;
        } else if (lastTarget !== currentTarget) {
          reAlign = true;
        }
      }
    }

    if (reAlign) {
      this.forceAlign();
    }

    if (props.monitorWindowResize && !props.disabled) {
      this.startMonitorWindowResize();
    } else {
      this.stopMonitorWindowResize();
    }
  },

  // 组件卸载时,解绑resize事件及清除定时器
  componentWillUnmount: function componentWillUnmount() {
    this.stopMonitorWindowResize();
  },

  // 当浏览器窗口缩放、即'resize'事件发生时,调整事件源节点的位置
  startMonitorWindowResize: function startMonitorWindowResize() {
    if (!this.resizeHandler) {
      this.bufferMonitor = buffer(this.forceAlign, this.props.monitorBufferTime);
      this.resizeHandler = (0, _addEventListener2["default"])(window, 'resize', this.bufferMonitor);
    }
  },

  // 解绑'resize'事件的监听函数、及清除调整位置的定时器
  stopMonitorWindowResize: function stopMonitorWindowResize() {
    if (this.resizeHandler) {
      this.bufferMonitor.clear();
      this.resizeHandler.remove();
      this.resizeHandler = null;
    }
  },

  // 调用domAlign调整事件源节点的位置,并执行this.props.onAlign方法
  forceAlign: function forceAlign() {
    var props = this.props;
    if (!props.disabled) {
      // 获取事件源节点,即this.props.children渲染后的节点元素
      var source = _reactDom2["default"].findDOMNode(this);

      // 调用domAlign调整事件源节点的位置后,再调用this.props.onAlign(source,offsetInfo)
      // 参数offsetInfo是事件源节点实际位置调整数据
      props.onAlign(source, (0, _domAlign2["default"])(source, props.target(), props.align));
    }
  },

  // rc-align只为props.children提供位置调整的实现,渲染依旧是props.children的固有机制,除去props变更
  render: function render() {
    var _props = this.props,
        childrenProps = _props.childrenProps,
        children = _props.children;

    // this.props.children只允许为单个reactElement
    var child = _react2["default"].Children.only(children);

    // this.props.children映射this.props中将要置入子元素child的属性
    if (childrenProps) {
      var newProps = {};
      for (var prop in childrenProps) {
        if (childrenProps.hasOwnProperty(prop)) {
          newProps[prop] = this.props[childrenProps[prop]];
        }
      }

      // 使用react.cloneElement方法为child提供新的props
      return _react2["default"].cloneElement(child, newProps);
    }
    return child;
  }
});

exports["default"] = Align;
module.exports = exports['default'];

dom-align:index.js

'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _utils = require('./utils');
var _utils2 = _interopRequireDefault(_utils);

var _getOffsetParent = require('./getOffsetParent');
var _getOffsetParent2 = _interopRequireDefault(_getOffsetParent);

var _getVisibleRectForElement = require('./getVisibleRectForElement');
var _getVisibleRectForElement2 = _interopRequireDefault(_getVisibleRectForElement);

var _adjustForViewport = require('./adjustForViewport');
var _adjustForViewport2 = _interopRequireDefault(_adjustForViewport);

var _getRegion = require('./getRegion');
var _getRegion2 = _interopRequireDefault(_getRegion);

var _getElFuturePos = require('./getElFuturePos');
var _getElFuturePos2 = _interopRequireDefault(_getElFuturePos);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }

// http://yiminghe.iteye.com/blog/1124720

/**
 * align dom node flexibly
 * @author [email protected]
 */

function isFailX(elFuturePos, elRegion, visibleRect) {
  return elFuturePos.left < visibleRect.left || elFuturePos.left + elRegion.width > visibleRect.right;
}

function isFailY(elFuturePos, elRegion, visibleRect) {
  return elFuturePos.top < visibleRect.top || elFuturePos.top + elRegion.height > visibleRect.bottom;
}

function isCompleteFailX(elFuturePos, elRegion, visibleRect) {
  return elFuturePos.left > visibleRect.right || elFuturePos.left + elRegion.width < visibleRect.left;
}

function isCompleteFailY(elFuturePos, elRegion, visibleRect) {
  return elFuturePos.top > visibleRect.bottom || elFuturePos.top + elRegion.height < visibleRect.top;
}

function flip(points, reg, map) {
  var ret = [];
  _utils2["default"].each(points, function (p) {
    ret.push(p.replace(reg, function (m) {
      return map[m];
    }));
  });
  return ret;
}

function flipOffset(offset, index) {
  offset[index] = -offset[index];
  return offset;
}

function convertOffset(str, offsetLen) {
  var n = void 0;
  if (/%$/.test(str)) {
    n = parseInt(str.substring(0, str.length - 1), 10) / 100 * offsetLen;
  } else {
    n = parseInt(str, 10);
  }
  return n || 0;
}

function normalizeOffset(offset, el) {
  offset[0] = convertOffset(offset[0], el.width);
  offset[1] = convertOffset(offset[1], el.height);
}

// 参数el待调整位置的事件源节点
// 参数refNode调整位置时的参照节点
// 参数align配置项{points,offset,targetOffset,overflow,target,source}
// align.points设置参照节点和目标节点对齐时的参照点,如["tl","cr"],取el的左上位置对齐relNode的右中位置
// align.offset设置为百分比时,根据事件源节点的宽高执行偏移;数值时,即正向以该数值执行偏移[10,"20%"]
// align.offset设置为百分比时,根据参照节点的宽高反向执行偏移;数值时,即反向执行偏移
// align.adjustX、adjustY可放置区域无法容纳事件源节点时,反向进行调整;若反向调整失败,削减事件源节点的宽高进行调整
// align.useCssRight、useCssBottom、align.useCssTransform设置调整事件源节点的模式
function domAlign(el, refNode, align) {
  var points = align.points;
  var offset = align.offset || [0, 0];
  var targetOffset = align.targetOffset || [0, 0];
  var overflow = align.overflow;
  var target = align.target || refNode;
  var source = align.source || el;
  offset = [].concat(offset);
  targetOffset = [].concat(targetOffset);
  overflow = overflow || {};
  var newOverflowCfg = {};

  var fail = 0;

  // 获取事件源节点position="static"的祖先节点,作为可放置范围,计算其top、left、bottom、right
  var visibleRect = (0, _getVisibleRectForElement2["default"])(source);

  // 事件源节点所占的区域, 含left、top、width、height属性
  var elRegion = (0, _getRegion2["default"])(source);

  // 目标节点所占的区域, 含left、top、width、height属性
  var refNodeRegion = (0, _getRegion2["default"])(target);

  // 将偏移量offset转换成数值
  normalizeOffset(offset, elRegion);
  normalizeOffset(targetOffset, refNodeRegion);

  // 根据对齐点points及偏移量offset、targetOffset计算事件源节点最终的x、y坐标{left,top}
  var elFuturePos = (0, _getElFuturePos2["default"])(elRegion, refNodeRegion, points, offset, targetOffset);
  
  // 获取事件源节点对齐后的left、top属性,返回{left,top,width,height}
  var newElRegion = _utils2["default"].merge(elRegion, elFuturePos);

  // 可放置区域不能完全容纳事件源节点,作反向调整,如左对齐的变成右对齐、相应偏移量也反向
  if (visibleRect && (overflow.adjustX || overflow.adjustY)) {
    if (overflow.adjustX) {
      if (isFailX(elFuturePos, elRegion, visibleRect)) {
        var newPoints = flip(points, /[lr]/ig, {
          l: 'r',
          r: 'l'
        });
        var newOffset = flipOffset(offset, 0);
        var newTargetOffset = flipOffset(targetOffset, 0);
        var newElFuturePos = (0, _getElFuturePos2["default"])(elRegion, refNodeRegion, newPoints, newOffset, newTargetOffset);
        if (!isCompleteFailX(newElFuturePos, elRegion, visibleRect)) {
          fail = 1;
          points = newPoints;
          offset = newOffset;
          targetOffset = newTargetOffset;
        }
      }
    }

    if (overflow.adjustY) {
      if (isFailY(elFuturePos, elRegion, visibleRect)) {
        var _newPoints = flip(points, /[tb]/ig, {
          t: 'b',
          b: 't'
        });
        var _newOffset = flipOffset(offset, 1);
        var _newTargetOffset = flipOffset(targetOffset, 1);
        var _newElFuturePos = (0, _getElFuturePos2["default"])(elRegion, refNodeRegion, _newPoints, _newOffset, _newTargetOffset);
        if (!isCompleteFailY(_newElFuturePos, elRegion, visibleRect)) {
          fail = 1;
          points = _newPoints;
          offset = _newOffset;
          targetOffset = _newTargetOffset;
        }
      }
    }

    if (fail) {
      elFuturePos = (0, _getElFuturePos2["default"])(elRegion, refNodeRegion, points, offset, targetOffset);
      _utils2["default"].mix(newElRegion, elFuturePos);
    }

    newOverflowCfg.adjustX = overflow.adjustX && isFailX(elFuturePos, elRegion, visibleRect);
    newOverflowCfg.adjustY = overflow.adjustY && isFailY(elFuturePos, elRegion, visibleRect);

    // 配置项要求调整,同时反向调整失败,削减事件源节点的宽高进行调整;newElRegion计算事件源节点允许的宽高
    if (newOverflowCfg.adjustX || newOverflowCfg.adjustY) {
      newElRegion = (0, _adjustForViewport2["default"])(elFuturePos, elRegion, visibleRect, newOverflowCfg);
    }
  }

  // 削减宽高
  if (newElRegion.width !== elRegion.width) {
    _utils2["default"].css(source, 'width', _utils2["default"].width(source) + newElRegion.width - elRegion.width);
  }
  if (newElRegion.height !== elRegion.height) {
    _utils2["default"].css(source, 'height', _utils2["default"].height(source) + newElRegion.height - elRegion.height);
  }

  // 调整事件源节点的位置,尾参option约定调整的方式
  _utils2["default"].offset(source, {
    left: newElRegion.left,
    top: newElRegion.top
  }, {
    useCssRight: align.useCssRight,
    useCssBottom: align.useCssBottom,
    useCssTransform: align.useCssTransform
  });

  // 返回事件源节点实际的调整数据
  return {
    points: points,
    offset: offset,
    targetOffset: targetOffset,
    overflow: newOverflowCfg
  };
}

domAlign.__getOffsetParent = _getOffsetParent2["default"];

domAlign.__getVisibleRectForElement = _getVisibleRectForElement2["default"];

exports["default"] = domAlign;

module.exports = exports['default'];

猜你喜欢

转载自schifred.iteye.com/blog/2364445
今日推荐