@yncoder/element-react
Version:
Element UI for React
162 lines (137 loc) • 4.24 kB
JSX
import React from 'react';
import ReactDOM from 'react-dom'
import { PropTypes, PureComponent } from '../../libs';
import { addResizeListener, removeResizeListener } from '../../libs/utils/resize-event';
import { getScrollBarWidth } from './scrollbar-width';
import { Bar } from './Bar'
export class Scrollbar extends PureComponent {
constructor(props) {
super(props);
this.state = {
sizeWidth: '0',
sizeHeight: '0',
moveX: 0,
moveY: 0
};
this.update = this._update.bind(this)
}
get wrap(){
return this.refs.wrap;
}
componentDidMount(){
if (this.native) return;
let rafId = requestAnimationFrame(this.update)
this.cleanRAF = ()=>{
cancelAnimationFrame(rafId)
}
}
componentDidUpdate() {
this.resizeDom = ReactDOM.findDOMNode(this.refs.resize)
if (!this.props.noresize){
this.cleanResize && this.cleanResize();
addResizeListener(this.resizeDom, this.update)
this.cleanResize = ()=>{
removeResizeListener(this.resizeDom, this.update);
}
}
}
componentWillUnmount(){
this.cleanRAF();
this.cleanResize && this.cleanResize();
}
handleScroll() {
const wrap = this.wrap;
this.setState({
moveY: ((wrap.scrollTop * 100) / wrap.clientHeight),
moveX: ((wrap.scrollLeft * 100) / wrap.clientWidth)
})
}
_update() {
let heightPercentage, widthPercentage;
const { wrap, state } = this;
if (!wrap) return;
heightPercentage = (wrap.clientHeight * 100 / wrap.scrollHeight);
widthPercentage = (wrap.clientWidth * 100 / wrap.scrollWidth);
let sizeHeight = (heightPercentage < 100) ? (heightPercentage + '%') : '';
let sizeWidth = (widthPercentage < 100) ? (widthPercentage + '%') : '';
if (state.sizeHeight !== sizeHeight || state.sizeWidth !== sizeWidth) {
this.setState({sizeHeight, sizeWidth})
}
}
render() {
/* eslint-disable */
let {
native, viewStyle, wrapStyle, viewClass, children, viewComponent, wrapClass, noresize,
className, ...others} = this.props;
let {moveX, moveY, sizeWidth, sizeHeight} = this.state;
/* eslint-enable */
let style = wrapStyle;
const gutter = getScrollBarWidth();
if (gutter) {
const gutterWith = `-${gutter}px`;
if (Array.isArray(wrapStyle)){
style = Object.assign.apply(null, [...wrapStyle, {marginRight: gutterWith, marginBottom: gutterWith}])
} else {
style = Object.assign({}, wrapStyle, {marginRight: gutterWith, marginBottom: gutterWith})
}
}
const view = React.createElement(viewComponent, {
className: this.classNames('el-scrollbar__view', viewClass),
style: viewStyle,
ref: 'resize'
}, children);
let nodes;
if (!native){
const wrap = (
<div
{...others}
ref="wrap"
key={0}
style={style}
onScroll={this.handleScroll.bind(this)}
className={this.classNames(wrapClass, 'el-scrollbar__wrap', gutter ? '' : 'el-scrollbar__wrap--hidden-default')}
>
{view}
</div>
)
nodes = [
wrap,
<Bar key={1} move={moveX} size={sizeWidth} getParentWrap={()=>this.wrap}></Bar>,
<Bar key={2} move={moveY} size={sizeHeight} getParentWrap={()=>this.wrap} vertical={true}></Bar>,
]
}else {
nodes = [
(
<div
{...others}
key={0}
ref="wrap"
className={this.classNames(wrapClass, 'el-scrollbar__wrap')}
style={style}>
{view}
</div>
)
]
}
return React.createElement('div', {className: this.classNames('el-scrollbar', className)}, nodes)
}
}
Scrollbar.propTypes = {
native: PropTypes.bool,
wrapStyle: PropTypes.object,
wrapClass: PropTypes.oneOfType([
PropTypes.string, PropTypes.object
]),
viewClass: PropTypes.oneOfType([
PropTypes.string, PropTypes.object
]),
viewStyle: PropTypes.object,
className: PropTypes.string,
viewComponent: PropTypes.oneOfType([
PropTypes.string, PropTypes.element
]),
noresize: PropTypes.bool
}
Scrollbar.defaultProps = {
viewComponent: 'div'
}