@hanzo/commerce 6.1.13 → 6.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/components/buy/_to_deprecate_select-family-item-card.tsx +3 -4
  2. package/components/buy/_to_deprecate_select-family-item-panel.tsx +4 -5
  3. package/components/buy/add-to-cart-widget.tsx +43 -32
  4. package/components/buy/buy-button.tsx +1 -1
  5. package/components/buy/buy-card.tsx +17 -26
  6. package/components/buy/buy-drawer.tsx +6 -25
  7. package/components/buy/carousel-buy-card.tsx +145 -194
  8. package/components/buy/foo.txt +27 -0
  9. package/components/cart/cart-panel/cart-line-item.tsx +1 -1
  10. package/components/checkout/payment-step-form/contact-form.tsx +2 -2
  11. package/components/checkout/payment-step-form/methods/crypto.tsx +1 -2
  12. package/components/checkout/shipping-step-form.tsx +5 -5
  13. package/components/index.ts +1 -3
  14. package/components/item-selector/button.tsx +175 -0
  15. package/components/item-selector/carousel.tsx +162 -0
  16. package/components/item-selector/index.ts +5 -0
  17. package/components/quantity-indicator.tsx +48 -0
  18. package/components/select-family/family-carousel/index.tsx +47 -38
  19. package/components/select-family/family-carousel/slide.tsx +83 -0
  20. package/components/select-family/family-carousel/state.ts +83 -0
  21. package/components/select-family/level-nodes-widget/index.tsx +2 -2
  22. package/components/select-family/level-nodes-widget/node-image.tsx +2 -2
  23. package/index.ts +7 -7
  24. package/package.json +7 -4
  25. package/service/context.tsx +8 -7
  26. package/service/debug.ts +41 -0
  27. package/service/get-instance.ts +3 -0
  28. package/service/impls/standalone/actual-line-item.ts +5 -7
  29. package/service/impls/standalone/index.ts +11 -3
  30. package/service/impls/standalone/standalone-service.ts +133 -38
  31. package/service/path-utils.ts +26 -0
  32. package/service/sep.ts +7 -0
  33. package/types/category-node.ts +38 -0
  34. package/types/commerce-service.ts +44 -12
  35. package/types/family.ts +4 -1
  36. package/types/index.ts +3 -2
  37. package/types/item-selector.ts +29 -14
  38. package/types/product.ts +5 -1
  39. package/types/selection-ui-specifier.ts +36 -0
  40. package/types/token-separators.ts +7 -0
  41. package/util/error.ts +34 -0
  42. package/util/index.ts +4 -1
  43. package/util/product-media-accessor.ts +58 -0
  44. package/util/promo-codes.ts +1 -2
  45. package/util/selection-ui-specifiers.ts +34 -0
  46. package/util/use-sync-sku-param-w-current-item.ts +2 -2
  47. package/components/select-family/family-carousel/family-slide.tsx +0 -36
  48. package/components/select-product/carousel-selector.tsx +0 -112
  49. package/components/select-product/image-selector.tsx +0 -162
  50. package/components/select-product/radio-selector.tsx +0 -125
  51. package/service/impls/index.ts +0 -3
  52. package/types/buy-ui-spec.ts +0 -8
  53. package/types/tree-node.ts +0 -24
  54. package/util/buy-ui-conf.ts +0 -35
  55. package/util/sep.ts +0 -5
@@ -1,261 +1,212 @@
1
1
  'use client'
2
2
  import React, { useRef, useEffect, type ComponentType } from 'react'
3
- import { reaction, type IReactionDisposer } from 'mobx'
4
3
  import { observer } from 'mobx-react-lite'
5
4
 
6
5
  import { cn } from '@hanzo/ui/util'
6
+ import { ApplyTypography, Button, MediaStack, Skeleton } from '@hanzo/ui/primitives'
7
7
 
8
8
  import type {
9
- SelectedPaths,
10
9
  ItemSelectorProps,
11
- ItemSelectorCompProps,
12
10
  LineItem,
13
11
  Family,
14
- ProductTreeNode,
12
+ CategoryNode,
13
+ CategoryNodeRole,
14
+ SelectionUISpecifier,
15
+ ItemSelectorOptions,
15
16
  } from '../../types'
16
17
 
17
18
  import { useCommerce } from '../../service/context'
18
- import SEP from '../../util/sep'
19
- import { ObsStringMutator } from '../../util'
19
+ import * as pathUtils from '../../service/path-utils'
20
+ import { ObsStringMutator, getSelectionUISpecifier } from '../../util'
21
+
22
+ import { CarouselItemSelector, ButtonItemSelector } from '../item-selector'
23
+ import FamilyCarousel from '../select-family/family-carousel'
20
24
 
21
25
  import AddToCartWidget from './add-to-cart-widget'
22
- import { ApplyTypography, MediaStack } from '@hanzo/ui/primitives'
23
26
 
24
- const CarouselBuyCard: React.FC<{
25
- skuPath: string
27
+ const SCROLL = {
28
+ scrollAfter: 5,
29
+ scrollHeightClx: 'h-[65vh]'
30
+ }
26
31
 
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
32
+ const MEDIA_CONSTRAINT = {w: 200, h: 200}
36
33
 
34
+ const CarouselBuyCard: React.FC<{
35
+ skuPath: string
37
36
  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
37
  mobile?: boolean
38
+ handleCheckout: () => void
39
+ onQuantityChanged?: (sku: string, oldV: number, newV: number) => void
55
40
  }> = observer(({
56
41
  skuPath,
57
- scrollAfter=5,
58
- scrollHeightClx='h-[80vh]',
59
42
  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
43
  mobile=false,
44
+ handleCheckout,
73
45
  onQuantityChanged,
74
46
  }) => {
75
47
 
76
48
  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
49
 
50
+ const r = useRef<{
51
+ role: CategoryNodeRole
52
+ item: LineItem | undefined
53
+ family: Family | undefined
54
+ families: Family[] | undefined
55
+ node: CategoryNode
56
+ uiSpec: SelectionUISpecifier
57
+ single?: {
58
+ items: LineItem[]
59
+ Selector: ComponentType<ItemSelectorProps>
60
+ selOptions: ItemSelectorOptions | undefined
61
+ scrollable: boolean
62
+ showItemMedia: boolean
63
+ }
64
+ } | undefined>(undefined)
84
65
 
85
66
  useEffect(() => {
86
67
 
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!')
68
+ const peek = cmmc.peek(skuPath)
69
+ if (typeof peek === 'string') {
70
+ throw new Error(peek)
94
71
  }
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
- }
72
+ if (peek.role === 'non-outermost') {
73
+ throw new Error(`BuyCard: skuPath ${skuPath} isn't an outermost tree node or product family!`)
118
74
  }
119
75
 
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()!)
76
+ const uiSpec = getSelectionUISpecifier(skuPath)
124
77
 
125
- inst.current = {
126
- level,
127
- reqFamily,
128
- parentNode,
129
- disposers: []
78
+ r.current = {
79
+ ...peek,
80
+ node: peek.node!, // else exception was thrown.
81
+ uiSpec,
130
82
  }
131
83
 
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
- }
84
+ if (peek.role === 'single-family') {
148
85
 
149
- return () => {
150
- inst.current?.disposers.forEach((d) => {d()})
151
- }
86
+ const singleSpec = uiSpec.singleFamily
87
+ const Selector: ComponentType<ItemSelectorProps> =
88
+ (singleSpec?.type === 'buttons') ? ButtonItemSelector : CarouselItemSelector
89
+ const selOptions = singleSpec?.options
90
+ const items = peek.family!.products as LineItem[]
152
91
 
153
- }, [])
92
+ r.current.single = {
93
+ items,
94
+ Selector,
95
+ selOptions,
96
+ scrollable: !!(items.length > SCROLL.scrollAfter),
97
+ showItemMedia: singleSpec?.type !== 'carousel'
98
+ }
154
99
 
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)
100
+ const currItem = peek.item ?? peek.family!.products[0]
101
+ cmmc.setCurrentItem(currItem.sku)
102
+ }
103
+ else {
104
+ const initialFamily = peek.family ? peek.family : peek.families![0]
105
+ const currItem = peek.item ?? initialFamily.products[0]
106
+ cmmc.setCurrentItem(currItem.sku)
159
107
  }
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
108
 
109
+ }, [])
172
110
 
173
111
  const TitleArea: React.FC<{
174
112
  title: string
175
113
  byline?: string
176
114
  clx?: string
115
+ bylineClx?: string
177
116
  }> = ({
178
117
  title,
179
118
  byline,
180
- clx=''
119
+ clx='',
120
+ bylineClx=''
181
121
  }) => (
182
122
  <ApplyTypography className={clx}>
183
123
  <h3>{title}</h3>
184
- {byline && (<h6>{byline}</h6>)}
124
+ {byline && (<h6 className={bylineClx}>{byline}</h6>)}
185
125
  </ApplyTypography>
186
126
  )
187
127
 
