Added drag and drop
This commit is contained in:
@@ -1,7 +1,14 @@
|
||||
"use client"
|
||||
|
||||
import { Accordion } from "@/components/ui/accordion"
|
||||
import { closestCenter, DndContext, DragEndEvent, KeyboardSensor, PointerSensor, useSensor, useSensors } from "@dnd-kit/core"
|
||||
import { arrayMove, SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy } from "@dnd-kit/sortable"
|
||||
import { useSortable } from "@dnd-kit/sortable"
|
||||
import { CSS } from "@dnd-kit/utilities"
|
||||
import Cookies from "js-cookie"
|
||||
import { GripVertical } from "lucide-react"
|
||||
import { usePathname } from "next/navigation"
|
||||
import { useEffect, useState } from "react"
|
||||
|
||||
import { Player } from "@/lib/schema/player"
|
||||
import ArcadeStats from "./_stats/arcade/arcade"
|
||||
@@ -18,6 +25,41 @@ import TNTGamesStats from "./_stats/tnt-games/tnt-games"
|
||||
import UHCStats from "./_stats/uhc/uhc"
|
||||
import WoolGamesStats from "./_stats/woolgames/woolgames"
|
||||
|
||||
interface SortableStatItemProps {
|
||||
id: string
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
function SortableStatItem({ id, children }: SortableStatItemProps) {
|
||||
const {
|
||||
attributes,
|
||||
listeners,
|
||||
setNodeRef,
|
||||
transform,
|
||||
transition,
|
||||
isDragging
|
||||
} = useSortable({ id })
|
||||
|
||||
const style = {
|
||||
transform: CSS.Transform.toString(transform),
|
||||
transition,
|
||||
opacity: isDragging ? 0.5 : 1
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={setNodeRef} style={style} className="relative group">
|
||||
<div
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
className="absolute -left-6 top-1/2 z-10 p-1 opacity-0 transition-opacity -translate-y-1/2 group-hover:opacity-50 hover:opacity-100 cursor-grab active:cursor-grabbing"
|
||||
>
|
||||
<GripVertical className="size-4 text-muted-foreground" />
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function PlayerPageLoadText() {
|
||||
const path = usePathname()
|
||||
|
||||
@@ -25,28 +67,105 @@ export function PlayerPageLoadText() {
|
||||
}
|
||||
|
||||
export default function PlayerStats(
|
||||
{ stats, achievements }: { stats: NonNullable<Player["player"]["stats"]>, achievements: Player["player"]["achievements"] }
|
||||
{ stats, achievements, layout }: {
|
||||
stats: NonNullable<Player["player"]["stats"]>
|
||||
achievements: Player["player"]["achievements"]
|
||||
layout: string[] | undefined
|
||||
}
|
||||
) {
|
||||
const statsComponents = {
|
||||
"bedwars": <BedwarsStats stats={stats.Bedwars} />,
|
||||
"skywars": (
|
||||
<SkyWarsStats
|
||||
stats={stats.SkyWars}
|
||||
achievements_skywars_opal_obsession={achievements?.["skywars_opal_obsession"] ?? 0}
|
||||
/>
|
||||
),
|
||||
"duels": <DuelsStats stats={stats.Duels} />,
|
||||
"murder-mystery": <MurderMysteryStats stats={stats.MurderMystery} />,
|
||||
"build-battle": <BuildBattleStats stats={stats.BuildBattle} />,
|
||||
"uhc": <UHCStats stats={stats.UHC} />,
|
||||
"pit": <PitStats stats={stats.Pit} />,
|
||||
"tnt-games": <TNTGamesStats stats={stats.TNTGames} />,
|
||||
"megawalls": <MegaWallsStats stats={stats.MegaWalls} />,
|
||||
"copsandcrims": <CopsAndCrimsStats stats={stats.CopsAndCrims} />,
|
||||
"woolgames": <WoolGamesStats stats={stats.WoolGames} />,
|
||||
"blitz": <BlitzStats stats={stats.Blitz} />,
|
||||
"arcade": <ArcadeStats stats={stats.Arcade} />
|
||||
} as const
|
||||
|
||||
const defaultOrder = Object.keys(statsComponents).sort()
|
||||
const orderToUse = layout || defaultOrder
|
||||
|
||||
const [statsOrder, setStatsOrder] = useState<string[]>(layout || defaultOrder)
|
||||
const [isClient, setIsClient] = useState(false)
|
||||
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor),
|
||||
useSensor(KeyboardSensor, {
|
||||
coordinateGetter: sortableKeyboardCoordinates
|
||||
})
|
||||
)
|
||||
|
||||
function updateStatsOrder(arr: string[]) {
|
||||
Cookies.set("player-stats-order", JSON.stringify(arr), {
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite: "lax",
|
||||
expires: 365
|
||||
})
|
||||
}
|
||||
|
||||
function handleDragEnd(event: DragEndEvent) {
|
||||
const { active, over } = event
|
||||
|
||||
if (over && active.id !== over.id) {
|
||||
const oldIndex = statsOrder.indexOf(active.id as string)
|
||||
const newIndex = statsOrder.indexOf(over.id as string)
|
||||
const newOrder = arrayMove(statsOrder, oldIndex, newIndex)
|
||||
|
||||
setStatsOrder(newOrder)
|
||||
updateStatsOrder(newOrder)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setIsClient(true)
|
||||
if (layout && layout.length > 0) {
|
||||
setStatsOrder(layout)
|
||||
}
|
||||
}, [layout])
|
||||
|
||||
if (!isClient) {
|
||||
return (
|
||||
<div className="mx-auto w-full lg:mx-0 lg:w-3/4 max-w-120 md:max-w-7/10">
|
||||
<Accordion type="multiple" className="space-y-4">
|
||||
{orderToUse.map((statKey) => (
|
||||
<div key={statKey}>
|
||||
{statsComponents[statKey as keyof typeof statsComponents]}
|
||||
</div>
|
||||
))}
|
||||
</Accordion>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx-auto w-full lg:mx-0 lg:w-3/4 max-w-120 md:max-w-7/10">
|
||||
<Accordion type="multiple" className="space-y-4">
|
||||
<BedwarsStats stats={stats.Bedwars} />
|
||||
<SkyWarsStats
|
||||
stats={stats.SkyWars}
|
||||
achievements_skywars_opal_obsession={achievements?.["skywars_opal_obsession"] ?? 0}
|
||||
/>
|
||||
<DuelsStats stats={stats.Duels} />
|
||||
<MurderMysteryStats stats={stats.MurderMystery} />
|
||||
<BuildBattleStats stats={stats.BuildBattle} />
|
||||
<UHCStats stats={stats.UHC} />
|
||||
<PitStats stats={stats.Pit} />
|
||||
<TNTGamesStats stats={stats.TNTGames} />
|
||||
<MegaWallsStats stats={stats.MegaWalls} />
|
||||
<CopsAndCrimsStats stats={stats.CopsAndCrims} />
|
||||
<WoolGamesStats stats={stats.WoolGames} />
|
||||
<BlitzStats stats={stats.Blitz} />
|
||||
<ArcadeStats stats={stats.Arcade} />
|
||||
</Accordion>
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
onDragEnd={handleDragEnd}
|
||||
>
|
||||
<SortableContext items={statsOrder} strategy={verticalListSortingStrategy}>
|
||||
<Accordion type="multiple" className="space-y-4">
|
||||
{statsOrder.map((statKey) => (
|
||||
<SortableStatItem key={statKey} id={statKey}>
|
||||
{statsComponents[statKey as keyof typeof statsComponents]}
|
||||
</SortableStatItem>
|
||||
))}
|
||||
</Accordion>
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user