react-native-ui-lib
Version:
<p align="center"> <img src="https://user-images.githubusercontent.com/1780255/105469025-56759000-5ca0-11eb-993d-3568c1fd54f4.png" height="250px" style="display:block"/> </p> <p align="center">UI Toolset & Components Library for React Native</p> <p a
212 lines (186 loc) • 5.74 kB
JavaScript
import _ from 'lodash';
import React from 'react';
import { StyleSheet } from 'react-native';
import { Colors, Spacings } from "../../style"; // TODO: we should use asBaseComponent here instead of using UIComponent directly
import UIComponent from "../../commons/UIComponent";
import View from "../view";
import Text from "../text";
import { Constants } from "../../commons/new";
import GridListItem from "../gridListItem";
import { formatLastItemLabel } from "../../helpers/FormattingPresenter";
const DEFAULT_NUM_COLUMNS = 3;
const DEFAULT_ITEM_SPACINGS = Spacings.s4;
/**
* @description: A auto-generated grid view that calculate item size according to given props
* @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/GridViewScreen.tsx
*/
class GridView extends UIComponent {
static displayName = 'GridView';
static defaultProps = {
numColumns: DEFAULT_NUM_COLUMNS,
itemSpacing: DEFAULT_ITEM_SPACINGS
};
state = {
viewWidth: this.getGridContainerWidth(),
numColumns: this.calcNumberOfColumns(),
itemSize: this.calcItemSize()
};
static getDerivedStateFromProps(nextProps, prevState) {
let viewWidth;
let numColumns;
if (nextProps.viewWidth && Math.floor(nextProps.viewWidth) !== prevState.viewWidth) {
viewWidth = Math.floor(nextProps.viewWidth);
}
if (!nextProps.keepItemSize && nextProps.numColumns !== prevState.numColumns) {
numColumns = nextProps.numColumns;
}
if (viewWidth || viewWidth) {
return {
viewWidth,
numColumns
};
} else {
return null;
}
}
componentDidMount() {
this.dimensionsChangeListener = Constants.addDimensionsEventListener(this.onOrientationChanged);
}
componentWillUnmount() {
Constants.removeDimensionsEventListener(this.dimensionsChangeListener || this.onOrientationChanged);
}
onOrientationChanged = () => {
const {
keepItemSize
} = this.props;
const {
itemSize
} = this.state;
if (!this.props.viewWidth) {
const newItemSize = keepItemSize ? itemSize : this.calcItemSize();
this.setState({
viewWidth: Math.floor(this.getDefaultViewWidth()),
numColumns: this.calcNumberOfColumns(),
itemSize: newItemSize
});
}
};
getDefaultViewWidth() {
return Constants.screenWidth - Spacings.s5 * 2;
}
getGridContainerWidth() {
return Math.floor(this.props.viewWidth || this.getDefaultViewWidth());
}
calcNumberOfColumns() {
const {
numColumns,
itemSpacing = DEFAULT_ITEM_SPACINGS,
maxItemWidth
} = this.props;
const containerWidth = this.getGridContainerWidth();
if (maxItemWidth) {
return Math.ceil((containerWidth + itemSpacing) / (maxItemWidth + itemSpacing));
} else {
return numColumns || DEFAULT_NUM_COLUMNS;
}
}
calcItemSize() {
const {
itemSpacing = DEFAULT_ITEM_SPACINGS
} = this.props;
const containerWidth = this.getGridContainerWidth();
const numColumns = this.calcNumberOfColumns();
return (containerWidth - itemSpacing * (numColumns - 1)) / numColumns;
}
getThemeColor(placeColor) {
if (_.toLower(placeColor) === _.toLower(Colors.white)) {
return Colors.black;
} else if (Colors.isDark(placeColor)) {
return placeColor;
} else {
return Colors.getColorTint(placeColor, 30);
}
}
renderLastItemOverlay() {
const {
lastItemLabel,
items
} = this.props;
const overlayColor = this.getThemeColor(this.props.lastItemOverlayColor ?? '');
const formattedLabel = formatLastItemLabel(lastItemLabel, {
shouldAddPlus: true
});
if (!lastItemLabel) {
return;
}
const imageBorderRadius = _.chain(items).first().get('imageProps.borderRadius').value();
return <View style={[styles.overlayContainer, {
backgroundColor: Colors.rgba(overlayColor, 0.6),
borderRadius: imageBorderRadius
}]}>
<Text mainBold white>
{formattedLabel}
</Text>
</View>;
}
renderItem = (item, index) => {
const {
itemSize
} = this.state;
const {
items,
itemSpacing,
renderCustomItem
} = this.props;
const {
numColumns = DEFAULT_NUM_COLUMNS
} = this.state;
const itemsCount = _.size(items);
const rowCount = Math.ceil(itemsCount / numColumns);
const isLastItemInRow = (index + 1) % numColumns === 0;
const isLastRow = index + 1 > (rowCount - 1) * numColumns;
const isLastItem = index === itemsCount - 1;
const size = typeof item.itemSize === 'object' ? {
width: itemSize,
height: item.itemSize?.height || itemSize
} : itemSize;
const itemProps = {
key: index,
...item,
itemSize: size,
containerStyle: [!isLastItemInRow && {
marginRight: itemSpacing
}, !isLastRow && {
marginBottom: itemSpacing
}, item.containerStyle],
children: isLastItem ? this.renderLastItemOverlay() : undefined
};
return renderCustomItem ? renderCustomItem(itemProps) : <GridListItem {...itemProps} />;
};
render() {
const {
itemSize
} = this.state;
const {
items,
viewWidth
} = this.props;
return <View style={[styles.container, {
width: viewWidth ? Math.floor(viewWidth) : undefined
}]}>
{itemSize && _.map(items, this.renderItem)}
</View>;
}
}
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
width: '100%',
flexWrap: 'wrap'
},
overlayContainer: { ...StyleSheet.absoluteFillObject,
alignItems: 'center',
justifyContent: 'center'
}
});
export default GridView;