mirror of
https://github.com/ourongxing/newsnow.git
synced 2025-01-19 11:19:14 +08:00
feat(ui): drop animation
This commit is contained in:
parent
115f4994bd
commit
46ac93c570
@ -41,7 +41,8 @@ export const CardWrapper = forwardRef<HTMLDivElement, ItemsProps>(({ id, isDragg
|
|||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"flex flex-col h-500px rounded-2xl bg-op-40 p-4 backdrop-blur-5",
|
"flex flex-col h-500px rounded-2xl p-4",
|
||||||
|
"bg-op-40 backdrop-blur-5 transition-opacity-300",
|
||||||
isDragged && "op-50",
|
isDragged && "op-50",
|
||||||
`bg-${sources[id].color}`,
|
`bg-${sources[id].color}`,
|
||||||
)}
|
)}
|
||||||
@ -56,44 +57,6 @@ 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 p-4",
|
|
||||||
"backdrop-blur-5 bg-op-50",
|
|
||||||
`bg-${sources[id].color}`,
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div className={clsx("flex justify-between mx-2 mt-0 mb-2 items-center")}>
|
|
||||||
<div className="flex gap-2 items-center">
|
|
||||||
<div
|
|
||||||
className={clsx("w-8 h-8 rounded-full bg-cover")}
|
|
||||||
style={{
|
|
||||||
backgroundImage: `url(/icons/${id.split("-")[0]}.png)`,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span className="flex flex-col">
|
|
||||||
<span className="flex items-center gap-2">
|
|
||||||
<span className="text-xl font-bold">
|
|
||||||
{sources[id].name}
|
|
||||||
</span>
|
|
||||||
{sources[id]?.title && <span className={clsx("text-sm", `color-${sources[id].color} bg-base op-80 bg-op-50! px-1 rounded`)}>{sources[id].title}</span>}
|
|
||||||
</span>
|
|
||||||
<span className="text-xs">拖拽中</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className={clsx("flex gap-2 text-lg", `color-${sources[id].color}`)}>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={clsx("i-ph:dots-six-vertical-duotone", "cursor-grabbing")}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className={`h-full p-2 overflow-x-auto bg-base bg-op-50! rounded-2xl sprinkle-${sources[id].color}`} />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function NewsCard({ id, inView, handleListeners }: NewsCardProps) {
|
function NewsCard({ id, inView, handleListeners }: NewsCardProps) {
|
||||||
const [focusSources, setFocusSources] = useAtom(focusSourcesAtom)
|
const [focusSources, setFocusSources] = useAtom(focusSourcesAtom)
|
||||||
const [refetchSource, setRefetchSource] = useAtom(refetchSourcesAtom)
|
const [refetchSource, setRefetchSource] = useAtom(refetchSourcesAtom)
|
||||||
@ -136,9 +99,9 @@ function NewsCard({ id, inView, handleListeners }: NewsCardProps) {
|
|||||||
<div className={clsx("flex justify-between mx-2 mt-0 mb-2 items-center")}>
|
<div className={clsx("flex justify-between mx-2 mt-0 mb-2 items-center")}>
|
||||||
<div className="flex gap-2 items-center">
|
<div className="flex gap-2 items-center">
|
||||||
<div
|
<div
|
||||||
className={clsx("w-8 h-8 rounded-full")}
|
className={clsx("w-8 h-8 rounded-full bg-cover")}
|
||||||
style={{
|
style={{
|
||||||
background: `center / contain no-repeat url(/icons/${id.split("-")[0]}.png)`,
|
backgroundImage: `url(/icons/${id.split("-")[0]}.png)`,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<span className="flex flex-col">
|
<span className="flex flex-col">
|
||||||
@ -148,7 +111,7 @@ function NewsCard({ id, inView, handleListeners }: NewsCardProps) {
|
|||||||
</span>
|
</span>
|
||||||
{sources[id]?.title && <span className={clsx("text-sm", `color-${sources[id].color} bg-base op-80 bg-op-50! px-1 rounded`)}>{sources[id].title}</span>}
|
{sources[id]?.title && <span className={clsx("text-sm", `color-${sources[id].color} bg-base op-80 bg-op-50! px-1 rounded`)}>{sources[id].title}</span>}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xs"><UpdateTime query={query} /></span>
|
<span className="text-xs op-70"><UpdateTime query={query} /></span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={clsx("flex gap-2 text-lg", `color-${sources[id].color}`)}>
|
<div className={clsx("flex gap-2 text-lg", `color-${sources[id].color}`)}>
|
||||||
@ -179,8 +142,9 @@ function NewsCard({ id, inView, handleListeners }: NewsCardProps) {
|
|||||||
options={{
|
options={{
|
||||||
overflow: { x: "hidden" },
|
overflow: { x: "hidden" },
|
||||||
}}
|
}}
|
||||||
|
defer
|
||||||
>
|
>
|
||||||
<div className={clsx("duration-500 transition-opacity", isFreshFetching && "op-20")}>
|
<div className={clsx("transition-opacity-500", isFreshFetching && "op-20")}>
|
||||||
{sources[id].type === "hottest" ? <NewsList query={query} /> : <NewsListTimeLine query={query} />}
|
{sources[id].type === "hottest" ? <NewsList query={query} /> : <NewsListTimeLine query={query} />}
|
||||||
</div>
|
</div>
|
||||||
</OverlayScrollbar>
|
</OverlayScrollbar>
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
MouseSensor,
|
MouseSensor,
|
||||||
TouchSensor,
|
TouchSensor,
|
||||||
closestCenter,
|
closestCenter,
|
||||||
|
defaultDropAnimationSideEffects,
|
||||||
useSensor,
|
useSensor,
|
||||||
useSensors,
|
useSensors,
|
||||||
} from "@dnd-kit/core"
|
} from "@dnd-kit/core"
|
||||||
@ -17,8 +18,10 @@ 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 { motion } from "framer-motion"
|
import { motion } from "framer-motion"
|
||||||
|
import { sources } from "@shared/sources"
|
||||||
|
import clsx from "clsx"
|
||||||
import type { ItemsProps } from "./card"
|
import type { ItemsProps } from "./card"
|
||||||
import { CardOverlay, CardWrapper } from "./card"
|
import { CardWrapper } from "./card"
|
||||||
import { currentSourcesAtom } from "~/atoms"
|
import { currentSourcesAtom } from "~/atoms"
|
||||||
|
|
||||||
export function Dnd() {
|
export function Dnd() {
|
||||||
@ -35,8 +38,8 @@ export function Dnd() {
|
|||||||
variants={{
|
variants={{
|
||||||
visible: {
|
visible: {
|
||||||
transition: {
|
transition: {
|
||||||
delayChildren: 0.3,
|
delayChildren: 0.2,
|
||||||
staggerChildren: 0.2,
|
staggerChildren: 0.1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
@ -65,13 +68,7 @@ interface DndProps {
|
|||||||
setItems: (update: SourceID[]) => void
|
setItems: (update: SourceID[]) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const measuringConfig = {
|
function DndWrapper({ items, setItems, children }: PropsWithChildren<DndProps>) {
|
||||||
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))
|
||||||
|
|
||||||
@ -97,7 +94,11 @@ export function DndWrapper({ items, setItems, children }: PropsWithChildren<DndP
|
|||||||
return (
|
return (
|
||||||
<DndContext
|
<DndContext
|
||||||
sensors={sensors}
|
sensors={sensors}
|
||||||
measuring={measuringConfig}
|
measuring={{
|
||||||
|
droppable: {
|
||||||
|
strategy: MeasuringStrategy.Always,
|
||||||
|
},
|
||||||
|
}}
|
||||||
collisionDetection={closestCenter}
|
collisionDetection={closestCenter}
|
||||||
onDragStart={handleDragStart}
|
onDragStart={handleDragStart}
|
||||||
onDragEnd={handleDragEnd}
|
onDragEnd={handleDragEnd}
|
||||||
@ -107,19 +108,61 @@ export function DndWrapper({ items, setItems, children }: PropsWithChildren<DndP
|
|||||||
{children}
|
{children}
|
||||||
</SortableContext>
|
</SortableContext>
|
||||||
<DragOverlay
|
<DragOverlay
|
||||||
adjustScale
|
className="transition-opacity-300"
|
||||||
dropAnimation={{
|
dropAnimation={{
|
||||||
duration: 300,
|
|
||||||
easing: "cubic-bezier(0.25, 1, 0.5, 1)",
|
easing: "cubic-bezier(0.25, 1, 0.5, 1)",
|
||||||
|
duration: 300,
|
||||||
|
sideEffects: defaultDropAnimationSideEffects({
|
||||||
|
className: {
|
||||||
|
active: "op-100",
|
||||||
|
dragOverlay: "op-0",
|
||||||
|
},
|
||||||
|
}),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* {!!activeId && <CardOverlay id={activeId as SourceID} />} */}
|
{!!activeId && <CardOverlay id={activeId as SourceID} />}
|
||||||
<CardOverlay id={activeId as SourceID} />
|
|
||||||
</DragOverlay>
|
</DragOverlay>
|
||||||
</DndContext>
|
</DndContext>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function CardOverlay({ id }: { id: SourceID }) {
|
||||||
|
return (
|
||||||
|
<div className={clsx(
|
||||||
|
"flex flex-col rounded-2xl p-4",
|
||||||
|
"backdrop-blur-5 bg-op-40",
|
||||||
|
`bg-${sources[id].color}`,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className={clsx("flex justify-between mx-2 items-center")}>
|
||||||
|
<div className="flex gap-2 items-center">
|
||||||
|
<div
|
||||||
|
className={clsx("w-8 h-8 rounded-full bg-cover")}
|
||||||
|
style={{
|
||||||
|
backgroundImage: `url(/icons/${id.split("-")[0]}.png)`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span className="flex flex-col">
|
||||||
|
<span className="flex items-center gap-2">
|
||||||
|
<span className="text-xl font-bold">
|
||||||
|
{sources[id].name}
|
||||||
|
</span>
|
||||||
|
{sources[id]?.title && <span className={clsx("text-sm", `color-${sources[id].color} bg-base op-80 bg-op-50! px-1 rounded`)}>{sources[id].title}</span>}
|
||||||
|
</span>
|
||||||
|
<span className="text-xs op-70">拖拽中</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className={clsx("flex gap-2 text-lg", `color-${sources[id].color}`)}>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={clsx("i-ph:dots-six-vertical-duotone", "cursor-grabbing")}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const animateLayoutChanges: AnimateLayoutChanges = (args) => {
|
const animateLayoutChanges: AnimateLayoutChanges = (args) => {
|
||||||
const { isSorting, wasDragging } = args
|
const { isSorting, wasDragging } = args
|
||||||
if (isSorting || wasDragging) {
|
if (isSorting || wasDragging) {
|
||||||
|
@ -16,7 +16,7 @@ const defaultScrollbarParams: UseOverlayScrollbarsParams = {
|
|||||||
defer: true,
|
defer: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function OverlayScrollbar({ children, options, events, defer, ...props }: PropsWithChildren<Props>) {
|
export function OverlayScrollbar({ disabled, children, options, events, defer, ...props }: PropsWithChildren<Props>) {
|
||||||
const ref = useRef<HTMLDivElement>(null)
|
const ref = useRef<HTMLDivElement>(null)
|
||||||
const scrollbarParams = useMemo(() => defu<UseOverlayScrollbarsParams, Array<UseOverlayScrollbarsParams> >({
|
const scrollbarParams = useMemo(() => defu<UseOverlayScrollbarsParams, Array<UseOverlayScrollbarsParams> >({
|
||||||
options,
|
options,
|
||||||
@ -27,6 +27,7 @@ export function OverlayScrollbar({ children, options, events, defer, ...props }:
|
|||||||
const [initialize] = useOverlayScrollbars(scrollbarParams)
|
const [initialize] = useOverlayScrollbars(scrollbarParams)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!disabled) {
|
||||||
initialize({
|
initialize({
|
||||||
target: ref.current!,
|
target: ref.current!,
|
||||||
cancel: {
|
cancel: {
|
||||||
@ -34,7 +35,8 @@ export function OverlayScrollbar({ children, options, events, defer, ...props }:
|
|||||||
nativeScrollbarsOverlaid: true,
|
nativeScrollbarsOverlaid: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}, [initialize])
|
}
|
||||||
|
}, [initialize, disabled])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={ref} {...props}>
|
<div ref={ref} {...props}>
|
||||||
@ -75,7 +77,7 @@ export function GlobalOverlayScrollbar({ children, ...props }: PropsWithChildren
|
|||||||
events: {
|
events: {
|
||||||
scroll: (_, e) => onScroll(e),
|
scroll: (_, e) => onScroll(e),
|
||||||
},
|
},
|
||||||
defer: true,
|
defer: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user