진행중
This commit is contained in:
@@ -281,7 +281,7 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
||||
const companyCode = (user as ExtendedUserInfo)?.company_code;
|
||||
|
||||
if (companyCode === "*") {
|
||||
setCurrentCompanyName("WACE (최고 관리자)");
|
||||
setCurrentCompanyName("Invyone (최고 관리자)");
|
||||
} else if (companyCode) {
|
||||
try {
|
||||
const response = await apiClient.get("/admin/companies/db");
|
||||
@@ -455,28 +455,84 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
||||
toast.warning("이 메뉴에 할당된 화면이 없습니다. 메뉴 설정을 확인해주세요.");
|
||||
};
|
||||
|
||||
const handleModeSwitch = useCallback(() => {
|
||||
const handleModeSwitch = useCallback((e?: React.MouseEvent<HTMLButtonElement>) => {
|
||||
if (modeTransition !== "idle") return;
|
||||
|
||||
// 레퍼런스 invion-layout-v5.html switchMode() 를 그대로 따름:
|
||||
// Phase 1 (0ms) : mode-fade 오버레이 페이드인 + 사이드바 slide-out + 탭 fade-out
|
||||
// Phase 2 (300ms) : 모드 swap (React 재렌더) → 새 사이드바/탭 등장
|
||||
// Phase 3 (600ms) : 오버레이 페이드아웃 + 정리
|
||||
const fade = document.getElementById("v5-mode-fade");
|
||||
// 강화된 mode transition — 옵션 b/c/e/f 적용 (d 버튼 burst 제거, mode-fade overlay 도 제거):
|
||||
// Phase 1 (0ms): 사이드바 items morph-out (stagger), 헤더 glow flash,
|
||||
// breadcrumb swap-out, 이탈 시 admin badge zoom-out
|
||||
// Phase 2 (350ms): React 가 모드 swap → 새 메뉴 morph-in (stagger), breadcrumb swap-in,
|
||||
// 진입 시 admin badge zoom-in
|
||||
// Phase 3 (~950ms): 모든 클래스 정리, idle 복귀
|
||||
const goingToAdmin = !isAdminMode;
|
||||
setModeTransition("out");
|
||||
fade?.classList.add("in");
|
||||
|
||||
// (b) 사이드바 items morph-out — stagger
|
||||
const oldItems = Array.from(document.querySelectorAll<HTMLElement>(".v5-side .v5-si"));
|
||||
oldItems.forEach((it, i) => {
|
||||
it.style.animationDelay = `${i * 35}ms`;
|
||||
it.classList.add("mode-morph-out");
|
||||
});
|
||||
|
||||
// (c) 헤더 glow line flash
|
||||
const hdrGlow = document.querySelector<HTMLElement>(".v5-hdr-glow");
|
||||
if (hdrGlow) {
|
||||
hdrGlow.classList.remove("mode-flash");
|
||||
void hdrGlow.offsetWidth; // reflow → 재시작 가능하게
|
||||
hdrGlow.classList.add("mode-flash");
|
||||
}
|
||||
|
||||
// (d) 토글 버튼 burst 효과는 제거됨 — 가운데 밝아지는 게 거슬려서 통째로 뺌
|
||||
|
||||
// (e) breadcrumb swap-out
|
||||
const bc = document.querySelector<HTMLElement>(".v5-hdr-bc");
|
||||
bc?.classList.remove("mode-swap-in");
|
||||
bc?.classList.add("mode-swap-out");
|
||||
|
||||
// (f) admin badge zoom-out (이탈 시에만)
|
||||
if (!goingToAdmin) {
|
||||
const badge = document.querySelector<HTMLElement>(".v5-admin-badge");
|
||||
badge?.classList.remove("mode-zoom-in");
|
||||
badge?.classList.add("mode-zoom-out");
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
// Phase 2: swap mode — React re-renders with new menus/tabs
|
||||
// Phase 2: 모드 swap → React 재렌더 (새 메뉴/탭/breadcrumb)
|
||||
setTabMode(isAdminMode ? "user" : "admin");
|
||||
setModeTransition("in");
|
||||
|
||||
// Phase 3: 오버레이 페이드아웃 후 정리
|
||||
// 다음 프레임에 새 items 에 morph-in 적용
|
||||
requestAnimationFrame(() => {
|
||||
const newItems = Array.from(document.querySelectorAll<HTMLElement>(".v5-side .v5-si"));
|
||||
newItems.forEach((it, i) => {
|
||||
it.style.animationDelay = `${i * 45}ms`;
|
||||
it.classList.add("mode-morph-in");
|
||||
});
|
||||
|
||||
// breadcrumb swap-in (텍스트는 이미 새 모드 기준)
|
||||
const newBc = document.querySelector<HTMLElement>(".v5-hdr-bc");
|
||||
newBc?.classList.remove("mode-swap-out");
|
||||
newBc?.classList.add("mode-swap-in");
|
||||
|
||||
// (f) admin badge zoom-in (진입 시에만)
|
||||
if (goingToAdmin) {
|
||||
const newBadge = document.querySelector<HTMLElement>(".v5-admin-badge");
|
||||
newBadge?.classList.remove("mode-zoom-out");
|
||||
newBadge?.classList.add("mode-zoom-in");
|
||||
}
|
||||
});
|
||||
|
||||
// Phase 3: 모든 애니메이션 클래스 정리
|
||||
setTimeout(() => {
|
||||
fade?.classList.remove("in");
|
||||
setModeTransition("idle");
|
||||
}, 300);
|
||||
}, 300);
|
||||
document.querySelectorAll<HTMLElement>(".v5-side .v5-si").forEach((it) => {
|
||||
it.classList.remove("mode-morph-in", "mode-morph-out");
|
||||
it.style.animationDelay = "";
|
||||
});
|
||||
document.querySelector<HTMLElement>(".v5-hdr-bc")?.classList.remove("mode-swap-in", "mode-swap-out");
|
||||
document.querySelector<HTMLElement>(".v5-admin-badge")?.classList.remove("mode-zoom-in", "mode-zoom-out");
|
||||
}, 600);
|
||||
}, 350);
|
||||
}, [isAdminMode, setTabMode, modeTransition]);
|
||||
|
||||
const handleLogout = async () => {
|
||||
@@ -727,9 +783,6 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
||||
{/* Theme fade overlay */}
|
||||
<div className="v5-theme-fade" id="v5-theme-fade" />
|
||||
|
||||
{/* Mode transition fade overlay — radial gradient 으로 화면을 한 번 덮어 사이드바/탭 swap 을 가림 */}
|
||||
<div className="v5-mode-fade" id="v5-mode-fade" />
|
||||
|
||||
{/* V5 Shell */}
|
||||
<div className={`v5-shell ${isAdminMode ? "v5-admin-mode" : ""}`}>
|
||||
{/* ===== Glass Header ===== */}
|
||||
@@ -739,7 +792,7 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
||||
<button className="v5-mobile-toggle" onClick={() => setSidebarOpen(!sidebarOpen)}>
|
||||
<Menu size={16} />
|
||||
</button>
|
||||
<div className="v5-hdr-logo">INVION</div>
|
||||
<div className="v5-hdr-logo">Invy.one</div>
|
||||
<div className="v5-hdr-bc">
|
||||
{isAdminMode ? "관리자" : "홈"} › <b>{breadcrumbText}</b>
|
||||
</div>
|
||||
@@ -748,6 +801,8 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
||||
관리자 모드
|
||||
</div>
|
||||
</div>
|
||||
{/* mode transition 헤더 glow 라인 — 평소엔 opacity 0, mode change 시에만 flash */}
|
||||
<div className="v5-hdr-glow" />
|
||||
<div className="v5-hdr-r">
|
||||
{/* Theme pill */}
|
||||
<div className="v5-pill">
|
||||
@@ -773,9 +828,9 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
||||
<div className="v5-bell-dot" />
|
||||
</button>
|
||||
|
||||
{/* Admin toggle (gear ↔ home) */}
|
||||
{/* Admin toggle (gear ↔ home) — 이벤트 전달해서 burst origin 으로 사용 */}
|
||||
{isAdmin && (
|
||||
<button className="v5-admin-btn" onClick={handleModeSwitch} title={isAdminMode ? "홈으로" : "관리자"}>
|
||||
<button className="v5-admin-btn" onClick={(e) => handleModeSwitch(e)} title={isAdminMode ? "홈으로" : "관리자"}>
|
||||
<Settings size={14} className="ic-gear" />
|
||||
<Home size={14} className="ic-home" />
|
||||
<span className="v5-admin-label">{isAdminMode ? "홈으로" : "관리자"}</span>
|
||||
|
||||
Reference in New Issue
Block a user