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
This commit is contained in:
1 parent
1fd0b00df3
commit
b114581c07
54 files changed
+16494
No files matched your search
@@ -0,0 +1,86 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user