@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,213 +0,0 @@
1
- 'use client'
2
- import React from 'react'
3
- import { observer } from 'mobx-react-lite'
4
-
5
- import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
6
-
7
- import {
8
- Label,
9
- RadioGroup,
10
- ScrollArea,
11
- cn,
12
- MediaStack,
13
- } from '@hanzo/ui'
14
- import type { Dimensions } from '@hanzo/ui/types'
15
-
16
- import type { ItemSelectorProps, LineItem } from '../../types'
17
- import { accessItemOptions, formatCurrencyValue } from '../../util'
18
-
19
- import QuantityIndicator from './quantity-indicator'
20
-
21
- const DEFAULT_CONSTRAINT = {h: 36, w: 72} // // Apple suggest 42px for clickability
22
-
23
- // `ImageDef.rounded` is a corner of the radius scale, spelled by whoever wrote
24
- // the catalog. It used to be pasted straight into a class — `rounded-${...}` —
25
- // and Tailwind reads source text, so no such rule was ever generated: setting
26
- // `rounded: 'full'` produced LESS rounding than leaving it unset, because the
27
- // truthy branch handed back a class name with nothing behind it while the
28
- // default branch at least said `rounded-sm`. Writing the scale out is what makes
29
- // the extractor see it. Anything off the scale falls back to the default, which
30
- // is what the old code did by accident for every value.
31
- const ROUNDED: Record<string, string> = {
32
- none: 'rounded-none',
33
- sm: 'rounded-sm',
34
- md: 'rounded-md',
35
- lg: 'rounded-lg',
36
- xl: 'rounded-xl',
37
- '2xl': 'rounded-2xl',
38
- '3xl': 'rounded-3xl',
39
- full: 'rounded-full',
40
- }
41
-
42
- const ImageRadioGroupItem = React.forwardRef<
43
- React.ElementRef<typeof RadioGroupPrimitive.Item>,
44
- Omit<React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>, 'value' | 'id'> & {
45
- item: LineItem,
46
- constrainTo: Dimensions
47
- }
48
- >(({
49
- item,
50
- constrainTo,
51
- className,
52
- ...props
53
- }, ref) => {
54
-
55
- const img = item.optionImg ? item.optionImg : item.img
56
-
57
- return (
58
- <RadioGroupPrimitive.Item
59
- ref={ref}
60
- data-vaul-no-drag
61
- className={cn(
62
- 'ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring',
63
- 'focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
64
- className,
65
- 'overflow-hidden',
66
- (img?.rounded && ROUNDED[img.rounded]) || 'rounded-sm'
67
- )}
68
- {...props}
69
- id={item.sku}
70
- value={item.sku}
71
- >
72
- {img ? (
73
- <MediaStack media={{img}} constrainTo={constrainTo} />
74
- ) : ( // placeholder so things align
75
- <div style={{height: constrainTo.h, width: constrainTo.w}}/>
76
- )}
77
- </RadioGroupPrimitive.Item>
78
- )
79
- })
80
- ImageRadioGroupItem.displayName = 'ImageRadioGroupItem'
81
-
82
- const ButtonItemSelector: React.FC<ItemSelectorProps> = observer(({
83
- items,
84
- selectedItemRef: itemRef,
85
- selectSku,
86
- clx='',
87
- itemClx='',
88
- soleItemClx='',
89
- scrollable=false,
90
- mobile=false,
91
- options={}
92
- }) => {
93
-
94
- const {
95
- showPrice,
96
- showQuantity,
97
- showFamilyInOption,
98
- buttonType,
99
- horizButtons,
100
- showButtonIfOnlyOne
101
- } = accessItemOptions(options)
102
-
103
- const showImage = buttonType !== 'text'
104
- const showText = buttonType !== 'image'
105
- const showBoth = buttonType === 'image-and-text'
106
-
107
- const labelAndPrice = (item : LineItem) => (
108
- (showFamilyInOption ? (item.familyTitle + ', ' + item.optionLabel) : item.optionLabel) +
109
- (showPrice ? ((showFamilyInOption ? ': ' : ', ') + formatCurrencyValue(item.price)) : '')
110
- )
111
-
112
- const Item: React.FC<{
113
- item: LineItem
114
- selected: boolean
115
- }> = observer(({
116
- item,
117
- selected
118
- }) => {
119
-
120
- const textClx = (selected) ? 'text-accent' : 'text-muted'
121
- const cursorClx = (selected) ? 'hover:cursor-default' : 'hover:cursor-pointer'
122
- const justifyClx = (showBoth) ? '' : 'justify-center'
123
- // If no image, the Label must fill the entire button since it has to be
124
- // clickable all the way to the border
125
- const paddingClx = (showImage) ? (scrollable ? 'px-4 py-2' : 'px-2 py-2' ) : ''
126
- let bgClx = ''
127
- let borderClx = ''
128
- if (scrollable) {
129
- borderClx += (selected) ? 'border-foreground border ' : 'border-b border-muted-2 '
130
- }
131
- else if (!showImage) {
132
- borderClx += 'border rounded-lg '
133
- borderClx += (selected) ? 'border-foreground ' : 'border-muted-2 '
134
- bgClx += 'hover:bg-level-2'
135
- }
136
-
137
- const outerClx = ['h-10 flex items-center', justifyClx, paddingClx, bgClx, borderClx]
138
-
139
- return (
140
- <div className={cn(...outerClx, itemClx )} data-vaul-no-drag >
141
- <ImageRadioGroupItem
142
- item={item}
143
- constrainTo={DEFAULT_CONSTRAINT}
144
- className={cn(
145
- cursorClx,
146
- (scrollable ? '' : 'border-transparent border-2 data-[state=checked]:border-foreground'),
147
- showBoth ? 'mr-2' : (showImage ? '' : 'hidden')
148
- )}
149
- />
150
- <Label htmlFor={item.sku} className={cn(
151
- showQuantity ? 'flex items-center' : 'block',
152
- textClx,
153
- cursorClx,
154
- (showImage ? '' : 'self-stretch w-full flex items-center justify-center px-3 py-2'),
155
- (showText ? '' : 'hidden')
156
- )}>
157
- <div className={showQuantity ? 'grow' : ''}>{labelAndPrice(item)}</div>
158
- {showQuantity && (
159
- <QuantityIndicator
160
- item={item}
161
- clx='grow-0 shrink-0 h-[20px] ml-2'
162
- iconClx={selected ? 'fill-foreground' : 'fill-muted'}
163
- digitClx='font-semibold text-primary-fg leading-none font-sans text-xxs'
164
- />
165
- )}
166
- </Label>
167
- </div>
168
- )
169
- })
170
-
171
- return showButtonIfOnlyOne || items.length > 1 ? (
172
- <RadioGroup
173
- className={cn(
174
- (scrollable ? 'shrink min-h-0 gap-0' : (mobile ? 'gap-3' : 'gap-1')),
175
- (horizButtons ? 'grid gap-1' : 'flex flex-col'),
176
- (mobile && showText) ? 'min-w-pr-50' : '',
177
- clx,
178
- )}
179
- // One column per item, same as the family tabs: the count is data, and
180
- // data cannot be a class name. Interpolating it produced a rule only when
181
- // the number happened to be 3, because two other files in this package
182
- // spell that one literally — so this laid out correctly in a three-option
183
- // selector and stacked in every other.
184
- style={horizButtons ? { gridTemplateColumns: `repeat(${items.length}, minmax(0, 1fr))` } : undefined}
185
- onValueChange={selectSku}
186
- value={itemRef.item ? itemRef.item.sku : ''}
187
- data-vaul-no-drag
188
- >
189
- {scrollable ? (
190
- <ScrollArea className='mt-2 w-full h-full py-0 border border-muted-2' data-vaul-no-drag>
191
- {items.map((item) => (
192
- <Item item={item} selected={itemRef.item?.sku === item.sku} key={item.sku}/>
193
- ))}
194
- </ScrollArea>
195
- ) : (<>
196
- {items.map((item) => (
197
- <Item item={item} selected={itemRef.item?.sku === item.sku} key={item.sku}/>
198
- ))}
199
- </>)}
200
- </RadioGroup>
201
- ) : (
202
- <div className={cn(showQuantity ? 'flex items-center' : 'block', soleItemClx )}>
203
- <div className={showQuantity ? 'grow' : ''}>
204
- {labelAndPrice(items[0])}
205
- </div>
206
- {showQuantity && (
207
- <QuantityIndicator item={items[0]} clx='grow-0 shrink-0 font-semibold text-sm leading-none px-2' />
208
- )}
209
- </div>
210
- )
211
- })
212
-
213
- export default ButtonItemSelector
@@ -1,196 +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 CarouselOptions,
10
- type CarouselApi,
11
- Carousel,
12
- CarouselContent,
13
- CarouselItem,
14
- CarouselPrevious,
15
- CarouselNext,
16
- MediaStack,
17
- } from '@hanzo/ui'
18
- import type { Dimensions } from '@hanzo/ui/types'
19
-
20
- import type { ItemSelectorProps, LineItem } from '../../../types'
21
- import { formatCurrencyValue, accessItemOptions } from '../../../util'
22
-
23
- import QuantityIndicator from '../quantity-indicator'
24
- import ItemCarouselSlider from './slider'
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
- clx?: string
39
- }> = ({
40
- item,
41
- constrainTo,
42
- clx=''
43
- }) => (
44
- <CarouselItem className={cn('p-2 flex flex-col justify-center items-center', clx)}>
45
- <MediaStack media={item} constrainTo={constrainTo} className='my-4' />
46
- </CarouselItem>
47
- )
48
-
49
- const CarouselItemSelector: React.FC<ItemSelectorProps> = observer(({
50
- items,
51
- selectSku,
52
- selectedItemRef: itemRef,
53
- clx='',
54
- itemClx='',
55
- options={},
56
- ext={
57
- options: {loop: true},
58
- constrainTo: DEFAULT_CONSTRAINT,
59
- imageOnly: false
60
- } satisfies CarouselItemSelectorPropsExt
61
- }) => {
62
-
63
- const {
64
- showPrice,
65
- showQuantity,
66
- showFamilyInOption,
67
- showByline,
68
- showSlider,
69
- } = accessItemOptions(options)
70
-
71
- const elbaApiRef = useRef<CarouselApi | undefined>(undefined)
72
- const scrollToRef = useRef<((index: number) => void) | undefined>(undefined)
73
- const dontRespondRef = useRef<boolean>(false)
74
-
75
- const setApi = (api: CarouselApi) => {elbaApiRef.current = api}
76
- const setScrollTo = (scrollTo: (index: number) => void) => { scrollToRef.current = scrollTo}
77
-
78
- const carouselOptions = 'options' in ext ? ext.options : undefined
79
- const constrainTo = 'constrainTo' in ext ? ext.constrainTo : DEFAULT_CONSTRAINT
80
- const imageOnly = 'imageOnly' in ext ? ext.imageOnly : false
81
-
82
- const onSelect = useCallback((emblaApi: CarouselApi) => {
83
- if (dontRespondRef.current) {
84
- dontRespondRef.current = false
85
- return
86
- }
87
- const index = emblaApi.selectedScrollSnap()
88
- if (index !== -1) {
89
- selectSku(items[index].sku)
90
- if (scrollToRef.current) {
91
- scrollToRef.current(index)
92
- }
93
- }
94
- dontRespondRef.current = false
95
- }, [scrollToRef.current ])
96
-
97
- useEffect(() => {
98
- return reaction(
99
- () => (itemRef.item),
100
- (item) => {
101
- if (elbaApiRef.current) {
102
- const index = items.findIndex((el) => (el.sku === item?.sku))
103
- if (index !== -1) {
104
- dontRespondRef.current = true
105
- elbaApiRef.current.scrollTo(index)
106
- }
107
- }
108
- }
109
- )
110
- }, [])
111
-
112
- const optionLabel = () => (
113
- showFamilyInOption ?
114
- (itemRef.item?.familyTitle + ', ' + itemRef.item?.optionLabel)
115
- :
116
- itemRef.item?.optionLabel
117
- )
118
-
119
- const debugBorder = (c: 'r' | 'g' | 'b'): string => {
120
- return ''
121
-
122
- switch (c) {
123
- case 'r': return ' border border-[#ffaaaa] '
124
- case 'g': return ' border border-[#aaffaa] '
125
- case 'b': return ' border border-[#aaaaff] '
126
- }
127
- }
128
-
129
- const onScrollIndexChange = (index: number) => {
130
- dontRespondRef.current = true
131
- selectSku(items![index].sku)
132
- elbaApiRef.current?.scrollTo(index)
133
- }
134
-
135
- return (
136
- <div className={cn('w-full flex flex-col items-center', clx)}>
137
- <Carousel
138
- options={showSlider ? {...carouselOptions, loop: false} : carouselOptions }
139
- className={'w-full px-2' + debugBorder('r')}
140
- onCarouselSelect={onSelect}
141
- setApi={setApi}
142
- >
143
- <CarouselContent>
144
- {items.map((item) => (
145
- <ItemSlide key={item.sku} item={item} constrainTo={constrainTo} clx={itemClx} />
146
- ))}
147
- </CarouselContent>
148
- {!showSlider && (<>
149
- <CarouselPrevious className='left-1'/>
150
- <CarouselNext className='right-1'/>
151
- </>)}
152
- </Carousel>
153
-
154
- {(!imageOnly && itemRef.item) && (<>
155
- <ApplyTypography className='flex flex-col items-center [&>*]:!m-0 !gap-1 '>
156
- <div className={
157
- 'flex items-center gap-1 [&>*]:!m-0 ' + debugBorder('g') +
158
- (showFamilyInOption ? 'flex-col' : 'flex-row')
159
- }>
160
- <h6 className='font-semibold'>
161
- {optionLabel() + (showPrice && !showFamilyInOption ? ',' : '')}
162
- </h6>
163
- <div className={
164
- 'flex items-center gap-1 [&>*]:!m-0 flex-row ' + debugBorder('b') +
165
- (showFamilyInOption ? 'w-full justify-between' : '')
166
- }>
167
- {showPrice && (<p>{formatCurrencyValue(itemRef.item.price)}</p>)}
168
- {showQuantity && (
169
- <QuantityIndicator
170
- item={itemRef.item}
171
- clx='h-[22px] ml-4'
172
- iconClx='fill-foreground'
173
- digitClx='not-typography font-semibold text-primary-fg leading-none font-sans text-xs'
174
- />
175
- )}
176
- </div>
177
- </div>
178
- {showByline && itemRef.item.byline && (<p>{itemRef.item.byline}</p>)}
179
- </ApplyTypography>
180
- {showSlider && items.length > 1 && (
181
- <ItemCarouselSlider
182
- clx='mt-5 w-[320px]'
183
- numStops={items.length}
184
- setScrollTo={setScrollTo}
185
- onIndexChange={onScrollIndexChange}
186
- />
187
- )}
188
- </>)}
189
- </div>
190
- )
191
- })
192
-
193
- export {
194
- type CarouselItemSelectorPropsExt,
195
- CarouselItemSelector as default
196
- }
@@ -1,37 +0,0 @@
1
- 'use client'
2
- import React, { useEffect, useState } from 'react'
3
-
4
- import {
5
- Slider,
6
- } from '@hanzo/ui'
7
- const ItemCarouselSlider: React.FC<{
8
- clx?: string
9
- setScrollTo: (scrollTo: (index: number) => void) => void
10
- onIndexChange: (i: number) => void
11
- numStops: number
12
- }> = ({
13
- clx='',
14
- setScrollTo,
15
- onIndexChange,
16
- numStops,
17
- }) => {
18
-
19
- const [index, setIndex] = useState<number>(0)
20
- useEffect(() => { setScrollTo(setIndex) }, [setScrollTo])
21
-
22
- const onValueChange = (v: number[]) => { setIndex(v[0]); onIndexChange(v[0]) }
23
-
24
- return (
25
- <Slider
26
- className={clx}
27
- defaultValue={[0]}
28
- min={0}
29
- max={numStops - 1}
30
- step={1}
31
- value={[index]}
32
- onValueChange={onValueChange}
33
- />
34
- )
35
- }
36
-
37
- export default ItemCarouselSlider
@@ -1,5 +0,0 @@
1
- export { default as ButtonItemSelector } from './button'
2
- export {
3
- default as CarouselItemSelector,
4
- type CarouselItemSelectorPropsExt
5
- } from './carousel'
@@ -1,50 +0,0 @@
1
- 'use client'
2
- import React from 'react'
3
- import { observer } from 'mobx-react-lite'
4
-
5
- import { type LucideProps } from 'lucide-react'
6
-
7
- import {
8
- cn,
9
- } from '@hanzo/ui'
10
- import type { LineItem } from '../../types'
11
-
12
- // Generalize this.
13
- const BagIcon: React.FC<LucideProps> = (props: LucideProps) => (
14
- <svg fill="currentColor" viewBox='0 0 20 23' {...props}>
15
- <path fillRule="evenodd" d="M5 5a5 5 0 0 1 10 0v1h-2V5a3 3 0 1 0-6 0v1H5V5Zm0 1v4h2V6h6v4h2V6h3.5a1 1 0 0 1 1 1v15a1 1 0 0 1-1 1h-17a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1H5Z" clipRule="evenodd"/>
16
- </svg>
17
- )
18
-
19
- const QuantityIndicator: React.FC<{
20
- item: LineItem
21
- clx?: string
22
- iconClx?: string
23
- digitClx?: string
24
- }> = observer(({
25
- item,
26
- clx='',
27
- iconClx='',
28
- digitClx=''
29
- }) => {
30
-
31
- if (!item.isInCart) {
32
- return <div />
33
- }
34
-
35
- return (
36
- <div className={cn('relative aspect-square flex items-center justify-center', clx)} >
37
- <div className={cn(
38
- 'z-above-content flex flex-col justify-center items-center',
39
- 'absolute left-0 right-0 top-0 bottom-0',
40
- digitClx
41
- )}>
42
- <div className='h-[1px] w-full' />
43
- <div style={{color: 'black' /* tailwind bug */}}>{item.quantity}</div>
44
- </div>
45
- <BagIcon className={cn('relative -top-[12%] h-full ', iconClx, )} aria-hidden="true" />
46
- </div>
47
- )
48
- }) // -top-[3px]
49
-
50
- export default QuantityIndicator
@@ -1,90 +0,0 @@
1
- 'use client'
2
- import React, { useState } from 'react'
3
-
4
- import {
5
- ToggleGroup,
6
- ToggleGroupItem,
7
- cn,
8
- type ToggleGroupSize,
9
- } from '@hanzo/ui'
10
- import type { CategoryNode, StringMutator, StringArrayMutator } from '../../types'
11
- import NodeImage from './node-image'
12
-
13
- const NodeTabs: React.FC<{
14
- levelNodes: CategoryNode[]
15
- mutator: StringMutator | StringArrayMutator
16
- multiple?: boolean
17
- className?: string
18
- /** Layout the caller computes from data. A column count is a number, and a
19
- * number cannot be a class name — Tailwind reads source text, so a template
20
- * literal never becomes a rule. */
21
- style?: React.CSSProperties
22
- buttonClx?: string
23
- itemClx?: string
24
- mobile?: boolean
25
- tabSize?: ToggleGroupSize
26
- show?: 'image' | 'label' | 'image-and-label'
27
- }> = ({
28
- levelNodes,
29
- mutator,
30
- multiple=false,
31
- buttonClx='',
32
- itemClx='',
33
- className='',
34
- style,
35
- mobile=false,
36
- tabSize,
37
- show='image-and-label'
38
- }) => {
39
-
40
- const [last, setLast] = useState<string | undefined>(undefined)
41
-
42
- const handleChangeMultiple = (selected: string[]) => {
43
- (mutator as StringArrayMutator).set(selected)
44
- setLast(selected.length === 1 ? selected[0] : undefined)
45
- }
46
-
47
- const handleChangeSingle = (selected: string) => {
48
- (mutator as StringMutator).set(selected)
49
- if (selected) { setLast(selected) }
50
- }
51
-
52
- const val = multiple
53
- ? (mutator as StringArrayMutator).get()
54
- : (mutator as StringMutator).get()
55
-
56
- // The per-item `rounded` that used to ride here named a 5.x variant. The
57
- // group owns the segmented look now, so passing it did nothing but say it did.
58
- return (
59
- <ToggleGroup
60
- type={multiple ? 'multiple' : 'single'}
61
- value={val}
62
- variant='default'
63
- size={tabSize ? tabSize : (mobile ? 'sm' : 'default')}
64
- onValueChange={multiple ? handleChangeMultiple : handleChangeSingle}
65
- className={className}
66
- // The two type systems meet here. gui types `style` as a native view
67
- // style; on web it hands the object straight to the DOM element, and a
68
- // `grid-template-columns` written this way arrives as inline CSS
69
- // (measured). The cast says which of the two platforms this is.
70
- style={style as React.ComponentProps<typeof ToggleGroup>['style']}
71
- >
72
- {levelNodes.map((treeNode) => (
73
- <ToggleGroupItem
74
- key={treeNode.skuToken}
75
- value={treeNode.skuToken}
76
- disabled={(last && last === treeNode.skuToken || treeNode.skuToken === mutator.get())}
77
- aria-label={`Select ${treeNode.label}`}
78
- className={buttonClx}
79
- >
80
- <span className={cn('flex flex-row justify-center gap-1 h-6 items-center', itemClx)} >
81
- {!(show === 'label') && (<NodeImage treeNode={treeNode} />) }
82
- {(!(show === 'image') || !treeNode.img) && (<span className='whitespace-nowrap'>{treeNode.label}</span>)}
83
- </span>
84
- </ToggleGroupItem>
85
- ))}
86
- </ToggleGroup>
87
- )
88
- }
89
-
90
- export default NodeTabs
@@ -1,31 +0,0 @@
1
- import React from 'react'
2
- import Image from 'next/image'
3
-
4
- import type { CategoryNode } from '../../types'
5
-
6
- const ICON_SIZE = 20
7
-
8
- const NodeImage: React.FC<{
9
- treeNode: CategoryNode
10
- }> = ({
11
- treeNode: {
12
- img,
13
- imgAR: ar,
14
- label
15
- }
16
- }) => {
17
-
18
- if (!img) { return null }
19
- // treat as URL
20
- return (typeof img === 'string') ? (
21
- <Image
22
- src={img as string}
23
- alt={`Toggle ${label}`}
24
- className={'block mr-1 '}
25
- width={ar ? ar * ICON_SIZE : ICON_SIZE}
26
- height={ICON_SIZE}
27
- />
28
- ) : (img as React.ReactNode)
29
- }
30
-
31
- export default NodeImage
package/index.ts DELETED
@@ -1,55 +0,0 @@
1
- export * from './service/context'
2
- export * from './components'
3
- // Impl-dependent, so leave w impl
4
- export type { StandaloneServiceOptions as ServiceOptions } from './service/impls/standalone'
5
- export {
6
- useSyncSkuParamWithCurrentItem,
7
- getFacetValuesMutator,
8
- formatCurrencyValue,
9
- ProductMediaAccessor,
10
- LineItemRef
11
- } from './util'
12
-
13
- export * from './util/selection-ui-specifiers'
14
-
15
- // Commerce API client
16
- export { Commerce, CommerceApiError, hanzoCommerce } from './client'
17
- export type {
18
- CommerceClientConfig,
19
- Balance,
20
- TierResponse,
21
- Transaction,
22
- Subscription,
23
- Plan,
24
- Payment,
25
- UsageRecord,
26
- Coupon,
27
- CouponType,
28
- CouponValidateResult,
29
- Discount,
30
- CheckoutItem,
31
- CheckoutSessionRequest,
32
- CheckoutSessionResponse,
33
- PaymentMethod,
34
- PaymentMethodType,
35
- Referral,
36
- Referrer,
37
- Affiliate,
38
- CreditGrant,
39
- } from './client'
40
-
41
- // Server-side usage metering (the one way every product meters to commerce)
42
- export {
43
- Metering,
44
- DEFAULT_COMMERCE_URL,
45
- identityFromHeaders,
46
- HEADER_USER_ID,
47
- HEADER_ORG_ID,
48
- } from './metering'
49
- export type {
50
- MeteringConfig,
51
- MeteringIdentity,
52
- UsageEvent,
53
- RecordResult,
54
- AuthDecision,
55
- } from './metering'