Skip to content

Commit f7b770f

Browse files
authored
Merge pull request #878: Make showcase expandable
2 parents f2e4113 + 13e8906 commit f7b770f

File tree

1 file changed

+85
-18
lines changed

1 file changed

+85
-18
lines changed

static-site/src/components/ListResources/Showcase.tsx

+85-18
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* eslint-disable react/prop-types */
22
import React, { useCallback, useEffect, useState } from 'react';
3+
import { FaChevronDown, FaChevronUp } from 'react-icons/fa';
34
import styled from 'styled-components';
45
import {CardInner, CardImg, CardTitle} from "../Cards/styles";
56
import { theme } from "../../layouts/theme";
@@ -9,26 +10,56 @@ import { LIST_ANCHOR } from "./index";
910
import { Card, FilterOption, Group } from './types';
1011

1112
const cardWidthHeight = 160; // pixels
13+
const expandPreviewHeight = 50 //pixels
14+
const transitionDuration = "0.3s"
15+
const transitionTimingFunction = "ease"
1216

1317
type ShowcaseProps = {
1418
cards: Card[]
1519
setSelectedFilterOptions: React.Dispatch<React.SetStateAction<readonly FilterOption[]>>
1620
}
1721

1822
export const Showcase = ({cards, setSelectedFilterOptions}: ShowcaseProps) => {
19-
if (!cards.length) return null;
23+
24+
const [cardsContainerHeight, setCardsContainerHeight] = useState<number>(0);
25+
const [isExpanded, setIsExpanded] = useState<boolean>(false);
26+
27+
const toggleExpand = () => {
28+
setIsExpanded(!isExpanded);
29+
};
30+
31+
/**
32+
* Function that runs on changes to the container.
33+
* Used to determine the height upon resize.
34+
*/
35+
function cardsContainerRef(cardsContainer: HTMLDivElement) {
36+
if (!cardsContainer) return;
37+
38+
if(cardsContainerHeight != cardsContainer.clientHeight) {
39+
setCardsContainerHeight(cardsContainer.clientHeight)
40+
}
41+
}
42+
43+
const isExpandable = cardsContainerHeight > cardWidthHeight;
44+
2045
return (
2146
<div>
2247
<Byline>
2348
Showcase resources: click to filter the resources to a pathogen
2449
</Byline>
25-
<SingleRow>
26-
<ShowcaseContainer>
50+
<ShowcaseContainer className={!isExpandable ? "" : isExpanded ? "expanded" : "collapsed"} $expandedHeight={cardsContainerHeight}>
51+
<CardsContainer ref={cardsContainerRef}>
2752
{cards.map((el) => (
2853
<ShowcaseTile card={el} key={el.name} setSelectedFilterOptions={setSelectedFilterOptions}/>
2954
))}
30-
</ShowcaseContainer>
31-
</SingleRow>
55+
</CardsContainer>
56+
<PreviewOverlay onClick={toggleExpand} className={!isExpandable || isExpanded ? "hidden" : "visible"} />
57+
</ShowcaseContainer>
58+
{isExpandable && <>
59+
<ArrowButton onClick={toggleExpand}>
60+
{isExpanded ? <FaChevronUp/> : <FaChevronDown/>}
61+
</ArrowButton>
62+
</>}
3263
<Spacer/>
3364
</div>
3465
)
@@ -66,23 +97,59 @@ const ShowcaseTile = ({card, setSelectedFilterOptions}: ShowcaseTileProps) => {
6697
}
6798

6899

69-
/* SingleRow only shows a single row of tiles. By using this to wrap a flexbox
70-
element we can leverage the intelligent wrapping of the flexbox to decide how
71-
many tiles to show in a single row. The downside is that showcase tiles are
72-
still in the DOM, and the images are still fetched etc */
73-
const SingleRow = styled.div`
74-
max-height: ${cardWidthHeight}px;
75-
overflow-y: clip;
100+
const ShowcaseContainer = styled.div<{$expandedHeight: number}>`
101+
position: relative;
102+
overflow-y: hidden;
103+
104+
&.collapsed {
105+
max-height: ${cardWidthHeight + expandPreviewHeight}px;
106+
}
107+
108+
&.expanded {
109+
max-height: ${(props) => `${props.$expandedHeight}px`};
110+
}
111+
112+
transition: max-height ${transitionDuration} ${transitionTimingFunction};
113+
`
114+
115+
const ArrowButton = styled.div`
116+
text-align: center;
117+
width: 100%;
118+
height: 1em;
119+
cursor: pointer;
76120
`
77121

78-
const ShowcaseContainer = styled.div`
122+
const PreviewOverlay = styled.div`
123+
position: absolute;
124+
z-index: 1;
125+
bottom: 0;
126+
left: 0;
127+
background-image: linear-gradient(
128+
to bottom,
129+
rgba(255, 255, 255, 0) -100%,
130+
rgba(255, 255, 255, 1) 100%);
131+
width: 100%;
132+
height: ${expandPreviewHeight}px;
133+
cursor: pointer;
134+
135+
&.visible {
136+
opacity: 1;
137+
}
138+
139+
&.hidden {
140+
opacity: 0;
141+
}
142+
143+
transition: opacity ${transitionDuration} ${transitionTimingFunction};
144+
`;
145+
146+
const CardsContainer = styled.div`
79147
/* background-color: #ffeab0; */
80-
display: flex;
81-
flex-direction: row;
82-
flex-wrap: wrap;
83-
overflow: hidden;
148+
display: grid;
149+
grid-template-columns: repeat(auto-fit, minmax(${cardWidthHeight}px, max-content));
150+
grid-gap: 1%;
84151
overflow: hidden;
85-
justify-content: space-between;
152+
justify-content: center;
86153
`;
87154

88155
const Spacer = styled.div`

0 commit comments

Comments
 (0)