diff --git a/packages/netlify-cms-backend-git-gateway/src/implementation.ts b/packages/netlify-cms-backend-git-gateway/src/implementation.ts index 633c4634..9864878a 100644 --- a/packages/netlify-cms-backend-git-gateway/src/implementation.ts +++ b/packages/netlify-cms-backend-git-gateway/src/implementation.ts @@ -1,6 +1,6 @@ import GoTrue from 'gotrue-js'; import jwtDecode from 'jwt-decode'; -import { fromPairs, get, pick, intersection, unzip } from 'lodash'; +import { get, pick, intersection } from 'lodash'; import ini from 'ini'; import { APIError, @@ -21,9 +21,9 @@ import { UnpublishedEntryMediaFile, parsePointerFile, getLargeMediaPatternsFromGitAttributesFile, - PointerFile, getPointerFileForMediaFileObj, getLargeMediaFilteredMediaFiles, + DisplayURLObject, } from 'netlify-cms-lib-util'; import { GitHubBackend } from 'netlify-cms-backend-github'; import { GitLabBackend } from 'netlify-cms-backend-gitlab'; @@ -75,12 +75,6 @@ interface NetlifyUser extends Credentials { user_metadata: { full_name: string; avatar_url: string }; } -interface GetMediaDisplayURLArgs { - path: string; - original: { id: string; path: string } | string; - largeMedia: PointerFile; -} - export default class GitGateway implements Implementation { config: Config; api?: GitHubAPI | GitLabAPI | BitBucketAPI; @@ -278,12 +272,8 @@ export default class GitGateway implements Implementation { const mediaFiles = await Promise.all( files.map(async file => { if (client.matchPath(file.path)) { - const { id, path } = file; - const largeMediaDisplayURLs = await this.getLargeMediaDisplayURLs( - [{ ...file, id }], - branch, - ); - const url = await client.getDownloadURL(largeMediaDisplayURLs[id]); + const { path, id } = file; + const url = await this.getLargeMediaDisplayURL({ path, id }, branch); return { id, name: basename(path), @@ -303,32 +293,7 @@ export default class GitGateway implements Implementation { } getMedia(mediaFolder = this.mediaFolder) { - return Promise.all([this.backend!.getMedia(mediaFolder), this.getLargeMediaClient()]).then( - async ([mediaFiles, largeMediaClient]) => { - if (!largeMediaClient.enabled) { - return mediaFiles.map(({ displayURL, ...rest }) => ({ - ...rest, - displayURL: { original: displayURL }, - })); - } - if (mediaFiles.length === 0) { - return []; - } - const largeMediaDisplayURLs = await this.getLargeMediaDisplayURLs(mediaFiles); - return mediaFiles.map(({ id, displayURL, path, ...rest }) => { - return { - ...rest, - id, - path, - displayURL: { - path, - original: displayURL, - largeMedia: largeMediaDisplayURLs[id], - }, - }; - }); - }, - ); + return this.backend!.getMedia(mediaFolder); } // this method memoizes this._getLargeMediaClient so that there can @@ -382,79 +347,54 @@ export default class GitGateway implements Implementation { }, ); } - async getLargeMediaDisplayURLs( - mediaFiles: { path: string; id: string | null }[], + async getLargeMediaDisplayURL( + { path, id }: { path: string; id: string | null }, branch = this.branch, ) { - const client = await this.getLargeMediaClient(); const readFile = ( path: string, id: string | null | undefined, { parseText }: { parseText: boolean }, ) => this.api!.readFile(path, id, { branch, parseText }); - const filesPromise = entriesByFiles(mediaFiles, readFile, 'Git-Gateway'); + const items = await entriesByFiles([{ path, id }], readFile, 'Git-Gateway'); + const entry = items[0]; + const pointerFile = parsePointerFile(entry.data); + if (!pointerFile.sha) { + console.warn(`Failed parsing pointer file ${path}`); + return path; + } - return filesPromise - .then(items => - items.map(({ file: { id, path }, data }) => { - const parsedPointerFile = parsePointerFile(data); - if (!parsedPointerFile.sha) { - console.warn(`Failed parsing pointer file ${path}`); - } - return [ - { - pointerId: id, - resourceId: parsedPointerFile.sha, - }, - parsedPointerFile, - ]; - }), - ) - .then(unzip) - .then(([idMaps, files]) => - Promise.all([ - idMaps as { pointerId: string; resourceId: string }[], - client.getResourceDownloadURLArgs(files as PointerFile[]).then(r => fromPairs(r)), - ]), - ) - .then(([idMaps, resourceMap]) => - idMaps.map(({ pointerId, resourceId }) => [pointerId, resourceMap[resourceId]]), - ) - .then(fromPairs); + const client = await this.getLargeMediaClient(); + const url = await client.getDownloadURL(pointerFile); + return url; } - getMediaDisplayURL(displayURL: DisplayURL) { - const { - path, - original, - largeMedia: largeMediaDisplayURL, - } = (displayURL as unknown) as GetMediaDisplayURLArgs; - return this.getLargeMediaClient().then(client => { - if (client.enabled && client.matchPath(path)) { - return client.getDownloadURL(largeMediaDisplayURL); - } - if (typeof original === 'string') { - return original; - } - if (this.backend!.getMediaDisplayURL) { - return this.backend!.getMediaDisplayURL(original); - } - const err = new Error( - `getMediaDisplayURL is not implemented by the ${this.backendType} backend, but the backend returned a displayURL which was not a string!`, - ) as Error & { - displayURL: DisplayURL; - }; - err.displayURL = displayURL; - return Promise.reject(err); - }); + async getMediaDisplayURL(displayURL: DisplayURL) { + const { path, id } = displayURL as DisplayURLObject; + const client = await this.getLargeMediaClient(); + if (client.enabled && client.matchPath(path)) { + return this.getLargeMediaDisplayURL({ path, id }); + } + if (typeof displayURL === 'string') { + return displayURL; + } + if (this.backend!.getMediaDisplayURL) { + return this.backend!.getMediaDisplayURL(displayURL); + } + const err = new Error( + `getMediaDisplayURL is not implemented by the ${this.backendType} backend, but the backend returned a displayURL which was not a string!`, + ) as Error & { + displayURL: DisplayURL; + }; + err.displayURL = displayURL; + return Promise.reject(err); } async getMediaFile(path: string) { const client = await this.getLargeMediaClient(); if (client.enabled && client.matchPath(path)) { - const largeMediaDisplayURLs = await this.getLargeMediaDisplayURLs([{ path, id: null }]); - const url = await client.getDownloadURL(Object.values(largeMediaDisplayURLs)[0]); + const url = await this.getLargeMediaDisplayURL({ path, id: null }); return { id: url, name: basename(path), diff --git a/packages/netlify-cms-backend-git-gateway/src/netlify-lfs-client.ts b/packages/netlify-cms-backend-git-gateway/src/netlify-lfs-client.ts index 363524ce..a90f09e6 100644 --- a/packages/netlify-cms-backend-git-gateway/src/netlify-lfs-client.ts +++ b/packages/netlify-cms-backend-git-gateway/src/netlify-lfs-client.ts @@ -66,11 +66,6 @@ const getDownloadURL = ( return Promise.resolve(''); }); -const getResourceDownloadURLArgs = (_clientConfig: ClientConfig, objects: PointerFile[]) => { - const result = objects.map(({ sha }) => [sha, { sha }]) as [string, { sha: string }][]; - return Promise.resolve(result); -}; - const uploadOperation = (objects: PointerFile[]) => ({ operation: 'upload', transfers: ['basic'], @@ -129,7 +124,6 @@ const configureFn = (config: ClientConfig, fn: Function) => (...args: unknown[]) const clientFns: Record = { resourceExists, getResourceUploadURLs, - getResourceDownloadURLArgs, getDownloadURL, uploadResource, matchPath, @@ -138,7 +132,6 @@ const clientFns: Record = { export type Client = { resourceExists: (pointer: PointerFile) => Promise; getResourceUploadURLs: (objects: PointerFile[]) => Promise; - getResourceDownloadURLArgs: (objects: PointerFile[]) => Promise<[string, { sha: string }][]>; getDownloadURL: (pointer: PointerFile) => Promise; uploadResource: (pointer: PointerFile, blob: Blob) => Promise; matchPath: (path: string) => boolean; diff --git a/packages/netlify-cms-core/package.json b/packages/netlify-cms-core/package.json index 46e5d454..05403c32 100644 --- a/packages/netlify-cms-core/package.json +++ b/packages/netlify-cms-core/package.json @@ -56,7 +56,9 @@ "react-sortable-hoc": "^1.0.0", "react-split-pane": "^0.1.85", "react-topbar-progress-indicator": "^2.0.0", + "react-virtualized-auto-sizer": "^1.0.2", "react-waypoint": "^8.1.0", + "react-window": "^1.8.5", "redux": "^4.0.1", "redux-notifications": "^4.0.1", "redux-optimist": "^1.0.0", diff --git a/packages/netlify-cms-core/src/components/MediaLibrary/MediaLibraryCard.js b/packages/netlify-cms-core/src/components/MediaLibrary/MediaLibraryCard.js index 8af223c7..6d61119a 100644 --- a/packages/netlify-cms-core/src/components/MediaLibrary/MediaLibraryCard.js +++ b/packages/netlify-cms-core/src/components/MediaLibrary/MediaLibraryCard.js @@ -8,7 +8,7 @@ const IMAGE_HEIGHT = 160; const Card = styled.div` width: ${props => props.width}; - height: 240px; + height: ${props => props.height}; margin: ${props => props.margin}; border: ${borders.textField}; border-color: ${props => props.isSelected && colors.active}; @@ -71,6 +71,7 @@ class MediaLibraryCard extends React.Component { onClick, draftText, width, + height, margin, isPrivate, type, @@ -83,6 +84,7 @@ class MediaLibraryCard extends React.Component { isSelected={isSelected} onClick={onClick} width={width} + height={height} margin={margin} tabIndex="-1" isPrivate={isPrivate} @@ -114,6 +116,7 @@ MediaLibraryCard.propTypes = { onClick: PropTypes.func.isRequired, draftText: PropTypes.string.isRequired, width: PropTypes.string.isRequired, + height: PropTypes.string.isRequired, margin: PropTypes.string.isRequired, isPrivate: PropTypes.bool, type: PropTypes.string, diff --git a/packages/netlify-cms-core/src/components/MediaLibrary/MediaLibraryCardGrid.js b/packages/netlify-cms-core/src/components/MediaLibrary/MediaLibraryCardGrid.js index e4ca9f9a..aa8ceefe 100644 --- a/packages/netlify-cms-core/src/components/MediaLibrary/MediaLibraryCardGrid.js +++ b/packages/netlify-cms-core/src/components/MediaLibrary/MediaLibraryCardGrid.js @@ -5,6 +5,143 @@ import Waypoint from 'react-waypoint'; import MediaLibraryCard from './MediaLibraryCard'; import { Map } from 'immutable'; import { colors } from 'netlify-cms-ui-default'; +import { FixedSizeGrid as Grid } from 'react-window'; +import AutoSizer from 'react-virtualized-auto-sizer'; + +const CardWrapper = props => { + const { + rowIndex, + columnIndex, + style, + data: { + mediaItems, + isSelectedFile, + onAssetClick, + cardDraftText, + cardWidth, + cardHeight, + isPrivate, + displayURLs, + loadDisplayURL, + columnCount, + gutter, + }, + } = props; + const index = rowIndex * columnCount + columnIndex; + if (index >= mediaItems.length) { + return null; + } + const file = mediaItems[index]; + + return ( +
+ onAssetClick(file)} + isDraft={file.draft} + draftText={cardDraftText} + width={cardWidth} + height={cardHeight} + margin={'0px'} + isPrivate={isPrivate} + displayURL={displayURLs.get(file.id, file.url ? Map({ url: file.url }) : Map())} + loadDisplayURL={() => loadDisplayURL(file)} + type={file.type} + isViewableImage={file.isViewableImage} + /> +
+ ); +}; + +const VirtualizedGrid = props => { + const { mediaItems, setScrollContainerRef } = props; + + return ( + + + {({ height, width }) => { + const cardWidth = parseInt(props.cardWidth, 10); + const cardHeight = parseInt(props.cardHeight, 10); + const gutter = parseInt(props.cardMargin, 10); + const columnWidth = cardWidth + gutter; + const rowHeight = cardHeight + gutter; + const columnCount = Math.floor(width / columnWidth); + const rowCount = Math.ceil(mediaItems.length / columnCount); + return ( + + {CardWrapper} + + ); + }} + + + ); +}; + +const PaginatedGrid = ({ + setScrollContainerRef, + mediaItems, + isSelectedFile, + onAssetClick, + cardDraftText, + cardWidth, + cardHeight, + cardMargin, + isPrivate, + displayURLs, + loadDisplayURL, + canLoadMore, + onLoadMore, + isPaginating, + paginatingMessage, +}) => { + return ( + + + {mediaItems.map(file => ( + onAssetClick(file)} + isDraft={file.draft} + draftText={cardDraftText} + width={cardWidth} + height={cardHeight} + margin={cardMargin} + isPrivate={isPrivate} + displayURL={displayURLs.get(file.id, file.url ? Map({ url: file.url }) : Map())} + loadDisplayURL={() => loadDisplayURL(file)} + type={file.type} + isViewableImage={file.isViewableImage} + /> + ))} + {!canLoadMore ? null : } + + {!isPaginating ? null : ( + {paginatingMessage} + )} + + ); +}; const CardGridContainer = styled.div` overflow-y: auto; @@ -23,48 +160,13 @@ const PaginatingMessage = styled.h1` color: ${props => props.isPrivate && colors.textFieldBorder}; `; -const MediaLibraryCardGrid = ({ - setScrollContainerRef, - mediaItems, - isSelectedFile, - onAssetClick, - canLoadMore, - onLoadMore, - isPaginating, - paginatingMessage, - cardDraftText, - cardWidth, - cardMargin, - isPrivate, - displayURLs, - loadDisplayURL, -}) => ( - - - {mediaItems.map(file => ( - onAssetClick(file)} - isDraft={file.draft} - draftText={cardDraftText} - width={cardWidth} - margin={cardMargin} - isPrivate={isPrivate} - displayURL={displayURLs.get(file.id, file.url ? Map({ url: file.url }) : Map())} - loadDisplayURL={() => loadDisplayURL(file)} - type={file.type} - isViewableImage={file.isViewableImage} - /> - ))} - {!canLoadMore ? null : } - - {!isPaginating ? null : ( - {paginatingMessage} - )} - -); +const MediaLibraryCardGrid = props => { + const { canLoadMore, isPaginating } = props; + if (canLoadMore || isPaginating) { + return ; + } + return ; +}; MediaLibraryCardGrid.propTypes = { setScrollContainerRef: PropTypes.func.isRequired, diff --git a/packages/netlify-cms-core/src/components/MediaLibrary/MediaLibraryModal.js b/packages/netlify-cms-core/src/components/MediaLibrary/MediaLibraryModal.js index bcd454b2..fd1d62f1 100644 --- a/packages/netlify-cms-core/src/components/MediaLibrary/MediaLibraryModal.js +++ b/packages/netlify-cms-core/src/components/MediaLibrary/MediaLibraryModal.js @@ -17,6 +17,7 @@ import { colors } from 'netlify-cms-ui-default'; * widths per breakpoint. */ const cardWidth = `280px`; +const cardHeight = `240px`; const cardMargin = `10px`; /** @@ -172,6 +173,7 @@ const MediaLibraryModal = ({ paginatingMessage={t('mediaLibrary.mediaLibraryModal.loading')} cardDraftText={t('mediaLibrary.mediaLibraryCard.draft')} cardWidth={cardWidth} + cardHeight={cardHeight} cardMargin={cardMargin} isPrivate={privateUpload} loadDisplayURL={loadDisplayURL} diff --git a/packages/netlify-cms-core/src/components/MediaLibrary/__tests__/MediaLibraryCard.spec.js b/packages/netlify-cms-core/src/components/MediaLibrary/__tests__/MediaLibraryCard.spec.js index cd2f640c..550ddb4b 100644 --- a/packages/netlify-cms-core/src/components/MediaLibrary/__tests__/MediaLibraryCard.spec.js +++ b/packages/netlify-cms-core/src/components/MediaLibrary/__tests__/MediaLibraryCard.spec.js @@ -10,6 +10,7 @@ describe('MediaLibraryCard', () => { onClick: jest.fn(), draftText: 'Draft', width: '100px', + height: '240px', margin: '10px', isViewableImage: true, loadDisplayURL: jest.fn(), diff --git a/packages/netlify-cms-core/src/components/MediaLibrary/__tests__/__snapshots__/MediaLibraryCard.spec.js.snap b/packages/netlify-cms-core/src/components/MediaLibrary/__tests__/__snapshots__/MediaLibraryCard.spec.js.snap index 6b03643c..452ba837 100644 --- a/packages/netlify-cms-core/src/components/MediaLibrary/__tests__/__snapshots__/MediaLibraryCard.spec.js.snap +++ b/packages/netlify-cms-core/src/components/MediaLibrary/__tests__/__snapshots__/MediaLibraryCard.spec.js.snap @@ -52,6 +52,7 @@ exports[`MediaLibraryCard should match snapshot for draft image 1`] = `
@@ -122,6 +123,7 @@ exports[`MediaLibraryCard should match snapshot for non draft image 1`] = `
@@ -188,6 +190,7 @@ exports[`MediaLibraryCard should match snapshot for non viewable image 1`] = `
diff --git a/packages/netlify-cms-lib-util/src/implementation.ts b/packages/netlify-cms-lib-util/src/implementation.ts index 6c65d8b2..739d4b9e 100644 --- a/packages/netlify-cms-lib-util/src/implementation.ts +++ b/packages/netlify-cms-lib-util/src/implementation.ts @@ -4,10 +4,7 @@ import { AsyncLock } from './asyncLock'; export type DisplayURLObject = { id: string; path: string }; -export type DisplayURL = - | DisplayURLObject - | string - | { original: DisplayURL; path?: string; largeMedia?: string }; +export type DisplayURL = DisplayURLObject | string; export interface ImplementationMediaFile { name: string; diff --git a/yarn.lock b/yarn.lock index 69c6afd8..17954caa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6495,11 +6495,6 @@ detect-indent@^5.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= -detect-libc@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= - detect-newline@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" @@ -8089,7 +8084,6 @@ fsevents@^1.2.7: dependencies: bindings "^1.5.0" nan "^2.12.1" - node-pre-gyp "*" fsevents@~2.1.2: version "2.1.2" @@ -9062,7 +9056,7 @@ husky@^3.0.9: run-node "^1.0.0" slash "^3.0.0" -iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: +iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -11261,16 +11255,16 @@ mem@^4.0.0: mimic-fn "^2.0.0" p-is-promise "^2.0.0" +"memoize-one@>=3.1.1 <6", memoize-one@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0" + integrity sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA== + memoize-one@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.1.0.tgz#a2387c58c03fff27ca390c31b764a79addf3f906" integrity sha512-2GApq0yI/b22J2j9rhbrAlsHb0Qcz+7yWxeLG8h+95sl1XPUgeLimQSOdur4Vw7cUhrBHwaUZxWFZueojqNRzA== -memoize-one@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0" - integrity sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA== - memoizerific@^1.11.3: version "1.11.3" resolved "https://registry.yarnpkg.com/memoizerific/-/memoizerific-1.11.3.tgz#7c87a4646444c32d75438570905f2dbd1b1a805a" @@ -11763,15 +11757,6 @@ ncp@^2.0.0: resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" integrity sha1-GVoh1sRuNh0vsSgbo4uR6d9727M= -needle@^2.2.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.3.2.tgz#3342dea100b7160960a450dc8c22160ac712a528" - integrity sha512-DUzITvPVDUy6vczKKYTnWc/pBZ0EnjMJnQ3y+Jo5zfKFimJs7S3HFCxCRZYB9FUZcrzUQr3WsmvZgddMEIZv6w== - dependencies: - debug "^3.2.6" - iconv-lite "^0.4.4" - sax "^1.2.4" - negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" @@ -11943,22 +11928,6 @@ node-polyglot@^2.3.0: string.prototype.trim "^1.1.2" warning "^4.0.3" -node-pre-gyp@*: - version "0.14.0" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz#9a0596533b877289bcad4e143982ca3d904ddc83" - integrity sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA== - dependencies: - detect-libc "^1.0.2" - mkdirp "^0.5.1" - needle "^2.2.1" - nopt "^4.0.1" - npm-packlist "^1.1.6" - npmlog "^4.0.2" - rc "^1.2.7" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^4.4.2" - node-releases@^1.1.29, node-releases@^1.1.50: version "1.1.50" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.50.tgz#803c40d2c45db172d0410e4efec83aa8c6ad0592" @@ -12080,7 +12049,7 @@ npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: semver "^5.6.0" validate-npm-package-name "^3.0.0" -npm-packlist@^1.1.6, npm-packlist@^1.4.4: +npm-packlist@^1.4.4: version "1.4.8" resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== @@ -12127,7 +12096,7 @@ npm-run-path@^4.0.0: dependencies: path-key "^3.0.0" -npmlog@^4.0.2, npmlog@^4.1.2: +npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== @@ -13622,7 +13591,7 @@ rbush@2.0.2: dependencies: quickselect "^1.0.1" -rc@^1.0.1, rc@^1.1.6, rc@^1.2.7: +rc@^1.0.1, rc@^1.1.6: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== @@ -14079,6 +14048,11 @@ react-transition-group@^2.2.1, react-transition-group@^2.6.0: prop-types "^15.6.2" react-lifecycles-compat "^3.0.4" +react-virtualized-auto-sizer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.2.tgz#a61dd4f756458bbf63bd895a92379f9b70f803bd" + integrity sha512-MYXhTY1BZpdJFjUovvYHVBmkq79szK/k7V3MO+36gJkWGkrXKtyr4vCPtpphaTLRAdDNoYEYFZWE8LjN+PIHNg== + react-waypoint@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/react-waypoint/-/react-waypoint-8.1.0.tgz#91d926a2fd1be4cbd0351cb8c3d494fac0ef1699" @@ -14088,6 +14062,14 @@ react-waypoint@^8.1.0: prop-types "^15.0.0" react-is "^16.6.3" +react-window@^1.8.5: + version "1.8.5" + resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.5.tgz#a56b39307e79979721021f5d06a67742ecca52d1" + integrity sha512-HeTwlNa37AFa8MDZFZOKcNEkuF2YflA0hpGPiTT9vR7OawEt+GZbfM6wqkBahD3D3pUjIabQYzsnY/BSJbgq6Q== + dependencies: + "@babel/runtime" "^7.0.0" + memoize-one ">=3.1.1 <6" + react@^16.12.0, react@^16.8.3, react@^16.8.4: version "16.13.0" resolved "https://registry.yarnpkg.com/react/-/react-16.13.0.tgz#d046eabcdf64e457bbeed1e792e235e1b9934cf7" @@ -14816,7 +14798,7 @@ rimraf@2.6.3: dependencies: glob "^7.1.3" -rimraf@^2.2.8, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@^2.7.1: +rimraf@^2.2.8, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@^2.7.1: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -15000,7 +14982,7 @@ semver-diff@^2.0.0: dependencies: semver "^5.0.3" -"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.4.1, semver@^5.5, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -16184,7 +16166,7 @@ tapable@^1.0.0, tapable@^1.1.3: resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== -tar@^4.4.10, tar@^4.4.12, tar@^4.4.2, tar@^4.4.8: +tar@^4.4.10, tar@^4.4.12, tar@^4.4.8: version "4.4.13" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==