feat(ground): 하상 selectable; 설계홍수위 and 세굴 깊이 for 하상
- shape-options: a per-option "confirmed" flag; 하상 is confirmed, 해상 is not. - The 위치 chips store the choice in the pier's draft; 선택 shows it. - 하상: the groundwater inputs give way to 설계홍수위 DHWL (floodLevelEl) and 세굴 깊이 (scourDepthM). The groundwater value is kept (남겨두기). - Checks: G07 DHWL missing, G08 DHWL below the 지반고, G09 scour negative or the scour line below the footing bottom. The groundwater check is skipped for 하상. - Section: the DHWL as a solid line and the 세굴선 dashed. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7b5517e312
commit
26a5aff517
9 files changed
+163
-45
No files matched your search
+57
-30
@@ -834,7 +834,7 @@ function setPileRow(name: string, row: number, delta: number): void {
|
||||
}
|
||||
|
||||
/** 형상 선택칩 (plan.md C5): the default is pressed; every other option is shown and disabled with the reason. */
|
||||
function renderShapeOptions(member: MemberId): void {
|
||||
function renderShapeOptions(member: MemberId, pier?: Pier): void {
|
||||
const box = $<HTMLDivElement>('member-options');
|
||||
box.replaceChildren();
|
||||
SHAPE_GROUPS[member].forEach((group, gi) => {
|
||||
@@ -843,12 +843,15 @@ function renderShapeOptions(member: MemberId): void {
|
||||
for (const option of group.options) {
|
||||
const chip = el('button', option.label, 'chip-opt');
|
||||
chip.type = 'button';
|
||||
chip.setAttribute('aria-pressed', String(option.isDefault));
|
||||
// The ground 위치 is stored in the pier's draft, so its pressed chip is the chosen one (승인 2026-10-26).
|
||||
const chosen = member === 'ground' && pier ? (pier.draft.groundPosition ?? '육상') : undefined;
|
||||
chip.setAttribute('aria-pressed', String(chosen !== undefined ? option.label === chosen : option.isDefault));
|
||||
chip.disabled = !isSelectable(option);
|
||||
if (chip.disabled) chip.title = UNDEFINED_REASON;
|
||||
chip.addEventListener('click', () => {
|
||||
try {
|
||||
choose(member, gi, option.label);
|
||||
if (member === 'ground' && pier) editGround(pier.input.name, 'groundPosition', option.label);
|
||||
} catch (err) {
|
||||
setError((err as Error).message);
|
||||
}
|
||||
@@ -1512,11 +1515,12 @@ function valueText(key: EditableKey, v: unknown): string {
|
||||
}
|
||||
|
||||
/** Sets one 지반·지층 input in the draft (applied to the model by 다시 생성). */
|
||||
function editGround(name: string, key: 'groundPosition' | 'groundWaterOn' | 'groundWaterEl', value: unknown): void {
|
||||
function editGround(name: string, key: 'groundPosition' | 'groundWaterOn' | 'groundWaterEl' | 'floodLevelEl' | 'scourDepthM', value: unknown): void {
|
||||
const p = state.piers.find((q) => q.input.name === name);
|
||||
if (!p) return;
|
||||
if (key === 'groundWaterEl' && value !== undefined && !Number.isFinite(value)) {
|
||||
setError('지하수위 EL은 숫자로 입력하세요. 값을 되돌렸습니다.');
|
||||
const numeric = key === 'groundWaterEl' || key === 'floodLevelEl' || key === 'scourDepthM';
|
||||
if (numeric && value !== undefined && !Number.isFinite(value)) {
|
||||
setError(`${FIELD_LABELS[key]}은(는) 숫자로 입력하세요. 값을 되돌렸습니다.`);
|
||||
renderAll();
|
||||
return;
|
||||
}
|
||||
@@ -1552,29 +1556,50 @@ function renderGroundModel(box: HTMLDivElement, pier: Pier): void {
|
||||
|
||||
// 위치 is the 형상 옵션 of this member (the chips above the card); only the default is selectable until Q6.
|
||||
|
||||
wrap.appendChild(el('p', '지하수위', 'lbl'));
|
||||
const water = el('div', undefined, 'ground-water');
|
||||
water.style.display = 'flex';
|
||||
water.style.flexWrap = 'wrap';
|
||||
water.style.alignItems = 'center';
|
||||
water.style.gap = '8px';
|
||||
const check = el('input');
|
||||
check.type = 'checkbox';
|
||||
check.checked = d.groundWaterOn === true;
|
||||
check.id = 'ground-water-on';
|
||||
check.addEventListener('change', () => editGround(name, 'groundWaterOn', check.checked));
|
||||
const checkLabel = el('label', ' 지하수위 고려');
|
||||
checkLabel.prepend(check);
|
||||
const level = el('input');
|
||||
level.type = 'number';
|
||||
level.step = 'any';
|
||||
level.id = 'ground-water-el';
|
||||
level.placeholder = '지하수위 EL (m)';
|
||||
level.value = d.groundWaterEl === undefined ? '' : String(d.groundWaterEl);
|
||||
level.disabled = !check.checked;
|
||||
level.addEventListener('change', () => editGround(name, 'groundWaterEl', level.value === '' ? undefined : Number(level.value)));
|
||||
water.append(checkLabel, level, el('span', 'm', 'muted small'));
|
||||
wrap.appendChild(water);
|
||||
const position = d.groundPosition ?? '육상';
|
||||
if (position === '하상') {
|
||||
// 하상 (승인 2026-10-26): the 설계홍수위 DHWL and the 세굴 깊이 replace the groundwater; the groundwater value is kept.
|
||||
wrap.appendChild(el('p', '설계홍수위 DHWL · 세굴', 'lbl'));
|
||||
const scourRow = el('div', undefined, 'ground-water');
|
||||
scourRow.style.display = 'flex';
|
||||
scourRow.style.flexWrap = 'wrap';
|
||||
scourRow.style.alignItems = 'center';
|
||||
scourRow.style.gap = '8px';
|
||||
for (const [key, label, value] of [['floodLevelEl', '설계홍수위 EL (m)', d.floodLevelEl], ['scourDepthM', '세굴 깊이 (m)', d.scourDepthM]] as const) {
|
||||
const input = el('input');
|
||||
input.type = 'number';
|
||||
input.step = 'any';
|
||||
input.placeholder = label;
|
||||
input.value = value === undefined ? '' : String(value);
|
||||
input.addEventListener('change', () => editGround(name, key, input.value === '' ? undefined : Number(input.value)));
|
||||
scourRow.append(el('span', label, 'muted small'), input);
|
||||
}
|
||||
wrap.appendChild(scourRow);
|
||||
} else {
|
||||
wrap.appendChild(el('p', '지하수위', 'lbl'));
|
||||
const water = el('div', undefined, 'ground-water');
|
||||
water.style.display = 'flex';
|
||||
water.style.flexWrap = 'wrap';
|
||||
water.style.alignItems = 'center';
|
||||
water.style.gap = '8px';
|
||||
const check = el('input');
|
||||
check.type = 'checkbox';
|
||||
check.checked = d.groundWaterOn === true;
|
||||
check.id = 'ground-water-on';
|
||||
check.addEventListener('change', () => editGround(name, 'groundWaterOn', check.checked));
|
||||
const checkLabel = el('label', ' 지하수위 고려');
|
||||
checkLabel.prepend(check);
|
||||
const level = el('input');
|
||||
level.type = 'number';
|
||||
level.step = 'any';
|
||||
level.id = 'ground-water-el';
|
||||
level.placeholder = '지하수위 EL (m)';
|
||||
level.value = d.groundWaterEl === undefined ? '' : String(d.groundWaterEl);
|
||||
level.disabled = !check.checked;
|
||||
level.addEventListener('change', () => editGround(name, 'groundWaterEl', level.value === '' ? undefined : Number(level.value)));
|
||||
water.append(checkLabel, level, el('span', 'm', 'muted small'));
|
||||
wrap.appendChild(water);
|
||||
}
|
||||
|
||||
// 지층 행 (승인 2026-10-26): one row per layer, from the top down; the EL is the layer's bottom EL.
|
||||
const rows = d.strataRows ?? [];
|
||||
@@ -1868,8 +1893,10 @@ function renderPlan(): void {
|
||||
const member = MEMBERS.find((m) => m.id === state.member)!;
|
||||
$('member-title').textContent = member.label;
|
||||
// Default and current selection are shown separately (spec §5.1). Only defaults are selectable today.
|
||||
$('member-summary').textContent = `기본: ${defaultSummary(member.id)} · 선택: ${defaultSummary(member.id)}`;
|
||||
renderShapeOptions(member.id);
|
||||
// 선택 shows the pier's own 위치 for the ground; the other members still choose only their defaults.
|
||||
const selected = member.id === 'ground' && pier ? (pier.draft.groundPosition ?? '육상') : defaultSummary(member.id);
|
||||
$('member-summary').textContent = `기본: ${defaultSummary(member.id)} · 선택: ${selected}`;
|
||||
renderShapeOptions(member.id, pier);
|
||||
$('member-note').textContent = member.note;
|
||||
if (member.id === 'bearing' && pier?.model) {
|
||||
// Row skew of the bearing blocks (계산값): the block sides follow the bearing row (승인 2026-10-23).
|
||||
|
||||
Reference in new issue
Block a user