@luxfi/core 4.3.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. package/components/access-code-input.tsx +71 -0
  2. package/components/auth/login-panel.tsx +80 -0
  3. package/components/chat-widget.tsx +77 -0
  4. package/components/commerce/bag-button.tsx +67 -0
  5. package/components/commerce/checkout-panel/close-button.tsx +23 -0
  6. package/components/commerce/checkout-panel/dt-bag-carousel.tsx +36 -0
  7. package/components/commerce/checkout-panel/dt-checkout-panel.tsx +68 -0
  8. package/components/commerce/checkout-panel/index.tsx +124 -0
  9. package/components/commerce/checkout-panel/links-row.tsx +21 -0
  10. package/components/commerce/checkout-panel/mb-checkout-panel.tsx +51 -0
  11. package/components/commerce/checkout-panel/steps-indicator.tsx +39 -0
  12. package/components/commerce/checkout-panel/thank-you.tsx +18 -0
  13. package/components/commerce/desktop-bag-popup.tsx +78 -0
  14. package/components/commerce/mobile-bag-drawer.tsx +51 -0
  15. package/components/commerce/mobile-menu-toggle-button.tsx +35 -0
  16. package/components/commerce/mobile-nav-menu.tsx +64 -0
  17. package/components/contact-dialog/contact-form.tsx +112 -0
  18. package/components/contact-dialog/disclaimer.tsx +13 -0
  19. package/components/contact-dialog/index.tsx +64 -0
  20. package/components/copyright.tsx +21 -0
  21. package/components/footer.tsx +78 -0
  22. package/components/header/desktop.tsx +54 -0
  23. package/components/header/index.tsx +26 -0
  24. package/components/header/mobile.tsx +161 -0
  25. package/components/header/theme-toggle.tsx +26 -0
  26. package/components/icons/bag-icon.tsx +10 -0
  27. package/components/icons/github.tsx +14 -0
  28. package/components/icons/index.tsx +35 -0
  29. package/components/icons/lux-logo.tsx +10 -0
  30. package/components/icons/secure-delivery.tsx +13 -0
  31. package/components/icons/social-icon.tsx +35 -0
  32. package/components/icons/social-svg.css +3 -0
  33. package/components/icons/youtube-logo.tsx +59 -0
  34. package/components/index.ts +26 -0
  35. package/components/logo.tsx +81 -0
  36. package/components/mini-chart/index.tsx +8 -0
  37. package/components/mini-chart/mini-chart-props.ts +44 -0
  38. package/components/mini-chart/mini-chart.tsx +85 -0
  39. package/components/mini-chart/wrapper.tsx +23 -0
  40. package/components/not-found/index.tsx +27 -0
  41. package/components/not-found/not-found-content.mdx +5 -0
  42. package/components/root-layout.tsx +71 -0
  43. package/components/scripts.tsx +23 -0
  44. package/conf/index.ts +50 -0
  45. package/environment.d.ts +6 -0
  46. package/next/analytics/fpixel.ts +16 -0
  47. package/next/analytics/google-analytics.ts +14 -0
  48. package/next/analytics/index.ts +3 -0
  49. package/next/analytics/pixel-analytics.tsx +55 -0
  50. package/next/determine-device-mw.ts +16 -0
  51. package/next/font/get-app-router-font-classes.ts +12 -0
  52. package/next/font/load-and-return-lux-next-fonts-on-import.ts +67 -0
  53. package/next/font/local/Druk-Wide-Bold.ttf +0 -0
  54. package/next/font/local/Druk-Wide-Medium.ttf +0 -0
  55. package/next/font/next-font-desc.ts +28 -0
  56. package/next/font/pages-router-font-vars.tsx +18 -0
  57. package/next/head-metadata/from-next/metadata-types.ts +158 -0
  58. package/next/head-metadata/from-next/opengraph-types.ts +267 -0
  59. package/next/head-metadata/from-next/twitter-types.ts +92 -0
  60. package/next/head-metadata/index.tsx +208 -0
  61. package/next/index.ts +1 -0
  62. package/package.json +72 -0
  63. package/server-actions/firebase-app.ts +14 -0
  64. package/server-actions/index.ts +5 -0
  65. package/server-actions/store-contact.ts +51 -0
  66. package/site-def/footer/community.tsx +67 -0
  67. package/site-def/footer/company.ts +37 -0
  68. package/site-def/footer/ecosystem.ts +37 -0
  69. package/site-def/footer/index.tsx +26 -0
  70. package/site-def/footer/legal.ts +28 -0
  71. package/site-def/footer/network.ts +45 -0
  72. package/site-def/footer/svg/warpcast-logo.svg +12 -0
  73. package/site-def/index.ts +3 -0
  74. package/site-def/main-nav.ts +35 -0
  75. package/site-def/site-def.ts +37 -0
  76. package/style/lux-colors.css +85 -0
  77. package/style/lux-global.css +19 -0
  78. package/tailwind/fontFamily.tailwind.lux.ts +18 -0
  79. package/tailwind/index.ts +2 -0
  80. package/tailwind/lux-tw-fonts.ts +40 -0
  81. package/tailwind/tailwind.config.lux-preset.ts +10 -0
  82. package/tsconfig.json +10 -0
  83. package/types/contact-info.ts +11 -0
  84. package/types/index.ts +1 -0
