@luxfi/core 5.0.9 → 5.1.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.
@@ -13,5 +13,6 @@ export const getBullionFamilies = (videoMap: Map<string, VideoDef>) => (
13
13
  )
14
14
  )
15
15
 
16
- export { default as serviceOptions } from '../../conf/lux-commerce-options'
16
+ export { default as serviceOptions } from './lux-service-options'
17
17
  export { default as bullionPrice1oz } from './bullion-price-1oz'
18
+
@@ -3,70 +3,29 @@ import React, {type PropsWithChildren } from 'react'
3
3
 
4
4
  import { X as LucideX} from 'lucide-react'
5
5
 
6
- import {
7
- Button,
8
- Drawer,
9
- DrawerContent,
10
- DrawerHandle,
11
- type DrawerProps,
12
- useDrawerContext
13
- } from '@hanzo/ui/primitives'
6
+ import { Button, Drawer, DrawerContent, type DrawerProps } from '@hanzo/ui/primitives'
14
7
  import { cn } from '@hanzo/ui/util'
15
8
 
16
- import '../../../style/drawer-handle-overrides.css'
17
-
18
9
  const CommerceDrawer: React.FC<PropsWithChildren &
19
10
  Omit<DrawerProps, 'onOpenChange'> &
20
11
  {
21
12
  setOpen: (b: boolean) => void
22
- handleHandleClicked: () => void
23
13
  drawerClx?: string
24
- setActiveSPIndexSetter?: (fn: (snapPoint: number | string | null) => void) => void
25
14
  }
