Feat: Create flow toolbar and remove flow management buttons from sidebar (#1501)

This commit is contained in:
Igor Carvalho 2024-03-06 20:56:49 -03:00 committed by GitHub
commit 0de8c84d0c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 175 additions and 85 deletions

View file

@ -90,3 +90,12 @@ body {
.jv-indent::-webkit-scrollbar-thumb:hover {
background-color: #bbb !important;
}
.custom-hover {
transition: background-color 0.5s ease;
}
.custom-hover:hover {
background-color: rgba(99, 102, 241, 0.1); /* Medium indigo color with 20% opacity */
}

View file

@ -521,10 +521,10 @@ export default function GenericNode({
) : !validationStatus ? (
<span className="flex">{STATUS_BUILD}</span>
) : (
<div className="max-h-100">
<div className="max-h-100 p-2">
<div>
{lastRunTime && (
<div className="justify-left flex text-muted-foreground">
<div className="justify-left flex text-muted-foreground font-normal">
<div>{RUN_TIMESTAMP_PREFIX}</div>
<div className="ml-1 text-status-blue">
{lastRunTime}
@ -532,19 +532,19 @@ export default function GenericNode({
</div>
)}
</div>
<div className="justify-left flex text-muted-foreground">
<div className="justify-left flex text-muted-foreground font-normal">
<div>Duration:</div>
<div className="ml-1 text-status-blue">
<div className="ml-1 text-status-blue mb-3">
{validationStatus?.data.duration}
</div>
</div>
<hr />
<span className="flex justify-center text-muted-foreground ">
<span className="flex justify-center text-muted-foreground mt-2 mb-2 font-semibold">
Output
</span>
<div className="max-h-96 overflow-auto custom-scroll">
<div className="max-h-96 overflow-auto custom-scroll font-normal">
{validationString.split("\n").map((line, index) => (
<div key={index}>{line}</div>
<div className="font-normal" key={index}>{line}</div>
))}
</div>
</div>

View file

@ -20,7 +20,12 @@ import { Badge } from "../ui/badge";
import { Button } from "../ui/button";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs";
export default function IOView({ children, open, setOpen }): JSX.Element {
export default function IOView({ children, open, setOpen, disable }: {
children: JSX.Element;
open: boolean;
setOpen: (open: boolean) => void;
disable?: boolean;
}): JSX.Element {
const inputs = useFlowStore((state) => state.inputs).filter(
(input) => input.type !== "ChatInput"
);
@ -98,6 +103,7 @@ export default function IOView({ children, open, setOpen }): JSX.Element {
size={haveChat ? (selectedTab === 0 ? "large-thin" : "large") : "small"}
open={open}
setOpen={setOpen}
disable={disable}
>
<BaseModal.Trigger>{children}</BaseModal.Trigger>
{/* TODO ADAPT TO ALL TYPES OF INPUTS AND OUTPUTS */}

View file

@ -1,14 +1,26 @@
import { useEffect, useRef, useState } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import useFlowStore from "../../stores/flowStore";
import { ChatType } from "../../types/chat";
import IOView from "../IOview";
import ChatTrigger from "../ViewTriggers/chat";
import { Transition } from "@headlessui/react";
import ForwardedIconComponent from "../genericIconComponent";
import { Separator } from "../ui/separator";
import ShareModal from "../../modals/shareModal";
import { useStoreStore } from "../../stores/storeStore";
import useFlowsManagerStore from "../../stores/flowsManagerStore";
import { classNames } from "../../utils/utils";
import ApiModal from "../../modals/ApiModal";
export default function Chat({ flow }: ChatType): JSX.Element {
export default function FlowToolbar({ flow }: ChatType): JSX.Element {
const [open, setOpen] = useState(false);
const flowState = useFlowStore((state) => state.flowState);
const nodes = useFlowStore((state) => state.nodes);
const hasIO = useFlowStore((state) => state.hasIO);
const hasStore = useStoreStore((state) => state.hasStore);
const validApiKey = useStoreStore((state) => state.validApiKey);
const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
const hasApiKey = useStoreStore((state) => state.hasApiKey);
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
@ -29,16 +41,102 @@ export default function Chat({ flow }: ChatType): JSX.Element {
const prevNodesRef = useRef<any[] | undefined>();
const ModalMemo = useMemo(
() => (
<ShareModal
is_component={false}
component={currentFlow!}
disabled={!hasApiKey || !validApiKey || !hasStore}
>
<button
disabled={!hasApiKey || !validApiKey || !hasStore}
className={classNames(
"relative inline-flex w-full h-full items-center justify-center hover:bg-hover bg-muted hover:bg-background px-5 py-3 text-foreground transition-all duration-500 ease-in-out gap-[4px] text-sm font-semibold ",
!hasApiKey || !validApiKey || !hasStore
? " button-disable text-muted-foreground "
: ""
)}
>
<ForwardedIconComponent
name="Share3"
className={classNames(
"-m-0.5 -ml-1 h-6 w-6",
!hasApiKey || !validApiKey || !hasStore
? "extra-side-bar-save-disable"
: ""
)}
/>
Share
</button>
</ShareModal>
),
[hasApiKey, validApiKey, currentFlow, hasStore]
);
return (
<>
<div className="flex flex-col">
{/* <BuildTrigger open={open} flow={flow} /> */}
{hasIO && (
<IOView open={open} setOpen={setOpen}>
<ChatTrigger />
<Transition
show={true}
appear={true}
enter="transition ease-out duration-300"
enterFrom="translate-y-96"
enterTo="translate-y-0"
leave="transition ease-in duration-300"
leaveFrom="translate-y-0"
leaveTo="translate-y-96"
>
<div
className={
"shadow-round-btn-shadow hover:shadow-round-btn-shadow message-button-position flex items-center justify-center rounded-sm bg-muted shadow-md transition-all gap-7 border"
}
>
<div className="flex">
<div className="flex gap-1 text-medium-indigo rounded-sm transition-all w-full h-full">
{hasIO ? (
<IOView open={open} setOpen={setOpen} disable={!hasIO}>
<div className="relative inline-flex w-full items-center justify-center hover:bg-hover transition-all duration-500 ease-in-out px-5 py-3 text-medium-indigo ease-in-out gap-1 text-sm font-semibold transition-all">
<ForwardedIconComponent
name="Zap"
className={"message-button-icon h-5 w-5 transition-all"}
/>
Run
</div>
</IOView>
) : (
<div className={`relative inline-flex w-full items-center justify-center transition-all duration-500 ease-in-out px-5 py-3 text-muted-foreground ease-in-out gap-1 text-sm font-semibold cursor-not-allowed`}>
<ForwardedIconComponent
name="Zap"
className={"message-button-icon h-5 w-5 transition-all fill-muted-foreground stroke-muted-foreground"}
/>
Run
</div>
)}
</div>
<div>
<Separator orientation="vertical" />
</div>
<div className="flex items-center gap-2 cursor-pointer">
{currentFlow && currentFlow.data && (
<ApiModal flow={currentFlow}>
<div className={classNames("relative inline-flex w-full items-center justify-center hover:bg-hover px-5 py-3 text-foreground transition-all duration-500 ease-in-out gap-1 text-sm font-semibold")}>
<ForwardedIconComponent
name="Code2"
className={" h-5 w-5"}
/>
API
</div>
</ApiModal>
)}
</div>
<div>
<Separator orientation="vertical" />
</div>
<div className="flex items-center gap-2">
<div className={`side-bar-button ${!hasApiKey || !validApiKey || !hasStore ? " cursor-not-allowed" : " cursor-pointer"}`}>{ModalMemo}</div>
</div>
</div>
</div>
</Transition>
</>
);
}

View file

@ -18,6 +18,9 @@ import { cn } from "../../../../utils/utils";
import ShadTooltip from "../../../ShadTooltipComponent";
import IconComponent from "../../../genericIconComponent";
import { Button } from "../../../ui/button";
import { UPLOAD_ERROR_ALERT } from "../../../../constants/alerts_constants";
import ExportModal from "../../../../modals/exportModal";
import { useStoreStore } from "../../../../stores/storeStore";
export const MenuBar = ({
removeFunction,
@ -32,7 +35,9 @@ export const MenuBar = ({
const saveLoading = useFlowsManagerStore((state) => state.saveLoading);
const [openSettings, setOpenSettings] = useState(false);
const n = useFlowStore((state) => state.nodes);
const uploadFlow = useFlowsManagerStore((state) => state.uploadFlow);
const hasApiKey = useStoreStore((state) => state.hasApiKey);
const validApiKey = useStoreStore((state) => state.validApiKey);
const navigate = useNavigate();
const isBuilding = useFlowStore((state) => state.isBuilding);
@ -100,7 +105,28 @@ export const MenuBar = ({
/>
Settings
</DropdownMenuItem>
<DropdownMenuItem
className="cursor-pointer"
onClick={() => {
uploadFlow({ newProject: false, isComponent: false }).catch(
(error) => {
setErrorData({
title: UPLOAD_ERROR_ALERT,
list: [error],
});
}
);
}}
>
<IconComponent name="FileUp" className="header-menu-options " />
Import
</DropdownMenuItem>
<ExportModal>
<div className="header-menubar-item">
<IconComponent name="FileDown" className="header-menu-options" />
Export
</div>
</ExportModal>
<DropdownMenuItem
onClick={() => {
undo();

View file

@ -12,7 +12,6 @@ import ReactFlow, {
updateEdge,
} from "reactflow";
import GenericNode from "../../../../CustomNodes/GenericNode";
import Chat from "../../../../components/chatComponent";
import {
INVALID_SELECTION_ERROR_ALERT,
UPLOAD_ALERT_LIST,
@ -38,6 +37,7 @@ import { getRandomName, isWrappedWithClass } from "../../../../utils/utils";
import ConnectionLineComponent from "../ConnectionLineComponent";
import SelectionMenu from "../SelectionMenuComponent";
import ExtraSidebar from "../extraSidebarComponent";
import FlowToolbar from "../../../../components/chatComponent";
const nodeTypes = {
genericNode: GenericNode,
@ -481,7 +481,7 @@ export default function Page({
}}
/>
</ReactFlow>
{!view && <Chat flow={flow} />}
{!view && <FlowToolbar flow={flow} />}
</div>
) : (
<></>

View file

@ -235,66 +235,6 @@ export default function ExtraSidebar(): JSX.Element {
return (
<div className="side-bar-arrangement">
<div className="side-bar-buttons-arrangement">
{hasStore && validApiKey && (
<ShadTooltip
content={
!hasApiKey || !validApiKey
? "Please review your API key."
: "Share"
}
side="top"
styleClasses="cursor-default"
>
<div className="side-bar-button">{ModalMemo}</div>
</ShadTooltip>
)}
<div className="side-bar-button">
<ShadTooltip content="Import" side="top">
<button
className="extra-side-bar-buttons"
onClick={() => {
uploadFlow({ newProject: false, isComponent: false }).catch(
(error) => {
setErrorData({
title: UPLOAD_ERROR_ALERT,
list: [error],
});
}
);
}}
>
<IconComponent name="FileUp" className="side-bar-button-size " />
</button>
</ShadTooltip>
</div>
{(!hasApiKey || !validApiKey) && (
<ShadTooltip
content="Export"
side="top"
styleClasses="cursor-default"
>
<div className="side-bar-button">{ExportMemo}</div>
</ShadTooltip>
)}
<ShadTooltip content={"Code"} side="top">
<div className="side-bar-button">
{currentFlow && currentFlow.data && (
<ApiModal flow={currentFlow}>
<button className={"w-full "}>
<div className={classNames("extra-side-bar-buttons")}>
<IconComponent
name="Code2"
className={"side-bar-button-size"}
/>
</div>
</button>
</ApiModal>
)}
</div>
</ShadTooltip>
</div>
<Separator />
<div className="side-bar-search-div-placement">
<Input
onFocusCapture={() => handleBlur()}
@ -318,7 +258,7 @@ export default function ExtraSidebar(): JSX.Element {
/>
</div>
</div>
<Separator />
<div className="side-bar-components-div-arrangement">
{Object.keys(dataFilter)
.sort(sortKeys)

View file

@ -43,6 +43,7 @@ export const SidebarDraggableComponent = forwardRef(
const deleteComponent = useFlowsManagerStore(
(state) => state.deleteComponent
);
const version = useDarkStore((state) => state.version);
const [cursorPos, setCursorPos] = useState({ x: 0, y: 0 });
const popoverRef = useRef<HTMLDivElement>(null);

View file

@ -69,7 +69,7 @@
@apply flex h-full w-[14.5rem] flex-col overflow-hidden border-r scrollbar-hide;
}
.side-bar-search-div-placement {
@apply relative mx-auto mb-2 mt-2 flex items-center;
@apply relative mx-auto mb-2 mt-2 flex items-center py-3;
}
.side-bar-components-icon {
@apply h-6 w-4 text-ring;
@ -114,7 +114,10 @@
@apply pointer-events-none;
}
.extra-side-bar-buttons {
@apply relative inline-flex w-full items-center justify-center rounded-md bg-background px-2 py-2 text-foreground shadow-sm ring-1 ring-inset ring-input transition-all duration-500 ease-in-out;
@apply relative inline-flex w-full items-center justify-center rounded-md bg-background px-2 py-2 text-foreground transition-all duration-500 ease-in-out;
}
.header-menubar-item {
@apply relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 cursor-pointer;
}
.extra-side-bar-buttons:hover {
@apply hover:bg-muted;
@ -495,7 +498,7 @@
@apply flex items-center gap-0.5 rounded-md px-1.5 py-1 text-sm font-medium;
}
.header-menu-bar-display {
@apply flex max-w-[120px] cursor-pointer items-center gap-2 lg:max-w-[200px];
@apply flex max-w-[115px] cursor-pointer items-center gap-2 lg:max-w-[145px];
}
.header-menu-flow-name {
@apply flex-1 truncate;

View file

@ -50,6 +50,9 @@
--component-icon: #d8598a;
--flow-icon: #2f67d0;
--hover: #F2F4F5;
--disabled-run: #6366f1;
/* Colors that are shared in dark and light mode */
--blur-shared: #151923de;
@ -69,6 +72,8 @@
--background: 224 35% 7.5%; /* hsl(224 40% 10%) */
--foreground: 213 31% 80%; /* hsl(213 31% 91%) */
--ice: #60A5FA;
--hover: #1A202E;
--disabled-run: #6366f1;
--muted: 223 27% 11%; /* hsl(223 27% 11%) */
--muted-foreground: 215.4 16.3% 56.9%; /* hsl(215 16% 56%) */

View file

@ -32,6 +32,7 @@ import {
Cpu,
Database,
Delete,
Dot,
Download,
DownloadCloud,
Edit,
@ -478,4 +479,5 @@ export const nodeIconsLucide: iconsType = {
Bot,
Delete,
Command,
Dot,
};

View file

@ -87,8 +87,8 @@ module.exports = {
"beta-foreground": "var(--beta-foreground)",
"chat-bot-icon": "var(--chat-bot-icon)",
"chat-user-icon": "var(--chat-user-icon)",
ice: "var(--ice)",
"ice": "var(--ice)",
hover: "var(--hover)",
white: "var(--white)",
border: "hsl(var(--border))",
input: "hsl(var(--input))",