Closes #19 only render visable gridrows

Doing this greatly improves render speed and bypasses the webbrowsers grid limitation of 1000 files
With a padding for a better user experience
This commit is contained in:
controlol 2021-11-01 23:46:03 +01:00
parent dbd7956491
commit a1b92f5587
2 changed files with 72 additions and 24 deletions

View File

@ -40,6 +40,15 @@ import { Button } from '../styled.js'
import assert from 'assert'
import LineLoader from './LineLoader.jsx'
const ROW_HEIGHT = 20
const ROW_GAP = 8
const DATA_PADDING = 3
const DEBOUNCE_THRESHOLD = 100
const OptimisticRow = styled.div`
grid-column: 1 / span 3;
`
const FBContainer = styled.div`
display: flex;
flex-direction: column;
@ -213,13 +222,16 @@ class FileBrowser extends Component {
renderMenu: false,
cursorX: 0,
cursorY: 0,
clicked: ""
clicked: "",
from: 0,
to: 100
}
this.backListener = undefined
this.handleInputChange = this.handleInputChange.bind(this)
this.searchTimeout = undefined
this.scrollTimeout = undefined
}
componentDidMount = () => {
@ -241,7 +253,7 @@ class FileBrowser extends Component {
this.props.currentPath.split("/").length < this.state.prevPath.split("/").length ||
this.props.currentPath === "/"
) direction = -1
this.setState({ prevPath: this.props.currentPath, transitionFiles: 1 * direction })
this.setState({ prevPath: this.props.currentPath, transitionFiles: 1 * direction, from: 0, to: 100 })
delay(5).then(() => this.setState({ transitionFiles: 2 * direction }))
delay(300).then(() => this.setState({ transitionFiles: 3 * direction, files: this.props.files }))
@ -399,17 +411,9 @@ class FileBrowser extends Component {
if (isNew) files = this.props.files
else files = this.state.files
files = this.getOrderedItems(files).slice(this.state.from, this.state.to)
return files
// apply search filter
.filter(v => v.Name?.toLowerCase().indexOf(this.state.filter) !== -1)
// sort by name
.sort((a,b) => this.state.orderBy === "name" ? this.state.orderAscending ? a.Name.localeCompare(b.Name, 'nl', { sensitivity: 'base' }) : b.Name.localeCompare(a.Name, 'nl', { sensitivity: 'base' }) : 0)
// // sort by modified date
// .sort((a,b) => this.state.orderBy === "modified" ? this.state.orderAscending ? a.modified - b.modified : b.modified - a.modified : 0)
// sort by size
.sort((a,b) => this.state.orderBy === "size" ? this.state.orderAscending ? a.Size - b.Size : b.Size - a.Size : 0)
// // sort folders to top
.sort((a,b) => (b.IsDir ? 1 : 0) - (a.IsDir ? 1 : 0))
.map(v => (
<Fragment key={v.Name + "file"}>
{ this.renderImage(v.MimeType, v.Name) }
@ -425,6 +429,34 @@ class FileBrowser extends Component {
))
}
getOrderedItems = files => {
return files
// apply search filter
.filter(v => v.Name?.toLowerCase().indexOf(this.state.filter) !== -1)
// sort by name
.sort((a,b) => this.state.orderBy === "name" ? this.state.orderAscending ? a.Name.localeCompare(b.Name, 'nl', { sensitivity: 'base' }) : b.Name.localeCompare(a.Name, 'nl', { sensitivity: 'base' }) : 0)
// // sort by modified date
// .sort((a,b) => this.state.orderBy === "modified" ? this.state.orderAscending ? a.modified - b.modified : b.modified - a.modified : 0)
// sort by size
.sort((a,b) => this.state.orderBy === "size" ? this.state.orderAscending ? a.Size - b.Size : b.Size - a.Size : 0)
// // sort folders to top
.sort((a,b) => (b.IsDir ? 1 : 0) - (a.IsDir ? 1 : 0))
}
handleDebounce = (scrollTop, clientHeight) => {
const maxVisibleRows = Math.ceil(clientHeight / (ROW_HEIGHT + ROW_GAP))
const from = Math.max(0, Math.floor(scrollTop / (ROW_HEIGHT + ROW_GAP)) - maxVisibleRows * DATA_PADDING)
const to = Math.min(this.state.files.length, from + maxVisibleRows * (DATA_PADDING * 2 + 1))
console.log({from, to})
this.setState({from, to})
}
handleGridScroll = e => {
clearTimeout(this.scrollTimeout)
const { scrollTop, clientHeight } = e.target
this.scrollTimeout = setTimeout(this.handleDebounce, DEBOUNCE_THRESHOLD, scrollTop, clientHeight)
}
// render the path the user is currently at
renderPath = () =>
path.join(...this.props.currentPath.split("/"))
@ -434,7 +466,8 @@ class FileBrowser extends Component {
)
render() {
const { transitionFiles } = this.state
const { transitionFiles, from, to, orderBy, orderAscending, files } = this.state
const rows = Math.max(this.props.files.length, files.length)
return (
<FBContainer>
@ -465,11 +498,11 @@ class FileBrowser extends Component {
<FilenameP onClick={() => this.updateOrder("name")} style={{ position: "relative", cursor: "pointer" }}>
filename
{
this.state.orderBy === "name" &&
<img src={CaretDown} alt={this.state.orderAscending ? "ascending" : "descending"}
orderBy === "name" &&
<img src={CaretDown} alt={orderAscending ? "ascending" : "descending"}
height="20" width="20"
style={{
transform: this.state.orderAscending ? "rotateZ(180deg)" : undefined,
transform: orderAscending ? "rotateZ(180deg)" : undefined,
position: "absolute",
top: "2px",
marginLeft: ".5rem"
@ -480,11 +513,11 @@ class FileBrowser extends Component {
{/* <ModifiedP onClick={() => this.updateOrder("modified")} style={{ position: "relative", cursor: "pointer" }}>
modified
{
this.state.orderBy === "modified" &&
<img src={CaretDown} alt={this.state.orderAscending ? "ascending" : "descending"}
orderBy === "modified" &&
<img src={CaretDown} alt={orderAscending ? "ascending" : "descending"}
height="20" width="20"
style={{
transform: this.state.orderAscending ? "rotateZ(180deg)" : undefined,
transform: orderAscending ? "rotateZ(180deg)" : undefined,
position: "absolute",
top: "2px",
marginLeft: ".5rem"
@ -495,11 +528,11 @@ class FileBrowser extends Component {
<SizeP onClick={() => this.updateOrder("size")} style={{ position: "relative", cursor: "pointer" }}>
size
{
this.state.orderBy === "size" &&
<img src={CaretDown} alt={this.state.orderAscending ? "ascending" : "descending"}
orderBy === "size" &&
<img src={CaretDown} alt={orderAscending ? "ascending" : "descending"}
height="20" width="20"
style={{
transform: this.state.orderAscending ? "rotateZ(180deg)" : undefined,
transform: orderAscending ? "rotateZ(180deg)" : undefined,
position: "absolute",
top: "2px",
marginLeft: ".5rem"
@ -510,7 +543,7 @@ class FileBrowser extends Component {
</GridFileBrowser>
</BrowserHeader>
<BrowserWrapper>
<BrowserWrapper onScroll={this.handleGridScroll}>
{
transitionFiles !== 0 &&
<GridFileBrowser style={{
@ -523,7 +556,15 @@ class FileBrowser extends Component {
transitionFiles > -2 && transitionFiles < 0 ?
"translateX(-100%)" : undefined
}}>
{
from > 0 &&
<OptimisticRow style={{ height: from * (ROW_HEIGHT + ROW_GAP) }} />
}
{ this.renderFiles(true) }
{
to < rows &&
<OptimisticRow style={{ height: (rows - to) * (ROW_HEIGHT + ROW_GAP) }} />
}
</GridFileBrowser>
}
@ -537,7 +578,15 @@ class FileBrowser extends Component {
transitionFiles === 3 || transitionFiles === -3 ?
"none" : undefined
}}>
{
from > 0 &&
<OptimisticRow style={{ height: from * (ROW_HEIGHT + ROW_GAP) }} />
}
{ this.renderFiles() }
{
to < rows &&
<OptimisticRow style={{ height: (rows - to) * (ROW_HEIGHT + ROW_GAP) }} />
}
</GridFileBrowser>
</BrowserWrapper>
</FBContainer>

View File

@ -69,7 +69,6 @@ class FileBrowserMenu extends Component {
.then(response => {
if (typeof response.data.list !== "object") return reject(new Error("Invalid response"))
if (response.data.list.length > 100) response.data.list.length = 100
let { files } = this.state
files[brIndex] = response.data.list
loading[brIndex] = false