26
15
  > = ({
27
16
  children,
28
17
  open,
29
18
  setOpen,
30
19
  modal,
31
- snapPoints,
32
- setActiveSnapPoint,
33
- activeSnapPoint,
34
- handleHandleClicked,
35
- setActiveSPIndexSetter,
36
20
  drawerClx='',
37
21
  ...rest
38
- }) => {
39
-
40
-
41
- return (
22
+ }) => (
42
23
  // @ts-ignore
43
- <Drawer
44
- open={open}
45
- onOpenChange={setOpen}
46
- modal={modal}
47
- snapPoints={snapPoints}
48
- setActiveSnapPoint={setActiveSnapPoint}
49
- activeSnapPoint={activeSnapPoint}
50
- fastDragSkipsToEnd={false}
51
- handleOnly={true}
52
- setActiveSPIndexSetter={setActiveSPIndexSetter}
53
-
54
-
55
- {...rest}
56
- >
57
- <DrawerContent defaultHandle={false} className={cn(
24
+ <Drawer open={open} onOpenChange={setOpen} modal={modal} {...rest}>
25
+ <DrawerContent modal={modal} className={cn(
58
26
  'rounded-t-xl mt-6 pt-6',
59
27
  drawerClx
60
28
  )}>
61
-
62
- <DrawerHandle
63
- className={
64
- 'absolute left-0 right-0 mx-auto top-2 ' +
65
- 'w-[100px] h-3 rounded-full bg-level-3 hover:bg-level-2 shrink-0'
66
- }
67
- handleClick={handleHandleClicked}
68
- />
69
-
70
29
  {children}
71
30
  <Button
72
31
  variant='ghost'
@@ -79,7 +38,6 @@ const CommerceDrawer: React.FC<PropsWithChildren &
79
38
  </DrawerContent>
80
39
  </Drawer>
81
40
  )
82
- }
83
41
 
84
42
 
85
43
  export default CommerceDrawer
@@ -1,242 +1,44 @@
1
1
  'use client'
2
- import React, { useEffect, useRef, useState } from 'react'
3
- import { usePathname, useRouter } from 'next/navigation'
4
- import { action, computed, makeObservable, observable, reaction, type IReactionDisposer } from 'mobx'
2
+ import React from 'react'
3
+ import { useRouter } from 'next/navigation'
5
4
  import { observer } from 'mobx-react-lite'
6
5
 
7
- import { CarouselBuyCard, useCommerce } from '@hanzo/commerce'
8
-
9
- import { useCommerceUI } from '../../../commerce/ui-context'
6
+ import { useCommerceUI, CarouselBuyCard } from '@hanzo/commerce'
10
7
 
11
8
  import CommerceDrawer from './drawer'
12
9
  import CheckoutButton from '../checkout-button'
13
10
 
14
- const BUY = '700px'
15
- const MICRO = '120px'
16
- const BOTH = [MICRO, BUY]
17
- const BUY_ONLY = [BUY]
18
- const MICRO_ONLY = [MICRO]
19
-
20
- type DrawerMode = 'checkout' | 'added' | 'buy' | 'buy-added' | 'buy-checkout' | 'none' | 'closed' // manually
21
- type DrawerState = 'micro' | 'buy' | 'closed'
22
-
23
- const MODE_TO_STATE = {
24
- checkout: 'micro',
25
- added: 'micro',
26
- buy: 'buy',
27
- 'buy-checkout': 'buy',
28
- 'buy-added': 'buy',
29
- none: 'closed',
30
- closed: 'closed'
31
- } satisfies Record<DrawerMode, DrawerState>
32
-
33
- const MODE_TO_POINTS = {
34
- checkout: MICRO_ONLY,
35
- added: BOTH,
36
- buy: BUY_ONLY,
37
- 'buy-checkout': BOTH,
38
- 'buy-added': BOTH,
39
- none: BOTH,
40
- closed: BOTH
41
- }
42
-
43
-
44
- class ObsDrawerState {
45
-
46
- _mode: DrawerMode = 'none'
47
-
48
- constructor() {
49
- makeObservable(this, {
50
- _mode: observable,
51
- setMode: action,
52
- mode: computed,
53
- state: computed,
54
- points: computed,
55
- modal: computed,
56
- activePoint: computed
57
- })
58
- }
59
-
60
- get mode(): DrawerMode {return this._mode}
61
- get state(): DrawerState { return MODE_TO_STATE[this._mode] }
62
- get points(): (number | string)[] { return MODE_TO_POINTS[this._mode] }
63
- get modal(): boolean { return this.state !== 'micro' }
64
- get activePoint(): number | string | null {
65
- if (this.state === 'buy') return BUY
66
- if (this.state === 'micro') return MICRO
67
- return null
68
- }
69
-
70
- setMode = (m: DrawerMode) => {this._mode = m}
71
- }
72
-
73
11
  const CommerceUIComponent: React.FC = observer(() => {
74
12
 
75
- const cmmc = useCommerce()
76
13
  const ui = useCommerceUI()
77
14
  const router = useRouter()
78
- const isCheckout = usePathname() === '/checkout'
79
-
80
- const stateRef = useRef<ObsDrawerState>(new ObsDrawerState())
81
- const reactionDisposers = useRef<IReactionDisposer[]>([])
82
-
83
- const [activeSnapPoint, setActiveSnapPoint] = useState<string | number | null>(null)
84
- const setterRef = useRef<((index: number ) => void) | undefined>(undefined)
85
-
86
- useEffect(() => {
87
-
88
- reactionDisposers.current.push(reaction(
89
-
90
- () => ({
91
- buy: !!ui.buyOptionsSkuPath,
92
- added: !isCheckout && ui.item,
93
- checkout: !isCheckout && !cmmc.cartEmpty,
94
- closed: ui.closed
95
- }),
96
- ({buy, added, checkout, closed}) => {
97
- let mode: DrawerMode = 'none' // TODO: 'closed'
98
- if (buy) {
99
- if (added) {
100
- mode = 'buy-added'
101
- }
102
- else if (checkout) {
103
- mode = 'buy-checkout'
104
- }
105
- else {
106
- mode = 'buy'
107
- }
108
- }
109
- else {
110
- if (closed) {
111
- mode = 'closed'
112
- }
113
- else if (added) {
114
- mode = 'added'
115
- }
116
- else if (checkout) {
117
- mode = 'checkout'
118
- }
119
- }
120
- stateRef.current.setMode(mode)
121
- },
122
- {equals: (val, prev) => (
123
- val.buy === prev.buy
124
- &&
125
- val.added === prev.added
126
- &&
127
- val.checkout === prev.checkout
128
- &&
129
- val.closed === prev.closed
130
- )}
131
- )),
132
- reactionDisposers.current.push(reaction(
133
- () => ( stateRef.current.state ),
134
- (s) => {
135
- if (s === 'buy') {
136
- //setterRef.current?.(stateRef.current.points.length - 1)
137
- setActiveSnapPoint(BUY)
138
- }
139
- else if (s === 'micro') {
140
- //setterRef.current?.(0)
141
- setActiveSnapPoint(MICRO)
142
- }
143
- }
144
- ))
145
- return () => {
146
- reactionDisposers.current?.forEach((d) => {d()})
147
- }
148
- }, [isCheckout])
149
-
150
- const _setActiveSnapPoint = (pt: string | number | null): void => {
151
- console.log("ON CHANGE: ", pt)
152
- setActiveSnapPoint(pt)
153
- }
154
15
 
155
16
  const handleCheckout = () => {
156
17
  router.push('/checkout')
157
18
  }
158
19
 
159
- const reallyOnlyCloseDrawer = (b: boolean) => {
160
20
  // Should only ever be called internally to close
161
- // Using handleCloseGesture()
162
- }
163
-
164
- const handleHandleClicked = () => {
165
- console.log("HANDLE CLICKED")
166
-
167
- if (stateRef.current.state === 'buy') {
168
- const toks = stateRef.current.mode.split('-')
169
- if (toks.length <= 1) {
170
- console.log("CLOSING 'BUY' ... ")
171
- ui.hideBuyOptions()
172
- }
173
- else {
174
- console.log("CLOSING 'BUY' to ", toks[1])
175
- ui.hideBuyOptions()
176
- }
177
- }
178
- else if (stateRef.current.state === 'micro') {
179
- if (stateRef.current.mode === 'checkout') {
180
- console.log(" CLOSING 'CHECKOUT' ... ")
181
- ui.setClosed(true)
182
- }
183
- else if (stateRef.current.mode === 'added') {
184
- console.log(" OPENING 'ADDED' ... ")
185
- ui.showBuyOptions(ui.item?.sku ?? '')
186
- }
187
- }
188
- }
189
-
190
- const handleCloseGesture = () => {
191
- if (stateRef.current.state === 'buy') {
192
- console.log(" CLOSING 'BUY' ... ")
193
- const toks = stateRef.current.mode.split('-')
194
- if (toks.length <= 1) {
195
- stateRef.current.setMode('none')
196
- }
197
- else {
198
- stateRef.current.setMode(toks[1] as DrawerMode) // 'checkout' or 'added'
199
- }
200
- return true // "handled!"
21
+ const reallyOnlyCloseDrawer = (b: boolean) => {
22
+ if (!b ) {
23
+ ui.hideBuyOptions()
201
24
  }
202
- console.log("DEFAULT CLOSE ACTION")
203
- return false
204
25
  }
205
26
 
206
-
207
- const setActiveSPIndexSetter = (fn: (index: number ) => void): void => {
208
- setterRef.current = fn
209
- }
210
-
211
-
212
27
  return (
213
28
  <CommerceDrawer
214
- open={!(stateRef.current.state === 'closed')}
29
+ open={!!ui.buyOptionsSkuPath}
215
30
  setOpen={reallyOnlyCloseDrawer}
216
- drawerClx={'w-full h-full'}
217
- snapPoints={stateRef.current.points}
218
- modal={stateRef.current.modal}
219
- activeSnapPoint={activeSnapPoint}
220
- setActiveSnapPoint={_setActiveSnapPoint}
221
- handleHandleClicked={handleHandleClicked}
222
- setActiveSPIndexSetter={setActiveSPIndexSetter}
223
- handleCloseGesture={handleCloseGesture}
31
+ drawerClx={'w-full md:max-w-[550px] md:mx-auto lg:max-w-[50vw]'}
224
32
  >
225
- {stateRef.current.state === 'buy' && (
226
- <CarouselBuyCard
227
- skuPath={ui.buyOptionsSkuPath!}
228
- checkoutButton={
229
- <CheckoutButton handleCheckout={handleCheckout} className='w-full min-w-[160px] sm:max-w-[320px]'/>
230
- }
231
- onQuantityChanged={ui.itemQuantityChanged}
232
- clx='w-full'
233
- addBtnClx='w-full min-w-[160px] sm:max-w-[320px]'
234
- selectorClx='max-w-[475px]'
235
- />
236
- )}
237
- {stateRef.current.state === 'micro' && (
238
- <p>Mode: {stateRef.current.mode}</p>
239
- )}
33
+ <CarouselBuyCard
34
+ skuPath={ui.buyOptionsSkuPath!}
35
+ checkoutButton={
36
+ <CheckoutButton handleCheckout={handleCheckout} className='w-full min-w-[160px] sm:max-w-[320px]'/>
37
+ }
38
+ clx='w-full'
39
+ addBtnClx='w-full min-w-[160px] sm:max-w-[320px]'
40
+ selectorClx='max-w-[475px]'
41
+ />
240
42
  </CommerceDrawer>
241
43
  )
242
44
  })
@@ -2,6 +2,7 @@
2
2
  import React, { useEffect, useRef } from 'react'
3
3
  import { observable, type IObservableValue, reaction } from 'mobx'
4
4
  import { observer } from 'mobx-react-lite'
5
+ import { type LucideProps } from 'lucide-react'
5
6
 
6
7
  import { Button, type ButtonProps } from '@hanzo/ui/primitives'
7
8
  import { cn } from '@hanzo/ui/util'
@@ -11,15 +12,11 @@ import * as Icons from '../icons'
11
12
 
12
13
  const IconAndQuantity: React.FC<{
13
14
  animateOnQuantityChange?: boolean
14
- showArrow?: boolean
15
- showQuantity?: boolean
16
15
  clx?: string
17
16
  iconClx?: string
18
17
  digitClx?: string
19
18
  }> = observer(({
20
- animateOnQuantityChange=false,
21
- showArrow=true,
22
- showQuantity=true,
19
+ animateOnQuantityChange=true,
23
20
  clx='',
24
21
  iconClx='',
25
22
  digitClx=''
@@ -50,7 +47,6 @@ const IconAndQuantity: React.FC<{
50
47
 
51
48
  return (
52
49
  <div className={cn('flex items-center justify-center', clx)}>
53
- {showQuantity && (
54
50
  <div className={cn(
55
51
  'relative flex items-center justify-center mr-1',
56
52
  ((wiggleRef.current.get() === 'more') ?
@@ -64,13 +60,12 @@ const IconAndQuantity: React.FC<{
64
60
  'absolute left-0 right-0 top-0 bottom-0',
65
61
  digitClx
66
62
  )}>
67
- <div style={{/* color: 'white' tailwind bug? ,*/ fontSize: '11px', position: 'relative', top: '1px' }}>{cmmc.cartQuantity}</div>
63
+ <div style={{color: 'white' /* tailwind bug? */, fontSize: '11px', position: 'relative', top: '1px' }}>{cmmc.cartQuantity}</div>
68
64
  </div>
69
65
  )}
70
66
  <Icons.bag width='19' height='24' className={cn('relative -top-[3px] opacity-70' , iconClx)} aria-hidden="true" />
71
67
  </div>
72
- )}
73
- {showArrow && (<span style={{fontSize: '17px',}}>&rsaquo;</span>)}
68
+ <span style={{fontSize: '17px',}}>&rsaquo;</span>
74
69
  </div>
75
70
  )
76
71
  })
@@ -78,7 +73,6 @@ const IconAndQuantity: React.FC<{
78
73
  const CheckoutButton: React.FC<ButtonProps & {
79
74
  handleCheckout: () => void
80
75
  showQuantity?: boolean
81
- showArrow?: boolean
82
76
  animateOnQuantityChange?: boolean
83
77
  centerText?: boolean
84
78
  }> = ({
@@ -87,10 +81,8 @@ const CheckoutButton: React.FC<ButtonProps & {
87
81
  rounded='lg',
88
82
  className,
89
83
  showQuantity=true,
90
- showArrow=true,
91
84
  animateOnQuantityChange=true,
92
85
  centerText=true,
93
- children,
94
86
  ...rest
95
87
  }) => {
96
88
 
@@ -101,27 +93,22 @@ const CheckoutButton: React.FC<ButtonProps & {
101
93
  variant={variant}
102
94
  rounded={rounded}
103
95
  className={cn(
104
- 'flex justify-between items-stretch group',
105
- showQuantity ? (centerText ? 'px-1.5' : 'pl-2.5 pr-1.5') : '',
106
96
  className,
97
+ 'flex justify-between items-stretch',
98
+ showQuantity ? (centerText ? 'px-1.5' : 'pl-2.5 pr-1.5') : ''
107
99
  )}
108
100
  >
109
- {centerText && ( // must scale this one too, as it effects layout
101
+ {showQuantity && centerText && (
102
+ <IconAndQuantity clx='invisible' />
103
+ )}
104
+ <div className='flex justify-center items-center'>Checkout</div>
105
+ {showQuantity && (
110
106
  <IconAndQuantity
111
- showArrow={showArrow}
112
- showQuantity={showQuantity}
113
- clx='invisible group-hover:scale-105 transition-scale transition-duration-300'
107
+ animateOnQuantityChange={animateOnQuantityChange}
108
+ iconClx='fill-fg-foreground'
109
+ digitClx='text-primary-fg leading-none font-bold font-sans'
114
110
  />
115
111
  )}
116
- {children ?? (<div className='flex justify-center items-center'>Checkout</div>)}
117
- <IconAndQuantity
118
- clx='group-hover:scale-105 transition-scale transition-duration-300'
119
- animateOnQuantityChange={animateOnQuantityChange}
120
- showArrow={showArrow}
121
- showQuantity={showQuantity}
122
- iconClx='fill-background'
123
- digitClx='text-foreground group-hover:opacity-80 leading-none font-bold font-sans'
124
- />
125
112
  </Button>
126
113
  )
127
114
  }
@@ -1,104 +1,18 @@
1
1
  'use client'
2
- import React, { useRef } from 'react'
2
+ import React from 'react'
3
3
  import { createPortal } from 'react-dom'
4
4
  import { usePathname, useRouter } from 'next/navigation'
5
5
  import { observer } from 'mobx-react-lite'
6
6
 
7
7
  import { cn } from '@hanzo/ui/util'
8
- import { useStepAnimation } from '@hanzo/ui/util-client'
9
-
10
8
  import { Image } from '@hanzo/ui/primitives'
11
9
 
12
- import { useCommerceUI } from '../../../commerce/ui-context'
10
+ import { useCommerceUI } from '@hanzo/commerce'
13
11
 
14
12
  import CheckoutButton from '../checkout-button'
15
13
  import useAnimationClxSet from './use-anim-clx-set'
14
+ import useLaggingItemRef from './use-lagging-item-ref'
16
15
  import CONST from './const'
17
- import type { LineItem } from '@hanzo/commerce/types'
18
-
19
- const transStyle = (t: { transition: string, from : string, to: string } | undefined) : any => (
20
- t ? {
21
- transitionProperty: t.transition,
22
- transitionTimingFunction: CONST.animTimingFn,
23
- transitionDuration: `${CONST.animDurationMs}ms`
24
- } : {}
25
- )
26
-
27
- const transClx = (on: boolean, t: { transition: string, from : string, to: string } | undefined) : string => (
28
- on ? (t?.from ?? '') : (t?.to ?? '')
29
- )
30
-
31
- const VARS: any = {
32
- BR: {
33
- pos: 'bottom-[24px] right-[66px]',
34
- width: 'w-initial',
35
- centerText: false,
36
- coClx: 'w-auto',
37
- infoClx: 'w-auto',
38
- activeItemAnim: {
39
- co: {
40
- transition: 'none',
41
- from : 'px-3 gap-2.5',
42
- to: ''
43
- },
44
- coText: {
45
- transition: 'max-width',
46
- from : 'max-w-[100px]',
47
- to: 'max-w-[0px]'
48
- },
49
- info: {
50
- transition: 'transform, opacity',
51
- from : 'scale-x-100 opacity-100 origin-right',
52
- to: 'scale-x-0 opacity-0 origin-right'
53
- }
54
- },
55
- showArrow: true
56
- },
57
- TR: {
58
- pos: 'top-[48px] md:top-[80px] right-[28px]',
59
- width: 'w-initial',
60
- centerText: false,
61
- showQuantity: false,
62
- showArrow: true,
63
- coClx: 'w-auto px-3 gap-1',
64
- infoClx: 'w-auto',
65
- activeItemAnim: {
66
- co: {
67
- transition: 'none',
68
- from : 'px-3 gap-2.5',
69
- to: ''
70
- },
71
- coText: {
72
- transition: 'max-width',
73
- from : 'max-w-[100px]',
74
- to: 'max-w-[0px]'
75
- },
76
- info: {
77
- transition: 'transform',
78
- from : 'scale-x-100 origin-right',
79
- to: 'scale-x-0 origin-right'
80
- }
81
- },
82
- },
83
- TRIO: {
84
- pos: 'top-[48px] md:top-[70px] right-[28px]',
85
- centerText: false,
86
- showQuantity: true,
87
- showArrow: true,
88
- width: 'w-initial',
89
- coClx: 'hidden',
90
- infoClx: 'w-auto',
91
- activeItemAnim: {
92
- info: {
93
- transition: 'transform, opacity',
94
- from : 'scale-x-100 opacity-100',
95
- to: 'scale-x-50 opacity-0'
96
- }
97
- },
98
- }
99
- }
100
-
101
- const v = 'TR'
102
16
 
103
17
  const CheckoutWidget: React.FC<{
104
18
  clx?: string
@@ -112,78 +26,58 @@ const CheckoutWidget: React.FC<{
112
26
  const clxSet = useAnimationClxSet(isCheckout)
113
27
 
114
28
  const itemRef = useCommerceUI()
115
-
116
- // for rendering content after itemRef.item() would return false
117
- const persistentRef = useRef<LineItem | undefined>(undefined)
118
-
119
- // Doing double duty of being initial step fn for StepAnimation,
120
- // and also capturing the item for persistentRef :)
121
- const initialStepFn = (): boolean => {
122
- if (!!itemRef.item && !persistentRef.current) {
123
- persistentRef.current = itemRef.item
124
- }
125
- return !!itemRef.item
126
- }
127
- const steps = useStepAnimation(initialStepFn, [CONST.animDurationMs, CONST.animDurationMs, CONST.animDurationMs])
29
+ const laggingRef = useLaggingItemRef(itemRef, CONST.animDurationMs)
128
30
 
129
31
  const handleCheckout = () => { router.push('/checkout')}
130
32
 
131
33
  return globalThis?.document?.body && createPortal(
132
34
  (<div
133
35
  className={cn(
134
- VARS[v].width,
135
- 'z-below-modal-2 fixed ',
136
- VARS[v].pos,
137
- 'rounded-lg',
36
+ 'min-w-[160px] sm:max-w-[320px] w-[calc(100%-72px)] ml-2 !h-10',
37
+ 'z-below-modal-2 fixed bottom-[20px] left-0 right-0',
38
+ 'rounded-lg bg-background',
138
39
  'flex',
139
- steps.notPast(0) ? 'bg-background' : '',
140
- steps.notPast(1) ? 'gap-2' : '',
40
+ itemRef.item ? 'gap-2' : '',
141
41
  clxSet.asArray.join(' ')
142
42
  )}
143
- style={steps.notPast(1) ? {} : VARS[v].coClx?.includes('hidden') ? {} : CONST.shadowStyle}
43
+ style={laggingRef.item ? {} : CONST.shadowStyle}
144
44
  >
145
45
  <div
146
46
  className={cn(
147
47
  'flex flex-row justify-between items-center',
148
- transClx(steps.notPast(0), VARS[v].activeItemAnim.info),
149
- VARS[v].itemClx,
150
- steps.notPast(1) ? 'px-3 border rounded-lg bg-level-1 border-muted-3' : ''
48
+ itemRef.item ? CONST.compWidthClx.itemInfo : 'w-0',
49
+ laggingRef.item ? 'px-3 border rounded-lg border-muted-3' : ''
151
50
  )}
152
- style={transStyle(VARS[v].activeItemAnim.info)}
51
+ style={{
52
+ transitionProperty: 'width',
53
+ transitionTimingFunction: CONST.animTimingFn,
54
+ transitionDuration: `${CONST.animDurationMs}ms`
55
+ }}
153
56
  >
154
- {steps.notPast(1) && persistentRef.current?.img && (
155
- <Image def={persistentRef.current.img} constrainTo={CONST.itemImgConstraint} preload className='grow-0 shrink-0'/>
156
- )}
157
- {steps.notPast(1) && persistentRef.current && (<div className='text-foreground grow ml-1'>
158
- <p className='whitespace-nowrap text-ellipsis text-sm'>{persistentRef.current.title}</p>
159
- <p className='whitespace-nowrap text-clip text-xxs' >recently added...</p>
160
- </div>)}
57
+ {laggingRef.item?.img ? (
58
+ <Image def={laggingRef.item.img} constrainTo={CONST.itemImgConstraint} preload className='grow-0 shrink-0'/>
59
+ ) : ( // placeholder so things align
60
+ <div style={{height: CONST.itemImgConstraint.h, width: CONST.itemImgConstraint.w}} className='bg-level-3 grow-0 shrink-0'/>
61
+ )}
62
+
63
+ <div className='text-muted grow ml-1'>
64
+ {laggingRef.item && (<>
65
+ <p className='whitespace-nowrap text-sm'>{laggingRef.item.title}</p>
66
+ <p className='whitespace-nowrap text-xxs' >recently added...</p>
67
+ </>)}
68
+ </div>
161
69
  </div>
162
70
  <CheckoutButton
163
71
  handleCheckout={handleCheckout}
164
- centerText={VARS[v].centerText ?? !steps.notPast(0)}
165
- variant='primary'
166
- rounded='lg'
167
- showQuantity={VARS[v].showQuantity ?? true}
168
- showArrow={VARS[v].showArrow ?? true}
169
- className={cn(
170
- // for setting and unsetting 'gap'
171
- transClx((VARS[v].activeItemAnim.coText ? steps.notPast(3) : true), VARS[v].activeItemAnim.co),
172
- VARS[v].coClx
173
- )}
174
- style={transStyle(VARS[v].activeItemAnim.co)}
175
- >
176
- <div
177
- className={cn(
178
- 'overflow-hidden',
179
- 'flex justify-center items-center',
180
- transClx(steps.notPast(2), VARS[v].activeItemAnim.coText),
181
- )}
182
- style={transStyle(VARS[v].activeItemAnim.coText)}
183
- >
184
- Checkout
185
- </div>
186
- </CheckoutButton>
72
+ centerText={!!!itemRef.item}
73
+ variant='primary' rounded='lg'
74
+ className={cn(itemRef.item ? CONST.compWidthClx.checkout : 'w-full')}
75
+ style={{
76
+ transitionProperty: 'width',
77
+ transitionTimingFunction: CONST.animTimingFn,
78
+ transitionDuration: `${CONST.animDurationMs}ms`
79
+ }}
80
+ />
187
81
  </div>),
188
82
  globalThis?.document?.body
189
83
  )
@@ -1,10 +1,8 @@
1
1
  import { useEffect, useRef } from 'react'
2
2
  import { reaction, runInAction} from 'mobx'
3
3
 
4
- import { useCommerce } from '@hanzo/commerce'
5
-
6
4
  import ObsStringSet from './obs-string-set'
7
- import { useCommerceUI } from '../../../commerce/ui-context'
5
+ import { useCommerce, useCommerceUI } from '@hanzo/commerce'
8
6
 
9
7
  export default (isCheckout: boolean): ObsStringSet => {
10
8
 
@@ -0,0 +1,30 @@
1
+ import { useEffect, useRef } from 'react'
2
+ import { reaction } from 'mobx'
3
+
4
+ import type { LineItem, ObsLineItemRef } from "@hanzo/commerce/types"
5
+ import { LineItemRef } from '@hanzo/commerce'
6
+
7
+ export default (orig: ObsLineItemRef, lagMs: number): ObsLineItemRef => {
8
+
9
+ // a ref that is synced to 'orig', but persists for lagMs longer
10
+ // so ui does not jump while animating out.
11
+ // (Fascilitates for start and end states in animation)
12
+ const laggingRef = useRef<LineItemRef>(new LineItemRef())
13
+
14
+ useEffect(() => (
15
+ reaction(
16
+ () => (orig.item),
17
+ (item: LineItem | undefined) => {
18
+ if (item) {
19
+ laggingRef.current.set(item)
20
+ }
21
+ else {
22
+ setTimeout(() => { laggingRef.current.set(undefined) }, lagMs)
23
+ }
24
+ },
25
+ {equals: (val, prev) => (val?.sku === prev?.sku)}
26
+ )
27
+ ), [])
28
+
29
+ return laggingRef.current
30
+ }
@@ -29,7 +29,7 @@ const DesktopHeader: React.FC<{
29
29
  {/* md or larger */}
30
30
  <div className={
31
31
  'flex flex-row h-[80px] items-center justify-between ' +
32
- 'px-[32px] w-full 2xl:mx-auto max-w-screen-2xl'
32
+ 'px-[8px] w-full mx-auto max-w-screen'
33
33
  }>
34
34
  <Logo size='md' href='/' className='hidden lg:flex' key='two' layout='text-only'/>
35
35
  <Logo size='sm' href='/' className='hidden md:flex lg:hidden' key='one' layout='text-only'/>
@@ -10,12 +10,18 @@ export { default as MiniChart } from './mini-chart'
10
10
  export { default as NotFound } from './not-found'
11
11
 
12
12
  export { default as AuthListener } from './auth/auth-listener'
13
- export { default as AddWidget } from './commerce/add-widget'
14
13
  export { default as BuyDrawer } from './commerce/buy-drawer'
15
- export { default as BuyButton } from './commerce/buy-button'
16
14
  export { default as CheckoutButton } from './commerce/checkout-button'
17
15
  export { default as CheckoutPanel } from './commerce/checkout-panel'
18
16
  export { default as CheckoutWidget } from './commerce/checkout-widget'
19
17
  export { default as LoginPanel } from './auth/login-panel'
20
18
  export { default as Scripts } from './scripts'
21
19
 
20
+
21
+ /* PLEASE KEEP
22
+ export {
23
+ default as HeadMetadata,
24
+ getTitleFromTemplateString,
25
+ TwitterComponent
26
+ } from './head-metadata'
27
+ */
package/conf/index.ts CHANGED
@@ -1,7 +1,5 @@
1
1
  import type { SelectionUISpecifier } from '@hanzo/commerce/types'
2
2
 
3
- export { default as commerceServiceOptions } from './lux-commerce-options'
4
-
5
3
  export const selectionUISpecifiers = {
6
4
 
7
5
  'LXM-CN': {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luxfi/core",
3
- "version": "5.0.9",
3
+ "version": "5.1.1",
4
4
  "description": "Library that contains shared UI primitives, support for a common design system, and other boilerplate support.",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/",
@@ -28,7 +28,6 @@
28
28
  "exports": {
29
29
  ".": "./components/index.ts",
30
30
  "./commerce": "./commerce/index.ts",
31
- "./commerce-data": "./commerce/data/index.ts",
32
31
  "./root-layout": "./root-layout/index.tsx",
33
32
  "./server-actions": "./server-actions/index.ts",
34
33
  "./next": "./next/index.ts",
@@ -39,9 +38,10 @@
39
38
  },
40
39
  "dependencies": {
41
40
  "@hanzo/auth": "2.4.6",
42
- "@hanzo/commerce": "7.0.4",
43
- "@hanzo/ui": "3.7.23",
41
+ "@hanzo/commerce": "7.0.2",
42
+ "@hanzo/ui": "3.7.0",
44
43
  "@next/third-parties": "^14.1.0",
44
+ "@types/node": "^20.12.12",
45
45
  "cookies-next": "^4.1.1",
46
46
  "date-fns": "^3.6.0",
47
47
  "embla-carousel-autoplay": "^8.0.1",
@@ -51,15 +51,13 @@
51
51
  "peerDependencies": {
52
52
  "@hookform/resolvers": "^3.3.2",
53
53
  "lucide-react": "^0.344.0",
54
- "mobx": "^6.12.0",
55
- "mobx-react-lite": "^4.0.5",
56
54
  "next": "14.1.3",
57
55
  "next-themes": "^0.2.1",
58
- "react": "^18.3.1",
59
- "react-dom": "^18.3.1",
60
- "react-hook-form": "^7.51.4",
56
+ "react": "^18.2.0",
57
+ "react-dom": "^18.2.0",
58
+ "react-hook-form": "^7.44.2",
61
59
  "validator": "^13.11.0",
62
- "zod": "3.23.8"
60
+ "zod": "3.21.4"
63
61
  },
64
62
  "devDependencies": {
65
63
  "@mdx-js/loader": "^3.0.0",
@@ -67,8 +65,8 @@
67
65
  "@types/facebook-pixel": "^0.0.30",
68
66
  "@types/gtag.js": "^0.0.19",
69
67
  "@types/mdx": "^2.0.9",
70
- "@types/react": "^18.3.2",
71
- "@types/react-dom": "^18.3.0",
68
+ "@types/react": "^18.2.64",
69
+ "@types/react-dom": "^18.2.18",
72
70
  "tailwindcss": "^3.4.2",
73
71
  "typescript": "5.3.3"
74
72
  }
@@ -10,10 +10,9 @@ import { CommerceProvider } from '@hanzo/commerce'
10
10
  import getAppRouterBodyFontClasses from '../next/font/get-app-router-font-classes'
11
11
  import { FacebookPixelHead } from '../next/analytics/pixel-analytics'
12
12
 
13
- import { CommerceUIProvider } from '../commerce/ui-context'
14
13
  import { AuthListener, ChatWidget, Header, Scripts } from '../components'
15
-
16
14
  import BuyDrawer from '../components/commerce/buy-drawer'
15
+ import CheckoutWidget from '../components/commerce/checkout-widget'
17
16
 
18
17
  import { selectionUISpecifiers } from '../conf'
19
18
  import type SiteDef from '../types/site-def'
@@ -100,10 +99,9 @@ const RootLayout: React.FC<PropsWithChildren & {
100
99
  options={siteDef.commerce!.options}
101
100
  uiSpecs={selectionUISpecifiers}
102
101
  >
103
- <CommerceUIProvider >
104
- <Guts />
105
- <BuyDrawer />
106
- </CommerceUIProvider>
102
+ <Guts />
103
+ <BuyDrawer />
104
+ <CheckoutWidget />
107
105
  </CommerceProvider>
108
106
  ) : (
109
107
  <Guts />
package/tsconfig.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
- "extends": "../tsconfig.modules.base.json",
2
+ "extends": "../tsconfig.lux.base.json",
3
3
  "include": [
4
4
  "**/*.ts",
5
- "**/*.tsx"
5
+ "**/*.tsx",
6
6
  ],
7
7
  "exclude": [
8
- "node_modules"
9
- ]
8
+ "node_modules",
9
+ ],
10
10
  }
@@ -1,118 +0,0 @@
1
- import {
2
- action,
3
- computed,
4
- makeObservable,
5
- observable,
6
- } from 'mobx'
7
- import type { CommerceService, LineItem, ObsLineItemRef } from '@hanzo/commerce/types'
8
-
9
-
10
- interface CommerceUI extends ObsLineItemRef {
11
- showBuyOptions: (skuPath: string) => void
12
- hideBuyOptions: () => void
13
- get buyOptionsSkuPath(): string | undefined
14
-
15
- itemQuantityChanged(sku: string, val: number, prevVal: number): void
16
-
17
- get closed(): boolean
18
- setClosed(b: boolean): void
19
-
20
- }
21
-
22
- class CommerceUIStore implements CommerceUI {
23
-
24
- static readonly TIMEOUT = 1500
25
- _buyOptionsSkuPath: string | undefined = undefined
26
- _closed: boolean = false
27
- _paused: boolean = false
28
- _activeItem: LineItem | undefined = undefined
29
- _lastActivity: number | undefined = undefined
30
- _service: CommerceService
31
-
32
- constructor(s: CommerceService) {
33
- this._service = s
34
- makeObservable(this, {
35
- _buyOptionsSkuPath: observable,
36
- _activeItem: observable.shallow,
37
- _closed: observable,
38
- showBuyOptions: action,
39
- hideBuyOptions: action,
40
- buyOptionsSkuPath: computed,
41
- itemQuantityChanged: action,
42
- setClosed: action,
43
- closed: computed,
44
- tick: action,
45
- item: computed
46
- })
47
- }
48
-
49
- showBuyOptions = (skuPath: string): void => {
50
- this._service.setCurrentItem(undefined)
51
- this._buyOptionsSkuPath = skuPath
52
- this._paused = true
53
- this._closed = false
54
- }
55
-
56
- hideBuyOptions = (): void => {
57
- this._buyOptionsSkuPath = undefined
58
- this._paused = false
59
- if (this._lastActivity) {
60
- this._lastActivity = Date.now()
61
- }
62
- }
63
-
64
- get buyOptionsSkuPath(): string | undefined {
65
- return this._buyOptionsSkuPath
66
- }
67
-
68
- tick = () => {
69
- if (
70
- !this._paused
71
- &&
72
- this._lastActivity
73
- &&
74
- (Date.now() - this._lastActivity >= CommerceUIStore.TIMEOUT)
75
- ) {
76
- this._activeItem = undefined
77
- this._lastActivity = undefined
78
- }
79
- }
80
-
81
- itemQuantityChanged = (sku: string, val: number, oldVal: number): void => {
82
-
83
- if (val === 0) {
84
- if (this._activeItem?.sku === sku) {
85
- this._activeItem = undefined
86
- this._lastActivity = undefined
87
- }
88
- // otherwise ignore
89
- }
90
- else if (val < oldVal) {
91
- if (this._activeItem?.sku === sku) {
92
- this._lastActivity = Date.now()
93
- }
94
- // otherwise ignore
95
- }
96
- else {
97
- this._activeItem = this._service.getItemBySku(sku)
98
- this._lastActivity = Date.now()
99
- }
100
- }
101
-
102
- get item(): LineItem | undefined {
103
- return this._activeItem
104
- }
105
-
106
- get closed(): boolean {
107
- return this._closed
108
- }
109
-
110
- setClosed = (b: boolean): void => { this._closed = b}
111
-
112
- dispose = () => {}
113
- }
114
-
115
- export {
116
- CommerceUIStore,
117
- type CommerceUI
118
- }
@@ -1,50 +0,0 @@
1
- 'use client'
2
- import React, {
3
- createContext,
4
- useContext,
5
- useRef,
6
- type PropsWithChildren,
7
- useEffect
8
- } from 'react'
9
-
10
- // https://dev.to/ivandotv/mobx-server-side-rendering-with-next-js-4m18
11
- import { enableStaticRendering } from 'mobx-react-lite'
12
- enableStaticRendering(typeof window === "undefined")
13
-
14
- import { type CommerceUI, CommerceUIStore } from './commerce-ui'
15
- import { useCommerce } from '@hanzo/commerce'
16
-
17
- const CommerceUIContext = createContext<CommerceUIStore | undefined>(undefined)
18
-
19
- const useCommerceUI = (): CommerceUI => {
20
- return useContext(CommerceUIContext) as CommerceUIStore
21
- }
22
-
23
- const CommerceUIProvider: React.FC<PropsWithChildren & {
24
- DEBUG_NO_TICK?: boolean
25
- }> = ({
26
- children,
27
- DEBUG_NO_TICK=false
28
- }) => {
29
-
30
- const cmmc = useCommerce()
31
- const valueRef = useRef<CommerceUIStore>(new CommerceUIStore(cmmc))
32
-
33
- useEffect(() => {
34
-
35
- //valueRef.current = new CommerceUIStore(cmmc)
36
- return () => { valueRef.current?.dispose() }
37
- }, [])
38
-
39
- return (
40
- <CommerceUIContext.Provider value={valueRef.current}>
41
- {children}
42
- </CommerceUIContext.Provider>
43
- )
44
- }
45
-
46
- export {
47
- useCommerceUI,
48
- CommerceUIProvider
49
- }
50
-
@@ -1,20 +0,0 @@
1
- 'use client'
2
- import React from 'react'
3
-
4
- import type { LineItem } from '@hanzo/commerce/types'
5
- import { AddToCartWidget } from '@hanzo/commerce'
6
-
7
- import { useCommerceUI } from '../../commerce/ui-context'
8
-
9
- const AddWidget: React.FC<{
10
- item: LineItem
11
- disabled?: boolean
12
- className?: string
13
- buttonClx?: string
14
- variant?: 'minimal' | 'primary' | 'outline'
15
- }> = (props) => {
16
- const ui = useCommerceUI()
17
- return <AddToCartWidget {...props} onQuantityChanged={ui.itemQuantityChanged}/>
18
- }
19
-
20
- export default AddWidget
@@ -1,34 +0,0 @@
1
- 'use client'
2
- import React, {type PropsWithChildren} from 'react'
3
-
4
- import { Button, buttonVariants } from '@hanzo/ui/primitives'
5
- import { type VariantProps } from '@hanzo/ui/util'
6
-
7
- import { cn } from '@hanzo/ui/util'
8
- import { useCommerceUI } from '../../commerce/ui-context'
9
-
10
- const BuyButton: React.FC<
11
- PropsWithChildren &
12
- VariantProps<typeof buttonVariants> &
13
- {
14
- skuPath: string
15
- className?: string
16
- }
17
- > = ({
18
- skuPath,
19
- children,
20
- className='',
21
- ...rest
22
- }) => {
23
-
24
- const ui = useCommerceUI()
25
- const handleClick = () => { ui.showBuyOptions(skuPath) }
26
-
27
- return (
28
- <Button onClick={handleClick} {...rest} className={cn(className, '')}>
29
- {children}
30
- </Button>
31
- )
32
- }
33
-
34
- export default BuyButton
@@ -1,154 +0,0 @@
1
- [vaul-drawer] {
2
- touch-action: none;
3
- transition: transform 0.5s cubic-bezier(0.32, 0.72, 0, 1);
4
- }
5
-
6
- [vaul-drawer][vaul-drawer-direction='bottom'] {
7
- transform: translate3d(0, 100%, 0);
8
- }
9
-
10
- [vaul-drawer][vaul-drawer-direction='top'] {
11
- transform: translate3d(0, -100%, 0);
12
- }
13
-
14
- [vaul-drawer][vaul-drawer-direction='left'] {
15
- transform: translate3d(-100%, 0, 0);
16
- }
17
-
18
- [vaul-drawer][vaul-drawer-direction='right'] {
19
- transform: translate3d(100%, 0, 0);
20
- }
21
-
22
- .vaul-dragging .vaul-scrollable [vault-drawer-direction='top'] {
23
- overflow-y: hidden !important;
24
- }
25
- .vaul-dragging .vaul-scrollable [vault-drawer-direction='bottom'] {
26
- overflow-y: hidden !important;
27
- }
28
-
29
- .vaul-dragging .vaul-scrollable [vault-drawer-direction='left'] {
30
- overflow-x: hidden !important;
31
- }
32
-
33
- .vaul-dragging .vaul-scrollable [vault-drawer-direction='right'] {
34
- overflow-x: hidden !important;
35
- }
36
-
37
- [vaul-drawer][vaul-drawer-visible='true'][vaul-drawer-direction='top'] {
38
- transform: translate3d(0, var(--snap-point-height, 0), 0);
39
- }
40
-
41
- [vaul-drawer][vaul-drawer-visible='true'][vaul-drawer-direction='bottom'] {
42
- transform: translate3d(0, var(--snap-point-height, 0), 0);
43
- }
44
-
45
- [vaul-drawer][vaul-drawer-visible='true'][vaul-drawer-direction='left'] {
46
- transform: translate3d(var(--snap-point-height, 0), 0, 0);
47
- }
48
-
49
- [vaul-drawer][vaul-drawer-visible='true'][vaul-drawer-direction='right'] {
50
- transform: translate3d(var(--snap-point-height, 0), 0, 0);
51
- }
52
-
53
- [vaul-overlay] {
54
- opacity: 0;
55
- transition: opacity 0.5s cubic-bezier(0.32, 0.72, 0, 1);
56
- }
57
-
58
- [vaul-overlay][vaul-drawer-visible='true'] {
59
- opacity: 1;
60
- }
61
-
62
- [vaul-drawer]::after {
63
- content: '';
64
- position: absolute;
65
- background: inherit;
66
- background-color: inherit;
67
- }
68
-
69
- [vaul-drawer][vaul-drawer-direction='top']::after {
70
- top: initial;
71
- bottom: 100%;
72
- left: 0;
73
- right: 0;
74
- height: 200%;
75
- }
76
-
77
- [vaul-drawer][vaul-drawer-direction='bottom']::after {
78
- top: 100%;
79
- bottom: initial;
80
- left: 0;
81
- right: 0;
82
- height: 200%;
83
- }
84
-
85
- [vaul-drawer][vaul-drawer-direction='left']::after {
86
- left: initial;
87
- right: 100%;
88
- top: 0;
89
- bottom: 0;
90
- width: 200%;
91
- }
92
-
93
- [vaul-drawer][vaul-drawer-direction='right']::after {
94
- left: 100%;
95
- right: initial;
96
- top: 0;
97
- bottom: 0;
98
- width: 200%;
99
- }
100
-
101
- [vaul-handle] {
102
- /* opacity: 0.8; */
103
- touch-action: pan-y;
104
- cursor: grab;
105
- }
106
-
107
- /* [vaul-handle]:hover, */
108
- [vaul-handle]:active {
109
- opacity: 1;
110
- }
111
-
112
- [vaul-handle]:active {
113
- cursor: grabbing;
114
- }
115
-
116
- [vaul-handle-hitarea] {
117
- position: absolute;
118
- left: 50%;
119
- top: 50%;
120
- transform: translate(-50%, -50%);
121
- width: max(100%, 2.75rem); /* 44px */
122
- height: max(100%, 2.75rem); /* 44px */
123
- touch-action: inherit;
124
- }
125
-
126
- [vaul-overlay][vaul-snap-points='true']:not([vaul-snap-points-overlay='true']):not([data-state='closed']) {
127
- opacity: 0;
128
- }
129
-
130
- [vaul-overlay][vaul-snap-points-overlay='true']:not([vaul-drawer-visible='false']) {
131
- opacity: 1;
132
- }
133
-
134
- /* This will allow us to not animate via animation, but still benefit from delaying unmount via Radix. */
135
- @keyframes fake-animation {
136
- from {
137
- }
138
- to {
139
- }
140
- }
141
-
142
- @media (pointer: fine) {
143
- [vaul-handle-hitarea] {
144
- width: 100%;
145
- height: 100%;
146
- }
147
- }
148
-
149
- @media (hover: hover) and (pointer: fine) {
150
- [vaul-drawer] {
151
- user-select: none;
152
- }
153
- }
154
-