Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added public/coffee-mug.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/coffee-mug2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/02-component-patterns/assets/no-image.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions src/02-component-patterns/components/ProducComponents.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useContext } from "react"
import ProductContext from "./productContext"
import styles from '../styles/styles.module.css'
import noImage from '../assets/no-image.jpg'
import { Props as ProductImageProps } from "./ProductImage"
import { Props as ProductTitleProps } from "./ProductTitle"
import { Props as ProdtuctButtonsProps } from "./ProductButtons"


export const ProductImage = ({img='', className = '', style }:ProductImageProps):JSX.Element => {
const {product} = useContext(ProductContext)
const { img:imageContext } = product
return <img style={style} className={`${styles.productImg} ${className}`} src={ !!img ? img : imageContext ? imageContext : noImage } alt="Coffee Mug" />
}

export const ProductTitle = ({title = '', className = '', style}: ProductTitleProps):JSX.Element => {
const {product} = useContext(ProductContext)
const { title: titleContext } = product
return <span style={style} className={`${styles.productDescription} ${className}`}>{ title ? title : titleContext }</span>
}
export const ProductButtons = ({className = '', style}: ProdtuctButtonsProps):JSX.Element => {

const {counter, increasedBy} = useContext(ProductContext)

return (
<div style={style} className={`${styles.buttonsContainer} ${className}`}>
<button className={styles.buttonMinus} onClick={() => increasedBy(-1) }>
-
</button>
<div className={styles.countLabel}>{counter}</div>
<button className={styles.buttonAdd} onClick={() => increasedBy(+1) } >
+
</button>
</div>
)
}
29 changes: 29 additions & 0 deletions src/02-component-patterns/components/ProductButtons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { CSSProperties, useContext } from "react";
import ProductContext from "./productContext";

import styles from '../styles/styles.module.css'


export interface Props {
className?: string
style?: CSSProperties
}

export const ProductButtons = ({className, style}:Props) => {

const { increasedBy, counter } = useContext( ProductContext );

return (
<div className={`${styles.buttonsContainer} ${className}`} style= { style } >
<button
className={ styles.buttonMinus }
onClick={ () => increasedBy( -1 ) }> - </button>

<div className={ styles.countLabel }> { counter } </div>

<button
className={ styles.buttonAdd }
onClick={ () => increasedBy( +1 ) }> + </button>
</div>
);
}
38 changes: 38 additions & 0 deletions src/02-component-patterns/components/ProductCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import styles from '../styles/styles.module.css'
import { useProduct } from '../hooks/useProduct';
import {ProductProps } from '../interfaces';
import ProductContext from './productContext';

import '../styles/custom-styles.css'
import { CSSProperties, useEffect } from 'react';
import { Product } from '../interfaces/interfaces';

export interface Props extends ProductProps {
className?: string
style?: CSSProperties
onChange?: (count:number, product: Product ) => void
value?: number
}

export const ProductCard = ({product, children, className, style, onChange, value }:Props):JSX.Element => {
const { Provider } = ProductContext
// console.log(styles)
// console.log(className)


const {counter, increasedBy} = useProduct({ initialState: 0 , onChange, product, value } )

return (
<>
<Provider value={{
product,
counter,
increasedBy
}}>
<div className={ `${styles.productCard} ${className}` } style= { style }>
{children}
</div>
</ Provider>
</>
)
}
30 changes: 30 additions & 0 deletions src/02-component-patterns/components/ProductImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { CSSProperties, useContext } from 'react';
import ProductContext from './productContext';

import styles from '../styles/styles.module.css'
import noImage from '../assets/no-image.jpg';

export interface Props {
img?: string
className?: string
style?: CSSProperties
}

export const ProductImage = ({ img = '', className= '', style }:Props) => {

const { product } = useContext( ProductContext );
let imgToShow: string;

if ( img ) {
imgToShow = img;
} else if ( product.img ) {
imgToShow = product.img
} else {
imgToShow = noImage;
}


return (
<img style={ style } className={ `${styles.productImg} ${className}`} src={ imgToShow } alt="Product" />
);
}
22 changes: 22 additions & 0 deletions src/02-component-patterns/components/ProductTitle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { CSSProperties, useContext } from 'react';
import ProductContext from './productContext';

import styles from '../styles/styles.module.css'
import '../styles/custom-styles.css'

export interface Props {
title?: string;
className?: string
style?: CSSProperties
}

export const ProductTitle = ({ title, className, style }:Props) => {

const { product } = useContext( ProductContext )

return (
<span className={ `${styles.productDescription} ${className}` } style= { style }>
{ title ? title : product.title }
</span>
);
}
14 changes: 14 additions & 0 deletions src/02-component-patterns/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ProductCard as ProductCardHOC } from "./ProductCard";
import { ProductImage, ProductTitle, ProductButtons } from "./ProducComponents";
// import { IProductCard } from "../interfaces";
import { ProductCardHOCProps, ProductCardProps } from "../interfaces/interfaces";

