pangolin/src/components/tags/tag-list.tsx
2025-02-26 21:24:35 -05:00

205 lines
9 KiB
TypeScript

import React from "react";
import { TagInputStyleClassesProps, type Tag as TagType } from "./tag-input";
import { Tag, TagProps } from "./tag";
import SortableList, { SortableItem } from "react-easy-sort";
import { cn } from "@app/lib/cn";
export type TagListProps = {
tags: TagType[];
customTagRenderer?: (tag: TagType, isActiveTag: boolean) => React.ReactNode;
direction?: TagProps["direction"];
onSortEnd: (oldIndex: number, newIndex: number) => void;
className?: string;
inlineTags?: boolean;
activeTagIndex?: number | null;
setActiveTagIndex?: (index: number | null) => void;
classStyleProps: {
tagListClasses: TagInputStyleClassesProps["tagList"];
tagClasses: TagInputStyleClassesProps["tag"];
};
disabled?: boolean;
} & Omit<TagProps, "tagObj">;
const DropTarget: React.FC = () => {
return <div className={cn("h-full rounded-md bg-secondary/50")} />;
};
export const TagList: React.FC<TagListProps> = ({
tags,
customTagRenderer,
direction,
draggable,
onSortEnd,
className,
inlineTags,
activeTagIndex,
setActiveTagIndex,
classStyleProps,
disabled,
...tagListProps
}) => {
const [draggedTagId, setDraggedTagId] = React.useState<string | null>(null);
const handleMouseDown = (id: string) => {
setDraggedTagId(id);
};
const handleMouseUp = () => {
setDraggedTagId(null);
};
return (
<>
{!inlineTags ? (
<div
className={cn(
"rounded-md w-full",
// className,
{
"flex flex-wrap gap-2": direction === "row",
"flex flex-col gap-2": direction === "column"
},
classStyleProps?.tagListClasses?.container
)}
>
{draggable ? (
<SortableList
onSortEnd={onSortEnd}
// className="flex flex-wrap gap-2 list"
className={`flex flex-wrap gap-2 list ${classStyleProps?.tagListClasses?.sortableList}`}
dropTarget={<DropTarget />}
>
{tags.map((tagObj, index) => (
<SortableItem key={tagObj.id}>
<div
onMouseDown={() =>
handleMouseDown(tagObj.id)
}
onMouseLeave={handleMouseUp}
className={cn(
{
"border border-solid border-primary rounded-md":
draggedTagId === tagObj.id
},
"transition-all duration-200 ease-in-out"
)}
>
{customTagRenderer ? (
customTagRenderer(
tagObj,
index === activeTagIndex
)
) : (
<Tag
tagObj={tagObj}
isActiveTag={
index === activeTagIndex
}
direction={direction}
draggable={draggable}
tagClasses={
classStyleProps?.tagClasses
}
{...tagListProps}
disabled={disabled}
/>
)}
</div>
</SortableItem>
))}
</SortableList>
) : (
tags.map((tagObj, index) =>
customTagRenderer ? (
customTagRenderer(
tagObj,
index === activeTagIndex
)
) : (
<Tag
key={tagObj.id}
tagObj={tagObj}
isActiveTag={index === activeTagIndex}
direction={direction}
draggable={draggable}
tagClasses={classStyleProps?.tagClasses}
{...tagListProps}
disabled={disabled}
/>
)
)
)}
</div>
) : (
<>
{draggable ? (
<SortableList
onSortEnd={onSortEnd}
className="flex flex-wrap gap-2 list"
dropTarget={<DropTarget />}
>
{tags.map((tagObj, index) => (
<SortableItem key={tagObj.id}>
<div
onMouseDown={() =>
handleMouseDown(tagObj.id)
}
onMouseLeave={handleMouseUp}
className={cn(
{
"border border-solid border-primary rounded-md":
draggedTagId === tagObj.id
},
"transition-all duration-200 ease-in-out"
)}
>
{customTagRenderer ? (
customTagRenderer(
tagObj,
index === activeTagIndex
)
) : (
<Tag
tagObj={tagObj}
isActiveTag={
index === activeTagIndex
}
direction={direction}
draggable={draggable}
tagClasses={
classStyleProps?.tagClasses
}
{...tagListProps}
disabled={disabled}
/>
)}
</div>
</SortableItem>
))}
</SortableList>
) : (
tags.map((tagObj, index) =>
customTagRenderer ? (
customTagRenderer(
tagObj,
index === activeTagIndex
)
) : (
<Tag
key={tagObj.id}
tagObj={tagObj}
isActiveTag={index === activeTagIndex}
direction={direction}
draggable={draggable}
tagClasses={classStyleProps?.tagClasses}
{...tagListProps}
disabled={disabled}
/>
)
)
)}
</>
)}
</>
);
};