feat: 공정실행 카드 긴 텍스트 자동 슬라이드

AutoScrollText 컴포넌트: 텍스트가 영역을 넘으면 자동 마키 애니메이션
- 제품명(제품코드) 긴 경우 자동 슬라이드
- 짧으면 정지 상태로 표시
- 접수가능 카드 + 진행중 카드 모두 적용
This commit is contained in:
SeongHyun Kim
2026-04-08 11:16:24 +09:00
parent a96d5ac2c1
commit 659bd9caad
@@ -1,6 +1,6 @@
"use client";
import React, { useState, useEffect, useCallback, useMemo } from "react";
import React, { useState, useEffect, useCallback, useMemo, useRef } from "react";
import { useRouter } from "next/navigation";
import { useAuth } from "@/hooks/useAuth";
import { apiClient } from "@/lib/api/client";
@@ -9,6 +9,41 @@ import { AcceptProcessModal } from "./AcceptProcessModal";
import { ProcessDetailModal, ProcessStep } from "./ProcessDetailModal";
import { ProcessWork } from "./ProcessWork";
/* 텍스트가 넘칠 때 자동 슬라이드 (마키) */
function AutoScrollText({ children, className }: { children: React.ReactNode; className?: string }) {
const outerRef = useRef<HTMLDivElement>(null);
const innerRef = useRef<HTMLSpanElement>(null);
const [needsScroll, setNeedsScroll] = useState(false);
useEffect(() => {
const check = () => {
if (outerRef.current && innerRef.current) {
setNeedsScroll(innerRef.current.scrollWidth > outerRef.current.clientWidth + 2);
}
};
check();
window.addEventListener("resize", check);
return () => window.removeEventListener("resize", check);
}, [children]);
return (
<div ref={outerRef} className={`overflow-hidden whitespace-nowrap ${className || ""}`}>
<span
ref={innerRef}
className="inline-block"
style={needsScroll ? { animation: "cardMarquee 8s linear infinite" } : undefined}
>
{children}
{needsScroll && <span className="inline-block w-12" />}
{needsScroll && children}
</span>
{needsScroll && (
<style>{`@keyframes cardMarquee { 0% { transform: translateX(0); } 100% { transform: translateX(-50%); } }`}</style>
)}
</div>
);
}
/* ------------------------------------------------------------------ */
/* Types */
/* ------------------------------------------------------------------ */
@@ -176,9 +211,9 @@ function FullscreenWorkModal({
<div className="text-base font-bold text-gray-900 mb-1">
{wi?.work_instruction_no || "작업지시"}
</div>
<div className="text-sm text-gray-500 mb-0.5 truncate">
{wi?.item_name || ""}{(wi?.item_code || wi?.item_number) ? `(${wi?.item_code || wi?.item_number})` : ""}
</div>
<AutoScrollText className="text-sm text-gray-500 mb-0.5">
📦 {wi?.item_name || ""}{(wi?.item_code || wi?.item_number) ? `(${wi?.item_code || wi?.item_number})` : ""}
</AutoScrollText>
<div className="text-sm text-gray-600 mb-0.5">
{proc.process_name} · {proc.equipment_code || "미배정"}
</div>
@@ -1401,8 +1436,10 @@ export function WorkOrderList() {
</div>
{/* Sub-info: item name + equipment */}
<div className="flex items-center gap-3 text-sm text-gray-500 mb-3">
<span className="truncate">📦 {wi?.item_name || "품목"}{(wi?.item_code || wi?.item_number) ? `(${wi?.item_code || wi?.item_number})` : ""}</span>
<div className="flex items-center gap-3 text-sm text-gray-500 mb-3 min-w-0">
<AutoScrollText className="flex-1 min-w-0">
📦 {wi?.item_name || "품목"}{(wi?.item_code || wi?.item_number) ? `(${wi?.item_code || wi?.item_number})` : ""}
</AutoScrollText>
{!isRework && (
<span className="shrink-0"> {eqName}</span>
)}