- @mui/icons-material → lucide-react 전환 (23파일, 잔여 0) - VoiceRecordingPanel: 9바 DOM 웨이브 → GradientWave (canvas) - Vanilla 팝업 5종 style.css v2 정합 (글래스 네이비/헤어라인/블루, CSS 변수 주입 구조 유지 — 테마 반응형 보존)
89 lines
2.3 KiB
TypeScript
89 lines
2.3 KiB
TypeScript
// src/renderer/components/meeting/DocumentTab.tsx
|
|
// Phase 14.5: 문서 탭 — MarkdownEditor + 자동저장 + 내보내기
|
|
|
|
import { useCallback, useRef } from 'react'
|
|
import { Box } from '@mui/material'
|
|
import { Trash2 } from 'lucide-react'
|
|
import { MarkdownEditor } from './MarkdownEditor'
|
|
import { ExportMenu } from './ExportMenu'
|
|
import { PhysicalButton } from '@d3ro/ui/components/ds'
|
|
import { d3roPalette, d3roTypo } from '@d3ro/ui/theme'
|
|
import { useI18n } from '@d3ro/i18n'
|
|
import type { MeetingExportFormat } from '@d3ro/core/types'
|
|
|
|
interface DocumentTabDoc {
|
|
id: string
|
|
title: string
|
|
content: string
|
|
templateType: string
|
|
}
|
|
|
|
interface DocumentTabProps {
|
|
document: DocumentTabDoc
|
|
onContentChange: (content: string) => void
|
|
onExport: (format: MeetingExportFormat) => void
|
|
onDelete: () => void
|
|
}
|
|
|
|
const AUTOSAVE_DELAY = 500
|
|
|
|
export function DocumentTab({
|
|
document: doc,
|
|
onContentChange,
|
|
onExport,
|
|
onDelete,
|
|
}: DocumentTabProps): React.ReactElement {
|
|
const { t } = useI18n()
|
|
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
|
|
const handleChange = useCallback(
|
|
(content: string) => {
|
|
if (timerRef.current) clearTimeout(timerRef.current)
|
|
timerRef.current = setTimeout(() => {
|
|
onContentChange(content)
|
|
}, AUTOSAVE_DELAY)
|
|
},
|
|
[onContentChange],
|
|
)
|
|
|
|
return (
|
|
<Box
|
|
sx={{
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
height: '100%',
|
|
}}
|
|
>
|
|
{/* 콘텐츠 편집기 */}
|
|
<Box sx={{ flex: 1, overflow: 'hidden', minHeight: 0 }}>
|
|
<MarkdownEditor
|
|
content={doc.content}
|
|
onChange={handleChange}
|
|
/>
|
|
</Box>
|
|
|
|
{/* 하단 액션 바 */}
|
|
<Box
|
|
sx={{
|
|
display: 'flex',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
mt: 1.5,
|
|
pt: 1.5,
|
|
borderTop: `1px solid ${d3roPalette.border.subtle}`,
|
|
}}
|
|
>
|
|
<ExportMenu onExport={onExport} />
|
|
<PhysicalButton
|
|
size="small"
|
|
color="error"
|
|
onClick={onDelete}
|
|
sx={{ height: 32, fontSize: d3roTypo.engrave.size }}
|
|
>
|
|
<Trash2 size={14} style={{ marginRight: 4 }} />
|
|
{t('common.delete')}
|
|
</PhysicalButton>
|
|
</Box>
|
|
</Box>
|
|
)
|
|
}
|