@hanzo/commerce 2.1.0 → 3.0.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 (51) hide show
  1. package/components/add-to-cart-widget.tsx +3 -3
  2. package/components/buy-item/buy-item-button.tsx +28 -0
  3. package/components/buy-item/buy-item-card.tsx +82 -0
  4. package/components/buy-item/buy-item-popup.tsx +38 -0
  5. package/components/{select-category-and-item-widget.tsx → buy-item/select-category-and-item-widget.tsx} +4 -4
  6. package/components/buy-item/select-category-item-card.tsx +111 -0
  7. package/components/{cart-line-item.tsx → cart-panel/cart-line-item.tsx} +4 -3
  8. package/components/{cart.tsx → cart-panel/index.tsx} +24 -18
  9. package/components/category-item-ios-wheel-selector.tsx +60 -0
  10. package/components/category-item-radio-selector.tsx +55 -0
  11. package/components/{checkout → checkout-panel}/confirm-order.tsx +3 -2
  12. package/components/{checkout → checkout-panel}/index.tsx +10 -5
  13. package/components/{checkout → checkout-panel}/payment/index.tsx +2 -1
  14. package/components/{checkout → checkout-panel}/payment/pay-with-card.tsx +8 -3
  15. package/components/{checkout → checkout-panel}/payment/pay-with-crypto.tsx +2 -3
  16. package/components/{facet-toggles-widget → facet-values-widget}/facet-image.tsx +7 -3
  17. package/components/{facet-toggles-widget → facet-values-widget}/index.tsx +7 -5
  18. package/components/index.ts +11 -9
  19. package/components/{select-item-in-category-view.tsx → select-category-item-panel.tsx} +72 -76
  20. package/index.ts +2 -2
  21. package/package.json +8 -3
  22. package/service/context.tsx +4 -4
  23. package/service/impls/standalone/index.ts +4 -4
  24. package/service/impls/standalone/standalone-service.ts +117 -38
  25. package/types/category.ts +3 -2
  26. package/types/commerce-service.ts +17 -4
  27. package/types/facet.ts +1 -3
  28. package/types/index.ts +3 -1
  29. package/types/item-selector.ts +14 -0
  30. package/util/index.ts +29 -0
  31. package/util/use-sync-sku-param-w-current-item.ts +4 -1
  32. package/blocks/components/commerce-cat-and-item-block.tsx +0 -13
  33. package/blocks/def/commerce-cat-and-item-block.ts +0 -9
  34. package/components/facets-widget.tsx +0 -55
  35. package/components/product-selection-mobile-picker.tsx +0 -78
  36. package/components/product-selection-radio-group.tsx +0 -38
  37. /package/components/{checkout → checkout-panel}/countries.ts +0 -0
  38. /package/components/{checkout → checkout-panel}/icons/btc.tsx +0 -0
  39. /package/components/{checkout → checkout-panel}/icons/eth.tsx +0 -0
  40. /package/components/{checkout → checkout-panel}/icons/usdt.tsx +0 -0
  41. /package/components/{checkout → checkout-panel}/payment/contact-info.tsx +0 -0
  42. /package/components/{checkout → checkout-panel}/payment/pay-with-bank-transfer.tsx +0 -0
  43. /package/components/{checkout → checkout-panel}/payment/payment-methods/amex.tsx +0 -0
  44. /package/components/{checkout → checkout-panel}/payment/payment-methods/diners-club.tsx +0 -0
  45. /package/components/{checkout → checkout-panel}/payment/payment-methods/discover.tsx +0 -0
  46. /package/components/{checkout → checkout-panel}/payment/payment-methods/index.tsx +0 -0
  47. /package/components/{checkout → checkout-panel}/payment/payment-methods/jcb.tsx +0 -0
  48. /package/components/{checkout → checkout-panel}/payment/payment-methods/mastercard.tsx +0 -0
  49. /package/components/{checkout → checkout-panel}/payment/payment-methods/visa.tsx +0 -0
  50. /package/components/{checkout → checkout-panel}/shipping-info.tsx +0 -0
  51. /package/components/{checkout → checkout-panel}/thank-you.tsx +0 -0
