Scaffolds the new app (Vite+React19+Tailwind v4) on HMDesign via git submodule + npm workspace, ports src/viewer2d/* and optionsStore.ts unchanged, and rebuilds the vanilla UI (toolbar/layers/properties/ options panels, coord readout, license dialog, load progress) as HM Design System components. Panel drag/collapse ported from panelUtils.ts as a React hook; Measure UI intentionally left unwired per original app parity. Typecheck and dev-server module serving verified; in-browser rendering not yet verified in this session (no browser tool available) — needs manual check. Refs #1
87 lines
2.2 KiB
TypeScript
87 lines
2.2 KiB
TypeScript
import type { ReactNode } from 'react';
|
|
import { Card, CardContent, CardHeader, CardTitle, CardAction } from '@hm-design/ui';
|
|
import { Button } from '@hm-design/ui';
|
|
import { ChevronDown, ChevronRight, X } from 'lucide-react';
|
|
import { useDraggablePanel } from '../hooks/useDraggablePanel';
|
|
|
|
export interface DraggablePanelProps {
|
|
title: string;
|
|
icon?: ReactNode;
|
|
initial: { left: number; top: number };
|
|
width?: number;
|
|
height?: number;
|
|
open: boolean;
|
|
onClose: () => void;
|
|
headerActions?: ReactNode;
|
|
children: ReactNode;
|
|
}
|
|
|
|
export function DraggablePanel({
|
|
title,
|
|
icon,
|
|
initial,
|
|
width = 320,
|
|
height = 420,
|
|
open,
|
|
onClose,
|
|
headerActions,
|
|
children,
|
|
}: DraggablePanelProps) {
|
|
const { panelRef, style, collapsed, toggleCollapse, headerProps } = useDraggablePanel(initial);
|
|
|
|
if (!open) return null;
|
|
|
|
return (
|
|
<Card
|
|
ref={panelRef}
|
|
size="sm"
|
|
className="fixed shadow-lg overflow-hidden"
|
|
style={{
|
|
...style,
|
|
width,
|
|
height: collapsed ? 'auto' : height,
|
|
minWidth: 250,
|
|
minHeight: collapsed ? 'auto' : 140,
|
|
maxWidth: '90vw',
|
|
maxHeight: '90vh',
|
|
resize: collapsed ? 'none' : 'both',
|
|
}}
|
|
>
|
|
<CardHeader className="select-none border-b py-2" {...headerProps}>
|
|
<CardTitle className="flex items-center gap-2 text-sm">
|
|
{icon}
|
|
{title}
|
|
</CardTitle>
|
|
<CardAction className="flex items-center gap-1">
|
|
{headerActions}
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="icon-xs"
|
|
aria-label={collapsed ? '펼치기' : '접기'}
|
|
title={collapsed ? '펼치기' : '접기'}
|
|
onClick={toggleCollapse}
|
|
>
|
|
{collapsed ? <ChevronRight /> : <ChevronDown />}
|
|
</Button>
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="icon-xs"
|
|
aria-label="닫기"
|
|
title="닫기"
|
|
onClick={onClose}
|
|
>
|
|
<X />
|
|
</Button>
|
|
</CardAction>
|
|
</CardHeader>
|
|
{!collapsed && (
|
|
<CardContent className="overflow-auto" style={{ height: 'calc(100% - 44px)' }}>
|
|
{children}
|
|
</CardContent>
|
|
)}
|
|
</Card>
|
|
);
|
|
}
|