@hero-design/rn 8.121.0 → 8.123.0
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 +16 -0
- package/es/index.js +283 -33
- package/lib/index.js +283 -32
- package/package.json +1 -1
- package/src/components/Avatar/Avatar.tsx +3 -3
- package/src/components/Avatar/StyledAvatar.tsx +7 -1
- package/src/components/InlineLoader/AnimatedGradientText.tsx +135 -0
- package/src/components/InlineLoader/InlineLoader.tsx +93 -0
- package/src/components/InlineLoader/StyledInlineLoader.tsx +20 -0
- package/src/components/InlineLoader/index.tsx +4 -0
- package/src/components/InlineLoader/types.ts +9 -0
- package/src/components/InlineLoader/utils.ts +66 -0
- package/src/index.ts +5 -0
- package/src/theme/components/avatar.ts +2 -0
- package/src/theme/components/inlineLoader.ts +18 -0
- package/src/theme/getTheme.ts +3 -0
- package/types/components/Avatar/Avatar.d.ts +2 -2
- package/types/components/Avatar/StyledAvatar.d.ts +1 -1
- package/types/components/InlineLoader/AnimatedGradientText.d.ts +8 -0
- package/types/components/InlineLoader/InlineLoader.d.ts +31 -0
- package/types/components/InlineLoader/StyledInlineLoader.d.ts +18 -0
- package/types/components/InlineLoader/index.d.ts +3 -0
- package/types/components/InlineLoader/types.d.ts +4 -0
- package/types/components/InlineLoader/utils.d.ts +9 -0
- package/types/index.d.ts +2 -1
- package/types/theme/components/avatar.d.ts +1 -0
- package/types/theme/components/inlineLoader.d.ts +16 -0
- package/types/theme/getTheme.d.ts +2 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import MaskedView from '@react-native-masked-view/masked-view';
|
|
2
|
+
import { LinearGradient } from 'expo-linear-gradient';
|
|
3
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
4
|
+
import type { LayoutChangeEvent } from 'react-native';
|
|
5
|
+
import { Animated, Easing, Platform, View } from 'react-native';
|
|
6
|
+
import { useTheme } from '../../theme';
|
|
7
|
+
import Typography from '../Typography';
|
|
8
|
+
|
|
9
|
+
interface AnimatedGradientTextProps {
|
|
10
|
+
children: string;
|
|
11
|
+
fontSize: number;
|
|
12
|
+
lineHeight: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const ANIMATION_DURATION_MS = 2000;
|
|
16
|
+
|
|
17
|
+
const AnimatedGradientText = ({
|
|
18
|
+
children,
|
|
19
|
+
fontSize,
|
|
20
|
+
lineHeight,
|
|
21
|
+
}: AnimatedGradientTextProps) => {
|
|
22
|
+
const theme = useTheme();
|
|
23
|
+
const gradient = theme.colors.gradients.aiHorizontal;
|
|
24
|
+
|
|
25
|
+
const [size, setSize] = useState<{ width: number; height: number } | null>(
|
|
26
|
+
null
|
|
27
|
+
);
|
|
28
|
+
const animatedValue = useRef(new Animated.Value(0));
|
|
29
|
+
|
|
30
|
+
const onLayout = useCallback((event: LayoutChangeEvent) => {
|
|
31
|
+
const { width, height } = event.nativeEvent.layout;
|
|
32
|
+
setSize((prev) => {
|
|
33
|
+
if (prev?.width === width && prev?.height === height) return prev;
|
|
34
|
+
return { width, height };
|
|
35
|
+
});
|
|
36
|
+
}, []);
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (!size) return;
|
|
40
|
+
animatedValue.current.setValue(0);
|
|
41
|
+
const animation = Animated.loop(
|
|
42
|
+
Animated.timing(animatedValue.current, {
|
|
43
|
+
toValue: 1,
|
|
44
|
+
duration: ANIMATION_DURATION_MS,
|
|
45
|
+
easing: Easing.linear,
|
|
46
|
+
useNativeDriver: Platform.OS !== 'web',
|
|
47
|
+
})
|
|
48
|
+
);
|
|
49
|
+
animation.start();
|
|
50
|
+
return () => animation.stop();
|
|
51
|
+
}, [size]);
|
|
52
|
+
|
|
53
|
+
// Slide left by one full text-width per loop cycle.
|
|
54
|
+
// Starting at 0 keeps the gradient visible from the first frame.
|
|
55
|
+
const translateX = size
|
|
56
|
+
? animatedValue.current.interpolate({
|
|
57
|
+
inputRange: [0, 1],
|
|
58
|
+
outputRange: [0, -size.width],
|
|
59
|
+
})
|
|
60
|
+
: animatedValue.current;
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<View>
|
|
64
|
+
<MaskedView
|
|
65
|
+
accessibilityElementsHidden
|
|
66
|
+
importantForAccessibility="no-hide-descendants"
|
|
67
|
+
maskElement={
|
|
68
|
+
<View
|
|
69
|
+
onLayout={onLayout}
|
|
70
|
+
style={{ backgroundColor: 'transparent' }}
|
|
71
|
+
testID="animated-gradient-text-mask"
|
|
72
|
+
>
|
|
73
|
+
<Typography.Body intent="body" style={{ fontSize, lineHeight }}>
|
|
74
|
+
{children}
|
|
75
|
+
</Typography.Body>
|
|
76
|
+
</View>
|
|
77
|
+
}
|
|
78
|
+
>
|
|
79
|
+
{size ? (
|
|
80
|
+
<View
|
|
81
|
+
style={{
|
|
82
|
+
width: size.width,
|
|
83
|
+
height: size.height,
|
|
84
|
+
overflow: 'hidden',
|
|
85
|
+
}}
|
|
86
|
+
>
|
|
87
|
+
{/*
|
|
88
|
+
* Double the colour pattern so the gradient tiles seamlessly:
|
|
89
|
+
* [A, B, A, B, A] at equal spacing means the slice at
|
|
90
|
+
* translateX=0 looks identical to the slice at translateX=-width,
|
|
91
|
+
* eliminating the jump when the loop restarts.
|
|
92
|
+
*/}
|
|
93
|
+
<Animated.View
|
|
94
|
+
style={{
|
|
95
|
+
width: size.width * 2,
|
|
96
|
+
height: size.height,
|
|
97
|
+
transform: [{ translateX }],
|
|
98
|
+
}}
|
|
99
|
+
>
|
|
100
|
+
<LinearGradient
|
|
101
|
+
start={gradient.start}
|
|
102
|
+
end={gradient.end}
|
|
103
|
+
colors={[
|
|
104
|
+
gradient.colors[0],
|
|
105
|
+
gradient.colors[1],
|
|
106
|
+
gradient.colors[2],
|
|
107
|
+
gradient.colors[1],
|
|
108
|
+
gradient.colors[0],
|
|
109
|
+
]}
|
|
110
|
+
locations={[0, 0.25, 0.5, 0.75, 1.0]}
|
|
111
|
+
style={{ width: '100%', height: '100%' }}
|
|
112
|
+
/>
|
|
113
|
+
</Animated.View>
|
|
114
|
+
</View>
|
|
115
|
+
) : (
|
|
116
|
+
<Typography.Body intent="body" style={{ fontSize, lineHeight }}>
|
|
117
|
+
{children}
|
|
118
|
+
</Typography.Body>
|
|
119
|
+
)}
|
|
120
|
+
</MaskedView>
|
|
121
|
+
{/* Visually hidden but accessible text for screen readers */}
|
|
122
|
+
<View
|
|
123
|
+
style={{ position: 'absolute', opacity: 0 }}
|
|
124
|
+
importantForAccessibility="yes"
|
|
125
|
+
accessibilityElementsHidden={false}
|
|
126
|
+
>
|
|
127
|
+
<Typography.Body intent="body" style={{ fontSize, lineHeight }}>
|
|
128
|
+
{children}
|
|
129
|
+
</Typography.Body>
|
|
130
|
+
</View>
|
|
131
|
+
</View>
|
|
132
|
+
);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export default AnimatedGradientText;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
import Icon from '../Icon';
|
|
4
|
+
import Typography from '../Typography';
|
|
5
|
+
import { useTheme } from '../../theme';
|
|
6
|
+
import AnimatedGradientText from './AnimatedGradientText';
|
|
7
|
+
import {
|
|
8
|
+
getIconName,
|
|
9
|
+
getIconIntent,
|
|
10
|
+
getIconSize,
|
|
11
|
+
getTextSizeName,
|
|
12
|
+
} from './utils';
|
|
13
|
+
import { StyledContainer, StyledIconContainer } from './StyledInlineLoader';
|
|
14
|
+
import type {
|
|
15
|
+
InlineLoaderState,
|
|
16
|
+
InlineLoaderIntent,
|
|
17
|
+
InlineLoaderTextSize,
|
|
18
|
+
} from './types';
|
|
19
|
+
|
|
20
|
+
export interface InlineLoaderProps {
|
|
21
|
+
/**
|
|
22
|
+
* Text content displayed next to the icon.
|
|
23
|
+
*/
|
|
24
|
+
text: string;
|
|
25
|
+
/**
|
|
26
|
+
* Current state of the loader.
|
|
27
|
+
*/
|
|
28
|
+
state?: InlineLoaderState;
|
|
29
|
+
/**
|
|
30
|
+
* Visual intent.
|
|
31
|
+
*/
|
|
32
|
+
intent?: InlineLoaderIntent;
|
|
33
|
+
/**
|
|
34
|
+
* Font size (px) — also controls icon size.
|
|
35
|
+
*/
|
|
36
|
+
size?: InlineLoaderTextSize;
|
|
37
|
+
/**
|
|
38
|
+
* Additional container style.
|
|
39
|
+
*/
|
|
40
|
+
style?: StyleProp<ViewStyle>;
|
|
41
|
+
/**
|
|
42
|
+
* Testing id.
|
|
43
|
+
*/
|
|
44
|
+
testID?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const InlineLoader = ({
|
|
48
|
+
text,
|
|
49
|
+
state = 'idle',
|
|
50
|
+
intent = 'neutral',
|
|
51
|
+
size = 14,
|
|
52
|
+
style,
|
|
53
|
+
testID,
|
|
54
|
+
}: InlineLoaderProps) => {
|
|
55
|
+
const theme = useTheme();
|
|
56
|
+
const textSizeName = getTextSizeName(size);
|
|
57
|
+
const lineHeight = theme.__hd__.inlineLoader.icon.lineHeights[textSizeName];
|
|
58
|
+
|
|
59
|
+
const iconName = getIconName(state);
|
|
60
|
+
const iconIntent = getIconIntent(state, intent);
|
|
61
|
+
const { size: iconSize, styleFontSize } = getIconSize(size);
|
|
62
|
+
|
|
63
|
+
const iconStyle = styleFontSize ? { fontSize: styleFontSize } : undefined;
|
|
64
|
+
const isAiLoading = state === 'loading' && intent === 'ai';
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<StyledContainer testID={testID} style={style}>
|
|
68
|
+
<StyledIconContainer themeSize={textSizeName}>
|
|
69
|
+
<Icon
|
|
70
|
+
icon={iconName}
|
|
71
|
+
intent={iconIntent}
|
|
72
|
+
size={iconSize}
|
|
73
|
+
spin={state === 'loading'}
|
|
74
|
+
style={iconStyle}
|
|
75
|
+
accessible={false}
|
|
76
|
+
accessibilityElementsHidden
|
|
77
|
+
importantForAccessibility="no-hide-descendants"
|
|
78
|
+
/>
|
|
79
|
+
</StyledIconContainer>
|
|
80
|
+
{isAiLoading ? (
|
|
81
|
+
<AnimatedGradientText fontSize={size} lineHeight={lineHeight}>
|
|
82
|
+
{text}
|
|
83
|
+
</AnimatedGradientText>
|
|
84
|
+
) : (
|
|
85
|
+
<Typography.Body intent="body" style={{ fontSize: size, lineHeight }}>
|
|
86
|
+
{text}
|
|
87
|
+
</Typography.Body>
|
|
88
|
+
)}
|
|
89
|
+
</StyledContainer>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export default InlineLoader;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { View } from 'react-native';
|
|
2
|
+
import styled from '@emotion/native';
|
|
3
|
+
import type { InlineLoaderTextSizeName } from './types';
|
|
4
|
+
|
|
5
|
+
type ThemeSize = InlineLoaderTextSizeName;
|
|
6
|
+
|
|
7
|
+
const StyledContainer = styled(View)(({ theme }) => ({
|
|
8
|
+
flexDirection: 'row',
|
|
9
|
+
alignItems: 'flex-start',
|
|
10
|
+
gap: theme.__hd__.inlineLoader.space.gap,
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
const StyledIconContainer = styled(View)<{
|
|
14
|
+
themeSize: ThemeSize;
|
|
15
|
+
}>(({ themeSize, theme }) => ({
|
|
16
|
+
height: theme.__hd__.inlineLoader.icon.lineHeights[themeSize],
|
|
17
|
+
justifyContent: 'center',
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
export { StyledContainer, StyledIconContainer };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type InlineLoaderState = 'idle' | 'loading' | 'success' | 'error';
|
|
2
|
+
export type InlineLoaderIntent = 'neutral' | 'ai';
|
|
3
|
+
export type InlineLoaderTextSize = 10 | 12 | 14 | 16 | 18;
|
|
4
|
+
export type InlineLoaderTextSizeName =
|
|
5
|
+
| 'xsmall'
|
|
6
|
+
| 'small'
|
|
7
|
+
| 'medium'
|
|
8
|
+
| 'large'
|
|
9
|
+
| 'xlarge';
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { IconName } from '../Icon';
|
|
2
|
+
import type {
|
|
3
|
+
InlineLoaderState,
|
|
4
|
+
InlineLoaderIntent,
|
|
5
|
+
InlineLoaderTextSize,
|
|
6
|
+
InlineLoaderTextSizeName,
|
|
7
|
+
} from './types';
|
|
8
|
+
|
|
9
|
+
const TEXT_SIZE_NAMES: Record<InlineLoaderTextSize, InlineLoaderTextSizeName> =
|
|
10
|
+
{
|
|
11
|
+
10: 'xsmall',
|
|
12
|
+
12: 'small',
|
|
13
|
+
14: 'medium',
|
|
14
|
+
16: 'large',
|
|
15
|
+
18: 'xlarge',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const getTextSizeName = (
|
|
19
|
+
size: InlineLoaderTextSize
|
|
20
|
+
): InlineLoaderTextSizeName => TEXT_SIZE_NAMES[size];
|
|
21
|
+
|
|
22
|
+
export const getIconName = (state: InlineLoaderState): IconName => {
|
|
23
|
+
switch (state) {
|
|
24
|
+
case 'idle':
|
|
25
|
+
return 'circle-ok-outlined';
|
|
26
|
+
case 'loading':
|
|
27
|
+
return 'loading';
|
|
28
|
+
case 'success':
|
|
29
|
+
return 'circle-check';
|
|
30
|
+
case 'error':
|
|
31
|
+
return 'circle-cancel-outlined';
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const getIconIntent = (
|
|
36
|
+
state: InlineLoaderState,
|
|
37
|
+
intent: InlineLoaderIntent
|
|
38
|
+
): 'inactive' | 'primary' | 'ai' | 'success' | 'danger' => {
|
|
39
|
+
switch (state) {
|
|
40
|
+
case 'idle':
|
|
41
|
+
return 'inactive';
|
|
42
|
+
case 'loading':
|
|
43
|
+
return intent === 'ai' ? 'ai' : 'primary';
|
|
44
|
+
case 'success':
|
|
45
|
+
return 'success';
|
|
46
|
+
case 'error':
|
|
47
|
+
return 'danger';
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const getIconSize = (
|
|
52
|
+
textSize: InlineLoaderTextSize
|
|
53
|
+
): { size: 'xxxsmall' | 'xsmall' | 'small'; styleFontSize?: number } => {
|
|
54
|
+
switch (textSize) {
|
|
55
|
+
case 10:
|
|
56
|
+
return { size: 'xxxsmall' };
|
|
57
|
+
case 12:
|
|
58
|
+
return { size: 'xxxsmall', styleFontSize: 14 };
|
|
59
|
+
case 14:
|
|
60
|
+
return { size: 'xxxsmall', styleFontSize: 14 };
|
|
61
|
+
case 16:
|
|
62
|
+
return { size: 'xsmall' };
|
|
63
|
+
case 18:
|
|
64
|
+
return { size: 'small' };
|
|
65
|
+
}
|
|
66
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -85,6 +85,9 @@ import SegmentedControl from './components/SegmentedControl';
|
|
|
85
85
|
import FloatingIsland from './components/FloatingIsland';
|
|
86
86
|
import LocaleProvider from './components/LocaleProvider';
|
|
87
87
|
import FilterTrigger from './components/FilterTrigger';
|
|
88
|
+
import InlineLoader, {
|
|
89
|
+
type InlineLoaderProps,
|
|
90
|
+
} from './components/InlineLoader';
|
|
88
91
|
|
|
89
92
|
export {
|
|
90
93
|
theme,
|
|
@@ -169,6 +172,8 @@ export {
|
|
|
169
172
|
FloatingIsland,
|
|
170
173
|
LocaleProvider,
|
|
171
174
|
FilterTrigger,
|
|
175
|
+
InlineLoader,
|
|
176
|
+
type InlineLoaderProps,
|
|
172
177
|
styled,
|
|
173
178
|
};
|
|
174
179
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { mobileAvatarPalette } from '@hero-design/colors';
|
|
1
2
|
import type { GlobalTheme } from '../global';
|
|
2
3
|
|
|
3
4
|
const getAvatarTheme = (theme: GlobalTheme) => {
|
|
4
5
|
const colors = {
|
|
6
|
+
neutral: mobileAvatarPalette.maasstrichtBlueLight25,
|
|
5
7
|
primary: theme.colors.primary,
|
|
6
8
|
info: theme.colors.info,
|
|
7
9
|
danger: theme.colors.error,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { GlobalTheme } from '../global';
|
|
2
|
+
|
|
3
|
+
const getInlineLoaderTheme = (theme: GlobalTheme) => ({
|
|
4
|
+
space: {
|
|
5
|
+
gap: theme.space.small,
|
|
6
|
+
},
|
|
7
|
+
icon: {
|
|
8
|
+
lineHeights: {
|
|
9
|
+
xsmall: theme.lineHeights.xsmall,
|
|
10
|
+
small: theme.lineHeights.small,
|
|
11
|
+
medium: theme.lineHeights.medium,
|
|
12
|
+
large: theme.lineHeights.large,
|
|
13
|
+
xlarge: theme.lineHeights.xlarge,
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export default getInlineLoaderTheme;
|
package/src/theme/getTheme.ts
CHANGED
|
@@ -66,6 +66,7 @@ import getAppCueTheme from './components/appCue';
|
|
|
66
66
|
import type { ThemeMode } from './global/colors/types';
|
|
67
67
|
import getFilterTriggerTheme from './components/filterTrigger';
|
|
68
68
|
import getSegmentedControlTheme from './components/segmentedControl';
|
|
69
|
+
import getInlineLoaderTheme from './components/inlineLoader';
|
|
69
70
|
|
|
70
71
|
type Theme = GlobalTheme & {
|
|
71
72
|
themeMode?: ThemeMode;
|
|
@@ -124,6 +125,7 @@ type Theme = GlobalTheme & {
|
|
|
124
125
|
toolbar: ReturnType<typeof getToolbarTheme>;
|
|
125
126
|
typography: ReturnType<typeof getTypographyTheme>;
|
|
126
127
|
floatingIsland: ReturnType<typeof getFloatingIslandTheme>;
|
|
128
|
+
inlineLoader: ReturnType<typeof getInlineLoaderTheme>;
|
|
127
129
|
segmentedControl: ReturnType<typeof getSegmentedControlTheme>;
|
|
128
130
|
};
|
|
129
131
|
};
|
|
@@ -190,6 +192,7 @@ const getTheme = (
|
|
|
190
192
|
toolbar: getToolbarTheme(globalTheme),
|
|
191
193
|
typography: getTypographyTheme(globalTheme),
|
|
192
194
|
floatingIsland: getFloatingIslandTheme(globalTheme),
|
|
195
|
+
inlineLoader: getInlineLoaderTheme(globalTheme),
|
|
193
196
|
segmentedControl: getSegmentedControlTheme(globalTheme),
|
|
194
197
|
},
|
|
195
198
|
};
|
|
@@ -14,9 +14,9 @@ export interface AvatarProps extends ViewProps {
|
|
|
14
14
|
*/
|
|
15
15
|
title?: string;
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
17
|
+
* Visual intent of the Avatar. Defaults to `'neutral'`.
|
|
18
18
|
*/
|
|
19
|
-
intent?: 'primary' | 'info' | 'danger' | 'success' | 'warning';
|
|
19
|
+
intent?: 'neutral' | 'primary' | 'info' | 'danger' | 'success' | 'warning';
|
|
20
20
|
/**
|
|
21
21
|
* Size of the avatar.
|
|
22
22
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { View, Image } from 'react-native';
|
|
2
2
|
import type { TextProps } from '../Typography/Text';
|
|
3
3
|
type ThemeSize = 'small' | 'medium' | 'large' | 'xlarge' | 'xxlarge' | 'xxxlarge' | 'xxxxlarge' | 'xxxxxlarge';
|
|
4
|
-
type ThemeIntent = 'primary' | 'info' | 'danger' | 'success' | 'warning';
|
|
4
|
+
type ThemeIntent = 'neutral' | 'primary' | 'info' | 'danger' | 'success' | 'warning';
|
|
5
5
|
declare const StyledWrapper: import("@emotion/native").StyledComponent<import("react-native").TouchableOpacityProps & import("react").RefAttributes<View> & {
|
|
6
6
|
theme?: import("@emotion/react").Theme;
|
|
7
7
|
as?: React.ElementType;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface AnimatedGradientTextProps {
|
|
3
|
+
children: string;
|
|
4
|
+
fontSize: number;
|
|
5
|
+
lineHeight: number;
|
|
6
|
+
}
|
|
7
|
+
declare const AnimatedGradientText: ({ children, fontSize, lineHeight, }: AnimatedGradientTextProps) => React.JSX.Element;
|
|
8
|
+
export default AnimatedGradientText;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
import type { InlineLoaderState, InlineLoaderIntent, InlineLoaderTextSize } from './types';
|
|
4
|
+
export interface InlineLoaderProps {
|
|
5
|
+
/**
|
|
6
|
+
* Text content displayed next to the icon.
|
|
7
|
+
*/
|
|
8
|
+
text: string;
|
|
9
|
+
/**
|
|
10
|
+
* Current state of the loader.
|
|
11
|
+
*/
|
|
12
|
+
state?: InlineLoaderState;
|
|
13
|
+
/**
|
|
14
|
+
* Visual intent.
|
|
15
|
+
*/
|
|
16
|
+
intent?: InlineLoaderIntent;
|
|
17
|
+
/**
|
|
18
|
+
* Font size (px) — also controls icon size.
|
|
19
|
+
*/
|
|
20
|
+
size?: InlineLoaderTextSize;
|
|
21
|
+
/**
|
|
22
|
+
* Additional container style.
|
|
23
|
+
*/
|
|
24
|
+
style?: StyleProp<ViewStyle>;
|
|
25
|
+
/**
|
|
26
|
+
* Testing id.
|
|
27
|
+
*/
|
|
28
|
+
testID?: string;
|
|
29
|
+
}
|
|
30
|
+
declare const InlineLoader: ({ text, state, intent, size, style, testID, }: InlineLoaderProps) => React.JSX.Element;
|
|
31
|
+
export default InlineLoader;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { View } from 'react-native';
|
|
2
|
+
import type { InlineLoaderTextSizeName } from './types';
|
|
3
|
+
type ThemeSize = InlineLoaderTextSizeName;
|
|
4
|
+
declare const StyledContainer: import("@emotion/native").StyledComponent<import("react-native").ViewProps & {
|
|
5
|
+
theme?: import("@emotion/react").Theme;
|
|
6
|
+
as?: React.ElementType;
|
|
7
|
+
}, {}, {
|
|
8
|
+
ref?: import("react").Ref<View> | undefined;
|
|
9
|
+
}>;
|
|
10
|
+
declare const StyledIconContainer: import("@emotion/native").StyledComponent<import("react-native").ViewProps & {
|
|
11
|
+
theme?: import("@emotion/react").Theme;
|
|
12
|
+
as?: React.ElementType;
|
|
13
|
+
} & {
|
|
14
|
+
themeSize: ThemeSize;
|
|
15
|
+
}, {}, {
|
|
16
|
+
ref?: import("react").Ref<View> | undefined;
|
|
17
|
+
}>;
|
|
18
|
+
export { StyledContainer, StyledIconContainer };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type InlineLoaderState = 'idle' | 'loading' | 'success' | 'error';
|
|
2
|
+
export type InlineLoaderIntent = 'neutral' | 'ai';
|
|
3
|
+
export type InlineLoaderTextSize = 10 | 12 | 14 | 16 | 18;
|
|
4
|
+
export type InlineLoaderTextSizeName = 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { IconName } from '../Icon';
|
|
2
|
+
import type { InlineLoaderState, InlineLoaderIntent, InlineLoaderTextSize, InlineLoaderTextSizeName } from './types';
|
|
3
|
+
export declare const getTextSizeName: (size: InlineLoaderTextSize) => InlineLoaderTextSizeName;
|
|
4
|
+
export declare const getIconName: (state: InlineLoaderState) => IconName;
|
|
5
|
+
export declare const getIconIntent: (state: InlineLoaderState, intent: InlineLoaderIntent) => "inactive" | "primary" | "ai" | "success" | "danger";
|
|
6
|
+
export declare const getIconSize: (textSize: InlineLoaderTextSize) => {
|
|
7
|
+
size: "xxxsmall" | "xsmall" | "small";
|
|
8
|
+
styleFontSize?: number;
|
|
9
|
+
};
|
package/types/index.d.ts
CHANGED
|
@@ -60,5 +60,6 @@ import SegmentedControl from './components/SegmentedControl';
|
|
|
60
60
|
import FloatingIsland from './components/FloatingIsland';
|
|
61
61
|
import LocaleProvider from './components/LocaleProvider';
|
|
62
62
|
import FilterTrigger from './components/FilterTrigger';
|
|
63
|
-
|
|
63
|
+
import InlineLoader, { type InlineLoaderProps } from './components/InlineLoader';
|
|
64
|
+
export { theme, getTheme, useTheme, scale, ThemeProvider, ThemeSwitcher, withTheme, swagSystemPalette, swagLightSystemPalette, swagLightJobsSystemPalette, swagDarkSystemPalette, workSystemPalette, jobsSystemPalette, walletSystemPalette, eBensSystemPalette, ehWorkDarkSystemPalette, ehWorkSystemPalette, ehJobsSystemPalette, Accordion, Alert, AppCue, Attachment, Avatar, useAvatarColors, Badge, BottomNavigation, BottomSheet, Box, Button, Calendar, Card, Chart, Carousel, Chip, Collapse, Checkbox, ContentNavigator, DatePicker, Divider, Drawer, Empty, Error, FAB, FlatListWithFAB, Icon, Illustration, type IllustrationName, IllustrationList, Image, HeroDesignProvider, MapPin, List, PinInput, Progress, Portal, PageControl, Skeleton, Slider, Spinner, Swipeable, Radio, Search, SegmentedControl, ScrollViewWithFAB, SectionHeading, SectionListWithFAB, Select, Success, Switch, Tabs, Tag, TextInput, TimePicker, Toast, Toolbar, Typography, Rate, RefreshControl, RichTextEditor, FloatingIsland, LocaleProvider, FilterTrigger, InlineLoader, type InlineLoaderProps, styled, };
|
|
64
65
|
export * from './types';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { GlobalTheme } from '../global';
|
|
2
|
+
declare const getInlineLoaderTheme: (theme: GlobalTheme) => {
|
|
3
|
+
space: {
|
|
4
|
+
gap: number;
|
|
5
|
+
};
|
|
6
|
+
icon: {
|
|
7
|
+
lineHeights: {
|
|
8
|
+
xsmall: number;
|
|
9
|
+
small: number;
|
|
10
|
+
medium: number;
|
|
11
|
+
large: number;
|
|
12
|
+
xlarge: number;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export default getInlineLoaderTheme;
|
|
@@ -54,6 +54,7 @@ import getAppCueTheme from './components/appCue';
|
|
|
54
54
|
import type { ThemeMode } from './global/colors/types';
|
|
55
55
|
import getFilterTriggerTheme from './components/filterTrigger';
|
|
56
56
|
import getSegmentedControlTheme from './components/segmentedControl';
|
|
57
|
+
import getInlineLoaderTheme from './components/inlineLoader';
|
|
57
58
|
type Theme = GlobalTheme & {
|
|
58
59
|
themeMode?: ThemeMode;
|
|
59
60
|
} & {
|
|
@@ -111,6 +112,7 @@ type Theme = GlobalTheme & {
|
|
|
111
112
|
toolbar: ReturnType<typeof getToolbarTheme>;
|
|
112
113
|
typography: ReturnType<typeof getTypographyTheme>;
|
|
113
114
|
floatingIsland: ReturnType<typeof getFloatingIslandTheme>;
|
|
115
|
+
inlineLoader: ReturnType<typeof getInlineLoaderTheme>;
|
|
114
116
|
segmentedControl: ReturnType<typeof getSegmentedControlTheme>;
|
|
115
117
|
};
|
|
116
118
|
};
|