Modularized Tags Selection and created share modal
This commit is contained in:
parent
b21b756982
commit
0a0c9ac27b
3 changed files with 133 additions and 164 deletions
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue