@deneb-ui/ui 2.0.95 → 2.0.97

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.
Files changed (37) hide show
  1. package/README.md +119 -0
  2. package/dist/DenebComponentStyles.js +7 -0
  3. package/dist/EditableCustomerReviews.d.ts +2 -1
  4. package/dist/EditableCustomerReviews.js +2 -2
  5. package/dist/EditableFAQAccordion.d.ts +3 -2
  6. package/dist/EditableFAQAccordion.js +2 -1
  7. package/dist/EditableGoogleFeedback.js +1 -1
  8. package/dist/EditableHero.js +2 -2
  9. package/dist/EditableProductCard.d.ts +16 -71
  10. package/dist/EditableProductCard.js +190 -27
  11. package/dist/EditableProductDetail.d.ts +11 -6
  12. package/dist/EditableProductDetail.js +24 -9
  13. package/dist/EditableProductGrid.d.ts +4 -1
  14. package/dist/EditableProductGrid.js +3 -3
  15. package/dist/EditableProductShowcase.js +1 -1
  16. package/dist/EditableSection.d.ts +39 -8
  17. package/dist/EditableSection.js +24 -8
  18. package/dist/EditableTestimonialSection.js +1 -1
  19. package/dist/EditableText.d.ts +5 -1
  20. package/dist/EditableText.js +2 -2
  21. package/dist/SiteDataProvider.js +16 -5
  22. package/dist/TrustBadges.js +1 -1
  23. package/dist/cart/useCart.d.ts +3 -0
  24. package/dist/cart/useCart.js +20 -9
  25. package/dist/examples/CompleteStorefrontExample.d.ts +11 -0
  26. package/dist/examples/CompleteStorefrontExample.js +97 -0
  27. package/dist/examples/ProductPricingExample.d.ts +9 -0
  28. package/dist/examples/ProductPricingExample.js +99 -0
  29. package/dist/examples/SectionLayoutExample.d.ts +7 -0
  30. package/dist/examples/SectionLayoutExample.js +17 -0
  31. package/dist/examples/index.d.ts +3 -0
  32. package/dist/examples/index.js +19 -0
  33. package/dist/index.d.ts +1 -0
  34. package/dist/index.js +2 -0
  35. package/dist/utils/productOptions.d.ts +57 -40
  36. package/dist/utils/productOptions.js +249 -62
  37. package/package.json +2 -2
@@ -1,10 +1,38 @@
1
- export type MeasurementUnit = 'size' | 'g' | 'kg' | 'mg' | 'ml' | 'l' | 'pcs' | 'pack' | 'oz' | 'lb' | (string & {});
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
- * Color selector label (e.g. "Available Colors").
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
- * Universal product option & measurement resolver.
71
- * Handles footwear/apparel sizes, food/grocery weights (g, kg, mg),
72
- * liquids/cosmetics volumes (ml, l), and counts (pcs, pack) seamlessly.
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 === 'string') {
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 === 'object' && 'name' in item && typeof item.name === 'string') {
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
- * Universal product option & measurement resolver.
27
- * Handles footwear/apparel sizes, food/grocery weights (g, kg, mg),
28
- * liquids/cosmetics volumes (ml, l), and counts (pcs, pack) seamlessly.
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 === 'string' && p.colorsLabel.trim()) || 'Available Colors';
37
- const defaultColor = colors[0] || 'Standard';
38
- // 2. Resolve Options / Sizes
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 === 'string' && p.measurement.trim()) {
238
+ if (!options.length && typeof p.measurement === "string" && p.measurement.trim()) {
47
239
  options = [p.measurement.trim()];
48
240
  }
49
- // 3. Detect Unit
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 === 'string' ? p.measurement : '',
56
- typeof p.title === 'string' ? p.title : '',
57
- typeof p.name === 'string' ? 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 = 'mg';
250
+ unit = "mg";
61
251
  }
62
252
  else if (/\b(\d+)\s*(kg)\b/i.test(probe)) {
63
- unit = 'kg';
253
+ unit = "kg";
64
254
  }
65
255
  else if (/\b(\d+)\s*(g|grams?)\b/i.test(probe)) {
66
- unit = 'g';
256
+ unit = "g";
67
257
  }
68
258
  else if (/\b(\d+)\s*(ml)\b/i.test(probe)) {
69
- unit = 'ml';
259
+ unit = "ml";
70
260
  }
71
261
  else if (/\b(\d+)\s*(l|litres?|liters?)\b/i.test(probe)) {
72
- unit = 'l';
262
+ unit = "l";
73
263
  }
74
264
  else if (/\b(\d+)\s*(pcs|pieces?|pack|packs?|boxes?)\b/i.test(probe)) {
75
- unit = 'pcs';
265
+ unit = "pcs";
76
266
  }
77
267
  else if (/\b(\d+)\s*(oz|fl\s*oz)\b/i.test(probe)) {
78
- unit = 'oz';
268
+ unit = "oz";
79
269
  }
80
270
  else if (/\b(\d+)\s*(lb|lbs)\b/i.test(probe)) {
81
- unit = 'lb';
271
+ unit = "lb";
82
272
  }
83
273
  else {
84
- unit = 'size';
274
+ unit = "size";
85
275
  }
86
276
  }
87
- const isMeasurement = unit !== 'size';
88
- // 4. Resolve Label
89
- let optionsLabel = (typeof p.optionsLabel === 'string' && p.optionsLabel.trim()) || '';
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 'g':
96
- case 'kg':
97
- case 'mg':
98
- case 'oz':
99
- case 'lb':
100
- optionsLabel = 'Net Weight';
284
+ case "g":
285
+ case "kg":
286
+ case "mg":
287
+ case "oz":
288
+ case "lb":
289
+ optionsLabel = "Net Weight";
101
290
  break;
102
- case 'ml':
103
- case 'l':
104
- optionsLabel = 'Volume';
291
+ case "ml":
292
+ case "l":
293
+ optionsLabel = "Volume";
105
294
  break;
106
- case 'pcs':
107
- case 'pack':
108
- optionsLabel = 'Pack Size';
295
+ case "pcs":
296
+ case "pack":
297
+ optionsLabel = "Pack Size";
109
298
  break;
110
- case 'size':
111
- optionsLabel = 'Available Sizes';
299
+ case "size":
300
+ optionsLabel = "Available Sizes";
112
301
  break;
113
302
  default:
114
- optionsLabel = 'Available Options';
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 ?? '').trim();
309
+ const raw = String(option ?? "").trim();
122
310
  if (!raw)
123
- return '';
124
- if (unit === 'size')
311
+ return "";
312
+ if (unit === "size")
125
313
  return raw;
126
- // Check if raw already ends with unit
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 === 'size') {
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 !== 'Standard' ? String(selectedColor).trim() : '';
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.95",
3
+ "version": "2.0.97",
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.95"
53
+ "@deneb-ui/core": "^2.0.97"
54
54
  },
55
55
  "peerDependencies": {
56
56
  "react": "^18.0.0 || ^19.0.0",