fix: multiple labels should wrap on cards

This commit is contained in:
Henry
2025-02-01 10:43:31 +00:00
parent e421b4326c
commit c4c60916f7
6 changed files with 89 additions and 107 deletions

View File

@@ -0,0 +1,10 @@
import type { ReactNode } from "react";
const Badge = ({ value, iconLeft }: { value: string; iconLeft: ReactNode }) => (
<span className="mt-1 inline-flex w-fit items-center gap-x-1.5 rounded-full px-2 py-1 text-[10px] font-medium text-neutral-600 ring-1 ring-inset ring-light-600 dark:text-dark-1000 dark:ring-dark-800">
{iconLeft}
<div>{value}</div>
</span>
);
export default Badge;

View File

@@ -0,0 +1,54 @@
import Avatar from "~/components/Avatar";
import Badge from "~/components/Badge";
import LabelIcon from "~/components/LabelIcon";
import { getPublicUrl } from "~/utils/supabase/getPublicUrl";
const Card = ({
title,
labels,
members,
}: {
title: string;
labels: { name: string; colourCode: string | null }[];
members: {
publicId: string;
user: { name: string | null; email: string; image: string | null } | null;
}[];
}) => {
return (
<div className="flex flex-col rounded-md border border-light-200 bg-light-50 px-3 py-2 text-sm text-neutral-900 dark:border-dark-200 dark:bg-dark-200 dark:text-dark-1000 dark:hover:bg-dark-300">
<span>{title}</span>
{labels.length || members.length ? (
<div className="mt-2 flex flex-col justify-end">
<div className="space-x-0.5">
{labels.map((label) => (
<Badge
value={label.name}
iconLeft={<LabelIcon colourCode={label.colourCode} />}
/>
))}
</div>
<div className="isolate flex justify-end -space-x-1 overflow-hidden">
{members.map(({ user }) => {
if (!user) return null;
const avatarUrl = user.image
? getPublicUrl(user.image)
: undefined;
return (
<Avatar
name={user.name ?? ""}
email={user.email}
imageUrl={avatarUrl}
/>
);
})}
</div>
</div>
) : null}
</div>
);
};
export default Card;

View File

