@hanzo/commerce 6.2.3 → 6.3.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.
Files changed (35) hide show
  1. package/components/buy/_to_deprecate_select-family-item-card.tsx +0 -1
  2. package/components/buy/_to_deprecate_select-family-item-panel.tsx +1 -1
  3. package/components/buy/add-to-cart-widget.tsx +9 -7
  4. package/components/buy/buy-card.tsx +3 -4
  5. package/components/buy/carousel-buy-card.tsx +121 -87
  6. package/components/buy/multi-family/all-variants-carousel.tsx +257 -0
  7. package/components/{select-family → buy/multi-family}/family-carousel/index.tsx +7 -17
  8. package/components/{select-family → buy/multi-family}/family-carousel/slide.tsx +3 -3
  9. package/components/{select-family → buy/multi-family}/family-carousel/state.ts +6 -2
  10. package/components/buy/multi-family/index.ts +2 -0
  11. package/components/buy/{foo.txt → please-keep.txt} +1 -1
  12. package/components/buy/single-family-selector.tsx +90 -0
  13. package/components/buy/title-and-byline.tsx +25 -0
  14. package/components/cart/cart-panel/cart-line-item.tsx +1 -1
  15. package/components/index.ts +1 -2
  16. package/components/item-selector/button.tsx +28 -18
  17. package/components/item-selector/carousel/index.tsx +197 -0
  18. package/components/item-selector/carousel/slider.tsx +40 -0
  19. package/components/{select-family/level-nodes-widget → node-tabs}/index.tsx +3 -3
  20. package/components/{select-family/level-nodes-widget → node-tabs}/node-image.tsx +1 -1
  21. package/package.json +3 -3
  22. package/service/impls/standalone/actual-line-item.ts +3 -1
  23. package/service/impls/standalone/standalone-service.ts +41 -4
  24. package/types/category-node.ts +21 -9
  25. package/types/commerce-service.ts +12 -13
  26. package/types/family.ts +7 -1
  27. package/types/index.ts +4 -3
  28. package/types/item-selector.ts +46 -9
  29. package/types/multi-family-selector-props.ts +20 -0
  30. package/types/selection-ui-specifier.ts +37 -21
  31. package/util/index.ts +3 -0
  32. package/util/item-selector-options-accessor.ts +32 -0
  33. package/util/multi-family-selector-options-accessor.ts +15 -0
  34. package/util/selection-ui-specifiers.ts +1 -5
  35. package/components/item-selector/carousel.tsx +0 -162
@@ -49,6 +49,7 @@ class StandaloneService
49
49
  private _promo: Promo | null = null
50
50
 
51
51
  private _options : StandaloneServiceOptions
52
+ private _currentFamily: Family | undefined = undefined
52
53
  private _currentItem: ActualLineItem | undefined = undefined
53
54
 
