@hanzo/commerce 5.1.3 → 6.0.0
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-item/select-category-item-card.tsx → buy/_to_deprecate_select-category-item-card.tsx} +20 -31
- package/components/{select-category-item-panel.tsx → buy/_to_deprecate_select-category-item-panel.tsx} +12 -20
- package/components/{buy-item/buy-item-popup.tsx → buy/_unused_buy-popup.tsx_} +4 -4
- package/components/{add-to-cart-widget.tsx → buy/add-to-cart-widget.tsx} +5 -3
- package/components/buy/buy-button.tsx +31 -0
- package/components/buy/buy-card.tsx +169 -0
- package/components/buy/buy-drawer.tsx +78 -0
- package/components/buy/buy-trigger-wrapper.tsx +20 -0
- package/components/{cart-accordian.tsx → cart/cart-accordian.tsx} +2 -1
- package/components/{cart-panel → cart/cart-panel}/cart-line-item.tsx +28 -5
- package/components/{cart-panel → cart/cart-panel}/index.tsx +3 -3
- package/components/{cart-panel → cart/cart-panel}/promo-code.tsx +3 -3
- package/components/{payment-step-form → checkout/payment-step-form}/contact-form.tsx +1 -1
- package/components/{payment-step-form → checkout/payment-step-form}/index.tsx +3 -3
- package/components/{payment-step-form → checkout/payment-step-form}/methods/bank-transfer.tsx +1 -1
- package/components/{payment-step-form → checkout/payment-step-form}/methods/card.tsx +4 -4
- package/components/{payment-step-form → checkout/payment-step-form}/methods/crypto.tsx +3 -3
- package/components/{payment-step-form → checkout/payment-step-form}/methods/index.ts +1 -1
- package/components/{shipping-step-form.tsx → checkout/shipping-step-form.tsx} +4 -4
- package/components/facet-values-widget/facet-image.tsx +2 -2
- package/components/facet-values-widget/index.tsx +13 -12
- package/components/index.ts +19 -17
- package/components/item/item-media.tsx +58 -0
- package/components/{product-card.tsx → item/product-card.tsx} +5 -7
- package/components/select/carousel-selector.tsx +64 -0
- package/components/select/image-selector.tsx +125 -0
- package/components/select/radio-selector.tsx +66 -0
- package/package.json +2 -1
- package/service/context.tsx +4 -4
- package/service/impls/standalone/index.ts +4 -4
- package/service/impls/standalone/standalone-service.ts +74 -62
- package/types/commerce-service.ts +20 -16
- package/types/index.ts +2 -2
- package/types/item-selector.ts +12 -5
- package/types/product.ts +1 -0
- package/types/tree-node.ts +24 -0
- package/util/index.ts +9 -9
- package/util/use-sync-sku-param-w-current-item.ts +5 -5
- package/components/buy-item/buy-item-button-wrapper.tsx +0 -27
- package/components/buy-item/buy-item-button.tsx +0 -34
- package/components/buy-item/buy-item-card.tsx +0 -92
- package/components/buy-item/buy-item-mobile-drawer.tsx +0 -54
- package/components/buy-item/select-category-and-item-widget.tsx +0 -83
- package/components/category-item-radio-selector.tsx +0 -55
- package/components/category-item-scroll-selector.tsx +0 -40
- package/components/item-carousel.tsx +0 -80
- package/types/facet.ts +0 -35
- /package/components/{cart-icon.tsx → cart/cart-icon.tsx} +0 -0
- /package/components/{payment-step-form → checkout/payment-step-form}/card-icon-row.tsx +0 -0
- /package/components/{payment-step-form → checkout/payment-step-form}/card-icons/amex.tsx +0 -0
- /package/components/{payment-step-form → checkout/payment-step-form}/card-icons/diners-club.tsx +0 -0
- /package/components/{payment-step-form → checkout/payment-step-form}/card-icons/discover.tsx +0 -0
- /package/components/{payment-step-form → checkout/payment-step-form}/card-icons/jcb.tsx +0 -0
- /package/components/{payment-step-form → checkout/payment-step-form}/card-icons/mastercard.tsx +0 -0
- /package/components/{payment-step-form → checkout/payment-step-form}/card-icons/visa.tsx +0 -0
- /package/components/{payment-step-form → checkout/payment-step-form}/cc-button.tsx +0 -0
- /package/components/{payment-step-form → checkout/payment-step-form}/crypto-icons/btc.tsx +0 -0
- /package/components/{payment-step-form → checkout/payment-step-form}/crypto-icons/eth.tsx +0 -0
- /package/components/{payment-step-form → checkout/payment-step-form}/crypto-icons/usdt.tsx +0 -0
|
@@ -19,12 +19,12 @@ import {
|
|
|
19
19
|
SelectValue
|
|
20
20
|
} from '@hanzo/ui/primitives'
|
|
21
21
|
|
|
22
|
-
import { useCommerce } from '
|
|
22
|
+
import { useCommerce } from '../../service/context'
|
|
23
23
|
|
|
24
|
-
import countries from '
|
|
25
|
-
import { sendGAEvent } from '
|
|
24
|
+
import countries from '../../util/countries'
|
|
25
|
+
import { sendGAEvent } from '../../util/analytics'
|
|
26
26
|
|
|
27
|
-
import type { CheckoutStepComponentProps } from '
|
|
27
|
+
import type { CheckoutStepComponentProps } from '../../types'
|
|
28
28
|
|
|
29
29
|
const shippingFormSchema = z.object({
|
|
30
30
|
addressLine1: z.string().min(2, 'Address must be at least 2 characters.'),
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import Image from 'next/image'
|
|
3
3
|
|
|
4
|
-
import type {
|
|
4
|
+
import type { ProductTreeNode } from '../../types'
|
|
5
5
|
|
|
6
6
|
const ICON_SIZE = 20
|
|
7
7
|
|
|
8
8
|
const FacetImage: React.FC<{
|
|
9
|
-
facetValueDesc:
|
|
9
|
+
facetValueDesc: ProductTreeNode
|
|
10
10
|
}> = ({
|
|
11
11
|
facetValueDesc
|
|
12
12
|
}) => {
|
|
@@ -4,18 +4,19 @@ 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 {
|
|
7
|
+
import type { ProductTreeNode, StringMutator, StringArrayMutator } from '../../types'
|
|
8
8
|
import FacetImage from './facet-image'
|
|
9
9
|
|
|
10
10
|
const FacetValuesWidget: React.FC<{
|
|
11
|
-
facetValues:
|
|
11
|
+
facetValues: ProductTreeNode[]
|
|
12
12
|
mutator: StringMutator | StringArrayMutator
|
|
13
13
|
multiple?: boolean
|
|
14
14
|
className?: string
|
|
15
15
|
buttonClx?: string
|
|
16
16
|
itemClx?: string
|
|
17
|
-
|
|
17
|
+
mobile?: boolean
|
|
18
18
|
tabSize?: string
|
|
19
|
+
show?: 'image' | 'label' | 'image-and-label'
|
|
19
20
|
}> = ({
|
|
20
21
|
facetValues,
|
|
21
22
|
mutator,
|
|
@@ -23,8 +24,9 @@ const FacetValuesWidget: React.FC<{
|
|
|
23
24
|
buttonClx='',
|
|
24
25
|
itemClx='',
|
|
25
26
|
className='',
|
|
26
|
-
|
|
27
|
-
tabSize
|
|
27
|
+
mobile=false,
|
|
28
|
+
tabSize,
|
|
29
|
+
show='image-and-label'
|
|
28
30
|
}) => {
|
|
29
31
|
|
|
30
32
|
const [last, setLast] = useState<string | undefined>(undefined)
|
|
@@ -54,7 +56,7 @@ const FacetValuesWidget: React.FC<{
|
|
|
54
56
|
type={multiple ? 'multiple' : 'single'}
|
|
55
57
|
value={val}
|
|
56
58
|
variant='default'
|
|
57
|
-
size={tabSize ? tabSize : (
|
|
59
|
+
size={tabSize ? tabSize : (mobile ? 'sm' : 'default')}
|
|
58
60
|
onValueChange={multiple ? handleChangeMultiple : handleChangeSingle}
|
|
59
61
|
className={className}
|
|
60
62
|
{...roundedToSpread}
|
|
@@ -68,16 +70,16 @@ const FacetValuesWidget: React.FC<{
|
|
|
68
70
|
}
|
|
69
71
|
return (
|
|
70
72
|
<ToggleGroupItem
|
|
71
|
-
key={fv.
|
|
72
|
-
value={fv.
|
|
73
|
-
disabled={(last && last === fv.
|
|
73
|
+
key={fv.skuToken}
|
|
74
|
+
value={fv.skuToken}
|
|
75
|
+
disabled={(last && last === fv.skuToken || fv.skuToken === mutator.get())}
|
|
74
76
|
aria-label={`Select ${fv.label}`}
|
|
75
77
|
{...roundedToSpread}
|
|
76
78
|
className={buttonClx}
|
|
77
79
|
>
|
|
78
80
|
<span className={cn('flex flex-row justify-center gap-1 h-6 items-center', itemClx)} >
|
|
79
|
-
<FacetImage facetValueDesc={fv} />
|
|
80
|
-
{(!
|
|
81
|
+
{!(show === 'label') && (<FacetImage facetValueDesc={fv} />) }
|
|
82
|
+
{(!(show === 'image') || !fv.img) && (<span className='whitespace-nowrap'>{fv.label}</span>)}
|
|
81
83
|
</span>
|
|
82
84
|
</ToggleGroupItem>
|
|
83
85
|
)
|
|
@@ -85,6 +87,5 @@ const FacetValuesWidget: React.FC<{
|
|
|
85
87
|
</ToggleGroup>
|
|
86
88
|
)
|
|
87
89
|
}
|
|
88
|
-
// hidden md:block
|
|
89
90
|
|
|
90
91
|
export default FacetValuesWidget
|
package/components/index.ts
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
export { default as AddToCartWidget } from './add-to-cart-widget'
|
|
2
|
-
export { default as
|
|
3
|
-
export { default as
|
|
4
|
-
export { default as
|
|
5
|
-
export { default as
|
|
6
|
-
|
|
7
|
-
export { default as
|
|
8
|
-
export { default as
|
|
9
|
-
|
|
1
|
+
export { default as AddToCartWidget } from './buy/add-to-cart-widget'
|
|
2
|
+
export { default as BuyTriggerWrapper } from './buy/buy-trigger-wrapper'
|
|
3
|
+
export { default as BuyButton } from './buy/buy-button'
|
|
4
|
+
export { default as BuyCard } from './buy/buy-card'
|
|
5
|
+
export { default as BuyDrawer } from './buy/buy-drawer'
|
|
6
|
+
|
|
7
|
+
export { default as CartAccordian } from './cart/cart-accordian'
|
|
8
|
+
export { default as CartPanel } from './cart/cart-panel'
|
|
9
|
+
|
|
10
10
|
export { default as FacetValuesWidget } from './facet-values-widget'
|
|
11
|
-
|
|
12
|
-
export { default as PaymentStepForm } from './payment-step-form'
|
|
13
|
-
export { default as
|
|
14
|
-
|
|
15
|
-
export { default as
|
|
16
|
-
export {
|
|
17
|
-
|
|
18
|
-
export {
|
|
11
|
+
|
|
12
|
+
export { default as PaymentStepForm } from './checkout/payment-step-form'
|
|
13
|
+
export { default as ShippingStepForm } from './checkout/shipping-step-form'
|
|
14
|
+
|
|
15
|
+
export { default as ProductCard } from './item/product-card'
|
|
16
|
+
export { Icons } from './Icons'
|
|
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'
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import Spline from '@splinetool/react-spline'
|
|
4
|
+
|
|
5
|
+
import type { Dimensions } from '@hanzo/ui/types'
|
|
6
|
+
import { cn } from '@hanzo/ui/util'
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
VideoBlockComponent,
|
|
10
|
+
ImageBlockComponent,
|
|
11
|
+
type ImageBlock,
|
|
12
|
+
type Block,
|
|
13
|
+
type VideoBlock
|
|
14
|
+
} from '@hanzo/ui/blocks'
|
|
15
|
+
import type { LineItem } from '../../types'
|
|
16
|
+
|
|
17
|
+
const ItemMedia: React.FC<{
|
|
18
|
+
item: LineItem
|
|
19
|
+
constrainTo?: Dimensions
|
|
20
|
+
clx?: string
|
|
21
|
+
}> = ({
|
|
22
|
+
item,
|
|
23
|
+
constrainTo: cnst = {w: 250, h: 250},
|
|
24
|
+
clx=''
|
|
25
|
+
}) => {
|
|
26
|
+
|
|
27
|
+
const {title, img, video, animation} = item
|
|
28
|
+
// Order of precedence: 3D > MP4 > Image
|
|
29
|
+
return animation ? (
|
|
30
|
+
<Spline
|
|
31
|
+
scene={animation}
|
|
32
|
+
className={cn(clx, 'pointer-events-none')}
|
|
33
|
+
style={{
|
|
34
|
+
// // !aspect-[12/10]
|
|
35
|
+
width: (6/5 * (typeof cnst.h === 'number' ? cnst.h as number : parseInt(cnst.h as string)) ),
|
|
36
|
+
height: cnst.h
|
|
37
|
+
}}
|
|
38
|
+
/>
|
|
39
|
+
) : video ? (
|
|
40
|
+
<VideoBlockComponent
|
|
41
|
+
constraint={cnst}
|
|
42
|
+
block={{blockType: 'video', ...video} satisfies VideoBlock as Block}
|
|
43
|
+
className={clx}
|
|
44
|
+
/>
|
|
45
|
+
) : (
|
|
46
|
+
<ImageBlockComponent
|
|
47
|
+
block={{
|
|
48
|
+
blockType: 'image',
|
|
49
|
+
src: img ?? '',
|
|
50
|
+
alt: title + ' image',
|
|
51
|
+
dim: cnst
|
|
52
|
+
} satisfies ImageBlock as Block}
|
|
53
|
+
className={cn('m-auto', clx)}
|
|
54
|
+
/>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export default ItemMedia
|
|
@@ -2,22 +2,20 @@ import React from 'react'
|
|
|
2
2
|
import Image from 'next/image'
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
-
AspectRatio,
|
|
6
5
|
Card,
|
|
7
6
|
CardContent,
|
|
8
|
-
CardDescription,
|
|
9
7
|
CardFooter,
|
|
10
8
|
CardHeader,
|
|
11
9
|
CardTitle,
|
|
12
10
|
} from '@hanzo/ui/primitives'
|
|
13
|
-
|
|
11
|
+
|
|
14
12
|
import { cn } from '@hanzo/ui/util'
|
|
15
13
|
|
|
16
|
-
import { formatCurrencyValue } from '
|
|
17
|
-
import type { LineItem } from '
|
|
18
|
-
import { Icons } from '
|
|
14
|
+
import { formatCurrencyValue } from '../../util'
|
|
15
|
+
import type { LineItem } from '../../types'
|
|
16
|
+
import { Icons } from '../Icons'
|
|
19
17
|
|
|
20
|
-
import AddToCartWidget from '
|
|
18
|
+
import AddToCartWidget from '../buy/add-to-cart-widget'
|
|
21
19
|
|
|
22
20
|
interface ProductCardProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
23
21
|
item: LineItem
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import Spline from '@splinetool/react-spline'
|
|
4
|
+
|
|
5
|
+
import type { Dimensions } from '@hanzo/ui/types'
|
|
6
|
+
import { cn } from '@hanzo/ui/util'
|
|
7
|
+
import ItemMedia from '../item/item-media'
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
type CarouselOptionsType,
|
|
11
|
+
Carousel,
|
|
12
|
+
CarouselContent,
|
|
13
|
+
CarouselItem,
|
|
14
|
+
CarouselPrevious,
|
|
15
|
+
CarouselNext
|
|
16
|
+
} from '@hanzo/ui/primitives'
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
VideoBlockComponent,
|
|
20
|
+
ImageBlockComponent,
|
|
21
|
+
type ImageBlock,
|
|
22
|
+
type Block,
|
|
23
|
+
type VideoBlock
|
|
24
|
+
} from '@hanzo/ui/blocks'
|
|
25
|
+
|
|
26
|
+
import type { ItemSelectorProps } from '../../types'
|
|
27
|
+
|
|
28
|
+
interface CarouselItemSelectorPropsExt {
|
|
29
|
+
constrainTo: Dimensions
|
|
30
|
+
options?: CarouselOptionsType
|
|
31
|
+
noSelection?: boolean // "display only" mode
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const CarouselItemSelector: React.FC<ItemSelectorProps> = ({
|
|
35
|
+
items,
|
|
36
|
+
clx='',
|
|
37
|
+
itemClx='',
|
|
38
|
+
ext={
|
|
39
|
+
options: {},
|
|
40
|
+
constrainTo: {w: 250, h: 250}
|
|
41
|
+
} satisfies CarouselItemSelectorPropsExt
|
|
42
|
+
}) => {
|
|
43
|
+
|
|
44
|
+
const { options, constrainTo} = ext
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<Carousel options={options} className={cn('px-2', clx)} >
|
|
48
|
+
<CarouselContent>
|
|
49
|
+
{items.map((item, index) => (
|
|
50
|
+
<CarouselItem key={index} className={cn('p-2 flex flex-row justify-center items-center', itemClx)}>
|
|
51
|
+
<ItemMedia item={item} constrainTo={constrainTo} clx='' />
|
|
52
|
+
</CarouselItem>
|
|
53
|
+
))}
|
|
54
|
+
</CarouselContent>
|
|
55
|
+
<CarouselPrevious className='left-1'/>
|
|
56
|
+
<CarouselNext className='right-1'/>
|
|
57
|
+
</Carousel>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export {
|
|
62
|
+
type CarouselItemSelectorPropsExt,
|
|
63
|
+
CarouselItemSelector as default
|
|
64
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import Image from 'next/image'
|
|
4
|
+
import { observer } from 'mobx-react-lite'
|
|
5
|
+
|
|
6
|
+
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
|
|
7
|
+
import { Label, RadioGroup, RadioGroupItem } from '@hanzo/ui/primitives'
|
|
8
|
+
import type { ItemSelectorProps, LineItem } from '../../types'
|
|
9
|
+
import { formatCurrencyValue } from '../../util'
|
|
10
|
+
import { cn } from '@hanzo/ui/util'
|
|
11
|
+
import type { Dimensions } from '@hanzo/ui/types'
|
|
12
|
+
|
|
13
|
+
const ImageRadioGroupItem = React.forwardRef<
|
|
14
|
+
React.ElementRef<typeof RadioGroupPrimitive.Item>,
|
|
15
|
+
Omit<React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>, 'value' | 'id'> & {
|
|
16
|
+
item: LineItem,
|
|
17
|
+
imgSizePx: number
|
|
18
|
+
}
|
|
19
|
+
>(({
|
|
20
|
+
item,
|
|
21
|
+
imgSizePx,
|
|
22
|
+
className,
|
|
23
|
+
...props
|
|
24
|
+
}, ref) => {
|
|
25
|
+
|
|
26
|
+
let dim: Dimensions
|
|
27
|
+
if (item.imgAR) {
|
|
28
|
+
if (item.imgAR >= 1) {
|
|
29
|
+
dim = {
|
|
30
|
+
w: imgSizePx,
|
|
31
|
+
h: imgSizePx / item.imgAR
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
dim = {
|
|
36
|
+
w: imgSizePx * item.imgAR,
|
|
37
|
+
h: imgSizePx
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
dim = {
|
|
43
|
+
w: imgSizePx,
|
|
44
|
+
h: imgSizePx
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<RadioGroupPrimitive.Item
|
|
50
|
+
ref={ref}
|
|
51
|
+
className={cn(
|
|
52
|
+
"ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
|
53
|
+
className
|
|
54
|
+
)}
|
|
55
|
+
{...props}
|
|
56
|
+
id={item.sku}
|
|
57
|
+
value={item.sku}
|
|
58
|
+
>
|
|
59
|
+
{!!item.img ? (
|
|
60
|
+
<Image src={item.img} alt={item.title + ' image'} height={dim.h} width={dim.w} className=''/>
|
|
61
|
+
) : ( // placeholder so things align
|
|
62
|
+
<div style={{height: dim.h, width: dim.w}}/>
|
|
63
|
+
)}
|
|
64
|
+
</RadioGroupPrimitive.Item>
|
|
65
|
+
)
|
|
66
|
+
})
|
|
67
|
+
ImageRadioGroupItem.displayName = 'ImageRadioGroupItem'
|
|
68
|
+
|
|
69
|
+
const ImageItemSelector: React.FC<ItemSelectorProps> = observer(({
|
|
70
|
+
items,
|
|
71
|
+
selectedItemRef: itemRef,
|
|
72
|
+
selectSku,
|
|
73
|
+
clx='',
|
|
74
|
+
itemClx='',
|
|
75
|
+
soleItemClx='',
|
|
76
|
+
showPrice=true,
|
|
77
|
+
showQuantity=true
|
|
78
|
+
}) => {
|
|
79
|
+
|
|
80
|
+
const Choice: React.FC<{
|
|
81
|
+
item: LineItem
|
|
82
|
+
className?: string
|
|
83
|
+
}> = ({
|
|
84
|
+
item,
|
|
85
|
+
className=''
|
|
86
|
+
}) => (
|
|
87
|
+
<div className={cn('flex items-center mb-1', className, itemClx)}>
|
|
88
|
+
<ImageRadioGroupItem item={item} imgSizePx={40} className='mr-2 border-2 border-transparent rounded-sm data-[state=checked]:border-foreground'/>
|
|
89
|
+
<Label htmlFor={item.sku}>{item.titleAsOption + (showPrice ? (', ' + formatCurrencyValue(item.price)) : '')}</Label>
|
|
90
|
+
</div>
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
const SoleChoice: React.FC<{
|
|
94
|
+
item: LineItem
|
|
95
|
+
className?: string
|
|
96
|
+
}> = ({
|
|
97
|
+
item,
|
|
98
|
+
className=''
|
|
99
|
+
}) => (
|
|
100
|
+
<div className={cn(className, soleItemClx)}>
|
|
101
|
+
{item.titleAsOption + (showPrice ? (', ' + formatCurrencyValue(item.price)) : '')}
|
|
102
|
+
</div>
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
return items.length > 1 ? (
|
|
106
|
+
<RadioGroup
|
|
107
|
+
className={cn('gap-0', showQuantity ? 'table' : '', clx)}
|
|
108
|
+
onValueChange={selectSku}
|
|
109
|
+
value={itemRef.item ? itemRef.item.sku : ''}
|
|
110
|
+
>
|
|
111
|
+
{items.map((item) => (showQuantity ? (
|
|
112
|
+
<div className='table-row' key={item.sku}>
|
|
113
|
+
<Choice item={item} className='table-cell pr-2 align-text-top pb-3'/>
|
|
114
|
+
<div className='table-cell font-semibold text-sm leading-none align-text-top pb-3'>{ item.quantity > 0 ? `(${item.quantity})` : ' '}</div>
|
|
115
|
+
</div>
|
|
116
|
+
) : (
|
|
117
|
+
<Choice item={item} className='mb-3' key={item.sku}/>
|
|
118
|
+
)))}
|
|
119
|
+
</RadioGroup>
|
|
120
|
+
) : (
|
|
121
|
+
<SoleChoice item={items[0]} className=''/>
|
|
122
|
+
)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
export default ImageItemSelector
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { observer } from 'mobx-react-lite'
|
|
4
|
+
|
|
5
|
+
import { Label, RadioGroup, RadioGroupItem } from '@hanzo/ui/primitives'
|
|
6
|
+
import type { ItemSelectorProps, LineItem } from '../../types'
|
|
7
|
+
import { formatCurrencyValue } from '../../util'
|
|
8
|
+
import { cn } from '@hanzo/ui/util'
|
|
9
|
+
|
|
10
|
+
const RadioItemSelector: React.FC<ItemSelectorProps> = observer(({
|
|
11
|
+
items,
|
|
12
|
+
selectedItemRef: itemRef,
|
|
13
|
+
selectSku,
|
|
14
|
+
clx='',
|
|
15
|
+
itemClx='',
|
|
16
|
+
soleItemClx='',
|
|
17
|
+
showPrice=true,
|
|
18
|
+
showQuantity=true
|
|
19
|
+
}) => {
|
|
20
|
+
|
|
21
|
+
const Choice: React.FC<{
|
|
22
|
+
item: LineItem
|
|
23
|
+
className?: string
|
|
24
|
+
}> = ({
|
|
25
|
+
item,
|
|
26
|
+
className=''
|
|
27
|
+
}) => (
|
|
28
|
+
<div className={cn(className, itemClx)}>
|
|
29
|
+
<RadioGroupItem value={item.sku} id={item.sku} className='mr-2'/>
|
|
30
|
+
<Label htmlFor={item.sku}>{item.titleAsOption + (showPrice ? (', ' + formatCurrencyValue(item.price)) : '')}</Label>
|
|
31
|
+
</div>
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
const SoleChoice: React.FC<{
|
|
35
|
+
item: LineItem
|
|
36
|
+
className?: string
|
|
37
|
+
}> = ({
|
|
38
|
+
item,
|
|
39
|
+
className=''
|
|
40
|
+
}) => (
|
|
41
|
+
<div className={cn(className, soleItemClx)}>
|
|
42
|
+
{item.titleAsOption + (showPrice ? (', ' + formatCurrencyValue(item.price)) : '')}
|
|
43
|
+
</div>
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
return items.length > 1 ? (
|
|
47
|
+
<RadioGroup
|
|
48
|
+
className={cn('gap-0', showQuantity ? 'table' : '', clx)}
|
|
49
|
+
onValueChange={selectSku}
|
|
50
|
+
value={itemRef.item ? itemRef.item.sku : ''}
|
|
51
|
+
>
|
|
52
|
+
{items.map((item) => (showQuantity ? (
|
|
53
|
+
<div className='table-row' key={item.sku}>
|
|
54
|
+
<Choice item={item} className='table-cell pr-2 align-text-top pb-2'/>
|
|
55
|
+
<div className='table-cell font-semibold text-sm leading-none align-text-top pb-2'>{ item.quantity > 0 ? `(${item.quantity})` : ' '}</div>
|
|
56
|
+
</div>
|
|
57
|
+
) : (
|
|
58
|
+
<Choice item={item} className='mb-2' key={item.sku}/>
|
|
59
|
+
)))}
|
|
60
|
+
</RadioGroup>
|
|
61
|
+
) : (
|
|
62
|
+
<SoleChoice item={items[0]} className=''/>
|
|
63
|
+
)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
export default RadioItemSelector
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/commerce",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"description": "Library with shopping cart components.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/",
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"@hanzo/auth": "^2.3.3",
|
|
43
43
|
"@hanzo/ui": "^3.1.0",
|
|
44
44
|
"@hookform/resolvers": "^3.3.4",
|
|
45
|
+
"@radix-ui/react-radio-group": "^1.1.3",
|
|
45
46
|
"firebase": "^10.8.0",
|
|
46
47
|
"lucide-react": "^0.307.0",
|
|
47
48
|
"mobx": "^6.12.0",
|
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 { Category,
|
|
12
|
+
import type { Category, ProductTreeNode } from '../types'
|
|
13
13
|
|
|
14
14
|
const CommerceServiceContext = createContext<CommerceService | undefined>(undefined)
|
|
15
15
|
|
|
@@ -20,17 +20,17 @@ const useCommerce = (): CommerceService => {
|
|
|
20
20
|
|
|
21
21
|
const CommerceServiceProvider: React.FC<PropsWithChildren & {
|
|
22
22
|
productsByCategory: Category[]
|
|
23
|
-
|
|
23
|
+
rootNode: ProductTreeNode
|
|
24
24
|
options?: ServiceOptions
|
|
25
25
|
}> = ({
|
|
26
26
|
children,
|
|
27
27
|
productsByCategory,
|
|
28
|
-
|
|
28
|
+
rootNode,
|
|
29
29
|
options
|
|
30
30
|
}) => {
|
|
31
31
|
|
|
32
32
|
// TODO: Inject Promo fixture here
|
|
33
|
-
const serviceRef = useRef<CommerceService>(getServiceSingleton(productsByCategory,
|
|
33
|
+
const serviceRef = useRef<CommerceService>(getServiceSingleton(productsByCategory, rootNode, options))
|
|
34
34
|
return (
|
|
35
35
|
<CommerceServiceContext.Provider value={serviceRef.current}>
|
|
36
36
|
{children}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { enableStaticRendering } from 'mobx-react-lite'
|
|
2
2
|
|
|
3
|
-
import type { CommerceService, Category,
|
|
3
|
+
import type { CommerceService, Category, ProductTreeNode } from '../../../types'
|
|
4
4
|
import StandaloneService, {type StandaloneServiceOptions} from './standalone-service'
|
|
5
5
|
|
|
6
6
|
import { readSnapshot, listenAndWriteSnapshots } from './localStorage'
|
|
@@ -19,7 +19,7 @@ let instance: StandaloneService | undefined = undefined
|
|
|
19
19
|
|
|
20
20
|
export const getInstance = (
|
|
21
21
|
categories: Category[],
|
|
22
|
-
|
|
22
|
+
rootNode: ProductTreeNode,
|
|
23
23
|
options?: StandaloneServiceOptions
|
|
24
24
|
): CommerceService => {
|
|
25
25
|
|
|
@@ -31,7 +31,7 @@ export const getInstance = (
|
|
|
31
31
|
//_log("NEW INSTANCE FOR SERVER")
|
|
32
32
|
return new StandaloneService(
|
|
33
33
|
categories,
|
|
34
|
-
|
|
34
|
+
rootNode,
|
|
35
35
|
options
|
|
36
36
|
)
|
|
37
37
|
}
|
|
@@ -42,7 +42,7 @@ export const getInstance = (
|
|
|
42
42
|
const snapShot = readSnapshot()
|
|
43
43
|
instance = new StandaloneService(
|
|
44
44
|
categories,
|
|
45
|
-
|
|
45
|
+
rootNode,
|
|
46
46
|
options,
|
|
47
47
|
snapShot
|
|
48
48
|
)
|