@@ -0,0 +1,5 @@
1
+ ### ...Huh?
2
+ #### Sorry, we're fresh out of those.
3
+ <br/>
4
+ #### ...try something from the [Home Page](/)?
5
+
@@ -0,0 +1,71 @@
1
+ import React, { type PropsWithChildren } from 'react'
2
+
3
+ import type { Viewport } from 'next'
4
+
5
+ import { Toaster } from '@hanzo/ui/primitives'
6
+
7
+ import Header from './header'
8
+ import type SiteDef from '../site-def/site-def'
9
+ import getAppRouterBodyFontClasses from '../next/font/get-app-router-font-classes'
10
+ import { FacebookPixelHead } from '../next/analytics/pixel-analytics'
11
+ import Scripts from './scripts'
12
+
13
+ // Next 14: https://nextjs.org/docs/app/building-your-application/upgrading/codemods#use-viewport-export
14
+ const viewport = {
15
+ themeColor: [
16
+ { media: '(prefers-color-scheme: light)', color: 'white' },
17
+ { media: '(prefers-color-scheme: dark)', color: 'black' },
18
+ ],
19
+ } satisfies Viewport
20
+
21
+ /*
22
+ These '.variable' fields are actually autogenerate css classnames that *define* the actual
23
+ css variables ('--<ugly-name>') that one asks for in the tailwind classes.
24
+ They are what make them available in the global scope. So this MUST
25
+ be done like this for the tailwind font classes to work.
26
+
27
+ (...not to be confused with the css var itself. This field should be named something else!)
28
+ */
29
+
30
+ /*
31
+ re body: overflow-y-hidden overflow-x-hidden, h-full
32
+ We cannot have these on body tag for scroll-snap to work on iOS!
33
+ */
34
+ const bodyClasses =
35
+ 'bg-background text-foreground flex flex-col min-h-full' +
36
+ getAppRouterBodyFontClasses()
37
+
38
+ const RootLayout: React.FC<PropsWithChildren & {
39
+ siteDef: SiteDef
40
+ header?: boolean
41
+ }> = ({
42
+ header = true,
43
+ siteDef,
44
+ children,
45
+ }) => (
46
+ <html lang='en' suppressHydrationWarning className='hanzo-ui-dark-theme' style={{backgroundColor: '#000'}}>
47
+ <head >
48
+ {/* https://stackoverflow.com/a/75716588/11645689 */ }
49
+ <base target='_blank' />
50
+ <FacebookPixelHead/>
51
+ </head>
52
+ <body className={bodyClasses} style={{
53
+ // Not sure why these got added (by my check in)
54
+ // overflow: hidden here breaks scroll snap, see comment above
55
+ //paddingRight: '0 !important',
56
+ //maxWidth: '100vw',
57
+ display: 'none', // see scripts.tsx
58
+
59
+ }}>
60
+ <Scripts/>
61
+ {header && <Header siteDef={siteDef}/>}
62
+ {children}
63
+ <Toaster position='top-center' duration={3000}/>
64
+ </body>
65
+ </html>
66
+ )
67
+
68
+ export {
69
+ RootLayout as default,
70
+ viewport
71
+ }
@@ -0,0 +1,23 @@
1
+ 'use client'
2
+
3
+ import { useEffect } from 'react'
4
+ import { GoogleAnalytics } from '@next/third-parties/google'
5
+ import { FacebookPixel } from '../next/analytics'
6
+
7
+ const Scripts = () => {
8
+
9
+ useEffect(() => {
10
+ document.body.style.display = 'block'
11
+ }, [])
12
+
13
+ return (
14
+ <>
15
+ <FacebookPixel />
16
+ <GoogleAnalytics gaId={process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID ?? ''} />
17
+ </>
18
+ )
19
+ }
20
+
21
+ export {
22
+ Scripts as default
23
+ }
package/conf/index.ts ADDED
@@ -0,0 +1,50 @@
1
+ import type { SelectionUISpecifier } from '@hanzo/commerce/types'
2
+
3
+ export const selectionUISpecifiers = {
4
+
5
+ 'LXM-CN': {
6
+ singleFamily: {
7
+ type: 'carousel',
8
+ options: {
9
+ familyTitle: 'long',
10
+ showByline: false,
11
+ showPrice: true,
12
+ }
13
+ }
14
+ },
15
+ 'LXM-FC': {
16
+ singleFamily: {
17
+ type: 'carousel',
18
+ options: {
19
+ //showQuantity: true
20
+ }
21
+ }
22
+ },
23
+ 'LXM-CR': {
24
+ multiFamily: {
25
+ type: 'all-variants-carousel',
26
+ selectorOptions: {
27
+ showParentTitle: false,
28
+ showItemSwatches: true
29
+ },
30
+ itemOptions: {
31
+ familyTitle: 'long',
32
+ showFamilyByline: true,
33
+ showPrice: false,
34
+ }
35
+ }
36
+ },
37
+ 'LXM-AG': {
38
+ multiFamily: {
39
+ type: 'all-variants-carousel',
40
+ selectorOptions: {
41
+ showParentTitle: true,
42
+ showItemSwatches: false
43
+ },
44
+ itemOptions: {
45
+ familyTitle: 'long',
46
+ }
47
+ }
48
+ },
49
+
50
+ } satisfies Record<string, SelectionUISpecifier>
@@ -0,0 +1,6 @@
1
+ namespace NodeJS {
2
+ interface ProcessEnv {
3
+ NEXT_PUBLIC_GA_MEASUREMENT_ID: string;
4
+ NEXT_PUBLIC_FACEBOOK_PIXEL_ID: string;
5
+ }
6
+ }
@@ -0,0 +1,16 @@
1
+ declare global {
2
+ interface Window {
3
+ fbq: Function;
4
+ }
5
+ }
6
+
7
+ export const FB_PIXEL_ID = process.env.NEXT_PUBLIC_FACEBOOK_PIXEL_ID
8
+
9
+ export const pageview = () => {
10
+ window.fbq('track', 'PageView')
11
+ }
12
+
13
+ // https://developers.facebook.com/docs/meta-pixel/reference
14
+ export const sendFBEvent = (name: string, options = {}) => {
15
+ window.fbq('track', name, options)
16
+ }
@@ -0,0 +1,14 @@
1
+ declare global {
2
+ interface Window {
3
+ gtag: Function;
4
+ }
5
+ }
6
+
7
+ // https://developers.google.com/analytics/devguides/collection/ga4/ecommerce?client_type=gtag
8
+ const sendGAEvent = (name: string, options = {}) => {
9
+ window.gtag('event', name, options)
10
+ }
11
+
12
+ export {
13
+ sendGAEvent as default
14
+ }
@@ -0,0 +1,3 @@
1
+ export { sendFBEvent } from './fpixel'
2
+ export { default as sendGAEvent } from './google-analytics'
3
+ export { FacebookPixel } from './pixel-analytics'
@@ -0,0 +1,55 @@
1
+ 'use client'
2
+
3
+ import { usePathname } from 'next/navigation'
4
+ import Script from 'next/script'
5
+ import { useEffect, useState } from 'react'
6
+ import * as fbq from './fpixel'
7
+
8
+ const FacebookPixelHead = () => {
9
+ return (
10
+ <noscript>
11
+ <img
12
+ height='1'
13
+ width='1'
14
+ style={{ display: 'none' }}
15
+ src={`https://www.facebook.com/tr?id=${fbq.FB_PIXEL_ID}&ev=PageView&noscript=1`}
16
+ />
17
+ </noscript>
18
+ )
19
+ }
20
+
21
+ const FacebookPixel = () => {
22
+ const [loaded, setLoaded] = useState(false)
23
+ const pathname = usePathname()
24
+
25
+ useEffect(() => {
26
+ if (!loaded) return
27
+
28
+ fbq.pageview()
29
+ }, [pathname, loaded])
30
+
31
+ return (
32
+ <div>
33
+ <Script
34
+ id='fb-pixel'
35
+ strategy='afterInteractive'
36
+ onLoad={() => setLoaded(true)}
37
+ dangerouslySetInnerHTML={{
38
+ __html: `
39
+ !function(f,b,e,v,n,t,s)
40
+ {if(f.fbq)return;n=f.fbq=function(){n.callMethod?
41
+ n.callMethod.apply(n,arguments):n.queue.push(arguments)};
42
+ if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
43
+ n.queue=[];t=b.createElement(e);t.async=!0;
44
+ t.src=v;s=b.getElementsByTagName(e)[0];
45
+ s.parentNode.insertBefore(t,s)}(window, document,'script',
46
+ 'https://connect.facebook.net/en_US/fbevents.js');
47
+ fbq('init', ${fbq.FB_PIXEL_ID});
48
+ `,
49
+ }}
50
+ />
51
+ </div>
52
+ )
53
+ }
54
+
55
+ export {FacebookPixelHead, FacebookPixel}
@@ -0,0 +1,16 @@
1
+ import { NextRequest, NextResponse, userAgent } from 'next/server'
2
+ import { getSelectorsByUserAgent } from 'react-device-detect'
3
+
4
+ // writed this way so they can be chained :)
5
+ const determineDeviceMW = async (request: NextRequest) => {
6
+
7
+ const ua = userAgent(request)
8
+ const { isMobileOnly, isTablet, isDesktop } = getSelectorsByUserAgent(ua.ua)
9
+ const agent = isMobileOnly ? 'phone' : (isTablet ? 'tablet' : (isDesktop ? 'desktop' : 'unknown'))
10
+ const { nextUrl: url } = request
11
+ //console.log(`\n=== from ${url.href} on a *${agent && agent.toUpperCase()}* device. ===\n`)
12
+ url.searchParams.set('agent', agent)
13
+ return NextResponse.rewrite(url)
14
+ }
15
+
16
+ export default determineDeviceMW
@@ -0,0 +1,12 @@
1
+ import nextFonts from './load-and-return-lux-next-fonts-on-import'
2
+ import type NextFontDesc from './next-font-desc'
3
+
4
+ // These will be injected for <body> in app router app that uses our RootLayout
5
+
6
+ // First is assumed to be mapped to the default font and is injected into <body>
7
+ // as a normal tw font family class.
8
+ export default () => (
9
+ nextFonts.map(
10
+ (desc: NextFontDesc) => (desc.nextFont!.variable)
11
+ ).join(' ') + ` font-${nextFonts[0].twName}`
12
+ )
@@ -0,0 +1,67 @@
1
+ import { Inter } from 'next/font/google'
2
+ import localFont from 'next/font/local'
3
+
4
+ import type { TwFontDesc } from '@hanzo/ui/tailwind' // TODO
5
+ import type NextFontDesc from './next-font-desc'
6
+
7
+ import twFonts from '../../tailwind/lux-tw-fonts'
8
+
9
+ /*
10
+ Creating NextFontDesc's and TwFontDesc's has to be seperated because they are needed
11
+ at different times during the next compile / build. Otherwise a nasty
12
+ race condition happens!
13
+
14
+ Also, requires that "Font loaders must be called and assigned to a const in the module scope"
15
+
16
+ */
17
+
18
+ const drukWide = localFont({
19
+ src: [
20
+ {
21
+ path: './local/Druk-Wide-Bold.ttf',
22
+ weight: '700',
23
+ style: 'normal'
24
+ },
25
+ {
26
+ path: './local/Druk-Wide-Medium.ttf',
27
+ weight: '500',
28
+ style: 'normal',
29
+ },
30
+ ],
31
+ display: 'swap',
32
+ variable: '--font-druk-wide' ,
33
+ })
34
+
35
+ const inter = Inter({
36
+ subsets: ["latin"],
37
+ variable: "--font-inter",
38
+ })
39
+
40
+ export default [
41
+ {
42
+ font: inter,
43
+ twName: 'sans'
44
+ },
45
+ {
46
+ font: inter,
47
+ twName: 'nav'
48
+ },
49
+ {
50
+ font: drukWide,
51
+ twName: 'heading'
52
+ }
53
+ ].map (
54
+ (el) => {
55
+ const twFont: TwFontDesc | undefined = twFonts.find((twf: TwFontDesc) => (el.twName === twf.twName))
56
+ if (!twFont) {
57
+ throw new Error('lux-next-fonts: Next font is not paired to a TW font!')
58
+ }
59
+
60
+ return ({
61
+ ...twFont,
62
+ nextFont: el.font,
63
+ })
64
+ }
65
+ ) satisfies NextFontDesc[]
66
+
67
+
@@ -0,0 +1,28 @@
1
+ import type { TwFontDesc } from '@hanzo/ui/tailwind'
2
+
3
+ // from next repo
4
+ type NextFont = {
5
+ className: string
6
+ style: { fontFamily: string; fontWeight?: number; fontStyle?: string }
7
+ }
8
+
9
+ // from next repo
10
+ type NextFontWithVariable = NextFont & {
11
+ variable: string
12
+ }
13
+
14
+
15
+ /*
16
+ NextFontDesc and TwFontDesc have to be seperate because they are needed
17
+ at different times during the next compile / build. Otherwise a nasty
18
+ race condition happens! That's why they are in different files.
19
+ */
20
+
21
+ interface NextFontDesc extends TwFontDesc {
22
+ nextFont: NextFontWithVariable
23
+ }
24
+
25
+ export {
26
+ type NextFontDesc as default,
27
+ type NextFontWithVariable,
28
+ }
@@ -0,0 +1,18 @@
1
+
2
+ import nextFonts from './load-and-return-lux-next-fonts-on-import'
3
+ import type NextFontDesc from './next-font-desc'
4
+
5
+ const PagesRouterFontVars: React.FC = () => {
6
+
7
+ const fontVars = nextFonts.map((fd: NextFontDesc) => (
8
+ `${fd.cssVar}: ${fd.nextFont.style.fontFamily};`
9
+ )).join('\n')
10
+
11
+ return <style jsx global>{`
12
+ html {
13
+ ${fontVars}
14
+ }
15
+ `}</style>
16
+ }
17
+
18
+ export default PagesRouterFontVars
@@ -0,0 +1,158 @@
1
+ /**
2
+ *
3
+ * Metadata types
4
+ *
5
+ */
6
+
7
+ export interface DeprecatedMetadataFields {
8
+ /**
9
+ * Deprecated options that have a preferred method
10
+ * @deprecated Use appWebApp to configure apple-mobile-web-app-capable which provides
11
+ * @see https://www.appsloveworld.com/coding/iphone/11/difference-between-apple-mobile-web-app-capable-and-apple-touch-fullscreen-ipho
12
+ */
13
+ 'apple-touch-fullscreen'?: never
14
+
15
+ /**
16
+ * Obsolete since iOS 7.
17
+ * @see https://web.dev/apple-touch-icon/
18
+ * @deprecated use icons.apple or instead
19
+ */
20
+ 'apple-touch-icon-precomposed'?: never
21
+ }
22
+
23
+ export type TemplateString =
24
+ | DefaultTemplateString
25
+ | AbsoluteTemplateString
26
+ | AbsoluteString
27
+ export type DefaultTemplateString = {
28
+ default: string
29
+ template: string
30
+ }
31
+ export type AbsoluteTemplateString = {
32
+ absolute: string
33
+ template: string | null
34
+ }
35
+ export type AbsoluteString = {
36
+ absolute: string
37
+ }
38
+
39
+ export type Author = {
40
+ // renders as <link rel="author"...
41
+ url?: string | URL
42
+ // renders as <meta name="author"...
43
+ name?: string
44
+ }
45
+
46
+ // does not include "unsafe-URL". to use this users should
47
+ // use '"unsafe-URL" as ReferrerEnum'
48
+ export type ReferrerEnum =
49
+ | 'no-referrer'
50
+ | 'origin'
51
+ | 'no-referrer-when-downgrade'
52
+ | 'origin-when-cross-origin'
53
+ | 'same-origin'
54
+ | 'strict-origin'
55
+ | 'strict-origin-when-cross-origin'
56
+
57
+ export type ColorSchemeEnum =
58
+ | 'normal'
59
+ | 'light'
60
+ | 'dark'
61
+ | 'light dark'
62
+ | 'dark light'
63
+ | 'only light'
64
+
65
+ type RobotsInfo = {
66
+ // all and none will be inferred from index/follow boolean options
67
+ index?: boolean
68
+ follow?: boolean
69
+
70
+ /** @deprecated set index to false instead */
71
+ noindex?: never
72
+ /** @deprecated set follow to false instead */
73
+ nofollow?: never
74
+
75
+ noarchive?: boolean
76
+ nosnippet?: boolean
77
+ noimageindex?: boolean
78
+ nocache?: boolean
79
+ notranslate?: boolean
80
+ indexifembedded?: boolean
81
+ nositelinkssearchbox?: boolean
82
+ unavailable_after?: string
83
+ 'max-video-preview'?: number | string
84
+ 'max-image-preview'?: 'none' | 'standard' | 'large'
85
+ 'max-snippet'?: number
86
+ }
87
+ export type Robots = RobotsInfo & {
88
+ // if you want to specify an alternate robots just for google
89
+ googleBot?: string | RobotsInfo
90
+ }
91
+
92
+ export type ResolvedRobots = {
93
+ basic: string | null
94
+ googleBot: string | null
95
+ }
96
+
97
+ export type IconURL = string | URL
98
+ export type Icon = IconURL | IconDescriptor
99
+ export type IconDescriptor = {
100
+ url: string | URL
101
+ type?: string
102
+ sizes?: string
103
+ color?: string
104
+ /** defaults to rel="icon" unless superseded by Icons map */
105
+ rel?: string
106
+ media?: string
107
+ /**
108
+ * @see https://developer.mozilla.org/docs/Web/API/HTMLImageElement/fetchPriority
109
+ */
110
+ fetchPriority?: 'high' | 'low' | 'auto'
111
+ }
112
+
113
+ export type Icons = {
114
+ /** rel="icon" */
115
+ icon?: Icon | Icon[]
116
+ /** rel="shortcut icon" */
117
+ shortcut?: Icon | Icon[]
118
+ /**
119
+ * @see https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html
120
+ * rel="apple-touch-icon"
121
+ */
122
+ apple?: Icon | Icon[]
123
+ /** rel inferred from descriptor, defaults to "icon" */
124
+ other?: IconDescriptor | IconDescriptor[]
125
+ }
126
+
127
+ export type Verification = {
128
+ google?: null | string | number | (string | number)[]
129
+ yahoo?: null | string | number | (string | number)[]
130
+ yandex?: null | string | number | (string | number)[]
131
+ me?: null | string | number | (string | number)[]
132
+ // if you ad-hoc additional verification
133
+ other?: {
134
+ [name: string]: string | number | (string | number)[]
135
+ }
136
+ }
137
+
138
+ export type ResolvedVerification = {
139
+ google?: null | (string | number)[]
140
+ yahoo?: null | (string | number)[]
141
+ yandex?: null | (string | number)[]
142
+ me?: null | (string | number)[]
143
+ other?: {
144
+ [name: string]: (string | number)[]
145
+ }
146
+ }
147
+
148
+ export type ResolvedIcons = {
149
+ icon: IconDescriptor[]
150
+ apple: IconDescriptor[]
151
+ shortcut?: IconDescriptor[]
152
+ other?: IconDescriptor[]
153
+ }
154
+
155
+ export type ThemeColorDescriptor = {
156
+ color: string
157
+ media?: string
158
+ }