UNPKG

rn-multiple-choice

Version:

A cross-platform (iOS / Android) single and multiple-choice React Native component.

186 lines (158 loc) 4.39 kB
"use strict"; import React from "react"; import PropTypes from "prop-types"; import { Text, TouchableOpacity, View, Image, ListView, ViewPropTypes } from "react-native"; import BaseComponent from "./BaseComponent"; import Styles from "./styles"; const propTypes = { options: PropTypes.array.isRequired, selectedOptions: PropTypes.array, maxSelectedOptions: PropTypes.number, onSelection: PropTypes.func, renderIndicator: PropTypes.func, renderSeparator: PropTypes.func, renderRow: PropTypes.func, renderText: PropTypes.func, style: ViewPropTypes.style, optionStyle: ViewPropTypes.style, disabled: PropTypes.bool }; const defaultProps = { options: [], selectedOptions: [], onSelection(option) {}, style: {}, optionStyle: {}, disabled: false }; class MultipleChoice extends BaseComponent { constructor(props) { super(props); const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => true }); this.ds = ds; this.state = { dataSource: ds.cloneWithRows(this.props.options), selectedOptions: this.props.selectedOptions || [], disabled: this.props.disabled }; this._bind( "_renderRow", "_selectOption", "_isSelected", "_updateSelectedOptions" ); } componentWillReceiveProps(nextProps) { this._updateSelectedOptions(nextProps.selectedOptions); this.setState({ disabled: nextProps.disabled }); } _updateSelectedOptions(selectedOptions) { this.setState({ selectedOptions, dataSource: this.ds.cloneWithRows(this.props.options) }); } _validateMaxSelectedOptions() { const maxSelectedOptions = this.props.maxSelectedOptions; const selectedOptions = this.state.selectedOptions; if ( maxSelectedOptions && selectedOptions.length > 0 && selectedOptions.length >= maxSelectedOptions ) { selectedOptions.splice(0, 1); } this._updateSelectedOptions(selectedOptions); } _selectOption(selectedOption) { let selectedOptions = this.state.selectedOptions; const index = selectedOptions.indexOf(selectedOption); if (index === -1) { this._validateMaxSelectedOptions(); selectedOptions.push(selectedOption); } else { selectedOptions.splice(index, 1); } this._updateSelectedOptions(selectedOptions); //run callback this.props.onSelection(selectedOption); } _isSelected(option) { return this.state.selectedOptions.indexOf(option) !== -1; } _renderIndicator(option) { if (this._isSelected(option)) { if (typeof this.props.renderIndicator === "function") { return this.props.renderIndicator(option); } return ( <Image style={Styles.optionIndicatorIcon} source={require("./assets/images/check.png")} /> ); } } _renderSeparator(option) { if (typeof this.props.renderSeparator === "function") { return this.props.renderSeparator(option); } return <View style={Styles.separator} />; } _renderText(option) { if (typeof this.props.renderText === "function") { return this.props.renderText(option); } return <Text>{option}</Text>; } _renderRow(option) { if (typeof this.props.renderRow === "function") { return this.props.renderRow(option); } const disabled = this.state.disabled; return ( <View style={this.props.optionStyle}> <TouchableOpacity activeOpacity={disabled ? 1 : 0.7} onPress={ !disabled ? () => { this._selectOption(option); } : null }> <View> <View style={Styles.row}> <View style={Styles.optionLabel}>{this._renderText(option)}</View> <View style={Styles.optionIndicator}> {this._renderIndicator(option)} </View> </View> </View> </TouchableOpacity> {this._renderSeparator(option)} </View> ); } render() { return ( <ListView style={[Styles.list, this.props.style]} dataSource={this.state.dataSource} renderRow={this._renderRow} /> ); } } MultipleChoice.propTypes = propTypes; MultipleChoice.defaultProps = defaultProps; module.exports = MultipleChoice;