@wq/material-web
Version:
Web bindings for @wq/material
190 lines (189 loc) • 6.39 kB
JavaScript
import React, { useMemo } from "react";
import { Field, getIn } from "formik";
import { Select as FMuiSelect } from "formik-mui";
import { MenuItem, Checkbox, ListItemText, ListSubheader } from "@mui/material";
import PropTypes from "prop-types";
import { useFormikContext } from "formik";
function ContextCheckbox({ value, field }) {
const { values } = useFormikContext();
const checked = (getIn(values, field) || []).some((val) => val === value);
return /*#__PURE__*/ React.createElement(Checkbox, {
checked: checked,
});
}
ContextCheckbox.propTypes = {
value: PropTypes.string,
field: PropTypes.string,
};
export default function Select({
choices,
label,
required,
native,
placeholder,
renderValue,
InputLabelProps,
...rest
}) {
const { name: fieldName, type, hint } = rest,
{ errors, touched } = useFormikContext(),
showError = !!getIn(errors, fieldName) && getIn(touched, fieldName),
multiple = type === "select";
if (multiple && !renderValue) {
renderValue = (sel) => sel.map(getLabel).join(", ");
}
if (placeholder && !renderValue) {
renderValue = (sel) => getLabel(sel) || sel || placeholder;
}
const getLabel = useMemo(() => {
const labels = {};
choices.forEach(({ name, label }) => {
labels[name] = label;
});
return (name) => labels[name];
}, [choices]);
const choiceGroups = useMemo(() => {
const choiceGroups = {};
choices.forEach((choice) => {
const group = choice.group || "";
if (!choiceGroups[group]) {
choiceGroups[group] = [];
}
choiceGroups[group].push(choice);
});
return choiceGroups;
}, [choices]);
const Option = useMemo(
() =>
native
? ({ value, children }) =>
/*#__PURE__*/ React.createElement(
"option",
{
value: value,
},
children
)
: ({ children, ...rest }) =>
/*#__PURE__*/ React.createElement(
MenuItem,
{
...rest,
},
multiple &&
/*#__PURE__*/ React.createElement(
ContextCheckbox,
{
value: rest["data-value"],
field: fieldName,
}
),
/*#__PURE__*/ React.createElement(ListItemText, {
primary: children,
"data-value": rest["data-value"],
primaryTypographyProps: {
"data-value": rest["data-value"],
},
})
),
[native, multiple, fieldName]
);
const renderChoices = (choices) =>
choices.map(({ name, label }) =>
/*#__PURE__*/ React.createElement(
Option,
{
key: name,
value: name,
},
label
)
);
const renderGroups = native
? (choiceGroups) =>
Object.entries(choiceGroups).map(([group, choices]) =>
group
? /*#__PURE__*/ React.createElement(
"optgroup",
{
key: group,
label: group,
},
renderChoices(choices)
)
: /*#__PURE__*/ React.createElement(
React.Fragment,
null,
renderChoices(choices)
)
)
: (choiceGroups) => {
const flattened = [];
Object.entries(choiceGroups).forEach(([group, choices]) => {
if (group) {
flattened.push(
/*#__PURE__*/ React.createElement(
ListSubheader,
{
style: {
backgroundColor: "white",
},
},
group
)
);
}
flattened.push(...renderChoices(choices));
});
return flattened;
};
return /*#__PURE__*/ React.createElement(
Field,
{
component: FMuiSelect,
formControl: {
fullWidth: true,
margin: "dense",
},
inputLabel: {
required,
error: showError,
...InputLabelProps,
},
formHelperText: {
children: hint,
},
multiple: multiple,
required: required,
native: native,
label: label,
renderValue: renderValue,
displayEmpty: !!placeholder,
...(InputLabelProps && InputLabelProps.shrink
? {
notched: true,
}
: {}),
...rest,
},
!multiple &&
/*#__PURE__*/ React.createElement(
Option,
{
value: "",
disabled: !!required,
},
required ? "Select one..." : "(No Selection)"
),
renderGroups(choiceGroups)
);
}
Select.propTypes = {
choices: PropTypes.arrayOf(PropTypes.object),
label: PropTypes.string,
placeholder: PropTypes.string,
required: PropTypes.bool,
native: PropTypes.bool,
renderValue: PropTypes.func,
InputLabelProps: PropTypes.object,
};