@hanzo/commerce 7.3.0 → 7.3.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 (83) hide show
  1. package/components/Icons.tsx +34 -34
  2. package/components/add-to-cart-widget.tsx +183 -183
  3. package/components/buy/buy-card.tsx +259 -259
  4. package/components/buy/carousel-buy-card.tsx +242 -242
  5. package/components/buy/multi-family/all-variants-carousel.tsx +261 -261
  6. package/components/buy/multi-family/family-carousel/index.tsx +77 -77
  7. package/components/buy/multi-family/family-carousel/slide.tsx +83 -83
  8. package/components/buy/multi-family/family-carousel/state.ts +87 -87
  9. package/components/buy/multi-family/index.ts +2 -2
  10. package/components/buy/single-family-selector.tsx +90 -90
  11. package/components/buy/title-and-byline.tsx +25 -25
  12. package/components/cart/cart-panel/cart-line-item.tsx +76 -76
  13. package/components/cart/cart-panel/index.tsx +154 -154
  14. package/components/cart/cart-panel/promo-code.tsx +106 -106
  15. package/components/cart/cart-panel/total-area.tsx +60 -60
  16. package/components/checkout/payment-step-form/card-icon-row.tsx +26 -26
  17. package/components/checkout/payment-step-form/card-icons/amex.tsx +32 -32
  18. package/components/checkout/payment-step-form/card-icons/diners-club.tsx +13 -13
  19. package/components/checkout/payment-step-form/card-icons/discover.tsx +25 -25
  20. package/components/checkout/payment-step-form/card-icons/jcb.tsx +26 -26
  21. package/components/checkout/payment-step-form/card-icons/mastercard.tsx +27 -27
  22. package/components/checkout/payment-step-form/card-icons/visa.tsx +25 -25
  23. package/components/checkout/payment-step-form/cc-button.tsx +17 -17
  24. package/components/checkout/payment-step-form/contact-form.tsx +49 -49
  25. package/components/checkout/payment-step-form/crypto-icons/btc.tsx +11 -11
  26. package/components/checkout/payment-step-form/crypto-icons/eth.tsx +20 -20
  27. package/components/checkout/payment-step-form/crypto-icons/usdt.tsx +13 -13
  28. package/components/checkout/payment-step-form/index.tsx +122 -122
  29. package/components/checkout/payment-step-form/methods/bank-transfer.tsx +79 -79
  30. package/components/checkout/payment-step-form/methods/card.tsx +232 -232
  31. package/components/checkout/payment-step-form/methods/crypto.tsx +227 -227
  32. package/components/checkout/payment-step-form/methods/index.ts +22 -22
  33. package/components/checkout/shipping-step-form.tsx +172 -172
  34. package/components/index.ts +11 -11
  35. package/components/item/product-card.tsx +48 -48
  36. package/components/item-selector/button.tsx +188 -188
  37. package/components/item-selector/carousel/index.tsx +197 -197
  38. package/components/item-selector/carousel/slider.tsx +40 -40
  39. package/components/item-selector/index.ts +5 -5
  40. package/components/item-selector/quantity-indicator.tsx +48 -48
  41. package/components/node-tabs/index.tsx +91 -91
  42. package/components/node-tabs/node-image.tsx +31 -31
  43. package/index.ts +12 -12
  44. package/package.json +60 -60
  45. package/service/context.tsx +45 -45
  46. package/service/debug.ts +41 -41
  47. package/service/impls/standalone/actual-line-item.ts +135 -135
  48. package/service/impls/standalone/get-instance.ts +64 -64
  49. package/service/impls/standalone/index.ts +579 -579
  50. package/service/impls/standalone/order/firebase.ts +14 -14
  51. package/service/impls/standalone/order/index.ts +128 -128
  52. package/service/impls/standalone/persistence.ts +33 -33
  53. package/service/path-utils.ts +25 -25
  54. package/service/sep.ts +6 -6
  55. package/tsconfig.json +10 -10
  56. package/types/README.md +1 -1
  57. package/types/category-node.ts +49 -49
  58. package/types/checkout.ts +46 -46
  59. package/types/commerce-config.ts +13 -13
  60. package/types/commerce-service.ts +127 -127
  61. package/types/family.ts +25 -25
  62. package/types/index.ts +15 -15
  63. package/types/item-selector.ts +96 -96
  64. package/types/line-item.ts +29 -29
  65. package/types/multi-family-selector-props.ts +19 -19
  66. package/types/product.ts +20 -20
  67. package/types/promo.ts +9 -9
  68. package/types/selection-ui-specifier.ts +51 -51
  69. package/types/string-mutator.ts +13 -13
  70. package/types/token-separators.ts +7 -7
  71. package/util/analytics.ts +20 -20
  72. package/util/countries.ts +195 -195
  73. package/util/error.ts +34 -34
  74. package/util/index.ts +70 -70
  75. package/util/item-selector-options-accessor.ts +34 -34
  76. package/util/line-item-ref.ts +23 -23
  77. package/util/multi-family-selector-options-accessor.ts +14 -14
  78. package/util/obs-string-mutator.ts +22 -22
  79. package/util/product-media-accessor.ts +58 -58
  80. package/util/promo-codes.ts +108 -108
  81. package/util/selection-ui-specifiers.ts +29 -29
  82. package/util/square-payment.ts +42 -42
  83. package/util/use-sync-sku-param-w-current-item.ts +88 -88
