@stackshift-ui/portfolio 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-4Y7ZRUFQ.mjs +1 -0
- package/dist/{chunk-6QPID4RW.mjs → chunk-AC3OTID7.mjs} +1 -1
- package/dist/chunk-ULBLZJEZ.mjs +1 -0
- package/dist/chunk-VJQNAKCY.mjs +1 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/portfolio.js +1 -1
- package/dist/portfolio.mjs +1 -1
- package/dist/portfolio_a.js +1 -1
- package/dist/portfolio_a.mjs +1 -1
- package/dist/portfolio_b.js +1 -1
- package/dist/portfolio_b.mjs +1 -1
- package/dist/portfolio_c.js +1 -1
- package/dist/portfolio_c.mjs +1 -1
- package/dist/portfolio_d.js +1 -1
- package/dist/portfolio_d.mjs +1 -1
- package/package.json +13 -12
- package/src/helper/index.ts +27 -0
- package/src/index.ts +8 -0
- package/src/portfolio.test.tsx +13 -0
- package/src/portfolio.tsx +39 -0
- package/src/portfolio_a.tsx +146 -0
- package/src/portfolio_b.tsx +127 -0
- package/src/portfolio_c.tsx +124 -0
- package/src/portfolio_d.tsx +184 -0
- package/src/types.ts +412 -0
- package/dist/chunk-AQJ5HHVM.mjs +0 -1
- package/dist/chunk-EFEWEAHL.mjs +0 -1
- package/dist/chunk-JF63GJ27.mjs +0 -1
- /package/dist/{chunk-DU3VU22S.mjs → chunk-NFRBR5UV.mjs} +0 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { Button } from "@stackshift-ui/button";
|
|
2
|
+
import { Container } from "@stackshift-ui/container";
|
|
3
|
+
import { Flex } from "@stackshift-ui/flex";
|
|
4
|
+
import { Heading } from "@stackshift-ui/heading";
|
|
5
|
+
import { Image } from "@stackshift-ui/image";
|
|
6
|
+
import { Section } from "@stackshift-ui/section";
|
|
7
|
+
import { Text } from "@stackshift-ui/text";
|
|
8
|
+
import React from "react";
|
|
9
|
+
|
|
10
|
+
import { PortfolioProps } from ".";
|
|
11
|
+
import { Content, LabeledRoute, PortfoliosWithCategories } from "./types";
|
|
12
|
+
|
|
13
|
+
export default function Portfolio_A({
|
|
14
|
+
caption,
|
|
15
|
+
title,
|
|
16
|
+
portfoliosWithCategory,
|
|
17
|
+
primaryButton,
|
|
18
|
+
length = 8,
|
|
19
|
+
}: PortfolioProps): React.JSX.Element {
|
|
20
|
+
const portfolioLength = length; //set initial number of portfolios to display for this variant
|
|
21
|
+
const [activeTab, setActiveTab] = React.useState(portfoliosWithCategory?.[0]?.category); //set the first index category as initial value
|
|
22
|
+
|
|
23
|
+
//creates new array of items filtered by active tab
|
|
24
|
+
const portfoliosPerCategory = portfoliosWithCategory?.find(data => data?.category === activeTab);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<Section className="py-20 bg-background">
|
|
28
|
+
<Container maxWidth={1280}>
|
|
29
|
+
<Container maxWidth={512} className="mb-8 text-center md:mb-16">
|
|
30
|
+
<CaptionAndTitleText caption={caption} title={title} />
|
|
31
|
+
<PortfolioCategories
|
|
32
|
+
categories={portfoliosWithCategory}
|
|
33
|
+
activeTab={activeTab}
|
|
34
|
+
onClickFn={setActiveTab}
|
|
35
|
+
/>
|
|
36
|
+
</Container>
|
|
37
|
+
<PortfolioContent
|
|
38
|
+
portfolios={portfoliosPerCategory?.content}
|
|
39
|
+
portfolioLength={portfolioLength}
|
|
40
|
+
/>
|
|
41
|
+
<PrimaryButton button={primaryButton} />
|
|
42
|
+
</Container>
|
|
43
|
+
</Section>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function CaptionAndTitleText({
|
|
48
|
+
caption,
|
|
49
|
+
title,
|
|
50
|
+
}: {
|
|
51
|
+
caption?: string | null;
|
|
52
|
+
title?: string | null;
|
|
53
|
+
}) {
|
|
54
|
+
if (!caption || !title) return null;
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<React.Fragment>
|
|
58
|
+
<Text weight="semibold">{caption}</Text>
|
|
59
|
+
<Heading className="mb-6" fontSize="2xl">
|
|
60
|
+
{title}
|
|
61
|
+
</Heading>
|
|
62
|
+
</React.Fragment>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function PrimaryButton({ button }: { button?: LabeledRoute | null }) {
|
|
67
|
+
if (!button?.label) return null;
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<div className="text-center">
|
|
71
|
+
<Button as="link" ariaLabel={button?.label} link={button}>
|
|
72
|
+
{button?.label}
|
|
73
|
+
</Button>
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function PortfolioCategories({
|
|
79
|
+
categories,
|
|
80
|
+
activeTab,
|
|
81
|
+
onClickFn,
|
|
82
|
+
}: {
|
|
83
|
+
categories?: PortfoliosWithCategories[] | null;
|
|
84
|
+
activeTab?: string | null;
|
|
85
|
+
onClickFn?: React.Dispatch<React.SetStateAction<string | null | undefined>>;
|
|
86
|
+
}) {
|
|
87
|
+
if (!categories || categories?.length === 0) return null;
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<Flex className="inline-flex py-1 text-sm bg-white rounded" wrap>
|
|
91
|
+
{categories?.map((content, index) => (
|
|
92
|
+
<Button
|
|
93
|
+
variant="tab"
|
|
94
|
+
as="button"
|
|
95
|
+
ariaLabel={content?.category ?? `Category button ${index + 1}`}
|
|
96
|
+
key={content?._key}
|
|
97
|
+
isActive={activeTab === content?.category}
|
|
98
|
+
onClick={() => onClickFn?.(content?.category)}>
|
|
99
|
+
{content?.category}
|
|
100
|
+
</Button>
|
|
101
|
+
))}
|
|
102
|
+
</Flex>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function PortfolioContent({
|
|
107
|
+
portfolios,
|
|
108
|
+
portfolioLength,
|
|
109
|
+
}: {
|
|
110
|
+
portfolios?: Content[] | null;
|
|
111
|
+
portfolioLength?: number;
|
|
112
|
+
}) {
|
|
113
|
+
if (!portfolios || portfolios?.length === 0) return null;
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<Flex wrap className="mb-8">
|
|
117
|
+
{portfolios?.slice(0, portfolioLength)?.map((content, index: number) => (
|
|
118
|
+
<Flex className="w-full space-x-5 px-4 mb-8 sm:w-1/2 lg:w-1/4" key={content?._key}>
|
|
119
|
+
<div className="relative mx-auto h-[256px] w-[332px] overflow-hidden rounded-global">
|
|
120
|
+
{content?.mainImage?.image && (
|
|
121
|
+
<Image
|
|
122
|
+
className="object-cover w-full h-full"
|
|
123
|
+
src={content?.mainImage?.image}
|
|
124
|
+
alt={content?.mainImage?.alt ?? `portfolio-image-${index}`}
|
|
125
|
+
/>
|
|
126
|
+
)}
|
|
127
|
+
<div className="absolute inset-0 z-10 flex items-center justify-center duration-300 bg-slate-900 rounded-lg opacity-0 hover:opacity-75">
|
|
128
|
+
{content?.primaryButton?.label && (
|
|
129
|
+
<Button
|
|
130
|
+
as="link"
|
|
131
|
+
variant="outline"
|
|
132
|
+
ariaLabel={content?.primaryButton?.label}
|
|
133
|
+
link={content?.primaryButton}
|
|
134
|
+
className="bg-transparent border-secondary outline text-white hover:bg-secondary/20 hover:border-secondary/20 inline-block rounded-l-xl rounded-t-xl font-bold transition duration-200 px-3 py-4">
|
|
135
|
+
{content?.primaryButton?.label}
|
|
136
|
+
</Button>
|
|
137
|
+
)}
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
</Flex>
|
|
141
|
+
))}
|
|
142
|
+
</Flex>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export { Portfolio_A };
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { Button } from "@stackshift-ui/button";
|
|
2
|
+
import { Container } from "@stackshift-ui/container";
|
|
3
|
+
import { Flex } from "@stackshift-ui/flex";
|
|
4
|
+
import { Heading } from "@stackshift-ui/heading";
|
|
5
|
+
import { Image } from "@stackshift-ui/image";
|
|
6
|
+
import { Section } from "@stackshift-ui/section";
|
|
7
|
+
import { Text } from "@stackshift-ui/text";
|
|
8
|
+
import React from "react";
|
|
9
|
+
|
|
10
|
+
import { PortfolioProps } from ".";
|
|
11
|
+
import { Content } from "./types";
|
|
12
|
+
|
|
13
|
+
export default function Portfolio_B({
|
|
14
|
+
caption,
|
|
15
|
+
title,
|
|
16
|
+
portfolios,
|
|
17
|
+
primaryButton,
|
|
18
|
+
length = 6,
|
|
19
|
+
}: PortfolioProps): React.JSX.Element {
|
|
20
|
+
const portfolioLength = length; //set initial number of portfolios to display for this variant
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<Section className="py-20 bg-background">
|
|
24
|
+
<Container maxWidth={1280}>
|
|
25
|
+
<Flex wrap align="center" justify="center" className="mb-16 md:justify-between">
|
|
26
|
+
<CaptionAndTitleText caption={caption} title={title} />
|
|
27
|
+
<div className="hidden mt-5 text-right md:mt-0 lg:mt-0 lg:block xl:mt-0">
|
|
28
|
+
{primaryButton?.label && (
|
|
29
|
+
<Button as="link" ariaLabel={primaryButton?.label} link={primaryButton}>
|
|
30
|
+
{primaryButton?.label}
|
|
31
|
+
</Button>
|
|
32
|
+
)}
|
|
33
|
+
</div>
|
|
34
|
+
</Flex>
|
|
35
|
+
<PortfolioContent portfolios={portfolios} portfolioLength={portfolioLength} />
|
|
36
|
+
<div className="block mt-5 text-center md:mt-0 lg:mt-0 lg:hidden xl:mt-0">
|
|
37
|
+
{primaryButton?.label && (
|
|
38
|
+
<Button as="link" ariaLabel={primaryButton?.label} link={primaryButton}>
|
|
39
|
+
{primaryButton?.label}
|
|
40
|
+
</Button>
|
|
41
|
+
)}
|
|
42
|
+
</div>
|
|
43
|
+
</Container>
|
|
44
|
+
</Section>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function CaptionAndTitleText({
|
|
49
|
+
caption,
|
|
50
|
+
title,
|
|
51
|
+
}: {
|
|
52
|
+
caption?: string | null;
|
|
53
|
+
title?: string | null;
|
|
54
|
+
}) {
|
|
55
|
+
if (!caption || !title) return null;
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<div className="text-center lg:text-left">
|
|
59
|
+
<Text weight="semibold">{caption}</Text>
|
|
60
|
+
<Heading className="mb-6" fontSize="2xl">
|
|
61
|
+
{title}
|
|
62
|
+
</Heading>
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function ProjectItem({ content }: { content: Content }) {
|
|
68
|
+
return (
|
|
69
|
+
<div className="w-full px-4 mb-4 md:w-1/2 lg:w-1/3">
|
|
70
|
+
<div className="relative mx-auto overflow-hidden rounded-global md:mb-5">
|
|
71
|
+
{content?.mainImage?.image && (
|
|
72
|
+
<Image
|
|
73
|
+
className="object-cover w-full h-80"
|
|
74
|
+
src={content?.mainImage?.image}
|
|
75
|
+
width={480}
|
|
76
|
+
height={320}
|
|
77
|
+
alt={content?.mainImage?.alt ?? `portfolio-image`}
|
|
78
|
+
/>
|
|
79
|
+
)}
|
|
80
|
+
|
|
81
|
+
<Flex
|
|
82
|
+
direction="col"
|
|
83
|
+
align="start"
|
|
84
|
+
className="absolute inset-0 z-10 items-start p-6 duration-300 bg-slate-900 rounded opacity-0 hover:opacity-75">
|
|
85
|
+
<Text className="text-secondary-foreground">{content?.dateAdded}</Text>
|
|
86
|
+
{content?.title && (
|
|
87
|
+
<Text weight="bold" className="mb-auto text-white md:text-xl lg:text-2xl">
|
|
88
|
+
{content?.title?.length > 80
|
|
89
|
+
? `${content?.title?.substring(0, 80)}...`
|
|
90
|
+
: content?.title}
|
|
91
|
+
</Text>
|
|
92
|
+
)}
|
|
93
|
+
{content?.primaryButton?.label && (
|
|
94
|
+
<Button
|
|
95
|
+
as="link"
|
|
96
|
+
variant="outline"
|
|
97
|
+
ariaLabel={content?.primaryButton?.label}
|
|
98
|
+
link={content?.primaryButton}
|
|
99
|
+
className="bg-transparent border-secondary outline text-white hover:bg-secondary/20 hover:border-secondary/20 inline-block rounded-l-xl rounded-t-xl font-bold transition duration-200 px-3 py-4">
|
|
100
|
+
{content?.primaryButton?.label}
|
|
101
|
+
</Button>
|
|
102
|
+
)}
|
|
103
|
+
</Flex>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function PortfolioContent({
|
|
110
|
+
portfolios,
|
|
111
|
+
portfolioLength,
|
|
112
|
+
}: {
|
|
113
|
+
portfolios?: Content[] | null;
|
|
114
|
+
portfolioLength?: number;
|
|
115
|
+
}) {
|
|
116
|
+
if (!portfolios || portfolios?.length === 0) return null;
|
|
117
|
+
|
|
118
|
+
return (
|
|
119
|
+
<Flex wrap className="mb-4 ">
|
|
120
|
+
{portfolios
|
|
121
|
+
?.slice(0, portfolioLength)
|
|
122
|
+
.map(content => <ProjectItem content={content} key={content._key} />)}
|
|
123
|
+
</Flex>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export { Portfolio_B };
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { Button } from "@stackshift-ui/button";
|
|
2
|
+
import { Container } from "@stackshift-ui/container";
|
|
3
|
+
import { Flex } from "@stackshift-ui/flex";
|
|
4
|
+
import { Heading } from "@stackshift-ui/heading";
|
|
5
|
+
import { Image } from "@stackshift-ui/image";
|
|
6
|
+
import { Section } from "@stackshift-ui/section";
|
|
7
|
+
import { Text } from "@stackshift-ui/text";
|
|
8
|
+
|
|
9
|
+
import { PortfolioProps } from ".";
|
|
10
|
+
import { Content } from "./types";
|
|
11
|
+
|
|
12
|
+
export default function Portfolio_C({
|
|
13
|
+
caption,
|
|
14
|
+
title,
|
|
15
|
+
portfolios,
|
|
16
|
+
primaryButton,
|
|
17
|
+
length = 6,
|
|
18
|
+
}: PortfolioProps) {
|
|
19
|
+
const portfolioLength = length; //set initial number of portfolios to display for this variant
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<Section className="py-20 bg-background">
|
|
23
|
+
<Container maxWidth={1280}>
|
|
24
|
+
<Flex wrap align="center" justify="center" className="mb-16 md:justify-between">
|
|
25
|
+
<CaptionAndTitleText caption={caption} title={title} />
|
|
26
|
+
<div className="hidden mt-5 text-right md:mt-0 lg:mt-0 lg:block xl:mt-0">
|
|
27
|
+
{primaryButton?.label && (
|
|
28
|
+
<Button as="link" ariaLabel={primaryButton?.label} link={primaryButton}>
|
|
29
|
+
{primaryButton?.label}
|
|
30
|
+
</Button>
|
|
31
|
+
)}
|
|
32
|
+
</div>
|
|
33
|
+
</Flex>
|
|
34
|
+
<PortfolioContent portfolios={portfolios} portfolioLength={portfolioLength} />
|
|
35
|
+
<div className="block mt-5 text-center md:mt-0 lg:mt-0 lg:hidden xl:mt-0">
|
|
36
|
+
{primaryButton?.label && (
|
|
37
|
+
<Button as="link" ariaLabel={primaryButton?.label} link={primaryButton}>
|
|
38
|
+
{primaryButton?.label}
|
|
39
|
+
</Button>
|
|
40
|
+
)}
|
|
41
|
+
</div>
|
|
42
|
+
</Container>
|
|
43
|
+
</Section>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function CaptionAndTitleText({
|
|
48
|
+
caption,
|
|
49
|
+
title,
|
|
50
|
+
}: {
|
|
51
|
+
caption?: string | null;
|
|
52
|
+
title?: string | null;
|
|
53
|
+
}) {
|
|
54
|
+
if (!caption || !title) return null;
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<div className="text-center lg:text-left">
|
|
58
|
+
<Text weight="semibold">{caption}</Text>
|
|
59
|
+
<Heading className="mb-6" fontSize="2xl">
|
|
60
|
+
{title}
|
|
61
|
+
</Heading>
|
|
62
|
+
</div>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function ProjectItem({ content }: { content: Content }) {
|
|
67
|
+
return (
|
|
68
|
+
<div className="relative w-full px-4 mb-8 md:w-1/2 lg:w-1/3">
|
|
69
|
+
{content?.mainImage?.image && (
|
|
70
|
+
<div
|
|
71
|
+
className="h-full overflow-hidden bg-white rounded-global"
|
|
72
|
+
style={{ maxHeight: "600px" }}>
|
|
73
|
+
<Image
|
|
74
|
+
className="object-cover w-full h-80"
|
|
75
|
+
src={content?.mainImage?.image}
|
|
76
|
+
width={480}
|
|
77
|
+
height={320}
|
|
78
|
+
alt={content?.mainImage?.alt ?? `portfolio-image`}
|
|
79
|
+
/>
|
|
80
|
+
<div className="p-6">
|
|
81
|
+
<Text muted>{content?.dateAdded}</Text>
|
|
82
|
+
{content?.title && (
|
|
83
|
+
<Text weight="bold" fontSize="xl" className="mb-4">
|
|
84
|
+
{content?.title?.length > 31
|
|
85
|
+
? `${content?.title?.substring(0, 31)}...`
|
|
86
|
+
: content?.title}
|
|
87
|
+
</Text>
|
|
88
|
+
)}
|
|
89
|
+
{content?.primaryButton?.label && (
|
|
90
|
+
<Button
|
|
91
|
+
as="link"
|
|
92
|
+
variant="link"
|
|
93
|
+
ariaLabel={content?.primaryButton?.label}
|
|
94
|
+
link={content?.primaryButton}
|
|
95
|
+
className="font-bold no-underline text-primary transition-200 hover:text-primary/50">
|
|
96
|
+
{content?.primaryButton?.label}
|
|
97
|
+
</Button>
|
|
98
|
+
)}
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
)}
|
|
102
|
+
</div>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function PortfolioContent({
|
|
107
|
+
portfolios,
|
|
108
|
+
portfolioLength,
|
|
109
|
+
}: {
|
|
110
|
+
portfolios?: Content[] | null;
|
|
111
|
+
portfolioLength?: number;
|
|
112
|
+
}) {
|
|
113
|
+
if (!portfolios || portfolios?.length === 0) return null;
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<Flex wrap className="mb-4">
|
|
117
|
+
{portfolios
|
|
118
|
+
?.slice(0, portfolioLength)
|
|
119
|
+
.map(content => <ProjectItem content={content} key={content._key} />)}
|
|
120
|
+
</Flex>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export { Portfolio_C };
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { Button } from "@stackshift-ui/button";
|
|
2
|
+
import { Container } from "@stackshift-ui/container";
|
|
3
|
+
import { Flex } from "@stackshift-ui/flex";
|
|
4
|
+
import { Heading } from "@stackshift-ui/heading";
|
|
5
|
+
import { Image } from "@stackshift-ui/image";
|
|
6
|
+
import { Section } from "@stackshift-ui/section";
|
|
7
|
+
import { Text } from "@stackshift-ui/text";
|
|
8
|
+
import React from "react";
|
|
9
|
+
|
|
10
|
+
import { PortfolioProps } from ".";
|
|
11
|
+
import { useMediaQuery } from "./helper";
|
|
12
|
+
import { Content, LabeledRoute, PortfoliosWithCategories } from "./types";
|
|
13
|
+
|
|
14
|
+
export default function Portfolio_D({
|
|
15
|
+
caption,
|
|
16
|
+
title,
|
|
17
|
+
portfoliosWithCategory,
|
|
18
|
+
primaryButton,
|
|
19
|
+
length = 6,
|
|
20
|
+
}: PortfolioProps) {
|
|
21
|
+
const portfoliosPerPage = length;
|
|
22
|
+
const count = 0; // default number of portfolios per category
|
|
23
|
+
const [activeTab, setActiveTab] = React.useState(portfoliosWithCategory?.[0]?.category); // set the first index category as initial value
|
|
24
|
+
|
|
25
|
+
// group portfolios per category
|
|
26
|
+
const portfoliosPerCategory = portfoliosWithCategory?.find(data => data?.category === activeTab);
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<Section className="py-20 bg-background">
|
|
30
|
+
<Container maxWidth={1280}>
|
|
31
|
+
<Container maxWidth={512} className="mb-8 text-center md:mb-16">
|
|
32
|
+
<CaptionAndTitleText caption={caption} title={title} />
|
|
33
|
+
<PortfolioCategories
|
|
34
|
+
categories={portfoliosWithCategory}
|
|
35
|
+
activeTab={activeTab}
|
|
36
|
+
onClickFn={setActiveTab}
|
|
37
|
+
/>
|
|
38
|
+
</Container>
|
|
39
|
+
<div className="mb-12 sm:flex">
|
|
40
|
+
<Flex wrap className="w-full mb-8 lg:mb-0 lg:w-1/2">
|
|
41
|
+
{portfoliosPerCategory?.content
|
|
42
|
+
?.slice(count, count + 2)
|
|
43
|
+
?.map(content => <ProjectItem size={"sm"} content={content} key={content._key} />)}
|
|
44
|
+
{portfoliosPerCategory?.content
|
|
45
|
+
?.slice(count + 2, count + 3)
|
|
46
|
+
?.map(content => <ProjectItem size={"lg"} content={content} key={content._key} />)}
|
|
47
|
+
</Flex>
|
|
48
|
+
<div className="w-full lg:w-1/2">
|
|
49
|
+
{portfoliosPerCategory?.content
|
|
50
|
+
?.slice(count + 3, count + 4)
|
|
51
|
+
?.map(content => <ProjectItem size={"lg"} content={content} key={content._key} />)}
|
|
52
|
+
<div className="flex flex-wrap">
|
|
53
|
+
{portfoliosPerCategory?.content
|
|
54
|
+
?.slice(count + 4, portfoliosPerPage)
|
|
55
|
+
?.map(content => <ProjectItem size={"sm"} content={content} key={content._key} />)}
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
<PrimaryButton button={primaryButton} />
|
|
60
|
+
</Container>
|
|
61
|
+
</Section>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function CaptionAndTitleText({
|
|
66
|
+
caption,
|
|
67
|
+
title,
|
|
68
|
+
}: {
|
|
69
|
+
caption?: string | null;
|
|
70
|
+
title?: string | null;
|
|
71
|
+
}) {
|
|
72
|
+
if (!caption || !title) return null;
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<React.Fragment>
|
|
76
|
+
<Text weight="semibold">{caption}</Text>
|
|
77
|
+
<Heading className="mb-6" fontSize="2xl">
|
|
78
|
+
{title}
|
|
79
|
+
</Heading>
|
|
80
|
+
</React.Fragment>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function PrimaryButton({ button }: { button?: LabeledRoute | null }) {
|
|
85
|
+
if (!button?.label) return null;
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<div className="text-center">
|
|
89
|
+
<Button as="link" ariaLabel={button?.label} link={button} className="text-white">
|
|
90
|
+
{button?.label}
|
|
91
|
+
</Button>
|
|
92
|
+
</div>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function PortfolioCategories({
|
|
97
|
+
categories,
|
|
98
|
+
activeTab,
|
|
99
|
+
onClickFn,
|
|
100
|
+
}: {
|
|
101
|
+
categories?: PortfoliosWithCategories[] | null;
|
|
102
|
+
activeTab?: string | null;
|
|
103
|
+
onClickFn?: React.Dispatch<React.SetStateAction<string | null | undefined>>;
|
|
104
|
+
}) {
|
|
105
|
+
if (!categories || categories?.length === 0) return null;
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<Flex className="inline-flex py-1 text-sm bg-white rounded" wrap>
|
|
109
|
+
{categories?.map((content, index) => (
|
|
110
|
+
<Button
|
|
111
|
+
variant="tab"
|
|
112
|
+
as="button"
|
|
113
|
+
ariaLabel={content?.category ?? `Category button ${index + 1}`}
|
|
114
|
+
key={content?._key}
|
|
115
|
+
isActive={activeTab === content?.category}
|
|
116
|
+
onClick={() => onClickFn?.(content?.category)}>
|
|
117
|
+
{content?.category}
|
|
118
|
+
</Button>
|
|
119
|
+
))}
|
|
120
|
+
</Flex>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function ProjectItem({ size, content }: { size?: string | null; content?: Content }) {
|
|
125
|
+
const breakpoints = useMediaQuery("639");
|
|
126
|
+
const maxLength = breakpoints ? 60 : 90;
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<div className={`w-full px-4 mb-8 ${size === "lg" ? "w-full" : "lg:w-1/2"}`}>
|
|
130
|
+
{content?.mainImage?.image && (
|
|
131
|
+
<div className="relative overflow-hidden rounded-global">
|
|
132
|
+
<Image
|
|
133
|
+
className={`object-cover w-full ${size === "lg" ? "h-128" : "h-64"}`}
|
|
134
|
+
src={content?.mainImage?.image}
|
|
135
|
+
//width={352}
|
|
136
|
+
//height={280}
|
|
137
|
+
alt={`portfolio-image-${content?._key}`}
|
|
138
|
+
/>
|
|
139
|
+
<div className="absolute inset-0 z-10 justify-center p-6 duration-300 bg-slate-900 rounded-lg opacity-0 hover:opacity-80">
|
|
140
|
+
<div className="max-w-md my-auto text-xs">
|
|
141
|
+
{content?.subtitle && (
|
|
142
|
+
<Text className="text-sm text-primary" weight="bold">
|
|
143
|
+
{content?.subtitle?.length > 26
|
|
144
|
+
? `${content?.subtitle?.substring(0, 26)}...`
|
|
145
|
+
: content?.subtitle}
|
|
146
|
+
</Text>
|
|
147
|
+
)}
|
|
148
|
+
{content?.title && (
|
|
149
|
+
<Heading
|
|
150
|
+
weight="bold"
|
|
151
|
+
className={`my-5 text-white ${size === "lg" ? "text-sm md:text-xl" : "text-sm"}`}>
|
|
152
|
+
{content?.title?.length > 38
|
|
153
|
+
? `${content?.title?.substring(0, 38)}...`
|
|
154
|
+
: content?.title}
|
|
155
|
+
</Heading>
|
|
156
|
+
)}
|
|
157
|
+
<div className="max-w-xs my-5">
|
|
158
|
+
{content?.description && (
|
|
159
|
+
<Text fontSize="xs" muted className="mb-6 text-white">
|
|
160
|
+
{content?.description?.length > maxLength
|
|
161
|
+
? `${content?.description?.substring(0, maxLength)}...`
|
|
162
|
+
: content?.description}
|
|
163
|
+
</Text>
|
|
164
|
+
)}
|
|
165
|
+
{content?.primaryButton?.label && (
|
|
166
|
+
<Button
|
|
167
|
+
as="link"
|
|
168
|
+
variant="outline"
|
|
169
|
+
ariaLabel={content?.primaryButton?.label}
|
|
170
|
+
link={content?.primaryButton}
|
|
171
|
+
className="bg-transparent border-secondary outline text-white hover:bg-secondary/20 hover:border-secondary/20 inline-block rounded-l-xl rounded-t-xl font-bold transition duration-200 px-3 py-4">
|
|
172
|
+
{content?.primaryButton?.label}
|
|
173
|
+
</Button>
|
|
174
|
+
)}
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
)}
|
|
180
|
+
</div>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export { Portfolio_D };
|