feat(project): 업무 설명란을 간단한 워드프로세서 에디터로 — 서식 도구 추가

굵게/기울임/밑줄/글머리목록/번호목록/인용구/링크/서식지우기 버튼을
설명란 위에 추가. execCommand 기반(같은 파일의 이미지 삽입에서 이미
insertHTML 로 쓰던 것과 같은 패턴)이고, 생성되는 태그(b/i/u/ul/ol/li/
blockquote/a)는 전부 기존 sanitize_description_html 허용 목록 안이라
서버 쪽 변경 없음. 버튼 mousedown 에서 preventDefault 해 클릭 시 에디터
선택범위가 풀리는 것 방지, 커서 위치에 따라 버튼 활성 상태 표시.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-16 00:23:12 +09:00
parent a7a294ce93
commit 79be87e7e9
5 changed files with 79 additions and 8 deletions
+34
View File
@@ -1999,6 +1999,40 @@
const hint = el("pj-desc-hint");
let taskId = null;
// ── 서식 도구 모음(간단 워드프로세서) — execCommand 는 구식 API지만
// 크롬/엣지는 여전히 지원하고, 이미 같은 파일의 이미지 삽입에서도 쓰고
// 있어(insertHTML) 일관된 패턴이다. 버튼 mousedown 에서 preventDefault
// 안 하면 클릭 순간 에디터가 포커스를 잃어 선택 범위가 풀린다.
const fmtbar = el("pj-desc-fmtbar");
if (fmtbar) {
fmtbar.addEventListener("mousedown", function (e) { e.preventDefault(); });
fmtbar.addEventListener("click", function (e) {
const btn = e.target.closest(".pj-desc-fmtbtn");
if (!btn) return;
box.focus();
const cmd = btn.dataset.cmd;
let arg = btn.dataset.arg || null;
if (cmd === "createLink") {
arg = prompt("연결할 주소(URL)를 입력하세요", "https://");
if (!arg) return;
}
try { document.execCommand(cmd, false, arg); } catch (_) {}
updateFmtState();
});
function updateFmtState() {
fmtbar.querySelectorAll(".pj-desc-fmtbtn[data-cmd]").forEach(function (btn) {
const cmd = btn.dataset.cmd;
if (cmd === "createLink" || cmd === "removeFormat") return;
let active = false;
try { active = document.queryCommandState(cmd); } catch (_) { active = false; }
btn.classList.toggle("is-active", active);
});
}
box.addEventListener("keyup", updateFmtState);
box.addEventListener("mouseup", updateFmtState);
box.addEventListener("focus", updateFmtState);
}
// ── 설명란 이미지 선택 → 크기 조절 핸들 + 삭제 버튼 오버레이 ──
// 크기는 style이 아니라 width 속성으로 저장한다(서버 sanitize가 style은
// 버리지만 img width/height 속성은 허용 목록에 있어 저장 후에도 유지된다).