Skip to content

Commit

Permalink
fix: dock improvement
Browse files Browse the repository at this point in the history
  • Loading branch information
itsarghyadas committed Dec 19, 2024
1 parent 0f81c46 commit a5b9413
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 35 deletions.
17 changes: 9 additions & 8 deletions content/docs/components/dock.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -55,19 +55,20 @@ npx shadcn@latest add "https://magicui.design/r/dock"

### Dock

| Prop | Type | Description | Default |
| ------------- | --------- | ------------------------------------ | -------- |
| className | string | Custom CSS class for styling | - |
| children | ReactNode | Children elements | - |
| magnification | number | Level of icon magnification | 60 |
| distance | number | Distance from cursor to magnify icon | 140 |
| direction | string | Direction of the dock | "bottom" |
| Prop | Type | Description | Default |
| ----------------- | --------- | ------------------------------------ | -------- |
| className | string | Custom CSS class for styling | - |
| children | ReactNode | Children elements | - |
| iconSize | number | Size of the icon | 40 |
| iconMagnification | number | Level of icon magnification | 60 |
| iconDistance | number | Distance from cursor to magnify icon | 140 |
| direction | string | Direction of the dock | "middle" |

### DockIcon

| Prop | Type | Description | Default |
| ------------- | ----------------- | ------------------------------------ | ------- |
| size | number | Size of the icon | - |
| size | number | Size of the icon | 40 |
| magnification | number | Level of icon magnification | 60 |
| distance | number | Distance from cursor to magnify icon | 140 |
| mouseX | any | Mouse X position for magnification | - |
Expand Down
2 changes: 1 addition & 1 deletion public/r/styles/default/dock.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"files": [
{
"path": "magicui/dock.tsx",
"content": "\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport {\n motion,\n MotionProps,\n useMotionValue,\n useSpring,\n useTransform,\n} from \"motion/react\";\nimport React, { useRef } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nexport interface DockProps extends VariantProps<typeof dockVariants> {\n className?: string;\n magnification?: number;\n distance?: number;\n direction?: \"top\" | \"middle\" | \"bottom\";\n children: React.ReactNode;\n}\n\nconst DEFAULT_MAGNIFICATION = 60;\nconst DEFAULT_DISTANCE = 140;\n\nconst dockVariants = cva(\n \"supports-backdrop-blur:bg-white/10 supports-backdrop-blur:dark:bg-black/10 mx-auto mt-8 flex h-[58px] w-max gap-2 rounded-2xl border p-2 backdrop-blur-md\",\n);\n\nconst Dock = React.forwardRef<HTMLDivElement, DockProps>(\n (\n {\n className,\n children,\n magnification = DEFAULT_MAGNIFICATION,\n distance = DEFAULT_DISTANCE,\n direction = \"bottom\",\n ...props\n },\n ref,\n ) => {\n const mouseX = useMotionValue(Infinity);\n\n const renderChildren = () => {\n return React.Children.map(children, (child) => {\n if (React.isValidElement(child) && child.type === DockIcon) {\n return React.cloneElement(child, {\n ...child.props,\n mouseX: mouseX,\n magnification: magnification,\n distance: distance,\n });\n }\n return child;\n });\n };\n\n return (\n <motion.div\n ref={ref}\n onMouseMove={(e) => mouseX.set(e.pageX)}\n onMouseLeave={() => mouseX.set(Infinity)}\n {...props}\n className={cn(dockVariants({ className }), {\n \"items-start\": direction === \"top\",\n \"items-center\": direction === \"middle\",\n \"items-end\": direction === \"bottom\",\n })}\n >\n {renderChildren()}\n </motion.div>\n );\n },\n);\n\nDock.displayName = \"Dock\";\nexport interface DockIconProps\n extends Omit<MotionProps & React.HTMLAttributes<HTMLDivElement>, \"children\"> {\n size?: number;\n magnification?: number;\n distance?: number;\n mouseX?: any;\n className?: string;\n children?: React.ReactNode;\n}\n\nconst DockIcon = ({\n size,\n magnification = DEFAULT_MAGNIFICATION,\n distance = DEFAULT_DISTANCE,\n mouseX,\n className,\n children,\n ...props\n}: DockIconProps) => {\n const ref = useRef<HTMLDivElement>(null);\n\n const distanceCalc = useTransform(mouseX, (val: number) => {\n const bounds = ref.current?.getBoundingClientRect() ?? { x: 0, width: 0 };\n\n return val - bounds.x - bounds.width / 2;\n });\n\n const widthSync = useTransform(\n distanceCalc,\n [-distance, 0, distance],\n [40, magnification, 40],\n );\n\n const width = useSpring(widthSync, {\n mass: 0.1,\n stiffness: 150,\n damping: 12,\n });\n\n return (\n <motion.div\n ref={ref}\n style={{ width }}\n className={cn(\n \"flex aspect-square cursor-pointer items-center justify-center rounded-full\",\n className,\n )}\n {...props}\n >\n {children}\n </motion.div>\n );\n};\n\nDockIcon.displayName = \"DockIcon\";\n\nexport { Dock, DockIcon, dockVariants };\n",
"content": "\"use client\";\n\nimport React, { PropsWithChildren, useRef } from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport {\n motion,\n MotionValue,\n useMotionValue,\n useSpring,\n useTransform,\n} from \"framer-motion\";\n\nimport { cn } from \"@/lib/utils\";\n\nexport interface DockProps extends VariantProps<typeof dockVariants> {\n className?: string;\n iconSize?: number;\n iconMagnification?: number;\n iconDistance?: number;\n direction?: \"top\" | \"middle\" | \"bottom\";\n children: React.ReactNode;\n}\n\nconst DEFAULT_SIZE = 40;\nconst DEFAULT_MAGNIFICATION = 60;\nconst DEFAULT_DISTANCE = 140;\n\nconst dockVariants = cva(\n \"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\",\n);\n\nconst Dock = React.forwardRef<HTMLDivElement, DockProps>(\n (\n {\n className,\n children,\n iconSize = DEFAULT_SIZE,\n iconMagnification = DEFAULT_MAGNIFICATION,\n iconDistance = DEFAULT_DISTANCE,\n direction = \"middle\",\n ...props\n },\n ref,\n ) => {\n const mouseX = useMotionValue(Infinity);\n\n const renderChildren = () => {\n return React.Children.map(children, (child) => {\n if (React.isValidElement(child) && child.type === DockIcon) {\n return React.cloneElement(child, {\n ...child.props,\n mouseX: mouseX,\n size: iconSize,\n magnification: iconMagnification,\n distance: iconDistance,\n });\n }\n return child;\n });\n };\n\n return (\n <motion.div\n ref={ref}\n onMouseMove={(e) => mouseX.set(e.pageX)}\n onMouseLeave={() => mouseX.set(Infinity)}\n {...props}\n className={cn(dockVariants({ className }), {\n \"items-start\": direction === \"top\",\n \"items-center\": direction === \"middle\",\n \"items-end\": direction === \"bottom\",\n })}\n >\n {renderChildren()}\n </motion.div>\n );\n },\n);\n\nDock.displayName = \"Dock\";\n\nexport interface DockIconProps {\n size?: number;\n magnification?: number;\n distance?: number;\n mouseX?: MotionValue<number>;\n className?: string;\n children?: React.ReactNode;\n props?: PropsWithChildren;\n}\n\nconst DockIcon = ({\n size = DEFAULT_SIZE,\n magnification = DEFAULT_MAGNIFICATION,\n distance = DEFAULT_DISTANCE,\n mouseX,\n className,\n children,\n ...props\n}: DockIconProps) => {\n const ref = useRef<HTMLDivElement>(null);\n const padding = Math.max(6, size * 0.2);\n const defaultMouseX = useMotionValue(Infinity);\n\n const distanceCalc = useTransform(mouseX ?? defaultMouseX, (val: number) => {\n const bounds = ref.current?.getBoundingClientRect() ?? { x: 0, width: 0 };\n return val - bounds.x - bounds.width / 2;\n });\n\n const sizeTransform = useTransform(\n distanceCalc,\n [-distance, 0, distance],\n [size, magnification, size],\n );\n\n const scaleSize = useSpring(sizeTransform, {\n mass: 0.1,\n stiffness: 150,\n damping: 12,\n });\n\n return (\n <motion.div\n ref={ref}\n style={{ width: scaleSize, height: scaleSize, padding }}\n className={cn(\n \"flex aspect-square cursor-pointer items-center justify-center rounded-full\",\n className,\n )}\n {...props}\n >\n {children}\n </motion.div>\n );\n};\n\nDockIcon.displayName = \"DockIcon\";\n\nexport { Dock, DockIcon, dockVariants };\n",
"type": "registry:ui",
"target": ""
}
Expand Down
10 changes: 5 additions & 5 deletions registry/default/example/dock-demo-3.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ export type IconProps = React.HTMLAttributes<SVGElement>;
export default function DockDemo() {
return (
<div className="relative">
<Dock magnification={60} distance={100}>
<DockIcon className="bg-black/10 dark:bg-white/10 p-3">
<Dock iconMagnification={60} iconDistance={100}>
<DockIcon className="bg-black/10 dark:bg-white/10">
<Icons.gitHub className="size-full" />
</DockIcon>
<DockIcon className="bg-black/10 dark:bg-white/10 p-3">
<DockIcon className="bg-black/10 dark:bg-white/10">
<Icons.googleDrive className="size-full" />
</DockIcon>
<DockIcon className="bg-black/10 dark:bg-white/10 p-3">
<DockIcon className="bg-black/10 dark:bg-white/10">
<Icons.notion className="size-full" />
</DockIcon>
<DockIcon className="bg-black/10 dark:bg-white/10 p-3">
<DockIcon className="bg-black/10 dark:bg-white/10">
<Icons.whatsapp className="size-full" />
</DockIcon>
</Dock>
Expand Down
48 changes: 27 additions & 21 deletions registry/default/magicui/dock.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,43 @@
"use client";

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

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

export interface DockProps extends VariantProps<typeof dockVariants> {
className?: string;
magnification?: number;
distance?: number;
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 gap-2 rounded-2xl border p-2 backdrop-blur-md",
"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,
magnification = DEFAULT_MAGNIFICATION,
distance = DEFAULT_DISTANCE,
direction = "bottom",
iconSize = DEFAULT_SIZE,
iconMagnification = DEFAULT_MAGNIFICATION,
iconDistance = DEFAULT_DISTANCE,
direction = "middle",
...props
},
ref,
Expand All @@ -47,8 +50,9 @@ const Dock = React.forwardRef<HTMLDivElement, DockProps>(
return React.cloneElement(child, {
...child.props,
mouseX: mouseX,
magnification: magnification,
distance: distance,
size: iconSize,
magnification: iconMagnification,
distance: iconDistance,
});
}
return child;
Expand All @@ -74,18 +78,19 @@ const Dock = React.forwardRef<HTMLDivElement, DockProps>(
);

Dock.displayName = "Dock";
export interface DockIconProps
extends Omit<MotionProps & React.HTMLAttributes<HTMLDivElement>, "children"> {

export interface DockIconProps {
size?: number;
magnification?: number;
distance?: number;
mouseX?: any;
mouseX?: MotionValue<number>;
className?: string;
children?: React.ReactNode;
props?: PropsWithChildren;
}

const DockIcon = ({
size,
size = DEFAULT_SIZE,
magnification = DEFAULT_MAGNIFICATION,
distance = DEFAULT_DISTANCE,
mouseX,
Expand All @@ -94,20 +99,21 @@ const DockIcon = ({
...props
}: DockIconProps) => {
const ref = useRef<HTMLDivElement>(null);
const padding = Math.max(6, size * 0.2);
const defaultMouseX = useMotionValue(Infinity);

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

return val - bounds.x - bounds.width / 2;
});

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

const width = useSpring(widthSync, {
const scaleSize = useSpring(sizeTransform, {
mass: 0.1,
stiffness: 150,
damping: 12,
Expand All @@ -116,7 +122,7 @@ const DockIcon = ({
return (
<motion.div
ref={ref}
style={{ width }}
style={{ width: scaleSize, height: scaleSize, padding }}
className={cn(
"flex aspect-square cursor-pointer items-center justify-center rounded-full",
className,
Expand Down

0 comments on commit a5b9413

Please sign in to comment.