@shohojdhara/atomix 0.2.9 → 0.3.1
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/CHANGELOG.md +4 -0
- package/dist/atomix.css +309 -105
- package/dist/atomix.min.css +3 -5
- package/dist/index.d.ts +807 -51
- package/dist/index.esm.js +16367 -16405
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +16277 -16330
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/themes/applemix.css +309 -105
- package/dist/themes/applemix.min.css +5 -7
- package/dist/themes/boomdevs.css +202 -10
- package/dist/themes/boomdevs.min.css +3 -5
- package/dist/themes/esrar.css +309 -105
- package/dist/themes/esrar.min.css +4 -6
- package/dist/themes/flashtrade.css +310 -105
- package/dist/themes/flashtrade.min.css +5 -7
- package/dist/themes/mashroom.css +300 -96
- package/dist/themes/mashroom.min.css +4 -6
- package/dist/themes/shaj-default.css +300 -96
- package/dist/themes/shaj-default.min.css +4 -6
- package/package.json +1 -1
- package/src/components/AtomixGlass/AtomixGlass.test.tsx +21 -32
- package/src/components/AtomixGlass/AtomixGlass.tsx +55 -42
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +205 -57
- package/src/components/AtomixGlass/GlassFilter.tsx +22 -8
- package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +221 -0
- package/src/components/AtomixGlass/atomixGLass.old.tsx +0 -3
- package/src/components/AtomixGlass/shader-utils.ts +8 -0
- package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +319 -100
- package/src/components/AtomixGlass/stories/Examples.stories.tsx +601 -105
- package/src/components/AtomixGlass/stories/Modes.stories.tsx +30 -12
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +173 -38
- package/src/components/AtomixGlass/stories/ShaderVariants.stories.tsx +18 -18
- package/src/components/AtomixGlass/stories/shared-components.tsx +27 -5
- package/src/components/Breadcrumb/Breadcrumb.tsx +8 -3
- package/src/components/Button/Button.tsx +62 -17
- package/src/components/Callout/Callout.test.tsx +8 -14
- package/src/components/Card/Card.tsx +103 -1
- package/src/components/Card/index.ts +3 -2
- package/src/components/Footer/Footer.stories.tsx +1 -2
- package/src/components/Footer/Footer.tsx +0 -5
- package/src/components/Footer/FooterLink.tsx +3 -2
- package/src/components/Footer/FooterSection.tsx +0 -7
- package/src/components/Icon/index.ts +1 -1
- package/src/components/Modal/Modal.stories.tsx +29 -38
- package/src/components/Modal/Modal.tsx +4 -4
- package/src/components/Navigation/Nav/NavItem.tsx +8 -3
- package/src/components/Navigation/SideMenu/SideMenu.tsx +49 -41
- package/src/components/Navigation/SideMenu/SideMenuItem.tsx +63 -19
- package/src/components/Popover/Popover.tsx +1 -1
- package/src/components/VideoPlayer/VideoPlayer.stories.tsx +977 -400
- package/src/components/VideoPlayer/VideoPlayer.tsx +1 -6
- package/src/lib/composables/shared-mouse-tracker.ts +133 -0
- package/src/lib/composables/useAtomixGlass.ts +303 -115
- package/src/lib/theme/ThemeManager.integration.test.ts +124 -0
- package/src/lib/theme/ThemeManager.stories.tsx +13 -13
- package/src/lib/theme/ThemeManager.test.ts +4 -0
- package/src/lib/theme/ThemeManager.ts +203 -59
- package/src/lib/theme/ThemeProvider.tsx +183 -33
- package/src/lib/theme/composeTheme.ts +375 -0
- package/src/lib/theme/createTheme.test.ts +475 -0
- package/src/lib/theme/createTheme.ts +510 -0
- package/src/lib/theme/generateCSSVariables.ts +713 -0
- package/src/lib/theme/index.ts +67 -0
- package/src/lib/theme/themeUtils.ts +333 -0
- package/src/lib/theme/types.ts +337 -8
- package/src/lib/theme/useTheme.test.tsx +2 -1
- package/src/lib/theme/useTheme.ts +6 -22
- package/src/lib/types/components.ts +152 -57
- package/src/styles/01-settings/_index.scss +2 -2
- package/src/styles/01-settings/_settings.badge.scss +2 -2
- package/src/styles/01-settings/_settings.border-radius.scss +1 -1
- package/src/styles/01-settings/{_settings.maps.scss → _settings.design-tokens.scss} +163 -49
- package/src/styles/01-settings/_settings.modal.scss +1 -1
- package/src/styles/01-settings/_settings.spacing.scss +14 -13
- package/src/styles/03-generic/_generic.root.scss +131 -50
- package/src/styles/05-objects/_objects.block.scss +1 -1
- package/src/styles/06-components/_components.atomix-glass.scss +20 -22
- package/src/styles/06-components/_components.badge.scss +2 -2
- package/src/styles/06-components/_components.button.scss +1 -1
- package/src/styles/06-components/_components.callout.scss +1 -1
- package/src/styles/06-components/_components.card.scss +74 -2
- package/src/styles/06-components/_components.chart.scss +1 -1
- package/src/styles/06-components/_components.dropdown.scss +6 -0
- package/src/styles/06-components/_components.footer.scss +1 -1
- package/src/styles/06-components/_components.list-group.scss +1 -1
- package/src/styles/06-components/_components.list.scss +1 -1
- package/src/styles/06-components/_components.menu.scss +1 -1
- package/src/styles/06-components/_components.messages.scss +1 -1
- package/src/styles/06-components/_components.modal.scss +7 -2
- package/src/styles/06-components/_components.navbar.scss +1 -1
- package/src/styles/06-components/_components.popover.scss +10 -0
- package/src/styles/06-components/_components.product-review.scss +1 -1
- package/src/styles/06-components/_components.progress.scss +1 -1
- package/src/styles/06-components/_components.rating.scss +1 -1
- package/src/styles/06-components/_components.spinner.scss +1 -1
- package/src/styles/99-utilities/_utilities.background.scss +1 -1
- package/src/styles/99-utilities/_utilities.border.scss +1 -1
- package/src/styles/99-utilities/_utilities.link.scss +1 -1
- package/src/styles/99-utilities/_utilities.text.scss +1 -1
|
@@ -3,6 +3,7 @@ import { useButton } from '../../lib/composables/useButton';
|
|
|
3
3
|
import { ButtonProps } from '../../lib/types/components';
|
|
4
4
|
import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
|
|
5
5
|
import { Spinner } from '../Spinner/Spinner';
|
|
6
|
+
import { Icon, type PhosphorIconsType } from '../Icon/Icon';
|
|
6
7
|
import { BUTTON } from '../../lib/constants/components';
|
|
7
8
|
|
|
8
9
|
export type ButtonAsProp = {
|
|
@@ -13,7 +14,7 @@ export type ButtonAsProp = {
|
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
export const Button = React.memo(
|
|
16
|
-
forwardRef<HTMLButtonElement, ButtonProps & ButtonAsProp>(
|
|
17
|
+
forwardRef<HTMLButtonElement | HTMLAnchorElement, ButtonProps & ButtonAsProp>(
|
|
17
18
|
(
|
|
18
19
|
{
|
|
19
20
|
label,
|
|
@@ -25,6 +26,8 @@ export const Button = React.memo(
|
|
|
25
26
|
loading = false,
|
|
26
27
|
loadingText,
|
|
27
28
|
icon,
|
|
29
|
+
iconName,
|
|
30
|
+
iconSize = 'sm',
|
|
28
31
|
iconPosition = 'start',
|
|
29
32
|
iconOnly = false,
|
|
30
33
|
rounded = false,
|
|
@@ -35,6 +38,8 @@ export const Button = React.memo(
|
|
|
35
38
|
type = 'button',
|
|
36
39
|
className = '',
|
|
37
40
|
as: Component = 'button',
|
|
41
|
+
href,
|
|
42
|
+
target,
|
|
38
43
|
glass,
|
|
39
44
|
onHover,
|
|
40
45
|
onFocus,
|
|
@@ -51,6 +56,18 @@ export const Button = React.memo(
|
|
|
51
56
|
) => {
|
|
52
57
|
const isDisabled = disabled || loading;
|
|
53
58
|
|
|
59
|
+
// Determine if we should render as a link
|
|
60
|
+
const shouldRenderAsLink = Boolean(href && !isDisabled);
|
|
61
|
+
|
|
62
|
+
// Resolve icon element - support both icon (ReactNode) and iconName (string)
|
|
63
|
+
const iconElement = useMemo(() => {
|
|
64
|
+
if (loading) return null;
|
|
65
|
+
if (iconName) {
|
|
66
|
+
return <Icon name={iconName as PhosphorIconsType} size={iconSize} />;
|
|
67
|
+
}
|
|
68
|
+
return icon;
|
|
69
|
+
}, [icon, iconName, iconSize, loading]);
|
|
70
|
+
|
|
54
71
|
const { generateButtonClass, handleClick } = useButton({
|
|
55
72
|
variant,
|
|
56
73
|
size,
|
|
@@ -85,21 +102,21 @@ export const Button = React.memo(
|
|
|
85
102
|
|
|
86
103
|
// Handle click with loading check
|
|
87
104
|
const handleClickEvent = useCallback(
|
|
88
|
-
(event: React.MouseEvent<HTMLButtonElement>) => {
|
|
105
|
+
(event: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement>) => {
|
|
89
106
|
if (isDisabled) {
|
|
90
107
|
event.preventDefault();
|
|
91
108
|
return;
|
|
92
109
|
}
|
|
93
|
-
onClick?.(event);
|
|
110
|
+
onClick?.(event as React.MouseEvent<HTMLButtonElement>);
|
|
94
111
|
},
|
|
95
112
|
[isDisabled, onClick]
|
|
96
113
|
);
|
|
97
114
|
|
|
98
115
|
// Handle hover
|
|
99
116
|
const handleMouseEnter = useCallback(
|
|
100
|
-
(event: React.MouseEvent<HTMLButtonElement>) => {
|
|
117
|
+
(event: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement>) => {
|
|
101
118
|
if (!isDisabled) {
|
|
102
|
-
onHover?.(event);
|
|
119
|
+
onHover?.(event as React.MouseEvent<HTMLButtonElement>);
|
|
103
120
|
}
|
|
104
121
|
},
|
|
105
122
|
[isDisabled, onHover]
|
|
@@ -107,9 +124,9 @@ export const Button = React.memo(
|
|
|
107
124
|
|
|
108
125
|
// Handle focus
|
|
109
126
|
const handleFocusEvent = useCallback(
|
|
110
|
-
(event: React.FocusEvent<HTMLButtonElement>) => {
|
|
127
|
+
(event: React.FocusEvent<HTMLButtonElement | HTMLAnchorElement>) => {
|
|
111
128
|
if (!isDisabled) {
|
|
112
|
-
onFocus?.(event);
|
|
129
|
+
onFocus?.(event as React.FocusEvent<HTMLButtonElement>);
|
|
113
130
|
}
|
|
114
131
|
},
|
|
115
132
|
[isDisabled, onFocus]
|
|
@@ -117,9 +134,9 @@ export const Button = React.memo(
|
|
|
117
134
|
|
|
118
135
|
// Handle blur
|
|
119
136
|
const handleBlurEvent = useCallback(
|
|
120
|
-
(event: React.FocusEvent<HTMLButtonElement>) => {
|
|
137
|
+
(event: React.FocusEvent<HTMLButtonElement | HTMLAnchorElement>) => {
|
|
121
138
|
if (!isDisabled) {
|
|
122
|
-
onBlur?.(event);
|
|
139
|
+
onBlur?.(event as React.FocusEvent<HTMLButtonElement>);
|
|
123
140
|
}
|
|
124
141
|
},
|
|
125
142
|
[isDisabled, onBlur]
|
|
@@ -141,9 +158,9 @@ export const Button = React.memo(
|
|
|
141
158
|
|
|
142
159
|
// Button content with icon positioning
|
|
143
160
|
const buttonContent = useMemo(() => {
|
|
144
|
-
const
|
|
161
|
+
const iconSpan = iconElement && (
|
|
145
162
|
<span className={BUTTON.ICON_CLASS} aria-hidden="true">
|
|
146
|
-
{
|
|
163
|
+
{iconElement}
|
|
147
164
|
</span>
|
|
148
165
|
);
|
|
149
166
|
|
|
@@ -169,7 +186,7 @@ export const Button = React.memo(
|
|
|
169
186
|
<>
|
|
170
187
|
{labelElement}
|
|
171
188
|
{spinnerElement}
|
|
172
|
-
{
|
|
189
|
+
{iconSpan}
|
|
173
190
|
</>
|
|
174
191
|
);
|
|
175
192
|
}
|
|
@@ -177,23 +194,23 @@ export const Button = React.memo(
|
|
|
177
194
|
return (
|
|
178
195
|
<>
|
|
179
196
|
{spinnerElement}
|
|
180
|
-
{
|
|
197
|
+
{iconSpan}
|
|
181
198
|
{labelElement}
|
|
182
199
|
</>
|
|
183
200
|
);
|
|
184
|
-
}, [
|
|
201
|
+
}, [iconElement, iconPosition, iconOnly, buttonText, loading, spinnerSize, variant]);
|
|
185
202
|
|
|
186
203
|
// Button props
|
|
187
204
|
const buttonProps = useMemo(
|
|
188
205
|
() => ({
|
|
189
206
|
ref,
|
|
190
207
|
className: buttonClass,
|
|
191
|
-
type: Component === 'button' ? type : undefined,
|
|
208
|
+
type: Component === 'button' && !shouldRenderAsLink ? type : undefined,
|
|
192
209
|
onClick: handleClickEvent,
|
|
193
210
|
onMouseEnter: onHover ? handleMouseEnter : undefined,
|
|
194
211
|
onFocus: onFocus ? handleFocusEvent : undefined,
|
|
195
212
|
onBlur: onBlur ? handleBlurEvent : undefined,
|
|
196
|
-
disabled: isDisabled && Component === 'button',
|
|
213
|
+
disabled: isDisabled && Component === 'button' && !shouldRenderAsLink,
|
|
197
214
|
'aria-disabled': isDisabled,
|
|
198
215
|
'aria-busy': loading,
|
|
199
216
|
'aria-label': ariaLabel || (iconOnly ? label || children : undefined),
|
|
@@ -228,8 +245,36 @@ export const Button = React.memo(
|
|
|
228
245
|
]
|
|
229
246
|
);
|
|
230
247
|
|
|
248
|
+
// Render as anchor if href is provided
|
|
249
|
+
if (shouldRenderAsLink) {
|
|
250
|
+
const { ref: _, ...buttonPropsWithoutRef } = buttonProps;
|
|
251
|
+
const anchorButtonProps = {
|
|
252
|
+
...buttonPropsWithoutRef,
|
|
253
|
+
type: undefined,
|
|
254
|
+
disabled: undefined,
|
|
255
|
+
};
|
|
256
|
+
const anchorElement = (
|
|
257
|
+
<a {...anchorButtonProps} ref={ref as React.Ref<HTMLAnchorElement>} href={href} target={target} rel={target === '_blank' ? 'noopener noreferrer' : undefined}>
|
|
258
|
+
{buttonContent}
|
|
259
|
+
</a>
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
if (glass) {
|
|
263
|
+
const defaultGlassProps = {
|
|
264
|
+
displacementScale: 20,
|
|
265
|
+
blurAmount: 0,
|
|
266
|
+
saturation: 200,
|
|
267
|
+
elasticity: 0,
|
|
268
|
+
};
|
|
269
|
+
const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
|
|
270
|
+
return <AtomixGlass {...glassProps}>{anchorElement}</AtomixGlass>;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return anchorElement;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Default button rendering
|
|
231
277
|
if (glass) {
|
|
232
|
-
// Default glass settings for buttons
|
|
233
278
|
const defaultGlassProps = {
|
|
234
279
|
displacementScale: 20,
|
|
235
280
|
blurAmount: 0,
|
|
@@ -151,13 +151,9 @@ describe('Callout Component', () => {
|
|
|
151
151
|
const glassProps = JSON.parse(glassElement.getAttribute('data-glass-props') || '{}');
|
|
152
152
|
|
|
153
153
|
expect(glassProps).toMatchObject({
|
|
154
|
-
displacementScale:
|
|
155
|
-
blurAmount: 0,
|
|
156
|
-
saturation: 160,
|
|
157
|
-
aberrationIntensity: 1,
|
|
154
|
+
displacementScale: 30,
|
|
158
155
|
cornerRadius: 8,
|
|
159
|
-
|
|
160
|
-
mode: 'standard',
|
|
156
|
+
elasticity: 0,
|
|
161
157
|
});
|
|
162
158
|
});
|
|
163
159
|
|
|
@@ -184,10 +180,8 @@ describe('Callout Component', () => {
|
|
|
184
180
|
blurAmount: 2,
|
|
185
181
|
saturation: 180,
|
|
186
182
|
cornerRadius: 12,
|
|
187
|
-
// Default values
|
|
188
|
-
|
|
189
|
-
overLight: false,
|
|
190
|
-
mode: 'standard',
|
|
183
|
+
// Default values from Callout
|
|
184
|
+
elasticity: 0,
|
|
191
185
|
});
|
|
192
186
|
});
|
|
193
187
|
|
|
@@ -196,7 +190,7 @@ describe('Callout Component', () => {
|
|
|
196
190
|
const TestIcon = () => <div data-testid="test-icon">Icon</div>;
|
|
197
191
|
const actions = <button data-testid="action-button">Action</button>;
|
|
198
192
|
|
|
199
|
-
render(
|
|
193
|
+
const { container } = render(
|
|
200
194
|
<Callout
|
|
201
195
|
title="Glass Test"
|
|
202
196
|
variant="success"
|
|
@@ -223,9 +217,9 @@ describe('Callout Component', () => {
|
|
|
223
217
|
// Check that glass wrapper is present
|
|
224
218
|
expect(screen.getByTestId('atomix-glass')).toBeInTheDocument();
|
|
225
219
|
|
|
226
|
-
// Check that all classes are applied
|
|
227
|
-
const
|
|
228
|
-
expect(
|
|
220
|
+
// Check that all classes are applied to the outer wrapper
|
|
221
|
+
const outerCallout = container.querySelector('.c-callout');
|
|
222
|
+
expect(outerCallout).toHaveClass(
|
|
229
223
|
'c-callout',
|
|
230
224
|
'c-callout--success',
|
|
231
225
|
'c-callout--oneline',
|
|
@@ -12,6 +12,8 @@ export const Card = React.memo(
|
|
|
12
12
|
variant = '',
|
|
13
13
|
appearance = 'filled',
|
|
14
14
|
elevation = 'none',
|
|
15
|
+
hoverable = false,
|
|
16
|
+
hoverElevation = 'md',
|
|
15
17
|
// Layout
|
|
16
18
|
row = false,
|
|
17
19
|
flat = false,
|
|
@@ -77,6 +79,9 @@ export const Card = React.memo(
|
|
|
77
79
|
elevation === 'md' ? CARD.CLASSES.ELEVATION_MD : '',
|
|
78
80
|
elevation === 'lg' ? CARD.CLASSES.ELEVATION_LG : '',
|
|
79
81
|
elevation === 'xl' ? CARD.CLASSES.ELEVATION_XL : '',
|
|
82
|
+
// Hoverable modifier
|
|
83
|
+
hoverable ? 'c-card--hoverable' : '',
|
|
84
|
+
hoverable && hoverElevation ? `c-card--hover-elevation-${hoverElevation}` : '',
|
|
80
85
|
// Layout modifiers
|
|
81
86
|
row ? CARD.CLASSES.ROW : '',
|
|
82
87
|
flat ? CARD.CLASSES.FLAT : '',
|
|
@@ -91,7 +96,7 @@ export const Card = React.memo(
|
|
|
91
96
|
]
|
|
92
97
|
.filter(Boolean)
|
|
93
98
|
.join(' '),
|
|
94
|
-
[size, variant, appearance, elevation, row, flat, active, disabled, loading, selected, interactive, isClickable, glass, className]
|
|
99
|
+
[size, variant, appearance, elevation, hoverable, hoverElevation, row, flat, active, disabled, loading, selected, interactive, isClickable, glass, className]
|
|
95
100
|
);
|
|
96
101
|
|
|
97
102
|
// Determine ARIA role
|
|
@@ -267,6 +272,103 @@ export const Card = React.memo(
|
|
|
267
272
|
|
|
268
273
|
Card.displayName = 'Card';
|
|
269
274
|
|
|
275
|
+
// Card subcomponents for structured content
|
|
276
|
+
export interface CardHeaderProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
277
|
+
/**
|
|
278
|
+
* Header title
|
|
279
|
+
*/
|
|
280
|
+
title?: React.ReactNode;
|
|
281
|
+
/**
|
|
282
|
+
* Header subtitle
|
|
283
|
+
*/
|
|
284
|
+
subtitle?: React.ReactNode;
|
|
285
|
+
/**
|
|
286
|
+
* Action element (e.g., button) to display in header
|
|
287
|
+
*/
|
|
288
|
+
action?: React.ReactNode;
|
|
289
|
+
/**
|
|
290
|
+
* Icon to display in header
|
|
291
|
+
*/
|
|
292
|
+
icon?: React.ReactNode;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export const CardHeader = forwardRef<HTMLDivElement, CardHeaderProps>(
|
|
296
|
+
({ title, subtitle, action, icon, children, className = '', ...props }, ref) => {
|
|
297
|
+
const headerClasses = `${CARD.SELECTORS.HEADER.substring(1)} ${className}`.trim();
|
|
298
|
+
|
|
299
|
+
return (
|
|
300
|
+
<div ref={ref} className={headerClasses} {...props}>
|
|
301
|
+
{icon && <div className={CARD.SELECTORS.ICON.substring(1)}>{icon}</div>}
|
|
302
|
+
{(title || subtitle) && (
|
|
303
|
+
<div>
|
|
304
|
+
{title && <h3 className={CARD.SELECTORS.TITLE.substring(1)}>{title}</h3>}
|
|
305
|
+
{subtitle && <p className={CARD.SELECTORS.TEXT.substring(1)}>{subtitle}</p>}
|
|
306
|
+
</div>
|
|
307
|
+
)}
|
|
308
|
+
{action && <div className={CARD.SELECTORS.ACTIONS.substring(1)}>{action}</div>}
|
|
309
|
+
{children}
|
|
310
|
+
</div>
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
CardHeader.displayName = 'CardHeader';
|
|
316
|
+
|
|
317
|
+
export interface CardBodyProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
318
|
+
/**
|
|
319
|
+
* Make body scrollable
|
|
320
|
+
*/
|
|
321
|
+
scrollable?: boolean;
|
|
322
|
+
/**
|
|
323
|
+
* Maximum height for scrollable body
|
|
324
|
+
*/
|
|
325
|
+
maxHeight?: string | number;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export const CardBody = forwardRef<HTMLDivElement, CardBodyProps>(
|
|
329
|
+
({ scrollable = false, maxHeight, children, className = '', style, ...props }, ref) => {
|
|
330
|
+
const bodyClasses = `${CARD.SELECTORS.BODY.substring(1)} ${scrollable ? 'c-card__body--scrollable' : ''} ${className}`.trim();
|
|
331
|
+
const bodyStyle: React.CSSProperties = {
|
|
332
|
+
...style,
|
|
333
|
+
...(scrollable && maxHeight ? { maxHeight: typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight, overflowY: 'auto' } : {}),
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
return (
|
|
337
|
+
<div ref={ref} className={bodyClasses} style={bodyStyle} {...props}>
|
|
338
|
+
{children}
|
|
339
|
+
</div>
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
CardBody.displayName = 'CardBody';
|
|
345
|
+
|
|
346
|
+
export interface CardFooterProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
347
|
+
/**
|
|
348
|
+
* Footer alignment
|
|
349
|
+
*/
|
|
350
|
+
align?: 'start' | 'center' | 'end' | 'between';
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export const CardFooter = forwardRef<HTMLDivElement, CardFooterProps>(
|
|
354
|
+
({ align, children, className = '', style, ...props }, ref) => {
|
|
355
|
+
const footerClasses = `${CARD.SELECTORS.FOOTER.substring(1)} ${align ? `c-card__footer--align-${align}` : ''} ${className}`.trim();
|
|
356
|
+
|
|
357
|
+
return (
|
|
358
|
+
<div ref={ref} className={footerClasses} style={style} {...props}>
|
|
359
|
+
{children}
|
|
360
|
+
</div>
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
CardFooter.displayName = 'CardFooter';
|
|
366
|
+
|
|
367
|
+
// Attach subcomponents to Card
|
|
368
|
+
(Card as any).Header = CardHeader;
|
|
369
|
+
(Card as any).Body = CardBody;
|
|
370
|
+
(Card as any).Footer = CardFooter;
|
|
371
|
+
|
|
270
372
|
export type { CardProps };
|
|
271
373
|
|
|
272
374
|
export default Card;
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* Types and hooks are defined in the lib directory.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
// Export the main Card component
|
|
9
|
-
export { default as Card } from './Card';
|
|
8
|
+
// Export the main Card component with subcomponents
|
|
9
|
+
export { default as Card, CardHeader, CardBody, CardFooter } from './Card';
|
|
10
|
+
export type { CardHeaderProps, CardBodyProps, CardFooterProps } from './Card';
|
|
10
11
|
|
|
11
12
|
// Export the ElevationCard variant
|
|
12
13
|
export { default as ElevationCard } from './ElevationCard';
|
|
@@ -143,7 +143,6 @@ export const WithNewsletter: Story = {
|
|
|
143
143
|
newsletterDescription:
|
|
144
144
|
'Get the latest updates, articles, and resources delivered to your inbox.',
|
|
145
145
|
onNewsletterSubmit: (email: string) => {
|
|
146
|
-
console.log('Newsletter signup:', email);
|
|
147
146
|
alert(`Thank you for subscribing with ${email}!`);
|
|
148
147
|
},
|
|
149
148
|
},
|
|
@@ -156,7 +155,7 @@ export const WithBackToTop: Story = {
|
|
|
156
155
|
showBackToTop: true,
|
|
157
156
|
backToTopText: 'Back to Top',
|
|
158
157
|
onBackToTop: () => {
|
|
159
|
-
|
|
158
|
+
// Back to top functionality
|
|
160
159
|
},
|
|
161
160
|
},
|
|
162
161
|
render: Default.render,
|
|
@@ -205,11 +205,6 @@ export const Footer = forwardRef<HTMLElement, FooterProps>(
|
|
|
205
205
|
{React.Children.map(children, child => {
|
|
206
206
|
// Check if the child is a valid React element
|
|
207
207
|
if (React.isValidElement(child)) {
|
|
208
|
-
console.log(
|
|
209
|
-
'Footer - passing showNewsletter:',
|
|
210
|
-
showNewsletter,
|
|
211
|
-
typeof showNewsletter
|
|
212
|
-
);
|
|
213
208
|
// Clone the element and pass the showNewsletter prop
|
|
214
209
|
return React.cloneElement(child, { showNewsletter } as any);
|
|
215
210
|
}
|
|
@@ -48,12 +48,13 @@ export const FooterLink = forwardRef<HTMLAnchorElement, FooterLinkProps>(
|
|
|
48
48
|
};
|
|
49
49
|
|
|
50
50
|
if (LinkComponent) {
|
|
51
|
+
const Component = LinkComponent as React.ComponentType<any>;
|
|
51
52
|
return (
|
|
52
|
-
<
|
|
53
|
+
<Component ref={ref} to={href} {...linkProps}>
|
|
53
54
|
{icon && <span className="c-footer__link-icon">{icon}</span>}
|
|
54
55
|
<span className="c-footer__link-text">{children}</span>
|
|
55
56
|
{external && <span className="c-footer__link-external">↗</span>}
|
|
56
|
-
</
|
|
57
|
+
</Component>
|
|
57
58
|
);
|
|
58
59
|
}
|
|
59
60
|
|
|
@@ -33,13 +33,6 @@ export const FooterSection = forwardRef<
|
|
|
33
33
|
) => {
|
|
34
34
|
const [isCollapsed, setIsCollapsed] = React.useState(defaultCollapsed);
|
|
35
35
|
|
|
36
|
-
console.log(
|
|
37
|
-
'FooterSection render - showNewsletter:',
|
|
38
|
-
showNewsletter,
|
|
39
|
-
'Type:',
|
|
40
|
-
typeof showNewsletter
|
|
41
|
-
);
|
|
42
|
-
|
|
43
36
|
const handleToggle = () => {
|
|
44
37
|
if (collapsible) {
|
|
45
38
|
setIsCollapsed(!isCollapsed);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { Icon, type IconProps, type IconSize, type IconWeight } from './Icon';
|
|
1
|
+
export { Icon, type IconProps, type IconSize, type IconWeight, type PhosphorIconsType } from './Icon';
|
|
2
2
|
export { default } from './Icon';
|
|
@@ -317,6 +317,7 @@ export const GlassModal: Story = {
|
|
|
317
317
|
appearance. The glass effect creates a modern, elegant look that works well over
|
|
318
318
|
colorful backgrounds.
|
|
319
319
|
</p>
|
|
320
|
+
<img src="https://picsum.photos/800/410" alt="desert" style={{ maxWidth: '100%' }} />
|
|
320
321
|
<p>
|
|
321
322
|
The glass effect includes displacement, blur, and chromatic aberration for a premium
|
|
322
323
|
feel.
|
|
@@ -329,9 +330,14 @@ export const GlassModal: Story = {
|
|
|
329
330
|
Story => (
|
|
330
331
|
<div
|
|
331
332
|
style={{
|
|
332
|
-
background: '
|
|
333
|
-
|
|
334
|
-
|
|
333
|
+
background: 'url(https://picsum.photos/1920/1080)',
|
|
334
|
+
height: '100vh',
|
|
335
|
+
width: '100vw',
|
|
336
|
+
backgroundSize: 'cover',
|
|
337
|
+
backgroundPosition: 'center',
|
|
338
|
+
display: 'flex',
|
|
339
|
+
alignItems: 'center',
|
|
340
|
+
justifyContent: 'center',
|
|
335
341
|
}}
|
|
336
342
|
>
|
|
337
343
|
<Story />
|
|
@@ -375,23 +381,10 @@ export const GlassModalCustom: Story = {
|
|
|
375
381
|
}
|
|
376
382
|
footer={
|
|
377
383
|
<>
|
|
378
|
-
<div
|
|
379
|
-
className="c-btn c-btn--outline-secondary"
|
|
380
|
-
onClick={() => setIsOpen(false)}
|
|
381
|
-
style={{
|
|
382
|
-
cursor: 'pointer',
|
|
383
|
-
padding: '8px 16px',
|
|
384
|
-
display: 'inline-block',
|
|
385
|
-
marginRight: '8px',
|
|
386
|
-
}}
|
|
387
|
-
>
|
|
384
|
+
<div className="c-btn c-btn--outline-secondary" onClick={() => setIsOpen(false)}>
|
|
388
385
|
Cancel
|
|
389
386
|
</div>
|
|
390
|
-
<div
|
|
391
|
-
className="c-btn c-btn--primary"
|
|
392
|
-
onClick={() => setIsOpen(false)}
|
|
393
|
-
style={{ cursor: 'pointer', padding: '8px 16px', display: 'inline-block' }}
|
|
394
|
-
>
|
|
387
|
+
<div className="c-btn c-btn--primary" onClick={() => setIsOpen(false)}>
|
|
395
388
|
Confirm
|
|
396
389
|
</div>
|
|
397
390
|
</>
|
|
@@ -402,6 +395,7 @@ export const GlassModalCustom: Story = {
|
|
|
402
395
|
aberration. The polar mode creates a different visual effect compared to the standard
|
|
403
396
|
shader mode.
|
|
404
397
|
</p>
|
|
398
|
+
<img src="https://picsum.photos/800/410" alt="desert" style={{ maxWidth: '100%' }} />
|
|
405
399
|
</Modal>
|
|
406
400
|
</>
|
|
407
401
|
);
|
|
@@ -410,12 +404,14 @@ export const GlassModalCustom: Story = {
|
|
|
410
404
|
Story => (
|
|
411
405
|
<div
|
|
412
406
|
style={{
|
|
413
|
-
background:
|
|
414
|
-
'url(https://images.unsplash.com/photo-1744872665943-fd335d371059?q=80&w=3024&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D)',
|
|
407
|
+
background: 'url(https://picsum.photos/1920/1080)',
|
|
415
408
|
height: '100vh',
|
|
416
409
|
width: '100vw',
|
|
417
410
|
backgroundSize: 'cover',
|
|
418
411
|
backgroundPosition: 'center',
|
|
412
|
+
display: 'flex',
|
|
413
|
+
alignItems: 'center',
|
|
414
|
+
justifyContent: 'center',
|
|
419
415
|
}}
|
|
420
416
|
>
|
|
421
417
|
<Story />
|
|
@@ -489,23 +485,10 @@ export const GlassModalSizes: Story = {
|
|
|
489
485
|
glass={true}
|
|
490
486
|
footer={
|
|
491
487
|
<>
|
|
492
|
-
<div
|
|
493
|
-
className="c-btn c-btn--outline-secondary"
|
|
494
|
-
onClick={() => setIsOpen(false)}
|
|
495
|
-
style={{
|
|
496
|
-
cursor: 'pointer',
|
|
497
|
-
padding: '8px 16px',
|
|
498
|
-
display: 'inline-block',
|
|
499
|
-
marginRight: '8px',
|
|
500
|
-
}}
|
|
501
|
-
>
|
|
488
|
+
<div className="c-btn c-btn--outline-secondary" onClick={() => setIsOpen(false)}>
|
|
502
489
|
Cancel
|
|
503
490
|
</div>
|
|
504
|
-
<div
|
|
505
|
-
className="c-btn c-btn--primary"
|
|
506
|
-
onClick={() => setIsOpen(false)}
|
|
507
|
-
style={{ cursor: 'pointer', padding: '8px 16px', display: 'inline-block' }}
|
|
508
|
-
>
|
|
491
|
+
<div className="c-btn c-btn--primary" onClick={() => setIsOpen(false)}>
|
|
509
492
|
Confirm
|
|
510
493
|
</div>
|
|
511
494
|
</>
|
|
@@ -515,6 +498,9 @@ export const GlassModalSizes: Story = {
|
|
|
515
498
|
<p>
|
|
516
499
|
The glass effect adapts to different modal sizes while maintaining its visual appeal.
|
|
517
500
|
</p>
|
|
501
|
+
<p>
|
|
502
|
+
The glass effect enhances the modal's appearance, making it visually appealing and easier to read.
|
|
503
|
+
</p>
|
|
518
504
|
</Modal>
|
|
519
505
|
</div>
|
|
520
506
|
);
|
|
@@ -523,9 +509,14 @@ export const GlassModalSizes: Story = {
|
|
|
523
509
|
Story => (
|
|
524
510
|
<div
|
|
525
511
|
style={{
|
|
526
|
-
background: '
|
|
527
|
-
|
|
528
|
-
|
|
512
|
+
background: 'url(https://picsum.photos/1920/1080)',
|
|
513
|
+
height: '100vh',
|
|
514
|
+
width: '100vw',
|
|
515
|
+
backgroundSize: 'cover',
|
|
516
|
+
backgroundPosition: 'center',
|
|
517
|
+
display: 'flex',
|
|
518
|
+
alignItems: 'center',
|
|
519
|
+
justifyContent: 'center',
|
|
529
520
|
}}
|
|
530
521
|
>
|
|
531
522
|
<Story />
|
|
@@ -197,11 +197,11 @@ export const Modal: React.FC<ModalProps> = ({
|
|
|
197
197
|
? // Default glass settings for modals
|
|
198
198
|
(() => {
|
|
199
199
|
const defaultGlassProps = {
|
|
200
|
-
displacementScale:
|
|
201
|
-
blurAmount: 2,
|
|
202
|
-
|
|
203
|
-
cornerRadius: 12,
|
|
200
|
+
displacementScale: document.querySelector('.c-modal---glass .c-modal__content')?.clientHeight,
|
|
201
|
+
blurAmount: 2.2,
|
|
202
|
+
elasticity: 0,
|
|
204
203
|
mode: 'shader' as const,
|
|
204
|
+
shaderMode: 'premiumGlass'
|
|
205
205
|
};
|
|
206
206
|
|
|
207
207
|
const glassProps =
|
|
@@ -148,9 +148,14 @@ export const NavItem = forwardRef<HTMLLIElement, NavItemProps>(
|
|
|
148
148
|
return (
|
|
149
149
|
<li ref={ref} className={navItemClass} role="menuitem" aria-haspopup={dropdown || megaMenu}>
|
|
150
150
|
{LinkComponent ? (
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
151
|
+
(() => {
|
|
152
|
+
const Component = LinkComponent as React.ComponentType<any>;
|
|
153
|
+
return (
|
|
154
|
+
<Component {...linkProps}>
|
|
155
|
+
{dropdown || megaMenu ? childContent[0] : children}
|
|
156
|
+
</Component>
|
|
157
|
+
);
|
|
158
|
+
})()
|
|
154
159
|
) : (
|
|
155
160
|
<a {...linkProps}>{dropdown || megaMenu ? childContent[0] : children}</a>
|
|
156
161
|
)}
|