Remove SwipeableFlatList from RN

Summary: According to TheSavior this was an experimental module that "accidentally" got added to RN open source. In fact, we only use it in two places internally. This diff moves these files to FB internal and removes them from RN completely. I skipped the deprecation message because it was always an experimental feature and I don't expect anyone out there using it.

Reviewed By: TheSavior

Differential Revision: D14631749

fbshipit-source-id: 87878fcbb901e1e7fa4a3ff3205e09886ff3ed43
This commit is contained in:
Christoph Nakazawa 2019-03-27 16:19:59 -07:00 коммит произвёл Facebook Github Bot
Родитель 58335abd97
Коммит 9ca7989f60
8 изменённых файлов: 0 добавлений и 866 удалений

Просмотреть файл

@ -1,172 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
'use strict';
import type {Props as FlatListProps} from 'FlatList';
import type {renderItemType} from 'VirtualizedList';
const React = require('React');
const SwipeableRow = require('SwipeableRow');
const FlatList = require('FlatList');
// TODO: Make this $ReadOnly and Exact. Will require doing the same to the props in
// Libraries/Lists/*
type SwipableListProps = {
/**
* To alert the user that swiping is possible, the first row can bounce
* on component mount.
*/
bounceFirstRowOnMount: boolean,
/**
* Maximum distance to open to after a swipe
*/
maxSwipeDistance: number | (Object => number),
/**
* Callback method to render the view that will be unveiled on swipe
*/
renderQuickActions: renderItemType,
};
type Props<ItemT> = SwipableListProps & FlatListProps<ItemT>;
type State = {|
openRowKey: ?string,
|};
/**
* A container component that renders multiple SwipeableRow's in a FlatList
* implementation. This is designed to be a drop-in replacement for the
* standard React Native `FlatList`, so use it as if it were a FlatList, but
* with extra props.
*
* SwipeableRow can be used independently of this component, but the main
* benefits of using this component are:
*
* - It ensures that at most 1 row is swiped open (auto closes others)
* - It can bounce the 1st row of the list so users know it's swipeable
* - Increase performance on iOS by locking list swiping when row swiping is occurring
* - More to come
*/
class SwipeableFlatList<ItemT> extends React.Component<Props<ItemT>, State> {
_flatListRef: ?FlatList<ItemT> = null;
_shouldBounceFirstRowOnMount: boolean = false;
static defaultProps = {
...FlatList.defaultProps,
bounceFirstRowOnMount: true,
renderQuickActions: () => null,
};
constructor(props: Props<ItemT>, context: any): void {
super(props, context);
this.state = {
openRowKey: null,
};
this._shouldBounceFirstRowOnMount = this.props.bounceFirstRowOnMount;
}
render(): React.Node {
return (
<FlatList
{...this.props}
ref={ref => {
this._flatListRef = ref;
}}
onScroll={this._onScroll}
renderItem={this._renderItem}
extraData={this.state}
/>
);
}
_onScroll = (e): void => {
// Close any opens rows on ListView scroll
if (this.state.openRowKey) {
this.setState({
openRowKey: null,
});
}
this.props.onScroll && this.props.onScroll(e);
};
_renderItem = (info: Object): ?React.Element<any> => {
const slideoutView = this.props.renderQuickActions(info);
const key = this.props.keyExtractor(info.item, info.index);
// If renderQuickActions is unspecified or returns falsey, don't allow swipe
if (!slideoutView) {
return this.props.renderItem(info);
}
let shouldBounceOnMount = false;
if (this._shouldBounceFirstRowOnMount) {
this._shouldBounceFirstRowOnMount = false;
shouldBounceOnMount = true;
}
return (
<SwipeableRow
slideoutView={slideoutView}
isOpen={key === this.state.openRowKey}
maxSwipeDistance={this._getMaxSwipeDistance(info)}
onOpen={() => this._onOpen(key)}
onClose={() => this._onClose(key)}
shouldBounceOnMount={shouldBounceOnMount}
onSwipeEnd={this._setListViewScrollable}
onSwipeStart={this._setListViewNotScrollable}>
{this.props.renderItem(info)}
</SwipeableRow>
);
};
// This enables rows having variable width slideoutView.
_getMaxSwipeDistance(info: Object): number {
if (typeof this.props.maxSwipeDistance === 'function') {
return this.props.maxSwipeDistance(info);
}
return this.props.maxSwipeDistance;
}
_setListViewScrollableTo(value: boolean) {
if (this._flatListRef) {
this._flatListRef.setNativeProps({
scrollEnabled: value,
});
}
}
_setListViewScrollable = () => {
this._setListViewScrollableTo(true);
};
_setListViewNotScrollable = () => {
this._setListViewScrollableTo(false);
};
_onOpen(key: any): void {
this.setState({
openRowKey: key,
});
}
_onClose(key: any): void {
this.setState({
openRowKey: null,
});
}
}
module.exports = SwipeableFlatList;

