Skip to content

Instantly share code, notes, and snippets.

@danieljpgo
Created July 12, 2024 14:47
Show Gist options
  • Save danieljpgo/3133e20a4cd706ddd05aa6fc28a0b4bd to your computer and use it in GitHub Desktop.
Save danieljpgo/3133e20a4cd706ddd05aa6fc28a0b4bd to your computer and use it in GitHub Desktop.
import * as React from "react";
import { cxm } from "ui/lib/cva";
const speeds = {
slow: "40s",
fast: "20s",
} as const;
const directions = {
left: "forwards",
right: "reverse",
} as const;
type MarqueeProps = React.ComponentPropsWithoutRef<"ul"> & {
children: Array<JSX.Element>;
speed?: keyof typeof speeds;
direction?: keyof typeof directions;
};
/**
* `<Marquee/>` component must be used together with `<MarqueeItem/>` to compose the
* basic behavior of a `Marquee` or 'Horizontal scroll'.
*
* @example
* ```tsx
* return (
* <Marquee speed="fast" direction="left" className="gap-4 md:gap-6">
* <MarqueeItem>...</MarqueeItem>
* <MarqueeItem>...</MarqueeItem>
* <MarqueeItem>...</MarqueeItem>
* ...
* </Marquee>
* );
* ```
*/
export const Marquee = React.forwardRef<React.ElementRef<"ul">, MarqueeProps>(
(props, ref) => {
const {
children,
className,
speed = "slow",
direction = "left",
style,
...attrs
} = props;
const duplicate = React.Children.map(children, (child) => {
if (child.type !== MarqueeItem) {
throw new Error(
"<Marquee/> component only accepts as children the <MarqueeItem/> component",
);
}
return child;
});
return (
<div className="horizontal-fade flex-nowrap overflow-hidden">
<ul
data-direction="left"
ref={ref}
className={cxm("flex w-max", className)}
style={{
"--duration": speeds[speed],
"--direction": directions[direction],
animation:
"marquee var(--duration) var(--direction) linear infinite",
...style,
}}
{...attrs}
>
{children}
{duplicate?.map((child) =>
React.cloneElement(child, {
["aria-hidden"]: true,
}),
)}
<li aria-hidden />
</ul>
</div>
);
},
);
Marquee.displayName = "Marquee";
/**
* `<MarqueeItem/>` component must be used together with `<Marquee/>`.
* @example
* ```tsx
* return (
* <Marquee className="gap-4 md:gap-6">
* ...
* <MarqueeItem>...</MarqueeItem>
* </Marquee>
* );
* ```
*/
export const MarqueeItem = React.forwardRef<
React.ElementRef<"li">,
React.ComponentPropsWithoutRef<"li">
>(({ children, ...attrs }, ref) => {
return (
<li ref={ref} {...attrs}>
{children}
</li>
);
});
MarqueeItem.displayName = "MarqueeItem";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment