From 94c656fe0f92e97bed7fc332490e9e13379c8745 Mon Sep 17 00:00:00 2001 From: muerwre <33246675+muerwre@users.noreply.github.com> Date: Fri, 8 Oct 2021 11:33:53 +0700 Subject: [PATCH] refactored flow cells, added colors for lab (#78) * made better flow cells * made cubical desaturation * made colorfull lab nodes * colorful lab nodes for all text ones * all lab nodes are colorful * disabled lazy loading on heroes * fixed color calculation hook * fixed lab color gradients calculation * fixed cell text on flow --- .env.development | 4 +- package.json | 1 + src/components/flow/CellShade/index.tsx | 13 ++- .../flow/CellShade/styles.module.scss | 15 +-- src/components/flow/FlowCell/index.tsx | 50 ++++++++++ .../flow/FlowCell/styles.module.scss | 97 +++++++++++++++++++ src/components/flow/FlowCellImage/index.tsx | 18 ++++ .../flow/FlowCellImage/styles.module.scss | 15 +++ src/components/flow/FlowCellText/index.tsx | 23 +++++ .../flow/FlowCellText/styles.module.scss | 11 +++ src/components/flow/FlowGrid/index.tsx | 31 +++--- .../flow/FlowGrid/styles.module.scss | 19 ++++ src/components/flow/FlowSwiperHero/index.tsx | 5 +- src/components/lab/LabImage/index.tsx | 2 - .../lab/LabImage/styles.module.scss | 2 - src/components/lab/LabNode/index.tsx | 5 +- .../node/NodeImageSwiperBlock/index.tsx | 3 +- src/components/node/NodeRelatedItem/index.tsx | 13 +-- .../node/NodeRelatedItem/styles.module.scss | 5 - src/redux/types.ts | 3 +- src/styles/_global.scss | 4 + src/styles/common/markdown.module.scss | 2 +- src/styles/variables.scss | 6 +- src/utils/color.ts | 18 ++-- src/utils/dom.ts | 6 +- src/utils/hooks/useColorFromString.ts | 10 ++ src/utils/hooks/useColorGradientFromString.ts | 17 ++++ src/utils/types.ts | 5 + yarn.lock | 5 + 29 files changed, 345 insertions(+), 63 deletions(-) create mode 100644 src/components/flow/FlowCell/index.tsx create mode 100644 src/components/flow/FlowCell/styles.module.scss create mode 100644 src/components/flow/FlowCellImage/index.tsx create mode 100644 src/components/flow/FlowCellImage/styles.module.scss create mode 100644 src/components/flow/FlowCellText/index.tsx create mode 100644 src/components/flow/FlowCellText/styles.module.scss create mode 100644 src/utils/hooks/useColorFromString.ts create mode 100644 src/utils/hooks/useColorGradientFromString.ts diff --git a/.env.development b/.env.development index cf60c666..589f28a1 100644 --- a/.env.development +++ b/.env.development @@ -1,3 +1,3 @@ #REACT_APP_API_HOST=http://localhost:3334/ -REACT_APP_API_HOST=https://pig.staging.vault48.org/ -REACT_APP_REMOTE_CURRENT=https://pig.staging.vault48.org/static/ +REACT_APP_API_HOST=https://pig.vault48.org/ +REACT_APP_REMOTE_CURRENT=https://pig.vault48.org/static/ diff --git a/package.json b/package.json index 1b5a380e..2d802faa 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "react": "^17.0.1", "react-dom": "^17.0.1", "react-dropzone": "^11.4.2", + "react-lazyload": "^3.2.0", "react-masonry-css": "^1.0.16", "react-popper": "^2.2.3", "react-redux": "^7.2.2", diff --git a/src/components/flow/CellShade/index.tsx b/src/components/flow/CellShade/index.tsx index 7ba39f25..b97a8471 100644 --- a/src/components/flow/CellShade/index.tsx +++ b/src/components/flow/CellShade/index.tsx @@ -1,21 +1,26 @@ import React, { FC, useMemo } from 'react'; import styles from './styles.module.scss'; import { DEFAULT_DOMINANT_COLOR } from '~/constants/node'; -import { convertHexToRGBA } from '~/utils/color'; import { DivProps } from '~/utils/types'; import classNames from 'classnames'; +import { transparentize } from 'color2k'; +import { normalizeBrightColor } from '~/utils/color'; interface Props extends DivProps { color?: string; + size?: number; } -const CellShade: FC = ({ color, ...rest }) => { +const CellShade: FC = ({ color, size = 50, ...rest }) => { const background = useMemo(() => { - if (!color || color === DEFAULT_DOMINANT_COLOR) { + const normalized = normalizeBrightColor(color); + + if (!color || color === DEFAULT_DOMINANT_COLOR || !normalized) { return undefined; } - return `linear-gradient(7deg, ${color} 50px, ${convertHexToRGBA(color, 0.3)} 250px)`; + return `linear-gradient(7deg, ${normalized} ${size}px, ${transparentize(normalized, 1)} ${size * + 5}px)`; }, [color]); return ( diff --git a/src/components/flow/CellShade/styles.module.scss b/src/components/flow/CellShade/styles.module.scss index ce9f167d..054a6051 100644 --- a/src/components/flow/CellShade/styles.module.scss +++ b/src/components/flow/CellShade/styles.module.scss @@ -1,16 +1,17 @@ @import "~/styles/variables"; .shade { - position: absolute; - bottom: 0; - left: 0; - right: 0; - top: 0; background: linear-gradient(7deg, transparentize($content_bg, 0.05) 30px, transparentize($content_bg, 1) 250px); pointer-events: none; touch-action: none; - @include tablet { - opacity: 0.7; + &.black::after { + content: ' '; + position: absolute; + top: 10px; + right: 10px; + width: 10px; + height: 10px; + background-color: blue; } } diff --git a/src/components/flow/FlowCell/index.tsx b/src/components/flow/FlowCell/index.tsx new file mode 100644 index 00000000..afc65ed1 --- /dev/null +++ b/src/components/flow/FlowCell/index.tsx @@ -0,0 +1,50 @@ +import React, { FC } from 'react'; +import styles from './styles.module.scss'; +import { NavLink } from 'react-router-dom'; +import { CellShade } from '~/components/flow/CellShade'; +import { FlowCellImage } from '~/components/flow/FlowCellImage'; +import { FlowDisplayVariant } from '~/redux/types'; +import { FlowCellText } from '~/components/flow/FlowCellText'; +import classNames from 'classnames'; + +interface Props { + to: string; + title: string; + image?: string; + color?: string; + text?: string; + display?: FlowDisplayVariant; +} + +const FlowCell: FC = ({ color, to, image, display = 'single', text, title }) => { + const withText = ((!!display && display !== 'single') || !image) && !!text; + + return ( + + {withText && ( + {title}}> + {text!} + + )} + + {image && ( + + )} + + + + {!withText && ( +
+

{title}

+
+ )} +
+ ); +}; + +export { FlowCell }; diff --git a/src/components/flow/FlowCell/styles.module.scss b/src/components/flow/FlowCell/styles.module.scss new file mode 100644 index 00000000..5c47fc92 --- /dev/null +++ b/src/components/flow/FlowCell/styles.module.scss @@ -0,0 +1,97 @@ +@import "~/styles/variables"; + +.cell { + @include inner_shadow; + + position: relative; + overflow: hidden; + border-radius: $radius; + display: flex; + width: 100%; + height: 100%; + background: $content_bg; + flex-direction: row; + color: inherit; + text-decoration: inherit; + font: inherit; + line-height: inherit; + + &.vertical { + flex-direction: column-reverse; + } +} + +.thumb { + @include outer_shadow; + + border-radius: $radius; + overflow: hidden; + position: relative; + z-index: 0; +} + +.shade { + @include outer_shadow; + + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 2; +} + +.text { + position: absolute; + bottom: 5px; + left: 5px; + z-index: 1; + overflow: hidden; + border-radius: $radius; + max-height: calc(100% - 10px); + max-width: calc(100% - 10px); + box-sizing: border-box; + font: $font_16_regular; + + @include tablet { + font: $font_14_regular; + left: 5px; + bottom: 5px; + } + + & :global(.grey) { + color: inherit; + opacity: 0.5; + } + + .quadro &, + .horizontal & { + max-width: calc(50% - 15px); + @include blur(transparentize($content_bg, 0), 10px, 0.5) + } + + .quadro &, + .vertical & { + max-height: calc(50% - 15px); + @include blur(transparentize($content_bg, 0), 10px, 0.5) + } +} + +.title_wrapper { + bottom: 0; + left: 0; + width: 100%; + z-index: 10; + padding: $gap; + position: absolute; +} + +.title { + font: $font_cell_title; + text-transform: uppercase; + word-break: break-word; + + @include tablet { + font: $font_18_semibold; + } +} diff --git a/src/components/flow/FlowCellImage/index.tsx b/src/components/flow/FlowCellImage/index.tsx new file mode 100644 index 00000000..b1b07334 --- /dev/null +++ b/src/components/flow/FlowCellImage/index.tsx @@ -0,0 +1,18 @@ +import React, { FC } from 'react'; +import LazyLoad from 'react-lazyload'; +import { IMGProps } from '~/utils/types'; +import styles from './styles.module.scss'; +import classNames from 'classnames'; + +interface Props extends IMGProps { + height?: number; +} + +const FlowCellImage: FC = ({ className, children, ...rest }) => ( + + + {children} + +); + +export { FlowCellImage }; diff --git a/src/components/flow/FlowCellImage/styles.module.scss b/src/components/flow/FlowCellImage/styles.module.scss new file mode 100644 index 00000000..61ac2651 --- /dev/null +++ b/src/components/flow/FlowCellImage/styles.module.scss @@ -0,0 +1,15 @@ +.wrapper { + width: 100%; + height: 100%; + position: relative; + + img { + position: absolute; + top: 50%; + left: 50%; + min-width: 100%; + min-height: 100%; + transform: translate(-50%, -50%); + object-fit: cover; + } +} diff --git a/src/components/flow/FlowCellText/index.tsx b/src/components/flow/FlowCellText/index.tsx new file mode 100644 index 00000000..26b6ffd1 --- /dev/null +++ b/src/components/flow/FlowCellText/index.tsx @@ -0,0 +1,23 @@ +import React, { FC, ReactElement } from 'react'; +import { Markdown } from '~/components/containers/Markdown'; +import { DivProps } from '~/utils/types'; +import classNames from 'classnames'; +import styles from './styles.module.scss'; +import { formatText } from '~/utils/dom'; + +interface Props extends DivProps { + children: string; + heading: string | ReactElement; +} + +const FlowCellText: FC = ({ children, heading, ...rest }) => ( +
+ {heading &&
{heading}
} + +
+); + +export { FlowCellText }; diff --git a/src/components/flow/FlowCellText/styles.module.scss b/src/components/flow/FlowCellText/styles.module.scss new file mode 100644 index 00000000..b120a797 --- /dev/null +++ b/src/components/flow/FlowCellText/styles.module.scss @@ -0,0 +1,11 @@ +@import "~/styles/variables"; + +.text { + padding: $gap; + line-height: 1.3em; +} + +.heading { + margin-bottom: 0.4em; +} + diff --git a/src/components/flow/FlowGrid/index.tsx b/src/components/flow/FlowGrid/index.tsx index 17e2c700..5eaf4be9 100644 --- a/src/components/flow/FlowGrid/index.tsx +++ b/src/components/flow/FlowGrid/index.tsx @@ -1,12 +1,13 @@ -import React, { FC, Fragment, useCallback } from 'react'; -import { Cell } from '~/components/flow/Cell'; +import React, { FC, Fragment } from 'react'; import { IFlowState } from '~/redux/flow/reducer'; import { INode } from '~/redux/types'; -import { canEditNode } from '~/utils/node'; import { IUser } from '~/redux/auth/types'; -import { useHistory } from 'react-router'; -import { URLS } from '~/constants/urls'; +import { PRESETS, URLS } from '~/constants/urls'; +import { FlowCell } from '~/components/flow/FlowCell'; +import classNames from 'classnames'; +import styles from './styles.module.scss'; +import { getURLFromString } from '~/utils/dom'; type IProps = Partial & { user: Partial; @@ -14,9 +15,6 @@ type IProps = Partial & { }; export const FlowGrid: FC = ({ user, nodes, onChangeCellView }) => { - const history = useHistory(); - const onSelect = useCallback((id: INode['id']) => history.push(URLS.NODE_URL(id)), [history]); - if (!nodes) { return null; } @@ -24,13 +22,16 @@ export const FlowGrid: FC = ({ user, nodes, onChangeCellView }) => { return ( {nodes.map(node => ( - +
+ +
))}
); diff --git a/src/components/flow/FlowGrid/styles.module.scss b/src/components/flow/FlowGrid/styles.module.scss index e69de29b..c3a20fe0 100644 --- a/src/components/flow/FlowGrid/styles.module.scss +++ b/src/components/flow/FlowGrid/styles.module.scss @@ -0,0 +1,19 @@ +@import "~/styles/variables"; + +@mixin mobile { + @media (max-width: $cell * 2) { + @content; + } +} + +.cell { + &.horizontal, + &.quadro { + grid-column-end: span 2; + } + + &.vertical, + &.quadro { + grid-row-end: span 2; + } +} diff --git a/src/components/flow/FlowSwiperHero/index.tsx b/src/components/flow/FlowSwiperHero/index.tsx index 78ffae59..6696544b 100644 --- a/src/components/flow/FlowSwiperHero/index.tsx +++ b/src/components/flow/FlowSwiperHero/index.tsx @@ -100,7 +100,8 @@ export const FlowSwiperHero: FC = ({ heroes }) => { speed={3000} className={styles.swiper} lazy={{ - loadPrevNextAmount: 3, + loadPrevNextAmount: 5, + checkInView: false, }} loop slidesPerView={1} @@ -122,7 +123,7 @@ export const FlowSwiperHero: FC = ({ heroes }) => { .map(node => ( diff --git a/src/components/lab/LabImage/index.tsx b/src/components/lab/LabImage/index.tsx index 9f92e020..f6ad69ee 100644 --- a/src/components/lab/LabImage/index.tsx +++ b/src/components/lab/LabImage/index.tsx @@ -23,7 +23,6 @@ interface IProps extends INodeComponentProps {} const breakpoints: SwiperOptions['breakpoints'] = { 599: { - spaceBetween: 20, navigation: true, }, }; @@ -64,7 +63,6 @@ const LabImage: FC = ({ node, isLoading }) => { initialSlide={0} slidesPerView={images.length > 1 ? 1.1 : 1} onSwiper={setControlledSwiper} - spaceBetween={10} grabCursor autoHeight breakpoints={breakpoints} diff --git a/src/components/lab/LabImage/styles.module.scss b/src/components/lab/LabImage/styles.module.scss index 308253be..876d4da6 100644 --- a/src/components/lab/LabImage/styles.module.scss +++ b/src/components/lab/LabImage/styles.module.scss @@ -28,7 +28,6 @@ text-transform: uppercase; font: $font_32_bold; display: flex; - border-radius: $radius; align-items: center; justify-content: center; width: auto; @@ -51,7 +50,6 @@ .image { max-height: calc(100vh - 70px - 70px); max-width: 100%; - border-radius: $radius; transition: box-shadow 1s; box-shadow: transparentize(black, 0.7) 0 3px 5px; diff --git a/src/components/lab/LabNode/index.tsx b/src/components/lab/LabNode/index.tsx index 729dd88e..83b6da31 100644 --- a/src/components/lab/LabNode/index.tsx +++ b/src/components/lab/LabNode/index.tsx @@ -5,6 +5,7 @@ import styles from './styles.module.scss'; import { LabBottomPanel } from '~/components/lab/LabBottomPanel'; import { isAfter, parseISO } from 'date-fns'; import classNames from 'classnames'; +import { useColorGradientFromString } from '~/utils/hooks/useColorGradientFromString'; interface IProps { node: INode; @@ -22,8 +23,10 @@ const LabNode: FC = ({ node, isLoading, lastSeen, commentCount }) => { [node.commented_at, lastSeen] ); + const background = useColorGradientFromString(node.title, 3, 2); + return ( -
+
{lab} = ({ node }) => { onLoad={updateSwiper} onClick={() => onOpenPhotoSwipe(i)} className={styles.image} - color={file?.metadata?.dominant_color} + color={normalizeBrightColor(file?.metadata?.dominant_color)} /> ))} diff --git a/src/components/node/NodeRelatedItem/index.tsx b/src/components/node/NodeRelatedItem/index.tsx index 18d3b5f1..2428ebfe 100644 --- a/src/components/node/NodeRelatedItem/index.tsx +++ b/src/components/node/NodeRelatedItem/index.tsx @@ -6,6 +6,9 @@ import { PRESETS, URLS } from '~/constants/urls'; import { RouteComponentProps, withRouter } from 'react-router'; import { getURL, stringToColour } from '~/utils/dom'; import { Avatar } from '~/components/common/Avatar'; +import { normalizeBrightColor } from '~/utils/color'; +import { adjustHue } from 'color2k'; +import { useColorGradientFromString } from '~/utils/hooks/useColorGradientFromString'; type IProps = RouteComponentProps & { item: Partial; @@ -37,10 +40,8 @@ const NodeRelatedItemUnconnected: FC = memo(({ item, history }) => { () => (item.thumbnail ? getURL({ url: item.thumbnail }, PRESETS.avatar) : ''), [item] ); - const backgroundColor = useMemo( - () => (!thumb && item.title && stringToColour(item.title)) || '', - [] - ); + + const background = useColorGradientFromString(!thumb ? item.title : ''); useEffect(() => { if (!ref.current) return; @@ -76,13 +77,13 @@ const NodeRelatedItemUnconnected: FC = memo(({ item, history }) => { /> {!item.thumbnail && size === 'small' && ( -
+
{getTitleLetters(item.title)}
)} {!item.thumbnail && size !== 'small' && ( -
+
{item.title}
)} diff --git a/src/components/node/NodeRelatedItem/styles.module.scss b/src/components/node/NodeRelatedItem/styles.module.scss index 8bd6b8d2..2424d5a4 100644 --- a/src/components/node/NodeRelatedItem/styles.module.scss +++ b/src/components/node/NodeRelatedItem/styles.module.scss @@ -43,11 +43,6 @@ div.thumb { font: $font_24_semibold; color: transparentize(white, 0.5); border-radius: $cell_radius; - background-image: linear-gradient( - 180deg, - transparentize($content_bg, 0.4), - transparentize($content_bg, 0.4) - ); } .title { diff --git a/src/redux/types.ts b/src/redux/types.ts index 9c45d24a..0d4306fa 100644 --- a/src/redux/types.ts +++ b/src/redux/types.ts @@ -111,6 +111,7 @@ export interface IBlockEmbed { } export type IBlock = IBlockText | IBlockEmbed; +export type FlowDisplayVariant = 'single' | 'vertical' | 'horizontal' | 'quadro'; export interface INode { id?: number; @@ -132,7 +133,7 @@ export interface INode { like_count?: number; flow: { - display: 'single' | 'vertical' | 'horizontal' | 'quadro'; + display: FlowDisplayVariant; show_description: boolean; dominant_color?: string; }; diff --git a/src/styles/_global.scss b/src/styles/_global.scss index ae283a3b..3223e55c 100644 --- a/src/styles/_global.scss +++ b/src/styles/_global.scss @@ -27,6 +27,10 @@ body { background-size: 600px 600px; pointer-events: none; } + + * { + box-sizing: border-box; + } } #app { diff --git a/src/styles/common/markdown.module.scss b/src/styles/common/markdown.module.scss index 82cb22a5..875b3de3 100644 --- a/src/styles/common/markdown.module.scss +++ b/src/styles/common/markdown.module.scss @@ -112,7 +112,7 @@ $margin: 1em; } :global(.grey) { - color: #555555; + color: #666666; white-space: pre-line; } } diff --git a/src/styles/variables.scss b/src/styles/variables.scss index baf86e5e..5b895b79 100644 --- a/src/styles/variables.scss +++ b/src/styles/variables.scss @@ -194,13 +194,13 @@ $sidebar_border: transparentize(white, 0.95); } } -@mixin blur($color: $content_bg, $radius: 15px) { - background: transparentize($color, 0.1); +@mixin blur($color: $content_bg, $radius: 15px, $opacity: 0.5) { + background: transparentize($color, $opacity / 2); @include can_backdrop { backdrop-filter: blur($radius); -webkit-backdrop-filter: blur($radius); - background: transparentize($color, 0.5); + background: transparentize($color, $opacity); } } diff --git a/src/utils/color.ts b/src/utils/color.ts index 07b136e0..eb05c27e 100644 --- a/src/utils/color.ts +++ b/src/utils/color.ts @@ -1,13 +1,15 @@ -export const convertHexToRGBA = (hexCode, opacity) => { - let hex = hexCode.replace('#', ''); +import { darken, desaturate, parseToHsla } from 'color2k'; +import { DEFAULT_DOMINANT_COLOR } from '~/constants/node'; - if (hex.length === 3) { - hex = `${hex[0]}${hex[0]}${hex[1]}${hex[1]}${hex[2]}${hex[2]}`; +export const normalizeBrightColor = (color?: string, saturationExp = 3, lightnessExp = 3) => { + if (!color) { + return ''; } - const r = parseInt(hex.substring(0, 2), 16); - const g = parseInt(hex.substring(2, 4), 16); - const b = parseInt(hex.substring(4, 6), 16); + const hsla = parseToHsla(color || DEFAULT_DOMINANT_COLOR); + const saturation = hsla[1]; + const lightness = hsla[2]; - return `rgba(${r},${g},${b},${opacity})`; + const desaturated = desaturate(color, saturation ** saturationExp); + return darken(desaturated, lightness ** lightnessExp); }; diff --git a/src/utils/dom.ts b/src/utils/dom.ts index eae9655f..3b7aedc4 100644 --- a/src/utils/dom.ts +++ b/src/utils/dom.ts @@ -76,17 +76,17 @@ export const describeArc = ( }; export const getURLFromString = ( - url: string, + url?: string, size?: typeof PRESETS[keyof typeof PRESETS] ): string => { if (size) { - return url.replace( + return (url || '').replace( 'REMOTE_CURRENT://', `${process.env.REACT_APP_REMOTE_CURRENT}cache/${size}/` ); } - return url.replace('REMOTE_CURRENT://', process.env.REACT_APP_REMOTE_CURRENT); + return (url || '').replace('REMOTE_CURRENT://', process.env.REACT_APP_REMOTE_CURRENT); }; export const getURL = ( diff --git a/src/utils/hooks/useColorFromString.ts b/src/utils/hooks/useColorFromString.ts new file mode 100644 index 00000000..90208506 --- /dev/null +++ b/src/utils/hooks/useColorFromString.ts @@ -0,0 +1,10 @@ +import { useMemo } from 'react'; +import { normalizeBrightColor } from '~/utils/color'; +import { stringToColour } from '~/utils/dom'; + +export const useColorFromString = (val?: string, saturation = 3, lightness = 3) => { + return useMemo( + () => (val && normalizeBrightColor(stringToColour(val), saturation, lightness)) || '', + [] + ); +}; diff --git a/src/utils/hooks/useColorGradientFromString.ts b/src/utils/hooks/useColorGradientFromString.ts new file mode 100644 index 00000000..a3a57720 --- /dev/null +++ b/src/utils/hooks/useColorGradientFromString.ts @@ -0,0 +1,17 @@ +import { useMemo } from 'react'; +import { adjustHue } from 'color2k'; +import { normalizeBrightColor } from '~/utils/color'; +import { stringToColour } from '~/utils/dom'; + +export const useColorGradientFromString = (val?: string, saturation = 3, lightness = 3) => + useMemo(() => { + if (!val) { + return ''; + } + + const color = normalizeBrightColor(stringToColour(val), saturation, lightness); + const second = normalizeBrightColor(adjustHue(color, 45), saturation, lightness); + const third = normalizeBrightColor(adjustHue(color, 90), saturation, lightness); + + return `linear-gradient(155deg, ${color}, ${second}, ${third})`; + }, [val]); diff --git a/src/utils/types.ts b/src/utils/types.ts index a7d786e1..4698ec0e 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -6,3 +6,8 @@ export type DivProps = React.DetailedHTMLProps< >; export type SVGProps = React.SVGProps; + +export type IMGProps = React.DetailedHTMLProps< + React.ImgHTMLAttributes, + HTMLImageElement +>; diff --git a/yarn.lock b/yarn.lock index 31dac5f2..1caf0701 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9417,6 +9417,11 @@ react-is@^17.0.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339" integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA== +react-lazyload@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/react-lazyload/-/react-lazyload-3.2.0.tgz#497bd06a6dbd7015e3376e1137a67dc47d2dd021" + integrity sha512-zJlrG8QyVZz4+xkYZH5v1w3YaP5wEFaYSUWC4CT9UXfK75IfRAIEdnyIUF+dXr3kX2MOtL1lUaZmaQZqrETwgw== + react-masonry-css@^1.0.16: version "1.0.16" resolved "https://registry.yarnpkg.com/react-masonry-css/-/react-masonry-css-1.0.16.tgz#72b28b4ae3484e250534700860597553a10f1a2c"