54
55
  constructor(
@@ -78,10 +79,12 @@ class StandaloneService
78
79
  StandaloneService,
79
80
  '_selectedPaths' |
80
81
  '_currentItem' |
82
+ '_currentFamily' |
81
83
  '_promo'
82
84
  >(this, {
83
85
  _selectedPaths : observable.deep,
84
86
  _currentItem: observable.shallow,
87
+ _currentFamily: observable.shallow,
85
88
  _promo: observable,
86
89
  })
87
90
 
@@ -95,8 +98,11 @@ class StandaloneService
95
98
  selectedFamilies: computed,
96
99
  hasSelection: computed,
97
100
  setCurrentItem: action,
101
+ setCurrentFamily: action,
98
102
  currentItem: computed,
103
+ currentFamily: computed,
99
104
  item: computed,
105
+ family: computed,
100
106
  selectedPaths: computed,
101
107
  appliedPromo: computed,
102
108
  setAppliedPromo: action,
@@ -154,7 +160,7 @@ class StandaloneService
154
160
  let error: string | undefined = undefined
155
161
 
156
162
  try {
157
- if (node.subNodes && atEnd && node.terminal) {
163
+ if (node.subNodes && atEnd && node.outermost) {
158
164
  role = 'multi-family'
159
165
  families = node.subNodes.map((sub) => {
160
166
  const familyId = skuPath + sep.tok + sub.skuToken
@@ -167,7 +173,7 @@ class StandaloneService
167
173
  }
168
174
  else if (!node.subNodes && (atEnd || possibleSKU)) {
169
175
  const _skuPath = (possibleSKU) ? getParentPath(skuPath) : skuPath
170
- if (parent?.terminal) {
176
+ if (parent?.outermost) {
171
177
  role = 'family-in-multi-family'
172
178
  const fam = this._familyMap.get(_skuPath)
173
179
  if (!fam) {
@@ -364,12 +370,13 @@ class StandaloneService
364
370
  }
365
371
  }
366
372
  return undefined
367
- })();
373
+ })(); // necessary semi
368
374
 
375
+ this.setCurrentFamily(this._currentItem ? this._currentItem.familyId : undefined)
369
376
  return !!this._currentItem
370
377
  }
371
378
 
372
- /* ObsLineItemRef */
379
+ /* for ObsLineItemRef */
373
380
  get item(): LineItem | undefined {
374
381
  return this._currentItem
375
382
  }
@@ -378,6 +385,36 @@ class StandaloneService
378
385
  return this._currentItem
379
386
  }
380
387
 
388
+ setCurrentFamily(id: string | undefined): boolean {
389
+
390
+ if (id === undefined || id.length === 0) {
391
+ this._currentFamily = undefined
392
+ return true
393
+ }
394
+
395
+ const fam = this._familyMap.get(id)
396
+ this._currentFamily = fam // undef ok
397
+
398
+ if (
399
+ this._currentFamily &&
400
+ this._currentItem &&
401
+ this._currentItem.familyId !== this._currentFamily.id
402
+ ) {
403
+ this._currentItem = undefined
404
+ }
405
+
406
+ return !!this._currentFamily
407
+ }
408
+
409
+ get currentFamily(): Family | undefined {
410
+ return this._currentFamily
411
+ }
412
+
413
+ /* for ObsFamilyRef */
414
+ get family(): Family | undefined {
415
+ return this._currentFamily
416
+ }
417
+
381
418
  selectPaths(sel: SelectedPaths): Family[] {
382
419
  runInAction (() => {
383
420
  this._selectedPaths = this._processAndValidate(sel)
@@ -4,19 +4,31 @@ interface CategoryNode {
4
4
  skuToken: string // a token in the sku
5
5
  label: string;
6
6
  /**
7
- * If this node has subnodes, marks it as a "last user-visible" / "outermost" Category.
8
- * Normally, a node without subnodes (one that has products and is a family), is by definition terminal.
9
- * If it *has* subnodes, but its subNodes are to be treated as sibling families
10
- * of a Category, rather rather sub Categories, this is specified by setting terminal=true.
7
+ * Explicitly marks this node as a "last user-visible" / "outermost" Category.
8
+ * Normally, a node is "outermost" when it has no subnodes
9
+ * (and thus is a family with products -- "single-family")
10
+ * But if it *has* subnodes, and its subNodes are actually sibling families
11
+ * of a Category, it must be marked as "outermost" ("multi-family")
11
12
  * */
12
- terminal?: boolean;
13
+ outermost?: boolean;
13
14
  img? : string | ReactNode // icon is required
14
15
  imgAR? : number // helps with svgs
15
16
 
16
- /** Short desc of what subNodes refer to.
17
- * For example, if this node's label is 'Lux Credit',
18
- * it's subNodesLabel would be 'Level',
19
- * refering to membership 'levels' ('black', 'elite', etc)
17
+ /**
18
+ * One word prompt that describes the 'meaning' of
19
+ * the subnodes.
20
+ * For example, if a node is 'Lux Credit',
21
+ * it's subNodesLabel might be 'Level', so some UI's
22
+ * might be configured to render 'Level: Black'
23
+ *
24
+ * or something like...
25
+ *
26
+ * <Tabs title={parentNode.label + ': ' + parentNode.subNodesLabel} values={families.map((f) => (f.shortName))} />
27
+ *
28
+ * ...which renders
29
+ *
30
+ * Lux Credit Levels:
31
+ * Black Elite Founder Sovereign
20
32
  */
21
33
  subNodesLabel?: string
22
34
  subNodes?: CategoryNode[]
@@ -1,10 +1,10 @@
1
1
  import type { LineItem, ObsLineItemRef } from './line-item'
2
2
  import type { CategoryNode, SelectedPaths, CategoryNodeRole } from './category-node'
3
- import type Family from './family'
3
+ import type { Family, ObsFamilyRef} from './family'
4
4
  import type Promo from './promo'
5
5
 
6
6
 
7
- interface CommerceService extends ObsLineItemRef {
7
+ interface CommerceService extends ObsLineItemRef, ObsFamilyRef {
8
8
 
9
9
  /** Items in cart */
10
10
  get cartItems(): LineItem[]
@@ -97,23 +97,22 @@ interface CommerceService extends ObsLineItemRef {
97
97
 
98
98
  /**
99
99
  * For convenience, so widgets can share state.
100
- * "current" is unrelated to what is "selected",
101
- * ie, facets' values
102
- * */
103
- setCurrentItem(sku: string | undefined): boolean // valid sku and was set.
104
- /**
105
- * For convenience, so widgets can share state.
106
- * "current" is unrelated to what is "selected",
107
- * ie, facets' values
100
+ * "current" is unrelated to what branches and items are
101
+ * "selected" (with CategoryNode's and paths)
108
102
  *
109
- * note: for ObsLineItemRef, there is also
103
+ * SEE ALSO: from ObsLineItemRef and ObsFamilyRef, there are also
110
104
  * get item(): LineItem | undefined
111
- * which simply delegates to this function
105
+ * get family(): Family | undefined
106
+ *
107
+ * These simply delegate to these functions
112
108
  * */
109
+ setCurrentItem(sku: string | undefined): boolean // valid sku and was set.
113
110
  get currentItem(): LineItem | undefined
111
+ setCurrentFamily(id: string | undefined): boolean // valid id and was set.
112
+ get currentFamily(): Family | undefined
114
113
 
114
+ // utility
115
115
  getFamily(id: string): Family | undefined
116
-
117
116
  }
118
117
 
119
118
 
package/types/family.ts CHANGED
@@ -15,6 +15,12 @@ interface Family {
15
15
  products: Product[]
16
16
  }
17
17
 
18
+
19
+ interface ObsFamilyRef {
20
+ get family(): Family | undefined
21
+ }
22
+
18
23
  export {
19
- type Family as default
24
+ type Family,
25
+ type ObsFamilyRef
20
26
  }
package/types/index.ts CHANGED
@@ -1,13 +1,14 @@
1
1
 
2
- export type { default as Family } from './family'
3
2
  export type { default as CommerceService } from './commerce-service'
3
+ export type { default as MultiFamilySelectorProps } from './multi-family-selector-props'
4
4
  export type { default as Product } from './product'
5
5
  export type { default as Promo } from './promo'
6
6
  export type { default as TokenSeparators } from './token-separators'
7
7
 
8
+ export * from './category-node'
8
9
  export * from './checkout'
10
+ export * from './family'
9
11
  export * from './item-selector'
10
12
  export * from './line-item'
11
- export * from './category-node'
12
- export * from './string-mutator'
13
13
  export * from './selection-ui-specifier'
14
+ export * from './string-mutator'
@@ -6,27 +6,62 @@ interface ItemSelector {
6
6
  selectSku: (sku: string) => void
7
7
  }
8
8
 
9
+ /** default: 'text' */
10
+ type ItemButton = 'text' | 'image-and-text' | 'image'
11
+
12
+ // NOTE: if a field is added here, it should also
13
+ // be added to util/item-selector-options-accessor.ts
9
14
  type ItemSelectorOptions = {
15
+
16
+ /**
17
+ * default: short
18
+ * 'short': product.shortTitle if defined
19
+ * 'long': always us product.title, even if .shortTitle is defined
20
+ * 'none': do not show title AND BYLINE
21
+ *
22
+ * (NOTE: *not* the same as buttonType = 'none')
23
+ */
24
+ familyTitle?: 'none' | 'long' | 'short'
25
+
26
+ /** only if title shown
27
+ * default: false */
28
+ showFamilyByline?: boolean
29
+
10
30
  /**
11
31
  * Whether the item label includes the Family name.
12
32
  * eg, 'Minted Bar, 1oz' vs '1oz'
13
33
  * default: false.
14
34
  */
15
- showFamily?: boolean
35
+ showFamilyInOption?: boolean
36
+ /** default: true if it exists */
37
+ showByline?: boolean
38
+
39
+ /** default: true */
40
+ showPrice?: boolean
41
+
16
42
  /**
17
43
  * Show the current item quantity along with title and price.
18
44
  * default: false
19
45
  */
20
46
  showQuantity?: boolean
21
47
 
22
- /** default: true */
23
- showPrice?: boolean
24
-
25
- /** default: false */
26
- imageButtons?: boolean
48
+ buttonType?: ItemButton
27
49
 
28
- /** default: false */
50
+ /** (button selector only) default: false */
29
51
  horizButtons?: boolean
52
+
53
+ /** (carousel selector only)
54
+ * affects sort. See below.
55
+ * default: false */
56
+ showSlider?: boolean
57
+
58
+ /**
59
+ * Sort by cost.
60
+ * If it's a carousel selector and 'showSlider' is true,
61
+ * defaults to 'asc', unless another value is explicitly specified.
62
+ * Otherwise, defaults to 'none'
63
+ * */
64
+ sort?: 'none' | 'asc' | 'desc'
30
65
  }
31
66
 
32
67
  interface _ItemSelectorCompProps {
@@ -37,8 +72,10 @@ interface _ItemSelectorCompProps {
37
72
  * eg, Carousel options.
38
73
  */
39
74
  ext?: any
40
- /** List selectors will scroll. Used internally */
41
- scrollable: boolean
75
+ /** List selectors will scroll.
76
+ * Used internally
77
+ * default: false */
78
+ scrollable? : boolean
42
79
  options?: ItemSelectorOptions
43
80
 
44
81
  mobile?: boolean
@@ -0,0 +1,20 @@
1
+ import type { Dimensions } from '@hanzo/ui/types'
2
+
3
+ import type { Family } from './family'
4
+ import type { ItemSelectorOptions } from './item-selector'
5
+ import type { MultiFamilySelectorOptions } from './selection-ui-specifier'
6
+ import type { CategoryNode } from './category-node'
7
+ import type { LineItem } from './line-item'
8
+
9
+ interface MultiFamilySelectorProps {
10
+ families: Family[]
11
+ parent: CategoryNode
12
+ clx?: string
13
+ itemClx?: string
14
+ itemOptions?: ItemSelectorOptions
15
+ selectorOptions?: MultiFamilySelectorOptions
16
+ mediaConstraint?: Dimensions
17
+ mobile?: boolean
18
+ }
19
+
20
+ export { type MultiFamilySelectorProps as default }
@@ -1,36 +1,52 @@
1
1
  import type { ItemSelectorOptions } from './item-selector'
2
- type SelectorType = 'buttons' | 'carousel'
3
2
 
4
- interface FamilyCarouselSlideOptions extends ItemSelectorOptions {
5
- /**
6
- * default: short
7
- * 'short': product.shortTitle if defined
8
- * 'long': always us product.title, even if .shortTitle is defined
9
- * 'none': do not show title AND BYLINE
10
- */
11
- title?: 'none' | 'long' | 'short'
12
- /** default: true */
13
- showByline?: boolean
3
+ type SingleFamilySelector = 'buttons' | 'carousel'
4
+ type MultiFamilySelector = 'family-carousel' | 'all-variants-carousel'
5
+
6
+ type MultiFamilySelectorOptions = {
7
+ /**
8
+ * Show title of parent node of families.
9
+ * eg,
10
+ * Lux Credit
11
+ * Black Card
12
+ *
13
+ * vs just Black Card
14
+ *
15
+ * ( parent CategoryNode.label)
16
+ *
17
+ * (goes to one line if only one family)
18
+ * default: true
19
+ * */
20
+ showParentTitle?: boolean
21
+
22
+ /** parent CategoryNode.subNodesLabel
23
+ *
24
+ * default: 'none'
25
+ */
26
+ parentByline?: 'none' | 'own-line' | 'comma-sep' | 'colon-sep'
27
+
28
+ /** show horiz image buttons of
29
+ * all sibling items below item info
30
+ * default: true*/
31
+ showItemSwatches?: boolean
14
32
  }
15
33
 
16
34
  interface SelectionUISpecifier {
17
35
  multiFamily?: {
18
- familySelector: 'carousel'
19
- /** default: true */
20
- showParentTitle: boolean
21
- slide: {
22
- type: 'buttons'
23
- options?: FamilyCarouselSlideOptions
24
- }
36
+ type: MultiFamilySelector
37
+ selectorOptions?: MultiFamilySelectorOptions
38
+ itemOptions?: ItemSelectorOptions
25
39
  }
26
40
  singleFamily?: {
27
- type: SelectorType
41
+ type: SingleFamilySelector
28
42
  options?: ItemSelectorOptions
29
43
  }
30
44
  }
31
45
 
32
46
  export {
33
47
  type SelectionUISpecifier,
34
- type FamilyCarouselSlideOptions,
35
- type SelectorType
48
+ type ItemSelectorOptions,
49
+ type SingleFamilySelector,
50
+ type MultiFamilySelector,
51
+ type MultiFamilySelectorOptions
36
52
  }
package/util/index.ts CHANGED
@@ -66,3 +66,6 @@ export { default as ProductMediaAccessor } from './product-media-accessor'
66
66
  export * from './selection-ui-specifiers'
67
67
 
68
68
  export { getErrorMessage } from './error'
69
+
70
+ export { default as accessItemOptions } from './item-selector-options-accessor'
71
+ export { default as accessMultiSelectorOptions } from './multi-family-selector-options-accessor'
@@ -0,0 +1,32 @@
1
+ import type { ItemSelectorOptions } from '../types'
2
+
3
+ export default (options: ItemSelectorOptions | undefined = {}): Required<ItemSelectorOptions> => {
4
+
5
+ const familyTitle = 'familyTitle' in options ? options.familyTitle! : 'short'
6
+ const showFamilyByline = 'showFamilyByline' in options ? options.showFamilyByline! : false
7
+
8
+ const showFamilyInOption = 'showFamilyInOption' in options ? options.showFamilyInOption! : false
9
+ const showByline = 'showByline' in options ? options.showByline! : true
10
+
11
+ const showPrice = 'showPrice' in options ? options.showPrice! : true
12
+ const showQuantity = 'showQuantity' in options ? options.showQuantity! : false
13
+
14
+ const buttonType = 'buttonType' in options ? options.buttonType! : 'text'
15
+ const horizButtons = 'horizButtons' in options ? options.horizButtons! : false
16
+ const showSlider = 'showSlider' in options ? options.showSlider! : true
17
+
18
+ const sort = 'sort' in options ? options.sort! : 'none'
19
+
20
+ return {
21
+ showFamilyInOption,
22
+ familyTitle,
23
+ showFamilyByline,
24
+ showByline,
25
+ showPrice,
26
+ showQuantity,
27
+ buttonType,
28
+ horizButtons,
29
+ showSlider,
30
+ sort,
31
+ }
32
+ }
@@ -0,0 +1,15 @@
1
+ import type { MultiFamilySelectorOptions } from '../types'
2
+
3
+ export default (options: MultiFamilySelectorOptions | undefined = {}): Required<MultiFamilySelectorOptions> => {
4
+
5
+ const showParentTitle = 'showParentTitle' in options ? options.showParentTitle! : true
6
+ const showItemSwatches = 'showItemSwatches' in options ? options.showItemSwatches! : true
7
+
8
+ const parentByline = 'parentByline' in options ? options.parentByline! : 'none'
9
+
10
+ return {
11
+ showParentTitle,
12
+ showItemSwatches,
13
+ parentByline,
14
+ }
15
+ }
@@ -3,11 +3,7 @@ import sep from '../service/sep'
3
3
 
4
4
  const DEFAULT = {
5
5
  multiFamily: {
6
- familySelector: 'carousel',
7
- showParentTitle: false,
8
- slide: {
9
- type: 'buttons'
10
- }
6
+ type: 'all-variants-carousel'
11
7
  },
12
8
  singleFamily: { type: 'carousel' }
13
9
  } satisfies SelectionUISpecifier
@@ -1,162 +0,0 @@
1
- 'use client'
2
- import React, { useCallback, useEffect, useRef } from 'react'
3
- import { reaction } from 'mobx'
4
- import { observer } from 'mobx-react-lite'
5
-
6
- import { cn } from '@hanzo/ui/util'
7
- import type { Dimensions } from '@hanzo/ui/types'
8
-
9
- import {
10
- type CarouselOptions,
11
- type CarouselApi,
12
- Carousel,
13
- CarouselContent,
14
- CarouselItem,
15
- CarouselPrevious,
16
- CarouselNext,
17
- ApplyTypography,
18
- MediaStack
19
- } from '@hanzo/ui/primitives'
20
-
21
- import type { ItemSelectorProps, LineItem } from '../../types'
22
- import { formatCurrencyValue } from '../../util'
23
-
24
- import QuantityIndicator from '../quantity-indicator'
25
-
26
- const DEFAULT_CONSTRAINT = {w: 250, h: 250}
27
-
28
- interface CarouselItemSelectorPropsExt {
29
- constrainTo: Dimensions
30
- options?: CarouselOptions
31
- /** Do not show Family and / or Item title and Price */
32
- imageOnly?: boolean
33
- }
34
-
35
- const ItemSlide: React.FC<{
36
- item: LineItem,
37
- constrainTo: Dimensions
38
- imageOnly: boolean
39
- showFamily: boolean
40
- showPrice: boolean
41
- showQuantity: boolean
42
- clx?: string
43
- }> = ({
44
- item,
45
- constrainTo,
46
- showFamily,
47
- imageOnly,
48
- showPrice,
49
- showQuantity,
50
- clx=''
51
- }) => {
52
-
53
- return (
54
- <CarouselItem className={cn('p-2 flex flex-col justify-center items-center', clx)}>
55
- {!imageOnly && (
56
- <ApplyTypography className='flex flex-col items-center !gap-2 [&>*]:!m-0'>
57
- {showFamily && (<h4>{item.familyTitle}</h4>)}
58
- <h6>{item.optionLabel}</h6>
59
- {item.byline && (<p>{item.byline}</p>)}
60
- </ApplyTypography>
61
- )}
62
- <MediaStack media={item} constrainTo={constrainTo} clx='my-4' />
63
- {!imageOnly && (showPrice || showQuantity) && (
64
- <ApplyTypography className='flex flex-row items-center !gap-2 [&>*]:!m-0'>
65
- {showPrice && (<p>{formatCurrencyValue(item.price)}</p>)}
66
- {showQuantity && (
67
- <QuantityIndicator
68
- item={item}
69
- clx='h-[26px] ml-1'
70
- iconClx='fill-foreground'
71
- digitClx='not-typography font-semibold text-primary-fg leading-none font-sans text-xs'
72
- />
73
- )}
74
- </ApplyTypography>
75
- )}
76
- </CarouselItem>
77
- )
78
- }
79
-
80
- const CarouselItemSelector: React.FC<ItemSelectorProps> = observer(({
81
- items,
82
- selectSku,
83
- selectedItemRef: itemRef,
84
- scrollable, // ignored
85
- clx='',
86
- itemClx='',
87
- options={},
88
- ext={
89
- options: {loop: true},
90
- constrainTo: DEFAULT_CONSTRAINT,
91
- imageOnly: false
92
- } satisfies CarouselItemSelectorPropsExt
93
- }) => {
94
-
95
- const showFamily = 'showFamily' in options ? options.showFamily! : false
96
- const showPrice = 'showPrice' in options ? options.showPrice! : true
97
- const showQuantity = 'showQuantity' in options ? options.showQuantity! : false
98
-
99
- const carouselOptions = 'options' in ext ? ext.options : undefined
100
- const constrainTo = 'constrainTo' in ext ? ext.constrainTo : DEFAULT_CONSTRAINT
101
- const imageOnly = 'imageOnly' in ext ? ext.imageOnly : false
102
-
103
- const elbaApiRef = useRef<CarouselApi | undefined>(undefined)
104
- const dontRespondRef = useRef<boolean>(false)
105
-
106
- const setApi = (api: CarouselApi) => {elbaApiRef.current = api}
107
-
108
- const onSelect = useCallback((emblaApi: CarouselApi) => {
109
- if (dontRespondRef.current) {
110
- dontRespondRef.current = false
111
- return
112
- }
113
- const index = emblaApi.selectedScrollSnap()
114
- if (index !== -1) {
115
- selectSku(items[index].sku)
116
- }
117
- dontRespondRef.current = false
118
- }, [])
119
-
120
- useEffect(() => {
121
- return reaction(
122
- () => ({item: itemRef.item}),
123
- ({item}) => {
124
- if (elbaApiRef.current) {
125
- const index = items.findIndex((el) => (el.sku === item?.sku))
126
- if (index !== -1) {
127
- dontRespondRef.current = true
128
- elbaApiRef.current.scrollTo(index)
129
- }
130
- }
131
- }
132
- )
133
- }, [])
134
-
135
- return (
136
- <Carousel options={carouselOptions} className={cn('w-full px-2', clx)} onCarouselSelect={onSelect} setApi={setApi}>
137
- <CarouselContent>
138
- {items.map((item) => (
139
- <ItemSlide
140
- key={item.sku}
141
- item={item}
142
- {...{
143
- showFamily,
144
- showPrice,
145
- showQuantity,
146
- constrainTo,
147
- imageOnly
148
- }}
149
- clx={itemClx}
150
- />
151
- ))}
152
- </CarouselContent>
153
- <CarouselPrevious className='left-1'/>
154
- <CarouselNext className='right-1'/>
155
- </Carousel>
156
- )
157
- })
158
-
159
- export {
160
- type CarouselItemSelectorPropsExt,
161
- CarouselItemSelector as default
162
- }