export const ProductCard:ProductCardHOCProps = Object.assign(ProductCardHOC, {
Image: ProductImage,
Title: ProductTitle,
Buttons: ProductButtons,
});

export default ProductCard;
export * from "./ProducComponents";
export { default as ProductContext } from "./productContext";
6 changes: 6 additions & 0 deletions src/02-component-patterns/components/productContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { createContext } from "react";
import { ProductContextProps } from "../interfaces";

const ProductContext = createContext({} as ProductContextProps)

export default ProductContext
18 changes: 18 additions & 0 deletions src/02-component-patterns/data/products.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Product } from "../interfaces/interfaces";

export const products:Product[] = [
{
id: '1',
title: 'Coffee Mug | Card 1',
img: './coffee-mug.png'
},
{
id: '2',
title: 'Coffee Mug | Card 2',
img: './coffee-mug2.png'
},
// {
// id: '3',
// title: 'Coffee Mug | Card 3',
// },
]
45 changes: 45 additions & 0 deletions src/02-component-patterns/hooks/useProduct.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useState, useEffect, useRef } from "react";
import { Product } from "../interfaces/interfaces";

type UseProductType = {
counter: number;
increasedBy: (value: number) => void;
};

type OnChangeType = (count: number, product: Product) => void;

type UseProductArgsType = {
initialState: number;
onChange?: OnChangeType;
product?: Product;
value?: number;
};

export const useProduct = ({
initialState,
onChange,
product,
value,
}: UseProductArgsType): UseProductType => {
const [counter, setcounter] = useState(initialState);

const isControlled = useRef(!!onChange);
// console.log("hook", value, counter, !!onChange, !!product);
const increasedBy = (value: number): void => {
if (isControlled.current) {
onChange!(value, product!);
return;
}
const newValue = Math.max(counter + value, 0);
setcounter(newValue);
};

useEffect(() => {
setcounter(value!);
}, [value, product]);

return {
counter,
increasedBy,
};
};
43 changes: 43 additions & 0 deletions src/02-component-patterns/hooks/useShoppingCart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { useState } from 'react'
import { Product, ProductsCart } from '../interfaces/interfaces'

type ProductInCart = {[key:string] : ProductsCart}


type UseShoppingCartResponse = {
onProductCountChange: (count: number, product: Product) => void;
shoppingCart: ProductInCart;
}

export const useShoppingCart = (): UseShoppingCartResponse => {

const [shoppingCart, setShoppingCart] = useState<ProductInCart>({ })

const onProductCountChange = (count:number, product:Product) => {
setShoppingCart((prev) => {
// console.log(count, product.id)

const productInCart: ProductsCart = prev[product.id] || { ...product, count:0 }

if( Math.max(productInCart.count + count, 0) > 0 ){
productInCart.count += count
return {
...prev,
[productInCart.id]: productInCart
}
}
/* Borra elemento del carrito */
// const cartWithoutZeros = { ...shoppingCart }
// delete cartWithoutZeros[product.id]
const { [product.id] : toDelete, ...rest } = prev
return {...rest }


})
}

return {
onProductCountChange,
shoppingCart
}
}
1 change: 1 addition & 0 deletions src/02-component-patterns/interfaces/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './productsInterfaces'
36 changes: 36 additions & 0 deletions src/02-component-patterns/interfaces/interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { CSSProperties, ReactElement } from "react";
import { Props as ProductCardType } from "../components/ProductCard";
import { Props as PropsProductImage } from "../components/ProductImage";
import { Props as PropsProductTitle } from "../components/ProductTitle";

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 {
({ children, product, className }: ProductCardType): JSX.Element;
Title: (Props: PropsProductTitle) => JSX.Element;
Image: (Props: PropsProductImage) => JSX.Element;
Buttons: ({
className,
}: {
className?: string;
style?: CSSProperties;
}) => JSX.Element;
}
export interface ProductsCart extends Product {
count: number;
}
26 changes: 26 additions & 0 deletions src/02-component-patterns/interfaces/productsInterfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { ReactNode } from "react";

export interface IProduct {
id: string;
title: string;
img?: string;
className?: string;
}

export interface ProductProps {
product: IProduct;
children?: ReactNode;
}

export interface ProductContextProps {
product: IProduct;
counter: number;
increasedBy: (value: number) => void;
}

export interface IProductCard {
({ product, children }: ProductProps): JSX.Element;
Image: ({ img }: { img?: string | undefined }) => JSX.Element;
Title: ({ title }: { title?: string | undefined }) => JSX.Element;
Buttons: () => JSX.Element;
}
Loading