forked from US-GHG-Center/veda-config-ghg
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #148 from NASA-IMPACT/1237-make-carousel-available…
…-for-eic Port the carousel feature to EIC
- Loading branch information
Showing
8 changed files
with
411 additions
and
384 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
.blocklink { | ||
&:focus { outline: none; } | ||
&:focus-visible { outline: 1px solid var(--veda-color-link);} | ||
@supports not selector(:focus-visible) { | ||
&:focus { | ||
outline: 1px solid var(--veda-color-link); | ||
} | ||
} | ||
} | ||
|
||
.hug-reset-container { | ||
grid-column: 1 / -1; | ||
} | ||
|
||
.card-shadow__hover { | ||
box-shadow: 0 0 2px 0 rgba(44,62,80,0.08),0 6px 6px 0 rgba(44,62,80,0.08); | ||
transition: all 0.24s ease-in-out 0s; | ||
&:hover { | ||
transform: translate(0, 0.125rem); | ||
} | ||
} | ||
|
||
.card-image__blend { | ||
mix-blend-mode: multiply; | ||
} | ||
|
||
.veda-color--link { | ||
color: var(--veda-color-link); | ||
} | ||
.veda-color--base { | ||
color: var(--veda-color-base); | ||
} | ||
|
||
// font-size: 20px (1.25rem) will be deprecated with USWDS design system | ||
// but we need a temporary style class for the consistency across the pages | ||
|
||
.font-size-md-deprecated { | ||
font-size: calc(1rem + var(--base-text-increment, 0rem)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import React from '$veda-ui/react'; | ||
import styled from '$veda-ui/styled-components'; | ||
import { Button, Icon } from '$veda-ui/@trussworks/react-uswds'; | ||
|
||
import { | ||
Card, | ||
CardBody, | ||
CardFooter, | ||
} from '$veda-ui/@trussworks/react-uswds'; | ||
|
||
|
||
const progressColor = '#1565EF'; | ||
|
||
const ProgressIndicator = styled.div` | ||
background-color: ${progressColor}; | ||
width: ${props => props.progressWidth}%; | ||
transition: ${props => props.noTransition? null: 'width 200ms ease-out'}; | ||
`; | ||
|
||
function ProgressBar({ selected, shouldProgress, progressDone, progressPercentage }) { | ||
// If progress is done, 100% - false if something is manually selected | ||
// If it is in progress, progress Percentage - false if something is manually selected | ||
// If it is manually selected, 100% | ||
const progressWidth = progressDone? 100: shouldProgress? progressPercentage: selected? 100: 0; | ||
const noTransition = (!shouldProgress && !progressDone && progressPercentage === 0)? true : false; | ||
|
||
return <> | ||
<div className="height-05 bg-base-lighter"> | ||
<ProgressIndicator className="height-full" progressWidth={progressWidth} noTransition={noTransition} /> | ||
</div> | ||
</> | ||
} | ||
|
||
export function ItemPanel({ item, linkComponent: LinkComponent }) { | ||
return (<> | ||
|
||
<div className="tablet:margin-top-0 margin-top-2 flex-align-self-stretch"> | ||
<p className="margin-top-2 margin-bottom-2 flex-align-self-stretch">{item.description}</p> | ||
<LinkComponent className="display-flex flex-align-center veda-color--link" to={item.link}> | ||
<Icon.ArrowForward stroke={progressColor} fill={progressColor} /> | ||
<span className="padding-left-1">Read more</span> | ||
</LinkComponent> | ||
</div> | ||
</>) | ||
} | ||
|
||
export default function CarouselItem({ item, itemIdx, onTitleClick, shouldProgress, progressDone, progressPercentage, selected, linkComponent: LinkComponent }) { | ||
return <Card | ||
gridLayout={{ tablet: { col: 4 } }} | ||
containerProps={{className:`hover:bg-base-lightest padding-x-1 radius-0 border-0 animation--transition ${(selected || shouldProgress)? 'opacity-100':'opacity-50'}`}}> | ||
<ProgressBar shouldProgress={shouldProgress} progressDone={progressDone} progressPercentage={progressPercentage}selected={selected} /> | ||
<CardBody className="padding-left-0 position-relative"> | ||
<h3 className="tablet:margin-top-1 carousel--title text-bold veda-color--base"> | ||
{item.title} | ||
</h3> | ||
<p className="margin-top-2 flex-align-self-stretch">{item.description}</p> | ||
<Button | ||
unstyled={true} | ||
className="position-absolute top-0 left-0 width-full height-full blocklink" | ||
onClick={() => { onTitleClick(item); } } | ||
type="button" | ||
role="tab" | ||
aria-label={`Slide ${itemIdx+1}`} | ||
aria-selected={selected.toString()} | ||
aria-controls={`carousel-item-${itemIdx+1}`} | ||
children={undefined} /> | ||
</CardBody> | ||
<CardFooter className="padding-left-0 padding-top-1"> | ||
<LinkComponent className="display-flex flex-align-center veda-color--link" to={item.link}> | ||
<Icon.ArrowForward stroke={progressColor} fill={progressColor} /> | ||
<span className="padding-left-1">Read more</span> | ||
</LinkComponent> | ||
</CardFooter> | ||
</Card> | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
/* The classes for css transition group */ | ||
.imagetransition { | ||
&-enter { | ||
opacity: 0.5; | ||
} | ||
&-enter-active { | ||
opacity: 1; | ||
transition: opacity 400ms ease-in; | ||
} | ||
&-exit { | ||
opacity: 1; | ||
} | ||
/* ending EXIT animation */ | ||
&-exit-active { | ||
opacity: 0; | ||
transition: opacity 400ms ease-out; | ||
} | ||
} | ||
|
||
.veda-color--link { | ||
color: var(--veda-color-link); | ||
} | ||
.veda-color--base { | ||
color: var(--veda-color-base); | ||
} | ||
|
||
.animation--transition { | ||
transition: opacity 200ms ease-out; | ||
} | ||
|
||
.carousel { | ||
&--height { | ||
height: 500px; | ||
} | ||
|
||
&--content-image { | ||
width: 100%; | ||
height: 100%; | ||
object-fit: cover; | ||
} | ||
&--title { | ||
font-size: 1.25rem; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import React, { useState, useEffect, useCallback } from '$veda-ui/react'; | ||
|
||
import { CSSTransition, TransitionGroup } from "react-transition-group"; | ||
import { useMediaQuery } from "$veda-ui-scripts/utils/use-media-query"; | ||
import { GridContainer, Grid } from '$veda-ui/@trussworks/react-uswds'; | ||
import LazyLoad from '$veda-ui/react-lazyload'; | ||
import { CardGroup } from '$veda-ui/@trussworks/react-uswds'; | ||
|
||
import CarouselItems from './items'; | ||
import CarouselItem, { ItemPanel } from './carousel-item'; | ||
|
||
|
||
import SmartLink from '$veda-ui-scripts/components/common/smart-link'; | ||
|
||
import '/common/styles.scss'; | ||
import './index.scss'; | ||
|
||
const interval = 100; | ||
const slide_length = 50; | ||
const item_n = CarouselItems.length; | ||
|
||
export function DesktopCarousel () { | ||
const [timer, setTimer] = useState(0); | ||
const [selectedItem, setSelectedItem] = useState(null); | ||
const [timerAnimationId, setTimerAnimationId] = useState(null); | ||
|
||
// Animation starts on landing, once it is stopped, it is not going to be played again. | ||
useEffect(() => { | ||
const intervalId = setInterval(() => { | ||
setTimer(prev => { | ||
return prev + 1; | ||
}); | ||
}, interval); | ||
setTimerAnimationId(intervalId); | ||
return () => { | ||
clearInterval(intervalId); | ||
}; | ||
}, []); | ||
|
||
const animationTimer = timer % slide_length; | ||
// animationTimer/slide_length will never be 1, compensating the value here | ||
const progressPercentage = Math.floor((animationTimer/slide_length) * (100*(slide_length/(slide_length-1)))); | ||
const currentProgressItemIdx = Math.floor((timer / slide_length)%item_n); | ||
const itemInProgress = selectedItem?? CarouselItems[currentProgressItemIdx]; | ||
|
||
const onTitleClick = useCallback((clickedItem) => { | ||
clearInterval(timerAnimationId); | ||
setTimerAnimationId(null); | ||
setSelectedItem(clickedItem); | ||
},[timerAnimationId]); | ||
|
||
|
||
return ( | ||
<GridContainer aria-roledescription="carousel" aria-label="Highlighted VEDA Dashboard projects"> | ||
<Grid row className="position-relative carousel--height" aria-live="off"> | ||
<TransitionGroup> | ||
<CSSTransition | ||
key={itemInProgress.title} | ||
timeout={2000} | ||
classNames="imagetransition" | ||
> | ||
<div className="carousel--height width-full position-absolute left-0 top-0 shadow-1"> | ||
<img className="carousel--content-image" src={itemInProgress.image} alt={itemInProgress.imageAlt} /> | ||
</div> | ||
</CSSTransition> | ||
</TransitionGroup> | ||
</Grid> | ||
<CardGroup className="tablet:margin-top-4 margin-top-2" role="tablist" aria-label="Slides"> | ||
{CarouselItems.map((item, itemIdx) => { | ||
return <CarouselItem | ||
key={item.title} | ||
item={item} | ||
itemIdx={itemIdx} | ||
onTitleClick={onTitleClick} | ||
progressDone= {selectedItem? false: itemIdx < currentProgressItemIdx} | ||
shouldProgress = {selectedItem? false: currentProgressItemIdx == itemIdx} | ||
selected={!timerAnimationId && selectedItem?.title === item.title} | ||
progressPercentage = {progressPercentage} | ||
linkComponent={SmartLink} | ||
/> | ||
})} | ||
</CardGroup> | ||
</GridContainer>) | ||
} | ||
|
||
function TabletCarousel() { | ||
return <GridContainer> | ||
<Grid row className="margin-top-2"> | ||
{CarouselItems.map((item) => { | ||
return <Grid col={12} key={item.title} className="margin-bottom-4"> | ||
<div> | ||
<img className="carousel--content-image" src={item.image} /> | ||
</div> | ||
<h3 className="margin-top-1">{item.title}</h3> | ||
<ItemPanel item={item} linkComponent={SmartLink} /> | ||
</Grid> | ||
})} | ||
</Grid> | ||
</GridContainer> | ||
} | ||
|
||
|
||
export default function Carousel() { | ||
const { isMediumUp } = useMediaQuery(); | ||
return isMediumUp? | ||
<LazyLoad | ||
className="hug-reset-container" | ||
offset={100} | ||
once | ||
> | ||
<DesktopCarousel /> | ||
</LazyLoad>: | ||
<div className="hug-reset-container"><TabletCarousel /></div> | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
export default [ | ||
{ | ||
link: 'https://eyes.nasa.gov/apps/earth/#/', | ||
title: 'Eyes on Earth', | ||
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean vitae lorem et neque egestas mollis. Praesent ut porttitor mauris, a sagittis erat", | ||
image: new URL('../media/seaside-background.jpg', import.meta.url).href, | ||
imageAlt: 'Example image alt EIC 2024' | ||
}, | ||
{ | ||
link: 'https://earth.gov/mobile-climate-mapper/', | ||
title: 'Card Title 2', | ||
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean vitae lorem et neque egestas mollis. Praesent ut porttitor mauris, a sagittis erat', | ||
image: new URL('../media/swamp.png', import.meta.url).href, | ||
imageAlt: 'Example image alt EIC 2024' | ||
}, | ||
{ | ||
link: '/stories/agriculture', | ||
title: 'Internal link to the Agriculture story', | ||
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean vitae lorem et neque egestas mollis. Praesent ut porttitor mauris, a sagittis erat', | ||
image: new URL('../media/hyperwall-image1.png', import.meta.url).href, | ||
imageAlt: 'Example image alt EIC 2024' | ||
}] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.