@shohojdhara/atomix 0.4.4 → 0.4.6
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/atomix.css +50 -11
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +1 -1
- package/dist/atomix.min.css.map +1 -1
- package/dist/charts.js +184 -189
- package/dist/charts.js.map +1 -1
- package/dist/core.d.ts +4 -4
- package/dist/core.js +194 -199
- package/dist/core.js.map +1 -1
- package/dist/forms.js +184 -189
- package/dist/forms.js.map +1 -1
- package/dist/heavy.js +189 -194
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +44 -47
- package/dist/index.esm.js +496 -613
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +528 -631
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/package.json +1 -1
- package/src/components/AtomixGlass/AtomixGlass.tsx +60 -39
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +8 -42
- package/src/components/AtomixGlass/glass-utils.ts +27 -14
- package/src/components/AtomixGlass/stories/Overview.stories.tsx +19 -21
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +1162 -515
- package/src/components/AtomixGlass/stories/shared-components.tsx +11 -3
- package/src/components/Breadcrumb/Breadcrumb.tsx +5 -5
- package/src/components/Breadcrumb/BreadcrumbCompound.test.tsx +2 -2
- package/src/components/Button/Button.tsx +6 -6
- package/src/components/Card/Card.tsx +3 -3
- package/src/components/Dropdown/Dropdown.tsx +5 -3
- package/src/components/Footer/Footer.tsx +124 -166
- package/src/components/Footer/FooterLink.tsx +16 -19
- package/src/components/Footer/FooterSection.tsx +40 -39
- package/src/components/Footer/FooterSocialLink.tsx +59 -58
- package/src/components/Footer/README.md +1 -1
- package/src/components/Hero/Hero.tsx +72 -142
- package/src/components/Navigation/Menu/MegaMenu.tsx +17 -12
- package/src/components/Navigation/Menu/Menu.tsx +49 -24
- package/src/components/Navigation/Nav/NavItem.tsx +5 -3
- package/src/components/Navigation/Navbar/Navbar.tsx +13 -5
- package/src/components/Navigation/SideMenu/SideMenu.tsx +2 -2
- package/src/components/Navigation/SideMenu/SideMenuItem.tsx +4 -4
- package/src/components/Slider/Slider.tsx +7 -4
- package/src/lib/composables/index.ts +1 -2
- package/src/lib/composables/useAtomixGlass.ts +246 -222
- package/src/lib/composables/useAtomixGlassStyles.ts +46 -23
- package/src/lib/composables/useFooter.ts +117 -20
- package/src/lib/composables/useSlider.ts +3 -1
- package/src/lib/constants/components.ts +3 -1
- package/src/lib/types/components.ts +44 -12
- package/src/styles/06-components/_components.atomix-glass.scss +72 -14
- package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +0 -222
- package/src/lib/composables/atomix-glass/useGlassBackgroundDetection.ts +0 -329
- package/src/lib/composables/atomix-glass/useGlassCornerRadius.ts +0 -82
- package/src/lib/composables/atomix-glass/useGlassMouseTracking.ts +0 -153
- package/src/lib/composables/atomix-glass/useGlassOverLight.ts +0 -198
- package/src/lib/composables/atomix-glass/useGlassState.ts +0 -112
- package/src/lib/composables/atomix-glass/useGlassTransforms.ts +0 -160
- package/src/lib/composables/glass-styles.ts +0 -302
- package/src/lib/composables/useGlassContainer.ts +0 -177
|
@@ -75,16 +75,18 @@ export const BackgroundWrapper: React.FC<BackgroundWrapperProps> = ({
|
|
|
75
75
|
|
|
76
76
|
const bgStyle = {
|
|
77
77
|
backgroundImage: bgImage ? `url(${bgImage})` : undefined,
|
|
78
|
+
backgroundSize: 'cover',
|
|
79
|
+
backgroundPosition: 'center',
|
|
80
|
+
backgroundRepeat: 'no-repeat',
|
|
78
81
|
height,
|
|
79
82
|
width,
|
|
80
83
|
borderRadius,
|
|
81
|
-
padding,
|
|
82
84
|
...style,
|
|
83
85
|
};
|
|
84
86
|
|
|
85
87
|
return (
|
|
86
88
|
<div
|
|
87
|
-
className={`u-relative u-overflow-hidden ${className}`}
|
|
89
|
+
className={`u-relative u-overflow-hidden u-flex u-items-center u-justify-center ${className}`}
|
|
88
90
|
style={bgStyle}
|
|
89
91
|
aria-hidden={ariaHidden}
|
|
90
92
|
>
|
|
@@ -94,10 +96,16 @@ export const BackgroundWrapper: React.FC<BackgroundWrapperProps> = ({
|
|
|
94
96
|
style={{
|
|
95
97
|
backgroundColor: overlayColor,
|
|
96
98
|
opacity: overlayOpacity,
|
|
99
|
+
padding,
|
|
97
100
|
}}
|
|
98
101
|
/>
|
|
99
102
|
)}
|
|
100
|
-
<div
|
|
103
|
+
<div
|
|
104
|
+
className="u-relative u-z-10 u-w-100 u-h-100 u-flex u-items-center u-justify-center"
|
|
105
|
+
style={{ padding }}
|
|
106
|
+
>
|
|
107
|
+
{children}
|
|
108
|
+
</div>
|
|
101
109
|
</div>
|
|
102
110
|
);
|
|
103
111
|
};
|
|
@@ -78,7 +78,7 @@ export interface BreadcrumbItemProps extends React.HTMLAttributes<HTMLLIElement>
|
|
|
78
78
|
linkAs?: React.ElementType<any>;
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
|
-
* Link props to pass to the underlying anchor or
|
|
81
|
+
* Link props to pass to the underlying anchor or linkComponent
|
|
82
82
|
*/
|
|
83
83
|
linkProps?: Record<string, any>;
|
|
84
84
|
}
|
|
@@ -169,7 +169,7 @@ export interface BreadcrumbProps {
|
|
|
169
169
|
/**
|
|
170
170
|
* Optional custom link component
|
|
171
171
|
*/
|
|
172
|
-
|
|
172
|
+
linkComponent?: React.ElementType;
|
|
173
173
|
|
|
174
174
|
/**
|
|
175
175
|
* Custom style for the breadcrumb
|
|
@@ -187,7 +187,7 @@ const BreadcrumbComponent: React.FC<BreadcrumbProps> = memo(function BreadcrumbB
|
|
|
187
187
|
divider,
|
|
188
188
|
className = '',
|
|
189
189
|
'aria-label': ariaLabel = 'Breadcrumb',
|
|
190
|
-
|
|
190
|
+
linkComponent,
|
|
191
191
|
style,
|
|
192
192
|
children,
|
|
193
193
|
}: BreadcrumbProps) {
|
|
@@ -209,7 +209,7 @@ const BreadcrumbComponent: React.FC<BreadcrumbProps> = memo(function BreadcrumbB
|
|
|
209
209
|
onClick={item.onClick as any}
|
|
210
210
|
className={item.className}
|
|
211
211
|
style={item.style}
|
|
212
|
-
linkAs={
|
|
212
|
+
linkAs={linkComponent}
|
|
213
213
|
>
|
|
214
214
|
{item.label}
|
|
215
215
|
</BreadcrumbItem>
|
|
@@ -228,7 +228,7 @@ const BreadcrumbComponent: React.FC<BreadcrumbProps> = memo(function BreadcrumbB
|
|
|
228
228
|
|
|
229
229
|
const newProps = {
|
|
230
230
|
active: active ?? (isLast ? true : undefined),
|
|
231
|
-
linkAs: linkAs ??
|
|
231
|
+
linkAs: linkAs ?? linkComponent,
|
|
232
232
|
};
|
|
233
233
|
|
|
234
234
|
return cloneElement(child, newProps as any);
|
|
@@ -46,7 +46,7 @@ describe('Breadcrumb Component', () => {
|
|
|
46
46
|
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
-
it('supports custom
|
|
49
|
+
it('supports custom linkComponent in compound components', () => {
|
|
50
50
|
const CustomLink = ({ href, children, ...props }: any) => (
|
|
51
51
|
<a href={href} data-testid="custom-link" {...props}>
|
|
52
52
|
{children} (Custom)
|
|
@@ -54,7 +54,7 @@ describe('Breadcrumb Component', () => {
|
|
|
54
54
|
);
|
|
55
55
|
|
|
56
56
|
render(
|
|
57
|
-
<Breadcrumb
|
|
57
|
+
<Breadcrumb linkComponent={CustomLink}>
|
|
58
58
|
<Breadcrumb.Item href="/custom">Link</Breadcrumb.Item>
|
|
59
59
|
<Breadcrumb.Item>Current</Breadcrumb.Item>
|
|
60
60
|
</Breadcrumb>
|
|
@@ -10,7 +10,7 @@ export type ButtonAsProp = {
|
|
|
10
10
|
as?: ElementType;
|
|
11
11
|
to?: string;
|
|
12
12
|
href?: string;
|
|
13
|
-
|
|
13
|
+
linkComponent?: React.ElementType;
|
|
14
14
|
[key: string]: any;
|
|
15
15
|
};
|
|
16
16
|
|
|
@@ -51,7 +51,7 @@ export const Button = React.memo(
|
|
|
51
51
|
'aria-controls': ariaControls,
|
|
52
52
|
tabIndex,
|
|
53
53
|
style,
|
|
54
|
-
|
|
54
|
+
linkComponent,
|
|
55
55
|
...props
|
|
56
56
|
},
|
|
57
57
|
ref
|
|
@@ -220,12 +220,12 @@ export const Button = React.memo(
|
|
|
220
220
|
|
|
221
221
|
// Render as anchor if href is provided
|
|
222
222
|
if (shouldRenderAsLink) {
|
|
223
|
-
// Use custom
|
|
224
|
-
if (
|
|
225
|
-
const LinkComp =
|
|
223
|
+
// Use custom linkComponent if provided (e.g., Next.js Link)
|
|
224
|
+
if (linkComponent) {
|
|
225
|
+
const LinkComp = linkComponent as React.ComponentType<any>;
|
|
226
226
|
const linkProps = {
|
|
227
227
|
...buttonProps,
|
|
228
|
-
ref: ref as any, //
|
|
228
|
+
ref: ref as any, // linkComponent usually forwards ref to anchor
|
|
229
229
|
href,
|
|
230
230
|
to: href,
|
|
231
231
|
target,
|
|
@@ -40,7 +40,7 @@ export const Card = React.memo(
|
|
|
40
40
|
href,
|
|
41
41
|
target,
|
|
42
42
|
// Custom Link
|
|
43
|
-
|
|
43
|
+
linkComponent,
|
|
44
44
|
// Glass
|
|
45
45
|
glass,
|
|
46
46
|
// Accessibility
|
|
@@ -246,8 +246,8 @@ export const Card = React.memo(
|
|
|
246
246
|
if (href && !isDisabled) {
|
|
247
247
|
let anchorElement: React.ReactElement;
|
|
248
248
|
|
|
249
|
-
if (
|
|
250
|
-
const LinkComp =
|
|
249
|
+
if (linkComponent) {
|
|
250
|
+
const LinkComp = linkComponent as React.ComponentType<any>;
|
|
251
251
|
anchorElement = (
|
|
252
252
|
<LinkComp
|
|
253
253
|
{...commonProps}
|
|
@@ -90,12 +90,13 @@ export const DropdownItem: React.FC<DropdownItemProps> = memo(
|
|
|
90
90
|
({
|
|
91
91
|
children,
|
|
92
92
|
href,
|
|
93
|
+
target,
|
|
93
94
|
active = false,
|
|
94
95
|
disabled = false,
|
|
95
96
|
icon,
|
|
96
97
|
onClick,
|
|
97
98
|
className = '',
|
|
98
|
-
|
|
99
|
+
linkComponent,
|
|
99
100
|
...props
|
|
100
101
|
}) => {
|
|
101
102
|
const { close } = useContext(DropdownContext);
|
|
@@ -126,6 +127,7 @@ export const DropdownItem: React.FC<DropdownItemProps> = memo(
|
|
|
126
127
|
const linkProps = {
|
|
127
128
|
href,
|
|
128
129
|
to: href,
|
|
130
|
+
target,
|
|
129
131
|
className: itemClasses,
|
|
130
132
|
onClick: handleClick,
|
|
131
133
|
role: 'menuitem',
|
|
@@ -136,9 +138,9 @@ export const DropdownItem: React.FC<DropdownItemProps> = memo(
|
|
|
136
138
|
if (href && !disabled) {
|
|
137
139
|
return (
|
|
138
140
|
<li>
|
|
139
|
-
{
|
|
141
|
+
{linkComponent ? (
|
|
140
142
|
(() => {
|
|
141
|
-
const Component =
|
|
143
|
+
const Component = linkComponent as React.ComponentType<any>;
|
|
142
144
|
return (
|
|
143
145
|
<Component {...linkProps}>
|
|
144
146
|
{icon && <span className="c-dropdown__menu-item-icon">{icon}</span>}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { forwardRef } from 'react';
|
|
2
|
-
import { FooterProps } from '../../lib/types/components';
|
|
2
|
+
import { FooterProps, AtomixGlassProps } from '../../lib/types/components';
|
|
3
3
|
import { useFooter } from '../../lib/composables/useFooter';
|
|
4
4
|
import { Button } from '../Button';
|
|
5
5
|
import { Input, Form } from '../Form';
|
|
@@ -7,7 +7,6 @@ import { FooterSocialLink } from './FooterSocialLink';
|
|
|
7
7
|
import { Grid, GridCol } from '../../layouts/Grid';
|
|
8
8
|
import { FooterSection } from './FooterSection';
|
|
9
9
|
import AtomixGlass from '../AtomixGlass/AtomixGlass';
|
|
10
|
-
import { AtomixGlassProps } from '../../lib/types/components';
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
12
|
* Footer component provides a comprehensive footer section with multiple layout options,
|
|
@@ -68,9 +67,9 @@ export const Footer = forwardRef<HTMLElement, FooterProps>(
|
|
|
68
67
|
brandClass,
|
|
69
68
|
sectionsClass,
|
|
70
69
|
bottomClass,
|
|
70
|
+
getResponsiveColumnProps,
|
|
71
71
|
handleNewsletterSubmit,
|
|
72
72
|
handleBackToTop,
|
|
73
|
-
socialLinks: footerSocialLinks,
|
|
74
73
|
} = useFooter({
|
|
75
74
|
layout,
|
|
76
75
|
variant,
|
|
@@ -78,179 +77,118 @@ export const Footer = forwardRef<HTMLElement, FooterProps>(
|
|
|
78
77
|
sticky,
|
|
79
78
|
showNewsletter,
|
|
80
79
|
showBackToTop,
|
|
80
|
+
showDivider,
|
|
81
81
|
socialLinks,
|
|
82
82
|
onNewsletterSubmit,
|
|
83
83
|
onBackToTop,
|
|
84
|
+
glass: Boolean(glass),
|
|
84
85
|
className,
|
|
85
86
|
});
|
|
86
87
|
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
case 'columns':
|
|
91
|
-
// For columns layout, we have 3 columns (brand, content, newsletter)
|
|
92
|
-
return {
|
|
93
|
-
brand: 4,
|
|
94
|
-
content: !showNewsletter ? 8 : 4,
|
|
95
|
-
newsletter: !showNewsletter ? 0 : 4,
|
|
96
|
-
};
|
|
97
|
-
case 'centered':
|
|
98
|
-
// For centered layout, brand takes full width, content and newsletter are centered
|
|
99
|
-
return { brand: 12, content: 12, newsletter: !showNewsletter ? 0 : 12 };
|
|
100
|
-
case 'minimal':
|
|
101
|
-
// For minimal layout, everything takes full width
|
|
102
|
-
return { brand: 12, content: 12, newsletter: !showNewsletter ? 0 : 12 };
|
|
103
|
-
case 'stacked':
|
|
104
|
-
// For stacked layout, everything takes full width but stacked vertically
|
|
105
|
-
return { brand: 12, content: 12, newsletter: !showNewsletter ? 0 : 12 };
|
|
106
|
-
case 'flexible':
|
|
107
|
-
// For flexible layout, adjust based on content
|
|
108
|
-
return { brand: 'auto', content: 'auto', newsletter: 'auto' };
|
|
109
|
-
case 'sidebar':
|
|
110
|
-
// For sidebar layout, brand on left, content and newsletter on right
|
|
111
|
-
return {
|
|
112
|
-
brand: 3,
|
|
113
|
-
content: !showNewsletter ? 9 : 9,
|
|
114
|
-
newsletter: !showNewsletter ? 0 : 9,
|
|
115
|
-
};
|
|
116
|
-
case 'wide':
|
|
117
|
-
// For wide layout, content takes more space
|
|
118
|
-
return {
|
|
119
|
-
brand: 3,
|
|
120
|
-
content: !showNewsletter ? 6 : 6,
|
|
121
|
-
newsletter: !showNewsletter ? 0 : 3,
|
|
122
|
-
};
|
|
123
|
-
default:
|
|
124
|
-
return {
|
|
125
|
-
brand: 4,
|
|
126
|
-
content: !showNewsletter ? 8 : 4,
|
|
127
|
-
newsletter: !showNewsletter ? 0 : 4,
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
};
|
|
88
|
+
// ──────────────────────────────────────────
|
|
89
|
+
// Render helpers
|
|
90
|
+
// ──────────────────────────────────────────
|
|
131
91
|
|
|
132
|
-
const
|
|
92
|
+
const renderBrandSection = () => {
|
|
93
|
+
if (!brand && !brandLogo && !brandDescription) return null;
|
|
133
94
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
95
|
+
return (
|
|
96
|
+
<GridCol {...(getResponsiveColumnProps('brand') as any)} className={brandClass}>
|
|
97
|
+
{brandLogo && (
|
|
98
|
+
<div className="c-footer__brand-logo">
|
|
99
|
+
{typeof brandLogo === 'string' ? <img src={brandLogo} alt="Brand Logo" /> : brandLogo}
|
|
100
|
+
</div>
|
|
101
|
+
)}
|
|
102
|
+
{brand && (
|
|
103
|
+
<div className="c-footer__brand-name">
|
|
104
|
+
{typeof brand === 'string' ? <h3>{brand}</h3> : brand}
|
|
105
|
+
</div>
|
|
106
|
+
)}
|
|
107
|
+
{brandDescription && (
|
|
108
|
+
<div className="c-footer__brand-description">{brandDescription}</div>
|
|
109
|
+
)}
|
|
110
|
+
{socialLinks.length > 0 && (
|
|
111
|
+
<div className="c-footer__social" data-testid="footer-social-links">
|
|
112
|
+
{socialLinks.map((link, index) => (
|
|
113
|
+
<FooterSocialLink
|
|
114
|
+
key={`${link.platform}-${index}`}
|
|
115
|
+
platform={link.platform}
|
|
116
|
+
url={link.url}
|
|
117
|
+
icon={link.icon}
|
|
118
|
+
label={link.label}
|
|
119
|
+
size={size}
|
|
120
|
+
/>
|
|
121
|
+
))}
|
|
122
|
+
</div>
|
|
123
|
+
)}
|
|
124
|
+
</GridCol>
|
|
125
|
+
);
|
|
126
|
+
};
|
|
140
127
|
|
|
141
|
-
|
|
142
|
-
if (
|
|
143
|
-
return { xs: 12, sm: true, md: true };
|
|
144
|
-
}
|
|
128
|
+
const renderSections = () => {
|
|
129
|
+
if (!children) return null;
|
|
145
130
|
|
|
146
|
-
|
|
147
|
-
|
|
131
|
+
return (
|
|
132
|
+
<GridCol {...(getResponsiveColumnProps('content') as any)} className="c-footer__content">
|
|
133
|
+
<Grid
|
|
134
|
+
className="c-footer__sections"
|
|
135
|
+
alignItems={layout === 'centered' || layout === 'stacked' ? 'center' : undefined}
|
|
136
|
+
>
|
|
137
|
+
{React.Children.map(children, child => {
|
|
138
|
+
if (React.isValidElement(child)) {
|
|
139
|
+
return React.cloneElement(child, { showNewsletter } as any);
|
|
140
|
+
}
|
|
141
|
+
return child;
|
|
142
|
+
})}
|
|
143
|
+
</Grid>
|
|
144
|
+
</GridCol>
|
|
145
|
+
);
|
|
148
146
|
};
|
|
149
147
|
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
148
|
+
const renderNewsletter = () => {
|
|
149
|
+
if (!showNewsletter) return null;
|
|
150
|
+
|
|
151
|
+
return (
|
|
152
|
+
<GridCol
|
|
153
|
+
{...(getResponsiveColumnProps('newsletter') as any)}
|
|
154
|
+
className="c-footer__newsletter"
|
|
157
155
|
>
|
|
158
|
-
{
|
|
159
|
-
{
|
|
160
|
-
<
|
|
161
|
-
{brandLogo && (
|
|
162
|
-
<div className="c-footer__brand-logo">
|
|
163
|
-
{typeof brandLogo === 'string' ? (
|
|
164
|
-
<img src={brandLogo} alt={'Brand Logo'} />
|
|
165
|
-
) : (
|
|
166
|
-
brandLogo
|
|
167
|
-
)}
|
|
168
|
-
</div>
|
|
169
|
-
)}
|
|
170
|
-
{brand && (
|
|
171
|
-
<div className="c-footer__brand-name">
|
|
172
|
-
{typeof brand === 'string' ? <h3>{brand}</h3> : brand}
|
|
173
|
-
</div>
|
|
174
|
-
)}
|
|
175
|
-
{brandDescription && (
|
|
176
|
-
<div className="c-footer__brand-description">{brandDescription}</div>
|
|
177
|
-
)}
|
|
178
|
-
{socialLinks.length > 0 && (
|
|
179
|
-
<div className="c-footer__social" data-testid="footer-social-links">
|
|
180
|
-
{socialLinks.map((link, index) => (
|
|
181
|
-
<FooterSocialLink
|
|
182
|
-
key={`${link.platform}-${index}`}
|
|
183
|
-
platform={link.platform}
|
|
184
|
-
url={link.url}
|
|
185
|
-
icon={link.icon}
|
|
186
|
-
label={link.label}
|
|
187
|
-
size={size}
|
|
188
|
-
/>
|
|
189
|
-
))}
|
|
190
|
-
</div>
|
|
191
|
-
)}
|
|
192
|
-
</GridCol>
|
|
156
|
+
<h4 className="c-footer__newsletter-title">{newsletterTitle}</h4>
|
|
157
|
+
{newsletterDescription && (
|
|
158
|
+
<p className="c-footer__newsletter-description">{newsletterDescription}</p>
|
|
193
159
|
)}
|
|
160
|
+
<Form
|
|
161
|
+
className="c-footer__newsletter-form"
|
|
162
|
+
onSubmit={e => {
|
|
163
|
+
e.preventDefault();
|
|
164
|
+
const formData = new FormData(e.currentTarget);
|
|
165
|
+
const email = formData.get('email') as string;
|
|
166
|
+
if (email) handleNewsletterSubmit(email);
|
|
167
|
+
}}
|
|
168
|
+
>
|
|
169
|
+
<div className="c-footer__newsletter-input-group">
|
|
170
|
+
<Input
|
|
171
|
+
type="email"
|
|
172
|
+
name="email"
|
|
173
|
+
className="c-footer__newsletter-input"
|
|
174
|
+
placeholder={newsletterPlaceholder}
|
|
175
|
+
required
|
|
176
|
+
/>
|
|
177
|
+
<Button type="submit" className="c-footer__newsletter-button">
|
|
178
|
+
{newsletterButtonText}
|
|
179
|
+
</Button>
|
|
180
|
+
</div>
|
|
181
|
+
</Form>
|
|
182
|
+
</GridCol>
|
|
183
|
+
);
|
|
184
|
+
};
|
|
194
185
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
<GridCol
|
|
198
|
-
{...(getResponsiveColumnProps('content') as any)}
|
|
199
|
-
className="c-footer__content"
|
|
200
|
-
>
|
|
201
|
-
<Grid
|
|
202
|
-
className="c-footer__sections"
|
|
203
|
-
alignItems={layout === 'centered' || layout === 'stacked' ? 'center' : undefined}
|
|
204
|
-
>
|
|
205
|
-
{React.Children.map(children, child => {
|
|
206
|
-
// Check if the child is a valid React element
|
|
207
|
-
if (React.isValidElement(child)) {
|
|
208
|
-
// Clone the element and pass the showNewsletter prop
|
|
209
|
-
return React.cloneElement(child, { showNewsletter } as any);
|
|
210
|
-
}
|
|
211
|
-
return child;
|
|
212
|
-
})}
|
|
213
|
-
</Grid>
|
|
214
|
-
</GridCol>
|
|
215
|
-
)}
|
|
186
|
+
const renderBottom = () => {
|
|
187
|
+
if (!copyright && !showBackToTop) return null;
|
|
216
188
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
{...(getResponsiveColumnProps('newsletter') as any)}
|
|
221
|
-
className="c-footer__newsletter"
|
|
222
|
-
>
|
|
223
|
-
<h4 className="c-footer__newsletter-title">{newsletterTitle}</h4>
|
|
224
|
-
{newsletterDescription && (
|
|
225
|
-
<p className="c-footer__newsletter-description">{newsletterDescription}</p>
|
|
226
|
-
)}
|
|
227
|
-
<Form
|
|
228
|
-
className="c-footer__newsletter-form"
|
|
229
|
-
onSubmit={e => {
|
|
230
|
-
e.preventDefault();
|
|
231
|
-
const formData = new FormData(e.currentTarget);
|
|
232
|
-
const email = formData.get('email') as string;
|
|
233
|
-
if (email) handleNewsletterSubmit(email);
|
|
234
|
-
}}
|
|
235
|
-
>
|
|
236
|
-
<div className="c-footer__newsletter-input-group">
|
|
237
|
-
<Input
|
|
238
|
-
type="email"
|
|
239
|
-
name="email"
|
|
240
|
-
className="c-footer__newsletter-input"
|
|
241
|
-
placeholder={newsletterPlaceholder}
|
|
242
|
-
required
|
|
243
|
-
/>
|
|
244
|
-
<Button type="submit" className="c-footer__newsletter-button">
|
|
245
|
-
{newsletterButtonText}
|
|
246
|
-
</Button>
|
|
247
|
-
</div>
|
|
248
|
-
</Form>
|
|
249
|
-
</GridCol>
|
|
250
|
-
)}
|
|
251
|
-
</Grid>
|
|
252
|
-
|
|
253
|
-
{(copyright || showBackToTop) && (
|
|
189
|
+
return (
|
|
190
|
+
<>
|
|
191
|
+
{showDivider && <hr className="c-footer__divider" />}
|
|
254
192
|
<div className={bottomClass}>
|
|
255
193
|
{copyright && <div className="c-footer__copyright">{copyright}</div>}
|
|
256
194
|
{showBackToTop && (
|
|
@@ -266,16 +204,36 @@ export const Footer = forwardRef<HTMLElement, FooterProps>(
|
|
|
266
204
|
</Button>
|
|
267
205
|
)}
|
|
268
206
|
</div>
|
|
269
|
-
|
|
207
|
+
</>
|
|
208
|
+
);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// ──────────────────────────────────────────
|
|
212
|
+
// Main content
|
|
213
|
+
// ──────────────────────────────────────────
|
|
214
|
+
|
|
215
|
+
const footerContent = (
|
|
216
|
+
<div className={containerClass}>
|
|
217
|
+
<Grid
|
|
218
|
+
className={sectionsClass}
|
|
219
|
+
alignItems="start"
|
|
220
|
+
justifyContent={layout === 'centered' ? 'center' : undefined}
|
|
221
|
+
>
|
|
222
|
+
{renderBrandSection()}
|
|
223
|
+
{renderSections()}
|
|
224
|
+
{renderNewsletter()}
|
|
225
|
+
</Grid>
|
|
226
|
+
|
|
227
|
+
{renderBottom()}
|
|
270
228
|
</div>
|
|
271
229
|
);
|
|
272
230
|
|
|
231
|
+
// ──────────────────────────────────────────
|
|
232
|
+
// Root element
|
|
233
|
+
// ──────────────────────────────────────────
|
|
234
|
+
|
|
273
235
|
return (
|
|
274
|
-
<footer
|
|
275
|
-
ref={ref}
|
|
276
|
-
className={footerClass + ` c-footer ${glass ? 'c-footer--glass' : ''}`}
|
|
277
|
-
{...props}
|
|
278
|
-
>
|
|
236
|
+
<footer ref={ref} className={footerClass} {...props}>
|
|
279
237
|
{glass ? (
|
|
280
238
|
<AtomixGlass {...(glass as unknown as AtomixGlassProps)} elasticity={0}>
|
|
281
239
|
<div className="c-footer__glass">{footerContent}</div>
|
|
@@ -22,7 +22,7 @@ export const FooterLink = forwardRef<HTMLAnchorElement, FooterLinkProps>(
|
|
|
22
22
|
onClick,
|
|
23
23
|
children,
|
|
24
24
|
className = '',
|
|
25
|
-
|
|
25
|
+
linkComponent,
|
|
26
26
|
...props
|
|
27
27
|
},
|
|
28
28
|
ref
|
|
@@ -36,40 +36,37 @@ export const FooterLink = forwardRef<HTMLAnchorElement, FooterLinkProps>(
|
|
|
36
36
|
.filter(Boolean)
|
|
37
37
|
.join(' ');
|
|
38
38
|
|
|
39
|
-
const
|
|
39
|
+
const sharedProps = {
|
|
40
40
|
className: linkClass,
|
|
41
41
|
onClick: disabled ? undefined : onClick,
|
|
42
42
|
'aria-disabled': disabled,
|
|
43
43
|
...(external && {
|
|
44
|
-
target: '_blank',
|
|
44
|
+
target: '_blank' as const,
|
|
45
45
|
rel: 'noopener noreferrer',
|
|
46
46
|
}),
|
|
47
47
|
...props,
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
};
|
|
50
|
+
const linkContent = (
|
|
51
|
+
<>
|
|
52
|
+
{icon && <span className="c-footer__link-icon">{icon}</span>}
|
|
53
|
+
<span className="c-footer__link-text">{children}</span>
|
|
54
|
+
{external && <span className="c-footer__link-external">↗</span>}
|
|
55
|
+
</>
|
|
56
|
+
);
|
|
58
57
|
|
|
58
|
+
if (linkComponent) {
|
|
59
|
+
const Component = linkComponent as React.ComponentType<any>;
|
|
59
60
|
return (
|
|
60
|
-
<Component {...
|
|
61
|
-
{
|
|
62
|
-
<span className="c-footer__link-text">{children}</span>
|
|
63
|
-
{external && <span className="c-footer__link-external">↗</span>}
|
|
61
|
+
<Component ref={ref} {...(href && !disabled ? { href, to: href } : {})} {...sharedProps}>
|
|
62
|
+
{linkContent}
|
|
64
63
|
</Component>
|
|
65
64
|
);
|
|
66
65
|
}
|
|
67
66
|
|
|
68
67
|
return (
|
|
69
|
-
<a ref={ref} href={disabled ? undefined : href} {...
|
|
70
|
-
{
|
|
71
|
-
<span className="c-footer__link-text">{children}</span>
|
|
72
|
-
{external && <span className="c-footer__link-external">↗</span>}
|
|
68
|
+
<a ref={ref} href={disabled ? undefined : href} {...sharedProps}>
|
|
69
|
+
{linkContent}
|
|
73
70
|
</a>
|
|
74
71
|
);
|
|
75
72
|
}
|