Checkpoint; nearing completion of custom filter panel

This commit is contained in:
2023-06-19 08:40:47 -05:00
parent 0c7330a01a
commit 50979a1ecc
9 changed files with 380 additions and 134 deletions

View File

@ -38,6 +38,7 @@ interface Props
tableName?: string;
processName?: string;
fieldName: string;
overrideId?: string;
fieldLabel: string;
inForm: boolean;
initialValue?: any;
@ -70,29 +71,34 @@ DynamicSelect.defaultProps = {
const qController = Client.getInstance();
function DynamicSelect({tableName, processName, fieldName, fieldLabel, inForm, initialValue, initialDisplayValue, initialValues, onChange, isEditable, isMultiple, bulkEditMode, bulkEditSwitchChangeHandler, otherValues}: Props)
function DynamicSelect({tableName, processName, fieldName, overrideId, fieldLabel, inForm, initialValue, initialDisplayValue, initialValues, onChange, isEditable, isMultiple, bulkEditMode, bulkEditSwitchChangeHandler, otherValues}: Props)
{
const [ open, setOpen ] = useState(false);
const [ options, setOptions ] = useState<readonly QPossibleValue[]>([]);
const [ searchTerm, setSearchTerm ] = useState(null);
const [ firstRender, setFirstRender ] = useState(true);
const [open, setOpen] = useState(false);
const [options, setOptions] = useState<readonly QPossibleValue[]>([]);
const [searchTerm, setSearchTerm] = useState(null);
const [firstRender, setFirstRender] = useState(true);
////////////////////////////////////////////////////////////////////////////////////////////////
// default value - needs to be an array (from initialValues (array) prop) for multiple mode - //
// else non-multiple, assume we took in an initialValue (id) and initialDisplayValue (label), //
// and build a little object that looks like a possibleValue out of those //
////////////////////////////////////////////////////////////////////////////////////////////////
const [defaultValue, _] = isMultiple ? useState(initialValues ?? undefined)
let [defaultValue, _] = isMultiple ? useState(initialValues ?? undefined)
: useState(initialValue && initialDisplayValue ? [{id: initialValue, label: initialDisplayValue}] : null);
if (isMultiple && defaultValue === null)
{
defaultValue = [];
}
// const loading = open && options.length === 0;
const [loading, setLoading] = useState(false);
const [ switchChecked, setSwitchChecked ] = useState(false);
const [ isDisabled, setIsDisabled ] = useState(!isEditable || bulkEditMode);
const [switchChecked, setSwitchChecked] = useState(false);
const [isDisabled, setIsDisabled] = useState(!isEditable || bulkEditMode);
const [tableMetaData, setTableMetaData] = useState(null as QTableMetaData);
let setFieldValueRef: (field: string, value: any, shouldValidate?: boolean) => void = null;
if(inForm)
if (inForm)
{
const {setFieldValue} = useFormikContext();
setFieldValueRef = setFieldValue;
@ -239,9 +245,11 @@ function DynamicSelect({tableName, processName, fieldName, fieldLabel, inForm, i
bulkEditSwitchChangeHandler(fieldName, newSwitchValue);
};
// console.log(`default value: ${JSON.stringify(defaultValue)}`);
const autocomplete = (
<Autocomplete
id={fieldName}
id={overrideId ?? fieldName}
sx={{background: isDisabled ? "#f0f2f5!important" : "initial"}}
open={open}
fullWidth
@ -291,6 +299,8 @@ function DynamicSelect({tableName, processName, fieldName, fieldLabel, inForm, i
disabled={isDisabled}
multiple={isMultiple}
disableCloseOnSelect={isMultiple}
limitTags={5}
slotProps={{popper: {className: "DynamicSelectPopper"}}}
renderInput={(params) => (
<TextField
{...params}

View File

@ -19,6 +19,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance";
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
import {QCriteriaOperator} from "@kingsrook/qqq-frontend-core/lib/model/query/QCriteriaOperator";
import {QFilterCriteria} from "@kingsrook/qqq-frontend-core/lib/model/query/QFilterCriteria";
@ -28,7 +29,7 @@ import Button from "@mui/material/Button/Button";
import Icon from "@mui/material/Icon/Icon";
import {GridFilterPanelProps, GridSlotsComponentsProps} from "@mui/x-data-grid-pro";
import React, {forwardRef, useReducer} from "react";
import {FilterCriteriaRow} from "qqq/components/query/FilterCriteriaRow";
import {FilterCriteriaRow, getDefaultCriteriaValue} from "qqq/components/query/FilterCriteriaRow";
declare module "@mui/x-data-grid"
@ -39,6 +40,7 @@ declare module "@mui/x-data-grid"
interface FilterPanelPropsOverrides
{
tableMetaData: QTableMetaData;
metaData: QInstance;
queryFilter: QQueryFilter;
updateFilter: (newFilter: QQueryFilter) => void;
}
@ -66,9 +68,9 @@ export const CustomFilterPanel = forwardRef<any, GridFilterPanelProps>(
{
setTimeout(() =>
{
console.log(`Try to focus ${criteriaId - 1}`);
try
{
// console.log(`Try to focus ${criteriaId - 1}`);
document.getElementById(`field-${criteriaId - 1}`).focus();
}
catch (e)
@ -80,7 +82,7 @@ export const CustomFilterPanel = forwardRef<any, GridFilterPanelProps>(
const addCriteria = () =>
{
const qFilterCriteriaWithId = new QFilterCriteriaWithId(null, QCriteriaOperator.EQUALS, [""]);
const qFilterCriteriaWithId = new QFilterCriteriaWithId(null, QCriteriaOperator.EQUALS, getDefaultCriteriaValue());
qFilterCriteriaWithId.id = criteriaId++;
console.log(`adding criteria id ${qFilterCriteriaWithId.id}`);
queryFilter.criteria.push(qFilterCriteriaWithId);
@ -98,8 +100,29 @@ export const CustomFilterPanel = forwardRef<any, GridFilterPanelProps>(
if (queryFilter.criteria.length == 0)
{
/////////////////////////////////////////////
// make sure there's at least one criteria //
/////////////////////////////////////////////
addCriteria();
}
else
{
////////////////////////////////////////////////////////////////////////////////////
// make sure all criteria have an id on them (to be used as react component keys) //
////////////////////////////////////////////////////////////////////////////////////
let updatedAny = false;
for (let i = 0; i < queryFilter.criteria.length; i++)
{
if (!queryFilter.criteria[i].id)
{
queryFilter.criteria[i].id = criteriaId++;
}
}
if (updatedAny)
{
props.updateFilter(queryFilter);
}
}
if(queryFilter.criteria.length == 1 && !queryFilter.criteria[0].fieldName)
{
@ -149,6 +172,7 @@ export const CustomFilterPanel = forwardRef<any, GridFilterPanelProps>(
id={criteria.id}
index={index}
tableMetaData={props.tableMetaData}
metaData={props.metaData}
criteria={criteria}
booleanOperator={booleanOperator}
updateCriteria={(newCriteria, needDebounce) => updateCriteria(newCriteria, index, needDebounce)}

View File

@ -36,11 +36,12 @@ import ChipTextField from "qqq/components/forms/ChipTextField";
interface Props
{
type: string;
onSave: (newValues: any[]) => void;
}
FilterCriteriaPaster.defaultProps = {};
function FilterCriteriaPaster({type}: Props): JSX.Element
function FilterCriteriaPaster({type, onSave}: Props): JSX.Element
{
enum Delimiter
{
@ -86,14 +87,6 @@ function FilterCriteriaPaster({type}: Props): JSX.Element
setPasteModalIsOpen(true);
};
const applyValue = (item: GridFilterItem) =>
{
console.log(`updating grid values: ${JSON.stringify(item.value)}`);
// todo!
// setGridFilterItem(item);
// props.applyValue(item);
};
const clearData = () =>
{
setDelimiter("");
@ -113,34 +106,19 @@ function FilterCriteriaPaster({type}: Props): JSX.Element
const handleSaveClicked = () =>
{
//x if (gridFilterItem)
/* todo
////////////////////////////////////////
// if numeric remove any non-numerics //
////////////////////////////////////////
let saveData = [];
for (let i = 0; i < chipData.length; i++)
{
////////////////////////////////////////
// if numeric remove any non-numerics //
////////////////////////////////////////
let saveData = [];
for (let i = 0; i < chipData.length; i++)
if (type !== "number" || !Number.isNaN(Number(chipData[i])))
{
if (type !== "number" || !Number.isNaN(Number(chipData[i])))
{
saveData.push(chipData[i]);
}
saveData.push(chipData[i]);
}
if (gridFilterItem.value)
{
gridFilterItem.value = [...gridFilterItem.value, ...saveData];
}
else
{
gridFilterItem.value = saveData;
}
setGridFilterItem(gridFilterItem);
props.applyValue(gridFilterItem);
}
*/
onSave(saveData);
clearData();
setPasteModalIsOpen(false);
@ -299,7 +277,7 @@ function FilterCriteriaPaster({type}: Props): JSX.Element
return (
<Box>
<Tooltip title="Quickly add many values to your filter by pasting them from a spreadsheet or any other data source.">
<Icon onClick={handlePasteClick} fontSize="small" color="info" sx={{marginLeft: "10px", cursor: "pointer"}}>paste_content</Icon>
<Icon onClick={handlePasteClick} fontSize="small" color="info" sx={{mx: 0.25, cursor: "pointer"}}>paste_content</Icon>
</Tooltip>
{
pasteModalIsOpen &&

View File

@ -20,6 +20,7 @@
*/
import {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType";
import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance";
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
import {QCriteriaOperator} from "@kingsrook/qqq-frontend-core/lib/model/query/QCriteriaOperator";
import {QFilterCriteria} from "@kingsrook/qqq-frontend-core/lib/model/query/QFilterCriteria";
@ -57,12 +58,14 @@ export interface OperatorOption
valueMode: ValueMode;
}
export const getDefaultCriteriaValue = () => [""];
interface FilterCriteriaRowProps
{
id: number;
index: number;
tableMetaData: QTableMetaData;
metaData: QInstance;
criteria: QFilterCriteria;
booleanOperator: "AND" | "OR" | null;
updateCriteria: (newCriteria: QFilterCriteria, needDebounce: boolean) => void;
@ -82,11 +85,11 @@ function makeFieldOptionsForTable(tableMetaData: QTableMetaData, fieldOptions: a
}
}
export function FilterCriteriaRow({id, index, tableMetaData, criteria, booleanOperator, updateCriteria, removeCriteria, updateBooleanOperator}: FilterCriteriaRowProps): JSX.Element
export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria, booleanOperator, updateCriteria, removeCriteria, updateBooleanOperator}: FilterCriteriaRowProps): JSX.Element
{
// console.log(`FilterCriteriaRow: criteria: ${JSON.stringify(criteria)}`);
const [operatorSelectedValue, setOperatorSelectedValue] = useState(null as OperatorOption);
const [operatorInputValue, setOperatorInputValue] = useState("")
const [operatorInputValue, setOperatorInputValue] = useState("");
///////////////////////////////////////////////////////////////
// set up the array of options for the fields Autocomplete //
@ -98,12 +101,14 @@ export function FilterCriteriaRow({id, index, tableMetaData, criteria, booleanOp
if (tableMetaData.exposedJoins && tableMetaData.exposedJoins.length > 0)
{
fieldsGroupBy = (option: any) => `${option.table.label} Fields`;
for (let i = 0; i < tableMetaData.exposedJoins.length; i++)
{
const exposedJoin = tableMetaData.exposedJoins[i];
makeFieldOptionsForTable(exposedJoin.joinTable, fieldOptions, true);
if (metaData.tables.has(exposedJoin.joinTable.name))
{
fieldsGroupBy = (option: any) => `${option.table.label} fields`;
makeFieldOptionsForTable(exposedJoin.joinTable, fieldOptions, true);
}
}
}
@ -124,8 +129,8 @@ export function FilterCriteriaRow({id, index, tableMetaData, criteria, booleanOp
//////////////////////////////////////////////////////
if (field.possibleValueSourceName)
{
operatorOptions.push({label: "is", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.PVS_SINGLE});
operatorOptions.push({label: "is not", value: QCriteriaOperator.NOT_EQUALS, valueMode: ValueMode.PVS_SINGLE});
operatorOptions.push({label: "equals", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.PVS_SINGLE});
operatorOptions.push({label: "does not equal", value: QCriteriaOperator.NOT_EQUALS_OR_IS_NULL, valueMode: ValueMode.PVS_SINGLE});
operatorOptions.push({label: "is empty", value: QCriteriaOperator.IS_BLANK, valueMode: ValueMode.NONE});
operatorOptions.push({label: "is not empty", value: QCriteriaOperator.IS_NOT_BLANK, valueMode: ValueMode.NONE});
operatorOptions.push({label: "is any of", value: QCriteriaOperator.IN, valueMode: ValueMode.PVS_MULTI});
@ -138,7 +143,7 @@ export function FilterCriteriaRow({id, index, tableMetaData, criteria, booleanOp
case QFieldType.DECIMAL:
case QFieldType.INTEGER:
operatorOptions.push({label: "equals", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.SINGLE});
operatorOptions.push({label: "not equals", value: QCriteriaOperator.NOT_EQUALS, valueMode: ValueMode.SINGLE});
operatorOptions.push({label: "does not equal", value: QCriteriaOperator.NOT_EQUALS_OR_IS_NULL, valueMode: ValueMode.SINGLE});
operatorOptions.push({label: "greater than", value: QCriteriaOperator.GREATER_THAN, valueMode: ValueMode.SINGLE});
operatorOptions.push({label: "greater than or equals", value: QCriteriaOperator.GREATER_THAN_OR_EQUALS, valueMode: ValueMode.SINGLE});
operatorOptions.push({label: "less than", value: QCriteriaOperator.LESS_THAN, valueMode: ValueMode.SINGLE});
@ -151,8 +156,8 @@ export function FilterCriteriaRow({id, index, tableMetaData, criteria, booleanOp
operatorOptions.push({label: "is none of", value: QCriteriaOperator.NOT_IN, valueMode: ValueMode.MULTI});
break;
case QFieldType.DATE:
operatorOptions.push({label: "is", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.SINGLE_DATE});
operatorOptions.push({label: "is not", value: QCriteriaOperator.NOT_EQUALS, valueMode: ValueMode.SINGLE_DATE});
operatorOptions.push({label: "equals", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.SINGLE_DATE});
operatorOptions.push({label: "does not equal", value: QCriteriaOperator.NOT_EQUALS_OR_IS_NULL, valueMode: ValueMode.SINGLE_DATE});
operatorOptions.push({label: "is after", value: QCriteriaOperator.GREATER_THAN, valueMode: ValueMode.SINGLE_DATE});
operatorOptions.push({label: "is before", value: QCriteriaOperator.LESS_THAN, valueMode: ValueMode.SINGLE_DATE});
operatorOptions.push({label: "is empty", value: QCriteriaOperator.IS_BLANK, valueMode: ValueMode.NONE});
@ -163,8 +168,8 @@ export function FilterCriteriaRow({id, index, tableMetaData, criteria, booleanOp
//? operatorOptions.push({label: "is none of", value: QCriteriaOperator.NOT_IN});
break;
case QFieldType.DATE_TIME:
operatorOptions.push({label: "is", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.SINGLE_DATE_TIME});
operatorOptions.push({label: "is not", value: QCriteriaOperator.NOT_EQUALS, valueMode: ValueMode.SINGLE_DATE_TIME});
operatorOptions.push({label: "equals", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.SINGLE_DATE_TIME});
operatorOptions.push({label: "does not equal", value: QCriteriaOperator.NOT_EQUALS_OR_IS_NULL, valueMode: ValueMode.SINGLE_DATE_TIME});
operatorOptions.push({label: "is after", value: QCriteriaOperator.GREATER_THAN, valueMode: ValueMode.SINGLE_DATE_TIME});
operatorOptions.push({label: "is on or after", value: QCriteriaOperator.GREATER_THAN_OR_EQUALS, valueMode: ValueMode.SINGLE_DATE_TIME});
operatorOptions.push({label: "is before", value: QCriteriaOperator.LESS_THAN, valueMode: ValueMode.SINGLE_DATE_TIME});
@ -175,8 +180,8 @@ export function FilterCriteriaRow({id, index, tableMetaData, criteria, booleanOp
//? operatorOptions.push({label: "is not between", value: QCriteriaOperator.NOT_BETWEEN});
break;
case QFieldType.BOOLEAN:
operatorOptions.push({label: "is yes", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.NONE, implicitValues: [true]});
operatorOptions.push({label: "is no", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.NONE, implicitValues: [false]});
operatorOptions.push({label: "equals yes", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.NONE, implicitValues: [true]});
operatorOptions.push({label: "equals no", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.NONE, implicitValues: [false]});
operatorOptions.push({label: "is empty", value: QCriteriaOperator.IS_BLANK, valueMode: ValueMode.NONE});
operatorOptions.push({label: "is not empty", value: QCriteriaOperator.IS_NOT_BLANK, valueMode: ValueMode.NONE});
/*
@ -266,20 +271,39 @@ export function FilterCriteriaRow({id, index, tableMetaData, criteria, booleanOp
//////////////////////////////////////////
const handleFieldChange = (event: any, newValue: any, reason: string) =>
{
criteria.fieldName = newValue ? newValue.fieldName : null;
updateCriteria(criteria, false);
const oldFieldName = criteria.fieldName;
setOperatorOptions(criteria.fieldName)
if(operatorOptions.length)
criteria.fieldName = newValue ? newValue.fieldName : null;
//////////////////////////////////////////////////////
// decide if we should clear out the values or not. //
//////////////////////////////////////////////////////
if (criteria.fieldName == null || isFieldTypeDifferent(oldFieldName, criteria.fieldName))
{
setOperatorSelectedValue(operatorOptions[0]);
setOperatorInputValue(operatorOptions[0].label);
criteria.values = getDefaultCriteriaValue();
}
////////////////////////////////////////////////////////////////////
// update the operator options, and the operator on this criteria //
////////////////////////////////////////////////////////////////////
setOperatorOptions(criteria.fieldName);
if (operatorOptions.length)
{
if (isFieldTypeDifferent(oldFieldName, criteria.fieldName))
{
criteria.operator = operatorOptions[0].value;
setOperatorSelectedValue(operatorOptions[0]);
setOperatorInputValue(operatorOptions[0].label);
}
}
else
{
criteria.operator = null;
setOperatorSelectedValue(null);
setOperatorInputValue("");
}
updateCriteria(criteria, false);
};
/////////////////////////////////////////////
@ -314,7 +338,7 @@ export function FilterCriteriaRow({id, index, tableMetaData, criteria, booleanOp
const handleValueChange = (event: React.ChangeEvent | SyntheticEvent, valueIndex: number | "all" = 0, newValue?: any) =>
{
// @ts-ignore
const value = newValue ? newValue : event ? event.target.value : null;
const value = newValue !== undefined ? newValue : event ? event.target.value : null;
if(!criteria.values)
{
@ -323,7 +347,7 @@ export function FilterCriteriaRow({id, index, tableMetaData, criteria, booleanOp
if(valueIndex == "all")
{
criteria.values= value;
criteria.values = value;
}
else
{
@ -333,6 +357,22 @@ export function FilterCriteriaRow({id, index, tableMetaData, criteria, booleanOp
updateCriteria(criteria, true);
};
const isFieldTypeDifferent = (fieldNameA: string, fieldNameB: string): boolean =>
{
const [fieldA] = FilterUtils.getField(tableMetaData, fieldNameA);
const [fieldB] = FilterUtils.getField(tableMetaData, fieldNameB);
if (fieldA?.type !== fieldB.type)
{
return (true);
}
if (fieldA.possibleValueSourceName !== fieldB.possibleValueSourceName)
{
return (true);
}
return (false);
};
function isFieldOptionEqual(option: any, value: any)
{
return option.fieldName === value.fieldName;
@ -465,6 +505,7 @@ export function FilterCriteriaRow({id, index, tableMetaData, criteria, booleanOp
renderOption={(props, option, state) => renderFieldOption(props, option, state)}
autoSelect={true}
autoHighlight={true}
slotProps={{popper: {style: {padding: 0, width: "250px"}}}}
/>
</Box>
<Box display="inline-block" width={200}>
@ -481,6 +522,7 @@ export function FilterCriteriaRow({id, index, tableMetaData, criteria, booleanOp
getOptionLabel={(option: any) => option.label}
autoSelect={true}
autoHighlight={true}
slotProps={{popper: {style: {padding: 0, maxHeight: "unset", width: "200px"}}}}
/*disabled={criteria.fieldName == null}*/
/>
</Tooltip>

View File

@ -25,11 +25,16 @@ import {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QField
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
import Autocomplete from "@mui/material/Autocomplete";
import Box from "@mui/material/Box";
import Icon from "@mui/material/Icon";
import IconButton from "@mui/material/IconButton";
import InputAdornment from "@mui/material/InputAdornment/InputAdornment";
import TextField from "@mui/material/TextField";
import React, {SyntheticEvent} from "react";
import React, {SyntheticEvent, useReducer} from "react";
import DynamicSelect from "qqq/components/forms/DynamicSelect";
import {QFilterCriteriaWithId} from "qqq/components/query/CustomFilterPanel";
import FilterCriteriaPaster from "qqq/components/query/FilterCriteriaPaster";
import {OperatorOption, ValueMode} from "qqq/components/query/FilterCriteriaRow";
import ValueUtils from "qqq/utils/qqq/ValueUtils";
interface Props
{
@ -45,31 +50,64 @@ FilterCriteriaRowValues.defaultProps = {
function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueChangeHandler}: Props): JSX.Element
{
if(!operatorOption)
const [, forceUpdate] = useReducer((x) => x + 1, 0);
if (!operatorOption)
{
return <br />
return <br />;
}
const makeTextField = (valueIndex: number = 0, label = "Value", idPrefix="value-") =>
const getTypeForTextField = (): string =>
{
let type = "search"
const inputLabelProps: any = {};
let type = "search";
if(field.type == QFieldType.INTEGER)
if (field.type == QFieldType.INTEGER)
{
type = "number";
}
else if(field.type == QFieldType.DATE)
else if (field.type == QFieldType.DATE)
{
type = "date";
inputLabelProps.shrink = true;
}
else if(field.type == QFieldType.DATE_TIME)
else if (field.type == QFieldType.DATE_TIME)
{
type = "datetime-local";
}
return (type);
};
const makeTextField = (valueIndex: number = 0, label = "Value", idPrefix = "value-") =>
{
let type = getTypeForTextField();
const inputLabelProps: any = {};
if (field.type == QFieldType.DATE || field.type == QFieldType.DATE_TIME)
{
inputLabelProps.shrink = true;
}
let value = criteria.values[valueIndex];
if (field.type == QFieldType.DATE_TIME && value && String(value).indexOf("Z") > -1)
{
value = ValueUtils.formatDateTimeValueForForm(value);
}
const clearValue = (event: React.MouseEvent<HTMLAnchorElement> | React.MouseEvent<HTMLButtonElement>, index: number) =>
{
valueChangeHandler(event, index, "");
document.getElementById(`${idPrefix}${criteria.id}`).focus();
};
const inputProps: any = {};
inputProps.endAdornment = (
<InputAdornment position="end">
<IconButton sx={{visibility: value ? "visible" : "hidden"}} onClick={(event) => clearValue(event, valueIndex)}>
<Icon>close</Icon>
</IconButton>
</InputAdornment>
);
return <TextField
id={`${idPrefix}${criteria.id}`}
label={label}
@ -77,17 +115,40 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC
autoComplete="off"
type={type}
onChange={(event) => valueChangeHandler(event, valueIndex)}
value={criteria.values[valueIndex]}
value={value}
InputLabelProps={inputLabelProps}
InputProps={inputProps}
fullWidth
// todo - x to clear value?
/>
/>;
};
function saveNewPasterValues(newValues: any[])
{
if (criteria.values)
{
criteria.values = [...criteria.values, ...newValues];
}
else
{
criteria.values = newValues;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// we are somehow getting some empty-strings as first-value leaking through. they aren't cool, so, remove them if we find them //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (criteria.values.length > 0 && criteria.values[0] == "")
{
criteria.values = criteria.values.splice(1);
}
valueChangeHandler(null, "all", criteria.values);
forceUpdate();
}
switch (operatorOption.valueMode)
{
case ValueMode.NONE:
return <br />
return <br />;
case ValueMode.SINGLE:
return makeTextField();
case ValueMode.SINGLE_DATE:
@ -100,30 +161,36 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC
{ makeTextField(0, "From", "from-") }
</Box>
<Box width="50%" display="inline-block">
{ makeTextField(1, "To", "to-") }
{makeTextField(1, "To", "to-")}
</Box>
</Box>;
case ValueMode.MULTI:
let values = criteria.values;
if(values && values.length == 1 && values[0] == "")
if (values && values.length == 1 && values[0] == "")
{
values = [];
}
return <Autocomplete
renderInput={(params) => (<TextField {...params} variant="standard" label="Values" />)}
options={[]}
multiple
freeSolo // todo - no debounce after enter?
selectOnFocus
clearOnBlur
limitTags={5}
value={values}
onChange={(event, value) => valueChangeHandler(event, "all", value)}
/>
// todo - need the Paste button
return <Box display="flex" alignItems="flex-end">
<Autocomplete
renderInput={(params) => (<TextField {...params} variant="standard" label="Values" />)}
options={[]}
multiple
freeSolo // todo - no debounce after enter?
selectOnFocus
clearOnBlur
fullWidth
limitTags={5}
value={values}
onChange={(event, value) => valueChangeHandler(event, "all", value)}
/>
<Box>
<FilterCriteriaPaster type={getTypeForTextField()} onSave={(newValues: any[]) => saveNewPasterValues(newValues)} />
</Box>
</Box>;
case ValueMode.PVS_SINGLE:
console.log("Doing pvs single: " + criteria.values);
let selectedPossibleValue = null;
if(criteria.values && criteria.values.length > 0)
if (criteria.values && criteria.values.length > 0)
{
selectedPossibleValue = criteria.values[0];
}
@ -131,22 +198,38 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC
<DynamicSelect
tableName={table.name}
fieldName={field.name}
overrideId={field.name + "-single-" + criteria.id}
key={field.name + "-single-" + criteria.id}
fieldLabel="Value"
initialValue={selectedPossibleValue?.id}
initialDisplayValue={selectedPossibleValue?.label}
inForm={false}
onChange={(value: any) => valueChangeHandler(null, 0, value)}
/>
</Box>
</Box>;
case ValueMode.PVS_MULTI:
// todo - values not sticking when re-opening filter panel
console.log("Doing pvs multi: " + criteria.values);
let initialValues: any[] = [];
if (criteria.values && criteria.values.length > 0)
{
if (criteria.values.length == 1 && criteria.values[0] == "")
{
// we never want a tag that's just ""...
}
else
{
initialValues = criteria.values;
}
}
return <Box mb={-1.5}>
<DynamicSelect
tableName={table.name}
fieldName={field.name}
overrideId={field.name + "-multi-" + criteria.id}
key={field.name + "-multi-" + criteria.id}
isMultiple
fieldLabel="Values"
initialValues={criteria.values || []}
initialValues={initialValues}
inForm={false}
onChange={(value: any) => valueChangeHandler(null, "all", value)}
/>