@render-props/choices
Version:
A state container which provides an interface for making selections from a group of choices. The `Choices` component itself is a context provider which can be used with the `Choice` and `ChoicesConsumer` components for deep-tree selections. It does not ha
274 lines (238 loc) • 7.57 kB
JavaScript
'use strict'
var _interopRequireDefault = require('@babel/runtime/helpers/interopRequireDefault')
exports.__esModule = true
exports.default = Choices
var _objectSpread2 = _interopRequireDefault(
require('@babel/runtime/helpers/objectSpread')
)
var _react = _interopRequireDefault(require('react'))
var _propTypes = _interopRequireDefault(require('prop-types'))
var _utils = require('@render-props/utils')
var _items = _interopRequireDefault(require('@render-props/items'))
var _ChoicesContext = _interopRequireDefault(require('./ChoicesContext'))
var _invariants = require('./invariants')
var _utils2 = require('./utils')
/**
import Choices, {Choice} from '@render-props/choices'
const FavoritePets = props => (
<Choices
initialChoices={new Set(['cat', 'dog', 'turtle'])}
initialSelections={new Set(['cat'])}
minChoices={1}
minSelections={1}
maxSelections={2}
onBoundMaxSelections={
function ({selections, select, deselect}) {
selections = Array.from(selections)
deselect(selections.shift())
select(selections.pop())
}
}
>
{PetsControl}
</Choices>
)
const PetsControl = ({
addChoice,
deleteChoice,
setChoices,
clearChoices,
isChoice,
select,
deselect,
setSelections,
clearSelections,
isSelected,
selections,
choices
}) => (
<div style={{borderWidth: 1}}>
<span>
Number of favorites: {selections.size}
</span>
{
Array.from(choices).map(pet => (
<Choice key={pet} value={pet}>
{
function ({select, deselect, toggle, isSelected}) {
return (
<button
onClick={toggle}
style={
isSelected
? {backgroundColor: 'green'}
: {backgroundColor: 'grey'}
}
>
{pet}
</button>
)
}
}
</Choice>
))
}
</div>
)
*/
function Choices(props) {
const nextProps = {
children: props.children,
}
function _ref(selectionsContext) {
nextProps.select = selectionsContext.addItem
nextProps.deselect = selectionsContext.deleteItem
nextProps.setSelections = selectionsContext.setItems
nextProps.isSelected = selectionsContext.includes
nextProps.clearSelections = selectionsContext.clearItems
nextProps.selections = selectionsContext.items
return _react.default.createElement(Choices_, nextProps)
}
return _react.default.createElement(
_items.default,
{
initialItems: props.initialChoices,
minItems: props.minChoices,
maxItems: props.maxChoices,
onAdd: props.onAddChoice,
onDelete: props.onDeleteChoice,
onChange: props.onChoicesChange,
onBoundMax: (0, _utils2.boundChoices)(props.onBoundMinChoices),
onBoundMin: (0, _utils2.boundChoices)(props.onBoundMaxChoices),
},
function(choicesContext) {
nextProps.addChoice = choicesContext.addItem
nextProps.deleteChoice = choicesContext.deleteItem
nextProps.clearChoices = choicesContext.clearItems
nextProps.setChoices = choicesContext.setItems
nextProps.isChoice = choicesContext.includes
nextProps.choices = choicesContext.items
return _react.default.createElement(
_items.default,
{
initialItems: props.initialSelections,
minItems: props.minSelections,
maxItems: props.maxSelections,
onAdd: props.onSelect,
onDelete: props.onDeselect,
onChange: props.onChange,
onBoundMin: (0, _utils2.boundSelections)(props.onBoundMinSelections),
onBoundMax: (0, _utils2.boundSelections)(props.onBoundMaxSelections),
},
_ref
)
}
)
}
Choices.propTypes = {
children: _propTypes.default.func.isRequired,
// choices
initialChoices: _propTypes.default.oneOfType([
_propTypes.default.array,
_propTypes.default.instanceOf(Set),
]),
minChoices: _propTypes.default.number,
maxChoices: _propTypes.default.number,
onAddChoice: _propTypes.default.func,
onDeleteChoice: _propTypes.default.func,
onChoicesChange: _propTypes.default.func,
onBoundMinChoices: _propTypes.default.func,
onBoundMaxChoices: _propTypes.default.func,
// selections
initialSelections: _propTypes.default.oneOfType([
_propTypes.default.array,
_propTypes.default.instanceOf(Set),
]),
minSelections: _propTypes.default.number,
maxSelections: _propTypes.default.number,
onSelect: _propTypes.default.func,
onDeselect: _propTypes.default.func,
onChange: _propTypes.default.func,
onBoundMinSelections: _propTypes.default.func,
onBoundMaxSelections: _propTypes.default.func,
}
class Choices_ extends _react.default.Component {
constructor(props) {
super(props)
this.select = (...items) => {
this.ifChoicesInclude(items)
return this.props.select(...items)
}
this.deselect = (...items) => {
this.ifChoicesInclude(items)
return this.props.deselect(...items)
}
this.toggle = item =>
this.props.isSelected(item) ? this.deselect(item) : this.select(item)
this.setSelections = items => {
this.ifChoicesInclude(items)
return this.props.setSelections(items)
}
this.deleteChoice = (...items) => {
const {isSelected, minSelections, deleteChoice} = this.props
const whichSize =
this.props.selections.size === void 0 ? 'length' : 'size' // removes all selections referencing this choice
for (let x = 0; x < items.length; x++) {
const item = items[x]
if (isSelected(item)) {
this.deselect(item)
if (minSelections > this.props.selections[whichSize] - 1) {
return
}
}
} // deletes the choice
return deleteChoice(...items)
}
this.setChoices = items => {
const diff = Array.from(items).filter(item => this.props.isChoice(item))
if (diff.length) {
this.props.deleteChoice(...diff)
}
return this.props.setChoices(items)
}
this.clearChoices = () => {
this.props.clearSelections()
this.props.clearChoices()
}
this.choicesContext = {
select: this.select,
deselect: this.deselect,
toggle: this.toggle,
setSelections: this.setSelections,
clearSelections: this.props.clearSelections,
isSelected: this.props.isSelected,
addChoice: this.props.addChoice,
deleteChoice: this.deleteChoice,
setChoices: this.setChoices,
clearChoices: this.clearChoices,
isChoice: this.props.isChoice,
selections: null,
choices: null,
}
}
ifChoicesInclude(items) {
if (typeof process !== void 0 && process.env.NODE_ENV !== 'production') {
for (let x = 0; x < items.length; x++) {
;(0, _invariants.includesInvariant)(this.props.choices, items[x])
}
}
}
render() {
if (
this.choicesContext.selections !== this.props.selections ||
this.choicesContext.choices !== this.props.choices
) {
this.choicesContext = (0, _objectSpread2.default)({}, this.choicesContext)
}
this.choicesContext.selections = this.props.selections
this.choicesContext.choices = this.props.choices
return _react.default.createElement(
_ChoicesContext.default.Provider,
{
value: this.choicesContext,
},
this.props.children(this.choicesContext)
)
}
}
Choices_.displayName = 'Choices'