Refactor: Change how minimize displays on screen

This commit is contained in:
igorrCarvalho 2023-09-12 20:19:05 -03:00
commit 602c12282d
5 changed files with 482 additions and 340 deletions

View file

@ -1,4 +1,4 @@
import { cloneDeep } from "lodash"; import { cloneDeep, divide } from "lodash";
import React, { import React, {
ReactNode, ReactNode,
useContext, useContext,
@ -45,6 +45,7 @@ export default function ParameterComponent({
required = false, required = false,
optionalHandle = null, optionalHandle = null,
info = "", info = "",
showNode,
}: ParameterComponentType): JSX.Element { }: ParameterComponentType): JSX.Element {
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
const refHtml = useRef<HTMLDivElement & ReactNode>(null); const refHtml = useRef<HTMLDivElement & ReactNode>(null);
@ -179,181 +180,225 @@ export default function ParameterComponent({
}, [tooltipTitle, flow]); }, [tooltipTitle, flow]);
return ( return (
<div !showNode ? (
ref={ref} <div
className="mt-1 flex w-full flex-wrap items-center justify-between bg-muted px-5 py-2" ref={ref}
> className="mt-1 flex w-full flex-wrap items-center justify-between bg-muted px-5 py-2"
<> >
<div {
className={ left &&
"w-full truncate text-sm" + (type === "str" ||
(left ? "" : " text-end") + type === "bool" ||
(info !== "" ? " flex items-center" : "") type === "float" ||
} type === "code" ||
> type === "prompt" ||
{title} type === "file" ||
<span className="text-status-red">{required ? " *" : ""}</span> type === "int") &&
<div className=""> !optionalHandle ? (
{info !== "" && ( <></>
<ShadTooltip content={infoHtml.current}> ) : (
{/* put div to avoid bug that does not display tooltip */} <ShadTooltip
<div> styleClasses={"tooltip-fixed-width custom-scroll nowheel"}
<IconComponent delayDuration={0}
name="Info" content={refHtml.current}
className="relative bottom-0.5 ml-2 h-3 w-4" side={left ? "left" : "right"}
/> >
</div> <Handle
</ShadTooltip> type={left ? "target" : "source"}
)} position={left ? Position.Left : Position.Right}
</div> id={id}
</div> isValidConnection={(connection) =>
{left && isValidConnection(connection, reactFlowInstance!)
(type === "str" ||
type === "bool" ||
type === "float" ||
type === "code" ||
type === "prompt" ||
type === "file" ||
type === "int") &&
!optionalHandle ? (
<></>
) : (
<ShadTooltip
styleClasses={"tooltip-fixed-width custom-scroll nowheel"}
delayDuration={0}
content={refHtml.current}
side={left ? "left" : "right"}
>
<Handle
type={left ? "target" : "source"}
position={left ? Position.Left : Position.Right}
id={id}
isValidConnection={(connection) =>
isValidConnection(connection, reactFlowInstance!)
}
className={classNames(
left ? "-ml-0.5 " : "-mr-0.5 ",
"h-3 w-3 rounded-full border-2 bg-background"
)}
style={{
borderColor: color,
top: position,
}}
></Handle>
</ShadTooltip>
)}
{left === true &&
type === "str" &&
!data.node?.template[name].options ? (
<div className="mt-2 w-full">
{data.node?.template[name].list ? (
<InputListComponent
disabled={disabled}
value={
!data.node.template[name].value ||
data.node.template[name].value === ""
? [""]
: data.node.template[name].value
} }
onChange={handleOnNewValue} className={classNames(
/> left ? "-ml-0.5 " : "-mr-0.5 ",
) : data.node?.template[name].multiline ? ( "h-3 w-3 rounded-full border-2 bg-background"
<TextAreaComponent )}
style={{
borderColor: color,
top: position,
}}
></Handle>
</ShadTooltip>
)}
</ div>
) : (
<div
ref={ref}
className="mt-1 flex w-full flex-wrap items-center justify-between bg-muted px-5 py-2"
>
<>
<div
className={
"w-full truncate text-sm" +
(left ? "" : " text-end") +
(info !== "" ? " flex items-center" : "")
}
>
{title}
<span className="text-status-red">{required ? " *" : ""}</span>
<div className="">
{info !== "" && (
<ShadTooltip content={infoHtml.current}>
{/* put div to avoid bug that does not display tooltip */}
<div>
<IconComponent
name="Info"
className="relative bottom-0.5 ml-2 h-3 w-4"
/>
</div>
</ShadTooltip>
)}
</div>
</div>
{left &&
(type === "str" ||
type === "bool" ||
type === "float" ||
type === "code" ||
type === "prompt" ||
type === "file" ||
type === "int") &&
!optionalHandle ? (
<></>
) : (
<ShadTooltip
styleClasses={"tooltip-fixed-width custom-scroll nowheel"}
delayDuration={0}
content={refHtml.current}
side={left ? "left" : "right"}
>
<Handle
type={left ? "target" : "source"}
position={left ? Position.Left : Position.Right}
id={id}
isValidConnection={(connection) =>
isValidConnection(connection, reactFlowInstance!)
}
className={classNames(
left ? "-ml-0.5 " : "-mr-0.5 ",
"h-3 w-3 rounded-full border-2 bg-background"
)}
style={{
borderColor: color,
top: position,
}}
></Handle>
</ShadTooltip>
)}
{left === true &&
type === "str" &&
!data.node?.template[name].options ? (
<div className="mt-2 w-full">
{data.node?.template[name].list ? (
<InputListComponent
disabled={disabled}
value={
!data.node.template[name].value ||
data.node.template[name].value === ""
? [""]
: data.node.template[name].value
}
onChange={handleOnNewValue}
/>
) : data.node?.template[name].multiline ? (
<TextAreaComponent
disabled={disabled}
value={data.node.template[name].value ?? ""}
onChange={handleOnNewValue}
/>
) : (
<InputComponent
disabled={disabled}
password={data.node?.template[name].password ?? false}
value={data.node?.template[name].value ?? ""}
onChange={handleOnNewValue}
/>
)}
</div>
) : left === true && type === "bool" ? (
<div className="mt-2 w-full">
<ToggleShadComponent
disabled={disabled} disabled={disabled}
value={data.node.template[name].value ?? ""} enabled={data.node?.template[name].value ?? false}
onChange={handleOnNewValue} setEnabled={(isEnabled) => {
handleOnNewValue(isEnabled);
}}
size="large"
/> />
) : ( </div>
<InputComponent ) : left === true && type === "float" ? (
<div className="mt-2 w-full">
<FloatComponent
disabled={disabled} disabled={disabled}
password={data.node?.template[name].password ?? false}
value={data.node?.template[name].value ?? ""} value={data.node?.template[name].value ?? ""}
onChange={handleOnNewValue} onChange={handleOnNewValue}
/> />
)} </div>
</div> ) : left === true &&
) : left === true && type === "bool" ? ( type === "str" &&
<div className="mt-2 w-full"> data.node?.template[name].options ? (
<ToggleShadComponent <div className="mt-2 w-full">
disabled={disabled} <Dropdown
enabled={data.node?.template[name].value ?? false} options={data.node.template[name].options}
setEnabled={(isEnabled) => { onSelect={handleOnNewValue}
handleOnNewValue(isEnabled); value={data.node.template[name].value ?? "Choose an option"}
}} ></Dropdown>
size="large" </div>
/> ) : left === true && type === "code" ? (
</div> <div className="mt-2 w-full">
) : left === true && type === "float" ? ( <CodeAreaComponent
<div className="mt-2 w-full"> dynamic={data.node?.template[name].dynamic ?? false}
<FloatComponent setNodeClass={(nodeClass) => {
disabled={disabled} data.node = nodeClass;
value={data.node?.template[name].value ?? ""} }}
onChange={handleOnNewValue} nodeClass={data.node}
/> disabled={disabled}
</div> value={data.node?.template[name].value ?? ""}
) : left === true && onChange={handleOnNewValue}
type === "str" && />
data.node?.template[name].options ? ( </div>
<div className="mt-2 w-full"> ) : left === true && type === "file" ? (
<Dropdown <div className="mt-2 w-full">
options={data.node.template[name].options} <InputFileComponent
onSelect={handleOnNewValue} disabled={disabled}
value={data.node.template[name].value ?? "Choose an option"} value={data.node?.template[name].value ?? ""}
></Dropdown> onChange={handleOnNewValue}
</div> fileTypes={data.node?.template[name].fileTypes}
) : left === true && type === "code" ? ( suffixes={data.node?.template[name].suffixes}
<div className="mt-2 w-full"> onFileChange={(filePath: string) => {
<CodeAreaComponent data.node!.template[name].file_path = filePath;
dynamic={data.node?.template[name].dynamic ?? false} save();
setNodeClass={(nodeClass) => { }}
data.node = nodeClass; ></InputFileComponent>
}} </div>
nodeClass={data.node} ) : left === true && type === "int" ? (
disabled={disabled} <div className="mt-2 w-full">
value={data.node?.template[name].value ?? ""} <IntComponent
onChange={handleOnNewValue} disabled={disabled}
/> value={data.node?.template[name].value ?? ""}
</div> onChange={handleOnNewValue}
) : left === true && type === "file" ? ( />
<div className="mt-2 w-full"> </div>
<InputFileComponent ) : left === true && type === "prompt" ? (
disabled={disabled} <div className="mt-2 w-full">
value={data.node?.template[name].value ?? ""} <PromptAreaComponent
onChange={handleOnNewValue} field_name={name}
fileTypes={data.node?.template[name].fileTypes} setNodeClass={(nodeClass) => {
suffixes={data.node?.template[name].suffixes} data.node = nodeClass;
onFileChange={(filePath: string) => { }}
data.node!.template[name].file_path = filePath; nodeClass={data.node}
save(); disabled={disabled}
}} value={data.node?.template[name].value ?? ""}
></InputFileComponent> onChange={handleOnNewValue}
</div> />
) : left === true && type === "int" ? ( </div>
<div className="mt-2 w-full"> ) : (
<IntComponent <></>
disabled={disabled} )}
value={data.node?.template[name].value ?? ""} </>
onChange={handleOnNewValue} </div>
/> )
</div>
) : left === true && type === "prompt" ? (
<div className="mt-2 w-full">
<PromptAreaComponent
field_name={name}
setNodeClass={(nodeClass) => {
data.node = nodeClass;
}}
nodeClass={data.node}
disabled={disabled}
value={data.node?.template[name].value ?? ""}
onChange={handleOnNewValue}
/>
</div>
) : (
<></>
)}
</>
</div>
); );
} }

View file

@ -72,13 +72,15 @@ export default function GenericNode({
data={data} data={data}
setData={setData} setData={setData}
deleteNode={deleteNode} deleteNode={deleteNode}
setShowNode={setShowNode}
></NodeToolbarComponent> ></NodeToolbarComponent>
</NodeToolbar> </NodeToolbar>
<div <div
className={classNames( className={classNames(
selected ? "border border-ring" : "border", selected ? "border border-ring" : "border",
"generic-node-div" showNode ? "w-96" : "w-32",
"generic-node-div",
)} )}
> >
{data.node?.beta && ( {data.node?.beta && (
@ -87,183 +89,264 @@ export default function GenericNode({
</div> </div>
)} )}
<div> <div>
<div className="grid justify-items-end bg-muted pt-2 pr-2 border-b rounded-t-lg"> <div className={"generic-node-div-title " + (!showNode ? "relative rounded-t-lg rounded-b-lg" : "justify-between rounded-t-lg")}>
<button <div className={"generic-node-title-arrangement " + (!showNode && "justify-center")}>
onClick={() => setShowNode(prev => !prev)}
>
<IconComponent
name="Minus"
className="hover:text-accent-foreground text-muted-foreground"
/>
</button>
</div>
<div className="generic-node-div-title">
<div className="generic-node-title-arrangement">
<IconComponent <IconComponent
name={name} name={name}
className="generic-node-icon" className={"generic-node-icon " + (!showNode && "absolute left-10")}
iconColor={`${nodeColors[types[data.type]]}`} iconColor={`${nodeColors[types[data.type]]}`}
/> />
<div className="generic-node-tooltip-div"> {showNode && (
<ShadTooltip content={data.node?.display_name}> <div className="generic-node-tooltip-div">
<div className="generic-node-tooltip-div text-primary"> <ShadTooltip content={data.node?.display_name}>
{data.node?.display_name} <div className="generic-node-tooltip-div text-primary">
</div> {data.node?.display_name}
</ShadTooltip> </div>
</div> </ShadTooltip>
</div>
)}
</div> </div>
<div className="round-button-div"> <div>
<div> {!showNode && (
<Tooltip <>
title={ {Object.keys(data.node!.template)
isBuilding ? ( .filter((templateField) => templateField.charAt(0) !== "_")
<span>Building...</span> .map((templateField: string, idx) => (
) : !validationStatus ? ( <div key={idx}>
<span className="flex"> {data.node!.template[templateField].show &&
Build{" "} !data.node!.template[templateField].advanced && (
<IconComponent <ParameterComponent
name="Zap" key={
className="mx-0.5 h-5 fill-build-trigger stroke-build-trigger stroke-1" (data.node!.template[templateField].input_types?.join(
/>{" "} ";"
flow to validate status. ) ?? data.node!.template[templateField].type) +
</span> "|" +
) : ( templateField +
<div className="max-h-96 overflow-auto"> "|" +
{typeof validationStatus.params === "string" data.id
? validationStatus.params }
.split("\n") data={data}
.map((line: string, index: number) => ( setData={setData}
<div key={index}>{line}</div> color={
)) nodeColors[
: ""} types[data.node?.template[templateField].type!]
</div> ] ??
) nodeColors[data.node?.template[templateField].type!] ??
} nodeColors.unknown
> }
<div className="generic-node-status-position"> title={
<div data.node?.template[templateField].display_name
className={classNames( ? data.node.template[templateField].display_name
validationStatus && validationStatus.valid : data.node?.template[templateField].name
? "green-status" ? toTitleCase(data.node.template[templateField].name)
: "status-build-animation", : toTitleCase(templateField)
"status-div" }
info={data.node?.template[templateField].info}
name={templateField}
tooltipTitle={
data.node?.template[templateField].input_types?.join(
"\n"
) ?? data.node?.template[templateField].type
}
required={data.node?.template[templateField].required}
id={
(data.node?.template[templateField].input_types?.join(
";"
) ?? data.node?.template[templateField].type) +
"|" +
templateField +
"|" +
data.id
}
left={true}
type={data.node?.template[templateField].type}
optionalHandle={
data.node?.template[templateField].input_types
}
showNode={showNode}
/>
)} )}
></div> </div>))}
<div <ParameterComponent
className={classNames( key={[data.type, data.id, ...data.node!.base_classes].join("|")}
validationStatus && !validationStatus.valid data={data}
? "red-status" setData={setData}
: "status-build-animation", color={nodeColors[types[data.type]] ?? nodeColors.unknown}
"status-div" title={
)} data.node?.output_types && data.node.output_types.length > 0
></div> ? data.node.output_types.join("|")
<div : data.type
className={classNames( }
!validationStatus || isBuilding tooltipTitle={data.node?.base_classes.join("\n")}
? "yellow-status" id={[data.type, data.id, ...data.node!.base_classes].join("|")}
: "status-build-animation", type={data.node?.base_classes.join("|")}
"status-div" left={false}
)} showNode={showNode}
></div> />
</div> </>
</Tooltip> )}
</div>
</div> </div>
{showNode && (
<div className="round-button-div">
<div>
<Tooltip
title={
isBuilding ? (
<span>Building...</span>
) : !validationStatus ? (
<span className="flex">
Build{" "}
<IconComponent
name="Zap"
className="mx-0.5 h-5 fill-build-trigger stroke-build-trigger stroke-1"
/>{" "}
flow to validate status.
</span>
) : (
<div className="max-h-96 overflow-auto">
{typeof validationStatus.params === "string"
? validationStatus.params
.split("\n")
.map((line: string, index: number) => (
<div key={index}>{line}</div>
))
: ""}
</div>
)
}
>
<div className="generic-node-status-position">
<div
className={classNames(
validationStatus && validationStatus.valid
? "green-status"
: "status-build-animation",
"status-div"
)}
></div>
<div
className={classNames(
validationStatus && !validationStatus.valid
? "red-status"
: "status-build-animation",
"status-div"
)}
></div>
<div
className={classNames(
!validationStatus || isBuilding
? "yellow-status"
: "status-build-animation",
"status-div"
)}
></div>
</div>
</Tooltip>
</div>
</div>
)}
</div> </div>
</div> </div>
<div className={"generic-node-desc " + (data.node?.description !== "" && showNode ? "py-5" : "pb-5")}> {showNode && (
{data.node?.description !== "" && showNode && ( <div className={(showNode ? "generic-node-desc " + (data.node?.description !== "" && showNode ? "py-5" : "pb-5") : "")}>
<div className="generic-node-desc-text">{data.node?.description}</div> {data.node?.description !== "" && showNode && (
)} <div className="generic-node-desc-text">{data.node?.description}</div>
)}
<> <>
{Object.keys(data.node!.template) {Object.keys(data.node!.template)
.filter((templateField) => templateField.charAt(0) !== "_") .filter((templateField) => templateField.charAt(0) !== "_")
.map((templateField: string, idx) => ( .map((templateField: string, idx) => (
<div key={idx}> <div key={idx}>
{data.node!.template[templateField].show && {data.node!.template[templateField].show &&
!data.node!.template[templateField].advanced && !data.node!.template[templateField].advanced ? (
showNode ? ( <ParameterComponent
<ParameterComponent key={
key={ (data.node!.template[templateField].input_types?.join(
(data.node!.template[templateField].input_types?.join( ";"
";" ) ?? data.node!.template[templateField].type) +
) ?? data.node!.template[templateField].type) + "|" +
"|" + templateField +
templateField + "|" +
"|" + data.id
data.id }
} data={data}
data={data} setData={setData}
setData={setData} color={
color={ nodeColors[
nodeColors[ types[data.node?.template[templateField].type!]
types[data.node?.template[templateField].type!] ] ??
] ?? nodeColors[data.node?.template[templateField].type!] ??
nodeColors[data.node?.template[templateField].type!] ?? nodeColors.unknown
nodeColors.unknown }
} title={
title={ data.node?.template[templateField].display_name
data.node?.template[templateField].display_name ? data.node.template[templateField].display_name
? data.node.template[templateField].display_name : data.node?.template[templateField].name
: data.node?.template[templateField].name ? toTitleCase(data.node.template[templateField].name)
? toTitleCase(data.node.template[templateField].name) : toTitleCase(templateField)
: toTitleCase(templateField) }
} info={data.node?.template[templateField].info}
info={data.node?.template[templateField].info} name={templateField}
name={templateField} tooltipTitle={
tooltipTitle={ data.node?.template[templateField].input_types?.join(
data.node?.template[templateField].input_types?.join( "\n"
"\n" ) ?? data.node?.template[templateField].type
) ?? data.node?.template[templateField].type }
} required={data.node?.template[templateField].required}
required={data.node?.template[templateField].required} id={
id={ (data.node?.template[templateField].input_types?.join(
(data.node?.template[templateField].input_types?.join( ";"
";" ) ?? data.node?.template[templateField].type) +
) ?? data.node?.template[templateField].type) + "|" +
"|" + templateField +
templateField + "|" +
"|" + data.id
data.id }
} left={true}
left={true} type={data.node?.template[templateField].type}
type={data.node?.template[templateField].type} optionalHandle={
optionalHandle={ data.node?.template[templateField].input_types
data.node?.template[templateField].input_types }
} showNode={showNode}
/> />
) : ( ) : (
<></> <></>
)} )}
</div> </div>
))} ))}
<div <div
className={classNames( className={classNames(
Object.keys(data.node!.template).length < 1 ? "hidden" : "", Object.keys(data.node!.template).length < 1 ? "hidden" : "",
"flex-max-width justify-center" "flex-max-width justify-center"
)} )}
> >
{" "} {" "}
</div> </div>
<ParameterComponent <ParameterComponent
key={[data.type, data.id, ...data.node!.base_classes].join("|")} key={[data.type, data.id, ...data.node!.base_classes].join("|")}
data={data} data={data}
setData={setData} setData={setData}
color={nodeColors[types[data.type]] ?? nodeColors.unknown} color={nodeColors[types[data.type]] ?? nodeColors.unknown}
title={ title={
data.node?.output_types && data.node.output_types.length > 0 data.node?.output_types && data.node.output_types.length > 0
? data.node.output_types.join("|") ? data.node.output_types.join("|")
: data.type : data.type
} }
tooltipTitle={data.node?.base_classes.join("\n")} tooltipTitle={data.node?.base_classes.join("\n")}
id={[data.type, data.id, ...data.node!.base_classes].join("|")} id={[data.type, data.id, ...data.node!.base_classes].join("|")}
type={data.node?.base_classes.join("|")} type={data.node?.base_classes.join("|")}
left={false} left={false}
/> showNode={showNode}
</> />
</div> </>
</div>
)}
</div> </div>
</> </>
); );

View file

@ -11,6 +11,7 @@ export default function NodeToolbarComponent({
data, data,
setData, setData,
deleteNode, deleteNode,
setShowNode,
}: nodeToolbarPropsType): JSX.Element { }: nodeToolbarPropsType): JSX.Element {
const [nodeLength, setNodeLength] = useState( const [nodeLength, setNodeLength] = useState(
Object.keys(data.node!.template).filter( Object.keys(data.node!.template).filter(
@ -106,7 +107,7 @@ export default function NodeToolbarComponent({
> >
<div <div
className={classNames( className={classNames(
"relative -ml-px inline-flex items-center rounded-r-md bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10" + "relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10" +
(nodeLength == 0 (nodeLength == 0
? " text-muted-foreground" ? " text-muted-foreground"
: " text-foreground") : " text-foreground")
@ -117,6 +118,17 @@ export default function NodeToolbarComponent({
</EditNodeModal> </EditNodeModal>
</div> </div>
</ShadTooltip> </ShadTooltip>
<ShadTooltip content="Minimize" side="top">
<button
className="relative inline-flex items-center rounded-r-md bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10"
onClick={() => {
setShowNode(prev => !prev);
}}
>
<IconComponent name="Minus" className="h-4 w-4" />
</button>
</ShadTooltip>
</span> </span>
</div> </div>
</> </>

View file

@ -264,10 +264,10 @@
@apply grid w-full gap-4 p-4 md:grid-cols-2 lg:grid-cols-4; @apply grid w-full gap-4 p-4 md:grid-cols-2 lg:grid-cols-4;
} }
.generic-node-div { .generic-node-div {
@apply relative flex w-96 flex-col justify-center rounded-lg bg-background; @apply relative flex flex-col justify-center rounded-lg bg-background;
} }
.generic-node-div-title { .generic-node-div-title {
@apply flex w-full items-center justify-between gap-8 border-b bg-muted p-4; @apply flex w-full items-center gap-8 border-b bg-muted p-4;
} }
.generic-node-title-arrangement { .generic-node-title-arrangement {
@apply flex-max-width items-center truncate; @apply flex-max-width items-center truncate;

View file

@ -46,6 +46,7 @@ export type ParameterComponentType = {
dataContext?: typesContextType; dataContext?: typesContextType;
optionalHandle?: Array<String> | null; optionalHandle?: Array<String> | null;
info?: string; info?: string;
showNode?: boolean;
}; };
export type InputListComponentType = { export type InputListComponentType = {
value: string[]; value: string[];
@ -398,6 +399,7 @@ export type nodeToolbarPropsType = {
data: NodeDataType; data: NodeDataType;
deleteNode: (idx: string) => void; deleteNode: (idx: string) => void;
setData: (newState: NodeDataType) => void; setData: (newState: NodeDataType) => void;
setShowNode: (boolean: any) => void;
}; };
export type parsedDataType = { export type parsedDataType = {