Product Item F

Component that represents a product item.

Preview

Code

import { useState } from "react"; import styles from "./product-item-f.module.css"; type Props = { className?: string; imageSrc?: string; title: string; category?: React.ReactNode; price?: string; rating?: string; description?: string; tags?: string[]; href?: string; buyButtonText?: string; isFavorite?: boolean; iconFavoriteOn?: React.ReactNode; iconFavoriteOff?: React.ReactNode; withItemCount?: boolean; iconAdd?: React.ReactNode; iconMinus?: React.ReactNode; iconRemove?: React.ReactNode; variant?: "default" | "simple"; onClickBuy?: () => void; onClickFavorite?: () => void; }; export default function ProductItemF({ className, imageSrc, title, category, price, description, tags, href, isFavorite, rating, iconFavoriteOn, iconFavoriteOff, withItemCount = true, buyButtonText, iconAdd = "+", iconMinus = "-", iconRemove = "x", variant, onClickBuy, onClickFavorite, }: Props) { const [itemCount, setItemCount] = useState(0); const onCountDown = () => { // add your code here, such as sending count data to server if (itemCount > 0) { setItemCount(itemCount - 1); } }; const onCountUp = () => { // add your code here, such as sending count data to server setItemCount(itemCount + 1); }; const onRemoveItem = () => { // add your code here, such as sending count data to server setItemCount(0); }; return ( <div className={[styles["wrapper"], styles[`variant-${variant}`], className].join( " " )} > <div className={styles["image-wrapper"]}> {/* You may need to replace the a tag with a Link if you use Next.js */} <a className={styles["link"]} href={href}> {/* If you use Next.js, replace 'img' with 'Image' element */} <img className={[styles["image"], styles["square"]].join(" ")} src={imageSrc} alt={title} width={360} height={360} /> </a> </div> <div className={styles["body"]}> <div className={styles["content"]}> {/* You may replace the a tag with a Link if you use NextJS */} <a className={styles["link"]} href={href}> <div className={styles["title-line"]}> <h3 className={[styles["t-left"], styles["title"]].join(" ")}> {title} </h3> {price && ( <div className={[styles["t-right"], styles["price"]].join(" ")}> {price} </div> )} </div> {description && <p className={styles["desc"]}>{description}</p>} </a> </div> <div className={styles["footer"]}> <div className={styles["f-left"]}> {category && <h6 className={styles["category"]}>{category}</h6>} {tags && ( <div className={styles["tags"]}> {tags?.map((tag) => ( <span key={tag} className={styles["tag"]}> {tag} </span> ))} </div> )} </div> <div className={styles["f-right"]}> {rating && ( <button className={[styles["favorite"], styles["favorite-btn"]].join( " " )} type="button" onClick={() => { // add on click behavior here eg: onClickFavorite && onClickFavorite(); }} > {(iconFavoriteOn || iconFavoriteOff) && ( <span className={[ styles["favorite-icon"], isFavorite && styles["favorite-active"], ].join(" ")} > {isFavorite ? iconFavoriteOn : iconFavoriteOff} </span> )} <span className={styles["favorite-text"]}>{rating}</span> </button> )} </div> </div> {withItemCount && ( <div className={[styles["footer"], styles["footer-secondary"]].join(" ")} > <div className={styles["f-left"]}> <div className={styles["count-controls"]}> <button type="button" className={[styles["count-btn"], styles["count-down"]].join( " " )} onClick={onCountDown} > {iconMinus || "x"} </button> <span className={styles["count-number"]}>{itemCount}</span> <button type="button" className={[styles["count-btn"], styles["count-up"]].join( " " )} onClick={onCountUp} > {iconAdd || "+"} </button> {itemCount > 0 && ( <button type="button" className={[styles["count-btn"], styles["remove-btn"]].join( " " )} onClick={onRemoveItem} > {iconRemove || "x"} </button> )} </div> </div> <div className={styles["f-right"]}> <div className={styles["btns-group"]}> {buyButtonText && ( <button className={styles["btn-text"]} onClick={() => { // add on click behavior here eg: onClickBuy && onClickBuy(); }}> {buyButtonText} </button> )} </div> </div> </div> )} </div> </div> ); }

Design

Figma design file:

Documentation

Properties

Props of the component:

  • className (string): Specifies the CSS class of the component.
  • imageSrc (string): Specifies the URL of the image.
  • title (string): Specifies the text that will be used as the title.
  • category (string): Specifies the text that will be used as the category.
  • price (string): Specifies the text that will be used as the price.
  • rating (string): Specifies the text that will be used as the string.
  • description (string or ReactNode): Specifies the text or component that will be used as the description.
  • tags (array of string): Specifies text tags.
  • href (string): Specifies the URL if the component is a link.
  • buyButtonText (string): Specifies the "buy" button.
  • isFavorite (boolean): Specifies the item is marked as favorite.
  • iconFavoriteOn (ReactNode): Specifies the "favorite on" icon component.
  • iconFavoriteOff (ReactNode): Specifies the "favorite off" icon component.
  • withItemCount (boolean): Show or hide the item count number and related control buttons.
  • iconAdd (ReactNode): Specifies the add icon component.
  • iconMinus (ReactNode): Specifies the minus icon component.
  • iconRemove (ReactNode): Specifies the remove icon component.
  • variant ("default" | "simple" or a customized value): Specifies the color or theme variant of the component. Check out the "Sample CSS customization" below for an example of how to use it.
  • onClickBuy (function): Fires when the buy button is clicked.
  • onClickFavorite (function): Fires when the favorite button is clicked.
Sample CSS customization
.variant-simple { --padding: 0px; --bg-color: transparent; --image-border-radius: 1rem; box-shadow: none; } .variant-simple:hover .image{ box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.1), 0px 8px 16px -4px rgba(0, 0, 0, 0.2); }