Files
codeg/src/components/project-boot/shadcn/shadcn-launcher.tsx
2026-03-27 14:13:43 +08:00

104 lines
3.1 KiB
TypeScript

"use client"
import { useMemo, useState, useCallback, useRef, useEffect } from "react"
import { cn } from "@/lib/utils"
import { ShadcnConfigPanel } from "./shadcn-config-panel"
import { ShadcnPreview } from "./shadcn-preview"
import {
DEFAULT_PRESET_CONFIG,
encodePreset,
buildPreviewUrl,
type ShadcnPresetConfig,
} from "./constants"
const MIN_WIDTH = 280
const MAX_WIDTH = 480
const DEFAULT_WIDTH = 360
export function ShadcnLauncher() {
const [config, setConfig] = useState<ShadcnPresetConfig>(
DEFAULT_PRESET_CONFIG
)
const [sidebarWidth, setSidebarWidth] = useState(DEFAULT_WIDTH)
const [isDragging, setIsDragging] = useState(false)
const startXRef = useRef(0)
const startWidthRef = useRef(0)
const presetCode = useMemo(() => encodePreset(config), [config])
const previewUrl = useMemo(
() => buildPreviewUrl(config.base, presetCode),
[config.base, presetCode]
)
const updateConfig = (key: keyof ShadcnPresetConfig, value: string) => {
setConfig((prev) => ({ ...prev, [key]: value }))
}
const handleMouseDown = useCallback(
(e: React.MouseEvent) => {
e.preventDefault()
setIsDragging(true)
startXRef.current = e.clientX
startWidthRef.current = sidebarWidth
},
[sidebarWidth]
)
useEffect(() => {
if (!isDragging) return
const handleMouseMove = (e: MouseEvent) => {
const newWidth = Math.min(
MAX_WIDTH,
Math.max(
MIN_WIDTH,
startWidthRef.current + (e.clientX - startXRef.current)
)
)
setSidebarWidth(newWidth)
}
const handleMouseUp = () => {
setIsDragging(false)
}
document.addEventListener("mousemove", handleMouseMove)
document.addEventListener("mouseup", handleMouseUp)
return () => {
document.removeEventListener("mousemove", handleMouseMove)
document.removeEventListener("mouseup", handleMouseUp)
}
}, [isDragging])
return (
<div className="flex h-full">
<div style={{ width: sidebarWidth }} className="shrink-0">
<ShadcnConfigPanel
config={config}
onConfigChange={updateConfig}
presetCode={presetCode}
/>
</div>
<div
className={cn(
"relative z-20 flex w-px cursor-col-resize items-center justify-center",
"before:pointer-events-none before:absolute before:inset-y-0 before:left-1/2 before:h-full before:w-[var(--resize-handle-thickness)] before:-translate-x-1/2 before:bg-border before:transition-[width,background-color] before:duration-150 before:ease-out",
"after:absolute after:inset-y-0 after:left-1/2 after:w-3 after:-translate-x-1/2",
isDragging
? "[--resize-handle-thickness:5px] before:bg-foreground/60"
: "[--resize-handle-thickness:1px] hover:[--resize-handle-thickness:5px] hover:before:bg-foreground/40"
)}
onMouseDown={handleMouseDown}
/>
<div
className={cn("min-w-0 flex-1", isDragging && "pointer-events-none")}
>
<ShadcnPreview previewUrl={previewUrl} />
</div>
</div>
)
}