Instantly share code, notes, and snippets.
Created
December 26, 2018 04:27
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save trinadhkoya/247266e05ad348c7df0134be30ce72ce to your computer and use it in GitHub Desktop.
Multiple Button select
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import TagSelect from './src/TagSelect'; | |
import TagSelectItem from './src/TagSelectItem'; | |
export { | |
TagSelect, | |
TagSelectItem | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react' | |
import PropTypes from 'prop-types' | |
import { | |
View, | |
ViewPropTypes, | |
StyleSheet | |
} from 'react-native' | |
import TagSelectItem from './TagSelectItem' | |
class TagSelect extends React.Component { | |
static propTypes = { | |
// Pre-selected values | |
value: PropTypes.array, | |
// Objet keys | |
labelAttr: PropTypes.string, | |
keyAttr: PropTypes.string, | |
// Available data | |
data: PropTypes.array, | |
// validations | |
max: PropTypes.number, | |
// Callbacks | |
onMaxError: PropTypes.func, | |
onItemPress: PropTypes.func, | |
// Styles | |
containerStyle: ViewPropTypes.style | |
} | |
static defaultProps = { | |
value: [], | |
labelAttr: 'label', | |
keyAttr: 'id', | |
data: [], | |
max: null, | |
onMaxError: null, | |
onItemPress: null, | |
containerStyle: {} | |
} | |
state = { | |
value: {} | |
} | |
componentDidMount () { | |
const value = {} | |
this.props.value.forEach((val) => { | |
value[val[[this.props.keyAttr]] || val] = val | |
}) | |
this.setState({ value }) | |
} | |
/** | |
* @description Return the number of items selected | |
* @return {Number} | |
*/ | |
get totalSelected () { | |
return Object.keys(this.state.value).length | |
} | |
/** | |
* @description Return the items selected | |
* @return {Array} | |
*/ | |
get itemsSelected () { | |
const items = [] | |
Object.entries(this.state.value).forEach(([key]) => { | |
items.push(this.state.value[key]) | |
}) | |
return items | |
} | |
/** | |
* @description Callback after select an item | |
* @param {Object} item | |
* @return {Void} | |
*/ | |
handleSelectItem = (item) => { | |
const key = item[this.props.keyAttr] || item | |
const value = { ...this.state.value } | |
const found = this.state.value[key] | |
// Item is on array, so user is removing the selection | |
if (found) { | |
delete value[key] | |
} else { | |
// User is adding but has reached the max number permitted | |
if (this.props.max && this.totalSelected >= this.props.max) { | |
if (this.props.onMaxError) { | |
return this.props.onMaxError() | |
} | |
} | |
value[key] = item | |
} | |
return this.setState({ value }, () => { | |
if (this.props.onItemPress) { | |
this.props.onItemPress(item) | |
} | |
}) | |
} | |
render () { | |
return ( | |
<View | |
style={[ | |
styles.container, | |
this.props.containerStyle | |
]} | |
> | |
{this.props.data.map((i) => { | |
return ( | |
<TagSelectItem | |
{...this.props} | |
label={i[this.props.labelAttr] ? i[this.props.labelAttr] : i} | |
key={i[this.props.keyAttr] ? i[this.props.keyAttr] : i} | |
onPress={this.handleSelectItem.bind(this, i)} | |
selected={(this.state.value[i[this.props.keyAttr]] || this.state.value[i]) && true} | |
/> | |
) | |
})} | |
</View> | |
) | |
} | |
} | |
const styles = StyleSheet.create({ | |
container: { | |
flexDirection: 'row', | |
flexWrap: 'wrap', | |
alignItems: 'flex-start' | |
} | |
}) | |
export default TagSelect |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react' | |
import PropTypes from 'prop-types' | |
import { | |
View, | |
ViewPropTypes, | |
Text, | |
StyleSheet, | |
TouchableOpacity | |
} from 'react-native' | |
TagSelectItem.propTypes = { | |
label: PropTypes.string, | |
// Callbacks | |
onPress: PropTypes.func, | |
// Indicate if the item is selected | |
selected: PropTypes.bool, | |
// Touch | |
activeOpacity: PropTypes.number, | |
// Styles | |
theme: PropTypes.oneOf([ | |
'default', | |
'inverse', | |
'success', | |
'info', | |
'danger', | |
'warning' | |
]), | |
itemStyle: ViewPropTypes.style, | |
itemStyleSelected: ViewPropTypes.style, | |
itemLabelStyle: PropTypes.any, | |
itemLabelStyleSelected: PropTypes.any | |
} | |
TagSelectItem.defaultProps = { | |
label: null, | |
onPress: null, | |
selected: false, | |
touchComponent: 'TouchableOpacity', | |
activeOpacity: 0.5, | |
theme: 'default', | |
itemStyle: null, | |
itemStyleSelected: null, | |
itemLabelStyle: null, | |
itemLabelStyleSelected: null | |
} | |
function TagSelectItem (props) { | |
return ( | |
<View style={styles.container}> | |
<TouchableOpacity | |
onPress={props.onPress} | |
activeOpacity={props.activeOpacity} | |
> | |
<View | |
style={[ | |
styles.inner, | |
styles[`${props.theme}Inner`], | |
props.itemStyle, | |
props.selected && styles[`${props.theme}InnerSelected`], | |
props.selected && props.itemStyleSelected | |
]} | |
> | |
<Text | |
numberOfLines={1} | |
style={[ | |
styles[`${props.theme}LabelText`], | |
props.itemLabelStyle, | |
props.selected && styles[`${props.theme}LabelTextSelected`], | |
props.selected && props.itemLabelStyleSelected | |
]} | |
> | |
{props.label} | |
</Text> | |
</View> | |
</TouchableOpacity> | |
</View> | |
) | |
} | |
const styles = StyleSheet.create({ | |
container: { | |
marginBottom: 10, | |
marginRight: 10 | |
}, | |
inner: { | |
padding: 10, | |
borderWidth: 1, | |
borderRadius: 6 | |
}, | |
defaultInner: { | |
backgroundColor: '#f8f9fa', | |
borderColor: '#f8f9fa' | |
}, | |
defaultInnerSelected: { | |
backgroundColor: '#6c757d', | |
borderColor: '#6c757d' | |
}, | |
defaultLabelText: { | |
color: '#333333' | |
}, | |
defaultLabelTextSelected: { | |
color: '#FFF' | |
}, | |
inverseInner: { | |
backgroundColor: '#FFFFFF', | |
borderColor: '#343a40' | |
}, | |
inverseInnerSelected: { | |
backgroundColor: '#343a40', | |
borderColor: '#343a40' | |
}, | |
inverseLabelText: { | |
color: '#343a40' | |
}, | |
inverseLabelTextSelected: { | |
color: '#FFF' | |
}, | |
successInner: { | |
backgroundColor: '#FFFFFF', | |
borderColor: '#28a745' | |
}, | |
successInnerSelected: { | |
backgroundColor: '#28a745', | |
borderColor: '#28a745' | |
}, | |
successLabelText: { | |
color: '#28a745' | |
}, | |
successLabelTextSelected: { | |
color: '#FFF' | |
}, | |
infoInner: { | |
backgroundColor: '#FFFFFF', | |
borderColor: '#007BFF' | |
}, | |
infoInnerSelected: { | |
backgroundColor: '#007bff', | |
borderColor: '#007BFE' | |
}, | |
infoLabelText: { | |
color: '#004085' | |
}, | |
infoLabelTextSelected: { | |
color: '#FFF' | |
}, | |
warningInner: { | |
backgroundColor: '#FFFFFF', | |
borderColor: '#ffc107' | |
}, | |
warningInnerSelected: { | |
backgroundColor: '#ffc107', | |
borderColor: '#ffc107' | |
}, | |
warningLabelText: { | |
color: '#333' | |
}, | |
warningLabelTextSelected: { | |
color: '#333' | |
}, | |
dangerInner: { | |
backgroundColor: '#FFFFFF', | |
borderColor: '#dc3545' | |
}, | |
dangerInnerSelected: { | |
backgroundColor: '#dc3545', | |
borderColor: '#dc3545' | |
}, | |
dangerLabelText: { | |
color: '#dc3545' | |
}, | |
dangerLabelTextSelected: { | |
color: '#FFF' | |
} | |
}) | |
export default TagSelectItem |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment