Sync with langflow.store
This commit is contained in:
parent
62015744d2
commit
a2af222778
3 changed files with 135 additions and 53 deletions
|
|
@ -31,11 +31,11 @@ export default function CollectionCardComponent({
|
||||||
button?: JSX.Element;
|
button?: JSX.Element;
|
||||||
onDelete?: () => void;
|
onDelete?: () => void;
|
||||||
}) {
|
}) {
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
const { addFlow } = useContext(FlowsContext);
|
const { addFlow } = useContext(FlowsContext);
|
||||||
const [loadingLike, setLoadingLike] = useState(false);
|
|
||||||
const { setSuccessData, setErrorData } = useContext(alertContext);
|
const { setSuccessData, setErrorData } = useContext(alertContext);
|
||||||
const { setValidApiKey } = useContext(StoreContext);
|
const { setValidApiKey } = useContext(StoreContext);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [loadingLike, setLoadingLike] = useState(false);
|
||||||
const [liked_by_user, setLiked_by_user] = useState(
|
const [liked_by_user, setLiked_by_user] = useState(
|
||||||
data?.liked_by_user ?? false
|
data?.liked_by_user ?? false
|
||||||
);
|
);
|
||||||
|
|
@ -91,9 +91,9 @@ export default function CollectionCardComponent({
|
||||||
const tempNum = likes_count;
|
const tempNum = likes_count;
|
||||||
setLiked_by_user((prev) => !prev);
|
setLiked_by_user((prev) => !prev);
|
||||||
if (!temp) {
|
if (!temp) {
|
||||||
setLikes_count((prev) => prev + 1);
|
setLikes_count((prev) => Number(prev) + 1);
|
||||||
} else {
|
} else {
|
||||||
setLikes_count((prev) => prev - 1);
|
setLikes_count((prev) => Number(prev) - 1);
|
||||||
}
|
}
|
||||||
postLikeComponent(data.id)
|
postLikeComponent(data.id)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { uniqueId } from "lodash";
|
import { uniqueId } from "lodash";
|
||||||
import { useContext, useEffect, useState } from "react";
|
import { useContext, useEffect, useRef, useState } from "react";
|
||||||
import PaginatorComponent from "../../components/PaginatorComponent";
|
import PaginatorComponent from "../../components/PaginatorComponent";
|
||||||
import ShadTooltip from "../../components/ShadTooltipComponent";
|
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||||
import CollectionCardComponent from "../../components/cardComponent";
|
import CollectionCardComponent from "../../components/cardComponent";
|
||||||
|
|
@ -23,7 +23,7 @@ import { StoreContext } from "../../contexts/storeContext";
|
||||||
import { getStoreComponents, getStoreTags } from "../../controllers/API";
|
import { getStoreComponents, getStoreTags } from "../../controllers/API";
|
||||||
import StoreApiKeyModal from "../../modals/StoreApiKeyModal";
|
import StoreApiKeyModal from "../../modals/StoreApiKeyModal";
|
||||||
import { storeComponent } from "../../types/store";
|
import { storeComponent } from "../../types/store";
|
||||||
import { classNames, cn } from "../../utils/utils";
|
import { cn } from "../../utils/utils";
|
||||||
export default function StorePage(): JSX.Element {
|
export default function StorePage(): JSX.Element {
|
||||||
const { validApiKey, setValidApiKey, hasApiKey, loadingApiKey } =
|
const { validApiKey, setValidApiKey, hasApiKey, loadingApiKey } =
|
||||||
useContext(StoreContext);
|
useContext(StoreContext);
|
||||||
|
|
@ -43,6 +43,51 @@ export default function StorePage(): JSX.Element {
|
||||||
const [searchNow, setSearchNow] = useState("");
|
const [searchNow, setSearchNow] = useState("");
|
||||||
const [selectFilter, setSelectFilter] = useState("all");
|
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(() => {
|
useEffect(() => {
|
||||||
handleGetTags();
|
handleGetTags();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
@ -131,7 +176,7 @@ export default function StorePage(): JSX.Element {
|
||||||
setTotalRowsCount(0);
|
setTotalRowsCount(0);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setErrorData({
|
setErrorData({
|
||||||
title: "Error to get components.",
|
title: "Error getting components.",
|
||||||
list: [err["response"]["data"]["detail"]],
|
list: [err["response"]["data"]["detail"]],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -155,30 +200,32 @@ export default function StorePage(): JSX.Element {
|
||||||
<>
|
<>
|
||||||
<Header />
|
<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>
|
||||||
<div className="community-page-nav-arrangement">
|
<div className="flex w-full justify-between py-8 pb-2">
|
||||||
<span className="community-page-nav-title">
|
<span className="flex items-center justify-center gap-2 text-2xl font-semibold">
|
||||||
<IconComponent name="Store" className="w-6" />
|
<IconComponent name="Store" className="w-6" />
|
||||||
Langflow Store
|
Langflow Store
|
||||||
</span>
|
</span>
|
||||||
<div className="community-page-nav-button">
|
<div className="flex gap-2">
|
||||||
<StoreApiKeyModal disabled={loading}>
|
{StoreApiKeyModal && (
|
||||||
<Button
|
<StoreApiKeyModal disabled={loading}>
|
||||||
disabled={loading}
|
<Button
|
||||||
className={classNames(
|
disabled={loading}
|
||||||
`${!validApiKey ? "animate-pulse border-error" : ""}`,
|
className={cn(
|
||||||
loading ? "cursor-not-allowed" : ""
|
`${!validApiKey ? "animate-pulse border-error" : ""}`,
|
||||||
)}
|
loading ? "cursor-not-allowed" : ""
|
||||||
variant="primary"
|
)}
|
||||||
>
|
variant="primary"
|
||||||
<IconComponent name="Key" className="main-page-nav-button" />
|
>
|
||||||
API Key
|
<IconComponent name="Key" className="mr-2 w-4" />
|
||||||
</Button>
|
API Key
|
||||||
</StoreApiKeyModal>
|
</Button>
|
||||||
|
</StoreApiKeyModal>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</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.
|
Search flows and components from the community.
|
||||||
</span>
|
</span>
|
||||||
<div className="flex w-full flex-col gap-4 p-0">
|
<div className="flex w-full flex-col gap-4 p-0">
|
||||||
|
|
@ -264,13 +311,13 @@ export default function StorePage(): JSX.Element {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex h-6 items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Select
|
<Select
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
onValueChange={setSelectFilter}
|
onValueChange={setSelectFilter}
|
||||||
value={selectFilter}
|
value={selectFilter}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="mr-4 w-[160px]">
|
<SelectTrigger className="mr-4 w-[160px] flex-shrink-0">
|
||||||
<SelectValue placeholder="Filter Values" />
|
<SelectValue placeholder="Filter Values" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
|
|
@ -281,31 +328,38 @@ export default function StorePage(): JSX.Element {
|
||||||
</SelectGroup>
|
</SelectGroup>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
{!loadingTags &&
|
<div ref={fadeContainerRef} className="fade-container">
|
||||||
tags.map((tag, idx) => (
|
<div
|
||||||
<button
|
ref={scrollContainerRef}
|
||||||
disabled={loading}
|
className="scroll-container flex gap-2"
|
||||||
className={loading ? "cursor-not-allowed" : ""}
|
>
|
||||||
onClick={() => {
|
{!loadingTags &&
|
||||||
updateTags(tag.name);
|
tags.map((tag, idx) => (
|
||||||
}}
|
<button
|
||||||
>
|
disabled={loading}
|
||||||
<Badge
|
className={loading ? "cursor-not-allowed" : ""}
|
||||||
key={idx}
|
onClick={() => {
|
||||||
variant="outline"
|
updateTags(tag.name);
|
||||||
size="sq"
|
}}
|
||||||
className={cn(
|
>
|
||||||
filteredCategories.some(
|
<Badge
|
||||||
(category) => category === tag.name
|
key={idx}
|
||||||
)
|
variant="outline"
|
||||||
? "bg-beta-foreground text-background hover:bg-beta-foreground"
|
size="sq"
|
||||||
: ""
|
className={cn(
|
||||||
)}
|
filteredCategories.some(
|
||||||
>
|
(category) => category === tag.name
|
||||||
{tag.name}
|
)
|
||||||
</Badge>
|
? "bg-beta-foreground text-background hover:bg-beta-foreground"
|
||||||
</button>
|
: ""
|
||||||
))}
|
)}
|
||||||
|
>
|
||||||
|
{tag.name}
|
||||||
|
</Badge>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-end justify-between">
|
<div className="flex items-end justify-between">
|
||||||
<span className="px-0.5 text-sm text-muted-foreground">
|
<span className="px-0.5 text-sm text-muted-foreground">
|
||||||
|
|
@ -359,7 +413,7 @@ export default function StorePage(): JSX.Element {
|
||||||
|
|
||||||
{!loading && searchData?.length === 0 && (
|
{!loading && searchData?.length === 0 && (
|
||||||
<div className="mt-6 flex w-full items-center justify-center text-center">
|
<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="flex w-full flex-col gap-4">
|
||||||
<div className="grid w-full gap-4">
|
<div className="grid w-full gap-4">
|
||||||
You haven't{" "}
|
You haven't{" "}
|
||||||
|
|
|
||||||
|
|
@ -1033,4 +1033,32 @@
|
||||||
.input-invalid {
|
.input-invalid {
|
||||||
@apply border-destructive focus:border-destructive focus:ring-destructive;
|
@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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue