-
Notifications
You must be signed in to change notification settings - Fork 2
Feature/tech 438 hero component #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 19 commits
5b760a8
368ad83
d22fe2d
a8400b3
fcdc4f0
d9290c7
24d5803
6e99681
351e29c
8fb2bfd
b416bb7
f9f5c9a
e51cc41
94468ab
d3d454b
ed8d805
634ac7d
02f91e1
5259f4e
b642209
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| import React, { useState } from 'react'; | ||
| import leftArrow from '../../public/arrow left.svg'; | ||
| import rightArrow from '../../public/arrow right.svg'; | ||
| import { useSwipeable } from 'react-swipeable'; | ||
|
|
||
| const Carousel = ({ children }) => { | ||
| const slideLength = children.length; | ||
| const handleNextClick = () => { | ||
| const index = currentIndex === slideLength - 1 ? 0 : currentIndex + 1; | ||
| setCurrentIndex(index); | ||
| }; | ||
| const handleOnPrevClick = () => { | ||
| const index = currentIndex === 0 ? slideLength - 1 : currentIndex - 1; | ||
| setCurrentIndex(index); | ||
| }; | ||
| const handlers = useSwipeable({ | ||
| onSwipedLeft: handleNextClick, | ||
| onSwipedRight: handleOnPrevClick, | ||
| swipeDuration: 500, | ||
| preventScrollOnSwipe: true, | ||
| trackMouse: true, | ||
| }); | ||
|
|
||
| const [currentIndex, setCurrentIndex] = useState(0); | ||
| return ( | ||
| <div className="m-auto"> | ||
| <div className="w-full relative select-none"> | ||
| <img | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's avoid using an In this case, let's move |
||
| src={leftArrow} | ||
| onClick={handleOnPrevClick} | ||
| className="absolute left-0 text-3xl inset-y-1/2 text-white cursor-pointer" | ||
| alt="left arrow" | ||
| /> | ||
| <div {...handlers}> | ||
| {children.map((child, index) => | ||
| React.cloneElement(child, { | ||
| className: ` | ||
| ${ | ||
| index === currentIndex | ||
| ? 'block w-full h-auto object-cover' | ||
| : 'hidden' | ||
| } | ||
| `, | ||
| }), | ||
| )} | ||
| </div> | ||
| <div className="absolute w-full flex justify-center bottom-0"> | ||
| {children.map((element, index) => { | ||
| return ( | ||
| <div | ||
| className={`h-2 w-2 rounded-full mx-2 mb-2 cursor-pointer ${index === currentIndex ? "bg-black" : "bg-white"}`} | ||
| key={index} | ||
| onClick={() => { | ||
| setCurrentIndex(index); | ||
| }} | ||
| /> | ||
| ); | ||
| })} | ||
| </div> | ||
| <img | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment as for the left arrow. |
||
| src={rightArrow} | ||
| alt="right arrow" | ||
| onClick={handleNextClick} | ||
| className="absolute right-0 text-3xl inset-y-1/2 text-white cursor-pointer" | ||
| /> | ||
| </div> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default Carousel; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| import React from 'react'; | ||
| import clsx from 'clsx'; | ||
|
|
||
| const Content = ({ | ||
| classNames = {}, | ||
| title, | ||
| subTitle, | ||
| imageUrl, | ||
| hasOverlay, | ||
| Image, | ||
| Button, | ||
| }) => { | ||
| return ( | ||
| <> | ||
| <div | ||
| className={clsx('flex items-center justify-center', classNames.wrapper)} | ||
| style={{ | ||
| background: Image === undefined ? 'url(' + imageUrl + ')' : '', | ||
| backgroundPosition: 'center', | ||
| backgroundSize: 'cover', | ||
| backgroundRepeat: 'no-repeat', | ||
| height: '100vh', | ||
| width: '100vw', | ||
| }} | ||
| > | ||
| {Image && ( | ||
| <div | ||
| style={{ | ||
| position: 'fixed', | ||
| height: '100vh', | ||
| width: '100vw', | ||
| overflow: 'hidden', | ||
| zIndex: -1, | ||
| }} | ||
| > | ||
| <Image /> | ||
| </div> | ||
| )} | ||
| <div | ||
| style={{ | ||
| background: hasOverlay | ||
| ? 'linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.6))' | ||
| : '', | ||
| height: '100vh', | ||
| width: '100vw', | ||
| }} | ||
| className={clsx( | ||
| ' px-4 py-16 sm:px-6 sm:py-24 lg:py-32 lg:px-8', | ||
| classNames.contentWrapper, | ||
| )} | ||
| > | ||
| <h1 | ||
| className={clsx( | ||
| 'text-center text-4xl font-extrabold mt-32 tracking-tight sm:text-5xl lg:text-6xl', | ||
| classNames.titleWrapper, | ||
| )} | ||
| > | ||
| <span className={clsx('block text-white', classNames.title)}> | ||
| {title} | ||
| </span> | ||
| </h1> | ||
| <p | ||
| className={clsx( | ||
| 'mt-6 max-w-lg mx-auto text-center text-xl text-white sm:max-w-3xl', | ||
| classNames.subTitle, | ||
| )} | ||
| > | ||
| {subTitle} | ||
| </p> | ||
| <div | ||
| className={clsx( | ||
| 'mt-10 max-w-sm mx-auto sm:max-w-none sm:flex sm:justify-center lg:max-w-sm', | ||
| classNames.buttonWrapper, | ||
| )} | ||
| > | ||
| <Button /> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </> | ||
| ); | ||
| }; | ||
|
|
||
| export default Content; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| import React, { lazy, Suspense } from 'react'; | ||
| import PropTypes, { element, elementType, node } from 'prop-types'; | ||
| import { Button as ButtonComponent } from '../Button'; | ||
| import Content from './Content'; | ||
| const Carousel = lazy(() => import('./Carousel')); | ||
|
|
||
| const Hero = ({ classNames = {}, hasOverlay, slides, Button }) => { | ||
| const isSlider = slides.length > 1; | ||
| return ( | ||
| <> | ||
| {isSlider ? ( | ||
| <Suspense fallback={<div>Loading...</div>}> | ||
| <Carousel> | ||
| {slides.map((slide, index) => ( | ||
| <div className="" key={index}> | ||
| <Content | ||
| title={slide.title} | ||
| classNames={classNames} | ||
| subTitle={slide.subTitle} | ||
| Button={Button} | ||
| Image={slide.imageComponent} | ||
| imageUrl={slide.imageUrl} | ||
| hasOverlay={hasOverlay} | ||
| /> | ||
| </div> | ||
| ))} | ||
| </Carousel> | ||
| </Suspense> | ||
| ) : ( | ||
| <Content | ||
| title={slides[0].title} | ||
| classNames={classNames} | ||
| subTitle={slides[0].subTitle} | ||
| Button={Button} | ||
| imageUrl={slides[0].imageUrl} | ||
| Image={slides[0].imageComponent} | ||
| hasOverlay={hasOverlay} | ||
| /> | ||
| )} | ||
| </> | ||
| ); | ||
| }; | ||
| Hero.propTypes = { | ||
| /** | ||
| * Object of classNames to be added to each part of the component. | ||
| * e.g. `{ container: 'my-custom-class', container: 'my-other-custom-class' }` | ||
| */ | ||
| classNames: PropTypes.shape({ | ||
| container: PropTypes.string, | ||
| title: PropTypes.string, | ||
| featureWrapper: PropTypes.string, | ||
| featureTitle: PropTypes.string, | ||
| featureSubtitle: PropTypes.string, | ||
| }), | ||
| /** | ||
| * Boolean that toggles the overlay | ||
| * e.g. `true` | ||
| */ | ||
| hasOverlay: PropTypes.bool, | ||
| /** | ||
| * React Button component | ||
| * e.g. `<ButtonComponent | ||
| * displayText="Shop Now" | ||
| * onClick={() => { | ||
| * alert('You clicked me'); | ||
| * }} | ||
| * />` | ||
| */ | ||
| Button: PropTypes.elementType.isRequired, | ||
| /** | ||
| * Array of objects for each slide to display | ||
| * e.g. `[{title: "Women's History Month",subTitle: "Discover our latest women's releases in celebration of Women's History Month",imageUrl: "https://images.unsplash.com/photo-1469334031218-e382a71b716b?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2670&q=80"}]` | ||
| */ | ||
| slides: PropTypes.arrayOf( | ||
| PropTypes.shape({ | ||
| /** | ||
| * Custom Image optimizer component | ||
| */ | ||
| imageComponent: PropTypes.elementType, | ||
| /** | ||
| * Title of Hero component | ||
| */ | ||
| title: PropTypes.string.isRequired, | ||
| /** | ||
| * Subtitle of Hero component | ||
| */ | ||
| subTitle: PropTypes.string.isRequired, | ||
| /** | ||
| * Hero image URL | ||
| */ | ||
| imageUrl: PropTypes.string.isRequired, | ||
| }), | ||
| ), | ||
| }; | ||
|
|
||
| export default Hero; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export { default as Hero } from './Hero'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would highly discourage using spaces in file names. That's a big no-no in my books.