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

View File

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

View File

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