Просмотреть файл

@ -1,79 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow
*/
'use strict';
const DeprecatedViewPropTypes = require('DeprecatedViewPropTypes');
const Image = require('Image');
const React = require('React');
const Text = require('Text');
const TouchableHighlight = require('TouchableHighlight');
const View = require('View');
import type {ImageSource} from 'ImageSource';
/**
* Standard set of quick action buttons that can, if the user chooses, be used
* with SwipeableListView. Each button takes an image and text with optional
* formatting.
*/
class SwipeableQuickActionButton extends React.Component<{
accessibilityLabel?: string,
imageSource?: ?(ImageSource | number),
/* $FlowFixMe(>=0.82.0 site=react_native_fb) This comment suppresses an error
* found when Flow v0.82 was deployed. To see the error delete this comment
* and run Flow. */
imageStyle?: ?DeprecatedViewPropTypes.style,
mainView?: ?React.Node,
onPress?: Function,
/* $FlowFixMe(>=0.82.0 site=react_native_fb) This comment suppresses an error
* found when Flow v0.82 was deployed. To see the error delete this comment
* and run Flow. */
style?: ?DeprecatedViewPropTypes.style,
/* $FlowFixMe(>=0.82.0 site=react_native_fb) This comment suppresses an error
* found when Flow v0.82 was deployed. To see the error delete this comment
* and run Flow. */
containerStyle?: ?DeprecatedViewPropTypes.style,
testID?: string,
text?: ?(string | Object | Array<string | Object>),
/* $FlowFixMe(>=0.82.0 site=react_native_fb) This comment suppresses an error
* found when Flow v0.82 was deployed. To see the error delete this comment
* and run Flow. */
textStyle?: ?DeprecatedViewPropTypes.style,
}> {
render(): React.Node {
if (!this.props.imageSource && !this.props.text && !this.props.mainView) {
return null;
}
const mainView = this.props.mainView ? (
this.props.mainView
) : (
<View style={this.props.style}>
<Image
accessibilityLabel={this.props.accessibilityLabel}
source={this.props.imageSource}
style={this.props.imageStyle}
/>
<Text style={this.props.textStyle}>{this.props.text}</Text>
</View>
);
return (
<TouchableHighlight
onPress={this.props.onPress}
testID={this.props.testID}
underlayColor="transparent"
style={this.props.containerStyle}>
{mainView}
</TouchableHighlight>
);
}
}
module.exports = SwipeableQuickActionButton;

Просмотреть файл

@ -1,69 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow strict-local
*/
'use strict';
const React = require('React');
const StyleSheet = require('StyleSheet');
const View = require('View');
import type {ViewStyleProp} from 'StyleSheet';
type Props = $ReadOnly<{|
style?: ?ViewStyleProp,
children: React.Node,
|}>;
/**
* A thin wrapper around standard quick action buttons that can, if the user
* chooses, be used with SwipeableListView. Sample usage is as follows, in the
* renderQuickActions callback:
*
* <SwipeableQuickActions>
* <SwipeableQuickActionButton {..props} />
* <SwipeableQuickActionButton {..props} />
* </SwipeableQuickActions>
*/
class SwipeableQuickActions extends React.Component<Props> {
render(): React.Node {
const children = this.props.children;
let buttons = [];
// Multiple children
if (children instanceof Array) {
for (let i = 0; i < children.length; i++) {
buttons.push(children[i]);
if (i < children.length - 1) {
// Not last button
buttons.push(<View key={i} style={styles.divider} />);
}
}
} else {
// 1 child
buttons = children;
}
return <View style={[styles.background, this.props.style]}>{buttons}</View>;
}
}
const styles = StyleSheet.create({
background: {
flex: 1,
flexDirection: 'row',
justifyContent: 'flex-end',
},
divider: {
width: 4,
},
});
module.exports = SwipeableQuickActions;

