@luxfi/core 5.1.3 → 5.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. package/commerce/AUTO-GEN-bullion-by-family.json +33 -33
  2. package/commerce/EDIT-ME-bullion-market-prices.ts +11 -11
  3. package/commerce/assign-prices.ts +49 -49
  4. package/commerce/assign-videos-by-family-group.ts +14 -14
  5. package/commerce/bullion-price-1oz.ts +5 -5
  6. package/commerce/index.ts +18 -18
  7. package/commerce/lux-service-options.ts +6 -6
  8. package/components/access-code-input.tsx +71 -71
  9. package/components/auth/auth-listener.tsx +29 -29
  10. package/components/auth/auth-token/clear-auth-token.tsx +12 -12
  11. package/components/auth/auth-token/set-auth-token.tsx +16 -16
  12. package/components/auth/common-auth-domains.ts +16 -16
  13. package/components/auth/login-panel.tsx +104 -104
  14. package/components/chat-widget.tsx +80 -80
  15. package/components/commerce/bag-button.tsx +98 -98
  16. package/components/commerce/buy-drawer/drawer.tsx +44 -44
  17. package/components/commerce/buy-drawer/index.tsx +46 -46
  18. package/components/commerce/checkout-button.tsx +116 -116
  19. package/components/commerce/checkout-panel/close-button.tsx +26 -26
  20. package/components/commerce/checkout-panel/dt-bag-carousel.tsx +36 -36
  21. package/components/commerce/checkout-panel/dt-checkout-panel.tsx +66 -66
  22. package/components/commerce/checkout-panel/index.tsx +123 -123
  23. package/components/commerce/checkout-panel/links-row.tsx +21 -21
  24. package/components/commerce/checkout-panel/mb-checkout-panel.tsx +54 -54
  25. package/components/commerce/checkout-panel/steps-indicator.tsx +39 -39
  26. package/components/commerce/checkout-panel/thank-you.tsx +18 -18
  27. package/components/commerce/checkout-widget/const.ts +13 -13
  28. package/components/commerce/checkout-widget/index.tsx +86 -86
  29. package/components/commerce/checkout-widget/obs-string-set.ts +48 -48
  30. package/components/commerce/checkout-widget/use-anim-clx-set.ts +56 -56
  31. package/components/commerce/checkout-widget/use-lagging-item-ref.ts +29 -29
  32. package/components/commerce/desktop-bag-popup.tsx +78 -78
  33. package/components/commerce/mobile-bag-drawer.tsx +51 -51
  34. package/components/commerce/mobile-login-button.tsx +101 -0
  35. package/components/commerce/mobile-menu-toggle-button.tsx +35 -35
  36. package/components/commerce/mobile-nav-menu-ai.tsx +98 -0
  37. package/components/commerce/mobile-nav-menu-item.tsx +46 -0
  38. package/components/commerce/mobile-nav-menu.tsx +79 -64
  39. package/components/contact-dialog/contact-form.tsx +112 -112
  40. package/components/contact-dialog/disclaimer.tsx +13 -13
  41. package/components/contact-dialog/index.tsx +64 -64
  42. package/components/copyright.tsx +21 -21
  43. package/components/footer.tsx +77 -77
  44. package/components/header/desktop.tsx +54 -54
  45. package/components/header/guts.tsx +27 -0
  46. package/components/header/index.tsx +47 -26
  47. package/components/header/mobile.tsx +165 -161
  48. package/components/header/theme-toggle.tsx +26 -26
  49. package/components/icons/avatar.tsx +11 -0
  50. package/components/icons/bag-icon.tsx +10 -10
  51. package/components/icons/github.tsx +14 -14
  52. package/components/icons/index.tsx +43 -35
  53. package/components/icons/left-arrow.tsx +11 -0
  54. package/components/icons/lux-logo.tsx +10 -10
  55. package/components/icons/right-arrow.tsx +10 -0
  56. package/components/icons/search.tsx +12 -0
  57. package/components/icons/secure-delivery.tsx +13 -13
  58. package/components/icons/social-icon.tsx +35 -35
  59. package/components/icons/social-svg.css +3 -3
  60. package/components/icons/youtube-logo.tsx +59 -59
  61. package/components/index.ts +27 -27
  62. package/components/logo.tsx +81 -81
  63. package/components/mini-chart/index.tsx +7 -7
  64. package/components/mini-chart/mini-chart-props.ts +43 -43
  65. package/components/mini-chart/mini-chart.tsx +85 -85
  66. package/components/mini-chart/wrapper.tsx +23 -23
  67. package/components/not-found/index.tsx +27 -27
  68. package/components/not-found/not-found-content.mdx +5 -5
  69. package/components/scripts.tsx +24 -24
  70. package/conf/index.ts +50 -50
  71. package/environment.d.ts +5 -5
  72. package/next/analytics/fpixel.ts +15 -15
  73. package/next/analytics/google-analytics.ts +13 -13
  74. package/next/analytics/index.ts +3 -3
  75. package/next/analytics/pixel-analytics.tsx +54 -54
  76. package/next/font/get-app-router-font-classes.ts +12 -12
  77. package/next/font/load-and-return-lux-next-fonts-on-import.ts +68 -68
  78. package/next/font/next-font-desc.ts +27 -27
  79. package/next/font/pages-router-font-vars.tsx +18 -18
  80. package/next/head-metadata/from-next/metadata-types.ts +158 -158
  81. package/next/head-metadata/from-next/opengraph-types.ts +267 -267
  82. package/next/head-metadata/from-next/twitter-types.ts +92 -92
  83. package/next/head-metadata/index.tsx +208 -208
  84. package/next/middleware/determine-device-mw.ts +16 -16
  85. package/package.json +73 -72
  86. package/root-layout/WHY_THIS_IS_SEPARATE.txt +1 -1
  87. package/root-layout/index.tsx +121 -120
  88. package/server-actions/firebase-app.ts +14 -14
  89. package/server-actions/index.ts +5 -5
  90. package/server-actions/store-contact.ts +51 -51
  91. package/site-def/footer/community.tsx +67 -67
  92. package/site-def/footer/company.ts +37 -37
  93. package/site-def/footer/ecosystem.ts +37 -37
  94. package/site-def/footer/index.tsx +26 -26
  95. package/site-def/footer/legal.ts +28 -28
  96. package/site-def/footer/network.ts +45 -45
  97. package/site-def/footer/svg/warpcast-logo.svg +11 -11
  98. package/site-def/index.ts +2 -2
  99. package/site-def/main-nav.tsx +292 -0
  100. package/style/cart-animation.css +29 -29
  101. package/style/checkout-animation.css +23 -23
  102. package/style/lux-colors.css +85 -85
  103. package/style/lux-global.css +30 -30
  104. package/tailwind/fontFamily.tailwind.lux.ts +18 -18
  105. package/tailwind/index.ts +2 -2
  106. package/tailwind/lux-tw-fonts.ts +39 -39
  107. package/tailwind/tailwind.config.lux-preset.ts +10 -10
  108. package/tsconfig.json +10 -28
  109. package/types/chatbot-config.ts +6 -6
  110. package/types/chatbot-suggested-question.ts +7 -7
  111. package/types/commerce-config.ts +10 -10
  112. package/types/contact-info.ts +10 -10
  113. package/types/index.ts +5 -5
  114. package/types/site-def.ts +45 -45
  115. package/site-def/main-nav.ts +0 -35
@@ -1,104 +1,104 @@
1
- 'use client'
2
-
3
- import Link from 'next/link'
4
- import { useRouter } from 'next/navigation'
5
-
6
- import Autoplay from 'embla-carousel-autoplay'
7
- import { setCookie } from 'cookies-next'
8
-
9
- import { cn } from '@hanzo/ui/util'
10
- import { Button, Carousel, CarouselContent, CarouselItem } from '@hanzo/ui/primitives'
11
- import { LoginPanel as Login } from '@hanzo/auth/components'
12
-
13
- import Logo from '../logo'
14
- import LuxLogo from '../icons/lux-logo'
15
- import { legal } from '../../site-def/footer'
16
-
17
- const LoginPanel: React.FC<{
18
- close: () => void
19
- getStartedUrl?: string
20
- redirectUrl?: string
21
- className?: string
22
- reviews: { text: string, author: string, href: string }[]
23
- }> = ({
24
- close,
25
- getStartedUrl='/',
26
- redirectUrl,
27
- className='',
28
- reviews
29
- }) => {
30
- const router = useRouter()
31
-
32
- const termsOfServiceUrl = legal.find(({title}) => title === 'Terms and Conditions')?.href || ''
33
- const privacyPolicyUrl = legal.find(({title}) => title === 'Privacy Policy')?.href || ''
34
-
35
- const onLogin = (token: string) => {
36
- setCookie('auth-token', token, {
37
- domain: 'lux.services',
38
- path: '/',
39
- sameSite: 'none',
40
- secure: true,
41
- httpOnly: false,
42
- expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30), // 30 days
43
- })
44
-
45
- redirectUrl && router.push(redirectUrl)
46
- }
47
-
48
- return (
49
- <div className={cn('grid grid-cols-1 md:grid-cols-2', className)}>
50
- <div className='hidden md:flex w-full h-full bg-level-1 flex-row items-end justify-end overflow-y-auto min-h-screen'>
51
- <div className='h-full w-full max-w-[750px] px-8 pt-0'>
52
- <div className='h-full w-full max-w-[550px] mx-auto flex flex-col justify-between min-h-screen py-10'>
53
- <Button
54
- variant='ghost'
55
- onClick={close}
56
- className='w-fit !min-w-0 p-2'
57
- >
58
- <Logo size='md' spanClassName='!cursor-pointer' layout='text-only'/>
59
- </Button>
60
- <Carousel
61
- options={{ align: 'center', loop: true }}
62
- className='w-full'
63
- plugins={[Autoplay({ delay: 5000, stopOnInteraction: true })]}
64
- >
65
- <CarouselContent>
66
- {reviews.map(({text, author, href}, index) => (
67
- <CarouselItem key={index}>
68
- <Link href={href} className='flex flex-col gap-3 cursor-pointer'>
69
- <p>“{text}“</p>
70
- <p className='text-sm'>{author}</p>
71
- </Link>
72
- </CarouselItem>
73
- ))}
74
- </CarouselContent>
75
- </Carousel>
76
- </div>
77
- </div>
78
- </div>
79
- <div className='w-full h-full bg-background flex flex-row items-center'>
80
- <div className='w-full max-w-[750px] relative flex flex-col items-center px-8 pt-0 text-center'>
81
- <div className='relative h-full w-full max-w-[400px] mx-auto flex flex-col gap-4 items-center py-10'>
82
- <Button
83
- variant='ghost'
84
- onClick={close}
85
- className='block md:hidden absolute rounded-full p-2 left-0 h-auto hover:bg-background'
86
- >
87
- <LuxLogo className='w-5 h-5'/>
88
- </Button>
89
- <Login
90
- getStartedUrl={getStartedUrl}
91
- redirectUrl={redirectUrl}
92
- className='w-full max-w-sm'
93
- termsOfServiceUrl={termsOfServiceUrl}
94
- privacyPolicyUrl={privacyPolicyUrl}
95
- onLoginChanged={onLogin}
96
- />
97
- </div>
98
- </div>
99
- </div>
100
- </div>
101
- )
102
- }
103
-
104
- export default LoginPanel
1
+ 'use client'
2
+
3
+ import Link from 'next/link'
4
+ import { useRouter } from 'next/navigation'
5
+
6
+ import Autoplay from 'embla-carousel-autoplay'
7
+ import { setCookie } from 'cookies-next'
8
+
9
+ import { cn } from '@hanzo/ui/util'
10
+ import { Button, Carousel, CarouselContent, CarouselItem } from '@hanzo/ui/primitives'
11
+ import { LoginPanel as Login } from '@hanzo/auth/components'
12
+
13
+ import Logo from '../logo'
14
+ import LuxLogo from '../icons/lux-logo'
15
+ import { legal } from '../../site-def/footer'
16
+
17
+ const LoginPanel: React.FC<{
18
+ close: () => void
19
+ getStartedUrl?: string
20
+ redirectUrl?: string
21
+ className?: string
22
+ reviews: { text: string, author: string, href: string }[]
23
+ }> = ({
24
+ close,
25
+ getStartedUrl='/',
26
+ redirectUrl,
27
+ className='',
28
+ reviews
29
+ }) => {
30
+ const router = useRouter()
31
+
32
+ const termsOfServiceUrl = legal.find(({title}) => title === 'Terms and Conditions')?.href || ''
33
+ const privacyPolicyUrl = legal.find(({title}) => title === 'Privacy Policy')?.href || ''
34
+
35
+ const onLogin = (token: string) => {
36
+ setCookie('auth-token', token, {
37
+ domain: 'lux.services',
38
+ path: '/',
39
+ sameSite: 'none',
40
+ secure: true,
41
+ httpOnly: false,
42
+ expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30), // 30 days
43
+ })
44
+
45
+ redirectUrl && router.push(redirectUrl)
46
+ }
47
+
48
+ return (
49
+ <div className={cn('grid grid-cols-1 md:grid-cols-2', className)}>
50
+ <div className='hidden md:flex w-full h-full bg-level-1 flex-row items-end justify-end overflow-y-auto min-h-screen'>
51
+ <div className='h-full w-full max-w-[750px] px-8 pt-0'>
52
+ <div className='h-full w-full max-w-[550px] mx-auto flex flex-col justify-between min-h-screen py-10'>
53
+ <Button
54
+ variant='ghost'
55
+ onClick={close}
56
+ className='w-fit !min-w-0 p-2'
57
+ >
58
+ <Logo size='md' spanClassName='!cursor-pointer' layout='text-only'/>
59
+ </Button>
60
+ <Carousel
61
+ options={{ align: 'center', loop: true }}
62
+ className='w-full'
63
+ plugins={[Autoplay({ delay: 5000, stopOnInteraction: true })]}
64
+ >
65
+ <CarouselContent>
66
+ {reviews.map(({text, author, href}, index) => (
67
+ <CarouselItem key={index}>
68
+ <Link href={href} className='flex flex-col gap-3 cursor-pointer'>
69
+ <p>“{text}“</p>
70
+ <p className='text-sm'>{author}</p>
71
+ </Link>
72
+ </CarouselItem>
73
+ ))}
74
+ </CarouselContent>
75
+ </Carousel>
76
+ </div>
77
+ </div>
78
+ </div>
79
+ <div className='w-full h-full bg-background flex flex-row items-center'>
80
+ <div className='w-full max-w-[750px] relative flex flex-col items-center px-8 pt-0 text-center'>
81
+ <div className='relative h-full w-full max-w-[400px] mx-auto flex flex-col gap-4 items-center py-10'>
82
+ <Button
83
+ variant='ghost'
84
+ onClick={close}
85
+ className='block md:hidden absolute rounded-full p-2 left-0 h-auto hover:bg-background'
86
+ >
87
+ <LuxLogo className='w-5 h-5'/>
88
+ </Button>
89
+ <Login
90
+ getStartedUrl={getStartedUrl}
91
+ redirectUrl={redirectUrl}
92
+ className='w-full max-w-sm'
93
+ termsOfServiceUrl={termsOfServiceUrl}
94
+ privacyPolicyUrl={privacyPolicyUrl}
95
+ onLoginChanged={onLogin}
96
+ />
97
+ </div>
98
+ </div>
99
+ </div>
100
+ </div>
101
+ )
102
+ }
103
+
104
+ export default LoginPanel
@@ -1,80 +1,80 @@
1
- 'use client'
2
- import React from 'react'
3
-
4
- import { Button, Card } from '@hanzo/ui/primitives'
5
- import { cn } from '@hanzo/ui/util'
6
-
7
- import LuxLogo from './icons/lux-logo'
8
- import type { ChatbotSuggestedQuestion } from '../types'
9
-
10
- const ChatWidget: React.FC<{
11
- title: string,
12
- chatbotUrl: string,
13
- subtitle?: string,
14
- question?: string,
15
- /*
16
- ChatBotSuggestQuestion.icon
17
- Currently supports these icons from remix icons (https://remixicon.com/):
18
- GlobalLineIcon,
19
- ShieldFlashLineIcon,
20
- BankCardLineIcon,
21
- GroupLineIcon,
22
- QuestionnaireLineIcon
23
- */
24
- suggestedQuestions?: ChatbotSuggestedQuestion[]
25
- }> = ({
26
- title,
27
- chatbotUrl,
28
- subtitle,
29
- question,
30
- suggestedQuestions
31
- }) => {
32
-
33
- const [showChatbot, setShowChatbot] = React.useState<boolean>(false)
34
-
35
- const onClick = () => { setShowChatbot(!showChatbot) }
36
-
37
- const searchParams = new URLSearchParams()
38
- if (question) {
39
- searchParams.append('question', question)
40
- }
41
- if (suggestedQuestions) {
42
- searchParams.append('sQuestions', suggestedQuestions.map(({ message }) => message).join(','))
43
- searchParams.append('sHeadings', suggestedQuestions.map(({ heading }) => heading).join(','))
44
- searchParams.append('sIcons', suggestedQuestions.map(({ icon }) => icon).join(','))
45
- }
46
-
47
- const iframeSrc = `${chatbotUrl}?${searchParams.toString()}`
48
-
49
- return (<>
50
- <div className={
51
- 'fixed bottom-0 sm:bottom-16 right-0 w-full h-full ' +
52
- 'sm:max-w-[400px] sm:max-h-[550px] sm:px-4 z-floating ' +
53
- (showChatbot ? 'flex' : 'hidden')
54
- }>
55
- <Card className='flex flex-col h-full w-full'>
56
- <div className='flex px-4 py-2 h-12 bg-level-0 items-center justify-between'>
57
- <h3 className='font-semibold font-heading'>{title} <span className='opacity-60'>{subtitle}</span></h3>
58
- <Button onClick={onClick} variant='link' size='icon' className='w-fit sm:hidden'>
59
- <LuxLogo width={24} height={24}/>
60
- </Button>
61
- </div>
62
- <iframe src={iframeSrc} className='h-full' />
63
- </Card>
64
- </div>
65
-
66
- <LuxLogo
67
- width={28}
68
- height={28}
69
- onClick={onClick}
70
- className={cn(
71
- // z-index should be below anything in commerce-iu (buy drawer and checkout widget)
72
- 'fixed bottom-5 right-5 z-below-modal-3 transition-all cursor-pointer hover:drop-shadow-[0_2px_6px_rgba(255,255,255,1)]',
73
- showChatbot ? 'rotate-180' : ''
74
- )}
75
- strokeWidth={1}
76
- />
77
- </>)
78
- }
79
-
80
- export default ChatWidget
1
+ 'use client'
2
+ import React from 'react'
3
+
4
+ import { Button, Card } from '@hanzo/ui/primitives'
5
+ import { cn } from '@hanzo/ui/util'
6
+
7
+ import LuxLogo from './icons/lux-logo'
8
+ import type { ChatbotSuggestedQuestion } from '../types'
9
+
10
+ const ChatWidget: React.FC<{
11
+ title: string,
12
+ chatbotUrl: string,
13
+ subtitle?: string,
14
+ question?: string,
15
+ /*
16
+ ChatBotSuggestQuestion.icon
17
+ Currently supports these icons from remix icons (https://remixicon.com/):
18
+ GlobalLineIcon,
19
+ ShieldFlashLineIcon,
20
+ BankCardLineIcon,
21
+ GroupLineIcon,
22
+ QuestionnaireLineIcon
23
+ */
24
+ suggestedQuestions?: ChatbotSuggestedQuestion[]
25
+ }> = ({
26
+ title,
27
+ chatbotUrl,
28
+ subtitle,
29
+ question,
30
+ suggestedQuestions
31
+ }) => {
32
+
33
+ const [showChatbot, setShowChatbot] = React.useState<boolean>(false)
34
+
35
+ const onClick = () => { setShowChatbot(!showChatbot) }
36
+
37
+ const searchParams = new URLSearchParams()
38
+ if (question) {
39
+ searchParams.append('question', question)
40
+ }
41
+ if (suggestedQuestions) {
42
+ searchParams.append('sQuestions', suggestedQuestions.map(({ message }) => message).join(','))
43
+ searchParams.append('sHeadings', suggestedQuestions.map(({ heading }) => heading).join(','))
44
+ searchParams.append('sIcons', suggestedQuestions.map(({ icon }) => icon).join(','))
45
+ }
46
+
47
+ const iframeSrc = `${chatbotUrl}?${searchParams.toString()}`
48
+
49
+ return (<>
50
+ <div className={
51
+ 'fixed bottom-0 sm:bottom-16 right-0 w-full h-full ' +
52
+ 'sm:max-w-[400px] sm:max-h-[550px] sm:px-4 z-floating ' +
53
+ (showChatbot ? 'flex' : 'hidden')
54
+ }>
55
+ <Card className='flex flex-col h-full w-full'>
56
+ <div className='flex px-4 py-2 h-12 bg-level-0 items-center justify-between'>
57
+ <h3 className='font-semibold font-heading'>{title} <span className='opacity-60'>{subtitle}</span></h3>
58
+ <Button onClick={onClick} variant='link' size='icon' className='w-fit sm:hidden'>
59
+ <LuxLogo width={24} height={24}/>
60
+ </Button>
61
+ </div>
62
+ <iframe src={iframeSrc} className='h-full' />
63
+ </Card>
64
+ </div>
65
+
66
+ <LuxLogo
67
+ width={28}
68
+ height={28}
69
+ onClick={onClick}
70
+ className={cn(
71
+ // z-index should be below anything in commerce-iu (buy drawer and checkout widget)
72
+ 'fixed bottom-5 right-5 z-below-modal-3 transition-all cursor-pointer hover:drop-shadow-[0_2px_6px_rgba(255,255,255,1)]',
73
+ showChatbot ? 'rotate-180' : ''
74
+ )}
75
+ strokeWidth={1}
76
+ />
77
+ </>)
78
+ }
79
+
80
+ export default ChatWidget
@@ -1,98 +1,98 @@
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
-
6
- import { buttonVariants } from '@hanzo/ui/primitives'
7
- import { cn, type VariantProps } from '@hanzo/ui/util'
8
- import { useCommerce } from '@hanzo/commerce'
9
-
10
- import * as Icons from '../icons'
11
-
12
- const BagButton: React.FC<{
13
- showIfEmpty?: boolean
14
- animateOnHover?: boolean
15
- animateOnQuantityChange?: boolean
16
- size?: VariantProps<typeof buttonVariants>['size']
17
- className?: string
18
- iconClx?: string
19
- onClick?: () => void
20
- }> = observer(({
21
- showIfEmpty=false,
22
- animateOnHover=true,
23
- animateOnQuantityChange=true,
24
- size='default',
25
- className='',
26
- iconClx='',
27
- onClick
28
- }) => {
29
-
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
- ), [])
52
-
53
- // undefined means context is not installed, ie commerce functions are not in use
54
- if (!c || (!showIfEmpty && c.cartEmpty)) {
55
- return <div /> // trigger code needs non-null
56
- }
57
-
58
-
59
- return (
60
- <div
61
- aria-label="Bag"
62
- role='button'
63
- onClick={onClick}
64
- className={cn(
65
- buttonVariants({ variant: 'ghost', size, rounded: 'md' }),
66
- // Overides the bg change on hover --not a "hover effect"
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' : ''),
72
- className
73
- )}
74
- >
75
- {!c.cartEmpty && (
76
- <div className={
77
- 'z-above-content flex flex-col justify-center items-center ' +
78
- 'absolute left-0 right-0 top-0 bottom-0 ' +
79
- 'leading-none font-sans font-bold text-primary-fg text-xs '
80
- }>
81
- <div className='h-[3px] w-full' />
82
- <div>{c.cartQuantity}</div>
83
- </div>
84
- )}
85
- <Icons.bag width='24' height='28' className={cn(
86
- 'relative -top-[3px] fill-primary',
87
- iconClx,
88
- (animateOnHover ?
89
- 'group-hover:fill-primary-hover group-hover:scale-105 transition-scale transition-duration-300'
90
- :
91
- ''
92
- )
93
- )} aria-hidden="true" />
94
- </div>
95
- )
96
- })
97
-
98
- export default BagButton
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
+
6
+ import { buttonVariants } from '@hanzo/ui/primitives'
7
+ import { cn, type VariantProps } from '@hanzo/ui/util'
8
+ import { useCommerce } from '@hanzo/commerce'
9
+
10
+ import * as Icons from '../icons'
11
+
12
+ const BagButton: React.FC<{
13
+ showIfEmpty?: boolean
14
+ animateOnHover?: boolean
15
+ animateOnQuantityChange?: boolean
16
+ size?: VariantProps<typeof buttonVariants>['size']
17
+ className?: string
18
+ iconClx?: string
19
+ onClick?: () => void
20
+ }> = observer(({
21
+ showIfEmpty=false,
22
+ animateOnHover=true,
23
+ animateOnQuantityChange=true,
24
+ size='default',
25
+ className='',
26
+ iconClx='',
27
+ onClick
28
+ }) => {
29
+
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
+ ), [])
52
+
53
+ // undefined means context is not installed, ie commerce functions are not in use
54
+ if (!c || (!showIfEmpty && c.cartEmpty)) {
55
+ return <div /> // trigger code needs non-null
56
+ }
57
+
58
+
59
+ return (
60
+ <div
61
+ aria-label="Bag"
62
+ role='button'
63
+ onClick={onClick}
64
+ className={cn(
65
+ buttonVariants({ variant: 'ghost', size, rounded: 'md' }),
66
+ // Overides the bg change on hover --not a "hover effect"
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' : ''),
72
+ className
73
+ )}
74
+ >
75
+ {!c.cartEmpty && (
76
+ <div className={
77
+ 'z-above-content flex flex-col justify-center items-center ' +
78
+ 'absolute left-0 right-0 top-0 bottom-0 ' +
79
+ 'leading-none font-sans font-bold text-primary-fg text-xs '
80
+ }>
81
+ <div className='h-[3px] w-full' />
82
+ <div>{c.cartQuantity}</div>
83
+ </div>
84
+ )}
85
+ <Icons.bag width='24' height='28' className={cn(
86
+ 'relative -top-[3px] fill-primary',
87
+ iconClx,
88
+ (animateOnHover ?
89
+ 'group-hover:fill-primary-hover group-hover:scale-105 transition-scale transition-duration-300'
90
+ :
91
+ ''
92
+ )
93
+ )} aria-hidden="true" />
94
+ </div>
95
+ )
96
+ })
97
+
98
+ export default BagButton
@@ -1,44 +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
-
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
+