Files
dwg-dxf-viewer-hm/src/components/DraggablePanel.tsx
T
minsung b114581c07 feat: React19+HMDesign shell with ported DWG/DXF viewer engine
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
2026-10-12 17:42:58 +09:00

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>
);
}