import { ReactNode, useRef, useState } from 'react' import { orderBy, resetRailOrder, setRailOrder, useRailOrder } from './railorder' // Every group is a page: clicking it opens it. `soon` groups are inert placeholders // for the roadmap. export interface Group { id: string; label: string; icon: ReactNode; soon?: boolean } interface Props { groups: Group[]; view: string; header?: ReactNode; badges?: Record; onView: (id: string) => void } // The pages are in the order the account keeps (railorder.ts). "Arrange" at the foot puts a // handle on each; drag one up or down — pointer events, so a finger works as well as a mouse — // and the order is saved with the UI prefs when it is let go. export default function Rail({ groups, view, header, badges, onView }: Props) { const saved = useRailOrder() const [arranging, setArranging] = useState(false) const [drag, setDrag] = useState<{ id: string; order: string[] } | null>(null) const rows = useRef(new Map()) const ordered = drag ? orderBy(groups, drag.order) : orderBy(groups, saved) const start = (id: string) => (e: React.PointerEvent) => { e.preventDefault() e.currentTarget.setPointerCapture(e.pointerId) setDrag({ id, order: ordered.map((g) => g.id) }) } const move = (e: React.PointerEvent) => { if (!drag) return // where the pointer is among the rows: the row whose middle it has crossed const y = e.clientY const order = drag.order.slice() const from = order.indexOf(drag.id) let to = from order.forEach((id, i) => { const el = rows.current.get(id) if (!el || id === drag.id) return const r = el.getBoundingClientRect() const mid = r.top + r.height / 2 if (i < from && y < mid && i < to) to = i if (i > from && y > mid && i > to) to = i }) if (to !== from) { order.splice(from, 1); order.splice(to, 0, drag.id); setDrag({ id: drag.id, order }) } } const end = () => { if (drag) { setRailOrder(drag.order); setDrag(null) } } return ( ) }