@deneb-ui/ui 2.0.95 → 2.0.96
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +119 -0
- package/dist/DenebComponentStyles.js +7 -0
- package/dist/EditableCustomerReviews.d.ts +2 -1
- package/dist/EditableCustomerReviews.js +2 -2
- package/dist/EditableFAQAccordion.d.ts +3 -2
- package/dist/EditableFAQAccordion.js +2 -1
- package/dist/EditableGoogleFeedback.js +1 -1
- package/dist/EditableHero.js +2 -2
- package/dist/EditableProductCard.d.ts +15 -71
- package/dist/EditableProductCard.js +176 -27
- package/dist/EditableProductDetail.d.ts +11 -6
- package/dist/EditableProductDetail.js +24 -9
- package/dist/EditableProductGrid.d.ts +4 -1
- package/dist/EditableProductGrid.js +3 -3
- package/dist/EditableProductShowcase.js +1 -1
- package/dist/EditableSection.d.ts +32 -7
- package/dist/EditableSection.js +20 -7
- package/dist/EditableTestimonialSection.js +1 -1
- package/dist/EditableText.d.ts +5 -1
- package/dist/EditableText.js +2 -2
- package/dist/SiteDataProvider.js +16 -5
- package/dist/TrustBadges.js +1 -1
- package/dist/cart/useCart.d.ts +3 -0
- package/dist/cart/useCart.js +20 -9
- package/dist/examples/CompleteStorefrontExample.d.ts +11 -0
- package/dist/examples/CompleteStorefrontExample.js +97 -0
- package/dist/examples/ProductPricingExample.d.ts +9 -0
- package/dist/examples/ProductPricingExample.js +99 -0
- package/dist/examples/SectionLayoutExample.d.ts +7 -0
- package/dist/examples/SectionLayoutExample.js +17 -0
- package/dist/examples/index.d.ts +3 -0
- package/dist/examples/index.js +19 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/utils/productOptions.d.ts +57 -40
- package/dist/utils/productOptions.js +249 -62
- package/package.json +2 -2
|
@@ -1,10 +1,38 @@
|
|
|
1
|
-
export type MeasurementUnit =
|
|
1
|
+
export type MeasurementUnit = "size" | "g" | "kg" | "mg" | "ml" | "l" | "pcs" | "pack" | "oz" | "lb" | (string & {});
|
|
2
|
+
export interface ProductVariant {
|
|
3
|
+
id?: string | number;
|
|
4
|
+
size?: string | number;
|
|
5
|
+
option?: string | number;
|
|
6
|
+
color?: string;
|
|
7
|
+
price?: string | number;
|
|
8
|
+
cost?: string | number;
|
|
9
|
+
amount?: string | number;
|
|
10
|
+
compareAtPrice?: string | number;
|
|
11
|
+
originalPrice?: string | number;
|
|
12
|
+
image?: string;
|
|
13
|
+
imageUrl?: string;
|
|
14
|
+
photo?: string;
|
|
15
|
+
sku?: string;
|
|
16
|
+
inStock?: boolean;
|
|
17
|
+
[key: string]: unknown;
|
|
18
|
+
}
|
|
2
19
|
export interface ProductOptionInput {
|
|
3
20
|
id?: string | number;
|
|
4
21
|
name?: string;
|
|
5
22
|
title?: string;
|
|
6
23
|
unit?: MeasurementUnit;
|
|
7
24
|
measurement?: string;
|
|
25
|
+
price?: string | number;
|
|
26
|
+
cost?: string | number;
|
|
27
|
+
amount?: string | number;
|
|
28
|
+
productPrice?: string | number;
|
|
29
|
+
minPrice?: string | number;
|
|
30
|
+
maxPrice?: string | number;
|
|
31
|
+
priceMin?: string | number;
|
|
32
|
+
priceMax?: string | number;
|
|
33
|
+
priceRange?: [number | string, number | string] | string;
|
|
34
|
+
compareAtPrice?: string | number;
|
|
35
|
+
originalPrice?: string | number;
|
|
8
36
|
optionsLabel?: string;
|
|
9
37
|
options?: (string | number)[];
|
|
10
38
|
optionsText?: string;
|
|
@@ -14,61 +42,50 @@ export interface ProductOptionInput {
|
|
|
14
42
|
colors?: Array<string | {
|
|
15
43
|
name: string;
|
|
16
44
|
hex?: string;
|
|
45
|
+
image?: string;
|
|
46
|
+
photo?: string;
|
|
47
|
+
imageUrl?: string;
|
|
17
48
|
}> | string;
|
|
18
49
|
colorsText?: string;
|
|
19
50
|
colorsLabel?: string;
|
|
51
|
+
variants?: ProductVariant[];
|
|
52
|
+
variantPrices?: Record<string, number | string>;
|
|
20
53
|
[key: string]: unknown;
|
|
21
54
|
}
|
|
55
|
+
export interface ResolvedPriceResult {
|
|
56
|
+
formattedPrice: string;
|
|
57
|
+
numericPrice: number;
|
|
58
|
+
isRange: boolean;
|
|
59
|
+
minPrice: number;
|
|
60
|
+
maxPrice: number;
|
|
61
|
+
formattedOriginalPrice?: string;
|
|
62
|
+
hasVariantPrice: boolean;
|
|
63
|
+
matchedVariant?: ProductVariant;
|
|
64
|
+
variantImage?: string;
|
|
65
|
+
}
|
|
22
66
|
export interface ResolvedProductOptions {
|
|
23
|
-
/**
|
|
24
|
-
* The detected or specified unit, e.g. "g", "ml", "size", "pcs".
|
|
25
|
-
*/
|
|
26
67
|
unit: string;
|
|
27
|
-
/**
|
|
28
|
-
* The user-facing label for the options selector.
|
|
29
|
-
* e.g. "Net Weight", "Volume", "Available Sizes", "Pack Size".
|
|
30
|
-
*/
|
|
31
68
|
optionsLabel: string;
|
|
32
|
-
/**
|
|
33
|
-
* Normalized list of available option values (e.g. ["100g", "250g", "500g"] or ["40", "41", "42"]).
|
|
34
|
-
*/
|
|
35
69
|
options: string[];
|
|
36
|
-
/**
|
|
37
|
-
* Default selected option (first option, or empty string).
|
|
38
|
-
*/
|
|
39
70
|
defaultOption: string;
|
|
40
|
-
/**
|
|
41
|
-
* Whether this option represents a physical measurement (weight, volume, count).
|
|
42
|
-
*/
|
|
43
71
|
isMeasurement: boolean;
|
|
44
|
-
/**
|
|
45
|
-
* Format option for button or badge rendering (e.g. "250g", "50ml", "42").
|
|
46
|
-
*/
|
|
47
72
|
formatOption: (option: string | number) => string;
|
|
48
|
-
/**
|
|
49
|
-
* Format option for headline or summary display (e.g. "250g", "50ml", "Size 42").
|
|
50
|
-
*/
|
|
51
73
|
formatSelectedDisplay: (option: string | number) => string;
|
|
52
|
-
/**
|
|
53
|
-
* Format an order snippet for WhatsApp or checkout (e.g. "(250g)" or "(Ivory, Size 42)").
|
|
54
|
-
*/
|
|
55
74
|
formatOrderSnippet: (selectedOption?: string | number, selectedColor?: string) => string;
|
|
56
|
-
/**
|
|
57
|
-
* Available color names.
|
|
58
|
-
*/
|
|
59
75
|
colors: string[];
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
76
|
+
detailedColors: Array<{
|
|
77
|
+
name: string;
|
|
78
|
+
hex?: string;
|
|
79
|
+
image?: string;
|
|
80
|
+
}>;
|
|
63
81
|
colorsLabel: string;
|
|
64
|
-
/**
|
|
65
|
-
* Default selected color.
|
|
66
|
-
*/
|
|
67
82
|
defaultColor: string;
|
|
68
83
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
84
|
+
export declare function parsePriceRange(rawPrice: unknown, minOverride?: unknown, maxOverride?: unknown, currency?: string): {
|
|
85
|
+
isRange: boolean;
|
|
86
|
+
min: number;
|
|
87
|
+
max: number;
|
|
88
|
+
formatted: string;
|
|
89
|
+
};
|
|
90
|
+
export declare function resolveVariantPrice(product?: ProductOptionInput | Record<string, unknown> | null, selectedOption?: string | number, selectedColor?: string, currency?: string): ResolvedPriceResult;
|
|
74
91
|
export declare function resolveProductOptions(product?: ProductOptionInput | Record<string, unknown> | null): ResolvedProductOptions;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parsePriceRange = parsePriceRange;
|
|
4
|
+
exports.resolveVariantPrice = resolveVariantPrice;
|
|
3
5
|
exports.resolveProductOptions = resolveProductOptions;
|
|
6
|
+
const useCart_1 = require("../cart/useCart");
|
|
4
7
|
function parseList(value) {
|
|
5
|
-
if (typeof value ===
|
|
8
|
+
if (typeof value === "string") {
|
|
6
9
|
return value
|
|
7
|
-
.split(
|
|
10
|
+
.split(",")
|
|
8
11
|
.map((s) => s.trim())
|
|
9
12
|
.filter(Boolean);
|
|
10
13
|
}
|
|
@@ -12,8 +15,8 @@ function parseList(value) {
|
|
|
12
15
|
return value
|
|
13
16
|
.map((item) => {
|
|
14
17
|
if (item == null)
|
|
15
|
-
return
|
|
16
|
-
if (typeof item ===
|
|
18
|
+
return "";
|
|
19
|
+
if (typeof item === "object" && "name" in item && typeof item.name === "string") {
|
|
17
20
|
return item.name.trim();
|
|
18
21
|
}
|
|
19
22
|
return String(item).trim();
|
|
@@ -22,20 +25,209 @@ function parseList(value) {
|
|
|
22
25
|
}
|
|
23
26
|
return [];
|
|
24
27
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
function parsePriceRange(rawPrice, minOverride, maxOverride, currency = "LKR") {
|
|
29
|
+
let min = (0, useCart_1.parseNumericPrice)(minOverride);
|
|
30
|
+
let max = (0, useCart_1.parseNumericPrice)(maxOverride);
|
|
31
|
+
if (typeof rawPrice === "string") {
|
|
32
|
+
const trimmed = rawPrice.trim();
|
|
33
|
+
if (trimmed.includes("-") || trimmed.includes("–") || trimmed.includes("—") || /\bto\b/i.test(trimmed)) {
|
|
34
|
+
const parts = trimmed
|
|
35
|
+
.split(/[-–—]|\bto\b/i)
|
|
36
|
+
.map((s) => (0, useCart_1.parseNumericPrice)(s))
|
|
37
|
+
.filter((n) => n > 0);
|
|
38
|
+
if (parts.length >= 2) {
|
|
39
|
+
min = Math.min(parts[0], parts[1]);
|
|
40
|
+
max = Math.max(parts[0], parts[1]);
|
|
41
|
+
}
|
|
42
|
+
else if (parts.length === 1 && !min) {
|
|
43
|
+
min = parts[0];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else if (Array.isArray(rawPrice) && rawPrice.length >= 2) {
|
|
48
|
+
min = (0, useCart_1.parseNumericPrice)(rawPrice[0]);
|
|
49
|
+
max = (0, useCart_1.parseNumericPrice)(rawPrice[1]);
|
|
50
|
+
}
|
|
51
|
+
if (min > 0 && max > 0 && min !== max) {
|
|
52
|
+
return {
|
|
53
|
+
isRange: true,
|
|
54
|
+
min,
|
|
55
|
+
max,
|
|
56
|
+
formatted: `${(0, useCart_1.formatCurrency)(min, currency)} – ${(0, useCart_1.formatCurrency)(max, currency)}`,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const single = min || max || (0, useCart_1.parseNumericPrice)(rawPrice);
|
|
60
|
+
return {
|
|
61
|
+
isRange: false,
|
|
62
|
+
min: single,
|
|
63
|
+
max: single,
|
|
64
|
+
formatted: single > 0 ? (0, useCart_1.formatCurrency)(single, currency) : (rawPrice ? String(rawPrice) : ""),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function resolveVariantPrice(product, selectedOption, selectedColor, currency = "LKR") {
|
|
68
|
+
const p = (product || {});
|
|
69
|
+
const baseRawPrice = p.price ?? p.cost ?? p.amount ?? p.productPrice;
|
|
70
|
+
const baseOriginalPrice = p.originalPrice ?? p.compareAtPrice;
|
|
71
|
+
const variants = Array.isArray(p.variants) ? p.variants : [];
|
|
72
|
+
const variantPrices = p.variantPrices && typeof p.variantPrices === "object" && !Array.isArray(p.variantPrices)
|
|
73
|
+
? p.variantPrices
|
|
74
|
+
: null;
|
|
75
|
+
const normOption = selectedOption ? String(selectedOption).trim().toLowerCase() : "";
|
|
76
|
+
const normColor = selectedColor && selectedColor !== "Standard"
|
|
77
|
+
? String(selectedColor).trim().toLowerCase()
|
|
78
|
+
: "";
|
|
79
|
+
let matchedVariant;
|
|
80
|
+
if (variants.length > 0 && (normOption || normColor)) {
|
|
81
|
+
if (normOption && normColor) {
|
|
82
|
+
matchedVariant = variants.find((v) => {
|
|
83
|
+
const vOpt = String(v.size || v.option || "").trim().toLowerCase();
|
|
84
|
+
const vCol = String(v.color || "").trim().toLowerCase();
|
|
85
|
+
return vOpt === normOption && vCol === normColor;
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
if (!matchedVariant && normOption) {
|
|
89
|
+
matchedVariant = variants.find((v) => {
|
|
90
|
+
const vOpt = String(v.size || v.option || "").trim().toLowerCase();
|
|
91
|
+
return vOpt === normOption;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
if (!matchedVariant && normColor) {
|
|
95
|
+
matchedVariant = variants.find((v) => {
|
|
96
|
+
const vCol = String(v.color || "").trim().toLowerCase();
|
|
97
|
+
return vCol === normColor;
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
let variantImage;
|
|
102
|
+
if (matchedVariant) {
|
|
103
|
+
variantImage =
|
|
104
|
+
(typeof matchedVariant.image === "string" && matchedVariant.image) ||
|
|
105
|
+
(typeof matchedVariant.imageUrl === "string" && matchedVariant.imageUrl) ||
|
|
106
|
+
(typeof matchedVariant.photo === "string" && matchedVariant.photo) ||
|
|
107
|
+
undefined;
|
|
108
|
+
}
|
|
109
|
+
if (!variantImage && normColor && Array.isArray(p.colors)) {
|
|
110
|
+
const colorObj = p.colors.find((c) => {
|
|
111
|
+
if (typeof c === "object" && c && "name" in c) {
|
|
112
|
+
return String(c.name).trim().toLowerCase() === normColor;
|
|
113
|
+
}
|
|
114
|
+
return false;
|
|
115
|
+
});
|
|
116
|
+
if (colorObj) {
|
|
117
|
+
variantImage = colorObj.image || colorObj.imageUrl || colorObj.photo;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
let variantPriceNum = null;
|
|
121
|
+
let variantOriginalPriceNum = null;
|
|
122
|
+
if (matchedVariant) {
|
|
123
|
+
const rawVPrice = matchedVariant.price ?? matchedVariant.cost ?? matchedVariant.amount;
|
|
124
|
+
if (rawVPrice !== undefined && rawVPrice !== null && String(rawVPrice).trim() !== "") {
|
|
125
|
+
variantPriceNum = (0, useCart_1.parseNumericPrice)(rawVPrice);
|
|
126
|
+
}
|
|
127
|
+
const rawVOriginal = matchedVariant.compareAtPrice ?? matchedVariant.originalPrice;
|
|
128
|
+
if (rawVOriginal !== undefined && rawVOriginal !== null && String(rawVOriginal).trim() !== "") {
|
|
129
|
+
variantOriginalPriceNum = (0, useCart_1.parseNumericPrice)(rawVOriginal);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (variantPriceNum === null && variantPrices && (normOption || normColor)) {
|
|
133
|
+
const keysToTry = [
|
|
134
|
+
`${selectedOption}-${selectedColor}`,
|
|
135
|
+
`${selectedColor}-${selectedOption}`,
|
|
136
|
+
String(selectedOption || ""),
|
|
137
|
+
String(selectedColor || ""),
|
|
138
|
+
];
|
|
139
|
+
for (const key of keysToTry) {
|
|
140
|
+
if (key && key in variantPrices) {
|
|
141
|
+
const val = variantPrices[key];
|
|
142
|
+
const parsed = (0, useCart_1.parseNumericPrice)(val);
|
|
143
|
+
if (parsed > 0) {
|
|
144
|
+
variantPriceNum = parsed;
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (variantPriceNum !== null && variantPriceNum > 0) {
|
|
151
|
+
return {
|
|
152
|
+
formattedPrice: (0, useCart_1.formatCurrency)(variantPriceNum, currency),
|
|
153
|
+
numericPrice: variantPriceNum,
|
|
154
|
+
isRange: false,
|
|
155
|
+
minPrice: variantPriceNum,
|
|
156
|
+
maxPrice: variantPriceNum,
|
|
157
|
+
formattedOriginalPrice: variantOriginalPriceNum && variantOriginalPriceNum > variantPriceNum
|
|
158
|
+
? (0, useCart_1.formatCurrency)(variantOriginalPriceNum, currency)
|
|
159
|
+
: (baseOriginalPrice ? (typeof baseOriginalPrice === "number" ? (0, useCart_1.formatCurrency)(baseOriginalPrice, currency) : String(baseOriginalPrice)) : undefined),
|
|
160
|
+
hasVariantPrice: true,
|
|
161
|
+
matchedVariant,
|
|
162
|
+
variantImage,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
const allVariantPrices = [];
|
|
166
|
+
if (variants.length > 0) {
|
|
167
|
+
variants.forEach((v) => {
|
|
168
|
+
const pNum = (0, useCart_1.parseNumericPrice)(v.price ?? v.cost ?? v.amount);
|
|
169
|
+
if (pNum > 0)
|
|
170
|
+
allVariantPrices.push(pNum);
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
else if (variantPrices) {
|
|
174
|
+
Object.values(variantPrices).forEach((val) => {
|
|
175
|
+
const pNum = (0, useCart_1.parseNumericPrice)(val);
|
|
176
|
+
if (pNum > 0)
|
|
177
|
+
allVariantPrices.push(pNum);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
const explicitMin = p.minPrice ?? p.priceMin ?? (Array.isArray(p.priceRange) ? p.priceRange[0] : undefined);
|
|
181
|
+
const explicitMax = p.maxPrice ?? p.priceMax ?? (Array.isArray(p.priceRange) ? p.priceRange[1] : undefined);
|
|
182
|
+
let rangeMin = (0, useCart_1.parseNumericPrice)(explicitMin);
|
|
183
|
+
let rangeMax = (0, useCart_1.parseNumericPrice)(explicitMax);
|
|
184
|
+
if (allVariantPrices.length > 1 && (!rangeMin || !rangeMax)) {
|
|
185
|
+
rangeMin = Math.min(...allVariantPrices);
|
|
186
|
+
rangeMax = Math.max(...allVariantPrices);
|
|
187
|
+
}
|
|
188
|
+
const rangeCheck = parsePriceRange(baseRawPrice, rangeMin || undefined, rangeMax || undefined, currency);
|
|
189
|
+
const formattedOriginalPrice = baseOriginalPrice
|
|
190
|
+
? (typeof baseOriginalPrice === "number"
|
|
191
|
+
? (0, useCart_1.formatCurrency)(baseOriginalPrice, currency)
|
|
192
|
+
: String(baseOriginalPrice))
|
|
193
|
+
: undefined;
|
|
194
|
+
return {
|
|
195
|
+
formattedPrice: rangeCheck.formatted || (baseRawPrice ? String(baseRawPrice) : "LKR 0"),
|
|
196
|
+
numericPrice: rangeCheck.min || (0, useCart_1.parseNumericPrice)(baseRawPrice),
|
|
197
|
+
isRange: rangeCheck.isRange,
|
|
198
|
+
minPrice: rangeCheck.min,
|
|
199
|
+
maxPrice: rangeCheck.max,
|
|
200
|
+
formattedOriginalPrice,
|
|
201
|
+
hasVariantPrice: false,
|
|
202
|
+
matchedVariant,
|
|
203
|
+
variantImage,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
30
206
|
function resolveProductOptions(product) {
|
|
31
207
|
const p = (product || {});
|
|
32
|
-
// 1. Resolve Colors
|
|
33
208
|
let colors = parseList(p.colorsText);
|
|
34
209
|
if (!colors.length)
|
|
35
210
|
colors = parseList(p.colors);
|
|
36
|
-
const colorsLabel = (typeof p.colorsLabel ===
|
|
37
|
-
const defaultColor = colors[0] ||
|
|
38
|
-
|
|
211
|
+
const colorsLabel = (typeof p.colorsLabel === "string" && p.colorsLabel.trim()) || "Available Colors";
|
|
212
|
+
const defaultColor = colors[0] || "Standard";
|
|
213
|
+
const detailedColors = [];
|
|
214
|
+
if (Array.isArray(p.colors)) {
|
|
215
|
+
p.colors.forEach((c) => {
|
|
216
|
+
if (typeof c === "string" && c.trim()) {
|
|
217
|
+
detailedColors.push({ name: c.trim() });
|
|
218
|
+
}
|
|
219
|
+
else if (c && typeof c === "object" && "name" in c) {
|
|
220
|
+
detailedColors.push({
|
|
221
|
+
name: String(c.name).trim(),
|
|
222
|
+
hex: typeof c.hex === "string" ? c.hex : undefined,
|
|
223
|
+
image: typeof c.image === "string" ? c.image : c.imageUrl || c.photo,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
else if (colors.length > 0) {
|
|
229
|
+
colors.forEach((name) => detailedColors.push({ name }));
|
|
230
|
+
}
|
|
39
231
|
let options = parseList(p.optionsText);
|
|
40
232
|
if (!options.length)
|
|
41
233
|
options = parseList(p.options);
|
|
@@ -43,91 +235,85 @@ function resolveProductOptions(product) {
|
|
|
43
235
|
options = parseList(p.sizesText);
|
|
44
236
|
if (!options.length)
|
|
45
237
|
options = parseList(p.sizes);
|
|
46
|
-
if (!options.length && typeof p.measurement ===
|
|
238
|
+
if (!options.length && typeof p.measurement === "string" && p.measurement.trim()) {
|
|
47
239
|
options = [p.measurement.trim()];
|
|
48
240
|
}
|
|
49
|
-
|
|
50
|
-
let unit = (typeof p.unit === 'string' ? p.unit.trim().toLowerCase() : '');
|
|
241
|
+
let unit = (typeof p.unit === "string" ? p.unit.trim().toLowerCase() : "");
|
|
51
242
|
if (!unit) {
|
|
52
|
-
// Auto-detect unit from options, measurement, or title
|
|
53
243
|
const probe = [
|
|
54
244
|
...options,
|
|
55
|
-
typeof p.measurement ===
|
|
56
|
-
typeof p.title ===
|
|
57
|
-
typeof p.name ===
|
|
58
|
-
].join(
|
|
245
|
+
typeof p.measurement === "string" ? p.measurement : "",
|
|
246
|
+
typeof p.title === "string" ? p.title : "",
|
|
247
|
+
typeof p.name === "string" ? p.name : "",
|
|
248
|
+
].join(" ");
|
|
59
249
|
if (/\b(\d+)\s*(mg)\b/i.test(probe)) {
|
|
60
|
-
unit =
|
|
250
|
+
unit = "mg";
|
|
61
251
|
}
|
|
62
252
|
else if (/\b(\d+)\s*(kg)\b/i.test(probe)) {
|
|
63
|
-
unit =
|
|
253
|
+
unit = "kg";
|
|
64
254
|
}
|
|
65
255
|
else if (/\b(\d+)\s*(g|grams?)\b/i.test(probe)) {
|
|
66
|
-
unit =
|
|
256
|
+
unit = "g";
|
|
67
257
|
}
|
|
68
258
|
else if (/\b(\d+)\s*(ml)\b/i.test(probe)) {
|
|
69
|
-
unit =
|
|
259
|
+
unit = "ml";
|
|
70
260
|
}
|
|
71
261
|
else if (/\b(\d+)\s*(l|litres?|liters?)\b/i.test(probe)) {
|
|
72
|
-
unit =
|
|
262
|
+
unit = "l";
|
|
73
263
|
}
|
|
74
264
|
else if (/\b(\d+)\s*(pcs|pieces?|pack|packs?|boxes?)\b/i.test(probe)) {
|
|
75
|
-
unit =
|
|
265
|
+
unit = "pcs";
|
|
76
266
|
}
|
|
77
267
|
else if (/\b(\d+)\s*(oz|fl\s*oz)\b/i.test(probe)) {
|
|
78
|
-
unit =
|
|
268
|
+
unit = "oz";
|
|
79
269
|
}
|
|
80
270
|
else if (/\b(\d+)\s*(lb|lbs)\b/i.test(probe)) {
|
|
81
|
-
unit =
|
|
271
|
+
unit = "lb";
|
|
82
272
|
}
|
|
83
273
|
else {
|
|
84
|
-
unit =
|
|
274
|
+
unit = "size";
|
|
85
275
|
}
|
|
86
276
|
}
|
|
87
|
-
const isMeasurement = unit !==
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (!optionsLabel && typeof p.sizesLabel === 'string' && p.sizesLabel.trim()) {
|
|
277
|
+
const isMeasurement = unit !== "size";
|
|
278
|
+
let optionsLabel = (typeof p.optionsLabel === "string" && p.optionsLabel.trim()) || "";
|
|
279
|
+
if (!optionsLabel && typeof p.sizesLabel === "string" && p.sizesLabel.trim()) {
|
|
91
280
|
optionsLabel = p.sizesLabel.trim();
|
|
92
281
|
}
|
|
93
282
|
if (!optionsLabel) {
|
|
94
283
|
switch (unit) {
|
|
95
|
-
case
|
|
96
|
-
case
|
|
97
|
-
case
|
|
98
|
-
case
|
|
99
|
-
case
|
|
100
|
-
optionsLabel =
|
|
284
|
+
case "g":
|
|
285
|
+
case "kg":
|
|
286
|
+
case "mg":
|
|
287
|
+
case "oz":
|
|
288
|
+
case "lb":
|
|
289
|
+
optionsLabel = "Net Weight";
|
|
101
290
|
break;
|
|
102
|
-
case
|
|
103
|
-
case
|
|
104
|
-
optionsLabel =
|
|
291
|
+
case "ml":
|
|
292
|
+
case "l":
|
|
293
|
+
optionsLabel = "Volume";
|
|
105
294
|
break;
|
|
106
|
-
case
|
|
107
|
-
case
|
|
108
|
-
optionsLabel =
|
|
295
|
+
case "pcs":
|
|
296
|
+
case "pack":
|
|
297
|
+
optionsLabel = "Pack Size";
|
|
109
298
|
break;
|
|
110
|
-
case
|
|
111
|
-
optionsLabel =
|
|
299
|
+
case "size":
|
|
300
|
+
optionsLabel = "Available Sizes";
|
|
112
301
|
break;
|
|
113
302
|
default:
|
|
114
|
-
optionsLabel =
|
|
303
|
+
optionsLabel = "Available Options";
|
|
115
304
|
break;
|
|
116
305
|
}
|
|
117
306
|
}
|
|
118
|
-
const defaultOption = options[0] ||
|
|
119
|
-
// 5. Formatter helpers
|
|
307
|
+
const defaultOption = options[0] || "";
|
|
120
308
|
const formatOption = (option) => {
|
|
121
|
-
const raw = String(option ??
|
|
309
|
+
const raw = String(option ?? "").trim();
|
|
122
310
|
if (!raw)
|
|
123
|
-
return
|
|
124
|
-
if (unit ===
|
|
311
|
+
return "";
|
|
312
|
+
if (unit === "size")
|
|
125
313
|
return raw;
|
|
126
|
-
|
|
127
|
-
const regex = new RegExp(`\\b(${unit})$`, 'i');
|
|
314
|
+
const regex = new RegExp(`\\b(${unit})$`, "i");
|
|
128
315
|
if (regex.test(raw))
|
|
129
316
|
return raw;
|
|
130
|
-
// If pure number and measurement unit
|
|
131
317
|
if (/^\d+(\.\d+)?$/.test(raw)) {
|
|
132
318
|
return `${raw}${unit}`;
|
|
133
319
|
}
|
|
@@ -136,8 +322,8 @@ function resolveProductOptions(product) {
|
|
|
136
322
|
const formatSelectedDisplay = (option) => {
|
|
137
323
|
const formatted = formatOption(option);
|
|
138
324
|
if (!formatted)
|
|
139
|
-
return
|
|
140
|
-
if (unit ===
|
|
325
|
+
return "";
|
|
326
|
+
if (unit === "size") {
|
|
141
327
|
if (/^size\s+/i.test(formatted))
|
|
142
328
|
return formatted;
|
|
143
329
|
return `Size ${formatted}`;
|
|
@@ -145,8 +331,8 @@ function resolveProductOptions(product) {
|
|
|
145
331
|
return formatted;
|
|
146
332
|
};
|
|
147
333
|
const formatOrderSnippet = (selectedOption, selectedColor) => {
|
|
148
|
-
const cleanColor = selectedColor && selectedColor !==
|
|
149
|
-
const cleanOption = selectedOption ? formatSelectedDisplay(selectedOption) :
|
|
334
|
+
const cleanColor = selectedColor && selectedColor !== "Standard" ? String(selectedColor).trim() : "";
|
|
335
|
+
const cleanOption = selectedOption ? formatSelectedDisplay(selectedOption) : "";
|
|
150
336
|
if (cleanColor && cleanOption) {
|
|
151
337
|
return `(${cleanColor}, ${cleanOption})`;
|
|
152
338
|
}
|
|
@@ -156,7 +342,7 @@ function resolveProductOptions(product) {
|
|
|
156
342
|
if (cleanColor) {
|
|
157
343
|
return `(${cleanColor})`;
|
|
158
344
|
}
|
|
159
|
-
return
|
|
345
|
+
return "";
|
|
160
346
|
};
|
|
161
347
|
return {
|
|
162
348
|
unit,
|
|
@@ -168,6 +354,7 @@ function resolveProductOptions(product) {
|
|
|
168
354
|
formatSelectedDisplay,
|
|
169
355
|
formatOrderSnippet,
|
|
170
356
|
colors,
|
|
357
|
+
detailedColors,
|
|
171
358
|
colorsLabel,
|
|
172
359
|
defaultColor,
|
|
173
360
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deneb-ui/ui",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.96",
|
|
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.
|
|
53
|
+
"@deneb-ui/core": "^2.0.96"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"react": "^18.0.0 || ^19.0.0",
|