PageGrid chunks; Image lazy removed (unstable in Safari)
This commit is contained in:
1 parent
23b4b35395
commit
b0b3f98233
3 files changed
+72
-94
No files matched your search
@@ -1,5 +1,5 @@
|
|||||||
import './styles.css'
|
import './styles.css'
|
||||||
import { memo, useRef, useState, useEffect, useCallback } from 'react'
|
import { memo, useRef, useEffect, useCallback } from 'react'
|
||||||
|
|
||||||
export type ImageProps = {
|
export type ImageProps = {
|
||||||
style?: React.CSSProperties
|
style?: React.CSSProperties
|
||||||
@@ -9,7 +9,6 @@ export type ImageProps = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const Image: React.FC<ImageProps> = memo(props => {
|
export const Image: React.FC<ImageProps> = memo(props => {
|
||||||
const [imageSrc, setImageSrc] = useState<[string, string]>([null, null])
|
|
||||||
const ref = useRef<HTMLImageElement>()
|
const ref = useRef<HTMLImageElement>()
|
||||||
const onLoad = useCallback((event) => {
|
const onLoad = useCallback((event) => {
|
||||||
event.target.classList.add('loaded')
|
event.target.classList.add('loaded')
|
||||||
@@ -22,43 +21,6 @@ export const Image: React.FC<ImageProps> = memo(props => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
let observer: IntersectionObserver
|
|
||||||
let didCancel = false
|
|
||||||
|
|
||||||
if (ref.current && imageSrc[0] !== props.src) {
|
|
||||||
if (IntersectionObserver) {
|
|
||||||
observer = new IntersectionObserver(
|
|
||||||
entries => {
|
|
||||||
entries.forEach(entry => {
|
|
||||||
if (
|
|
||||||
!didCancel &&
|
|
||||||
(entry.intersectionRatio > 0 || entry.isIntersecting)
|
|
||||||
) {
|
|
||||||
setImageSrc([props.src, props.srcSet])
|
|
||||||
observer.unobserve(ref.current)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
threshold: 0.01,
|
|
||||||
rootMargin: "75%",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
observer.observe(ref.current)
|
|
||||||
} else {
|
|
||||||
// Old browsers fallback
|
|
||||||
setImageSrc([props.src, props.srcSet])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return () => {
|
|
||||||
didCancel = true
|
|
||||||
if (observer && observer.unobserve) {
|
|
||||||
observer.unobserve(ref.current)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [props.src, props.srcSet, imageSrc, ref])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<img
|
<img
|
||||||
ref={ref}
|
ref={ref}
|
||||||
@@ -66,8 +28,8 @@ export const Image: React.FC<ImageProps> = memo(props => {
|
|||||||
className={'image'}
|
className={'image'}
|
||||||
style={props.style}
|
style={props.style}
|
||||||
alt={props.alt}
|
alt={props.alt}
|
||||||
src={imageSrc[0]}
|
src={props.src}
|
||||||
srcSet={imageSrc[1]}
|
srcSet={props.srcSet}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -1,12 +1,3 @@
|
|||||||
@keyframes loaded {
|
|
||||||
0% {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.image {
|
.image {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: block;
|
display: block;
|
||||||
@@ -17,6 +8,5 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.image.loaded {
|
.image.loaded {
|
||||||
animation: loaded 250ms ease-in-out;
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
@@ -4,58 +4,84 @@ import { Card, CardLayout } from '../Card'
|
|||||||
import { Date } from './Date'
|
import { Date } from './Date'
|
||||||
import { Block } from '../Block'
|
import { Block } from '../Block'
|
||||||
import { Spacer } from '../Spacer'
|
import { Spacer } from '../Spacer'
|
||||||
import { imageSrcSet } from 'src/lib/image'
|
import { imageSrcSet, imageSrc } from 'src/lib/image'
|
||||||
|
import { Button } from '../Button'
|
||||||
|
import { useState, useCallback } from 'react'
|
||||||
|
|
||||||
export type PageGridProps = {
|
export type PageGridProps = {
|
||||||
items: PageCardData[]
|
items: PageCardData[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PageGrid: React.FC<PageGridProps> = props => {
|
export const PageGrid: React.FC<PageGridProps> = props => {
|
||||||
|
const [page, setPage] = useState(1)
|
||||||
|
const i = page * 24
|
||||||
|
const items = props.items.slice(0, i)
|
||||||
|
const onMore = useCallback(() => {
|
||||||
|
setPage(x => x + 1)
|
||||||
|
}, [])
|
||||||
|
const hasMore = i < props.items.length
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CardGrid
|
<>
|
||||||
style={{
|
<CardGrid
|
||||||
marginBottom: 'var(--size-m)',
|
style={{
|
||||||
}}
|
marginBottom: 'var(--size-m)',
|
||||||
>
|
}}
|
||||||
{props.items.map((item, i) => {
|
>
|
||||||
const gridColumn = item.featured ? 'span 2' : 'auto'
|
{items.map((item, i) => {
|
||||||
const layout: CardLayout = item.featured ? 'featured' : 'simple'
|
const gridColumn = item.featured ? 'span 2' : 'auto'
|
||||||
const src = item.coverSrc ?? process.env.APP_CARD_DEFAULT_IMAGE
|
const layout: CardLayout = item.featured ? 'featured' : 'simple'
|
||||||
const srcSet = imageSrcSet(src, [ImageSize.large, ImageSize.medium, ImageSize.small])
|
const cover = item.coverSrc ?? process.env.APP_CARD_DEFAULT_IMAGE
|
||||||
|
const src = cover
|
||||||
|
const srcSet = imageSrcSet(src, [ImageSize.large, ImageSize.medium, ImageSize.small])
|
||||||
|
|
||||||
const content = !item.featured
|
const content = !item.featured
|
||||||
? (
|
? (
|
||||||
<Block
|
<Block
|
||||||
direction={'vertical'}
|
direction={'vertical'}
|
||||||
|
>
|
||||||
|
{item.title}
|
||||||
|
<Spacer />
|
||||||
|
<Date style={{
|
||||||
|
marginTop: 'var(--size-m)',
|
||||||
|
}}>{item.date}</Date>
|
||||||
|
</Block>
|
||||||
|
) : (
|
||||||
|
item.title
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
key={i}
|
||||||
|
href={item.url}
|
||||||
|
img={{
|
||||||
|
alt: item.title,
|
||||||
|
src,
|
||||||
|
srcSet,
|
||||||
|
}}
|
||||||
|
layout={layout}
|
||||||
|
style={{
|
||||||
|
gridColumn,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{item.title}
|
{content}
|
||||||
<Spacer />
|
</Card>
|
||||||
<Date style={{
|
|
||||||
marginTop: 'var(--size-m)',
|
|
||||||
}}>{item.date}</Date>
|
|
||||||
</Block>
|
|
||||||
) : (
|
|
||||||
item.title
|
|
||||||
)
|
)
|
||||||
|
})}
|
||||||
return (
|
</CardGrid>
|
||||||
<Card
|
<Block direction={'horizontal'} style={{
|
||||||
key={i}
|
marginTop: 'var(--size-l)'
|
||||||
href={item.url}
|
}}>
|
||||||
img={{
|
<Spacer />
|
||||||
alt: item.title,
|
<Button
|
||||||
src,
|
disabled={!hasMore}
|
||||||
srcSet,
|
onClick={onMore}
|
||||||
}}
|
style={{
|
||||||
layout={layout}
|
flex: 2,
|
||||||
style={{
|
}}
|
||||||
gridColumn,
|
>Загрузить ещё</Button>
|
||||||
}}
|
<Spacer />
|
||||||
>
|
</Block>
|
||||||
{content}
|
</>
|
||||||
</Card>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</CardGrid>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user