107 lines
3.2 KiB
TypeScript
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 };
|