@@ -9,7 +9,6 @@ import { HiOutlinePlusSmall } from "react-icons/hi2";
import type { UpdateBoardInput } from "@kan/api/types";
import Avatar from "~/components/Avatar";
import Button from "~/components/Button";
import Modal from "~/components/modal";
import { NewWorkspaceForm } from "~/components/NewWorkspaceForm";
@@ -21,8 +20,8 @@ import { useModal } from "~/providers/modal";
import { useWorkspace } from "~/providers/workspace";
import { api } from "~/utils/api";
import { formatToArray } from "~/utils/helpers";
import { getPublicUrl } from "~/utils/supabase/getPublicUrl";
import BoardDropdown from "./components/BoardDropdown";
import Card from "./components/Card";
import { DeleteBoardConfirmation } from "./components/DeleteBoardConfirmation";
import { DeleteListConfirmation } from "./components/DeleteListConfirmation";
import Filters from "./components/Filters";
@@ -240,7 +239,7 @@ export default function BoardPage() {
}}
key={card.publicId}
href={`/cards/${card.publicId}`}
className={`mb-2 flex !cursor-pointer flex-col rounded-md border border-light-200 bg-light-50 px-3 py-2 text-sm text-neutral-900 dark:border-dark-200 dark:bg-dark-200 dark:text-dark-1000 dark:hover:bg-dark-300 ${
className={`mb-2 flex !cursor-pointer flex-col ${
card.publicId.startsWith("PLACEHOLDER")
? "pointer-events-none"
: ""
@@ -249,52 +248,11 @@ export default function BoardPage() {
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<div>{card.title}</div>
{(card.labels.length ?? 0) ||
(card.members.length ?? 0) ? (
<div className="mt-2 flex justify-end space-x-1">
{card.labels.map((label) => (
<span
key={label.publicId}
className="inline-flex w-fit items-center gap-x-1.5 rounded-full px-2 py-1 text-[10px] font-medium text-neutral-600 ring-1 ring-inset ring-light-600 dark:text-dark-1000 dark:ring-dark-800"
>
<svg
fill={
label.colourCode ?? undefined
}
className="h-2 w-2"
viewBox="0 0 6 6"
aria-hidden="true"
>
<circle cx={3} cy={3} r={3} />
</svg>
<div>{label.name}</div>
</span>
))}
<div className="isolate flex -space-x-1 overflow-hidden">
{card.members.map((member) => {
const fileName =
member.user?.image;
const avatarUrl = fileName
? getPublicUrl(fileName)
: undefined;
return (
<Avatar
key={member.publicId}
name={member.user?.name ?? ""}
email={
member.user?.email ?? ""
}
imageUrl={avatarUrl}
size="sm"
/>
);
})}
</div>
</div>
) : null}
<Card
title={card.title}
labels={card.labels}
members={card.members}
/>
</Link>
)}
</Draggable>

View File

@@ -3,6 +3,7 @@ import { Fragment } from "react";
import { useForm } from "react-hook-form";
import { HiEllipsisHorizontal, HiMiniPlus } from "react-icons/hi2";
import Badge from "~/components/Badge";
import LabelIcon from "~/components/LabelIcon";
import { useModal } from "~/providers/modal";
import { api } from "~/utils/api";
@@ -58,28 +59,19 @@ export default function LabelSelector({
className="relative flex w-full flex-wrap items-center text-left"
>
{selectedLabels.length ? (
<>
<div className="flex flex-wrap gap-x-0.5">
{selectedLabels.map((label) => (
<Menu.Button
key={label.publicId}
className="my-1 mr-2 inline-flex w-fit items-center gap-x-1.5 rounded-full px-2 py-1 text-[12px] font-medium text-light-800 ring-1 ring-inset ring-light-600 dark:text-dark-1000 dark:ring-dark-800"
>
<svg
fill={label.colourCode}
className="h-2 w-2"
viewBox="0 0 6 6"
aria-hidden="true"
>
<circle cx={3} cy={3} r={3} />
</svg>
<div>{label.name}</div>
<Menu.Button key={label.publicId}>
<Badge
value={label.name}
iconLeft={<LabelIcon colourCode={label.colourCode} />}
/>
</Menu.Button>
))}
<Menu.Button className="my-1 inline-flex w-fit items-center gap-x-1.5 rounded-full py-1 pl-2 pr-4 text-[12px] font-medium text-dark-800 ring-inset ring-dark-800 hover:bg-light-400 dark:hover:bg-dark-200">
<HiMiniPlus size={16} />
Add label
<Menu.Button>
<Badge value="Add label" iconLeft={<HiMiniPlus size={14} />} />
</Menu.Button>
</>
</div>
) : (
<Menu.Button className="flex h-full w-full items-center rounded-[5px] border-[1px] border-light-200 pl-2 text-left text-sm text-neutral-900 hover:bg-light-300 dark:border-dark-100 dark:text-dark-1000 dark:hover:border-dark-300 dark:hover:bg-dark-200">
<HiMiniPlus size={22} className="pr-2" />

View File

@@ -191,7 +191,7 @@ export default function CardPage() {
</div>
</div>
</div>
<div className="min-w-[325px] border-l-[1px] border-light-600 bg-light-200 p-8 text-light-900 dark:border-dark-400 dark:bg-dark-100 dark:text-dark-900">
<div className="w-[475px] border-l-[1px] border-light-600 bg-light-200 p-8 text-light-900 dark:border-dark-400 dark:bg-dark-100 dark:text-dark-900">
<div className="mb-4 flex w-full">
<p className="my-2 w-[100px] text-sm">List</p>
<ListSelector

View File

@@ -12,6 +12,7 @@ import { useModal } from "~/providers/modal";
import { usePopup } from "~/providers/popup";
import { api } from "~/utils/api";
import { formatToArray } from "~/utils/helpers";
import Card from "~/views/board/components/Card";
import Filters from "~/views/board/components/Filters";
import { CardModal } from "./CardModal";
@@ -132,50 +133,17 @@ export default function PublicBoardView() {
<Link
key={card.publicId}
href={`/${data.workspace?.slug}/${data.slug}/${card.publicId}`}
className={`mb-2 flex !cursor-pointer flex-col rounded-md border border-light-200 bg-light-50 px-3 py-2 text-sm text-neutral-900 dark:border-dark-200 dark:bg-dark-200 dark:text-dark-1000 dark:hover:bg-dark-300`}
className={`mb-2 flex !cursor-pointer flex-col`}
shallow={true}
onClick={() => {
openModal("CARD");
}}
>
<div>{card.title}</div>
{card.labels.length ? (
<div className="mt-2 flex justify-end space-x-1">
{card.labels.map((label) => (
<span
key={label.publicId}
className="inline-flex w-fit items-center gap-x-1.5 rounded-full px-2 py-1 text-[10px] font-medium text-neutral-600 ring-1 ring-inset ring-light-600 dark:text-dark-1000 dark:ring-dark-800"
>
<svg
fill={label.colourCode ?? undefined}
className="h-2 w-2"
viewBox="0 0 6 6"
aria-hidden="true"
>
<circle cx={3} cy={3} r={3} />
</svg>
<div>{label.name}</div>
</span>
))}
{/* <div className="isolate flex -space-x-1 overflow-hidden">
{card.members.map((member) => (
<span
key={member.publicId}
className="inline-flex h-6 w-6 items-center justify-center rounded-full bg-light-900 ring-2 ring-light-50 dark:bg-gray-500 dark:ring-dark-500"
>
<span className="text-[10px] font-medium leading-none text-white">
{member.user?.name
?.split(" ")
.map((namePart) =>
namePart.charAt(0).toUpperCase(),
)
.join("")}
</span>
</span>
))}
</div> */}
</div>
) : null}
<Card
title={card.title}
labels={card.labels}
members={[]}
/>
</Link>
))}
</div>