Skip to content

Commit 317d408

Browse files
Support passing max item width to grid view (#1640)
* Support passing max item width to grid view * Update src/components/gridView/index.tsx Co-authored-by: Inbal Tish <[email protected]> * Pass GridViewState internally * Use const for default numColumns value * Create a global const for default item spacings * push generated types Co-authored-by: Inbal Tish <[email protected]>
1 parent 6d9a70c commit 317d408

File tree

3 files changed

+80
-62
lines changed

3 files changed

+80
-62
lines changed

demo/src/screens/componentScreens/GridViewScreen.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,11 @@ class GridViewScreen extends Component {
179179
<GridView items={pairs} numColumns={2}/>
180180
<Text marginV-s5 text60BO>
181181
Dynamic itemSize
182+
<Text text90>
183+
{' '} (Using maxItemWidth)
184+
</Text>
182185
</Text>
183-
<GridView items={dynamicLayout} numColumns={3}/>
186+
<GridView items={dynamicLayout} maxItemWidth={120}/>
184187
<Text marginV-s5 text60BO>
185188
OverlayText
186189
</Text>

generatedTypes/src/components/gridView/index.d.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ export interface GridViewProps {
1111
*/
1212
viewWidth?: number;
1313
/**
14-
* Number of items to show in a row
14+
* Allow a responsive item width to the maximum item width
15+
*/
16+
maxItemWidth?: number;
17+
/**
18+
* Number of items to show in a row (ignored when passing maxItemWidth)
1519
*/
1620
numColumns?: number;
1721
/**
@@ -29,38 +33,42 @@ export interface GridViewProps {
2933
/**
3034
* whether to keep the items initial size when orientation changes,
3135
* in which case the apt number of columns will be calculated automatically.
36+
* Irrelevant when passing maxItemWidth
3237
*/
3338
keepItemSize?: boolean;
3439
}
35-
interface State {
40+
interface GridViewState {
3641
viewWidth: number;
3742
numColumns: number;
43+
itemSize: number;
3844
}
39-
declare type ExistProps = GridViewProps & State;
4045
/**
4146
* @description: A auto-generated grid view that calculate item size according to given props
4247
* @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/GridViewScreen.tsx
4348
*/
44-
declare class GridView extends UIComponent<GridViewProps, State> {
49+
declare class GridView extends UIComponent<GridViewProps, GridViewState> {
4550
static displayName: string;
4651
static defaultProps: {
4752
numColumns: number;
4853
itemSpacing: number;
4954
};
50-
private itemSize?;
5155
private dimensionsChangeListener;
52-
constructor(props: ExistProps);
53-
static getDerivedStateFromProps(nextProps: ExistProps, prevState: State): {
56+
state: {
57+
viewWidth: number;
58+
numColumns: number;
59+
itemSize: number;
60+
};
61+
static getDerivedStateFromProps(nextProps: GridViewProps, prevState: GridViewState): {
5462
viewWidth: number;
5563
numColumns: number | undefined;
5664
} | null;
5765
componentDidMount(): void;
5866
componentWillUnmount(): void;
5967
onOrientationChanged: () => void;
60-
shouldUpdateItemSize(): boolean;
6168
getDefaultViewWidth(): number;
62-
getCalculatedNumOfColumns(): number;
63-
getItemSize(): number | undefined;
69+
getGridContainerWidth(): number;
70+
calcNumberOfColumns(): number;
71+
calcItemSize(): number;
6472
getThemeColor(placeColor: string): any;
6573
renderLastItemOverlay(): JSX.Element | undefined;
6674
renderItem: (item: GridListItemProps, index: number) => JSX.Element;

src/components/gridView/index.tsx

Lines changed: 58 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import _ from 'lodash';
22
import React from 'react';
33
import {StyleSheet} from 'react-native';
4-
// TODO: we should use asBaseComponent here instead of using UIComponent directly
54
import {Colors, Spacings} from 'style';
5+
// TODO: we should use asBaseComponent here instead of using UIComponent directly
66
import UIComponent from '../../commons/UIComponent';
77
import View from '../view';
88
import Text from '../text';
99
import {Constants} from 'helpers';
1010
import GridListItem, {GridListItemProps} from '../gridListItem';
1111
import {formatLastItemLabel} from '../../helpers/FormattingPresenter';
1212

13+
const DEFAULT_NUM_COLUMNS = 3;
14+
const DEFAULT_ITEM_SPACINGS = Spacings.s4;
1315
export interface GridViewProps {
1416
/**
1517
* The list of items based on GridListItem props
@@ -20,7 +22,11 @@ export interface GridViewProps {
2022
*/
2123
viewWidth?: number;
2224
/**
23-
* Number of items to show in a row
25+
* Allow a responsive item width to the maximum item width
26+
*/
27+
maxItemWidth?: number;
28+
/**
29+
* Number of items to show in a row (ignored when passing maxItemWidth)
2430
*/
2531
numColumns?: number;
2632
/**
@@ -38,44 +44,38 @@ export interface GridViewProps {
3844
/**
3945
* whether to keep the items initial size when orientation changes,
4046
* in which case the apt number of columns will be calculated automatically.
47+
* Ignored when passing 'maxItemWidth'
4148
*/
4249
keepItemSize?: boolean;
4350
}
4451

45-
interface State {
52+
interface GridViewState {
4653
viewWidth: number;
4754
numColumns: number;
55+
itemSize: number;
4856
}
4957

50-
51-
type ExistProps = GridViewProps & State
5258
/**
5359
* @description: A auto-generated grid view that calculate item size according to given props
5460
* @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/GridViewScreen.tsx
5561
*/
56-
class GridView extends UIComponent<GridViewProps, State> {
62+
class GridView extends UIComponent<GridViewProps, GridViewState> {
5763
static displayName = 'GridView';
5864

5965
static defaultProps = {
60-
numColumns: 3,
61-
itemSpacing: Spacings.s4
66+
numColumns: DEFAULT_NUM_COLUMNS,
67+
itemSpacing: DEFAULT_ITEM_SPACINGS
6268
};
6369

64-
private itemSize?: number;
6570
private dimensionsChangeListener: any;
6671

67-
constructor(props: ExistProps) {
68-
super(props);
69-
70-
this.state = {
71-
viewWidth: Math.floor(props.viewWidth || this.getDefaultViewWidth()),
72-
numColumns: props.numColumns
73-
};
74-
75-
this.itemSize = undefined;
76-
}
72+
state = {
73+
viewWidth: this.getGridContainerWidth(),
74+
numColumns: this.calcNumberOfColumns(),
75+
itemSize: this.calcItemSize()
76+
};
7777

78-
static getDerivedStateFromProps(nextProps: ExistProps, prevState: State) {
78+
static getDerivedStateFromProps(nextProps: GridViewProps, prevState: GridViewState) {
7979
let viewWidth;
8080
let numColumns;
8181
if (nextProps.viewWidth && Math.floor(nextProps.viewWidth) !== prevState.viewWidth) {
@@ -101,38 +101,44 @@ class GridView extends UIComponent<GridViewProps, State> {
101101
}
102102

103103
onOrientationChanged = () => {
104+
const {keepItemSize} = this.props;
105+
const {itemSize} = this.state;
106+
104107
if (!this.props.viewWidth) {
105-
this.setState({viewWidth: Math.floor(this.getDefaultViewWidth()), numColumns: this.getCalculatedNumOfColumns()});
108+
const newItemSize = keepItemSize ? itemSize : this.calcItemSize();
109+
this.setState({
110+
viewWidth: Math.floor(this.getDefaultViewWidth()),
111+
numColumns: this.calcNumberOfColumns(),
112+
itemSize: newItemSize
113+
});
106114
}
107115
};
108116

109-
shouldUpdateItemSize() {
110-
return !this.itemSize || !this.props.keepItemSize;
117+
getDefaultViewWidth() {
118+
return Constants.screenWidth - Spacings.s5 * 2;
111119
}
112120

113-
getDefaultViewWidth() {
114-
// @ts-ignore
115-
return Constants.screenWidth - (Spacings.page * 2);
121+
getGridContainerWidth() {
122+
return Math.floor(this.props.viewWidth || this.getDefaultViewWidth());
116123
}
117124

118-
getCalculatedNumOfColumns() {
119-
const {itemSpacing, numColumns} = this.props as ExistProps;
125+
calcNumberOfColumns() {
126+
const {numColumns, itemSpacing = DEFAULT_ITEM_SPACINGS, maxItemWidth} = this.props;
127+
const containerWidth = this.getGridContainerWidth();
120128

121-
if (!this.shouldUpdateItemSize() && Constants.orientation === 'landscape' && this.itemSize && itemSpacing) {
122-
const numberOfColumns = this.getDefaultViewWidth() / (this.itemSize + itemSpacing);
123-
return Math.floor(numberOfColumns);
129+
if (maxItemWidth) {
130+
return Math.ceil((containerWidth + itemSpacing) / (maxItemWidth + itemSpacing));
131+
} else {
132+
return numColumns || DEFAULT_NUM_COLUMNS;
124133
}
125-
return numColumns;
126134
}
127135

128-
getItemSize() {
129-
const {itemSpacing} = this.props;
130-
const {viewWidth, numColumns} = this.state;
136+
calcItemSize() {
137+
const {itemSpacing = DEFAULT_ITEM_SPACINGS} = this.props;
138+
const containerWidth = this.getGridContainerWidth();
139+
const numColumns = this.calcNumberOfColumns();
131140

132-
if (this.shouldUpdateItemSize() && viewWidth && itemSpacing && numColumns) {
133-
return (viewWidth - itemSpacing * (numColumns - 1)) / numColumns;
134-
}
135-
return this.itemSize;
141+
return (containerWidth - itemSpacing * (numColumns - 1)) / numColumns;
136142
}
137143

138144
getThemeColor(placeColor: string) {
@@ -154,10 +160,7 @@ class GridView extends UIComponent<GridViewProps, State> {
154160
return;
155161
}
156162

157-
const imageBorderRadius = _.chain(items)
158-
.first()
159-
.get('imageProps.borderRadius')
160-
.value();
163+
const imageBorderRadius = _.chain(items).first().get('imageProps.borderRadius').value();
161164
return (
162165
<View
163166
style={[
@@ -173,36 +176,40 @@ class GridView extends UIComponent<GridViewProps, State> {
173176
}
174177

175178
renderItem = (item: GridListItemProps, index: number) => {
179+
const {itemSize} = this.state;
176180
const {items, itemSpacing} = this.props;
177-
const {numColumns} = this.state;
181+
182+
const {numColumns = DEFAULT_NUM_COLUMNS} = this.state;
178183
const itemsCount = _.size(items);
179184
const rowCount = itemsCount / numColumns;
180185
const isLastItemInRow = (index + 1) % numColumns === 0;
181186
const isLastRow = index + 1 > (rowCount - 1) * numColumns;
182187
const isLastItem = index === itemsCount - 1;
183-
const size = typeof item.itemSize === 'object'
184-
? {width: this.itemSize, height: _.get(item.itemSize, 'height', this.itemSize)}
185-
: this.itemSize;
188+
const size =
189+
typeof item.itemSize === 'object' ? {width: itemSize, height: item.itemSize?.height || itemSize} : itemSize;
186190
return (
187191
<GridListItem
188192
key={index}
189193
{...item}
190194
itemSize={size}
191-
containerStyle={[!isLastItemInRow && {marginRight: itemSpacing},
192-
!isLastRow && {marginBottom: itemSpacing}, item.containerStyle]}
195+
containerStyle={[
196+
!isLastItemInRow && {marginRight: itemSpacing},
197+
!isLastRow && {marginBottom: itemSpacing},
198+
item.containerStyle
199+
]}
193200
>
194201
{isLastItem && this.renderLastItemOverlay()}
195202
</GridListItem>
196203
);
197204
};
198205

199206
render() {
207+
const {itemSize} = this.state;
200208
const {items, viewWidth} = this.props;
201-
this.itemSize = this.getItemSize();
202209

203210
return (
204211
<View style={[styles.container, {width: viewWidth ? Math.floor(viewWidth) : undefined}]}>
205-
{this.itemSize && _.map(items, this.renderItem)}
212+
{itemSize && _.map(items, this.renderItem)}
206213
</View>
207214
);
208215
}

0 commit comments

Comments
 (0)