Merge pull request #294 from sartography/feature/typeahead_in_custom_widgets
Feature/typeahead in custom widgets
This commit is contained in:
commit
6eb0ab0286
|
@ -265,17 +265,18 @@ class CustomBpmnScriptEngine(PythonScriptEngine): # type: ignore
|
|||
"dateparser": dateparser,
|
||||
"datetime": datetime,
|
||||
"decimal": decimal,
|
||||
"dict": dict,
|
||||
"enumerate": enumerate,
|
||||
"filter": filter,
|
||||
"format": format,
|
||||
"json": json,
|
||||
"list": list,
|
||||
"dict": dict,
|
||||
"map": map,
|
||||
"pytz": pytz,
|
||||
"set": set,
|
||||
"sum": sum,
|
||||
"time": time,
|
||||
"timedelta": timedelta,
|
||||
"set": set,
|
||||
}
|
||||
|
||||
# This will overwrite the standard builtins
|
||||
|
|
|
@ -1 +1 @@
|
|||
/src/themes/carbon
|
||||
/src/rjsf/carbon_theme
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import React from 'react';
|
||||
import { FieldHelpProps } from '@rjsf/utils';
|
||||
import FormHelperText from '@mui/material/FormHelperText';
|
||||
|
||||
/** The `FieldHelpTemplate` component renders any help desired for a field
|
||||
*
|
|
@ -1,5 +1,4 @@
|
|||
import React from 'react';
|
||||
// import Box from '@mui/material/Box';
|
||||
// @ts-ignore
|
||||
import { Button } from '@carbon/react';
|
||||
import { SubmitButtonProps, getSubmitButtonOptions } from '@rjsf/utils';
|
|
@ -0,0 +1,110 @@
|
|||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { ComboBox } from '@carbon/react';
|
||||
import HttpService from '../../../services/HttpService';
|
||||
|
||||
interface typeaheadArgs {
|
||||
id: string;
|
||||
onChange: any;
|
||||
options: any;
|
||||
value: any;
|
||||
uiSchema?: any;
|
||||
disabled?: boolean;
|
||||
readonly?: boolean;
|
||||
rawErrors?: any;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
export default function TypeaheadWidget({
|
||||
id,
|
||||
onChange,
|
||||
options: { category, itemFormat },
|
||||
value,
|
||||
uiSchema,
|
||||
disabled,
|
||||
readonly,
|
||||
placeholder,
|
||||
rawErrors = [],
|
||||
}: typeaheadArgs) {
|
||||
const lastSearchTerm = useRef('');
|
||||
const [items, setItems] = useState<any[]>([]);
|
||||
const [selectedItem, setSelectedItem] = useState<any>(null);
|
||||
const itemFormatRegex = /[^{}]+(?=})/g;
|
||||
const itemFormatSubstitutions = itemFormat.match(itemFormatRegex);
|
||||
|
||||
const typeaheadSearch = useCallback(
|
||||
(inputText: string) => {
|
||||
const pathForCategory = (text: string) => {
|
||||
return `/connector-proxy/typeahead/${category}?prefix=${text}&limit=100`;
|
||||
};
|
||||
if (inputText) {
|
||||
lastSearchTerm.current = inputText;
|
||||
// TODO: check cache of prefixes -> results
|
||||
HttpService.makeCallToBackend({
|
||||
path: pathForCategory(inputText),
|
||||
successCallback: (result: any) => {
|
||||
if (lastSearchTerm.current === inputText) {
|
||||
setItems(result);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
[category]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (value) {
|
||||
setSelectedItem(JSON.parse(value));
|
||||
typeaheadSearch(value);
|
||||
}
|
||||
}, [value, typeaheadSearch]);
|
||||
|
||||
const itemToString = (item: any) => {
|
||||
if (!item) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let str = itemFormat;
|
||||
itemFormatSubstitutions.forEach((key: string) => {
|
||||
str = str.replace(`{${key}}`, item[key]);
|
||||
});
|
||||
return str;
|
||||
};
|
||||
|
||||
let placeholderText = `Start typing to search...`;
|
||||
if (placeholder) {
|
||||
placeholderText = placeholder;
|
||||
}
|
||||
|
||||
let helperText = null;
|
||||
if (uiSchema && uiSchema['ui:help']) {
|
||||
helperText = uiSchema['ui:help'];
|
||||
}
|
||||
|
||||
let invalid = false;
|
||||
let errorMessageForField = null;
|
||||
if (rawErrors && rawErrors.length > 0) {
|
||||
invalid = true;
|
||||
[errorMessageForField] = rawErrors;
|
||||
}
|
||||
|
||||
return (
|
||||
<ComboBox
|
||||
onInputChange={typeaheadSearch}
|
||||
onChange={(event: any) => {
|
||||
setSelectedItem(event.selectedItem);
|
||||
onChange(JSON.stringify(event.selectedItem));
|
||||
}}
|
||||
id={id}
|
||||
items={items}
|
||||
itemToString={itemToString}
|
||||
placeholder={placeholderText}
|
||||
selectedItem={selectedItem}
|
||||
helperText={helperText}
|
||||
disabled={disabled}
|
||||
readOnly={readonly}
|
||||
invalid={invalid}
|
||||
invalidText={errorMessageForField}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -12,7 +12,7 @@ import {
|
|||
} from '@carbon/react';
|
||||
import validator from '@rjsf/validator-ajv8';
|
||||
import { FormField, JsonSchemaForm } from '../interfaces';
|
||||
import { Form } from '../themes/carbon';
|
||||
import { Form } from '../rjsf/carbon_theme';
|
||||
import {
|
||||
modifyProcessIdentifierForPathParam,
|
||||
slugifyString,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import validator from '@rjsf/validator-ajv8';
|
||||
|
||||
|
@ -8,85 +8,18 @@ import {
|
|||
Tabs,
|
||||
Grid,
|
||||
Column,
|
||||
ComboBox,
|
||||
Button,
|
||||
ButtonSet,
|
||||
} from '@carbon/react';
|
||||
|
||||
// eslint-disable-next-line import/no-named-as-default
|
||||
import Form from '../themes/carbon';
|
||||
import { Form } from '../rjsf/carbon_theme';
|
||||
import HttpService from '../services/HttpService';
|
||||
import useAPIError from '../hooks/UseApiError';
|
||||
import { modifyProcessIdentifierForPathParam } from '../helpers';
|
||||
import { EventDefinition, Task } from '../interfaces';
|
||||
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
||||
import InstructionsForEndUser from '../components/InstructionsForEndUser';
|
||||
|
||||
// TODO: move this somewhere else
|
||||
function TypeaheadWidget({
|
||||
id,
|
||||
onChange,
|
||||
options: { category, itemFormat },
|
||||
}: {
|
||||
id: string;
|
||||
onChange: any;
|
||||
options: any;
|
||||
}) {
|
||||
const pathForCategory = (inputText: string) => {
|
||||
return `/connector-proxy/typeahead/${category}?prefix=${inputText}&limit=100`;
|
||||
};
|
||||
|
||||
const lastSearchTerm = useRef('');
|
||||
const [items, setItems] = useState<any[]>([]);
|
||||
const [selectedItem, setSelectedItem] = useState<any>(null);
|
||||
const itemFormatRegex = /[^{}]+(?=})/g;
|
||||
const itemFormatSubstitutions = itemFormat.match(itemFormatRegex);
|
||||
|
||||
const itemToString = (item: any) => {
|
||||
if (!item) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let str = itemFormat;
|
||||
itemFormatSubstitutions.forEach((key: string) => {
|
||||
str = str.replace(`{${key}}`, item[key]);
|
||||
});
|
||||
return str;
|
||||
};
|
||||
|
||||
const handleTypeAheadResult = (result: any, inputText: string) => {
|
||||
if (lastSearchTerm.current === inputText) {
|
||||
setItems(result);
|
||||
}
|
||||
};
|
||||
|
||||
const typeaheadSearch = (inputText: string) => {
|
||||
if (inputText) {
|
||||
lastSearchTerm.current = inputText;
|
||||
// TODO: check cache of prefixes -> results
|
||||
HttpService.makeCallToBackend({
|
||||
path: pathForCategory(inputText),
|
||||
successCallback: (result: any) =>
|
||||
handleTypeAheadResult(result, inputText),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ComboBox
|
||||
onInputChange={typeaheadSearch}
|
||||
onChange={(event: any) => {
|
||||
setSelectedItem(event.selectedItem);
|
||||
onChange(itemToString(event.selectedItem));
|
||||
}}
|
||||
id={id}
|
||||
items={items}
|
||||
itemToString={itemToString}
|
||||
placeholder={`Start typing to search for ${category}...`}
|
||||
selectedItem={selectedItem}
|
||||
/>
|
||||
);
|
||||
}
|
||||
import TypeaheadWidget from '../rjsf/custom_widgets/TypeaheadWidget/TypeaheadWidget';
|
||||
|
||||
export default function TaskShow() {
|
||||
const [task, setTask] = useState<Task | null>(null);
|
||||
|
|
Loading…
Reference in New Issue