Skip to content

Commit

Permalink
aspect ratio on grid
Browse files Browse the repository at this point in the history
  • Loading branch information
giannif committed Jan 22, 2025
1 parent 24cedc5 commit c9621bd
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 49 deletions.
7 changes: 2 additions & 5 deletions packages/react-components/src/components/gif.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type GifProps = {
gif: IGif
width: number
percentWidth?: string
percentHeight?: string
height?: number
backgroundColor?: string
className?: string
Expand Down Expand Up @@ -92,6 +93,7 @@ const Gif = ({
gif: { bottle_data: bottleData = {} },
width,
percentWidth,
percentHeight,
height: forcedHeight,
onGifRightClick = noop,
className = '',
Expand Down Expand Up @@ -233,11 +235,6 @@ const Gif = ({
}
}, [])
const height = forcedHeight || getGifHeight(gif, width)
let percentHeight: string | undefined
if (!forcedHeight && percentWidth) {
const ratio = Math.round((height / width) * 100)
percentHeight = `${ratio}%`
}
const bestRendition = getBestRendition(gif.images, width, height)
if (!bestRendition) {
if (gif.images) {
Expand Down
39 changes: 24 additions & 15 deletions packages/react-components/src/components/grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import { debounce } from 'throttle-debounce'
import Observer from '../util/observer'
import FetchError from './fetch-error'
import Gif, { EventProps } from './gif'
import DotsLoader from './loader'
import MasonryGrid from './masonry-grid'
import MasonryGrid, { fillArray } from './masonry-grid'
import PingbackContextManager from './pingback-context-manager'
import type { GifOverlayProps } from './types'

Expand Down Expand Up @@ -163,19 +162,30 @@ class Grid extends PureComponent<Props, State> {
loaderConfig,
tabIndex = 0,
layoutType = 'GRID',
loader: LoaderVisual = DotsLoader,
fetchPriority,
} = this.props
const { gifWidth, gifs, isError, isDoneFetching } = this.state
const showLoader = !isDoneFetching
const isFirstLoad = gifs.length === 0
// get the height of each grid item
const itemHeights = gifs.map((gif) => getGifHeight(gif, gifWidth))
const gifPercentWidth = percentWidth ? `${(gifWidth / width) * 100}%` : undefined
const totalHeights: number[] = fillArray(columns, columnOffsets)
const itemHeights = gifs.map((gif) => getGifHeight(gif, gifWidth))
gifs.forEach((_, index: number) => {
const columnTarget = totalHeights.indexOf(Math.min(...totalHeights))
const height = itemHeights[index]
if (height) {
totalHeights[columnTarget] += height + gutter
}
})
const totalHeight = Math.round(Math.max(...totalHeights) - gutter)
const percentHeights = itemHeights.map((h) => `${(h / totalHeight) * 100}%`)
return (
<PingbackContextManager attributes={{ layout_type: layoutType }}>
<div className={className} style={{ width: percentWidth || width }}>
<MasonryGrid
totalWidth={width}
totalHeight={totalHeight}
itemHeights={itemHeights}
useTransform={useTransform}
itemWidth={gifWidth}
Expand All @@ -192,6 +202,7 @@ class Grid extends PureComponent<Props, State> {
width={gifWidth}
height={percentWidth ? itemHeights[index] : undefined}
percentWidth={gifPercentWidth}
percentHeight={percentHeights[index]}
onGifClick={onGifClick}
onGifKeyPress={onGifKeyPress}
onGifSeen={onGifSeen}
Expand All @@ -208,18 +219,16 @@ class Grid extends PureComponent<Props, State> {
))}
</MasonryGrid>
{!showLoader && gifs.length === 0 && noResultsMessage}
{isError ? (
<FetchError onClick={this.onFetch} />
) : (
showLoader && (
<Observer onVisibleChange={this.onLoaderVisible} config={loaderConfig}>
<Loader $isFirstLoad={isFirstLoad}>
<LoaderVisual className={Grid.loaderClassName} />
</Loader>
</Observer>
)
)}
</div>
{isError ? (
<FetchError onClick={this.onFetch} />
) : (
showLoader && (
<Observer onVisibleChange={this.onLoaderVisible} config={loaderConfig}>
<Loader $isFirstLoad={isFirstLoad}></Loader>
</Observer>
)
)}
</PingbackContextManager>
)
}
Expand Down
28 changes: 12 additions & 16 deletions packages/react-components/src/components/masonry-grid.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React, { CSSProperties, memo, ReactNode } from 'react'

function fillArray(length: number, columnOffsets: number[] = []) {
export function fillArray(length: number, columnOffsets: number[] = []) {
return Array.apply(null, Array(length)).map((_, index) => columnOffsets[index] || 0)
}

type Props = {
totalHeight: number
totalWidth: number
columns: number
gutter: number
percentWidth?: string
Expand All @@ -15,6 +17,8 @@ type Props = {
columnOffsets?: number[]
}
const MasonryGrid = ({
totalHeight,
totalWidth,
columns,
gutter,
itemWidth,
Expand All @@ -23,22 +27,15 @@ const MasonryGrid = ({
columnOffsets = [],
percentWidth,
}: Props) => {
const containerStyle: CSSProperties = {}
const containerStyle: CSSProperties = {
position: 'relative',
}
function getChildren() {
const totalHeights: number[] = fillArray(columns, columnOffsets)
const totalWidth = columns * itemWidth + (columns - 1) * gutter
React.Children.forEach(children, (_, index: number) => {
const columnTarget = totalHeights.indexOf(Math.min(...totalHeights))
const height = itemHeights[index]
if (height) {
totalHeights[columnTarget] += height + gutter
}
})
const totalHeight = Math.max(...totalHeights) - gutter
const columnHeights: number[] = fillArray(columns, columnOffsets)
const result = React.Children.map(children, (child: React.ReactNode, index: number) => {
const columnTarget = columnHeights.indexOf(Math.min.apply(Math, columnHeights))
const top = `${(columnHeights[columnTarget] / totalHeight) * 100}%`
const columnTarget = columnHeights.indexOf(Math.min(...columnHeights))
const topPerc = (columnHeights[columnTarget] / totalHeight) * 100
const top = `${topPerc}%`
const leftPixelTarget = (itemWidth + gutter) * columnTarget
const left = `${(leftPixelTarget / totalWidth) * 100}%`
const height = itemHeights[index]
Expand All @@ -53,9 +50,8 @@ const MasonryGrid = ({
},
})
})
containerStyle.position = 'relative'
containerStyle.width = percentWidth
containerStyle.height = `${Math.max(...columnHeights) - gutter}px`
containerStyle.aspectRatio = `${totalWidth} / ${totalHeight}`
return result
}

Expand Down
31 changes: 18 additions & 13 deletions packages/react-components/stories/grid.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ const NoResultsContainer = styled.div`

const Overlay = ({ gif, isHovered }: GifOverlayProps) => <OverlayContainer>{isHovered ? gif.id : ''}</OverlayContainer>

type GridProps = Partial<React.ComponentProps<typeof GridComponent>>
type GridProps = Partial<React.ComponentProps<typeof GridComponent>> & { containerSize: number }

const Grid = ({ loader, ...other }: GridProps) => {
const Grid = ({ loader, containerSize, ...other }: GridProps) => {
const [term, setTerm] = useState('always sunny')
const [width, setWidth] = useState(innerWidth)
const onResize = throttle(500, () => setWidth(innerWidth))
Expand Down Expand Up @@ -63,17 +63,19 @@ const Grid = ({ loader, ...other }: GridProps) => {
value={term}
/>
{term && (
<GridComponent
key={term}
width={width}
columns={columns}
gutter={gutter}
noResultsMessage={NoResults}
loader={loader}
fetchGifs={fetchGifs}
overlay={Overlay}
{...other}
/>
<div style={{ width: containerSize, position: 'relative' }}>
<GridComponent
key={term}
width={width}
columns={columns}
noResultsMessage={NoResults}
loader={loader}
gutter={gutter}
fetchGifs={fetchGifs}
overlay={Overlay}
{...other}
/>
</div>
)}
</>
)
Expand Down Expand Up @@ -107,6 +109,9 @@ export const GridStory: Story = {}
export const GridResponsive: Story = {
args: {
percentWidth: '100%',
width: 375,
columns: 2,
containerSize: 500,
},
}

Expand Down

0 comments on commit c9621bd

Please sign in to comment.