@stackshift-ui/navigation 6.0.2 → 6.0.4
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.
- package/dist/chunk-6GFWMJF7.mjs +1 -0
- package/dist/chunk-JPXJT6MQ.mjs +1 -0
- package/dist/chunk-NEHPXLTQ.mjs +1 -0
- package/dist/chunk-TXZJ7HY5.mjs +1 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/navigation.js +1 -1
- package/dist/navigation.mjs +1 -1
- package/dist/navigation_a.js +1 -1
- package/dist/navigation_a.mjs +1 -1
- package/dist/navigation_b.js +1 -1
- package/dist/navigation_b.mjs +1 -1
- package/dist/navigation_c.js +1 -1
- package/dist/navigation_c.mjs +1 -1
- package/dist/navigation_d.js +1 -1
- package/dist/navigation_d.mjs +1 -1
- package/package.json +11 -10
- package/src/helper/blockStyle.tsx +37 -0
- package/src/helper/index.ts +14 -0
- package/src/index.ts +9 -0
- package/src/navigation.test.tsx +13 -0
- package/src/navigation.tsx +45 -0
- package/src/navigation_a.tsx +259 -0
- package/src/navigation_b.tsx +250 -0
- package/src/navigation_c.tsx +252 -0
- package/src/navigation_d.tsx +252 -0
- package/src/navigation_e.tsx +476 -0
- package/src/types.ts +418 -0
- package/dist/chunk-BBLTN2UT.mjs +0 -1
- package/dist/chunk-LCKSZIYO.mjs +0 -1
- package/dist/chunk-SHQ3E2LC.mjs +0 -1
- package/dist/chunk-SP7T3FEY.mjs +0 -1
- /package/dist/{chunk-CH3GDNJW.mjs → chunk-PDD3FFTJ.mjs} +0 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { MyPortableTextComponents } from "../types";
|
|
2
|
+
import { Text } from "@stackshift-ui/text";
|
|
3
|
+
|
|
4
|
+
// block styling as props to `serializers` of the PortableText component
|
|
5
|
+
export const blockStyle: MyPortableTextComponents = {
|
|
6
|
+
block: {
|
|
7
|
+
normal: ({ children }) => {
|
|
8
|
+
return (
|
|
9
|
+
<Text fontSize="xs" weight="bold" className="text-white ">
|
|
10
|
+
{children}
|
|
11
|
+
</Text>
|
|
12
|
+
);
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
code: ({ value }) => {
|
|
16
|
+
return (
|
|
17
|
+
<pre data-language={value.language}>
|
|
18
|
+
<code>{value.code}</code>
|
|
19
|
+
</pre>
|
|
20
|
+
);
|
|
21
|
+
},
|
|
22
|
+
marks: {
|
|
23
|
+
strong: ({ children }) => <strong>{children}</strong>,
|
|
24
|
+
em: ({ children }) => <em>{children}</em>,
|
|
25
|
+
code: ({ children }) => <code>{children}</code>,
|
|
26
|
+
link: ({ children, value }) => (
|
|
27
|
+
<a
|
|
28
|
+
aria-label={value?.href ?? "external link"}
|
|
29
|
+
className="text-primary-foreground hover:text-secondary-foreground"
|
|
30
|
+
href={value?.href}
|
|
31
|
+
target="_blank"
|
|
32
|
+
rel="noopener noreferrer">
|
|
33
|
+
{children}
|
|
34
|
+
</a>
|
|
35
|
+
),
|
|
36
|
+
},
|
|
37
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Logo } from "../types";
|
|
2
|
+
|
|
3
|
+
export const logoLink = (logo: Logo) => {
|
|
4
|
+
if (logo?.internalLink && logo?.type === "linkInternal") {
|
|
5
|
+
if (logo?.internalLink?.toLowerCase()?.includes("home")) {
|
|
6
|
+
return "/";
|
|
7
|
+
}
|
|
8
|
+
return `/${logo.internalLink}`;
|
|
9
|
+
} else if (logo?.externalLink && logo?.type === "linkExternal") {
|
|
10
|
+
return logo?.externalLink ?? "/";
|
|
11
|
+
} else {
|
|
12
|
+
return "/";
|
|
13
|
+
}
|
|
14
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { cleanup, render, screen } from "@testing-library/react";
|
|
2
|
+
import { afterEach, describe, test } from "vitest";
|
|
3
|
+
import { Navigation } from "./navigation";
|
|
4
|
+
|
|
5
|
+
describe.concurrent("navigation", () => {
|
|
6
|
+
afterEach(cleanup);
|
|
7
|
+
|
|
8
|
+
test.skip("Dummy test - test if renders without errors", ({ expect }) => {
|
|
9
|
+
const clx = "my-class";
|
|
10
|
+
render(<Navigation />);
|
|
11
|
+
expect(screen.getByTestId("{ kebabCase name }}").classList).toContain(clx);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { lazy } from "react";
|
|
2
|
+
import { LabeledRoute, LabeledRouteWithKey, Logo, SectionsProps } from "./types";
|
|
3
|
+
|
|
4
|
+
const Variants = {
|
|
5
|
+
variant_a: lazy(() => import("./navigation_a")),
|
|
6
|
+
variant_b: lazy(() => import("./navigation_b")),
|
|
7
|
+
variant_c: lazy(() => import("./navigation_c")),
|
|
8
|
+
variant_d: lazy(() => import("./navigation_d")),
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export interface ResponsiveNavLinksProps {
|
|
12
|
+
menu: boolean;
|
|
13
|
+
showMenu: () => void;
|
|
14
|
+
links?: LabeledRouteWithKey[];
|
|
15
|
+
primaryButton?: LabeledRoute;
|
|
16
|
+
secondaryButton?: LabeledRoute;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface NavigationProps {
|
|
20
|
+
template?: any;
|
|
21
|
+
logo?: Logo;
|
|
22
|
+
links?: LabeledRouteWithKey[];
|
|
23
|
+
primaryButton?: LabeledRoute;
|
|
24
|
+
secondaryButton?: LabeledRoute;
|
|
25
|
+
banner?: any;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const displayName = "Navigation";
|
|
29
|
+
|
|
30
|
+
export const Navigation: React.FC<SectionsProps> = ({ data }) => {
|
|
31
|
+
const variant = data?.variant;
|
|
32
|
+
const Variant = variant && Variants?.[variant as keyof typeof Variants];
|
|
33
|
+
|
|
34
|
+
const props = {
|
|
35
|
+
logo: data?.variants?.logo ?? undefined,
|
|
36
|
+
links: data?.variants?.routes ?? undefined,
|
|
37
|
+
primaryButton: data?.variants?.primaryButton ?? undefined,
|
|
38
|
+
secondaryButton: data?.variants?.secondaryButton ?? undefined,
|
|
39
|
+
banner: data?.variants?.banner ?? undefined,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return Variant ? <Variant {...props} /> : null;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
Navigation.displayName = displayName;
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { Button } from "@stackshift-ui/button";
|
|
2
|
+
import { Flex } from "@stackshift-ui/flex";
|
|
3
|
+
import { Image } from "@stackshift-ui/image";
|
|
4
|
+
import { Link } from "@stackshift-ui/link";
|
|
5
|
+
import { Section } from "@stackshift-ui/section";
|
|
6
|
+
import { Text } from "@stackshift-ui/text";
|
|
7
|
+
import React from "react";
|
|
8
|
+
import { NavigationProps, ResponsiveNavLinksProps } from ".";
|
|
9
|
+
import { logoLink } from "./helper";
|
|
10
|
+
import { LabeledRoute, LabeledRouteWithKey, Logo } from "./types";
|
|
11
|
+
|
|
12
|
+
export default function Navigation_A({
|
|
13
|
+
links,
|
|
14
|
+
primaryButton,
|
|
15
|
+
secondaryButton,
|
|
16
|
+
logo,
|
|
17
|
+
}: NavigationProps) {
|
|
18
|
+
const [menu, setMenu] = React.useState(false);
|
|
19
|
+
const showMenu = () => {
|
|
20
|
+
setMenu(prevState => !prevState);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<Section className="bg-background">
|
|
25
|
+
<Flex align="center" justify="between" className="px-6 py-6">
|
|
26
|
+
<LogoSection logo={logo} />
|
|
27
|
+
<NavLinks links={links} />
|
|
28
|
+
<Buttons primaryButton={primaryButton} secondaryButton={secondaryButton} />
|
|
29
|
+
<MobileMenu showMenu={showMenu} />
|
|
30
|
+
</Flex>
|
|
31
|
+
<ResponsiveNavLinks
|
|
32
|
+
menu={menu}
|
|
33
|
+
showMenu={showMenu}
|
|
34
|
+
links={links}
|
|
35
|
+
primaryButton={primaryButton}
|
|
36
|
+
secondaryButton={secondaryButton}
|
|
37
|
+
/>
|
|
38
|
+
</Section>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function LogoSection({ logo }: { logo?: Logo }) {
|
|
43
|
+
if (!logo) return null;
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<Link
|
|
47
|
+
aria-label={`Go to ${logoLink(logo) === "/" ? "home page" : logoLink(logo)}`}
|
|
48
|
+
className="text-3xl font-bold leading-none"
|
|
49
|
+
href={logoLink(logo)}
|
|
50
|
+
target={logo?.linkTarget}
|
|
51
|
+
rel={logo?.linkTarget === "_blank" ? "noopener noreferrer" : ""}>
|
|
52
|
+
<Image
|
|
53
|
+
src={logo?.image}
|
|
54
|
+
alt={logo?.alt ?? "navigation-logo"}
|
|
55
|
+
width={50}
|
|
56
|
+
height={50}
|
|
57
|
+
className="text-3xl font-bold leading-none"
|
|
58
|
+
/>
|
|
59
|
+
</Link>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function NavIcon() {
|
|
64
|
+
return (
|
|
65
|
+
<li className="text-gray-500">
|
|
66
|
+
<svg
|
|
67
|
+
className="w-4 h-4 current-fill"
|
|
68
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
69
|
+
fill="none"
|
|
70
|
+
viewBox="0 0 24 24"
|
|
71
|
+
stroke="currentColor">
|
|
72
|
+
<path
|
|
73
|
+
strokeLinecap="round"
|
|
74
|
+
strokeLinejoin="round"
|
|
75
|
+
strokeWidth="2"
|
|
76
|
+
d="M12 5v.01M12 12v.01M12 19v.01M12 6a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2z"></path>
|
|
77
|
+
</svg>
|
|
78
|
+
</li>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function NavLinks({ links }: { links?: LabeledRouteWithKey[] }) {
|
|
83
|
+
if (!links) return null;
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<Flex>
|
|
87
|
+
<ul className="hidden lg:flex lg:items-center lg:space-x-6">
|
|
88
|
+
{links?.map((link: any, index: number) => (
|
|
89
|
+
<React.Fragment key={index}>
|
|
90
|
+
<NavItem link={link} key={link._key} />
|
|
91
|
+
{links.length !== index + 1 ? <NavIcon /> : null}
|
|
92
|
+
</React.Fragment>
|
|
93
|
+
))}
|
|
94
|
+
</ul>
|
|
95
|
+
</Flex>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function NavItem({ link }: { link?: LabeledRoute }) {
|
|
100
|
+
if (!link?.label) return null;
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<li>
|
|
104
|
+
<Button
|
|
105
|
+
as="link"
|
|
106
|
+
link={link}
|
|
107
|
+
ariaLabel={link?.label}
|
|
108
|
+
className="text-sm text-gray-500 no-underline hover:text-gray-900">
|
|
109
|
+
{link?.label}
|
|
110
|
+
</Button>
|
|
111
|
+
</li>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function ResponsiveNavLinks({
|
|
116
|
+
menu,
|
|
117
|
+
showMenu,
|
|
118
|
+
links,
|
|
119
|
+
primaryButton,
|
|
120
|
+
secondaryButton,
|
|
121
|
+
}: ResponsiveNavLinksProps) {
|
|
122
|
+
return (
|
|
123
|
+
<div className={`${menu ? null : "hidden"} mobile-nav relative z-50`}>
|
|
124
|
+
<div className="fixed inset-0 bg-gray-800 opacity-25 navbar-backdrop" onClick={showMenu} />
|
|
125
|
+
<Flex
|
|
126
|
+
as="nav"
|
|
127
|
+
direction="col"
|
|
128
|
+
className="fixed top-0 bottom-0 left-0 w-5/6 max-w-sm px-6 py-6 overflow-y-auto bg-white border-r">
|
|
129
|
+
<BurgerMenuIcon showMenu={showMenu} />
|
|
130
|
+
<div className="w-full">
|
|
131
|
+
{links ? (
|
|
132
|
+
<ul>
|
|
133
|
+
{links?.map((link: any, index: number) => (
|
|
134
|
+
<li className="mb-1" key={index}>
|
|
135
|
+
<Button
|
|
136
|
+
as="link"
|
|
137
|
+
ariaLabel={link?.label}
|
|
138
|
+
className="block w-full cursor-pointer p-4 text-sm font-semibold text-gray-900 no-underline rounded hover:bg-secondary-foreground hover:text-primary"
|
|
139
|
+
link={link}>
|
|
140
|
+
{link?.label}
|
|
141
|
+
</Button>
|
|
142
|
+
</li>
|
|
143
|
+
))}
|
|
144
|
+
</ul>
|
|
145
|
+
) : null}
|
|
146
|
+
</div>
|
|
147
|
+
<div className="w-full mt-auto">
|
|
148
|
+
<Flex direction="col" className="pt-6 space-x-2">
|
|
149
|
+
{primaryButton?.label ? (
|
|
150
|
+
<Button
|
|
151
|
+
as="link"
|
|
152
|
+
link={primaryButton}
|
|
153
|
+
ariaLabel={primaryButton?.label}
|
|
154
|
+
variant="outline"
|
|
155
|
+
className="block w-full px-4 py-3 mb-3 text-xs cursor-pointer font-semibold leading-loose text-center text-gray-900 rounded-global bg-secondary hover:bg-secondary/50">
|
|
156
|
+
{primaryButton?.label}
|
|
157
|
+
</Button>
|
|
158
|
+
) : null}
|
|
159
|
+
{secondaryButton?.label ? (
|
|
160
|
+
<Button
|
|
161
|
+
as="link"
|
|
162
|
+
link={secondaryButton}
|
|
163
|
+
ariaLabel={secondaryButton?.label}
|
|
164
|
+
variant="solid"
|
|
165
|
+
className={`block w-full px-4 py-3 mb-2 cursor-pointer leading-loose text-xs text-center font-semibold bg-primary hover:bg-primary-foreground rounded-global`}>
|
|
166
|
+
{secondaryButton?.label}
|
|
167
|
+
</Button>
|
|
168
|
+
) : null}
|
|
169
|
+
</Flex>
|
|
170
|
+
<Text fontSize="xs" className="my-4 text-center text-gray-900">
|
|
171
|
+
<span>{`© ${new Date().getFullYear()} All rights reserved.`}</span>
|
|
172
|
+
</Text>
|
|
173
|
+
</div>
|
|
174
|
+
</Flex>
|
|
175
|
+
</div>
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function MobileMenu({ showMenu }: { showMenu: () => void }) {
|
|
180
|
+
return (
|
|
181
|
+
<div className="lg:hidden">
|
|
182
|
+
<Button
|
|
183
|
+
variant="unstyled"
|
|
184
|
+
as="button"
|
|
185
|
+
ariaLabel="Navigation Menu"
|
|
186
|
+
className="flex items-center p-3 navbar-burger text-primary"
|
|
187
|
+
onClick={showMenu}>
|
|
188
|
+
<svg
|
|
189
|
+
className="block w-4 h-4 fill-current"
|
|
190
|
+
viewBox="0 0 20 20"
|
|
191
|
+
xmlns="http://www.w3.org/2000/svg">
|
|
192
|
+
<title>Mobile menu</title>
|
|
193
|
+
<path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z"></path>
|
|
194
|
+
</svg>
|
|
195
|
+
</Button>
|
|
196
|
+
</div>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function Buttons({
|
|
201
|
+
primaryButton,
|
|
202
|
+
secondaryButton,
|
|
203
|
+
}: {
|
|
204
|
+
primaryButton?: LabeledRoute;
|
|
205
|
+
secondaryButton?: LabeledRoute;
|
|
206
|
+
}) {
|
|
207
|
+
return (
|
|
208
|
+
<Flex gap={4}>
|
|
209
|
+
{primaryButton?.label ? (
|
|
210
|
+
<Button
|
|
211
|
+
as="link"
|
|
212
|
+
link={primaryButton}
|
|
213
|
+
ariaLabel={primaryButton?.label}
|
|
214
|
+
variant="outline"
|
|
215
|
+
className="hidden lg:flex px-4 py-3 mb-2 leading-loose text-center font-semibold text-gray-900 rounded-global bg-secondary hover:bg-secondary/50">
|
|
216
|
+
{primaryButton?.label}
|
|
217
|
+
</Button>
|
|
218
|
+
) : null}
|
|
219
|
+
{secondaryButton?.label ? (
|
|
220
|
+
<Button
|
|
221
|
+
as="link"
|
|
222
|
+
link={secondaryButton}
|
|
223
|
+
ariaLabel={secondaryButton?.label}
|
|
224
|
+
variant="solid"
|
|
225
|
+
className="hidden lg:flex px-4 py-3 mb-2 leading-loose text-center font-semibold text-white bg-primary hover:bg-primary-foreground rounded-global">
|
|
226
|
+
{secondaryButton?.label}
|
|
227
|
+
</Button>
|
|
228
|
+
) : null}
|
|
229
|
+
</Flex>
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function BurgerMenuIcon({ showMenu }: { showMenu: () => void }) {
|
|
234
|
+
return (
|
|
235
|
+
<div className="flex items-center mb-8">
|
|
236
|
+
<Button
|
|
237
|
+
variant="unstyled"
|
|
238
|
+
as="button"
|
|
239
|
+
ariaLabel="Navigation Menu"
|
|
240
|
+
className="navbar-close"
|
|
241
|
+
onClick={showMenu}>
|
|
242
|
+
<svg
|
|
243
|
+
className="w-6 h-6 text-gray-500 cursor-pointer hover:text-gray-500"
|
|
244
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
245
|
+
fill="none"
|
|
246
|
+
viewBox="0 0 24 24"
|
|
247
|
+
stroke="currentColor">
|
|
248
|
+
<path
|
|
249
|
+
strokeLinecap="round"
|
|
250
|
+
strokeLinejoin="round"
|
|
251
|
+
strokeWidth="2"
|
|
252
|
+
d="M6 18L18 6M6 6l12 12"></path>
|
|
253
|
+
</svg>
|
|
254
|
+
</Button>
|
|
255
|
+
</div>
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export { Navigation_A };
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { Button } from "@stackshift-ui/button";
|
|
2
|
+
import { Container } from "@stackshift-ui/container";
|
|
3
|
+
import { Flex } from "@stackshift-ui/flex";
|
|
4
|
+
import { Image } from "@stackshift-ui/image";
|
|
5
|
+
import { Link } from "@stackshift-ui/link";
|
|
6
|
+
import { Section } from "@stackshift-ui/section";
|
|
7
|
+
import { Text } from "@stackshift-ui/text";
|
|
8
|
+
import React from "react";
|
|
9
|
+
import { NavigationProps, ResponsiveNavLinksProps } from ".";
|
|
10
|
+
import { logoLink } from "./helper";
|
|
11
|
+
import { LabeledRoute, LabeledRouteWithKey, Logo } from "./types";
|
|
12
|
+
|
|
13
|
+
export default function Navigation_B({
|
|
14
|
+
links,
|
|
15
|
+
primaryButton,
|
|
16
|
+
secondaryButton,
|
|
17
|
+
logo,
|
|
18
|
+
}: NavigationProps) {
|
|
19
|
+
const [menu, setMenu] = React.useState(false);
|
|
20
|
+
const showMenu = () => {
|
|
21
|
+
setMenu(prevState => !prevState);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<Section className="bg-background">
|
|
26
|
+
<nav className="relative py-6">
|
|
27
|
+
<Container maxWidth={1000}>
|
|
28
|
+
<Flex align="center" justify="between">
|
|
29
|
+
<LogoSection logo={logo} />
|
|
30
|
+
<MobileMenu showMenu={showMenu} />
|
|
31
|
+
<NavLinks links={links} />
|
|
32
|
+
<Buttons primaryButton={primaryButton} secondaryButton={secondaryButton} />
|
|
33
|
+
</Flex>
|
|
34
|
+
</Container>
|
|
35
|
+
</nav>
|
|
36
|
+
<ResponsiveNavLinks
|
|
37
|
+
menu={menu}
|
|
38
|
+
showMenu={showMenu}
|
|
39
|
+
links={links}
|
|
40
|
+
primaryButton={primaryButton}
|
|
41
|
+
secondaryButton={secondaryButton}
|
|
42
|
+
/>
|
|
43
|
+
</Section>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function LogoSection({ logo }: { logo?: Logo }) {
|
|
48
|
+
if (!logo) return null;
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<Link
|
|
52
|
+
aria-label={`Go to ${logoLink(logo) === "/" ? "home page" : logoLink(logo)}`}
|
|
53
|
+
className="text-3xl font-bold leading-none"
|
|
54
|
+
href={logoLink(logo)}
|
|
55
|
+
target={logo?.linkTarget}
|
|
56
|
+
rel={logo?.linkTarget === "_blank" ? "noopener noreferrer" : ""}>
|
|
57
|
+
<Image
|
|
58
|
+
src={logo?.image}
|
|
59
|
+
alt={logo?.alt ?? "navigation-logo"}
|
|
60
|
+
width={48}
|
|
61
|
+
height={48}
|
|
62
|
+
className="text-3xl font-bold leading-none"
|
|
63
|
+
/>
|
|
64
|
+
</Link>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function NavLinks({ links }: { links?: LabeledRouteWithKey[] }) {
|
|
69
|
+
if (!links) return null;
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<ul className="absolute hidden transform -translate-x-1/2 -translate-y-1/2 left-1/2 top-1/2 lg:mx-auto lg:flex lg:w-auto lg:items-center lg:space-x-6">
|
|
73
|
+
{links?.map((link, index) => (
|
|
74
|
+
<React.Fragment key={index}>
|
|
75
|
+
{link?.label && <NavItem link={link} key={index} />}
|
|
76
|
+
{links.length !== index + 1 ? <NavIcon /> : null}
|
|
77
|
+
</React.Fragment>
|
|
78
|
+
))}
|
|
79
|
+
</ul>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function NavItem({ link }: { link?: LabeledRouteWithKey }) {
|
|
84
|
+
if (!link) return null;
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<li>
|
|
88
|
+
<Button
|
|
89
|
+
as="link"
|
|
90
|
+
ariaLabel={link?.label}
|
|
91
|
+
link={link}
|
|
92
|
+
className="text-sm text-gray-500 no-underline hover:text-gray-900">
|
|
93
|
+
{link?.label}
|
|
94
|
+
</Button>
|
|
95
|
+
</li>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function NavIcon() {
|
|
100
|
+
return (
|
|
101
|
+
<li className="text-gray-500">
|
|
102
|
+
<svg
|
|
103
|
+
className="w-4 h-4 current-fill"
|
|
104
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
105
|
+
fill="none"
|
|
106
|
+
viewBox="0 0 24 24"
|
|
107
|
+
stroke="currentColor">
|
|
108
|
+
<path
|
|
109
|
+
strokeLinecap="round"
|
|
110
|
+
strokeLinejoin="round"
|
|
111
|
+
strokeWidth="2"
|
|
112
|
+
d="M12 5v.01M12 12v.01M12 19v.01M12 6a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2z"></path>
|
|
113
|
+
</svg>
|
|
114
|
+
</li>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function Buttons({
|
|
119
|
+
primaryButton,
|
|
120
|
+
secondaryButton,
|
|
121
|
+
}: {
|
|
122
|
+
primaryButton?: LabeledRoute;
|
|
123
|
+
secondaryButton?: LabeledRoute;
|
|
124
|
+
}) {
|
|
125
|
+
return (
|
|
126
|
+
<React.Fragment>
|
|
127
|
+
{primaryButton?.label && (
|
|
128
|
+
<Button
|
|
129
|
+
as="link"
|
|
130
|
+
ariaLabel={primaryButton?.label}
|
|
131
|
+
link={primaryButton}
|
|
132
|
+
className="hidden lg:inline-block px-4 py-3 mb-2 text-gray-900 lg:ml-auto lg:mr-3 font-semibold rounded-global bg-secondary hover:bg-secondary/50">
|
|
133
|
+
{primaryButton?.label}
|
|
134
|
+
</Button>
|
|
135
|
+
)}
|
|
136
|
+
{secondaryButton?.label && (
|
|
137
|
+
<Button
|
|
138
|
+
as="link"
|
|
139
|
+
ariaLabel={secondaryButton?.label}
|
|
140
|
+
link={secondaryButton}
|
|
141
|
+
className="hidden lg:inline-block px-4 py-3 mb-2 leading-loose text-center text-white font-semibold bg-primary hover:bg-primary-foreground rounded-global">
|
|
142
|
+
{secondaryButton?.label}
|
|
143
|
+
</Button>
|
|
144
|
+
)}
|
|
145
|
+
</React.Fragment>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function MobileMenu({ showMenu }: { showMenu: () => void }) {
|
|
150
|
+
return (
|
|
151
|
+
<div className="lg:hidden">
|
|
152
|
+
<Button
|
|
153
|
+
variant="unstyled"
|
|
154
|
+
as="button"
|
|
155
|
+
ariaLabel="Navigation menu"
|
|
156
|
+
className="flex items-center p-3 navbar-burger text-primary"
|
|
157
|
+
onClick={showMenu}>
|
|
158
|
+
<svg
|
|
159
|
+
className="block w-4 h-4 fill-current"
|
|
160
|
+
viewBox="0 0 20 20"
|
|
161
|
+
xmlns="http://www.w3.org/2000/svg">
|
|
162
|
+
<title>Mobile menu</title>
|
|
163
|
+
<path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z"></path>
|
|
164
|
+
</svg>
|
|
165
|
+
</Button>
|
|
166
|
+
</div>
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function ResponsiveNavLinks({
|
|
171
|
+
menu,
|
|
172
|
+
showMenu,
|
|
173
|
+
links,
|
|
174
|
+
primaryButton,
|
|
175
|
+
secondaryButton,
|
|
176
|
+
}: ResponsiveNavLinksProps) {
|
|
177
|
+
return (
|
|
178
|
+
<React.Fragment>
|
|
179
|
+
<div className={`${menu ? null : "hidden"} mobile-nav relative z-50`}>
|
|
180
|
+
<div className="fixed inset-0 bg-gray-800 opacity-25 navbar-backdrop" onClick={showMenu} />
|
|
181
|
+
<nav className="fixed top-0 bottom-0 left-0 flex flex-col w-5/6 max-w-sm px-6 py-6 overflow-y-auto bg-white border-r">
|
|
182
|
+
<div className="flex items-center mb-8">
|
|
183
|
+
<Button
|
|
184
|
+
variant="unstyled"
|
|
185
|
+
as="button"
|
|
186
|
+
ariaLabel="Navigation menu"
|
|
187
|
+
className="navbar-close"
|
|
188
|
+
onClick={showMenu}>
|
|
189
|
+
<svg
|
|
190
|
+
className="w-6 h-6 text-gray-500 cursor-pointer hover:text-gray-500"
|
|
191
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
192
|
+
fill="none"
|
|
193
|
+
viewBox="0 0 24 24"
|
|
194
|
+
stroke="currentColor">
|
|
195
|
+
<path
|
|
196
|
+
strokeLinecap="round"
|
|
197
|
+
strokeLinejoin="round"
|
|
198
|
+
strokeWidth="2"
|
|
199
|
+
d="M6 18L18 6M6 6l12 12"
|
|
200
|
+
/>
|
|
201
|
+
</svg>
|
|
202
|
+
</Button>
|
|
203
|
+
</div>
|
|
204
|
+
{links && (
|
|
205
|
+
<ul>
|
|
206
|
+
{links?.map((link, index) => (
|
|
207
|
+
<li className="mb-1" key={index}>
|
|
208
|
+
<Button
|
|
209
|
+
as="link"
|
|
210
|
+
ariaLabel={link?.label}
|
|
211
|
+
link={link}
|
|
212
|
+
className="block p-4 text-sm font-semibold text-gray-700 no-underline rounded hover:bg-secondary-foreground hover:text-primary">
|
|
213
|
+
{link?.label}
|
|
214
|
+
</Button>
|
|
215
|
+
</li>
|
|
216
|
+
))}
|
|
217
|
+
</ul>
|
|
218
|
+
)}
|
|
219
|
+
<div className="mt-auto">
|
|
220
|
+
<div className="pt-6">
|
|
221
|
+
{primaryButton?.label && (
|
|
222
|
+
<Button
|
|
223
|
+
as="link"
|
|
224
|
+
ariaLabel={primaryButton?.label}
|
|
225
|
+
link={primaryButton}
|
|
226
|
+
className="block px-4 py-3 mb-2 text-gray-900 text-center lg:ml-auto lg:mr-3 font-semibold rounded-global bg-secondary hover:bg-secondary/50">
|
|
227
|
+
{primaryButton?.label}
|
|
228
|
+
</Button>
|
|
229
|
+
)}
|
|
230
|
+
{secondaryButton?.label && (
|
|
231
|
+
<Button
|
|
232
|
+
as="link"
|
|
233
|
+
ariaLabel={secondaryButton?.label}
|
|
234
|
+
link={secondaryButton}
|
|
235
|
+
className="block px-4 py-3 mb-2 leading-loose text-center text-white font-semibold bg-primary hover:bg-primary-foreground rounded-global">
|
|
236
|
+
{secondaryButton?.label}
|
|
237
|
+
</Button>
|
|
238
|
+
)}
|
|
239
|
+
</div>
|
|
240
|
+
<Text fontSize="xs" className="my-4 text-center text-gray-900">
|
|
241
|
+
<span>{`© ${new Date().getFullYear()} All rights reserved.`}</span>
|
|
242
|
+
</Text>
|
|
243
|
+
</div>
|
|
244
|
+
</nav>
|
|
245
|
+
</div>
|
|
246
|
+
</React.Fragment>
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export { Navigation_B };
|