langflow/src/frontend/src/components/codeTabsComponent/index.tsx
2023-08-21 17:47:37 -03:00

786 lines
44 KiB
TypeScript

import { cloneDeep } from "lodash";
import { useContext, useEffect, useState } from "react";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
import AccordionComponent from "../../components/AccordionComponent";
import ShadTooltip from "../../components/ShadTooltipComponent";
import CodeAreaComponent from "../../components/codeAreaComponent";
import Dropdown from "../../components/dropdownComponent";
import FloatComponent from "../../components/floatComponent";
import InputComponent from "../../components/inputComponent";
import InputFileComponent from "../../components/inputFileComponent";
import InputListComponent from "../../components/inputListComponent";
import IntComponent from "../../components/intComponent";
import PromptAreaComponent from "../../components/promptComponent";
import TextAreaComponent from "../../components/textAreaComponent";
import ToggleShadComponent from "../../components/toggleShadComponent";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "../../components/ui/table";
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "../../components/ui/tabs";
import { darkContext } from "../../contexts/darkContext";
import { codeTabsPropsType } from "../../types/components";
import { classNames } from "../../utils/utils";
import IconComponent from "../genericIconComponent";
export default function CodeTabsComponent({
flow,
tabs,
activeTab,
setActiveTab,
isMessage,
tweaks,
}: codeTabsPropsType) {
const [isCopied, setIsCopied] = useState<Boolean>(false);
const [data, setData] = useState(flow ? flow["data"]!["nodes"] : null);
const [openAccordion, setOpenAccordion] = useState<string[]>([]);
const { dark } = useContext(darkContext);
useEffect(() => {
if (flow && flow["data"]!["nodes"]) {
setData(flow["data"]!["nodes"]);
}
}, [flow]);
const copyToClipboard = () => {
if (!navigator.clipboard || !navigator.clipboard.writeText) {
return;
}
navigator.clipboard.writeText(tabs[activeTab].code).then(() => {
setIsCopied(true);
setTimeout(() => {
setIsCopied(false);
}, 2000);
});
};
const downloadAsFile = () => {
const fileExtension = tabs[activeTab].language || ".txt";
const suggestedFileName = `${"generated-code."}${fileExtension}`;
const fileName = window.prompt("Enter the file name.", suggestedFileName);
if (!fileName) {
// user pressed cancel on prompt
return;
}
const blob = new Blob([tabs[activeTab].code], { type: "text/plain" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.download = fileName;
link.href = url;
link.style.display = "none";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
};
function openAccordions() {
let accordionsToOpen: string[] = [];
tweaks?.tweak!.current.forEach((el) => {
Object.keys(el).forEach((key) => {
if (Object.keys(el[key]).length > 0) {
accordionsToOpen.push(key);
setOpenAccordion(accordionsToOpen);
}
});
});
if (accordionsToOpen.length == 0) {
setOpenAccordion([]);
}
}
return (
<Tabs
value={activeTab}
className={
"api-modal-tabs " +
(isMessage ? "dark " : "") +
(dark && isMessage ? "bg-background" : "")
}
onValueChange={(value) => {
setActiveTab(value);
if (value === "3") {
openAccordions();
}
}}
>
<div className="api-modal-tablist-div">
{tabs.length > 0 && tabs[0].name !== "" ? (
<TabsList>
{tabs.map((tab, index) => (
<TabsTrigger
className={
isMessage ? "data-[state=active]:bg-primary-foreground" : ""
}
key={index}
value={index.toString()}
>
{tab.name}
</TabsTrigger>
))}
</TabsList>
) : (
<div></div>
)}
{Number(activeTab) < 4 && (
<div className="float-right mx-1 mb-1 mt-2 flex gap-2">
<button
className="flex items-center gap-1.5 rounded bg-none p-1 text-xs text-gray-500 dark:text-gray-300"
onClick={copyToClipboard}
>
{isCopied ? (
<IconComponent name="Check" className="h-4 w-4" />
) : (
<IconComponent name="Clipboard" className="h-4 w-4" />
)}
{isCopied ? "Copied!" : "Copy code"}
</button>
<button
className="flex items-center gap-1.5 rounded bg-none p-1 text-xs text-gray-500 dark:text-gray-300"
onClick={downloadAsFile}
>
<IconComponent name="Download" className="h-5 w-5" />
</button>
</div>
)}
</div>
{tabs.map((tab, index) => (
<TabsContent
value={index.toString()}
className="api-modal-tabs-content"
key={index} // Remember to add a unique key prop
>
{index < 4 ? (
<>
{tab.description && (
<div
className="mb-2 w-full text-left text-sm"
dangerouslySetInnerHTML={{ __html: tab.description }}
></div>
)}
<SyntaxHighlighter
className="mt-0 h-full w-full overflow-auto custom-scroll"
language={tab.mode}
style={oneDark}
>
{tab.code}
</SyntaxHighlighter>
</>
) : index === 4 ? (
<>
<div className="api-modal-according-display">
<div
className={classNames(
"h-[70vh] w-full rounded-lg bg-muted",
1 == 1
? "overflow-scroll overflow-x-hidden custom-scroll"
: "overflow-hidden"
)}
>
{data?.map((node: any, index) => (
<div className="px-3" key={index}>
{tweaks?.tweaksList!.current.includes(
node["data"]["id"]
) && (
<AccordionComponent
trigger={node["data"]["id"]}
open={openAccordion}
keyValue={node["data"]["id"]}
>
<div className="api-modal-table-arrangement">
<Table className="table-fixed bg-muted outline-1">
<TableHeader className="h-10 border-input text-xs font-medium text-ring">
<TableRow className="dark:border-b-muted">
<TableHead className="h-7 text-center">
PARAM
</TableHead>
<TableHead className="h-7 p-0 text-center">
VALUE
</TableHead>
</TableRow>
</TableHeader>
<TableBody className="p-0">
{Object.keys(node["data"]["node"]["template"])
.filter(
(templateField) =>
templateField.charAt(0) !== "_" &&
node.data.node.template[templateField]
.show &&
(node.data.node.template[templateField]
.type === "str" ||
node.data.node.template[templateField]
.type === "bool" ||
node.data.node.template[templateField]
.type === "float" ||
node.data.node.template[templateField]
.type === "code" ||
node.data.node.template[templateField]
.type === "prompt" ||
node.data.node.template[templateField]
.type === "file" ||
node.data.node.template[templateField]
.type === "int")
)
.map((templateField, index) => {
return (
<TableRow
key={index}
className="h-10 dark:border-b-muted"
>
<TableCell className="p-0 text-center text-sm text-foreground">
{templateField}
</TableCell>
<TableCell className="p-0 text-xs text-foreground">
<div className="m-auto w-[250px]">
{node.data.node.template[
templateField
].type === "str" &&
!node.data.node.template[
templateField
].options ? (
<div className="mx-auto">
{node.data.node.template[
templateField
].list ? (
<InputListComponent
editNode={true}
disabled={false}
value={
!node.data.node.template[
templateField
].value ||
node.data.node.template[
templateField
].value === ""
? [""]
: node.data.node
.template[
templateField
].value
}
onChange={(target) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList!.find(
(obj) =>
obj.data.node
.template[
templateField
]
)!.data.node.template[
templateField
].value = target;
return newInputList;
});
tweaks.buildTweakObject!(
node["data"]["id"],
target,
node.data.node.template[
templateField
]
);
}}
/>
) : node.data.node.template[
templateField
].multiline ? (
<ShadTooltip
content={tweaks.buildContent!(
node.data.node.template[
templateField
].value
)}
>
<div>
<TextAreaComponent
disabled={false}
editNode={true}
value={
!node.data.node
.template[
templateField
].value ||
node.data.node
.template[
templateField
].value === ""
? ""
: node.data.node
.template[
templateField
].value
}
onChange={(target) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList!.find(
(obj) =>
obj.data.node
.template[
templateField
]
)!.data.node.template[
templateField
].value = target;
return newInputList;
});
tweaks.buildTweakObject!(
node["data"]["id"],
target,
node.data.node
.template[
templateField
]
);
}}
/>
</div>
</ShadTooltip>
) : (
<InputComponent
editNode={true}
disabled={false}
password={
node.data.node.template[
templateField
].password ?? false
}
value={
!node.data.node.template[
templateField
].value ||
node.data.node.template[
templateField
].value === ""
? ""
: node.data.node
.template[
templateField
].value
}
onChange={(target) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList!.find(
(obj) =>
obj.data.node
.template[
templateField
]
)!.data.node.template[
templateField
].value = target;
return newInputList;
});
tweaks.buildTweakObject!(
node["data"]["id"],
target,
node.data.node.template[
templateField
]
);
}}
/>
)}
</div>
) : node.data.node.template[
templateField
].type === "bool" ? (
<div className="ml-auto">
{" "}
<ToggleShadComponent
enabled={
node.data.node.template[
templateField
].value
}
setEnabled={(e) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList!.find(
(obj) =>
obj.data.node
.template[
templateField
]
)!.data.node.template[
templateField
].value = e;
return newInputList;
});
tweaks.buildTweakObject!(
node["data"]["id"],
e,
node.data.node.template[
templateField
]
);
}}
size="small"
disabled={false}
/>
</div>
) : node.data.node.template[
templateField
].type === "file" ? (
<ShadTooltip
content={tweaks.buildContent!(
!node.data.node.template[
templateField
].value ||
node.data.node.template[
templateField
].value === ""
? ""
: node.data.node.template[
templateField
].value
)}
>
<div className="mx-auto">
<InputFileComponent
editNode={true}
disabled={false}
value={
node.data.node.template[
templateField
].value ?? ""
}
onChange={(
target: any
) => {}}
fileTypes={
node.data.node.template[
templateField
].fileTypes
}
suffixes={
node.data.node.template[
templateField
].suffixes
}
onFileChange={(
value: any
) => {
node.data.node.template[
templateField
].file_path = value;
}}
></InputFileComponent>
</div>
</ShadTooltip>
) : node.data.node.template[
templateField
].type === "float" ? (
<div className="mx-auto">
<FloatComponent
disabled={false}
editNode={true}
value={
!node.data.node.template[
templateField
].value ||
node.data.node.template[
templateField
].value === ""
? ""
: node.data.node.template[
templateField
].value
}
onChange={(target) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList!.find(
(obj) =>
obj.data.node
.template[
templateField
]
)!.data.node.template[
templateField
].value = target;
return newInputList;
});
tweaks.buildTweakObject!(
node["data"]["id"],
target,
node.data.node.template[
templateField
]
);
}}
/>
</div>
) : node.data.node.template[
templateField
].type === "str" &&
node.data.node.template[
templateField
].options ? (
<div className="mx-auto">
<Dropdown
editNode={true}
apiModal={true}
options={
node.data.node.template[
templateField
].options
}
onSelect={(target) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList!.find(
(obj) =>
obj.data.node
.template[
templateField
]
)!.data.node.template[
templateField
].value = target;
return newInputList;
});
tweaks.buildTweakObject!(
node["data"]["id"],
target,
node.data.node.template[
templateField
]
);
}}
value={
!node.data.node.template[
templateField
].value ||
node.data.node.template[
templateField
].value === ""
? ""
: node.data.node.template[
templateField
].value
}
></Dropdown>
</div>
) : node.data.node.template[
templateField
].type === "int" ? (
<div className="mx-auto">
<IntComponent
disabled={false}
editNode={true}
value={
!node.data.node.template[
templateField
].value ||
node.data.node.template[
templateField
].value === ""
? ""
: node.data.node.template[
templateField
].value
}
onChange={(target) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList!.find(
(obj) =>
obj.data.node
.template[
templateField
]
)!.data.node.template[
templateField
].value = target;
return newInputList;
});
tweaks.buildTweakObject!(
node["data"]["id"],
target,
node.data.node.template[
templateField
]
);
}}
/>
</div>
) : node.data.node.template[
templateField
].type === "prompt" ? (
<ShadTooltip
content={tweaks.buildContent!(
!node.data.node.template[
templateField
].value ||
node.data.node.template[
templateField
].value === ""
? ""
: node.data.node.template[
templateField
].value
)}
>
<div className="mx-auto">
<PromptAreaComponent
editNode={true}
disabled={false}
value={
!node.data.node.template[
templateField
].value ||
node.data.node.template[
templateField
].value === ""
? ""
: node.data.node
.template[
templateField
].value
}
onChange={(target) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList!.find(
(obj) =>
obj.data.node
.template[
templateField
]
)!.data.node.template[
templateField
].value = target;
return newInputList;
});
tweaks.buildTweakObject!(
node["data"]["id"],
target,
node.data.node.template[
templateField
]
);
}}
/>
</div>
</ShadTooltip>
) : node.data.node.template[
templateField
].type === "code" ? (
<ShadTooltip
content={tweaks.buildContent!(
tweaks.getValue!(
node.data.node.template[
templateField
].value,
node.data,
node.data.node.template[
templateField
]
)
)}
>
<div className="mx-auto">
<CodeAreaComponent
disabled={false}
editNode={true}
value={
!node.data.node.template[
templateField
].value ||
node.data.node.template[
templateField
].value === ""
? ""
: node.data.node
.template[
templateField
].value
}
onChange={(target) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList!.find(
(obj) =>
obj.data.node
.template[
templateField
]
)!.data.node.template[
templateField
].value = target;
return newInputList;
});
tweaks.buildTweakObject!(
node["data"]["id"],
target,
node.data.node.template[
templateField
]
);
}}
/>
</div>
</ShadTooltip>
) : node.data.node.template[
templateField
].type === "Any" ? (
"-"
) : (
<div className="hidden"></div>
)}
</div>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</div>
</AccordionComponent>
)}
{tweaks?.tweaksList!.current.length === 0 && (
<>
<div className="pt-3">
No tweaks are available for this flow.
</div>
</>
)}
</div>
))}
</div>
</div>
</>
) : null}
</TabsContent>
))}
</Tabs>
);
}