Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions public/r/animated-list.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "animated-list",
"type": "registry:component",
"dependencies": [
"clsx",
"tailwind-merge",
Expand All @@ -26,5 +25,6 @@
"content": "import { cn } from \"@/lib/utils\";\ninterface Item {\n name: string;\n description: string;\n icon: string;\n color: string;\n time: string;\n}\n\nexport const Notification = ({\n name,\n description,\n icon,\n color,\n time,\n}: Item) => {\n return (\n <figure\n className={cn(\n \"relative mx-auto min-h-fit w-full max-w-[400px] cursor-pointer overflow-hidden rounded-2xl p-4\",\n // animation styles\n \"transition-all duration-200 ease-in-out hover:scale-[103%]\",\n // light styles\n \"bg-white [box-shadow:0_0_0_1px_rgba(0,0,0,.03),0_2px_4px_rgba(0,0,0,.05),0_12px_24px_rgba(0,0,0,.05)]\",\n // dark styles\n \"transform-gpu dark:bg-transparent dark:backdrop-blur-md dark:[border:1px_solid_rgba(255,255,255,.1)] dark:[box-shadow:0_-20px_80px_-20px_#ffffff1f_inset]\",\n )}\n >\n <div className=\"flex flex-row items-center gap-3\">\n <div\n className=\"flex size-10 items-center justify-center rounded-2xl\"\n style={{\n backgroundColor: color,\n }}\n >\n <span className=\"text-lg\">{icon}</span>\n </div>\n <div className=\"flex flex-col overflow-hidden\">\n <figcaption className=\"flex flex-row items-center whitespace-pre text-lg font-medium dark:text-white \">\n <span className=\"text-sm sm:text-lg\">{name}</span>\n <span className=\"mx-1\">·</span>\n <span className=\"text-xs text-gray-500\">{time}</span>\n </figcaption>\n <p className=\"text-sm font-normal dark:text-white/60\">\n {description}\n </p>\n </div>\n </div>\n </figure>\n );\n};\n",
"type": "registry:component"
}
]
],
"type": "registry:component"
}
4 changes: 2 additions & 2 deletions public/r/animated-pricing.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "animated-pricing",
"type": "registry:component",
"dependencies": [
"clsx",
"tailwind-merge"
Expand Down Expand Up @@ -29,5 +28,6 @@
"content": "import React, { ReactNode, useState, useMemo, MouseEvent, CSSProperties } from 'react';\n\ninterface RippleState {\n key: number;\n x: number;\n y: number;\n size: number;\n color: string;\n}\n\ninterface RippleButtonProps {\n children: ReactNode;\n onClick?: (event: MouseEvent<HTMLButtonElement>) => void;\n className?: string;\n disabled?: boolean;\n variant?: 'default' | 'hover' | 'ghost' | 'hoverborder';\n rippleColor?: string; // User override for the JS click ripple color\n rippleDuration?: number; // Duration for the JS click ripple (all variants)\n\n // For 'hover' variant\n hoverBaseColor?: string;\n hoverRippleColor?: string;\n\n // For 'hoverborder' variant\n hoverBorderEffectColor?: string; // Color of the visual effect forming the border\n hoverBorderEffectThickness?: string; // Thickness of the border effect (e.g., \"0.3em\", \"2px\")\n}\n\nconst hexToRgba = (hex: string, alpha: number): string => {\n let hexValue = hex.startsWith('#') ? hex.slice(1) : hex;\n if (hexValue.length === 3) {\n hexValue = hexValue.split('').map(char => char + char).join('');\n }\n const r = parseInt(hexValue.slice(0, 2), 16);\n const g = parseInt(hexValue.slice(2, 4), 16);\n const b = parseInt(hexValue.slice(4, 6), 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n};\n\nconst GRID_HOVER_NUM_COLS = 36;\nconst GRID_HOVER_NUM_ROWS = 12;\nconst GRID_HOVER_TOTAL_CELLS = GRID_HOVER_NUM_COLS * GRID_HOVER_NUM_ROWS;\nconst GRID_HOVER_RIPPLE_EFFECT_SIZE = \"18.973665961em\";\n\nconst JS_RIPPLE_KEYFRAMES = `\n @keyframes js-ripple-animation {\n 0% { transform: scale(0); opacity: 1; }\n 100% { transform: scale(1); opacity: 0; }\n }\n .animate-js-ripple-effect {\n animation: js-ripple-animation var(--ripple-duration) ease-out forwards;\n }\n`;\n\nconst RippleButton: React.FC<RippleButtonProps> = ({\n children,\n onClick,\n className = '',\n disabled = false,\n variant = 'default',\n rippleColor: userProvidedRippleColor,\n rippleDuration = 600,\n hoverBaseColor = '#6996e2',\n hoverRippleColor: customHoverRippleColor,\n hoverBorderEffectColor = '#6996e277',\n hoverBorderEffectThickness = '0.3em',\n}) => {\n const [jsRipples, setJsRipples] = useState<RippleState[]>([]);\n\n const determinedJsRippleColor = useMemo(() => {\n if (userProvidedRippleColor) {\n return userProvidedRippleColor;\n }\n return 'var(--button-ripple-color, rgba(0, 0, 0, 0.1))';\n }, [userProvidedRippleColor]);\n\n const dynamicGridHoverStyles = useMemo(() => {\n let nthChildHoverRules = '';\n const cellDim = 0.25;\n const initialTopOffset = 0.125;\n const initialLeftOffset = 0.1875;\n\n // Standardized hover transition duration for width and height\n const hoverEffectDuration = '0.9s'; // CHANGED: Standardized to 0.9s\n\n for (let r = 0; r < GRID_HOVER_NUM_ROWS; r++) {\n for (let c = 0; c < GRID_HOVER_NUM_COLS; c++) {\n const childIndex = r * GRID_HOVER_NUM_COLS + c + 1;\n const topPos = initialTopOffset + r * cellDim;\n const leftPos = initialLeftOffset + c * cellDim;\n\n if (variant === 'hover') {\n nthChildHoverRules += `\n .hover-variant-grid-cell:nth-child(${childIndex}):hover ~ .hover-variant-visual-ripple {\n top: ${topPos}em; left: ${leftPos}em;\n transition: width ${hoverEffectDuration} ease, height ${hoverEffectDuration} ease, top 0s linear, left 0s linear;\n }`;\n } else if (variant === 'hoverborder') {\n nthChildHoverRules += `\n .hoverborder-variant-grid-cell:nth-child(${childIndex}):hover ~ .hoverborder-variant-visual-ripple {\n top: ${topPos}em; left: ${leftPos}em;\n transition: width ${hoverEffectDuration} ease-out, height ${hoverEffectDuration} ease-out, top 0s linear, left 0s linear;\n }`; // Using ease-out for hoverborder as it was before, just changed duration\n }\n }\n }\n\n if (variant === 'hover') {\n const actualHoverRippleColor = customHoverRippleColor\n ? customHoverRippleColor\n : hexToRgba(hoverBaseColor, 0.466);\n return `\n .hover-variant-visual-ripple {\n background-color: ${actualHoverRippleColor};\n transition: width ${hoverEffectDuration} ease, height ${hoverEffectDuration} ease, top 99999s linear, left 99999s linear;\n }\n .hover-variant-grid-cell:hover ~ .hover-variant-visual-ripple {\n width: ${GRID_HOVER_RIPPLE_EFFECT_SIZE}; height: ${GRID_HOVER_RIPPLE_EFFECT_SIZE};\n }\n ${nthChildHoverRules}\n `;\n } else if (variant === 'hoverborder') {\n return `\n .hoverborder-variant-ripple-container {\n padding: ${hoverBorderEffectThickness};\n mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);\n mask-composite: exclude;\n -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);\n -webkit-mask-composite: xor;\n }\n .hoverborder-variant-visual-ripple {\n background-color: ${hoverBorderEffectColor};\n /* Ensure the base transition also uses the standardized duration for width/height */\n transition: width ${hoverEffectDuration} ease-out, height ${hoverEffectDuration} ease-out, top 99999s linear, left 9999s linear;\n }\n .hoverborder-variant-grid-cell:hover ~ .hoverborder-variant-visual-ripple {\n width: ${GRID_HOVER_RIPPLE_EFFECT_SIZE}; height: ${GRID_HOVER_RIPPLE_EFFECT_SIZE};\n }\n ${nthChildHoverRules}\n `;\n }\n return '';\n }, [variant, hoverBaseColor, customHoverRippleColor, hoverBorderEffectColor, hoverBorderEffectThickness]);\n\n const createJsRipple = (event: MouseEvent<HTMLButtonElement>) => {\n const button = event.currentTarget;\n const rect = button.getBoundingClientRect();\n const size = Math.max(rect.width, rect.height) * 2;\n const x = event.clientX - rect.left - size / 2;\n const y = event.clientY - rect.top - size / 2;\n const newRipple: RippleState = { key: Date.now(), x, y, size, color: determinedJsRippleColor };\n setJsRipples(prev => [...prev, newRipple]);\n setTimeout(() => {\n setJsRipples(currentRipples => currentRipples.filter(r => r.key !== newRipple.key));\n }, rippleDuration);\n };\n\n const handleButtonClick = (event: MouseEvent<HTMLButtonElement>) => {\n if (!disabled) {\n createJsRipple(event);\n if (onClick) onClick(event);\n }\n };\n\n const jsRippleElements = (\n <div className=\"absolute inset-0 pointer-events-none z-[5]\">\n {jsRipples.map(ripple => (\n <span\n key={ripple.key}\n className=\"absolute rounded-full animate-js-ripple-effect\"\n style={{\n left: ripple.x, top: ripple.y, width: ripple.size, height: ripple.size,\n backgroundColor: ripple.color,\n ['--ripple-duration' as string]: `${rippleDuration}ms`,\n } as CSSProperties}\n />\n ))}\n </div>\n );\n\n if (variant === 'hover') {\n const hoverButtonFinalClassName = [\n \"relative\", \"rounded-lg\", \"text-lg\", \"px-4\", \"py-2\",\n \"border-none\", \"bg-transparent\", \"isolate\", \"overflow-hidden\", \"cursor-pointer\",\n disabled ? \"opacity-50 cursor-not-allowed pointer-events-none\" : \"\",\n className,\n ].filter(Boolean).join(\" \");\n return (\n <>\n <style dangerouslySetInnerHTML={{ __html: JS_RIPPLE_KEYFRAMES }} />\n <style dangerouslySetInnerHTML={{ __html: dynamicGridHoverStyles }} />\n <button className={hoverButtonFinalClassName} onClick={handleButtonClick} disabled={disabled}>\n <span className=\"relative z-[10] pointer-events-none\">{children}</span>\n {jsRippleElements}\n <div\n className=\"hover-variant-grid-container absolute inset-0 grid overflow-hidden pointer-events-none z-[0]\"\n style={{ gridTemplateColumns: `repeat(${GRID_HOVER_NUM_COLS}, 0.25em)` }}\n >\n {Array.from({ length: GRID_HOVER_TOTAL_CELLS }, (_, index) => (\n <span key={index} className=\"hover-variant-grid-cell relative flex justify-center items-center pointer-events-auto\" />\n ))}\n <div className=\"hover-variant-visual-ripple pointer-events-none absolute w-0 h-0 rounded-full transform -translate-x-1/2 -translate-y-1/2 top-0 left-0 z-[-1]\" />\n </div>\n </button>\n </>\n );\n }\n\n if (variant === 'hoverborder') {\n const hoverBorderButtonFinalClassName = [\n \"relative\", \"rounded-lg\", \"overflow-hidden\", \"text-lg\", \"px-4\", \"py-2\",\n \"border-none\", \"bg-transparent\", \"isolate\", \"cursor-pointer\",\n disabled ? \"opacity-50 cursor-not-allowed pointer-events-none\" : \"\",\n className,\n ].filter(Boolean).join(\" \");\n\n return (\n <>\n <style dangerouslySetInnerHTML={{ __html: JS_RIPPLE_KEYFRAMES }} />\n <style dangerouslySetInnerHTML={{ __html: dynamicGridHoverStyles }} />\n <button\n className={hoverBorderButtonFinalClassName}\n onClick={handleButtonClick}\n disabled={disabled}\n >\n <span className=\"relative z-[10] pointer-events-none\">{children}</span>\n {jsRippleElements}\n <div\n className=\"hoverborder-variant-ripple-container absolute inset-0 grid rounded-[0.8em] overflow-hidden pointer-events-none z-[0]\"\n style={{ gridTemplateColumns: `repeat(${GRID_HOVER_NUM_COLS}, 0.25em)` }}\n >\n {Array.from({ length: GRID_HOVER_TOTAL_CELLS }, (_, index) => (\n <span\n key={index}\n className=\"hoverborder-variant-grid-cell relative flex justify-center items-center pointer-events-auto\"\n />\n ))}\n <div className=\"hoverborder-variant-visual-ripple pointer-events-none absolute w-0 h-0 rounded-full transform -translate-x-1/2 -translate-y-1/2 top-0 left-0 z-[-1]\" />\n </div>\n </button>\n </>\n );\n }\n\n if (variant === 'ghost') {\n const ghostButtonFinalClassName = [\n \"relative\", \"border-none\", \"bg-transparent\", \"isolate\", \"overflow-hidden\", \"cursor-pointer\",\n \"px-4\", \"py-2\", \"rounded-lg\", \"text-lg\",\n disabled ? \"opacity-50 cursor-not-allowed pointer-events-none\" : \"\",\n className,\n ].filter(Boolean).join(\" \");\n return (\n <>\n <style dangerouslySetInnerHTML={{ __html: JS_RIPPLE_KEYFRAMES }} />\n <button className={ghostButtonFinalClassName} onClick={handleButtonClick} disabled={disabled}>\n <span className=\"relative z-10 pointer-events-none\">{children}</span>\n {jsRippleElements}\n </button>\n </>\n );\n }\n\n // Default variant\n const baseClasses = \"relative border-none overflow-hidden isolate transition-all duration-200 cursor-pointer px-4 py-2 bg-blue-600 hover:opacity-90 text-white rounded-lg\";\n const disabledClasses = disabled ? \"opacity-50 cursor-not-allowed\" : \"\";\n const buttonClasses = `${baseClasses} ${disabledClasses} ${className}`;\n return (\n <>\n <style dangerouslySetInnerHTML={{ __html: JS_RIPPLE_KEYFRAMES }} />\n <button className={buttonClasses} onClick={handleButtonClick} disabled={disabled}>\n <span className=\"relative z-[1] pointer-events-none\">{children}</span>\n {jsRippleElements}\n </button>\n </>\n );\n};\n\nexport { RippleButton };",
"type": "registry:component"
}
]
],
"type": "registry:component"
}
4 changes: 2 additions & 2 deletions public/r/banner-close.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "banner-close",
"type": "registry:component",
"dependencies": [
"clsx",
"tailwind-merge",
Expand All @@ -22,5 +21,6 @@
"content": "\"use client\";\nimport { type HTMLAttributes, useEffect, useState } from \"react\";\nimport { X } from \"lucide-react\";\nimport { buttonVariants } from \"@/components/ui/button\";\nimport { cn } from \"@/lib/utils\";\n\ntype BannerVariant = \"rainbow\" | \"normal\";\n\nexport function Banner({\n id,\n xColor,\n variant = \"normal\",\n changeLayout = true,\n height = \"3rem\",\n rainbowColors = [\n \"rgba(0,149,255,0.56)\",\n \"rgba(231,77,255,0.77)\",\n \"rgba(255,0,0,0.73)\",\n \"rgba(131,255,166,0.66)\",\n ],\n ...props\n}: HTMLAttributes<HTMLDivElement> & {\n /**\n * @defaultValue 3rem\n */\n height?: string;\n\n xColor?: string;\n\n /**\n * @defaultValue 'normal'\n */\n variant?: BannerVariant;\n\n /**\n * For rainbow variant only, customise the colors\n */\n rainbowColors?: string[];\n\n /**\n * Change Fumadocs layout styles\n *\n * @defaultValue true\n */\n changeLayout?: boolean;\n}) {\n const [open, setOpen] = useState(true);\n const globalKey = id ? `nd-banner-${id}` : null;\n\n useEffect(() => {\n if (globalKey) setOpen(localStorage.getItem(globalKey) !== \"true\");\n }, [globalKey]);\n\n if (!open) return null;\n\n return (\n <div\n id={id}\n {...props}\n className={cn(\n \"sticky top-0 z-40 flex flex-row items-center justify-center px-4 text-center text-sm font-medium\",\n variant === \"normal\" && \"bg-fd-secondary\",\n variant === \"rainbow\" && \"bg-fd-background\",\n !open && \"hidden\",\n props.className,\n )}\n style={{\n height,\n }}\n >\n {changeLayout && open ? (\n <style>\n {globalKey\n ? `:root:not(.${globalKey}) { --fd-banner-height: ${height}; }`\n : `:root { --fd-banner-height: ${height}; }`}\n </style>\n ) : null}\n {globalKey ? (\n <style>{`.${globalKey} #${id} { display: none; }`}</style>\n ) : null}\n {globalKey ? (\n <script\n dangerouslySetInnerHTML={{\n __html: `if (localStorage.getItem('${globalKey}') === 'true') document.documentElement.classList.add('${globalKey}');`,\n }}\n />\n ) : null}\n\n {variant === \"rainbow\"\n ? flow({\n colors: rainbowColors,\n })\n : null}\n {props.children}\n {id ? (\n <button\n type=\"button\"\n aria-label=\"Close Banner\"\n onClick={() => {\n setOpen(false);\n if (globalKey) {\n localStorage.setItem(globalKey, \"true\");\n window.dispatchEvent(new Event(\"banner-status-changed\"));\n }\n }}\n className={cn(\n buttonVariants({\n variant: \"ghost\",\n className:\n \"absolute end-2 md:end-20 top-1/2 -translate-y-1/2 text-fd-muted-foreground/50\",\n size: \"icon\",\n }),\n )}\n >\n <X color={xColor} />\n </button>\n ) : null}\n </div>\n );\n}\n\nconst maskImage =\n \"linear-gradient(to bottom,white,transparent), radial-gradient(circle at top center, white, transparent)\";\n\nfunction flow({ colors }: { colors: string[] }) {\n return (\n <>\n <div\n className=\"absolute inset-0 z-[-1]\"\n style={\n {\n maskImage,\n maskComposite: \"intersect\",\n animation: \"fd-moving-banner 20s linear infinite\",\n backgroundImage: `repeating-linear-gradient(70deg, ${[...colors, colors[0]].map((color, i) => `${color} ${(i * 50) / colors.length}%`).join(\", \")})`,\n backgroundSize: \"200% 100%\",\n filter: \"saturate(2)\",\n } as object\n }\n />\n <style>\n {`@keyframes fd-moving-banner {\n from { background-position: 0% 0; }\n to { background-position: 100% 0; }\n }`}\n </style>\n </>\n );\n}\n",
"type": "registry:component"
}
]
],
"type": "registry:component"
}
Loading
Loading