@umituz/react-native-design-system 4.23.117 → 4.23.119
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 +1 -1
- package/src/device/presentation/hooks/useAnonymousUser.ts +25 -31
- package/src/device/presentation/hooks/useDeviceInfo.ts +47 -91
- package/src/init/useAppInitialization.ts +24 -57
- package/src/media/domain/entities/Media.ts +1 -0
- package/src/media/infrastructure/utils/media-collection-utils.ts +9 -6
- package/src/media/presentation/hooks/useMedia.ts +73 -128
- package/src/molecules/alerts/AlertBanner.tsx +24 -46
- package/src/molecules/alerts/AlertInline.tsx +8 -9
- package/src/molecules/alerts/AlertModal.tsx +23 -14
- package/src/molecules/alerts/AlertToast.tsx +25 -53
- package/src/molecules/alerts/components/AlertContent.tsx +79 -0
- package/src/molecules/alerts/components/AlertIcon.tsx +31 -0
- package/src/molecules/alerts/components/index.ts +6 -0
- package/src/molecules/alerts/hooks/index.ts +6 -0
- package/src/molecules/alerts/hooks/useAlertAutoDismiss.ts +26 -0
- package/src/molecules/alerts/hooks/useAlertDismissHandler.ts +21 -0
- package/src/molecules/alerts/utils/alertUtils.ts +0 -21
- package/src/storage/cache/presentation/useCachedValue.ts +24 -65
- package/src/storage/presentation/hooks/useStorageState.ts +20 -29
- package/src/utilities/sharing/presentation/hooks/useSharing.ts +75 -140
- package/src/utils/errors/DesignSystemError.ts +57 -1
- package/src/utils/errors/ErrorHandler.ts +105 -1
- package/src/utils/errors/adapters/CacheErrorAdapter.ts +68 -0
- package/src/utils/errors/adapters/ImageErrorAdapter.ts +91 -0
- package/src/utils/errors/adapters/StorageErrorAdapter.ts +107 -0
- package/src/utils/errors/index.ts +5 -1
- package/src/utils/errors/types/Result.ts +64 -0
- package/src/utils/hooks/index.ts +12 -0
- package/src/utils/hooks/types/AsyncOperationTypes.ts +75 -0
- package/src/utils/hooks/useAsyncOperation.ts +223 -0
|
@@ -6,38 +6,28 @@
|
|
|
6
6
|
* Auto-dismisses after duration (default 3 seconds).
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import React
|
|
9
|
+
import React from 'react';
|
|
10
10
|
import { StyleSheet, View, Pressable } from 'react-native';
|
|
11
11
|
import { useSafeAreaInsets } from '../../safe-area';
|
|
12
|
-
import { AtomicText,
|
|
12
|
+
import { AtomicText, useIconName } from '../../atoms';
|
|
13
13
|
import { useAppDesignTokens } from '../../theme';
|
|
14
14
|
import { Alert, AlertPosition } from './AlertTypes';
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
15
|
+
import { getAlertBackgroundColor, getAlertTextColor, getActionButtonStyle, getActionTextColor } from './utils/alertUtils';
|
|
16
|
+
import { useAlertDismissHandler, useAlertAutoDismiss } from './hooks';
|
|
17
|
+
import { AlertIcon, AlertContent } from './components';
|
|
17
18
|
|
|
18
19
|
interface AlertBannerProps {
|
|
19
20
|
alert: Alert;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
export function AlertBanner({ alert }: AlertBannerProps) {
|
|
23
|
-
const dismissAlert = useAlertStore((state: { dismissAlert: (id: string) => void }) => state.dismissAlert);
|
|
24
24
|
const insets = useSafeAreaInsets();
|
|
25
25
|
const tokens = useAppDesignTokens();
|
|
26
26
|
const closeIcon = useIconName('close');
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}, [alert.id, dismissAlert, alert.onDismiss]);
|
|
32
|
-
|
|
33
|
-
// Auto-dismiss after duration
|
|
34
|
-
useEffect(() => {
|
|
35
|
-
const duration = alert.duration ?? DEFAULT_ALERT_DURATION;
|
|
36
|
-
if (duration <= 0) return;
|
|
37
|
-
|
|
38
|
-
const timer = setTimeout(handleDismiss, duration);
|
|
39
|
-
return () => clearTimeout(timer);
|
|
40
|
-
}, [alert.duration, handleDismiss]);
|
|
28
|
+
// Use shared hooks (replaces 20 lines of duplicate code)
|
|
29
|
+
const handleDismiss = useAlertDismissHandler(alert);
|
|
30
|
+
useAlertAutoDismiss(alert, handleDismiss);
|
|
41
31
|
|
|
42
32
|
const backgroundColor = getAlertBackgroundColor(alert.type, tokens);
|
|
43
33
|
const textColor = getAlertTextColor(tokens);
|
|
@@ -59,36 +49,22 @@ export function AlertBanner({ alert }: AlertBannerProps) {
|
|
|
59
49
|
<View style={styles.content}>
|
|
60
50
|
<View style={styles.row}>
|
|
61
51
|
{alert.icon && (
|
|
62
|
-
<
|
|
52
|
+
<AlertIcon
|
|
63
53
|
name={alert.icon}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
style={{ marginRight: tokens.spacing.sm }}
|
|
54
|
+
color={textColor}
|
|
55
|
+
marginRight={tokens.spacing.sm}
|
|
67
56
|
/>
|
|
68
57
|
)}
|
|
69
58
|
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
{alert.message && (
|
|
80
|
-
<AtomicText
|
|
81
|
-
type="bodySmall"
|
|
82
|
-
style={[
|
|
83
|
-
styles.message,
|
|
84
|
-
{ color: textColor, marginTop: tokens.spacing.xs },
|
|
85
|
-
]}
|
|
86
|
-
numberOfLines={2}
|
|
87
|
-
>
|
|
88
|
-
{alert.message}
|
|
89
|
-
</AtomicText>
|
|
90
|
-
)}
|
|
91
|
-
</View>
|
|
59
|
+
<AlertContent
|
|
60
|
+
title={alert.title}
|
|
61
|
+
message={alert.message}
|
|
62
|
+
titleColor={textColor}
|
|
63
|
+
messageColor={textColor}
|
|
64
|
+
messageMarginTop={tokens.spacing.xs}
|
|
65
|
+
titleNumberOfLines={1}
|
|
66
|
+
messageNumberOfLines={2}
|
|
67
|
+
/>
|
|
92
68
|
|
|
93
69
|
{alert.dismissible && (
|
|
94
70
|
<Pressable
|
|
@@ -96,7 +72,7 @@ export function AlertBanner({ alert }: AlertBannerProps) {
|
|
|
96
72
|
style={[styles.closeButton, { marginLeft: tokens.spacing.sm }]}
|
|
97
73
|
hitSlop={8}
|
|
98
74
|
>
|
|
99
|
-
<
|
|
75
|
+
<AlertIcon name={closeIcon} color={textColor} />
|
|
100
76
|
</Pressable>
|
|
101
77
|
)}
|
|
102
78
|
</View>
|
|
@@ -118,14 +94,16 @@ export function AlertBanner({ alert }: AlertBannerProps) {
|
|
|
118
94
|
paddingVertical: tokens.spacing.xs,
|
|
119
95
|
paddingHorizontal: tokens.spacing.sm,
|
|
120
96
|
marginRight: tokens.spacing.xs,
|
|
97
|
+
borderRadius: tokens.borders.radius.sm,
|
|
121
98
|
},
|
|
99
|
+
getActionButtonStyle(action.style, tokens),
|
|
122
100
|
]}
|
|
123
101
|
>
|
|
124
102
|
<AtomicText
|
|
125
103
|
type="bodySmall"
|
|
126
104
|
style={[
|
|
127
105
|
styles.actionText,
|
|
128
|
-
{ color:
|
|
106
|
+
{ color: getActionTextColor(action.style, tokens) },
|
|
129
107
|
]}
|
|
130
108
|
>
|
|
131
109
|
{action.label}
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import { StyleSheet, View } from 'react-native';
|
|
7
|
-
import { AtomicText } from '../../atoms';
|
|
8
7
|
import { useAppDesignTokens } from '../../theme';
|
|
9
8
|
import { Alert } from './AlertTypes';
|
|
10
9
|
import { getAlertBorderColor, getAlertBackgroundColorInline } from './utils/alertUtils';
|
|
10
|
+
import { AlertContent } from './components';
|
|
11
11
|
|
|
12
12
|
interface AlertInlineProps {
|
|
13
13
|
alert: Alert;
|
|
@@ -27,14 +27,13 @@ export const AlertInline: React.FC<AlertInlineProps> = ({ alert }) => {
|
|
|
27
27
|
marginVertical: tokens.spacing.sm,
|
|
28
28
|
}
|
|
29
29
|
]}>
|
|
30
|
-
<
|
|
31
|
-
{alert.title}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
)}
|
|
30
|
+
<AlertContent
|
|
31
|
+
title={alert.title}
|
|
32
|
+
message={alert.message}
|
|
33
|
+
titleColor={tokens.colors.textPrimary}
|
|
34
|
+
messageColor={tokens.colors.textSecondary}
|
|
35
|
+
messageMarginTop={tokens.spacing.xs}
|
|
36
|
+
/>
|
|
38
37
|
</View>
|
|
39
38
|
);
|
|
40
39
|
};
|
|
@@ -4,24 +4,22 @@
|
|
|
4
4
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import { StyleSheet, View, Modal, Pressable } from 'react-native';
|
|
7
|
-
import {
|
|
7
|
+
import { AtomicButton } from '../../atoms';
|
|
8
8
|
import { useAppDesignTokens } from '../../theme';
|
|
9
9
|
import { Alert } from './AlertTypes';
|
|
10
|
-
import { useAlertStore } from './AlertStore';
|
|
11
10
|
import { getAlertBackgroundColor } from './utils/alertUtils';
|
|
11
|
+
import { useAlertDismissHandler } from './hooks';
|
|
12
|
+
import { AlertContent } from './components';
|
|
12
13
|
|
|
13
14
|
interface AlertModalProps {
|
|
14
15
|
alert: Alert;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export const AlertModal: React.FC<AlertModalProps> = ({ alert }) => {
|
|
18
|
-
const dismissAlert = useAlertStore((state: { dismissAlert: (id: string) => void }) => state.dismissAlert);
|
|
19
19
|
const tokens = useAppDesignTokens();
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
alert.onDismiss?.();
|
|
24
|
-
};
|
|
21
|
+
// Use shared hook (replaces 8 lines of duplicate code)
|
|
22
|
+
const handleClose = useAlertDismissHandler(alert);
|
|
25
23
|
|
|
26
24
|
const headerColor = getAlertBackgroundColor(alert.type, tokens);
|
|
27
25
|
|
|
@@ -47,16 +45,26 @@ export const AlertModal: React.FC<AlertModalProps> = ({ alert }) => {
|
|
|
47
45
|
}
|
|
48
46
|
]}>
|
|
49
47
|
<View style={[styles.header, { backgroundColor: headerColor }]}>
|
|
50
|
-
<
|
|
51
|
-
{alert.title}
|
|
52
|
-
|
|
48
|
+
<AlertContent
|
|
49
|
+
title={alert.title}
|
|
50
|
+
message=""
|
|
51
|
+
titleColor={tokens.colors.textInverse}
|
|
52
|
+
messageColor={tokens.colors.textInverse}
|
|
53
|
+
titleType="titleLarge"
|
|
54
|
+
textAlign="center"
|
|
55
|
+
/>
|
|
53
56
|
</View>
|
|
54
57
|
|
|
55
58
|
<View style={[styles.content, { padding: tokens.spacing.lg }]}>
|
|
56
59
|
{alert.message && (
|
|
57
|
-
<
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
<AlertContent
|
|
61
|
+
title=""
|
|
62
|
+
message={alert.message}
|
|
63
|
+
titleColor={tokens.colors.textPrimary}
|
|
64
|
+
messageColor={tokens.colors.textPrimary}
|
|
65
|
+
messageType="bodyMedium"
|
|
66
|
+
textAlign="center"
|
|
67
|
+
/>
|
|
60
68
|
)}
|
|
61
69
|
|
|
62
70
|
<View style={[styles.actions, { marginTop: tokens.spacing.lg, gap: tokens.spacing.sm }]}>
|
|
@@ -66,10 +74,11 @@ export const AlertModal: React.FC<AlertModalProps> = ({ alert }) => {
|
|
|
66
74
|
title={action.label}
|
|
67
75
|
variant={action.style === 'destructive' ? 'danger' : action.style === 'secondary' ? 'secondary' : 'primary'}
|
|
68
76
|
onPress={async () => {
|
|
77
|
+
// BUG FIX: Execute action BEFORE closing (was backwards)
|
|
78
|
+
await action.onPress();
|
|
69
79
|
if (action.closeOnPress ?? true) {
|
|
70
80
|
handleClose();
|
|
71
81
|
}
|
|
72
|
-
await action.onPress();
|
|
73
82
|
}}
|
|
74
83
|
fullWidth
|
|
75
84
|
/>
|
|
@@ -5,48 +5,31 @@
|
|
|
5
5
|
* Floats on top of content.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import React
|
|
8
|
+
import React from 'react';
|
|
9
9
|
import { StyleSheet, View, Pressable } from 'react-native';
|
|
10
|
-
import { AtomicText,
|
|
10
|
+
import { AtomicText, useIconName } from '../../atoms';
|
|
11
11
|
import { useAppDesignTokens } from '../../theme';
|
|
12
12
|
import { Alert } from './AlertTypes';
|
|
13
|
-
import { useAlertStore } from './AlertStore';
|
|
14
13
|
import {
|
|
15
14
|
getAlertBackgroundColor,
|
|
16
15
|
getAlertTextColor,
|
|
17
16
|
getActionButtonStyle,
|
|
18
17
|
getActionTextColor,
|
|
19
|
-
DEFAULT_ALERT_DURATION,
|
|
20
18
|
} from './utils/alertUtils';
|
|
19
|
+
import { useAlertDismissHandler, useAlertAutoDismiss } from './hooks';
|
|
20
|
+
import { AlertIcon, AlertContent } from './components';
|
|
21
21
|
|
|
22
22
|
interface AlertToastProps {
|
|
23
23
|
alert: Alert;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export function AlertToast({ alert }: AlertToastProps) {
|
|
27
|
-
const dismissAlert = useAlertStore((state: { dismissAlert: (id: string) => void }) => state.dismissAlert);
|
|
28
27
|
const tokens = useAppDesignTokens();
|
|
29
28
|
const closeIcon = useIconName('close');
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}, [alert.id, dismissAlert, alert.onDismiss]);
|
|
35
|
-
|
|
36
|
-
const handleDismiss = useCallback(() => {
|
|
37
|
-
if (alert.dismissible) {
|
|
38
|
-
dismiss();
|
|
39
|
-
}
|
|
40
|
-
}, [alert.dismissible, dismiss]);
|
|
41
|
-
|
|
42
|
-
// Auto-dismiss after duration
|
|
43
|
-
useEffect(() => {
|
|
44
|
-
const duration = alert.duration ?? DEFAULT_ALERT_DURATION;
|
|
45
|
-
if (duration <= 0) return;
|
|
46
|
-
|
|
47
|
-
const timer = setTimeout(dismiss, duration);
|
|
48
|
-
return () => clearTimeout(timer);
|
|
49
|
-
}, [alert.duration, dismiss]);
|
|
30
|
+
// Use shared hooks (replaces 25 lines of duplicate code)
|
|
31
|
+
const handleDismiss = useAlertDismissHandler(alert);
|
|
32
|
+
useAlertAutoDismiss(alert, handleDismiss);
|
|
50
33
|
|
|
51
34
|
const backgroundColor = getAlertBackgroundColor(alert.type, tokens);
|
|
52
35
|
const textColor = getAlertTextColor(tokens);
|
|
@@ -63,39 +46,28 @@ export function AlertToast({ alert }: AlertToastProps) {
|
|
|
63
46
|
]}
|
|
64
47
|
testID={alert.testID}
|
|
65
48
|
>
|
|
66
|
-
<Pressable
|
|
49
|
+
<Pressable
|
|
50
|
+
onPress={alert.dismissible ? handleDismiss : undefined}
|
|
51
|
+
style={styles.content}
|
|
52
|
+
>
|
|
67
53
|
<View style={styles.row}>
|
|
68
54
|
{alert.icon && (
|
|
69
|
-
<
|
|
55
|
+
<AlertIcon
|
|
70
56
|
name={alert.icon}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
style={{ marginRight: tokens.spacing.sm }}
|
|
57
|
+
color={textColor}
|
|
58
|
+
marginRight={tokens.spacing.sm}
|
|
74
59
|
/>
|
|
75
60
|
)}
|
|
76
61
|
|
|
77
|
-
<
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
{alert.message && (
|
|
87
|
-
<AtomicText
|
|
88
|
-
type="bodySmall"
|
|
89
|
-
style={[
|
|
90
|
-
styles.message,
|
|
91
|
-
{ color: textColor, marginTop: tokens.spacing.xs },
|
|
92
|
-
]}
|
|
93
|
-
numberOfLines={3}
|
|
94
|
-
>
|
|
95
|
-
{alert.message}
|
|
96
|
-
</AtomicText>
|
|
97
|
-
)}
|
|
98
|
-
</View>
|
|
62
|
+
<AlertContent
|
|
63
|
+
title={alert.title}
|
|
64
|
+
message={alert.message}
|
|
65
|
+
titleColor={textColor}
|
|
66
|
+
messageColor={textColor}
|
|
67
|
+
messageMarginTop={tokens.spacing.xs}
|
|
68
|
+
titleNumberOfLines={2}
|
|
69
|
+
messageNumberOfLines={3}
|
|
70
|
+
/>
|
|
99
71
|
|
|
100
72
|
{alert.dismissible && (
|
|
101
73
|
<Pressable
|
|
@@ -103,7 +75,7 @@ export function AlertToast({ alert }: AlertToastProps) {
|
|
|
103
75
|
style={[styles.closeButton, { marginLeft: tokens.spacing.sm }]}
|
|
104
76
|
hitSlop={8}
|
|
105
77
|
>
|
|
106
|
-
<
|
|
78
|
+
<AlertIcon name={closeIcon} color={textColor} />
|
|
107
79
|
</Pressable>
|
|
108
80
|
)}
|
|
109
81
|
</View>
|
|
@@ -116,7 +88,7 @@ export function AlertToast({ alert }: AlertToastProps) {
|
|
|
116
88
|
onPress={async () => {
|
|
117
89
|
await action.onPress();
|
|
118
90
|
if (action.closeOnPress ?? true) {
|
|
119
|
-
|
|
91
|
+
handleDismiss();
|
|
120
92
|
}
|
|
121
93
|
}}
|
|
122
94
|
style={[
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AlertContent Component
|
|
3
|
+
*
|
|
4
|
+
* Shared title + message layout for alert variants.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import { StyleSheet, View } from 'react-native';
|
|
9
|
+
import { AtomicText } from '../../../atoms';
|
|
10
|
+
|
|
11
|
+
interface AlertContentProps {
|
|
12
|
+
title: string;
|
|
13
|
+
message?: string;
|
|
14
|
+
titleColor: string;
|
|
15
|
+
messageColor: string;
|
|
16
|
+
titleType?: 'bodyMedium' | 'titleLarge';
|
|
17
|
+
messageType?: 'bodySmall' | 'bodyMedium';
|
|
18
|
+
messageMarginTop?: number;
|
|
19
|
+
titleNumberOfLines?: number;
|
|
20
|
+
messageNumberOfLines?: number;
|
|
21
|
+
textAlign?: 'left' | 'center' | 'right';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function AlertContent({
|
|
25
|
+
title,
|
|
26
|
+
message,
|
|
27
|
+
titleColor,
|
|
28
|
+
messageColor,
|
|
29
|
+
titleType = 'bodyMedium',
|
|
30
|
+
messageType = 'bodySmall',
|
|
31
|
+
messageMarginTop = 0,
|
|
32
|
+
titleNumberOfLines = 2,
|
|
33
|
+
messageNumberOfLines = 3,
|
|
34
|
+
textAlign = 'left',
|
|
35
|
+
}: AlertContentProps) {
|
|
36
|
+
return (
|
|
37
|
+
<View style={styles.container}>
|
|
38
|
+
<AtomicText
|
|
39
|
+
type={titleType}
|
|
40
|
+
style={[
|
|
41
|
+
styles.title,
|
|
42
|
+
{ color: titleColor, textAlign },
|
|
43
|
+
]}
|
|
44
|
+
numberOfLines={titleNumberOfLines}
|
|
45
|
+
>
|
|
46
|
+
{title}
|
|
47
|
+
</AtomicText>
|
|
48
|
+
|
|
49
|
+
{message && (
|
|
50
|
+
<AtomicText
|
|
51
|
+
type={messageType}
|
|
52
|
+
style={[
|
|
53
|
+
styles.message,
|
|
54
|
+
{
|
|
55
|
+
color: messageColor,
|
|
56
|
+
marginTop: messageMarginTop,
|
|
57
|
+
textAlign,
|
|
58
|
+
},
|
|
59
|
+
]}
|
|
60
|
+
numberOfLines={messageNumberOfLines}
|
|
61
|
+
>
|
|
62
|
+
{message}
|
|
63
|
+
</AtomicText>
|
|
64
|
+
)}
|
|
65
|
+
</View>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const styles = StyleSheet.create({
|
|
70
|
+
container: {
|
|
71
|
+
flex: 1,
|
|
72
|
+
},
|
|
73
|
+
title: {
|
|
74
|
+
fontWeight: '700',
|
|
75
|
+
},
|
|
76
|
+
message: {
|
|
77
|
+
opacity: 0.9,
|
|
78
|
+
},
|
|
79
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AlertIcon Component
|
|
3
|
+
*
|
|
4
|
+
* Shared icon rendering for alert variants.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import { AtomicIcon } from '../../../atoms';
|
|
9
|
+
|
|
10
|
+
interface AlertIconProps {
|
|
11
|
+
name: string;
|
|
12
|
+
color: string;
|
|
13
|
+
size?: number;
|
|
14
|
+
marginRight?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function AlertIcon({
|
|
18
|
+
name,
|
|
19
|
+
color,
|
|
20
|
+
size = 20,
|
|
21
|
+
marginRight = 0
|
|
22
|
+
}: AlertIconProps) {
|
|
23
|
+
return (
|
|
24
|
+
<AtomicIcon
|
|
25
|
+
name={name}
|
|
26
|
+
customSize={size}
|
|
27
|
+
customColor={color}
|
|
28
|
+
style={{ marginRight }}
|
|
29
|
+
/>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useAlertAutoDismiss Hook
|
|
3
|
+
*
|
|
4
|
+
* Handles auto-dismiss timer for alerts with duration.
|
|
5
|
+
* Checks dismissible flag before setting timer (fixes AlertBanner bug).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { useEffect } from 'react';
|
|
9
|
+
import type { Alert } from '../AlertTypes';
|
|
10
|
+
import { DEFAULT_ALERT_DURATION } from '../utils/alertUtils';
|
|
11
|
+
|
|
12
|
+
export function useAlertAutoDismiss(
|
|
13
|
+
alert: Alert,
|
|
14
|
+
onDismiss: () => void
|
|
15
|
+
) {
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
// BUG FIX: Check dismissible flag before auto-dismissing
|
|
18
|
+
if (!alert.dismissible) return;
|
|
19
|
+
|
|
20
|
+
const duration = alert.duration ?? DEFAULT_ALERT_DURATION;
|
|
21
|
+
if (duration <= 0) return;
|
|
22
|
+
|
|
23
|
+
const timer = setTimeout(onDismiss, duration);
|
|
24
|
+
return () => clearTimeout(timer);
|
|
25
|
+
}, [alert.duration, alert.dismissible, onDismiss]);
|
|
26
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useAlertDismissHandler Hook
|
|
3
|
+
*
|
|
4
|
+
* Extracts common dismiss logic from all alert variants.
|
|
5
|
+
* Combines store dismissal with optional callback.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { useCallback } from 'react';
|
|
9
|
+
import { useAlertStore } from '../AlertStore';
|
|
10
|
+
import type { Alert } from '../AlertTypes';
|
|
11
|
+
|
|
12
|
+
export function useAlertDismissHandler(alert: Alert) {
|
|
13
|
+
const dismissAlert = useAlertStore((state) => state.dismissAlert);
|
|
14
|
+
|
|
15
|
+
const handleDismiss = useCallback(() => {
|
|
16
|
+
dismissAlert(alert.id);
|
|
17
|
+
alert.onDismiss?.();
|
|
18
|
+
}, [alert.id, alert.onDismiss, dismissAlert]);
|
|
19
|
+
|
|
20
|
+
return handleDismiss;
|
|
21
|
+
}
|
|
@@ -66,27 +66,6 @@ export function getAlertTextColor(tokens: DesignTokens): string {
|
|
|
66
66
|
return tokens.colors.textInverse;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
/**
|
|
70
|
-
* Gets default icon for alert type
|
|
71
|
-
*
|
|
72
|
-
* @param type - Alert type
|
|
73
|
-
* @returns Icon name or undefined
|
|
74
|
-
*/
|
|
75
|
-
export function getAlertIcon(type: AlertType): string | undefined {
|
|
76
|
-
switch (type) {
|
|
77
|
-
case AlertType.SUCCESS:
|
|
78
|
-
return 'CheckCircle';
|
|
79
|
-
case AlertType.ERROR:
|
|
80
|
-
return 'AlertCircle';
|
|
81
|
-
case AlertType.WARNING:
|
|
82
|
-
return 'AlertTriangle';
|
|
83
|
-
case AlertType.INFO:
|
|
84
|
-
return 'Info';
|
|
85
|
-
default:
|
|
86
|
-
return undefined;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
69
|
/**
|
|
91
70
|
* Gets action button style
|
|
92
71
|
*
|