ERP-node/frontend/components/report/designer/Ruler.tsx

149 lines
3.5 KiB
TypeScript

"use client";
import { JSX } from "react";
interface RulerProps {
orientation: "horizontal" | "vertical";
length: number; // mm 단위
offset?: number; // 스크롤 오프셋 (px)
}
// 고정 스케일 팩터 (화면 해상도와 무관)
const MM_TO_PX = 4;
export function Ruler({ orientation, length, offset = 0 }: RulerProps) {
// mm를 px로 변환
const mmToPx = (mm: number) => mm * MM_TO_PX;
const lengthPx = mmToPx(length);
const isHorizontal = orientation === "horizontal";
// 눈금 생성 (10mm 단위 큰 눈금, 5mm 단위 중간 눈금, 1mm 단위 작은 눈금)
const renderTicks = () => {
const ticks: JSX.Element[] = [];
const maxMm = length;
for (let mm = 0; mm <= maxMm; mm++) {
const px = mmToPx(mm);
// 10mm 단위 큰 눈금
if (mm % 10 === 0) {
ticks.push(
<div
key={`major-${mm}`}
className="absolute bg-gray-700"
style={
isHorizontal
? {
left: `${px}px`,
top: "0",
width: "1px",
height: "12px",
}
: {
top: `${px}px`,
left: "0",
height: "1px",
width: "12px",
}
}
/>,
);
// 숫자 표시 (10mm 단위)
if (mm > 0) {
ticks.push(
<div
key={`label-${mm}`}
className="absolute text-[9px] text-gray-600"
style={
isHorizontal
? {
left: `${px + 2}px`,
top: "0px",
}
: {
top: `${px + 2}px`,
left: "0px",
writingMode: "vertical-lr",
}
}
>
{mm}
</div>,
);
}
}
// 5mm 단위 중간 눈금
else if (mm % 5 === 0) {
ticks.push(
<div
key={`medium-${mm}`}
className="absolute bg-gray-500"
style={
isHorizontal
? {
left: `${px}px`,
top: "4px",
width: "1px",
height: "8px",
}
: {
top: `${px}px`,
left: "4px",
height: "1px",
width: "8px",
}
}
/>,
);
}
// 1mm 단위 작은 눈금
else {
ticks.push(
<div
key={`minor-${mm}`}
className="absolute bg-gray-400"
style={
isHorizontal
? {
left: `${px}px`,
top: "8px",
width: "1px",
height: "4px",
}
: {
top: `${px}px`,
left: "8px",
height: "1px",
width: "4px",
}
}
/>,
);
}
}
return ticks;
};
return (
<div
className="relative bg-gray-100 select-none"
style={
isHorizontal
? {
width: `${lengthPx}px`,
height: "20px",
}
: {
width: "20px",
height: `${lengthPx}px`,
}
}
>
{renderTicks()}
</div>
);
}