diff --git a/apps/docs/app/layout.tsx b/apps/docs/app/layout.tsx index 3ea6f986a5..a905af4bfe 100644 --- a/apps/docs/app/layout.tsx +++ b/apps/docs/app/layout.tsx @@ -13,7 +13,7 @@ import {siteConfig} from "@/config/site"; import {fonts} from "@/config/fonts"; import {Navbar} from "@/components/navbar"; import {Footer} from "@/components/footer"; -import {RandomBanner} from "@/components/random-banner"; +import {ProBanner} from "@/components/pro-banner"; export const metadata: Metadata = { title: { @@ -83,7 +83,7 @@ export default function RootLayout({children}: {children: React.ReactNode}) { >
- + {children} diff --git a/apps/docs/components/marketing/hero/hero.tsx b/apps/docs/components/marketing/hero/hero.tsx index 26b6f5cdf4..2f8ed22992 100644 --- a/apps/docs/components/marketing/hero/hero.tsx +++ b/apps/docs/components/marketing/hero/hero.tsx @@ -1,12 +1,13 @@ "use client"; import NextLink from "next/link"; -import {Button, Link, Chip, Snippet} from "@heroui/react"; +import {Button, Link, Snippet} from "@heroui/react"; import {ArrowRightIcon} from "@heroui/shared-icons"; import dynamic from "next/dynamic"; import {usePostHog} from "posthog-js/react"; import {FloatingComponents} from "./floating-components"; +import {V3ReleaseBanner} from "./v3-release-banner"; import {GithubIcon} from "@/components/icons"; import {title, subtitle} from "@/components/primitives"; @@ -19,35 +20,11 @@ const BgLooper = dynamic(() => import("./bg-looper").then((mod) => mod.BgLooper) export const Hero = () => { const posthog = usePostHog(); - const handlePressAnnouncement = (name: string, url: string) => { - posthog.capture("NavbarItem", { - name, - action: "press", - category: "home - hero", - data: url, - }); - }; - return (
-
- handlePressAnnouncement("HeroUI v2.8.0", "/blog/v2.8.0")} - > - HeroUI v2.8.0  - - 🔥 - - +
+
diff --git a/apps/docs/components/marketing/hero/v3-release-banner.tsx b/apps/docs/components/marketing/hero/v3-release-banner.tsx new file mode 100644 index 0000000000..6baa16e3c0 --- /dev/null +++ b/apps/docs/components/marketing/hero/v3-release-banner.tsx @@ -0,0 +1,46 @@ +"use client"; + +import {Chip} from "@heroui/react"; +import {Icon} from "@iconify/react/dist/offline"; +import arrowRightUpIcon from "@iconify/icons-solar/arrow-right-up-linear"; + +const releaseInfo = { + title: "HeroUI v3.0.0 (Beta)", + href: "https://v3.heroui.com?ref=heroui-v2", + emoji: "🔥", +}; + +export function V3ReleaseBanner() { + return ( + + {releaseInfo.emoji} + + {releaseInfo.title} + + + + ); +} diff --git a/apps/docs/components/navbar.tsx b/apps/docs/components/navbar.tsx index a5b47431d1..1146b5c6a5 100644 --- a/apps/docs/components/navbar.tsx +++ b/apps/docs/components/navbar.tsx @@ -185,7 +185,7 @@ export const Navbar: FC = ({children, routes, mobileRoutes = [], sl {versionChip} - = ({children, routes, mobileRoutes = [], sl 🔥 - + */} diff --git a/packages/components/toast/src/use-toast.ts b/packages/components/toast/src/use-toast.ts index ece5929ab5..4a1149aabf 100644 --- a/packages/components/toast/src/use-toast.ts +++ b/packages/components/toast/src/use-toast.ts @@ -134,8 +134,6 @@ export type UseToastProps = Props & ToastVariantProps & Omit, "div">; -const SWIPE_THRESHOLD_X = 100; -const SWIPE_THRESHOLD_Y = 20; const INITIAL_POSITION = 50; export function useToast(originalProps: UseToastProps) { @@ -359,62 +357,6 @@ export function useToast(originalProps: UseToastProps) exit: {opacity: 0, y: -INITIAL_POSITION * multiplier}, }; - const [drag, setDrag] = useState(false); - const [dragValue, setDragValue] = useState(0); - - const shouldCloseToast = (offsetX: number, offsetY: number) => { - const isRight = placement.includes("right"); - const isLeft = placement.includes("left"); - const isCenterTop = placement === "top-center"; - const isCenterBottom = placement === "bottom-center"; - - if ( - (isRight && offsetX >= SWIPE_THRESHOLD_X) || - (isLeft && offsetX <= -SWIPE_THRESHOLD_X) || - (isCenterTop && offsetY <= -SWIPE_THRESHOLD_Y) || - (isCenterBottom && offsetY >= SWIPE_THRESHOLD_Y) - ) { - return true; - } - }; - - const getDragElasticConstraints = (placement: string) => { - const elasticConstraint = {top: 0, bottom: 0, right: 0, left: 0}; - - if (placement === "bottom-center") { - elasticConstraint.bottom = 1; - - return elasticConstraint; - } - if (placement === "top-center") { - elasticConstraint.top = 1; - - return elasticConstraint; - } - if (placement.includes("right")) { - elasticConstraint.right = 1; - - return elasticConstraint; - } - if (placement.includes("left")) { - elasticConstraint.left = 1; - - return elasticConstraint; - } - - elasticConstraint.left = 1; - elasticConstraint.right = 1; - - return elasticConstraint; - }; - - let opacityValue: undefined | number = undefined; - - if ((drag && placement === "bottom-center") || placement === "top-center") { - opacityValue = Math.max(0, 1 - dragValue / (SWIPE_THRESHOLD_Y + 5)); - } else if (drag) { - opacityValue = Math.max(0, 1 - dragValue / (SWIPE_THRESHOLD_X + 20)); - } const getToastProps: PropGetter = useCallback( (props = {}) => { @@ -432,7 +374,6 @@ export function useToast(originalProps: UseToastProps) "data-has-title": dataAttr(!isEmpty(title)), "data-has-description": dataAttr(!isEmpty(description)), "data-placement": placement, - "data-drag-value": dragValue, "data-toast": true, "aria-label": "toast", "data-toast-exiting": dataAttr(isToastExiting), @@ -447,9 +388,8 @@ export function useToast(originalProps: UseToastProps) } }, style: { - opacity: opacityValue, ...pseudoElementStyles, - }, + } as React.CSSProperties, ...mergeProps(props, otherProps, toastProps, hoverProps), }; }, @@ -460,7 +400,6 @@ export function useToast(originalProps: UseToastProps) hoverProps, toast, toast.key, - opacityValue, isToastExiting, state, toast.key, @@ -557,28 +496,48 @@ export function useToast(originalProps: UseToastProps) [], ); + /** + * Get motion div props for toast animation + * + * Note: Drag functionality has been completely removed from toast component. + * + * Why drag was removed: + * - Drag functionality was causing issues with opacity during drag operations + * - The drag ghost image (SVG snapshot) created by Framer Motion was causing visual artifacts + * - Users can still close toasts using the close button + * + * If drag functionality is needed in the future, the official Framer Motion solution is: + * - Use `dragListener={false}` to prevent automatic drag listener creation + * - Use `useDragControls()` for manual drag control if needed + * - This prevents Framer Motion from creating the SVG/ghost layer snapshot + * + * Example: + * ```tsx + * const controls = useDragControls(); + * + * ``` + */ const getMotionDivProps = useCallback( ( props = {}, ): MotionProps & { - "data-drag": string | boolean; "data-placement": string; - "data-drag-value": number; className: string; } => { const comparingValue = isRegionExpanded ? maxVisibleToasts - 1 : Math.min(2, maxVisibleToasts - 1); const isCloseToEnd = total - index - 1 <= comparingValue; - const dragDirection = placement === "bottom-center" || placement === "top-center" ? "y" : "x"; - const dragConstraints = {left: 0, right: 0, top: 0, bottom: 0}; - const dragElastic = getDragElasticConstraints(placement); const animateProps = (() => { if (placement.includes("top")) { return { top: - isRegionExpanded || drag + isRegionExpanded ? liftHeight + toastOffset : (total - 1 - index) * 8 + toastOffset, bottom: "auto", @@ -586,7 +545,7 @@ export function useToast(originalProps: UseToastProps) } else if (placement.includes("bottom")) { return { bottom: - isRegionExpanded || drag + isRegionExpanded ? liftHeight + toastOffset : (total - 1 - index) * 8 + toastOffset, top: "auto", @@ -600,13 +559,11 @@ export function useToast(originalProps: UseToastProps) animate: { opacity: isCloseToEnd ? 1 : 0, pointerEvents: isCloseToEnd ? "all" : "none", - scaleX: isRegionExpanded || drag ? 1 : 1 - (total - 1 - index) * 0.1, - height: isRegionExpanded || drag ? initialHeight : frontHeight, + scaleX: isRegionExpanded ? 1 : 1 - (total - 1 - index) * 0.1, + height: isRegionExpanded ? initialHeight : frontHeight, y: 0, ...animateProps, }, - drag: dragDirection, - dragConstraints, exit: { opacity: 0, transition: {duration: 0.3}, @@ -614,42 +571,7 @@ export function useToast(originalProps: UseToastProps) initial: {opacity: 0, scale: 1, y: -40 * multiplier}, transition: {duration: 0.3, ease: "easeOut"}, variants: toastVariants, - dragElastic, - onDragEnd: (_, info) => { - const {x: offsetX, y: offsetY} = info.offset; - - setDrag(false); - - if (shouldCloseToast(offsetX, offsetY)) { - setIsToastExiting(true); - - return; - } - setDragValue(0); - }, - onDrag: (_, info) => { - let updatedDragValue = 0; - - if (placement === "top-center") { - updatedDragValue = -info.offset.y; - } else if (placement === "bottom-center") { - updatedDragValue = info.offset.y; - } else if (placement.includes("right")) { - updatedDragValue = info.offset.x; - } else if (placement.includes("left")) { - updatedDragValue = -info.offset.x; - } - - if (updatedDragValue >= 0) { - setDragValue(updatedDragValue); - } - }, - onDragStart: () => { - setDrag(true); - }, - "data-drag": dataAttr(drag), "data-placement": placement, - "data-drag-value": dragValue, className: slots.motionDiv({class: classNames?.motionDiv}), ...props, ...motionProps, @@ -667,10 +589,6 @@ export function useToast(originalProps: UseToastProps) frontHeight, toastVariants, classNames, - drag, - dataAttr, - setDrag, - shouldCloseToast, slots, toastOffset, maxVisibleToasts,