-
Notifications
You must be signed in to change notification settings - Fork 735
Picker Refactor #1 #1000
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Picker Refactor #1 #1000
Changes from all commits
f72e066
e2edb61
1c4cf1a
f62aa77
9dd94bb
2e8666a
7b6bbe0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,9 @@ | ||
import _ from 'lodash'; | ||
import PropTypes from 'prop-types'; | ||
import React from 'react'; | ||
import React, {useCallback, useEffect, useMemo} from 'react'; | ||
import {StyleSheet} from 'react-native'; | ||
import {Colors, Typography} from '../../style'; | ||
import {BaseComponent} from '../../commons'; | ||
import * as Modifiers from '../../commons/modifiers'; | ||
import Assets from '../../assets'; | ||
import View from '../view'; | ||
import TouchableOpacity from '../touchableOpacity'; | ||
|
@@ -16,153 +16,138 @@ import Text from '../text'; | |
* @extendslink: docs/TouchableOpacity | ||
* @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/PickerScreen.js | ||
*/ | ||
class PickerItem extends BaseComponent { | ||
static displayName = 'Picker.Item'; | ||
|
||
static propTypes = { | ||
/** | ||
* Item's value | ||
*/ | ||
value: PropTypes.oneOfType([PropTypes.object, PropTypes.string, PropTypes.number]), | ||
/** | ||
* Item's label | ||
*/ | ||
label: PropTypes.string, | ||
/** | ||
* Custom function for the item label (e.g (value) => customLabel) | ||
*/ | ||
getItemLabel: PropTypes.func, | ||
/** | ||
* DEPRECATE: Function to return the value out of the item value prop when value is custom shaped. | ||
*/ | ||
getItemValue: PropTypes.func, | ||
/** | ||
* Is the item selected | ||
*/ | ||
isSelected: PropTypes.bool, | ||
/** | ||
* Pass to change the selected icon | ||
*/ | ||
selectedIcon: PropTypes.oneOfType([PropTypes.object, PropTypes.number]), | ||
/** | ||
* Pass to change the selected icon color | ||
*/ | ||
selectedIconColor: PropTypes.string, | ||
/** | ||
* Is the item disabled | ||
*/ | ||
disabled: PropTypes.bool, | ||
/** | ||
* Render custom item | ||
*/ | ||
renderItem: PropTypes.elementType, | ||
/** | ||
* Callback for onPress action | ||
*/ | ||
onPress: PropTypes.func, | ||
/** | ||
* Callback for onLayout event | ||
*/ | ||
onSelectedLayout: PropTypes.func | ||
}; | ||
|
||
|
||
constructor(props) { | ||
super(props); | ||
|
||
if (_.isPlainObject(props.value)) { | ||
const PickerItem = props => { | ||
const { | ||
renderItem, | ||
value, | ||
label, | ||
onPress, | ||
disabled, | ||
isSelected, | ||
selectedIcon = Assets.icons.check, | ||
selectedIconColor = Colors.primary, | ||
testID | ||
} = props; | ||
|
||
useEffect(() => { | ||
if (_.isPlainObject(value)) { | ||
console.warn('UILib Picker.Item will stop supporting passing object as value & label (e.g {value, label}) in the next major version. Please pass separate label and value props'); | ||
} | ||
} | ||
|
||
}, [value]); | ||
|
||
generateStyles() { | ||
this.styles = createStyles(this.props); | ||
} | ||
// TODO: deprecate the check for object | ||
const _onPress = useCallback(() => { | ||
// onPress(_.isObject(value) ? value : {value, label}); | ||
onPress(value); | ||
}, [value, onPress]); | ||
|
||
getLabel() { | ||
const {value, label} = this.props; | ||
const onSelectedLayout = useCallback((...args) => { | ||
_.invoke(props, 'onSelectedLayout', ...args); | ||
}, []); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. shouldn't we pass There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is internal usage. |
||
|
||
const getLabel = () => { | ||
if (_.isObject(value)) { | ||
return _.invoke(this.props, 'getItemLabel', value) || _.get(value, 'label'); | ||
return _.invoke(props, 'getItemLabel', value) || _.get(value, 'label'); | ||
} | ||
return label; | ||
} | ||
|
||
renderSelectedIndicator() { | ||
const { | ||
isSelected, | ||
disabled, | ||
selectedIcon = Assets.icons.check, | ||
selectedIconColor = Colors.primary | ||
} = this.props; | ||
}; | ||
|
||
const selectedIndicator = useMemo(() => { | ||
if (isSelected) { | ||
return <Image source={selectedIcon} tintColor={disabled ? Colors.dark60 : selectedIconColor}/>; | ||
} | ||
} | ||
|
||
renderItem() { | ||
const {disabled} = this.props; | ||
}, [isSelected, disabled, selectedIcon, selectedIconColor]); | ||
|
||
const _renderItem = () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also in here, should be memoized There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't want to overkill the component with memoization. memoizing this will require wrapping Anyway, Personally I still not certain what is best practice with how often to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Gotcha. confusing topic indeed :) |
||
return ( | ||
<View style={this.styles.container} flex row spread centerV> | ||
<Text numberOfLines={1} style={[this.styles.labelText, disabled && this.styles.labelTextDisabled]}> | ||
{this.getLabel()} | ||
<View style={styles.container} flex row spread centerV> | ||
<Text numberOfLines={1} style={[styles.labelText, disabled && styles.labelTextDisabled]}> | ||
{getLabel()} | ||
</Text> | ||
{this.renderSelectedIndicator()} | ||
{selectedIndicator} | ||
</View> | ||
); | ||
} | ||
|
||
onSelectedLayout = (...args) => { | ||
_.invoke(this.props, 'onSelectedLayout', ...args); | ||
}; | ||
|
||
render() { | ||
const {renderItem, value, disabled, isSelected, testID} = this.props; | ||
|
||
return ( | ||
<TouchableOpacity | ||
activeOpacity={0.5} | ||
onPress={this.onPress} | ||
onLayout={isSelected ? this.onSelectedLayout : undefined} | ||
disabled={disabled} | ||
testID={testID} | ||
throttleTime={0} | ||
{...this.extractAccessibilityProps()} | ||
> | ||
{renderItem ? renderItem(value, this.props, this.getLabel()) : this.renderItem()} | ||
</TouchableOpacity> | ||
); | ||
return ( | ||
<TouchableOpacity | ||
activeOpacity={0.5} | ||
onPress={_onPress} | ||
onLayout={isSelected ? onSelectedLayout : undefined} | ||
disabled={disabled} | ||
testID={testID} | ||
throttleTime={0} | ||
{...Modifiers.extractAccessibilityProps(props)} | ||
> | ||
{renderItem ? renderItem(value, props, getLabel()) : _renderItem()} | ||
</TouchableOpacity> | ||
); | ||
}; | ||
|
||
const styles = StyleSheet.create({ | ||
container: { | ||
height: 56.5, | ||
paddingHorizontal: 23, | ||
borderColor: Colors.rgba(Colors.dark10, 0.1), | ||
borderBottomWidth: 1 | ||
}, | ||
labelText: { | ||
...Typography.text70, | ||
color: Colors.dark10, | ||
flex: 1, | ||
textAlign: 'left' | ||
}, | ||
labelTextDisabled: { | ||
color: Colors.dark60 | ||
} | ||
|
||
// TODO: deprecate the check for object | ||
onPress = () => { | ||
const {value, label, onPress} = this.props; | ||
onPress(_.isObject(value) ? value : {value, label}); | ||
// onPress(value); | ||
}; | ||
} | ||
|
||
function createStyles() { | ||
return StyleSheet.create({ | ||
container: { | ||
height: 56.5, | ||
paddingHorizontal: 23, | ||
borderColor: Colors.rgba(Colors.dark10, 0.1), | ||
borderBottomWidth: 1 | ||
}, | ||
labelText: { | ||
...Typography.text70, | ||
color: Colors.dark10, | ||
flex: 1, | ||
textAlign: 'left' | ||
}, | ||
labelTextDisabled: { | ||
color: Colors.dark60 | ||
} | ||
}); | ||
} | ||
}); | ||
|
||
PickerItem.displayName = 'Picker.Item'; | ||
PickerItem.propTypes = { | ||
/** | ||
* Item's value | ||
*/ | ||
value: PropTypes.oneOfType([PropTypes.object, PropTypes.string, PropTypes.number]), | ||
/** | ||
* Item's label | ||
*/ | ||
label: PropTypes.string, | ||
/** | ||
* Custom function for the item label (e.g (value) => customLabel) | ||
*/ | ||
getItemLabel: PropTypes.func, | ||
/** | ||
* DEPRECATE: Function to return the value out of the item value prop when value is custom shaped. | ||
*/ | ||
getItemValue: PropTypes.func, | ||
/** | ||
* Is the item selected | ||
*/ | ||
isSelected: PropTypes.bool, | ||
/** | ||
* Pass to change the selected icon | ||
*/ | ||
selectedIcon: PropTypes.oneOfType([PropTypes.object, PropTypes.number]), | ||
/** | ||
* Pass to change the selected icon color | ||
*/ | ||
selectedIconColor: PropTypes.string, | ||
/** | ||
* Is the item disabled | ||
*/ | ||
disabled: PropTypes.bool, | ||
/** | ||
* Render custom item | ||
*/ | ||
renderItem: PropTypes.elementType, | ||
/** | ||
* Callback for onPress action | ||
*/ | ||
onPress: PropTypes.func, | ||
/** | ||
* Callback for onLayout event | ||
*/ | ||
onSelectedLayout: PropTypes.func | ||
}; | ||
|
||
export default PickerItem; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Due tho some of the of the props in PickerItem (like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Understood :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO hints for
isObject
check, but inonPress
it's commented outThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed it in a following PR