Review
Keyboard-sortable boarding pass sections
Airline ops asked for drag reorder of boarding-pass layout blocks (gate map, bag tags, lounge codes). Wires @dnd-kit sortable with pointer + keyboard sensors so agents can reorder without a mouse, using closestCenter collision and sortableKeyboardCoordinates.
dnd-kitTier 2a11ydnd-kitfrontend
Click a line to flag it, pick one or more labels, then submit. If the change looks correct, approve it.
components/boarding/PassSectionOrder.tsx+21-7
| 18 | 18 | export function PassSectionOrder({ sections, onChange }: Props) { | |
| 19 | + | const sensors = useSensors( | |
| 20 | + | useSensor(PointerSensor, { activationConstraint: { distance: 6 } }), | |
| 21 | + | useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }), | |
| 22 | + | ); | |
| 19 | 23 | ||
| 24 | + | function handleDragEnd({ active, over }: DragEndEvent) { | |
| 25 | + | if (!over || active.id === over.id) return; | |
| 26 | + | const oldIndex = sections.findIndex((s) => s.id === active.id); | |
| 27 | + | const newIndex = sections.findIndex((s) => s.id === over.id); | |
| 28 | + | onChange(arrayMove(sections, oldIndex, newIndex)); | |
| 29 | + | } | |
| 20 | 30 | ||
| 21 | - | return ( | |
| 22 | - | <ul className="section-list"> | |
| 23 | - | {sections.map((s) => ( | |
| 24 | - | <li key={s.id}>{s.label}</li> | |
| 25 | - | ))} | |
| 26 | - | </ul> | |
| 27 | - | ); | |
| 31 | + | return ( | |
| 32 | + | <DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}> | |
| 33 | + | <SortableContext items={sections.map((s) => s.id)} strategy={verticalListSortingStrategy}> | |
| 34 | + | <ul className="section-list" aria-label="Boarding pass section order"> | |
| 35 | + | {sections.map((s) => ( | |
| 36 | + | <SortableSection key={s.id} id={s.id} label={s.label} /> | |
| 37 | + | ))} | |
| 38 | + | </ul> | |
| 39 | + | </SortableContext> | |
| 40 | + | </DndContext> | |
| 41 | + | ); | |
| 28 | 42 | } |