diff --git a/web/package.json b/web/package.json
index ce5efa6..1dbb14a 100644
--- a/web/package.json
+++ b/web/package.json
@@ -10,7 +10,6 @@
"fontsource-roboto": "^4.0.0",
"i18next": "^20.3.1",
"i18next-browser-languagedetector": "^6.1.1",
- "konva": "^8.0.1",
"lodash": "^4.17.21",
"material-ui-image": "^3.3.2",
"parse-torrent": "^9.1.3",
@@ -21,12 +20,9 @@
"react-dom": "^17.0.2",
"react-dropzone": "^11.3.2",
"react-i18next": "^11.10.0",
- "react-konva": "^17.0.2-4",
"react-measure": "^2.5.2",
"react-query": "^3.17.0",
"react-scripts": "4.0.3",
- "react-virtualized-auto-sizer": "^1.0.5",
- "react-window": "^1.8.6",
"styled-components": "^5.3.0",
"uuid": "^8.3.2"
},
diff --git a/web/src/components/DialogTorrentDetailsContent/TorrentCache/DefaultSnake.jsx b/web/src/components/DialogTorrentDetailsContent/TorrentCache/DefaultSnake.jsx
deleted file mode 100644
index ada28a9..0000000
--- a/web/src/components/DialogTorrentDetailsContent/TorrentCache/DefaultSnake.jsx
+++ /dev/null
@@ -1,127 +0,0 @@
-import { useEffect, useState } from 'react'
-import DialogContent from '@material-ui/core/DialogContent'
-import { Stage, Layer } from 'react-konva'
-import Measure from 'react-measure'
-import { v4 as uuidv4 } from 'uuid'
-import styled from 'styled-components'
-import { useTranslation } from 'react-i18next'
-
-import SingleBlock from './SingleBlock'
-import getShortCacheMap from './getShortCacheMap'
-
-const ScrollNotification = styled.div`
- margin-top: 10px;
- text-transform: uppercase;
- color: rgba(0, 0, 0, 0.5);
- align-self: center;
-`
-
-export default function DefaultSnake({ isMini, cacheMap, preloadPiecesAmount }) {
- const { t } = useTranslation()
- const [dimensions, setDimensions] = useState({ width: 0, height: 0 })
- const [stageSettings, setStageSettings] = useState({
- boxHeight: null,
- strokeWidth: null,
- marginBetweenBlocks: null,
- stageOffset: null,
- })
- const updateStageSettings = (boxHeight, strokeWidth) => {
- setStageSettings({
- boxHeight,
- strokeWidth,
- marginBetweenBlocks: strokeWidth,
- stageOffset: strokeWidth * 2,
- })
- }
-
- useEffect(() => {
- // initializing stageSettings
- if (isMini) return dimensions.width < 500 ? updateStageSettings(20, 3) : updateStageSettings(24, 4)
- updateStageSettings(12, 2)
- }, [isMini, dimensions.width])
-
- const miniCacheMaxHeight = 340
-
- const { boxHeight, strokeWidth, marginBetweenBlocks, stageOffset } = stageSettings
-
- const blockSizeWithMargin = boxHeight + strokeWidth + marginBetweenBlocks
- const piecesInOneRow = Math.floor((dimensions.width * 0.9) / blockSizeWithMargin)
-
- const shortCacheMap = isMini ? getShortCacheMap({ cacheMap, preloadPiecesAmount, piecesInOneRow }) : []
-
- const amountOfRows = Math.ceil((isMini ? shortCacheMap.length : cacheMap.length) / piecesInOneRow)
-
- const getItemCoordinates = blockOrder => {
- const currentRow = Math.floor(blockOrder / piecesInOneRow)
- const x = (blockOrder % piecesInOneRow) * blockSizeWithMargin || 0
- const y = currentRow * blockSizeWithMargin || 0
-
- return { x, y }
- }
-
- return (
- setDimensions(bounds)}>
- {({ measureRef }) => (
-
-
-
-
- {isMini
- ? shortCacheMap.map(({ percentage, isComplete, inProgress, isActive, isReaderRange }, i) => {
- const { x, y } = getItemCoordinates(i)
-
- return (
-
- )
- })
- : cacheMap.map(({ id, percentage, isComplete, inProgress, isActive, isReaderRange }) => {
- const { x, y } = getItemCoordinates(id)
-
- return (
-
- )
- })}
-
-
-
-
- {isMini &&
- (stageOffset + blockSizeWithMargin * amountOfRows || 0) >= miniCacheMaxHeight &&
- dimensions.height >= miniCacheMaxHeight && {t('ScrollDown')}}
-
- )}
-
- )
-}
diff --git a/web/src/components/DialogTorrentDetailsContent/TorrentCache/LargeSnake.jsx b/web/src/components/DialogTorrentDetailsContent/TorrentCache/LargeSnake.jsx
deleted file mode 100644
index 82e1d56..0000000
--- a/web/src/components/DialogTorrentDetailsContent/TorrentCache/LargeSnake.jsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import { FixedSizeGrid as Grid } from 'react-window'
-import AutoSizer from 'react-virtualized-auto-sizer'
-import { memo } from 'react'
-
-import { getLargeSnakeColors } from './colors'
-
-const Cell = memo(({ columnIndex, rowIndex, style, data }) => {
- const { columnCount, cacheMap, gutterSize, borderSize, pieces } = data
- const itemIndex = rowIndex * columnCount + columnIndex
-
- const { borderColor, backgroundColor } = getLargeSnakeColors(cacheMap[itemIndex] || {})
-
- 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,
- }
-
- return
-})
-
-const gutterSize = 2
-const borderSize = 1
-const pieceSize = 12
-const pieceSizeWithSpacing = pieceSize + gutterSize
-
-export default function LargeSnake({ cacheMap }) {
- const pieces = cacheMap.length
-
- return (
-
-
- {({ height, width }) => {
- const columnCount = Math.floor(width / (gutterSize + pieceSize)) - 1
- const rowCount = pieces / columnCount + 1
-
- return (
-
- {Cell}
-
- )
- }}
-
-
- )
-}
diff --git a/web/src/components/DialogTorrentDetailsContent/TorrentCache/SingleBlock.jsx b/web/src/components/DialogTorrentDetailsContent/TorrentCache/SingleBlock.jsx
deleted file mode 100644
index 3951532..0000000
--- a/web/src/components/DialogTorrentDetailsContent/TorrentCache/SingleBlock.jsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import { Rect } from 'react-konva'
-
-import { activeColor, completeColor, defaultBorderColor, progressColor, rangeColor } from './colors'
-
-export default function SingleBlock({
- x,
- y,
- percentage,
- isActive = false,
- inProgress = false,
- isReaderRange = false,
- isComplete = false,
- boxHeight,
- strokeWidth,
-}) {
- const strokeColor = isActive
- ? activeColor
- : isComplete
- ? completeColor
- : inProgress
- ? progressColor
- : isReaderRange
- ? rangeColor
- : defaultBorderColor
- const backgroundColor = inProgress ? progressColor : defaultBorderColor
- const percentageProgressColor = completeColor
- const processCompletedColor = completeColor
-
- return (
-
- )
-}
diff --git a/web/src/components/DialogTorrentDetailsContent/TorrentCache/colors.js b/web/src/components/DialogTorrentDetailsContent/TorrentCache/colors.js
deleted file mode 100644
index 235be1d..0000000
--- a/web/src/components/DialogTorrentDetailsContent/TorrentCache/colors.js
+++ /dev/null
@@ -1,26 +0,0 @@
-export const defaultBorderColor = '#eef2f4'
-export const defaultBackgroundColor = '#fff'
-export const completeColor = '#00a572'
-export const progressColor = '#ffa724'
-export const activeColor = '#000'
-export const rangeColor = '#9a9aff'
-
-export const getLargeSnakeColors = ({ isActive, isComplete, inProgress, isReaderRange, percentage }) => {
- const gradientBackgroundColor = inProgress ? progressColor : defaultBackgroundColor
- const gradient = `linear-gradient(to top, ${completeColor} 0%, ${completeColor} ${
- percentage * 100
- }%, ${gradientBackgroundColor} ${percentage * 100}%, ${gradientBackgroundColor} 100%)`
-
- const borderColor = isActive
- ? activeColor
- : isComplete
- ? completeColor
- : inProgress
- ? progressColor
- : isReaderRange
- ? rangeColor
- : defaultBorderColor
- const backgroundColor = isComplete ? completeColor : inProgress ? gradient : defaultBackgroundColor
-
- return { borderColor, backgroundColor }
-}
diff --git a/web/src/components/DialogTorrentDetailsContent/TorrentCache/getShortCacheMap.js b/web/src/components/DialogTorrentDetailsContent/TorrentCache/getShortCacheMap.js
index 729c643..9aed090 100644
--- a/web/src/components/DialogTorrentDetailsContent/TorrentCache/getShortCacheMap.js
+++ b/web/src/components/DialogTorrentDetailsContent/TorrentCache/getShortCacheMap.js
@@ -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]
}
diff --git a/web/src/components/DialogTorrentDetailsContent/TorrentCache/index.jsx b/web/src/components/DialogTorrentDetailsContent/TorrentCache/index.jsx
index 10c75fd..6a4903f 100644
--- a/web/src/components/DialogTorrentDetailsContent/TorrentCache/index.jsx
+++ b/web/src/components/DialogTorrentDetailsContent/TorrentCache/index.jsx
@@ -1,26 +1,59 @@
-import { memo } from 'react'
+import Measure from 'react-measure'
+import { useState, memo } from 'react'
+import { v4 as uuidv4 } from 'uuid'
+import { useTranslation } from 'react-i18next'
import isEqual from 'lodash/isEqual'
import { useCreateCacheMap } from '../customHooks'
-import LargeSnake from './LargeSnake'
-import DefaultSnake from './DefaultSnake'
+import { gapBetweenPieces, miniCacheMaxHeight, pieceSizeForMiniMap, defaultPieceSize } from './snakeSettings'
+import getShortCacheMap from './getShortCacheMap'
+import { SnakeWrapper, PercentagePiece, ScrollNotification } from './style'
-const TorrentCache = memo(
- ({ cache, isMini }) => {
- const cacheMap = useCreateCacheMap(cache)
+const TorrentCache = ({ cache, isMini }) => {
+ const { t } = useTranslation()
+ const [dimensions, setDimensions] = useState({ width: 0, height: 0 })
+ const cacheMap = useCreateCacheMap(cache)
- const preloadPiecesAmount = Math.round(cache.Capacity / cache.PiecesLength - 1)
- const isSnakeLarge = cacheMap.length > 1000
+ const preloadPiecesAmount = Math.round(cache.Capacity / cache.PiecesLength - 1)
- return isMini ? (
-
- ) : isSnakeLarge ? (
-
- ) : (
-
- )
- },
+ const pieceSize = isMini ? pieceSizeForMiniMap : defaultPieceSize
+
+ let piecesInOneRow
+ let shotCacheMap
+ if (isMini) {
+ const pieceSizeWithGap = pieceSize + gapBetweenPieces
+ piecesInOneRow = Math.floor((dimensions.width * 0.95) / pieceSizeWithGap)
+ shotCacheMap = isMini && getShortCacheMap({ cacheMap, preloadPiecesAmount, piecesInOneRow })
+ }
+
+ return isMini ? (
+ setDimensions(bounds)}>
+ {({ measureRef }) => (
+
+
+ {shotCacheMap.map(({ className, id, percentage }) => (
+
+ {percentage > 0 && percentage <= 100 && }
+
+ ))}
+
+
+ {dimensions.height >= miniCacheMaxHeight &&
{t('ScrollDown')}}
+
+ )}
+
+ ) : (
+
+ {cacheMap.map(({ className, id, percentage }) => (
+
+ {percentage > 0 && percentage <= 100 && }
+
+ ))}
+
+ )
+}
+
+export default memo(
+ TorrentCache,
(prev, next) => isEqual(prev.cache.Pieces, next.cache.Pieces) && isEqual(prev.cache.Readers, next.cache.Readers),
)
-
-export default TorrentCache
diff --git a/web/src/components/DialogTorrentDetailsContent/TorrentCache/snakeSettings.js b/web/src/components/DialogTorrentDetailsContent/TorrentCache/snakeSettings.js
new file mode 100644
index 0000000..82c4463
--- /dev/null
+++ b/web/src/components/DialogTorrentDetailsContent/TorrentCache/snakeSettings.js
@@ -0,0 +1,12 @@
+export const borderWidth = 1
+export const defaultPieceSize = 14
+export const pieceSizeForMiniMap = 23
+export const gapBetweenPieces = 3
+export const miniCacheMaxHeight = 340
+
+export const defaultBorderColor = '#eef2f4'
+export const defaultBackgroundColor = '#fff'
+export const completeColor = '#00a572'
+export const progressColor = '#ffa724'
+export const activeColor = '#000'
+export const rangeColor = '#9a9aff'
diff --git a/web/src/components/DialogTorrentDetailsContent/TorrentCache/style.js b/web/src/components/DialogTorrentDetailsContent/TorrentCache/style.js
new file mode 100644
index 0000000..532a6d3
--- /dev/null
+++ b/web/src/components/DialogTorrentDetailsContent/TorrentCache/style.js
@@ -0,0 +1,66 @@
+import styled, { css } from 'styled-components'
+
+import {
+ defaultBackgroundColor,
+ defaultBorderColor,
+ progressColor,
+ completeColor,
+ activeColor,
+ rangeColor,
+ gapBetweenPieces,
+ miniCacheMaxHeight,
+ borderWidth,
+} from './snakeSettings'
+
+export const ScrollNotification = styled.div`
+ margin-top: 10px;
+ text-transform: uppercase;
+ color: rgba(0, 0, 0, 0.5);
+ align-self: center;
+`
+
+export 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;
+
+ ${piecesInOneRow &&
+ css`
+ max-height: ${miniCacheMaxHeight}px;
+ overflow: auto;
+ `}
+
+ .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};
+ }
+ `}
+`
+
+export const PercentagePiece = styled.div`
+ background: ${completeColor};
+ height: ${({ percentage }) => (percentage / 100) * 12}px;
+`
diff --git a/web/src/components/DialogTorrentDetailsContent/customHooks.jsx b/web/src/components/DialogTorrentDetailsContent/customHooks.jsx
index 5eeed67..6c50096 100644
--- a/web/src/components/DialogTorrentDetailsContent/customHooks.jsx
+++ b/web/src/components/DialogTorrentDetailsContent/customHooks.jsx
@@ -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])
diff --git a/web/yarn.lock b/web/yarn.lock
index 4e7b69f..34ff6b6 100644
--- a/web/yarn.lock
+++ b/web/yarn.lock
@@ -1174,7 +1174,7 @@
dependencies:
regenerator-runtime "^0.13.4"
-"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.14.0", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
+"@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.14.0", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
version "7.14.0"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.0.tgz#46794bc20b612c5f75e62dd071e24dfd95f1cbe6"
integrity sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA==
@@ -7938,11 +7938,6 @@ klona@^2.0.4:
resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0"
integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==
-konva@^8.0.1:
- version "8.0.1"
- resolved "https://registry.yarnpkg.com/konva/-/konva-8.0.1.tgz#f34f483cdf62c36f966addc1a7484ed694313c2b"
- integrity sha512-QDppGS1L5Dhod1zjwy9GVVjeyfPBHnPncL5oRh1NyjR1mEvhrLjzflrkdW+p73uFIW9hwCDZVLGxzzjQre9izw==
-
language-subtag-registry@~0.3.2:
version "0.3.21"
resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz#04ac218bea46f04cb039084602c6da9e788dd45a"
@@ -8306,11 +8301,6 @@ media-typer@0.3.0:
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
-"memoize-one@>=3.1.1 <6":
- version "5.2.1"
- resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
- integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==
-
memory-fs@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
@@ -10477,14 +10467,6 @@ react-is@^16.7.0, react-is@^16.8.1:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
-react-konva@^17.0.2-4:
- version "17.0.2-4"
- resolved "https://registry.yarnpkg.com/react-konva/-/react-konva-17.0.2-4.tgz#afd0968e1295b624bf2a7a154ba294e0d5be55cd"
- integrity sha512-YvRVPT81y8sMQV1SY1/tIDetGxBK+7Rk86u4LmiyDBLLE17vD78F01b8EC3AuP3nI3hUaTblfBugUF35cm6Etg==
- dependencies:
- react-reconciler "~0.26.2"
- scheduler "^0.20.2"
-
react-measure@^2.5.2:
version "2.5.2"
resolved "https://registry.yarnpkg.com/react-measure/-/react-measure-2.5.2.tgz#4ffc410e8b9cb836d9455a9ff18fc1f0fca67f89"
@@ -10504,15 +10486,6 @@ react-query@^3.17.0:
broadcast-channel "^3.4.1"
match-sorter "^6.0.2"
-react-reconciler@~0.26.2:
- version "0.26.2"
- resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.26.2.tgz#bbad0e2d1309423f76cf3c3309ac6c96e05e9d91"
- integrity sha512-nK6kgY28HwrMNwDnMui3dvm3rCFjZrcGiuwLc5COUipBK5hWHLOxMJhSnSomirqWwjPBJKV1QcbkI0VJr7Gl1Q==
- dependencies:
- loose-envify "^1.1.0"
- object-assign "^4.1.1"
- scheduler "^0.20.2"
-
react-refresh@^0.8.3:
version "0.8.3"
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f"
@@ -10594,19 +10567,6 @@ react-transition-group@^4.4.0:
loose-envify "^1.4.0"
prop-types "^15.6.2"
-react-virtualized-auto-sizer@^1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.5.tgz#9eeeb8302022de56fbd7a860b08513120ce36509"
- integrity sha512-kivjYVWX15TX2IUrm8F1jaCEX8EXrpy3DD+u41WGqJ1ZqbljWpiwscV+VxOM1l7sSIM1jwi2LADjhhAJkJ9dxA==
-
-react-window@^1.8.6:
- version "1.8.6"
- resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.6.tgz#d011950ac643a994118632665aad0c6382e2a112"
- integrity sha512-8VwEEYyjz6DCnGBsd+MgkD0KJ2/OXFULyDtorIiTz+QzwoP94tBoA7CnbtyXMm+cCeAUER5KJcPtWl9cpKbOBg==
- dependencies:
- "@babel/runtime" "^7.0.0"
- memoize-one ">=3.1.1 <6"
-
react@^17.0.2:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"