diff --git a/public/coffee-mug.png b/public/coffee-mug.png new file mode 100644 index 000000000..c19c2088d Binary files /dev/null and b/public/coffee-mug.png differ diff --git a/src/02-component-patterns/assets/no-image.jpg b/src/02-component-patterns/assets/no-image.jpg new file mode 100644 index 000000000..ae122b097 Binary files /dev/null and b/src/02-component-patterns/assets/no-image.jpg differ diff --git a/src/02-component-patterns/components/ProductButtons.tsx b/src/02-component-patterns/components/ProductButtons.tsx new file mode 100644 index 000000000..5332c59bd --- /dev/null +++ b/src/02-component-patterns/components/ProductButtons.tsx @@ -0,0 +1,18 @@ +import { useContext } from 'react' +import { ProductContext } from './ProductCard' +import styles from '../styles/styles.module.css' + +export const ProductButtons = () => { + const {increaseBy, counter } = useContext(ProductContext) + return ( + <div className={styles.buttonsContainer}> + <button onClick={() => increaseBy(-1)} className={styles.buttonMinus}> + - + </button> + <div className={styles.countLabel}>{counter}</div> + <button onClick={() => increaseBy(1)} className={styles.buttonAdd}> + + + </button> + </div> + ) +} diff --git a/src/02-component-patterns/components/ProductCard.tsx b/src/02-component-patterns/components/ProductCard.tsx new file mode 100644 index 000000000..e5fcb33fc --- /dev/null +++ b/src/02-component-patterns/components/ProductCard.tsx @@ -0,0 +1,23 @@ +import { createContext } from 'react' +import styles from '../styles/styles.module.css' +import { useProduct } from '../hooks/useProduct' +import { ProductContextProps, ProductCardProps } from '../interfaces/interfaces' + +export const ProductContext = createContext({} as ProductContextProps) +const { Provider } = ProductContext + +export const ProductCard = ({ product, children }: ProductCardProps) => { + const { counter, increaseBy } = useProduct() + + return( + <Provider value={{ + counter, + increaseBy, + product + }}> + <div className={styles.productCard}> + {children} + </div> + </Provider> + ) +} \ No newline at end of file diff --git a/src/02-component-patterns/components/ProductImage.tsx b/src/02-component-patterns/components/ProductImage.tsx new file mode 100644 index 000000000..7d747eb5a --- /dev/null +++ b/src/02-component-patterns/components/ProductImage.tsx @@ -0,0 +1,13 @@ +import { useContext } from 'react' +import { ProductContext } from './ProductCard' +import noImage from '../assets/no-image.jpg' +import styles from '../styles/styles.module.css' + +export const ProductImage = ({ img = '' }) => { + const {product} = useContext(ProductContext) + const imgShow = img || product.img || noImage + + return ( + <img className={styles.productImg} src={imgShow} alt="Product image"/> + ) +} \ No newline at end of file diff --git a/src/02-component-patterns/components/ProductTitle.tsx b/src/02-component-patterns/components/ProductTitle.tsx new file mode 100644 index 000000000..88bbf0ec1 --- /dev/null +++ b/src/02-component-patterns/components/ProductTitle.tsx @@ -0,0 +1,12 @@ +import { useContext } from 'react' +import { ProductContext } from './ProductCard' +import styles from '../styles/styles.module.css' + +export const ProductTitle = ({ title }: { title?: string }) => { + const {product} = useContext(ProductContext) + const titleShow = title || product.title + + return ( + <span className={styles.productDescription}>{titleShow}</span> + ) +} diff --git a/src/02-component-patterns/components/index.ts b/src/02-component-patterns/components/index.ts new file mode 100644 index 000000000..d5c704d4d --- /dev/null +++ b/src/02-component-patterns/components/index.ts @@ -0,0 +1,15 @@ +import { ProductCardHOCProps } from '../interfaces/interfaces' +import { ProductCard as ProductCardHOC } from './ProductCard' +import { ProductImage } from './ProductImage' +import { ProductTitle } from './ProductTitle' +import { ProductButtons } from './ProductButtons' + +export { ProductImage } from './ProductImage' +export { ProductTitle } from './ProductTitle' +export { ProductButtons } from './ProductButtons' + +export const ProductCard: ProductCardHOCProps = Object.assign( ProductCardHOC, { + Title: ProductTitle, + Image: ProductImage, + Buttons: ProductButtons +} ) \ No newline at end of file diff --git a/src/02-component-patterns/hooks/useProduct.ts b/src/02-component-patterns/hooks/useProduct.ts new file mode 100644 index 000000000..8f854f251 --- /dev/null +++ b/src/02-component-patterns/hooks/useProduct.ts @@ -0,0 +1,11 @@ +import { useState } from 'react' + +export const useProduct = () => { + const [counter, setCounter] = useState(0) + + const increaseBy = (value: number) => { + setCounter(prev => Math.max(prev + value, 0)) + } + + return { counter, increaseBy } +} \ No newline at end of file diff --git a/src/02-component-patterns/interfaces/interfaces.ts b/src/02-component-patterns/interfaces/interfaces.ts new file mode 100644 index 000000000..15547c59d --- /dev/null +++ b/src/02-component-patterns/interfaces/interfaces.ts @@ -0,0 +1,25 @@ +import { ReactElement } from 'react' + +export interface ProductCardProps { + product: Product, + children?: ReactElement | ReactElement[] +} + +export interface Product { + id: string + title: string + img?: string +} + +export interface ProductContextProps { + counter: number + increaseBy: (value: number) => void, + product: Product +} + +export interface ProductCardHOCProps { + ({ product, children }: ProductCardProps): JSX.Element, + Title: ({ title }: {title?: string}) => JSX.Element, + Image: ({ img }: {img?: string}) => JSX.Element, + Buttons: () => JSX.Element +} \ No newline at end of file diff --git a/src/02-component-patterns/pages/ShoppingPage.tsx b/src/02-component-patterns/pages/ShoppingPage.tsx new file mode 100644 index 000000000..56902684d --- /dev/null +++ b/src/02-component-patterns/pages/ShoppingPage.tsx @@ -0,0 +1,35 @@ +import { ProductButtons, ProductCard, ProductImage, ProductTitle } from "../components" + +const product = { + id: '1', + title: 'Coffee Mug - Card', + img: './coffee-mug.png' +} + +export const ShoppindPage = () =>{ + return ( + <div> + <h1>Shoppieng Page</h1> + <hr/> + <div + style={{ + display: 'flex', + flexDirection: 'row', + flexWrap: 'wrap' + }} + > + <ProductCard product={product}> + <ProductCard.Image/> + <ProductCard.Title/> + <ProductCard.Buttons/> + </ProductCard> + + <ProductCard product={product}> + <ProductImage/> + <ProductTitle title="Hola mundo"/> + <ProductButtons/> + </ProductCard> + </div> + </div> + ) +} \ No newline at end of file diff --git a/src/02-component-patterns/styles/styles.module.css b/src/02-component-patterns/styles/styles.module.css new file mode 100644 index 000000000..48ab3f819 --- /dev/null +++ b/src/02-component-patterns/styles/styles.module.css @@ -0,0 +1,62 @@ + + +.productCard { + background-color: white; + border-radius: 15px; + color: black; + padding-bottom: 5px; + width: 250px; + margin-right: 5px; + margin-top: 5px; +} + +.productImg { + border-radius: 15px 15px 0px 0px; + width: 100%; +} + +.productDescription { + margin: 10px; +} + +.buttonsContainer { + margin: 10px; + display: flex; + flex-direction: row; +} + +.buttonMinus { + cursor: pointer; + background-color: transparent; + border: 1px solid black; + border-radius: 5px 0px 0px 5px; + font-size: 20px; + width: 30px; +} + +.buttonMinus:hover { + background-color: rgba(0, 0, 0, 0.1); +} + +.countLabel { + border-bottom: 1px solid black; + border-top: 1px solid black; + font-size: 16px; + height: 25px; + padding-top: 5px; + text-align: center; + width: 30px; +} + +.buttonAdd { + cursor: pointer; + background-color: transparent; + border: 1px solid black; + border-radius: 0px 5px 5px 0px; + font-size: 20px; + width: 30px; +} + +.buttonAdd:hover { + background-color: rgba(0, 0, 0, 0.1); +} \ No newline at end of file diff --git a/src/routes/Navigation.tsx b/src/routes/Navigation.tsx index a9bfe83b0..bb82c5153 100644 --- a/src/routes/Navigation.tsx +++ b/src/routes/Navigation.tsx @@ -4,6 +4,7 @@ import { Route, NavLink } from 'react-router-dom'; +import { ShoppindPage } from '../02-component-patterns/pages/ShoppingPage'; import logo from '../logo.svg'; @@ -15,7 +16,7 @@ export const Navigation = () => { <img src={ logo } alt="React Logo" /> <ul> <li> - <NavLink to="/" activeClassName="nav-active" exact>Home</NavLink> + <NavLink to="/" activeClassName="nav-active" exact>Shopping</NavLink> </li> <li> <NavLink to="/about" activeClassName="nav-active" exact>About</NavLink> @@ -36,7 +37,7 @@ export const Navigation = () => { <h1>Users</h1> </Route> <Route path="/"> - <h1>Home</h1> + <ShoppindPage/> </Route> </Switch> </div>