"use client";
import { type HTMLMotionProps, isMotionComponent, motion } from "motion/react";
import * as React from "react";
import { cn } from "@/lib/utils";
type AnyProps = Record;
type DOMMotionProps = Omit<
HTMLMotionProps,
"ref"
> & { ref?: React.Ref };
type WithAsChild =
| (Base & { asChild: true; children: React.ReactElement })
| (Base & { asChild?: false | undefined });
type SlotProps = {
children?: React.ReactElement;
} & DOMMotionProps;
function mergeRefs(
...refs: (React.Ref | undefined)[]
): React.RefCallback {
return (node) => {
refs.forEach((ref) => {
if (!ref) return;
if (typeof ref === "function") {
ref(node);
} else {
(ref as React.RefObject).current = node;
}
});
};
}
function mergeProps(
childProps: AnyProps,
slotProps: DOMMotionProps,
): AnyProps {
const merged: AnyProps = { ...childProps, ...slotProps };
if (childProps.className || slotProps.className) {
merged.className = cn(
childProps.className as string,
slotProps.className as string,
);
}
if (childProps.style || slotProps.style) {
merged.style = {
...(childProps.style as React.CSSProperties),
...(slotProps.style as React.CSSProperties),
};
}
return merged;
}
function Slot({
children,
ref,
...props
}: SlotProps) {
const isAlreadyMotion = React.useMemo(() => {
if (!React.isValidElement(children)) return false;
return (
typeof children.type === "object" &&
children.type !== null &&
isMotionComponent(children.type)
);
}, [children]);
const Base = React.useMemo(() => {
if (!React.isValidElement(children)) return motion.div;
return isAlreadyMotion
? (children.type as React.ElementType)
: motion.create(children.type as React.ElementType);
}, [isAlreadyMotion, children]);
if (!React.isValidElement(children)) return null;
const { ref: childRef, ...childProps } = children.props as AnyProps;
const mergedProps = mergeProps(childProps, props);
return (
, ref)} />
);
}
export {
type AnyProps,
type DOMMotionProps,
Slot,
type SlotProps,
type WithAsChild,
};