@@ -7,12 +7,14 @@ import {
7
7
  toJS
8
8
  } from 'mobx'
9
9
 
10
+ import { computedFn } from 'mobx-utils'
11
+
10
12
  import type {
11
13
  CommerceService,
12
14
  Category,
13
15
  LineItem,
14
16
  FacetsValue,
15
- FacetsDesc
17
+ FacetValueDesc
16
18
  } from '../../../types'
17
19
 
18
20
  import {
@@ -23,6 +25,8 @@ import {
23
25
 
24
26
  import ActualLineItem, { type ActualLineItemSnapshot } from './actual-line-item'
25
27
 
28
+ const SEP = '-'
29
+
26
30
  type StandaloneServiceOptions = {
27
31
  levelZeroPrefix?: string
28
32
  dbName: string
@@ -37,7 +41,7 @@ class StandaloneService
37
41
  implements CommerceService
38
42
  {
39
43
  private _categoryMap = new Map<string, Category>()
40
- private _facetsDesc: FacetsDesc
44
+ private _rootFacet: FacetValueDesc
41
45
  private _selectedFacets: FacetsValue = {}
42
46
 
43
47
  private _options : StandaloneServiceOptions
@@ -45,12 +49,12 @@ class StandaloneService
45
49
 
46
50
  constructor(
47
51
  categories: Category[],
48
- facets: FacetsDesc,
52
+ rootFacet: FacetValueDesc,
49
53
  options: StandaloneServiceOptions,
50
54
  serviceSnapshot?: StandaloneServiceSnapshot,
51
55
  ) {
52
56
 
53
- this._facetsDesc = facets
57
+ this._rootFacet = rootFacet
54
58
  this._options = options
55
59
 
56
60
  categories.forEach((c) => {
@@ -79,6 +83,7 @@ class StandaloneService
79
83
  cartItems: computed,
80
84
  cartQuantity: computed,
81
85
  cartTotal: computed,
86
+ cartEmpty: computed,
82
87
  specifiedItems: computed,
83
88
  specifiedCategories: computed,
84
89
  setCurrentItem: action,
@@ -89,6 +94,54 @@ class StandaloneService
89
94
  })
90
95
  }
91
96
 
97
+
98
+ getCategory(id: string): Category | undefined {
99
+ return this._categoryMap.get(id)
100
+ }
101
+
102
+ getFacetValuesAtSkuPath(skuPath: string): FacetValueDesc[] | undefined {
103
+ const toks = skuPath.split(SEP)
104
+ let level = 1
105
+ let valuesAtLevel: FacetValueDesc[] | undefined = this._rootFacet.sub
106
+ do {
107
+ const fvalue = valuesAtLevel!.find((vf) => (vf.value === toks[level]))
108
+ valuesAtLevel = fvalue ? fvalue.sub : undefined
109
+ level++
110
+ }
111
+ while (valuesAtLevel && (level < toks.length))
112
+ return level === toks.length ? valuesAtLevel : undefined
113
+ }
114
+
115
+ getFacetValuesSpecified = computedFn((level: number): FacetValueDesc[] | undefined => {
116
+
117
+ let lvl = 1
118
+ let valuesAtLevel: FacetValueDesc[] | undefined = this._rootFacet.sub
119
+
120
+ do {
121
+ let selectedAtLevel: FacetValueDesc[] | undefined = undefined
122
+ // If not specified, assume all
123
+ if (lvl in this._selectedFacets) {
124
+ selectedAtLevel = valuesAtLevel!.filter((fv) => (this._selectedFacets[lvl].includes(fv.value)))
125
+ }
126
+ else {
127
+ selectedAtLevel = valuesAtLevel
128
+ }
129
+ let allSubsOfSelected: FacetValueDesc[] = []
130
+ selectedAtLevel?.forEach((fvd: FacetValueDesc) => {
131
+ if (fvd.sub) {
132
+ allSubsOfSelected = [...allSubsOfSelected, ...fvd.sub]
133
+ }
134
+ })
135
+
136
+ valuesAtLevel = allSubsOfSelected
137
+ lvl++
138
+ } while (valuesAtLevel.length > 0 && lvl <= level)
139
+
140
+ return (valuesAtLevel.length > 0 && ((lvl - 1) === level)) ? valuesAtLevel : undefined
141
+ })
142
+
143
+
144
+ //async createOrder(email: string, paymentMethod: string): Promise<string | undefined> {
92
145
  async createOrder(email: string, name?: string): Promise<string | undefined> {
93
146
  const snapshot = this.takeSnapshot()
94
147
  const order = await createOrderHelper(email, snapshot.items, this._options, name) // didn't want to have two levels of 'items'
@@ -117,6 +170,10 @@ class StandaloneService
117
170
  return result.sort((it1, it2) => ((it1 as ActualLineItem).timeAdded - (it2 as ActualLineItem).timeAdded))
118
171
  }
119
172
 
173
+ get cartEmpty(): boolean {
174
+ return this.cartItems.length === 0
175
+ }
176
+
120
177
  get cartTotal(): number {
121
178
  return this.cartItems.reduce(
122
179
  (total, item) => (total + item.price * item.quantity),
@@ -163,7 +220,6 @@ class StandaloneService
163
220
  return !!this._currentItem
164
221
  }
165
222
 
166
-
167
223
  /* ObsLineItemRef */
168
224
  get item(): LineItem | undefined {
169
225
  return this._currentItem
@@ -175,10 +231,7 @@ class StandaloneService
175
231
 
176
232
  setFacets(sel: FacetsValue): Category[] {
177
233
  runInAction (() => {
178
- const res = this._processAndValidate(sel)
179
- if (res) {
180
- this._selectedFacets = res
181
- }
234
+ this._selectedFacets = this._processAndValidate(sel)
182
235
  })
183
236
  return this.specifiedCategories
184
237
  }
@@ -191,46 +244,72 @@ class StandaloneService
191
244
  return result
192
245
  }
193
246
 
247
+
194
248
  get specifiedCategories(): Category[] {
195
249
  if (Object.keys(toJS(this._selectedFacets)).length === 0) {
196
250
  // FacetsDesc have never been set or unset, so cannot evaluate them
197
251
  return []
198
252
  }
199
- const keysStr = Object.keys(this._facetsDesc)
200
- // 1-base, visiting two per iteration
201
- let current: string[] = this._selectedFacets[1]
202
- for (let i = 2; i <= keysStr.length; i++) {
203
- current = StandaloneService._visit(current, this._selectedFacets[i])
204
- }
205
- const prefix = this._options.levelZeroPrefix ?? ''
206
- return current.map((almostTheCatId) => (this._categoryMap.get(prefix + almostTheCatId)!))
253
+
254
+ return this._rootFacet.sub!.reduce(
255
+ (acc: Category[], subFacet: FacetValueDesc) => (
256
+ // Pass the root token as a one member array
257
+ this._reduceNode([this._rootFacet.value], acc, subFacet)
258
+ ),
259
+ []
260
+ )
207
261
  }
208
262
 
209
- private static _visit(current: string[], next: string[]): string[] {
210
- const result: string[] = []
211
- current.forEach((c) => {
212
- next.forEach((n) => {
213
- result.push(`${c}-${n}`)
214
- })
215
- })
216
- return result
263
+ private _reduceNode(parentPath: string[], acc: Category[], node: FacetValueDesc): Category[] {
264
+ const path = [...parentPath, node.value] // Don't mutate original please :)
265
+ const level = path.length - 1
266
+ // If there is no token array supplied for this level,
267
+ // assume all are specified. Otherwise, see if the
268
+ // current node is in the array
269
+ const specified = (
270
+ !this._selectedFacets[level]
271
+ ||
272
+ this._selectedFacets[level].includes(node.value)
273
+ )
274
+ if (specified) {
275
+ // Process subnodes
276
+ if (node.sub && node.sub.length > 0) {
277
+ return node.sub.reduce((acc, n) => (
278
+ this._reduceNode(path, acc, n)
279
+ )
280
+ , acc)
281
+ }
282
+ // Process leaf
283
+ const cat = this._categoryMap.get(path.join(SEP))
284
+ if (!cat) {
285
+ throw new Error("specifiedCategories WTF?!" + path.join(SEP))
286
+ }
287
+ acc.push(cat)
288
+ }
289
+ return acc
217
290
  }
218
291
 
219
- private _processAndValidate(partial: FacetsValue): FacetsValue | undefined {
292
+ private _processAndValidate(partial: FacetsValue): FacetsValue {
293
+
220
294
  const result: FacetsValue = {}
221
- const keysStr = Object.keys(this._facetsDesc)
222
- const keysNum = keysStr.map((key) => (parseInt(key)))
223
- keysNum.forEach((key) => {
224
- if (partial[key]) {
225
- // If present, filter out the bad values (the one's that don't exist in the Desc)
226
- const filtered = partial[key].filter((fv) => (this._facetsDesc[key].find((fvDesc) => (fvDesc.value === fv))))
227
- result[key] = filtered
228
- }
229
- // if not present, assume the facet is "off" and allow all (include all in the set).
230
- else {
231
- result[key] = this._facetsDesc[key].map((fv) => (fv.value))
295
+
296
+ let level = 1
297
+ let currentSet = this._rootFacet.sub!
298
+
299
+ while (true) {
300
+ let possibleCurrent = currentSet.map((el) => (el.value))
301
+ const validTokens = !partial[level] ? undefined : partial[level].filter((tok) => possibleCurrent.includes(tok))
302
+ if (!validTokens) {
303
+ break
232
304
  }
233
- })
305
+ result[level] = validTokens
306
+ currentSet = validTokens.map((tok) => {
307
+ const fd = currentSet.find((node) => ( node.value === tok ))
308
+ return (fd && fd.sub && fd.sub.length > 0) ? fd.sub : []
309
+ }).flat()
310
+ level++
311
+ }
312
+
234
313
  return result
235
314
  }
236
315
 
package/types/category.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import type Product from './product'
2
2
 
3
3
  interface Category {
4
- id: string // LXB-AU-B
5
- title: string // Lux Gold, Minted Bar
4
+ id: string // LXB-AU-B
5
+ title: string // Minted Bar
6
+ parentTitle?: string // Lux Gold
6
7
  desc?: string
7
8
  img?: string
8
9
  // inbound they're Products and then interally they become LineItems
@@ -1,15 +1,17 @@
1
1
  import type { LineItem, ObsLineItemRef } from './line-item'
2
- import type { FacetsValue } from './facet'
2
+ import type { FacetValueDesc, FacetsValue } from './facet'
3
3
  import type Category from './category'
4
4
 
5
5
  interface CommerceService extends ObsLineItemRef {
6
6
 
7
7
  /** Items in cart */
8
8
  get cartItems(): LineItem[]
9
- /** Total of all quantities of items in cart */
9
+ /** Total of all quantities of all items in cart */
10
10
  get cartQuantity(): number
11
- /** Total of all prices X quantities of items in cart */
11
+ /** Total of all prices * quantities of items in cart */
12
12
  get cartTotal(): number
13
+
14
+ get cartEmpty(): boolean
13
15
 
14
16
  getCartCategorySubtotal(categoryId: string): number
15
17
 
@@ -31,7 +33,16 @@ interface CommerceService extends ObsLineItemRef {
31
33
  get specifiedItems(): LineItem[]
32
34
  get specifiedCategories(): Category[]
33
35
 
34
- /**
36
+ /** Whether this path defines a Category, or if it has further levels */
37
+ getFacetValuesAtSkuPath(skuPath: string): FacetValueDesc[] | undefined
38
+
39
+ /** Based on current value of 'level', what are the available subfacets?
40
+ * If more than one value is specified at 'level' returned FacetValueDesc[]
41
+ * may represent multiple sets.
42
+ * */
43
+ getFacetValuesSpecified(level: number): FacetValueDesc[] | undefined
44
+
45
+ /**
35
46
  * For convenience, so widgets can share state.
36
47
  * "current" is unrelated to what is "specified",
37
48
  * ie, facets' values
@@ -48,6 +59,8 @@ interface CommerceService extends ObsLineItemRef {
48
59
  * */
49
60
  get currentItem(): LineItem | undefined
50
61
 
62
+ getCategory(id: string): Category | undefined
63
+
51
64
  }
52
65
 
53
66
  export {
package/types/facet.ts CHANGED
@@ -5,6 +5,7 @@ interface FacetValueDesc {
5
5
  label: string
6
6
  img? : string | ReactNode // icon is required
7
7
  imgAR? : number // helps with svgs
8
+ sub?: FacetValueDesc[]
8
9
  }
9
10
 
10
11
  /* *** FOR EXAMPLE **
@@ -25,13 +26,10 @@ interface FacetValueDesc {
25
26
  ]
26
27
  }
27
28
  */
28
- type FacetsDesc = Record<number, FacetValueDesc[]>
29
-
30
29
  // Which facets tokens are on at each level
31
30
  type FacetsValue = Record<number, string[]>
32
31
 
33
32
  export type {
34
33
  FacetValueDesc,
35
- FacetsDesc,
36
34
  FacetsValue
37
35
  }
package/types/index.ts CHANGED
@@ -1,7 +1,9 @@
1
1
 
2
+ export type { default as Category } from './category'
2
3
  export type { default as CommerceService } from './commerce-service'
4
+ export type { default as ItemSelector } from './item-selector'
3
5
  export type { default as Product } from './product'
4
- export type { default as Category } from './category'
6
+
5
7
  export type { default as TransactionStatus } from './checkout'
6
8
  export * from './line-item'
7
9
  export * from './facet'
@@ -0,0 +1,14 @@
1
+ import type Category from './category'
2
+ import type { ObsLineItemRef } from './line-item'
3
+
4
+ interface ItemSelector {
5
+ category: Category
6
+ selectedItemRef: ObsLineItemRef
7
+ selectSku: (sku: string) => void
8
+ showPrice?: boolean // true by default
9
+ showQuantity?: boolean // true by default (not impl)
10
+ }
11
+
12
+ export {
13
+ type ItemSelector as default
14
+ }
package/util/index.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { StringMutator, CommerceService } from '../types'
1
2
 
2
3
  export function toTitleCase(str: string) {
3
4
  return str.replace(
@@ -27,6 +28,34 @@ export function formatPrice(price: number): string {
27
28
  return (str.endsWith('.00')) ? str.replace('.00', '') : str
28
29
  }
29
30
 
31
+ export const getFacetValuesMutator = (level: number, cmmc: CommerceService): StringMutator => {
32
+
33
+ const setLevel = (value: string, level: number ): void => {
34
+ const facets = cmmc.facetsValue
35
+ facets[level] = [value]
36
+ cmmc.setFacets(facets)
37
+ const subFacets = cmmc.getFacetValuesSpecified(level)
38
+ if (subFacets) {
39
+ const facets = cmmc.facetsValue
40
+ facets[level + 1] = [subFacets[0].value]
41
+ cmmc.setFacets(facets)
42
+ }
43
+ }
44
+
45
+ const getLevelValueSafe = (level: number): string | null => {
46
+ const facets = cmmc.facetsValue
47
+ if (!(level in facets) || facets[level].length === 0 ) {
48
+ return null
49
+ }
50
+ return facets[level][0]
51
+ }
52
+
53
+ return {
54
+ get: () => (getLevelValueSafe(level)),
55
+ set: (v: string) => {setLevel(v, level)}
56
+ } satisfies StringMutator
57
+ }
58
+
30
59
 
31
60
  export { default as useSyncSkuParamWithCurrentItem } from './use-sync-sku-param-w-current-item'
32
61
  export { default as processSquareCardPayment } from './square-payment'
@@ -56,8 +56,11 @@ const useSyncSkuParamWithCurrentItem = (
56
56
  const setCurrentCategoryFromSku = (sku: string) => {
57
57
  const toks: string[] = sku.split('-')
58
58
  const fv: FacetsValue = {}
59
+ // TODO: confirm that extra trailing nonsense tokens won't break setFacets()
59
60
  for (let i = 1; i < categoryLevel; i++) {
60
- fv[i] = [toks[i]]
61
+ if (i in toks) {
62
+ fv[i] = [toks[i]]
63
+ }
61
64
  }
62
65
  cmmc.setFacets(fv)
63
66
  }
@@ -1,13 +0,0 @@
1
- import React from 'react'
2
- import type CommerceCatAndItemBlock from '../def/commerce-cat-and-item-block'
3
-
4
- /*
5
-
6
- interface CommerceCatAndItemBlock extends Block {
7
- blockType: 'commerce-cat-and-item'
8
- parentPath: string // SKU path of parent of Category ... eg, in Market, LXB-AU or LXB-AG. Next level is Cat.
9
- defaultCatPath?: string // SKU path of default Category... eg, in Market, LXB-AU-B (Minted Bar)
10
- }
11
-
12
- */
13
-
@@ -1,9 +0,0 @@
1
- import type { Block } from '@hanzo/ui/blocks'
2
-
3
- interface CommerceCatAndItemBlock extends Block {
4
- blockType: 'commerce-cat-and-item'
5
- parentPath: string // SKU path of parent of Category ... eg, in Market, LXB-AU or LXB-AG. Next level is Cat.
6
- defaultCatPath?: string // SKU path of default Category... eg, in Market, LXB-AU-B (Minted Bar)
7
- }
8
-
9
- export { type CommerceCatAndItemBlock as default }
@@ -1,55 +0,0 @@
1
- 'use client'
2
- import React, { type PropsWithChildren } from 'react'
3
-
4
- import { cn } from '@hanzo/ui/util'
5
-
6
- import type { FacetsDesc, StringMutator, StringArrayMutator } from '../types'
7
-
8
- import FacetTogglesWidget from './facet-toggles-widget'
9
-
10
- const FacetsWidget: React.FC<PropsWithChildren & {
11
- facets: FacetsDesc
12
- mutators: StringMutator[] | StringArrayMutator[]
13
- facetClx?: string[]
14
- facetItemClx?: string
15
- multiple?: boolean
16
- isMobile?: boolean
17
- id?: string
18
- className?: string
19
- tabSize?: string
20
- childrenAfter?: boolean
21
- }> = ({
22
- children,
23
- facets,
24
- mutators,
25
- multiple=false,
26
- facetClx,
27
- facetItemClx='',
28
- isMobile=false,
29
- className='',
30
- tabSize,
31
- id='FacetsWidget',
32
- childrenAfter=true
33
- }) => {
34
- const horiz = className.includes('flex-row')
35
- return (
36
- <div id={id} className={className} >
37
- {!childrenAfter && children}
38
- {Object.keys(facets).map((key, i) => (
39
- <FacetTogglesWidget
40
- key={i}
41
- multiple={multiple}
42
- mutator={mutators[i]}
43
- isMobile={isMobile}
44
- facetValues={facets[parseInt(key)]}
45
- className={cn((horiz ? '' : 'mb-2'), (i !== 0 && !horiz) ? 'mt-2' : '', (facetClx?.[i]) ?? '')}
46
- buttonClx={facetItemClx}
47
- tabSize={tabSize}
48
- />
49
- ))}
50
- {childrenAfter && children}
51
- </div>
52
- )
53
- }
54
-
55
- export default FacetsWidget
@@ -1,78 +0,0 @@
1
- import React, { useState } from 'react'
2
- import Picker from 'react-mobile-picker'
3
-
4
- import type { Product } from '../types'
5
- import { formatPrice } from '../util'
6
- import { cn } from '@hanzo/ui/util'
7
-
8
- // from source code
9
- interface PickerItemRenderProps {
10
- selected: boolean;
11
- }
12
-
13
- function renderOptions(options: string[], selectedColor: string) {
14
- return options.map((option) => (
15
- <Picker.Item key={option} value={option}>
16
- {({ selected }: PickerItemRenderProps) => (
17
- <div className={selected ? `font-semibold ${selectedColor}` : 'text-neutral-400'}>{option}</div>
18
- )}
19
- </Picker.Item>
20
- ))
21
- }
22
-
23
- // const DEFAULT_HEIGHT = 216
24
- // const DEFAULT_ITEM_HEIGHT = 36
25
-
26
-
27
- const ProductSelectionMobilePicker: React.FC<{
28
- products: Product[]
29
- selectedSku: string | undefined
30
- onValueChange: (v: string) => void
31
- height?: number
32
- itemHeight?: number
33
- outerClx?: string
34
- itemClx?: string
35
- }> = ({
36
- products,
37
- selectedSku,
38
- onValueChange,
39
- height,
40
- itemHeight,
41
- outerClx='',
42
- itemClx=''
43
- }) => {
44
-
45
- // @ts-ignore
46
- const onChange = (val, ignore) => {
47
- console.log("VAL", val)
48
- console.log("key")
49
- onValueChange(val.item)
50
- }
51
-
52
- return (
53
- <div className={cn('border border-muted-4 rounded-lg px-2', outerClx)}>
54
- <Picker
55
- className=''
56
- value={selectedSku ? {item: selectedSku} : {item: undefined}}
57
- onChange={onChange}
58
- wheelMode="natural"
59
- height={height}
60
- itemHeight={itemHeight}
61
- >
62
- <Picker.Column name="item">
63
- {products.map((prod) => (
64
- <Picker.Item key={prod.sku} value={prod.sku}>
65
- {({ selected }: PickerItemRenderProps) => (
66
- <div className={cn(selected ? 'font-semibold text-accent' : 'text-muted', itemClx)}>
67
- {prod.titleAsOption + ', ' + formatPrice(prod.price)}
68
- </div>
69
- )}
70
- </Picker.Item>
71
- ))}
72
- </Picker.Column>
73
- </Picker>
74
- </div>
75
- )
76
- }
77
-
78
- export default ProductSelectionMobilePicker
@@ -1,38 +0,0 @@
1
- 'use client'
2
-
3
- import { Label, RadioGroup, RadioGroupItem } from '@hanzo/ui/primitives'
4
- import type { Product } from '../types'
5
- import { formatPrice } from '../util'
6
-
7
- const ProductSelectionRadioGroup: React.FC<{
8
- products: Product[]
9
- selectedSku: string | undefined
10
- onValueChange: (v: string) => void
11
- groupClx?: string
12
- itemClx?: string
13
- showPrice?: boolean
14
- }> = ({
15
- products,
16
- selectedSku,
17
- onValueChange,
18
- groupClx='',
19
- itemClx='',
20
- showPrice=true
21
- }) => (
22
- products.length > 1 && (
23
- <RadioGroup
24
- className={groupClx}
25
- onValueChange={onValueChange}
26
- value={selectedSku}
27
- >
28
- {products.map((prod) => (
29
- <div className={itemClx} key={prod.sku}>
30
- <RadioGroupItem value={prod.sku} id={prod.sku} />
31
- <Label htmlFor={prod.sku}>{prod.titleAsOption + (showPrice ? (', ' + formatPrice(prod.price)) : '')}</Label>
32
- </div>
33
- ))}
34
- </RadioGroup>
35
- )
36
- )
37
-
38
- export default ProductSelectionRadioGroup