188
- const scroll = !!(itemsToShow && itemsToShow.length > scrollAfter)
189
128
  return (
190
129
  <div className={cn(
191
- 'px-4 md:px-6 pt-3 pb-4 flex flex-col items-center min-h-[60vh]',
130
+ 'px-4 md:px-6 pt-3 pb-4 flex flex-col gap-4 items-center min-h-[40vh]',
192
131
  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
- )}
132
+ r.current?.single?.scrollable ? SCROLL.scrollHeightClx : 'h-auto'
133
+ )}>
134
+ {r.current?.single ? (<>
135
+ <TitleArea title={r.current?.family?.title ?? ''} clx=''/>
136
+ {(r.current.single.showItemMedia && cmmc.currentItem ) && (
137
+ <MediaStack
138
+ media={cmmc.currentItem}
139
+ constrainTo={MEDIA_CONSTRAINT}
140
+ clx={(r.current.single.scrollable ? 'shrink-0' : '')}
141
+ />
142
+ )}
143
+ {(r.current.single.showItemMedia && !cmmc.currentItem ) && (
144
+ <Skeleton className={'w-[200px] h-[200px] my-4 ' + (r.current.single.scrollable ? 'shrink-0' : '')}/>
145
+ )}
146
+ {r.current.single.items && (
147
+ <r.current.single.Selector
148
+ items={r.current.single.items}
149
+ selectedItemRef={cmmc}
150
+ selectSku={cmmc.setCurrentItem.bind(cmmc)}
151
+ scrollable={r.current.single.scrollable}
152
+ mobile={mobile}
153
+ options={r.current.single.selOptions}
154
+ />
155
+ )}
156
+ </>) : (<>
157
+ {r.current?.uiSpec.multiFamily?.showParentTitle && (
158
+ <TitleArea
159
+ title={r.current?.node.label ?? ''}
160
+ byline={r.current?.node.subNodesLabel}
161
+ clx={'mb-2 '}
162
+ bylineClx='!text-center font-bold text-muted mt-3'
163
+ />
164
+ )}
165
+ {r.current?.families && ( // safegaurd for first render etc.
166
+ <FamilyCarousel
167
+ families={r.current?.families}
168
+ clx='w-full'
169
+ slideOptions={r.current?.uiSpec.multiFamily?.slide.options}
170
+ mediaConstraint={MEDIA_CONSTRAINT}
171
+ mobile={mobile}
172
+ />
173
+ )}
174
+ </>)}
249
175
  {(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
- />
176
+ <div className={cn(
177
+ 'self-stretch mt-4 flex flex-col items-center gap-3',
178
+ (r.current?.single?.scrollable ? 'shrink-0' : '')
179
+ )}>
180
+ <AddToCartWidget
181
+ size='default'
182
+ item={cmmc.currentItem}
183
+ onQuantityChanged={onQuantityChanged}
184
+ variant={cmmc.cartEmpty ? 'primary' : 'outline'}
185
+ className='min-w-[160px] w-full sm:max-w-[320px]'
186
+ />
187
+ {!cmmc.cartEmpty && (
188
+ <Button
189
+ onClick={handleCheckout}
190
+ variant='primary'
191
+ rounded='lg'
192
+ className='min-w-[160px] w-full sm:max-w-[320px]'
193
+ >
194
+ Checkout
195
+ </Button>
196
+ )}
197
+ </div>
256
198
  )}
257
199
  </div >
258
200
  )
259
201
  })
260
202
 
203
+ /* Above family carousel
204
+ <TitleArea
205
+ title={r.current?.node.label ?? ''}
206
+ byline={r.current?.node.subNodesLabel}
207
+ clx={'mb-2 '}
208
+ bylineClx='!text-center font-bold text-muted mt-3'
209
+ />
210
+ */
211
+
261
212
  export default CarouselBuyCard
