@stackshift-ui/footer 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 CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "@stackshift-ui/footer",
3
3
  "description": "",
4
- "version": "6.0.3",
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": {
@@ -33,18 +34,18 @@
33
34
  },
34
35
  "dependencies": {
35
36
  "classnames": "^2.5.1",
37
+ "@stackshift-ui/button": "6.0.3",
38
+ "@stackshift-ui/system": "6.0.3",
36
39
  "@stackshift-ui/scripts": "6.0.2",
37
- "@stackshift-ui/text": "6.0.2",
38
- "@stackshift-ui/system": "6.0.2",
39
- "@stackshift-ui/button": "6.0.2",
40
- "@stackshift-ui/image": "6.0.2",
41
- "@stackshift-ui/link": "6.0.2",
42
- "@stackshift-ui/social-icons": "6.0.2",
43
- "@stackshift-ui/section": "6.0.2",
44
- "@stackshift-ui/container": "6.0.2",
45
- "@stackshift-ui/flex": "6.0.2",
46
- "@stackshift-ui/grid": "6.0.2",
47
- "@stackshift-ui/grid-item": "6.0.2"
40
+ "@stackshift-ui/text": "6.0.3",
41
+ "@stackshift-ui/image": "6.0.3",
42
+ "@stackshift-ui/link": "6.0.3",
43
+ "@stackshift-ui/section": "6.0.3",
44
+ "@stackshift-ui/container": "6.0.3",
45
+ "@stackshift-ui/flex": "6.0.3",
46
+ "@stackshift-ui/grid": "6.0.3",
47
+ "@stackshift-ui/grid-item": "6.0.3",
48
+ "@stackshift-ui/social-icons": "6.0.3"
48
49
  },
