102 lines
3.6 KiB
TypeScript
102 lines
3.6 KiB
TypeScript
'use client';
|
|
|
|
import * as React from 'react';
|
|
|
|
import { cn } from '@workspace/ui/lib/utils';
|
|
|
|
type HexagonBackgroundProps = React.ComponentProps<'div'> & {
|
|
children?: React.ReactNode;
|
|
hexagonProps?: React.ComponentProps<'div'>;
|
|
hexagonSize?: number; // value greater than 50
|
|
hexagonMargin?: number;
|
|
};
|
|
|
|
function HexagonBackground({
|
|
className,
|
|
children,
|
|
hexagonProps,
|
|
hexagonSize = 75,
|
|
hexagonMargin = 3,
|
|
...props
|
|
}: HexagonBackgroundProps) {
|
|
const hexagonWidth = hexagonSize;
|
|
const hexagonHeight = hexagonSize * 1.1;
|
|
const rowSpacing = hexagonSize * 0.8;
|
|
const baseMarginTop = -36 - 0.275 * (hexagonSize - 100);
|
|
const computedMarginTop = baseMarginTop + hexagonMargin;
|
|
const oddRowMarginLeft = -(hexagonSize / 2);
|
|
const evenRowMarginLeft = hexagonMargin / 2;
|
|
|
|
const [gridDimensions, setGridDimensions] = React.useState({
|
|
rows: 0,
|
|
columns: 0,
|
|
});
|
|
|
|
const updateGridDimensions = React.useCallback(() => {
|
|
const rows = Math.ceil(window.innerHeight / rowSpacing);
|
|
const columns = Math.ceil(window.innerWidth / hexagonWidth) + 1;
|
|
setGridDimensions({ rows, columns });
|
|
}, [rowSpacing, hexagonWidth]);
|
|
|
|
React.useEffect(() => {
|
|
updateGridDimensions();
|
|
window.addEventListener('resize', updateGridDimensions);
|
|
return () => window.removeEventListener('resize', updateGridDimensions);
|
|
}, [updateGridDimensions]);
|
|
|
|
return (
|
|
<div
|
|
data-slot="hexagon-background"
|
|
className={cn(
|
|
'relative size-full overflow-hidden dark:bg-neutral-900 bg-neutral-100',
|
|
className,
|
|
)}
|
|
{...props}
|
|
>
|
|
<style>{`:root { --hexagon-margin: ${hexagonMargin}px; }`}</style>
|
|
<div className="absolute top-0 -left-0 size-full overflow-hidden">
|
|
{Array.from({ length: gridDimensions.rows }).map((_, rowIndex) => (
|
|
<div
|
|
key={`row-${rowIndex}`}
|
|
style={{
|
|
marginTop: computedMarginTop,
|
|
marginLeft:
|
|
((rowIndex + 1) % 2 === 0
|
|
? evenRowMarginLeft
|
|
: oddRowMarginLeft) - 10,
|
|
}}
|
|
className="inline-flex"
|
|
>
|
|
{Array.from({ length: gridDimensions.columns }).map(
|
|
(_, colIndex) => (
|
|
<div
|
|
key={`hexagon-${rowIndex}-${colIndex}`}
|
|
{...hexagonProps}
|
|
style={{
|
|
width: hexagonWidth,
|
|
height: hexagonHeight,
|
|
marginLeft: hexagonMargin,
|
|
...hexagonProps?.style,
|
|
}}
|
|
className={cn(
|
|
'relative',
|
|
'[clip-path:polygon(50%_0%,_100%_25%,_100%_75%,_50%_100%,_0%_75%,_0%_25%)]',
|
|
"before:content-[''] before:absolute before:top-0 before:left-0 before:w-full before:h-full dark:before:bg-neutral-950 before:bg-white before:opacity-100 before:transition-all before:duration-1000",
|
|
"after:content-[''] after:absolute after:inset-[var(--hexagon-margin)] dark:after:bg-neutral-950 after:bg-white",
|
|
'after:[clip-path:polygon(50%_0%,_100%_25%,_100%_75%,_50%_100%,_0%_75%,_0%_25%)]',
|
|
'hover:before:bg-neutral-200 dark:hover:before:bg-neutral-800 hover:before:opacity-100 hover:before:duration-0 dark:hover:after:bg-neutral-900 hover:after:bg-neutral-100 hover:after:opacity-100 hover:after:duration-0',
|
|
hexagonProps?.className,
|
|
)}
|
|
/>
|
|
),
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export { HexagonBackground, type HexagonBackgroundProps };
|