Updated global variables to ShadCN and updated logic to show variables as different things

This commit is contained in:
Lucas Oliveira 2024-03-20 18:05:16 +01:00
commit d003be4fe2
7 changed files with 202 additions and 169 deletions

View file

@ -42,6 +42,7 @@
"base64-js": "^1.5.1",
"class-variance-authority": "^0.6.1",
"clsx": "^1.2.1",
"cmdk": "^1.0.0",
"dompurify": "^3.0.5",
"esbuild": "^0.17.19",
"framer-motion": "^11.0.6",
@ -4797,6 +4798,19 @@
"node": ">=6"
}
},
"node_modules/cmdk": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.0.0.tgz",
"integrity": "sha512-gDzVf0a09TvoJ5jnuPvygTB77+XdOSwEmJ88L6XPFPlv7T3RxbP9jgenfylrAMD0+Le1aO0nVjQUzl2g+vjz5Q==",
"dependencies": {
"@radix-ui/react-dialog": "1.0.5",
"@radix-ui/react-primitive": "1.0.3"
},
"peerDependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
},
"node_modules/code-block-writer": {
"version": "12.0.0",
"resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-12.0.0.tgz",

View file

@ -37,6 +37,7 @@
"base64-js": "^1.5.1",
"class-variance-authority": "^0.6.1",
"clsx": "^1.2.1",
"cmdk": "^1.0.0",
"dompurify": "^3.0.5",
"esbuild": "^0.17.19",
"framer-motion": "^11.0.6",

View file

@ -16,6 +16,7 @@ import PromptAreaComponent from "../../../../components/promptComponent";
import TextAreaComponent from "../../../../components/textAreaComponent";
import ToggleShadComponent from "../../../../components/toggleShadComponent";
import { Button } from "../../../../components/ui/button";
import { CommandItem } from "../../../../components/ui/command";
import { RefreshButton } from "../../../../components/ui/refreshButton";
import {
INPUT_HANDLER_HOVER,
@ -23,6 +24,7 @@ import {
OUTPUT_HANDLER_HOVER,
TOOLTIP_EMPTY,
} from "../../../../constants/constants";
import AddNewVariableButton from "../../../../pages/globalVariablesPage/components/addNewVariableButton";
import useAlertStore from "../../../../stores/alertStore";
import useFlowStore from "../../../../stores/flowStore";
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
@ -46,7 +48,7 @@ import {
nodeIconsLucide,
nodeNames,
} from "../../../../utils/styleUtils";
import { classNames, groupByFamily } from "../../../../utils/utils";
import { classNames, cn, groupByFamily } from "../../../../utils/utils";
export default function ParameterComponent({
left,
@ -386,7 +388,7 @@ export default function ParameterComponent({
<>
<div
className={
"w-full truncate text-sm flex items-center" +
"flex w-full items-center truncate text-sm" +
(left ? "" : " text-end")
}
>
@ -546,33 +548,47 @@ export default function ParameterComponent({
password={data.node?.template[name].password ?? false}
value={data.node?.template[name].value ?? ""}
options={globalVariablesEntries}
onChange={(value) => {
handleOnNewValue(value);
if (globalVariablesEntries.includes(value)) {
setNoticeData({
title: `the value inserted in ${data.node?.display_name} is a global variable, \n
the real value will be update on run`,
});
setNode(data.id, (oldNode) => {
let newNode = cloneDeep(oldNode);
newNode.data = {
...newNode.data,
};
newNode.data.node.template[name].load_from_db = true;
return newNode;
});
} else {
setNode(data.id, (oldNode) => {
let newNode = cloneDeep(oldNode);
newNode.data = {
...newNode.data,
};
newNode.data.node.template[name].load_from_db = false;
return newNode;
});
}
//mark as global variable
}}
optionsPlaceholder={"Global Variables"}
optionsButton={
<AddNewVariableButton>
<CommandItem value={"new"}>
<IconComponent
name="Plus"
className={cn("mr-2 h-4 w-4 text-primary")}
aria-hidden="true"
/>
Add New Variable
</CommandItem>
</AddNewVariableButton>
}
selectedOption={
data?.node?.template[name].load_from_db ?? false
? data?.node?.template[name].value
: ""
}
setSelectedOption={(value) => {
handleOnNewValue(value);
setNode(data.id, (oldNode) => {
let newNode = cloneDeep(oldNode);
newNode.data = {
...newNode.data,
};
newNode.data.node.template[name].load_from_db =
value === "" ? false : true;
return newNode;
});
}}
onChange={(value) => {
handleOnNewValue(value);
setNode(data.id, (oldNode) => {
let newNode = cloneDeep(oldNode);
newNode.data = {
...newNode.data,
};
newNode.data.node.template[name].load_from_db = false;
return newNode;
});
}}
/>
</div>
{data.node?.template[name].refresh_button && (

View file

@ -1,13 +1,20 @@
import { Listbox, Transition } from "@headlessui/react";
import * as Form from "@radix-ui/react-form";
import { Fragment, useEffect, useRef, useState } from "react";
import AddNewVariableButton from "../../pages/globalVariablesPage/components/addNewVariableButton";
import { PopoverAnchor } from "@radix-ui/react-popover";
import { useEffect, useRef, useState } from "react";
import { InputComponentType } from "../../types/components";
import { handleKeyDown } from "../../utils/reactflowUtils";
import { classNames, cn } from "../../utils/utils";
import IconComponent from "../genericIconComponent";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "../ui/command";
import { Input } from "../ui/input";
import { Separator } from "../ui/separator";
import { Popover, PopoverContentWithoutPortal } from "../ui/popover";
export default function InputComponent({
autoFocus = false,
@ -23,7 +30,11 @@ export default function InputComponent({
className,
id = "",
blurOnEnter = false,
selectedOption,
setSelectedOption,
options = [],
optionsPlaceholder = "Search options...",
optionsButton,
}: InputComponentType): JSX.Element {
const [pwdVisible, setPwdVisible] = useState(false);
const refInput = useRef<HTMLInputElement>(null);
@ -36,10 +47,6 @@ export default function InputComponent({
}
}, [disabled]);
const filteredOptions = options.filter((option) =>
option.toLowerCase().includes(value.toLowerCase())
);
function onInputLostFocus(event): void {
if (onBlur) onBlur(event);
}
@ -81,144 +88,118 @@ export default function InputComponent({
</Form.Control>
) : (
<>
<Input
id={id}
ref={refInput}
type="text"
onBlur={onInputLostFocus}
value={value}
autoFocus={autoFocus}
disabled={disabled}
required={required}
className={classNames(
password && !pwdVisible && value !== ""
? " text-clip password "
: "",
editNode ? " input-edit-node " : "",
password && editNode ? "pr-8" : "",
password && !editNode ? "pr-10" : "",
className!
)}
placeholder={password && editNode ? "Key" : placeholder}
onChange={(e) => {
onChange(e.target.value);
}}
onKeyDown={(e) => {
handleKeyDown(e, value, "");
if (blurOnEnter && e.key === "Enter") refInput.current?.blur();
}}
data-testid={editNode ? id + "-edit" : id}
/>
<Listbox
onChange={(val) => {
onChange(val);
}}
>
<>
<div className={"relative mt-1 "}>
<Transition
show={showOptions}
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options
className={classNames(
editNode
? "dropdown-component-true-options nowheel custom-scroll"
: "dropdown-component-false-options nowheel custom-scroll",
false ? "mb-2 w-[250px]" : "absolute w-full"
)}
>
<div className="flex items-center justify-between px-4 pb-3 pt-2 font-semibold">
<div className="flex items-center gap-2">
<IconComponent name="Globe" className="h-4 w-4" />
Global Variables
</div>
<div>
<AddNewVariableButton>
<button className="text-muted-foreground hover:text-accent-foreground">
<IconComponent name="Plus" className="h-5 w-5" />
</button>
</AddNewVariableButton>
</div>
</div>
<Separator />
{filteredOptions.map((option, id) => (
<Listbox.Option
<Popover open={showOptions} onOpenChange={setShowOptions}>
<PopoverAnchor>
<Input
id={id}
ref={refInput}
type="text"
onBlur={onInputLostFocus}
value={value}
autoFocus={autoFocus}
disabled={disabled || selectedOption !== ""}
onClick={() => {
selectedOption !== "" && setShowOptions(true);
}}
required={required}
className={classNames(
password && !pwdVisible && value !== ""
? " text-clip password "
: "",
editNode ? " input-edit-node " : "",
password && editNode ? "pr-8" : "",
password && !editNode ? "pr-10" : "",
className!
)}
placeholder={password && editNode ? "Key" : placeholder}
onChange={(e) => {
onChange(e.target.value);
}}
onKeyDown={(e) => {
handleKeyDown(e, value, "");
if (blurOnEnter && e.key === "Enter")
refInput.current?.blur();
}}
data-testid={editNode ? id + "-edit" : id}
/>
</PopoverAnchor>
<PopoverContentWithoutPortal
className="nocopy nopan nodelete nodrag noundo p-0"
style={{ minWidth: refInput?.current?.clientWidth ?? "200px" }}
side="bottom"
align="center"
>
<Command>
<CommandInput placeholder={optionsPlaceholder} />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
<CommandGroup defaultChecked={false}>
{options.map((option, id) => (
<CommandItem
key={id}
className={({ active }) =>
classNames(
active ? " bg-accent" : "",
editNode
? "dropdown-component-false-option"
: "dropdown-component-true-option",
" hover:bg-accent"
)
}
value={option}
onSelect={(currentValue) => {
setSelectedOption &&
setSelectedOption(
currentValue === selectedOption
? ""
: currentValue
);
setShowOptions(false);
}}
>
{({ selected, active }) => (
<>
<span
className={classNames(
selected ? "font-semibold" : "font-normal",
"block truncate "
)}
data-testid={`${option}-${id ?? ""}-option`}
>
{option}
</span>
{selected ? (
<span
className={classNames(
"dropdown-component-choosal"
)}
>
<IconComponent
name="Check"
className={
"dropdown-component-check-icon text-foreground"
}
aria-hidden="true"
/>
</span>
) : null}
</>
)}
</Listbox.Option>
<IconComponent
name="Check"
className={cn(
"mr-2 h-4 w-4 text-primary",
selectedOption === option
? "opacity-100"
: "opacity-0"
)}
aria-hidden="true"
/>
{option}
</CommandItem>
))}
</Listbox.Options>
</Transition>
</div>
</>
</Listbox>
{optionsButton && optionsButton}
</CommandGroup>
</CommandList>
</Command>
</PopoverContentWithoutPortal>
</Popover>
<div
className={cn(
"pointer-events-auto absolute inset-y-0 h-full w-full",
selectedOption !== "" ? "" : "hidden"
)}
onClick={
selectedOption !== ""
? (e) => {
setShowOptions((old) => !old);
e.preventDefault();
e.stopPropagation();
}
: () => {}
}
></div>
</>
)}
{options.length > 0 && (
<span
className={cn(
password ? "right-8" : "right-0",
"absolute inset-y-0 flex items-center pr-2"
)}
<span
className={cn(
password ? "right-8" : "right-0",
"absolute inset-y-0 flex items-center pr-2"
)}
>
<button
onClick={() => {
setShowOptions(!showOptions);
}}
className="text-muted-foreground hover:text-accent-foreground"
>
<button
onClick={() => {
setShowOptions(!showOptions);
}}
className="text-muted-foreground hover:text-accent-foreground"
>
<IconComponent
name="Globe"
className="h-4 w-4"
aria-hidden="true"
/>
</button>
</span>
)}
<IconComponent name="Globe" className="h-4 w-4" aria-hidden="true" />
</button>
</span>
{password && (
<button

View file

@ -117,7 +117,7 @@ const CommandItem = React.forwardRef<
<CommandPrimitive.Item
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50",
className
)}
{...props}

View file

@ -27,4 +27,21 @@ const PopoverContent = React.forwardRef<
));
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
export { Popover, PopoverContent, PopoverTrigger };
const PopoverContentWithoutPortal = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
<PopoverPrimitive.Content
ref={ref}
align={align}
sideOffset={sideOffset}
className={cn(
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
/>
));
PopoverContentWithoutPortal.displayName = PopoverPrimitive.Content.displayName;
export { Popover, PopoverContent, PopoverContentWithoutPortal, PopoverTrigger };

View file

@ -20,7 +20,11 @@ export type InputComponentType = {
className?: string;
id?: string;
blurOnEnter?: boolean;
optionsPlaceholder?: string;
options?: string[];
optionsButton?: ReactElement;
selectedOption?: string;
setSelectedOption?: (value: string) => void;
};
export type ToggleComponentType = {
enabled: boolean;