mirror of
https://github.com/YuzuZensai/Minikura.git
synced 2026-09-13 18:59:25 +00:00
🐛 fix: align dashboard with secured APIs
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
"use client";
|
||||
|
||||
import type { ComponentProps, ReactNode } from "react";
|
||||
import { useRef } from "react";
|
||||
import { cn } from "@/lib/cn";
|
||||
import { gsap, motionDuration, motionEase, prefersReducedMotion, useGSAP } from "@/lib/motion";
|
||||
|
||||
type FadeInProps = ComponentProps<"div"> & {
|
||||
delay?: number;
|
||||
duration?: number;
|
||||
y?: number;
|
||||
x?: number;
|
||||
};
|
||||
|
||||
export function FadeIn({
|
||||
children,
|
||||
className,
|
||||
delay = 0,
|
||||
duration = motionDuration.base,
|
||||
y = 16,
|
||||
x = 0,
|
||||
...props
|
||||
}: FadeInProps) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
useGSAP(
|
||||
() => {
|
||||
const el = ref.current;
|
||||
if (!el) return;
|
||||
if (prefersReducedMotion()) {
|
||||
gsap.set(el, { autoAlpha: 1, x: 0, y: 0 });
|
||||
return;
|
||||
}
|
||||
gsap.fromTo(
|
||||
el,
|
||||
{ autoAlpha: 0, x, y },
|
||||
{ autoAlpha: 1, x: 0, y: 0, delay, duration, ease: motionEase.out }
|
||||
);
|
||||
},
|
||||
{ scope: ref }
|
||||
);
|
||||
|
||||
return (
|
||||
<div ref={ref} className={className} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type StaggerProps = {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
delay?: number;
|
||||
stagger?: number;
|
||||
duration?: number;
|
||||
y?: number;
|
||||
selector?: string;
|
||||
};
|
||||
|
||||
export function Stagger({
|
||||
children,
|
||||
className,
|
||||
delay = 0,
|
||||
stagger = 0.06,
|
||||
duration = motionDuration.base,
|
||||
y = 14,
|
||||
selector = ":scope > *",
|
||||
}: StaggerProps) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
useGSAP(
|
||||
() => {
|
||||
const root = ref.current;
|
||||
if (!root) return;
|
||||
const items = root.querySelectorAll(selector);
|
||||
if (!items.length) return;
|
||||
if (prefersReducedMotion()) {
|
||||
gsap.set(items, { autoAlpha: 1, y: 0 });
|
||||
return;
|
||||
}
|
||||
gsap.fromTo(
|
||||
items,
|
||||
{ autoAlpha: 0, y },
|
||||
{ autoAlpha: 1, y: 0, delay, duration, stagger, ease: motionEase.out }
|
||||
);
|
||||
},
|
||||
{ scope: ref }
|
||||
);
|
||||
|
||||
return (
|
||||
<div ref={ref} className={className}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type HoverLiftProps = {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
y?: number;
|
||||
};
|
||||
|
||||
export function HoverLift({ children, className, y = -3 }: HoverLiftProps) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
useGSAP(
|
||||
() => {
|
||||
const el = ref.current;
|
||||
if (!el || prefersReducedMotion()) return;
|
||||
|
||||
const enter = () => gsap.to(el, { y, duration: motionDuration.fast, ease: motionEase.out });
|
||||
const leave = () =>
|
||||
gsap.to(el, { y: 0, duration: motionDuration.fast, ease: motionEase.out });
|
||||
|
||||
el.addEventListener("pointerenter", enter);
|
||||
el.addEventListener("pointerleave", leave);
|
||||
return () => {
|
||||
el.removeEventListener("pointerenter", enter);
|
||||
el.removeEventListener("pointerleave", leave);
|
||||
};
|
||||
},
|
||||
{ scope: ref }
|
||||
);
|
||||
|
||||
return (
|
||||
<div ref={ref} className={cn("will-change-transform", className)}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function useShake(trigger: string | null | undefined) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
useGSAP(
|
||||
() => {
|
||||
const el = ref.current;
|
||||
if (!el || !trigger || prefersReducedMotion()) return;
|
||||
gsap.fromTo(el, { x: -6 }, { x: 0, duration: 0.42, ease: "elastic.out(1, 0.4)" });
|
||||
},
|
||||
{ dependencies: [trigger], scope: ref }
|
||||
);
|
||||
|
||||
return ref;
|
||||
}
|
||||
Reference in New Issue
Block a user