리사이징, 체크박스,엔터치면 다음 칸으로 이동, 표수정, 컬럼에서 이미지 넣는거 등등

This commit is contained in:
leeheejin
2025-11-06 12:11:49 +09:00
parent 0b676098a5
commit 0839f7f603
38 changed files with 1285 additions and 260 deletions

View File

@@ -1,9 +1,10 @@
"use client";
import React, { useState, useCallback } from "react";
import React, { useState, useCallback, useEffect } from "react";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { ResizableDialog, ResizableDialogContent, ResizableDialogHeader, DialogTitle } from "@/components/ui/dialog";
import { ResizableDialog, ResizableDialogContent, ResizableDialogHeader } from "@/components/ui/resizable-dialog";
import { DialogTitle, DialogHeader } from "@/components/ui/dialog";
import { useAuth } from "@/hooks/useAuth";
import { uploadFilesAndCreateData } from "@/lib/api/file";
import { toast } from "sonner";
@@ -120,6 +121,67 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
[userName],
);
// 🆕 Enter 키로 다음 필드 이동
useEffect(() => {
const handleEnterKey = (e: KeyboardEvent) => {
if (e.key === "Enter" && !e.shiftKey) {
const target = e.target as HTMLElement;
// textarea는 제외 (여러 줄 입력)
if (target.tagName === "TEXTAREA") {
return;
}
// input, select 등의 폼 요소에서만 작동
if (
target.tagName === "INPUT" ||
target.tagName === "SELECT" ||
target.getAttribute("role") === "combobox"
) {
e.preventDefault();
// 모든 포커스 가능한 요소 찾기
const focusableElements = document.querySelectorAll<HTMLElement>(
'input:not([disabled]):not([type="hidden"]), select:not([disabled]), textarea:not([disabled]), [role="combobox"]:not([disabled])'
);
// 화면에 보이는 순서(Y 좌표 → X 좌표)대로 정렬
const focusableArray = Array.from(focusableElements).sort((a, b) => {
const rectA = a.getBoundingClientRect();
const rectB = b.getBoundingClientRect();
// Y 좌표 차이가 10px 이상이면 Y 좌표로 정렬 (위에서 아래로)
if (Math.abs(rectA.top - rectB.top) > 10) {
return rectA.top - rectB.top;
}
// 같은 줄이면 X 좌표로 정렬 (왼쪽에서 오른쪽으로)
return rectA.left - rectB.left;
});
const currentIndex = focusableArray.indexOf(target);
if (currentIndex !== -1 && currentIndex < focusableArray.length - 1) {
// 다음 요소로 포커스 이동
const nextElement = focusableArray[currentIndex + 1];
nextElement.focus();
// input이면 전체 선택
if (nextElement.tagName === "INPUT") {
(nextElement as HTMLInputElement).select();
}
}
}
}
};
document.addEventListener("keydown", handleEnterKey);
return () => {
document.removeEventListener("keydown", handleEnterKey);
};
}, []);
// 🆕 autoFill 자동 입력 초기화
React.useEffect(() => {
const initAutoInputFields = async () => {
@@ -630,11 +692,17 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
{/* 팝업 화면 렌더링 */}
{popupScreen && (
<Dialog open={!!popupScreen} onOpenChange={() => setPopupScreen(null)}>
<DialogContent
className={` ${
popupScreen.size === "small" ? "max-w-md" : popupScreen.size === "large" ? "max-w-6xl" : "max-w-4xl"
} max-h-[90vh] overflow-y-auto`}
<ResizableDialog open={!!popupScreen} onOpenChange={() => setPopupScreen(null)}>
<ResizableDialogContent
className="overflow-hidden p-0"
defaultWidth={popupScreen.size === "small" ? 600 : popupScreen.size === "large" ? 1400 : 1000}
defaultHeight={800}
minWidth={500}
minHeight={400}
maxWidth={1600}
maxHeight={1200}
modalId={`popup-screen-${popupScreen.screenId}`}
userId={user?.userId || "guest"}
>
<DialogHeader>
<DialogTitle>{popupScreen.title}</DialogTitle>
@@ -668,8 +736,8 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
))}
</div>
)}
</DialogContent>
</Dialog>
</ResizableDialogContent>
</ResizableDialog>
)}
</>
);