@hanzo/commerce 7.7.1 → 7.7.3
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/dist/components/cart/cart-panel/promo-code.d.ts +2 -2
- package/dist/service/impls/standalone/index.js +4 -4
- package/dist/service/impls/standalone/index.js.map +1 -1
- package/package.json +46 -17
- package/billing.ts +0 -2
- package/client.ts +0 -783
- package/components/Icons.tsx +0 -35
- package/components/add-to-cart-widget.tsx +0 -195
- package/components/buy/buy-card.tsx +0 -269
- package/components/buy/carousel-buy-card.tsx +0 -243
- package/components/buy/multi-family/all-variants-carousel.tsx +0 -259
- package/components/buy/multi-family/family-carousel/index.tsx +0 -75
- package/components/buy/multi-family/family-carousel/slide.tsx +0 -82
- package/components/buy/multi-family/family-carousel/state.ts +0 -87
- package/components/buy/multi-family/index.ts +0 -2
- package/components/buy/single-family-selector.tsx +0 -91
- package/components/buy/title-and-byline.tsx +0 -26
- package/components/cart/cart-panel/cart-line-item.tsx +0 -77
- package/components/cart/cart-panel/index.tsx +0 -157
- package/components/cart/cart-panel/promo-code.tsx +0 -105
- package/components/cart/cart-panel/total-area.tsx +0 -61
- package/components/checkout/payment-step-form/card-icon-row.tsx +0 -26
- package/components/checkout/payment-step-form/card-icons/amex.tsx +0 -32
- package/components/checkout/payment-step-form/card-icons/diners-club.tsx +0 -13
- package/components/checkout/payment-step-form/card-icons/discover.tsx +0 -25
- package/components/checkout/payment-step-form/card-icons/jcb.tsx +0 -26
- package/components/checkout/payment-step-form/card-icons/mastercard.tsx +0 -27
- package/components/checkout/payment-step-form/card-icons/visa.tsx +0 -25
- package/components/checkout/payment-step-form/cc-button.tsx +0 -18
- package/components/checkout/payment-step-form/contact-form.tsx +0 -48
- package/components/checkout/payment-step-form/crypto-icons/btc.tsx +0 -11
- package/components/checkout/payment-step-form/crypto-icons/eth.tsx +0 -20
- package/components/checkout/payment-step-form/crypto-icons/usdt.tsx +0 -13
- package/components/checkout/payment-step-form/index.tsx +0 -113
- package/components/checkout/payment-step-form/methods/bank-transfer.tsx +0 -81
- package/components/checkout/payment-step-form/methods/card.tsx +0 -253
- package/components/checkout/payment-step-form/methods/crypto.tsx +0 -226
- package/components/checkout/payment-step-form/methods/index.ts +0 -23
- package/components/checkout/shipping-step-form.tsx +0 -171
- package/components/index.ts +0 -11
- package/components/item/product-card.tsx +0 -66
- package/components/item-selector/button.tsx +0 -213
- package/components/item-selector/carousel/index.tsx +0 -196
- package/components/item-selector/carousel/slider.tsx +0 -37
- package/components/item-selector/index.ts +0 -5
- package/components/item-selector/quantity-indicator.tsx +0 -50
- package/components/node-tabs/index.tsx +0 -90
- package/components/node-tabs/node-image.tsx +0 -31
- package/index.ts +0 -55
- package/metering.test.ts +0 -199
- package/metering.ts +0 -289
- package/product-card.test.tsx +0 -104
- package/service/context.tsx +0 -45
- package/service/debug.ts +0 -41
- package/service/impls/standalone/actual-line-item.ts +0 -136
- package/service/impls/standalone/get-instance.ts +0 -64
- package/service/impls/standalone/index.ts +0 -579
- package/service/impls/standalone/order/firebase.ts +0 -5
- package/service/impls/standalone/order/index.ts +0 -58
- package/service/impls/standalone/persistence.ts +0 -33
- package/service/path-utils.ts +0 -26
- package/service/sep.ts +0 -7
- package/tsconfig.json +0 -17
- package/types/README.md +0 -2
- package/types/category-node.ts +0 -50
- package/types/checkout.ts +0 -47
- package/types/commerce-config.ts +0 -13
- package/types/commerce-service.ts +0 -128
- package/types/family.ts +0 -26
- package/types/index.ts +0 -15
- package/types/item-selector.ts +0 -97
- package/types/line-item.ts +0 -29
- package/types/multi-family-selector-props.ts +0 -20
- package/types/product.ts +0 -21
- package/types/promo.ts +0 -10
- package/types/selection-ui-specifier.ts +0 -52
- package/types/string-mutator.ts +0 -14
- package/types/token-separators.ts +0 -7
- package/util/analytics.ts +0 -21
- package/util/countries.ts +0 -196
- package/util/error.ts +0 -34
- package/util/index.ts +0 -71
- package/util/item-selector-options-accessor.ts +0 -35
- package/util/line-item-ref.ts +0 -23
- package/util/multi-family-selector-options-accessor.ts +0 -15
- package/util/obs-string-mutator.ts +0 -22
- package/util/product-media-accessor.ts +0 -58
- package/util/promo-codes.ts +0 -106
- package/util/selection-ui-specifiers.ts +0 -30
- package/util/square-payment.ts +0 -50
- package/util/use-sync-sku-param-w-current-item.ts +0 -88
package/components/Icons.tsx
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ShoppingCart,
|
|
3
|
-
Menu,
|
|
4
|
-
ChevronLeft,
|
|
5
|
-
ChevronRight,
|
|
6
|
-
Loader2,
|
|
7
|
-
Check,
|
|
8
|
-
Plus,
|
|
9
|
-
Minus,
|
|
10
|
-
ChevronDown,
|
|
11
|
-
ChevronsLeft,
|
|
12
|
-
ChevronsRight,
|
|
13
|
-
Image,
|
|
14
|
-
Trash2,
|
|
15
|
-
Search,
|
|
16
|
-
Barcode
|
|
17
|
-
} from "lucide-react"
|
|
18
|
-
|
|
19
|
-
export default {
|
|
20
|
-
shoppingCart: ShoppingCart,
|
|
21
|
-
menu: Menu,
|
|
22
|
-
chevronLeft: ChevronLeft,
|
|
23
|
-
chevronRight: ChevronRight,
|
|
24
|
-
spinner: Loader2,
|
|
25
|
-
check: Check,
|
|
26
|
-
plus: Plus,
|
|
27
|
-
minus: Minus,
|
|
28
|
-
chevronDown: ChevronDown,
|
|
29
|
-
chevronsLeft: ChevronsLeft,
|
|
30
|
-
chevronsRight: ChevronsRight,
|
|
31
|
-
placeholder: Image,
|
|
32
|
-
trash: Trash2,
|
|
33
|
-
search: Search,
|
|
34
|
-
barcode: Barcode
|
|
35
|
-
}
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
import React, { useEffect, useRef } from 'react'
|
|
3
|
-
import { reaction, type IReactionDisposer } from 'mobx'
|
|
4
|
-
import { observer } from 'mobx-react-lite'
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
Button,
|
|
8
|
-
buttonVariants,
|
|
9
|
-
cn,
|
|
10
|
-
sx,
|
|
11
|
-
} from '@hanzo/ui'
|
|
12
|
-
import Icons from './Icons'
|
|
13
|
-
import type { LineItem } from '../types'
|
|
14
|
-
import { sendFBEvent, sendGAEvent } from '../util/analytics'
|
|
15
|
-
|
|
16
|
-
const AddToCartWidget: React.FC<{
|
|
17
|
-
item: LineItem
|
|
18
|
-
disabled?: boolean
|
|
19
|
-
className?: string
|
|
20
|
-
buttonClx?: string
|
|
21
|
-
variant?: 'minimal' | 'primary' | 'primary-smaller' | 'outline'
|
|
22
|
-
onQuantityChanged?: (sku: string, oldV: number, newV: number) => void
|
|
23
|
-
}> = observer(({
|
|
24
|
-
item,
|
|
25
|
-
variant='primary',
|
|
26
|
-
disabled=false,
|
|
27
|
-
className='',
|
|
28
|
-
buttonClx='',
|
|
29
|
-
onQuantityChanged
|
|
30
|
-
}) => {
|
|
31
|
-
|
|
32
|
-
const reactionDisposer = useRef<IReactionDisposer | undefined>(undefined)
|
|
33
|
-
useEffect(() => {
|
|
34
|
-
if (onQuantityChanged) {
|
|
35
|
-
reactionDisposer.current = reaction(
|
|
36
|
-
() => (item.quantity),
|
|
37
|
-
(quantity: number, previous: number) => {
|
|
38
|
-
onQuantityChanged(item.sku, quantity, previous)
|
|
39
|
-
}
|
|
40
|
-
)
|
|
41
|
-
}
|
|
42
|
-
return () => {
|
|
43
|
-
if (reactionDisposer.current) {
|
|
44
|
-
reactionDisposer.current()
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}, [])
|
|
48
|
-
|
|
49
|
-
// `rounded-lg` written out, not interpolated. The note that used to sit here
|
|
50
|
-
// said safelisting was unnecessary "since its used widely" — which is true
|
|
51
|
-
// only for as long as some OTHER file in whatever the consumer scans spells it
|
|
52
|
-
// literally. Tailwind reads source text; a template literal is not a class
|
|
53
|
-
// name, so this one was borrowing a rule it did not ask for.
|
|
54
|
-
const ROUNDED_CLX = ' rounded-lg ' // the class
|
|
55
|
-
|
|
56
|
-
const ghost = variant === 'minimal'
|
|
57
|
-
const outline = variant === 'outline'
|
|
58
|
-
const primary = variant === 'primary'
|
|
59
|
-
const priSmaller = variant === 'primary-smaller'
|
|
60
|
-
|
|
61
|
-
let iconClx = ghost ? 'h-4 w-4 md:h-3 md:w-3 text-muted-3 ' : (priSmaller ? 'h-4 w-6 px-0.5 opacity-70' : 'h-5 w-7 px-1 opacity-70 ')
|
|
62
|
-
iconClx += ghost ? 'group-hover:text-foreground' : 'group-hover:opacity-100'
|
|
63
|
-
|
|
64
|
-
let digitClx = ghost ? 'px-2 md:px-0.5 ' : 'sm:px-2 font-semibold '
|
|
65
|
-
digitClx += (ghost || outline) ? 'text-foreground ' : 'text-primary-fg '
|
|
66
|
-
|
|
67
|
-
if (disabled) {
|
|
68
|
-
return (
|
|
69
|
-
<div className={cn('flex flex-row items-stretch' + ROUNDED_CLX + (!(primary || priSmaller) ? 'bg-transparent' : 'bg-primary'), className)}>
|
|
70
|
-
<div className={'text-sm flex items-center cursor-default ' + digitClx} >{item.quantity}</div>
|
|
71
|
-
</div>
|
|
72
|
-
)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const inc = (event: React.MouseEvent<HTMLElement>) => {
|
|
76
|
-
item.increment()
|
|
77
|
-
event.stopPropagation() // in case we're part of a larger selection UI
|
|
78
|
-
sendGAEvent('add_to_cart', {
|
|
79
|
-
items: [{
|
|
80
|
-
item_id: item.sku,
|
|
81
|
-
item_name: item.title,
|
|
82
|
-
item_category: item.familyId,
|
|
83
|
-
price: item.price,
|
|
84
|
-
quantity: item.quantity
|
|
85
|
-
}],
|
|
86
|
-
value: item.price,
|
|
87
|
-
currency: 'USD',
|
|
88
|
-
})
|
|
89
|
-
sendFBEvent('AddToCart', {
|
|
90
|
-
content_ids: [item.sku],
|
|
91
|
-
contents: [{
|
|
92
|
-
id: item.sku,
|
|
93
|
-
quantity: item.quantity
|
|
94
|
-
}],
|
|
95
|
-
content_name: item.title,
|
|
96
|
-
value: item.price,
|
|
97
|
-
currency: 'USD',
|
|
98
|
-
})
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const dec = (event: React.MouseEvent<HTMLElement>) => {
|
|
102
|
-
item.decrement()
|
|
103
|
-
event.stopPropagation() // in case we're part of a larger selection UI
|
|
104
|
-
sendGAEvent('remove_from_cart', {
|
|
105
|
-
items: [{
|
|
106
|
-
item_id: item.sku,
|
|
107
|
-
item_name: item.title,
|
|
108
|
-
item_category: item.familyId,
|
|
109
|
-
price: item.price,
|
|
110
|
-
quantity: item.quantity
|
|
111
|
-
}],
|
|
112
|
-
value: item.price,
|
|
113
|
-
currency: 'USD',
|
|
114
|
-
})
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return ( item.isInCart ? (
|
|
118
|
-
<div className={cn(
|
|
119
|
-
'flex flex-row items-stretch justify-between ',
|
|
120
|
-
// should match 'sm' and 'default' button heights
|
|
121
|
-
(ghost || priSmaller ? 'h-8' : 'h-9'),
|
|
122
|
-
ROUNDED_CLX,
|
|
123
|
-
(primary || priSmaller ? 'bg-primary' : 'bg-transparent'),
|
|
124
|
-
(outline ? 'border border-muted' : ''),
|
|
125
|
-
className
|
|
126
|
-
)}>
|
|
127
|
-
<Button
|
|
128
|
-
aria-label={'Remove a ' + item.title + ' from the cart'}
|
|
129
|
-
size={ghost || priSmaller ? 'sm' : 'default'}
|
|
130
|
-
variant={primary || priSmaller ? 'primary' : 'ghost'}
|
|
131
|
-
{...sx(ghost ? 'rounded-full' : 'rounded-lg')}
|
|
132
|
-
className={cn(
|
|
133
|
-
'lg:min-w-0 lg:px-2 grow justify-start group',
|
|
134
|
-
(ghost || priSmaller ? 'px-1' : 'px-2'),
|
|
135
|
-
(outline ? 'hover:bg-transparent' : ''),
|
|
136
|
-
buttonClx,
|
|
137
|
-
'h-auto self-stretch' // must be smaller than normal
|
|
138
|
-
)}
|
|
139
|
-
key='left'
|
|
140
|
-
onClick={dec}
|
|
141
|
-
>
|
|
142
|
-
{(item.quantity > 1) ? (
|
|
143
|
-
<Icons.minus className={iconClx} aria-hidden='true'/>
|
|
144
|
-
) : (
|
|
145
|
-
<Icons.trash className={iconClx} aria-hidden='true'/>
|
|
146
|
-
)}
|
|
147
|
-
</Button>
|
|
148
|
-
<div className={cn(
|
|
149
|
-
'grow-0 shrink-0 flex items-center cursor-default xs:px-2',
|
|
150
|
-
priSmaller || ghost ? 'text-xs' : 'text-sm',
|
|
151
|
-
digitClx
|
|
152
|
-
)}>
|
|
153
|
-
{item.quantity}{ghost ? '' : ' in Bag'}
|
|
154
|
-
</div>
|
|
155
|
-
<Button
|
|
156
|
-
aria-label={'Add another ' + item.title + ' to the cart'}
|
|
157
|
-
itemProp='potentialAction'
|
|
158
|
-
itemScope
|
|
159
|
-
itemType='https://schema.org/AddAction'
|
|
160
|
-
size={ghost || priSmaller ? 'sm' : 'default'}
|
|
161
|
-
|
|
162
|
-
variant={primary || priSmaller ? 'primary' : 'ghost'}
|
|
163
|
-
{...sx(ghost ? 'rounded-full' : 'rounded-lg')}
|
|
164
|
-
className={cn(
|
|
165
|
-
'lg:min-w-0 lg:px-2 grow justify-end group',
|
|
166
|
-
(ghost || priSmaller ? 'px-1' : 'px-2'),
|
|
167
|
-
(outline ? 'hover:bg-transparent' : ''),
|
|
168
|
-
buttonClx,
|
|
169
|
-
'h-auto self-stretch' // must be smaller than normal
|
|
170
|
-
)}
|
|
171
|
-
onClick={inc}
|
|
172
|
-
key='right'
|
|
173
|
-
>
|
|
174
|
-
<Icons.plus className={iconClx} aria-hidden='true'/>
|
|
175
|
-
</Button>
|
|
176
|
-
</div>
|
|
177
|
-
) : (
|
|
178
|
-
<Button
|
|
179
|
-
aria-label={'Add a ' + item.title + ' to cart'}
|
|
180
|
-
itemProp='potentialAction'
|
|
181
|
-
itemScope
|
|
182
|
-
itemType='https://schema.org/AddAction'
|
|
183
|
-
size={ghost || priSmaller ? 'sm' : 'default'}
|
|
184
|
-
variant={ghost ? 'ghost' : (primary || priSmaller ? 'primary' : 'outline')}
|
|
185
|
-
{...sx('rounded-lg')}
|
|
186
|
-
className={cn(buttonClx, className)}
|
|
187
|
-
onClick={inc}
|
|
188
|
-
>
|
|
189
|
-
<span className='m1-1'>Add</span>
|
|
190
|
-
<Icons.plus className='h-5 w-5 ml-1' aria-hidden='true'/>
|
|
191
|
-
</Button>
|
|
192
|
-
))
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
export default AddToCartWidget
|
|
@@ -1,269 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
import React, { useRef, useEffect, type ComponentType } from 'react'
|
|
3
|
-
import { reaction, type IReactionDisposer } from 'mobx'
|
|
4
|
-
import { observer } from 'mobx-react-lite'
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
cn,
|
|
8
|
-
ApplyTypography,
|
|
9
|
-
MediaStack,
|
|
10
|
-
} from '@hanzo/ui'
|
|
11
|
-
import type {
|
|
12
|
-
ItemSelectorProps,
|
|
13
|
-
ItemSelectorCompProps,
|
|
14
|
-
LineItem,
|
|
15
|
-
Family,
|
|
16
|
-
CategoryNode,
|
|
17
|
-
} from '../../types'
|
|
18
|
-
|
|
19
|
-
import { useCommerce } from '../../service/context'
|
|
20
|
-
import * as pathUtils from '../../service/path-utils'
|
|
21
|
-
import { getFacetValuesMutator, ObsStringMutator } from '../../util'
|
|
22
|
-
|
|
23
|
-
import NodeTabs from '../node-tabs'
|
|
24
|
-
import AddToCartWidget from '../add-to-cart-widget'
|
|
25
|
-
|
|
26
|
-
const BuyCard: React.FC<{
|
|
27
|
-
skuPath: string
|
|
28
|
-
|
|
29
|
-
/** For any selectors that show a list of choices,
|
|
30
|
-
* starting with 'scrollAfter' elements,
|
|
31
|
-
* the list will scroll, and the card will
|
|
32
|
-
* have 'scrollHeightClx' applied to it to define and fix the height.
|
|
33
|
-
* This is most like something like '80vh'
|
|
34
|
-
* defaults are '5' and 'h-[70vh]'
|
|
35
|
-
* */
|
|
36
|
-
scrollAfter?: number
|
|
37
|
-
scrollHeightClx?: string
|
|
38
|
-
|
|
39
|
-
clx?: string
|
|
40
|
-
famWidgetClx?: string
|
|
41
|
-
addWidgetClx?: string
|
|
42
|
-
|
|
43
|
-
selector: ComponentType<ItemSelectorProps>
|
|
44
|
-
selectorProps?: ItemSelectorCompProps
|
|
45
|
-
|
|
46
|
-
/** Show all items from all sibling families
|
|
47
|
-
* If skuPath defines a Family, the first of it's items will
|
|
48
|
-
* be selected. The facet widget will just select a the first
|
|
49
|
-
* item in the chosen Family without changings the overall set
|
|
50
|
-
* of choices. */
|
|
51
|
-
allVariants?: boolean
|
|
52
|
-
|
|
53
|
-
familyTabAs?: 'image' | 'label' | 'image-and-label'
|
|
54
|
-
showItemMedia?: boolean
|
|
55
|
-
|
|
56
|
-
onQuantityChanged?: (sku: string, oldV: number, newV: number) => void
|
|
57
|
-
mobile?: boolean
|
|
58
|
-
}> = observer(({
|
|
59
|
-
skuPath,
|
|
60
|
-
scrollAfter=5,
|
|
61
|
-
scrollHeightClx='h-[80vh]',
|
|
62
|
-
clx='',
|
|
63
|
-
famWidgetClx='',
|
|
64
|
-
addWidgetClx='',
|
|
65
|
-
selector: Selector,
|
|
66
|
-
selectorProps={
|
|
67
|
-
clx: '',
|
|
68
|
-
soleItemClx: '',
|
|
69
|
-
itemClx: '',
|
|
70
|
-
ext: {}
|
|
71
|
-
},
|
|
72
|
-
familyTabAs='image-and-label',
|
|
73
|
-
showItemMedia=true,
|
|
74
|
-
allVariants=false,
|
|
75
|
-
mobile=false,
|
|
76
|
-
onQuantityChanged,
|
|
77
|
-
}) => {
|
|
78
|
-
|
|
79
|
-
const cmmc = useCommerce()
|
|
80
|
-
const inst = useRef<{
|
|
81
|
-
level: number,
|
|
82
|
-
requestedFamily : Family | undefined
|
|
83
|
-
requestedNode: CategoryNode | undefined
|
|
84
|
-
currentFamTokenMutator: ObsStringMutator
|
|
85
|
-
disposers: IReactionDisposer[]
|
|
86
|
-
} | undefined>(undefined)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
useEffect(() => {
|
|
90
|
-
|
|
91
|
-
let requestedFamily = cmmc.getFamilyById(skuPath)
|
|
92
|
-
let requestedNode = requestedFamily ? undefined : cmmc.getNodeAtPath(skuPath)
|
|
93
|
-
let initialFamily = undefined
|
|
94
|
-
|
|
95
|
-
const { paths, level } = pathUtils.getSelectedPaths(skuPath)
|
|
96
|
-
|
|
97
|
-
if (level === 0) {
|
|
98
|
-
throw new Error('BuyCard.useEffect(): must specify at least at least one Level in skuPath!')
|
|
99
|
-
}
|
|
100
|
-
if (requestedFamily) {
|
|
101
|
-
if (allVariants && level >= 2) {
|
|
102
|
-
// select all siblings of requestedFamily.
|
|
103
|
-
// ie, go one level back
|
|
104
|
-
delete paths[level]
|
|
105
|
-
initialFamily = requestedFamily
|
|
106
|
-
requestedFamily = undefined
|
|
107
|
-
requestedNode = cmmc.getNodeAtPath(pathUtils.getParentPath(skuPath))
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
else {
|
|
111
|
-
// select siblings paths
|
|
112
|
-
if (allVariants) {
|
|
113
|
-
paths[level + 1] = [...requestedNode!.subNodes!.map((node) => (node.skuToken))]
|
|
114
|
-
}
|
|
115
|
-
// Actually select the the first fam at the fam level,
|
|
116
|
-
else {
|
|
117
|
-
paths[level + 1] = [requestedNode!.subNodes![0].skuToken]
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const selectedFams = cmmc.selectPaths(paths)
|
|
122
|
-
initialFamily = initialFamily ? initialFamily : (requestedFamily ? requestedFamily : selectedFams[0])
|
|
123
|
-
cmmc.setCurrentItem(initialFamily.products[0].sku)
|
|
124
|
-
const currentFamTokenMutator = new ObsStringMutator(pathUtils.lastToken(initialFamily!.id))
|
|
125
|
-
|
|
126
|
-
inst.current = {
|
|
127
|
-
level,
|
|
128
|
-
requestedFamily,
|
|
129
|
-
requestedNode,
|
|
130
|
-
currentFamTokenMutator,
|
|
131
|
-
disposers: []
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (!allVariants) {
|
|
135
|
-
inst.current?.disposers.push(reaction(
|
|
136
|
-
() => {
|
|
137
|
-
const fams = cmmc.selectedFamilies
|
|
138
|
-
return (fams.length > 0) ? fams[0].id : undefined
|
|
139
|
-
},
|
|
140
|
-
(famId) => {
|
|
141
|
-
if (famId && famId !== cmmc.currentItem?.familyId) {
|
|
142
|
-
const fam = cmmc.getFamilyById(famId)
|
|
143
|
-
if (fam) {
|
|
144
|
-
cmmc.setCurrentItem(fam.products[0].sku)
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
))
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return () => {
|
|
152
|
-
inst.current?.disposers.forEach((d) => {d()})
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
}, [])
|
|
156
|
-
|
|
157
|
-
const setFamilyPath = (pathValue: string | null) => {
|
|
158
|
-
if (inst.current?.currentFamTokenMutator.get() !== pathValue) {
|
|
159
|
-
inst.current?.currentFamTokenMutator.set(pathValue)
|
|
160
|
-
}
|
|
161
|
-
const found = cmmc.selectedItems.find((item) => (item.familyId.endsWith(pathValue!)))
|
|
162
|
-
if (found) {
|
|
163
|
-
cmmc.setCurrentItem(found.sku)
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const selectSku = (sku: string) => {
|
|
168
|
-
cmmc.setCurrentItem(sku)
|
|
169
|
-
const pathValue = pathUtils.lastToken(cmmc.currentItem!.familyId)
|
|
170
|
-
if (pathValue !== inst.current?.currentFamTokenMutator.get()) {
|
|
171
|
-
inst.current?.currentFamTokenMutator.set(pathValue)
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const famTitle = inst.current?.requestedFamily ? inst.current.requestedFamily.title : cmmc.selectedFamilies[0].title
|
|
176
|
-
|
|
177
|
-
const itemsToShow = (!cmmc.hasSelection) ? undefined :
|
|
178
|
-
allVariants ? cmmc.selectedItems : cmmc.selectedFamilies[0].products as LineItem[]
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const TitleArea: React.FC<{
|
|
182
|
-
title: string
|
|
183
|
-
byline?: string
|
|
184
|
-
clx?: string
|
|
185
|
-
}> = ({
|
|
186
|
-
title,
|
|
187
|
-
byline,
|
|
188
|
-
clx=''
|
|
189
|
-
}) => (
|
|
190
|
-
<ApplyTypography className={clx}>
|
|
191
|
-
<h3>{title}</h3>
|
|
192
|
-
{byline && (<h6>{byline}</h6>)}
|
|
193
|
-
</ApplyTypography>
|
|
194
|
-
)
|
|
195
|
-
|
|
196
|
-
const scroll = !!(itemsToShow && itemsToShow.length > scrollAfter)
|
|
197
|
-
return (
|
|
198
|
-
<div className={cn(
|
|
199
|
-
'px-4 md:px-6 pt-3 pb-4 flex flex-col items-center min-h-[60vh]',
|
|
200
|
-
clx,
|
|
201
|
-
scroll ? scrollHeightClx : 'h-auto'
|
|
202
|
-
)} >
|
|
203
|
-
{inst.current?.requestedNode && (<>
|
|
204
|
-
<ApplyTypography className={'mb-2 ' + (scroll ? 'shrink-0' : '')}>
|
|
205
|
-
<h3>{inst.current!.requestedNode.label}</h3>
|
|
206
|
-
{inst.current!.requestedNode.subNodesLabel && (
|
|
207
|
-
<h6 className='!text-center font-bold text-muted mt-3'>{inst.current!.requestedNode.subNodesLabel}</h6>
|
|
208
|
-
)}
|
|
209
|
-
</ApplyTypography>
|
|
210
|
-
<NodeTabs
|
|
211
|
-
className={cn(
|
|
212
|
-
'grid gap-0 align-stretch justify-normal',
|
|
213
|
-
'border-b-2 rounded-lg border-level-3 mb-4 -mr-2 -ml-2 max-w-[460px] h-10', // height is needed for iPhone bug
|
|
214
|
-
(scroll ? 'shrink-0' : ''),
|
|
215
|
-
famWidgetClx
|
|
216
|
-
)}
|
|
217
|
-
// One column per sub-node. The count used to be interpolated into a
|
|
218
|
-
// column-count utility in the className, and Tailwind's extractor never
|
|
219
|
-
// saw it: the extractor reads source text, and a template literal is not
|
|
220
|
-
// a class name. The element got `display:grid` with the default
|
|
221
|
-
// `grid-template-columns: none`, so every family tab row stacked in a
|
|
222
|
-
// single column. `repeat(n, minmax(0, 1fr))` is exactly what the utility
|
|
223
|
-
// would have compiled to, and it holds for any n.
|
|
224
|
-
style={{ gridTemplateColumns: `repeat(${inst.current.requestedNode.subNodes!.length}, minmax(0, 1fr))` }}
|
|
225
|
-
mobile={mobile}
|
|
226
|
-
mutator={allVariants ?
|
|
227
|
-
{
|
|
228
|
-
get: () => (inst.current!.currentFamTokenMutator.get()),
|
|
229
|
-
set: setFamilyPath
|
|
230
|
-
}
|
|
231
|
-
:
|
|
232
|
-
getFacetValuesMutator(inst.current.level + 1, cmmc)
|
|
233
|
-
}
|
|
234
|
-
itemClx='flex-col h-auto gap-0 py-1 px-3'
|
|
235
|
-
buttonClx={
|
|
236
|
-
'h-full ' +
|
|
237
|
-
'!border-level-3'
|
|
238
|
-
}
|
|
239
|
-
levelNodes={inst.current.requestedNode.subNodes!}
|
|
240
|
-
show={familyTabAs}
|
|
241
|
-
/>
|
|
242
|
-
</>)}
|
|
243
|
-
{!inst.current?.requestedNode && famTitle && (
|
|
244
|
-
<TitleArea title={famTitle} clx=''/>
|
|
245
|
-
)}
|
|
246
|
-
{(cmmc.currentItem && showItemMedia) && (
|
|
247
|
-
<MediaStack media={cmmc.currentItem} constrainTo={{w: 200, h: 200}} className={(scroll ? 'shrink-0' : '')}/>
|
|
248
|
-
)}
|
|
249
|
-
{itemsToShow && (
|
|
250
|
-
<Selector
|
|
251
|
-
items={itemsToShow}
|
|
252
|
-
selectedItemRef={cmmc}
|
|
253
|
-
selectSku={selectSku}
|
|
254
|
-
{...selectorProps}
|
|
255
|
-
scrollable={scroll}
|
|
256
|
-
/>
|
|
257
|
-
)}
|
|
258
|
-
{(cmmc.currentItem) && (
|
|
259
|
-
<AddToCartWidget
|
|
260
|
-
item={cmmc.currentItem}
|
|
261
|
-
onQuantityChanged={onQuantityChanged}
|
|
262
|
-
className={cn('min-w-[160px] mx-auto mt-4', (scroll ? 'shrink-0' : ''), addWidgetClx)}
|
|
263
|
-
/>
|
|
264
|
-
)}
|
|
265
|
-
</div >
|
|
266
|
-
)
|
|
267
|
-
})
|
|
268
|
-
|
|
269
|
-
export default BuyCard
|