w-funnel/src/components/widgets/BottomActionButton/BottomActionButton.tsx
dev.daminik00 653b15e09c add blur
2025-10-21 21:20:35 +02:00

107 lines
3.2 KiB
TypeScript

"use client";
import { cn } from "@/lib/utils";
import React, {
forwardRef,
useEffect,
useImperativeHandle,
useRef,
} from "react";
import { GradientBlur } from "../GradientBlur/GradientBlur";
import { ActionButton } from "@/components/ui/ActionButton/ActionButton";
export interface BottomActionButtonProps extends React.ComponentProps<"div"> {
actionButtonProps?: React.ComponentProps<typeof ActionButton>;
/** Контент над кнопкой (например подсказка) */
childrenAboveButton?: React.ReactNode;
/** Контент под кнопкой (например дисклеймер) */
childrenUnderButton?: React.ReactNode;
/** Управление блюром подложки */
showGradientBlur?: boolean;
/** Синхронизировать CSS-переменную --bottom-action-button-height на <html> */
syncCssVar?: boolean;
gradientBlurProps?: React.ComponentProps<typeof GradientBlur>;
}
const BottomActionButton = forwardRef<HTMLDivElement, BottomActionButtonProps>(
function BottomActionButton(
{
actionButtonProps,
childrenAboveButton,
childrenUnderButton,
showGradientBlur = true,
className,
syncCssVar = true,
gradientBlurProps,
...props
},
ref
) {
const innerRef = useRef<HTMLDivElement>(null);
useImperativeHandle(ref, () => innerRef.current as HTMLDivElement, []);
const hasButton = Boolean(actionButtonProps);
const hasExtra =
Boolean(childrenAboveButton) || Boolean(childrenUnderButton);
const hasContent = hasButton || hasExtra;
const shouldRender = hasContent || showGradientBlur;
useEffect(() => {
if (!syncCssVar || typeof window === "undefined" || !hasContent) return;
const el = innerRef.current;
if (!el) return;
const setVar = () => {
document.documentElement.style.setProperty(
"--bottom-action-button-height",
`${el.offsetHeight}px`
);
};
setVar();
if ("ResizeObserver" in window) {
const ro = new ResizeObserver(setVar);
ro.observe(el);
return () => ro.disconnect();
} else {
const onResize = () => setVar();
globalThis.addEventListener("resize", onResize);
return () => globalThis.removeEventListener("resize", onResize);
}
}, [syncCssVar, hasContent]);
// Ничего не рендерим, если нет контента И не нужен blur
if (!shouldRender) return null;
return (
<div
ref={innerRef}
className={cn(
"fixed bottom-0 left-1/2 -translate-x-1/2 w-full z-10",
className
)}
{...props}
>
<GradientBlur
isActiveBlur={showGradientBlur}
{...gradientBlurProps}
className={cn("p-6 pt-11", gradientBlurProps?.className)}
>
{childrenAboveButton && (
<div className="w-full flex justify-center">
{childrenAboveButton}
</div>
)}
{hasButton ? <ActionButton {...actionButtonProps} /> : null}
{childrenUnderButton}
</GradientBlur>
</div>
);
}
);
export { BottomActionButton };