diff --git a/src/frontend/src/components/cardComponent/index.tsx b/src/frontend/src/components/cardComponent/index.tsx index dfe09f9ee..42711750f 100644 --- a/src/frontend/src/components/cardComponent/index.tsx +++ b/src/frontend/src/components/cardComponent/index.tsx @@ -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) => { diff --git a/src/frontend/src/pages/StorePage/index.tsx b/src/frontend/src/pages/StorePage/index.tsx index 4dbab7504..11ce76a41 100644 --- a/src/frontend/src/pages/StorePage/index.tsx +++ b/src/frontend/src/pages/StorePage/index.tsx @@ -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(null); + const fadeContainerRef = useRef(null); + const [divWidth, setDivWidth] = useState(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 { <>
-
+
-
- +
+ Langflow Store -
- - - +
+ {StoreApiKeyModal && ( + + + + )}
- + Search flows and components from the community.
@@ -264,13 +311,13 @@ export default function StorePage(): JSX.Element {
-
+
- {!loadingTags && - tags.map((tag, idx) => ( - - ))} +
+
+ {!loadingTags && + tags.map((tag, idx) => ( + + ))} +
+
@@ -359,7 +413,7 @@ export default function StorePage(): JSX.Element { {!loading && searchData?.length === 0 && (
-
+
You haven't{" "} diff --git a/src/frontend/src/style/applies.css b/src/frontend/src/style/applies.css index 3dcbb6df3..8270aac85 100644 --- a/src/frontend/src/style/applies.css +++ b/src/frontend/src/style/applies.css @@ -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; + } }