@stackshift-ui/portfolio 6.0.3 → 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/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-BJOQ75B3.mjs +0 -1
- package/dist/chunk-EJMMBK25.mjs +0 -1
package/package.json
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackshift-ui/portfolio",
|
|
3
3
|
"description": "",
|
|
4
|
-
"version": "6.0.
|
|
4
|
+
"version": "6.0.4",
|
|
5
5
|
"private": false,
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"main": "./dist/index.js",
|
|
8
8
|
"module": "./dist/index.mjs",
|
|
9
9
|
"types": "./dist/index.d.ts",
|
|
10
10
|
"files": [
|
|
11
|
-
"dist/**"
|
|
11
|
+
"dist/**",
|
|
12
|
+
"src"
|
|
12
13
|
],
|
|
13
14
|
"author": "WebriQ <info@webriq.com>",
|
|
14
15
|
"devDependencies": {
|
|
@@ -28,20 +29,20 @@
|
|
|
28
29
|
"typescript": "^5.6.2",
|
|
29
30
|
"vite-tsconfig-paths": "^5.0.1",
|
|
30
31
|
"vitest": "^2.1.1",
|
|
31
|
-
"@stackshift-ui/
|
|
32
|
-
"@stackshift-ui/
|
|
32
|
+
"@stackshift-ui/typescript-config": "6.0.2",
|
|
33
|
+
"@stackshift-ui/eslint-config": "6.0.2"
|
|
33
34
|
},
|
|
34
35
|
"dependencies": {
|
|
35
36
|
"classnames": "^2.5.1",
|
|
36
37
|
"@stackshift-ui/scripts": "6.0.2",
|
|
37
|
-
"@stackshift-ui/
|
|
38
|
-
"@stackshift-ui/
|
|
39
|
-
"@stackshift-ui/
|
|
40
|
-
"@stackshift-ui/
|
|
41
|
-
"@stackshift-ui/
|
|
42
|
-
"@stackshift-ui/
|
|
43
|
-
"@stackshift-ui/
|
|
44
|
-
"@stackshift-ui/
|
|
38
|
+
"@stackshift-ui/text": "6.0.3",
|
|
39
|
+
"@stackshift-ui/image": "6.0.3",
|
|
40
|
+
"@stackshift-ui/section": "6.0.3",
|
|
41
|
+
"@stackshift-ui/container": "6.0.3",
|
|
42
|
+
"@stackshift-ui/flex": "6.0.3",
|
|
43
|
+
"@stackshift-ui/heading": "6.0.3",
|
|
44
|
+
"@stackshift-ui/system": "6.0.3",
|
|
45
|
+
"@stackshift-ui/button": "6.0.3"
|
|
45
46
|
},
|
|
46
47
|
"peerDependencies": {
|
|
47
48
|
"@types/react": "16.8 - 19",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
export const useMediaQuery = (width: string) => {
|
|
4
|
+
const [targetReached, setTargetReached] = React.useState(false);
|
|
5
|
+
|
|
6
|
+
const updateTarget = React.useCallback((e: any) => {
|
|
7
|
+
if (e.matches) {
|
|
8
|
+
setTargetReached(true);
|
|
9
|
+
} else {
|
|
10
|
+
setTargetReached(false);
|
|
11
|
+
}
|
|
12
|
+
}, []);
|
|
13
|
+
|
|
14
|
+
React.useEffect(() => {
|
|
15
|
+
const media = window.matchMedia(`(max-width: ${width}px)`);
|
|
16
|
+
media.addEventListener("change", updateTarget);
|
|
17
|
+
|
|
18
|
+
// Check on mount (callback is not called until a change occurs)
|
|
19
|
+
if (media.matches) {
|
|
20
|
+
setTargetReached(true);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return () => media.removeEventListener("change", updateTarget);
|
|
24
|
+
}, []);
|
|
25
|
+
|
|
26
|
+
return targetReached;
|
|
27
|
+
};
|
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 { Portfolio } from "./portfolio";
|
|
4
|
+
|
|
5
|
+
describe.concurrent("portfolio", () => {
|
|
6
|
+
afterEach(cleanup);
|
|
7
|
+
|
|
8
|
+
test.skip("Dummy test - test if renders without errors", ({ expect }) => {
|
|
9
|
+
const clx = "my-class";
|
|
10
|
+
render(<Portfolio />);
|
|
11
|
+
expect(screen.getByTestId("{ kebabCase name }}").classList).toContain(clx);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React, { lazy } from "react";
|
|
2
|
+
|
|
3
|
+
import { LabeledRoute, Portfolios, PortfoliosWithCategories, SectionsProps } from "./types";
|
|
4
|
+
|
|
5
|
+
const Variants = {
|
|
6
|
+
variant_a: lazy(() => import("./portfolio_a")),
|
|
7
|
+
variant_b: lazy(() => import("./portfolio_b")),
|
|
8
|
+
variant_c: lazy(() => import("./portfolio_c")),
|
|
9
|
+
variant_d: lazy(() => import("./portfolio_d")),
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export interface PortfolioProps {
|
|
13
|
+
caption?: string;
|
|
14
|
+
title?: string;
|
|
15
|
+
portfoliosWithCategory?: PortfoliosWithCategories[];
|
|
16
|
+
portfolios?: Portfolios[];
|
|
17
|
+
primaryButton?: LabeledRoute;
|
|
18
|
+
length?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const displayName = "Portfolio";
|
|
22
|
+
|
|
23
|
+
export const Portfolio: React.FC<SectionsProps> = ({ data }) => {
|
|
24
|
+
const variant = data?.variant;
|
|
25
|
+
const Variant = variant && Variants?.[variant as keyof typeof Variants];
|
|
26
|
+
|
|
27
|
+
const props = {
|
|
28
|
+
caption: data?.variants?.subtitle ?? undefined,
|
|
29
|
+
title: data?.variants?.title ?? undefined,
|
|
30
|
+
portfoliosWithCategory: data?.variants?.portfoliosWithCategories ?? undefined,
|
|
31
|
+
portfolios: data?.variants?.portfolios ?? undefined,
|
|
32
|
+
primaryButton: data?.variants?.primaryButton ?? undefined,
|
|
33
|
+
length: data?.variants?.length,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return Variant ? <Variant {...props} /> : null;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
Portfolio.displayName = displayName;
|
|
@@ -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 };
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
export type StyleVariants<T extends string> = Record<T, string>;
|
|
2
|
+
|
|
3
|
+
export type Socials = "facebook" | "instagram" | "youtube" | "linkedin" | "twitter";
|
|
4
|
+
export interface MainImage {
|
|
5
|
+
image: string;
|
|
6
|
+
alt?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface LabeledRoute extends ConditionalLink {
|
|
10
|
+
ariaLabel?: string;
|
|
11
|
+
label?: string;
|
|
12
|
+
linkTarget?: string;
|
|
13
|
+
linkType?: string;
|
|
14
|
+
_type?: string;
|
|
15
|
+
linkInternal?: any;
|
|
16
|
+
}
|
|
17
|
+
export interface ConditionalLink {
|
|
18
|
+
type?: string;
|
|
19
|
+
internalLink?: string | null;
|
|
20
|
+
externalLink?: string | null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface StatItems {
|
|
24
|
+
label?: string;
|
|
25
|
+
mainImage?: MainImage;
|
|
26
|
+
value?: string;
|
|
27
|
+
_key?: string;
|
|
28
|
+
_type?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface Logo extends ConditionalLink {
|
|
32
|
+
alt?: string;
|
|
33
|
+
linkTarget?: string;
|
|
34
|
+
image?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface Images {
|
|
38
|
+
image?: string;
|
|
39
|
+
_key?: string;
|
|
40
|
+
_type?: string;
|
|
41
|
+
alt?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface ContactDetails {
|
|
45
|
+
addressInfo?: string;
|
|
46
|
+
contactInfo?: string;
|
|
47
|
+
emailInfo?: string;
|
|
48
|
+
_key?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface SocialLink {
|
|
52
|
+
socialMedia?: string | null;
|
|
53
|
+
socialMediaLink?: string | null;
|
|
54
|
+
_key?: string | null;
|
|
55
|
+
_type?: string | null;
|
|
56
|
+
socialMediaIcon?: {
|
|
57
|
+
alt?: string;
|
|
58
|
+
image?: string;
|
|
59
|
+
} | null;
|
|
60
|
+
socialMediaPlatform?: string | null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface LabeledRouteWithKey extends LabeledRoute {
|
|
64
|
+
_key?: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface ArrayOfImageTitleAndText {
|
|
68
|
+
mainImage?: {
|
|
69
|
+
alt?: string;
|
|
70
|
+
image?: string;
|
|
71
|
+
};
|
|
72
|
+
plainText?: string;
|
|
73
|
+
title?: string;
|
|
74
|
+
_key?: string;
|
|
75
|
+
_type?: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface FeaturedItem {
|
|
79
|
+
description?: string;
|
|
80
|
+
mainImage?: MainImage;
|
|
81
|
+
title?: string;
|
|
82
|
+
subtitle?: string;
|
|
83
|
+
_key?: string;
|
|
84
|
+
_type?: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface ArrayOfTitleAndText {
|
|
88
|
+
_key?: string;
|
|
89
|
+
plainText?: string;
|
|
90
|
+
title?: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface BlogPost extends SanityBody {
|
|
94
|
+
authors?: Author[] | null;
|
|
95
|
+
body?: any;
|
|
96
|
+
categories?: Category[] | null;
|
|
97
|
+
excerpt?: string | null;
|
|
98
|
+
link?: string | null;
|
|
99
|
+
mainImage?: string | null;
|
|
100
|
+
publishedAt?: string;
|
|
101
|
+
seo?: Seo | null;
|
|
102
|
+
slug?: SanitySlug | null;
|
|
103
|
+
title?: string;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface Seo {
|
|
107
|
+
_type?: string;
|
|
108
|
+
seoTitle?: string;
|
|
109
|
+
seoDescription?: string;
|
|
110
|
+
seoImage?: string;
|
|
111
|
+
seoKeywords?: string;
|
|
112
|
+
seoSynonyms?: string;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface SanitySlug {
|
|
116
|
+
current?: string;
|
|
117
|
+
_type?: "slug";
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface SanityBody {
|
|
121
|
+
_createdAt?: string;
|
|
122
|
+
_id?: string;
|
|
123
|
+
_rev?: string;
|
|
124
|
+
_type?: string;
|
|
125
|
+
_updatedAt?: string;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface Author extends SanityBody {
|
|
129
|
+
link?: string | null;
|
|
130
|
+
bio?: string | null;
|
|
131
|
+
name?: string | null;
|
|
132
|
+
slug?: SanitySlug | null;
|
|
133
|
+
image?: string | null;
|
|
134
|
+
profile?: {
|
|
135
|
+
alt: string;
|
|
136
|
+
image: string;
|
|
137
|
+
} | null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export interface Category extends SanityBody {
|
|
141
|
+
title?: string;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface Form {
|
|
145
|
+
id?: string | null;
|
|
146
|
+
buttonLabel?: string | null;
|
|
147
|
+
name?: string | null;
|
|
148
|
+
subtitle?: string | null;
|
|
149
|
+
fields?: FormFields[] | null;
|
|
150
|
+
thankYouPage?: ThankYouPage | null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export interface FormFields {
|
|
154
|
+
name?: string;
|
|
155
|
+
placeholder?: string;
|
|
156
|
+
pricingType?: string;
|
|
157
|
+
type?: FormTypes;
|
|
158
|
+
_key?: string;
|
|
159
|
+
_type?: string;
|
|
160
|
+
isRequired?: boolean;
|
|
161
|
+
label?: string;
|
|
162
|
+
items?: string[];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export type FormTypes =
|
|
166
|
+
| "inputText"
|
|
167
|
+
| "inputEmail"
|
|
168
|
+
| "inputPassword"
|
|
169
|
+
| "inputNumber"
|
|
170
|
+
| "textarea"
|
|
171
|
+
| "inputFile"
|
|
172
|
+
| "inputRadio"
|
|
173
|
+
| "inputCheckbox"
|
|
174
|
+
| "inputSelect";
|
|
175
|
+
|
|
176
|
+
export interface ThankYouPage {
|
|
177
|
+
externalLink?: string | null;
|
|
178
|
+
internalLink?: string | null;
|
|
179
|
+
linkInternal?: any;
|
|
180
|
+
linkTarget?: string;
|
|
181
|
+
linkType?: string;
|
|
182
|
+
type?: string;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
//Used on different sections
|
|
186
|
+
export interface SectionsProps {
|
|
187
|
+
template?: Template;
|
|
188
|
+
data?: Sections;
|
|
189
|
+
variant?: string | null | undefined;
|
|
190
|
+
schema?: Variants;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export interface Sections extends SanityBody {
|
|
194
|
+
label?: string;
|
|
195
|
+
variant?: string;
|
|
196
|
+
variants?: Variants;
|
|
197
|
+
_key?: string;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
//*EDIT THIS SECTION WHEN CREATING/UPDATING SCHEMAS ON STUDIO */
|
|
201
|
+
export interface Variants {
|
|
202
|
+
template?: Template;
|
|
203
|
+
multipleMenus?: any;
|
|
204
|
+
arrayOfTitleAndText?: ArrayOfTitleAndText[] | null;
|
|
205
|
+
logo?: Logo | null;
|
|
206
|
+
primaryButton?: LabeledRoute | null;
|
|
207
|
+
secondaryButton?: LabeledRoute | null;
|
|
208
|
+
routes?: LabeledRouteWithKey[] | null;
|
|
209
|
+
menu?: LabeledRouteWithKey[] | null;
|
|
210
|
+
plans?: Plans[] | null;
|
|
211
|
+
formLinks?: LabeledRouteWithKey[] | null;
|
|
212
|
+
portfolios?: Portfolios[] | null;
|
|
213
|
+
portfoliosWithCategories?: PortfoliosWithCategories[] | null;
|
|
214
|
+
length?: number;
|
|
215
|
+
signInLink?: LabeledRoute | null;
|
|
216
|
+
signinLink?: LabeledRoute | null;
|
|
217
|
+
tags?: string[] | null;
|
|
218
|
+
posts?: BlogPost[] | null;
|
|
219
|
+
blogsPerPage?: number | null;
|
|
220
|
+
form?: Form | null;
|
|
221
|
+
collections?: Collection | null;
|
|
222
|
+
products?: CollectionProduct | null;
|
|
223
|
+
allProducts?: Collection[];
|
|
224
|
+
subtitle?: string | null;
|
|
225
|
+
caption?: string | null;
|
|
226
|
+
title?: string | null;
|
|
227
|
+
plainText?: string | null;
|
|
228
|
+
contactDescription?: string | null;
|
|
229
|
+
officeInformation?: string | null;
|
|
230
|
+
contactEmail?: string | null;
|
|
231
|
+
contactNumber?: string | null;
|
|
232
|
+
socialLinks?: SocialLink[] | null;
|
|
233
|
+
block?: any;
|
|
234
|
+
heading?: string | null;
|
|
235
|
+
acceptButtonLabel?: string | null;
|
|
236
|
+
declineButtonLabel?: string | null;
|
|
237
|
+
faqsWithCategories?: FaqsWithCategory[] | null;
|
|
238
|
+
faqs?: AskedQuestion[] | null;
|
|
239
|
+
arrayOfImageTitleAndText?: ArrayOfImageTitleAndText[] | null;
|
|
240
|
+
description?: string | null;
|
|
241
|
+
featuredItems?: FeaturedItem[] | null;
|
|
242
|
+
images?: Images[] | null;
|
|
243
|
+
contactDetails?: ContactDetails[] | null;
|
|
244
|
+
copyright?: string | null;
|
|
245
|
+
mainImage?: MainImage | null;
|
|
246
|
+
youtubeLink?: string | null;
|
|
247
|
+
banner?: any;
|
|
248
|
+
stats?: StatItems[] | null;
|
|
249
|
+
teams?: Team[] | null;
|
|
250
|
+
testimonials?: Testimonial[] | null;
|
|
251
|
+
selectStripeAccount?: string;
|
|
252
|
+
annualBilling?: string;
|
|
253
|
+
monthlyBilling?: string;
|
|
254
|
+
productDetails?: ProductDetail[];
|
|
255
|
+
btnLabel?: string;
|
|
256
|
+
selectAccount?: string;
|
|
257
|
+
hashtags?: string[];
|
|
258
|
+
numberOfPosts?: number;
|
|
259
|
+
text?: string;
|
|
260
|
+
button?: LabeledRoute;
|
|
261
|
+
features?: string[];
|
|
262
|
+
config?: {
|
|
263
|
+
enableAnalytics: boolean;
|
|
264
|
+
cookiePolicy?: {
|
|
265
|
+
siteName: string;
|
|
266
|
+
cookiePolicyPage: Reference;
|
|
267
|
+
};
|
|
268
|
+
consentModalPosition?: string;
|
|
269
|
+
};
|
|
270
|
+
contactLink?: LabeledRoute;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export interface Template {
|
|
274
|
+
bg?: string;
|
|
275
|
+
color?: string;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export type Plans = {
|
|
279
|
+
_key?: string | null;
|
|
280
|
+
_type?: "planItems" | null;
|
|
281
|
+
checkoutButtonName?: string | null;
|
|
282
|
+
description?: string | null;
|
|
283
|
+
monthlyPrice?: string | null;
|
|
284
|
+
planType?: string | null;
|
|
285
|
+
yearlyPrice?: string | null;
|
|
286
|
+
planIncludes?: string[] | null;
|
|
287
|
+
primaryButton?: LabeledRoute | null;
|
|
288
|
+
} & Record<string, string>;
|
|
289
|
+
|
|
290
|
+
export interface Portfolios {
|
|
291
|
+
dateAdded?: string | null;
|
|
292
|
+
mainImage?: {
|
|
293
|
+
image?: string | null;
|
|
294
|
+
alt?: string | null;
|
|
295
|
+
} | null;
|
|
296
|
+
primaryButton?: LabeledRoute | null;
|
|
297
|
+
title?: string | null;
|
|
298
|
+
_key?: string | null;
|
|
299
|
+
_type?: string | null;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export interface PortfoliosWithCategories {
|
|
303
|
+
category?: string | null;
|
|
304
|
+
content?: Content[] | null;
|
|
305
|
+
primaryButton?: LabeledRoute | null;
|
|
306
|
+
_key?: string | null;
|
|
307
|
+
_type?: string | null;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export interface Content extends Portfolios {
|
|
311
|
+
description?: string | null;
|
|
312
|
+
subtitle?: string | null;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export interface Collection extends SanityBody {
|
|
316
|
+
collectionInfoVariant?: {
|
|
317
|
+
variant?: string;
|
|
318
|
+
} | null;
|
|
319
|
+
name?: string | null;
|
|
320
|
+
products?: CollectionProduct[] | null;
|
|
321
|
+
sections?: any; //todo
|
|
322
|
+
seo?: Seo | null;
|
|
323
|
+
slug?: SanitySlug | null;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export interface CollectionProduct extends SanityBody {
|
|
327
|
+
compareToPrice?: number | null;
|
|
328
|
+
description?: string | null;
|
|
329
|
+
ecwidProductId?: number | null;
|
|
330
|
+
name?: string | null;
|
|
331
|
+
price?: number | null;
|
|
332
|
+
productInfo?: ProductInfo | null;
|
|
333
|
+
productInfoVariant?: {
|
|
334
|
+
variant?: string;
|
|
335
|
+
} | null;
|
|
336
|
+
sections?: any; //todo
|
|
337
|
+
seo?: Seo | null;
|
|
338
|
+
slug?: SanitySlug | null;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
//TODO, RECHECK PRODUCT INFO DATA FROM SANITY
|
|
342
|
+
interface ProductInfo {
|
|
343
|
+
btnLabel?: string | null;
|
|
344
|
+
images?: ProductInfoImage[] | null;
|
|
345
|
+
productDetails?: ProductDetail[] | null;
|
|
346
|
+
socialLinks?: SocialLink[] | null;
|
|
347
|
+
subtitle?: string | null;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
//TODO, RECHECK PRODUCT INFO DATA FROM SANITY
|
|
351
|
+
export interface ProductDetail {
|
|
352
|
+
blockContent?: any;
|
|
353
|
+
contentType?: string;
|
|
354
|
+
tabName?: string;
|
|
355
|
+
_key?: string;
|
|
356
|
+
[key: string]: any;
|
|
357
|
+
}
|
|
358
|
+
interface ProductInfoImage {
|
|
359
|
+
alt?: string | null;
|
|
360
|
+
_key: string;
|
|
361
|
+
_type: string;
|
|
362
|
+
image?: string | null;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export interface FaqsWithCategory {
|
|
366
|
+
askedQuestions?: AskedQuestion[] | null;
|
|
367
|
+
category?: string | null;
|
|
368
|
+
_key?: string;
|
|
369
|
+
_type?: string;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
export interface AskedQuestion {
|
|
373
|
+
answer?: string | null;
|
|
374
|
+
question?: string | null;
|
|
375
|
+
hidden?: boolean;
|
|
376
|
+
_key?: string;
|
|
377
|
+
_type?: string;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
export interface Team {
|
|
381
|
+
jobTitle?: string;
|
|
382
|
+
mainImage?: MainImage;
|
|
383
|
+
name?: string;
|
|
384
|
+
plainText?: string;
|
|
385
|
+
_key?: string;
|
|
386
|
+
_type?: string;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export interface Testimonial {
|
|
390
|
+
jobTitle?: string;
|
|
391
|
+
mainImage?: MainImage;
|
|
392
|
+
name?: string;
|
|
393
|
+
rating?: string;
|
|
394
|
+
testimony?: string;
|
|
395
|
+
_key?: string;
|
|
396
|
+
_type?: string;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
export declare interface Reference {
|
|
400
|
+
_type: string;
|
|
401
|
+
_ref: string;
|
|
402
|
+
_key?: string;
|
|
403
|
+
_weak?: boolean;
|
|
404
|
+
_strengthenOnPublish?: {
|
|
405
|
+
type: string;
|
|
406
|
+
weak?: boolean;
|
|
407
|
+
template?: {
|
|
408
|
+
id: string;
|
|
409
|
+
params: Record<string, string | number | boolean>;
|
|
410
|
+
};
|
|
411
|
+
};
|
|
412
|
+
}
|
package/dist/chunk-BJOQ75B3.mjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{Button as v}from"@stackshift-ui/button";import{Container as w}from"@stackshift-ui/container";import{Flex as p}from"@stackshift-ui/flex";import{Heading as h}from"@stackshift-ui/heading";import{Image as k}from"@stackshift-ui/image";import{Section as B}from"@stackshift-ui/section";import{Text as P}from"@stackshift-ui/text";import N from"react";import{jsx as r,jsxs as g}from"react/jsx-runtime";function L({caption:l,title:e,portfoliosWithCategory:i,primaryButton:a,length:s=8}){var x;let m=s,[b,f]=N.useState((x=i==null?void 0:i[0])==null?void 0:x.category),u=i==null?void 0:i.find(d=>(d==null?void 0:d.category)===b);return r(B,{className:"py-20 bg-background",children:g(w,{maxWidth:1280,children:[g(w,{maxWidth:512,className:"mb-8 text-center md:mb-16",children:[r(T,{caption:l,title:e}),r(S,{categories:i,activeTab:b,onClickFn:f})]}),r(A,{portfolios:u==null?void 0:u.content,portfolioLength:m}),r(R,{button:a})]})})}function T({caption:l,title:e}){return!l||!e?null:g(N.Fragment,{children:[r(P,{weight:"semibold",children:l}),r(h,{className:"mb-6",fontSize:"2xl",children:e})]})}function R({button:l}){return l!=null&&l.label?r("div",{className:"text-center",children:r(v,{as:"link",ariaLabel:l==null?void 0:l.label,link:l,children:l==null?void 0:l.label})}):null}function S({categories:l,activeTab:e,onClickFn:i}){return!l||(l==null?void 0:l.length)===0?null:r(p,{className:"inline-flex py-1 text-sm bg-white rounded-global",wrap:!0,children:l==null?void 0:l.map((a,s)=>{var m;return r(v,{variant:"tab",as:"button",ariaLabel:(m=a==null?void 0:a.category)!=null?m:`Category button ${s+1}`,isActive:e===(a==null?void 0:a.category),onClick:()=>i==null?void 0:i(a==null?void 0:a.category),children:a==null?void 0:a.category},a==null?void 0:a._key)})})}function A({portfolios:l,portfolioLength:e}){var i;return!l||(l==null?void 0:l.length)===0?null:r(p,{wrap:!0,className:"mb-8",children:(i=l==null?void 0:l.slice(0,e))==null?void 0:i.map((a,s)=>{var m,b,f,u,x,d,y;return r(p,{className:"w-full space-x-5 px-4 mb-8 sm:w-1/2 lg:w-1/4",children:g("div",{className:"relative mx-auto h-[256px] w-[332px] overflow-hidden rounded-global",children:[((m=a==null?void 0:a.mainImage)==null?void 0:m.image)&&r(k,{className:"object-cover w-full h-full",src:(b=a==null?void 0:a.mainImage)==null?void 0:b.image,alt:(u=(f=a==null?void 0:a.mainImage)==null?void 0:f.alt)!=null?u:`portfolio-image-${s}`}),r("div",{className:"absolute inset-0 z-10 flex items-center justify-center duration-300 bg-slate-900 rounded-lg opacity-0 hover:opacity-75",children:((x=a==null?void 0:a.primaryButton)==null?void 0:x.label)&&r(v,{as:"link",variant:"outline",ariaLabel:(d=a==null?void 0:a.primaryButton)==null?void 0:d.label,link:a==null?void 0:a.primaryButton,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",children:(y=a==null?void 0:a.primaryButton)==null?void 0:y.label})})]})},a==null?void 0:a._key)})})}export{L as a};
|
package/dist/chunk-EJMMBK25.mjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{lazy as i}from"react";import{jsx as b}from"react/jsx-runtime";var r={variant_a:i(()=>import("./portfolio_a.mjs")),variant_b:i(()=>import("./portfolio_b.mjs")),variant_c:i(()=>import("./portfolio_c.mjs")),variant_d:i(()=>import("./portfolio_d.mjs"))},h="Portfolio",C=({data:o})=>{var e,s,l,p,f,c,m,u,v,g,y;let n=o==null?void 0:o.variant,t=n&&(r==null?void 0:r[n]),P={caption:(s=(e=o==null?void 0:o.variants)==null?void 0:e.subtitle)!=null?s:void 0,title:(p=(l=o==null?void 0:o.variants)==null?void 0:l.title)!=null?p:void 0,portfoliosWithCategory:(c=(f=o==null?void 0:o.variants)==null?void 0:f.portfoliosWithCategories)!=null?c:void 0,portfolios:(u=(m=o==null?void 0:o.variants)==null?void 0:m.portfolios)!=null?u:void 0,primaryButton:(g=(v=o==null?void 0:o.variants)==null?void 0:v.primaryButton)!=null?g:void 0,length:(y=o==null?void 0:o.variants)==null?void 0:y.length};return t?b(t,{...P}):null};C.displayName=h;export{C as a};
|