@luxfi/core 4.4.14 → 4.4.16

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 (84) hide show
  1. package/components/access-code-input.tsx +71 -71
  2. package/components/auth/auth-listener.tsx +29 -29
  3. package/components/auth/auth-token/clear-auth-token.tsx +12 -12
  4. package/components/auth/auth-token/set-auth-token.tsx +16 -16
  5. package/components/auth/common-auth-domains.ts +16 -16
  6. package/components/auth/login-panel.tsx +103 -98
  7. package/components/chat-widget.tsx +77 -77
  8. package/components/commerce/bag-button.tsx +67 -67
  9. package/components/commerce/checkout-panel/close-button.tsx +26 -26
  10. package/components/commerce/checkout-panel/dt-bag-carousel.tsx +36 -36
  11. package/components/commerce/checkout-panel/dt-checkout-panel.tsx +66 -66
  12. package/components/commerce/checkout-panel/index.tsx +123 -123
  13. package/components/commerce/checkout-panel/links-row.tsx +21 -21
  14. package/components/commerce/checkout-panel/mb-checkout-panel.tsx +55 -55
  15. package/components/commerce/checkout-panel/steps-indicator.tsx +39 -39
  16. package/components/commerce/checkout-panel/thank-you.tsx +18 -18
  17. package/components/commerce/desktop-bag-popup.tsx +78 -78
  18. package/components/commerce/mobile-bag-drawer.tsx +51 -51
  19. package/components/commerce/mobile-menu-toggle-button.tsx +35 -35
  20. package/components/commerce/mobile-nav-menu.tsx +64 -64
  21. package/components/contact-dialog/contact-form.tsx +112 -112
  22. package/components/contact-dialog/disclaimer.tsx +13 -13
  23. package/components/contact-dialog/index.tsx +64 -64
  24. package/components/copyright.tsx +21 -21
  25. package/components/footer.tsx +77 -77
  26. package/components/header/desktop.tsx +54 -54
  27. package/components/header/index.tsx +26 -26
  28. package/components/header/mobile.tsx +161 -161
  29. package/components/header/theme-toggle.tsx +26 -26
  30. package/components/icons/bag-icon.tsx +10 -10
  31. package/components/icons/github.tsx +14 -14
  32. package/components/icons/index.tsx +35 -35
  33. package/components/icons/lux-logo.tsx +10 -10
  34. package/components/icons/secure-delivery.tsx +13 -13
  35. package/components/icons/social-icon.tsx +35 -35
  36. package/components/icons/social-svg.css +3 -3
  37. package/components/icons/youtube-logo.tsx +59 -59
  38. package/components/index.ts +27 -27
  39. package/components/logo.tsx +81 -81
  40. package/components/mini-chart/index.tsx +7 -7
  41. package/components/mini-chart/mini-chart-props.ts +43 -43
  42. package/components/mini-chart/mini-chart.tsx +85 -85
  43. package/components/mini-chart/wrapper.tsx +23 -23
  44. package/components/not-found/index.tsx +27 -27
  45. package/components/not-found/not-found-content.mdx +5 -5
  46. package/components/root-layout.tsx +71 -71
  47. package/components/scripts.tsx +23 -23
  48. package/conf/index.ts +50 -50
  49. package/environment.d.ts +5 -5
  50. package/next/analytics/fpixel.ts +15 -15
  51. package/next/analytics/google-analytics.ts +13 -13
  52. package/next/analytics/index.ts +3 -3
  53. package/next/analytics/pixel-analytics.tsx +54 -54
  54. package/next/determine-device-mw.ts +16 -16
  55. package/next/font/get-app-router-font-classes.ts +12 -12
  56. package/next/font/load-and-return-lux-next-fonts-on-import.ts +68 -68
  57. package/next/font/next-font-desc.ts +27 -27
  58. package/next/font/pages-router-font-vars.tsx +18 -18
  59. package/next/head-metadata/from-next/metadata-types.ts +158 -158
  60. package/next/head-metadata/from-next/opengraph-types.ts +267 -267
  61. package/next/head-metadata/from-next/twitter-types.ts +92 -92
  62. package/next/head-metadata/index.tsx +208 -208
  63. package/package.json +73 -73
  64. package/server-actions/firebase-app.ts +14 -14
  65. package/server-actions/index.ts +5 -5
  66. package/server-actions/store-contact.ts +51 -51
  67. package/site-def/footer/community.tsx +67 -67
  68. package/site-def/footer/company.ts +37 -37
  69. package/site-def/footer/ecosystem.ts +37 -37
  70. package/site-def/footer/index.tsx +26 -26
  71. package/site-def/footer/legal.ts +28 -28
  72. package/site-def/footer/network.ts +45 -45
  73. package/site-def/footer/svg/warpcast-logo.svg +11 -11
  74. package/site-def/index.ts +2 -2
  75. package/site-def/main-nav.ts +35 -35
  76. package/site-def/site-def.ts +36 -36
  77. package/style/lux-colors.css +85 -85
  78. package/style/lux-global.css +30 -30
  79. package/tailwind/fontFamily.tailwind.lux.ts +18 -18
  80. package/tailwind/index.ts +2 -2
  81. package/tailwind/lux-tw-fonts.ts +39 -39
  82. package/tailwind/tailwind.config.lux-preset.ts +10 -10
  83. package/tsconfig.json +10 -10
  84. package/types/contact-info.ts +10 -10
