feat: support dnd to reorder favorite relays
This commit is contained in:
@@ -1,17 +1,63 @@
|
||||
import { useFavoriteRelays } from '@/providers/FavoriteRelaysProvider'
|
||||
import {
|
||||
closestCenter,
|
||||
DndContext,
|
||||
DragEndEvent,
|
||||
KeyboardSensor,
|
||||
PointerSensor,
|
||||
useSensor,
|
||||
useSensors
|
||||
} from '@dnd-kit/core'
|
||||
import { restrictToParentElement, restrictToVerticalAxis } from '@dnd-kit/modifiers'
|
||||
import {
|
||||
arrayMove,
|
||||
SortableContext,
|
||||
sortableKeyboardCoordinates,
|
||||
verticalListSortingStrategy
|
||||
} from '@dnd-kit/sortable'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import RelayItem from './RelayItem'
|
||||
|
||||
export default function FavoriteRelayList() {
|
||||
const { t } = useTranslation()
|
||||
const { favoriteRelays } = useFavoriteRelays()
|
||||
const { favoriteRelays, reorderFavoriteRelays } = useFavoriteRelays()
|
||||
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor),
|
||||
useSensor(KeyboardSensor, {
|
||||
coordinateGetter: sortableKeyboardCoordinates
|
||||
})
|
||||
)
|
||||
|
||||
const handleDragEnd = (event: DragEndEvent) => {
|
||||
const { active, over } = event
|
||||
|
||||
if (over && active.id !== over.id) {
|
||||
const oldIndex = favoriteRelays.findIndex((relay) => relay === active.id)
|
||||
const newIndex = favoriteRelays.findIndex((relay) => relay === over.id)
|
||||
|
||||
const reorderedRelays = arrayMove(favoriteRelays, oldIndex, newIndex)
|
||||
reorderFavoriteRelays(reorderedRelays)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<div className="text-muted-foreground font-semibold select-none">{t('Relays')}</div>
|
||||
{favoriteRelays.map((relay) => (
|
||||
<RelayItem key={relay} relay={relay} />
|
||||
))}
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
onDragEnd={handleDragEnd}
|
||||
modifiers={[restrictToVerticalAxis, restrictToParentElement]}
|
||||
>
|
||||
<SortableContext items={favoriteRelays} strategy={verticalListSortingStrategy}>
|
||||
<div className="grid gap-2">
|
||||
{favoriteRelays.map((relay) => (
|
||||
<RelayItem key={relay} relay={relay} />
|
||||
))}
|
||||
</div>
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,18 +1,43 @@
|
||||
import { toRelay } from '@/lib/link'
|
||||
import { useSecondaryPage } from '@/PageManager'
|
||||
import { useSortable } from '@dnd-kit/sortable'
|
||||
import { CSS } from '@dnd-kit/utilities'
|
||||
import { GripVertical } from 'lucide-react'
|
||||
import RelayIcon from '../RelayIcon'
|
||||
import SaveRelayDropdownMenu from '../SaveRelayDropdownMenu'
|
||||
|
||||
export default function RelayItem({ relay }: { relay: string }) {
|
||||
const { push } = useSecondaryPage()
|
||||
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
|
||||
id: relay
|
||||
})
|
||||
|
||||
const style = {
|
||||
transform: CSS.Transform.toString(transform),
|
||||
transition,
|
||||
opacity: isDragging ? 0.5 : 1
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex gap-2 border rounded-lg p-4 items-center clickable select-none"
|
||||
className="relative group clickable flex gap-2 border rounded-lg p-2 pr-2.5 items-center justify-between select-none"
|
||||
ref={setNodeRef}
|
||||
style={style}
|
||||
onClick={() => push(toRelay(relay))}
|
||||
>
|
||||
<RelayIcon url={relay} />
|
||||
<div className="flex-1 w-0 truncate font-semibold">{relay}</div>
|
||||
<div className="flex items-center gap-1 flex-1">
|
||||
<div
|
||||
className="cursor-grab active:cursor-grabbing p-2 hover:bg-muted rounded touch-none shrink-0"
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
>
|
||||
<GripVertical className="size-4 text-muted-foreground" />
|
||||
</div>
|
||||
<div className="flex gap-2 items-center flex-1">
|
||||
<RelayIcon url={relay} />
|
||||
<div className="flex-1 w-0 truncate font-semibold">{relay}</div>
|
||||
</div>
|
||||
</div>
|
||||
<SaveRelayDropdownMenu urls={[relay]} />
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -88,7 +88,9 @@ export default function SaveRelayDropdownMenu({
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>{trigger}</DropdownMenuTrigger>
|
||||
<DropdownMenuTrigger asChild className="px-2">
|
||||
{trigger}
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent onClick={(e) => e.stopPropagation()}>
|
||||
<DropdownMenuLabel>{t('Save to')} ...</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
Reference in New Issue
Block a user