diff --git a/src/frontend/src/components/cardComponent/index.tsx b/src/frontend/src/components/cardComponent/index.tsx index 422417d99..e207b8853 100644 --- a/src/frontend/src/components/cardComponent/index.tsx +++ b/src/frontend/src/components/cardComponent/index.tsx @@ -55,6 +55,32 @@ export default function CollectionCardComponent({ const { onDragStart } = useDragStart(data); + const handlePlaygroundClick = (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + track("Playground Button Clicked", { flowId: data.id }); + setLoadingPlayground(true); + const flow = getFlowById(data.id); + if (flow) { + if (!hasPlayground(flow)) { + setErrorData({ + title: "Error", + list: ["This flow doesn't have a playground."], + }); + setLoadingPlayground(false); + return; + } + setCurrentFlow(flow); + setOpenPlayground(true); + setLoadingPlayground(false); + } else { + setErrorData({ + title: "Error", + list: ["Error getting flow data."], + }); + } + }; + return ( <> { - e.preventDefault(); - e.stopPropagation(); - track("Playground Button Clicked", { flowId: data.id }); - setLoadingPlayground(true); - const flow = getFlowById(data.id); - if (flow) { - if (!hasPlayground(flow)) { - setErrorData({ - title: "Error", - list: ["This flow doesn't have a playground."], - }); - setLoadingPlayground(false); - return; - } - setCurrentFlow(flow); - setOpenPlayground(true); - setLoadingPlayground(false); - } else { - setErrorData({ - title: "Error", - list: ["Error getting flow data."], - }); - } - }} + onClick={handlePlaygroundClick} > {!loadingPlayground ? ( { - if (disabled && myValue !== "") { - setMyValue(""); + if (disabled && componentValue !== "") { + setComponentValue(""); onChange("", undefined, true); } }, [disabled]); useEffect(() => { - setMyValue(typeof value == "string" ? value : JSON.stringify(value)); + setComponentValue(typeof value == "string" ? value : JSON.stringify(value)); }, [value]); + const handleValueChange = (newValue) => { + onChange(newValue); + }; + + const renderInputText = () => ( + + {value !== "" ? value : "Type something..."} + + ); + + const renderExternalLinkIcon = () => { + if (editNode) return null; + + return ( + + ); + }; + return ( -
+
{ - setMyValue(value); - onChange(value); - }} + setValue={handleValueChange} >
- - {myValue !== "" ? myValue : "Type something..."} - - {!editNode && ( - - )} + {renderInputText()} + {renderExternalLinkIcon()}
diff --git a/src/frontend/src/components/dropdownComponent/index.tsx b/src/frontend/src/components/dropdownComponent/index.tsx index f930d18d1..aa1ee20d1 100644 --- a/src/frontend/src/components/dropdownComponent/index.tsx +++ b/src/frontend/src/components/dropdownComponent/index.tsx @@ -69,131 +69,125 @@ export default function Dropdown({ } }, [open]); - return ( - <> - {Object.keys(options ?? [])?.length > 0 || combobox ? ( - <> - {} : setOpen}> - {children ? ( - {children} - ) : ( - - + + ); - - - - )} - - -
- - -
- - No values found. - - {filteredOptions?.map((option, id) => ( - -
- { - onSelect(currentValue); - setOpen(false); - }} - className="items-center overflow-hidden truncate" - data-testid={`${option}-${id ?? ""}-option`} - > - {customValue === option ? ( - - Text:  - - ) : ( - <> - )} - {option} - - -
-
- ))} -
-
-
-
-
- + const renderSearchInput = () => ( +
+ + +
+ ); + + const renderOptionsList = () => ( + + No values found. + + {filteredOptions?.map((option, index) => ( + +
+ { + onSelect(currentValue); + setOpen(false); + }} + className="items-center overflow-hidden truncate" + data-testid={`${option}-${index}-option`} + > + {customValue === option && ( + Text:  + )} + {option} + + +
+
+ ))} +
+
+ ); + + const renderPopoverContent = () => ( + + + {renderSearchInput()} + {renderOptionsList()} + + + ); + + if (Object.keys(options).length === 0 && !combobox) { + return isLoading ? ( +
+ Loading... +
+ ) : ( +
+ + No parameters are available for display. + +
+ ); + } + + return ( + {} : setOpen}> + {children ? ( + {children} ) : ( - <> - {(!isLoading && ( -
- - No parameters are available for display. - -
- )) || ( -
- Loading... -
- )} - + renderTriggerButton() )} - + {renderPopoverContent()} +
); } diff --git a/src/frontend/src/components/floatComponent/index.tsx b/src/frontend/src/components/floatComponent/index.tsx index 979f55aba..1a316a9e1 100644 --- a/src/frontend/src/components/floatComponent/index.tsx +++ b/src/frontend/src/components/floatComponent/index.tsx @@ -20,6 +20,19 @@ export default function FloatComponent({ } }, [disabled]); + const handleInput = (event: React.ChangeEvent) => { + const inputValue = Number(event.target.value); + if (inputValue < min) { + event.target.value = min.toString(); + } else if (inputValue > max) { + event.target.value = max.toString(); + } + }; + + const handleChange = (event) => { + onChange(event.target.value); + }; + return (
) => { - if (Number(event.target.value) < min) { - event.target.value = min.toString(); - } - if (Number(event.target.value) > max) { - event.target.value = max.toString(); - } - }} max={max} value={value ?? ""} disabled={disabled} className={editNode ? "input-edit-node" : ""} - placeholder={`Enter a value`} - onChange={(event) => { - onChange(event.target.value); - }} - onKeyDown={(e) => { - handleKeyDown(e, value, ""); - }} + placeholder="Enter a value" + onInput={handleInput} + onChange={handleChange} + onKeyDown={(e) => handleKeyDown(e, value, "")} />
); diff --git a/src/frontend/src/components/inputListComponent/index.tsx b/src/frontend/src/components/inputListComponent/index.tsx index ddf75c1c1..dce65cb15 100644 --- a/src/frontend/src/components/inputListComponent/index.tsx +++ b/src/frontend/src/components/inputListComponent/index.tsx @@ -29,6 +29,36 @@ export default function InputListComponent({ if (!value?.length) value = [""]; + const handleInputChange = (index, newValue) => { + const newInputList = _.cloneDeep(value); + newInputList[index] = newValue; + onChange(newInputList); + }; + + const addNewInput = (e) => { + e.preventDefault(); + const newInputList = _.cloneDeep(value); + newInputList.push(""); + onChange(newInputList); + }; + + const removeInput = (index, e) => { + e.preventDefault(); + const newInputList = _.cloneDeep(value); + newInputList.splice(index, 1); + onChange(newInputList); + }; + + const getButtonClassName = () => + classNames( + disabled || playgroundDisabled + ? "cursor-not-allowed text-muted-foreground" + : "text-primary hover:text-accent-foreground", + ); + + const getTestId = (type, index) => + `input-list-${type}-btn${editNode ? "-edit" : ""}_${componentName}-${index}`; + return (
- {value.map((singleValue, idx) => { - return ( -
- { - let newInputList = _.cloneDeep(value); - newInputList[idx] = event.target.value; - onChange(newInputList); - }} - data-testid={`${id}_` + idx} + {value.map((singleValue, index) => ( +
+ handleInputChange(index, event.target.value)} + data-testid={`${id}_${index}`} + /> + - ) : ( - - )} -
- ); - })} + +
+ ))}
); } diff --git a/src/frontend/src/components/intComponent/index.tsx b/src/frontend/src/components/intComponent/index.tsx index 10bcad7bf..9ebd54121 100644 --- a/src/frontend/src/components/intComponent/index.tsx +++ b/src/frontend/src/components/intComponent/index.tsx @@ -38,36 +38,57 @@ export default function IntComponent({ onChange(Number(e.target.value)); }; + const getStepValue = () => { + return (Number.isInteger(rangeSpec?.step) ? rangeSpec.step : 1) ?? 1; + }; + + const getMinValue = () => { + return rangeSpec?.min ?? min; + }; + + const getMaxValue = () => { + return rangeSpec?.max ?? undefined; + }; + + const getInputClassName = () => { + return cn( + editNode ? "input-edit-node" : "", + "nopan nodelete nodrag noflow primary-input", + ); + }; + + const handleNumberChange = (newValue) => { + onChange(Number(newValue)); + }; + + const handleInputChange = (event) => { + const inputValue = Number(event.target.value); + if (inputValue < getMinValue()) { + event.target.value = getMinValue().toString(); + } + }; + + const inputRef = useRef(null); + return (
{ - onChange(Number(value)); - }} + step={getStepValue()} + min={getMinValue()} + max={getMaxValue()} + onChange={handleNumberChange} value={value ?? ""} > { - handleKeyDown(event, value, ""); - }} - onInput={(event: React.ChangeEvent) => { - if (Number(event.target.value) < min) { - event.target.value = min.toString(); - } - }} + onKeyDown={(event) => handleKeyDown(event, value, "")} + onInput={handleInputChange} disabled={disabled} placeholder={editNode ? "Integer number" : "Type an integer number"} data-testid={id} - ref={ref} + ref={inputRef} /> diff --git a/src/frontend/src/components/keypairListComponent/index.tsx b/src/frontend/src/components/keypairListComponent/index.tsx index 118097f5e..a77e7107d 100644 --- a/src/frontend/src/components/keypairListComponent/index.tsx +++ b/src/frontend/src/components/keypairListComponent/index.tsx @@ -7,7 +7,6 @@ import { hasDuplicateKeys, } from "@/utils/reactflowUtils"; import { cloneDeep } from "lodash"; -import { classNames } from "../../utils/utils"; import IconComponent from "../genericIconComponent"; import { Input } from "../ui/input"; @@ -27,7 +26,7 @@ export default function KeypairListComponent({ const [duplicateKey, setDuplicateKey] = useState(false); - const myValue = + const values = Object.keys(value || {})?.length === 0 || !value ? [{ "": "" }] : convertObjToArray(value, "dict"); @@ -43,120 +42,100 @@ export default function KeypairListComponent({ }; const handleChangeKey = (event, idx) => { - const oldKey = Object.keys(myValue[idx])[0]; - const updatedObj = { [event.target.value]: myValue[idx][oldKey] }; + const oldKey = Object.keys(values[idx])[0]; + const updatedObj = { [event.target.value]: values[idx][oldKey] }; - const newValue = cloneDeep(myValue); + const newValue = cloneDeep(values); newValue[idx] = updatedObj; handleNewValue(newValue); }; const handleChangeValue = (event, idx) => { - const key = Object.keys(myValue[idx])[0]; + const key = Object.keys(values[idx])[0]; const updatedObj = { [key]: event.target.value }; - const newValue = cloneDeep(myValue); + const newValue = cloneDeep(values); newValue[idx] = updatedObj; handleNewValue(newValue); }; + const addNewKeyValuePair = () => { + const newValues = cloneDeep(values); + newValues.push({ "": "" }); + onChange(newValues); + }; + + const removeKeyValuePair = (index) => { + const newValues = cloneDeep(values); + newValues.splice(index, 1); + onChange(newValues); + }; + + const getInputClassName = (isEditNode, isDuplicateKey) => { + return `${isEditNode ? "input-edit-node" : ""} ${isDuplicateKey ? "input-invalid" : ""}`.trim(); + }; + + const getTestId = (prefix, index) => + `${editNode ? "editNode" : ""}${prefix}${index}`; + return (
1 && editNode ? "mx-2 my-1" : "", - "flex h-full flex-col gap-3", - )} + className={`flex h-full flex-col gap-3 ${values?.length > 1 && editNode ? "mx-2 my-1" : ""}`} > - {myValue?.map((obj, index) => { - return Object.keys(obj).map((key, idx) => { - return ( -
- handleChangeKey(event, index)} - /> + {values?.map((obj, index) => + Object.keys(obj).map((key, idx) => ( +
+ handleChangeKey(event, index)} + /> - handleChangeValue(event, index)} - /> + handleChangeValue(event, index)} + /> - {isList && index === myValue.length - 1 ? ( + {isList && + (index === values.length - 1 ? ( - ) : isList ? ( + ) : ( - ) : ( - "" - )} -
- ); - }); - })} + ))} +
+ )), + )}
); } diff --git a/src/frontend/src/components/multiselectComponent/index.tsx b/src/frontend/src/components/multiselectComponent/index.tsx index d432d6a24..59572469b 100644 --- a/src/frontend/src/components/multiselectComponent/index.tsx +++ b/src/frontend/src/components/multiselectComponent/index.tsx @@ -92,156 +92,139 @@ export default function MultiselectComponent({ } }, [open]); - return ( - <> - {Object.keys(options ?? [])?.length > 0 || combobox ? ( - <> - {} : setOpen}> - {children ? ( - {children} - ) : ( - - + + ); + + const renderSearchInput = () => ( +
+ + { + setSearchValue(event.target.value); + }} + placeholder="Search options..." + className="flex h-9 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50" + /> + +
+ ); + + const renderOptionsList = () => ( + + No values found. + + {filteredOptions.map((option, index) => ( + +
+ + {(customValues.includes(option) || searchValue === option) && ( + Text:  + )} + {option} + - - {value && - value.length > 0 && - options.find((option) => value.includes(option)) - ? value.join(", ") - : "Choose an option..."} - + /> + +
+
+ ))} +
+
+ ); - - - - )} - { - event.preventDefault(); - }} - side="bottom" - avoidCollisions={!!children} - className="noflow nowheel nopan nodelete nodrag p-0" - style={ - children - ? {} - : { minWidth: refButton?.current?.clientWidth ?? "200px" } - } - > - -
- - { - setSearchValue(event.target.value); - searchRoleByTerm(event.target.value); - }} - placeholder="Search options..." - className="flex h-9 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50" - /> - -
+ if (Object.keys(options).length === 0 && !combobox) { + return isLoading ? ( +
+ Loading... +
+ ) : ( +
+ + No parameters are available for display. + +
+ ); + } - - No values found. - - {filteredOptions?.map((option, id) => ( - -
- { - if (value.includes(currentValue)) { - onSelect( - value.filter((v) => v !== currentValue), - ); - } else { - onSelect([...value, currentValue]); - } - }} - className="items-center overflow-hidden truncate" - data-testid={`${option}-${id ?? ""}-option`} - > - {customValues.includes(option) || - searchValue === option ? ( - - Text:  - - ) : ( - <> - )} - {option} - - -
-
- ))} -
-
-
-
-
- + return ( + {} : setOpen}> + {children ? ( + {children} ) : ( - <> - {(!isLoading && ( -
- - No parameters are available for display. - -
- )) || ( -
- Loading... -
- )} - + renderDropdownTrigger() )} - + event.preventDefault()} + side="bottom" + avoidCollisions={!!children} + className="noflow nowheel nopan nodelete nodrag p-0" + style={ + children + ? {} + : { minWidth: refButton?.current?.clientWidth ?? "200px" } + } + > + + {renderSearchInput()} + {renderOptionsList()} + + +
); } diff --git a/src/frontend/src/components/promptComponent/index.tsx b/src/frontend/src/components/promptComponent/index.tsx index 3d3ed8084..9d577ed1e 100644 --- a/src/frontend/src/components/promptComponent/index.tsx +++ b/src/frontend/src/components/promptComponent/index.tsx @@ -1,6 +1,7 @@ import PromptModal from "@/modals/promptModal"; import { useEffect } from "react"; import { PromptAreaComponentType } from "../../types/components"; +import { cn } from "../../utils/utils"; import IconComponent from "../genericIconComponent"; import { Button } from "../ui/button"; @@ -21,8 +22,38 @@ export default function PromptAreaComponent({ } }, [disabled]); + const renderPromptText = () => ( + + {value !== "" ? value : "Type your prompt here..."} + + ); + + const renderExternalLinkIcon = () => { + if (editNode) return null; + + return ( + + ); + }; + return ( -
+
diff --git a/src/frontend/src/components/textAreaComponent/index.tsx b/src/frontend/src/components/textAreaComponent/index.tsx index 9999d624d..b4e749753 100644 --- a/src/frontend/src/components/textAreaComponent/index.tsx +++ b/src/frontend/src/components/textAreaComponent/index.tsx @@ -22,107 +22,117 @@ export default function TextAreaComponent({ } }, [disabled]); - return ( -
-
-