feat: 공정실행 카드 긴 텍스트 자동 슬라이드
AutoScrollText 컴포넌트: 텍스트가 영역을 넘으면 자동 마키 애니메이션 - 제품명(제품코드) 긴 경우 자동 슬라이드 - 짧으면 정지 상태로 표시 - 접수가능 카드 + 진행중 카드 모두 적용
This commit is contained in:
@@ -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>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user