Просмотреть файл

@ -1,384 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow
*/
'use strict';
const Animated = require('Animated');
const I18nManager = require('I18nManager');
const PanResponder = require('PanResponder');
const React = require('React');
const StyleSheet = require('StyleSheet');
const View = require('View');
import type {LayoutEvent, PressEvent} from 'CoreEventTypes';
import type {GestureState} from 'PanResponder';
const IS_RTL = I18nManager.isRTL;
// NOTE: Eventually convert these consts to an input object of configurations
// Position of the left of the swipable item when closed
const CLOSED_LEFT_POSITION = 0;
// Minimum swipe distance before we recognize it as such
const HORIZONTAL_SWIPE_DISTANCE_THRESHOLD = 10;
// Minimum swipe speed before we fully animate the user's action (open/close)
const HORIZONTAL_FULL_SWIPE_SPEED_THRESHOLD = 0.3;
// Factor to divide by to get slow speed; i.e. 4 means 1/4 of full speed
const SLOW_SPEED_SWIPE_FACTOR = 4;
// Time, in milliseconds, of how long the animated swipe should be
const SWIPE_DURATION = 300;
/**
* On SwipeableListView mount, the 1st item will bounce to show users it's
* possible to swipe
*/
const ON_MOUNT_BOUNCE_DELAY = 700;
const ON_MOUNT_BOUNCE_DURATION = 400;
// Distance left of closed position to bounce back when right-swiping from closed
const RIGHT_SWIPE_BOUNCE_BACK_DISTANCE = 30;
const RIGHT_SWIPE_BOUNCE_BACK_DURATION = 300;
/**
* Max distance of right swipe to allow (right swipes do functionally nothing).
* Must be multiplied by SLOW_SPEED_SWIPE_FACTOR because gestureState.dx tracks
* how far the finger swipes, and not the actual animation distance.
*/
const RIGHT_SWIPE_THRESHOLD = 30 * SLOW_SPEED_SWIPE_FACTOR;
const DEFAULT_SWIPE_THRESHOLD = 30;
const emptyFunction = () => {};
type Props = $ReadOnly<{|
children?: ?React.Node,
isOpen?: ?boolean,
maxSwipeDistance?: ?number,
onClose?: ?() => void,
onOpen?: ?() => void,
onSwipeEnd?: ?() => void,
onSwipeStart?: ?() => void,
preventSwipeRight?: ?boolean,
shouldBounceOnMount?: ?boolean,
slideoutView?: ?React.Node,
swipeThreshold?: ?number,
|}>;
type State = {
currentLeft: Animated.Value,
isSwipeableViewRendered: boolean,
rowHeight: ?number,
};
/**
* Creates a swipable row that allows taps on the main item and a custom View
* on the item hidden behind the row. Typically this should be used in
* conjunction with SwipeableListView for additional functionality, but can be
* used in a normal ListView. See the renderRow for SwipeableListView to see how
* to use this component separately.
*/
class SwipeableRow extends React.Component<Props, State> {
_handleMoveShouldSetPanResponderCapture = (
event: PressEvent,
gestureState: GestureState,
): boolean => {
// Decides whether a swipe is responded to by this component or its child
return gestureState.dy < 10 && this._isValidSwipe(gestureState);
};
_handlePanResponderGrant = (
event: PressEvent,
gestureState: GestureState,
): void => {};
_handlePanResponderMove = (
event: PressEvent,
gestureState: GestureState,
): void => {
if (this._isSwipingExcessivelyRightFromClosedPosition(gestureState)) {
return;
}
this.props.onSwipeStart && this.props.onSwipeStart();
if (this._isSwipingRightFromClosed(gestureState)) {
this._swipeSlowSpeed(gestureState);
} else {
this._swipeFullSpeed(gestureState);
}
};
_onPanResponderTerminationRequest = (
event: PressEvent,
gestureState: GestureState,
): boolean => {
return false;
};
_handlePanResponderEnd = (
event: PressEvent,
gestureState: GestureState,
): void => {
const horizontalDistance = IS_RTL ? -gestureState.dx : gestureState.dx;
if (this._isSwipingRightFromClosed(gestureState)) {
this.props.onOpen && this.props.onOpen();
this._animateBounceBack(RIGHT_SWIPE_BOUNCE_BACK_DURATION);
} else if (this._shouldAnimateRemainder(gestureState)) {
if (horizontalDistance < 0) {
// Swiped left
this.props.onOpen && this.props.onOpen();
this._animateToOpenPositionWith(gestureState.vx, horizontalDistance);
} else {
// Swiped right
this.props.onClose && this.props.onClose();
this._animateToClosedPosition();
}
} else {
if (this._previousLeft === CLOSED_LEFT_POSITION) {
this._animateToClosedPosition();
} else {
this._animateToOpenPosition();
}
}
this.props.onSwipeEnd && this.props.onSwipeEnd();
};
_panResponder = PanResponder.create({
onMoveShouldSetPanResponderCapture: this
._handleMoveShouldSetPanResponderCapture,
onPanResponderGrant: this._handlePanResponderGrant,
onPanResponderMove: this._handlePanResponderMove,
onPanResponderRelease: this._handlePanResponderEnd,
onPanResponderTerminationRequest: this._onPanResponderTerminationRequest,
onPanResponderTerminate: this._handlePanResponderEnd,
onShouldBlockNativeResponder: (event, gestureState) => false,
});
_previousLeft = CLOSED_LEFT_POSITION;
_timeoutID: ?TimeoutID = null;
state = {
currentLeft: new Animated.Value(this._previousLeft),
/**
* In order to render component A beneath component B, A must be rendered
* before B. However, this will cause "flickering", aka we see A briefly
* then B. To counter this, _isSwipeableViewRendered flag is used to set
* component A to be transparent until component B is loaded.
*/
isSwipeableViewRendered: false,
rowHeight: null,
};
componentDidMount(): void {
if (this.props.shouldBounceOnMount) {
/**
* Do the on mount bounce after a delay because if we animate when other
* components are loading, the animation will be laggy
*/
this._timeoutID = setTimeout(() => {
this._animateBounceBack(ON_MOUNT_BOUNCE_DURATION);
}, ON_MOUNT_BOUNCE_DELAY);
}
}
UNSAFE_componentWillReceiveProps(nextProps: $Shape<Props>): void {
/**
* We do not need an "animateOpen(noCallback)" because this animation is
* handled internally by this component.
*/
const isOpen = this.props.isOpen ?? false;
const nextIsOpen = nextProps.isOpen ?? false;
if (isOpen && !nextIsOpen) {
this._animateToClosedPosition();
}
}
componentWillUnmount() {
if (this._timeoutID != null) {
clearTimeout(this._timeoutID);
}
}
render(): React.Element<any> {
// The view hidden behind the main view
let slideOutView;
if (this.state.isSwipeableViewRendered && this.state.rowHeight) {
slideOutView = (
<View
style={[styles.slideOutContainer, {height: this.state.rowHeight}]}>
{this.props.slideoutView}
</View>
);
}
// The swipeable item
const swipeableView = (
<Animated.View
onLayout={this._onSwipeableViewLayout}
style={{transform: [{translateX: this.state.currentLeft}]}}>
{this.props.children}
</Animated.View>
);
return (
<View {...this._panResponder.panHandlers}>
{slideOutView}
{swipeableView}
</View>
);
}
close(): void {
this.props.onClose && this.props.onClose();
this._animateToClosedPosition();
}
_onSwipeableViewLayout = (event: LayoutEvent): void => {
this.setState({
isSwipeableViewRendered: true,
rowHeight: event.nativeEvent.layout.height,
});
};
_isSwipingRightFromClosed(gestureState: GestureState): boolean {
const gestureStateDx = IS_RTL ? -gestureState.dx : gestureState.dx;
return this._previousLeft === CLOSED_LEFT_POSITION && gestureStateDx > 0;
}
_swipeFullSpeed(gestureState: GestureState): void {
this.state.currentLeft.setValue(this._previousLeft + gestureState.dx);
}
_swipeSlowSpeed(gestureState: GestureState): void {
this.state.currentLeft.setValue(
this._previousLeft + gestureState.dx / SLOW_SPEED_SWIPE_FACTOR,
);
}
_isSwipingExcessivelyRightFromClosedPosition(
gestureState: GestureState,
): boolean {
/**
* We want to allow a BIT of right swipe, to allow users to know that
* swiping is available, but swiping right does not do anything
* functionally.
*/
const gestureStateDx = IS_RTL ? -gestureState.dx : gestureState.dx;
return (
this._isSwipingRightFromClosed(gestureState) &&
gestureStateDx > RIGHT_SWIPE_THRESHOLD
);
}
_animateTo(
toValue: number,
duration: number = SWIPE_DURATION,
callback: Function = emptyFunction,
): void {
Animated.timing(this.state.currentLeft, {
duration,
toValue,
useNativeDriver: true,
}).start(() => {
this._previousLeft = toValue;
callback();
});
}
_animateToOpenPosition(): void {
const maxSwipeDistance = this.props.maxSwipeDistance ?? 0;
const directionAwareMaxSwipeDistance = IS_RTL
? -maxSwipeDistance
: maxSwipeDistance;
this._animateTo(-directionAwareMaxSwipeDistance);
}
_animateToOpenPositionWith(speed: number, distMoved: number): void {
/**
* Ensure the speed is at least the set speed threshold to prevent a slow
* swiping animation
*/
speed =
speed > HORIZONTAL_FULL_SWIPE_SPEED_THRESHOLD
? speed
: HORIZONTAL_FULL_SWIPE_SPEED_THRESHOLD;
const maxSwipeDistance = this.props.maxSwipeDistance ?? 0;
/**
* Calculate the duration the row should take to swipe the remaining distance
* at the same speed the user swiped (or the speed threshold)
*/
const duration = Math.abs((maxSwipeDistance - Math.abs(distMoved)) / speed);
const directionAwareMaxSwipeDistance = IS_RTL
? -maxSwipeDistance
: maxSwipeDistance;
this._animateTo(-directionAwareMaxSwipeDistance, duration);
}
_animateToClosedPosition(duration: number = SWIPE_DURATION): void {
this._animateTo(CLOSED_LEFT_POSITION, duration);
}
_animateToClosedPositionDuringBounce = (): void => {
this._animateToClosedPosition(RIGHT_SWIPE_BOUNCE_BACK_DURATION);
};
_animateBounceBack(duration: number): void {
/**
* When swiping right, we want to bounce back past closed position on release
* so users know they should swipe right to get content.
*/
const swipeBounceBackDistance = IS_RTL
? -RIGHT_SWIPE_BOUNCE_BACK_DISTANCE
: RIGHT_SWIPE_BOUNCE_BACK_DISTANCE;
this._animateTo(
-swipeBounceBackDistance,
duration,
this._animateToClosedPositionDuringBounce,
);
}
// Ignore swipes due to user's finger moving slightly when tapping
_isValidSwipe(gestureState: GestureState): boolean {
const preventSwipeRight = this.props.preventSwipeRight ?? false;
if (
preventSwipeRight &&
this._previousLeft === CLOSED_LEFT_POSITION &&
gestureState.dx > 0
) {
return false;
}
return Math.abs(gestureState.dx) > HORIZONTAL_SWIPE_DISTANCE_THRESHOLD;
}
_shouldAnimateRemainder(gestureState: GestureState): boolean {
/**
* If user has swiped past a certain distance, animate the rest of the way
* if they let go
*/
const swipeThreshold = this.props.swipeThreshold ?? DEFAULT_SWIPE_THRESHOLD;
return (
Math.abs(gestureState.dx) > swipeThreshold ||
gestureState.vx > HORIZONTAL_FULL_SWIPE_SPEED_THRESHOLD
);
}
}
const styles = StyleSheet.create({
slideOutContainer: {
bottom: 0,
left: 0,
position: 'absolute',
right: 0,
top: 0,
},
});
module.exports = SwipeableRow;

