@hanzo/commerce 7.7.0 → 7.7.2

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 (90) hide show
  1. package/dist/components/checkout/payment-step-form/methods/card.js +20 -2
  2. package/dist/components/checkout/payment-step-form/methods/card.js.map +1 -1
  3. package/package.json +38 -9
  4. package/billing.ts +0 -2
  5. package/client.ts +0 -783
  6. package/components/Icons.tsx +0 -35
  7. package/components/add-to-cart-widget.tsx +0 -195
  8. package/components/buy/buy-card.tsx +0 -269
  9. package/components/buy/carousel-buy-card.tsx +0 -243
  10. package/components/buy/multi-family/all-variants-carousel.tsx +0 -259
  11. package/components/buy/multi-family/family-carousel/index.tsx +0 -75
  12. package/components/buy/multi-family/family-carousel/slide.tsx +0 -82
  13. package/components/buy/multi-family/family-carousel/state.ts +0 -87
  14. package/components/buy/multi-family/index.ts +0 -2
  15. package/components/buy/single-family-selector.tsx +0 -91
  16. package/components/buy/title-and-byline.tsx +0 -26
  17. package/components/cart/cart-panel/cart-line-item.tsx +0 -77
  18. package/components/cart/cart-panel/index.tsx +0 -157
  19. package/components/cart/cart-panel/promo-code.tsx +0 -105
  20. package/components/cart/cart-panel/total-area.tsx +0 -61
  21. package/components/checkout/payment-step-form/card-icon-row.tsx +0 -26
  22. package/components/checkout/payment-step-form/card-icons/amex.tsx +0 -32
  23. package/components/checkout/payment-step-form/card-icons/diners-club.tsx +0 -13
  24. package/components/checkout/payment-step-form/card-icons/discover.tsx +0 -25
  25. package/components/checkout/payment-step-form/card-icons/jcb.tsx +0 -26
  26. package/components/checkout/payment-step-form/card-icons/mastercard.tsx +0 -27
  27. package/components/checkout/payment-step-form/card-icons/visa.tsx +0 -25
  28. package/components/checkout/payment-step-form/cc-button.tsx +0 -18
  29. package/components/checkout/payment-step-form/contact-form.tsx +0 -48
  30. package/components/checkout/payment-step-form/crypto-icons/btc.tsx +0 -11
  31. package/components/checkout/payment-step-form/crypto-icons/eth.tsx +0 -20
  32. package/components/checkout/payment-step-form/crypto-icons/usdt.tsx +0 -13
  33. package/components/checkout/payment-step-form/index.tsx +0 -113
  34. package/components/checkout/payment-step-form/methods/bank-transfer.tsx +0 -81
  35. package/components/checkout/payment-step-form/methods/card.tsx +0 -230
  36. package/components/checkout/payment-step-form/methods/crypto.tsx +0 -226
  37. package/components/checkout/payment-step-form/methods/index.ts +0 -23
  38. package/components/checkout/shipping-step-form.tsx +0 -171
  39. package/components/index.ts +0 -11
  40. package/components/item/product-card.tsx +0 -66
  41. package/components/item-selector/button.tsx +0 -213
  42. package/components/item-selector/carousel/index.tsx +0 -196
  43. package/components/item-selector/carousel/slider.tsx +0 -37
  44. package/components/item-selector/index.ts +0 -5
  45. package/components/item-selector/quantity-indicator.tsx +0 -50
  46. package/components/node-tabs/index.tsx +0 -90
  47. package/components/node-tabs/node-image.tsx +0 -31
  48. package/index.ts +0 -55
  49. package/metering.test.ts +0 -199
  50. package/metering.ts +0 -289
  51. package/product-card.test.tsx +0 -104
  52. package/service/context.tsx +0 -45
  53. package/service/debug.ts +0 -41
  54. package/service/impls/standalone/actual-line-item.ts +0 -136
  55. package/service/impls/standalone/get-instance.ts +0 -64
  56. package/service/impls/standalone/index.ts +0 -579
  57. package/service/impls/standalone/order/firebase.ts +0 -5
  58. package/service/impls/standalone/order/index.ts +0 -58
  59. package/service/impls/standalone/persistence.ts +0 -33
  60. package/service/path-utils.ts +0 -26
  61. package/service/sep.ts +0 -7
  62. package/tsconfig.json +0 -17
  63. package/types/README.md +0 -2
  64. package/types/category-node.ts +0 -50
  65. package/types/checkout.ts +0 -47
  66. package/types/commerce-config.ts +0 -13
  67. package/types/commerce-service.ts +0 -128
  68. package/types/family.ts +0 -26
  69. package/types/index.ts +0 -15
  70. package/types/item-selector.ts +0 -97
  71. package/types/line-item.ts +0 -29
  72. package/types/multi-family-selector-props.ts +0 -20
  73. package/types/product.ts +0 -21
  74. package/types/promo.ts +0 -10
  75. package/types/selection-ui-specifier.ts +0 -52
  76. package/types/string-mutator.ts +0 -14
  77. package/types/token-separators.ts +0 -7
  78. package/util/analytics.ts +0 -21
  79. package/util/countries.ts +0 -196
  80. package/util/error.ts +0 -34
  81. package/util/index.ts +0 -71
  82. package/util/item-selector-options-accessor.ts +0 -35
  83. package/util/line-item-ref.ts +0 -23
  84. package/util/multi-family-selector-options-accessor.ts +0 -15
  85. package/util/obs-string-mutator.ts +0 -22
  86. package/util/product-media-accessor.ts +0 -58
  87. package/util/promo-codes.ts +0 -106
  88. package/util/selection-ui-specifiers.ts +0 -30
  89. package/util/square-payment.ts +0 -50
  90. package/util/use-sync-sku-param-w-current-item.ts +0 -88
