@deneb-ui/ui 2.0.76 → 2.0.78

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.
@@ -41,6 +41,7 @@ export interface ProductDetailItem {
41
41
  colors?: Array<string | {
42
42
  name: string;
43
43
  hex?: string;
44
+ image?: string;
44
45
  }> | string;
45
46
  colorsText?: string;
46
47
  colorsLabel?: string;
@@ -56,15 +57,18 @@ export interface EditableProductDetailProps extends React.HTMLAttributes<HTMLEle
56
57
  */
57
58
  product: ProductDetailItem;
58
59
  /**
59
- * Available sizes (e.g. ['40', '41', '42', '43', '44', '45', '46']).
60
+ * Optional custom available sizes or options (e.g. ['S', 'M', 'L'] or ['50ml', '100ml']).
61
+ * If omitted, resolved dynamically from the product object.
60
62
  */
61
63
  sizes?: string[];
62
64
  /**
63
- * Available color swatches.
65
+ * Optional custom color swatches.
66
+ * If omitted, resolved dynamically from the product object.
64
67
  */
65
68
  colors?: Array<{
66
69
  name: string;
67
70
  hex: string;
71
+ image?: string;
68
72
  }>;
69
73
  /**
70
74
  * Callback on adding product to selection or cart.
@@ -81,12 +85,14 @@ export interface EditableProductDetailProps extends React.HTMLAttributes<HTMLEle
81
85
  * engineered for high-conversion commerce and 100% compliant with Fivora Visual Editing.
82
86
  *
83
87
  * Features:
84
- * - Multi-image gallery with active thumbnail selector and basePath resolution
85
- * - Floating badges with conditional rendering (zero hidden marker contract violations)
86
- * - Live synchronized field paths for Title, Price, Description, CTAs, and Policies
87
- * - Interactive size & color pickers
88
- * - Direct WhatsApp Click-to-Order integration
89
- * - Specifications and Shipping & Returns tabs
88
+ * - Truly Adaptive: Seamlessly renders shoes/apparel (sizes), liquids/cosmetics (ml, L),
89
+ * food/groceries (g, kg), single-dimension items, and simple products without variants.
90
+ * - Multi-image gallery with active thumbnail selector and basePath resolution.
91
+ * - Dynamic color swatches with optional color-to-image auto-switching.
92
+ * - Floating badges with conditional rendering (zero hidden marker contract violations).
93
+ * - Live synchronized field paths for Title, Price, Description, CTAs, and Policies.
94
+ * - Direct WhatsApp Click-to-Order integration with dynamic variant snippets.
95
+ * - Context-aware specifications and Shipping & Returns tabs.
90
96
  *
91
97
  * Created by Chamika Gayashan & Induranga Kawishwara
92
98
  */
@@ -11,33 +11,101 @@ const urls_1 = require("./utils/urls");
11
11
  * engineered for high-conversion commerce and 100% compliant with Fivora Visual Editing.
12
12
  *
13
13
  * Features:
14
- * - Multi-image gallery with active thumbnail selector and basePath resolution
15
- * - Floating badges with conditional rendering (zero hidden marker contract violations)
16
- * - Live synchronized field paths for Title, Price, Description, CTAs, and Policies
17
- * - Interactive size & color pickers
18
- * - Direct WhatsApp Click-to-Order integration
19
- * - Specifications and Shipping & Returns tabs
14
+ * - Truly Adaptive: Seamlessly renders shoes/apparel (sizes), liquids/cosmetics (ml, L),
15
+ * food/groceries (g, kg), single-dimension items, and simple products without variants.
16
+ * - Multi-image gallery with active thumbnail selector and basePath resolution.
17
+ * - Dynamic color swatches with optional color-to-image auto-switching.
18
+ * - Floating badges with conditional rendering (zero hidden marker contract violations).
19
+ * - Live synchronized field paths for Title, Price, Description, CTAs, and Policies.
20
+ * - Direct WhatsApp Click-to-Order integration with dynamic variant snippets.
21
+ * - Context-aware specifications and Shipping & Returns tabs.
20
22
  *
21
23
  * Created by Chamika Gayashan & Induranga Kawishwara
22
24
  */
23
- function EditableProductDetail({ sectionPath = 'product', product, sizes = ['40', '41', '42', '43', '44', '45', '46'], colors = [
24
- { name: 'Ivory', hex: '#F0EFEB' },
25
- { name: 'Obsidian', hex: '#1C2541' },
26
- ], onAddToSelection, whatsappUrl, className = '', style, ...props }) {
27
- const images = (product.gallery && product.gallery.length > 0)
28
- ? product.gallery
29
- : [product.featuredImage || product.imageUrl || '/products/vanta-aero-x.jpg'];
25
+ function EditableProductDetail({ sectionPath = 'product', product, sizes, colors, onAddToSelection, whatsappUrl, className = '', style, ...props }) {
26
+ // 1. Resolve Images: Support gallery, images array, and single image fields
27
+ const validImages = (0, react_1.useMemo)(() => {
28
+ const rawList = (product.gallery && product.gallery.length > 0)
29
+ ? product.gallery
30
+ : (product.images && product.images.length > 0)
31
+ ? product.images
32
+ : [product.featuredImage || product.imageUrl || product.image || '/products/vanta-aero-x.jpg'];
33
+ return rawList.filter(Boolean);
34
+ }, [product.gallery, product.images, product.featuredImage, product.imageUrl, product.image]);
35
+ // 2. Resolve Options (Sizes, Volumes, Weights, Counts)
30
36
  const resolved = (0, productOptions_1.resolveProductOptions)(product);
31
- const effectiveOptions = product.options || product.optionsText
32
- ? resolved.options
33
- : (product.sizes || product.sizesText)
34
- ? resolved.options
35
- : sizes;
36
- const effectiveOptionsLabel = resolved.optionsLabel || 'Available Sizes';
37
- const [activeImage, setActiveImage] = (0, react_1.useState)(images[0] || '');
37
+ const effectiveOptions = (0, react_1.useMemo)(() => {
38
+ const hasProductOptions = Boolean((product.options && product.options.length > 0) ||
39
+ (typeof product.optionsText === 'string' && product.optionsText.trim()) ||
40
+ (product.sizes && (Array.isArray(product.sizes) ? product.sizes.length > 0 : Boolean(product.sizes))) ||
41
+ (typeof product.sizesText === 'string' && product.sizesText.trim()) ||
42
+ (typeof product.measurement === 'string' && product.measurement.trim()));
43
+ if (hasProductOptions) {
44
+ return resolved.options;
45
+ }
46
+ if (sizes && sizes.length > 0) {
47
+ return sizes;
48
+ }
49
+ return [];
50
+ }, [product.options, product.optionsText, product.sizes, product.sizesText, product.measurement, resolved.options, sizes]);
51
+ const effectiveOptionsLabel = resolved.optionsLabel || 'Available Options';
52
+ // 3. Resolve Colors
53
+ const effectiveColors = (0, react_1.useMemo)(() => {
54
+ if (Array.isArray(product.colors) && product.colors.length > 0) {
55
+ return product.colors.map((c) => {
56
+ if (typeof c === 'string') {
57
+ return { name: c.trim(), hex: undefined };
58
+ }
59
+ return c;
60
+ });
61
+ }
62
+ if (typeof product.colorsText === 'string' && product.colorsText.trim()) {
63
+ return product.colorsText
64
+ .split(',')
65
+ .map((s) => s.trim())
66
+ .filter(Boolean)
67
+ .map((name) => ({ name, hex: undefined }));
68
+ }
69
+ if (colors && colors.length > 0) {
70
+ return colors;
71
+ }
72
+ return [];
73
+ }, [product.colors, product.colorsText, colors]);
74
+ // Component States
75
+ const [activeImage, setActiveImage] = (0, react_1.useState)(validImages[0] || '');
38
76
  const [selectedSize, setSelectedSize] = (0, react_1.useState)(effectiveOptions[0] || '');
39
- const [selectedColor, setSelectedColor] = (0, react_1.useState)(colors[0]?.name || '');
77
+ const [selectedColor, setSelectedColor] = (0, react_1.useState)(effectiveColors[0]?.name || '');
40
78
  const [activeTab, setActiveTab] = (0, react_1.useState)('specs');
79
+ // Keep active image in sync when product or images change
80
+ (0, react_1.useEffect)(() => {
81
+ if (validImages.length > 0 && !validImages.includes(activeImage)) {
82
+ setActiveImage(validImages[0]);
83
+ }
84
+ }, [validImages, activeImage]);
85
+ // Keep selected size in sync when options change
86
+ (0, react_1.useEffect)(() => {
87
+ if (effectiveOptions.length > 0 && (!selectedSize || !effectiveOptions.includes(selectedSize))) {
88
+ setSelectedSize(effectiveOptions[0]);
89
+ }
90
+ else if (effectiveOptions.length === 0 && selectedSize) {
91
+ setSelectedSize('');
92
+ }
93
+ }, [effectiveOptions, selectedSize]);
94
+ // Keep selected color in sync when colors change
95
+ (0, react_1.useEffect)(() => {
96
+ if (effectiveColors.length > 0 && (!selectedColor || !effectiveColors.some((c) => c.name === selectedColor))) {
97
+ setSelectedColor(effectiveColors[0]?.name || '');
98
+ }
99
+ else if (effectiveColors.length === 0 && selectedColor) {
100
+ setSelectedColor('');
101
+ }
102
+ }, [effectiveColors, selectedColor]);
103
+ const handleSelectColor = (c) => {
104
+ setSelectedColor(c.name);
105
+ if (c.image) {
106
+ setActiveImage(c.image);
107
+ }
108
+ };
41
109
  const name = String(product.name || product.title || 'Product Title');
42
110
  const price = product.price !== undefined ? String(product.price) : 'LKR 0';
43
111
  const originalPrice = product.originalPrice ?? product.compareAtPrice;
@@ -49,17 +117,35 @@ function EditableProductDetail({ sectionPath = 'product', product, sizes = ['40'
49
117
  const shippingTitle = String(product.shippingTitle || 'Shipping & Returns');
50
118
  const shippingSummary = String(product.shippingSummary || 'Free Islandwide Delivery within 2-3 business days. Cash on delivery available.');
51
119
  const shippingReturns = String(product.shippingReturns || '14-day hassle-free exchanges for unworn items in original packaging.');
52
- const orderSnippet = resolved.formatOrderSnippet(selectedSize, selectedColor);
120
+ const orderSnippet = resolved.formatOrderSnippet(effectiveOptions.length > 0 ? selectedSize : undefined, effectiveColors.length > 0 ? selectedColor : undefined);
53
121
  const resolvedWhatsappUrl = whatsappUrl || (product.whatsappNumber
54
122
  ? (0, urls_1.createWhatsAppUrl)(product.whatsappNumber, product.whatsappMessage || `Hi, I would like to order ${name}${orderSnippet ? ` ${orderSnippet}` : ''} - ${price}`)
55
123
  : '');
56
- return ((0, jsx_runtime_1.jsx)("section", { "data-preview-page-key": sectionPath, className: `editable-product-detail max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12 ${className}`.trim(), style: style, ...props, children: (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-1 lg:grid-cols-12 gap-12 lg:gap-16 items-start", children: [(0, jsx_runtime_1.jsxs)("div", { className: "lg:col-span-7 flex flex-col-reverse md:flex-row gap-4", children: [images.length > 1 && ((0, jsx_runtime_1.jsx)("div", { className: "deneb-product-detail-gallery flex md:flex-col gap-3 overflow-x-auto md:overflow-y-auto max-h-[580px] pb-2 md:pb-0 scrollbar-none", children: images.map((img, idx) => ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => setActiveImage(img), className: `w-20 h-20 sm:w-24 sm:h-24 rounded-2xl overflow-hidden border-2 transition-all duration-200 flex-shrink-0 bg-slate-900/40 p-2 ${activeImage === img
57
- ? 'border-brand-primary scale-98 shadow-md'
124
+ // Dynamic Specs: Use explicit specs if provided, else fallback to available product metadata
125
+ const effectiveSpecs = (0, react_1.useMemo)(() => {
126
+ if (product.specs && product.specs.length > 0) {
127
+ return product.specs;
128
+ }
129
+ const dynamic = [];
130
+ if (product.brand)
131
+ dynamic.push({ label: 'Brand', value: String(product.brand) });
132
+ if (product.category)
133
+ dynamic.push({ label: 'Category', value: String(product.category) });
134
+ if (product.unit && product.unit !== 'size') {
135
+ dynamic.push({ label: 'Measurement Unit', value: String(product.unit).toUpperCase() });
136
+ }
137
+ if (product.measurement) {
138
+ dynamic.push({ label: 'Measurement', value: String(product.measurement) });
139
+ }
140
+ return dynamic;
141
+ }, [product.specs, product.brand, product.category, product.unit, product.measurement]);
142
+ return ((0, jsx_runtime_1.jsx)("section", { "data-preview-page-key": sectionPath, className: `editable-product-detail max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12 ${className}`.trim(), style: style, ...props, children: (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-1 lg:grid-cols-12 gap-12 lg:gap-16 items-start", children: [(0, jsx_runtime_1.jsxs)("div", { className: "lg:col-span-7 flex flex-col-reverse md:flex-row gap-4", children: [validImages.length > 1 && ((0, jsx_runtime_1.jsx)("div", { className: "deneb-product-detail-gallery flex md:flex-col gap-3 overflow-x-auto md:overflow-y-auto max-h-[580px] pb-2 md:pb-0 scrollbar-none", children: validImages.map((img, idx) => ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => setActiveImage(img), className: `w-20 h-20 sm:w-24 sm:h-24 rounded-2xl overflow-hidden border-2 transition-all duration-200 flex-shrink-0 bg-slate-900/40 p-2 ${activeImage === img
143
+ ? 'border-lime-400 scale-98 shadow-md ring-2 ring-lime-400/20'
58
144
  : 'border-slate-800/80 opacity-60 hover:opacity-100 hover:border-slate-700'}`, children: (0, jsx_runtime_1.jsx)("img", { src: (0, utils_1.withBasePath)(img), alt: "", className: "w-full h-full object-contain" }) }, idx))) })), (0, jsx_runtime_1.jsxs)("div", { className: "relative flex-1 aspect-square rounded-3xl bg-gradient-to-b from-slate-900/50 to-slate-950/80 border border-slate-800/80 p-8 flex items-center justify-center overflow-hidden group", children: [badge && ((0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `${sectionPath}.badge`, className: "absolute top-6 left-6 z-10 px-3.5 py-1 text-xs font-black tracking-wider uppercase rounded-full bg-lime-400 text-slate-950 shadow-lg", children: badge })), (0, jsx_runtime_1.jsx)("img", { "data-preview-field-path": `${sectionPath}.featuredImage`, src: (0, utils_1.withBasePath)(activeImage), alt: name, className: "w-full h-full object-contain transition-transform duration-500 group-hover:scale-105" })] })] }), (0, jsx_runtime_1.jsx)("div", { className: "lg:col-span-5 flex flex-col justify-between", children: (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h1", { "data-preview-field-path": `${sectionPath}.name`, className: "text-3xl sm:text-4xl font-extrabold tracking-tight text-white leading-tight", children: name }), (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `${sectionPath}.isAvailable`, className: `mt-3 inline-flex rounded-full px-3 py-1 text-xs font-bold ${isAvailable
59
145
  ? 'bg-emerald-500/15 text-emerald-300'
60
- : 'bg-rose-500/15 text-rose-300'}`, children: isAvailable ? 'In Stock' : 'Out of Stock' }), (0, jsx_runtime_1.jsxs)("div", { className: "mt-4 flex items-baseline gap-3", children: [(0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `${sectionPath}.price`, className: "text-3xl font-black text-lime-400 tracking-tight", children: price }), originalPrice ? ((0, jsx_runtime_1.jsx)("span", { className: "text-lg font-medium text-slate-500 line-through", children: String(originalPrice) })) : null] }), description && ((0, jsx_runtime_1.jsx)("p", { "data-preview-field-path": `${sectionPath}.description`, className: "mt-6 text-base text-slate-300 leading-relaxed", children: description })), colors.length > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "mt-8", children: [(0, jsx_runtime_1.jsxs)("span", { className: "block text-xs font-bold uppercase tracking-wider text-slate-400 mb-3", children: ["Color \u2014 ", (0, jsx_runtime_1.jsx)("span", { className: "text-white", children: selectedColor })] }), (0, jsx_runtime_1.jsx)("div", { className: "flex gap-3", children: colors.map((c) => ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => setSelectedColor(c.name), className: `w-9 h-9 rounded-full border-2 transition-all p-0.5 ${selectedColor === c.name
146
+ : 'bg-rose-500/15 text-rose-300'}`, children: isAvailable ? 'In Stock' : 'Out of Stock' }), (0, jsx_runtime_1.jsxs)("div", { className: "mt-4 flex items-baseline gap-3", children: [(0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `${sectionPath}.price`, className: "text-3xl font-black text-lime-400 tracking-tight", children: price }), originalPrice ? ((0, jsx_runtime_1.jsx)("span", { className: "text-lg font-medium text-slate-500 line-through", children: String(originalPrice) })) : null] }), description && ((0, jsx_runtime_1.jsx)("p", { "data-preview-field-path": `${sectionPath}.description`, className: "mt-6 text-base text-slate-300 leading-relaxed", children: description })), effectiveColors.length > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "mt-8", children: [(0, jsx_runtime_1.jsxs)("span", { className: "block text-xs font-bold uppercase tracking-wider text-slate-400 mb-3", children: ["Color \u2014 ", (0, jsx_runtime_1.jsx)("span", { className: "text-white", children: selectedColor })] }), (0, jsx_runtime_1.jsx)("div", { className: "flex flex-wrap gap-3", children: effectiveColors.map((c) => ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => handleSelectColor(c), className: `w-9 h-9 rounded-full border-2 transition-all p-0.5 relative flex items-center justify-center ${selectedColor === c.name
61
147
  ? 'border-lime-400 ring-2 ring-lime-400/30 scale-105'
62
- : 'border-slate-700 hover:border-slate-500'}`, style: { backgroundColor: c.hex }, title: c.name, "aria-label": c.name }, c.name))) })] })), effectiveOptions.length > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "mt-8", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between mb-3", children: [(0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `${sectionPath}.${product.optionsLabel ? 'optionsLabel' : 'sizesLabel'}`, className: "block text-xs font-bold uppercase tracking-wider text-slate-400", children: effectiveOptionsLabel }), (0, jsx_runtime_1.jsx)("span", { className: "text-xs font-semibold text-slate-300", children: resolved.formatSelectedDisplay(selectedSize) })] }), (0, jsx_runtime_1.jsx)("div", { className: "deneb-product-detail-sizes grid grid-cols-3 sm:grid-cols-6 md:grid-cols-7 gap-2", children: effectiveOptions.map((s) => ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => setSelectedSize(s), className: `py-2.5 px-2 rounded-xl font-bold text-sm transition-all duration-150 ${selectedSize === s
63
- ? 'bg-white text-slate-950 shadow-md font-extrabold scale-102'
64
- : 'bg-slate-900/60 text-slate-300 border border-slate-800 hover:bg-slate-800 hover:text-white'}`, children: resolved.formatOption(s) }, s))) })] })), (0, jsx_runtime_1.jsxs)("div", { className: "mt-8 flex flex-col gap-3", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", disabled: !isAvailable, onClick: () => onAddToSelection?.(product, selectedSize, selectedColor), className: "w-full py-4 px-6 rounded-2xl font-black text-sm uppercase tracking-wider bg-lime-400 text-slate-950 hover:bg-lime-300 active:scale-98 transition-all duration-150 shadow-lg shadow-lime-400/20 flex items-center justify-center gap-2 disabled:cursor-not-allowed disabled:bg-slate-700 disabled:text-slate-300 disabled:shadow-none", children: (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `${sectionPath}.addToSelectionLabel`, children: isAvailable ? addToSelectionLabel : 'Out of Stock' }) }), isAvailable && resolvedWhatsappUrl && ((0, jsx_runtime_1.jsxs)("a", { href: resolvedWhatsappUrl, target: "_blank", rel: "noopener noreferrer", className: "w-full py-3.5 px-6 rounded-2xl font-bold text-sm bg-emerald-600/20 text-emerald-400 hover:bg-emerald-600/30 border border-emerald-500/30 transition-all duration-150 flex items-center justify-center gap-2", children: [(0, jsx_runtime_1.jsx)("svg", { className: "w-5 h-5 fill-current", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { d: "M17.472 14.382c-.301-.15-1.78-.879-2.056-.98-.276-.1-.477-.15-.678.15-.201.3-.78 0.98-.956 1.18-.175.2-.351.226-.652.075-.301-.15-1.272-.469-2.423-1.496-.896-.799-1.501-1.787-1.677-2.088-.175-.301-.019-.464.132-.614.136-.135.301-.351.452-.527.15-.176.201-.301.301-.502.1-.2.05-.376-.025-.527-.075-.15-.678-1.635-.93-2.242-.244-.592-.493-.512-.678-.521-.175-.009-.376-.009-.577-.009-.201 0-.527.075-.803.376-.276.301-1.054 1.03-1.054 2.512 0 1.482 1.079 2.912 1.23 3.113.15.201 2.124 3.243 5.145 4.548.718.311 1.279.497 1.716.636.722.23 1.378.197 1.898.12.579-.087 1.78-.728 2.03-1.431.251-.703.251-1.305.176-1.431-.075-.126-.276-.201-.577-.351zM12.004 2C6.48 2 2 6.48 2 12c0 1.82.49 3.52 1.34 4.99L2 22l5.14-1.35c1.42.78 3.04 1.22 4.86 1.22 5.52 0 10-4.48 10-10S17.524 2 12.004 2z" }) }), (0, jsx_runtime_1.jsx)("span", { children: "Order Directly via WhatsApp" })] }))] }), (0, jsx_runtime_1.jsxs)("div", { className: "mt-10 border-t border-slate-800/80 pt-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex gap-6 border-b border-slate-800 pb-3 mb-4", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => setActiveTab('specs'), className: `text-sm font-bold tracking-wide transition-colors ${activeTab === 'specs' ? 'text-lime-400' : 'text-slate-400 hover:text-slate-200'}`, children: (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `${sectionPath}.specsTitle`, children: specsTitle }) }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => setActiveTab('shipping'), className: `text-sm font-bold tracking-wide transition-colors ${activeTab === 'shipping' ? 'text-lime-400' : 'text-slate-400 hover:text-slate-200'}`, children: (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `${sectionPath}.shippingTitle`, children: shippingTitle }) })] }), activeTab === 'specs' ? ((0, jsx_runtime_1.jsx)("div", { className: "space-y-3", children: product.specs && product.specs.length > 0 ? (product.specs.map((spec, i) => ((0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between text-xs py-1 border-b border-slate-900", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-slate-400 font-medium", children: spec.label }), (0, jsx_runtime_1.jsx)("span", { className: "text-slate-200 font-semibold", children: spec.value })] }, i)))) : ((0, jsx_runtime_1.jsxs)("div", { className: "text-xs text-slate-400 space-y-2", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between py-1 border-b border-slate-800/50", children: [(0, jsx_runtime_1.jsx)("span", { children: "Upper Material" }), (0, jsx_runtime_1.jsx)("span", { className: "text-slate-200 font-semibold", children: "Engineered breathable mesh" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between py-1 border-b border-slate-800/50", children: [(0, jsx_runtime_1.jsx)("span", { children: "Midsole Technology" }), (0, jsx_runtime_1.jsx)("span", { className: "text-slate-200 font-semibold", children: "Dual-density responsive foam" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between py-1 border-b border-slate-800/50", children: [(0, jsx_runtime_1.jsx)("span", { children: "Outsole Grip" }), (0, jsx_runtime_1.jsx)("span", { className: "text-slate-200 font-semibold", children: "High-abrasion tactical rubber" })] })] })) })) : ((0, jsx_runtime_1.jsxs)("div", { className: "text-xs text-slate-300 space-y-3 leading-relaxed", children: [(0, jsx_runtime_1.jsx)("p", { "data-preview-field-path": `${sectionPath}.shippingSummary`, children: shippingSummary }), (0, jsx_runtime_1.jsx)("p", { "data-preview-field-path": `${sectionPath}.shippingReturns`, className: "text-slate-400", children: shippingReturns })] }))] })] }) })] }) }));
148
+ : 'border-slate-700 hover:border-slate-500'}`, style: { backgroundColor: c.hex || '#334155' }, title: c.name, "aria-label": c.name, children: !c.hex && ((0, jsx_runtime_1.jsx)("span", { className: "text-[9px] font-bold text-white uppercase px-1 truncate", children: c.name.slice(0, 3) })) }, c.name))) })] })), effectiveOptions.length > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "mt-8", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between mb-3", children: [(0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `${sectionPath}.${product.optionsLabel ? 'optionsLabel' : 'sizesLabel'}`, className: "block text-xs font-bold uppercase tracking-wider text-slate-400", children: effectiveOptionsLabel }), (0, jsx_runtime_1.jsx)("span", { className: "text-xs font-semibold text-slate-300", children: resolved.formatSelectedDisplay(selectedSize) })] }), (0, jsx_runtime_1.jsx)("div", { className: "deneb-product-detail-sizes flex flex-wrap gap-2.5", children: effectiveOptions.map((s) => ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => setSelectedSize(s), className: `py-2.5 px-3.5 min-w-[48px] rounded-xl font-bold text-sm transition-all duration-150 text-center ${selectedSize === s
149
+ ? 'bg-white text-slate-950 shadow-md font-extrabold scale-102 ring-2 ring-white/20'
150
+ : 'bg-slate-900/60 text-slate-300 border border-slate-800 hover:bg-slate-800 hover:text-white'}`, children: resolved.formatOption(s) }, s))) })] })), (0, jsx_runtime_1.jsxs)("div", { className: "mt-8 flex flex-col gap-3", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", disabled: !isAvailable, onClick: () => onAddToSelection?.(product, selectedSize || undefined, selectedColor || undefined), className: "w-full py-4 px-6 rounded-2xl font-black text-sm uppercase tracking-wider bg-lime-400 text-slate-950 hover:bg-lime-300 active:scale-98 transition-all duration-150 shadow-lg shadow-lime-400/20 flex items-center justify-center gap-2 disabled:cursor-not-allowed disabled:bg-slate-700 disabled:text-slate-300 disabled:shadow-none", children: (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `${sectionPath}.addToSelectionLabel`, children: isAvailable ? addToSelectionLabel : 'Out of Stock' }) }), isAvailable && resolvedWhatsappUrl && ((0, jsx_runtime_1.jsxs)("a", { href: resolvedWhatsappUrl, target: "_blank", rel: "noopener noreferrer", className: "w-full py-3.5 px-6 rounded-2xl font-bold text-sm bg-emerald-600/20 text-emerald-400 hover:bg-emerald-600/30 border border-emerald-500/30 transition-all duration-150 flex items-center justify-center gap-2", children: [(0, jsx_runtime_1.jsx)("svg", { className: "w-5 h-5 fill-current", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { d: "M17.472 14.382c-.301-.15-1.78-.879-2.056-.98-.276-.1-.477-.15-.678.15-.201.3-.78 0.98-.956 1.18-.175.2-.351.226-.652.075-.301-.15-1.272-.469-2.423-1.496-.896-.799-1.501-1.787-1.677-2.088-.175-.301-.019-.464.132-.614.136-.135.301-.351.452-.527.15-.176.201-.301.301-.502.1-.2.05-.376-.025-.527-.075-.15-.678-1.635-.93-2.242-.244-.592-.493-.512-.678-.521-.175-.009-.376-.009-.577-.009-.201 0-.527.075-.803.376-.276.301-1.054 1.03-1.054 2.512 0 1.482 1.079 2.912 1.23 3.113.15.201 2.124 3.243 5.145 4.548.718.311 1.279.497 1.716.636.722.23 1.378.197 1.898.12.579-.087 1.78-.728 2.03-1.431.251-.703.251-1.305.176-1.431-.075-.126-.276-.201-.577-.351zM12.004 2C6.48 2 2 6.48 2 12c0 1.82.49 3.52 1.34 4.99L2 22l5.14-1.35c1.42.78 3.04 1.22 4.86 1.22 5.52 0 10-4.48 10-10S17.524 2 12.004 2z" }) }), (0, jsx_runtime_1.jsx)("span", { children: "Order Directly via WhatsApp" })] }))] }), (0, jsx_runtime_1.jsxs)("div", { className: "mt-10 border-t border-slate-800/80 pt-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex gap-6 border-b border-slate-800 pb-3 mb-4", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => setActiveTab('specs'), className: `text-sm font-bold tracking-wide transition-colors ${activeTab === 'specs' ? 'text-lime-400' : 'text-slate-400 hover:text-slate-200'}`, children: (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `${sectionPath}.specsTitle`, children: specsTitle }) }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => setActiveTab('shipping'), className: `text-sm font-bold tracking-wide transition-colors ${activeTab === 'shipping' ? 'text-lime-400' : 'text-slate-400 hover:text-slate-200'}`, children: (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `${sectionPath}.shippingTitle`, children: shippingTitle }) })] }), activeTab === 'specs' ? ((0, jsx_runtime_1.jsx)("div", { className: "space-y-3", children: effectiveSpecs.length > 0 ? (effectiveSpecs.map((spec, i) => ((0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between text-xs py-1 border-b border-slate-900", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-slate-400 font-medium", children: spec.label }), (0, jsx_runtime_1.jsx)("span", { className: "text-slate-200 font-semibold", children: spec.value })] }, i)))) : ((0, jsx_runtime_1.jsxs)("div", { className: "text-xs text-slate-400 space-y-2", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between py-1 border-b border-slate-800/50", children: [(0, jsx_runtime_1.jsx)("span", { children: "Status" }), (0, jsx_runtime_1.jsx)("span", { className: "text-slate-200 font-semibold", children: isAvailable ? 'Verified Authentic & In Stock' : 'Out of Stock' })] }), product.category && ((0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between py-1 border-b border-slate-800/50", children: [(0, jsx_runtime_1.jsx)("span", { children: "Category" }), (0, jsx_runtime_1.jsx)("span", { className: "text-slate-200 font-semibold", children: String(product.category) })] }))] })) })) : ((0, jsx_runtime_1.jsxs)("div", { className: "text-xs text-slate-300 space-y-3 leading-relaxed", children: [(0, jsx_runtime_1.jsx)("p", { "data-preview-field-path": `${sectionPath}.shippingSummary`, children: shippingSummary }), (0, jsx_runtime_1.jsx)("p", { "data-preview-field-path": `${sectionPath}.shippingReturns`, className: "text-slate-400", children: shippingReturns })] }))] })] }) })] }) }));
65
151
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deneb-ui/ui",
3
- "version": "2.0.76",
3
+ "version": "2.0.78",
4
4
  "description": "Visual-first React component library for editable commerce storefronts. Built for Next.js and Fivora.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -50,7 +50,7 @@
50
50
  ],
51
51
  "license": "MIT",
52
52
  "dependencies": {
53
- "@deneb-ui/core": "^2.0.76"
53
+ "@deneb-ui/core": "^2.0.78"
54
54
  },
55
55
  "peerDependencies": {
56
56
  "react": "^18.0.0 || ^19.0.0",