Просмотреть файл

@ -137,9 +137,6 @@ module.exports = {
);
return require('StatusBar');
},
get SwipeableFlatList() {
return require('SwipeableFlatList');
},
get Text() {
return require('Text');
},

Просмотреть файл

@ -71,10 +71,6 @@ const ComponentExamples: Array<RNTesterExample> = [
key: 'StatusBarExample',
module: require('./StatusBarExample'),
},
{
key: 'SwipeableFlatListExample',
module: require('./SwipeableFlatListExample'),
},
{
key: 'SwitchExample',
module: require('./SwitchExample'),

Просмотреть файл

@ -128,11 +128,6 @@ const ComponentExamples: Array<RNTesterExample> = [
module: require('./StatusBarExample'),
supportsTVOS: false,
},
{
key: 'SwipeableFlatListExample',
module: require('./SwipeableFlatListExample'),
supportsTVOS: false,
},
{
key: 'SwitchExample',
module: require('./SwitchExample'),

Просмотреть файл

@ -1,150 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
'use strict';
const React = require('react');
const ReactNative = require('react-native');
const {
Image,
SwipeableFlatList,
TouchableHighlight,
StyleSheet,
Text,
View,
Alert,
} = ReactNative;
const RNTesterPage = require('./RNTesterPage');
import type {RNTesterProps} from 'RNTesterTypes';
const data = [
{
key: 'like',
icon: require('./Thumbnails/like.png'),
data: 'Like!',
},
{
key: 'heart',
icon: require('./Thumbnails/heart.png'),
data: 'Heart!',
},
{
key: 'party',
icon: require('./Thumbnails/party.png'),
data: 'Party!',
},
];
class SwipeableFlatListExample extends React.Component<RNTesterProps> {
render() {
return (
<RNTesterPage
title={this.props.navigator ? null : '<SwipeableFlatList>'}
noSpacer={true}
noScroll={true}>
<SwipeableFlatList
data={data}
bounceFirstRowOnMount={true}
maxSwipeDistance={160}
renderItem={this._renderItem.bind(this)}
renderQuickActions={this._renderQuickActions.bind(this)}
/>
</RNTesterPage>
);
}
_renderItem({item}): ?React.Element<any> {
return (
<View style={styles.row}>
<Image style={styles.rowIcon} source={item.icon} />
<View style={styles.rowData}>
<Text style={styles.rowDataText}>{item.data}</Text>
</View>
</View>
);
}
_renderQuickActions({item}: Object): ?React.Element<any> {
return (
<View style={styles.actionsContainer}>
<TouchableHighlight
style={styles.actionButton}
onPress={() => {
Alert.alert(
'Tips',
'You could do something with this edit action!',
);
}}>
<Text style={styles.actionButtonText}>Edit</Text>
</TouchableHighlight>
<TouchableHighlight
style={[styles.actionButton, styles.actionButtonDestructive]}
onPress={() => {
Alert.alert(
'Tips',
'You could do something with this remove action!',
);
}}>
<Text style={styles.actionButtonText}>Remove</Text>
</TouchableHighlight>
</View>
);
}
}
const styles = StyleSheet.create({
row: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
padding: 10,
backgroundColor: '#F6F6F6',
},
rowIcon: {
width: 64,
height: 64,
marginRight: 20,
},
rowData: {
flex: 1,
},
rowDataText: {
fontSize: 24,
},
actionsContainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'flex-end',
alignItems: 'center',
},
actionButton: {
padding: 10,
width: 80,
backgroundColor: '#999999',
},
actionButtonDestructive: {
backgroundColor: '#FF0000',
},
actionButtonText: {
textAlign: 'center',
},
});
exports.title = '<SwipeableFlatList>';
exports.description = 'Performant, scrollable, swipeable list of data.';
exports.examples = [
{
title: 'Simple swipable list',
render: function(): React.Element<typeof SwipeableFlatListExample> {
return <SwipeableFlatListExample />;
},
},
];