Skip to content

Commit 8a7a304

Browse files
authored
GridListItem - horizontal alignment support (#3001)
* horizontal aligment support * passing the alignment style to the custom item and the image wrapper * added horizontalAlignment to api.json * fixed overley use case, changed the prop name * small refactor * removed textStyle, changed the children type * children can be an array * fixed review notes * fixed review notes
1 parent d6d1d7d commit 8a7a304

File tree

5 files changed

+79
-35
lines changed

5 files changed

+79
-35
lines changed

demo/src/screens/componentScreens/GridListScreen.tsx

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,27 +12,40 @@ import {
1212
GridListItem
1313
} from 'react-native-ui-lib';
1414
import products from '../../data/products';
15-
import {renderBooleanOption} from '../ExampleScreenPresenter';
15+
import {renderBooleanOption, renderMultipleSegmentOptions} from '../ExampleScreenPresenter';
1616

1717
class GridListScreen extends Component {
1818
state = {
1919
orientation: Constants.orientation,
20-
useGridListItem: false
20+
useGridListItem: true,
21+
horizontalAlignment: GridListItem.horizontalAlignment.left,
22+
overlayText: false,
23+
alignToStart: false
2124
};
2225

2326
renderHeader = () => {
2427
return (
2528
<View>
26-
<Text h1 marginB-s5>
29+
<Text h1 marginV-s3>
2730
GridList
2831
</Text>
2932
{renderBooleanOption.call(this, 'UseGridListItem', 'useGridListItem')}
33+
<Text h3 marginV-s2>
34+
GridListItem props
35+
</Text>
36+
{renderMultipleSegmentOptions.call(this, 'Horizontal Alignment:', 'horizontalAlignment', [
37+
{label: 'left', value: GridListItem.horizontalAlignment.left},
38+
{label: 'center', value: GridListItem.horizontalAlignment.center},
39+
{label: 'right', value: GridListItem.horizontalAlignment.right}
40+
])}
41+
{renderBooleanOption.call(this, 'Align to start:', 'alignToStart')}
42+
{renderBooleanOption.call(this, 'Use overlay text:', 'overlayText')}
3043
</View>
3144
);
3245
};
3346

3447
renderItem: GridListProps<(typeof products)[0]>['renderItem'] = ({item}) => {
35-
const {useGridListItem} = this.state;
48+
const {useGridListItem, horizontalAlignment, overlayText, alignToStart} = this.state;
3649

3750
if (useGridListItem) {
3851
return (
@@ -41,8 +54,10 @@ class GridListScreen extends Component {
4154
itemSize={{width: '100%', height: 200}}
4255
imageProps={{source: {uri: item.mediaUrl}}}
4356
title="Title"
44-
subtitle="Subitle"
45-
overlayText
57+
subtitle="Subtile"
58+
alignToStart={alignToStart}
59+
overlayText={overlayText}
60+
horizontalAlignment={horizontalAlignment}
4661
/>
4762
);
4863
} else {

src/components/gridListItem/gridListItem.api.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@
4040
"description": "Custom container styling for inline text"
4141
},
4242
{"name": "alignToStart", "type": "boolean", "description": "Should content be align to start", "default": "center"},
43+
{
44+
"name": "horizontalAlignment",
45+
"type": "HorizontalAlignment",
46+
"description": "Content horizontal alignment",
47+
"default": "center"
48+
},
4349
{"name": "containerStyle", "type": "ViewStyle", "description": "Custom container style"},
4450
{"name": "onPress", "type": "TouchableOpacityProps['onPress']", "description": "The item's action handler"},
4551
{

src/components/gridListItem/index.tsx

Lines changed: 50 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
import _ from 'lodash';
22
import React, {Component} from 'react';
33
import {StyleProp, StyleSheet, ViewStyle} from 'react-native';
4+
import memoize from 'memoize-one';
45
import * as Modifiers from '../../commons/modifiers';
56
import {Colors, Spacings, Typography} from 'style';
67
import View, {ViewProps} from '../view';
78
import TouchableOpacity, {TouchableOpacityProps} from '../touchableOpacity';
89
import Text from '../text';
910
import Image, {ImageProps} from '../image';
1011

12+
export enum HorizontalAlignment {
13+
left = 'left',
14+
center = 'center',
15+
right = 'right'
16+
}
17+
1118
export interface GridListItemProps {
1219
/**
1320
* Image props object for rendering an image item
@@ -85,6 +92,10 @@ export interface GridListItemProps {
8592
* Should content be align to start (default is center)
8693
*/
8794
alignToStart?: boolean;
95+
/**
96+
* Content horizontal alignment (default is center)
97+
*/
98+
horizontalAlignment?: HorizontalAlignment | `${HorizontalAlignment}`;
8899
/**
89100
* Custom container style
90101
*/
@@ -101,7 +112,7 @@ export interface GridListItemProps {
101112
* Test ID for component
102113
*/
103114
testID?: string;
104-
children?: React.ReactNode;
115+
children?: React.ReactElement | React.ReactElement[];
105116
}
106117

107118
interface RenderContentType {
@@ -120,6 +131,8 @@ interface RenderContentType {
120131
class GridListItem extends Component<GridListItemProps> {
121132
static displayName = 'GridListItem';
122133

134+
static horizontalAlignment = HorizontalAlignment;
135+
123136
static defaultProps = {
124137
itemSize: 48
125138
};
@@ -139,14 +152,34 @@ class GridListItem extends Component<GridListItemProps> {
139152
return {width: itemSize as number, height: itemSize as number};
140153
}
141154

155+
getContainerHorizontalAlignment = memoize(horizontalAlignment => {
156+
switch (horizontalAlignment) {
157+
case HorizontalAlignment.left:
158+
return 'flex-start';
159+
case HorizontalAlignment.right:
160+
return 'flex-end';
161+
case HorizontalAlignment.center:
162+
return 'center';
163+
default:
164+
undefined;
165+
}
166+
});
167+
142168
renderContent({text, typography, color, numberOfLines = 1, style, testID}: RenderContentType) {
143-
const {alignToStart} = this.props;
169+
const {alignToStart, horizontalAlignment} = this.props;
170+
const textAlign = alignToStart ? 'left' : horizontalAlignment;
144171
if (text) {
145172
return (
146173
<Text
147174
testID={testID}
148175
// @ts-ignore
149-
style={[style, Typography[typography], color && {color}, alignToStart && styles.contentAlignedToStart]}
176+
style={[
177+
style,
178+
//@ts-ignore
179+
Typography[typography],
180+
color && {color},
181+
{textAlign}
182+
]}
150183
numberOfLines={numberOfLines}
151184
>
152185
{text}
@@ -160,6 +193,7 @@ class GridListItem extends Component<GridListItemProps> {
160193
testID,
161194
imageProps,
162195
alignToStart,
196+
horizontalAlignment,
163197
containerStyle,
164198
containerProps = {},
165199
renderCustomItem,
@@ -181,32 +215,27 @@ class GridListItem extends Component<GridListItemProps> {
181215
onPress,
182216
renderOverlay
183217
} = this.props;
184-
const hasPress = _.isFunction(onPress);
185-
const hasOverlay = _.isFunction(renderOverlay);
186-
const Container = hasPress ? TouchableOpacity : View;
187-
const imageStyle = {...this.getItemSizeObj()};
188-
const width = _.get(imageStyle, 'width');
218+
const Container = onPress ? TouchableOpacity : View;
189219
const TextContainer = overlayText ? View : React.Fragment;
190-
const textContainerStyle = overlayText ? {style: [styles.overlayText, overlayTextContainerStyle]} : null;
191-
const imageBorderRadius = imageProps?.borderRadius;
220+
const itemSize = this.getItemSizeObj();
221+
const {width} = itemSize;
222+
const alignItems = alignToStart ? 'flex-start' : this.getContainerHorizontalAlignment(horizontalAlignment);
223+
const textContainerStyle = overlayText && {
224+
style: [styles.overlayText, overlayTextContainerStyle]
225+
};
192226
const {hitSlop, ...otherContainerProps} = containerProps; // eslint-disable-line
193227

194228
return (
195229
<Container
196-
style={[styles.container, alignToStart && styles.containerAlignedToStart, {width}, containerStyle]}
230+
style={[styles.container, {alignItems}, {width}, containerStyle]}
197231
{...otherContainerProps}
198-
onPress={hasPress ? this.onItemPress : undefined}
232+
onPress={onPress && this.onItemPress}
199233
accessible={renderCustomItem ? true : undefined}
200234
{...Modifiers.extractAccessibilityProps(this.props)}
201235
>
202-
{imageProps && (
203-
<View style={[{borderRadius: imageBorderRadius}, imageStyle]}>
204-
<Image {...imageProps} style={[imageStyle, imageProps?.style]}/>
205-
{children}
206-
</View>
207-
)}
236+
{imageProps && <Image style={itemSize} {...imageProps} customOverlayContent={children}/>}
208237
{!_.isNil(renderCustomItem) && <View style={{width}}>{renderCustomItem()}</View>}
209-
{hasOverlay && <View style={[styles.overlay, this.getItemSizeObj()]}>{renderOverlay?.()}</View>}
238+
{renderOverlay && <View style={[styles.overlay, itemSize]}>{renderOverlay()}</View>}
210239
<TextContainer {...textContainerStyle}>
211240
{this.renderContent({
212241
testID: `${testID}.title`,
@@ -243,9 +272,6 @@ const styles = StyleSheet.create({
243272
alignSelf: 'flex-start',
244273
alignItems: 'center'
245274
},
246-
containerAlignedToStart: {
247-
alignItems: 'flex-start'
248-
},
249275
title: {
250276
marginTop: Spacings.s1,
251277
textAlign: 'center',
@@ -259,18 +285,15 @@ const styles = StyleSheet.create({
259285
textAlign: 'center',
260286
...Typography.subtext
261287
},
262-
contentAlignedToStart: {
263-
textAlign: 'left'
264-
},
265288
overlay: {
266289
position: 'absolute',
267290
left: 0,
268291
top: 0
269292
},
270293
overlayText: {
271294
position: 'absolute',
272-
bottom: 10,
273-
left: 10
295+
bottom: 0,
296+
padding: Spacings.s3
274297
}
275298
});
276299

src/components/image/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export type ImageProps = Omit<RNImageProps, 'source'> &
7373
/**
7474
* Render an overlay with custom content
7575
*/
76-
customOverlayContent?: JSX.Element;
76+
customOverlayContent?: React.ReactElement | React.ReactElement[];
7777
/**
7878
* Default image source in case of an error
7979
*/

src/components/overlay/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export type OverlayTypes = Pick<ImageProps, 'borderRadius'> & {
3939
/**
4040
* Custom overlay content to be rendered on top of the image
4141
*/
42-
customContent?: JSX.Element;
42+
customContent?: React.ReactElement | React.ReactElement[];
4343
};
4444

4545
/**

0 commit comments

Comments
 (0)