-
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 17 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,64 @@ | ||
| import React, { useState } from "react"; | ||
| import { AiOutlineLeft, AiOutlineRight } from "react-icons/ai"; | ||
| import Swipe from "react-easy-swipe"; | ||
|
|
||
| const Carousel = ({children}) => { | ||
| const slideLength = children.length; | ||
| const handleNextClick = () => { | ||
| const index = currentIndex === slideLength - 1 ? 0 : currentIndex + 1 | ||
|
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. Don't forget the trailing semicolons! Same comment for most of the lines below |
||
| setCurrentIndex(index) | ||
| } | ||
| const handleOnPrevClick = () => { | ||
| const index = currentIndex === 0 ? slideLength - 1 : currentIndex - 1 | ||
| setCurrentIndex(index) | ||
| }; | ||
|
|
||
| const [currentIndex, setCurrentIndex] = useState(0) | ||
| return ( | ||
| <div className="m-auto"> | ||
| <div className="w-full relative select-none"> | ||
| <AiOutlineLeft | ||
| onClick={handleOnPrevClick} | ||
| className="absolute left-0 text-3xl inset-y-1/2 text-white cursor-pointer" | ||
| /> | ||
| <Swipe> | ||
| { | ||
| children.map((child, index) => | ||
| React.cloneElement(child, { | ||
|
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. Quite smart! Well done. |
||
| className: ` | ||
| ${index === currentIndex | ||
| ? "block w-full h-auto object-cover" | ||
| : "hidden"} | ||
| `}) | ||
| ) | ||
| } | ||
| <div> | ||
| </div> | ||
|
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. Why the empty |
||
| </Swipe> | ||
| <div className="absolute w-full flex justify-center bottom-0"> | ||
| {children.map((element, index) => { | ||
| return ( | ||
| <div | ||
| className={ | ||
| index === currentIndex | ||
| ? "h-2 w-2 bg-black rounded-full mx-2 mb-2 cursor-pointer" | ||
| : "h-2 w-2 bg-white rounded-full mx-2 mb-2 cursor-pointer" | ||
|
ikhoaA marked this conversation as resolved.
Outdated
|
||
| } | ||
| key={index} | ||
| onClick={() => { | ||
|
ikhoaA marked this conversation as resolved.
Outdated
|
||
| setCurrentIndex(index); | ||
| }} | ||
| /> | ||
| ); | ||
| })} | ||
| </div> | ||
| <AiOutlineRight | ||
| 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,109 @@ | ||
| 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(() => | ||
|
loan-laux marked this conversation as resolved.
Outdated
|
||
| import('./Carousel'), | ||
| ); | ||
|
|
||
|
|
||
|
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. No need for an extra line break here |
||
| 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 | ||
| * e.g. `import Image from 'next/image'` | ||
| */ | ||
| imageComponent: PropTypes.elementType, | ||
| /** | ||
| * Title of Hero component | ||
| * e.g. `Hello world` | ||
| */ | ||
| title: PropTypes.string.isRequired, | ||
| /** | ||
| * Subtitle of Hero component | ||
| * e.g. `How to make you feel good` | ||
|
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. I appreciate the effort, but I don't think we necessarily need these examples. The names of the props are self-explanatory and the types provide enough info as to what they should be. |
||
| */ | ||
| subTitle: PropTypes.string.isRequired, | ||
| /** | ||
| * Hero image URL | ||
| * e.g. `https://images.unsplash.com/photo-1469334031218-e382a71b716b?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2670&q=80` | ||
| */ | ||
| imageUrl: PropTypes.string , | ||
|
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 remove the space before this trailing coma |
||
| }), | ||
| ), | ||
| }; | ||
|
|
||
| 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.
Leave spaces in your object destructuring notation:
const Carousel = ({ children }) => {