Sync with langflow.store

This commit is contained in:
Lucas Oliveira 2023-11-18 01:54:49 -03:00
commit a2af222778
3 changed files with 135 additions and 53 deletions

View file

@ -31,11 +31,11 @@ export default function CollectionCardComponent({
button?: JSX.Element;
onDelete?: () => void;
}) {
const [loading, setLoading] = useState(false);
const { addFlow } = useContext(FlowsContext);
const [loadingLike, setLoadingLike] = useState(false);
const { setSuccessData, setErrorData } = useContext(alertContext);
const { setValidApiKey } = useContext(StoreContext);
const [loading, setLoading] = useState(false);
const [loadingLike, setLoadingLike] = useState(false);
const [liked_by_user, setLiked_by_user] = useState(
data?.liked_by_user ?? false
);
@ -91,9 +91,9 @@ export default function CollectionCardComponent({
const tempNum = likes_count;
setLiked_by_user((prev) => !prev);
if (!temp) {
setLikes_count((prev) => prev + 1);
setLikes_count((prev) => Number(prev) + 1);
} else {
setLikes_count((prev) => prev - 1);
setLikes_count((prev) => Number(prev) - 1);
}
postLikeComponent(data.id)
.then((response) => {

View file

@ -1,5 +1,5 @@
import { uniqueId } from "lodash";
import { useContext, useEffect, useState } from "react";
import { useContext, useEffect, useRef, useState } from "react";
import PaginatorComponent from "../../components/PaginatorComponent";
import ShadTooltip from "../../components/ShadTooltipComponent";
import CollectionCardComponent from "../../components/cardComponent";
@ -23,7 +23,7 @@ import { StoreContext } from "../../contexts/storeContext";
import { getStoreComponents, getStoreTags } from "../../controllers/API";
import StoreApiKeyModal from "../../modals/StoreApiKeyModal";
import { storeComponent } from "../../types/store";
import { classNames, cn } from "../../utils/utils";
import { cn } from "../../utils/utils";
export default function StorePage(): JSX.Element {
const { validApiKey, setValidApiKey, hasApiKey, loadingApiKey } =
useContext(StoreContext);
@ -43,6 +43,51 @@ export default function StorePage(): JSX.Element {
const [searchNow, setSearchNow] = useState("");
const [selectFilter, setSelectFilter] = useState("all");
const scrollContainerRef = useRef<HTMLDivElement>(null);
const fadeContainerRef = useRef<HTMLDivElement>(null);
const [divWidth, setDivWidth] = useState<number>(0);
useEffect(() => {
const handleResize = () => {
if (scrollContainerRef.current) {
setDivWidth(scrollContainerRef.current.clientWidth);
}
};
window.addEventListener("resize", handleResize);
handleResize(); // call the function at start to get the initial width
return () => window.removeEventListener("resize", handleResize);
}, []);
useEffect(() => {
const handleScroll = () => {
if (!scrollContainerRef.current || !fadeContainerRef.current) return;
const { scrollLeft, scrollWidth, clientWidth } =
scrollContainerRef.current;
const atStart = scrollLeft === 0;
const atEnd = scrollLeft === scrollWidth - clientWidth;
const isScrollable = scrollWidth > clientWidth;
fadeContainerRef.current.classList.toggle(
"fade-left",
isScrollable && !atStart
);
fadeContainerRef.current.classList.toggle(
"fade-right",
isScrollable && !atEnd
);
};
const scrollContainer = scrollContainerRef.current;
if (scrollContainer) {
scrollContainer.addEventListener("scroll", handleScroll);
// Delay the initial scroll event dispatch to ensure correct calculation
setTimeout(() => scrollContainer.dispatchEvent(new Event("scroll")), 200);
return () => scrollContainer.removeEventListener("scroll", handleScroll);
}
}, [divWidth]); // Depend on divWidth
useEffect(() => {
handleGetTags();
}, []);
@ -131,7 +176,7 @@ export default function StorePage(): JSX.Element {
setTotalRowsCount(0);
setLoading(false);
setErrorData({
title: "Error to get components.",
title: "Error getting components.",
list: [err["response"]["data"]["detail"]],
});
}
@ -155,30 +200,32 @@ export default function StorePage(): JSX.Element {
<>
<Header />
<div className="community-page-arrangement">
<div className="flex h-full w-full flex-col justify-between overflow-auto bg-background px-16">
<div>
<div className="community-page-nav-arrangement">
<span className="community-page-nav-title">
<div className="flex w-full justify-between py-8 pb-2">
<span className="flex items-center justify-center gap-2 text-2xl font-semibold">
<IconComponent name="Store" className="w-6" />
Langflow Store
</span>
<div className="community-page-nav-button">
<StoreApiKeyModal disabled={loading}>
<Button
disabled={loading}
className={classNames(
`${!validApiKey ? "animate-pulse border-error" : ""}`,
loading ? "cursor-not-allowed" : ""
)}
variant="primary"
>
<IconComponent name="Key" className="main-page-nav-button" />
API Key
</Button>
</StoreApiKeyModal>
<div className="flex gap-2">
{StoreApiKeyModal && (
<StoreApiKeyModal disabled={loading}>
<Button
disabled={loading}
className={cn(
`${!validApiKey ? "animate-pulse border-error" : ""}`,
loading ? "cursor-not-allowed" : ""
)}
variant="primary"
>
<IconComponent name="Key" className="mr-2 w-4" />
API Key
</Button>
</StoreApiKeyModal>
)}
</div>
</div>
<span className="community-page-description-text">
<span className="flex w-[70%] pb-8 text-muted-foreground">
Search flows and components from the community.
</span>
<div className="flex w-full flex-col gap-4 p-0">
@ -264,13 +311,13 @@ export default function StorePage(): JSX.Element {
</div>
</div>
<div className="flex h-6 items-center gap-2">
<div className="flex items-center gap-2">
<Select
disabled={loading}
onValueChange={setSelectFilter}
value={selectFilter}
>
<SelectTrigger className="mr-4 w-[160px]">
<SelectTrigger className="mr-4 w-[160px] flex-shrink-0">
<SelectValue placeholder="Filter Values" />
</SelectTrigger>
<SelectContent>
@ -281,31 +328,38 @@ export default function StorePage(): JSX.Element {
</SelectGroup>
</SelectContent>
</Select>
{!loadingTags &&
tags.map((tag, idx) => (
<button
disabled={loading}
className={loading ? "cursor-not-allowed" : ""}
onClick={() => {
updateTags(tag.name);
}}
>
<Badge
key={idx}
variant="outline"
size="sq"
className={cn(
filteredCategories.some(
(category) => category === tag.name
)
? "bg-beta-foreground text-background hover:bg-beta-foreground"
: ""
)}
>
{tag.name}
</Badge>
</button>
))}
<div ref={fadeContainerRef} className="fade-container">
<div
ref={scrollContainerRef}
className="scroll-container flex gap-2"
>
{!loadingTags &&
tags.map((tag, idx) => (
<button
disabled={loading}
className={loading ? "cursor-not-allowed" : ""}
onClick={() => {
updateTags(tag.name);
}}
>
<Badge
key={idx}
variant="outline"
size="sq"
className={cn(
filteredCategories.some(
(category) => category === tag.name
)
? "bg-beta-foreground text-background hover:bg-beta-foreground"
: ""
)}
>
{tag.name}
</Badge>
</button>
))}
</div>
</div>
</div>
<div className="flex items-end justify-between">
<span className="px-0.5 text-sm text-muted-foreground">
@ -359,7 +413,7 @@ export default function StorePage(): JSX.Element {
{!loading && searchData?.length === 0 && (
<div className="mt-6 flex w-full items-center justify-center text-center">
<div className="flex-max-width h-full flex-col">
<div className="flex h-full w-full flex-col">
<div className="flex w-full flex-col gap-4">
<div className="grid w-full gap-4">
You haven't{" "}

View file

@ -1033,4 +1033,32 @@
.input-invalid {
@apply border-destructive focus:border-destructive focus:ring-destructive;
}
.fade-container {
@apply relative overflow-hidden;
}
.fade-container::before,
.fade-container::after {
@apply absolute top-0 bottom-0 pointer-events-none;
content: '';
width: 50px;
opacity: 0;
transition: opacity 0.3s;
background: linear-gradient(to right, white, transparent);
}
.fade-container::after {
@apply right-0;
transform: rotate(180deg);
}
.fade-container.fade-left::before,
.fade-container.fade-right::after {
opacity: 1;
}
.scroll-container {
@apply flex overflow-x-scroll scrollbar-hide;
}
}