@q2devel/q2-storybook 1.0.66 → 1.0.68
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/dist/components/Base/badge/Badge.d.ts +1 -1
- package/dist/components/Base/badge/Badge.js +2 -2
- package/dist/components/Base/image-gallery-modal/ImageGalleryModal.d.ts +11 -0
- package/dist/components/Base/image-gallery-modal/ImageGalleryModal.js +49 -0
- package/dist/components/Base/modal/Modal.js +1 -1
- package/dist/components/Ecommerce/basket-dialog/BasketDialog.d.ts +3 -3
- package/dist/components/Ecommerce/basket-dialog/BasketDialog.js +24 -29
- package/dist/components/Ecommerce/category-tree/CategoryTree.js +1 -1
- package/dist/components/Ecommerce/product-card/ProductCard.d.ts +8 -1
- package/dist/components/Ecommerce/product-card/ProductCard.js +35 -14
- package/dist/components/Ecommerce/product-detail/ProductDetail.d.ts +76 -0
- package/dist/components/Ecommerce/product-detail/ProductDetail.js +159 -0
- package/dist/components/Forms/contact-form/ContactForm.js +7 -1
- package/dist/components/Forms/text-area/TextArea.d.ts +4 -2
- package/dist/components/Forms/text-area/TextArea.js +16 -4
- package/dist/components/Forms/text-field/TextField.d.ts +4 -2
- package/dist/components/Forms/text-field/TextField.js +53 -51
- package/dist/components/Forms/text-field/TextField.types.d.ts +2 -0
- package/dist/components/Web/banner/Banner.d.ts +1 -1
- package/dist/components/Web/dynamic-grid/DynamicLayout.d.ts +8 -0
- package/dist/components/Web/dynamic-grid/DynamicLayout.js +172 -0
- package/dist/components/Web/footer/Footer.js +86 -1
- package/dist/components/Web/nav-bar/NavBar.d.ts +1 -1
- package/dist/components/Web/nav-bar/NavBar.js +6 -6
- package/dist/components/Web/web-section/WebSection.d.ts +5 -2
- package/dist/components/Web/web-section/WebSection.js +17 -3
- package/dist/components/label/Label.js +2 -2
- package/dist/index.d.ts +36 -27
- package/dist/index.js +37 -28
- package/dist/server/grid-endpoint.d.ts +1 -0
- package/dist/server/grid-endpoint.js +35 -0
- package/dist/style.css +1 -1
- package/dist/utils/generalHelperFunction.d.ts +15 -1
- package/dist/utils/generalHelperFunction.js +69 -8
- package/package.json +5 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
const Badge = ({ children, onClick, color =
|
|
3
|
+
const Badge = ({ children, onClick, color = "bg-blue-100 text-blue-800", className = "", labelClassName = "", rightIcon, }) => (_jsxs("span", { className: `inline-flex items-center rounded px-2.5 py-0.5 text-xs font-medium ${color} ${className}`, onClick: onClick, children: [_jsx("span", { className: labelClassName, children: children }), rightIcon && _jsx("span", { className: "ml-1", children: rightIcon })] }));
|
|
4
4
|
export default Badge;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type ImageGalleryProps = {
|
|
2
|
+
images: Array<{
|
|
3
|
+
href: string;
|
|
4
|
+
alt: string;
|
|
5
|
+
}>;
|
|
6
|
+
isOpen: boolean;
|
|
7
|
+
initialIndex?: number;
|
|
8
|
+
onClose: () => void;
|
|
9
|
+
onImageChange?: (index: number) => void;
|
|
10
|
+
};
|
|
11
|
+
export declare const ImageGalleryModal: ({ images, isOpen, initialIndex, onClose, onImageChange, }: ImageGalleryProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Dialog, DialogBackdrop, DialogPanel } from '@headlessui/react';
|
|
4
|
+
import { ChevronLeftIcon, ChevronRightIcon, XMarkIcon } from '@heroicons/react/24/outline';
|
|
5
|
+
import { useEffect, useState } from 'react';
|
|
6
|
+
export const ImageGalleryModal = ({ images, isOpen, initialIndex = 0, onClose, onImageChange, }) => {
|
|
7
|
+
const [currentImageIndex, setCurrentImageIndex] = useState(initialIndex);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
setCurrentImageIndex(initialIndex);
|
|
10
|
+
}, [initialIndex]);
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const handleKeyDown = (event) => {
|
|
13
|
+
if (!isOpen)
|
|
14
|
+
return;
|
|
15
|
+
switch (event.key) {
|
|
16
|
+
case 'ArrowLeft':
|
|
17
|
+
goToPrevImage();
|
|
18
|
+
break;
|
|
19
|
+
case 'ArrowRight':
|
|
20
|
+
goToNextImage();
|
|
21
|
+
break;
|
|
22
|
+
case 'Escape':
|
|
23
|
+
onClose();
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
28
|
+
return () => document.removeEventListener('keydown', handleKeyDown);
|
|
29
|
+
}, [isOpen]);
|
|
30
|
+
const goToNextImage = () => {
|
|
31
|
+
const newIndex = currentImageIndex === images.length - 1 ? 0 : currentImageIndex + 1;
|
|
32
|
+
setCurrentImageIndex(newIndex);
|
|
33
|
+
onImageChange?.(newIndex);
|
|
34
|
+
};
|
|
35
|
+
const goToPrevImage = () => {
|
|
36
|
+
const newIndex = currentImageIndex === 0 ? images.length - 1 : currentImageIndex - 1;
|
|
37
|
+
setCurrentImageIndex(newIndex);
|
|
38
|
+
onImageChange?.(newIndex);
|
|
39
|
+
};
|
|
40
|
+
const goToImage = (index) => {
|
|
41
|
+
setCurrentImageIndex(index);
|
|
42
|
+
onImageChange?.(index);
|
|
43
|
+
};
|
|
44
|
+
if (!isOpen || images.length === 0)
|
|
45
|
+
return null;
|
|
46
|
+
return (_jsxs(Dialog, { open: isOpen, onClose: onClose, className: "relative z-50", children: [_jsx(DialogBackdrop, { className: "fixed inset-0 bg-black/75" }), _jsx("div", { className: "fixed inset-0 flex items-center justify-center p-4", children: _jsxs(DialogPanel, { className: "relative w-full max-w-7xl max-h-full", children: [_jsx("button", { onClick: onClose, className: "absolute top-4 right-4 z-10 bg-black/50 text-white p-2 rounded-full hover:bg-black/70 transition-colors", "aria-label": "Close gallery", children: _jsx(XMarkIcon, { className: "h-6 w-6" }) }), _jsxs("div", { className: "relative bg-white rounded-lg overflow-hidden", children: [_jsx("img", { src: images[currentImageIndex]?.href, alt: images[currentImageIndex]?.alt, className: "w-full h-auto max-h-[80vh] object-contain" }), images.length > 1 && (_jsxs(_Fragment, { children: [_jsx("button", { onClick: goToPrevImage, className: "absolute left-4 top-1/2 -translate-y-1/2 bg-black/50 text-white p-3 rounded-full hover:bg-black/70 transition-colors", "aria-label": "Previous image", children: _jsx(ChevronLeftIcon, { className: "h-6 w-6" }) }), _jsx("button", { onClick: goToNextImage, className: "absolute right-4 top-1/2 -translate-y-1/2 bg-black/50 text-white p-3 rounded-full hover:bg-black/70 transition-colors", "aria-label": "Next image", children: _jsx(ChevronRightIcon, { className: "h-6 w-6" }) })] }))] }), images.length > 1 && (_jsx("div", { className: "mt-4 flex justify-center gap-2 flex-wrap max-h-24 overflow-y-auto", children: images.map((image, index) => (_jsx("button", { onClick: () => goToImage(index), className: `flex-shrink-0 w-16 h-16 border-2 rounded overflow-hidden transition-colors ${index === currentImageIndex
|
|
47
|
+
? 'border-[var(--primary)]'
|
|
48
|
+
: 'border-gray-300 hover:border-gray-400'}`, "aria-label": `View image ${index + 1}`, children: _jsx("img", { src: image.href, alt: image.alt, className: "w-full h-full object-contain" }) }, index))) })), _jsxs("div", { className: "absolute bottom-4 left-1/2 -translate-x-1/2 bg-black/50 text-white px-3 py-1 rounded-full text-sm", children: [currentImageIndex + 1, " / ", images.length] })] }) })] }));
|
|
49
|
+
};
|
|
@@ -7,6 +7,6 @@ export const Modal = ({ title, description, descriptionClassName, buttonText, sh
|
|
|
7
7
|
const handleClose = (value) => {
|
|
8
8
|
onClose?.();
|
|
9
9
|
};
|
|
10
|
-
return (_jsxs(Dialog, { open: isOpen, onClose: handleClose, className: buildClassesByJoining('relative z-10', className), children: [_jsx(DialogBackdrop, { transition: true, className: buildClassesByJoining('fixed inset-0 bg-gray-500/75 transition-opacity data-closed:opacity-0 data-enter:duration-300 data-enter:ease-out data-leave:duration-200 data-leave:ease-in', backdropClassName) }), _jsx("div", { className: "fixed inset-0 z-10 w-screen overflow-y-auto", children: _jsx("div", { className: "flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0", children: _jsxs(DialogPanel, { transition: true, className: buildClassesByJoining('relative transform overflow-hidden rounded-lg bg-white px-4 pt-5 pb-4 text-left shadow-xl transition-all data-closed:translate-y-4 data-closed:opacity-0 data-enter:duration-300 data-enter:ease-out data-leave:duration-200 data-leave:ease-in sm:my-8 sm:w-full sm:max-w-sm sm:p-6 data-closed:sm:translate-y-0 data-closed:sm:scale-95', panelClassName), children: [showCloseButton && (_jsxs("button", { type: "button", className: buildClassesByJoining('absolute top-4 right-4 text-gray-400 hover:text-gray-500 focus:outline-none', closeButtonClassName), onClick: () => onClose?.(), children: [_jsx("span", { className: "sr-only", children: "Close" }), _jsx(XMarkIcon, { className: "h-6 w-6", "aria-hidden": "true" })] })), _jsxs("div", { children: [showIcon && (_jsx("div", { className: buildClassesByJoining('mx-auto flex size-12 items-center justify-center rounded-full', iconBackgroundColor), children: _jsx("div", { className: iconColor, children: icon }) })), _jsxs("div", { className: "mt-3 text-center sm:mt-5", children: [title && (_jsx(DialogTitle, { as: "h3", className: "text-base font-semibold text-gray-900", children: title })), _jsx("div", { className: buildClassesByJoining('mt-2', descriptionClassName), children: description })] })] }), buttonText && (_jsx("div", { className: "mt-5 sm:mt-6", children: _jsx("button", { type: "button", onClick: () => onClose?.(), className: buildClassesByJoining('inline-flex w-full justify-center rounded-md bg-green-600 px-3 py-2 text-sm font-semibold text-white shadow-xs hover:bg-green-500 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600', buttonClassName), children: buttonText }) }))] }) }) })] }));
|
|
10
|
+
return (_jsxs(Dialog, { open: isOpen, onClose: handleClose, className: buildClassesByJoining('relative z-10', className), children: [_jsx(DialogBackdrop, { transition: true, className: buildClassesByJoining('fixed inset-0 bg-gray-500/75 transition-opacity data-closed:opacity-0 data-enter:duration-300 data-enter:ease-out data-leave:duration-200 data-leave:ease-in', backdropClassName) }), _jsx("div", { className: "fixed inset-0 z-10 w-screen overflow-y-auto", children: _jsx("div", { className: "flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0", children: _jsxs(DialogPanel, { transition: true, className: buildClassesByJoining('relative transform overflow-hidden rounded-lg bg-white px-4 pt-5 pb-4 text-left shadow-xl transition-all data-closed:translate-y-4 data-closed:opacity-0 data-enter:duration-300 data-enter:ease-out data-leave:duration-200 data-leave:ease-in sm:my-8 sm:w-full sm:max-w-sm sm:p-6 data-closed:sm:translate-y-0 data-closed:sm:scale-95', panelClassName), children: [showCloseButton && (_jsxs("button", { type: "button", className: buildClassesByJoining('absolute top-4 right-4 text-gray-400 hover:text-gray-500 focus:outline-none', closeButtonClassName), onClick: () => onClose?.(), children: [_jsx("span", { className: "sr-only", children: "Close" }), _jsx(XMarkIcon, { className: "h-6 w-6", "aria-hidden": "true" })] })), _jsxs("div", { children: [showIcon && (_jsx("div", { className: buildClassesByJoining('mx-auto flex size-12 items-center justify-center rounded-full', iconBackgroundColor), children: _jsx("div", { className: iconColor, children: icon }) })), _jsxs("div", { className: "mt-3 text-center sm:mt-5", children: [title && (_jsx(DialogTitle, { as: "h3", className: "text-base font-semibold text-gray-900", children: title })), _jsx("div", { className: buildClassesByJoining('mt-2', descriptionClassName), children: description })] })] }), buttonText && (_jsx("div", { className: "mt-5 sm:mt-6", children: _jsx("button", { type: "button", onClick: () => onClose?.(), className: buildClassesByJoining('inline-flex w-full justify-center rounded-md bg-green-600 px-3 py-2 text-sm font-semibold text-white shadow-xs hover:bg-green-500 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600', buttonClassName), children: buttonText }) })), buttonText && (_jsx("div", { className: "mt-5 sm:mt-6", children: _jsx("button", { type: "button", onClick: () => onClose?.(), className: buildClassesByJoining('inline-flex w-full justify-center rounded-md bg-green-600 px-3 py-2 text-sm font-semibold text-white shadow-xs hover:bg-green-500 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600', buttonClassName), children: buttonText }) }))] }) }) })] }));
|
|
11
11
|
};
|
|
12
12
|
export default Modal;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from
|
|
1
|
+
import React from "react";
|
|
2
2
|
export type BasketProduct = {
|
|
3
3
|
id: string;
|
|
4
4
|
name: string;
|
|
@@ -12,8 +12,8 @@ export type BasketProduct = {
|
|
|
12
12
|
color?: string;
|
|
13
13
|
[key: string]: any;
|
|
14
14
|
};
|
|
15
|
-
export type BasketDialogVariant =
|
|
16
|
-
export type BasketDialogPosition =
|
|
15
|
+
export type BasketDialogVariant = "default" | "compact" | "full";
|
|
16
|
+
export type BasketDialogPosition = "right" | "left";
|
|
17
17
|
export type BasketDialogProps = {
|
|
18
18
|
isOpen: boolean;
|
|
19
19
|
onClose: () => void;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { Dialog, DialogBackdrop, DialogPanel, DialogTitle } from
|
|
4
|
-
import { MinusIcon, PlusIcon, XMarkIcon } from
|
|
3
|
+
import { Dialog, DialogBackdrop, DialogPanel, DialogTitle, } from "@headlessui/react";
|
|
4
|
+
import { MinusIcon, PlusIcon, XMarkIcon } from "@heroicons/react/24/outline";
|
|
5
5
|
// Helper funkcia pre formátovanie ceny
|
|
6
6
|
const formatPrice = (price) => {
|
|
7
|
-
return new Intl.NumberFormat(
|
|
8
|
-
style:
|
|
9
|
-
currency:
|
|
7
|
+
return new Intl.NumberFormat("cs-CZ", {
|
|
8
|
+
style: "currency",
|
|
9
|
+
currency: "CZK",
|
|
10
10
|
}).format(price);
|
|
11
11
|
};
|
|
12
12
|
export const BasketDialog = ({
|
|
@@ -15,11 +15,11 @@ isOpen, onClose, products, subtotal,
|
|
|
15
15
|
// Callback funkcie
|
|
16
16
|
onQuantityChange, onRemoveProduct, onCheckout, onContinueShopping, onProductClick,
|
|
17
17
|
// Texty s predvolenými hodnotami
|
|
18
|
-
title =
|
|
18
|
+
title = "Váš košík", checkoutText = "Pokračovat k objednávce", continueShoppingText = "Pokračovat v nákupu", emptyCartText = "Váš košík je prázdný", quantityLabel = "Množství:", removeButtonText = "Odstranit", totalLabel = "Celkem", shippingNoteText = "Doprava a daně budou vypočítány při dokončení objednávky.",
|
|
19
19
|
// Zobrazenie/konfigurácia s predvolenými hodnotami
|
|
20
|
-
variant =
|
|
20
|
+
variant = "default", position = "right", showProductImages = true, showProductColors = true, showProductLinks = true, showRemoveButton = true, showSubtotal = true, showCheckoutButton = true, showContinueShoppingButton = true, showShippingNote = true, showQuantityControls = true,
|
|
21
21
|
// Štýlovanie s predvolenými hodnotami
|
|
22
|
-
customClassName =
|
|
22
|
+
customClassName = "", maxHeight = "100vh", animationDuration = 500, backdropOpacity = 0.75,
|
|
23
23
|
// Vlastné renderovanie
|
|
24
24
|
renderCustomProduct, renderCustomFooter,
|
|
25
25
|
// Stav
|
|
@@ -54,16 +54,16 @@ pendingUpdates = {}, processingItems = {}, }) => {
|
|
|
54
54
|
};
|
|
55
55
|
const getVariantClasses = () => {
|
|
56
56
|
switch (variant) {
|
|
57
|
-
case
|
|
58
|
-
return
|
|
59
|
-
case
|
|
60
|
-
return
|
|
57
|
+
case "compact":
|
|
58
|
+
return "max-w-sm";
|
|
59
|
+
case "full":
|
|
60
|
+
return "max-w-2xl";
|
|
61
61
|
default:
|
|
62
|
-
return
|
|
62
|
+
return "max-w-md";
|
|
63
63
|
}
|
|
64
64
|
};
|
|
65
65
|
const getPositionClasses = () => {
|
|
66
|
-
return position ===
|
|
66
|
+
return position === "left" ? "left-0" : "right-0";
|
|
67
67
|
};
|
|
68
68
|
return (_jsxs(Dialog, { open: isOpen, onClose: onClose, className: "relative z-60", children: [_jsx(DialogBackdrop, { transition: true, className: `fixed inset-0 bg-gray-500/75 transition-opacity duration-${animationDuration} ease-in-out data-closed:opacity-0`, style: { opacity: backdropOpacity } }), _jsx("div", { className: "fixed inset-0 overflow-hidden", children: _jsx("div", { className: "absolute inset-0 overflow-hidden", children: _jsx("div", { className: `pointer-events-none fixed inset-y-0 ${getPositionClasses()} flex max-w-full pl-10`, children: _jsx(DialogPanel, { transition: true, className: `pointer-events-auto w-screen ${getVariantClasses()} transform transition duration-${animationDuration} ease-in-out data-closed:translate-x-full sm:duration-700 ${customClassName}`, children: _jsxs("div", { className: "flex h-full flex-col overflow-y-scroll bg-white shadow-xl", style: { maxHeight }, children: [_jsxs("div", { className: "flex-1 overflow-y-auto px-4 py-6 sm:px-6", children: [_jsxs("div", { className: "flex items-start justify-between", children: [_jsx(DialogTitle, { className: "text-lg font-medium text-gray-900", children: title }), _jsx("div", { className: "ml-3 flex h-7 items-center", children: _jsxs("button", { type: "button", onClick: onClose, className: "relative -m-2 p-2 text-gray-400 hover:text-gray-500", children: [_jsx("span", { className: "absolute -inset-0.5" }), _jsx("span", { className: "sr-only", children: "Close panel" }), _jsx(XMarkIcon, { "aria-hidden": "true", className: "size-6" })] }) })] }), _jsx("div", { className: "mt-8", children: _jsx("div", { className: "flow-root", children: products.length === 0 ? (_jsx("div", { className: "py-6 text-center text-gray-500", children: emptyCartText })) : (_jsx("ul", { role: "list", className: "-my-6 divide-y divide-gray-200", children: products.map((product) => {
|
|
69
69
|
if (renderCustomProduct) {
|
|
@@ -74,28 +74,23 @@ pendingUpdates = {}, processingItems = {}, }) => {
|
|
|
74
74
|
: product.quantity;
|
|
75
75
|
const isProcessing = processingItems[product.id] === true;
|
|
76
76
|
return (_jsxs("li", { className: "flex py-6", children: [showProductImages &&
|
|
77
|
-
product.variaton_image && (_jsx("div", { className: "size-24 shrink-0 overflow-hidden rounded-md border border-gray-200", children: _jsx("img", { alt: product.variaton_image
|
|
78
|
-
.alt, src: product.variaton_image
|
|
79
|
-
.src, className: "size-full object-cover" }) })), _jsxs("div", { className: "ml-4 flex flex-1 flex-col", children: [_jsxs("div", { children: [_jsxs("div", { className: "flex justify-between text-base font-medium text-gray-900", children: [_jsx("h3", { children: showProductLinks &&
|
|
80
|
-
product.href ? (_jsx("a", { href: product.href, onClick: (e) => {
|
|
77
|
+
product.variaton_image && (_jsx("div", { className: "size-24 shrink-0 overflow-hidden rounded-md border border-gray-200", children: _jsx("img", { alt: product.variaton_image.alt, src: product.variaton_image.src, className: "size-full object-cover" }) })), _jsxs("div", { className: "ml-4 flex flex-1 flex-col", children: [_jsxs("div", { children: [_jsxs("div", { className: "flex justify-between text-base font-medium text-gray-900", children: [_jsx("h3", { children: showProductLinks && product.href ? (_jsx("a", { href: product.href, onClick: (e) => {
|
|
81
78
|
if (onProductClick) {
|
|
82
79
|
e.preventDefault();
|
|
83
80
|
onProductClick(product);
|
|
84
81
|
}
|
|
85
|
-
}, children: product.name })) : (product.name) }), _jsx("p", { className: "ml-4", children: formatPrice(product.price) })] }), showProductColors &&
|
|
86
|
-
product.color && (_jsx("p", { className: "mt-1 text-sm text-gray-500", children: product.color }))] }), _jsxs("div", { className: "flex flex-1 items-end justify-between text-sm", children: [showQuantityControls &&
|
|
82
|
+
}, children: product.name })) : (product.name) }), _jsx("p", { className: "ml-4", children: formatPrice(product.price) })] }), showProductColors && product.color && (_jsx("p", { className: "mt-1 text-sm text-gray-500", children: product.color }))] }), _jsxs("div", { className: "flex flex-1 items-end justify-between text-sm", children: [showQuantityControls &&
|
|
87
83
|
onQuantityChange ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("p", { className: "text-gray-500 mr-2", children: quantityLabel }), _jsxs("div", { className: "flex items-center", children: [_jsx("button", { type: "button", onClick: () => !isProcessing &&
|
|
88
84
|
handleDecrement(product.id, displayQuantity), disabled: isProcessing, className: `rounded-full p-1 text-gray-600 ${isProcessing
|
|
89
|
-
?
|
|
90
|
-
:
|
|
85
|
+
? "bg-gray-100 cursor-not-allowed opacity-50"
|
|
86
|
+
: "bg-gray-200 hover:bg-gray-300"}`, children: _jsx(MinusIcon, { className: "h-4 w-4" }) }), _jsx("span", { className: "mx-2 min-w-[20px] text-center text-black", children: displayQuantity }), _jsx("button", { type: "button", onClick: () => !isProcessing &&
|
|
91
87
|
handleIncrement(product.id, displayQuantity), disabled: isProcessing, className: `rounded-full p-1 text-gray-600 ${isProcessing
|
|
92
|
-
?
|
|
93
|
-
:
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
: 'text-green-600 hover:text-green-500'}`, onClick: () => !isProcessing &&
|
|
88
|
+
? "bg-gray-100 cursor-not-allowed opacity-50"
|
|
89
|
+
: "bg-gray-200 hover:bg-gray-300"}`, children: _jsx(PlusIcon, { className: "h-4 w-4" }) })] })] })) : (_jsxs("p", { className: "text-gray-500", children: [quantityLabel, " ", displayQuantity] })), showRemoveButton && onRemoveProduct && (_jsx("div", { className: "flex", children: _jsx("button", { type: "button", className: `font-medium ${isProcessing
|
|
90
|
+
? "text-gray-400 cursor-not-allowed"
|
|
91
|
+
: "text-green-600 hover:text-green-500"}`, onClick: () => !isProcessing &&
|
|
97
92
|
onRemoveProduct(product.id), disabled: isProcessing, children: removeButtonText }) }))] })] })] }, product.id));
|
|
98
|
-
}) })) }) })] }), _jsxs("div", { className: "border-t border-gray-200 px-4 py-6 sm:px-6", children: [showSubtotal && (_jsxs("div", { className: "flex justify-between text-base font-medium text-gray-900", children: [_jsx("p", { children: totalLabel }), _jsx("p", { children: typeof subtotal ===
|
|
93
|
+
}) })) }) })] }), _jsxs("div", { className: "border-t border-gray-200 px-4 py-6 sm:px-6", children: [showSubtotal && (_jsxs("div", { className: "flex justify-between text-base font-medium text-gray-900", children: [_jsx("p", { children: totalLabel }), _jsx("p", { children: typeof subtotal === "number"
|
|
99
94
|
? formatPrice(subtotal)
|
|
100
95
|
: subtotal })] })), showShippingNote && (_jsx("p", { className: "mt-0.5 text-sm text-gray-500", children: shippingNoteText })), products.length > 0 && showCheckoutButton && onCheckout && (_jsx("div", { className: "mt-6", children: _jsx("button", { onClick: handleCheckout, className: "flex w-full items-center justify-center rounded-md border border-transparent bg-green-600 px-6 py-3 text-base font-medium text-white shadow-xs hover:bg-green-700", children: checkoutText }) })), showContinueShoppingButton && (_jsx("div", { className: "mt-6 flex justify-center text-center text-sm text-gray-500", children: _jsx("p", { children: _jsxs("button", { type: "button", onClick: handleContinueShopping, className: "font-medium text-green-600 hover:text-green-500", children: [continueShoppingText, _jsx("span", { "aria-hidden": "true", children: " \u2192" })] }) }) })), renderCustomFooter && renderCustomFooter()] })] }) }) }) }) })] }));
|
|
101
96
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Product } from '@q2devel/q2-core';
|
|
2
2
|
import React from 'react';
|
|
3
|
+
import { PriceFormatOptions } from '../../../utils/generalHelperFunction';
|
|
3
4
|
export type Wishlist = {
|
|
4
5
|
id: string;
|
|
5
6
|
name: string;
|
|
@@ -34,6 +35,10 @@ export type ProductCardProps = {
|
|
|
34
35
|
actionButton?: React.ReactNode;
|
|
35
36
|
showAddButton?: boolean;
|
|
36
37
|
showAddButtonOnHover?: boolean;
|
|
38
|
+
addButtonPosition?: 'overlay' | 'bottom';
|
|
39
|
+
addButtonAlignment?: 'left' | 'center' | 'right';
|
|
40
|
+
addButtonText?: string;
|
|
41
|
+
addButtonBottomClassName?: string;
|
|
37
42
|
borderOnHoverClassName?: string;
|
|
38
43
|
currentQuantity: number;
|
|
39
44
|
onQuantityChange?: (product: Product, quantity: number) => void;
|
|
@@ -65,5 +70,7 @@ export type ProductCardProps = {
|
|
|
65
70
|
isDeletePending?: boolean;
|
|
66
71
|
deleteButtonSize?: 'sm' | 'md' | 'lg';
|
|
67
72
|
deleteButtonPosition?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left';
|
|
73
|
+
priceFormatOptions?: PriceFormatOptions;
|
|
74
|
+
quantUnit?: string;
|
|
68
75
|
};
|
|
69
|
-
export declare const ProductCard: ({ product, href, className, imageClassName, contentClassName, titleClassName,
|
|
76
|
+
export declare const ProductCard: ({ product, href, className, imageClassName, contentClassName, titleClassName, priceClassName, descriptionClassName, badgeClassName, aspectRatio, imagePosition, imageObjectFit, layout, hoverEffect, renderBadges, renderColor, renderDescription, elevationLevel, rounded, titleLines, onCardClick, onImageClick, renderFooter, renderHeader, renderVariationName, actionButton, showAddButton, showAddButtonOnHover, addButtonPosition, addButtonAlignment, addButtonText, addButtonBottomClassName, borderOnHoverClassName, currentQuantity, onQuantityChange, addButtonIcon, maxQuantity, controlsClassName, buttonClassName, quantityClassName, customAddButton, disabled, locale, cartMode, showSpinnerInAdd, showWishlistButton, isInWishlist, onWishlistToggle, wishlistButtonClassName, isWishlistPending, enableMultipleWishlists, wishlists, onAddToWishlist, onRemoveFromWishlist, onCreateWishlist, wishlistDropdownClassName, productWishlistIds, showDeleteButton, onDelete, deleteButtonClassName, isDeletePending, deleteButtonSize, deleteButtonPosition, priceFormatOptions, quantUnit, }: ProductCardProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,30 +1,39 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { formatPrice } from '../../../utils/generalHelperFunction';
|
|
4
|
-
import { buildClassesByJoining } from '../../../utils/StyleHelper';
|
|
5
3
|
import { ShoppingBagIcon } from '@heroicons/react/24/solid';
|
|
6
|
-
import
|
|
4
|
+
import parseHTML from 'html-react-parser';
|
|
5
|
+
import { useCallback, useState } from 'react';
|
|
6
|
+
import { formatPriceForHTML, formatPriceString, } from '../../../utils/generalHelperFunction';
|
|
7
|
+
import { buildClassesByJoining } from '../../../utils/StyleHelper';
|
|
8
|
+
import Badge from '../../Base/badge/Badge';
|
|
7
9
|
import AddToCart from '../../Base/button/AddToCart';
|
|
8
|
-
import Heading from '../../Base/heading/Heading';
|
|
9
10
|
import Button from '../../Base/button/Button';
|
|
11
|
+
import Heading from '../../Base/heading/Heading';
|
|
10
12
|
import Spinner from '../../Base/spinner/Spinner';
|
|
11
|
-
import Badge from '../../Base/badge/Badge';
|
|
12
|
-
import LikeButton from '../wishlist/LikeButton';
|
|
13
13
|
import DeleteButton from '../wishlist/DeleteButton';
|
|
14
|
-
|
|
14
|
+
import LikeButton from '../wishlist/LikeButton';
|
|
15
|
+
export const ProductCard = ({ product, href, className, imageClassName, contentClassName, titleClassName, priceClassName, descriptionClassName, badgeClassName, aspectRatio = 'square', imagePosition = 'center', imageObjectFit = 'cover', layout = 'default', hoverEffect = 'opacity', renderBadges = true, renderColor = true, renderDescription = false, elevationLevel = 0, rounded = 'md', titleLines = 1, onCardClick, onImageClick, renderFooter, renderHeader, renderVariationName = false, actionButton,
|
|
15
16
|
// Add to cart functionality
|
|
16
|
-
showAddButton = false, showAddButtonOnHover = false, borderOnHoverClassName = '', currentQuantity = 0, onQuantityChange, addButtonIcon = _jsx(ShoppingBagIcon, { className: "h-4 w-4" }), maxQuantity = 99, controlsClassName = '', buttonClassName = '', quantityClassName = '', customAddButton, disabled = false, locale, cartMode = 'full', showSpinnerInAdd = false,
|
|
17
|
+
showAddButton = false, showAddButtonOnHover = false, addButtonPosition = 'overlay', addButtonAlignment = 'center', addButtonText = 'Pridať do košíka', addButtonBottomClassName = '', borderOnHoverClassName = '', currentQuantity = 0, onQuantityChange, addButtonIcon = _jsx(ShoppingBagIcon, { className: "h-4 w-4" }), maxQuantity = 99, controlsClassName = '', buttonClassName = '', quantityClassName = '', customAddButton, disabled = false, locale, cartMode = 'full', showSpinnerInAdd = false,
|
|
17
18
|
// Wishlist functionality
|
|
18
19
|
showWishlistButton = false, isInWishlist = false, onWishlistToggle, wishlistButtonClassName = '', isWishlistPending = false,
|
|
19
20
|
// Multiple wishlists support
|
|
20
21
|
enableMultipleWishlists = false, wishlists = [], onAddToWishlist, onRemoveFromWishlist, onCreateWishlist, wishlistDropdownClassName = '', productWishlistIds = [],
|
|
21
22
|
// Delete functionality
|
|
22
|
-
showDeleteButton = false, onDelete, deleteButtonClassName = '', isDeletePending = false, deleteButtonSize = 'md', deleteButtonPosition = 'top-right',
|
|
23
|
+
showDeleteButton = false, onDelete, deleteButtonClassName = '', isDeletePending = false, deleteButtonSize = 'md', deleteButtonPosition = 'top-right',
|
|
24
|
+
// Price formatting - NOVÉ JEDNODUCHÉ API
|
|
25
|
+
priceFormatOptions, quantUnit = '', }) => {
|
|
23
26
|
const [showWishlistDropdown, setShowWishlistDropdown] = useState(false);
|
|
24
27
|
const [showNewWishlistInput, setShowNewWishlistInput] = useState(false);
|
|
25
28
|
const [newWishlistName, setNewWishlistName] = useState('');
|
|
26
|
-
const
|
|
27
|
-
|
|
29
|
+
const getBottomButtonAlignment = () => {
|
|
30
|
+
const alignmentClasses = {
|
|
31
|
+
left: 'justify-start',
|
|
32
|
+
center: 'justify-center',
|
|
33
|
+
right: 'justify-end',
|
|
34
|
+
};
|
|
35
|
+
return alignmentClasses[addButtonAlignment];
|
|
36
|
+
};
|
|
28
37
|
const shouldShowQuantityControls = () => {
|
|
29
38
|
if (cartMode === 'add-only') {
|
|
30
39
|
return false;
|
|
@@ -35,7 +44,7 @@ showDeleteButton = false, onDelete, deleteButtonClassName = '', isDeletePending
|
|
|
35
44
|
if (cartMode === 'add-only') {
|
|
36
45
|
return true;
|
|
37
46
|
}
|
|
38
|
-
return showAddButton;
|
|
47
|
+
return showAddButton && currentQuantity === 0;
|
|
39
48
|
};
|
|
40
49
|
// Determine which icon to show
|
|
41
50
|
const getButtonIcon = () => {
|
|
@@ -152,6 +161,18 @@ showDeleteButton = false, onDelete, deleteButtonClassName = '', isDeletePending
|
|
|
152
161
|
onDelete(product);
|
|
153
162
|
}
|
|
154
163
|
}, [onDelete, product]);
|
|
155
|
-
|
|
156
|
-
|
|
164
|
+
// Rozhodnutie či použiť HTML alebo string formát
|
|
165
|
+
const shouldUseHtml = priceFormatOptions?.numberFormat === 'decimal-index' ||
|
|
166
|
+
priceFormatOptions?.numberClassName ||
|
|
167
|
+
priceFormatOptions?.currencyClassName ||
|
|
168
|
+
priceFormatOptions?.containerClassName;
|
|
169
|
+
return (_jsxs("div", { className: buildClassesByJoining('group relative h-full', layoutClass, elevationClass, onCardClick ? 'cursor-pointer' : '', borderOnHoverClassName, className), onClick: handleCardClick, children: [renderHeader && renderHeader(product), _jsxs("div", { className: buildClassesByJoining(imageLayoutClass, 'relative'), children: [product.badges && renderBadges && (_jsx("div", { className: "absolute top-2 left-2 z-10 flex flex-wrap gap-1", children: product.badges.map((badge, index) => (_jsx(Badge, { color: badge.color, children: badge.text }, index))) })), showDeleteButton ? (_jsx(DeleteButton, { show: showDeleteButton, onDelete: handleDelete, deleteButtonClassName: deleteButtonClassName, isDeletePending: isDeletePending, size: deleteButtonSize, position: deleteButtonPosition })) : (_jsx(LikeButton, { show: showWishlistButton, enableMultiple: enableMultipleWishlists, isInWishlist: isInWishlist, wishlists: wishlists, productWishlistIds: productWishlistIds, wishlistButtonClassName: wishlistButtonClassName, wishlistDropdownClassName: wishlistDropdownClassName, isWishlistPending: isWishlistPending, handleAddToWishlist: handleAddToWishlist, handleRemoveFromWishlist: handleRemoveFromWishlist, handleCreateNewWishlist: handleCreateNewWishlist, handleWishlistToggle: handleWishlistToggle })), _jsx("img", { alt: product.variation_images[0]?.alt, src: product.variation_images[0]?.href, className: buildClassesByJoining(aspectRatioClass, objectFitClass, objectPositionClass, hoverEffect !== 'none' ? hoverClass : '', roundedClass, 'bg-gray-200', onImageClick ? 'cursor-pointer' : '', imageClassName), onClick: handleImageClick }), addButtonPosition === 'overlay' && (_jsx("div", { className: showAddButtonOnHover === true
|
|
170
|
+
? 'opacity-0 group-hover:opacity-100 transition-opacity duration-300'
|
|
171
|
+
: '', children: _jsx(AddToCart, { buttonClassName: buttonClassName, addButtonIcon: getButtonIcon(), handleAddToCart: handleAddToCart, showAddButton: shouldShowAddButton(), disabled: disabled, customAddButton: customAddButton }) }))] }), _jsxs("div", { className: buildClassesByJoining('mt-4 h-full flex flex-col justify-between grow', contentLayoutClass, compactClass, contentClassName), children: [_jsxs("div", { className: "flex flex-col h-full justify-between", children: [_jsxs("div", { children: [_jsx(Heading, { level: 3, className: buildClassesByJoining('text-lg text-gray-700', titleLineClampClass, titleClassName), children: _jsxs(Button, { asLink: true, unstyled: true, href: href, children: [!onCardClick && (_jsx("span", { "aria-hidden": "true", className: "absolute inset-0" })), product.product_name] }) }), renderVariationName && product.variation_name && (_jsx(Heading, { className: "text-md", level: 4, children: product.variation_name })), renderDescription && product.description && (_jsx("span", { className: buildClassesByJoining('mt-1 text-sm text-gray-500 line-clamp-2', descriptionClassName), children: parseHTML(product.description) }))] }), _jsxs("div", { className: "flex flex-col justify-between", children: [shouldUseHtml ? (_jsxs("span", { className: "flex items-baseline gap-1", children: [_jsx("div", { className: buildClassesByJoining('text-md font-bold text-gray-900', priceClassName), dangerouslySetInnerHTML: formatPriceForHTML(product.price, locale, priceFormatOptions) }), quantUnit && (_jsx("p", { className: "text-md font-bold text-black", children: quantUnit }))] })) : (_jsxs("span", { className: "flex items-baseline gap-1", children: [_jsx("p", { className: buildClassesByJoining('text-md font-bold text-gray-900', priceClassName), children: formatPriceString(product.price, locale, priceFormatOptions) }), quantUnit && (_jsx("p", { className: "text-md font-bold text-black", children: quantUnit }))] })), product?.discountPercent != null &&
|
|
172
|
+
product.discountPercent > 0 &&
|
|
173
|
+
(shouldUseHtml ? (_jsxs("span", { className: "flex items-baseline gap-1", children: [_jsx("div", { className: buildClassesByJoining('text-sm font-normal text-gray-900 line-through', priceClassName), dangerouslySetInnerHTML: formatPriceForHTML(product.price / (1 - product.discountPercent / 100), locale, priceFormatOptions) }), quantUnit && (_jsx("p", { className: "text-sm font-normal text-black line-through", children: quantUnit }))] })) : (_jsxs("span", { className: "flex items-baseline gap-1", children: [_jsx("p", { className: buildClassesByJoining('text-sm font-normal text-gray-900 line-through', priceClassName), children: formatPriceString(product.price / (1 - product.discountPercent / 100), locale, priceFormatOptions) }), quantUnit && (_jsx("p", { className: "text-sm font-normal text-black line-through", children: quantUnit }))] }))), addButtonPosition === 'bottom' && (_jsx("div", { className: buildClassesByJoining('mt-3 flex w-full', getBottomButtonAlignment()), children: shouldShowQuantityControls() ? (
|
|
174
|
+
// Quantity controls
|
|
175
|
+
_jsxs("div", { className: buildClassesByJoining('flex items-center gap-2', controlsClassName), children: [_jsx(Button, { onClick: handleDecrement, className: buildClassesByJoining('w-8 h-8 flex items-center justify-center rounded border', addButtonBottomClassName), disabled: disabled || currentQuantity <= 0, children: "\u2212" }), _jsx("span", { className: buildClassesByJoining('mx-2 font-medium', quantityClassName), children: currentQuantity }), _jsx(Button, { onClick: handleIncrement, className: buildClassesByJoining('w-8 h-8 flex items-center justify-center rounded border', addButtonBottomClassName), disabled: disabled || currentQuantity >= maxQuantity, children: "+" })] })) : shouldShowAddButton() ? (
|
|
176
|
+
// Add button
|
|
177
|
+
customAddButton || (_jsxs(Button, { onClick: handleAddToCart, className: buildClassesByJoining('flex items-center gap-2 px-4 py-2 rounded-md transition-colors', disabled ? 'opacity-50 cursor-not-allowed' : '', addButtonBottomClassName), disabled: disabled, children: [getButtonIcon(), _jsx("span", { children: addButtonText })] }))) : null }))] })] }), actionButton && _jsx("div", { className: "mt-2", children: actionButton }), renderFooter && renderFooter(product)] })] }));
|
|
157
178
|
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { HorizontalTabProps } from '@/components/Web/tabs/HorizontalTab';
|
|
2
|
+
import { PriceFormatOptions } from '@/utils/generalHelperFunction';
|
|
3
|
+
import { Product, ProductVariation } from '@q2devel/q2-core';
|
|
4
|
+
export type ProductDetail = {
|
|
5
|
+
name: string;
|
|
6
|
+
items: string[];
|
|
7
|
+
};
|
|
8
|
+
export type ProductCardWishlist = {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
};
|
|
12
|
+
export type ProductProps = {
|
|
13
|
+
name: string;
|
|
14
|
+
price: number;
|
|
15
|
+
href: string;
|
|
16
|
+
discountPercent?: number;
|
|
17
|
+
rating?: number;
|
|
18
|
+
images: {
|
|
19
|
+
href: string;
|
|
20
|
+
alt: string;
|
|
21
|
+
}[];
|
|
22
|
+
description: string;
|
|
23
|
+
detail?: HorizontalTabProps;
|
|
24
|
+
showColors?: boolean;
|
|
25
|
+
buttonText?: string;
|
|
26
|
+
className?: string;
|
|
27
|
+
product?: Product;
|
|
28
|
+
showAddButton?: boolean;
|
|
29
|
+
currentQuantity: number;
|
|
30
|
+
onQuantityChange?: (product: Product, quantity: number) => void;
|
|
31
|
+
maxQuantity?: number;
|
|
32
|
+
disabled?: boolean;
|
|
33
|
+
badges?: {
|
|
34
|
+
text: string;
|
|
35
|
+
color: string;
|
|
36
|
+
}[];
|
|
37
|
+
showVariations?: boolean;
|
|
38
|
+
variations: ProductVariation[];
|
|
39
|
+
currentVariationId: string;
|
|
40
|
+
cartMode?: 'full' | 'add-only';
|
|
41
|
+
onOpenBasket?: () => void;
|
|
42
|
+
showWishlistButton?: boolean;
|
|
43
|
+
isInWishlist?: boolean;
|
|
44
|
+
onWishlistToggle?: (product: Product) => void;
|
|
45
|
+
enableMultipleWishlists?: boolean;
|
|
46
|
+
wishlists?: ProductCardWishlist[];
|
|
47
|
+
onAddToWishlist?: (product: Product, wishlistId: string) => void;
|
|
48
|
+
onRemoveFromWishlist?: (product: Product, wishlistId: string) => void;
|
|
49
|
+
onCreateWishlist?: (name: string, product: Product) => void;
|
|
50
|
+
productWishlistIds?: string[];
|
|
51
|
+
isWishlistPending?: boolean;
|
|
52
|
+
isLogged?: boolean;
|
|
53
|
+
onWishlistTextClick?: () => void;
|
|
54
|
+
showWishlistDropdown?: boolean;
|
|
55
|
+
onCloseWishlistDropdown?: () => void;
|
|
56
|
+
onAddToSpecificWishlist?: (wishlistId: string) => void;
|
|
57
|
+
onRemoveFromSpecificWishlist?: (wishlistId: string) => void;
|
|
58
|
+
onCreateNewWishlistFromDropdown?: (name: string) => void;
|
|
59
|
+
onShippingOptionsClick?: () => void;
|
|
60
|
+
isWatchdogEnabled?: boolean;
|
|
61
|
+
onWatchdogClick?: () => void;
|
|
62
|
+
onDemandClick?: () => void;
|
|
63
|
+
translations: {
|
|
64
|
+
recommendedPrice: string;
|
|
65
|
+
shippingDetailFree: string;
|
|
66
|
+
shippingDetailFee: string;
|
|
67
|
+
watchDog: string;
|
|
68
|
+
question: string;
|
|
69
|
+
optionOfShipping: string;
|
|
70
|
+
addToWishlist: string;
|
|
71
|
+
};
|
|
72
|
+
locale: string;
|
|
73
|
+
quantUnit?: string;
|
|
74
|
+
priceFormatOptions?: PriceFormatOptions;
|
|
75
|
+
};
|
|
76
|
+
export declare const ProductDetail: ({ name, price, href, discountPercent, description, detail, buttonText, className, product, currentQuantity, onQuantityChange, maxQuantity, disabled, badges, showVariations, variations, currentVariationId, cartMode, onOpenBasket, showWishlistButton, isInWishlist, onWishlistToggle, enableMultipleWishlists, wishlists, onAddToWishlist, onRemoveFromWishlist, onCreateWishlist, productWishlistIds, isWishlistPending, isLogged, onWishlistTextClick, showWishlistDropdown, onCloseWishlistDropdown, onAddToSpecificWishlist, onRemoveFromSpecificWishlist, onCreateNewWishlistFromDropdown, onShippingOptionsClick, isWatchdogEnabled, onWatchdogClick, onDemandClick, translations, locale, quantUnit, priceFormatOptions, }: ProductProps) => import("react/jsx-runtime").JSX.Element | null;
|