Skip to content

Commit

Permalink
Merge branch 'release/1.0.0' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
Vlad committed Jul 23, 2019
2 parents f559c60 + aa35533 commit ff69c2c
Show file tree
Hide file tree
Showing 21 changed files with 693 additions and 399 deletions.
25 changes: 13 additions & 12 deletions components/DropDownMenu/DropDownModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@ import PropTypes from 'prop-types';
import React, { PureComponent } from 'react';
import {
Modal,
ListView,
LayoutAnimation,
Dimensions,
NativeModules,
} from 'react-native';
import _ from 'lodash';

import { connectStyle, changeColorAlpha } from '@shoutem/theme';
import { TimingDriver, FadeIn, ZoomOut } from '@shoutem/animation';
import { connectStyle, changeColorAlpha } from '@shoutem/theme';

import { Button } from '../Button';
import { Icon } from '../Icon';
import { Text } from '../Text';
import { View } from '../View';
import { LinearGradient } from '../LinearGradient';
import { ListView } from '../ListView';
import { Text } from '../Text';
import { TouchableOpacity } from '../TouchableOpacity';
import { View } from '../View';

const window = Dimensions.get('window');

Expand Down Expand Up @@ -76,17 +76,18 @@ class DropDownModal extends PureComponent {

constructor(props) {
super(props);
this.state = {
optionHeight: 0,
shouldRenderModalContent: false,
};

this.close = this.close.bind(this);
this.emitOnOptionSelectedEvent = this.emitOnOptionSelectedEvent.bind(this);
this.renderGradient = this.renderGradient.bind(this);
this.renderRow = this.renderRow.bind(this);
this.selectOption = this.selectOption.bind(this);
this.onOptionLayout = this.onOptionLayout.bind(this);
this.ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });

this.state = {
optionHeight: 0,
shouldRenderModalContent: false,
};
}

