Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
components.form.fields.InputSingleSelectFieldLocal.js Maven / Gradle / Ivy
import {
selectOption,
isOption,
filterOptionsByLabelWithExpression
} from "utils/option-utils";
import * as React from "react";
import { formatChoices } from 'components/form/utils/input-utils';
import { Autocomplete } from '@mui/material';
import { CircularProgress } from "@mui/material";
import TextField from "@mui/material/TextField";
import { isEmptyValue, useDebounce, withDefault } from "utils/utils";
import { validateField } from "components/form/utils/validate-utils";
import InputError from "components/form/fields/InputError";
import { useFieldInfo } from "hooks/field";
import { useFormInfo } from "hooks/form";
import { useNotifier } from "hooks/notification";
import { useTranslator } from "hooks/translator";
import { useAutoSubmitSignal } from "hooks/autosubmit";
import { useConfig } from "hooks/config";
import { fieldMinWidthStyle } from "components/form/utils/field-utils";
const InputSingleSelectFieldContent = (props) => {
const { augProps, fieldProps, info } = useFieldInfo()
const { valueType } = props.selectProps
const formInfo = useFormInfo()
const processKey = formInfo.processDefinition.key
const { t, translator } = useTranslator()
const [input, setInput, debouncedInput, skipDebounce] = useDebounce("", 500)
const [open, setOpen] = React.useState(false);
const [options, setOptions] = React.useState(info.options || []);
const [selected, setSelected] = React.useState(null); // this value is never allowed to be 'undefined'!!
const [highlighted, setHighlighted] = React.useState(null)
const [loading, setLoading ] = React.useState(false)
const notifier = useNotifier()
const { setFocus, setViewOpen } = props
const {signal} = useAutoSubmitSignal()
const {props: {taskRendering}} = useConfig()
const getOptions = () => formatChoices(translator, info.options, info.optionsKind, processKey)
const getOption = (valueOrOption) => {
const option = isOption(valueOrOption) ? valueOrOption : selectOption(info.options, valueOrOption)
if (!option) {
notifier.error("Unable to obtain the proper option for field " + info.field.label + " given selected value: " + JSON.stringify(selected))
console.log("selected %o is not in options %o", selected, info.options)
return null
} else
return { value: option.value, label: translator.toValue(option.value, option.label, processKey) }
}
// set selected value
React.useEffect(() => {
const selected = fieldProps.value
if (isEmptyValue(selected))
setSelected(null)
else {
const option = getOption(selected)
setSelected(option)
switch (valueType) {
case "option":
if (!isEmptyValue(selected) && !isOption(selected)){
console.debug("Select field %o will convert a value to an option", info.rpath)
handleChange(null, option)
}
break
case "value":
if (!isEmptyValue(fieldProps.value) && isOption(fieldProps.value)){
console.debug("Select field %o will convert a option to a value", info.rpath)
handleChange(null, option)
}
break
default:
}
}
}, [fieldProps.value]);
// filter options
React.useEffect(() => {
if (!(open))
return;
setLoading(true)
let active = true;
(async () => {
const options = getOptions()
const filteredOptions = debouncedInput === "" || debouncedInput === selected?.label
? options
: filterOptionsByLabelWithExpression(options, debouncedInput)
if (active) {
setOptions(filteredOptions)
setLoading(false)
}
})();
return () => { active = false; }
}, [open, debouncedInput]);
// update current load state
React.useEffect(() => {
if (!open) {
setOptions([]);
setLoading(false)
} else {
if (input !== debouncedInput)
setLoading(true)
skipDebounce()
}
}, [input, open])
function handleValidate(e, option) {
const error = validateField("select", fieldProps.required, e, option)
augProps.setError(error)
}
function handleBlur (e) {
setFocus(false)
fieldProps.onBlur(e)
handleValidate(e, selected)
signal()
}
function handleFocus(e) {
setFocus(true)
fieldProps.onFocus(e)
}
// store selected option locally and with formik. This HAS to be in two places, as a workaround for re-render behavior.
function handleChange(e, value) {
augProps.setRuntimeError(undefined)
const option = withDefault(value, null)
setSelected(option)
const formikValue = valueType == 'value' && isOption(option) ? option.value : option
augProps.setValue(formikValue)
handleValidate(e, option)
}
function handleInputChange(e, value) {
setInput(value)
}
function optionMatches(option, value) {
return Boolean(option?.label?.toLowerCase().includes(input?.toLowerCase() || ""))
}
function handleTabSelection(e, value) {
const options = getOptions()
const opt = options.find(option => optionMatches(option, value))
if (opt){
handleChange(e, opt)
} else {
notifier.error("no option matches input: " + value)
e.stopPropagation()
e.preventDefault()
}
}
function handleKeyDown(e) {
switch (e.key) {
case "Tab": // tab completion
if (input == "")
return
const option = highlighted
const tabAlreadyMatches = optionMatches(option, input)
if (tabAlreadyMatches){
handleChange(e, option)
} else if(optionMatches(selected, input)) {
return
} else {
skipDebounce()
handleTabSelection(e, input)
}
break;
case "Enter": // enter completion
e.preventDefault();
if (input !== debouncedInput) {
e.stopPropagation()
skipDebounce()
}
break;
default:
}
}
function handleHightlightChange(e, value) {
setHighlighted(value)
}
function getOptionLabel(option) {
return option.label || 'ERROR: No label for option ' + JSON.stringify(option)
}
function getOptionKey(option) {
return option.value
}
const { onChange, ...localFieldProps } = fieldProps
return (
x}
isOptionEqualToValue={(option, value) => option.value === value?.value}
getOptionLabel={getOptionLabel}
getOptionKey={getOptionKey}
// highlight options
autoHighlight
onHighlightChange={handleHightlightChange}
sx={{flexGrow: 1}}//width: taskRendering == 'standard' ? "100%": undefined}}
componentsProps={{paper: {sx: {minWidth: "100%", width: "max-content"}}}} // This increases the width of the dropdown box
// popper options
open={open}
onOpen={() => { setOpen(true); setViewOpen(true) }}
onClose={() => { setOpen(false); setViewOpen(false) }}
// localization options
loadingText={t("select.loading")}
clearText={t("select.clear")}
closeText={t("select.close")}
openText={t("select.open")}
noOptionsText={t("select.nooptions")}
// input value options
value={selected}
loading={loading}
onChange={handleChange}
onInputChange={handleInputChange}
inputValue={input ? input : ""}
renderInput={(params) => {
params.inputProps.onKeyDown = handleKeyDown; // add onKey event here, because it is overwritten if you give it to 'autocomplete'
return (
{loading ? : null}
{params.InputProps.endAdornment}
),
inputProps: {
...params.inputProps,
"data-state": "local"
}
}}
size={fieldProps.size}
style={taskRendering == 'standard' ? fieldMinWidthStyle(formInfo, info.field) : undefined}
fullWidth
/>
)
}}
/>
);
}
const InputSingleSelectFieldLocal = (props) => (
)
export default InputSingleSelectFieldLocal