49
50
  "peerDependencies": {
50
51
  "@types/react": "16.8 - 19",
@@ -0,0 +1,13 @@
1
+ import { cleanup, render, screen } from "@testing-library/react";
2
+ import { afterEach, describe, test } from "vitest";
3
+ import { Footer } from "./footer";
4
+
5
+ describe.concurrent("footer", () => {
6
+ afterEach(cleanup);
7
+
8
+ test.skip("Dummy test - test if renders without errors", ({ expect }) => {
9
+ const clx = "my-class";
10
+ render(<Footer />);
11
+ expect(screen.getByTestId("{ kebabCase name }}").classList).toContain(clx);
12
+ });
13
+ });
package/src/footer.tsx ADDED
@@ -0,0 +1,40 @@
1
+ import React, { lazy } from "react";
2
+ import { SectionsProps, Logo, ContactDetails, SocialLink, LabeledRouteWithKey } from "./types";
3
+
4
+ const Variants = {
5
+ variant_a: lazy(() => import("./footer_a")),
6
+ variant_b: lazy(() => import("./footer_b")),
7
+ variant_c: lazy(() => import("./footer_c")),
8
+ variant_d: lazy(() => import("./footer_d")),
9
+ };
10
+
11
+ export interface FooterProps {
12
+ logo?: Logo;
13
+ text?: string;
14
+ contacts?: ContactDetails[];
15
+ copyright?: string;
16
+ socialMedia?: SocialLink[];
17
+ menu?: LabeledRouteWithKey[];
18
+ multipleMenus?: any;
19
+ }
20
+
21
+ const displayName = "Footer";
22
+
23
+ export const Footer: React.FC<SectionsProps> = ({ data }) => {
24
+ const variant = data?.variant;
25
+ const Variant = variant && Variants[variant as keyof typeof Variants];
26
+
27
+ const props = {
28
+ logo: data?.variants?.logo ?? undefined,
29
+ text: data?.variants?.plainText ?? undefined,
30
+ contacts: data?.variants?.contactDetails ?? undefined,
31
+ copyright: data?.variants?.copyright ?? undefined,
32
+ socialMedia: data?.variants?.socialLinks ?? undefined,
33
+ menu: data?.variants?.menu ?? undefined,
34
+ multipleMenus: data?.variants?.multipleMenus ?? undefined,
35
+ };
36
+
37
+ return Variant ? <Variant {...props} /> : null;
38
+ };
39
+
40
+ Footer.displayName = displayName;
@@ -0,0 +1,152 @@
1
+ import { Container } from "@stackshift-ui/container";
2
+ import { Flex } from "@stackshift-ui/flex";
3
+ import { Grid } from "@stackshift-ui/grid";
4
+ import { GridItem } from "@stackshift-ui/grid-item";
5
+ import { Image } from "@stackshift-ui/image";
6
+ import { Link } from "@stackshift-ui/link";
7
+ import { Section } from "@stackshift-ui/section";
8
+ import { SocialIcons } from "@stackshift-ui/social-icons";
9
+ import { Text } from "@stackshift-ui/text";
10
+
11
+ import { FooterProps } from ".";
12
+ import { logoLink } from "./helper";
13
+ import { ContactDetails, Logo, SocialLink, Socials } from "./types";
14
+
15
+ export default function Footer_A({ logo, text, contacts, copyright, socialMedia }: FooterProps) {
16
+ return (
17
+ <Section className="py-20 bg-background">
18
+ <Container maxWidth={1000}>
19
+ <Flex wrap className="mb-5 lg:mb-20">
20
+ <LogoSection logo={logo} />
21
+ <TextContainer text={text} />
22
+ <ContactsContainer contacts={contacts} />
23
+ </Flex>
24
+ <Flex justify="between" align="center" className="w-full mx-auto lg:flex">
25
+ <CopyrightContainer copyright={copyright} />
26
+ <SocialMediaContainer socialMedia={socialMedia} />
27
+ </Flex>
28
+ </Container>
29
+ </Section>
30
+ );
31
+ }
32
+
33
+ function LogoSection({ logo }: { logo?: Logo }) {
34
+ if (!logo?.image) return null;
35
+
36
+ return (
37
+ <div className="w-full mb-5 lg:w-1/5">
38
+ <Link
39
+ aria-label={logoLink(logo) === "/" ? "Go to home page" : `Go to ${logoLink(logo)}`}
40
+ className="text-3xl font-bold leading-none"
41
+ href={logoLink(logo)}
42
+ target={logo?.linkTarget}
43
+ rel={logo?.linkTarget === "_blank" ? "noopener noreferrer" : ""}>
44
+ <Image
45
+ className="h-14"
46
+ src={`${logo?.image}`}
47
+ sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
48
+ width={132}
49
+ height={56}
50
+ alt={logo?.alt ?? "footer-logo"}
51
+ />
52
+ </Link>
53
+ </div>
54
+ );
55
+ }
56
+
57
+ function TextContainer({ text }: { text?: string }) {
58
+ if (!text) return null;
59
+
60
+ return (
61
+ <Container className="w-full mb-5 lg:w-1/5" maxWidth={1000}>
62
+ <Text muted className="leading-loose">
63
+ {text}
64
+ </Text>
65
+ </Container>
66
+ );
67
+ }
68
+
69
+ function ContactsContainer({ contacts }: { contacts?: ContactDetails[] }) {
70
+ if (!contacts) return null;
71
+
72
+ return (
73
+ <Container className="w-full mt-1 ml-auto lg:w-1/2" maxWidth={1000}>
74
+ {contacts?.map((contact, index) => (
75
+ <Grid columns={1} gap={4} className="lg:gap-10 md:grid-cols-3" key={index}>
76
+ <GridItem span={1}>
77
+ <Text weight="bold" className="mb-4">
78
+ Address
79
+ </Text>
80
+ <Text muted className="mb-5">
81
+ {contact?.addressInfo}
82
+ </Text>
83
+ </GridItem>
84
+ <GridItem span={1}>
85
+ <Text weight="bold" className="mb-4">
86
+ Email
87
+ </Text>
88
+ <Text muted className="mb-5">
89
+ {contact?.emailInfo}
90
+ </Text>
91
+ </GridItem>
92
+ <GridItem span={1}>
93
+ <Text weight="bold" className="mb-4">
94
+ Number
95
+ </Text>
96
+ <Text muted className="mb-5">
97
+ {contact?.contactInfo}
98
+ </Text>
99
+ </GridItem>
100
+ </Grid>
101
+ ))}
102
+ </Container>
103
+ );
104
+ }
105
+
106
+ function CopyrightContainer({ copyright }: { copyright?: string }) {
107
+ if (!copyright) return null;
108
+
109
+ return (
110
+ <Text muted className="text-sm">
111
+ {copyright}
112
+ </Text>
113
+ );
114
+ }
115
+
116
+ function SocialMediaContainer({ socialMedia }: { socialMedia?: SocialLink[] }) {
117
+ if (!socialMedia) return null;
118
+
119
+ return (
120
+ <Flex wrap className="space-x-2 lg:mx-10 lg:space-x-4">
121
+ {socialMedia?.map((social, index) => <SocialMediaLink social={social} index={index} />)}
122
+ </Flex>
123
+ );
124
+ }
125
+
126
+ function SocialMediaLink({ social, index }: { social?: SocialLink; index?: number }) {
127
+ if (!social?.socialMediaLink) return null;
128
+
129
+ return (
130
+ <Link
131
+ aria-label={social?.socialMedia || social?.socialMediaPlatform || ""}
132
+ className="inline-block p-2 mr-2 rounded bg-gray-50 hover:bg-gray-100"
133
+ target="_blank"
134
+ rel="noopener noreferrer"
135
+ href={social?.socialMediaLink}
136
+ key={index}>
137
+ {social?.socialMediaIcon?.image ? (
138
+ <Image
139
+ className="h-6"
140
+ src={`${social?.socialMediaIcon?.image}`}
141
+ width={24}
142
+ height={24}
143
+ alt={social?.socialMediaIcon?.alt ?? "contact-socialMedia-icon"}
144
+ />
145
+ ) : (
146
+ <SocialIcons social={social?.socialMedia as Socials} />
147
+ )}
148
+ </Link>
149
+ );
150
+ }
151
+
152
+ export { Footer_A };
@@ -0,0 +1,154 @@
1
+ import { Button } from "@stackshift-ui/button";
2
+ import { Container } from "@stackshift-ui/container";
3
+ import { Flex } from "@stackshift-ui/flex";
4
+ import { Image } from "@stackshift-ui/image";
5
+ import { Link } from "@stackshift-ui/link";
6
+ import { Section } from "@stackshift-ui/section";
7
+ import { SocialIcons } from "@stackshift-ui/social-icons";
8
+ import { Text } from "@stackshift-ui/text";
9
+ import React from "react";
10
+
11
+ import { FooterProps } from ".";
12
+ import { logoLink } from "./helper";
13
+ import { LabeledRoute, LabeledRouteWithKey, Logo, SocialLink, Socials } from "./types";
14
+
15
+ export default function Footer_B({ logo, copyright, socialMedia, menu }: FooterProps) {
16
+ return (
17
+ <Section className="py-20 bg-background">
18
+ <Container maxWidth={1280}>
19
+ <Flex wrap align="center" justify="between" className="pb-12 border-b border-gray-100">
20
+ <LogoSection logo={logo} />
21
+ <MenuLists menu={menu} />
22
+ </Flex>
23
+ <Flex wrap align="center" justify="between" className="mt-8">
24
+ <CopyrightContainer copyright={copyright} />
25
+ <SocialMediaContainer socialMedia={socialMedia} />
26
+ </Flex>
27
+ </Container>
28
+ </Section>
29
+ );
30
+ }
31
+
32
+ function LogoSection({ logo }: { logo?: Logo }) {
33
+ if (!logo?.image) return null;
34
+
35
+ return (
36
+ <div className="w-full mb-12 lg:mb-4 lg:w-1/5">
37
+ <Link
38
+ aria-label={logoLink(logo) === "/" ? "Go to home page" : `Go to ${logoLink(logo)}`}
39
+ className="inline-block text-3xl font-bold leading-none"
40
+ href={logoLink(logo)}
41
+ target={logo?.linkTarget}
42
+ rel={logo?.linkTarget === "_blank" ? "noopener noreferrer" : ""}>
43
+ <Image
44
+ src={logo?.image}
45
+ alt={logo?.alt ?? "footer-logo"}
46
+ width={132}
47
+ height={132}
48
+ className="inline-block text-3xl font-bold leading-none"
49
+ />
50
+ </Link>
51
+ </div>
52
+ );
53
+ }
54
+
55
+ function MenuLists({ menu }: { menu?: LabeledRouteWithKey[] }) {
56
+ if (!menu) return null;
57
+
58
+ return (
59
+ <div className="w-full lg:w-auto">
60
+ <Flex wrap align="center" justify="between" as="ul" className=" mt-8 lg:space-x-5">
61
+ {menu?.map((links, index, { length }) => (
62
+ <React.Fragment key={links?._key || index}>
63
+ <MenuList links={links} />
64
+ {index + 1 !== length ? <MenuSeparatorIcon /> : null}
65
+ </React.Fragment>
66
+ ))}
67
+ </Flex>
68
+ </div>
69
+ );
70
+ }
71
+
72
+ function MenuList({ links, index }: { links?: LabeledRoute; index?: number }) {
73
+ if (!links) return null;
74
+
75
+ return (
76
+ <li className="w-full mb-2 md:mb-0 md:w-auto" key={index}>
77
+ <Button
78
+ as="link"
79
+ link={links}
80
+ className="text-gray-500 no-underline lg:text-sm hover:text-gray-700"
81
+ ariaLabel={links?.label}>
82
+ {links?.label}
83
+ </Button>
84
+ </li>
85
+ );
86
+ }
87
+
88
+ function MenuSeparatorIcon() {
89
+ return (
90
+ <li className="hidden md:block">
91
+ <svg
92
+ className="w-4 h-4 mx-4 text-gray-500"
93
+ xmlns="http://www.w3.org/2000/svg"
94
+ fill="none"
95
+ viewBox="0 0 24 24"
96
+ stroke="currentColor">
97
+ <path
98
+ strokeLinecap="round"
99
+ strokeLinejoin="round"
100
+ strokeWidth="2"
101
+ d="M12 5v.01M12 12v.01M12 19v.01M12 6a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2z"
102
+ />
103
+ </svg>
104
+ </li>
105
+ );
106
+ }
107
+
108
+ function CopyrightContainer({ copyright }: { copyright?: string }) {
109
+ if (!copyright) return null;
110
+
111
+ return (
112
+ <Text muted className="order-last text-sm ">
113
+ {copyright}
114
+ </Text>
115
+ );
116
+ }
117
+
118
+ function SocialMediaContainer({ socialMedia }: { socialMedia?: SocialLink[] }) {
119
+ if (!socialMedia) return null;
120
+
121
+ return (
122
+ <div className="order-first mb-4 lg:order-last lg:mb-0">
123
+ {socialMedia?.map(social => <SocialMediaLink social={social} />)}
124
+ </div>
125
+ );
126
+ }
127
+
128
+ function SocialMediaLink({ social }: { social?: SocialLink }) {
129
+ if (!social?.socialMediaLink) return null;
130
+
131
+ return (
132
+ <Link
133
+ aria-label={social?.socialMedia || social?.socialMediaPlatform || ""}
134
+ className="inline-block p-2 mr-2 rounded bg-gray-50 hover:bg-gray-100"
135
+ target="_blank"
136
+ rel="noopener noreferrer"
137
+ href={social?.socialMediaLink}
138
+ key={social?._key}>
139
+ {social?.socialMediaIcon?.image ? (
140
+ <Image
141
+ className="h-6"
142
+ src={`${social?.socialMediaIcon?.image}`}
143
+ width={24}
144
+ height={24}
145
+ alt={social?.socialMediaIcon?.alt ?? "contact-socialMedia-icon"}
146
+ />
147
+ ) : (
148
+ <SocialIcons social={social?.socialMedia as Socials} />
149
+ )}
150
+ </Link>
151
+ );
152
+ }
153
+
154
+ export { Footer_B };
@@ -0,0 +1,154 @@
1
+ import { Button } from "@stackshift-ui/button";
2
+ import { Container } from "@stackshift-ui/container";
3
+ import { Flex } from "@stackshift-ui/flex";
4
+ import { Image } from "@stackshift-ui/image";
5
+ import { Link } from "@stackshift-ui/link";
6
+ import { Section } from "@stackshift-ui/section";
7
+ import { SocialIcons } from "@stackshift-ui/social-icons";
8
+ import { Text } from "@stackshift-ui/text";
9
+
10
+ import { FooterProps } from ".";
11
+ import { logoLink } from "./helper";
12
+ import { LabeledRoute, LabeledRouteWithKey, Logo, SocialLink, Socials } from "./types";
13
+
14
+ export default function Footer_C({ logo, menu, copyright, socialMedia }: FooterProps) {
15
+ return (
16
+ <Section className="bg-background">
17
+ <BorderStyle />
18
+ <Container maxWidth={1000}>
19
+ <div className="pt-10 pb-12">
20
+ <Flex
21
+ justify="between"
22
+ align="center"
23
+ className="relative flex-col gap-8 mb-8 md:flex-row lg:border-b lg:border-gray-300 lg:pb-8">
24
+ <CopyrightSection copyright={copyright} />
25
+ <MenuLists menu={menu} />
26
+ <LogoSection logo={logo} />
27
+ </Flex>
28
+ <SocialMediaContainer socialMedia={socialMedia} />
29
+ </div>
30
+ </Container>
31
+ </Section>
32
+ );
33
+ }
34
+
35
+ function CopyrightSection({ copyright }: { copyright?: string }) {
36
+ if (!copyright) return null;
37
+
38
+ return <Text className="w-full text-sm text-center">{copyright}</Text>;
39
+ }
40
+
41
+ function LogoSection({ logo }: { logo?: Logo }) {
42
+ if (!logo?.image) return null;
43
+
44
+ return (
45
+ <div className="w-full text-center">
46
+ <Link
47
+ className="inline-block text-xl font-bold leading-none"
48
+ aria-label={logoLink(logo) === "/" ? "Go to home page" : `Go to ${logoLink(logo)}`}
49
+ href={logoLink(logo)}
50
+ target={logo?.linkTarget}
51
+ rel={logo?.linkTarget === "_blank" ? "noopener noreferrer" : ""}>
52
+ <Image
53
+ src={logo?.image}
54
+ alt={logo?.alt ?? "footer-logo"}
55
+ className="inline-block text-xl font-bold leading-none"
56
+ width={64}
57
+ height={64}
58
+ />
59
+ </Link>
60
+ </div>
61
+ );
62
+ }
63
+
64
+ function MenuLists({ menu }: { menu?: LabeledRouteWithKey[] }) {
65
+ if (!menu) return null;
66
+
67
+ return (
68
+ <div className="mx-auto">
69
+ <Flex
70
+ className="flex-col gap-0 lg:flex-row lg:gap-10"
71
+ as="ul"
72
+ align="center"
73
+ justify="center">
74
+ {menu?.map((links, index) => <MenuList links={links} index={index} />)}
75
+ </Flex>
76
+ </div>
77
+ );
78
+ }
79
+
80
+ function MenuList({ links, index }: { links?: LabeledRoute; index?: number }) {
81
+ if (!links) return null;
82
+
83
+ return (
84
+ <li className="w-full text-center" key={index}>
85
+ <Button
86
+ as="link"
87
+ link={links}
88
+ className="text-sm text-center text-black no-underline hover:text-gray-500 whitespace-nowrap"
89
+ ariaLabel={links?.label}>
90
+ {links?.label}
91
+ </Button>
92
+ </li>
93
+ );
94
+ }
95
+
96
+ function SocialMediaContainer({ socialMedia }: { socialMedia?: SocialLink[] }) {
97
+ if (!socialMedia) return null;
98
+
99
+ return (
100
+ <Flex wrap justify="center" className="space-y-2 sm:space-y-0">
101
+ {socialMedia?.map((social, index) => <SocialMediaLink social={social} index={index} />)}
102
+ </Flex>
103
+ );
104
+ }
105
+
106
+ function SocialMediaLink({ social, index }: { social?: SocialLink; index?: number }) {
107
+ if (!social?.socialMediaLink) return null;
108
+
109
+ return (
110
+ <Link
111
+ aria-label={social?.socialMedia || social?.socialMediaPlatform || ""}
112
+ className="inline-block p-2 mr-2 rounded bg-gray-50 hover:bg-gray-100"
113
+ target="_blank"
114
+ rel="noopener noreferrer"
115
+ href={social?.socialMediaLink}
116
+ key={index}>
117
+ {social?.socialMediaIcon?.image ? (
118
+ <Image
119
+ className="h-6"
120
+ src={`${social?.socialMediaIcon?.image}`}
121
+ width={24}
122
+ height={24}
123
+ alt={social?.socialMediaIcon?.alt ?? "contact-socialMedia-icon"}
124
+ />
125
+ ) : (
126
+ <SocialIcons social={social?.socialMedia as Socials} />
127
+ )}
128
+ </Link>
129
+ );
130
+ }
131
+
132
+ function BorderStyle() {
133
+ return (
134
+ <div className="flex w-full">
135
+ <div className="flex w-1/3">
136
+ <div className="w-1/3 py-1 bg-secondary" />
137
+ <div className="w-1/3 py-1 bg-primary-foreground" />
138
+ <div className="w-1/3 py-1 bg-primary" />
139
+ </div>
140
+ <div className="flex w-1/3">
141
+ <div className="w-1/3 py-1 bg-secondary" />
142
+ <div className="w-1/3 py-1 bg-primary-foreground" />
143
+ <div className="w-1/3 py-1 bg-primary" />
144
+ </div>
145
+ <div className="flex w-1/3">
146
+ <div className="w-1/3 py-1 bg-secondary" />
147
+ <div className="w-1/3 py-1 bg-primary-foreground" />
148
+ <div className="w-1/3 py-1 bg-primary" />
149
+ </div>
150
+ </div>
151
+ );
152
+ }
153
+
154
+ export { Footer_C };
@@ -0,0 +1,154 @@
1
+ import { Button } from "@stackshift-ui/button";
2
+ import { Container } from "@stackshift-ui/container";
3
+ import { Flex } from "@stackshift-ui/flex";
4
+ import { Image } from "@stackshift-ui/image";
5
+ import { Link } from "@stackshift-ui/link";
6
+ import { Section } from "@stackshift-ui/section";
7
+ import { SocialIcons } from "@stackshift-ui/social-icons";
8
+ import { Text } from "@stackshift-ui/text";
9
+
10
+ import { FooterProps } from ".";
11
+ import { logoLink } from "./helper";
12
+ import { Logo, SocialLink, Socials } from "./types";
13
+
14
+ export default function Footer_D({
15
+ logo,
16
+ multipleMenus,
17
+ copyright,
18
+ socialMedia,
19
+ text,
20
+ }: FooterProps) {
21
+ return (
22
+ <Section className="py-20 overflow-hidden bg-background">
23
+ <Container maxWidth={1280}>
24
+ <Flex wrap className="space-y-4 lg:space-y-0">
25
+ <div className="w-full max-w-2xl space-y-4 lg:w-1/4">
26
+ <LogoSection logo={logo} />
27
+ <TextSection text={text} />
28
+ </div>
29
+
30
+ <MenuSection multipleMenus={multipleMenus} />
31
+ <SocialMediaContainer socialMedia={socialMedia} />
32
+ </Flex>
33
+ <CopyrightSection copyright={copyright} />
34
+ </Container>
35
+ </Section>
36
+ );
37
+ }
38
+
39
+ function LogoSection({ logo }: { logo?: Logo }) {
40
+ if (!logo?.image) return null;
41
+
42
+ return (
43
+ <Link
44
+ className="inline-block text-xl font-bold leading-none"
45
+ aria-label={logoLink(logo) === "/" ? "Go to home page" : `Go to ${logoLink(logo)}`}
46
+ href={logoLink(logo)}
47
+ target={logo?.linkTarget}
48
+ rel={logo?.linkTarget === "_blank" ? "noopener noreferrer" : ""}>
49
+ <Image
50
+ className="h-14"
51
+ src={`${logo?.image}`}
52
+ sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
53
+ width={132}
54
+ height={56}
55
+ alt={logo?.alt ?? "footer-logo"}
56
+ />
57
+ </Link>
58
+ );
59
+ }
60
+
61
+ function TextSection({ text }: { text?: string }) {
62
+ if (!text) return null;
63
+
64
+ return (
65
+ <Text muted className="leading-normal">
66
+ {text}
67
+ </Text>
68
+ );
69
+ }
70
+
71
+ function CopyrightSection({ copyright }: { copyright?: string }) {
72
+ if (!copyright) return null;
73
+
74
+ return <Text className="pt-10 text-sm">{copyright}</Text>;
75
+ }
76
+
77
+ function MenuSection({ multipleMenus }: { multipleMenus?: any[] }) {
78
+ if (!multipleMenus) return null;
79
+
80
+ return (
81
+ <Flex wrap className="flex-grow lg:justify-around" gap={4}>
82
+ {multipleMenus?.map(menu => <MenuLinks key={menu?._key} menu={menu} />)}
83
+ </Flex>
84
+ );
85
+ }
86
+
87
+ // Individual Menu Links Component
88
+ function MenuLinks({ menu }: { menu?: any }) {
89
+ if (!menu) return null;
90
+
91
+ return (
92
+ <div className="w-[200px] flex-none">
93
+ {menu?.title && (
94
+ <Text weight="bold" className="mb-3">
95
+ {menu?.title}
96
+ </Text>
97
+ )}
98
+ {menu?.links?.length > 0 && (
99
+ <Flex as="ul" direction="col" className="space-y-2">
100
+ {menu?.links?.map((link: any) => (
101
+ <li key={link?._key}>
102
+ <Button
103
+ as="link"
104
+ link={link}
105
+ className="text-gray-500 no-underline hover:text-gray-700"
106
+ ariaLabel={link?.label}>
107
+ {link?.label}
108
+ </Button>
109
+ </li>
110
+ ))}
111
+ </Flex>
112
+ )}
113
+ </div>
114
+ );
115
+ }
116
+
117
+ function SocialMediaContainer({ socialMedia }: { socialMedia?: SocialLink[] }) {
118
+ if (!socialMedia) return null;
119
+
120
+ return (
121
+ <Flex className="w-full ml-auto lg:justify-end lg:w-1/6">
122
+ {socialMedia?.map(social => social?.socialMediaLink && <SocialMediaLink social={social} />)}
123
+ </Flex>
124
+ );
125
+ }
126
+
127
+ function SocialMediaLink({ social }: { social?: SocialLink }) {
128
+ if (!social?.socialMediaLink) return null;
129
+
130
+ return (
131
+ <span key={social?._key}>
132
+ <Link
133
+ aria-label={social?.socialMedia || social?.socialMediaPlatform || ""}
134
+ className="inline-block p-2 mr-2 rounded bg-gray-50 hover:bg-gray-100"
135
+ target="_blank"
136
+ rel="noopener noreferrer"
137
+ href={social?.socialMediaLink}>
138
+ {social?.socialMediaIcon?.image ? (
139
+ <Image
140
+ className="h-6"
141
+ src={`${social?.socialMediaIcon?.image}`}
142
+ width={24}
143
+ height={24}
144
+ alt={social?.socialMediaIcon?.alt ?? "contact-socialMedia-icon"}
145
+ />
146
+ ) : (
147
+ <SocialIcons social={social?.socialMedia as Socials} />
148
+ )}
149
+ </Link>
150
+ </span>
151
+ );
152
+ }
153
+
154
+ export { Footer_D };
@@ -0,0 +1,15 @@
1
+ import { Logo } from "../types";
2
+
3
+ // Logo link
4
+ export const logoLink = (logo: Logo) => {
5
+ if (logo?.internalLink && logo?.type === "linkInternal") {
6
+ if (logo?.internalLink?.toLowerCase()?.includes("home")) {
7
+ return "/";
8
+ }
9
+ return `/${logo.internalLink}`;
10
+ } else if (logo?.externalLink && logo?.type === "linkExternal") {
11
+ return logo?.externalLink ?? "/";
12
+ } else {
13
+ return "/";
14
+ }
15
+ };
package/src/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ "use client";
2
+
3
+ // component exports
4
+ export * from "./footer";
5
+ export * from "./footer_a";
6
+ export * from "./footer_b";
7
+ export * from "./footer_c";
8
+ export * from "./footer_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
+ }