@deneb-ui/ui 2.0.20 → 2.0.21

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.
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ import { CartItem } from './cart/useCart';
3
+ export interface EditableCartDrawerProps {
4
+ basePath?: string;
5
+ whatsappNumber?: string;
6
+ storeName?: string;
7
+ currency?: string;
8
+ checkoutUrl?: string;
9
+ freeShippingThreshold?: number;
10
+ onCheckout?: (items: CartItem[], total: number) => void;
11
+ className?: string;
12
+ }
13
+ export declare function EditableCartDrawer({ basePath, whatsappNumber, storeName, currency, checkoutUrl, freeShippingThreshold, onCheckout, className, }: EditableCartDrawerProps): React.JSX.Element | null;
14
+ export declare const CartDrawer: typeof EditableCartDrawer;
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ 'use client';
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.CartDrawer = void 0;
5
+ exports.EditableCartDrawer = EditableCartDrawer;
6
+ const jsx_runtime_1 = require("react/jsx-runtime");
7
+ const react_1 = require("react");
8
+ const useCart_1 = require("./cart/useCart");
9
+ const SiteDataProvider_1 = require("./SiteDataProvider");
10
+ const urls_1 = require("./utils/urls");
11
+ function EditableCartDrawer({ basePath = 'cart', whatsappNumber = '94770000000', storeName, currency = '$', checkoutUrl, freeShippingThreshold, onCheckout, className = '', }) {
12
+ const { items, isOpen, closeCart, removeItem, updateQuantity, clearCart, totalCount, subtotal, getWhatsAppOrderUrl, } = (0, useCart_1.useCart)();
13
+ const { siteData } = (0, SiteDataProvider_1.useSiteData)();
14
+ // Helper to read content from siteData
15
+ const getContent = (key, fallback) => {
16
+ const section = siteData?.content?.[basePath];
17
+ return section?.[key] || fallback;
18
+ };
19
+ const fieldPath = (key) => `${basePath}.${key}`;
20
+ // Close on Escape key
21
+ (0, react_1.useEffect)(() => {
22
+ const handleKeyDown = (e) => {
23
+ if (e.key === 'Escape')
24
+ closeCart();
25
+ };
26
+ if (isOpen) {
27
+ document.body.style.overflow = 'hidden';
28
+ window.addEventListener('keydown', handleKeyDown);
29
+ }
30
+ return () => {
31
+ document.body.style.overflow = '';
32
+ window.removeEventListener('keydown', handleKeyDown);
33
+ };
34
+ }, [isOpen, closeCart]);
35
+ // If closed, return null so hidden markers do not fail static compliance
36
+ if (!isOpen)
37
+ return null;
38
+ const title = getContent('title', 'Shopping Cart');
39
+ const subtitle = getContent('subtitle', `${totalCount} item${totalCount !== 1 ? 's' : ''} in your cart`);
40
+ const emptyTitle = getContent('emptyTitle', 'Your cart is empty');
41
+ const emptyMessage = getContent('emptyMessage', 'Discover our latest arrivals and add items to your bag.');
42
+ const subtotalLabel = getContent('subtotalLabel', 'Subtotal');
43
+ const shippingNote = getContent('shippingNote', 'Taxes and shipping calculated at checkout');
44
+ const checkoutButtonText = getContent('checkoutButtonText', 'Proceed to Checkout');
45
+ const whatsappButtonText = getContent('whatsappButtonText', 'Order via WhatsApp');
46
+ const clearCartText = getContent('clearCartText', 'Clear all');
47
+ const whatsappHref = getWhatsAppOrderUrl(whatsappNumber, {
48
+ storeName,
49
+ currency,
50
+ });
51
+ const progressPercent = freeShippingThreshold
52
+ ? Math.min(100, Math.round((subtotal / freeShippingThreshold) * 100))
53
+ : 100;
54
+ return ((0, jsx_runtime_1.jsxs)("div", { className: `deneb-cart-drawer-root fixed inset-0 z-50 flex justify-end ${className}`, children: [(0, jsx_runtime_1.jsx)("div", { className: "fixed inset-0 bg-black/60 backdrop-blur-sm transition-opacity", onClick: closeCart, "data-preview-static": "cart-backdrop", "aria-hidden": "true" }), (0, jsx_runtime_1.jsxs)("aside", { className: "relative z-10 flex h-full w-full max-w-md flex-col bg-neutral-900 text-white shadow-2xl transition-transform duration-300 ease-out border-l border-neutral-800", "aria-label": "Shopping Cart Drawer", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between border-b border-neutral-800 px-6 py-5", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-xl font-bold tracking-tight text-white", "data-preview-field-path": fieldPath('title'), children: title }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-neutral-400 mt-0.5", "data-preview-field-path": fieldPath('subtitle'), children: subtitle })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [items.length > 0 && ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: clearCart, className: "text-xs text-neutral-400 hover:text-red-400 transition-colors px-2 py-1 rounded", "data-preview-static": "cart-clear-btn", children: (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": fieldPath('clearCartText'), children: clearCartText }) })), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: closeCart, className: "rounded-full p-2 text-neutral-400 hover:bg-neutral-800 hover:text-white transition-colors", "data-preview-static": "cart-close-btn", "aria-label": "Close cart", children: (0, jsx_runtime_1.jsx)("svg", { className: "h-5 w-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] })] }), freeShippingThreshold && ((0, jsx_runtime_1.jsxs)("div", { className: "border-b border-neutral-800 bg-neutral-950/50 px-6 py-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between text-xs text-neutral-400 mb-1.5", children: [(0, jsx_runtime_1.jsx)("span", { children: subtotal >= freeShippingThreshold ? ((0, jsx_runtime_1.jsx)("span", { className: "font-semibold text-emerald-400", children: "\uD83C\uDF89 You unlocked free shipping!" })) : ((0, jsx_runtime_1.jsxs)("span", { children: ["Add ", (0, jsx_runtime_1.jsxs)("strong", { className: "text-white", children: [currency, (freeShippingThreshold - subtotal).toFixed(2)] }), " more for free shipping"] })) }), (0, jsx_runtime_1.jsxs)("span", { children: [progressPercent, "%"] })] }), (0, jsx_runtime_1.jsx)("div", { className: "h-1.5 w-full overflow-hidden rounded-full bg-neutral-800", children: (0, jsx_runtime_1.jsx)("div", { className: "h-full bg-emerald-500 transition-all duration-300", style: { width: `${progressPercent}%` } }) })] })), (0, jsx_runtime_1.jsx)("div", { className: "flex-1 overflow-y-auto px-6 py-4", children: items.length === 0 ? ((0, jsx_runtime_1.jsxs)("div", { className: "flex h-full flex-col items-center justify-center text-center py-12", children: [(0, jsx_runtime_1.jsx)("div", { className: "mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-neutral-800 text-neutral-400", children: (0, jsx_runtime_1.jsx)("svg", { className: "h-8 w-8", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z" }) }) }), (0, jsx_runtime_1.jsx)("h3", { className: "text-base font-semibold text-white", "data-preview-field-path": fieldPath('emptyTitle'), children: emptyTitle }), (0, jsx_runtime_1.jsx)("p", { className: "mt-1 text-sm text-neutral-400 max-w-xs", "data-preview-field-path": fieldPath('emptyMessage'), children: emptyMessage })] })) : ((0, jsx_runtime_1.jsx)("ul", { className: "divide-y divide-neutral-800", "data-preview-list-path": fieldPath('items'), children: items.map((item, idx) => ((0, jsx_runtime_1.jsxs)("li", { className: "flex gap-4 py-4", "data-preview-item-path": `${fieldPath('items')}[${idx}]`, children: [item.image ? ((0, jsx_runtime_1.jsx)("img", { src: (0, urls_1.withBasePath)(item.image), alt: item.name, className: "h-20 w-20 flex-shrink-0 rounded-lg object-cover bg-neutral-800 border border-neutral-700", "data-preview-static": "cart-item-image" })) : ((0, jsx_runtime_1.jsx)("div", { className: "flex h-20 w-20 flex-shrink-0 items-center justify-center rounded-lg bg-neutral-800 text-neutral-500 border border-neutral-700", "data-preview-static": "cart-item-placeholder", children: (0, jsx_runtime_1.jsx)("svg", { className: "h-8 w-8", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" }) }) })), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-1 flex-col justify-between", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between", children: [(0, jsx_runtime_1.jsx)("h4", { className: "text-sm font-medium text-white line-clamp-1", children: item.name }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => removeItem(item.id, item.size, item.color), className: "text-neutral-500 hover:text-red-400 transition-colors p-1", "data-preview-static": "cart-item-remove", "aria-label": `Remove ${item.name}`, children: (0, jsx_runtime_1.jsx)("svg", { className: "h-4 w-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" }) }) })] }), (item.size || item.color) && ((0, jsx_runtime_1.jsxs)("p", { className: "mt-0.5 text-xs text-neutral-400", children: [item.size && `Size: ${item.size}`, item.size && item.color && ' · ', item.color && `Color: ${item.color}`] })), (0, jsx_runtime_1.jsxs)("p", { className: "mt-1 text-sm font-semibold text-emerald-400", children: [currency, (item.price * item.quantity).toFixed(2)] })] }), (0, jsx_runtime_1.jsx)("div", { className: "flex items-center gap-2 mt-2", children: (0, jsx_runtime_1.jsxs)("div", { className: "inline-flex items-center rounded-lg border border-neutral-700 bg-neutral-800", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => updateQuantity(item.id, item.quantity - 1, item.size, item.color), className: "h-7 w-7 flex items-center justify-center text-neutral-300 hover:text-white transition-colors", "data-preview-static": "cart-item-decrement", "aria-label": "Decrease quantity", children: "-" }), (0, jsx_runtime_1.jsx)("span", { className: "w-8 text-center text-xs font-semibold text-white", children: item.quantity }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => updateQuantity(item.id, item.quantity + 1, item.size, item.color), className: "h-7 w-7 flex items-center justify-center text-neutral-300 hover:text-white transition-colors", "data-preview-static": "cart-item-increment", "aria-label": "Increase quantity", children: "+" })] }) })] })] }, `${item.id}-${item.size || ''}-${item.color || ''}`))) })) }), items.length > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "border-t border-neutral-800 bg-neutral-950 px-6 py-5", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-baseline mb-2", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-sm text-neutral-400", "data-preview-field-path": fieldPath('subtotalLabel'), children: subtotalLabel }), (0, jsx_runtime_1.jsxs)("span", { className: "text-xl font-bold text-white", children: [currency, subtotal.toFixed(2)] })] }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-neutral-500 mb-4", "data-preview-field-path": fieldPath('shippingNote'), children: shippingNote }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-2.5", children: [(0, jsx_runtime_1.jsxs)("a", { href: whatsappHref, target: "_blank", rel: "noopener noreferrer", "data-preview-static": "cart-whatsapp-cta", className: "flex w-full items-center justify-center gap-2 rounded-xl bg-emerald-600 px-5 py-3.5 text-sm font-semibold text-white shadow-lg shadow-emerald-900/30 hover:bg-emerald-500 transition-all active:scale-[0.98]", children: [(0, jsx_runtime_1.jsx)("svg", { className: "h-4 w-4 fill-current", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { d: "M12.031 6.172c-3.181 0-5.767 2.586-5.768 5.766-.001 1.298.38 2.27 1.019 3.287l-.711 2.598 2.664-.699c.971.531 1.769.813 2.796.813h.001c3.182 0 5.768-2.586 5.768-5.766 0-3.18-2.586-5.766-5.769-5.766zm9.969 5.766c0 5.518-4.482 10-10 10-1.745 0-3.415-.453-4.887-1.258l-5.113 1.341 1.365-4.989c-.931-1.537-1.465-3.327-1.465-5.094 0-5.518 4.482-10 10-10 5.518 0 10 4.482 10 10z" }) }), (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": fieldPath('whatsappButtonText'), children: whatsappButtonText })] }), (checkoutUrl || onCheckout) && ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => {
55
+ if (onCheckout)
56
+ onCheckout(items, subtotal);
57
+ if (checkoutUrl)
58
+ window.location.href = checkoutUrl;
59
+ }, "data-preview-static": "cart-checkout-btn", className: "flex w-full items-center justify-center rounded-xl bg-neutral-800 px-5 py-3 text-sm font-semibold text-white hover:bg-neutral-700 transition-colors", children: (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": fieldPath('checkoutButtonText'), children: checkoutButtonText }) }))] })] }))] })] }));
60
+ }
61
+ exports.CartDrawer = EditableCartDrawer;
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ export interface FilterState {
3
+ selectedCategories: string[];
4
+ priceRange: [number, number];
5
+ selectedSizes: string[];
6
+ inStockOnly?: boolean;
7
+ }
8
+ export interface EditableFilterSidebarProps {
9
+ basePath?: string;
10
+ categories?: string[];
11
+ sizes?: string[];
12
+ minPrice?: number;
13
+ maxPrice?: number;
14
+ currency?: string;
15
+ initialFilters?: Partial<FilterState>;
16
+ onFilterChange?: (filters: FilterState) => void;
17
+ className?: string;
18
+ }
19
+ export declare function EditableFilterSidebar({ basePath, categories, sizes, minPrice, maxPrice, currency, initialFilters, onFilterChange, className, }: EditableFilterSidebarProps): React.JSX.Element;
20
+ export declare const FilterSidebar: typeof EditableFilterSidebar;
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ 'use client';
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.FilterSidebar = void 0;
5
+ exports.EditableFilterSidebar = EditableFilterSidebar;
6
+ const jsx_runtime_1 = require("react/jsx-runtime");
7
+ const react_1 = require("react");
8
+ const SiteDataProvider_1 = require("./SiteDataProvider");
9
+ function EditableFilterSidebar({ basePath = 'filters', categories = ['All', 'Running', 'Lifestyle', 'Basketball', 'Training', 'Outdoor'], sizes = ['US 7', 'US 8', 'US 9', 'US 10', 'US 11', 'US 12'], minPrice = 0, maxPrice = 300, currency = '$', initialFilters, onFilterChange, className = '', }) {
10
+ const { siteData } = (0, SiteDataProvider_1.useSiteData)();
11
+ const [selectedCategories, setSelectedCategories] = (0, react_1.useState)(initialFilters?.selectedCategories || []);
12
+ const [currentPrice, setCurrentPrice] = (0, react_1.useState)(initialFilters?.priceRange ? initialFilters.priceRange[1] : maxPrice);
13
+ const [selectedSizes, setSelectedSizes] = (0, react_1.useState)(initialFilters?.selectedSizes || []);
14
+ const [inStockOnly, setInStockOnly] = (0, react_1.useState)(initialFilters?.inStockOnly || false);
15
+ const getContent = (key, fallback) => {
16
+ const section = siteData?.content?.[basePath];
17
+ return section?.[key] || fallback;
18
+ };
19
+ const fieldPath = (key) => `${basePath}.${key}`;
20
+ const triggerChange = (nextCats, nextPrice, nextSizes, nextInStock) => {
21
+ if (onFilterChange) {
22
+ onFilterChange({
23
+ selectedCategories: nextCats,
24
+ priceRange: [minPrice, nextPrice],
25
+ selectedSizes: nextSizes,
26
+ inStockOnly: nextInStock,
27
+ });
28
+ }
29
+ };
30
+ const toggleCategory = (cat) => {
31
+ const next = selectedCategories.includes(cat)
32
+ ? selectedCategories.filter((c) => c !== cat)
33
+ : [...selectedCategories, cat];
34
+ setSelectedCategories(next);
35
+ triggerChange(next, currentPrice, selectedSizes, inStockOnly);
36
+ };
37
+ const toggleSize = (size) => {
38
+ const next = selectedSizes.includes(size)
39
+ ? selectedSizes.filter((s) => s !== size)
40
+ : [...selectedSizes, size];
41
+ setSelectedSizes(next);
42
+ triggerChange(selectedCategories, currentPrice, next, inStockOnly);
43
+ };
44
+ const handlePriceChange = (e) => {
45
+ const val = Number(e.target.value);
46
+ setCurrentPrice(val);
47
+ triggerChange(selectedCategories, val, selectedSizes, inStockOnly);
48
+ };
49
+ const toggleInStock = () => {
50
+ const next = !inStockOnly;
51
+ setInStockOnly(next);
52
+ triggerChange(selectedCategories, currentPrice, selectedSizes, next);
53
+ };
54
+ const handleClearAll = () => {
55
+ setSelectedCategories([]);
56
+ setCurrentPrice(maxPrice);
57
+ setSelectedSizes([]);
58
+ setInStockOnly(false);
59
+ triggerChange([], maxPrice, [], false);
60
+ };
61
+ const activeFiltersCount = selectedCategories.length +
62
+ selectedSizes.length +
63
+ (currentPrice < maxPrice ? 1 : 0) +
64
+ (inStockOnly ? 1 : 0);
65
+ const title = getContent('title', 'Filters');
66
+ const categoryTitle = getContent('categoryTitle', 'Categories');
67
+ const priceTitle = getContent('priceTitle', 'Price Range');
68
+ const sizeTitle = getContent('sizeTitle', 'Size');
69
+ const availabilityTitle = getContent('availabilityTitle', 'Availability');
70
+ const inStockLabel = getContent('inStockLabel', 'In Stock Only');
71
+ const clearAllText = getContent('clearAllText', 'Clear All');
72
+ return ((0, jsx_runtime_1.jsxs)("aside", { className: `deneb-filter-sidebar w-full rounded-2xl border border-neutral-800 bg-neutral-900/80 backdrop-blur-md p-6 text-white shadow-xl ${className}`, "aria-label": "Product Filters", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between border-b border-neutral-800 pb-4 mb-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-lg font-bold tracking-tight text-white", "data-preview-field-path": fieldPath('title'), children: title }), activeFiltersCount > 0 && ((0, jsx_runtime_1.jsx)("span", { className: "flex h-5 w-5 items-center justify-center rounded-full bg-emerald-500 text-xs font-bold text-black", "data-preview-static": "filter-active-count", children: activeFiltersCount }))] }), activeFiltersCount > 0 && ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: handleClearAll, className: "text-xs font-medium text-neutral-400 hover:text-white transition-colors", "data-preview-static": "filter-clear-all", children: (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": fieldPath('clearAllText'), children: clearAllText }) }))] }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-6", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h4", { className: "text-xs font-semibold uppercase tracking-wider text-neutral-400 mb-3", "data-preview-field-path": fieldPath('categoryTitle'), children: categoryTitle }), (0, jsx_runtime_1.jsx)("div", { className: "flex flex-wrap gap-2", children: categories.map((cat) => {
73
+ const isSelected = selectedCategories.includes(cat);
74
+ return ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => toggleCategory(cat), "data-preview-static": "filter-cat-btn", className: `rounded-lg px-3 py-1.5 text-xs font-medium transition-all ${isSelected
75
+ ? 'bg-white text-black shadow-md'
76
+ : 'bg-neutral-800 text-neutral-300 hover:bg-neutral-700 hover:text-white'}`, children: cat }, cat));
77
+ }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "border-t border-neutral-800/80 pt-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-center mb-2", children: [(0, jsx_runtime_1.jsx)("h4", { className: "text-xs font-semibold uppercase tracking-wider text-neutral-400", "data-preview-field-path": fieldPath('priceTitle'), children: priceTitle }), (0, jsx_runtime_1.jsxs)("span", { className: "text-sm font-semibold text-emerald-400", "data-preview-static": "filter-price-display", children: ["Up to ", currency, currentPrice] })] }), (0, jsx_runtime_1.jsx)("input", { type: "range", min: minPrice, max: maxPrice, step: 5, value: currentPrice, onChange: handlePriceChange, "data-preview-static": "filter-price-slider", className: "w-full accent-emerald-500 bg-neutral-800 h-1.5 rounded-lg cursor-pointer" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between text-xs text-neutral-500 mt-1", children: [(0, jsx_runtime_1.jsxs)("span", { children: [currency, minPrice] }), (0, jsx_runtime_1.jsxs)("span", { children: [currency, maxPrice] })] })] }), sizes.length > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "border-t border-neutral-800/80 pt-6", children: [(0, jsx_runtime_1.jsx)("h4", { className: "text-xs font-semibold uppercase tracking-wider text-neutral-400 mb-3", "data-preview-field-path": fieldPath('sizeTitle'), children: sizeTitle }), (0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-3 gap-2", children: sizes.map((size) => {
78
+ const isSelected = selectedSizes.includes(size);
79
+ return ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => toggleSize(size), "data-preview-static": "filter-size-btn", className: `rounded-lg py-2 text-xs font-semibold transition-all border ${isSelected
80
+ ? 'border-white bg-white text-black'
81
+ : 'border-neutral-800 bg-neutral-800/50 text-neutral-300 hover:border-neutral-700'}`, children: size }, size));
82
+ }) })] })), (0, jsx_runtime_1.jsx)("div", { className: "border-t border-neutral-800/80 pt-6", children: (0, jsx_runtime_1.jsxs)("label", { className: "flex items-center justify-between cursor-pointer group", "data-preview-static": "filter-instock-toggle", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-xs font-medium text-neutral-300 group-hover:text-white transition-colors", "data-preview-field-path": fieldPath('inStockLabel'), children: inStockLabel }), (0, jsx_runtime_1.jsx)("input", { type: "checkbox", checked: inStockOnly, onChange: toggleInStock, className: "h-4 w-4 rounded border-neutral-700 bg-neutral-800 text-emerald-500 accent-emerald-500 cursor-pointer" })] }) })] })] }));
83
+ }
84
+ exports.FilterSidebar = EditableFilterSidebar;
@@ -0,0 +1,37 @@
1
+ import React from 'react';
2
+ export interface CartItem {
3
+ id: string;
4
+ name: string;
5
+ price: number;
6
+ originalPrice?: number;
7
+ image?: string;
8
+ size?: string;
9
+ color?: string;
10
+ quantity: number;
11
+ sku?: string;
12
+ metadata?: Record<string, unknown>;
13
+ }
14
+ export interface CartContextValue {
15
+ items: CartItem[];
16
+ isOpen: boolean;
17
+ totalCount: number;
18
+ subtotal: number;
19
+ openCart: () => void;
20
+ closeCart: () => void;
21
+ toggleCart: () => void;
22
+ addItem: (item: Omit<CartItem, 'quantity'> & {
23
+ quantity?: number;
24
+ }) => void;
25
+ removeItem: (id: string, size?: string, color?: string) => void;
26
+ updateQuantity: (id: string, quantity: number, size?: string, color?: string) => void;
27
+ clearCart: () => void;
28
+ getWhatsAppOrderUrl: (whatsappNumber: string, options?: {
29
+ storeName?: string;
30
+ currency?: string;
31
+ }) => string;
32
+ }
33
+ export declare function CartProvider({ children, storageKey, }: {
34
+ children: React.ReactNode;
35
+ storageKey?: string;
36
+ }): React.JSX.Element;
37
+ export declare function useCart(): CartContextValue;
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+ 'use client';
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.CartProvider = CartProvider;
5
+ exports.useCart = useCart;
6
+ const jsx_runtime_1 = require("react/jsx-runtime");
7
+ const react_1 = require("react");
8
+ const CartContext = (0, react_1.createContext)(null);
9
+ const STORAGE_KEY = 'deneb_cart_items_v1';
10
+ function CartProvider({ children, storageKey = STORAGE_KEY, }) {
11
+ const [items, setItems] = (0, react_1.useState)([]);
12
+ const [isOpen, setIsOpen] = (0, react_1.useState)(false);
13
+ const [hasHydrated, setHasHydrated] = (0, react_1.useState)(false);
14
+ // Safely hydrate from localStorage after client mount
15
+ (0, react_1.useEffect)(() => {
16
+ try {
17
+ const stored = localStorage.getItem(storageKey);
18
+ if (stored) {
19
+ const parsed = JSON.parse(stored);
20
+ if (Array.isArray(parsed)) {
21
+ setItems(parsed);
22
+ }
23
+ }
24
+ }
25
+ catch {
26
+ // Ignore localStorage errors (e.g. private browsing)
27
+ }
28
+ setHasHydrated(true);
29
+ }, [storageKey]);
30
+ // Persist items to localStorage on change (after initial mount)
31
+ (0, react_1.useEffect)(() => {
32
+ if (!hasHydrated)
33
+ return;
34
+ try {
35
+ localStorage.setItem(storageKey, JSON.stringify(items));
36
+ }
37
+ catch {
38
+ // Ignore quota exceeded
39
+ }
40
+ }, [items, hasHydrated, storageKey]);
41
+ const openCart = (0, react_1.useCallback)(() => setIsOpen(true), []);
42
+ const closeCart = (0, react_1.useCallback)(() => setIsOpen(false), []);
43
+ const toggleCart = (0, react_1.useCallback)(() => setIsOpen((prev) => !prev), []);
44
+ const getItemKey = (id, size, color) => `${id}-${size || ''}-${color || ''}`;
45
+ const addItem = (0, react_1.useCallback)((newItem) => {
46
+ const qty = newItem.quantity && newItem.quantity > 0 ? newItem.quantity : 1;
47
+ setItems((prevItems) => {
48
+ const targetKey = getItemKey(newItem.id, newItem.size, newItem.color);
49
+ const existingIndex = prevItems.findIndex((item) => getItemKey(item.id, item.size, item.color) === targetKey);
50
+ if (existingIndex > -1) {
51
+ const copy = [...prevItems];
52
+ copy[existingIndex] = {
53
+ ...copy[existingIndex],
54
+ quantity: copy[existingIndex].quantity + qty,
55
+ };
56
+ return copy;
57
+ }
58
+ return [...prevItems, { ...newItem, quantity: qty }];
59
+ });
60
+ setIsOpen(true);
61
+ }, []);
62
+ const removeItem = (0, react_1.useCallback)((id, size, color) => {
63
+ const targetKey = getItemKey(id, size, color);
64
+ setItems((prev) => prev.filter((item) => getItemKey(item.id, item.size, item.color) !== targetKey));
65
+ }, []);
66
+ const updateQuantity = (0, react_1.useCallback)((id, quantity, size, color) => {
67
+ const targetKey = getItemKey(id, size, color);
68
+ setItems((prev) => {
69
+ if (quantity <= 0) {
70
+ return prev.filter((item) => getItemKey(item.id, item.size, item.color) !== targetKey);
71
+ }
72
+ return prev.map((item) => getItemKey(item.id, item.size, item.color) === targetKey
73
+ ? { ...item, quantity }
74
+ : item);
75
+ });
76
+ }, []);
77
+ const clearCart = (0, react_1.useCallback)(() => setItems([]), []);
78
+ const totalCount = (0, react_1.useMemo)(() => items.reduce((acc, item) => acc + item.quantity, 0), [items]);
79
+ const subtotal = (0, react_1.useMemo)(() => items.reduce((acc, item) => acc + item.price * item.quantity, 0), [items]);
80
+ const getWhatsAppOrderUrl = (0, react_1.useCallback)((whatsappNumber, options) => {
81
+ const cleanNumber = whatsappNumber.replace(/\D/g, '');
82
+ const currency = options?.currency || '$';
83
+ const store = options?.storeName ? ` at *${options.storeName}*` : '';
84
+ const lines = [
85
+ `*New Order Request${store}*`,
86
+ `Date: ${new Date().toLocaleDateString()}`,
87
+ '',
88
+ '*Order Items:*',
89
+ ];
90
+ items.forEach((item, index) => {
91
+ const variantParts = [];
92
+ if (item.size)
93
+ variantParts.push(`Size: ${item.size}`);
94
+ if (item.color)
95
+ variantParts.push(`Color: ${item.color}`);
96
+ const variantStr = variantParts.length > 0 ? ` (${variantParts.join(', ')})` : '';
97
+ lines.push(`${index + 1}. *${item.name}*${variantStr}`);
98
+ lines.push(` Qty: ${item.quantity} x ${currency}${item.price.toFixed(2)} = *${currency}${(item.price * item.quantity).toFixed(2)}*`);
99
+ });
100
+ lines.push('');
101
+ lines.push(`*Total Amount: ${currency}${subtotal.toFixed(2)}*`);
102
+ lines.push(`Total Items: ${totalCount}`);
103
+ lines.push('');
104
+ lines.push('Please confirm availability and delivery details. Thank you!');
105
+ return `https://wa.me/${cleanNumber}?text=${encodeURIComponent(lines.join('\n'))}`;
106
+ }, [items, subtotal, totalCount]);
107
+ const value = (0, react_1.useMemo)(() => ({
108
+ items,
109
+ isOpen,
110
+ totalCount,
111
+ subtotal,
112
+ openCart,
113
+ closeCart,
114
+ toggleCart,
115
+ addItem,
116
+ removeItem,
117
+ updateQuantity,
118
+ clearCart,
119
+ getWhatsAppOrderUrl,
120
+ }), [
121
+ items,
122
+ isOpen,
123
+ totalCount,
124
+ subtotal,
125
+ openCart,
126
+ closeCart,
127
+ toggleCart,
128
+ addItem,
129
+ removeItem,
130
+ updateQuantity,
131
+ clearCart,
132
+ getWhatsAppOrderUrl,
133
+ ]);
134
+ return (0, jsx_runtime_1.jsx)(CartContext.Provider, { value: value, children: children });
135
+ }
136
+ function useCart() {
137
+ const context = (0, react_1.useContext)(CartContext);
138
+ if (!context) {
139
+ throw new Error('useCart must be used within a <CartProvider>');
140
+ }
141
+ return context;
142
+ }
package/dist/index.d.ts CHANGED
@@ -28,7 +28,9 @@ export * from './StickyMobileBar';
28
28
  export * from './TrustBadges';
29
29
  export * from './ProductQuickView';
30
30
  export * from './CookieConsentBanner';
31
- export * from './ThemeStyles';
31
+ export * from './cart/useCart';
32
+ export * from './EditableCartDrawer';
33
+ export * from './EditableFilterSidebar';
32
34
  export * from './SiteDataProvider';
33
35
  export * from './utils';
34
36
  export * from './core';
@@ -68,6 +70,8 @@ export { TrustBadges } from './TrustBadges';
68
70
  export { ProductQuickView } from './ProductQuickView';
69
71
  export { CookieConsentBanner } from './CookieConsentBanner';
70
72
  export { FloatingContactWidget } from './contact/FloatingContactWidget';
73
+ export { EditableCartDrawer as CartDrawer } from './EditableCartDrawer';
74
+ export { EditableFilterSidebar as FilterSidebar } from './EditableFilterSidebar';
71
75
  /**
72
76
  * DENEB UI Framework Metadata
73
77
  * Developed by Chamika Gayashan & Induranga Kawishwara
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
18
18
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
19
19
  };
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.DENEB_AUTHOR = exports.DENEB_FRAMEWORK_VERSION = exports.DENEB_FRAMEWORK_NAME = exports.FloatingContactWidget = exports.CookieConsentBanner = exports.ProductQuickView = exports.TrustBadges = exports.StickyMobileBar = exports.CategoryPills = exports.AnnouncementBar = exports.HeroSplit = exports.Hero = exports.Footer = exports.Header = exports.Navbar = exports.ContactForm = exports.FAQ = exports.Accordion = exports.TestimonialCard = exports.PricingCard = exports.ServiceCard = exports.CustomerReviews = exports.ProductDetail = exports.ProductGrid = exports.ProductCard = exports.List = exports.Box = exports.Section = exports.Grid = exports.Image = exports.Quote = exports.Badge = exports.Paragraph = exports.Heading = exports.Text = exports.Dialog = exports.Card = exports.Button = void 0;
21
+ exports.DENEB_AUTHOR = exports.DENEB_FRAMEWORK_VERSION = exports.DENEB_FRAMEWORK_NAME = exports.FilterSidebar = exports.CartDrawer = exports.FloatingContactWidget = exports.CookieConsentBanner = exports.ProductQuickView = exports.TrustBadges = exports.StickyMobileBar = exports.CategoryPills = exports.AnnouncementBar = exports.HeroSplit = exports.Hero = exports.Footer = exports.Header = exports.Navbar = exports.ContactForm = exports.FAQ = exports.Accordion = exports.TestimonialCard = exports.PricingCard = exports.ServiceCard = exports.CustomerReviews = exports.ProductDetail = exports.ProductGrid = exports.ProductCard = exports.List = exports.Box = exports.Section = exports.Grid = exports.Image = exports.Quote = exports.Badge = exports.Paragraph = exports.Heading = exports.Text = exports.Dialog = exports.Card = exports.Button = void 0;
22
22
  __exportStar(require("./EditableText"), exports);
23
23
  __exportStar(require("./EditableImage"), exports);
24
24
  __exportStar(require("./EditableList"), exports);
@@ -45,7 +45,9 @@ __exportStar(require("./StickyMobileBar"), exports);
45
45
  __exportStar(require("./TrustBadges"), exports);
46
46
  __exportStar(require("./ProductQuickView"), exports);
47
47
  __exportStar(require("./CookieConsentBanner"), exports);
48
- __exportStar(require("./ThemeStyles"), exports);
48
+ __exportStar(require("./cart/useCart"), exports);
49
+ __exportStar(require("./EditableCartDrawer"), exports);
50
+ __exportStar(require("./EditableFilterSidebar"), exports);
49
51
  __exportStar(require("./SiteDataProvider"), exports);
50
52
  // Smart template components & actions
51
53
  __exportStar(require("./utils"), exports);
@@ -122,6 +124,10 @@ var CookieConsentBanner_1 = require("./CookieConsentBanner");
122
124
  Object.defineProperty(exports, "CookieConsentBanner", { enumerable: true, get: function () { return CookieConsentBanner_1.CookieConsentBanner; } });
123
125
  var FloatingContactWidget_1 = require("./contact/FloatingContactWidget");
124
126
  Object.defineProperty(exports, "FloatingContactWidget", { enumerable: true, get: function () { return FloatingContactWidget_1.FloatingContactWidget; } });
127
+ var EditableCartDrawer_1 = require("./EditableCartDrawer");
128
+ Object.defineProperty(exports, "CartDrawer", { enumerable: true, get: function () { return EditableCartDrawer_1.EditableCartDrawer; } });
129
+ var EditableFilterSidebar_1 = require("./EditableFilterSidebar");
130
+ Object.defineProperty(exports, "FilterSidebar", { enumerable: true, get: function () { return EditableFilterSidebar_1.EditableFilterSidebar; } });
125
131
  /**
126
132
  * DENEB UI Framework Metadata
127
133
  * Developed by Chamika Gayashan & Induranga Kawishwara
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deneb-ui/ui",
3
- "version": "2.0.20",
3
+ "version": "2.0.21",
4
4
  "description": "DENEB UI - The premier visual-first React component framework for the modern web. Created by Chamika Gayashan & Induranga Kawishwara.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",