Skip to content

Commit c64ce95

Browse files
committed
stack tenor gif results tightly in a cascade grid
1 parent a326807 commit c64ce95

File tree

4 files changed

+201
-57
lines changed

4 files changed

+201
-57
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
.grid {
2+
max-height: 300px;
3+
overflow: auto;
4+
}
5+
6+
.gridColumns {
7+
display: flex;
8+
flex-direction: row;
9+
justify-content: center;
10+
}
11+
12+
.gridColumn {
13+
display: flex;
14+
flex-direction: column;
15+
}
16+
17+
.gridControls {
18+
width: 100%;
19+
display: flex;
20+
justify-content: center;
21+
align-items: center;
22+
}
23+
24+
.gridItem {
25+
width: 85px;
26+
background: var(--palette-background-body);
27+
border-style: none;
28+
padding: 0px;
29+
}
30+
31+
.gridImage {
32+
width: 100%;
33+
height: auto;
34+
}
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import { Localized } from "@fluent/react/compat";
2+
import React, {
3+
FunctionComponent,
4+
useCallback,
5+
useEffect,
6+
useMemo,
7+
useRef,
8+
useState,
9+
} from "react";
10+
11+
import { useCoralContext } from "coral-framework/lib/bootstrap";
12+
import { Button } from "coral-ui/components/v2";
13+
14+
import { GifResult } from "./TenorInput";
15+
16+
import styles from "./TenorGrid.css";
17+
18+
interface GridItemProps {
19+
gif: GifResult;
20+
onSelect: (gif: GifResult) => void;
21+
}
22+
23+
const TenorGridItem: FunctionComponent<GridItemProps> = ({ gif, onSelect }) => {
24+
const onClick = useCallback(() => {
25+
onSelect(gif);
26+
}, [gif, onSelect]);
27+
28+
return (
29+
<button className={styles.gridItem} onClick={onClick}>
30+
<img className={styles.gridImage} alt={gif.title} src={gif.preview}></img>
31+
</button>
32+
);
33+
};
34+
35+
interface GridColumnsProps {
36+
gifs: GifResult[];
37+
onSelectGif: (gif: GifResult) => void;
38+
numColumns: number;
39+
}
40+
41+
const TenorGridColumns: FunctionComponent<GridColumnsProps> = ({
42+
gifs,
43+
onSelectGif,
44+
numColumns,
45+
}) => {
46+
const columns = useMemo(() => {
47+
const resultColumns: GifResult[][] = [];
48+
for (let i = 0; i < numColumns; i++) {
49+
resultColumns.push(new Array<GifResult>());
50+
}
51+
52+
let columnIndex = 0;
53+
// eslint-disable-next-line @typescript-eslint/prefer-for-of
54+
for (let j = 0; j < gifs.length; j++) {
55+
const column = resultColumns[columnIndex];
56+
const gif = gifs[j];
57+
58+
column.push(gif);
59+
60+
columnIndex++;
61+
if (columnIndex >= numColumns) {
62+
columnIndex = 0;
63+
}
64+
}
65+
66+
return resultColumns;
67+
}, [gifs, numColumns]);
68+
69+
return (
70+
<div className={styles.gridColumns}>
71+
{columns.map((colGifs, colIndex) => {
72+
return (
73+
<div
74+
key={`tenor-gif-result-column-${colIndex}`}
75+
className={styles.gridColumn}
76+
>
77+
{colGifs &&
78+
colGifs.map((gif, index) => {
79+
return (
80+
<TenorGridItem
81+
key={`${gif.id}-${index}`}
82+
gif={gif}
83+
onSelect={onSelectGif}
84+
/>
85+
);
86+
})}
87+
</div>
88+
);
89+
})}
90+
</div>
91+
);
92+
};
93+
94+
interface Props {
95+
gifs: GifResult[];
96+
showLoadMore?: boolean;
97+
98+
onSelectGif: (gif: GifResult) => void;
99+
onLoadMore: () => void;
100+
}
101+
102+
const TenorGrid: FunctionComponent<Props> = ({
103+
gifs,
104+
showLoadMore,
105+
onSelectGif,
106+
onLoadMore,
107+
}) => {
108+
const { window } = useCoralContext();
109+
110+
const gridRef = useRef<HTMLDivElement>(null);
111+
const [cols, setCols] = useState<number>(0);
112+
113+
const resizeGrid = useCallback(() => {
114+
if (!gridRef || !gridRef.current) {
115+
setCols(0);
116+
return;
117+
}
118+
119+
const rect = gridRef.current.getBoundingClientRect();
120+
const numCols = rect.width / 90;
121+
122+
setCols(numCols);
123+
}, [gridRef, setCols]);
124+
125+
useEffect(() => {
126+
window.requestAnimationFrame(resizeGrid);
127+
window.addEventListener("resize", resizeGrid);
128+
129+
return () => {
130+
window.removeEventListener("resize", resizeGrid);
131+
};
132+
// include gifs so we re-calc grid col's on gif change
133+
// eslint-disable-next-line react-hooks/exhaustive-deps
134+
}, [gifs]);
135+
136+
return (
137+
<div className={styles.grid}>
138+
{gifs && cols > 0 && (
139+
<TenorGridColumns
140+
gifs={gifs}
141+
onSelectGif={onSelectGif}
142+
numColumns={cols}
143+
/>
144+
)}
145+
{showLoadMore && (
146+
<div className={styles.gridControls} ref={gridRef}>
147+
<Localized id="comments-postComment-gifSearch-search-loadMore">
148+
<Button color="stream" onClick={onLoadMore}>
149+
Load More
150+
</Button>
151+
</Localized>
152+
</div>
153+
)}
154+
</div>
155+
);
156+
};
157+
158+
export default TenorGrid;

