@tatan22/jcd-product-card 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 tatancarduar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # JCD-Product-Card
2
+
3
+ Este es un paquete de pruebas de despliegue de NPM
4
+
5
+ ## Jhonatan Cardona Duarte
6
+
7
+ ## Ejemplos
8
+
9
+ ```tsx
10
+ import { ProductCard , ProductImage, ProductTitle, ProductButtons} from 'jcd-product-card';
11
+ ```
12
+
13
+ ```tsx
14
+ <ProductCard
15
+ product={product}
16
+ initialValues={{
17
+ count: 4,
18
+ maxCount: 10,
19
+ }}
20
+ >
21
+ {({ reset, count, increaseBy, isMaxCountReached }) => (
22
+ <>
23
+ <ProductCard.Image />
24
+ <ProductCard.Title />
25
+ <ProductCard.Buttons />
26
+
27
+ </>
28
+ )}
29
+ </ProductCard>
30
+ ```
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ export interface Props {
3
+ className?: string;
4
+ style?: React.CSSProperties;
5
+ activeBtnClass?: string;
6
+ }
7
+ export declare const ProductButtons: ({ className, style }: Props) => React.JSX.Element;
@@ -0,0 +1,17 @@
1
+ import React, { JSX } from 'react';
2
+ import { InitialValues, onChangeArgs, Product, ProductCardHandlers } from '../interfaces/interfaces';
3
+ export interface Props {
4
+ product: Product;
5
+ children: (args: ProductCardHandlers) => JSX.Element;
6
+ className?: string;
7
+ style?: React.CSSProperties;
8
+ onChange?: (args: onChangeArgs) => void;
9
+ value?: number;
10
+ initialValues?: InitialValues;
11
+ }
12
+ export declare const ProductCard: {
13
+ ({ children, product, className, style, onChange, value, initialValues, }: Props): JSX.Element;
14
+ Image: ({ img, className, style }: import("./ProductImage").Props) => JSX.Element;
15
+ Title: ({ title, className, style }: import("./ProductTitle").Props) => React.ReactElement<any, string | ((props: any) => React.ReactElement<any, any> | null) | (new (props: any) => React.Component<any, any, any>)>;
16
+ Buttons: ({ className, style }: import("./ProductButtons").Props) => JSX.Element;
17
+ };
@@ -0,0 +1,3 @@
1
+ /// <reference types="react" />
2
+ import { ProductContextProps } from "../interfaces/interfaces";
3
+ export declare const ProductContext: import("react").Context<ProductContextProps>;
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ export interface Props {
3
+ img?: string;
4
+ className?: string;
5
+ style?: React.CSSProperties;
6
+ }
7
+ export declare const ProductImage: ({ img, className, style }: Props) => React.JSX.Element;
@@ -0,0 +1,7 @@
1
+ import React, { ReactElement } from "react";
2
+ export interface Props {
3
+ title?: string;
4
+ className?: string;
5
+ style?: React.CSSProperties;
6
+ }
7
+ export declare const ProductTitle: ({ title, className, style }: Props) => ReactElement;
@@ -0,0 +1,6 @@
1
+ import { ProductCardHOCProps } from '../interfaces/interfaces';
2
+ export { ProductButtons } from './ProductButtons';
3
+ export { ProductImage } from './ProductImage';
4
+ export { ProductTitle } from './ProductTitle';
5
+ export declare const ProductCard: ProductCardHOCProps;
6
+ export default ProductCard;
@@ -0,0 +1,15 @@
1
+ import { InitialValues, onChangeArgs, Product } from "../interfaces/interfaces";
2
+ interface useProductArgs {
3
+ product: Product;
4
+ onChange?: (args: onChangeArgs) => void;
5
+ value?: number;
6
+ initialValues?: InitialValues;
7
+ }
8
+ export declare const useProduct: ({ onChange, product, value, initialValues, }: useProductArgs) => {
9
+ counter: number;
10
+ maxCount: number | undefined;
11
+ increaseBy: (value?: number | undefined) => void;
12
+ isMaxCountReached: boolean;
13
+ reset: () => void;
14
+ };
15
+ export {};
@@ -0,0 +1 @@
1
+ export * from "./components";
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+
2
+ 'use strict'
3
+
4
+ if (process.env.NODE_ENV === 'production') {
5
+ module.exports = require('./jcd-product-card.cjs.production.min.js')
6
+ } else {
7
+ module.exports = require('./jcd-product-card.cjs.development.js')
8
+ }
@@ -0,0 +1,41 @@
1
+ import { JSX, JSXElementConstructor, ReactElement } from 'react';
2
+ import { Props as ProductCardProps } from '../components/ProductCard';
3
+ import { Props as ProductTitleProps } from '../components/ProductTitle';
4
+ import { Props as ProductImageProps } from '../components/ProductImage';
5
+ import { Props as ProductButtonsProps } from '../components/ProductButtons';
6
+ export interface Product {
7
+ id: string;
8
+ title: string;
9
+ img?: string;
10
+ }
11
+ export interface ProductContextProps {
12
+ counter: number;
13
+ maxCount?: number;
14
+ product: Product;
15
+ increaseBy: (value?: number) => void;
16
+ }
17
+ export interface ProductCardHOCProps {
18
+ ({ children, product }: ProductCardProps): JSX.Element;
19
+ Image: (Props: ProductImageProps) => JSX.Element;
20
+ Button: (Props: ProductButtonsProps) => JSX.Element;
21
+ Title: (Props: ProductTitleProps) => ReactElement<unknown, string | JSXElementConstructor<any>>;
22
+ }
23
+ export interface onChangeArgs {
24
+ product: Product;
25
+ count: number;
26
+ }
27
+ export interface ProductsInCart extends Product {
28
+ count: number;
29
+ }
30
+ export interface InitialValues {
31
+ count?: number;
32
+ maxCount?: number;
33
+ }
34
+ export interface ProductCardHandlers {
35
+ count: number;
36
+ isMaxCountReached: boolean;
37
+ maxCount?: number;
38
+ product: Product;
39
+ increaseBy: (value: number) => void;
40
+ reset: () => void;
41
+ }
@@ -0,0 +1,217 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
6
+
7
+ var React = require('react');
8
+ var React__default = _interopDefault(React);
9
+
10
+ var useProduct = function useProduct(_ref) {
11
+ var onChange = _ref.onChange,
12
+ product = _ref.product,
13
+ _ref$value = _ref.value,
14
+ value = _ref$value === void 0 ? 0 : _ref$value,
15
+ initialValues = _ref.initialValues;
16
+ // if (initialValues) {
17
+ // value = initialValues.count || value;
18
+ // }
19
+ var _useState = React.useState((initialValues == null ? void 0 : initialValues.count) || value),
20
+ counter = _useState[0],
21
+ setCounter = _useState[1];
22
+ var isMounted = React.useRef(false);
23
+ var increaseBy = function increaseBy(value) {
24
+ var amount = value != null ? value : 1;
25
+ var newValue = Math.max(counter + amount, 0);
26
+ if (initialValues != null && initialValues.maxCount) {
27
+ newValue = Math.min(newValue, initialValues.maxCount);
28
+ }
29
+ setCounter(newValue);
30
+ onChange && onChange({
31
+ count: newValue,
32
+ product: product
33
+ });
34
+ };
35
+ var reset = function reset() {
36
+ var newValue = (initialValues == null ? void 0 : initialValues.count) || value;
37
+ setCounter(newValue);
38
+ onChange && onChange({
39
+ count: newValue,
40
+ product: product
41
+ });
42
+ };
43
+ // El use ref se puede usar como un elemento que no tiene dependencias de renderizado
44
+ React.useEffect(function () {
45
+ if (!isMounted.current) return;
46
+ setCounter(value);
47
+ }, [value]);
48
+ React.useEffect(function () {
49
+ isMounted.current = true;
50
+ }, []);
51
+ return {
52
+ //Props
53
+ counter: counter,
54
+ maxCount: initialValues == null ? void 0 : initialValues.maxCount,
55
+ //Methods
56
+ increaseBy: increaseBy,
57
+ isMaxCountReached: !!(initialValues != null && initialValues.maxCount) && counter === initialValues.maxCount,
58
+ reset: reset
59
+ };
60
+ };
61
+
62
+ function styleInject(css, ref) {
63
+ if ( ref === void 0 ) ref = {};
64
+ var insertAt = ref.insertAt;
65
+
66
+ if (!css || typeof document === 'undefined') { return; }
67
+
68
+ var head = document.head || document.getElementsByTagName('head')[0];
69
+ var style = document.createElement('style');
70
+ style.type = 'text/css';
71
+
72
+ if (insertAt === 'top') {
73
+ if (head.firstChild) {
74
+ head.insertBefore(style, head.firstChild);
75
+ } else {
76
+ head.appendChild(style);
77
+ }
78
+ } else {
79
+ head.appendChild(style);
80
+ }
81
+
82
+ if (style.styleSheet) {
83
+ style.styleSheet.cssText = css;
84
+ } else {
85
+ style.appendChild(document.createTextNode(css));
86
+ }
87
+ }
88
+
89
+ var css_248z = ".styles-module_productCard__oaIVo {\n background-color: #1e2025;\n border-radius: 15px;\n box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.15);\n color: white;\n padding-bottom: 5px;\n font-family: Arial, Helvetica, sans-serif;\n width: 250px;\n margin-right: 5px;\n margin-top: 5px;\n}\n\n.styles-module_productImg__vPsTp {\n border-radius: 15px 15px 0px 0px;\n width: 100%;\n}\n\n.styles-module_productDescription__Ariub {\n margin: 10px;\n}\n\n.styles-module_buttonsContainer__ghCDF {\n margin: 10px;\n display: flex;\n flex-direction: row;\n}\n\n.styles-module_buttonMinus__BTgTf {\n cursor: pointer;\n background-color: transparent;\n border: 1px solid white;\n border-radius: 5px 0px 0px 5px;\n color: white;\n font-size: 20px;\n width: 30px;\n}\n\n.styles-module_buttonMinus__BTgTf:hover {\n background-color: rgba(0, 0, 0, 0.1);\n}\n\n.styles-module_countLabel__S6HZ- {\n border-bottom: 1px solid white;\n border-top: 1px solid white;\n color: white;\n font-size: 16px;\n height: 25px;\n padding-top: 5px;\n text-align: center;\n width: 30px;\n}\n\n.styles-module_buttonAdd__s8wd6 {\n cursor: pointer;\n background-color: transparent;\n border: 1px solid white;\n border-radius: 0px 5px 5px 0px;\n color: white;\n font-size: 20px;\n width: 30px;\n}\n\n.styles-module_buttonAdd__s8wd6:hover {\n background-color: rgba(0, 0, 0, 0.1);\n}\n\n.styles-module_disabled__k5aZm {\n border-color: grey !important;\n border-left: 1px solid white !important;\n color: grey !important;\n}\n";
90
+ var styles = {"productCard":"styles-module_productCard__oaIVo","productImg":"styles-module_productImg__vPsTp","productDescription":"styles-module_productDescription__Ariub","buttonsContainer":"styles-module_buttonsContainer__ghCDF","buttonMinus":"styles-module_buttonMinus__BTgTf","countLabel":"styles-module_countLabel__S6HZ-","buttonAdd":"styles-module_buttonAdd__s8wd6","disabled":"styles-module_disabled__k5aZm"};
91
+ styleInject(css_248z);
92
+
93
+ // ProductContext.tsx
94
+ var ProductContext = /*#__PURE__*/React.createContext({});
95
+
96
+ var DEFAULT_IMAGE = "https://via.placeholder.com/300x200?text=No+Image";
97
+ var ProductImage = function ProductImage(_ref) {
98
+ var img = _ref.img,
99
+ className = _ref.className,
100
+ style = _ref.style;
101
+ var _useContext = React.useContext(ProductContext),
102
+ product = _useContext.product;
103
+ var imgToShow = "";
104
+ if (img) {
105
+ imgToShow = img;
106
+ } else if (product.img) {
107
+ imgToShow = product.img;
108
+ } else {
109
+ imgToShow = DEFAULT_IMAGE;
110
+ }
111
+ return React__default.createElement("img", {
112
+ className: "\t" + styles.productImg + " " + className,
113
+ src: imgToShow,
114
+ alt: "Product",
115
+ style: style
116
+ });
117
+ };
118
+
119
+ // export const ProductTitle = ({ title, className }: { title?: string, className?: string }): ReactElement => {
120
+ var ProductTitle = function ProductTitle(_ref) {
121
+ var title = _ref.title,
122
+ className = _ref.className,
123
+ style = _ref.style;
124
+ var _useContext = React.useContext(ProductContext),
125
+ product = _useContext.product;
126
+ return React__default.createElement("span", {
127
+ className: styles.productTitle + " " + className,
128
+ style: style
129
+ }, title || product.title);
130
+ };
131
+
132
+ var ProductButtons = function ProductButtons(_ref) {
133
+ var className = _ref.className,
134
+ style = _ref.style;
135
+ var _useContext = React.useContext(ProductContext),
136
+ increaseBy = _useContext.increaseBy,
137
+ counter = _useContext.counter,
138
+ maxCount = _useContext.maxCount;
139
+ //TODO: Una función isMaxReached = useCallback, dependencias [counter, maxCounter]
140
+ //? True si el count === maxCount, caso contrario False
141
+ var isMaxReached = React.useCallback(function () {
142
+ return !!maxCount && counter === maxCount;
143
+ },
144
+ // retorna true o false
145
+ [counter, maxCount]);
146
+ return React__default.createElement("div", {
147
+ className: "\t" + styles.buttonsContainer + " " + className,
148
+ style: style
149
+ }, React__default.createElement("button", {
150
+ className: styles.buttonMinus,
151
+ onClick: function onClick() {
152
+ return increaseBy(-1);
153
+ }
154
+ }, "-"), React__default.createElement("div", {
155
+ className: styles.countLabel
156
+ }, counter), React__default.createElement("button", {
157
+ className: styles.buttonAdd + " " + (isMaxReached() && styles.disabled),
158
+ onClick: function onClick() {
159
+ return increaseBy(1);
160
+ }
161
+ }, "+"));
162
+ };
163
+
164
+ var Provider = ProductContext.Provider;
165
+ var ProductCard = function ProductCard(_ref) {
166
+ var children = _ref.children,
167
+ product = _ref.product,
168
+ className = _ref.className,
169
+ style = _ref.style,
170
+ onChange = _ref.onChange,
171
+ value = _ref.value,
172
+ initialValues = _ref.initialValues;
173
+ var _useProduct = useProduct({
174
+ onChange: onChange,
175
+ product: product,
176
+ value: value,
177
+ initialValues: initialValues
178
+ }),
179
+ counter = _useProduct.counter,
180
+ increaseBy = _useProduct.increaseBy,
181
+ isMaxCountReached = _useProduct.isMaxCountReached,
182
+ maxCount = _useProduct.maxCount,
183
+ reset = _useProduct.reset;
184
+ return React__default.createElement(Provider, {
185
+ value: {
186
+ counter: counter,
187
+ increaseBy: increaseBy,
188
+ maxCount: maxCount,
189
+ product: product
190
+ }
191
+ }, React__default.createElement("div", {
192
+ className: styles.productCard + " " + className,
193
+ style: style
194
+ }, children({
195
+ count: counter,
196
+ isMaxCountReached: isMaxCountReached,
197
+ maxCount: initialValues == null ? void 0 : initialValues.maxCount,
198
+ product: product,
199
+ increaseBy: increaseBy,
200
+ reset: reset
201
+ })));
202
+ };
203
+ ProductCard.Image = ProductImage;
204
+ ProductCard.Title = ProductTitle;
205
+ ProductCard.Buttons = ProductButtons;
206
+
207
+ var ProductCard$1 = /*#__PURE__*/Object.assign(ProductCard, {
208
+ Button: ProductButtons,
209
+ Image: ProductImage,
210
+ Title: ProductTitle
211
+ });
212
+
213
+ exports.ProductButtons = ProductButtons;
214
+ exports.ProductCard = ProductCard$1;
215
+ exports.ProductImage = ProductImage;
216
+ exports.ProductTitle = ProductTitle;
217
+ //# sourceMappingURL=jcd-product-card.cjs.development.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jcd-product-card.cjs.development.js","sources":["../src/hooks/useProduct.ts","../node_modules/style-inject/dist/style-inject.es.js","../src/components/ProductContext.tsx","../src/components/ProductImage.tsx","../src/components/ProductTitle.tsx","../src/components/ProductButtons.tsx","../src/components/ProductCard.tsx","../src/components/index.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\r\nimport { InitialValues, onChangeArgs, Product } from \"../interfaces/interfaces\";\r\n\r\ninterface useProductArgs {\r\n\tproduct: Product;\r\n\tonChange?: (args: onChangeArgs) => void;\r\n\tvalue?: number;\r\n\tinitialValues?: InitialValues;\r\n}\r\n\r\nexport const useProduct = ({\r\n\tonChange,\r\n\tproduct,\r\n\tvalue = 0,\r\n\tinitialValues,\r\n}: useProductArgs) => {\r\n\t// if (initialValues) {\r\n\t// \tvalue = initialValues.count || value;\r\n\t// }\r\n\r\n\tconst [counter, setCounter] = useState<number>(initialValues?.count || value);\r\n\r\n\tconst isMounted = useRef(false);\r\n\r\n\tconst increaseBy = (value?: number) => {\r\n\t\tconst amount = value ?? 1;\r\n\t\tlet newValue = Math.max(counter + amount, 0);\r\n\r\n\t\tif (initialValues?.maxCount) {\r\n\t\t\tnewValue = Math.min(newValue, initialValues.maxCount);\r\n\t\t}\r\n\r\n\t\tsetCounter(newValue);\r\n\r\n\t\tonChange && onChange({ count: newValue, product });\r\n\t};\r\n\r\n\tconst reset = () => {\r\n\t\tconst newValue = initialValues?.count || value;\r\n\t\tsetCounter(newValue);\r\n\t\tonChange && onChange({ count: newValue, product });\r\n\r\n\t}\r\n\r\n\t// El use ref se puede usar como un elemento que no tiene dependencias de renderizado\r\n\tuseEffect(() => {\r\n\t\tif (!isMounted.current) return;\r\n\t\tsetCounter(value);\r\n\t}, [value]);\r\n\r\n\tuseEffect(() => {\r\n\t\tisMounted.current = true;\r\n\t}, []);\r\n\r\n\treturn {\r\n\t\t//Props\r\n\t\tcounter,\r\n\t\tmaxCount: initialValues?.maxCount,\r\n\t\t//Methods\r\n\t\tincreaseBy,\r\n\t\tisMaxCountReached: !!initialValues?.maxCount && counter === initialValues.maxCount,\r\n\t\treset\r\n\t};\r\n};\r\n","function styleInject(css, ref) {\n if ( ref === void 0 ) ref = {};\n var insertAt = ref.insertAt;\n\n if (!css || typeof document === 'undefined') { return; }\n\n var head = document.head || document.getElementsByTagName('head')[0];\n var style = document.createElement('style');\n style.type = 'text/css';\n\n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild);\n } else {\n head.appendChild(style);\n }\n } else {\n head.appendChild(style);\n }\n\n if (style.styleSheet) {\n style.styleSheet.cssText = css;\n } else {\n style.appendChild(document.createTextNode(css));\n }\n}\n\nexport default styleInject;\n","// ProductContext.tsx\r\nimport { createContext } from \"react\";\r\nimport { ProductContextProps } from \"../interfaces/interfaces\";\r\n\r\nexport const ProductContext = createContext({} as ProductContextProps);","import React, { useContext } from \"react\";\r\nimport styles from \"../styles/styles.module.css\";\r\n\r\n// import noImages from \"../assets/no-image.jpg\";\r\nimport { ProductContext } from \"./ProductContext\";\r\n\r\nexport interface Props {\r\n\timg?: string;\r\n\tclassName?: string;\r\n\tstyle?: React.CSSProperties;\r\n}\r\nconst DEFAULT_IMAGE =\r\n \"https://via.placeholder.com/300x200?text=No+Image\";\r\n\r\nexport const ProductImage = ({ img , className, style }: Props) => {\r\n\tconst { product } = useContext(ProductContext);\r\n\tlet imgToShow: string = \"\";\r\n\tif (img) {\r\n\t\timgToShow = img;\r\n\t} else if (product.img) {\r\n\t\timgToShow = product.img;\r\n\t} else {\r\n\t\timgToShow = DEFAULT_IMAGE;\r\n\t}\r\n\r\n\treturn <img className={`\t${styles.productImg} ${className}`} src={imgToShow} alt=\"Product\" style={style} />;\r\n};\r\n","import React, { ReactElement, useContext } from \"react\";\r\nimport styles from \"../styles/styles.module.css\";\r\nimport { ProductContext } from \"./ProductContext\";\r\n\r\nexport interface Props {\r\n\ttitle?: string;\r\n\tclassName?: string;\r\n\tstyle?: React.CSSProperties;\r\n}\r\n\r\n// export const ProductTitle = ({ title, className }: { title?: string, className?: string }): ReactElement => {\r\nexport const ProductTitle = ({ title, className, style }: Props): ReactElement => {\r\n\tconst { product } = useContext(ProductContext);\r\n\r\n\treturn (\r\n\t\t<span className={`${styles.productTitle} ${className}`} style={style}>\r\n\t\t\t{title || product.title}\r\n\t\t</span>\r\n\t);\r\n};\r\n\r\n\r\n","import React, { useCallback, useContext } from \"react\";\r\nimport styles from \"../styles/styles.module.css\";\r\nimport { ProductContext } from \"./ProductContext\";\r\n\r\nexport interface Props {\r\n\t// className para poder recibir estilos personalizados\r\n\tclassName?: string;\r\n\tstyle?: React.CSSProperties;\r\n\tactiveBtnClass?: string;\r\n}\r\n\r\nexport const ProductButtons = ({ className, style }: Props) => {\r\n\r\n\tconst { increaseBy, counter, maxCount } = useContext(ProductContext);\r\n\r\n//TODO: Una función isMaxReached = useCallback, dependencias [counter, maxCounter]\r\n//? True si el count === maxCount, caso contrario False\r\n\r\nconst isMaxReached = useCallback(\r\n\t() => !!maxCount && counter === maxCount, // retorna true o false\r\n\t[counter, maxCount],\r\n)\r\n\r\n\treturn (\r\n\t\t<div className={`\t${styles.buttonsContainer} ${className}`} style={style}>\r\n\t\t\t<button className={styles.buttonMinus} onClick={() => increaseBy(-1)}>\r\n\t\t\t\t-\r\n\t\t\t</button>\r\n\t\t\t<div className={styles.countLabel}>{counter}</div>\r\n\r\n\t\t\t<button className={`${styles.buttonAdd} ${ isMaxReached() && styles.disabled}`} onClick={() => increaseBy(1)}>\r\n\t\t\t\t+\r\n\t\t\t</button>\r\n\t\t</div>\r\n\t);\r\n};\r\n","import React, { JSX } from 'react';\r\nimport { useProduct } from '../hooks/useProduct';\r\nimport {\r\n InitialValues,\r\n onChangeArgs,\r\n Product,\r\n ProductCardHandlers,\r\n} from '../interfaces/interfaces';\r\nimport styles from '../styles/styles.module.css';\r\n\r\nimport { ProductImage } from './ProductImage';\r\nimport { ProductTitle } from './ProductTitle';\r\nimport { ProductButtons } from './ProductButtons';\r\nimport { ProductContext } from './ProductContext';\r\n\r\nconst { Provider } = ProductContext;\r\n\r\nexport interface Props {\r\n product: Product;\r\n // children?: ReactElement | ReactElement[];\r\n children: (args: ProductCardHandlers) => JSX.Element;\r\n className?: string;\r\n style?: React.CSSProperties;\r\n onChange?: (args: onChangeArgs) => void;\r\n value?: number;\r\n initialValues?: InitialValues;\r\n}\r\n\r\nexport const ProductCard = ({\r\n children,\r\n product,\r\n className,\r\n style,\r\n onChange,\r\n value,\r\n initialValues,\r\n}: Props) => {\r\n const {\r\n counter,\r\n increaseBy,\r\n isMaxCountReached,\r\n maxCount,\r\n reset,\r\n } = useProduct({\r\n onChange,\r\n product,\r\n value,\r\n initialValues,\r\n });\r\n\r\n return (\r\n <Provider value={{ counter, increaseBy, maxCount, product }}>\r\n <div className={`${styles.productCard} ${className}`} style={style}>\r\n {/* {children} */}\r\n {children({\r\n count: counter,\r\n isMaxCountReached,\r\n maxCount: initialValues?.maxCount,\r\n product,\r\n increaseBy,\r\n reset,\r\n })}\r\n </div>\r\n </Provider>\r\n );\r\n};\r\n\r\nProductCard.Image = ProductImage;\r\nProductCard.Title = ProductTitle;\r\nProductCard.Buttons = ProductButtons;\r\n","import { ProductCard as ProductCardHOC } from './ProductCard';\r\nimport { ProductCardHOCProps } from '../interfaces/interfaces';\r\nimport { ProductButtons } from './ProductButtons';\r\nimport { ProductImage } from './ProductImage';\r\nimport { ProductTitle } from './ProductTitle';\r\nexport { ProductButtons } from './ProductButtons';\r\nexport { ProductImage } from './ProductImage';\r\nexport { ProductTitle } from './ProductTitle';\r\n\r\nexport const ProductCard: ProductCardHOCProps = Object.assign(ProductCardHOC, {\r\n Button: ProductButtons,\r\n Image: ProductImage,\r\n Title: ProductTitle,\r\n});\r\n\r\nexport default ProductCard;"],"names":["useProduct","_ref","onChange","product","_ref$value","value","initialValues","_useState","useState","count","counter","setCounter","isMounted","useRef","increaseBy","amount","newValue","Math","max","maxCount","min","reset","useEffect","current","isMaxCountReached","ProductContext","createContext","DEFAULT_IMAGE","ProductImage","img","className","style","_useContext","useContext","imgToShow","React","styles","productImg","src","alt","ProductTitle","title","productTitle","ProductButtons","isMaxReached","useCallback","buttonsContainer","buttonMinus","onClick","countLabel","buttonAdd","disabled","Provider","ProductCard","children","_useProduct","productCard","Image","Title","Buttons","Object","assign","ProductCardHOC","Button"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var t,e=require("react"),n=(t=e)&&"object"==typeof t&&"default"in t?t.default:t,o={productCard:"styles-module_productCard__oaIVo",productImg:"styles-module_productImg__vPsTp",productDescription:"styles-module_productDescription__Ariub",buttonsContainer:"styles-module_buttonsContainer__ghCDF",buttonMinus:"styles-module_buttonMinus__BTgTf",countLabel:"styles-module_countLabel__S6HZ-",buttonAdd:"styles-module_buttonAdd__s8wd6",disabled:"styles-module_disabled__k5aZm"};!function(t,e){void 0===e&&(e={});var n=e.insertAt;if("undefined"!=typeof document){var o=document.head||document.getElementsByTagName("head")[0],r=document.createElement("style");r.type="text/css","top"===n&&o.firstChild?o.insertBefore(r,o.firstChild):o.appendChild(r),r.styleSheet?r.styleSheet.cssText=t:r.appendChild(document.createTextNode(t))}}(".styles-module_productCard__oaIVo {\n background-color: #1e2025;\n border-radius: 15px;\n box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.15);\n color: white;\n padding-bottom: 5px;\n font-family: Arial, Helvetica, sans-serif;\n width: 250px;\n margin-right: 5px;\n margin-top: 5px;\n}\n\n.styles-module_productImg__vPsTp {\n border-radius: 15px 15px 0px 0px;\n width: 100%;\n}\n\n.styles-module_productDescription__Ariub {\n margin: 10px;\n}\n\n.styles-module_buttonsContainer__ghCDF {\n margin: 10px;\n display: flex;\n flex-direction: row;\n}\n\n.styles-module_buttonMinus__BTgTf {\n cursor: pointer;\n background-color: transparent;\n border: 1px solid white;\n border-radius: 5px 0px 0px 5px;\n color: white;\n font-size: 20px;\n width: 30px;\n}\n\n.styles-module_buttonMinus__BTgTf:hover {\n background-color: rgba(0, 0, 0, 0.1);\n}\n\n.styles-module_countLabel__S6HZ- {\n border-bottom: 1px solid white;\n border-top: 1px solid white;\n color: white;\n font-size: 16px;\n height: 25px;\n padding-top: 5px;\n text-align: center;\n width: 30px;\n}\n\n.styles-module_buttonAdd__s8wd6 {\n cursor: pointer;\n background-color: transparent;\n border: 1px solid white;\n border-radius: 0px 5px 5px 0px;\n color: white;\n font-size: 20px;\n width: 30px;\n}\n\n.styles-module_buttonAdd__s8wd6:hover {\n background-color: rgba(0, 0, 0, 0.1);\n}\n\n.styles-module_disabled__k5aZm {\n border-color: grey !important;\n border-left: 1px solid white !important;\n color: grey !important;\n}\n");var r=e.createContext({}),u=function(t){var u=t.img,a=t.className,s=t.style,l=e.useContext(r).product;return n.createElement("img",{className:"\t"+o.productImg+" "+a,src:u||(l.img?l.img:"https://via.placeholder.com/300x200?text=No+Image"),alt:"Product",style:s})},a=function(t){var u=t.title,a=t.className,s=t.style,l=e.useContext(r);return n.createElement("span",{className:o.productTitle+" "+a,style:s},u||l.product.title)},s=function(t){var u=t.className,a=t.style,s=e.useContext(r),l=s.increaseBy,d=s.counter,i=s.maxCount,c=e.useCallback((function(){return!!i&&d===i}),[d,i]);return n.createElement("div",{className:"\t"+o.buttonsContainer+" "+u,style:a},n.createElement("button",{className:o.buttonMinus,onClick:function(){return l(-1)}},"-"),n.createElement("div",{className:o.countLabel},d),n.createElement("button",{className:o.buttonAdd+" "+(c()&&o.disabled),onClick:function(){return l(1)}},"+"))},l=r.Provider,d=function(t){var r=t.children,u=t.product,a=t.className,s=t.style,d=t.initialValues,i=function(t){var n=t.onChange,o=t.product,r=t.value,u=void 0===r?0:r,a=t.initialValues,s=e.useState((null==a?void 0:a.count)||u),l=s[0],d=s[1],i=e.useRef(!1);return e.useEffect((function(){i.current&&d(u)}),[u]),e.useEffect((function(){i.current=!0}),[]),{counter:l,maxCount:null==a?void 0:a.maxCount,increaseBy:function(t){var e=Math.max(l+(null!=t?t:1),0);null!=a&&a.maxCount&&(e=Math.min(e,a.maxCount)),d(e),n&&n({count:e,product:o})},isMaxCountReached:!(null==a||!a.maxCount)&&l===a.maxCount,reset:function(){var t=(null==a?void 0:a.count)||u;d(t),n&&n({count:t,product:o})}}}({onChange:t.onChange,product:u,value:t.value,initialValues:d}),c=i.counter,p=i.increaseBy;return n.createElement(l,{value:{counter:c,increaseBy:p,maxCount:i.maxCount,product:u}},n.createElement("div",{className:o.productCard+" "+a,style:s},r({count:c,isMaxCountReached:i.isMaxCountReached,maxCount:null==d?void 0:d.maxCount,product:u,increaseBy:p,reset:i.reset})))};d.Image=u,d.Title=a,d.Buttons=s;var i=Object.assign(d,{Button:s,Image:u,Title:a});exports.ProductButtons=s,exports.ProductCard=i,exports.ProductImage=u,exports.ProductTitle=a;
2
+ //# sourceMappingURL=jcd-product-card.cjs.production.min.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jcd-product-card.cjs.production.min.js","sources":["../node_modules/style-inject/dist/style-inject.es.js","../src/components/ProductContext.tsx","../src/components/ProductImage.tsx","../src/components/ProductTitle.tsx","../src/components/ProductButtons.tsx","../src/components/ProductCard.tsx","../src/hooks/useProduct.ts","../src/components/index.ts"],"sourcesContent":["function styleInject(css, ref) {\n if ( ref === void 0 ) ref = {};\n var insertAt = ref.insertAt;\n\n if (!css || typeof document === 'undefined') { return; }\n\n var head = document.head || document.getElementsByTagName('head')[0];\n var style = document.createElement('style');\n style.type = 'text/css';\n\n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild);\n } else {\n head.appendChild(style);\n }\n } else {\n head.appendChild(style);\n }\n\n if (style.styleSheet) {\n style.styleSheet.cssText = css;\n } else {\n style.appendChild(document.createTextNode(css));\n }\n}\n\nexport default styleInject;\n","// ProductContext.tsx\r\nimport { createContext } from \"react\";\r\nimport { ProductContextProps } from \"../interfaces/interfaces\";\r\n\r\nexport const ProductContext = createContext({} as ProductContextProps);","import React, { useContext } from \"react\";\r\nimport styles from \"../styles/styles.module.css\";\r\n\r\n// import noImages from \"../assets/no-image.jpg\";\r\nimport { ProductContext } from \"./ProductContext\";\r\n\r\nexport interface Props {\r\n\timg?: string;\r\n\tclassName?: string;\r\n\tstyle?: React.CSSProperties;\r\n}\r\nconst DEFAULT_IMAGE =\r\n \"https://via.placeholder.com/300x200?text=No+Image\";\r\n\r\nexport const ProductImage = ({ img , className, style }: Props) => {\r\n\tconst { product } = useContext(ProductContext);\r\n\tlet imgToShow: string = \"\";\r\n\tif (img) {\r\n\t\timgToShow = img;\r\n\t} else if (product.img) {\r\n\t\timgToShow = product.img;\r\n\t} else {\r\n\t\timgToShow = DEFAULT_IMAGE;\r\n\t}\r\n\r\n\treturn <img className={`\t${styles.productImg} ${className}`} src={imgToShow} alt=\"Product\" style={style} />;\r\n};\r\n","import React, { ReactElement, useContext } from \"react\";\r\nimport styles from \"../styles/styles.module.css\";\r\nimport { ProductContext } from \"./ProductContext\";\r\n\r\nexport interface Props {\r\n\ttitle?: string;\r\n\tclassName?: string;\r\n\tstyle?: React.CSSProperties;\r\n}\r\n\r\n// export const ProductTitle = ({ title, className }: { title?: string, className?: string }): ReactElement => {\r\nexport const ProductTitle = ({ title, className, style }: Props): ReactElement => {\r\n\tconst { product } = useContext(ProductContext);\r\n\r\n\treturn (\r\n\t\t<span className={`${styles.productTitle} ${className}`} style={style}>\r\n\t\t\t{title || product.title}\r\n\t\t</span>\r\n\t);\r\n};\r\n\r\n\r\n","import React, { useCallback, useContext } from \"react\";\r\nimport styles from \"../styles/styles.module.css\";\r\nimport { ProductContext } from \"./ProductContext\";\r\n\r\nexport interface Props {\r\n\t// className para poder recibir estilos personalizados\r\n\tclassName?: string;\r\n\tstyle?: React.CSSProperties;\r\n\tactiveBtnClass?: string;\r\n}\r\n\r\nexport const ProductButtons = ({ className, style }: Props) => {\r\n\r\n\tconst { increaseBy, counter, maxCount } = useContext(ProductContext);\r\n\r\n//TODO: Una función isMaxReached = useCallback, dependencias [counter, maxCounter]\r\n//? True si el count === maxCount, caso contrario False\r\n\r\nconst isMaxReached = useCallback(\r\n\t() => !!maxCount && counter === maxCount, // retorna true o false\r\n\t[counter, maxCount],\r\n)\r\n\r\n\treturn (\r\n\t\t<div className={`\t${styles.buttonsContainer} ${className}`} style={style}>\r\n\t\t\t<button className={styles.buttonMinus} onClick={() => increaseBy(-1)}>\r\n\t\t\t\t-\r\n\t\t\t</button>\r\n\t\t\t<div className={styles.countLabel}>{counter}</div>\r\n\r\n\t\t\t<button className={`${styles.buttonAdd} ${ isMaxReached() && styles.disabled}`} onClick={() => increaseBy(1)}>\r\n\t\t\t\t+\r\n\t\t\t</button>\r\n\t\t</div>\r\n\t);\r\n};\r\n","import React, { JSX } from 'react';\r\nimport { useProduct } from '../hooks/useProduct';\r\nimport {\r\n InitialValues,\r\n onChangeArgs,\r\n Product,\r\n ProductCardHandlers,\r\n} from '../interfaces/interfaces';\r\nimport styles from '../styles/styles.module.css';\r\n\r\nimport { ProductImage } from './ProductImage';\r\nimport { ProductTitle } from './ProductTitle';\r\nimport { ProductButtons } from './ProductButtons';\r\nimport { ProductContext } from './ProductContext';\r\n\r\nconst { Provider } = ProductContext;\r\n\r\nexport interface Props {\r\n product: Product;\r\n // children?: ReactElement | ReactElement[];\r\n children: (args: ProductCardHandlers) => JSX.Element;\r\n className?: string;\r\n style?: React.CSSProperties;\r\n onChange?: (args: onChangeArgs) => void;\r\n value?: number;\r\n initialValues?: InitialValues;\r\n}\r\n\r\nexport const ProductCard = ({\r\n children,\r\n product,\r\n className,\r\n style,\r\n onChange,\r\n value,\r\n initialValues,\r\n}: Props) => {\r\n const {\r\n counter,\r\n increaseBy,\r\n isMaxCountReached,\r\n maxCount,\r\n reset,\r\n } = useProduct({\r\n onChange,\r\n product,\r\n value,\r\n initialValues,\r\n });\r\n\r\n return (\r\n <Provider value={{ counter, increaseBy, maxCount, product }}>\r\n <div className={`${styles.productCard} ${className}`} style={style}>\r\n {/* {children} */}\r\n {children({\r\n count: counter,\r\n isMaxCountReached,\r\n maxCount: initialValues?.maxCount,\r\n product,\r\n increaseBy,\r\n reset,\r\n })}\r\n </div>\r\n </Provider>\r\n );\r\n};\r\n\r\nProductCard.Image = ProductImage;\r\nProductCard.Title = ProductTitle;\r\nProductCard.Buttons = ProductButtons;\r\n","import { useEffect, useRef, useState } from \"react\";\r\nimport { InitialValues, onChangeArgs, Product } from \"../interfaces/interfaces\";\r\n\r\ninterface useProductArgs {\r\n\tproduct: Product;\r\n\tonChange?: (args: onChangeArgs) => void;\r\n\tvalue?: number;\r\n\tinitialValues?: InitialValues;\r\n}\r\n\r\nexport const useProduct = ({\r\n\tonChange,\r\n\tproduct,\r\n\tvalue = 0,\r\n\tinitialValues,\r\n}: useProductArgs) => {\r\n\t// if (initialValues) {\r\n\t// \tvalue = initialValues.count || value;\r\n\t// }\r\n\r\n\tconst [counter, setCounter] = useState<number>(initialValues?.count || value);\r\n\r\n\tconst isMounted = useRef(false);\r\n\r\n\tconst increaseBy = (value?: number) => {\r\n\t\tconst amount = value ?? 1;\r\n\t\tlet newValue = Math.max(counter + amount, 0);\r\n\r\n\t\tif (initialValues?.maxCount) {\r\n\t\t\tnewValue = Math.min(newValue, initialValues.maxCount);\r\n\t\t}\r\n\r\n\t\tsetCounter(newValue);\r\n\r\n\t\tonChange && onChange({ count: newValue, product });\r\n\t};\r\n\r\n\tconst reset = () => {\r\n\t\tconst newValue = initialValues?.count || value;\r\n\t\tsetCounter(newValue);\r\n\t\tonChange && onChange({ count: newValue, product });\r\n\r\n\t}\r\n\r\n\t// El use ref se puede usar como un elemento que no tiene dependencias de renderizado\r\n\tuseEffect(() => {\r\n\t\tif (!isMounted.current) return;\r\n\t\tsetCounter(value);\r\n\t}, [value]);\r\n\r\n\tuseEffect(() => {\r\n\t\tisMounted.current = true;\r\n\t}, []);\r\n\r\n\treturn {\r\n\t\t//Props\r\n\t\tcounter,\r\n\t\tmaxCount: initialValues?.maxCount,\r\n\t\t//Methods\r\n\t\tincreaseBy,\r\n\t\tisMaxCountReached: !!initialValues?.maxCount && counter === initialValues.maxCount,\r\n\t\treset\r\n\t};\r\n};\r\n","import { ProductCard as ProductCardHOC } from './ProductCard';\r\nimport { ProductCardHOCProps } from '../interfaces/interfaces';\r\nimport { ProductButtons } from './ProductButtons';\r\nimport { ProductImage } from './ProductImage';\r\nimport { ProductTitle } from './ProductTitle';\r\nexport { ProductButtons } from './ProductButtons';\r\nexport { ProductImage } from './ProductImage';\r\nexport { ProductTitle } from './ProductTitle';\r\n\r\nexport const ProductCard: ProductCardHOCProps = Object.assign(ProductCardHOC, {\r\n Button: ProductButtons,\r\n Image: ProductImage,\r\n Title: ProductTitle,\r\n});\r\n\r\nexport default ProductCard;"],"names":["css","ref","insertAt","document","head","getElementsByTagName","style","createElement","type","firstChild","insertBefore","appendChild","styleSheet","cssText","createTextNode","ProductContext","createContext","ProductImage","_ref","img","className","product","useContext","React","styles","productImg","src","alt","ProductTitle","title","_useContext","productTitle","ProductButtons","increaseBy","counter","maxCount","isMaxReached","useCallback","buttonsContainer","buttonMinus","onClick","countLabel","buttonAdd","disabled","Provider","ProductCard","children","initialValues","_useProduct","onChange","_ref$value","value","_useState","useState","count","setCounter","isMounted","useRef","useEffect","current","newValue","Math","max","min","isMaxCountReached","reset","useProduct","productCard","Image","Title","Buttons","Object","assign","ProductCardHOC","Button"],"mappings":"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"}
@@ -0,0 +1,207 @@
1
+ import React, { useState, useRef, useEffect, createContext, useContext, useCallback } from 'react';
2
+
3
+ var useProduct = function useProduct(_ref) {
4
+ var onChange = _ref.onChange,
5
+ product = _ref.product,
6
+ _ref$value = _ref.value,
7
+ value = _ref$value === void 0 ? 0 : _ref$value,
8
+ initialValues = _ref.initialValues;
9
+ // if (initialValues) {
10
+ // value = initialValues.count || value;
11
+ // }
12
+ var _useState = useState((initialValues == null ? void 0 : initialValues.count) || value),
13
+ counter = _useState[0],
14
+ setCounter = _useState[1];
15
+ var isMounted = useRef(false);
16
+ var increaseBy = function increaseBy(value) {
17
+ var amount = value != null ? value : 1;
18
+ var newValue = Math.max(counter + amount, 0);
19
+ if (initialValues != null && initialValues.maxCount) {
20
+ newValue = Math.min(newValue, initialValues.maxCount);
21
+ }
22
+ setCounter(newValue);
23
+ onChange && onChange({
24
+ count: newValue,
25
+ product: product
26
+ });
27
+ };
28
+ var reset = function reset() {
29
+ var newValue = (initialValues == null ? void 0 : initialValues.count) || value;
30
+ setCounter(newValue);
31
+ onChange && onChange({
32
+ count: newValue,
33
+ product: product
34
+ });
35
+ };
36
+ // El use ref se puede usar como un elemento que no tiene dependencias de renderizado
37
+ useEffect(function () {
38
+ if (!isMounted.current) return;
39
+ setCounter(value);
40
+ }, [value]);
41
+ useEffect(function () {
42
+ isMounted.current = true;
43
+ }, []);
44
+ return {
45
+ //Props
46
+ counter: counter,
47
+ maxCount: initialValues == null ? void 0 : initialValues.maxCount,
48
+ //Methods
49
+ increaseBy: increaseBy,
50
+ isMaxCountReached: !!(initialValues != null && initialValues.maxCount) && counter === initialValues.maxCount,
51
+ reset: reset
52
+ };
53
+ };
54
+
55
+ function styleInject(css, ref) {
56
+ if ( ref === void 0 ) ref = {};
57
+ var insertAt = ref.insertAt;
58
+
59
+ if (!css || typeof document === 'undefined') { return; }
60
+
61
+ var head = document.head || document.getElementsByTagName('head')[0];
62
+ var style = document.createElement('style');
63
+ style.type = 'text/css';
64
+
65
+ if (insertAt === 'top') {
66
+ if (head.firstChild) {
67
+ head.insertBefore(style, head.firstChild);
68
+ } else {
69
+ head.appendChild(style);
70
+ }
71
+ } else {
72
+ head.appendChild(style);
73
+ }
74
+
75
+ if (style.styleSheet) {
76
+ style.styleSheet.cssText = css;
77
+ } else {
78
+ style.appendChild(document.createTextNode(css));
79
+ }
80
+ }
81
+
82
+ var css_248z = ".styles-module_productCard__oaIVo {\n background-color: #1e2025;\n border-radius: 15px;\n box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.15);\n color: white;\n padding-bottom: 5px;\n font-family: Arial, Helvetica, sans-serif;\n width: 250px;\n margin-right: 5px;\n margin-top: 5px;\n}\n\n.styles-module_productImg__vPsTp {\n border-radius: 15px 15px 0px 0px;\n width: 100%;\n}\n\n.styles-module_productDescription__Ariub {\n margin: 10px;\n}\n\n.styles-module_buttonsContainer__ghCDF {\n margin: 10px;\n display: flex;\n flex-direction: row;\n}\n\n.styles-module_buttonMinus__BTgTf {\n cursor: pointer;\n background-color: transparent;\n border: 1px solid white;\n border-radius: 5px 0px 0px 5px;\n color: white;\n font-size: 20px;\n width: 30px;\n}\n\n.styles-module_buttonMinus__BTgTf:hover {\n background-color: rgba(0, 0, 0, 0.1);\n}\n\n.styles-module_countLabel__S6HZ- {\n border-bottom: 1px solid white;\n border-top: 1px solid white;\n color: white;\n font-size: 16px;\n height: 25px;\n padding-top: 5px;\n text-align: center;\n width: 30px;\n}\n\n.styles-module_buttonAdd__s8wd6 {\n cursor: pointer;\n background-color: transparent;\n border: 1px solid white;\n border-radius: 0px 5px 5px 0px;\n color: white;\n font-size: 20px;\n width: 30px;\n}\n\n.styles-module_buttonAdd__s8wd6:hover {\n background-color: rgba(0, 0, 0, 0.1);\n}\n\n.styles-module_disabled__k5aZm {\n border-color: grey !important;\n border-left: 1px solid white !important;\n color: grey !important;\n}\n";
83
+ var styles = {"productCard":"styles-module_productCard__oaIVo","productImg":"styles-module_productImg__vPsTp","productDescription":"styles-module_productDescription__Ariub","buttonsContainer":"styles-module_buttonsContainer__ghCDF","buttonMinus":"styles-module_buttonMinus__BTgTf","countLabel":"styles-module_countLabel__S6HZ-","buttonAdd":"styles-module_buttonAdd__s8wd6","disabled":"styles-module_disabled__k5aZm"};
84
+ styleInject(css_248z);
85
+
86
+ // ProductContext.tsx
87
+ var ProductContext = /*#__PURE__*/createContext({});
88
+
89
+ var DEFAULT_IMAGE = "https://via.placeholder.com/300x200?text=No+Image";
90
+ var ProductImage = function ProductImage(_ref) {
91
+ var img = _ref.img,
92
+ className = _ref.className,
93
+ style = _ref.style;
94
+ var _useContext = useContext(ProductContext),
95
+ product = _useContext.product;
96
+ var imgToShow = "";
97
+ if (img) {
98
+ imgToShow = img;
99
+ } else if (product.img) {
100
+ imgToShow = product.img;
101
+ } else {
102
+ imgToShow = DEFAULT_IMAGE;
103
+ }
104
+ return React.createElement("img", {
105
+ className: "\t" + styles.productImg + " " + className,
106
+ src: imgToShow,
107
+ alt: "Product",
108
+ style: style
109
+ });
110
+ };
111
+
112
+ // export const ProductTitle = ({ title, className }: { title?: string, className?: string }): ReactElement => {
113
+ var ProductTitle = function ProductTitle(_ref) {
114
+ var title = _ref.title,
115
+ className = _ref.className,
116
+ style = _ref.style;
117
+ var _useContext = useContext(ProductContext),
118
+ product = _useContext.product;
119
+ return React.createElement("span", {
120
+ className: styles.productTitle + " " + className,
121
+ style: style
122
+ }, title || product.title);
123
+ };
124
+
125
+ var ProductButtons = function ProductButtons(_ref) {
126
+ var className = _ref.className,
127
+ style = _ref.style;
128
+ var _useContext = useContext(ProductContext),
129
+ increaseBy = _useContext.increaseBy,
130
+ counter = _useContext.counter,
131
+ maxCount = _useContext.maxCount;
132
+ //TODO: Una función isMaxReached = useCallback, dependencias [counter, maxCounter]
133
+ //? True si el count === maxCount, caso contrario False
134
+ var isMaxReached = useCallback(function () {
135
+ return !!maxCount && counter === maxCount;
136
+ },
137
+ // retorna true o false
138
+ [counter, maxCount]);
139
+ return React.createElement("div", {
140
+ className: "\t" + styles.buttonsContainer + " " + className,
141
+ style: style
142
+ }, React.createElement("button", {
143
+ className: styles.buttonMinus,
144
+ onClick: function onClick() {
145
+ return increaseBy(-1);
146
+ }
147
+ }, "-"), React.createElement("div", {
148
+ className: styles.countLabel
149
+ }, counter), React.createElement("button", {
150
+ className: styles.buttonAdd + " " + (isMaxReached() && styles.disabled),
151
+ onClick: function onClick() {
152
+ return increaseBy(1);
153
+ }
154
+ }, "+"));
155
+ };
156
+
157
+ var Provider = ProductContext.Provider;
158
+ var ProductCard = function ProductCard(_ref) {
159
+ var children = _ref.children,
160
+ product = _ref.product,
161
+ className = _ref.className,
162
+ style = _ref.style,
163
+ onChange = _ref.onChange,
164
+ value = _ref.value,
165
+ initialValues = _ref.initialValues;
166
+ var _useProduct = useProduct({
167
+ onChange: onChange,
168
+ product: product,
169
+ value: value,
170
+ initialValues: initialValues
171
+ }),
172
+ counter = _useProduct.counter,
173
+ increaseBy = _useProduct.increaseBy,
174
+ isMaxCountReached = _useProduct.isMaxCountReached,
175
+ maxCount = _useProduct.maxCount,
176
+ reset = _useProduct.reset;
177
+ return React.createElement(Provider, {
178
+ value: {
179
+ counter: counter,
180
+ increaseBy: increaseBy,
181
+ maxCount: maxCount,
182
+ product: product
183
+ }
184
+ }, React.createElement("div", {
185
+ className: styles.productCard + " " + className,
186
+ style: style
187
+ }, children({
188
+ count: counter,
189
+ isMaxCountReached: isMaxCountReached,
190
+ maxCount: initialValues == null ? void 0 : initialValues.maxCount,
191
+ product: product,
192
+ increaseBy: increaseBy,
193
+ reset: reset
194
+ })));
195
+ };
196
+ ProductCard.Image = ProductImage;
197
+ ProductCard.Title = ProductTitle;
198
+ ProductCard.Buttons = ProductButtons;
199
+
200
+ var ProductCard$1 = /*#__PURE__*/Object.assign(ProductCard, {
201
+ Button: ProductButtons,
202
+ Image: ProductImage,
203
+ Title: ProductTitle
204
+ });
205
+
206
+ export { ProductButtons, ProductCard$1 as ProductCard, ProductImage, ProductTitle };
207
+ //# sourceMappingURL=jcd-product-card.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jcd-product-card.esm.js","sources":["../src/hooks/useProduct.ts","../node_modules/style-inject/dist/style-inject.es.js","../src/components/ProductContext.tsx","../src/components/ProductImage.tsx","../src/components/ProductTitle.tsx","../src/components/ProductButtons.tsx","../src/components/ProductCard.tsx","../src/components/index.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\r\nimport { InitialValues, onChangeArgs, Product } from \"../interfaces/interfaces\";\r\n\r\ninterface useProductArgs {\r\n\tproduct: Product;\r\n\tonChange?: (args: onChangeArgs) => void;\r\n\tvalue?: number;\r\n\tinitialValues?: InitialValues;\r\n}\r\n\r\nexport const useProduct = ({\r\n\tonChange,\r\n\tproduct,\r\n\tvalue = 0,\r\n\tinitialValues,\r\n}: useProductArgs) => {\r\n\t// if (initialValues) {\r\n\t// \tvalue = initialValues.count || value;\r\n\t// }\r\n\r\n\tconst [counter, setCounter] = useState<number>(initialValues?.count || value);\r\n\r\n\tconst isMounted = useRef(false);\r\n\r\n\tconst increaseBy = (value?: number) => {\r\n\t\tconst amount = value ?? 1;\r\n\t\tlet newValue = Math.max(counter + amount, 0);\r\n\r\n\t\tif (initialValues?.maxCount) {\r\n\t\t\tnewValue = Math.min(newValue, initialValues.maxCount);\r\n\t\t}\r\n\r\n\t\tsetCounter(newValue);\r\n\r\n\t\tonChange && onChange({ count: newValue, product });\r\n\t};\r\n\r\n\tconst reset = () => {\r\n\t\tconst newValue = initialValues?.count || value;\r\n\t\tsetCounter(newValue);\r\n\t\tonChange && onChange({ count: newValue, product });\r\n\r\n\t}\r\n\r\n\t// El use ref se puede usar como un elemento que no tiene dependencias de renderizado\r\n\tuseEffect(() => {\r\n\t\tif (!isMounted.current) return;\r\n\t\tsetCounter(value);\r\n\t}, [value]);\r\n\r\n\tuseEffect(() => {\r\n\t\tisMounted.current = true;\r\n\t}, []);\r\n\r\n\treturn {\r\n\t\t//Props\r\n\t\tcounter,\r\n\t\tmaxCount: initialValues?.maxCount,\r\n\t\t//Methods\r\n\t\tincreaseBy,\r\n\t\tisMaxCountReached: !!initialValues?.maxCount && counter === initialValues.maxCount,\r\n\t\treset\r\n\t};\r\n};\r\n","function styleInject(css, ref) {\n if ( ref === void 0 ) ref = {};\n var insertAt = ref.insertAt;\n\n if (!css || typeof document === 'undefined') { return; }\n\n var head = document.head || document.getElementsByTagName('head')[0];\n var style = document.createElement('style');\n style.type = 'text/css';\n\n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild);\n } else {\n head.appendChild(style);\n }\n } else {\n head.appendChild(style);\n }\n\n if (style.styleSheet) {\n style.styleSheet.cssText = css;\n } else {\n style.appendChild(document.createTextNode(css));\n }\n}\n\nexport default styleInject;\n","// ProductContext.tsx\r\nimport { createContext } from \"react\";\r\nimport { ProductContextProps } from \"../interfaces/interfaces\";\r\n\r\nexport const ProductContext = createContext({} as ProductContextProps);","import React, { useContext } from \"react\";\r\nimport styles from \"../styles/styles.module.css\";\r\n\r\n// import noImages from \"../assets/no-image.jpg\";\r\nimport { ProductContext } from \"./ProductContext\";\r\n\r\nexport interface Props {\r\n\timg?: string;\r\n\tclassName?: string;\r\n\tstyle?: React.CSSProperties;\r\n}\r\nconst DEFAULT_IMAGE =\r\n \"https://via.placeholder.com/300x200?text=No+Image\";\r\n\r\nexport const ProductImage = ({ img , className, style }: Props) => {\r\n\tconst { product } = useContext(ProductContext);\r\n\tlet imgToShow: string = \"\";\r\n\tif (img) {\r\n\t\timgToShow = img;\r\n\t} else if (product.img) {\r\n\t\timgToShow = product.img;\r\n\t} else {\r\n\t\timgToShow = DEFAULT_IMAGE;\r\n\t}\r\n\r\n\treturn <img className={`\t${styles.productImg} ${className}`} src={imgToShow} alt=\"Product\" style={style} />;\r\n};\r\n","import React, { ReactElement, useContext } from \"react\";\r\nimport styles from \"../styles/styles.module.css\";\r\nimport { ProductContext } from \"./ProductContext\";\r\n\r\nexport interface Props {\r\n\ttitle?: string;\r\n\tclassName?: string;\r\n\tstyle?: React.CSSProperties;\r\n}\r\n\r\n// export const ProductTitle = ({ title, className }: { title?: string, className?: string }): ReactElement => {\r\nexport const ProductTitle = ({ title, className, style }: Props): ReactElement => {\r\n\tconst { product } = useContext(ProductContext);\r\n\r\n\treturn (\r\n\t\t<span className={`${styles.productTitle} ${className}`} style={style}>\r\n\t\t\t{title || product.title}\r\n\t\t</span>\r\n\t);\r\n};\r\n\r\n\r\n","import React, { useCallback, useContext } from \"react\";\r\nimport styles from \"../styles/styles.module.css\";\r\nimport { ProductContext } from \"./ProductContext\";\r\n\r\nexport interface Props {\r\n\t// className para poder recibir estilos personalizados\r\n\tclassName?: string;\r\n\tstyle?: React.CSSProperties;\r\n\tactiveBtnClass?: string;\r\n}\r\n\r\nexport const ProductButtons = ({ className, style }: Props) => {\r\n\r\n\tconst { increaseBy, counter, maxCount } = useContext(ProductContext);\r\n\r\n//TODO: Una función isMaxReached = useCallback, dependencias [counter, maxCounter]\r\n//? True si el count === maxCount, caso contrario False\r\n\r\nconst isMaxReached = useCallback(\r\n\t() => !!maxCount && counter === maxCount, // retorna true o false\r\n\t[counter, maxCount],\r\n)\r\n\r\n\treturn (\r\n\t\t<div className={`\t${styles.buttonsContainer} ${className}`} style={style}>\r\n\t\t\t<button className={styles.buttonMinus} onClick={() => increaseBy(-1)}>\r\n\t\t\t\t-\r\n\t\t\t</button>\r\n\t\t\t<div className={styles.countLabel}>{counter}</div>\r\n\r\n\t\t\t<button className={`${styles.buttonAdd} ${ isMaxReached() && styles.disabled}`} onClick={() => increaseBy(1)}>\r\n\t\t\t\t+\r\n\t\t\t</button>\r\n\t\t</div>\r\n\t);\r\n};\r\n","import React, { JSX } from 'react';\r\nimport { useProduct } from '../hooks/useProduct';\r\nimport {\r\n InitialValues,\r\n onChangeArgs,\r\n Product,\r\n ProductCardHandlers,\r\n} from '../interfaces/interfaces';\r\nimport styles from '../styles/styles.module.css';\r\n\r\nimport { ProductImage } from './ProductImage';\r\nimport { ProductTitle } from './ProductTitle';\r\nimport { ProductButtons } from './ProductButtons';\r\nimport { ProductContext } from './ProductContext';\r\n\r\nconst { Provider } = ProductContext;\r\n\r\nexport interface Props {\r\n product: Product;\r\n // children?: ReactElement | ReactElement[];\r\n children: (args: ProductCardHandlers) => JSX.Element;\r\n className?: string;\r\n style?: React.CSSProperties;\r\n onChange?: (args: onChangeArgs) => void;\r\n value?: number;\r\n initialValues?: InitialValues;\r\n}\r\n\r\nexport const ProductCard = ({\r\n children,\r\n product,\r\n className,\r\n style,\r\n onChange,\r\n value,\r\n initialValues,\r\n}: Props) => {\r\n const {\r\n counter,\r\n increaseBy,\r\n isMaxCountReached,\r\n maxCount,\r\n reset,\r\n } = useProduct({\r\n onChange,\r\n product,\r\n value,\r\n initialValues,\r\n });\r\n\r\n return (\r\n <Provider value={{ counter, increaseBy, maxCount, product }}>\r\n <div className={`${styles.productCard} ${className}`} style={style}>\r\n {/* {children} */}\r\n {children({\r\n count: counter,\r\n isMaxCountReached,\r\n maxCount: initialValues?.maxCount,\r\n product,\r\n increaseBy,\r\n reset,\r\n })}\r\n </div>\r\n </Provider>\r\n );\r\n};\r\n\r\nProductCard.Image = ProductImage;\r\nProductCard.Title = ProductTitle;\r\nProductCard.Buttons = ProductButtons;\r\n","import { ProductCard as ProductCardHOC } from './ProductCard';\r\nimport { ProductCardHOCProps } from '../interfaces/interfaces';\r\nimport { ProductButtons } from './ProductButtons';\r\nimport { ProductImage } from './ProductImage';\r\nimport { ProductTitle } from './ProductTitle';\r\nexport { ProductButtons } from './ProductButtons';\r\nexport { ProductImage } from './ProductImage';\r\nexport { ProductTitle } from './ProductTitle';\r\n\r\nexport const ProductCard: ProductCardHOCProps = Object.assign(ProductCardHOC, {\r\n Button: ProductButtons,\r\n Image: ProductImage,\r\n Title: ProductTitle,\r\n});\r\n\r\nexport default ProductCard;"],"names":["useProduct","_ref","onChange","product","_ref$value","value","initialValues","_useState","useState","count","counter","setCounter","isMounted","useRef","increaseBy","amount","newValue","Math","max","maxCount","min","reset","useEffect","current","isMaxCountReached","ProductContext","createContext","DEFAULT_IMAGE","ProductImage","img","className","style","_useContext","useContext","imgToShow","React","styles","productImg","src","alt","ProductTitle","title","productTitle","ProductButtons","isMaxReached","useCallback","buttonsContainer","buttonMinus","onClick","countLabel","buttonAdd","disabled","Provider","ProductCard","children","_useProduct","productCard","Image","Title","Buttons","Object","assign","ProductCardHOC","Button"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,95 @@
1
+ {
2
+ "version": "0.0.1",
3
+ "license": "MIT",
4
+ "main": "dist/index.js",
5
+ "typings": "dist/index.d.ts",
6
+ "files": [
7
+ "dist",
8
+ "src"
9
+ ],
10
+ "repository": {
11
+ "url": "https://github.com/tatan22/jcd-product-card",
12
+ "type": "git"
13
+ },
14
+ "homepage": "https://www.youtube.com/@tatancarduar",
15
+ "engines": {
16
+ "node": ">=10"
17
+ },
18
+ "scripts": {
19
+ "start": "tsdx watch",
20
+ "build": "tsdx build",
21
+ "test": "tsdx test --passWithNoTests",
22
+ "test:watch": "tsdx test --watch --no-cache",
23
+ "lint": "tsdx lint",
24
+ "prepare": "tsdx build",
25
+ "size": "size-limit",
26
+ "analyze": "size-limit --why"
27
+ },
28
+ "peerDependencies": {
29
+ "react": ">=16"
30
+ },
31
+ "husky": {
32
+ "hooks": {
33
+ "pre-commit": "tsdx lint"
34
+ }
35
+ },
36
+ "prettier": {
37
+ "printWidth": 80,
38
+ "semi": true,
39
+ "singleQuote": true,
40
+ "trailingComma": "es5"
41
+ },
42
+ "name": "@tatan22/jcd-product-card",
43
+ "author": "Jhonatan Cardona",
44
+ "module": "dist/do-product-card.esm.js",
45
+ "size-limit": [
46
+ {
47
+ "path": "dist/do-product-card.cjs.production.min.js",
48
+ "limit": "10 KB"
49
+ },
50
+ {
51
+ "path": "dist/do-product-card.esm.js",
52
+ "limit": "10 KB"
53
+ }
54
+ ],
55
+ "devDependencies": {
56
+ "@babel/preset-env": "^7.28.6",
57
+ "@babel/preset-react": "^7.28.5",
58
+ "@rollup/plugin-image": "^2.1.1",
59
+ "@size-limit/preset-small-lib": "^5.0.5",
60
+ "@testing-library/dom": "^10.4.1",
61
+ "@testing-library/react": "^16.3.1",
62
+ "@types/jest": "^30.0.0",
63
+ "@types/react": "^19.2.8",
64
+ "@types/react-dom": "^19.2.3",
65
+ "@types/react-test-renderer": "^19.1.0",
66
+ "babel-jest": "^30.2.0",
67
+ "husky": "^7.0.2",
68
+ "identity-obj-proxy": "^3.0.0",
69
+ "jest": "^30.2.0",
70
+ "react": "^19.2.3",
71
+ "react-dom": "^19.2.3",
72
+ "react-test-renderer": "^19.2.3",
73
+ "rollup-plugin-images": "^1.0.0",
74
+ "rollup-plugin-postcss": "^4.0.2",
75
+ "size-limit": "^5.0.5",
76
+ "ts-jest": "^29.4.6",
77
+ "tsdx": "^0.14.1",
78
+ "tslib": "^2.3.1",
79
+ "typescript": "^4.4.3"
80
+ },
81
+ "keywords": [
82
+ "product",
83
+ "card",
84
+ "tatancarduar",
85
+ "Jhonatan",
86
+ "Cardona"
87
+ ],
88
+ "dependencies": {},
89
+ "jest": {
90
+ "moduleNameMapper": {
91
+ "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "identity-obj-proxy",
92
+ "\\.(css|less|scss|sass)$": "identity-obj-proxy"
93
+ }
94
+ }
95
+ }
Binary file
@@ -0,0 +1,36 @@
1
+ import React, { useCallback, useContext } from "react";
2
+ import styles from "../styles/styles.module.css";
3
+ import { ProductContext } from "./ProductContext";
4
+
5
+ export interface Props {
6
+ // className para poder recibir estilos personalizados
7
+ className?: string;
8
+ style?: React.CSSProperties;
9
+ activeBtnClass?: string;
10
+ }
11
+
12
+ export const ProductButtons = ({ className, style }: Props) => {
13
+
14
+ const { increaseBy, counter, maxCount } = useContext(ProductContext);
15
+
16
+ //TODO: Una función isMaxReached = useCallback, dependencias [counter, maxCounter]
17
+ //? True si el count === maxCount, caso contrario False
18
+
19
+ const isMaxReached = useCallback(
20
+ () => !!maxCount && counter === maxCount, // retorna true o false
21
+ [counter, maxCount],
22
+ )
23
+
24
+ return (
25
+ <div className={` ${styles.buttonsContainer} ${className}`} style={style}>
26
+ <button className={styles.buttonMinus} onClick={() => increaseBy(-1)}>
27
+ -
28
+ </button>
29
+ <div className={styles.countLabel}>{counter}</div>
30
+
31
+ <button className={`${styles.buttonAdd} ${ isMaxReached() && styles.disabled}`} onClick={() => increaseBy(1)}>
32
+ +
33
+ </button>
34
+ </div>
35
+ );
36
+ };
@@ -0,0 +1,70 @@
1
+ import React, { JSX } from 'react';
2
+ import { useProduct } from '../hooks/useProduct';
3
+ import {
4
+ InitialValues,
5
+ onChangeArgs,
6
+ Product,
7
+ ProductCardHandlers,
8
+ } from '../interfaces/interfaces';
9
+ import styles from '../styles/styles.module.css';
10
+
11
+ import { ProductImage } from './ProductImage';
12
+ import { ProductTitle } from './ProductTitle';
13
+ import { ProductButtons } from './ProductButtons';
14
+ import { ProductContext } from './ProductContext';
15
+
16
+ const { Provider } = ProductContext;
17
+
18
+ export interface Props {
19
+ product: Product;
20
+ // children?: ReactElement | ReactElement[];
21
+ children: (args: ProductCardHandlers) => JSX.Element;
22
+ className?: string;
23
+ style?: React.CSSProperties;
24
+ onChange?: (args: onChangeArgs) => void;
25
+ value?: number;
26
+ initialValues?: InitialValues;
27
+ }
28
+
29
+ export const ProductCard = ({
30
+ children,
31
+ product,
32
+ className,
33
+ style,
34
+ onChange,
35
+ value,
36
+ initialValues,
37
+ }: Props) => {
38
+ const {
39
+ counter,
40
+ increaseBy,
41
+ isMaxCountReached,
42
+ maxCount,
43
+ reset,
44
+ } = useProduct({
45
+ onChange,
46
+ product,
47
+ value,
48
+ initialValues,
49
+ });
50
+
51
+ return (
52
+ <Provider value={{ counter, increaseBy, maxCount, product }}>
53
+ <div className={`${styles.productCard} ${className}`} style={style}>
54
+ {/* {children} */}
55
+ {children({
56
+ count: counter,
57
+ isMaxCountReached,
58
+ maxCount: initialValues?.maxCount,
59
+ product,
60
+ increaseBy,
61
+ reset,
62
+ })}
63
+ </div>
64
+ </Provider>
65
+ );
66
+ };
67
+
68
+ ProductCard.Image = ProductImage;
69
+ ProductCard.Title = ProductTitle;
70
+ ProductCard.Buttons = ProductButtons;
@@ -0,0 +1,5 @@
1
+ // ProductContext.tsx
2
+ import { createContext } from "react";
3
+ import { ProductContextProps } from "../interfaces/interfaces";
4
+
5
+ export const ProductContext = createContext({} as ProductContextProps);
@@ -0,0 +1,27 @@
1
+ import React, { useContext } from "react";
2
+ import styles from "../styles/styles.module.css";
3
+
4
+ // import noImages from "../assets/no-image.jpg";
5
+ import { ProductContext } from "./ProductContext";
6
+
7
+ export interface Props {
8
+ img?: string;
9
+ className?: string;
10
+ style?: React.CSSProperties;
11
+ }
12
+ const DEFAULT_IMAGE =
13
+ "https://via.placeholder.com/300x200?text=No+Image";
14
+
15
+ export const ProductImage = ({ img , className, style }: Props) => {
16
+ const { product } = useContext(ProductContext);
17
+ let imgToShow: string = "";
18
+ if (img) {
19
+ imgToShow = img;
20
+ } else if (product.img) {
21
+ imgToShow = product.img;
22
+ } else {
23
+ imgToShow = DEFAULT_IMAGE;
24
+ }
25
+
26
+ return <img className={` ${styles.productImg} ${className}`} src={imgToShow} alt="Product" style={style} />;
27
+ };
@@ -0,0 +1,22 @@
1
+ import React, { ReactElement, useContext } from "react";
2
+ import styles from "../styles/styles.module.css";
3
+ import { ProductContext } from "./ProductContext";
4
+
5
+ export interface Props {
6
+ title?: string;
7
+ className?: string;
8
+ style?: React.CSSProperties;
9
+ }
10
+
11
+ // export const ProductTitle = ({ title, className }: { title?: string, className?: string }): ReactElement => {
12
+ export const ProductTitle = ({ title, className, style }: Props): ReactElement => {
13
+ const { product } = useContext(ProductContext);
14
+
15
+ return (
16
+ <span className={`${styles.productTitle} ${className}`} style={style}>
17
+ {title || product.title}
18
+ </span>
19
+ );
20
+ };
21
+
22
+
@@ -0,0 +1,16 @@
1
+ import { ProductCard as ProductCardHOC } from './ProductCard';
2
+ import { ProductCardHOCProps } from '../interfaces/interfaces';
3
+ import { ProductButtons } from './ProductButtons';
4
+ import { ProductImage } from './ProductImage';
5
+ import { ProductTitle } from './ProductTitle';
6
+ export { ProductButtons } from './ProductButtons';
7
+ export { ProductImage } from './ProductImage';
8
+ export { ProductTitle } from './ProductTitle';
9
+
10
+ export const ProductCard: ProductCardHOCProps = Object.assign(ProductCardHOC, {
11
+ Button: ProductButtons,
12
+ Image: ProductImage,
13
+ Title: ProductTitle,
14
+ });
15
+
16
+ export default ProductCard;
@@ -0,0 +1,64 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+ import { InitialValues, onChangeArgs, Product } from "../interfaces/interfaces";
3
+
4
+ interface useProductArgs {
5
+ product: Product;
6
+ onChange?: (args: onChangeArgs) => void;
7
+ value?: number;
8
+ initialValues?: InitialValues;
9
+ }
10
+
11
+ export const useProduct = ({
12
+ onChange,
13
+ product,
14
+ value = 0,
15
+ initialValues,
16
+ }: useProductArgs) => {
17
+ // if (initialValues) {
18
+ // value = initialValues.count || value;
19
+ // }
20
+
21
+ const [counter, setCounter] = useState<number>(initialValues?.count || value);
22
+
23
+ const isMounted = useRef(false);
24
+
25
+ const increaseBy = (value?: number) => {
26
+ const amount = value ?? 1;
27
+ let newValue = Math.max(counter + amount, 0);
28
+
29
+ if (initialValues?.maxCount) {
30
+ newValue = Math.min(newValue, initialValues.maxCount);
31
+ }
32
+
33
+ setCounter(newValue);
34
+
35
+ onChange && onChange({ count: newValue, product });
36
+ };
37
+
38
+ const reset = () => {
39
+ const newValue = initialValues?.count || value;
40
+ setCounter(newValue);
41
+ onChange && onChange({ count: newValue, product });
42
+
43
+ }
44
+
45
+ // El use ref se puede usar como un elemento que no tiene dependencias de renderizado
46
+ useEffect(() => {
47
+ if (!isMounted.current) return;
48
+ setCounter(value);
49
+ }, [value]);
50
+
51
+ useEffect(() => {
52
+ isMounted.current = true;
53
+ }, []);
54
+
55
+ return {
56
+ //Props
57
+ counter,
58
+ maxCount: initialValues?.maxCount,
59
+ //Methods
60
+ increaseBy,
61
+ isMaxCountReached: !!initialValues?.maxCount && counter === initialValues.maxCount,
62
+ reset
63
+ };
64
+ };
package/src/index.tsx ADDED
@@ -0,0 +1 @@
1
+ export * from "./components";
@@ -0,0 +1,49 @@
1
+ import { JSX, JSXElementConstructor, ReactElement } from 'react';
2
+ import { Props as ProductCardProps } from '../components/ProductCard';
3
+ import { Props as ProductTitleProps } from '../components/ProductTitle';
4
+ import { Props as ProductImageProps } from '../components/ProductImage';
5
+ import { Props as ProductButtonsProps } from '../components/ProductButtons';
6
+
7
+ export interface Product {
8
+ id: string;
9
+ title: string;
10
+ img?: string;
11
+ }
12
+
13
+ export interface ProductContextProps {
14
+ counter: number;
15
+ maxCount?: number;
16
+ product: Product;
17
+ increaseBy: (value?: number) => void;
18
+ }
19
+
20
+ export interface ProductCardHOCProps {
21
+ ({ children, product }: ProductCardProps): JSX.Element;
22
+ Image: (Props: ProductImageProps) => JSX.Element;
23
+ Button: (Props: ProductButtonsProps) => JSX.Element;
24
+ Title: (
25
+ Props: ProductTitleProps
26
+ ) => ReactElement<unknown, string | JSXElementConstructor<any>>;
27
+ }
28
+
29
+ export interface onChangeArgs {
30
+ product: Product;
31
+ count: number;
32
+ }
33
+
34
+ export interface ProductsInCart extends Product {
35
+ count: number;
36
+ }
37
+ export interface InitialValues {
38
+ count?: number;
39
+ maxCount?: number;
40
+ }
41
+
42
+ export interface ProductCardHandlers {
43
+ count: number;
44
+ isMaxCountReached: boolean;
45
+ maxCount?: number;
46
+ product: Product;
47
+ increaseBy: (value: number) => void;
48
+ reset: () => void;
49
+ }
@@ -0,0 +1,71 @@
1
+ .productCard {
2
+ background-color: #1e2025;
3
+ border-radius: 15px;
4
+ box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.15);
5
+ color: white;
6
+ padding-bottom: 5px;
7
+ font-family: Arial, Helvetica, sans-serif;
8
+ width: 250px;
9
+ margin-right: 5px;
10
+ margin-top: 5px;
11
+ }
12
+
13
+ .productImg {
14
+ border-radius: 15px 15px 0px 0px;
15
+ width: 100%;
16
+ }
17
+
18
+ .productDescription {
19
+ margin: 10px;
20
+ }
21
+
22
+ .buttonsContainer {
23
+ margin: 10px;
24
+ display: flex;
25
+ flex-direction: row;
26
+ }
27
+
28
+ .buttonMinus {
29
+ cursor: pointer;
30
+ background-color: transparent;
31
+ border: 1px solid white;
32
+ border-radius: 5px 0px 0px 5px;
33
+ color: white;
34
+ font-size: 20px;
35
+ width: 30px;
36
+ }
37
+
38
+ .buttonMinus:hover {
39
+ background-color: rgba(0, 0, 0, 0.1);
40
+ }
41
+
42
+ .countLabel {
43
+ border-bottom: 1px solid white;
44
+ border-top: 1px solid white;
45
+ color: white;
46
+ font-size: 16px;
47
+ height: 25px;
48
+ padding-top: 5px;
49
+ text-align: center;
50
+ width: 30px;
51
+ }
52
+
53
+ .buttonAdd {
54
+ cursor: pointer;
55
+ background-color: transparent;
56
+ border: 1px solid white;
57
+ border-radius: 0px 5px 5px 0px;
58
+ color: white;
59
+ font-size: 20px;
60
+ width: 30px;
61
+ }
62
+
63
+ .buttonAdd:hover {
64
+ background-color: rgba(0, 0, 0, 0.1);
65
+ }
66
+
67
+ .disabled {
68
+ border-color: grey !important;
69
+ border-left: 1px solid white !important;
70
+ color: grey !important;
71
+ }
@@ -0,0 +1,9 @@
1
+ declare module '*.css' {
2
+ const content: { [className: string]: string };
3
+ export default content;
4
+ }
5
+
6
+ declare module "*.jpg" {
7
+ const value: any;
8
+ export default value;
9
+ }