@hero-design/rn 8.26.0 → 8.26.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +2 -1
- package/.turbo/turbo-build.log +1 -1
- package/es/index.js +68 -12
- package/lib/index.js +68 -12
- package/package.json +6 -5
- package/src/components/FAB/ActionGroup/StyledActionGroup.tsx +17 -3
- package/src/components/FAB/ActionGroup/__tests__/__snapshots__/index.spec.tsx.snap +558 -1066
- package/src/components/FAB/ActionGroup/__tests__/index.spec.tsx +9 -15
- package/src/components/FAB/ActionGroup/index.tsx +97 -35
- package/src/theme/__tests__/__snapshots__/index.spec.ts.snap +1 -0
- package/src/theme/components/fab.ts +1 -0
- package/types/components/FAB/ActionGroup/StyledActionGroup.d.ts +7 -1
- package/types/theme/components/fab.d.ts +1 -0
|
@@ -10,7 +10,7 @@ describe('ActionGroup', () => {
|
|
|
10
10
|
${true}
|
|
11
11
|
${false}
|
|
12
12
|
`('has active $active', ({ active }) => {
|
|
13
|
-
const { toJSON,
|
|
13
|
+
const { toJSON, queryByTestId, queryByText } = renderWithTheme(
|
|
14
14
|
<ActionGroup
|
|
15
15
|
fabTitle="Shout out"
|
|
16
16
|
active={active}
|
|
@@ -39,25 +39,19 @@ describe('ActionGroup', () => {
|
|
|
39
39
|
|
|
40
40
|
expect(toJSON()).toMatchSnapshot();
|
|
41
41
|
|
|
42
|
-
expect(
|
|
43
|
-
expect(
|
|
44
|
-
expect(
|
|
45
|
-
expect(
|
|
46
|
-
expect(
|
|
47
|
-
expect(
|
|
42
|
+
expect(queryByText('What would you like to create?')).toBeDefined();
|
|
43
|
+
expect(queryByText('Shout out')).toBeDefined();
|
|
44
|
+
expect(queryByTestId('speaker-action-item')).toBeDefined();
|
|
45
|
+
expect(queryByTestId('target-action-item')).toBeDefined();
|
|
46
|
+
expect(queryByTestId('plane-action-item')).toBeDefined();
|
|
47
|
+
expect(queryByTestId('health-bag-action-item')).toBeDefined();
|
|
48
48
|
|
|
49
49
|
if (active) {
|
|
50
50
|
// verify action group appears
|
|
51
|
-
expect(
|
|
51
|
+
expect(queryByTestId('action-group')).toHaveStyle({
|
|
52
52
|
transform: [{ translateX: 0 }],
|
|
53
53
|
});
|
|
54
|
-
expect(
|
|
55
|
-
} else {
|
|
56
|
-
// verify action group disappears
|
|
57
|
-
expect(getByTestId('action-group')).toHaveStyle({
|
|
58
|
-
transform: [{ translateX: 400 }],
|
|
59
|
-
});
|
|
60
|
-
expect(getByTestId('back-drop')).toHaveProp('pointerEvents', 'box-none');
|
|
54
|
+
expect(queryByTestId('back-drop')).toHaveProp('pointerEvents', 'auto');
|
|
61
55
|
}
|
|
62
56
|
});
|
|
63
57
|
|
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
import React, { useEffect, useRef } from 'react';
|
|
2
|
-
import { Animated, Easing, Platform, View } from 'react-native';
|
|
1
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
2
|
import type { StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
import { Animated, Easing, Modal, Platform, View } from 'react-native';
|
|
4
|
+
import { useTheme } from '../../../theme';
|
|
5
|
+
import type { IconName } from '../../Icon';
|
|
6
|
+
import type { ActionItemProps } from './ActionItem';
|
|
4
7
|
import ActionItem from './ActionItem';
|
|
5
8
|
import {
|
|
9
|
+
StyledActionGroupContainer,
|
|
6
10
|
StyledBackdrop,
|
|
7
11
|
StyledContainer,
|
|
8
12
|
StyledFAB,
|
|
9
13
|
StyledHeaderText,
|
|
10
|
-
|
|
14
|
+
StyledModalView,
|
|
11
15
|
} from './StyledActionGroup';
|
|
12
|
-
import type { IconName } from '../../Icon';
|
|
13
|
-
import type { ActionItemProps } from './ActionItem';
|
|
14
16
|
|
|
15
17
|
type ActionItemsContainerProps = {
|
|
16
18
|
style?: StyleProp<ViewStyle>;
|
|
@@ -74,6 +76,7 @@ export interface ActionGroupProps {
|
|
|
74
76
|
|
|
75
77
|
testID?: string;
|
|
76
78
|
}
|
|
79
|
+
|
|
77
80
|
const ActionGroup = ({
|
|
78
81
|
headerTitle,
|
|
79
82
|
onPress,
|
|
@@ -84,18 +87,48 @@ const ActionGroup = ({
|
|
|
84
87
|
fabTitle,
|
|
85
88
|
fabIcon = 'add',
|
|
86
89
|
}: ActionGroupProps) => {
|
|
90
|
+
const theme = useTheme();
|
|
91
|
+
// Internal state to control the animation of the action group
|
|
92
|
+
const [visible, setVisibility] = useState(active);
|
|
87
93
|
const tranlateXAnimation = useRef<Animated.Value>(
|
|
88
94
|
new Animated.Value(active ? 1 : 0)
|
|
89
95
|
);
|
|
96
|
+
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
if (active && !visible) {
|
|
99
|
+
setVisibility(true);
|
|
100
|
+
}
|
|
101
|
+
}, [active]);
|
|
102
|
+
|
|
90
103
|
useEffect(() => {
|
|
104
|
+
if (active) {
|
|
105
|
+
const animation = Animated.timing(tranlateXAnimation.current, {
|
|
106
|
+
toValue: 1,
|
|
107
|
+
useNativeDriver: Platform.OS === 'ios' || Platform.OS === 'android',
|
|
108
|
+
easing: Easing.inOut(Easing.cubic),
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
animation.start();
|
|
112
|
+
}
|
|
113
|
+
}, [active]);
|
|
114
|
+
|
|
115
|
+
// Make sure the animation finishes running before closing the modal
|
|
116
|
+
const onInternalFABPress = useCallback(() => {
|
|
117
|
+
if (!onPress) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
91
121
|
const animation = Animated.timing(tranlateXAnimation.current, {
|
|
92
|
-
toValue:
|
|
122
|
+
toValue: 0,
|
|
93
123
|
useNativeDriver: Platform.OS === 'ios' || Platform.OS === 'android',
|
|
94
124
|
easing: Easing.inOut(Easing.cubic),
|
|
95
125
|
});
|
|
96
126
|
|
|
97
|
-
animation.start()
|
|
98
|
-
|
|
127
|
+
animation.start(() => {
|
|
128
|
+
setVisibility(false);
|
|
129
|
+
onPress();
|
|
130
|
+
});
|
|
131
|
+
}, [visible]);
|
|
99
132
|
|
|
100
133
|
const interpolatedTranlateXAnimation = tranlateXAnimation.current.interpolate(
|
|
101
134
|
{
|
|
@@ -117,34 +150,63 @@ const ActionGroup = ({
|
|
|
117
150
|
|
|
118
151
|
return (
|
|
119
152
|
<StyledContainer testID={testID} pointerEvents="box-none" style={style}>
|
|
120
|
-
<
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
<StyledActionGroupContainer
|
|
126
|
-
pointerEvents={active ? 'auto' : 'none'}
|
|
127
|
-
testID="action-group"
|
|
128
|
-
style={{
|
|
129
|
-
opacity: interpolatedActionGroupOpacityAnimation,
|
|
130
|
-
transform: [{ translateX: interpolatedTranlateXAnimation }],
|
|
131
|
-
}}
|
|
153
|
+
<Modal
|
|
154
|
+
visible={visible}
|
|
155
|
+
transparent
|
|
156
|
+
statusBarTranslucent
|
|
157
|
+
animationType="none"
|
|
132
158
|
>
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
159
|
+
<StyledBackdrop
|
|
160
|
+
style={{
|
|
161
|
+
opacity: interpolatedBackdropOpacityAnimation,
|
|
162
|
+
}}
|
|
163
|
+
testID="back-drop"
|
|
164
|
+
pointerEvents={active ? 'auto' : 'box-none'}
|
|
165
|
+
/>
|
|
166
|
+
|
|
167
|
+
<StyledModalView>
|
|
168
|
+
<StyledActionGroupContainer
|
|
169
|
+
pointerEvents={active ? 'auto' : 'none'}
|
|
170
|
+
testID="action-group"
|
|
171
|
+
style={{
|
|
172
|
+
opacity: interpolatedActionGroupOpacityAnimation,
|
|
173
|
+
transform: [{ translateX: interpolatedTranlateXAnimation }],
|
|
174
|
+
}}
|
|
175
|
+
>
|
|
176
|
+
{!!headerTitle && (
|
|
177
|
+
<StyledHeaderText testID="header-text">
|
|
178
|
+
{headerTitle}
|
|
179
|
+
</StyledHeaderText>
|
|
180
|
+
)}
|
|
181
|
+
<ActionItemsListComponent items={items} />
|
|
182
|
+
</StyledActionGroupContainer>
|
|
183
|
+
|
|
184
|
+
{active && (
|
|
185
|
+
<StyledFAB
|
|
186
|
+
testID="fab"
|
|
187
|
+
icon={fabIcon}
|
|
188
|
+
onPress={onInternalFABPress}
|
|
189
|
+
animated
|
|
190
|
+
active={active}
|
|
191
|
+
title={fabTitle}
|
|
192
|
+
style={{
|
|
193
|
+
marginBottom: theme.__hd__.fab.space.internalFABMarginBottom,
|
|
194
|
+
}}
|
|
195
|
+
/>
|
|
196
|
+
)}
|
|
197
|
+
</StyledModalView>
|
|
198
|
+
</Modal>
|
|
199
|
+
|
|
200
|
+
{!active && (
|
|
201
|
+
<StyledFAB
|
|
202
|
+
testID="fab"
|
|
203
|
+
icon={fabIcon}
|
|
204
|
+
onPress={onPress}
|
|
205
|
+
animated
|
|
206
|
+
active={active}
|
|
207
|
+
title={fabTitle}
|
|
208
|
+
/>
|
|
209
|
+
)}
|
|
148
210
|
</StyledContainer>
|
|
149
211
|
);
|
|
150
212
|
};
|
|
@@ -58,6 +58,7 @@ const getFABTheme = (theme: GlobalTheme) => {
|
|
|
58
58
|
headerTextMarginBottom: theme.space.large,
|
|
59
59
|
containerPadding: theme.space.large - theme.space.xsmall,
|
|
60
60
|
titleMarginHorizontal: theme.space.small,
|
|
61
|
+
internalFABMarginBottom: theme.space.large,
|
|
61
62
|
};
|
|
62
63
|
|
|
63
64
|
const radii = {
|
|
@@ -28,4 +28,10 @@ declare const StyledHeaderText: import("@emotion/native").StyledComponent<TextPr
|
|
|
28
28
|
theme?: import("@emotion/react").Theme | undefined;
|
|
29
29
|
as?: import("react").ElementType<any> | undefined;
|
|
30
30
|
}, {}, {}>;
|
|
31
|
-
|
|
31
|
+
declare const StyledModalView: import("@emotion/native").StyledComponent<ViewProps & {
|
|
32
|
+
theme?: import("@emotion/react").Theme | undefined;
|
|
33
|
+
as?: import("react").ElementType<any> | undefined;
|
|
34
|
+
}, {}, {
|
|
35
|
+
ref?: import("react").Ref<View> | undefined;
|
|
36
|
+
}>;
|
|
37
|
+
export { StyledHeaderText, StyledBackdrop, StyledContainer, StyledActionGroupContainer, StyledFAB, StyledModalView, };
|