all snakes are div's now again

This commit is contained in:
Daniel Shleifman
2021-06-15 17:03:12 +03:00
parent 1dd0eb30f5
commit d1c25a92b4
6 changed files with 108 additions and 96 deletions

View File

@@ -1,59 +1,95 @@
import { FixedSizeGrid as Grid } from 'react-window'
import AutoSizer from 'react-virtualized-auto-sizer'
import { memo } from 'react'
import styled, { css } from 'styled-components'
import Measure from 'react-measure'
import { useState } from 'react'
import { v4 as uuidv4 } from 'uuid'
import { getLargeSnakeColors } from './colors'
import {
defaultBackgroundColor,
defaultBorderColor,
progressColor,
completeColor,
activeColor,
rangeColor,
} from './colors'
import getShortCacheMap from './getShortCacheMap'
const Cell = memo(({ columnIndex, rowIndex, style, data }) => {
const { columnCount, cacheMap, gutterSize, borderSize, pieces } = data
const itemIndex = rowIndex * columnCount + columnIndex
const borderWidth = 1
const defaultPieceSize = 14
const pieceSizeForMiniMap = 23
const gapBetweenPieces = 3
const { borderColor, backgroundColor } = getLargeSnakeColors(cacheMap[itemIndex] || {})
const SnakeWrapper = styled.div`
${({ pieceSize, piecesInOneRow }) => css`
display: grid;
gap: ${gapBetweenPieces}px;
grid-template-columns: repeat(${piecesInOneRow || 'auto-fit'}, ${pieceSize}px);
grid-auto-rows: max-content;
justify-content: center;
const newStyle = {
...style,
left: style.left + gutterSize,
top: style.top + gutterSize,
width: style.width - gutterSize,
height: style.height - gutterSize,
border: `${borderSize}px solid ${borderColor}`,
display: itemIndex >= pieces ? 'none' : null,
background: backgroundColor,
.piece {
width: ${pieceSize}px;
height: ${pieceSize}px;
background: ${defaultBackgroundColor};
border: ${borderWidth}px solid ${defaultBorderColor};
display: grid;
align-items: end;
&-loading {
background: ${progressColor};
border-color: ${progressColor};
}
&-complete {
background: ${completeColor};
border-color: ${completeColor};
}
&-reader {
border-color: ${activeColor};
}
}
.reader-range {
border-color: ${rangeColor};
}
`}
`
const PercentagePiece = styled.div`
background: ${completeColor};
height: ${({ percentage }) => (percentage / 100) * 12}px;
`
export default function LargeSnake({ cacheMap, isMini, preloadPiecesAmount }) {
const [dimensions, setDimensions] = useState({ width: 0, height: 0 })
const pieceSize = isMini ? pieceSizeForMiniMap : defaultPieceSize
let piecesInOneRow
let shotCacheMap
if (isMini) {
const pieceSizeWithGap = pieceSize + gapBetweenPieces
piecesInOneRow = Math.floor(dimensions.width / pieceSizeWithGap)
shotCacheMap = isMini && getShortCacheMap({ cacheMap, preloadPiecesAmount, piecesInOneRow })
}
return <div style={newStyle} />
})
const gutterSize = 2
const borderSize = 1
const pieceSize = 12
const pieceSizeWithSpacing = pieceSize + gutterSize
export default function LargeSnake({ cacheMap }) {
const pieces = cacheMap.length
return (
<div style={{ height: '60vh', overflow: 'hidden' }}>
<AutoSizer>
{({ height, width }) => {
const columnCount = Math.floor(width / (gutterSize + pieceSize)) - 1
const rowCount = pieces / columnCount + 1
return (
<Grid
columnCount={columnCount}
rowCount={rowCount}
columnWidth={pieceSizeWithSpacing}
rowHeight={pieceSizeWithSpacing}
height={height}
width={width}
itemData={{ columnCount, cacheMap, gutterSize, borderSize, pieces }}
>
{Cell}
</Grid>
)
}}
</AutoSizer>
</div>
return isMini ? (
<Measure bounds onResize={({ bounds }) => setDimensions(bounds)}>
{({ measureRef }) => (
<SnakeWrapper ref={measureRef} pieceSize={pieceSize} piecesInOneRow={piecesInOneRow}>
{shotCacheMap.map(({ className, id, percentage }) => (
<span key={id || uuidv4()} className={className}>
{percentage > 0 && percentage <= 100 && <PercentagePiece percentage={percentage} />}
</span>
))}
</SnakeWrapper>
)}
</Measure>
) : (
<SnakeWrapper pieceSize={pieceSize}>
{cacheMap.map(({ className, id, percentage }) => (
<span key={id || uuidv4()} className={className}>
{percentage > 0 && percentage <= 100 && <PercentagePiece percentage={percentage} />}
</span>
))}
</SnakeWrapper>
)
}

View File

@@ -1,5 +1,7 @@
export default ({ cacheMap, preloadPiecesAmount, piecesInOneRow }) => {
const cacheMapWithoutEmptyBlocks = cacheMap.filter(({ isComplete, inProgress }) => inProgress || isComplete)
const cacheMapWithoutEmptyBlocks = cacheMap.filter(
({ className }) => className.includes('piece-complete') || className.includes('piece-loading'),
)
const getFullAmountOfBlocks = amountOfBlocks =>
// this function counts existed amount of blocks with extra "empty blocks" to fill the row till the end
@@ -21,7 +23,9 @@ export default ({ cacheMap, preloadPiecesAmount, piecesInOneRow }) => {
const extraBlocksAmount = finalAmountOfBlocksToRenderInShortView - cacheMapWithoutEmptyBlocks.length + 1
// amount of blocks needed to fill the line till the end
const extraEmptyBlocksForFillingLine = extraBlocksAmount ? new Array(extraBlocksAmount).fill({}) : []
const extraEmptyBlocksForFillingLine = extraBlocksAmount
? new Array(extraBlocksAmount).fill({ className: 'piece' })
: []
return [...cacheMapWithoutEmptyBlocks, ...extraEmptyBlocksForFillingLine]
}

View File

@@ -3,22 +3,14 @@ import isEqual from 'lodash/isEqual'
import { useCreateCacheMap } from '../customHooks'
import LargeSnake from './LargeSnake'
import DefaultSnake from './DefaultSnake'
const TorrentCache = memo(
({ cache, isMini }) => {
const cacheMap = useCreateCacheMap(cache)
const preloadPiecesAmount = Math.round(cache.Capacity / cache.PiecesLength - 1)
const isSnakeLarge = cacheMap.length > 1000
return isMini ? (
<DefaultSnake isMini cacheMap={cacheMap} preloadPiecesAmount={preloadPiecesAmount} />
) : isSnakeLarge ? (
<LargeSnake cacheMap={cacheMap} />
) : (
<DefaultSnake cacheMap={cacheMap} preloadPiecesAmount={preloadPiecesAmount} />
)
return <LargeSnake isMini={isMini} cacheMap={cacheMap} preloadPiecesAmount={preloadPiecesAmount} />
},
(prev, next) => isEqual(prev.cache.Pieces, next.cache.Pieces) && isEqual(prev.cache.Readers, next.cache.Readers),
)

View File

@@ -38,32 +38,32 @@ export const useCreateCacheMap = cache => {
const [cacheMap, setCacheMap] = useState([])
useEffect(() => {
if (!cache.PiecesCount || !cache.Pieces) return
const { Pieces, PiecesCount, Readers } = cache
const { PiecesCount, Pieces, Readers } = cache
const map = []
for (let i = 0; i < PiecesCount; i++) {
const newPiece = { id: i }
const currentPiece = Pieces[i]
if (currentPiece) {
if (currentPiece.Completed && currentPiece.Size === currentPiece.Length) newPiece.isComplete = true
else {
newPiece.inProgress = true
newPiece.percentage = (currentPiece.Size / currentPiece.Length).toFixed(2)
}
const activeBlock = Pieces[i]
const className = ['piece']
if (activeBlock) {
const { Completed, Size, Length } = activeBlock
className.push(Completed && Size >= Length ? 'piece-complete' : 'piece-loading')
newPiece.percentage = ((Size / Length) * 100).toFixed(2)
}
Readers.forEach(r => {
if (i === r.Reader) newPiece.isActive = true
if (i >= r.Start && i <= r.End) newPiece.isReaderRange = true
if (i === r.Reader) {
className.push('piece-reader')
} else if (i >= r.Start && i <= r.End) className.push('reader-range')
})
newPiece.className = className.join(' ')
map.push(newPiece)
}
setCacheMap(map)
}, [cache])