1
1
/* eslint-disable react/prop-types */
2
2
import React , { useCallback , useEffect , useState } from 'react' ;
3
+ import { FaChevronDown , FaChevronUp } from 'react-icons/fa' ;
3
4
import styled from 'styled-components' ;
4
5
import { CardInner , CardImg , CardTitle } from "../Cards/styles" ;
5
6
import { theme } from "../../layouts/theme" ;
@@ -9,26 +10,56 @@ import { LIST_ANCHOR } from "./index";
9
10
import { Card , FilterOption , Group } from './types' ;
10
11
11
12
const cardWidthHeight = 160 ; // pixels
13
+ const expandPreviewHeight = 50 //pixels
14
+ const transitionDuration = "0.3s"
15
+ const transitionTimingFunction = "ease"
12
16
13
17
type ShowcaseProps = {
14
18
cards : Card [ ]
15
19
setSelectedFilterOptions : React . Dispatch < React . SetStateAction < readonly FilterOption [ ] > >
16
20
}
17
21
18
22
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
+
20
45
return (
21
46
< div >
22
47
< Byline >
23
48
Showcase resources: click to filter the resources to a pathogen
24
49
</ Byline >
25
- < SingleRow >
26
- < ShowcaseContainer >
50
+ < ShowcaseContainer className = { ! isExpandable ? "" : isExpanded ? "expanded" : "collapsed" } $expandedHeight = { cardsContainerHeight } >
51
+ < CardsContainer ref = { cardsContainerRef } >
27
52
{ cards . map ( ( el ) => (
28
53
< ShowcaseTile card = { el } key = { el . name } setSelectedFilterOptions = { setSelectedFilterOptions } />
29
54
) ) }
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
+ </ > }
32
63
< Spacer />
33
64
</ div >
34
65
)
@@ -66,23 +97,59 @@ const ShowcaseTile = ({card, setSelectedFilterOptions}: ShowcaseTileProps) => {
66
97
}
67
98
68
99
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;
76
120
`
77
121
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 `
79
147
/* 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%;
84
151
overflow: hidden;
85
- justify-content: space-between ;
152
+ justify-content: center ;
86
153
` ;
87
154
88
155
const Spacer = styled . div `
0 commit comments