client/src/core/client/stream/tabs/Comments/TenorInput/TenorInput.css

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -29,35 +29,6 @@
2929
max-width: 100%;
3030
}
3131

32-
.grid {
33-
max-height: 300px;
34-
overflow: auto;
35-
36-
display: flex;
37-
flex-wrap: wrap;
38-
justify-content: center;
39-
gap: 8px;
40-
}
41-
42-
.gridControls {
43-
width: 100%;
44-
display: flex;
45-
justify-content: center;
46-
align-items: center;
47-
}
48-
49-
.gridItem {
50-
width: 85px;
51-
background: var(--palette-background-body);
52-
border-style: none;
53-
padding: 0px;
54-
}
55-
56-
.gridImage {
57-
width: 100%;
58-
height: auto;
59-
}
60-
6132
.input {
6233
border-top-right-radius: 0;
6334
border-bottom-right-radius: 0;

client/src/core/client/stream/tabs/Comments/TenorInput/TenorInput.tsx

Lines changed: 9 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { ButtonSvgIcon, SearchIcon } from "coral-ui/components/icons";
1818
import { Button, HorizontalGutter, TextField } from "coral-ui/components/v2";
1919

2020
import TenorAttribution from "./TenorAttribution";
21+
import TenorGrid from "./TenorGrid";
2122

2223
import styles from "./TenorInput.css";
2324

@@ -181,34 +182,14 @@ const TenorInput: FunctionComponent<Props> = ({ onSelect }) => {
181182
}
182183
ref={inputRef}
183184
/>
184-
<div className={styles.grid}>
185-
{query &&
186-
gifs &&
187-
gifs.map((gif, index) => {
188-
return (
189-
<button
190-
className={styles.gridItem}
191-
key={`${gif.id}-${index}`}
192-
onClick={() => onGifClick(gif)}
193-
>
194-
<img
195-
className={styles.gridImage}
196-
alt={gif.title}
197-
src={gif.preview}
198-
></img>
199-
</button>
200-
);
201-
})}
202-
{next && gifs && gifs.length > 0 && query?.length > 0 && (
203-
<div className={styles.gridControls}>
204-
<Localized id="comments-postComment-gifSearch-search-loadMore">
205-
<Button color="stream" onClick={onLoadMore}>
206-
Load More
207-
</Button>
208-
</Localized>
209-
</div>
210-
)}
211-
</div>
185+
<TenorGrid
186+
gifs={query ? gifs : []}
187+
showLoadMore={
188+
!!(next && gifs && gifs.length > 0 && query?.length > 0)
189+
}
190+
onSelectGif={onGifClick}
191+
onLoadMore={onLoadMore}
192+
/>
212193
<TenorAttribution />
213194
</HorizontalGutter>
214195
</div>

0 commit comments

Comments
 (0)