mirror of
https://github.com/ershisan99/todolist_next.git
synced 2026-01-29 21:02:05 +00:00
wip
This commit is contained in:
@@ -1,17 +1,26 @@
|
||||
import type { ButtonHTMLAttributes, DetailedHTMLProps, FC } from "react";
|
||||
import type { ComponentPropsWithoutRef, FC } from "react";
|
||||
|
||||
type Props = DetailedHTMLProps<
|
||||
ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
HTMLButtonElement
|
||||
>;
|
||||
import { cn } from "@/helpers";
|
||||
|
||||
export const Button: FC<Props> = ({ className, ...rest }) => {
|
||||
type Props = ComponentPropsWithoutRef<"button"> & {
|
||||
variant?: "primary" | "outlined" | "icon";
|
||||
};
|
||||
|
||||
export const Button: FC<Props> = ({
|
||||
className,
|
||||
variant = "primary",
|
||||
...rest
|
||||
}) => {
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
className={`rounded-md border border-gray-300 bg-sky-700 px-4 py-2 text-white focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-600 ${className}`}
|
||||
{...rest}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
className={cn(
|
||||
"flex items-center justify-center rounded-md border border-gray-300 bg-sky-700 px-4 py-2 text-white focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-600",
|
||||
variant === "outlined" && "border-sky-700 bg-inherit text-sky-700",
|
||||
variant === "icon" &&
|
||||
"h-6 w-6 shrink-0 border-none bg-inherit p-0 text-sky-700 hover:bg-slate-100 hover:text-sky-800",
|
||||
className
|
||||
)}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -7,11 +7,9 @@ type Props = DetailedHTMLProps<
|
||||
|
||||
export const Input: FC<Props> = ({ className, ...rest }) => {
|
||||
return (
|
||||
<div>
|
||||
<input
|
||||
className={`w-full rounded-md border border-gray-300 px-4 py-2 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-600 ${className}`}
|
||||
{...rest}
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
className={`w-full rounded-md border border-gray-300 px-4 py-2 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-600 ${className}`}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
import type { ChangeEvent, FC, MouseEventHandler } from "react";
|
||||
import type { ChangeEvent, FC, FormEvent, MouseEventHandler } from "react";
|
||||
import { memo, useState } from "react";
|
||||
|
||||
import type { Task, Todolist as TodolistType } from "../../services/todolists";
|
||||
import { Trash, Plus } from "lucide-react";
|
||||
|
||||
import type {
|
||||
Task,
|
||||
Todolist as TodolistType,
|
||||
} from "../../services/todolist-api/todolists";
|
||||
import { Button } from "../button";
|
||||
import { Input } from "../input";
|
||||
|
||||
import {
|
||||
ToggleGroup,
|
||||
ToggleGroupItem,
|
||||
} from "@/components/toggle-group/toggle-group";
|
||||
import {
|
||||
TaskStatus,
|
||||
useCreateTaskMutation,
|
||||
useDeleteTaskMutation,
|
||||
useDeleteTodolistMutation,
|
||||
@@ -29,7 +39,11 @@ export const Todolist: FC<Props> = memo(({ todolist }) => {
|
||||
const [filter, setFilter] = useState("all");
|
||||
|
||||
const handleChangeStatus = (todolistId: string, task: Task) => {
|
||||
const newTask = { ...task, status: task.status === 0 ? 2 : 0 };
|
||||
const newTask: Task = {
|
||||
...task,
|
||||
status:
|
||||
task.status === TaskStatus.DONE ? TaskStatus.OPEN : TaskStatus.DONE,
|
||||
};
|
||||
|
||||
putTask({ todolistId, task: newTask });
|
||||
};
|
||||
@@ -39,7 +53,8 @@ export const Todolist: FC<Props> = memo(({ todolist }) => {
|
||||
const handleDeleteTodolist = (todolistId: string) => {
|
||||
deleteTodolist({ todolistId });
|
||||
};
|
||||
const handleAddTask = () => {
|
||||
const handleAddTask = (e: FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
createTask({ todolistId: todolist.id, title: newTaskTitle });
|
||||
setNewTaskTitle("");
|
||||
};
|
||||
@@ -47,17 +62,17 @@ export const Todolist: FC<Props> = memo(({ todolist }) => {
|
||||
setNewTaskTitle(e.target.value);
|
||||
};
|
||||
|
||||
const handleFilterChange: MouseEventHandler<HTMLButtonElement> = (e) => {
|
||||
setFilter(e.currentTarget.value as Filter);
|
||||
const handleFilterChange = (value: string) => {
|
||||
setFilter(value as Filter);
|
||||
};
|
||||
|
||||
if (isLoading) return <div>loading...</div>;
|
||||
const filteredTasks = tasks?.items?.filter((task) => {
|
||||
const filteredTasks = tasks?.filter((task) => {
|
||||
switch (filter) {
|
||||
case "active":
|
||||
return task.status === 0;
|
||||
return task.status === TaskStatus.OPEN;
|
||||
case "completed":
|
||||
return task.status === 2;
|
||||
return task.status === TaskStatus.DONE;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
@@ -67,40 +82,65 @@ export const Todolist: FC<Props> = memo(({ todolist }) => {
|
||||
<div
|
||||
key={todolist.id}
|
||||
className={
|
||||
"w- flex w-72 flex-col gap-3 rounded-md border border-gray-300 p-4"
|
||||
"flex w-72 flex-col gap-3 rounded-md border border-gray-300 p-4"
|
||||
}
|
||||
>
|
||||
<div className={"flex items-center justify-between "}>
|
||||
<h2 className={"text-xl"}>{todolist.title}</h2>
|
||||
<button onClick={() => handleDeleteTodolist(todolist.id)}>x</button>
|
||||
<Button
|
||||
variant={"icon"}
|
||||
onClick={() => handleDeleteTodolist(todolist.id)}
|
||||
>
|
||||
<Trash size={16} />
|
||||
</Button>
|
||||
</div>
|
||||
<div className={"flex w-52 gap-4"}>
|
||||
<form
|
||||
onSubmit={handleAddTask}
|
||||
className={"relative flex w-full items-center gap-4"}
|
||||
>
|
||||
<Input value={newTaskTitle} onChange={handleNewTaskTitleChange} />
|
||||
<Button onClick={handleAddTask}>+</Button>
|
||||
</div>
|
||||
{filteredTasks?.map((task) => (
|
||||
<div className={"flex items-center gap-2"} key={task.id}>
|
||||
<input
|
||||
onChange={() => handleChangeStatus(todolist.id, task)}
|
||||
type={"checkbox"}
|
||||
checked={[0, 1, 3].includes(task.status)}
|
||||
/>
|
||||
<div key={task.id}>{task.title}</div>
|
||||
<button onClick={() => handleDeleteTask(todolist.id, task.id)}>
|
||||
X
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
<Button type={"submit"} variant={"icon"} className={"absolute right-4"}>
|
||||
<Plus size={16} />
|
||||
</Button>
|
||||
</form>
|
||||
{filteredTasks?.map((task) => {
|
||||
return (
|
||||
<div className={"flex cursor-pointer items-center"} key={task.id}>
|
||||
<label className={"flex cursor-pointer items-center gap-2"}>
|
||||
<input
|
||||
onChange={() => handleChangeStatus(todolist.id, task)}
|
||||
type={"checkbox"}
|
||||
checked={TaskStatus.DONE === task.status}
|
||||
/>
|
||||
{task.title}
|
||||
</label>
|
||||
<Button
|
||||
variant={"icon"}
|
||||
onClick={() => handleDeleteTask(todolist.id, task.id)}
|
||||
className={"ml-2"}
|
||||
>
|
||||
<Trash size={16} />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
<div className={"flex"}>
|
||||
<Button onClick={handleFilterChange} value={"all"}>
|
||||
All
|
||||
</Button>
|
||||
<Button onClick={handleFilterChange} value={"active"}>
|
||||
Active
|
||||
</Button>
|
||||
<Button onClick={handleFilterChange} value={"completed"}>
|
||||
Completed
|
||||
</Button>
|
||||
<ToggleGroup
|
||||
type="single"
|
||||
onValueChange={handleFilterChange}
|
||||
value={filter}
|
||||
className={"w-full"}
|
||||
>
|
||||
<ToggleGroupItem className={"w-full"} value="all">
|
||||
All
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem className={"w-full"} value="active">
|
||||
Active
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem className={"w-full"} value="completed">
|
||||
Completed
|
||||
</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
83
src/components/toggle-group/toggle-group.tsx
Normal file
83
src/components/toggle-group/toggle-group.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
|
||||
import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group";
|
||||
import type { VariantProps } from "class-variance-authority";
|
||||
import { cva } from "class-variance-authority";
|
||||
|
||||
import { cn } from "@/helpers";
|
||||
|
||||
const toggleVariants = cva(
|
||||
"inline-flex border border-sky-700 items-center justify-center rounded-md text-sm font-medium transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-sky-700 data-[state=on]:text-white",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-transparent",
|
||||
outline:
|
||||
"border border-input bg-transparent shadow-sm hover:bg-accent hover:text-accent-foreground",
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-3",
|
||||
sm: "h-8 px-2",
|
||||
lg: "h-10 px-3",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
}
|
||||
);
|
||||
const ToggleGroupContext = React.createContext<
|
||||
VariantProps<typeof toggleVariants>
|
||||
>({
|
||||
size: "default",
|
||||
variant: "default",
|
||||
});
|
||||
|
||||
const ToggleGroup = React.forwardRef<
|
||||
React.ElementRef<typeof ToggleGroupPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root> &
|
||||
VariantProps<typeof toggleVariants>
|
||||
>(({ className, variant, size, children, ...props }, ref) => (
|
||||
<ToggleGroupPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn("flex items-center justify-center gap-1", className)}
|
||||
{...props}
|
||||
>
|
||||
<ToggleGroupContext.Provider value={{ variant, size }}>
|
||||
{children}
|
||||
</ToggleGroupContext.Provider>
|
||||
</ToggleGroupPrimitive.Root>
|
||||
));
|
||||
|
||||
ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName;
|
||||
|
||||
const ToggleGroupItem = React.forwardRef<
|
||||
React.ElementRef<typeof ToggleGroupPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> &
|
||||
VariantProps<typeof toggleVariants>
|
||||
>(({ className, children, variant, size, ...props }, ref) => {
|
||||
const context = React.useContext(ToggleGroupContext);
|
||||
|
||||
return (
|
||||
<ToggleGroupPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
toggleVariants({
|
||||
variant: context.variant || variant,
|
||||
size: context.size || size,
|
||||
}),
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</ToggleGroupPrimitive.Item>
|
||||
);
|
||||
});
|
||||
|
||||
ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName;
|
||||
|
||||
export { ToggleGroup, ToggleGroupItem };
|
||||
Reference in New Issue
Block a user