@@ -1,78 +1,78 @@
1
- import React from 'react'
2
-
3
- import type { LinkDef } from '@hanzo/ui/types'
4
- import { NavItems } from '@hanzo/ui/primitives'
5
- import { cn } from '@hanzo/ui/util'
6
-
7
- import Copyright from './copyright'
8
- import type { SiteDef } from '../site-def'
9
- import { legal } from '../site-def/footer/legal'
10
- import Logo from './logo'
11
-
12
- const Footer: React.FC<{
13
- siteDef: SiteDef,
14
- className?: string,
15
- noHorizPadding?: boolean
16
- }> = ({
17
- siteDef,
18
- className='',
19
- noHorizPadding=false
20
- }) => {
21
-
22
- const { footer, aboveCopyright } = siteDef
23
- const smGridCols = Math.floor(footer.length/2)
24
- const smGridColsClx = `sm:grid-cols-${smGridCols} `
25
- const _aboveCopyright = (typeof aboveCopyright === 'undefined') ? legal : aboveCopyright
26
-
27
- return (
28
- <footer className={cn('grow flex flex-col justify-between gap-6 pb-[2vh]', className)}>
29
- <div className={
30
- (noHorizPadding ? '' : 'px-5 md:px-8 ') +
31
- 'grid grid-cols-2 gap-4 gap-y-6 md:gap-x-6 lg:gap-8 ' + smGridColsClx +
32
- 'md:w-full sm:justify-items-center md:mx-0 lg:w-full max-w-screen-2xl ' +
33
- 'md:flex md:flex-row md:justify-between '
34
- }>
35
- <div className='hidden lg:flex flex-col' key={0}>
36
- <Logo size='md' layout='text-only' />
37
- </div>
38
- {footer.map((defs: LinkDef[], index: number) => {
39
-
40
- const xsColSpanClx = ((index === footer.length - 1) && (footer.length % 2 === 1)) ?
41
- 'xs:col-span-2 xs:mx-auto md:col-span-1 md:mx-0 ' : ''
42
-
43
- return (
44
- <NavItems
45
- items={defs}
46
- currentAs={siteDef.currentAs}
47
- as='nav'
48
- className={cn('sm:min-w-[150px] md:min-w-0 flex flex-col justify-start items-start ' +
49
- 'gap-[11px] sm:gap-[12px] md:gap-[15px] ',
50
- xsColSpanClx
51
- )}
52
- key={index + 1}
53
- itemClx={(def: LinkDef) => ((def.variant === 'linkFG') ?
54
- 'font-nav text-[15px]/[1.3] font-medium text-foreground tracking-normal'
55
- :
56
- 'text-[15px]/[1.1] font-normal tracking-[0.2px] text-muted-1'
57
- )}
58
- />
59
- )
60
- })}
61
- </div>
62
- <div className='md:mt-[2vh]'>
63
- {_aboveCopyright.length > 0 && (
64
- <NavItems
65
- items={_aboveCopyright}
66
- as='div'
67
- className={'flex flex-row justify-center gap-4 mb-2'}
68
- itemClx={'text-sm text-center text-muted-2 underline hover:text-foreground'}
69
- />
70
- )}
71
- <Copyright className='text-sm text-center text-muted-3'/>
72
- </div>
73
- </footer>
74
- )
75
- }
76
-
77
- export default Footer
1
+ import React from 'react'
2
+
3
+ import type { LinkDef } from '@hanzo/ui/types'
4
+ import { NavItems } from '@hanzo/ui/primitives'
5
+ import { cn } from '@hanzo/ui/util'
6
+
7
+ import Copyright from './copyright'
8
+ import type { SiteDef } from '../site-def'
9
+ import { legal } from '../site-def/footer/legal'
10
+ import Logo from './logo'
11
+
12
+ const Footer: React.FC<{
13
+ siteDef: SiteDef,
14
+ className?: string,
15
+ noHorizPadding?: boolean
16
+ }> = ({
17
+ siteDef,
18
+ className='',
19
+ noHorizPadding=false
20
+ }) => {
21
+
22
+ const { footer, aboveCopyright } = siteDef
23
+ const smGridCols = Math.floor(footer.length/2)
24
+ const smGridColsClx = `sm:grid-cols-${smGridCols} `
25
+ const _aboveCopyright = (typeof aboveCopyright === 'undefined') ? legal : aboveCopyright
26
+
27
+ return (
28
+ <footer className={cn('grow flex flex-col justify-between gap-6 pb-[2vh]', className)}>
29
+ <div className={
30
+ (noHorizPadding ? '' : 'px-5 md:px-8 ') +
31
+ 'grid grid-cols-2 gap-4 gap-y-6 md:gap-x-6 lg:gap-8 ' + smGridColsClx +
32
+ 'md:w-full sm:justify-items-center md:mx-0 lg:w-full max-w-screen-2xl ' +
33
+ 'md:flex md:flex-row md:justify-between '
34
+ }>
35
+ <div className='hidden lg:flex flex-col' key={0}>
36
+ <Logo size='md' layout='text-only' />
37
+ </div>
38
+ {footer.map((defs: LinkDef[], index: number) => {
39
+
40
+ const xsColSpanClx = ((index === footer.length - 1) && (footer.length % 2 === 1)) ?
41
+ 'xs:col-span-2 xs:mx-auto md:col-span-1 md:mx-0 ' : ''
42
+
43
+ return (
44
+ <NavItems
45
+ items={defs}
46
+ currentAs={siteDef.currentAs}
47
+ as='nav'
48
+ className={cn('sm:min-w-[150px] md:min-w-0 flex flex-col justify-start items-start ' +
49
+ 'gap-[11px] sm:gap-[12px] md:gap-[15px] ',
50
+ xsColSpanClx
51
+ )}
52
+ key={index + 1}
53
+ itemClx={(def: LinkDef) => ((def.variant === 'linkFG') ?
54
+ 'font-nav text-[15px]/[1.3] font-medium text-foreground tracking-normal'
55
+ :
56
+ 'text-[15px]/[1.1] font-normal tracking-[0.2px] text-muted-1'
57
+ )}
58
+ />
59
+ )
60
+ })}
61
+ </div>
62
+ <div className='md:mt-[2vh]'>
63
+ {_aboveCopyright.length > 0 && (
64
+ <NavItems
65
+ items={_aboveCopyright}
66
+ as='div'
67
+ className={'flex flex-row justify-center gap-4 mb-2'}
68
+ itemClx={'text-sm text-center text-muted-2 underline hover:text-foreground'}
69
+ />
70
+ )}
71
+ <Copyright className='text-sm text-center text-muted-3'/>
72
+ </div>
73
+ </footer>
74
+ )
75
+ }
76
+
77
+ export default Footer
78
78
  // flex flex-col justify-between gap-6
