funuicss 3.8.6 → 3.8.8
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/css/fun.css +393 -89
- package/index.d.ts +1 -0
- package/index.js +3 -1
- package/package.json +1 -1
- package/ui/accordion/Accordion.js +3 -1
- package/ui/button/Button.d.ts +2 -1
- package/ui/button/Button.js +8 -7
- package/ui/div/Div.d.ts +3 -1
- package/ui/div/Div.js +2 -2
- package/ui/form/Form.d.ts +68 -0
- package/ui/form/Form.js +658 -0
- package/ui/input/FileUpload.js +370 -21
- package/ui/input/Input.d.ts +35 -1
- package/ui/input/Input.js +1041 -41
- package/ui/products/CartModal.d.ts +0 -2
- package/ui/products/CartModal.js +59 -39
- package/ui/products/ProductCard.js +9 -22
- package/ui/products/ProductDetail.js +111 -80
- package/ui/products/ProductLoader.d.ts +3 -0
- package/ui/products/ProductLoader.js +22 -0
- package/ui/products/Store.d.ts +32 -7
- package/ui/products/Store.js +393 -94
- package/ui/progress/Bar.js +2 -2
- package/ui/theme/theme.d.ts +4 -0
- package/ui/theme/theme.js +336 -133
package/ui/products/CartModal.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// components/products/CartModal.tsx
|
|
3
2
|
'use client';
|
|
4
3
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
5
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
@@ -15,71 +14,92 @@ var Div_1 = __importDefault(require("../div/Div"));
|
|
|
15
14
|
var Button_1 = __importDefault(require("../button/Button"));
|
|
16
15
|
var Circle_1 = __importDefault(require("../specials/Circle"));
|
|
17
16
|
var Flex_1 = __importDefault(require("../flex/Flex"));
|
|
18
|
-
var
|
|
17
|
+
var ImageScaler_1 = __importDefault(require("../components/ImageScaler"));
|
|
18
|
+
var tb_1 = require("react-icons/tb");
|
|
19
|
+
var Empty_1 = __importDefault(require("../empty/Empty"));
|
|
20
|
+
var generateCartItemId = function (item) {
|
|
21
|
+
var _a;
|
|
19
22
|
var parts = [
|
|
20
|
-
product.id,
|
|
21
|
-
(
|
|
22
|
-
|
|
23
|
+
item.product.id,
|
|
24
|
+
((_a = item.variant) === null || _a === void 0 ? void 0 : _a.id) || '',
|
|
25
|
+
item.selectedColor || '',
|
|
26
|
+
item.selectedSize || ''
|
|
23
27
|
].filter(Boolean);
|
|
24
28
|
return parts.join('_');
|
|
25
29
|
};
|
|
26
30
|
var CartModal = function (_a) {
|
|
27
|
-
var cart = _a.cart, isOpen = _a.isOpen, setIsOpen = _a.setIsOpen, _b = _a.currency, currency = _b === void 0 ? '$' : _b, updateQuantity = _a.updateQuantity, removeFromCart = _a.removeFromCart, clearCart = _a.clearCart, handleCheckout = _a.handleCheckout, _c = _a.cartBadgeColor, cartBadgeColor = _c === void 0 ? 'error' : _c, _d = _a.checkoutText, checkoutText = _d === void 0 ? 'Checkout' : _d, checkoutIcon = _a.checkoutIcon, _e = _a.
|
|
31
|
+
var cart = _a.cart, isOpen = _a.isOpen, setIsOpen = _a.setIsOpen, _b = _a.currency, currency = _b === void 0 ? '$' : _b, updateQuantity = _a.updateQuantity, removeFromCart = _a.removeFromCart, clearCart = _a.clearCart, handleCheckout = _a.handleCheckout, _c = _a.cartBadgeColor, cartBadgeColor = _c === void 0 ? 'error' : _c, _d = _a.checkoutText, checkoutText = _d === void 0 ? 'Checkout' : _d, checkoutIcon = _a.checkoutIcon, _e = _a.persistCart, persistCart = _e === void 0 ? true : _e;
|
|
28
32
|
var totalItems = cart.reduce(function (sum, item) { return sum + item.quantity; }, 0);
|
|
29
|
-
var subtotal = cart.reduce(function (sum, item) {
|
|
33
|
+
var subtotal = cart.reduce(function (sum, item) {
|
|
34
|
+
var _a;
|
|
35
|
+
var price = ((_a = item.variant) === null || _a === void 0 ? void 0 : _a.price) || item.product.price;
|
|
36
|
+
return sum + (price * item.quantity);
|
|
37
|
+
}, 0);
|
|
38
|
+
// Calculate total savings from discounts
|
|
39
|
+
var totalSavings = cart.reduce(function (sum, item) {
|
|
40
|
+
var _a;
|
|
41
|
+
var originalPrice = item.originalPrice || item.product.comparePrice || item.product.price;
|
|
42
|
+
var currentPrice = ((_a = item.variant) === null || _a === void 0 ? void 0 : _a.price) || item.product.price;
|
|
43
|
+
var savings = originalPrice !== currentPrice ? (originalPrice - currentPrice) * item.quantity : 0;
|
|
44
|
+
return sum + savings;
|
|
45
|
+
}, 0);
|
|
30
46
|
var handleRemoveItem = function (item) {
|
|
31
|
-
var cartItemId = generateCartItemId(item
|
|
32
|
-
color: item.selectedColor,
|
|
33
|
-
size: item.selectedSize
|
|
34
|
-
});
|
|
47
|
+
var cartItemId = generateCartItemId(item);
|
|
35
48
|
removeFromCart(cartItemId);
|
|
36
49
|
};
|
|
37
50
|
var handleUpdateQuantity = function (item, newQuantity) {
|
|
38
|
-
var cartItemId = generateCartItemId(item
|
|
39
|
-
color: item.selectedColor,
|
|
40
|
-
size: item.selectedSize
|
|
41
|
-
});
|
|
51
|
+
var cartItemId = generateCartItemId(item);
|
|
42
52
|
updateQuantity(cartItemId, newQuantity);
|
|
43
53
|
};
|
|
44
|
-
return (react_1.default.createElement(Modal_1.default, { animation: "
|
|
45
|
-
react_1.default.createElement(
|
|
54
|
+
return (react_1.default.createElement(Modal_1.default, { animation: "SlideDown", duration: 0.3, open: isOpen, setOpen: setIsOpen, maxWidth: '600px', title: react_1.default.createElement(Text_1.default, { text: "Your Cart", size: "h3" }), body: react_1.default.createElement(Div_1.default, { funcss: "pt pb" }, cart.length === 0 ? (react_1.default.createElement(Div_1.default, null,
|
|
55
|
+
react_1.default.createElement(Empty_1.default, { title: "Your cart is empty", showCta: true, description: "Add items to your cart to checkout", ctaText: "Continue Shopping", ctaOnClick: function () { return setIsOpen(false); }, ctaIcon: react_1.default.createElement(pi_1.PiBag, null) }))) : (react_1.default.createElement(react_1.default.Fragment, null,
|
|
46
56
|
cart.map(function (item, index) {
|
|
47
|
-
var _a;
|
|
48
|
-
var cartItemId = generateCartItemId(item
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
57
|
+
var _a, _b, _c;
|
|
58
|
+
var cartItemId = generateCartItemId(item);
|
|
59
|
+
var currentPrice = ((_a = item.variant) === null || _a === void 0 ? void 0 : _a.price) || item.product.price;
|
|
60
|
+
var originalPrice = item.originalPrice || item.product.comparePrice || currentPrice;
|
|
61
|
+
var hasDiscount = originalPrice > currentPrice;
|
|
62
|
+
var isFirst = index === 0;
|
|
63
|
+
var isLast = index === cart.length - 1;
|
|
64
|
+
return (react_1.default.createElement(Flex_1.default, { key: "".concat(cartItemId, "-").concat(index), funcss: "bt pt pb ".concat(isLast ? "bb" : ""), alignItems: "center", justify: 'space-between', gap: 1, width: '100%' },
|
|
65
|
+
react_1.default.createElement(Div_1.default, { funcss: " width-80 height-80" }, ((_b = item.product.images) === null || _b === void 0 ? void 0 : _b[0]) ? (react_1.default.createElement(ImageScaler_1.default, { src: item.product.images[0], size: '80px' })) : (react_1.default.createElement(Div_1.default, { funcss: "w-80 h-80 flex central" },
|
|
54
66
|
react_1.default.createElement(Text_1.default, { text: "No Image", color: "text-light", size: "sm" })))),
|
|
55
|
-
react_1.default.createElement(Div_1.default, { funcss:
|
|
56
|
-
react_1.default.createElement(Text_1.default, { text: item.product.name, block: true }),
|
|
57
|
-
(item.
|
|
58
|
-
react_1.default.createElement(Text_1.default, { text: "".concat(item.
|
|
67
|
+
react_1.default.createElement(Div_1.default, { funcss: 'w-200' },
|
|
68
|
+
react_1.default.createElement(Text_1.default, { text: item.product.name, block: true, weight: 600 }),
|
|
69
|
+
((_c = item.variant) === null || _c === void 0 ? void 0 : _c.name) && (react_1.default.createElement(Text_1.default, { text: item.variant.name, size: 'sm', color: "text-light" })),
|
|
70
|
+
(item.selectedColor || item.selectedSize) && (react_1.default.createElement(Text_1.default, { text: "".concat(item.selectedColor ? "Color: ".concat(item.selectedColor) : '', " ").concat(item.selectedSize ? "Size: ".concat(item.selectedSize) : ''), size: 'sm', color: "text-light" })),
|
|
71
|
+
react_1.default.createElement(Flex_1.default, { gap: 0.2 },
|
|
72
|
+
hasDiscount && (react_1.default.createElement(Text_1.default, { text: "".concat(currency).concat(originalPrice.toFixed(2)), size: 'sm', color: "text-light", style: { textDecoration: 'line-through' } })),
|
|
73
|
+
react_1.default.createElement(Text_1.default, { text: "".concat(currency).concat(currentPrice.toFixed(2)).concat(hasDiscount ? " (Save ".concat(currency).concat((originalPrice - currentPrice).toFixed(2), ")") : ''), color: hasDiscount ? 'success' : 'text', size: 'xs' }))),
|
|
59
74
|
react_1.default.createElement("div", { className: "col" },
|
|
60
75
|
react_1.default.createElement(Flex_1.default, { width: '100%', gap: 0.5, alignItems: 'center' },
|
|
61
|
-
react_1.default.createElement(Circle_1.default, { size:
|
|
62
|
-
react_1.default.createElement("div", { className: "w-
|
|
76
|
+
react_1.default.createElement(Circle_1.default, { size: 1.5, bg: 'lighter', body: react_1.default.createElement(pi_1.PiMinus, { size: 12 }), onClick: function () { return handleUpdateQuantity(item, item.quantity - 1); }, "aria-label": "Decrease quantity of ".concat(item.product.name) }),
|
|
77
|
+
react_1.default.createElement("div", { className: "w-70" },
|
|
63
78
|
react_1.default.createElement(Input_1.default, { type: "number", value: item.quantity, onChange: function (e) {
|
|
64
79
|
var value = parseInt(e.target.value);
|
|
65
80
|
if (!isNaN(value)) {
|
|
66
81
|
handleUpdateQuantity(item, Math.max(1, value));
|
|
67
82
|
}
|
|
68
|
-
}, fullWidth: true, bordered: true })),
|
|
69
|
-
react_1.default.createElement(Circle_1.default, { bg: '
|
|
70
|
-
|
|
83
|
+
}, fullWidth: true, bordered: true, "aria-label": "Quantity for ".concat(item.product.name) })),
|
|
84
|
+
react_1.default.createElement(Circle_1.default, { bg: 'lighter', size: 1.5, body: react_1.default.createElement(pi_1.PiPlus, { size: 12 }), onClick: function () { return handleUpdateQuantity(item, item.quantity + 1); }, "aria-label": "Increase quantity of ".concat(item.product.name) }),
|
|
85
|
+
react_1.default.createElement("div", null),
|
|
86
|
+
react_1.default.createElement(Circle_1.default, { size: 1.5, body: react_1.default.createElement(pi_1.PiTrash, { size: 12 }), onClick: function () { return handleRemoveItem(item); }, bg: 'error', "aria-label": "Remove ".concat(item.product.name, " from cart") })))));
|
|
71
87
|
}),
|
|
72
|
-
react_1.default.createElement(
|
|
73
|
-
|
|
74
|
-
react_1.default.createElement(Text_1.default, { text: "Subtotal", color: "text-light" }),
|
|
75
|
-
react_1.default.createElement(Text_1.default, { text: "".concat(currency).concat(subtotal.toFixed(2)) })),
|
|
88
|
+
react_1.default.createElement("div", { className: "section" }, persistCart && (react_1.default.createElement(Button_1.default, { text: "Clear Cart", onClick: clearCart, startIcon: react_1.default.createElement(tb_1.TbShoppingBagX, null), bg: "error-light", color: "error", funcss: "full-width" }))),
|
|
89
|
+
react_1.default.createElement(Div_1.default, { funcss: 'mt' },
|
|
76
90
|
react_1.default.createElement(RowFlex_1.default, { justify: "space-between" },
|
|
77
91
|
react_1.default.createElement(Text_1.default, { text: "Items", color: "text-light" }),
|
|
78
|
-
react_1.default.createElement(Text_1.default, { text: totalItems.toString() }))
|
|
92
|
+
react_1.default.createElement(Text_1.default, { text: totalItems.toString() })),
|
|
93
|
+
totalSavings > 0 && (react_1.default.createElement(RowFlex_1.default, { justify: "space-between" },
|
|
94
|
+
react_1.default.createElement(Text_1.default, { text: "Total Savings", color: "text-light" }),
|
|
95
|
+
react_1.default.createElement(Text_1.default, { text: "-".concat(currency).concat(totalSavings.toFixed(2)), color: "success" }))),
|
|
96
|
+
react_1.default.createElement(RowFlex_1.default, { justify: "space-between" },
|
|
97
|
+
react_1.default.createElement(Text_1.default, { text: "Subtotal", color: "text-light" }),
|
|
98
|
+
react_1.default.createElement(Text_1.default, { text: "".concat(currency).concat(subtotal.toFixed(2)) })))))), footer: cart.length > 0 ? (react_1.default.createElement(Div_1.default, { funcss: "pt pb" },
|
|
79
99
|
react_1.default.createElement(RowFlex_1.default, { justify: "space-between", alignItems: "center" },
|
|
80
100
|
react_1.default.createElement(Div_1.default, null,
|
|
81
101
|
react_1.default.createElement(Text_1.default, { text: "Total", color: "text-light", size: "sm", block: true }),
|
|
82
102
|
react_1.default.createElement(Text_1.default, { text: "".concat(currency).concat(subtotal.toFixed(2)), size: "h4" })),
|
|
83
|
-
react_1.default.createElement(Button_1.default, { text: checkoutText, bg: "primary", raised: true, onClick: handleCheckout, funcss: "padding-x-30", startIcon: checkoutIcon
|
|
103
|
+
react_1.default.createElement(Button_1.default, { text: checkoutText, bg: "primary", raised: true, onClick: handleCheckout, funcss: "padding-x-30", startIcon: checkoutIcon })))) : react_1.default.createElement(react_1.default.Fragment, null) }));
|
|
84
104
|
};
|
|
85
105
|
exports.default = CartModal;
|
|
@@ -15,42 +15,29 @@ var ProductCard = function (_a) {
|
|
|
15
15
|
var discountPercent = hasDiscount
|
|
16
16
|
? Math.round(((product.comparePrice - product.price) / product.comparePrice) * 100)
|
|
17
17
|
: 0;
|
|
18
|
-
var stockAvailable = product.stock === undefined || product.stock > 0;
|
|
19
18
|
var handleClick = function () {
|
|
20
19
|
onClick === null || onClick === void 0 ? void 0 : onClick(product);
|
|
21
20
|
};
|
|
22
|
-
var handleAddToCart = function (e) {
|
|
23
|
-
e.stopPropagation();
|
|
24
|
-
onAddToCart === null || onAddToCart === void 0 ? void 0 : onAddToCart(product);
|
|
25
|
-
};
|
|
26
21
|
var getDisplayPrice = function () {
|
|
27
22
|
var price = product.price || 0;
|
|
28
23
|
var productCurrency = product.currency || currency;
|
|
29
24
|
return "".concat(productCurrency).concat(price.toFixed(2));
|
|
30
25
|
};
|
|
31
26
|
return (react_1.default.createElement(Div_1.default, { funcss: "funui_store_product-card ".concat(className, " ").concat(funcss), onClick: handleClick, customStyle: { cursor: 'pointer' } },
|
|
32
|
-
react_1.default.createElement(Div_1.default, { funcss: "funui_store_image-container" },
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
product.isNew && (react_1.default.createElement(Text_1.default, { size: 'xs', color: 'success', weight: 600 }, "New"))),
|
|
40
|
-
react_1.default.createElement(Flex_1.default, { gap: 0.5, direction: 'column', width: '100%' },
|
|
41
|
-
react_1.default.createElement(Text_1.default, { block: true, size: 'lg', truncate: 2 }, product.name),
|
|
42
|
-
react_1.default.createElement(Flex_1.default, { width: '100%', gap: 1, justify: 'space-between' },
|
|
27
|
+
react_1.default.createElement(Div_1.default, { funcss: "funui_store_image-container round-edge" }, ((_b = product.images) === null || _b === void 0 ? void 0 : _b[0]) ? (react_1.default.createElement("img", { src: product.images[0], alt: product.name, loading: "lazy", className: "funui_store_product-image" })) : (react_1.default.createElement(Div_1.default, { funcss: "funui_store_no-image" },
|
|
28
|
+
react_1.default.createElement(Text_1.default, { text: "No Image", color: "text-muted", size: "sm" })))),
|
|
29
|
+
react_1.default.createElement(Div_1.default, { funcss: "funui_store_product-info " },
|
|
30
|
+
product.category && (react_1.default.createElement(Text_1.default, { size: 'xs', opacity: 4, uppercase: true }, product.category)),
|
|
31
|
+
react_1.default.createElement(Flex_1.default, { width: '100%', gap: 0.5, direction: 'column' },
|
|
32
|
+
react_1.default.createElement(Text_1.default, { block: true, weight: 500, truncate: 2 }, product.name),
|
|
33
|
+
react_1.default.createElement(Flex_1.default, { width: '100%', gap: 1, justify: 'space-between', alignItems: 'center' },
|
|
43
34
|
react_1.default.createElement(Flex_1.default, { gap: 0.5, alignItems: 'center' },
|
|
44
|
-
react_1.default.createElement("span", { className: "
|
|
35
|
+
react_1.default.createElement("span", { className: "block" }, getDisplayPrice()),
|
|
45
36
|
hasDiscount && (react_1.default.createElement(Text_1.default, { size: 'xs', color: 'info', weight: 600 },
|
|
46
37
|
discountPercent,
|
|
47
38
|
"% Off"))),
|
|
48
39
|
hasDiscount && (react_1.default.createElement(Text_1.default, { size: "sm", opacity: 4, textDecoration: 'line-through' },
|
|
49
40
|
product.currency || currency,
|
|
50
|
-
product.comparePrice.toFixed(2))))
|
|
51
|
-
!stockAvailable ? (react_1.default.createElement("span", { className: "funui_store_stock-info out text-xs" }, "Out of Stock")) : product.stock !== undefined && product.stock > 0 && product.stock < 10 ? (react_1.default.createElement("span", { className: "funui_store_stock-info low text-xs" },
|
|
52
|
-
"Only ",
|
|
53
|
-
product.stock,
|
|
54
|
-
" left")) : stockAvailable && (react_1.default.createElement("span", { className: "funui_store_stock-info in text-xs" }, "In Stock"))))));
|
|
41
|
+
product.comparePrice.toFixed(2))))))));
|
|
55
42
|
};
|
|
56
43
|
exports.default = ProductCard;
|
|
@@ -74,6 +74,9 @@ var ProductDetail = function (_a) {
|
|
|
74
74
|
var discountPercent = hasDiscount
|
|
75
75
|
? Math.round(((product.comparePrice - product.price) / product.comparePrice) * 100)
|
|
76
76
|
: 0;
|
|
77
|
+
// Stock information logic
|
|
78
|
+
var stockAvailable = product.stock === undefined || product.stock > 0;
|
|
79
|
+
var lowStock = product.stock !== undefined && product.stock > 0 && product.stock < 10;
|
|
77
80
|
var getDisplayPrice = function () {
|
|
78
81
|
var price = product.price || 0;
|
|
79
82
|
var productCurrency = product.currency || currency;
|
|
@@ -143,96 +146,124 @@ var ProductDetail = function (_a) {
|
|
|
143
146
|
return (react_1.default.createElement(Modal_1.default, { animation: "SlideDown", open: open, setOpen: setOpen, maxWidth: '1000px', title: react_1.default.createElement(react_1.default.Fragment, null), body: react_1.default.createElement(Flex_1.default, { width: '100%', gap: 2 },
|
|
144
147
|
react_1.default.createElement("div", { className: "w-400" }, product.images && product.images.length > 0 && (react_1.default.createElement(Div_1.default, { funcss: "margin-bottom-20" },
|
|
145
148
|
react_1.default.createElement(Div_1.default, { funcss: "funui_products_main_image_container mb-3" },
|
|
146
|
-
react_1.default.createElement(ImageScaler_1.default, { src: product.images[selectedImageIndex], size: "
|
|
149
|
+
react_1.default.createElement(ImageScaler_1.default, { src: product.images[selectedImageIndex], size: "400px", funcss: 'round-edge' })),
|
|
147
150
|
product.images.length > 1 && (react_1.default.createElement(Carousel_1.default, { gap: 1 }, product.images.map(function (image, index) { return (react_1.default.createElement(Div_1.default, { key: index, funcss: "funui_products_thumbnail rounde-edge ".concat(selectedImageIndex === index ? 'funui_products_thumbnail-active' : ''), onClick: function () { return setSelectedImageIndex(index); } },
|
|
148
|
-
react_1.default.createElement(ImageScaler_1.default, { src: image, size: "100px" }))); })))))),
|
|
151
|
+
react_1.default.createElement(ImageScaler_1.default, { src: image, size: "100px", funcss: 'round-edge' }))); })))))),
|
|
149
152
|
react_1.default.createElement("div", { className: "col" },
|
|
150
|
-
react_1.default.createElement(Flex_1.default, { direction: 'column', gap: 2, alignItems: 'flex-start', justify: 'flex-start' },
|
|
151
|
-
react_1.default.createElement("div",
|
|
152
|
-
react_1.default.createElement(Text_1.default, { text: product.name, size: "2xl", block: true }),
|
|
153
|
+
react_1.default.createElement(Flex_1.default, { direction: 'column', gap: 2, alignItems: 'flex-start', justify: 'flex-start', width: '100%' },
|
|
154
|
+
react_1.default.createElement("div", { className: "w-full" },
|
|
153
155
|
react_1.default.createElement(Flex_1.default, { justify: "space-between", alignItems: "center", width: '100%' },
|
|
154
|
-
react_1.default.createElement(
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
react_1.default.createElement(
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
156
|
+
react_1.default.createElement(Flex_1.default, { gap: 1, alignItems: "center" },
|
|
157
|
+
product.category && (react_1.default.createElement(Text_1.default, { size: 'xs', opacity: 4, uppercase: true, weight: 500, color: "text-muted" }, product.category)),
|
|
158
|
+
product.isNew && (react_1.default.createElement(Div_1.default, { funcss: "badge-new" },
|
|
159
|
+
react_1.default.createElement(Text_1.default, { size: 'xs', color: 'white', weight: 600 }, "NEW"))),
|
|
160
|
+
product.isSale && (react_1.default.createElement(Div_1.default, { funcss: "badge-sale" },
|
|
161
|
+
react_1.default.createElement(Text_1.default, { size: 'xs', color: 'white', weight: 600 }, "SALE")))),
|
|
162
|
+
!stockAvailable ? (react_1.default.createElement(Text_1.default, { size: 'xs', color: 'error', weight: 600 }, "Out of Stock")) : lowStock ? (react_1.default.createElement(Text_1.default, { size: 'xs', color: 'warning', weight: 600 },
|
|
163
|
+
"Only ",
|
|
164
|
+
product.stock,
|
|
165
|
+
" left")) : (react_1.default.createElement(Text_1.default, { size: 'xs', color: 'success', weight: 600 }, "In Stock"))),
|
|
166
|
+
react_1.default.createElement(Text_1.default, { text: product.name, size: "2xl", block: true, weight: 600, funcss: "mb-3" }),
|
|
167
|
+
react_1.default.createElement(Flex_1.default, { justify: "space-between", alignItems: "center", width: '100%', funcss: "mb-3" },
|
|
168
|
+
react_1.default.createElement(Flex_1.default, { gap: 1, alignItems: "baseline" },
|
|
169
|
+
react_1.default.createElement(Text_1.default, { text: getDisplayPrice(), size: "xl", weight: 700, color: "primary" }),
|
|
170
|
+
hasDiscount && (react_1.default.createElement(Div_1.default, { funcss: "discount-percent" },
|
|
171
|
+
react_1.default.createElement(Text_1.default, { size: 'sm', color: 'white', weight: 600 },
|
|
172
|
+
"-",
|
|
173
|
+
discountPercent,
|
|
174
|
+
"%")))),
|
|
175
|
+
(hasDiscount || product.comparePrice) && (react_1.default.createElement(Text_1.default, { text: "".concat(product.currency || currency).concat(product.comparePrice.toFixed(2)), textDecoration: 'line-through', size: "sm", color: "text-muted" })))),
|
|
176
|
+
product.stock !== undefined && (react_1.default.createElement(Div_1.default, null,
|
|
177
|
+
react_1.default.createElement(Text_1.default, { size: 'xs', opacity: 4 },
|
|
178
|
+
product.stock,
|
|
179
|
+
" units available"))),
|
|
180
|
+
((product.colors && product.colors.length > 0) || (product.sizes && product.sizes.length > 0)) && (react_1.default.createElement(Flex_1.default, { width: '100%', gap: 1 },
|
|
181
|
+
product.colors && product.colors.length > 0 && (react_1.default.createElement("div", { className: "col" },
|
|
182
|
+
react_1.default.createElement(Text_1.default, { size: "sm", weight: 500, funcss: "mb-1" }, "Color"),
|
|
183
|
+
react_1.default.createElement(Select_1.default, { fullWidth: true, options: __spreadArray([
|
|
184
|
+
{ text: 'Select Color', value: '' }
|
|
185
|
+
], product.colors.map(function (color) { return ({
|
|
186
|
+
text: color.name,
|
|
187
|
+
value: color.name,
|
|
188
|
+
prefix: (react_1.default.createElement(Div_1.default, { funcss: "color-preview", customStyle: {
|
|
189
|
+
width: "20px",
|
|
190
|
+
height: "20px",
|
|
191
|
+
borderRadius: "50%",
|
|
192
|
+
backgroundColor: color.code,
|
|
193
|
+
border: selectedColor === color.name ? '2px solid var(--primary)' : '1px solid var(--border)'
|
|
194
|
+
} }))
|
|
195
|
+
}); }), true), value: selectedColor, onChange: function (e) { return setSelectedColor(e); }, bordered: true }))),
|
|
196
|
+
product.sizes && product.sizes.length > 0 && (react_1.default.createElement("div", { className: "col" },
|
|
197
|
+
react_1.default.createElement(Text_1.default, { size: "sm", weight: 500, funcss: "mb-1" }, "Size"),
|
|
198
|
+
react_1.default.createElement(Select_1.default, { fullWidth: true, options: __spreadArray([
|
|
199
|
+
{ text: 'Select Size', value: '' }
|
|
200
|
+
], product.sizes.map(function (size) { return ({ text: size, value: size }); }), true), value: selectedSize, onChange: function (e) { return setSelectedSize(e); }, bordered: true }))))),
|
|
201
|
+
processedDescription && (react_1.default.createElement(Div_1.default, null,
|
|
202
|
+
react_1.default.createElement(Text_1.default, { text: "Description", size: "lg", weight: 600, funcss: "margin-bottom-1" }),
|
|
172
203
|
react_1.default.createElement("div", { className: "article text-sm ".concat(hasHTML ? '' : 'whitespace-pre-wrap'), dangerouslySetInnerHTML: { __html: displayDescription } }),
|
|
173
|
-
shouldTruncate && (react_1.default.createElement(Button_1.default, { text: showFullDescription ? 'Show Less' : 'Read More', onClick: function () { return setShowFullDescription(!showFullDescription); }, small: true, bg: 'lighter', startIcon: showFullDescription ? react_1.default.createElement(pi_1.PiCaretUp, null) : react_1.default.createElement(pi_1.PiCaretDown, null) })))),
|
|
174
|
-
react_1.default.createElement("
|
|
204
|
+
shouldTruncate && (react_1.default.createElement(Button_1.default, { text: showFullDescription ? 'Show Less' : 'Read More', onClick: function () { return setShowFullDescription(!showFullDescription); }, small: true, bg: 'lighter', startIcon: showFullDescription ? react_1.default.createElement(pi_1.PiCaretUp, null) : react_1.default.createElement(pi_1.PiCaretDown, null), funcss: "mt-2" })))),
|
|
205
|
+
react_1.default.createElement(Div_1.default, { funcss: "product-details-grid" },
|
|
175
206
|
react_1.default.createElement(Flex_1.default, { gap: 3, width: '100%' },
|
|
176
|
-
product.
|
|
177
|
-
react_1.default.createElement(
|
|
178
|
-
react_1.default.createElement(
|
|
179
|
-
react_1.default.createElement(
|
|
180
|
-
react_1.default.createElement(Text_1.default, { text: "
|
|
181
|
-
react_1.default.createElement(Text_1.default, { text: product.
|
|
182
|
-
product.
|
|
183
|
-
react_1.default.createElement(
|
|
207
|
+
product.brand && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
|
|
208
|
+
react_1.default.createElement(Div_1.default, null,
|
|
209
|
+
react_1.default.createElement(si_1.SiBlackmagicdesign, { className: 'text-primary' })),
|
|
210
|
+
react_1.default.createElement(Div_1.default, null,
|
|
211
|
+
react_1.default.createElement(Text_1.default, { text: "Brand", size: "xs", opacity: 4, block: true }),
|
|
212
|
+
react_1.default.createElement(Text_1.default, { text: product.brand, size: "sm", block: true, lineHeight: '1', weight: 500 })))),
|
|
213
|
+
product.sku && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
|
|
214
|
+
react_1.default.createElement(Div_1.default, null,
|
|
215
|
+
react_1.default.createElement(pi_1.PiTag, { className: 'text-primary' })),
|
|
216
|
+
react_1.default.createElement(Div_1.default, null,
|
|
217
|
+
react_1.default.createElement(Text_1.default, { text: "SKU", size: "xs", opacity: 4, block: true }),
|
|
218
|
+
react_1.default.createElement(Text_1.default, { text: product.sku, size: "sm", block: true, lineHeight: '1', weight: 500 })))),
|
|
219
|
+
product.rating && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
|
|
220
|
+
react_1.default.createElement(Div_1.default, null,
|
|
221
|
+
react_1.default.createElement(tfi_1.TfiComments, { className: 'text-primary' })),
|
|
222
|
+
react_1.default.createElement(Div_1.default, null,
|
|
223
|
+
react_1.default.createElement(Text_1.default, { text: "Rating", size: "xs", opacity: 4, block: true }),
|
|
224
|
+
react_1.default.createElement(Text_1.default, { text: product.rating.toString(), size: "sm", block: true, lineHeight: '1', weight: 500 })))),
|
|
225
|
+
product.weight && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
|
|
226
|
+
react_1.default.createElement(Div_1.default, null,
|
|
227
|
+
react_1.default.createElement(pi_1.PiScales, { className: 'text-primary' })),
|
|
228
|
+
react_1.default.createElement(Div_1.default, null,
|
|
229
|
+
react_1.default.createElement(Text_1.default, { text: "Weight", size: "xs", opacity: 4, block: true }),
|
|
230
|
+
react_1.default.createElement(Text_1.default, { text: "".concat(product.weight, " ").concat(product.weightUnit || ''), size: "sm", block: true, lineHeight: '1', weight: 500 })))),
|
|
231
|
+
product.manufacturer && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
|
|
232
|
+
react_1.default.createElement(Div_1.default, null,
|
|
184
233
|
react_1.default.createElement(pi_1.PiUser, { className: 'text-primary' })),
|
|
185
|
-
react_1.default.createElement(
|
|
234
|
+
react_1.default.createElement(Div_1.default, null,
|
|
186
235
|
react_1.default.createElement(Text_1.default, { text: "Manufacturer", size: "xs", opacity: 4, block: true }),
|
|
187
|
-
react_1.default.createElement(Text_1.default, { text: product.manufacturer, size: "sm", block: true, lineHeight: '1' })))),
|
|
188
|
-
product.countryOfOrigin && (react_1.default.createElement(Flex_1.default, { gap: 0.3 },
|
|
189
|
-
react_1.default.createElement(
|
|
236
|
+
react_1.default.createElement(Text_1.default, { text: product.manufacturer, size: "sm", block: true, lineHeight: '1', weight: 500 })))),
|
|
237
|
+
product.countryOfOrigin && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
|
|
238
|
+
react_1.default.createElement(Div_1.default, null,
|
|
190
239
|
react_1.default.createElement(pi_1.PiGlobe, { className: 'text-primary' })),
|
|
191
|
-
react_1.default.createElement(
|
|
240
|
+
react_1.default.createElement(Div_1.default, null,
|
|
192
241
|
react_1.default.createElement(Text_1.default, { text: "Country of Origin", size: "xs", opacity: 4, block: true }),
|
|
193
|
-
react_1.default.createElement(Text_1.default, { text: product.countryOfOrigin, size: "sm", block: true, lineHeight: '1' })))),
|
|
194
|
-
product.
|
|
195
|
-
react_1.default.createElement(
|
|
196
|
-
react_1.default.createElement(pi_1.
|
|
197
|
-
react_1.default.createElement(
|
|
198
|
-
react_1.default.createElement(Text_1.default, { text: "
|
|
199
|
-
react_1.default.createElement(Text_1.default, { text:
|
|
200
|
-
product.
|
|
201
|
-
react_1.default.createElement(
|
|
202
|
-
react_1.default.createElement(tfi_1.TfiComments, { className: 'text-primary' })),
|
|
203
|
-
react_1.default.createElement("div", null,
|
|
204
|
-
react_1.default.createElement(Text_1.default, { text: "Rating", size: "xs", opacity: 4, block: true }),
|
|
205
|
-
react_1.default.createElement(Text_1.default, { text: product.rating, size: "sm", block: true, lineHeight: '1' })))),
|
|
206
|
-
product.brand && (react_1.default.createElement(Flex_1.default, { gap: 0.3 },
|
|
207
|
-
react_1.default.createElement("div", null,
|
|
208
|
-
react_1.default.createElement(si_1.SiBlackmagicdesign, { className: 'text-primary' })),
|
|
209
|
-
react_1.default.createElement("div", null,
|
|
210
|
-
react_1.default.createElement(Text_1.default, { text: "Brand", size: "xs", opacity: 4, block: true }),
|
|
211
|
-
react_1.default.createElement(Text_1.default, { text: product.brand, size: "sm", block: true, lineHeight: '1' })))),
|
|
212
|
-
product.category && (react_1.default.createElement(Flex_1.default, { gap: 0.3 },
|
|
213
|
-
react_1.default.createElement("div", null,
|
|
242
|
+
react_1.default.createElement(Text_1.default, { text: product.countryOfOrigin, size: "sm", block: true, lineHeight: '1', weight: 500 })))),
|
|
243
|
+
product.warranty && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
|
|
244
|
+
react_1.default.createElement(Div_1.default, null,
|
|
245
|
+
react_1.default.createElement(pi_1.PiShieldCheck, { className: 'text-primary' })),
|
|
246
|
+
react_1.default.createElement(Div_1.default, null,
|
|
247
|
+
react_1.default.createElement(Text_1.default, { text: "Warranty", size: "xs", opacity: 4, block: true }),
|
|
248
|
+
react_1.default.createElement(Text_1.default, { text: product.warranty, size: "sm", block: true, lineHeight: '1', weight: 500 })))),
|
|
249
|
+
product.category && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
|
|
250
|
+
react_1.default.createElement(Div_1.default, null,
|
|
214
251
|
react_1.default.createElement(io5_1.IoLayersOutline, { className: 'text-primary' })),
|
|
215
|
-
react_1.default.createElement(
|
|
252
|
+
react_1.default.createElement(Div_1.default, null,
|
|
216
253
|
react_1.default.createElement(Text_1.default, { text: "Category", size: "xs", opacity: 4, block: true }),
|
|
217
|
-
react_1.default.createElement(Text_1.default, { text: product.category, size: "sm", block: true, lineHeight: '1' })))),
|
|
218
|
-
product.
|
|
219
|
-
react_1.default.createElement(
|
|
220
|
-
react_1.default.createElement(pi_1.
|
|
221
|
-
react_1.default.createElement(
|
|
222
|
-
react_1.default.createElement(Text_1.default, { text: "
|
|
223
|
-
react_1.default.createElement(Text_1.default, { text:
|
|
224
|
-
product.
|
|
225
|
-
react_1.default.createElement(
|
|
226
|
-
react_1.default.createElement(pi_1.
|
|
227
|
-
react_1.default.createElement(
|
|
228
|
-
react_1.default.createElement(Text_1.default, { text: "
|
|
229
|
-
react_1.default.createElement(Text_1.default, { text:
|
|
230
|
-
product.weight && (react_1.default.createElement(Flex_1.default, { gap: 0.3 },
|
|
231
|
-
react_1.default.createElement("div", null,
|
|
232
|
-
react_1.default.createElement(pi_1.PiScales, { className: 'text-primary' })),
|
|
233
|
-
react_1.default.createElement("div", null,
|
|
234
|
-
react_1.default.createElement(Text_1.default, { text: "Weight", size: "xs", opacity: 4, block: true }),
|
|
235
|
-
react_1.default.createElement(Text_1.default, { text: product.weight + " " + product.weightUnit, size: "sm", block: true, lineHeight: '1' }))))))))), footer: react_1.default.createElement(RowFlex_1.default, { gap: 1, justify: 'flex-end', funcss: 'pt' },
|
|
254
|
+
react_1.default.createElement(Text_1.default, { text: product.category, size: "sm", block: true, lineHeight: '1', weight: 500 })))),
|
|
255
|
+
product.brand && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
|
|
256
|
+
react_1.default.createElement(Div_1.default, null,
|
|
257
|
+
react_1.default.createElement(pi_1.PiStorefront, { className: 'text-primary' })),
|
|
258
|
+
react_1.default.createElement(Div_1.default, null,
|
|
259
|
+
react_1.default.createElement(Text_1.default, { text: "Store", size: "xs", opacity: 4, block: true }),
|
|
260
|
+
react_1.default.createElement(Text_1.default, { text: product.brand, size: "sm", block: true, lineHeight: '1', weight: 500 })))),
|
|
261
|
+
product.tags && product.tags.length > 0 && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
|
|
262
|
+
react_1.default.createElement(Div_1.default, null,
|
|
263
|
+
react_1.default.createElement(pi_1.PiTag, { className: 'text-primary' })),
|
|
264
|
+
react_1.default.createElement(Div_1.default, null,
|
|
265
|
+
react_1.default.createElement(Text_1.default, { text: "Tags", size: "xs", opacity: 4, block: true }),
|
|
266
|
+
react_1.default.createElement(Text_1.default, { text: product.tags.join(', '), size: "sm", block: true, lineHeight: '1', truncate: 1 }))))))))), footer: react_1.default.createElement(RowFlex_1.default, { gap: 1, justify: 'flex-end', funcss: 'pt' },
|
|
236
267
|
react_1.default.createElement(RowFlex_1.default, { gap: 0.5, alignItems: "center" },
|
|
237
268
|
react_1.default.createElement(Circle_1.default, { body: react_1.default.createElement(pi_1.PiMinus, null), onClick: function () { return setQuantity(Math.max(1, quantity - 1)); }, funcss: quantity <= 1 ? "disabled" : "" }),
|
|
238
269
|
react_1.default.createElement("div", { className: "w-90" },
|
|
@@ -244,6 +275,6 @@ var ProductDetail = function (_a) {
|
|
|
244
275
|
}, funcss: "text-center", bordered: true })),
|
|
245
276
|
react_1.default.createElement(Circle_1.default, { onClick: function () { return setQuantity(quantity + 1); }, bg: 'lighter' },
|
|
246
277
|
react_1.default.createElement(pi_1.PiPlus, null))),
|
|
247
|
-
react_1.default.createElement(Button_1.default, { text: "Add to Cart - ".concat(currency).concat(totalPrice), bg: "primary", raised: true, onClick: handleAddToCart })) }));
|
|
278
|
+
react_1.default.createElement(Button_1.default, { text: "Add to Cart - ".concat(currency).concat(totalPrice), bg: "primary", raised: true, onClick: handleAddToCart, disabled: !stockAvailable })) }));
|
|
248
279
|
};
|
|
249
280
|
exports.default = ProductDetail;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
var react_1 = __importDefault(require("react"));
|
|
8
|
+
var Div_1 = __importDefault(require("../div/Div"));
|
|
9
|
+
var View_1 = __importDefault(require("../view/View"));
|
|
10
|
+
var ProductLoader = function () {
|
|
11
|
+
return (react_1.default.createElement(Div_1.default, { funcss: "funui_store_product-card " },
|
|
12
|
+
react_1.default.createElement(Div_1.default, { funcss: "funui_store_image-container round-edge skeleton" }),
|
|
13
|
+
react_1.default.createElement(View_1.default, { funcss: 'skeleton round-edge', style: {
|
|
14
|
+
height: "1rem",
|
|
15
|
+
marginTop: "0.5rem"
|
|
16
|
+
} }),
|
|
17
|
+
react_1.default.createElement(View_1.default, { funcss: 'skeleton round-edge', style: {
|
|
18
|
+
height: "3rem",
|
|
19
|
+
marginTop: "0.5rem"
|
|
20
|
+
} })));
|
|
21
|
+
};
|
|
22
|
+
exports.default = ProductLoader;
|
package/ui/products/Store.d.ts
CHANGED
|
@@ -59,16 +59,43 @@ export type CartStorage = {
|
|
|
59
59
|
items: CartItem[];
|
|
60
60
|
updatedAt: number;
|
|
61
61
|
};
|
|
62
|
+
export type UserInfoField = {
|
|
63
|
+
infoName: string;
|
|
64
|
+
type: 'text' | 'tel' | 'email' | 'number' | 'textarea';
|
|
65
|
+
required: boolean;
|
|
66
|
+
label?: string;
|
|
67
|
+
placeholder?: string;
|
|
68
|
+
};
|
|
69
|
+
export type OtherInfo = UserInfoField[] | string;
|
|
70
|
+
export type CheckoutData = {
|
|
71
|
+
cartItems: CartItem[];
|
|
72
|
+
totalAmount: number;
|
|
73
|
+
userInfo: Record<string, string>;
|
|
74
|
+
};
|
|
62
75
|
type ProductsPageProps = {
|
|
63
76
|
products?: Product[] | string;
|
|
64
77
|
bucket?: string;
|
|
65
78
|
bucketPage?: number;
|
|
66
79
|
bucketSize?: number;
|
|
67
80
|
title?: string;
|
|
68
|
-
|
|
81
|
+
heroAlign?: 'left' | 'center' | 'right' | 'justify';
|
|
82
|
+
heroTitle?: string;
|
|
83
|
+
heroDescription?: string;
|
|
84
|
+
heroBackgroundImage?: string;
|
|
85
|
+
heroHeight?: string;
|
|
86
|
+
overlayColor?: string;
|
|
87
|
+
overlayOpacity?: number;
|
|
88
|
+
overlayGradient?: boolean;
|
|
89
|
+
gradientDirection?: 'to-bottom' | 'to-top' | 'to-left' | 'to-right' | 'to-bottom-right' | 'to-bottom-left' | 'to-top-right' | 'to-top-left';
|
|
90
|
+
invertGradient?: boolean;
|
|
69
91
|
showSearch?: boolean;
|
|
70
92
|
showFilters?: boolean;
|
|
71
|
-
|
|
93
|
+
showHero?: boolean;
|
|
94
|
+
titleSize?: string;
|
|
95
|
+
titleColor?: string;
|
|
96
|
+
descriptionSize?: string;
|
|
97
|
+
descriptionColor?: string;
|
|
98
|
+
descriptionOpacity?: number;
|
|
72
99
|
cartIcon?: string | React.ReactNode;
|
|
73
100
|
cartBadgeColor?: string;
|
|
74
101
|
cartBadgeText?: string;
|
|
@@ -77,21 +104,19 @@ type ProductsPageProps = {
|
|
|
77
104
|
currency?: string;
|
|
78
105
|
persistCart?: boolean;
|
|
79
106
|
storageKey?: string;
|
|
107
|
+
whatsappOrderNumber?: string;
|
|
108
|
+
otherInfo?: OtherInfo;
|
|
80
109
|
onAddToCart?: (item: CartItem) => void;
|
|
81
110
|
onRemoveFromCart?: (itemId: string) => void;
|
|
82
111
|
onUpdateQuantity?: (itemId: string, quantity: number) => void;
|
|
83
|
-
onCheckout?: (
|
|
112
|
+
onCheckout?: (checkoutData: CheckoutData) => void;
|
|
84
113
|
onProductClick?: (product: Product) => void;
|
|
85
114
|
className?: string;
|
|
86
115
|
gridClassName?: string;
|
|
87
116
|
children?: React.ReactNode;
|
|
88
117
|
id?: string;
|
|
89
118
|
funcss?: string;
|
|
90
|
-
bg?: string;
|
|
91
|
-
color?: string;
|
|
92
119
|
fullWidth?: boolean;
|
|
93
|
-
small?: boolean;
|
|
94
|
-
big?: boolean;
|
|
95
120
|
itemsPerPage?: number;
|
|
96
121
|
variant?: string;
|
|
97
122
|
};
|