@luxfi/core 4.5.3 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -47,7 +47,7 @@ const ChatWidget: React.FC<{
47
47
  return (<>
48
48
  <div className={
49
49
  'fixed bottom-0 sm:bottom-16 right-0 w-full h-full ' +
50
- 'sm:max-w-[400px] sm:max-h-[550px] sm:px-4 z-above-floating ' +
50
+ 'sm:max-w-[400px] sm:max-h-[550px] sm:px-4 z-floating ' +
51
51
  (showChatbot ? 'flex' : 'hidden')
52
52
  }>
53
53
  <Card className='flex flex-col h-full w-full'>
@@ -66,7 +66,8 @@ const ChatWidget: React.FC<{
66
66
  height={28}
67
67
  onClick={onClick}
68
68
  className={cn(
69
- 'fixed bottom-5 right-5 z-floating transition-all cursor-pointer hover:drop-shadow-[0_2px_6px_rgba(255,255,255,1)]',
69
+ // z-index should be below anything in commerce-iu (buy drawer and checkout widget)
70
+ 'fixed bottom-5 right-5 z-below-modal-3 transition-all cursor-pointer hover:drop-shadow-[0_2px_6px_rgba(255,255,255,1)]',
70
71
  showChatbot ? 'rotate-180' : ''
71
72
  )}
72
73
  strokeWidth={1}
@@ -1,23 +1,26 @@
1
1
  'use client'
2
- import React from 'react'
2
+ import React, { useEffect, useRef } from 'react'
3
+ import { observable, type IObservableValue, reaction } from 'mobx'
3
4
  import { observer } from 'mobx-react-lite'
4
5
 
5
- import { buttonVariants, type ButtonSizes } from '@hanzo/ui/primitives'
6
- import { cn } from '@hanzo/ui/util'
6
+ import { buttonVariants } from '@hanzo/ui/primitives'
7
+ import { cn, type VariantProps } from '@hanzo/ui/util'
7
8
  import { useCommerce } from '@hanzo/commerce'
8
9
 
9
10
  import * as Icons from '../icons'
10
11
 
11
12
  const BagButton: React.FC<{
12
13
  showIfEmpty?: boolean
13
- noHoverEffects?: boolean
14
- size?: ButtonSizes
14
+ animateOnHover?: boolean
15
+ animateOnQuantityChange?: boolean
16
+ size?: VariantProps<typeof buttonVariants>['size']
15
17
  className?: string
16
18
  iconClx?: string
17
19
  onClick?: () => void
18
20
  }> = observer(({
19
21
  showIfEmpty=false,
20
- noHoverEffects=false,
22
+ animateOnHover=true,
23
+ animateOnQuantityChange=true,
21
24
  size='default',
22
25
  className='',
23
26
  iconClx='',
@@ -25,12 +28,34 @@ const BagButton: React.FC<{
25
28
  }) => {
26
29
 
27
30
  const c = useCommerce()
31
+ const wiggleRef = useRef<IObservableValue<'more' | 'less' | 'none'>>(observable.box('none'))
32
+
33
+ useEffect(() => (
34
+ // return IReactionDisposer
35
+ animateOnQuantityChange ? reaction(
36
+ () => (c.cartQuantity),
37
+ (curr, prev) => {
38
+ if (curr > prev) {
39
+ wiggleRef.current.set('more')
40
+ }
41
+ else {
42
+ wiggleRef.current.set('less')
43
+ }
44
+ setTimeout(() => {
45
+ // Note that this doesn't actually stop the animation
46
+ // just resets the styles
47
+ wiggleRef.current.set('none')
48
+ }, 800)
49
+ }
50
+ ) : undefined
51
+ ), [])
28
52
 
29
53
  // undefined means context is not installed, ie commerce functions are not in use
30
54
  if (!c || (!showIfEmpty && c.cartEmpty)) {
31
55
  return <div /> // trigger code needs non-null
32
56
  }
33
57
 
58
+
34
59
  return (
35
60
  <div
36
61
  aria-label="Bag"
@@ -40,6 +65,10 @@ const BagButton: React.FC<{
40
65
  buttonVariants({ variant: 'ghost', size, rounded: 'md' }),
41
66
  // Overides the bg change on hover --not a "hover effect"
42
67
  'relative group p-0 aspect-square hover:bg-background',
68
+ ((wiggleRef.current.get() === 'more') ?
69
+ 'item-added-to-cart-animation'
70
+ :
71
+ (wiggleRef.current.get() === 'less') ? 'item-removed-from-cart-animation' : ''),
43
72
  className
44
73
  )}
45
74
  >
@@ -53,12 +82,14 @@ const BagButton: React.FC<{
53
82
  <div>{c.cartQuantity}</div>
54
83
  </div>
55
84
  )}
56
- <Icons.bag className={cn(
57
- 'relative -top-[3px] fill-primary w-6 h-7 ',
85
+ <Icons.bag width='24' height='28' className={cn(
86
+ 'relative -top-[3px] fill-primary',
58
87
  iconClx,
59
- (noHoverEffects ? '' : (
88
+ (animateOnHover ?
60
89
  'group-hover:fill-primary-hover group-hover:scale-105 transition-scale transition-duration-300'
61
- ))
90
+ :
91
+ ''
92
+ )
62
93
  )} aria-hidden="true" />
63
94
  </div>
64
95
  )
@@ -0,0 +1,4 @@
1
+ <svg viewBox="0 0 32 40" >
2
+ <path d="M27.724,27.85a3.4,3.4,0,0,0,.825-2.672l-1.8-14.4A3.405,3.405,0,0,0,23.375,7.8h-.521a5.173,5.173,0,0,0-10.32,0H3.894a1,1,0,0,0,0,2H8.929a3.316,3.316,0,0,0-.29.978l-.382,3.055a.889.889,0,0,0-.163-.033h-3a1,1,0,0,0,0,2H8.011L6.839,25.178A3.4,3.4,0,0,0,10.212,29H25.175A3.4,3.4,0,0,0,27.724,27.85ZM17.694,5a3.2,3.2,0,0,1,3.16,2.8h-6.32A3.194,3.194,0,0,1,17.694,5ZM9.163,26.526a1.384,1.384,0,0,1-.34-1.1l1.8-14.4A1.4,1.4,0,0,1,12.013,9.8h.481V13a1,1,0,0,0,2,0V9.8h6.4V13a1,1,0,0,0,2,0V9.8h.481a1.4,1.4,0,0,1,1.39,1.226l1.8,14.4A1.4,1.4,0,0,1,25.175,27H10.212A1.38,1.38,0,0,1,9.163,26.526Z"/>
3
+ <path d="M7.894,11.8a1,1,0,0,0-1-1h-3a1,1,0,0,0,0,2h3A1,1,0,0,0,7.894,11.8Z"/>
4
+ </svg>
@@ -0,0 +1,44 @@
1
+ 'use client'
2
+ import React, {type PropsWithChildren } from 'react'
3
+
4
+ import { X as LucideX} from 'lucide-react'
5
+
6
+ import { Button, Drawer, DrawerContent, type DrawerProps } from '@hanzo/ui/primitives'
7
+ import { cn } from '@hanzo/ui/util'
8
+
9
+ const CommerceDrawer: React.FC<PropsWithChildren &
10
+ Omit<DrawerProps, 'onOpenChange'> &
11
+ {
12
+ setOpen: (b: boolean) => void
13
+ drawerClx?: string
14
+ }
15
+ > = ({
16
+ children,
17
+ open,
18
+ setOpen,
19
+ modal,
20
+ drawerClx='',
21
+ ...rest
22
+ }) => (
23
+ // @ts-ignore
24
+ <Drawer open={open} onOpenChange={setOpen} modal={modal} {...rest}>
25
+ <DrawerContent modal={modal} className={cn(
26
+ 'rounded-t-xl mt-6 pt-6',
27
+ drawerClx
28
+ )}>
29
+ {children}
30
+ <Button
31
+ variant='ghost'
32
+ size='icon'
33
+ onClick={() => {setOpen(false)}}
34
+ className={'absolute top-4 right-4 w-8 h-8 group rounded-full p-1 hidden md:flex items-center'}
35
+ >
36
+ <LucideX className='w-6 h-6 text-muted group-hover:text-foreground'/>
37
+ </Button>
38
+ </DrawerContent>
39
+ </Drawer>
40
+ )
41
+
42
+
43
+ export default CommerceDrawer
44
+
@@ -0,0 +1,192 @@
1
+ 'use client'
2
+ import React, { useEffect, useRef } from 'react'
3
+ import { createPortal } from 'react-dom'
4
+ import { useRouter, usePathname } from 'next/navigation'
5
+ import { ObservableSet, observable, reaction, type IReactionDisposer, runInAction} from 'mobx'
6
+ import { observer } from 'mobx-react-lite'
7
+
8
+ import { cn } from '@hanzo/ui/util'
9
+ import { Image } from '@hanzo/ui/primitives'
10
+
11
+ import { useCommerce, useCommerceUI, CarouselBuyCard, LineItemRef } from '@hanzo/commerce'
12
+ import type { LineItem } from '@hanzo/commerce/types'
13
+
14
+ import CommerceDrawer from './drawer'
15
+ import CheckoutButton from '../checkout-button'
16
+
17
+ const DEF_IMG_CONSTRAINT={w: 40, h: 24}
18
+ const CO_ANIM_DURATION = 400
19
+ const CO_ANIM_TIMING_FN = 'cubic-bezier(0.4, 0, 0.2, 1)'
20
+ const CO_WIDGET_W_CLX = {
21
+ checkout: 'w-pr-40',
22
+ itemInfo: 'w-pr-60'
23
+ }
24
+
25
+ const CO_WIDGET_SHADOW_STYLE = {
26
+ border: '1px solid rgb(100 100 100)',
27
+ boxShadow: '2px 4px 4px -3px rgb(125 125 125 / 0.7), 4px -4px 8px -4px rgb(125 125 125 / 0.7)'
28
+ }
29
+
30
+ const CommerceUIComponent: React.FC = observer(() => {
31
+
32
+ const ui = useCommerceUI()
33
+ const cmmc = useCommerce()
34
+ const router = useRouter()
35
+ const isCheckout = usePathname() === '/checkout'
36
+
37
+ const animClxRef = useRef<ObservableSet<string>>(observable.set(new Set<string>(
38
+ (cmmc.cartEmpty || ui.buyOptionsSkuPath || isCheckout) ? ['hidden'] : []
39
+ )))
40
+
41
+ // a ref that is synced to ui.activeItem, but persists for CO_ANIM_DURATION longer
42
+ // so ui does not jump while animating "out"
43
+ const laggingActiveItemRef = useRef<LineItemRef>(new LineItemRef())
44
+ const disposersRef = useRef<IReactionDisposer[]>([])
45
+
46
+ useEffect(() => {
47
+ disposersRef.current.push(
48
+ reaction(
49
+ () => (ui.activeItem),
50
+ (item: LineItem | undefined) => {
51
+ if (item) {
52
+ laggingActiveItemRef.current.set(item)
53
+ }
54
+ else {
55
+ setTimeout(() => { laggingActiveItemRef.current.set(undefined) }, CO_ANIM_DURATION)
56
+ }
57
+ },
58
+ {equals: (val, prev) => (val?.sku === prev?.sku)}
59
+ )
60
+ )
61
+
62
+ disposersRef.current.push(
63
+ reaction(
64
+ () => ({
65
+ microOpen: !(cmmc.cartEmpty || !!ui.buyOptionsSkuPath || isCheckout),
66
+ buyOpen: !!ui.buyOptionsSkuPath
67
+ }),
68
+ (val, prev) => {
69
+
70
+ runInAction(() => {
71
+ if (!val.microOpen && prev.microOpen) {
72
+ animClxRef.current.add('checkout-widget-disappears')
73
+ }
74
+ else if (val.microOpen && !prev.microOpen) {
75
+ animClxRef.current.delete('hidden')
76
+ animClxRef.current.add('checkout-widget-appears')
77
+ }
78
+ if (!val.buyOpen && prev.buyOpen) {
79
+ animClxRef.current.add('checkout-widget-appears-after-buy-drawer-closes')
80
+ }
81
+ else {
82
+ animClxRef.current.delete('checkout-widget-appears-after-buy-drawer-closes')
83
+ }
84
+ })
85
+
86
+ setTimeout(() => {runInAction(() => {
87
+ animClxRef.current.delete('checkout-widget-appears')
88
+ animClxRef.current.delete('checkout-widget-appears-after-buy-drawer-closes')
89
+ if (animClxRef.current.has('checkout-widget-disappears') ) {
90
+ animClxRef.current.delete('checkout-widget-disappears')
91
+ animClxRef.current.add('hidden')
92
+ }
93
+ })}, 800)
94
+ },
95
+ {equals: (val, prev) => (
96
+ val.microOpen === prev.microOpen
97
+ &&
98
+ val.buyOpen === prev.buyOpen
99
+ )}
100
+ )
101
+ )
102
+
103
+ return () => {
104
+ disposersRef.current.forEach((d) => {d()})
105
+ }
106
+ }, [])
107
+
108
+
109
+
110
+ const handleCheckout = () => {
111
+ // router.push('/checkout')
112
+ }
113
+
114
+ // Should only ever be called internally to close
115
+ const reallyOnlyCloseDrawer = (b: boolean) => {
116
+ if (!b ) {
117
+ ui.hideBuyOptions()
118
+ }
119
+ }
120
+
121
+ return (<>
122
+ <CommerceDrawer
123
+ open={!!ui.buyOptionsSkuPath}
124
+ setOpen={reallyOnlyCloseDrawer}
125
+ modal={true}
126
+ drawerClx={'w-full md:max-w-[550px] md:mx-auto lg:max-w-[50vw]'}
127
+ >
128
+ <CarouselBuyCard
129
+ skuPath={ui.buyOptionsSkuPath!}
130
+ checkoutButton={
131
+ <CheckoutButton handleCheckout={handleCheckout} className='w-full min-w-[160px] sm:max-w-[320px]'/>
132
+ }
133
+ clx='w-full'
134
+ addBtnClx='w-full min-w-[160px] sm:max-w-[320px]'
135
+ selectorClx='max-w-[475px]'
136
+ />
137
+ </CommerceDrawer>
138
+ {globalThis?.document?.body && createPortal(
139
+ <div
140
+ className={cn(
141
+ 'min-w-[160px] sm:max-w-[320px] w-[calc(100%-32px)] mx-auto !h-10',
142
+ 'z-below-modal-2 fixed bottom-[20px] left-0 right-0',
143
+ 'rounded-lg bg-background',
144
+ 'flex',
145
+ ui.activeItem ? 'gap-2' : '',
146
+ Array.from(animClxRef.current).join(' ')
147
+ )}
148
+ style={laggingActiveItemRef.current.item ? {} : CO_WIDGET_SHADOW_STYLE}
149
+ >
150
+ <div
151
+ className={cn(
152
+ 'flex flex-row justify-between items-center',
153
+ ui.activeItem ? CO_WIDGET_W_CLX.itemInfo : 'w-0',
154
+ laggingActiveItemRef.current.item ? 'px-3 border rounded-lg border-muted-3' : ''
155
+ )}
156
+ style={{
157
+ transitionProperty: 'width',
158
+ transitionTimingFunction: CO_ANIM_TIMING_FN,
159
+ transitionDuration: `${CO_ANIM_DURATION}ms`
160
+ }}
161
+ >
162
+ {laggingActiveItemRef.current.item?.img ? (
163
+ <Image def={laggingActiveItemRef.current.item.img} constrainTo={DEF_IMG_CONSTRAINT} preload className='grow-0 shrink-0'/>
164
+ ) : ( // placeholder so things align
165
+ <div style={{height: DEF_IMG_CONSTRAINT.h, width: DEF_IMG_CONSTRAINT.w}} className='bg-level-3 grow-0 shrink-0'/>
166
+ )}
167
+
168
+ <div className='text-muted grow ml-1'>
169
+ {laggingActiveItemRef.current.item && (<>
170
+ <p className='whitespace-nowrap text-sm'>{laggingActiveItemRef.current.item.title}</p>
171
+ <p className='whitespace-nowrap text-xxs' >recently added...</p>
172
+ </>)}
173
+ </div>
174
+ </div>
175
+ <CheckoutButton
176
+ handleCheckout={handleCheckout}
177
+ centerText={!!!ui.activeItem}
178
+ variant='primary' rounded='lg'
179
+ className={cn(ui.activeItem ? CO_WIDGET_W_CLX.checkout : 'w-full')}
180
+ style={{
181
+ transitionProperty: 'width',
182
+ transitionTimingFunction: CO_ANIM_TIMING_FN,
183
+ transitionDuration: `${CO_ANIM_DURATION}ms`
184
+ }}
185
+ />
186
+ </div>,
187
+ globalThis?.document?.body
188
+ )}
189
+ </>)
190
+ })
191
+
192
+ export default CommerceUIComponent
@@ -0,0 +1,116 @@
1
+ 'use client'
2
+ import React, { useEffect, useRef } from 'react'
3
+ import { observable, type IObservableValue, reaction } from 'mobx'
4
+ import { observer } from 'mobx-react-lite'
5
+ import { type LucideProps } from 'lucide-react'
6
+
7
+ import { Button, type ButtonProps } from '@hanzo/ui/primitives'
8
+ import { cn } from '@hanzo/ui/util'
9
+ import { useCommerce } from '@hanzo/commerce'
10
+
11
+ import * as Icons from '../icons'
12
+
13
+ const IconAndQuantity: React.FC<{
14
+ animateOnQuantityChange?: boolean
15
+ clx?: string
16
+ iconClx?: string
17
+ digitClx?: string
18
+ }> = observer(({
19
+ animateOnQuantityChange=true,
20
+ clx='',
21
+ iconClx='',
22
+ digitClx=''
23
+ }) => {
24
+
25
+ const cmmc = useCommerce()
26
+ const wiggleRef = useRef<IObservableValue<'more' | 'less' | 'none'>>(observable.box('none'))
27
+
28
+ useEffect(() => (
29
+ // return IReactionDisposer
30
+ animateOnQuantityChange ? reaction(
31
+ () => (cmmc.cartQuantity),
32
+ (curr, prev) => {
33
+ if (curr > prev) {
34
+ wiggleRef.current.set('more')
35
+ }
36
+ else {
37
+ wiggleRef.current.set('less')
38
+ }
39
+ setTimeout(() => {
40
+ // Note that this doesn't actually stop the animation
41
+ // just resets the styles
42
+ wiggleRef.current.set('none')
43
+ }, 800)
44
+ }
45
+ ) : undefined
46
+ ), [])
47
+
48
+ return (
49
+ <div className={cn('flex items-center justify-center', clx)}>
50
+ <div className={cn(
51
+ 'relative flex items-center justify-center mr-1',
52
+ ((wiggleRef.current.get() === 'more') ?
53
+ 'item-added-to-cart-animation'
54
+ :
55
+ (wiggleRef.current.get() === 'less') ? 'item-removed-from-cart-animation' : ''),
56
+ )} >
57
+ {cmmc.cartQuantity > 0 && (
58
+ <div className={cn(
59
+ 'z-above-content flex flex-col justify-center items-center',
60
+ 'absolute left-0 right-0 top-0 bottom-0',
61
+ digitClx
62
+ )}>
63
+ <div style={{color: 'white' /* tailwind bug? */, fontSize: '11px', position: 'relative', top: '1px' }}>{cmmc.cartQuantity}</div>
64
+ </div>
65
+ )}
66
+ <Icons.bag width='19' height='24' className={cn('relative -top-[3px] opacity-70' , iconClx)} aria-hidden="true" />
67
+ </div>
68
+ <span style={{fontSize: '17px',}}>&rsaquo;</span>
69
+ </div>
70
+ )
71
+ })
72
+
73
+ const CheckoutButton: React.FC<ButtonProps & {
74
+ handleCheckout: () => void
75
+ showQuantity?: boolean
76
+ animateOnQuantityChange?: boolean
77
+ centerText?: boolean
78
+ }> = ({
79
+ handleCheckout,
80
+ variant='primary',
81
+ rounded='lg',
82
+ className,
83
+ showQuantity=true,
84
+ animateOnQuantityChange=true,
85
+ centerText=true,
86
+ ...rest
87
+ }) => {
88
+
89
+ return (
90
+ <Button
91
+ {...rest}
92
+ onClick={handleCheckout}
93
+ variant={variant}
94
+ rounded={rounded}
95
+ className={cn(
96
+ className,
97
+ 'flex justify-between items-stretch',
98
+ showQuantity ? (centerText ? 'px-1.5' : 'pl-2.5 pr-1.5') : ''
99
+ )}
100
+ >
101
+ {showQuantity && centerText && (
102
+ <IconAndQuantity clx='invisible' />
103
+ )}
104
+ <div className='flex justify-center items-center'>Checkout</div>
105
+ {showQuantity && (
106
+ <IconAndQuantity
107
+ animateOnQuantityChange={animateOnQuantityChange}
108
+ iconClx='fill-fg-foreground'
109
+ digitClx='text-primary-fg leading-none font-bold font-sans'
110
+ />
111
+ )}
112
+ </Button>
113
+ )
114
+ }
115
+
116
+ export default CheckoutButton
@@ -32,12 +32,11 @@ const MobileCheckoutPanel: React.FC<PropsWithChildren & {
32
32
  <CartAccordian
33
33
  icon={
34
34
  <BagButton
35
- noHoverEffects
35
+ animateOnHover={false}
36
36
  showIfEmpty
37
37
  size='sm'
38
- className=
39
- 'mr-1 relative w-5 h-6 sm:w-6 sm:h-7 '
40
- iconClx='fill-foreground '
38
+ className='mr-1 relative w-5 h-6 sm:w-6 sm:h-7'
39
+ iconClx='fill-foreground'
41
40
  />
42
41
  }
43
42
  className='flex items-center justify-center w-full'
@@ -33,7 +33,7 @@ const MobileBagDrawer: React.FC<{
33
33
  >
34
34
  <div className='flex flex-row items-center flex-none justify-center '>
35
35
  <BagButton
36
- noHoverEffects
36
+ animateOnHover={false}
37
37
  showIfEmpty
38
38
  className=
39
39
  'mr-2 relative w-6 h-7'
@@ -25,7 +25,7 @@ const DesktopHeader: React.FC<{
25
25
  // TODO move 13px into a size class and configure twMerge to recognize say, 'text-size-nav'
26
26
  // (vs be beat out by 'text-color-nav')
27
27
  return (
28
- <header className={cn('bg-background sticky z-header top-0 ', className)} >
28
+ <header className={cn('bg-background fixed z-header top-0 left-0 right-0', className)} >
29
29
  {/* md or larger */}
30
30
  <div className={
31
31
  'flex flex-row h-[80px] items-center justify-between ' +
@@ -10,7 +10,10 @@ export { default as MiniChart } from './mini-chart'
10
10
  export { default as NotFound } from './not-found'
11
11
 
12
12
 
13
+
14
+ export { default as BuyDrawer } from './commerce/buy-drawer'
13
15
  export { default as CheckoutPanel } from './commerce/checkout-panel'
16
+ export { default as CheckoutButton } from './commerce/checkout-button'
14
17
  export { default as LoginPanel } from './auth/login-panel'
15
18
  export { default as AuthListener } from './auth/auth-listener'
16
19
  export { default as Scripts } from './scripts'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luxfi/core",
3
- "version": "4.5.3",
3
+ "version": "5.0.0",
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/",
@@ -37,8 +37,8 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "@hanzo/auth": "2.4.6",
40
- "@hanzo/commerce": "6.4.7",
41
- "@hanzo/ui": "3.6.5",
40
+ "@hanzo/commerce": "7.0.0",
41
+ "@hanzo/ui": "3.7.0",
42
42
  "@next/third-parties": "^14.1.0",
43
43
  "cookies-next": "^4.1.1",
44
44
  "date-fns": "^3.6.0",
@@ -47,6 +47,9 @@
47
47
  "react-social-icons": "^6.4.0"
48
48
  },
49
49
  "peerDependencies": {
50
+ "@hanzo/auth": "2.4.6",
51
+ "@hanzo/commerce": "6.4.7",
52
+ "@hanzo/ui": "3.6.5",
50
53
  "@hookform/resolvers": "^3.3.2",
51
54
  "lucide-react": "^0.344.0",
52
55
  "next": "14.1.3",
@@ -0,0 +1,29 @@
1
+ @keyframes wiggle-larger {
2
+ 0% { transform: rotate(0deg); }
3
+ 18% { transform: rotate(10deg) scale(0.9); }
4
+ 32% { transform: rotate(0deg) scale(1.05);; }
5
+ 50% { transform: rotate(-10deg) scale(1.05); }
6
+ 68% { transform: rotate(0deg) scale(1.1); }
7
+ 84% { transform: rotate(10deg) scale(1); }
8
+ 100% { transform: rotate(0deg) scale(1);}
9
+ }
10
+
11
+ @keyframes wiggle-smaller {
12
+ 0% { transform: rotate(0deg); }
13
+ 18% { transform: rotate(10deg) scale(1.05); }
14
+ 32% { transform: rotate(0deg) scale(0.9);; }
15
+ 50% { transform: rotate(-10deg) scale(0.9); }
16
+ 68% { transform: rotate(0deg) scale(0.85); }
17
+ 84% { transform: rotate(10deg) scale(1); }
18
+ 100% { transform: rotate(0deg) scale(1);}
19
+ }
20
+
21
+ .wiggle-larger-animation,
22
+ .item-added-to-cart-animation {
23
+ animation: wiggle-larger 350ms;
24
+ }
25
+
26
+ .wiggle-smaller-animation,
27
+ .item-removed-from-cart-animation {
28
+ animation: wiggle-smaller 300ms;
29
+ }
@@ -0,0 +1,23 @@
1
+ @keyframes checkout-widget-fade-in {
2
+ 0% { transform: scale(0.5); opacity: 0; }
3
+ 100% { transform: scale(1); opacity: 1;}
4
+ }
5
+
6
+ .checkout-widget-appears {
7
+ animation-name: checkout-widget-fade-in;
8
+ animation-duration: 200ms;
9
+ }
10
+
11
+ .checkout-widget-disappears {
12
+ animation-name: checkout-widget-fade-in;
13
+ animation-duration: 200ms;
14
+ animation-direction: reverse;
15
+ animation-fill-mode: forwards;
16
+ }
17
+
18
+ .checkout-widget-appears-after-buy-drawer-closes {
19
+ animation-fill-mode: backwards;
20
+ /* Drawer is hardcoded to open in 0.5s. We can start slightly sooner. */
21
+ animation-delay: 400ms;
22
+ }
23
+
@@ -10,7 +10,7 @@
10
10
  @import "tailwindcss/components";
11
11
  @import "tailwindcss/utilities";
12
12
 
13
- .nextjs-toast-errors-parent {
13
+ div.nextjs-toast-errors-parent[data-nextjs-toast="true"] {
14
14
  display: none;
15
15
  }
16
16