Modularized Tags Selection and created share modal

This commit is contained in:
Lucas Oliveira 2023-11-18 22:44:30 -03:00
commit 0a0c9ac27b
3 changed files with 133 additions and 164 deletions

View file

@ -1,26 +1,101 @@
import TagComponent from "./tagComponent";
import { useEffect, useRef, useState } from "react";
import { cn } from "../../utils/utils";
import { Badge } from "../ui/badge";
export function TagsSelector({
tags,
disabled = false,
loadingTags,
selectedTags,
setSelectedTags,
}: {
tags: string[];
selectedTags: Set<string>;
setSelectedTags: (tag: string) => void;
tags: { id: string; name: string }[];
disabled?: boolean;
loadingTags: boolean;
selectedTags: any[];
setSelectedTags: (tags: any[]) => void;
}) {
const updateTags = (tagName: string) => {
const index = selectedTags.indexOf(tagName);
let newArray =
index === -1
? [...selectedTags, tagName]
: selectedTags.filter((_, i) => i !== index);
setSelectedTags(newArray);
};
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
scrollContainer.dispatchEvent(new Event("scroll"));
return () => scrollContainer.removeEventListener("scroll", handleScroll);
}
}, [divWidth, loadingTags]); // Depend on divWidth
return (
<div className=" flex h-full w-full flex-row flex-wrap gap-1 align-middle">
{tags.map((tag, index) => {
return (
<TagComponent
tag={tag}
selected={selectedTags.has(tag)}
key={index}
handleClick={setSelectedTags}
/>
);
})}
<div ref={fadeContainerRef} className="fade-container">
<div ref={scrollContainerRef} className="scroll-container flex gap-2">
{!loadingTags &&
tags.map((tag, idx) => (
<button
disabled={disabled}
className={disabled ? "cursor-not-allowed" : ""}
onClick={() => {
updateTags(tag.name);
}}
>
<Badge
key={idx}
variant="outline"
size="sq"
className={cn(
selectedTags.some((category) => category === tag.name)
? "bg-beta-foreground text-background hover:bg-beta-foreground"
: ""
)}
>
{tag.name}
</Badge>
</button>
))}
</div>
</div>
);
}

View file

