Skip to content
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

Component control alt #59

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
20,570 changes: 20,570 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"web-vitals": "^1.0.1"
},
"scripts": {
"start": "react-scripts start",
"start": "react-scripts --openssl-legacy-provider start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
Expand Down
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/assets/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 src/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.
28 changes: 28 additions & 0 deletions src/components/ProductButtons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useContext } from "react";

import styles from "../styles/styles.module.css";
import { ProductContext } from "./ProductCard";

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

export const ProductButtons = ({ className, style }: Props) => {
//forma de pasar funciones es con el useContext
const { counter, increaseBy } = useContext(ProductContext);

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

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

<button onClick={() => increaseBy(+1)} className={styles.buttonAdd}>
+
</button>
</div>
);
};
53 changes: 53 additions & 0 deletions src/components/ProductCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import styles from "../styles/styles.module.css";

import { useProducts } from "../hooks/useProduct";
import { ReactElement, createContext } from "react";
import {
Product,
ProductContextProps,
onChangeArg,
} from "../interfaces/interfaces";

export interface Props {
product: Product;
value?: number;
children?: ReactElement | ReactElement[];
className?: string;
style?: React.CSSProperties;
onChange?: (args: onChangeArg) => void;
}

export const ProductContext = createContext({} as ProductContextProps);

export const { Provider } = ProductContext;

export const ProductCard = ({
children,
product,
className,
style,
onChange,
value
}: Props) => {
const { counter, increaseBy } = useProducts({ product, onChange, value});

return (
<Provider value={{ counter, increaseBy, product }}>
<div style={style} className={`${styles.productCard} ${className}`}>
{children}
{/* esto se renderisa super bien y se llama Component patter */}
{/* <ProductImage img={product.img} /> */}

{/* <ProductTitle title="Cofie" /> */}

{/* <img src={NoImg} alt="No Imagen" /> */}

{/* <ProductButtons counter={counter} increaseBy={increaseBy} /> */}
</div>
</Provider>
);
};

// ProductCard.Title = ProductTitle;
// ProductCard.Image = ProductImage;
// ProductCard.Buttons = ProductButtons;
35 changes: 35 additions & 0 deletions src/components/ProductImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import NoImg from "../assets/no-image.jpg";
import styles from "../styles/styles.module.css";

import { useContext } from "react";
import { ProductContext } from "./ProductCard";

export interface Props {
className?: string;
img?: string;
style?: React.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 = NoImg;
}

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

import styles from "../styles/styles.module.css";

export interface Prop {
className?: string;
title?: string;
activeClass?: string;
style?: React.CSSProperties;
}

//de esta manera se obliga a pasar el title
export const ProductTitle = ({ title, className, style }: Prop) => {
const { product } = useContext(ProductContext);

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

export { ProductButtons } from "./ProductButtons";
export { ProductImage } from "./ProductImage";
export { ProductTitle } from "./ProductTitle";

// object.assing es para asignar un nuevo valor al objeto

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

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

const product1 = {
id: "01",
title: "Coffie -img",
img: "./coffee-mug.png",
};

export const product2 = {
id: "02",
title: "Coffie -Meme",
img: "./coffee-mug2.png",
};

// de esta manera construimos un arreglo

export const products: Product[] = [product1, product2];
37 changes: 37 additions & 0 deletions src/hooks/useProduct.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { useEffect, useState } from "react";
import { Product, onChangeArg } from "../interfaces/interfaces";

interface useProductArgs {
product: Product;
onChange?: (args: onChangeArg) => void;
value?: number;
}

export const useProducts = ({
onChange,
product,
value = 0,
}: useProductArgs) => {
const [counter, setcounter] = useState(value);

const increaseBy = (value: number) => {
/// Math.max retorna el valor maxmo
// if (isControlled.current ) {
// return onChange!({counter: value, product})
// }

const newValue = Math.max(counter + value, 0);

setcounter(newValue);
onChange && onChange({ counter: newValue, product });
};

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

return {
counter,
increaseBy,
};
};
37 changes: 37 additions & 0 deletions src/hooks/useShoppingCart.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { useState } from "react";
import { ProductInCart, onChangeArg } from "../interfaces/interfaces";
import { products } from "../data/products";

export const useShoppingCardt = () => {
const [shoppingCard, setShoppingCard] = useState<{
[key: string]: ProductInCart;
}>({});

const onProductConstChange = ({ counter, product }: onChangeArg) => {
console.log({ counter });
// console.log(counter, product);
/// los tres puntos trae todo o copia todo el arreglo o el objeto
/// es decir que muestra todos los datos que en este caso ya existen en producto
setShoppingCard((oldShoppinCard) => {
if (counter <= 0) {
delete oldShoppinCard[product.id];

return {
...oldShoppinCard,
};
}

return {
...oldShoppinCard,
[product.id]: { ...product, counter },
};
});
};

return {
products,
onProductConstChange,
shoppingCard,
setShoppingCard,
};
};
38 changes: 38 additions & 0 deletions src/interfaces/interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Props as ProductButtonProps } from "../components/ProductButtons";
import { Props as ProductCardProps } from "../components/ProductCard";
import { Props as ProductImgProps } from "../components/ProductImage";
import { Prop as ProductTitlesProps } from "../components/ProductTitle";

export interface Product {
id: string;
title: string;
img?: string;
}

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


export interface ProductInCart extends Product {
counter: number;
}

export interface ProductCardsHOCProps {
({ children, product }: ProductCardProps): JSX.Element;

Title: (Props: ProductTitlesProps) => JSX.Element;

Image: (Props: ProductImgProps) => JSX.Element;

Buttons: (Props: ProductButtonProps) => JSX.Element;
}


export interface onChangeArg {
product: Product
counter: number

}
80 changes: 80 additions & 0 deletions src/pages/ShoppingPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import "../styles/coustomer.css";

import {
ProductButtons,
ProductCard,
ProductImage,
ProductTitle,
} from "../components/index";
import { useShoppingCardt } from "../hooks/useShoppingCart";
import { product2 } from "../data/products";

export const ShoppingPage = () => {
const { onProductConstChange, shoppingCard, products } = useShoppingCardt();

return (
<div>
<h1>Shopping page </h1>
<hr />
<div style={{ display: "flex", flexDirection: "row", flexWrap: "wrap" }}>
{/* HOC = ES TO QUIERE DECIR QUE RECIBE HIJOS */}
{/* <ProductCard product={product1} className="dark-gray text-white">
<ProductCard.Image className="custom-image" />
<ProductCard.Title activeClass="active" title="Cofie" />
<ProductCard.Buttons />
</ProductCard> */}

{products.map((producto) => (
<ProductCard
key={producto.id}
className="dark-gray text-white"
product={producto}
value={shoppingCard[producto.id]?.counter || 0}
onChange={(event) => onProductConstChange(event)}
>
<ProductImage className="custom-image" />
<ProductTitle className="text-white" activeClass="active" />
<ProductButtons className="custom-buttons" />
</ProductCard>
))}
</div>
<div className="shoppin-card">
<ProductCard
style={{ width: "100px" }}
className="dark-gray text-white"
product={product2}
>
<ProductImage className="custom-image" />
<ProductButtons
className="custom-buttons"
style={{ display: "flex", justifyContent: "center" }}
/>
</ProductCard>
{
// el Object.entries es para cuando no se deja hacer el .map y necesitamos obtener los valores de entreda o mapearlo
// pero tiene que salir si o si dos valores , en este caso la key y el product
Object.entries(shoppingCard).map(([key, product]) => (
<ProductCard
key={key}
style={{ width: "100px" }}
className="dark-gray text-white"
product={product}
value={product.counter}
onChange={onProductConstChange}
>
<ProductImage className="custom-image" />
<ProductButtons
className="custom-buttons"
style={{ display: "flex", justifyContent: "center" }}
/>
</ProductCard>
))
}
</div>

{/* <div>
<code>{JSON.stringify(shoppingCard, null, 5)}</code>
</div> */}
</div>
);
};
Loading