Initial commit

This commit is contained in:
dk
2025-12-21 14:11:02 +02:00
commit 7774d69c2d
308 changed files with 64898 additions and 0 deletions

View File

@@ -0,0 +1,81 @@
"use client";
import { useRef, memo } from "react";
import { useDirectionalHover } from "./useDirectionalHover";
import { useButtonClick } from "../useButtonClick";
import { cls } from "@/lib/utils";
import "./DirectionalButton.css";
export interface ButtonDirectionalHoverProps {
text: string;
onClick?: () => void;
href?: string;
className?: string;
bgClassName?: string;
textClassName?: string;
circleClassName?: string;
disabled?: boolean;
ariaLabel?: string;
type?: "button" | "submit" | "reset";
}
const ButtonDirectionalHover = ({
text,
onClick,
href,
className = "",
bgClassName = "",
textClassName = "",
circleClassName = "",
disabled = false,
ariaLabel,
type = "button",
}: ButtonDirectionalHoverProps) => {
const buttonRef = useRef<HTMLButtonElement>(null);
const handleClick = useButtonClick(href, onClick);
useDirectionalHover(buttonRef);
return (
<button
ref={buttonRef}
type={type}
onClick={handleClick}
disabled={disabled}
aria-label={ariaLabel || text}
data-href={href}
className={cls(
"directional-button relative cursor-pointer flex items-center justify-center bg-transparent border-none leading-none no-underline h-9 px-6 min-w-0 w-fit max-w-full rounded-theme text-background",
"disabled:cursor-not-allowed disabled:opacity-50",
className
)}
>
<div
className={cls(
"directional-button-bg absolute inset-0 rounded-theme primary-button",
bgClassName
)}
></div>
<div className="directional-button-circle-wrap">
<div
className={cls(
"directional-button-circle bg-accent",
circleClassName
)}
></div>
</div>
<span
className={cls(
"directional-button-text relative text-sm inline-block overflow-hidden truncate whitespace-nowrap",
textClassName
)}
>
{text}
</span>
</button>
);
};
ButtonDirectionalHover.displayName = "ButtonDirectionalHover";
export default memo(ButtonDirectionalHover);

View File

@@ -0,0 +1,37 @@
.directional-button-circle-wrap {
border-radius: inherit;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
overflow: hidden;
}
.directional-button-circle {
pointer-events: none;
border-radius: 50%;
width: 100%;
display: block;
position: absolute;
top: 50%;
left: 50%;
transition: transform 0.7s cubic-bezier(0.625, 0.05, 0, 1);
transform: translate(-50%, -50%) scale(0) rotate(0.001deg);
}
.directional-button-circle::before {
content: "";
display: block;
padding-top: 100%;
}
.directional-button:hover .directional-button-circle {
transform: translate(-50%, -50%) scale(1) rotate(0.001deg);
}
@media (max-width: 768px) {
.directional-button:hover .directional-button-circle {
transform: translate(-50%, -50%) scale(0) rotate(0.001deg);
}
}

View File

@@ -0,0 +1,48 @@
import { useEffect, useCallback, RefObject } from "react";
export const useDirectionalHover = (
buttonRef: RefObject<HTMLButtonElement | null>,
circleSelector: string = ".directional-button-circle"
) => {
const handleHover = useCallback(
(event: MouseEvent) => {
const button = buttonRef.current;
if (!button) return;
const buttonRect = button.getBoundingClientRect();
const buttonWidth = buttonRect.width;
const buttonHeight = buttonRect.height;
const buttonCenterX = buttonRect.left + buttonWidth / 2;
const mouseX = event.clientX;
const mouseY = event.clientY;
const offsetXFromLeft = ((mouseX - buttonRect.left) / buttonWidth) * 100;
const offsetYFromTop = ((mouseY - buttonRect.top) / buttonHeight) * 100;
let offsetXFromCenter = ((mouseX - buttonCenterX) / (buttonWidth / 2)) * 50;
offsetXFromCenter = Math.abs(offsetXFromCenter);
const circle = button.querySelector(circleSelector) as HTMLElement;
if (circle) {
circle.style.left = `${offsetXFromLeft.toFixed(1)}%`;
circle.style.top = `${offsetYFromTop.toFixed(1)}%`;
circle.style.width = `${115 + offsetXFromCenter * 2}%`;
}
},
[buttonRef, circleSelector]
);
useEffect(() => {
const button = buttonRef.current;
if (!button) return;
button.addEventListener("mouseenter", handleHover);
button.addEventListener("mouseleave", handleHover);
return () => {
button.removeEventListener("mouseenter", handleHover);
button.removeEventListener("mouseleave", handleHover);
};
}, [buttonRef, handleHover]);
};