@libreapps/commerce 7.5.1
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/.turbo/turbo-build.log +4 -0
- package/CHANGELOG.md +13 -0
- package/LICENSE.md +21 -0
- package/components/Icons.tsx +35 -0
- package/components/add-to-cart-widget.tsx +183 -0
- package/components/buy/buy-card.tsx +259 -0
- package/components/buy/carousel-buy-card.tsx +242 -0
- package/components/buy/multi-family/all-variants-carousel.tsx +261 -0
- package/components/buy/multi-family/family-carousel/index.tsx +77 -0
- package/components/buy/multi-family/family-carousel/slide.tsx +83 -0
- package/components/buy/multi-family/family-carousel/state.ts +87 -0
- package/components/buy/multi-family/index.ts +2 -0
- package/components/buy/single-family-selector.tsx +90 -0
- package/components/buy/title-and-byline.tsx +25 -0
- package/components/cart/cart-panel/cart-line-item.tsx +76 -0
- package/components/cart/cart-panel/index.tsx +154 -0
- package/components/cart/cart-panel/promo-code.tsx +109 -0
- package/components/cart/cart-panel/total-area.tsx +60 -0
- package/components/checkout/payment-step-form/card-icon-row.tsx +26 -0
- package/components/checkout/payment-step-form/card-icons/amex.tsx +32 -0
- package/components/checkout/payment-step-form/card-icons/diners-club.tsx +13 -0
- package/components/checkout/payment-step-form/card-icons/discover.tsx +25 -0
- package/components/checkout/payment-step-form/card-icons/jcb.tsx +26 -0
- package/components/checkout/payment-step-form/card-icons/mastercard.tsx +27 -0
- package/components/checkout/payment-step-form/card-icons/visa.tsx +25 -0
- package/components/checkout/payment-step-form/cc-button.tsx +17 -0
- package/components/checkout/payment-step-form/contact-form.tsx +50 -0
- package/components/checkout/payment-step-form/crypto-icons/btc.tsx +11 -0
- package/components/checkout/payment-step-form/crypto-icons/eth.tsx +20 -0
- package/components/checkout/payment-step-form/crypto-icons/usdt.tsx +13 -0
- package/components/checkout/payment-step-form/index.tsx +122 -0
- package/components/checkout/payment-step-form/methods/bank-transfer.tsx +79 -0
- package/components/checkout/payment-step-form/methods/card.tsx +232 -0
- package/components/checkout/payment-step-form/methods/crypto.tsx +227 -0
- package/components/checkout/payment-step-form/methods/index.ts +23 -0
- package/components/checkout/shipping-step-form.tsx +175 -0
- package/components/index.ts +11 -0
- package/components/item/product-card.tsx +48 -0
- package/components/item-selector/button.tsx +188 -0
- package/components/item-selector/carousel/index.tsx +197 -0
- package/components/item-selector/carousel/slider.tsx +40 -0
- package/components/item-selector/index.ts +5 -0
- package/components/item-selector/quantity-indicator.tsx +48 -0
- package/components/node-tabs/index.tsx +91 -0
- package/components/node-tabs/node-image.tsx +31 -0
- package/dist/components/Icons.d.ts +18 -0
- package/dist/components/Icons.js +19 -0
- package/dist/components/Icons.js.map +1 -0
- package/dist/components/add-to-cart-widget.d.ts +11 -0
- package/dist/components/add-to-cart-widget.js +85 -0
- package/dist/components/add-to-cart-widget.js.map +1 -0
- package/dist/components/buy/buy-card.d.ts +30 -0
- package/dist/components/buy/buy-card.js +109 -0
- package/dist/components/buy/buy-card.js.map +1 -0
- package/dist/components/buy/carousel-buy-card.d.ts +12 -0
- package/dist/components/buy/carousel-buy-card.js +94 -0
- package/dist/components/buy/carousel-buy-card.js.map +1 -0
- package/dist/components/buy/multi-family/all-variants-carousel.d.ts +4 -0
- package/dist/components/buy/multi-family/all-variants-carousel.js +115 -0
- package/dist/components/buy/multi-family/all-variants-carousel.js.map +1 -0
- package/dist/components/buy/multi-family/family-carousel/index.d.ts +4 -0
- package/dist/components/buy/multi-family/family-carousel/index.js +27 -0
- package/dist/components/buy/multi-family/family-carousel/index.js.map +1 -0
- package/dist/components/buy/multi-family/family-carousel/slide.d.ts +11 -0
- package/dist/components/buy/multi-family/family-carousel/slide.js +35 -0
- package/dist/components/buy/multi-family/family-carousel/slide.js.map +1 -0
- package/dist/components/buy/multi-family/family-carousel/state.d.ts +20 -0
- package/dist/components/buy/multi-family/family-carousel/state.js +59 -0
- package/dist/components/buy/multi-family/family-carousel/state.js.map +1 -0
- package/dist/components/buy/multi-family/index.d.ts +2 -0
- package/dist/components/buy/multi-family/index.js +3 -0
- package/dist/components/buy/multi-family/index.js.map +1 -0
- package/dist/components/buy/single-family-selector.d.ts +15 -0
- package/dist/components/buy/single-family-selector.js +28 -0
- package/dist/components/buy/single-family-selector.js.map +1 -0
- package/dist/components/buy/title-and-byline.d.ts +8 -0
- package/dist/components/buy/title-and-byline.js +7 -0
- package/dist/components/buy/title-and-byline.js.map +1 -0
- package/dist/components/cart/cart-panel/cart-line-item.d.ts +11 -0
- package/dist/components/cart/cart-panel/cart-line-item.js +25 -0
- package/dist/components/cart/cart-panel/cart-line-item.js.map +1 -0
- package/dist/components/cart/cart-panel/index.d.ts +19 -0
- package/dist/components/cart/cart-panel/index.js +65 -0
- package/dist/components/cart/cart-panel/index.js.map +1 -0
- package/dist/components/cart/cart-panel/promo-code.d.ts +4 -0
- package/dist/components/cart/cart-panel/promo-code.js +62 -0
- package/dist/components/cart/cart-panel/promo-code.js.map +1 -0
- package/dist/components/cart/cart-panel/total-area.d.ts +7 -0
- package/dist/components/cart/cart-panel/total-area.js +14 -0
- package/dist/components/cart/cart-panel/total-area.js.map +1 -0
- package/dist/components/checkout/payment-step-form/card-icon-row.d.ts +2 -0
- package/dist/components/checkout/payment-step-form/card-icon-row.js +14 -0
- package/dist/components/checkout/payment-step-form/card-icon-row.js.map +1 -0
- package/dist/components/checkout/payment-step-form/card-icons/amex.d.ts +4 -0
- package/dist/components/checkout/payment-step-form/card-icons/amex.js +6 -0
- package/dist/components/checkout/payment-step-form/card-icons/amex.js.map +1 -0
- package/dist/components/checkout/payment-step-form/card-icons/diners-club.d.ts +4 -0
- package/dist/components/checkout/payment-step-form/card-icons/diners-club.js +6 -0
- package/dist/components/checkout/payment-step-form/card-icons/diners-club.js.map +1 -0
- package/dist/components/checkout/payment-step-form/card-icons/discover.d.ts +4 -0
- package/dist/components/checkout/payment-step-form/card-icons/discover.js +6 -0
- package/dist/components/checkout/payment-step-form/card-icons/discover.js.map +1 -0
- package/dist/components/checkout/payment-step-form/card-icons/jcb.d.ts +4 -0
- package/dist/components/checkout/payment-step-form/card-icons/jcb.js +6 -0
- package/dist/components/checkout/payment-step-form/card-icons/jcb.js.map +1 -0
- package/dist/components/checkout/payment-step-form/card-icons/mastercard.d.ts +4 -0
- package/dist/components/checkout/payment-step-form/card-icons/mastercard.js +6 -0
- package/dist/components/checkout/payment-step-form/card-icons/mastercard.js.map +1 -0
- package/dist/components/checkout/payment-step-form/card-icons/visa.d.ts +4 -0
- package/dist/components/checkout/payment-step-form/card-icons/visa.js +6 -0
- package/dist/components/checkout/payment-step-form/card-icons/visa.js.map +1 -0
- package/dist/components/checkout/payment-step-form/cc-button.d.ts +3 -0
- package/dist/components/checkout/payment-step-form/cc-button.js +6 -0
- package/dist/components/checkout/payment-step-form/cc-button.js.map +1 -0
- package/dist/components/checkout/payment-step-form/contact-form.d.ts +5 -0
- package/dist/components/checkout/payment-step-form/contact-form.js +6 -0
- package/dist/components/checkout/payment-step-form/contact-form.js.map +1 -0
- package/dist/components/checkout/payment-step-form/crypto-icons/btc.d.ts +4 -0
- package/dist/components/checkout/payment-step-form/crypto-icons/btc.js +6 -0
- package/dist/components/checkout/payment-step-form/crypto-icons/btc.js.map +1 -0
- package/dist/components/checkout/payment-step-form/crypto-icons/eth.d.ts +4 -0
- package/dist/components/checkout/payment-step-form/crypto-icons/eth.js +6 -0
- package/dist/components/checkout/payment-step-form/crypto-icons/eth.js.map +1 -0
- package/dist/components/checkout/payment-step-form/crypto-icons/usdt.d.ts +4 -0
- package/dist/components/checkout/payment-step-form/crypto-icons/usdt.js +6 -0
- package/dist/components/checkout/payment-step-form/crypto-icons/usdt.js.map +1 -0
- package/dist/components/checkout/payment-step-form/index.d.ts +4 -0
- package/dist/components/checkout/payment-step-form/index.js +77 -0
- package/dist/components/checkout/payment-step-form/index.js.map +1 -0
- package/dist/components/checkout/payment-step-form/methods/bank-transfer.d.ts +4 -0
- package/dist/components/checkout/payment-step-form/methods/bank-transfer.js +24 -0
- package/dist/components/checkout/payment-step-form/methods/bank-transfer.js.map +1 -0
- package/dist/components/checkout/payment-step-form/methods/card.d.ts +4 -0
- package/dist/components/checkout/payment-step-form/methods/card.js +160 -0
- package/dist/components/checkout/payment-step-form/methods/card.js.map +1 -0
- package/dist/components/checkout/payment-step-form/methods/crypto.d.ts +9 -0
- package/dist/components/checkout/payment-step-form/methods/crypto.js +137 -0
- package/dist/components/checkout/payment-step-form/methods/crypto.js.map +1 -0
- package/dist/components/checkout/payment-step-form/methods/index.d.ts +6 -0
- package/dist/components/checkout/payment-step-form/methods/index.js +21 -0
- package/dist/components/checkout/payment-step-form/methods/index.js.map +1 -0
- package/dist/components/checkout/shipping-step-form.d.ts +3 -0
- package/dist/components/checkout/shipping-step-form.js +53 -0
- package/dist/components/checkout/shipping-step-form.js.map +1 -0
- package/dist/components/index.d.ts +8 -0
- package/dist/components/index.js +9 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/item/product-card.d.ts +7 -0
- package/dist/components/item/product-card.js +9 -0
- package/dist/components/item/product-card.js.map +1 -0
- package/dist/components/item-selector/button.d.ts +4 -0
- package/dist/components/item-selector/button.js +47 -0
- package/dist/components/item-selector/button.js.map +1 -0
- package/dist/components/item-selector/carousel/index.d.ts +12 -0
- package/dist/components/item-selector/carousel/index.js +74 -0
- package/dist/components/item-selector/carousel/index.js.map +1 -0
- package/dist/components/item-selector/carousel/slider.d.ts +8 -0
- package/dist/components/item-selector/carousel/slider.js +12 -0
- package/dist/components/item-selector/carousel/slider.js.map +1 -0
- package/dist/components/item-selector/index.d.ts +2 -0
- package/dist/components/item-selector/index.js +3 -0
- package/dist/components/item-selector/index.js.map +1 -0
- package/dist/components/item-selector/quantity-indicator.d.ts +9 -0
- package/dist/components/item-selector/quantity-indicator.js +16 -0
- package/dist/components/item-selector/quantity-indicator.js.map +1 -0
- package/dist/components/node-tabs/index.d.ts +14 -0
- package/dist/components/node-tabs/index.js +42 -0
- package/dist/components/node-tabs/index.js.map +1 -0
- package/dist/components/node-tabs/node-image.d.ts +6 -0
- package/dist/components/node-tabs/node-image.js +13 -0
- package/dist/components/node-tabs/node-image.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/service/context.d.ts +8 -0
- package/dist/service/context.js +19 -0
- package/dist/service/context.js.map +1 -0
- package/dist/service/debug.d.ts +10 -0
- package/dist/service/debug.js +30 -0
- package/dist/service/debug.js.map +1 -0
- package/dist/service/impls/standalone/actual-line-item.d.ts +40 -0
- package/dist/service/impls/standalone/actual-line-item.js +84 -0
- package/dist/service/impls/standalone/actual-line-item.js.map +1 -0
- package/dist/service/impls/standalone/get-instance.d.ts +2 -0
- package/dist/service/impls/standalone/get-instance.js +39 -0
- package/dist/service/impls/standalone/get-instance.js.map +1 -0
- package/dist/service/impls/standalone/index.d.ts +67 -0
- package/dist/service/impls/standalone/index.js +416 -0
- package/dist/service/impls/standalone/index.js.map +1 -0
- package/dist/service/impls/standalone/order/firebase.d.ts +2 -0
- package/dist/service/impls/standalone/order/firebase.js +13 -0
- package/dist/service/impls/standalone/order/firebase.js.map +1 -0
- package/dist/service/impls/standalone/order/index.d.ts +24 -0
- package/dist/service/impls/standalone/order/index.js +61 -0
- package/dist/service/impls/standalone/order/index.js.map +1 -0
- package/dist/service/impls/standalone/persistence.d.ts +4 -0
- package/dist/service/impls/standalone/persistence.js +22 -0
- package/dist/service/impls/standalone/persistence.js.map +1 -0
- package/dist/service/path-utils.d.ts +7 -0
- package/dist/service/path-utils.js +16 -0
- package/dist/service/path-utils.js.map +1 -0
- package/dist/service/sep.d.ts +6 -0
- package/dist/service/sep.js +6 -0
- package/dist/service/sep.js.map +1 -0
- package/dist/types/category-node.d.ts +36 -0
- package/dist/types/category-node.js +2 -0
- package/dist/types/category-node.js.map +1 -0
- package/dist/types/checkout.d.ts +33 -0
- package/dist/types/checkout.js +2 -0
- package/dist/types/checkout.js.map +1 -0
- package/dist/types/commerce-config.d.ts +11 -0
- package/dist/types/commerce-config.js +2 -0
- package/dist/types/commerce-config.js.map +1 -0
- package/dist/types/commerce-service.d.ts +109 -0
- package/dist/types/commerce-service.js +2 -0
- package/dist/types/commerce-service.js.map +1 -0
- package/dist/types/family.d.ts +16 -0
- package/dist/types/family.js +2 -0
- package/dist/types/family.js.map +1 -0
- package/dist/types/index.d.ts +13 -0
- package/dist/types/index.js +8 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/item-selector.d.ts +72 -0
- package/dist/types/item-selector.js +2 -0
- package/dist/types/item-selector.js.map +1 -0
- package/dist/types/line-item.d.ts +14 -0
- package/dist/types/line-item.js +2 -0
- package/dist/types/line-item.js.map +1 -0
- package/dist/types/multi-family-selector-props.d.ts +16 -0
- package/dist/types/multi-family-selector-props.js +2 -0
- package/dist/types/multi-family-selector-props.js.map +1 -0
- package/dist/types/product.d.ts +15 -0
- package/dist/types/product.js +2 -0
- package/dist/types/product.js.map +1 -0
- package/dist/types/promo.d.ts +7 -0
- package/dist/types/promo.js +2 -0
- package/dist/types/promo.js.map +1 -0
- package/dist/types/selection-ui-specifier.d.ts +40 -0
- package/dist/types/selection-ui-specifier.js +2 -0
- package/dist/types/selection-ui-specifier.js.map +1 -0
- package/dist/types/string-mutator.d.ts +9 -0
- package/dist/types/string-mutator.js +2 -0
- package/dist/types/string-mutator.js.map +1 -0
- package/dist/types/token-separators.d.ts +6 -0
- package/dist/types/token-separators.js +2 -0
- package/dist/types/token-separators.js.map +1 -0
- package/dist/util/analytics.d.ts +9 -0
- package/dist/util/analytics.js +10 -0
- package/dist/util/analytics.js.map +1 -0
- package/dist/util/countries.d.ts +7 -0
- package/dist/util/countries.js +197 -0
- package/dist/util/countries.js.map +1 -0
- package/dist/util/error.d.ts +1 -0
- package/dist/util/error.js +22 -0
- package/dist/util/error.js.map +1 -0
- package/dist/util/index.d.ts +15 -0
- package/dist/util/index.js +54 -0
- package/dist/util/index.js.map +1 -0
- package/dist/util/item-selector-options-accessor.d.ts +3 -0
- package/dist/util/item-selector-options-accessor.js +27 -0
- package/dist/util/item-selector-options-accessor.js.map +1 -0
- package/dist/util/line-item-ref.d.ts +8 -0
- package/dist/util/line-item-ref.js +15 -0
- package/dist/util/line-item-ref.js.map +1 -0
- package/dist/util/multi-family-selector-options-accessor.d.ts +3 -0
- package/dist/util/multi-family-selector-options-accessor.js +11 -0
- package/dist/util/multi-family-selector-options-accessor.js.map +1 -0
- package/dist/util/obs-string-mutator.d.ts +8 -0
- package/dist/util/obs-string-mutator.js +15 -0
- package/dist/util/obs-string-mutator.js.map +1 -0
- package/dist/util/product-media-accessor.d.ts +29 -0
- package/dist/util/product-media-accessor.js +22 -0
- package/dist/util/product-media-accessor.js.map +1 -0
- package/dist/util/promo-codes.d.ts +3 -0
- package/dist/util/promo-codes.js +100 -0
- package/dist/util/promo-codes.js.map +1 -0
- package/dist/util/selection-ui-specifiers.d.ts +3 -0
- package/dist/util/selection-ui-specifiers.js +24 -0
- package/dist/util/selection-ui-specifiers.js.map +1 -0
- package/dist/util/square-payment.d.ts +7 -0
- package/dist/util/square-payment.js +37 -0
- package/dist/util/square-payment.js.map +1 -0
- package/dist/util/use-sync-sku-param-w-current-item.d.ts +2 -0
- package/dist/util/use-sync-sku-param-w-current-item.js +61 -0
- package/dist/util/use-sync-sku-param-w-current-item.js.map +1 -0
- package/index.ts +13 -0
- package/libreapps-ui.d.ts +108 -0
- package/package.json +67 -0
- package/service/context.tsx +45 -0
- package/service/debug.ts +41 -0
- package/service/impls/standalone/actual-line-item.ts +136 -0
- package/service/impls/standalone/get-instance.ts +64 -0
- package/service/impls/standalone/index.ts +579 -0
- package/service/impls/standalone/order/firebase.ts +14 -0
- package/service/impls/standalone/order/index.ts +129 -0
- package/service/impls/standalone/persistence.ts +33 -0
- package/service/path-utils.ts +26 -0
- package/service/sep.ts +7 -0
- package/tsconfig.json +17 -0
- package/types/README.md +2 -0
- package/types/category-node.ts +50 -0
- package/types/checkout.ts +47 -0
- package/types/commerce-config.ts +13 -0
- package/types/commerce-service.ts +128 -0
- package/types/family.ts +26 -0
- package/types/index.ts +15 -0
- package/types/item-selector.ts +97 -0
- package/types/line-item.ts +29 -0
- package/types/multi-family-selector-props.ts +20 -0
- package/types/product.ts +21 -0
- package/types/promo.ts +10 -0
- package/types/selection-ui-specifier.ts +52 -0
- package/types/string-mutator.ts +14 -0
- package/types/token-separators.ts +7 -0
- package/util/analytics.ts +21 -0
- package/util/countries.ts +196 -0
- package/util/error.ts +34 -0
- package/util/index.ts +71 -0
- package/util/item-selector-options-accessor.ts +35 -0
- package/util/line-item-ref.ts +23 -0
- package/util/multi-family-selector-options-accessor.ts +15 -0
- package/util/obs-string-mutator.ts +22 -0
- package/util/product-media-accessor.ts +58 -0
- package/util/promo-codes.ts +106 -0
- package/util/selection-ui-specifiers.ts +30 -0
- package/util/square-payment.ts +50 -0
- package/util/use-sync-sku-param-w-current-item.ts +88 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import React, {
|
|
3
|
+
useRef,
|
|
4
|
+
useEffect,
|
|
5
|
+
type ComponentType,
|
|
6
|
+
useState
|
|
7
|
+
} from 'react'
|
|
8
|
+
import { observer } from 'mobx-react-lite'
|
|
9
|
+
|
|
10
|
+
import { cn } from '@libreapps/ui/util'
|
|
11
|
+
|
|
12
|
+
import type {
|
|
13
|
+
ItemSelectorProps,
|
|
14
|
+
LineItem,
|
|
15
|
+
Family,
|
|
16
|
+
CategoryNode,
|
|
17
|
+
CategoryNodeRole,
|
|
18
|
+
SelectionUISpecifier,
|
|
19
|
+
ItemSelectorOptions,
|
|
20
|
+
MultiFamilySelectorProps,
|
|
21
|
+
MultiFamilySelectorOptions,
|
|
22
|
+
} from '../../types'
|
|
23
|
+
|
|
24
|
+
import { useCommerce } from '../../service/context'
|
|
25
|
+
import { getSelectionUISpecifier } from '../../util'
|
|
26
|
+
|
|
27
|
+
import { CarouselItemSelector, ButtonItemSelector } from '../item-selector'
|
|
28
|
+
import SingleFamilySelector from './single-family-selector'
|
|
29
|
+
import { FamilyCarousel, AllVariantsCarousel } from './multi-family'
|
|
30
|
+
|
|
31
|
+
import AddToCartWidget from '../add-to-cart-widget'
|
|
32
|
+
|
|
33
|
+
const SCROLL = {
|
|
34
|
+
scrollAfter: 5,
|
|
35
|
+
scrollHeightClx: 'h-[65vh]'
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const MEDIA_CONSTRAINT = {w: 200, h: 200}
|
|
39
|
+
|
|
40
|
+
const sortItems = (items: LineItem[], sort: 'asc' | 'desc' | 'none'): LineItem[] => (
|
|
41
|
+
((sort === 'asc') ?
|
|
42
|
+
items.sort((a: LineItem, b: LineItem): number => (a.price - b.price))
|
|
43
|
+
:
|
|
44
|
+
((sort === 'desc') ?
|
|
45
|
+
items.sort((a: LineItem, b: LineItem): number => (b.price - a.price ))
|
|
46
|
+
:
|
|
47
|
+
items
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
const CarouselBuyCard: React.FC<{
|
|
53
|
+
skuPath: string
|
|
54
|
+
checkoutButton: React.ReactNode
|
|
55
|
+
clx?: string
|
|
56
|
+
selectorClx?: string
|
|
57
|
+
addBtnClx?: string
|
|
58
|
+
buttonsAreaClx?: string
|
|
59
|
+
mobile?: boolean
|
|
60
|
+
onQuantityChanged?: (sku: string, oldV: number, newV: number) => void
|
|
61
|
+
}> = ({
|
|
62
|
+
skuPath,
|
|
63
|
+
checkoutButton,
|
|
64
|
+
clx='',
|
|
65
|
+
selectorClx='',
|
|
66
|
+
addBtnClx='',
|
|
67
|
+
buttonsAreaClx='',
|
|
68
|
+
mobile=false,
|
|
69
|
+
onQuantityChanged,
|
|
70
|
+
}) => {
|
|
71
|
+
|
|
72
|
+
const cmmc = useCommerce()
|
|
73
|
+
|
|
74
|
+
const r = useRef<{
|
|
75
|
+
role: CategoryNodeRole
|
|
76
|
+
item: LineItem | undefined
|
|
77
|
+
family: Family | undefined
|
|
78
|
+
families: Family[] | undefined
|
|
79
|
+
node: CategoryNode
|
|
80
|
+
uiSpec: SelectionUISpecifier
|
|
81
|
+
single?: {
|
|
82
|
+
items: LineItem[]
|
|
83
|
+
Selector: ComponentType<ItemSelectorProps>
|
|
84
|
+
selOptions: ItemSelectorOptions | undefined
|
|
85
|
+
scrollable: boolean
|
|
86
|
+
showItemMedia: boolean
|
|
87
|
+
}
|
|
88
|
+
multi?: {
|
|
89
|
+
Selector: ComponentType<MultiFamilySelectorProps>
|
|
90
|
+
selectorOptions: MultiFamilySelectorOptions | undefined
|
|
91
|
+
itemOptions: ItemSelectorOptions | undefined
|
|
92
|
+
initialFamilyId: string
|
|
93
|
+
}
|
|
94
|
+
} | undefined>(undefined)
|
|
95
|
+
|
|
96
|
+
const [changeMeToRerender, setChangeMeToRerender] = useState<boolean>(false)
|
|
97
|
+
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
|
|
100
|
+
if (!skuPath || skuPath.length === 0 ) {
|
|
101
|
+
// The component is being hidden (w an amination)
|
|
102
|
+
// keep things the same so no layout jump
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const peek = cmmc.peek(skuPath)
|
|
107
|
+
if (typeof peek === 'string') {
|
|
108
|
+
throw new Error(peek)
|
|
109
|
+
}
|
|
110
|
+
if (peek.role === 'non-outermost') {
|
|
111
|
+
throw new Error(`BuyCard: skuPath ${skuPath} isn't an outermost tree node or product family!`)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const uiSpec = getSelectionUISpecifier(skuPath)
|
|
115
|
+
|
|
116
|
+
r.current = {
|
|
117
|
+
...peek,
|
|
118
|
+
node: peek.node!, // else exception was thrown.
|
|
119
|
+
uiSpec,
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (peek.role === 'single-family') {
|
|
123
|
+
|
|
124
|
+
const sort = (): 'none' | 'asc' | 'desc' => {
|
|
125
|
+
const options = uiSpec.singleFamily?.options ?? {}
|
|
126
|
+
const showSlider = 'showSlider' in options ? options.showSlider! : true
|
|
127
|
+
return ('sort' in options) ?
|
|
128
|
+
options.sort!
|
|
129
|
+
:
|
|
130
|
+
(showSlider ? 'asc' : 'none')
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const items = peek.family!.products as LineItem[]
|
|
134
|
+
const Selector: ComponentType<ItemSelectorProps> =
|
|
135
|
+
(uiSpec.singleFamily?.type === 'buttons') ? ButtonItemSelector : CarouselItemSelector
|
|
136
|
+
const selOptions = uiSpec.singleFamily?.options
|
|
137
|
+
|
|
138
|
+
r.current.single = {
|
|
139
|
+
items: sortItems(items, sort()),
|
|
140
|
+
Selector,
|
|
141
|
+
selOptions,
|
|
142
|
+
scrollable: !!(items.length > SCROLL.scrollAfter),
|
|
143
|
+
showItemMedia: uiSpec.singleFamily?.type !== 'carousel'
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const currItem = peek.item ?? r.current.single.items[0]
|
|
147
|
+
cmmc.setCurrentItem(currItem.sku)
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
const initialFamily = peek.family ? peek.family : peek.families![0]
|
|
151
|
+
// TODO: Does this ever need to be sorted??
|
|
152
|
+
const currItem = peek.item ?? initialFamily.products[0]
|
|
153
|
+
|
|
154
|
+
// sets currentFamily as well
|
|
155
|
+
cmmc.setCurrentItem(currItem.sku)
|
|
156
|
+
|
|
157
|
+
r.current.multi = {
|
|
158
|
+
Selector: (uiSpec.multiFamily?.type === 'family-carousel') ?
|
|
159
|
+
FamilyCarousel
|
|
160
|
+
:
|
|
161
|
+
AllVariantsCarousel,
|
|
162
|
+
itemOptions: uiSpec.multiFamily?.itemOptions,
|
|
163
|
+
selectorOptions: uiSpec.multiFamily?.selectorOptions,
|
|
164
|
+
initialFamilyId: initialFamily.id
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Must do this since Dialog code takes this comp out of the React shadow DOM
|
|
169
|
+
// (only in prod apparently.)
|
|
170
|
+
setChangeMeToRerender(!changeMeToRerender)
|
|
171
|
+
}, [skuPath])
|
|
172
|
+
|
|
173
|
+
const MultiFamilyUI: React.FC<{
|
|
174
|
+
Selector: ComponentType<MultiFamilySelectorProps>
|
|
175
|
+
selectorOptions: MultiFamilySelectorOptions | undefined
|
|
176
|
+
itemOptions: ItemSelectorOptions | undefined
|
|
177
|
+
families: Family[]
|
|
178
|
+
parent: CategoryNode
|
|
179
|
+
clx?: string
|
|
180
|
+
}> = ({
|
|
181
|
+
Selector,
|
|
182
|
+
itemOptions,
|
|
183
|
+
selectorOptions,
|
|
184
|
+
families,
|
|
185
|
+
parent,
|
|
186
|
+
clx=''
|
|
187
|
+
}) => (
|
|
188
|
+
<Selector
|
|
189
|
+
families={families}
|
|
190
|
+
parent={parent}
|
|
191
|
+
clx={clx}
|
|
192
|
+
itemOptions={itemOptions}
|
|
193
|
+
selectorOptions={selectorOptions}
|
|
194
|
+
mediaConstraint={MEDIA_CONSTRAINT}
|
|
195
|
+
mobile={mobile}
|
|
196
|
+
/>
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
const Buttons: React.FC<{clx?: string}> = observer(({
|
|
200
|
+
clx=''
|
|
201
|
+
}) => (cmmc.currentItem ? (
|
|
202
|
+
<div className={clx}>
|
|
203
|
+
<AddToCartWidget
|
|
204
|
+
item={cmmc.currentItem}
|
|
205
|
+
onQuantityChanged={onQuantityChanged}
|
|
206
|
+
variant={cmmc.cartEmpty ? 'primary' : 'outline'}
|
|
207
|
+
className={addBtnClx}
|
|
208
|
+
/>
|
|
209
|
+
{!cmmc.cartEmpty && checkoutButton}
|
|
210
|
+
</div>
|
|
211
|
+
) : null))
|
|
212
|
+
|
|
213
|
+
return (
|
|
214
|
+
<div className={cn(
|
|
215
|
+
'px-4 md:px-6 pt-3 pb-4 flex flex-col gap-1 items-center',
|
|
216
|
+
r.current?.single?.scrollable ? SCROLL.scrollHeightClx : 'h-auto',
|
|
217
|
+
clx,
|
|
218
|
+
)}>
|
|
219
|
+
{r.current?.single ? (
|
|
220
|
+
<SingleFamilySelector
|
|
221
|
+
{...r.current.single}
|
|
222
|
+
mediaConstraint={MEDIA_CONSTRAINT}
|
|
223
|
+
mobile={mobile}
|
|
224
|
+
clx={selectorClx}
|
|
225
|
+
/>
|
|
226
|
+
) : (r.current?.multi && r.current.families && /* safegaurd for first render, etc. */ (
|
|
227
|
+
<MultiFamilyUI
|
|
228
|
+
{...r.current.multi}
|
|
229
|
+
families={r.current.families}
|
|
230
|
+
parent={r.current.node}
|
|
231
|
+
clx={selectorClx}
|
|
232
|
+
/>
|
|
233
|
+
))}
|
|
234
|
+
<Buttons clx={cn(
|
|
235
|
+
'self-stretch mt-8 flex flex-col items-center gap-3 shrink-0 grow-0',
|
|
236
|
+
buttonsAreaClx
|
|
237
|
+
)}/>
|
|
238
|
+
</div >
|
|
239
|
+
)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export default CarouselBuyCard
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
|
3
|
+
import { reaction } from 'mobx'
|
|
4
|
+
import { observer } from 'mobx-react-lite'
|
|
5
|
+
|
|
6
|
+
import { cn } from '@libreapps/ui/util'
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
ApplyTypography,
|
|
10
|
+
type CarouselApi,
|
|
11
|
+
Carousel,
|
|
12
|
+
CarouselContent,
|
|
13
|
+
CarouselItem,
|
|
14
|
+
CarouselPrevious,
|
|
15
|
+
CarouselNext,
|
|
16
|
+
MediaStack,
|
|
17
|
+
} from '@libreapps/ui/primitives'
|
|
18
|
+
|
|
19
|
+
import type { MultiFamilySelectorProps, LineItem } from '../../../types'
|
|
20
|
+
import {
|
|
21
|
+
formatCurrencyValue,
|
|
22
|
+
accessItemOptions,
|
|
23
|
+
accessMultiSelectorOptions
|
|
24
|
+
} from '../../../util'
|
|
25
|
+
|
|
26
|
+
import QuantityIndicator from '../../item-selector/quantity-indicator'
|
|
27
|
+
import { ButtonItemSelector, useCommerce } from '../../..'
|
|
28
|
+
|
|
29
|
+
const debugBorder = (c: 'r' | 'g' | 'b', disable: boolean = true): string => {
|
|
30
|
+
|
|
31
|
+
if (disable) {
|
|
32
|
+
return ''
|
|
33
|
+
}
|
|
34
|
+
switch (c) {
|
|
35
|
+
case 'r': return ' border border-[#ffaaaa] '
|
|
36
|
+
case 'g': return ' border border-[#aaffaa] '
|
|
37
|
+
case 'b': return ' border border-[#aaaaff] '
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const DEFAULT_CONSTRAINT = {w: 250, h: 250}
|
|
42
|
+
|
|
43
|
+
const AllVariantsCarousel: React.FC<MultiFamilySelectorProps> = ({
|
|
44
|
+
families,
|
|
45
|
+
parent,
|
|
46
|
+
clx='',
|
|
47
|
+
itemClx='',
|
|
48
|
+
itemOptions,
|
|
49
|
+
selectorOptions,
|
|
50
|
+
mediaConstraint=DEFAULT_CONSTRAINT,
|
|
51
|
+
mobile=false, // not relavant to any children
|
|
52
|
+
}) => {
|
|
53
|
+
|
|
54
|
+
const cmmc = useCommerce()
|
|
55
|
+
const r = useRef<{
|
|
56
|
+
api: CarouselApi | undefined
|
|
57
|
+
items: LineItem[]
|
|
58
|
+
dontRespond: boolean
|
|
59
|
+
initialIndex: number
|
|
60
|
+
} | undefined>(undefined)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
const [changeMeToRerender, setChangeMeToRerender] = useState<boolean>(false)
|
|
64
|
+
// Safe, since Carousel is only render (and this method passed), once the ref is initialized.
|
|
65
|
+
const setApi = (api: CarouselApi) => { r.current!.api = api }
|
|
66
|
+
|
|
67
|
+
const onSelect = useCallback((emblaApi: CarouselApi) => {
|
|
68
|
+
if (r.current?.dontRespond) {
|
|
69
|
+
r.current.dontRespond = false
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
const index = emblaApi.selectedScrollSnap()
|
|
73
|
+
if (index !== -1) {
|
|
74
|
+
const item = r.current?.items[index]
|
|
75
|
+
cmmc.setCurrentItem(item?.sku) // (sets currentFamily as well)
|
|
76
|
+
}
|
|
77
|
+
if (r.current) {
|
|
78
|
+
r.current.dontRespond = false
|
|
79
|
+
}
|
|
80
|
+
}, [])
|
|
81
|
+
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
|
|
84
|
+
const items = families.map((fam) => (fam.products as LineItem[])).flat()
|
|
85
|
+
const foundIndex = cmmc.currentItem ? items.findIndex((item) => (cmmc.currentItem!.sku == item.sku)) : -1
|
|
86
|
+
r.current = {
|
|
87
|
+
items,
|
|
88
|
+
initialIndex: foundIndex === -1 ? 0 : foundIndex,
|
|
89
|
+
dontRespond: false,
|
|
90
|
+
api: undefined
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
setChangeMeToRerender(!changeMeToRerender)
|
|
94
|
+
|
|
95
|
+
// This responds to the swatch clicks
|
|
96
|
+
return reaction(
|
|
97
|
+
() => (cmmc.currentItem),
|
|
98
|
+
(item) => {
|
|
99
|
+
if (r.current && r.current.api) {
|
|
100
|
+
const index = r.current.items.findIndex((_item: LineItem) => (_item.sku === item?.sku))
|
|
101
|
+
if (index && index !== -1) {
|
|
102
|
+
// no need to sync family, since ui only allows selecting within a family
|
|
103
|
+
r.current.dontRespond = true
|
|
104
|
+
r.current.api.scrollTo(index)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
}, [])
|
|
110
|
+
|
|
111
|
+
const Header: React.FC<{clx?: string}> = observer(({
|
|
112
|
+
clx=''
|
|
113
|
+
}) => {
|
|
114
|
+
|
|
115
|
+
const { showParentTitle, parentByline: parentBylineMode } = accessMultiSelectorOptions(selectorOptions)
|
|
116
|
+
const { familyTitle, showFamilyByline } = accessItemOptions(itemOptions)
|
|
117
|
+
|
|
118
|
+
const parentTitleDisplay = showParentTitle ? parent.label : undefined
|
|
119
|
+
const parentBylineDisplay = (parentBylineMode === 'own-line') ? parent.subNodesLabel : ''
|
|
120
|
+
|
|
121
|
+
const title = (familyTitle === 'none' ?
|
|
122
|
+
undefined
|
|
123
|
+
:
|
|
124
|
+
(familyTitle === 'long' ?
|
|
125
|
+
cmmc.currentFamily?.title
|
|
126
|
+
:
|
|
127
|
+
(cmmc.currentFamily?.titleShort ?? cmmc.currentFamily?.title)
|
|
128
|
+
)
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
let titleLinePrefix = (parentBylineMode === 'none' || parentBylineMode === 'own-line') ? '' : (parent.subNodesLabel ?? '')
|
|
132
|
+
if (titleLinePrefix.length > 0 && title && parentBylineMode === 'comma-sep') {
|
|
133
|
+
titleLinePrefix += ', '
|
|
134
|
+
}
|
|
135
|
+
else if (titleLinePrefix.length > 0 && title && parentBylineMode === 'colon-sep') {
|
|
136
|
+
titleLinePrefix += ': '
|
|
137
|
+
}
|
|
138
|
+
const titleDisplay = title ? (titleLinePrefix + title) : undefined
|
|
139
|
+
const bylineDisplay = (familyTitle !== 'none') && showFamilyByline ? cmmc.currentFamily?.byline : undefined
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<ApplyTypography className={cn('flex flex-col items-center !gap-0 [&>*]:!m-0 ', clx)} >
|
|
143
|
+
{parentTitleDisplay && <h4>{parentTitleDisplay}</h4>}
|
|
144
|
+
{parentBylineDisplay && <h4>{parentBylineDisplay}</h4>}
|
|
145
|
+
{titleDisplay && <h4>{titleDisplay}</h4>}
|
|
146
|
+
{bylineDisplay && (<h6 >{bylineDisplay}</h6>)}
|
|
147
|
+
</ApplyTypography>
|
|
148
|
+
)
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
const ItemInfo: React.FC<{
|
|
152
|
+
clx?: string
|
|
153
|
+
labelClx?: string
|
|
154
|
+
}> = observer(({
|
|
155
|
+
clx='',
|
|
156
|
+
labelClx=''
|
|
157
|
+
}) => {
|
|
158
|
+
|
|
159
|
+
const {
|
|
160
|
+
showPrice,
|
|
161
|
+
showQuantity,
|
|
162
|
+
showFamilyInOption,
|
|
163
|
+
showByline,
|
|
164
|
+
} = accessItemOptions(itemOptions)
|
|
165
|
+
|
|
166
|
+
const optionLabel = () => (
|
|
167
|
+
showFamilyInOption ?
|
|
168
|
+
(cmmc.currentItem!.familyTitle + ', ' + cmmc.currentItem!.optionLabel)
|
|
169
|
+
:
|
|
170
|
+
cmmc.currentItem!.optionLabel
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
return (cmmc.currentItem && (
|
|
174
|
+
<ApplyTypography className={cn('flex flex-col items-center [&>*]:!m-0 !gap-1 ', clx)}>
|
|
175
|
+
<div className={
|
|
176
|
+
'flex items-center gap-1 [&>*]:!m-0 ' +
|
|
177
|
+
debugBorder('g') +
|
|
178
|
+
(showFamilyInOption ? 'flex-col' : 'flex-row')
|
|
179
|
+
}>
|
|
180
|
+
<h6 className={cn('font-semibold', labelClx)}>
|
|
181
|
+
{optionLabel() + (showPrice && !showFamilyInOption ? ',' : '')}
|
|
182
|
+
</h6>
|
|
183
|
+
<div className={
|
|
184
|
+
'flex items-center gap-1 [&>*]:!m-0 flex-row ' + debugBorder('b') +
|
|
185
|
+
(showFamilyInOption ? 'w-full justify-between' : '')
|
|
186
|
+
}>
|
|
187
|
+
{showPrice && (<p>{formatCurrencyValue(cmmc.currentItem.price)}</p>)}
|
|
188
|
+
{showQuantity && (
|
|
189
|
+
<QuantityIndicator
|
|
190
|
+
item={cmmc.currentItem}
|
|
191
|
+
clx='h-[22px] ml-4'
|
|
192
|
+
iconClx='fill-foreground'
|
|
193
|
+
digitClx='not-typography font-semibold text-primary-fg leading-none font-sans text-xs'
|
|
194
|
+
/>
|
|
195
|
+
)}
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
{showByline && cmmc.currentItem.byline && (<p>{cmmc.currentItem.byline}</p>)}
|
|
199
|
+
</ApplyTypography>
|
|
200
|
+
))
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
const Swatches: React.FC<{
|
|
204
|
+
clx?: string
|
|
205
|
+
}> = observer(({
|
|
206
|
+
clx=''
|
|
207
|
+
}) => {
|
|
208
|
+
const { showItemSwatches } = accessMultiSelectorOptions(selectorOptions)
|
|
209
|
+
if (
|
|
210
|
+
!showItemSwatches ||
|
|
211
|
+
!cmmc.currentFamily
|
|
212
|
+
//|| cmmc.currentFamily.products.length === 1
|
|
213
|
+
) {
|
|
214
|
+
return null
|
|
215
|
+
}
|
|
216
|
+
return <ButtonItemSelector
|
|
217
|
+
items={cmmc.currentFamily.products as LineItem[]}
|
|
218
|
+
selectedItemRef={cmmc}
|
|
219
|
+
selectSku={cmmc.setCurrentItem.bind(cmmc)}
|
|
220
|
+
clx={clx}
|
|
221
|
+
options={{
|
|
222
|
+
buttonType: 'image',
|
|
223
|
+
horizButtons: true
|
|
224
|
+
}}
|
|
225
|
+
/>
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
return (
|
|
229
|
+
<div className={cn('w-full flex flex-col items-center', clx)}>
|
|
230
|
+
<Header />
|
|
231
|
+
{r.current && /* Only render once we've set the ref fields, or else bad things! */ (
|
|
232
|
+
<Carousel
|
|
233
|
+
options={{loop: true, startIndex: r.current.initialIndex}}
|
|
234
|
+
className={'w-full px-2' + debugBorder('r')}
|
|
235
|
+
onCarouselSelect={onSelect}
|
|
236
|
+
setApi={setApi}
|
|
237
|
+
>
|
|
238
|
+
<CarouselContent>
|
|
239
|
+
{r.current.items.map((item) => (
|
|
240
|
+
<CarouselItem key={item.sku} className={cn('p-2 flex flex-col justify-center items-center', itemClx)}>
|
|
241
|
+
<MediaStack media={item} constrainTo={mediaConstraint} clx='my-4'/>
|
|
242
|
+
</CarouselItem>
|
|
243
|
+
))}
|
|
244
|
+
</CarouselContent>
|
|
245
|
+
{r.current.items.length > 1 && (<>
|
|
246
|
+
<CarouselPrevious className='left-1'/>
|
|
247
|
+
<CarouselNext className='right-1'/>
|
|
248
|
+
</>)}
|
|
249
|
+
</Carousel>
|
|
250
|
+
)}
|
|
251
|
+
<div className='flex flex-col items-center justify-start'>
|
|
252
|
+
<ItemInfo labelClx='!text-base font-medium'/>
|
|
253
|
+
<Swatches clx='mt-2'/>
|
|
254
|
+
</div>
|
|
255
|
+
</div>
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export {
|
|
260
|
+
AllVariantsCarousel as default
|
|
261
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import React, { useCallback, useRef } from 'react'
|
|
3
|
+
|
|
4
|
+
import { cn } from '@libreapps/ui/util'
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
type CarouselApi,
|
|
8
|
+
Carousel,
|
|
9
|
+
CarouselContent,
|
|
10
|
+
CarouselItem,
|
|
11
|
+
CarouselPrevious,
|
|
12
|
+
CarouselNext,
|
|
13
|
+
} from '@libreapps/ui/primitives'
|
|
14
|
+
|
|
15
|
+
import type { MultiFamilySelectorProps } from '../../../../types'
|
|
16
|
+
|
|
17
|
+
import FamilySlide from './slide'
|
|
18
|
+
import { FamilyCarouselState } from './state'
|
|
19
|
+
|
|
20
|
+
import { useCommerce } from '../../../..'
|
|
21
|
+
|
|
22
|
+
const FamilyCarousel: React.FC<MultiFamilySelectorProps> = ({
|
|
23
|
+
families,
|
|
24
|
+
clx='',
|
|
25
|
+
itemClx='',
|
|
26
|
+
itemOptions,
|
|
27
|
+
selectorOptions,
|
|
28
|
+
mediaConstraint={w: 250, h: 250},
|
|
29
|
+
mobile=false,
|
|
30
|
+
}) => {
|
|
31
|
+
|
|
32
|
+
const cmmc = useCommerce()
|
|
33
|
+
const stateRef = useRef<FamilyCarouselState>(
|
|
34
|
+
new FamilyCarouselState(families, cmmc.setCurrentItem.bind(cmmc))
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
const onSelect = useCallback((emblaApi: CarouselApi) => {
|
|
38
|
+
const index = emblaApi.selectedScrollSnap()
|
|
39
|
+
if (index !== -1) {
|
|
40
|
+
stateRef.current.setCurrentFamily(families[index].id)
|
|
41
|
+
}
|
|
42
|
+
}, [stateRef.current])
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<Carousel
|
|
46
|
+
className={cn('px-2', clx)}
|
|
47
|
+
options={{loop: false}}
|
|
48
|
+
onCarouselSelect={onSelect}
|
|
49
|
+
>
|
|
50
|
+
<CarouselContent>
|
|
51
|
+
{families.map((family) => {
|
|
52
|
+
const slideState = stateRef.current.getSlideState(family.id)
|
|
53
|
+
if (!slideState) {
|
|
54
|
+
throw new Error(`FamilyCarousel: no SlideState for family '${family.id}'!`)
|
|
55
|
+
}
|
|
56
|
+
return (
|
|
57
|
+
<CarouselItem key={family.id} className='p-2 flex flex-col justify-center items-center'>
|
|
58
|
+
<FamilySlide
|
|
59
|
+
family={family}
|
|
60
|
+
selectedItemRef={slideState}
|
|
61
|
+
selectSku={slideState.selectSku.bind(slideState)}
|
|
62
|
+
mediaConstraint={mediaConstraint}
|
|
63
|
+
options={itemOptions}
|
|
64
|
+
mobile={mobile}
|
|
65
|
+
clx={itemClx}
|
|
66
|
+
/>
|
|
67
|
+
</CarouselItem>
|
|
68
|
+
)}
|
|
69
|
+
)}
|
|
70
|
+
</CarouselContent>
|
|
71
|
+
<CarouselPrevious className='left-1'/>
|
|
72
|
+
<CarouselNext className='right-1'/>
|
|
73
|
+
</Carousel>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export default FamilyCarousel
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
ApplyTypography,
|
|
6
|
+
MediaStack
|
|
7
|
+
} from '@libreapps/ui/primitives'
|
|
8
|
+
import type { Dimensions } from '@libreapps/ui/types'
|
|
9
|
+
|
|
10
|
+
import type { ItemSelectorOptions, Family, LineItem, ItemSelector } from '../../../../types'
|
|
11
|
+
import { ButtonItemSelector } from '../../..'
|
|
12
|
+
import { cn } from '@libreapps/ui/util'
|
|
13
|
+
|
|
14
|
+
const FamilySlide: React.FC<Omit<ItemSelector, 'items'> & {
|
|
15
|
+
family: Family
|
|
16
|
+
mediaConstraint: Dimensions
|
|
17
|
+
clx?: string
|
|
18
|
+
mobile?: boolean,
|
|
19
|
+
options?: ItemSelectorOptions
|
|
20
|
+
}> = ({
|
|
21
|
+
family,
|
|
22
|
+
selectedItemRef,
|
|
23
|
+
selectSku,
|
|
24
|
+
mediaConstraint: cnst = {w: 200, h: 200},
|
|
25
|
+
clx='',
|
|
26
|
+
mobile=false,
|
|
27
|
+
options={}
|
|
28
|
+
}) => {
|
|
29
|
+
|
|
30
|
+
const titleSpec = 'title' in options ? options.title : 'short'
|
|
31
|
+
const showByline = 'showByline' in options ? options.showByline : true
|
|
32
|
+
const title = titleSpec === 'long' ?
|
|
33
|
+
family.title
|
|
34
|
+
:
|
|
35
|
+
(
|
|
36
|
+
titleSpec === 'short' ?
|
|
37
|
+
(family.titleShort ? family.titleShort : family.title )
|
|
38
|
+
:
|
|
39
|
+
undefined
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
const byline = (showByline && family.byline) ? family.byline : undefined
|
|
43
|
+
/*
|
|
44
|
+
let byline: string | undefined = undefined
|
|
45
|
+
if (showByline) {
|
|
46
|
+
if (family.byline) {
|
|
47
|
+
// if byline names another field thats a function, call it
|
|
48
|
+
if (family.byline in family && typeof (family as any)[family.byline] === 'function') {
|
|
49
|
+
byline = (family as any)[family.byline]() as string
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
byline = family.byline
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<div className={cn('flex flex-col items-center gap-4', clx)} data-vaul-no-drag >
|
|
60
|
+
{title && (
|
|
61
|
+
<ApplyTypography className='flex flex-col items-center !gap-1 [&>*]:!m-0' data-vaul-no-drag >
|
|
62
|
+
<h6 className='font-bold text-lg'>{title}</h6>
|
|
63
|
+
{byline && (<p className={''}>{byline}</p>)}
|
|
64
|
+
</ApplyTypography>
|
|
65
|
+
)}
|
|
66
|
+
<MediaStack
|
|
67
|
+
media={selectedItemRef.item!}
|
|
68
|
+
constrainTo={cnst}
|
|
69
|
+
/>
|
|
70
|
+
<ButtonItemSelector
|
|
71
|
+
items={family.products as LineItem[]}
|
|
72
|
+
selectedItemRef={selectedItemRef}
|
|
73
|
+
selectSku={selectSku}
|
|
74
|
+
scrollable={false}
|
|
75
|
+
mobile={mobile}
|
|
76
|
+
options={options}
|
|
77
|
+
itemClx='text-sm'
|
|
78
|
+
/>
|
|
79
|
+
</div>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export default FamilySlide
|