@ -9,7 +9,6 @@ import {
import EditFlowSettings from "../../components/EditFlowSettingsComponent";
import IconComponent from "../../components/genericIconComponent";
import { TagsSelector } from "../../components/tagsSelectorComponent";
import ToggleShadComponent from "../../components/toggleShadComponent";
import { Button } from "../../components/ui/button";
import { Checkbox } from "../../components/ui/checkbox";
import { alertContext } from "../../contexts/alertContext";
@ -42,36 +41,29 @@ const ShareModal = forwardRef(
const nameComponent = props.is_component ? "Component" : "Flow";
const [tags, setTags] = useState<string[]>([]);
const [tags, setTags] = useState<{ id: string; name: string }[]>([]);
const [loadingTags, setLoadingTags] = useState<boolean>(false);
const [sharePublic, setSharePublic] = useState(true);
const [selectedTags, setSelectedTags] = useState<Set<string>>(new Set());
const [selectedTags, setSelectedTags] = useState<string[]>([]);
const tagListId = useRef<{ id: string; name: string }[]>([]);
useEffect(() => {
getStoreTags().then((res) => {
tagListId.current = res;
let tags = res.map((tag) => tag.name);
setTags(tags);
});
handleGetTags();
}, []);
function handleGetTags() {
setLoadingTags(true);
getStoreTags().then((res) => {
setTags(res);
setLoadingTags(false);
});
}
useEffect(() => {
setName(props.component?.name ?? "");
setDescription(props.component?.description ?? "");
}, [props.component]);
function handleTagSelection(tag: string) {
setSelectedTags((prev) => {
const newSet = new Set(prev);
if (newSet.has(tag)) {
newSet.delete(tag);
} else {
newSet.add(tag);
}
return newSet;
});
}
const handleShareComponent = () => {
const saveFlow: FlowType = checked
? {
@ -92,7 +84,7 @@ const ShareModal = forwardRef(
});
saveFlowStore(
saveFlow,
getTagsIds(Array.from(selectedTags), tagListId),
getTagsIds(selectedTags, tagListId),
sharePublic
).then(
() => {
@ -138,6 +130,27 @@ const ShareModal = forwardRef(
setName={setName}
setDescription={setDescription}
/>
<div className="mt-3 flex h-8 w-full">
<TagsSelector
tags={tags}
loadingTags={loadingTags}
disabled={false}
selectedTags={selectedTags}
setSelectedTags={setSelectedTags}
/>
</div>
<div className="mt-5 flex items-center space-x-2">
<Checkbox
id="terms"
checked={sharePublic}
onCheckedChange={(event: boolean) => {
setSharePublic(event);
}}
/>
<label htmlFor="terms" className="export-modal-save-api text-sm ">
Make {nameComponent} Public
</label>
</div>
<div className="mt-3 flex items-center space-x-2">
<Checkbox
id="terms"
@ -154,42 +167,6 @@ const ShareModal = forwardRef(
Caution: Uncheck this box only removes API keys from fields
specifically designated for API keys.
</span>
<div className="flex h-full w-full flex-col gap-3">
<div className="flex justify-start pt-4 align-middle">
<ToggleShadComponent
disabled={false}
size="medium"
setEnabled={setSharePublic}
enabled={sharePublic}
/>
<div
className="cursor-pointer pl-1"
onClick={() => {
setSharePublic(!sharePublic);
}}
>
{sharePublic ? (
<span>
This flow will be avaliable <b>for everyone</b>
</span>
) : (
<span>
This flow will be avaliable <b>just for you</b>
</span>
)}
</div>
</div>
<div className="w-full pt-2">
<span className="text-sm">
Add some tags to your {nameComponent}
</span>
<TagsSelector
tags={tags}
selectedTags={selectedTags}
setSelectedTags={handleTagSelection}
/>
</div>
</div>
</BaseModal.Content>
<BaseModal.Footer>

View file

@ -1,15 +1,15 @@
import { uniqueId } from "lodash";
import { useContext, useEffect, useRef, useState } from "react";
import { useContext, useEffect, useState } from "react";
import PaginatorComponent from "../../components/PaginatorComponent";
import ShadTooltip from "../../components/ShadTooltipComponent";
import CollectionCardComponent from "../../components/cardComponent";
import IconComponent from "../../components/genericIconComponent";
import PageLayout from "../../components/pageLayout";
import { SkeletonCardComponent } from "../../components/skeletonCardComponent";
import { Badge } from "../../components/ui/badge";
import { Button } from "../../components/ui/button";
import { Input } from "../../components/ui/input";
import { TagsSelector } from "../../components/tagsSelectorComponent";
import {
Select,
SelectContent,
@ -44,51 +44,6 @@ 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
scrollContainer.dispatchEvent(new Event("scroll"));
return () => scrollContainer.removeEventListener("scroll", handleScroll);
}
}, [divWidth, loadingTags]); // Depend on divWidth
useEffect(() => {
handleGetTags();
}, []);
@ -184,19 +139,6 @@ export default function StorePage(): JSX.Element {
});
}
const updateTags = (tagName: string) => {
setFilterCategories((prevArray) => {
const index = prevArray.indexOf(tagName);
if (index === -1) {
// Item does not exist in array, add it
return [...prevArray, tagName];
} else {
// Item exists in array, remove it
return prevArray.filter((_, i) => i !== index);
}
});
};
return (
<PageLayout
title="Langflow Store"
@ -320,38 +262,13 @@ export default function StorePage(): JSX.Element {
</SelectGroup>
</SelectContent>
</Select>
<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>
<TagsSelector
tags={tags}
loadingTags={loadingTags}
disabled={loading}
selectedTags={filteredCategories}
setSelectedTags={setFilterCategories}
/>
</div>
<div className="flex items-end justify-between">
<span className="px-0.5 text-sm text-muted-foreground">