@shohojdhara/atomix 0.5.5 → 0.5.7
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/README.md +43 -21
- package/dist/atomix.css +1016 -1681
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +5 -5
- package/dist/atomix.min.css.map +1 -1
- package/dist/core.d.ts +102 -9
- package/dist/core.js +89 -79
- package/dist/core.js.map +1 -1
- package/dist/forms.js +1 -7
- package/dist/forms.js.map +1 -1
- package/dist/heavy.js +7 -3
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +181 -55
- package/dist/index.esm.js +112 -99
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +112 -99
- 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/Accordion/Accordion.tsx +40 -25
- package/src/components/Breadcrumb/Breadcrumb.tsx +23 -14
- package/src/components/Button/Button.tsx +4 -5
- package/src/components/Callout/Callout.tsx +98 -96
- package/src/components/Card/Card.tsx +117 -103
- package/src/components/Card/index.ts +7 -5
- package/src/components/Dropdown/Dropdown.tsx +27 -8
- package/src/components/EdgePanel/EdgePanel.tsx +7 -2
- package/src/components/Modal/Modal.tsx +27 -8
- package/src/components/Spinner/Spinner.tsx +60 -43
- package/src/components/Tabs/Tabs.tsx +163 -149
- package/src/lib/composables/useInput.ts +11 -9
- package/src/lib/types/components.ts +84 -0
- package/src/styles/01-settings/_settings.background.scss +2 -1
- package/src/styles/02-tools/_tools.background.scss +100 -294
- package/src/styles/06-components/_components.avatar-group.scss +1 -3
- package/src/styles/06-components/_components.avatar.scss +1 -1
- package/src/styles/06-components/_components.badge.scss +2 -2
- package/src/styles/06-components/_components.button.scss +3 -3
- package/src/styles/06-components/_components.callout.scss +5 -5
- package/src/styles/06-components/_components.card.scss +4 -7
- package/src/styles/06-components/_components.checkbox.scss +1 -1
- package/src/styles/06-components/_components.data-table.scss +1 -1
- package/src/styles/06-components/_components.datepicker.scss +1 -1
- package/src/styles/06-components/_components.dropdown.scss +3 -3
- package/src/styles/06-components/_components.edge-panel.scss +5 -9
- package/src/styles/06-components/_components.footer.scss +12 -13
- package/src/styles/06-components/_components.hero.scss +2 -2
- package/src/styles/06-components/_components.input.scss +3 -3
- package/src/styles/06-components/_components.list.scss +1 -1
- package/src/styles/06-components/_components.menu.scss +2 -2
- package/src/styles/06-components/_components.messages.scss +16 -18
- package/src/styles/06-components/_components.modal.scss +6 -6
- package/src/styles/06-components/_components.nav.scss +0 -3
- package/src/styles/06-components/_components.navbar.scss +3 -3
- package/src/styles/06-components/_components.pagination.scss +3 -3
- package/src/styles/06-components/_components.photoviewer.scss +3 -3
- package/src/styles/06-components/_components.popover.scss +3 -3
- package/src/styles/06-components/_components.product-review.scss +2 -2
- package/src/styles/06-components/_components.progress.scss +2 -2
- package/src/styles/06-components/_components.river.scss +1 -1
- package/src/styles/06-components/_components.sectionintro.scss +1 -1
- package/src/styles/06-components/_components.select.scss +5 -6
- package/src/styles/06-components/_components.side-menu.scss +6 -6
- package/src/styles/06-components/_components.skeleton.scss +8 -8
- package/src/styles/06-components/_components.slider.scss +6 -6
- package/src/styles/06-components/_components.steps.scss +2 -2
- package/src/styles/06-components/_components.tabs.scss +2 -2
- package/src/styles/06-components/_components.todo.scss +1 -1
- package/src/styles/06-components/_components.toggle.scss +3 -5
- package/src/styles/06-components/_components.tooltip.scss +2 -4
- package/src/styles/06-components/_components.upload.scss +1 -2
- package/src/styles/06-components/_components.video-player.scss +2 -2
- package/src/styles/99-utilities/_utilities.link.scss +4 -5
|
@@ -3,7 +3,108 @@ import { CARD } from '../../lib/constants/components';
|
|
|
3
3
|
import { CardProps } from '../../lib/types/components';
|
|
4
4
|
import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
// Card subcomponents for structured content
|
|
7
|
+
|
|
8
|
+
export interface CardHeaderProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
9
|
+
/**
|
|
10
|
+
* Header title
|
|
11
|
+
*/
|
|
12
|
+
title?: React.ReactNode;
|
|
13
|
+
/**
|
|
14
|
+
* Header subtitle
|
|
15
|
+
*/
|
|
16
|
+
subtitle?: React.ReactNode;
|
|
17
|
+
/**
|
|
18
|
+
* Action element (e.g., button) to display in header
|
|
19
|
+
*/
|
|
20
|
+
action?: React.ReactNode;
|
|
21
|
+
/**
|
|
22
|
+
* Icon to display in header
|
|
23
|
+
*/
|
|
24
|
+
icon?: React.ReactNode;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const CardHeader = forwardRef<HTMLDivElement, CardHeaderProps>(
|
|
28
|
+
({ title, subtitle, action, icon, children, className = '', ...props }, ref) => {
|
|
29
|
+
const headerClasses = `${CARD.SELECTORS.HEADER.substring(1)} ${className}`.trim();
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div ref={ref} className={headerClasses} {...props}>
|
|
33
|
+
{icon && <div className={CARD.SELECTORS.ICON.substring(1)}>{icon}</div>}
|
|
34
|
+
{(title || subtitle) && (
|
|
35
|
+
<div>
|
|
36
|
+
{title && <h3 className={CARD.SELECTORS.TITLE.substring(1)}>{title}</h3>}
|
|
37
|
+
{subtitle && <p className={CARD.SELECTORS.TEXT.substring(1)}>{subtitle}</p>}
|
|
38
|
+
</div>
|
|
39
|
+
)}
|
|
40
|
+
{action && <div className={CARD.SELECTORS.ACTIONS.substring(1)}>{action}</div>}
|
|
41
|
+
{children}
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
CardHeader.displayName = 'CardHeader';
|
|
48
|
+
|
|
49
|
+
export interface CardBodyProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
50
|
+
/**
|
|
51
|
+
* Make body scrollable
|
|
52
|
+
*/
|
|
53
|
+
scrollable?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Maximum height for scrollable body
|
|
56
|
+
*/
|
|
57
|
+
maxHeight?: string | number;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const CardBody = forwardRef<HTMLDivElement, CardBodyProps>(
|
|
61
|
+
({ scrollable = false, maxHeight, children, className = '', style, ...props }, ref) => {
|
|
62
|
+
const bodyClasses =
|
|
63
|
+
`${CARD.SELECTORS.BODY.substring(1)} ${scrollable ? 'c-card__body--scrollable' : ''} ${className}`.trim();
|
|
64
|
+
const bodyStyle: React.CSSProperties = {
|
|
65
|
+
...style,
|
|
66
|
+
...(scrollable && maxHeight
|
|
67
|
+
? {
|
|
68
|
+
maxHeight: typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight,
|
|
69
|
+
overflowY: 'auto',
|
|
70
|
+
}
|
|
71
|
+
: {}),
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<div ref={ref} className={bodyClasses} style={bodyStyle} {...props}>
|
|
76
|
+
{children}
|
|
77
|
+
</div>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
CardBody.displayName = 'CardBody';
|
|
83
|
+
|
|
84
|
+
export interface CardFooterProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
85
|
+
/**
|
|
86
|
+
* Footer alignment
|
|
87
|
+
*/
|
|
88
|
+
align?: 'start' | 'center' | 'end' | 'between';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export const CardFooter = forwardRef<HTMLDivElement, CardFooterProps>(
|
|
92
|
+
({ align, children, className = '', style, ...props }, ref) => {
|
|
93
|
+
const footerClasses =
|
|
94
|
+
`${CARD.SELECTORS.FOOTER.substring(1)} ${align ? `c-card__footer--align-${align}` : ''} ${className}`.trim();
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<div ref={ref} className={footerClasses} style={style} {...props}>
|
|
98
|
+
{children}
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
CardFooter.displayName = 'CardFooter';
|
|
105
|
+
|
|
106
|
+
// Main Card component implementation
|
|
107
|
+
const CardImpl = React.memo(
|
|
7
108
|
forwardRef<HTMLDivElement | HTMLAnchorElement, CardProps>(
|
|
8
109
|
(
|
|
9
110
|
{
|
|
@@ -299,111 +400,24 @@ export const Card = React.memo(
|
|
|
299
400
|
)
|
|
300
401
|
);
|
|
301
402
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
// Card subcomponents for structured content
|
|
305
|
-
export interface CardHeaderProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
306
|
-
/**
|
|
307
|
-
* Header title
|
|
308
|
-
*/
|
|
309
|
-
title?: React.ReactNode;
|
|
310
|
-
/**
|
|
311
|
-
* Header subtitle
|
|
312
|
-
*/
|
|
313
|
-
subtitle?: React.ReactNode;
|
|
314
|
-
/**
|
|
315
|
-
* Action element (e.g., button) to display in header
|
|
316
|
-
*/
|
|
317
|
-
action?: React.ReactNode;
|
|
318
|
-
/**
|
|
319
|
-
* Icon to display in header
|
|
320
|
-
*/
|
|
321
|
-
icon?: React.ReactNode;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
export const CardHeader = forwardRef<HTMLDivElement, CardHeaderProps>(
|
|
325
|
-
({ title, subtitle, action, icon, children, className = '', ...props }, ref) => {
|
|
326
|
-
const headerClasses = `${CARD.SELECTORS.HEADER.substring(1)} ${className}`.trim();
|
|
327
|
-
|
|
328
|
-
return (
|
|
329
|
-
<div ref={ref} className={headerClasses} {...props}>
|
|
330
|
-
{icon && <div className={CARD.SELECTORS.ICON.substring(1)}>{icon}</div>}
|
|
331
|
-
{(title || subtitle) && (
|
|
332
|
-
<div>
|
|
333
|
-
{title && <h3 className={CARD.SELECTORS.TITLE.substring(1)}>{title}</h3>}
|
|
334
|
-
{subtitle && <p className={CARD.SELECTORS.TEXT.substring(1)}>{subtitle}</p>}
|
|
335
|
-
</div>
|
|
336
|
-
)}
|
|
337
|
-
{action && <div className={CARD.SELECTORS.ACTIONS.substring(1)}>{action}</div>}
|
|
338
|
-
{children}
|
|
339
|
-
</div>
|
|
340
|
-
);
|
|
341
|
-
}
|
|
342
|
-
);
|
|
343
|
-
|
|
344
|
-
CardHeader.displayName = 'CardHeader';
|
|
345
|
-
|
|
346
|
-
export interface CardBodyProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
347
|
-
/**
|
|
348
|
-
* Make body scrollable
|
|
349
|
-
*/
|
|
350
|
-
scrollable?: boolean;
|
|
351
|
-
/**
|
|
352
|
-
* Maximum height for scrollable body
|
|
353
|
-
*/
|
|
354
|
-
maxHeight?: string | number;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
export const CardBody = forwardRef<HTMLDivElement, CardBodyProps>(
|
|
358
|
-
({ scrollable = false, maxHeight, children, className = '', style, ...props }, ref) => {
|
|
359
|
-
const bodyClasses =
|
|
360
|
-
`${CARD.SELECTORS.BODY.substring(1)} ${scrollable ? 'c-card__body--scrollable' : ''} ${className}`.trim();
|
|
361
|
-
const bodyStyle: React.CSSProperties = {
|
|
362
|
-
...style,
|
|
363
|
-
...(scrollable && maxHeight
|
|
364
|
-
? {
|
|
365
|
-
maxHeight: typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight,
|
|
366
|
-
overflowY: 'auto',
|
|
367
|
-
}
|
|
368
|
-
: {}),
|
|
369
|
-
};
|
|
370
|
-
|
|
371
|
-
return (
|
|
372
|
-
<div ref={ref} className={bodyClasses} style={bodyStyle} {...props}>
|
|
373
|
-
{children}
|
|
374
|
-
</div>
|
|
375
|
-
);
|
|
376
|
-
}
|
|
377
|
-
);
|
|
378
|
-
|
|
379
|
-
CardBody.displayName = 'CardBody';
|
|
380
|
-
|
|
381
|
-
export interface CardFooterProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
382
|
-
/**
|
|
383
|
-
* Footer alignment
|
|
384
|
-
*/
|
|
385
|
-
align?: 'start' | 'center' | 'end' | 'between';
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
export const CardFooter = forwardRef<HTMLDivElement, CardFooterProps>(
|
|
389
|
-
({ align, children, className = '', style, ...props }, ref) => {
|
|
390
|
-
const footerClasses =
|
|
391
|
-
`${CARD.SELECTORS.FOOTER.substring(1)} ${align ? `c-card__footer--align-${align}` : ''} ${className}`.trim();
|
|
403
|
+
CardImpl.displayName = 'Card';
|
|
392
404
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
405
|
+
/**
|
|
406
|
+
* Card component with subcomponents for creating card layouts
|
|
407
|
+
*/
|
|
408
|
+
type CardComponent = React.FC<CardProps> & {
|
|
409
|
+
Header: typeof CardHeader;
|
|
410
|
+
Body: typeof CardBody;
|
|
411
|
+
Footer: typeof CardFooter;
|
|
412
|
+
};
|
|
400
413
|
|
|
401
|
-
|
|
414
|
+
// Create compound component with proper typing
|
|
415
|
+
const CardWithSubcomponents = CardImpl as unknown as CardComponent;
|
|
416
|
+
CardWithSubcomponents.Header = CardHeader;
|
|
417
|
+
CardWithSubcomponents.Body = CardBody;
|
|
418
|
+
CardWithSubcomponents.Footer = CardFooter;
|
|
402
419
|
|
|
403
|
-
|
|
404
|
-
(Card as any).Header = CardHeader;
|
|
405
|
-
(Card as any).Body = CardBody;
|
|
406
|
-
(Card as any).Footer = CardFooter;
|
|
420
|
+
export const Card = CardWithSubcomponents;
|
|
407
421
|
|
|
408
422
|
export type { CardProps };
|
|
409
423
|
|
|
@@ -6,11 +6,13 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
// Export the main Card component with subcomponents
|
|
9
|
-
export {
|
|
10
|
-
export type { CardHeaderProps, CardBodyProps, CardFooterProps } from './Card';
|
|
9
|
+
export { Card, CardHeader, CardBody, CardFooter } from './Card';
|
|
10
|
+
export type { CardProps, CardHeaderProps, CardBodyProps, CardFooterProps } from './Card';
|
|
11
|
+
|
|
12
|
+
// Export the ElevationCard component
|
|
13
|
+
export { default as ElevationCard, type ElevationCardProps } from './ElevationCard';
|
|
14
|
+
|
|
11
15
|
|
|
12
|
-
// Export the ElevationCard variant
|
|
13
|
-
export { default as ElevationCard } from './ElevationCard';
|
|
14
16
|
|
|
15
17
|
// Default export for the main Card component
|
|
16
|
-
export { default } from './Card';
|
|
18
|
+
export { default } from './Card';
|
|
@@ -8,6 +8,7 @@ import React, {
|
|
|
8
8
|
memo,
|
|
9
9
|
forwardRef,
|
|
10
10
|
ReactNode,
|
|
11
|
+
ComponentType,
|
|
11
12
|
} from 'react';
|
|
12
13
|
import { DROPDOWN } from '../../lib/constants/components';
|
|
13
14
|
import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
|
|
@@ -27,6 +28,18 @@ export type DropdownContextType = {
|
|
|
27
28
|
trigger: string;
|
|
28
29
|
};
|
|
29
30
|
|
|
31
|
+
// Type definitions for compound component handling
|
|
32
|
+
type ExtendedComponentType<P = {}> = ComponentType<P> & {
|
|
33
|
+
displayName?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface DropdownTriggerProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
37
|
+
onClick?: (e: React.MouseEvent) => void;
|
|
38
|
+
onKeyDown?: (e: React.KeyboardEvent) => void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface DropdownMenuProps extends React.HTMLAttributes<HTMLUListElement> {}
|
|
42
|
+
|
|
30
43
|
// Create context for dropdown state management
|
|
31
44
|
const DropdownContext = createContext<DropdownContextType>({
|
|
32
45
|
isOpen: false,
|
|
@@ -140,7 +153,9 @@ export const DropdownItem: React.FC<DropdownItemProps> = memo(
|
|
|
140
153
|
<li>
|
|
141
154
|
{linkComponent ? (
|
|
142
155
|
(() => {
|
|
143
|
-
const Component = linkComponent as React.ComponentType<
|
|
156
|
+
const Component = linkComponent as React.ComponentType<
|
|
157
|
+
React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
158
|
+
>;
|
|
144
159
|
return (
|
|
145
160
|
<Component {...linkProps}>
|
|
146
161
|
{icon && <span className="c-dropdown__menu-item-icon">{icon}</span>}
|
|
@@ -402,7 +417,9 @@ export const Dropdown: DropdownComponent = memo(function DropdownBase({
|
|
|
402
417
|
const hasCompoundComponents = React.Children.toArray(children).some(
|
|
403
418
|
child =>
|
|
404
419
|
React.isValidElement(child) &&
|
|
405
|
-
['DropdownTrigger', 'DropdownMenu'].includes(
|
|
420
|
+
['DropdownTrigger', 'DropdownMenu'].includes(
|
|
421
|
+
(child.type as React.ComponentType & { displayName?: string }).displayName || ''
|
|
422
|
+
)
|
|
406
423
|
);
|
|
407
424
|
|
|
408
425
|
let triggerContent: ReactNode;
|
|
@@ -412,23 +429,25 @@ export const Dropdown: DropdownComponent = memo(function DropdownBase({
|
|
|
412
429
|
// Find Trigger and Menu in children
|
|
413
430
|
React.Children.forEach(children, child => {
|
|
414
431
|
if (React.isValidElement(child)) {
|
|
415
|
-
|
|
416
|
-
|
|
432
|
+
const component = child.type as ExtendedComponentType;
|
|
433
|
+
if (component.displayName === 'DropdownTrigger') {
|
|
434
|
+
const triggerProps: DropdownTriggerProps = {
|
|
417
435
|
ref: toggleRef,
|
|
418
436
|
onClick: (e: React.MouseEvent) => {
|
|
419
437
|
handleToggleClick(e);
|
|
420
|
-
(child.props as
|
|
438
|
+
(child.props as DropdownTriggerProps).onClick?.(e);
|
|
421
439
|
},
|
|
422
440
|
onKeyDown: (e: React.KeyboardEvent) => {
|
|
423
441
|
handleToggleKeyDown(e);
|
|
424
|
-
(child.props as
|
|
442
|
+
(child.props as DropdownTriggerProps).onKeyDown?.(e);
|
|
425
443
|
},
|
|
426
444
|
'aria-haspopup': 'menu',
|
|
427
445
|
'aria-expanded': isOpen,
|
|
428
446
|
'aria-controls': dropdownId,
|
|
429
447
|
tabIndex: 0,
|
|
430
|
-
}
|
|
431
|
-
|
|
448
|
+
};
|
|
449
|
+
triggerContent = React.cloneElement(child, triggerProps);
|
|
450
|
+
} else if (component.displayName === 'DropdownMenu') {
|
|
432
451
|
menuContentNode = child;
|
|
433
452
|
}
|
|
434
453
|
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import React, { useRef, useEffect, memo, forwardRef } from 'react';
|
|
1
|
+
import React, { useRef, useEffect, memo, forwardRef, ComponentType } from 'react';
|
|
2
2
|
import { EdgePanelProps } from '../../lib/types/components';
|
|
3
3
|
import { useEdgePanel } from '../../lib/composables/useEdgePanel';
|
|
4
4
|
import { EDGE_PANEL } from '../../lib/constants/components';
|
|
5
5
|
import { Icon } from '../Icon/Icon';
|
|
6
6
|
import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
|
|
7
7
|
|
|
8
|
+
// Type-safe type alias for compound component detection
|
|
9
|
+
type ExtendedComponentType<P = {}> = ComponentType<P> & {
|
|
10
|
+
displayName?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
8
13
|
// Subcomponents
|
|
9
14
|
export const EdgePanelHeader = forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
10
15
|
({ children, className = '', ...props }, ref) => (
|
|
@@ -146,7 +151,7 @@ const EdgePanelComponentBase = ({
|
|
|
146
151
|
child =>
|
|
147
152
|
React.isValidElement(child) &&
|
|
148
153
|
['EdgePanelHeader', 'EdgePanelBody', 'EdgePanelFooter'].includes(
|
|
149
|
-
(child.type as
|
|
154
|
+
(child.type as ExtendedComponentType).displayName || ''
|
|
150
155
|
)
|
|
151
156
|
);
|
|
152
157
|
|
|
@@ -1,6 +1,21 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {
|
|
2
|
+
useEffect,
|
|
3
|
+
useRef,
|
|
4
|
+
useState,
|
|
5
|
+
useCallback,
|
|
6
|
+
memo,
|
|
7
|
+
forwardRef,
|
|
8
|
+
ReactNode,
|
|
9
|
+
useId,
|
|
10
|
+
ComponentType,
|
|
11
|
+
} from 'react';
|
|
2
12
|
import { ModalProps } from '../../lib/types/components';
|
|
3
13
|
import { MODAL } from '../../lib/constants/components';
|
|
14
|
+
|
|
15
|
+
// Define ExtendedComponentType as a type alias instead of an interface to avoid TS2312 error
|
|
16
|
+
type ExtendedComponentType<P = {}> = ComponentType<P> & {
|
|
17
|
+
displayName?: string;
|
|
18
|
+
}
|
|
4
19
|
import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
|
|
5
20
|
import { useFocusTrap } from '../../lib/composables/useFocusTrap';
|
|
6
21
|
|
|
@@ -225,9 +240,12 @@ const ModalImpl = memo(
|
|
|
225
240
|
.join(' ');
|
|
226
241
|
|
|
227
242
|
// Check for compound components usage
|
|
228
|
-
const hasCompoundComponents = React.Children.toArray(children).some(
|
|
229
|
-
|
|
230
|
-
|
|
243
|
+
const hasCompoundComponents = React.Children.toArray(children).some(
|
|
244
|
+
child =>
|
|
245
|
+
React.isValidElement(child) &&
|
|
246
|
+
['ModalHeader', 'ModalBody', 'ModalFooter'].includes(
|
|
247
|
+
(child.type as ExtendedComponentType).displayName || ''
|
|
248
|
+
)
|
|
231
249
|
);
|
|
232
250
|
|
|
233
251
|
const modalContent = (
|
|
@@ -236,12 +254,13 @@ const ModalImpl = memo(
|
|
|
236
254
|
React.Children.map(children, child => {
|
|
237
255
|
if (
|
|
238
256
|
React.isValidElement(child) &&
|
|
239
|
-
(child.type as
|
|
257
|
+
(child.type as ExtendedComponentType).displayName === 'ModalHeader'
|
|
240
258
|
) {
|
|
241
|
-
return React.cloneElement(child
|
|
242
|
-
|
|
259
|
+
return React.cloneElement(child as React.ReactElement<ModalHeaderProps>, {
|
|
260
|
+
...(child.props as ModalHeaderProps),
|
|
261
|
+
onClose: (child.props as ModalHeaderProps).onClose || close,
|
|
243
262
|
id: titleId,
|
|
244
|
-
}
|
|
263
|
+
});
|
|
245
264
|
}
|
|
246
265
|
return child;
|
|
247
266
|
})
|
|
@@ -1,52 +1,69 @@
|
|
|
1
|
-
import React, { memo } from 'react';
|
|
1
|
+
import React, { memo, forwardRef } from 'react';
|
|
2
2
|
import { SpinnerProps } from '../../lib/types/components';
|
|
3
3
|
import { useSpinner } from '../../lib/composables/useSpinner';
|
|
4
4
|
import { SPINNER } from '../../lib/constants/components';
|
|
5
5
|
import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
|
|
6
6
|
|
|
7
|
-
export const Spinner
|
|
8
|
-
(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
7
|
+
export const Spinner = memo(
|
|
8
|
+
forwardRef<HTMLDivElement, SpinnerProps>(
|
|
9
|
+
(
|
|
10
|
+
{
|
|
11
|
+
size = 'md',
|
|
12
|
+
variant = 'primary',
|
|
13
|
+
fullscreen = false,
|
|
14
|
+
className = '',
|
|
15
|
+
style,
|
|
16
|
+
glass,
|
|
17
|
+
'aria-label': ariaLabel,
|
|
18
|
+
role = 'status',
|
|
19
|
+
'aria-live': ariaLive = 'polite',
|
|
20
|
+
'aria-describe': ariaDescribe,
|
|
21
|
+
...rest
|
|
22
|
+
},
|
|
23
|
+
ref
|
|
24
|
+
) => {
|
|
25
|
+
const { generateSpinnerClass } = useSpinner({
|
|
26
|
+
size,
|
|
27
|
+
variant,
|
|
28
|
+
fullscreen,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const spinnerClass = generateSpinnerClass({
|
|
32
|
+
size,
|
|
33
|
+
variant,
|
|
34
|
+
fullscreen,
|
|
35
|
+
className: `${className} ${glass ? 'c-spinner--glass' : ''}`.trim(),
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const spinnerContent = (
|
|
39
|
+
<div
|
|
40
|
+
ref={ref}
|
|
41
|
+
className={spinnerClass}
|
|
42
|
+
style={style}
|
|
43
|
+
role={role}
|
|
44
|
+
aria-label={ariaLabel || 'Loading'}
|
|
45
|
+
aria-live={ariaLive}
|
|
46
|
+
aria-describe={ariaDescribe}
|
|
47
|
+
{...rest}
|
|
48
|
+
>
|
|
49
|
+
<span className={SPINNER.VISUALLY_HIDDEN}>{ariaLabel || 'Loading content...'}</span>
|
|
50
|
+
</div>
|
|
51
|
+
);
|
|
47
52
|
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
if (glass) {
|
|
54
|
+
const defaultGlassProps = {
|
|
55
|
+
displacementScale: 20,
|
|
56
|
+
blurAmount: 1,
|
|
57
|
+
borderRadius: 999,
|
|
58
|
+
mode: 'shader' as const,
|
|
59
|
+
};
|
|
60
|
+
const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
|
|
61
|
+
return <AtomixGlass {...glassProps}>{spinnerContent}</AtomixGlass>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return spinnerContent;
|
|
65
|
+
}
|
|
66
|
+
)
|
|
50
67
|
);
|
|
51
68
|
|
|
52
69
|
export type { SpinnerProps };
|