refactor(ui): use dndkit instead of framer-motion

This commit is contained in:
Ou 2024-10-17 18:34:31 +08:00
parent 51018d4adb
commit 115f4994bd
3 changed files with 74 additions and 45 deletions

View File

@ -20,6 +20,7 @@ export const originSources = {
"zhihu": { "zhihu": {
name: "知乎", name: "知乎",
type: "hottest", type: "hottest",
color: "blue",
home: "https://www.zhihu.com", home: "https://www.zhihu.com",
}, },
"weibo": { "weibo": {
@ -47,12 +48,14 @@ export const originSources = {
name: "华尔街见闻", name: "华尔街见闻",
interval: Time.Fast, interval: Time.Fast,
type: "realtime", type: "realtime",
color: "blue",
home: "https://wallstreetcn.com/", home: "https://wallstreetcn.com/",
title: "快讯", title: "快讯",
}, },
"36kr": { "36kr": {
name: "36氪", name: "36氪",
type: "realtime", type: "realtime",
color: "blue",
home: "https://36kr.com", home: "https://36kr.com",
sub: { sub: {
quick: { quick: {

View File

@ -59,8 +59,8 @@ export const CardWrapper = forwardRef<HTMLDivElement, ItemsProps>(({ id, isDragg
export function CardOverlay({ id }: { id: SourceID }) { export function CardOverlay({ id }: { id: SourceID }) {
return ( return (
<div className={clsx( <div className={clsx(
"flex flex-col h-500px rounded-2xl bg-op-50 p-4 backdrop-blur-5", "flex flex-col h-500px rounded-2xl p-4",
"backdrop-blur-5 bg-op-40", "backdrop-blur-5 bg-op-50",
`bg-${sources[id].color}`, `bg-${sources[id].color}`,
)} )}
> >

View File

@ -1,19 +1,22 @@
import type { PropsWithChildren } from "react"
import { useCallback, useState } from "react" import { useCallback, useState } from "react"
import type { DragEndEvent, DragStartEvent } from "@dnd-kit/core" import type { DragEndEvent, DragStartEvent } from "@dnd-kit/core"
import { import {
DndContext, DndContext,
DragOverlay, DragOverlay,
MeasuringStrategy,
MouseSensor, MouseSensor,
TouchSensor, TouchSensor,
closestCenter, closestCenter,
useSensor, useSensor,
useSensors, useSensors,
} from "@dnd-kit/core" } from "@dnd-kit/core"
import { SortableContext, arrayMove, rectSortingStrategy, useSortable } from "@dnd-kit/sortable" import type { AnimateLayoutChanges } from "@dnd-kit/sortable"
import { SortableContext, arrayMove, defaultAnimateLayoutChanges, rectSortingStrategy, useSortable } from "@dnd-kit/sortable"
import { useAtom } from "jotai" import { useAtom } from "jotai"
import type { SourceID } from "@shared/types" import type { SourceID } from "@shared/types"
import { CSS } from "@dnd-kit/utilities" import { CSS } from "@dnd-kit/utilities"
import { LayoutGroup, motion } from "framer-motion" import { motion } from "framer-motion"
import type { ItemsProps } from "./card" import type { ItemsProps } from "./card"
import { CardOverlay, CardWrapper } from "./card" import { CardOverlay, CardWrapper } from "./card"
import { currentSourcesAtom } from "~/atoms" import { currentSourcesAtom } from "~/atoms"
@ -22,42 +25,37 @@ export function Dnd() {
const [items, setItems] = useAtom(currentSourcesAtom) const [items, setItems] = useAtom(currentSourcesAtom)
return ( return (
<DndWrapper items={items} setItems={setItems}> <DndWrapper items={items} setItems={setItems}>
{activeid => ( <motion.ol
<motion.ol className="grid w-full gap-6"
className="grid w-full gap-6" style={{
style={{ gridTemplateColumns: "repeat(auto-fill, minmax(350px, 1fr))",
gridTemplateColumns: "repeat(auto-fill, minmax(350px, 1fr))", }}
}} initial="hidden"
initial="hidden" animate="visible"
animate="visible" variants={{
variants={{ visible: {
visible: { transition: {
transition: { delayChildren: 0.3,
delayChildren: 0.3, staggerChildren: 0.2,
staggerChildren: 0.2,
},
}, },
}} },
> }}
<LayoutGroup> >
{items.map(id => ( {items.map(id => (
<motion.li <motion.li
layout={!activeid} key={id}
key={id} variants={{
variants={{ hidden: { y: 20, opacity: 0 },
hidden: { y: 20, opacity: 0 }, visible: {
visible: { y: 0,
y: 0, opacity: 1,
opacity: 1, },
}, }}
}} >
> <SortableCardWrapper id={id} />
<SortableCardWrapper id={id} /> </motion.li>
</motion.li> ))}
))} </motion.ol>
</LayoutGroup>
</motion.ol>
)}
</DndWrapper> </DndWrapper>
) )
} }
@ -65,10 +63,15 @@ export function Dnd() {
interface DndProps { interface DndProps {
items: SourceID[] items: SourceID[]
setItems: (update: SourceID[]) => void setItems: (update: SourceID[]) => void
children: (activeId: string | null) => React.ReactNode
} }
export function DndWrapper({ items, setItems, children }: DndProps) { const measuringConfig = {
droppable: {
strategy: MeasuringStrategy.Always,
},
}
export function DndWrapper({ items, setItems, children }: PropsWithChildren<DndProps>) {
const [activeId, setActiveId] = useState<string | null>(null) const [activeId, setActiveId] = useState<string | null>(null)
const sensors = useSensors(useSensor(MouseSensor), useSensor(TouchSensor)) const sensors = useSensors(useSensor(MouseSensor), useSensor(TouchSensor))
@ -94,21 +97,37 @@ export function DndWrapper({ items, setItems, children }: DndProps) {
return ( return (
<DndContext <DndContext
sensors={sensors} sensors={sensors}
measuring={measuringConfig}
collisionDetection={closestCenter} collisionDetection={closestCenter}
onDragStart={handleDragStart} onDragStart={handleDragStart}
onDragEnd={handleDragEnd} onDragEnd={handleDragEnd}
onDragCancel={handleDragCancel} onDragCancel={handleDragCancel}
> >
<SortableContext items={items} strategy={rectSortingStrategy}> <SortableContext items={items} strategy={rectSortingStrategy}>
{children(activeId)} {children}
</SortableContext> </SortableContext>
<DragOverlay adjustScale style={{ transformOrigin: "0 0 " }}> <DragOverlay
{!!activeId && <CardOverlay id={activeId as SourceID} />} adjustScale
dropAnimation={{
duration: 300,
easing: "cubic-bezier(0.25, 1, 0.5, 1)",
}}
>
{/* {!!activeId && <CardOverlay id={activeId as SourceID} />} */}
<CardOverlay id={activeId as SourceID} />
</DragOverlay> </DragOverlay>
</DndContext> </DndContext>
) )
} }
const animateLayoutChanges: AnimateLayoutChanges = (args) => {
const { isSorting, wasDragging } = args
if (isSorting || wasDragging) {
return defaultAnimateLayoutChanges(args)
}
return true
}
function SortableCardWrapper({ id, ...props }: ItemsProps) { function SortableCardWrapper({ id, ...props }: ItemsProps) {
const { const {
isDragging, isDragging,
@ -117,7 +136,14 @@ function SortableCardWrapper({ id, ...props }: ItemsProps) {
setNodeRef, setNodeRef,
transform, transform,
transition, transition,
} = useSortable({ id }) } = useSortable({
id,
animateLayoutChanges,
transition: {
duration: 300,
easing: "cubic-bezier(0.25, 1, 0.5, 1)",
},
})
const style = { const style = {
transform: CSS.Transform.toString(transform), transform: CSS.Transform.toString(transform),