Base Widget

PreviousNext

base widgets component

Docs
einuiui

Preview

Loading preview…
registry/widgets/base-widget.tsx
"use client"

import * as React from "react"
import { motion, type HTMLMotionProps, type Variants } from "framer-motion"
import { cn } from "@/lib/utils"

interface GlassWidgetBaseProps extends Omit<HTMLMotionProps<"div">, "children"> {
  children: React.ReactNode
  size?: "sm" | "md" | "lg" | "xl"
  glowEffect?: boolean
  glowColor?: "cyan" | "purple" | "blue" | "pink" | "green" | "amber" | "red"
  hoverScale?: boolean
  interactive?: boolean
}

const sizeClasses = {
  sm: "p-3",
  md: "p-4",
  lg: "p-5",
  xl: "p-6",
}

const glowColors = {
  cyan: "from-cyan-500/30 via-blue-500/30 to-purple-500/30",
  purple: "from-purple-500/30 via-pink-500/30 to-purple-500/30",
  blue: "from-blue-500/30 via-indigo-500/30 to-blue-500/30",
  pink: "from-pink-500/30 via-rose-500/30 to-pink-500/30",
  green: "from-emerald-500/30 via-teal-500/30 to-emerald-500/30",
  amber: "from-amber-500/30 via-orange-500/30 to-amber-500/30",
  red: "from-red-500/30 via-rose-500/30 to-red-500/30",
}

const widgetVariants = {
  hidden: { opacity: 0, y: 20, scale: 0.95 },
  visible: {
    opacity: 1,
    y: 0,
    scale: 1,
    transition: {
      type: "spring",
      visualDuration: 0.4,
      bounce: 0.2,
    },
  },
  hover: {
    y: -2,
    transition: {
      type: "spring",
      visualDuration: 0.3,
      bounce: 0.4,
    },
  },
} as const

const glowVariants: Variants = {
  initial: { opacity: 0.4, scale: 0.98 },
  animate: {
    opacity: [0.4, 0.6, 0.4] as number[],
    scale: [0.98, 1, 0.98] as number[],
    transition: {
      duration: 4,
      repeat: Number.POSITIVE_INFINITY,
      ease: "easeInOut",
    },
  },
  hover: {
    opacity: 0.8,
    scale: 1.02,
    transition: {
      type: "spring",
      visualDuration: 0.3,
      bounce: 0.3,
    },
  },
}

const GlassWidgetBase = React.forwardRef<HTMLDivElement, GlassWidgetBaseProps>(
  (
    {
      className,
      children,
      size = "md",
      glowEffect = true,
      glowColor = "cyan",
      hoverScale = true,
      interactive = true,
      ...props
    },
    ref,
  ) => {
    return (
      <motion.div
        className="relative"
        initial="hidden"
        animate="visible"
        whileHover={interactive && hoverScale ? "hover" : undefined}
        variants={widgetVariants}
      >
        {/* Glow effect */}
        {glowEffect && (
          <motion.div
            className={cn("absolute -inset-0.5 rounded-2xl bg-linear-to-r blur-xl", glowColors[glowColor])}
            variants={glowVariants}
            initial="initial"
            animate="animate"
            whileHover={interactive ? "hover" : undefined}
            aria-hidden="true"
          />
        )}

        {/* Widget container */}
        <motion.div
          ref={ref}
          className={cn(
            "relative rounded-2xl border border-white/20",
            "bg-white/10 backdrop-blur-xl",
            "shadow-[0_8px_32px_rgba(0,0,0,0.37)]",
            // Inner highlight linear
            "before:absolute before:inset-0 before:rounded-2xl",
            "before:bg-linear-to-b before:from-white/20 before:to-transparent before:pointer-events-none",
            // Inner shadow for depth
            "after:absolute after:inset-px after:rounded-[calc(1rem-1px)]",
            "after:shadow-[inset_0_1px_1px_rgba(255,255,255,0.1)] after:pointer-events-none",
            sizeClasses[size],
            className,
          )}
          role="article"
          {...props}
        >
          <div className="relative z-10">{children}</div>
        </motion.div>
      </motion.div>
    )
  },
)
GlassWidgetBase.displayName = "GlassWidgetBase"

export { GlassWidgetBase }
export type { GlassWidgetBaseProps }

Installation

npx shadcn@latest add @einui/base-widget

Usage

import { BaseWidget } from "@/components/ui/base-widget"
<BaseWidget />