mirror of
https://github.com/hanzoai/shadcn.git
synced 2026-11-02 13:07:46 +00:00
Tailwind is live in app/ — the deployed export at app/out ships 2208 class
selectors — so nothing here is an inert class to delete. These two are the other
failure: a utility assembled at runtime, which Tailwind cannot see in any
project, ours or a customer's.
animated-list is the one that matters. `grid-cols-${gridColumns}` looked correct
in our own docs because this repo's pages spell 1 through 7 literally somewhere,
so the rules exist here and the showcase lays out. A project that installs the
component from the registry has only its own source, and `gridColumns={4}`
generates nothing: compiling a stylesheet for a fake consumer whose only input is
the installed component and one page gives 6871 bytes with no four-column rule,
and Chromium renders the four items as a 472x208 stack. With the track list set
directly it is 472x40, four across, for any count. The layout class and the
layout style are one function each, and the caller's own style still wins.
image-zoom asked for `scale-${zoomScale * 100}` two lines above an inline
`transform: scale(zoomScale)` that was already doing the zoom. The default scale
of 2 wants a 200% utility that is absent even from our own deployed stylesheet
(it has 0, 100, 105, 125), and an inline transform outranks a utility anyway.
The class was decoration on a working mechanism.
Nobody caught either because the docs demo imports AnimatedList from the
PUBLISHED @hanzo/ui-shadcn package, not from the registry source next to it —
the page you look at is not the code you ship.
Both registry payloads regenerated (registry:build), so what the CLI serves
matches the source. app/ typechecks with the same 10 pre-existing TS7016 errors
about @hanzo/ui-shadcn shipping no declarations, none in these files.
Not touched, deliberately: app/lib/colors.ts and apps/v4/lib/colors.ts build
`bg-${color.name}-100`, but that string is rendered as TEXT in the colour-format
picker for the reader to copy. It is never applied, so there is nothing to
generate.
63 lines
1.9 KiB
TypeScript
63 lines
1.9 KiB
TypeScript
"use client"
|
|
|
|
import * as React from "react"
|
|
import { ZoomIn } from "lucide-react"
|
|
|
|
import { cn } from "@/lib/utils"
|
|
|
|
export interface ImageZoomProps
|
|
extends React.ImgHTMLAttributes<HTMLImageElement> {
|
|
zoomScale?: number
|
|
}
|
|
|
|
const ImageZoom = React.forwardRef<HTMLDivElement, ImageZoomProps>(
|
|
({ className, src, alt, zoomScale = 2, ...props }, ref) => {
|
|
const [isZoomed, setIsZoomed] = React.useState(false)
|
|
const [position, setPosition] = React.useState({ x: 0, y: 0 })
|
|
|
|
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
if (!isZoomed) return
|
|
const rect = e.currentTarget.getBoundingClientRect()
|
|
const x = ((e.clientX - rect.left) / rect.width) * 100
|
|
const y = ((e.clientY - rect.top) / rect.height) * 100
|
|
setPosition({ x, y })
|
|
}
|
|
|
|
return (
|
|
<div
|
|
ref={ref}
|
|
className={cn(
|
|
"group relative cursor-zoom-in overflow-hidden rounded-lg",
|
|
className
|
|
)}
|
|
onMouseEnter={() => setIsZoomed(true)}
|
|
onMouseLeave={() => setIsZoomed(false)}
|
|
onMouseMove={handleMouseMove}
|
|
>
|
|
<img
|
|
src={src}
|
|
alt={alt}
|
|
// Scale is the inline transform below: zoomScale is a number, and a
|
|
// number cannot be a utility class.
|
|
className="h-full w-full object-cover transition-transform duration-200"
|
|
style={
|
|
isZoomed
|
|
? {
|
|
transform: `scale(${zoomScale})`,
|
|
transformOrigin: `${position.x}% ${position.y}%`,
|
|
}
|
|
: undefined
|
|
}
|
|
{...props}
|
|
/>
|
|
<div className="absolute right-2 top-2 rounded-full bg-black/50 p-1.5 opacity-0 transition-opacity group-hover:opacity-100">
|
|
<ZoomIn className="h-4 w-4 text-white" />
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
)
|
|
ImageZoom.displayName = "ImageZoom"
|
|
|
|
export { ImageZoom }
|