129 lines
4.1 KiB
TypeScript
129 lines
4.1 KiB
TypeScript
import { useDraggable, useDroppable } from "@dnd-kit/core";
|
|
import clsx from "clsx";
|
|
import { MdClose, MdDelete, MdDragIndicator } from "react-icons/md";
|
|
import { CSS } from "@dnd-kit/utilities";
|
|
import { useEffect, useState } from "react";
|
|
import ConfirmDeleteBtn from "../../Shared/ConfirmDeleteBtn";
|
|
|
|
interface BlankProps {
|
|
id: number;
|
|
module: string;
|
|
variant: "text" | "bank";
|
|
isSelected?: boolean;
|
|
isDragging?: boolean;
|
|
onSelect?: (id: number) => void;
|
|
onRemove?: (id: number) => void;
|
|
disabled?: boolean;
|
|
}
|
|
|
|
export const Blank: React.FC<BlankProps> = ({
|
|
id,
|
|
module,
|
|
variant,
|
|
isSelected,
|
|
isDragging,
|
|
onSelect,
|
|
onRemove,
|
|
disabled,
|
|
}) => {
|
|
const { attributes, listeners, setNodeRef, transform } = useDraggable({
|
|
id: `${variant}-blank-${id}`,
|
|
disabled: disabled || variant !== "text",
|
|
});
|
|
|
|
const style = transform ? {
|
|
transform: CSS.Translate.toString(transform),
|
|
transition: 'none',
|
|
zIndex: 999,
|
|
position: 'relative' as const,
|
|
touchAction: 'none',
|
|
} : {
|
|
transition: 'transform 0.2s cubic-bezier(0.25, 1, 0.5, 1)',
|
|
touchAction: 'none',
|
|
position: 'relative' as const,
|
|
};
|
|
|
|
const handleClick = (e: React.MouseEvent) => {
|
|
if (variant === "bank" && !disabled && onSelect) {
|
|
onSelect(id);
|
|
}
|
|
};
|
|
|
|
const dragProps = variant === "text" ? {
|
|
...attributes,
|
|
...listeners,
|
|
} : {};
|
|
|
|
return (
|
|
<div
|
|
ref={setNodeRef}
|
|
style={style}
|
|
className={clsx(
|
|
"group relative inline-flex items-center gap-2 px-2 py-1.5 rounded-lg select-none",
|
|
"transform-gpu transition-colors duration-150",
|
|
"hover:ring-2 hover:ring-offset-1 shadow-sm",
|
|
(
|
|
isSelected ? (
|
|
isDragging ?
|
|
`bg-ielts-${module}/20 bg-ielts-${module} hover:ring-ielts-${module}/50` :
|
|
`bg-ielts-${module}/20 bg-ielts-${module}/80 hover:ring-ielts-${module}/40`
|
|
)
|
|
: `bg-ielts-${module}/20 bg-ielts-${module} hover:ring-ielts-${module}/50`
|
|
),
|
|
!disabled && (variant === "text" ? "cursor-grab active:cursor-grabbing" : "cursor-pointer"),
|
|
disabled && "cursor-default",
|
|
variant === "bank" && "w-12"
|
|
)}
|
|
onClick={variant === "bank" ? handleClick : undefined}
|
|
{...dragProps}
|
|
role="button"
|
|
>
|
|
{variant === "text" && (
|
|
<span
|
|
className={clsx(
|
|
"text-xl p-1.5 -ml-1 rounded-md",
|
|
"transition-colors duration-150"
|
|
)}
|
|
title="Drag to reorder"
|
|
>
|
|
{isSelected ?
|
|
<MdDragIndicator className="transform scale-125" color="white" /> :
|
|
<MdDragIndicator className="transform scale-125" color="#898492" />
|
|
}
|
|
</span>
|
|
)}
|
|
<span className={clsx(
|
|
"font-semibold px-1 text-mti-gray-taupe",
|
|
isSelected && !isDragging && "text-white"
|
|
)}>
|
|
{id}
|
|
</span>
|
|
|
|
{onRemove && !isDragging && (
|
|
<ConfirmDeleteBtn
|
|
onDelete={() => onRemove(id)}
|
|
size="md"
|
|
position="top-right"
|
|
className="-translate-y-2 translate-x-1.5"
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export const DropZone: React.FC<{ index: number, module: string; }> = ({ index, module }) => {
|
|
const { setNodeRef, isOver } = useDroppable({
|
|
id: `drop-${index}`,
|
|
});
|
|
|
|
return (
|
|
<span
|
|
ref={setNodeRef}
|
|
className={clsx(
|
|
"inline-block h-6 w-4 mx-px transition-all duration-200 select-none",
|
|
isOver ? `bg-ielts-${module}/20 w-4.5` : `bg-transparent hover:bg-ielts-${module}/20`
|
|
)}
|
|
role="presentation"
|
|
/>
|
|
);
|
|
}; |