@hero-design/rn 8.120.2 → 8.121.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 +12 -0
- package/es/index.js +281 -32
- package/jest.config.js +1 -1
- package/lib/index.js +280 -30
- package/package.json +5 -1
- package/rollup.config.mjs +2 -0
- package/src/components/Icon/AnimatedIcon.tsx +7 -40
- package/src/components/Icon/GradientIcon/index.tsx +71 -0
- package/src/components/Icon/SpinWrapper.tsx +39 -0
- package/src/components/Icon/index.tsx +22 -1
- package/src/components/Toolbar/StyledToolbar.tsx +2 -2
- package/src/components/Typography/Body/StyledBody.tsx +2 -2
- package/src/components/Typography/Body/index.tsx +20 -2
- package/src/components/Typography/Caption/StyledCaption.tsx +2 -2
- package/src/components/Typography/Caption/index.tsx +20 -2
- package/src/components/Typography/GradientText/index.tsx +85 -0
- package/src/components/Typography/Label/StyledLabel.tsx +2 -2
- package/src/components/Typography/Label/index.tsx +20 -2
- package/src/components/Typography/Title/StyledTitle.tsx +2 -2
- package/src/components/Typography/Title/index.tsx +32 -13
- package/src/components/Typography/types.ts +3 -1
- package/src/components/Typography/utils.ts +31 -0
- package/src/theme/global/colors/gradients.ts +78 -0
- package/src/theme/global/colors/types.ts +22 -0
- package/src/theme/global/index.ts +5 -2
- package/testUtils/setup.tsx +34 -0
- package/types/components/Icon/GradientIcon/index.d.ts +12 -0
- package/types/components/Icon/SpinWrapper.d.ts +9 -0
- package/types/components/Icon/index.d.ts +1 -1
- package/types/components/TextInput/StyledTextInput.d.ts +1 -1
- package/types/components/Toolbar/StyledToolbar.d.ts +4 -4
- package/types/components/Typography/Body/StyledBody.d.ts +2 -2
- package/types/components/Typography/Body/index.d.ts +1 -1
- package/types/components/Typography/Caption/StyledCaption.d.ts +2 -2
- package/types/components/Typography/Caption/index.d.ts +1 -1
- package/types/components/Typography/GradientText/index.d.ts +7 -0
- package/types/components/Typography/Label/StyledLabel.d.ts +2 -2
- package/types/components/Typography/Label/index.d.ts +1 -1
- package/types/components/Typography/Title/StyledTitle.d.ts +2 -2
- package/types/components/Typography/Title/index.d.ts +1 -1
- package/types/components/Typography/types.d.ts +2 -1
- package/types/components/Typography/utils.d.ts +2 -0
- package/types/theme/global/colors/gradients.d.ts +3 -0
- package/types/theme/global/colors/types.d.ts +21 -0
- package/types/theme/global/index.d.ts +3 -2
|
@@ -3,6 +3,8 @@ import type { AccessibilityProps, StyleProp, TextStyle } from 'react-native';
|
|
|
3
3
|
import IconList from './IconList';
|
|
4
4
|
import HeroIcon from './HeroIcon';
|
|
5
5
|
import AnimatedIcon from './AnimatedIcon';
|
|
6
|
+
import GradientIcon from './GradientIcon';
|
|
7
|
+
import SpinWrapper from './SpinWrapper';
|
|
6
8
|
import { useDeprecation } from '../../utils/hooks';
|
|
7
9
|
|
|
8
10
|
export type IconName = typeof IconList[number];
|
|
@@ -28,7 +30,8 @@ export interface IconProps extends AccessibilityProps {
|
|
|
28
30
|
| 'disabled-text'
|
|
29
31
|
| 'text-inverted'
|
|
30
32
|
| 'muted'
|
|
31
|
-
| 'inactive'
|
|
33
|
+
| 'inactive'
|
|
34
|
+
| 'ai';
|
|
32
35
|
/**
|
|
33
36
|
* Size of the Icon.
|
|
34
37
|
*/
|
|
@@ -87,6 +90,24 @@ const Icon = ({
|
|
|
87
90
|
accessibilityActions,
|
|
88
91
|
};
|
|
89
92
|
|
|
93
|
+
if (intent === 'ai') {
|
|
94
|
+
const gradientIcon = (
|
|
95
|
+
<GradientIcon
|
|
96
|
+
name={icon}
|
|
97
|
+
themeSize={size}
|
|
98
|
+
testID={testID}
|
|
99
|
+
style={spin ? undefined : style}
|
|
100
|
+
{...accessibilityProps}
|
|
101
|
+
/>
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
if (spin) {
|
|
105
|
+
return <SpinWrapper style={style}>{gradientIcon}</SpinWrapper>;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return gradientIcon;
|
|
109
|
+
}
|
|
110
|
+
|
|
90
111
|
return spin ? (
|
|
91
112
|
<AnimatedIcon
|
|
92
113
|
name={icon}
|
|
@@ -2,7 +2,7 @@ import styled from '@emotion/native';
|
|
|
2
2
|
import { TextInput, TouchableOpacity, View } from 'react-native';
|
|
3
3
|
import type { ViewProps } from 'react-native';
|
|
4
4
|
import Typography from '../Typography';
|
|
5
|
-
import type {
|
|
5
|
+
import type { TypographyColorIntent } from '../Typography/types';
|
|
6
6
|
|
|
7
7
|
export type ToolbarMessageState =
|
|
8
8
|
| 'default'
|
|
@@ -63,7 +63,7 @@ const IconButtonLabel = styled(Typography.Body)(({ theme }) => ({
|
|
|
63
63
|
}));
|
|
64
64
|
|
|
65
65
|
const StyledLabel = styled(Typography.Body)<{
|
|
66
|
-
intent:
|
|
66
|
+
intent: TypographyColorIntent;
|
|
67
67
|
}>(({ theme, intent }) => ({
|
|
68
68
|
color:
|
|
69
69
|
intent === 'secondary'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import styled from '@emotion/native';
|
|
2
2
|
import { Text } from 'react-native';
|
|
3
|
-
import type {
|
|
3
|
+
import type { TypographyColorIntent } from '../types';
|
|
4
4
|
|
|
5
5
|
type ThemeVariant = 'small' | 'small-bold' | 'regular' | 'regular-bold';
|
|
6
6
|
type FontWeights = 'regular' | 'semiBold';
|
|
@@ -20,7 +20,7 @@ const FONTSIZE_MAP: Record<ThemeVariant, FontSizes> = {
|
|
|
20
20
|
'small-bold': 'small',
|
|
21
21
|
} as const;
|
|
22
22
|
const StyledBody = styled(Text)<{
|
|
23
|
-
themeIntent:
|
|
23
|
+
themeIntent: TypographyColorIntent;
|
|
24
24
|
themeTypeface: 'neutral' | 'playful';
|
|
25
25
|
themeVariant: ThemeVariant;
|
|
26
26
|
themeIsItalic?: boolean;
|
|
@@ -6,7 +6,9 @@ import type {
|
|
|
6
6
|
TextStyle,
|
|
7
7
|
} from 'react-native';
|
|
8
8
|
import type { TypographyIntent } from '../types';
|
|
9
|
+
import { pickAccessibilityProps } from '../utils';
|
|
9
10
|
import { StyledBody } from './StyledBody';
|
|
11
|
+
import GradientText from '../GradientText';
|
|
10
12
|
|
|
11
13
|
export interface BodyProps extends NativeTextProps {
|
|
12
14
|
/**
|
|
@@ -48,20 +50,36 @@ const Body = ({
|
|
|
48
50
|
typeface = 'neutral',
|
|
49
51
|
variant = 'regular',
|
|
50
52
|
fontStyle = 'normal',
|
|
53
|
+
style,
|
|
54
|
+
testID,
|
|
51
55
|
...nativeProps
|
|
52
56
|
}: BodyProps) => {
|
|
53
|
-
|
|
57
|
+
const isAi = intent === 'ai';
|
|
58
|
+
|
|
59
|
+
const styledText = (
|
|
54
60
|
<StyledBody
|
|
55
61
|
{...nativeProps}
|
|
56
62
|
themeTypeface={typeface}
|
|
57
|
-
themeIntent={intent}
|
|
63
|
+
themeIntent={isAi ? 'body' : intent}
|
|
58
64
|
themeVariant={variant}
|
|
59
65
|
themeIsItalic={fontStyle === 'italic'}
|
|
60
66
|
allowFontScaling={allowFontScaling}
|
|
67
|
+
style={style}
|
|
68
|
+
testID={testID}
|
|
61
69
|
>
|
|
62
70
|
{children}
|
|
63
71
|
</StyledBody>
|
|
64
72
|
);
|
|
73
|
+
|
|
74
|
+
if (isAi) {
|
|
75
|
+
return (
|
|
76
|
+
<GradientText {...pickAccessibilityProps(nativeProps)}>
|
|
77
|
+
{styledText}
|
|
78
|
+
</GradientText>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return styledText;
|
|
65
83
|
};
|
|
66
84
|
|
|
67
85
|
export default Body;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Text } from 'react-native';
|
|
2
2
|
import styled from '@emotion/native';
|
|
3
|
-
import type {
|
|
3
|
+
import type { TypographyColorIntent } from '../types';
|
|
4
4
|
import { FONTWEIGHT_MAP } from '../types';
|
|
5
5
|
|
|
6
6
|
const StyledCaption = styled(Text)<{
|
|
7
7
|
themeFontWeight: 'regular' | 'semi-bold';
|
|
8
|
-
themeIntent:
|
|
8
|
+
themeIntent: TypographyColorIntent;
|
|
9
9
|
themeIsItalic?: boolean;
|
|
10
10
|
}>(({ themeFontWeight, themeIntent, theme, themeIsItalic }) => {
|
|
11
11
|
const baseFontWeight = FONTWEIGHT_MAP[themeFontWeight];
|
|
@@ -7,6 +7,8 @@ import type {
|
|
|
7
7
|
} from 'react-native';
|
|
8
8
|
import { StyledCaption } from './StyledCaption';
|
|
9
9
|
import type { TypographyIntent } from '../types';
|
|
10
|
+
import { pickAccessibilityProps } from '../utils';
|
|
11
|
+
import GradientText from '../GradientText';
|
|
10
12
|
|
|
11
13
|
export interface CaptionProps extends NativeTextProps {
|
|
12
14
|
/**
|
|
@@ -41,19 +43,35 @@ const Caption = ({
|
|
|
41
43
|
intent = 'body',
|
|
42
44
|
allowFontScaling = false,
|
|
43
45
|
fontStyle = 'normal',
|
|
46
|
+
style,
|
|
47
|
+
testID,
|
|
44
48
|
...nativeProps
|
|
45
49
|
}: CaptionProps) => {
|
|
46
|
-
|
|
50
|
+
const isAi = intent === 'ai';
|
|
51
|
+
|
|
52
|
+
const styledText = (
|
|
47
53
|
<StyledCaption
|
|
48
54
|
{...nativeProps}
|
|
49
55
|
themeFontWeight={fontWeight}
|
|
50
|
-
themeIntent={intent}
|
|
56
|
+
themeIntent={isAi ? 'body' : intent}
|
|
51
57
|
themeIsItalic={fontStyle === 'italic'}
|
|
52
58
|
allowFontScaling={allowFontScaling}
|
|
59
|
+
style={style}
|
|
60
|
+
testID={testID}
|
|
53
61
|
>
|
|
54
62
|
{children}
|
|
55
63
|
</StyledCaption>
|
|
56
64
|
);
|
|
65
|
+
|
|
66
|
+
if (isAi) {
|
|
67
|
+
return (
|
|
68
|
+
<GradientText {...pickAccessibilityProps(nativeProps)}>
|
|
69
|
+
{styledText}
|
|
70
|
+
</GradientText>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return styledText;
|
|
57
75
|
};
|
|
58
76
|
|
|
59
77
|
export default Caption;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import MaskedView from '@react-native-masked-view/masked-view';
|
|
2
|
+
import { LinearGradient } from 'expo-linear-gradient';
|
|
3
|
+
import React, { useCallback, useState } from 'react';
|
|
4
|
+
import type { AccessibilityProps, LayoutChangeEvent } from 'react-native';
|
|
5
|
+
import { View } from 'react-native';
|
|
6
|
+
|
|
7
|
+
import { useTheme } from '../../../theme';
|
|
8
|
+
|
|
9
|
+
interface GradientTextProps extends AccessibilityProps {
|
|
10
|
+
children: React.ReactNode;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const GradientText = ({
|
|
14
|
+
children,
|
|
15
|
+
...accessibilityProps
|
|
16
|
+
}: GradientTextProps) => {
|
|
17
|
+
const theme = useTheme();
|
|
18
|
+
const gradient = theme.colors.gradients.aiDiagonal;
|
|
19
|
+
const [size, setSize] = useState<{ width: number; height: number } | null>(
|
|
20
|
+
null
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const onLayout = useCallback((event: LayoutChangeEvent) => {
|
|
24
|
+
const { width, height } = event.nativeEvent.layout;
|
|
25
|
+
setSize((prevSize) => {
|
|
26
|
+
if (prevSize && prevSize.width === width && prevSize.height === height) {
|
|
27
|
+
return prevSize;
|
|
28
|
+
}
|
|
29
|
+
return { width, height };
|
|
30
|
+
});
|
|
31
|
+
}, []);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<View {...accessibilityProps}>
|
|
35
|
+
{/*
|
|
36
|
+
* MaskedView is entirely hidden from the accessibility tree.
|
|
37
|
+
* It is purely visual: the mask defines the text clip shape and
|
|
38
|
+
* the content shows either a gradient or a visible fallback.
|
|
39
|
+
* Screen readers must not traverse into either branch here.
|
|
40
|
+
*/}
|
|
41
|
+
<MaskedView
|
|
42
|
+
style={{ alignSelf: 'stretch' }}
|
|
43
|
+
accessibilityElementsHidden
|
|
44
|
+
importantForAccessibility="no-hide-descendants"
|
|
45
|
+
maskElement={
|
|
46
|
+
<View
|
|
47
|
+
onLayout={onLayout}
|
|
48
|
+
style={{ backgroundColor: 'transparent' }}
|
|
49
|
+
testID="gradient-text-mask"
|
|
50
|
+
>
|
|
51
|
+
{children}
|
|
52
|
+
</View>
|
|
53
|
+
}
|
|
54
|
+
>
|
|
55
|
+
{size ? (
|
|
56
|
+
<LinearGradient
|
|
57
|
+
start={gradient.start}
|
|
58
|
+
end={gradient.end}
|
|
59
|
+
colors={gradient.colors}
|
|
60
|
+
locations={gradient.locations}
|
|
61
|
+
style={{ width: size.width, height: size.height }}
|
|
62
|
+
/>
|
|
63
|
+
) : (
|
|
64
|
+
// Render children as fallback until layout is measured, so text
|
|
65
|
+
// is visible immediately rather than blank on the first frame.
|
|
66
|
+
children
|
|
67
|
+
)}
|
|
68
|
+
</MaskedView>
|
|
69
|
+
{/*
|
|
70
|
+
* Visually hidden but accessible: the single Text node that
|
|
71
|
+
* screen readers announce. Kept outside MaskedView so it is
|
|
72
|
+
* always present and never duplicated.
|
|
73
|
+
*/}
|
|
74
|
+
<View
|
|
75
|
+
style={{ position: 'absolute', opacity: 0 }}
|
|
76
|
+
importantForAccessibility="yes"
|
|
77
|
+
accessibilityElementsHidden={false}
|
|
78
|
+
>
|
|
79
|
+
{children}
|
|
80
|
+
</View>
|
|
81
|
+
</View>
|
|
82
|
+
);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export default GradientText;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import styled from '@emotion/native';
|
|
2
2
|
import { Text } from 'react-native';
|
|
3
|
-
import type {
|
|
3
|
+
import type { TypographyColorIntent } from '../types';
|
|
4
4
|
|
|
5
5
|
const StyledLabel = styled(Text)<{
|
|
6
|
-
themeIntent:
|
|
6
|
+
themeIntent: TypographyColorIntent;
|
|
7
7
|
themeIsItalic?: boolean;
|
|
8
8
|
}>(({ themeIntent, theme, themeIsItalic }) => {
|
|
9
9
|
// For Label, we assume 'regular' weight for base font family
|
|
@@ -7,6 +7,8 @@ import type {
|
|
|
7
7
|
} from 'react-native';
|
|
8
8
|
import { StyledLabel } from './StyledLabel';
|
|
9
9
|
import type { TypographyIntent } from '../types';
|
|
10
|
+
import { pickAccessibilityProps } from '../utils';
|
|
11
|
+
import GradientText from '../GradientText';
|
|
10
12
|
|
|
11
13
|
export interface LabelProps extends NativeTextProps {
|
|
12
14
|
/**
|
|
@@ -36,18 +38,34 @@ const Label = ({
|
|
|
36
38
|
intent = 'body',
|
|
37
39
|
allowFontScaling = false,
|
|
38
40
|
fontStyle = 'normal',
|
|
41
|
+
style,
|
|
42
|
+
testID,
|
|
39
43
|
...nativeProps
|
|
40
44
|
}: LabelProps) => {
|
|
41
|
-
|
|
45
|
+
const isAi = intent === 'ai';
|
|
46
|
+
|
|
47
|
+
const styledText = (
|
|
42
48
|
<StyledLabel
|
|
43
49
|
{...nativeProps}
|
|
44
|
-
themeIntent={intent}
|
|
50
|
+
themeIntent={isAi ? 'body' : intent}
|
|
45
51
|
themeIsItalic={fontStyle === 'italic'}
|
|
46
52
|
allowFontScaling={allowFontScaling}
|
|
53
|
+
style={style}
|
|
54
|
+
testID={testID}
|
|
47
55
|
>
|
|
48
56
|
{children}
|
|
49
57
|
</StyledLabel>
|
|
50
58
|
);
|
|
59
|
+
|
|
60
|
+
if (isAi) {
|
|
61
|
+
return (
|
|
62
|
+
<GradientText {...pickAccessibilityProps(nativeProps)}>
|
|
63
|
+
{styledText}
|
|
64
|
+
</GradientText>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return styledText;
|
|
51
69
|
};
|
|
52
70
|
|
|
53
71
|
export default Label;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import styled from '@emotion/native';
|
|
2
2
|
import { Text } from 'react-native';
|
|
3
|
-
import type {
|
|
3
|
+
import type { TypographyColorIntent } from '../types';
|
|
4
4
|
|
|
5
5
|
type ThemeLevel = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
|
|
6
6
|
|
|
7
7
|
const StyledTitle = styled(Text)<{
|
|
8
|
-
themeIntent:
|
|
8
|
+
themeIntent: TypographyColorIntent;
|
|
9
9
|
themeLevel: ThemeLevel;
|
|
10
10
|
themeTypeface: 'neutral' | 'playful';
|
|
11
11
|
themeIsItalic?: boolean;
|
|
@@ -6,7 +6,9 @@ import type {
|
|
|
6
6
|
TextStyle,
|
|
7
7
|
} from 'react-native';
|
|
8
8
|
import type { TypographyIntent } from '../types';
|
|
9
|
+
import { pickAccessibilityProps } from '../utils';
|
|
9
10
|
import { StyledTitle } from './StyledTitle';
|
|
11
|
+
import GradientText from '../GradientText';
|
|
10
12
|
|
|
11
13
|
export interface TitleProps extends NativeTextProps {
|
|
12
14
|
/**
|
|
@@ -31,7 +33,6 @@ export interface TitleProps extends NativeTextProps {
|
|
|
31
33
|
* - `playful`: To visualise a playful content.
|
|
32
34
|
*/
|
|
33
35
|
typeface?: 'neutral' | 'playful';
|
|
34
|
-
|
|
35
36
|
/**
|
|
36
37
|
* The level of Title including h1, h2, h3, h4, h5 and h6.
|
|
37
38
|
*/
|
|
@@ -49,18 +50,36 @@ const Title = ({
|
|
|
49
50
|
level = 'h1',
|
|
50
51
|
typeface = 'neutral',
|
|
51
52
|
fontStyle = 'normal',
|
|
53
|
+
style,
|
|
54
|
+
testID,
|
|
52
55
|
...nativeProps
|
|
53
|
-
}: TitleProps) =>
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
56
|
+
}: TitleProps) => {
|
|
57
|
+
const isAi = intent === 'ai';
|
|
58
|
+
|
|
59
|
+
const styledText = (
|
|
60
|
+
<StyledTitle
|
|
61
|
+
{...nativeProps}
|
|
62
|
+
themeLevel={level}
|
|
63
|
+
themeTypeface={typeface}
|
|
64
|
+
themeIntent={isAi ? 'body' : intent}
|
|
65
|
+
themeIsItalic={fontStyle === 'italic'}
|
|
66
|
+
allowFontScaling={allowFontScaling}
|
|
67
|
+
style={style}
|
|
68
|
+
testID={testID}
|
|
69
|
+
>
|
|
70
|
+
{children}
|
|
71
|
+
</StyledTitle>
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (isAi) {
|
|
75
|
+
return (
|
|
76
|
+
<GradientText {...pickAccessibilityProps(nativeProps)}>
|
|
77
|
+
{styledText}
|
|
78
|
+
</GradientText>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return styledText;
|
|
83
|
+
};
|
|
65
84
|
|
|
66
85
|
export default Title;
|
|
@@ -4,7 +4,7 @@ export const FONTWEIGHT_MAP = {
|
|
|
4
4
|
'semi-bold': 'semiBold',
|
|
5
5
|
} as const;
|
|
6
6
|
|
|
7
|
-
export type
|
|
7
|
+
export type TypographyColorIntent =
|
|
8
8
|
| 'body'
|
|
9
9
|
| 'subdued'
|
|
10
10
|
| 'primary'
|
|
@@ -18,3 +18,5 @@ export type TypographyIntent =
|
|
|
18
18
|
| 'disabled'
|
|
19
19
|
| 'muted'
|
|
20
20
|
| 'inactive';
|
|
21
|
+
|
|
22
|
+
export type TypographyIntent = TypographyColorIntent | 'ai';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { AccessibilityProps, TextProps } from 'react-native';
|
|
2
|
+
|
|
3
|
+
const ACCESSIBILITY_KEYS: ReadonlyArray<keyof AccessibilityProps> = [
|
|
4
|
+
'accessible',
|
|
5
|
+
'accessibilityActions',
|
|
6
|
+
'accessibilityLabel',
|
|
7
|
+
'accessibilityRole',
|
|
8
|
+
'accessibilityState',
|
|
9
|
+
'accessibilityHint',
|
|
10
|
+
'accessibilityValue',
|
|
11
|
+
'onAccessibilityAction',
|
|
12
|
+
// Android
|
|
13
|
+
'accessibilityLabelledBy',
|
|
14
|
+
'accessibilityLiveRegion',
|
|
15
|
+
'importantForAccessibility',
|
|
16
|
+
// iOS
|
|
17
|
+
'accessibilityElementsHidden',
|
|
18
|
+
'accessibilityLanguage',
|
|
19
|
+
'accessibilityIgnoresInvertColors',
|
|
20
|
+
'accessibilityViewIsModal',
|
|
21
|
+
'onAccessibilityEscape',
|
|
22
|
+
'onAccessibilityTap',
|
|
23
|
+
'onMagicTap',
|
|
24
|
+
'accessibilityIgnoresInvertColors',
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
export const pickAccessibilityProps = (props: TextProps): AccessibilityProps =>
|
|
28
|
+
ACCESSIBILITY_KEYS.filter((key) => key in props).reduce<AccessibilityProps>(
|
|
29
|
+
(acc, key) => Object.assign(acc, { [key]: props[key] }),
|
|
30
|
+
{}
|
|
31
|
+
);
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { mobileVisualisationPalette } from '@hero-design/colors';
|
|
2
|
+
|
|
3
|
+
import type { GradientPoint, Gradients, SystemPalette } from './types';
|
|
4
|
+
|
|
5
|
+
const DIAGONAL_ANGLE = 282;
|
|
6
|
+
const HORIZONTAL_ANGLE = 90;
|
|
7
|
+
|
|
8
|
+
const DIAGONAL_LOCATIONS = [0, 0.2931, 0.993] as const;
|
|
9
|
+
const HORIZONTAL_LOCATIONS = [0, 0.25, 0.75] as const;
|
|
10
|
+
|
|
11
|
+
// Convert CSS gradient angle (degrees) to expo-linear-gradient start/end points.
|
|
12
|
+
// Follows CSS convention: 0° = bottom→top, 90° = left→right,
|
|
13
|
+
// 180° = top→bottom, 270° = right→left.
|
|
14
|
+
const angleToPoints = (
|
|
15
|
+
angleDeg: number
|
|
16
|
+
): { start: GradientPoint; end: GradientPoint } => {
|
|
17
|
+
const rad = ((angleDeg - 90) * Math.PI) / 180;
|
|
18
|
+
const x = Math.cos(rad);
|
|
19
|
+
const y = Math.sin(rad);
|
|
20
|
+
// Normalize so both components are in [0, 1]
|
|
21
|
+
const start = {
|
|
22
|
+
x: Math.round(((1 - x) / 2) * 100) / 100,
|
|
23
|
+
y: Math.round(((1 - y) / 2) * 100) / 100,
|
|
24
|
+
};
|
|
25
|
+
const end = {
|
|
26
|
+
x: Math.round(((1 + x) / 2) * 100) / 100,
|
|
27
|
+
y: Math.round(((1 + y) / 2) * 100) / 100,
|
|
28
|
+
};
|
|
29
|
+
return { start, end };
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const getGradients = (systemPalette: SystemPalette): Gradients => {
|
|
33
|
+
const { blueMedium } = mobileVisualisationPalette;
|
|
34
|
+
const { pinkMedium } = mobileVisualisationPalette;
|
|
35
|
+
const brandPrimary = systemPalette.primary;
|
|
36
|
+
const backgroundFallback = systemPalette.defaultGlobalSurface;
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
aiDiagonal: {
|
|
40
|
+
angle: DIAGONAL_ANGLE,
|
|
41
|
+
...angleToPoints(DIAGONAL_ANGLE),
|
|
42
|
+
colors: [blueMedium, brandPrimary, pinkMedium],
|
|
43
|
+
locations: DIAGONAL_LOCATIONS,
|
|
44
|
+
},
|
|
45
|
+
aiDiagonal8: {
|
|
46
|
+
angle: DIAGONAL_ANGLE,
|
|
47
|
+
...angleToPoints(DIAGONAL_ANGLE),
|
|
48
|
+
colors: [blueMedium, brandPrimary, pinkMedium],
|
|
49
|
+
locations: DIAGONAL_LOCATIONS,
|
|
50
|
+
opacity: 0.08,
|
|
51
|
+
backgroundFallback,
|
|
52
|
+
},
|
|
53
|
+
aiDiagonal16: {
|
|
54
|
+
angle: DIAGONAL_ANGLE,
|
|
55
|
+
...angleToPoints(DIAGONAL_ANGLE),
|
|
56
|
+
colors: [blueMedium, brandPrimary, pinkMedium],
|
|
57
|
+
locations: DIAGONAL_LOCATIONS,
|
|
58
|
+
opacity: 0.16,
|
|
59
|
+
backgroundFallback,
|
|
60
|
+
},
|
|
61
|
+
aiDiagonal24: {
|
|
62
|
+
angle: DIAGONAL_ANGLE,
|
|
63
|
+
...angleToPoints(DIAGONAL_ANGLE),
|
|
64
|
+
colors: [blueMedium, brandPrimary, pinkMedium],
|
|
65
|
+
locations: DIAGONAL_LOCATIONS,
|
|
66
|
+
opacity: 0.24,
|
|
67
|
+
backgroundFallback,
|
|
68
|
+
},
|
|
69
|
+
aiHorizontal: {
|
|
70
|
+
angle: HORIZONTAL_ANGLE,
|
|
71
|
+
...angleToPoints(HORIZONTAL_ANGLE),
|
|
72
|
+
colors: [brandPrimary, pinkMedium, brandPrimary],
|
|
73
|
+
locations: HORIZONTAL_LOCATIONS,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export default getGradients;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { ColorValue } from 'react-native';
|
|
2
|
+
|
|
1
3
|
export type GlobalSystemPalette = {
|
|
2
4
|
// Updated 14 / Nov / 22
|
|
3
5
|
// Surfaces
|
|
@@ -64,6 +66,26 @@ export type BrandSystemPalette = {
|
|
|
64
66
|
decorativeSecondarySurface?: string;
|
|
65
67
|
};
|
|
66
68
|
|
|
69
|
+
export type GradientPoint = { x: number; y: number };
|
|
70
|
+
|
|
71
|
+
export type GradientToken = {
|
|
72
|
+
angle: number;
|
|
73
|
+
start: GradientPoint;
|
|
74
|
+
end: GradientPoint;
|
|
75
|
+
colors: readonly [ColorValue, ColorValue, ...ColorValue[]];
|
|
76
|
+
locations: readonly [number, number, ...number[]];
|
|
77
|
+
opacity?: number;
|
|
78
|
+
backgroundFallback?: string;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export type Gradients = {
|
|
82
|
+
aiDiagonal: GradientToken;
|
|
83
|
+
aiDiagonal8: GradientToken;
|
|
84
|
+
aiDiagonal16: GradientToken;
|
|
85
|
+
aiDiagonal24: GradientToken;
|
|
86
|
+
aiHorizontal: GradientToken;
|
|
87
|
+
};
|
|
88
|
+
|
|
67
89
|
export type ThemeMode = 'light' | 'dark';
|
|
68
90
|
type ThemeName =
|
|
69
91
|
| 'swagLight'
|
|
@@ -16,8 +16,9 @@ import { getSpace } from './space';
|
|
|
16
16
|
import { getSizes } from './sizes';
|
|
17
17
|
import { getBorderWidths, getRadii } from './borders';
|
|
18
18
|
import type { Scale } from './scale';
|
|
19
|
-
import type { SystemPalette } from './colors/types';
|
|
19
|
+
import type { GradientToken, Gradients, SystemPalette } from './colors/types';
|
|
20
20
|
import { getShadows } from './shadows';
|
|
21
|
+
import getGradients from './colors/gradients';
|
|
21
22
|
|
|
22
23
|
const getGlobalTheme = (scale: Scale, systemPalette: SystemPalette) => {
|
|
23
24
|
const fonts = getFonts(scale.font);
|
|
@@ -28,10 +29,12 @@ const getGlobalTheme = (scale: Scale, systemPalette: SystemPalette) => {
|
|
|
28
29
|
const sizes = getSizes(scale.size);
|
|
29
30
|
const radii = getRadii(scale.radius);
|
|
30
31
|
const shadows = getShadows(systemPalette);
|
|
32
|
+
const gradients = getGradients(systemPalette);
|
|
31
33
|
|
|
32
34
|
return {
|
|
33
35
|
colors: {
|
|
34
36
|
...systemPalette,
|
|
37
|
+
gradients,
|
|
35
38
|
},
|
|
36
39
|
fonts,
|
|
37
40
|
fontSizes,
|
|
@@ -46,7 +49,7 @@ const getGlobalTheme = (scale: Scale, systemPalette: SystemPalette) => {
|
|
|
46
49
|
|
|
47
50
|
type GlobalTheme = ReturnType<typeof getGlobalTheme>;
|
|
48
51
|
|
|
49
|
-
export type { GlobalTheme, Scale, SystemPalette };
|
|
52
|
+
export type { GlobalTheme, GradientToken, Gradients, Scale, SystemPalette };
|
|
50
53
|
|
|
51
54
|
export {
|
|
52
55
|
getGlobalTheme,
|
package/testUtils/setup.tsx
CHANGED
|
@@ -110,6 +110,40 @@ jest.mock('react-native-webview', () => {
|
|
|
110
110
|
};
|
|
111
111
|
});
|
|
112
112
|
|
|
113
|
+
jest.mock('@react-native-masked-view/masked-view', () => {
|
|
114
|
+
const React = jest.requireActual('react');
|
|
115
|
+
const { View } = jest.requireActual('react-native');
|
|
116
|
+
|
|
117
|
+
const MaskedView = ({
|
|
118
|
+
maskElement,
|
|
119
|
+
children,
|
|
120
|
+
style,
|
|
121
|
+
testID,
|
|
122
|
+
}: {
|
|
123
|
+
maskElement: React.ReactNode;
|
|
124
|
+
children: React.ReactNode;
|
|
125
|
+
style?: object;
|
|
126
|
+
testID?: string;
|
|
127
|
+
}) => React.createElement(View, { style, testID }, maskElement, children);
|
|
128
|
+
|
|
129
|
+
return { __esModule: true, default: MaskedView };
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
jest.mock('expo-linear-gradient', () => {
|
|
133
|
+
const React = jest.requireActual('react');
|
|
134
|
+
const { View } = jest.requireActual('react-native');
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
LinearGradient: ({
|
|
138
|
+
children,
|
|
139
|
+
style,
|
|
140
|
+
}: {
|
|
141
|
+
children?: React.ReactNode;
|
|
142
|
+
style?: object;
|
|
143
|
+
}) => React.createElement(View, { style }, children),
|
|
144
|
+
};
|
|
145
|
+
});
|
|
146
|
+
|
|
113
147
|
jest.mock('hero-editor/dist/app.js', () => ({ default: '' }));
|
|
114
148
|
|
|
115
149
|
jest.mock('react-native-gesture-handler', () => {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { AccessibilityProps, StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
import type { IconName } from '..';
|
|
4
|
+
type GradientIconSize = 'xxxsmall' | 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge';
|
|
5
|
+
interface GradientIconProps extends AccessibilityProps {
|
|
6
|
+
name: IconName;
|
|
7
|
+
themeSize: GradientIconSize;
|
|
8
|
+
testID?: string;
|
|
9
|
+
style?: StyleProp<ViewStyle>;
|
|
10
|
+
}
|
|
11
|
+
declare const GradientIcon: ({ name, themeSize, testID, style, ...accessibilityProps }: GradientIconProps) => React.JSX.Element;
|
|
12
|
+
export default GradientIcon;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
export interface SpinWrapperProps {
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
style?: StyleProp<ViewStyle>;
|
|
6
|
+
testID?: string;
|
|
7
|
+
}
|
|
8
|
+
declare const SpinWrapper: ({ children, style, testID }: SpinWrapperProps) => React.JSX.Element;
|
|
9
|
+
export default SpinWrapper;
|
|
@@ -12,7 +12,7 @@ export interface IconProps extends AccessibilityProps {
|
|
|
12
12
|
/**
|
|
13
13
|
* Intent of the Icon.
|
|
14
14
|
*/
|
|
15
|
-
intent?: 'text' | 'primary' | 'secondary' | 'info' | 'danger' | 'success' | 'warning' | 'disabled-text' | 'text-inverted' | 'muted' | 'inactive';
|
|
15
|
+
intent?: 'text' | 'primary' | 'secondary' | 'info' | 'danger' | 'success' | 'warning' | 'disabled-text' | 'text-inverted' | 'muted' | 'inactive' | 'ai';
|
|
16
16
|
/**
|
|
17
17
|
* Size of the Icon.
|
|
18
18
|
*/
|