imnya.ng/src/components/ui/banner.tsx
imnyang aa721322da
chore: update dependencies and improve UI components
- Updated dependencies in bun.lock and package.json to specific versions for better stability.
- Added a new Contact component to display contact methods with tooltips.
- Introduced a Banner component for notifications with customizable actions.
- Enhanced the Dialog, Checkbox, Button, Input, and Label components for better usability and styling.
- Integrated react-snowfall for a snow effect in the main page.
- Added a Discord SVG icon to the project.
2025-12-19 17:21:23 +09:00

123 lines
No EOL
3.1 KiB
TypeScript

"use client"
import * as React from "react"
import { X } from "lucide-react"
import { cn } from "@/lib/utils"
function Grid({
cellSize = 12,
strokeWidth = 1,
patternOffset = [0, 0],
className,
}: {
cellSize?: number
strokeWidth?: number
patternOffset?: [number, number]
className?: string
}) {
const id = React.useId()
return (
<svg
className={cn(
"pointer-events-none absolute inset-0 text-black/10",
className,
)}
width="100%"
height="100%"
>
<defs>
<pattern
id={`grid-${id}`}
x={patternOffset[0] - 1}
y={patternOffset[1] - 1}
width={cellSize}
height={cellSize}
patternUnits="userSpaceOnUse"
>
<path
d={`M ${cellSize} 0 L 0 0 0 ${cellSize}`}
fill="transparent"
stroke="currentColor"
strokeWidth={strokeWidth}
/>
</pattern>
</defs>
<rect fill={`url(#grid-${id})`} width="100%" height="100%" />
</svg>
)
}
interface BannerProps {
show: boolean
onHide: () => void
icon?: React.ReactNode
title: React.ReactNode
action: {
label: string
onClick: () => void
}
learnMoreUrl?: string
}
export function Banner({
show,
onHide,
icon,
title,
action,
learnMoreUrl,
}: BannerProps) {
if (!show) return null
return (
<div className="relative isolate flex flex-col justify-between gap-3 overflow-hidden rounded-lg border border-green-600/15 bg-gradient-to-r from-lime-100/80 to-emerald-100/80 py-3 pl-4 pr-12 sm:flex-row sm:items-center sm:py-2">
<Grid
cellSize={13}
patternOffset={[0, -1]}
className="text-black/30 mix-blend-overlay [mask-image:linear-gradient(to_right,black,transparent)] md:[mask-image:linear-gradient(to_right,black_60%,transparent)]"
/>
<div className="flex items-center gap-3">
{icon && (
<div className="hidden rounded-full border border-green-600/50 bg-white/50 p-1 shadow-[inset_0_0_1px_1px_#fff] sm:block">
{icon}
</div>
)}
<p className="text-sm text-gray-900">
{title}
{learnMoreUrl && (
<>
{" "}
<a
href={learnMoreUrl}
target="_blank"
className="text-gray-700 underline transition-colors hover:text-black"
>
Learn more
</a>
</>
)}
</p>
</div>
<div className="flex items-center sm:-my-1">
<button
type="button"
className="whitespace-nowrap rounded-md border border-green-700/50 px-3 py-1 text-sm text-gray-800 transition-colors hover:bg-green-500/10"
onClick={action.onClick}
>
{action.label}
</button>
</div>
<button
type="button"
className="absolute inset-y-0 right-2.5 p-1 text-sm text-green-700 underline transition-colors hover:text-green-900"
onClick={onHide}
>
<X className="h-[18px] w-[18px]" />
</button>
</div>
)
}