Refine BIM comment modal controls
This commit is contained in:
parent
d70f6e0886
commit
405113fa4f
|
|
@ -1583,6 +1583,7 @@ header {
|
|||
.comment-card-modal {
|
||||
position: fixed;
|
||||
--env-control-h: 42px;
|
||||
--ops-comment-accent: var(--comment-target-active, #c4ff67);
|
||||
--ops-detail-bg: rgba(18, 18, 20, 0.96);
|
||||
--ops-detail-surface: rgba(28, 28, 31, 0.76);
|
||||
--ops-detail-surface-strong: rgba(32, 32, 35, 0.92);
|
||||
|
|
@ -1607,6 +1608,15 @@ header {
|
|||
-webkit-backdrop-filter: blur(30px) saturate(1.08);
|
||||
}
|
||||
|
||||
.comment-card-modal.is-expanded {
|
||||
top: 16px !important;
|
||||
left: 16px !important;
|
||||
width: calc(100vw - 32px);
|
||||
max-height: calc(100vh - 32px);
|
||||
height: calc(100vh - 32px);
|
||||
border-radius: 26px;
|
||||
}
|
||||
|
||||
.comment-card-modal.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -1614,7 +1624,7 @@ header {
|
|||
.comment-card-modal__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
justify-content: flex-end;
|
||||
min-height: 64px;
|
||||
padding: 20px 28px 8px;
|
||||
cursor: grab;
|
||||
|
|
@ -1794,6 +1804,14 @@ header {
|
|||
color: rgba(255, 255, 255, 0.95);
|
||||
}
|
||||
|
||||
.bim-subitem-menu {
|
||||
z-index: 30070;
|
||||
display: grid;
|
||||
gap: 0.18rem;
|
||||
min-width: 17.25rem;
|
||||
max-width: min(22rem, calc(100vw - 24px));
|
||||
}
|
||||
|
||||
.ops-comment-subitems,
|
||||
.comment-attachments,
|
||||
.comment-thread {
|
||||
|
|
@ -1830,13 +1848,19 @@ header {
|
|||
background: rgba(255, 255, 255, 0.026);
|
||||
}
|
||||
|
||||
.ops-comment-subitem[data-kind="text"] {
|
||||
grid-template-columns: minmax(0, 1fr) 28px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.ops-comment-subitem input[type="checkbox"] {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
accent-color: var(--accent);
|
||||
accent-color: var(--ops-comment-accent);
|
||||
}
|
||||
|
||||
.ops-comment-subitem input[type="text"] {
|
||||
.ops-comment-subitem input[type="text"],
|
||||
.ops-comment-subitem textarea {
|
||||
min-width: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
|
|
@ -1846,6 +1870,12 @@ header {
|
|||
outline: none;
|
||||
}
|
||||
|
||||
.ops-comment-subitem textarea {
|
||||
min-height: 84px;
|
||||
line-height: 1.45;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.ops-comment-subitem button {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
|
|
@ -1877,12 +1907,12 @@ header {
|
|||
}
|
||||
|
||||
.ops-detail-dropzone.drag-over {
|
||||
background: color-mix(in srgb, var(--accent) 18%, rgba(255, 255, 255, 0.04));
|
||||
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 44%, transparent);
|
||||
background: color-mix(in srgb, var(--ops-comment-accent) 18%, rgba(255, 255, 255, 0.04));
|
||||
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ops-comment-accent) 44%, transparent);
|
||||
}
|
||||
|
||||
.ops-detail-dropzone__icon {
|
||||
color: var(--accent);
|
||||
color: var(--ops-comment-accent);
|
||||
}
|
||||
|
||||
.ops-detail-dropzone__copy {
|
||||
|
|
@ -1909,7 +1939,7 @@ header {
|
|||
border: 0;
|
||||
border-radius: 13px;
|
||||
background: transparent;
|
||||
color: var(--accent);
|
||||
color: var(--ops-comment-accent);
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
font-size: 13px;
|
||||
|
|
@ -2121,8 +2151,8 @@ header {
|
|||
.comment-card-modal__footer {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
padding: 14px 32px 18px;
|
||||
background: var(--ops-detail-bg);
|
||||
|
|
@ -2131,7 +2161,17 @@ header {
|
|||
|
||||
.comment-card-modal__footer .primary-btn,
|
||||
.comment-card-modal__footer .secondary-btn {
|
||||
min-width: 148px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.comment-card-modal .primary-btn {
|
||||
background: var(--ops-comment-accent);
|
||||
color: var(--toolbar-bg-active-contrast, #15170f);
|
||||
box-shadow: 0 8px 24px color-mix(in srgb, var(--ops-comment-accent) 24%, transparent);
|
||||
}
|
||||
|
||||
.comment-card-modal .primary-btn:not(:disabled):hover {
|
||||
box-shadow: 0 10px 30px color-mix(in srgb, var(--ops-comment-accent) 30%, transparent);
|
||||
}
|
||||
|
||||
.comment-card-modal .error-text {
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ const commentModalBackdrop = document.getElementById("commentModalBackdrop");
|
|||
const commentModal = document.getElementById("commentModal");
|
||||
const commentModalHeader = document.getElementById("commentModalHeader");
|
||||
const commentModalClose = document.getElementById("commentModalClose");
|
||||
const commentModalExpandButton = document.getElementById("commentModalExpandButton");
|
||||
const commentModalSubtitle = document.getElementById("commentModalSubtitle");
|
||||
const commentModalEditedMeta = document.getElementById("commentModalEditedMeta");
|
||||
const commentTitleInput = document.getElementById("commentTitleInput");
|
||||
|
|
@ -67,6 +68,7 @@ const commentAttachmentGrid = document.getElementById("commentAttachmentGrid");
|
|||
const commentSubitemsSection = document.getElementById("commentSubitemsSection");
|
||||
const commentSubitemsList = document.getElementById("commentSubitemsList");
|
||||
const commentAddSubitemButton = document.getElementById("commentAddSubitemButton");
|
||||
const commentSubitemMenu = document.getElementById("commentSubitemMenu");
|
||||
const commentThreadList = document.getElementById("commentThreadList");
|
||||
const commentReplyInput = document.getElementById("commentReplyInput");
|
||||
const commentReplyAttachButton = document.getElementById("commentReplyAttachButton");
|
||||
|
|
@ -75,6 +77,7 @@ const commentReplyAttachmentGrid = document.getElementById("commentReplyAttachme
|
|||
const commentModalError = document.getElementById("commentModalError");
|
||||
const commentCancelButton = document.getElementById("commentCancelButton");
|
||||
const commentApplyButton = document.getElementById("commentApplyButton");
|
||||
const commentSaveButton = document.getElementById("commentSaveButton");
|
||||
const commentApplyInlineButton = document.getElementById("commentApplyInlineButton");
|
||||
const treeContainer = document.getElementById("treeContainer");
|
||||
const displayModeControl = document.getElementById("displayModeControl");
|
||||
|
|
@ -299,6 +302,7 @@ let commentSourcesLoaded = new Set();
|
|||
let commentContextTarget = null;
|
||||
let commentModalMode = "create";
|
||||
let commentModalCommentId = null;
|
||||
let commentModalExpanded = false;
|
||||
let activeCommentId = null;
|
||||
let hoverCommentId = null;
|
||||
let commentDraftAttachments = [];
|
||||
|
|
@ -1058,27 +1062,94 @@ const renderModalAttachmentInputs = () => {
|
|||
});
|
||||
};
|
||||
|
||||
const setCommentModalExpanded = (expanded) => {
|
||||
commentModalExpanded = !!expanded;
|
||||
commentModal?.classList.toggle("is-expanded", commentModalExpanded);
|
||||
if (commentModalExpandButton) {
|
||||
commentModalExpandButton.setAttribute("aria-label", commentModalExpanded ? "Свернуть" : "Развернуть");
|
||||
commentModalExpandButton.title = commentModalExpanded ? "Свернуть" : "Развернуть";
|
||||
}
|
||||
};
|
||||
|
||||
const hideCommentSubitemMenu = () => {
|
||||
commentSubitemMenu?.classList.add("hidden");
|
||||
};
|
||||
|
||||
const showCommentSubitemMenu = () => {
|
||||
if (!commentSubitemMenu || !commentAddSubitemButton) return;
|
||||
commentSubitemMenu.classList.remove("hidden");
|
||||
const buttonRect = commentAddSubitemButton.getBoundingClientRect();
|
||||
const width = commentSubitemMenu.offsetWidth || 276;
|
||||
const height = commentSubitemMenu.offsetHeight || 94;
|
||||
const x = Math.max(8, Math.min(window.innerWidth - width - 8, buttonRect.left + (buttonRect.width - width) / 2));
|
||||
const y = Math.max(8, Math.min(window.innerHeight - height - 8, buttonRect.bottom + 8));
|
||||
commentSubitemMenu.style.left = `${x}px`;
|
||||
commentSubitemMenu.style.top = `${y}px`;
|
||||
};
|
||||
|
||||
const toggleCommentSubitemMenu = () => {
|
||||
if (!commentSubitemMenu) return;
|
||||
if (commentSubitemMenu.classList.contains("hidden")) {
|
||||
showCommentSubitemMenu();
|
||||
} else {
|
||||
hideCommentSubitemMenu();
|
||||
}
|
||||
};
|
||||
|
||||
const addCommentSubitem = (kind) => {
|
||||
const nextKind = kind === "text" ? "text" : "checker";
|
||||
commentDraftSubitems.push({
|
||||
id: `sub_${Date.now()}_${Math.random().toString(36).slice(2)}`,
|
||||
kind: nextKind,
|
||||
title: "",
|
||||
body: "",
|
||||
done: false,
|
||||
});
|
||||
renderCommentSubitems();
|
||||
window.requestAnimationFrame(() => {
|
||||
const selector = nextKind === "text"
|
||||
? ".ops-comment-subitem[data-kind='text'] textarea"
|
||||
: ".ops-comment-subitem[data-kind='checker'] input[type='text']";
|
||||
const inputs = commentSubitemsList?.querySelectorAll?.(selector);
|
||||
inputs?.[inputs.length - 1]?.focus?.();
|
||||
});
|
||||
};
|
||||
|
||||
const renderCommentSubitems = () => {
|
||||
if (!commentSubitemsList || !commentSubitemsSection) return;
|
||||
commentSubitemsList.replaceChildren();
|
||||
const subitems = Array.isArray(commentDraftSubitems) ? commentDraftSubitems : [];
|
||||
commentSubitemsSection.classList.toggle("hidden", !subitems.length);
|
||||
subitems.forEach((subitem, index) => {
|
||||
const row = document.createElement("label");
|
||||
const kind = subitem.kind === "text" ? "text" : "checker";
|
||||
const row = document.createElement(kind === "text" ? "div" : "label");
|
||||
row.className = "ops-comment-subitem";
|
||||
row.dataset.kind = kind;
|
||||
let editor = null;
|
||||
if (kind === "text") {
|
||||
editor = document.createElement("textarea");
|
||||
editor.rows = 3;
|
||||
editor.value = subitem.body || subitem.title || "";
|
||||
editor.placeholder = "Текстовый блок";
|
||||
editor.addEventListener("input", () => {
|
||||
commentDraftSubitems[index] = { ...commentDraftSubitems[index], kind, body: editor.value, title: editor.value };
|
||||
});
|
||||
} else {
|
||||
const checkbox = document.createElement("input");
|
||||
checkbox.type = "checkbox";
|
||||
checkbox.checked = !!subitem.done;
|
||||
checkbox.addEventListener("change", () => {
|
||||
commentDraftSubitems[index] = { ...commentDraftSubitems[index], done: checkbox.checked };
|
||||
commentDraftSubitems[index] = { ...commentDraftSubitems[index], kind, done: checkbox.checked };
|
||||
});
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.value = subitem.title || "";
|
||||
input.placeholder = "Новый подэлемент";
|
||||
input.addEventListener("input", () => {
|
||||
commentDraftSubitems[index] = { ...commentDraftSubitems[index], title: input.value };
|
||||
editor = document.createElement("input");
|
||||
editor.type = "text";
|
||||
editor.value = subitem.title || subitem.body || "";
|
||||
editor.placeholder = "Новый чекер";
|
||||
editor.addEventListener("input", () => {
|
||||
commentDraftSubitems[index] = { ...commentDraftSubitems[index], kind, title: editor.value };
|
||||
});
|
||||
row.appendChild(checkbox);
|
||||
}
|
||||
const remove = document.createElement("button");
|
||||
remove.type = "button";
|
||||
remove.setAttribute("aria-label", "Удалить подэлемент");
|
||||
|
|
@ -1087,13 +1158,14 @@ const renderCommentSubitems = () => {
|
|||
commentDraftSubitems.splice(index, 1);
|
||||
renderCommentSubitems();
|
||||
});
|
||||
row.append(checkbox, input, remove);
|
||||
row.append(editor, remove);
|
||||
commentSubitemsList.appendChild(row);
|
||||
});
|
||||
};
|
||||
|
||||
const openCommentModalForTarget = () => {
|
||||
if (!commentContextTarget || !commentModal) return;
|
||||
hideCommentSubitemMenu();
|
||||
commentsModeActive = true;
|
||||
commentsCollapsed = false;
|
||||
commentModalMode = "create";
|
||||
|
|
@ -1124,6 +1196,8 @@ const openCommentModalForTarget = () => {
|
|||
const openCommentModalForComment = (commentId) => {
|
||||
const comment = commentRecords.find((item) => item.id === commentId);
|
||||
if (!comment || !commentModal) return;
|
||||
hideCommentSubitemMenu();
|
||||
commentContextTarget = null;
|
||||
commentModalMode = "edit";
|
||||
commentModalCommentId = comment.id;
|
||||
activeCommentId = comment.id;
|
||||
|
|
@ -1150,8 +1224,10 @@ const openCommentModalForComment = (commentId) => {
|
|||
};
|
||||
|
||||
const closeCommentModal = () => {
|
||||
hideCommentSubitemMenu();
|
||||
commentModalBackdrop?.classList.add("hidden");
|
||||
commentModal?.classList.add("hidden");
|
||||
setCommentModalExpanded(false);
|
||||
if (commentModalMode === "create") {
|
||||
commentContextTarget = null;
|
||||
}
|
||||
|
|
@ -1165,12 +1241,18 @@ const closeCommentModal = () => {
|
|||
|
||||
const normalizedSubitemsDraft = () => (
|
||||
(commentDraftSubitems || [])
|
||||
.map((item) => ({
|
||||
.map((item) => {
|
||||
const kind = item.kind === "text" ? "text" : "checker";
|
||||
const content = String(kind === "text" ? (item.body || item.title || "") : (item.title || item.body || "")).trim();
|
||||
return {
|
||||
id: item.id || `sub_${Date.now()}_${Math.random().toString(36).slice(2)}`,
|
||||
title: String(item.title || "").trim(),
|
||||
done: !!item.done,
|
||||
}))
|
||||
.filter((item) => item.title)
|
||||
kind,
|
||||
title: content,
|
||||
body: kind === "text" ? content : "",
|
||||
done: kind === "checker" && item.done === true,
|
||||
};
|
||||
})
|
||||
.filter((item) => item.title || item.body)
|
||||
);
|
||||
|
||||
const buildCommentPayload = ({ title, body, replyBody } = {}) => {
|
||||
|
|
@ -1184,7 +1266,32 @@ const buildCommentPayload = ({ title, body, replyBody } = {}) => {
|
|||
};
|
||||
};
|
||||
|
||||
const applyCommentModal = async () => {
|
||||
const syncCommentModalAfterSave = (comment) => {
|
||||
if (!comment) return;
|
||||
commentModalMode = "edit";
|
||||
commentModalCommentId = comment.id;
|
||||
commentContextTarget = null;
|
||||
activeCommentId = comment.id;
|
||||
commentTitleInput && (commentTitleInput.value = comment.title || "");
|
||||
commentBodyInput && (commentBodyInput.value = comment.body || "");
|
||||
commentReplyInput && (commentReplyInput.value = "");
|
||||
commentDraftAttachments = [];
|
||||
commentReplyAttachments = [];
|
||||
commentDraftSubitems = Array.isArray(comment.subitems)
|
||||
? comment.subitems.map((item) => ({ ...item }))
|
||||
: [];
|
||||
if (commentModalSubtitle) {
|
||||
commentModalSubtitle.textContent = comment.id || "BIM-COMMENT";
|
||||
}
|
||||
if (commentModalEditedMeta) {
|
||||
commentModalEditedMeta.textContent = `Последнее редактирование: ${formatDateTime(comment.updatedAt || comment.createdAt)}`;
|
||||
}
|
||||
renderModalAttachmentInputs();
|
||||
renderCommentSubitems();
|
||||
renderCommentThread(comment);
|
||||
};
|
||||
|
||||
const applyCommentModal = async ({ closeOnSave = false } = {}) => {
|
||||
if (isGuestMode()) return;
|
||||
const title = commentTitleInput?.value?.trim() || "";
|
||||
const body = commentBodyInput?.value?.trim() || "";
|
||||
|
|
@ -1197,6 +1304,10 @@ const applyCommentModal = async () => {
|
|||
commentApplyButton.disabled = true;
|
||||
commentApplyButton.textContent = "Сохранение...";
|
||||
}
|
||||
if (commentSaveButton) {
|
||||
commentSaveButton.disabled = true;
|
||||
commentSaveButton.textContent = "Сохранение...";
|
||||
}
|
||||
if (commentApplyInlineButton) {
|
||||
commentApplyInlineButton.disabled = true;
|
||||
}
|
||||
|
|
@ -1211,18 +1322,23 @@ const applyCommentModal = async () => {
|
|||
} else if (commentModalCommentId) {
|
||||
const original = commentRecords.find((item) => item.id === commentModalCommentId);
|
||||
const subitems = normalizedSubitemsDraft();
|
||||
const messageAttachments = [...commentDraftAttachments, ...commentReplyAttachments].slice(0, 8);
|
||||
const patchPayload = {};
|
||||
if (!original || title !== (original.title || "")) patchPayload.title = title;
|
||||
if (!original || body !== (original.body || "")) patchPayload.body = body;
|
||||
if (JSON.stringify(subitems) !== JSON.stringify(original?.subitems || [])) patchPayload.subitems = subitems;
|
||||
if (replyBody || commentReplyAttachments.length) {
|
||||
if (replyBody || messageAttachments.length) {
|
||||
patchPayload.message = {
|
||||
body: replyBody,
|
||||
attachments: commentReplyAttachments,
|
||||
attachments: messageAttachments,
|
||||
};
|
||||
}
|
||||
if (!Object.keys(patchPayload).length) {
|
||||
if (closeOnSave) {
|
||||
closeCommentModal();
|
||||
} else {
|
||||
setStatus("Изменений нет");
|
||||
}
|
||||
return;
|
||||
}
|
||||
payload = await fetchJSON(`${COMMENTS_API}/${encodeURIComponent(commentModalCommentId)}`, {
|
||||
|
|
@ -1238,7 +1354,10 @@ const applyCommentModal = async () => {
|
|||
commentContextTarget = null;
|
||||
commentsModeActive = true;
|
||||
commentsCollapsed = false;
|
||||
syncCommentModalAfterSave(saved);
|
||||
if (closeOnSave) {
|
||||
closeCommentModal();
|
||||
}
|
||||
updateCommentsVisibility();
|
||||
setStatus("Комментарий сохранён");
|
||||
}
|
||||
|
|
@ -1250,6 +1369,10 @@ const applyCommentModal = async () => {
|
|||
commentApplyButton.disabled = false;
|
||||
commentApplyButton.textContent = "Применить";
|
||||
}
|
||||
if (commentSaveButton) {
|
||||
commentSaveButton.disabled = false;
|
||||
commentSaveButton.textContent = "Сохранить";
|
||||
}
|
||||
if (commentApplyInlineButton) {
|
||||
commentApplyInlineButton.disabled = false;
|
||||
}
|
||||
|
|
@ -5076,22 +5199,32 @@ const loadProjectSnapshot = async (project) => {
|
|||
});
|
||||
commentModalClose?.addEventListener("click", closeCommentModal);
|
||||
commentCancelButton?.addEventListener("click", closeCommentModal);
|
||||
commentApplyButton?.addEventListener("click", applyCommentModal);
|
||||
commentApplyInlineButton?.addEventListener("click", applyCommentModal);
|
||||
commentModalExpandButton?.addEventListener("click", () => setCommentModalExpanded(!commentModalExpanded));
|
||||
commentApplyButton?.addEventListener("click", () => applyCommentModal({ closeOnSave: false }));
|
||||
commentSaveButton?.addEventListener("click", () => applyCommentModal({ closeOnSave: true }));
|
||||
commentApplyInlineButton?.addEventListener("click", () => applyCommentModal({ closeOnSave: false }));
|
||||
commentModalBackdrop?.addEventListener("click", (event) => {
|
||||
if (event.target === commentModalBackdrop) closeCommentModal();
|
||||
});
|
||||
commentAttachButton?.addEventListener("click", () => commentAttachmentInput?.click());
|
||||
commentReplyAttachButton?.addEventListener("click", () => commentReplyAttachmentInput?.click());
|
||||
commentAddSubitemButton?.addEventListener("click", () => {
|
||||
commentDraftSubitems.push({
|
||||
id: `sub_${Date.now()}_${Math.random().toString(36).slice(2)}`,
|
||||
title: "",
|
||||
done: false,
|
||||
commentAddSubitemButton?.addEventListener("click", (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
toggleCommentSubitemMenu();
|
||||
});
|
||||
renderCommentSubitems();
|
||||
const inputs = commentSubitemsList?.querySelectorAll?.(".ops-comment-subitem input[type='text']");
|
||||
inputs?.[inputs.length - 1]?.focus?.();
|
||||
commentSubitemMenu?.addEventListener("click", (event) => {
|
||||
const button = event.target?.closest?.("[data-comment-subitem-kind]");
|
||||
if (!button) return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
addCommentSubitem(button.dataset.commentSubitemKind);
|
||||
hideCommentSubitemMenu();
|
||||
});
|
||||
document.addEventListener("click", (event) => {
|
||||
if (!commentSubitemMenu || commentSubitemMenu.classList.contains("hidden")) return;
|
||||
if (commentSubitemMenu.contains(event.target) || commentAddSubitemButton?.contains(event.target)) return;
|
||||
hideCommentSubitemMenu();
|
||||
});
|
||||
commentAttachmentInput?.addEventListener("change", async (event) => {
|
||||
const files = await readAttachmentFiles(event.target.files);
|
||||
|
|
@ -5306,6 +5439,12 @@ const loadProjectSnapshot = async (project) => {
|
|||
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key !== "Escape") return;
|
||||
if (commentSubitemMenu && !commentSubitemMenu.classList.contains("hidden")) {
|
||||
hideCommentSubitemMenu();
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
return;
|
||||
}
|
||||
if (commentModal && !commentModal.classList.contains("hidden")) {
|
||||
closeCommentModal();
|
||||
e.preventDefault();
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@
|
|||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="./dcViewer.css?v=25">
|
||||
<link rel="stylesheet" href="./dcViewer.css?v=26">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
|
|
@ -295,18 +295,6 @@
|
|||
<div id="commentModalBackdrop" class="modal-backdrop hidden"></div>
|
||||
<div id="commentModal" class="comment-card-modal ops-comment-detail hidden" role="dialog" aria-modal="true" aria-label="Комментарий к модели">
|
||||
<div class="comment-card-modal__header" id="commentModalHeader">
|
||||
<div class="ops-detail-nav">
|
||||
<button class="ops-detail-icon-btn" type="button" aria-label="Назад" title="Назад">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M14 7 9 12l5 5"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<button class="ops-detail-icon-btn" type="button" aria-label="Развернуть" title="Развернуть">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M14 5h5v5M19 5l-7 7M10 19H5v-5M5 19l7-7"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="ops-detail-actions">
|
||||
<button class="ops-detail-icon-btn" type="button" aria-label="Уведомления" title="Уведомления">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
|
|
@ -314,9 +302,9 @@
|
|||
<path d="M10 21h4"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<button class="ops-detail-icon-btn" type="button" aria-label="Ещё" title="Ещё">
|
||||
<button class="ops-detail-icon-btn" id="commentModalExpandButton" type="button" aria-label="Развернуть" title="Развернуть">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M12 12h.01M18 12h.01M6 12h.01"></path>
|
||||
<path d="M14 5h5v5M19 5l-7 7M10 19H5v-5M5 19l7-7"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<button class="ops-detail-icon-btn modal-close" id="commentModalClose" type="button" aria-label="Закрыть" title="Закрыть">
|
||||
|
|
@ -346,6 +334,21 @@
|
|||
<span>Добавить подэлемент</span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="commentSubitemMenu" class="nodedc-dropdown-surface bim-subitem-menu hidden" role="menu" aria-label="Тип подэлемента">
|
||||
<button class="nodedc-dropdown-option" data-comment-subitem-kind="text" type="button" role="menuitem">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M4 6h16M4 12h10M4 18h12"></path>
|
||||
</svg>
|
||||
<span>Создать текстовый блок</span>
|
||||
</button>
|
||||
<button class="nodedc-dropdown-option" data-comment-subitem-kind="checker" type="button" role="menuitem">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M9 11l2 2 4-5"></path>
|
||||
<path d="M4 5h16M4 12h3M4 19h16"></path>
|
||||
</svg>
|
||||
<span>Создать чекер</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="ops-comment-subitems hidden" id="commentSubitemsSection">
|
||||
|
|
@ -418,6 +421,7 @@
|
|||
<div class="comment-card-modal__footer">
|
||||
<button class="secondary-btn" id="commentCancelButton" type="button">Отменить</button>
|
||||
<button class="primary-btn" id="commentApplyButton" type="button">Применить</button>
|
||||
<button class="primary-btn" id="commentSaveButton" type="button">Сохранить</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="saveProjectBackdrop" class="modal-backdrop hidden"></div>
|
||||
|
|
@ -569,6 +573,6 @@
|
|||
<input id="fileInput" class="hidden" type="file"
|
||||
accept=".xkt,.glb,.gltf,.bim,.las,.laz,.obj,.stl">
|
||||
|
||||
<script type="module" src="./dcViewer.js?v=29"></script>
|
||||
<script type="module" src="./dcViewer.js?v=30"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1606,14 +1606,20 @@ const sanitizeCommentSubitems = (value) => {
|
|||
if (!item || typeof item !== "object") {
|
||||
return [];
|
||||
}
|
||||
const title = sanitizeCommentText(item.title ?? item.text ?? item.name, 300);
|
||||
if (!title) {
|
||||
const kind = item.kind === "text" ? "text" : "checker";
|
||||
const rawContent = kind === "text"
|
||||
? (item.body ?? item.title ?? item.text ?? item.name)
|
||||
: (item.title ?? item.text ?? item.name ?? item.body);
|
||||
const content = sanitizeCommentText(rawContent, kind === "text" ? 4000 : 300);
|
||||
if (!content) {
|
||||
return [];
|
||||
}
|
||||
return [{
|
||||
id: stringOrNull(item.id) || `sub_${randomBase64Url(8)}`,
|
||||
title,
|
||||
done: item.done === true,
|
||||
kind,
|
||||
title: content,
|
||||
body: kind === "text" ? content : "",
|
||||
done: kind === "checker" && item.done === true,
|
||||
createdAt: stringOrNull(item.createdAt) || nowIso()
|
||||
}];
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue