mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-27 10:46:51 +00:00
fix(ui): resolve 9 animated design system bugs including Monaco tab height accumulation
- fix(editor): Monaco compose↔.env tab switching no longer accumulates height
each switch — root cause was Monaco's position:static internal child creating
a circular CSS dependency with the CSS grid; broken by calling layout({0,0})
to force reflow before re-measuring. Added overflow-hidden to Monaco container.
- fix(ui): sliding tab indicator on compose/env tabs via motion.div layoutId
- fix(ui): sliding tab indicator on Notification tabs (Discord/Slack/Webhook)
- fix(ui): switch thumb now animates position (Radix data-attribute CSS translation)
- fix(ui): 'Always Local' tooltip no longer crashes — replaced animate-ui tooltip
(getStrictContext throws outside provider) with pure Radix primitives + CSS animation
- feat(settings): Auto theme option added (light/dark/auto with matchMedia listener)
- fix(ui): NodeManager dialog buttons no longer stuck together (DialogFooter gap)
- fix(ui): AlertDialog spring animation + Quick Clean button text wraps correctly
- fix(ui): Resources/App Store/Logs buttons toggle off on second click
This commit is contained in:
@@ -1,14 +1,13 @@
|
||||
import * as React from "react"
|
||||
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
|
||||
import * as React from 'react';
|
||||
import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
|
||||
import { motion } from 'motion/react';
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { buttonVariants } from "@/components/ui/button"
|
||||
import { cn } from '@/lib/utils';
|
||||
import { buttonVariants } from '@/components/ui/button';
|
||||
|
||||
const AlertDialog = AlertDialogPrimitive.Root
|
||||
|
||||
const AlertDialogTrigger = AlertDialogPrimitive.Trigger
|
||||
|
||||
const AlertDialogPortal = AlertDialogPrimitive.Portal
|
||||
const AlertDialog = AlertDialogPrimitive.Root;
|
||||
const AlertDialogTrigger = AlertDialogPrimitive.Trigger;
|
||||
const AlertDialogPortal = AlertDialogPrimitive.Portal;
|
||||
|
||||
const AlertDialogOverlay = React.forwardRef<
|
||||
React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
|
||||
@@ -16,60 +15,59 @@ const AlertDialogOverlay = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AlertDialogPrimitive.Overlay
|
||||
className={cn(
|
||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
))
|
||||
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
|
||||
));
|
||||
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
|
||||
|
||||
const AlertDialogContent = React.forwardRef<
|
||||
React.ElementRef<typeof AlertDialogPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
|
||||
>(({ className, ...props }, ref) => (
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<AlertDialogPortal>
|
||||
<AlertDialogOverlay />
|
||||
<AlertDialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
<AlertDialogPrimitive.Content ref={ref} asChild {...props}>
|
||||
<motion.div
|
||||
className={cn(
|
||||
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg sm:rounded-lg',
|
||||
className
|
||||
)}
|
||||
initial={{ opacity: 0, scale: 0.92, y: -12 }}
|
||||
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||||
transition={{ type: 'spring', stiffness: 380, damping: 32 }}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
</AlertDialogPrimitive.Content>
|
||||
</AlertDialogPortal>
|
||||
))
|
||||
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
|
||||
));
|
||||
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
|
||||
|
||||
const AlertDialogHeader = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col space-y-2 text-center sm:text-left",
|
||||
className
|
||||
)}
|
||||
className={cn('flex flex-col space-y-2 text-center sm:text-left', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
AlertDialogHeader.displayName = "AlertDialogHeader"
|
||||
);
|
||||
AlertDialogHeader.displayName = 'AlertDialogHeader';
|
||||
|
||||
const AlertDialogFooter = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||
className
|
||||
)}
|
||||
className={cn('flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
AlertDialogFooter.displayName = "AlertDialogFooter"
|
||||
);
|
||||
AlertDialogFooter.displayName = 'AlertDialogFooter';
|
||||
|
||||
const AlertDialogTitle = React.forwardRef<
|
||||
React.ElementRef<typeof AlertDialogPrimitive.Title>,
|
||||
@@ -77,11 +75,11 @@ const AlertDialogTitle = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AlertDialogPrimitive.Title
|
||||
ref={ref}
|
||||
className={cn("text-lg font-semibold", className)}
|
||||
className={cn('text-lg font-semibold', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
|
||||
));
|
||||
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
|
||||
|
||||
const AlertDialogDescription = React.forwardRef<
|
||||
React.ElementRef<typeof AlertDialogPrimitive.Description>,
|
||||
@@ -89,12 +87,11 @@ const AlertDialogDescription = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AlertDialogPrimitive.Description
|
||||
ref={ref}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
className={cn('text-sm text-muted-foreground', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AlertDialogDescription.displayName =
|
||||
AlertDialogPrimitive.Description.displayName
|
||||
));
|
||||
AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName;
|
||||
|
||||
const AlertDialogAction = React.forwardRef<
|
||||
React.ElementRef<typeof AlertDialogPrimitive.Action>,
|
||||
@@ -105,8 +102,8 @@ const AlertDialogAction = React.forwardRef<
|
||||
className={cn(buttonVariants(), className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
|
||||
));
|
||||
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
|
||||
|
||||
const AlertDialogCancel = React.forwardRef<
|
||||
React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
|
||||
@@ -114,15 +111,11 @@ const AlertDialogCancel = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AlertDialogPrimitive.Cancel
|
||||
ref={ref}
|
||||
className={cn(
|
||||
buttonVariants({ variant: "outline" }),
|
||||
"mt-2 sm:mt-0",
|
||||
className
|
||||
)}
|
||||
className={cn(buttonVariants({ variant: 'outline' }), className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
|
||||
));
|
||||
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
|
||||
|
||||
export {
|
||||
AlertDialog,
|
||||
@@ -136,4 +129,4 @@ export {
|
||||
AlertDialogDescription,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
DialogTrigger,
|
||||
DialogContent as AnimateDialogContent,
|
||||
DialogHeader,
|
||||
DialogFooter,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
} from '@/components/animate-ui/primitives/radix/dialog';
|
||||
@@ -51,6 +50,18 @@ const DialogOverlay = React.forwardRef<
|
||||
));
|
||||
DialogOverlay.displayName = 'DialogOverlay';
|
||||
|
||||
// Override DialogFooter to add proper spacing (animate-ui's version has no classes)
|
||||
const DialogFooter = ({ className, ...props }: React.ComponentProps<'div'>) => (
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-col-reverse gap-2 sm:flex-row sm:justify-end',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
DialogFooter.displayName = 'DialogFooter';
|
||||
|
||||
export {
|
||||
Dialog,
|
||||
DialogPortal,
|
||||
|
||||
@@ -22,7 +22,7 @@ const Switch = React.forwardRef<HTMLButtonElement, SwitchProps>(
|
||||
{...props}
|
||||
>
|
||||
<SwitchThumb
|
||||
className="pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0"
|
||||
className="pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"
|
||||
pressedAnimation={{ scale: 1.15 }}
|
||||
/>
|
||||
</AnimateSwitch>
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import * as React from 'react';
|
||||
import { Tabs as RadixTabs } from 'radix-ui';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
import {
|
||||
Tabs,
|
||||
TabsHighlight,
|
||||
TabsHighlightItem,
|
||||
TabsList as AnimateTabsList,
|
||||
TabsTrigger as AnimateTabsTrigger,
|
||||
TabsContent as AnimateTabsContent,
|
||||
} from '@/components/animate-ui/primitives/radix/tabs';
|
||||
|
||||
const TabsList = React.forwardRef<
|
||||
@@ -38,20 +40,22 @@ const TabsTrigger = React.forwardRef<
|
||||
));
|
||||
TabsTrigger.displayName = 'TabsTrigger';
|
||||
|
||||
// Custom TabsContent: uses Radix directly (no Framer Motion `layout` prop)
|
||||
// to prevent unintended height expansion when switching tabs.
|
||||
const TabsContent = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.ComponentProps<typeof AnimateTabsContent>
|
||||
React.ComponentProps<typeof RadixTabs.Content>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AnimateTabsContent
|
||||
<RadixTabs.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
||||
'data-[state=active]:animate-in data-[state=active]:fade-in-0 data-[state=active]:duration-200',
|
||||
className
|
||||
)}
|
||||
transition={{ duration: 0.2, ease: 'easeOut' }}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TabsContent.displayName = 'TabsContent';
|
||||
|
||||
export { Tabs, TabsList, TabsTrigger, TabsContent };
|
||||
export { Tabs, TabsList, TabsTrigger, TabsContent, TabsHighlight, TabsHighlightItem };
|
||||
|
||||
@@ -1,33 +1,35 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
import {
|
||||
TooltipProvider,
|
||||
Tooltip,
|
||||
TooltipTrigger,
|
||||
TooltipPortal,
|
||||
TooltipContent as AnimateTooltipContent,
|
||||
} from '@/components/animate-ui/primitives/radix/tooltip';
|
||||
// Use Radix primitives directly to avoid animate-ui context dependencies
|
||||
// that can cause crashes in certain component tree configurations.
|
||||
const TooltipProvider = TooltipPrimitive.Provider;
|
||||
const Tooltip = TooltipPrimitive.Root;
|
||||
const TooltipTrigger = TooltipPrimitive.Trigger;
|
||||
|
||||
// Bundles the portal so consumers keep the same <TooltipContent> API
|
||||
const TooltipContent = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.ComponentProps<typeof AnimateTooltipContent>
|
||||
React.ComponentProps<typeof TooltipPrimitive.Content>
|
||||
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||
<TooltipPortal>
|
||||
<AnimateTooltipContent
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
'z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground origin-[--radix-tooltip-content-transform-origin]',
|
||||
'z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground',
|
||||
'origin-[--radix-tooltip-content-transform-origin]',
|
||||
'animate-in fade-in-0 zoom-in-95 duration-150',
|
||||
'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
|
||||
'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2',
|
||||
'data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
className
|
||||
)}
|
||||
transition={{ type: 'spring', stiffness: 350, damping: 28 }}
|
||||
{...props}
|
||||
/>
|
||||
</TooltipPortal>
|
||||
</TooltipPrimitive.Portal>
|
||||
));
|
||||
TooltipContent.displayName = 'TooltipContent';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user