feat: dropdown enhancements
This commit is contained in:
@@ -4,13 +4,13 @@ export default function Dropdown({
|
||||
items,
|
||||
children,
|
||||
}: {
|
||||
items: { label: string; action: () => void }[];
|
||||
items: { label: string; action: () => void; icon?: React.ReactNode }[];
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<Menu as="div" className="relative inline-block text-left">
|
||||
<div>
|
||||
<Menu.Button className="flex h-8 w-8 items-center justify-center rounded-[5px] hover:bg-light-200 dark:hover:bg-dark-200">
|
||||
<Menu.Button className="flex h-7 w-7 items-center justify-center rounded-[5px] hover:bg-light-200 dark:hover:bg-dark-200">
|
||||
{children}
|
||||
</Menu.Button>
|
||||
</div>
|
||||
@@ -24,14 +24,15 @@ export default function Dropdown({
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items className="absolute right-0 z-30 mt-2 w-56 origin-top-right rounded-md border border-light-200 bg-light-50 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:border-dark-400 dark:bg-dark-300">
|
||||
<div className="flex">
|
||||
<Menu.Items className="absolute right-0 z-30 mt-2 w-56 origin-top-right rounded-md border border-light-200 bg-light-50 p-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:border-dark-400 dark:bg-dark-300">
|
||||
<div className="flex flex-col">
|
||||
{items.map((item) => (
|
||||
<Menu.Item key={item.label}>
|
||||
<button
|
||||
onClick={item.action}
|
||||
className="m-1 w-full rounded-[5px] px-3 py-2 text-left text-sm text-neutral-900 hover:bg-light-200 dark:text-dark-1000 dark:hover:bg-dark-400"
|
||||
className="flex w-auto items-center gap-2 rounded-[5px] px-2.5 py-1.5 text-left text-sm text-neutral-900 hover:bg-light-200 dark:text-dark-950 dark:hover:bg-dark-400"
|
||||
>
|
||||
{item.icon}
|
||||
{item.label}
|
||||
</button>
|
||||
</Menu.Item>
|
||||
|
||||
@@ -19,7 +19,7 @@ const FeedbackButton: React.FC = () => {
|
||||
<button
|
||||
type="button"
|
||||
onMouseEnter={handleMouseEnter}
|
||||
className="flex items-center rounded-md border-[1px] border-light-600 bg-light-50 px-2.5 py-1.5 text-sm font-normal text-neutral-900 shadow-sm dark:border-dark-600 dark:bg-dark-50 dark:text-dark-1000"
|
||||
className="flex items-center rounded-md border-[1px] border-light-600 bg-light-50 px-2.5 py-1.5 text-sm font-normal text-neutral-900 shadow-sm dark:border-dark-400 dark:bg-dark-50 dark:text-dark-1000"
|
||||
>
|
||||
<LottieIcon
|
||||
index={index}
|
||||
|
||||
@@ -50,7 +50,7 @@ export default function SideNavigation({
|
||||
|
||||
return (
|
||||
<>
|
||||
<nav className="flex w-72 flex-col justify-between border-r border-light-600 px-3 pb-3 pt-5 dark:border-dark-600">
|
||||
<nav className="flex w-72 flex-col justify-between border-r border-light-600 px-3 pb-3 pt-5 dark:border-dark-400">
|
||||
<div>
|
||||
<WorkspaceMenu />
|
||||
<ul role="list" className="space-y-1">
|
||||
|
||||
@@ -7,7 +7,7 @@ export default function Dashboard(props: { children: React.ReactNode }) {
|
||||
|
||||
return (
|
||||
<div className="flex h-screen flex-col items-center bg-light-100 dark:bg-dark-50">
|
||||
<div className="m-auto flex h-16 min-h-16 w-full justify-between border-b border-light-600 px-5 py-2 align-middle dark:border-dark-600">
|
||||
<div className="m-auto flex h-16 min-h-16 w-full justify-between border-b border-light-600 px-5 py-2 align-middle dark:border-dark-400">
|
||||
<div className="my-auto flex w-full items-center justify-between">
|
||||
<h1 className="text-lg font-bold tracking-tight text-neutral-900 dark:text-dark-1000">
|
||||
kan.bn
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { WorkspaceProvider } from "~/providers/workspace";
|
||||
import Dashboard from "~/components/dashboard";
|
||||
import Dashboard from "~/components/Dashboard";
|
||||
import Popup from "~/components/Popup";
|
||||
import BoardView from "~/views/board";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { WorkspaceProvider } from "~/providers/workspace";
|
||||
import Dashboard from "~/components/dashboard";
|
||||
import Dashboard from "~/components/Dashboard";
|
||||
import Popup from "~/components/Popup";
|
||||
import BoardsView from "~/views/boards";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { WorkspaceProvider } from "~/providers/workspace";
|
||||
import Dashboard from "~/components/dashboard";
|
||||
import Dashboard from "~/components/Dashboard";
|
||||
import Popup from "~/components/Popup";
|
||||
import CardView from "~/views/card";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { WorkspaceProvider } from "~/providers/workspace";
|
||||
import Dashboard from "~/components/dashboard";
|
||||
import Dashboard from "~/components/Dashboard";
|
||||
import Popup from "~/components/Popup";
|
||||
import MembersView from "~/views/members";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { WorkspaceProvider } from "~/providers/workspace";
|
||||
import Dashboard from "~/components/dashboard";
|
||||
import Dashboard from "~/components/Dashboard";
|
||||
import SettingsView from "~/views/settings";
|
||||
import Popup from "~/components/Popup";
|
||||
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import { type ReactNode } from "react";
|
||||
import { HiOutlinePlusSmall } from "react-icons/hi2";
|
||||
import {
|
||||
HiOutlinePlusSmall,
|
||||
HiEllipsisHorizontal,
|
||||
HiOutlineTrash,
|
||||
HiOutlineSquaresPlus,
|
||||
} from "react-icons/hi2";
|
||||
import { Draggable } from "react-beautiful-dnd";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
import { api } from "~/utils/api";
|
||||
import { useModal } from "~/providers/modal";
|
||||
|
||||
import ListDropdown from "./ListDropdown";
|
||||
import Dropdown from "~/components/Dropdown";
|
||||
|
||||
interface ListProps {
|
||||
children: ReactNode;
|
||||
@@ -60,6 +65,11 @@ export default function List({
|
||||
});
|
||||
};
|
||||
|
||||
const handleOpenDeleteListConfirmation = () => {
|
||||
setSelectedPublicListId(list.publicId);
|
||||
openModal("DELETE_LIST");
|
||||
};
|
||||
|
||||
return (
|
||||
<Draggable key={list.publicId} draggableId={list.publicId} index={index}>
|
||||
{(provided) => (
|
||||
@@ -85,7 +95,7 @@ export default function List({
|
||||
</form>
|
||||
<div>
|
||||
<button
|
||||
className="mx-1 inline-flex h-fit items-center rounded-md p-1 px-1 text-sm font-semibold text-dark-50 hover:bg-light-400 dark:hover:bg-dark-400"
|
||||
className="mx-1 inline-flex h-fit items-center rounded-md p-1 px-1 text-sm font-semibold text-dark-50 hover:bg-light-400 dark:hover:bg-dark-200"
|
||||
onClick={() => openNewCardForm(list.publicId)}
|
||||
>
|
||||
<HiOutlinePlusSmall
|
||||
@@ -93,11 +103,28 @@ export default function List({
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
<ListDropdown
|
||||
setSelectedPublicListId={() =>
|
||||
setSelectedPublicListId(list.publicId)
|
||||
}
|
||||
/>
|
||||
<div className="relative mr-1 inline-block">
|
||||
<Dropdown
|
||||
items={[
|
||||
{
|
||||
label: "Add a card",
|
||||
action: () => openNewCardForm(list.publicId),
|
||||
icon: (
|
||||
<HiOutlineSquaresPlus className="h-[18px] w-[18px] text-dark-900" />
|
||||
),
|
||||
},
|
||||
{
|
||||
label: "Delete list",
|
||||
action: handleOpenDeleteListConfirmation,
|
||||
icon: (
|
||||
<HiOutlineTrash className="h-[18px] w-[18px] text-dark-900" />
|
||||
),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<HiEllipsisHorizontal className="h-5 w-5 text-dark-900" />
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{children}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
import { Fragment } from "react";
|
||||
import { Menu, Transition } from "@headlessui/react";
|
||||
import { HiEllipsisHorizontal } from "react-icons/hi2";
|
||||
import { useModal } from "~/providers/modal";
|
||||
|
||||
interface ListDropdownProps {
|
||||
setSelectedPublicListId: () => void;
|
||||
}
|
||||
|
||||
export default function ListDropdown({
|
||||
setSelectedPublicListId,
|
||||
}: ListDropdownProps) {
|
||||
const { openModal } = useModal();
|
||||
|
||||
const handleOpenDeleteListConfirmation = () => {
|
||||
setSelectedPublicListId();
|
||||
openModal("DELETE_LIST");
|
||||
};
|
||||
|
||||
return (
|
||||
<Menu as="div" className="relative inline-block text-left">
|
||||
<div>
|
||||
<Menu.Button className="mr-1 inline-flex h-fit items-center rounded-md p-1 px-1 text-sm font-semibold text-dark-50 hover:bg-light-400 dark:hover:bg-dark-400">
|
||||
<HiEllipsisHorizontal className="h-5 w-5 text-dark-900" />
|
||||
</Menu.Button>
|
||||
</div>
|
||||
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items className="dark-text-dark-1000 absolute right-0 z-10 mt-2 w-56 origin-top-right rounded-md border border-light-400 bg-light-50 text-neutral-900 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:border-dark-400 dark:bg-dark-300">
|
||||
<div className="flex">
|
||||
<Menu.Item>
|
||||
<button
|
||||
onClick={handleOpenDeleteListConfirmation}
|
||||
className="m-1 w-full rounded-[5px] px-3 py-2 text-left text-sm text-neutral-900 hover:bg-light-200 dark:text-dark-1000 dark:hover:bg-dark-400"
|
||||
>
|
||||
Delete list
|
||||
</button>
|
||||
</Menu.Item>
|
||||
</div>
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
@@ -16,7 +16,6 @@ import { formatToArray } from "~/utils/helpers";
|
||||
import { useBoard } from "~/providers/board";
|
||||
import { useModal } from "~/providers/modal";
|
||||
import { useWorkspace } from "~/providers/workspace";
|
||||
|
||||
import { PageHead } from "~/components/PageHead";
|
||||
import Modal from "~/components/modal";
|
||||
import PatternedBackground from "~/components/PatternedBackground";
|
||||
@@ -147,7 +146,7 @@ export default function BoardPage() {
|
||||
<div className="z-10 flex w-full justify-between p-8">
|
||||
{isLoading ? (
|
||||
<div className="flex space-x-2">
|
||||
<div className="h-[2.3rem] w-[150px] animate-pulse rounded-[5px] bg-light-200 dark:bg-dark-200" />
|
||||
<div className="h-[2.3rem] w-[150px] animate-pulse rounded-[5px] bg-light-200 dark:bg-dark-100" />
|
||||
</div>
|
||||
) : (
|
||||
<form
|
||||
@@ -184,9 +183,9 @@ export default function BoardPage() {
|
||||
<div className="scrollbar-w-none z-0 flex-1 overflow-y-hidden overflow-x-scroll overscroll-contain scrollbar scrollbar-track-light-200 scrollbar-thumb-light-400 scrollbar-track-rounded-[4px] scrollbar-thumb-rounded-[4px] scrollbar-h-[8px] dark:scrollbar-track-dark-100 dark:scrollbar-thumb-dark-300">
|
||||
{isLoading ? (
|
||||
<div className="ml-[2rem] flex">
|
||||
<div className="0 mr-5 h-[500px] w-[18rem] animate-pulse rounded-md bg-light-200 dark:bg-dark-200" />
|
||||
<div className="0 mr-5 h-[275px] w-[18rem] animate-pulse rounded-md bg-light-200 dark:bg-dark-200" />
|
||||
<div className="0 mr-5 h-[375px] w-[18rem] animate-pulse rounded-md bg-light-200 dark:bg-dark-200" />
|
||||
<div className="0 mr-5 h-[500px] w-[18rem] animate-pulse rounded-md bg-light-200 dark:bg-dark-100" />
|
||||
<div className="0 mr-5 h-[275px] w-[18rem] animate-pulse rounded-md bg-light-200 dark:bg-dark-100" />
|
||||
<div className="0 mr-5 h-[375px] w-[18rem] animate-pulse rounded-md bg-light-200 dark:bg-dark-100" />
|
||||
</div>
|
||||
) : (
|
||||
<DragDropContext onDragEnd={onDragEnd}>
|
||||
@@ -236,7 +235,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-300 dark:text-dark-1000 dark:hover:bg-dark-400 ${
|
||||
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 ${
|
||||
card.publicId.startsWith("PLACEHOLDER")
|
||||
? "pointer-events-none"
|
||||
: ""
|
||||
|
||||
@@ -15,9 +15,9 @@ export function BoardsList() {
|
||||
if (isLoading)
|
||||
return (
|
||||
<div className="grid w-full grid-cols-1 gap-4 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 xxl:grid-cols-5">
|
||||
<div className="mr-5 flex h-[150px] w-full animate-pulse rounded-md bg-light-200 dark:bg-dark-200" />
|
||||
<div className="mr-5 flex h-[150px] w-full animate-pulse rounded-md bg-light-200 dark:bg-dark-200" />
|
||||
<div className="mr-5 flex h-[150px] w-full animate-pulse rounded-md bg-light-200 dark:bg-dark-200" />
|
||||
<div className="mr-5 flex h-[150px] w-full animate-pulse rounded-md bg-light-200 dark:bg-dark-100" />
|
||||
<div className="mr-5 flex h-[150px] w-full animate-pulse rounded-md bg-light-200 dark:bg-dark-100" />
|
||||
<div className="mr-5 flex h-[150px] w-full animate-pulse rounded-md bg-light-200 dark:bg-dark-100" />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -27,7 +27,7 @@ export function BoardsList() {
|
||||
<div className="grid w-full grid-cols-1 gap-4 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 xxl:grid-cols-5">
|
||||
{data?.map((board) => (
|
||||
<Link key={board.publicId} href={`boards/${board.publicId}`}>
|
||||
<div className="align-center relative mr-5 flex h-[150px] w-full items-center justify-center rounded-md border border-dashed border-light-600 bg-light-50 shadow-sm hover:bg-light-200 dark:border-dark-600 dark:bg-dark-50 dark:hover:bg-dark-100">
|
||||
<div className="align-center relative mr-5 flex h-[150px] w-full items-center justify-center rounded-md border border-dashed border-light-400 bg-light-50 shadow-sm hover:bg-light-200 dark:border-dark-600 dark:bg-dark-50 dark:hover:bg-dark-100">
|
||||
<PatternedBackground />
|
||||
<p className="text-md px-4 font-medium text-neutral-900 dark:text-dark-1000">
|
||||
{board.name}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { usePopup } from "~/providers/popup";
|
||||
import Avatar from "~/components/Avatar";
|
||||
import Button from "~/components/Button";
|
||||
import Dropdown from "~/components/Dropdown";
|
||||
import { HiEllipsisHorizontal } from "react-icons/hi2";
|
||||
import { HiEllipsisHorizontal, HiPencil } from "react-icons/hi2";
|
||||
|
||||
interface FormValues {
|
||||
comment: string;
|
||||
@@ -69,7 +69,7 @@ const Comment = ({
|
||||
return (
|
||||
<div
|
||||
key={publicId}
|
||||
className="group relative flex w-full flex-col rounded-xl border border-light-600 bg-light-200 p-5 text-light-900 focus-visible:outline-none dark:border-dark-600 dark:bg-dark-100 dark:text-dark-1000 sm:text-sm sm:leading-6"
|
||||
className="group relative flex w-full flex-col rounded-xl border border-light-600 bg-light-200 p-4 text-light-900 focus-visible:outline-none dark:border-dark-400 dark:bg-dark-100 dark:text-dark-1000 sm:text-sm sm:leading-6"
|
||||
>
|
||||
<div className="flex justify-between">
|
||||
<div className="flex items-center space-x-2">
|
||||
@@ -96,16 +96,19 @@ const Comment = ({
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Dropdown
|
||||
items={[
|
||||
{
|
||||
label: "Edit",
|
||||
action: () => setIsEditing(true),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<HiEllipsisHorizontal className="h-5 w-5" />
|
||||
</Dropdown>
|
||||
<div className="absolute right-4 top-4">
|
||||
<Dropdown
|
||||
items={[
|
||||
{
|
||||
label: "Edit comment",
|
||||
action: () => setIsEditing(true),
|
||||
icon: <HiPencil className="h-[18px] w-[18px] text-dark-900" />,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<HiEllipsisHorizontal className="h-5 w-5 text-light-900 dark:text-dark-800" />
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
{!isEditing ? (
|
||||
<p className="mt-2 text-sm">{comment}</p>
|
||||
|
||||
@@ -43,7 +43,7 @@ const NewCommentForm = ({ cardPublicId }: { cardPublicId: string }) => {
|
||||
return (
|
||||
<form
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
className="flex w-full flex-col rounded-xl border border-light-600 bg-light-200 p-5 text-light-900 focus-visible:outline-none dark:border-dark-600 dark:bg-dark-100 dark:text-dark-1000 sm:text-sm sm:leading-6"
|
||||
className="flex w-full flex-col rounded-xl border border-light-600 bg-light-200 p-4 text-light-900 focus-visible:outline-none dark:border-dark-400 dark:bg-dark-100 dark:text-dark-1000 sm:text-sm sm:leading-6"
|
||||
>
|
||||
<ContentEditable
|
||||
placeholder="Add a comment..."
|
||||
@@ -56,7 +56,7 @@ const NewCommentForm = ({ cardPublicId }: { cardPublicId: string }) => {
|
||||
<button
|
||||
type="submit"
|
||||
disabled={addCommentMutation.isPending}
|
||||
className="flex h-8 w-8 items-center justify-center rounded-full border border-light-600 bg-light-300 hover:bg-light-400 disabled:opacity-50 dark:border-dark-600 dark:bg-dark-300 dark:hover:bg-dark-400"
|
||||
className="flex h-8 w-8 items-center justify-center rounded-full border border-light-600 bg-light-300 hover:bg-light-400 disabled:opacity-50 dark:border-dark-400 dark:bg-dark-200 dark:hover:bg-dark-400"
|
||||
>
|
||||
{addCommentMutation.isPending ? (
|
||||
<LoadingSpinner size="sm" />
|
||||
|
||||
@@ -175,7 +175,7 @@ export default function CardPage() {
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div className="border-t-[1px] border-light-600 pt-12 dark:border-dark-600">
|
||||
<div className="border-t-[1px] border-light-600 pt-12 dark:border-dark-400">
|
||||
<h2 className="text-md pb-4 font-medium text-light-900 dark:text-dark-1000">
|
||||
Activity
|
||||
</h2>
|
||||
|
||||
Reference in New Issue
Block a user