@@ -1,188 +1,188 @@
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
- Image,
9
- Label,
10
- RadioGroup,
11
- ScrollArea
12
- } from '@hanzo/ui/primitives'
13
- import { cn } from '@hanzo/ui/util'
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
- const ImageRadioGroupItem = React.forwardRef<
24
- React.ElementRef<typeof RadioGroupPrimitive.Item>,
25
- Omit<React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>, 'value' | 'id'> & {
26
- item: LineItem,
27
- constrainTo: Dimensions
28
- }
29
- >(({
30
- item,
31
- constrainTo,
32
- className,
33
- ...props
34
- }, ref) => {
35
-
36
- const img = item.optionImg ? item.optionImg : item.img
37
-
38
- return (
39
- <RadioGroupPrimitive.Item
40
- ref={ref}
41
- data-vaul-no-drag
42
- className={cn(
43
- 'ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring',
44
- 'focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
45
- className,
46
- 'overflow-hidden',
47
- img?.rounded ? `rounded-${img.rounded}` : 'rounded-sm'
48
- )}
49
- {...props}
50
- id={item.sku}
51
- value={item.sku}
52
- >
53
- {img ? (
54
- <Image def={img} constrainTo={constrainTo} preload className=''/>
55
- ) : ( // placeholder so things align
56
- <div style={{height: constrainTo.h, width: constrainTo.w}}/>
57
- )}
58
- </RadioGroupPrimitive.Item>
59
- )
60
- })
61
- ImageRadioGroupItem.displayName = 'ImageRadioGroupItem'
62
-
63
- const ButtonItemSelector: React.FC<ItemSelectorProps> = observer(({
64
- items,
65
- selectedItemRef: itemRef,
66
- selectSku,
67
- clx='',
68
- itemClx='',
69
- soleItemClx='',
70
- scrollable=false,
71
- mobile=false,
72
- options={}
73
- }) => {
74
-
75
- const {
76
- showPrice,
77
- showQuantity,
78
- showFamilyInOption,
79
- buttonType,
80
- horizButtons,
81
- showButtonIfOnlyOne
82
- } = accessItemOptions(options)
83
-
84
- const showImage = buttonType !== 'text'
85
- const showText = buttonType !== 'image'
86
- const showBoth = buttonType === 'image-and-text'
87
-
88
- const labelAndPrice = (item : LineItem) => (
89
- (showFamilyInOption ? (item.familyTitle + ', ' + item.optionLabel) : item.optionLabel) +
90
- (showPrice ? ((showFamilyInOption ? ': ' : ', ') + formatCurrencyValue(item.price)) : '')
91
- )
92
-
93
- const Item: React.FC<{
94
- item: LineItem
95
- selected: boolean
96
- }> = observer(({
97
- item,
98
- selected
99
- }) => {
100
-
101
- const textClx = (selected) ? 'text-accent' : 'text-muted'
102
- const cursorClx = (selected) ? 'hover:cursor-default' : 'hover:cursor-pointer'
103
- const justifyClx = (showBoth) ? '' : 'justify-center'
104
- // If no image, the Label must fill the entire button since it has to be
105
- // clickable all the way to the border
106
- const paddingClx = (showImage) ? (scrollable ? 'px-4 py-2' : 'px-2 py-2' ) : ''
107
- let bgClx = ''
108
- let borderClx = ''
109
- if (scrollable) {
110
- borderClx += (selected) ? 'border-foreground border ' : 'border-b border-muted-2 '
111
- }
112
- else if (!showImage) {
113
- borderClx += 'border rounded-lg '
114
- borderClx += (selected) ? 'border-foreground ' : 'border-muted-2 '
115
- bgClx += 'hover:bg-level-2'
116
- }
117
-
118
- const outerClx = ['h-10 flex items-center', justifyClx, paddingClx, bgClx, borderClx]
119
-
120
- return (
121
- <div className={cn(...outerClx, itemClx )} data-vaul-no-drag >
122
- <ImageRadioGroupItem
123
- item={item}
124
- constrainTo={DEFAULT_CONSTRAINT}
125
- className={cn(
126
- cursorClx,
127
- (scrollable ? '' : 'border-transparent border-2 data-[state=checked]:border-foreground'),
128
- showBoth ? 'mr-2' : (showImage ? '' : 'hidden')
129
- )}
130
- />
131
- <Label htmlFor={item.sku} className={cn(
132
- showQuantity ? 'flex items-center' : 'block',
133
- textClx,
134
- cursorClx,
135
- (showImage ? '' : 'self-stretch w-full flex items-center justify-center px-3 py-2'),
136
- (showText ? '' : 'hidden')
137
- )}>
138
- <div className={showQuantity ? 'grow' : ''}>{labelAndPrice(item)}</div>
139
- {showQuantity && (
140
- <QuantityIndicator
141
- item={item}
142
- clx='grow-0 shrink-0 h-[20px] ml-2'
143
- iconClx={selected ? 'fill-foreground' : 'fill-muted'}
144
- digitClx='font-semibold text-primary-fg leading-none font-sans text-xxs'
145
- />
146
- )}
147
- </Label>
148
- </div>
149
- )
150
- })
151
-
152
- return showButtonIfOnlyOne || items.length > 1 ? (
153
- <RadioGroup
154
- className={cn(
155
- (scrollable ? 'shrink min-h-0 gap-0' : (mobile ? 'gap-3' : 'gap-1')),
156
- (horizButtons ? `grid grid-cols-${items.length} gap-1` : 'flex flex-col'),
157
- (mobile && showText) ? 'min-w-pr-50' : '',
158
- clx,
159
- )}
160
- onValueChange={selectSku}
161
- value={itemRef.item ? itemRef.item.sku : ''}
162
- data-vaul-no-drag
163
- >
164
- {scrollable ? (
165
- <ScrollArea className='mt-2 w-full h-full py-0 border border-muted-2' data-vaul-no-drag>
166
- {items.map((item) => (
167
- <Item item={item} selected={itemRef.item?.sku === item.sku} key={item.sku}/>
168
- ))}
169
- </ScrollArea>
170
- ) : (<>
171
- {items.map((item) => (
172
- <Item item={item} selected={itemRef.item?.sku === item.sku} key={item.sku}/>
173
- ))}
174
- </>)}
175
- </RadioGroup>
176
- ) : (
177
- <div className={cn(showQuantity ? 'flex items-center' : 'block', soleItemClx )}>
178
- <div className={showQuantity ? 'grow' : ''}>
179
- {labelAndPrice(items[0])}
180
- </div>
181
- {showQuantity && (
182
- <QuantityIndicator item={items[0]} clx='grow-0 shrink-0 font-semibold text-sm leading-none px-2' />
183
- )}
184
- </div>
185
- )
186
- })
187
-
188
- export default ButtonItemSelector
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
+ Image,
9
+ Label,
10
+ RadioGroup,
11
+ ScrollArea
12
+ } from '@hanzo/ui/primitives'
13
+ import { cn } from '@hanzo/ui/util'
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
+ const ImageRadioGroupItem = React.forwardRef<
24
+ React.ElementRef<typeof RadioGroupPrimitive.Item>,
25
+ Omit<React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>, 'value' | 'id'> & {
26
+ item: LineItem,
27
+ constrainTo: Dimensions
28
+ }
29
+ >(({
30
+ item,
31
+ constrainTo,
32
+ className,
33
+ ...props
34
+ }, ref) => {
35
+
36
+ const img = item.optionImg ? item.optionImg : item.img
37
+
38
+ return (
39
+ <RadioGroupPrimitive.Item
40
+ ref={ref}
41
+ data-vaul-no-drag
42
+ className={cn(
43
+ 'ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring',
44
+ 'focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
45
+ className,
46
+ 'overflow-hidden',
47
+ img?.rounded ? `rounded-${img.rounded}` : 'rounded-sm'
48
+ )}
49
+ {...props}
50
+ id={item.sku}
51
+ value={item.sku}
52
+ >
53
+ {img ? (
54
+ <Image def={img} constrainTo={constrainTo} preload className=''/>
55
+ ) : ( // placeholder so things align
56
+ <div style={{height: constrainTo.h, width: constrainTo.w}}/>
57
+ )}
58
+ </RadioGroupPrimitive.Item>
59
+ )
60
+ })
61
+ ImageRadioGroupItem.displayName = 'ImageRadioGroupItem'
62
+
63
+ const ButtonItemSelector: React.FC<ItemSelectorProps> = observer(({
64
+ items,
65
+ selectedItemRef: itemRef,
66
+ selectSku,
67
+ clx='',
68
+ itemClx='',
69
+ soleItemClx='',
70
+ scrollable=false,
71
+ mobile=false,
72
+ options={}
73
+ }) => {
74
+
75
+ const {
76
+ showPrice,
77
+ showQuantity,
78
+ showFamilyInOption,
79
+ buttonType,
80
+ horizButtons,
81
+ showButtonIfOnlyOne
82
+ } = accessItemOptions(options)
83
+
84
+ const showImage = buttonType !== 'text'
85
+ const showText = buttonType !== 'image'
86
+ const showBoth = buttonType === 'image-and-text'
87
+
88
+ const labelAndPrice = (item : LineItem) => (
89
+ (showFamilyInOption ? (item.familyTitle + ', ' + item.optionLabel) : item.optionLabel) +
90
+ (showPrice ? ((showFamilyInOption ? ': ' : ', ') + formatCurrencyValue(item.price)) : '')
91
+ )
92
+
93
+ const Item: React.FC<{
94
+ item: LineItem
95
+ selected: boolean
96
+ }> = observer(({
97
+ item,
98
+ selected
99
+ }) => {
100
+
101
+ const textClx = (selected) ? 'text-accent' : 'text-muted'
102
+ const cursorClx = (selected) ? 'hover:cursor-default' : 'hover:cursor-pointer'
103
+ const justifyClx = (showBoth) ? '' : 'justify-center'
104
+ // If no image, the Label must fill the entire button since it has to be
105
+ // clickable all the way to the border
106
+ const paddingClx = (showImage) ? (scrollable ? 'px-4 py-2' : 'px-2 py-2' ) : ''
107
+ let bgClx = ''
108
+ let borderClx = ''
109
+ if (scrollable) {
110
+ borderClx += (selected) ? 'border-foreground border ' : 'border-b border-muted-2 '
111
+ }
112
+ else if (!showImage) {
113
+ borderClx += 'border rounded-lg '
114
+ borderClx += (selected) ? 'border-foreground ' : 'border-muted-2 '
115
+ bgClx += 'hover:bg-level-2'
116
+ }
117
+
118
+ const outerClx = ['h-10 flex items-center', justifyClx, paddingClx, bgClx, borderClx]
119
+
120
+ return (
121
+ <div className={cn(...outerClx, itemClx )} data-vaul-no-drag >
122
+ <ImageRadioGroupItem
123
+ item={item}
124
+ constrainTo={DEFAULT_CONSTRAINT}
125
+ className={cn(
126
+ cursorClx,
127
+ (scrollable ? '' : 'border-transparent border-2 data-[state=checked]:border-foreground'),
128
+ showBoth ? 'mr-2' : (showImage ? '' : 'hidden')
129
+ )}
130
+ />
131
+ <Label htmlFor={item.sku} className={cn(
132
+ showQuantity ? 'flex items-center' : 'block',
133
+ textClx,
134
+ cursorClx,
135
+ (showImage ? '' : 'self-stretch w-full flex items-center justify-center px-3 py-2'),
136
+ (showText ? '' : 'hidden')
137
+ )}>
138
+ <div className={showQuantity ? 'grow' : ''}>{labelAndPrice(item)}</div>
139
+ {showQuantity && (
140
+ <QuantityIndicator
141
+ item={item}
142
+ clx='grow-0 shrink-0 h-[20px] ml-2'
143
+ iconClx={selected ? 'fill-foreground' : 'fill-muted'}
144
+ digitClx='font-semibold text-primary-fg leading-none font-sans text-xxs'
145
+ />
146
+ )}
147
+ </Label>
148
+ </div>
149
+ )
150
+ })
151
+
152
+ return showButtonIfOnlyOne || items.length > 1 ? (
153
+ <RadioGroup
154
+ className={cn(
155
+ (scrollable ? 'shrink min-h-0 gap-0' : (mobile ? 'gap-3' : 'gap-1')),
156
+ (horizButtons ? `grid grid-cols-${items.length} gap-1` : 'flex flex-col'),
157
+ (mobile && showText) ? 'min-w-pr-50' : '',
158
+ clx,
159
+ )}
160
+ onValueChange={selectSku}
161
+ value={itemRef.item ? itemRef.item.sku : ''}
162
+ data-vaul-no-drag
163
+ >
164
+ {scrollable ? (
165
+ <ScrollArea className='mt-2 w-full h-full py-0 border border-muted-2' data-vaul-no-drag>
166
+ {items.map((item) => (
167
+ <Item item={item} selected={itemRef.item?.sku === item.sku} key={item.sku}/>
168
+ ))}
169
+ </ScrollArea>
170
+ ) : (<>
171
+ {items.map((item) => (
172
+ <Item item={item} selected={itemRef.item?.sku === item.sku} key={item.sku}/>
173
+ ))}
174
+ </>)}
175
+ </RadioGroup>
176
+ ) : (
177
+ <div className={cn(showQuantity ? 'flex items-center' : 'block', soleItemClx )}>
178
+ <div className={showQuantity ? 'grow' : ''}>
179
+ {labelAndPrice(items[0])}
180
+ </div>
181
+ {showQuantity && (
182
+ <QuantityIndicator item={items[0]} clx='grow-0 shrink-0 font-semibold text-sm leading-none px-2' />
183
+ )}
184
+ </div>
185
+ )
186
+ })
187
+
188
+ export default ButtonItemSelector