- 원본 찾기 창처럼 처음부터/현재위치부터 다음찾기로 행 이동, 호표 자동 전환 - 단가산출 찾은 행 한 건 바꾸기 뒤 다음 위치로 이동 - 편집 메뉴 찾기와 Ctrl+F 로 열기 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
122 lines
5.0 KiB
JavaScript
122 lines
5.0 KiB
JavaScript
// 원본 찾기(TFRMESTSERCH: 검색어·처음부터/현재위치부터·다음찾기)와
|
|
// 산출근거 바꾸기(TFRMCOSTWORDCHANGE: 다음찾기·바꾸기)를 옮긴 찾기 창. 모두 바꾸기는 기존 `산출근거 문구 바꾸기`.
|
|
const EstFind = (() => {
|
|
const grouped = ["HeadMainDat", "CostMainDat", "MachMainDat"];
|
|
const fold = (value) =>
|
|
String(value ?? "")
|
|
.normalize("NFKC")
|
|
.toLowerCase();
|
|
|
|
function text(row) {
|
|
return Object.entries(row)
|
|
.filter(([key, value]) => typeof value === "string" && !key.endsWith("_"))
|
|
.map(([key, value]) => (key === "LineData" ? EstText.clean(value) : value))
|
|
.join("\n");
|
|
}
|
|
|
|
function next(rows, query, from = 0) {
|
|
const q = fold(query).trim();
|
|
if (!q) throw Error("검색어를 입력하세요.");
|
|
for (let index = Math.max(0, from); index < rows.length; index++)
|
|
if (fold(text(rows[index])).includes(q)) return index;
|
|
return -1;
|
|
}
|
|
|
|
function groupOf(rows, index) {
|
|
for (const [code, group] of EstNative.groups(rows))
|
|
if (index >= group.start && index < group.start + group.rows.length)
|
|
return { code, position: index - group.start };
|
|
return { code: "", position: index };
|
|
}
|
|
|
|
function open(state) {
|
|
const rows = state.project.tables[state.view];
|
|
if (!Array.isArray(rows)) throw Error("표 화면에서 찾기를 사용하세요.");
|
|
state.find ||= { query: "", replacement: "", from: "current" };
|
|
const { esc } = EstApp,
|
|
cost = state.view === "CostMainDat";
|
|
EstApp.showDialog(
|
|
"찾기(Ctrl+F)",
|
|
`<div class="form-fields"><label>검색어<input name="find-query" value="${esc(state.find.query)}"></label>${cost ? `<label>바꿀 내용<input name="find-replacement" value="${esc(state.find.replacement)}"></label>` : ""}<label>시작<select name="find-from"><option value="current" ${state.find.from === "current" ? "selected" : ""}>현재위치부터 검색</option><option value="first" ${state.find.from === "first" ? "selected" : ""}>처음부터 검색</option></select></label></div><button data-action="find-next">다음찾기</button>${cost ? ' <button data-action="find-replace">바꾸기</button>' : ""}<p id="find-result" role="status"></p>`,
|
|
);
|
|
document.querySelector('#dialog-content [name="find-query"]').focus();
|
|
}
|
|
|
|
function read(state) {
|
|
const value = (name) => document.querySelector(`#dialog-content [name="${name}"]`)?.value;
|
|
state.find.query = value("find-query") ?? "";
|
|
state.find.replacement = value("find-replacement") ?? state.find.replacement;
|
|
state.find.from = value("find-from") || "current";
|
|
}
|
|
|
|
function show(state, index) {
|
|
const rows = state.project.tables[state.view],
|
|
{ code, position } = grouped.includes(state.view)
|
|
? groupOf(rows, index)
|
|
: { code: state.group, position: index };
|
|
state.group = code;
|
|
state.query = "";
|
|
EstApp.byId("search").value = "";
|
|
state.page = Math.floor(position / 100);
|
|
state.selection = new Set([index]);
|
|
EstApp.render();
|
|
}
|
|
|
|
function findNext(state) {
|
|
read(state);
|
|
const rows = state.project.tables[state.view],
|
|
start =
|
|
state.find.from === "first"
|
|
? 0
|
|
: Math.max(-1, ...[...state.selection].map(Number).filter(Number.isFinite)) + 1;
|
|
const index = next(rows, state.find.query, start);
|
|
const result = EstApp.byId("find-result");
|
|
if (index < 0) {
|
|
result.textContent = "끝까지 찾았습니다. 더 찾을 항목이 없습니다.";
|
|
return;
|
|
}
|
|
state.find.from = "current";
|
|
document.querySelector('#dialog-content [name="find-from"]').value = "current";
|
|
show(state, index);
|
|
result.textContent = `${index + 1}번째 행을 찾았습니다.`;
|
|
}
|
|
|
|
function replaceCurrent(state) {
|
|
read(state);
|
|
if (state.view !== "CostMainDat") throw Error("단가산출 화면에서 바꾸기를 사용하세요.");
|
|
const rows = state.project.tables.CostMainDat,
|
|
index = [...state.selection][0];
|
|
if (state.selection.size !== 1 || !rows[index]) throw Error("먼저 다음찾기로 행을 찾으세요.");
|
|
const updates = EstText.replaceWords(
|
|
[{ row: rows[index], index }],
|
|
state.find.query,
|
|
state.find.replacement,
|
|
);
|
|
if (!updates.length) throw Error("이 행에는 바꿀 문구가 없습니다. 다음찾기를 누르세요.");
|
|
EstApp.change((project) => (project.tables.CostMainDat[index].LineData = updates[0].value));
|
|
findNext(state);
|
|
}
|
|
|
|
function handle(name, state) {
|
|
if (name === "find") open(state);
|
|
else if (name === "find-next") findNext(state);
|
|
else if (name === "find-replace") replaceCurrent(state);
|
|
else return false;
|
|
return true;
|
|
}
|
|
|
|
if (typeof document !== "undefined" && document.addEventListener)
|
|
document.addEventListener("keydown", (event) => {
|
|
if (!(event.ctrlKey || event.metaKey) || event.key.toLowerCase() !== "f") return;
|
|
event.preventDefault();
|
|
try {
|
|
open(EstApp.state);
|
|
} catch (error) {
|
|
EstApp.state.notice = error.message;
|
|
EstApp.render();
|
|
}
|
|
});
|
|
|
|
return { text, next, groupOf, handle };
|
|
})();
|