nuke-components
Advanced tools
Comparing version 0.2.26 to 0.2.27
# Changelog | ||
## 0.2.27 / 2017-05-12 | ||
* [[2091aa4](http://gitlab.alibaba-inc.com/nuke/components/commit/2091aa4929df888ec389d8bf7b72786655a0c041)] - `feat` add refresh control + scrollview | ||
## 0.2.26 / 2017-05-11 | ||
@@ -5,0 +9,0 @@ |
@@ -13,2 +13,14 @@ 'use strict'; | ||
var _nukeEnv2 = _interopRequireDefault(_nukeEnv); | ||
var _raxRefreshcontrol = require('rax-refreshcontrol'); | ||
var _raxRefreshcontrol2 = _interopRequireDefault(_raxRefreshcontrol); | ||
var _view = require('./view'); | ||
var _view2 = _interopRequireDefault(_view); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
@@ -21,2 +33,5 @@ | ||
var isWeex = _nukeEnv2.default.isWeex, | ||
isWeb = _nukeEnv2.default.isWeb; | ||
var RefreshControl = function (_Component) { | ||
@@ -34,11 +49,6 @@ _inherits(RefreshControl, _Component); | ||
value: function render() { | ||
if (_nukeEnv.isWeex) { | ||
var displayRefresh = this.props.refreshing ? 'show' : 'hide'; | ||
return (0, _rax.createElement)( | ||
'refresh', | ||
{ id: this.props.id, style: this.props.style, display: displayRefresh, onRefresh: this.props.onRefresh }, | ||
this.props.children | ||
); | ||
if (isWeex) { | ||
return (0, _rax.createElement)(_raxRefreshcontrol2.default, this.props); | ||
} else { | ||
return null; | ||
return (0, _rax.createElement)(_view2.default, this.props); | ||
} | ||
@@ -45,0 +55,0 @@ } |
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
value: true | ||
}); | ||
@@ -15,2 +15,4 @@ | ||
var _nukeEnv2 = _interopRequireDefault(_nukeEnv); | ||
var _view = require('./view'); | ||
@@ -20,4 +22,14 @@ | ||
var _refreshControl = require('./refresh-control'); | ||
var _refreshControl2 = _interopRequireDefault(_refreshControl); | ||
var _wptr = require('./wptr.js'); | ||
var _wptr2 = _interopRequireDefault(_wptr); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
@@ -27,4 +39,7 @@ | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /* @jsx createElement */ | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } | ||
var isWeex = _nukeEnv2.default.isWeex, | ||
isWeb = _nukeEnv2.default.isWeb; | ||
var DEFAULT_END_REACHED_THRESHOLD = 500; | ||
@@ -35,190 +50,283 @@ var DEFAULT_SCROLL_CALLBACK_THROTTLE = 50; | ||
var ScrollView = function (_Component) { | ||
_inherits(ScrollView, _Component); | ||
_inherits(ScrollView, _Component); | ||
function ScrollView() { | ||
var _ref; | ||
function ScrollView(props) { | ||
_classCallCheck(this, ScrollView); | ||
var _temp, _this, _ret; | ||
var _this = _possibleConstructorReturn(this, (ScrollView.__proto__ || Object.getPrototypeOf(ScrollView)).call(this, props)); | ||
_classCallCheck(this, ScrollView); | ||
_this.lastScrollDistance = 0; | ||
_this.lastScrollContentSize = 0; | ||
_this.loadmoreretry = 1; | ||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
_this.handleScroll = function (e) { | ||
if (isWeb) { | ||
if (_this.props.onScroll) { | ||
e.nativeEvent = { | ||
get contentOffset() { | ||
return { | ||
x: e.target.scrollLeft, | ||
y: e.target.scrollTop | ||
}; | ||
} | ||
}; | ||
_this.props.onScroll(e); | ||
} | ||
return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = ScrollView.__proto__ || Object.getPrototypeOf(ScrollView)).call.apply(_ref, [this].concat(args))), _this), _this.lastScrollDistance = 0, _this.lastScrollContentSize = 0, _this.handleScroll = function (e) { | ||
if (_this.props.onEndReached) { | ||
if (!_this.scrollerNode) { | ||
_this.scrollerNode = (0, _rax.findDOMNode)(_this.refs.scroller); | ||
_this.scrollerContentNode = (0, _rax.findDOMNode)(_this.refs.contentContainer); | ||
if (_nukeEnv.isWeb) { | ||
_this.scrollerNodeSize = _this.props.horizontal ? _this.scrollerNode.offsetWidth : _this.scrollerNode.offsetHeight; | ||
} | ||
if (_this.props.onScroll) { | ||
_this.props.onScroll(e); | ||
} | ||
// NOTE:in iOS7/8 offsetHeight/Width is is inaccurate ( use scrollHeight/Width ) | ||
var scrollContentSize = _this.props.horizontal ? _this.scrollerNode.scrollWidth : _this.scrollerNode.scrollHeight; | ||
var scrollDistance = _this.props.horizontal ? _this.scrollerNode.scrollLeft : _this.scrollerNode.scrollTop; | ||
var isEndReached = scrollContentSize - scrollDistance - _this.scrollerNodeSize < _this.props.onEndReachedThreshold; | ||
if (_this.props.onEndReached) { | ||
if (!_this.scrollerNode) { | ||
_this.scrollerNode = (0, _rax.findDOMNode)(_this.refs.scroller); | ||
_this.scrollerContentNode = (0, _rax.findDOMNode)(_this.refs.contentContainer); | ||
_this.scrollerNodeSize = _this.props.horizontal ? _this.scrollerNode.offsetWidth : _this.scrollerNode.offsetHeight; | ||
} | ||
var isScrollToEnd = scrollDistance > _this.lastScrollDistance; | ||
var isLoadedMoreContent = scrollContentSize != _this.lastScrollContentSize; | ||
// NOTE:iOS7/8下使用offsetHeight/Width获取高/宽度值是屏幕高度,不符合期望,改成 scrollHeight/Width | ||
var scrollContentSize = _this.props.horizontal ? _this.scrollerNode.scrollWidth : _this.scrollerNode.scrollHeight; | ||
var scrollDistance = _this.props.horizontal ? _this.scrollerNode.scrollLeft : _this.scrollerNode.scrollTop; | ||
if (isEndReached && isScrollToEnd && isLoadedMoreContent) { | ||
_this.lastScrollContentSize = scrollContentSize; | ||
_this.props.onEndReached(e); | ||
} | ||
var isEndReached = scrollContentSize - scrollDistance - _this.scrollerNodeSize < _this.props.onEndReachedThreshold; | ||
var isScrollToEnd = scrollDistance > _this.lastScrollDistance; | ||
var isLoadedMoreContent = scrollContentSize != _this.lastScrollContentSize; | ||
_this.lastScrollDistance = scrollDistance; | ||
} | ||
} | ||
if (isWeex) { | ||
e.nativeEvent = { | ||
contentOffset: { | ||
// HACK: weex scroll event value is opposite of web | ||
x: -e.contentOffset.x, | ||
y: -e.contentOffset.y | ||
} | ||
}; | ||
_this.props.onScroll(e); | ||
} | ||
}; | ||
if (isEndReached && isScrollToEnd && isLoadedMoreContent) { | ||
_this.lastScrollContentSize = scrollContentSize; | ||
_this.props.onEndReached(e); | ||
} | ||
_this.resetScroll = function () { | ||
if (isWeb) { | ||
_this.lastScrollContentSize = 0; | ||
_this.lastScrollDistance = 0; | ||
} else { | ||
_this.setState({ | ||
loadmoreretry: _this.loadmoreretry++ | ||
}); | ||
} | ||
}; | ||
_this.lastScrollDistance = scrollDistance; | ||
} | ||
} else { | ||
if (_this.props.onScroll) { | ||
_this.props.onScroll(e); | ||
} | ||
} | ||
}, _this.resetScroll = function (options) { | ||
if (_nukeEnv.isWeb) { | ||
_this.lastScrollContentSize = 0; | ||
_this.lastScrollDistance = 0; | ||
} | ||
}, _this.scrollTo = function (options) { | ||
_this.scrollTo = function (options) { | ||
var x = parseInt(options.x); | ||
var y = parseInt(options.y); | ||
var x = parseInt(options.x); | ||
var y = parseInt(options.y); | ||
if (isWeex) { | ||
var dom = require('@weex-module/dom'); | ||
var contentContainer = (0, _rax.findDOMNode)(_this.refs.contentContainer); | ||
dom.scrollToElement(contentContainer.ref, { | ||
offset: x || y || 0 | ||
}); | ||
} else { | ||
var pixelRatio = document.documentElement.clientWidth / FULL_WIDTH; | ||
if (_nukeEnv.isWeex) { | ||
var dom = require('@weex-module/dom'); | ||
var contentContainer = (0, _rax.findDOMNode)(_this.refs.contentContainer); | ||
dom.scrollToElement(contentContainer.ref, { | ||
offset: x || y || 0 | ||
}); | ||
} else { | ||
var pixelRatio = document.documentElement.clientWidth / FULL_WIDTH; | ||
if (x >= 0) { | ||
(0, _rax.findDOMNode)(_this.refs.scroller).scrollLeft = pixelRatio * x; | ||
} | ||
if (x >= 0) { | ||
(0, _rax.findDOMNode)(_this.refs.scroller).scrollLeft = pixelRatio * x; | ||
} | ||
if (y >= 0) { | ||
(0, _rax.findDOMNode)(_this.refs.scroller).scrollTop = pixelRatio * y; | ||
} | ||
} | ||
}; | ||
if (y >= 0) { | ||
(0, _rax.findDOMNode)(_this.refs.scroller).scrollTop = pixelRatio * y; | ||
_this.handleRefresh = function () { | ||
return new Promise(function (resolve, reject) { | ||
_this.onRefresh && _this.onRefresh(resolve, reject); | ||
}); | ||
}; | ||
_this.resetLoadmore = function () { | ||
if (isWeex) { | ||
_this.refs.scroller.resetLoadmore(); | ||
} | ||
}; | ||
_this.state = { | ||
loadmoreretry: 0, | ||
initializedWptr: false | ||
}; | ||
_this.onRefresh = null; | ||
return _this; | ||
} | ||
_createClass(ScrollView, [{ | ||
key: 'initWeb', | ||
value: function initWeb() { | ||
if (isWeb && !this.state.initializedWptr) { | ||
(0, _wptr2.default)().init({ | ||
bodyEl: this.refs.scroller, | ||
ptrEl: this.refs.refreshX, | ||
contentEl: this.refs.contentContainer, | ||
distanceToRefresh: this.props.distanceToRefresh || undefined, | ||
loadingFunction: this.handleRefresh, | ||
resistance: this.props.resistance || undefined, | ||
hammerOptions: this.props.hammerOptions || undefined | ||
}); | ||
this.setState({ | ||
initializedWptr: true | ||
}); | ||
} | ||
} | ||
} | ||
}, _this.resetLoadmore = function () { | ||
if (_nukeEnv.isWeex) { | ||
_this.refs.scroller.resetLoadmore(); | ||
} | ||
}, _temp), _possibleConstructorReturn(_this, _ret); | ||
} | ||
}, { | ||
key: 'componentDidMount', | ||
value: function componentDidMount() { | ||
if (!this.props.disabledPtr) { | ||
this.initWeb(); | ||
} | ||
} | ||
}, { | ||
key: 'componentDidUpdate', | ||
value: function componentDidUpdate() { | ||
if (!this.props.disabledPtr) { | ||
this.initWeb(); | ||
} | ||
} | ||
}, { | ||
key: 'render', | ||
value: function render() { | ||
var _this2 = this; | ||
// Reset scroll state, only for web now. | ||
var _props = this.props, | ||
id = _props.id, | ||
style = _props.style, | ||
scrollEventThrottle = _props.scrollEventThrottle, | ||
showsHorizontalScrollIndicator = _props.showsHorizontalScrollIndicator, | ||
showsVerticalScrollIndicator = _props.showsVerticalScrollIndicator, | ||
onEndReached = _props.onEndReached, | ||
onEndReachedThreshold = _props.onEndReachedThreshold, | ||
onScroll = _props.onScroll, | ||
children = _props.children; | ||
// In weex must be int value | ||
_createClass(ScrollView, [{ | ||
key: 'render', | ||
value: function render() { | ||
var _this2 = this; | ||
onEndReachedThreshold = parseInt(onEndReachedThreshold, 10); | ||
var _props = this.props, | ||
id = _props.id, | ||
style = _props.style, | ||
scrollEventThrottle = _props.scrollEventThrottle, | ||
showsHorizontalScrollIndicator = _props.showsHorizontalScrollIndicator, | ||
showsVerticalScrollIndicator = _props.showsVerticalScrollIndicator, | ||
onEndReached = _props.onEndReached, | ||
onEndReachedThreshold = _props.onEndReachedThreshold; | ||
var contentContainerStyle = [this.props.horizontal && styles.contentContainerHorizontal, this.props.contentContainerStyle]; | ||
// In weex must be int value | ||
// bugfix: fix scrollview flex in ios 78 | ||
if (!isWeex && !this.props.horizontal) { | ||
contentContainerStyle.push(styles.containerWebStyle); | ||
} | ||
onEndReachedThreshold = parseInt(onEndReachedThreshold, 10); | ||
if (this.props.style) { | ||
var childLayoutProps = ['alignItems', 'justifyContent'].filter(function (prop) { | ||
return _this2.props.style[prop] !== undefined; | ||
}); | ||
var contentContainerStyle = [this.props.horizontal && styles.contentContainerHorizontal, this.props.contentContainerStyle]; | ||
if (childLayoutProps.length !== 0) {} | ||
} | ||
// bugfix: fix scrollview flex in ios 78 | ||
if (!_nukeEnv.isWeex && !this.props.horizontal) { | ||
contentContainerStyle.push(styles.containerWebStyle); | ||
} | ||
var refreshContainer = _rax.createElement(_view2.default, null), | ||
contentChild = void 0; | ||
if (Array.isArray(children)) { | ||
contentChild = children.map(function (child, index) { | ||
if (child && child.type == _refreshControl2.default) { | ||
refreshContainer = child; | ||
_this2.onRefresh = child.props.onRefresh; | ||
} else { | ||
return child; | ||
} | ||
}); | ||
} else { | ||
contentChild = children; | ||
} | ||
if (this.props.style) { | ||
var childLayoutProps = ['alignItems', 'justifyContent'].filter(function (prop) { | ||
return _this2.props.style[prop] !== undefined; | ||
}); | ||
var contentContainer = _rax.createElement( | ||
_view2.default, | ||
{ | ||
ref: 'contentContainer', | ||
style: contentContainerStyle }, | ||
contentChild | ||
); | ||
if (childLayoutProps.length !== 0) {} | ||
} | ||
var baseStyle = this.props.horizontal ? styles.baseHorizontal : styles.baseVertical; | ||
var contentContainer = (0, _rax.createElement)( | ||
_view2.default, | ||
{ | ||
ref: 'contentContainer', | ||
style: contentContainerStyle }, | ||
this.props.children | ||
); | ||
var scrollerStyle = _extends({}, baseStyle, this.props.style); | ||
var baseStyle = this.props.horizontal ? styles.baseHorizontal : styles.baseVertical; | ||
var showsScrollIndicator = this.props.horizontal ? showsHorizontalScrollIndicator : showsVerticalScrollIndicator; | ||
var scrollerStyle = _extends({}, baseStyle, this.props.style); | ||
if (isWeex) { | ||
return _rax.createElement( | ||
'scroller', | ||
_extends({}, this.props, { | ||
ref: 'scroller', | ||
style: scrollerStyle, | ||
showScrollbar: showsScrollIndicator, | ||
onLoadmore: onEndReached, | ||
onScroll: onScroll ? this.handleScroll : null, | ||
loadmoreoffset: onEndReachedThreshold, | ||
loadmoreretry: this.state.loadmoreretry, | ||
scrollDirection: this.props.horizontal ? 'horizontal' : 'vertical' | ||
}), | ||
refreshContainer, | ||
contentContainer | ||
); | ||
} else { | ||
var _Object$assign; | ||
var showsScrollIndicator = this.props.horizontal ? showsHorizontalScrollIndicator : showsVerticalScrollIndicator; | ||
var handleScroll = this.handleScroll; | ||
if (scrollEventThrottle) { | ||
handleScroll = throttle(handleScroll, scrollEventThrottle); | ||
} | ||
if (_nukeEnv.isWeex) { | ||
if (!showsScrollIndicator && !document.getElementById('rax-scrollview-style')) { | ||
var styleNode = document.createElement('style'); | ||
styleNode.id = 'rax-scrollview-style'; | ||
document.head.appendChild(styleNode); | ||
styleNode.innerHTML = '.' + this.props.className + '::-webkit-scrollbar{display: none;}'; | ||
} | ||
return (0, _rax.createElement)( | ||
'scroller', | ||
{ | ||
id: id, | ||
ref: 'scroller', | ||
style: scrollerStyle, | ||
showScrollbar: showsScrollIndicator, | ||
onLoadmore: onEndReached, | ||
loadmoreretry: true, | ||
loadmoreoffset: onEndReachedThreshold, | ||
scrollDirection: this.props.horizontal ? 'horizontal' : 'vertical' | ||
}, | ||
contentContainer | ||
); | ||
} else { | ||
scrollerStyle.webkitOverflowScrolling = 'touch'; | ||
scrollerStyle.overflow = 'scroll'; | ||
var handleScroll = this.handleScroll; | ||
if (scrollEventThrottle) { | ||
handleScroll = throttle(handleScroll, scrollEventThrottle); | ||
var webProps = _extends({}, this.props, { | ||
ref: 'scroller', | ||
style: scrollerStyle, | ||
onScroll: handleScroll | ||
}); | ||
delete webProps.onEndReachedThreshold; | ||
if (this.props.disabledPtr) { | ||
return _rax.createElement( | ||
_view2.default, | ||
webProps, | ||
contentContainer | ||
); | ||
} | ||
return _rax.createElement( | ||
_view2.default, | ||
webProps, | ||
(0, _rax.cloneElement)(refreshContainer, { | ||
ref: "refreshX", | ||
className: "ptr-element", | ||
style: Object.assign((_Object$assign = { position: 'absolute', top: 0, left: 0, width: 750, color: '#aaaaaa' }, _defineProperty(_Object$assign, 'z-index', 10), _defineProperty(_Object$assign, 'textAlign', 'center'), _Object$assign), refreshContainer.props.style) | ||
}), | ||
contentContainer | ||
); | ||
} | ||
} | ||
}]); | ||
if (!showsScrollIndicator && !document.getElementById('rx-scrollview-style')) { | ||
var styleNode = document.createElement('style'); | ||
styleNode.id = 'rx-scrollview-style'; | ||
document.head.appendChild(styleNode); | ||
styleNode.innerHTML = '.' + this.props.className + '::-webkit-scrollbar{display: none;}'; | ||
} | ||
scrollerStyle.webkitOverflowScrolling = 'touch'; | ||
scrollerStyle.overflow = 'scroll'; | ||
var webProps = _extends({}, this.props, { | ||
ref: 'scroller', | ||
style: scrollerStyle, | ||
onScroll: handleScroll | ||
}); | ||
delete webProps.onEndReachedThreshold; | ||
return (0, _rax.createElement)( | ||
_view2.default, | ||
webProps, | ||
contentContainer | ||
); | ||
} | ||
} | ||
}]); | ||
return ScrollView; | ||
return ScrollView; | ||
}(_rax.Component); | ||
ScrollView.defaultProps = { | ||
scrollEventThrottle: DEFAULT_SCROLL_CALLBACK_THROTTLE, | ||
onEndReachedThreshold: DEFAULT_END_REACHED_THRESHOLD, | ||
showsHorizontalScrollIndicator: true, | ||
showsVerticalScrollIndicator: true, | ||
className: 'rx-scrollview' | ||
scrollEventThrottle: DEFAULT_SCROLL_CALLBACK_THROTTLE, | ||
onEndReachedThreshold: DEFAULT_END_REACHED_THRESHOLD, | ||
showsHorizontalScrollIndicator: true, | ||
showsVerticalScrollIndicator: true, | ||
className: 'rax-scrollview' | ||
}; | ||
@@ -228,36 +336,37 @@ | ||
function throttle(func, wait) { | ||
var ctx, args, rtn, timeoutID; | ||
var last = 0; | ||
var ctx, args, rtn, timeoutID; | ||
var last = 0; | ||
function call() { | ||
timeoutID = 0; | ||
last = +new Date(); | ||
rtn = func.apply(ctx, args); | ||
ctx = null; | ||
args = null; | ||
} | ||
function call() { | ||
timeoutID = 0; | ||
last = +new Date(); | ||
rtn = func.apply(ctx, args); | ||
ctx = null; | ||
args = null; | ||
} | ||
return function throttled() { | ||
ctx = this; | ||
args = arguments; | ||
var delta = new Date() - last; | ||
if (!timeoutID) if (delta >= wait) call();else timeoutID = setTimeout(call, wait - delta); | ||
return rtn; | ||
}; | ||
return function throttled() { | ||
ctx = this; | ||
args = arguments; | ||
var delta = new Date() - last; | ||
if (!timeoutID) if (delta >= wait) call();else timeoutID = setTimeout(call, wait - delta); | ||
return rtn; | ||
}; | ||
} | ||
var styles = { | ||
baseVertical: { | ||
flex: 1, | ||
flexDirection: 'column' | ||
}, | ||
baseHorizontal: { | ||
flexDirection: 'row' | ||
}, | ||
contentContainerHorizontal: { | ||
flexDirection: 'row' | ||
}, | ||
containerWebStyle: { | ||
display: 'block' | ||
} | ||
baseVertical: { | ||
flex: 1, | ||
flexDirection: 'column' | ||
}, | ||
baseHorizontal: { | ||
flex: 1, | ||
flexDirection: 'row' | ||
}, | ||
contentContainerHorizontal: { | ||
flexDirection: 'row' | ||
}, | ||
containerWebStyle: { | ||
display: 'block' | ||
} | ||
}; | ||
@@ -264,0 +373,0 @@ |
{ | ||
"name": "nuke-components", | ||
"version": "0.2.26", | ||
"version": "0.2.27", | ||
"description": "nuke原件库", | ||
@@ -16,3 +16,3 @@ "main": "lib/index", | ||
"nuke", | ||
"rx", | ||
"rax", | ||
"components" | ||
@@ -36,4 +36,6 @@ ], | ||
"dependencies": { | ||
"hammerjs": "^2.0.8", | ||
"nuke-dimensions": "0.x.x", | ||
"nuke-env": "0.x.x", | ||
"rax-refreshcontrol": "^0.3.2", | ||
"rax-scrollview": "^0.2.13" | ||
@@ -40,0 +42,0 @@ }, |
/* @jsx createElement */ | ||
import {Component, createElement, PropTypes} from 'rax'; | ||
import {isWeex} from 'nuke-env'; | ||
import Env from 'nuke-env'; | ||
import RaxRefreshControl from 'rax-refreshcontrol'; | ||
import View from './view'; | ||
const {isWeex, isWeb} = Env; | ||
class RefreshControl extends Component { | ||
render() { | ||
if (isWeex) { | ||
let displayRefresh = this.props.refreshing ? 'show' : 'hide'; | ||
return ( | ||
<refresh id={this.props.id} style={this.props.style} display={displayRefresh} onRefresh={this.props.onRefresh}> | ||
{this.props.children} | ||
</refresh> | ||
); | ||
return (<RaxRefreshControl {...this.props} />); | ||
} else { | ||
return null; | ||
return <View {...this.props} />; | ||
} | ||
@@ -17,0 +16,0 @@ } |
@@ -1,6 +0,8 @@ | ||
/* @jsx createElement */ | ||
import {Component, createElement, findDOMNode,cloneElement} from 'rax'; | ||
import Env from 'nuke-env'; | ||
import View from './view'; | ||
import RefreshControl from './refresh-control'; | ||
import WebPullToRefresh from './wptr.js'; | ||
import {Component, createElement, findDOMNode} from 'rax'; | ||
import {isWeex,isWeb} from 'nuke-env'; | ||
import View from './view'; | ||
const {isWeex, isWeb} = Env; | ||
const DEFAULT_END_REACHED_THRESHOLD = 500; | ||
@@ -10,231 +12,321 @@ const DEFAULT_SCROLL_CALLBACK_THROTTLE = 50; | ||
class ScrollView extends Component { | ||
static defaultProps = { | ||
scrollEventThrottle: DEFAULT_SCROLL_CALLBACK_THROTTLE, | ||
onEndReachedThreshold: DEFAULT_END_REACHED_THRESHOLD, | ||
showsHorizontalScrollIndicator: true, | ||
showsVerticalScrollIndicator: true, | ||
className: 'rx-scrollview', | ||
}; | ||
lastScrollDistance = 0; | ||
lastScrollContentSize = 0; | ||
static defaultProps = { | ||
scrollEventThrottle: DEFAULT_SCROLL_CALLBACK_THROTTLE, | ||
onEndReachedThreshold: DEFAULT_END_REACHED_THRESHOLD, | ||
showsHorizontalScrollIndicator: true, | ||
showsVerticalScrollIndicator: true, | ||
className: 'rax-scrollview', | ||
}; | ||
handleScroll = (e) => { | ||
lastScrollDistance = 0; | ||
lastScrollContentSize = 0; | ||
loadmoreretry = 1; | ||
if (isWeb) { | ||
constructor(props) { | ||
super(props); | ||
this.state = { | ||
loadmoreretry: 0, | ||
initializedWptr: false, | ||
}; | ||
this.onRefresh = null; | ||
} | ||
if (this.props.onScroll) { | ||
this.props.onScroll(e); | ||
} | ||
handleScroll = (e) => { | ||
if (isWeb) { | ||
if (this.props.onScroll) { | ||
e.nativeEvent = { | ||
get contentOffset() { | ||
return { | ||
x: e.target.scrollLeft, | ||
y: e.target.scrollTop | ||
}; | ||
} | ||
}; | ||
this.props.onScroll(e); | ||
} | ||
if (this.props.onEndReached) { | ||
if (!this.scrollerNode) { | ||
this.scrollerNode = findDOMNode(this.refs.scroller); | ||
this.scrollerContentNode = findDOMNode(this.refs.contentContainer); | ||
this.scrollerNodeSize = this.props.horizontal ? this.scrollerNode.offsetWidth : this.scrollerNode.offsetHeight; | ||
} | ||
if (this.props.onEndReached) { | ||
if (!this.scrollerNode) { | ||
this.scrollerNode = findDOMNode(this.refs.scroller); | ||
this.scrollerContentNode = findDOMNode(this.refs.contentContainer); | ||
// NOTE:iOS7/8下使用offsetHeight/Width获取高/宽度值是屏幕高度,不符合期望,改成 scrollHeight/Width | ||
let scrollContentSize = this.props.horizontal ? this.scrollerNode.scrollWidth : this.scrollerNode.scrollHeight; | ||
let scrollDistance = this.props.horizontal ? this.scrollerNode.scrollLeft : this.scrollerNode.scrollTop; | ||
this.scrollerNodeSize = this.props.horizontal ? this.scrollerNode.offsetWidth : this.scrollerNode.offsetHeight; | ||
} | ||
let isEndReached = (scrollContentSize - scrollDistance - this.scrollerNodeSize) < this.props.onEndReachedThreshold; | ||
let isScrollToEnd = scrollDistance > this.lastScrollDistance; | ||
let isLoadedMoreContent = scrollContentSize != this.lastScrollContentSize; | ||
// NOTE:in iOS7/8 offsetHeight/Width is is inaccurate ( use scrollHeight/Width ) | ||
let scrollContentSize = this.props.horizontal ? this.scrollerNode.scrollWidth : this.scrollerNode.scrollHeight; | ||
let scrollDistance = this.props.horizontal ? this.scrollerNode.scrollLeft : this.scrollerNode.scrollTop; | ||
let isEndReached = scrollContentSize - scrollDistance - this.scrollerNodeSize < this.props.onEndReachedThreshold; | ||
if (isEndReached && isScrollToEnd && isLoadedMoreContent) { | ||
this.lastScrollContentSize = scrollContentSize; | ||
this.props.onEndReached(e); | ||
let isScrollToEnd = scrollDistance > this.lastScrollDistance; | ||
let isLoadedMoreContent = scrollContentSize != this.lastScrollContentSize; | ||
if (isEndReached && isScrollToEnd && isLoadedMoreContent) { | ||
this.lastScrollContentSize = scrollContentSize; | ||
this.props.onEndReached(e); | ||
} | ||
this.lastScrollDistance = scrollDistance; | ||
} | ||
} | ||
this.lastScrollDistance = scrollDistance; | ||
} | ||
}else{ | ||
if (this.props.onScroll) { | ||
this.props.onScroll(e); | ||
} | ||
if (isWeex) { | ||
e.nativeEvent = { | ||
contentOffset: { | ||
// HACK: weex scroll event value is opposite of web | ||
x: - e.contentOffset.x, | ||
y: - e.contentOffset.y | ||
} | ||
}; | ||
this.props.onScroll(e); | ||
} | ||
} | ||
} | ||
// Reset scroll state, only for web now. | ||
resetScroll = (options) => { | ||
if (isWeb) { | ||
this.lastScrollContentSize = 0; | ||
this.lastScrollDistance = 0; | ||
resetScroll = () => { | ||
if (isWeb) { | ||
this.lastScrollContentSize = 0; | ||
this.lastScrollDistance = 0; | ||
} else { | ||
this.setState({ | ||
loadmoreretry: this.loadmoreretry++, | ||
}); | ||
} | ||
} | ||
} | ||
scrollTo = (options) => { | ||
scrollTo = (options) => { | ||
let x = parseInt(options.x); | ||
let y = parseInt(options.y); | ||
let x = parseInt(options.x); | ||
let y = parseInt(options.y); | ||
if (isWeex) { | ||
let dom = require('@weex-module/dom'); | ||
let contentContainer = findDOMNode(this.refs.contentContainer); | ||
dom.scrollToElement(contentContainer.ref, { | ||
offset: x || y || 0 | ||
}); | ||
} else { | ||
let pixelRatio = document.documentElement.clientWidth / FULL_WIDTH; | ||
if (isWeex) { | ||
let dom = require('@weex-module/dom'); | ||
let contentContainer = findDOMNode(this.refs.contentContainer); | ||
dom.scrollToElement(contentContainer.ref, { | ||
offset: x || y || 0 | ||
}); | ||
} else { | ||
let pixelRatio = document.documentElement.clientWidth / FULL_WIDTH; | ||
if (x >= 0) { | ||
findDOMNode(this.refs.scroller).scrollLeft = pixelRatio * x; | ||
} | ||
if (x >= 0) { | ||
findDOMNode(this.refs.scroller).scrollLeft = pixelRatio * x; | ||
} | ||
if (y >= 0) { | ||
findDOMNode(this.refs.scroller).scrollTop = pixelRatio * y; | ||
} | ||
} | ||
} | ||
handleRefresh=()=> { | ||
return new Promise((resolve, reject) => { | ||
this.onRefresh && this.onRefresh(resolve, reject); | ||
}); | ||
} | ||
if (y >= 0) { | ||
findDOMNode(this.refs.scroller).scrollTop = pixelRatio * y; | ||
} | ||
initWeb() { | ||
if (isWeb && !this.state.initializedWptr) { | ||
WebPullToRefresh().init({ | ||
bodyEl: this.refs.scroller, | ||
ptrEl: this.refs.refreshX, | ||
contentEl: this.refs.contentContainer, | ||
distanceToRefresh: this.props.distanceToRefresh || undefined, | ||
loadingFunction: this.handleRefresh, | ||
resistance: this.props.resistance || undefined, | ||
hammerOptions: this.props.hammerOptions || undefined | ||
}); | ||
this.setState({ | ||
initializedWptr: true | ||
}); | ||
} | ||
} | ||
} | ||
resetLoadmore=()=>{ | ||
if(isWeex){ | ||
this.refs.scroller.resetLoadmore(); | ||
componentDidMount() { | ||
if (!this.props.disabledPtr) { | ||
this.initWeb(); | ||
} | ||
} | ||
} | ||
render() { | ||
let { | ||
id, | ||
style, | ||
scrollEventThrottle, | ||
showsHorizontalScrollIndicator, | ||
showsVerticalScrollIndicator, | ||
onEndReached, | ||
onEndReachedThreshold, | ||
} = this.props; | ||
componentDidUpdate() { | ||
if (!this.props.disabledPtr) { | ||
this.initWeb(); | ||
} | ||
} | ||
resetLoadmore=()=>{ | ||
if(isWeex){ | ||
this.refs.scroller.resetLoadmore(); | ||
// In weex must be int value | ||
onEndReachedThreshold = parseInt(onEndReachedThreshold, 10); | ||
} | ||
} | ||
const contentContainerStyle = [ | ||
this.props.horizontal && styles.contentContainerHorizontal, | ||
this.props.contentContainerStyle, | ||
]; | ||
render() { | ||
let { | ||
id, | ||
style, | ||
scrollEventThrottle, | ||
showsHorizontalScrollIndicator, | ||
showsVerticalScrollIndicator, | ||
onEndReached, | ||
onEndReachedThreshold, | ||
onScroll, | ||
children, | ||
} = this.props; | ||
// bugfix: fix scrollview flex in ios 78 | ||
if (!isWeex && !this.props.horizontal) { | ||
contentContainerStyle.push(styles.containerWebStyle); | ||
} | ||
// In weex must be int value | ||
onEndReachedThreshold = parseInt(onEndReachedThreshold, 10); | ||
if (this.props.style) { | ||
let childLayoutProps = ['alignItems', 'justifyContent'] | ||
.filter((prop) => this.props.style[prop] !== undefined); | ||
const contentContainerStyle = [ | ||
this.props.horizontal && styles.contentContainerHorizontal, | ||
this.props.contentContainerStyle, | ||
]; | ||
if (childLayoutProps.length !== 0) { | ||
console.warn( | ||
'ScrollView child layout (' + JSON.stringify(childLayoutProps) + | ||
') must be applied through the contentContainerStyle prop.' | ||
); | ||
} | ||
} | ||
// bugfix: fix scrollview flex in ios 78 | ||
if (!isWeex && !this.props.horizontal) { | ||
contentContainerStyle.push(styles.containerWebStyle); | ||
} | ||
const contentContainer = | ||
<View | ||
ref="contentContainer" | ||
style={contentContainerStyle}> | ||
{this.props.children} | ||
</View>; | ||
if (this.props.style) { | ||
let childLayoutProps = ['alignItems', 'justifyContent'] | ||
.filter((prop) => this.props.style[prop] !== undefined); | ||
const baseStyle = this.props.horizontal ? styles.baseHorizontal : styles.baseVertical; | ||
if (childLayoutProps.length !== 0) { | ||
console.warn( | ||
'ScrollView child layout (' + JSON.stringify(childLayoutProps) + | ||
') must be applied through the contentContainerStyle prop.' | ||
); | ||
} | ||
} | ||
const scrollerStyle = { | ||
...baseStyle, | ||
...this.props.style | ||
}; | ||
let refreshContainer = <View />, contentChild; | ||
if (Array.isArray(children)) { | ||
contentChild = children.map((child, index) => { | ||
if (child && child.type == RefreshControl) { | ||
refreshContainer = child; | ||
this.onRefresh = child.props.onRefresh; | ||
} else { | ||
return child; | ||
} | ||
}); | ||
} else { | ||
contentChild = children; | ||
} | ||
let showsScrollIndicator = this.props.horizontal ? showsHorizontalScrollIndicator : showsVerticalScrollIndicator; | ||
const contentContainer = | ||
<View | ||
ref="contentContainer" | ||
style={contentContainerStyle}> | ||
{contentChild} | ||
</View>; | ||
if (isWeex) { | ||
const baseStyle = this.props.horizontal ? styles.baseHorizontal : styles.baseVertical; | ||
return ( | ||
<scroller | ||
id={id} | ||
ref="scroller" | ||
style={scrollerStyle} | ||
showScrollbar={showsScrollIndicator} | ||
onLoadmore={onEndReached} | ||
loadmoreretry={true} | ||
loadmoreoffset={onEndReachedThreshold} | ||
scrollDirection={this.props.horizontal ? 'horizontal' : 'vertical'} | ||
> | ||
{contentContainer} | ||
</scroller> | ||
); | ||
const scrollerStyle = { | ||
...baseStyle, | ||
...this.props.style | ||
}; | ||
} else { | ||
let showsScrollIndicator = this.props.horizontal ? showsHorizontalScrollIndicator : showsVerticalScrollIndicator; | ||
let handleScroll = this.handleScroll; | ||
if (scrollEventThrottle) { | ||
handleScroll = throttle(handleScroll, scrollEventThrottle); | ||
} | ||
if (isWeex) { | ||
return ( | ||
<scroller | ||
{...this.props} | ||
ref="scroller" | ||
style={scrollerStyle} | ||
showScrollbar={showsScrollIndicator} | ||
onLoadmore={onEndReached} | ||
onScroll={onScroll ? this.handleScroll : null} | ||
loadmoreoffset={onEndReachedThreshold} | ||
loadmoreretry={this.state.loadmoreretry} | ||
scrollDirection={this.props.horizontal ? 'horizontal' : 'vertical'} | ||
> | ||
{refreshContainer} | ||
{contentContainer} | ||
</scroller> | ||
); | ||
} else { | ||
let handleScroll = this.handleScroll; | ||
if (scrollEventThrottle) { | ||
handleScroll = throttle(handleScroll, scrollEventThrottle); | ||
} | ||
if (!showsScrollIndicator && !document.getElementById('rx-scrollview-style')) { | ||
let styleNode = document.createElement('style'); | ||
styleNode.id = 'rx-scrollview-style'; | ||
document.head.appendChild(styleNode); | ||
styleNode.innerHTML = `.${this.props.className}::-webkit-scrollbar{display: none;}`; | ||
} | ||
if (!showsScrollIndicator && !document.getElementById('rax-scrollview-style')) { | ||
let styleNode = document.createElement('style'); | ||
styleNode.id = 'rax-scrollview-style'; | ||
document.head.appendChild(styleNode); | ||
styleNode.innerHTML = `.${this.props.className}::-webkit-scrollbar{display: none;}`; | ||
} | ||
scrollerStyle.webkitOverflowScrolling = 'touch'; | ||
scrollerStyle.overflow = 'scroll'; | ||
let webProps = { | ||
...this.props, | ||
...{ | ||
ref: 'scroller', | ||
style: scrollerStyle, | ||
onScroll: handleScroll | ||
scrollerStyle.webkitOverflowScrolling = 'touch'; | ||
scrollerStyle.overflow = 'scroll'; | ||
let webProps = { | ||
...this.props, | ||
...{ | ||
ref: 'scroller', | ||
style: scrollerStyle, | ||
onScroll: handleScroll | ||
} | ||
}; | ||
delete webProps.onEndReachedThreshold; | ||
if (this.props.disabledPtr) { | ||
return ( | ||
<View {...webProps}> | ||
{contentContainer} | ||
</View> | ||
); | ||
} | ||
} | ||
delete webProps.onEndReachedThreshold; | ||
return ( | ||
<View {...webProps}> | ||
{contentContainer} | ||
</View> | ||
); | ||
return ( | ||
<View {...webProps}> | ||
{cloneElement(refreshContainer,{ | ||
ref:"refreshX", | ||
className:"ptr-element", | ||
style:Object.assign({position: 'absolute',top: 0,left: 0,width: 750,color: '#aaaaaa',[`z-index`]: 10,textAlign: 'center'},refreshContainer.props.style) | ||
})} | ||
{contentContainer} | ||
</View> | ||
); | ||
} | ||
} | ||
} | ||
} | ||
function throttle(func, wait) { | ||
var ctx, args, rtn, timeoutID; | ||
var last = 0; | ||
var ctx, args, rtn, timeoutID; | ||
var last = 0; | ||
function call() { | ||
timeoutID = 0; | ||
last = +new Date(); | ||
rtn = func.apply(ctx, args); | ||
ctx = null; | ||
args = null; | ||
} | ||
function call() { | ||
timeoutID = 0; | ||
last = +new Date(); | ||
rtn = func.apply(ctx, args); | ||
ctx = null; | ||
args = null; | ||
} | ||
return function throttled() { | ||
ctx = this; | ||
args = arguments; | ||
var delta = new Date() - last; | ||
if (!timeoutID) | ||
if (delta >= wait) call(); | ||
else timeoutID = setTimeout(call, wait - delta); | ||
return rtn; | ||
}; | ||
return function throttled() { | ||
ctx = this; | ||
args = arguments; | ||
var delta = new Date() - last; | ||
if (!timeoutID) | ||
if (delta >= wait) call(); | ||
else timeoutID = setTimeout(call, wait - delta); | ||
return rtn; | ||
}; | ||
} | ||
const styles = { | ||
baseVertical: { | ||
flex: 1, | ||
flexDirection: 'column', | ||
}, | ||
baseHorizontal: { | ||
flexDirection: 'row', | ||
}, | ||
contentContainerHorizontal: { | ||
flexDirection: 'row', | ||
}, | ||
containerWebStyle: { | ||
display: 'block', | ||
} | ||
baseVertical: { | ||
flex: 1, | ||
flexDirection: 'column', | ||
}, | ||
baseHorizontal: { | ||
flex: 1, | ||
flexDirection: 'row', | ||
}, | ||
contentContainerHorizontal: { | ||
flexDirection: 'row', | ||
}, | ||
containerWebStyle: { | ||
display: 'block', | ||
} | ||
}; | ||
export default ScrollView; | ||
export default ScrollView; |
@@ -103,3 +103,3 @@ /* @jsx createElement */ | ||
getStyles() { | ||
let sizeObj= this.props.size === 'small' ? Size.small : Size.medium; | ||
let sizeObj = this.props.size === 'small' ? Size.small : Size.medium; | ||
return { | ||
@@ -106,0 +106,0 @@ span: { |
157518
43
3024
5
+ Addedhammerjs@^2.0.8
+ Addedrax-refreshcontrol@^0.3.2
+ Addedhammerjs@2.0.8(transitive)
+ Addedrax-refreshcontrol@0.3.8(transitive)
+ Addeduniversal-env@0.3.8(transitive)