260723_3
This commit is contained in:
@@ -161,6 +161,12 @@ export function createIrregularStationsSection(
|
||||
});
|
||||
}
|
||||
|
||||
/** 잔여거리는 측점 간격을 넘을 수 없다(넘으면 다음 측점이 됨). max·검증에 함께 쓴다. */
|
||||
function intervalMax(): number {
|
||||
const interval = callbacks.getInterval();
|
||||
return interval > 0 ? interval : 20;
|
||||
}
|
||||
|
||||
function commit(): void {
|
||||
const station = Number.parseInt(stationField.value, 10);
|
||||
const remainder = Number.parseFloat(remainderField.value || "0");
|
||||
@@ -168,7 +174,10 @@ export function createIrregularStationsSection(
|
||||
stationField.focus();
|
||||
return;
|
||||
}
|
||||
const safeRemainder = Number.isFinite(remainder) && remainder >= 0 ? remainder : 0;
|
||||
// 잔여거리는 [0, 측점간격] 범위로 클램프한다.
|
||||
const safeRemainder = Number.isFinite(remainder)
|
||||
? Math.min(Math.max(remainder, 0), intervalMax())
|
||||
: 0;
|
||||
const structure = structureField.value.trim();
|
||||
const chainage_m = chainageOf(station, safeRemainder);
|
||||
if (editingId) {
|
||||
@@ -189,6 +198,14 @@ export function createIrregularStationsSection(
|
||||
callbacks.onChange([...stations]);
|
||||
}
|
||||
|
||||
// 측점 간격은 옵션에서 바뀔 수 있으므로, 잔여거리의 max를 입력 직전에 현재 간격으로 맞춘다.
|
||||
const syncRemainderMax = (): void => {
|
||||
remainderField.max = String(intervalMax());
|
||||
};
|
||||
remainderField.addEventListener("focus", syncRemainderMax);
|
||||
remainderField.addEventListener("input", syncRemainderMax);
|
||||
syncRemainderMax();
|
||||
|
||||
primary.addEventListener("click", commit);
|
||||
remove.addEventListener("click", () => {
|
||||
if (!editingId) return;
|
||||
|
||||
@@ -174,13 +174,23 @@ export async function renderB05Route(root: HTMLElement): Promise<void> {
|
||||
syncIrregularSelection(stationId);
|
||||
});
|
||||
|
||||
/** 그래프·3D에서 비정규 측점을 고르면 사이드바 입력 폼에 로드해 수정/삭제할 수 있게 한다. */
|
||||
/**
|
||||
* 그래프·3D에서 비정규 측점을 고르면 사이드바 입력 폼에 로드해 수정/삭제할 수 있게 한다.
|
||||
* 선택은 3D·그래프·사이드바가 서로를 갱신하므로, `selectionSyncing` 가드로 재진입을 막아
|
||||
* 무한 재귀(스택 오버플로우·프리즈)를 방지한다.
|
||||
*/
|
||||
function syncIrregularSelection(stationId: string | null): void {
|
||||
if (selectionSyncing) return;
|
||||
const prefix = irregularStationId("");
|
||||
if (!stationId?.startsWith(prefix)) return;
|
||||
const id = stationId.slice(prefix.length);
|
||||
const station = irregularStations.find((entry) => entry.id === id);
|
||||
panel.irregularStations.selectByChainage(station ? station.chainage_m : null);
|
||||
selectionSyncing = true;
|
||||
try {
|
||||
panel.irregularStations.selectByChainage(station ? station.chainage_m : null);
|
||||
} finally {
|
||||
selectionSyncing = false;
|
||||
}
|
||||
}
|
||||
let confirmedSurface: SurfaceModelSummary | null = null;
|
||||
let latest: RouteLatestResponse | null = null;
|
||||
@@ -190,6 +200,8 @@ export async function renderB05Route(root: HTMLElement): Promise<void> {
|
||||
let stale = false;
|
||||
let restoring = true;
|
||||
let irregularStations: IrregularStation[] = [];
|
||||
// 선택 동기화 재진입 가드(3D↔그래프↔사이드바 상호 갱신의 무한 재귀 차단).
|
||||
let selectionSyncing = false;
|
||||
|
||||
const panel = createRoutePanel({
|
||||
onSolve: () => void solve(),
|
||||
@@ -207,9 +219,15 @@ export async function renderB05Route(root: HTMLElement): Promise<void> {
|
||||
onInputChange: markStale,
|
||||
onIrregularChange: (stations) => applyIrregularStations(stations),
|
||||
onIrregularSelect: (station) => {
|
||||
const id = station ? irregularStationId(station.id) : null;
|
||||
viewer.markers.selectStation(id);
|
||||
profilePanel.setSelectedStation(id);
|
||||
if (selectionSyncing) return;
|
||||
selectionSyncing = true;
|
||||
try {
|
||||
const id = station ? irregularStationId(station.id) : null;
|
||||
viewer.markers.selectStation(id);
|
||||
profilePanel.setSelectedStation(id);
|
||||
} finally {
|
||||
selectionSyncing = false;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -241,17 +241,7 @@ export function createRoutePanel(callbacks: PanelCallbacks) {
|
||||
minUphillGrade.wrapper,
|
||||
minDownhillGrade.wrapper,
|
||||
);
|
||||
const help = document.createElement("details");
|
||||
help.innerHTML =
|
||||
"<summary>최적경로란?</summary><p>배치 포인트와 경사·곡선·회피 조건을 만족하며 비용을 최소화한 경로입니다.</p>";
|
||||
conditions.body.append(
|
||||
algorithmLabel,
|
||||
gradeLabel,
|
||||
paved.wrapper,
|
||||
avoidPass.wrapper,
|
||||
details,
|
||||
help,
|
||||
);
|
||||
conditions.body.append(algorithmLabel, gradeLabel, paved.wrapper, avoidPass.wrapper, details);
|
||||
|
||||
const sectionOptions = section(L("B05_Route_Group_SectionOptions"));
|
||||
const stationInterval = numberField(L("B05_Route_Field_StationInterval"));
|
||||
@@ -290,11 +280,7 @@ export function createRoutePanel(callbacks: PanelCallbacks) {
|
||||
startElevationOffset.wrapper,
|
||||
endElevationOffset.wrapper,
|
||||
);
|
||||
const gradeHelp = document.createElement("details");
|
||||
gradeHelp.innerHTML =
|
||||
"<summary>계획선이란?</summary><p>공사 후 노면이 될 높이입니다. 직선과 종단곡선만으로 구성되며, " +
|
||||
"절토량과 성토량이 균형을 이루도록(적분값 0) 자동 산출됩니다.</p>";
|
||||
gradeLine.body.append(terrainLabel, criteriaNote, gradeAdvanced, gradeHelp);
|
||||
gradeLine.body.append(terrainLabel, criteriaNote, gradeAdvanced);
|
||||
|
||||
// 비정규 측점(구조물 측점) — 측점번호+잔여거리로 추가/수정/삭제. 목록 변경은 Page로 올려
|
||||
// 그래프·테이블·3D에 반영한다. chainage 환산 기준인 측점간격은 실시간 조회한다.
|
||||
@@ -369,6 +355,16 @@ export function createRoutePanel(callbacks: PanelCallbacks) {
|
||||
actionRow,
|
||||
);
|
||||
|
||||
// 컨테이너 제목(h3) 클릭 시 본문을 접거나 편다(제목은 남긴다). 본문 안의 details 등 별도
|
||||
// 접힘 항목은 손대지 않으므로 열려 있으면 그대로 보인다.
|
||||
root.addEventListener("click", (event) => {
|
||||
const heading = (event.target as HTMLElement).closest("h3");
|
||||
const container = heading?.parentElement;
|
||||
if (heading && container?.classList.contains("b05-route__panel-section")) {
|
||||
container.classList.toggle("is-collapsed");
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
root,
|
||||
viewControls,
|
||||
|
||||
@@ -393,11 +393,12 @@ export function createRouteProfilePanel(
|
||||
labelWidth: LONG_PAD.left,
|
||||
rowCount: TABLE_ROW_COUNT,
|
||||
x,
|
||||
// 비정규 측점은 규칙 격자를 건드리지 않고 주석(파선 세로선+라벨+구조물)으로 얹는다.
|
||||
// 비정규 측점은 규칙 격자를 건드리지 않고, 선택된 측점만 값 열로 오버레이한다.
|
||||
irregularStations: irregularStations.filter(
|
||||
(entry) =>
|
||||
entry.chainage_m >= 0 && entry.chainage_m <= maxChainageOf(longitudinal) + 1e-6,
|
||||
),
|
||||
selectedStationId,
|
||||
onCurveRadiusChange: (curve, radius) =>
|
||||
applyEdits(setCurveRadius(store.edits(), curve, radius ?? 0)),
|
||||
})
|
||||
|
||||
@@ -18,7 +18,11 @@ import type {
|
||||
ProfileAlignment,
|
||||
} from "./B05_wf2_Route_UI_Profile_Alignment";
|
||||
import { stationLabel } from "../B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Section_Common";
|
||||
import { irregularLabel, type IrregularStation } from "./B05_wf2_Route_UI_IrregularStations";
|
||||
import {
|
||||
irregularLabel,
|
||||
irregularStationId,
|
||||
type IrregularStation,
|
||||
} from "./B05_wf2_Route_UI_IrregularStations";
|
||||
|
||||
export interface ProfileTableOptions {
|
||||
alignment: ProfileAlignment;
|
||||
@@ -33,8 +37,10 @@ export interface ProfileTableOptions {
|
||||
rowCount: number;
|
||||
/** 종단면도와 공유하는 chainage → x(px) 매핑. */
|
||||
x: (chainageM: number) => number;
|
||||
/** 규칙 격자 밖 비정규 측점(구조물). 격자를 건드리지 않고 주석으로 얹는다. */
|
||||
/** 규칙 격자 밖 비정규 측점(구조물). 선택된 측점만 값 열로 오버레이한다. */
|
||||
irregularStations?: IrregularStation[];
|
||||
/** 현재 선택된 측점 id. 비정규 측점이면 그 측점의 값 열을 테이블에 겹쳐 보여준다. */
|
||||
selectedStationId?: string | null;
|
||||
onCurveRadiusChange: (curve: AlignmentCurve, radiusM: number | null) => void;
|
||||
}
|
||||
|
||||
@@ -238,9 +244,13 @@ function buildCurveRows(options: ProfileTableOptions, centers: number[]): HTMLEl
|
||||
const lengthRow = createRow(
|
||||
"b05-profile-table__row--curve is-group-start",
|
||||
"곡선 L",
|
||||
"m 종단곡선 길이 (R × |대수차|)",
|
||||
"m 종단곡선 길이 (입력) — L·R은 서로 연동",
|
||||
);
|
||||
const radiusRow = createRow(
|
||||
"b05-profile-table__row--curve",
|
||||
"곡선 R",
|
||||
"m 종단곡선 반경 (입력) — L·R은 서로 연동",
|
||||
);
|
||||
const radiusRow = createRow("b05-profile-table__row--curve", "곡선 R", "m 종단곡선 반경 (입력)");
|
||||
const curveByChainage = new Map(
|
||||
alignment.curves.map((curve) => [curve.chainage_m.toFixed(3), curve]),
|
||||
);
|
||||
@@ -250,21 +260,22 @@ function buildCurveRows(options: ProfileTableOptions, centers: number[]): HTMLEl
|
||||
const radiusCell = element("span", "b05-profile-table__curve");
|
||||
const curve = curveByChainage.get(station.chainage_m.toFixed(3));
|
||||
if (curve) {
|
||||
lengthCell.textContent = curve.l_m.toFixed(2);
|
||||
lengthCell.title = curveTitle(curve);
|
||||
|
||||
const input = document.createElement("input");
|
||||
input.type = "number";
|
||||
input.step = "1";
|
||||
input.min = "1";
|
||||
input.className = "b05-profile-table__radius";
|
||||
input.value = curve.r_m.toFixed(1);
|
||||
input.title = `종단곡선 반경 R (m) — 이 값이 기준이고 곡선길이 L이 따라 계산됩니다.\n${curveTitle(curve)}`;
|
||||
input.addEventListener("change", () => {
|
||||
const parsed = Number.parseFloat(input.value);
|
||||
onCurveRadiusChange(curve, Number.isFinite(parsed) && parsed > 0 ? parsed : null);
|
||||
});
|
||||
radiusCell.append(input);
|
||||
// L·R은 L = R × |대수차| 로 연동된다. 둘 다 입력 가능하고, 어느 쪽을 고쳐도 R로 환산해
|
||||
// 같은 파이프라인(onCurveRadiusChange)을 태운다. 재계산 후 마지막에 입력한 값이 반영된다.
|
||||
// L 입력 → R = L × (r_m / l_m) (현재 곡선의 L:R 비율 = 1/|대수차|, 단위 무관).
|
||||
radiusCell.append(
|
||||
curveInput(
|
||||
curve,
|
||||
curve.r_m.toFixed(1),
|
||||
`종단곡선 반경 R (m)\n${curveTitle(curve)}`,
|
||||
(r) => r,
|
||||
),
|
||||
);
|
||||
lengthCell.append(
|
||||
curveInput(curve, curve.l_m.toFixed(2), `종단곡선 길이 L (m)\n${curveTitle(curve)}`, (l) =>
|
||||
curve.l_m > 1e-9 ? (l * curve.r_m) / curve.l_m : null,
|
||||
),
|
||||
);
|
||||
|
||||
[lengthCell, radiusCell].forEach((cell) => {
|
||||
if (curve.skip_allowed) cell.classList.add("is-optional");
|
||||
@@ -274,42 +285,98 @@ function buildCurveRows(options: ProfileTableOptions, centers: number[]): HTMLEl
|
||||
placeCell(lengthRow, centers[index], lengthCell);
|
||||
placeCell(radiusRow, centers[index], radiusCell);
|
||||
});
|
||||
|
||||
function curveInput(
|
||||
curve: AlignmentCurve,
|
||||
value: string,
|
||||
title: string,
|
||||
toRadius: (parsed: number) => number | null,
|
||||
): HTMLInputElement {
|
||||
const input = document.createElement("input");
|
||||
input.type = "number";
|
||||
input.step = "any";
|
||||
input.min = "0";
|
||||
// no-spin: 상·하 토글 버튼 제거(항상 사용자가 값 직접 입력).
|
||||
input.className = "b05-profile-table__radius b05-profile-table__no-spin";
|
||||
input.value = value;
|
||||
input.title = title;
|
||||
input.addEventListener("change", () => {
|
||||
const parsed = Number.parseFloat(input.value);
|
||||
const radius = Number.isFinite(parsed) && parsed > 0 ? toRadius(parsed) : null;
|
||||
onCurveRadiusChange(curve, radius && radius > 0 ? radius : null);
|
||||
});
|
||||
return input;
|
||||
}
|
||||
|
||||
return [lengthRow, radiusRow];
|
||||
}
|
||||
|
||||
/**
|
||||
* 비정규 측점 주석층: 규칙 격자 위에 파선 세로선 + (구조물)라벨을 얹는다.
|
||||
* 라벨은 이웃과 가까우면 상·하 2슬롯으로 번갈아 내려 겹침을 피한다(2행 스태거).
|
||||
*/
|
||||
function buildIrregularAnnotations(
|
||||
stations: IrregularStation[],
|
||||
x: (chainageM: number) => number,
|
||||
cellWidth: number,
|
||||
): HTMLElement[] {
|
||||
const sorted = [...stations].sort((a, b) => a.chainage_m - b.chainage_m);
|
||||
const nodes: HTMLElement[] = [];
|
||||
let previousX = -Infinity;
|
||||
let row = 0;
|
||||
sorted.forEach((station) => {
|
||||
const centerX = x(station.chainage_m);
|
||||
const line = element("div", "b05-profile-table__irregular-line");
|
||||
line.style.left = `${centerX}px`;
|
||||
nodes.push(line);
|
||||
/** 정렬된 계획선 샘플에서 chainage 위치 값을 선형보간한다(범위 밖은 양 끝 클램프). */
|
||||
function interpolateSample(
|
||||
samples: ProfileAlignment["samples"],
|
||||
chainageM: number,
|
||||
key: "elevation_m" | "ground_elevation_m",
|
||||
): number | null {
|
||||
if (!samples.length) return null;
|
||||
if (chainageM <= samples[0].chainage_m) return samples[0][key];
|
||||
const last = samples[samples.length - 1];
|
||||
if (chainageM >= last.chainage_m) return last[key];
|
||||
for (let index = 1; index < samples.length; index += 1) {
|
||||
const current = samples[index];
|
||||
if (current.chainage_m < chainageM) continue;
|
||||
const previous = samples[index - 1];
|
||||
const span = current.chainage_m - previous.chainage_m;
|
||||
if (span <= 1e-9) return current[key];
|
||||
const ratio = (chainageM - previous.chainage_m) / span;
|
||||
return previous[key] + (current[key] - previous[key]) * ratio;
|
||||
}
|
||||
return last[key];
|
||||
}
|
||||
|
||||
row = centerX - previousX < cellWidth ? (row + 1) % 2 : 0;
|
||||
previousX = centerX;
|
||||
const tag = element("div", `b05-profile-table__irregular-tag is-row-${row}`);
|
||||
tag.style.left = `${centerX}px`;
|
||||
tag.append(
|
||||
element("span", "b05-profile-table__irregular-name", irregularLabel(station)),
|
||||
element("span", "b05-profile-table__irregular-desc", station.structure || "구조물"),
|
||||
/**
|
||||
* 선택된 비정규 측점의 **값 열 오버레이**. 규칙 측점 열과 같은 12행 순서로 값(계획고·지반고·
|
||||
* 누가거리·측점 등, 계획선 샘플 보간)을 쌓아 테이블 위에 겹친다. 구조물 이름은 사이드바 입력에
|
||||
* 있으므로 여기엔 넣지 않고, 세로 점선도 두지 않는다.
|
||||
*/
|
||||
function buildIrregularColumn(
|
||||
station: IrregularStation,
|
||||
alignment: ProfileAlignment,
|
||||
centerX: number,
|
||||
cellWidth: number,
|
||||
interval: number,
|
||||
): HTMLElement {
|
||||
const plan = interpolateSample(alignment.samples, station.chainage_m, "elevation_m");
|
||||
const ground = interpolateSample(alignment.samples, station.chainage_m, "ground_elevation_m");
|
||||
const cut = plan !== null && ground !== null && ground - plan > 0.005 ? ground - plan : null;
|
||||
const fill = plan !== null && ground !== null && plan - ground > 0.005 ? plan - ground : null;
|
||||
// 테이블 행 순서(구배 3 · 측점값 7 · 곡선 2)와 정확히 같게 채운다. 없는 값은 공백.
|
||||
const rows: Array<[string, string]> = [
|
||||
["", ""], // 구배 L
|
||||
["", ""], // 구배 H
|
||||
["", "grade"], // 구배 S
|
||||
[cut !== null ? cut.toFixed(2) : "", "cut"], // 절토고
|
||||
[fill !== null ? fill.toFixed(2) : "", "fill"], // 성토고
|
||||
[plan !== null ? plan.toFixed(2) : "", "plan"], // 계획고
|
||||
[ground !== null ? ground.toFixed(2) : "", ""], // 지반고
|
||||
[station.chainage_m.toFixed(2), ""], // 누가거리
|
||||
["", ""], // 거리
|
||||
[stationLabel(station.chainage_m, interval), ""], // 측점
|
||||
["", ""], // 곡선 L
|
||||
["", ""], // 곡선 R
|
||||
];
|
||||
const column = element("div", "b05-profile-table__irregular-col");
|
||||
column.style.left = `${centerX}px`;
|
||||
column.style.width = `${cellWidth}px`;
|
||||
rows.forEach(([text, modifier]) => {
|
||||
const cell = element(
|
||||
"div",
|
||||
`b05-profile-table__irregular-col-cell${modifier ? ` is-${modifier}` : ""}`,
|
||||
text,
|
||||
);
|
||||
tag.title = `비정규 측점 ${irregularLabel(station)} · ${station.chainage_m.toFixed(2)}m${
|
||||
station.structure ? `\n구조물: ${station.structure}` : ""
|
||||
}`;
|
||||
nodes.push(tag);
|
||||
column.append(cell);
|
||||
});
|
||||
return nodes;
|
||||
column.title = `비정규 측점 ${irregularLabel(station)} · ${station.chainage_m.toFixed(2)}m`;
|
||||
return column;
|
||||
}
|
||||
|
||||
export function createProfileTable(options: ProfileTableOptions): HTMLElement {
|
||||
@@ -343,8 +410,20 @@ export function createProfileTable(options: ProfileTableOptions): HTMLElement {
|
||||
table.append(row);
|
||||
});
|
||||
table.append(...buildCurveRows(options, centers));
|
||||
if (options.irregularStations?.length) {
|
||||
table.append(...buildIrregularAnnotations(options.irregularStations, x, cellWidth));
|
||||
// 선택된 비정규 측점만 값 열로 오버레이한다(세로 점선·구조물 태그 없음).
|
||||
const selectedIrregular = options.irregularStations?.find(
|
||||
(entry) => irregularStationId(entry.id) === options.selectedStationId,
|
||||
);
|
||||
if (selectedIrregular) {
|
||||
table.append(
|
||||
buildIrregularColumn(
|
||||
selectedIrregular,
|
||||
alignment,
|
||||
x(selectedIrregular.chainage_m),
|
||||
cellWidth,
|
||||
stationInterval,
|
||||
),
|
||||
);
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
@@ -186,6 +186,13 @@
|
||||
margin: 0;
|
||||
color: var(--color-text);
|
||||
font-size: var(--text-body-sm);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* 제목 클릭으로 접힌 컨테이너: 본문만 감추고 제목은 남긴다. */
|
||||
.b05-route__panel-section.is-collapsed .b05-route__panel-body {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.b05-route__panel-body,
|
||||
@@ -580,55 +587,65 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ─── 비정규 측점 주석 (규칙 격자 위에 얹는 파선 세로선 + 라벨) ──────────── */
|
||||
.b05-profile-table__irregular-line {
|
||||
/* 숫자 입력의 상·하 토글(스피너) 제거 — 항상 사용자가 값 직접 입력. */
|
||||
.b05-profile-table__no-spin::-webkit-outer-spin-button,
|
||||
.b05-profile-table__no-spin::-webkit-inner-spin-button {
|
||||
margin: 0;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
.b05-profile-table__no-spin {
|
||||
-moz-appearance: textfield;
|
||||
appearance: textfield;
|
||||
}
|
||||
|
||||
/* ─── 선택된 비정규 측점 값 열 오버레이 (규칙 열과 같은 12행) ──────────────
|
||||
세로 점선·구조물 태그 없이, 선택 시에만 값 열을 테이블 위에 겹쳐 보여준다. */
|
||||
.b05-profile-table__irregular-col {
|
||||
position: absolute;
|
||||
z-index: 6;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 0;
|
||||
border-left: 1px dashed var(--color-royal-amethyst, rgb(139 92 246));
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
border-inline: 1px solid var(--color-royal-amethyst, rgb(109 40 217));
|
||||
background: color-mix(
|
||||
in srgb,
|
||||
var(--color-royal-amethyst, rgb(109 40 217)) 14%,
|
||||
var(--color-surface)
|
||||
);
|
||||
transform: translateX(-50%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.b05-profile-table__irregular-tag {
|
||||
position: absolute;
|
||||
z-index: 7;
|
||||
.b05-profile-table__irregular-col-cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 0;
|
||||
align-items: center;
|
||||
max-width: 84px;
|
||||
padding: 1px 4px;
|
||||
border: 1px solid
|
||||
color-mix(in srgb, var(--color-royal-amethyst, rgb(109 40 217)) 55%, transparent);
|
||||
border-radius: 3px;
|
||||
background: var(--color-surface-raised);
|
||||
line-height: 1.15;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
/* 2행 스태거: 가까운 라벨은 아래 슬롯으로 내려 겹침을 피한다. */
|
||||
.b05-profile-table__irregular-tag.is-row-0 {
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
.b05-profile-table__irregular-tag.is-row-1 {
|
||||
top: calc(2px + 2.4em);
|
||||
}
|
||||
|
||||
.b05-profile-table__irregular-name {
|
||||
color: var(--color-royal-amethyst, rgb(109 40 217));
|
||||
font-weight: var(--font-weight-medium);
|
||||
justify-content: center;
|
||||
min-height: 0;
|
||||
border-bottom: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent);
|
||||
color: var(--color-text);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.b05-profile-table__irregular-desc {
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
color: var(--color-text-body);
|
||||
font-size: 0.85em;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
.b05-profile-table__irregular-col-cell:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.b05-profile-table__irregular-col-cell.is-cut {
|
||||
color: rgb(220 38 38);
|
||||
}
|
||||
|
||||
.b05-profile-table__irregular-col-cell.is-fill {
|
||||
color: rgb(37 99 235);
|
||||
}
|
||||
|
||||
.b05-profile-table__irregular-col-cell.is-plan {
|
||||
color: var(--color-royal-amethyst, rgb(109 40 217));
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
|
||||
/* ─── 계획고 편집 버튼 (크기 유지·상시 표시·밝은 글자) ───────────────────── */
|
||||
|
||||
Reference in New Issue
Block a user