fix(extra) : add drag sorting and correct character links
This commit is contained in:
@@ -4,8 +4,6 @@ import dynamic from "next/dynamic";
|
||||
import Image from "next/image";
|
||||
import {
|
||||
ChartNoAxesCombinedIcon,
|
||||
ChevronDownIcon,
|
||||
ChevronUpIcon,
|
||||
ClipboardCopyIcon,
|
||||
ClipboardPasteIcon,
|
||||
CopyIcon,
|
||||
@@ -3370,48 +3368,76 @@ function ExtraCatalogItemsEditor({
|
||||
artifacts: CatalogItem[];
|
||||
onChange: (value: ExtraCatalogItem[]) => void;
|
||||
}) {
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor),
|
||||
useSensor(KeyboardSensor, {
|
||||
coordinateGetter: sortableKeyboardCoordinates,
|
||||
}),
|
||||
);
|
||||
const [kind, setKind] = useState<ExtraCatalogItem["kind"]>("character");
|
||||
const [key, setKey] = useState("");
|
||||
const catalogs = { character: characters, weapon: weapons, artifact: artifacts };
|
||||
const labels = { character: "ตัวละคร", weapon: "อาวุธ", artifact: "อาร์ติแฟกต์" };
|
||||
const items = catalogs[kind];
|
||||
const itemId = (item: ExtraCatalogItem) => `${item.kind}:${item.key}`;
|
||||
const handleDragEnd = ({ active, over }: DragEndEvent) => {
|
||||
if (!over || active.id === over.id) return;
|
||||
const from = value.findIndex((item) => itemId(item) === active.id);
|
||||
const to = value.findIndex((item) => itemId(item) === over.id);
|
||||
if (from !== -1 && to !== -1) onChange(arrayMove(value, from, to));
|
||||
};
|
||||
|
||||
return (
|
||||
<Field>
|
||||
<FieldLabel>ตัวละคร อาวุธ และอาร์ติแฟกต์</FieldLabel>
|
||||
<div className="flex flex-col gap-3">
|
||||
{value.length ? (
|
||||
<div className="flex flex-col gap-2">
|
||||
{value.map((reference, index) => {
|
||||
const item = catalogs[reference.kind].find(
|
||||
(candidate) => candidate.key === reference.key,
|
||||
);
|
||||
return (
|
||||
<Card key={`${reference.kind}:${reference.key}`} size="sm">
|
||||
<CardContent className="flex items-center gap-3">
|
||||
{item ? (
|
||||
<div className={cn("relative size-12 shrink-0 overflow-hidden rounded-md", rarityGradientClass(item.rarity))}>
|
||||
<Image src={item.imageUrl} alt="" fill className="object-contain" />
|
||||
</div>
|
||||
) : null}
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate font-medium">{item?.name ?? reference.key}</p>
|
||||
<p className="text-sm text-muted-foreground">{labels[reference.kind]}</p>
|
||||
</div>
|
||||
<Button type="button" variant="ghost" size="icon-sm" disabled={index === 0} onClick={() => onChange(arrayMove(value, index, index - 1))} aria-label="เลื่อนขึ้น">
|
||||
<ChevronUpIcon />
|
||||
</Button>
|
||||
<Button type="button" variant="ghost" size="icon-sm" disabled={index === value.length - 1} onClick={() => onChange(arrayMove(value, index, index + 1))} aria-label="เลื่อนลง">
|
||||
<ChevronDownIcon />
|
||||
</Button>
|
||||
<Button type="button" variant="ghost" size="icon-sm" onClick={() => onChange(value.filter((_, itemIndex) => itemIndex !== index))} aria-label={`ลบ ${item?.name ?? reference.key}`}>
|
||||
<XIcon />
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
onDragEnd={handleDragEnd}
|
||||
>
|
||||
<SortableContext
|
||||
items={value.map(itemId)}
|
||||
strategy={verticalListSortingStrategy}
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
{value.map((reference, index) => {
|
||||
const item = catalogs[reference.kind].find(
|
||||
(candidate) => candidate.key === reference.key,
|
||||
);
|
||||
const label = item?.name ?? reference.key;
|
||||
return (
|
||||
<SortableExtraItem
|
||||
key={itemId(reference)}
|
||||
id={itemId(reference)}
|
||||
label={`ลากเพื่อจัดลำดับ ${label}`}
|
||||
>
|
||||
{(dragHandle) => (
|
||||
<Card size="sm">
|
||||
<CardContent className="flex items-center gap-3">
|
||||
{item ? (
|
||||
<div className={cn("relative size-12 shrink-0 overflow-hidden rounded-md", rarityGradientClass(item.rarity))}>
|
||||
<Image src={item.imageUrl} alt="" fill className="object-contain" />
|
||||
</div>
|
||||
) : null}
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate font-medium">{label}</p>
|
||||
<p className="text-sm text-muted-foreground">{labels[reference.kind]}</p>
|
||||
</div>
|
||||
{dragHandle}
|
||||
<Button type="button" variant="ghost" size="icon-sm" onClick={() => onChange(value.filter((_, itemIndex) => itemIndex !== index))} aria-label={`ลบ ${label}`}>
|
||||
<XIcon />
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</SortableExtraItem>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
) : null}
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<Select value={kind} onValueChange={(next) => { setKind(next as ExtraCatalogItem["kind"]); setKey(""); }}>
|
||||
|
||||
@@ -318,7 +318,8 @@ function lunarisItemUrl(
|
||||
kind: "character" | "weapon" | "artifact",
|
||||
key: string,
|
||||
): string {
|
||||
return `https://lunaris.moe/${kind}/${encodeURIComponent(key)}`;
|
||||
const path = kind === "character" ? "char" : kind;
|
||||
return `https://lunaris.moe/${path}/${encodeURIComponent(key)}`;
|
||||
}
|
||||
|
||||
function WeaponSection({ guide }: { guide: PublicGuide }) {
|
||||
|
||||
Reference in New Issue
Block a user