forked from enviPath/enviPy
Current Dev State
This commit is contained in:
101
static/js/ketcher2/script/ui/component/visibleview.jsx
Normal file
101
static/js/ketcher2/script/ui/component/visibleview.jsx
Normal file
@ -0,0 +1,101 @@
|
||||
/****************************************************************************
|
||||
* Copyright 2017 EPAM Systems
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
***************************************************************************/
|
||||
|
||||
import { h, Component } from 'preact';
|
||||
/** @jsx h */
|
||||
|
||||
const STYLE_INNER = 'position:relative; overflow:hidden; width:100%; min-height:100%;';
|
||||
|
||||
const STYLE_CONTENT = 'position:absolute; top:0; left:0; height:100%; width:100%; overflow:visible;';
|
||||
|
||||
export default class VirtualList extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
offset: 0,
|
||||
height: 0
|
||||
};
|
||||
}
|
||||
|
||||
resize = (ev, reset) => {
|
||||
const height = this.base.offsetHeight;
|
||||
|
||||
if (this.state.height !== height) {
|
||||
this.setState({ height });
|
||||
}
|
||||
|
||||
if (reset) {
|
||||
this.setState({offset: 0});
|
||||
this.base.scrollTop = 0;
|
||||
}
|
||||
};
|
||||
|
||||
handleScroll = () => {
|
||||
this.setState({ offset: this.base.scrollTop });
|
||||
if (this.props.sync) this.forceUpdate();
|
||||
};
|
||||
|
||||
componentDidUpdate({data}) {
|
||||
const equal = (data.length === this.props.data.length &&
|
||||
this.props.data.every((v, i)=> v === data[i]));
|
||||
|
||||
this.resize(null, !equal);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.resize();
|
||||
addEventListener('resize', this.resize);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
removeEventListener('resize', this.resize);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { data, rowHeight, children, Tag="div", overscanCount=1, sync, ...props } = this.props;
|
||||
const { offset, height } = this.state;
|
||||
|
||||
// first visible row index
|
||||
let start = (offset / rowHeight) || 0;
|
||||
const renderRow = children[0];
|
||||
|
||||
// actual number of visible rows (without overscan)
|
||||
let visibleRowCount = (height / rowHeight) || 0;
|
||||
|
||||
// Overscan: render blocks of rows modulo an overscan row count
|
||||
// This dramatically reduces DOM writes during scrolling
|
||||
if (overscanCount) {
|
||||
start = Math.max(0, start - (start % overscanCount));
|
||||
visibleRowCount += overscanCount;
|
||||
}
|
||||
|
||||
// last visible + overscan row index
|
||||
const end = start + 1 + visibleRowCount;
|
||||
|
||||
// data slice currently in viewport plus overscan items
|
||||
let selection = data.slice(start, end);
|
||||
|
||||
return (
|
||||
<div onScroll={this.handleScroll} {...props}>
|
||||
<div style={`${STYLE_INNER} height:${data.length*rowHeight}px;`}>
|
||||
<Tag style={`${STYLE_CONTENT} top:${start*rowHeight}px;`}>
|
||||
{ selection.map((d, i) => renderRow(d, start + i)) }
|
||||
</Tag>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user