@@ -0,0 +1,27 @@
1
+ {/*
2
+ <LevelNodesWidget
3
+ className={cn(
4
+ 'grid gap-0 align-stretch justify-normal ' + `grid-cols-${inst.current.parentNode.subNodes!.length}`,
5
+ '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
6
+ (scroll ? 'shrink-0' : ''),
7
+ famWidgetClx
8
+ )}
9
+ mobile={mobile}
10
+ mutator={allVariants ?
11
+ {
12
+ get: () => (inst.current!.currentFamily.s),
13
+ set: setFamilyPath
14
+ }
15
+ :
16
+ getFacetValuesMutator(inst.current.level + 1, cmmc)
17
+ }
18
+ itemClx='flex-col h-auto gap-0 py-1 px-3'
19
+ buttonClx={
20
+ 'h-full ' +
21
+ '!border-level-3'
22
+ }
23
+ levelNodes={inst.current.parentNode.subNodes!}
24
+ show={familyTabAs}
25
+ />
26
+ */}
27
+ </>)}
@@ -61,7 +61,7 @@ const CartLineItem: React.FC<{
61
61
  </div>
62
62
  <div className='flex flex-row items-center justify-between w-full'>
63
63
  <div className='flex flex-row items-center'>
64
- <AddToCartWidget className='' item={item} size='xs' buttonClx='h-8 md:h-6' ghost/>
64
+ <AddToCartWidget className='' item={item} size='xs' buttonClx='h-8 md:h-6' variant='minimal'/>
65
65
  {item.quantity > 1 && (<span className='pl-2.5'>{'@' + formatCurrencyValue(item.price)}</span>)}
66
66
  </div>
67
67
  <div className='flex flex-row gap-1 items-center justify-end'>
@@ -23,7 +23,7 @@ const ContactForm: React.FC<{
23
23
  render={({ field }) => (
24
24
  <FormItem className='w-full'>
25
25
  <FormControl>
26
- <Input {...field} className='border-muted-4' placeholder='Full name'/>
26
+ <Input {...field} placeholder='Full name'/>
27
27
  </FormControl>
28
28
  <FormMessage className='py-0 space-y-0 !m-0'/>
29
29
  </FormItem>
@@ -35,7 +35,7 @@ const ContactForm: React.FC<{
35
35
  render={({ field }) => (
36
36
  <FormItem className='w-full'>
37
37
  <FormControl>
38
- <Input {...field} className='border-muted-4' placeholder='Email'/>
38
+ <Input {...field} placeholder='Email'/>
39
39
  </FormControl>
40
40
  <FormMessage className='py-0 space-y-0 !m-0'/>
41
41
  </FormItem>
@@ -178,7 +178,7 @@ const PayWithCrypto: React.FC<PaymentMethodComponentProps> = observer(({
178
178
  <div className='flex gap-2 grid grid-cols-3'>
179
179
  <Select onValueChange={(token) => {/*ONLY ETH setSelectedToken(token) */}} defaultValue='eth'>
180
180
  <SelectTrigger>
181
- <SelectValue defaultValue='eth' className='border-muted-4'/>
181
+ <SelectValue defaultValue='eth' />
182
182
  </SelectTrigger>
183
183
  <SelectContent>
184
184
  <SelectGroup>
@@ -192,7 +192,6 @@ const PayWithCrypto: React.FC<PaymentMethodComponentProps> = observer(({
192
192
  <Input
193
193
  value={amount ? amount/(10**18) : amount}
194
194
  contentEditable={false}
195
- className='border-muted-4'
196
195
  />
197
196
  <div className='relative flex items-center gap-2 -top-[32px] justify-end px-2 py-1 rounded-lg bg-muted-4 w-fit text-xs float-right mr-3'>
198
197
  <Eth height={10}/>
@@ -82,7 +82,7 @@ const ShippingStepForm: React.FC<CheckoutStepComponentProps> = ({
82
82
  render={({ field }) => (
83
83
  <FormItem className='space-y-1 w-full'>
84
84
  <FormControl>
85
- <Input {...field} className='border-muted-4' placeholder='Address line 1'/>
85
+ <Input {...field} placeholder='Address line 1'/>
86
86
  </FormControl>
87
87
  <FormMessage />
88
88
  </FormItem>
@@ -94,7 +94,7 @@ const ShippingStepForm: React.FC<CheckoutStepComponentProps> = ({
94
94
  render={({ field }) => (
95
95
  <FormItem className='space-y-1 w-full'>
96
96
  <FormControl>
97
- <Input {...field} className='border-muted-4' placeholder='Address line 2 (optional)'/>
97
+ <Input {...field} placeholder='Address line 2 (optional)'/>
98
98
  </FormControl>
99
99
  <FormMessage />
100
100
  </FormItem>
@@ -107,7 +107,7 @@ const ShippingStepForm: React.FC<CheckoutStepComponentProps> = ({
107
107
  render={({ field }) => (
108
108
  <FormItem className='space-y-1 w-full'>
109
109
  <FormControl>
110
- <Input {...field} className='border-muted-4' placeholder='Zip code'/>
110
+ <Input {...field} placeholder='Zip code'/>
111
111
  </FormControl>
112
112
  <FormMessage />
113
113
  </FormItem>
@@ -119,7 +119,7 @@ const ShippingStepForm: React.FC<CheckoutStepComponentProps> = ({
119
119
  render={({ field }) => (
120
120
  <FormItem className='space-y-1 w-full'>
121
121
  <FormControl>
122
- <Input {...field} className='border-muted-4' placeholder='City'/>
122
+ <Input {...field} placeholder='City'/>
123
123
  </FormControl>
124
124
  <FormMessage />
125
125
  </FormItem>
@@ -133,7 +133,7 @@ const ShippingStepForm: React.FC<CheckoutStepComponentProps> = ({
133
133
  render={({ field }) => (
134
134
  <FormItem className='space-y-1 w-full'>
135
135
  <FormControl>
136
- <Input {...field} className='border-muted-4' placeholder='State (optional)'/>
136
+ <Input {...field} placeholder='State (optional)'/>
137
137
  </FormControl>
138
138
  <FormMessage />
139
139
  </FormItem>
@@ -15,6 +15,4 @@ 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-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'
18
+ export * from './item-selector'