@luxfi/core 4.4.10 → 4.4.12

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 +23 -41
  3. package/components/auth/auth-token/clear-auth-token.tsx +12 -0
  4. package/components/auth/auth-token/set-auth-token.tsx +16 -0
  5. package/components/auth/common-auth-domains.ts +16 -15
  6. package/components/auth/login-panel.tsx +119 -117
  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,112 +1,112 @@
1
- 'use client'
2
-
3
- import React, { useTransition } from 'react'
4
-
5
- import { zodResolver } from '@hookform/resolvers/zod'
6
- import { useForm, type SubmitHandler, type ControllerRenderProps } from 'react-hook-form'
7
- import * as z from 'zod'
8
- // @ts-ignore
9
- import validator from 'validator'
10
-
11
- import { Loader2 } from 'lucide-react'
12
-
13
- import {
14
- Button,
15
- Input,
16
- Form,
17
- FormControl,
18
- FormField,
19
- FormItem,
20
- FormMessage,
21
- } from '@hanzo/ui/primitives'
22
-
23
- import type { SubmitServerAction } from '@hanzo/ui/types'
24
- import type { ContactInfo } from '../../types'
25
-
26
- const ValidationSchema = z.object({
27
- email: z
28
- .string()
29
- .min(1, { message: "Email must be provided." })
30
- .email("Invalid email."),
31
- phone: z
32
- .string()
33
- .min(1, { message: "Telephone must be provided." })
34
- .refine(validator.isMobilePhone, { message: "Invalid format." })
35
- })
36
-
37
- const ContactForm: React.FC<{
38
- onSubmit: SubmitServerAction
39
- enclosure: any
40
- }> = ({
41
- onSubmit,
42
- enclosure
43
- }) => {
44
-
45
- const form = useForm<ContactInfo>({
46
- // @ts-ignore (pnpm linking / tsc bug )
47
- resolver: zodResolver(ValidationSchema),
48
- defaultValues: {
49
- email: '',
50
- phone: '',
51
- },
52
- })
53
-
54
- const [isPending, startTransition] = useTransition()
55
-
56
- const onFormSubmit: SubmitHandler<ContactInfo> = (data) => {
57
- // https://github.com/orgs/react-hook-form/discussions/10757#discussioncomment-6672403
58
- // @ts-ignore
59
- startTransition(async () => {
60
- await onSubmit(data, enclosure)
61
- })
62
- }
63
-
64
- const MyFormItem: React.FC<{
65
- field: ControllerRenderProps<ContactInfo, 'email'> | ControllerRenderProps<ContactInfo, 'phone'>
66
- placeholder: string
67
- }> = ({
68
- field,
69
- placeholder
70
- }) => (
71
- <FormItem className="space-y-0" >
72
- <FormControl>
73
- <Input placeholder={placeholder} {...field} className="mt-0 text-foreground"/>
74
- </FormControl>
75
- <div className="flex flex-row justify-start items-stretch gap-2">
76
- <FormMessage />
77
- </div>
78
- </FormItem>
79
- )
80
-
81
- return (
82
- <Form {...form}>
83
- <form onSubmit={form.handleSubmit(onFormSubmit)} className="w-3/4">
84
- <div className='flex flex-col justify-start items-stretch mt-4'>
85
- <FormField
86
- control={form.control}
87
- name='email'
88
- // @ts-ignore
89
- render={({ field }) => ( <MyFormItem field={field} placeholder='email'/> )}
90
- />
91
- <FormField
92
- control={form.control}
93
- name='phone'
94
- // @ts-ignore
95
- render={({ field }) => ( <MyFormItem field={field} placeholder='phone'/> )}
96
- />
97
- <Button disabled={isPending} type='submit' className='bg-primary text-primary-fg hover:bg-primary-hover'>
98
- {isPending ? (<>
99
- <Loader2 className="mr-2 h-4 w-4 animate-spin" />
100
- Please wait
101
- </>
102
- ) : (
103
- <>Submit</>
104
- )}
105
- </Button>
106
- </div>
107
- </form>
108
- </Form>
109
- )
110
- }
111
-
112
- export default ContactForm
1
+ 'use client'
2
+
3
+ import React, { useTransition } from 'react'
4
+
5
+ import { zodResolver } from '@hookform/resolvers/zod'
6
+ import { useForm, type SubmitHandler, type ControllerRenderProps } from 'react-hook-form'
7
+ import * as z from 'zod'
8
+ // @ts-ignore
9
+ import validator from 'validator'
10
+
11
+ import { Loader2 } from 'lucide-react'
12
+
13
+ import {
14
+ Button,
15
+ Input,
16
+ Form,
17
+ FormControl,
18
+ FormField,
19
+ FormItem,
20
+ FormMessage,
21
+ } from '@hanzo/ui/primitives'
22
+
23
+ import type { SubmitServerAction } from '@hanzo/ui/types'
24
+ import type { ContactInfo } from '../../types'
25
+
26
+ const ValidationSchema = z.object({
27
+ email: z
28
+ .string()
29
+ .min(1, { message: "Email must be provided." })
30
+ .email("Invalid email."),
31
+ phone: z
32
+ .string()
33
+ .min(1, { message: "Telephone must be provided." })
34
+ .refine(validator.isMobilePhone, { message: "Invalid format." })
35
+ })
36
+
37
+ const ContactForm: React.FC<{
38
+ onSubmit: SubmitServerAction
39
+ enclosure: any
40
+ }> = ({
41
+ onSubmit,
42
+ enclosure
43
+ }) => {
44
+
45
+ const form = useForm<ContactInfo>({
46
+ // @ts-ignore (pnpm linking / tsc bug )
47
+ resolver: zodResolver(ValidationSchema),
48
+ defaultValues: {
49
+ email: '',
50
+ phone: '',
51
+ },
52
+ })
53
+
54
+ const [isPending, startTransition] = useTransition()
55
+
56
+ const onFormSubmit: SubmitHandler<ContactInfo> = (data) => {
57
+ // https://github.com/orgs/react-hook-form/discussions/10757#discussioncomment-6672403
58
+ // @ts-ignore
59
+ startTransition(async () => {
60
+ await onSubmit(data, enclosure)
61
+ })
62
+ }
63
+
64
+ const MyFormItem: React.FC<{
65
+ field: ControllerRenderProps<ContactInfo, 'email'> | ControllerRenderProps<ContactInfo, 'phone'>
66
+ placeholder: string
67
+ }> = ({
68
+ field,
69
+ placeholder
70
+ }) => (
71
+ <FormItem className="space-y-0" >
72
+ <FormControl>
73
+ <Input placeholder={placeholder} {...field} className="mt-0 text-foreground"/>
74
+ </FormControl>
75
+ <div className="flex flex-row justify-start items-stretch gap-2">
76
+ <FormMessage />
77
+ </div>
78
+ </FormItem>
79
+ )
80
+
81
+ return (
82
+ <Form {...form}>
83
+ <form onSubmit={form.handleSubmit(onFormSubmit)} className="w-3/4">
84
+ <div className='flex flex-col justify-start items-stretch mt-4'>
85
+ <FormField
86
+ control={form.control}
87
+ name='email'
88
+ // @ts-ignore
89
+ render={({ field }) => ( <MyFormItem field={field} placeholder='email'/> )}
90
+ />
91
+ <FormField
92
+ control={form.control}
93
+ name='phone'
94
+ // @ts-ignore
95
+ render={({ field }) => ( <MyFormItem field={field} placeholder='phone'/> )}
96
+ />
97
+ <Button disabled={isPending} type='submit' className='bg-primary text-primary-fg hover:bg-primary-hover'>
98
+ {isPending ? (<>
99
+ <Loader2 className="mr-2 h-4 w-4 animate-spin" />
100
+ Please wait
101
+ </>
102
+ ) : (
103
+ <>Submit</>
104
+ )}
105
+ </Button>
106
+ </div>
107
+ </form>
108
+ </Form>
109
+ )
110
+ }
111
+
112
+ export default ContactForm
@@ -1,13 +1,13 @@
1
- import React from 'react'
2
-
3
- const Disclaimer: React.FC = () => (
4
- <div>
5
- By entering your mobile number and submitting, you consent to receive text messages from Lux at the number provided.
6
- Message and data rates may apply. Message frequency varies.
7
- You can unsubscribe at any time by replying STOP.
8
- View our <a href='/privacy'>Privacy Policy</a> and <a href='/terms'>Terms & conditions</a>.
9
- </div>
10
- )
11
-
12
- export default Disclaimer
13
-
1
+ import React from 'react'
2
+
3
+ const Disclaimer: React.FC = () => (
4
+ <div>
5
+ By entering your mobile number and submitting, you consent to receive text messages from Lux at the number provided.
6
+ Message and data rates may apply. Message frequency varies.
7
+ You can unsubscribe at any time by replying STOP.
8
+ View our <a href='/privacy'>Privacy Policy</a> and <a href='/terms'>Terms & conditions</a>.
9
+ </div>
10
+ )
11
+
12
+ export default Disclaimer
13
+
@@ -1,64 +1,64 @@
1
- 'use client'
2
-
3
- import React, { useState } from 'react'
4
-
5
- import type { ButtonModalProps} from '@hanzo/ui/types'
6
-
7
- import {
8
- Button,
9
- Dialog,
10
- DialogContent,
11
- DialogDescription,
12
- DialogHeader,
13
- DialogTitle,
14
- DialogTrigger,
15
- } from '@hanzo/ui/primitives'
16
-
17
- import ContactForm from './contact-form'
18
- import Disclaimer from './disclaimer'
19
-
20
- const ContactDialog: React.FC<ButtonModalProps> = ({
21
- open,
22
- onOpenChange,
23
- buttonText,
24
- buttonProps,
25
- title,
26
- byline,
27
- action,
28
- actionEnclosure
29
- }) => {
30
- const [success, setSuccess] = useState(false)
31
-
32
- const onSubmit = async (formData: any, enclosure: any) => {
33
- const result = await action(formData, enclosure)
34
- if (result.success) {
35
- setSuccess(true)
36
- }
37
- }
38
-
39
- return (
40
- <Dialog open={open} onOpenChange={onOpenChange} >
41
- <DialogTrigger asChild>
42
- <Button {...buttonProps} >{buttonText}</Button>
43
- </DialogTrigger>
44
- <DialogContent className="sm:max-w-[500px] p-0 gap-0 bg-background border">
45
- <DialogHeader className='py-6 text-foreground'>
46
- <DialogTitle className='text-4xl text-center text-inherit'>{title}</DialogTitle>
47
- {byline && (<DialogDescription className='text-inherit text-lg text-center'>{byline} </DialogDescription>)}
48
- </DialogHeader>
49
- <div className='px-8 pb-8 rounded-e-lg flex flex-col justify-start items-center'>
50
- {success ? (
51
- <p>{actionEnclosure?.reply}</p>
52
- ) : (
53
- <ContactForm onSubmit={onSubmit} enclosure={actionEnclosure}/>
54
- )}
55
- <div className='text-muted-1 text-xs mt-4' >
56
- <Disclaimer />
57
- </div>
58
- </div>
59
- </DialogContent>
60
- </Dialog>
61
- )
62
- }
63
-
64
- export default ContactDialog
1
+ 'use client'
2
+
3
+ import React, { useState } from 'react'
4
+
5
+ import type { ButtonModalProps} from '@hanzo/ui/types'
6
+
7
+ import {
8
+ Button,
9
+ Dialog,
10
+ DialogContent,
11
+ DialogDescription,
12
+ DialogHeader,
13
+ DialogTitle,
14
+ DialogTrigger,
15
+ } from '@hanzo/ui/primitives'
16
+
17
+ import ContactForm from './contact-form'
18
+ import Disclaimer from './disclaimer'
19
+
20
+ const ContactDialog: React.FC<ButtonModalProps> = ({
21
+ open,
22
+ onOpenChange,
23
+ buttonText,
24
+ buttonProps,
25
+ title,
26
+ byline,
27
+ action,
28
+ actionEnclosure
29
+ }) => {
30
+ const [success, setSuccess] = useState(false)
31
+
32
+ const onSubmit = async (formData: any, enclosure: any) => {
33
+ const result = await action(formData, enclosure)
34
+ if (result.success) {
35
+ setSuccess(true)
36
+ }
37
+ }
38
+
39
+ return (
40
+ <Dialog open={open} onOpenChange={onOpenChange} >
41
+ <DialogTrigger asChild>
42
+ <Button {...buttonProps} >{buttonText}</Button>
43
+ </DialogTrigger>
44
+ <DialogContent className="sm:max-w-[500px] p-0 gap-0 bg-background border">
45
+ <DialogHeader className='py-6 text-foreground'>
46
+ <DialogTitle className='text-4xl text-center text-inherit'>{title}</DialogTitle>
47
+ {byline && (<DialogDescription className='text-inherit text-lg text-center'>{byline} </DialogDescription>)}
48
+ </DialogHeader>
49
+ <div className='px-8 pb-8 rounded-e-lg flex flex-col justify-start items-center'>
50
+ {success ? (
51
+ <p>{actionEnclosure?.reply}</p>
52
+ ) : (
53
+ <ContactForm onSubmit={onSubmit} enclosure={actionEnclosure}/>
54
+ )}
55
+ <div className='text-muted-1 text-xs mt-4' >
56
+ <Disclaimer />
57
+ </div>
58
+ </div>
59
+ </DialogContent>
60
+ </Dialog>
61
+ )
62
+ }
63
+
64
+ export default ContactDialog
@@ -1,21 +1,21 @@
1
- import React from 'react'
2
-
3
- const FIRST = 2020
4
-
5
- const Copyright: React.FC<{
6
- className?: string
7
- }> = ({
8
- className=''
9
- }) => {
10
-
11
- const year = new Date().getFullYear()
12
- const yearString = (year > FIRST) ? `${FIRST} - ${year}` : FIRST.toString()
13
-
14
- return (
15
- <div className={className}>
16
- {`Copyright © ${yearString}`}&nbsp;<br className='sm:hidden'/>Lux Partners Ltd.&nbsp;<br className='md:hidden'/>&nbsp;All rights reserved.
17
- </div>
18
- )
19
- }
20
-
21
- export default Copyright
1
+ import React from 'react'
2
+
3
+ const FIRST = 2020
4
+
5
+ const Copyright: React.FC<{
6
+ className?: string
7
+ }> = ({
8
+ className=''
9
+ }) => {
10
+
11
+ const year = new Date().getFullYear()
12
+ const yearString = (year > FIRST) ? `${FIRST} - ${year}` : FIRST.toString()
13
+
14
+ return (
15
+ <div className={className}>
16
+ {`Copyright © ${yearString}`}&nbsp;<br className='sm:hidden'/>Lux Partners Ltd.&nbsp;<br className='md:hidden'/>&nbsp;All rights reserved.
17
+ </div>
18
+ )
19
+ }
20
+
21
+ export default Copyright
@@ -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