[as] feat: 웹기안기 '열기' 기능 추가(개발용)

This commit is contained in:
2026-09-14 17:52:16 +09:00
parent 00e1cb707e
commit 90a1d6fdff
2 changed files with 95 additions and 36 deletions
@@ -1,12 +1,17 @@
"use client";
import { Download, Fullscreen, Save } from "lucide-react";
import { useId } from "react";
import { Button } from "@common/components";
import { Download, FolderOpen, Fullscreen, Save } from "lucide-react";
import { Button, cn } from "@common/components";
const WEBHYP_DEV_MODE = process.env.NEXT_PUBLIC_WEBHYP_DEV_MODE === "true";
interface HangulGiangiToolbarProps {
isBusy?: boolean;
canSave?: boolean;
onOpen: (file: File) => void;
onSave: () => void;
onDownload: () => void;
onFullscreen: () => void;
@@ -15,12 +20,51 @@ interface HangulGiangiToolbarProps {
function HangulGiangiToolbar({
isBusy = false,
canSave = true,
onOpen,
onSave,
onDownload,
onFullscreen,
}: HangulGiangiToolbarProps) {
const openInputId = useId();
return (
<div className="flex w-full flex-nowrap items-center gap-2 border-b border-border bg-background p-3">
{WEBHYP_DEV_MODE && (
<label
htmlFor={openInputId}
className={cn("cursor-pointer", isBusy && "pointer-events-none")}
>
<input
id={openInputId}
type="file"
accept=".hwpx,application/haansofthwpx"
disabled={isBusy}
className="peer sr-only"
aria-label="열기"
onChange={(event) => {
const file = event.target.files?.[0];
if (file) onOpen(file);
event.target.value = "";
}}
/>
<Button
type="button"
color="white"
size="sm"
disabled={isBusy}
className={cn(
"peer-focus-visible:ring-2 peer-focus-visible:ring-ring peer-focus-visible:ring-offset-2",
isBusy && "pointer-events-none opacity-50",
)}
asChild
>
<span>
<FolderOpen className="!size-5" />
열기
</span>
</Button>
</label>
)}
{canSave && (
<Button type="button" color="white" size="sm" disabled={isBusy} onClick={onSave}>
<Save className="!size-5" />
@@ -70,43 +70,46 @@ function HangulGiangi({
return () => window.clearInterval(timer);
}, [isCtrlReady, onReady]);
const handleOpen = useCallback(() => {
const ctrl = resolveHwpCtrl(iframeRef.current, isCtrlReady);
if (!ctrl) return;
const openHwpxBlob = useCallback(
(blob: Blob, fileName: string) => {
const ctrl = resolveHwpCtrl(iframeRef.current, isCtrlReady);
if (!ctrl) return;
setIsBusy(true);
openingDocumentKeyRef.current = documentKey;
attemptedDocumentKeyRef.current = documentKey;
try {
const documentFile = new File([documentBlob], documentFileName, {
type: HWPX_CONTENT_TYPE,
});
const isAccepted = ctrl.Open(
documentFile,
"HWPX",
"",
(result) => {
const callbackDocumentKey = getCallbackDocumentKey(result.CallbackUserData, documentKey);
if (openingDocumentKeyRef.current !== callbackDocumentKey) return;
setIsBusy(true);
openingDocumentKeyRef.current = documentKey;
attemptedDocumentKeyRef.current = documentKey;
try {
const documentFile = new File([blob], fileName, {
type: HWPX_CONTENT_TYPE,
});
const isAccepted = ctrl.Open(
documentFile,
"HWPX",
"",
(result) => {
const callbackDocumentKey = getCallbackDocumentKey(result.CallbackUserData, documentKey);
if (openingDocumentKeyRef.current !== callbackDocumentKey) return;
openingDocumentKeyRef.current = null;
setIsBusy(false);
if (!result.result) return;
openingDocumentKeyRef.current = null;
setIsBusy(false);
if (!result.result) return;
openedDocumentKeyRef.current = callbackDocumentKey;
},
{ documentKey } satisfies DocumentCallbackUserData,
);
openedDocumentKeyRef.current = callbackDocumentKey;
},
{ documentKey } satisfies DocumentCallbackUserData,
);
if (isAccepted) return;
if (isAccepted) return;
openingDocumentKeyRef.current = null;
setIsBusy(false);
} catch {
openingDocumentKeyRef.current = null;
setIsBusy(false);
}
}, [documentBlob, documentFileName, documentKey, isCtrlReady]);
openingDocumentKeyRef.current = null;
setIsBusy(false);
} catch {
openingDocumentKeyRef.current = null;
setIsBusy(false);
}
},
[documentKey, isCtrlReady],
);
useEffect(() => {
if (
@@ -117,8 +120,19 @@ function HangulGiangi({
) {
return;
}
handleOpen();
}, [documentKey, handleOpen, isCtrlReady]);
openHwpxBlob(documentBlob, documentFileName);
}, [documentBlob, documentFileName, documentKey, isCtrlReady, openHwpxBlob]);
const handleToolbarOpen = useCallback(
(file: File) => {
if (!file.name.toLowerCase().endsWith(".hwpx")) {
alert("HWPX 파일만 열 수 있습니다.");
return;
}
openHwpxBlob(file, file.name);
},
[openHwpxBlob],
);
const handleSave = useCallback(() => {
const ctrl = resolveHwpCtrl(iframeRef.current, isCtrlReady);
@@ -185,6 +199,7 @@ function HangulGiangi({
<HangulGiangiToolbar
isBusy={isBusy || !isCtrlReady}
canSave={canSave}
onOpen={handleToolbarOpen}
onSave={handleSave}
onDownload={handleDownload}
onFullscreen={handleFullscreen}