mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-08 08:36:37 +08:00
wavelet init
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import {type HTMLMotionProps, motion} from 'motion/react';
|
||||
|
||||
import {Slot, type WithAsChild} from '@/components/animate-ui/primitives/animate/slot';
|
||||
|
||||
type ButtonProps = WithAsChild<
|
||||
HTMLMotionProps<'button'> & {
|
||||
hoverScale?: number;
|
||||
tapScale?: number;
|
||||
}
|
||||
>;
|
||||
|
||||
function Button({
|
||||
hoverScale = 1.05,
|
||||
tapScale = 0.95,
|
||||
asChild = false,
|
||||
...props
|
||||
}: ButtonProps) {
|
||||
const Component = asChild ? Slot : motion.button;
|
||||
|
||||
return (
|
||||
<Component
|
||||
whileTap={{ scale: tapScale }}
|
||||
whileHover={{ scale: hoverScale }}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export { Button, type ButtonProps };
|
||||
@@ -0,0 +1,144 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import {type HTMLMotionProps, motion} from 'motion/react';
|
||||
|
||||
import {getStrictContext} from '@/lib/utils/get-strict-context';
|
||||
import {Slot, type WithAsChild} from '@/components/animate-ui/primitives/animate/slot';
|
||||
|
||||
type Ripple = {
|
||||
id: number;
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
|
||||
type RippleButtonContextType = {
|
||||
ripples: Ripple[];
|
||||
setRipples: (ripples: Ripple[]) => void;
|
||||
};
|
||||
|
||||
const [RippleButtonProvider, useRippleButton] =
|
||||
getStrictContext<RippleButtonContextType>('RippleButtonContext');
|
||||
|
||||
type RippleButtonProps = WithAsChild<
|
||||
HTMLMotionProps<'button'> & {
|
||||
hoverScale?: number;
|
||||
tapScale?: number;
|
||||
}
|
||||
>;
|
||||
|
||||
function RippleButton({
|
||||
ref,
|
||||
onClick,
|
||||
hoverScale = 1,
|
||||
tapScale = 0.95,
|
||||
asChild = false,
|
||||
style,
|
||||
...props
|
||||
}: RippleButtonProps) {
|
||||
const [ripples, setRipples] = React.useState<Ripple[]>([]);
|
||||
const buttonRef = React.useRef<HTMLButtonElement>(null);
|
||||
React.useImperativeHandle(ref, () => buttonRef.current as HTMLButtonElement);
|
||||
|
||||
const createRipple = React.useCallback(
|
||||
(event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
const button = buttonRef.current;
|
||||
if (!button) return;
|
||||
|
||||
const rect = button.getBoundingClientRect();
|
||||
const x = event.clientX - rect.left;
|
||||
const y = event.clientY - rect.top;
|
||||
|
||||
const newRipple: Ripple = {
|
||||
id: Date.now(),
|
||||
x,
|
||||
y,
|
||||
};
|
||||
|
||||
setRipples((prev) => [...prev, newRipple]);
|
||||
|
||||
setTimeout(() => {
|
||||
setRipples((prev) => prev.filter((r) => r.id !== newRipple.id));
|
||||
}, 600);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const handleClick = React.useCallback(
|
||||
(event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
createRipple(event);
|
||||
if (onClick) {
|
||||
onClick(event);
|
||||
}
|
||||
},
|
||||
[createRipple, onClick],
|
||||
);
|
||||
|
||||
const Component = asChild ? Slot : motion.button;
|
||||
|
||||
return (
|
||||
<RippleButtonProvider value={{ ripples, setRipples }}>
|
||||
<Component
|
||||
ref={buttonRef}
|
||||
data-slot="ripple-button"
|
||||
onClick={handleClick}
|
||||
whileTap={{ scale: tapScale }}
|
||||
whileHover={{ scale: hoverScale }}
|
||||
style={{
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
...style,
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
</RippleButtonProvider>
|
||||
);
|
||||
}
|
||||
|
||||
type RippleButtonRipplesProps = WithAsChild<
|
||||
HTMLMotionProps<'span'> & {
|
||||
color?: string;
|
||||
scale?: number;
|
||||
}
|
||||
>;
|
||||
|
||||
function RippleButtonRipples({
|
||||
color = 'var(--ripple-button-ripple-color)',
|
||||
scale = 10,
|
||||
transition = { duration: 0.6, ease: 'easeOut' },
|
||||
asChild = false,
|
||||
style,
|
||||
...props
|
||||
}: RippleButtonRipplesProps) {
|
||||
const { ripples } = useRippleButton();
|
||||
|
||||
const Component = asChild ? Slot : motion.span;
|
||||
|
||||
return ripples.map((ripple) => (
|
||||
<Component
|
||||
key={ripple.id}
|
||||
initial={{ scale: 0, opacity: 0.5 }}
|
||||
animate={{ scale, opacity: 0 }}
|
||||
transition={transition}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
borderRadius: '50%',
|
||||
pointerEvents: 'none',
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
backgroundColor: color,
|
||||
top: ripple.y - 10,
|
||||
left: ripple.x - 10,
|
||||
...style,
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
}
|
||||
|
||||
export {
|
||||
RippleButton,
|
||||
RippleButtonRipples,
|
||||
type RippleButtonProps,
|
||||
type RippleButtonRipplesProps,
|
||||
};
|
||||
Reference in New Issue
Block a user