11'use client' ;
22
3- import {
4- closestCenter ,
5- DndContext ,
6- type DragEndEvent ,
7- KeyboardSensor ,
8- PointerSensor ,
9- useSensor ,
10- useSensors ,
11- } from '@dnd-kit/core' ;
12- import {
13- arrayMove ,
14- SortableContext ,
15- sortableKeyboardCoordinates ,
16- useSortable ,
17- verticalListSortingStrategy ,
18- } from '@dnd-kit/sortable' ;
19- import { CSS } from '@dnd-kit/utilities' ;
203import { GripVertical } from 'lucide-react' ;
214import { useState } from 'react' ;
225
@@ -36,69 +19,6 @@ interface OrderingProps {
3619 onSubmit ?: ( order : number [ ] ) => void ;
3720}
3821
39- interface OrderedItem {
40- text : string ;
41- origIdx : number ;
42- }
43-
44- function SortableRow ( {
45- item,
46- position,
47- } : {
48- item : OrderedItem ;
49- position : number ;
50- } ) {
51- const {
52- attributes,
53- listeners,
54- setNodeRef,
55- setActivatorNodeRef,
56- transform,
57- transition,
58- isDragging,
59- } = useSortable ( { id : item . origIdx } ) ;
60-
61- return (
62- < div
63- ref = { setNodeRef }
64- style = { { transform : CSS . Transform . toString ( transform ) , transition } }
65- className = { cn (
66- 'flex w-full items-center gap-3 rounded-2xl px-4 py-3.5' ,
67- isDragging
68- ? 'relative z-10 border-2 border-[var(--selected-border)] bg-[var(--selected-bg)] shadow-[0_4px_12px_rgba(246,162,0,0.25)]'
69- : 'border-[1.5px] border-[var(--border)] bg-[var(--card-bg)]' ,
70- ) }
71- >
72- < button
73- ref = { setActivatorNodeRef }
74- type = "button"
75- aria-label = "Przeciągnij, aby zmienić kolejność"
76- className = "flex cursor-grab touch-none items-center active:cursor-grabbing"
77- { ...attributes }
78- { ...listeners }
79- >
80- < GripVertical
81- size = { 18 }
82- className = { cn (
83- isDragging ? 'text-[var(--orange)]' : 'text-[var(--text-muted)]' ,
84- ) }
85- />
86- </ button >
87- < span className = "flex h-[26px] w-[26px] flex-shrink-0 items-center justify-center rounded-full bg-[var(--orange)] text-xs font-bold text-white" >
88- { position }
89- </ span >
90- < span
91- className = { cn (
92- 'text-sm text-[var(--text-dark)]' ,
93- isDragging ? 'font-semibold' : 'font-medium' ,
94- ) }
95- >
96- { item . text }
97- </ span >
98- </ div >
99- ) ;
100- }
101-
10222export default function Ordering ( {
10323 questionNumber,
10424 totalQuestions,
@@ -108,25 +28,16 @@ export default function Ordering({
10828 items,
10929 onSubmit,
11030} : OrderingProps ) {
111- const [ ordered , setOrdered ] = useState < OrderedItem [ ] > ( ( ) =>
31+ const [ ordered , setOrdered ] = useState ( ( ) =>
11232 items . map ( ( text , origIdx ) => ( { text, origIdx } ) ) ,
11333 ) ;
34+ const [ dragging , setDragging ] = useState < number | null > ( null ) ;
11435
115- const sensors = useSensors (
116- useSensor ( PointerSensor , { activationConstraint : { distance : 8 } } ) ,
117- useSensor ( KeyboardSensor , {
118- coordinateGetter : sortableKeyboardCoordinates ,
119- } ) ,
120- ) ;
121-
122- const handleDragEnd = ( { active, over } : DragEndEvent ) => {
123- if ( over && active . id !== over . id ) {
124- setOrdered ( ( prev ) => {
125- const oldIndex = prev . findIndex ( ( o ) => o . origIdx === active . id ) ;
126- const newIndex = prev . findIndex ( ( o ) => o . origIdx === over . id ) ;
127- return arrayMove ( prev , oldIndex , newIndex ) ;
128- } ) ;
129- }
36+ const moveItem = ( from : number , to : number ) => {
37+ const next = [ ...ordered ] ;
38+ const [ item ] = next . splice ( from , 1 ) ;
39+ next . splice ( to , 0 , item ) ;
40+ setOrdered ( next ) ;
13041 } ;
13142
13243 return (
@@ -137,22 +48,52 @@ export default function Ordering({
13748 >
13849 < QuestionCard question = { question } hint = { hint } />
13950
140- < DndContext
141- sensors = { sensors }
142- collisionDetection = { closestCenter }
143- onDragEnd = { handleDragEnd }
144- >
145- < SortableContext
146- items = { ordered . map ( ( o ) => o . origIdx ) }
147- strategy = { verticalListSortingStrategy }
148- >
149- < div className = "flex flex-col gap-2.5" >
150- { ordered . map ( ( item , i ) => (
151- < SortableRow key = { item . origIdx } item = { item } position = { i + 1 } />
152- ) ) }
153- </ div >
154- </ SortableContext >
155- </ DndContext >
51+ < div className = "flex flex-col gap-2.5" >
52+ { ordered . map ( ( { text, origIdx } , i ) => {
53+ const isDragging = dragging === i ;
54+ return (
55+ < div
56+ key = { origIdx }
57+ draggable
58+ onDragStart = { ( ) => setDragging ( i ) }
59+ onDragOver = { ( e ) => {
60+ e . preventDefault ( ) ;
61+ if ( dragging !== null && dragging !== i ) {
62+ moveItem ( dragging , i ) ;
63+ setDragging ( i ) ;
64+ }
65+ } }
66+ onDragEnd = { ( ) => setDragging ( null ) }
67+ className = { cn (
68+ 'flex w-full cursor-grab items-center gap-3 rounded-2xl px-4 py-3.5 transition-all' ,
69+ isDragging
70+ ? 'border-2 border-[var(--selected-border)] bg-[var(--selected-bg)] shadow-[0_4px_12px_rgba(246,162,0,0.25)]'
71+ : 'border-[1.5px] border-[var(--border)] bg-[var(--card-bg)]' ,
72+ ) }
73+ >
74+ < GripVertical
75+ size = { 18 }
76+ className = { cn (
77+ isDragging
78+ ? 'text-[var(--orange)]'
79+ : 'text-[var(--text-muted)]' ,
80+ ) }
81+ />
82+ < span className = "flex h-[26px] w-[26px] flex-shrink-0 items-center justify-center rounded-full bg-[var(--orange)] text-xs font-bold text-white" >
83+ { i + 1 }
84+ </ span >
85+ < span
86+ className = { cn (
87+ 'text-sm text-[var(--text-dark)]' ,
88+ isDragging ? 'font-semibold' : 'font-medium' ,
89+ ) }
90+ >
91+ { text }
92+ </ span >
93+ </ div >
94+ ) ;
95+ } ) }
96+ </ div >
15697
15798 < SubmitButton
15899 label = "Zatwierdź kolejność"
0 commit comments