UNPKG

6.08 kBJavaScriptView Raw
1function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2
3import * as React from 'react';
4import { View, StyleSheet, Animated } from 'react-native';
5import Surface from './Surface';
6import Text from './Typography/Text';
7import Button from './Button';
8import Icon from './Icon';
9import { withTheme } from '../core/theming';
10import shadow from '../styles/shadow';
11const ELEVATION = 1;
12const DEFAULT_MAX_WIDTH = 960;
13
14/**
15 * Banner displays a prominent message and related actions.
16 *
17 * <div class="screenshots">
18 * <img class="medium" src="screenshots/banner.gif" />
19 * </div>
20 *
21 * ## Usage
22 * ```js
23 * import * as React from 'react';
24 * import { Image } from 'react-native';
25 * import { Banner } from 'react-native-paper';
26 *
27 * const MyComponent = () => {
28 * const [visible, setVisible] = React.useState(true);
29 *
30 * return (
31 * <Banner
32 * visible={visible}
33 * actions={[
34 * {
35 * label: 'Fix it',
36 * onPress: () => setVisible(false),
37 * },
38 * {
39 * label: 'Learn more',
40 * onPress: () => setVisible(false),
41 * },
42 * ]}
43 * icon={({size}) => (
44 * <Image
45 * source={{
46 * uri: 'https://avatars3.githubusercontent.com/u/17571969?s=400&v=4',
47 * }}
48 * style={{
49 * width: size,
50 * height: size,
51 * }}
52 * />
53 * )}>
54 * There was a problem processing a transaction on your credit card.
55 * </Banner>
56 * );
57 * };
58 *
59 * export default MyComponent;
60 * ```
61 */
62const Banner = _ref => {
63 let {
64 visible,
65 icon,
66 children,
67 actions,
68 contentStyle,
69 style,
70 theme,
71 onShowAnimationFinished = () => {},
72 onHideAnimationFinished = () => {},
73 ...rest
74 } = _ref;
75 const {
76 current: position
77 } = React.useRef(new Animated.Value(visible ? 1 : 0));
78 const [layout, setLayout] = React.useState({
79 height: 0,
80 measured: false
81 });
82 const {
83 scale
84 } = theme.animation;
85 React.useEffect(() => {
86 if (visible) {
87 // show
88 Animated.timing(position, {
89 duration: 250 * scale,
90 toValue: 1,
91 useNativeDriver: false
92 }).start(onShowAnimationFinished);
93 } else {
94 // hide
95 Animated.timing(position, {
96 duration: 200 * scale,
97 toValue: 0,
98 useNativeDriver: false
99 }).start(onHideAnimationFinished);
100 }
101 }, [visible, position, scale]);
102
103 const handleLayout = _ref2 => {
104 let {
105 nativeEvent
106 } = _ref2;
107 const {
108 height
109 } = nativeEvent.layout;
110 setLayout({
111 height,
112 measured: true
113 });
114 }; // The banner animation has 2 parts:
115 // 1. Blank spacer element which animates its height to move the content
116 // 2. Actual banner which animates its translateY
117 // In initial render, we position everything normally and measure the height of the banner
118 // Once we have the height, we apply the height to the spacer and switch the banner to position: absolute
119 // We need this because we need to move the content below as if banner's height was being animated
120 // However we can't animated banner's height directly as it'll also resize the content inside
121
122
123 const height = Animated.multiply(position, layout.height);
124 const translateY = Animated.multiply(Animated.add(position, -1), layout.height);
125 return /*#__PURE__*/React.createElement(Surface, _extends({}, rest, {
126 style: [styles.container, shadow(ELEVATION), style],
127 theme: theme
128 }), /*#__PURE__*/React.createElement(View, {
129 style: [styles.wrapper, contentStyle]
130 }, /*#__PURE__*/React.createElement(Animated.View, {
131 style: {
132 height
133 }
134 }), /*#__PURE__*/React.createElement(Animated.View, {
135 onLayout: handleLayout,
136 style: [layout.measured || !visible ? // If we have measured banner's height or it's invisible,
137 // Position it absolutely, the layout will be taken care of the spacer
138 [styles.absolute, {
139 transform: [{
140 translateY
141 }]
142 }] : // Otherwise position it normally
143 null, !layout.measured && !visible ? // If we haven't measured banner's height yet and it's invisible,
144 // hide it with opacity: 0 so user doesn't see it
145 {
146 opacity: 0
147 } : null]
148 }, /*#__PURE__*/React.createElement(View, {
149 style: styles.content
150 }, icon ? /*#__PURE__*/React.createElement(View, {
151 style: styles.icon
152 }, /*#__PURE__*/React.createElement(Icon, {
153 source: icon,
154 size: 40
155 })) : null, /*#__PURE__*/React.createElement(Text, {
156 style: [styles.message, {
157 color: theme.colors.text
158 }],
159 accessibilityLiveRegion: visible ? 'polite' : 'none',
160 accessibilityRole: "alert"
161 }, children)), /*#__PURE__*/React.createElement(View, {
162 style: styles.actions
163 }, actions.map((_ref3, i) => {
164 let {
165 label,
166 ...others
167 } = _ref3;
168 return /*#__PURE__*/React.createElement(Button, _extends({
169 key:
170 /* eslint-disable-line react/no-array-index-key */
171 i,
172 compact: true,
173 mode: "text",
174 style: styles.button,
175 color: theme.colors.primary
176 }, others), label);
177 })))));
178};
179
180const styles = StyleSheet.create({
181 container: {
182 elevation: ELEVATION
183 },
184 wrapper: {
185 overflow: 'hidden',
186 alignSelf: 'center',
187 width: '100%',
188 maxWidth: DEFAULT_MAX_WIDTH
189 },
190 absolute: {
191 position: 'absolute',
192 top: 0,
193 width: '100%'
194 },
195 content: {
196 flexDirection: 'row',
197 justifyContent: 'flex-start',
198 marginHorizontal: 8,
199 marginTop: 16,
200 marginBottom: 0
201 },
202 icon: {
203 margin: 8
204 },
205 message: {
206 flex: 1,
207 margin: 8
208 },
209 actions: {
210 flexDirection: 'row',
211 justifyContent: 'flex-end',
212 margin: 4
213 },
214 button: {
215 margin: 4
216 }
217});
218export default withTheme(Banner);
219//# sourceMappingURL=Banner.js.map
\No newline at end of file