@hanzo/commerce 6.1.7 → 6.1.9
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/components/buy/{_to_deprecate_select-category-item-card.tsx → _to_deprecate_select-family-item-card.tsx} +3 -3
- package/components/buy/{_to_deprecate_select-category-item-panel.tsx → _to_deprecate_select-family-item-panel.tsx} +20 -20
- package/components/buy/add-to-cart-widget.tsx +2 -2
- package/components/buy/buy-card.tsx +56 -54
- package/components/buy/buy-drawer.tsx +25 -11
- package/components/buy/carousel-buy-card.tsx +261 -0
- package/components/cart/cart-panel/index.tsx +1 -1
- package/components/checkout/payment-step-form/index.tsx +1 -1
- package/components/checkout/payment-step-form/methods/card.tsx +1 -1
- package/components/checkout/payment-step-form/methods/crypto.tsx +1 -1
- package/components/checkout/shipping-step-form.tsx +1 -1
- package/components/index.ts +4 -4
- package/components/item/product-card.tsx +2 -2
- package/components/select-family/family-carousel/family-slide.tsx +36 -0
- package/components/select-family/family-carousel/index.tsx +78 -0
- package/components/{level-nodes-widget → select-family/level-nodes-widget}/index.tsx +1 -1
- package/components/{level-nodes-widget → select-family/level-nodes-widget}/node-image.tsx +1 -1
- package/components/{select → select-product}/carousel-selector.tsx +9 -9
- package/components/{select → select-product}/image-selector.tsx +3 -3
- package/components/{select → select-product}/radio-selector.tsx +3 -3
- package/index.ts +8 -1
- package/package.json +2 -4
- package/service/context.tsx +4 -4
- package/service/impls/standalone/actual-line-item.ts +9 -9
- package/service/impls/standalone/index.ts +4 -4
- package/service/impls/standalone/standalone-service.ts +39 -53
- package/types/commerce-service.ts +18 -9
- package/types/{category.ts → family.ts} +2 -2
- package/types/index.ts +1 -1
- package/types/item-selector.ts +2 -2
- package/types/product.ts +4 -7
- package/util/buy-ui-conf.ts +2 -1
- package/util/sep.ts +5 -0
- package/util/use-sync-sku-param-w-current-item.ts +9 -8
- package/components/item/item-media.tsx +0 -51
|
@@ -0,0 +1,261 @@
|
|
|
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 { cn } from '@hanzo/ui/util'
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
SelectedPaths,
|
|
10
|
+
ItemSelectorProps,
|
|
11
|
+
ItemSelectorCompProps,
|
|
12
|
+
LineItem,
|
|
13
|
+
Family,
|
|
14
|
+
ProductTreeNode,
|
|
15
|
+
} from '../../types'
|
|
16
|
+
|
|
17
|
+
import { useCommerce } from '../../service/context'
|
|
18
|
+
import SEP from '../../util/sep'
|
|
19
|
+
import { ObsStringMutator } from '../../util'
|
|
20
|
+
|
|
21
|
+
import AddToCartWidget from './add-to-cart-widget'
|
|
22
|
+
import { ApplyTypography, MediaStack } from '@hanzo/ui/primitives'
|
|
23
|
+
|
|
24
|
+
const CarouselBuyCard: React.FC<{
|
|
25
|
+
skuPath: string
|
|
26
|
+
|
|
27
|
+
/** For any selectors that show a list of choices,
|
|
28
|
+
* starting with 'scrollAfter' elements,
|
|
29
|
+
* the list will scroll, and the card will
|
|
30
|
+
* have 'scrollHeightClx' applied to it to define and fix the height.
|
|
31
|
+
* This is most like something like '80vh'
|
|
32
|
+
* defaults are '5' and 'h-[70vh]'
|
|
33
|
+
* */
|
|
34
|
+
scrollAfter?: number
|
|
35
|
+
scrollHeightClx?: string
|
|
36
|
+
|
|
37
|
+
clx?: string
|
|
38
|
+
famWidgetClx?: string
|
|
39
|
+
addWidgetClx?: string
|
|
40
|
+
selector: ComponentType<ItemSelectorProps>
|
|
41
|
+
selectorProps?: ItemSelectorCompProps
|
|
42
|
+
|
|
43
|
+
/** Show all items from all sibling families
|
|
44
|
+
* If skuPath defines a Family, the first of it's items will
|
|
45
|
+
* be selected. The facet widget will just select a the first
|
|
46
|
+
* item in the chosen Family without changings the overall set
|
|
47
|
+
* of choices. */
|
|
48
|
+
allVariants?: boolean
|
|
49
|
+
|
|
50
|
+
familyTabAs?: 'image' | 'label' | 'image-and-label'
|
|
51
|
+
showItemMedia?: boolean
|
|
52
|
+
|
|
53
|
+
onQuantityChanged?: (sku: string, oldV: number, newV: number) => void
|
|
54
|
+
mobile?: boolean
|
|
55
|
+
}> = observer(({
|
|
56
|
+
skuPath,
|
|
57
|
+
scrollAfter=5,
|
|
58
|
+
scrollHeightClx='h-[80vh]',
|
|
59
|
+
clx='',
|
|
60
|
+
famWidgetClx='',
|
|
61
|
+
addWidgetClx='',
|
|
62
|
+
selector: Selector,
|
|
63
|
+
selectorProps={
|
|
64
|
+
clx: '',
|
|
65
|
+
soleItemClx: '',
|
|
66
|
+
itemClx: '',
|
|
67
|
+
ext: {}
|
|
68
|
+
},
|
|
69
|
+
familyTabAs='image-and-label',
|
|
70
|
+
showItemMedia=true,
|
|
71
|
+
allVariants=false,
|
|
72
|
+
mobile=false,
|
|
73
|
+
onQuantityChanged,
|
|
74
|
+
}) => {
|
|
75
|
+
|
|
76
|
+
const cmmc = useCommerce()
|
|
77
|
+
const inst = useRef<{
|
|
78
|
+
level: number,
|
|
79
|
+
reqFamily : Family | undefined
|
|
80
|
+
parentNode: ProductTreeNode | undefined
|
|
81
|
+
disposers: IReactionDisposer[]
|
|
82
|
+
} | undefined>(undefined)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
|
|
87
|
+
let reqFamily = cmmc.getFamily(skuPath)
|
|
88
|
+
let parentNode = reqFamily ? undefined : cmmc.getNodeAtPath(skuPath)
|
|
89
|
+
let initialFamily = undefined
|
|
90
|
+
const toks = skuPath.split(SEP.TOK)
|
|
91
|
+
const level = toks.length - 1
|
|
92
|
+
if (level === 0) {
|
|
93
|
+
throw new Error('BuyCard.useEffect(): must specify at least at least one Level in skuPath!')
|
|
94
|
+
}
|
|
95
|
+
const paths: SelectedPaths = {}
|
|
96
|
+
for (let l = 1; l <= level; l++ ) {
|
|
97
|
+
paths[l] = [toks[l]]
|
|
98
|
+
}
|
|
99
|
+
if (reqFamily) {
|
|
100
|
+
if (allVariants && level >= 2) {
|
|
101
|
+
// select all siblings of reqFamily.
|
|
102
|
+
// ie, go one level back
|
|
103
|
+
delete paths[level]
|
|
104
|
+
initialFamily = reqFamily
|
|
105
|
+
reqFamily = undefined
|
|
106
|
+
parentNode = cmmc.getNodeAtPath(toks.slice(0, -1).join(SEP.TOK))
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
// select siblings paths
|
|
111
|
+
if (allVariants) {
|
|
112
|
+
paths[level + 1] = [...parentNode!.subNodes!.map((node) => (node.skuToken))]
|
|
113
|
+
}
|
|
114
|
+
// Actually select the the first fam at the fam level,
|
|
115
|
+
else {
|
|
116
|
+
paths[level + 1] = [parentNode!.subNodes![0].skuToken]
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const selFamilies = cmmc.selectPaths(paths)
|
|
121
|
+
initialFamily = initialFamily ? initialFamily : (reqFamily ? reqFamily : selFamilies[0])
|
|
122
|
+
cmmc.setCurrentItem(initialFamily.products[0].sku)
|
|
123
|
+
const currentFamily = new ObsStringMutator(initialFamily!.id.split(SEP.TOK).pop()!)
|
|
124
|
+
|
|
125
|
+
inst.current = {
|
|
126
|
+
level,
|
|
127
|
+
reqFamily,
|
|
128
|
+
parentNode,
|
|
129
|
+
disposers: []
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (!allVariants) {
|
|
133
|
+
inst.current?.disposers.push(reaction(
|
|
134
|
+
() => {
|
|
135
|
+
const fams = cmmc.selectedFamilies
|
|
136
|
+
return (fams.length > 0) ? fams[0].id : undefined
|
|
137
|
+
},
|
|
138
|
+
(famId) => {
|
|
139
|
+
if (famId && famId !== cmmc.currentItem?.familyId) {
|
|
140
|
+
const fam = cmmc.getFamily(famId)
|
|
141
|
+
if (fam) {
|
|
142
|
+
cmmc.setCurrentItem(fam.products[0].sku)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
))
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return () => {
|
|
150
|
+
inst.current?.disposers.forEach((d) => {d()})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
}, [])
|
|
154
|
+
|
|
155
|
+
const setFamilyPath = (pathValue: string | null) => {
|
|
156
|
+
const found = cmmc.selectedItems.find((item) => (item.familyId.endsWith(pathValue!)))
|
|
157
|
+
if (found) {
|
|
158
|
+
cmmc.setCurrentItem(found.sku)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const selectSku = (sku: string) => {
|
|
163
|
+
cmmc.setCurrentItem(sku)
|
|
164
|
+
const pathValue = cmmc.currentItem?.familyId.split(SEP.TOK).pop()!
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const famTitle = inst.current?.reqFamily ? inst.current.reqFamily.title : cmmc.selectedFamilies?.[0]?.title
|
|
168
|
+
|
|
169
|
+
const itemsToShow = (!cmmc.hasSelection) ? undefined :
|
|
170
|
+
allVariants ? cmmc.selectedItems : cmmc.selectedFamilies[0].products as LineItem[]
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
const TitleArea: React.FC<{
|
|
174
|
+
title: string
|
|
175
|
+
byline?: string
|
|
176
|
+
clx?: string
|
|
177
|
+
}> = ({
|
|
178
|
+
title,
|
|
179
|
+
byline,
|
|
180
|
+
clx=''
|
|
181
|
+
}) => (
|
|
182
|
+
<ApplyTypography className={clx}>
|
|
183
|
+
<h3>{title}</h3>
|
|
184
|
+
{byline && (<h6>{byline}</h6>)}
|
|
185
|
+
</ApplyTypography>
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
const scroll = !!(itemsToShow && itemsToShow.length > scrollAfter)
|
|
189
|
+
return (
|
|
190
|
+
<div className={cn(
|
|
191
|
+
'px-4 md:px-6 pt-3 pb-4 flex flex-col items-center min-h-[60vh]',
|
|
192
|
+
clx,
|
|
193
|
+
scroll ? scrollHeightClx : 'h-auto'
|
|
194
|
+
)} >
|
|
195
|
+
{inst.current?.parentNode && (<>
|
|
196
|
+
<ApplyTypography className={'mb-2 ' + (scroll ? 'shrink-0' : '')}>
|
|
197
|
+
<h3>{inst.current!.parentNode.label}</h3>
|
|
198
|
+
{inst.current!.parentNode.subNodesLabel && (
|
|
199
|
+
<h6 className='!text-center font-bold text-muted mt-3'>{inst.current!.parentNode.subNodesLabel}</h6>
|
|
200
|
+
)}
|
|
201
|
+
</ApplyTypography>
|
|
202
|
+
{/* }
|
|
203
|
+
<LevelNodesWidget
|
|
204
|
+
className={cn(
|
|
205
|
+
'grid gap-0 align-stretch justify-normal ' + `grid-cols-${inst.current.parentNode.subNodes!.length}`,
|
|
206
|
+
'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
|
|
207
|
+
(scroll ? 'shrink-0' : ''),
|
|
208
|
+
famWidgetClx
|
|
209
|
+
)}
|
|
210
|
+
mobile={mobile}
|
|
211
|
+
mutator={allVariants ?
|
|
212
|
+
{
|
|
213
|
+
get: () => (inst.current!.currentFamily.s),
|
|
214
|
+
set: setFamilyPath
|
|
215
|
+
}
|
|
216
|
+
:
|
|
217
|
+
getFacetValuesMutator(inst.current.level + 1, cmmc)
|
|
218
|
+
}
|
|
219
|
+
itemClx='flex-col h-auto gap-0 py-1 px-3'
|
|
220
|
+
buttonClx={
|
|
221
|
+
'h-full ' +
|
|
222
|
+
'!border-level-3'
|
|
223
|
+
}
|
|
224
|
+
levelNodes={inst.current.parentNode.subNodes!}
|
|
225
|
+
show={familyTabAs}
|
|
226
|
+
/>
|
|
227
|
+
*/}
|
|
228
|
+
</>)}
|
|
229
|
+
{!inst.current?.parentNode && famTitle && (
|
|
230
|
+
<TitleArea title={famTitle} clx=''/>
|
|
231
|
+
)}
|
|
232
|
+
{(cmmc.currentItem && showItemMedia) && (
|
|
233
|
+
<MediaStack media={cmmc.currentItem} constrainTo={{w: 200, h: 200}} clx={(scroll ? 'shrink-0' : '')}/>
|
|
234
|
+
)}
|
|
235
|
+
{itemsToShow && (
|
|
236
|
+
<Selector
|
|
237
|
+
items={itemsToShow}
|
|
238
|
+
selectedItemRef={cmmc}
|
|
239
|
+
selectSku={selectSku}
|
|
240
|
+
{...selectorProps}
|
|
241
|
+
scrollList={scroll}
|
|
242
|
+
showFamily={selectorProps.showFamily ?
|
|
243
|
+
selectorProps.showFamily
|
|
244
|
+
:
|
|
245
|
+
(allVariants && !inst.current?.reqFamily)
|
|
246
|
+
}
|
|
247
|
+
/>
|
|
248
|
+
)}
|
|
249
|
+
{(cmmc.currentItem) && (
|
|
250
|
+
<AddToCartWidget
|
|
251
|
+
size='default'
|
|
252
|
+
item={cmmc.currentItem}
|
|
253
|
+
onQuantityChanged={onQuantityChanged}
|
|
254
|
+
className={cn('min-w-[160px] mx-auto mt-4', (scroll ? 'shrink-0' : ''), addWidgetClx)}
|
|
255
|
+
/>
|
|
256
|
+
)}
|
|
257
|
+
</div >
|
|
258
|
+
)
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
export default CarouselBuyCard
|
|
@@ -59,7 +59,7 @@ const CartPanel: React.FC<PropsWithChildren & {
|
|
|
59
59
|
items: cmmc.cartItems.map((item) => ({
|
|
60
60
|
item_id: item.sku,
|
|
61
61
|
item_name: item.title,
|
|
62
|
-
item_category: item.
|
|
62
|
+
item_category: item.familyId,
|
|
63
63
|
price: item.price,
|
|
64
64
|
quantity: item.quantity
|
|
65
65
|
})),
|
|
@@ -56,7 +56,7 @@ const PaymentStepForm: React.FC<CheckoutStepComponentProps> = observer(({
|
|
|
56
56
|
items: cmmc.cartItems.map((item) => ({
|
|
57
57
|
item_id: item.sku,
|
|
58
58
|
item_name: item.title,
|
|
59
|
-
item_category: item.
|
|
59
|
+
item_category: item.familyId,
|
|
60
60
|
price: item.price,
|
|
61
61
|
quantity: item.quantity
|
|
62
62
|
})),
|
|
@@ -49,7 +49,7 @@ const PayWithCard: React.FC<PaymentMethodComponentProps> = observer(({
|
|
|
49
49
|
items: cmmc.cartItems.map((item) => ({
|
|
50
50
|
item_id: item.sku,
|
|
51
51
|
item_name: item.title,
|
|
52
|
-
item_category: item.
|
|
52
|
+
item_category: item.familyId,
|
|
53
53
|
price: item.price,
|
|
54
54
|
quantity: item.quantity
|
|
55
55
|
})),
|
|
@@ -135,7 +135,7 @@ const PayWithCrypto: React.FC<PaymentMethodComponentProps> = observer(({
|
|
|
135
135
|
items: cmmc.cartItems.map((item) => ({
|
|
136
136
|
item_id: item.sku,
|
|
137
137
|
item_name: item.title,
|
|
138
|
-
item_category: item.
|
|
138
|
+
item_category: item.familyId,
|
|
139
139
|
price: item.price,
|
|
140
140
|
quantity: item.quantity
|
|
141
141
|
})),
|
|
@@ -61,7 +61,7 @@ const ShippingStepForm: React.FC<CheckoutStepComponentProps> = ({
|
|
|
61
61
|
items: cmmc.cartItems.map((item) => ({
|
|
62
62
|
item_id: item.sku,
|
|
63
63
|
item_name: item.title,
|
|
64
|
-
item_category: item.
|
|
64
|
+
item_category: item.familyId,
|
|
65
65
|
price: item.price,
|
|
66
66
|
quantity: item.quantity
|
|
67
67
|
})),
|
package/components/index.ts
CHANGED
|
@@ -7,7 +7,7 @@ export { default as BuyDrawer } from './buy/buy-drawer'
|
|
|
7
7
|
export { default as CartAccordian } from './cart/cart-accordian'
|
|
8
8
|
export { default as CartPanel } from './cart/cart-panel'
|
|
9
9
|
|
|
10
|
-
export { default as LevelNodesWidget } from './level-nodes-widget'
|
|
10
|
+
export { default as LevelNodesWidget } from './select-family/level-nodes-widget'
|
|
11
11
|
|
|
12
12
|
export { default as PaymentStepForm } from './checkout/payment-step-form'
|
|
13
13
|
export { default as ShippingStepForm } from './checkout/shipping-step-form'
|
|
@@ -15,6 +15,6 @@ export { default as ShippingStepForm } from './checkout/shipping-step-form'
|
|
|
15
15
|
export { default as ProductCard } from './item/product-card'
|
|
16
16
|
export { Icons } from './Icons'
|
|
17
17
|
|
|
18
|
-
export { default as ImageItemSelector } from './select/image-selector'
|
|
19
|
-
export { default as RadioItemSelector } from './select/radio-selector'
|
|
20
|
-
export { default as CarouselItemSelector, type CarouselItemSelectorPropsExt } from './select/carousel-selector'
|
|
18
|
+
export { default as ImageItemSelector } from './select-product/image-selector'
|
|
19
|
+
export { default as RadioItemSelector } from './select-product/radio-selector'
|
|
20
|
+
export { default as CarouselItemSelector, type CarouselItemSelectorPropsExt } from './select-product/carousel-selector'
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
CardFooter,
|
|
7
7
|
CardHeader,
|
|
8
8
|
CardTitle,
|
|
9
|
+
MediaStack
|
|
9
10
|
} from '@hanzo/ui/primitives'
|
|
10
11
|
|
|
11
12
|
import { cn } from '@hanzo/ui/util'
|
|
@@ -14,7 +15,6 @@ import { formatCurrencyValue } from '../../util'
|
|
|
14
15
|
import type { LineItem } from '../../types'
|
|
15
16
|
|
|
16
17
|
import AddToCartWidget from '../buy/add-to-cart-widget'
|
|
17
|
-
import ItemMedia from './item-media'
|
|
18
18
|
|
|
19
19
|
interface ProductCardProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
20
20
|
item: LineItem
|
|
@@ -30,7 +30,7 @@ const ProductCard: React.FC<ProductCardProps> = ({
|
|
|
30
30
|
{...props}
|
|
31
31
|
>
|
|
32
32
|
<CardHeader className='w-full border-b p-6 min-h-[180px] max-h-[240px] relative'>
|
|
33
|
-
<
|
|
33
|
+
<MediaStack media={item} constrainTo={{w: 700, h:700}} clx='p-6' />
|
|
34
34
|
</CardHeader>
|
|
35
35
|
<CardContent className='grid gap-2.5 p-4'>
|
|
36
36
|
<CardTitle className='text-sm sm:text-base flex flex-col justify-start items-center line-clap-3'>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import React, { useCallback, useEffect, useRef } from 'react'
|
|
3
|
+
import { reaction } from 'mobx'
|
|
4
|
+
import { observer } from 'mobx-react-lite'
|
|
5
|
+
|
|
6
|
+
import { cn } from '@hanzo/ui/util'
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
ApplyTypography,
|
|
10
|
+
MediaStack
|
|
11
|
+
} from '@hanzo/ui/primitives'
|
|
12
|
+
import type { Family } from '../../../types'
|
|
13
|
+
|
|
14
|
+
const FamilySlide: React.FC<{
|
|
15
|
+
family: Family
|
|
16
|
+
clx?: string
|
|
17
|
+
mediaConstraint?: {w: number, h: number}
|
|
18
|
+
getBylineText?: (c: Family) => string
|
|
19
|
+
variantOrientation?: 'vert' | 'horiz'
|
|
20
|
+
showVariantImage?: boolean
|
|
21
|
+
showVariantPrice?: boolean
|
|
22
|
+
}> = ({
|
|
23
|
+
family,
|
|
24
|
+
clx='',
|
|
25
|
+
mediaConstraint,
|
|
26
|
+
getBylineText,
|
|
27
|
+
variantOrientation,
|
|
28
|
+
showVariantImage,
|
|
29
|
+
showVariantPrice
|
|
30
|
+
}) => {
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
return <></>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default FamilySlide
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import React, { useCallback, useEffect, useRef } from 'react'
|
|
3
|
+
import { reaction } from 'mobx'
|
|
4
|
+
import { observer } from 'mobx-react-lite'
|
|
5
|
+
|
|
6
|
+
import { cn } from '@hanzo/ui/util'
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
type CarouselOptions,
|
|
10
|
+
type CarouselApi,
|
|
11
|
+
Carousel,
|
|
12
|
+
CarouselContent,
|
|
13
|
+
CarouselItem,
|
|
14
|
+
CarouselPrevious,
|
|
15
|
+
CarouselNext,
|
|
16
|
+
ApplyTypography,
|
|
17
|
+
MediaStack
|
|
18
|
+
} from '@hanzo/ui/primitives'
|
|
19
|
+
|
|
20
|
+
import type { Family, LineItem, ProductTreeNode } from '../../../types'
|
|
21
|
+
import { formatCurrencyValue } from '../../../util'
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
const FamilyCarousel: React.FC<{
|
|
25
|
+
skuPath: string
|
|
26
|
+
clx?: string
|
|
27
|
+
itemClx?: string
|
|
28
|
+
options?: CarouselOptions
|
|
29
|
+
mediaConstraint?: {w: number, h: number}
|
|
30
|
+
getBylineText?: (c: Family) => string
|
|
31
|
+
variantOrientation?: 'vert' | 'horiz'
|
|
32
|
+
showVariantImage?: boolean
|
|
33
|
+
showVariantPrice?: boolean
|
|
34
|
+
onQuantityChanged?: (sku: string, oldV: number, newV: number) => void
|
|
35
|
+
mobile?: boolean
|
|
36
|
+
}> = observer(({
|
|
37
|
+
skuPath,
|
|
38
|
+
clx='',
|
|
39
|
+
itemClx='',
|
|
40
|
+
options={loop: true},
|
|
41
|
+
mediaConstraint={w: 250, h: 250},
|
|
42
|
+
variantOrientation='vert',
|
|
43
|
+
showVariantImage=true,
|
|
44
|
+
showVariantPrice=true,
|
|
45
|
+
onQuantityChanged,
|
|
46
|
+
mobile=false,
|
|
47
|
+
}) => {
|
|
48
|
+
|
|
49
|
+
const elbaApiRef = useRef<CarouselApi | undefined>(undefined)
|
|
50
|
+
const setApi = (api: CarouselApi) => {elbaApiRef.current = api}
|
|
51
|
+
|
|
52
|
+
const onSelect = useCallback((emblaApi: CarouselApi) => {
|
|
53
|
+
const index = emblaApi.selectedScrollSnap()
|
|
54
|
+
if (index !== -1) {
|
|
55
|
+
//selectSku(items[index].sku)
|
|
56
|
+
// cmmc.selectPath
|
|
57
|
+
}
|
|
58
|
+
}, [])
|
|
59
|
+
|
|
60
|
+
const TEST = ["one", "two", "three"]
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<Carousel options={options} className={cn('w-full px-2', clx)} onCarouselSelect={onSelect} setApi={setApi}>
|
|
64
|
+
<CarouselContent>
|
|
65
|
+
|
|
66
|
+
{/*items.map((item, index) => (
|
|
67
|
+
<CarouselItem key={index} className={cn('p-2 flex flex-col justify-center items-center', itemClx)}>
|
|
68
|
+
<FamilySlide cat
|
|
69
|
+
</CarouselItem>
|
|
70
|
+
))*/}
|
|
71
|
+
</CarouselContent>
|
|
72
|
+
<CarouselPrevious className='left-1'/>
|
|
73
|
+
<CarouselNext className='right-1'/>
|
|
74
|
+
</Carousel>
|
|
75
|
+
)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
export default FamilyCarousel
|
|
@@ -4,7 +4,7 @@ import React, { useState } from 'react'
|
|
|
4
4
|
import { ToggleGroup, ToggleGroupItem} from "@hanzo/ui/primitives"
|
|
5
5
|
import { cn } from '@hanzo/ui/util'
|
|
6
6
|
|
|
7
|
-
import type { ProductTreeNode, StringMutator, StringArrayMutator } from '
|
|
7
|
+
import type { ProductTreeNode, StringMutator, StringArrayMutator } from '../../../types'
|
|
8
8
|
import NodeImage from './node-image'
|
|
9
9
|
|
|
10
10
|
const LevelNodesWidget: React.FC<{
|
|
@@ -4,17 +4,17 @@ import { reaction } from 'mobx'
|
|
|
4
4
|
import { observer } from 'mobx-react-lite'
|
|
5
5
|
|
|
6
6
|
import { cn } from '@hanzo/ui/util'
|
|
7
|
-
import ItemMedia from '../item/item-media'
|
|
8
7
|
|
|
9
8
|
import {
|
|
10
|
-
type
|
|
9
|
+
type CarouselOptions,
|
|
11
10
|
type CarouselApi,
|
|
12
11
|
Carousel,
|
|
13
12
|
CarouselContent,
|
|
14
13
|
CarouselItem,
|
|
15
14
|
CarouselPrevious,
|
|
16
15
|
CarouselNext,
|
|
17
|
-
ApplyTypography
|
|
16
|
+
ApplyTypography,
|
|
17
|
+
MediaStack
|
|
18
18
|
} from '@hanzo/ui/primitives'
|
|
19
19
|
|
|
20
20
|
import type { ItemSelectorProps, LineItem } from '../../types'
|
|
@@ -22,8 +22,8 @@ import { formatCurrencyValue } from '../../util'
|
|
|
22
22
|
|
|
23
23
|
interface CarouselItemSelectorPropsExt {
|
|
24
24
|
constrainTo: {w: number, h: number}
|
|
25
|
-
options?:
|
|
26
|
-
/** Do not show
|
|
25
|
+
options?: CarouselOptions
|
|
26
|
+
/** Do not show Family and / or Item title and Price */
|
|
27
27
|
imageOnly?: boolean
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -34,7 +34,7 @@ const CarouselItemSelector: React.FC<ItemSelectorProps> = observer(({
|
|
|
34
34
|
scrollList, // ignored
|
|
35
35
|
clx='',
|
|
36
36
|
itemClx='',
|
|
37
|
-
|
|
37
|
+
showFamily=false,
|
|
38
38
|
ext={
|
|
39
39
|
options: {loop: true},
|
|
40
40
|
constrainTo: {w: 250, h: 250},
|
|
@@ -84,18 +84,18 @@ const CarouselItemSelector: React.FC<ItemSelectorProps> = observer(({
|
|
|
84
84
|
clx
|
|
85
85
|
}) => (
|
|
86
86
|
<ApplyTypography className={cn(clx, 'flex flex-col items-center !gap-2 [&>*]:!m-0')}>
|
|
87
|
-
{
|
|
87
|
+
{showFamily && (<h4>{item.familyTitle}</h4>)}
|
|
88
88
|
<p>{item.optionLabel}</p>
|
|
89
89
|
<p>{formatCurrencyValue(item.price)}</p>
|
|
90
90
|
</ApplyTypography>
|
|
91
91
|
)
|
|
92
92
|
|
|
93
93
|
return (
|
|
94
|
-
<Carousel options={options} className={cn('w-full px-2', clx)}
|
|
94
|
+
<Carousel options={options} className={cn('w-full px-2', clx)} onCarouselSelect={onSelect} setApi={setApi}>
|
|
95
95
|
<CarouselContent>
|
|
96
96
|
{items.map((item, index) => (
|
|
97
97
|
<CarouselItem key={index} className={cn('p-2 flex flex-col justify-center items-center', itemClx)}>
|
|
98
|
-
<
|
|
98
|
+
<MediaStack media={item} constrainTo={constrainTo} clx='' />
|
|
99
99
|
{!imageOnly && (<ItemInfo item={item} clx=''/>)}
|
|
100
100
|
</CarouselItem>
|
|
101
101
|
))}
|
|
@@ -56,14 +56,14 @@ const ImageItemSelector: React.FC<ItemSelectorProps> = observer(({
|
|
|
56
56
|
clx='',
|
|
57
57
|
itemClx='',
|
|
58
58
|
soleItemClx='',
|
|
59
|
-
|
|
59
|
+
showFamily=false,
|
|
60
60
|
showQuantity=false,
|
|
61
61
|
scrollList=false
|
|
62
62
|
}) => {
|
|
63
63
|
|
|
64
64
|
const LabelText: React.FC<{item: LineItem}> = ({item}) => (
|
|
65
|
-
(
|
|
66
|
-
(
|
|
65
|
+
(showFamily ? (item.familyTitle + ', ' + item.optionLabel) : item.optionLabel) +
|
|
66
|
+
(showFamily ? ': ' : ', ') + formatCurrencyValue(item.price)
|
|
67
67
|
)
|
|
68
68
|
|
|
69
69
|
const ItemAndPrice: React.FC<{
|
|
@@ -20,14 +20,14 @@ const RadioItemSelector: React.FC<ItemSelectorProps> = observer(({
|
|
|
20
20
|
clx='',
|
|
21
21
|
itemClx='',
|
|
22
22
|
soleItemClx='',
|
|
23
|
-
|
|
23
|
+
showFamily=false,
|
|
24
24
|
showQuantity=false,
|
|
25
25
|
scrollList=false
|
|
26
26
|
}) => {
|
|
27
27
|
|
|
28
28
|
const LabelText: React.FC<{item: LineItem}> = ({item}) => (
|
|
29
|
-
(
|
|
30
|
-
(
|
|
29
|
+
(showFamily ? (item.familyTitle + ', ' + item.optionLabel) : item.optionLabel) +
|
|
30
|
+
(showFamily ? ': ' : ', ') + formatCurrencyValue(item.price)
|
|
31
31
|
)
|
|
32
32
|
|
|
33
33
|
const ItemAndPrice: React.FC<{
|
package/index.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
export * from './service/context'
|
|
2
2
|
export * from './components'
|
|
3
3
|
export type { StandaloneServiceOptions as ServiceOptions } from './service/impls/standalone/standalone-service'
|
|
4
|
-
export { useSyncSkuParamWithCurrentItem, getFacetValuesMutator, formatCurrencyValue } from './util'
|
|
4
|
+
export { useSyncSkuParamWithCurrentItem, getFacetValuesMutator, formatCurrencyValue } from './util'
|
|
5
|
+
//export { default as SEP } from './util/sep'
|
|
6
|
+
|
|
7
|
+
export const sep = {
|
|
8
|
+
TOK: '-',
|
|
9
|
+
SUB_TOK: '_',
|
|
10
|
+
DECIMAL: '.'
|
|
11
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/commerce",
|
|
3
|
-
"version": "6.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "6.1.9",
|
|
4
|
+
"description": "e-commerce framework.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/",
|
|
7
7
|
"access": "public",
|
|
@@ -31,8 +31,6 @@
|
|
|
31
31
|
"./types": "./types/index.ts"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@splinetool/react-spline": "^2.2.6",
|
|
35
|
-
"@splinetool/runtime": "^1.0.75",
|
|
36
34
|
"ethers": "^6.11.1",
|
|
37
35
|
"next-usequerystate": "^1.17.0",
|
|
38
36
|
"react-square-web-payments-sdk": "^3.2.1",
|
package/service/context.tsx
CHANGED
|
@@ -9,7 +9,7 @@ enableStaticRendering(typeof window === "undefined")
|
|
|
9
9
|
import type CommerceService from '../types/commerce-service'
|
|
10
10
|
import type { ServiceOptions } from '..'
|
|
11
11
|
import getServiceSingleton from './impls'
|
|
12
|
-
import type {
|
|
12
|
+
import type { Family, ProductTreeNode } from '../types'
|
|
13
13
|
|
|
14
14
|
const CommerceServiceContext = createContext<CommerceService | undefined>(undefined)
|
|
15
15
|
|
|
@@ -19,18 +19,18 @@ const useCommerce = (): CommerceService => {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
const CommerceServiceProvider: React.FC<PropsWithChildren & {
|
|
22
|
-
|
|
22
|
+
productsByFamily: Family[]
|
|
23
23
|
rootNode: ProductTreeNode
|
|
24
24
|
options?: ServiceOptions
|
|
25
25
|
}> = ({
|
|
26
26
|
children,
|
|
27
|
-
|
|
27
|
+
productsByFamily,
|
|
28
28
|
rootNode,
|
|
29
29
|
options
|
|
30
30
|
}) => {
|
|
31
31
|
|
|
32
32
|
// TODO: Inject Promo fixture here
|
|
33
|
-
const serviceRef = useRef<CommerceService>(getServiceSingleton(
|
|
33
|
+
const serviceRef = useRef<CommerceService>(getServiceSingleton(productsByFamily, rootNode, options))
|
|
34
34
|
return (
|
|
35
35
|
<CommerceServiceContext.Provider value={serviceRef.current}>
|
|
36
36
|
{children}
|