@q2devel/q2-storybook 1.0.84 → 1.0.86

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.
@@ -1,5 +1,5 @@
1
- import React from 'react';
2
- export interface ButtonProps {
1
+ import React, { ButtonHTMLAttributes } from 'react';
2
+ export interface ButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onClick'> {
3
3
  children?: React.ReactNode;
4
4
  label?: string;
5
5
  variant?: 'primary' | 'secondary';
@@ -18,5 +18,5 @@ export interface ButtonProps {
18
18
  rel?: string;
19
19
  unstyled?: boolean;
20
20
  }
21
- declare const Button: React.FC<ButtonProps>;
21
+ declare const Button: ({ children, label, variant, disabled, asLink, openInNewTab, href, onClick, onMouseEnter, onMouseLeave, onFocus, onBlur, rounded, className, target, rel, unstyled, type, ...restProps }: ButtonProps) => import("react/jsx-runtime").JSX.Element | null;
22
22
  export default Button;
@@ -14,7 +14,9 @@ const variantClasses = {
14
14
  primary: 'bg-blue-500 hover:bg-blue-600 text-white',
15
15
  secondary: 'bg-gray-500 hover:bg-gray-600 text-white',
16
16
  };
17
- const Button = ({ children, label, variant, disabled = false, asLink = false, openInNewTab = false, href, onClick, onMouseEnter, onMouseLeave, onFocus, onBlur, rounded = 'md', className = '', target, rel, unstyled = false, }) => {
17
+ const Button = ({ children, label, variant, disabled = false, asLink = false, openInNewTab = false, href, onClick, onMouseEnter, onMouseLeave, onFocus, onBlur, rounded = 'md', className = '', target, rel, unstyled = false, type = 'button', // ← Teraz má default z natívneho button
18
+ ...restProps // ← Všetky ostatné natívne props
19
+ }) => {
18
20
  const baseClasses = unstyled
19
21
  ? className
20
22
  : buildClassesByJoining('inline-block px-4 py-2 font-medium transition-colors duration-200', variantClasses[variant ?? 'primary'], roundedClasses[rounded], {
@@ -40,6 +42,6 @@ const Button = ({ children, label, variant, disabled = false, asLink = false, op
40
42
  }
41
43
  return (_jsx(Link, { href: href, ...commonProps, target: openInNewTab ? '_blank' : target, rel: openInNewTab ? 'noopener noreferrer' : rel, children: content }));
42
44
  }
43
- return (_jsx("button", { ...commonProps, disabled: disabled, children: content }));
45
+ return (_jsx("button", { ...commonProps, ...restProps, disabled: disabled, type: type, children: content }));
44
46
  };
45
47
  export default Button;
@@ -1,18 +1,40 @@
1
- import React from 'react';
2
- import { ReactNode } from 'react';
3
- export interface TooltipRef {
4
- show: () => void;
5
- hide: () => void;
6
- }
7
- interface TooltipProps {
8
- content?: ReactNode;
9
- children: ReactNode;
1
+ import { Placement, Strategy } from '@floating-ui/react';
2
+ import { HTMLAttributes, ReactNode, Ref } from 'react';
3
+ export type TooltipRef = {
4
+ open: () => void;
5
+ close: () => void;
6
+ };
7
+ export type TooltipChildren = ((ref: Ref<HTMLElement>, props: Record<string, unknown>, disabled: boolean) => ReactNode) | ReactNode;
8
+ export type TooltipProps = Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'content'> & {
9
+ content: ((ref: Ref<HTMLElement>, props: Record<string, unknown>) => ReactNode) | ReactNode;
10
+ children: TooltipChildren;
11
+ placement?: Placement;
12
+ strategy?: Strategy;
13
+ contentClassName?: string;
14
+ zIndex?: number;
15
+ portalTarget?: HTMLElement;
10
16
  backgroundColor?: string;
11
17
  textColor?: string;
12
- placement?: 'top' | 'bottom' | 'left' | 'right';
18
+ disabled?: boolean;
19
+ disableAutoHide?: boolean;
20
+ lazy?: boolean;
21
+ size?: 'sm' | 'md' | 'lg';
22
+ variant?: 'default' | 'dark' | 'light' | 'error' | 'warning' | 'success';
23
+ };
24
+ declare const _default: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLDivElement>, "children" | "content"> & {
25
+ content: ((ref: Ref<HTMLElement>, props: Record<string, unknown>) => ReactNode) | ReactNode;
26
+ children: TooltipChildren;
27
+ placement?: Placement;
28
+ strategy?: Strategy;
13
29
  contentClassName?: string;
30
+ zIndex?: number;
31
+ portalTarget?: HTMLElement;
32
+ backgroundColor?: string;
33
+ textColor?: string;
14
34
  disabled?: boolean;
35
+ disableAutoHide?: boolean;
15
36
  lazy?: boolean;
16
- }
17
- declare const Tooltip: React.ForwardRefExoticComponent<TooltipProps & React.RefAttributes<TooltipRef>>;
18
- export default Tooltip;
37
+ size?: "sm" | "md" | "lg";
38
+ variant?: "default" | "dark" | "light" | "error" | "warning" | "success";
39
+ } & import("react").RefAttributes<TooltipRef>>;
40
+ export default _default;
@@ -1,18 +1,145 @@
1
- import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { forwardRef } from 'react';
3
- const Tooltip = forwardRef(({ content, children, backgroundColor = 'bg-black', textColor = 'text-white', placement = 'bottom', contentClassName = '', disabled = false, lazy = true, }, ref) => {
4
- if (disabled)
5
- return _jsx(_Fragment, { children: children });
6
- return (_jsxs("span", { className: "relative group cursor-pointer", children: [children, content && (_jsx("span", { className: `absolute z-10 hidden group-hover:block
7
- ${placement === 'top'
8
- ? 'bottom-full mb-2'
9
- : placement === 'bottom'
10
- ? 'top-full mt-2'
11
- : placement === 'left'
12
- ? 'right-full mr-2'
13
- : 'left-full ml-2'}
14
- left-1/2 -translate-x-1/2 px-3 py-2 rounded ${backgroundColor} ${textColor}
15
- text-xs whitespace-pre-wrap ${contentClassName}`, children: content }))] }));
16
- });
17
- Tooltip.displayName = 'Tooltip';
18
- export default Tooltip;
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import buildClassesByJoining from '@/utils/StyleHelper';
3
+ import { FloatingPortal, arrow, autoUpdate, hide, offset, safePolygon, shift, useDismiss, useFloating, useFocus, useHover, useInteractions, useRole, useTransitionStyles, } from '@floating-ui/react';
4
+ import { forwardRef, useImperativeHandle, useRef, useState, } from 'react';
5
+ const Tooltip = ({ className, children, content, placement = 'top', strategy: strategyProp = 'absolute', contentClassName, zIndex = 30, portalTarget, backgroundColor, textColor, disabled = false, disableAutoHide, lazy = true, size = 'md', variant = 'default', ...props }, ref) => {
6
+ const [open, setOpen] = useState(false);
7
+ const arrowRef = useRef(null);
8
+ const { x, y, refs, strategy, context, middlewareData: { arrow: { x: arrowX, y: arrowY } = {}, hide: hideMiddleware }, placement: floatingPlacement, } = useFloating({
9
+ open: open,
10
+ onOpenChange: (value) => {
11
+ return disabled ? setOpen(false) : setOpen(disableAutoHide || value);
12
+ },
13
+ placement: placement,
14
+ strategy: strategyProp,
15
+ whileElementsMounted: autoUpdate,
16
+ middleware: [
17
+ offset(5),
18
+ shift(),
19
+ arrow({
20
+ element: arrowRef.current,
21
+ }),
22
+ hide(),
23
+ ],
24
+ });
25
+ const { isMounted, styles } = useTransitionStyles(context);
26
+ const hover = useHover(context, {
27
+ move: false,
28
+ handleClose: safePolygon(),
29
+ });
30
+ const focus = useFocus(context);
31
+ const dismiss = useDismiss(context);
32
+ const role = useRole(context, { role: 'tooltip' });
33
+ const { getReferenceProps, getFloatingProps } = useInteractions([hover, focus, dismiss, role]);
34
+ useImperativeHandle(ref, () => {
35
+ return {
36
+ open() {
37
+ setOpen(true);
38
+ },
39
+ close() {
40
+ setOpen(false);
41
+ },
42
+ };
43
+ });
44
+ // Get variant-based colors
45
+ const getVariantColors = () => {
46
+ const variants = {
47
+ default: {
48
+ bg: backgroundColor || '#374151', // gray-700
49
+ text: textColor || '#ffffff',
50
+ },
51
+ dark: {
52
+ bg: backgroundColor || '#1f2937', // gray-800
53
+ text: textColor || '#ffffff',
54
+ },
55
+ light: {
56
+ bg: backgroundColor || '#f9fafb', // gray-50
57
+ text: textColor || '#374151', // gray-700
58
+ },
59
+ error: {
60
+ bg: backgroundColor || '#dc2626', // red-600
61
+ text: textColor || '#ffffff',
62
+ },
63
+ warning: {
64
+ bg: backgroundColor || '#d97706', // amber-600
65
+ text: textColor || '#ffffff',
66
+ },
67
+ success: {
68
+ bg: backgroundColor || '#059669', // emerald-600
69
+ text: textColor || '#ffffff',
70
+ },
71
+ };
72
+ return variants[variant];
73
+ };
74
+ const colors = getVariantColors();
75
+ // Tooltip content classes based on size and variant
76
+ const tooltipClasses = buildClassesByJoining([
77
+ // Base classes
78
+ 'rounded-md leading-tight shadow-2xl z-10 relative',
79
+ // Size variations
80
+ size === 'sm' && 'text-xs px-1.5 py-1',
81
+ size === 'md' && 'text-sm px-2 py-1.5',
82
+ size === 'lg' && 'text-base px-3 py-2',
83
+ // Image max width
84
+ '[&_img]:max-w-[420px]',
85
+ contentClassName,
86
+ ]);
87
+ const tooltipStyles = {
88
+ zIndex,
89
+ display: (isMounted &&
90
+ hideMiddleware &&
91
+ !hideMiddleware.escaped &&
92
+ !hideMiddleware.referenceHidden) ||
93
+ lazy
94
+ ? 'block'
95
+ : 'none',
96
+ position: strategy,
97
+ top: y ?? 0,
98
+ left: x ?? 0,
99
+ color: colors.text,
100
+ backgroundColor: colors.bg,
101
+ boxShadow: '0 12px 32px 10px rgba(9, 9, 9, 0.09)',
102
+ ...styles,
103
+ };
104
+ const getAllReferenceProps = () => {
105
+ return {
106
+ ...getReferenceProps(),
107
+ ref: refs.setReference,
108
+ };
109
+ };
110
+ const arrowSide = {
111
+ top: 'bottom',
112
+ right: 'left',
113
+ bottom: 'top',
114
+ left: 'right',
115
+ }[floatingPlacement.split('-')[0]] || 'bottom';
116
+ const renderChildren = () => {
117
+ if (typeof children === 'function') {
118
+ return children(refs.setReference, getReferenceProps(), disabled);
119
+ }
120
+ return (_jsx("div", { ...props, ...getAllReferenceProps(), className: buildClassesByJoining(['inline-block', className]), children: children }));
121
+ };
122
+ const renderTooltipContent = () => {
123
+ if (disabled || (lazy && !isMounted)) {
124
+ return;
125
+ }
126
+ if (typeof content === 'function') {
127
+ return content(refs.setFloating, {
128
+ style: tooltipStyles,
129
+ ...getFloatingProps(),
130
+ });
131
+ }
132
+ return (_jsxs("div", { className: tooltipClasses, ref: refs.setFloating, style: tooltipStyles, ...getFloatingProps(), children: [_jsx("span", { className: "relative z-10", dangerouslySetInnerHTML: typeof content === 'string' ? { __html: content } : undefined, children: typeof content !== 'string' ? content : null }), _jsx("div", { ref: arrowRef, style: {
133
+ position: 'absolute',
134
+ width: '10px',
135
+ height: '10px',
136
+ backgroundColor: colors.bg,
137
+ left: arrowX != null ? `${arrowX}px` : '0px',
138
+ top: arrowY != null ? `${arrowY}px` : '0px',
139
+ [arrowSide]: '-5px',
140
+ transform: 'rotate(45deg)',
141
+ } })] }));
142
+ };
143
+ return (_jsxs(_Fragment, { children: [_jsx(FloatingPortal, { root: portalTarget, children: renderTooltipContent() }), renderChildren()] }));
144
+ };
145
+ export default forwardRef(Tooltip);
@@ -1,55 +1,43 @@
1
- import React from "react";
2
- export type BasketProduct = {
3
- id: string;
4
- name: string;
5
- price: number;
1
+ import { PriceFormatOptions } from '@/utils/generalHelperFunction';
2
+ import { Product } from '@q2devel/q2-core';
3
+ export type BasketProduct = Omit<Product, 'price'> & {
6
4
  quantity: number;
7
- variation_image?: {
8
- src: string;
9
- alt: string;
10
- };
11
- href?: string;
12
- color?: string;
13
- [key: string]: any;
5
+ price: number;
6
+ formattedPrice?: string;
7
+ order_item_id: string;
8
+ };
9
+ export type BasketProductExtended = BasketProduct & {
10
+ drupalOrderItemId?: number;
14
11
  };
15
- export type BasketDialogVariant = "default" | "compact" | "full";
16
- export type BasketDialogPosition = "right" | "left";
17
12
  export type BasketDialogProps = {
18
13
  isOpen: boolean;
19
14
  onClose: () => void;
20
15
  products: BasketProduct[];
21
- subtotal: string | number;
22
- onQuantityChange?: (productId: string, quantity: number) => void;
23
- onRemoveProduct?: (productId: string) => void;
16
+ subtotal: number;
17
+ onQuantityChange?: (productUuid: string, quantity: number) => void;
18
+ onRemoveProduct?: (productUuid: string) => void;
24
19
  onCheckout?: () => void;
25
20
  onContinueShopping?: () => void;
26
- onProductClick?: (product: BasketProduct) => void;
21
+ productLink?: (productName: string, variationId: string) => string;
27
22
  title?: string;
28
23
  checkoutText?: string;
29
24
  continueShoppingText?: string;
30
- emptyCartText?: string;
31
- quantityLabel?: string;
32
- removeButtonText?: string;
33
- totalLabel?: string;
34
- shippingNoteText?: string;
35
- variant?: BasketDialogVariant;
36
- position?: BasketDialogPosition;
37
- showProductImages?: boolean;
38
- showProductColors?: boolean;
39
- showProductLinks?: boolean;
40
- showRemoveButton?: boolean;
41
- showSubtotal?: boolean;
42
- showCheckoutButton?: boolean;
43
- showContinueShoppingButton?: boolean;
44
25
  showShippingNote?: boolean;
45
26
  showQuantityControls?: boolean;
46
- customClassName?: string;
47
- maxHeight?: string;
48
- animationDuration?: number;
49
- backdropOpacity?: number;
50
- renderCustomProduct?: (product: BasketProduct) => React.ReactNode;
51
- renderCustomFooter?: () => React.ReactNode;
52
27
  pendingUpdates?: Record<string, number>;
53
28
  processingItems?: Record<string, boolean>;
29
+ isAddingToCart?: boolean;
30
+ totalItems?: number;
31
+ locale: string;
32
+ productPriceOptions?: PriceFormatOptions;
33
+ totalPriceOptions?: PriceFormatOptions;
34
+ translations: {
35
+ emptyBasket: string;
36
+ amount: string;
37
+ remove: string;
38
+ addingToBasket: string;
39
+ total: string;
40
+ shippingAndTax: string;
41
+ };
54
42
  };
55
- export declare const BasketDialog: ({ isOpen, onClose, products, subtotal, onQuantityChange, onRemoveProduct, onCheckout, onContinueShopping, onProductClick, title, checkoutText, continueShoppingText, emptyCartText, quantityLabel, removeButtonText, totalLabel, shippingNoteText, variant, position, showProductImages, showProductColors, showProductLinks, showRemoveButton, showSubtotal, showCheckoutButton, showContinueShoppingButton, showShippingNote, showQuantityControls, customClassName, maxHeight, animationDuration, backdropOpacity, renderCustomProduct, renderCustomFooter, pendingUpdates, processingItems, }: BasketDialogProps) => import("react/jsx-runtime").JSX.Element;
43
+ export declare const BasketDialog: ({ isOpen, onClose, products, subtotal, onQuantityChange, onRemoveProduct, onCheckout, onContinueShopping, productLink, title, checkoutText, continueShoppingText, showShippingNote, showQuantityControls, pendingUpdates, processingItems, isAddingToCart, totalItems, locale, productPriceOptions, totalPriceOptions, translations, }: BasketDialogProps) => import("react/jsx-runtime").JSX.Element;
@@ -1,29 +1,27 @@
1
- "use client";
1
+ 'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { Dialog, DialogBackdrop, DialogPanel, DialogTitle, } from "@headlessui/react";
4
- import { MinusIcon, PlusIcon, XMarkIcon } from "@heroicons/react/24/outline";
5
- // Helper funkcia pre formátovanie ceny
6
- const formatPrice = (price) => {
7
- return new Intl.NumberFormat("cs-CZ", {
8
- style: "currency",
9
- currency: "CZK",
10
- }).format(price);
11
- };
12
- export const BasketDialog = ({
13
- // Základné props
14
- isOpen, onClose, products, subtotal,
15
- // Callback funkcie
16
- onQuantityChange, onRemoveProduct, onCheckout, onContinueShopping, onProductClick,
17
- // Texty s predvolenými hodnotami
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
- // Zobrazenie/konfigurácia s predvolenými hodnotami
20
- variant = "default", position = "right", showProductImages = true, showProductColors = true, showProductLinks = true, showRemoveButton = true, showSubtotal = true, showCheckoutButton = true, showContinueShoppingButton = true, showShippingNote = true, showQuantityControls = true,
21
- // Štýlovanie s predvolenými hodnotami
22
- customClassName = "", maxHeight = "100vh", animationDuration = 500, backdropOpacity = 0.75,
23
- // Vlastné renderovanie
24
- renderCustomProduct, renderCustomFooter,
25
- // Stav
26
- pendingUpdates = {}, processingItems = {}, }) => {
3
+ import Button from '@/components/Base/button/Button';
4
+ import Heading from '@/components/Base/heading/Heading';
5
+ import { Price } from '@/components/Base/price/Price';
6
+ import Spinner from '@/components/Base/spinner/Spinner';
7
+ import { Dialog, DialogBackdrop, DialogPanel, DialogTitle } from '@headlessui/react';
8
+ import { MinusIcon, PlusIcon, XMarkIcon } from '@heroicons/react/24/outline';
9
+ import { useEffect, useRef, useState } from 'react';
10
+ export const BasketDialog = ({ isOpen, onClose, products, subtotal, onQuantityChange, onRemoveProduct, onCheckout, onContinueShopping, productLink, title = 'Váš košík', checkoutText = 'Pokračovat k objednávce', continueShoppingText = 'Pokračovat v nákupu', showShippingNote = true, showQuantityControls = true, pendingUpdates = {}, processingItems = {}, isAddingToCart = false, totalItems, locale, productPriceOptions, totalPriceOptions, translations, }) => {
11
+ const [shouldShowAddingIndicator, setShouldShowAddingIndicator] = useState(false);
12
+ const initialTotalItems = useRef(null);
13
+ useEffect(() => {
14
+ if (isAddingToCart) {
15
+ initialTotalItems.current = totalItems || 0;
16
+ setShouldShowAddingIndicator(true);
17
+ }
18
+ else if (initialTotalItems.current !== null) {
19
+ if (totalItems !== initialTotalItems.current) {
20
+ setShouldShowAddingIndicator(false);
21
+ initialTotalItems.current = null;
22
+ }
23
+ }
24
+ }, [isAddingToCart, totalItems]);
27
25
  const handleCheckout = () => {
28
26
  if (onCheckout) {
29
27
  onCheckout();
@@ -37,60 +35,53 @@ pendingUpdates = {}, processingItems = {}, }) => {
37
35
  onClose();
38
36
  }
39
37
  };
40
- const handleIncrement = (productId, quantity) => {
38
+ const handleIncrement = (productUuid, quantity) => {
41
39
  if (onQuantityChange) {
42
- onQuantityChange(productId, quantity + 1);
40
+ onQuantityChange(productUuid, quantity + 1);
43
41
  }
44
42
  };
45
- const handleDecrement = (productId, quantity) => {
43
+ const handleDecrement = (productUuid, quantity) => {
46
44
  if (onQuantityChange) {
47
45
  if (quantity > 1) {
48
- onQuantityChange(productId, quantity - 1);
46
+ onQuantityChange(productUuid, quantity - 1);
49
47
  }
50
48
  else {
51
- onQuantityChange(productId, 0);
49
+ onQuantityChange(productUuid, 0);
52
50
  }
53
51
  }
54
52
  };
55
- const getVariantClasses = () => {
56
- switch (variant) {
57
- case "compact":
58
- return "max-w-sm";
59
- case "full":
60
- return "max-w-2xl";
61
- default:
62
- return "max-w-md";
63
- }
64
- };
65
- const getPositionClasses = () => {
66
- return position === "left" ? "left-0" : "right-0";
67
- };
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
- if (renderCustomProduct) {
70
- return renderCustomProduct(product);
71
- }
72
- const displayQuantity = product.id in pendingUpdates
73
- ? pendingUpdates[product.id]
74
- : product.quantity;
75
- const isProcessing = processingItems[product.id] === true;
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.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) => {
78
- if (onProductClick) {
79
- e.preventDefault();
80
- onProductClick(product);
81
- }
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 &&
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 &&
84
- handleDecrement(product.id, displayQuantity), disabled: isProcessing, className: `rounded-full p-1 text-gray-600 ${isProcessing
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 &&
87
- handleIncrement(product.id, displayQuantity), disabled: isProcessing, className: `rounded-full p-1 text-gray-600 ${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 &&
92
- onRemoveProduct(product.id), disabled: isProcessing, children: removeButtonText }) }))] })] })] }, product.id));
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"
94
- ? formatPrice(subtotal)
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()] })] }) }) }) }) })] }));
53
+ return (_jsxs(Dialog, { open: isOpen, onClose: onClose, className: "relative z-110", children: [_jsx(DialogBackdrop, { transition: true, className: "fixed inset-0 bg-gray-500/75 transition-opacity duration-500 ease-in-out data-closed:opacity-0" }), _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 right-0 flex max-w-full pl-10", children: _jsx(DialogPanel, { transition: true, className: "pointer-events-auto w-screen max-w-md transform transition duration-500 ease-in-out data-closed:translate-x-full sm:duration-700", children: _jsxs("div", { className: "flex h-full flex-col overflow-y-scroll bg-white shadow-xl", 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(XMarkIcon, { "aria-hidden": "true", className: "size-6" })] }) })] }), _jsx("div", { className: "mt-8", children: _jsx("div", { className: "flow-root", children: products.length === 0 && !shouldShowAddingIndicator ? (_jsx("div", { className: "py-6 text-center text-gray-500", children: translations.emptyBasket })) : (_jsxs("ul", { role: "list", className: "-my-6 divide-y divide-gray-200", children: [products.map((product) => {
54
+ const productKey = product.variation_uuid;
55
+ const displayQuantity = productKey in pendingUpdates
56
+ ? pendingUpdates[productKey]
57
+ : product.quantity;
58
+ const isProcessing = processingItems[productKey] === true;
59
+ const isTemporaryRow = shouldShowAddingIndicator &&
60
+ productKey === 'adding';
61
+ return (_jsxs("li", { className: `flex py-6 ${isTemporaryRow
62
+ ? 'opacity-75 bg-gray-50'
63
+ : ''}`, children: [_jsxs("div", { className: "size-24 shrink-0 overflow-hidden border border-gray-200 relative", children: [_jsx("img", { alt: product
64
+ .variation_images[0]
65
+ ?.alt ||
66
+ product.variation_name, src: product
67
+ .variation_images[0]
68
+ ?.href ||
69
+ '/lk_logo.png', className: "size-full object-contain" }), isTemporaryRow && (_jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-white bg-opacity-75", children: _jsx(Spinner, { className: "h-8 w-8 text-[var(--primary)]" }) }))] }), _jsxs("div", { className: "ml-4 flex flex-1 flex-col", children: [_jsx("div", { children: _jsxs("div", { className: "flex justify-between text-base font-medium text-gray-900", children: [_jsx(Heading, { level: 3, className: "font-extralight text-lg", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { asLink: true, unstyled: true, href: productLink?.(product.product_name, product.variation_uuid), children: product.product_name }), isProcessing &&
70
+ !isTemporaryRow && (_jsx(Spinner, { className: "h-4 w-4 text-gray-400" }))] }) }), _jsx(Price, { value: product.price, locale: locale, options: productPriceOptions, className: "ml-4 font-light" })] }) }), _jsxs("div", { className: "flex flex-1 items-end justify-between text-sm", children: [isTemporaryRow ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Spinner, { className: "h-4 w-4 text-[var(--primary)]" }), _jsx("p", { className: "text-gray-500 italic", children: "Prid\u00E1va sa do ko\u0161\u00EDka..." })] })) : showQuantityControls &&
71
+ onQuantityChange ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("p", { className: "text-gray-500 mr-2", children: translations.amount }), _jsxs("div", { className: "flex items-center", children: [_jsx("button", { type: "button", onClick: () => !isProcessing &&
72
+ handleDecrement(product.variation_uuid, displayQuantity), disabled: isProcessing, className: `rounded-full p-1 text-gray-600 ${isProcessing
73
+ ? 'bg-gray-100 cursor-not-allowed opacity-50'
74
+ : '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 &&
75
+ handleIncrement(product.variation_uuid, displayQuantity), disabled: isProcessing, className: `rounded-full p-1 text-gray-600 ${isProcessing
76
+ ? 'bg-gray-100 cursor-not-allowed opacity-50'
77
+ : 'bg-gray-200 hover:bg-gray-300'}`, children: _jsx(PlusIcon, { className: "h-4 w-4" }) })] })] })) : (_jsx("p", { className: "text-gray-500", children: translations.amount +
78
+ ' ' +
79
+ displayQuantity })), !isTemporaryRow &&
80
+ onRemoveProduct && (_jsx("div", { className: "flex", children: _jsx("button", { type: "button", className: `font-medium ${isProcessing
81
+ ? 'text-gray-400 cursor-not-allowed'
82
+ : 'text-[var(--accent)] hover:text-[var(--primary)]'}`, onClick: () => !isProcessing &&
83
+ onRemoveProduct(product.variation_uuid), disabled: isProcessing, children: translations.remove }) }))] })] })] }, `${product.variation_uuid}-${isTemporaryRow
84
+ ? 'temp'
85
+ : 'normal'}`));
86
+ }), shouldShowAddingIndicator && (_jsxs("li", { className: "flex py-6 opacity-75 bg-gray-50", children: [_jsx("div", { className: "size-24 shrink-0 overflow-hidden rounded-md border border-gray-200 relative", children: _jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-white bg-opacity-75", children: _jsx(Spinner, { className: "h-8 w-8 text-[var(--primary)]" }) }) }), _jsx("div", { className: "ml-4 flex flex-1 flex-col justify-center", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Spinner, { className: "h-4 w-4 text-[var(--primary)]" }), _jsx("p", { className: "text-gray-500 italic", children: translations.addingToBasket })] }) })] }))] })) }) })] }), _jsxs("div", { className: "border-t border-gray-200 px-4 py-6 sm:px-6", children: [_jsxs("div", { className: "flex justify-between text-base font-medium text-gray-900", children: [_jsx("p", { children: translations.total }), _jsx(Price, { value: subtotal, locale: locale, options: totalPriceOptions })] }), showShippingNote && (_jsx("p", { className: "mt-0.5 text-sm text-gray-500", children: translations.shippingAndTax })), products.length > 0 && onCheckout && (_jsx("div", { className: "mt-6", children: _jsx("button", { onClick: handleCheckout, className: "button-brand w-full", children: checkoutText }) })), _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-[var(--primary)] hover:text-[var(--primary)]", children: [continueShoppingText, _jsx("span", { "aria-hidden": "true", children: " \u2192" })] }) }) })] })] }) }) }) }) })] }));
96
87
  };
@@ -0,0 +1,34 @@
1
+ import { PriceFormatOptions } from '@/utils/generalHelperFunction';
2
+ import { Product } from '@q2devel/q2-core';
3
+ export type BasketProduct = Product & {
4
+ quantity: number;
5
+ inStock?: boolean;
6
+ leadTime?: string;
7
+ size?: string;
8
+ };
9
+ export type BasketOverviewProps = {
10
+ products: BasketProduct[];
11
+ subtotal: number;
12
+ onQuantityChange?: (productId: string, quantity: number) => void;
13
+ onRemoveProduct?: (productId: string) => void;
14
+ onClearBasket?: () => void;
15
+ onCheckout?: () => void;
16
+ onContinueShopping?: () => void;
17
+ productLink?: (productName: string, variationId: string) => string;
18
+ title?: string;
19
+ checkoutText?: string;
20
+ continueShoppingText?: string;
21
+ shippingNote?: string;
22
+ totalText?: string;
23
+ showShippingNote?: boolean;
24
+ clearBasketText?: string;
25
+ pendingUpdates?: Record<string, number>;
26
+ processingItems?: Record<string, boolean>;
27
+ locale: string;
28
+ productPriceOptions?: PriceFormatOptions;
29
+ TotalPriceOptions?: PriceFormatOptions;
30
+ translations?: {
31
+ emptyBasketPlaceholder?: string;
32
+ };
33
+ };
34
+ export declare const BasketOverview: ({ products, subtotal, onQuantityChange, onRemoveProduct, onClearBasket, onCheckout, onContinueShopping, productLink, title, checkoutText, continueShoppingText, showShippingNote, clearBasketText, totalText, shippingNote, pendingUpdates, processingItems, locale, productPriceOptions, TotalPriceOptions, translations, }: BasketOverviewProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,38 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import Button from '@/components/Base/button/Button';
4
+ import Heading from '@/components/Base/heading/Heading';
5
+ import { Price } from '@/components/Base/price/Price';
6
+ import { CheckIcon, ClockIcon, TrashIcon } from '@heroicons/react/20/solid';
7
+ import { MinusIcon, PlusIcon } from '@heroicons/react/24/solid';
8
+ export const BasketOverview = ({ products, subtotal, onQuantityChange, onRemoveProduct, onClearBasket, onCheckout, onContinueShopping, productLink, title = 'Shopping Cart', checkoutText = 'Checkout', continueShoppingText = 'Continue Shopping', showShippingNote = true, clearBasketText = 'Clear basket', totalText, shippingNote, pendingUpdates = {}, processingItems = {}, locale, productPriceOptions, TotalPriceOptions, translations, }) => {
9
+ const handleIncrement = (product, quantity) => {
10
+ if (onQuantityChange) {
11
+ onQuantityChange(product.variation_uuid, quantity + 1);
12
+ }
13
+ };
14
+ const handleDecrement = (product, quantity) => {
15
+ if (onQuantityChange) {
16
+ if (quantity > 1) {
17
+ onQuantityChange(product.variation_uuid, quantity - 1);
18
+ }
19
+ else if (onRemoveProduct) {
20
+ onRemoveProduct(product.variation_uuid);
21
+ }
22
+ }
23
+ };
24
+ return (_jsx("div", { children: _jsxs("div", { className: "mx-auto max-w-2xl px-4 py-16 sm:px-6 sm:py-8 lg:px-0", children: [_jsx(Heading, { level: 1, className: "label", children: title }), _jsxs("form", { className: "mt-12", children: [_jsxs("section", { "aria-labelledby": "cart-heading", children: [_jsx(Heading, { level: 2, id: "cart-heading", className: "sr-only", children: "Items in your shopping cart" }), _jsx("ul", { role: "list", className: "divide-y divide-gray-200 border-t border-b border-gray-200", children: products.length === 0 ? (_jsx("li", { className: "py-6 text-center text-gray-500", children: translations?.emptyBasketPlaceholder })) : (products.map((product) => {
25
+ const productKey = product.variation_uuid;
26
+ const isProcessing = processingItems[productKey] || false;
27
+ const displayQuantity = productKey in pendingUpdates
28
+ ? pendingUpdates[productKey]
29
+ : product.quantity;
30
+ return (_jsxs("li", { className: "flex py-6", children: [_jsx("div", { className: "shrink-0", children: _jsx("img", { width: 128, height: 128, alt: product.variation_images[0]?.alt, src: product.variation_images[0]?.href, className: "size-24 rounded-md object-contain sm:size-32" }) }), _jsxs("div", { className: "ml-4 flex flex-1 flex-col sm:ml-6", children: [_jsxs("div", { children: [_jsxs("div", { className: "flex justify-between", children: [_jsx(Heading, { level: 4, children: _jsx(Button, { asLink: true, unstyled: true, href: productLink?.(product.product_name, product.variation_uuid), className: "font-medium text-gray-700 hover:text-gray-800", children: product.product_name }) }), _jsx("span", { className: "ml-4 text-sm font-medium text-gray-900", children: _jsx(Price, { value: product.price, locale: locale, options: productPriceOptions }) })] }), product.size && (_jsx("p", { className: "mt-1 text-sm text-gray-500", children: product.size }))] }), _jsxs("div", { className: "mt-4 flex flex-1 items-end justify-between", children: [_jsx("div", { className: "flex items-center space-x-2 text-sm text-gray-700", children: product.inStock !== undefined && (_jsxs(_Fragment, { children: [product.inStock ? (_jsx(CheckIcon, { "aria-hidden": "true", className: "size-5 shrink-0 text-[var(--primary)]" })) : (_jsx(ClockIcon, { "aria-hidden": "true", className: "size-5 shrink-0 text-gray-300" })), _jsx("span", { children: product.inStock
31
+ ? 'In stock'
32
+ : `Will ship in ${product.leadTime}` })] })) }), _jsxs("div", { className: "flex items-center", children: [_jsx("button", { type: "button", onClick: () => handleDecrement(product, displayQuantity), disabled: isProcessing, className: `rounded-full p-1 text-gray-600 ${isProcessing
33
+ ? 'bg-gray-100 cursor-not-allowed opacity-50'
34
+ : '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: () => handleIncrement(product, displayQuantity), disabled: isProcessing, className: `rounded-full p-1 text-gray-600 ${isProcessing
35
+ ? 'bg-gray-100 cursor-not-allowed opacity-50'
36
+ : 'bg-gray-200 hover:bg-gray-300'}`, children: _jsx(PlusIcon, { className: "h-4 w-4" }) })] })] })] })] }, product.variation_uuid));
37
+ })) })] }), _jsxs("section", { "aria-labelledby": "summary-heading", className: "mt-10", children: [_jsx(Heading, { level: 2, id: "summary-heading", className: "sr-only", children: "Order summary" }), products.length > 0 && onClearBasket && (_jsx("div", { className: "mb-4", children: _jsxs("button", { type: "button", onClick: onClearBasket, className: "inline-flex items-center text-sm font-medium text-red-600 hover:text-red-500", children: [_jsx(TrashIcon, { className: "mr-1 h-5 w-5 text-red-500" }), clearBasketText] }) })), _jsxs("div", { children: [_jsx("dl", { className: "space-y-4", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("dt", { className: "text-base font-medium text-gray-900", children: totalText }), _jsx("dd", { className: "ml-4 text-base font-medium text-gray-900", children: _jsx(Price, { value: subtotal, locale: locale, options: TotalPriceOptions }) })] }) }), showShippingNote && (_jsx("p", { className: "mt-1 text-sm text-gray-500", children: shippingNote }))] }), products.length > 0 && onCheckout && (_jsx("div", { className: "mt-10", children: _jsx("button", { type: "button", onClick: onCheckout, className: "button-brand w-full", children: checkoutText }) })), onContinueShopping && (_jsx("div", { className: "mt-6 text-center text-sm", children: _jsx("p", { children: _jsxs("button", { type: "button", onClick: onContinueShopping, className: "font-medium text-[var(--primary)] hover:text-[var(--primary)]", children: [continueShoppingText, _jsx("span", { "aria-hidden": "true", children: " \u2192" })] }) }) }))] })] })] }) }));
38
+ };