@windstream/react-shared-components 0.2.2 → 0.2.3
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/contentful/index.d.ts +2 -3
- package/dist/contentful/index.esm.js +2 -2
- package/dist/contentful/index.esm.js.map +1 -1
- package/dist/contentful/index.js +2 -2
- package/dist/contentful/index.js.map +1 -1
- package/dist/core.d.ts +3 -3
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/components/pagination/index.test.tsx +117 -79
- package/src/components/pagination/index.tsx +41 -19
- package/src/components/pagination/types.ts +2 -1
- package/src/contentful/blocks/blogs-grid/index.tsx +24 -3
- package/src/contentful/blocks/blogs-grid-base/index.tsx +2 -0
- package/src/contentful/blocks/blogs-grid-base/types.ts +1 -0
- package/src/contentful/blocks/breadcrumbs/index.tsx +95 -95
- package/src/contentful/blocks/callout/index.tsx +1 -1
- package/src/contentful/blocks/cards/blog-card/index.test.tsx +4 -9
- package/src/contentful/blocks/cards/blog-card/index.tsx +1 -4
- package/src/contentful/blocks/cards/blog-card/types.ts +0 -2
- package/src/contentful/blocks/cards/floating-image-card/index.tsx +2 -2
- package/src/contentful/blocks/find-kinetic/index.tsx +147 -147
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useEffect, useRef } from "react";
|
|
3
4
|
import { BlogCategoryOption, BlogGridProps } from "./types";
|
|
4
5
|
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
|
5
6
|
|
|
@@ -19,6 +20,15 @@ export function BlogGrid({
|
|
|
19
20
|
const router = useRouter();
|
|
20
21
|
const pathname = usePathname();
|
|
21
22
|
const searchParams = useSearchParams();
|
|
23
|
+
const isInitialMount = useRef(true);
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (isInitialMount.current) {
|
|
27
|
+
isInitialMount.current = false;
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
window.scrollTo({ top: 0, behavior: "smooth" });
|
|
31
|
+
}, [currentPage]);
|
|
22
32
|
|
|
23
33
|
function updateQueryParams(key: string, value: string) {
|
|
24
34
|
const params = new URLSearchParams(searchParams.toString());
|
|
@@ -40,9 +50,19 @@ export function BlogGrid({
|
|
|
40
50
|
updateQueryParams("category", cat.value === "all" ? "" : cat.value);
|
|
41
51
|
}
|
|
42
52
|
|
|
43
|
-
function handlePageChange(
|
|
44
|
-
|
|
45
|
-
|
|
53
|
+
function handlePageChange(_page: number) {
|
|
54
|
+
// Scroll is handled by the useEffect on currentPage change
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function getPageHref(page: number): string {
|
|
58
|
+
const params = new URLSearchParams(searchParams.toString());
|
|
59
|
+
if (page === 1) {
|
|
60
|
+
params.delete("page");
|
|
61
|
+
} else {
|
|
62
|
+
params.set("page", page.toString());
|
|
63
|
+
}
|
|
64
|
+
const qs = params.toString();
|
|
65
|
+
return qs ? `${pathname}?${qs}` : pathname;
|
|
46
66
|
}
|
|
47
67
|
|
|
48
68
|
return (
|
|
@@ -125,6 +145,7 @@ export function BlogGrid({
|
|
|
125
145
|
<Pagination
|
|
126
146
|
currentPage={currentPage}
|
|
127
147
|
totalPages={totalPages}
|
|
148
|
+
getPageHref={getPageHref}
|
|
128
149
|
onPageChange={handlePageChange}
|
|
129
150
|
ariaLabel="Blog article pagination"
|
|
130
151
|
/>
|
|
@@ -15,6 +15,7 @@ export function BlogGridBase({
|
|
|
15
15
|
categoryOptions,
|
|
16
16
|
onCategoryChange,
|
|
17
17
|
onPageChange,
|
|
18
|
+
getPageHref,
|
|
18
19
|
imageComponent,
|
|
19
20
|
}: BlogGridBaseProps) {
|
|
20
21
|
function handleCategoryChange(value: unknown) {
|
|
@@ -110,6 +111,7 @@ export function BlogGridBase({
|
|
|
110
111
|
<Pagination
|
|
111
112
|
currentPage={currentPage}
|
|
112
113
|
totalPages={totalPages}
|
|
114
|
+
getPageHref={getPageHref}
|
|
113
115
|
onPageChange={handlePageChange}
|
|
114
116
|
ariaLabel="Blog article pagination"
|
|
115
117
|
/>
|
|
@@ -26,6 +26,7 @@ export interface BlogGridBaseProps {
|
|
|
26
26
|
categoryOptions: BlogCategoryOption[];
|
|
27
27
|
onCategoryChange?: (category: BlogCategoryOption) => void;
|
|
28
28
|
onPageChange?: (page: number) => void;
|
|
29
|
+
getPageHref: (page: number) => string;
|
|
29
30
|
/** Pass next/image's Image component here to enable image optimization */
|
|
30
31
|
imageComponent?: React.ComponentType<BlogCardImageProps>;
|
|
31
32
|
}
|
|
@@ -1,95 +1,95 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { BreadcrumbNavigationProps } from "./types";
|
|
3
|
-
|
|
4
|
-
import { MaterialIcon } from "@shared/components/material-icon";
|
|
5
|
-
import { Text } from "@shared/components/text";
|
|
6
|
-
|
|
7
|
-
export const BreadcrumbNavigation: React.FC<
|
|
8
|
-
BreadcrumbNavigationProps
|
|
9
|
-
> = props => {
|
|
10
|
-
const {
|
|
11
|
-
links = [],
|
|
12
|
-
textColor = "dark",
|
|
13
|
-
mobileTextColor,
|
|
14
|
-
desktopTextColor,
|
|
15
|
-
maxWidth = true,
|
|
16
|
-
float = "desktop",
|
|
17
|
-
} = props;
|
|
18
|
-
const resolvedMobile =
|
|
19
|
-
mobileTextColor ?? (float === "desktop" ? "dark" : textColor);
|
|
20
|
-
const resolvedDesktop =
|
|
21
|
-
desktopTextColor ?? (float === "mobile" ? "dark" : textColor);
|
|
22
|
-
const mobileColor = resolvedMobile === "dark" ? "text-text" : "text-white";
|
|
23
|
-
const desktopColor =
|
|
24
|
-
resolvedDesktop === "dark" ? "xl:text-text" : "xl:text-white";
|
|
25
|
-
const color = `${mobileColor} ${desktopColor}`;
|
|
26
|
-
|
|
27
|
-
const olPosition =
|
|
28
|
-
float === "always"
|
|
29
|
-
? "absolute"
|
|
30
|
-
: float === "mobile"
|
|
31
|
-
? "absolute xl:relative"
|
|
32
|
-
: float === "desktop"
|
|
33
|
-
? "relative xl:absolute"
|
|
34
|
-
: "relative";
|
|
35
|
-
|
|
36
|
-
if (!links.length) return null;
|
|
37
|
-
return (
|
|
38
|
-
<nav
|
|
39
|
-
aria-label="Breadcrumb"
|
|
40
|
-
className={`${maxWidth ? `${float === "none" && "mx-5"} max-w-120 xl:mx-auto` : "mx-auto"} relative`}
|
|
41
|
-
>
|
|
42
|
-
<ol
|
|
43
|
-
className={`${float === "always" && "px-7 md:px-14"} right-0 z-10 mx-0 flex w-full flex-nowrap items-center gap-2 pb-0 pt-8 ${olPosition} xl:mx-auto xl:flex-wrap xl:px-3`}
|
|
44
|
-
>
|
|
45
|
-
{links.map((link, index) => {
|
|
46
|
-
const { image, ...linkProps } = link;
|
|
47
|
-
const imageSrc = image?.url
|
|
48
|
-
? image.url.startsWith("//")
|
|
49
|
-
? `https:${image.url}`
|
|
50
|
-
: image.url
|
|
51
|
-
: "";
|
|
52
|
-
const isLast = index === links.length - 1;
|
|
53
|
-
return !isLast ? (
|
|
54
|
-
<li key={index} className="flex flex-none items-center">
|
|
55
|
-
{imageSrc && (
|
|
56
|
-
<img
|
|
57
|
-
src={imageSrc}
|
|
58
|
-
alt={image?.alt || ""}
|
|
59
|
-
width={20}
|
|
60
|
-
height={20}
|
|
61
|
-
className="mr-2 h-10 w-10"
|
|
62
|
-
/>
|
|
63
|
-
)}
|
|
64
|
-
<a
|
|
65
|
-
href={linkProps.href}
|
|
66
|
-
className={`label3 mr-2 whitespace-nowrap ${color} hover:underline`}
|
|
67
|
-
>
|
|
68
|
-
{linkProps.buttonLabel}
|
|
69
|
-
</a>
|
|
70
|
-
|
|
71
|
-
<MaterialIcon name="chevron_right" className={`${color} `} />
|
|
72
|
-
</li>
|
|
73
|
-
) : (
|
|
74
|
-
<li key={index} className="flex min-w-0 items-center">
|
|
75
|
-
{imageSrc && (
|
|
76
|
-
<img
|
|
77
|
-
src={imageSrc}
|
|
78
|
-
alt={image?.alt || ""}
|
|
79
|
-
width={20}
|
|
80
|
-
height={20}
|
|
81
|
-
className="mr-2 h-10 w-10"
|
|
82
|
-
/>
|
|
83
|
-
)}
|
|
84
|
-
<Text as="span" className={`body3 mr-2 break-words ${color}`}>
|
|
85
|
-
{link.buttonLabel}
|
|
86
|
-
</Text>
|
|
87
|
-
</li>
|
|
88
|
-
);
|
|
89
|
-
})}
|
|
90
|
-
</ol>
|
|
91
|
-
</nav>
|
|
92
|
-
);
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
export default BreadcrumbNavigation;
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { BreadcrumbNavigationProps } from "./types";
|
|
3
|
+
|
|
4
|
+
import { MaterialIcon } from "@shared/components/material-icon";
|
|
5
|
+
import { Text } from "@shared/components/text";
|
|
6
|
+
|
|
7
|
+
export const BreadcrumbNavigation: React.FC<
|
|
8
|
+
BreadcrumbNavigationProps
|
|
9
|
+
> = props => {
|
|
10
|
+
const {
|
|
11
|
+
links = [],
|
|
12
|
+
textColor = "dark",
|
|
13
|
+
mobileTextColor,
|
|
14
|
+
desktopTextColor,
|
|
15
|
+
maxWidth = true,
|
|
16
|
+
float = "desktop",
|
|
17
|
+
} = props;
|
|
18
|
+
const resolvedMobile =
|
|
19
|
+
mobileTextColor ?? (float === "desktop" ? "dark" : textColor);
|
|
20
|
+
const resolvedDesktop =
|
|
21
|
+
desktopTextColor ?? (float === "mobile" ? "dark" : textColor);
|
|
22
|
+
const mobileColor = resolvedMobile === "dark" ? "text-text" : "text-white";
|
|
23
|
+
const desktopColor =
|
|
24
|
+
resolvedDesktop === "dark" ? "xl:text-text" : "xl:text-white";
|
|
25
|
+
const color = `${mobileColor} ${desktopColor}`;
|
|
26
|
+
|
|
27
|
+
const olPosition =
|
|
28
|
+
float === "always"
|
|
29
|
+
? "absolute"
|
|
30
|
+
: float === "mobile"
|
|
31
|
+
? "absolute xl:relative"
|
|
32
|
+
: float === "desktop"
|
|
33
|
+
? "relative xl:absolute"
|
|
34
|
+
: "relative";
|
|
35
|
+
|
|
36
|
+
if (!links.length) return null;
|
|
37
|
+
return (
|
|
38
|
+
<nav
|
|
39
|
+
aria-label="Breadcrumb"
|
|
40
|
+
className={`${maxWidth ? `${float === "none" && "mx-5"} max-w-120 xl:mx-auto` : "mx-auto"} relative`}
|
|
41
|
+
>
|
|
42
|
+
<ol
|
|
43
|
+
className={`${float === "always" && "px-7 md:px-14"} right-0 z-10 mx-0 flex w-full flex-nowrap items-center gap-2 pb-0 pt-8 ${olPosition} xl:mx-auto xl:flex-wrap xl:px-3`}
|
|
44
|
+
>
|
|
45
|
+
{links.map((link, index) => {
|
|
46
|
+
const { image, ...linkProps } = link;
|
|
47
|
+
const imageSrc = image?.url
|
|
48
|
+
? image.url.startsWith("//")
|
|
49
|
+
? `https:${image.url}`
|
|
50
|
+
: image.url
|
|
51
|
+
: "";
|
|
52
|
+
const isLast = index === links.length - 1;
|
|
53
|
+
return !isLast ? (
|
|
54
|
+
<li key={index} className="flex flex-none items-center">
|
|
55
|
+
{imageSrc && (
|
|
56
|
+
<img
|
|
57
|
+
src={imageSrc}
|
|
58
|
+
alt={image?.alt || ""}
|
|
59
|
+
width={20}
|
|
60
|
+
height={20}
|
|
61
|
+
className="mr-2 h-10 w-10"
|
|
62
|
+
/>
|
|
63
|
+
)}
|
|
64
|
+
<a
|
|
65
|
+
href={linkProps.href}
|
|
66
|
+
className={`label3 mr-2 whitespace-nowrap ${color} hover:underline`}
|
|
67
|
+
>
|
|
68
|
+
{linkProps.buttonLabel}
|
|
69
|
+
</a>
|
|
70
|
+
|
|
71
|
+
<MaterialIcon name="chevron_right" className={`${color} `} />
|
|
72
|
+
</li>
|
|
73
|
+
) : (
|
|
74
|
+
<li key={index} className="flex min-w-0 items-center">
|
|
75
|
+
{imageSrc && (
|
|
76
|
+
<img
|
|
77
|
+
src={imageSrc}
|
|
78
|
+
alt={image?.alt || ""}
|
|
79
|
+
width={20}
|
|
80
|
+
height={20}
|
|
81
|
+
className="mr-2 h-10 w-10"
|
|
82
|
+
/>
|
|
83
|
+
)}
|
|
84
|
+
<Text as="span" className={`body3 mr-2 break-words ${color}`}>
|
|
85
|
+
{link.buttonLabel}
|
|
86
|
+
</Text>
|
|
87
|
+
</li>
|
|
88
|
+
);
|
|
89
|
+
})}
|
|
90
|
+
</ol>
|
|
91
|
+
</nav>
|
|
92
|
+
);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export default BreadcrumbNavigation;
|
|
@@ -95,7 +95,7 @@ export const Callout: React.FC<CalloutProps> = ({
|
|
|
95
95
|
// flex-wrap layout that always renders multi-column on md+ and
|
|
96
96
|
// full-width on mobile. This matches the pre-0.1.79 DOM and keeps
|
|
97
97
|
// consumers like SMB-browse rendering correctly without changes.
|
|
98
|
-
const isStackMode = cardsWidth === false;
|
|
98
|
+
const isStackMode = cardsWidth === false && cardType !== "blog";
|
|
99
99
|
const gridClass = isStackMode
|
|
100
100
|
? cx(
|
|
101
101
|
"flex flex-col items-stretch self-stretch",
|
|
@@ -149,18 +149,13 @@ describe("BlogCard", () => {
|
|
|
149
149
|
expect(article?.className).toContain("callout-card");
|
|
150
150
|
});
|
|
151
151
|
|
|
152
|
-
it("applies
|
|
152
|
+
it("applies h-full in non-grid mode for equal height cards", () => {
|
|
153
153
|
const { container } = render(
|
|
154
|
-
<BlogCard
|
|
155
|
-
{...defaultProps}
|
|
156
|
-
asGrid={false}
|
|
157
|
-
lgWidth="lg:w-1/3"
|
|
158
|
-
mdWidth="md:w-1/2"
|
|
159
|
-
/>
|
|
154
|
+
<BlogCard {...defaultProps} asGrid={false} />
|
|
160
155
|
);
|
|
161
156
|
const article = container.querySelector("article");
|
|
162
|
-
expect(article?.className).toContain("
|
|
163
|
-
expect(article?.className).toContain("
|
|
157
|
+
expect(article?.className).toContain("h-full");
|
|
158
|
+
expect(article?.className).toContain("flex-col");
|
|
164
159
|
});
|
|
165
160
|
|
|
166
161
|
it("applies data-section-type attribute", () => {
|
|
@@ -23,14 +23,11 @@ export const BlogCard: React.FC<BlogCardProps> = ({
|
|
|
23
23
|
imageComponent: ImageComponent,
|
|
24
24
|
readMoreText = "Read more",
|
|
25
25
|
asGrid = true,
|
|
26
|
-
lgWidth,
|
|
27
|
-
mdWidth,
|
|
28
26
|
index,
|
|
29
27
|
}: BlogCardProps) => {
|
|
30
|
-
const basehwClass = "lg:w-[calc(33.3333%-1rem)] md:w-[calc(50%-1rem)]";
|
|
31
28
|
const parentClassName = asGrid
|
|
32
29
|
? "flex h-full flex-col overflow-hidden rounded-card-lg bg-white shadow-drop transition-all duration-200 hover:-translate-y-0.5 hover:shadow-cardDrop"
|
|
33
|
-
: `callout-card
|
|
30
|
+
: `callout-card w-full flex h-full flex-col overflow-hidden rounded-card-lg bg-white shadow-drop transition-all duration-200 hover:-translate-y-0.5 hover:shadow-cardDrop`;
|
|
34
31
|
|
|
35
32
|
return (
|
|
36
33
|
<article
|
|
@@ -79,10 +79,10 @@ export const FloatingImageCard: React.FC<FloatingImageCardProps> = ({
|
|
|
79
79
|
src={image.href}
|
|
80
80
|
alt={image.title ?? title ?? "card-image"}
|
|
81
81
|
width={image.width || 400}
|
|
82
|
-
height={image.height ||
|
|
82
|
+
height={image.height || 330}
|
|
83
83
|
onClick={handleImageClick}
|
|
84
84
|
className={cx(
|
|
85
|
-
"h-full max-h-[
|
|
85
|
+
"h-full max-h-[330px] w-full rounded-[40px] object-cover",
|
|
86
86
|
imageLinkAvailableClass
|
|
87
87
|
)}
|
|
88
88
|
/>
|
|
@@ -1,147 +1,147 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { FindKineticProps, ThemeKey } from "./types";
|
|
3
|
-
|
|
4
|
-
import { Link } from "@shared/components/link";
|
|
5
|
-
import { Text } from "@shared/components/text";
|
|
6
|
-
import { NextImage } from "@shared/next";
|
|
7
|
-
|
|
8
|
-
export const FindKinetic: React.FC<FindKineticProps> = ({
|
|
9
|
-
background = "white",
|
|
10
|
-
description,
|
|
11
|
-
enableHeading,
|
|
12
|
-
image,
|
|
13
|
-
list = [],
|
|
14
|
-
localPathPrefix = "/local",
|
|
15
|
-
subTitle,
|
|
16
|
-
title,
|
|
17
|
-
color = "dark",
|
|
18
|
-
maxWidth = true,
|
|
19
|
-
}) => {
|
|
20
|
-
const bgColorClasses: Record<ThemeKey, string> = {
|
|
21
|
-
blue: "bg-[#07B2E2]",
|
|
22
|
-
green: "bg-[#26B170]",
|
|
23
|
-
yellow: "bg-[#F5FF1E]",
|
|
24
|
-
purple: "bg-[#931D69]",
|
|
25
|
-
white: "bg-white",
|
|
26
|
-
navy: "bg-[#00002D]",
|
|
27
|
-
};
|
|
28
|
-
const normalizedLocalPathPrefix = localPathPrefix
|
|
29
|
-
? `/${localPathPrefix.replace(/^\/+|\/+$/g, "")}`
|
|
30
|
-
: "";
|
|
31
|
-
|
|
32
|
-
const getLocationHref = (code: string) =>
|
|
33
|
-
`${normalizedLocalPathPrefix}/${code.toLowerCase()}`;
|
|
34
|
-
|
|
35
|
-
return (
|
|
36
|
-
<div
|
|
37
|
-
className={`${bgColorClasses[background]} component-container overflow-hidden ${color == "dark" ? "text-text" : "text-white"}`}
|
|
38
|
-
>
|
|
39
|
-
<div
|
|
40
|
-
className={`${maxWidth ? "max-w-120 xl:mx-auto" : ""} mx-5 mb-5 ${image ? "mt-12" : "mt-0 xl:mt-0"} xl:my-20`}
|
|
41
|
-
>
|
|
42
|
-
{title && (
|
|
43
|
-
<Text
|
|
44
|
-
as={enableHeading ? "h1" : "h2"}
|
|
45
|
-
className="heading2 md:heading1 md:text-center"
|
|
46
|
-
>
|
|
47
|
-
{title}
|
|
48
|
-
</Text>
|
|
49
|
-
)}
|
|
50
|
-
{subTitle && (
|
|
51
|
-
<Text
|
|
52
|
-
as={enableHeading ? "h2" : "h3"}
|
|
53
|
-
className="subheading1 mt-4 md:text-center"
|
|
54
|
-
>
|
|
55
|
-
{subTitle}
|
|
56
|
-
</Text>
|
|
57
|
-
)}
|
|
58
|
-
|
|
59
|
-
<div
|
|
60
|
-
className={`mt-8 flex flex-col items-center xl:flex-row ${image ? "xl:mt-16" : "xl:mt-10"} gap-10`}
|
|
61
|
-
>
|
|
62
|
-
<div
|
|
63
|
-
className={`flex flex-col ${image ? "xl:items-start" : "items-center"} gap-10 self-stretch xl:flex-[1_0_0]`}
|
|
64
|
-
>
|
|
65
|
-
{description && (
|
|
66
|
-
<Text as="p" className="body1">
|
|
67
|
-
{description}
|
|
68
|
-
</Text>
|
|
69
|
-
)}
|
|
70
|
-
{list.length > 0 && (
|
|
71
|
-
<>
|
|
72
|
-
<ul className="grid w-full grid-cols-2 gap-x-6 gap-y-5 md:hidden">
|
|
73
|
-
{(() => {
|
|
74
|
-
const sortedList = [...list].sort((a, b) =>
|
|
75
|
-
a.name.localeCompare(b.name)
|
|
76
|
-
);
|
|
77
|
-
const numColumns = 2;
|
|
78
|
-
const numRows = Math.ceil(sortedList.length / numColumns);
|
|
79
|
-
const rearranged = [];
|
|
80
|
-
for (let row = 0; row < numRows; row++) {
|
|
81
|
-
for (let col = 0; col < numColumns; col++) {
|
|
82
|
-
const index = col * numRows + row;
|
|
83
|
-
if (index < sortedList.length) {
|
|
84
|
-
rearranged.push(sortedList[index]);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
return rearranged.map((item, index: number) => (
|
|
89
|
-
<li key={`item-list-2-${index}`}>
|
|
90
|
-
<Link
|
|
91
|
-
href={getLocationHref(item.code)}
|
|
92
|
-
className="label1"
|
|
93
|
-
>
|
|
94
|
-
{item.name}
|
|
95
|
-
</Link>
|
|
96
|
-
</li>
|
|
97
|
-
));
|
|
98
|
-
})()}
|
|
99
|
-
</ul>
|
|
100
|
-
<ul
|
|
101
|
-
className={`hidden ${image ? "gap-x-6" : "gap-x-20"} gap-y-5 md:grid ${image ? "md:grid-cols-3" : "md:grid-cols-4"}`}
|
|
102
|
-
>
|
|
103
|
-
{(() => {
|
|
104
|
-
const sortedList = [...list].sort((a, b) =>
|
|
105
|
-
a.name.localeCompare(b.name)
|
|
106
|
-
);
|
|
107
|
-
const numColumns = 3;
|
|
108
|
-
const numRows = Math.ceil(sortedList.length / numColumns);
|
|
109
|
-
const rearranged = [];
|
|
110
|
-
for (let row = 0; row < numRows; row++) {
|
|
111
|
-
for (let col = 0; col < numColumns; col++) {
|
|
112
|
-
const index = col * numRows + row;
|
|
113
|
-
if (index < sortedList.length) {
|
|
114
|
-
rearranged.push(sortedList[index]);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
return rearranged.map((item, index: number) => (
|
|
119
|
-
<li
|
|
120
|
-
key={`item-list-3-${index}`}
|
|
121
|
-
className={`${image ? "" : "w-[172px]"}`}
|
|
122
|
-
>
|
|
123
|
-
<Link
|
|
124
|
-
href={getLocationHref(item.code)}
|
|
125
|
-
className="label1"
|
|
126
|
-
>
|
|
127
|
-
{item.name}
|
|
128
|
-
</Link>
|
|
129
|
-
</li>
|
|
130
|
-
));
|
|
131
|
-
})()}
|
|
132
|
-
</ul>
|
|
133
|
-
</>
|
|
134
|
-
)}
|
|
135
|
-
</div>
|
|
136
|
-
{image && (
|
|
137
|
-
<aside className="hidden md:block">
|
|
138
|
-
<NextImage width={472} height={100} src={image} alt="image" />
|
|
139
|
-
</aside>
|
|
140
|
-
)}
|
|
141
|
-
</div>
|
|
142
|
-
</div>
|
|
143
|
-
</div>
|
|
144
|
-
);
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
export default FindKinetic;
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { FindKineticProps, ThemeKey } from "./types";
|
|
3
|
+
|
|
4
|
+
import { Link } from "@shared/components/link";
|
|
5
|
+
import { Text } from "@shared/components/text";
|
|
6
|
+
import { NextImage } from "@shared/next";
|
|
7
|
+
|
|
8
|
+
export const FindKinetic: React.FC<FindKineticProps> = ({
|
|
9
|
+
background = "white",
|
|
10
|
+
description,
|
|
11
|
+
enableHeading,
|
|
12
|
+
image,
|
|
13
|
+
list = [],
|
|
14
|
+
localPathPrefix = "/local",
|
|
15
|
+
subTitle,
|
|
16
|
+
title,
|
|
17
|
+
color = "dark",
|
|
18
|
+
maxWidth = true,
|
|
19
|
+
}) => {
|
|
20
|
+
const bgColorClasses: Record<ThemeKey, string> = {
|
|
21
|
+
blue: "bg-[#07B2E2]",
|
|
22
|
+
green: "bg-[#26B170]",
|
|
23
|
+
yellow: "bg-[#F5FF1E]",
|
|
24
|
+
purple: "bg-[#931D69]",
|
|
25
|
+
white: "bg-white",
|
|
26
|
+
navy: "bg-[#00002D]",
|
|
27
|
+
};
|
|
28
|
+
const normalizedLocalPathPrefix = localPathPrefix
|
|
29
|
+
? `/${localPathPrefix.replace(/^\/+|\/+$/g, "")}`
|
|
30
|
+
: "";
|
|
31
|
+
|
|
32
|
+
const getLocationHref = (code: string) =>
|
|
33
|
+
`${normalizedLocalPathPrefix}/${code.toLowerCase()}`;
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div
|
|
37
|
+
className={`${bgColorClasses[background]} component-container overflow-hidden ${color == "dark" ? "text-text" : "text-white"}`}
|
|
38
|
+
>
|
|
39
|
+
<div
|
|
40
|
+
className={`${maxWidth ? "max-w-120 xl:mx-auto" : ""} mx-5 mb-5 ${image ? "mt-12" : "mt-0 xl:mt-0"} xl:my-20`}
|
|
41
|
+
>
|
|
42
|
+
{title && (
|
|
43
|
+
<Text
|
|
44
|
+
as={enableHeading ? "h1" : "h2"}
|
|
45
|
+
className="heading2 md:heading1 md:text-center"
|
|
46
|
+
>
|
|
47
|
+
{title}
|
|
48
|
+
</Text>
|
|
49
|
+
)}
|
|
50
|
+
{subTitle && (
|
|
51
|
+
<Text
|
|
52
|
+
as={enableHeading ? "h2" : "h3"}
|
|
53
|
+
className="subheading1 mt-4 md:text-center"
|
|
54
|
+
>
|
|
55
|
+
{subTitle}
|
|
56
|
+
</Text>
|
|
57
|
+
)}
|
|
58
|
+
|
|
59
|
+
<div
|
|
60
|
+
className={`mt-8 flex flex-col items-center xl:flex-row ${image ? "xl:mt-16" : "xl:mt-10"} gap-10`}
|
|
61
|
+
>
|
|
62
|
+
<div
|
|
63
|
+
className={`flex flex-col ${image ? "xl:items-start" : "items-center"} gap-10 self-stretch xl:flex-[1_0_0]`}
|
|
64
|
+
>
|
|
65
|
+
{description && (
|
|
66
|
+
<Text as="p" className="body1">
|
|
67
|
+
{description}
|
|
68
|
+
</Text>
|
|
69
|
+
)}
|
|
70
|
+
{list.length > 0 && (
|
|
71
|
+
<>
|
|
72
|
+
<ul className="grid w-full grid-cols-2 gap-x-6 gap-y-5 md:hidden">
|
|
73
|
+
{(() => {
|
|
74
|
+
const sortedList = [...list].sort((a, b) =>
|
|
75
|
+
a.name.localeCompare(b.name)
|
|
76
|
+
);
|
|
77
|
+
const numColumns = 2;
|
|
78
|
+
const numRows = Math.ceil(sortedList.length / numColumns);
|
|
79
|
+
const rearranged = [];
|
|
80
|
+
for (let row = 0; row < numRows; row++) {
|
|
81
|
+
for (let col = 0; col < numColumns; col++) {
|
|
82
|
+
const index = col * numRows + row;
|
|
83
|
+
if (index < sortedList.length) {
|
|
84
|
+
rearranged.push(sortedList[index]);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return rearranged.map((item, index: number) => (
|
|
89
|
+
<li key={`item-list-2-${index}`}>
|
|
90
|
+
<Link
|
|
91
|
+
href={getLocationHref(item.code)}
|
|
92
|
+
className="label1"
|
|
93
|
+
>
|
|
94
|
+
{item.name}
|
|
95
|
+
</Link>
|
|
96
|
+
</li>
|
|
97
|
+
));
|
|
98
|
+
})()}
|
|
99
|
+
</ul>
|
|
100
|
+
<ul
|
|
101
|
+
className={`hidden ${image ? "gap-x-6" : "gap-x-20"} gap-y-5 md:grid ${image ? "md:grid-cols-3" : "md:grid-cols-4"}`}
|
|
102
|
+
>
|
|
103
|
+
{(() => {
|
|
104
|
+
const sortedList = [...list].sort((a, b) =>
|
|
105
|
+
a.name.localeCompare(b.name)
|
|
106
|
+
);
|
|
107
|
+
const numColumns = 3;
|
|
108
|
+
const numRows = Math.ceil(sortedList.length / numColumns);
|
|
109
|
+
const rearranged = [];
|
|
110
|
+
for (let row = 0; row < numRows; row++) {
|
|
111
|
+
for (let col = 0; col < numColumns; col++) {
|
|
112
|
+
const index = col * numRows + row;
|
|
113
|
+
if (index < sortedList.length) {
|
|
114
|
+
rearranged.push(sortedList[index]);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return rearranged.map((item, index: number) => (
|
|
119
|
+
<li
|
|
120
|
+
key={`item-list-3-${index}`}
|
|
121
|
+
className={`${image ? "" : "w-[172px]"}`}
|
|
122
|
+
>
|
|
123
|
+
<Link
|
|
124
|
+
href={getLocationHref(item.code)}
|
|
125
|
+
className="label1"
|
|
126
|
+
>
|
|
127
|
+
{item.name}
|
|
128
|
+
</Link>
|
|
129
|
+
</li>
|
|
130
|
+
));
|
|
131
|
+
})()}
|
|
132
|
+
</ul>
|
|
133
|
+
</>
|
|
134
|
+
)}
|
|
135
|
+
</div>
|
|
136
|
+
{image && (
|
|
137
|
+
<aside className="hidden md:block">
|
|
138
|
+
<NextImage width={472} height={100} src={image} alt="image" />
|
|
139
|
+
</aside>
|
|
140
|
+
)}
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
export default FindKinetic;
|