mayank1513/vscode-extension-trello-kanban-board

View on GitHub
src/components/task/index.tsx

Summary

Maintainability
A
30 mins
Test Coverage
A
100%
import { TaskType } from "@/interface";
import { RefObject, useId, useRef, useState } from "react";
import { Draggable } from "react-beautiful-dnd";
import ReactMarkdown from "react-markdown";
import rehypeRaw from "rehype-raw";
import remarkGfm from "remark-gfm";
import { useGlobalState } from "utils/context";
import styles from "./task.module.scss";
import { vscode } from "utils/vscode";
import { ColorSelector } from "components/color-selector";
import { autoLinkMd } from "react-markdown-autolink";

function resizeTextArea(textareaRef: RefObject<HTMLTextAreaElement>) {
  const target = textareaRef.current;
  if (!target) return;
  target.style.height = "auto";
  target.style.height = `${target.scrollHeight}px`;
}

export default function Task({ task, index }: { task: TaskType; index: number }) {
  const { state, setState } = useGlobalState();
  const [isEditing, setIsEditing] = useState(false);
  const [showColorSelector, setShowColorSelector] = useState(false);
  const textareaRef = useRef<HTMLTextAreaElement>(null);
  const id = useId();

  const removeTask = () => {
    const tasks = { ...state.tasks };
    delete tasks[task.id];
    const columns = [...state.columns];
    const column = columns.find((c) => c.id === task.columnId);
    if (column) {
      column.tasksIds = column.tasksIds.filter((id) => id !== task.id);
    }
    setState({ ...state, tasks, columns });
    vscode.toast("Task deleted!", "success");
  };

  const setColor = (color: string) => {
    task.color = color;
    setState({ ...state, tasks: { ...state.tasks } });
    setShowColorSelector(false);
  };
  return (
    <>
      <Draggable draggableId={task.id} index={index}>
        {(provided, snapshot) => {
          if (snapshot.isDragging && provided.draggableProps.style?.transform)
            provided.draggableProps.style.transform += " rotate(5deg)";

          return (
            <div ref={provided.innerRef} {...provided.draggableProps}>
              <label
                htmlFor={id}
                className={[styles.task, isEditing ? styles.active : ""].join(" ")}
                style={{ background: task.color }}>
                <header {...provided.dragHandleProps}>
                  <span>∘∘∘</span>
                  <button onClick={() => setShowColorSelector(true)}>🖌</button>
                  <button
                    onClick={() => {
                      setIsEditing(true);
                      if (textareaRef.current?.value) {
                        textareaRef.current.value = "hk";
                        textareaRef.current.value = task.description;
                      }
                      setTimeout(() => {
                        textareaRef.current && textareaRef.current.focus();
                        resizeTextArea(textareaRef);
                      }, 100);
                    }}>
                    🖉
                  </button>
                  <button className={styles.close} onClick={removeTask}>
                    ✖
                  </button>
                </header>
                <textarea
                  id={id}
                  value={task.description}
                  ref={textareaRef}
                  onChange={(e) => {
                    task.description = e.target.value.replace(/ +/, " ").replace(/\n\n+/g, "\n\n");
                    setState({ ...state, tasks: { ...state.tasks } });
                    resizeTextArea(textareaRef);
                  }}
                  onBlur={() => setIsEditing(false)}
                  placeholder="Enter task description in Markdown format"
                  hidden={!isEditing}
                />
                {!isEditing &&
                  (task.description.trim() ? (
                    <ReactMarkdown rehypePlugins={[rehypeRaw]} remarkPlugins={[remarkGfm]}>
                      {autoLinkMd(task.description.replace(/\n+/g, "\n\n"))}
                    </ReactMarkdown>
                  ) : (
                    <p className={styles.placeholder}>Enter task description in Markdown format.</p>
                  ))}
              </label>
            </div>
          );
        }}
      </Draggable>
      {showColorSelector && (
        <ColorSelector color={task.color} setColor={setColor} onClose={() => setShowColorSelector(false)} />
      )}
    </>
  );
}