优化启动器样式
This commit is contained in:
@@ -13,7 +13,7 @@ export function ProjectBootWorkspace() {
|
|||||||
const t = useTranslations("ProjectBoot")
|
const t = useTranslations("ProjectBoot")
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs defaultValue="shadcn" className="flex h-full flex-col">
|
<Tabs defaultValue="shadcn" className="flex h-full flex-col gap-0">
|
||||||
<div className="shrink-0 border-b px-4 py-2">
|
<div className="shrink-0 border-b px-4 py-2">
|
||||||
<TabsList>
|
<TabsList>
|
||||||
<TabsTrigger value="shadcn">{t("tabs.shadcn")}</TabsTrigger>
|
<TabsTrigger value="shadcn">{t("tabs.shadcn")}</TabsTrigger>
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export function ShadcnConfigPanel({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full flex-col">
|
<div className="flex h-full flex-col">
|
||||||
<ScrollArea className="flex-1 px-4 py-3">
|
<ScrollArea className="min-h-0 flex-1 px-4 py-3">
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{CONFIG_FIELDS.map((field) => (
|
{CONFIG_FIELDS.map((field) => (
|
||||||
<div key={field.key} className="space-y-1">
|
<div key={field.key} className="space-y-1">
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useMemo, useState } from "react"
|
import { useMemo, useState, useCallback, useRef, useEffect } from "react"
|
||||||
import {
|
import { cn } from "@/lib/utils"
|
||||||
ResizablePanelGroup,
|
|
||||||
ResizablePanel,
|
|
||||||
ResizableHandle,
|
|
||||||
} from "@/components/ui/resizable"
|
|
||||||
import { ShadcnConfigPanel } from "./shadcn-config-panel"
|
import { ShadcnConfigPanel } from "./shadcn-config-panel"
|
||||||
import { ShadcnPreview } from "./shadcn-preview"
|
import { ShadcnPreview } from "./shadcn-preview"
|
||||||
import {
|
import {
|
||||||
@@ -15,10 +11,18 @@ import {
|
|||||||
type ShadcnPresetConfig,
|
type ShadcnPresetConfig,
|
||||||
} from "./constants"
|
} from "./constants"
|
||||||
|
|
||||||
|
const MIN_WIDTH = 280
|
||||||
|
const MAX_WIDTH = 480
|
||||||
|
const DEFAULT_WIDTH = 360
|
||||||
|
|
||||||
export function ShadcnLauncher() {
|
export function ShadcnLauncher() {
|
||||||
const [config, setConfig] = useState<ShadcnPresetConfig>(
|
const [config, setConfig] = useState<ShadcnPresetConfig>(
|
||||||
DEFAULT_PRESET_CONFIG
|
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 presetCode = useMemo(() => encodePreset(config), [config])
|
||||||
const previewUrl = useMemo(
|
const previewUrl = useMemo(
|
||||||
@@ -30,21 +34,70 @@ export function ShadcnLauncher() {
|
|||||||
setConfig((prev) => ({ ...prev, [key]: value }))
|
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 (
|
return (
|
||||||
<ResizablePanelGroup direction="horizontal" className="h-full">
|
<div className="flex h-full">
|
||||||
<ResizablePanel defaultSize={40} minSize={30} maxSize={50}>
|
<div style={{ width: sidebarWidth }} className="shrink-0">
|
||||||
<ShadcnConfigPanel
|
<ShadcnConfigPanel
|
||||||
config={config}
|
config={config}
|
||||||
onConfigChange={updateConfig}
|
onConfigChange={updateConfig}
|
||||||
presetCode={presetCode}
|
presetCode={presetCode}
|
||||||
/>
|
/>
|
||||||
</ResizablePanel>
|
</div>
|
||||||
|
|
||||||
<ResizableHandle />
|
<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}
|
||||||
|
/>
|
||||||
|
|
||||||
<ResizablePanel defaultSize={60} minSize={40}>
|
<div
|
||||||
|
className={cn("min-w-0 flex-1", isDragging && "pointer-events-none")}
|
||||||
|
>
|
||||||
<ShadcnPreview previewUrl={previewUrl} />
|
<ShadcnPreview previewUrl={previewUrl} />
|
||||||
</ResizablePanel>
|
</div>
|
||||||
</ResizablePanelGroup>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user