decap-cms-widget-select
Version:
Selectable values widget for Decap CMS.
138 lines • 4.1 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { Map, List, fromJS } from 'immutable';
import find from 'lodash/find';
import Select from 'react-select';
import { reactSelectStyles } from 'decap-cms-ui-default';
import { validations } from 'decap-cms-lib-widgets';
import { jsx as ___EmotionJSX } from "@emotion/react";
function optionToString(option) {
return option && (typeof option.value === 'number' || typeof option.value === 'string') ? option.value : null;
}
function convertToOption(raw) {
if (typeof raw === 'string') {
return {
label: raw,
value: raw
};
}
return Map.isMap(raw) ? raw.toJS() : raw;
}
function getSelectedValue({
value,
options,
isMultiple
}) {
if (isMultiple) {
const selectedOptions = List.isList(value) ? value.toJS() : value;
if (!selectedOptions || !Array.isArray(selectedOptions)) {
return null;
}
return selectedOptions.map(i => options.find(o => o.value === (i.value || i))).filter(Boolean).map(convertToOption);
} else {
return find(options, ['value', value]) || null;
}
}
export default class SelectControl extends React.Component {
static propTypes = {
onChange: PropTypes.func.isRequired,
value: PropTypes.node,
forID: PropTypes.string.isRequired,
classNameWrapper: PropTypes.string.isRequired,
setActiveStyle: PropTypes.func.isRequired,
setInactiveStyle: PropTypes.func.isRequired,
field: ImmutablePropTypes.contains({
options: ImmutablePropTypes.listOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number, ImmutablePropTypes.contains({
label: PropTypes.string.isRequired,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired
})])).isRequired
})
};
isValid = () => {
const {
field,
value,
t
} = this.props;
const min = field.get('min');
const max = field.get('max');
if (!field.get('multiple')) {
return {
error: false
};
}
const error = validations.validateMinMax(t, field.get('label', field.get('name')), value, min, max);
return error ? {
error
} : {
error: false
};
};
handleChange = selectedOption => {
const {
onChange,
field
} = this.props;
const isMultiple = field.get('multiple', false);
const isEmpty = isMultiple ? !selectedOption?.length : !selectedOption;
if (field.get('required') && isEmpty && isMultiple) {
onChange(List());
} else if (isEmpty) {
onChange(null);
} else if (isMultiple) {
const options = selectedOption.map(optionToString);
onChange(fromJS(options));
} else {
onChange(optionToString(selectedOption));
}
};
componentDidMount() {
// Manually validate PropTypes - React 19 breaking change
PropTypes.checkPropTypes(SelectControl.propTypes, this.props, 'prop', 'SelectControl');
const {
field,
onChange,
value
} = this.props;
if (field.get('required') && field.get('multiple')) {
if (value && !List.isList(value)) {
onChange(fromJS([value]));
} else if (!value) {
onChange(fromJS([]));
}
}
}
render() {
const {
field,
value,
forID,
classNameWrapper,
setActiveStyle,
setInactiveStyle
} = this.props;
const fieldOptions = field.get('options');
const isMultiple = field.get('multiple', false);
const isClearable = !field.get('required', true) || isMultiple;
const options = [...fieldOptions.map(convertToOption)];
const selectedValue = getSelectedValue({
options,
value,
isMultiple
});
return ___EmotionJSX(Select, {
inputId: forID,
value: selectedValue,
onChange: this.handleChange,
className: classNameWrapper,
onFocus: setActiveStyle,
onBlur: setInactiveStyle,
options: options,
styles: reactSelectStyles,
isMulti: isMultiple,
isClearable: isClearable,
placeholder: ""
});
}
}