@@ -1,54 +1,54 @@
1
- import React from 'react'
2
-
3
- import { NavItems } from '@hanzo/ui/primitives'
4
- import { cn } from '@hanzo/ui/util'
5
- import { AuthWidget } from '@hanzo/auth/components'
6
-
7
- import { Logo } from '..'
8
-
9
- import DesktopBagPopup from '../commerce/desktop-bag-popup'
10
- import BagButton from '../commerce/bag-button'
11
-
12
-
13
- import type { LinkDef } from '@hanzo/ui/types'
14
-
15
- const DesktopHeader: React.FC<{
16
- currentAs: string | undefined
17
- links: LinkDef[]
18
- className?: string
19
- }> = ({
20
- currentAs,
21
- links,
22
- className = ''
23
- }) => {
24
-
25
- // TODO move 13px into a size class and configure twMerge to recognize say, 'text-size-nav'
26
- // (vs be beat out by 'text-color-nav')
27
- return (
28
- <header className={cn('bg-background sticky z-header top-0 ', className)} >
29
- {/* md or larger */}
30
- <div className={
31
- 'flex flex-row h-[80px] items-center justify-between ' +
32
- 'px-[32px] w-full 2xl:mx-auto max-w-screen-2xl'
33
- }>
34
- <Logo size='md' href='/' className='hidden lg:flex' key='two' layout='text-only'/>
35
- <Logo size='sm' href='/' className='hidden md:flex lg:hidden' key='one' layout='text-only'/>
36
- {/* md or larger */}
37
- <div className='flex gap-4 items-center'>
38
- <NavItems
39
- currentAs={currentAs}
40
- items={links}
41
- className='flex md:gap-4 lg:justify-between lg:gap-7'
42
- itemClx='font-nav h-8'
43
- key='three'
44
- />
45
- <DesktopBagPopup popupClx='w-[340px]' trigger={<BagButton className='text-primary -mr-[3px] lg:min-w-0' />} />
46
- <AuthWidget/>
47
- </div>
48
- </div>
49
- </header>
50
- )
51
- }
52
-
53
- export default DesktopHeader
54
-
1
+ import React from 'react'
2
+
3
+ import { NavItems } from '@hanzo/ui/primitives'
4
+ import { cn } from '@hanzo/ui/util'
5
+ import { AuthWidget } from '@hanzo/auth/components'
6
+
7
+ import { Logo } from '..'
8
+
9
+ import DesktopBagPopup from '../commerce/desktop-bag-popup'
10
+ import BagButton from '../commerce/bag-button'
11
+
12
+
13
+ import type { LinkDef } from '@hanzo/ui/types'
14
+
15
+ const DesktopHeader: React.FC<{
16
+ currentAs: string | undefined
17
+ links: LinkDef[]
18
+ className?: string
19
+ }> = ({
20
+ currentAs,
21
+ links,
22
+ className = ''
23
+ }) => {
24
+
25
+ // TODO move 13px into a size class and configure twMerge to recognize say, 'text-size-nav'
26
+ // (vs be beat out by 'text-color-nav')
27
+ return (
28
+ <header className={cn('bg-background sticky z-header top-0 ', className)} >
29
+ {/* md or larger */}
30
+ <div className={
31
+ 'flex flex-row h-[80px] items-center justify-between ' +
32
+ 'px-[32px] w-full 2xl:mx-auto max-w-screen-2xl'
33
+ }>
34
+ <Logo size='md' href='/' className='hidden lg:flex' key='two' layout='text-only'/>
35
+ <Logo size='sm' href='/' className='hidden md:flex lg:hidden' key='one' layout='text-only'/>
36
+ {/* md or larger */}
37
+ <div className='flex gap-4 items-center'>
38
+ <NavItems
39
+ currentAs={currentAs}
40
+ items={links}
41
+ className='flex md:gap-4 lg:justify-between lg:gap-7'
42
+ itemClx='font-nav h-8'
43
+ key='three'
44
+ />
45
+ <DesktopBagPopup popupClx='w-[340px]' trigger={<BagButton className='text-primary -mr-[3px] lg:min-w-0' />} />
46
+ <AuthWidget/>
47
+ </div>
48
+ </div>
49
+ </header>
50
+ )
51
+ }
52
+
53
+ export default DesktopHeader
54
+
@@ -1,26 +1,26 @@
1
- import React from 'react'
2
-
3
- import type { SiteDef } from '../../site-def'
4
-
5
- import DesktopHeader from './desktop'
6
- import MobileHeader from './mobile'
7
- import { cn } from '@hanzo/ui/util'
8
-
9
- const Header: React.FC<{
10
- siteDef: SiteDef
11
- className?: string
12
- }> = ({
13
- siteDef,
14
- className = ''
15
- }) => {
16
-
17
- const { nav: { common, featured }, currentAs } = siteDef
18
- const links = (featured) ? [...common, ...featured] : common
19
-
20
- return (<>
21
- <DesktopHeader className={cn(className, 'hidden md:flex')} links={links} currentAs={currentAs} />
22
- <MobileHeader className={cn(className, 'md:hidden')} links={links} currentAs={currentAs} />
23
- </>)
24
- }
25
-
26
- export default Header
1
+ import React from 'react'
2
+
3
+ import type { SiteDef } from '../../site-def'
4
+
5
+ import DesktopHeader from './desktop'
6
+ import MobileHeader from './mobile'
7
+ import { cn } from '@hanzo/ui/util'
8
+
9
+ const Header: React.FC<{
10
+ siteDef: SiteDef
11
+ className?: string
12
+ }> = ({
13
+ siteDef,
14
+ className = ''
15
+ }) => {
16
+
17
+ const { nav: { common, featured }, currentAs } = siteDef
18
+ const links = (featured) ? [...common, ...featured] : common
19
+
20
+ return (<>
21
+ <DesktopHeader className={cn(className, 'hidden md:flex')} links={links} currentAs={currentAs} />
22
+ <MobileHeader className={cn(className, 'md:hidden')} links={links} currentAs={currentAs} />
23
+ </>)
24
+ }
25
+
26
+ export default Header
@@ -1,161 +1,161 @@
1
- 'use client'
2
- import React, { useState, useEffect } from 'react'
3
- import { useRouter } from 'next/navigation'
4
-
5
- import type { LinkDef } from '@hanzo/ui/types'
6
- import { cn } from '@hanzo/ui/util'
7
-
8
- import { CartPanel, useCommerce } from '@hanzo/commerce'
9
- import { AuthWidget, LoginPanel } from '@hanzo/auth/components'
10
-
11
- import sendGAEvent from '../../next/analytics/google-analytics'
12
- import * as Icons from '../icons'
13
-
14
- import { Logo } from '..'
15
-
16
- import MenuToggleButton from '../commerce/mobile-menu-toggle-button'
17
- import BagButton from '../commerce/bag-button'
18
- import MobileBagDrawer from '../commerce/mobile-bag-drawer'
19
- import NavMenu from '../commerce/mobile-nav-menu'
20
-
21
- const bagClx = 'mt-4 mb-8 border-none py-0 px-4 w-full ' +
22
- 'sm:min-w-[350px] sm:max-w-[500px] sm:mx-auto min-h-[60vh] max-h-[70vh] ' +
23
- 'sm:animate-in sm:zoom-in-90 '
24
-
25
- const MobileHeader: React.FC<{
26
- currentAs: string | undefined
27
- links: LinkDef[]
28
- className?: string
29
- }> = ({
30
- currentAs,
31
- links,
32
- className = ''
33
- }) => {
34
-
35
- const cmmc = useCommerce()
36
- const [menuState, setMenuState] = useState<'closed' | 'nav' | 'login' | 'bag'>('closed')
37
- const [bagDrawerOpen, setBagDrawerOpen] = useState<boolean>(false)
38
- const router = useRouter()
39
-
40
- useEffect(() => {
41
- if (menuState === 'bag' || bagDrawerOpen) {
42
- sendGAEvent('view_cart', {
43
- items: cmmc.cartItems.map((item) => ({
44
- item_id: item.sku,
45
- item_name: item.title,
46
- item_category: item.familyId,
47
- price: item.price,
48
- quantity: item.quantity
49
- })),
50
- value: cmmc.cartTotal,
51
- currency: 'USD',
52
- })
53
- }
54
- }, [menuState, bagDrawerOpen])
55
-
56
- const menuOpen = () => (menuState !== 'closed')
57
-
58
- const onLoginChanged = (token: string) => {
59
- // by def, menu was in state 'login'
60
- if (!!token) { setMenuState('nav') }
61
- }
62
-
63
- const setMenuOpen = (open: boolean) => {
64
- if (!open) {
65
- setMenuState('closed')
66
- }
67
- else {
68
- setMenuState('nav')
69
- }
70
- }
71
-
72
- const handleCheckout = () => {
73
- setMenuState('closed')
74
- setBagDrawerOpen(false)
75
- router.push('/checkout')
76
- }
77
-
78
- const openBag = () => {
79
- if (menuOpen()) {
80
- setMenuState('bag')
81
- }
82
- else {
83
- setBagDrawerOpen(true)
84
- }
85
- }
86
- // header element MUST be fixed, and NOT sticky. Or else drawer breaks on mobile browsers
87
- return (<>
88
- <header className={cn(
89
- 'bg-background fixed z-header top-0 left-0 w-full',
90
- className
91
- )}>
92
- {/* smaller than md: mobile style drawer menu; h-11 is 44px, the standard mobile header height */}
93
- <div className="flex h-11 items-center justify-between pl-6 pr-4">
94
- <div className='relative h-full w-200 flex flex-row'>
95
- <Logo href='/' size='sm' className={'top-[3px] h-full'} layout='text-only'/>
96
- {/* Not that key to the cross-fade effect
97
- is that this is **on top of** the logo. */}
98
- {menuOpen() && (
99
- <div className={'absolute left-0 top-0 bottom-0 right-0 ' +
100
- 'flex flex-col justify-center ' +
101
- 'bg-background animate-mobile-menu-open'
102
- }>
103
- <AuthWidget className='' handleLogin={() => {setMenuState('login')}}/>
104
- </div>
105
- )}
106
- </div>
107
- <div className='flex gap-0 '>
108
- <BagButton className='text-primary -mr-[3px]' onClick={openBag}/>
109
- <MenuToggleButton className='text-foreground' open={menuOpen()} setOpen={setMenuOpen} />
110
- </div>
111
- </div>
112
- </header>
113
- <MobileBagDrawer
114
- className=''
115
- open={bagDrawerOpen}
116
- setOpen={setBagDrawerOpen}
117
- handleCheckout={handleCheckout}
118
- />
119
- {menuOpen() && (
120
- <div className={
121
- 'fixed top-0 left-0 w-full h-full ' +
122
- // z must below header itself
123
- 'flex flex-column p-6 pt-15 bg-background z-below-header animate-mobile-menu-open'
124
- }>
125
- {menuState === 'login' ? (
126
- <LoginPanel noHeading onLoginChanged={onLoginChanged} className='sm:animate-in sm:zoom-in-90' />
127
- ) : (
128
- menuState === 'bag' ? (
129
-
130
- <CartPanel
131
- handleCheckout={() => {router.push('/checkout')}}
132
- className={bagClx}
133
- listClx='rounded-sm'
134
- scrollAfter={6}
135
- scrollHeightClx='h-[80vh]'
136
- itemClx='mt-2'
137
- totalClx='sticky px-1 pr-2 border rounded-sm -bottom-[1px] bg-level-1'
138
- buttonClx='max-w-[220px] flex-none'
139
- >
140
- <div className='flex flex-row items-center flex-none justify-center '>
141
- <Icons.bag className='mr-2 relative w-4 h-5 fill-foreground ' />
142
- <p className='font-heading text-foreground text-default'>Your Bag</p>
143
- </div>
144
- <div className='h-[1px] w-pr-80 bg-muted-3 mx-auto mt-1.5 flex-none'/>
145
- </CartPanel>
146
-
147
- ) : ( /* menuState === 'nav' */
148
- <NavMenu
149
- currentAs={currentAs}
150
- links={links}
151
- className='sm:animate-in sm:zoom-in-90'
152
- commonItemClx='px-0 text-xl h-16 justify-start '
153
- />
154
- )
155
- )}
156
- </div>
157
- ) /* menuOpen */}
158
- </>)
159
- }
160
-
161
- export default MobileHeader
1
+ 'use client'
2
+ import React, { useState, useEffect } from 'react'
3
+ import { useRouter } from 'next/navigation'
4
+
5
+ import type { LinkDef } from '@hanzo/ui/types'
6
+ import { cn } from '@hanzo/ui/util'
7
+
8
+ import { CartPanel, useCommerce } from '@hanzo/commerce'
9
+ import { AuthWidget, LoginPanel } from '@hanzo/auth/components'
10
+
11
+ import sendGAEvent from '../../next/analytics/google-analytics'
12
+ import * as Icons from '../icons'
13
+
14
+ import { Logo } from '..'
15
+
16
+ import MenuToggleButton from '../commerce/mobile-menu-toggle-button'
17
+ import BagButton from '../commerce/bag-button'
18
+ import MobileBagDrawer from '../commerce/mobile-bag-drawer'
19
+ import NavMenu from '../commerce/mobile-nav-menu'
20
+
21
+ const bagClx = 'mt-4 mb-8 border-none py-0 px-4 w-full ' +
22
+ 'sm:min-w-[350px] sm:max-w-[500px] sm:mx-auto min-h-[60vh] max-h-[70vh] ' +
23
+ 'sm:animate-in sm:zoom-in-90 '
24
+
25
+ const MobileHeader: React.FC<{
26
+ currentAs: string | undefined
27
+ links: LinkDef[]
28
+ className?: string
29
+ }> = ({
30
+ currentAs,
31
+ links,
32
+ className = ''
33
+ }) => {
34
+
35
+ const cmmc = useCommerce()
36
+ const [menuState, setMenuState] = useState<'closed' | 'nav' | 'login' | 'bag'>('closed')
37
+ const [bagDrawerOpen, setBagDrawerOpen] = useState<boolean>(false)
38
+ const router = useRouter()
39
+
40
+ useEffect(() => {
41
+ if (menuState === 'bag' || bagDrawerOpen) {
42
+ sendGAEvent('view_cart', {
43
+ items: cmmc.cartItems.map((item) => ({
44
+ item_id: item.sku,
45
+ item_name: item.title,
46
+ item_category: item.familyId,
47
+ price: item.price,
48
+ quantity: item.quantity
49
+ })),
50
+ value: cmmc.cartTotal,
51
+ currency: 'USD',
52
+ })
53
+ }
54
+ }, [menuState, bagDrawerOpen])
55
+
56
+ const menuOpen = () => (menuState !== 'closed')
57
+
58
+ const onLoginChanged = (token: string) => {
59
+ // by def, menu was in state 'login'
60
+ if (!!token) { setMenuState('nav') }
61
+ }
62
+
63
+ const setMenuOpen = (open: boolean) => {
64
+ if (!open) {
65
+ setMenuState('closed')
66
+ }
67
+ else {
68
+ setMenuState('nav')
69
+ }
70
+ }
71
+
72
+ const handleCheckout = () => {
73
+ setMenuState('closed')
74
+ setBagDrawerOpen(false)
75
+ router.push('/checkout')
76
+ }
77
+
78
+ const openBag = () => {
79
+ if (menuOpen()) {
80
+ setMenuState('bag')
81
+ }
82
+ else {
83
+ setBagDrawerOpen(true)
84
+ }
85
+ }
86
+ // header element MUST be fixed, and NOT sticky. Or else drawer breaks on mobile browsers
87
+ return (<>
88
+ <header className={cn(
89
+ 'bg-background fixed z-header top-0 left-0 w-full',
90
+ className
91
+ )}>
92
+ {/* smaller than md: mobile style drawer menu; h-11 is 44px, the standard mobile header height */}
93
+ <div className="flex h-11 items-center justify-between pl-6 pr-4">
94
+ <div className='relative h-full w-200 flex flex-row'>
95
+ <Logo href='/' size='sm' className={'top-[3px] h-full'} layout='text-only'/>
96
+ {/* Not that key to the cross-fade effect
97
+ is that this is **on top of** the logo. */}
98
+ {menuOpen() && (
99
+ <div className={'absolute left-0 top-0 bottom-0 right-0 ' +
100
+ 'flex flex-col justify-center ' +
101
+ 'bg-background animate-mobile-menu-open'
102
+ }>
103
+ <AuthWidget className='' handleLogin={() => {setMenuState('login')}}/>
104
+ </div>
105
+ )}
106
+ </div>
107
+ <div className='flex gap-0 '>
108
+ <BagButton className='text-primary -mr-[3px]' onClick={openBag}/>
109
+ <MenuToggleButton className='text-foreground' open={menuOpen()} setOpen={setMenuOpen} />
110
+ </div>
111
+ </div>
112
+ </header>
113
+ <MobileBagDrawer
114
+ className=''
115
+ open={bagDrawerOpen}
116
+ setOpen={setBagDrawerOpen}
117
+ handleCheckout={handleCheckout}
118
+ />
119
+ {menuOpen() && (
120
+ <div className={
121
+ 'fixed top-0 left-0 w-full h-full ' +
122
+ // z must below header itself
123
+ 'flex flex-column p-6 pt-15 bg-background z-below-header animate-mobile-menu-open'
124
+ }>
125
+ {menuState === 'login' ? (
126
+ <LoginPanel noHeading onLoginChanged={onLoginChanged} className='sm:animate-in sm:zoom-in-90' />
127
+ ) : (
128
+ menuState === 'bag' ? (
129
+
130
+ <CartPanel
131
+ handleCheckout={() => {router.push('/checkout')}}
132
+ className={bagClx}
133
+ listClx='rounded-sm'
134
+ scrollAfter={6}
135
+ scrollHeightClx='h-[80vh]'
136
+ itemClx='mt-2'
137
+ totalClx='sticky px-1 pr-2 border rounded-sm -bottom-[1px] bg-level-1'
138
+ buttonClx='max-w-[220px] flex-none'
139
+ >
140
+ <div className='flex flex-row items-center flex-none justify-center '>
141
+ <Icons.bag className='mr-2 relative w-4 h-5 fill-foreground ' />
142
+ <p className='font-heading text-foreground text-default'>Your Bag</p>
143
+ </div>
144
+ <div className='h-[1px] w-pr-80 bg-muted-3 mx-auto mt-1.5 flex-none'/>
145
+ </CartPanel>
146
+
147
+ ) : ( /* menuState === 'nav' */
148
+ <NavMenu
149
+ currentAs={currentAs}
150
+ links={links}
151
+ className='sm:animate-in sm:zoom-in-90'
152
+ commonItemClx='px-0 text-xl h-16 justify-start '
153
+ />
154
+ )
155
+ )}
156
+ </div>
157
+ ) /* menuOpen */}
158
+ </>)
159
+ }
160
+
161
+ export default MobileHeader