Skip to content

Commit

Permalink
增加dock栏,但是暂时不考虑增加
Browse files Browse the repository at this point in the history
  • Loading branch information
buyfakett committed Feb 21, 2025
1 parent e227af3 commit c8835b4
Show file tree
Hide file tree
Showing 2 changed files with 173 additions and 0 deletions.
25 changes: 25 additions & 0 deletions src/util/MacDock.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';

import { Dock, DockIcon } from '@/view/magicui/DockUtil';
import { FaGithub } from 'react-icons/fa';

export function MacDock() {
return (
<div className="relative">
<Dock direction="middle">
<DockIcon>
<FaGithub />
</DockIcon>
<DockIcon>
<FaGithub />
</DockIcon>
<DockIcon>
<FaGithub />
</DockIcon>
<DockIcon>
<FaGithub />
</DockIcon>
</Dock>
</div>
);
}
148 changes: 148 additions & 0 deletions src/view/magicui/DockUtil.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
'use client';

import { cva, type VariantProps } from 'class-variance-authority';
import {
motion,
MotionProps,
MotionValue,
useMotionValue,
useSpring,
useTransform,
} from 'motion/react';
import React, { PropsWithChildren, useRef } from 'react';

import { cn } from '@/lib/utils';

export interface DockProps extends VariantProps<typeof dockVariants> {
className?: string;
iconSize?: number;
iconMagnification?: number;
iconDistance?: number;
direction?: 'top' | 'middle' | 'bottom';
children: React.ReactNode;
}

const DEFAULT_SIZE = 40;
const DEFAULT_MAGNIFICATION = 60;
const DEFAULT_DISTANCE = 140;

const dockVariants = cva(
'supports-backdrop-blur:bg-white/10 supports-backdrop-blur:dark:bg-black/10 mx-auto mt-8 flex h-[58px] w-max items-center justify-center gap-2 rounded-2xl border p-2 backdrop-blur-md',
);

const Dock = React.forwardRef<HTMLDivElement, DockProps>(
(
{
className,
children,
iconSize = DEFAULT_SIZE,
iconMagnification = DEFAULT_MAGNIFICATION,
iconDistance = DEFAULT_DISTANCE,
direction = 'middle',
...props
},
ref,
) => {
const mouseX = useMotionValue(Infinity);

const renderChildren = () => {
return React.Children.map(children, (child) => {
if (React.isValidElement(child) && child.type === DockIcon) {
return React.cloneElement(child, {
...child.props,
mouseX: mouseX,
size: iconSize,
magnification: iconMagnification,
distance: iconDistance,
});
}
return child;
});
};

return (
<motion.div
ref={ref}
onMouseMove={(e) => mouseX.set(e.pageX)}
onMouseLeave={() => mouseX.set(Infinity)}
{...props}
className={cn(dockVariants({ className }), {
'items-start': direction === 'top',
'items-center': direction === 'middle',
'items-end': direction === 'bottom',
})}>
{renderChildren()}
</motion.div>
);
},
);

Dock.displayName = 'Dock';

export interface DockIconProps
extends Omit<
MotionProps & React.HTMLAttributes<HTMLDivElement>,
'children'
> {
size?: number;
magnification?: number;
distance?: number;
mouseX?: MotionValue<number>;
className?: string;
children?: React.ReactNode;
props?: PropsWithChildren;
}

const DockIcon = ({
size = DEFAULT_SIZE,
magnification = DEFAULT_MAGNIFICATION,
distance = DEFAULT_DISTANCE,
mouseX,
className,
children,
...props
}: DockIconProps) => {
const ref = useRef<HTMLDivElement>(null);
const padding = Math.max(6, size * 0.2);
const defaultMouseX = useMotionValue(Infinity);

const distanceCalc = useTransform(
mouseX ?? defaultMouseX,
(val: number) => {
const bounds = ref.current?.getBoundingClientRect() ?? {
x: 0,
width: 0,
};
return val - bounds.x - bounds.width / 2;
},
);

const sizeTransform = useTransform(
distanceCalc,
[-distance, 0, distance],
[size, magnification, size],
);

const scaleSize = useSpring(sizeTransform, {
mass: 0.1,
stiffness: 150,
damping: 12,
});

return (
<motion.div
ref={ref}
style={{ width: scaleSize, height: scaleSize, padding }}
className={cn(
'flex aspect-square cursor-pointer items-center justify-center rounded-full',
className,
)}
{...props}>
{children}
</motion.div>
);
};

DockIcon.displayName = 'DockIcon';

export { Dock, DockIcon, dockVariants };

0 comments on commit c8835b4

Please sign in to comment.