import PropTypes from 'prop-types';
import * as React from 'react';
import { useController, useFormContext } from 'react-hook-form';
import { IconButton } from '@mui/material';
import FormHelperText from '@mui/material/FormHelperText';
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
import ClearIcon from '@mui/icons-material/Clear';
import { ActionButtonsWrapper } from './style';
import ClickAwayListener from '@mui/base/ClickAwayListener';
import InputLabel from '@mui/material/InputLabel';
import { createEditor } from 'slate';
import { Editable, ReactEditor } from 'slate-react';
import Slate from 'components/Slate';
import Element from 'components/Slate/Element';
import {
serialize,
deserialize,
insertVariable,
customizeEditor,
resetEditor,
overrideEditorValue,
focusEditor,
} from 'components/Slate/utils';
import {
FakeInput,
InputLabelWrapper,
ChildrenWrapper,
} from 'components/PowerInput/style';
import CustomOptions from './CustomOptions';
import { processStepWithExecutions } from 'components/PowerInput/data';
import { StepExecutionsContext } from 'contexts/StepExecutions';
function ControlledCustomAutocomplete(props) {
const {
defaultValue = '',
name,
label,
required,
options = [],
dependsOn = [],
description,
loading,
disabled,
shouldUnregister,
} = props;
const { control, watch } = useFormContext();
const { field, fieldState } = useController({
control,
name,
defaultValue,
rules: { required },
shouldUnregister,
});
const {
value,
onChange: controllerOnChange,
onBlur: controllerOnBlur,
} = field;
const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
const [isInitialValueSet, setInitialValue] = React.useState(false);
const [isSingleChoice, setSingleChoice] = React.useState(undefined);
const priorStepsWithExecutions = React.useContext(StepExecutionsContext);
const editorRef = React.useRef(null);
const renderElement = React.useCallback(
(props) => ,
[disabled],
);
const [editor] = React.useState(() => customizeEditor(createEditor()));
const [showVariableSuggestions, setShowVariableSuggestions] =
React.useState(false);
let dependsOnValues = [];
if (dependsOn?.length) {
dependsOnValues = watch(dependsOn);
}
React.useEffect(() => {
const ref = ReactEditor.toDOMNode(editor, editor);
resizeObserver.observe(ref);
return () => resizeObserver.unobserve(ref);
}, []);
const promoteValue = () => {
const serializedValue = serialize(editor.children);
controllerOnChange(serializedValue);
};
const resizeObserver = React.useMemo(function syncCustomOptionsPosition() {
return new ResizeObserver(() => {
forceUpdate();
});
}, []);
React.useEffect(() => {
const hasDependencies = dependsOnValues.length;
if (hasDependencies) {
// Reset the field when a dependent has been updated
resetEditor(editor);
}
}, dependsOnValues);
React.useEffect(
function updateInitialValue() {
const hasOptions = options.length;
const isOptionsLoaded = loading === false;
if (!isInitialValueSet && hasOptions && isOptionsLoaded) {
setInitialValue(true);
const option = options.find((option) => option.value === value);
if (option) {
overrideEditorValue(editor, { option, focus: false });
setSingleChoice(true);
} else if (value) {
setSingleChoice(false);
}
}
},
[isInitialValueSet, options, loading],
);
React.useEffect(() => {
if (!showVariableSuggestions && value !== serialize(editor.children)) {
promoteValue();
}
}, [showVariableSuggestions]);
const hideSuggestionsOnShift = (event) => {
if (event.code === 'Tab') {
setShowVariableSuggestions(false);
}
};
const handleKeyDown = (event) => {
hideSuggestionsOnShift(event);
if (event.code === 'Tab') {
promoteValue();
}
if (isSingleChoice && event.code !== 'Tab') {
event.preventDefault();
}
};
const stepsWithVariables = React.useMemo(() => {
return processStepWithExecutions(priorStepsWithExecutions);
}, [priorStepsWithExecutions]);
const handleVariableSuggestionClick = React.useCallback(
(variable) => {
insertVariable(editor, variable, stepsWithVariables);
},
[stepsWithVariables],
);
const handleOptionClick = React.useCallback(
(event, option) => {
event.stopPropagation();
overrideEditorValue(editor, { option, focus: false });
setShowVariableSuggestions(false);
setSingleChoice(true);
},
[stepsWithVariables],
);
const handleClearButtonClick = (event) => {
event.stopPropagation();
resetEditor(editor);
promoteValue();
setSingleChoice(undefined);
};
const reset = (tabIndex) => {
const isOptions = tabIndex === 0;
setSingleChoice(isOptions);
resetEditor(editor, { focus: true });
};
return (
setShowVariableSuggestions(false)}
>
{/* ref-able single child for ClickAwayListener */}
{
focusEditor(editor);
}}
>
{`${label}${required ? ' *' : ''}`}
{
setShowVariableSuggestions(true);
}}
onBlur={() => {
controllerOnBlur();
}}
/>
{isSingleChoice && serialize(editor.children) !== '' && (
)}
{/* ghost placer for the variables popover */}
{fieldState.isTouched
? fieldState.error?.message || description
: description}
);
}
ControlledCustomAutocomplete.propTypes = {
options: PropTypes.array,
loading: PropTypes.bool.isRequired,
showOptionValue: PropTypes.bool,
dependsOn: PropTypes.arrayOf(PropTypes.string),
defaultValue: PropTypes.string,
name: PropTypes.string.isRequired,
label: PropTypes.string,
type: PropTypes.string,
required: PropTypes.bool,
readOnly: PropTypes.bool,
description: PropTypes.string,
docUrl: PropTypes.string,
clickToCopy: PropTypes.bool,
disabled: PropTypes.bool,
shouldUnregister: PropTypes.bool,
};
export default ControlledCustomAutocomplete;