1 | function _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 |
|
3 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
4 |
|
5 | import Color from 'color';
|
6 | import * as React from 'react';
|
7 | import { Animated, InteractionManager, Platform, StyleSheet, View } from 'react-native';
|
8 | import { forModalPresentationIOS } from '../../TransitionConfigs/CardStyleInterpolators';
|
9 | import CardAnimationContext from '../../utils/CardAnimationContext';
|
10 | import getDistanceForDirection from '../../utils/getDistanceForDirection';
|
11 | import getInvertedMultiplier from '../../utils/getInvertedMultiplier';
|
12 | import memoize from '../../utils/memoize';
|
13 | import { GestureState, PanGestureHandler } from '../GestureHandler';
|
14 | import ModalStatusBarManager from '../ModalStatusBarManager';
|
15 | import CardSheet from './CardSheet';
|
16 | const GESTURE_VELOCITY_IMPACT = 0.3;
|
17 | const TRUE = 1;
|
18 | const FALSE = 0;
|
19 |
|
20 |
|
21 |
|
22 |
|
23 | const GESTURE_RESPONSE_DISTANCE_HORIZONTAL = 50;
|
24 | const GESTURE_RESPONSE_DISTANCE_VERTICAL = 135;
|
25 | const useNativeDriver = Platform.OS !== 'web';
|
26 |
|
27 | const hasOpacityStyle = style => {
|
28 | if (style) {
|
29 | const flattenedStyle = StyleSheet.flatten(style);
|
30 | return flattenedStyle.opacity != null;
|
31 | }
|
32 |
|
33 | return false;
|
34 | };
|
35 |
|
36 | export default class Card extends React.Component {
|
37 | constructor() {
|
38 | super(...arguments);
|
39 |
|
40 | _defineProperty(this, "isCurrentlyMounted", false);
|
41 |
|
42 | _defineProperty(this, "isClosing", new Animated.Value(FALSE));
|
43 |
|
44 | _defineProperty(this, "inverted", new Animated.Value(getInvertedMultiplier(this.props.gestureDirection)));
|
45 |
|
46 | _defineProperty(this, "layout", {
|
47 | width: new Animated.Value(this.props.layout.width),
|
48 | height: new Animated.Value(this.props.layout.height)
|
49 | });
|
50 |
|
51 | _defineProperty(this, "isSwiping", new Animated.Value(FALSE));
|
52 |
|
53 | _defineProperty(this, "interactionHandle", void 0);
|
54 |
|
55 | _defineProperty(this, "pendingGestureCallback", void 0);
|
56 |
|
57 | _defineProperty(this, "lastToValue", void 0);
|
58 |
|
59 | _defineProperty(this, "animate", _ref => {
|
60 | let {
|
61 | closing,
|
62 | velocity
|
63 | } = _ref;
|
64 | const {
|
65 | gesture,
|
66 | transitionSpec,
|
67 | onOpen,
|
68 | onClose,
|
69 | onTransition
|
70 | } = this.props;
|
71 | const toValue = this.getAnimateToValue({ ...this.props,
|
72 | closing
|
73 | });
|
74 | this.lastToValue = toValue;
|
75 | this.isClosing.setValue(closing ? TRUE : FALSE);
|
76 | const spec = closing ? transitionSpec.close : transitionSpec.open;
|
77 | const animation = spec.animation === 'spring' ? Animated.spring : Animated.timing;
|
78 | this.setPointerEventsEnabled(!closing);
|
79 | this.handleStartInteraction();
|
80 | clearTimeout(this.pendingGestureCallback);
|
81 | onTransition === null || onTransition === void 0 ? void 0 : onTransition({
|
82 | closing,
|
83 | gesture: velocity !== undefined
|
84 | });
|
85 | animation(gesture, { ...spec.config,
|
86 | velocity,
|
87 | toValue,
|
88 | useNativeDriver,
|
89 | isInteraction: false
|
90 | }).start(_ref2 => {
|
91 | let {
|
92 | finished
|
93 | } = _ref2;
|
94 | this.handleEndInteraction();
|
95 | clearTimeout(this.pendingGestureCallback);
|
96 |
|
97 | if (finished) {
|
98 | if (closing) {
|
99 | onClose();
|
100 | } else {
|
101 | onOpen();
|
102 | }
|
103 |
|
104 | if (this.isCurrentlyMounted) {
|
105 |
|
106 | this.forceUpdate();
|
107 | }
|
108 | }
|
109 | });
|
110 | });
|
111 |
|
112 | _defineProperty(this, "getAnimateToValue", _ref3 => {
|
113 | let {
|
114 | closing,
|
115 | layout,
|
116 | gestureDirection
|
117 | } = _ref3;
|
118 |
|
119 | if (!closing) {
|
120 | return 0;
|
121 | }
|
122 |
|
123 | return getDistanceForDirection(layout, gestureDirection);
|
124 | });
|
125 |
|
126 | _defineProperty(this, "setPointerEventsEnabled", enabled => {
|
127 | var _this$contentRef$curr;
|
128 |
|
129 | const pointerEvents = enabled ? 'box-none' : 'none';
|
130 | (_this$contentRef$curr = this.contentRef.current) === null || _this$contentRef$curr === void 0 ? void 0 : _this$contentRef$curr.setNativeProps({
|
131 | pointerEvents
|
132 | });
|
133 | });
|
134 |
|
135 | _defineProperty(this, "handleStartInteraction", () => {
|
136 | if (this.interactionHandle === undefined) {
|
137 | this.interactionHandle = InteractionManager.createInteractionHandle();
|
138 | }
|
139 | });
|
140 |
|
141 | _defineProperty(this, "handleEndInteraction", () => {
|
142 | if (this.interactionHandle !== undefined) {
|
143 | InteractionManager.clearInteractionHandle(this.interactionHandle);
|
144 | this.interactionHandle = undefined;
|
145 | }
|
146 | });
|
147 |
|
148 | _defineProperty(this, "handleGestureStateChange", _ref4 => {
|
149 | let {
|
150 | nativeEvent
|
151 | } = _ref4;
|
152 | const {
|
153 | layout,
|
154 | onClose,
|
155 | onGestureBegin,
|
156 | onGestureCanceled,
|
157 | onGestureEnd,
|
158 | gestureDirection,
|
159 | gestureVelocityImpact
|
160 | } = this.props;
|
161 |
|
162 | switch (nativeEvent.state) {
|
163 | case GestureState.ACTIVE:
|
164 | this.isSwiping.setValue(TRUE);
|
165 | this.handleStartInteraction();
|
166 | onGestureBegin === null || onGestureBegin === void 0 ? void 0 : onGestureBegin();
|
167 | break;
|
168 |
|
169 | case GestureState.CANCELLED:
|
170 | {
|
171 | this.isSwiping.setValue(FALSE);
|
172 | this.handleEndInteraction();
|
173 | const velocity = gestureDirection === 'vertical' || gestureDirection === 'vertical-inverted' ? nativeEvent.velocityY : nativeEvent.velocityX;
|
174 | this.animate({
|
175 | closing: this.props.closing,
|
176 | velocity
|
177 | });
|
178 | onGestureCanceled === null || onGestureCanceled === void 0 ? void 0 : onGestureCanceled();
|
179 | break;
|
180 | }
|
181 |
|
182 | case GestureState.END:
|
183 | {
|
184 | this.isSwiping.setValue(FALSE);
|
185 | let distance;
|
186 | let translation;
|
187 | let velocity;
|
188 |
|
189 | if (gestureDirection === 'vertical' || gestureDirection === 'vertical-inverted') {
|
190 | distance = layout.height;
|
191 | translation = nativeEvent.translationY;
|
192 | velocity = nativeEvent.velocityY;
|
193 | } else {
|
194 | distance = layout.width;
|
195 | translation = nativeEvent.translationX;
|
196 | velocity = nativeEvent.velocityX;
|
197 | }
|
198 |
|
199 | const closing = (translation + velocity * gestureVelocityImpact) * getInvertedMultiplier(gestureDirection) > distance / 2 ? velocity !== 0 || translation !== 0 : this.props.closing;
|
200 | this.animate({
|
201 | closing,
|
202 | velocity
|
203 | });
|
204 |
|
205 | if (closing) {
|
206 |
|
207 |
|
208 | this.pendingGestureCallback = setTimeout(() => {
|
209 | onClose();
|
210 |
|
211 |
|
212 | this.forceUpdate();
|
213 | }, 32);
|
214 | }
|
215 |
|
216 | onGestureEnd === null || onGestureEnd === void 0 ? void 0 : onGestureEnd();
|
217 | break;
|
218 | }
|
219 | }
|
220 | });
|
221 |
|
222 | _defineProperty(this, "getInterpolatedStyle", memoize((styleInterpolator, animation) => styleInterpolator(animation)));
|
223 |
|
224 | _defineProperty(this, "getCardAnimation", memoize((interpolationIndex, current, next, layout, insetTop, insetRight, insetBottom, insetLeft) => ({
|
225 | index: interpolationIndex,
|
226 | current: {
|
227 | progress: current
|
228 | },
|
229 | next: next && {
|
230 | progress: next
|
231 | },
|
232 | closing: this.isClosing,
|
233 | swiping: this.isSwiping,
|
234 | inverted: this.inverted,
|
235 | layouts: {
|
236 | screen: layout
|
237 | },
|
238 | insets: {
|
239 | top: insetTop,
|
240 | right: insetRight,
|
241 | bottom: insetBottom,
|
242 | left: insetLeft
|
243 | }
|
244 | })));
|
245 |
|
246 | _defineProperty(this, "contentRef", React.createRef());
|
247 | }
|
248 |
|
249 | componentDidMount() {
|
250 | this.animate({
|
251 | closing: this.props.closing
|
252 | });
|
253 | this.isCurrentlyMounted = true;
|
254 | }
|
255 |
|
256 | componentDidUpdate(prevProps) {
|
257 | const {
|
258 | layout,
|
259 | gestureDirection,
|
260 | closing
|
261 | } = this.props;
|
262 | const {
|
263 | width,
|
264 | height
|
265 | } = layout;
|
266 |
|
267 | if (width !== prevProps.layout.width) {
|
268 | this.layout.width.setValue(width);
|
269 | }
|
270 |
|
271 | if (height !== prevProps.layout.height) {
|
272 | this.layout.height.setValue(height);
|
273 | }
|
274 |
|
275 | if (gestureDirection !== prevProps.gestureDirection) {
|
276 | this.inverted.setValue(getInvertedMultiplier(gestureDirection));
|
277 | }
|
278 |
|
279 | const toValue = this.getAnimateToValue(this.props);
|
280 |
|
281 | if (this.getAnimateToValue(prevProps) !== toValue || this.lastToValue !== toValue) {
|
282 |
|
283 |
|
284 |
|
285 |
|
286 |
|
287 | this.animate({
|
288 | closing
|
289 | });
|
290 | }
|
291 | }
|
292 |
|
293 | componentWillUnmount() {
|
294 | this.props.gesture.stopAnimation();
|
295 | this.isCurrentlyMounted = false;
|
296 | this.handleEndInteraction();
|
297 | }
|
298 |
|
299 | gestureActivationCriteria() {
|
300 | const {
|
301 | layout,
|
302 | gestureDirection,
|
303 | gestureResponseDistance
|
304 | } = this.props;
|
305 | const enableTrackpadTwoFingerGesture = true;
|
306 | const distance = gestureResponseDistance !== undefined ? gestureResponseDistance : gestureDirection === 'vertical' || gestureDirection === 'vertical-inverted' ? GESTURE_RESPONSE_DISTANCE_VERTICAL : GESTURE_RESPONSE_DISTANCE_HORIZONTAL;
|
307 |
|
308 | if (gestureDirection === 'vertical') {
|
309 | return {
|
310 | maxDeltaX: 15,
|
311 | minOffsetY: 5,
|
312 | hitSlop: {
|
313 | bottom: -layout.height + distance
|
314 | },
|
315 | enableTrackpadTwoFingerGesture
|
316 | };
|
317 | } else if (gestureDirection === 'vertical-inverted') {
|
318 | return {
|
319 | maxDeltaX: 15,
|
320 | minOffsetY: -5,
|
321 | hitSlop: {
|
322 | top: -layout.height + distance
|
323 | },
|
324 | enableTrackpadTwoFingerGesture
|
325 | };
|
326 | } else {
|
327 | const hitSlop = -layout.width + distance;
|
328 | const invertedMultiplier = getInvertedMultiplier(gestureDirection);
|
329 |
|
330 | if (invertedMultiplier === 1) {
|
331 | return {
|
332 | minOffsetX: 5,
|
333 | maxDeltaY: 20,
|
334 | hitSlop: {
|
335 | right: hitSlop
|
336 | },
|
337 | enableTrackpadTwoFingerGesture
|
338 | };
|
339 | } else {
|
340 | return {
|
341 | minOffsetX: -5,
|
342 | maxDeltaY: 20,
|
343 | hitSlop: {
|
344 | left: hitSlop
|
345 | },
|
346 | enableTrackpadTwoFingerGesture
|
347 | };
|
348 | }
|
349 | }
|
350 | }
|
351 |
|
352 | render() {
|
353 | const {
|
354 | styleInterpolator,
|
355 | interpolationIndex,
|
356 | current,
|
357 | gesture,
|
358 | next,
|
359 | layout,
|
360 | insets,
|
361 | overlay,
|
362 | overlayEnabled,
|
363 | shadowEnabled,
|
364 | gestureEnabled,
|
365 | gestureDirection,
|
366 | pageOverflowEnabled,
|
367 | headerDarkContent,
|
368 | children,
|
369 | containerStyle: customContainerStyle,
|
370 | contentStyle,
|
371 | ...rest
|
372 | } = this.props;
|
373 | const interpolationProps = this.getCardAnimation(interpolationIndex, current, next, layout, insets.top, insets.right, insets.bottom, insets.left);
|
374 | const interpolatedStyle = this.getInterpolatedStyle(styleInterpolator, interpolationProps);
|
375 | const {
|
376 | containerStyle,
|
377 | cardStyle,
|
378 | overlayStyle,
|
379 | shadowStyle
|
380 | } = interpolatedStyle;
|
381 | const handleGestureEvent = gestureEnabled ? Animated.event([{
|
382 | nativeEvent: gestureDirection === 'vertical' || gestureDirection === 'vertical-inverted' ? {
|
383 | translationY: gesture
|
384 | } : {
|
385 | translationX: gesture
|
386 | }
|
387 | }], {
|
388 | useNativeDriver
|
389 | }) : undefined;
|
390 | const {
|
391 | backgroundColor
|
392 | } = StyleSheet.flatten(contentStyle || {});
|
393 | const isTransparent = typeof backgroundColor === 'string' ? Color(backgroundColor).alpha() === 0 : false;
|
394 | return React.createElement(CardAnimationContext.Provider, {
|
395 | value: interpolationProps
|
396 | },
|
397 |
|
398 | Platform.OS === 'ios' && overlayEnabled && next && getIsModalPresentation(styleInterpolator) ? React.createElement(ModalStatusBarManager, {
|
399 | dark: headerDarkContent,
|
400 | layout: layout,
|
401 | insets: insets,
|
402 | style: cardStyle
|
403 | }) : null, React.createElement(Animated.View, {
|
404 | style: {
|
405 |
|
406 |
|
407 |
|
408 |
|
409 | opacity: current
|
410 | }
|
411 | ,
|
412 | collapsable: false
|
413 | }), React.createElement(View, _extends({
|
414 | pointerEvents: "box-none"
|
415 | }, rest), overlayEnabled ? React.createElement(View, {
|
416 | pointerEvents: "box-none",
|
417 | style: StyleSheet.absoluteFill
|
418 | }, overlay({
|
419 | style: overlayStyle
|
420 | })) : null, React.createElement(Animated.View, {
|
421 | style: [styles.container, containerStyle, customContainerStyle],
|
422 | pointerEvents: "box-none"
|
423 | }, React.createElement(PanGestureHandler, _extends({
|
424 | enabled: layout.width !== 0 && gestureEnabled,
|
425 | onGestureEvent: handleGestureEvent,
|
426 | onHandlerStateChange: this.handleGestureStateChange
|
427 | }, this.gestureActivationCriteria()), React.createElement(Animated.View, {
|
428 | needsOffscreenAlphaCompositing: hasOpacityStyle(cardStyle),
|
429 | style: [styles.container, cardStyle]
|
430 | }, shadowEnabled && shadowStyle && !isTransparent ? React.createElement(Animated.View, {
|
431 | style: [styles.shadow, gestureDirection === 'horizontal' ? [styles.shadowHorizontal, styles.shadowLeft] : gestureDirection === 'horizontal-inverted' ? [styles.shadowHorizontal, styles.shadowRight] : gestureDirection === 'vertical' ? [styles.shadowVertical, styles.shadowTop] : [styles.shadowVertical, styles.shadowBottom], {
|
432 | backgroundColor
|
433 | }, shadowStyle],
|
434 | pointerEvents: "none"
|
435 | }) : null, React.createElement(CardSheet, {
|
436 | ref: this.contentRef,
|
437 | enabled: pageOverflowEnabled,
|
438 | layout: layout,
|
439 | style: contentStyle
|
440 | }, children))))));
|
441 | }
|
442 |
|
443 | }
|
444 |
|
445 | _defineProperty(Card, "defaultProps", {
|
446 | shadowEnabled: false,
|
447 | gestureEnabled: true,
|
448 | gestureVelocityImpact: GESTURE_VELOCITY_IMPACT,
|
449 | overlay: _ref5 => {
|
450 | let {
|
451 | style
|
452 | } = _ref5;
|
453 | return style ? React.createElement(Animated.View, {
|
454 | pointerEvents: "none",
|
455 | style: [styles.overlay, style]
|
456 | }) : null;
|
457 | }
|
458 | });
|
459 |
|
460 | export const getIsModalPresentation = cardStyleInterpolator => {
|
461 | return cardStyleInterpolator === forModalPresentationIOS ||
|
462 | cardStyleInterpolator.name === 'forModalPresentationIOS';
|
463 | };
|
464 | const styles = StyleSheet.create({
|
465 | container: {
|
466 | flex: 1
|
467 | },
|
468 | overlay: {
|
469 | flex: 1,
|
470 | backgroundColor: '#000'
|
471 | },
|
472 | shadow: {
|
473 | position: 'absolute',
|
474 | shadowRadius: 5,
|
475 | shadowColor: '#000',
|
476 | shadowOpacity: 0.3
|
477 | },
|
478 | shadowHorizontal: {
|
479 | top: 0,
|
480 | bottom: 0,
|
481 | width: 3,
|
482 | shadowOffset: {
|
483 | width: -1,
|
484 | height: 1
|
485 | }
|
486 | },
|
487 | shadowLeft: {
|
488 | left: 0
|
489 | },
|
490 | shadowRight: {
|
491 | right: 0
|
492 | },
|
493 | shadowVertical: {
|
494 | left: 0,
|
495 | right: 0,
|
496 | height: 3,
|
497 | shadowOffset: {
|
498 | width: 1,
|
499 | height: -1
|
500 | }
|
501 | },
|
502 | shadowTop: {
|
503 | top: 0
|
504 | },
|
505 | shadowBottom: {
|
506 | bottom: 0
|
507 | }
|
508 | });
|
509 |
|
\ | No newline at end of file |