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:
parent
dbd7956491
commit
a1b92f5587
@ -40,6 +40,15 @@ import { Button } from '../styled.js'
|
|||||||
import assert from 'assert'
|
import assert from 'assert'
|
||||||
import LineLoader from './LineLoader.jsx'
|
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`
|
const FBContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -213,13 +222,16 @@ class FileBrowser extends Component {
|
|||||||
renderMenu: false,
|
renderMenu: false,
|
||||||
cursorX: 0,
|
cursorX: 0,
|
||||||
cursorY: 0,
|
cursorY: 0,
|
||||||
clicked: ""
|
clicked: "",
|
||||||
|
from: 0,
|
||||||
|
to: 100
|
||||||
}
|
}
|
||||||
|
|
||||||
this.backListener = undefined
|
this.backListener = undefined
|
||||||
|
|
||||||
this.handleInputChange = this.handleInputChange.bind(this)
|
this.handleInputChange = this.handleInputChange.bind(this)
|
||||||
this.searchTimeout = undefined
|
this.searchTimeout = undefined
|
||||||
|
this.scrollTimeout = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount = () => {
|
componentDidMount = () => {
|
||||||
@ -241,7 +253,7 @@ class FileBrowser extends Component {
|
|||||||
this.props.currentPath.split("/").length < this.state.prevPath.split("/").length ||
|
this.props.currentPath.split("/").length < this.state.prevPath.split("/").length ||
|
||||||
this.props.currentPath === "/"
|
this.props.currentPath === "/"
|
||||||
) direction = -1
|
) 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(5).then(() => this.setState({ transitionFiles: 2 * direction }))
|
||||||
delay(300).then(() => this.setState({ transitionFiles: 3 * direction, files: this.props.files }))
|
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
|
if (isNew) files = this.props.files
|
||||||
else files = this.state.files
|
else files = this.state.files
|
||||||
|
|
||||||
|
files = this.getOrderedItems(files).slice(this.state.from, this.state.to)
|
||||||
|
|
||||||
return 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))
|
|
||||||
.map(v => (
|
.map(v => (
|
||||||
<Fragment key={v.Name + "file"}>
|
<Fragment key={v.Name + "file"}>
|
||||||
{ this.renderImage(v.MimeType, v.Name) }
|
{ 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
|
// render the path the user is currently at
|
||||||
renderPath = () =>
|
renderPath = () =>
|
||||||
path.join(...this.props.currentPath.split("/"))
|
path.join(...this.props.currentPath.split("/"))
|
||||||
@ -434,7 +466,8 @@ class FileBrowser extends Component {
|
|||||||
)
|
)
|
||||||
|
|
||||||
render() {
|
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 (
|
return (
|
||||||
<FBContainer>
|
<FBContainer>
|
||||||
@ -465,11 +498,11 @@ class FileBrowser extends Component {
|
|||||||
<FilenameP onClick={() => this.updateOrder("name")} style={{ position: "relative", cursor: "pointer" }}>
|
<FilenameP onClick={() => this.updateOrder("name")} style={{ position: "relative", cursor: "pointer" }}>
|
||||||
filename
|
filename
|
||||||
{
|
{
|
||||||
this.state.orderBy === "name" &&
|
orderBy === "name" &&
|
||||||
<img src={CaretDown} alt={this.state.orderAscending ? "ascending" : "descending"}
|
<img src={CaretDown} alt={orderAscending ? "ascending" : "descending"}
|
||||||
height="20" width="20"
|
height="20" width="20"
|
||||||
style={{
|
style={{
|
||||||
transform: this.state.orderAscending ? "rotateZ(180deg)" : undefined,
|
transform: orderAscending ? "rotateZ(180deg)" : undefined,
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
top: "2px",
|
top: "2px",
|
||||||
marginLeft: ".5rem"
|
marginLeft: ".5rem"
|
||||||
@ -480,11 +513,11 @@ class FileBrowser extends Component {
|
|||||||
{/* <ModifiedP onClick={() => this.updateOrder("modified")} style={{ position: "relative", cursor: "pointer" }}>
|
{/* <ModifiedP onClick={() => this.updateOrder("modified")} style={{ position: "relative", cursor: "pointer" }}>
|
||||||
modified
|
modified
|
||||||
{
|
{
|
||||||
this.state.orderBy === "modified" &&
|
orderBy === "modified" &&
|
||||||
<img src={CaretDown} alt={this.state.orderAscending ? "ascending" : "descending"}
|
<img src={CaretDown} alt={orderAscending ? "ascending" : "descending"}
|
||||||
height="20" width="20"
|
height="20" width="20"
|
||||||
style={{
|
style={{
|
||||||
transform: this.state.orderAscending ? "rotateZ(180deg)" : undefined,
|
transform: orderAscending ? "rotateZ(180deg)" : undefined,
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
top: "2px",
|
top: "2px",
|
||||||
marginLeft: ".5rem"
|
marginLeft: ".5rem"
|
||||||
@ -495,11 +528,11 @@ class FileBrowser extends Component {
|
|||||||
<SizeP onClick={() => this.updateOrder("size")} style={{ position: "relative", cursor: "pointer" }}>
|
<SizeP onClick={() => this.updateOrder("size")} style={{ position: "relative", cursor: "pointer" }}>
|
||||||
size
|
size
|
||||||
{
|
{
|
||||||
this.state.orderBy === "size" &&
|
orderBy === "size" &&
|
||||||
<img src={CaretDown} alt={this.state.orderAscending ? "ascending" : "descending"}
|
<img src={CaretDown} alt={orderAscending ? "ascending" : "descending"}
|
||||||
height="20" width="20"
|
height="20" width="20"
|
||||||
style={{
|
style={{
|
||||||
transform: this.state.orderAscending ? "rotateZ(180deg)" : undefined,
|
transform: orderAscending ? "rotateZ(180deg)" : undefined,
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
top: "2px",
|
top: "2px",
|
||||||
marginLeft: ".5rem"
|
marginLeft: ".5rem"
|
||||||
@ -510,7 +543,7 @@ class FileBrowser extends Component {
|
|||||||
</GridFileBrowser>
|
</GridFileBrowser>
|
||||||
</BrowserHeader>
|
</BrowserHeader>
|
||||||
|
|
||||||
<BrowserWrapper>
|
<BrowserWrapper onScroll={this.handleGridScroll}>
|
||||||
{
|
{
|
||||||
transitionFiles !== 0 &&
|
transitionFiles !== 0 &&
|
||||||
<GridFileBrowser style={{
|
<GridFileBrowser style={{
|
||||||
@ -523,7 +556,15 @@ class FileBrowser extends Component {
|
|||||||
transitionFiles > -2 && transitionFiles < 0 ?
|
transitionFiles > -2 && transitionFiles < 0 ?
|
||||||
"translateX(-100%)" : undefined
|
"translateX(-100%)" : undefined
|
||||||
}}>
|
}}>
|
||||||
|
{
|
||||||
|
from > 0 &&
|
||||||
|
<OptimisticRow style={{ height: from * (ROW_HEIGHT + ROW_GAP) }} />
|
||||||
|
}
|
||||||
{ this.renderFiles(true) }
|
{ this.renderFiles(true) }
|
||||||
|
{
|
||||||
|
to < rows &&
|
||||||
|
<OptimisticRow style={{ height: (rows - to) * (ROW_HEIGHT + ROW_GAP) }} />
|
||||||
|
}
|
||||||
</GridFileBrowser>
|
</GridFileBrowser>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -537,7 +578,15 @@ class FileBrowser extends Component {
|
|||||||
transitionFiles === 3 || transitionFiles === -3 ?
|
transitionFiles === 3 || transitionFiles === -3 ?
|
||||||
"none" : undefined
|
"none" : undefined
|
||||||
}}>
|
}}>
|
||||||
|
{
|
||||||
|
from > 0 &&
|
||||||
|
<OptimisticRow style={{ height: from * (ROW_HEIGHT + ROW_GAP) }} />
|
||||||
|
}
|
||||||
{ this.renderFiles() }
|
{ this.renderFiles() }
|
||||||
|
{
|
||||||
|
to < rows &&
|
||||||
|
<OptimisticRow style={{ height: (rows - to) * (ROW_HEIGHT + ROW_GAP) }} />
|
||||||
|
}
|
||||||
</GridFileBrowser>
|
</GridFileBrowser>
|
||||||
</BrowserWrapper>
|
</BrowserWrapper>
|
||||||
</FBContainer>
|
</FBContainer>
|
||||||
|
@ -69,7 +69,6 @@ class FileBrowserMenu extends Component {
|
|||||||
.then(response => {
|
.then(response => {
|
||||||
if (typeof response.data.list !== "object") return reject(new Error("Invalid 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
|
let { files } = this.state
|
||||||
files[brIndex] = response.data.list
|
files[brIndex] = response.data.list
|
||||||
loading[brIndex] = false
|
loading[brIndex] = false
|
||||||
|
Loading…
x
Reference in New Issue
Block a user