@hanzo/commerce 7.3.2 → 7.3.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/components/Icons.tsx +34 -34
- package/components/add-to-cart-widget.tsx +183 -183
- package/components/buy/buy-card.tsx +259 -259
- package/components/buy/carousel-buy-card.tsx +242 -242
- package/components/buy/multi-family/all-variants-carousel.tsx +261 -261
- package/components/buy/multi-family/family-carousel/index.tsx +77 -77
- package/components/buy/multi-family/family-carousel/slide.tsx +83 -83
- package/components/buy/multi-family/family-carousel/state.ts +87 -87
- package/components/buy/multi-family/index.ts +2 -2
- package/components/buy/single-family-selector.tsx +90 -90
- package/components/buy/title-and-byline.tsx +25 -25
- package/components/cart/cart-panel/cart-line-item.tsx +76 -76
- package/components/cart/cart-panel/index.tsx +154 -154
- package/components/cart/cart-panel/promo-code.tsx +106 -106
- package/components/cart/cart-panel/total-area.tsx +60 -60
- package/components/checkout/payment-step-form/card-icon-row.tsx +26 -26
- package/components/checkout/payment-step-form/card-icons/amex.tsx +32 -32
- package/components/checkout/payment-step-form/card-icons/diners-club.tsx +13 -13
- package/components/checkout/payment-step-form/card-icons/discover.tsx +25 -25
- package/components/checkout/payment-step-form/card-icons/jcb.tsx +26 -26
- package/components/checkout/payment-step-form/card-icons/mastercard.tsx +27 -27
- package/components/checkout/payment-step-form/card-icons/visa.tsx +25 -25
- package/components/checkout/payment-step-form/cc-button.tsx +17 -17
- package/components/checkout/payment-step-form/contact-form.tsx +49 -49
- package/components/checkout/payment-step-form/crypto-icons/btc.tsx +11 -11
- package/components/checkout/payment-step-form/crypto-icons/eth.tsx +20 -20
- package/components/checkout/payment-step-form/crypto-icons/usdt.tsx +13 -13
- package/components/checkout/payment-step-form/index.tsx +122 -122
- package/components/checkout/payment-step-form/methods/bank-transfer.tsx +79 -79
- package/components/checkout/payment-step-form/methods/card.tsx +232 -232
- package/components/checkout/payment-step-form/methods/crypto.tsx +227 -227
- package/components/checkout/payment-step-form/methods/index.ts +22 -22
- package/components/checkout/shipping-step-form.tsx +172 -172
- package/components/index.ts +11 -11
- package/components/item/product-card.tsx +48 -48
- package/components/item-selector/button.tsx +188 -188
- package/components/item-selector/carousel/index.tsx +197 -197
- package/components/item-selector/carousel/slider.tsx +40 -40
- package/components/item-selector/index.ts +5 -5
- package/components/item-selector/quantity-indicator.tsx +48 -48
- package/components/node-tabs/index.tsx +91 -91
- package/components/node-tabs/node-image.tsx +31 -31
- package/index.ts +12 -12
- package/package.json +60 -60
- package/service/context.tsx +45 -45
- package/service/debug.ts +41 -41
- package/service/impls/standalone/actual-line-item.ts +135 -135
- package/service/impls/standalone/get-instance.ts +64 -64
- package/service/impls/standalone/index.ts +579 -579
- package/service/impls/standalone/order/firebase.ts +14 -14
- package/service/impls/standalone/order/index.ts +128 -128
- package/service/impls/standalone/persistence.ts +33 -33
- package/service/path-utils.ts +25 -25
- package/service/sep.ts +6 -6
- package/tsconfig.json +10 -10
- package/types/README.md +1 -1
- package/types/category-node.ts +49 -49
- package/types/checkout.ts +46 -46
- package/types/commerce-config.ts +13 -13
- package/types/commerce-service.ts +127 -127
- package/types/family.ts +25 -25
- package/types/index.ts +15 -15
- package/types/item-selector.ts +96 -96
- package/types/line-item.ts +29 -29
- package/types/multi-family-selector-props.ts +19 -19
- package/types/product.ts +20 -20
- package/types/promo.ts +9 -9
- package/types/selection-ui-specifier.ts +51 -51
- package/types/string-mutator.ts +13 -13
- package/types/token-separators.ts +7 -7
- package/util/analytics.ts +20 -20
- package/util/countries.ts +195 -195
- package/util/error.ts +34 -34
- package/util/index.ts +70 -70
- package/util/item-selector-options-accessor.ts +34 -34
- package/util/line-item-ref.ts +23 -23
- package/util/multi-family-selector-options-accessor.ts +14 -14
- package/util/obs-string-mutator.ts +22 -22
- package/util/product-media-accessor.ts +58 -58
- package/util/promo-codes.ts +108 -108
- package/util/selection-ui-specifiers.ts +29 -29
- package/util/square-payment.ts +42 -42
- package/util/use-sync-sku-param-w-current-item.ts +88 -88
|
@@ -1,83 +1,83 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
import React from 'react'
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
ApplyTypography,
|
|
6
|
-
MediaStack
|
|
7
|
-
} from '@hanzo/ui/primitives'
|
|
8
|
-
import type { Dimensions } from '@hanzo/ui/types'
|
|
9
|
-
|
|
10
|
-
import type { ItemSelectorOptions, Family, LineItem, ItemSelector } from '../../../../types'
|
|
11
|
-
import { ButtonItemSelector } from '../../..'
|
|
12
|
-
import { cn } from '@hanzo/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
|
|
1
|
+
'use client'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
ApplyTypography,
|
|
6
|
+
MediaStack
|
|
7
|
+
} from '@hanzo/ui/primitives'
|
|
8
|
+
import type { Dimensions } from '@hanzo/ui/types'
|
|
9
|
+
|
|
10
|
+
import type { ItemSelectorOptions, Family, LineItem, ItemSelector } from '../../../../types'
|
|
11
|
+
import { ButtonItemSelector } from '../../..'
|
|
12
|
+
import { cn } from '@hanzo/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
|
|
@@ -1,87 +1,87 @@
|
|
|
1
|
-
import type { Family, LineItem, ObsLineItemRef, Product } from '../../../../types'
|
|
2
|
-
import { observable, action, computed, makeObservable } from 'mobx'
|
|
3
|
-
|
|
4
|
-
class SlideState implements ObsLineItemRef {
|
|
5
|
-
|
|
6
|
-
_fam: Family
|
|
7
|
-
_item: Product
|
|
8
|
-
_syncSku: (sku: string) => void
|
|
9
|
-
_selected: boolean
|
|
10
|
-
|
|
11
|
-
constructor(fam: Family, initiallySelected: boolean, sync: (sku: string) => void) {
|
|
12
|
-
this._fam = fam
|
|
13
|
-
this._item = fam.products[0]
|
|
14
|
-
this._selected = initiallySelected
|
|
15
|
-
this._syncSku = sync
|
|
16
|
-
|
|
17
|
-
makeObservable(this, {
|
|
18
|
-
_item: observable,
|
|
19
|
-
item: computed,
|
|
20
|
-
selectSku: action
|
|
21
|
-
})
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
selectSku = (sku: string) => {
|
|
25
|
-
if (this._item.sku === sku) return;
|
|
26
|
-
const found = this._fam.products.find((i) => (i.sku === sku))
|
|
27
|
-
if (found) {
|
|
28
|
-
this._item = found
|
|
29
|
-
if (this._selected) {
|
|
30
|
-
this._syncSku(sku)
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
throw new Error('SlideState.selectSku(): no item found!')
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
setSelected = (b: boolean) => {this._selected = b}
|
|
39
|
-
|
|
40
|
-
get id(): string {return (this._fam.id)}
|
|
41
|
-
|
|
42
|
-
get item(): LineItem | undefined {
|
|
43
|
-
return this._item as LineItem
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
class FamilyCarouselState {
|
|
48
|
-
|
|
49
|
-
_map = new Map<string, SlideState>()
|
|
50
|
-
_syncSku: (sku: string) => void
|
|
51
|
-
|
|
52
|
-
constructor(
|
|
53
|
-
fams: Family[],
|
|
54
|
-
// initialFamilyId: string,
|
|
55
|
-
syncSkuToCurrentSlide: (sku: string) => void
|
|
56
|
-
) {
|
|
57
|
-
|
|
58
|
-
fams.forEach((f, index) => {this._map.set(f.id, new SlideState(
|
|
59
|
-
f,
|
|
60
|
-
(index === 0),
|
|
61
|
-
syncSkuToCurrentSlide
|
|
62
|
-
))})
|
|
63
|
-
this._syncSku = syncSkuToCurrentSlide
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
setCurrentFamily = (id: string) => {
|
|
67
|
-
|
|
68
|
-
this._map.forEach((s) => {
|
|
69
|
-
s.setSelected(s.id === id)
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
const slideState = this._map.get(id)
|
|
73
|
-
if (slideState) {
|
|
74
|
-
this._syncSku(slideState.item!.sku)
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
throw new Error(`FamilyCarouselState.setCurrentFamily: no state object for id '${id}'!`)
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
getSlideState = (familyId: string): SlideState | undefined => (this._map.get(familyId))
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export {
|
|
85
|
-
SlideState,
|
|
86
|
-
FamilyCarouselState
|
|
87
|
-
}
|
|
1
|
+
import type { Family, LineItem, ObsLineItemRef, Product } from '../../../../types'
|
|
2
|
+
import { observable, action, computed, makeObservable } from 'mobx'
|
|
3
|
+
|
|
4
|
+
class SlideState implements ObsLineItemRef {
|
|
5
|
+
|
|
6
|
+
_fam: Family
|
|
7
|
+
_item: Product
|
|
8
|
+
_syncSku: (sku: string) => void
|
|
9
|
+
_selected: boolean
|
|
10
|
+
|
|
11
|
+
constructor(fam: Family, initiallySelected: boolean, sync: (sku: string) => void) {
|
|
12
|
+
this._fam = fam
|
|
13
|
+
this._item = fam.products[0]
|
|
14
|
+
this._selected = initiallySelected
|
|
15
|
+
this._syncSku = sync
|
|
16
|
+
|
|
17
|
+
makeObservable(this, {
|
|
18
|
+
_item: observable,
|
|
19
|
+
item: computed,
|
|
20
|
+
selectSku: action
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
selectSku = (sku: string) => {
|
|
25
|
+
if (this._item.sku === sku) return;
|
|
26
|
+
const found = this._fam.products.find((i) => (i.sku === sku))
|
|
27
|
+
if (found) {
|
|
28
|
+
this._item = found
|
|
29
|
+
if (this._selected) {
|
|
30
|
+
this._syncSku(sku)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
throw new Error('SlideState.selectSku(): no item found!')
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
setSelected = (b: boolean) => {this._selected = b}
|
|
39
|
+
|
|
40
|
+
get id(): string {return (this._fam.id)}
|
|
41
|
+
|
|
42
|
+
get item(): LineItem | undefined {
|
|
43
|
+
return this._item as LineItem
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
class FamilyCarouselState {
|
|
48
|
+
|
|
49
|
+
_map = new Map<string, SlideState>()
|
|
50
|
+
_syncSku: (sku: string) => void
|
|
51
|
+
|
|
52
|
+
constructor(
|
|
53
|
+
fams: Family[],
|
|
54
|
+
// initialFamilyId: string,
|
|
55
|
+
syncSkuToCurrentSlide: (sku: string) => void
|
|
56
|
+
) {
|
|
57
|
+
|
|
58
|
+
fams.forEach((f, index) => {this._map.set(f.id, new SlideState(
|
|
59
|
+
f,
|
|
60
|
+
(index === 0),
|
|
61
|
+
syncSkuToCurrentSlide
|
|
62
|
+
))})
|
|
63
|
+
this._syncSku = syncSkuToCurrentSlide
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
setCurrentFamily = (id: string) => {
|
|
67
|
+
|
|
68
|
+
this._map.forEach((s) => {
|
|
69
|
+
s.setSelected(s.id === id)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
const slideState = this._map.get(id)
|
|
73
|
+
if (slideState) {
|
|
74
|
+
this._syncSku(slideState.item!.sku)
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
throw new Error(`FamilyCarouselState.setCurrentFamily: no state object for id '${id}'!`)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
getSlideState = (familyId: string): SlideState | undefined => (this._map.get(familyId))
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export {
|
|
85
|
+
SlideState,
|
|
86
|
+
FamilyCarouselState
|
|
87
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { default as FamilyCarousel } from './family-carousel'
|
|
2
|
-
export { default as AllVariantsCarousel } from './all-variants-carousel'
|
|
1
|
+
export { default as FamilyCarousel } from './family-carousel'
|
|
2
|
+
export { default as AllVariantsCarousel } from './all-variants-carousel'
|
|
@@ -1,90 +1,90 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { observer } from 'mobx-react-lite'
|
|
3
|
-
|
|
4
|
-
import { MediaStack, Skeleton } from '@hanzo/ui/primitives'
|
|
5
|
-
import type { Dimensions } from '@hanzo/ui/types'
|
|
6
|
-
|
|
7
|
-
import { useCommerce } from '../..'
|
|
8
|
-
|
|
9
|
-
import type { ItemSelectorOptions, ItemSelectorProps, LineItem } from '../../types'
|
|
10
|
-
import TitleAndByline from './title-and-byline'
|
|
11
|
-
import { accessItemOptions } from '../../util'
|
|
12
|
-
import { cn } from '@hanzo/ui/util'
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const DEF_MEDIA_CONSTRAINT = {w: 200, h: 200}
|
|
16
|
-
|
|
17
|
-
const SingleFamilySelector: React.FC<{
|
|
18
|
-
items: LineItem[] // these may be sorted, so are we can't just use currentFamily.products
|
|
19
|
-
Selector: React.ComponentType<ItemSelectorProps>
|
|
20
|
-
selOptions: ItemSelectorOptions | undefined
|
|
21
|
-
scrollable?: boolean
|
|
22
|
-
showItemMedia?: boolean
|
|
23
|
-
mediaConstraint?: Dimensions
|
|
24
|
-
mobile?: boolean
|
|
25
|
-
clx?: string
|
|
26
|
-
titleAreaClx?: string
|
|
27
|
-
}> = ({
|
|
28
|
-
items,
|
|
29
|
-
Selector,
|
|
30
|
-
selOptions,
|
|
31
|
-
scrollable=false,
|
|
32
|
-
showItemMedia=true,
|
|
33
|
-
mediaConstraint=DEF_MEDIA_CONSTRAINT,
|
|
34
|
-
mobile=false,
|
|
35
|
-
clx='',
|
|
36
|
-
titleAreaClx=''
|
|
37
|
-
}) => {
|
|
38
|
-
|
|
39
|
-
const cmmc = useCommerce()
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const MyTitleAndByline: React.FC<{clx?: string}> = observer(({
|
|
43
|
-
clx=''
|
|
44
|
-
}) => {
|
|
45
|
-
const { familyTitle, showFamilyByline } = accessItemOptions(selOptions)
|
|
46
|
-
const title = (familyTitle === 'none' ?
|
|
47
|
-
undefined
|
|
48
|
-
:
|
|
49
|
-
(familyTitle === 'long' ?
|
|
50
|
-
cmmc.currentFamily?.title
|
|
51
|
-
:
|
|
52
|
-
(cmmc.currentFamily?.titleShort ?? cmmc.currentFamily?.title)
|
|
53
|
-
)
|
|
54
|
-
)
|
|
55
|
-
const byline = (familyTitle !== 'none') && showFamilyByline ? cmmc.currentFamily?.byline : undefined
|
|
56
|
-
return ( <TitleAndByline clx={clx} title={title} byline={byline}/> )
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
const ItemMedia: React.FC = observer(() => (
|
|
60
|
-
cmmc.currentItem ? (
|
|
61
|
-
<MediaStack
|
|
62
|
-
media={cmmc.currentItem!}
|
|
63
|
-
constrainTo={mediaConstraint}
|
|
64
|
-
clx={'mb-2 ' + (scrollable ? 'shrink-0' : '')}
|
|
65
|
-
/>
|
|
66
|
-
) : (
|
|
67
|
-
<Skeleton className={'w-[200px] h-[200px] my-2 ' + (scrollable ? 'shrink-0' : '')}/>
|
|
68
|
-
)
|
|
69
|
-
))
|
|
70
|
-
|
|
71
|
-
return (
|
|
72
|
-
<div className={cn(clx, 'flex flex-col items-center', (scrollable ? 'shrink' : ''))}>
|
|
73
|
-
<MyTitleAndByline clx={cn(titleAreaClx, (scrollable ? 'shrink-0' : ''))} />
|
|
74
|
-
{showItemMedia && (<ItemMedia />)}
|
|
75
|
-
{items && (
|
|
76
|
-
<Selector
|
|
77
|
-
items={items}
|
|
78
|
-
selectedItemRef={cmmc}
|
|
79
|
-
selectSku={cmmc.setCurrentItem.bind(cmmc)}
|
|
80
|
-
scrollable={scrollable}
|
|
81
|
-
mobile={mobile}
|
|
82
|
-
options={selOptions}
|
|
83
|
-
clx={(scrollable ? 'shrink' : '')}
|
|
84
|
-
/>
|
|
85
|
-
)}
|
|
86
|
-
</div>
|
|
87
|
-
)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export default SingleFamilySelector
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { observer } from 'mobx-react-lite'
|
|
3
|
+
|
|
4
|
+
import { MediaStack, Skeleton } from '@hanzo/ui/primitives'
|
|
5
|
+
import type { Dimensions } from '@hanzo/ui/types'
|
|
6
|
+
|
|
7
|
+
import { useCommerce } from '../..'
|
|
8
|
+
|
|
9
|
+
import type { ItemSelectorOptions, ItemSelectorProps, LineItem } from '../../types'
|
|
10
|
+
import TitleAndByline from './title-and-byline'
|
|
11
|
+
import { accessItemOptions } from '../../util'
|
|
12
|
+
import { cn } from '@hanzo/ui/util'
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
const DEF_MEDIA_CONSTRAINT = {w: 200, h: 200}
|
|
16
|
+
|
|
17
|
+
const SingleFamilySelector: React.FC<{
|
|
18
|
+
items: LineItem[] // these may be sorted, so are we can't just use currentFamily.products
|
|
19
|
+
Selector: React.ComponentType<ItemSelectorProps>
|
|
20
|
+
selOptions: ItemSelectorOptions | undefined
|
|
21
|
+
scrollable?: boolean
|
|
22
|
+
showItemMedia?: boolean
|
|
23
|
+
mediaConstraint?: Dimensions
|
|
24
|
+
mobile?: boolean
|
|
25
|
+
clx?: string
|
|
26
|
+
titleAreaClx?: string
|
|
27
|
+
}> = ({
|
|
28
|
+
items,
|
|
29
|
+
Selector,
|
|
30
|
+
selOptions,
|
|
31
|
+
scrollable=false,
|
|
32
|
+
showItemMedia=true,
|
|
33
|
+
mediaConstraint=DEF_MEDIA_CONSTRAINT,
|
|
34
|
+
mobile=false,
|
|
35
|
+
clx='',
|
|
36
|
+
titleAreaClx=''
|
|
37
|
+
}) => {
|
|
38
|
+
|
|
39
|
+
const cmmc = useCommerce()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
const MyTitleAndByline: React.FC<{clx?: string}> = observer(({
|
|
43
|
+
clx=''
|
|
44
|
+
}) => {
|
|
45
|
+
const { familyTitle, showFamilyByline } = accessItemOptions(selOptions)
|
|
46
|
+
const title = (familyTitle === 'none' ?
|
|
47
|
+
undefined
|
|
48
|
+
:
|
|
49
|
+
(familyTitle === 'long' ?
|
|
50
|
+
cmmc.currentFamily?.title
|
|
51
|
+
:
|
|
52
|
+
(cmmc.currentFamily?.titleShort ?? cmmc.currentFamily?.title)
|
|
53
|
+
)
|
|
54
|
+
)
|
|
55
|
+
const byline = (familyTitle !== 'none') && showFamilyByline ? cmmc.currentFamily?.byline : undefined
|
|
56
|
+
return ( <TitleAndByline clx={clx} title={title} byline={byline}/> )
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
const ItemMedia: React.FC = observer(() => (
|
|
60
|
+
cmmc.currentItem ? (
|
|
61
|
+
<MediaStack
|
|
62
|
+
media={cmmc.currentItem!}
|
|
63
|
+
constrainTo={mediaConstraint}
|
|
64
|
+
clx={'mb-2 ' + (scrollable ? 'shrink-0' : '')}
|
|
65
|
+
/>
|
|
66
|
+
) : (
|
|
67
|
+
<Skeleton className={'w-[200px] h-[200px] my-2 ' + (scrollable ? 'shrink-0' : '')}/>
|
|
68
|
+
)
|
|
69
|
+
))
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<div className={cn(clx, 'flex flex-col items-center', (scrollable ? 'shrink' : ''))}>
|
|
73
|
+
<MyTitleAndByline clx={cn(titleAreaClx, (scrollable ? 'shrink-0' : ''))} />
|
|
74
|
+
{showItemMedia && (<ItemMedia />)}
|
|
75
|
+
{items && (
|
|
76
|
+
<Selector
|
|
77
|
+
items={items}
|
|
78
|
+
selectedItemRef={cmmc}
|
|
79
|
+
selectSku={cmmc.setCurrentItem.bind(cmmc)}
|
|
80
|
+
scrollable={scrollable}
|
|
81
|
+
mobile={mobile}
|
|
82
|
+
options={selOptions}
|
|
83
|
+
clx={(scrollable ? 'shrink' : '')}
|
|
84
|
+
/>
|
|
85
|
+
)}
|
|
86
|
+
</div>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export default SingleFamilySelector
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
|
|
3
|
-
import { ApplyTypography } from '@hanzo/ui/primitives'
|
|
4
|
-
import { cn } from '@hanzo/ui/util'
|
|
5
|
-
|
|
6
|
-
const TitleAndByline: React.FC<{
|
|
7
|
-
title: string | undefined
|
|
8
|
-
byline?: string | undefined
|
|
9
|
-
clx?: string
|
|
10
|
-
bylineClx?: string
|
|
11
|
-
}> = ({
|
|
12
|
-
title,
|
|
13
|
-
byline,
|
|
14
|
-
clx='',
|
|
15
|
-
bylineClx=''
|
|
16
|
-
}) => ( (title || byline) ? (
|
|
17
|
-
<ApplyTypography className={cn('flex flex-col items-center !gap-0 [&>*]:!m-0 ', clx)} >
|
|
18
|
-
<h4>{title}</h4>
|
|
19
|
-
{byline && (<h6 className={bylineClx}>{byline}</h6>)}
|
|
20
|
-
</ApplyTypography>
|
|
21
|
-
) : (null)
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
export default TitleAndByline
|
|
25
|
-
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import { ApplyTypography } from '@hanzo/ui/primitives'
|
|
4
|
+
import { cn } from '@hanzo/ui/util'
|
|
5
|
+
|
|
6
|
+
const TitleAndByline: React.FC<{
|
|
7
|
+
title: string | undefined
|
|
8
|
+
byline?: string | undefined
|
|
9
|
+
clx?: string
|
|
10
|
+
bylineClx?: string
|
|
11
|
+
}> = ({
|
|
12
|
+
title,
|
|
13
|
+
byline,
|
|
14
|
+
clx='',
|
|
15
|
+
bylineClx=''
|
|
16
|
+
}) => ( (title || byline) ? (
|
|
17
|
+
<ApplyTypography className={cn('flex flex-col items-center !gap-0 [&>*]:!m-0 ', clx)} >
|
|
18
|
+
<h4>{title}</h4>
|
|
19
|
+
{byline && (<h6 className={bylineClx}>{byline}</h6>)}
|
|
20
|
+
</ApplyTypography>
|
|
21
|
+
) : (null)
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
export default TitleAndByline
|
|
25
|
+
|