componentWillMount() {
Expand Down Expand Up @@ -242,12 +243,13 @@ class DropDownModal extends PureComponent {
render() {
const { titleProperty, options, style } = this.props;
const { shouldRenderModalContent } = this.state;

if (_.size(options) === 0) {
return null;
}

const listViewStyle = this.resolveListViewStyle();
const dataSource = this.ds.cloneWithRows(options.filter((option) => option[titleProperty]));
const data = options.filter((option) => option[titleProperty]);

return (
<Modal
Expand All @@ -260,8 +262,7 @@ class DropDownModal extends PureComponent {
<View style={style.modal} styleName="vertical">
{shouldRenderModalContent ?
<ListView
scrollRenderAheadDistance={50}
dataSource={dataSource}
data={data}
renderRow={this.renderRow}
style={listViewStyle}
renderFooter={this.renderFooter}
Expand Down
73 changes: 73 additions & 0 deletions components/EmptyStateView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';

import { connectStyle } from '@shoutem/theme';

import { Icon } from './Icon';
import { View } from './View';
import { Button } from './Button';
import { Subtitle, Text } from './Text';

class EmptyStateView extends PureComponent {
static defaultProps = {
retryButtonTitle: 'TRY AGAIN',
icon: 'error',
}

constructor(props) {
super(props);

this.onRetry = this.onRetry.bind(this);
this.renderRetryButton = this.renderRetryButton.bind(this);
}

onRetry() {
this.props.onRetry();
}

renderRetryButton() {
const { retryButtonTitle } = this.props;

// Show retry button at the bottom only if
// there is a onRetry action passed.
return (
<View styleName="horizontal anchor-bottom">
<Button styleName="full-width" onPress={this.onRetry}>
<Text>{retryButtonTitle}</Text>
</Button>
</View>
);
}

render() {
const { icon, message, onRetry } = this.props;

return (
<View
{...this.props}
styleName="vertical flexible h-center v-center"
>
<View styleName="icon-placeholder">
<Icon name={icon} />
</View>

<Subtitle styleName="h-center">{message}</Subtitle>

{onRetry && this.renderRetryButton()}
</View>
);
}
}

EmptyStateView.propTypes = {
...EmptyStateView.propTypes,
onRetry: PropTypes.func,
message: PropTypes.string,
icon: PropTypes.string,
};

const StyledView = connectStyle('shoutem.ui.EmptyStateView')(EmptyStateView);

export {
StyledView as EmptyStateView,
};
14 changes: 9 additions & 5 deletions components/LinearGradient.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
import React, { PureComponent } from 'react';
import RNLinearGradient from 'react-native-linear-gradient';
import _ from 'lodash';

import { connectAnimation } from '@shoutem/animation';
import { connectStyle } from '@shoutem/theme';

const RNLinearGradientPropsKeys = Object.keys(RNLinearGradient.propTypes);
const RNLinearGradientPropsKeys = ['start', 'end', 'colors', 'locations'];

class LinearGradient extends PureComponent {
render () {
const { props } = this;

const style = { ..._.omit(props.style, RNLinearGradientPropsKeys) };
const styleWithOmissions = _.omit(props.style, RNLinearGradientPropsKeys);
const linearGradientProps = {
...props,
..._.pick(props.style, RNLinearGradientPropsKeys),
};

return (
<RNLinearGradient
{...props}
{..._.pick(props.style, RNLinearGradientPropsKeys)}
style={style}
{...linearGradientProps}
style={styleWithOmissions}
>
{props.children}
</RNLinearGradient>
Expand Down
119 changes: 52 additions & 67 deletions components/ListView.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import {
View,
ListView as RNListView,
FlatList,
SectionList,
RefreshControl,
StatusBar,
Platform,
Expand All @@ -12,6 +13,8 @@ import _ from 'lodash';

import { connectStyle } from '@shoutem/theme';

import { Caption } from './Text';
import { Divider } from './Divider';
import { Spinner } from './Spinner';

const scrollViewProps = _.keys(ScrollView.propTypes);
Expand All @@ -23,49 +26,6 @@ const Status = {
IDLE: 'idle',
};

/**
* Provides dataSource to ListView.
* Clones items and group them by section if needed.
*/
class ListDataSource {
constructor(config, getSectionId) {
this.getSectionId = getSectionId;
this.withSections = !!config.sectionHeaderHasChanged;
this.dataSource = new RNListView.DataSource(config);
}

/**
* Transforms items list ([...items]) to [[...sectionItems], [...sectionItems]]
* @param data
* @returns {*}
*/
groupItemsIntoSections(data) {
let prevSectionId;
return data.reduce((sections, item) => {
const sectionId = this.getSectionId(item);
if (prevSectionId !== sectionId) {
prevSectionId = sectionId;
sections.push([]);
}
const lastSectionIndex = sections.length - 1;
sections[lastSectionIndex].push(item);
return sections;
}, []);
}

/**
* Transforms items list [<item>, <item>]
* @param data
* @returns {*}
*/
clone(data) {
if (this.withSections) {
return this.dataSource.cloneWithRowsAndSections(this.groupItemsIntoSections(data));
}
return this.dataSource.cloneWithRows(data);
}
}

class ListView extends Component {
static propTypes = {
autoHideHeader: PropTypes.bool,
Expand All @@ -75,12 +35,14 @@ class ListView extends Component {
onLoadMore: PropTypes.func,
onRefresh: PropTypes.func,
getSectionId: PropTypes.func,
sections: PropTypes.object,
renderRow: PropTypes.func,
renderHeader: PropTypes.func,
renderFooter: PropTypes.func,
renderSectionHeader: PropTypes.func,
scrollDriver: PropTypes.object,
// TODO(Braco) - add render separator
hasFeaturedItem: PropTypes.bool,
renderFeaturedItem: PropTypes.func,
};

constructor(props, context) {
Expand All @@ -92,25 +54,12 @@ class ListView extends Component {
this.renderRefreshControl = this.renderRefreshControl.bind(this);
this.listView = null;


this.listDataSource = new ListDataSource({
rowHasChanged: (r1, r2) => r1 !== r2,
sectionHeaderHasChanged: props.renderSectionHeader ? (s1, s2) => s1 !== s2 : undefined,
getSectionHeaderData: (dataBlob, sectionId) => props.getSectionId(dataBlob[sectionId][0]),
}, props.getSectionId);


this.state = {
status: props.loading ? Status.LOADING : Status.IDLE,
dataSource: this.listDataSource.clone(props.data),
};
}

componentWillReceiveProps(nextProps) {
if (nextProps.data !== this.props.data) {
this.setState({ dataSource: this.listDataSource.clone(nextProps.data) });
}

if (nextProps.loading !== this.props.loading) {
this.setLoading(nextProps.loading);
}
Expand Down Expand Up @@ -153,26 +102,46 @@ class ListView extends Component {
// configuration
// default load more threshold
mappedProps.onEndReachedThreshold = 40;
// React native warning
// NOTE: In react 0.23 it can't be set to false
mappedProps.enableEmptySections = true;

// style
mappedProps.style = props.style.list;

mappedProps.contentContainerStyle = props.style.listContent;

// rendering
mappedProps.renderHeader = this.createRenderHeader(props.renderHeader, props.autoHideHeader);
mappedProps.renderRow = props.renderRow;
mappedProps.renderFooter = this.renderFooter;
mappedProps.renderSectionHeader = props.renderSectionHeader;
mappedProps.renderItem = (data) => props.renderRow(data.item);
mappedProps.ListFooterComponent = this.renderFooter;

if (props.hasFeaturedItem && !props.sections) {
mappedProps.sections = [
{ data: [props.data[0]], renderItem: (data) => props.renderFeaturedItem(data.item) },
{ data: props.data.slice(1) },
]
}

if (props.renderSectionHeader) {
mappedProps.renderSectionHeader = ({section}) => props.renderSectionHeader(section);
}
else if (!props.hasFeaturedItem) {
mappedProps.renderSectionHeader = ({section}) => this.renderDefaultSectionHeader(section);
}

// events
mappedProps.onEndReached = this.createOnLoadMore();

// data to display
mappedProps.dataSource = this.state.dataSource;
mappedProps.data = props.data;

// key extractor
mappedProps.keyExtractor = (item, index) => index.toString();

// sections for SectionList
if (props.sections) {
mappedProps.sections = props.sections;
}

// is data refreshing
mappedProps.refreshing = this.state.refreshing === Status.REFRESHING;

// refresh control
mappedProps.refreshControl = props.onRefresh && this.renderRefreshControl();
Expand Down Expand Up @@ -253,6 +222,16 @@ class ListView extends Component {
this.listView = listView;
}

renderDefaultSectionHeader(section) {
const title = _.get(section, 'title', '');

return (
<Divider styleName="section-header">
<Caption>{title.toUpperCase()}</Caption>
</Divider>
);
}

renderFooter() {
const { style, renderFooter } = this.props;
const { status } = this.state;
Expand Down Expand Up @@ -305,7 +284,13 @@ class ListView extends Component {
}

render() {
return <RNListView {...this.getPropsToPass()} />;
const { sections, hasFeaturedItem } = this.props;

if (sections || hasFeaturedItem) {
return <SectionList {...this.getPropsToPass()} />;
}

return <FlatList {...this.getPropsToPass()} />;
}
}

Expand Down
Loading

0 comments on commit ff69c2c

Please sign in to comment.