@@ -1,243 +0,0 @@
1
- 'use client'
2
- import React, {
3
- useRef,
4
- useEffect,
5
- type ComponentType,
6
- useState
7
- } from 'react'
8
- import { observer } from 'mobx-react-lite'
9
-
10
- import {
11
- cn,
12
- } from '@hanzo/ui'
13
- import type {
14
- ItemSelectorProps,
15
- LineItem,
16
- Family,
17
- CategoryNode,
18
- CategoryNodeRole,
19
- SelectionUISpecifier,
20
- ItemSelectorOptions,
21
- MultiFamilySelectorProps,
22
- MultiFamilySelectorOptions,
23
- } from '../../types'
24
-
25
- import { useCommerce } from '../../service/context'
26
- import { getSelectionUISpecifier } from '../../util'
27
-
28
- import { CarouselItemSelector, ButtonItemSelector } from '../item-selector'
29
- import SingleFamilySelector from './single-family-selector'
30
- import { FamilyCarousel, AllVariantsCarousel } from './multi-family'
31
-
32
- import AddToCartWidget from '../add-to-cart-widget'
33
-
34
- const SCROLL = {
35
- scrollAfter: 5,
36
- scrollHeightClx: 'h-[65vh]'
37
- }
38
-
39
- const MEDIA_CONSTRAINT = {w: 200, h: 200}
40
-
41
- const sortItems = (items: LineItem[], sort: 'asc' | 'desc' | 'none'): LineItem[] => (
42
- ((sort === 'asc') ?
43
- items.sort((a: LineItem, b: LineItem): number => (a.price - b.price))
44
- :
45
- ((sort === 'desc') ?
46
- items.sort((a: LineItem, b: LineItem): number => (b.price - a.price ))
47
- :
48
- items
49
- )
50
- )
51
- )
52
-
53
- const CarouselBuyCard: React.FC<{
54
- skuPath: string
55
- checkoutButton: React.ReactNode
56
- clx?: string
57
- selectorClx?: string
58
- addBtnClx?: string
59
- buttonsAreaClx?: string
60
- mobile?: boolean
61
- onQuantityChanged?: (sku: string, oldV: number, newV: number) => void
62
- }> = ({
63
- skuPath,
64
- checkoutButton,
65
- clx='',
66
- selectorClx='',
67
- addBtnClx='',
68
- buttonsAreaClx='',
69
- mobile=false,
70
- onQuantityChanged,
71
- }) => {
72
-
73
- const cmmc = useCommerce()
74
-
75
- const r = useRef<{
76
- role: CategoryNodeRole
77
- item: LineItem | undefined
78
- family: Family | undefined
79
- families: Family[] | undefined
80
- node: CategoryNode
81
- uiSpec: SelectionUISpecifier
82
- single?: {
83
- items: LineItem[]
84
- Selector: ComponentType<ItemSelectorProps>
85
- selOptions: ItemSelectorOptions | undefined
86
- scrollable: boolean
87
- showItemMedia: boolean
88
- }
89
- multi?: {
90
- Selector: ComponentType<MultiFamilySelectorProps>
91
- selectorOptions: MultiFamilySelectorOptions | undefined
92
- itemOptions: ItemSelectorOptions | undefined
93
- initialFamilyId: string
94
- }
95
- } | undefined>(undefined)
96
-
97
- const [changeMeToRerender, setChangeMeToRerender] = useState<boolean>(false)
98
-
99
- useEffect(() => {
100
-
101
- if (!skuPath || skuPath.length === 0 ) {
102
- // The component is being hidden (w an amination)
103
- // keep things the same so no layout jump
104
- return
105
- }
106
-
107
- const peek = cmmc.peek(skuPath)
108
- if (typeof peek === 'string') {
109
- throw new Error(peek)
110
- }
111
- if (peek.role === 'non-outermost') {
112
- throw new Error(`BuyCard: skuPath ${skuPath} isn't an outermost tree node or product family!`)
113
- }
114
-
115
- const uiSpec = getSelectionUISpecifier(skuPath)
116
-
117
- r.current = {
118
- ...peek,
119
- node: peek.node!, // else exception was thrown.
120
- uiSpec,
121
- }
122
-
123
- if (peek.role === 'single-family') {
124
-
125
- const sort = (): 'none' | 'asc' | 'desc' => {
126
- const options = uiSpec.singleFamily?.options ?? {}
127
- const showSlider = 'showSlider' in options ? options.showSlider! : true
128
- return ('sort' in options) ?
129
- options.sort!
130
- :
131
- (showSlider ? 'asc' : 'none')
132
- }
133
-
134
- const items = peek.family!.products as LineItem[]
135
- const Selector: ComponentType<ItemSelectorProps> =
136
- (uiSpec.singleFamily?.type === 'buttons') ? ButtonItemSelector : CarouselItemSelector
137
- const selOptions = uiSpec.singleFamily?.options
138
-
139
- r.current.single = {
140
- items: sortItems(items, sort()),
141
- Selector,
142
- selOptions,
143
- scrollable: !!(items.length > SCROLL.scrollAfter),
144
- showItemMedia: uiSpec.singleFamily?.type !== 'carousel'
145
- }
146
-
147
- const currItem = peek.item ?? r.current.single.items[0]
148
- cmmc.setCurrentItem(currItem.sku)
149
- }
150
- else {
151
- const initialFamily = peek.family ? peek.family : peek.families![0]
152
- // TODO: Does this ever need to be sorted??
153
- const currItem = peek.item ?? initialFamily.products[0]
154
-
155
- // sets currentFamily as well
156
- cmmc.setCurrentItem(currItem.sku)
157
-
158
- r.current.multi = {
159
- Selector: (uiSpec.multiFamily?.type === 'family-carousel') ?
160
- FamilyCarousel
161
- :
162
- AllVariantsCarousel,
163
- itemOptions: uiSpec.multiFamily?.itemOptions,
164
- selectorOptions: uiSpec.multiFamily?.selectorOptions,
165
- initialFamilyId: initialFamily.id
166
- }
167
- }
168
-
169
- // Must do this since Dialog code takes this comp out of the React shadow DOM
170
- // (only in prod apparently.)
171
- setChangeMeToRerender(!changeMeToRerender)
172
- }, [skuPath])
173
-
174
- const MultiFamilyUI: React.FC<{
175
- Selector: ComponentType<MultiFamilySelectorProps>
176
- selectorOptions: MultiFamilySelectorOptions | undefined
177
- itemOptions: ItemSelectorOptions | undefined
178
- families: Family[]
179
- parent: CategoryNode
180
- clx?: string
181
- }> = ({
182
- Selector,
183
- itemOptions,
184
- selectorOptions,
185
- families,
186
- parent,
187
- clx=''
188
- }) => (
189
- <Selector
190
- families={families}
191
- parent={parent}
192
- clx={clx}
193
- itemOptions={itemOptions}
194
- selectorOptions={selectorOptions}
195
- mediaConstraint={MEDIA_CONSTRAINT}
196
- mobile={mobile}
197
- />
198
- )
199
-
200
- const Buttons: React.FC<{clx?: string}> = observer(({
201
- clx=''
202
- }) => (cmmc.currentItem ? (
203
- <div className={clx}>
204
- <AddToCartWidget
205
- item={cmmc.currentItem}
206
- onQuantityChanged={onQuantityChanged}
207
- variant={cmmc.cartEmpty ? 'primary' : 'outline'}
208
- className={addBtnClx}
209
- />
210
- {!cmmc.cartEmpty && checkoutButton}
211
- </div>
212
- ) : null))
213
-
214
- return (
215
- <div className={cn(
216
- 'px-4 md:px-6 pt-3 pb-4 flex flex-col gap-1 items-center',
217
- r.current?.single?.scrollable ? SCROLL.scrollHeightClx : 'h-auto',
218
- clx,
219
- )}>
220
- {r.current?.single ? (
221
- <SingleFamilySelector
222
- {...r.current.single}
223
- mediaConstraint={MEDIA_CONSTRAINT}
224
- mobile={mobile}
225
- clx={selectorClx}
226
- />
227
- ) : (r.current?.multi && r.current.families && /* safegaurd for first render, etc. */ (
228
- <MultiFamilyUI
229
- {...r.current.multi}
230
- families={r.current.families}
231
- parent={r.current.node}
232
- clx={selectorClx}
233
- />
234
- ))}
235
- <Buttons clx={cn(
236
- 'self-stretch mt-8 flex flex-col items-center gap-3 shrink-0 grow-0',
237
- buttonsAreaClx
238
- )}/>
239
- </div >
240
- )
241
- }
242
-
243
- export default CarouselBuyCard
@@ -1,259 +0,0 @@
1
- 'use client'
2
- import React, { useCallback, useEffect, useRef, useState } from 'react'
3
- import { reaction } from 'mobx'
4
- import { observer } from 'mobx-react-lite'
5
-
6
- import {
7
- cn,
8
- ApplyTypography,
9
- type CarouselApi,
10
- Carousel,
11
- CarouselContent,
12
- CarouselItem,
13
- CarouselPrevious,
14
- CarouselNext,
15
- MediaStack,
16
- } from '@hanzo/ui'
17
- import type { MultiFamilySelectorProps, LineItem } from '../../../types'
18
- import {
19
- formatCurrencyValue,
20
- accessItemOptions,
21
- accessMultiSelectorOptions
22
- } from '../../../util'
23
-
24
- import QuantityIndicator from '../../item-selector/quantity-indicator'
25
- import { ButtonItemSelector, useCommerce } from '../../..'
26
-
27
- const debugBorder = (c: 'r' | 'g' | 'b', disable: boolean = true): string => {
28
-
29
- if (disable) {
30
- return ''
31
- }
32
- switch (c) {
33
- case 'r': return ' border border-[#ffaaaa] '
34
- case 'g': return ' border border-[#aaffaa] '
35
- case 'b': return ' border border-[#aaaaff] '
36
- }
37
- }
38
-
39
- const DEFAULT_CONSTRAINT = {w: 250, h: 250}
40
-
41
- const AllVariantsCarousel: React.FC<MultiFamilySelectorProps> = ({
42
- families,
43
- parent,
44
- clx='',
45
- itemClx='',
46
- itemOptions,
47
- selectorOptions,
48
- mediaConstraint=DEFAULT_CONSTRAINT,
49
- mobile=false, // not relavant to any children
50
- }) => {
51
-
52
- const cmmc = useCommerce()
53
- const r = useRef<{
54
- api: CarouselApi | undefined
55
- items: LineItem[]
56
- dontRespond: boolean
57
- initialIndex: number
58
- } | undefined>(undefined)
59
-
60
-
61
- const [changeMeToRerender, setChangeMeToRerender] = useState<boolean>(false)
62
- // Safe, since Carousel is only render (and this method passed), once the ref is initialized.
63
- const setApi = (api: CarouselApi) => { r.current!.api = api }
64
-
65
- const onSelect = useCallback((emblaApi: CarouselApi) => {
66
- if (r.current?.dontRespond) {
67
- r.current.dontRespond = false
68
- return
69
- }
70
- const index = emblaApi.selectedScrollSnap()
71
- if (index !== -1) {
72
- const item = r.current?.items[index]
73
- cmmc.setCurrentItem(item?.sku) // (sets currentFamily as well)
74
- }
75
- if (r.current) {
76
- r.current.dontRespond = false
77
- }
78
- }, [])
79
-
80
- useEffect(() => {
81
-
82
- const items = families.map((fam) => (fam.products as LineItem[])).flat()
83
- const foundIndex = cmmc.currentItem ? items.findIndex((item) => (cmmc.currentItem!.sku == item.sku)) : -1
84
- r.current = {
85
- items,
86
- initialIndex: foundIndex === -1 ? 0 : foundIndex,
87
- dontRespond: false,
88
- api: undefined
89
- }
90
-
91
- setChangeMeToRerender(!changeMeToRerender)
92
-
93
- // This responds to the swatch clicks
94
- return reaction(
95
- () => (cmmc.currentItem),
96
- (item) => {
97
- if (r.current && r.current.api) {
98
- const index = r.current.items.findIndex((_item: LineItem) => (_item.sku === item?.sku))
99
- if (index && index !== -1) {
100
- // no need to sync family, since ui only allows selecting within a family
101
- r.current.dontRespond = true
102
- r.current.api.scrollTo(index)
103
- }
104
- }
105
- }
106
- )
107
- }, [])
108
-
109
- const Header: React.FC<{clx?: string}> = observer(({
110
- clx=''
111
- }) => {
112
-
113
- const { showParentTitle, parentByline: parentBylineMode } = accessMultiSelectorOptions(selectorOptions)
114
- const { familyTitle, showFamilyByline } = accessItemOptions(itemOptions)
115
-
116
- const parentTitleDisplay = showParentTitle ? parent.label : undefined
117
- const parentBylineDisplay = (parentBylineMode === 'own-line') ? parent.subNodesLabel : ''
118
-
119
- const title = (familyTitle === 'none' ?
120
- undefined
121
- :
122
- (familyTitle === 'long' ?
123
- cmmc.currentFamily?.title
124
- :
125
- (cmmc.currentFamily?.titleShort ?? cmmc.currentFamily?.title)
126
- )
127
- )
128
-
129
- let titleLinePrefix = (parentBylineMode === 'none' || parentBylineMode === 'own-line') ? '' : (parent.subNodesLabel ?? '')
130
- if (titleLinePrefix.length > 0 && title && parentBylineMode === 'comma-sep') {
131
- titleLinePrefix += ', '
132
- }
133
- else if (titleLinePrefix.length > 0 && title && parentBylineMode === 'colon-sep') {
134
- titleLinePrefix += ': '
135
- }
136
- const titleDisplay = title ? (titleLinePrefix + title) : undefined
137
- const bylineDisplay = (familyTitle !== 'none') && showFamilyByline ? cmmc.currentFamily?.byline : undefined
138
-
139
- return (
140
- <ApplyTypography className={cn('flex flex-col items-center !gap-0 [&>*]:!m-0 ', clx)} >
141
- {parentTitleDisplay && <h4>{parentTitleDisplay}</h4>}
142
- {parentBylineDisplay && <h4>{parentBylineDisplay}</h4>}
143
- {titleDisplay && <h4>{titleDisplay}</h4>}
144
- {bylineDisplay && (<h6 >{bylineDisplay}</h6>)}
145
- </ApplyTypography>
146
- )
147
- })
148
-
149
- const ItemInfo: React.FC<{
150
- clx?: string
151
- labelClx?: string
152
- }> = observer(({
153
- clx='',
154
- labelClx=''
155
- }) => {
156
-
157
- const {
158
- showPrice,
159
- showQuantity,
160
- showFamilyInOption,
161
- showByline,
162
- } = accessItemOptions(itemOptions)
163
-
164
- const optionLabel = () => (
165
- showFamilyInOption ?
166
- (cmmc.currentItem!.familyTitle + ', ' + cmmc.currentItem!.optionLabel)
167
- :
168
- cmmc.currentItem!.optionLabel
169
- )
170
-
171
- return (cmmc.currentItem && (
172
- <ApplyTypography className={cn('flex flex-col items-center [&>*]:!m-0 !gap-1 ', clx)}>
173
- <div className={
174
- 'flex items-center gap-1 [&>*]:!m-0 ' +
175
- debugBorder('g') +
176
- (showFamilyInOption ? 'flex-col' : 'flex-row')
177
- }>
178
- <h6 className={cn('font-semibold', labelClx)}>
179
- {optionLabel() + (showPrice && !showFamilyInOption ? ',' : '')}
180
- </h6>
181
- <div className={
182
- 'flex items-center gap-1 [&>*]:!m-0 flex-row ' + debugBorder('b') +
183
- (showFamilyInOption ? 'w-full justify-between' : '')
184
- }>
185
- {showPrice && (<p>{formatCurrencyValue(cmmc.currentItem.price)}</p>)}
186
- {showQuantity && (
187
- <QuantityIndicator
188
- item={cmmc.currentItem}
189
- clx='h-[22px] ml-4'
190
- iconClx='fill-foreground'
191
- digitClx='not-typography font-semibold text-primary-fg leading-none font-sans text-xs'
192
- />
193
- )}
194
- </div>
195
- </div>
196
- {showByline && cmmc.currentItem.byline && (<p>{cmmc.currentItem.byline}</p>)}
197
- </ApplyTypography>
198
- ))
199
- })
200
-
201
- const Swatches: React.FC<{
202
- clx?: string
203
- }> = observer(({
204
- clx=''
205
- }) => {
206
- const { showItemSwatches } = accessMultiSelectorOptions(selectorOptions)
207
- if (
208
- !showItemSwatches ||
209
- !cmmc.currentFamily
210
- //|| cmmc.currentFamily.products.length === 1
211
- ) {
212
- return null
213
- }
214
- return <ButtonItemSelector
215
- items={cmmc.currentFamily.products as LineItem[]}
216
- selectedItemRef={cmmc}
217
- selectSku={cmmc.setCurrentItem.bind(cmmc)}
218
- clx={clx}
219
- options={{
220
- buttonType: 'image',
221
- horizButtons: true
222
- }}
223
- />
224
- })
225
-
226
- return (
227
- <div className={cn('w-full flex flex-col items-center', clx)}>
228
- <Header />
229
- {r.current && /* Only render once we've set the ref fields, or else bad things! */ (
230
- <Carousel
231
- options={{loop: true, startIndex: r.current.initialIndex}}
232
- className={'w-full px-2' + debugBorder('r')}
233
- onCarouselSelect={onSelect}
234
- setApi={setApi}
235
- >
236
- <CarouselContent>
237
- {r.current.items.map((item) => (
238
- <CarouselItem key={item.sku} className={cn('p-2 flex flex-col justify-center items-center', itemClx)}>
239
- <MediaStack media={item} constrainTo={mediaConstraint} className='my-4'/>
240
- </CarouselItem>
241
- ))}
242
- </CarouselContent>
243
- {r.current.items.length > 1 && (<>
244
- <CarouselPrevious className='left-1'/>
245
- <CarouselNext className='right-1'/>
246
- </>)}
247
- </Carousel>
248
- )}
249
- <div className='flex flex-col items-center justify-start'>
250
- <ItemInfo labelClx='!text-base font-medium'/>
251
- <Swatches clx='mt-2'/>
252
- </div>
253
- </div>
254
- )
255
- }
256
-
257
- export {
258
- AllVariantsCarousel as default
259
- }
@@ -1,75 +0,0 @@
1
- 'use client'
2
- import React, { useCallback, useRef } from 'react'
3
-
4
- import {
5
- cn,
6
- type CarouselApi,
7
- Carousel,
8
- CarouselContent,
9
- CarouselItem,
10
- CarouselPrevious,
11
- CarouselNext,
12
- } from '@hanzo/ui'
13
- import type { MultiFamilySelectorProps } from '../../../../types'
14
-
15
- import FamilySlide from './slide'
16
- import { FamilyCarouselState } from './state'
17
-
18
- import { useCommerce } from '../../../..'
19
-
20
- const FamilyCarousel: React.FC<MultiFamilySelectorProps> = ({
21
- families,
22
- clx='',
23
- itemClx='',
24
- itemOptions,
25
- selectorOptions,
26
- mediaConstraint={w: 250, h: 250},
27
- mobile=false,
28
- }) => {
29
-
30
- const cmmc = useCommerce()
31
- const stateRef = useRef<FamilyCarouselState>(
32
- new FamilyCarouselState(families, cmmc.setCurrentItem.bind(cmmc))
33
- )
34
-
35
- const onSelect = useCallback((emblaApi: CarouselApi) => {
36
- const index = emblaApi.selectedScrollSnap()
37
- if (index !== -1) {
38
- stateRef.current.setCurrentFamily(families[index].id)
39
- }
40
- }, [stateRef.current])
41
-
42
- return (
43
- <Carousel
44
- className={cn('px-2', clx)}
45
- options={{loop: false}}
46
- onCarouselSelect={onSelect}
47
- >
48
- <CarouselContent>
49
- {families.map((family) => {
50
- const slideState = stateRef.current.getSlideState(family.id)
51
- if (!slideState) {
52
- throw new Error(`FamilyCarousel: no SlideState for family '${family.id}'!`)
53
- }
54
- return (
55
- <CarouselItem key={family.id} className='p-2 flex flex-col justify-center items-center'>
56
- <FamilySlide
57
- family={family}
58
- selectedItemRef={slideState}
59
- selectSku={slideState.selectSku.bind(slideState)}
60
- mediaConstraint={mediaConstraint}
61
- options={itemOptions}
62
- mobile={mobile}
63
- clx={itemClx}
64
- />
65
- </CarouselItem>
66
- )}
67
- )}
68
- </CarouselContent>
69
- <CarouselPrevious className='left-1'/>
70
- <CarouselNext className='right-1'/>
71
- </Carousel>
72
- )
73
- }
74
-
75
- export default FamilyCarousel
@@ -1,82 +0,0 @@
1
- 'use client'
2
- import React from 'react'
3
-
4
- import {
5
- ApplyTypography,
6
- MediaStack,
7
- cn,
8
- } from '@hanzo/ui'
9
- import type { Dimensions } from '@hanzo/ui/types'
10
-
11
- import type { ItemSelectorOptions, Family, LineItem, ItemSelector } from '../../../../types'
12
- import { ButtonItemSelector } from '../../..'
13
- const FamilySlide: React.FC<Omit<ItemSelector, 'items'> & {
14
- family: Family
15
- mediaConstraint: Dimensions
16
- clx?: string
17
- mobile?: boolean,
18
- options?: ItemSelectorOptions
19
- }> = ({
20
- family,
21
- selectedItemRef,
22
- selectSku,
23
- mediaConstraint: cnst = {w: 200, h: 200},
24
- clx='',
25
- mobile=false,
26
- options={}
27
- }) => {
28
-
29
- const titleSpec = 'title' in options ? options.title : 'short'
30
- const showByline = 'showByline' in options ? options.showByline : true
31
- const title = titleSpec === 'long' ?
32
- family.title
33
- :
34
- (
35
- titleSpec === 'short' ?
36
- (family.titleShort ? family.titleShort : family.title )
37
- :
38
- undefined
39
- )
40
-
41
- const byline = (showByline && family.byline) ? family.byline : undefined
42
- /*
43
- let byline: string | undefined = undefined
44
- if (showByline) {
45
- if (family.byline) {
46
- // if byline names another field thats a function, call it
47
- if (family.byline in family && typeof (family as any)[family.byline] === 'function') {
48
- byline = (family as any)[family.byline]() as string
49
- }
50
- else {
51
- byline = family.byline
52
- }
53
- }
54
- }
55
- */
56
-
57
- return (
58
- <div className={cn('flex flex-col items-center gap-4', clx)} data-vaul-no-drag >
59
- {title && (
60
- <ApplyTypography className='flex flex-col items-center !gap-1 [&>*]:!m-0' data-vaul-no-drag >
61
- <h6 className='font-bold text-lg'>{title}</h6>
62
- {byline && (<p className={''}>{byline}</p>)}
63
- </ApplyTypography>
64
- )}
65
- <MediaStack
66
- media={selectedItemRef.item!}
67
- constrainTo={cnst}
68
- />
69
- <ButtonItemSelector
70
- items={family.products as LineItem[]}
71
- selectedItemRef={selectedItemRef}
72
- selectSku={selectSku}
73
- scrollable={false}
74
- mobile={mobile}
75
- options={options}
76
- itemClx='text-sm'
77
- />
78
- </div>
79
- )
80
- }
81
-
82
- export default FamilySlide