Files
Sahaj Jain cde8adc6bd Chore/update shadcn components (#266)
* chore: update shadcn/ui components to v3.8.5 and migrate to unified radix-ui

- Update shadcn CLI from 2.5.0 to 3.8.5
- Update 45 standard UI components to latest versions
- Migrate from 28 individual @radix-ui/react-* packages to unified radix-ui
- Update react-resizable-panels from v2 to v4 (direction→orientation,
  onLayout→onLayoutChanged, ref→panelRef, numeric sizes→percentage strings)
- Fix revola.tsx runtime error by sharing shouldUseDialog via React context
  instead of independent useMediaQuery calls in each sub-component
- Preserve custom components: sidebar.tsx, base-ui-tabs.tsx, revola.tsx
- Re-apply button accent variant after component update
- Fix direct Radix imports in theme-toggle, user-profile-dropdown,
  data-table-view-options
- Fix registryItemSchema import (shadcn/registry→shadcn/schema)
- Fix ScrollArea viewPortClassName removal (use data-slot CSS selector)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve CSS specificity regressions from shadcn v3.8.5 update

- button: remove has-[>svg]:px-* variants that override custom padding, restore lg px-8
- dialog: revert sm:max-w-lg to max-w-lg so consumer max-w overrides work
- separator: use JS conditionals instead of data-attribute variants for orientation
- tabs: restore old TabsTrigger styles to prevent dark mode border overrides
- select consumers: use size="sm" prop instead of className h-8 override

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 21:22:27 +05:30

173 lines
5.4 KiB
TypeScript

"use client";
import { Monitor, Smartphone, Tablet } from "lucide-react";
import React from "react";
import { PanelImperativeHandle } from "react-resizable-panels";
import { ComponentErrorBoundary } from "@/components/error-boundary";
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable";
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
import { cn } from "@/lib/utils";
type BlockViewerContext = {
resizablePanelRef: React.RefObject<PanelImperativeHandle | null>;
toggleValue: string;
setToggleValue: (value: string) => void;
};
const BlockViewerContext = React.createContext<BlockViewerContext | null>(null);
function useBlockViewer() {
const context = React.useContext(BlockViewerContext);
if (!context) {
throw new Error("useBlockViewer must be used within a BlockViewerProvider.");
}
return context;
}
function BlockViewerProvider({ children }: { children: React.ReactNode }) {
const resizablePanelRef = React.useRef<PanelImperativeHandle>(null);
const [toggleValue, setToggleValue] = React.useState("100");
return (
<BlockViewerContext.Provider
value={{
resizablePanelRef,
toggleValue,
setToggleValue,
}}
>
{children}
</BlockViewerContext.Provider>
);
}
export function BlockViewer({
className,
name,
children,
...props
}: React.ComponentProps<"div"> & {
name: string;
}) {
return (
<BlockViewerProvider>
<div
data-slot="block-viewer"
className={cn(
"group/block-viewer bg-background @container isolate flex size-full min-w-0 flex-col overflow-clip",
className
)}
{...props}
>
{children}
</div>
</BlockViewerProvider>
);
}
export function BlockViewerToolbar({
name,
className,
toolbarControls,
}: React.ComponentProps<"div"> & {
name: string;
className?: string;
toolbarControls?: React.ReactNode;
}) {
const { resizablePanelRef, toggleValue, setToggleValue } = useBlockViewer();
return (
<div data-slot="block-viewer-toolbar" className={cn("h-12 w-full border-b p-2", className)}>
<div className="flex size-full items-center justify-between gap-4">
<div className="flex-1">
{!!toolbarControls ? (
toolbarControls
) : (
<span className="text-sm font-medium capitalize">{name}</span>
)}
</div>
<div className="hidden items-center justify-between lg:group-has-data-[slot=block-viewer-display]/block-viewer:flex">
<ToggleGroup
className="bg-background flex gap-0.5 rounded-lg border p-0.5"
type="single"
value={toggleValue}
onValueChange={(value) => {
if (value && resizablePanelRef?.current) {
resizablePanelRef.current.resize(parseInt(value));
setToggleValue(value);
}
}}
>
<ToggleGroupItem value="100" className="flex size-7 items-center justify-center p-1">
<Monitor className="size-4" />
</ToggleGroupItem>
<ToggleGroupItem value="60" className="flex size-7 items-center justify-center p-1">
<Tablet className="size-4" />
</ToggleGroupItem>
<ToggleGroupItem value="30" className="flex size-7 items-center justify-center p-1">
<Smartphone className="size-4" />
</ToggleGroupItem>
</ToggleGroup>
</div>
</div>
</div>
);
}
export function BlockViewerDisplay({
name,
className,
children,
...props
}: React.ComponentProps<"div"> & {
name: string;
}) {
const { resizablePanelRef, setToggleValue } = useBlockViewer();
// Auto-resize to full width when screen goes under lg breakpoint (1024px)
React.useEffect(() => {
const mql = window.matchMedia("(max-width: 1023px)");
const resizePanel = () => {
if (window.innerWidth < 1024 && resizablePanelRef?.current) {
resizablePanelRef.current.resize(100);
setToggleValue("100");
}
};
resizePanel();
mql.addEventListener("change", resizePanel);
return () => mql.removeEventListener("change", resizePanel);
}, [resizablePanelRef, setToggleValue]);
return (
<ComponentErrorBoundary name={name}>
<div
id={name}
data-slot="block-viewer-display"
data-name={name.toLowerCase()}
className={cn("size-full min-h-0 gap-4 overflow-hidden", className)}
{...props}
>
<ResizablePanelGroup orientation="horizontal" className="relative isolate z-10 size-full">
<div className="bg-muted absolute inset-0 right-3 [background-image:radial-gradient(var(--muted-foreground),transparent_1px)] [background-size:1rem_1rem] opacity-60 dark:opacity-35" />
<ResizablePanel
panelRef={resizablePanelRef}
className="bg-background relative min-w-[350px] lg:aspect-auto"
defaultSize="100%"
minSize="30%"
>
{children}
</ResizablePanel>
<ResizableHandle className="after:bg-border relative inset-x-0 mx-auto hidden w-3 border-l bg-transparent after:absolute after:top-1/2 after:h-8 after:w-[4px] after:-translate-y-1/2 after:rounded-full after:transition-all hover:after:h-12 active:after:h-12 lg:block" />
<ResizablePanel defaultSize="0%" minSize="0%" />
</ResizablePanelGroup>
</div>
</ComponentErrorBoundary>
);
}